1#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4pub enum ComponentCategory {
5 Explore,
6 Actions,
7 Forms,
8 Navigation,
9 Overlays,
10 Display,
11 Messages,
12 MacOsLayouts,
13 Charts,
14}
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub struct ComponentInfo {
18 pub id: &'static str,
19 pub name: &'static str,
20 pub category: ComponentCategory,
21 pub description: &'static str,
22 pub documentation: Option<&'static str>,
24}
25
26pub const COMPONENT_GROUPS: &[(&str, &[&str])] = &[
28 ("Explore", &["overview", "theme", "root", "focus_trap"]),
29 (
30 "Actions",
31 &[
32 "button",
33 "button_group",
34 "dropdown_button",
35 "link",
36 "toggle",
37 "toggle_group",
38 "clipboard",
39 "command",
40 ],
41 ),
42 (
43 "Forms",
44 &[
45 "input",
46 "textarea",
47 "editor",
48 "number_input",
49 "input_group",
50 "select",
51 "combobox",
52 "calendar",
53 "date_picker",
54 "time_field",
55 "color_picker",
56 "otp_input",
57 "slider",
58 "checkbox",
59 "switch",
60 "radio",
61 "form",
62 "questionnaire",
63 "stepper",
64 "search_field",
65 "secure_field",
66 ],
67 ),
68 (
69 "Navigation",
70 &[
71 "menu",
72 "tabs",
73 "accordion",
74 "collapsible",
75 "nav_stack",
76 "pagination",
77 "breadcrumb",
78 "sidebar",
79 ],
80 ),
81 (
82 "Overlays",
83 &[
84 "sheet",
85 "dialog",
86 "alert_dialog",
87 "popover",
88 "tooltip",
89 "hover_card",
90 "toast",
91 "notification",
92 ],
93 ),
94 (
95 "Display",
96 &[
97 "icon",
98 "avatar",
99 "text_view",
100 "label",
101 "panel",
102 "group_box",
103 "description_list",
104 "list",
105 "virtual_list",
106 "scrollbar",
107 "scrollable",
108 "table",
109 "data_table",
110 "tree",
111 "resizable",
112 "dock",
113 "separator",
114 "keycap",
115 "badge",
116 "tag",
117 "alert",
118 "empty_state",
119 "progress",
120 "spinner",
121 "skeleton",
122 "shimmer",
123 "rating",
124 "carousel",
125 "image",
126 "diff",
127 ],
128 ),
129 (
130 "Messages",
131 &[
132 "message",
133 "bubble",
134 "marker",
135 "attachment",
136 "message_scroller",
137 "speech",
138 ],
139 ),
140 (
141 "macOS layouts",
142 &[
143 "title_bar",
144 "toolbar",
145 "status_bar",
146 "settings",
147 "inspector",
148 "inset_list",
149 "material",
150 ],
151 ),
152 (
153 "Charts",
154 &[
155 "plot",
156 "line_chart",
157 "bar_chart",
158 "area_chart",
159 "pie_chart",
160 "radar_chart",
161 "candlestick_chart",
162 "sankey_chart",
163 ],
164 ),
165];
166
167pub const COMPONENTS: &[ComponentInfo] = &[
169 ComponentInfo {
170 id: "overview",
171 name: "Overview",
172 category: ComponentCategory::Explore,
173 description: "Explore the components together, then use the sidebar to inspect each one.",
174 documentation: Some("index"),
175 },
176 ComponentInfo {
177 id: "theme",
178 name: "Theme",
179 category: ComponentCategory::Explore,
180 description: "System appearance, macOS accent colors, and semantic light and dark tokens.",
181 documentation: Some("theme"),
182 },
183 ComponentInfo {
184 id: "root",
185 name: "Root",
186 category: ComponentCategory::Explore,
187 description: "The native window host coordinates overlays, focus, and text selection.",
188 documentation: Some("root"),
189 },
190 ComponentInfo {
191 id: "focus_trap",
192 name: "Focus trap",
193 category: ComponentCategory::Explore,
194 description: "Keep keyboard focus within the current group of related controls.",
195 documentation: Some("focus-trap"),
196 },
197 ComponentInfo {
198 id: "button",
199 name: "Button",
200 category: ComponentCategory::Actions,
201 description: "Content-sized actions with quiet hover, focus and pressed states.",
202 documentation: Some("button"),
203 },
204 ComponentInfo {
205 id: "button_group",
206 name: "Button group",
207 category: ComponentCategory::Actions,
208 description: "Choose one setting from a row of mutually exclusive options.",
209 documentation: Some("button"),
210 },
211 ComponentInfo {
212 id: "dropdown_button",
213 name: "Dropdown button",
214 category: ComponentCategory::Actions,
215 description: "Combine a primary command and its related actions in one compact control.",
216 documentation: Some("dropdown_button"),
217 },
218 ComponentInfo {
219 id: "link",
220 name: "Link",
221 category: ComponentCategory::Actions,
222 description: "Open a named destination.",
223 documentation: None,
224 },
225 ComponentInfo {
226 id: "toggle",
227 name: "Toggle",
228 category: ComponentCategory::Actions,
229 description: "Keep a command active until it is pressed again.",
230 documentation: Some("toggle"),
231 },
232 ComponentInfo {
233 id: "toggle_group",
234 name: "Toggle group",
235 category: ComponentCategory::Actions,
236 description: "Combine independent filters to show more than one status.",
237 documentation: Some("toggle"),
238 },
239 ComponentInfo {
240 id: "clipboard",
241 name: "Clipboard",
242 category: ComponentCategory::Actions,
243 description: "Copy a value with keyboard-accessible confirmation.",
244 documentation: Some("clipboard"),
245 },
246 ComponentInfo {
247 id: "command",
248 name: "Command",
249 category: ComponentCategory::Actions,
250 description: "Find and invoke commands with a searchable, keyboard-controlled palette.",
251 documentation: Some("command"),
252 },
253 ComponentInfo {
254 id: "input",
255 name: "Input",
256 category: ComponentCategory::Forms,
257 description: "Single-line editing with selection, clipboard and IME support.",
258 documentation: Some("input"),
259 },
260 ComponentInfo {
261 id: "textarea",
262 name: "Textarea",
263 category: ComponentCategory::Forms,
264 description: "Multi-line notes with native text editing.",
265 documentation: Some("textarea"),
266 },
267 ComponentInfo {
268 id: "editor",
269 name: "Editor",
270 category: ComponentCategory::Forms,
271 description: "Edit source with line numbers, folding, search, and multiple cursors.",
272 documentation: Some("editor"),
273 },
274 ComponentInfo {
275 id: "number_input",
276 name: "Number input",
277 category: ComponentCategory::Forms,
278 description: "A compact numeric field with keyboard and button stepping.",
279 documentation: Some("number-input"),
280 },
281 ComponentInfo {
282 id: "input_group",
283 name: "Input group",
284 category: ComponentCategory::Forms,
285 description: "Compose a text field with leading and trailing accessories.",
286 documentation: Some("input-group"),
287 },
288 ComponentInfo {
289 id: "select",
290 name: "Select",
291 category: ComponentCategory::Forms,
292 description: "Choose one value from a fixed set of options.",
293 documentation: Some("select"),
294 },
295 ComponentInfo {
296 id: "combobox",
297 name: "Combobox",
298 category: ComponentCategory::Forms,
299 description: "Search a collection, then choose a matching option.",
300 documentation: Some("combobox"),
301 },
302 ComponentInfo {
303 id: "calendar",
304 name: "Calendar",
305 category: ComponentCategory::Forms,
306 description: "Choose a date with month and year navigation.",
307 documentation: Some("calendar"),
308 },
309 ComponentInfo {
310 id: "date_picker",
311 name: "Date picker",
312 category: ComponentCategory::Forms,
313 description: "Choose a date from a calendar anchored to a field.",
314 documentation: Some("date-picker"),
315 },
316 ComponentInfo {
317 id: "time_field",
318 name: "Time field",
319 category: ComponentCategory::Forms,
320 description: "Edit hours and minutes as keyboard-navigable time segments.",
321 documentation: Some("time-field"),
322 },
323 ComponentInfo {
324 id: "color_picker",
325 name: "Color picker",
326 category: ComponentCategory::Forms,
327 description: "Choose a label color with Hex and HSLA controls.",
328 documentation: Some("color-picker"),
329 },
330 ComponentInfo {
331 id: "otp_input",
332 name: "OTP input",
333 category: ComponentCategory::Forms,
334 description: "Enter a six-digit verification code.",
335 documentation: Some("otp-input"),
336 },
337 ComponentInfo {
338 id: "slider",
339 name: "Slider",
340 category: ComponentCategory::Forms,
341 description: "Adjust one value or a range with the pointer or keyboard.",
342 documentation: Some("slider"),
343 },
344 ComponentInfo {
345 id: "checkbox",
346 name: "Checkbox",
347 category: ComponentCategory::Forms,
348 description: "Independent choices, including a mixed selection.",
349 documentation: Some("checkbox"),
350 },
351 ComponentInfo {
352 id: "switch",
353 name: "Switch",
354 category: ComponentCategory::Forms,
355 description: "An immediate on/off choice with a visible track and thumb.",
356 documentation: Some("switch"),
357 },
358 ComponentInfo {
359 id: "radio",
360 name: "Radio",
361 category: ComponentCategory::Forms,
362 description: "Choose one option from a group.",
363 documentation: Some("radio"),
364 },
365 ComponentInfo {
366 id: "form",
367 name: "Form",
368 category: ComponentCategory::Forms,
369 description: "Labeled, validated fields with consistent alignment and save actions.",
370 documentation: Some("form"),
371 },
372 ComponentInfo {
373 id: "questionnaire",
374 name: "Questionnaire",
375 category: ComponentCategory::Forms,
376 description: "Move through questions with retained answers, validation, and progress.",
377 documentation: Some("questionnaire"),
378 },
379 ComponentInfo {
380 id: "stepper",
381 name: "Stepper",
382 category: ComponentCategory::Forms,
383 description: "Walk through the steps of a task with visible completion state.",
384 documentation: Some("stepper"),
385 },
386 ComponentInfo {
387 id: "search_field",
388 name: "Search field",
389 category: ComponentCategory::Forms,
390 description: "Search using a native inset field with an inline search symbol.",
391 documentation: Some("input"),
392 },
393 ComponentInfo {
394 id: "secure_field",
395 name: "Secure field",
396 category: ComponentCategory::Forms,
397 description: "Enter a password in a masked text field.",
398 documentation: Some("input"),
399 },
400 ComponentInfo {
401 id: "menu",
402 name: "Menu",
403 category: ComponentCategory::Navigation,
404 description: "An anchored action menu with one pointer and keyboard cursor.",
405 documentation: Some("menu"),
406 },
407 ComponentInfo {
408 id: "tabs",
409 name: "Tabs",
410 category: ComponentCategory::Navigation,
411 description: "Switch between related views while keeping context.",
412 documentation: Some("tabs"),
413 },
414 ComponentInfo {
415 id: "accordion",
416 name: "Accordion",
417 category: ComponentCategory::Navigation,
418 description: "Reveal supporting content when it is needed.",
419 documentation: Some("accordion"),
420 },
421 ComponentInfo {
422 id: "collapsible",
423 name: "Collapsible",
424 category: ComponentCategory::Navigation,
425 description: "Expand a single region for additional settings.",
426 documentation: Some("collapsible"),
427 },
428 ComponentInfo {
429 id: "nav_stack",
430 name: "Navigation stack",
431 category: ComponentCategory::Navigation,
432 description: "Navigate between persistent pages and return to where you left off.",
433 documentation: None,
434 },
435 ComponentInfo {
436 id: "pagination",
437 name: "Pagination",
438 category: ComponentCategory::Navigation,
439 description: "Move through a paged collection.",
440 documentation: Some("pagination"),
441 },
442 ComponentInfo {
443 id: "breadcrumb",
444 name: "Breadcrumb",
445 category: ComponentCategory::Navigation,
446 description: "Return to an ancestor in a document or folder hierarchy.",
447 documentation: None,
448 },
449 ComponentInfo {
450 id: "sidebar",
451 name: "Sidebar",
452 category: ComponentCategory::Navigation,
453 description: "Source-list navigation with icons and rounded selected rows.",
454 documentation: Some("sidebar"),
455 },
456 ComponentInfo {
457 id: "sheet",
458 name: "Sheet",
459 category: ComponentCategory::Overlays,
460 description: "Inspect project details in an edge-attached panel.",
461 documentation: Some("sheet"),
462 },
463 ComponentInfo {
464 id: "dialog",
465 name: "Dialog",
466 category: ComponentCategory::Overlays,
467 description: "A focused task with confirmation, cancellation and focus return.",
468 documentation: Some("dialog"),
469 },
470 ComponentInfo {
471 id: "alert_dialog",
472 name: "Alert dialog",
473 category: ComponentCategory::Overlays,
474 description: "An explicit decision that cannot be dismissed by clicking the backdrop.",
475 documentation: Some("alert-dialog"),
476 },
477 ComponentInfo {
478 id: "popover",
479 name: "Popover",
480 category: ComponentCategory::Overlays,
481 description: "Adjust contextual settings while keeping the workspace in view.",
482 documentation: Some("popover"),
483 },
484 ComponentInfo {
485 id: "tooltip",
486 name: "Tooltip",
487 category: ComponentCategory::Overlays,
488 description: "A short explanation for an action, shown on hover.",
489 documentation: Some("tooltip"),
490 },
491 ComponentInfo {
492 id: "hover_card",
493 name: "Hover card",
494 category: ComponentCategory::Overlays,
495 description: "Preview supporting details without leaving the page.",
496 documentation: Some("hover-card"),
497 },
498 ComponentInfo {
499 id: "toast",
500 name: "Toast",
501 category: ComponentCategory::Overlays,
502 description: "Brief feedback that leaves your current task in place.",
503 documentation: Some("notification"),
504 },
505 ComponentInfo {
506 id: "notification",
507 name: "Notification",
508 category: ComponentCategory::Overlays,
509 description: "Window-hosted notifications with semantic status and dismissal.",
510 documentation: Some("notification"),
511 },
512 ComponentInfo {
513 id: "icon",
514 name: "Icon",
515 category: ComponentCategory::Display,
516 description: "Native SF Symbols on macOS with Lucide fallbacks that inherit text color.",
517 documentation: Some("icon"),
518 },
519 ComponentInfo {
520 id: "avatar",
521 name: "Avatar",
522 category: ComponentCategory::Display,
523 description: "Identify people and workspaces with an image or initials.",
524 documentation: Some("avatar"),
525 },
526 ComponentInfo {
527 id: "text_view",
528 name: "Text view",
529 category: ComponentCategory::Display,
530 description: "Read structured documents with selectable text and links.",
531 documentation: Some("text-view"),
532 },
533 ComponentInfo {
534 id: "label",
535 name: "Label",
536 category: ComponentCategory::Display,
537 description: "Readable labels with secondary text, masking, and search highlights.",
538 documentation: Some("label"),
539 },
540 ComponentInfo {
541 id: "panel",
542 name: "Panel",
543 category: ComponentCategory::Display,
544 description: "A surface for a related group of settings or information.",
545 documentation: None,
546 },
547 ComponentInfo {
548 id: "group_box",
549 name: "Group box",
550 category: ComponentCategory::Display,
551 description: "Group preferences in a titled, rounded container.",
552 documentation: Some("group-box"),
553 },
554 ComponentInfo {
555 id: "description_list",
556 name: "Description list",
557 category: ComponentCategory::Display,
558 description: "Compare labels and values in a structured inspector.",
559 documentation: Some("description-list"),
560 },
561 ComponentInfo {
562 id: "list",
563 name: "List",
564 category: ComponentCategory::Display,
565 description: "Selectable rows with icons, secondary content, and keyboard navigation.",
566 documentation: Some("list"),
567 },
568 ComponentInfo {
569 id: "virtual_list",
570 name: "Virtual list",
571 category: ComponentCategory::Display,
572 description: "Browse a large activity log with variable-height rows.",
573 documentation: Some("virtual-list"),
574 },
575 ComponentInfo {
576 id: "scrollbar",
577 name: "Scrollbar",
578 category: ComponentCategory::Display,
579 description: "Drag the scroll thumb to move through a long activity log.",
580 documentation: None,
581 },
582 ComponentInfo {
583 id: "scrollable",
584 name: "Scrollable",
585 category: ComponentCategory::Display,
586 description: "Scroll long content in a bounded native pane.",
587 documentation: Some("scrollable"),
588 },
589 ComponentInfo {
590 id: "table",
591 name: "Table",
592 category: ComponentCategory::Display,
593 description: "Aligned columns for comparing records.",
594 documentation: Some("table"),
595 },
596 ComponentInfo {
597 id: "data_table",
598 name: "Data table",
599 category: ComponentCategory::Display,
600 description: "Use the full table delegate for selection, columns, and large datasets.",
601 documentation: Some("data-table"),
602 },
603 ComponentInfo {
604 id: "tree",
605 name: "Tree",
606 category: ComponentCategory::Display,
607 description: "Explore nested folders and select a workspace document.",
608 documentation: Some("tree"),
609 },
610 ComponentInfo {
611 id: "resizable",
612 name: "Resizable",
613 category: ComponentCategory::Display,
614 description: "Drag the divider to adjust space between panes.",
615 documentation: Some("resizable"),
616 },
617 ComponentInfo {
618 id: "dock",
619 name: "Dock",
620 category: ComponentCategory::Display,
621 description: "Rearrange document panels by dragging their tabs.",
622 documentation: Some("dock"),
623 },
624 ComponentInfo {
625 id: "separator",
626 name: "Separator",
627 category: ComponentCategory::Display,
628 description: "A quiet boundary between distinct sections.",
629 documentation: None,
630 },
631 ComponentInfo {
632 id: "keycap",
633 name: "Keycap",
634 category: ComponentCategory::Display,
635 description: "Compact, readable keyboard hints.",
636 documentation: Some("kbd"),
637 },
638 ComponentInfo {
639 id: "badge",
640 name: "Badge",
641 category: ComponentCategory::Display,
642 description: "Short labels for neutral and semantic status.",
643 documentation: Some("badge"),
644 },
645 ComponentInfo {
646 id: "tag",
647 name: "Tag",
648 category: ComponentCategory::Display,
649 description: "Compact semantic labels with filled and outline presentations.",
650 documentation: Some("tag"),
651 },
652 ComponentInfo {
653 id: "alert",
654 name: "Alert",
655 category: ComponentCategory::Display,
656 description: "Inline feedback banners for neutral and semantic conditions.",
657 documentation: Some("alert"),
658 },
659 ComponentInfo {
660 id: "empty_state",
661 name: "Empty state",
662 category: ComponentCategory::Display,
663 description: "Explain an empty collection and its next step.",
664 documentation: Some("empty"),
665 },
666 ComponentInfo {
667 id: "progress",
668 name: "Progress",
669 category: ComponentCategory::Display,
670 description: "Show how much of a known task is complete.",
671 documentation: Some("progress"),
672 },
673 ComponentInfo {
674 id: "spinner",
675 name: "Spinner",
676 category: ComponentCategory::Display,
677 description: "Show an ongoing task with an animated progress indicator.",
678 documentation: Some("spinner"),
679 },
680 ComponentInfo {
681 id: "skeleton",
682 name: "Skeleton",
683 category: ComponentCategory::Display,
684 description: "Reserve stable layout while content is loading.",
685 documentation: Some("skeleton"),
686 },
687 ComponentInfo {
688 id: "shimmer",
689 name: "Shimmer",
690 category: ComponentCategory::Display,
691 description: "Animate a subtle highlight over text during background work.",
692 documentation: Some("shimmer"),
693 },
694 ComponentInfo {
695 id: "rating",
696 name: "Rating",
697 category: ComponentCategory::Display,
698 description: "Choose a star rating or display a read-only score.",
699 documentation: Some("rating"),
700 },
701 ComponentInfo {
702 id: "carousel",
703 name: "Carousel",
704 category: ComponentCategory::Display,
705 description: "Browse a collection with previous, next, and pagination controls.",
706 documentation: Some("carousel"),
707 },
708 ComponentInfo {
709 id: "image",
710 name: "Image",
711 category: ComponentCategory::Display,
712 description: "Draw bundled or local images with preserved aspect ratio and cropping.",
713 documentation: Some("image"),
714 },
715 ComponentInfo {
716 id: "diff",
717 name: "Diff",
718 category: ComponentCategory::Display,
719 description: "Review source changes in unified or side-by-side views.",
720 documentation: Some("diff"),
721 },
722 ComponentInfo {
723 id: "message",
724 name: "Message",
725 category: ComponentCategory::Messages,
726 description: "Compose avatars, headers, bodies, and footers in a conversation.",
727 documentation: Some("message"),
728 },
729 ComponentInfo {
730 id: "bubble",
731 name: "Bubble",
732 category: ComponentCategory::Messages,
733 description: "Display incoming and outgoing message bubbles.",
734 documentation: Some("bubble"),
735 },
736 ComponentInfo {
737 id: "marker",
738 name: "Marker",
739 category: ComponentCategory::Messages,
740 description: "Mark dates and events in a conversation timeline.",
741 documentation: Some("marker"),
742 },
743 ComponentInfo {
744 id: "attachment",
745 name: "Attachment",
746 category: ComponentCategory::Messages,
747 description: "Show files with progress, retry, and remove actions.",
748 documentation: Some("attachment"),
749 },
750 ComponentInfo {
751 id: "message_scroller",
752 name: "Message scroller",
753 category: ComponentCategory::Messages,
754 description: "Follow new messages while preserving a manually scrolled position.",
755 documentation: Some("message-scroller"),
756 },
757 ComponentInfo {
758 id: "speech",
759 name: "Speech",
760 category: ComponentCategory::Messages,
761 description: "Speech capture and waveform controls; system recognition is an optional feature.",
762 documentation: Some("speech"),
763 },
764 ComponentInfo {
765 id: "title_bar",
766 name: "Title bar",
767 category: ComponentCategory::MacOsLayouts,
768 description: "A native title-bar surface leaves room for the system window controls.",
769 documentation: Some("title-bar"),
770 },
771 ComponentInfo {
772 id: "toolbar",
773 name: "Toolbar",
774 category: ComponentCategory::MacOsLayouts,
775 description: "Compact command groups on a translucent macOS toolbar surface.",
776 documentation: Some("toolbar"),
777 },
778 ComponentInfo {
779 id: "status_bar",
780 name: "Status bar",
781 category: ComponentCategory::MacOsLayouts,
782 description: "Quiet document status and contextual controls along a window edge.",
783 documentation: Some("status-bar"),
784 },
785 ComponentInfo {
786 id: "settings",
787 name: "Settings",
788 category: ComponentCategory::MacOsLayouts,
789 description: "Grouped preference rows with descriptive labels and trailing controls.",
790 documentation: Some("settings"),
791 },
792 ComponentInfo {
793 id: "inspector",
794 name: "Inspector",
795 category: ComponentCategory::MacOsLayouts,
796 description: "A compact details pane with clear field alignment.",
797 documentation: None,
798 },
799 ComponentInfo {
800 id: "inset_list",
801 name: "Inset list",
802 category: ComponentCategory::MacOsLayouts,
803 description: "Rounded inset rows for grouped preferences and collections.",
804 documentation: None,
805 },
806 ComponentInfo {
807 id: "material",
808 name: "Material",
809 category: ComponentCategory::MacOsLayouts,
810 description: "Semantic surface fills and opt-in native system glass underlays.",
811 documentation: None,
812 },
813 ComponentInfo {
814 id: "plot",
815 name: "Plot",
816 category: ComponentCategory::Charts,
817 description: "Compose axes, marks, scales, and grids for custom visualizations.",
818 documentation: Some("plot"),
819 },
820 ComponentInfo {
821 id: "line_chart",
822 name: "Line chart",
823 category: ComponentCategory::Charts,
824 description: "Follow one measure over time. Hover a point for its value.",
825 documentation: Some("chart"),
826 },
827 ComponentInfo {
828 id: "bar_chart",
829 name: "Bar chart",
830 category: ComponentCategory::Charts,
831 description: "Compare totals across categories.",
832 documentation: Some("chart"),
833 },
834 ComponentInfo {
835 id: "area_chart",
836 name: "Area chart",
837 category: ComponentCategory::Charts,
838 description: "Compare related measures over time, such as download and upload.",
839 documentation: Some("chart"),
840 },
841 ComponentInfo {
842 id: "pie_chart",
843 name: "Pie chart",
844 category: ComponentCategory::Charts,
845 description: "Show how a whole divides into parts.",
846 documentation: Some("chart"),
847 },
848 ComponentInfo {
849 id: "radar_chart",
850 name: "Radar chart",
851 category: ComponentCategory::Charts,
852 description: "Compare profiles across several dimensions at once.",
853 documentation: Some("chart"),
854 },
855 ComponentInfo {
856 id: "candlestick_chart",
857 name: "Candlestick chart",
858 category: ComponentCategory::Charts,
859 description: "Read the open, high, low and close of each trading session.",
860 documentation: Some("chart"),
861 },
862 ComponentInfo {
863 id: "sankey_chart",
864 name: "Sankey chart",
865 category: ComponentCategory::Charts,
866 description: "Trace how an amount flows from its sources to where it goes.",
867 documentation: Some("chart"),
868 },
869];
870
871pub fn component(id: &str) -> Option<&'static ComponentInfo> {
873 COMPONENTS.iter().find(|item| item.id == id)
874}
875
876impl ComponentInfo {
877 pub fn interpretation(&self) -> &'static crate::interpretation::ComponentInterpretation {
879 crate::interpretation::interpretation(self.id)
880 .expect("every catalog page has a presentation contract")
881 }
882}
883
884#[cfg(test)]
885mod tests {
886 use super::*;
887 #[test]
888 fn component_inventory_is_complete_and_unique() {
889 let ids: std::collections::HashSet<_> = COMPONENTS.iter().map(|item| item.id).collect();
890 assert_eq!(ids.len(), COMPONENTS.len());
891 assert!(COMPONENTS.len() >= 90);
892 let grouped: Vec<_> = COMPONENT_GROUPS
893 .iter()
894 .flat_map(|(_, ids)| ids.iter().copied())
895 .collect();
896 assert_eq!(grouped.len(), COMPONENTS.len());
897 assert_eq!(
898 grouped
899 .iter()
900 .copied()
901 .collect::<std::collections::HashSet<_>>(),
902 ids
903 );
904 assert!(
905 COMPONENTS
906 .iter()
907 .all(|item| !item.name.is_empty() && !item.description.is_empty())
908 );
909 }
910}