@devalok/shilp-sutra 0.49.2 → 0.49.4
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/dist/_chunks/badge-group.js.map +1 -1
- package/dist/_chunks/block-shell.js.map +1 -1
- package/dist/_chunks/chart-container.js.map +1 -1
- package/dist/_chunks/chat.js.map +1 -1
- package/dist/_chunks/colors.js.map +1 -1
- package/dist/_chunks/document-preview.js.map +1 -1
- package/dist/_chunks/emoji-suggestion.js.map +1 -1
- package/dist/_chunks/emoji.js +2 -2
- package/dist/_chunks/emoji.js.map +1 -1
- package/dist/_chunks/field-state.js.map +1 -1
- package/dist/_chunks/grid-lines.js.map +1 -1
- package/dist/_chunks/image-preview.js.map +1 -1
- package/dist/_chunks/keybinding.js.map +1 -1
- package/dist/_chunks/link-context.js.map +1 -1
- package/dist/_chunks/mention-suggestion.js.map +1 -1
- package/dist/_chunks/motion-provider.js.map +1 -1
- package/dist/_chunks/normalize-icon.js.map +1 -1
- package/dist/_chunks/oauth-button.js.map +1 -1
- package/dist/_chunks/popover.js.map +1 -1
- package/dist/_chunks/primitives.js +1129 -1162
- package/dist/_chunks/primitives.js.map +1 -1
- package/dist/_chunks/shared.js.map +1 -1
- package/dist/_chunks/success.js.map +1 -1
- package/dist/_chunks/tiptap.js +4283 -3460
- package/dist/_chunks/tiptap.js.map +1 -1
- package/dist/_chunks/tooltip.js.map +1 -1
- package/dist/_chunks/tree-view.js +1 -1
- package/dist/_chunks/tree-view.js.map +1 -1
- package/dist/_chunks/use-calendar.js.map +1 -1
- package/dist/_chunks/use-controllable-open.js.map +1 -1
- package/dist/_chunks/vendor-client.js +86 -59
- package/dist/_chunks/vendor-client.js.map +1 -1
- package/dist/_chunks/vendor-utils.js +555 -513
- package/dist/_chunks/vendor-utils.js.map +1 -1
- package/dist/ai/ai-command-provider.d.ts +1 -1
- package/dist/ai/ai-command-provider.d.ts.map +1 -1
- package/dist/ai/ai-command-provider.js.map +1 -1
- package/dist/ai/block-renderer.js.map +1 -1
- package/dist/ai/blocks/block-shell.d.ts +1 -1
- package/dist/ai/blocks/block-shell.d.ts.map +1 -1
- package/dist/ai/blocks/block-table.d.ts +1 -1
- package/dist/ai/blocks/block-table.d.ts.map +1 -1
- package/dist/ai/blocks/confirm.d.ts +1 -1
- package/dist/ai/blocks/confirm.d.ts.map +1 -1
- package/dist/ai/blocks/divider.d.ts +1 -1
- package/dist/ai/blocks/divider.d.ts.map +1 -1
- package/dist/ai/blocks/error.d.ts +1 -1
- package/dist/ai/blocks/error.d.ts.map +1 -1
- package/dist/ai/blocks/error.js.map +1 -1
- package/dist/ai/blocks/info.d.ts +1 -1
- package/dist/ai/blocks/info.d.ts.map +1 -1
- package/dist/ai/blocks/loading.d.ts +1 -1
- package/dist/ai/blocks/loading.d.ts.map +1 -1
- package/dist/ai/blocks/stat-row.d.ts +1 -1
- package/dist/ai/blocks/stat-row.d.ts.map +1 -1
- package/dist/ai/blocks/success.d.ts +1 -1
- package/dist/ai/blocks/success.d.ts.map +1 -1
- package/dist/ai/blocks/text.d.ts +1 -1
- package/dist/ai/blocks/text.d.ts.map +1 -1
- package/dist/ai/blocks/text.js.map +1 -1
- package/dist/ai/command-bar.js.map +1 -1
- package/dist/ai/conversation.js.map +1 -1
- package/dist/ai/devadoot-icon.d.ts +1 -1
- package/dist/ai/devadoot-icon.d.ts.map +1 -1
- package/dist/ai/index.js.map +1 -1
- package/dist/composed/activity-feed.js.map +1 -1
- package/dist/composed/avatar-group.js.map +1 -1
- package/dist/composed/bulk-action-bar.js.map +1 -1
- package/dist/composed/command-palette.js.map +1 -1
- package/dist/composed/confirm-dialog.js.map +1 -1
- package/dist/composed/content-card.js.map +1 -1
- package/dist/composed/date-picker/index.js.map +1 -1
- package/dist/composed/deadline-indicator.js.map +1 -1
- package/dist/composed/emoji-picker.d.ts +2 -2
- package/dist/composed/emoji-picker.d.ts.map +1 -1
- package/dist/composed/emoji-picker.js.map +1 -1
- package/dist/composed/empty-state.js.map +1 -1
- package/dist/composed/error-boundary.d.ts +1 -1
- package/dist/composed/error-boundary.js.map +1 -1
- package/dist/composed/extensions/emoji-node.js.map +1 -1
- package/dist/composed/file-preview/audio-preview.d.ts +2 -1
- package/dist/composed/file-preview/audio-preview.d.ts.map +1 -1
- package/dist/composed/file-preview/document-preview.d.ts +2 -1
- package/dist/composed/file-preview/document-preview.d.ts.map +1 -1
- package/dist/composed/file-preview/embed-preview.d.ts +2 -1
- package/dist/composed/file-preview/embed-preview.d.ts.map +1 -1
- package/dist/composed/file-preview/image-preview.d.ts +2 -1
- package/dist/composed/file-preview/image-preview.d.ts.map +1 -1
- package/dist/composed/file-preview/shared.d.ts +5 -5
- package/dist/composed/file-preview/shared.d.ts.map +1 -1
- package/dist/composed/file-preview/video-preview.d.ts +2 -1
- package/dist/composed/file-preview/video-preview.d.ts.map +1 -1
- package/dist/composed/file-preview.d.ts +1 -1
- package/dist/composed/file-preview.d.ts.map +1 -1
- package/dist/composed/file-preview.js.map +1 -1
- package/dist/composed/filter-bar.d.ts +2 -2
- package/dist/composed/filter-bar.d.ts.map +1 -1
- package/dist/composed/filter-bar.js.map +1 -1
- package/dist/composed/form-section.js.map +1 -1
- package/dist/composed/global-loading.js.map +1 -1
- package/dist/composed/inline-edit.js.map +1 -1
- package/dist/composed/lib/string-utils.js.map +1 -1
- package/dist/composed/loading-skeleton.js.map +1 -1
- package/dist/composed/markdown-viewer.js.map +1 -1
- package/dist/composed/master-detail.d.ts +3 -3
- package/dist/composed/master-detail.d.ts.map +1 -1
- package/dist/composed/master-detail.js.map +1 -1
- package/dist/composed/member-picker.js.map +1 -1
- package/dist/composed/multi-select-popover.js.map +1 -1
- package/dist/composed/page-header.js.map +1 -1
- package/dist/composed/page-skeletons.js.map +1 -1
- package/dist/composed/priority-indicator.js.map +1 -1
- package/dist/composed/responsive-modal.js +8 -4
- package/dist/composed/responsive-modal.js.map +1 -1
- package/dist/composed/rich-chat-input/attachment-strip.d.ts +2 -1
- package/dist/composed/rich-chat-input/attachment-strip.d.ts.map +1 -1
- package/dist/composed/rich-chat-input/chat-toolbar.d.ts +17 -17
- package/dist/composed/rich-chat-input/chat-toolbar.d.ts.map +1 -1
- package/dist/composed/rich-chat-input/recording-overlay.d.ts +2 -1
- package/dist/composed/rich-chat-input/recording-overlay.d.ts.map +1 -1
- package/dist/composed/rich-chat-input/reply-banner.d.ts +2 -1
- package/dist/composed/rich-chat-input/reply-banner.d.ts.map +1 -1
- package/dist/composed/rich-chat-input/schedule-send.d.ts +4 -3
- package/dist/composed/rich-chat-input/schedule-send.d.ts.map +1 -1
- package/dist/composed/rich-chat-input.js +2 -1
- package/dist/composed/rich-chat-input.js.map +1 -1
- package/dist/composed/rich-text-editor.js +2 -1
- package/dist/composed/rich-text-editor.js.map +1 -1
- package/dist/composed/schedule-view.js.map +1 -1
- package/dist/composed/simple-tooltip.js.map +1 -1
- package/dist/composed/status-badge.js.map +1 -1
- package/dist/hooks/use-color-mode.js +2 -1
- package/dist/hooks/use-color-mode.js.map +1 -1
- package/dist/hooks/use-mobile.js.map +1 -1
- package/dist/hooks/use-touch-device.js.map +1 -1
- package/dist/hooks/use-viewport-height.js.map +1 -1
- package/dist/motion/index.js.map +1 -1
- package/dist/motion/motion-provider.d.ts +1 -1
- package/dist/motion/motion-provider.d.ts.map +1 -1
- package/dist/motion/primitives-index.js.map +1 -1
- package/dist/primitives/_internal/react-dismissable-layer.d.ts.map +1 -1
- package/dist/primitives/_internal/react-focus-scope.d.ts.map +1 -1
- package/dist/shell/app-command-palette.js.map +1 -1
- package/dist/shell/bottom-navbar.js.map +1 -1
- package/dist/shell/command-registry.d.ts +1 -1
- package/dist/shell/command-registry.d.ts.map +1 -1
- package/dist/shell/command-registry.js.map +1 -1
- package/dist/shell/notification-center.js.map +1 -1
- package/dist/shell/notification-preferences.js.map +1 -1
- package/dist/shell/sidebar.js.map +1 -1
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/ui/accordion.js.map +1 -1
- package/dist/ui/alert-dialog.js.map +1 -1
- package/dist/ui/alert.js.map +1 -1
- package/dist/ui/aspect-ratio.js.map +1 -1
- package/dist/ui/autocomplete.js.map +1 -1
- package/dist/ui/avatar.d.ts +1 -1
- package/dist/ui/avatar.js.map +1 -1
- package/dist/ui/badge-group.d.ts +1 -1
- package/dist/ui/badge-group.d.ts.map +1 -1
- package/dist/ui/badge-indicator.d.ts +1 -1
- package/dist/ui/badge-indicator.d.ts.map +1 -1
- package/dist/ui/badge-indicator.js.map +1 -1
- package/dist/ui/banner.js.map +1 -1
- package/dist/ui/breadcrumb.d.ts +2 -2
- package/dist/ui/breadcrumb.js.map +1 -1
- package/dist/ui/button-group.js.map +1 -1
- package/dist/ui/button-processing.d.ts +2 -1
- package/dist/ui/button-processing.d.ts.map +1 -1
- package/dist/ui/button-processing.js.map +1 -1
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/card.js +1 -1
- package/dist/ui/card.js.map +1 -1
- package/dist/ui/charts/_internal/axes.d.ts +2 -1
- package/dist/ui/charts/_internal/axes.d.ts.map +1 -1
- package/dist/ui/charts/_internal/grid-lines.d.ts +2 -1
- package/dist/ui/charts/_internal/grid-lines.d.ts.map +1 -1
- package/dist/ui/charts/_internal/legend.d.ts +2 -1
- package/dist/ui/charts/_internal/legend.d.ts.map +1 -1
- package/dist/ui/charts/_internal/tooltip.d.ts +1 -1
- package/dist/ui/charts/_internal/tooltip.d.ts.map +1 -1
- package/dist/ui/charts/area-chart.js.map +1 -1
- package/dist/ui/charts/bar-chart.js.map +1 -1
- package/dist/ui/charts/gauge-chart.js.map +1 -1
- package/dist/ui/charts/line-chart.js.map +1 -1
- package/dist/ui/charts/pie-chart.js.map +1 -1
- package/dist/ui/charts/radar-chart.js.map +1 -1
- package/dist/ui/charts/sparkline.js +2 -2
- package/dist/ui/charts/sparkline.js.map +1 -1
- package/dist/ui/chat/date-separator.d.ts +2 -1
- package/dist/ui/chat/date-separator.d.ts.map +1 -1
- package/dist/ui/chat/message.d.ts +8 -8
- package/dist/ui/chat/message.d.ts.map +1 -1
- package/dist/ui/chat/typing-indicator.d.ts +2 -1
- package/dist/ui/chat/typing-indicator.d.ts.map +1 -1
- package/dist/ui/chat/unread-separator.d.ts +2 -1
- package/dist/ui/chat/unread-separator.d.ts.map +1 -1
- package/dist/ui/checkbox.js.map +1 -1
- package/dist/ui/code.js.map +1 -1
- package/dist/ui/collapsible.js.map +1 -1
- package/dist/ui/color-input.js.map +1 -1
- package/dist/ui/color-swatch.js.map +1 -1
- package/dist/ui/combobox.js +15 -6
- package/dist/ui/combobox.js.map +1 -1
- package/dist/ui/container.js.map +1 -1
- package/dist/ui/context-menu.d.ts +2 -2
- package/dist/ui/context-menu.js.map +1 -1
- package/dist/ui/data-table-body.d.ts +1 -1
- package/dist/ui/data-table-body.d.ts.map +1 -1
- package/dist/ui/data-table-body.js +1 -1
- package/dist/ui/data-table-body.js.map +1 -1
- package/dist/ui/data-table-bulk-actions.d.ts +2 -1
- package/dist/ui/data-table-bulk-actions.d.ts.map +1 -1
- package/dist/ui/data-table-bulk-actions.js.map +1 -1
- package/dist/ui/data-table-card.d.ts +1 -1
- package/dist/ui/data-table-card.d.ts.map +1 -1
- package/dist/ui/data-table-card.js.map +1 -1
- package/dist/ui/data-table-context.d.ts +1 -1
- package/dist/ui/data-table-context.d.ts.map +1 -1
- package/dist/ui/data-table-context.js.map +1 -1
- package/dist/ui/data-table-header.d.ts +2 -1
- package/dist/ui/data-table-header.d.ts.map +1 -1
- package/dist/ui/data-table-header.js.map +1 -1
- package/dist/ui/data-table-pagination.d.ts +2 -1
- package/dist/ui/data-table-pagination.d.ts.map +1 -1
- package/dist/ui/data-table-pagination.js.map +1 -1
- package/dist/ui/data-table-toolbar.d.ts +1 -1
- package/dist/ui/data-table-toolbar.d.ts.map +1 -1
- package/dist/ui/data-table-toolbar.js.map +1 -1
- package/dist/ui/data-table.d.ts +1 -1
- package/dist/ui/data-table.d.ts.map +1 -1
- package/dist/ui/data-table.js.map +1 -1
- package/dist/ui/devalok-grain.d.ts +2 -1
- package/dist/ui/devalok-grain.d.ts.map +1 -1
- package/dist/ui/devalok-grain.js.map +1 -1
- package/dist/ui/dialog.js.map +1 -1
- package/dist/ui/dot.js +1 -1
- package/dist/ui/dot.js.map +1 -1
- package/dist/ui/dropdown-menu.d.ts +1 -1
- package/dist/ui/dropdown-menu.js.map +1 -1
- package/dist/ui/file-upload.js.map +1 -1
- package/dist/ui/form.js.map +1 -1
- package/dist/ui/hover-card.js.map +1 -1
- package/dist/ui/icon-button.js.map +1 -1
- package/dist/ui/icon-context.d.ts +1 -1
- package/dist/ui/icon-context.d.ts.map +1 -1
- package/dist/ui/icon-context.js.map +1 -1
- package/dist/ui/icon-group.js.map +1 -1
- package/dist/ui/icon.js.map +1 -1
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/input-otp.js.map +1 -1
- package/dist/ui/input.js.map +1 -1
- package/dist/ui/label.js.map +1 -1
- package/dist/ui/lib/bottom-sheet.d.ts +1 -1
- package/dist/ui/lib/bottom-sheet.d.ts.map +1 -1
- package/dist/ui/lib/date-utils.js.map +1 -1
- package/dist/ui/lib/link-context.d.ts +1 -1
- package/dist/ui/lib/link-context.d.ts.map +1 -1
- package/dist/ui/lib/motion.js.map +1 -1
- package/dist/ui/lib/utils.js.map +1 -1
- package/dist/ui/link.js.map +1 -1
- package/dist/ui/menubar.d.ts +1 -1
- package/dist/ui/menubar.js.map +1 -1
- package/dist/ui/navigation-menu.js +14 -9
- package/dist/ui/navigation-menu.js.map +1 -1
- package/dist/ui/number-input.js.map +1 -1
- package/dist/ui/pagination.js.map +1 -1
- package/dist/ui/progress-ring.js.map +1 -1
- package/dist/ui/progress.js.map +1 -1
- package/dist/ui/radio.js.map +1 -1
- package/dist/ui/search-input.js.map +1 -1
- package/dist/ui/segmented-control.js.map +1 -1
- package/dist/ui/select.js.map +1 -1
- package/dist/ui/separator.js.map +1 -1
- package/dist/ui/sheet.js.map +1 -1
- package/dist/ui/sidebar.js +1 -1
- package/dist/ui/sidebar.js.map +1 -1
- package/dist/ui/skeleton.js.map +1 -1
- package/dist/ui/slider.js.map +1 -1
- package/dist/ui/spinner.js.map +1 -1
- package/dist/ui/split-button.js.map +1 -1
- package/dist/ui/stack.js.map +1 -1
- package/dist/ui/stat-card.js.map +1 -1
- package/dist/ui/stat-flash.d.ts +2 -1
- package/dist/ui/stat-flash.d.ts.map +1 -1
- package/dist/ui/stat-flash.js +1 -1
- package/dist/ui/stat-flash.js.map +1 -1
- package/dist/ui/stepper.d.ts +1 -1
- package/dist/ui/stepper.d.ts.map +1 -1
- package/dist/ui/stepper.js +1 -1
- package/dist/ui/stepper.js.map +1 -1
- package/dist/ui/surface.js.map +1 -1
- package/dist/ui/switch.d.ts +1 -1
- package/dist/ui/switch.js.map +1 -1
- package/dist/ui/table-row-link.js.map +1 -1
- package/dist/ui/table.js.map +1 -1
- package/dist/ui/tabs.js.map +1 -1
- package/dist/ui/text.js.map +1 -1
- package/dist/ui/textarea.js.map +1 -1
- package/dist/ui/toast-registry.js.map +1 -1
- package/dist/ui/toast.d.ts +4 -4
- package/dist/ui/toast.d.ts.map +1 -1
- package/dist/ui/toast.js +5 -3
- package/dist/ui/toast.js.map +1 -1
- package/dist/ui/toaster.js.map +1 -1
- package/dist/ui/toggle-group.js.map +1 -1
- package/dist/ui/toggle.js.map +1 -1
- package/dist/ui/tooltip.js.map +1 -1
- package/dist/ui/truncated-text.js.map +1 -1
- package/dist/ui/visually-hidden.js.map +1 -1
- package/llms.txt +1 -1
- package/mcp-manifest.json +1 -1
- package/package.json +21 -21
- package/skill/SKILL.md +1 -1
- package/skill/references/components.md +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"master-detail.d.ts","sourceRoot":"","sources":["../../src/composed/master-detail.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC7E,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IAC/B,mEAAmE;IACnE,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAA;CAChD;AAED,UAAU,qBAAsB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;CAAG;AAE/E,UAAU,uBAAwB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;CAAG;AAEjF,UAAU,yBAA0B,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IACvF,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AA0DD,iBAAS,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,qBAAqB,
|
|
1
|
+
{"version":3,"file":"master-detail.d.ts","sourceRoot":"","sources":["../../src/composed/master-detail.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC7E,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IAC/B,mEAAmE;IACnE,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAA;CAChD;AAED,UAAU,qBAAsB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;CAAG;AAE/E,UAAU,uBAAwB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;CAAG;AAEjF,UAAU,yBAA0B,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IACvF,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AA0DD,iBAAS,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,qBAAqB,4BAgFjF;AAED,iBAAS,kBAAkB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,uBAAuB,4BA4BrF;AAwCD,iBAAS,gBAAgB,CAAC,EACxB,QAAe,EACf,MAAM,EACN,WAAqB,EACrB,UAAiB,EACjB,UAAU,EACV,UAAU,EACV,QAAQ,EACR,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,iBAAiB,qBA4BnB;AAED,QAAA,MAAM,YAAY;;;;CAIhB,CAAA;AAEF,OAAO,EAAE,YAAY,EAAE,CAAA;AACvB,YAAY,EAAE,yBAAyB,EAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"master-detail.js","names":[],"sources":["../../src/composed/master-detail.tsx"],"sourcesContent":["'use client'\n\nimport { IconArrowLeft } from '@tabler/icons-react'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\n\n// ============================================================\n// Types\n// ============================================================\n\n/**\n * A responsive master-detail layout. On desktop, shows a fixed-width list panel\n * alongside a detail panel. Below the breakpoint, switches to a stacked view\n * with animated transitions and a back button.\n */\nexport interface MasterDetailProps extends React.HTMLAttributes<HTMLDivElement> {\n /** ID of currently selected item. null = no selection (show list on mobile). */\n selected?: string | null\n /** Called when mobile back button is pressed. */\n onBack?: () => void\n /** Master panel width on desktop. @default '280px' */\n masterWidth?: string\n /** Breakpoint below which stacked mode activates. @default 'md' */\n breakpoint?: 'sm' | 'md' | 'lg'\n /** Content to show in the detail pane when nothing is selected. */\n emptyState?: React.ReactNode\n /** Called when user presses ArrowUp/ArrowDown while the list has focus. */\n onNavigate?: (direction: 'up' | 'down') => void\n}\n\ninterface MasterDetailListProps extends React.HTMLAttributes<HTMLDivElement> {}\n\ninterface MasterDetailDetailProps extends React.HTMLAttributes<HTMLDivElement> {}\n\ninterface MasterDetailListItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n active?: boolean\n}\n\n// ============================================================\n// Breakpoint values\n// ============================================================\n\nconst breakpoints: Record<string, string> = {\n sm: '640px',\n md: '768px',\n lg: '1024px',\n}\n\nfunction useMediaQuery(query: string): boolean {\n const [matches, setMatches] = React.useState(false)\n\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const mql = window.matchMedia(query)\n setMatches(mql.matches)\n const handler = (e: MediaQueryListEvent) => setMatches(e.matches)\n mql.addEventListener('change', handler)\n return () => mql.removeEventListener('change', handler)\n }, [query])\n\n return matches\n}\n\n// ============================================================\n// Context\n// ============================================================\n\ninterface MasterDetailContextValue {\n selected: string | null\n isMobile: boolean\n onBack?: () => void\n emptyState?: React.ReactNode\n onNavigate?: (direction: 'up' | 'down') => void\n activeIndex: number\n setActiveIndex: (index: number) => void\n registerItemRef: (index: number, el: HTMLButtonElement | null) => void\n itemCount: number\n setItemCount: (count: number) => void\n}\n\nconst MasterDetailContext = React.createContext<MasterDetailContextValue>({\n selected: null,\n isMobile: false,\n activeIndex: 0,\n setActiveIndex: () => {},\n registerItemRef: () => {},\n itemCount: 0,\n setItemCount: () => {},\n})\n\n// ============================================================\n// Compound Components\n// ============================================================\n\nfunction MasterDetailList({ children, className, ...props }: MasterDetailListProps) {\n const { selected, isMobile, onNavigate, activeIndex, setActiveIndex, registerItemRef, setItemCount } = React.useContext(MasterDetailContext)\n const itemRefsLocal = React.useRef<(HTMLButtonElement | null)[]>([])\n\n // Count children and register with context\n const childCount = React.Children.count(children)\n React.useEffect(() => {\n setItemCount(childCount)\n }, [childCount, setItemCount])\n\n // Keep roving focus in sync with the active/selected item, so keyboard nav\n // starts on the current selection instead of always the first row.\n const activeChildIndex = React.useMemo(() => {\n let idx = -1\n React.Children.forEach(children, (child, i) => {\n if (\n React.isValidElement(child) &&\n (child.props as { active?: boolean }).active\n ) {\n idx = i\n }\n })\n return idx\n }, [children])\n React.useEffect(() => {\n if (activeChildIndex >= 0) setActiveIndex(activeChildIndex)\n }, [activeChildIndex, setActiveIndex])\n\n // On mobile, hide list when something is selected\n if (isMobile && selected) return null\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n const next = Math.min(activeIndex + 1, childCount - 1)\n setActiveIndex(next)\n itemRefsLocal.current[next]?.focus()\n onNavigate?.('down')\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n const prev = Math.max(activeIndex - 1, 0)\n setActiveIndex(prev)\n itemRefsLocal.current[prev]?.focus()\n onNavigate?.('up')\n } else if (e.key === 'Home') {\n e.preventDefault()\n setActiveIndex(0)\n itemRefsLocal.current[0]?.focus()\n } else if (e.key === 'End') {\n e.preventDefault()\n const last = childCount - 1\n setActiveIndex(last)\n itemRefsLocal.current[last]?.focus()\n }\n }\n\n return (\n <div\n role=\"listbox\"\n tabIndex={-1}\n className={cn(\n 'overflow-y-auto focus-visible:outline-hidden',\n !isMobile && 'border-r border-surface-border',\n className,\n )}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {React.Children.map(children, (child, i) => {\n if (!React.isValidElement(child)) return child\n return React.cloneElement(child as React.ReactElement<any>, {\n ref: (el: HTMLButtonElement | null) => {\n itemRefsLocal.current[i] = el\n registerItemRef(i, el)\n },\n tabIndex: i === activeIndex ? 0 : -1,\n })\n })}\n </div>\n )\n}\n\nfunction MasterDetailDetail({ children, className, ...props }: MasterDetailDetailProps) {\n const { selected, isMobile, onBack, emptyState } = React.useContext(MasterDetailContext)\n\n // On mobile, hide detail when nothing selected\n if (isMobile && !selected) return null\n\n return (\n <div className={cn('flex-1 overflow-y-auto', className)} {...props}>\n {isMobile && onBack && (\n <div className=\"border-b border-surface-border px-ds-04 py-ds-03\">\n <Button variant=\"ghost\" size=\"xs\" onClick={onBack} startIcon={<Icon icon={IconArrowLeft} />}>\n Back\n </Button>\n </div>\n )}\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={selected ?? 'empty'}\n initial={isMobile ? { x: 20, opacity: 0 } : false}\n animate={{ x: 0, opacity: 1 }}\n exit={isMobile ? { x: -20, opacity: 0 } : undefined}\n transition={springs.snappy}\n >\n {selected ? children : (emptyState ?? children)}\n </motion.div>\n </AnimatePresence>\n </div>\n )\n}\n\nconst MasterDetailListItem = React.forwardRef<HTMLButtonElement, MasterDetailListItemProps>(\n function MasterDetailListItem({ active = false, children, className, onKeyDown, ...props }, ref) {\n function handleKeyDown(e: React.KeyboardEvent<HTMLButtonElement>) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n ;(e.target as HTMLButtonElement).click()\n }\n onKeyDown?.(e)\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"option\"\n aria-selected={active}\n data-active={active || undefined}\n onKeyDown={handleKeyDown}\n className={cn(\n 'flex w-full items-center px-ds-04 py-ds-03 text-left text-ds-md font-sans text-surface-fg',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-7 focus-visible:ring-inset',\n active && 'bg-accent-2 text-accent-11 font-medium',\n className,\n )}\n {...props}\n >\n {children}\n </button>\n )\n },\n)\n\n// ============================================================\n// MasterDetail Root\n// ============================================================\n\nfunction MasterDetailRoot({\n selected = null,\n onBack,\n masterWidth = '280px',\n breakpoint = 'md',\n emptyState,\n onNavigate,\n children,\n className,\n style,\n ...props\n}: MasterDetailProps) {\n const isMobile = useMediaQuery(`(max-width: ${breakpoints[breakpoint]})`)\n const [activeIndex, setActiveIndex] = React.useState(0)\n const [itemCount, setItemCount] = React.useState(0)\n const itemRefs = React.useRef<(HTMLButtonElement | null)[]>([])\n\n const registerItemRef = React.useCallback((index: number, el: HTMLButtonElement | null) => {\n itemRefs.current[index] = el\n }, [])\n\n return (\n <MasterDetailContext.Provider value={{ selected, isMobile, onBack, emptyState, onNavigate, activeIndex, setActiveIndex, registerItemRef, itemCount, setItemCount }}>\n <div\n className={cn(\n 'flex h-full',\n !isMobile && 'grid',\n className,\n )}\n style={{\n ...style,\n ...(isMobile ? {} : { gridTemplateColumns: `${masterWidth} 1fr` }),\n }}\n {...props}\n >\n {children}\n </div>\n </MasterDetailContext.Provider>\n )\n}\n\nconst MasterDetail = Object.assign(MasterDetailRoot, {\n List: MasterDetailList,\n Detail: MasterDetailDetail,\n ListItem: MasterDetailListItem,\n})\n\nexport { MasterDetail }\nexport type { MasterDetailListItemProps }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"master-detail.js","names":[],"sources":["../../src/composed/master-detail.tsx"],"sourcesContent":["'use client'\n\nimport { IconArrowLeft } from '@tabler/icons-react'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\n\n// ============================================================\n// Types\n// ============================================================\n\n/**\n * A responsive master-detail layout. On desktop, shows a fixed-width list panel\n * alongside a detail panel. Below the breakpoint, switches to a stacked view\n * with animated transitions and a back button.\n */\nexport interface MasterDetailProps extends React.HTMLAttributes<HTMLDivElement> {\n /** ID of currently selected item. null = no selection (show list on mobile). */\n selected?: string | null\n /** Called when mobile back button is pressed. */\n onBack?: () => void\n /** Master panel width on desktop. @default '280px' */\n masterWidth?: string\n /** Breakpoint below which stacked mode activates. @default 'md' */\n breakpoint?: 'sm' | 'md' | 'lg'\n /** Content to show in the detail pane when nothing is selected. */\n emptyState?: React.ReactNode\n /** Called when user presses ArrowUp/ArrowDown while the list has focus. */\n onNavigate?: (direction: 'up' | 'down') => void\n}\n\ninterface MasterDetailListProps extends React.HTMLAttributes<HTMLDivElement> {}\n\ninterface MasterDetailDetailProps extends React.HTMLAttributes<HTMLDivElement> {}\n\ninterface MasterDetailListItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n active?: boolean\n}\n\n// ============================================================\n// Breakpoint values\n// ============================================================\n\nconst breakpoints: Record<string, string> = {\n sm: '640px',\n md: '768px',\n lg: '1024px',\n}\n\nfunction useMediaQuery(query: string): boolean {\n const [matches, setMatches] = React.useState(false)\n\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const mql = window.matchMedia(query)\n setMatches(mql.matches)\n const handler = (e: MediaQueryListEvent) => setMatches(e.matches)\n mql.addEventListener('change', handler)\n return () => mql.removeEventListener('change', handler)\n }, [query])\n\n return matches\n}\n\n// ============================================================\n// Context\n// ============================================================\n\ninterface MasterDetailContextValue {\n selected: string | null\n isMobile: boolean\n onBack?: () => void\n emptyState?: React.ReactNode\n onNavigate?: (direction: 'up' | 'down') => void\n activeIndex: number\n setActiveIndex: (index: number) => void\n registerItemRef: (index: number, el: HTMLButtonElement | null) => void\n itemCount: number\n setItemCount: (count: number) => void\n}\n\nconst MasterDetailContext = React.createContext<MasterDetailContextValue>({\n selected: null,\n isMobile: false,\n activeIndex: 0,\n setActiveIndex: () => {},\n registerItemRef: () => {},\n itemCount: 0,\n setItemCount: () => {},\n})\n\n// ============================================================\n// Compound Components\n// ============================================================\n\nfunction MasterDetailList({ children, className, ...props }: MasterDetailListProps) {\n const { selected, isMobile, onNavigate, activeIndex, setActiveIndex, registerItemRef, setItemCount } = React.useContext(MasterDetailContext)\n const itemRefsLocal = React.useRef<(HTMLButtonElement | null)[]>([])\n\n // Count children and register with context\n const childCount = React.Children.count(children)\n React.useEffect(() => {\n setItemCount(childCount)\n }, [childCount, setItemCount])\n\n // Keep roving focus in sync with the active/selected item, so keyboard nav\n // starts on the current selection instead of always the first row.\n const activeChildIndex = React.useMemo(() => {\n let idx = -1\n React.Children.forEach(children, (child, i) => {\n if (\n React.isValidElement(child) &&\n (child.props as { active?: boolean }).active\n ) {\n idx = i\n }\n })\n return idx\n }, [children])\n React.useEffect(() => {\n if (activeChildIndex >= 0) setActiveIndex(activeChildIndex)\n }, [activeChildIndex, setActiveIndex])\n\n // On mobile, hide list when something is selected\n if (isMobile && selected) return null\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n const next = Math.min(activeIndex + 1, childCount - 1)\n setActiveIndex(next)\n itemRefsLocal.current[next]?.focus()\n onNavigate?.('down')\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n const prev = Math.max(activeIndex - 1, 0)\n setActiveIndex(prev)\n itemRefsLocal.current[prev]?.focus()\n onNavigate?.('up')\n } else if (e.key === 'Home') {\n e.preventDefault()\n setActiveIndex(0)\n itemRefsLocal.current[0]?.focus()\n } else if (e.key === 'End') {\n e.preventDefault()\n const last = childCount - 1\n setActiveIndex(last)\n itemRefsLocal.current[last]?.focus()\n }\n }\n\n return (\n <div\n role=\"listbox\"\n tabIndex={-1}\n className={cn(\n 'overflow-y-auto focus-visible:outline-hidden',\n !isMobile && 'border-r border-surface-border',\n className,\n )}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {React.Children.map(children, (child, i) => {\n if (!React.isValidElement(child)) return child\n return React.cloneElement(child as React.ReactElement<any>, {\n ref: (el: HTMLButtonElement | null) => {\n itemRefsLocal.current[i] = el\n registerItemRef(i, el)\n },\n tabIndex: i === activeIndex ? 0 : -1,\n })\n })}\n </div>\n )\n}\n\nfunction MasterDetailDetail({ children, className, ...props }: MasterDetailDetailProps) {\n const { selected, isMobile, onBack, emptyState } = React.useContext(MasterDetailContext)\n\n // On mobile, hide detail when nothing selected\n if (isMobile && !selected) return null\n\n return (\n <div className={cn('flex-1 overflow-y-auto', className)} {...props}>\n {isMobile && onBack && (\n <div className=\"border-b border-surface-border px-ds-04 py-ds-03\">\n <Button variant=\"ghost\" size=\"xs\" onClick={onBack} startIcon={<Icon icon={IconArrowLeft} />}>\n Back\n </Button>\n </div>\n )}\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={selected ?? 'empty'}\n initial={isMobile ? { x: 20, opacity: 0 } : false}\n animate={{ x: 0, opacity: 1 }}\n exit={isMobile ? { x: -20, opacity: 0 } : undefined}\n transition={springs.snappy}\n >\n {selected ? children : (emptyState ?? children)}\n </motion.div>\n </AnimatePresence>\n </div>\n )\n}\n\nconst MasterDetailListItem = React.forwardRef<HTMLButtonElement, MasterDetailListItemProps>(\n function MasterDetailListItem({ active = false, children, className, onKeyDown, ...props }, ref) {\n function handleKeyDown(e: React.KeyboardEvent<HTMLButtonElement>) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n ;(e.target as HTMLButtonElement).click()\n }\n onKeyDown?.(e)\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"option\"\n aria-selected={active}\n data-active={active || undefined}\n onKeyDown={handleKeyDown}\n className={cn(\n 'flex w-full items-center px-ds-04 py-ds-03 text-left text-ds-md font-sans text-surface-fg',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-7 focus-visible:ring-inset',\n active && 'bg-accent-2 text-accent-11 font-medium',\n className,\n )}\n {...props}\n >\n {children}\n </button>\n )\n },\n)\n\n// ============================================================\n// MasterDetail Root\n// ============================================================\n\nfunction MasterDetailRoot({\n selected = null,\n onBack,\n masterWidth = '280px',\n breakpoint = 'md',\n emptyState,\n onNavigate,\n children,\n className,\n style,\n ...props\n}: MasterDetailProps) {\n const isMobile = useMediaQuery(`(max-width: ${breakpoints[breakpoint]})`)\n const [activeIndex, setActiveIndex] = React.useState(0)\n const [itemCount, setItemCount] = React.useState(0)\n const itemRefs = React.useRef<(HTMLButtonElement | null)[]>([])\n\n const registerItemRef = React.useCallback((index: number, el: HTMLButtonElement | null) => {\n itemRefs.current[index] = el\n }, [])\n\n return (\n <MasterDetailContext.Provider value={{ selected, isMobile, onBack, emptyState, onNavigate, activeIndex, setActiveIndex, registerItemRef, itemCount, setItemCount }}>\n <div\n className={cn(\n 'flex h-full',\n !isMobile && 'grid',\n className,\n )}\n style={{\n ...style,\n ...(isMobile ? {} : { gridTemplateColumns: `${masterWidth} 1fr` }),\n }}\n {...props}\n >\n {children}\n </div>\n </MasterDetailContext.Provider>\n )\n}\n\nconst MasterDetail = Object.assign(MasterDetailRoot, {\n List: MasterDetailList,\n Detail: MasterDetailDetail,\n ListItem: MasterDetailListItem,\n})\n\nexport { MasterDetail }\nexport type { MasterDetailListItemProps }\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"member-picker.js","names":[],"sources":["../../src/composed/member-picker.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport {\n Avatar,\n AvatarFallback,\n AvatarImage,\n} from '../ui/avatar'\nimport { getInitials } from './lib/string-utils'\nimport { type MultiSelectItem,MultiSelectPopover } from './multi-select-popover'\n\n// ============================================================\n// Types — backward-compatible with original MemberPicker\n// ============================================================\n\nexport interface MemberPickerMember {\n id: string\n name: string\n avatar?: string\n}\n\nexport interface MemberPickerProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onSelect'> {\n members: MemberPickerMember[]\n selectedIds: string[]\n onSelect: (memberId: string) => void\n multiple?: boolean\n placeholder?: string\n children: React.ReactNode\n}\n\n// ============================================================\n// MemberPicker — thin wrapper around MultiSelectPopover\n// ============================================================\n\nconst MemberPicker = React.forwardRef<HTMLDivElement, MemberPickerProps>(\n (\n {\n members,\n selectedIds,\n onSelect,\n children,\n multiple = false,\n placeholder = 'Search members...',\n ...props\n },\n ref,\n ) => {\n const items: MultiSelectItem[] = React.useMemo(\n () => members.map((m) => ({ id: m.id, label: m.name, image: m.avatar })),\n [members],\n )\n\n function handleChange(ids: string[]) {\n // Find the toggled id\n const added = ids.find((id) => !selectedIds.includes(id))\n const removed = selectedIds.find((id) => !ids.includes(id))\n const toggled = added ?? removed\n if (toggled) onSelect(toggled)\n }\n\n return (\n <MultiSelectPopover\n ref={ref}\n items={items}\n value={selectedIds}\n onValueChange={handleChange}\n searchPlaceholder={placeholder}\n maxSelections={multiple ? undefined : 1}\n renderItem={(item) => (\n <div className=\"flex items-center gap-ds-03\">\n <Avatar className=\"h-ico-md w-ico-md\">\n {item.image && <AvatarImage src={item.image} alt={item.label} />}\n <AvatarFallback className=\"bg-surface-raised-hover text-ds-xs font-semibold text-surface-fg\">\n {getInitials(item.label)}\n </AvatarFallback>\n </Avatar>\n <span className=\"flex-1 truncate text-ds-md font-body text-surface-fg\">\n {item.label}\n </span>\n </div>\n )}\n {...props}\n >\n {children}\n </MultiSelectPopover>\n )\n },\n)\n\nMemberPicker.displayName = 'MemberPicker'\n\nexport { MemberPicker }\n"],"mappings":";;;;;;;AAmCA,IAAM,IAAe,EAAM,YAEvB,EACE,YACA,gBACA,aACA,aACA,cAAW,IACX,iBAAc,qBACd,GAAG,KAEL,MACG;CACH,IAAM,IAA2B,EAAM,cAC/B,EAAQ,KAAK,OAAO;EAAE,IAAI,EAAE;EAAI,OAAO,EAAE;EAAM,OAAO,EAAE;
|
|
1
|
+
{"version":3,"file":"member-picker.js","names":[],"sources":["../../src/composed/member-picker.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport {\n Avatar,\n AvatarFallback,\n AvatarImage,\n} from '../ui/avatar'\nimport { getInitials } from './lib/string-utils'\nimport { type MultiSelectItem,MultiSelectPopover } from './multi-select-popover'\n\n// ============================================================\n// Types — backward-compatible with original MemberPicker\n// ============================================================\n\nexport interface MemberPickerMember {\n id: string\n name: string\n avatar?: string\n}\n\nexport interface MemberPickerProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onSelect'> {\n members: MemberPickerMember[]\n selectedIds: string[]\n onSelect: (memberId: string) => void\n multiple?: boolean\n placeholder?: string\n children: React.ReactNode\n}\n\n// ============================================================\n// MemberPicker — thin wrapper around MultiSelectPopover\n// ============================================================\n\nconst MemberPicker = React.forwardRef<HTMLDivElement, MemberPickerProps>(\n (\n {\n members,\n selectedIds,\n onSelect,\n children,\n multiple = false,\n placeholder = 'Search members...',\n ...props\n },\n ref,\n ) => {\n const items: MultiSelectItem[] = React.useMemo(\n () => members.map((m) => ({ id: m.id, label: m.name, image: m.avatar })),\n [members],\n )\n\n function handleChange(ids: string[]) {\n // Find the toggled id\n const added = ids.find((id) => !selectedIds.includes(id))\n const removed = selectedIds.find((id) => !ids.includes(id))\n const toggled = added ?? removed\n if (toggled) onSelect(toggled)\n }\n\n return (\n <MultiSelectPopover\n ref={ref}\n items={items}\n value={selectedIds}\n onValueChange={handleChange}\n searchPlaceholder={placeholder}\n maxSelections={multiple ? undefined : 1}\n renderItem={(item) => (\n <div className=\"flex items-center gap-ds-03\">\n <Avatar className=\"h-ico-md w-ico-md\">\n {item.image && <AvatarImage src={item.image} alt={item.label} />}\n <AvatarFallback className=\"bg-surface-raised-hover text-ds-xs font-semibold text-surface-fg\">\n {getInitials(item.label)}\n </AvatarFallback>\n </Avatar>\n <span className=\"flex-1 truncate text-ds-md font-body text-surface-fg\">\n {item.label}\n </span>\n </div>\n )}\n {...props}\n >\n {children}\n </MultiSelectPopover>\n )\n },\n)\n\nMemberPicker.displayName = 'MemberPicker'\n\nexport { MemberPicker }\n"],"mappings":";;;;;;;AAmCA,IAAM,IAAe,EAAM,YAEvB,EACE,YACA,gBACA,aACA,aACA,cAAW,IACX,iBAAc,qBACd,GAAG,KAEL,MACG;CACH,IAAM,IAA2B,EAAM,cAC/B,EAAQ,KAAK,OAAO;EAAE,IAAI,EAAE;EAAI,OAAO,EAAE;EAAM,OAAO,EAAE;CAAO,EAAE,GACvE,CAAC,CAAO,CACV;CAEA,SAAS,EAAa,GAAe;EAEnC,IAAM,IAAQ,EAAI,MAAM,MAAO,CAAC,EAAY,SAAS,CAAE,CAAC,GAClD,IAAU,EAAY,MAAM,MAAO,CAAC,EAAI,SAAS,CAAE,CAAC,GACpD,IAAU,KAAS;EACzB,AAAI,KAAS,EAAS,CAAO;CAC/B;CAEA,OACE,kBAAC,GAAD;EACO;EACE;EACP,OAAO;EACP,eAAe;EACf,mBAAmB;EACnB,eAAe,IAAW,KAAA,IAAY;EACtC,aAAa,MACX,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IAAQ,WAAU;cAAlB,CACG,EAAK,SAAS,kBAAC,GAAD;KAAa,KAAK,EAAK;KAAO,KAAK,EAAK;IAAQ,CAAA,GAC/D,kBAAC,GAAD;KAAgB,WAAU;eACvB,EAAY,EAAK,KAAK;IACT,CAAA,CACV;OACR,kBAAC,QAAD;IAAM,WAAU;cACb,EAAK;GACF,CAAA,CACH;;EAEP,GAAI;EAEH;CACiB,CAAA;AAExB,CACF;AAEA,EAAa,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-select-popover.js","names":[],"sources":["../../src/composed/multi-select-popover.tsx"],"sourcesContent":["'use client'\n\nimport { IconCheck, IconSearch } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '../ui/popover'\nimport { Spinner } from '../ui/spinner'\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface MultiSelectItem {\n id: string\n label: string\n image?: string\n description?: string\n disabled?: boolean\n}\n\nexport interface MultiSelectGroup {\n label: string\n items: MultiSelectItem[]\n}\n\nexport interface MultiSelectPopoverProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onSelect'> {\n /** Flat list of items (use `groups` for grouped rendering) */\n items?: MultiSelectItem[]\n /** Grouped items with section headers */\n groups?: MultiSelectGroup[]\n /** Currently selected item IDs */\n value: string[]\n /** Called when selection changes */\n onValueChange: (ids: string[]) => void\n /** @default 'Search...' */\n searchPlaceholder?: string\n /** Async search — replaces local filter with server results */\n onSearch?: (query: string) => Promise<MultiSelectItem[]>\n /** Debounce for async search in ms @default 300 */\n searchDebounce?: number\n /** Custom item renderer */\n renderItem?: (item: MultiSelectItem, selected: boolean) => React.ReactNode\n /** Message when no items match filter @default 'No results found' */\n emptyMessage?: string\n /** Maximum number of selections */\n maxSelections?: number\n /** Popover alignment @default 'start' */\n align?: 'start' | 'center' | 'end'\n /** Popover width */\n width?: string | number\n /** Trigger element */\n children: React.ReactNode\n}\n\n// ============================================================\n// MultiSelectPopover\n// ============================================================\n\nconst MultiSelectPopover = React.forwardRef<HTMLDivElement, MultiSelectPopoverProps>(\n (\n {\n items,\n groups,\n value,\n onValueChange,\n searchPlaceholder = 'Search...',\n onSearch,\n searchDebounce = 300,\n renderItem,\n emptyMessage = 'No results found',\n maxSelections,\n align = 'start',\n width,\n children,\n className,\n ...props\n },\n ref,\n ) => {\n const [open, setOpen] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [asyncItems, setAsyncItems] = React.useState<MultiSelectItem[] | null>(null)\n const [loading, setLoading] = React.useState(false)\n const [focusedIndex, setFocusedIndex] = React.useState(-1)\n const debounceRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const listRef = React.useRef<HTMLDivElement>(null)\n\n // Reset search and focus when popover closes\n React.useEffect(() => {\n if (!open) {\n setSearch('')\n setAsyncItems(null)\n setFocusedIndex(-1)\n }\n }, [open])\n\n // Async search\n React.useEffect(() => {\n if (!onSearch || !open) return\n if (!search.trim()) {\n setAsyncItems(null)\n setLoading(false)\n return\n }\n setLoading(true)\n clearTimeout(debounceRef.current)\n debounceRef.current = setTimeout(() => {\n onSearch(search)\n .then((results) => setAsyncItems(results))\n .catch(() => setAsyncItems([]))\n .finally(() => setLoading(false))\n }, searchDebounce)\n return () => clearTimeout(debounceRef.current)\n }, [search, onSearch, searchDebounce, open])\n\n // Resolve visible items\n const allItems = React.useMemo(() => {\n if (asyncItems) return asyncItems\n if (groups) return groups.flatMap((g) => g.items)\n return items ?? []\n }, [items, groups, asyncItems])\n\n const filteredItems = React.useMemo(() => {\n if (onSearch) return allItems // async search handles its own filtering\n const q = search.toLowerCase()\n if (!q) return allItems\n return allItems.filter((item) => item.label.toLowerCase().includes(q))\n }, [allItems, search, onSearch])\n\n const filteredGroups = React.useMemo(() => {\n if (!groups || onSearch || asyncItems) return null\n const q = search.toLowerCase()\n return groups\n .map((g) => ({\n ...g,\n items: q ? g.items.filter((item) => item.label.toLowerCase().includes(q)) : g.items,\n }))\n .filter((g) => g.items.length > 0)\n }, [groups, search, onSearch, asyncItems])\n\n function toggle(id: string) {\n const isSelected = value.includes(id)\n if (isSelected) {\n onValueChange(value.filter((v) => v !== id))\n } else {\n if (maxSelections && value.length >= maxSelections) {\n // Replace: drop the first selected item to make room for the new one\n onValueChange([...value.slice(1), id])\n } else {\n onValueChange([...value, id])\n }\n }\n }\n\n // Reset focused index when search text changes\n \n React.useEffect(() => { setFocusedIndex(-1) }, [search])\n\n // Scroll focused item into view\n React.useEffect(() => {\n if (focusedIndex < 0) return\n const list = listRef.current\n if (!list) return\n const items = list.querySelectorAll('[data-multiselect-item]')\n items[focusedIndex]?.scrollIntoView({ block: 'nearest' })\n }, [focusedIndex])\n\n function handleSearchKeyDown(e: React.KeyboardEvent) {\n const count = filteredItems.length\n if (count === 0) return\n\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setFocusedIndex((prev) => (prev < count - 1 ? prev + 1 : 0))\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setFocusedIndex((prev) => (prev <= 0 ? count - 1 : prev - 1))\n } else if (e.key === 'Enter' && focusedIndex >= 0) {\n e.preventDefault()\n const item = filteredItems[focusedIndex]\n if (item && !item.disabled) toggle(item.id)\n }\n }\n\n let itemCounter = 0\n\n function renderItemRow(item: MultiSelectItem) {\n const isSelected = value.includes(item.id)\n const index = itemCounter++\n const isFocused = index === focusedIndex\n return (\n <motion.button\n key={item.id}\n id={`msp-item-${index}`}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n disabled={item.disabled}\n data-multiselect-item=\"\"\n data-focused={isFocused ? '' : undefined}\n initial={{ opacity: 0, x: -8 }}\n animate={{ opacity: 1, x: 0 }}\n transition={{ ...springs.snappy, delay: index * 0.02 }}\n onClick={() => toggle(item.id)}\n className={cn(\n 'flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'disabled:opacity-action-disabled disabled:cursor-not-allowed',\n isSelected && 'bg-accent-2 text-accent-11',\n isFocused && 'bg-surface-raised-hover',\n )}\n >\n {renderItem ? (\n <span className=\"flex-1 min-w-0\">{renderItem(item, isSelected)}</span>\n ) : (\n <>\n {item.image && (\n <img\n src={item.image}\n alt=\"\"\n className=\"h-ico-md w-ico-md rounded-pill object-cover shrink-0\"\n />\n )}\n <span className=\"flex-1 min-w-0\">\n <span className=\"block truncate text-ds-md font-body text-surface-fg\">{item.label}</span>\n {item.description && (\n <span className=\"block truncate text-ds-xs text-surface-fg-subtle\">{item.description}</span>\n )}\n </span>\n </>\n )}\n {isSelected && (\n <motion.span\n initial={{ scale: 0 }}\n animate={{ scale: 1 }}\n transition={springs.snappy}\n className=\"inline-flex shrink-0\"\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"text-accent-11\" />\n </motion.span>\n )}\n </motion.button>\n )\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen} modal={false}>\n <PopoverTrigger asChild>{children}</PopoverTrigger>\n <PopoverContent\n ref={ref}\n {...props}\n className={cn(\n 'border-surface-border-strong bg-surface-overlay p-0',\n className,\n )}\n style={{ width: width ?? 240 }}\n align={align}\n sideOffset={4}\n >\n {/* Search */}\n <div className=\"flex items-center gap-ds-03 border-b border-surface-border-strong px-ds-04 py-ds-03\">\n <Icon icon={IconSearch} size=\"sm\" stroke=\"light\" className=\"shrink-0 text-surface-fg-subtle\" />\n <input\n type=\"text\"\n placeholder={searchPlaceholder}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onKeyDown={handleSearchKeyDown}\n aria-label=\"Search\"\n aria-activedescendant={focusedIndex >= 0 ? `msp-item-${focusedIndex}` : undefined}\n className=\"w-full bg-transparent text-ds-md font-body text-surface-fg placeholder:text-surface-fg-subtle outline-hidden\"\n />\n {loading && <Spinner size=\"sm\" />}\n </div>\n\n {/* Items */}\n <div ref={listRef} role=\"listbox\" aria-multiselectable=\"true\" className=\"max-h-[240px] overflow-y-auto py-ds-02\">\n {filteredGroups\n ? filteredGroups.map((group) => (\n <div key={group.label}>\n <div className=\"px-ds-04 py-ds-02 text-ds-xs font-semibold uppercase tracking-wider text-surface-fg-subtle\">\n {group.label}\n </div>\n {group.items.map(renderItemRow)}\n </div>\n ))\n : filteredItems.map(renderItemRow)}\n {!loading && filteredItems.length === 0 && (\n <p className=\"px-ds-04 py-ds-05 text-center text-ds-sm font-body text-surface-fg-subtle\">\n {emptyMessage}\n </p>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )\n },\n)\nMultiSelectPopover.displayName = 'MultiSelectPopover'\n\nexport { MultiSelectPopover }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"multi-select-popover.js","names":[],"sources":["../../src/composed/multi-select-popover.tsx"],"sourcesContent":["'use client'\n\nimport { IconCheck, IconSearch } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '../ui/popover'\nimport { Spinner } from '../ui/spinner'\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface MultiSelectItem {\n id: string\n label: string\n image?: string\n description?: string\n disabled?: boolean\n}\n\nexport interface MultiSelectGroup {\n label: string\n items: MultiSelectItem[]\n}\n\nexport interface MultiSelectPopoverProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onSelect'> {\n /** Flat list of items (use `groups` for grouped rendering) */\n items?: MultiSelectItem[]\n /** Grouped items with section headers */\n groups?: MultiSelectGroup[]\n /** Currently selected item IDs */\n value: string[]\n /** Called when selection changes */\n onValueChange: (ids: string[]) => void\n /** @default 'Search...' */\n searchPlaceholder?: string\n /** Async search — replaces local filter with server results */\n onSearch?: (query: string) => Promise<MultiSelectItem[]>\n /** Debounce for async search in ms @default 300 */\n searchDebounce?: number\n /** Custom item renderer */\n renderItem?: (item: MultiSelectItem, selected: boolean) => React.ReactNode\n /** Message when no items match filter @default 'No results found' */\n emptyMessage?: string\n /** Maximum number of selections */\n maxSelections?: number\n /** Popover alignment @default 'start' */\n align?: 'start' | 'center' | 'end'\n /** Popover width */\n width?: string | number\n /** Trigger element */\n children: React.ReactNode\n}\n\n// ============================================================\n// MultiSelectPopover\n// ============================================================\n\nconst MultiSelectPopover = React.forwardRef<HTMLDivElement, MultiSelectPopoverProps>(\n (\n {\n items,\n groups,\n value,\n onValueChange,\n searchPlaceholder = 'Search...',\n onSearch,\n searchDebounce = 300,\n renderItem,\n emptyMessage = 'No results found',\n maxSelections,\n align = 'start',\n width,\n children,\n className,\n ...props\n },\n ref,\n ) => {\n const [open, setOpen] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [asyncItems, setAsyncItems] = React.useState<MultiSelectItem[] | null>(null)\n const [loading, setLoading] = React.useState(false)\n const [focusedIndex, setFocusedIndex] = React.useState(-1)\n const debounceRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const listRef = React.useRef<HTMLDivElement>(null)\n\n // Reset search and focus when popover closes\n React.useEffect(() => {\n if (!open) {\n setSearch('')\n setAsyncItems(null)\n setFocusedIndex(-1)\n }\n }, [open])\n\n // Async search\n React.useEffect(() => {\n if (!onSearch || !open) return\n if (!search.trim()) {\n setAsyncItems(null)\n setLoading(false)\n return\n }\n setLoading(true)\n clearTimeout(debounceRef.current)\n debounceRef.current = setTimeout(() => {\n onSearch(search)\n .then((results) => setAsyncItems(results))\n .catch(() => setAsyncItems([]))\n .finally(() => setLoading(false))\n }, searchDebounce)\n return () => clearTimeout(debounceRef.current)\n }, [search, onSearch, searchDebounce, open])\n\n // Resolve visible items\n const allItems = React.useMemo(() => {\n if (asyncItems) return asyncItems\n if (groups) return groups.flatMap((g) => g.items)\n return items ?? []\n }, [items, groups, asyncItems])\n\n const filteredItems = React.useMemo(() => {\n if (onSearch) return allItems // async search handles its own filtering\n const q = search.toLowerCase()\n if (!q) return allItems\n return allItems.filter((item) => item.label.toLowerCase().includes(q))\n }, [allItems, search, onSearch])\n\n const filteredGroups = React.useMemo(() => {\n if (!groups || onSearch || asyncItems) return null\n const q = search.toLowerCase()\n return groups\n .map((g) => ({\n ...g,\n items: q ? g.items.filter((item) => item.label.toLowerCase().includes(q)) : g.items,\n }))\n .filter((g) => g.items.length > 0)\n }, [groups, search, onSearch, asyncItems])\n\n function toggle(id: string) {\n const isSelected = value.includes(id)\n if (isSelected) {\n onValueChange(value.filter((v) => v !== id))\n } else {\n if (maxSelections && value.length >= maxSelections) {\n // Replace: drop the first selected item to make room for the new one\n onValueChange([...value.slice(1), id])\n } else {\n onValueChange([...value, id])\n }\n }\n }\n\n // Reset focused index when search text changes\n \n React.useEffect(() => { setFocusedIndex(-1) }, [search])\n\n // Scroll focused item into view\n React.useEffect(() => {\n if (focusedIndex < 0) return\n const list = listRef.current\n if (!list) return\n const items = list.querySelectorAll('[data-multiselect-item]')\n items[focusedIndex]?.scrollIntoView({ block: 'nearest' })\n }, [focusedIndex])\n\n function handleSearchKeyDown(e: React.KeyboardEvent) {\n const count = filteredItems.length\n if (count === 0) return\n\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setFocusedIndex((prev) => (prev < count - 1 ? prev + 1 : 0))\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setFocusedIndex((prev) => (prev <= 0 ? count - 1 : prev - 1))\n } else if (e.key === 'Enter' && focusedIndex >= 0) {\n e.preventDefault()\n const item = filteredItems[focusedIndex]\n if (item && !item.disabled) toggle(item.id)\n }\n }\n\n let itemCounter = 0\n\n function renderItemRow(item: MultiSelectItem) {\n const isSelected = value.includes(item.id)\n const index = itemCounter++\n const isFocused = index === focusedIndex\n return (\n <motion.button\n key={item.id}\n id={`msp-item-${index}`}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n disabled={item.disabled}\n data-multiselect-item=\"\"\n data-focused={isFocused ? '' : undefined}\n initial={{ opacity: 0, x: -8 }}\n animate={{ opacity: 1, x: 0 }}\n transition={{ ...springs.snappy, delay: index * 0.02 }}\n onClick={() => toggle(item.id)}\n className={cn(\n 'flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'disabled:opacity-action-disabled disabled:cursor-not-allowed',\n isSelected && 'bg-accent-2 text-accent-11',\n isFocused && 'bg-surface-raised-hover',\n )}\n >\n {renderItem ? (\n <span className=\"flex-1 min-w-0\">{renderItem(item, isSelected)}</span>\n ) : (\n <>\n {item.image && (\n <img\n src={item.image}\n alt=\"\"\n className=\"h-ico-md w-ico-md rounded-pill object-cover shrink-0\"\n />\n )}\n <span className=\"flex-1 min-w-0\">\n <span className=\"block truncate text-ds-md font-body text-surface-fg\">{item.label}</span>\n {item.description && (\n <span className=\"block truncate text-ds-xs text-surface-fg-subtle\">{item.description}</span>\n )}\n </span>\n </>\n )}\n {isSelected && (\n <motion.span\n initial={{ scale: 0 }}\n animate={{ scale: 1 }}\n transition={springs.snappy}\n className=\"inline-flex shrink-0\"\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"text-accent-11\" />\n </motion.span>\n )}\n </motion.button>\n )\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen} modal={false}>\n <PopoverTrigger asChild>{children}</PopoverTrigger>\n <PopoverContent\n ref={ref}\n {...props}\n className={cn(\n 'border-surface-border-strong bg-surface-overlay p-0',\n className,\n )}\n style={{ width: width ?? 240 }}\n align={align}\n sideOffset={4}\n >\n {/* Search */}\n <div className=\"flex items-center gap-ds-03 border-b border-surface-border-strong px-ds-04 py-ds-03\">\n <Icon icon={IconSearch} size=\"sm\" stroke=\"light\" className=\"shrink-0 text-surface-fg-subtle\" />\n <input\n type=\"text\"\n placeholder={searchPlaceholder}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onKeyDown={handleSearchKeyDown}\n aria-label=\"Search\"\n aria-activedescendant={focusedIndex >= 0 ? `msp-item-${focusedIndex}` : undefined}\n className=\"w-full bg-transparent text-ds-md font-body text-surface-fg placeholder:text-surface-fg-subtle outline-hidden\"\n />\n {loading && <Spinner size=\"sm\" />}\n </div>\n\n {/* Items */}\n <div ref={listRef} role=\"listbox\" aria-multiselectable=\"true\" className=\"max-h-[240px] overflow-y-auto py-ds-02\">\n {filteredGroups\n ? filteredGroups.map((group) => (\n <div key={group.label}>\n <div className=\"px-ds-04 py-ds-02 text-ds-xs font-semibold uppercase tracking-wider text-surface-fg-subtle\">\n {group.label}\n </div>\n {group.items.map(renderItemRow)}\n </div>\n ))\n : filteredItems.map(renderItemRow)}\n {!loading && filteredItems.length === 0 && (\n <p className=\"px-ds-04 py-ds-05 text-center text-ds-sm font-body text-surface-fg-subtle\">\n {emptyMessage}\n </p>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )\n },\n)\nMultiSelectPopover.displayName = 'MultiSelectPopover'\n\nexport { MultiSelectPopover }\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-header.js","names":[],"sources":["../../src/composed/page-header.tsx"],"sourcesContent":["// @server-safe\nimport * as React from 'react'\n\nimport {\n Breadcrumb as BreadcrumbNav,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '../ui/breadcrumb'\nimport { cn } from '../ui/lib/utils'\n\nexport interface Breadcrumb {\n label: string\n href?: string\n}\n\nexport interface PageHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n title?: string\n subtitle?: string\n actions?: React.ReactNode\n breadcrumbs?: Breadcrumb[]\n titleClassName?: string\n}\n\nconst PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(\n (\n { title, subtitle, actions, breadcrumbs, titleClassName, className, ...props },\n ref,\n ) => {\n const resolvedTitle =\n title ?? breadcrumbs?.[breadcrumbs.length - 1]?.label ?? ''\n\n return (\n <div\n ref={ref}\n className={cn(\n 'flex flex-col gap-ds-05 border-b border-surface-border-strong pb-ds-06',\n className,\n )}\n {...props}\n >\n {breadcrumbs && breadcrumbs.length > 0 && (\n <BreadcrumbNav className=\"text-ds-sm\">\n <BreadcrumbList>\n {breadcrumbs.map((crumb, index) => {\n const isLast = index === breadcrumbs.length - 1\n return (\n <React.Fragment key={index}>\n {index > 0 && <BreadcrumbSeparator />}\n <BreadcrumbItem>\n {crumb.href && !isLast ? (\n <BreadcrumbLink href={crumb.href}>{crumb.label}</BreadcrumbLink>\n ) : (\n <BreadcrumbPage>{crumb.label}</BreadcrumbPage>\n )}\n </BreadcrumbItem>\n </React.Fragment>\n )\n })}\n </BreadcrumbList>\n </BreadcrumbNav>\n )}\n\n {(resolvedTitle || subtitle || actions) && (\n <div className=\"flex flex-wrap items-start justify-between gap-ds-05\">\n <div className=\"flex min-w-0 flex-col gap-ds-02b\">\n {resolvedTitle && (\n <h1\n className={cn(\n 'text-ds-2xl font-semibold text-surface-fg',\n titleClassName,\n )}\n >\n {resolvedTitle}\n </h1>\n )}\n {subtitle && (\n <p className=\"text-ds-md text-surface-fg-subtle line-clamp-2\">\n {subtitle}\n </p>\n )}\n </div>\n\n {actions && (\n <div className=\"flex flex-wrap items-center gap-ds-03\">{actions}</div>\n )}\n </div>\n )}\n </div>\n )\n },\n)\nPageHeader.displayName = 'PageHeader'\n\nexport { PageHeader }\n"],"mappings":";;;;;AA0BA,IAAM,IAAa,EAAM,YAErB,EAAE,UAAO,aAAU,YAAS,gBAAa,mBAAgB,cAAW,GAAG,KACvE,MACG;CACH,IAAM,IACJ,KAAS,IAAc,EAAY,SAAS,
|
|
1
|
+
{"version":3,"file":"page-header.js","names":[],"sources":["../../src/composed/page-header.tsx"],"sourcesContent":["// @server-safe\nimport * as React from 'react'\n\nimport {\n Breadcrumb as BreadcrumbNav,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '../ui/breadcrumb'\nimport { cn } from '../ui/lib/utils'\n\nexport interface Breadcrumb {\n label: string\n href?: string\n}\n\nexport interface PageHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n title?: string\n subtitle?: string\n actions?: React.ReactNode\n breadcrumbs?: Breadcrumb[]\n titleClassName?: string\n}\n\nconst PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(\n (\n { title, subtitle, actions, breadcrumbs, titleClassName, className, ...props },\n ref,\n ) => {\n const resolvedTitle =\n title ?? breadcrumbs?.[breadcrumbs.length - 1]?.label ?? ''\n\n return (\n <div\n ref={ref}\n className={cn(\n 'flex flex-col gap-ds-05 border-b border-surface-border-strong pb-ds-06',\n className,\n )}\n {...props}\n >\n {breadcrumbs && breadcrumbs.length > 0 && (\n <BreadcrumbNav className=\"text-ds-sm\">\n <BreadcrumbList>\n {breadcrumbs.map((crumb, index) => {\n const isLast = index === breadcrumbs.length - 1\n return (\n <React.Fragment key={index}>\n {index > 0 && <BreadcrumbSeparator />}\n <BreadcrumbItem>\n {crumb.href && !isLast ? (\n <BreadcrumbLink href={crumb.href}>{crumb.label}</BreadcrumbLink>\n ) : (\n <BreadcrumbPage>{crumb.label}</BreadcrumbPage>\n )}\n </BreadcrumbItem>\n </React.Fragment>\n )\n })}\n </BreadcrumbList>\n </BreadcrumbNav>\n )}\n\n {(resolvedTitle || subtitle || actions) && (\n <div className=\"flex flex-wrap items-start justify-between gap-ds-05\">\n <div className=\"flex min-w-0 flex-col gap-ds-02b\">\n {resolvedTitle && (\n <h1\n className={cn(\n 'text-ds-2xl font-semibold text-surface-fg',\n titleClassName,\n )}\n >\n {resolvedTitle}\n </h1>\n )}\n {subtitle && (\n <p className=\"text-ds-md text-surface-fg-subtle line-clamp-2\">\n {subtitle}\n </p>\n )}\n </div>\n\n {actions && (\n <div className=\"flex flex-wrap items-center gap-ds-03\">{actions}</div>\n )}\n </div>\n )}\n </div>\n )\n },\n)\nPageHeader.displayName = 'PageHeader'\n\nexport { PageHeader }\n"],"mappings":";;;;;AA0BA,IAAM,IAAa,EAAM,YAErB,EAAE,UAAO,aAAU,YAAS,gBAAa,mBAAgB,cAAW,GAAG,KACvE,MACG;CACH,IAAM,IACJ,KAAS,IAAc,EAAY,SAAS,EAAE,EAAE,SAAS;CAE3D,OACE,kBAAC,OAAD;EACO;EACL,WAAW,EACT,0EACA,CACF;EACA,GAAI;YANN,CAQG,KAAe,EAAY,SAAS,KACnC,kBAAC,GAAD;GAAe,WAAU;aACvB,kBAAC,GAAD,EAAA,UACG,EAAY,KAAK,GAAO,MAAU;IACjC,IAAM,IAAS,MAAU,EAAY,SAAS;IAC9C,OACE,kBAAC,EAAM,UAAP,EAAA,UAAA,CACG,IAAQ,KAAK,kBAAC,GAAD,CAAsB,CAAA,GACpC,kBAAC,GAAD,EAAA,UACG,EAAM,QAAQ,CAAC,IACd,kBAAC,GAAD;KAAgB,MAAM,EAAM;eAAO,EAAM;IAAsB,CAAA,IAE/D,kBAAC,GAAD,EAAA,UAAiB,EAAM,MAAsB,CAAA,EAEjC,CAAA,CACF,EAAA,GATK,CASL;GAEpB,CAAC,EACa,CAAA;EACH,CAAA,IAGf,KAAiB,KAAY,MAC7B,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,KACC,kBAAC,MAAD;KACE,WAAW,EACT,6CACA,CACF;eAEC;IACC,CAAA,GAEL,KACC,kBAAC,KAAD;KAAG,WAAU;eACV;IACA,CAAA,CAEF;OAEJ,KACC,kBAAC,OAAD;IAAK,WAAU;cAAyC;GAAa,CAAA,CAEpE;IAEJ;;AAET,CACF;AACA,EAAW,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-skeletons.js","names":[],"sources":["../../src/composed/page-skeletons.tsx"],"sourcesContent":["// @server-safe\nimport * as React from 'react'\n\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\n\nconst shimmer = 'bg-surface-raised-hover'\n\n// --- Dashboard Skeleton ---\n\nexport interface DashboardSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst DashboardSkeleton = React.forwardRef<HTMLDivElement, DashboardSkeletonProps>(\n function DashboardSkeleton({ className, ...props }, ref) {\n return (\n <div ref={ref} {...props} className={cn(\"flex flex-col gap-ds-06\", className)}>\n {/* Stat cards grid */}\n <div className=\"grid grid-cols-1 gap-ds-05 sm:grid-cols-2 lg:grid-cols-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={`stat-${i}`}\n className=\"flex flex-col gap-ds-04 rounded-overlay-lg border border-surface-border-strong bg-surface-raised p-ds-05b\"\n >\n <div className=\"flex items-center justify-between\">\n <Skeleton className={cn('h-ds-04 w-ds-11', shimmer)} />\n <Skeleton className={cn('h-ds-sm w-ds-sm rounded-surface', shimmer)} />\n </div>\n <Skeleton className={cn('h-ds-xs-plus w-ds-10', shimmer)} />\n <Skeleton className={cn('h-ds-03 w-[112px]', shimmer)} />\n </div>\n ))}\n </div>\n\n {/* Attendance calendar placeholder */}\n <div className=\"rounded-overlay-lg border border-surface-border-strong bg-surface-raised p-ds-06\">\n <div className=\"mb-ds-06 flex items-center justify-between\">\n <Skeleton className={cn('h-ds-05b w-[128px]', shimmer)} />\n <div className=\"flex items-center gap-ds-03\">\n <Skeleton className={cn('h-ds-sm w-ds-sm rounded-surface', shimmer)} />\n <Skeleton className={cn('h-ds-05 w-[112px]', shimmer)} />\n <Skeleton className={cn('h-ds-sm w-ds-sm rounded-surface', shimmer)} />\n </div>\n </div>\n {/* Weekday headers */}\n <div className=\"mb-ds-03 grid grid-cols-7 gap-ds-03\">\n {Array.from({ length: 7 }).map((_, i) => (\n <Skeleton key={`wh-${i}`} className={cn('mx-auto h-ds-04 w-ds-07', shimmer)} />\n ))}\n </div>\n {/* Calendar grid */}\n <div className=\"grid grid-cols-7 gap-ds-03\">\n {Array.from({ length: 35 }).map((_, i) => (\n <Skeleton\n key={`cal-${i}`}\n className={cn('mx-auto h-ds-md w-ds-md rounded-pill', shimmer)}\n />\n ))}\n </div>\n </div>\n </div>\n )\n},\n)\n\nDashboardSkeleton.displayName = 'DashboardSkeleton'\n\n// --- Project List Skeleton ---\n\nexport interface ProjectListSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst ProjectListSkeleton = React.forwardRef<HTMLDivElement, ProjectListSkeletonProps>(\n function ProjectListSkeleton({ className, ...props }, ref) {\n return (\n <div ref={ref} {...props} className={cn(\"flex flex-col gap-ds-06\", className)}>\n {/* Header */}\n <div className=\"flex flex-col gap-ds-05 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-col gap-ds-03\">\n <Skeleton className={cn('h-ds-06 w-[112px]', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-ds-13', shimmer)} />\n </div>\n <Skeleton className={cn('h-ds-sm-plus w-[128px] rounded-surface', shimmer)} />\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col gap-ds-04 sm:flex-row sm:items-center\">\n <Skeleton className={cn('h-ds-sm-plus flex-1 rounded-surface', shimmer)} />\n <div className=\"flex gap-ds-02b\">\n {Array.from({ length: 4 }).map((_, i) => (\n <Skeleton key={`f-${i}`} className={cn('h-ds-sm w-ds-10 rounded-surface', shimmer)} />\n ))}\n </div>\n </div>\n\n {/* Project cards grid */}\n <div className=\"grid grid-cols-1 gap-ds-05 sm:grid-cols-2 lg:grid-cols-3\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div\n key={`proj-${i}`}\n className=\"flex flex-col gap-ds-05 rounded-overlay-lg border border-surface-border-strong bg-surface-raised p-ds-05b\"\n >\n {/* Top row: status + org */}\n <div className=\"flex items-center justify-between\">\n <Skeleton className={cn('h-ds-05b w-ds-10 rounded-pill', shimmer)} />\n <Skeleton className={cn('h-ds-05 w-ds-11 rounded-control', shimmer)} />\n </div>\n {/* Title + description */}\n <div className=\"flex flex-col gap-ds-03\">\n <Skeleton className={cn('h-ds-05 w-3/4', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-full', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-2/3', shimmer)} />\n </div>\n {/* Bottom row: members + count */}\n <div className=\"flex items-center justify-between pt-ds-02\">\n <div className=\"flex -space-x-ds-03\">\n {Array.from({ length: 3 }).map((_, j) => (\n <Skeleton\n key={`av-${i}-${j}`}\n className={cn('h-ds-xs-plus w-ds-xs-plus rounded-pill', shimmer)}\n />\n ))}\n </div>\n <Skeleton className={cn('h-ds-04 w-ds-md', shimmer)} />\n </div>\n </div>\n ))}\n </div>\n </div>\n )\n},\n)\n\nProjectListSkeleton.displayName = 'ProjectListSkeleton'\n\n// --- Task Detail Skeleton ---\n\nexport interface TaskDetailSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst TaskDetailSkeleton = React.forwardRef<HTMLDivElement, TaskDetailSkeletonProps>(\n function TaskDetailSkeleton({ className, ...props }, ref) {\n return (\n <div ref={ref} {...props} className={cn(\"flex h-full flex-col gap-0 rounded-overlay-lg border border-surface-border-strong bg-surface-raised\", className)}>\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-surface-border-strong px-ds-05b py-ds-05\">\n <Skeleton className={cn('h-ds-05b w-[192px]', shimmer)} />\n <div className=\"flex items-center gap-ds-03\">\n <Skeleton className={cn('h-ds-xs-plus w-ds-xs-plus rounded-surface', shimmer)} />\n <Skeleton className={cn('h-ds-xs-plus w-ds-xs-plus rounded-surface', shimmer)} />\n </div>\n </div>\n\n {/* Property rows */}\n <div className=\"flex flex-col gap-0 border-b border-surface-border-strong px-ds-05b py-ds-05\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div\n key={`prop-${i}`}\n className=\"flex items-center gap-ds-05 py-ds-03\"\n >\n <Skeleton className={cn('h-ds-04 w-ds-12 shrink-0', shimmer)} />\n <Skeleton className={cn('h-ds-06 w-[128px] rounded-control', shimmer)} />\n </div>\n ))}\n </div>\n\n {/* Tabs */}\n <div className=\"border-b border-surface-border-strong px-ds-05b\">\n <div className=\"flex gap-ds-05 py-ds-04\">\n {Array.from({ length: 3 }).map((_, i) => (\n <Skeleton key={`tab-${i}`} className={cn('h-ds-05 w-ds-10', shimmer)} />\n ))}\n </div>\n </div>\n\n {/* Tab content */}\n <div className=\"flex flex-1 flex-col gap-ds-04 px-ds-05b py-ds-05\">\n <Skeleton className={cn('h-ds-04 w-full', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-4/5', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-3/5', shimmer)} />\n <Skeleton className={cn('mt-ds-03 h-ds-04 w-full', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-2/3', shimmer)} />\n </div>\n </div>\n )\n},\n)\n\nTaskDetailSkeleton.displayName = 'TaskDetailSkeleton'\n\nexport {\n DashboardSkeleton,\n ProjectListSkeleton,\n TaskDetailSkeleton,\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"page-skeletons.js","names":[],"sources":["../../src/composed/page-skeletons.tsx"],"sourcesContent":["// @server-safe\nimport * as React from 'react'\n\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\n\nconst shimmer = 'bg-surface-raised-hover'\n\n// --- Dashboard Skeleton ---\n\nexport interface DashboardSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst DashboardSkeleton = React.forwardRef<HTMLDivElement, DashboardSkeletonProps>(\n function DashboardSkeleton({ className, ...props }, ref) {\n return (\n <div ref={ref} {...props} className={cn(\"flex flex-col gap-ds-06\", className)}>\n {/* Stat cards grid */}\n <div className=\"grid grid-cols-1 gap-ds-05 sm:grid-cols-2 lg:grid-cols-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={`stat-${i}`}\n className=\"flex flex-col gap-ds-04 rounded-overlay-lg border border-surface-border-strong bg-surface-raised p-ds-05b\"\n >\n <div className=\"flex items-center justify-between\">\n <Skeleton className={cn('h-ds-04 w-ds-11', shimmer)} />\n <Skeleton className={cn('h-ds-sm w-ds-sm rounded-surface', shimmer)} />\n </div>\n <Skeleton className={cn('h-ds-xs-plus w-ds-10', shimmer)} />\n <Skeleton className={cn('h-ds-03 w-[112px]', shimmer)} />\n </div>\n ))}\n </div>\n\n {/* Attendance calendar placeholder */}\n <div className=\"rounded-overlay-lg border border-surface-border-strong bg-surface-raised p-ds-06\">\n <div className=\"mb-ds-06 flex items-center justify-between\">\n <Skeleton className={cn('h-ds-05b w-[128px]', shimmer)} />\n <div className=\"flex items-center gap-ds-03\">\n <Skeleton className={cn('h-ds-sm w-ds-sm rounded-surface', shimmer)} />\n <Skeleton className={cn('h-ds-05 w-[112px]', shimmer)} />\n <Skeleton className={cn('h-ds-sm w-ds-sm rounded-surface', shimmer)} />\n </div>\n </div>\n {/* Weekday headers */}\n <div className=\"mb-ds-03 grid grid-cols-7 gap-ds-03\">\n {Array.from({ length: 7 }).map((_, i) => (\n <Skeleton key={`wh-${i}`} className={cn('mx-auto h-ds-04 w-ds-07', shimmer)} />\n ))}\n </div>\n {/* Calendar grid */}\n <div className=\"grid grid-cols-7 gap-ds-03\">\n {Array.from({ length: 35 }).map((_, i) => (\n <Skeleton\n key={`cal-${i}`}\n className={cn('mx-auto h-ds-md w-ds-md rounded-pill', shimmer)}\n />\n ))}\n </div>\n </div>\n </div>\n )\n},\n)\n\nDashboardSkeleton.displayName = 'DashboardSkeleton'\n\n// --- Project List Skeleton ---\n\nexport interface ProjectListSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst ProjectListSkeleton = React.forwardRef<HTMLDivElement, ProjectListSkeletonProps>(\n function ProjectListSkeleton({ className, ...props }, ref) {\n return (\n <div ref={ref} {...props} className={cn(\"flex flex-col gap-ds-06\", className)}>\n {/* Header */}\n <div className=\"flex flex-col gap-ds-05 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-col gap-ds-03\">\n <Skeleton className={cn('h-ds-06 w-[112px]', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-ds-13', shimmer)} />\n </div>\n <Skeleton className={cn('h-ds-sm-plus w-[128px] rounded-surface', shimmer)} />\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col gap-ds-04 sm:flex-row sm:items-center\">\n <Skeleton className={cn('h-ds-sm-plus flex-1 rounded-surface', shimmer)} />\n <div className=\"flex gap-ds-02b\">\n {Array.from({ length: 4 }).map((_, i) => (\n <Skeleton key={`f-${i}`} className={cn('h-ds-sm w-ds-10 rounded-surface', shimmer)} />\n ))}\n </div>\n </div>\n\n {/* Project cards grid */}\n <div className=\"grid grid-cols-1 gap-ds-05 sm:grid-cols-2 lg:grid-cols-3\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div\n key={`proj-${i}`}\n className=\"flex flex-col gap-ds-05 rounded-overlay-lg border border-surface-border-strong bg-surface-raised p-ds-05b\"\n >\n {/* Top row: status + org */}\n <div className=\"flex items-center justify-between\">\n <Skeleton className={cn('h-ds-05b w-ds-10 rounded-pill', shimmer)} />\n <Skeleton className={cn('h-ds-05 w-ds-11 rounded-control', shimmer)} />\n </div>\n {/* Title + description */}\n <div className=\"flex flex-col gap-ds-03\">\n <Skeleton className={cn('h-ds-05 w-3/4', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-full', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-2/3', shimmer)} />\n </div>\n {/* Bottom row: members + count */}\n <div className=\"flex items-center justify-between pt-ds-02\">\n <div className=\"flex -space-x-ds-03\">\n {Array.from({ length: 3 }).map((_, j) => (\n <Skeleton\n key={`av-${i}-${j}`}\n className={cn('h-ds-xs-plus w-ds-xs-plus rounded-pill', shimmer)}\n />\n ))}\n </div>\n <Skeleton className={cn('h-ds-04 w-ds-md', shimmer)} />\n </div>\n </div>\n ))}\n </div>\n </div>\n )\n},\n)\n\nProjectListSkeleton.displayName = 'ProjectListSkeleton'\n\n// --- Task Detail Skeleton ---\n\nexport interface TaskDetailSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst TaskDetailSkeleton = React.forwardRef<HTMLDivElement, TaskDetailSkeletonProps>(\n function TaskDetailSkeleton({ className, ...props }, ref) {\n return (\n <div ref={ref} {...props} className={cn(\"flex h-full flex-col gap-0 rounded-overlay-lg border border-surface-border-strong bg-surface-raised\", className)}>\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-surface-border-strong px-ds-05b py-ds-05\">\n <Skeleton className={cn('h-ds-05b w-[192px]', shimmer)} />\n <div className=\"flex items-center gap-ds-03\">\n <Skeleton className={cn('h-ds-xs-plus w-ds-xs-plus rounded-surface', shimmer)} />\n <Skeleton className={cn('h-ds-xs-plus w-ds-xs-plus rounded-surface', shimmer)} />\n </div>\n </div>\n\n {/* Property rows */}\n <div className=\"flex flex-col gap-0 border-b border-surface-border-strong px-ds-05b py-ds-05\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div\n key={`prop-${i}`}\n className=\"flex items-center gap-ds-05 py-ds-03\"\n >\n <Skeleton className={cn('h-ds-04 w-ds-12 shrink-0', shimmer)} />\n <Skeleton className={cn('h-ds-06 w-[128px] rounded-control', shimmer)} />\n </div>\n ))}\n </div>\n\n {/* Tabs */}\n <div className=\"border-b border-surface-border-strong px-ds-05b\">\n <div className=\"flex gap-ds-05 py-ds-04\">\n {Array.from({ length: 3 }).map((_, i) => (\n <Skeleton key={`tab-${i}`} className={cn('h-ds-05 w-ds-10', shimmer)} />\n ))}\n </div>\n </div>\n\n {/* Tab content */}\n <div className=\"flex flex-1 flex-col gap-ds-04 px-ds-05b py-ds-05\">\n <Skeleton className={cn('h-ds-04 w-full', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-4/5', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-3/5', shimmer)} />\n <Skeleton className={cn('mt-ds-03 h-ds-04 w-full', shimmer)} />\n <Skeleton className={cn('h-ds-04 w-2/3', shimmer)} />\n </div>\n </div>\n )\n},\n)\n\nTaskDetailSkeleton.displayName = 'TaskDetailSkeleton'\n\nexport {\n DashboardSkeleton,\n ProjectListSkeleton,\n TaskDetailSkeleton,\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"priority-indicator.js","names":[],"sources":["../../src/composed/priority-indicator.tsx"],"sourcesContent":["'use client'\n\nimport type { Icon as TablerIcon } from '@tabler/icons-react'\nimport {\n IconAlertTriangle,\n IconArrowDown,\n IconArrowUp,\n IconMinus,\n} from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon, type IconProps } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\n\nexport type Priority = 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT' | 'low' | 'medium' | 'high' | 'urgent'\n\nconst priorityConfig: Record<\n Uppercase<Priority>,\n { icon: TablerIcon; color: string; bgColor: string; label: string }\n> = {\n LOW: {\n icon: IconArrowDown,\n color: 'text-category-slate-11',\n bgColor: 'bg-category-slate-3',\n label: 'Low',\n },\n MEDIUM: {\n icon: IconMinus,\n color: 'text-warning-11',\n bgColor: 'bg-warning-3',\n label: 'Medium',\n },\n HIGH: {\n icon: IconArrowUp,\n color: 'text-error-11',\n bgColor: 'bg-error-3',\n label: 'High',\n },\n URGENT: {\n icon: IconAlertTriangle,\n color: 'text-error-11',\n bgColor: 'bg-error-3',\n label: 'Urgent',\n },\n}\n\nconst priorityVariants = cva(\n 'inline-flex items-center gap-ds-02b font-body',\n {\n variants: {\n display: {\n compact: '',\n full: '',\n },\n },\n defaultVariants: {\n display: 'full',\n },\n },\n)\n\nexport interface PriorityIndicatorProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof priorityVariants> {\n priority: Priority\n}\n\nconst PriorityIndicator = React.forwardRef<HTMLDivElement, PriorityIndicatorProps>(\n ({ priority, display, className, ...props }, ref) => {\n const normalizedPriority = priority.toUpperCase() as 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT'\n const config = priorityConfig[normalizedPriority]\n const PriorityIcon = config.icon as IconProps['icon']\n\n const isUrgent = normalizedPriority === 'URGENT'\n\n if (display === 'compact') {\n const iconWrapper = (\n <div\n ref={ref}\n className={cn(\n 'inline-flex items-center justify-center rounded-control p-ds-02',\n config.bgColor,\n className,\n )}\n title={config.label}\n {...props}\n >\n <Icon icon={PriorityIcon} size=\"sm\" className={config.color} />\n </div>\n )\n\n if (isUrgent) {\n return (\n <motion.div\n animate={{ scale: [1, 1.1, 1] }}\n transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}\n className=\"inline-flex\"\n >\n {iconWrapper}\n </motion.div>\n )\n }\n\n return iconWrapper\n }\n\n return (\n <div\n ref={ref}\n className={cn(priorityVariants({ display }), className)}\n {...props}\n >\n {isUrgent ? (\n <motion.div\n animate={{ scale: [1, 1.1, 1] }}\n transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}\n className={cn(\n 'inline-flex items-center justify-center rounded-control p-ds-01',\n config.bgColor,\n )}\n >\n <Icon icon={PriorityIcon} size=\"sm\" className={config.color} />\n </motion.div>\n ) : (\n <div\n className={cn(\n 'inline-flex items-center justify-center rounded-control p-ds-01',\n config.bgColor,\n )}\n >\n <Icon icon={PriorityIcon} size=\"sm\" className={config.color} />\n </div>\n )}\n <span className=\"text-ds-sm text-surface-fg-muted\">\n {config.label}\n </span>\n </div>\n )\n },\n)\nPriorityIndicator.displayName = 'PriorityIndicator'\n\nexport { PriorityIndicator }\n"],"mappings":";;;;;;;;;AAkBA,IAAM,IAGF;CACF,KAAK;EACH,MAAM;EACN,OAAO;EACP,SAAS;EACT,OAAO;
|
|
1
|
+
{"version":3,"file":"priority-indicator.js","names":[],"sources":["../../src/composed/priority-indicator.tsx"],"sourcesContent":["'use client'\n\nimport type { Icon as TablerIcon } from '@tabler/icons-react'\nimport {\n IconAlertTriangle,\n IconArrowDown,\n IconArrowUp,\n IconMinus,\n} from '@tabler/icons-react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon, type IconProps } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\n\nexport type Priority = 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT' | 'low' | 'medium' | 'high' | 'urgent'\n\nconst priorityConfig: Record<\n Uppercase<Priority>,\n { icon: TablerIcon; color: string; bgColor: string; label: string }\n> = {\n LOW: {\n icon: IconArrowDown,\n color: 'text-category-slate-11',\n bgColor: 'bg-category-slate-3',\n label: 'Low',\n },\n MEDIUM: {\n icon: IconMinus,\n color: 'text-warning-11',\n bgColor: 'bg-warning-3',\n label: 'Medium',\n },\n HIGH: {\n icon: IconArrowUp,\n color: 'text-error-11',\n bgColor: 'bg-error-3',\n label: 'High',\n },\n URGENT: {\n icon: IconAlertTriangle,\n color: 'text-error-11',\n bgColor: 'bg-error-3',\n label: 'Urgent',\n },\n}\n\nconst priorityVariants = cva(\n 'inline-flex items-center gap-ds-02b font-body',\n {\n variants: {\n display: {\n compact: '',\n full: '',\n },\n },\n defaultVariants: {\n display: 'full',\n },\n },\n)\n\nexport interface PriorityIndicatorProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof priorityVariants> {\n priority: Priority\n}\n\nconst PriorityIndicator = React.forwardRef<HTMLDivElement, PriorityIndicatorProps>(\n ({ priority, display, className, ...props }, ref) => {\n const normalizedPriority = priority.toUpperCase() as 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT'\n const config = priorityConfig[normalizedPriority]\n const PriorityIcon = config.icon as IconProps['icon']\n\n const isUrgent = normalizedPriority === 'URGENT'\n\n if (display === 'compact') {\n const iconWrapper = (\n <div\n ref={ref}\n className={cn(\n 'inline-flex items-center justify-center rounded-control p-ds-02',\n config.bgColor,\n className,\n )}\n title={config.label}\n {...props}\n >\n <Icon icon={PriorityIcon} size=\"sm\" className={config.color} />\n </div>\n )\n\n if (isUrgent) {\n return (\n <motion.div\n animate={{ scale: [1, 1.1, 1] }}\n transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}\n className=\"inline-flex\"\n >\n {iconWrapper}\n </motion.div>\n )\n }\n\n return iconWrapper\n }\n\n return (\n <div\n ref={ref}\n className={cn(priorityVariants({ display }), className)}\n {...props}\n >\n {isUrgent ? (\n <motion.div\n animate={{ scale: [1, 1.1, 1] }}\n transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}\n className={cn(\n 'inline-flex items-center justify-center rounded-control p-ds-01',\n config.bgColor,\n )}\n >\n <Icon icon={PriorityIcon} size=\"sm\" className={config.color} />\n </motion.div>\n ) : (\n <div\n className={cn(\n 'inline-flex items-center justify-center rounded-control p-ds-01',\n config.bgColor,\n )}\n >\n <Icon icon={PriorityIcon} size=\"sm\" className={config.color} />\n </div>\n )}\n <span className=\"text-ds-sm text-surface-fg-muted\">\n {config.label}\n </span>\n </div>\n )\n },\n)\nPriorityIndicator.displayName = 'PriorityIndicator'\n\nexport { PriorityIndicator }\n"],"mappings":";;;;;;;;;AAkBA,IAAM,IAGF;CACF,KAAK;EACH,MAAM;EACN,OAAO;EACP,SAAS;EACT,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,OAAO;EACP,SAAS;EACT,OAAO;CACT;CACA,MAAM;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,OAAO;CACT;CACA,QAAQ;EACN,MAAM;EACN,OAAO;EACP,SAAS;EACT,OAAO;CACT;AACF,GAEM,IAAmB,EACvB,iDACA;CACE,UAAU,EACR,SAAS;EACP,SAAS;EACT,MAAM;CACR,EACF;CACA,iBAAiB,EACf,SAAS,OACX;AACF,CACF,GAQM,IAAoB,EAAM,YAC7B,EAAE,aAAU,YAAS,cAAW,GAAG,KAAS,MAAQ;CACnD,IAAM,IAAqB,EAAS,YAAY,GAC1C,IAAS,EAAe,IACxB,IAAe,EAAO,MAEtB,IAAW,MAAuB;CAExC,IAAI,MAAY,WAAW;EACzB,IAAM,IACJ,kBAAC,OAAD;GACO;GACL,WAAW,EACT,mEACA,EAAO,SACP,CACF;GACA,OAAO,EAAO;GACd,GAAI;aAEJ,kBAAC,GAAD;IAAM,MAAM;IAAc,MAAK;IAAK,WAAW,EAAO;GAAQ,CAAA;EAC3D,CAAA;EAeP,OAZI,IAEA,kBAAC,EAAO,KAAR;GACE,SAAS,EAAE,OAAO;IAAC;IAAG;IAAK;GAAC,EAAE;GAC9B,YAAY;IAAE,QAAQ;IAAU,UAAU;IAAG,MAAM;GAAY;GAC/D,WAAU;aAET;EACS,CAAA,IAIT;CACT;CAEA,OACE,kBAAC,OAAD;EACO;EACL,WAAW,EAAG,EAAiB,EAAE,WAAQ,CAAC,GAAG,CAAS;EACtD,GAAI;YAHN,CAKG,IACC,kBAAC,EAAO,KAAR;GACE,SAAS,EAAE,OAAO;IAAC;IAAG;IAAK;GAAC,EAAE;GAC9B,YAAY;IAAE,QAAQ;IAAU,UAAU;IAAG,MAAM;GAAY;GAC/D,WAAW,EACT,mEACA,EAAO,OACT;aAEA,kBAAC,GAAD;IAAM,MAAM;IAAc,MAAK;IAAK,WAAW,EAAO;GAAQ,CAAA;EACpD,CAAA,IAEZ,kBAAC,OAAD;GACE,WAAW,EACT,mEACA,EAAO,OACT;aAEA,kBAAC,GAAD;IAAM,MAAM;IAAc,MAAK;IAAK,WAAW,EAAO;GAAQ,CAAA;EAC3D,CAAA,GAEP,kBAAC,QAAD;GAAM,WAAU;aACb,EAAO;EACJ,CAAA,CACH;;AAET,CACF;AACA,EAAkB,cAAc"}
|
|
@@ -77,7 +77,11 @@ var k = 120, A = 500, j = p.forwardRef(({ className: n, children: r, snapPoints:
|
|
|
77
77
|
}, [x, f]);
|
|
78
78
|
let V = p.useCallback((e) => {
|
|
79
79
|
B.current = e, typeof s == "function" ? s(e) : s && (s.current = e);
|
|
80
|
-
}, [s]), H = p.useCallback((e) =>
|
|
80
|
+
}, [s]), H = p.useCallback((e) => {
|
|
81
|
+
if (!N.length || !R) return 0;
|
|
82
|
+
let t = N[e];
|
|
83
|
+
return (P - t) / P * R;
|
|
84
|
+
}, [
|
|
81
85
|
N,
|
|
82
86
|
R,
|
|
83
87
|
P
|
|
@@ -86,12 +90,12 @@ var k = 120, A = 500, j = p.forwardRef(({ className: n, children: r, snapPoints:
|
|
|
86
90
|
(t.offset.y > k || t.velocity.y > A) && w();
|
|
87
91
|
return;
|
|
88
92
|
}
|
|
89
|
-
let n =
|
|
90
|
-
if (I === 0 && (
|
|
93
|
+
let n = H(I) + t.offset.y, r = P - n / R * P, i = N[0] - r;
|
|
94
|
+
if (I === 0 && (i > .15 || t.velocity.y > A)) {
|
|
91
95
|
w();
|
|
92
96
|
return;
|
|
93
97
|
}
|
|
94
|
-
L(O(
|
|
98
|
+
L(O(r, N));
|
|
95
99
|
}, [
|
|
96
100
|
N,
|
|
97
101
|
H,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responsive-modal.js","names":[],"sources":["../../src/composed/responsive-modal.tsx"],"sourcesContent":["'use client'\n\nimport * as DialogPrimitive from '@primitives/react-dialog'\nimport { IconX } from '@tabler/icons-react'\nimport { AnimatePresence, motion, useDragControls, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useIsMobile } from '../hooks/use-mobile'\nimport { Icon } from '../ui/icon'\nimport { springs, tweens } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\n\n/**\n * ResponsiveModal — one overlay that is a centered **Dialog** on desktop (md+)\n * and a partial, content-height **bottom sheet** on mobile (drag-to-dismiss,\n * optional snap points). Built on the same accessible dialog primitive as\n * `Dialog` and `Sheet` (focus trap, Escape, scroll lock), so it is an\n * alternative-to both — not a re-roll.\n *\n * Why it exists: `DialogContent responsive` collapses to a full-screen takeover\n * on mobile (`inset-0`), which leaves dead space under short content and drops\n * the drawer affordances users expect. This composes the two correct halves and\n * owns the fiddly bits (pinned header, internal scroll body with sane height\n * caps, close-button stacking above an optional full-bleed background) so\n * consumers stop hand-rolling a `useIsMobile()` switch.\n *\n * **Parts (in composition order):**\n * - `ResponsiveModal` — open/close state (this root)\n * - `ResponsiveModalTrigger` — opens it (use `asChild`)\n * - `ResponsiveModalContent` — the switching panel (Dialog md+ / bottom sheet below)\n * - `ResponsiveModalBackground` — optional full-bleed layer painted behind content (`-z-10`)\n * - `ResponsiveModalHeader` — pinned; stays put while the body scrolls\n * - `ResponsiveModalTitle` — required for accessibility (labels the dialog)\n * - `ResponsiveModalDescription` — optional subtitle\n * - `ResponsiveModalBody` — internal scroll region (capped 85dvh desktop / 90dvh mobile)\n * - `ResponsiveModalFooter` — pinned action row\n * - `ResponsiveModalClose` — manual close (a close button is built into Content)\n *\n * @compound\n * @example\n * <ResponsiveModal>\n * <ResponsiveModalTrigger asChild><Button>Open</Button></ResponsiveModalTrigger>\n * <ResponsiveModalContent>\n * <ResponsiveModalHeader>\n * <ResponsiveModalTitle>Filters</ResponsiveModalTitle>\n * <ResponsiveModalDescription>Narrow the result set.</ResponsiveModalDescription>\n * </ResponsiveModalHeader>\n * <ResponsiveModalBody>…form fields…</ResponsiveModalBody>\n * <ResponsiveModalFooter>\n * <ResponsiveModalClose asChild><Button variant=\"soft\">Cancel</Button></ResponsiveModalClose>\n * <Button>Apply</Button>\n * </ResponsiveModalFooter>\n * </ResponsiveModalContent>\n * </ResponsiveModal>\n */\n\n// ── Context ──────────────────────────────────────────────────────────────────\n\ntype ResponsiveModalContextValue = {\n open: boolean\n isMobile: boolean\n dismissable: boolean\n onClose: () => void\n}\nconst ResponsiveModalContext = React.createContext<ResponsiveModalContextValue>({\n open: false,\n isMobile: false,\n dismissable: true,\n onClose: () => {},\n})\nconst useResponsiveModal = () => React.useContext(ResponsiveModalContext)\n\n// ── Root ──────────────────────────────────────────────────────────────────────\n\nexport interface ResponsiveModalProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root> {\n /** When false, Escape/overlay/drag cannot dismiss — only an explicit Close/onOpenChange can. @default true */\n dismissable?: boolean\n}\n\nconst ResponsiveModal: React.FC<ResponsiveModalProps> = ({\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n dismissable = true,\n children,\n ...props\n}) => {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = controlledOpen !== undefined\n const open = isControlled ? controlledOpen : uncontrolledOpen\n const isMobile = useIsMobile()\n\n const handleOpenChange = React.useCallback(\n (value: boolean) => {\n // A non-dismissable modal ignores the primitive's implicit close attempts\n // (Escape, outside-click); only value=true or an explicit close gets through.\n if (!value && !dismissable) return\n if (!isControlled) setUncontrolledOpen(value)\n onOpenChange?.(value)\n },\n [isControlled, onOpenChange, dismissable],\n )\n\n const contextValue = React.useMemo<ResponsiveModalContextValue>(\n () => ({ open, isMobile, dismissable, onClose: () => handleOpenChange(false) }),\n [open, isMobile, dismissable, handleOpenChange],\n )\n\n return (\n <ResponsiveModalContext.Provider value={contextValue}>\n <DialogPrimitive.Root open={open} onOpenChange={handleOpenChange} {...props}>\n {children}\n </DialogPrimitive.Root>\n </ResponsiveModalContext.Provider>\n )\n}\nResponsiveModal.displayName = 'ResponsiveModal'\n\nconst ResponsiveModalTrigger = DialogPrimitive.Trigger\nconst ResponsiveModalClose = DialogPrimitive.Close\nconst ResponsiveModalPortal = DialogPrimitive.Portal\n\nconst ResponsiveModalOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n forceMount\n className={cn('fixed inset-0 z-overlay bg-overlay', className)}\n {...props}\n />\n))\nResponsiveModalOverlay.displayName = 'ResponsiveModalOverlay'\n\n// ── Snap points ────────────────────────────────────────────────────────────────\n// A snap point is a fraction (0,1] of the viewport height the sheet rests at.\n// Omitted → the sheet is content-height (capped at 90dvh) with a single rest at\n// its natural height. Provided → the sheet is tall enough for the largest snap\n// and rests at each fraction; dragging below the smallest past a threshold\n// dismisses (when dismissable).\n\nfunction nearestSnapIndex(currentVisibleFraction: number, snapPoints: number[]) {\n let best = 0\n let bestDist = Infinity\n for (let i = 0; i < snapPoints.length; i++) {\n const d = Math.abs(snapPoints[i] - currentVisibleFraction)\n if (d < bestDist) {\n bestDist = d\n best = i\n }\n }\n return best\n}\n\n// ── Content ──────────────────────────────────────────────────────────────────\n\nexport interface ResponsiveModalContentProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {\n /**\n * Mobile-only. Rest heights as ascending fractions of viewport height, e.g.\n * `[0.5, 0.9]`. Omit for a content-height sheet. The largest fraction caps the\n * sheet height. Ignored on desktop.\n */\n snapPoints?: number[]\n /** Mobile-only. Index into `snapPoints` the sheet opens at. @default last (tallest) */\n defaultSnapPoint?: number\n}\n\nconst DRAG_DISMISS_THRESHOLD_PX = 120\nconst DRAG_DISMISS_VELOCITY = 500\n\nconst ResponsiveModalContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n ResponsiveModalContentProps\n>(({ className, children, snapPoints, defaultSnapPoint, ...props }, ref) => {\n const { open, isMobile, dismissable, onClose } = useResponsiveModal()\n const reduced = useReducedMotion()\n const dragControls = useDragControls()\n\n const hasSnaps = isMobile && Array.isArray(snapPoints) && snapPoints.length > 0\n const sortedSnaps = React.useMemo(\n () => (hasSnaps ? [...snapPoints!].sort((a, b) => a - b) : []),\n [hasSnaps, snapPoints],\n )\n const maxSnap = sortedSnaps.length ? sortedSnaps[sortedSnaps.length - 1] : 1\n const initialSnap = React.useMemo(() => {\n if (!sortedSnaps.length) return 0\n const d = defaultSnapPoint ?? sortedSnaps.length - 1\n return Math.min(Math.max(d, 0), sortedSnaps.length - 1)\n }, [sortedSnaps, defaultSnapPoint])\n\n const [activeSnap, setActiveSnap] = React.useState(initialSnap)\n const [sheetHeight, setSheetHeight] = React.useState(0)\n const internalRef = React.useRef<HTMLDivElement>(null)\n\n // Reset to the opening snap each time it re-opens.\n React.useEffect(() => {\n if (open) setActiveSnap(initialSnap)\n }, [open, initialSnap])\n\n // Measure the laid-out sheet height so snap rest positions can be expressed in px.\n React.useEffect(() => {\n if (!isMobile) return\n const el = internalRef.current\n if (!el) return\n const measure = () => setSheetHeight(el.getBoundingClientRect().height)\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(el)\n return () => ro.disconnect()\n }, [isMobile, open])\n\n const composedRef = React.useCallback(\n (node: HTMLDivElement | null) => {\n internalRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\n },\n [ref],\n )\n\n // Rest translateY for a snap index: at the tallest snap y=0; smaller snaps push\n // the sheet down so only that fraction of viewport height shows.\n const restY = React.useCallback(\n (index: number) => {\n if (!sortedSnaps.length || !sheetHeight) return 0\n const visible = sortedSnaps[index]\n return ((maxSnap - visible) / maxSnap) * sheetHeight\n },\n [sortedSnaps, sheetHeight, maxSnap],\n )\n\n const canDrag = isMobile && dismissable && !reduced\n\n const handleDragEnd = React.useCallback(\n (\n _e: unknown,\n info: { offset: { y: number }; velocity: { y: number } },\n ) => {\n if (!sortedSnaps.length) {\n // Content-height sheet: single rest; drag down past threshold dismisses.\n if (info.offset.y > DRAG_DISMISS_THRESHOLD_PX || info.velocity.y > DRAG_DISMISS_VELOCITY) {\n onClose()\n }\n return\n }\n // Snapped sheet: project the release point onto the visible-fraction axis.\n const currentY = restY(activeSnap) + info.offset.y\n const draggedVisible = maxSnap - (currentY / sheetHeight) * maxSnap\n const belowLowest = sortedSnaps[0] - draggedVisible\n if (\n activeSnap === 0 &&\n (belowLowest > 0.15 || info.velocity.y > DRAG_DISMISS_VELOCITY)\n ) {\n onClose()\n return\n }\n setActiveSnap(nearestSnapIndex(draggedVisible, sortedSnaps))\n },\n [sortedSnaps, restY, activeSnap, maxSnap, sheetHeight, onClose],\n )\n\n // ── Desktop: centered Dialog ──\n const desktopClasses =\n 'fixed left-[50%] top-[50%] z-modal flex max-h-[85dvh] w-full max-w-lg flex-col overflow-hidden rounded-overlay-lg bg-surface-overlay shadow-overlay'\n\n // ── Mobile: bottom sheet ──\n const mobileClasses = cn(\n 'fixed inset-x-0 bottom-0 z-modal flex flex-col overflow-hidden rounded-t-overlay-lg border-t border-surface-border-strong bg-surface-overlay shadow-overlay',\n // Snap sheets are as tall as the largest snap; content sheets hug content up to 90dvh.\n hasSnaps ? undefined : 'max-h-[90dvh]',\n )\n\n const closeButton = dismissable ? (\n <DialogPrimitive.Close\n title=\"Close\"\n className=\"absolute right-ds-05 top-ds-05 z-10 flex min-h-ds-xs min-w-ds-xs items-center justify-center rounded-control-inner text-surface-fg-subtle transition-colors duration-fast-01 ease-productive-standard hover:bg-surface-raised-hover hover:text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 active:scale-90 disabled:pointer-events-none\"\n >\n <Icon icon={IconX} size=\"lg\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n ) : null\n\n return (\n <AnimatePresence>\n {open && (\n <ResponsiveModalPortal forceMount>\n <ResponsiveModalOverlay asChild>\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n />\n </ResponsiveModalOverlay>\n <DialogPrimitive.Content\n ref={composedRef}\n forceMount\n asChild\n onPointerDownOutside={dismissable ? undefined : (e) => e.preventDefault()}\n {...props}\n >\n <motion.div\n className={cn(isMobile ? mobileClasses : desktopClasses, className)}\n style={\n hasSnaps\n ? { height: `${maxSnap * 100}dvh` }\n : undefined\n }\n initial={isMobile ? { y: '100%' } : { opacity: 0, scale: 0.96, x: '-50%', y: '-50%' }}\n animate={\n isMobile\n ? { y: hasSnaps ? restY(activeSnap) : 0 }\n : { opacity: 1, scale: 1, x: '-50%', y: '-50%' }\n }\n exit={isMobile ? { y: '100%' } : { opacity: 0, scale: 0.96, x: '-50%', y: '-50%' }}\n transition={isMobile ? springs.smooth : { ...springs.smooth, opacity: tweens.fade }}\n drag={canDrag ? 'y' : false}\n dragListener={false}\n dragControls={dragControls}\n dragConstraints={{ top: 0 }}\n dragElastic={0.15}\n onDragEnd={canDrag ? handleDragEnd : undefined}\n >\n {/* Drag handle — the only surface that initiates a drag, so the\n scroll body below never fights the gesture (useDragControls). */}\n {isMobile && (\n <div\n className={cn('flex shrink-0 justify-center pb-ds-01 pt-ds-03', canDrag && 'cursor-grab touch-none')}\n onPointerDown={canDrag ? (e) => dragControls.start(e) : undefined}\n >\n <div className=\"h-1 w-8 rounded-pill bg-surface-border\" />\n </div>\n )}\n {closeButton}\n {children}\n </motion.div>\n </DialogPrimitive.Content>\n </ResponsiveModalPortal>\n )}\n </AnimatePresence>\n )\n})\nResponsiveModalContent.displayName = 'ResponsiveModalContent'\n\n// ── Background slot ────────────────────────────────────────────────────────────\n\n/**\n * Full-bleed layer painted behind the modal content (`-z-10`), clipped to the\n * panel's rounded corners. Render whatever you want behind the content (a\n * gradient, an aurora, an image). It is non-interactive by default.\n */\nconst ResponsiveModalBackground = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n aria-hidden\n className={cn('pointer-events-none absolute inset-0 -z-10 overflow-hidden rounded-[inherit]', className)}\n {...props}\n />\n ),\n)\nResponsiveModalBackground.displayName = 'ResponsiveModalBackground'\n\n// ── Layout parts ───────────────────────────────────────────────────────────────\n\n/** Pinned header — sits above the scroll body and does not scroll with it. */\nconst ResponsiveModalHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex shrink-0 flex-col gap-ds-02b px-ds-06 pb-ds-04 pt-ds-05 text-center sm:text-left',\n className,\n )}\n {...props}\n />\n ),\n)\nResponsiveModalHeader.displayName = 'ResponsiveModalHeader'\n\n/** Internal scroll region — grows to fill, scrolls when content exceeds the height cap. */\nconst ResponsiveModalBody = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('min-h-0 flex-1 overflow-y-auto px-ds-06 py-ds-02', className)}\n {...props}\n />\n ),\n)\nResponsiveModalBody.displayName = 'ResponsiveModalBody'\n\n/** Pinned footer action row. Stacks on mobile, right-aligned inline on desktop. */\nconst ResponsiveModalFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex shrink-0 flex-col-reverse gap-ds-03 px-ds-06 pb-ds-06 pt-ds-04 sm:flex-row sm:justify-end',\n className,\n )}\n {...props}\n />\n ),\n)\nResponsiveModalFooter.displayName = 'ResponsiveModalFooter'\n\nconst ResponsiveModalTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn('text-ds-lg font-semibold text-surface-fg', className)}\n {...props}\n />\n))\nResponsiveModalTitle.displayName = 'ResponsiveModalTitle'\n\nconst ResponsiveModalDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-ds-md text-surface-fg-muted', className)}\n {...props}\n />\n))\nResponsiveModalDescription.displayName = 'ResponsiveModalDescription'\n\nexport {\n ResponsiveModal,\n ResponsiveModalBackground,\n ResponsiveModalBody,\n ResponsiveModalClose,\n ResponsiveModalContent,\n ResponsiveModalDescription,\n ResponsiveModalFooter,\n ResponsiveModalHeader,\n ResponsiveModalOverlay,\n ResponsiveModalPortal,\n ResponsiveModalTitle,\n ResponsiveModalTrigger,\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"responsive-modal.js","names":[],"sources":["../../src/composed/responsive-modal.tsx"],"sourcesContent":["'use client'\n\nimport * as DialogPrimitive from '@primitives/react-dialog'\nimport { IconX } from '@tabler/icons-react'\nimport { AnimatePresence, motion, useDragControls, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useIsMobile } from '../hooks/use-mobile'\nimport { Icon } from '../ui/icon'\nimport { springs, tweens } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\n\n/**\n * ResponsiveModal — one overlay that is a centered **Dialog** on desktop (md+)\n * and a partial, content-height **bottom sheet** on mobile (drag-to-dismiss,\n * optional snap points). Built on the same accessible dialog primitive as\n * `Dialog` and `Sheet` (focus trap, Escape, scroll lock), so it is an\n * alternative-to both — not a re-roll.\n *\n * Why it exists: `DialogContent responsive` collapses to a full-screen takeover\n * on mobile (`inset-0`), which leaves dead space under short content and drops\n * the drawer affordances users expect. This composes the two correct halves and\n * owns the fiddly bits (pinned header, internal scroll body with sane height\n * caps, close-button stacking above an optional full-bleed background) so\n * consumers stop hand-rolling a `useIsMobile()` switch.\n *\n * **Parts (in composition order):**\n * - `ResponsiveModal` — open/close state (this root)\n * - `ResponsiveModalTrigger` — opens it (use `asChild`)\n * - `ResponsiveModalContent` — the switching panel (Dialog md+ / bottom sheet below)\n * - `ResponsiveModalBackground` — optional full-bleed layer painted behind content (`-z-10`)\n * - `ResponsiveModalHeader` — pinned; stays put while the body scrolls\n * - `ResponsiveModalTitle` — required for accessibility (labels the dialog)\n * - `ResponsiveModalDescription` — optional subtitle\n * - `ResponsiveModalBody` — internal scroll region (capped 85dvh desktop / 90dvh mobile)\n * - `ResponsiveModalFooter` — pinned action row\n * - `ResponsiveModalClose` — manual close (a close button is built into Content)\n *\n * @compound\n * @example\n * <ResponsiveModal>\n * <ResponsiveModalTrigger asChild><Button>Open</Button></ResponsiveModalTrigger>\n * <ResponsiveModalContent>\n * <ResponsiveModalHeader>\n * <ResponsiveModalTitle>Filters</ResponsiveModalTitle>\n * <ResponsiveModalDescription>Narrow the result set.</ResponsiveModalDescription>\n * </ResponsiveModalHeader>\n * <ResponsiveModalBody>…form fields…</ResponsiveModalBody>\n * <ResponsiveModalFooter>\n * <ResponsiveModalClose asChild><Button variant=\"soft\">Cancel</Button></ResponsiveModalClose>\n * <Button>Apply</Button>\n * </ResponsiveModalFooter>\n * </ResponsiveModalContent>\n * </ResponsiveModal>\n */\n\n// ── Context ──────────────────────────────────────────────────────────────────\n\ntype ResponsiveModalContextValue = {\n open: boolean\n isMobile: boolean\n dismissable: boolean\n onClose: () => void\n}\nconst ResponsiveModalContext = React.createContext<ResponsiveModalContextValue>({\n open: false,\n isMobile: false,\n dismissable: true,\n onClose: () => {},\n})\nconst useResponsiveModal = () => React.useContext(ResponsiveModalContext)\n\n// ── Root ──────────────────────────────────────────────────────────────────────\n\nexport interface ResponsiveModalProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root> {\n /** When false, Escape/overlay/drag cannot dismiss — only an explicit Close/onOpenChange can. @default true */\n dismissable?: boolean\n}\n\nconst ResponsiveModal: React.FC<ResponsiveModalProps> = ({\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n dismissable = true,\n children,\n ...props\n}) => {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = controlledOpen !== undefined\n const open = isControlled ? controlledOpen : uncontrolledOpen\n const isMobile = useIsMobile()\n\n const handleOpenChange = React.useCallback(\n (value: boolean) => {\n // A non-dismissable modal ignores the primitive's implicit close attempts\n // (Escape, outside-click); only value=true or an explicit close gets through.\n if (!value && !dismissable) return\n if (!isControlled) setUncontrolledOpen(value)\n onOpenChange?.(value)\n },\n [isControlled, onOpenChange, dismissable],\n )\n\n const contextValue = React.useMemo<ResponsiveModalContextValue>(\n () => ({ open, isMobile, dismissable, onClose: () => handleOpenChange(false) }),\n [open, isMobile, dismissable, handleOpenChange],\n )\n\n return (\n <ResponsiveModalContext.Provider value={contextValue}>\n <DialogPrimitive.Root open={open} onOpenChange={handleOpenChange} {...props}>\n {children}\n </DialogPrimitive.Root>\n </ResponsiveModalContext.Provider>\n )\n}\nResponsiveModal.displayName = 'ResponsiveModal'\n\nconst ResponsiveModalTrigger = DialogPrimitive.Trigger\nconst ResponsiveModalClose = DialogPrimitive.Close\nconst ResponsiveModalPortal = DialogPrimitive.Portal\n\nconst ResponsiveModalOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n forceMount\n className={cn('fixed inset-0 z-overlay bg-overlay', className)}\n {...props}\n />\n))\nResponsiveModalOverlay.displayName = 'ResponsiveModalOverlay'\n\n// ── Snap points ────────────────────────────────────────────────────────────────\n// A snap point is a fraction (0,1] of the viewport height the sheet rests at.\n// Omitted → the sheet is content-height (capped at 90dvh) with a single rest at\n// its natural height. Provided → the sheet is tall enough for the largest snap\n// and rests at each fraction; dragging below the smallest past a threshold\n// dismisses (when dismissable).\n\nfunction nearestSnapIndex(currentVisibleFraction: number, snapPoints: number[]) {\n let best = 0\n let bestDist = Infinity\n for (let i = 0; i < snapPoints.length; i++) {\n const d = Math.abs(snapPoints[i] - currentVisibleFraction)\n if (d < bestDist) {\n bestDist = d\n best = i\n }\n }\n return best\n}\n\n// ── Content ──────────────────────────────────────────────────────────────────\n\nexport interface ResponsiveModalContentProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {\n /**\n * Mobile-only. Rest heights as ascending fractions of viewport height, e.g.\n * `[0.5, 0.9]`. Omit for a content-height sheet. The largest fraction caps the\n * sheet height. Ignored on desktop.\n */\n snapPoints?: number[]\n /** Mobile-only. Index into `snapPoints` the sheet opens at. @default last (tallest) */\n defaultSnapPoint?: number\n}\n\nconst DRAG_DISMISS_THRESHOLD_PX = 120\nconst DRAG_DISMISS_VELOCITY = 500\n\nconst ResponsiveModalContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n ResponsiveModalContentProps\n>(({ className, children, snapPoints, defaultSnapPoint, ...props }, ref) => {\n const { open, isMobile, dismissable, onClose } = useResponsiveModal()\n const reduced = useReducedMotion()\n const dragControls = useDragControls()\n\n const hasSnaps = isMobile && Array.isArray(snapPoints) && snapPoints.length > 0\n const sortedSnaps = React.useMemo(\n () => (hasSnaps ? [...snapPoints!].sort((a, b) => a - b) : []),\n [hasSnaps, snapPoints],\n )\n const maxSnap = sortedSnaps.length ? sortedSnaps[sortedSnaps.length - 1] : 1\n const initialSnap = React.useMemo(() => {\n if (!sortedSnaps.length) return 0\n const d = defaultSnapPoint ?? sortedSnaps.length - 1\n return Math.min(Math.max(d, 0), sortedSnaps.length - 1)\n }, [sortedSnaps, defaultSnapPoint])\n\n const [activeSnap, setActiveSnap] = React.useState(initialSnap)\n const [sheetHeight, setSheetHeight] = React.useState(0)\n const internalRef = React.useRef<HTMLDivElement>(null)\n\n // Reset to the opening snap each time it re-opens.\n React.useEffect(() => {\n if (open) setActiveSnap(initialSnap)\n }, [open, initialSnap])\n\n // Measure the laid-out sheet height so snap rest positions can be expressed in px.\n React.useEffect(() => {\n if (!isMobile) return\n const el = internalRef.current\n if (!el) return\n const measure = () => setSheetHeight(el.getBoundingClientRect().height)\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(el)\n return () => ro.disconnect()\n }, [isMobile, open])\n\n const composedRef = React.useCallback(\n (node: HTMLDivElement | null) => {\n internalRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\n },\n [ref],\n )\n\n // Rest translateY for a snap index: at the tallest snap y=0; smaller snaps push\n // the sheet down so only that fraction of viewport height shows.\n const restY = React.useCallback(\n (index: number) => {\n if (!sortedSnaps.length || !sheetHeight) return 0\n const visible = sortedSnaps[index]\n return ((maxSnap - visible) / maxSnap) * sheetHeight\n },\n [sortedSnaps, sheetHeight, maxSnap],\n )\n\n const canDrag = isMobile && dismissable && !reduced\n\n const handleDragEnd = React.useCallback(\n (\n _e: unknown,\n info: { offset: { y: number }; velocity: { y: number } },\n ) => {\n if (!sortedSnaps.length) {\n // Content-height sheet: single rest; drag down past threshold dismisses.\n if (info.offset.y > DRAG_DISMISS_THRESHOLD_PX || info.velocity.y > DRAG_DISMISS_VELOCITY) {\n onClose()\n }\n return\n }\n // Snapped sheet: project the release point onto the visible-fraction axis.\n const currentY = restY(activeSnap) + info.offset.y\n const draggedVisible = maxSnap - (currentY / sheetHeight) * maxSnap\n const belowLowest = sortedSnaps[0] - draggedVisible\n if (\n activeSnap === 0 &&\n (belowLowest > 0.15 || info.velocity.y > DRAG_DISMISS_VELOCITY)\n ) {\n onClose()\n return\n }\n setActiveSnap(nearestSnapIndex(draggedVisible, sortedSnaps))\n },\n [sortedSnaps, restY, activeSnap, maxSnap, sheetHeight, onClose],\n )\n\n // ── Desktop: centered Dialog ──\n const desktopClasses =\n 'fixed left-[50%] top-[50%] z-modal flex max-h-[85dvh] w-full max-w-lg flex-col overflow-hidden rounded-overlay-lg bg-surface-overlay shadow-overlay'\n\n // ── Mobile: bottom sheet ──\n const mobileClasses = cn(\n 'fixed inset-x-0 bottom-0 z-modal flex flex-col overflow-hidden rounded-t-overlay-lg border-t border-surface-border-strong bg-surface-overlay shadow-overlay',\n // Snap sheets are as tall as the largest snap; content sheets hug content up to 90dvh.\n hasSnaps ? undefined : 'max-h-[90dvh]',\n )\n\n const closeButton = dismissable ? (\n <DialogPrimitive.Close\n title=\"Close\"\n className=\"absolute right-ds-05 top-ds-05 z-10 flex min-h-ds-xs min-w-ds-xs items-center justify-center rounded-control-inner text-surface-fg-subtle transition-colors duration-fast-01 ease-productive-standard hover:bg-surface-raised-hover hover:text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 active:scale-90 disabled:pointer-events-none\"\n >\n <Icon icon={IconX} size=\"lg\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n ) : null\n\n return (\n <AnimatePresence>\n {open && (\n <ResponsiveModalPortal forceMount>\n <ResponsiveModalOverlay asChild>\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n />\n </ResponsiveModalOverlay>\n <DialogPrimitive.Content\n ref={composedRef}\n forceMount\n asChild\n onPointerDownOutside={dismissable ? undefined : (e) => e.preventDefault()}\n {...props}\n >\n <motion.div\n className={cn(isMobile ? mobileClasses : desktopClasses, className)}\n style={\n hasSnaps\n ? { height: `${maxSnap * 100}dvh` }\n : undefined\n }\n initial={isMobile ? { y: '100%' } : { opacity: 0, scale: 0.96, x: '-50%', y: '-50%' }}\n animate={\n isMobile\n ? { y: hasSnaps ? restY(activeSnap) : 0 }\n : { opacity: 1, scale: 1, x: '-50%', y: '-50%' }\n }\n exit={isMobile ? { y: '100%' } : { opacity: 0, scale: 0.96, x: '-50%', y: '-50%' }}\n transition={isMobile ? springs.smooth : { ...springs.smooth, opacity: tweens.fade }}\n drag={canDrag ? 'y' : false}\n dragListener={false}\n dragControls={dragControls}\n dragConstraints={{ top: 0 }}\n dragElastic={0.15}\n onDragEnd={canDrag ? handleDragEnd : undefined}\n >\n {/* Drag handle — the only surface that initiates a drag, so the\n scroll body below never fights the gesture (useDragControls). */}\n {isMobile && (\n <div\n className={cn('flex shrink-0 justify-center pb-ds-01 pt-ds-03', canDrag && 'cursor-grab touch-none')}\n onPointerDown={canDrag ? (e) => dragControls.start(e) : undefined}\n >\n <div className=\"h-1 w-8 rounded-pill bg-surface-border\" />\n </div>\n )}\n {closeButton}\n {children}\n </motion.div>\n </DialogPrimitive.Content>\n </ResponsiveModalPortal>\n )}\n </AnimatePresence>\n )\n})\nResponsiveModalContent.displayName = 'ResponsiveModalContent'\n\n// ── Background slot ────────────────────────────────────────────────────────────\n\n/**\n * Full-bleed layer painted behind the modal content (`-z-10`), clipped to the\n * panel's rounded corners. Render whatever you want behind the content (a\n * gradient, an aurora, an image). It is non-interactive by default.\n */\nconst ResponsiveModalBackground = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n aria-hidden\n className={cn('pointer-events-none absolute inset-0 -z-10 overflow-hidden rounded-[inherit]', className)}\n {...props}\n />\n ),\n)\nResponsiveModalBackground.displayName = 'ResponsiveModalBackground'\n\n// ── Layout parts ───────────────────────────────────────────────────────────────\n\n/** Pinned header — sits above the scroll body and does not scroll with it. */\nconst ResponsiveModalHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex shrink-0 flex-col gap-ds-02b px-ds-06 pb-ds-04 pt-ds-05 text-center sm:text-left',\n className,\n )}\n {...props}\n />\n ),\n)\nResponsiveModalHeader.displayName = 'ResponsiveModalHeader'\n\n/** Internal scroll region — grows to fill, scrolls when content exceeds the height cap. */\nconst ResponsiveModalBody = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('min-h-0 flex-1 overflow-y-auto px-ds-06 py-ds-02', className)}\n {...props}\n />\n ),\n)\nResponsiveModalBody.displayName = 'ResponsiveModalBody'\n\n/** Pinned footer action row. Stacks on mobile, right-aligned inline on desktop. */\nconst ResponsiveModalFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex shrink-0 flex-col-reverse gap-ds-03 px-ds-06 pb-ds-06 pt-ds-04 sm:flex-row sm:justify-end',\n className,\n )}\n {...props}\n />\n ),\n)\nResponsiveModalFooter.displayName = 'ResponsiveModalFooter'\n\nconst ResponsiveModalTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn('text-ds-lg font-semibold text-surface-fg', className)}\n {...props}\n />\n))\nResponsiveModalTitle.displayName = 'ResponsiveModalTitle'\n\nconst ResponsiveModalDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-ds-md text-surface-fg-muted', className)}\n {...props}\n />\n))\nResponsiveModalDescription.displayName = 'ResponsiveModalDescription'\n\nexport {\n ResponsiveModal,\n ResponsiveModalBackground,\n ResponsiveModalBody,\n ResponsiveModalClose,\n ResponsiveModalContent,\n ResponsiveModalDescription,\n ResponsiveModalFooter,\n ResponsiveModalHeader,\n ResponsiveModalOverlay,\n ResponsiveModalPortal,\n ResponsiveModalTitle,\n ResponsiveModalTrigger,\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
2
3
|
export interface Attachment {
|
|
3
4
|
id: string;
|
|
4
5
|
url?: string;
|
|
@@ -11,5 +12,5 @@ export interface AttachmentStripProps {
|
|
|
11
12
|
attachments: Attachment[];
|
|
12
13
|
onRemoveAttachment: (id: string) => void;
|
|
13
14
|
}
|
|
14
|
-
export declare function AttachmentStrip({ attachments, onRemoveAttachment, }: AttachmentStripProps):
|
|
15
|
+
export declare function AttachmentStrip({ attachments, onRemoveAttachment, }: AttachmentStripProps): React.JSX.Element;
|
|
15
16
|
//# sourceMappingURL=attachment-strip.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment-strip.d.ts","sourceRoot":"","sources":["../../../src/composed/rich-chat-input/attachment-strip.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"attachment-strip.d.ts","sourceRoot":"","sources":["../../../src/composed/rich-chat-input/attachment-strip.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,GAAG,MAAM,CAAA;IACtB,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,kBAAkB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CACzC;AAQD,wBAAgB,eAAe,CAAC,EAC9B,WAAW,EACX,kBAAkB,GACnB,EAAE,oBAAoB,qBAiGtB"}
|
|
@@ -16,27 +16,27 @@ export declare function ToolbarButton({ onClick, isActive, disabled: disabledPro
|
|
|
16
16
|
title: string;
|
|
17
17
|
children: React.ReactNode;
|
|
18
18
|
className?: string;
|
|
19
|
-
}):
|
|
19
|
+
}): React.JSX.Element;
|
|
20
20
|
/** Visual separator between toolbar button groups. */
|
|
21
|
-
export declare function ToolbarDivider():
|
|
21
|
+
export declare function ToolbarDivider(): React.JSX.Element;
|
|
22
22
|
/** Group wrapper — applies consistent spacing to a set of buttons. */
|
|
23
23
|
export declare function ToolbarGroup({ children, className }: {
|
|
24
24
|
children: React.ReactNode;
|
|
25
25
|
className?: string;
|
|
26
|
-
}):
|
|
27
|
-
export declare function BoldButton():
|
|
28
|
-
export declare function ItalicButton():
|
|
29
|
-
export declare function UnderlineButton():
|
|
30
|
-
export declare function StrikeButton():
|
|
31
|
-
export declare function HighlightButton():
|
|
32
|
-
export declare function CodeButton():
|
|
33
|
-
export declare function BulletListButton():
|
|
34
|
-
export declare function OrderedListButton():
|
|
35
|
-
export declare function BlockquoteButton():
|
|
36
|
-
export declare function LinkButton():
|
|
37
|
-
export declare function MentionButton():
|
|
38
|
-
export declare function EmojiButton():
|
|
39
|
-
export declare function SlashCommandButton():
|
|
26
|
+
}): React.JSX.Element;
|
|
27
|
+
export declare function BoldButton(): React.JSX.Element;
|
|
28
|
+
export declare function ItalicButton(): React.JSX.Element;
|
|
29
|
+
export declare function UnderlineButton(): React.JSX.Element;
|
|
30
|
+
export declare function StrikeButton(): React.JSX.Element;
|
|
31
|
+
export declare function HighlightButton(): React.JSX.Element;
|
|
32
|
+
export declare function CodeButton(): React.JSX.Element;
|
|
33
|
+
export declare function BulletListButton(): React.JSX.Element;
|
|
34
|
+
export declare function OrderedListButton(): React.JSX.Element;
|
|
35
|
+
export declare function BlockquoteButton(): React.JSX.Element;
|
|
36
|
+
export declare function LinkButton(): React.JSX.Element;
|
|
37
|
+
export declare function MentionButton(): React.JSX.Element;
|
|
38
|
+
export declare function EmojiButton(): React.JSX.Element;
|
|
39
|
+
export declare function SlashCommandButton(): React.JSX.Element;
|
|
40
40
|
export type ChatToolbarItem = 'bold' | 'italic' | 'underline' | 'strike' | 'highlight' | 'code' | 'bulletList' | 'orderedList' | 'blockquote' | 'link' | 'mention' | 'emoji' | 'slash';
|
|
41
41
|
export interface ChatToolbarProps {
|
|
42
42
|
editor: Editor;
|
|
@@ -47,5 +47,5 @@ export interface ChatToolbarProps {
|
|
|
47
47
|
hasSlashCommands: boolean;
|
|
48
48
|
disabled: boolean;
|
|
49
49
|
}
|
|
50
|
-
export declare function ChatToolbar({ editor, toolbar, isMobile, hasMentions, hasSlashCommands, disabled, }: ChatToolbarProps):
|
|
50
|
+
export declare function ChatToolbar({ editor, toolbar, isMobile, hasMentions, hasSlashCommands, disabled, }: ChatToolbarProps): React.JSX.Element | null;
|
|
51
51
|
//# sourceMappingURL=chat-toolbar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-toolbar.d.ts","sourceRoot":"","sources":["../../../src/composed/rich-chat-input/chat-toolbar.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAkB9B;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,QAAQ,EAAE,YAAY,EACtB,KAAK,EACL,QAAQ,EACR,SAAS,EAAE,aAAa,GACzB,EAAE;IACD,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,
|
|
1
|
+
{"version":3,"file":"chat-toolbar.d.ts","sourceRoot":"","sources":["../../../src/composed/rich-chat-input/chat-toolbar.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAkB9B;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,QAAQ,EAAE,YAAY,EACtB,KAAK,EACL,QAAQ,EACR,SAAS,EAAE,aAAa,GACzB,EAAE;IACD,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,qBA0BA;AAED,sDAAsD;AACtD,wBAAgB,cAAc,sBAE7B;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,qBAEtG;AAKD,wBAAgB,UAAU,sBAWzB;AAED,wBAAgB,YAAY,sBAW3B;AAED,wBAAgB,eAAe,sBAW9B;AAED,wBAAgB,YAAY,sBAW3B;AAED,wBAAgB,eAAe,sBAW9B;AAED,wBAAgB,UAAU,sBAWzB;AAED,wBAAgB,gBAAgB,sBAW/B;AAED,wBAAgB,iBAAiB,sBAWhC;AAED,wBAAgB,gBAAgB,sBAW/B;AAED,wBAAgB,UAAU,sBAkBzB;AAED,wBAAgB,aAAa,sBAO5B;AAED,wBAAgB,WAAW,sBAO1B;AAED,wBAAgB,kBAAkB,sBAOjC;AAID,MAAM,MAAM,eAAe,GACvB,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,GACjE,YAAY,GAAG,aAAa,GAC5B,YAAY,GAAG,MAAM,GACrB,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;AA2DjC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,+FAA+F;IAC/F,OAAO,EAAE,OAAO,GAAG,eAAe,EAAE,GAAG,KAAK,CAAC,SAAS,CAAA;IACtD,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,OAAO,CAAA;IACzB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,OAAO,EACP,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,QAAQ,GACT,EAAE,gBAAgB,4BAwBlB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
2
3
|
interface RecordingOverlayProps {
|
|
3
4
|
duration: number;
|
|
4
5
|
analyserNode: AnalyserNode | null;
|
|
@@ -6,7 +7,7 @@ interface RecordingOverlayProps {
|
|
|
6
7
|
/** Escape key handler — cancel recording. Stop/cancel buttons are external. */
|
|
7
8
|
onCancel: () => void;
|
|
8
9
|
}
|
|
9
|
-
declare function RecordingOverlay({ duration, analyserNode, maxDuration, onCancel, }: RecordingOverlayProps):
|
|
10
|
+
declare function RecordingOverlay({ duration, analyserNode, maxDuration, onCancel, }: RecordingOverlayProps): React.JSX.Element;
|
|
10
11
|
declare namespace RecordingOverlay {
|
|
11
12
|
var displayName: string;
|
|
12
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recording-overlay.d.ts","sourceRoot":"","sources":["../../../src/composed/rich-chat-input/recording-overlay.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"recording-overlay.d.ts","sourceRoot":"","sources":["../../../src/composed/rich-chat-input/recording-overlay.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB;AAYD,iBAAS,gBAAgB,CAAC,EACxB,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,QAAQ,GACT,EAAE,qBAAqB,qBA6EvB;kBAlFQ,gBAAgB;;;AAqFzB,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAC3B,YAAY,EAAE,qBAAqB,EAAE,CAAA"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
2
3
|
export interface ReplyBannerProps {
|
|
3
4
|
author: string;
|
|
4
5
|
preview: string;
|
|
5
6
|
onDismiss: () => void;
|
|
6
7
|
}
|
|
7
|
-
export declare function ReplyBanner({ author, preview, onDismiss }: ReplyBannerProps):
|
|
8
|
+
export declare function ReplyBanner({ author, preview, onDismiss }: ReplyBannerProps): React.JSX.Element;
|
|
8
9
|
//# sourceMappingURL=reply-banner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reply-banner.d.ts","sourceRoot":"","sources":["../../../src/composed/rich-chat-input/reply-banner.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reply-banner.d.ts","sourceRoot":"","sources":["../../../src/composed/rich-chat-input/reply-banner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,gBAAgB,qBAwC3E"}
|