@webjsdev/ui 0.3.17 → 0.5.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/README.md +67 -8
- package/THIRD_PARTY_NOTICES.md +349 -0
- package/package.json +4 -2
- package/packages/registry/blocks/_shared/placeholder.svg +1 -0
- package/packages/registry/blocks/charts/chart-area-axes.ts +72 -0
- package/packages/registry/blocks/charts/chart-area-default.ts +64 -0
- package/packages/registry/blocks/charts/chart-area-gradient.ts +82 -0
- package/packages/registry/blocks/charts/chart-area-icons.ts +77 -0
- package/packages/registry/blocks/charts/chart-area-interactive.ts +204 -0
- package/packages/registry/blocks/charts/chart-area-legend.ts +72 -0
- package/packages/registry/blocks/charts/chart-area-linear.ts +64 -0
- package/packages/registry/blocks/charts/chart-area-stacked-expand.ts +77 -0
- package/packages/registry/blocks/charts/chart-area-stacked.ts +71 -0
- package/packages/registry/blocks/charts/chart-area-step.ts +68 -0
- package/packages/registry/blocks/charts/chart-bar-active.ts +93 -0
- package/packages/registry/blocks/charts/chart-bar-default.ts +60 -0
- package/packages/registry/blocks/charts/chart-bar-horizontal.ts +62 -0
- package/packages/registry/blocks/charts/chart-bar-interactive.ts +185 -0
- package/packages/registry/blocks/charts/chart-bar-label-custom.ts +78 -0
- package/packages/registry/blocks/charts/chart-bar-label.ts +66 -0
- package/packages/registry/blocks/charts/chart-bar-mixed.ts +87 -0
- package/packages/registry/blocks/charts/chart-bar-multiple.ts +67 -0
- package/packages/registry/blocks/charts/chart-bar-negative.ts +61 -0
- package/packages/registry/blocks/charts/chart-bar-stacked.ts +68 -0
- package/packages/registry/blocks/charts/chart-line-default.ts +61 -0
- package/packages/registry/blocks/charts/chart-line-dots-colors.ts +86 -0
- package/packages/registry/blocks/charts/chart-line-dots-custom.ts +75 -0
- package/packages/registry/blocks/charts/chart-line-dots.ts +65 -0
- package/packages/registry/blocks/charts/chart-line-interactive.ts +185 -0
- package/packages/registry/blocks/charts/chart-line-label-custom.ts +94 -0
- package/packages/registry/blocks/charts/chart-line-label.ts +73 -0
- package/packages/registry/blocks/charts/chart-line-linear.ts +61 -0
- package/packages/registry/blocks/charts/chart-line-multiple.ts +71 -0
- package/packages/registry/blocks/charts/chart-line-step.ts +61 -0
- package/packages/registry/blocks/charts/chart-pie-donut-active.ts +86 -0
- package/packages/registry/blocks/charts/chart-pie-donut-text.ts +89 -0
- package/packages/registry/blocks/charts/chart-pie-donut.ts +77 -0
- package/packages/registry/blocks/charts/chart-pie-interactive.ts +120 -0
- package/packages/registry/blocks/charts/chart-pie-label-custom.ts +82 -0
- package/packages/registry/blocks/charts/chart-pie-label-list.ts +86 -0
- package/packages/registry/blocks/charts/chart-pie-label.ts +77 -0
- package/packages/registry/blocks/charts/chart-pie-legend.ts +79 -0
- package/packages/registry/blocks/charts/chart-pie-separator-none.ts +77 -0
- package/packages/registry/blocks/charts/chart-pie-simple.ts +77 -0
- package/packages/registry/blocks/charts/chart-pie-stacked.ts +99 -0
- package/packages/registry/blocks/charts/chart-radar-default.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-dots.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-circle-fill.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-circle-no-lines.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-circle.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-custom.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-fill.ts +61 -0
- package/packages/registry/blocks/charts/chart-radar-grid-none.ts +60 -0
- package/packages/registry/blocks/charts/chart-radar-icons.ts +76 -0
- package/packages/registry/blocks/charts/chart-radar-label-custom.ts +77 -0
- package/packages/registry/blocks/charts/chart-radar-legend.ts +70 -0
- package/packages/registry/blocks/charts/chart-radar-lines-only.ts +68 -0
- package/packages/registry/blocks/charts/chart-radar-multiple.ts +68 -0
- package/packages/registry/blocks/charts/chart-radar-radius.ts +68 -0
- package/packages/registry/blocks/charts/chart-radial-grid.ts +80 -0
- package/packages/registry/blocks/charts/chart-radial-label.ts +85 -0
- package/packages/registry/blocks/charts/chart-radial-shape.ts +81 -0
- package/packages/registry/blocks/charts/chart-radial-simple.ts +79 -0
- package/packages/registry/blocks/charts/chart-radial-stacked.ts +69 -0
- package/packages/registry/blocks/charts/chart-radial-text.ts +82 -0
- package/packages/registry/blocks/charts/chart-tooltip-advanced.ts +84 -0
- package/packages/registry/blocks/charts/chart-tooltip-default.ts +62 -0
- package/packages/registry/blocks/charts/chart-tooltip-formatter.ts +75 -0
- package/packages/registry/blocks/charts/chart-tooltip-icons.ts +68 -0
- package/packages/registry/blocks/charts/chart-tooltip-indicator-line.ts +62 -0
- package/packages/registry/blocks/charts/chart-tooltip-indicator-none.ts +62 -0
- package/packages/registry/blocks/charts/chart-tooltip-label-custom.ts +65 -0
- package/packages/registry/blocks/charts/chart-tooltip-label-formatter.ts +66 -0
- package/packages/registry/blocks/charts/chart-tooltip-label-none.ts +62 -0
- package/packages/registry/blocks/dashboard-01/components/app-sidebar.ts +108 -0
- package/packages/registry/blocks/dashboard-01/components/chart-area-interactive.ts +276 -0
- package/packages/registry/blocks/dashboard-01/components/dashboard-data.ts +89 -0
- package/packages/registry/blocks/dashboard-01/components/data-table.ts +644 -0
- package/packages/registry/blocks/dashboard-01/components/nav-documents.ts +65 -0
- package/packages/registry/blocks/dashboard-01/components/nav-main.ts +61 -0
- package/packages/registry/blocks/dashboard-01/components/nav-secondary.ts +40 -0
- package/packages/registry/blocks/dashboard-01/components/nav-user.ts +66 -0
- package/packages/registry/blocks/dashboard-01/components/section-cards.ts +59 -0
- package/packages/registry/blocks/dashboard-01/components/site-header.ts +52 -0
- package/packages/registry/blocks/dashboard-01/page.ts +39 -0
- package/packages/registry/blocks/login-01/components/login-form.ts +70 -0
- package/packages/registry/blocks/login-01/page.ts +19 -0
- package/packages/registry/blocks/login-02/components/login-form.ts +68 -0
- package/packages/registry/blocks/login-02/page.ts +36 -0
- package/packages/registry/blocks/login-03/components/login-form.ts +89 -0
- package/packages/registry/blocks/login-03/page.ts +28 -0
- package/packages/registry/blocks/login-04/components/login-form.ts +91 -0
- package/packages/registry/blocks/login-04/page.ts +20 -0
- package/packages/registry/blocks/login-05/components/login-form.ts +75 -0
- package/packages/registry/blocks/login-05/page.ts +19 -0
- package/packages/registry/blocks/preview/cards/activate-agent-dialog.ts +77 -0
- package/packages/registry/blocks/preview/cards/analytics-card.ts +64 -0
- package/packages/registry/blocks/preview/cards/anomaly-alert.ts +35 -0
- package/packages/registry/blocks/preview/cards/bar-chart-card.ts +91 -0
- package/packages/registry/blocks/preview/cards/book-appointment.ts +51 -0
- package/packages/registry/blocks/preview/cards/codespaces-card.ts +172 -0
- package/packages/registry/blocks/preview/cards/contributions-activity.ts +63 -0
- package/packages/registry/blocks/preview/cards/contributors.ts +51 -0
- package/packages/registry/blocks/preview/cards/environment-variables.ts +47 -0
- package/packages/registry/blocks/preview/cards/feedback-form.ts +61 -0
- package/packages/registry/blocks/preview/cards/file-upload.ts +51 -0
- package/packages/registry/blocks/preview/cards/github-profile.ts +66 -0
- package/packages/registry/blocks/preview/cards/icon-preview-grid.ts +47 -0
- package/packages/registry/blocks/preview/cards/invite-team.ts +78 -0
- package/packages/registry/blocks/preview/cards/invoice.ts +91 -0
- package/packages/registry/blocks/preview/cards/live-waveform.ts +330 -0
- package/packages/registry/blocks/preview/cards/no-team-members.ts +61 -0
- package/packages/registry/blocks/preview/cards/not-found.ts +46 -0
- package/packages/registry/blocks/preview/cards/observability-card.ts +44 -0
- package/packages/registry/blocks/preview/cards/pie-chart-card.ts +84 -0
- package/packages/registry/blocks/preview/cards/report-bug.ts +71 -0
- package/packages/registry/blocks/preview/cards/shipping-address.ts +79 -0
- package/packages/registry/blocks/preview/cards/shortcuts.ts +57 -0
- package/packages/registry/blocks/preview/cards/skeleton-loading.ts +37 -0
- package/packages/registry/blocks/preview/cards/sleep-report.ts +85 -0
- package/packages/registry/blocks/preview/cards/style-overview.ts +64 -0
- package/packages/registry/blocks/preview/cards/typography-specimen.ts +107 -0
- package/packages/registry/blocks/preview/cards/ui-elements.ts +164 -0
- package/packages/registry/blocks/preview/cards/usage-card.ts +63 -0
- package/packages/registry/blocks/preview/cards/visitors.ts +71 -0
- package/packages/registry/blocks/preview/cards/weekly-fitness-summary.ts +54 -0
- package/packages/registry/blocks/preview/page.ts +103 -0
- package/packages/registry/blocks/preview-02/cards/account-access.ts +70 -0
- package/packages/registry/blocks/preview-02/cards/card-overview.ts +75 -0
- package/packages/registry/blocks/preview-02/cards/claimable-balance.ts +54 -0
- package/packages/registry/blocks/preview-02/cards/contribution-history.ts +80 -0
- package/packages/registry/blocks/preview-02/cards/cover-art.ts +41 -0
- package/packages/registry/blocks/preview-02/cards/dividend-income.ts +76 -0
- package/packages/registry/blocks/preview-02/cards/empty-connect-bank.ts +38 -0
- package/packages/registry/blocks/preview-02/cards/empty-distribute-track.ts +38 -0
- package/packages/registry/blocks/preview-02/cards/empty-explore-catalog.ts +39 -0
- package/packages/registry/blocks/preview-02/cards/faq.ts +79 -0
- package/packages/registry/blocks/preview-02/cards/front-door.ts +40 -0
- package/packages/registry/blocks/preview-02/cards/index-investing.ts +31 -0
- package/packages/registry/blocks/preview-02/cards/kitchen-island.ts +90 -0
- package/packages/registry/blocks/preview-02/cards/loading-card.ts +34 -0
- package/packages/registry/blocks/preview-02/cards/new-milestone.ts +53 -0
- package/packages/registry/blocks/preview-02/cards/notification-settings.ts +126 -0
- package/packages/registry/blocks/preview-02/cards/payments.ts +88 -0
- package/packages/registry/blocks/preview-02/cards/payout-threshold.ts +92 -0
- package/packages/registry/blocks/preview-02/cards/power-usage.ts +84 -0
- package/packages/registry/blocks/preview-02/cards/preferences.ts +89 -0
- package/packages/registry/blocks/preview-02/cards/qr-connect.ts +54 -0
- package/packages/registry/blocks/preview-02/cards/receiving-method.ts +89 -0
- package/packages/registry/blocks/preview-02/cards/recent-transactions.ts +101 -0
- package/packages/registry/blocks/preview-02/cards/release-catalog.ts +73 -0
- package/packages/registry/blocks/preview-02/cards/roller-shades.ts +110 -0
- package/packages/registry/blocks/preview-02/cards/savings-progress.ts +68 -0
- package/packages/registry/blocks/preview-02/cards/savings-targets.ts +132 -0
- package/packages/registry/blocks/preview-02/cards/sidebar-nav.ts +88 -0
- package/packages/registry/blocks/preview-02/cards/social-links.ts +57 -0
- package/packages/registry/blocks/preview-02/cards/stock-performance.ts +106 -0
- package/packages/registry/blocks/preview-02/cards/syncing-state.ts +37 -0
- package/packages/registry/blocks/preview-02/cards/transfer-funds.ts +111 -0
- package/packages/registry/blocks/preview-02/cards/upcoming-payments.ts +69 -0
- package/packages/registry/blocks/preview-02/page.ts +111 -0
- package/packages/registry/blocks/preview-03/cards/files-chat.ts +75 -0
- package/packages/registry/blocks/preview-03/cards/group-chat.ts +114 -0
- package/packages/registry/blocks/preview-03/cards/reasoning-chat.ts +88 -0
- package/packages/registry/blocks/preview-03/cards/simple-chat.ts +53 -0
- package/packages/registry/blocks/preview-03/components/chat-card.ts +126 -0
- package/packages/registry/blocks/preview-03/components/chat-player.ts +174 -0
- package/packages/registry/blocks/preview-03/components/part-file.ts +77 -0
- package/packages/registry/blocks/preview-03/components/part-reasoning.ts +25 -0
- package/packages/registry/blocks/preview-03/components/part-source.ts +48 -0
- package/packages/registry/blocks/preview-03/components/part-text.ts +69 -0
- package/packages/registry/blocks/preview-03/components/part-tool.ts +71 -0
- package/packages/registry/blocks/preview-03/components/prompt-input.ts +86 -0
- package/packages/registry/blocks/preview-03/page.ts +39 -0
- package/packages/registry/blocks/sidebar-01/components/app-sidebar.ts +113 -0
- package/packages/registry/blocks/sidebar-01/components/search-form.ts +33 -0
- package/packages/registry/blocks/sidebar-01/components/version-switcher.ts +50 -0
- package/packages/registry/blocks/sidebar-01/page.ts +58 -0
- package/packages/registry/blocks/sidebar-02/components/app-sidebar.ts +131 -0
- package/packages/registry/blocks/sidebar-02/components/search-form.ts +33 -0
- package/packages/registry/blocks/sidebar-02/components/version-switcher.ts +50 -0
- package/packages/registry/blocks/sidebar-02/page.ts +53 -0
- package/packages/registry/blocks/sidebar-03/components/app-sidebar.ts +134 -0
- package/packages/registry/blocks/sidebar-03/page.ts +60 -0
- package/packages/registry/blocks/sidebar-04/components/app-sidebar.ts +136 -0
- package/packages/registry/blocks/sidebar-04/page.ts +58 -0
- package/packages/registry/blocks/sidebar-05/components/app-sidebar.ts +147 -0
- package/packages/registry/blocks/sidebar-05/components/search-form.ts +33 -0
- package/packages/registry/blocks/sidebar-05/page.ts +58 -0
- package/packages/registry/blocks/sidebar-06/components/app-sidebar.ts +105 -0
- package/packages/registry/blocks/sidebar-06/components/nav-main.ts +56 -0
- package/packages/registry/blocks/sidebar-06/components/sidebar-opt-in-form.ts +44 -0
- package/packages/registry/blocks/sidebar-06/page.ts +57 -0
- package/packages/registry/blocks/sidebar-07/components/app-sidebar.ts +108 -0
- package/packages/registry/blocks/sidebar-07/components/nav-main.ts +61 -0
- package/packages/registry/blocks/sidebar-07/components/nav-projects.ts +65 -0
- package/packages/registry/blocks/sidebar-07/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-07/components/team-switcher.ts +66 -0
- package/packages/registry/blocks/sidebar-07/page.ts +59 -0
- package/packages/registry/blocks/sidebar-08/components/app-sidebar.ts +122 -0
- package/packages/registry/blocks/sidebar-08/components/nav-main.ts +68 -0
- package/packages/registry/blocks/sidebar-08/components/nav-projects.ts +65 -0
- package/packages/registry/blocks/sidebar-08/components/nav-secondary.ts +40 -0
- package/packages/registry/blocks/sidebar-08/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-08/page.ts +59 -0
- package/packages/registry/blocks/sidebar-09/components/app-sidebar.ts +213 -0
- package/packages/registry/blocks/sidebar-09/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-09/page.ts +52 -0
- package/packages/registry/blocks/sidebar-10/components/app-sidebar.ts +125 -0
- package/packages/registry/blocks/sidebar-10/components/nav-actions.ts +84 -0
- package/packages/registry/blocks/sidebar-10/components/nav-favorites.ts +66 -0
- package/packages/registry/blocks/sidebar-10/components/nav-main.ts +37 -0
- package/packages/registry/blocks/sidebar-10/components/nav-secondary.ts +43 -0
- package/packages/registry/blocks/sidebar-10/components/nav-workspaces.ts +78 -0
- package/packages/registry/blocks/sidebar-10/components/team-switcher.ts +63 -0
- package/packages/registry/blocks/sidebar-10/page.ts +46 -0
- package/packages/registry/blocks/sidebar-11/components/app-sidebar.ts +114 -0
- package/packages/registry/blocks/sidebar-11/page.ts +63 -0
- package/packages/registry/blocks/sidebar-12/components/app-sidebar.ts +63 -0
- package/packages/registry/blocks/sidebar-12/components/calendars.ts +53 -0
- package/packages/registry/blocks/sidebar-12/components/date-picker.ts +26 -0
- package/packages/registry/blocks/sidebar-12/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-12/page.ts +43 -0
- package/packages/registry/blocks/sidebar-13/components/settings-dialog.ts +114 -0
- package/packages/registry/blocks/sidebar-13/page.ts +21 -0
- package/packages/registry/blocks/sidebar-14/components/app-sidebar.ts +120 -0
- package/packages/registry/blocks/sidebar-14/page.ts +55 -0
- package/packages/registry/blocks/sidebar-15/components/calendars.ts +53 -0
- package/packages/registry/blocks/sidebar-15/components/date-picker.ts +26 -0
- package/packages/registry/blocks/sidebar-15/components/nav-favorites.ts +66 -0
- package/packages/registry/blocks/sidebar-15/components/nav-main.ts +35 -0
- package/packages/registry/blocks/sidebar-15/components/nav-secondary.ts +43 -0
- package/packages/registry/blocks/sidebar-15/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-15/components/nav-workspaces.ts +75 -0
- package/packages/registry/blocks/sidebar-15/components/sidebar-left.ts +126 -0
- package/packages/registry/blocks/sidebar-15/components/sidebar-right.ts +62 -0
- package/packages/registry/blocks/sidebar-15/components/team-switcher.ts +63 -0
- package/packages/registry/blocks/sidebar-15/page.ts +47 -0
- package/packages/registry/blocks/sidebar-16/components/app-sidebar.ts +124 -0
- package/packages/registry/blocks/sidebar-16/components/nav-main.ts +67 -0
- package/packages/registry/blocks/sidebar-16/components/nav-projects.ts +65 -0
- package/packages/registry/blocks/sidebar-16/components/nav-secondary.ts +40 -0
- package/packages/registry/blocks/sidebar-16/components/nav-user.ts +70 -0
- package/packages/registry/blocks/sidebar-16/components/search-form.ts +27 -0
- package/packages/registry/blocks/sidebar-16/components/site-header.ts +42 -0
- package/packages/registry/blocks/sidebar-16/page.ts +38 -0
- package/packages/registry/blocks/signup-01/components/signup-form.ts +78 -0
- package/packages/registry/blocks/signup-01/page.ts +19 -0
- package/packages/registry/blocks/signup-02/components/signup-form.ts +78 -0
- package/packages/registry/blocks/signup-02/page.ts +36 -0
- package/packages/registry/blocks/signup-03/components/signup-form.ts +79 -0
- package/packages/registry/blocks/signup-03/page.ts +28 -0
- package/packages/registry/blocks/signup-04/components/signup-form.ts +100 -0
- package/packages/registry/blocks/signup-04/page.ts +20 -0
- package/packages/registry/blocks/signup-05/components/signup-form.ts +75 -0
- package/packages/registry/blocks/signup-05/page.ts +20 -0
- package/packages/registry/components/accordion.ts +20 -7
- package/packages/registry/components/alert-dialog.ts +58 -13
- package/packages/registry/components/alert.ts +11 -6
- package/packages/registry/components/attachment.ts +119 -0
- package/packages/registry/components/avatar.ts +17 -8
- package/packages/registry/components/badge.ts +16 -9
- package/packages/registry/components/breadcrumb.ts +6 -6
- package/packages/registry/components/bubble.ts +95 -0
- package/packages/registry/components/button-group.ts +73 -0
- package/packages/registry/components/button.ts +35 -25
- package/packages/registry/components/calendar.ts +566 -0
- package/packages/registry/components/card.ts +8 -8
- package/packages/registry/components/carousel.ts +352 -0
- package/packages/registry/components/chart-element.ts +547 -0
- package/packages/registry/components/chart-geometry.ts +457 -0
- package/packages/registry/components/chart.ts +932 -0
- package/packages/registry/components/checkbox.ts +22 -84
- package/packages/registry/components/code-block.ts +191 -0
- package/packages/registry/components/collapsible.ts +5 -2
- package/packages/registry/components/combobox.ts +802 -0
- package/packages/registry/components/command.ts +618 -0
- package/packages/registry/components/context-menu.ts +302 -0
- package/packages/registry/components/dialog.ts +156 -45
- package/packages/registry/components/direction.ts +43 -0
- package/packages/registry/components/drawer.ts +226 -0
- package/packages/registry/components/dropdown-menu.ts +262 -83
- package/packages/registry/components/empty.ts +8 -8
- package/packages/registry/components/field.ts +20 -18
- package/packages/registry/components/floating.ts +124 -0
- package/packages/registry/components/hover-card.ts +19 -8
- package/packages/registry/components/input-group.ts +118 -0
- package/packages/registry/components/input-otp.ts +365 -0
- package/packages/registry/components/input.ts +3 -3
- package/packages/registry/components/item.ts +134 -0
- package/packages/registry/components/kbd.ts +2 -2
- package/packages/registry/components/label.ts +1 -1
- package/packages/registry/components/marker.ts +63 -0
- package/packages/registry/components/menubar.ts +388 -0
- package/packages/registry/components/message-scroller.ts +470 -0
- package/packages/registry/components/message.ts +69 -0
- package/packages/registry/components/native-select.ts +5 -5
- package/packages/registry/components/navigation-menu.ts +564 -0
- package/packages/registry/components/pagination.ts +5 -5
- package/packages/registry/components/popover.ts +30 -84
- package/packages/registry/components/progress.ts +7 -3
- package/packages/registry/components/questionnaire.ts +840 -0
- package/packages/registry/components/radio-group.ts +18 -73
- package/packages/registry/components/resizable.ts +330 -0
- package/packages/registry/components/scroll-area.ts +46 -0
- package/packages/registry/components/select.ts +650 -0
- package/packages/registry/components/separator.ts +1 -1
- package/packages/registry/components/sheet.ts +128 -0
- package/packages/registry/components/sidebar.ts +457 -0
- package/packages/registry/components/skeleton.ts +1 -1
- package/packages/registry/components/slider.ts +333 -0
- package/packages/registry/components/sonner.ts +74 -7
- package/packages/registry/components/spinner.ts +7 -4
- package/packages/registry/components/switch.ts +117 -9
- package/packages/registry/components/table.ts +11 -9
- package/packages/registry/components/tabs.ts +76 -25
- package/packages/registry/components/textarea.ts +1 -1
- package/packages/registry/components/toggle-group.ts +30 -11
- package/packages/registry/components/toggle.ts +20 -13
- package/packages/registry/components/tooltip.ts +68 -7
- package/packages/registry/components/trace.ts +75 -0
- package/packages/registry/lib/dom.ts +3 -4
- package/packages/registry/lib/icons-hugeicons.ts +955 -0
- package/packages/registry/lib/icons-phosphor.ts +955 -0
- package/packages/registry/lib/icons-remixicon.ts +955 -0
- package/packages/registry/lib/icons-tabler.ts +955 -0
- package/packages/registry/lib/icons.ts +768 -0
- package/packages/registry/lib/utils.ts +52 -8
- package/packages/registry/registry.json +4056 -72
- package/packages/registry/styles/shadcn-tailwind.css +654 -0
- package/packages/registry/styles/style-luma.css +1987 -0
- package/packages/registry/styles/style-lyra.css +1965 -0
- package/packages/registry/styles/style-maia.css +1987 -0
- package/packages/registry/styles/style-mira.css +1992 -0
- package/packages/registry/styles/style-new-york.css +1597 -0
- package/packages/registry/styles/style-nova.css +1990 -0
- package/packages/registry/styles/style-rhea.css +1987 -0
- package/packages/registry/styles/style-sera.css +1977 -0
- package/packages/registry/styles/style-vega.css +1990 -0
- package/packages/registry/styles/supplement.json +54 -0
- package/packages/registry/styles/tw-animate.css +6 -0
- package/packages/registry/themes/base-colors.js +8 -2
- package/packages/registry/themes/fonts.js +120 -0
- package/packages/registry/themes/index.css +2 -0
- package/packages/registry/themes/items.js +61 -0
- package/packages/registry/themes/preset.js +302 -0
- package/packages/registry/themes/themes.js +1193 -0
- package/src/commands/add.js +266 -14
- package/src/commands/diff.js +34 -20
- package/src/commands/info.js +7 -0
- package/src/commands/init.js +127 -10
- package/src/commands/list.js +43 -11
- package/src/commands/view.js +33 -5
- package/src/lint/rules/no-restyle.js +1 -1
- package/src/registry/extract.js +194 -5
- package/src/registry/icons.js +31 -0
- package/src/registry/local.js +15 -1
- package/src/registry/schema.js +13 -0
- package/src/registry/style-bake.js +506 -0
- package/src/utils/theme.js +110 -21
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Select: a styled single-choice picker that ENHANCES a native `<select>`.
|
|
3
|
+
* Tier-2. You author an ordinary `<select>` with its options inside
|
|
4
|
+
* `<ui-select>`. Without JavaScript that select is what the page shows and
|
|
5
|
+
* what the form submits. With JavaScript the element hides it (visually only:
|
|
6
|
+
* it still submits, validates and resets with its form) and draws shadcn's
|
|
7
|
+
* trigger and listbox in its place, writing every choice back into it.
|
|
8
|
+
*
|
|
9
|
+
* shadcn parity:
|
|
10
|
+
* Select → <ui-select placeholder size position align content-class>
|
|
11
|
+
* SelectTrigger (size) → drawn by the element (`size="sm" | "default"`)
|
|
12
|
+
* SelectTrigger children → a <template data-select-trigger> (see below)
|
|
13
|
+
* SelectValue placeholder → the `placeholder` attribute, or a placeholder <option>
|
|
14
|
+
* SelectContent → drawn by the element (`position="item-aligned"`, the
|
|
15
|
+
* default, opens over the trigger on the chosen option;
|
|
16
|
+
* `"popper"` opens below it, at `align="start"` (the
|
|
17
|
+
* default), `"center"` or `"end"`)
|
|
18
|
+
* SelectContent className → the `content-class` attribute
|
|
19
|
+
* SelectGroup + SelectLabel→ <optgroup label="...">
|
|
20
|
+
* SelectItem → <option value="...">
|
|
21
|
+
* SelectItem children → a <template> inside the <option> (see below)
|
|
22
|
+
* SelectItem className → the <option>'s class
|
|
23
|
+
* SelectSeparator → <hr> between options
|
|
24
|
+
* SelectScrollUp/DownButton→ drawn when the list overflows
|
|
25
|
+
* onValueChange → `ui-value-change` ({ detail: { value } }); the
|
|
26
|
+
* native select also fires `input` and `change`
|
|
27
|
+
*
|
|
28
|
+
* The options stay native elements because the HTML parser drops a custom
|
|
29
|
+
* element inside a `<select>`. A placeholder option is one with `value=""`
|
|
30
|
+
* that is `hidden` or `disabled`: it is not listed, and its text is the
|
|
31
|
+
* placeholder unless the `placeholder` attribute names one.
|
|
32
|
+
*
|
|
33
|
+
* An item shows its option's label (the `label` attribute, else its text),
|
|
34
|
+
* and so does the trigger once it is chosen. For an item with more in it,
|
|
35
|
+
* shadcn's SelectItem children such as a name over a muted value, put a
|
|
36
|
+
* `<template>` inside the `<option>`: the list renders the template's content
|
|
37
|
+
* in that item, and the trigger and the native select keep the label. The
|
|
38
|
+
* parser keeps a `<template>` inside an `<option>`, and it adds no text to the
|
|
39
|
+
* option, so the no-JavaScript select shows the label alone. A class on the
|
|
40
|
+
* `<option>` lands on its item, as SelectItem's `className` does.
|
|
41
|
+
*
|
|
42
|
+
* For a trigger with more than the value in it, shadcn's SelectTrigger
|
|
43
|
+
* children ("Format: " beside the chosen format), put a
|
|
44
|
+
* `<template data-select-trigger>` inside `<ui-select>`, beside the
|
|
45
|
+
* select. The trigger renders its content, and fills an element in it marked
|
|
46
|
+
* `data-slot="select-value"` with the chosen option's label (or the
|
|
47
|
+
* placeholder). That content is `aria-hidden`: the trigger still carries the
|
|
48
|
+
* label alone as its text for assistive technology, so the select is
|
|
49
|
+
* announced by its label and its value, as before. With JavaScript off the
|
|
50
|
+
* template is inert and the native select shows as it always did.
|
|
51
|
+
*
|
|
52
|
+
* Keyboard (as Radix's): on the trigger, ArrowDown / ArrowUp, Enter or Space
|
|
53
|
+
* open the list on the selected option, and typing selects the first option
|
|
54
|
+
* starting with what was typed, as a native select does. In the open list,
|
|
55
|
+
* ArrowDown / ArrowUp / Home / End move, typing jumps, Enter or Space choose,
|
|
56
|
+
* Escape closes without a change, and Tab chooses the focused option and
|
|
57
|
+
* moves on (the WAI-ARIA select-only combobox pattern). Every close returns
|
|
58
|
+
* focus to the trigger.
|
|
59
|
+
*
|
|
60
|
+
* A11y (handled by the element, plus your part): the trigger is a
|
|
61
|
+
* `role="combobox"` button with `aria-haspopup="listbox"`, `aria-expanded` and
|
|
62
|
+
* `aria-controls`, named by the select's `<label>`s (a label click focuses the
|
|
63
|
+
* trigger) or its `aria-label`, and it carries the select's
|
|
64
|
+
* `aria-describedby` and `aria-invalid`. The list is a `role="listbox"` of
|
|
65
|
+
* `role="option"` items with `aria-selected`, an `<optgroup>` is a named
|
|
66
|
+
* `role="group"`, and a disabled option is skipped. When a `required` select
|
|
67
|
+
* fails validation the trigger takes `aria-invalid="true"` and focus. Your
|
|
68
|
+
* part: a real `<label for>` on the select, and the field's error text wired
|
|
69
|
+
* through `aria-describedby` on the select as for any control.
|
|
70
|
+
*
|
|
71
|
+
* The native select stands in for the trigger, so the classes written on it
|
|
72
|
+
* are the trigger's classes (shadcn's `className` on SelectTrigger): the drawn
|
|
73
|
+
* trigger takes them, and falls back to `selectTriggerClass()` when the select
|
|
74
|
+
* carries none. `class=${cn(selectTriggerClass(), 'w-full')}` on the select
|
|
75
|
+
* gives a full-width trigger with JavaScript on and off alike.
|
|
76
|
+
*
|
|
77
|
+
* Style the native select for the no-JavaScript paint: `selectTriggerClass()`
|
|
78
|
+
* gives it the trigger's look with the browser's own arrow.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```html
|
|
82
|
+
* <label for="fruit" class=${labelClass()}>Fruit</label>
|
|
83
|
+
* <ui-select placeholder="Select a fruit">
|
|
84
|
+
* <select id="fruit" name="fruit" class=${selectTriggerClass()}>
|
|
85
|
+
* <option value="" disabled selected hidden>Select a fruit</option>
|
|
86
|
+
* <optgroup label="Fruits">
|
|
87
|
+
* <option value="apple">Apple</option>
|
|
88
|
+
* <option value="banana">Banana</option>
|
|
89
|
+
* <option value="grapes" disabled>Grapes</option>
|
|
90
|
+
* </optgroup>
|
|
91
|
+
* <hr>
|
|
92
|
+
* <option value="pineapple">Pineapple</option>
|
|
93
|
+
* </select>
|
|
94
|
+
* </ui-select>
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
import { WebComponent, html, prop, signal, nothing } from '@webjsdev/core';
|
|
98
|
+
import { templateContent, unsafeHTML } from '@webjsdev/core/directives';
|
|
99
|
+
import { cn, ensureId } from '../lib/utils.ts';
|
|
100
|
+
import { positionFloating } from './floating.ts';
|
|
101
|
+
|
|
102
|
+
/** The trigger's classes: the element's drawn trigger, and the native select's no-JavaScript look. */
|
|
103
|
+
export const selectTriggerClass = (): string =>
|
|
104
|
+
'cn-select-trigger flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0';
|
|
105
|
+
/** The trigger's chevron. */
|
|
106
|
+
export const selectTriggerIconClass = (): string =>
|
|
107
|
+
'cn-select-trigger-icon pointer-events-none';
|
|
108
|
+
/** The listbox panel's classes (the element applies them). */
|
|
109
|
+
export const selectContentClass = (): string =>
|
|
110
|
+
'cn-select-content z-50 max-h-(--radix-select-content-available-height) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none not-open:hidden';
|
|
111
|
+
/** The listbox's inner viewport (the element applies them). */
|
|
112
|
+
export const selectViewportClass = (): string =>
|
|
113
|
+
'cn-select-viewport data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)';
|
|
114
|
+
/** A group's classes (the element applies them). */
|
|
115
|
+
export const selectGroupClass = (): string => 'cn-select-group';
|
|
116
|
+
/** A group label's classes (the element applies them). */
|
|
117
|
+
export const selectLabelClass = (): string => 'cn-select-label';
|
|
118
|
+
/** An option's classes (the element applies them). */
|
|
119
|
+
export const selectItemClass = (): string =>
|
|
120
|
+
'cn-select-item relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0';
|
|
121
|
+
/** The selected option's check mark slot. */
|
|
122
|
+
export const selectItemIndicatorClass = (): string => 'cn-select-item-indicator';
|
|
123
|
+
/** The selected option's check mark. */
|
|
124
|
+
export const selectItemIndicatorIconClass = (): string =>
|
|
125
|
+
'cn-select-item-indicator-icon pointer-events-none';
|
|
126
|
+
/** A separator's classes (the element applies them). */
|
|
127
|
+
export const selectSeparatorClass = (): string =>
|
|
128
|
+
'cn-select-separator pointer-events-none';
|
|
129
|
+
/** The scroll-up affordance, shown when the list overflows above. */
|
|
130
|
+
export const selectScrollUpButtonClass = (): string =>
|
|
131
|
+
'cn-select-scroll-up-button sticky top-0 z-10 bg-inherit';
|
|
132
|
+
/** The scroll-down affordance, shown when the list overflows below. */
|
|
133
|
+
export const selectScrollDownButtonClass = (): string =>
|
|
134
|
+
'cn-select-scroll-down-button sticky bottom-0 z-10 bg-inherit';
|
|
135
|
+
|
|
136
|
+
type Entry =
|
|
137
|
+
| { kind: 'option'; index: number; value: string; label: string; disabled: boolean; content: HTMLTemplateElement | null; cls: string }
|
|
138
|
+
| { kind: 'group'; label: string; options: Entry[] }
|
|
139
|
+
| { kind: 'separator' };
|
|
140
|
+
|
|
141
|
+
// The visual-only hide Radix uses for its form control: still focusable for a
|
|
142
|
+
// validation bubble, still submitted and reset with the form.
|
|
143
|
+
const HIDDEN_CONTROL =
|
|
144
|
+
'position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;opacity:0;pointer-events:none;overflow:hidden;';
|
|
145
|
+
|
|
146
|
+
let uid = 0;
|
|
147
|
+
|
|
148
|
+
export class UiSelect extends WebComponent({
|
|
149
|
+
placeholder: String,
|
|
150
|
+
size: prop<'default' | 'sm'>(String),
|
|
151
|
+
position: prop<'popper' | 'item-aligned'>(String),
|
|
152
|
+
// Where a `position="popper"` list sits along the trigger (Radix's align).
|
|
153
|
+
align: prop<'start' | 'center' | 'end'>(String),
|
|
154
|
+
// Classes for the list panel, shadcn's SelectContent `className`.
|
|
155
|
+
contentClass: String,
|
|
156
|
+
open: prop(Boolean, { reflect: true }),
|
|
157
|
+
}) {
|
|
158
|
+
// Set once the element has taken over from the native select.
|
|
159
|
+
readonly #enhanced = signal(false);
|
|
160
|
+
// A failed constraint check, until the value is valid again.
|
|
161
|
+
readonly #invalid = signal(false);
|
|
162
|
+
_id = '';
|
|
163
|
+
_observer: MutationObserver | null = null;
|
|
164
|
+
_waiting: MutationObserver | null = null;
|
|
165
|
+
_form: HTMLFormElement | null = null;
|
|
166
|
+
_savedStyle: string | null = null;
|
|
167
|
+
_focusLast = false;
|
|
168
|
+
_typeBuffer = '';
|
|
169
|
+
_typeTimer = 0;
|
|
170
|
+
_scrollTimer = 0;
|
|
171
|
+
|
|
172
|
+
constructor() {
|
|
173
|
+
super();
|
|
174
|
+
this.placeholder = '';
|
|
175
|
+
this.size = 'default';
|
|
176
|
+
this.position = 'item-aligned';
|
|
177
|
+
this.open = false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** The native select this element enhances. */
|
|
181
|
+
get select(): HTMLSelectElement | null {
|
|
182
|
+
return this.querySelector('select');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** The chosen value, as the native select holds it. */
|
|
186
|
+
get value(): string {
|
|
187
|
+
return this.select?.value ?? '';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
set value(v: string) {
|
|
191
|
+
const select = this.select;
|
|
192
|
+
if (!select) return;
|
|
193
|
+
select.value = v;
|
|
194
|
+
this.requestUpdate();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
connectedCallback(): void {
|
|
198
|
+
super.connectedCallback?.();
|
|
199
|
+
this._id ||= `ui-select-${++uid}`;
|
|
200
|
+
// A client render can connect the element before appending the authored
|
|
201
|
+
// select, so wait for it to arrive.
|
|
202
|
+
queueMicrotask(() => {
|
|
203
|
+
if (this._enhance() || this._waiting) return;
|
|
204
|
+
this._waiting = new MutationObserver(() => {
|
|
205
|
+
if (this._enhance()) { this._waiting?.disconnect(); this._waiting = null; }
|
|
206
|
+
});
|
|
207
|
+
this._waiting.observe(this, { childList: true, subtree: true });
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
disconnectedCallback(): void {
|
|
212
|
+
this._waiting?.disconnect();
|
|
213
|
+
this._waiting = null;
|
|
214
|
+
this._observer?.disconnect();
|
|
215
|
+
this._observer = null;
|
|
216
|
+
const select = this.select;
|
|
217
|
+
if (select) {
|
|
218
|
+
select.removeEventListener('change', this._onNativeChange);
|
|
219
|
+
select.removeEventListener('focus', this._onNativeFocus);
|
|
220
|
+
select.removeEventListener('invalid', this._onInvalid);
|
|
221
|
+
if (this._savedStyle !== null) select.setAttribute('style', this._savedStyle);
|
|
222
|
+
else select.removeAttribute('style');
|
|
223
|
+
select.removeAttribute('aria-hidden');
|
|
224
|
+
select.removeAttribute('tabindex');
|
|
225
|
+
}
|
|
226
|
+
this._form?.removeEventListener('reset', this._onReset);
|
|
227
|
+
document.removeEventListener('pointerdown', this._onDocPointerDown, true);
|
|
228
|
+
clearInterval(this._scrollTimer);
|
|
229
|
+
super.disconnectedCallback?.();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Take over from the native select. True once it has (or already had). */
|
|
233
|
+
_enhance(): boolean {
|
|
234
|
+
if (this.#enhanced.get()) return true;
|
|
235
|
+
const select = this.select;
|
|
236
|
+
if (!select) return false;
|
|
237
|
+
this._savedStyle = select.getAttribute('style');
|
|
238
|
+
select.setAttribute('style', (this._savedStyle ?? '') + HIDDEN_CONTROL);
|
|
239
|
+
select.setAttribute('aria-hidden', 'true');
|
|
240
|
+
select.tabIndex = -1;
|
|
241
|
+
select.addEventListener('change', this._onNativeChange);
|
|
242
|
+
select.addEventListener('focus', this._onNativeFocus);
|
|
243
|
+
select.addEventListener('invalid', this._onInvalid);
|
|
244
|
+
this._form = select.form;
|
|
245
|
+
this._form?.addEventListener('reset', this._onReset);
|
|
246
|
+
this._observer = new MutationObserver(() => { this.requestUpdate(); });
|
|
247
|
+
this._observer.observe(select, { childList: true, subtree: true, attributes: true, characterData: true });
|
|
248
|
+
this.#enhanced.set(true);
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// The select changed under the element: an option edit, a form reset, or a
|
|
253
|
+
// script setting its value.
|
|
254
|
+
_onNativeChange = (): void => {
|
|
255
|
+
if (this.#invalid.get() && this.select?.checkValidity()) this.#invalid.set(false);
|
|
256
|
+
this.requestUpdate();
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// A reset restores the value AFTER its event, so read it a tick later.
|
|
260
|
+
_onReset = (): void => {
|
|
261
|
+
queueMicrotask(() => { this.#invalid.set(false); this.requestUpdate(); });
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// The hidden select takes focus from its label and from a failed submit;
|
|
265
|
+
// either way the user means the trigger.
|
|
266
|
+
_onNativeFocus = (): void => {
|
|
267
|
+
this._trigger()?.focus();
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
_onInvalid = (): void => {
|
|
271
|
+
this.#invalid.set(true);
|
|
272
|
+
this._trigger()?.focus();
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
_trigger(): HTMLButtonElement | null {
|
|
276
|
+
return this.querySelector('[data-slot="select-trigger"]');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
_panel(): HTMLElement | null {
|
|
280
|
+
return this.querySelector('[data-slot="select-content"]');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
_optionEls(): HTMLElement[] {
|
|
284
|
+
return Array.from(this.querySelectorAll<HTMLElement>('[data-slot="select-item"]'));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
_enabledOptionEls(): HTMLElement[] {
|
|
288
|
+
return this._optionEls().filter((o) => !o.hasAttribute('data-disabled'));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
static _isPlaceholder(o: HTMLOptionElement): boolean {
|
|
292
|
+
return o.value === '' && (!!o.hidden || o.disabled);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
_entries(): Entry[] {
|
|
296
|
+
const select = this.select;
|
|
297
|
+
if (!select) return [];
|
|
298
|
+
const options = Array.from(select.options);
|
|
299
|
+
const toOption = (o: HTMLOptionElement): Entry | null =>
|
|
300
|
+
UiSelect._isPlaceholder(o)
|
|
301
|
+
? null
|
|
302
|
+
: {
|
|
303
|
+
kind: 'option',
|
|
304
|
+
index: options.indexOf(o),
|
|
305
|
+
value: o.value,
|
|
306
|
+
label: o.label || o.text,
|
|
307
|
+
disabled: o.disabled || !!(o.parentElement as HTMLOptGroupElement | null)?.disabled,
|
|
308
|
+
content: Array.from(o.children).find((c): c is HTMLTemplateElement => c instanceof HTMLTemplateElement) ?? null,
|
|
309
|
+
cls: o.getAttribute('class') ?? '',
|
|
310
|
+
};
|
|
311
|
+
const out: Entry[] = [];
|
|
312
|
+
for (const child of Array.from(select.children)) {
|
|
313
|
+
if (child instanceof HTMLOptionElement) {
|
|
314
|
+
const e = toOption(child);
|
|
315
|
+
if (e) out.push(e);
|
|
316
|
+
} else if (child instanceof HTMLOptGroupElement) {
|
|
317
|
+
const opts = Array.from(child.children)
|
|
318
|
+
.filter((c): c is HTMLOptionElement => c instanceof HTMLOptionElement)
|
|
319
|
+
.map(toOption)
|
|
320
|
+
.filter((e): e is Entry => !!e);
|
|
321
|
+
out.push({ kind: 'group', label: child.label, options: opts });
|
|
322
|
+
} else if (child.tagName === 'HR') {
|
|
323
|
+
out.push({ kind: 'separator' });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return out;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
_placeholderText(): string {
|
|
330
|
+
if (this.placeholder) return this.placeholder;
|
|
331
|
+
const select = this.select;
|
|
332
|
+
const p = select ? Array.from(select.options).find((o) => UiSelect._isPlaceholder(o)) : undefined;
|
|
333
|
+
return p?.text ?? '';
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// The trigger, as shadcn's SelectTrigger, is the box that sits in the
|
|
337
|
+
// layout (a flex item a `w-full max-w-48` sizes against the row, a field's
|
|
338
|
+
// column), so neither the host nor the wrapper draws a box of its own. The
|
|
339
|
+
// host's is turned off through its style, which the server serves, unlike
|
|
340
|
+
// a class the element changes.
|
|
341
|
+
render() {
|
|
342
|
+
if (typeof this.getAttribute === 'function' && !this.getAttribute('style')) this.setAttribute('style', 'display: contents');
|
|
343
|
+
if (!this.#enhanced.get()) {
|
|
344
|
+
return html`<div data-slot="select" class="contents"><slot></slot></div>`;
|
|
345
|
+
}
|
|
346
|
+
const select = this.select;
|
|
347
|
+
const current = select && select.selectedIndex >= 0 ? select.options[select.selectedIndex] : null;
|
|
348
|
+
const shown = current && !UiSelect._isPlaceholder(current) ? current : null;
|
|
349
|
+
const labels = select?.labels ? Array.from(select.labels).map((l) => ensureId(l, 'ui-select-label')) : [];
|
|
350
|
+
const invalid = this.#invalid.get() || select?.getAttribute('aria-invalid') === 'true';
|
|
351
|
+
const listId = `${this._id}-listbox`;
|
|
352
|
+
const renderEntry = (e: Entry): unknown => {
|
|
353
|
+
if (e.kind === 'separator') {
|
|
354
|
+
return html`<div data-slot="select-separator" aria-hidden="true" class=${selectSeparatorClass()}></div>`;
|
|
355
|
+
}
|
|
356
|
+
if (e.kind === 'group') {
|
|
357
|
+
// An <optgroup> with no label is shadcn's SelectGroup with no
|
|
358
|
+
// SelectLabel: the group's spacing around its items, and no heading.
|
|
359
|
+
if (!e.label) {
|
|
360
|
+
return html`<div data-slot="select-group" role="group" class=${selectGroupClass()}>${e.options.map(renderEntry)}</div>`;
|
|
361
|
+
}
|
|
362
|
+
const labelId = `${this._id}-group-${e.label.replace(/\W+/g, '-')}`;
|
|
363
|
+
return html`<div data-slot="select-group" role="group" aria-labelledby=${labelId} class=${selectGroupClass()}><div
|
|
364
|
+
data-slot="select-label" id=${labelId} class=${selectLabelClass()}>${e.label}</div>${e.options.map(renderEntry)}</div>`;
|
|
365
|
+
}
|
|
366
|
+
const selected = !!shown && shown.index === e.index;
|
|
367
|
+
return html`<div
|
|
368
|
+
data-slot="select-item"
|
|
369
|
+
role="option"
|
|
370
|
+
id=${`${this._id}-option-${e.index}`}
|
|
371
|
+
tabindex="-1"
|
|
372
|
+
data-value=${e.value}
|
|
373
|
+
aria-selected=${selected ? 'true' : 'false'}
|
|
374
|
+
data-state=${selected ? 'checked' : 'unchecked'}
|
|
375
|
+
aria-disabled=${e.disabled ? 'true' : nothing}
|
|
376
|
+
?data-disabled=${e.disabled}
|
|
377
|
+
class=${e.cls ? cn(selectItemClass(), e.cls) : selectItemClass()}
|
|
378
|
+
@click=${() => this._choose(e.index)}
|
|
379
|
+
@pointermove=${this._onOptionPointerMove}
|
|
380
|
+
><span data-slot="select-item-indicator" class=${selectItemIndicatorClass()}>${selected
|
|
381
|
+
? html`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class=${selectItemIndicatorIconClass()}><path d="M20 6 9 17l-5-5"></path></svg>`
|
|
382
|
+
: ''}</span><span>${e.content ? templateContent(e.content) : e.label}</span></div>`;
|
|
383
|
+
};
|
|
384
|
+
return html`<div data-slot="select" class="contents"><slot></slot><button
|
|
385
|
+
type="button"
|
|
386
|
+
data-slot="select-trigger"
|
|
387
|
+
role="combobox"
|
|
388
|
+
data-size=${this.size || 'default'}
|
|
389
|
+
?data-placeholder=${!shown}
|
|
390
|
+
aria-haspopup="listbox"
|
|
391
|
+
aria-expanded=${this.open ? 'true' : 'false'}
|
|
392
|
+
aria-controls=${listId}
|
|
393
|
+
aria-labelledby=${labels.length ? labels.join(' ') : nothing}
|
|
394
|
+
aria-label=${!labels.length && select?.getAttribute('aria-label') ? select.getAttribute('aria-label') : nothing}
|
|
395
|
+
aria-describedby=${select?.getAttribute('aria-describedby') || nothing}
|
|
396
|
+
aria-invalid=${invalid ? 'true' : nothing}
|
|
397
|
+
aria-required=${select?.required ? 'true' : nothing}
|
|
398
|
+
?disabled=${!!select?.disabled}
|
|
399
|
+
class=${select?.getAttribute('class') || selectTriggerClass()}
|
|
400
|
+
@click=${this._onTriggerClick}
|
|
401
|
+
@keydown=${this._onTriggerKeyDown}
|
|
402
|
+
>${this._triggerBody(shown ? shown.label || shown.text : this._placeholderText())}<svg
|
|
403
|
+
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"
|
|
404
|
+
class=${selectTriggerIconClass()}
|
|
405
|
+
><path d="m6 9 6 6 6-6"></path></svg></button><div
|
|
406
|
+
data-slot="select-content"
|
|
407
|
+
id=${listId}
|
|
408
|
+
role="listbox"
|
|
409
|
+
popover="manual"
|
|
410
|
+
aria-labelledby=${labels.length ? labels.join(' ') : nothing}
|
|
411
|
+
data-state=${this.open ? 'open' : 'closed'}
|
|
412
|
+
data-position=${this.position || 'item-aligned'}
|
|
413
|
+
data-align=${this.position === 'popper' ? this.align || 'start' : nothing}
|
|
414
|
+
class=${this.contentClass ? cn(selectContentClass(), this.contentClass) : selectContentClass()}
|
|
415
|
+
@keydown=${this._onListKeyDown}
|
|
416
|
+
@pointerdown=${this._onListPointerDown}
|
|
417
|
+
@scroll=${this._syncScrollButtons}
|
|
418
|
+
><div data-slot="select-scroll-up-button" aria-hidden="true" hidden class=${selectScrollUpButtonClass()}
|
|
419
|
+
@pointerenter=${() => this._autoScroll(-1)} @pointerleave=${this._stopScroll}
|
|
420
|
+
><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"></path></svg></div><div
|
|
421
|
+
data-slot="select-viewport" data-position=${this.position || 'item-aligned'} class=${selectViewportClass()}
|
|
422
|
+
>${this._entries().map(renderEntry)}</div><div data-slot="select-scroll-down-button" aria-hidden="true" hidden class=${selectScrollDownButtonClass()}
|
|
423
|
+
@pointerenter=${() => this._autoScroll(1)} @pointerleave=${this._stopScroll}
|
|
424
|
+
><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"></path></svg></div></div></div>`;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** The author's trigger content, if any: `<template data-select-trigger>`. */
|
|
428
|
+
_triggerTemplate(): HTMLTemplateElement | null {
|
|
429
|
+
return this.querySelector<HTMLTemplateElement>('template[data-select-trigger]');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// The trigger's content: the value alone, or the author's template with the
|
|
433
|
+
// value written into its `data-slot="select-value"` element, hidden from
|
|
434
|
+
// assistive technology beside the value as plain text, so the trigger still
|
|
435
|
+
// reads as its label. The template is the author's own markup from the page
|
|
436
|
+
// and the value goes in as text, so the serialized copy is safe to render.
|
|
437
|
+
_triggerBody(text: string): unknown {
|
|
438
|
+
const tpl = this._triggerTemplate();
|
|
439
|
+
if (!tpl) return html`<span data-slot="select-value">${text}</span>`;
|
|
440
|
+
const box = document.createElement('div');
|
|
441
|
+
box.append(tpl.content.cloneNode(true));
|
|
442
|
+
const slot = box.querySelector('[data-slot="select-value"]');
|
|
443
|
+
if (slot) slot.textContent = text;
|
|
444
|
+
return html`<span data-slot="select-trigger-content" aria-hidden="true" class="contents">${unsafeHTML(box.innerHTML)}</span><span class="sr-only">${text}</span>`;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
updated(changedProperties: Map<string, unknown>): void {
|
|
448
|
+
super.updated?.(changedProperties);
|
|
449
|
+
if (!changedProperties.has('open') || changedProperties.get('open') === undefined) return;
|
|
450
|
+
queueMicrotask(() => (this.open ? this._afterOpen() : this._afterClose()));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/** Open the list. */
|
|
454
|
+
show(): void {
|
|
455
|
+
if (this.select?.disabled) return;
|
|
456
|
+
this.open = true;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** Close the list, returning focus to the trigger if it was in the list. */
|
|
460
|
+
hide(): void {
|
|
461
|
+
const panel = this._panel();
|
|
462
|
+
const active = document.activeElement;
|
|
463
|
+
// Focus leaves the panel BEFORE it hides, or the engine drops it to <body>.
|
|
464
|
+
if (panel && active && panel.contains(active)) this._trigger()?.focus();
|
|
465
|
+
this.open = false;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
_afterOpen(): void {
|
|
469
|
+
const panel = this._panel() as (HTMLElement & { showPopover?: () => void }) | null;
|
|
470
|
+
const trigger = this._trigger();
|
|
471
|
+
if (!panel || !trigger) return;
|
|
472
|
+
if (typeof panel.showPopover === 'function' && !panel.matches(':popover-open')) panel.showPopover();
|
|
473
|
+
this._reposition();
|
|
474
|
+
document.addEventListener('pointerdown', this._onDocPointerDown, true);
|
|
475
|
+
window.addEventListener('resize', this._reposition);
|
|
476
|
+
const enabled = this._enabledOptionEls();
|
|
477
|
+
const selected = enabled.find((o) => o.getAttribute('aria-selected') === 'true');
|
|
478
|
+
const target = selected ?? (this._focusLast ? enabled[enabled.length - 1] : enabled[0]);
|
|
479
|
+
this._focusLast = false;
|
|
480
|
+
target?.focus({ preventScroll: true });
|
|
481
|
+
target?.scrollIntoView({ block: 'nearest' });
|
|
482
|
+
this._syncScrollButtons();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
_afterClose(): void {
|
|
486
|
+
const panel = this._panel() as (HTMLElement & { hidePopover?: () => void }) | null;
|
|
487
|
+
if (panel && typeof panel.hidePopover === 'function' && panel.matches(':popover-open')) panel.hidePopover();
|
|
488
|
+
document.removeEventListener('pointerdown', this._onDocPointerDown, true);
|
|
489
|
+
window.removeEventListener('resize', this._reposition);
|
|
490
|
+
this._stopScroll();
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
_reposition = (): void => {
|
|
494
|
+
const panel = this._panel();
|
|
495
|
+
const trigger = this._trigger();
|
|
496
|
+
if (!panel || !trigger) return;
|
|
497
|
+
const t = trigger.getBoundingClientRect();
|
|
498
|
+
panel.style.setProperty('--radix-select-trigger-width', `${t.width}px`);
|
|
499
|
+
panel.style.setProperty('--radix-select-trigger-height', `${t.height}px`);
|
|
500
|
+
const room = Math.max(window.innerHeight - t.bottom, t.top) - 12;
|
|
501
|
+
panel.style.setProperty('--radix-select-content-available-height', `${Math.max(room, 120)}px`);
|
|
502
|
+
panel.style.minWidth = `${t.width}px`;
|
|
503
|
+
if (this.position === 'popper') {
|
|
504
|
+
positionFloating(trigger, panel, { side: 'bottom', align: this.align || 'start', sideOffset: 4 });
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
// item-aligned: open OVER the trigger, the chosen option's text lying on
|
|
508
|
+
// the trigger's value text, as Radix does, kept inside the viewport.
|
|
509
|
+
const option =
|
|
510
|
+
this._optionEls().find((o) => o.getAttribute('aria-selected') === 'true') ??
|
|
511
|
+
this._enabledOptionEls()[0];
|
|
512
|
+
const valueEl = trigger.querySelector<HTMLElement>('[data-slot="select-value"]');
|
|
513
|
+
if (!option || !valueEl) {
|
|
514
|
+
positionFloating(trigger, panel, { side: 'bottom', align: 'start', sideOffset: 4 });
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const p = panel.getBoundingClientRect();
|
|
518
|
+
const o = option.getBoundingClientRect();
|
|
519
|
+
const text = (option.lastElementChild as HTMLElement | null)?.getBoundingClientRect() ?? o;
|
|
520
|
+
const v = valueEl.getBoundingClientRect();
|
|
521
|
+
const margin = 10;
|
|
522
|
+
let top = t.top + t.height / 2 - (o.top - p.top + o.height / 2);
|
|
523
|
+
let left = v.left - (text.left - p.left);
|
|
524
|
+
top = Math.max(margin, Math.min(top, window.innerHeight - p.height - margin));
|
|
525
|
+
left = Math.max(margin, Math.min(left, window.innerWidth - p.width - margin));
|
|
526
|
+
panel.style.top = `${top}px`;
|
|
527
|
+
panel.style.left = `${left}px`;
|
|
528
|
+
panel.setAttribute('data-side', 'none');
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
/** Choose the option at `index` in the native select, then close. */
|
|
532
|
+
_choose(index: number): void {
|
|
533
|
+
const select = this.select;
|
|
534
|
+
if (!select) return;
|
|
535
|
+
const option = select.options[index];
|
|
536
|
+
if (!option || option.disabled) return;
|
|
537
|
+
const changed = select.selectedIndex !== index;
|
|
538
|
+
if (changed) {
|
|
539
|
+
select.selectedIndex = index;
|
|
540
|
+
select.dispatchEvent(new Event('input', { bubbles: true }));
|
|
541
|
+
select.dispatchEvent(new Event('change', { bubbles: true }));
|
|
542
|
+
this.dispatchEvent(new CustomEvent('ui-value-change', { bubbles: true, detail: { value: select.value } }));
|
|
543
|
+
}
|
|
544
|
+
this.requestUpdate();
|
|
545
|
+
if (this.open) this.hide();
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
_indexOf(optionEl: Element | null): number {
|
|
549
|
+
const id = optionEl?.id ?? '';
|
|
550
|
+
const m = /-option-(\d+)$/.exec(id);
|
|
551
|
+
return m ? Number(m[1]) : -1;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
_onTriggerClick = (): void => {
|
|
555
|
+
if (this.open) this.hide();
|
|
556
|
+
else this.show();
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
_onTriggerKeyDown = (e: KeyboardEvent): void => {
|
|
560
|
+
if (this.open) return;
|
|
561
|
+
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
562
|
+
e.preventDefault();
|
|
563
|
+
this._focusLast = e.key === 'ArrowUp';
|
|
564
|
+
this.show();
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
// Typing on a closed select picks, as the native control does.
|
|
568
|
+
if (e.key.length === 1 && e.key !== ' ' && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
|
569
|
+
const match = this._typeahead(e.key);
|
|
570
|
+
if (match) this._choose(this._indexOf(match));
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
_onListKeyDown = (e: KeyboardEvent): void => {
|
|
575
|
+
const enabled = this._enabledOptionEls();
|
|
576
|
+
const active = (document.activeElement as HTMLElement | null)?.closest('[data-slot="select-item"]') as HTMLElement | null;
|
|
577
|
+
const i = active ? enabled.indexOf(active) : -1;
|
|
578
|
+
const focus = (el: HTMLElement | undefined) => {
|
|
579
|
+
if (!el) return;
|
|
580
|
+
el.focus({ preventScroll: true });
|
|
581
|
+
el.scrollIntoView({ block: 'nearest' });
|
|
582
|
+
};
|
|
583
|
+
if (e.key === 'ArrowDown') { e.preventDefault(); focus(enabled[Math.min(i + 1, enabled.length - 1)]); }
|
|
584
|
+
else if (e.key === 'ArrowUp') { e.preventDefault(); focus(enabled[Math.max(i - 1, 0)]); }
|
|
585
|
+
else if (e.key === 'Home') { e.preventDefault(); focus(enabled[0]); }
|
|
586
|
+
else if (e.key === 'End') { e.preventDefault(); focus(enabled[enabled.length - 1]); }
|
|
587
|
+
else if (e.key === 'Escape') { e.preventDefault(); this.hide(); }
|
|
588
|
+
else if (e.key === 'Enter' || (e.key === ' ' && !this._typeBuffer)) {
|
|
589
|
+
e.preventDefault();
|
|
590
|
+
if (active) this._choose(this._indexOf(active));
|
|
591
|
+
} else if (e.key === 'Tab') {
|
|
592
|
+
// Choose and move on: focus goes back to the trigger here, then the
|
|
593
|
+
// browser's own Tab advances from it.
|
|
594
|
+
if (active) this._choose(this._indexOf(active));
|
|
595
|
+
else this.hide();
|
|
596
|
+
} else if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
|
597
|
+
const match = this._typeahead(e.key);
|
|
598
|
+
if (match) { e.preventDefault(); focus(match); }
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
_typeahead(key: string): HTMLElement | null {
|
|
603
|
+
this._typeBuffer = (this._typeBuffer + key).toLowerCase();
|
|
604
|
+
clearTimeout(this._typeTimer);
|
|
605
|
+
this._typeTimer = window.setTimeout(() => { this._typeBuffer = ''; }, 500);
|
|
606
|
+
const buffer = this._typeBuffer;
|
|
607
|
+
return this._enabledOptionEls().find((o) => (o.textContent ?? '').trim().toLowerCase().startsWith(buffer)) ?? null;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Pointing at an option focuses it, which is what highlights it.
|
|
611
|
+
_onOptionPointerMove = (e: PointerEvent): void => {
|
|
612
|
+
const option = e.currentTarget as HTMLElement;
|
|
613
|
+
if (option.hasAttribute('data-disabled') || document.activeElement === option) return;
|
|
614
|
+
option.focus({ preventScroll: true });
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
// A press inside the list must not blur the focused option first.
|
|
618
|
+
_onListPointerDown = (e: PointerEvent): void => {
|
|
619
|
+
e.preventDefault();
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
_onDocPointerDown = (e: Event): void => {
|
|
623
|
+
if (e.composedPath().includes(this)) return;
|
|
624
|
+
this.hide();
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
_syncScrollButtons = (): void => {
|
|
628
|
+
const panel = this._panel();
|
|
629
|
+
if (!panel) return;
|
|
630
|
+
const up = panel.querySelector<HTMLElement>('[data-slot="select-scroll-up-button"]');
|
|
631
|
+
const down = panel.querySelector<HTMLElement>('[data-slot="select-scroll-down-button"]');
|
|
632
|
+
if (up) up.hidden = panel.scrollTop <= 0;
|
|
633
|
+
if (down) down.hidden = panel.scrollTop + panel.clientHeight >= panel.scrollHeight - 1;
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
_autoScroll(dir: number): void {
|
|
637
|
+
this._stopScroll();
|
|
638
|
+
this._scrollTimer = window.setInterval(() => {
|
|
639
|
+
const panel = this._panel();
|
|
640
|
+
if (!panel) return;
|
|
641
|
+
panel.scrollTop += dir * 16;
|
|
642
|
+
this._syncScrollButtons();
|
|
643
|
+
}, 30);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
_stopScroll = (): void => {
|
|
647
|
+
clearInterval(this._scrollTimer);
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
UiSelect.register('ui-select');
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import { cn } from '../lib/utils.ts';
|
|
26
26
|
|
|
27
27
|
const BASE =
|
|
28
|
-
'shrink-0 bg-border data-
|
|
28
|
+
'cn-separator shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch';
|
|
29
29
|
|
|
30
30
|
export type SeparatorOrientation = 'horizontal' | 'vertical';
|
|
31
31
|
|