@heyramzi/ui 0.0.0-stage → 1.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +133 -0
- package/README.md +136 -2
- package/package.json +217 -3
- package/scripts/lib/sync-core.mjs +113 -0
- package/scripts/relative-imports.mjs +39 -0
- package/scripts/sf-symbol.mjs +93 -0
- package/scripts/sync-shadcn.mjs +354 -0
- package/scripts/sync-transitions.mjs +146 -0
- package/scripts/validate-apple-symbol-exports.mjs +45 -0
- package/src/agents/agent-avatar.svelte +592 -0
- package/src/agents/agent-cast.svelte +197 -0
- package/src/agents/demo.svelte +125 -0
- package/src/agents/helpers.ts +58 -0
- package/src/agents/index.ts +11 -0
- package/src/agents/page-state.ts +70 -0
- package/src/agents/types.ts +74 -0
- package/src/agents/vendor/LICENSE +21 -0
- package/src/agents/vendor/color.ts +155 -0
- package/src/agents/vendor/draw.ts +840 -0
- package/src/agents/vendor/engine.ts +845 -0
- package/src/agents/vendor/mesh.ts +838 -0
- package/src/agents/vendor/plastic.ts +4958 -0
- package/src/agents/vendor/presets.ts +103 -0
- package/src/agents/vendor/shapes.ts +41 -0
- package/src/agents/vendor/ticker.ts +67 -0
- package/src/agents/vendor/types.ts +261 -0
- package/src/agents/vendor/wear.ts +1699 -0
- package/src/ai/ai-button/AiButton.svelte +86 -0
- package/src/ai/ai-button/index.ts +4 -0
- package/src/ai/confirmation/Confirmation.svelte +54 -0
- package/src/ai/confirmation/ConfirmationAccepted.svelte +23 -0
- package/src/ai/confirmation/ConfirmationAction.svelte +53 -0
- package/src/ai/confirmation/ConfirmationActions.svelte +24 -0
- package/src/ai/confirmation/ConfirmationFailed.svelte +23 -0
- package/src/ai/confirmation/ConfirmationRejected.svelte +23 -0
- package/src/ai/confirmation/ConfirmationRequest.svelte +25 -0
- package/src/ai/confirmation/ConfirmationTitle.svelte +25 -0
- package/src/ai/confirmation/confirmation-context.svelte.ts +22 -0
- package/src/ai/confirmation/index.ts +50 -0
- package/src/ai/generation-progress/GenerationProgress.svelte +354 -0
- package/src/ai/generation-progress/index.ts +7 -0
- package/src/ai/index.ts +111 -0
- package/src/ai/loading-state/PixelLoader.svelte +138 -0
- package/src/ai/loading-state/index.ts +5 -0
- package/src/ai/prompt-input/PromptInput.svelte +56 -0
- package/src/ai/prompt-input/PromptInputBody.svelte +19 -0
- package/src/ai/prompt-input/PromptInputSubmit.svelte +79 -0
- package/src/ai/prompt-input/PromptInputTextarea.svelte +74 -0
- package/src/ai/prompt-input/PromptInputToolbar.svelte +19 -0
- package/src/ai/prompt-input/index.ts +34 -0
- package/src/ai/reasoning/Reasoning.svelte +50 -0
- package/src/ai/reasoning/ReasoningContent.svelte +27 -0
- package/src/ai/reasoning/ReasoningTrigger.svelte +48 -0
- package/src/ai/reasoning/index.ts +22 -0
- package/src/ai/reasoning/reasoning-context.svelte.ts +20 -0
- package/src/ai/response/Response.css +32 -0
- package/src/ai/response/Response.svelte +130 -0
- package/src/ai/response/index.ts +5 -0
- package/src/ai/suggestion/Suggestion.svelte +43 -0
- package/src/ai/suggestion/Suggestions.svelte +25 -0
- package/src/ai/suggestion/index.ts +14 -0
- package/src/ai/thinking-dots/ThinkingDots.svelte +42 -0
- package/src/ai/thinking-dots/index.ts +5 -0
- package/src/ai/typing-dots/TypingDots.css +39 -0
- package/src/ai/typing-dots/TypingDots.svelte +27 -0
- package/src/ai/typing-dots/index.ts +10 -0
- package/src/ai/voice-waveform/VoiceWaveform.css +40 -0
- package/src/ai/voice-waveform/VoiceWaveform.svelte +46 -0
- package/src/ai/voice-waveform/index.ts +5 -0
- package/src/ambient.d.ts +4 -0
- package/src/animations/README.md +84 -0
- package/src/animations/avatar-group-hover.css +47 -0
- package/src/animations/card-tilt.css +74 -0
- package/src/animations/digit-pop-in.css +78 -0
- package/src/animations/dropdown.css +121 -0
- package/src/animations/index.css +23 -0
- package/src/animations/input-clear-dissolve.css +86 -0
- package/src/animations/input-shake.css +114 -0
- package/src/animations/learn-more-hover.css +48 -0
- package/src/animations/like-button.css +93 -0
- package/src/animations/modal.css +75 -0
- package/src/animations/page-side-by-side.css +63 -0
- package/src/animations/plus-menu-morph.css +97 -0
- package/src/animations/resize.css +20 -0
- package/src/animations/shimmer-text.css +72 -0
- package/src/animations/spinning-counter.css +52 -0
- package/src/animations/success-check.css +120 -0
- package/src/animations/text-swap.css +40 -0
- package/src/animations/text-swap.ts +40 -0
- package/src/animations/thinking-states.css +65 -0
- package/src/animations/upstream-coverage.test.ts +65 -0
- package/src/components/copy/clipboard.test.ts +206 -0
- package/src/components/copy/clipboard.ts +49 -0
- package/src/components/copy/copied.svelte.ts +39 -0
- package/src/components/copy/copy-button.svelte +175 -0
- package/src/components/copy/copy-button.test.ts +110 -0
- package/src/components/copy/index.ts +3 -0
- package/src/components/data-table/data-table.test.ts +134 -0
- package/src/components/data-table/data-table.ts +77 -0
- package/src/components/data-table/index.ts +20 -0
- package/src/components/data-table/infinite-scroll.svelte +69 -0
- package/src/components/data-table/pagination.svelte +62 -0
- package/src/components/data-table/row-arrow.svelte +28 -0
- package/src/components/data-table/row-context-menu.svelte +106 -0
- package/src/components/data-table/row-context-menu.test.ts +18 -0
- package/src/components/data-table/sortable-head.svelte +56 -0
- package/src/components/data-table/table-skeleton.svelte +83 -0
- package/src/components/hold-to-confirm/hold-to-confirm.svelte +232 -0
- package/src/components/hold-to-confirm/index.ts +1 -0
- package/src/components/icon/icon.svelte +23 -0
- package/src/components/icon/index.ts +1 -0
- package/src/components/language-flag/index.ts +1 -0
- package/src/components/language-flag/language-flag.svelte +60 -0
- package/src/components/list-page/detail-empty.svelte +50 -0
- package/src/components/list-page/detail-header.svelte +107 -0
- package/src/components/list-page/detail-panel.svelte +90 -0
- package/src/components/list-page/detail-row.svelte +36 -0
- package/src/components/list-page/detail-stack.svelte +33 -0
- package/src/components/list-page/empty-state.svelte +91 -0
- package/src/components/list-page/filter-item.svelte +49 -0
- package/src/components/list-page/filter-popover.svelte +88 -0
- package/src/components/list-page/filter-section.ts +12 -0
- package/src/components/list-page/grouped-list.svelte +116 -0
- package/src/components/list-page/index.ts +28 -0
- package/src/components/list-page/list-page-shell.svelte +195 -0
- package/src/components/list-page/list-table.ts +19 -0
- package/src/components/list-page/master-detail.svelte +206 -0
- package/src/components/list-page/master-detail.test.ts +37 -0
- package/src/components/list-page/notice-bar.svelte +68 -0
- package/src/components/list-page/prev-next.svelte +104 -0
- package/src/components/list-page/queue-rail.ts +25 -0
- package/src/components/list-page/rail-body.svelte +29 -0
- package/src/components/list-page/rail-empty.svelte +51 -0
- package/src/components/list-page/rail-head.svelte +49 -0
- package/src/components/list-page/rail-row.svelte +163 -0
- package/src/components/list-page/section-host.ts +24 -0
- package/src/components/list-page/section-label.svelte +39 -0
- package/src/components/list-page/step.test.ts +21 -0
- package/src/components/list-page/step.ts +10 -0
- package/src/components/list-page/toolbar-search.svelte +63 -0
- package/src/components/list-page/toolbar-trigger.ts +23 -0
- package/src/components/list-page/types.ts +1 -0
- package/src/components/locale-switch/index.ts +1 -0
- package/src/components/locale-switch/locale-switch.svelte +59 -0
- package/src/components/mark/index.ts +1 -0
- package/src/components/mark/mark.svelte +70 -0
- package/src/components/open-in/index.ts +6 -0
- package/src/components/open-in/open-in.svelte +106 -0
- package/src/components/picker/index.ts +4 -0
- package/src/components/picker/mark-picker.svelte +84 -0
- package/src/components/picker/picker.svelte +143 -0
- package/src/components/picker/trigger.ts +42 -0
- package/src/components/picker/types.ts +26 -0
- package/src/components/segmented/animated-tabs.svelte +175 -0
- package/src/components/segmented/collapse.test.ts +49 -0
- package/src/components/segmented/collapse.ts +39 -0
- package/src/components/segmented/index.ts +41 -0
- package/src/components/segmented/section-runs.svelte +79 -0
- package/src/components/segmented/segment.svelte +106 -0
- package/src/components/segmented/segmented-filter.svelte +125 -0
- package/src/components/segmented/segmented-nav.svelte +82 -0
- package/src/components/segmented/segmented-overflow.svelte +85 -0
- package/src/components/segmented/segmented-track.svelte +181 -0
- package/src/components/segmented/segmented.test.ts +20 -0
- package/src/components/segmented/segmented.ts +80 -0
- package/src/components/star-rating-input/index.ts +9 -0
- package/src/components/star-rating-input/star-rating-input.svelte +256 -0
- package/src/components/svg/index.ts +1 -0
- package/src/components/svg/svg.svelte +9 -0
- package/src/design/index.css +270 -0
- package/src/design/polish.css +159 -0
- package/src/effects/animated-beam/AnimatedBeam.svelte +132 -0
- package/src/effects/animated-beam/diagrams/BeamSvg.svelte +91 -0
- package/src/effects/animated-beam/index.ts +2 -0
- package/src/effects/animated-beam/path-calculator.svelte.ts +70 -0
- package/src/effects/animated-beam/types.ts +23 -0
- package/src/effects/border-beam/BorderBeam.svelte +223 -0
- package/src/effects/border-beam/css.test.ts +46 -0
- package/src/effects/border-beam/css.ts +574 -0
- package/src/effects/border-beam/presets.ts +699 -0
- package/src/effects/border-beam/register-properties.test.ts +74 -0
- package/src/effects/border-beam/register-properties.ts +73 -0
- package/src/effects/border-beam/types.ts +38 -0
- package/src/effects/image-comparison/ImageComparison.css +100 -0
- package/src/effects/image-comparison/ImageComparison.svelte +209 -0
- package/src/effects/image-comparison/compare.test.ts +17 -0
- package/src/effects/image-comparison/compare.ts +5 -0
- package/src/effects/image-reveal/ImageReveal.css +10 -0
- package/src/effects/image-reveal/ImageReveal.svelte +357 -0
- package/src/effects/image-reveal/index.ts +2 -0
- package/src/effects/image-reveal/types.ts +35 -0
- package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +338 -0
- package/src/effects/magic-card/MagicCard.css +47 -0
- package/src/effects/magic-card/MagicCard.svelte +57 -0
- package/src/effects/magnetic/magnetic.ts +81 -0
- package/src/effects/marquee/Marquee.css +78 -0
- package/src/effects/marquee/Marquee.svelte +160 -0
- package/src/effects/progressive-blur/ProgressiveBlur.css +9 -0
- package/src/effects/progressive-blur/ProgressiveBlur.svelte +63 -0
- package/src/effects/reveal/AnimatedGroup.svelte +101 -0
- package/src/effects/reveal/InView.svelte +77 -0
- package/src/effects/reveal/TextEffect.svelte +142 -0
- package/src/effects/reveal/index.ts +11 -0
- package/src/effects/reveal/observe.test.ts +60 -0
- package/src/effects/reveal/observe.ts +65 -0
- package/src/effects/reveal/reveal.css +141 -0
- package/src/effects/reveal/types.ts +32 -0
- package/src/effects/shader-gradient/ShaderGradient.svelte +227 -0
- package/src/effects/shader-gradient/color.ts +44 -0
- package/src/effects/shader-gradient/index.ts +5 -0
- package/src/effects/shader-gradient/renderer.ts +456 -0
- package/src/effects/shader-gradient/shaders.ts +324 -0
- package/src/effects/shader-gradient/types.ts +68 -0
- package/src/effects/spotlight/Spotlight.css +14 -0
- package/src/effects/spotlight/Spotlight.svelte +61 -0
- package/src/effects/theme-toggle/ThemeToggle.css +214 -0
- package/src/effects/theme-toggle/ThemeToggle.svelte +126 -0
- package/src/gallery.test.ts +52 -0
- package/src/icons/TradeIcon.svelte +16 -0
- package/src/icons/apple-symbol-exports.ts +135 -0
- package/src/icons/sf-symbols.ts +527 -0
- package/src/icons/trades.ts +89 -0
- package/src/index.ts +53 -0
- package/src/lib/arrive.ts +26 -0
- package/src/lib/icon-types.ts +17 -0
- package/src/lib/layout.ts +8 -0
- package/src/lib/panel.ts +21 -0
- package/src/lib/sliding-pill.ts +68 -0
- package/src/lib/theme-transition.ts +79 -0
- package/src/lib/tone.ts +48 -0
- package/src/shadcn/accordion/accordion-content.svelte +22 -0
- package/src/shadcn/accordion/accordion-item.svelte +17 -0
- package/src/shadcn/accordion/accordion-trigger.svelte +44 -0
- package/src/shadcn/accordion/accordion.svelte +19 -0
- package/src/shadcn/accordion/index.ts +16 -0
- package/src/shadcn/alert/alert-description.svelte +23 -0
- package/src/shadcn/alert/alert-title.svelte +20 -0
- package/src/shadcn/alert/alert.svelte +46 -0
- package/src/shadcn/alert/index.ts +14 -0
- package/src/shadcn/alert-dialog/alert-dialog-action.svelte +18 -0
- package/src/shadcn/alert-dialog/alert-dialog-cancel.svelte +18 -0
- package/src/shadcn/alert-dialog/alert-dialog-content.svelte +29 -0
- package/src/shadcn/alert-dialog/alert-dialog-description.svelte +17 -0
- package/src/shadcn/alert-dialog/alert-dialog-footer.svelte +20 -0
- package/src/shadcn/alert-dialog/alert-dialog-header.svelte +20 -0
- package/src/shadcn/alert-dialog/alert-dialog-overlay.svelte +17 -0
- package/src/shadcn/alert-dialog/alert-dialog-portal.svelte +7 -0
- package/src/shadcn/alert-dialog/alert-dialog-title.svelte +17 -0
- package/src/shadcn/alert-dialog/alert-dialog-trigger.svelte +7 -0
- package/src/shadcn/alert-dialog/alert-dialog.svelte +7 -0
- package/src/shadcn/alert-dialog/index.ts +37 -0
- package/src/shadcn/attachment/attachment-action.svelte +23 -0
- package/src/shadcn/attachment/attachment-actions.svelte +23 -0
- package/src/shadcn/attachment/attachment-content.svelte +23 -0
- package/src/shadcn/attachment/attachment-description.svelte +23 -0
- package/src/shadcn/attachment/attachment-group.svelte +23 -0
- package/src/shadcn/attachment/attachment-media.svelte +44 -0
- package/src/shadcn/attachment/attachment-title.svelte +23 -0
- package/src/shadcn/attachment/attachment-trigger.svelte +29 -0
- package/src/shadcn/attachment/attachment.svelte +55 -0
- package/src/shadcn/attachment/index.ts +40 -0
- package/src/shadcn/avatar/avatar-badge.svelte +26 -0
- package/src/shadcn/avatar/avatar-fallback.svelte +20 -0
- package/src/shadcn/avatar/avatar-group-count.svelte +23 -0
- package/src/shadcn/avatar/avatar-group.svelte +23 -0
- package/src/shadcn/avatar/avatar-image.svelte +17 -0
- package/src/shadcn/avatar/avatar.svelte +26 -0
- package/src/shadcn/avatar/index.ts +22 -0
- package/src/shadcn/badge/badge.svelte +54 -0
- package/src/shadcn/badge/index.ts +2 -0
- package/src/shadcn/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
- package/src/shadcn/breadcrumb/breadcrumb-item.svelte +20 -0
- package/src/shadcn/breadcrumb/breadcrumb-link.svelte +31 -0
- package/src/shadcn/breadcrumb/breadcrumb-list.svelte +23 -0
- package/src/shadcn/breadcrumb/breadcrumb-page.svelte +23 -0
- package/src/shadcn/breadcrumb/breadcrumb-separator.svelte +27 -0
- package/src/shadcn/breadcrumb/breadcrumb.svelte +21 -0
- package/src/shadcn/breadcrumb/index.ts +25 -0
- package/src/shadcn/bubble/bubble-content.svelte +31 -0
- package/src/shadcn/bubble/bubble-group.svelte +20 -0
- package/src/shadcn/bubble/bubble-reactions.svelte +52 -0
- package/src/shadcn/bubble/bubble.svelte +58 -0
- package/src/shadcn/bubble/index.ts +25 -0
- package/src/shadcn/button/button.svelte +121 -0
- package/src/shadcn/button/index.ts +17 -0
- package/src/shadcn/calendar/calendar-caption.svelte +76 -0
- package/src/shadcn/calendar/calendar-cell.svelte +19 -0
- package/src/shadcn/calendar/calendar-day.svelte +35 -0
- package/src/shadcn/calendar/calendar-grid-body.svelte +12 -0
- package/src/shadcn/calendar/calendar-grid-head.svelte +12 -0
- package/src/shadcn/calendar/calendar-grid-row.svelte +12 -0
- package/src/shadcn/calendar/calendar-grid.svelte +16 -0
- package/src/shadcn/calendar/calendar-head-cell.svelte +19 -0
- package/src/shadcn/calendar/calendar-header.svelte +19 -0
- package/src/shadcn/calendar/calendar-heading.svelte +16 -0
- package/src/shadcn/calendar/calendar-month-select.svelte +44 -0
- package/src/shadcn/calendar/calendar-month.svelte +15 -0
- package/src/shadcn/calendar/calendar-months.svelte +19 -0
- package/src/shadcn/calendar/calendar-nav.svelte +19 -0
- package/src/shadcn/calendar/calendar-next-button.svelte +31 -0
- package/src/shadcn/calendar/calendar-prev-button.svelte +31 -0
- package/src/shadcn/calendar/calendar-year-select.svelte +43 -0
- package/src/shadcn/calendar/calendar.svelte +124 -0
- package/src/shadcn/calendar/index.ts +40 -0
- package/src/shadcn/card/card-action.svelte +23 -0
- package/src/shadcn/card/card-content.svelte +20 -0
- package/src/shadcn/card/card-description.svelte +20 -0
- package/src/shadcn/card/card-footer.svelte +23 -0
- package/src/shadcn/card/card-header.svelte +23 -0
- package/src/shadcn/card/card-title.svelte +29 -0
- package/src/shadcn/card/card.svelte +25 -0
- package/src/shadcn/card/index.ts +25 -0
- package/src/shadcn/checkbox/checkbox.svelte +38 -0
- package/src/shadcn/checkbox/index.ts +6 -0
- package/src/shadcn/code-block/code-block-actions.svelte +23 -0
- package/src/shadcn/code-block/code-block-content.svelte +55 -0
- package/src/shadcn/code-block/code-block-copy-button.svelte +10 -0
- package/src/shadcn/code-block/code-block-footer.svelte +20 -0
- package/src/shadcn/code-block/code-block-header.svelte +23 -0
- package/src/shadcn/code-block/code-block-line.svelte +71 -0
- package/src/shadcn/code-block/code-block-title.svelte +23 -0
- package/src/shadcn/code-block/code-block.svelte +47 -0
- package/src/shadcn/code-block/index.ts +42 -0
- package/src/shadcn/collapsible/collapsible-content.svelte +7 -0
- package/src/shadcn/collapsible/collapsible-trigger.svelte +7 -0
- package/src/shadcn/collapsible/collapsible.svelte +11 -0
- package/src/shadcn/collapsible/index.ts +13 -0
- package/src/shadcn/command/command-dialog.svelte +66 -0
- package/src/shadcn/command/command-empty.svelte +17 -0
- package/src/shadcn/command/command-group.svelte +40 -0
- package/src/shadcn/command/command-input.svelte +64 -0
- package/src/shadcn/command/command-item.svelte +37 -0
- package/src/shadcn/command/command-link-item.svelte +20 -0
- package/src/shadcn/command/command-list.svelte +20 -0
- package/src/shadcn/command/command-loading.svelte +7 -0
- package/src/shadcn/command/command-separator.svelte +17 -0
- package/src/shadcn/command/command-shortcut.svelte +23 -0
- package/src/shadcn/command/command.svelte +28 -0
- package/src/shadcn/command/index.ts +40 -0
- package/src/shadcn/context-menu/context-menu-checkbox-item.svelte +41 -0
- package/src/shadcn/context-menu/context-menu-content.svelte +28 -0
- package/src/shadcn/context-menu/context-menu-group-heading.svelte +21 -0
- package/src/shadcn/context-menu/context-menu-group.svelte +7 -0
- package/src/shadcn/context-menu/context-menu-item.svelte +27 -0
- package/src/shadcn/context-menu/context-menu-label.svelte +27 -0
- package/src/shadcn/context-menu/context-menu-portal.svelte +7 -0
- package/src/shadcn/context-menu/context-menu-radio-group.svelte +16 -0
- package/src/shadcn/context-menu/context-menu-radio-item.svelte +35 -0
- package/src/shadcn/context-menu/context-menu-separator.svelte +17 -0
- package/src/shadcn/context-menu/context-menu-shortcut.svelte +23 -0
- package/src/shadcn/context-menu/context-menu-sub-content.svelte +25 -0
- package/src/shadcn/context-menu/context-menu-sub-trigger.svelte +29 -0
- package/src/shadcn/context-menu/context-menu-sub.svelte +7 -0
- package/src/shadcn/context-menu/context-menu-trigger.svelte +17 -0
- package/src/shadcn/context-menu/context-menu.svelte +7 -0
- package/src/shadcn/context-menu/index.ts +52 -0
- package/src/shadcn/dialog/dialog-close.svelte +11 -0
- package/src/shadcn/dialog/dialog-content.svelte +57 -0
- package/src/shadcn/dialog/dialog-description.svelte +20 -0
- package/src/shadcn/dialog/dialog-footer.svelte +32 -0
- package/src/shadcn/dialog/dialog-header.svelte +20 -0
- package/src/shadcn/dialog/dialog-overlay.svelte +20 -0
- package/src/shadcn/dialog/dialog-portal.svelte +7 -0
- package/src/shadcn/dialog/dialog-title.svelte +17 -0
- package/src/shadcn/dialog/dialog-trigger.svelte +11 -0
- package/src/shadcn/dialog/dialog.svelte +7 -0
- package/src/shadcn/dialog/index.ts +34 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-checkbox-item.svelte +43 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-content.svelte +31 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-group.svelte +7 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-item.svelte +29 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-label.svelte +27 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-portal.svelte +7 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-radio-item.svelte +34 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-separator.svelte +17 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-shortcut.svelte +23 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-sub-content.svelte +20 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-sub.svelte +7 -0
- package/src/shadcn/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
- package/src/shadcn/dropdown-menu/dropdown-menu.svelte +7 -0
- package/src/shadcn/dropdown-menu/index.ts +56 -0
- package/src/shadcn/dropdown-menu/menu-item.ts +43 -0
- package/src/shadcn/empty/empty-content.svelte +23 -0
- package/src/shadcn/empty/empty-description.svelte +23 -0
- package/src/shadcn/empty/empty-header.svelte +20 -0
- package/src/shadcn/empty/empty-media.svelte +41 -0
- package/src/shadcn/empty/empty-title.svelte +20 -0
- package/src/shadcn/empty/empty.svelte +23 -0
- package/src/shadcn/empty/index.ts +22 -0
- package/src/shadcn/field/field-content.svelte +20 -0
- package/src/shadcn/field/field-description.svelte +25 -0
- package/src/shadcn/field/field-error.svelte +58 -0
- package/src/shadcn/field/field-group.svelte +23 -0
- package/src/shadcn/field/field-label.svelte +26 -0
- package/src/shadcn/field/field-legend.svelte +29 -0
- package/src/shadcn/field/field-separator.svelte +35 -0
- package/src/shadcn/field/field-set.svelte +24 -0
- package/src/shadcn/field/field-title.svelte +23 -0
- package/src/shadcn/field/field.svelte +53 -0
- package/src/shadcn/field/index.ts +36 -0
- package/src/shadcn/field/simple-field.svelte +71 -0
- package/src/shadcn/field/simple-field.test.ts +47 -0
- package/src/shadcn/hooks/highlighter.ts +132 -0
- package/src/shadcn/hooks/is-mobile.svelte.ts +9 -0
- package/src/shadcn/hooks/use-highlighted-code.svelte.ts +98 -0
- package/src/shadcn/hover-card/hover-card-content.svelte +31 -0
- package/src/shadcn/hover-card/hover-card-portal.svelte +7 -0
- package/src/shadcn/hover-card/hover-card-trigger.svelte +7 -0
- package/src/shadcn/hover-card/hover-card.svelte +7 -0
- package/src/shadcn/hover-card/index.ts +15 -0
- package/src/shadcn/input/index.ts +9 -0
- package/src/shadcn/input/input.svelte +48 -0
- package/src/shadcn/input/secret-input.svelte +75 -0
- package/src/shadcn/input-otp/index.ts +15 -0
- package/src/shadcn/input-otp/input-otp-group.svelte +20 -0
- package/src/shadcn/input-otp/input-otp-separator.svelte +25 -0
- package/src/shadcn/input-otp/input-otp-slot.svelte +26 -0
- package/src/shadcn/input-otp/input-otp.svelte +22 -0
- package/src/shadcn/kbd/index.ts +10 -0
- package/src/shadcn/kbd/kbd-group.svelte +20 -0
- package/src/shadcn/kbd/kbd.svelte +23 -0
- package/src/shadcn/label/index.ts +7 -0
- package/src/shadcn/label/label.svelte +20 -0
- package/src/shadcn/marker/index.ts +15 -0
- package/src/shadcn/marker/marker-content.svelte +23 -0
- package/src/shadcn/marker/marker-icon.svelte +21 -0
- package/src/shadcn/marker/marker.svelte +52 -0
- package/src/shadcn/message/index.ts +22 -0
- package/src/shadcn/message/message-avatar.svelte +23 -0
- package/src/shadcn/message/message-content.svelte +23 -0
- package/src/shadcn/message/message-footer.svelte +23 -0
- package/src/shadcn/message/message-group.svelte +20 -0
- package/src/shadcn/message/message-header.svelte +23 -0
- package/src/shadcn/message/message.svelte +27 -0
- package/src/shadcn/message-scroller/index.ts +28 -0
- package/src/shadcn/message-scroller/message-scroller-button.svelte +52 -0
- package/src/shadcn/message-scroller/message-scroller-content.svelte +17 -0
- package/src/shadcn/message-scroller/message-scroller-item.svelte +22 -0
- package/src/shadcn/message-scroller/message-scroller-provider.svelte +7 -0
- package/src/shadcn/message-scroller/message-scroller-viewport.svelte +20 -0
- package/src/shadcn/message-scroller/message-scroller.svelte +20 -0
- package/src/shadcn/popover/index.ts +28 -0
- package/src/shadcn/popover/popover-close.svelte +7 -0
- package/src/shadcn/popover/popover-content.svelte +38 -0
- package/src/shadcn/popover/popover-description.svelte +20 -0
- package/src/shadcn/popover/popover-header.svelte +20 -0
- package/src/shadcn/popover/popover-portal.svelte +7 -0
- package/src/shadcn/popover/popover-title.svelte +15 -0
- package/src/shadcn/popover/popover-trigger.svelte +17 -0
- package/src/shadcn/popover/popover.svelte +7 -0
- package/src/shadcn/progress/index.ts +7 -0
- package/src/shadcn/progress/progress.svelte +30 -0
- package/src/shadcn/radio-group/index.ts +10 -0
- package/src/shadcn/radio-group/radio-group-item.svelte +32 -0
- package/src/shadcn/radio-group/radio-group.svelte +19 -0
- package/src/shadcn/scroll-area/index.ts +10 -0
- package/src/shadcn/scroll-area/scroll-area-scrollbar.svelte +30 -0
- package/src/shadcn/scroll-area/scroll-area.svelte +43 -0
- package/src/shadcn/select/index.ts +37 -0
- package/src/shadcn/select/select-content.svelte +45 -0
- package/src/shadcn/select/select-group-heading.svelte +21 -0
- package/src/shadcn/select/select-group.svelte +17 -0
- package/src/shadcn/select/select-item.svelte +38 -0
- package/src/shadcn/select/select-label.svelte +20 -0
- package/src/shadcn/select/select-portal.svelte +7 -0
- package/src/shadcn/select/select-scroll-down-button.svelte +23 -0
- package/src/shadcn/select/select-scroll-up-button.svelte +23 -0
- package/src/shadcn/select/select-separator.svelte +18 -0
- package/src/shadcn/select/select-trigger.svelte +33 -0
- package/src/shadcn/select/select.svelte +11 -0
- package/src/shadcn/separator/index.ts +7 -0
- package/src/shadcn/separator/separator.svelte +23 -0
- package/src/shadcn/sheet/index.ts +34 -0
- package/src/shadcn/sheet/sheet-close.svelte +7 -0
- package/src/shadcn/sheet/sheet-content.svelte +73 -0
- package/src/shadcn/sheet/sheet-description.svelte +17 -0
- package/src/shadcn/sheet/sheet-footer.svelte +20 -0
- package/src/shadcn/sheet/sheet-header.svelte +20 -0
- package/src/shadcn/sheet/sheet-overlay.svelte +20 -0
- package/src/shadcn/sheet/sheet-portal.svelte +7 -0
- package/src/shadcn/sheet/sheet-title.svelte +17 -0
- package/src/shadcn/sheet/sheet-trigger.svelte +7 -0
- package/src/shadcn/sheet/sheet.css +48 -0
- package/src/shadcn/sheet/sheet.svelte +7 -0
- package/src/shadcn/sidebar/constants.ts +6 -0
- package/src/shadcn/sidebar/context.svelte.ts +81 -0
- package/src/shadcn/sidebar/index.ts +75 -0
- package/src/shadcn/sidebar/sidebar-content.svelte +24 -0
- package/src/shadcn/sidebar/sidebar-footer.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-group-action.svelte +33 -0
- package/src/shadcn/sidebar/sidebar-group-content.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-group-label.svelte +33 -0
- package/src/shadcn/sidebar/sidebar-group.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-header.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-input.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-inset.svelte +23 -0
- package/src/shadcn/sidebar/sidebar-menu-action.svelte +37 -0
- package/src/shadcn/sidebar/sidebar-menu-badge.svelte +24 -0
- package/src/shadcn/sidebar/sidebar-menu-button.svelte +103 -0
- package/src/shadcn/sidebar/sidebar-menu-item.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-menu-skeleton.svelte +36 -0
- package/src/shadcn/sidebar/sidebar-menu-sub-button.svelte +39 -0
- package/src/shadcn/sidebar/sidebar-menu-sub-item.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-menu-sub.svelte +24 -0
- package/src/shadcn/sidebar/sidebar-menu.svelte +21 -0
- package/src/shadcn/sidebar/sidebar-provider.svelte +62 -0
- package/src/shadcn/sidebar/sidebar-rail.svelte +36 -0
- package/src/shadcn/sidebar/sidebar-separator.svelte +19 -0
- package/src/shadcn/sidebar/sidebar-trigger.svelte +37 -0
- package/src/shadcn/sidebar/sidebar.svelte +109 -0
- package/src/shadcn/skeleton/index.ts +7 -0
- package/src/shadcn/skeleton/skeleton.svelte +17 -0
- package/src/shadcn/slider/index.ts +7 -0
- package/src/shadcn/slider/slider.svelte +52 -0
- package/src/shadcn/sonner/index.ts +1 -0
- package/src/shadcn/sonner/sonner.svelte +31 -0
- package/src/shadcn/spinner/index.ts +1 -0
- package/src/shadcn/spinner/spinner.svelte +26 -0
- package/src/shadcn/switch/index.ts +7 -0
- package/src/shadcn/switch/switch.svelte +31 -0
- package/src/shadcn/table/index.ts +28 -0
- package/src/shadcn/table/table-body.svelte +20 -0
- package/src/shadcn/table/table-caption.svelte +20 -0
- package/src/shadcn/table/table-cell.svelte +20 -0
- package/src/shadcn/table/table-footer.svelte +20 -0
- package/src/shadcn/table/table-head.svelte +23 -0
- package/src/shadcn/table/table-header.svelte +20 -0
- package/src/shadcn/table/table-row.svelte +23 -0
- package/src/shadcn/table/table.svelte +25 -0
- package/src/shadcn/tabs/index.ts +16 -0
- package/src/shadcn/tabs/tabs-content.svelte +17 -0
- package/src/shadcn/tabs/tabs-list.svelte +36 -0
- package/src/shadcn/tabs/tabs-trigger.svelte +20 -0
- package/src/shadcn/tabs/tabs.svelte +19 -0
- package/src/shadcn/textarea/index.ts +7 -0
- package/src/shadcn/textarea/textarea.svelte +23 -0
- package/src/shadcn/toggle/index.ts +13 -0
- package/src/shadcn/toggle/toggle.svelte +53 -0
- package/src/shadcn/toggle-group/index.ts +10 -0
- package/src/shadcn/toggle-group/toggle-group-item.svelte +35 -0
- package/src/shadcn/toggle-group/toggle-group.svelte +66 -0
- package/src/shadcn/tooltip/context.ts +16 -0
- package/src/shadcn/tooltip/index.ts +19 -0
- package/src/shadcn/tooltip/tooltip-content.svelte +38 -0
- package/src/shadcn/tooltip/tooltip-portal.svelte +7 -0
- package/src/shadcn/tooltip/tooltip-provider.svelte +16 -0
- package/src/shadcn/tooltip/tooltip-trigger.svelte +7 -0
- package/src/shadcn/tooltip/tooltip.svelte +19 -0
- package/src/shadcn/trace/index.ts +29 -0
- package/src/shadcn/trace/trace-collapsible-content.svelte +29 -0
- package/src/shadcn/trace/trace-collapsible-trigger.svelte +29 -0
- package/src/shadcn/trace/trace-collapsible.svelte +20 -0
- package/src/shadcn/trace/trace-description.svelte +20 -0
- package/src/shadcn/trace/trace-icon.svelte +24 -0
- package/src/shadcn/trace/trace-line.svelte +31 -0
- package/src/shadcn/trace/trace-title.svelte +20 -0
- package/src/shadcn/trace/trace.svelte +23 -0
- package/src/shadcn/utilities.css +549 -0
- package/src/test-setup.ts +34 -0
- package/src/tokens.css +993 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The segmented control. Three components draw it, one per ARIA shape, and they
|
|
3
|
+
* share the track (`segmented.ts`) and segment body (`segment.svelte`).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// WHY no low-level `Tabs.Root`/`List`/`Trigger` here: that's `shadcn/tabs`. This
|
|
7
|
+
// is the data-driven layer on top, and `AnimatedTabs` is the way in.
|
|
8
|
+
|
|
9
|
+
// Past `SEGMENT_MAX` objects the tail collapses into a menu: `collapse.ts`
|
|
10
|
+
// decides, `segmented-overflow.svelte` draws it. No call site opts in.
|
|
11
|
+
import AnimatedTabs, {
|
|
12
|
+
type TabDef,
|
|
13
|
+
type TabsPlacement,
|
|
14
|
+
} from "./animated-tabs.svelte";
|
|
15
|
+
import SegmentedFilter from "./segmented-filter.svelte";
|
|
16
|
+
import SegmentedNav, {
|
|
17
|
+
type SegmentedNavItem,
|
|
18
|
+
} from "./segmented-nav.svelte";
|
|
19
|
+
import SectionRuns, {
|
|
20
|
+
type SectionRun,
|
|
21
|
+
type SectionStrip,
|
|
22
|
+
} from "./section-runs.svelte";
|
|
23
|
+
import { type SegmentContent } from "./segment.svelte";
|
|
24
|
+
import { segmentedRowClass, type SegmentedVariant } from "./segmented.js";
|
|
25
|
+
import { SEGMENT_MAX } from "./collapse.js";
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
AnimatedTabs,
|
|
29
|
+
SectionRuns,
|
|
30
|
+
SegmentedFilter,
|
|
31
|
+
SegmentedNav,
|
|
32
|
+
SEGMENT_MAX,
|
|
33
|
+
segmentedRowClass,
|
|
34
|
+
type SectionRun,
|
|
35
|
+
type SectionStrip,
|
|
36
|
+
type SegmentContent,
|
|
37
|
+
type SegmentedNavItem,
|
|
38
|
+
type SegmentedVariant,
|
|
39
|
+
type TabDef,
|
|
40
|
+
type TabsPlacement,
|
|
41
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { SegmentedNavItem } from "./segmented-nav.svelte";
|
|
3
|
+
|
|
4
|
+
/** One bordered track on the section row. A run with a `label` is a reference
|
|
5
|
+
* set, not a step, so `SectionRuns` titles it and sends it right. */
|
|
6
|
+
export interface SectionRun {
|
|
7
|
+
label?: string;
|
|
8
|
+
items: readonly SegmentedNavItem[];
|
|
9
|
+
/** Overrides the strip's `current` for this run, so two nested runs each light one tab. */
|
|
10
|
+
current?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// WHY runs: a section can hold a flow and the references it's read against.
|
|
14
|
+
// Each run gets its own track so they don't read as one sequence.
|
|
15
|
+
/** What an app shell publishes so a page's toolbar can draw the section strip. */
|
|
16
|
+
export interface SectionStrip {
|
|
17
|
+
runs: readonly SectionRun[];
|
|
18
|
+
current: string;
|
|
19
|
+
ariaLabel: string;
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<script lang="ts">
|
|
24
|
+
// =========================
|
|
25
|
+
// IMPORTS
|
|
26
|
+
// =========================
|
|
27
|
+
import { cn } from "@heyramzi/utils";
|
|
28
|
+
import SegmentedNav from "./segmented-nav.svelte";
|
|
29
|
+
|
|
30
|
+
// =========================
|
|
31
|
+
// TYPES
|
|
32
|
+
// =========================
|
|
33
|
+
interface Props {
|
|
34
|
+
strip: SectionStrip;
|
|
35
|
+
/** The host row's own constraints: order, width, container queries. */
|
|
36
|
+
class?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// =========================
|
|
40
|
+
// PROPS
|
|
41
|
+
// =========================
|
|
42
|
+
let { strip, class: className }: Props = $props();
|
|
43
|
+
|
|
44
|
+
// =========================
|
|
45
|
+
// DERIVED
|
|
46
|
+
// =========================
|
|
47
|
+
|
|
48
|
+
// The titled run takes the right edge, so a reference set reads apart from the steps.
|
|
49
|
+
let firstTitled = $derived(strip.runs.findIndex((run) => run.label));
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<!-- =========================== -->
|
|
53
|
+
<!-- MARKUP -->
|
|
54
|
+
<!-- =========================== -->
|
|
55
|
+
|
|
56
|
+
<!-- WHY it grows only with a titled run: `ml-auto` needs room to push into, and
|
|
57
|
+
a one-run strip keeps its width beside the search. -->
|
|
58
|
+
<div
|
|
59
|
+
class={cn(
|
|
60
|
+
"no-scrollbar flex min-w-0 items-center gap-x-3 overflow-x-auto",
|
|
61
|
+
firstTitled >= 0 && "flex-1",
|
|
62
|
+
className,
|
|
63
|
+
)}
|
|
64
|
+
>
|
|
65
|
+
{#each strip.runs as run, index (index)}
|
|
66
|
+
<div class={cn("flex shrink-0 items-center gap-2", index === firstTitled && "ml-auto")}>
|
|
67
|
+
{#if run.label}
|
|
68
|
+
<span class="text-[10px] font-medium tracking-wider text-muted-foreground uppercase">
|
|
69
|
+
{run.label}
|
|
70
|
+
</span>
|
|
71
|
+
{/if}
|
|
72
|
+
<SegmentedNav
|
|
73
|
+
items={run.items}
|
|
74
|
+
current={run.current ?? strip.current}
|
|
75
|
+
ariaLabel={run.label ?? strip.ariaLabel}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
{/each}
|
|
79
|
+
</div>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { PhosphorIcon } from "../../lib/icon-types.js";
|
|
3
|
+
|
|
4
|
+
/** Everything a segment may say, for every strip drawn from this folder. */
|
|
5
|
+
|
|
6
|
+
// WHY one shape: the tab, nav and filter strips each grew an item type and
|
|
7
|
+
// drifted, down to drawing a logo fallback two ways. Now it's one type, one renderer.
|
|
8
|
+
export type SegmentContent = {
|
|
9
|
+
label: string;
|
|
10
|
+
/** A number after the label. `0` still renders; omit for "no number". */
|
|
11
|
+
count?: number;
|
|
12
|
+
icon?: PhosphorIcon;
|
|
13
|
+
/** A square mark drawn in place of an icon, for a brand that has one. */
|
|
14
|
+
image?: string;
|
|
15
|
+
/**
|
|
16
|
+
* This segment's work is finished. Drawn as a dot after the label, and only
|
|
17
|
+
* where there is no count, because a count already says the same thing.
|
|
18
|
+
*/
|
|
19
|
+
done?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* A short note after the label, such as "Save 20%". Text where `count` is a
|
|
22
|
+
* number, in the accent colour. Drawn before a count or a done dot.
|
|
23
|
+
*/
|
|
24
|
+
badge?: string;
|
|
25
|
+
};
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<script lang="ts">
|
|
29
|
+
// =========================
|
|
30
|
+
// IMPORTS
|
|
31
|
+
// =========================
|
|
32
|
+
import { cn } from "@heyramzi/utils";
|
|
33
|
+
import { Icon } from "../icon/index.js";
|
|
34
|
+
import { Mark } from "../mark/index.js";
|
|
35
|
+
|
|
36
|
+
// =========================
|
|
37
|
+
// TYPES
|
|
38
|
+
// =========================
|
|
39
|
+
type Props = SegmentContent & {
|
|
40
|
+
/**
|
|
41
|
+
* `true`/`false` when the caller owns the state. `"auto"` when bits-ui owns it
|
|
42
|
+
* in a `data-state` on the trigger, and the stylesheet answers.
|
|
43
|
+
*/
|
|
44
|
+
active?: boolean | "auto";
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// =========================
|
|
48
|
+
// PROPS
|
|
49
|
+
// =========================
|
|
50
|
+
let { label, count, icon, image, done = false, badge, active = false }: Props = $props();
|
|
51
|
+
|
|
52
|
+
// =========================
|
|
53
|
+
// DERIVED
|
|
54
|
+
// =========================
|
|
55
|
+
let auto = $derived(active === "auto");
|
|
56
|
+
let on = $derived(active === true);
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<!-- =========================== -->
|
|
60
|
+
<!-- MARKUP -->
|
|
61
|
+
<!-- =========================== -->
|
|
62
|
+
{#if image}
|
|
63
|
+
<Mark
|
|
64
|
+
src={image}
|
|
65
|
+
{label}
|
|
66
|
+
class={cn(
|
|
67
|
+
"opacity-75 transition-opacity duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)] group-hover/segment:opacity-100",
|
|
68
|
+
on && "opacity-100",
|
|
69
|
+
auto && "group-data-[state=active]/segment:opacity-100",
|
|
70
|
+
)}
|
|
71
|
+
/>
|
|
72
|
+
{:else if icon}
|
|
73
|
+
<Icon {icon} size={16} class="shrink-0" />
|
|
74
|
+
{/if}
|
|
75
|
+
|
|
76
|
+
{label}
|
|
77
|
+
|
|
78
|
+
{#if badge}
|
|
79
|
+
<span class="text-primary text-[10px] font-semibold tracking-wide whitespace-nowrap uppercase">{badge}</span>
|
|
80
|
+
{/if}
|
|
81
|
+
|
|
82
|
+
{#if count != null}
|
|
83
|
+
<span
|
|
84
|
+
class={cn(
|
|
85
|
+
// WHY 16px, not 18: inside a 22px segment, 18 read as a chip nested in the pill.
|
|
86
|
+
|
|
87
|
+
// WHY min-width equals height with 4px padding: `min-w-5 px-1.5` made a digit
|
|
88
|
+
// 20 by 18, so `rounded-full` drew an ellipse.
|
|
89
|
+
"inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] leading-none font-medium tabular-nums",
|
|
90
|
+
"bg-muted-foreground/15 text-muted-foreground",
|
|
91
|
+
on && "bg-brand-indigo/14 text-brand-indigo",
|
|
92
|
+
auto && "group-data-[state=active]/segment:bg-brand-indigo/14 group-data-[state=active]/segment:text-brand-indigo",
|
|
93
|
+
)}
|
|
94
|
+
>
|
|
95
|
+
{count}
|
|
96
|
+
</span>
|
|
97
|
+
{:else if done}
|
|
98
|
+
<span
|
|
99
|
+
class={cn(
|
|
100
|
+
"size-1.5 shrink-0 rounded-full bg-muted-foreground/50",
|
|
101
|
+
on && "bg-brand-indigo",
|
|
102
|
+
auto && "group-data-[state=active]/segment:bg-brand-indigo",
|
|
103
|
+
)}
|
|
104
|
+
aria-hidden="true"
|
|
105
|
+
></span>
|
|
106
|
+
{/if}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<script lang="ts" generics="T extends string">
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import { cn } from "@heyramzi/utils";
|
|
6
|
+
import Segment from "./segment.svelte";
|
|
7
|
+
import type { TabDef } from "./animated-tabs.svelte";
|
|
8
|
+
import SegmentedTrack from "./segmented-track.svelte";
|
|
9
|
+
import SegmentedOverflow from "./segmented-overflow.svelte";
|
|
10
|
+
import { SEGMENT_MAX, splitSegments } from "./collapse.js";
|
|
11
|
+
import { segmentedTriggerClass } from "./segmented.js";
|
|
12
|
+
|
|
13
|
+
// =========================
|
|
14
|
+
// TYPES
|
|
15
|
+
// =========================
|
|
16
|
+
type Props = {
|
|
17
|
+
// WHY: picks which rows are listed, with no panel of its own. That's a
|
|
18
|
+
// segmented control, not a tab set.
|
|
19
|
+
|
|
20
|
+
// WHY not `AnimatedTabs`: a strip with no tabpanel is a `group` of `aria-pressed`
|
|
21
|
+
// toggles. "Tab 1 of 4" sends a screen reader looking for a panel.
|
|
22
|
+
|
|
23
|
+
// WHY `TabDef`: an option and a tab were the same five fields written twice.
|
|
24
|
+
options: readonly TabDef<T>[];
|
|
25
|
+
value: T;
|
|
26
|
+
// For a caller whose value is derived and can't be bound. Same contract as
|
|
27
|
+
// `AnimatedTabs`, so the two are picked by ARIA alone.
|
|
28
|
+
onValueChange?: (value: T) => void;
|
|
29
|
+
// Count per option, keyed by value. Merged over `options[].count`, because
|
|
30
|
+
// the counts usually arrive from the data while the labels are a constant.
|
|
31
|
+
counts?: Partial<Record<T, number>>;
|
|
32
|
+
ariaLabel: string;
|
|
33
|
+
/** Fill the row rather than sit at its natural width. On for the list-page
|
|
34
|
+
* filter strip, off for a segment sitting in a toolbar beside others. */
|
|
35
|
+
block?: boolean;
|
|
36
|
+
/** Objects the row may draw before its tail becomes a menu. See `collapse.ts`. */
|
|
37
|
+
max?: number;
|
|
38
|
+
/** What the menu segment says. Translate at the call site. */
|
|
39
|
+
moreLabel?: string;
|
|
40
|
+
class?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// =========================
|
|
44
|
+
// PROPS
|
|
45
|
+
// =========================
|
|
46
|
+
let {
|
|
47
|
+
options,
|
|
48
|
+
value = $bindable(),
|
|
49
|
+
onValueChange,
|
|
50
|
+
counts,
|
|
51
|
+
ariaLabel,
|
|
52
|
+
block = true,
|
|
53
|
+
max = SEGMENT_MAX,
|
|
54
|
+
moreLabel,
|
|
55
|
+
class: className,
|
|
56
|
+
}: Props = $props();
|
|
57
|
+
|
|
58
|
+
// =========================
|
|
59
|
+
// DERIVED
|
|
60
|
+
// =========================
|
|
61
|
+
|
|
62
|
+
// Counts arrive from data, labels are constant. Merged once here, so the pill
|
|
63
|
+
// and the overflow hint say the same thing.
|
|
64
|
+
let merged = $derived(
|
|
65
|
+
options.map((option) => {
|
|
66
|
+
const count = counts?.[option.value] ?? option.count;
|
|
67
|
+
return count === option.count ? option : { ...option, count };
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
let split = $derived(
|
|
71
|
+
splitSegments(
|
|
72
|
+
merged,
|
|
73
|
+
max,
|
|
74
|
+
merged.findIndex((option) => option.value === value),
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
function pick(next: T) {
|
|
79
|
+
value = next;
|
|
80
|
+
onValueChange?.(next);
|
|
81
|
+
}
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<!-- =========================== -->
|
|
85
|
+
<!-- MARKUP -->
|
|
86
|
+
<!-- =========================== -->
|
|
87
|
+
<!-- WHY scroll, not wrap: it's one object. Past `max` the tail collapses into
|
|
88
|
+
a menu first, so scrolling is the last resort. -->
|
|
89
|
+
<div
|
|
90
|
+
class={cn("no-scrollbar flex max-w-full min-w-0 overflow-x-auto", className)}
|
|
91
|
+
role="group"
|
|
92
|
+
aria-label={ariaLabel}
|
|
93
|
+
>
|
|
94
|
+
<SegmentedTrack fill={block}>
|
|
95
|
+
{#each split.visible as option (option.value)}
|
|
96
|
+
{@const active = value === option.value}
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
class={segmentedTriggerClass({ fill: block, active })}
|
|
100
|
+
onclick={() => pick(option.value)}
|
|
101
|
+
aria-pressed={active}
|
|
102
|
+
>
|
|
103
|
+
<Segment
|
|
104
|
+
label={option.label}
|
|
105
|
+
count={option.count}
|
|
106
|
+
icon={option.icon}
|
|
107
|
+
image={option.image}
|
|
108
|
+
done={option.done}
|
|
109
|
+
badge={option.badge}
|
|
110
|
+
{active}
|
|
111
|
+
/>
|
|
112
|
+
</button>
|
|
113
|
+
{/each}
|
|
114
|
+
{#snippet after()}
|
|
115
|
+
{#if split.hidden.length > 0}
|
|
116
|
+
<SegmentedOverflow
|
|
117
|
+
items={split.hidden}
|
|
118
|
+
onSelect={pick}
|
|
119
|
+
label={moreLabel}
|
|
120
|
+
fill={block}
|
|
121
|
+
/>
|
|
122
|
+
{/if}
|
|
123
|
+
{/snippet}
|
|
124
|
+
</SegmentedTrack>
|
|
125
|
+
</div>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { SegmentContent } from "./segment.svelte";
|
|
3
|
+
|
|
4
|
+
/** One destination. See `SegmentContent` for everything a segment can say. */
|
|
5
|
+
export type SegmentedNavItem = SegmentContent & { href: string };
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
// =========================
|
|
10
|
+
// IMPORTS
|
|
11
|
+
// =========================
|
|
12
|
+
import { cn } from "@heyramzi/utils";
|
|
13
|
+
import Segment from "./segment.svelte";
|
|
14
|
+
import SegmentedTrack from "./segmented-track.svelte";
|
|
15
|
+
import { segmentedTriggerClass } from "./segmented.js";
|
|
16
|
+
|
|
17
|
+
// =========================
|
|
18
|
+
// TYPES
|
|
19
|
+
// =========================
|
|
20
|
+
type Props = {
|
|
21
|
+
// WHY a third component: tabs are a `tablist`, filters a `group` of toggles,
|
|
22
|
+
// and this goes to sibling pages, so it's a `nav` of links with `aria-current`.
|
|
23
|
+
|
|
24
|
+
// Links give middle-click, bookmarks and the back button. The look comes from
|
|
25
|
+
// `segmented.ts` and `segment.svelte`, so nothing here can drift.
|
|
26
|
+
items: readonly SegmentedNavItem[];
|
|
27
|
+
/** The href that is current. Compared after trailing slashes are dropped. */
|
|
28
|
+
current: string;
|
|
29
|
+
ariaLabel: string;
|
|
30
|
+
/** Fill the row rather than sit at its natural width. */
|
|
31
|
+
block?: boolean;
|
|
32
|
+
class?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// =========================
|
|
36
|
+
// PROPS
|
|
37
|
+
// =========================
|
|
38
|
+
let {
|
|
39
|
+
items,
|
|
40
|
+
current,
|
|
41
|
+
ariaLabel,
|
|
42
|
+
block = false,
|
|
43
|
+
class: className,
|
|
44
|
+
}: Props = $props();
|
|
45
|
+
|
|
46
|
+
// =========================
|
|
47
|
+
// DERIVED
|
|
48
|
+
// =========================
|
|
49
|
+
let currentHref = $derived(current.replace(/\/$/, ""));
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<!-- =========================== -->
|
|
53
|
+
<!-- MARKUP -->
|
|
54
|
+
<!-- =========================== -->
|
|
55
|
+
<!-- WHY scroll, not wrap: seven brands outrun a laptop pane. A clipped segment
|
|
56
|
+
must read as "more to the right", never as a broken row. -->
|
|
57
|
+
<nav
|
|
58
|
+
class={cn("no-scrollbar flex max-w-full min-w-0 overflow-x-auto", className)}
|
|
59
|
+
aria-label={ariaLabel}
|
|
60
|
+
>
|
|
61
|
+
<SegmentedTrack fill={block}>
|
|
62
|
+
{#each items as item (item.href)}
|
|
63
|
+
{@const active = item.href.replace(/\/$/, "") === currentHref}
|
|
64
|
+
<a
|
|
65
|
+
href={item.href}
|
|
66
|
+
aria-current={active ? "page" : undefined}
|
|
67
|
+
title={item.label}
|
|
68
|
+
class={segmentedTriggerClass({ fill: block, active })}
|
|
69
|
+
>
|
|
70
|
+
<Segment
|
|
71
|
+
label={item.label}
|
|
72
|
+
count={item.count}
|
|
73
|
+
icon={item.icon}
|
|
74
|
+
image={item.image}
|
|
75
|
+
done={item.done}
|
|
76
|
+
badge={item.badge}
|
|
77
|
+
{active}
|
|
78
|
+
/>
|
|
79
|
+
</a>
|
|
80
|
+
{/each}
|
|
81
|
+
</SegmentedTrack>
|
|
82
|
+
</nav>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script lang="ts" generics="V extends string">
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import { CaretDown } from "phosphor-svelte";
|
|
6
|
+
import { Picker } from "../picker/index.js";
|
|
7
|
+
import { cn } from "@heyramzi/utils";
|
|
8
|
+
import { segmentedTriggerClass, type SegmentedVariant } from "./segmented.js";
|
|
9
|
+
import type { SegmentContent } from "./segment.svelte";
|
|
10
|
+
|
|
11
|
+
// =========================
|
|
12
|
+
// TYPES
|
|
13
|
+
// =========================
|
|
14
|
+
/** The last object in an overfull strip: one segment that opens the rest. */
|
|
15
|
+
|
|
16
|
+
// WHY the `Picker`: every "pick one of these" goes there, so keys and
|
|
17
|
+
// filtering match the command palette.
|
|
18
|
+
|
|
19
|
+
// WHY never active: `splitSegments` pulls the current item into a visible slot,
|
|
20
|
+
// so this never fights bits-ui's `data-state="open"`.
|
|
21
|
+
type Props = {
|
|
22
|
+
items: readonly (SegmentContent & { value: V })[];
|
|
23
|
+
onSelect: (value: V) => void;
|
|
24
|
+
/** What the segment says. Translate at the call site. */
|
|
25
|
+
label?: string;
|
|
26
|
+
fill?: boolean;
|
|
27
|
+
variant?: SegmentedVariant;
|
|
28
|
+
searchPlaceholder?: string;
|
|
29
|
+
emptyLabel?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// =========================
|
|
33
|
+
// PROPS
|
|
34
|
+
// =========================
|
|
35
|
+
let {
|
|
36
|
+
items,
|
|
37
|
+
onSelect,
|
|
38
|
+
label = "More",
|
|
39
|
+
fill = false,
|
|
40
|
+
variant = "pill",
|
|
41
|
+
searchPlaceholder,
|
|
42
|
+
emptyLabel,
|
|
43
|
+
}: Props = $props();
|
|
44
|
+
|
|
45
|
+
// =========================
|
|
46
|
+
// DERIVED
|
|
47
|
+
// =========================
|
|
48
|
+
|
|
49
|
+
// A count rides as the row's hint, where the strip draws it as a pill.
|
|
50
|
+
let options = $derived(
|
|
51
|
+
items.map((item) => ({
|
|
52
|
+
value: item.value,
|
|
53
|
+
label: item.label,
|
|
54
|
+
hint: item.count != null ? String(item.count) : undefined,
|
|
55
|
+
icon: item.icon,
|
|
56
|
+
})),
|
|
57
|
+
);
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<!-- =========================== -->
|
|
61
|
+
<!-- MARKUP -->
|
|
62
|
+
<!-- =========================== -->
|
|
63
|
+
<Picker
|
|
64
|
+
{options}
|
|
65
|
+
value={null}
|
|
66
|
+
onSelect={(value) => onSelect(value)}
|
|
67
|
+
triggerClass={cn(
|
|
68
|
+
segmentedTriggerClass({ variant, fill }),
|
|
69
|
+
"data-[state=open]:bg-foreground/5 data-[state=open]:text-foreground",
|
|
70
|
+
)}
|
|
71
|
+
triggerLabel={label}
|
|
72
|
+
align="end"
|
|
73
|
+
{searchPlaceholder}
|
|
74
|
+
{emptyLabel}
|
|
75
|
+
>
|
|
76
|
+
{#snippet trigger()}
|
|
77
|
+
{label}
|
|
78
|
+
<CaretDown
|
|
79
|
+
class={cn(
|
|
80
|
+
"shrink-0 transition-transform duration-150 group-data-[state=open]/segment:rotate-180",
|
|
81
|
+
"size-3.5",
|
|
82
|
+
)}
|
|
83
|
+
/>
|
|
84
|
+
{/snippet}
|
|
85
|
+
</Picker>
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import type { Snippet } from "svelte";
|
|
6
|
+
import { cn } from "@heyramzi/utils";
|
|
7
|
+
import { segmentedTrackClass, type SegmentedVariant } from "./segmented.js";
|
|
8
|
+
import { slidingPill } from "../../lib/sliding-pill.js";
|
|
9
|
+
|
|
10
|
+
// =========================
|
|
11
|
+
// TYPES
|
|
12
|
+
// =========================
|
|
13
|
+
type Props = {
|
|
14
|
+
/** Fill the row rather than sit at its natural width. */
|
|
15
|
+
fill?: boolean;
|
|
16
|
+
/** `pill`, a bordered well with a travelling highlight, or `underline`, a travelling rule. */
|
|
17
|
+
variant?: SegmentedVariant;
|
|
18
|
+
class?: string;
|
|
19
|
+
children: Snippet;
|
|
20
|
+
/**
|
|
21
|
+
* A last segment that isn't an item: today the overflow trigger. It's inside
|
|
22
|
+
* the track but outside the ARIA container, so a `tablist` owns only tabs.
|
|
23
|
+
*/
|
|
24
|
+
after?: Snippet;
|
|
25
|
+
// What the ARIA shapes differ by: `role`/`aria-label` for the filter, nothing
|
|
26
|
+
// for the nav (its <nav> has it), bits-ui's wiring for tabs.
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// =========================
|
|
31
|
+
// PROPS
|
|
32
|
+
// =========================
|
|
33
|
+
let {
|
|
34
|
+
fill = false,
|
|
35
|
+
variant = "pill",
|
|
36
|
+
class: className,
|
|
37
|
+
children,
|
|
38
|
+
after,
|
|
39
|
+
...rest
|
|
40
|
+
}: Props = $props();
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<!-- =========================== -->
|
|
44
|
+
<!-- MARKUP -->
|
|
45
|
+
<!-- =========================== -->
|
|
46
|
+
<!-- WHY its own component: the active segment is a travelling pill, and the
|
|
47
|
+
pill needs one element inside the track that no caller can forget. -->
|
|
48
|
+
<div
|
|
49
|
+
{...rest}
|
|
50
|
+
data-variant={variant}
|
|
51
|
+
class={cn("track relative", segmentedTrackClass({ variant, fill }), className)}
|
|
52
|
+
>
|
|
53
|
+
<!-- Renders at zero width until the attachment measures, so a server-rendered
|
|
54
|
+
strip shows the segment's own background and never an empty pill. -->
|
|
55
|
+
<span class="pill" aria-hidden="true" {@attach slidingPill}></span>
|
|
56
|
+
{@render children()}
|
|
57
|
+
{@render after?.()}
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<!-- =========================== -->
|
|
61
|
+
<!-- STYLES -->
|
|
62
|
+
<!-- =========================== -->
|
|
63
|
+
<style>
|
|
64
|
+
/* Inset separators keep the strip one bordered object. */
|
|
65
|
+
.track :global(.ui-segment)::before {
|
|
66
|
+
content: "";
|
|
67
|
+
position: absolute;
|
|
68
|
+
top: 22%;
|
|
69
|
+
bottom: 22%;
|
|
70
|
+
left: 0;
|
|
71
|
+
width: 1px;
|
|
72
|
+
background-color: var(--color-border);
|
|
73
|
+
transition: opacity var(--duration-fast) var(--ease-smooth-out);
|
|
74
|
+
pointer-events: none;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.track :global(.pill + .ui-segment)::before,
|
|
78
|
+
.track :global([data-slot="tabs-list"] > .ui-segment:first-child)::before {
|
|
79
|
+
display: none;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* The highlight and hover wash hide the seams on either side. */
|
|
83
|
+
.track :global(.ui-segment[data-state="active"])::before,
|
|
84
|
+
.track :global(.ui-segment[aria-current="page"])::before,
|
|
85
|
+
.track :global(.ui-segment[aria-pressed="true"])::before,
|
|
86
|
+
.track :global(.ui-segment[data-state="active"] + .ui-segment)::before,
|
|
87
|
+
.track :global(.ui-segment[aria-current="page"] + .ui-segment)::before,
|
|
88
|
+
.track :global(.ui-segment[aria-pressed="true"] + .ui-segment)::before,
|
|
89
|
+
.track :global([data-slot="tabs-list"]:has(> .ui-segment:last-child[data-state="active"]) + .ui-segment)::before,
|
|
90
|
+
.track :global(.ui-segment:hover)::before,
|
|
91
|
+
.track :global(.ui-segment:hover + .ui-segment)::before {
|
|
92
|
+
opacity: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* The label's colour lands with the pill, on the same fast step. */
|
|
96
|
+
.track :global(.ui-segment) {
|
|
97
|
+
transition:
|
|
98
|
+
background-color var(--duration-quick) var(--ease-smooth-out),
|
|
99
|
+
color var(--duration-fast) var(--ease-smooth-out),
|
|
100
|
+
transform var(--duration-micro) var(--ease-smooth-out);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* A faint wash in the well says which segment the pointer would pick. */
|
|
104
|
+
@media (hover: hover) {
|
|
105
|
+
.track
|
|
106
|
+
:global(
|
|
107
|
+
.ui-segment:hover:not([data-state="active"], [aria-current="page"], [aria-pressed="true"], [data-state="open"])
|
|
108
|
+
) {
|
|
109
|
+
background-color: color-mix(in oklch, var(--color-muted) 55%, transparent);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.pill {
|
|
114
|
+
position: absolute;
|
|
115
|
+
top: 2px;
|
|
116
|
+
bottom: 2px;
|
|
117
|
+
left: 0;
|
|
118
|
+
z-index: 0;
|
|
119
|
+
width: 0;
|
|
120
|
+
opacity: 0;
|
|
121
|
+
/* Concentric with the border and the 2px inset. */
|
|
122
|
+
border-radius: calc(var(--radius-lg) - 3px);
|
|
123
|
+
background-color: var(--color-muted);
|
|
124
|
+
box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--color-foreground) 6%, transparent);
|
|
125
|
+
transform: translateX(0);
|
|
126
|
+
/* transitions-dev `16-tabs-sliding`: the fast step, 250ms. */
|
|
127
|
+
transition:
|
|
128
|
+
transform var(--duration-fast) var(--ease-smooth-out),
|
|
129
|
+
width var(--duration-fast) var(--ease-smooth-out),
|
|
130
|
+
opacity var(--duration-quick) var(--ease-smooth-out);
|
|
131
|
+
will-change: transform, width;
|
|
132
|
+
pointer-events: none;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Before the attachment measures, the active segment draws the pill itself,
|
|
136
|
+
so a server-rendered strip is right before any JS arrives. */
|
|
137
|
+
.track:not([data-pill="on"]) :global(:is([data-state="active"], [aria-current="page"], [aria-pressed="true"])) {
|
|
138
|
+
background-color: var(--color-muted);
|
|
139
|
+
box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--color-foreground) 6%, transparent);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* The underline: no seams, no wash, and the pill becomes a 2px rule on the
|
|
143
|
+
hairline. `--segmented-rule` lets a brand colour it from a class. */
|
|
144
|
+
.track[data-variant="underline"] :global(.ui-segment)::before {
|
|
145
|
+
display: none;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@media (hover: hover) {
|
|
149
|
+
.track[data-variant="underline"]
|
|
150
|
+
:global(
|
|
151
|
+
.ui-segment:hover:not([data-state="active"], [aria-current="page"], [aria-pressed="true"], [data-state="open"])
|
|
152
|
+
) {
|
|
153
|
+
background-color: transparent;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.track[data-variant="underline"] .pill {
|
|
158
|
+
top: auto;
|
|
159
|
+
bottom: -1px;
|
|
160
|
+
height: 2px;
|
|
161
|
+
border-radius: 1px;
|
|
162
|
+
background-color: var(--segmented-rule, var(--color-foreground));
|
|
163
|
+
box-shadow: none;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.track[data-variant="underline"]:not([data-pill="on"])
|
|
167
|
+
:global(:is([data-state="active"], [aria-current="page"], [aria-pressed="true"])) {
|
|
168
|
+
background-color: transparent;
|
|
169
|
+
box-shadow: inset 0 -2px 0 var(--segmented-rule, var(--color-foreground));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@media (prefers-reduced-motion: reduce) {
|
|
173
|
+
.pill {
|
|
174
|
+
transition: opacity var(--duration-quick) var(--ease-smooth-out);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.track :global(.ui-segment) {
|
|
178
|
+
transition: none;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
</style>
|