@burtson-labs/ui 0.13.1 → 0.14.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/LICENSES/DEPENDENCIES.txt +2745 -0
- package/LICENSES/Radix-MIT.txt +21 -0
- package/LICENSES/cmdk-MIT.txt +21 -0
- package/LICENSES/overrides/react-remove-scroll-bar.txt +26 -0
- package/LICENSES/shadcn-MIT.txt +21 -0
- package/README.md +20 -12
- package/THIRD_PARTY_NOTICES.md +15 -3
- package/dist/components/accordion.d.ts +4 -4
- package/dist/components/accordion.js +22 -18
- package/dist/components/accordion.js.map +1 -1
- package/dist/components/alert-dialog.d.ts +13 -11
- package/dist/components/alert-dialog.js +41 -31
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/alert.d.ts +5 -3
- package/dist/components/alert.js +12 -7
- package/dist/components/alert.js.map +1 -1
- package/dist/components/app-shell.d.ts +7 -6
- package/dist/components/app-shell.js +22 -14
- package/dist/components/app-shell.js.map +1 -1
- package/dist/components/attachment.d.ts +3 -3
- package/dist/components/attachment.js +11 -7
- package/dist/components/attachment.js.map +1 -1
- package/dist/components/audio-player.d.ts +2 -2
- package/dist/components/audio-player.js +9 -6
- package/dist/components/audio-player.js.map +1 -1
- package/dist/components/avatar.d.ts +4 -4
- package/dist/components/avatar.js +16 -12
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/badge.js +15 -13
- package/dist/components/badge.js.map +1 -1
- package/dist/components/breadcrumb.d.ts +8 -8
- package/dist/components/breadcrumb.js +27 -20
- package/dist/components/breadcrumb.js.map +1 -1
- package/dist/components/button.d.ts +9 -2
- package/dist/components/button.js +15 -6
- package/dist/components/button.js.map +1 -1
- package/dist/components/card.d.ts +9 -9
- package/dist/components/card.js +33 -23
- package/dist/components/card.js.map +1 -1
- package/dist/components/chat-history.d.ts +1 -1
- package/dist/components/chat-history.js +11 -9
- package/dist/components/chat-history.js.map +1 -1
- package/dist/components/chat-layout.d.ts +1 -1
- package/dist/components/chat-layout.js +8 -3
- package/dist/components/chat-layout.js.map +1 -1
- package/dist/components/checkbox-card.d.ts +3 -3
- package/dist/components/checkbox-card.js +12 -10
- package/dist/components/checkbox-card.js.map +1 -1
- package/dist/components/checkbox.d.ts +24 -3
- package/dist/components/checkbox.js +52 -9
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/collapsible.d.ts +3 -3
- package/dist/components/collapsible.js +14 -11
- package/dist/components/collapsible.js.map +1 -1
- package/dist/components/combobox.d.ts +14 -4
- package/dist/components/combobox.js +19 -8
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/command.d.ts +68 -12
- package/dist/components/command.js +48 -34
- package/dist/components/command.js.map +1 -1
- package/dist/components/composer.d.ts +2 -2
- package/dist/components/composer.js +10 -7
- package/dist/components/composer.js.map +1 -1
- package/dist/components/connection-status.d.ts +3 -3
- package/dist/components/connection-status.js +19 -14
- package/dist/components/connection-status.js.map +1 -1
- package/dist/components/context-menu.d.ts +15 -14
- package/dist/components/context-menu.js +52 -41
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/conversation.d.ts +1 -1
- package/dist/components/conversation.js +7 -5
- package/dist/components/conversation.js.map +1 -1
- package/dist/components/copy-button.d.ts +1 -1
- package/dist/components/copy-button.js +4 -2
- package/dist/components/copy-button.js.map +1 -1
- package/dist/components/data-table.d.ts +7 -2
- package/dist/components/data-table.js +17 -42
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/dialog.d.ts +19 -10
- package/dist/components/dialog.js +50 -33
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/dropdown-menu.d.ts +15 -14
- package/dist/components/dropdown-menu.js +52 -41
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/editor-tabs.d.ts +1 -1
- package/dist/components/editor-tabs.js +7 -5
- package/dist/components/editor-tabs.js.map +1 -1
- package/dist/components/empty-state.d.ts +5 -5
- package/dist/components/empty-state.js +17 -11
- package/dist/components/empty-state.js.map +1 -1
- package/dist/components/error-summary.d.ts +21 -0
- package/dist/components/error-summary.js +62 -0
- package/dist/components/error-summary.js.map +1 -0
- package/dist/components/field.d.ts +66 -7
- package/dist/components/field.js +152 -20
- package/dist/components/field.js.map +1 -1
- package/dist/components/form-actions.d.ts +18 -0
- package/dist/components/form-actions.js +30 -0
- package/dist/components/form-actions.js.map +1 -0
- package/dist/components/icon-button.d.ts +3 -1
- package/dist/components/icon-button.js +6 -3
- package/dist/components/icon-button.js.map +1 -1
- package/dist/components/input.d.ts +42 -7
- package/dist/components/input.js +54 -10
- package/dist/components/input.js.map +1 -1
- package/dist/components/kbd.d.ts +2 -2
- package/dist/components/kbd.js +8 -5
- package/dist/components/kbd.js.map +1 -1
- package/dist/components/label.d.ts +7 -2
- package/dist/components/label.js +14 -7
- package/dist/components/label.js.map +1 -1
- package/dist/components/markdown.d.ts +4 -3
- package/dist/components/markdown.js +8 -6
- package/dist/components/markdown.js.map +1 -1
- package/dist/components/menubar.d.ts +17 -15
- package/dist/components/menubar.js +58 -45
- package/dist/components/menubar.js.map +1 -1
- package/dist/components/message-actions.d.ts +3 -3
- package/dist/components/message-actions.js +16 -12
- package/dist/components/message-actions.js.map +1 -1
- package/dist/components/message.d.ts +1 -1
- package/dist/components/message.js +6 -3
- package/dist/components/message.js.map +1 -1
- package/dist/components/mobile-nav.d.ts +1 -1
- package/dist/components/mobile-nav.js +7 -5
- package/dist/components/mobile-nav.js.map +1 -1
- package/dist/components/native-select.d.ts +16 -0
- package/dist/components/native-select.js +33 -0
- package/dist/components/native-select.js.map +1 -0
- package/dist/components/navigation-menu.d.ts +13 -12
- package/dist/components/navigation-menu.js +43 -33
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/number-input.d.ts +23 -0
- package/dist/components/number-input.js +45 -0
- package/dist/components/number-input.js.map +1 -0
- package/dist/components/onboarding-checklist.d.ts +1 -1
- package/dist/components/onboarding-checklist.js +5 -3
- package/dist/components/onboarding-checklist.js.map +1 -1
- package/dist/components/page-header.d.ts +7 -6
- package/dist/components/page-header.js +23 -15
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/pagination.d.ts +1 -1
- package/dist/components/pagination.js +4 -2
- package/dist/components/pagination.js.map +1 -1
- package/dist/components/popover.d.ts +14 -11
- package/dist/components/popover.js +30 -23
- package/dist/components/popover.js.map +1 -1
- package/dist/components/progress.d.ts +2 -2
- package/dist/components/progress.js +11 -9
- package/dist/components/progress.js.map +1 -1
- package/dist/components/radio-group.d.ts +23 -4
- package/dist/components/radio-group.js +56 -14
- package/dist/components/radio-group.js.map +1 -1
- package/dist/components/reasoning.d.ts +3 -3
- package/dist/components/reasoning.js +14 -11
- package/dist/components/reasoning.js.map +1 -1
- package/dist/components/resizable.js +1 -0
- package/dist/components/resizable.js.map +1 -1
- package/dist/components/scroll-area.d.ts +4 -3
- package/dist/components/scroll-area.js +18 -14
- package/dist/components/scroll-area.js.map +1 -1
- package/dist/components/secret-input.d.ts +3 -2
- package/dist/components/secret-input.js +7 -4
- package/dist/components/secret-input.js.map +1 -1
- package/dist/components/select.d.ts +12 -7
- package/dist/components/select.js +42 -35
- package/dist/components/select.js.map +1 -1
- package/dist/components/separator.d.ts +2 -2
- package/dist/components/separator.js +8 -6
- package/dist/components/separator.js.map +1 -1
- package/dist/components/sheet.d.ts +11 -9
- package/dist/components/sheet.js +41 -29
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/skeleton.d.ts +2 -1
- package/dist/components/skeleton.js +7 -4
- package/dist/components/skeleton.js.map +1 -1
- package/dist/components/slider.d.ts +10 -6
- package/dist/components/slider.js +16 -7
- package/dist/components/slider.js.map +1 -1
- package/dist/components/source.d.ts +2 -2
- package/dist/components/source.js +10 -7
- package/dist/components/source.js.map +1 -1
- package/dist/components/spinner.d.ts +4 -2
- package/dist/components/spinner.js +5 -3
- package/dist/components/spinner.js.map +1 -1
- package/dist/components/stat-card.d.ts +27 -2
- package/dist/components/stat-card.js +70 -9
- package/dist/components/stat-card.js.map +1 -1
- package/dist/components/status.d.ts +7 -6
- package/dist/components/status.js +12 -5
- package/dist/components/status.js.map +1 -1
- package/dist/components/steps.d.ts +1 -1
- package/dist/components/steps.js +5 -3
- package/dist/components/steps.js.map +1 -1
- package/dist/components/switch.d.ts +47 -3
- package/dist/components/switch.js +89 -11
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table.d.ts +29 -12
- package/dist/components/table.js +80 -34
- package/dist/components/table.js.map +1 -1
- package/dist/components/tabs.d.ts +10 -5
- package/dist/components/tabs.js +29 -19
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/textarea.d.ts +6 -1
- package/dist/components/textarea.js +4 -3
- package/dist/components/textarea.js.map +1 -1
- package/dist/components/toast.d.ts +11 -7
- package/dist/components/toast.js +37 -27
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toaster.d.ts +4 -2
- package/dist/components/toaster.js +7 -2
- package/dist/components/toaster.js.map +1 -1
- package/dist/components/tool-call.d.ts +2 -2
- package/dist/components/tool-call.js +15 -12
- package/dist/components/tool-call.js.map +1 -1
- package/dist/components/toolbar.d.ts +9 -4
- package/dist/components/toolbar.js +15 -9
- package/dist/components/toolbar.js.map +1 -1
- package/dist/components/tooltip.d.ts +4 -3
- package/dist/components/tooltip.js +17 -13
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/tour.js +10 -8
- package/dist/components/tour.js.map +1 -1
- package/dist/components/tree-view.d.ts +1 -1
- package/dist/components/tree-view.js +10 -5
- package/dist/components/tree-view.js.map +1 -1
- package/dist/components/voice-recorder.d.ts +1 -1
- package/dist/components/voice-recorder.js +5 -2
- package/dist/components/voice-recorder.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +17 -12
- package/dist/lib/media.d.ts +18 -0
- package/dist/lib/media.js +34 -0
- package/dist/lib/media.js.map +1 -0
- package/dist/lib/utils.d.ts +32 -0
- package/dist/lib/utils.js +33 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/mui/index.js +20 -8
- package/dist/mui/index.js.map +1 -1
- package/dist/primitives/vendor/cmdk/command-score.d.ts +1 -0
- package/dist/primitives/vendor/cmdk/command-score.js +64 -0
- package/dist/primitives/vendor/cmdk/command-score.js.map +1 -0
- package/dist/primitives/vendor/cmdk/index.d.ts +384 -0
- package/dist/primitives/vendor/cmdk/index.js +687 -0
- package/dist/primitives/vendor/cmdk/index.js.map +1 -0
- package/dist/primitives/vendor/radix/number/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/number/number.d.ts +2 -0
- package/dist/primitives/vendor/radix/number/number.js +9 -0
- package/dist/primitives/vendor/radix/number/number.js.map +1 -0
- package/dist/primitives/vendor/radix/primitive/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/primitive/is-development.d.ts +1 -0
- package/dist/primitives/vendor/radix/primitive/is-development.js +7 -0
- package/dist/primitives/vendor/radix/primitive/is-development.js.map +1 -0
- package/dist/primitives/vendor/radix/primitive/primitive.d.ts +14 -0
- package/dist/primitives/vendor/radix/primitive/primitive.js +12 -0
- package/dist/primitives/vendor/radix/primitive/primitive.js.map +1 -0
- package/dist/primitives/vendor/radix/primitive/types.d.ts +5 -0
- package/dist/primitives/vendor/radix/react-accordion/accordion.d.ts +112 -0
- package/dist/primitives/vendor/radix/react-accordion/accordion.js +266 -0
- package/dist/primitives/vendor/radix/react-accordion/accordion.js.map +1 -0
- package/dist/primitives/vendor/radix/react-accordion/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-alert-dialog/alert-dialog.d.ts +49 -0
- package/dist/primitives/vendor/radix/react-alert-dialog/alert-dialog.js +122 -0
- package/dist/primitives/vendor/radix/react-alert-dialog/alert-dialog.js.map +1 -0
- package/dist/primitives/vendor/radix/react-alert-dialog/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-arrow/arrow.d.ts +9 -0
- package/dist/primitives/vendor/radix/react-arrow/arrow.js +22 -0
- package/dist/primitives/vendor/radix/react-arrow/arrow.js.map +1 -0
- package/dist/primitives/vendor/radix/react-arrow/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-avatar/avatar.d.ts +19 -0
- package/dist/primitives/vendor/radix/react-avatar/avatar.js +132 -0
- package/dist/primitives/vendor/radix/react-avatar/avatar.js.map +1 -0
- package/dist/primitives/vendor/radix/react-avatar/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-checkbox/checkbox.d.ts +47 -0
- package/dist/primitives/vendor/radix/react-checkbox/checkbox.js +197 -0
- package/dist/primitives/vendor/radix/react-checkbox/checkbox.js.map +1 -0
- package/dist/primitives/vendor/radix/react-checkbox/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-collapsible/collapsible.d.ts +31 -0
- package/dist/primitives/vendor/radix/react-collapsible/collapsible.js +133 -0
- package/dist/primitives/vendor/radix/react-collapsible/collapsible.js.map +1 -0
- package/dist/primitives/vendor/radix/react-collapsible/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-collection/collection-legacy.d.ts +21 -0
- package/dist/primitives/vendor/radix/react-collection/collection-legacy.js +84 -0
- package/dist/primitives/vendor/radix/react-collection/collection-legacy.js.map +1 -0
- package/dist/primitives/vendor/radix/react-collection/collection.d.ts +32 -0
- package/dist/primitives/vendor/radix/react-collection/index.d.ts +4 -0
- package/dist/primitives/vendor/radix/react-collection/ordered-dictionary.d.ts +51 -0
- package/dist/primitives/vendor/radix/react-compose-refs/compose-refs.d.ts +13 -0
- package/dist/primitives/vendor/radix/react-compose-refs/compose-refs.js +43 -0
- package/dist/primitives/vendor/radix/react-compose-refs/compose-refs.js.map +1 -0
- package/dist/primitives/vendor/radix/react-compose-refs/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-context/create-context.d.ts +45 -0
- package/dist/primitives/vendor/radix/react-context/create-context.js +74 -0
- package/dist/primitives/vendor/radix/react-context/create-context.js.map +1 -0
- package/dist/primitives/vendor/radix/react-context/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-context-menu/context-menu.d.ts +95 -0
- package/dist/primitives/vendor/radix/react-context-menu/context-menu.js +279 -0
- package/dist/primitives/vendor/radix/react-context-menu/context-menu.js.map +1 -0
- package/dist/primitives/vendor/radix/react-context-menu/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-dialog/dialog.d.ts +97 -0
- package/dist/primitives/vendor/radix/react-dialog/dialog.js +262 -0
- package/dist/primitives/vendor/radix/react-dialog/dialog.js.map +1 -0
- package/dist/primitives/vendor/radix/react-dialog/index.d.ts +4 -0
- package/dist/primitives/vendor/radix/react-direction/direction.d.ts +10 -0
- package/dist/primitives/vendor/radix/react-direction/direction.js +13 -0
- package/dist/primitives/vendor/radix/react-direction/direction.js.map +1 -0
- package/dist/primitives/vendor/radix/react-direction/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-dismissable-layer/dismissable-layer.d.ts +61 -0
- package/dist/primitives/vendor/radix/react-dismissable-layer/dismissable-layer.js +336 -0
- package/dist/primitives/vendor/radix/react-dismissable-layer/dismissable-layer.js.map +1 -0
- package/dist/primitives/vendor/radix/react-dismissable-layer/index.d.ts +4 -0
- package/dist/primitives/vendor/radix/react-dropdown-menu/dropdown-menu.d.ts +95 -0
- package/dist/primitives/vendor/radix/react-dropdown-menu/dropdown-menu.js +257 -0
- package/dist/primitives/vendor/radix/react-dropdown-menu/dropdown-menu.js.map +1 -0
- package/dist/primitives/vendor/radix/react-dropdown-menu/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-focus-guards/focus-guards.d.ts +11 -0
- package/dist/primitives/vendor/radix/react-focus-guards/focus-guards.js +49 -0
- package/dist/primitives/vendor/radix/react-focus-guards/focus-guards.js.map +1 -0
- package/dist/primitives/vendor/radix/react-focus-guards/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-focus-scope/focus-scope.d.ts +31 -0
- package/dist/primitives/vendor/radix/react-focus-scope/focus-scope.js +221 -0
- package/dist/primitives/vendor/radix/react-focus-scope/focus-scope.js.map +1 -0
- package/dist/primitives/vendor/radix/react-focus-scope/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-id/id.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-id/id.js +17 -0
- package/dist/primitives/vendor/radix/react-id/id.js.map +1 -0
- package/dist/primitives/vendor/radix/react-id/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-label/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-label/label.d.ts +9 -0
- package/dist/primitives/vendor/radix/react-label/label.js +21 -0
- package/dist/primitives/vendor/radix/react-label/label.js.map +1 -0
- package/dist/primitives/vendor/radix/react-menu/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-menu/menu.d.ts +173 -0
- package/dist/primitives/vendor/radix/react-menu/menu.js +806 -0
- package/dist/primitives/vendor/radix/react-menu/menu.js.map +1 -0
- package/dist/primitives/vendor/radix/react-menubar/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-menubar/menubar.d.ts +106 -0
- package/dist/primitives/vendor/radix/react-menubar/menubar.js +377 -0
- package/dist/primitives/vendor/radix/react-menubar/menubar.js.map +1 -0
- package/dist/primitives/vendor/radix/react-navigation-menu/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-navigation-menu/navigation-menu.d.ts +124 -0
- package/dist/primitives/vendor/radix/react-navigation-menu/navigation-menu.js +743 -0
- package/dist/primitives/vendor/radix/react-navigation-menu/navigation-menu.js.map +1 -0
- package/dist/primitives/vendor/radix/react-popover/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-popover/popover.d.ts +83 -0
- package/dist/primitives/vendor/radix/react-popover/popover.js +236 -0
- package/dist/primitives/vendor/radix/react-popover/popover.js.map +1 -0
- package/dist/primitives/vendor/radix/react-popper/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-popper/popper.d.ts +44 -0
- package/dist/primitives/vendor/radix/react-popper/popper.js +276 -0
- package/dist/primitives/vendor/radix/react-popper/popper.js.map +1 -0
- package/dist/primitives/vendor/radix/react-portal/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-portal/portal.d.ts +13 -0
- package/dist/primitives/vendor/radix/react-portal/portal.js +21 -0
- package/dist/primitives/vendor/radix/react-portal/portal.js.map +1 -0
- package/dist/primitives/vendor/radix/react-presence/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-presence/presence.d.ts +11 -0
- package/dist/primitives/vendor/radix/react-presence/presence.js +152 -0
- package/dist/primitives/vendor/radix/react-presence/presence.js.map +1 -0
- package/dist/primitives/vendor/radix/react-presence/use-state-machine.d.ts +10 -0
- package/dist/primitives/vendor/radix/react-presence/use-state-machine.js +12 -0
- package/dist/primitives/vendor/radix/react-presence/use-state-machine.js.map +1 -0
- package/dist/primitives/vendor/radix/react-primitive/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-primitive/primitive.d.ts +56 -0
- package/dist/primitives/vendor/radix/react-primitive/primitive.js +84 -0
- package/dist/primitives/vendor/radix/react-primitive/primitive.js.map +1 -0
- package/dist/primitives/vendor/radix/react-progress/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-progress/progress.d.ts +17 -0
- package/dist/primitives/vendor/radix/react-progress/progress.js +79 -0
- package/dist/primitives/vendor/radix/react-progress/progress.js.map +1 -0
- package/dist/primitives/vendor/radix/react-radio-group/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-radio-group/radio-group.d.ts +55 -0
- package/dist/primitives/vendor/radix/react-radio-group/radio-group.js +173 -0
- package/dist/primitives/vendor/radix/react-radio-group/radio-group.js.map +1 -0
- package/dist/primitives/vendor/radix/react-radio-group/radio.d.ts +62 -0
- package/dist/primitives/vendor/radix/react-radio-group/radio.js +150 -0
- package/dist/primitives/vendor/radix/react-radio-group/radio.js.map +1 -0
- package/dist/primitives/vendor/radix/react-roving-focus/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-roving-focus/roving-focus-group.d.ts +47 -0
- package/dist/primitives/vendor/radix/react-roving-focus/roving-focus-group.js +218 -0
- package/dist/primitives/vendor/radix/react-roving-focus/roving-focus-group.js.map +1 -0
- package/dist/primitives/vendor/radix/react-scroll-area/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-scroll-area/scroll-area.d.ts +121 -0
- package/dist/primitives/vendor/radix/react-scroll-area/scroll-area.js +713 -0
- package/dist/primitives/vendor/radix/react-scroll-area/scroll-area.js.map +1 -0
- package/dist/primitives/vendor/radix/react-scroll-area/use-state-machine.d.ts +10 -0
- package/dist/primitives/vendor/radix/react-scroll-area/use-state-machine.js +12 -0
- package/dist/primitives/vendor/radix/react-scroll-area/use-state-machine.js.map +1 -0
- package/dist/primitives/vendor/radix/react-select/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-select/select.d.ts +163 -0
- package/dist/primitives/vendor/radix/react-select/select.js +1040 -0
- package/dist/primitives/vendor/radix/react-select/select.js.map +1 -0
- package/dist/primitives/vendor/radix/react-separator/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-separator/separator.d.ts +20 -0
- package/dist/primitives/vendor/radix/react-separator/separator.js +29 -0
- package/dist/primitives/vendor/radix/react-separator/separator.js.map +1 -0
- package/dist/primitives/vendor/radix/react-slot/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-slot/slot.d.ts +29 -0
- package/dist/primitives/vendor/radix/react-slot/slot.js +107 -0
- package/dist/primitives/vendor/radix/react-slot/slot.js.map +1 -0
- package/dist/primitives/vendor/radix/react-switch/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-switch/switch.d.ts +41 -0
- package/dist/primitives/vendor/radix/react-switch/switch.js +185 -0
- package/dist/primitives/vendor/radix/react-switch/switch.js.map +1 -0
- package/dist/primitives/vendor/radix/react-tabs/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-tabs/tabs.d.ts +54 -0
- package/dist/primitives/vendor/radix/react-tabs/tabs.js +145 -0
- package/dist/primitives/vendor/radix/react-tabs/tabs.js.map +1 -0
- package/dist/primitives/vendor/radix/react-toast/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-toast/toast.d.ts +125 -0
- package/dist/primitives/vendor/radix/react-toast/toast.js +557 -0
- package/dist/primitives/vendor/radix/react-toast/toast.js.map +1 -0
- package/dist/primitives/vendor/radix/react-tooltip/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-tooltip/tooltip.d.ts +99 -0
- package/dist/primitives/vendor/radix/react-tooltip/tooltip.js +503 -0
- package/dist/primitives/vendor/radix/react-tooltip/tooltip.js.map +1 -0
- package/dist/primitives/vendor/radix/react-use-callback-ref/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-use-callback-ref/use-callback-ref.d.ts +6 -0
- package/dist/primitives/vendor/radix/react-use-callback-ref/use-callback-ref.js +18 -0
- package/dist/primitives/vendor/radix/react-use-callback-ref/use-callback-ref.js.map +1 -0
- package/dist/primitives/vendor/radix/react-use-controllable-state/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-use-controllable-state/use-controllable-state-reducer.d.ts +28 -0
- package/dist/primitives/vendor/radix/react-use-controllable-state/use-controllable-state.d.ts +11 -0
- package/dist/primitives/vendor/radix/react-use-controllable-state/use-controllable-state.js +59 -0
- package/dist/primitives/vendor/radix/react-use-controllable-state/use-controllable-state.js.map +1 -0
- package/dist/primitives/vendor/radix/react-use-effect-event/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-use-effect-event/use-effect-event.d.ts +7 -0
- package/dist/primitives/vendor/radix/react-use-is-hydrated/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-use-is-hydrated/use-is-hydrated-legacy.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-use-is-hydrated/use-is-hydrated-legacy.js +18 -0
- package/dist/primitives/vendor/radix/react-use-is-hydrated/use-is-hydrated-legacy.js.map +1 -0
- package/dist/primitives/vendor/radix/react-use-is-hydrated/use-is-hydrated.d.ts +6 -0
- package/dist/primitives/vendor/radix/react-use-is-hydrated/use-is-hydrated.js +19 -0
- package/dist/primitives/vendor/radix/react-use-is-hydrated/use-is-hydrated.js.map +1 -0
- package/dist/primitives/vendor/radix/react-use-layout-effect/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-use-layout-effect/use-layout-effect.d.ts +10 -0
- package/dist/primitives/vendor/radix/react-use-layout-effect/use-layout-effect.js +15 -0
- package/dist/primitives/vendor/radix/react-use-layout-effect/use-layout-effect.js.map +1 -0
- package/dist/primitives/vendor/radix/react-use-previous/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-use-previous/use-previous.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-use-previous/use-previous.js +20 -0
- package/dist/primitives/vendor/radix/react-use-previous/use-previous.js.map +1 -0
- package/dist/primitives/vendor/radix/react-use-size/index.d.ts +1 -0
- package/dist/primitives/vendor/radix/react-use-size/use-size.d.ts +5 -0
- package/dist/primitives/vendor/radix/react-use-size/use-size.js +42 -0
- package/dist/primitives/vendor/radix/react-use-size/use-size.js.map +1 -0
- package/dist/primitives/vendor/radix/react-visually-hidden/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/react-visually-hidden/visually-hidden.d.ts +21 -0
- package/dist/primitives/vendor/radix/react-visually-hidden/visually-hidden.js +32 -0
- package/dist/primitives/vendor/radix/react-visually-hidden/visually-hidden.js.map +1 -0
- package/dist/primitives/vendor/radix/rect/index.d.ts +2 -0
- package/dist/primitives/vendor/radix/rect/observe-element-rect.d.ts +16 -0
- package/dist/styles.css +1 -1
- package/dist/theme.css +36 -17
- package/dist/tokens.d.ts +78 -36
- package/dist/tokens.js +63 -32
- package/dist/tokens.js.map +1 -1
- package/package.json +25 -13
- package/vendor-manifest.json +1101 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-history.js","names":["_Fragment"],"sources":["../../src/components/chat-history.tsx"],"sourcesContent":["import AlertCircle from '@burtson-labs/icons/react/alert-circle';\nimport Edit from '@burtson-labs/icons/react/edit';\nimport MessageSquarePlus from '@burtson-labs/icons/react/message-square-plus';\nimport MoreHorizontal from '@burtson-labs/icons/react/more-horizontal';\nimport Pin from '@burtson-labs/icons/react/pin';\nimport PinOff from '@burtson-labs/icons/react/pin-off';\nimport Search from '@burtson-labs/icons/react/search';\nimport Trash from '@burtson-labs/icons/react/trash';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { Button } from './button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from './dropdown-menu';\nimport { fieldFocusClasses } from './input';\n\nexport interface ChatHistoryItem {\n id: string;\n title: string;\n /** When the conversation last changed; decides its group. */\n updatedAt: Date | string | number;\n pinned?: boolean;\n /** A line of the latest message, searched along with the title. */\n preview?: string;\n}\n\nexport type ChatHistoryGroupKey = 'pinned' | 'today' | 'yesterday' | 'week' | 'older';\n\nexport interface ChatHistoryGroup {\n key: ChatHistoryGroupKey;\n label: string;\n items: ChatHistoryItem[];\n}\n\nconst LABELS: Record<ChatHistoryGroupKey, string> = {\n pinned: 'Pinned',\n today: 'Today',\n yesterday: 'Yesterday',\n week: 'Previous 7 days',\n older: 'Older',\n};\n\nconst time = (d: ChatHistoryItem['updatedAt']) => new Date(d).getTime();\nconst startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();\n\n/**\n * Pinned first, then Today, Yesterday, Previous 7 days and Older by the\n * local calendar, newest first in each. Empty groups are left out.\n */\nexport function groupConversations(\n items: ChatHistoryItem[],\n now: Date = new Date(),\n): ChatHistoryGroup[] {\n const today = startOfDay(now);\n const day = 24 * 60 * 60 * 1000;\n const buckets: Record<ChatHistoryGroupKey, ChatHistoryItem[]> = {\n pinned: [],\n today: [],\n yesterday: [],\n week: [],\n older: [],\n };\n for (const item of [...items].sort((a, b) => time(b.updatedAt) - time(a.updatedAt))) {\n if (item.pinned) {\n buckets.pinned.push(item);\n continue;\n }\n const t = startOfDay(new Date(item.updatedAt));\n const key: ChatHistoryGroupKey =\n t >= today\n ? 'today'\n : t >= today - day\n ? 'yesterday'\n : t >= today - 7 * day\n ? 'week'\n : 'older';\n buckets[key].push(item);\n }\n return (Object.keys(buckets) as ChatHistoryGroupKey[])\n .filter((k) => buckets[k].length > 0)\n .map((k) => ({ key: k, label: LABELS[k], items: buckets[k] }));\n}\n\n/** Case-insensitive match on every word, across title and preview. */\nexport function matchesConversation(item: ChatHistoryItem, query: string): boolean {\n const hay = `${item.title} ${item.preview ?? ''}`.toLowerCase();\n return query\n .toLowerCase()\n .split(/\\s+/)\n .filter(Boolean)\n .every((w) => hay.includes(w));\n}\n\nexport interface ChatHistoryProps extends Omit<React.ComponentProps<'div'>, 'onSelect'> {\n items: ChatHistoryItem[];\n activeId?: string;\n onSelect: (id: string) => void;\n /** Shows a \"New chat\" button at the top. */\n onNewChat?: () => void;\n /** Adds Rename to the row menu; the title is edited in place. */\n onRename?: (id: string, title: string) => void;\n /** Adds Delete to the row menu. Confirm or offer undo in your app. */\n onDelete?: (id: string) => void;\n /** Adds Pin / Unpin to the row menu. */\n onPinChange?: (id: string, pinned: boolean) => void;\n /** Controlled search text; leave out to let the list keep its own. */\n query?: string;\n onQueryChange?: (query: string) => void;\n /** Hides the search box, e.g. when the app searches on the server. */\n searchable?: boolean;\n loading?: boolean;\n /** Why the list could not load. Shown with Retry when `onRetry` is set. */\n error?: React.ReactNode;\n onRetry?: () => void;\n /** Shown when there are no conversations at all. */\n empty?: React.ReactNode;\n /** The clock grouping uses; pass it in tests. */\n now?: Date;\n /**\n * Virtualisation hook for long histories: receives a group's items and the\n * row renderer, returns what to render (e.g. a windowed list). Default\n * renders every row.\n */\n renderItems?: (\n items: ChatHistoryItem[],\n renderItem: (item: ChatHistoryItem) => React.ReactNode,\n ) => React.ReactNode;\n label?: string;\n}\n\n/**\n * The conversation list beside a chat: a New chat action, search, and the\n * history grouped by day with pinned chats on top. Up and Down move between\n * rows, Home and End jump, Enter opens; each row has a menu for rename, pin\n * and delete. The app owns the data; this only reports what the person did.\n */\nfunction ChatHistory({\n items,\n activeId,\n onSelect,\n onNewChat,\n onRename,\n onDelete,\n onPinChange,\n query: controlledQuery,\n onQueryChange,\n searchable = true,\n loading = false,\n error,\n onRetry,\n empty,\n now,\n renderItems,\n label = 'Chat history',\n className,\n ...props\n}: ChatHistoryProps) {\n const [ownQuery, setOwnQuery] = React.useState('');\n const query = controlledQuery ?? ownQuery;\n const setQuery = (q: string) => {\n if (controlledQuery === undefined) setOwnQuery(q);\n onQueryChange?.(q);\n };\n const [renaming, setRenaming] = React.useState<string | null>(null);\n const list = React.useRef<HTMLDivElement>(null);\n const searchId = React.useId();\n\n const visible = query.trim() ? items.filter((i) => matchesConversation(i, query)) : items;\n const groups = groupConversations(visible, now);\n\n const onRowKeyDown = (e: React.KeyboardEvent) => {\n const rows = Array.from(\n list.current?.querySelectorAll<HTMLElement>('[data-slot=\"chat-history-row\"]') ?? [],\n );\n const i = rows.indexOf(document.activeElement as HTMLElement);\n let next = -1;\n if (e.key === 'ArrowDown') next = Math.min(rows.length - 1, i + 1);\n else if (e.key === 'ArrowUp') next = Math.max(0, i - 1);\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = rows.length - 1;\n const row = next >= 0 ? rows[next] : undefined;\n if (row) {\n e.preventDefault();\n row.focus();\n }\n };\n\n const renderItem = (item: ChatHistoryItem) => (\n <HistoryRow\n key={item.id}\n item={item}\n active={item.id === activeId}\n renaming={renaming === item.id}\n onSelect={() => onSelect(item.id)}\n onStartRename={onRename ? () => setRenaming(item.id) : undefined}\n onRename={(title) => {\n setRenaming(null);\n if (onRename && title.trim() && title.trim() !== item.title)\n onRename(item.id, title.trim());\n }}\n onDelete={onDelete ? () => onDelete(item.id) : undefined}\n onPinChange={onPinChange ? (p) => onPinChange(item.id, p) : undefined}\n onKeyDown={onRowKeyDown}\n />\n );\n\n return (\n <div\n data-slot=\"chat-history\"\n aria-label={label}\n role=\"navigation\"\n className={cn('flex h-full min-h-0 flex-col', className)}\n {...props}\n >\n {(onNewChat || searchable) && (\n <div className=\"grid gap-2 p-2\">\n {onNewChat && (\n <Button variant=\"outline\" className=\"justify-start\" onClick={onNewChat}>\n <MessageSquarePlus /> New chat\n </Button>\n )}\n {searchable && (\n <label htmlFor={searchId} className=\"relative block\">\n <span className=\"sr-only\">Search chats</span>\n <Search\n aria-hidden\n className=\"pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground\"\n />\n <input\n id={searchId}\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search chats\"\n className={cn(\n 'h-8 w-full rounded-md border border-input bg-surface pr-2 pl-8 text-base placeholder:text-muted-foreground sm:text-sm',\n fieldFocusClasses,\n )}\n />\n </label>\n )}\n </div>\n )}\n <div ref={list} className=\"min-h-0 flex-1 overflow-y-auto px-2 pb-2\">\n {loading ? (\n <div role=\"status\" aria-label=\"Loading chats\" className=\"grid gap-1.5 pt-1\">\n {[72, 56, 64, 48, 60].map((w, i) => (\n <div key={i} className=\"h-8 rounded-md bg-muted/70 motion-safe:animate-pulse\">\n <span className=\"sr-only\">Loading</span>\n <span\n aria-hidden\n className=\"ml-2 inline-block h-2.5 translate-y-3 rounded bg-muted-foreground/15\"\n style={{ width: `${w}%` }}\n />\n </div>\n ))}\n </div>\n ) : error ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2 p-2 text-sm\">\n <span className=\"inline-flex items-center gap-1.5 font-medium text-destructive [&_svg]:size-4\">\n <AlertCircle aria-hidden /> Chats could not load\n </span>\n <span className=\"text-muted-foreground\">{error}</span>\n {onRetry && (\n <Button size=\"sm\" variant=\"outline\" onClick={onRetry}>\n Try again\n </Button>\n )}\n </div>\n ) : items.length === 0 ? (\n <div className=\"p-3 text-sm text-muted-foreground\">\n {empty ?? 'No chats yet. Start one and it will appear here.'}\n </div>\n ) : groups.length === 0 ? (\n <p role=\"status\" className=\"p-3 text-sm text-muted-foreground\">\n No chats match “{query}”.\n </p>\n ) : (\n groups.map((g) => (\n <section key={g.key} aria-label={g.label} className=\"mt-2 first:mt-0\">\n <h3 className=\"px-2 pt-2 pb-1 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase\">\n {g.label}\n </h3>\n <ul className=\"grid grid-cols-[minmax(0,1fr)] gap-0.5\">\n {renderItems ? renderItems(g.items, renderItem) : g.items.map(renderItem)}\n </ul>\n </section>\n ))\n )}\n </div>\n </div>\n );\n}\n\nfunction HistoryRow({\n item,\n active,\n renaming,\n onSelect,\n onStartRename,\n onRename,\n onDelete,\n onPinChange,\n onKeyDown,\n}: {\n item: ChatHistoryItem;\n active: boolean;\n renaming: boolean;\n onSelect: () => void;\n onStartRename?: () => void;\n onRename: (title: string) => void;\n onDelete?: () => void;\n onPinChange?: (pinned: boolean) => void;\n onKeyDown: (e: React.KeyboardEvent) => void;\n}) {\n const hasMenu = Boolean(onStartRename || onDelete || onPinChange);\n if (renaming) return <RenameRow title={item.title} onDone={onRename} />;\n return (\n <li className=\"group/row relative min-w-0\">\n <button\n type=\"button\"\n data-slot=\"chat-history-row\"\n aria-current={active ? 'page' : undefined}\n onClick={onSelect}\n onKeyDown={onKeyDown}\n className={cn(\n 'flex h-8 w-full items-center gap-2 rounded-md px-2 text-left text-sm outline-none transition-colors hover:bg-muted focus-visible:ring-[3px] focus-visible:ring-ring/20 pointer-coarse:h-11',\n hasMenu && 'pr-9',\n active && 'bg-accent font-medium text-accent-foreground hover:bg-accent',\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">{item.title || 'Untitled chat'}</span>\n </button>\n {hasMenu && (\n <DropdownMenu>\n <DropdownMenuTrigger\n aria-label={`Actions for ${item.title || 'chat'}`}\n className={cn(\n 'absolute top-1/2 right-1 grid size-7 -translate-y-1/2 place-items-center rounded-sm text-muted-foreground opacity-0 outline-none transition-opacity hover:bg-background hover:text-foreground focus-visible:opacity-100 focus-visible:ring-[3px] focus-visible:ring-ring/20 group-hover/row:opacity-100 data-[state=open]:opacity-100 pointer-coarse:size-9 pointer-coarse:opacity-100 [&_svg]:size-4',\n active && 'opacity-100',\n )}\n >\n <MoreHorizontal aria-hidden />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-40\">\n {onStartRename && (\n <DropdownMenuItem onSelect={onStartRename}>\n <Edit aria-hidden /> Rename\n </DropdownMenuItem>\n )}\n {onPinChange && (\n <DropdownMenuItem onSelect={() => onPinChange(!item.pinned)}>\n {item.pinned ? <PinOff aria-hidden /> : <Pin aria-hidden />}\n {item.pinned ? 'Unpin' : 'Pin'}\n </DropdownMenuItem>\n )}\n {onDelete && (\n <>\n {(onStartRename || onPinChange) && <DropdownMenuSeparator />}\n <DropdownMenuItem variant=\"destructive\" onSelect={onDelete}>\n <Trash aria-hidden /> Delete\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </li>\n );\n}\n\n/** Rename in place: Enter or leaving the field saves, Escape keeps the old title. */\nfunction RenameRow({ title, onDone }: { title: string; onDone: (title: string) => void }) {\n const [draft, setDraft] = React.useState(title);\n const input = React.useRef<HTMLInputElement>(null);\n React.useEffect(() => {\n input.current?.select();\n }, []);\n return (\n <li className=\"px-1 py-0.5\">\n <input\n ref={input}\n aria-label=\"Chat title\"\n value={draft}\n // eslint-disable-next-line jsx-a11y/no-autofocus -- rename was just asked for\n autoFocus\n onChange={(e) => setDraft(e.target.value)}\n onBlur={() => onDone(draft)}\n onKeyDown={(e) => {\n if (e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229) return;\n if (e.key === 'Enter') {\n e.preventDefault();\n onDone(draft);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n onDone(title);\n }\n }}\n className=\"h-8 w-full rounded-md border border-ring bg-surface px-2 text-base inset-ring-1 inset-ring-ring outline-hidden! sm:text-sm\"\n />\n </li>\n );\n}\n\nexport { ChatHistory };\n"],"mappings":";;;;;;;;;;;;;;;AAwCA,IAAM,SAA8C;CAClD,QAAQ;CACR,OAAO;CACP,WAAW;CACX,MAAM;CACN,OAAO;AACT;AAEA,IAAM,QAAQ,MAAoC,IAAI,KAAK,CAAC,CAAC,CAAC,QAAQ;AACtE,IAAM,cAAc,MAAY,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ;;;;;AAM7F,SAAgB,mBACd,OACA,sBAAY,IAAI,KAAK,GACD;CACpB,MAAM,QAAQ,WAAW,GAAG;CAC5B,MAAM,MAAM;CACZ,MAAM,UAA0D;EAC9D,QAAQ,CAAC;EACT,OAAO,CAAC;EACR,WAAW,CAAC;EACZ,MAAM,CAAC;EACP,OAAO,CAAC;CACV;CACA,KAAK,MAAM,QAAQ,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,KAAK,EAAE,SAAS,IAAI,KAAK,EAAE,SAAS,CAAC,GAAG;EACnF,IAAI,KAAK,QAAQ;GACf,QAAQ,OAAO,KAAK,IAAI;GACxB;EACF;EACA,MAAM,IAAI,WAAW,IAAI,KAAK,KAAK,SAAS,CAAC;EAS7C,QAPE,KAAK,QACD,UACA,KAAK,QAAQ,MACX,cACA,KAAK,QAAQ,IAAI,MACf,SACA,QACE,CAAC,KAAK,IAAI;CACxB;CACA,OAAQ,OAAO,KAAK,OAAO,CAAC,CACzB,QAAQ,MAAM,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CACpC,KAAK,OAAO;EAAE,KAAK;EAAG,OAAO,OAAO;EAAI,OAAO,QAAQ;CAAG,EAAE;AACjE;;AAGA,SAAgB,oBAAoB,MAAuB,OAAwB;CACjF,MAAM,MAAM,GAAG,KAAK,MAAM,GAAG,KAAK,WAAW,KAAK,YAAY;CAC9D,OAAO,MACJ,YAAY,CAAC,CACb,MAAM,KAAK,CAAC,CACZ,OAAO,OAAO,CAAC,CACf,OAAO,MAAM,IAAI,SAAS,CAAC,CAAC;AACjC;;;;;;;AA6CA,SAAS,YAAY,EACnB,OACA,UACA,UACA,WACA,UACA,UACA,aACA,OAAO,iBACP,eACA,aAAa,MACb,UAAU,OACV,OACA,SACA,OACA,KACA,aACA,QAAQ,gBACR,WACA,GAAG,SACgB;CACnB,MAAM,CAAC,UAAU,eAAe,MAAM,SAAS,EAAE;CACjD,MAAM,QAAQ,mBAAmB;CACjC,MAAM,YAAY,MAAc;EAC9B,IAAI,oBAAoB,KAAA,GAAW,YAAY,CAAC;EAChD,gBAAgB,CAAC;CACnB;CACA,MAAM,CAAC,UAAU,eAAe,MAAM,SAAwB,IAAI;CAClE,MAAM,OAAO,MAAM,OAAuB,IAAI;CAC9C,MAAM,WAAW,MAAM,MAAM;CAG7B,MAAM,SAAS,mBADC,MAAM,KAAK,IAAI,MAAM,QAAQ,MAAM,oBAAoB,GAAG,KAAK,CAAC,IAAI,OACzC,GAAG;CAE9C,MAAM,gBAAgB,MAA2B;EAC/C,MAAM,OAAO,MAAM,KACjB,KAAK,SAAS,iBAA8B,kCAAgC,KAAK,CAAC,CACpF;EACA,MAAM,IAAI,KAAK,QAAQ,SAAS,aAA4B;EAC5D,IAAI,OAAO;EACX,IAAI,EAAE,QAAQ,aAAa,OAAO,KAAK,IAAI,KAAK,SAAS,GAAG,IAAI,CAAC;OAC5D,IAAI,EAAE,QAAQ,WAAW,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;OACjD,IAAI,EAAE,QAAQ,QAAQ,OAAO;OAC7B,IAAI,EAAE,QAAQ,OAAO,OAAO,KAAK,SAAS;EAC/C,MAAM,MAAM,QAAQ,IAAI,KAAK,QAAQ,KAAA;EACrC,IAAI,KAAK;GACP,EAAE,eAAe;GACjB,IAAI,MAAM;EACZ;CACF;CAEA,MAAM,cAAc,SAClB,oBAAC,YAAD;EAEQ;EACN,QAAQ,KAAK,OAAO;EACpB,UAAU,aAAa,KAAK;EAC5B,gBAAgB,SAAS,KAAK,EAAE;EAChC,eAAe,iBAAiB,YAAY,KAAK,EAAE,IAAI,KAAA;EACvD,WAAW,UAAU;GACnB,YAAY,IAAI;GAChB,IAAI,YAAY,MAAM,KAAK,KAAK,MAAM,KAAK,MAAM,KAAK,OACpD,SAAS,KAAK,IAAI,MAAM,KAAK,CAAC;EAClC;EACA,UAAU,iBAAiB,SAAS,KAAK,EAAE,IAAI,KAAA;EAC/C,aAAa,eAAe,MAAM,YAAY,KAAK,IAAI,CAAC,IAAI,KAAA;EAC5D,WAAW;CACZ,GAdM,KAAK,EAcX;CAGH,OACE,qBAAC,OAAD;EACE,aAAU;EACV,cAAY;EACZ,MAAK;EACL,WAAW,GAAG,gCAAgC,SAAS;EACvD,GAAI;EALN,UAAA,EAOI,aAAa,eACb,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACG,aACC,qBAAC,QAAD;IAAQ,SAAQ;IAAU,WAAU;IAAgB,SAAS;IAA7D,UAAA,CACE,oBAAC,mBAAD,CAAoB,CAAA,GAAC,WACf;GAET,CAAA,GAAA,cACC,qBAAC,SAAD;IAAO,SAAS;IAAU,WAAU;IAApC,UAAA;KACE,oBAAC,QAAD;MAAM,WAAU;MAAU,UAAA;KAAkB,CAAA;KAC5C,oBAAC,QAAD;MACE,eAAA;MACA,WAAU;KACX,CAAA;KACD,oBAAC,SAAD;MACE,IAAI;MACJ,MAAK;MACL,OAAO;MACP,WAAW,MAAM,SAAS,EAAE,OAAO,KAAK;MACxC,aAAY;MACZ,WAAW,GACT,yHAAA,sJAEF;KACD,CAAA;IACI;GAEN,CAAA,CAAA;EAEP,CAAA,GAAA,oBAAC,OAAD;GAAK,KAAK;GAAM,WAAU;GACvB,UAAA,UACC,oBAAC,OAAD;IAAK,MAAK;IAAS,cAAW;IAAgB,WAAU;IACrD,UAAA;KAAC;KAAI;KAAI;KAAI;KAAI;IAAE,CAAC,CAAC,KAAK,GAAG,MAC5B,qBAAC,OAAD;KAAa,WAAU;KAAvB,UAAA,CACE,oBAAC,QAAD;MAAM,WAAU;MAAU,UAAA;KAAa,CAAA,GACvC,oBAAC,QAAD;MACE,eAAA;MACA,WAAU;MACV,OAAO,EAAE,OAAO,GAAG,EAAE,GAAG;KACzB,CAAA,CACE;IAPK,GAAA,CAOL,CACN;GACE,CAAA,IACH,QACF,qBAAC,OAAD;IAAK,MAAK;IAAQ,WAAU;IAA5B,UAAA;KACE,qBAAC,QAAD;MAAM,WAAU;MAAhB,UAAA,CACE,oBAAC,aAAD,EAAa,eAAA,KAAa,CAAA,GAAC,uBACvB;;KACN,oBAAC,QAAD;MAAM,WAAU;MAAyB,UAAA;KAAY,CAAA;KACpD,WACC,oBAAC,QAAD;MAAQ,MAAK;MAAK,SAAQ;MAAU,SAAS;MAAS,UAAA;KAE9C,CAAA;IAEP;GACH,CAAA,IAAA,MAAM,WAAW,IACnB,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,SAAS;GACP,CAAA,IACH,OAAO,WAAW,IACpB,qBAAC,KAAD;IAAG,MAAK;IAAS,WAAU;IAA3B,UAAA;KAA+D;KACtC;KAAM;IAC5B;GAEH,CAAA,IAAA,OAAO,KAAK,MACV,qBAAC,WAAD;IAAqB,cAAY,EAAE;IAAO,WAAU;IAApD,UAAA,CACE,oBAAC,MAAD;KAAI,WAAU;KACX,UAAA,EAAE;IACD,CAAA,GACJ,oBAAC,MAAD;KAAI,WAAU;KACX,UAAA,cAAc,YAAY,EAAE,OAAO,UAAU,IAAI,EAAE,MAAM,IAAI,UAAU;IACtE,CAAA,CACG;GAPK,GAAA,EAAE,GAOP,CACV;EAEA,CAAA,CACF;;AAET;AAEA,SAAS,WAAW,EAClB,MACA,QACA,UACA,UACA,eACA,UACA,UACA,aACA,aAWC;CACD,MAAM,UAAU,QAAQ,iBAAiB,YAAY,WAAW;CAChE,IAAI,UAAU,OAAO,oBAAC,WAAD;EAAW,OAAO,KAAK;EAAO,QAAQ;CAAW,CAAA;CACtE,OACE,qBAAC,MAAD;EAAI,WAAU;EAAd,UAAA,CACE,oBAAC,UAAD;GACE,MAAK;GACL,aAAU;GACV,gBAAc,SAAS,SAAS,KAAA;GAChC,SAAS;GACE;GACX,WAAW,GACT,8LACA,WAAW,QACX,UAAU,8DACZ;GAEA,UAAA,oBAAC,QAAD;IAAM,WAAU;IAA2B,UAAA,KAAK,SAAS;GAAsB,CAAA;EACzE,CAAA,GACP,WACC,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,qBAAD;GACE,cAAY,eAAe,KAAK,SAAS;GACzC,WAAW,GACT,yYACA,UAAU,aACZ;GAEA,UAAA,oBAAC,gBAAD,EAAgB,eAAA,KAAa,CAAA;EACV,CAAA,GACrB,qBAAC,qBAAD;GAAqB,OAAM;GAAM,WAAU;GAA3C,UAAA;IACG,iBACC,qBAAC,kBAAD;KAAkB,UAAU;KAA5B,UAAA,CACE,oBAAC,MAAD,EAAM,eAAA,KAAa,CAAA,GAAC,SACJ;;IAEnB,eACC,qBAAC,kBAAD;KAAkB,gBAAgB,YAAY,CAAC,KAAK,MAAM;KAA1D,UAAA,CACG,KAAK,SAAS,oBAAC,QAAD,EAAQ,eAAA,KAAa,CAAA,IAAI,oBAAC,KAAD,EAAK,eAAA,KAAa,CAAA,GACzD,KAAK,SAAS,UAAU,KACT;;IAEnB,YACC,qBAAAA,UAAA,EAAA,UAAA,EACI,iBAAiB,gBAAgB,oBAAC,uBAAD,CAAwB,CAAA,GAC3D,qBAAC,kBAAD;KAAkB,SAAQ;KAAc,UAAU;KAAlD,UAAA,CACE,oBAAC,OAAD,EAAO,eAAA,KAAa,CAAA,GAAC,SACL;IAClB,CAAA,CAAA,EAAA,CAAA;GAEe;EACT,CAAA,CAAA,EAAA,CAAA,CAEd;;AAER;;AAGA,SAAS,UAAU,EAAE,OAAO,UAA8D;CACxF,MAAM,CAAC,OAAO,YAAY,MAAM,SAAS,KAAK;CAC9C,MAAM,QAAQ,MAAM,OAAyB,IAAI;CACjD,MAAM,gBAAgB;EACpB,MAAM,SAAS,OAAO;CACxB,GAAG,CAAC,CAAC;CACL,OACE,oBAAC,MAAD;EAAI,WAAU;EACZ,UAAA,oBAAC,SAAD;GACE,KAAK;GACL,cAAW;GACX,OAAO;GAEP,WAAA;GACA,WAAW,MAAM,SAAS,EAAE,OAAO,KAAK;GACxC,cAAc,OAAO,KAAK;GAC1B,YAAY,MAAM;IAChB,IAAI,EAAE,YAAY,eAAe,EAAE,YAAY,YAAY,KAAK;IAChE,IAAI,EAAE,QAAQ,SAAS;KACrB,EAAE,eAAe;KACjB,OAAO,KAAK;IACd,OAAO,IAAI,EAAE,QAAQ,UAAU;KAC7B,EAAE,eAAe;KACjB,OAAO,KAAK;IACd;GACF;GACA,WAAU;EACX,CAAA;CACC,CAAA;AAER"}
|
|
1
|
+
{"version":3,"file":"chat-history.js","names":["_Fragment"],"sources":["../../src/components/chat-history.tsx"],"sourcesContent":["import AlertCircle from '@burtson-labs/icons/react/alert-circle';\nimport Edit from '@burtson-labs/icons/react/edit';\nimport MessageSquarePlus from '@burtson-labs/icons/react/message-square-plus';\nimport MoreHorizontal from '@burtson-labs/icons/react/more-horizontal';\nimport Pin from '@burtson-labs/icons/react/pin';\nimport PinOff from '@burtson-labs/icons/react/pin-off';\nimport Search from '@burtson-labs/icons/react/search';\nimport Trash from '@burtson-labs/icons/react/trash';\nimport * as React from 'react';\n\nimport { cn, focusRingClasses, noOutlineClasses, touchTargetClasses } from '../lib/utils';\n\nimport { Button } from './button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from './dropdown-menu';\nimport { fieldFocusClasses } from './input';\n\nexport interface ChatHistoryItem {\n id: string;\n title: string;\n /** When the conversation last changed; decides its group. */\n updatedAt: Date | string | number;\n pinned?: boolean;\n /** A line of the latest message, searched along with the title. */\n preview?: string;\n}\n\nexport type ChatHistoryGroupKey = 'pinned' | 'today' | 'yesterday' | 'week' | 'older';\n\nexport interface ChatHistoryGroup {\n key: ChatHistoryGroupKey;\n label: string;\n items: ChatHistoryItem[];\n}\n\nconst LABELS: Record<ChatHistoryGroupKey, string> = {\n pinned: 'Pinned',\n today: 'Today',\n yesterday: 'Yesterday',\n week: 'Previous 7 days',\n older: 'Older',\n};\n\nconst time = (d: ChatHistoryItem['updatedAt']) => new Date(d).getTime();\nconst startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();\n\n/**\n * Pinned first, then Today, Yesterday, Previous 7 days and Older by the\n * local calendar, newest first in each. Empty groups are left out.\n */\nexport function groupConversations(\n items: ChatHistoryItem[],\n now: Date = new Date(),\n): ChatHistoryGroup[] {\n const today = startOfDay(now);\n const day = 24 * 60 * 60 * 1000;\n const buckets: Record<ChatHistoryGroupKey, ChatHistoryItem[]> = {\n pinned: [],\n today: [],\n yesterday: [],\n week: [],\n older: [],\n };\n for (const item of [...items].sort((a, b) => time(b.updatedAt) - time(a.updatedAt))) {\n if (item.pinned) {\n buckets.pinned.push(item);\n continue;\n }\n const t = startOfDay(new Date(item.updatedAt));\n const key: ChatHistoryGroupKey =\n t >= today\n ? 'today'\n : t >= today - day\n ? 'yesterday'\n : t >= today - 7 * day\n ? 'week'\n : 'older';\n buckets[key].push(item);\n }\n return (Object.keys(buckets) as ChatHistoryGroupKey[])\n .filter((k) => buckets[k].length > 0)\n .map((k) => ({ key: k, label: LABELS[k], items: buckets[k] }));\n}\n\n/** Case-insensitive match on every word, across title and preview. */\nexport function matchesConversation(item: ChatHistoryItem, query: string): boolean {\n const hay = `${item.title} ${item.preview ?? ''}`.toLowerCase();\n return query\n .toLowerCase()\n .split(/\\s+/)\n .filter(Boolean)\n .every((w) => hay.includes(w));\n}\n\nexport interface ChatHistoryProps extends Omit<React.ComponentProps<'div'>, 'onSelect'> {\n items: ChatHistoryItem[];\n activeId?: string;\n onSelect: (id: string) => void;\n /** Shows a \"New chat\" button at the top. */\n onNewChat?: () => void;\n /** Adds Rename to the row menu; the title is edited in place. */\n onRename?: (id: string, title: string) => void;\n /** Adds Delete to the row menu. Confirm or offer undo in your app. */\n onDelete?: (id: string) => void;\n /** Adds Pin / Unpin to the row menu. */\n onPinChange?: (id: string, pinned: boolean) => void;\n /** Controlled search text; leave out to let the list keep its own. */\n query?: string;\n onQueryChange?: (query: string) => void;\n /** Hides the search box, e.g. when the app searches on the server. */\n searchable?: boolean;\n loading?: boolean;\n /** Why the list could not load. Shown with Retry when `onRetry` is set. */\n error?: React.ReactNode;\n onRetry?: () => void;\n /** Shown when there are no conversations at all. */\n empty?: React.ReactNode;\n /** The clock grouping uses; pass it in tests. */\n now?: Date;\n /**\n * Virtualisation hook for long histories: receives a group's items and the\n * row renderer, returns what to render (e.g. a windowed list). Default\n * renders every row.\n */\n renderItems?: (\n items: ChatHistoryItem[],\n renderItem: (item: ChatHistoryItem) => React.ReactNode,\n ) => React.ReactNode;\n label?: string;\n}\n\n/**\n * The conversation list beside a chat: a New chat action, search, and the\n * history grouped by day with pinned chats on top. Up and Down move between\n * rows, Home and End jump, Enter opens; each row has a menu for rename, pin\n * and delete. The app owns the data; this only reports what the person did.\n */\nconst ChatHistory = React.forwardRef<HTMLDivElement, ChatHistoryProps>(function ChatHistory(\n {\n items,\n activeId,\n onSelect,\n onNewChat,\n onRename,\n onDelete,\n onPinChange,\n query: controlledQuery,\n onQueryChange,\n searchable = true,\n loading = false,\n error,\n onRetry,\n empty,\n now,\n renderItems,\n label = 'Chat history',\n className,\n ...props\n },\n ref,\n) {\n const [ownQuery, setOwnQuery] = React.useState('');\n const query = controlledQuery ?? ownQuery;\n const setQuery = (q: string) => {\n if (controlledQuery === undefined) setOwnQuery(q);\n onQueryChange?.(q);\n };\n const [renaming, setRenaming] = React.useState<string | null>(null);\n const list = React.useRef<HTMLDivElement>(null);\n const searchId = React.useId();\n\n const visible = query.trim() ? items.filter((i) => matchesConversation(i, query)) : items;\n const groups = groupConversations(visible, now);\n\n const onRowKeyDown = (e: React.KeyboardEvent) => {\n const rows = Array.from(\n list.current?.querySelectorAll<HTMLElement>('[data-slot=\"chat-history-row\"]') ?? [],\n );\n const i = rows.indexOf(document.activeElement as HTMLElement);\n let next = -1;\n if (e.key === 'ArrowDown') next = Math.min(rows.length - 1, i + 1);\n else if (e.key === 'ArrowUp') next = Math.max(0, i - 1);\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = rows.length - 1;\n const row = next >= 0 ? rows[next] : undefined;\n if (row) {\n e.preventDefault();\n row.focus();\n }\n };\n\n const renderItem = (item: ChatHistoryItem) => (\n <HistoryRow\n key={item.id}\n item={item}\n active={item.id === activeId}\n renaming={renaming === item.id}\n onSelect={() => onSelect(item.id)}\n onStartRename={onRename ? () => setRenaming(item.id) : undefined}\n onRename={(title) => {\n setRenaming(null);\n if (onRename && title.trim() && title.trim() !== item.title)\n onRename(item.id, title.trim());\n }}\n onDelete={onDelete ? () => onDelete(item.id) : undefined}\n onPinChange={onPinChange ? (p) => onPinChange(item.id, p) : undefined}\n onKeyDown={onRowKeyDown}\n />\n );\n\n return (\n <div\n ref={ref}\n data-slot=\"chat-history\"\n aria-label={label}\n role=\"navigation\"\n className={cn('flex h-full min-h-0 flex-col', className)}\n {...props}\n >\n {(onNewChat || searchable) && (\n <div className=\"grid gap-2 p-2\">\n {onNewChat && (\n <Button variant=\"outline\" className=\"justify-start\" onClick={onNewChat}>\n <MessageSquarePlus /> New chat\n </Button>\n )}\n {searchable && (\n <label htmlFor={searchId} className=\"relative block\">\n <span className=\"sr-only\">Search chats</span>\n <Search\n aria-hidden\n className=\"pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground\"\n />\n <input\n id={searchId}\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search chats\"\n className={cn(\n 'h-8 w-full rounded-md border border-input bg-surface pr-2 pl-8 text-base placeholder:text-muted-foreground sm:text-sm pointer-coarse:h-11',\n fieldFocusClasses,\n )}\n />\n </label>\n )}\n </div>\n )}\n <div ref={list} className=\"min-h-0 flex-1 overflow-y-auto px-2 pb-2\">\n {loading ? (\n <div role=\"status\" aria-label=\"Loading chats\" className=\"grid gap-1.5 pt-1\">\n {[72, 56, 64, 48, 60].map((w, i) => (\n <div key={i} className=\"h-8 rounded-md bg-muted/70 motion-safe:animate-pulse\">\n <span className=\"sr-only\">Loading</span>\n <span\n aria-hidden\n className=\"ml-2 inline-block h-2.5 translate-y-3 rounded bg-muted-foreground/15\"\n style={{ width: `${w}%` }}\n />\n </div>\n ))}\n </div>\n ) : error ? (\n <div role=\"alert\" className=\"grid justify-items-start gap-2 p-2 text-sm\">\n <span className=\"inline-flex items-center gap-1.5 font-medium text-destructive [&_svg]:size-4\">\n <AlertCircle aria-hidden /> Chats could not load\n </span>\n <span className=\"text-muted-foreground\">{error}</span>\n {onRetry && (\n <Button size=\"sm\" variant=\"outline\" onClick={onRetry}>\n Try again\n </Button>\n )}\n </div>\n ) : items.length === 0 ? (\n <div className=\"p-3 text-sm text-muted-foreground\">\n {empty ?? 'No chats yet. Start one and it will appear here.'}\n </div>\n ) : groups.length === 0 ? (\n <p role=\"status\" className=\"p-3 text-sm text-muted-foreground\">\n No chats match “{query}”.\n </p>\n ) : (\n groups.map((g) => (\n <section key={g.key} aria-label={g.label} className=\"mt-2 first:mt-0\">\n <h3 className=\"px-2 pt-2 pb-1 text-[11px] font-semibold tracking-[0.06em] text-muted-foreground uppercase\">\n {g.label}\n </h3>\n <ul className=\"grid grid-cols-[minmax(0,1fr)] gap-0.5\">\n {renderItems ? renderItems(g.items, renderItem) : g.items.map(renderItem)}\n </ul>\n </section>\n ))\n )}\n </div>\n </div>\n );\n});\n\nfunction HistoryRow({\n item,\n active,\n renaming,\n onSelect,\n onStartRename,\n onRename,\n onDelete,\n onPinChange,\n onKeyDown,\n}: {\n item: ChatHistoryItem;\n active: boolean;\n renaming: boolean;\n onSelect: () => void;\n onStartRename?: () => void;\n onRename: (title: string) => void;\n onDelete?: () => void;\n onPinChange?: (pinned: boolean) => void;\n onKeyDown: (e: React.KeyboardEvent) => void;\n}) {\n const hasMenu = Boolean(onStartRename || onDelete || onPinChange);\n if (renaming) return <RenameRow title={item.title} onDone={onRename} />;\n return (\n <li className=\"group/row relative min-w-0\">\n <button\n type=\"button\"\n data-slot=\"chat-history-row\"\n aria-current={active ? 'page' : undefined}\n onClick={onSelect}\n onKeyDown={onKeyDown}\n className={cn(\n 'flex h-8 w-full items-center gap-2 rounded-md px-2 text-left text-sm transition-colors hover:bg-muted pointer-coarse:h-11',\n focusRingClasses,\n 'focus-visible:outline-offset-[-2px]',\n hasMenu && 'pr-9',\n active && 'bg-accent font-medium text-accent-foreground hover:bg-accent',\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">{item.title || 'Untitled chat'}</span>\n </button>\n {hasMenu && (\n <DropdownMenu>\n <DropdownMenuTrigger\n aria-label={`Actions for ${item.title || 'chat'}`}\n className={cn(\n 'absolute top-1/2 right-1 grid size-7 -translate-y-1/2 place-items-center rounded-sm text-muted-foreground opacity-0 transition-opacity hover:bg-background hover:text-foreground focus-visible:opacity-100 group-hover/row:opacity-100 data-[state=open]:opacity-100 pointer-coarse:size-9 pointer-coarse:opacity-100 [&_svg]:size-4',\n focusRingClasses,\n touchTargetClasses,\n 'absolute',\n active && 'opacity-100',\n )}\n >\n <MoreHorizontal aria-hidden />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-40\">\n {onStartRename && (\n <DropdownMenuItem onSelect={onStartRename}>\n <Edit aria-hidden /> Rename\n </DropdownMenuItem>\n )}\n {onPinChange && (\n <DropdownMenuItem onSelect={() => onPinChange(!item.pinned)}>\n {item.pinned ? <PinOff aria-hidden /> : <Pin aria-hidden />}\n {item.pinned ? 'Unpin' : 'Pin'}\n </DropdownMenuItem>\n )}\n {onDelete && (\n <>\n {(onStartRename || onPinChange) && <DropdownMenuSeparator />}\n <DropdownMenuItem variant=\"destructive\" onSelect={onDelete}>\n <Trash aria-hidden /> Delete\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </li>\n );\n}\n\n/** Rename in place: Enter or leaving the field saves, Escape keeps the old title. */\nfunction RenameRow({ title, onDone }: { title: string; onDone: (title: string) => void }) {\n const [draft, setDraft] = React.useState(title);\n const input = React.useRef<HTMLInputElement>(null);\n React.useEffect(() => {\n input.current?.select();\n }, []);\n return (\n <li className=\"px-1 py-0.5\">\n <input\n ref={input}\n aria-label=\"Chat title\"\n value={draft}\n // eslint-disable-next-line jsx-a11y/no-autofocus -- rename was just asked for\n autoFocus\n onChange={(e) => setDraft(e.target.value)}\n onBlur={() => onDone(draft)}\n onKeyDown={(e) => {\n if (e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229) return;\n if (e.key === 'Enter') {\n e.preventDefault();\n onDone(draft);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n onDone(title);\n }\n }}\n className={cn(\n 'h-8 w-full rounded-md border border-ring bg-surface px-2 text-base inset-ring-1 inset-ring-ring sm:text-sm',\n noOutlineClasses,\n )}\n />\n </li>\n );\n}\n\nexport { ChatHistory };\n"],"mappings":"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"}
|
|
@@ -38,5 +38,5 @@ export interface ChatLayoutProps extends Omit<React.ComponentProps<'div'>, 'titl
|
|
|
38
38
|
* details column, and a Full screen mode. Put a Conversation and a Composer
|
|
39
39
|
* in `children`; the layout stacks them and keeps the composer at the bottom.
|
|
40
40
|
*/
|
|
41
|
-
declare
|
|
41
|
+
declare const ChatLayout: React.ForwardRefExoticComponent<Omit<ChatLayoutProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
42
42
|
export { ChatLayout };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { cn } from "../lib/utils.js";
|
|
2
3
|
import { IconButton } from "./icon-button.js";
|
|
3
4
|
import { ResizablePanel, ResizablePanelGroup, ResizeHandle } from "./resizable.js";
|
|
@@ -88,7 +89,7 @@ function useDesktop(query = "(min-width: 768px)") {
|
|
|
88
89
|
* details column, and a Full screen mode. Put a Conversation and a Composer
|
|
89
90
|
* in `children`; the layout stacks them and keeps the composer at the bottom.
|
|
90
91
|
*/
|
|
91
|
-
function ChatLayout({ sidebar, sidebarLabel = "Chats", details, detailsLabel = "Details", title, actions, sidebarOpen: controlledOpen, defaultSidebarOpen = true, onSidebarOpenChange, allowFullscreen = true, onFullscreenChange, sidebarSize = "24%", children, className, ...props }) {
|
|
92
|
+
var ChatLayout = React.forwardRef(function ChatLayout({ sidebar, sidebarLabel = "Chats", details, detailsLabel = "Details", title, actions, sidebarOpen: controlledOpen, defaultSidebarOpen = true, onSidebarOpenChange, allowFullscreen = true, onFullscreenChange, sidebarSize = "24%", children, className, ...props }, ref) {
|
|
92
93
|
const root = React.useRef(null);
|
|
93
94
|
const desktop = useDesktop();
|
|
94
95
|
const [ownOpen, setOwnOpen] = React.useState(defaultSidebarOpen);
|
|
@@ -121,7 +122,11 @@ function ChatLayout({ sidebar, sidebarLabel = "Chats", details, detailsLabel = "
|
|
|
121
122
|
children
|
|
122
123
|
});
|
|
123
124
|
return /* @__PURE__ */ jsxs("div", {
|
|
124
|
-
ref:
|
|
125
|
+
ref: (node) => {
|
|
126
|
+
root.current = node;
|
|
127
|
+
if (typeof ref === "function") ref(node);
|
|
128
|
+
else if (ref) ref.current = node;
|
|
129
|
+
},
|
|
125
130
|
"data-slot": "chat-layout",
|
|
126
131
|
"data-fullscreen": fullscreen || void 0,
|
|
127
132
|
className: cn("flex h-full min-h-0 flex-col overflow-hidden bg-background text-foreground", "data-[fullscreen]:fixed data-[fullscreen]:inset-0 data-[fullscreen]:z-50 data-[fullscreen]:h-dvh data-[fullscreen]:w-screen", className),
|
|
@@ -210,7 +215,7 @@ function ChatLayout({ sidebar, sidebarLabel = "Chats", details, detailsLabel = "
|
|
|
210
215
|
})
|
|
211
216
|
]
|
|
212
217
|
});
|
|
213
|
-
}
|
|
218
|
+
});
|
|
214
219
|
//#endregion
|
|
215
220
|
export { ChatLayout, useFullscreen };
|
|
216
221
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-layout.js","names":["_Fragment"],"sources":["../../src/components/chat-layout.tsx"],"sourcesContent":["import Maximize from '@burtson-labs/icons/react/maximize';\nimport Minimize from '@burtson-labs/icons/react/minimize';\nimport PanelLeft from '@burtson-labs/icons/react/panel-left';\nimport PanelLeftClose from '@burtson-labs/icons/react/panel-left-close';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { IconButton } from './icon-button';\nimport { ResizablePanel, ResizablePanelGroup, ResizeHandle } from './resizable';\nimport { Sheet, SheetContent, SheetDescription, SheetTitle } from './sheet';\n\n/**\n * Full screen for one element: the Fullscreen API where the browser allows\n * it, otherwise the element is pinned over the page with CSS. Escape leaves\n * either way. Nothing remounts, so focus and scroll stay where they were.\n */\nexport function useFullscreen(\n ref: React.RefObject<HTMLElement | null>,\n onChange?: (fullscreen: boolean) => void,\n) {\n const [native, setNative] = React.useState(false);\n const [fallback, setFallback] = React.useState(false);\n const active = native || fallback;\n const changed = React.useRef(onChange);\n React.useEffect(() => {\n changed.current = onChange;\n }, [onChange]);\n\n React.useEffect(() => {\n const sync = () =>\n setNative(Boolean(ref.current && document.fullscreenElement === ref.current));\n document.addEventListener('fullscreenchange', sync);\n return () => document.removeEventListener('fullscreenchange', sync);\n }, [ref]);\n\n React.useEffect(() => {\n if (!fallback) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && !e.defaultPrevented) setFallback(false);\n };\n const root = document.documentElement;\n const previous = root.style.overflow;\n root.style.overflow = 'hidden';\n document.addEventListener('keydown', onKey);\n return () => {\n root.style.overflow = previous;\n document.removeEventListener('keydown', onKey);\n };\n }, [fallback]);\n\n const first = React.useRef(true);\n React.useEffect(() => {\n if (first.current) {\n first.current = false;\n return;\n }\n changed.current?.(active);\n }, [active]);\n\n const enter = React.useCallback(async () => {\n const el = ref.current;\n if (!el) return;\n const canNative = document.fullscreenEnabled && typeof el.requestFullscreen === 'function';\n // Denied (iframe without allowfullscreen, iOS) resolves false: use the CSS fallback.\n const ok = canNative\n ? await el.requestFullscreen().then(\n () => true,\n () => false,\n )\n : false;\n if (!ok) setFallback(true);\n }, [ref]);\n\n const exit = React.useCallback(async () => {\n setFallback(false);\n if (document.fullscreenElement && typeof document.exitFullscreen === 'function') {\n await document.exitFullscreen().catch(() => undefined);\n }\n }, []);\n\n const toggle = React.useCallback(() => (active ? exit() : enter()), [active, enter, exit]);\n return { fullscreen: active, native, enter, exit, toggle };\n}\n\nfunction useDesktop(query = '(min-width: 768px)') {\n const get = () => (typeof matchMedia === 'function' ? matchMedia(query).matches : true);\n const [desktop, setDesktop] = React.useState(get);\n React.useEffect(() => {\n if (typeof matchMedia !== 'function') return;\n const mq = matchMedia(query);\n const on = () => setDesktop(mq.matches);\n mq.addEventListener?.('change', on);\n return () => mq.removeEventListener?.('change', on);\n }, [query]);\n return desktop;\n}\n\nexport interface ChatLayoutProps extends Omit<React.ComponentProps<'div'>, 'title'> {\n /** The history column, usually a ChatHistory. A sheet from the left on phones. */\n sidebar?: React.ReactNode;\n sidebarLabel?: string;\n /** An optional right column: sources, files, run details. Hidden on phones. */\n details?: React.ReactNode;\n detailsLabel?: string;\n /** Shown in the header: the conversation title. */\n title?: React.ReactNode;\n /** Header controls right of the title (model picker, share). */\n actions?: React.ReactNode;\n /** Controlled sidebar state; leave out to let the layout keep its own. */\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n onSidebarOpenChange?: (open: boolean) => void;\n /** Show the Full screen button. Default true. */\n allowFullscreen?: boolean;\n onFullscreenChange?: (fullscreen: boolean) => void;\n /** Sidebar width, e.g. \"22%\" or 280 (px), as ResizablePanel sizes. */\n sidebarSize?: number | string;\n}\n\n/**\n * The frame for a chat app: header, a resizable history sidebar (a sheet on\n * phones that closes when a chat is picked), the conversation, an optional\n * details column, and a Full screen mode. Put a Conversation and a Composer\n * in `children`; the layout stacks them and keeps the composer at the bottom.\n */\nfunction ChatLayout({\n sidebar,\n sidebarLabel = 'Chats',\n details,\n detailsLabel = 'Details',\n title,\n actions,\n sidebarOpen: controlledOpen,\n defaultSidebarOpen = true,\n onSidebarOpenChange,\n allowFullscreen = true,\n onFullscreenChange,\n sidebarSize = '24%',\n children,\n className,\n ...props\n}: ChatLayoutProps) {\n const root = React.useRef<HTMLDivElement>(null);\n const desktop = useDesktop();\n const [ownOpen, setOwnOpen] = React.useState(defaultSidebarOpen);\n const [sheetOpen, setSheetOpen] = React.useState(false);\n const open = controlledOpen ?? ownOpen;\n const setOpen = (v: boolean) => {\n if (controlledOpen === undefined) setOwnOpen(v);\n onSidebarOpenChange?.(v);\n };\n const { fullscreen, toggle } = useFullscreen(root, onFullscreenChange);\n\n const sidebarToggle = sidebar ? (\n desktop ? (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n label={open ? `Hide ${sidebarLabel.toLowerCase()}` : `Show ${sidebarLabel.toLowerCase()}`}\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n {open ? <PanelLeftClose /> : <PanelLeft />}\n </IconButton>\n ) : (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"pointer-coarse:size-11\"\n label={`Open ${sidebarLabel.toLowerCase()}`}\n aria-expanded={sheetOpen}\n onClick={() => setSheetOpen(true)}\n >\n <PanelLeft />\n </IconButton>\n )\n ) : null;\n\n const main = (\n <div data-slot=\"chat-layout-main\" className=\"flex h-full min-h-0 min-w-0 flex-col\">\n {children}\n </div>\n );\n\n return (\n <div\n ref={root}\n data-slot=\"chat-layout\"\n data-fullscreen={fullscreen || undefined}\n className={cn(\n 'flex h-full min-h-0 flex-col overflow-hidden bg-background text-foreground',\n 'data-[fullscreen]:fixed data-[fullscreen]:inset-0 data-[fullscreen]:z-50 data-[fullscreen]:h-dvh data-[fullscreen]:w-screen',\n className,\n )}\n {...props}\n >\n <header className=\"flex h-12 shrink-0 items-center gap-1 border-b px-2\">\n {sidebarToggle}\n <div className=\"min-w-0 flex-1 truncate px-1 text-sm font-medium\">{title}</div>\n {actions}\n {allowFullscreen && (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"pointer-coarse:size-11\"\n label={fullscreen ? 'Exit full screen' : 'Full screen'}\n aria-pressed={fullscreen}\n onClick={() => void toggle()}\n >\n {fullscreen ? <Minimize /> : <Maximize />}\n </IconButton>\n )}\n </header>\n <div className=\"min-h-0 flex-1\">\n {desktop && (sidebar || details) ? (\n <ResizablePanelGroup orientation=\"horizontal\">\n {sidebar && open && (\n <>\n <ResizablePanel\n id=\"chat-sidebar\"\n defaultSize={sidebarSize}\n minSize={180}\n maxSize=\"45%\"\n className=\"bg-surface-muted/50\"\n >\n <aside aria-label={sidebarLabel} className=\"h-full\">\n {sidebar}\n </aside>\n </ResizablePanel>\n <ResizeHandle aria-label={`Resize ${sidebarLabel.toLowerCase()}`} />\n </>\n )}\n <ResizablePanel id=\"chat-main\" minSize=\"30%\">\n {main}\n </ResizablePanel>\n {details && (\n <>\n <ResizeHandle aria-label={`Resize ${detailsLabel.toLowerCase()}`} />\n <ResizablePanel id=\"chat-details\" defaultSize=\"26%\" minSize={200} maxSize=\"45%\">\n <aside aria-label={detailsLabel} className=\"h-full overflow-y-auto\">\n {details}\n </aside>\n </ResizablePanel>\n </>\n )}\n </ResizablePanelGroup>\n ) : (\n main\n )}\n </div>\n {sidebar && !desktop && (\n <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>\n <SheetContent side=\"left\" className=\"w-[min(20rem,88vw)] gap-0 p-0\">\n <SheetTitle className=\"border-b px-4 py-3.5 text-sm\">{sidebarLabel}</SheetTitle>\n <SheetDescription className=\"sr-only\">Pick a conversation.</SheetDescription>\n <div\n className=\"min-h-0 flex-1\"\n // Picking a chat (or New chat) closes the sheet, as on a phone it should.\n onClickCapture={(e) => {\n const t = e.target as HTMLElement;\n if (t.closest('[data-slot=\"chat-history-row\"], [data-close-sheet]'))\n setSheetOpen(false);\n }}\n >\n {sidebar}\n </div>\n </SheetContent>\n </Sheet>\n )}\n </div>\n );\n}\n\nexport { ChatLayout };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat-layout.js","names":["_Fragment"],"sources":["../../src/components/chat-layout.tsx"],"sourcesContent":["import Maximize from '@burtson-labs/icons/react/maximize';\nimport Minimize from '@burtson-labs/icons/react/minimize';\nimport PanelLeft from '@burtson-labs/icons/react/panel-left';\nimport PanelLeftClose from '@burtson-labs/icons/react/panel-left-close';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { IconButton } from './icon-button';\nimport { ResizablePanel, ResizablePanelGroup, ResizeHandle } from './resizable';\nimport { Sheet, SheetContent, SheetDescription, SheetTitle } from './sheet';\n\n/**\n * Full screen for one element: the Fullscreen API where the browser allows\n * it, otherwise the element is pinned over the page with CSS. Escape leaves\n * either way. Nothing remounts, so focus and scroll stay where they were.\n */\nexport function useFullscreen(\n ref: React.RefObject<HTMLElement | null>,\n onChange?: (fullscreen: boolean) => void,\n) {\n const [native, setNative] = React.useState(false);\n const [fallback, setFallback] = React.useState(false);\n const active = native || fallback;\n const changed = React.useRef(onChange);\n React.useEffect(() => {\n changed.current = onChange;\n }, [onChange]);\n\n React.useEffect(() => {\n const sync = () =>\n setNative(Boolean(ref.current && document.fullscreenElement === ref.current));\n document.addEventListener('fullscreenchange', sync);\n return () => document.removeEventListener('fullscreenchange', sync);\n }, [ref]);\n\n React.useEffect(() => {\n if (!fallback) return;\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && !e.defaultPrevented) setFallback(false);\n };\n const root = document.documentElement;\n const previous = root.style.overflow;\n root.style.overflow = 'hidden';\n document.addEventListener('keydown', onKey);\n return () => {\n root.style.overflow = previous;\n document.removeEventListener('keydown', onKey);\n };\n }, [fallback]);\n\n const first = React.useRef(true);\n React.useEffect(() => {\n if (first.current) {\n first.current = false;\n return;\n }\n changed.current?.(active);\n }, [active]);\n\n const enter = React.useCallback(async () => {\n const el = ref.current;\n if (!el) return;\n const canNative = document.fullscreenEnabled && typeof el.requestFullscreen === 'function';\n // Denied (iframe without allowfullscreen, iOS) resolves false: use the CSS fallback.\n const ok = canNative\n ? await el.requestFullscreen().then(\n () => true,\n () => false,\n )\n : false;\n if (!ok) setFallback(true);\n }, [ref]);\n\n const exit = React.useCallback(async () => {\n setFallback(false);\n if (document.fullscreenElement && typeof document.exitFullscreen === 'function') {\n await document.exitFullscreen().catch(() => undefined);\n }\n }, []);\n\n const toggle = React.useCallback(() => (active ? exit() : enter()), [active, enter, exit]);\n return { fullscreen: active, native, enter, exit, toggle };\n}\n\nfunction useDesktop(query = '(min-width: 768px)') {\n const get = () => (typeof matchMedia === 'function' ? matchMedia(query).matches : true);\n const [desktop, setDesktop] = React.useState(get);\n React.useEffect(() => {\n if (typeof matchMedia !== 'function') return;\n const mq = matchMedia(query);\n const on = () => setDesktop(mq.matches);\n mq.addEventListener?.('change', on);\n return () => mq.removeEventListener?.('change', on);\n }, [query]);\n return desktop;\n}\n\nexport interface ChatLayoutProps extends Omit<React.ComponentProps<'div'>, 'title'> {\n /** The history column, usually a ChatHistory. A sheet from the left on phones. */\n sidebar?: React.ReactNode;\n sidebarLabel?: string;\n /** An optional right column: sources, files, run details. Hidden on phones. */\n details?: React.ReactNode;\n detailsLabel?: string;\n /** Shown in the header: the conversation title. */\n title?: React.ReactNode;\n /** Header controls right of the title (model picker, share). */\n actions?: React.ReactNode;\n /** Controlled sidebar state; leave out to let the layout keep its own. */\n sidebarOpen?: boolean;\n defaultSidebarOpen?: boolean;\n onSidebarOpenChange?: (open: boolean) => void;\n /** Show the Full screen button. Default true. */\n allowFullscreen?: boolean;\n onFullscreenChange?: (fullscreen: boolean) => void;\n /** Sidebar width, e.g. \"22%\" or 280 (px), as ResizablePanel sizes. */\n sidebarSize?: number | string;\n}\n\n/**\n * The frame for a chat app: header, a resizable history sidebar (a sheet on\n * phones that closes when a chat is picked), the conversation, an optional\n * details column, and a Full screen mode. Put a Conversation and a Composer\n * in `children`; the layout stacks them and keeps the composer at the bottom.\n */\nconst ChatLayout = React.forwardRef<HTMLDivElement, ChatLayoutProps>(function ChatLayout(\n {\n sidebar,\n sidebarLabel = 'Chats',\n details,\n detailsLabel = 'Details',\n title,\n actions,\n sidebarOpen: controlledOpen,\n defaultSidebarOpen = true,\n onSidebarOpenChange,\n allowFullscreen = true,\n onFullscreenChange,\n sidebarSize = '24%',\n children,\n className,\n ...props\n },\n ref,\n) {\n const root = React.useRef<HTMLDivElement>(null);\n const desktop = useDesktop();\n const [ownOpen, setOwnOpen] = React.useState(defaultSidebarOpen);\n const [sheetOpen, setSheetOpen] = React.useState(false);\n const open = controlledOpen ?? ownOpen;\n const setOpen = (v: boolean) => {\n if (controlledOpen === undefined) setOwnOpen(v);\n onSidebarOpenChange?.(v);\n };\n const { fullscreen, toggle } = useFullscreen(root, onFullscreenChange);\n\n const sidebarToggle = sidebar ? (\n desktop ? (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n label={open ? `Hide ${sidebarLabel.toLowerCase()}` : `Show ${sidebarLabel.toLowerCase()}`}\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n {open ? <PanelLeftClose /> : <PanelLeft />}\n </IconButton>\n ) : (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"pointer-coarse:size-11\"\n label={`Open ${sidebarLabel.toLowerCase()}`}\n aria-expanded={sheetOpen}\n onClick={() => setSheetOpen(true)}\n >\n <PanelLeft />\n </IconButton>\n )\n ) : null;\n\n const main = (\n <div data-slot=\"chat-layout-main\" className=\"flex h-full min-h-0 min-w-0 flex-col\">\n {children}\n </div>\n );\n\n return (\n <div\n ref={(node) => {\n root.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n }}\n data-slot=\"chat-layout\"\n data-fullscreen={fullscreen || undefined}\n className={cn(\n 'flex h-full min-h-0 flex-col overflow-hidden bg-background text-foreground',\n 'data-[fullscreen]:fixed data-[fullscreen]:inset-0 data-[fullscreen]:z-50 data-[fullscreen]:h-dvh data-[fullscreen]:w-screen',\n className,\n )}\n {...props}\n >\n <header className=\"flex h-12 shrink-0 items-center gap-1 border-b px-2\">\n {sidebarToggle}\n <div className=\"min-w-0 flex-1 truncate px-1 text-sm font-medium\">{title}</div>\n {actions}\n {allowFullscreen && (\n <IconButton\n variant=\"ghost\"\n size=\"icon-sm\"\n className=\"pointer-coarse:size-11\"\n label={fullscreen ? 'Exit full screen' : 'Full screen'}\n aria-pressed={fullscreen}\n onClick={() => void toggle()}\n >\n {fullscreen ? <Minimize /> : <Maximize />}\n </IconButton>\n )}\n </header>\n <div className=\"min-h-0 flex-1\">\n {desktop && (sidebar || details) ? (\n <ResizablePanelGroup orientation=\"horizontal\">\n {sidebar && open && (\n <>\n <ResizablePanel\n id=\"chat-sidebar\"\n defaultSize={sidebarSize}\n minSize={180}\n maxSize=\"45%\"\n className=\"bg-surface-muted/50\"\n >\n <aside aria-label={sidebarLabel} className=\"h-full\">\n {sidebar}\n </aside>\n </ResizablePanel>\n <ResizeHandle aria-label={`Resize ${sidebarLabel.toLowerCase()}`} />\n </>\n )}\n <ResizablePanel id=\"chat-main\" minSize=\"30%\">\n {main}\n </ResizablePanel>\n {details && (\n <>\n <ResizeHandle aria-label={`Resize ${detailsLabel.toLowerCase()}`} />\n <ResizablePanel id=\"chat-details\" defaultSize=\"26%\" minSize={200} maxSize=\"45%\">\n <aside aria-label={detailsLabel} className=\"h-full overflow-y-auto\">\n {details}\n </aside>\n </ResizablePanel>\n </>\n )}\n </ResizablePanelGroup>\n ) : (\n main\n )}\n </div>\n {sidebar && !desktop && (\n <Sheet open={sheetOpen} onOpenChange={setSheetOpen}>\n <SheetContent side=\"left\" className=\"w-[min(20rem,88vw)] gap-0 p-0\">\n <SheetTitle className=\"border-b px-4 py-3.5 text-sm\">{sidebarLabel}</SheetTitle>\n <SheetDescription className=\"sr-only\">Pick a conversation.</SheetDescription>\n <div\n className=\"min-h-0 flex-1\"\n // Picking a chat (or New chat) closes the sheet, as on a phone it should.\n onClickCapture={(e) => {\n const t = e.target as HTMLElement;\n if (t.closest('[data-slot=\"chat-history-row\"], [data-close-sheet]'))\n setSheetOpen(false);\n }}\n >\n {sidebar}\n </div>\n </SheetContent>\n </Sheet>\n )}\n </div>\n );\n});\n\nexport { ChatLayout };\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Checkbox as CheckboxPrimitive } from 'radix-ui';
|
|
2
1
|
import * as React from 'react';
|
|
2
|
+
import * as CheckboxPrimitive from '../primitives/vendor/radix/react-checkbox';
|
|
3
3
|
export interface CheckboxCardProps extends Omit<React.ComponentProps<typeof CheckboxPrimitive.Root>, 'title' | 'children'> {
|
|
4
4
|
/** The option's name. */
|
|
5
5
|
title: React.ReactNode;
|
|
@@ -13,7 +13,7 @@ export interface CheckboxCardProps extends Omit<React.ComponentProps<typeof Chec
|
|
|
13
13
|
* area (44px or taller). For choices that need explaining: roles,
|
|
14
14
|
* permissions, notification types. One focus ring, on the card.
|
|
15
15
|
*/
|
|
16
|
-
declare
|
|
16
|
+
declare const CheckboxCard: React.ForwardRefExoticComponent<Omit<CheckboxCardProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
17
17
|
export interface CheckboxCardGroupProps extends React.ComponentProps<'fieldset'> {
|
|
18
18
|
/** The group's name, shown as its legend. */
|
|
19
19
|
label: React.ReactNode;
|
|
@@ -21,5 +21,5 @@ export interface CheckboxCardGroupProps extends React.ComponentProps<'fieldset'>
|
|
|
21
21
|
description?: React.ReactNode;
|
|
22
22
|
}
|
|
23
23
|
/** A fieldset of CheckboxCards with a visible legend. `disabled` disables every card. */
|
|
24
|
-
declare
|
|
24
|
+
declare const CheckboxCardGroup: React.ForwardRefExoticComponent<Omit<CheckboxCardGroupProps, "ref"> & React.RefAttributes<HTMLFieldSetElement>>;
|
|
25
25
|
export { CheckboxCard, CheckboxCardGroup };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import "
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn, noOutlineClasses } from "../lib/utils.js";
|
|
3
|
+
import { Checkbox } from "./checkbox.js";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/checkbox-card.tsx
|
|
@@ -9,19 +9,20 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
* area (44px or taller). For choices that need explaining: roles,
|
|
10
10
|
* permissions, notification types. One focus ring, on the card.
|
|
11
11
|
*/
|
|
12
|
-
function CheckboxCard({ title, description, id: idProp, disabled, className, cardClassName, ...props }) {
|
|
12
|
+
var CheckboxCard = React.forwardRef(function CheckboxCard({ title, description, id: idProp, disabled, className, cardClassName, ...props }, ref) {
|
|
13
13
|
const auto = React.useId();
|
|
14
14
|
const id = idProp ?? auto;
|
|
15
15
|
return /* @__PURE__ */ jsxs("label", {
|
|
16
16
|
htmlFor: id,
|
|
17
17
|
"data-slot": "checkbox-card",
|
|
18
18
|
className: cn("flex min-h-11 cursor-pointer items-start gap-3 rounded-md border border-border bg-surface px-3 py-2.5 transition-colors hover:border-border-strong", "has-[[data-state=checked]]:border-brand/40 has-[[data-state=checked]]:bg-brand-soft/50", "has-[:focus-visible]:border-ring has-[:focus-visible]:inset-ring-1 has-[:focus-visible]:inset-ring-ring", "has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-60 has-[:disabled]:hover:border-border", cardClassName),
|
|
19
|
-
children: [/* @__PURE__ */ jsx(Checkbox
|
|
19
|
+
children: [/* @__PURE__ */ jsx(Checkbox, {
|
|
20
|
+
ref,
|
|
20
21
|
id,
|
|
21
22
|
disabled,
|
|
22
23
|
"aria-labelledby": `${id}-title`,
|
|
23
24
|
"aria-describedby": description ? `${id}-description` : void 0,
|
|
24
|
-
className: cn("mt-0.5
|
|
25
|
+
className: cn("mt-0.5 focus-visible:outline-none", noOutlineClasses, className),
|
|
25
26
|
...props
|
|
26
27
|
}), /* @__PURE__ */ jsxs("span", {
|
|
27
28
|
className: "grid min-w-0 gap-0.5",
|
|
@@ -36,18 +37,19 @@ function CheckboxCard({ title, description, id: idProp, disabled, className, car
|
|
|
36
37
|
})]
|
|
37
38
|
})]
|
|
38
39
|
});
|
|
39
|
-
}
|
|
40
|
+
});
|
|
40
41
|
/** A fieldset of CheckboxCards with a visible legend. `disabled` disables every card. */
|
|
41
|
-
function CheckboxCardGroup({ label, description, className, children, ...props }) {
|
|
42
|
+
var CheckboxCardGroup = React.forwardRef(function CheckboxCardGroup({ label, description, className, children, ...props }, ref) {
|
|
42
43
|
const id = React.useId();
|
|
43
44
|
return /* @__PURE__ */ jsxs("fieldset", {
|
|
45
|
+
ref,
|
|
44
46
|
"data-slot": "checkbox-card-group",
|
|
45
47
|
"aria-describedby": description ? `${id}-description` : void 0,
|
|
46
48
|
className: cn("grid min-w-0 gap-2", className),
|
|
47
49
|
...props,
|
|
48
50
|
children: [
|
|
49
51
|
/* @__PURE__ */ jsx("legend", {
|
|
50
|
-
className: "mb-1.5 text-sm font-
|
|
52
|
+
className: "mb-1.5 text-sm font-semibold",
|
|
51
53
|
children: label
|
|
52
54
|
}),
|
|
53
55
|
description && /* @__PURE__ */ jsx("p", {
|
|
@@ -58,7 +60,7 @@ function CheckboxCardGroup({ label, description, className, children, ...props }
|
|
|
58
60
|
children
|
|
59
61
|
]
|
|
60
62
|
});
|
|
61
|
-
}
|
|
63
|
+
});
|
|
62
64
|
//#endregion
|
|
63
65
|
export { CheckboxCard, CheckboxCardGroup };
|
|
64
66
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox-card.js","names":[
|
|
1
|
+
{"version":3,"file":"checkbox-card.js","names":[],"sources":["../../src/components/checkbox-card.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn, noOutlineClasses } from '../lib/utils';\nimport * as CheckboxPrimitive from '../primitives/vendor/radix/react-checkbox';\n\nimport { Checkbox } from './checkbox';\n\nexport interface CheckboxCardProps extends Omit<\n React.ComponentProps<typeof CheckboxPrimitive.Root>,\n 'title' | 'children'\n> {\n /** The option's name. */\n title: React.ReactNode;\n /** A sentence under the name: what choosing it means. */\n description?: React.ReactNode;\n /** Extra classes for the card (the label); className goes to the checkbox. */\n cardClassName?: string;\n}\n\n/**\n * A checkbox with a title and a sentence, where the whole card is the hit\n * area (44px or taller). For choices that need explaining: roles,\n * permissions, notification types. One focus ring, on the card.\n */\nconst CheckboxCard = React.forwardRef<HTMLButtonElement, CheckboxCardProps>(function CheckboxCard(\n { title, description, id: idProp, disabled, className, cardClassName, ...props },\n ref,\n) {\n const auto = React.useId();\n const id = idProp ?? auto;\n return (\n <label\n htmlFor={id}\n data-slot=\"checkbox-card\"\n className={cn(\n 'flex min-h-11 cursor-pointer items-start gap-3 rounded-md border border-border bg-surface px-3 py-2.5 transition-colors hover:border-border-strong',\n 'has-[[data-state=checked]]:border-brand/40 has-[[data-state=checked]]:bg-brand-soft/50',\n 'has-[:focus-visible]:border-ring has-[:focus-visible]:inset-ring-1 has-[:focus-visible]:inset-ring-ring',\n // :disabled also catches a disabled fieldset around the card.\n 'has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-60 has-[:disabled]:hover:border-border',\n cardClassName,\n )}\n >\n <Checkbox\n ref={ref}\n id={id}\n disabled={disabled}\n aria-labelledby={`${id}-title`}\n aria-describedby={description ? `${id}-description` : undefined}\n // The card draws the focus ring; the box keeps a system outline in forced colors.\n className={cn('mt-0.5 focus-visible:outline-none', noOutlineClasses, className)}\n {...props}\n />\n <span className=\"grid min-w-0 gap-0.5\">\n <span id={`${id}-title`} className=\"text-sm leading-5 font-medium\">\n {title}\n </span>\n {description && (\n <span id={`${id}-description`} className=\"text-[13px] leading-5 text-muted-foreground\">\n {description}\n </span>\n )}\n </span>\n </label>\n );\n});\n\nexport interface CheckboxCardGroupProps extends React.ComponentProps<'fieldset'> {\n /** The group's name, shown as its legend. */\n label: React.ReactNode;\n /** Help under the legend. */\n description?: React.ReactNode;\n}\n\n/** A fieldset of CheckboxCards with a visible legend. `disabled` disables every card. */\nconst CheckboxCardGroup = React.forwardRef<HTMLFieldSetElement, CheckboxCardGroupProps>(\n function CheckboxCardGroup({ label, description, className, children, ...props }, ref) {\n const id = React.useId();\n return (\n <fieldset\n ref={ref}\n data-slot=\"checkbox-card-group\"\n aria-describedby={description ? `${id}-description` : undefined}\n className={cn('grid min-w-0 gap-2', className)}\n {...props}\n >\n <legend className=\"mb-1.5 text-sm font-semibold\">{label}</legend>\n {description && (\n <p id={`${id}-description`} className=\"-mt-1.5 text-[13px] text-muted-foreground\">\n {description}\n </p>\n )}\n {children}\n </fieldset>\n );\n },\n);\n\nexport { CheckboxCard, CheckboxCardGroup };\n"],"mappings":";;;;;;;;;;;AAwBA,IAAM,eAAe,MAAM,WAAiD,SAAS,aACnF,EAAE,OAAO,aAAa,IAAI,QAAQ,UAAU,WAAW,eAAe,GAAG,SACzE,KACA;CACA,MAAM,OAAO,MAAM,MAAM;CACzB,MAAM,KAAK,UAAU;CACrB,OACE,qBAAC,SAAD;EACE,SAAS;EACT,aAAU;EACV,WAAW,GACT,sJACA,0FACA,2GAEA,qGACA,aACF;EAVF,UAAA,CAYE,oBAAC,UAAD;GACO;GACD;GACM;GACV,mBAAiB,GAAG,GAAG;GACvB,oBAAkB,cAAc,GAAG,GAAG,gBAAgB,KAAA;GAEtD,WAAW,GAAG,qCAAqC,kBAAkB,SAAS;GAC9E,GAAI;EACL,CAAA,GACD,qBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,oBAAC,QAAD;IAAM,IAAI,GAAG,GAAG;IAAS,WAAU;IAChC,UAAA;GACG,CAAA,GACL,eACC,oBAAC,QAAD;IAAM,IAAI,GAAG,GAAG;IAAe,WAAU;IACtC,UAAA;GACG,CAAA,CAEJ;EACD,CAAA,CAAA;;AAEX,CAAC;;AAUD,IAAM,oBAAoB,MAAM,WAC9B,SAAS,kBAAkB,EAAE,OAAO,aAAa,WAAW,UAAU,GAAG,SAAS,KAAK;CACrF,MAAM,KAAK,MAAM,MAAM;CACvB,OACE,qBAAC,YAAD;EACO;EACL,aAAU;EACV,oBAAkB,cAAc,GAAG,GAAG,gBAAgB,KAAA;EACtD,WAAW,GAAG,sBAAsB,SAAS;EAC7C,GAAI;EALN,UAAA;GAOE,oBAAC,UAAD;IAAQ,WAAU;IAAgC,UAAA;GAAc,CAAA;GAC/D,eACC,oBAAC,KAAD;IAAG,IAAI,GAAG,GAAG;IAAe,WAAU;IACnC,UAAA;GACA,CAAA;GAEJ;EACO;;AAEd,CACF"}
|
|
@@ -1,4 +1,25 @@
|
|
|
1
|
-
import { Checkbox as CheckboxPrimitive } from 'radix-ui';
|
|
2
1
|
import * as React from 'react';
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import * as CheckboxPrimitive from '../primitives/vendor/radix/react-checkbox';
|
|
3
|
+
/**
|
|
4
|
+
* A 16px box with a 44px hit area on touch screens. Its unchecked border is
|
|
5
|
+
* the field border tone (`input`, 3:1 on the page in both modes); checked it
|
|
6
|
+
* fills with primary in light mode and with brand (the ring colour, 4.5:1 on
|
|
7
|
+
* the page, where primary reaches 2.8:1) in dark mode. Focus is the shared
|
|
8
|
+
* 2px outline. Wrap it in a Label, or use CheckboxRow or CheckboxCard.
|
|
9
|
+
*/
|
|
10
|
+
declare const Checkbox: React.ForwardRefExoticComponent<Omit<CheckboxPrimitive.CheckboxProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
11
|
+
export interface CheckboxRowProps extends Omit<React.ComponentProps<typeof CheckboxPrimitive.Root>, 'children'> {
|
|
12
|
+
/** The option's name. */
|
|
13
|
+
label: React.ReactNode;
|
|
14
|
+
/** A line under the name. */
|
|
15
|
+
description?: React.ReactNode;
|
|
16
|
+
/** Extra classes for the row (the label); className goes to the checkbox. */
|
|
17
|
+
rowClassName?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A checkbox with its label to the right and an optional line under it, the
|
|
21
|
+
* whole row clickable. For a bordered option with a sentence of explanation
|
|
22
|
+
* use CheckboxCard.
|
|
23
|
+
*/
|
|
24
|
+
declare const CheckboxRow: React.ForwardRefExoticComponent<Omit<CheckboxRowProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
25
|
+
export { Checkbox, CheckboxRow };
|
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import "react";
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn, focusRingClasses, touchTargetClasses } from "../lib/utils.js";
|
|
3
|
+
import { Checkbox as Checkbox$1, CheckboxIndicator } from "../primitives/vendor/radix/react-checkbox/checkbox.js";
|
|
4
|
+
import * as React from "react";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
6
|
import Check from "@burtson-labs/icons/react/check";
|
|
6
7
|
import Minus from "@burtson-labs/icons/react/minus";
|
|
7
8
|
//#region src/components/checkbox.tsx
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* A 16px box with a 44px hit area on touch screens. Its unchecked border is
|
|
11
|
+
* the field border tone (`input`, 3:1 on the page in both modes); checked it
|
|
12
|
+
* fills with primary in light mode and with brand (the ring colour, 4.5:1 on
|
|
13
|
+
* the page, where primary reaches 2.8:1) in dark mode. Focus is the shared
|
|
14
|
+
* 2px outline. Wrap it in a Label, or use CheckboxRow or CheckboxCard.
|
|
15
|
+
*/
|
|
16
|
+
var Checkbox = React.forwardRef(function Checkbox({ className, ...props }, ref) {
|
|
17
|
+
return /* @__PURE__ */ jsx(Checkbox$1, {
|
|
18
|
+
ref,
|
|
10
19
|
"data-slot": "checkbox",
|
|
11
|
-
className: cn("peer size-4 shrink-0 rounded-xs border border-
|
|
20
|
+
className: cn("peer size-4 shrink-0 rounded-xs border border-input bg-surface shadow-xs transition-[border-color,box-shadow,background-color] hover:border-brand/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground dark:data-[state=checked]:border-brand dark:data-[state=checked]:bg-brand dark:data-[state=checked]:text-background dark:data-[state=indeterminate]:border-brand dark:data-[state=indeterminate]:bg-brand dark:data-[state=indeterminate]:text-background", focusRingClasses, touchTargetClasses, className),
|
|
12
21
|
...props,
|
|
13
|
-
children: /* @__PURE__ */ jsxs(
|
|
22
|
+
children: /* @__PURE__ */ jsxs(CheckboxIndicator, {
|
|
14
23
|
"data-slot": "checkbox-indicator",
|
|
15
24
|
className: "group/indicator grid place-content-center text-current",
|
|
16
25
|
children: [/* @__PURE__ */ jsx(Check, {
|
|
@@ -24,8 +33,42 @@ function Checkbox$1({ className, ...props }) {
|
|
|
24
33
|
})]
|
|
25
34
|
})
|
|
26
35
|
});
|
|
27
|
-
}
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* A checkbox with its label to the right and an optional line under it, the
|
|
39
|
+
* whole row clickable. For a bordered option with a sentence of explanation
|
|
40
|
+
* use CheckboxCard.
|
|
41
|
+
*/
|
|
42
|
+
var CheckboxRow = React.forwardRef(function CheckboxRow({ label, description, id: idProp, className, rowClassName, disabled, ...props }, ref) {
|
|
43
|
+
const auto = React.useId();
|
|
44
|
+
const id = idProp ?? auto;
|
|
45
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
46
|
+
htmlFor: id,
|
|
47
|
+
"data-slot": "checkbox-row",
|
|
48
|
+
className: cn("flex min-h-5 cursor-pointer items-start gap-2.5 text-sm leading-5 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-60", rowClassName),
|
|
49
|
+
children: [/* @__PURE__ */ jsx(Checkbox, {
|
|
50
|
+
ref,
|
|
51
|
+
id,
|
|
52
|
+
disabled,
|
|
53
|
+
"aria-labelledby": `${id}-label`,
|
|
54
|
+
"aria-describedby": description ? `${id}-description` : void 0,
|
|
55
|
+
className: cn("mt-0.5", className),
|
|
56
|
+
...props
|
|
57
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
58
|
+
className: "grid min-w-0 gap-0.5",
|
|
59
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
60
|
+
id: `${id}-label`,
|
|
61
|
+
className: "font-medium",
|
|
62
|
+
children: label
|
|
63
|
+
}), description && /* @__PURE__ */ jsx("span", {
|
|
64
|
+
id: `${id}-description`,
|
|
65
|
+
className: "text-xs leading-4 text-muted-foreground",
|
|
66
|
+
children: description
|
|
67
|
+
})]
|
|
68
|
+
})]
|
|
69
|
+
});
|
|
70
|
+
});
|
|
28
71
|
//#endregion
|
|
29
|
-
export { Checkbox
|
|
72
|
+
export { Checkbox, CheckboxRow };
|
|
30
73
|
|
|
31
74
|
//# sourceMappingURL=checkbox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox.js","names":["
|
|
1
|
+
{"version":3,"file":"checkbox.js","names":["CheckboxPrimitive.Root","CheckboxPrimitive.Indicator"],"sources":["../../src/components/checkbox.tsx"],"sourcesContent":["import Check from '@burtson-labs/icons/react/check';\nimport Minus from '@burtson-labs/icons/react/minus';\nimport * as React from 'react';\n\nimport { cn, focusRingClasses, touchTargetClasses } from '../lib/utils';\nimport * as CheckboxPrimitive from '../primitives/vendor/radix/react-checkbox';\n\n/**\n * A 16px box with a 44px hit area on touch screens. Its unchecked border is\n * the field border tone (`input`, 3:1 on the page in both modes); checked it\n * fills with primary in light mode and with brand (the ring colour, 4.5:1 on\n * the page, where primary reaches 2.8:1) in dark mode. Focus is the shared\n * 2px outline. Wrap it in a Label, or use CheckboxRow or CheckboxCard.\n */\nconst Checkbox = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<typeof CheckboxPrimitive.Root>\n>(function Checkbox({ className, ...props }, ref) {\n return (\n <CheckboxPrimitive.Root\n ref={ref}\n data-slot=\"checkbox\"\n className={cn(\n 'peer size-4 shrink-0 rounded-xs border border-input bg-surface shadow-xs transition-[border-color,box-shadow,background-color] hover:border-brand/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground dark:data-[state=checked]:border-brand dark:data-[state=checked]:bg-brand dark:data-[state=checked]:text-background dark:data-[state=indeterminate]:border-brand dark:data-[state=indeterminate]:bg-brand dark:data-[state=indeterminate]:text-background',\n focusRingClasses,\n touchTargetClasses,\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"group/indicator grid place-content-center text-current\"\n >\n <Check\n className=\"size-3.5 group-data-[state=indeterminate]/indicator:hidden\"\n strokeWidth={2.5}\n aria-hidden\n />\n <Minus\n className=\"hidden size-3.5 group-data-[state=indeterminate]/indicator:block\"\n strokeWidth={2.5}\n aria-hidden\n />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n});\n\nexport interface CheckboxRowProps extends Omit<\n React.ComponentProps<typeof CheckboxPrimitive.Root>,\n 'children'\n> {\n /** The option's name. */\n label: React.ReactNode;\n /** A line under the name. */\n description?: React.ReactNode;\n /** Extra classes for the row (the label); className goes to the checkbox. */\n rowClassName?: string;\n}\n\n/**\n * A checkbox with its label to the right and an optional line under it, the\n * whole row clickable. For a bordered option with a sentence of explanation\n * use CheckboxCard.\n */\nconst CheckboxRow = React.forwardRef<HTMLButtonElement, CheckboxRowProps>(function CheckboxRow(\n { label, description, id: idProp, className, rowClassName, disabled, ...props },\n ref,\n) {\n const auto = React.useId();\n const id = idProp ?? auto;\n return (\n <label\n htmlFor={id}\n data-slot=\"checkbox-row\"\n className={cn(\n 'flex min-h-5 cursor-pointer items-start gap-2.5 text-sm leading-5 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-60',\n rowClassName,\n )}\n >\n <Checkbox\n ref={ref}\n id={id}\n disabled={disabled}\n aria-labelledby={`${id}-label`}\n aria-describedby={description ? `${id}-description` : undefined}\n className={cn('mt-0.5', className)}\n {...props}\n />\n <span className=\"grid min-w-0 gap-0.5\">\n <span id={`${id}-label`} className=\"font-medium\">\n {label}\n </span>\n {description && (\n <span id={`${id}-description`} className=\"text-xs leading-4 text-muted-foreground\">\n {description}\n </span>\n )}\n </span>\n </label>\n );\n});\n\nexport { Checkbox, CheckboxRow };\n"],"mappings":";;;;;;;;;;;;;;;AAcA,IAAM,WAAW,MAAM,WAGrB,SAAS,SAAS,EAAE,WAAW,GAAG,SAAS,KAAK;CAChD,OACE,oBAACA,YAAD;EACO;EACL,aAAU;EACV,WAAW,GACT,stBACA,kBACA,oBACA,SACF;EACA,GAAI;EAEJ,UAAA,qBAACC,mBAAD;GACE,aAAU;GACV,WAAU;GAFZ,UAAA,CAIE,oBAAC,OAAD;IACE,WAAU;IACV,aAAa;IACb,eAAA;GACD,CAAA,GACD,oBAAC,OAAD;IACE,WAAU;IACV,aAAa;IACb,eAAA;GACD,CAAA,CAC0B;;CACP,CAAA;AAE5B,CAAC;;;;;;AAmBD,IAAM,cAAc,MAAM,WAAgD,SAAS,YACjF,EAAE,OAAO,aAAa,IAAI,QAAQ,WAAW,cAAc,UAAU,GAAG,SACxE,KACA;CACA,MAAM,OAAO,MAAM,MAAM;CACzB,MAAM,KAAK,UAAU;CACrB,OACE,qBAAC,SAAD;EACE,SAAS;EACT,aAAU;EACV,WAAW,GACT,mIACA,YACF;EANF,UAAA,CAQE,oBAAC,UAAD;GACO;GACD;GACM;GACV,mBAAiB,GAAG,GAAG;GACvB,oBAAkB,cAAc,GAAG,GAAG,gBAAgB,KAAA;GACtD,WAAW,GAAG,UAAU,SAAS;GACjC,GAAI;EACL,CAAA,GACD,qBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,oBAAC,QAAD;IAAM,IAAI,GAAG,GAAG;IAAS,WAAU;IAChC,UAAA;GACG,CAAA,GACL,eACC,oBAAC,QAAD;IAAM,IAAI,GAAG,GAAG;IAAe,WAAU;IACtC,UAAA;GACG,CAAA,CAEJ;EACD,CAAA,CAAA;;AAEX,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Collapsible as CollapsiblePrimitive } from 'radix-ui';
|
|
2
1
|
import * as React from 'react';
|
|
2
|
+
import * as CollapsiblePrimitive from '../primitives/vendor/radix/react-collapsible';
|
|
3
3
|
declare function Collapsible(props: React.ComponentProps<typeof CollapsiblePrimitive.Root>): React.JSX.Element;
|
|
4
|
-
declare
|
|
5
|
-
declare
|
|
4
|
+
declare const CollapsibleTrigger: React.ForwardRefExoticComponent<Omit<CollapsiblePrimitive.CollapsibleTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
5
|
+
declare const CollapsibleContent: React.ForwardRefExoticComponent<Omit<CollapsiblePrimitive.CollapsibleContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
6
6
|
export { Collapsible, CollapsibleContent, CollapsibleTrigger };
|
|
@@ -1,28 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { cn } from "../lib/utils.js";
|
|
2
|
-
import {
|
|
3
|
-
import "react";
|
|
3
|
+
import { Content, Root, Trigger } from "../primitives/vendor/radix/react-collapsible/collapsible.js";
|
|
4
|
+
import * as React from "react";
|
|
4
5
|
import { jsx } from "react/jsx-runtime";
|
|
5
6
|
//#region src/components/collapsible.tsx
|
|
6
|
-
function Collapsible
|
|
7
|
-
return /* @__PURE__ */ jsx(
|
|
7
|
+
function Collapsible(props) {
|
|
8
|
+
return /* @__PURE__ */ jsx(Root, {
|
|
8
9
|
"data-slot": "collapsible",
|
|
9
10
|
...props
|
|
10
11
|
});
|
|
11
12
|
}
|
|
12
|
-
function CollapsibleTrigger(props) {
|
|
13
|
-
return /* @__PURE__ */ jsx(
|
|
13
|
+
var CollapsibleTrigger = React.forwardRef(function CollapsibleTrigger(props, ref) {
|
|
14
|
+
return /* @__PURE__ */ jsx(Trigger, {
|
|
15
|
+
ref,
|
|
14
16
|
"data-slot": "collapsible-trigger",
|
|
15
17
|
...props
|
|
16
18
|
});
|
|
17
|
-
}
|
|
18
|
-
function CollapsibleContent({ className, ...props }) {
|
|
19
|
-
return /* @__PURE__ */ jsx(
|
|
19
|
+
});
|
|
20
|
+
var CollapsibleContent = React.forwardRef(function CollapsibleContent({ className, ...props }, ref) {
|
|
21
|
+
return /* @__PURE__ */ jsx(Content, {
|
|
22
|
+
ref,
|
|
20
23
|
"data-slot": "collapsible-content",
|
|
21
24
|
className: cn("overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down", className),
|
|
22
25
|
...props
|
|
23
26
|
});
|
|
24
|
-
}
|
|
27
|
+
});
|
|
25
28
|
//#endregion
|
|
26
|
-
export { Collapsible
|
|
29
|
+
export { Collapsible, CollapsibleContent, CollapsibleTrigger };
|
|
27
30
|
|
|
28
31
|
//# sourceMappingURL=collapsible.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collapsible.js","names":["
|
|
1
|
+
{"version":3,"file":"collapsible.js","names":["CollapsiblePrimitive.Root","CollapsiblePrimitive.Trigger","CollapsiblePrimitive.Content"],"sources":["../../src/components/collapsible.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as CollapsiblePrimitive from '../primitives/vendor/radix/react-collapsible';\n\nfunction Collapsible(props: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {\n return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />;\n}\n\nconst CollapsibleTrigger = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<typeof CollapsiblePrimitive.Trigger>\n>(function CollapsibleTrigger(props, ref) {\n return <CollapsiblePrimitive.Trigger ref={ref} data-slot=\"collapsible-trigger\" {...props} />;\n});\n\nconst CollapsibleContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof CollapsiblePrimitive.Content>\n>(function CollapsibleContent({ className, ...props }, ref) {\n return (\n <CollapsiblePrimitive.Content\n ref={ref}\n data-slot=\"collapsible-content\"\n className={cn(\n 'overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down',\n className,\n )}\n {...props}\n />\n );\n});\n\nexport { Collapsible, CollapsibleContent, CollapsibleTrigger };\n"],"mappings":";;;;;;AAKA,SAAS,YAAY,OAA+D;CAClF,OAAO,oBAACA,MAAD;EAA2B,aAAU;EAAc,GAAI;CAAQ,CAAA;AACxE;AAEA,IAAM,qBAAqB,MAAM,WAG/B,SAAS,mBAAmB,OAAO,KAAK;CACxC,OAAO,oBAACC,SAAD;EAAmC;EAAK,aAAU;EAAsB,GAAI;CAAQ,CAAA;AAC7F,CAAC;AAED,IAAM,qBAAqB,MAAM,WAG/B,SAAS,mBAAmB,EAAE,WAAW,GAAG,SAAS,KAAK;CAC1D,OACE,oBAACC,SAAD;EACO;EACL,aAAU;EACV,WAAW,GACT,yGACA,SACF;EACA,GAAI;CACL,CAAA;AAEL,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { type FieldWidth } from './input';
|
|
2
3
|
export interface ComboboxOption {
|
|
3
4
|
value: string;
|
|
4
5
|
label: string;
|
|
@@ -8,8 +9,11 @@ export interface ComboboxOption {
|
|
|
8
9
|
}
|
|
9
10
|
export interface ComboboxProps {
|
|
10
11
|
options: ComboboxOption[];
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
/** Controlled: the chosen value, or null. Leave out for an uncontrolled combobox. */
|
|
13
|
+
value?: string | null;
|
|
14
|
+
/** The starting value when uncontrolled. */
|
|
15
|
+
defaultValue?: string | null;
|
|
16
|
+
onValueChange?: (value: string | null) => void;
|
|
13
17
|
placeholder?: string;
|
|
14
18
|
searchPlaceholder?: string;
|
|
15
19
|
emptyText?: string;
|
|
@@ -23,15 +27,21 @@ export interface ComboboxProps {
|
|
|
23
27
|
onSearchChange?: (search: string) => void;
|
|
24
28
|
/** Let the person clear the selection by picking it again. */
|
|
25
29
|
clearable?: boolean;
|
|
30
|
+
/** How wide the trigger is from 640px up; full width on phones. */
|
|
31
|
+
width?: FieldWidth;
|
|
26
32
|
id?: string;
|
|
27
33
|
className?: string;
|
|
28
34
|
'aria-invalid'?: boolean;
|
|
29
35
|
'aria-label'?: string;
|
|
30
36
|
'aria-labelledby'?: string;
|
|
31
37
|
'aria-describedby'?: string;
|
|
38
|
+
'aria-required'?: boolean;
|
|
32
39
|
name?: string;
|
|
33
40
|
required?: boolean;
|
|
34
41
|
}
|
|
35
|
-
/**
|
|
36
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Pick one value from a long or remote list by typing. The ref reaches the
|
|
44
|
+
* trigger button, so a form library or an ErrorSummary can focus it.
|
|
45
|
+
*/
|
|
46
|
+
declare const Combobox: React.ForwardRefExoticComponent<ComboboxProps & React.RefAttributes<HTMLButtonElement>>;
|
|
37
47
|
export { Combobox };
|