@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
package/dist/ui/text.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","names":[],"sources":["../../src/ui/text.tsx"],"sourcesContent":["// @server-safe\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\n\nconst textVariants = cva('font-sans', {\n variants: {\n variant: {\n 'heading-2xl': 'font-display text-[length:var(--typo-heading-2xl-size)] font-[number:var(--typo-heading-2xl-weight)] leading-[var(--typo-heading-2xl-leading)] tracking-[var(--typo-heading-2xl-tracking)]',\n 'heading-xl': 'font-display text-[length:var(--typo-heading-xl-size)] font-[number:var(--typo-heading-xl-weight)] leading-[var(--typo-heading-xl-leading)] tracking-[var(--typo-heading-xl-tracking)]',\n 'heading-lg': 'font-display text-[length:var(--typo-heading-lg-size)] font-[number:var(--typo-heading-lg-weight)] leading-[var(--typo-heading-lg-leading)] tracking-[var(--typo-heading-lg-tracking)]',\n 'heading-md': 'font-display text-[length:var(--typo-heading-md-size)] font-[number:var(--typo-heading-md-weight)] leading-[var(--typo-heading-md-leading)] tracking-[var(--typo-heading-md-tracking)]',\n 'heading-sm': 'font-display text-[length:var(--typo-heading-sm-size)] font-[number:var(--typo-heading-sm-weight)] leading-[var(--typo-heading-sm-leading)] tracking-[var(--typo-heading-sm-tracking)]',\n 'heading-xs': 'font-display text-[length:var(--typo-heading-xs-size)] font-[number:var(--typo-heading-xs-weight)] leading-[var(--typo-heading-xs-leading)] tracking-[var(--typo-heading-xs-tracking)]',\n 'body-lg': 'text-[length:var(--typo-body-lg-size)] font-[number:var(--typo-body-lg-weight)] leading-[var(--typo-body-lg-leading)] tracking-[var(--typo-body-lg-tracking)]',\n 'body-md': 'text-[length:var(--typo-body-md-size)] font-[number:var(--typo-body-md-weight)] leading-[var(--typo-body-md-leading)] tracking-[var(--typo-body-md-tracking)]',\n 'body-sm': 'text-[length:var(--typo-body-sm-size)] font-[number:var(--typo-body-sm-weight)] leading-[var(--typo-body-sm-leading)] tracking-[var(--typo-body-sm-tracking)]',\n 'body-xs': 'text-[length:var(--typo-body-xs-size)] font-[number:var(--typo-body-xs-weight)] leading-[var(--typo-body-xs-leading)] tracking-[var(--typo-body-xs-tracking)]',\n 'label-lg': 'text-[length:var(--typo-label-lg-size)] font-[number:var(--typo-label-lg-weight)] leading-[var(--typo-label-lg-leading)] tracking-[var(--typo-label-lg-tracking)] uppercase',\n 'label-md': 'text-[length:var(--typo-label-md-size)] font-[number:var(--typo-label-md-weight)] leading-[var(--typo-label-md-leading)] tracking-[var(--typo-label-md-tracking)] uppercase',\n 'label-sm': 'text-[length:var(--typo-label-sm-size)] font-[number:var(--typo-label-sm-weight)] leading-[var(--typo-label-sm-leading)] tracking-[var(--typo-label-sm-tracking)] uppercase',\n 'label-xs': 'text-[length:var(--typo-label-xs-size)] font-[number:var(--typo-label-xs-weight)] leading-[var(--typo-label-xs-leading)] tracking-[var(--typo-label-xs-tracking)] uppercase',\n caption: 'text-[length:var(--typo-caption-size)] font-[number:var(--typo-caption-weight)] leading-[var(--typo-caption-leading)] tracking-[var(--typo-caption-tracking)]',\n overline: 'text-[length:var(--typo-overline-size)] font-[number:var(--typo-overline-weight)] leading-[var(--typo-overline-leading)] tracking-[var(--typo-overline-tracking)] uppercase',\n code: 'text-[length:var(--typo-code-size)] font-[number:var(--typo-code-weight)] leading-[var(--typo-code-leading)] tracking-[var(--typo-code-tracking)] font-mono',\n 'label-plain-lg': 'text-[length:var(--typo-label-plain-lg-size)] font-[number:var(--typo-label-plain-lg-weight)] leading-[var(--typo-label-plain-lg-leading)] tracking-[var(--typo-label-plain-lg-tracking)]',\n 'label-plain-md': 'text-[length:var(--typo-label-plain-md-size)] font-[number:var(--typo-label-plain-md-weight)] leading-[var(--typo-label-plain-md-leading)] tracking-[var(--typo-label-plain-md-tracking)]',\n 'label-plain-sm': 'text-[length:var(--typo-label-plain-sm-size)] font-[number:var(--typo-label-plain-sm-weight)] leading-[var(--typo-label-plain-sm-leading)] tracking-[var(--typo-label-plain-sm-tracking)]',\n },\n },\n defaultVariants: {\n variant: 'body-md',\n },\n})\n\ntype TextVariant = NonNullable<VariantProps<typeof textVariants>['variant']>\n\nconst defaultElementMap: Record<TextVariant, keyof React.JSX.IntrinsicElements> = {\n 'heading-2xl': 'h1',\n 'heading-xl': 'h2',\n 'heading-lg': 'h3',\n 'heading-md': 'h4',\n 'heading-sm': 'h5',\n 'heading-xs': 'h6',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n 'body-xs': 'p',\n 'label-lg': 'span',\n 'label-md': 'span',\n 'label-sm': 'span',\n 'label-xs': 'span',\n caption: 'span',\n overline: 'span',\n code: 'code',\n 'label-plain-lg': 'span',\n 'label-plain-md': 'span',\n 'label-plain-sm': 'span',\n}\n\n/**\n * Props for Text — a polymorphic typography component covering the full type scale:\n * headings, body, labels, captions, and overlines — with automatic semantic HTML element selection.\n *\n * **Variants (grouped):**\n * - Headings: `heading-2xl` → `h1`, `heading-xl` → `h2`, `heading-lg` → `h3`, etc.\n * - Body: `body-lg` | `body-md` (default) | `body-sm` | `body-xs` → `<p>`\n * - Labels: `label-lg` | `label-md` | `label-sm` | `label-xs` → `<span>` (uppercase)\n * - Misc: `caption` | `overline` → `<span>` (overline is uppercase)\n *\n * **`as` prop:** Override the rendered element. E.g. `<Text variant=\"heading-lg\" as=\"div\">` renders\n * `<div class=\"text-heading-lg ...\">` — useful when you need heading styles on a non-heading element.\n *\n * @example\n * // Page heading:\n * <Text variant=\"heading-2xl\">Welcome to Shilp Sutra</Text>\n *\n * @example\n * // Section label (uppercase, small):\n * <Text variant=\"label-sm\" className=\"text-surface-fg-muted\">Recent activity</Text>\n *\n * @example\n * // Body copy with custom element (renders as <span> for inline use):\n * <Text variant=\"body-sm\" as=\"span\" className=\"text-surface-fg-muted\">\n * Last updated 3 hours ago\n * </Text>\n *\n * @example\n * // Caption under an image or figure:\n * <Text variant=\"caption\" className=\"text-surface-fg-subtle\">Figure 1: System architecture</Text>\n * // These are just a few ways — feel free to combine props creatively!\n */\ntype TextProps<T extends React.ElementType = 'p'> = {\n variant?: TextVariant\n as?: T\n className?: string\n children?: React.ReactNode\n} & Omit<React.ComponentPropsWithRef<T>, 'as' | 'variant' | 'className' | 'children'>\n\n// Polymorphic component type — preserves T across the call site so element-\n// specific props (e.g. `htmlFor` on `as=\"label\"`, `href` on `as=\"a\"`) typecheck.\n// React.forwardRef can't keep a generic parameter live at its export, so we\n// type the implementation loosely and cast the public export to a generic\n// callable. Standard polymorphic-component pattern (same shape as Radix).\ntype TextComponent = (<T extends React.ElementType = 'p'>(\n props: TextProps<T> & { ref?: React.ComponentPropsWithRef<T>['ref'] },\n) => React.ReactElement | null) & { displayName?: string }\n\nconst TextImpl = React.forwardRef<HTMLElement, TextProps>(\n ({ variant = 'body-md', as, className, children, ...props }, ref) => {\n const Component = as || defaultElementMap[variant] || 'p'\n return React.createElement(\n Component,\n { ref, className: cn(textVariants({ variant }), className), ...props },\n children,\n )\n },\n)\nTextImpl.displayName = 'Text'\n\nconst Text = TextImpl as unknown as TextComponent\n\nexport { Text, type TextProps, type TextVariant,textVariants }\n"],"mappings":";;;;AAMA,IAAM,IAAe,EAAI,aAAa;CACpC,UAAU,EACR,SAAS;EACP,eAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,WAAe;EACf,WAAe;EACf,WAAe;EACf,WAAe;EACf,YAAe;EACf,YAAe;EACf,YAAe;EACf,YAAe;EACf,SAAe;EACf,UAAe;EACf,MAAmB;EACnB,kBAAmB;EACnB,kBAAmB;EACnB,kBAAmB;
|
|
1
|
+
{"version":3,"file":"text.js","names":[],"sources":["../../src/ui/text.tsx"],"sourcesContent":["// @server-safe\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\n\nconst textVariants = cva('font-sans', {\n variants: {\n variant: {\n 'heading-2xl': 'font-display text-[length:var(--typo-heading-2xl-size)] font-[number:var(--typo-heading-2xl-weight)] leading-[var(--typo-heading-2xl-leading)] tracking-[var(--typo-heading-2xl-tracking)]',\n 'heading-xl': 'font-display text-[length:var(--typo-heading-xl-size)] font-[number:var(--typo-heading-xl-weight)] leading-[var(--typo-heading-xl-leading)] tracking-[var(--typo-heading-xl-tracking)]',\n 'heading-lg': 'font-display text-[length:var(--typo-heading-lg-size)] font-[number:var(--typo-heading-lg-weight)] leading-[var(--typo-heading-lg-leading)] tracking-[var(--typo-heading-lg-tracking)]',\n 'heading-md': 'font-display text-[length:var(--typo-heading-md-size)] font-[number:var(--typo-heading-md-weight)] leading-[var(--typo-heading-md-leading)] tracking-[var(--typo-heading-md-tracking)]',\n 'heading-sm': 'font-display text-[length:var(--typo-heading-sm-size)] font-[number:var(--typo-heading-sm-weight)] leading-[var(--typo-heading-sm-leading)] tracking-[var(--typo-heading-sm-tracking)]',\n 'heading-xs': 'font-display text-[length:var(--typo-heading-xs-size)] font-[number:var(--typo-heading-xs-weight)] leading-[var(--typo-heading-xs-leading)] tracking-[var(--typo-heading-xs-tracking)]',\n 'body-lg': 'text-[length:var(--typo-body-lg-size)] font-[number:var(--typo-body-lg-weight)] leading-[var(--typo-body-lg-leading)] tracking-[var(--typo-body-lg-tracking)]',\n 'body-md': 'text-[length:var(--typo-body-md-size)] font-[number:var(--typo-body-md-weight)] leading-[var(--typo-body-md-leading)] tracking-[var(--typo-body-md-tracking)]',\n 'body-sm': 'text-[length:var(--typo-body-sm-size)] font-[number:var(--typo-body-sm-weight)] leading-[var(--typo-body-sm-leading)] tracking-[var(--typo-body-sm-tracking)]',\n 'body-xs': 'text-[length:var(--typo-body-xs-size)] font-[number:var(--typo-body-xs-weight)] leading-[var(--typo-body-xs-leading)] tracking-[var(--typo-body-xs-tracking)]',\n 'label-lg': 'text-[length:var(--typo-label-lg-size)] font-[number:var(--typo-label-lg-weight)] leading-[var(--typo-label-lg-leading)] tracking-[var(--typo-label-lg-tracking)] uppercase',\n 'label-md': 'text-[length:var(--typo-label-md-size)] font-[number:var(--typo-label-md-weight)] leading-[var(--typo-label-md-leading)] tracking-[var(--typo-label-md-tracking)] uppercase',\n 'label-sm': 'text-[length:var(--typo-label-sm-size)] font-[number:var(--typo-label-sm-weight)] leading-[var(--typo-label-sm-leading)] tracking-[var(--typo-label-sm-tracking)] uppercase',\n 'label-xs': 'text-[length:var(--typo-label-xs-size)] font-[number:var(--typo-label-xs-weight)] leading-[var(--typo-label-xs-leading)] tracking-[var(--typo-label-xs-tracking)] uppercase',\n caption: 'text-[length:var(--typo-caption-size)] font-[number:var(--typo-caption-weight)] leading-[var(--typo-caption-leading)] tracking-[var(--typo-caption-tracking)]',\n overline: 'text-[length:var(--typo-overline-size)] font-[number:var(--typo-overline-weight)] leading-[var(--typo-overline-leading)] tracking-[var(--typo-overline-tracking)] uppercase',\n code: 'text-[length:var(--typo-code-size)] font-[number:var(--typo-code-weight)] leading-[var(--typo-code-leading)] tracking-[var(--typo-code-tracking)] font-mono',\n 'label-plain-lg': 'text-[length:var(--typo-label-plain-lg-size)] font-[number:var(--typo-label-plain-lg-weight)] leading-[var(--typo-label-plain-lg-leading)] tracking-[var(--typo-label-plain-lg-tracking)]',\n 'label-plain-md': 'text-[length:var(--typo-label-plain-md-size)] font-[number:var(--typo-label-plain-md-weight)] leading-[var(--typo-label-plain-md-leading)] tracking-[var(--typo-label-plain-md-tracking)]',\n 'label-plain-sm': 'text-[length:var(--typo-label-plain-sm-size)] font-[number:var(--typo-label-plain-sm-weight)] leading-[var(--typo-label-plain-sm-leading)] tracking-[var(--typo-label-plain-sm-tracking)]',\n },\n },\n defaultVariants: {\n variant: 'body-md',\n },\n})\n\ntype TextVariant = NonNullable<VariantProps<typeof textVariants>['variant']>\n\nconst defaultElementMap: Record<TextVariant, keyof React.JSX.IntrinsicElements> = {\n 'heading-2xl': 'h1',\n 'heading-xl': 'h2',\n 'heading-lg': 'h3',\n 'heading-md': 'h4',\n 'heading-sm': 'h5',\n 'heading-xs': 'h6',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n 'body-xs': 'p',\n 'label-lg': 'span',\n 'label-md': 'span',\n 'label-sm': 'span',\n 'label-xs': 'span',\n caption: 'span',\n overline: 'span',\n code: 'code',\n 'label-plain-lg': 'span',\n 'label-plain-md': 'span',\n 'label-plain-sm': 'span',\n}\n\n/**\n * Props for Text — a polymorphic typography component covering the full type scale:\n * headings, body, labels, captions, and overlines — with automatic semantic HTML element selection.\n *\n * **Variants (grouped):**\n * - Headings: `heading-2xl` → `h1`, `heading-xl` → `h2`, `heading-lg` → `h3`, etc.\n * - Body: `body-lg` | `body-md` (default) | `body-sm` | `body-xs` → `<p>`\n * - Labels: `label-lg` | `label-md` | `label-sm` | `label-xs` → `<span>` (uppercase)\n * - Misc: `caption` | `overline` → `<span>` (overline is uppercase)\n *\n * **`as` prop:** Override the rendered element. E.g. `<Text variant=\"heading-lg\" as=\"div\">` renders\n * `<div class=\"text-heading-lg ...\">` — useful when you need heading styles on a non-heading element.\n *\n * @example\n * // Page heading:\n * <Text variant=\"heading-2xl\">Welcome to Shilp Sutra</Text>\n *\n * @example\n * // Section label (uppercase, small):\n * <Text variant=\"label-sm\" className=\"text-surface-fg-muted\">Recent activity</Text>\n *\n * @example\n * // Body copy with custom element (renders as <span> for inline use):\n * <Text variant=\"body-sm\" as=\"span\" className=\"text-surface-fg-muted\">\n * Last updated 3 hours ago\n * </Text>\n *\n * @example\n * // Caption under an image or figure:\n * <Text variant=\"caption\" className=\"text-surface-fg-subtle\">Figure 1: System architecture</Text>\n * // These are just a few ways — feel free to combine props creatively!\n */\ntype TextProps<T extends React.ElementType = 'p'> = {\n variant?: TextVariant\n as?: T\n className?: string\n children?: React.ReactNode\n} & Omit<React.ComponentPropsWithRef<T>, 'as' | 'variant' | 'className' | 'children'>\n\n// Polymorphic component type — preserves T across the call site so element-\n// specific props (e.g. `htmlFor` on `as=\"label\"`, `href` on `as=\"a\"`) typecheck.\n// React.forwardRef can't keep a generic parameter live at its export, so we\n// type the implementation loosely and cast the public export to a generic\n// callable. Standard polymorphic-component pattern (same shape as Radix).\ntype TextComponent = (<T extends React.ElementType = 'p'>(\n props: TextProps<T> & { ref?: React.ComponentPropsWithRef<T>['ref'] },\n) => React.ReactElement | null) & { displayName?: string }\n\nconst TextImpl = React.forwardRef<HTMLElement, TextProps>(\n ({ variant = 'body-md', as, className, children, ...props }, ref) => {\n const Component = as || defaultElementMap[variant] || 'p'\n return React.createElement(\n Component,\n { ref, className: cn(textVariants({ variant }), className), ...props },\n children,\n )\n },\n)\nTextImpl.displayName = 'Text'\n\nconst Text = TextImpl as unknown as TextComponent\n\nexport { Text, type TextProps, type TextVariant,textVariants }\n"],"mappings":";;;;AAMA,IAAM,IAAe,EAAI,aAAa;CACpC,UAAU,EACR,SAAS;EACP,eAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,WAAe;EACf,WAAe;EACf,WAAe;EACf,WAAe;EACf,YAAe;EACf,YAAe;EACf,YAAe;EACf,YAAe;EACf,SAAe;EACf,UAAe;EACf,MAAmB;EACnB,kBAAmB;EACnB,kBAAmB;EACnB,kBAAmB;CACrB,EACF;CACA,iBAAiB,EACf,SAAS,UACX;AACF,CAAC,GAIK,IAA4E;CAChF,eAAe;CACf,cAAe;CACf,cAAe;CACf,cAAe;CACf,cAAe;CACf,cAAe;CACf,WAAe;CACf,WAAe;CACf,WAAe;CACf,WAAe;CACf,YAAe;CACf,YAAe;CACf,YAAe;CACf,YAAe;CACf,SAAe;CACf,UAAe;CACf,MAAmB;CACnB,kBAAmB;CACnB,kBAAmB;CACnB,kBAAmB;AACrB,GAkDM,IAAW,EAAM,YACpB,EAAE,aAAU,WAAW,OAAI,cAAW,aAAU,GAAG,KAAS,MAAQ;CACnE,IAAM,IAAY,KAAM,EAAkB,MAAY;CACtD,OAAO,EAAM,cACX,GACA;EAAE;EAAK,WAAW,EAAG,EAAa,EAAE,WAAQ,CAAC,GAAG,CAAS;EAAG,GAAG;CAAM,GACrE,CACF;AACF,CACF;AACA,EAAS,cAAc;AAEvB,IAAM,IAAO"}
|
package/dist/ui/textarea.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textarea.js","names":[],"sources":["../../src/ui/textarea.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { motionProps } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst textareaVariants = cva(\n [\n 'flex w-full font-sans resize-y',\n 'bg-surface-raised-hover text-surface-fg',\n 'border border-surface-border-strong rounded-control',\n 'placeholder:text-surface-fg-subtle',\n 'hover:bg-surface-raised-active',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:border-accent-7',\n 'disabled:cursor-not-allowed disabled:opacity-action-disabled',\n 'read-only:bg-surface-raised read-only:cursor-default',\n ],\n {\n variants: {\n // Min-height ramp 48/60/80/120: 48/80 map to spacing tokens (ds-09/ds-11);\n // 60/120 have no exact token so stay arbitrary (allowed). Rendered sizes unchanged.\n size: {\n xs: 'min-h-ds-09 text-ds-sm px-ds-02 py-ds-02',\n sm: 'min-h-[60px] text-ds-sm px-ds-03 py-ds-02',\n md: 'min-h-ds-11 text-ds-md px-ds-04 py-ds-03',\n lg: 'min-h-[120px] text-ds-md px-ds-05 py-ds-04',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\n/**\n * Props for Textarea — a resizable multi-line text input with size variants and validation state coloring,\n * sharing the same `FieldState` type as every form control.\n *\n * **Sizes:** `sm` (min 60px) | `md` (min 80px, default) | `lg` (min 120px) — all are vertically resizable.\n *\n * **Validation states:** `state=\"error\"` | `\"warning\"` | `\"success\"` — changes border color and focus ring.\n * HTML's native `size` attribute is omitted; use `rows` for initial height or CSS `min-height`.\n *\n * **Pair with FormField:** Use inside `<FormField>` to get a label, helper text, and associated `state`.\n *\n * @example\n * // Basic description textarea in a form:\n * <Textarea placeholder=\"Describe the issue...\" rows={4} />\n *\n * @example\n * // Error state when validation fails:\n * <Textarea state=\"error\" value={bio} onChange={(e) => setBio(e.target.value)} />\n *\n * @example\n * // Large textarea for a full email draft composer:\n * <Textarea size=\"lg\" placeholder=\"Write your message...\" className=\"min-h-[200px]\" />\n *\n * @example\n * // Read-only view of a previously submitted note:\n * <Textarea readOnly value={submission.notes} size=\"md\" />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface TextareaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>,\n VariantProps<typeof textareaVariants> {\n state?: FieldState\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Merge FormField context — explicit props always win (shared precedence)\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <motion.textarea\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n className={cn(\n textareaVariants({ size }),\n state === 'error' && 'border-error-7 focus-visible:ring-error-7',\n state === 'warning' && 'border-warning-7 focus-visible:ring-warning-7',\n state === 'success' && 'border-success-7 focus-visible:ring-success-7',\n className,\n )}\n ref={ref}\n {...motionProps(props)}\n />\n )\n },\n)\nTextarea.displayName = 'Textarea'\n\nexport { Textarea, textareaVariants }\n"],"mappings":";;;;;;;;;;AAWA,IAAM,IAAmB,EACvB;CACE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;
|
|
1
|
+
{"version":3,"file":"textarea.js","names":[],"sources":["../../src/ui/textarea.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { motionProps } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst textareaVariants = cva(\n [\n 'flex w-full font-sans resize-y',\n 'bg-surface-raised-hover text-surface-fg',\n 'border border-surface-border-strong rounded-control',\n 'placeholder:text-surface-fg-subtle',\n 'hover:bg-surface-raised-active',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 focus-visible:border-accent-7',\n 'disabled:cursor-not-allowed disabled:opacity-action-disabled',\n 'read-only:bg-surface-raised read-only:cursor-default',\n ],\n {\n variants: {\n // Min-height ramp 48/60/80/120: 48/80 map to spacing tokens (ds-09/ds-11);\n // 60/120 have no exact token so stay arbitrary (allowed). Rendered sizes unchanged.\n size: {\n xs: 'min-h-ds-09 text-ds-sm px-ds-02 py-ds-02',\n sm: 'min-h-[60px] text-ds-sm px-ds-03 py-ds-02',\n md: 'min-h-ds-11 text-ds-md px-ds-04 py-ds-03',\n lg: 'min-h-[120px] text-ds-md px-ds-05 py-ds-04',\n },\n },\n defaultVariants: { size: 'md' },\n },\n)\n\n/**\n * Props for Textarea — a resizable multi-line text input with size variants and validation state coloring,\n * sharing the same `FieldState` type as every form control.\n *\n * **Sizes:** `sm` (min 60px) | `md` (min 80px, default) | `lg` (min 120px) — all are vertically resizable.\n *\n * **Validation states:** `state=\"error\"` | `\"warning\"` | `\"success\"` — changes border color and focus ring.\n * HTML's native `size` attribute is omitted; use `rows` for initial height or CSS `min-height`.\n *\n * **Pair with FormField:** Use inside `<FormField>` to get a label, helper text, and associated `state`.\n *\n * @example\n * // Basic description textarea in a form:\n * <Textarea placeholder=\"Describe the issue...\" rows={4} />\n *\n * @example\n * // Error state when validation fails:\n * <Textarea state=\"error\" value={bio} onChange={(e) => setBio(e.target.value)} />\n *\n * @example\n * // Large textarea for a full email draft composer:\n * <Textarea size=\"lg\" placeholder=\"Write your message...\" className=\"min-h-[200px]\" />\n *\n * @example\n * // Read-only view of a previously submitted note:\n * <Textarea readOnly value={submission.notes} size=\"md\" />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface TextareaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>,\n VariantProps<typeof textareaVariants> {\n state?: FieldState\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, state: stateProp, size, ...props }, ref) => {\n const fieldCtx = useFormField()\n // Merge FormField context — explicit props always win (shared precedence)\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n return (\n <motion.textarea\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n className={cn(\n textareaVariants({ size }),\n state === 'error' && 'border-error-7 focus-visible:ring-error-7',\n state === 'warning' && 'border-warning-7 focus-visible:ring-warning-7',\n state === 'success' && 'border-success-7 focus-visible:ring-success-7',\n className,\n )}\n ref={ref}\n {...motionProps(props)}\n />\n )\n },\n)\nTextarea.displayName = 'Textarea'\n\nexport { Textarea, textareaVariants }\n"],"mappings":";;;;;;;;;;AAWA,IAAM,IAAmB,EACvB;CACE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,GACA;CACE,UAAU,EAGR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF,GAoCM,IAAW,EAAM,YACpB,EAAE,cAAW,OAAO,GAAW,SAAM,GAAG,KAAS,MAAQ;CACxD,IAAM,IAAW,EAAa,GAExB,IAAQ,EAAkB,GAAW,EAAS,KAAK,GACnD,IAAkB,EAAM,uBAAuB,EAAS,cACxD,IAAe,EAAM,oBAAoB,EAAS;CAExD,OACE,kBAAC,EAAO,UAAR;EACE,gBAAc,MAAU,WAAW,KAAA;EACnC,oBAAkB;EAClB,iBAAe,KAAgB,KAAA;EAC/B,WAAW,EACT,EAAiB,EAAE,QAAK,CAAC,GACzB,MAAU,WAAW,6CACrB,MAAU,aAAa,iDACvB,MAAU,aAAa,iDACvB,CACF;EACK;EACL,GAAI,EAAY,CAAK;CACtB,CAAA;AAEL,CACF;AACA,EAAS,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast-registry.js","names":[],"sources":["../../src/ui/toast-registry.ts"],"sourcesContent":["declare const process: { env?: { NODE_ENV?: string } } | undefined\n\nlet mountedToasters = 0\nlet warned = false\n\nexport function registerToaster(): () => void {\n mountedToasters += 1\n return () => {\n mountedToasters = Math.max(0, mountedToasters - 1)\n }\n}\n\n/**\n * Called by toast() in dev to warn once if no <Toaster /> is mounted.\n * Silent on missing Toaster so we never crash the UI — only a console warning.\n */\nexport function assertToasterMounted(): void {\n const env = typeof process !== 'undefined' ? process.env?.NODE_ENV : undefined\n if (env === 'production') return\n if (warned) return\n if (mountedToasters > 0) return\n warned = true\n \n console.warn(\n '[shilp-sutra] toast() was called but no <Toaster /> is mounted. ' +\n 'Render <Toaster /> once at your app root (e.g. in RootLayout). ' +\n 'Without it, toasts are dropped silently.',\n )\n}\n"],"mappings":";AAEA,IAAI,IAAkB,GAClB,IAAS;AAEb,SAAgB,IAA8B;
|
|
1
|
+
{"version":3,"file":"toast-registry.js","names":[],"sources":["../../src/ui/toast-registry.ts"],"sourcesContent":["declare const process: { env?: { NODE_ENV?: string } } | undefined\n\nlet mountedToasters = 0\nlet warned = false\n\nexport function registerToaster(): () => void {\n mountedToasters += 1\n return () => {\n mountedToasters = Math.max(0, mountedToasters - 1)\n }\n}\n\n/**\n * Called by toast() in dev to warn once if no <Toaster /> is mounted.\n * Silent on missing Toaster so we never crash the UI — only a console warning.\n */\nexport function assertToasterMounted(): void {\n const env = typeof process !== 'undefined' ? process.env?.NODE_ENV : undefined\n if (env === 'production') return\n if (warned) return\n if (mountedToasters > 0) return\n warned = true\n \n console.warn(\n '[shilp-sutra] toast() was called but no <Toaster /> is mounted. ' +\n 'Render <Toaster /> once at your app root (e.g. in RootLayout). ' +\n 'Without it, toasts are dropped silently.',\n )\n}\n"],"mappings":";AAEA,IAAI,IAAkB,GAClB,IAAS;AAEb,SAAgB,IAA8B;CAE5C,OADA,KAAmB,SACN;EACX,IAAkB,KAAK,IAAI,GAAG,IAAkB,CAAC;CACnD;AACF;AAMA,SAAgB,IAA6B;CAE3C,CADY,OAAO,UAAY,MAAA,QAAA,IAAA,WAAsC,KAAA,OACzD,iBACR,KACA,IAAkB,MACtB,IAAS,IAET,QAAQ,KACN,yKAGF;AACF"}
|
package/dist/ui/toast.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ declare function TimerBar({ duration, type, paused, }: {
|
|
|
6
6
|
duration: number;
|
|
7
7
|
type: ToastType;
|
|
8
8
|
paused: boolean;
|
|
9
|
-
}):
|
|
9
|
+
}): React.JSX.Element;
|
|
10
10
|
declare function ToastContent({ type, title, description, action, cancel, duration, showTimerBar, showAccent, selfDismissId, }: {
|
|
11
11
|
type: ToastType;
|
|
12
12
|
title?: React.ReactNode;
|
|
@@ -19,13 +19,13 @@ declare function ToastContent({ type, title, description, action, cancel, durati
|
|
|
19
19
|
showAccent?: boolean;
|
|
20
20
|
/** When set, this component manages its own dismiss timer (hover-aware). */
|
|
21
21
|
selfDismissId?: string | number;
|
|
22
|
-
}):
|
|
22
|
+
}): React.JSX.Element;
|
|
23
23
|
declare function formatFileSize(bytes: number): string;
|
|
24
24
|
declare function UploadFileRow({ file, onRetry, onRemove, }: {
|
|
25
25
|
file: UploadFile;
|
|
26
26
|
onRetry?: (fileId: string) => void;
|
|
27
27
|
onRemove?: (fileId: string) => void;
|
|
28
|
-
}):
|
|
28
|
+
}): React.JSX.Element;
|
|
29
29
|
declare function UploadToastContent({ files, onRetry, onRemove, selfDismissId, showAccent, }: {
|
|
30
30
|
files: UploadFile[];
|
|
31
31
|
onRetry?: (fileId: string) => void;
|
|
@@ -33,7 +33,7 @@ declare function UploadToastContent({ files, onRetry, onRemove, selfDismissId, s
|
|
|
33
33
|
selfDismissId?: string | number;
|
|
34
34
|
/** Render the colored left accent bar. Off by default. */
|
|
35
35
|
showAccent?: boolean;
|
|
36
|
-
}):
|
|
36
|
+
}): React.JSX.Element;
|
|
37
37
|
/**
|
|
38
38
|
* Toast API — imperative toast notifications with Sonner rendering engine.
|
|
39
39
|
*
|
package/dist/ui/toast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../src/ui/toast.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,QAAQ,CAAA;AAQhE,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACX,MAAM,eAAe,CAAA;AAiEtB,iBAAS,QAAQ,CAAC,EAChB,QAAQ,EACR,IAAI,EACJ,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,SAAS,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;CAChB,
|
|
1
|
+
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../src/ui/toast.tsx"],"names":[],"mappings":"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"}
|
package/dist/ui/toast.js
CHANGED
|
@@ -175,7 +175,7 @@ function k({ type: a, title: o, description: u, action: d, cancel: f, duration:
|
|
|
175
175
|
]
|
|
176
176
|
});
|
|
177
177
|
}
|
|
178
|
-
var A = new Set([
|
|
178
|
+
var A = /* @__PURE__ */ new Set([
|
|
179
179
|
"jpg",
|
|
180
180
|
"jpeg",
|
|
181
181
|
"png",
|
|
@@ -528,9 +528,11 @@ z.message = (e, t) => R("message", e, t), z.success = (e, t) => R("success", e,
|
|
|
528
528
|
});
|
|
529
529
|
};
|
|
530
530
|
return e.then((e) => {
|
|
531
|
-
|
|
531
|
+
let n = typeof t.success == "function" ? t.success(e) : t.success;
|
|
532
|
+
r("success", n);
|
|
532
533
|
}).catch((e) => {
|
|
533
|
-
|
|
534
|
+
let n = typeof t.error == "function" ? t.error(e) : t.error;
|
|
535
|
+
r("error", n);
|
|
534
536
|
}), n;
|
|
535
537
|
}, z.undo = (e, t) => {
|
|
536
538
|
let n = t.duration ?? T;
|
package/dist/ui/toast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.js","names":[],"sources":["../../src/ui/toast.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlertCircle,\n IconAlertTriangle,\n IconCheck,\n IconCircleCheck,\n IconCircleX,\n IconFile,\n IconInfoCircle,\n IconPhoto,\n IconRefresh,\n IconUpload,\n IconX,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\nimport { type ExternalToast,toast as sonnerToast } from 'sonner'\n\nimport { Icon } from './icon'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { Progress } from './progress'\nimport { Spinner } from './spinner'\nimport { assertToasterMounted } from './toast-registry'\nimport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n\n/* ---------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------ */\n\nconst DEFAULT_DURATION = 5000\nconst UNDO_DURATION = 8000\nconst UPLOAD_COMPLETE_DELAY = 3000\n\n/* ---------------------------------------------------------------------------\n * Accent bar & icon config per toast type\n * ------------------------------------------------------------------------ */\n\nconst TOAST_TYPE_CONFIG: Record<\n ToastType,\n {\n accentClass: string\n iconClass: string\n icon: React.ForwardRefExoticComponent<any> | null\n timerBarClass: string\n }\n> = {\n message: {\n accentClass: '',\n iconClass: '',\n icon: null,\n timerBarClass: 'bg-surface-border',\n },\n success: {\n accentClass: 'bg-success-9',\n iconClass: 'text-success-11',\n icon: IconCircleCheck,\n timerBarClass: 'bg-success-9',\n },\n error: {\n accentClass: 'bg-error-9',\n iconClass: 'text-error-11',\n icon: IconCircleX,\n timerBarClass: 'bg-error-9',\n },\n warning: {\n accentClass: 'bg-warning-9',\n iconClass: 'text-warning-11',\n icon: IconAlertTriangle,\n timerBarClass: 'bg-warning-9',\n },\n info: {\n accentClass: 'bg-info-9',\n iconClass: 'text-info-11',\n icon: IconInfoCircle,\n timerBarClass: 'bg-info-9',\n },\n loading: {\n accentClass: 'bg-accent-9',\n iconClass: 'text-accent-11',\n icon: null,\n timerBarClass: 'bg-accent-9',\n },\n}\n\n/* ---------------------------------------------------------------------------\n * Timer bar — shows auto-dismiss countdown\n * ------------------------------------------------------------------------ */\n\nfunction TimerBar({\n duration,\n type,\n paused,\n}: {\n duration: number\n type: ToastType\n paused: boolean\n}) {\n const config = TOAST_TYPE_CONFIG[type]\n return (\n <div className=\"absolute bottom-0 left-0 right-0 h-ds-01 overflow-hidden\">\n <div\n className={cn(\n 'h-full w-full origin-left opacity-30',\n config.timerBarClass,\n 'motion-safe:animate-timer-bar',\n )}\n style={{\n animationDuration: `${duration}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }}\n />\n </div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Base toast UI — used by all typed toasts\n * ------------------------------------------------------------------------ */\n\nfunction ToastContent({\n type,\n title,\n description,\n action,\n cancel,\n duration = DEFAULT_DURATION,\n showTimerBar = true,\n showAccent = false,\n selfDismissId,\n}: {\n type: ToastType\n title?: React.ReactNode\n description?: React.ReactNode\n action?: ToastActionOptions\n cancel?: ToastActionOptions\n duration?: number\n showTimerBar?: boolean\n /** Render the colored left accent bar. Off by default (status = icon + timer bar). */\n showAccent?: boolean\n /** When set, this component manages its own dismiss timer (hover-aware). */\n selfDismissId?: string | number\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const config = TOAST_TYPE_CONFIG[type]\n const StatusIcon = config.icon\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(duration)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || duration === Infinity) return\n if (paused) {\n // Pause: save remaining time\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n // Start/resume timer\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, duration, paused])\n\n // Error toasts announce assertively so screen readers interrupt;\n // warnings stay polite but atomically re-read the message.\n const isUrgent = type === 'error'\n return (\n <motion.div\n layout=\"position\"\n role={isUrgent ? 'alert' : 'status'}\n aria-live={isUrgent ? 'assertive' : 'polite'}\n aria-atomic=\"true\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint on error only — the one you must not miss.\n type === 'error' && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent (status is carried by icon + timer bar) */}\n {showAccent && config.accentClass && (\n <div\n className={cn('w-1 shrink-0 rounded-l-overlay-sm', config.accentClass)}\n />\n )}\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 items-start gap-ds-03 p-ds-04\">\n {/* Status icon — animates between spinner ↔ typed icon */}\n <AnimatePresence mode=\"wait\">\n {type === 'loading' ? (\n <motion.div\n key=\"loading\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n className=\"mt-0.5 shrink-0\"\n >\n <Spinner size=\"sm\" className=\"h-4 w-4\" />\n </motion.div>\n ) : StatusIcon ? (\n <motion.div\n key={type}\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n className=\"mt-0.5 shrink-0\"\n >\n <Icon icon={StatusIcon} size=\"sm\" className={config.iconClass} />\n </motion.div>\n ) : null}\n </AnimatePresence>\n\n {/* Text */}\n <div className=\"min-w-0 flex-1\">\n {title && (\n <p className=\"text-ds-md font-semibold text-surface-fg\">\n {title}\n </p>\n )}\n {description && (\n <p\n className={cn(\n 'text-ds-sm text-surface-fg-muted',\n title && 'mt-0.5',\n )}\n >\n {description}\n </p>\n )}\n\n {/* Action / cancel buttons */}\n {(action || cancel) && (\n <div className=\"mt-ds-03 flex items-center gap-ds-03\">\n {action && (\n <button\n type=\"button\"\n onClick={action.onClick}\n className=\"text-ds-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-raised-hover rounded-control-inner px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {action.label}\n </button>\n )}\n {cancel && (\n <button\n type=\"button\"\n onClick={cancel.onClick}\n className=\"text-ds-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {cancel.label}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Timer bar */}\n {showTimerBar && type !== 'loading' && (\n <TimerBar duration={duration} type={type} paused={paused} />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Upload toast UI\n * ------------------------------------------------------------------------ */\n\nconst IMAGE_EXTENSIONS = new Set([\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif',\n])\n\nfunction isImageFile(file: UploadFile): boolean {\n if (file.previewUrl) return true\n const ext = file.name.split('.').pop()?.toLowerCase() ?? ''\n return IMAGE_EXTENSIONS.has(ext)\n}\n\nfunction getProgressColor(\n status: UploadFile['status'],\n): 'accent' | 'success' | 'error' {\n switch (status) {\n case 'complete':\n return 'success'\n case 'error':\n return 'error'\n default:\n return 'accent'\n }\n}\n\nfunction getProgressValue(file: UploadFile): number | undefined {\n switch (file.status) {\n case 'pending':\n return 0\n case 'uploading':\n return file.progress ?? 0\n case 'processing':\n return undefined\n case 'complete':\n return 100\n case 'error':\n return file.progress ?? 0\n }\n}\n\nfunction formatFileSize(bytes: number): string {\n if (!bytes || bytes < 0 || !Number.isFinite(bytes)) return '0 B'\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`\n}\n\nfunction UploadFileRow({\n file,\n onRetry,\n onRemove,\n}: {\n file: UploadFile\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n}) {\n const isTerminal = file.status === 'complete' || file.status === 'error'\n const isUploading =\n file.status === 'uploading' || file.status === 'processing'\n\n return (\n <motion.div\n layout\n className=\"flex items-center gap-ds-02 py-1\"\n initial={false}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, x: -20, height: 0 }}\n transition={springs.snappy}\n >\n {/* File icon — animates between states */}\n <div className=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <AnimatePresence mode=\"wait\">\n {file.status === 'complete' ? (\n <motion.div\n key=\"complete\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"xs\" className=\"text-success-11\" />\n </motion.div>\n ) : file.status === 'error' ? (\n <motion.div\n key=\"error\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n >\n <Icon icon={IconAlertCircle} size=\"xs\" className=\"text-error-11\" />\n </motion.div>\n ) : file.status === 'processing' ? (\n <motion.div\n key=\"processing\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Spinner size=\"sm\" className=\"h-3.5 w-3.5\" />\n </motion.div>\n ) : isImageFile(file) ? (\n <motion.div\n key=\"image\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconPhoto} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"file\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconFile} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n\n {/* Filename */}\n <span className=\"min-w-0 flex-1 truncate text-ds-xs text-surface-fg\">\n {file.name}\n </span>\n\n {/* Progress bar or status */}\n {isUploading ? (\n <div className=\"flex w-16 items-center gap-1\">\n <Progress\n size=\"sm\"\n color={getProgressColor(file.status)}\n value={getProgressValue(file)}\n className=\"flex-1\"\n />\n {file.progress !== undefined && (\n <span className=\"shrink-0 text-ds-xs tabular-nums text-surface-fg-muted\">\n {file.progress}%\n </span>\n )}\n </div>\n ) : file.status === 'complete' ? (\n <span className=\"text-ds-xs text-success-11\">Done</span>\n ) : file.status === 'error' ? (\n <span className=\"max-w-[60px] truncate text-ds-xs text-error-11\">\n {file.error || 'Failed'}\n </span>\n ) : (\n <span className=\"text-ds-xs text-surface-fg-muted\">\n {formatFileSize(file.size)}\n </span>\n )}\n\n {/* Actions */}\n {file.status === 'error' && onRetry && (\n <button\n type=\"button\"\n onClick={() => onRetry(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Retry ${file.name}`}\n title=\"Retry\"\n >\n <IconRefresh className=\"h-3.5 w-3.5\" />\n </button>\n )}\n {!isTerminal && onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Cancel ${file.name}`}\n title=\"Remove\"\n >\n <IconX className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </motion.div>\n )\n}\n\nfunction UploadToastContent({\n files,\n onRetry,\n onRemove,\n selfDismissId,\n showAccent = false,\n}: {\n files: UploadFile[]\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n selfDismissId?: string | number\n /** Render the colored left accent bar. Off by default. */\n showAccent?: boolean\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const completeCount = files.filter((f) => f.status === 'complete').length\n const errorCount = files.filter((f) => f.status === 'error').length\n const allTerminal = files.every(\n (f) => f.status === 'complete' || f.status === 'error',\n )\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(UPLOAD_COMPLETE_DELAY)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || !allTerminal) return\n if (paused) {\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, allTerminal, paused])\n\n const accentClass = allTerminal\n ? errorCount > 0\n ? 'bg-error-7'\n : 'bg-success-7'\n : 'bg-accent-9'\n\n const timerBarType: ToastType = allTerminal\n ? errorCount > 0\n ? 'error'\n : 'success'\n : 'loading'\n\n const uploadUrgent = allTerminal && errorCount > 0\n return (\n <motion.div\n layout\n role={uploadUrgent ? 'alert' : 'status'}\n aria-live={uploadUrgent ? 'assertive' : 'polite'}\n aria-label=\"File uploads\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint when the batch ended with failures.\n uploadUrgent && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent */}\n {showAccent && (\n <div className={cn('w-1 shrink-0 rounded-l-overlay-sm', accentClass)} />\n )}\n\n {/* Content */}\n <div className=\"min-w-0 flex-1 p-ds-04\">\n {/* Header — icon morphs between upload ↔ result */}\n <div className=\"flex items-center gap-ds-02\">\n <AnimatePresence mode=\"wait\">\n {allTerminal ? (\n errorCount > 0 ? (\n <motion.div\n key=\"error-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconAlertCircle} size=\"sm\" className=\"shrink-0 text-error-11\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"success-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"shrink-0 text-success-11\" />\n </motion.div>\n )\n ) : (\n <motion.div\n key=\"upload-icon\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconUpload} size=\"sm\" className=\"shrink-0 text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n <p className=\"text-ds-md font-semibold text-surface-fg\">\n {allTerminal\n ? errorCount > 0\n ? `${completeCount} of ${files.length} uploaded`\n : `${files.length} file${files.length > 1 ? 's' : ''} uploaded`\n : `Uploading ${files.length} file${files.length > 1 ? 's' : ''}`}\n </p>\n </div>\n {!allTerminal && (\n <p className=\"mt-0.5 text-ds-xs text-surface-fg-muted\">\n {completeCount} of {files.length} complete\n {errorCount > 0 && ` · ${errorCount} failed`}\n </p>\n )}\n\n {/* File list */}\n <div className=\"mt-ds-02 max-h-[140px] overflow-y-auto\">\n <AnimatePresence>\n {files.map((file) => (\n <UploadFileRow\n key={file.id}\n file={file}\n onRetry={onRetry}\n onRemove={onRemove}\n />\n ))}\n </AnimatePresence>\n </div>\n\n {/* Screen reader announcement */}\n <div className=\"sr-only\" aria-live=\"polite\">\n {completeCount} of {files.length} files uploaded\n {errorCount > 0 && `, ${errorCount} failed`}\n </div>\n </div>\n\n {/* Timer bar — only after all terminal */}\n {allTerminal && (\n <TimerBar\n duration={UPLOAD_COMPLETE_DELAY}\n type={timerBarType}\n paused={false}\n />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Toast API — wraps Sonner with our styled UI\n * ------------------------------------------------------------------------ */\n\nfunction createSonnerOptions(\n options?: ToastOptions,\n duration?: number,\n): ExternalToast {\n return {\n id: options?.id,\n duration: options?.duration ?? duration ?? DEFAULT_DURATION,\n unstyled: true,\n classNames: { toast: 'w-full' },\n }\n}\n\nfunction createTypedToast(\n type: ToastType,\n message: string | React.ReactNode,\n options?: ToastOptions,\n) {\n assertToasterMounted()\n const duration = options?.duration ?? DEFAULT_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n description={options?.description}\n action={options?.action}\n cancel={options?.cancel}\n duration={duration}\n showAccent={options?.showAccent}\n />\n ),\n createSonnerOptions(options, duration),\n )\n}\n\n/**\n * Toast API — imperative toast notifications with Sonner rendering engine.\n *\n * Mount `<Toaster />` once at your root layout. Then call `toast()` from anywhere.\n *\n * @example\n * toast('Something happened')\n * toast.success('Saved!')\n * toast.error('Failed to save')\n * toast.promise(saveFn(), { loading: 'Saving...', success: 'Done!', error: 'Failed' })\n * toast.undo('Deleted', { onUndo: () => restore() })\n * toast.upload({ files, onRetry, onRemove })\n */\nfunction toast(message: string | React.ReactNode, options?: ToastOptions) {\n return createTypedToast('message', message, options)\n}\n\ntoast.message = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('message', message, options)\n}\n\ntoast.success = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('success', message, options)\n}\n\ntoast.error = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('error', message, options)\n}\n\ntoast.warning = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('warning', message, options)\n}\n\ntoast.info = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('info', message, options)\n}\n\ntoast.loading = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('loading', message, {\n ...options,\n duration: options?.duration ?? Infinity,\n })\n}\n\ntoast.promise = <T,>(\n promise: Promise<T>,\n options: {\n loading: string | React.ReactNode\n success: string | React.ReactNode | ((data: T) => string | React.ReactNode)\n error:\n | string\n | React.ReactNode\n | ((error: unknown) => string | React.ReactNode)\n },\n) => {\n const id = sonnerToast.custom(\n () => (\n <ToastContent type=\"loading\" title={options.loading} showTimerBar={false} />\n ),\n { duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n\n const replaceWith = (type: ToastType, message: React.ReactNode) => {\n // Sonner doesn't reset its auto-dismiss timer when updating a custom toast\n // in-place from duration: Infinity, so ToastContent manages its own\n // hover-aware dismiss timer via selfDismissId.\n sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n duration={DEFAULT_DURATION}\n selfDismissId={id}\n />\n ),\n { id, duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n }\n\n promise\n .then((data) => {\n const message =\n typeof options.success === 'function'\n ? options.success(data)\n : options.success\n replaceWith('success', message)\n })\n .catch((error) => {\n const message =\n typeof options.error === 'function'\n ? options.error(error)\n : options.error\n replaceWith('error', message)\n })\n\n return id\n}\n\ntoast.undo = (message: string | React.ReactNode, options: ToastUndoOptions) => {\n const duration = options.duration ?? UNDO_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type=\"message\"\n title={message}\n description={options.description}\n action={{ label: 'Undo', onClick: options.onUndo }}\n duration={duration}\n />\n ),\n { duration, unstyled: true, classNames: { toast: 'w-full' } },\n )\n}\n\ntoast.upload = (options: ToastUploadOptions) => {\n // Pre-determine the id so we can pass it to UploadToastContent for self-dismiss.\n const toastId = options.id ?? String(Date.now())\n\n sonnerToast.custom(\n () => (\n <UploadToastContent\n files={options.files}\n onRetry={options.onRetry}\n onRemove={options.onRemove}\n selfDismissId={toastId}\n showAccent={options.showAccent}\n />\n ),\n {\n id: toastId,\n duration: Infinity,\n unstyled: true,\n classNames: { toast: 'w-full' },\n },\n )\n\n return toastId\n}\n\ntoast.custom = (\n render: (id: string | number) => React.ReactElement,\n options?: ExternalToast,\n) => {\n return sonnerToast.custom(render, {\n ...options,\n unstyled: true,\n classNames: { toast: 'w-full', ...(options?.classNames as Record<string, string>) },\n })\n}\n\ntoast.dismiss = (id?: string | number) => {\n if (id !== undefined) {\n sonnerToast.dismiss(id)\n } else {\n sonnerToast.dismiss()\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------ */\n\nexport type ToastProps = ToastOptions\n\nexport {\n formatFileSize,\n TimerBar,\n toast,\n ToastContent,\n UploadFileRow,\n UploadToastContent,\n}\nexport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"toast.js","names":[],"sources":["../../src/ui/toast.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlertCircle,\n IconAlertTriangle,\n IconCheck,\n IconCircleCheck,\n IconCircleX,\n IconFile,\n IconInfoCircle,\n IconPhoto,\n IconRefresh,\n IconUpload,\n IconX,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\nimport { type ExternalToast,toast as sonnerToast } from 'sonner'\n\nimport { Icon } from './icon'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { Progress } from './progress'\nimport { Spinner } from './spinner'\nimport { assertToasterMounted } from './toast-registry'\nimport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n\n/* ---------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------ */\n\nconst DEFAULT_DURATION = 5000\nconst UNDO_DURATION = 8000\nconst UPLOAD_COMPLETE_DELAY = 3000\n\n/* ---------------------------------------------------------------------------\n * Accent bar & icon config per toast type\n * ------------------------------------------------------------------------ */\n\nconst TOAST_TYPE_CONFIG: Record<\n ToastType,\n {\n accentClass: string\n iconClass: string\n icon: React.ForwardRefExoticComponent<any> | null\n timerBarClass: string\n }\n> = {\n message: {\n accentClass: '',\n iconClass: '',\n icon: null,\n timerBarClass: 'bg-surface-border',\n },\n success: {\n accentClass: 'bg-success-9',\n iconClass: 'text-success-11',\n icon: IconCircleCheck,\n timerBarClass: 'bg-success-9',\n },\n error: {\n accentClass: 'bg-error-9',\n iconClass: 'text-error-11',\n icon: IconCircleX,\n timerBarClass: 'bg-error-9',\n },\n warning: {\n accentClass: 'bg-warning-9',\n iconClass: 'text-warning-11',\n icon: IconAlertTriangle,\n timerBarClass: 'bg-warning-9',\n },\n info: {\n accentClass: 'bg-info-9',\n iconClass: 'text-info-11',\n icon: IconInfoCircle,\n timerBarClass: 'bg-info-9',\n },\n loading: {\n accentClass: 'bg-accent-9',\n iconClass: 'text-accent-11',\n icon: null,\n timerBarClass: 'bg-accent-9',\n },\n}\n\n/* ---------------------------------------------------------------------------\n * Timer bar — shows auto-dismiss countdown\n * ------------------------------------------------------------------------ */\n\nfunction TimerBar({\n duration,\n type,\n paused,\n}: {\n duration: number\n type: ToastType\n paused: boolean\n}) {\n const config = TOAST_TYPE_CONFIG[type]\n return (\n <div className=\"absolute bottom-0 left-0 right-0 h-ds-01 overflow-hidden\">\n <div\n className={cn(\n 'h-full w-full origin-left opacity-30',\n config.timerBarClass,\n 'motion-safe:animate-timer-bar',\n )}\n style={{\n animationDuration: `${duration}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }}\n />\n </div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Base toast UI — used by all typed toasts\n * ------------------------------------------------------------------------ */\n\nfunction ToastContent({\n type,\n title,\n description,\n action,\n cancel,\n duration = DEFAULT_DURATION,\n showTimerBar = true,\n showAccent = false,\n selfDismissId,\n}: {\n type: ToastType\n title?: React.ReactNode\n description?: React.ReactNode\n action?: ToastActionOptions\n cancel?: ToastActionOptions\n duration?: number\n showTimerBar?: boolean\n /** Render the colored left accent bar. Off by default (status = icon + timer bar). */\n showAccent?: boolean\n /** When set, this component manages its own dismiss timer (hover-aware). */\n selfDismissId?: string | number\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const config = TOAST_TYPE_CONFIG[type]\n const StatusIcon = config.icon\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(duration)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || duration === Infinity) return\n if (paused) {\n // Pause: save remaining time\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n // Start/resume timer\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, duration, paused])\n\n // Error toasts announce assertively so screen readers interrupt;\n // warnings stay polite but atomically re-read the message.\n const isUrgent = type === 'error'\n return (\n <motion.div\n layout=\"position\"\n role={isUrgent ? 'alert' : 'status'}\n aria-live={isUrgent ? 'assertive' : 'polite'}\n aria-atomic=\"true\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint on error only — the one you must not miss.\n type === 'error' && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent (status is carried by icon + timer bar) */}\n {showAccent && config.accentClass && (\n <div\n className={cn('w-1 shrink-0 rounded-l-overlay-sm', config.accentClass)}\n />\n )}\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 items-start gap-ds-03 p-ds-04\">\n {/* Status icon — animates between spinner ↔ typed icon */}\n <AnimatePresence mode=\"wait\">\n {type === 'loading' ? (\n <motion.div\n key=\"loading\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n className=\"mt-0.5 shrink-0\"\n >\n <Spinner size=\"sm\" className=\"h-4 w-4\" />\n </motion.div>\n ) : StatusIcon ? (\n <motion.div\n key={type}\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n className=\"mt-0.5 shrink-0\"\n >\n <Icon icon={StatusIcon} size=\"sm\" className={config.iconClass} />\n </motion.div>\n ) : null}\n </AnimatePresence>\n\n {/* Text */}\n <div className=\"min-w-0 flex-1\">\n {title && (\n <p className=\"text-ds-md font-semibold text-surface-fg\">\n {title}\n </p>\n )}\n {description && (\n <p\n className={cn(\n 'text-ds-sm text-surface-fg-muted',\n title && 'mt-0.5',\n )}\n >\n {description}\n </p>\n )}\n\n {/* Action / cancel buttons */}\n {(action || cancel) && (\n <div className=\"mt-ds-03 flex items-center gap-ds-03\">\n {action && (\n <button\n type=\"button\"\n onClick={action.onClick}\n className=\"text-ds-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-raised-hover rounded-control-inner px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {action.label}\n </button>\n )}\n {cancel && (\n <button\n type=\"button\"\n onClick={cancel.onClick}\n className=\"text-ds-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {cancel.label}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Timer bar */}\n {showTimerBar && type !== 'loading' && (\n <TimerBar duration={duration} type={type} paused={paused} />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Upload toast UI\n * ------------------------------------------------------------------------ */\n\nconst IMAGE_EXTENSIONS = new Set([\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif',\n])\n\nfunction isImageFile(file: UploadFile): boolean {\n if (file.previewUrl) return true\n const ext = file.name.split('.').pop()?.toLowerCase() ?? ''\n return IMAGE_EXTENSIONS.has(ext)\n}\n\nfunction getProgressColor(\n status: UploadFile['status'],\n): 'accent' | 'success' | 'error' {\n switch (status) {\n case 'complete':\n return 'success'\n case 'error':\n return 'error'\n default:\n return 'accent'\n }\n}\n\nfunction getProgressValue(file: UploadFile): number | undefined {\n switch (file.status) {\n case 'pending':\n return 0\n case 'uploading':\n return file.progress ?? 0\n case 'processing':\n return undefined\n case 'complete':\n return 100\n case 'error':\n return file.progress ?? 0\n }\n}\n\nfunction formatFileSize(bytes: number): string {\n if (!bytes || bytes < 0 || !Number.isFinite(bytes)) return '0 B'\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`\n}\n\nfunction UploadFileRow({\n file,\n onRetry,\n onRemove,\n}: {\n file: UploadFile\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n}) {\n const isTerminal = file.status === 'complete' || file.status === 'error'\n const isUploading =\n file.status === 'uploading' || file.status === 'processing'\n\n return (\n <motion.div\n layout\n className=\"flex items-center gap-ds-02 py-1\"\n initial={false}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, x: -20, height: 0 }}\n transition={springs.snappy}\n >\n {/* File icon — animates between states */}\n <div className=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <AnimatePresence mode=\"wait\">\n {file.status === 'complete' ? (\n <motion.div\n key=\"complete\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"xs\" className=\"text-success-11\" />\n </motion.div>\n ) : file.status === 'error' ? (\n <motion.div\n key=\"error\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n >\n <Icon icon={IconAlertCircle} size=\"xs\" className=\"text-error-11\" />\n </motion.div>\n ) : file.status === 'processing' ? (\n <motion.div\n key=\"processing\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Spinner size=\"sm\" className=\"h-3.5 w-3.5\" />\n </motion.div>\n ) : isImageFile(file) ? (\n <motion.div\n key=\"image\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconPhoto} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"file\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconFile} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n\n {/* Filename */}\n <span className=\"min-w-0 flex-1 truncate text-ds-xs text-surface-fg\">\n {file.name}\n </span>\n\n {/* Progress bar or status */}\n {isUploading ? (\n <div className=\"flex w-16 items-center gap-1\">\n <Progress\n size=\"sm\"\n color={getProgressColor(file.status)}\n value={getProgressValue(file)}\n className=\"flex-1\"\n />\n {file.progress !== undefined && (\n <span className=\"shrink-0 text-ds-xs tabular-nums text-surface-fg-muted\">\n {file.progress}%\n </span>\n )}\n </div>\n ) : file.status === 'complete' ? (\n <span className=\"text-ds-xs text-success-11\">Done</span>\n ) : file.status === 'error' ? (\n <span className=\"max-w-[60px] truncate text-ds-xs text-error-11\">\n {file.error || 'Failed'}\n </span>\n ) : (\n <span className=\"text-ds-xs text-surface-fg-muted\">\n {formatFileSize(file.size)}\n </span>\n )}\n\n {/* Actions */}\n {file.status === 'error' && onRetry && (\n <button\n type=\"button\"\n onClick={() => onRetry(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Retry ${file.name}`}\n title=\"Retry\"\n >\n <IconRefresh className=\"h-3.5 w-3.5\" />\n </button>\n )}\n {!isTerminal && onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Cancel ${file.name}`}\n title=\"Remove\"\n >\n <IconX className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </motion.div>\n )\n}\n\nfunction UploadToastContent({\n files,\n onRetry,\n onRemove,\n selfDismissId,\n showAccent = false,\n}: {\n files: UploadFile[]\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n selfDismissId?: string | number\n /** Render the colored left accent bar. Off by default. */\n showAccent?: boolean\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const completeCount = files.filter((f) => f.status === 'complete').length\n const errorCount = files.filter((f) => f.status === 'error').length\n const allTerminal = files.every(\n (f) => f.status === 'complete' || f.status === 'error',\n )\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(UPLOAD_COMPLETE_DELAY)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || !allTerminal) return\n if (paused) {\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, allTerminal, paused])\n\n const accentClass = allTerminal\n ? errorCount > 0\n ? 'bg-error-7'\n : 'bg-success-7'\n : 'bg-accent-9'\n\n const timerBarType: ToastType = allTerminal\n ? errorCount > 0\n ? 'error'\n : 'success'\n : 'loading'\n\n const uploadUrgent = allTerminal && errorCount > 0\n return (\n <motion.div\n layout\n role={uploadUrgent ? 'alert' : 'status'}\n aria-live={uploadUrgent ? 'assertive' : 'polite'}\n aria-label=\"File uploads\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint when the batch ended with failures.\n uploadUrgent && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent */}\n {showAccent && (\n <div className={cn('w-1 shrink-0 rounded-l-overlay-sm', accentClass)} />\n )}\n\n {/* Content */}\n <div className=\"min-w-0 flex-1 p-ds-04\">\n {/* Header — icon morphs between upload ↔ result */}\n <div className=\"flex items-center gap-ds-02\">\n <AnimatePresence mode=\"wait\">\n {allTerminal ? (\n errorCount > 0 ? (\n <motion.div\n key=\"error-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconAlertCircle} size=\"sm\" className=\"shrink-0 text-error-11\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"success-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"shrink-0 text-success-11\" />\n </motion.div>\n )\n ) : (\n <motion.div\n key=\"upload-icon\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconUpload} size=\"sm\" className=\"shrink-0 text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n <p className=\"text-ds-md font-semibold text-surface-fg\">\n {allTerminal\n ? errorCount > 0\n ? `${completeCount} of ${files.length} uploaded`\n : `${files.length} file${files.length > 1 ? 's' : ''} uploaded`\n : `Uploading ${files.length} file${files.length > 1 ? 's' : ''}`}\n </p>\n </div>\n {!allTerminal && (\n <p className=\"mt-0.5 text-ds-xs text-surface-fg-muted\">\n {completeCount} of {files.length} complete\n {errorCount > 0 && ` · ${errorCount} failed`}\n </p>\n )}\n\n {/* File list */}\n <div className=\"mt-ds-02 max-h-[140px] overflow-y-auto\">\n <AnimatePresence>\n {files.map((file) => (\n <UploadFileRow\n key={file.id}\n file={file}\n onRetry={onRetry}\n onRemove={onRemove}\n />\n ))}\n </AnimatePresence>\n </div>\n\n {/* Screen reader announcement */}\n <div className=\"sr-only\" aria-live=\"polite\">\n {completeCount} of {files.length} files uploaded\n {errorCount > 0 && `, ${errorCount} failed`}\n </div>\n </div>\n\n {/* Timer bar — only after all terminal */}\n {allTerminal && (\n <TimerBar\n duration={UPLOAD_COMPLETE_DELAY}\n type={timerBarType}\n paused={false}\n />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Toast API — wraps Sonner with our styled UI\n * ------------------------------------------------------------------------ */\n\nfunction createSonnerOptions(\n options?: ToastOptions,\n duration?: number,\n): ExternalToast {\n return {\n id: options?.id,\n duration: options?.duration ?? duration ?? DEFAULT_DURATION,\n unstyled: true,\n classNames: { toast: 'w-full' },\n }\n}\n\nfunction createTypedToast(\n type: ToastType,\n message: string | React.ReactNode,\n options?: ToastOptions,\n) {\n assertToasterMounted()\n const duration = options?.duration ?? DEFAULT_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n description={options?.description}\n action={options?.action}\n cancel={options?.cancel}\n duration={duration}\n showAccent={options?.showAccent}\n />\n ),\n createSonnerOptions(options, duration),\n )\n}\n\n/**\n * Toast API — imperative toast notifications with Sonner rendering engine.\n *\n * Mount `<Toaster />` once at your root layout. Then call `toast()` from anywhere.\n *\n * @example\n * toast('Something happened')\n * toast.success('Saved!')\n * toast.error('Failed to save')\n * toast.promise(saveFn(), { loading: 'Saving...', success: 'Done!', error: 'Failed' })\n * toast.undo('Deleted', { onUndo: () => restore() })\n * toast.upload({ files, onRetry, onRemove })\n */\nfunction toast(message: string | React.ReactNode, options?: ToastOptions) {\n return createTypedToast('message', message, options)\n}\n\ntoast.message = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('message', message, options)\n}\n\ntoast.success = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('success', message, options)\n}\n\ntoast.error = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('error', message, options)\n}\n\ntoast.warning = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('warning', message, options)\n}\n\ntoast.info = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('info', message, options)\n}\n\ntoast.loading = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('loading', message, {\n ...options,\n duration: options?.duration ?? Infinity,\n })\n}\n\ntoast.promise = <T,>(\n promise: Promise<T>,\n options: {\n loading: string | React.ReactNode\n success: string | React.ReactNode | ((data: T) => string | React.ReactNode)\n error:\n | string\n | React.ReactNode\n | ((error: unknown) => string | React.ReactNode)\n },\n) => {\n const id = sonnerToast.custom(\n () => (\n <ToastContent type=\"loading\" title={options.loading} showTimerBar={false} />\n ),\n { duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n\n const replaceWith = (type: ToastType, message: React.ReactNode) => {\n // Sonner doesn't reset its auto-dismiss timer when updating a custom toast\n // in-place from duration: Infinity, so ToastContent manages its own\n // hover-aware dismiss timer via selfDismissId.\n sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n duration={DEFAULT_DURATION}\n selfDismissId={id}\n />\n ),\n { id, duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n }\n\n promise\n .then((data) => {\n const message =\n typeof options.success === 'function'\n ? options.success(data)\n : options.success\n replaceWith('success', message)\n })\n .catch((error) => {\n const message =\n typeof options.error === 'function'\n ? options.error(error)\n : options.error\n replaceWith('error', message)\n })\n\n return id\n}\n\ntoast.undo = (message: string | React.ReactNode, options: ToastUndoOptions) => {\n const duration = options.duration ?? UNDO_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type=\"message\"\n title={message}\n description={options.description}\n action={{ label: 'Undo', onClick: options.onUndo }}\n duration={duration}\n />\n ),\n { duration, unstyled: true, classNames: { toast: 'w-full' } },\n )\n}\n\ntoast.upload = (options: ToastUploadOptions) => {\n // Pre-determine the id so we can pass it to UploadToastContent for self-dismiss.\n const toastId = options.id ?? String(Date.now())\n\n sonnerToast.custom(\n () => (\n <UploadToastContent\n files={options.files}\n onRetry={options.onRetry}\n onRemove={options.onRemove}\n selfDismissId={toastId}\n showAccent={options.showAccent}\n />\n ),\n {\n id: toastId,\n duration: Infinity,\n unstyled: true,\n classNames: { toast: 'w-full' },\n },\n )\n\n return toastId\n}\n\ntoast.custom = (\n render: (id: string | number) => React.ReactElement,\n options?: ExternalToast,\n) => {\n return sonnerToast.custom(render, {\n ...options,\n unstyled: true,\n classNames: { toast: 'w-full', ...(options?.classNames as Record<string, string>) },\n })\n}\n\ntoast.dismiss = (id?: string | number) => {\n if (id !== undefined) {\n sonnerToast.dismiss(id)\n } else {\n sonnerToast.dismiss()\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------ */\n\nexport type ToastProps = ToastOptions\n\nexport {\n formatFileSize,\n TimerBar,\n toast,\n ToastContent,\n UploadFileRow,\n UploadToastContent,\n}\nexport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n"],"mappings":"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"}
|
package/dist/ui/toaster.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toaster.js","names":[],"sources":["../../src/ui/toaster.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Toaster as SonnerToaster } from 'sonner'\n\nimport { cn } from './lib/utils'\nimport { registerToaster } from './toast-registry'\n\n/**\n * Toaster — mount once at your root layout to enable toast notifications.\n *\n * **⚠ Peer dependency required:** `sonner`. Without it your build fails with\n * `Cannot find module 'sonner'`. Install:\n * ```bash\n * pnpm add sonner # or: npm i sonner / yarn add sonner / bun add sonner\n * ```\n * `sonner` is listed as an optional peer because consumers who never render\n * `<Toaster />` shouldn't pay the install cost — but once you import this\n * component, it becomes effectively required.\n *\n * @example\n * import { Toaster } from '@devalok/shilp-sutra/ui/toaster'\n *\n * export default function RootLayout({ children }: { children: React.ReactNode }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * {children}\n * <Toaster />\n * </body>\n * </html>\n * )\n * }\n *\n * // Then from any component:\n * import { toast } from '@devalok/shilp-sutra/ui/toast'\n * toast.success('Saved!')\n */\nexport interface ToasterProps {\n /** Toast position on screen. Default: 'bottom-right' */\n position?:\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right'\n /** Show close button on all toasts. Default: false */\n closeButton?: boolean\n /** Default auto-dismiss duration in ms. Default: 5000 */\n duration?: number\n /** Keyboard shortcut to focus toast region. Default: ['altKey', 'KeyT'] */\n hotkey?: string[]\n /** Max visible toasts before stacking. Default: 3 */\n visibleToasts?: number\n /** Additional CSS class */\n className?: string\n}\n\nexport const Toaster = React.forwardRef<HTMLDivElement, ToasterProps>(\n (\n {\n position = 'bottom-right',\n closeButton = false,\n duration = 5000,\n hotkey = ['altKey', 'KeyT'],\n visibleToasts = 3,\n className,\n },\n ref,\n ) => {\n React.useEffect(() => registerToaster(), [])\n return (\n <div ref={ref} className={cn('z-toast', className)}>\n <SonnerToaster\n position={position}\n closeButton={closeButton}\n duration={duration}\n hotkey={hotkey}\n visibleToasts={visibleToasts}\n toastOptions={{\n unstyled: true,\n classNames: {\n toast: 'w-full',\n },\n }}\n />\n </div>\n )\n },\n)\nToaster.displayName = 'Toaster'\n"],"mappings":";;;;;;;AA2DA,IAAa,IAAU,EAAM,YAEzB,EACE,cAAW,gBACX,iBAAc,IACd,cAAW,KACX,YAAS,CAAC,UAAU,
|
|
1
|
+
{"version":3,"file":"toaster.js","names":[],"sources":["../../src/ui/toaster.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Toaster as SonnerToaster } from 'sonner'\n\nimport { cn } from './lib/utils'\nimport { registerToaster } from './toast-registry'\n\n/**\n * Toaster — mount once at your root layout to enable toast notifications.\n *\n * **⚠ Peer dependency required:** `sonner`. Without it your build fails with\n * `Cannot find module 'sonner'`. Install:\n * ```bash\n * pnpm add sonner # or: npm i sonner / yarn add sonner / bun add sonner\n * ```\n * `sonner` is listed as an optional peer because consumers who never render\n * `<Toaster />` shouldn't pay the install cost — but once you import this\n * component, it becomes effectively required.\n *\n * @example\n * import { Toaster } from '@devalok/shilp-sutra/ui/toaster'\n *\n * export default function RootLayout({ children }: { children: React.ReactNode }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * {children}\n * <Toaster />\n * </body>\n * </html>\n * )\n * }\n *\n * // Then from any component:\n * import { toast } from '@devalok/shilp-sutra/ui/toast'\n * toast.success('Saved!')\n */\nexport interface ToasterProps {\n /** Toast position on screen. Default: 'bottom-right' */\n position?:\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right'\n /** Show close button on all toasts. Default: false */\n closeButton?: boolean\n /** Default auto-dismiss duration in ms. Default: 5000 */\n duration?: number\n /** Keyboard shortcut to focus toast region. Default: ['altKey', 'KeyT'] */\n hotkey?: string[]\n /** Max visible toasts before stacking. Default: 3 */\n visibleToasts?: number\n /** Additional CSS class */\n className?: string\n}\n\nexport const Toaster = React.forwardRef<HTMLDivElement, ToasterProps>(\n (\n {\n position = 'bottom-right',\n closeButton = false,\n duration = 5000,\n hotkey = ['altKey', 'KeyT'],\n visibleToasts = 3,\n className,\n },\n ref,\n ) => {\n React.useEffect(() => registerToaster(), [])\n return (\n <div ref={ref} className={cn('z-toast', className)}>\n <SonnerToaster\n position={position}\n closeButton={closeButton}\n duration={duration}\n hotkey={hotkey}\n visibleToasts={visibleToasts}\n toastOptions={{\n unstyled: true,\n classNames: {\n toast: 'w-full',\n },\n }}\n />\n </div>\n )\n },\n)\nToaster.displayName = 'Toaster'\n"],"mappings":";;;;;;;AA2DA,IAAa,IAAU,EAAM,YAEzB,EACE,cAAW,gBACX,iBAAc,IACd,cAAW,KACX,YAAS,CAAC,UAAU,MAAM,GAC1B,mBAAgB,GAChB,gBAEF,OAEA,EAAM,gBAAgB,EAAgB,GAAG,CAAC,CAAC,GAEzC,kBAAC,OAAD;CAAU;CAAK,WAAW,EAAG,WAAW,CAAS;WAC/C,kBAAC,GAAD;EACY;EACG;EACH;EACF;EACO;EACf,cAAc;GACZ,UAAU;GACV,YAAY,EACV,OAAO,SACT;EACF;CACD,CAAA;AACE,CAAA,EAGX;AACA,EAAQ,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toggle-group.js","names":[],"sources":["../../src/ui/toggle-group.tsx"],"sourcesContent":["'use client'\n\nimport * as ToggleGroupPrimitive from '@primitives/react-toggle-group'\nimport { type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\nimport { toggleVariants } from './toggle'\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants>\n>({\n size: 'md',\n variant: 'default',\n color: 'accent',\n})\n\nconst ToggleGroup = React.forwardRef<\n React.ElementRef<typeof ToggleGroupPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants>\n>(({ className, variant, size, color, children, ...props }, ref) => {\n const contextValue = React.useMemo(() => ({ variant, size, color }), [variant, size, color])\n\n return (\n <ToggleGroupPrimitive.Root\n ref={ref}\n className={cn('inline-flex items-center gap-ds-02', className)}\n {...props}\n >\n <ToggleGroupContext.Provider value={contextValue}>\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n})\nToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName\n\nconst ToggleGroupItem = React.forwardRef<\n React.ElementRef<typeof ToggleGroupPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>\n>(({ className, variant, size, color, ...props }, ref) => {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n ref={ref}\n className={cn(\n toggleVariants({\n variant: variant ?? context.variant,\n size: size ?? context.size,\n color: color ?? context.color,\n }),\n 'active:scale-[0.95] transition-transform',\n className,\n )}\n {...props}\n />\n )\n})\nToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName\n\nexport type ToggleGroupProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants>\nexport type ToggleGroupItemProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>\n\nexport { ToggleGroup, ToggleGroupItem }\n"],"mappings":";;;;;;;AASA,IAAM,IAAqB,EAAM,cAE/B;CACA,MAAM;CACN,SAAS;CACT,OAAO;
|
|
1
|
+
{"version":3,"file":"toggle-group.js","names":[],"sources":["../../src/ui/toggle-group.tsx"],"sourcesContent":["'use client'\n\nimport * as ToggleGroupPrimitive from '@primitives/react-toggle-group'\nimport { type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\nimport { toggleVariants } from './toggle'\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants>\n>({\n size: 'md',\n variant: 'default',\n color: 'accent',\n})\n\nconst ToggleGroup = React.forwardRef<\n React.ElementRef<typeof ToggleGroupPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants>\n>(({ className, variant, size, color, children, ...props }, ref) => {\n const contextValue = React.useMemo(() => ({ variant, size, color }), [variant, size, color])\n\n return (\n <ToggleGroupPrimitive.Root\n ref={ref}\n className={cn('inline-flex items-center gap-ds-02', className)}\n {...props}\n >\n <ToggleGroupContext.Provider value={contextValue}>\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n})\nToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName\n\nconst ToggleGroupItem = React.forwardRef<\n React.ElementRef<typeof ToggleGroupPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>\n>(({ className, variant, size, color, ...props }, ref) => {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n ref={ref}\n className={cn(\n toggleVariants({\n variant: variant ?? context.variant,\n size: size ?? context.size,\n color: color ?? context.color,\n }),\n 'active:scale-[0.95] transition-transform',\n className,\n )}\n {...props}\n />\n )\n})\nToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName\n\nexport type ToggleGroupProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants>\nexport type ToggleGroupItemProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>\n\nexport { ToggleGroup, ToggleGroupItem }\n"],"mappings":";;;;;;;AASA,IAAM,IAAqB,EAAM,cAE/B;CACA,MAAM;CACN,SAAS;CACT,OAAO;AACT,CAAC,GAEK,IAAc,EAAM,YAIvB,EAAE,cAAW,YAAS,SAAM,UAAO,aAAU,GAAG,KAAS,MAAQ;CAClE,IAAM,IAAe,EAAM,eAAe;EAAE;EAAS;EAAM;CAAM,IAAI;EAAC;EAAS;EAAM;CAAK,CAAC;CAE3F,OACE,kBAAC,GAAD;EACO;EACL,WAAW,EAAG,sCAAsC,CAAS;EAC7D,GAAI;YAEJ,kBAAC,EAAmB,UAApB;GAA6B,OAAO;GACjC;EAC0B,CAAA;CACJ,CAAA;AAE/B,CAAC;AACD,EAAY,cAAA,EAAwC;AAEpD,IAAM,IAAkB,EAAM,YAI3B,EAAE,cAAW,YAAS,SAAM,UAAO,GAAG,KAAS,MAAQ;CACxD,IAAM,IAAU,EAAM,WAAW,CAAkB;CAEnD,OACE,kBAAC,GAAD;EACO;EACL,WAAW,EACT,EAAe;GACb,SAAS,KAAW,EAAQ;GAC5B,MAAM,KAAQ,EAAQ;GACtB,OAAO,KAAS,EAAQ;EAC1B,CAAC,GACD,4CACA,CACF;EACA,GAAI;CACL,CAAA;AAEL,CAAC;AACD,EAAgB,cAAA,EAAwC"}
|
package/dist/ui/toggle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toggle.js","names":[],"sources":["../../src/ui/toggle.tsx"],"sourcesContent":["'use client'\n\nimport * as TogglePrimitive from '@primitives/react-toggle'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { motionProps,springs } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst MotionToggleRoot = motion.create(TogglePrimitive.Root)\n\nconst toggleVariants = cva(\n 'inline-flex items-center justify-center gap-ds-03 rounded-control font-medium transition-colors duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-action-disabled',\n {\n variants: {\n variant: {\n default:\n 'bg-transparent hover:bg-surface-raised-hover text-surface-fg-muted',\n outline:\n 'border border-surface-border-strong bg-transparent hover:bg-surface-raised-hover hover:border-surface-border-strong',\n },\n size: {\n sm: 'h-ds-sm px-ds-03 text-ds-sm',\n md: 'h-ds-md px-ds-04 text-ds-md',\n lg: 'h-ds-lg px-ds-05 text-ds-base',\n },\n color: {\n accent: 'data-[state=on]:bg-accent-2 data-[state=on]:text-accent-11',\n error: 'data-[state=on]:bg-error-2 data-[state=on]:text-error-11',\n success: 'data-[state=on]:bg-success-2 data-[state=on]:text-success-11',\n neutral: 'data-[state=on]:bg-surface-raised-hover data-[state=on]:text-surface-fg',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n color: 'accent',\n },\n },\n)\n\nconst Toggle = React.forwardRef<\n React.ElementRef<typeof TogglePrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n>(({ className, variant, size, color, ...props }, ref) => (\n <MotionToggleRoot\n ref={ref}\n whileTap={{ scale: 0.95 }}\n transition={springs.snappy}\n className={cn(toggleVariants({ variant, size, color }), className)}\n {...motionProps(props)}\n />\n))\nToggle.displayName = TogglePrimitive.Root.displayName\n\nexport type ToggleProps = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n\nexport { Toggle, toggleVariants }\n"],"mappings":";;;;;;;;;AAUA,IAAM,IAAmB,EAAO,OAAO,
|
|
1
|
+
{"version":3,"file":"toggle.js","names":[],"sources":["../../src/ui/toggle.tsx"],"sourcesContent":["'use client'\n\nimport * as TogglePrimitive from '@primitives/react-toggle'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { motionProps,springs } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst MotionToggleRoot = motion.create(TogglePrimitive.Root)\n\nconst toggleVariants = cva(\n 'inline-flex items-center justify-center gap-ds-03 rounded-control font-medium transition-colors duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-action-disabled',\n {\n variants: {\n variant: {\n default:\n 'bg-transparent hover:bg-surface-raised-hover text-surface-fg-muted',\n outline:\n 'border border-surface-border-strong bg-transparent hover:bg-surface-raised-hover hover:border-surface-border-strong',\n },\n size: {\n sm: 'h-ds-sm px-ds-03 text-ds-sm',\n md: 'h-ds-md px-ds-04 text-ds-md',\n lg: 'h-ds-lg px-ds-05 text-ds-base',\n },\n color: {\n accent: 'data-[state=on]:bg-accent-2 data-[state=on]:text-accent-11',\n error: 'data-[state=on]:bg-error-2 data-[state=on]:text-error-11',\n success: 'data-[state=on]:bg-success-2 data-[state=on]:text-success-11',\n neutral: 'data-[state=on]:bg-surface-raised-hover data-[state=on]:text-surface-fg',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n color: 'accent',\n },\n },\n)\n\nconst Toggle = React.forwardRef<\n React.ElementRef<typeof TogglePrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n>(({ className, variant, size, color, ...props }, ref) => (\n <MotionToggleRoot\n ref={ref}\n whileTap={{ scale: 0.95 }}\n transition={springs.snappy}\n className={cn(toggleVariants({ variant, size, color }), className)}\n {...motionProps(props)}\n />\n))\nToggle.displayName = TogglePrimitive.Root.displayName\n\nexport type ToggleProps = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n\nexport { Toggle, toggleVariants }\n"],"mappings":";;;;;;;;;AAUA,IAAM,IAAmB,EAAO,OAAO,CAAoB,GAErD,IAAiB,EACrB,qTACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,SACE;EACJ;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EACA,OAAO;GACL,QAAQ;GACR,OAAO;GACP,SAAS;GACT,SAAS;EACX;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;CACT;AACF,CACF,GAEM,IAAS,EAAM,YAIlB,EAAE,cAAW,YAAS,SAAM,UAAO,GAAG,KAAS,MAChD,kBAAC,GAAD;CACO;CACL,UAAU,EAAE,OAAO,IAAK;CACxB,YAAY,EAAQ;CACpB,WAAW,EAAG,EAAe;EAAE;EAAS;EAAM;CAAM,CAAC,GAAG,CAAS;CACjE,GAAI,EAAY,CAAK;AACtB,CAAA,CACF;AACD,EAAO,cAAA,EAAmC"}
|
package/dist/ui/tooltip.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.js","names":[],"sources":["../../src/ui/tooltip.tsx"],"sourcesContent":["'use client'\n\nimport * as TooltipPrimitive from '@primitives/react-tooltip'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { springs, tweens } from './lib/motion'\nimport { useControllableOpen } from './lib/use-controllable-open'\nimport { cn } from './lib/utils'\n\n// ── Auto-provider: wraps with TooltipPrimitive.Provider if none exists ──\n\nconst TooltipProviderContext = React.createContext(false)\n\nconst TooltipProvider: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>> = ({\n children,\n ...props\n}) => (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider {...props}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n)\n\nfunction AutoProvider({ children }: { children: React.ReactNode }) {\n const hasProvider = React.useContext(TooltipProviderContext)\n if (hasProvider) return <>{children}</>\n return (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider delayDuration={300}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n )\n}\n\n// ── Internal context to thread `open` state to animated children ──\n\ntype TooltipContextValue = { open: boolean }\nconst TooltipContext = React.createContext<TooltipContextValue>({ open: false })\n\nconst Tooltip: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>> = ({\n open: openProp,\n defaultOpen,\n onOpenChange,\n ...props\n}) => {\n const { open, setOpen } = useControllableOpen({ open: openProp, defaultOpen, onOpenChange })\n\n const contextValue = React.useMemo(() => ({ open }), [open])\n\n return (\n <AutoProvider>\n <TooltipContext.Provider value={contextValue}>\n <TooltipPrimitive.Root open={open} onOpenChange={setOpen} {...props} />\n </TooltipContext.Provider>\n </AutoProvider>\n )\n}\nTooltip.displayName = 'Tooltip'\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\n// ── Side-based slide offsets ──\n\nconst sideOffset: Record<string, { x?: number; y?: number }> = {\n top: { y: 4 },\n bottom: { y: -4 },\n left: { x: 4 },\n right: { x: -4 },\n}\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset: sideOffsetProp = 4, side = 'top', children, ...props }, ref) => {\n const { open } = React.useContext(TooltipContext)\n const slideInit = sideOffset[side] ?? {}\n\n return (\n <AnimatePresence>\n {open && (\n <TooltipPrimitive.Portal forceMount>\n <TooltipPrimitive.Content\n ref={ref}\n forceMount\n sideOffset={sideOffsetProp}\n side={side}\n {...props}\n asChild\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95, ...slideInit }}\n animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, ...slideInit }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-ds-sm text-surface-inverted-fg shadow-floating',\n className,\n )}\n >\n {children}\n </motion.div>\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n )}\n </AnimatePresence>\n )\n})\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport type TooltipContentProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n\nexport { Tooltip, TooltipContent, TooltipProvider,TooltipTrigger }\n"],"mappings":";;;;;;;;;AAYA,IAAM,IAAyB,EAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"tooltip.js","names":[],"sources":["../../src/ui/tooltip.tsx"],"sourcesContent":["'use client'\n\nimport * as TooltipPrimitive from '@primitives/react-tooltip'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { springs, tweens } from './lib/motion'\nimport { useControllableOpen } from './lib/use-controllable-open'\nimport { cn } from './lib/utils'\n\n// ── Auto-provider: wraps with TooltipPrimitive.Provider if none exists ──\n\nconst TooltipProviderContext = React.createContext(false)\n\nconst TooltipProvider: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>> = ({\n children,\n ...props\n}) => (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider {...props}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n)\n\nfunction AutoProvider({ children }: { children: React.ReactNode }) {\n const hasProvider = React.useContext(TooltipProviderContext)\n if (hasProvider) return <>{children}</>\n return (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider delayDuration={300}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n )\n}\n\n// ── Internal context to thread `open` state to animated children ──\n\ntype TooltipContextValue = { open: boolean }\nconst TooltipContext = React.createContext<TooltipContextValue>({ open: false })\n\nconst Tooltip: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>> = ({\n open: openProp,\n defaultOpen,\n onOpenChange,\n ...props\n}) => {\n const { open, setOpen } = useControllableOpen({ open: openProp, defaultOpen, onOpenChange })\n\n const contextValue = React.useMemo(() => ({ open }), [open])\n\n return (\n <AutoProvider>\n <TooltipContext.Provider value={contextValue}>\n <TooltipPrimitive.Root open={open} onOpenChange={setOpen} {...props} />\n </TooltipContext.Provider>\n </AutoProvider>\n )\n}\nTooltip.displayName = 'Tooltip'\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\n// ── Side-based slide offsets ──\n\nconst sideOffset: Record<string, { x?: number; y?: number }> = {\n top: { y: 4 },\n bottom: { y: -4 },\n left: { x: 4 },\n right: { x: -4 },\n}\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset: sideOffsetProp = 4, side = 'top', children, ...props }, ref) => {\n const { open } = React.useContext(TooltipContext)\n const slideInit = sideOffset[side] ?? {}\n\n return (\n <AnimatePresence>\n {open && (\n <TooltipPrimitive.Portal forceMount>\n <TooltipPrimitive.Content\n ref={ref}\n forceMount\n sideOffset={sideOffsetProp}\n side={side}\n {...props}\n asChild\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95, ...slideInit }}\n animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, ...slideInit }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-ds-sm text-surface-inverted-fg shadow-floating',\n className,\n )}\n >\n {children}\n </motion.div>\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n )}\n </AnimatePresence>\n )\n})\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport type TooltipContentProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n\nexport { Tooltip, TooltipContent, TooltipProvider,TooltipTrigger }\n"],"mappings":";;;;;;;;;AAYA,IAAM,IAAyB,EAAM,cAAc,EAAK,GAElD,KAA+F,EACnG,aACA,GAAG,QAEH,kBAAC,EAAuB,UAAxB;CAAiC,OAAO;WACtC,kBAAC,GAAD;EAA2B,GAAI;EAC5B;CACwB,CAAA;AACI,CAAA;AAGnC,SAAS,EAAa,EAAE,eAA2C;CAGjE,OAFoB,EAAM,WAAW,CACjC,IAAoB,kBAAA,GAAA,EAAG,YAAW,CAAA,IAEpC,kBAAC,EAAuB,UAAxB;EAAiC,OAAO;YACtC,kBAAC,GAAD;GAA2B,eAAe;GACvC;EACwB,CAAA;CACI,CAAA;AAErC;AAKA,IAAM,IAAiB,EAAM,cAAmC,EAAE,MAAM,GAAM,CAAC,GAEzE,KAAmF,EACvF,MAAM,GACN,gBACA,iBACA,GAAG,QACC;CACJ,IAAM,EAAE,SAAM,eAAY,EAAoB;EAAE,MAAM;EAAU;EAAa;CAAa,CAAC,GAErF,IAAe,EAAM,eAAe,EAAE,QAAK,IAAI,CAAC,CAAI,CAAC;CAE3D,OACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAe,UAAhB;EAAyB,OAAO;YAC9B,kBAAC,GAAD;GAA6B;GAAM,cAAc;GAAS,GAAI;EAAQ,CAAA;CAC/C,CAAA,EACb,CAAA;AAElB;AACA,EAAQ,cAAc;AAEtB,IAAM,IAAiB,GAIjB,IAAyD;CAC7D,KAAK,EAAE,GAAG,EAAE;CACZ,QAAQ,EAAE,GAAG,GAAG;CAChB,MAAM,EAAE,GAAG,EAAE;CACb,OAAO,EAAE,GAAG,GAAG;AACjB,GAEM,IAAiB,EAAM,YAG1B,EAAE,cAAW,YAAY,IAAiB,GAAG,UAAO,OAAO,aAAU,GAAG,KAAS,MAAQ;CAC1F,IAAM,EAAE,YAAS,EAAM,WAAW,CAAc,GAC1C,IAAY,EAAW,MAAS,CAAC;CAEvC,OACE,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,GAAD;EAAyB,YAAA;YACvB,kBAAC,GAAD;GACO;GACL,YAAA;GACA,YAAY;GACN;GACN,GAAI;GACJ,SAAA;aAEA,kBAAC,EAAO,KAAR;IACE,SAAS;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;IAAU;IACjD,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG,GAAG;KAAG,GAAG;IAAE;IAC5C,MAAM;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;IAAU;IAC9C,YAAY;KAAE,GAAG,EAAQ;KAAQ,SAAS,EAAO;IAAK;IACtD,WAAW,EACT,2IACA,CACF;IAEC;GACS,CAAA;EACY,CAAA;CACH,CAAA,EAEZ,CAAA;AAErB,CAAC;AACD,EAAe,cAAA,EAAuC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"truncated-text.js","names":[],"sources":["../../src/ui/truncated-text.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\nimport { Tooltip, TooltipContent, TooltipTrigger } from './tooltip'\n\ntype TruncateMode = 'end' | 'clamp' | 'middle'\n\nexport interface TruncatedTextProps {\n /** The full text. Always the accessible name, even when visually shortened. */\n children: string\n /**\n * `end` (default) single-line \"…end\"; `clamp` multi-line (`lines`); `middle`\n * keeps both ends (filenames, emails) — `name…end.pdf`.\n */\n mode?: TruncateMode\n /** Lines for `mode=\"clamp\"`. @default 2 */\n lines?: number\n /** Element to render. @default 'span' */\n as?: 'span' | 'p' | 'div'\n /** Show the full text in a tooltip when (and only when) actually truncated. @default true */\n tooltip?: boolean\n className?: string\n /** Override the full-text accessible name / tooltip (defaults to `children`). */\n title?: string\n}\n\n/** Keep `keep` chars total, ellipsis in the middle. */\nfunction truncateMiddle(str: string, keep: number, ellipsis = '…'): string {\n if (keep >= str.length || keep < 2) return str\n const budget = keep - ellipsis.length\n const head = Math.ceil(budget / 2)\n const tail = Math.floor(budget / 2)\n // slice(-0) returns the whole string, so guard the tail explicitly.\n return str.slice(0, head) + ellipsis + (tail > 0 ? str.slice(-tail) : '')\n}\n\n/**\n * Text that truncates and recovers. CSS handles `end`/`clamp` (full text stays in the\n * DOM, screen-reader safe); `middle` measures + JS-shortens but keeps the full string as\n * the accessible name. A tooltip appears ONLY when the text is actually clipped.\n *\n * Note: for `end`/`middle` in a flex/grid row, the shrinking item still needs `min-w-0`\n * (CSS gotcha) — apply it at the call site.\n *\n * @example\n * <TruncatedText>{user.name}</TruncatedText>\n * <TruncatedText mode=\"middle\">{fileName}</TruncatedText> // report…v2.pdf\n * <TruncatedText mode=\"clamp\" lines={2}>{description}</TruncatedText>\n */\nexport const TruncatedText = React.forwardRef<HTMLElement, TruncatedTextProps>(\n ({ children, mode = 'end', lines = 2, as = 'span', tooltip = true, className, title }, forwardedRef) => {\n const full = children\n const [overflowing, setOverflowing] = React.useState(false)\n const [display, setDisplay] = React.useState(full)\n const roRef = React.useRef<ResizeObserver | null>(null)\n\n // Callback ref owns the ResizeObserver so it always tracks the CURRENT node.\n // Wrapping in a Tooltip remounts the element when `overflowing` flips — a one-shot\n // layout effect would leave a stale observer on the detached node (which then fires\n // with size 0 and resets the measurement). Reconnecting here avoids that loop.\n const attachRef = React.useCallback(\n (node: HTMLElement | null) => {\n if (typeof forwardedRef === 'function') forwardedRef(node)\n else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLElement | null>).current = node\n\n roRef.current?.disconnect()\n if (!node) return\n\n const measure = () => {\n if (mode === 'middle') {\n node.textContent = full\n if (node.clientWidth === 0 || node.scrollWidth <= node.clientWidth) {\n setDisplay(full)\n setOverflowing(false)\n return\n }\n // Largest middle-truncated string that fits, via binary search.\n let lo = 2\n let hi = full.length\n let best = 2\n while (lo <= hi) {\n const midKeep = (lo + hi) >> 1\n node.textContent = truncateMiddle(full, midKeep)\n if (node.scrollWidth <= node.clientWidth) {\n best = midKeep\n lo = midKeep + 1\n } else {\n hi = midKeep - 1\n }\n }\n setDisplay(truncateMiddle(full, best))\n setOverflowing(true)\n } else if (mode === 'clamp') {\n setOverflowing(node.scrollHeight > node.clientHeight + 1)\n } else {\n setOverflowing(node.scrollWidth > node.clientWidth + 1)\n }\n }\n\n measure()\n roRef.current = new ResizeObserver(measure)\n roRef.current.observe(node)\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [full, mode, lines, forwardedRef],\n )\n\n const Tag = as as React.ElementType\n const content = (\n <Tag\n ref={attachRef}\n className={cn(\n mode === 'end' && 'block truncate',\n mode === 'middle' && 'block overflow-hidden whitespace-nowrap',\n mode === 'clamp' && 'overflow-hidden',\n className,\n )}\n style={\n mode === 'clamp'\n ? ({ display: '-webkit-box', WebkitBoxOrient: 'vertical', WebkitLineClamp: lines } as React.CSSProperties)\n : undefined\n }\n // When clipped, the visible text may be shortened (middle mode) — keep the full\n // string as the accessible name so screen readers always read it in full.\n aria-label={overflowing ? (title ?? full) : undefined}\n >\n {mode === 'middle' ? display : full}\n </Tag>\n )\n\n if (tooltip && overflowing) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>{content}</TooltipTrigger>\n <TooltipContent>{title ?? full}</TooltipContent>\n </Tooltip>\n )\n }\n return content\n },\n)\nTruncatedText.displayName = 'TruncatedText'\n"],"mappings":";;;;;;AA6BA,SAAS,EAAe,GAAa,GAAc,IAAW,KAAa;
|
|
1
|
+
{"version":3,"file":"truncated-text.js","names":[],"sources":["../../src/ui/truncated-text.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\nimport { Tooltip, TooltipContent, TooltipTrigger } from './tooltip'\n\ntype TruncateMode = 'end' | 'clamp' | 'middle'\n\nexport interface TruncatedTextProps {\n /** The full text. Always the accessible name, even when visually shortened. */\n children: string\n /**\n * `end` (default) single-line \"…end\"; `clamp` multi-line (`lines`); `middle`\n * keeps both ends (filenames, emails) — `name…end.pdf`.\n */\n mode?: TruncateMode\n /** Lines for `mode=\"clamp\"`. @default 2 */\n lines?: number\n /** Element to render. @default 'span' */\n as?: 'span' | 'p' | 'div'\n /** Show the full text in a tooltip when (and only when) actually truncated. @default true */\n tooltip?: boolean\n className?: string\n /** Override the full-text accessible name / tooltip (defaults to `children`). */\n title?: string\n}\n\n/** Keep `keep` chars total, ellipsis in the middle. */\nfunction truncateMiddle(str: string, keep: number, ellipsis = '…'): string {\n if (keep >= str.length || keep < 2) return str\n const budget = keep - ellipsis.length\n const head = Math.ceil(budget / 2)\n const tail = Math.floor(budget / 2)\n // slice(-0) returns the whole string, so guard the tail explicitly.\n return str.slice(0, head) + ellipsis + (tail > 0 ? str.slice(-tail) : '')\n}\n\n/**\n * Text that truncates and recovers. CSS handles `end`/`clamp` (full text stays in the\n * DOM, screen-reader safe); `middle` measures + JS-shortens but keeps the full string as\n * the accessible name. A tooltip appears ONLY when the text is actually clipped.\n *\n * Note: for `end`/`middle` in a flex/grid row, the shrinking item still needs `min-w-0`\n * (CSS gotcha) — apply it at the call site.\n *\n * @example\n * <TruncatedText>{user.name}</TruncatedText>\n * <TruncatedText mode=\"middle\">{fileName}</TruncatedText> // report…v2.pdf\n * <TruncatedText mode=\"clamp\" lines={2}>{description}</TruncatedText>\n */\nexport const TruncatedText = React.forwardRef<HTMLElement, TruncatedTextProps>(\n ({ children, mode = 'end', lines = 2, as = 'span', tooltip = true, className, title }, forwardedRef) => {\n const full = children\n const [overflowing, setOverflowing] = React.useState(false)\n const [display, setDisplay] = React.useState(full)\n const roRef = React.useRef<ResizeObserver | null>(null)\n\n // Callback ref owns the ResizeObserver so it always tracks the CURRENT node.\n // Wrapping in a Tooltip remounts the element when `overflowing` flips — a one-shot\n // layout effect would leave a stale observer on the detached node (which then fires\n // with size 0 and resets the measurement). Reconnecting here avoids that loop.\n const attachRef = React.useCallback(\n (node: HTMLElement | null) => {\n if (typeof forwardedRef === 'function') forwardedRef(node)\n else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLElement | null>).current = node\n\n roRef.current?.disconnect()\n if (!node) return\n\n const measure = () => {\n if (mode === 'middle') {\n node.textContent = full\n if (node.clientWidth === 0 || node.scrollWidth <= node.clientWidth) {\n setDisplay(full)\n setOverflowing(false)\n return\n }\n // Largest middle-truncated string that fits, via binary search.\n let lo = 2\n let hi = full.length\n let best = 2\n while (lo <= hi) {\n const midKeep = (lo + hi) >> 1\n node.textContent = truncateMiddle(full, midKeep)\n if (node.scrollWidth <= node.clientWidth) {\n best = midKeep\n lo = midKeep + 1\n } else {\n hi = midKeep - 1\n }\n }\n setDisplay(truncateMiddle(full, best))\n setOverflowing(true)\n } else if (mode === 'clamp') {\n setOverflowing(node.scrollHeight > node.clientHeight + 1)\n } else {\n setOverflowing(node.scrollWidth > node.clientWidth + 1)\n }\n }\n\n measure()\n roRef.current = new ResizeObserver(measure)\n roRef.current.observe(node)\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [full, mode, lines, forwardedRef],\n )\n\n const Tag = as as React.ElementType\n const content = (\n <Tag\n ref={attachRef}\n className={cn(\n mode === 'end' && 'block truncate',\n mode === 'middle' && 'block overflow-hidden whitespace-nowrap',\n mode === 'clamp' && 'overflow-hidden',\n className,\n )}\n style={\n mode === 'clamp'\n ? ({ display: '-webkit-box', WebkitBoxOrient: 'vertical', WebkitLineClamp: lines } as React.CSSProperties)\n : undefined\n }\n // When clipped, the visible text may be shortened (middle mode) — keep the full\n // string as the accessible name so screen readers always read it in full.\n aria-label={overflowing ? (title ?? full) : undefined}\n >\n {mode === 'middle' ? display : full}\n </Tag>\n )\n\n if (tooltip && overflowing) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>{content}</TooltipTrigger>\n <TooltipContent>{title ?? full}</TooltipContent>\n </Tooltip>\n )\n }\n return content\n },\n)\nTruncatedText.displayName = 'TruncatedText'\n"],"mappings":";;;;;;AA6BA,SAAS,EAAe,GAAa,GAAc,IAAW,KAAa;CACzE,IAAI,KAAQ,EAAI,UAAU,IAAO,GAAG,OAAO;CAC3C,IAAM,IAAS,IAAO,EAAS,QACzB,IAAO,KAAK,KAAK,IAAS,CAAC,GAC3B,IAAO,KAAK,MAAM,IAAS,CAAC;CAElC,OAAO,EAAI,MAAM,GAAG,CAAI,IAAI,KAAY,IAAO,IAAI,EAAI,MAAM,CAAC,CAAI,IAAI;AACxE;AAeA,IAAa,IAAgB,EAAM,YAChC,EAAE,aAAU,UAAO,OAAO,WAAQ,GAAG,QAAK,QAAQ,aAAU,IAAM,cAAW,YAAS,MAAiB;CACtG,IAAM,IAAO,GACP,CAAC,GAAa,KAAkB,EAAM,SAAS,EAAK,GACpD,CAAC,GAAS,KAAc,EAAM,SAAS,CAAI,GAC3C,IAAQ,EAAM,OAA8B,IAAI,GAsDhD,IACJ,kBAAC,GAAD;EACE,KAlDc,EAAM,aACrB,MAA6B;GAK5B,IAJI,OAAO,KAAiB,aAAY,EAAa,CAAI,IAChD,MAAc,EAA6D,UAAU,IAE9F,EAAM,SAAS,WAAW,GACtB,CAAC,GAAM;GAEX,IAAM,UAAgB;IACpB,IAAI,MAAS,UAAU;KAErB,IADA,EAAK,cAAc,GACf,EAAK,gBAAgB,KAAK,EAAK,eAAe,EAAK,aAAa;MAElE,AADA,EAAW,CAAI,GACf,EAAe,EAAK;MACpB;KACF;KAEA,IAAI,IAAK,GACL,IAAK,EAAK,QACV,IAAO;KACX,OAAO,KAAM,IAAI;MACf,IAAM,IAAW,IAAK,KAAO;MAE7B,AADA,EAAK,cAAc,EAAe,GAAM,CAAO,GAC3C,EAAK,eAAe,EAAK,eAC3B,IAAO,GACP,IAAK,IAAU,KAEf,IAAK,IAAU;KAEnB;KAEA,AADA,EAAW,EAAe,GAAM,CAAI,CAAC,GACrC,EAAe,EAAI;IACrB,OAAO,AACL,EADS,MAAS,UACH,EAAK,eAAe,EAAK,eAAe,IAExC,EAAK,cAAc,EAAK,cAAc,CAAC;GAE1D;GAIA,AAFA,EAAQ,GACR,EAAM,UAAU,IAAI,eAAe,CAAO,GAC1C,EAAM,QAAQ,QAAQ,CAAI;EAC5B,GAEA;GAAC;GAAM;GAAM;GAAO;EAAY,CAMzB;EACL,WAAW,EACT,MAAS,SAAS,kBAClB,MAAS,YAAY,2CACrB,MAAS,WAAW,mBACpB,CACF;EACA,OACE,MAAS,UACJ;GAAE,SAAS;GAAe,iBAAiB;GAAY,iBAAiB;EAAM,IAC/E,KAAA;EAIN,cAAY,IAAe,KAAS,IAAQ,KAAA;YAE3C,MAAS,WAAW,IAAU;CAC5B,CAAA;CAWP,OARI,KAAW,IAEX,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;EAAgB,SAAA;YAAS;CAAwB,CAAA,GACjD,kBAAC,GAAD,EAAA,UAAiB,KAAS,EAAqB,CAAA,CACxC,EAAA,CAAA,IAGN;AACT,CACF;AACA,EAAc,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visually-hidden.js","names":[],"sources":["../../src/ui/visually-hidden.tsx"],"sourcesContent":["// @server-safe\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\n\nexport interface VisuallyHiddenProps\n extends React.HTMLAttributes<HTMLSpanElement> {}\n\nconst VisuallyHidden = React.forwardRef<HTMLSpanElement, VisuallyHiddenProps>(\n ({ className, ...props }, ref) => {\n return <span ref={ref} className={cn('sr-only', className)} {...props} />\n },\n)\nVisuallyHidden.displayName = 'VisuallyHidden'\n\nexport { VisuallyHidden }\n"],"mappings":";;;;AAQA,IAAM,IAAiB,EAAM,YAC1B,EAAE,cAAW,GAAG,KAAS,MACjB,kBAAC,QAAD;CAAW;CAAK,WAAW,EAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"visually-hidden.js","names":[],"sources":["../../src/ui/visually-hidden.tsx"],"sourcesContent":["// @server-safe\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\n\nexport interface VisuallyHiddenProps\n extends React.HTMLAttributes<HTMLSpanElement> {}\n\nconst VisuallyHidden = React.forwardRef<HTMLSpanElement, VisuallyHiddenProps>(\n ({ className, ...props }, ref) => {\n return <span ref={ref} className={cn('sr-only', className)} {...props} />\n },\n)\nVisuallyHidden.displayName = 'VisuallyHidden'\n\nexport { VisuallyHidden }\n"],"mappings":";;;;AAQA,IAAM,IAAiB,EAAM,YAC1B,EAAE,cAAW,GAAG,KAAS,MACjB,kBAAC,QAAD;CAAW;CAAK,WAAW,EAAG,WAAW,CAAS;CAAG,GAAI;AAAQ,CAAA,CAE5E;AACA,EAAe,cAAc"}
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @devalok/shilp-sutra
|
|
2
2
|
|
|
3
|
-
> Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.49.
|
|
3
|
+
> Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.49.4.
|
|
4
4
|
> Built on the same primitives as shadcn/ui but with DIFFERENT prop APIs — never guess from shadcn knowledge; verify every prop.
|
|
5
5
|
> This file is a ROUTER: it tells you what exists and where to get details. Do not look for prop tables here — fetch them per component (MCP tool or per-component doc file below).
|
|
6
6
|
|
package/mcp-manifest.json
CHANGED