@astryxdesign/cli 0.1.1 → 0.1.2-canary.043f3fd

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/docs/theme.doc.mjs +2 -2
  3. package/docs/working-with-ai.doc.mjs +6 -6
  4. package/package.json +31 -8
  5. package/src/api/component.mjs +253 -12
  6. package/src/api/discover.mjs +87 -28
  7. package/src/api/doctor.mjs +7 -22
  8. package/src/api/index.mjs +1 -0
  9. package/src/api/layout.mjs +302 -0
  10. package/src/api/layout.test.mjs +241 -0
  11. package/src/api/template-integration.test.mjs +225 -0
  12. package/src/api/template.mjs +421 -122
  13. package/src/api/template.test.mjs +2 -0
  14. package/src/api/theme-add.mjs +182 -0
  15. package/src/api/validate-integration.mjs +370 -0
  16. package/src/api/validate-integration.test.mjs +222 -0
  17. package/src/codemod.mjs +93 -0
  18. package/src/codemod.test.mjs +134 -0
  19. package/src/codemods/__tests__/registry.test.mjs +2 -0
  20. package/src/codemods/__tests__/runner.test.mjs +103 -0
  21. package/src/codemods/integration-discovery.mjs +168 -0
  22. package/src/codemods/integration-discovery.test.mjs +234 -0
  23. package/src/codemods/integration-runner.mjs +109 -0
  24. package/src/codemods/registry.mjs +2 -0
  25. package/src/codemods/run-codemod.mjs +207 -0
  26. package/src/codemods/runner.mjs +73 -96
  27. package/src/codemods/transforms/v0.0.15/index.mjs +0 -13
  28. package/src/codemods/transforms/v0.1.0/__tests__/v0.1.0-ordering.test.mjs +81 -0
  29. package/src/codemods/transforms/{v0.0.15 → v0.1.0}/drop-xds-prefix-imports.mjs +15 -4
  30. package/src/codemods/transforms/v0.1.0/index.mjs +15 -6
  31. package/src/codemods/transforms/v0.1.2/__tests__/rename-text-color-active-to-accent.test.mjs +120 -0
  32. package/src/codemods/transforms/v0.1.2/index.mjs +19 -0
  33. package/src/codemods/transforms/v0.1.2/rename-text-color-active-to-accent.mjs +136 -0
  34. package/src/codemods/transforms/v0.1.3/__tests__/migrate-layout-components-to-experimental.test.mjs +360 -0
  35. package/src/codemods/transforms/v0.1.3/index.mjs +19 -0
  36. package/src/codemods/transforms/v0.1.3/migrate-layout-components-to-experimental.mjs +260 -0
  37. package/src/commands/build-theme.import-path.test.mjs +22 -2
  38. package/src/commands/build-theme.mjs +185 -16
  39. package/src/commands/component/index.mjs +44 -10
  40. package/src/commands/component-ownership.test.mjs +227 -0
  41. package/src/commands/discover.mjs +5 -16
  42. package/src/commands/doctor.test.mjs +3 -3
  43. package/src/commands/init.mjs +34 -8
  44. package/src/commands/init.next-steps.test.mjs +46 -0
  45. package/src/commands/json-contract.test.mjs +0 -32
  46. package/src/commands/layout.mjs +139 -0
  47. package/src/commands/swizzle.mjs +241 -224
  48. package/src/commands/swizzle.path-safety.test.mjs +3 -3
  49. package/src/commands/swizzle.routing.test.mjs +279 -0
  50. package/src/commands/template.mjs +33 -37
  51. package/src/commands/upgrade.config-ordering.test.mjs +273 -0
  52. package/src/commands/upgrade.integration-policy.test.mjs +204 -0
  53. package/src/commands/upgrade.mjs +349 -239
  54. package/src/commands/validate-integration.mjs +110 -0
  55. package/src/commands/validate-integration.test.mjs +124 -0
  56. package/src/config.mjs +18 -0
  57. package/src/config.test.mjs +100 -0
  58. package/src/index.mjs +13 -3
  59. package/src/integration.mjs +19 -0
  60. package/src/lib/component-discovery.mjs +177 -0
  61. package/src/lib/config-cache.mjs +91 -0
  62. package/src/lib/config-cache.test.mjs +83 -0
  63. package/src/lib/config-schema.mjs +105 -0
  64. package/src/lib/error-codes.mjs +18 -12
  65. package/src/lib/integration-warnings.mjs +62 -0
  66. package/src/lib/integration-warnings.test.mjs +102 -0
  67. package/src/lib/integrations.mjs +132 -0
  68. package/src/lib/integrations.test.mjs +135 -0
  69. package/src/lib/levenshtein.mjs +29 -0
  70. package/src/lib/manifest.mjs +15 -2
  71. package/src/lib/module-loader.mjs +80 -0
  72. package/src/lib/module-loader.test.mjs +106 -0
  73. package/src/lib/package-scanner.mjs +31 -7
  74. package/src/lib/project.mjs +502 -0
  75. package/src/lib/project.test.mjs +308 -0
  76. package/src/lib/string-utils.mjs +5 -14
  77. package/src/lib/xle/browser.d.ts +100 -0
  78. package/src/lib/xle/browser.mjs +120 -0
  79. package/src/lib/xle/expand.mjs +622 -0
  80. package/src/lib/xle/parse.mjs +581 -0
  81. package/src/lib/xle/print.mjs +174 -0
  82. package/src/lib/xle/registry-core.mjs +170 -0
  83. package/src/lib/xle/registry.mjs +237 -0
  84. package/src/lib/xle/splice.mjs +137 -0
  85. package/src/lib/xle/validate.mjs +356 -0
  86. package/src/lib/xle/xle.test.mjs +333 -0
  87. package/src/template.mjs +73 -0
  88. package/src/template.test.mjs +127 -0
  89. package/src/types/api.d.ts +2 -6
  90. package/src/types/base.d.ts +3 -9
  91. package/src/types/codemod.d.ts +81 -0
  92. package/src/types/component.d.ts +27 -2
  93. package/src/types/config.d.ts +70 -0
  94. package/src/types/error-codes.d.ts +5 -3
  95. package/src/types/index.d.ts +0 -1
  96. package/src/types/integration.d.ts +29 -0
  97. package/src/types/swizzle.d.ts +9 -2
  98. package/src/types/template-api.d.ts +54 -0
  99. package/src/types/template.d.ts +10 -7
  100. package/src/types/upgrade.d.ts +29 -0
  101. package/src/types/validate-integration.d.ts +24 -0
  102. package/src/utils/github.mjs +0 -252
  103. package/src/utils/interactive.mjs +2 -2
  104. package/templates/blocks/components/Card/ClickableCardWithNestedButton.doc.mjs +1 -1
  105. package/templates/blocks/components/Card/SelectableCardMulti.doc.mjs +1 -1
  106. package/templates/blocks/components/ChatComposerDrawer/ChatComposerDrawerWithProgress.tsx +1 -1
  107. package/templates/blocks/components/ChatDictationButton/ChatDictationButtonShowcase.tsx +3 -3
  108. package/templates/blocks/components/CommandPaletteEmpty/CommandPaletteEmptyShowcase.doc.mjs +15 -0
  109. package/templates/blocks/components/CommandPaletteEmpty/CommandPaletteEmptyShowcase.tsx +26 -0
  110. package/templates/blocks/components/DateInput/DateInputDateRange.doc.mjs +2 -2
  111. package/templates/blocks/components/DateRangeInput/DateRangeInputWithPresets.doc.mjs +14 -0
  112. package/templates/blocks/components/DateRangeInput/DateRangeInputWithPresets.tsx +46 -0
  113. package/templates/blocks/components/DateRangeInput/DateRangeInputWithValidation.doc.mjs +14 -0
  114. package/templates/blocks/components/DateRangeInput/DateRangeInputWithValidation.tsx +52 -0
  115. package/templates/blocks/components/DateTimeInput/DateTimeInputWithValidation.doc.mjs +14 -0
  116. package/templates/blocks/components/DateTimeInput/DateTimeInputWithValidation.tsx +43 -0
  117. package/templates/blocks/components/FileInput/FileInputShowcase.tsx +4 -2
  118. package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.doc.mjs +1 -1
  119. package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.tsx +9 -4
  120. package/templates/blocks/components/LinkProvider/LinkProviderCustomLink.doc.mjs +14 -0
  121. package/templates/blocks/components/LinkProvider/LinkProviderCustomLink.tsx +43 -0
  122. package/templates/blocks/components/MultiSelector/MultiSelectorShowcase.tsx +6 -2
  123. package/templates/blocks/components/NumberInput/NumberInputShowcase.tsx +6 -2
  124. package/templates/blocks/components/Outline/OutlineShowcase.doc.mjs +15 -0
  125. package/templates/blocks/components/Outline/OutlineShowcase.tsx +22 -0
  126. package/templates/blocks/components/RadioList/RadioListShowcase.tsx +8 -1
  127. package/templates/blocks/components/SegmentedControl/SegmentedControlShowcase.tsx +3 -1
  128. package/templates/blocks/components/Selector/SelectorShowcase.tsx +4 -1
  129. package/templates/blocks/components/Slider/SliderShowcase.tsx +10 -1
  130. package/templates/blocks/components/Tab/TabShowcase.tsx +3 -1
  131. package/templates/blocks/components/Tab/TabWithSelectedIcon.doc.mjs +13 -0
  132. package/templates/blocks/components/Tab/TabWithSelectedIcon.tsx +39 -0
  133. package/templates/blocks/components/TabList/TabListShowcase.tsx +3 -1
  134. package/templates/blocks/components/TabMenu/TabMenuShowcase.tsx +3 -1
  135. package/templates/blocks/components/Table/ColumnResizeHookUsage.doc.mjs +14 -0
  136. package/templates/blocks/components/Table/ColumnResizeHookUsage.tsx +59 -0
  137. package/templates/blocks/components/Table/StickyColumnsHookUsage.doc.mjs +14 -0
  138. package/templates/blocks/components/Table/StickyColumnsHookUsage.tsx +104 -0
  139. package/templates/blocks/components/Text/TextColors.tsx +20 -5
  140. package/templates/blocks/components/TextArea/TextAreaShowcase.tsx +4 -2
  141. package/templates/blocks/components/TextInput/TextInputShowcase.tsx +4 -2
  142. package/templates/blocks/components/Thumbnail/ThumbnailDisabled.tsx +11 -6
  143. package/templates/blocks/components/Thumbnail/ThumbnailGallery.tsx +43 -7
  144. package/templates/blocks/components/Thumbnail/ThumbnailRemovable.tsx +40 -3
  145. package/templates/blocks/components/Thumbnail/ThumbnailShowcase.tsx +6 -5
  146. package/templates/blocks/components/Thumbnail/ThumbnailStates.tsx +11 -6
  147. package/templates/blocks/components/Timestamp/TimestampColors.tsx +2 -2
  148. package/templates/blocks/components/ToggleButton/ToggleButtonGroup.doc.mjs +1 -1
  149. package/templates/blocks/components/Tokenizer/TokenizerShowcase.tsx +8 -6
  150. package/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +2 -2
  151. package/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +23 -14
  152. package/templates/blocks/components/Typeahead/TypeaheadShowcase.tsx +6 -2
  153. package/templates/pages/shell-nav/page.tsx +321 -0
  154. package/templates/pages/shell-nav/template.doc.mjs +12 -0
  155. package/templates/pages/shell-side-nav/page.tsx +242 -0
  156. package/templates/pages/shell-side-nav/template.doc.mjs +12 -0
  157. package/templates/pages/shell-top-nav/page.tsx +224 -0
  158. package/templates/pages/shell-top-nav/template.doc.mjs +12 -0
  159. package/templates/pages/theme-showcase/page.tsx +8 -19
  160. package/templates/themes/butter/butterTheme.ts +916 -0
  161. package/templates/themes/butter/icons.tsx +77 -0
  162. package/templates/themes/chocolate/chocolateTheme.ts +230 -0
  163. package/templates/themes/chocolate/icons.tsx +77 -0
  164. package/templates/themes/gothic/gothicTheme.ts +657 -0
  165. package/templates/themes/gothic/icons.tsx +77 -0
  166. package/templates/themes/manifest.json +90 -0
  167. package/templates/themes/matcha/icons.tsx +67 -0
  168. package/templates/themes/matcha/matchaTheme.ts +247 -0
  169. package/templates/themes/neutral/icons.tsx +77 -0
  170. package/templates/themes/neutral/neutralTheme.ts +603 -0
  171. package/templates/themes/stone/icons.tsx +77 -0
  172. package/templates/themes/stone/stoneTheme.ts +652 -0
  173. package/templates/themes/y2k/icons.tsx +67 -0
  174. package/templates/themes/y2k/y2kTheme.ts +617 -0
  175. package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-config-surfaces.test.mjs +0 -116
  176. package/src/codemods/transforms/v0.1.0/migrate-xds-config-surfaces.mjs +0 -230
  177. package/src/commands/gap-report.mjs +0 -456
  178. package/src/commands/gap-report.test.mjs +0 -163
  179. package/src/commands/swizzle-gap-safety.test.mjs +0 -273
  180. package/src/lib/config.mjs +0 -86
  181. package/src/lib/config.test.mjs +0 -42
  182. package/src/types/gap-report.d.ts +0 -29
  183. package/templates/blocks/components/MoreMenu/MoreMenuInToolbar.doc.mjs +0 -14
  184. package/templates/blocks/components/MoreMenu/MoreMenuInToolbar.tsx +0 -57
  185. /package/src/codemods/transforms/{v0.0.15 → v0.1.0}/__tests__/drop-xds-prefix-imports.test.mjs +0 -0
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'useTableColumnResize',
7
+ name: 'useTableColumnResize — Draggable Columns',
8
+ displayName: 'useTableColumnResize — Draggable Columns',
9
+ description:
10
+ 'A Table using useTableColumnResize. Drag the right edge of any column header to resize; widths are committed on release. The last proportional column flexes to fill remaining space.',
11
+ isReady: true,
12
+ aspectRatio: 16 / 9,
13
+ componentsUsed: ['Table'],
14
+ };
@@ -0,0 +1,59 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {useState} from 'react';
6
+ import {
7
+ Table,
8
+ useTableColumnResize,
9
+ proportional,
10
+ pixel,
11
+ } from '@astryxdesign/core/Table';
12
+
13
+ interface User extends Record<string, unknown> {
14
+ id: string;
15
+ name: string;
16
+ email: string;
17
+ role: string;
18
+ }
19
+
20
+ const users: User[] = [
21
+ {
22
+ id: '1',
23
+ name: 'Alice Johnson',
24
+ email: 'alice@example.com',
25
+ role: 'Engineer',
26
+ },
27
+ {id: '2', name: 'Bob Smith', email: 'bob@example.com', role: 'Designer'},
28
+ {id: '3', name: 'Charlie Brown', email: 'charlie@example.com', role: 'PM'},
29
+ {id: '4', name: 'Diana Prince', email: 'diana@example.com', role: 'Engineer'},
30
+ {id: '5', name: 'Eve Davis', email: 'eve@example.com', role: 'Analyst'},
31
+ ];
32
+
33
+ const columns = [
34
+ {key: 'name', header: 'Name'},
35
+ {key: 'email', header: 'Email', width: proportional(2)},
36
+ {key: 'role', header: 'Role', width: pixel(120)},
37
+ ];
38
+
39
+ export default function ColumnResizeHookUsage() {
40
+ const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
41
+
42
+ const columnResize = useTableColumnResize({
43
+ columnWidths,
44
+ columns,
45
+ onColumnResizeEnd: updates => {
46
+ setColumnWidths(prev => ({...prev, ...updates}));
47
+ },
48
+ });
49
+
50
+ return (
51
+ <Table
52
+ data={users}
53
+ columns={columns}
54
+ idKey="id"
55
+ hasHover
56
+ plugins={{columnResize}}
57
+ />
58
+ );
59
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
+ export const doc = {
5
+ type: 'block',
6
+ exampleFor: 'useTableStickyColumns',
7
+ name: 'useTableStickyColumns — Pinned Columns',
8
+ displayName: 'useTableStickyColumns — Pinned Columns',
9
+ description:
10
+ 'A Table using useTableStickyColumns to pin the Name column to the start edge and Status to the end edge. Scroll horizontally; pinned columns stay in view with a soft shadow over the scrolling content.',
11
+ isReady: true,
12
+ aspectRatio: 16 / 9,
13
+ componentsUsed: ['Table'],
14
+ };
@@ -0,0 +1,104 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ import {Table, useTableStickyColumns, pixel} from '@astryxdesign/core/Table';
6
+
7
+ interface Employee extends Record<string, unknown> {
8
+ id: string;
9
+ name: string;
10
+ email: string;
11
+ team: string;
12
+ role: string;
13
+ location: string;
14
+ startDate: string;
15
+ status: string;
16
+ }
17
+
18
+ const employees: Employee[] = [
19
+ {
20
+ id: '1',
21
+ name: 'Alice Johnson',
22
+ email: 'alice@example.com',
23
+ team: 'Design Systems',
24
+ role: 'Staff Engineer',
25
+ location: 'San Francisco',
26
+ startDate: '2019-03-12',
27
+ status: 'Active',
28
+ },
29
+ {
30
+ id: '2',
31
+ name: 'Bob Smith',
32
+ email: 'bob@example.com',
33
+ team: 'Design Systems',
34
+ role: 'Senior Designer',
35
+ location: 'New York',
36
+ startDate: '2020-07-01',
37
+ status: 'Active',
38
+ },
39
+ {
40
+ id: '3',
41
+ name: 'Charlie Brown',
42
+ email: 'charlie@example.com',
43
+ team: 'Platform',
44
+ role: 'Engineering Manager',
45
+ location: 'London',
46
+ startDate: '2017-11-20',
47
+ status: 'On leave',
48
+ },
49
+ {
50
+ id: '4',
51
+ name: 'Diana Prince',
52
+ email: 'diana@example.com',
53
+ team: 'Platform',
54
+ role: 'Staff Engineer',
55
+ location: 'Remote',
56
+ startDate: '2021-01-15',
57
+ status: 'Active',
58
+ },
59
+ {
60
+ id: '5',
61
+ name: 'Eve Davis',
62
+ email: 'eve@example.com',
63
+ team: 'Growth',
64
+ role: 'Product Engineer',
65
+ location: 'Berlin',
66
+ startDate: '2022-05-30',
67
+ status: 'Active',
68
+ },
69
+ ];
70
+
71
+ // Wide fixed-width columns so the table overflows and scrolls horizontally,
72
+ // keeping the pinned Name (start) and Status (end) columns in view.
73
+ const columns = [
74
+ {key: 'name', header: 'Name', width: pixel(180)},
75
+ {key: 'email', header: 'Email', width: pixel(220)},
76
+ {key: 'team', header: 'Team', width: pixel(180)},
77
+ {key: 'role', header: 'Role', width: pixel(200)},
78
+ {key: 'location', header: 'Location', width: pixel(160)},
79
+ {key: 'startDate', header: 'Start date', width: pixel(140)},
80
+ {key: 'status', header: 'Status', width: pixel(140)},
81
+ ];
82
+
83
+ export default function StickyColumnsHookUsage() {
84
+ const stickyColumns = useTableStickyColumns<Employee>({
85
+ startKeys: ['name'],
86
+ endKeys: ['status'],
87
+ });
88
+
89
+ return (
90
+ // Constrain the width so the Table's own horizontal scroll container is
91
+ // narrower than its columns — that scroll container is what sticky columns
92
+ // pin against. Without a width cap the table renders full-width and never
93
+ // scrolls internally, so nothing sticks.
94
+ <div style={{width: 560, maxWidth: '100%'}}>
95
+ <Table
96
+ data={employees}
97
+ columns={columns}
98
+ idKey="id"
99
+ hasHover
100
+ plugins={{stickyColumns}}
101
+ />
102
+ </div>
103
+ );
104
+ }
@@ -6,11 +6,26 @@ import {Text} from '@astryxdesign/core/Text';
6
6
  import {Stack} from '@astryxdesign/core/Stack';
7
7
 
8
8
  const COLORS = [
9
- {color: 'primary' as const, description: 'Primary — Default for headings and body text'},
10
- {color: 'secondary' as const, description: 'Secondary — Supporting details and metadata'},
11
- {color: 'active' as const, description: 'ActiveLinks, success states, and emphasis'},
12
- {color: 'disabled' as const, description: 'Disabled — Unavailable or inactive content'},
13
- {color: 'placeholder' as const, description: 'Placeholder — Empty field hints'},
9
+ {
10
+ color: 'primary' as const,
11
+ description: 'PrimaryDefault for headings and body text',
12
+ },
13
+ {
14
+ color: 'secondary' as const,
15
+ description: 'Secondary — Supporting details and metadata',
16
+ },
17
+ {
18
+ color: 'accent' as const,
19
+ description: 'Accent — Links, emphasis, and accent-colored text',
20
+ },
21
+ {
22
+ color: 'disabled' as const,
23
+ description: 'Disabled — Unavailable or inactive content',
24
+ },
25
+ {
26
+ color: 'placeholder' as const,
27
+ description: 'Placeholder — Empty field hints',
28
+ },
14
29
  ];
15
30
 
16
31
  export default function TextColors() {
@@ -2,15 +2,17 @@
2
2
 
3
3
  'use client';
4
4
 
5
+ import {useState} from 'react';
5
6
  import {TextArea} from '@astryxdesign/core/TextArea';
6
7
 
7
8
  export default function TextAreaShowcase() {
9
+ const [value, setValue] = useState('');
8
10
  return (
9
11
  <div style={{width: 400}}>
10
12
  <TextArea
11
13
  label="Description"
12
- value=""
13
- onChange={() => {}}
14
+ value={value}
15
+ onChange={setValue}
14
16
  placeholder="Enter a description..."
15
17
  />
16
18
  </div>
@@ -2,15 +2,17 @@
2
2
 
3
3
  'use client';
4
4
 
5
+ import {useState} from 'react';
5
6
  import {TextInput} from '@astryxdesign/core/TextInput';
6
7
 
7
8
  export default function TextInputShowcase() {
9
+ const [value, setValue] = useState('');
8
10
  return (
9
11
  <div style={{width: 300}}>
10
12
  <TextInput
11
13
  label="Name"
12
- value=""
13
- onChange={() => {}}
14
+ value={value}
15
+ onChange={setValue}
14
16
  placeholder="Enter your name"
15
17
  />
16
18
  </div>
@@ -6,6 +6,11 @@ import {Thumbnail} from '@astryxdesign/core/Thumbnail';
6
6
  import {Stack} from '@astryxdesign/core/Layout';
7
7
  import {Text} from '@astryxdesign/core/Text';
8
8
 
9
+ // Inline same-origin data URI so the example is self-contained — see
10
+ // ThumbnailRemovable for why image-backed Thumbnails avoid cross-origin URLs.
11
+ const GOLDEN_SUNSET =
12
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAkFBMVEX/+uv/7sj6tFr3sVn1r1nzrVjxq1jvqVftp1frpVbpo1bnoVXln1XjnVThm1TfmVPdl1PblVLZk1LXkVLVj1HTjVHRi1DPiVDNh0/LhU/Jg07HgU7Ff03DfU3Be0y/eUy9d0u7dUu5c0u2cEq0bkqybEmwakmuaEisZkiqZEeoYkemYEakXkaiXEVgLjQ8HCjaJlMkAAABI0lEQVR42u3Mx3qDMBSE0Z/03nsvdhxjcnn/twsYjISR5MUkO5+V9N2ZYUPEpogtEdsidkTsitgTsS/iQMShiCMRxyJORJyKOBNxLuJCxKWIKxHXIm5E3Iq4E3Ev4kHEo4gnEc8iXkS8inhLymrJBO8J2UIiw0dc5sRDfEbVRWr1o/pbMMWox9yz6zcLIzMbBTD2VaHu7fr1gjXGA3w5baj5+H0w6x09TDpdaP7rDZhn0sd3y89Y9fcHbPnoYdroZ2w6dQNmg6OHfG4QyrsBC8gdZpVQxtqF4M1mjepFURThTLNgMYsePxaX2WqY6D8GSm2grK2slbGBciHZ9hOE2smNpTuxenhieCdeH06E7qTqvYnImWTbbUSPlKL1wHrgTwZ+AWq+3H5MpRkwAAAAAElFTkSuQmCC';
13
+
9
14
  export default function ThumbnailDisabled() {
10
15
  return (
11
16
  <Stack direction="vertical" gap={4}>
@@ -15,9 +20,9 @@ export default function ThumbnailDisabled() {
15
20
  </Text>
16
21
  <Stack direction="horizontal" gap={3} vAlign="center">
17
22
  <Thumbnail
18
- src="https://lookaside.facebook.com/assets/astryx/moody-scene-vertical-2.png"
19
- alt="Bright landscape"
20
- label="landscape.jpg"
23
+ src={GOLDEN_SUNSET}
24
+ alt="Golden sunset over mountains"
25
+ label="golden-sunset.jpg"
21
26
  onRemove={() => {}}
22
27
  />
23
28
  <Thumbnail label="document.pdf" onRemove={() => {}} />
@@ -29,9 +34,9 @@ export default function ThumbnailDisabled() {
29
34
  </Text>
30
35
  <Stack direction="horizontal" gap={3} vAlign="center">
31
36
  <Thumbnail
32
- src="https://lookaside.facebook.com/assets/astryx/moody-scene-vertical-2.png"
33
- alt="Bright landscape"
34
- label="landscape.jpg"
37
+ src={GOLDEN_SUNSET}
38
+ alt="Golden sunset over mountains"
39
+ label="golden-sunset.jpg"
35
40
  onRemove={() => {}}
36
41
  isDisabled
37
42
  />
@@ -7,11 +7,47 @@ import {Thumbnail} from '@astryxdesign/core/Thumbnail';
7
7
  import {Stack} from '@astryxdesign/core/Layout';
8
8
  import {Text} from '@astryxdesign/core/Text';
9
9
 
10
+ // Demo imagery is inlined as same-origin data URIs so the example is
11
+ // self-contained. Thumbnail's remove-button overlay uses useImageMode, which
12
+ // FETCHES the image to sample pixels (APCA) for contrast — a cross-origin CDN
13
+ // URL without CORS headers cannot be fetched/sampled, so contrast detection
14
+ // fails silently in hosted previews. These scenes are shared verbatim across
15
+ // the Thumbnail examples and span a luminance ramp (night → snow) so the
16
+ // overlay's contrast adaptation is visible.
17
+ const NIGHT_FOREST =
18
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEX18+H08uDy8d/y8N/v7t/m5NXl49Tk4tPc3NDX1cnW1cnW1cjW1MjV1MjV1MfR0cXIyLzEw7m8vLC3t68wM0QvM0MvM0IvMkIuMkEtMEErLz8iJjkhJTggJTcgJDcfJDYfIzYeIjUcITMbHzIWHiwYGzIXGjEWGjAWGTAVGS8VGC8UGC4UGC0UFy4TFy0TFywTFi0SFiwRFSsQFSkNFR4QFCoPFCkPFCgPEykOEygOEigNEicNEScMESYIDw8ECgr+gzZIAAABqElEQVR42uWQ61aCQBRGD5GylNBMKS9FZgNdNE0cyUvB+79VM8MoF9Fgplau2j/8nO8c9hqAD0ngDwjeJYH1lrfBWoCtYDboNu/Zv7qQYNap1bpCN1gxFh1dNe5WAsCScWqoWttbChAKvHZZ0W6XQoIFpW8ooFmLCG2Rl1BgaZAU5Ac8yg0RlC9nngCRQDFuhQRzinWmkCuYk3lxQoFrlskVqlcTMcEFvYIKoFbNfmEFuIypWaGGkt66tqZuEbjAHZl6SQVF1WqNvpDAHVktXTvR9JbgDZii1+tZI7cYMJXkCASvksB4L5VxHmAsibzgRRIYbjgfCgFDSeBZkh8W1HMIniSBR0mOQPCQA3RgBk4M5DhNZwdEcbKgLXy5iDiZg5gApRYbqTql2FZgMxAKeEWSNUFYx3s2SfU28HPWItrpySSwE2cEdqrYtxgmxgHGScFmHrAGk8SxczIxZnNC1McFOGshShyb4yxB9sI3CXDGnH0MLsD7F5LvvptEgH12Zg1Pn2WuHsjvrwt8308t+nwxT38sgiAE8/RTeagH+vx/F8SeF0Ja8Ak14ia/LgmpZQAAAABJRU5ErkJggg==';
19
+ const MISTY_VALLEY =
20
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEWPnaqJmKSIlaKHlaGGlaKGk6CEkp6Ckp+Cj5uAj51/jZp6i5h8ipd7iZZ4hpR1g5FzhpdzhJN0g5FuhZdsgpVqgZRpgJNpf5JmfpFufYxufYtlfJBpe4tke49neo1ieo5kd4tndoZidopheY1geIxfd4tmdYVddYpcdIlbc4hgcoNebn5ZcodYcYZXb4VVboRUbYNTbIJaantWaXxRa4FQaYBPaH9NZ35UZXZQY3dMZn1LZXxKZHxMYHRKXnIoOEh3WzsBAAABeElEQVR42u3T11oCQQyG4R9FEREFqVJEgh17xbZ7/3cl29gpmQGeeOh7OF+Ss8G3EL6E8CmEdyG8CeFVCC9CeBTCgxDuhXAnhFsh3AjhSgiXQrgQwtlayHrB6TqIyHzCREMTH4rpbxip5nnkRintEUNF0oc8yqnP6Oey3ueQSnlHN6P0ro0Mi4BOSu8dA1mygnbC7G0NMdKEVszuLQXx4oZmhO3NDLlEEY05R280PDGbgD0RaBeI/Bdg7weBMkDLwF4PtBOcwH0gCJZf0DPYdd8Jo8K177hgZbjW+RN2hWffusBVnMR+XE5yfMRx5MPtOOOI0Xd+9ot/rTOiVrtepuabwcDwNFgP6przSH0dqKiOUhUfvaKcO8yVncyM0sK+psSyK4qpPUvRwlUUYrusgoaviG27YMFXNz38I0nc8PNPAFtC2BFClRFWV4cDU5g4WEE0hJ4q1PU8sgmMF0LOmKN2TBOh21RjRsxms3C5WYwrCIX+D/zBgV8HCFv4gVKf1AAAAABJRU5ErkJggg==';
21
+ const GOLDEN_SUNSET =
22
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAkFBMVEX/+uv/7sj6tFr3sVn1r1nzrVjxq1jvqVftp1frpVbpo1bnoVXln1XjnVThm1TfmVPdl1PblVLZk1LXkVLVj1HTjVHRi1DPiVDNh0/LhU/Jg07HgU7Ff03DfU3Be0y/eUy9d0u7dUu5c0u2cEq0bkqybEmwakmuaEisZkiqZEeoYkemYEakXkaiXEVgLjQ8HCjaJlMkAAABI0lEQVR42u3Mx3qDMBSE0Z/03nsvdhxjcnn/twsYjISR5MUkO5+V9N2ZYUPEpogtEdsidkTsitgTsS/iQMShiCMRxyJORJyKOBNxLuJCxKWIKxHXIm5E3Iq4E3Ev4kHEo4gnEc8iXkS8inhLymrJBO8J2UIiw0dc5sRDfEbVRWr1o/pbMMWox9yz6zcLIzMbBTD2VaHu7fr1gjXGA3w5baj5+H0w6x09TDpdaP7rDZhn0sd3y89Y9fcHbPnoYdroZ2w6dQNmg6OHfG4QyrsBC8gdZpVQxtqF4M1mjepFURThTLNgMYsePxaX2WqY6D8GSm2grK2slbGBciHZ9hOE2smNpTuxenhieCdeH06E7qTqvYnImWTbbUSPlKL1wHrgTwZ+AWq+3H5MpRkwAAAAAElFTkSuQmCC';
23
+ const SNOWY_PEAKS =
24
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEX////6/P/4+//g7vjf7fbf7fTe7fje7fTe7PTd7Pjc7Pfd7PTc6/Xc6/Pb6/fb6/Xb6vTa6/fa6/Xa6vfZ6vfZ6vba6vTZ6fXY6vfY6vbY6ffY6fXX6fbX6fTW6fbX6PXW6PbW6PTV6PbV6PXU6PbV5/XU5/bU5/TU5vXT5/bS5/XT5vTS5vXS5vTR5vXR5fXQ5fXP5fXP5PXO5PXO5PTN5PTN4/TM4/TL4/TM4vTL4vTK4vTO3OqqvtKbrsFcfGgMVnjaAAAC7klEQVR42u2Ta3eaQBCGp0kbTbBBMWDMoiCWEOsFtV4SdcP//1edYQUWwSye9kNOT58POVlm3mdmUWG/3+9et5vVYj6bjoMgGE9n88Vqs33d7SsBcX67WmI+EKBhudpWNcBu94bzlyGO9weEj0uES9zhbVcFSPMYdxzmOKi4xACbNa0/DgY91rGIDusNgjFdY72tAKzXq0U4pTyGm80m/iXDNFys1psqApwf4nwc32x+J9DRYbhDuMRrqIF5vD+zKK0R5LBYfIvFcqUEBZPRsNex9IaW0NCtTm84mqDglxIIQ3z/uEBDqydoDVrBH4d4CyUwowUsA+fXa4I67mBYtAJeQglM4jeg0wLfBLSCLt5COFcC4xffYVZ8gVSABos5/sskVAOjYNjvWrqWxGOFplvd/jD4OZkpgSAYiA1yAtpgGIzGEyW0wYCZeqOeGer1hm6yQUXBD8+1uySofU2okaBre97zSA34nuswk74GqYC+CCZzXM9/VgO+77o2M+LPUcTpUzSYXS3/DB7m4ztomrhEjb7LeAPbdb0KgOe5fTIY6Y8B51O+Xynvgeu6js2Yaep6Q6DrpsmY7biVABthXdPEFfRj3sBTl9nl8JNzLMANSGEahm5QGuNn8/zEAE8xD0g7hrfbdHgqhxO5J/B4BEMtBOv43+M5YkHuCTxk0HwuDGfgvNAAbZljQ/sMnEOhAVoSPKFVCuYBThvgXiKdcF8GlwRZB+Tz8IEhLhca4C6F8yusH0TDXYFjPjEkj+E2gecm8NsT+IkgaQBVQ6F80gA3Ai4uANkl+I0ElYlDjNwAhXyp4Zg/Cg4FAS+dwAsLiPq7PAGuEc5LJ3B+LUjLSf2Q1aGk4V1uyJVTQdYAqgZ5v7T+nu0IuXwmkAwf1yE3IJuQjZDLkiBpAK5o4BUEV6UNh5J8SR1y9VzDoZgvqasFV2frByFQNCgFX/6Q/4J/SxDJTy84/DVBFEmlSw6fRhBFUumiw2cRRJFUuuzwG+qew9oBo+n5AAAAAElFTkSuQmCC';
25
+
10
26
  const ATTACHMENTS = [
11
- {id: 1, src: 'https://lookaside.facebook.com/assets/astryx/illustrative-vertical-1.png', alt: 'River through a valley', label: 'valley.jpg'},
12
- {id: 2, src: 'https://lookaside.facebook.com/assets/astryx/illustrative-vertical-2.png', alt: 'Foggy mountain peak', label: 'mountain.jpg'},
13
- {id: 3, src: 'https://lookaside.facebook.com/assets/astryx/illustrative-vertical-3.png', alt: 'Golden retriever puppy', label: 'puppy.jpg'},
14
- {id: 4, src: 'https://lookaside.facebook.com/assets/astryx/illustrative-vertical-4.png', alt: 'Bridge at sunset', label: 'bridge.jpg'},
27
+ {
28
+ id: 1,
29
+ src: NIGHT_FOREST,
30
+ alt: 'Forest at night under a crescent moon',
31
+ label: 'forest-night.jpg',
32
+ },
33
+ {
34
+ id: 2,
35
+ src: MISTY_VALLEY,
36
+ alt: 'Misty mountain valley',
37
+ label: 'misty-valley.jpg',
38
+ },
39
+ {
40
+ id: 3,
41
+ src: GOLDEN_SUNSET,
42
+ alt: 'Golden sunset over mountains',
43
+ label: 'golden-sunset.jpg',
44
+ },
45
+ {
46
+ id: 4,
47
+ src: SNOWY_PEAKS,
48
+ alt: 'Snowy mountain peaks',
49
+ label: 'snowy-peaks.jpg',
50
+ },
15
51
  ];
16
52
 
17
53
  export default function ThumbnailGallery() {
@@ -37,9 +73,9 @@ export default function ThumbnailGallery() {
37
73
  />
38
74
  ))}
39
75
  </Stack>
40
- {selected != null && (
41
- <Text type="supporting" color="active">
42
- Previewing: {selected}
76
+ {selected && (
77
+ <Text type="supporting" color="secondary">
78
+ Selected: {selected}
43
79
  </Text>
44
80
  )}
45
81
  </Stack>
@@ -7,10 +7,47 @@ import {Thumbnail} from '@astryxdesign/core/Thumbnail';
7
7
  import {Stack} from '@astryxdesign/core/Layout';
8
8
  import {Text} from '@astryxdesign/core/Text';
9
9
 
10
+ // Demo imagery is inlined as same-origin data URIs so the example is
11
+ // self-contained. Thumbnail's remove-button overlay uses useImageMode, which
12
+ // FETCHES the image to sample pixels (APCA) for contrast — a cross-origin CDN
13
+ // URL without CORS headers cannot be fetched/sampled, so contrast detection
14
+ // fails silently in hosted previews. These scenes are shared verbatim across
15
+ // the Thumbnail examples and span a luminance ramp (night → snow) so the
16
+ // overlay's contrast adaptation is visible.
17
+ const NIGHT_FOREST =
18
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEX18+H08uDy8d/y8N/v7t/m5NXl49Tk4tPc3NDX1cnW1cnW1cjW1MjV1MjV1MfR0cXIyLzEw7m8vLC3t68wM0QvM0MvM0IvMkIuMkEtMEErLz8iJjkhJTggJTcgJDcfJDYfIzYeIjUcITMbHzIWHiwYGzIXGjEWGjAWGTAVGS8VGC8UGC4UGC0UFy4TFy0TFywTFi0SFiwRFSsQFSkNFR4QFCoPFCkPFCgPEykOEygOEigNEicNEScMESYIDw8ECgr+gzZIAAABqElEQVR42uWQ61aCQBRGD5GylNBMKS9FZgNdNE0cyUvB+79VM8MoF9Fgplau2j/8nO8c9hqAD0ngDwjeJYH1lrfBWoCtYDboNu/Zv7qQYNap1bpCN1gxFh1dNe5WAsCScWqoWttbChAKvHZZ0W6XQoIFpW8ooFmLCG2Rl1BgaZAU5Ac8yg0RlC9nngCRQDFuhQRzinWmkCuYk3lxQoFrlskVqlcTMcEFvYIKoFbNfmEFuIypWaGGkt66tqZuEbjAHZl6SQVF1WqNvpDAHVktXTvR9JbgDZii1+tZI7cYMJXkCASvksB4L5VxHmAsibzgRRIYbjgfCgFDSeBZkh8W1HMIniSBR0mOQPCQA3RgBk4M5DhNZwdEcbKgLXy5iDiZg5gApRYbqTql2FZgMxAKeEWSNUFYx3s2SfU28HPWItrpySSwE2cEdqrYtxgmxgHGScFmHrAGk8SxczIxZnNC1McFOGshShyb4yxB9sI3CXDGnH0MLsD7F5LvvptEgH12Zg1Pn2WuHsjvrwt8308t+nwxT38sgiAE8/RTeagH+vx/F8SeF0Ja8Ak14ia/LgmpZQAAAABJRU5ErkJggg==';
19
+ const MISTY_VALLEY =
20
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEWPnaqJmKSIlaKHlaGGlaKGk6CEkp6Ckp+Cj5uAj51/jZp6i5h8ipd7iZZ4hpR1g5FzhpdzhJN0g5FuhZdsgpVqgZRpgJNpf5JmfpFufYxufYtlfJBpe4tke49neo1ieo5kd4tndoZidopheY1geIxfd4tmdYVddYpcdIlbc4hgcoNebn5ZcodYcYZXb4VVboRUbYNTbIJaantWaXxRa4FQaYBPaH9NZ35UZXZQY3dMZn1LZXxKZHxMYHRKXnIoOEh3WzsBAAABeElEQVR42u3T11oCQQyG4R9FEREFqVJEgh17xbZ7/3cl29gpmQGeeOh7OF+Ss8G3EL6E8CmEdyG8CeFVCC9CeBTCgxDuhXAnhFsh3AjhSgiXQrgQwtlayHrB6TqIyHzCREMTH4rpbxip5nnkRintEUNF0oc8yqnP6Oey3ueQSnlHN6P0ro0Mi4BOSu8dA1mygnbC7G0NMdKEVszuLQXx4oZmhO3NDLlEEY05R280PDGbgD0RaBeI/Bdg7weBMkDLwF4PtBOcwH0gCJZf0DPYdd8Jo8K177hgZbjW+RN2hWffusBVnMR+XE5yfMRx5MPtOOOI0Xd+9ot/rTOiVrtepuabwcDwNFgP6przSH0dqKiOUhUfvaKcO8yVncyM0sK+psSyK4qpPUvRwlUUYrusgoaviG27YMFXNz38I0nc8PNPAFtC2BFClRFWV4cDU5g4WEE0hJ4q1PU8sgmMF0LOmKN2TBOh21RjRsxms3C5WYwrCIX+D/zBgV8HCFv4gVKf1AAAAABJRU5ErkJggg==';
21
+ const GOLDEN_SUNSET =
22
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAkFBMVEX/+uv/7sj6tFr3sVn1r1nzrVjxq1jvqVftp1frpVbpo1bnoVXln1XjnVThm1TfmVPdl1PblVLZk1LXkVLVj1HTjVHRi1DPiVDNh0/LhU/Jg07HgU7Ff03DfU3Be0y/eUy9d0u7dUu5c0u2cEq0bkqybEmwakmuaEisZkiqZEeoYkemYEakXkaiXEVgLjQ8HCjaJlMkAAABI0lEQVR42u3Mx3qDMBSE0Z/03nsvdhxjcnn/twsYjISR5MUkO5+V9N2ZYUPEpogtEdsidkTsitgTsS/iQMShiCMRxyJORJyKOBNxLuJCxKWIKxHXIm5E3Iq4E3Ev4kHEo4gnEc8iXkS8inhLymrJBO8J2UIiw0dc5sRDfEbVRWr1o/pbMMWox9yz6zcLIzMbBTD2VaHu7fr1gjXGA3w5baj5+H0w6x09TDpdaP7rDZhn0sd3y89Y9fcHbPnoYdroZ2w6dQNmg6OHfG4QyrsBC8gdZpVQxtqF4M1mjepFURThTLNgMYsePxaX2WqY6D8GSm2grK2slbGBciHZ9hOE2smNpTuxenhieCdeH06E7qTqvYnImWTbbUSPlKL1wHrgTwZ+AWq+3H5MpRkwAAAAAElFTkSuQmCC';
23
+ const SNOWY_PEAKS =
24
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEX////6/P/4+//g7vjf7fbf7fTe7fje7fTe7PTd7Pjc7Pfd7PTc6/Xc6/Pb6/fb6/Xb6vTa6/fa6/Xa6vfZ6vfZ6vba6vTZ6fXY6vfY6vbY6ffY6fXX6fbX6fTW6fbX6PXW6PbW6PTV6PbV6PXU6PbV5/XU5/bU5/TU5vXT5/bS5/XT5vTS5vXS5vTR5vXR5fXQ5fXP5fXP5PXO5PXO5PTN5PTN4/TM4/TL4/TM4vTL4vTK4vTO3OqqvtKbrsFcfGgMVnjaAAAC7klEQVR42u2Ta3eaQBCGp0kbTbBBMWDMoiCWEOsFtV4SdcP//1edYQUWwSye9kNOT58POVlm3mdmUWG/3+9et5vVYj6bjoMgGE9n88Vqs33d7SsBcX67WmI+EKBhudpWNcBu94bzlyGO9weEj0uES9zhbVcFSPMYdxzmOKi4xACbNa0/DgY91rGIDusNgjFdY72tAKzXq0U4pTyGm80m/iXDNFys1psqApwf4nwc32x+J9DRYbhDuMRrqIF5vD+zKK0R5LBYfIvFcqUEBZPRsNex9IaW0NCtTm84mqDglxIIQ3z/uEBDqydoDVrBH4d4CyUwowUsA+fXa4I67mBYtAJeQglM4jeg0wLfBLSCLt5COFcC4xffYVZ8gVSABos5/sskVAOjYNjvWrqWxGOFplvd/jD4OZkpgSAYiA1yAtpgGIzGEyW0wYCZeqOeGer1hm6yQUXBD8+1uySofU2okaBre97zSA34nuswk74GqYC+CCZzXM9/VgO+77o2M+LPUcTpUzSYXS3/DB7m4ztomrhEjb7LeAPbdb0KgOe5fTIY6Y8B51O+Xynvgeu6js2Yaep6Q6DrpsmY7biVABthXdPEFfRj3sBTl9nl8JNzLMANSGEahm5QGuNn8/zEAE8xD0g7hrfbdHgqhxO5J/B4BEMtBOv43+M5YkHuCTxk0HwuDGfgvNAAbZljQ/sMnEOhAVoSPKFVCuYBThvgXiKdcF8GlwRZB+Tz8IEhLhca4C6F8yusH0TDXYFjPjEkj+E2gecm8NsT+IkgaQBVQ6F80gA3Ai4uANkl+I0ElYlDjNwAhXyp4Zg/Cg4FAS+dwAsLiPq7PAGuEc5LJ3B+LUjLSf2Q1aGk4V1uyJVTQdYAqgZ5v7T+nu0IuXwmkAwf1yE3IJuQjZDLkiBpAK5o4BUEV6UNh5J8SR1y9VzDoZgvqasFV2frByFQNCgFX/6Q/4J/SxDJTy84/DVBFEmlSw6fRhBFUumiw2cRRJFUuuzwG+qew9oBo+n5AAAAAElFTkSuQmCC';
25
+
10
26
  const IMAGES = [
11
- {id: 1, src: 'https://lookaside.facebook.com/assets/astryx/moody-scene-vertical-1.png', alt: 'Dark cityscape at night', label: 'dark-city.jpg'},
12
- {id: 2, src: 'https://lookaside.facebook.com/assets/astryx/moody-scene-vertical-2.png', alt: 'Bright snowy landscape', label: 'snow.jpg'},
13
- {id: 3, src: 'https://lookaside.facebook.com/assets/astryx/moody-home-vertical-1.png', alt: 'Warm sunset over mountains', label: 'sunset.jpg'},
27
+ {
28
+ id: 1,
29
+ src: NIGHT_FOREST,
30
+ alt: 'Forest at night under a crescent moon',
31
+ label: 'forest-night.jpg',
32
+ },
33
+ {
34
+ id: 2,
35
+ src: MISTY_VALLEY,
36
+ alt: 'Misty mountain valley',
37
+ label: 'misty-valley.jpg',
38
+ },
39
+ {
40
+ id: 3,
41
+ src: GOLDEN_SUNSET,
42
+ alt: 'Golden sunset over mountains',
43
+ label: 'golden-sunset.jpg',
44
+ },
45
+ {
46
+ id: 4,
47
+ src: SNOWY_PEAKS,
48
+ alt: 'Snowy mountain peaks',
49
+ label: 'snowy-peaks.jpg',
50
+ },
14
51
  ];
15
52
 
16
53
  export default function ThumbnailRemovable() {
@@ -4,12 +4,13 @@
4
4
 
5
5
  import {Thumbnail} from '@astryxdesign/core/Thumbnail';
6
6
 
7
+ // Inline same-origin data URI so the example is self-contained — see
8
+ // ThumbnailRemovable for why image-backed Thumbnails avoid cross-origin URLs.
9
+ const SNOWY_PEAKS =
10
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEX////6/P/4+//g7vjf7fbf7fTe7fje7fTe7PTd7Pjc7Pfd7PTc6/Xc6/Pb6/fb6/Xb6vTa6/fa6/Xa6vfZ6vfZ6vba6vTZ6fXY6vfY6vbY6ffY6fXX6fbX6fTW6fbX6PXW6PbW6PTV6PbV6PXU6PbV5/XU5/bU5/TU5vXT5/bS5/XT5vTS5vXS5vTR5vXR5fXQ5fXP5fXP5PXO5PXO5PTN5PTN4/TM4/TL4/TM4vTL4vTK4vTO3OqqvtKbrsFcfGgMVnjaAAAC7klEQVR42u2Ta3eaQBCGp0kbTbBBMWDMoiCWEOsFtV4SdcP//1edYQUWwSye9kNOT58POVlm3mdmUWG/3+9et5vVYj6bjoMgGE9n88Vqs33d7SsBcX67WmI+EKBhudpWNcBu94bzlyGO9weEj0uES9zhbVcFSPMYdxzmOKi4xACbNa0/DgY91rGIDusNgjFdY72tAKzXq0U4pTyGm80m/iXDNFys1psqApwf4nwc32x+J9DRYbhDuMRrqIF5vD+zKK0R5LBYfIvFcqUEBZPRsNex9IaW0NCtTm84mqDglxIIQ3z/uEBDqydoDVrBH4d4CyUwowUsA+fXa4I67mBYtAJeQglM4jeg0wLfBLSCLt5COFcC4xffYVZ8gVSABos5/sskVAOjYNjvWrqWxGOFplvd/jD4OZkpgSAYiA1yAtpgGIzGEyW0wYCZeqOeGer1hm6yQUXBD8+1uySofU2okaBre97zSA34nuswk74GqYC+CCZzXM9/VgO+77o2M+LPUcTpUzSYXS3/DB7m4ztomrhEjb7LeAPbdb0KgOe5fTIY6Y8B51O+Xynvgeu6js2Yaep6Q6DrpsmY7biVABthXdPEFfRj3sBTl9nl8JNzLMANSGEahm5QGuNn8/zEAE8xD0g7hrfbdHgqhxO5J/B4BEMtBOv43+M5YkHuCTxk0HwuDGfgvNAAbZljQ/sMnEOhAVoSPKFVCuYBThvgXiKdcF8GlwRZB+Tz8IEhLhca4C6F8yusH0TDXYFjPjEkj+E2gecm8NsT+IkgaQBVQ6F80gA3Ai4uANkl+I0ElYlDjNwAhXyp4Zg/Cg4FAS+dwAsLiPq7PAGuEc5LJ3B+LUjLSf2Q1aGk4V1uyJVTQdYAqgZ5v7T+nu0IuXwmkAwf1yE3IJuQjZDLkiBpAK5o4BUEV6UNh5J8SR1y9VzDoZgvqasFV2frByFQNCgFX/6Q/4J/SxDJTy84/DVBFEmlSw6fRhBFUumiw2cRRJFUuuzwG+qew9oBo+n5AAAAAElFTkSuQmCC';
11
+
7
12
  export default function ThumbnailShowcase() {
8
13
  return (
9
- <Thumbnail
10
- src="https://lookaside.facebook.com/assets/astryx/moody-scene-vertical-2.png"
11
- alt="Sample image"
12
- label="photo.jpg"
13
- />
14
+ <Thumbnail src={SNOWY_PEAKS} alt="Snowy mountain peaks" label="snowy-peaks.jpg" />
14
15
  );
15
16
  }
@@ -6,6 +6,11 @@ import {Thumbnail} from '@astryxdesign/core/Thumbnail';
6
6
  import {Stack} from '@astryxdesign/core/Layout';
7
7
  import {Text} from '@astryxdesign/core/Text';
8
8
 
9
+ // Inline same-origin data URI so the example is self-contained — see
10
+ // ThumbnailRemovable for why image-backed Thumbnails avoid cross-origin URLs.
11
+ const MISTY_VALLEY =
12
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAwFBMVEWPnaqJmKSIlaKHlaGGlaKGk6CEkp6Ckp+Cj5uAj51/jZp6i5h8ipd7iZZ4hpR1g5FzhpdzhJN0g5FuhZdsgpVqgZRpgJNpf5JmfpFufYxufYtlfJBpe4tke49neo1ieo5kd4tndoZidopheY1geIxfd4tmdYVddYpcdIlbc4hgcoNebn5ZcodYcYZXb4VVboRUbYNTbIJaantWaXxRa4FQaYBPaH9NZ35UZXZQY3dMZn1LZXxKZHxMYHRKXnIoOEh3WzsBAAABeElEQVR42u3T11oCQQyG4R9FEREFqVJEgh17xbZ7/3cl29gpmQGeeOh7OF+Ss8G3EL6E8CmEdyG8CeFVCC9CeBTCgxDuhXAnhFsh3AjhSgiXQrgQwtlayHrB6TqIyHzCREMTH4rpbxip5nnkRintEUNF0oc8yqnP6Oey3ueQSnlHN6P0ro0Mi4BOSu8dA1mygnbC7G0NMdKEVszuLQXx4oZmhO3NDLlEEY05R280PDGbgD0RaBeI/Bdg7weBMkDLwF4PtBOcwH0gCJZf0DPYdd8Jo8K177hgZbjW+RN2hWffusBVnMR+XE5yfMRx5MPtOOOI0Xd+9ot/rTOiVrtepuabwcDwNFgP6przSH0dqKiOUhUfvaKcO8yVncyM0sK+psSyK4qpPUvRwlUUYrusgoaviG27YMFXNz38I0nc8PNPAFtC2BFClRFWV4cDU5g4WEE0hJ4q1PU8sgmMF0LOmKN2TBOh21RjRsxms3C5WYwrCIX+D/zBgV8HCFv4gVKf1AAAAABJRU5ErkJggg==';
13
+
9
14
  export default function ThumbnailStates() {
10
15
  return (
11
16
  <Stack direction="vertical" gap={4}>
@@ -28,10 +33,10 @@ export default function ThumbnailStates() {
28
33
  </Stack>
29
34
  <Stack direction="vertical" gap={1} hAlign="center">
30
35
  <Thumbnail
31
- src="https://lookaside.facebook.com/assets/astryx/moody-home-vertical-1.png"
32
- alt="Mountain landscape"
36
+ src={MISTY_VALLEY}
37
+ alt="Misty mountain valley"
33
38
  isLoading
34
- label="landscape.jpg"
39
+ label="misty-valley.jpg"
35
40
  />
36
41
  <Text type="supporting" color="secondary">
37
42
  Uploading
@@ -39,9 +44,9 @@ export default function ThumbnailStates() {
39
44
  </Stack>
40
45
  <Stack direction="vertical" gap={1} hAlign="center">
41
46
  <Thumbnail
42
- src="https://lookaside.facebook.com/assets/astryx/moody-home-vertical-1.png"
43
- alt="Mountain landscape"
44
- label="landscape.jpg"
47
+ src={MISTY_VALLEY}
48
+ alt="Misty mountain valley"
49
+ label="misty-valley.jpg"
45
50
  />
46
51
  <Text type="supporting" color="secondary">
47
52
  Loaded
@@ -31,9 +31,9 @@ export default function TimestampColors() {
31
31
  </Stack>
32
32
  <Stack direction="vertical" gap={1}>
33
33
  <Text type="supporting" color="secondary">
34
- Active
34
+ Accent
35
35
  </Text>
36
- <Timestamp value={DATE} format="date_time" color="active" />
36
+ <Timestamp value={DATE} format="date_time" color="accent" />
37
37
  </Stack>
38
38
  </Stack>
39
39
  );
@@ -3,7 +3,7 @@
3
3
  /** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
4
4
  export const doc = {
5
5
  type: 'block',
6
- exampleFor: 'ToggleButton',
6
+ exampleFor: 'ToggleButtonGroup',
7
7
  name: 'ToggleButton — Group',
8
8
  displayName: 'ToggleButton — Group',
9
9
  description: 'Toggle button groups in single-select and multi-select modes. Single selection acts as a view mode switcher; multiple selection forms a formatting toolbar.',
@@ -2,8 +2,9 @@
2
2
 
3
3
  'use client';
4
4
 
5
+ import {useState} from 'react';
5
6
  import {Tokenizer} from '@astryxdesign/core/Tokenizer';
6
- import type {SearchSource} from '@astryxdesign/core/Typeahead';
7
+ import type {SearchableItem, SearchSource} from '@astryxdesign/core/Typeahead';
7
8
 
8
9
  const source: SearchSource = {
9
10
  search: () => [],
@@ -11,16 +12,17 @@ const source: SearchSource = {
11
12
  };
12
13
 
13
14
  export default function TokenizerShowcase() {
15
+ const [value, setValue] = useState<SearchableItem[]>([
16
+ {id: '1', label: 'Design'},
17
+ {id: '2', label: 'Engineering'},
18
+ ]);
14
19
  return (
15
20
  <Tokenizer
16
21
  label="Tags"
17
22
  placeholder="Search..."
18
23
  searchSource={source}
19
- value={[
20
- {id: '1', label: 'Design'},
21
- {id: '2', label: 'Engineering'},
22
- ]}
23
- onChange={() => {}}
24
+ value={value}
25
+ onChange={setValue}
24
26
  style={{width: 400}}
25
27
  />
26
28
  );
@@ -7,8 +7,8 @@ export const doc = {
7
7
  name: 'Toolbar — Table Filter',
8
8
  displayName: 'Toolbar — Table Filter',
9
9
  description:
10
- 'A compact toolbar with a search input, filter buttons, and an overflow menu. Use above a data table to let users search, filter, and access view options.',
10
+ 'A compact toolbar with a search input, Status and Priority filter selectors, and an overflow menu. Use above a data table to let users search, filter, and access view options.',
11
11
  isReady: true,
12
12
  aspectRatio: 16 / 9,
13
- componentsUsed: ['Toolbar', 'Button', 'Icon', 'TextInput', 'MoreMenu', 'Layout', 'Table'],
13
+ componentsUsed: ['Toolbar', 'Selector', 'TextInput', 'MoreMenu', 'Layout', 'Table'],
14
14
  };
@@ -2,19 +2,20 @@
2
2
 
3
3
  'use client';
4
4
 
5
+ import {useState} from 'react';
5
6
  import {Toolbar} from '@astryxdesign/core/Toolbar';
6
- import {Button} from '@astryxdesign/core/Button';
7
- import {Icon} from '@astryxdesign/core/Icon';
7
+ import {Selector} from '@astryxdesign/core/Selector';
8
8
  import {TextInput} from '@astryxdesign/core/TextInput';
9
9
  import {MoreMenu} from '@astryxdesign/core/MoreMenu';
10
10
  import {Stack} from '@astryxdesign/core/Layout';
11
11
  import {Table} from '@astryxdesign/core/Table';
12
- import {
13
- MagnifyingGlassIcon,
14
- ChevronDownIcon,
15
- } from '@heroicons/react/24/outline';
12
+ import {MagnifyingGlassIcon} from '@heroicons/react/24/outline';
16
13
 
17
14
  export default function ToolbarTableFilter() {
15
+ const [search, setSearch] = useState('');
16
+ const [status, setStatus] = useState<string | null>(null);
17
+ const [priority, setPriority] = useState<string | null>(null);
18
+
18
19
  return (
19
20
  <Stack direction="vertical" style={{width: '100%'}}>
20
21
  <Toolbar
@@ -27,19 +28,27 @@ export default function ToolbarTableFilter() {
27
28
  label="Search"
28
29
  isLabelHidden
29
30
  placeholder="Search..."
30
- value=""
31
- onChange={() => {}}
31
+ value={search}
32
+ onChange={setSearch}
32
33
  startIcon={MagnifyingGlassIcon}
33
34
  />
34
- <Button
35
+ <Selector
35
36
  label="Status"
36
- variant="secondary"
37
- endContent={<Icon icon={ChevronDownIcon} />}
37
+ isLabelHidden
38
+ placeholder="Status"
39
+ hasClear
40
+ value={status}
41
+ onChange={setStatus}
42
+ options={['Open', 'In progress', 'Done']}
38
43
  />
39
- <Button
44
+ <Selector
40
45
  label="Priority"
41
- variant="secondary"
42
- endContent={<Icon icon={ChevronDownIcon} />}
46
+ isLabelHidden
47
+ placeholder="Priority"
48
+ hasClear
49
+ value={priority}
50
+ onChange={setPriority}
51
+ options={['High', 'Medium', 'Low']}
43
52
  />
44
53
  </>
45
54
  }