@webjsdev/ui 0.3.16 → 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 +71 -10
- 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 +88 -0
- package/packages/registry/components/field.ts +165 -0
- 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 +66 -0
- 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 +4092 -60
- 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,637 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dashboard-01 data table: the document's outline sections under a row of
|
|
3
|
+
* view tabs, with a columns menu, row selection, drag-to-reorder, inline
|
|
4
|
+
* target and limit fields, a reviewer picker, a row actions menu, pagination,
|
|
5
|
+
* and a drawer that opens a section for editing. Ported from shadcn/ui's
|
|
6
|
+
* dashboard-01, which builds it on TanStack Table and dnd-kit.
|
|
7
|
+
*
|
|
8
|
+
* The server renders the first page of 10 rows, so the first paint is
|
|
9
|
+
* shadcn's and reads with JavaScript off, and the document holds only the
|
|
10
|
+
* rows on screen. Every row rides on the element's `rows` attribute, and
|
|
11
|
+
* `<dashboard-01-data-table>` (below) renders the current page's rows from
|
|
12
|
+
* it on each change, with no third-party code: the page buttons and the page
|
|
13
|
+
* size, the row checkboxes with select-all and the "n of m row(s) selected"
|
|
14
|
+
* count, the columns menu, dragging a row by its handle (or moving it with
|
|
15
|
+
* the arrow keys while the handle has focus), the view select on a narrow
|
|
16
|
+
* card, and the one drawer that every row's title fills and opens. The order,
|
|
17
|
+
* the selection and the edited cells live in the element, so they hold across
|
|
18
|
+
* pages.
|
|
19
|
+
*
|
|
20
|
+
* The forms have no action yet. Bind a server action from a `'use server'`
|
|
21
|
+
* file to the inline target and limit forms and to the drawer's form
|
|
22
|
+
* (`<form action=${saveSection}>`), and render the rows from your own query
|
|
23
|
+
* in place of `dashboardData`. Until then the inline forms submit as a GET
|
|
24
|
+
* back to the page.
|
|
25
|
+
*/
|
|
26
|
+
import { WebComponent, html, prop, render, repeat } from '@webjsdev/core';
|
|
27
|
+
import { cn } from '../../../lib/utils.ts';
|
|
28
|
+
import { badgeClass } from '../../../components/badge.ts';
|
|
29
|
+
import { buttonClass } from '../../../components/button.ts';
|
|
30
|
+
import { areaChart, type ChartConfig } from '../../../components/chart.ts';
|
|
31
|
+
import { checkboxClass } from '../../../components/checkbox.ts';
|
|
32
|
+
import {
|
|
33
|
+
drawerDescriptionClass,
|
|
34
|
+
drawerFooterClass,
|
|
35
|
+
drawerHeaderClass,
|
|
36
|
+
drawerTitleClass,
|
|
37
|
+
} from '../../../components/drawer.ts';
|
|
38
|
+
import { inputClass } from '../../../components/input.ts';
|
|
39
|
+
import { labelClass } from '../../../components/label.ts';
|
|
40
|
+
import { selectTriggerClass } from '../../../components/select.ts';
|
|
41
|
+
import { separatorClass } from '../../../components/separator.ts';
|
|
42
|
+
import {
|
|
43
|
+
tableBodyClass,
|
|
44
|
+
tableCellClass,
|
|
45
|
+
tableClass,
|
|
46
|
+
tableContainerClass,
|
|
47
|
+
tableHeadClass,
|
|
48
|
+
tableHeaderClass,
|
|
49
|
+
tableRowClass,
|
|
50
|
+
} from '../../../components/table.ts';
|
|
51
|
+
import '../../../components/checkbox.ts';
|
|
52
|
+
import '../../../components/drawer.ts';
|
|
53
|
+
import '../../../components/dropdown-menu.ts';
|
|
54
|
+
import '../../../components/select.ts';
|
|
55
|
+
import '../../../components/tabs.ts';
|
|
56
|
+
import type { DashboardRow } from './dashboard-data.ts';
|
|
57
|
+
import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronsLeftIcon, ChevronsRightIcon, CircleCheckIcon, Columns3Icon, EllipsisVerticalIcon, GripVerticalIcon, LoaderIcon, PlusIcon, TrendingUpIcon } from '../../../lib/icons.ts';
|
|
58
|
+
|
|
59
|
+
/** The columns the columns menu can hide, shadcn's hideable accessor columns. */
|
|
60
|
+
const HIDEABLE = ['type', 'status', 'target', 'limit', 'reviewer'];
|
|
61
|
+
|
|
62
|
+
const VIEWS = [
|
|
63
|
+
{ value: 'outline', label: 'Outline' },
|
|
64
|
+
{ value: 'past-performance', label: 'Past Performance', count: 3 },
|
|
65
|
+
{ value: 'key-personnel', label: 'Key Personnel', count: 2 },
|
|
66
|
+
{ value: 'focus-documents', label: 'Focus Documents' },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const PAGE_SIZES = [10, 20, 30, 40, 50];
|
|
70
|
+
|
|
71
|
+
const SECTION_TYPES = ['Table of Contents', 'Executive Summary', 'Technical Approach', 'Design', 'Capabilities', 'Focus Documents', 'Narrative', 'Cover Page'];
|
|
72
|
+
const STATUSES = ['Done', 'In Progress', 'Not Started'];
|
|
73
|
+
const REVIEWERS = ['Eddie Lake', 'Jamik Tashpulatov', 'Emily Whalen'];
|
|
74
|
+
|
|
75
|
+
const INLINE_INPUT = 'h-8 w-16 border-transparent bg-transparent text-right shadow-none hover:bg-input/30 focus-visible:border focus-visible:bg-background dark:bg-transparent dark:hover:bg-input/30 dark:focus-visible:bg-input/30';
|
|
76
|
+
|
|
77
|
+
const head = (cls = '') => cn(tableHeadClass(), cls);
|
|
78
|
+
const cell = (cls = '') => cn(tableCellClass(), cls);
|
|
79
|
+
|
|
80
|
+
/** What the row edits in place: the inline fields and the reviewer pick. */
|
|
81
|
+
type RowEdit = Partial<Pick<DashboardRow, 'target' | 'limit' | 'reviewer'>>;
|
|
82
|
+
|
|
83
|
+
/** How one row renders: selected or not, its edits, the hidden columns. */
|
|
84
|
+
interface RowState {
|
|
85
|
+
selected: boolean;
|
|
86
|
+
edit: RowEdit;
|
|
87
|
+
hidden: ReadonlySet<string>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const FIRST_PAINT: RowState = { selected: false, edit: {}, hidden: new Set() };
|
|
91
|
+
|
|
92
|
+
/** One inline field (target or limit), a form of its own as in shadcn. */
|
|
93
|
+
function inlineField(row: DashboardRow, key: 'target' | 'limit', label: string, edit: RowEdit) {
|
|
94
|
+
return html`<form>
|
|
95
|
+
<label for=${`${row.id}-${key}`} data-slot="label" class=${cn(labelClass(), 'sr-only')}>${label}</label>
|
|
96
|
+
<input id=${`${row.id}-${key}`} name=${key} value=${edit[key] ?? row[key]} data-row-edit=${key} data-slot="input" class=${cn(inputClass(), INLINE_INPUT)}>
|
|
97
|
+
</form>`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function reviewerCell(row: DashboardRow, edit: RowEdit) {
|
|
101
|
+
if (row.reviewer !== 'Assign reviewer') return row.reviewer;
|
|
102
|
+
const picked = edit.reviewer ?? '';
|
|
103
|
+
return html`
|
|
104
|
+
<label for=${`${row.id}-reviewer`} data-slot="label" class=${cn(labelClass(), 'sr-only')}>Reviewer</label>
|
|
105
|
+
<ui-select size="sm" placeholder="Assign reviewer">
|
|
106
|
+
<select id=${`${row.id}-reviewer`} name="reviewer" data-row-edit="reviewer" class=${cn(selectTriggerClass(), 'w-38 **:data-[slot=select-value]:block **:data-[slot=select-value]:truncate')}>
|
|
107
|
+
<option value="" disabled hidden ?selected=${!picked}>Assign reviewer</option>
|
|
108
|
+
<option value="Eddie Lake" ?selected=${picked === 'Eddie Lake'}>Eddie Lake</option>
|
|
109
|
+
<option value="Jamik Tashpulatov" ?selected=${picked === 'Jamik Tashpulatov'}>Jamik Tashpulatov</option>
|
|
110
|
+
</select>
|
|
111
|
+
</ui-select>
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function row(r: DashboardRow, state: RowState) {
|
|
116
|
+
const { selected, edit, hidden } = state;
|
|
117
|
+
return html`
|
|
118
|
+
<tr data-slot="table-row" data-row-id=${String(r.id)} data-dragging="false" data-state=${selected ? 'selected' : null} class=${cn(tableRowClass(), 'relative z-0 data-[dragging=true]:z-10 data-[dragging=true]:opacity-80')}>
|
|
119
|
+
<td data-slot="table-cell" class=${cell()}>
|
|
120
|
+
<button type="button" draggable="true" data-drag-handle data-slot="button" data-variant="ghost" data-size="icon" class=${cn(buttonClass({ variant: 'ghost', size: 'icon' }), 'size-7 cursor-grab text-muted-foreground hover:bg-transparent')}>
|
|
121
|
+
${GripVerticalIcon('size-3 text-muted-foreground')}
|
|
122
|
+
<span class="sr-only">Drag to reorder</span>
|
|
123
|
+
</button>
|
|
124
|
+
</td>
|
|
125
|
+
<td data-slot="table-cell" class=${cell()}>
|
|
126
|
+
<div class="flex items-center justify-center">
|
|
127
|
+
<input type="checkbox" data-slot="checkbox" data-row-select class=${checkboxClass()} aria-label="Select row" .checked=${selected}>
|
|
128
|
+
</div>
|
|
129
|
+
</td>
|
|
130
|
+
<td data-slot="table-cell" class=${cell()}>
|
|
131
|
+
<button type="button" data-row-open data-slot="button" data-variant="link" class=${cn(buttonClass({ variant: 'link' }), 'w-fit px-0 text-left text-foreground')}>${r.header}</button>
|
|
132
|
+
</td>
|
|
133
|
+
<td data-slot="table-cell" data-column="type" class=${cell()} ?hidden=${hidden.has('type')}>
|
|
134
|
+
<div class="w-32">
|
|
135
|
+
<span data-slot="badge" data-variant="outline" class=${cn(badgeClass({ variant: 'outline' }), 'px-1.5 text-muted-foreground')}>${r.type}</span>
|
|
136
|
+
</div>
|
|
137
|
+
</td>
|
|
138
|
+
<td data-slot="table-cell" data-column="status" class=${cell()} ?hidden=${hidden.has('status')}>
|
|
139
|
+
<span data-slot="badge" data-variant="outline" class=${cn(badgeClass({ variant: 'outline' }), 'px-1.5 text-muted-foreground')}>
|
|
140
|
+
${r.status === 'Done' ? CircleCheckIcon('fill-green-500 dark:fill-green-400') : LoaderIcon()}
|
|
141
|
+
${r.status}
|
|
142
|
+
</span>
|
|
143
|
+
</td>
|
|
144
|
+
<td data-slot="table-cell" data-column="target" class=${cell()} ?hidden=${hidden.has('target')}>${inlineField(r, 'target', 'Target', edit)}</td>
|
|
145
|
+
<td data-slot="table-cell" data-column="limit" class=${cell()} ?hidden=${hidden.has('limit')}>${inlineField(r, 'limit', 'Limit', edit)}</td>
|
|
146
|
+
<td data-slot="table-cell" data-column="reviewer" class=${cell()} ?hidden=${hidden.has('reviewer')}>${reviewerCell(r, edit)}</td>
|
|
147
|
+
<td data-slot="table-cell" class=${cell()}>
|
|
148
|
+
<ui-dropdown-menu>
|
|
149
|
+
<ui-dropdown-menu-trigger>
|
|
150
|
+
<button type="button" data-slot="button" data-variant="ghost" data-size="icon" class=${cn(buttonClass({ variant: 'ghost', size: 'icon' }), 'flex size-8 text-muted-foreground aria-expanded:bg-muted')}>
|
|
151
|
+
${EllipsisVerticalIcon()}
|
|
152
|
+
<span class="sr-only">Open menu</span>
|
|
153
|
+
</button>
|
|
154
|
+
</ui-dropdown-menu-trigger>
|
|
155
|
+
<ui-dropdown-menu-content align="end" class="w-32">
|
|
156
|
+
<ui-dropdown-menu-item>Edit</ui-dropdown-menu-item>
|
|
157
|
+
<ui-dropdown-menu-item>Make a copy</ui-dropdown-menu-item>
|
|
158
|
+
<ui-dropdown-menu-item>Favorite</ui-dropdown-menu-item>
|
|
159
|
+
<ui-dropdown-menu-separator></ui-dropdown-menu-separator>
|
|
160
|
+
<ui-dropdown-menu-item variant="destructive">Delete</ui-dropdown-menu-item>
|
|
161
|
+
</ui-dropdown-menu-content>
|
|
162
|
+
</ui-dropdown-menu>
|
|
163
|
+
</td>
|
|
164
|
+
</tr>
|
|
165
|
+
`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** The body's rows, keyed by id so a reorder moves the row's own element. */
|
|
169
|
+
const bodyRows = (rows: DashboardRow[], stateOf: (r: DashboardRow) => RowState) =>
|
|
170
|
+
html`${repeat(rows, (r) => r.id, (r) => row(r, stateOf(r)))}`;
|
|
171
|
+
|
|
172
|
+
const pageButton = (action: string, label: string, icon: unknown, cls: string, disabled: boolean) =>
|
|
173
|
+
html`<button type="button" data-page=${action} data-slot="button" data-variant="outline" class=${cn(buttonClass({ variant: 'outline', size: 'icon' }), cls)} ?disabled=${disabled}>
|
|
174
|
+
<span class="sr-only">${label}</span>${icon}
|
|
175
|
+
</button>`;
|
|
176
|
+
|
|
177
|
+
// The drawer's chart, shadcn's TableCellViewer chart.
|
|
178
|
+
const drawerChartData = [
|
|
179
|
+
{ month: 'January', desktop: 186, mobile: 80 },
|
|
180
|
+
{ month: 'February', desktop: 305, mobile: 200 },
|
|
181
|
+
{ month: 'March', desktop: 237, mobile: 120 },
|
|
182
|
+
{ month: 'April', desktop: 73, mobile: 190 },
|
|
183
|
+
{ month: 'May', desktop: 209, mobile: 130 },
|
|
184
|
+
{ month: 'June', desktop: 214, mobile: 140 },
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
const drawerChartConfig = {
|
|
188
|
+
desktop: { label: 'Desktop', color: 'var(--primary)' },
|
|
189
|
+
mobile: { label: 'Mobile', color: 'var(--primary)' },
|
|
190
|
+
} satisfies ChartConfig;
|
|
191
|
+
|
|
192
|
+
function drawerSelect(id: string, label: string, placeholder: string, options: string[], current: string) {
|
|
193
|
+
return html`<div class="flex flex-col gap-3">
|
|
194
|
+
<label for=${id} data-slot="label" class=${labelClass()}>${label}</label>
|
|
195
|
+
<ui-select placeholder=${placeholder}>
|
|
196
|
+
<select id=${id} name=${id.replace('drawer-', '')} class=${cn(selectTriggerClass(), 'w-full')}>
|
|
197
|
+
<option value="" disabled hidden ?selected=${!options.includes(current)}>${placeholder}</option>
|
|
198
|
+
${options.map((o) => html`<option value=${o} ?selected=${o === current}>${o}</option>`)}
|
|
199
|
+
</select>
|
|
200
|
+
</ui-select>
|
|
201
|
+
</div>`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function drawerInput(id: string, label: string, value: string) {
|
|
205
|
+
return html`<div class="flex flex-col gap-3">
|
|
206
|
+
<label for=${id} data-slot="label" class=${labelClass()}>${label}</label>
|
|
207
|
+
<input id=${id} name=${id.replace('drawer-', '')} value=${value} data-slot="input" class=${inputClass()}>
|
|
208
|
+
</div>`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* shadcn's TableCellViewer, rendered once: every row's title opens this
|
|
213
|
+
* drawer, and the element fills it with that row's values first. shadcn hides
|
|
214
|
+
* the chart on a phone, where the drawer comes up from the bottom; here it
|
|
215
|
+
* always slides in from the right and the chart hides below md.
|
|
216
|
+
*/
|
|
217
|
+
function rowDrawer(first: DashboardRow) {
|
|
218
|
+
return html`
|
|
219
|
+
<ui-drawer data-row-drawer>
|
|
220
|
+
<ui-drawer-content direction="right">
|
|
221
|
+
<div data-slot="drawer-header" class=${cn(drawerHeaderClass(), 'gap-1')}>
|
|
222
|
+
<h2 data-slot="drawer-title" data-drawer-title class=${drawerTitleClass()}>${first.header}</h2>
|
|
223
|
+
<p data-slot="drawer-description" class=${drawerDescriptionClass()}>Showing total visitors for the last 6 months</p>
|
|
224
|
+
</div>
|
|
225
|
+
<div class="flex flex-col gap-4 overflow-y-auto px-4 text-sm">
|
|
226
|
+
<div class="hidden flex-col gap-4 md:flex">
|
|
227
|
+
${areaChart({
|
|
228
|
+
config: drawerChartConfig,
|
|
229
|
+
data: drawerChartData,
|
|
230
|
+
title: 'Desktop and mobile visitors, January to June',
|
|
231
|
+
margin: { left: 0, right: 10 },
|
|
232
|
+
grid: { vertical: false },
|
|
233
|
+
xAxis: { dataKey: 'month', tickLine: false, axisLine: false, tickMargin: 8, tickFormatter: (value) => String(value).slice(0, 3), hide: true },
|
|
234
|
+
tooltip: { cursor: false, indicator: 'dot' },
|
|
235
|
+
areas: [
|
|
236
|
+
{ dataKey: 'mobile', type: 'natural', fill: 'var(--color-mobile)', fillOpacity: 0.6, stroke: 'var(--color-mobile)', stackId: 'a' },
|
|
237
|
+
{ dataKey: 'desktop', type: 'natural', fill: 'var(--color-desktop)', fillOpacity: 0.4, stroke: 'var(--color-desktop)', stackId: 'a' },
|
|
238
|
+
],
|
|
239
|
+
})}
|
|
240
|
+
<div role="none" data-slot="separator" data-orientation="horizontal" class=${separatorClass()}></div>
|
|
241
|
+
<div class="grid gap-2">
|
|
242
|
+
<div class="flex gap-2 leading-none font-medium">Trending up by 5.2% this month ${TrendingUpIcon('size-4')}</div>
|
|
243
|
+
<div class="text-muted-foreground">Showing total visitors for the last 6 months. This is just some random text to test the layout. It spans multiple lines and should wrap around.</div>
|
|
244
|
+
</div>
|
|
245
|
+
<div role="none" data-slot="separator" data-orientation="horizontal" class=${separatorClass()}></div>
|
|
246
|
+
</div>
|
|
247
|
+
<form id="drawer-form" class="flex flex-col gap-4">
|
|
248
|
+
${drawerInput('drawer-header', 'Header', first.header)}
|
|
249
|
+
<div class="grid grid-cols-2 gap-4">
|
|
250
|
+
${drawerSelect('drawer-type', 'Type', 'Select a type', SECTION_TYPES, first.type)}
|
|
251
|
+
${drawerSelect('drawer-status', 'Status', 'Select a status', STATUSES, first.status)}
|
|
252
|
+
</div>
|
|
253
|
+
<div class="grid grid-cols-2 gap-4">
|
|
254
|
+
${drawerInput('drawer-target', 'Target', first.target)}
|
|
255
|
+
${drawerInput('drawer-limit', 'Limit', first.limit)}
|
|
256
|
+
</div>
|
|
257
|
+
${drawerSelect('drawer-reviewer', 'Reviewer', 'Select a reviewer', REVIEWERS, first.reviewer)}
|
|
258
|
+
</form>
|
|
259
|
+
</div>
|
|
260
|
+
<div data-slot="drawer-footer" class=${drawerFooterClass()}>
|
|
261
|
+
<button type="submit" form="drawer-form" data-slot="button" class=${buttonClass()}>Submit</button>
|
|
262
|
+
<ui-drawer-close><button type="button" data-slot="button" data-variant="outline" class=${cn(buttonClass({ variant: 'outline' }), 'w-full')}>Done</button></ui-drawer-close>
|
|
263
|
+
</div>
|
|
264
|
+
</ui-drawer-content>
|
|
265
|
+
</ui-drawer>
|
|
266
|
+
`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function dataTable({ data }: { data: DashboardRow[] }) {
|
|
270
|
+
const pageCount = Math.max(1, Math.ceil(data.length / PAGE_SIZES[0]));
|
|
271
|
+
return html`
|
|
272
|
+
<dashboard-01-data-table rows=${JSON.stringify(data)}>
|
|
273
|
+
<ui-tabs value="outline" class="w-full flex-col justify-start gap-6">
|
|
274
|
+
<div class="flex items-center justify-between px-4 lg:px-6">
|
|
275
|
+
<label for="view-selector" data-slot="label" class=${cn(labelClass(), 'sr-only')}>View</label>
|
|
276
|
+
<ui-select size="sm" placeholder="Select a view">
|
|
277
|
+
<select id="view-selector" data-view-select class=${cn(selectTriggerClass(), 'flex w-fit @4xl/main:hidden')}>
|
|
278
|
+
${VIEWS.map((v) => html`<option value=${v.value} ?selected=${v.value === 'outline'}>${v.label}</option>`)}
|
|
279
|
+
</select>
|
|
280
|
+
</ui-select>
|
|
281
|
+
<ui-tabs-list class="hidden **:data-[slot=badge]:size-5 **:data-[slot=badge]:rounded-full **:data-[slot=badge]:bg-muted-foreground/30 **:data-[slot=badge]:px-1 @4xl/main:flex">
|
|
282
|
+
${VIEWS.map((v) => html`<ui-tabs-trigger value=${v.value}>${v.label}${v.count ? html` <span data-slot="badge" data-variant="secondary" class=${badgeClass({ variant: 'secondary' })}>${v.count}</span>` : ''}</ui-tabs-trigger>`)}
|
|
283
|
+
</ui-tabs-list>
|
|
284
|
+
<div class="flex items-center gap-2">
|
|
285
|
+
<ui-dropdown-menu>
|
|
286
|
+
<ui-dropdown-menu-trigger>
|
|
287
|
+
<button type="button" data-slot="button" data-variant="outline" data-size="sm" class=${buttonClass({ variant: 'outline', size: 'sm' })}>
|
|
288
|
+
${Columns3Icon()}
|
|
289
|
+
Columns
|
|
290
|
+
${ChevronDownIcon()}
|
|
291
|
+
</button>
|
|
292
|
+
</ui-dropdown-menu-trigger>
|
|
293
|
+
<ui-dropdown-menu-content align="end" class="w-32">
|
|
294
|
+
${HIDEABLE.map((c) => html`<ui-dropdown-menu-item type="checkbox" value=${c} checked data-column-toggle class="capitalize">${c}</ui-dropdown-menu-item>`)}
|
|
295
|
+
</ui-dropdown-menu-content>
|
|
296
|
+
</ui-dropdown-menu>
|
|
297
|
+
<button type="button" data-slot="button" data-variant="outline" data-size="sm" class=${buttonClass({ variant: 'outline', size: 'sm' })}>
|
|
298
|
+
${PlusIcon()}
|
|
299
|
+
<span class="hidden lg:inline">Add Section</span>
|
|
300
|
+
</button>
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
<ui-tabs-content value="outline" class="relative flex flex-col gap-4 overflow-auto px-4 lg:px-6">
|
|
304
|
+
<div class="overflow-hidden rounded-lg border">
|
|
305
|
+
<div data-slot="table-container" class=${tableContainerClass()}>
|
|
306
|
+
<table data-slot="table" class=${tableClass()}>
|
|
307
|
+
<thead data-slot="table-header" class=${cn(tableHeaderClass(), 'sticky top-0 z-10 bg-muted')}>
|
|
308
|
+
<tr data-slot="table-row" class=${tableRowClass()}>
|
|
309
|
+
<th scope="col" data-slot="table-head" class=${head()}><span class="sr-only">Drag to reorder</span></th>
|
|
310
|
+
<th scope="col" data-slot="table-head" class=${head()}>
|
|
311
|
+
<div class="flex items-center justify-center">
|
|
312
|
+
<input type="checkbox" data-slot="checkbox" data-select-all class=${checkboxClass()} aria-label="Select all">
|
|
313
|
+
</div>
|
|
314
|
+
</th>
|
|
315
|
+
<th scope="col" data-slot="table-head" class=${head()}>Header</th>
|
|
316
|
+
<th scope="col" data-slot="table-head" data-column="type" class=${head()}>Section Type</th>
|
|
317
|
+
<th scope="col" data-slot="table-head" data-column="status" class=${head()}>Status</th>
|
|
318
|
+
<th scope="col" data-slot="table-head" data-column="target" class=${head()}><div class="w-full text-right">Target</div></th>
|
|
319
|
+
<th scope="col" data-slot="table-head" data-column="limit" class=${head()}><div class="w-full text-right">Limit</div></th>
|
|
320
|
+
<th scope="col" data-slot="table-head" data-column="reviewer" class=${head()}>Reviewer</th>
|
|
321
|
+
<th scope="col" data-slot="table-head" class=${head()}><span class="sr-only">Actions</span></th>
|
|
322
|
+
</tr>
|
|
323
|
+
</thead>
|
|
324
|
+
<tbody data-slot="table-body" data-rows class=${cn(tableBodyClass(), '**:data-[slot=table-cell]:first:w-8')}>
|
|
325
|
+
${data.length
|
|
326
|
+
? bodyRows(data.slice(0, PAGE_SIZES[0]), () => FIRST_PAINT)
|
|
327
|
+
: html`<tr data-slot="table-row" class=${tableRowClass()}><td colspan="9" data-slot="table-cell" class=${cell('h-24 text-center')}>No results.</td></tr>`}
|
|
328
|
+
</tbody>
|
|
329
|
+
</table>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
<div class="flex items-center justify-between px-4">
|
|
333
|
+
<div class="hidden flex-1 text-sm text-muted-foreground lg:flex" aria-live="polite">
|
|
334
|
+
<span><span data-selected-count>0</span> of <span data-row-count>${data.length}</span> row(s) selected.</span>
|
|
335
|
+
</div>
|
|
336
|
+
<div class="flex w-full items-center gap-8 lg:w-fit">
|
|
337
|
+
<div class="hidden items-center gap-2 lg:flex">
|
|
338
|
+
<label for="rows-per-page" data-slot="label" class=${cn(labelClass(), 'text-sm font-medium')}>Rows per page</label>
|
|
339
|
+
<ui-select size="sm">
|
|
340
|
+
<select id="rows-per-page" data-page-size class=${cn(selectTriggerClass(), 'w-20')}>
|
|
341
|
+
${PAGE_SIZES.map((n) => html`<option value=${String(n)} ?selected=${n === PAGE_SIZES[0]}>${n}</option>`)}
|
|
342
|
+
</select>
|
|
343
|
+
</ui-select>
|
|
344
|
+
</div>
|
|
345
|
+
<div class="flex w-fit items-center justify-center text-sm font-medium" data-page-label>Page 1 of ${pageCount}</div>
|
|
346
|
+
<div class="ml-auto flex items-center gap-2 lg:ml-0">
|
|
347
|
+
${pageButton('first', 'Go to first page', ChevronsLeftIcon(), 'hidden h-8 w-8 p-0 lg:flex', true)}
|
|
348
|
+
${pageButton('previous', 'Go to previous page', ChevronLeftIcon(), 'size-8', true)}
|
|
349
|
+
${pageButton('next', 'Go to next page', ChevronRightIcon(), 'size-8', pageCount <= 1)}
|
|
350
|
+
${pageButton('last', 'Go to last page', ChevronsRightIcon(), 'hidden size-8 lg:flex', pageCount <= 1)}
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
</ui-tabs-content>
|
|
355
|
+
${VIEWS.slice(1).map((v) => html`<ui-tabs-content value=${v.value} class="flex flex-col px-4 lg:px-6">
|
|
356
|
+
<div class="aspect-video w-full flex-1 rounded-lg border border-dashed"></div>
|
|
357
|
+
</ui-tabs-content>`)}
|
|
358
|
+
</ui-tabs>
|
|
359
|
+
${data.length ? rowDrawer(data[0]) : ''}
|
|
360
|
+
</dashboard-01-data-table>
|
|
361
|
+
`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
type Valued = HTMLElement & { value: string };
|
|
365
|
+
type Openable = HTMLElement & { open: boolean };
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* `<dashboard-01-data-table>`: the table's behaviour. It holds every row (the
|
|
369
|
+
* `rows` attribute the server writes) in the order the reader set, with the
|
|
370
|
+
* selection and the edited cells, and renders the current page's rows into
|
|
371
|
+
* the body on each page, page-size, reorder, selection and column change, so
|
|
372
|
+
* the document only ever holds one page.
|
|
373
|
+
*/
|
|
374
|
+
export class Dashboard01DataTable extends WebComponent({
|
|
375
|
+
rows: prop<DashboardRow[]>(Array),
|
|
376
|
+
}) {
|
|
377
|
+
_pageIndex = 0;
|
|
378
|
+
_pageSize = PAGE_SIZES[0];
|
|
379
|
+
_order: DashboardRow[] | null = null;
|
|
380
|
+
_selected = new Set<number>();
|
|
381
|
+
_edits = new Map<number, RowEdit>();
|
|
382
|
+
_hidden = new Set<string>();
|
|
383
|
+
_dragged: number | null = null;
|
|
384
|
+
|
|
385
|
+
constructor() {
|
|
386
|
+
super();
|
|
387
|
+
this.rows = [];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
connectedCallback() {
|
|
391
|
+
super.connectedCallback();
|
|
392
|
+
this.addEventListener('change', this._onChange);
|
|
393
|
+
this.addEventListener('input', this._onInput);
|
|
394
|
+
this.addEventListener('click', this._onClick);
|
|
395
|
+
this.addEventListener('ui-item-select', this._onItemSelect);
|
|
396
|
+
this.addEventListener('ui-value-change', this._onTabs);
|
|
397
|
+
this.addEventListener('dragstart', this._onDragStart);
|
|
398
|
+
this.addEventListener('dragover', this._onDragOver);
|
|
399
|
+
this.addEventListener('dragend', this._onDragEnd);
|
|
400
|
+
this.addEventListener('keydown', this._onKeyDown);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
disconnectedCallback() {
|
|
404
|
+
this.removeEventListener('change', this._onChange);
|
|
405
|
+
this.removeEventListener('input', this._onInput);
|
|
406
|
+
this.removeEventListener('click', this._onClick);
|
|
407
|
+
this.removeEventListener('ui-item-select', this._onItemSelect);
|
|
408
|
+
this.removeEventListener('ui-value-change', this._onTabs);
|
|
409
|
+
this.removeEventListener('dragstart', this._onDragStart);
|
|
410
|
+
this.removeEventListener('dragover', this._onDragOver);
|
|
411
|
+
this.removeEventListener('dragend', this._onDragEnd);
|
|
412
|
+
this.removeEventListener('keydown', this._onKeyDown);
|
|
413
|
+
super.disconnectedCallback();
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** Every row, in the reader's order. */
|
|
417
|
+
_all(): DashboardRow[] {
|
|
418
|
+
return (this._order ??= [...(this.rows || [])]);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
_pageCount(): number {
|
|
422
|
+
return Math.max(1, Math.ceil(this._all().length / this._pageSize));
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
_pageRows(): DashboardRow[] {
|
|
426
|
+
const start = this._pageIndex * this._pageSize;
|
|
427
|
+
return this._all().slice(start, start + this._pageSize);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
_rowId(el: Element | null): number | null {
|
|
431
|
+
const id = el?.closest('tr[data-row-id]')?.getAttribute('data-row-id');
|
|
432
|
+
return id == null ? null : Number(id);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
_stateOf = (r: DashboardRow): RowState => ({
|
|
436
|
+
selected: this._selected.has(r.id),
|
|
437
|
+
edit: this._edits.get(r.id) ?? {},
|
|
438
|
+
hidden: this._hidden,
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
/** Render the current page's rows, then the pager and the selection text. */
|
|
442
|
+
_update(): void {
|
|
443
|
+
const all = this._all();
|
|
444
|
+
const pageCount = this._pageCount();
|
|
445
|
+
this._pageIndex = Math.min(Math.max(0, this._pageIndex), pageCount - 1);
|
|
446
|
+
const body = this.querySelector<HTMLElement>('tbody[data-rows]');
|
|
447
|
+
if (body && all.length) render(bodyRows(this._pageRows(), this._stateOf), body);
|
|
448
|
+
const label = this.querySelector('[data-page-label]');
|
|
449
|
+
if (label) label.textContent = `Page ${this._pageIndex + 1} of ${pageCount}`;
|
|
450
|
+
const atStart = this._pageIndex === 0;
|
|
451
|
+
const atEnd = this._pageIndex >= pageCount - 1;
|
|
452
|
+
for (const b of Array.from(this.querySelectorAll<HTMLButtonElement>('button[data-page]'))) {
|
|
453
|
+
b.disabled = b.dataset.page === 'first' || b.dataset.page === 'previous' ? atStart : atEnd;
|
|
454
|
+
}
|
|
455
|
+
const count = this.querySelector('[data-selected-count]');
|
|
456
|
+
if (count) count.textContent = String(this._selected.size);
|
|
457
|
+
const total = this.querySelector('[data-row-count]');
|
|
458
|
+
if (total) total.textContent = String(all.length);
|
|
459
|
+
const box = this.querySelector<HTMLInputElement>('input[data-select-all]');
|
|
460
|
+
if (box) {
|
|
461
|
+
const page = this._pageRows().map((r) => this._selected.has(r.id));
|
|
462
|
+
box.checked = page.length > 0 && page.every(Boolean);
|
|
463
|
+
box.indeterminate = !box.checked && page.some(Boolean);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
_onChange = (e: Event): void => {
|
|
468
|
+
const t = e.target as HTMLInputElement | HTMLSelectElement;
|
|
469
|
+
if (t.matches('input[data-select-all]')) {
|
|
470
|
+
for (const r of this._pageRows()) {
|
|
471
|
+
if ((t as HTMLInputElement).checked) this._selected.add(r.id);
|
|
472
|
+
else this._selected.delete(r.id);
|
|
473
|
+
}
|
|
474
|
+
this._update();
|
|
475
|
+
} else if (t.matches('input[data-row-select]')) {
|
|
476
|
+
const id = this._rowId(t);
|
|
477
|
+
if (id != null) {
|
|
478
|
+
if ((t as HTMLInputElement).checked) this._selected.add(id);
|
|
479
|
+
else this._selected.delete(id);
|
|
480
|
+
}
|
|
481
|
+
this._update();
|
|
482
|
+
} else if (t.matches('select[data-row-edit]')) {
|
|
483
|
+
this._edit(t);
|
|
484
|
+
} else if (t.matches('select[data-page-size]')) {
|
|
485
|
+
this._pageSize = Number(t.value) || PAGE_SIZES[0];
|
|
486
|
+
this._pageIndex = 0;
|
|
487
|
+
this._update();
|
|
488
|
+
} else if (t.matches('select[data-view-select]')) {
|
|
489
|
+
const tabs = this.querySelector('ui-tabs') as Valued | null;
|
|
490
|
+
if (tabs && tabs.value !== t.value) tabs.value = t.value;
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
// An edited cell is kept with its row, so it holds across pages.
|
|
495
|
+
_onInput = (e: Event): void => {
|
|
496
|
+
const t = e.target as HTMLInputElement;
|
|
497
|
+
if (t.matches?.('input[data-row-edit]')) this._edit(t);
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
_edit(field: HTMLInputElement | HTMLSelectElement): void {
|
|
501
|
+
const id = this._rowId(field);
|
|
502
|
+
const key = field.dataset.rowEdit as keyof RowEdit | undefined;
|
|
503
|
+
if (id == null || !key) return;
|
|
504
|
+
this._edits.set(id, { ...this._edits.get(id), [key]: field.value });
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// A tab picked on a wide card moves the narrow card's view select with it.
|
|
508
|
+
_onTabs = (e: Event): void => {
|
|
509
|
+
if ((e.target as Element).tagName !== 'UI-TABS') return;
|
|
510
|
+
const value = String((e as CustomEvent<{ value: string }>).detail?.value ?? '');
|
|
511
|
+
const select = this.querySelector<HTMLSelectElement>('select[data-view-select]');
|
|
512
|
+
if (select && value && select.value !== value) {
|
|
513
|
+
select.value = value;
|
|
514
|
+
select.dispatchEvent(new Event('change', { bubbles: true }));
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
_onClick = (e: Event): void => {
|
|
519
|
+
const button = (e.target as Element).closest?.('button');
|
|
520
|
+
if (!button || !this.contains(button)) return;
|
|
521
|
+
const page = button.dataset.page;
|
|
522
|
+
if (page) {
|
|
523
|
+
const last = this._pageCount() - 1;
|
|
524
|
+
this._pageIndex = page === 'first' ? 0 : page === 'previous' ? this._pageIndex - 1 : page === 'next' ? this._pageIndex + 1 : last;
|
|
525
|
+
this._update();
|
|
526
|
+
} else if (button.hasAttribute('data-row-open')) {
|
|
527
|
+
this._openDrawer(button.closest('tr'));
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
// The columns menu: each checkbox item shows or hides its column, the
|
|
532
|
+
// header cell here and the body cells on every render.
|
|
533
|
+
_onItemSelect = (e: Event): void => {
|
|
534
|
+
const item = e.target as HTMLElement;
|
|
535
|
+
if (!item.hasAttribute('data-column-toggle')) return;
|
|
536
|
+
const { value, checked } = (e as CustomEvent<{ value: string; checked: boolean }>).detail;
|
|
537
|
+
if (checked) this._hidden.delete(value);
|
|
538
|
+
else this._hidden.add(value);
|
|
539
|
+
for (const c of Array.from(this.querySelectorAll<HTMLElement>(`[data-column="${value}"]`))) c.hidden = !checked;
|
|
540
|
+
this._update();
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
/** Fill the shared drawer with one row's values and open it. */
|
|
544
|
+
_openDrawer(row: HTMLTableRowElement | null): void {
|
|
545
|
+
const drawer = this.querySelector('ui-drawer[data-row-drawer]') as Openable | null;
|
|
546
|
+
const id = this._rowId(row);
|
|
547
|
+
const r = this._all().find((x) => x.id === id);
|
|
548
|
+
if (!r || !drawer) return;
|
|
549
|
+
const edit = this._edits.get(r.id) ?? {};
|
|
550
|
+
const values: Record<string, string> = {
|
|
551
|
+
'drawer-header': r.header,
|
|
552
|
+
'drawer-type': r.type,
|
|
553
|
+
'drawer-status': r.status,
|
|
554
|
+
'drawer-target': edit.target ?? r.target,
|
|
555
|
+
'drawer-limit': edit.limit ?? r.limit,
|
|
556
|
+
'drawer-reviewer': edit.reviewer ?? (r.reviewer === 'Assign reviewer' ? '' : r.reviewer),
|
|
557
|
+
};
|
|
558
|
+
const title = drawer.querySelector('[data-drawer-title]');
|
|
559
|
+
if (title) title.textContent = r.header;
|
|
560
|
+
for (const [field, value] of Object.entries(values)) {
|
|
561
|
+
const el = drawer.querySelector<HTMLInputElement | HTMLSelectElement>(`#${field}`);
|
|
562
|
+
if (!el) continue;
|
|
563
|
+
el.value = value;
|
|
564
|
+
if (el instanceof HTMLSelectElement) el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
565
|
+
}
|
|
566
|
+
drawer.open = true;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/** Move the row with id `id` to `index` in the full order and re-render. */
|
|
570
|
+
_move(id: number, index: number): void {
|
|
571
|
+
const all = this._all();
|
|
572
|
+
const from = all.findIndex((r) => r.id === id);
|
|
573
|
+
if (from < 0) return;
|
|
574
|
+
const to = Math.min(Math.max(0, index), all.length - 1);
|
|
575
|
+
if (from === to) return;
|
|
576
|
+
const [moved] = all.splice(from, 1);
|
|
577
|
+
all.splice(to, 0, moved);
|
|
578
|
+
this._update();
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
_onDragStart = (e: DragEvent): void => {
|
|
582
|
+
const handle = (e.target as Element).closest?.('[data-drag-handle]');
|
|
583
|
+
const row = handle?.closest('tr') as HTMLTableRowElement | null;
|
|
584
|
+
const id = this._rowId(row);
|
|
585
|
+
if (!row || id == null) return;
|
|
586
|
+
this._dragged = id;
|
|
587
|
+
row.dataset.dragging = 'true';
|
|
588
|
+
if (e.dataTransfer) {
|
|
589
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
590
|
+
e.dataTransfer.setData('text/plain', String(id));
|
|
591
|
+
e.dataTransfer.setDragImage(row, 16, 16);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// Rows move within the body only, the vertical-axis restriction shadcn sets.
|
|
596
|
+
_onDragOver = (e: DragEvent): void => {
|
|
597
|
+
const dragged = this._dragged;
|
|
598
|
+
const over = (e.target as Element).closest?.('tbody[data-rows] > tr[data-row-id]') as HTMLTableRowElement | null;
|
|
599
|
+
const overId = this._rowId(over);
|
|
600
|
+
if (dragged == null || !over || overId == null) return;
|
|
601
|
+
e.preventDefault();
|
|
602
|
+
if (overId === dragged) return;
|
|
603
|
+
const box = over.getBoundingClientRect();
|
|
604
|
+
const all = this._all();
|
|
605
|
+
const from = all.findIndex((r) => r.id === dragged);
|
|
606
|
+
let to = all.findIndex((r) => r.id === overId);
|
|
607
|
+
if (e.clientY > box.top + box.height / 2) to += 1;
|
|
608
|
+
if (from < to) to -= 1;
|
|
609
|
+
this._move(dragged, to);
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
_onDragEnd = (): void => {
|
|
613
|
+
const row = this._dragged == null ? null : this.querySelector<HTMLElement>(`tr[data-row-id="${this._dragged}"]`);
|
|
614
|
+
if (row) row.dataset.dragging = 'false';
|
|
615
|
+
this._dragged = null;
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
// The keyboard way to reorder: the arrow keys move the row whose handle has
|
|
619
|
+
// focus, as dnd-kit's keyboard sensor does, across pages too.
|
|
620
|
+
_onKeyDown = (e: KeyboardEvent): void => {
|
|
621
|
+
if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
|
|
622
|
+
const handle = (e.target as Element).closest?.('[data-drag-handle]') as HTMLElement | null;
|
|
623
|
+
const id = this._rowId(handle);
|
|
624
|
+
if (!handle || id == null) return;
|
|
625
|
+
e.preventDefault();
|
|
626
|
+
const index = this._all().findIndex((r) => r.id === id) + (e.key === 'ArrowUp' ? -1 : 1);
|
|
627
|
+
if (index < 0 || index >= this._all().length) return;
|
|
628
|
+
this._pageIndex = Math.floor(index / this._pageSize);
|
|
629
|
+
this._move(id, index);
|
|
630
|
+
this.querySelector<HTMLElement>(`tr[data-row-id="${id}"] [data-drag-handle]`)?.focus();
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
render() {
|
|
634
|
+
return html`<slot></slot>`;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
Dashboard01DataTable.register('dashboard-01-data-table');
|