@teamofsilicons/silicon-ui 0.0.0-stage → 0.2.0
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.
- package/LICENSE +22 -0
- package/README.md +81 -2
- package/check.mjs +236 -0
- package/components/accordion/accordion.d.ts +13 -0
- package/components/accordion/accordion.js +35 -0
- package/components/accordion/accordion.module.css +15 -0
- package/components/action-button/action-button.d.ts +11 -0
- package/components/action-button/action-button.js +57 -0
- package/components/action-button/action-button.module.css +15 -0
- package/components/activity-heatmap/activity-heatmap.d.ts +37 -0
- package/components/activity-heatmap/activity-heatmap.js +303 -0
- package/components/activity-heatmap/activity-heatmap.module.css +83 -0
- package/components/alert/alert.d.ts +13 -0
- package/components/alert/alert.js +79 -0
- package/components/alert/alert.module.css +15 -0
- package/components/animated-counter/animated-counter.d.ts +13 -0
- package/components/animated-counter/animated-counter.js +73 -0
- package/components/animated-counter/animated-counter.module.css +12 -0
- package/components/announcement-bar/announcement-bar.d.ts +53 -0
- package/components/announcement-bar/announcement-bar.js +187 -0
- package/components/announcement-bar/announcement-bar.module.css +60 -0
- package/components/avatar/avatar.d.ts +8 -0
- package/components/avatar/avatar.js +18 -0
- package/components/avatar/avatar.module.css +60 -0
- package/components/avatar-group/avatar-group.d.ts +13 -0
- package/components/avatar-group/avatar-group.js +24 -0
- package/components/avatar-group/avatar-group.module.css +27 -0
- package/components/badge/badge.d.ts +10 -0
- package/components/badge/badge.js +66 -0
- package/components/badge/badge.module.css +52 -0
- package/components/bar-chart/bar-chart.d.ts +32 -0
- package/components/bar-chart/bar-chart.js +327 -0
- package/components/bar-chart/bar-chart.module.css +49 -0
- package/components/billing-toggle/billing-toggle.d.ts +35 -0
- package/components/billing-toggle/billing-toggle.js +83 -0
- package/components/billing-toggle/billing-toggle.module.css +139 -0
- package/components/bottom-sheet/bottom-sheet.d.ts +27 -0
- package/components/bottom-sheet/bottom-sheet.js +386 -0
- package/components/bottom-sheet/bottom-sheet.module.css +69 -0
- package/components/breadcrumb/breadcrumb.d.ts +13 -0
- package/components/breadcrumb/breadcrumb.js +19 -0
- package/components/breadcrumb/breadcrumb.module.css +12 -0
- package/components/brush-chart/brush-chart.d.ts +45 -0
- package/components/brush-chart/brush-chart.js +426 -0
- package/components/brush-chart/brush-chart.module.css +69 -0
- package/components/button/button.d.ts +10 -0
- package/components/button/button.js +97 -0
- package/components/button/button.module.css +39 -0
- package/components/button-group/button-group.d.ts +59 -0
- package/components/button-group/button-group.js +280 -0
- package/components/button-group/button-group.module.css +87 -0
- package/components/button-link/button-link.d.ts +24 -0
- package/components/button-link/button-link.js +11 -0
- package/components/button-link/button-link.module.css +7 -0
- package/components/calendar/calendar.d.ts +22 -0
- package/components/calendar/calendar.js +253 -0
- package/components/calendar/calendar.module.css +51 -0
- package/components/card/card.d.ts +19 -0
- package/components/card/card.js +108 -0
- package/components/card/card.module.css +51 -0
- package/components/card-stack/card-stack.d.ts +34 -0
- package/components/card-stack/card-stack.js +291 -0
- package/components/card-stack/card-stack.module.css +63 -0
- package/components/carousel/carousel.d.ts +28 -0
- package/components/carousel/carousel.js +332 -0
- package/components/carousel/carousel.module.css +42 -0
- package/components/chat-thread/chat-thread.d.ts +135 -0
- package/components/chat-thread/chat-thread.js +406 -0
- package/components/chat-thread/chat-thread.module.css +242 -0
- package/components/checkbox/checkbox.d.ts +7 -0
- package/components/checkbox/checkbox.js +23 -0
- package/components/checkbox/checkbox.module.css +15 -0
- package/components/chip-group/chip-group.d.ts +24 -0
- package/components/chip-group/chip-group.js +196 -0
- package/components/chip-group/chip-group.module.css +38 -0
- package/components/code-block/code-block.d.ts +11 -0
- package/components/code-block/code-block.js +133 -0
- package/components/code-block/code-block.module.css +43 -0
- package/components/color-picker/color-picker.d.ts +40 -0
- package/components/color-picker/color-picker.js +449 -0
- package/components/color-picker/color-picker.module.css +111 -0
- package/components/combobox/combobox.d.ts +19 -0
- package/components/combobox/combobox.js +167 -0
- package/components/combobox/combobox.module.css +28 -0
- package/components/command-palette/command-palette.d.ts +20 -0
- package/components/command-palette/command-palette.js +151 -0
- package/components/command-palette/command-palette.module.css +94 -0
- package/components/comment-thread/comment-thread.d.ts +120 -0
- package/components/comment-thread/comment-thread.js +343 -0
- package/components/comment-thread/comment-thread.module.css +195 -0
- package/components/confirm-morph/confirm-morph.d.ts +65 -0
- package/components/confirm-morph/confirm-morph.js +355 -0
- package/components/confirm-morph/confirm-morph.module.css +146 -0
- package/components/context-menu/context-menu.d.ts +18 -0
- package/components/context-menu/context-menu.js +148 -0
- package/components/context-menu/context-menu.module.css +17 -0
- package/components/copy-button/copy-button.d.ts +10 -0
- package/components/copy-button/copy-button.js +69 -0
- package/components/copy-button/copy-button.module.css +21 -0
- package/components/countdown/countdown.d.ts +31 -0
- package/components/countdown/countdown.js +177 -0
- package/components/countdown/countdown.module.css +49 -0
- package/components/date-picker/date-picker.d.ts +17 -0
- package/components/date-picker/date-picker.js +100 -0
- package/components/date-picker/date-picker.module.css +34 -0
- package/components/date-range-picker/date-range-picker.d.ts +44 -0
- package/components/date-range-picker/date-range-picker.js +534 -0
- package/components/date-range-picker/date-range-picker.module.css +214 -0
- package/components/dialog/dialog.d.ts +11 -0
- package/components/dialog/dialog.js +52 -0
- package/components/dialog/dialog.module.css +36 -0
- package/components/donut-chart/donut-chart.d.ts +46 -0
- package/components/donut-chart/donut-chart.js +382 -0
- package/components/donut-chart/donut-chart.module.css +48 -0
- package/components/drawer/drawer.d.ts +20 -0
- package/components/drawer/drawer.js +111 -0
- package/components/drawer/drawer.module.css +97 -0
- package/components/dropdown-menu/dropdown-menu.d.ts +16 -0
- package/components/dropdown-menu/dropdown-menu.js +39 -0
- package/components/dropdown-menu/dropdown-menu.module.css +38 -0
- package/components/elastic-slider/elastic-slider.d.ts +39 -0
- package/components/elastic-slider/elastic-slider.js +261 -0
- package/components/elastic-slider/elastic-slider.module.css +40 -0
- package/components/empty-state/empty-state.d.ts +12 -0
- package/components/empty-state/empty-state.js +69 -0
- package/components/empty-state/empty-state.module.css +13 -0
- package/components/expandable-card/expandable-card.d.ts +13 -0
- package/components/expandable-card/expandable-card.js +89 -0
- package/components/expandable-card/expandable-card.module.css +22 -0
- package/components/expanding-button-group/expanding-button-group.d.ts +41 -0
- package/components/expanding-button-group/expanding-button-group.js +258 -0
- package/components/expanding-button-group/expanding-button-group.module.css +44 -0
- package/components/expanding-search/expanding-search.d.ts +38 -0
- package/components/expanding-search/expanding-search.js +242 -0
- package/components/expanding-search/expanding-search.module.css +52 -0
- package/components/file-dropzone/file-dropzone.d.ts +44 -0
- package/components/file-dropzone/file-dropzone.js +379 -0
- package/components/file-dropzone/file-dropzone.module.css +115 -0
- package/components/file-upload/file-upload.d.ts +22 -0
- package/components/file-upload/file-upload.js +192 -0
- package/components/file-upload/file-upload.module.css +49 -0
- package/components/filter-toolbar/filter-toolbar.d.ts +48 -0
- package/components/filter-toolbar/filter-toolbar.js +446 -0
- package/components/filter-toolbar/filter-toolbar.module.css +70 -0
- package/components/floating-button-group/floating-button-group.d.ts +51 -0
- package/components/floating-button-group/floating-button-group.js +215 -0
- package/components/floating-button-group/floating-button-group.module.css +74 -0
- package/components/gauge/gauge.d.ts +18 -0
- package/components/gauge/gauge.js +101 -0
- package/components/gauge/gauge.module.css +22 -0
- package/components/hold-to-confirm/hold-to-confirm.d.ts +25 -0
- package/components/hold-to-confirm/hold-to-confirm.js +165 -0
- package/components/hold-to-confirm/hold-to-confirm.module.css +20 -0
- package/components/hover-card/hover-card.d.ts +38 -0
- package/components/hover-card/hover-card.js +145 -0
- package/components/hover-card/hover-card.module.css +20 -0
- package/components/image-compare/image-compare.d.ts +27 -0
- package/components/image-compare/image-compare.js +267 -0
- package/components/image-compare/image-compare.module.css +25 -0
- package/components/in-view-title/in-view-title.d.ts +17 -0
- package/components/in-view-title/in-view-title.js +70 -0
- package/components/in-view-title/in-view-title.module.css +13 -0
- package/components/inline-edit/inline-edit.d.ts +27 -0
- package/components/inline-edit/inline-edit.js +315 -0
- package/components/inline-edit/inline-edit.module.css +73 -0
- package/components/input/input.d.ts +7 -0
- package/components/input/input.js +60 -0
- package/components/input/input.module.css +22 -0
- package/components/json-viewer/json-viewer.d.ts +48 -0
- package/components/json-viewer/json-viewer.js +463 -0
- package/components/json-viewer/json-viewer.module.css +92 -0
- package/components/line-chart/line-chart.d.ts +58 -0
- package/components/line-chart/line-chart.js +383 -0
- package/components/line-chart/line-chart.module.css +60 -0
- package/components/mention-input/mention-input.d.ts +72 -0
- package/components/mention-input/mention-input.js +417 -0
- package/components/mention-input/mention-input.module.css +134 -0
- package/components/metric-card/metric-card.d.ts +8 -0
- package/components/metric-card/metric-card.js +44 -0
- package/components/metric-card/metric-card.module.css +18 -0
- package/components/money-input/money-input.d.ts +48 -0
- package/components/money-input/money-input.js +503 -0
- package/components/money-input/money-input.module.css +77 -0
- package/components/morph-select/morph-select.d.ts +50 -0
- package/components/morph-select/morph-select.js +395 -0
- package/components/morph-select/morph-select.module.css +88 -0
- package/components/multi-select/multi-select.d.ts +18 -0
- package/components/multi-select/multi-select.js +105 -0
- package/components/multi-select/multi-select.module.css +37 -0
- package/components/notification-center/notification-center.d.ts +24 -0
- package/components/notification-center/notification-center.js +146 -0
- package/components/notification-center/notification-center.module.css +79 -0
- package/components/number-field/number-field.d.ts +41 -0
- package/components/number-field/number-field.js +455 -0
- package/components/number-field/number-field.module.css +71 -0
- package/components/otp-input/otp-input.d.ts +13 -0
- package/components/otp-input/otp-input.js +129 -0
- package/components/otp-input/otp-input.module.css +28 -0
- package/components/pagination/pagination.d.ts +7 -0
- package/components/pagination/pagination.js +60 -0
- package/components/pagination/pagination.module.css +16 -0
- package/components/password-field/password-field.d.ts +14 -0
- package/components/password-field/password-field.js +51 -0
- package/components/password-field/password-field.module.css +27 -0
- package/components/password-strength/password-strength.d.ts +107 -0
- package/components/password-strength/password-strength.js +161 -0
- package/components/password-strength/password-strength.module.css +57 -0
- package/components/phone-input/phone-input.d.ts +73 -0
- package/components/phone-input/phone-input.js +612 -0
- package/components/phone-input/phone-input.module.css +98 -0
- package/components/popover/popover.d.ts +6 -0
- package/components/popover/popover.js +19 -0
- package/components/popover/popover.module.css +32 -0
- package/components/progress/progress.d.ts +8 -0
- package/components/progress/progress.js +42 -0
- package/components/progress/progress.module.css +1 -0
- package/components/radio-cards/radio-cards.d.ts +35 -0
- package/components/radio-cards/radio-cards.js +110 -0
- package/components/radio-cards/radio-cards.module.css +84 -0
- package/components/radio-group/radio-group.d.ts +13 -0
- package/components/radio-group/radio-group.js +55 -0
- package/components/radio-group/radio-group.module.css +30 -0
- package/components/resizable-panels/resizable-panels.d.ts +37 -0
- package/components/resizable-panels/resizable-panels.js +338 -0
- package/components/resizable-panels/resizable-panels.module.css +40 -0
- package/components/rich-text-editor/rich-text-editor.d.ts +55 -0
- package/components/rich-text-editor/rich-text-editor.js +1733 -0
- package/components/rich-text-editor/rich-text-editor.module.css +201 -0
- package/components/ridgeline/ridgeline.d.ts +37 -0
- package/components/ridgeline/ridgeline.js +346 -0
- package/components/ridgeline/ridgeline.module.css +58 -0
- package/components/scroll-area/scroll-area.d.ts +40 -0
- package/components/scroll-area/scroll-area.js +153 -0
- package/components/scroll-area/scroll-area.module.css +79 -0
- package/components/search-field/search-field.d.ts +7 -0
- package/components/search-field/search-field.js +20 -0
- package/components/search-field/search-field.module.css +17 -0
- package/components/segmented-control/segmented-control.d.ts +16 -0
- package/components/segmented-control/segmented-control.js +56 -0
- package/components/segmented-control/segmented-control.module.css +15 -0
- package/components/select/select.d.ts +15 -0
- package/components/select/select.js +36 -0
- package/components/select/select.module.css +35 -0
- package/components/shortcut-recorder/shortcut-recorder.d.ts +108 -0
- package/components/shortcut-recorder/shortcut-recorder.js +380 -0
- package/components/shortcut-recorder/shortcut-recorder.module.css +85 -0
- package/components/signature-pad/signature-pad.d.ts +72 -0
- package/components/signature-pad/signature-pad.js +450 -0
- package/components/signature-pad/signature-pad.module.css +84 -0
- package/components/skeleton/skeleton.d.ts +13 -0
- package/components/skeleton/skeleton.js +53 -0
- package/components/skeleton/skeleton.module.css +11 -0
- package/components/slider/slider.d.ts +45 -0
- package/components/slider/slider.js +347 -0
- package/components/slider/slider.module.css +45 -0
- package/components/slope-chart/slope-chart.d.ts +38 -0
- package/components/slope-chart/slope-chart.js +183 -0
- package/components/slope-chart/slope-chart.module.css +51 -0
- package/components/slot-text/slot-text.d.ts +26 -0
- package/components/slot-text/slot-text.js +119 -0
- package/components/slot-text/slot-text.module.css +19 -0
- package/components/sortable-data-table/sortable-data-table.d.ts +38 -0
- package/components/sortable-data-table/sortable-data-table.js +240 -0
- package/components/sortable-data-table/sortable-data-table.module.css +116 -0
- package/components/sparkline/sparkline.d.ts +19 -0
- package/components/sparkline/sparkline.js +239 -0
- package/components/sparkline/sparkline.module.css +26 -0
- package/components/split-button/split-button.d.ts +19 -0
- package/components/split-button/split-button.js +33 -0
- package/components/split-button/split-button.module.css +51 -0
- package/components/stepper/stepper.d.ts +36 -0
- package/components/stepper/stepper.js +108 -0
- package/components/stepper/stepper.module.css +74 -0
- package/components/streamgraph/streamgraph.d.ts +51 -0
- package/components/streamgraph/streamgraph.js +376 -0
- package/components/streamgraph/streamgraph.module.css +55 -0
- package/components/swipe-actions/swipe-actions.d.ts +35 -0
- package/components/swipe-actions/swipe-actions.js +330 -0
- package/components/swipe-actions/swipe-actions.module.css +64 -0
- package/components/switch/switch.d.ts +7 -0
- package/components/switch/switch.js +45 -0
- package/components/switch/switch.module.css +12 -0
- package/components/tabs/tabs.d.ts +8 -0
- package/components/tabs/tabs.js +138 -0
- package/components/tabs/tabs.module.css +27 -0
- package/components/tag-input/tag-input.d.ts +15 -0
- package/components/tag-input/tag-input.js +150 -0
- package/components/tag-input/tag-input.module.css +34 -0
- package/components/text-morph/text-morph.d.ts +13 -0
- package/components/text-morph/text-morph.js +69 -0
- package/components/text-morph/text-morph.module.css +8 -0
- package/components/text-reveal/text-reveal.d.ts +14 -0
- package/components/text-reveal/text-reveal.js +20 -0
- package/components/text-reveal/text-reveal.module.css +13 -0
- package/components/text-shimmer/text-shimmer.d.ts +18 -0
- package/components/text-shimmer/text-shimmer.js +90 -0
- package/components/text-shimmer/text-shimmer.module.css +9 -0
- package/components/textarea/textarea.d.ts +7 -0
- package/components/textarea/textarea.js +56 -0
- package/components/textarea/textarea.module.css +22 -0
- package/components/theme-switch/theme-switch.d.ts +11 -0
- package/components/theme-switch/theme-switch.js +37 -0
- package/components/theme-switch/theme-switch.module.css +31 -0
- package/components/time-picker/time-picker.d.ts +13 -0
- package/components/time-picker/time-picker.js +95 -0
- package/components/time-picker/time-picker.module.css +23 -0
- package/components/timeline/timeline.d.ts +47 -0
- package/components/timeline/timeline.js +171 -0
- package/components/timeline/timeline.module.css +51 -0
- package/components/toast/toast.d.ts +8 -0
- package/components/toast/toast.js +118 -0
- package/components/toast/toast.module.css +100 -0
- package/components/toast-stack/toast-stack.d.ts +57 -0
- package/components/toast-stack/toast-stack.js +399 -0
- package/components/toast-stack/toast-stack.module.css +61 -0
- package/components/tooltip/tooltip.d.ts +8 -0
- package/components/tooltip/tooltip.js +62 -0
- package/components/tooltip/tooltip.module.css +18 -0
- package/components/tree-view/tree-view.d.ts +17 -0
- package/components/tree-view/tree-view.js +147 -0
- package/components/tree-view/tree-view.module.css +22 -0
- package/components/treemap/treemap.d.ts +35 -0
- package/components/treemap/treemap.js +375 -0
- package/components/treemap/treemap.module.css +59 -0
- package/components/usage-meter/usage-meter.d.ts +24 -0
- package/components/usage-meter/usage-meter.js +296 -0
- package/components/usage-meter/usage-meter.module.css +61 -0
- package/components/user-menu/user-menu.d.ts +62 -0
- package/components/user-menu/user-menu.js +316 -0
- package/components/user-menu/user-menu.module.css +97 -0
- package/components/waffle-chart/waffle-chart.d.ts +39 -0
- package/components/waffle-chart/waffle-chart.js +244 -0
- package/components/waffle-chart/waffle-chart.module.css +52 -0
- package/components.json +1330 -0
- package/eslint.js +53 -0
- package/lib/escape.d.ts +19 -0
- package/lib/escape.js +50 -0
- package/lib/media.d.ts +353 -0
- package/lib/media.js +65 -0
- package/lib/menu-highlight.d.ts +28 -0
- package/lib/menu-highlight.js +43 -0
- package/lib/motion-tokens.d.ts +60 -0
- package/lib/motion-tokens.js +25 -0
- package/lib/return-focus.d.ts +9 -0
- package/lib/return-focus.js +53 -0
- package/lib/silicon-provider.d.ts +60 -0
- package/lib/silicon-provider.js +47 -0
- package/lib/use-copy-feedback.d.ts +8 -0
- package/lib/use-copy-feedback.js +36 -0
- package/lib/use-today.d.ts +2 -0
- package/lib/use-today.js +29 -0
- package/package.json +76 -4
- package/styles.css +218 -0
- package/styles.css.d.ts +1 -0
package/eslint.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rules that keep an app on Silicon UI: build with its components, never re-create or restyle them.
|
|
3
|
+
*
|
|
4
|
+
* // eslint.config.mjs
|
|
5
|
+
* import siliconUi from "@teamofsilicons/silicon-ui/eslint";
|
|
6
|
+
* export default [...yourConfig, ...siliconUi()];
|
|
7
|
+
*
|
|
8
|
+
* Options:
|
|
9
|
+
* files globs the rules apply to (default: every .tsx and .jsx file)
|
|
10
|
+
* ignores globs they skip (for example a vendored exception)
|
|
11
|
+
* elements override the element → component table below
|
|
12
|
+
*
|
|
13
|
+
* This config sets `no-restricted-syntax` and `no-restricted-imports`. ESLint replaces, not merges, a rule's options, so
|
|
14
|
+
* if your own config also sets either rule, put this one last and move your entries into `extraSyntax` / `extraImports`.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Raw elements Silicon UI already has a component for. */
|
|
18
|
+
export const ELEMENTS = {
|
|
19
|
+
button: "Use Button (actions) or ButtonLink (navigation) from @teamofsilicons/silicon-ui.",
|
|
20
|
+
input: "Use Input, Checkbox, Switch, RadioGroup, Slider or another field from @teamofsilicons/silicon-ui.",
|
|
21
|
+
select: "Use Select, Combobox or MorphSelect from @teamofsilicons/silicon-ui.",
|
|
22
|
+
textarea: "Use Textarea from @teamofsilicons/silicon-ui.",
|
|
23
|
+
dialog: "Use Dialog, Drawer or BottomSheet from @teamofsilicons/silicon-ui.",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const IMPORTS = [
|
|
27
|
+
{
|
|
28
|
+
regex: "^@teamofsilicons/silicon-ui/(?!styles\\.css$).*\\.css$",
|
|
29
|
+
message: "Do not borrow a Silicon UI component's CSS. Render the component; if it lacks a variant, add it to the library.",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
regex: "(^|/)components/silicon-ui/(?!blocks/)",
|
|
33
|
+
message: "Import components from @teamofsilicons/silicon-ui, not from a local copy. Never copy library code into the app.",
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
export default function siliconUi({ files = ["**/*.{tsx,jsx}"], ignores = [], elements = ELEMENTS, extraSyntax = [], extraImports = [] } = {}) {
|
|
38
|
+
const syntax = Object.entries(elements).map(([name, message]) => ({
|
|
39
|
+
selector: `JSXOpeningElement[name.type="JSXIdentifier"][name.name="${name}"]`,
|
|
40
|
+
message: `<${name}> is not allowed here. ${message}`,
|
|
41
|
+
}));
|
|
42
|
+
return [
|
|
43
|
+
{
|
|
44
|
+
name: "silicon-ui/components",
|
|
45
|
+
files,
|
|
46
|
+
ignores,
|
|
47
|
+
rules: {
|
|
48
|
+
"no-restricted-syntax": ["error", ...syntax, ...extraSyntax],
|
|
49
|
+
"no-restricted-imports": ["error", { patterns: [...IMPORTS, ...extraImports] }],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
}
|
package/lib/escape.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape inside a layer (Dialog, Drawer, BottomSheet, Popover) belongs to the control that has focus when that control
|
|
3
|
+
* has something of its own to close: an open Combobox list, an open DatePicker calendar, an InlineEdit being edited, a
|
|
4
|
+
* ConfirmMorph asking its question. Only the next Escape closes the layer.
|
|
5
|
+
*
|
|
6
|
+
* Radix closes its layers from a capture listener on the document, before the control's own key handler runs, so every
|
|
7
|
+
* Silicon UI layer passes its `onEscapeKeyDown` through `layerEscape` (and marks its own panel with `data-escape-layer`, where
|
|
8
|
+
* the search for an inner popup stops). Pages need nothing for this; their own `onEscapeKeyDown` still runs first and
|
|
9
|
+
* can keep the layer open for reasons of its own with `event.preventDefault()`.
|
|
10
|
+
*/
|
|
11
|
+
/** Marks a layer's own panel (so it is never mistaken for a popup of a control inside it). */
|
|
12
|
+
export declare const ESCAPE_LAYER_ATTRIBUTE = "data-escape-layer";
|
|
13
|
+
/** True when Escape at `target` is handled by a control inside the layer, which closes its own part first. */
|
|
14
|
+
export declare function escapeBelongsInside(target: EventTarget | null): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* The `onEscapeKeyDown` a Silicon UI layer gives Radix: the page's own handler first, then the layer stays open when the
|
|
17
|
+
* Escape belongs to a control inside it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function layerEscape<E extends KeyboardEvent>(own?: (event: E) => void): (event: E) => void;
|
package/lib/escape.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape inside a layer (Dialog, Drawer, BottomSheet, Popover) belongs to the control that has focus when that control
|
|
3
|
+
* has something of its own to close: an open Combobox list, an open DatePicker calendar, an InlineEdit being edited, a
|
|
4
|
+
* ConfirmMorph asking its question. Only the next Escape closes the layer.
|
|
5
|
+
*
|
|
6
|
+
* Radix closes its layers from a capture listener on the document, before the control's own key handler runs, so every
|
|
7
|
+
* Silicon UI layer passes its `onEscapeKeyDown` through `layerEscape` (and marks its own panel with `data-escape-layer`, where
|
|
8
|
+
* the search for an inner popup stops). Pages need nothing for this; their own `onEscapeKeyDown` still runs first and
|
|
9
|
+
* can keep the layer open for reasons of its own with `event.preventDefault()`.
|
|
10
|
+
*/
|
|
11
|
+
/** Marks a layer's own panel (so it is never mistaken for a popup of a control inside it). */
|
|
12
|
+
export const ESCAPE_LAYER_ATTRIBUTE = "data-escape-layer";
|
|
13
|
+
/** Controls that close something of their own on Escape while focus is in them. */
|
|
14
|
+
const OWN_ESCAPE = [
|
|
15
|
+
// Combobox with its list open (focus stays in its input).
|
|
16
|
+
'[role="combobox"][aria-expanded="true"]',
|
|
17
|
+
// DatePicker's trigger (or any popup button) while its panel is open.
|
|
18
|
+
'[aria-haspopup="dialog"][aria-expanded="true"]',
|
|
19
|
+
'[aria-haspopup="listbox"][aria-expanded="true"]',
|
|
20
|
+
// InlineEdit while editing: Escape puts the saved text back.
|
|
21
|
+
"[data-editing]",
|
|
22
|
+
].join(", ");
|
|
23
|
+
/** ConfirmMorph asking its question or showing a result (Escape returns it to rest). */
|
|
24
|
+
const MORPH_STATES = '[data-state="confirming"], [data-state="done"], [data-state="error"]';
|
|
25
|
+
/** True when Escape at `target` is handled by a control inside the layer, which closes its own part first. */
|
|
26
|
+
export function escapeBelongsInside(target) {
|
|
27
|
+
if (!(target instanceof Element))
|
|
28
|
+
return false;
|
|
29
|
+
if (target.closest(OWN_ESCAPE))
|
|
30
|
+
return true;
|
|
31
|
+
// A ConfirmMorph's root (focused while it works or shows a result) or one of its faces. A HoldToConfirm button also
|
|
32
|
+
// says data-state="done", but it has no faces and no Escape of its own.
|
|
33
|
+
const morph = target.closest(MORPH_STATES);
|
|
34
|
+
if (morph?.querySelector("[data-face]"))
|
|
35
|
+
return true;
|
|
36
|
+
// A popup of a control (DatePicker's calendar is a dialog inside the layer); the layer's own panel is not one.
|
|
37
|
+
const dialog = target.closest('[role="dialog"]');
|
|
38
|
+
return !!dialog && !dialog.hasAttribute(ESCAPE_LAYER_ATTRIBUTE);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The `onEscapeKeyDown` a Silicon UI layer gives Radix: the page's own handler first, then the layer stays open when the
|
|
42
|
+
* Escape belongs to a control inside it.
|
|
43
|
+
*/
|
|
44
|
+
export function layerEscape(own) {
|
|
45
|
+
return event => {
|
|
46
|
+
own?.(event);
|
|
47
|
+
if (!event.defaultPrevented && escapeBelongsInside(event.target))
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
};
|
|
50
|
+
}
|
package/lib/media.d.ts
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared demo media. Files live in `public/media/`, credits in `public/media/CREDITS.md`.
|
|
3
|
+
* Portraits are 400 px squares; photos are 1600 px on the long side.
|
|
4
|
+
*/
|
|
5
|
+
export interface MediaPerson {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
role?: string;
|
|
9
|
+
src: string;
|
|
10
|
+
}
|
|
11
|
+
export interface MediaPhoto {
|
|
12
|
+
id: string;
|
|
13
|
+
alt: string;
|
|
14
|
+
src: string;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const people: readonly [{
|
|
19
|
+
readonly id: "emma-collins";
|
|
20
|
+
readonly name: "Emma Collins";
|
|
21
|
+
readonly role: "Product designer";
|
|
22
|
+
readonly src: "/media/people/emma-collins.jpg";
|
|
23
|
+
}, {
|
|
24
|
+
readonly id: "marcus-johnson";
|
|
25
|
+
readonly name: "Marcus Johnson";
|
|
26
|
+
readonly role: "Frontend engineer";
|
|
27
|
+
readonly src: "/media/people/marcus-johnson.jpg";
|
|
28
|
+
}, {
|
|
29
|
+
readonly id: "jasmine-brooks";
|
|
30
|
+
readonly name: "Jasmine Brooks";
|
|
31
|
+
readonly role: "Design lead";
|
|
32
|
+
readonly src: "/media/people/jasmine-brooks.jpg";
|
|
33
|
+
}, {
|
|
34
|
+
readonly id: "olivia-bennett";
|
|
35
|
+
readonly name: "Olivia Bennett";
|
|
36
|
+
readonly role: "Platform engineer";
|
|
37
|
+
readonly src: "/media/people/olivia-bennett.jpg";
|
|
38
|
+
}, {
|
|
39
|
+
readonly id: "sofia-ramirez";
|
|
40
|
+
readonly name: "Sofia Ramirez";
|
|
41
|
+
readonly role: "Operations lead";
|
|
42
|
+
readonly src: "/media/people/sofia-ramirez.jpg";
|
|
43
|
+
}, {
|
|
44
|
+
readonly id: "ryan-sullivan";
|
|
45
|
+
readonly name: "Ryan Sullivan";
|
|
46
|
+
readonly role: "Account executive";
|
|
47
|
+
readonly src: "/media/people/ryan-sullivan.jpg";
|
|
48
|
+
}, {
|
|
49
|
+
readonly id: "hannah-walsh";
|
|
50
|
+
readonly name: "Hannah Walsh";
|
|
51
|
+
readonly role: "Customer success";
|
|
52
|
+
readonly src: "/media/people/hannah-walsh.jpg";
|
|
53
|
+
}, {
|
|
54
|
+
readonly id: "chloe-nguyen";
|
|
55
|
+
readonly name: "Chloe Nguyen";
|
|
56
|
+
readonly role: "Data analyst";
|
|
57
|
+
readonly src: "/media/people/chloe-nguyen.jpg";
|
|
58
|
+
}, {
|
|
59
|
+
readonly id: "ava-mitchell";
|
|
60
|
+
readonly name: "Ava Mitchell";
|
|
61
|
+
readonly role: "Marketing manager";
|
|
62
|
+
readonly src: "/media/people/ava-mitchell.jpg";
|
|
63
|
+
}, {
|
|
64
|
+
readonly id: "daniel-kim";
|
|
65
|
+
readonly name: "Daniel Kim";
|
|
66
|
+
readonly role: "Backend engineer";
|
|
67
|
+
readonly src: "/media/people/daniel-kim.jpg";
|
|
68
|
+
}, {
|
|
69
|
+
readonly id: "jordan-reyes";
|
|
70
|
+
readonly name: "Jordan Reyes";
|
|
71
|
+
readonly role: "Support specialist";
|
|
72
|
+
readonly src: "/media/people/jordan-reyes.jpg";
|
|
73
|
+
}, {
|
|
74
|
+
readonly id: "mateo-alvarez";
|
|
75
|
+
readonly name: "Mateo Alvarez";
|
|
76
|
+
readonly role: "Mobile engineer";
|
|
77
|
+
readonly src: "/media/people/mateo-alvarez.jpg";
|
|
78
|
+
}, {
|
|
79
|
+
readonly id: "tyler-hayes";
|
|
80
|
+
readonly name: "Tyler Hayes";
|
|
81
|
+
readonly role: "Sales lead";
|
|
82
|
+
readonly src: "/media/people/tyler-hayes.jpg";
|
|
83
|
+
}, {
|
|
84
|
+
readonly id: "andre-williams";
|
|
85
|
+
readonly name: "Andre Williams";
|
|
86
|
+
readonly role: "Finance partner";
|
|
87
|
+
readonly src: "/media/people/andre-williams.jpg";
|
|
88
|
+
}, {
|
|
89
|
+
readonly id: "nathan-cole";
|
|
90
|
+
readonly name: "Nathan Cole";
|
|
91
|
+
readonly role: "Engineering manager";
|
|
92
|
+
readonly src: "/media/people/nathan-cole.jpg";
|
|
93
|
+
}, {
|
|
94
|
+
readonly id: "diane-foster";
|
|
95
|
+
readonly name: "Diane Foster";
|
|
96
|
+
readonly role: "Chief operating officer";
|
|
97
|
+
readonly src: "/media/people/diane-foster.jpg";
|
|
98
|
+
}];
|
|
99
|
+
export declare const photos: readonly [{
|
|
100
|
+
readonly id: "lounge-chair";
|
|
101
|
+
readonly alt: "A woven oak lounge chair with a sheepskin and linen cushion on a concrete floor";
|
|
102
|
+
readonly src: "/media/photos/lounge-chair.jpg";
|
|
103
|
+
readonly width: 1280;
|
|
104
|
+
readonly height: 1600;
|
|
105
|
+
}, {
|
|
106
|
+
readonly id: "table-lamp";
|
|
107
|
+
readonly alt: "A white mushroom table lamp glowing beside books and a small vase";
|
|
108
|
+
readonly src: "/media/photos/table-lamp.jpg";
|
|
109
|
+
readonly width: 1600;
|
|
110
|
+
readonly height: 900;
|
|
111
|
+
}, {
|
|
112
|
+
readonly id: "linen-throw";
|
|
113
|
+
readonly alt: "Folded natural linen throws with fringed edges in soft window light";
|
|
114
|
+
readonly src: "/media/photos/linen-throw.jpg";
|
|
115
|
+
readonly width: 1067;
|
|
116
|
+
readonly height: 1600;
|
|
117
|
+
}, {
|
|
118
|
+
readonly id: "glass-carafe";
|
|
119
|
+
readonly alt: "A hand pouring water from a ribbed glass carafe into tumblers";
|
|
120
|
+
readonly src: "/media/photos/glass-carafe.jpg";
|
|
121
|
+
readonly width: 1280;
|
|
122
|
+
readonly height: 1600;
|
|
123
|
+
}, {
|
|
124
|
+
readonly id: "stoneware-cups";
|
|
125
|
+
readonly alt: "Two speckled stoneware cups with a lid on a pale table";
|
|
126
|
+
readonly src: "/media/photos/stoneware-cups.jpg";
|
|
127
|
+
readonly width: 1067;
|
|
128
|
+
readonly height: 1600;
|
|
129
|
+
}, {
|
|
130
|
+
readonly id: "stacked-bowls";
|
|
131
|
+
readonly alt: "Two stacked speckled ceramic bowls against a dark wall";
|
|
132
|
+
readonly src: "/media/photos/stacked-bowls.jpg";
|
|
133
|
+
readonly width: 1600;
|
|
134
|
+
readonly height: 1067;
|
|
135
|
+
}, {
|
|
136
|
+
readonly id: "ceramic-lamp";
|
|
137
|
+
readonly alt: "A sculptural ceramic lamp with a linen shade on a walnut sideboard";
|
|
138
|
+
readonly src: "/media/photos/ceramic-lamp.jpg";
|
|
139
|
+
readonly width: 1067;
|
|
140
|
+
readonly height: 1600;
|
|
141
|
+
}, {
|
|
142
|
+
readonly id: "living-room";
|
|
143
|
+
readonly alt: "A bright living room with timber beams, arched windows, and cream sofas";
|
|
144
|
+
readonly src: "/media/photos/living-room.jpg";
|
|
145
|
+
readonly width: 1200;
|
|
146
|
+
readonly height: 1600;
|
|
147
|
+
}, {
|
|
148
|
+
readonly id: "sunroom";
|
|
149
|
+
readonly alt: "A sunroom with a round dining table, plants, and windows on three sides";
|
|
150
|
+
readonly src: "/media/photos/sunroom.jpg";
|
|
151
|
+
readonly width: 1600;
|
|
152
|
+
readonly height: 1067;
|
|
153
|
+
}, {
|
|
154
|
+
readonly id: "home-office";
|
|
155
|
+
readonly alt: "A home office with a wooden desk and deep green walls";
|
|
156
|
+
readonly src: "/media/photos/home-office.jpg";
|
|
157
|
+
readonly width: 1600;
|
|
158
|
+
readonly height: 1200;
|
|
159
|
+
}, {
|
|
160
|
+
readonly id: "reading-chair";
|
|
161
|
+
readonly alt: "A grey armchair and ottoman with a knit throw in a dark green room";
|
|
162
|
+
readonly src: "/media/photos/reading-chair.jpg";
|
|
163
|
+
readonly width: 1600;
|
|
164
|
+
readonly height: 900;
|
|
165
|
+
}, {
|
|
166
|
+
readonly id: "bedroom";
|
|
167
|
+
readonly alt: "A made bed with striped linen pillows against an oak headboard";
|
|
168
|
+
readonly src: "/media/photos/bedroom.jpg";
|
|
169
|
+
readonly width: 1067;
|
|
170
|
+
readonly height: 1600;
|
|
171
|
+
}, {
|
|
172
|
+
readonly id: "restaurant";
|
|
173
|
+
readonly alt: "A warm restaurant dining room with woven pendant lights and a tree";
|
|
174
|
+
readonly src: "/media/photos/restaurant.jpg";
|
|
175
|
+
readonly width: 1067;
|
|
176
|
+
readonly height: 1600;
|
|
177
|
+
}, {
|
|
178
|
+
readonly id: "wine-bar";
|
|
179
|
+
readonly alt: "A glass carafe of red wine on a bar table in low evening light";
|
|
180
|
+
readonly src: "/media/photos/wine-bar.jpg";
|
|
181
|
+
readonly width: 1600;
|
|
182
|
+
readonly height: 1067;
|
|
183
|
+
}, {
|
|
184
|
+
readonly id: "concert-hall";
|
|
185
|
+
readonly alt: "Curved stainless steel panels of the Walt Disney Concert Hall against a blue sky";
|
|
186
|
+
readonly src: "/media/photos/concert-hall.jpg";
|
|
187
|
+
readonly width: 1600;
|
|
188
|
+
readonly height: 1143;
|
|
189
|
+
}, {
|
|
190
|
+
readonly id: "curved-facade";
|
|
191
|
+
readonly alt: "A white tiled building facade with curved balconies";
|
|
192
|
+
readonly src: "/media/photos/curved-facade.jpg";
|
|
193
|
+
readonly width: 1600;
|
|
194
|
+
readonly height: 1067;
|
|
195
|
+
}, {
|
|
196
|
+
readonly id: "pool-house";
|
|
197
|
+
readonly alt: "A modern glass house beside a long pool under a clear sky";
|
|
198
|
+
readonly src: "/media/photos/pool-house.jpg";
|
|
199
|
+
readonly width: 1600;
|
|
200
|
+
readonly height: 900;
|
|
201
|
+
}, {
|
|
202
|
+
readonly id: "terracotta-waves";
|
|
203
|
+
readonly alt: "Wavy terracotta walls rising toward a blue sky";
|
|
204
|
+
readonly src: "/media/photos/terracotta-waves.jpg";
|
|
205
|
+
readonly width: 1067;
|
|
206
|
+
readonly height: 1600;
|
|
207
|
+
}, {
|
|
208
|
+
readonly id: "mountain-ridges";
|
|
209
|
+
readonly alt: "Layered mountain ridges under a warm evening sky";
|
|
210
|
+
readonly src: "/media/photos/mountain-ridges.jpg";
|
|
211
|
+
readonly width: 1600;
|
|
212
|
+
readonly height: 1068;
|
|
213
|
+
}, {
|
|
214
|
+
readonly id: "alpine-lake";
|
|
215
|
+
readonly alt: "A calm alpine lake reflecting a rocky peak at golden hour";
|
|
216
|
+
readonly src: "/media/photos/alpine-lake.jpg";
|
|
217
|
+
readonly width: 1067;
|
|
218
|
+
readonly height: 1600;
|
|
219
|
+
}, {
|
|
220
|
+
readonly id: "coastline";
|
|
221
|
+
readonly alt: "A long coastline with waves rolling onto a beach below green cliffs";
|
|
222
|
+
readonly src: "/media/photos/coastline.jpg";
|
|
223
|
+
readonly width: 1200;
|
|
224
|
+
readonly height: 1600;
|
|
225
|
+
}, {
|
|
226
|
+
readonly id: "sea-at-dusk";
|
|
227
|
+
readonly alt: "A calm sea at dusk with a low island on the horizon";
|
|
228
|
+
readonly src: "/media/photos/sea-at-dusk.jpg";
|
|
229
|
+
readonly width: 1067;
|
|
230
|
+
readonly height: 1600;
|
|
231
|
+
}, {
|
|
232
|
+
readonly id: "lisbon-tram";
|
|
233
|
+
readonly alt: "A yellow tram on a street lined with historic buildings in Lisbon";
|
|
234
|
+
readonly src: "/media/photos/lisbon-tram.jpg";
|
|
235
|
+
readonly width: 1600;
|
|
236
|
+
readonly height: 1064;
|
|
237
|
+
}, {
|
|
238
|
+
readonly id: "lisbon-bridge";
|
|
239
|
+
readonly alt: "The 25 de Abril Bridge crossing the Tagus in Lisbon";
|
|
240
|
+
readonly src: "/media/photos/lisbon-bridge.jpg";
|
|
241
|
+
readonly width: 1600;
|
|
242
|
+
readonly height: 1166;
|
|
243
|
+
}, {
|
|
244
|
+
readonly id: "lisbon-rooftops";
|
|
245
|
+
readonly alt: "Terracotta rooftops of Lisbon running down to the river";
|
|
246
|
+
readonly src: "/media/photos/lisbon-rooftops.jpg";
|
|
247
|
+
readonly width: 1280;
|
|
248
|
+
readonly height: 1600;
|
|
249
|
+
}, {
|
|
250
|
+
readonly id: "salmon-dinner";
|
|
251
|
+
readonly alt: "Seared salmon with a bright herb salsa and a glass of red wine";
|
|
252
|
+
readonly src: "/media/photos/salmon-dinner.jpg";
|
|
253
|
+
readonly width: 1067;
|
|
254
|
+
readonly height: 1600;
|
|
255
|
+
}, {
|
|
256
|
+
readonly id: "chef-plating";
|
|
257
|
+
readonly alt: "A chef spooning sauce onto a plated dish in a dark kitchen";
|
|
258
|
+
readonly src: "/media/photos/chef-plating.jpg";
|
|
259
|
+
readonly width: 1600;
|
|
260
|
+
readonly height: 1600;
|
|
261
|
+
}];
|
|
262
|
+
export type PersonId = (typeof people)[number]["id"];
|
|
263
|
+
export type PhotoId = (typeof photos)[number]["id"];
|
|
264
|
+
/** Looks up a person by id. */
|
|
265
|
+
export declare function person(id: PersonId): MediaPerson;
|
|
266
|
+
/** Looks up a photo by id. */
|
|
267
|
+
export declare function photo(id: PhotoId): MediaPhoto;
|
|
268
|
+
/** Square 400 px avatar path for a person id, for `src` props. */
|
|
269
|
+
export declare const avatar: (id: PersonId) => string;
|
|
270
|
+
/** 800 × 1000 portrait crop of the same photo, for image-led layouts. */
|
|
271
|
+
export declare const portrait: (id: PersonId) => string;
|
|
272
|
+
/** The first `count` people, for avatar stacks and lists. */
|
|
273
|
+
export declare const peopleSample: (count: number) => ({
|
|
274
|
+
readonly id: "emma-collins";
|
|
275
|
+
readonly name: "Emma Collins";
|
|
276
|
+
readonly role: "Product designer";
|
|
277
|
+
readonly src: "/media/people/emma-collins.jpg";
|
|
278
|
+
} | {
|
|
279
|
+
readonly id: "marcus-johnson";
|
|
280
|
+
readonly name: "Marcus Johnson";
|
|
281
|
+
readonly role: "Frontend engineer";
|
|
282
|
+
readonly src: "/media/people/marcus-johnson.jpg";
|
|
283
|
+
} | {
|
|
284
|
+
readonly id: "jasmine-brooks";
|
|
285
|
+
readonly name: "Jasmine Brooks";
|
|
286
|
+
readonly role: "Design lead";
|
|
287
|
+
readonly src: "/media/people/jasmine-brooks.jpg";
|
|
288
|
+
} | {
|
|
289
|
+
readonly id: "olivia-bennett";
|
|
290
|
+
readonly name: "Olivia Bennett";
|
|
291
|
+
readonly role: "Platform engineer";
|
|
292
|
+
readonly src: "/media/people/olivia-bennett.jpg";
|
|
293
|
+
} | {
|
|
294
|
+
readonly id: "sofia-ramirez";
|
|
295
|
+
readonly name: "Sofia Ramirez";
|
|
296
|
+
readonly role: "Operations lead";
|
|
297
|
+
readonly src: "/media/people/sofia-ramirez.jpg";
|
|
298
|
+
} | {
|
|
299
|
+
readonly id: "ryan-sullivan";
|
|
300
|
+
readonly name: "Ryan Sullivan";
|
|
301
|
+
readonly role: "Account executive";
|
|
302
|
+
readonly src: "/media/people/ryan-sullivan.jpg";
|
|
303
|
+
} | {
|
|
304
|
+
readonly id: "hannah-walsh";
|
|
305
|
+
readonly name: "Hannah Walsh";
|
|
306
|
+
readonly role: "Customer success";
|
|
307
|
+
readonly src: "/media/people/hannah-walsh.jpg";
|
|
308
|
+
} | {
|
|
309
|
+
readonly id: "chloe-nguyen";
|
|
310
|
+
readonly name: "Chloe Nguyen";
|
|
311
|
+
readonly role: "Data analyst";
|
|
312
|
+
readonly src: "/media/people/chloe-nguyen.jpg";
|
|
313
|
+
} | {
|
|
314
|
+
readonly id: "ava-mitchell";
|
|
315
|
+
readonly name: "Ava Mitchell";
|
|
316
|
+
readonly role: "Marketing manager";
|
|
317
|
+
readonly src: "/media/people/ava-mitchell.jpg";
|
|
318
|
+
} | {
|
|
319
|
+
readonly id: "daniel-kim";
|
|
320
|
+
readonly name: "Daniel Kim";
|
|
321
|
+
readonly role: "Backend engineer";
|
|
322
|
+
readonly src: "/media/people/daniel-kim.jpg";
|
|
323
|
+
} | {
|
|
324
|
+
readonly id: "jordan-reyes";
|
|
325
|
+
readonly name: "Jordan Reyes";
|
|
326
|
+
readonly role: "Support specialist";
|
|
327
|
+
readonly src: "/media/people/jordan-reyes.jpg";
|
|
328
|
+
} | {
|
|
329
|
+
readonly id: "mateo-alvarez";
|
|
330
|
+
readonly name: "Mateo Alvarez";
|
|
331
|
+
readonly role: "Mobile engineer";
|
|
332
|
+
readonly src: "/media/people/mateo-alvarez.jpg";
|
|
333
|
+
} | {
|
|
334
|
+
readonly id: "tyler-hayes";
|
|
335
|
+
readonly name: "Tyler Hayes";
|
|
336
|
+
readonly role: "Sales lead";
|
|
337
|
+
readonly src: "/media/people/tyler-hayes.jpg";
|
|
338
|
+
} | {
|
|
339
|
+
readonly id: "andre-williams";
|
|
340
|
+
readonly name: "Andre Williams";
|
|
341
|
+
readonly role: "Finance partner";
|
|
342
|
+
readonly src: "/media/people/andre-williams.jpg";
|
|
343
|
+
} | {
|
|
344
|
+
readonly id: "nathan-cole";
|
|
345
|
+
readonly name: "Nathan Cole";
|
|
346
|
+
readonly role: "Engineering manager";
|
|
347
|
+
readonly src: "/media/people/nathan-cole.jpg";
|
|
348
|
+
} | {
|
|
349
|
+
readonly id: "diane-foster";
|
|
350
|
+
readonly name: "Diane Foster";
|
|
351
|
+
readonly role: "Chief operating officer";
|
|
352
|
+
readonly src: "/media/people/diane-foster.jpg";
|
|
353
|
+
})[];
|
package/lib/media.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared demo media. Files live in `public/media/`, credits in `public/media/CREDITS.md`.
|
|
3
|
+
* Portraits are 400 px squares; photos are 1600 px on the long side.
|
|
4
|
+
*/
|
|
5
|
+
export const people = [
|
|
6
|
+
{ id: "emma-collins", name: "Emma Collins", role: "Product designer", src: "/media/people/emma-collins.jpg" },
|
|
7
|
+
{ id: "marcus-johnson", name: "Marcus Johnson", role: "Frontend engineer", src: "/media/people/marcus-johnson.jpg" },
|
|
8
|
+
{ id: "jasmine-brooks", name: "Jasmine Brooks", role: "Design lead", src: "/media/people/jasmine-brooks.jpg" },
|
|
9
|
+
{ id: "olivia-bennett", name: "Olivia Bennett", role: "Platform engineer", src: "/media/people/olivia-bennett.jpg" },
|
|
10
|
+
{ id: "sofia-ramirez", name: "Sofia Ramirez", role: "Operations lead", src: "/media/people/sofia-ramirez.jpg" },
|
|
11
|
+
{ id: "ryan-sullivan", name: "Ryan Sullivan", role: "Account executive", src: "/media/people/ryan-sullivan.jpg" },
|
|
12
|
+
{ id: "hannah-walsh", name: "Hannah Walsh", role: "Customer success", src: "/media/people/hannah-walsh.jpg" },
|
|
13
|
+
{ id: "chloe-nguyen", name: "Chloe Nguyen", role: "Data analyst", src: "/media/people/chloe-nguyen.jpg" },
|
|
14
|
+
{ id: "ava-mitchell", name: "Ava Mitchell", role: "Marketing manager", src: "/media/people/ava-mitchell.jpg" },
|
|
15
|
+
{ id: "daniel-kim", name: "Daniel Kim", role: "Backend engineer", src: "/media/people/daniel-kim.jpg" },
|
|
16
|
+
{ id: "jordan-reyes", name: "Jordan Reyes", role: "Support specialist", src: "/media/people/jordan-reyes.jpg" },
|
|
17
|
+
{ id: "mateo-alvarez", name: "Mateo Alvarez", role: "Mobile engineer", src: "/media/people/mateo-alvarez.jpg" },
|
|
18
|
+
{ id: "tyler-hayes", name: "Tyler Hayes", role: "Sales lead", src: "/media/people/tyler-hayes.jpg" },
|
|
19
|
+
{ id: "andre-williams", name: "Andre Williams", role: "Finance partner", src: "/media/people/andre-williams.jpg" },
|
|
20
|
+
{ id: "nathan-cole", name: "Nathan Cole", role: "Engineering manager", src: "/media/people/nathan-cole.jpg" },
|
|
21
|
+
{ id: "diane-foster", name: "Diane Foster", role: "Chief operating officer", src: "/media/people/diane-foster.jpg" },
|
|
22
|
+
];
|
|
23
|
+
export const photos = [
|
|
24
|
+
{ id: "lounge-chair", alt: "A woven oak lounge chair with a sheepskin and linen cushion on a concrete floor", src: "/media/photos/lounge-chair.jpg", width: 1280, height: 1600 },
|
|
25
|
+
{ id: "table-lamp", alt: "A white mushroom table lamp glowing beside books and a small vase", src: "/media/photos/table-lamp.jpg", width: 1600, height: 900 },
|
|
26
|
+
{ id: "linen-throw", alt: "Folded natural linen throws with fringed edges in soft window light", src: "/media/photos/linen-throw.jpg", width: 1067, height: 1600 },
|
|
27
|
+
{ id: "glass-carafe", alt: "A hand pouring water from a ribbed glass carafe into tumblers", src: "/media/photos/glass-carafe.jpg", width: 1280, height: 1600 },
|
|
28
|
+
{ id: "stoneware-cups", alt: "Two speckled stoneware cups with a lid on a pale table", src: "/media/photos/stoneware-cups.jpg", width: 1067, height: 1600 },
|
|
29
|
+
{ id: "stacked-bowls", alt: "Two stacked speckled ceramic bowls against a dark wall", src: "/media/photos/stacked-bowls.jpg", width: 1600, height: 1067 },
|
|
30
|
+
{ id: "ceramic-lamp", alt: "A sculptural ceramic lamp with a linen shade on a walnut sideboard", src: "/media/photos/ceramic-lamp.jpg", width: 1067, height: 1600 },
|
|
31
|
+
{ id: "living-room", alt: "A bright living room with timber beams, arched windows, and cream sofas", src: "/media/photos/living-room.jpg", width: 1200, height: 1600 },
|
|
32
|
+
{ id: "sunroom", alt: "A sunroom with a round dining table, plants, and windows on three sides", src: "/media/photos/sunroom.jpg", width: 1600, height: 1067 },
|
|
33
|
+
{ id: "home-office", alt: "A home office with a wooden desk and deep green walls", src: "/media/photos/home-office.jpg", width: 1600, height: 1200 },
|
|
34
|
+
{ id: "reading-chair", alt: "A grey armchair and ottoman with a knit throw in a dark green room", src: "/media/photos/reading-chair.jpg", width: 1600, height: 900 },
|
|
35
|
+
{ id: "bedroom", alt: "A made bed with striped linen pillows against an oak headboard", src: "/media/photos/bedroom.jpg", width: 1067, height: 1600 },
|
|
36
|
+
{ id: "restaurant", alt: "A warm restaurant dining room with woven pendant lights and a tree", src: "/media/photos/restaurant.jpg", width: 1067, height: 1600 },
|
|
37
|
+
{ id: "wine-bar", alt: "A glass carafe of red wine on a bar table in low evening light", src: "/media/photos/wine-bar.jpg", width: 1600, height: 1067 },
|
|
38
|
+
{ id: "concert-hall", alt: "Curved stainless steel panels of the Walt Disney Concert Hall against a blue sky", src: "/media/photos/concert-hall.jpg", width: 1600, height: 1143 },
|
|
39
|
+
{ id: "curved-facade", alt: "A white tiled building facade with curved balconies", src: "/media/photos/curved-facade.jpg", width: 1600, height: 1067 },
|
|
40
|
+
{ id: "pool-house", alt: "A modern glass house beside a long pool under a clear sky", src: "/media/photos/pool-house.jpg", width: 1600, height: 900 },
|
|
41
|
+
{ id: "terracotta-waves", alt: "Wavy terracotta walls rising toward a blue sky", src: "/media/photos/terracotta-waves.jpg", width: 1067, height: 1600 },
|
|
42
|
+
{ id: "mountain-ridges", alt: "Layered mountain ridges under a warm evening sky", src: "/media/photos/mountain-ridges.jpg", width: 1600, height: 1068 },
|
|
43
|
+
{ id: "alpine-lake", alt: "A calm alpine lake reflecting a rocky peak at golden hour", src: "/media/photos/alpine-lake.jpg", width: 1067, height: 1600 },
|
|
44
|
+
{ id: "coastline", alt: "A long coastline with waves rolling onto a beach below green cliffs", src: "/media/photos/coastline.jpg", width: 1200, height: 1600 },
|
|
45
|
+
{ id: "sea-at-dusk", alt: "A calm sea at dusk with a low island on the horizon", src: "/media/photos/sea-at-dusk.jpg", width: 1067, height: 1600 },
|
|
46
|
+
{ id: "lisbon-tram", alt: "A yellow tram on a street lined with historic buildings in Lisbon", src: "/media/photos/lisbon-tram.jpg", width: 1600, height: 1064 },
|
|
47
|
+
{ id: "lisbon-bridge", alt: "The 25 de Abril Bridge crossing the Tagus in Lisbon", src: "/media/photos/lisbon-bridge.jpg", width: 1600, height: 1166 },
|
|
48
|
+
{ id: "lisbon-rooftops", alt: "Terracotta rooftops of Lisbon running down to the river", src: "/media/photos/lisbon-rooftops.jpg", width: 1280, height: 1600 },
|
|
49
|
+
{ id: "salmon-dinner", alt: "Seared salmon with a bright herb salsa and a glass of red wine", src: "/media/photos/salmon-dinner.jpg", width: 1067, height: 1600 },
|
|
50
|
+
{ id: "chef-plating", alt: "A chef spooning sauce onto a plated dish in a dark kitchen", src: "/media/photos/chef-plating.jpg", width: 1600, height: 1600 },
|
|
51
|
+
];
|
|
52
|
+
/** Looks up a person by id. */
|
|
53
|
+
export function person(id) {
|
|
54
|
+
return people.find(entry => entry.id === id);
|
|
55
|
+
}
|
|
56
|
+
/** Looks up a photo by id. */
|
|
57
|
+
export function photo(id) {
|
|
58
|
+
return photos.find(entry => entry.id === id);
|
|
59
|
+
}
|
|
60
|
+
/** Square 400 px avatar path for a person id, for `src` props. */
|
|
61
|
+
export const avatar = (id) => person(id).src;
|
|
62
|
+
/** 800 × 1000 portrait crop of the same photo, for image-led layouts. */
|
|
63
|
+
export const portrait = (id) => `/media/people/portrait/${id}.jpg`;
|
|
64
|
+
/** The first `count` people, for avatar stacks and lists. */
|
|
65
|
+
export const peopleSample = (count) => people.slice(0, count);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FocusEvent } from "react";
|
|
2
|
+
type Highlight = {
|
|
3
|
+
top: number;
|
|
4
|
+
height: number;
|
|
5
|
+
danger: boolean;
|
|
6
|
+
glide: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* One highlight that glides between the items of a Radix menu or listbox for the pointer, and jumps instantly for the keyboard.
|
|
10
|
+
* Radix focuses the highlighted item, so the highlight follows focus inside the content. Spread `contentProps` on the content,
|
|
11
|
+
* call `reset` when it opens, and render `<MenuHighlight state={highlight} className={styles.highlight} />` as its first child.
|
|
12
|
+
* Items mark a destructive tone with `data-tone="danger"`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useMenuHighlight(itemSelector?: string): {
|
|
15
|
+
highlight: Highlight | null;
|
|
16
|
+
reset: () => void;
|
|
17
|
+
contentProps: {
|
|
18
|
+
onFocus: (event: FocusEvent<HTMLElement>) => void;
|
|
19
|
+
onPointerMoveCapture: () => void;
|
|
20
|
+
onKeyDownCapture: () => void;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/** The gliding highlight itself. Position it absolutely inside the content, under the items. */
|
|
24
|
+
export declare function MenuHighlight({ state, className }: {
|
|
25
|
+
state: Highlight | null;
|
|
26
|
+
className?: string;
|
|
27
|
+
}): import("react").JSX.Element;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { motion, useReducedMotion } from "motion/react";
|
|
5
|
+
import { motionTokens } from "./motion-tokens.js";
|
|
6
|
+
/**
|
|
7
|
+
* One highlight that glides between the items of a Radix menu or listbox for the pointer, and jumps instantly for the keyboard.
|
|
8
|
+
* Radix focuses the highlighted item, so the highlight follows focus inside the content. Spread `contentProps` on the content,
|
|
9
|
+
* call `reset` when it opens, and render `<MenuHighlight state={highlight} className={styles.highlight} />` as its first child.
|
|
10
|
+
* Items mark a destructive tone with `data-tone="danger"`.
|
|
11
|
+
*/
|
|
12
|
+
export function useMenuHighlight(itemSelector = '[role="menuitem"], [role="option"]') {
|
|
13
|
+
const [highlight, setHighlight] = useState(null);
|
|
14
|
+
const pointer = useRef(false);
|
|
15
|
+
const clearTimer = useRef(0);
|
|
16
|
+
useEffect(() => () => window.clearTimeout(clearTimer.current), []);
|
|
17
|
+
function onFocus(event) {
|
|
18
|
+
const item = event.target instanceof HTMLElement ? event.target.closest(itemSelector) : null;
|
|
19
|
+
window.clearTimeout(clearTimer.current);
|
|
20
|
+
if (!item) {
|
|
21
|
+
// A short grace period keeps the highlight gliding across separators and item gaps.
|
|
22
|
+
clearTimer.current = window.setTimeout(() => setHighlight(null), pointer.current ? 70 : 0);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const next = { top: item.offsetTop, height: item.offsetHeight, danger: item.dataset.tone === "danger" };
|
|
26
|
+
const glide = pointer.current;
|
|
27
|
+
setHighlight(current => ({ ...next, glide: glide && current !== null }));
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
highlight,
|
|
31
|
+
reset: () => { window.clearTimeout(clearTimer.current); setHighlight(null); },
|
|
32
|
+
contentProps: {
|
|
33
|
+
onFocus,
|
|
34
|
+
onPointerMoveCapture: () => { pointer.current = true; },
|
|
35
|
+
onKeyDownCapture: () => { pointer.current = false; },
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** The gliding highlight itself. Position it absolutely inside the content, under the items. */
|
|
40
|
+
export function MenuHighlight({ state, className }) {
|
|
41
|
+
const reduced = useReducedMotion();
|
|
42
|
+
return _jsx(motion.span, { className: className, "data-tone": state?.danger ? "danger" : undefined, "aria-hidden": "true", initial: false, animate: state ? { y: state.top, height: state.height, opacity: 1 } : { opacity: 0 }, transition: { default: state?.glide && !reduced ? motionTokens.spring.snappy : { duration: 0 }, opacity: { duration: reduced ? 0 : .08 } } });
|
|
43
|
+
}
|