@sableui/react 0.6.2 → 0.7.1
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/AGENTS.md +2 -2
- package/CHANGELOG.md +315 -0
- package/CONTRIBUTING.md +16 -10
- package/DESIGN.md +3 -3
- package/README.md +3 -3
- package/bin/sable-lint.mjs +18 -0
- package/bin/sable.mjs +470 -62
- package/dist/blocks/api-keys-block.js +44 -0
- package/dist/blocks/api-keys-block.meta.js +21 -0
- package/dist/blocks/chat-thread-block.js +17 -0
- package/dist/blocks/chat-thread-block.meta.js +20 -0
- package/dist/blocks/command-palette-block.js +24 -0
- package/dist/blocks/command-palette-block.meta.js +21 -0
- package/dist/blocks/cta-band-block.js +27 -0
- package/dist/blocks/cta-band-block.meta.js +20 -0
- package/dist/blocks/danger-zone-block.js +39 -0
- package/dist/blocks/danger-zone-block.meta.js +21 -0
- package/dist/blocks/device-comparison-block.js +69 -0
- package/dist/blocks/device-comparison-block.meta.js +21 -0
- package/dist/blocks/event-card-block.js +40 -0
- package/dist/blocks/event-card-block.meta.js +21 -0
- package/dist/blocks/faq-block.js +38 -0
- package/dist/blocks/faq-block.meta.js +20 -0
- package/dist/blocks/feature-grid-block.js +38 -0
- package/dist/blocks/feature-grid-block.meta.js +21 -0
- package/dist/blocks/feedback-form-block.js +30 -0
- package/dist/blocks/feedback-form-block.meta.js +21 -0
- package/dist/blocks/goal-progress-block.js +24 -0
- package/dist/blocks/goal-progress-block.meta.js +21 -0
- package/dist/blocks/hero-block.js +33 -0
- package/dist/blocks/hero-block.meta.js +21 -0
- package/dist/blocks/index.js +162 -1964
- package/dist/blocks/invite-team-block.js +36 -0
- package/dist/blocks/invite-team-block.meta.js +20 -0
- package/dist/blocks/invoice-history-block.js +41 -0
- package/dist/blocks/invoice-history-block.meta.js +21 -0
- package/dist/blocks/kpi-sparkline-block.js +29 -0
- package/dist/blocks/kpi-sparkline-block.meta.js +20 -0
- package/dist/blocks/media-player-block.js +68 -0
- package/dist/blocks/media-player-block.meta.js +21 -0
- package/dist/blocks/newsletter-block.js +35 -0
- package/dist/blocks/newsletter-block.meta.js +21 -0
- package/dist/blocks/not-found-block.js +27 -0
- package/dist/blocks/not-found-block.meta.js +20 -0
- package/dist/blocks/notification-list-block.js +32 -0
- package/dist/blocks/notification-list-block.meta.js +21 -0
- package/dist/blocks/notification-settings-block.js +25 -0
- package/dist/blocks/notification-settings-block.meta.js +20 -0
- package/dist/blocks/order-tracking-block.js +26 -0
- package/dist/blocks/order-tracking-block.meta.js +20 -0
- package/dist/blocks/pricing-card-block.js +46 -0
- package/dist/blocks/pricing-card-block.meta.js +21 -0
- package/dist/blocks/pricing-table-block.js +88 -0
- package/dist/blocks/pricing-table-block.meta.js +20 -0
- package/dist/blocks/product-card-block.js +26 -0
- package/dist/blocks/product-card-block.meta.js +21 -0
- package/dist/blocks/profile-card-block.js +58 -0
- package/dist/blocks/profile-card-block.meta.js +21 -0
- package/dist/blocks/profile-form-block.js +70 -0
- package/dist/blocks/profile-form-block.meta.js +21 -0
- package/dist/blocks/revenue-breakdown-block.js +38 -0
- package/dist/blocks/revenue-breakdown-block.meta.js +21 -0
- package/dist/blocks/sign-in-block.js +38 -0
- package/dist/blocks/sign-in-block.meta.js +21 -0
- package/dist/blocks/sign-up-block.js +48 -0
- package/dist/blocks/sign-up-block.meta.js +21 -0
- package/dist/blocks/site-footer-block.js +54 -0
- package/dist/blocks/site-footer-block.meta.js +20 -0
- package/dist/blocks/stat-row-block.js +28 -0
- package/dist/blocks/stat-row-block.meta.js +21 -0
- package/dist/blocks/steps-block.js +37 -0
- package/dist/blocks/steps-block.meta.js +20 -0
- package/dist/blocks/task-list-block.js +52 -0
- package/dist/blocks/task-list-block.meta.js +21 -0
- package/dist/blocks/team-members-block.js +49 -0
- package/dist/blocks/team-members-block.meta.js +21 -0
- package/dist/blocks/testimonial-block.js +49 -0
- package/dist/blocks/testimonial-block.meta.js +20 -0
- package/dist/blocks/traffic-sources-block.js +39 -0
- package/dist/blocks/traffic-sources-block.meta.js +21 -0
- package/dist/blocks/traffic-trend-block.js +61 -0
- package/dist/blocks/traffic-trend-block.meta.js +21 -0
- package/dist/blocks/upload-list-block.js +39 -0
- package/dist/blocks/upload-list-block.meta.js +21 -0
- package/dist/blocks/verify-code-block.js +28 -0
- package/dist/blocks/verify-code-block.meta.js +21 -0
- package/dist/blocks/wallet-block.js +48 -0
- package/dist/blocks/wallet-block.meta.js +21 -0
- package/dist/components/accordion/accordion.js +63 -0
- package/dist/components/accordion/accordion.meta.js +155 -0
- package/dist/components/accordion/index.js +15 -0
- package/dist/components/app-header/app-header.js +30 -0
- package/dist/components/app-header/app-header.meta.js +88 -0
- package/dist/components/app-header/index.js +7 -0
- package/dist/components/app-shell/app-shell-dock.js +37 -0
- package/dist/components/app-shell/app-shell.js +67 -0
- package/dist/components/app-shell/app-shell.meta.js +176 -0
- package/dist/components/app-shell/index.js +7 -0
- package/dist/components/area-chart/area-chart.js +10 -0
- package/dist/components/area-chart/area-chart.meta.js +218 -0
- package/dist/components/area-chart/index.js +7 -0
- package/dist/components/attachment/attachment.js +176 -0
- package/dist/components/attachment/attachment.meta.js +179 -0
- package/dist/components/attachment/attachment.variants.js +47 -0
- package/dist/components/attachment/index.js +7 -0
- package/dist/components/avatar/avatar.js +192 -0
- package/dist/components/avatar/avatar.meta.js +233 -0
- package/dist/components/avatar/avatar.variants.js +44 -0
- package/dist/components/avatar/index.js +22 -0
- package/dist/components/badge/badge.js +26 -0
- package/dist/components/badge/badge.meta.js +171 -0
- package/dist/components/badge/badge.variants.js +103 -0
- package/dist/components/badge/index.js +11 -0
- package/dist/components/banner/banner.js +76 -0
- package/dist/components/banner/banner.meta.js +172 -0
- package/dist/components/banner/banner.variants.js +44 -0
- package/dist/components/banner/index.js +9 -0
- package/dist/components/bar-chart/bar-chart.js +10 -0
- package/dist/components/bar-chart/bar-chart.meta.js +245 -0
- package/dist/components/bar-chart/index.js +7 -0
- package/dist/components/bottom-nav/bottom-nav.js +63 -0
- package/dist/components/bottom-nav/bottom-nav.meta.js +95 -0
- package/dist/components/bottom-nav/index.js +11 -0
- package/dist/components/breadcrumbs/breadcrumbs.js +90 -0
- package/dist/components/breadcrumbs/breadcrumbs.meta.js +111 -0
- package/dist/components/breadcrumbs/index.js +17 -0
- package/dist/components/browser/browser.js +39 -0
- package/dist/components/browser/browser.meta.js +80 -0
- package/dist/components/browser/index.js +7 -0
- package/dist/components/bubble/bubble.js +110 -0
- package/dist/components/bubble/bubble.meta.js +158 -0
- package/dist/components/bubble/bubble.variants.js +90 -0
- package/dist/components/bubble/index.js +13 -0
- package/dist/components/button/button.js +87 -0
- package/dist/components/button/button.meta.js +302 -0
- package/dist/components/button/button.variants.js +137 -0
- package/dist/components/button/index.js +9 -0
- package/dist/components/calendar/calendar.js +89 -0
- package/dist/components/calendar/calendar.meta.js +136 -0
- package/dist/components/calendar/index.js +7 -0
- package/dist/components/card/card.js +203 -0
- package/dist/components/card/card.meta.js +212 -0
- package/dist/components/card/index.js +23 -0
- package/dist/components/carousel/carousel.js +428 -0
- package/dist/components/carousel/carousel.meta.js +100 -0
- package/dist/components/carousel/index.js +7 -0
- package/dist/components/chat-composer/chat-composer.js +174 -0
- package/dist/components/chat-composer/chat-composer.meta.js +241 -0
- package/dist/components/chat-composer/chat-composer.variants.js +17 -0
- package/dist/components/chat-composer/index.js +7 -0
- package/dist/components/checkbox/checkbox-mark.js +28 -0
- package/dist/components/checkbox/checkbox.js +30 -0
- package/dist/components/checkbox/checkbox.meta.js +127 -0
- package/dist/components/checkbox/index.js +7 -0
- package/dist/components/checkbox-card/checkbox-card.js +94 -0
- package/dist/components/checkbox-card/checkbox-card.meta.js +154 -0
- package/dist/components/checkbox-card/checkbox-card.variants.js +43 -0
- package/dist/components/checkbox-card/index.js +11 -0
- package/dist/components/code/code.js +19 -0
- package/dist/components/code/code.meta.js +71 -0
- package/dist/components/code/index.js +7 -0
- package/dist/components/code-block/code-block.js +84 -0
- package/dist/components/code-block/code-block.meta.js +103 -0
- package/dist/components/code-block/index.js +8 -0
- package/dist/components/collapsible/collapsible.js +38 -0
- package/dist/components/collapsible/collapsible.meta.js +117 -0
- package/dist/components/collapsible/index.js +15 -0
- package/dist/components/color-field/color-field.js +293 -0
- package/dist/components/color-field/color-field.meta.js +142 -0
- package/dist/components/color-field/color.js +69 -0
- package/dist/components/color-field/index.js +15 -0
- package/dist/components/combobox/combobox.js +486 -0
- package/dist/components/combobox/combobox.meta.js +217 -0
- package/dist/components/combobox/index.js +37 -0
- package/dist/components/command-menu/command-menu-trigger.js +122 -0
- package/dist/components/command-menu/command-menu.js +433 -0
- package/dist/components/command-menu/command-menu.meta.js +279 -0
- package/dist/components/command-menu/index.js +21 -0
- package/dist/components/context-card/context-card.js +62 -0
- package/dist/components/context-card/context-card.meta.js +106 -0
- package/dist/components/context-card/index.js +17 -0
- package/dist/components/context-menu/context-menu.js +92 -0
- package/dist/components/context-menu/context-menu.meta.js +138 -0
- package/dist/components/context-menu/index.js +23 -0
- package/dist/components/data-table/data-table.js +142 -0
- package/dist/components/data-table/data-table.meta.js +146 -0
- package/dist/components/data-table/index.js +9 -0
- package/dist/components/date-picker/date-picker.js +196 -0
- package/dist/components/date-picker/date-picker.meta.js +164 -0
- package/dist/components/date-picker/index.js +7 -0
- package/dist/components/date-tile/date-tile.js +29 -0
- package/dist/components/date-tile/date-tile.meta.js +72 -0
- package/dist/components/date-tile/index.js +7 -0
- package/dist/components/destructive-action-modal/destructive-action-modal.js +130 -0
- package/dist/components/destructive-action-modal/destructive-action-modal.meta.js +136 -0
- package/dist/components/destructive-action-modal/index.js +9 -0
- package/dist/components/dialog/dialog.js +207 -0
- package/dist/components/dialog/dialog.meta.js +239 -0
- package/dist/components/dialog/index.js +45 -0
- package/dist/components/disclosure-picker/disclosure-picker.js +417 -0
- package/dist/components/disclosure-picker/disclosure-picker.meta.js +213 -0
- package/dist/components/disclosure-picker/index.js +21 -0
- package/dist/components/empty-state/empty-state.js +52 -0
- package/dist/components/empty-state/empty-state.meta.js +158 -0
- package/dist/components/empty-state/index.js +7 -0
- package/dist/components/entity/entity.js +70 -0
- package/dist/components/entity/entity.meta.js +108 -0
- package/dist/components/entity/entity.variants.js +16 -0
- package/dist/components/entity/index.js +7 -0
- package/dist/components/feedback/feedback.js +93 -0
- package/dist/components/feedback/feedback.meta.js +109 -0
- package/dist/components/feedback/index.js +7 -0
- package/dist/components/fieldset/fieldset.js +80 -0
- package/dist/components/fieldset/fieldset.meta.js +160 -0
- package/dist/components/fieldset/index.js +17 -0
- package/dist/components/file-dropzone/file-dropzone.js +193 -0
- package/dist/components/file-dropzone/file-dropzone.meta.js +210 -0
- package/dist/components/file-dropzone/file-dropzone.variants.js +26 -0
- package/dist/components/file-dropzone/index.js +13 -0
- package/dist/components/font-picker/font-picker.js +77 -0
- package/dist/components/font-picker/font-picker.meta.js +119 -0
- package/dist/components/font-picker/index.js +7 -0
- package/dist/components/form/form.js +145 -0
- package/dist/components/form/form.meta.js +142 -0
- package/dist/components/form/index.js +11 -0
- package/dist/components/gliding-highlight/gliding-highlight.js +50 -0
- package/dist/components/gliding-highlight/gliding-highlight.meta.js +135 -0
- package/dist/components/gliding-highlight/index.js +9 -0
- package/dist/components/gradient-art/gradient-art.js +168 -0
- package/dist/components/gradient-art/gradient-art.meta.js +81 -0
- package/dist/components/gradient-art/index.js +7 -0
- package/dist/components/gradient-art/renderer.js +336 -0
- package/dist/components/inline-status/index.js +7 -0
- package/dist/components/inline-status/inline-status.js +33 -0
- package/dist/components/inline-status/inline-status.meta.js +83 -0
- package/dist/components/inline-status/inline-status.variants.js +18 -0
- package/dist/components/input/index.js +7 -0
- package/dist/components/input/input.js +33 -0
- package/dist/components/input/input.meta.js +155 -0
- package/dist/components/kbd/index.js +8 -0
- package/dist/components/kbd/kbd.js +54 -0
- package/dist/components/kbd/kbd.meta.js +95 -0
- package/dist/components/label/index.js +7 -0
- package/dist/components/label/label.js +51 -0
- package/dist/components/label/label.meta.js +98 -0
- package/dist/components/line-chart/index.js +7 -0
- package/dist/components/line-chart/line-chart.js +10 -0
- package/dist/components/line-chart/line-chart.meta.js +222 -0
- package/dist/components/marker/index.js +7 -0
- package/dist/components/marker/marker.js +39 -0
- package/dist/components/marker/marker.meta.js +132 -0
- package/dist/components/marker/marker.variants.js +25 -0
- package/dist/components/marquee/index.js +7 -0
- package/dist/components/marquee/marquee.js +146 -0
- package/dist/components/marquee/marquee.meta.js +106 -0
- package/dist/components/media-image/index.js +7 -0
- package/dist/components/media-image/media-image.js +56 -0
- package/dist/components/media-image/media-image.meta.js +92 -0
- package/dist/components/media-tile/index.js +7 -0
- package/dist/components/media-tile/media-tile.js +135 -0
- package/dist/components/media-tile/media-tile.meta.js +133 -0
- package/dist/components/media-tile/media-tile.variants.js +48 -0
- package/dist/components/menu/index.js +33 -0
- package/dist/components/menu/menu.js +168 -0
- package/dist/components/menu/menu.meta.js +176 -0
- package/dist/components/message/index.js +7 -0
- package/dist/components/message/message.js +83 -0
- package/dist/components/message/message.meta.js +150 -0
- package/dist/components/message/message.variants.js +41 -0
- package/dist/components/message-scroller/index.js +9 -0
- package/dist/components/message-scroller/message-scroller.js +300 -0
- package/dist/components/message-scroller/message-scroller.meta.js +172 -0
- package/dist/components/meter/index.js +20 -0
- package/dist/components/meter/meter.js +112 -0
- package/dist/components/meter/meter.meta.js +227 -0
- package/dist/components/meter/meter.variants.js +61 -0
- package/dist/components/middle-truncate/index.js +7 -0
- package/dist/components/middle-truncate/middle-truncate.js +28 -0
- package/dist/components/middle-truncate/middle-truncate.meta.js +77 -0
- package/dist/components/note/index.js +9 -0
- package/dist/components/note/note.js +73 -0
- package/dist/components/note/note.meta.js +112 -0
- package/dist/components/note/note.variants.js +22 -0
- package/dist/components/notification-badge/index.js +15 -0
- package/dist/components/notification-badge/notification-badge.js +48 -0
- package/dist/components/notification-badge/notification-badge.meta.js +107 -0
- package/dist/components/notification-badge/notification-badge.variants.js +57 -0
- package/dist/components/number-field/index.js +7 -0
- package/dist/components/number-field/number-field.js +70 -0
- package/dist/components/number-field/number-field.meta.js +109 -0
- package/dist/components/otp-field/index.js +7 -0
- package/dist/components/otp-field/otp-field.js +70 -0
- package/dist/components/otp-field/otp-field.meta.js +197 -0
- package/dist/components/overflow-menu/index.js +7 -0
- package/dist/components/overflow-menu/overflow-menu.js +26 -0
- package/dist/components/overflow-menu/overflow-menu.meta.js +100 -0
- package/dist/components/page-header/index.js +7 -0
- package/dist/components/page-header/page-header.js +34 -0
- package/dist/components/page-header/page-header.meta.js +104 -0
- package/dist/components/page-loader/index.js +7 -0
- package/dist/components/page-loader/page-loader.js +37 -0
- package/dist/components/page-loader/page-loader.meta.js +85 -0
- package/dist/components/pagination/index.js +7 -0
- package/dist/components/pagination/pagination.js +105 -0
- package/dist/components/pagination/pagination.meta.js +141 -0
- package/dist/components/panel-group/index.js +10 -0
- package/dist/components/panel-group/panel-group.js +168 -0
- package/dist/components/panel-group/panel-group.meta.js +153 -0
- package/dist/components/panel-section/index.js +9 -0
- package/dist/components/panel-section/panel-section.js +115 -0
- package/dist/components/panel-section/panel-section.meta.js +156 -0
- package/dist/components/phone/index.js +7 -0
- package/dist/components/phone/phone.js +50 -0
- package/dist/{chunk-KFZ43RKU.js → components/phone/phone.meta.js} +3 -50
- package/dist/{chunk-NEQNXI2G.js → components/phone/status-icons.js} +4 -4
- package/dist/components/pie-chart/index.js +7 -0
- package/dist/components/pie-chart/pie-chart.js +191 -0
- package/dist/components/pie-chart/pie-chart.meta.js +210 -0
- package/dist/components/pill/index.js +9 -0
- package/dist/components/pill/pill.js +24 -0
- package/dist/components/pill/pill.meta.js +108 -0
- package/dist/components/pill/pill.variants.js +65 -0
- package/dist/components/popover/index.js +23 -0
- package/dist/components/popover/popover.js +100 -0
- package/dist/components/popover/popover.meta.js +136 -0
- package/dist/components/progress/index.js +19 -0
- package/dist/components/progress/progress.js +70 -0
- package/dist/components/progress/progress.meta.js +171 -0
- package/dist/components/progress/progress.variants.js +17 -0
- package/dist/components/radar-chart/index.js +7 -0
- package/dist/components/radar-chart/radar-chart.js +161 -0
- package/dist/components/radar-chart/radar-chart.meta.js +204 -0
- package/dist/components/radial-chart/index.js +7 -0
- package/dist/components/radial-chart/radial-chart.js +175 -0
- package/dist/components/radial-chart/radial-chart.meta.js +216 -0
- package/dist/components/radio/index.js +8 -0
- package/dist/components/radio/radio.js +39 -0
- package/dist/components/radio/radio.meta.js +104 -0
- package/dist/components/radio-card/index.js +7 -0
- package/dist/components/radio-card/radio-card.js +81 -0
- package/dist/components/radio-card/radio-card.meta.js +144 -0
- package/dist/components/radio-card/radio-card.variants.js +43 -0
- package/dist/components/relative-time/index.js +7 -0
- package/dist/components/relative-time/relative-time.js +47 -0
- package/dist/components/relative-time/relative-time.meta.js +77 -0
- package/dist/components/scroller/index.js +7 -0
- package/dist/components/scroller/scroller.js +111 -0
- package/dist/components/scroller/scroller.meta.js +118 -0
- package/dist/components/scrub-field/index.js +7 -0
- package/dist/components/scrub-field/scrub-field.js +124 -0
- package/dist/components/scrub-field/scrub-field.meta.js +139 -0
- package/dist/components/search-input/index.js +7 -0
- package/dist/components/search-input/search-input.js +91 -0
- package/dist/components/search-input/search-input.meta.js +129 -0
- package/dist/components/section-header/index.js +7 -0
- package/dist/components/section-header/section-header.js +39 -0
- package/dist/components/section-header/section-header.meta.js +174 -0
- package/dist/components/segmented-bar/index.js +14 -0
- package/dist/components/segmented-bar/segmented-bar.js +26 -0
- package/dist/components/segmented-bar/segmented-bar.meta.js +108 -0
- package/dist/components/segmented-bar/segmented-bar.variants.js +39 -0
- package/dist/components/select/index.js +25 -0
- package/dist/components/select/select.js +191 -0
- package/dist/components/select/select.meta.js +189 -0
- package/dist/components/separator/index.js +7 -0
- package/dist/components/separator/separator.js +42 -0
- package/dist/components/separator/separator.meta.js +84 -0
- package/dist/components/settings-menu/index.js +11 -0
- package/dist/components/settings-menu/settings-menu.js +101 -0
- package/dist/components/settings-menu/settings-menu.meta.js +177 -0
- package/dist/components/settings-menu/settings-menu.variants.js +15 -0
- package/dist/components/sheet/index.js +29 -0
- package/dist/components/sheet/sheet.js +121 -0
- package/dist/components/sheet/sheet.meta.js +167 -0
- package/dist/components/sheet/sheet.variants.js +21 -0
- package/dist/components/show-more/index.js +7 -0
- package/dist/components/show-more/show-more.js +87 -0
- package/dist/components/show-more/show-more.meta.js +79 -0
- package/dist/components/side-nav/index.js +13 -0
- package/dist/components/side-nav/side-nav.js +75 -0
- package/dist/components/side-nav/side-nav.meta.js +139 -0
- package/dist/components/side-nav/side-nav.variants.js +34 -0
- package/dist/components/sidebar/index.js +21 -0
- package/dist/components/sidebar/sidebar.js +196 -0
- package/dist/components/sidebar/sidebar.meta.js +118 -0
- package/dist/components/skeleton/index.js +7 -0
- package/dist/components/skeleton/skeleton.js +16 -0
- package/dist/components/skeleton/skeleton.meta.js +90 -0
- package/dist/components/slider/index.js +21 -0
- package/dist/components/slider/slider.js +115 -0
- package/dist/components/slider/slider.meta.js +214 -0
- package/dist/components/sparkline/index.js +7 -0
- package/dist/components/sparkline/sparkline.js +84 -0
- package/dist/components/sparkline/sparkline.meta.js +147 -0
- package/dist/components/spinner/index.js +7 -0
- package/dist/components/spinner/spinner.js +59 -0
- package/dist/components/spinner/spinner.meta.js +118 -0
- package/dist/components/status-bar/index.js +9 -0
- package/dist/components/status-bar/status-bar.js +40 -0
- package/dist/components/status-bar/status-bar.meta.js +117 -0
- package/dist/components/status-dot/index.js +9 -0
- package/dist/components/status-dot/status-dot.js +40 -0
- package/dist/components/status-dot/status-dot.meta.js +113 -0
- package/dist/components/status-dot/status-dot.variants.js +28 -0
- package/dist/components/stepper/index.js +17 -0
- package/dist/components/stepper/stepper.js +161 -0
- package/dist/components/stepper/stepper.meta.js +190 -0
- package/dist/components/stepper/stepper.variants.js +72 -0
- package/dist/components/streaming-text/index.js +7 -0
- package/dist/components/streaming-text/streaming-text.js +55 -0
- package/dist/components/streaming-text/streaming-text.meta.js +147 -0
- package/dist/components/switch/index.js +7 -0
- package/dist/components/switch/switch.js +26 -0
- package/dist/components/switch/switch.meta.js +133 -0
- package/dist/components/table/index.js +23 -0
- package/dist/components/table/table.js +117 -0
- package/dist/components/table/table.meta.js +143 -0
- package/dist/components/tabs/index.js +17 -0
- package/dist/components/tabs/tabs.js +58 -0
- package/dist/components/tabs/tabs.meta.js +120 -0
- package/dist/components/textarea/index.js +7 -0
- package/dist/components/textarea/textarea.js +38 -0
- package/dist/components/textarea/textarea.meta.js +117 -0
- package/dist/components/theme-selector/index.js +11 -0
- package/dist/components/theme-selector/theme-selector.js +145 -0
- package/dist/components/theme-selector/theme-selector.meta.js +181 -0
- package/dist/components/theme-switcher/index.js +7 -0
- package/dist/components/theme-switcher/theme-switcher.js +106 -0
- package/dist/components/theme-switcher/theme-switcher.meta.js +99 -0
- package/dist/components/thinking-orb/index.js +9 -0
- package/dist/components/thinking-orb/thinking-orb.js +126 -0
- package/dist/components/thinking-orb/thinking-orb.meta.js +142 -0
- package/dist/components/thinking-orb/thinking-orb.render.js +252 -0
- package/dist/components/toast/index.js +29 -0
- package/dist/components/toast/toast.js +254 -0
- package/dist/components/toast/toast.meta.js +261 -0
- package/dist/components/toggle/index.js +13 -0
- package/dist/components/toggle/toggle.js +51 -0
- package/dist/components/toggle/toggle.meta.js +129 -0
- package/dist/components/toolbar/index.js +27 -0
- package/dist/components/toolbar/toolbar.js +80 -0
- package/dist/components/toolbar/toolbar.meta.js +272 -0
- package/dist/components/toolbar/toolbar.variants.js +16 -0
- package/dist/components/tooltip/index.js +21 -0
- package/dist/components/tooltip/tooltip.js +74 -0
- package/dist/components/tooltip/tooltip.meta.js +156 -0
- package/dist/components/tree/index.js +12 -0
- package/dist/components/tree/tree.js +364 -0
- package/dist/components/tree/tree.meta.js +334 -0
- package/dist/components/video/index.js +7 -0
- package/dist/components/video/video.js +59 -0
- package/dist/components/video/video.meta.js +81 -0
- package/dist/components/workspace/index.js +9 -0
- package/dist/components/workspace/workspace.js +79 -0
- package/dist/components/workspace/workspace.meta.js +183 -0
- package/dist/foundations/accessibility.js +107 -0
- package/dist/foundations/index.js +25 -792
- package/dist/foundations/motion.js +135 -0
- package/dist/foundations/scrolling.js +62 -0
- package/dist/foundations/shape.js +113 -0
- package/dist/foundations/spacing.js +121 -0
- package/dist/foundations/surfaces.js +107 -0
- package/dist/foundations/types.js +0 -0
- package/dist/foundations/typography.js +152 -0
- package/dist/icon/Icon.js +30 -0
- package/dist/icon/IconProvider.js +27 -0
- package/dist/icon/compose.js +13 -0
- package/dist/icon/index.d.ts +13 -5
- package/dist/icon/index.js +13 -2
- package/dist/icon/registry.js +0 -0
- package/dist/icon-sets/brands.generated.js +123 -0
- package/dist/icon-sets/brands.js +10 -123
- package/dist/icon-sets/reicon.d.ts +3 -1
- package/dist/icon-sets/reicon.js +209 -1
- package/dist/index.d.ts +618 -34
- package/dist/index.js +211 -11351
- package/dist/ios/chrome.js +36 -0
- package/dist/ios/index.js +19 -315
- package/dist/ios/liquid-glass.js +103 -0
- package/dist/ios/native-app.js +46 -0
- package/dist/ios/navigation.js +63 -0
- package/dist/ios/phone-frame.js +64 -0
- package/dist/ios/preview-theme.js +83 -0
- package/dist/ios/preview.js +10 -137
- package/dist/ios/sheet.js +85 -0
- package/dist/ios/theme.js +19 -0
- package/dist/layout/AspectBox.js +27 -0
- package/dist/layout/Bleed.js +15 -0
- package/dist/layout/Center.js +21 -0
- package/dist/layout/Columns.js +70 -0
- package/dist/layout/Grid.js +50 -0
- package/dist/layout/GridOverlay.js +41 -0
- package/dist/layout/Page.js +11 -0
- package/dist/layout/Region.js +39 -0
- package/dist/layout/Row.js +45 -0
- package/dist/layout/SafeArea.js +26 -0
- package/dist/layout/Spacer.js +9 -0
- package/dist/layout/SplitPane.js +55 -0
- package/dist/layout/Stack.js +42 -0
- package/dist/layout/_responsive.js +16 -0
- package/dist/layout/_safeArea.js +18 -0
- package/dist/layout/index.d.ts +30 -8
- package/dist/layout/index.js +14 -2
- package/dist/lib/anchored-scroll.js +7 -0
- package/dist/lib/bleed.js +7 -0
- package/dist/lib/chart/cartesian.js +365 -0
- package/dist/lib/chart/chart-frame.js +163 -0
- package/dist/lib/chart/chart-legend.js +13 -0
- package/dist/lib/chart/chart-tooltip.js +38 -0
- package/dist/lib/chart/format.js +24 -0
- package/dist/lib/chart/geometry.js +114 -0
- package/dist/lib/chart/motion.js +34 -0
- package/dist/lib/chart/scale.js +72 -0
- package/dist/lib/chart/types.js +45 -0
- package/dist/lib/chart/use-arrival.js +41 -0
- package/dist/{chunk-SRQWQUTW.js → lib/cn.js} +5 -5
- package/dist/lib/component-guidance.js +215 -0
- package/dist/lib/density.js +54 -0
- package/dist/lib/field-shell.js +11 -0
- package/dist/lib/glyphs.js +9 -0
- package/dist/lib/heading-row.js +36 -0
- package/dist/lib/highlight-glide.js +84 -0
- package/dist/lib/hit-target.js +5 -0
- package/dist/lib/meta.js +0 -0
- package/dist/lib/portal-theme-scope.js +74 -0
- package/dist/{chunk-FKZXAFIY.js → lib/presentation.js} +8 -3
- package/dist/lib/refs.js +14 -0
- package/dist/lib/scrub-gesture.js +50 -0
- package/dist/lib/segmented.js +13 -0
- package/dist/lib/selection-card.js +41 -0
- package/dist/lib/status-tone.js +13 -0
- package/dist/lib/taxonomy.js +214 -0
- package/dist/lib/trim-label.js +20 -0
- package/dist/lib/use-copy.js +27 -0
- package/dist/lib/use-edge-fade.js +32 -0
- package/dist/lib/use-isomorphic-layout-effect.js +8 -0
- package/dist/lib/use-resize-observer.js +18 -0
- package/dist/lib/use-scroll-activity.js +52 -0
- package/dist/lib/use-wheel-to-horizontal.js +65 -0
- package/dist/motion/MotionProvider.js +75 -0
- package/dist/motion/Presence.js +112 -0
- package/dist/motion/Reveal.js +42 -0
- package/dist/motion/TextReveal.js +61 -0
- package/dist/motion/index.js +24 -0
- package/dist/motion/useInView.js +36 -0
- package/dist/motion/useReducedMotion.js +25 -0
- package/dist/motion/viewTransition.js +47 -0
- package/dist/patterns/auth-page.js +90 -0
- package/dist/patterns/auth-page.meta.js +27 -0
- package/dist/patterns/brand-landing-page.js +341 -0
- package/dist/patterns/brand-landing-page.meta.js +28 -0
- package/dist/patterns/chat-panel.js +185 -0
- package/dist/patterns/chat-panel.meta.js +31 -0
- package/dist/patterns/dashboard-page.js +332 -0
- package/dist/patterns/dashboard-page.meta.js +29 -0
- package/dist/patterns/feed-page.js +205 -0
- package/dist/patterns/feed-page.meta.js +25 -0
- package/dist/patterns/index.js +34 -2140
- package/dist/patterns/landing-page.js +356 -0
- package/dist/patterns/landing-page.meta.js +26 -0
- package/dist/patterns/list-detail-page.js +373 -0
- package/dist/patterns/list-detail-page.meta.js +24 -0
- package/dist/patterns/settings-page.js +268 -0
- package/dist/patterns/settings-page.meta.js +27 -0
- package/dist/presets/index.d.ts +7 -1
- package/dist/presets/index.js +53 -185
- package/dist/styles.css +118 -3
- package/dist/{theme-provider-kQszHYji.d.ts → theme-provider-S0NVJs2Y.d.ts} +6 -1
- package/dist/{chunk-NETUFI34.js → themes/_contract.js} +14 -2
- package/dist/themes/atelier/index.js +17 -1
- package/dist/themes/contrast.js +288 -0
- package/dist/themes/ember/ember.css +6 -6
- package/dist/themes/ember/index.js +16 -1
- package/dist/themes/index.d.ts +1 -1
- package/dist/themes/index.js +3 -8
- package/dist/themes/meridian/index.js +16 -1
- package/dist/themes/poppy/index.js +17 -1
- package/dist/themes/prism/index.js +17 -1
- package/dist/themes/prism/prism.css +2 -2
- package/dist/themes/registry.js +59 -0
- package/dist/themes/sable/index.js +16 -1
- package/dist/{chunk-EFLC4NA3.js → themes/theme-provider.js} +38 -54
- package/dist/themes/verdant/index.js +17 -1
- package/llms/blocks/media-player-block.md +20 -7
- package/llms/blocks/product-card-block.md +6 -4
- package/llms/blocks/task-list-block.md +1 -1
- package/llms/blocks/traffic-sources-block.md +1 -1
- package/llms/blocks/traffic-trend-block.md +5 -3
- package/llms/blocks/verify-code-block.md +1 -1
- package/llms/components/app-shell.md +5 -2
- package/llms/components/avatar.md +25 -3
- package/llms/components/button.md +20 -1
- package/llms/components/card.md +1 -1
- package/llms/components/disclosure-picker.md +168 -0
- package/llms/components/font-picker.md +104 -0
- package/llms/components/media-image.md +98 -0
- package/llms/components/media-tile.md +143 -0
- package/llms/components/page-header.md +6 -5
- package/llms/components/pie-chart.md +2 -2
- package/llms/components/radial-chart.md +1 -1
- package/llms/components/section-header.md +144 -0
- package/llms/components/sheet.md +10 -0
- package/llms/components/sidebar.md +4 -3
- package/llms/components/slider.md +14 -2
- package/llms/components/theme-selector.md +44 -4
- package/llms/components/tree.md +3 -3
- package/llms/layout/grid.md +24 -3
- package/llms/layout/region.md +16 -3
- package/llms/patterns/dashboard-page.md +3 -6
- package/llms/patterns/feed-page.md +4 -4
- package/llms/patterns/list-detail-page.md +1 -0
- package/llms/patterns/settings-page.md +2 -5
- package/llms/reference/utilities.md +84 -4
- package/llms/tasks/building-a-page.md +34 -3
- package/llms/tasks/converting-an-app.md +14 -4
- package/llms/tasks/creating-a-preset.md +4 -1
- package/llms/tasks/reviewing-a-design.md +19 -0
- package/llms/tasks/using-presets.md +19 -0
- package/llms-full.txt +1066 -78
- package/llms.txt +26 -9
- package/package.json +1 -1
- package/registry.json +2342 -164
- package/scripts/agent-setup.mjs +90 -0
- package/scripts/design-presets.mjs +59 -6
- package/scripts/grade.mjs +195 -29
- package/scripts/lint-rules.mjs +486 -10
- package/scripts/review-evidence.mjs +28 -1
- package/skills/sable/SKILL.md +67 -24
- package/targets/ios.json +45 -0
- package/dist/chunk-3IWZYBGH.js +0 -398
- package/dist/chunk-7MG4SHYX.js +0 -15
- package/dist/chunk-7QSDYBXO.js +0 -7421
- package/dist/chunk-ADLLDWLW.js +0 -15
- package/dist/chunk-C4Z3CF54.js +0 -15
- package/dist/chunk-EDXLM7YL.js +0 -5078
- package/dist/chunk-EHH4MHO6.js +0 -14
- package/dist/chunk-K7RUHUGC.js +0 -14
- package/dist/chunk-KO3NAEBF.js +0 -15
- package/dist/chunk-L4IEIQQA.js +0 -14
- package/dist/chunk-QJ6FWTUG.js +0 -40
- package/dist/chunk-VVW4UCDI.js +0 -4908
- package/dist/chunk-XAZGMY73.js +0 -187
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/disclosure-picker/disclosure-picker.tsx
|
|
4
|
+
import {
|
|
5
|
+
createContext,
|
|
6
|
+
useCallback,
|
|
7
|
+
useContext,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
useState
|
|
12
|
+
} from "react";
|
|
13
|
+
import { Icon } from "../../icon/index.js";
|
|
14
|
+
import { cn } from "../../lib/cn.js";
|
|
15
|
+
import { FIELD_SHELL_XS } from "../../lib/field-shell.js";
|
|
16
|
+
import { useIsomorphicLayoutEffect } from "../../lib/use-isomorphic-layout-effect.js";
|
|
17
|
+
import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "../collapsible/collapsible.js";
|
|
18
|
+
import { SearchInput } from "../search-input/search-input.js";
|
|
19
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
20
|
+
var Context = createContext(null);
|
|
21
|
+
function usePicker(part) {
|
|
22
|
+
const ctx = useContext(Context);
|
|
23
|
+
if (!ctx) throw new Error(`<${part}> must be rendered inside <DisclosurePicker>.`);
|
|
24
|
+
return ctx;
|
|
25
|
+
}
|
|
26
|
+
function useDisclosurePicker() {
|
|
27
|
+
const { open, setOpen, size } = usePicker("useDisclosurePicker");
|
|
28
|
+
return { open, close: () => setOpen(false), size };
|
|
29
|
+
}
|
|
30
|
+
var SEARCH_SELECTOR = "[data-disclosure-picker-search] input";
|
|
31
|
+
var OPTION = '[role="option"]';
|
|
32
|
+
var ENABLED_OPTION = '[role="option"]:not([aria-disabled="true"])';
|
|
33
|
+
var CHOSEN_OPTION = '[role="option"][aria-selected="true"]';
|
|
34
|
+
function chosenOption(panel) {
|
|
35
|
+
if (!panel) return null;
|
|
36
|
+
return panel.querySelector(CHOSEN_OPTION) ?? panel.querySelector(ENABLED_OPTION);
|
|
37
|
+
}
|
|
38
|
+
function DisclosurePicker({
|
|
39
|
+
open: openProp,
|
|
40
|
+
defaultOpen = false,
|
|
41
|
+
onOpenChange,
|
|
42
|
+
size = "md",
|
|
43
|
+
className,
|
|
44
|
+
ref,
|
|
45
|
+
children,
|
|
46
|
+
...props
|
|
47
|
+
}) {
|
|
48
|
+
const [uncontrolled, setUncontrolled] = useState(defaultOpen);
|
|
49
|
+
const controlled = openProp !== void 0;
|
|
50
|
+
const open = controlled ? openProp : uncontrolled;
|
|
51
|
+
const rootRef = useRef(null);
|
|
52
|
+
const triggerRef = useRef(null);
|
|
53
|
+
const panelRef = useRef(null);
|
|
54
|
+
const intentRef = useRef(null);
|
|
55
|
+
const focusInsideRef = useRef(false);
|
|
56
|
+
const setOpen = useCallback(
|
|
57
|
+
(next) => {
|
|
58
|
+
if (!controlled) setUncontrolled(next);
|
|
59
|
+
onOpenChange?.(next);
|
|
60
|
+
},
|
|
61
|
+
[controlled, onOpenChange]
|
|
62
|
+
);
|
|
63
|
+
const focusList = useCallback((preferSearch) => {
|
|
64
|
+
const panel = panelRef.current;
|
|
65
|
+
const search = preferSearch ? panel?.querySelector(SEARCH_SELECTOR) : null;
|
|
66
|
+
(search ?? chosenOption(panel))?.focus({ preventScroll: true });
|
|
67
|
+
}, []);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
const root = rootRef.current;
|
|
70
|
+
if (!open || !root) {
|
|
71
|
+
const active = document.activeElement;
|
|
72
|
+
const lost = !active || active === document.body;
|
|
73
|
+
if (panelRef.current?.contains(active) || focusInsideRef.current && lost) {
|
|
74
|
+
triggerRef.current?.focus({ preventScroll: true });
|
|
75
|
+
}
|
|
76
|
+
focusInsideRef.current = false;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => root.scrollIntoView?.({ block: "nearest" }));
|
|
80
|
+
observer?.observe(root);
|
|
81
|
+
const settle = window.setTimeout(() => observer?.disconnect(), 500);
|
|
82
|
+
const frame = requestAnimationFrame(() => {
|
|
83
|
+
const panel = panelRef.current;
|
|
84
|
+
const chosen = panel?.querySelector(CHOSEN_OPTION);
|
|
85
|
+
const list = chosen?.closest('[role="listbox"]');
|
|
86
|
+
if (list && chosen && list.scrollHeight > list.clientHeight) {
|
|
87
|
+
list.scrollTop = chosen.offsetTop - (list.clientHeight - chosen.offsetHeight) / 2;
|
|
88
|
+
}
|
|
89
|
+
const intent = intentRef.current;
|
|
90
|
+
intentRef.current = null;
|
|
91
|
+
if (!intent) return;
|
|
92
|
+
const hasSearch = !!panel?.querySelector(SEARCH_SELECTOR);
|
|
93
|
+
const finePointer = typeof window.matchMedia === "function" && window.matchMedia("(pointer: fine)").matches;
|
|
94
|
+
if (intent === "keyboard") focusList(true);
|
|
95
|
+
else if (hasSearch && finePointer) focusList(true);
|
|
96
|
+
});
|
|
97
|
+
return () => {
|
|
98
|
+
observer?.disconnect();
|
|
99
|
+
window.clearTimeout(settle);
|
|
100
|
+
cancelAnimationFrame(frame);
|
|
101
|
+
};
|
|
102
|
+
}, [open, focusList]);
|
|
103
|
+
const value = useMemo(
|
|
104
|
+
() => ({ open, setOpen, size, triggerRef, panelRef, intentRef, focusList }),
|
|
105
|
+
[open, setOpen, size, focusList]
|
|
106
|
+
);
|
|
107
|
+
return /* @__PURE__ */ jsx(Context.Provider, { value, children: /* @__PURE__ */ jsx(
|
|
108
|
+
Collapsible,
|
|
109
|
+
{
|
|
110
|
+
ref: (node) => {
|
|
111
|
+
rootRef.current = node;
|
|
112
|
+
if (typeof ref === "function") ref(node);
|
|
113
|
+
else if (ref) ref.current = node;
|
|
114
|
+
},
|
|
115
|
+
open,
|
|
116
|
+
onOpenChange: (next) => {
|
|
117
|
+
if (!next) intentRef.current = null;
|
|
118
|
+
setOpen(next);
|
|
119
|
+
},
|
|
120
|
+
"data-size": size,
|
|
121
|
+
onFocus: (event) => {
|
|
122
|
+
if (panelRef.current?.contains(event.target)) focusInsideRef.current = true;
|
|
123
|
+
},
|
|
124
|
+
onBlur: (event) => {
|
|
125
|
+
if (!panelRef.current?.contains(event.relatedTarget)) focusInsideRef.current = false;
|
|
126
|
+
},
|
|
127
|
+
className: cn(
|
|
128
|
+
// Breathing room when the row scrolls itself into view.
|
|
129
|
+
"scroll-my-3",
|
|
130
|
+
size === "xs" ? "rounded-[var(--panel-field-radius,var(--radius-xs))]" : "rounded-sm",
|
|
131
|
+
// Open, the row and its list share one recessed well.
|
|
132
|
+
open && "sable-well",
|
|
133
|
+
className
|
|
134
|
+
),
|
|
135
|
+
...props,
|
|
136
|
+
children
|
|
137
|
+
}
|
|
138
|
+
) });
|
|
139
|
+
}
|
|
140
|
+
DisclosurePicker.displayName = "DisclosurePicker";
|
|
141
|
+
function DisclosurePickerTrigger({
|
|
142
|
+
label,
|
|
143
|
+
media,
|
|
144
|
+
children,
|
|
145
|
+
className,
|
|
146
|
+
ref,
|
|
147
|
+
onKeyDown,
|
|
148
|
+
onPointerDown,
|
|
149
|
+
...props
|
|
150
|
+
}) {
|
|
151
|
+
const { open, setOpen, size, triggerRef, intentRef, focusList } = usePicker("DisclosurePickerTrigger");
|
|
152
|
+
return /* @__PURE__ */ jsxs(
|
|
153
|
+
CollapsibleTrigger,
|
|
154
|
+
{
|
|
155
|
+
ref: (node) => {
|
|
156
|
+
triggerRef.current = node;
|
|
157
|
+
if (typeof ref === "function") ref(node);
|
|
158
|
+
else if (ref) ref.current = node;
|
|
159
|
+
},
|
|
160
|
+
onPointerDown: (event) => {
|
|
161
|
+
intentRef.current = "pointer";
|
|
162
|
+
onPointerDown?.(event);
|
|
163
|
+
},
|
|
164
|
+
onKeyDown: (event) => {
|
|
165
|
+
onKeyDown?.(event);
|
|
166
|
+
if (event.defaultPrevented) return;
|
|
167
|
+
if (event.key === "Enter" || event.key === " ") intentRef.current = "keyboard";
|
|
168
|
+
if (event.key === "Escape" && open) {
|
|
169
|
+
event.preventDefault();
|
|
170
|
+
event.stopPropagation();
|
|
171
|
+
setOpen(false);
|
|
172
|
+
}
|
|
173
|
+
if (event.key === "ArrowDown") {
|
|
174
|
+
event.preventDefault();
|
|
175
|
+
if (open) focusList(true);
|
|
176
|
+
else {
|
|
177
|
+
intentRef.current = "keyboard";
|
|
178
|
+
setOpen(true);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
className: cn(
|
|
183
|
+
"group/disclosure-picker flex w-full items-center gap-2 text-start text-foreground motion-interactive",
|
|
184
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
185
|
+
size === "xs" ? cn("h-(--control-xs) px-1.5", FIELD_SHELL_XS) : cn(
|
|
186
|
+
// Hover is a step off whatever surface the row sits on, never a
|
|
187
|
+
// fixed fill: a popover-coloured hover read as a hole in the panel.
|
|
188
|
+
"h-(--control-md) rounded-[inherit] px-3 sable-surface-interactive",
|
|
189
|
+
// Closed, the row is its own well; open, the root's well holds both.
|
|
190
|
+
!open && "sable-well"
|
|
191
|
+
),
|
|
192
|
+
className
|
|
193
|
+
),
|
|
194
|
+
...props,
|
|
195
|
+
children: [
|
|
196
|
+
label != null ? /* @__PURE__ */ jsx("span", { className: cn("shrink-0 truncate text-muted-foreground", size === "xs" ? "w-16 text-ui-md" : "w-20 text-body-sm"), children: label }) : null,
|
|
197
|
+
media != null ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", children: media }) : null,
|
|
198
|
+
/* @__PURE__ */ jsx("span", { className: cn("min-w-0 flex-1 truncate", size === "xs" ? "text-ui-md" : "text-body-md"), children }),
|
|
199
|
+
/* @__PURE__ */ jsx(
|
|
200
|
+
Icon,
|
|
201
|
+
{
|
|
202
|
+
name: "chevron-down",
|
|
203
|
+
className: "shrink-0 text-sm text-muted-foreground motion-interactive group-data-[panel-open]/disclosure-picker:rotate-180"
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
DisclosurePickerTrigger.displayName = "DisclosurePickerTrigger";
|
|
211
|
+
function DisclosurePickerPanel({ search, children, className, ...props }) {
|
|
212
|
+
const { open, setOpen, panelRef, focusList } = usePicker("DisclosurePickerPanel");
|
|
213
|
+
const clear = search?.value ? search.onValueChange : null;
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
if (!open && clear) clear("");
|
|
216
|
+
}, [open, clear]);
|
|
217
|
+
return /* @__PURE__ */ jsx(CollapsiblePanel, { ...props, children: /* @__PURE__ */ jsxs(
|
|
218
|
+
"div",
|
|
219
|
+
{
|
|
220
|
+
ref: panelRef,
|
|
221
|
+
onKeyDown: (event) => {
|
|
222
|
+
if (event.key === "Escape") {
|
|
223
|
+
event.preventDefault();
|
|
224
|
+
event.stopPropagation();
|
|
225
|
+
const target = event.target;
|
|
226
|
+
if (search?.value && target.matches(SEARCH_SELECTOR)) search.onValueChange("");
|
|
227
|
+
else setOpen(false);
|
|
228
|
+
} else if (event.key === "ArrowDown" && event.target.matches(SEARCH_SELECTOR)) {
|
|
229
|
+
event.preventDefault();
|
|
230
|
+
focusList(false);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
className: cn("grid gap-1 border-t border-hairline p-1.5", className),
|
|
234
|
+
children: [
|
|
235
|
+
search ? /* @__PURE__ */ jsx("div", { "data-disclosure-picker-search": "", children: /* @__PURE__ */ jsx(
|
|
236
|
+
SearchInput,
|
|
237
|
+
{
|
|
238
|
+
value: search.value,
|
|
239
|
+
onValueChange: search.onValueChange,
|
|
240
|
+
placeholder: search.placeholder ?? "Search",
|
|
241
|
+
"aria-label": search.label ?? search.placeholder ?? "Search"
|
|
242
|
+
}
|
|
243
|
+
) }) : null,
|
|
244
|
+
children
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
) });
|
|
248
|
+
}
|
|
249
|
+
DisclosurePickerPanel.displayName = "DisclosurePickerPanel";
|
|
250
|
+
var ListCtx = createContext(null);
|
|
251
|
+
var TYPEAHEAD_MS = 500;
|
|
252
|
+
function textOf(option) {
|
|
253
|
+
return (option.dataset.textValue ?? option.querySelector("[data-disclosure-picker-label]")?.textContent ?? option.textContent ?? "").trim().toLowerCase();
|
|
254
|
+
}
|
|
255
|
+
function moveTo(option) {
|
|
256
|
+
if (!option) return;
|
|
257
|
+
option.focus({ preventScroll: true });
|
|
258
|
+
option.scrollIntoView?.({ block: "nearest" });
|
|
259
|
+
}
|
|
260
|
+
function DisclosurePickerList({ value, onValueChange, className, ref, onKeyDown, onFocus, children, ...props }) {
|
|
261
|
+
const { setOpen, size, panelRef } = usePicker("DisclosurePickerList");
|
|
262
|
+
const listRef = useRef(null);
|
|
263
|
+
const typeahead = useRef({ text: "", timer: 0 });
|
|
264
|
+
const pick = useCallback(
|
|
265
|
+
(next) => {
|
|
266
|
+
if (next !== value) onValueChange(next);
|
|
267
|
+
setOpen(false);
|
|
268
|
+
},
|
|
269
|
+
[value, onValueChange, setOpen]
|
|
270
|
+
);
|
|
271
|
+
useIsomorphicLayoutEffect(() => {
|
|
272
|
+
const list = listRef.current;
|
|
273
|
+
if (!list) return;
|
|
274
|
+
const options = [...list.querySelectorAll(OPTION)];
|
|
275
|
+
const enabled = (option) => option.getAttribute("aria-disabled") !== "true";
|
|
276
|
+
const stop = options.find((option) => option === document.activeElement) ?? options.find((option) => option.getAttribute("aria-selected") === "true" && enabled(option)) ?? options.find(enabled);
|
|
277
|
+
for (const option of options) option.tabIndex = option === stop ? 0 : -1;
|
|
278
|
+
});
|
|
279
|
+
useEffect(() => () => window.clearTimeout(typeahead.current.timer), []);
|
|
280
|
+
const handleKeyDown = (event) => {
|
|
281
|
+
onKeyDown?.(event);
|
|
282
|
+
const list = listRef.current;
|
|
283
|
+
if (event.defaultPrevented || !list) return;
|
|
284
|
+
const current = event.target.closest(OPTION);
|
|
285
|
+
if (!current || !list.contains(current)) return;
|
|
286
|
+
const options = [...list.querySelectorAll(ENABLED_OPTION)];
|
|
287
|
+
const index = options.indexOf(current);
|
|
288
|
+
const run = typeahead.current;
|
|
289
|
+
const typing = event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
290
|
+
if (typing && (event.key !== " " || run.text)) {
|
|
291
|
+
event.preventDefault();
|
|
292
|
+
window.clearTimeout(run.timer);
|
|
293
|
+
run.text += event.key.toLowerCase();
|
|
294
|
+
run.timer = window.setTimeout(() => {
|
|
295
|
+
run.text = "";
|
|
296
|
+
}, TYPEAHEAD_MS);
|
|
297
|
+
const repeat = [...run.text].every((char) => char === run.text[0]);
|
|
298
|
+
const needle = repeat ? run.text[0] : run.text;
|
|
299
|
+
const from = repeat ? index + 1 : Math.max(index, 0);
|
|
300
|
+
const ordered = [...options.slice(from), ...options.slice(0, from)];
|
|
301
|
+
moveTo(ordered.find((option) => textOf(option).startsWith(needle)));
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
switch (event.key) {
|
|
305
|
+
case "ArrowDown":
|
|
306
|
+
event.preventDefault();
|
|
307
|
+
moveTo(options[Math.min(index + 1, options.length - 1)]);
|
|
308
|
+
break;
|
|
309
|
+
case "ArrowUp": {
|
|
310
|
+
event.preventDefault();
|
|
311
|
+
const search = index <= 0 ? panelRef.current?.querySelector(SEARCH_SELECTOR) : null;
|
|
312
|
+
if (search) search.focus();
|
|
313
|
+
else moveTo(options[Math.max(index - 1, 0)]);
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
case "Home":
|
|
317
|
+
event.preventDefault();
|
|
318
|
+
moveTo(options[0]);
|
|
319
|
+
break;
|
|
320
|
+
case "End":
|
|
321
|
+
event.preventDefault();
|
|
322
|
+
moveTo(options[options.length - 1]);
|
|
323
|
+
break;
|
|
324
|
+
case "Enter":
|
|
325
|
+
case " ": {
|
|
326
|
+
event.preventDefault();
|
|
327
|
+
const next = current.getAttribute("aria-disabled") === "true" ? void 0 : current.dataset.value;
|
|
328
|
+
if (next !== void 0) pick(next);
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
const context = useMemo(() => ({ value, size, pick }), [value, size, pick]);
|
|
334
|
+
return /* @__PURE__ */ jsx(ListCtx.Provider, { value: context, children: /* @__PURE__ */ jsx(
|
|
335
|
+
"div",
|
|
336
|
+
{
|
|
337
|
+
ref: (node) => {
|
|
338
|
+
listRef.current = node;
|
|
339
|
+
if (typeof ref === "function") ref(node);
|
|
340
|
+
else if (ref) ref.current = node;
|
|
341
|
+
},
|
|
342
|
+
role: "listbox",
|
|
343
|
+
onKeyDown: handleKeyDown,
|
|
344
|
+
onFocus: (event) => {
|
|
345
|
+
onFocus?.(event);
|
|
346
|
+
const option = event.target.closest(OPTION);
|
|
347
|
+
if (!option || !listRef.current?.contains(option)) return;
|
|
348
|
+
for (const other of listRef.current.querySelectorAll(OPTION)) other.tabIndex = other === option ? 0 : -1;
|
|
349
|
+
},
|
|
350
|
+
className: cn("relative grid max-h-64 gap-0.5 overflow-y-auto scrollbar-quiet", className),
|
|
351
|
+
...props,
|
|
352
|
+
children
|
|
353
|
+
}
|
|
354
|
+
) });
|
|
355
|
+
}
|
|
356
|
+
DisclosurePickerList.displayName = "DisclosurePickerList";
|
|
357
|
+
function DisclosurePickerOption({
|
|
358
|
+
value,
|
|
359
|
+
children,
|
|
360
|
+
media,
|
|
361
|
+
note,
|
|
362
|
+
disabled = false,
|
|
363
|
+
textValue,
|
|
364
|
+
className,
|
|
365
|
+
onClick,
|
|
366
|
+
...props
|
|
367
|
+
}) {
|
|
368
|
+
const list = useContext(ListCtx);
|
|
369
|
+
if (!list) throw new Error("<DisclosurePickerOption> must be rendered inside <DisclosurePickerList>.");
|
|
370
|
+
const { size, pick } = list;
|
|
371
|
+
const selected = list.value === value;
|
|
372
|
+
return /* @__PURE__ */ jsxs(
|
|
373
|
+
"div",
|
|
374
|
+
{
|
|
375
|
+
...props,
|
|
376
|
+
role: "option",
|
|
377
|
+
"aria-selected": selected,
|
|
378
|
+
"aria-disabled": disabled || void 0,
|
|
379
|
+
"data-value": value,
|
|
380
|
+
"data-text-value": textValue,
|
|
381
|
+
"data-selected": selected ? "" : void 0,
|
|
382
|
+
onClick: (event) => {
|
|
383
|
+
onClick?.(event);
|
|
384
|
+
if (!event.defaultPrevented && !disabled) pick(value);
|
|
385
|
+
},
|
|
386
|
+
className: cn(
|
|
387
|
+
"flex items-center gap-3 rounded-sm px-(--item-px) text-start motion-interactive",
|
|
388
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring aria-disabled:cursor-not-allowed aria-disabled:opacity-50",
|
|
389
|
+
size === "xs" ? "min-h-(--control-xs)" : "min-h-(--control-sm) py-1",
|
|
390
|
+
// Chosen: lifted out of the well, like a segmented control's knob.
|
|
391
|
+
selected ? "bg-popover shadow-sm" : "sable-surface-interactive",
|
|
392
|
+
className
|
|
393
|
+
),
|
|
394
|
+
children: [
|
|
395
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
396
|
+
media != null ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", children: media }) : null,
|
|
397
|
+
/* @__PURE__ */ jsx("span", { "data-disclosure-picker-label": "", className: cn("min-w-0 truncate", size === "xs" ? "text-ui-md" : "text-body-md"), children })
|
|
398
|
+
] }),
|
|
399
|
+
note != null ? /* @__PURE__ */ jsx("span", { className: cn("shrink-0 text-muted-foreground", size === "xs" ? "text-ui-sm" : "text-caption"), children: note }) : selected ? /* @__PURE__ */ jsx(Icon, { name: "check", className: "shrink-0 text-sm" }) : null
|
|
400
|
+
]
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
DisclosurePickerOption.displayName = "DisclosurePickerOption";
|
|
405
|
+
function DisclosurePickerEmpty({ children, className }) {
|
|
406
|
+
return /* @__PURE__ */ jsx("p", { className: cn("px-2.5 py-2 text-caption text-muted-foreground", className), children });
|
|
407
|
+
}
|
|
408
|
+
DisclosurePickerEmpty.displayName = "DisclosurePickerEmpty";
|
|
409
|
+
export {
|
|
410
|
+
DisclosurePicker,
|
|
411
|
+
DisclosurePickerEmpty,
|
|
412
|
+
DisclosurePickerList,
|
|
413
|
+
DisclosurePickerOption,
|
|
414
|
+
DisclosurePickerPanel,
|
|
415
|
+
DisclosurePickerTrigger,
|
|
416
|
+
useDisclosurePicker
|
|
417
|
+
};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// src/components/disclosure-picker/disclosure-picker.meta.ts
|
|
2
|
+
var disclosurePickerMeta = {
|
|
3
|
+
name: "DisclosurePicker",
|
|
4
|
+
summary: "A property row that names the current value and opens its list in place, beneath the row, instead of in a popup \u2014 for a tool panel or settings column where the choice is made while looking at what it changes. Built on Collapsible; the list is a listbox \u2014 arrow keys move, Enter or a click picks, applies and closes \u2014 and an optional search field filters long lists.",
|
|
5
|
+
status: "stable",
|
|
6
|
+
keywords: [
|
|
7
|
+
"disclosure picker",
|
|
8
|
+
"inline picker",
|
|
9
|
+
"in-place select",
|
|
10
|
+
"expanding list",
|
|
11
|
+
"property row",
|
|
12
|
+
"font picker",
|
|
13
|
+
"preset picker",
|
|
14
|
+
"inspector",
|
|
15
|
+
"picker row"
|
|
16
|
+
],
|
|
17
|
+
usage: {
|
|
18
|
+
description: "Use DisclosurePicker for one-of-many choices in a docked panel or inspector where a popup would cover the preview the choice is about: the row shows the current value (in its own face, with a tile, as the content warrants), and the list opens beneath it, pushing the panel down. Use Select for a form field or anywhere a popup is fine; use Combobox when people type to find an option among hundreds; use RadioGroup or RadioCard when every option should stay visible. Nothing applies while arrowing through the list \u2014 only Enter, Space or a click picks \u2014 so it suits choices with a cost (a confirm, a font load, a reset); where each option should apply as focus lands on it, that is a radio group (ThemeSelector, RadioGroup).",
|
|
19
|
+
bestPractices: [
|
|
20
|
+
{
|
|
21
|
+
guidance: "do",
|
|
22
|
+
description: "Enable search (`search` on DisclosurePickerPanel) only for long lists \u2014 roughly a dozen options or more, i.e. once the list scrolls. A short list (a handful of presets) reads faster with no field to skip past; the panel then has no search row and no gap."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
guidance: "do",
|
|
26
|
+
description: "Show the value as the thing it is: a font row renders the family in its own face, a theme row leads with a ThemePreview tile in `media`. The row is a preview of the choice, not a label for it."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
guidance: "do",
|
|
30
|
+
description: 'Use `size="xs"` in a property grid (a compact Density / PanelSection), where it sits on the inset panel-field fill beside ScrubField and an xs SelectTrigger. Keep `md` in a settings column or a simple panel.'
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
guidance: "do",
|
|
34
|
+
description: "When several rows share a panel, control `open` from the parent so only one list is open at a time \u2014 two open lists push each other and the preview out of view."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
guidance: "do",
|
|
38
|
+
description: "Use it for a choice with a cost \u2014 one that opens a confirm, loads something, or resets other settings. Arrow keys only move focus; onValueChange fires once, on Enter, Space or a click, and only when the pick differs from `value`."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
guidance: "do",
|
|
42
|
+
description: "Give options a leading visual with `media` (a ThemePreview tile, a swatch) instead of nesting another component's list: ThemeSelector's radio list applies each theme as the arrow keys land on it, which defeats commit-on-Enter. A list the picker cannot express (a Tree) can still close the row with `useDisclosurePicker().close()`."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
guidance: "do",
|
|
46
|
+
description: "Render DisclosurePickerEmpty in place of the list when a search matches nothing, not inside it \u2014 a listbox holds only options."
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
guidance: "dont",
|
|
50
|
+
description: "Use it in a page-register form. A form field that pushes the form down on open reads as a layout jump; that is Select's job."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
guidance: "dont",
|
|
54
|
+
description: "Rebuild the row from a Collapsible with hand-copied classes. The disclosure, the well, the hover step, the xs field shell, focus return and scroll-into-view all live here; two hand copies of this row in one panel is how it came to exist."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
guidance: "dont",
|
|
58
|
+
description: "Turn search on for a short list just because the other rows have it. A search field over six options is a control with nothing to do."
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
anatomy: [
|
|
62
|
+
{ name: "DisclosurePicker", required: true, description: "Root. Owns open state, size, focus return and scroll-into-view." },
|
|
63
|
+
{ name: "DisclosurePickerTrigger", required: true, description: "The row: optional `label` caption, optional `media`, the value, and a chevron that flips when open." },
|
|
64
|
+
{ name: "DisclosurePickerPanel", required: true, description: "What opens beneath the row: the opt-in search field, then the list." },
|
|
65
|
+
{ name: "DisclosurePickerList", required: false, description: "A listbox of options that scrolls past 16rem. Arrow keys move; Enter, Space or a click picks." },
|
|
66
|
+
{ name: "DisclosurePickerOption", required: false, description: "One option row, with optional leading media and trailing note. Chosen, it lifts out of the well; focused, it takes the ring." },
|
|
67
|
+
{ name: "DisclosurePickerEmpty", required: false, description: "The no-match line, rendered in place of the list when a search filters everything out." }
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
related: {
|
|
71
|
+
components: ["Collapsible", "Select", "Combobox", "SearchInput", "RadioGroup", "ThemeSelector", "FontPicker", "PropertyRow"]
|
|
72
|
+
},
|
|
73
|
+
props: [
|
|
74
|
+
{ name: "open", type: "boolean", description: "Controlled open state. Pair with onOpenChange." },
|
|
75
|
+
{ name: "defaultOpen", type: "boolean", default: "false", description: "Uncontrolled initial open state." },
|
|
76
|
+
{ name: "onOpenChange", type: "(open: boolean) => void", description: "Called with the next open state \u2014 from the trigger, a pick, or Escape." },
|
|
77
|
+
{
|
|
78
|
+
name: "size",
|
|
79
|
+
type: '"md" | "xs"',
|
|
80
|
+
default: '"md"',
|
|
81
|
+
description: "md is a --control-md row (40px comfortable) in a settings column, its own well when closed. xs is the property-grid rung: a --control-xs row on the inset panel-field fill with ui-tier type. Height, padding, radius, type and surface move together."
|
|
82
|
+
},
|
|
83
|
+
{ name: "className", type: "string", description: "Merged onto the root (layout only \u2014 the look comes from size)." }
|
|
84
|
+
],
|
|
85
|
+
subcomponents: [
|
|
86
|
+
{
|
|
87
|
+
name: "DisclosurePickerTrigger",
|
|
88
|
+
type: "{ label?: ReactNode; media?: ReactNode; children?: ReactNode } + Base UI Collapsible.Trigger props",
|
|
89
|
+
description: "The row. `label` is a muted leading caption on a fixed column so stacked rows align; `media` a leading visual (a ThemePreview tile); children the value, truncated on one line. ArrowDown opens it, and steps into the list once open."
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "DisclosurePickerPanel",
|
|
93
|
+
type: "{ search?: { value: string; onValueChange(value: string): void; placeholder?: string; label?: string } } + Collapsible.Panel props",
|
|
94
|
+
description: "Opens beneath the row. `search` opts in to a SearchInput above the list (off by default); it is controlled because the consumer filters, and it is cleared when the row closes. Escape clears a query first, then closes; ArrowDown in the field steps into the list."
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "DisclosurePickerList",
|
|
98
|
+
type: "{ value: string; onValueChange(value: string): void; aria-label?: string } + div props",
|
|
99
|
+
description: "A listbox. ArrowUp/ArrowDown, Home/End and type-ahead move focus without applying anything (ArrowUp from the first option returns to the search field); Enter, Space or a click picks the focused option \u2014 onValueChange fires once, only when it differs from `value` \u2014 and closes the row. Escape closes without a pick."
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "DisclosurePickerOption",
|
|
103
|
+
type: "{ value: string; children?: ReactNode; media?: ReactNode; note?: ReactNode; disabled?: boolean; textValue?: string }",
|
|
104
|
+
description: "One option row. `media` is a leading visual (a ThemePreview tile); `note` a muted trailing annotation \u2014 options without one show a check when chosen. `disabled` skips it for the keyboard and refuses the pick; `textValue` is what type-ahead matches when the name is not plain text."
|
|
105
|
+
},
|
|
106
|
+
{ name: "DisclosurePickerEmpty", type: "{ children?: ReactNode }", description: "The no-match line, rendered in place of the list." },
|
|
107
|
+
{
|
|
108
|
+
name: "useDisclosurePicker",
|
|
109
|
+
type: '() => { open: boolean; close(): void; size: "md" | "xs" }',
|
|
110
|
+
description: "For a list the picker does not own (a Tree) that should close the row when something is picked."
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
tokensUsed: ["foreground", "muted-foreground", "popover", "hairline", "panel-field", "panel-field-hover", "ring", "accent"],
|
|
114
|
+
a11y: [
|
|
115
|
+
"The trigger is a real <button>; Base UI wires aria-expanded and aria-controls to the panel. Its accessible name is its text (caption + value) unless an aria-label is given.",
|
|
116
|
+
'The list is role="listbox" with role="option" rows; aria-selected marks the committed value, not the focused row, so a screen reader hears both where focus is and what is chosen. Selection does not follow focus: ArrowUp/ArrowDown, Home/End and type-ahead move focus (roving tabindex \u2014 one tab stop, so Tab leaves the list in one step) and apply nothing; Enter, Space or a click picks, applies and closes. Disabled options carry aria-disabled and are skipped.',
|
|
117
|
+
"Why a listbox and not a radio group: a radio group's checked state follows focus, so every arrow press would be a pick \u2014 wrong for a choice that opens a confirm, loads a font or resets settings. Base UI ships no standalone listbox (Select is popup-only, Combobox's inline list is focus-in-the-input, Composite is internal), so the keyboard model lives here.",
|
|
118
|
+
"Opening from the keyboard (Enter, Space, ArrowDown) moves focus into the panel: the search field when there is one, otherwise the chosen option (or the first). A pointer open focuses the search field on a fine pointer only, so touch does not raise the on-screen keyboard.",
|
|
119
|
+
"Escape closes the row without changing the value (clearing a query first) and returns focus to the trigger; a pick does the same. Focus is not pulled back when it has already moved on, e.g. to a confirm dialog the pick opened.",
|
|
120
|
+
"The panel animates with motion-disclosure, which is static under prefers-reduced-motion or MotionProvider off; the scroll that keeps the row in view is instant, never smooth."
|
|
121
|
+
],
|
|
122
|
+
motion: [
|
|
123
|
+
{ slot: "arrival", recipe: "none", part: "root", why: "A property row is present from first paint; animating it in would stage chrome the user is already scanning." },
|
|
124
|
+
{
|
|
125
|
+
slot: "response",
|
|
126
|
+
recipe: "motion-interactive",
|
|
127
|
+
part: "DisclosurePickerTrigger",
|
|
128
|
+
why: "The row reads as a value, not a button; the hover step is what says it opens before it is clicked."
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
slot: "transition",
|
|
132
|
+
recipe: "motion-disclosure",
|
|
133
|
+
part: "DisclosurePickerPanel",
|
|
134
|
+
why: "The list pushes the panel down; growing its height ties the movement below to this row rather than to a layout jump."
|
|
135
|
+
},
|
|
136
|
+
{ slot: "activity", recipe: "none", part: "root", why: "Nothing is indeterminate: the options are resident. A list that loads should show its own loader inside the panel." }
|
|
137
|
+
],
|
|
138
|
+
examples: [
|
|
139
|
+
{
|
|
140
|
+
label: "A short list \u2014 no search",
|
|
141
|
+
code: `const [preset, setPreset] = useState("comfortable")
|
|
142
|
+
const [open, setOpen] = useState(false)
|
|
143
|
+
<DisclosurePicker open={open} onOpenChange={setOpen} className="w-72">
|
|
144
|
+
<DisclosurePickerTrigger label="Density">{preset === "compact" ? "Compact" : "Comfortable"}</DisclosurePickerTrigger>
|
|
145
|
+
<DisclosurePickerPanel>
|
|
146
|
+
<DisclosurePickerList aria-label="Density" value={preset} onValueChange={setPreset}>
|
|
147
|
+
<DisclosurePickerOption value="comfortable">Comfortable</DisclosurePickerOption>
|
|
148
|
+
<DisclosurePickerOption value="compact">Compact</DisclosurePickerOption>
|
|
149
|
+
</DisclosurePickerList>
|
|
150
|
+
</DisclosurePickerPanel>
|
|
151
|
+
</DisclosurePicker>`
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
label: "A long list \u2014 search opted in",
|
|
155
|
+
code: `const COUNTRIES = ["Argentina", "Australia", "Austria", "Belgium", "Brazil", "Canada", "Chile", "Denmark", "Finland", "France", "Germany", "Ireland", "Italy", "Japan", "Mexico", "Norway", "Portugal", "Spain", "Sweden"]
|
|
156
|
+
const [country, setCountry] = useState("Canada")
|
|
157
|
+
const [query, setQuery] = useState("")
|
|
158
|
+
const shown = COUNTRIES.filter((name) => name.toLowerCase().includes(query.trim().toLowerCase()))
|
|
159
|
+
<DisclosurePicker className="w-72">
|
|
160
|
+
<DisclosurePickerTrigger label="Country">{country}</DisclosurePickerTrigger>
|
|
161
|
+
<DisclosurePickerPanel search={{ value: query, onValueChange: setQuery, placeholder: \`Search \${COUNTRIES.length} countries\` }}>
|
|
162
|
+
{shown.length > 0 ? (
|
|
163
|
+
<DisclosurePickerList aria-label="Country" value={country} onValueChange={setCountry}>
|
|
164
|
+
{shown.map((name) => (
|
|
165
|
+
<DisclosurePickerOption key={name} value={name}>{name}</DisclosurePickerOption>
|
|
166
|
+
))}
|
|
167
|
+
</DisclosurePickerList>
|
|
168
|
+
) : (
|
|
169
|
+
<DisclosurePickerEmpty>No countries match \u201C{query}\u201D.</DisclosurePickerEmpty>
|
|
170
|
+
)}
|
|
171
|
+
</DisclosurePickerPanel>
|
|
172
|
+
</DisclosurePicker>`
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
label: "A theme picker \u2014 media tiles, commit on Enter",
|
|
176
|
+
code: `const { themeId, setThemeId, themes, mode } = useSableTheme()
|
|
177
|
+
const current = themes.find((theme) => theme.id === themeId)
|
|
178
|
+
<DisclosurePicker>
|
|
179
|
+
<DisclosurePickerTrigger aria-label={\`Theme: \${current?.label}\`} media={current ? <ThemePreview theme={current} mode={mode} size="sm" /> : null}>
|
|
180
|
+
{current?.label}
|
|
181
|
+
</DisclosurePickerTrigger>
|
|
182
|
+
<DisclosurePickerPanel>
|
|
183
|
+
<DisclosurePickerList aria-label="Theme" value={themeId} onValueChange={setThemeId}>
|
|
184
|
+
{themes.map((theme) => (
|
|
185
|
+
<DisclosurePickerOption key={theme.id} value={theme.id} media={<ThemePreview theme={theme} mode={mode} size="sm" />}>
|
|
186
|
+
{theme.label}
|
|
187
|
+
</DisclosurePickerOption>
|
|
188
|
+
))}
|
|
189
|
+
</DisclosurePickerList>
|
|
190
|
+
</DisclosurePickerPanel>
|
|
191
|
+
</DisclosurePicker>`
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
label: "Property-grid rung",
|
|
195
|
+
code: `const [blend, setBlend] = useState("normal")
|
|
196
|
+
<Density value="compact" className="w-64">
|
|
197
|
+
<DisclosurePicker size="xs">
|
|
198
|
+
<DisclosurePickerTrigger label="Blend">{blend}</DisclosurePickerTrigger>
|
|
199
|
+
<DisclosurePickerPanel>
|
|
200
|
+
<DisclosurePickerList aria-label="Blend mode" value={blend} onValueChange={setBlend}>
|
|
201
|
+
<DisclosurePickerOption value="normal">normal</DisclosurePickerOption>
|
|
202
|
+
<DisclosurePickerOption value="multiply">multiply</DisclosurePickerOption>
|
|
203
|
+
<DisclosurePickerOption value="screen">screen</DisclosurePickerOption>
|
|
204
|
+
</DisclosurePickerList>
|
|
205
|
+
</DisclosurePickerPanel>
|
|
206
|
+
</DisclosurePicker>
|
|
207
|
+
</Density>`
|
|
208
|
+
}
|
|
209
|
+
]
|
|
210
|
+
};
|
|
211
|
+
export {
|
|
212
|
+
disclosurePickerMeta
|
|
213
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/components/disclosure-picker/index.ts
|
|
2
|
+
import {
|
|
3
|
+
DisclosurePicker,
|
|
4
|
+
DisclosurePickerTrigger,
|
|
5
|
+
DisclosurePickerPanel,
|
|
6
|
+
DisclosurePickerList,
|
|
7
|
+
DisclosurePickerOption,
|
|
8
|
+
DisclosurePickerEmpty,
|
|
9
|
+
useDisclosurePicker
|
|
10
|
+
} from "./disclosure-picker.js";
|
|
11
|
+
import { disclosurePickerMeta } from "./disclosure-picker.meta.js";
|
|
12
|
+
export {
|
|
13
|
+
DisclosurePicker,
|
|
14
|
+
DisclosurePickerEmpty,
|
|
15
|
+
DisclosurePickerList,
|
|
16
|
+
DisclosurePickerOption,
|
|
17
|
+
DisclosurePickerPanel,
|
|
18
|
+
DisclosurePickerTrigger,
|
|
19
|
+
disclosurePickerMeta,
|
|
20
|
+
useDisclosurePicker
|
|
21
|
+
};
|