@heyramzi/ui 0.0.0-stage → 1.53.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +590 -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 +112 -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 +186 -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 +263 -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,186 @@
|
|
|
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
|
+
interface 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
|
+
opacity: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@media (hover: hover) and (pointer: fine) {
|
|
94
|
+
.track :global(.ui-segment:hover)::before,
|
|
95
|
+
.track :global(.ui-segment:hover + .ui-segment)::before {
|
|
96
|
+
opacity: 0;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* The label's colour lands with the pill, on the same fast step. */
|
|
101
|
+
.track :global(.ui-segment) {
|
|
102
|
+
transition:
|
|
103
|
+
background-color var(--duration-quick) var(--ease-smooth-out),
|
|
104
|
+
color var(--duration-fast) var(--ease-smooth-out),
|
|
105
|
+
transform var(--duration-micro) var(--ease-smooth-out);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* A faint wash in the well says which segment the pointer would pick. */
|
|
109
|
+
@media (hover: hover) {
|
|
110
|
+
.track
|
|
111
|
+
:global(
|
|
112
|
+
.ui-segment:hover:not([data-state="active"], [aria-current="page"], [aria-pressed="true"], [data-state="open"])
|
|
113
|
+
) {
|
|
114
|
+
background-color: color-mix(in oklch, var(--color-muted) 55%, transparent);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.pill {
|
|
119
|
+
position: absolute;
|
|
120
|
+
top: 2px;
|
|
121
|
+
bottom: 2px;
|
|
122
|
+
left: 0;
|
|
123
|
+
z-index: 0;
|
|
124
|
+
width: 0;
|
|
125
|
+
opacity: 0;
|
|
126
|
+
/* Concentric with the border and the 2px inset. */
|
|
127
|
+
border-radius: calc(var(--radius-lg) - 3px);
|
|
128
|
+
background-color: var(--color-muted);
|
|
129
|
+
box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--color-foreground) 6%, transparent);
|
|
130
|
+
transform: translateX(0);
|
|
131
|
+
/* transitions-dev `16-tabs-sliding`: the fast step, 250ms. */
|
|
132
|
+
transition:
|
|
133
|
+
transform var(--duration-fast) var(--ease-smooth-out),
|
|
134
|
+
width var(--duration-fast) var(--ease-smooth-out),
|
|
135
|
+
opacity var(--duration-quick) var(--ease-smooth-out);
|
|
136
|
+
will-change: transform, width;
|
|
137
|
+
pointer-events: none;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Before the attachment measures, the active segment draws the pill itself,
|
|
141
|
+
so a server-rendered strip is right before any JS arrives. */
|
|
142
|
+
.track:not([data-pill="on"]) :global(:is([data-state="active"], [aria-current="page"], [aria-pressed="true"])) {
|
|
143
|
+
background-color: var(--color-muted);
|
|
144
|
+
box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--color-foreground) 6%, transparent);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* The underline: no seams, no wash, and the pill becomes a 2px rule on the
|
|
148
|
+
hairline. `--segmented-rule` lets a brand colour it from a class. */
|
|
149
|
+
.track[data-variant="underline"] :global(.ui-segment)::before {
|
|
150
|
+
display: none;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@media (hover: hover) {
|
|
154
|
+
.track[data-variant="underline"]
|
|
155
|
+
:global(
|
|
156
|
+
.ui-segment:hover:not([data-state="active"], [aria-current="page"], [aria-pressed="true"], [data-state="open"])
|
|
157
|
+
) {
|
|
158
|
+
background-color: transparent;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.track[data-variant="underline"] .pill {
|
|
163
|
+
top: auto;
|
|
164
|
+
bottom: -1px;
|
|
165
|
+
height: 2px;
|
|
166
|
+
border-radius: 1px;
|
|
167
|
+
background-color: var(--segmented-rule, var(--color-foreground));
|
|
168
|
+
box-shadow: none;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.track[data-variant="underline"]:not([data-pill="on"])
|
|
172
|
+
:global(:is([data-state="active"], [aria-current="page"], [aria-pressed="true"])) {
|
|
173
|
+
background-color: transparent;
|
|
174
|
+
box-shadow: inset 0 -2px 0 var(--segmented-rule, var(--color-foreground));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@media (prefers-reduced-motion: reduce) {
|
|
178
|
+
.pill {
|
|
179
|
+
transition: opacity var(--duration-quick) var(--ease-smooth-out);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.track :global(.ui-segment) {
|
|
183
|
+
transition: none;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
2
|
+
import { segmentedTrackClass } from "./segmented.js";
|
|
3
|
+
|
|
4
|
+
describe("segmentedTrackClass", () => {
|
|
5
|
+
// Every segmented control shares one height, whatever its look.
|
|
6
|
+
it("holds one height across variants", () => {
|
|
7
|
+
for (const variant of ["pill", "underline"] as const) {
|
|
8
|
+
const classes = segmentedTrackClass({ variant }).split(" ");
|
|
9
|
+
expect(classes).toContain("h-7");
|
|
10
|
+
expect(classes).toContain("any-pointer-coarse:h-10");
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("draws the pill as a bordered well and the underline as a hairline", () => {
|
|
15
|
+
expect(segmentedTrackClass({ variant: "pill" })).toContain("rounded-lg");
|
|
16
|
+
const underline = segmentedTrackClass({ variant: "underline" });
|
|
17
|
+
expect(underline).toContain("border-b");
|
|
18
|
+
expect(underline).not.toContain("rounded-lg");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { tv } from "tailwind-variants";
|
|
2
|
+
import { pageGutterClass } from "../../lib/layout.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The one look for a segmented control: the track and trigger here, what a
|
|
6
|
+
* segment says in `segment.svelte`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// WHY a recipe, not one component: tabs are a `tablist`, filters a `group` of
|
|
10
|
+
// `aria-pressed` toggles, page links a `nav`. Only the ARIA shape may differ.
|
|
11
|
+
|
|
12
|
+
// The look used to drift three ways, one with an at-rest shadow. All three read from here now.
|
|
13
|
+
export const segmentedTrackClass = tv({
|
|
14
|
+
// WHY the height lives on the track: it carries the inset, so an `h-7` trigger
|
|
15
|
+
// drew 30px beside a 28px button. One toolbar row came out at five heights.
|
|
16
|
+
|
|
17
|
+
// WHY no size variant: `sm` drew 24px, and every tab control must share one height.
|
|
18
|
+
base: "h-7 any-pointer-coarse:h-10 shrink-0 items-stretch",
|
|
19
|
+
variants: {
|
|
20
|
+
// `pill`: a bordered well with a travelling highlight. `underline`: bare
|
|
21
|
+
// labels over one hairline, the active one marked by a rule that travels.
|
|
22
|
+
variant: {
|
|
23
|
+
pill: "overflow-hidden rounded-lg border border-border p-0.5",
|
|
24
|
+
underline: "gap-5 border-b border-border",
|
|
25
|
+
},
|
|
26
|
+
// Fill the row rather than sit at its natural width. For a narrow rail.
|
|
27
|
+
fill: { true: "flex w-full", false: "inline-flex w-fit" },
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: { variant: "pill", fill: false },
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export type SegmentedVariant = "pill" | "underline";
|
|
33
|
+
|
|
34
|
+
export const segmentedTriggerClass = tv({
|
|
35
|
+
base: [
|
|
36
|
+
// `t-press` is the kit's one press: colour, shadow and a pixel of travel.
|
|
37
|
+
|
|
38
|
+
// `relative z-[1]` keeps the label above the travelling pill, or the pill
|
|
39
|
+
// paints over the word it highlights.
|
|
40
|
+
|
|
41
|
+
// `ui-segment` isn't a style. `segmented-track.svelte` matches on it to time
|
|
42
|
+
// the label colour with the pill.
|
|
43
|
+
"ui-segment group/segment t-press relative z-[1] inline-flex items-center justify-center gap-1.5 whitespace-nowrap font-medium select-none",
|
|
44
|
+
"text-muted-foreground hover:text-foreground",
|
|
45
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
46
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
47
|
+
// WHY an inset focus ring: the track clips to its rounded corners, so an
|
|
48
|
+
// outside ring was cut in half on the end segments.
|
|
49
|
+
"focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-inset focus-visible:outline-none",
|
|
50
|
+
// WHY no height on the trigger: an icon makes a segment taller than a word.
|
|
51
|
+
// `h-full` against the track's `items-stretch` keeps every segment the same box.
|
|
52
|
+
"h-full text-xs",
|
|
53
|
+
],
|
|
54
|
+
variants: {
|
|
55
|
+
// The pill is concentric with the track's border and its 2px inset. The
|
|
56
|
+
// underline has no well to be concentric with, so no padding either: the
|
|
57
|
+
// rule under a label is as wide as the label.
|
|
58
|
+
variant: {
|
|
59
|
+
pill: "rounded-[calc(var(--radius-lg)-3px)] px-2.5 any-pointer-coarse:px-3.5",
|
|
60
|
+
underline: "rounded-sm px-0",
|
|
61
|
+
},
|
|
62
|
+
// WHY `min-w-fit flex-auto`, not equal fractions: fractions cut "Approved" to
|
|
63
|
+
// "Appro…" in a 280px rail. Each segment claims its label and shares the slack.
|
|
64
|
+
fill: { true: "min-w-fit flex-auto", false: "" },
|
|
65
|
+
active: { true: "text-foreground", false: "" },
|
|
66
|
+
},
|
|
67
|
+
defaultVariants: { variant: "pill", fill: false, active: false },
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The row a segmented strip sits in, and the one recipe for a navigation row
|
|
72
|
+
* under the app header. Written per caller it produced two different left
|
|
73
|
+
* edges.
|
|
74
|
+
*/
|
|
75
|
+
// WHY no bottom hairline: with a section strip and a page toolbar stacked it
|
|
76
|
+
// drew two rules for one boundary. The gap is the boundary.
|
|
77
|
+
export const segmentedRowClass = `flex shrink-0 flex-wrap items-center gap-x-4 gap-y-2 py-2 ${pageGutterClass}`;
|
|
78
|
+
|
|
79
|
+
/** The active style, for a strip whose state lives in a `data-active` attribute. */
|
|
80
|
+
export const segmentedActiveClass = "data-active:text-foreground";
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/**
|
|
3
|
+
* Two sizes, picked by the job: `sm` reads a value back in a row or cell,
|
|
4
|
+
* `lg` is the ask.
|
|
5
|
+
*/
|
|
6
|
+
// A middle `md` step used to exist. Nobody could place it, so the same question
|
|
7
|
+
// ended up asked at three sizes.
|
|
8
|
+
export type StarRatingSize = "sm" | "lg";
|
|
9
|
+
|
|
10
|
+
const STAR_SIZE: Record<StarRatingSize, string> = {
|
|
11
|
+
sm: "size-4",
|
|
12
|
+
lg: "size-7",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const HIT_SIZE: Record<StarRatingSize, string> = {
|
|
16
|
+
sm: "p-0.5",
|
|
17
|
+
lg: "p-1",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Pop duration plus the longest stagger, after which the class can come off. */
|
|
21
|
+
const POP_WINDOW_MS = 350 + 4 * 40;
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<script lang="ts">
|
|
25
|
+
import { Star } from "phosphor-svelte";
|
|
26
|
+
import { cn } from "@heyramzi/utils";
|
|
27
|
+
|
|
28
|
+
interface Props {
|
|
29
|
+
/** Current rating value (0 = unrated). */
|
|
30
|
+
value: number;
|
|
31
|
+
/** Maximum rating. Defaults to 5. */
|
|
32
|
+
max?: number;
|
|
33
|
+
/** Visual size of each star. `sm` reads back, `lg` asks. */
|
|
34
|
+
size?: StarRatingSize;
|
|
35
|
+
/** Render as static display, no hover or click. */
|
|
36
|
+
readonly?: boolean;
|
|
37
|
+
/** Disable interaction. */
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
/** Form field name for native submission. When set, a hidden input is rendered. */
|
|
40
|
+
name?: string;
|
|
41
|
+
/** ARIA label for the radiogroup. */
|
|
42
|
+
ariaLabel?: string;
|
|
43
|
+
/** Per-step descriptive labels (length must equal max). */
|
|
44
|
+
labels?: readonly string[];
|
|
45
|
+
/** Show the label of the currently hovered/selected step beside the stars. */
|
|
46
|
+
showLabel?: boolean;
|
|
47
|
+
class?: string;
|
|
48
|
+
onchange?: (value: number) => void;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
let {
|
|
52
|
+
value = $bindable(0),
|
|
53
|
+
max = 5,
|
|
54
|
+
size = "sm",
|
|
55
|
+
readonly = false,
|
|
56
|
+
disabled = false,
|
|
57
|
+
name,
|
|
58
|
+
ariaLabel,
|
|
59
|
+
labels,
|
|
60
|
+
showLabel = false,
|
|
61
|
+
class: className,
|
|
62
|
+
onchange,
|
|
63
|
+
}: Props = $props();
|
|
64
|
+
|
|
65
|
+
let hover = $state(0);
|
|
66
|
+
/** Held for the length of the pop, then dropped so the next pick can replay it. */
|
|
67
|
+
let popping = $state(false);
|
|
68
|
+
let popTimer: ReturnType<typeof setTimeout> | undefined;
|
|
69
|
+
let displayed = $derived(hover || value);
|
|
70
|
+
let starSize = $derived(STAR_SIZE[size]);
|
|
71
|
+
let hitSize = $derived(HIT_SIZE[size]);
|
|
72
|
+
let groupAriaLabel = $derived(ariaLabel ?? `Rating, ${max} stars`);
|
|
73
|
+
let labelText = $derived(
|
|
74
|
+
displayed > 0 && labels && labels.length >= displayed ? labels[displayed - 1] : "",
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
function buttonAriaLabel(step: number): string {
|
|
78
|
+
const stepLabel = labels?.[step - 1];
|
|
79
|
+
const base = `${step} of ${max}`;
|
|
80
|
+
return stepLabel ? `${base} (${stepLabel})` : base;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function setRating(step: number) {
|
|
84
|
+
if (readonly || disabled) return;
|
|
85
|
+
value = step;
|
|
86
|
+
onchange?.(step);
|
|
87
|
+
|
|
88
|
+
// Drop the class before re-adding it, or a second pick inside the animation
|
|
89
|
+
// window finds it already set and nothing replays.
|
|
90
|
+
popping = false;
|
|
91
|
+
clearTimeout(popTimer);
|
|
92
|
+
requestAnimationFrame(() => {
|
|
93
|
+
popping = true;
|
|
94
|
+
popTimer = setTimeout(() => (popping = false), POP_WINDOW_MS);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function handleEnter(step: number) {
|
|
99
|
+
if (readonly || disabled) return;
|
|
100
|
+
hover = step;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function handleLeave() {
|
|
104
|
+
if (readonly || disabled) return;
|
|
105
|
+
hover = 0;
|
|
106
|
+
}
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<!-- One star, drawn once. It used to be written twice, once per branch, and the
|
|
110
|
+
two copies had already drifted apart on the glow. -->
|
|
111
|
+
{#snippet star(step: number)}
|
|
112
|
+
<Star
|
|
113
|
+
class={cn("star", starSize, step <= displayed && "is-filled")}
|
|
114
|
+
weight={step <= displayed ? "fill" : "regular"}
|
|
115
|
+
/>
|
|
116
|
+
{/snippet}
|
|
117
|
+
|
|
118
|
+
<div class={cn("rating", `rating-${size}`, className)}>
|
|
119
|
+
<div
|
|
120
|
+
class={cn("stars", popping && "is-popping")}
|
|
121
|
+
role={readonly ? "img" : "radiogroup"}
|
|
122
|
+
aria-label={groupAriaLabel}
|
|
123
|
+
onmouseleave={handleLeave}
|
|
124
|
+
>
|
|
125
|
+
{#each Array(max) as _, i (i)}
|
|
126
|
+
{@const step = i + 1}
|
|
127
|
+
{#if readonly}
|
|
128
|
+
<span class={cn("hit", hitSize)}>{@render star(step)}</span>
|
|
129
|
+
{:else}
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
role="radio"
|
|
133
|
+
aria-checked={value === step}
|
|
134
|
+
aria-label={buttonAriaLabel(step)}
|
|
135
|
+
{disabled}
|
|
136
|
+
data-on={step <= value}
|
|
137
|
+
onclick={() => setRating(step)}
|
|
138
|
+
onmouseenter={() => handleEnter(step)}
|
|
139
|
+
onfocus={() => handleEnter(step)}
|
|
140
|
+
onblur={handleLeave}
|
|
141
|
+
style:--pop-delay="{i * 40}ms"
|
|
142
|
+
class={cn("hit hit-button", hitSize, disabled && "is-disabled")}
|
|
143
|
+
>
|
|
144
|
+
{@render star(step)}
|
|
145
|
+
</button>
|
|
146
|
+
{/if}
|
|
147
|
+
{/each}
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
{#if showLabel && labelText}
|
|
151
|
+
<span class="rating-caption">{labelText}</span>
|
|
152
|
+
{/if}
|
|
153
|
+
|
|
154
|
+
{#if name}
|
|
155
|
+
<input type="hidden" {name} value={value} />
|
|
156
|
+
{/if}
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<style>
|
|
160
|
+
.rating {
|
|
161
|
+
--like-pop: var(--duration-medium);
|
|
162
|
+
--like-pop-ease: var(--ease-smooth-out);
|
|
163
|
+
--like-fill: var(--duration-quick);
|
|
164
|
+
--like-ease: var(--ease-smooth-out);
|
|
165
|
+
|
|
166
|
+
display: flex;
|
|
167
|
+
align-items: center;
|
|
168
|
+
gap: 0.25rem;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.stars {
|
|
172
|
+
display: flex;
|
|
173
|
+
align-items: center;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.hit {
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
border-radius: var(--radius-sm);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.hit-button {
|
|
182
|
+
transition: transform var(--duration-quick, 150ms) var(--ease-smooth-out, ease);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.hit-button:focus-visible {
|
|
186
|
+
outline: none;
|
|
187
|
+
box-shadow: 0 0 0 2px var(--color-ring);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@media (hover: hover) and (pointer: fine) {
|
|
191
|
+
.hit-button:not(.is-disabled):hover {
|
|
192
|
+
transform: scale(1.1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.hit-button:not(.is-disabled):active {
|
|
197
|
+
transform: scale(0.95);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.is-disabled {
|
|
201
|
+
cursor: not-allowed;
|
|
202
|
+
opacity: 0.6;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.rating :global(.star) {
|
|
206
|
+
color: var(--color-border);
|
|
207
|
+
transition: color var(--like-fill) var(--like-ease);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.rating :global(.star.is-filled) {
|
|
211
|
+
color: var(--color-warning-400);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* The glow is the size's business, not a prop: it belongs to the ask, where
|
|
215
|
+
the stars are large enough to carry it, and it turned to mud at 16px. */
|
|
216
|
+
.rating-lg :global(.star.is-filled) {
|
|
217
|
+
filter: drop-shadow(0 0 4px color-mix(in oklab, var(--color-warning-400) 45%, transparent));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* transitions.dev like-button pop. Every selected star pops, left to right,
|
|
221
|
+
as one gesture. A class on the group drives it, since rebuilding the row
|
|
222
|
+
would drop keyboard focus. */
|
|
223
|
+
.is-popping .hit-button[data-on="true"] {
|
|
224
|
+
animation: t-like-pop var(--like-pop) var(--like-pop-ease) var(--pop-delay, 0ms);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@keyframes t-like-pop {
|
|
228
|
+
0% {
|
|
229
|
+
transform: scale(1);
|
|
230
|
+
}
|
|
231
|
+
30% {
|
|
232
|
+
transform: scale(0.82);
|
|
233
|
+
}
|
|
234
|
+
100% {
|
|
235
|
+
transform: scale(1);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.rating-caption {
|
|
240
|
+
font-size: 0.75rem;
|
|
241
|
+
font-weight: 500;
|
|
242
|
+
font-variant-numeric: tabular-nums;
|
|
243
|
+
color: var(--color-muted-foreground);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@media (prefers-reduced-motion: reduce) {
|
|
247
|
+
.hit-button,
|
|
248
|
+
.is-popping .hit-button[data-on="true"] {
|
|
249
|
+
animation: none;
|
|
250
|
+
transition: none;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.hit-button:not(.is-disabled):active {
|
|
254
|
+
transform: none;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@media (hover: hover) and (pointer: fine) {
|
|
258
|
+
.hit-button:not(.is-disabled):hover {
|
|
259
|
+
transform: none;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Svg } from "./svg.svelte";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Renders a drawing kept in its own .svg file, imported with `?raw`.
|
|
3
|
+
// The markup is ours and static, so rendering it as HTML is safe.
|
|
4
|
+
let { src, class: className = "" }: { src: string; class?: string } = $props();
|
|
5
|
+
|
|
6
|
+
const markup = $derived(className ? src.replace("<svg", `<svg class="${className}"`) : src);
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
{@html markup}
|