Skip to content

Commit fe448fd

Browse files
authored
Reduce duplicated class and icon logic (#6889)
This PR cleans up the Jaspr site implementation a bit and has no functional changes. - Use the `.toClasses` extension to convert a list of strings into a single class string. - Migrate remaining manual material symbol creation to use shared `MaterialIcon` component. - Some other nearby cleanup to avoid the not-null assertion operator.
1 parent 696bccd commit fe448fd

File tree

10 files changed

+49
-61
lines changed

10 files changed

+49
-61
lines changed

site/lib/src/components/breadcrumbs.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:collection/collection.dart';
66
import 'package:jaspr/jaspr.dart';
77
import 'package:jaspr_content/jaspr_content.dart';
88

9+
import '../util.dart';
910
import 'material_icon.dart';
1011

1112
/// Breadcrumbs navigation component that
@@ -149,7 +150,7 @@ final class _BreadcrumbItemComponent extends StatelessComponent {
149150
classes: [
150151
'breadcrumb-item',
151152
if (isLast) 'active',
152-
].join(' '),
153+
].toClasses,
153154
attributes: {
154155
'property': 'itemListElement',
155156
'typeof': 'ListItem',

site/lib/src/components/button.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:jaspr/jaspr.dart';
66

7+
import '../util.dart';
78
import 'material_icon.dart';
89

910
/// A generic button component with different style variants.
@@ -48,7 +49,7 @@ class Button extends StatelessComponent {
4849
style.cssClass,
4950
if (icon != null && content == null) 'icon-button',
5051
...?classes,
51-
].join(' ');
52+
].toClasses;
5253

5354
final children = [
5455
if (icon case final iconId?) MaterialIcon(iconId),

site/lib/src/components/card.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import 'package:jaspr/jaspr.dart';
66

7+
import '../util.dart';
8+
79
class Card extends StatelessComponent {
810
/// Creates a card that can have a [header], [content], and [actions].
911
///
@@ -62,7 +64,7 @@ class Card extends StatelessComponent {
6264
if (filled) 'filled-card',
6365
if (expandable) 'expandable-card',
6466
if (additionalClasses != null) additionalClasses!,
65-
].join(' ');
67+
].toClasses;
6668

6769
final children = [
6870
if (header.isNotEmpty) div(classes: 'card-header', header),
@@ -73,7 +75,7 @@ class Card extends StatelessComponent {
7375
classes: [
7476
'card-content',
7577
if (expandable) 'expandable-content',
76-
].join(' '),
78+
].toClasses,
7779
content,
7880
),
7981
?actions,

site/lib/src/components/chip.dart

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
import 'package:jaspr/jaspr.dart';
66

7+
import '../util.dart';
8+
import 'material_icon.dart';
9+
710
/// A set of Material Design-like chips for configuration.
811
class ChipSet extends StatelessComponent {
912
const ChipSet(this.chips, {this.resettable = false});
@@ -50,32 +53,25 @@ class InfoChip extends StatelessComponent {
5053
final chipClasses = ['chip', 'info-chip', ...?classes];
5154

5255
return div(
53-
classes: chipClasses.join(' '),
56+
classes: chipClasses.toClasses,
5457
attributes: attributes,
5558
[
56-
if (icon != null)
57-
span(
58-
classes: 'material-symbols chip-icon',
59-
attributes: {
60-
'aria-hidden': 'true',
61-
if (title != null) 'title': title!,
62-
},
63-
[text(icon!)],
64-
)
65-
else if (iconPath != null)
59+
if (icon case final icon?)
60+
MaterialIcon(icon, title: title, classes: ['chip-icon'])
61+
else if (iconPath case final iconPath?)
6662
svg(
6763
classes: 'chip-icon',
6864
width: iconSize.px,
6965
height: iconSize.px,
7066
viewBox: iconViewBox,
7167
attributes: {
7268
'aria-hidden': 'true',
73-
if (title != null) 'title': title!,
69+
'title': ?title,
7470
},
7571
[
7672
Component.element(
7773
tag: 'path',
78-
attributes: {'d': iconPath!},
74+
attributes: {'d': iconPath},
7975
),
8076
],
8177
),
@@ -117,7 +113,7 @@ class FilterChip extends StatelessComponent {
117113
'data-filter': dataFilter,
118114
'role': 'checkbox',
119115
'aria-checked': 'false',
120-
if (ariaLabel != null) 'aria-label': ariaLabel!,
116+
'aria-label': ?ariaLabel,
121117
},
122118
[
123119
if (showCheckIcon)
@@ -139,13 +135,9 @@ class FilterChip extends StatelessComponent {
139135
),
140136
],
141137
)
142-
else if (icon != null)
143-
span(
144-
classes: 'material-symbols chip-icon leading-icon',
145-
attributes: {'aria-hidden': 'true'},
146-
[text(icon!)],
147-
)
148-
else if (iconPath != null)
138+
else if (icon case final icon?)
139+
MaterialIcon(icon, classes: ['chip-icon', 'leading-icon'])
140+
else if (iconPath case final iconPath?)
149141
svg(
150142
classes: 'chip-icon leading-icon',
151143
width: iconSize.px,
@@ -155,7 +147,7 @@ class FilterChip extends StatelessComponent {
155147
[
156148
Component.element(
157149
tag: 'path',
158-
attributes: {'d': iconPath!},
150+
attributes: {'d': iconPath},
159151
),
160152
],
161153
),
@@ -190,7 +182,7 @@ class SelectChip extends StatelessComponent {
190182
classes: 'chip select-chip',
191183
attributes: {
192184
'data-menu': menuId,
193-
if (dataTitle != null) 'data-title': dataTitle!,
185+
'data-title': ?dataTitle,
194186
'aria-controls': menuId,
195187
'aria-expanded': 'false',
196188
},
@@ -261,13 +253,9 @@ class SelectMenuItem extends StatelessComponent {
261253
'aria-selected': isSelected.toString(),
262254
},
263255
[
264-
if (icon != null)
265-
span(
266-
classes: 'material-symbols',
267-
attributes: {'aria-hidden': 'true'},
268-
[text(icon!)],
269-
)
270-
else if (iconPath != null)
256+
if (icon case final icon?)
257+
MaterialIcon(icon)
258+
else if (iconPath case final iconPath?)
271259
svg(
272260
classes: 'menu-icon',
273261
width: iconSize.px,
@@ -277,7 +265,7 @@ class SelectMenuItem extends StatelessComponent {
277265
[
278266
Component.element(
279267
tag: 'path',
280-
attributes: {'d': iconPath!},
268+
attributes: {'d': iconPath},
281269
),
282270
],
283271
),

site/lib/src/components/cookie_notice.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:jaspr/jaspr.dart';
66
import 'package:universal_web/web.dart' as web;
77

8+
import '../util.dart';
89
import 'button.dart';
910

1011
/// The cookie banner to show on a user's first time visiting the site.
@@ -48,7 +49,7 @@ final class _CookieNoticeState extends State<CookieNotice> {
4849
Component build(BuildContext context) {
4950
return section(
5051
id: 'cookie-notice',
51-
classes: [if (showNotice) 'show'].join(' '),
52+
classes: [if (showNotice) 'show'].toClasses,
5253
attributes: {'data-nosnippet': 'true'},
5354
[
5455
div(classes: 'container', [

site/lib/src/components/header.dart

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:collection/collection.dart';
66
import 'package:jaspr/jaspr.dart';
77
import 'package:jaspr_content/jaspr_content.dart';
88

9+
import '../util.dart';
910
import 'material_icon.dart';
1011

1112
/// The site-wide top navigation bar.
@@ -48,7 +49,7 @@ class DashHeader extends StatelessComponent {
4849
classes: [
4950
'nav-link',
5051
if (activeEntry == _ActiveNavEntry.overview) 'active',
51-
].join(' '),
52+
].toClasses,
5253
[text('Overview')],
5354
),
5455
]),
@@ -58,7 +59,7 @@ class DashHeader extends StatelessComponent {
5859
classes: [
5960
'nav-link',
6061
if (activeEntry == _ActiveNavEntry.docs) 'active',
61-
].join(' '),
62+
].toClasses,
6263
[
6364
span([text('Docs')]),
6465
],
@@ -70,7 +71,7 @@ class DashHeader extends StatelessComponent {
7071
classes: [
7172
'nav-link',
7273
if (activeEntry == _ActiveNavEntry.blog) 'active',
73-
].join(' '),
74+
].toClasses,
7475
[text('Blog')],
7576
),
7677
]),
@@ -80,7 +81,7 @@ class DashHeader extends StatelessComponent {
8081
classes: [
8182
'nav-link',
8283
if (activeEntry == _ActiveNavEntry.community) 'active',
83-
].join(' '),
84+
].toClasses,
8485
[text('Community')],
8586
),
8687
]),
@@ -100,7 +101,7 @@ class DashHeader extends StatelessComponent {
100101
classes: [
101102
'nav-link',
102103
if (activeEntry == _ActiveNavEntry.getDart) 'active',
103-
].join(' '),
104+
].toClasses,
104105
[text('Get Dart')],
105106
),
106107
]),
@@ -297,7 +298,7 @@ class _SiteWordMarkListEntry extends StatelessComponent {
297298
[
298299
a(
299300
href: href,
300-
classes: ['site-wordmark', if (current) 'current-site'].join(' '),
301+
classes: ['site-wordmark', if (current) 'current-site'].toClasses,
301302
attributes: {
302303
'role': 'menuitem',
303304
'title': 'Navigate to the $_combinedName website.',

site/lib/src/components/material_icon.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import 'package:jaspr/jaspr.dart';
66

7+
import '../util.dart';
8+
79
/// A Material Symbols icon rendered as a span element.
810
class MaterialIcon extends StatelessComponent {
911
const MaterialIcon(
@@ -21,7 +23,7 @@ class MaterialIcon extends StatelessComponent {
2123
@override
2224
Component build(BuildContext _) {
2325
return span(
24-
classes: ['material-symbols', ...classes].join(' '),
26+
classes: ['material-symbols', ...classes].toClasses,
2527
attributes: {
2628
'title': ?title,
2729
'aria-label': ?(label ?? title),

site/lib/src/components/prev_next.dart

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import 'package:jaspr/jaspr.dart';
66

7+
import 'material_icon.dart';
8+
79
/// Previous and next page buttons to display at the end of a page
810
/// in a connected series of pages, such as the language docs.
911
class PrevNext extends StatelessComponent {
@@ -38,15 +40,9 @@ class _PrevNextCard extends StatelessComponent {
3840
final classes = isPrevious ? 'prev' : 'next';
3941
final subtitle = isPrevious ? 'Previous' : 'Next';
4042
final ariaLabel = isPrevious ? 'Previous page: ' : 'Next page: ';
41-
final iconName = isPrevious ? 'chevron_left' : 'chevron_right';
4243

4344
return a(classes: classes, href: page.url, [
44-
if (isPrevious)
45-
span(
46-
classes: 'material-symbols',
47-
attributes: {'aria-hidden': 'true'},
48-
[text(iconName)],
49-
),
45+
if (isPrevious) const MaterialIcon('chevron_left'),
5046
div([
5147
span(
5248
classes: 'prev-next-subtitle',
@@ -55,12 +51,7 @@ class _PrevNextCard extends StatelessComponent {
5551
),
5652
span(classes: 'prev-next-title', [text(page.title)]),
5753
]),
58-
if (!isPrevious)
59-
span(
60-
classes: 'material-symbols',
61-
attributes: {'aria-hidden': 'true'},
62-
[text(iconName)],
63-
),
54+
if (!isPrevious) const MaterialIcon('chevron_right'),
6455
]);
6556
}
6657
}

site/lib/src/components/tabs.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class _DashTabsWrapper extends StatelessComponent {
6666
Component build(BuildContext context) {
6767
return div(
6868
id: id,
69-
classes: ['tabs-wrapper', if (wrapped) 'wrapped'].join(' '),
69+
classes: ['tabs-wrapper', if (wrapped) 'wrapped'].toClasses,
7070
attributes: {
7171
'data-tab-save-key': ?saveKey,
7272
},
@@ -85,7 +85,7 @@ class _DashTabsWrapper extends StatelessComponent {
8585
classes: [
8686
'nav-link',
8787
if (tab.isActive) 'active',
88-
].join(' '),
88+
].toClasses,
8989
attributes: {
9090
'tabindex': '0',
9191
'data-tab-save-id': tab.saveId,

site/lib/src/components/wrapped_code_block.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:jaspr/jaspr.dart';
66

7+
import '../util.dart';
78
import 'copy_button.dart';
89

910
/// A rendered code block with support for syntax highlighting,
@@ -50,7 +51,7 @@ final class WrappedCodeBlock extends StatelessComponent {
5051
classes: [
5152
'code-block-body',
5253
if (tag case final codeTag?) ...['has-tag', codeTag.parentClass],
53-
].join(' '),
54+
].toClasses,
5455
[
5556
if (tag case final codeTag?)
5657
span(
@@ -67,7 +68,7 @@ final class WrappedCodeBlock extends StatelessComponent {
6768
classes: [
6869
if (showLineNumbers) 'show-line-numbers',
6970
'opal',
70-
].join(' '),
71+
].toClasses,
7172
attributes: {'tabindex': '0'},
7273
[
7374
code(
@@ -82,7 +83,7 @@ final class WrappedCodeBlock extends StatelessComponent {
8283
'line',
8384
if (highlightLines.contains(lineIndex + 1))
8485
'highlighted-line',
85-
].join(' '),
86+
].toClasses,
8687
attributes: {
8788
if (showLineNumbers)
8889
'data-line': '${initialLineNumber + lineIndex}',

0 commit comments

Comments
 (0)