@espressif/dashboard-ui-components 1.10.0 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +75 -0
- package/dist/_vendor/@radix-ui/primitive/dist/index.js +9 -0
- package/dist/_vendor/@radix-ui/react-checkbox/dist/index.js +185 -0
- package/dist/_vendor/@radix-ui/react-compose-refs/dist/index.js +28 -0
- package/dist/_vendor/@radix-ui/react-context/dist/index.js +68 -0
- package/dist/_vendor/@radix-ui/react-presence/dist/index.js +127 -0
- package/dist/_vendor/@radix-ui/react-primitive/dist/index.js +42 -0
- package/dist/_vendor/@radix-ui/react-use-controllable-state/dist/index.js +55 -0
- package/dist/_vendor/@radix-ui/react-use-layout-effect/dist/index.js +5 -0
- package/dist/_vendor/@radix-ui/react-use-previous/dist/index.js +17 -0
- package/dist/_vendor/@radix-ui/react-use-size/dist/index.js +39 -0
- package/dist/_vendor/lottie-react/build/index.es.js +537 -0
- package/dist/_vendor/lottie-web/build/player/lottie.js +14149 -0
- package/dist/_virtual/_rolldown/runtime.js +24 -0
- package/dist/assets/illustrations/bulb-cloud-drawing.js +4 -0
- package/dist/assets/illustrations/rm-dashboard-update.js +4 -0
- package/dist/assets/illustrations/rm-data-download.js +4 -0
- package/dist/assets/logos/espressif/espressif-no-text.js +4 -0
- package/dist/assets/logos/espressif/espressif-with-text-dm.js +4 -0
- package/dist/assets/logos/espressif/espressif-with-text.js +4 -0
- package/dist/assets/logos/insights/insights-no-text.js +4 -0
- package/dist/assets/logos/insights/insights-with-text-dm.js +4 -0
- package/dist/assets/logos/insights/insights-with-text.js +4 -0
- package/dist/assets/logos/launchpad/launchpad-no-text-dm.js +4 -0
- package/dist/assets/logos/launchpad/launchpad-no-text.js +4 -0
- package/dist/assets/logos/launchpad/launchpad-with-text-dm.js +4 -0
- package/dist/assets/logos/launchpad/launchpad-with-text.js +4 -0
- package/dist/assets/logos/private-agents/private-agents-no-text.js +4 -0
- package/dist/assets/logos/private-agents/private-agents-with-text-dm.js +4 -0
- package/dist/assets/logos/private-agents/private-agents-with-text.js +4 -0
- package/dist/assets/logos/rainmaker/rainmaker-no-text.js +4 -0
- package/dist/assets/logos/rainmaker/rainmaker-with-text-dm.js +4 -0
- package/dist/assets/logos/rainmaker/rainmaker-with-text.js +4 -0
- package/dist/assets/logos/rainmaker-classic/rainmaker-classic-no-text-dm.js +4 -0
- package/dist/assets/logos/rainmaker-classic/rainmaker-classic-no-text.js +4 -0
- package/dist/assets/logos/rainmaker-classic/rainmaker-classic-with-text-dm.js +4 -0
- package/dist/assets/logos/rainmaker-classic/rainmaker-classic-with-text.js +4 -0
- package/dist/assets/logos/rainmaker-neo/rainmaker-neo-no-text.js +4 -0
- package/dist/assets/logos/rainmaker-neo/rainmaker-neo-with-text-dm.js +4 -0
- package/dist/assets/logos/rainmaker-neo/rainmaker-neo-with-text.js +4 -0
- package/dist/assets/logos/ui-components/ui-components-no-text.js +4 -0
- package/dist/assets/logos/ui-components/ui-components-with-text-dm.js +4 -0
- package/dist/assets/logos/ui-components/ui-components-with-text.js +4 -0
- package/dist/assets/logos/zerocode/zerocode-no-text.js +4 -0
- package/dist/assets/logos/zerocode/zerocode-with-text-dm.js +4 -0
- package/dist/assets/logos/zerocode/zerocode-with-text.js +4 -0
- package/dist/assets/logos/zerocode-ai/zerocode-ai-no-text.js +4 -0
- package/dist/assets/logos/zerocode-ai/zerocode-ai-with-text-dm.js +4 -0
- package/dist/assets/logos/zerocode-ai/zerocode-ai-with-text.js +4 -0
- package/dist/common/animated-rainmaker-logo/animated-rainmaker-logo.constants.js +18 -0
- package/dist/common/animated-rainmaker-logo/animated-rainmaker-logo.js +24 -0
- package/dist/common/animated-rainmaker-logo/rainmaker-c-logo.js +50 -0
- package/dist/common/animated-rainmaker-logo/use-animated-rainmaker-logo-animation.js +85 -0
- package/dist/common/app-logo/app-logo.js +59 -0
- package/dist/common/app-logo/logo-config.js +297 -0
- package/dist/common/copyright-card/copyright-card.js +39 -0
- package/dist/common/footer-card/footer-card.js +22 -0
- package/dist/common/index.js +8 -8
- package/dist/common/my-account-menu/my-account-menu.js +32 -0
- package/dist/common/page-loader/page-loader.js +21 -0
- package/dist/common/profile-card/profile-card.js +26 -0
- package/dist/components/accordion/accordion.js +52 -0
- package/dist/components/advanced-search-box/advanced-search-box.js +106 -0
- package/dist/components/alert/alert.js +85 -0
- package/dist/components/animated-containers/pop-collection-item.js +28 -0
- package/dist/components/async-multi-select/async-multi-select.js +199 -0
- package/dist/components/avatar/avatar.js +41 -0
- package/dist/components/button/button.js +191 -0
- package/dist/components/button/flash-button.js +110 -0
- package/dist/components/button/invert-button.js +13 -0
- package/dist/components/calendar/calendar.js +105 -0
- package/dist/components/cards/animated-card/animated-card-animations.js +57 -0
- package/dist/components/cards/animated-card/animated-card.js +42 -0
- package/dist/components/cards/basic-details-card.js +46 -0
- package/dist/components/cards/collapsible-card.js +48 -0
- package/dist/components/cards/no-data-card/no-data-card.js +30 -0
- package/dist/components/cards/section-card.js +265 -0
- package/dist/components/cards/simple-card.js +24 -0
- package/dist/components/carousel/carousel.js +158 -0
- package/dist/components/chart/area-chart.js +135 -0
- package/dist/components/chart/bar-chart.js +151 -0
- package/dist/components/chart/chart-colors.js +85 -0
- package/dist/components/chart/chart-series-data.js +53 -0
- package/dist/components/chart/chart.js +147 -0
- package/dist/components/chart/chart.types.js +7 -0
- package/dist/components/chart/composed-chart.js +182 -0
- package/dist/components/chart/donut-chart.js +177 -0
- package/dist/components/chart/line-chart.js +137 -0
- package/dist/components/chart/treemap-chart.js +294 -0
- package/dist/components/circular-progress/circular-progress.js +125 -0
- package/dist/components/code-editor/code-editor.js +235 -0
- package/dist/components/code-editor/code-editor.utils.js +122 -0
- package/dist/components/code-editor/use-code-editor-keys.js +79 -0
- package/dist/components/code-editor/use-highlighted-html.js +102 -0
- package/dist/components/confirmation-dialog/confirmation-dialog.js +72 -0
- package/dist/components/content-container/content-container.js +20 -0
- package/dist/components/copiable-text/copiable-text.js +46 -0
- package/dist/components/data-table/data-table.js +186 -0
- package/dist/components/data-table/responsive-columns.js +65 -0
- package/dist/components/data-table/row-actions.js +27 -0
- package/dist/components/data-table/sticky-columns.js +71 -0
- package/dist/components/data-table/table-body.js +57 -0
- package/dist/components/data-table/table-header.js +21 -0
- package/dist/components/date-picker/date-picker.js +95 -0
- package/dist/components/date-range-picker/date-range-picker.js +301 -0
- package/dist/components/date-range-picker/presets.js +174 -0
- package/dist/components/date-time-picker/date-time-picker.js +144 -0
- package/dist/components/date-time-picker/date-time-picker.utils.js +32 -0
- package/dist/components/device-icon/device-icon.config.js +194 -0
- package/dist/components/device-icon/device-icon.js +17 -0
- package/dist/components/device-icon/icons/air-conditioner.js +22 -0
- package/dist/components/device-icon/icons/blind.js +24 -0
- package/dist/components/device-icon/icons/dimmer.js +14 -0
- package/dist/components/device-icon/icons/extension-board.js +42 -0
- package/dist/components/device-icon/icons/fan.js +14 -0
- package/dist/components/device-icon/icons/knob.js +14 -0
- package/dist/components/device-icon/icons/led-strip.js +22 -0
- package/dist/components/device-icon/icons/light.js +14 -0
- package/dist/components/device-icon/icons/refrigerator.js +18 -0
- package/dist/components/device-icon/icons/retro-switch.js +18 -0
- package/dist/components/device-icon/icons/router.js +20 -0
- package/dist/components/device-icon/icons/sensor.js +20 -0
- package/dist/components/device-icon/icons/socket.js +19 -0
- package/dist/components/device-icon/icons/switch.js +19 -0
- package/dist/components/device-icon/icons/thermostat.js +21 -0
- package/dist/components/device-icon/icons/washing-machine.js +22 -0
- package/dist/components/device-icon/match-device.js +61 -0
- package/dist/components/dynamic-list/dynamic-list.js +445 -0
- package/dist/components/error-display/full-size-error/full-size-error.js +20 -0
- package/dist/components/error-display/inline-error/inline-error.js +12 -0
- package/dist/components/file-input/file-input.js +318 -0
- package/dist/components/file-upload/file-upload.js +317 -0
- package/dist/components/filters-bar/filters-bar.js +36 -0
- package/dist/components/fullscreen-dialog/fullscreen-dialog.js +68 -0
- package/dist/components/icon-avatar/icon-avatar.js +130 -0
- package/dist/components/icon-text-action-card/icon-text-action-card.js +190 -0
- package/dist/components/illustration/illustration-registry.js +37 -0
- package/dist/components/illustration/illustration-variants.js +21 -0
- package/dist/components/illustration/illustration.js +68 -0
- package/dist/components/index.d.ts +0 -20
- package/dist/components/index.js +134 -6
- package/dist/components/key-value-manager/key-value-manager-add-popover.js +126 -0
- package/dist/components/key-value-manager/key-value-manager-field.js +47 -0
- package/dist/components/key-value-manager/key-value-manager-table.js +188 -0
- package/dist/components/key-value-manager/key-value-manager.js +111 -0
- package/dist/components/key-value-manager/key-value-manager.props.js +28 -0
- package/dist/components/key-value-manager/key-value-manager.utils.js +21 -0
- package/dist/components/link/link.js +67 -0
- package/dist/components/list/dropdown-list-item.js +60 -0
- package/dist/components/list/list-item.utils.js +76 -0
- package/dist/components/list/list.js +49 -0
- package/dist/components/list/list.props.js +27 -0
- package/dist/components/list/standard-list-item.js +66 -0
- package/dist/components/list-details/list-details-item-button.js +46 -0
- package/dist/components/list-details/list-details-item-dropdown.js +112 -0
- package/dist/components/list-details/list-details-item-strip.js +125 -0
- package/dist/components/list-details/list-details.js +148 -0
- package/dist/components/lottie-animation-container/lottie-animation-container.js +31 -0
- package/dist/components/lottie-animations/animation-data/access-denied.js +3980 -0
- package/dist/components/lottie-animations/animation-data/active.js +825 -0
- package/dist/components/lottie-animations/animation-data/bar-chart.js +610 -0
- package/dist/components/lottie-animations/animation-data/chart-loading.js +5368 -0
- package/dist/components/lottie-animations/animation-data/cloud-download.js +1660 -0
- package/dist/components/lottie-animations/animation-data/cloud-sync.js +932 -0
- package/dist/components/lottie-animations/animation-data/coud-upload.js +7060 -0
- package/dist/components/lottie-animations/animation-data/cross-throb.js +1506 -0
- package/dist/components/lottie-animations/animation-data/dashboard-loading.js +14330 -0
- package/dist/components/lottie-animations/animation-data/error-spread-out.js +5567 -0
- package/dist/components/lottie-animations/animation-data/error.js +1211 -0
- package/dist/components/lottie-animations/animation-data/green-success.js +14826 -0
- package/dist/components/lottie-animations/animation-data/inactive.js +723 -0
- package/dist/components/lottie-animations/animation-data/light.js +2678 -0
- package/dist/components/lottie-animations/animation-data/lock.js +900 -0
- package/dist/components/lottie-animations/animation-data/lock2.js +1686 -0
- package/dist/components/lottie-animations/animation-data/locked.js +641 -0
- package/dist/components/lottie-animations/animation-data/search-error.js +954 -0
- package/dist/components/lottie-animations/animation-data/security.js +4121 -0
- package/dist/components/lottie-animations/animation-data/success.js +404 -0
- package/dist/components/lottie-animations/animation-data/verification-in-progress.js +10138 -0
- package/dist/components/lottie-animations/animation-data/warning.js +843 -0
- package/dist/components/lottie-animations/verification-in-progress/verification-in-progress.js +19 -0
- package/dist/components/markdown-content/markdown-content.components.js +114 -0
- package/dist/components/markdown-content/markdown-content.js +39 -0
- package/dist/components/menu/menu.js +19 -0
- package/dist/components/monospace-content/monospace-content.js +80 -0
- package/dist/components/nav-bar/nav-bar-details-panel.js +51 -0
- package/dist/components/nav-bar/nav-bar-mobile-menu.js +105 -0
- package/dist/components/nav-bar/nav-bar.js +449 -0
- package/dist/components/nav-bar/use-nav-bar-overflow.js +74 -0
- package/dist/components/nav-bar/use-nav-bar-scroll.js +42 -0
- package/dist/components/overflow-badge-list/overflow-badge-list.js +58 -0
- package/dist/components/page-container/page-container-skeleton.js +72 -0
- package/dist/components/page-container/page-container.js +93 -0
- package/dist/components/pagination/pagination.js +96 -0
- package/dist/components/pagination/pagination.props.js +16 -0
- package/dist/components/progress-bar/progress-bar.js +106 -0
- package/dist/components/radio-group/radio-group.js +41 -0
- package/dist/components/requirement-list/requirement-list.js +83 -0
- package/dist/components/scrollable-sections/scrollable-sections.js +270 -0
- package/dist/components/search-box/search-box.js +54 -0
- package/dist/components/search-field-shell.js +24 -0
- package/dist/components/selectable-card-list/checkbox-card-list.js +85 -0
- package/dist/components/selectable-card-list/radio-card-list.js +99 -0
- package/dist/components/selectable-card-list/selectable-card-list-shared.js +135 -0
- package/dist/components/selectable-card-list/selectable-card-list.js +34 -0
- package/dist/components/sidebar/sidebar.js +344 -0
- package/dist/components/sidebar/types.js +6 -0
- package/dist/components/simple-clickable-card/simple-clickable-card-shared.js +309 -0
- package/dist/components/simple-clickable-card/simple-clickable-card.js +106 -0
- package/dist/components/simple-info-card/simple-info-card.js +154 -0
- package/dist/components/simple-list/simple-list.js +146 -0
- package/dist/components/simple-paginated-list/simple-paginated-list.js +178 -0
- package/dist/components/simplified-date/simplified-date.js +56 -0
- package/dist/components/skeletons/table-skeleton.js +16 -0
- package/dist/components/spinner/spinner.js +25 -0
- package/dist/components/stat-card/stat-card.js +206 -0
- package/dist/components/status-badge/status-badge.js +108 -0
- package/dist/components/status-card-list/status-card-list.js +282 -0
- package/dist/components/tag/tag.js +144 -0
- package/dist/components/tag-input/tag-input.js +161 -0
- package/dist/components/text-effects/glitch-effect/glitch-text.js +115 -0
- package/dist/components/text-effects/gradient-text/gradient-text.js +32 -0
- package/dist/components/text-effects/highlight-text/highlight-text.js +35 -0
- package/dist/components/text-effects/parse-text-markup.js +53 -0
- package/dist/components/text-effects/shimmer-text/shimmer-text.js +67 -0
- package/dist/components/time-picker/time-picker.js +23 -0
- package/dist/components/toast/toast.js +126 -0
- package/dist/components/togglable-items-list/togglable-items-list.js +28 -0
- package/dist/components/tooltip/tooltip.js +21 -0
- package/dist/components/transfer-list/transfer-list.js +413 -0
- package/dist/components/truncated-line-with-tooltip/truncated-line-with-tooltip.js +61 -0
- package/dist/components/typography/typography-variants.js +41 -0
- package/dist/components/typography/typography.js +15 -0
- package/dist/components/user-type-badge/user-type-badge.js +40 -0
- package/dist/components/user-type-badge/user-type-badge.registry.js +17 -0
- package/dist/components/vertical-stepper/vertical-stepper.js +171 -0
- package/dist/core/alert.js +33 -0
- package/dist/core/animate-ui/components/animate/code-header.js +30 -0
- package/dist/core/animate-ui/components/animate/code.js +35 -0
- package/dist/core/animate-ui/components/animate/tabs.js +188 -0
- package/dist/core/animate-ui/components/animate/tooltip.js +39 -0
- package/dist/core/animate-ui/components/base/preview-card.js +29 -0
- package/dist/core/animate-ui/components/base/progress.js +32 -0
- package/dist/core/animate-ui/components/base/radio.js +24 -0
- package/dist/core/animate-ui/components/buttons/copy.js +95 -0
- package/dist/core/animate-ui/components/radix/accordion.js +36 -0
- package/dist/core/animate-ui/components/radix/dialog.js +60 -0
- package/dist/core/animate-ui/components/radix/dropdown-menu.js +129 -0
- package/dist/core/animate-ui/components/radix/sheet.js +110 -0
- package/dist/core/animate-ui/components/radix/sidebar.js +416 -0
- package/dist/core/animate-ui/components/radix/switch.js +34 -0
- package/dist/core/animate-ui/components/radix/toggle-group.js +115 -0
- package/dist/core/animate-ui/components/radix/toggle.js +24 -0
- package/dist/core/animate-ui/primitives/animate/code-block.js +131 -0
- package/dist/core/animate-ui/primitives/animate/escape-html.js +7 -0
- package/dist/core/animate-ui/primitives/animate/shiki-highlighter.js +64 -0
- package/dist/core/animate-ui/primitives/animate/slot.js +39 -0
- package/dist/core/animate-ui/primitives/animate/tabs.js +202 -0
- package/dist/core/animate-ui/primitives/animate/tooltip.js +351 -0
- package/dist/core/animate-ui/primitives/base/preview-card.js +107 -0
- package/dist/core/animate-ui/primitives/base/progress.js +60 -0
- package/dist/core/animate-ui/primitives/base/radio.js +83 -0
- package/dist/core/animate-ui/primitives/buttons/button.js +16 -0
- package/dist/core/animate-ui/primitives/effects/highlight.js +407 -0
- package/dist/core/animate-ui/primitives/radix/accordion.js +76 -0
- package/dist/core/animate-ui/primitives/radix/dialog.js +136 -0
- package/dist/core/animate-ui/primitives/radix/dropdown-menu.js +305 -0
- package/dist/core/animate-ui/primitives/radix/sheet.js +165 -0
- package/dist/core/animate-ui/primitives/radix/switch.js +86 -0
- package/dist/core/animate-ui/primitives/radix/toggle-group.js +100 -0
- package/dist/core/animate-ui/primitives/texts/counting-number.js +75 -0
- package/dist/core/avatar.js +32 -0
- package/dist/core/badge.js +27 -0
- package/dist/core/breadcrumb.js +62 -0
- package/dist/core/button/button.js +43 -0
- package/dist/core/button/craft-button.js +78 -0
- package/dist/core/button-group.js +38 -0
- package/dist/core/button.js +40 -0
- package/dist/core/card.js +42 -0
- package/dist/core/carousel/carousel.js +149 -0
- package/dist/core/checkbox/motion-checkbox.js +103 -0
- package/dist/core/collapsible.js +54 -0
- package/dist/core/combobox/combobox.js +85 -0
- package/dist/core/command.js +61 -0
- package/dist/core/field-end-icons.js +36 -0
- package/dist/core/field-label-row.js +33 -0
- package/dist/core/form.js +92 -0
- package/dist/core/hooks/use-controlled-state.js +27 -0
- package/dist/core/hooks/use-data-state.js +39 -0
- package/dist/core/hooks/use-is-in-view.js +18 -0
- package/dist/core/hooks/use-mobile.js +19 -0
- package/dist/core/hooks/use-reveal-on-scroll-up.js +141 -0
- package/dist/core/hooks/use-scroll-overflow.js +50 -0
- package/dist/core/index.js +18 -52
- package/dist/core/input/input-password.js +30 -0
- package/dist/core/input/input.js +147 -0
- package/dist/core/input-otp/input-otp.js +183 -0
- package/dist/core/label.js +15 -0
- package/dist/core/popover.js +31 -0
- package/dist/core/scroll-area.js +42 -0
- package/dist/core/select.js +121 -0
- package/dist/core/separator.js +40 -0
- package/dist/core/skeleton.js +11 -0
- package/dist/core/table.js +59 -0
- package/dist/core/textarea/textarea.js +97 -0
- package/dist/core/utils/get-strict-context.js +18 -0
- package/dist/index.js +148 -9
- package/dist/layouts/entry-layout/entry-layout-bg.js +4 -0
- package/dist/layouts/entry-layout/entry-layout.js +52 -0
- package/dist/layouts/index.js +7 -6
- package/dist/layouts/workspace-layout/account-menu/account-menu.js +108 -0
- package/dist/layouts/workspace-layout/breadcrumbs/derive-breadcrumbs.js +37 -0
- package/dist/layouts/workspace-layout/breadcrumbs/workspace-breadcrumbs.js +22 -0
- package/dist/layouts/workspace-layout/footer/footer.js +15 -0
- package/dist/layouts/workspace-layout/header/header.js +54 -0
- package/dist/layouts/workspace-layout/workspace-layout.js +157 -0
- package/dist/tailwind-preset.js +3 -4
- package/dist/utils/cn.js +8 -0
- package/dist/utils/color.js +72 -0
- package/dist/utils/index.js +3 -2
- package/dist/utils/path-match.js +17 -0
- package/dist/utils/silver-shine.js +63 -0
- package/dist/utils/variant-gradient-underlay.js +45 -0
- package/dist/utils/variant.js +426 -0
- package/package.json +7 -4
- package/dist/chunk-2GDYZAKR.js +0 -505
- package/dist/chunk-ALCWXFUC.js +0 -733
- package/dist/chunk-CDHBLANU.js +0 -59
- package/dist/chunk-LUESSJAF.js +0 -70068
- package/dist/chunk-TMORRNEB.js +0 -323
- package/dist/chunk-TRTQSARU.js +0 -31
- package/dist/chunk-VU4CZ76T.js +0 -43
- package/dist/chunk-XNLCVHBC.js +0 -364
- package/dist/chunk-Z4776NRY.js +0 -4347
- package/src/assets/illustrations/add-to-group.svg +0 -32
- package/src/assets/illustrations/chip.svg +0 -51
- package/src/assets/illustrations/create-new-group.svg +0 -27
- package/src/assets/illustrations/new-ota.svg +0 -61
- package/src/assets/illustrations/secured-key.svg +0 -48
- /package/dist/assets/{entry-layout-bg-JPI7GJ42.webp → entry-layout-bg-DUM0UhT3.webp} +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/components/key-value-manager/key-value-manager.utils.ts
|
|
2
|
+
function validateField(value, meta) {
|
|
3
|
+
if (!value.trim()) return false;
|
|
4
|
+
if (meta?.maxLength != null && value.length > meta.maxLength) return false;
|
|
5
|
+
if (meta?.options && !meta.options.includes(value)) return false;
|
|
6
|
+
if (meta?.regex) try {
|
|
7
|
+
if (!new RegExp(meta.regex, meta.regexFlags).test(value)) return false;
|
|
8
|
+
} catch {}
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
function filterKeyValueRows(items, searchQuery) {
|
|
12
|
+
const q = searchQuery.trim().toLowerCase();
|
|
13
|
+
const rows = items.map((item, sourceIndex) => ({
|
|
14
|
+
item,
|
|
15
|
+
sourceIndex
|
|
16
|
+
}));
|
|
17
|
+
if (!q) return rows;
|
|
18
|
+
return rows.filter(({ item }) => item.key.toLowerCase().includes(q) || item.value.toLowerCase().includes(q));
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { filterKeyValueRows, validateField };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import * as React$1 from "react";
|
|
3
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/link/link.tsx
|
|
5
|
+
var EXTERNAL_PROTOCOL = /^(https?:|mailto:|tel:|sms:|ftp:)/i;
|
|
6
|
+
var PROTOCOL_RELATIVE = /^\/\//;
|
|
7
|
+
function isExternalHref(to) {
|
|
8
|
+
return EXTERNAL_PROTOCOL.test(to) || PROTOCOL_RELATIVE.test(to);
|
|
9
|
+
}
|
|
10
|
+
var baseClasses = "inline-flex items-center gap-1.5 rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0";
|
|
11
|
+
var colorClasses = {
|
|
12
|
+
primary: "px-2 h-8 text-sm font-medium text-primary hover:bg-primary/10",
|
|
13
|
+
secondary: "px-2 h-8 text-sm font-medium text-secondary hover:bg-secondary/10",
|
|
14
|
+
error: "px-2 h-8 text-sm font-medium text-error hover:bg-error/10",
|
|
15
|
+
info: "px-2 h-8 text-sm font-medium text-info hover:bg-info/10",
|
|
16
|
+
success: "px-2 h-8 text-sm font-medium text-success hover:bg-success/10",
|
|
17
|
+
warning: "px-2 h-8 text-sm font-medium text-warning hover:bg-warning/10",
|
|
18
|
+
gray: "px-2 h-8 text-sm font-medium text-gray hover:bg-gray/10"
|
|
19
|
+
};
|
|
20
|
+
var variantClasses = {
|
|
21
|
+
default: "",
|
|
22
|
+
ghost: "h-auto hover:bg-transparent p-0"
|
|
23
|
+
};
|
|
24
|
+
var underlineClasses = "relative no-underline after:absolute after:-bottom-0.5 after:left-0 after:h-px after:w-full after:bg-current hover:after:animate-underline-slide";
|
|
25
|
+
function renderIconSlot(icon) {
|
|
26
|
+
if (icon == null) return null;
|
|
27
|
+
return /* @__PURE__ */ jsx("span", {
|
|
28
|
+
className: "inline-flex shrink-0",
|
|
29
|
+
children: icon
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
var Link = React$1.forwardRef(function Link({ to, params, search, linkComponent, color = "primary", variant = "default", underline = false, startIcon, endIcon, className, children, ...rest }, ref) {
|
|
33
|
+
const merged = cn(baseClasses, color != null && colorClasses[color], variantClasses[variant], underline && underlineClasses, className);
|
|
34
|
+
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
35
|
+
renderIconSlot(startIcon),
|
|
36
|
+
children,
|
|
37
|
+
renderIconSlot(endIcon)
|
|
38
|
+
] });
|
|
39
|
+
if (isExternalHref(to)) return /* @__PURE__ */ jsx("a", {
|
|
40
|
+
...rest,
|
|
41
|
+
ref,
|
|
42
|
+
href: to,
|
|
43
|
+
target: "_blank",
|
|
44
|
+
rel: "noopener noreferrer",
|
|
45
|
+
className: merged,
|
|
46
|
+
children: content
|
|
47
|
+
});
|
|
48
|
+
if (linkComponent) return /* @__PURE__ */ jsx(linkComponent, {
|
|
49
|
+
...rest,
|
|
50
|
+
ref,
|
|
51
|
+
to,
|
|
52
|
+
params,
|
|
53
|
+
search,
|
|
54
|
+
className: merged,
|
|
55
|
+
children: content
|
|
56
|
+
});
|
|
57
|
+
return /* @__PURE__ */ jsx("a", {
|
|
58
|
+
...rest,
|
|
59
|
+
ref,
|
|
60
|
+
href: to,
|
|
61
|
+
className: merged,
|
|
62
|
+
children: content
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
Link.displayName = "Link";
|
|
66
|
+
//#endregion
|
|
67
|
+
export { Link };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { DropdownMenuItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from "../../core/animate-ui/components/radix/dropdown-menu.js";
|
|
3
|
+
import { getItemClassName, getItemStyles, renderItemContent } from "./list-item.utils.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/list/dropdown-list-item.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Leaf menu row. With `href` plus an adapter the content is wrapped in a real link so
|
|
8
|
+
* right-click / middle-click / modifier-click work, while Radix keeps its keyboard and
|
|
9
|
+
* focus semantics on the surrounding menu item. Falls back to the click-only row
|
|
10
|
+
* otherwise.
|
|
11
|
+
*
|
|
12
|
+
* The link is nested inside the menu item rather than composed via `asChild`, because
|
|
13
|
+
* the underlying animate-ui primitive already consumes `asChild` for its motion
|
|
14
|
+
* wrapper. This mirrors how `Sidebar`'s collapsed flyout renders its links.
|
|
15
|
+
*/
|
|
16
|
+
function DropdownLeafItem({ item, itemClassName, linkComponent: LinkComponent }) {
|
|
17
|
+
const itemStyles = getItemStyles(item);
|
|
18
|
+
if (item.href && LinkComponent) return /* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
19
|
+
className: "p-0",
|
|
20
|
+
onClick: item.onClick,
|
|
21
|
+
children: /* @__PURE__ */ jsx(LinkComponent, {
|
|
22
|
+
to: item.href,
|
|
23
|
+
className: cn("gap-1.5 cursor-pointer", getItemClassName(item, itemClassName)),
|
|
24
|
+
style: itemStyles.style,
|
|
25
|
+
"aria-current": item.isSelected ? "page" : void 0,
|
|
26
|
+
children: renderItemContent(item)
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
return /* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
30
|
+
className: cn("gap-1.5", getItemClassName(item, itemClassName)),
|
|
31
|
+
style: itemStyles.style,
|
|
32
|
+
onClick: item.onClick,
|
|
33
|
+
children: renderItemContent(item)
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function DropdownListItem({ item, itemClassName, linkComponent }) {
|
|
37
|
+
if (item.items && item.items.length > 0) {
|
|
38
|
+
const itemStyles = getItemStyles(item);
|
|
39
|
+
return /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [/* @__PURE__ */ jsx(DropdownMenuSubTrigger, {
|
|
40
|
+
className: cn("gap-1.5", getItemClassName(item, itemClassName)),
|
|
41
|
+
style: itemStyles.style,
|
|
42
|
+
children: renderItemContent(item)
|
|
43
|
+
}), /* @__PURE__ */ jsx(DropdownMenuSubContent, {
|
|
44
|
+
sideOffset: 4,
|
|
45
|
+
className: "z-[60] min-w-[8rem]",
|
|
46
|
+
children: item.items?.map((subItem) => /* @__PURE__ */ jsx(DropdownLeafItem, {
|
|
47
|
+
item: subItem,
|
|
48
|
+
itemClassName,
|
|
49
|
+
linkComponent
|
|
50
|
+
}, subItem.id))
|
|
51
|
+
})] });
|
|
52
|
+
}
|
|
53
|
+
return /* @__PURE__ */ jsx(DropdownLeafItem, {
|
|
54
|
+
item,
|
|
55
|
+
itemClassName,
|
|
56
|
+
linkComponent
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { DropdownListItem as default };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { isPresetColor, resolveColor } from "../../utils/color.js";
|
|
3
|
+
import { LIST_CLASS_NAMES } from "./list.props.js";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/list/list-item.utils.tsx
|
|
7
|
+
var listItemBase = "min-h-[var(--menu-item-height)] w-full scroll-mt-[calc(var(--spacing)*1.5)] px-[calc(var(--spacing)*2.5)] py-[calc(var(--spacing)*1.5)] my-[calc(var(--spacing)*0.5)] text-sm leading-[var(--tw-leading,var(--text-sm--line-height))] select-none rounded-[10px] items-center flex relative no-underline";
|
|
8
|
+
/**
|
|
9
|
+
* Interaction classes shared by the `<button>` and link renderings of a row, so a
|
|
10
|
+
* linked item is visually identical to a clickable one.
|
|
11
|
+
*/
|
|
12
|
+
var listItemInteractive = "w-full text-left cursor-pointer hover:bg-accent focus:bg-accent focus:outline-none";
|
|
13
|
+
var presetColorStyles = {
|
|
14
|
+
primary: "text-primary",
|
|
15
|
+
secondary: "text-secondary-foreground",
|
|
16
|
+
error: "text-error",
|
|
17
|
+
info: "text-info",
|
|
18
|
+
success: "text-success",
|
|
19
|
+
warning: "text-warning",
|
|
20
|
+
disabled: "text-disabled",
|
|
21
|
+
gray: "text-gray",
|
|
22
|
+
mist: "text-mist-foreground"
|
|
23
|
+
};
|
|
24
|
+
var sizeStyles = {
|
|
25
|
+
sm: "text-xs py-1 px-2",
|
|
26
|
+
default: "text-sm py-1.5 px-2",
|
|
27
|
+
lg: "text-base py-2 px-3"
|
|
28
|
+
};
|
|
29
|
+
function isListItem(item) {
|
|
30
|
+
return typeof item === "object" && item !== null && !React.isValidElement(item) && "id" in item && "label" in item && typeof item.id === "string" && typeof item.label === "string";
|
|
31
|
+
}
|
|
32
|
+
function getItemStyles(item) {
|
|
33
|
+
const color = item.color || "default";
|
|
34
|
+
const size = item.size || "default";
|
|
35
|
+
const isSelected = item.isSelected || false;
|
|
36
|
+
if (color === "default" || isPresetColor(color) && presetColorStyles[color]) return { className: cn(LIST_CLASS_NAMES.item, listItemBase, color === "default" ? "" : presetColorStyles[color], sizeStyles[size], isSelected && "bg-accent font-medium") };
|
|
37
|
+
const resolved = resolveColor(color, "text");
|
|
38
|
+
return {
|
|
39
|
+
className: cn(LIST_CLASS_NAMES.item, listItemBase, sizeStyles[size], isSelected && "bg-accent font-medium"),
|
|
40
|
+
style: resolved.style
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Class string for a non-interactive row (dropdown items, submenu triggers).
|
|
45
|
+
*
|
|
46
|
+
* Caller-supplied classes come last — list-wide `itemClassName` first, then the row's own
|
|
47
|
+
* `className` — so they win over the component's defaults under tailwind-merge.
|
|
48
|
+
*/
|
|
49
|
+
function getItemClassName(item, itemClassName) {
|
|
50
|
+
return cn(getItemStyles(item).className, itemClassName, item.className);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Class string for an interactive standard-list row, used by both the `<button>` and the
|
|
54
|
+
* link rendering. Ordering follows {@link getItemClassName}.
|
|
55
|
+
*/
|
|
56
|
+
function getInteractiveItemClassName(item, itemClassName) {
|
|
57
|
+
return cn(getItemStyles(item).className, listItemInteractive, itemClassName, item.className);
|
|
58
|
+
}
|
|
59
|
+
function renderItemContent(item) {
|
|
60
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
61
|
+
item.startIcon && /* @__PURE__ */ jsx("span", {
|
|
62
|
+
className: "mr-2 flex-shrink-0",
|
|
63
|
+
children: item.startIcon
|
|
64
|
+
}),
|
|
65
|
+
/* @__PURE__ */ jsx("span", {
|
|
66
|
+
className: "flex-1",
|
|
67
|
+
children: item.label
|
|
68
|
+
}),
|
|
69
|
+
item.endIcon && /* @__PURE__ */ jsx("span", {
|
|
70
|
+
className: "ml-2 flex-shrink-0",
|
|
71
|
+
children: item.endIcon
|
|
72
|
+
})
|
|
73
|
+
] });
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { getInteractiveItemClassName, getItemClassName, getItemStyles, isListItem, renderItemContent, sizeStyles };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { DropdownMenuLabel, DropdownMenuSeparator } from "../../core/animate-ui/components/radix/dropdown-menu.js";
|
|
3
|
+
import { LIST_CLASS_NAMES } from "./list.props.js";
|
|
4
|
+
import { isListItem } from "./list-item.utils.js";
|
|
5
|
+
import StandardListItem from "./standard-list-item.js";
|
|
6
|
+
import DropdownListItem from "./dropdown-list-item.js";
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/components/list/list.tsx
|
|
10
|
+
var groupLabelBase = "text-sm font-normal truncate overflow-ellipsis font-inter text-gray-400 flex items-center gap-2";
|
|
11
|
+
function List({ items, role = "list", showSeparators = true, className, itemClassName, separatorClassName, linkComponent }) {
|
|
12
|
+
const shouldShowSeparators = showSeparators && items.length > 1;
|
|
13
|
+
const isDropdown = role === "dropdown-list";
|
|
14
|
+
const ItemComponent = isDropdown ? DropdownListItem : StandardListItem;
|
|
15
|
+
return /* @__PURE__ */ jsx(isDropdown ? React.Fragment : ({ children }) => /* @__PURE__ */ jsx("div", {
|
|
16
|
+
className: cn(LIST_CLASS_NAMES.container, "flex flex-col", className),
|
|
17
|
+
children
|
|
18
|
+
}), { children: items.map((group, groupIndex) => {
|
|
19
|
+
const isLastGroup = groupIndex === items.length - 1;
|
|
20
|
+
const labelContent = group.label && /* @__PURE__ */ jsxs(Fragment$1, { children: [group.icon && /* @__PURE__ */ jsx("span", {
|
|
21
|
+
className: "flex-shrink-0",
|
|
22
|
+
children: group.icon
|
|
23
|
+
}), /* @__PURE__ */ jsx("span", { children: group.label })] });
|
|
24
|
+
const labelClassName = cn(LIST_CLASS_NAMES.groupLabel, groupLabelBase, !isDropdown && "px-2.5 py-1.5", group.labelClassName);
|
|
25
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
26
|
+
group.label && (isDropdown ? /* @__PURE__ */ jsx(DropdownMenuLabel, {
|
|
27
|
+
className: labelClassName,
|
|
28
|
+
children: labelContent
|
|
29
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
30
|
+
className: labelClassName,
|
|
31
|
+
children: labelContent
|
|
32
|
+
})),
|
|
33
|
+
group.items.map((item, index) => {
|
|
34
|
+
if (isListItem(item)) return /* @__PURE__ */ jsx(ItemComponent, {
|
|
35
|
+
item,
|
|
36
|
+
itemClassName,
|
|
37
|
+
linkComponent
|
|
38
|
+
}, item.id);
|
|
39
|
+
else return /* @__PURE__ */ jsx("div", {
|
|
40
|
+
className: cn(LIST_CLASS_NAMES.customItem, "px-2.5 py-1.5", itemClassName),
|
|
41
|
+
children: item
|
|
42
|
+
}, index);
|
|
43
|
+
}),
|
|
44
|
+
shouldShowSeparators && !isLastGroup && (isDropdown ? /* @__PURE__ */ jsx(DropdownMenuSeparator, { className: cn(LIST_CLASS_NAMES.separator, separatorClassName) }) : /* @__PURE__ */ jsx("hr", { className: cn(LIST_CLASS_NAMES.separator, "my-1 border-border", separatorClassName) }))
|
|
45
|
+
] }, group.id);
|
|
46
|
+
}) });
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { List as default };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/components/list/list.props.ts
|
|
2
|
+
/**
|
|
3
|
+
* Stable class hooks on every rendered part of a `List`, so consumers can target them
|
|
4
|
+
* from Tailwind arbitrary variants (`[&_.espd-list-item]:rounded-none`) or plain CSS
|
|
5
|
+
* without depending on the component's internal utility classes.
|
|
6
|
+
*
|
|
7
|
+
* These are contract: they are always present, and they are emitted *before* any
|
|
8
|
+
* caller-supplied `className`, so caller utilities win under tailwind-merge.
|
|
9
|
+
*
|
|
10
|
+
* The `espd-` prefix is required, not cosmetic: an unprefixed `list-item` is a real
|
|
11
|
+
* Tailwind `display` utility, so `twMerge` would drop it as soon as the component added
|
|
12
|
+
* `flex` — and `list-*` is Tailwind's list-style namespace generally.
|
|
13
|
+
*/
|
|
14
|
+
var LIST_CLASS_NAMES = {
|
|
15
|
+
/** Flex column wrapping every group. Standard (`role="list"`) rendering only. */
|
|
16
|
+
container: "espd-list",
|
|
17
|
+
/** Heading rendered for a {@link ListGroup.label}. */
|
|
18
|
+
groupLabel: "espd-list-group-label",
|
|
19
|
+
/** Every row rendered from a {@link ListItem}, in both roles and at any nesting depth. */
|
|
20
|
+
item: "espd-list-item",
|
|
21
|
+
/** Wrapper around a raw `ReactNode` passed in {@link ListGroup.items}. */
|
|
22
|
+
customItem: "espd-list-custom-item",
|
|
23
|
+
/** Divider drawn between groups. */
|
|
24
|
+
separator: "espd-list-separator"
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { LIST_CLASS_NAMES };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { getInteractiveItemClassName, getItemStyles, renderItemContent } from "./list-item.utils.js";
|
|
3
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../core/collapsible.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { ChevronDown } from "lucide-react";
|
|
6
|
+
//#region src/components/list/standard-list-item.tsx
|
|
7
|
+
/**
|
|
8
|
+
* Leaf row: a real link when the item declares `href` and an adapter is available,
|
|
9
|
+
* otherwise the plain click button. Both share `getInteractiveItemClassName`, so the
|
|
10
|
+
* two renderings are visually identical.
|
|
11
|
+
*/
|
|
12
|
+
function StandardLeafItem({ item, itemClassName, linkComponent: LinkComponent }) {
|
|
13
|
+
const itemStyles = getItemStyles(item);
|
|
14
|
+
const className = getInteractiveItemClassName(item, itemClassName);
|
|
15
|
+
if (item.href && LinkComponent) return /* @__PURE__ */ jsx(LinkComponent, {
|
|
16
|
+
to: item.href,
|
|
17
|
+
className,
|
|
18
|
+
style: itemStyles.style,
|
|
19
|
+
"aria-current": item.isSelected ? "page" : void 0,
|
|
20
|
+
children: renderItemContent(item)
|
|
21
|
+
});
|
|
22
|
+
return /* @__PURE__ */ jsx("button", {
|
|
23
|
+
type: "button",
|
|
24
|
+
className,
|
|
25
|
+
style: itemStyles.style,
|
|
26
|
+
onClick: item.onClick,
|
|
27
|
+
children: renderItemContent(item)
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function StandardListItem({ item, itemClassName, linkComponent }) {
|
|
31
|
+
if (item.items && item.items.length > 0) {
|
|
32
|
+
const itemStyles = getItemStyles(item);
|
|
33
|
+
return /* @__PURE__ */ jsxs(Collapsible, {
|
|
34
|
+
className: "w-full",
|
|
35
|
+
children: [/* @__PURE__ */ jsx(CollapsibleTrigger, {
|
|
36
|
+
asChild: true,
|
|
37
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
38
|
+
type: "button",
|
|
39
|
+
className: cn("group", getInteractiveItemClassName(item, itemClassName)),
|
|
40
|
+
style: itemStyles.style,
|
|
41
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
42
|
+
className: "flex flex-1 items-center",
|
|
43
|
+
children: renderItemContent(item)
|
|
44
|
+
}), /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 shrink-0 transition-transform duration-200 group-data-[state=open]:rotate-180" })]
|
|
45
|
+
})
|
|
46
|
+
}), /* @__PURE__ */ jsx(CollapsibleContent, {
|
|
47
|
+
className: "w-full",
|
|
48
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
49
|
+
className: "pl-4 w-full",
|
|
50
|
+
children: item.items?.map((subItem) => /* @__PURE__ */ jsx(StandardLeafItem, {
|
|
51
|
+
item: subItem,
|
|
52
|
+
itemClassName,
|
|
53
|
+
linkComponent
|
|
54
|
+
}, subItem.id))
|
|
55
|
+
})
|
|
56
|
+
})]
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return /* @__PURE__ */ jsx(StandardLeafItem, {
|
|
60
|
+
item,
|
|
61
|
+
itemClassName,
|
|
62
|
+
linkComponent
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { StandardListItem as default };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../../utils/cn.js";
|
|
3
|
+
import { Button } from "../button/button.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { ChevronRight } from "lucide-react";
|
|
6
|
+
//#region src/components/list-details/list-details-item-button.tsx
|
|
7
|
+
/** One item of the vertical title list: the side panel, or the small-screen dropdown. */
|
|
8
|
+
function ListDetailsItemButton({ item, isSelected, onSelect, showDefaultEndIcon }) {
|
|
9
|
+
const defaultEndIcon = showDefaultEndIcon ? /* @__PURE__ */ jsx(ChevronRight, {
|
|
10
|
+
className: "size-4",
|
|
11
|
+
"aria-hidden": true
|
|
12
|
+
}) : null;
|
|
13
|
+
const endIcon = item.endIcon ?? defaultEndIcon;
|
|
14
|
+
if (typeof item.title !== "string") return /* @__PURE__ */ jsxs("button", {
|
|
15
|
+
type: "button",
|
|
16
|
+
onClick: () => onSelect(item.id),
|
|
17
|
+
"aria-pressed": isSelected,
|
|
18
|
+
className: cn("flex w-full items-center gap-2 rounded-md px-3 py-2 text-left", "border-0 bg-transparent shadow-none", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"),
|
|
19
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
20
|
+
className: "flex min-w-0 flex-1 items-center",
|
|
21
|
+
children: item.title
|
|
22
|
+
}), endIcon != null && /* @__PURE__ */ jsx("span", {
|
|
23
|
+
className: "shrink-0",
|
|
24
|
+
children: endIcon
|
|
25
|
+
})]
|
|
26
|
+
});
|
|
27
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
28
|
+
type: "button",
|
|
29
|
+
variant: isSelected ? "outline" : "ghost",
|
|
30
|
+
color: isSelected ? "primary" : "gray",
|
|
31
|
+
onClick: () => onSelect(item.id),
|
|
32
|
+
"aria-pressed": isSelected,
|
|
33
|
+
className: !isSelected ? "border border-transparent" : "",
|
|
34
|
+
endIcon: endIcon ?? void 0,
|
|
35
|
+
hideRingOnHover: true,
|
|
36
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
37
|
+
className: "flex min-w-0 flex-1 items-center",
|
|
38
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
39
|
+
className: "min-w-0 truncate",
|
|
40
|
+
children: item.title
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { ListDetailsItemButton };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../../utils/cn.js";
|
|
3
|
+
import { Button } from "../button/button.js";
|
|
4
|
+
import { ScrollArea } from "../../core/scroll-area.js";
|
|
5
|
+
import { ListDetailsItemButton } from "./list-details-item-button.js";
|
|
6
|
+
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../../core/popover.js";
|
|
7
|
+
import { useRef, useState } from "react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { ChevronDown, ChevronLeft, ChevronRight } from "lucide-react";
|
|
10
|
+
//#region src/components/list-details/list-details-item-dropdown.tsx
|
|
11
|
+
function focusSelectedItem(list) {
|
|
12
|
+
list?.querySelector("[aria-pressed=\"true\"]")?.focus();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The item picker on small screens: previous/next arrows around a menu button
|
|
16
|
+
* showing the selected title. The menu lists every item, with the `footer`
|
|
17
|
+
* (e.g. "Load more") at its bottom, as under the side panel.
|
|
18
|
+
*/
|
|
19
|
+
function ListDetailsItemDropdown({ data, selectedId, onSelect, footer, labels, className }) {
|
|
20
|
+
const [open, setOpen] = useState(false);
|
|
21
|
+
const listRef = useRef(null);
|
|
22
|
+
const selectedIndex = data.findIndex((item) => item.id === selectedId);
|
|
23
|
+
const selectedItem = data[selectedIndex];
|
|
24
|
+
const previousItem = selectedIndex > 0 ? data[selectedIndex - 1] : void 0;
|
|
25
|
+
const nextItem = selectedIndex >= 0 ? data[selectedIndex + 1] : void 0;
|
|
26
|
+
const handleSelect = (itemId) => {
|
|
27
|
+
onSelect(itemId);
|
|
28
|
+
setOpen(false);
|
|
29
|
+
};
|
|
30
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
31
|
+
open,
|
|
32
|
+
onOpenChange: setOpen,
|
|
33
|
+
children: [/* @__PURE__ */ jsx(PopoverAnchor, {
|
|
34
|
+
asChild: true,
|
|
35
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
36
|
+
"data-slot": "list-details-item-dropdown",
|
|
37
|
+
className: cn("flex items-center gap-2", className),
|
|
38
|
+
children: [
|
|
39
|
+
/* @__PURE__ */ jsx(Button, {
|
|
40
|
+
type: "button",
|
|
41
|
+
size: "icon",
|
|
42
|
+
variant: "outline",
|
|
43
|
+
color: "gray",
|
|
44
|
+
"aria-label": labels.previousItem,
|
|
45
|
+
disabled: previousItem == null,
|
|
46
|
+
onClick: () => previousItem && onSelect(previousItem.id),
|
|
47
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": true })
|
|
48
|
+
}),
|
|
49
|
+
/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
50
|
+
asChild: true,
|
|
51
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
52
|
+
type: "button",
|
|
53
|
+
variant: "outline",
|
|
54
|
+
color: "gray",
|
|
55
|
+
className: "min-w-0 flex-1 justify-between px-3",
|
|
56
|
+
endIcon: /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true }),
|
|
57
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
58
|
+
className: "flex min-w-0 flex-1 items-center",
|
|
59
|
+
children: [
|
|
60
|
+
/* @__PURE__ */ jsxs("span", {
|
|
61
|
+
className: "sr-only",
|
|
62
|
+
children: [labels.chooseItem, ":"]
|
|
63
|
+
}),
|
|
64
|
+
" ",
|
|
65
|
+
typeof selectedItem?.title === "string" ? /* @__PURE__ */ jsx("span", {
|
|
66
|
+
className: "min-w-0 truncate",
|
|
67
|
+
children: selectedItem.title
|
|
68
|
+
}) : selectedItem?.title
|
|
69
|
+
]
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
}),
|
|
73
|
+
/* @__PURE__ */ jsx(Button, {
|
|
74
|
+
type: "button",
|
|
75
|
+
size: "icon",
|
|
76
|
+
variant: "outline",
|
|
77
|
+
color: "gray",
|
|
78
|
+
"aria-label": labels.nextItem,
|
|
79
|
+
disabled: nextItem == null,
|
|
80
|
+
onClick: () => nextItem && onSelect(nextItem.id),
|
|
81
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": true })
|
|
82
|
+
})
|
|
83
|
+
]
|
|
84
|
+
})
|
|
85
|
+
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
86
|
+
align: "start",
|
|
87
|
+
className: "flex w-[var(--radix-popover-trigger-width)] max-h-[min(24rem,var(--radix-popover-content-available-height))] flex-col gap-2 p-2",
|
|
88
|
+
onOpenAutoFocus: (event) => {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
focusSelectedItem(listRef.current);
|
|
91
|
+
},
|
|
92
|
+
children: [/* @__PURE__ */ jsx(ScrollArea, {
|
|
93
|
+
className: "min-h-0 flex-1",
|
|
94
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
95
|
+
ref: listRef,
|
|
96
|
+
className: "flex flex-col gap-2",
|
|
97
|
+
children: data.map((item) => /* @__PURE__ */ jsx(ListDetailsItemButton, {
|
|
98
|
+
item,
|
|
99
|
+
isSelected: item.id === selectedId,
|
|
100
|
+
onSelect: handleSelect,
|
|
101
|
+
showDefaultEndIcon: false
|
|
102
|
+
}, item.id))
|
|
103
|
+
})
|
|
104
|
+
}), footer != null ? /* @__PURE__ */ jsx("div", {
|
|
105
|
+
className: "shrink-0",
|
|
106
|
+
children: footer
|
|
107
|
+
}) : null]
|
|
108
|
+
})]
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
export { ListDetailsItemDropdown };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../../utils/cn.js";
|
|
3
|
+
import { Button } from "../button/button.js";
|
|
4
|
+
import { useScrollOverflow } from "../../core/hooks/use-scroll-overflow.js";
|
|
5
|
+
import { useEffect, useRef } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
8
|
+
//#region src/components/list-details/list-details-item-strip.tsx
|
|
9
|
+
/** Width of the edge fade, and the margin kept beside the selected item when it is scrolled into view. */
|
|
10
|
+
var EDGE_SIZE = 24;
|
|
11
|
+
function prefersReducedMotion() {
|
|
12
|
+
return typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
13
|
+
}
|
|
14
|
+
function fadeMask(start, end) {
|
|
15
|
+
if (!start && !end) return {};
|
|
16
|
+
const image = `linear-gradient(to right, ${start ? `transparent, #000 ${EDGE_SIZE}px` : "#000"}, ${end ? `#000 calc(100% - ${EDGE_SIZE}px), transparent` : "#000"})`;
|
|
17
|
+
return {
|
|
18
|
+
maskImage: image,
|
|
19
|
+
WebkitMaskImage: image
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
var arrowButtonClass = "inline-flex size-8 shrink-0 items-center justify-center rounded-full border border-border bg-background text-muted-foreground transition-colors hover:text-foreground disabled:pointer-events-none disabled:opacity-40";
|
|
23
|
+
/**
|
|
24
|
+
* The item list on small screens: a single scrollable row with arrow buttons,
|
|
25
|
+
* shown above the details instead of the side panel.
|
|
26
|
+
*/
|
|
27
|
+
function ListDetailsItemStrip({ data, selectedId, onSelect, className }) {
|
|
28
|
+
const scrollerRef = useRef(null);
|
|
29
|
+
const overflow = useScrollOverflow(scrollerRef, { getContent: (scroller) => scroller.firstElementChild });
|
|
30
|
+
const hasOverflow = overflow.start || overflow.end;
|
|
31
|
+
const mountedRef = useRef(false);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const scroller = scrollerRef.current;
|
|
34
|
+
const selected = scroller?.querySelector("[aria-pressed=\"true\"]");
|
|
35
|
+
if (!scroller || !selected) return;
|
|
36
|
+
const scrollerRect = scroller.getBoundingClientRect();
|
|
37
|
+
if (scrollerRect.width === 0) return;
|
|
38
|
+
const itemRect = selected.getBoundingClientRect();
|
|
39
|
+
const overStart = itemRect.left - (scrollerRect.left + EDGE_SIZE);
|
|
40
|
+
const overEnd = itemRect.right - (scrollerRect.right - EDGE_SIZE);
|
|
41
|
+
let delta = 0;
|
|
42
|
+
if (overStart < 0) delta = overStart;
|
|
43
|
+
else if (overEnd > 0) delta = Math.min(overEnd, overStart);
|
|
44
|
+
const instant = !mountedRef.current || prefersReducedMotion();
|
|
45
|
+
mountedRef.current = true;
|
|
46
|
+
if (Math.abs(delta) >= 1) scroller.scrollBy({
|
|
47
|
+
left: delta,
|
|
48
|
+
behavior: instant ? "auto" : "smooth"
|
|
49
|
+
});
|
|
50
|
+
}, [selectedId, hasOverflow]);
|
|
51
|
+
const scrollByPage = (direction) => {
|
|
52
|
+
const scroller = scrollerRef.current;
|
|
53
|
+
if (!scroller) return;
|
|
54
|
+
scroller.scrollBy({
|
|
55
|
+
left: direction * scroller.clientWidth * .8,
|
|
56
|
+
behavior: prefersReducedMotion() ? "auto" : "smooth"
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
60
|
+
"data-slot": "list-details-item-strip",
|
|
61
|
+
className: cn("flex items-center gap-1", className),
|
|
62
|
+
children: [
|
|
63
|
+
hasOverflow && /* @__PURE__ */ jsx("button", {
|
|
64
|
+
type: "button",
|
|
65
|
+
tabIndex: -1,
|
|
66
|
+
"aria-hidden": true,
|
|
67
|
+
disabled: !overflow.start,
|
|
68
|
+
className: arrowButtonClass,
|
|
69
|
+
onClick: () => scrollByPage(-1),
|
|
70
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
|
|
71
|
+
}),
|
|
72
|
+
/* @__PURE__ */ jsx("div", {
|
|
73
|
+
ref: scrollerRef,
|
|
74
|
+
className: "min-w-0 flex-1 overflow-x-auto overscroll-x-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
75
|
+
style: fadeMask(overflow.start, overflow.end),
|
|
76
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
77
|
+
className: "flex w-max gap-2 p-1",
|
|
78
|
+
children: data.map((item) => {
|
|
79
|
+
const isSelected = selectedId === item.id;
|
|
80
|
+
if (typeof item.title !== "string") return /* @__PURE__ */ jsxs("button", {
|
|
81
|
+
type: "button",
|
|
82
|
+
onClick: () => onSelect(item.id),
|
|
83
|
+
"aria-pressed": isSelected,
|
|
84
|
+
className: cn("flex shrink-0 items-center gap-2 rounded-md px-3 py-2 text-left", "border-0 bg-transparent shadow-none", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"),
|
|
85
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
86
|
+
className: "flex items-center",
|
|
87
|
+
children: item.title
|
|
88
|
+
}), item.endIcon != null && /* @__PURE__ */ jsx("span", {
|
|
89
|
+
className: "shrink-0",
|
|
90
|
+
children: item.endIcon
|
|
91
|
+
})]
|
|
92
|
+
}, item.id);
|
|
93
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
94
|
+
type: "button",
|
|
95
|
+
fullWidth: false,
|
|
96
|
+
variant: isSelected ? "outline" : "ghost",
|
|
97
|
+
color: isSelected ? "primary" : "gray",
|
|
98
|
+
onClick: () => onSelect(item.id),
|
|
99
|
+
"aria-pressed": isSelected,
|
|
100
|
+
className: cn("shrink-0", !isSelected && "border border-transparent"),
|
|
101
|
+
endIcon: item.endIcon,
|
|
102
|
+
hideRingOnHover: true,
|
|
103
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
104
|
+
className: "max-w-48 truncate",
|
|
105
|
+
title: item.title,
|
|
106
|
+
children: item.title
|
|
107
|
+
})
|
|
108
|
+
}, item.id);
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
}),
|
|
112
|
+
hasOverflow && /* @__PURE__ */ jsx("button", {
|
|
113
|
+
type: "button",
|
|
114
|
+
tabIndex: -1,
|
|
115
|
+
"aria-hidden": true,
|
|
116
|
+
disabled: !overflow.end,
|
|
117
|
+
className: arrowButtonClass,
|
|
118
|
+
onClick: () => scrollByPage(1),
|
|
119
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
|
|
120
|
+
})
|
|
121
|
+
]
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
export { ListDetailsItemStrip };
|