@webjsdev/ui 0.3.17 → 0.4.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 +66 -7
- 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 +637 -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/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 +49 -4
- 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 +236 -12
- package/src/commands/diff.js +34 -20
- package/src/commands/info.js +7 -0
- package/src/commands/init.js +125 -8
- package/src/commands/list.js +43 -11
- package/src/commands/view.js +33 -5
- package/src/registry/extract.js +192 -2
- 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,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slider: pick a number, or a range, on a track. Tier 1 and Tier 2.
|
|
3
|
+
*
|
|
4
|
+
* Tier 1: `sliderClass()` styles one native `<input type="range">`, which is
|
|
5
|
+
* all a single value needs and works with no JavaScript at all.
|
|
6
|
+
*
|
|
7
|
+
* Tier 2: `<ui-slider>` ENHANCES one native range input per thumb (D13). You
|
|
8
|
+
* author the inputs, so without JavaScript the page shows them and the form
|
|
9
|
+
* submits them. With JavaScript the element hides them (visually only: they
|
|
10
|
+
* still submit and reset with the form) and draws shadcn's track, range and
|
|
11
|
+
* thumbs, writing every move back into the inputs. Two inputs make a range,
|
|
12
|
+
* and a thumb never passes its neighbour.
|
|
13
|
+
*
|
|
14
|
+
* shadcn parity:
|
|
15
|
+
* Slider → <ui-slider orientation> around one
|
|
16
|
+
* <input type="range" min max step value> per thumb
|
|
17
|
+
* defaultValue / value → each input's value
|
|
18
|
+
* min / max / step → the inputs' own attributes (read from the first)
|
|
19
|
+
* disabled → `disabled` on the inputs
|
|
20
|
+
* onValueChange → `ui-value-change` ({ detail: { value: number[] } });
|
|
21
|
+
* each input also fires `input`
|
|
22
|
+
* onValueCommit → `ui-value-commit` ({ detail: { value: number[] } });
|
|
23
|
+
* each moved input also fires `change`
|
|
24
|
+
*
|
|
25
|
+
* Keyboard (as Radix's): ArrowRight / ArrowUp step up, ArrowLeft / ArrowDown
|
|
26
|
+
* step down (ArrowLeft / ArrowRight swap under `dir="rtl"`), PageUp / PageDown
|
|
27
|
+
* and Shift with an arrow move ten steps, Home / End go to the ends. A press
|
|
28
|
+
* on the track moves the nearest thumb there and drags it.
|
|
29
|
+
*
|
|
30
|
+
* A11y (handled by the element, plus your part): each thumb is a focusable
|
|
31
|
+
* `role="slider"` with `aria-valuemin`, `aria-valuemax`, `aria-valuenow` and
|
|
32
|
+
* `aria-orientation`, named by its input's `<label>`s or `aria-label`, and a
|
|
33
|
+
* label click focuses its thumb. Your part: a name for every input, which for
|
|
34
|
+
* a range means one per thumb ("Minimum price", "Maximum price").
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```html
|
|
38
|
+
* <input type="range" class=${sliderClass()} min="0" max="100" value="50" aria-label="Volume">
|
|
39
|
+
*
|
|
40
|
+
* <ui-slider>
|
|
41
|
+
* <input type="range" class=${sliderClass()} name="min" min="0" max="100" step="5" value="25" aria-label="Minimum">
|
|
42
|
+
* <input type="range" class=${sliderClass()} name="max" min="0" max="100" step="5" value="75" aria-label="Maximum">
|
|
43
|
+
* </ui-slider>
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
import { WebComponent, html, prop, signal, nothing } from '@webjsdev/core';
|
|
47
|
+
import { ensureId } from '../lib/utils.ts';
|
|
48
|
+
|
|
49
|
+
/** One native `<input type="range">`, styled to match the drawn slider. */
|
|
50
|
+
export function sliderClass(): string {
|
|
51
|
+
return [
|
|
52
|
+
'h-4 w-full cursor-pointer appearance-none bg-transparent disabled:cursor-not-allowed disabled:opacity-50',
|
|
53
|
+
'focus-visible:outline-hidden',
|
|
54
|
+
'[&::-webkit-slider-runnable-track]:h-1.5 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-muted',
|
|
55
|
+
'[&::-webkit-slider-thumb]:-mt-[5px] [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border [&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-sm',
|
|
56
|
+
'focus-visible:[&::-webkit-slider-thumb]:ring-4 focus-visible:[&::-webkit-slider-thumb]:ring-ring/50',
|
|
57
|
+
'[&::-moz-range-track]:h-1.5 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-muted',
|
|
58
|
+
'[&::-moz-range-progress]:h-1.5 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-primary',
|
|
59
|
+
'[&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border [&::-moz-range-thumb]:border-primary [&::-moz-range-thumb]:bg-white [&::-moz-range-thumb]:shadow-sm',
|
|
60
|
+
].join(' ');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The drawn slider's root (the element applies them). */
|
|
64
|
+
export const sliderRootClass = (): string =>
|
|
65
|
+
'cn-slider relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col';
|
|
66
|
+
/** The track (the element applies them). */
|
|
67
|
+
export const sliderTrackClass = (): string =>
|
|
68
|
+
'cn-slider-track relative grow overflow-hidden data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full';
|
|
69
|
+
/** The filled range (the element applies them). */
|
|
70
|
+
export const sliderRangeClass = (): string =>
|
|
71
|
+
'cn-slider-range absolute select-none data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full';
|
|
72
|
+
/** A thumb (the element applies them). */
|
|
73
|
+
export const sliderThumbClass = (): string =>
|
|
74
|
+
'cn-slider-thumb block shrink-0 select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50';
|
|
75
|
+
|
|
76
|
+
// The visual-only hide the select uses: still submitted and reset with the form.
|
|
77
|
+
const HIDDEN_CONTROL =
|
|
78
|
+
'position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;opacity:0;pointer-events:none;overflow:hidden;';
|
|
79
|
+
|
|
80
|
+
export class UiSlider extends WebComponent({
|
|
81
|
+
orientation: prop<'horizontal' | 'vertical'>(String, { reflect: true }),
|
|
82
|
+
}) {
|
|
83
|
+
readonly #enhanced = signal(false);
|
|
84
|
+
_observer: MutationObserver | null = null;
|
|
85
|
+
_waiting: MutationObserver | null = null;
|
|
86
|
+
_form: HTMLFormElement | null = null;
|
|
87
|
+
_saved = new Map<HTMLInputElement, string | null>();
|
|
88
|
+
_drag = -1;
|
|
89
|
+
_moved = new Set<number>();
|
|
90
|
+
|
|
91
|
+
constructor() {
|
|
92
|
+
super();
|
|
93
|
+
this.orientation = 'horizontal';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** The native range inputs, one per thumb. */
|
|
97
|
+
inputs(): HTMLInputElement[] {
|
|
98
|
+
return Array.from(this.querySelectorAll<HTMLInputElement>('input[type="range"]'));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** The current values, one per thumb. */
|
|
102
|
+
get value(): number[] {
|
|
103
|
+
return this.inputs().map((i) => Number(i.value));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
connectedCallback(): void {
|
|
107
|
+
super.connectedCallback?.();
|
|
108
|
+
queueMicrotask(() => {
|
|
109
|
+
if (this._enhance() || this._waiting) return;
|
|
110
|
+
this._waiting = new MutationObserver(() => {
|
|
111
|
+
if (this._enhance()) { this._waiting?.disconnect(); this._waiting = null; }
|
|
112
|
+
});
|
|
113
|
+
this._waiting.observe(this, { childList: true, subtree: true });
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
disconnectedCallback(): void {
|
|
118
|
+
this._waiting?.disconnect();
|
|
119
|
+
this._observer?.disconnect();
|
|
120
|
+
for (const [input, style] of this._saved) {
|
|
121
|
+
input.removeEventListener('focus', this._onInputFocus);
|
|
122
|
+
if (style !== null) input.setAttribute('style', style);
|
|
123
|
+
else input.removeAttribute('style');
|
|
124
|
+
input.removeAttribute('aria-hidden');
|
|
125
|
+
input.removeAttribute('tabindex');
|
|
126
|
+
}
|
|
127
|
+
this._saved.clear();
|
|
128
|
+
this._form?.removeEventListener('reset', this._onReset);
|
|
129
|
+
super.disconnectedCallback?.();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Take over from the native inputs. True once it has (or already had). */
|
|
133
|
+
_enhance(): boolean {
|
|
134
|
+
if (this.#enhanced.get()) return true;
|
|
135
|
+
const inputs = this.inputs();
|
|
136
|
+
if (!inputs.length) return false;
|
|
137
|
+
for (const input of inputs) {
|
|
138
|
+
const style = input.getAttribute('style');
|
|
139
|
+
this._saved.set(input, style);
|
|
140
|
+
input.setAttribute('style', (style ?? '') + HIDDEN_CONTROL);
|
|
141
|
+
input.setAttribute('aria-hidden', 'true');
|
|
142
|
+
input.tabIndex = -1;
|
|
143
|
+
input.addEventListener('focus', this._onInputFocus);
|
|
144
|
+
}
|
|
145
|
+
this._form = inputs[0].form;
|
|
146
|
+
this._form?.addEventListener('reset', this._onReset);
|
|
147
|
+
this._observer = new MutationObserver(() => { this.requestUpdate(); });
|
|
148
|
+
for (const input of inputs) this._observer.observe(input, { attributes: true });
|
|
149
|
+
this.#enhanced.set(true);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
_onReset = (): void => {
|
|
154
|
+
queueMicrotask(() => { this.requestUpdate(); });
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// A label click focuses the hidden input; the user means its thumb.
|
|
158
|
+
_onInputFocus = (e: Event): void => {
|
|
159
|
+
const i = this.inputs().indexOf(e.target as HTMLInputElement);
|
|
160
|
+
this._thumbs()[i]?.focus();
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
_thumbs(): HTMLElement[] {
|
|
164
|
+
return Array.from(this.querySelectorAll<HTMLElement>('[data-slot="slider-thumb"]'));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
_bounds(): { min: number; max: number; step: number } {
|
|
168
|
+
const first = this.inputs()[0];
|
|
169
|
+
const min = Number(first?.min || 0);
|
|
170
|
+
const max = Number(first?.max || 100);
|
|
171
|
+
const step = Number(first?.step) > 0 ? Number(first.step) : 1;
|
|
172
|
+
return { min, max: max > min ? max : min + 1, step };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
_rtl(): boolean {
|
|
176
|
+
return typeof getComputedStyle === 'function' && getComputedStyle(this).direction === 'rtl';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
render() {
|
|
180
|
+
if (!this.#enhanced.get()) {
|
|
181
|
+
return html`<div data-slot="slider" data-orientation=${this.orientation || 'horizontal'} class="grid gap-2"><slot></slot></div>`;
|
|
182
|
+
}
|
|
183
|
+
const vertical = this.orientation === 'vertical';
|
|
184
|
+
const orientation = vertical ? 'vertical' : 'horizontal';
|
|
185
|
+
const inputs = this.inputs();
|
|
186
|
+
const { min, max } = this._bounds();
|
|
187
|
+
const pct = (v: number) => ((Math.min(max, Math.max(min, v)) - min) / (max - min)) * 100;
|
|
188
|
+
const values = inputs.map((i) => Number(i.value));
|
|
189
|
+
const disabled = inputs.some((i) => i.disabled);
|
|
190
|
+
const lo = values.length > 1 ? pct(Math.min(...values)) : 0;
|
|
191
|
+
const hi = pct(Math.max(...values));
|
|
192
|
+
const rangeStyle = vertical
|
|
193
|
+
? `bottom:${lo}%;top:${100 - hi}%`
|
|
194
|
+
: `inset-inline-start:${lo}%;inset-inline-end:${100 - hi}%`;
|
|
195
|
+
const flip = this._rtl() ? 1 : -1;
|
|
196
|
+
return html`<div
|
|
197
|
+
data-slot="slider"
|
|
198
|
+
data-orientation=${orientation}
|
|
199
|
+
?data-disabled=${disabled}
|
|
200
|
+
class=${sliderRootClass()}
|
|
201
|
+
@pointerdown=${this._onPointerDown}
|
|
202
|
+
@pointermove=${this._onPointerMove}
|
|
203
|
+
@pointerup=${this._onPointerUp}
|
|
204
|
+
@pointercancel=${this._onPointerUp}
|
|
205
|
+
><slot></slot><span data-slot="slider-track" data-orientation=${orientation} class=${sliderTrackClass()}><span
|
|
206
|
+
data-slot="slider-range" data-orientation=${orientation} class=${sliderRangeClass()} style=${rangeStyle}
|
|
207
|
+
></span></span>${inputs.map((input, i) => {
|
|
208
|
+
const p = pct(values[i]);
|
|
209
|
+
// At P% along the track, shifted back by P% of its own size, so a
|
|
210
|
+
// thumb sits inside the track at both ends, as Radix's does.
|
|
211
|
+
const place = vertical
|
|
212
|
+
? `position:absolute;bottom:${p}%;transform:translateY(${p}%)`
|
|
213
|
+
: `position:absolute;inset-inline-start:${p}%;transform:translateX(${flip * p}%)`;
|
|
214
|
+
const labels = input.labels ? Array.from(input.labels).map((l) => ensureId(l, 'ui-slider-label')) : [];
|
|
215
|
+
return html`<span style=${place}><span
|
|
216
|
+
data-slot="slider-thumb"
|
|
217
|
+
role="slider"
|
|
218
|
+
tabindex=${input.disabled ? '-1' : '0'}
|
|
219
|
+
aria-valuemin=${String(min)}
|
|
220
|
+
aria-valuemax=${String(max)}
|
|
221
|
+
aria-valuenow=${String(values[i])}
|
|
222
|
+
aria-valuetext=${input.getAttribute('aria-valuetext') || nothing}
|
|
223
|
+
aria-orientation=${orientation}
|
|
224
|
+
aria-labelledby=${labels.length ? labels.join(' ') : nothing}
|
|
225
|
+
aria-label=${!labels.length && input.getAttribute('aria-label') ? input.getAttribute('aria-label') : nothing}
|
|
226
|
+
aria-disabled=${input.disabled ? 'true' : nothing}
|
|
227
|
+
?data-disabled=${input.disabled}
|
|
228
|
+
data-orientation=${orientation}
|
|
229
|
+
class=${sliderThumbClass()}
|
|
230
|
+
@keydown=${(e: KeyboardEvent) => this._onKeyDown(e, i)}
|
|
231
|
+
></span></span>`;
|
|
232
|
+
})}</div>`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Move thumb `i` to `raw`, snapped to the step and kept between its neighbours. */
|
|
236
|
+
_set(i: number, raw: number): boolean {
|
|
237
|
+
const inputs = this.inputs();
|
|
238
|
+
const input = inputs[i];
|
|
239
|
+
if (!input || input.disabled) return false;
|
|
240
|
+
const { min, max, step } = this._bounds();
|
|
241
|
+
let v = Math.round((raw - min) / step) * step + min;
|
|
242
|
+
v = Number(v.toFixed(10));
|
|
243
|
+
const lower = i > 0 ? Number(inputs[i - 1].value) : min;
|
|
244
|
+
const upper = i < inputs.length - 1 ? Number(inputs[i + 1].value) : max;
|
|
245
|
+
v = Math.min(Math.max(v, lower, min), upper, max);
|
|
246
|
+
if (Number(input.value) === v) return false;
|
|
247
|
+
input.value = String(v);
|
|
248
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
249
|
+
this._moved.add(i);
|
|
250
|
+
this.requestUpdate();
|
|
251
|
+
this.dispatchEvent(new CustomEvent('ui-value-change', { bubbles: true, detail: { value: this.value } }));
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// The end of a gesture: a key press, or a pointer release.
|
|
256
|
+
_commit(): void {
|
|
257
|
+
if (!this._moved.size) return;
|
|
258
|
+
const inputs = this.inputs();
|
|
259
|
+
for (const i of this._moved) inputs[i]?.dispatchEvent(new Event('change', { bubbles: true }));
|
|
260
|
+
this._moved.clear();
|
|
261
|
+
this.dispatchEvent(new CustomEvent('ui-value-commit', { bubbles: true, detail: { value: this.value } }));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
_onKeyDown(e: KeyboardEvent, i: number): void {
|
|
265
|
+
const { min, max, step } = this._bounds();
|
|
266
|
+
const v = Number(this.inputs()[i]?.value);
|
|
267
|
+
const big = step * 10;
|
|
268
|
+
const rtl = this._rtl() && this.orientation !== 'vertical';
|
|
269
|
+
const up = (n: number) => this._set(i, v + n);
|
|
270
|
+
let handled = true;
|
|
271
|
+
switch (e.key) {
|
|
272
|
+
case 'ArrowUp': up(e.shiftKey ? big : step); break;
|
|
273
|
+
case 'ArrowDown': up(-(e.shiftKey ? big : step)); break;
|
|
274
|
+
case 'ArrowRight': up((rtl ? -1 : 1) * (e.shiftKey ? big : step)); break;
|
|
275
|
+
case 'ArrowLeft': up((rtl ? 1 : -1) * (e.shiftKey ? big : step)); break;
|
|
276
|
+
case 'PageUp': up(big); break;
|
|
277
|
+
case 'PageDown': up(-big); break;
|
|
278
|
+
case 'Home': this._set(i, min); break;
|
|
279
|
+
case 'End': this._set(i, max); break;
|
|
280
|
+
default: handled = false;
|
|
281
|
+
}
|
|
282
|
+
if (!handled) return;
|
|
283
|
+
e.preventDefault();
|
|
284
|
+
this._commit();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// The value under a pointer position, from the track's box.
|
|
288
|
+
_valueAt(e: PointerEvent): number {
|
|
289
|
+
const track = this.querySelector<HTMLElement>('[data-slot="slider-track"]');
|
|
290
|
+
const { min, max } = this._bounds();
|
|
291
|
+
if (!track) return min;
|
|
292
|
+
const r = track.getBoundingClientRect();
|
|
293
|
+
let t: number;
|
|
294
|
+
if (this.orientation === 'vertical') t = (r.bottom - e.clientY) / r.height;
|
|
295
|
+
else t = this._rtl() ? (r.right - e.clientX) / r.width : (e.clientX - r.left) / r.width;
|
|
296
|
+
return min + Math.min(1, Math.max(0, t)) * (max - min);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
_onPointerDown = (e: PointerEvent): void => {
|
|
300
|
+
const inputs = this.inputs();
|
|
301
|
+
if (!inputs.length || inputs.every((i) => i.disabled) || e.button !== 0) return;
|
|
302
|
+
const v = this._valueAt(e);
|
|
303
|
+
const values = inputs.map((i) => Number(i.value));
|
|
304
|
+
// The nearest thumb; between two at the same spot, the one the press is
|
|
305
|
+
// past, so a range collapsed to one point can still be pulled apart.
|
|
306
|
+
let best = 0;
|
|
307
|
+
values.forEach((x, i) => {
|
|
308
|
+
const d = Math.abs(x - v);
|
|
309
|
+
const bestD = Math.abs(values[best] - v);
|
|
310
|
+
if (d < bestD || (d === bestD && v > x)) best = i;
|
|
311
|
+
});
|
|
312
|
+
if (inputs[best].disabled) return;
|
|
313
|
+
e.preventDefault();
|
|
314
|
+
this._drag = best;
|
|
315
|
+
// A pointer the engine does not track (a synthetic one) cannot be captured.
|
|
316
|
+
try { (e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId); } catch { /* drag without capture */ }
|
|
317
|
+
this._set(best, v);
|
|
318
|
+
this._thumbs()[best]?.focus();
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
_onPointerMove = (e: PointerEvent): void => {
|
|
322
|
+
if (this._drag < 0) return;
|
|
323
|
+
this._set(this._drag, this._valueAt(e));
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
_onPointerUp = (e: PointerEvent): void => {
|
|
327
|
+
if (this._drag < 0) return;
|
|
328
|
+
this._drag = -1;
|
|
329
|
+
try { (e.currentTarget as HTMLElement).releasePointerCapture?.(e.pointerId); } catch { /* already released */ }
|
|
330
|
+
this._commit();
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
UiSlider.register('ui-slider');
|
|
@@ -23,6 +23,19 @@
|
|
|
23
23
|
* `action`: { label, onClick } | undefined. Renders an action button.
|
|
24
24
|
* `cancel`: { label, onClick } | undefined. Renders a cancel button.
|
|
25
25
|
* Both buttons dismiss the toast after running their onClick.
|
|
26
|
+
* `position`: one of the viewport positions. The toast shows at that
|
|
27
|
+
* corner instead of the viewport's own (shadcn's per-toast
|
|
28
|
+
* `position`).
|
|
29
|
+
*
|
|
30
|
+
* Firing a toast with no script of your own: any element carrying
|
|
31
|
+
* `data-toast="<message>"` fires one when clicked, as shadcn's demos do with
|
|
32
|
+
* `onClick={() => toast(...)}`. A mounted `<ui-sonner>` handles the clicks
|
|
33
|
+
* for the whole page through one delegated listener. The optional attributes
|
|
34
|
+
* are `data-toast-type` (`success` | `error` | `info` | `warning` |
|
|
35
|
+
* `loading`), `data-toast-description`, `data-toast-position`,
|
|
36
|
+
* `data-toast-duration` (ms), and `data-toast-action` (a button label; the
|
|
37
|
+
* button dismisses the toast). Put it on a real `<button type="button">` so it
|
|
38
|
+
* is focusable and fires from the keyboard too.
|
|
26
39
|
*
|
|
27
40
|
* Events: none dispatched (consumers act on the id returned by `toast()`).
|
|
28
41
|
*
|
|
@@ -83,6 +96,7 @@ interface ToastOptions {
|
|
|
83
96
|
duration?: number;
|
|
84
97
|
action?: { label: string; onClick: () => void };
|
|
85
98
|
cancel?: { label: string; onClick: () => void };
|
|
99
|
+
position?: SonnerPosition;
|
|
86
100
|
}
|
|
87
101
|
|
|
88
102
|
interface ToastItem extends ToastOptions {
|
|
@@ -126,9 +140,47 @@ function makeToast(message: string, opts: ToastOptions = {}, type: ToastType = '
|
|
|
126
140
|
duration: opts.duration ?? (type === 'loading' ? 0 : 4000),
|
|
127
141
|
action: opts.action,
|
|
128
142
|
cancel: opts.cancel,
|
|
143
|
+
position: opts.position,
|
|
129
144
|
});
|
|
130
145
|
}
|
|
131
146
|
|
|
147
|
+
const TOAST_TYPES = ['default', 'success', 'error', 'info', 'warning', 'loading'];
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* A click on (or inside) an element carrying `data-toast` fires the toast it
|
|
151
|
+
* describes. One listener serves the page, however many viewports are
|
|
152
|
+
* mounted, and it routes through the same bus `toast()` uses.
|
|
153
|
+
*/
|
|
154
|
+
function onToastClick(e: Event): void {
|
|
155
|
+
const target = e.target as Element | null;
|
|
156
|
+
const el = target && typeof target.closest === 'function' ? target.closest('[data-toast]') : null;
|
|
157
|
+
if (!el) return;
|
|
158
|
+
const message = el.getAttribute('data-toast') || '';
|
|
159
|
+
if (!message) return;
|
|
160
|
+
const rawType = el.getAttribute('data-toast-type') || 'default';
|
|
161
|
+
const type = (TOAST_TYPES.includes(rawType) ? rawType : 'default') as ToastType;
|
|
162
|
+
const position = el.getAttribute('data-toast-position') as SonnerPosition | null;
|
|
163
|
+
const duration = Number(el.getAttribute('data-toast-duration'));
|
|
164
|
+
const action = el.getAttribute('data-toast-action');
|
|
165
|
+
makeToast(message, {
|
|
166
|
+
description: el.getAttribute('data-toast-description') || undefined,
|
|
167
|
+
position: position && position in POSITIONS ? position : undefined,
|
|
168
|
+
duration: el.hasAttribute('data-toast-duration') && Number.isFinite(duration) ? duration : undefined,
|
|
169
|
+
action: action ? { label: action, onClick: () => {} } : undefined,
|
|
170
|
+
}, type);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Install the page's one `data-toast` listener, counted per mounted viewport. */
|
|
174
|
+
function holdToastClicks(): () => void {
|
|
175
|
+
const g = globalThis as unknown as { __webjsSonnerClicks?: number };
|
|
176
|
+
if (!g.__webjsSonnerClicks) document.addEventListener('click', onToastClick);
|
|
177
|
+
g.__webjsSonnerClicks = (g.__webjsSonnerClicks ?? 0) + 1;
|
|
178
|
+
return () => {
|
|
179
|
+
g.__webjsSonnerClicks = (g.__webjsSonnerClicks ?? 1) - 1;
|
|
180
|
+
if (!g.__webjsSonnerClicks) document.removeEventListener('click', onToastClick);
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
132
184
|
export function toast(message: string, opts?: ToastOptions): string | number {
|
|
133
185
|
return makeToast(message, opts, 'default');
|
|
134
186
|
}
|
|
@@ -159,11 +211,11 @@ toast.promise = <T,>(p: Promise<T>, opts: { loading: string; success: string; er
|
|
|
159
211
|
// --------------------------------------------------------------------------
|
|
160
212
|
|
|
161
213
|
const POSITIONS = {
|
|
162
|
-
'top-right': 'top-4
|
|
163
|
-
'top-left': 'top-4
|
|
214
|
+
'top-right': 'top-4 end-4',
|
|
215
|
+
'top-left': 'top-4 start-4',
|
|
164
216
|
'top-center': 'top-4 left-1/2 -translate-x-1/2',
|
|
165
|
-
'bottom-right': 'bottom-4
|
|
166
|
-
'bottom-left': 'bottom-4
|
|
217
|
+
'bottom-right': 'bottom-4 end-4',
|
|
218
|
+
'bottom-left': 'bottom-4 start-4',
|
|
167
219
|
'bottom-center': 'bottom-4 left-1/2 -translate-x-1/2',
|
|
168
220
|
} as const;
|
|
169
221
|
|
|
@@ -184,9 +236,10 @@ const TYPE_ICON_COLOR: Record<ToastType, string> = {
|
|
|
184
236
|
export class UiSonner extends WebComponent({
|
|
185
237
|
position: prop<SonnerPosition>(String, { reflect: true }),
|
|
186
238
|
}) {
|
|
187
|
-
items = signal<ToastItem[]>([]);
|
|
239
|
+
readonly items = signal<ToastItem[]>([]);
|
|
188
240
|
|
|
189
241
|
_disposeBeforeCache?: () => void;
|
|
242
|
+
_releaseClicks?: () => void;
|
|
190
243
|
|
|
191
244
|
constructor() {
|
|
192
245
|
super();
|
|
@@ -198,10 +251,13 @@ export class UiSonner extends WebComponent({
|
|
|
198
251
|
// Clear pending toasts before a back/forward snapshot so a restored page
|
|
199
252
|
// does not show stale toasts that should have auto-dismissed (#766).
|
|
200
253
|
this._disposeBeforeCache = onBeforeCache(() => this.items.set([]));
|
|
254
|
+
this._releaseClicks = holdToastClicks();
|
|
201
255
|
}
|
|
202
256
|
|
|
203
257
|
disconnectedCallback(): void {
|
|
204
258
|
this._disposeBeforeCache?.();
|
|
259
|
+
this._releaseClicks?.();
|
|
260
|
+
this._releaseClicks = undefined;
|
|
205
261
|
super.disconnectedCallback?.();
|
|
206
262
|
}
|
|
207
263
|
|
|
@@ -235,6 +291,7 @@ export class UiSonner extends WebComponent({
|
|
|
235
291
|
duration: opts.duration ?? (type === 'loading' ? 0 : 4000),
|
|
236
292
|
action: opts.action,
|
|
237
293
|
cancel: opts.cancel,
|
|
294
|
+
position: opts.position,
|
|
238
295
|
});
|
|
239
296
|
return id;
|
|
240
297
|
}
|
|
@@ -251,7 +308,16 @@ export class UiSonner extends WebComponent({
|
|
|
251
308
|
}
|
|
252
309
|
|
|
253
310
|
render() {
|
|
254
|
-
const
|
|
311
|
+
const own = this.position in POSITIONS ? this.position : 'bottom-right';
|
|
312
|
+
const items = this.items.get();
|
|
313
|
+
// A toast with its own position shows at that corner, in a region of its
|
|
314
|
+
// own; the viewport's region stays in the DOM from the first render.
|
|
315
|
+
const elsewhere = [...new Set(items.map((i) => i.position).filter((p): p is SonnerPosition => !!p && p !== own && p in POSITIONS))];
|
|
316
|
+
return html`${this._region(own, items.filter((i) => !i.position || i.position === own || !(i.position in POSITIONS)))}${elsewhere.map((p) => this._region(p, items.filter((i) => i.position === p)))}`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
_region(position: SonnerPosition, items: ToastItem[]) {
|
|
320
|
+
const pos = POSITIONS[position];
|
|
255
321
|
// The container is a persistent live region: it is in the DOM from the
|
|
256
322
|
// first render (even with zero toasts), so a screen reader announces
|
|
257
323
|
// each toast as it is inserted. It is polite, and it is the ONLY live
|
|
@@ -266,6 +332,7 @@ export class UiSonner extends WebComponent({
|
|
|
266
332
|
// polite viewport, and its second live root is the accepted cost.
|
|
267
333
|
return html`<div
|
|
268
334
|
data-slot="sonner"
|
|
335
|
+
data-position=${position}
|
|
269
336
|
role="region"
|
|
270
337
|
aria-label="Notifications"
|
|
271
338
|
aria-live="polite"
|
|
@@ -274,7 +341,7 @@ export class UiSonner extends WebComponent({
|
|
|
274
341
|
class=${`pointer-events-none fixed z-[100] flex flex-col gap-2 ${pos}`}
|
|
275
342
|
>
|
|
276
343
|
${repeat(
|
|
277
|
-
|
|
344
|
+
items,
|
|
278
345
|
(item) => item.id,
|
|
279
346
|
// The role is BRANCHED rather than written as a nullish hole. A hole
|
|
280
347
|
// that evaluates to null serves role="" from the server renderer while
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* </button>
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
|
-
import { html } from '@webjsdev/core';
|
|
42
|
+
import { html, nothing } from '@webjsdev/core';
|
|
43
43
|
import { cn } from '../lib/utils.ts';
|
|
44
44
|
|
|
45
45
|
/** Spinner classes: `size-4` plus `animate-spin`. Apply to your own `<svg>`. */
|
|
@@ -51,13 +51,16 @@ export const spinnerClass = (): string => 'size-4 animate-spin';
|
|
|
51
51
|
* "Loading"), and `decorative` hides it from assistive tech for use beside
|
|
52
52
|
* visible text such as a busy button's label.
|
|
53
53
|
*/
|
|
54
|
-
export function spinner(opts: { class?: string; label?: string; decorative?: boolean } = {}) {
|
|
54
|
+
export function spinner(opts: { class?: string; label?: string; decorative?: boolean; icon?: 'inline-start' | 'inline-end' } = {}) {
|
|
55
55
|
const cls = cn(spinnerClass(), opts.class);
|
|
56
|
+
// shadcn's `data-icon`, which a button or badge reads to tighten its padding
|
|
57
|
+
// on the spinner's side. Left out, the attribute is omitted.
|
|
58
|
+
const icon = opts.icon ?? nothing;
|
|
56
59
|
// Branched rather than a nullish `role=${...}` hole: the server renderer
|
|
57
60
|
// stringifies a null attribute value, so a single template would serve
|
|
58
61
|
// `role=""` on the decorative form.
|
|
59
62
|
if (opts.decorative) {
|
|
60
|
-
return html`<svg data-slot="spinner" aria-hidden="true" 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" class=${cls}><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>`;
|
|
63
|
+
return html`<svg data-slot="spinner" data-icon=${icon} aria-hidden="true" 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" class=${cls}><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>`;
|
|
61
64
|
}
|
|
62
|
-
return html`<svg data-slot="spinner" role="status" aria-label=${opts.label ?? 'Loading'} 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" class=${cls}><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>`;
|
|
65
|
+
return html`<svg data-slot="spinner" data-icon=${icon} role="status" aria-label=${opts.label ?? 'Loading'} 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" class=${cls}><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>`;
|
|
63
66
|
}
|