@@ -6,6 +6,10 @@ import 'package:collection/collection.dart';
66import 'package:jaspr/jaspr.dart' ;
77import 'package:jaspr_content/jaspr_content.dart' ;
88
9+ import '../../markdown/markdown_parser.dart' ;
10+ import '../../models/widget_catalog_model.dart' ;
11+ import '../../util.dart' ;
12+
913class WidgetCatalogCategories extends CustomComponentBase {
1014 const WidgetCatalogCategories ();
1115
@@ -24,10 +28,10 @@ class WidgetCatalogCategories extends CustomComponentBase {
2428 {'catalog' : {'index' : final List <Object ?> index}} =>
2529 index
2630 .cast <Map <String , Object ?>>()
27- .map (_WidgetCatalogCategory .new )
31+ .map (WidgetCatalogCategory .new )
2832 .sortedBy ((c) => c.name),
2933 _ => throw Exception (
30- 'Catalog not found. '
34+ 'Widget Catalog not found. '
3135 'Make sure the `data/catalog/index.yml` file exists.' ,
3236 ),
3337 };
@@ -59,16 +63,123 @@ class WidgetCatalogCategories extends CustomComponentBase {
5963 }
6064}
6165
62- extension type _WidgetCatalogCategory (Map <String , Object ?> _data) {
63- String get id =>
64- _data['id' ] as String ? ??
65- (throw Exception ('Missing id for widget catalog category. ' ));
66- String get name =>
67- _data['name' ] as String ? ??
68- (throw Exception ('Missing name for widget catalog category. ' ));
69- String get description =>
70- _data['description' ] as String ? ??
71- (throw Exception (
72- 'Missing description for widget catalog category "$name ".' ,
73- ));
66+ class WidgetCatalogGrid extends CustomComponentBase {
67+ const WidgetCatalogGrid ();
68+
69+ @override
70+ Pattern get pattern => 'WidgetCatalogGrid' ;
71+
72+ @override
73+ Component apply (
74+ String name,
75+ Map <String , String > attributes,
76+ Component ? child,
77+ ) {
78+ return Builder (
79+ builder: (context) {
80+ final widgets = switch (context.page.data) {
81+ {'catalog' : {'widgets' : final List <Object ?> widgets}} =>
82+ widgets
83+ .cast <Map <String , Object ?>>()
84+ .map (WidgetCatalogWidget .new )
85+ .sortedBy ((c) => c.name),
86+ _ => throw Exception (
87+ 'Catalog not found. '
88+ 'Make sure the `data/catalog/widgets.yml` file exists.' ,
89+ ),
90+ };
91+
92+ return div (classes: 'card-grid' , [
93+ for (final widget in widgets) WidgetCatalogCard (widget: widget),
94+ ]);
95+ },
96+ );
97+ }
98+ }
99+
100+ class WidgetCatalogCard extends StatelessComponent {
101+ const WidgetCatalogCard ({
102+ required this .widget,
103+ this .isMaterialCatalog = false ,
104+ this .subcategory,
105+ super .key,
106+ });
107+
108+ final WidgetCatalogWidget widget;
109+ final bool isMaterialCatalog;
110+ final WidgetCatalogSubcategory ? subcategory;
111+
112+ @override
113+ Component build (BuildContext context) {
114+ return a (href: widget.link, classes: 'card outlined-card' , [
115+ _buildCardImageHolder (),
116+ div (classes: 'card-header' , [
117+ span (classes: 'card-title' , [text (widget.name)]),
118+ ]),
119+ div (classes: 'card-content' , [
120+ p ([
121+ DashMarkdown (
122+ inline: true ,
123+ content: truncateWords (widget.description, 25 ),
124+ ),
125+ ]),
126+ ]),
127+ ]);
128+ }
129+
130+ Component _buildCardImageHolder () {
131+ final holderClass = isMaterialCatalog
132+ ? 'card-image-holder-material-3'
133+ : 'card-image-holder' ;
134+
135+ final imageAlt = isMaterialCatalog
136+ ? 'Rendered example of the ${widget .name } Material widget.'
137+ : 'Rendered image or visualization of the ${widget .name } widget.' ;
138+
139+ final styleAttributes = isMaterialCatalog && subcategory? .color != null
140+ ? {'style' : '--bg-color: ${subcategory ?.color }' }
141+ : < String , String > {};
142+
143+ final placeholder = img (
144+ alt:
145+ 'Placeholder Flutter logo in place of '
146+ 'missing widget image or visualization.' ,
147+ src: '/assets/images/docs/catalog-widget-placeholder.png' ,
148+ attributes: {'aria-hidden' : 'true' },
149+ );
150+
151+ return div (
152+ classes: holderClass,
153+ attributes: styleAttributes,
154+ [
155+ if (isMaterialCatalog) ...[
156+ // Material catalog always expects an image.
157+ if (widget.imageSrc case final imageSrc? when imageSrc.isNotEmpty)
158+ img (alt: imageAlt, src: imageSrc)
159+ else
160+ placeholder,
161+ if (widget.hoverBackgroundSrc case final hoverBackgroundSrc?
162+ when hoverBackgroundSrc.isNotEmpty)
163+ div (classes: 'card-image-material-3-hover' , [
164+ img (
165+ alt:
166+ 'Decorated background for '
167+ 'Material widget visualizations.' ,
168+ src: hoverBackgroundSrc,
169+ attributes: {'aria-hidden' : 'true' },
170+ ),
171+ ]),
172+ ] else ...[
173+ // Standard catalog prefers vector, then image, then placeholder.
174+ if (widget.vector case final vector? when vector.isNotEmpty)
175+ raw (vector)
176+ else if (widget.imageSrc case final imageSrc?
177+ when imageSrc.isNotEmpty)
178+ img (alt: imageAlt, src: imageSrc)
179+ else
180+ placeholder,
181+ ],
182+ ],
183+ );
184+ }
74185}
0 commit comments