@devalok/shilp-sutra 0.55.1 → 0.57.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/BREAKING.json +13 -0
- package/MIGRATION.md +163 -0
- package/README.md +16 -1
- package/dist/_chunks/badge-group.js +92 -78
- package/dist/_chunks/badge-group.js.map +1 -1
- package/dist/_chunks/chat.js +91 -75
- package/dist/_chunks/chat.js.map +1 -1
- package/dist/_chunks/document-preview.js +1 -1
- package/dist/_chunks/document-preview.js.map +1 -1
- package/dist/_chunks/emoji-suggestion.js +32 -30
- package/dist/_chunks/emoji-suggestion.js.map +1 -1
- package/dist/_chunks/mention-suggestion.js +36 -35
- package/dist/_chunks/mention-suggestion.js.map +1 -1
- package/dist/_chunks/oauth-button.js +1 -1
- package/dist/_chunks/oauth-button.js.map +1 -1
- package/dist/_chunks/primitives-internal.js +437 -537
- package/dist/_chunks/primitives-internal.js.map +1 -1
- package/dist/_chunks/success.js +2 -2
- package/dist/_chunks/success.js.map +1 -1
- package/dist/_chunks/tree-view.js +1 -1
- package/dist/_chunks/tree-view.js.map +1 -1
- package/dist/_chunks/use-calendar.js +4 -4
- package/dist/_chunks/use-calendar.js.map +1 -1
- package/dist/ai/blocks/text.js +1 -1
- package/dist/ai/blocks/text.js.map +1 -1
- package/dist/ai/command-bar.js +9 -9
- package/dist/ai/command-bar.js.map +1 -1
- package/dist/ai/conversation.js +2 -2
- package/dist/ai/conversation.js.map +1 -1
- package/dist/composed/activity-feed.js +1 -1
- package/dist/composed/activity-feed.js.map +1 -1
- package/dist/composed/avatar-group.d.ts +7 -2
- package/dist/composed/avatar-group.d.ts.map +1 -1
- package/dist/composed/avatar-group.js +3 -3
- package/dist/composed/avatar-group.js.map +1 -1
- package/dist/composed/command-palette.js +7 -7
- package/dist/composed/command-palette.js.map +1 -1
- package/dist/composed/content-card.js +20 -20
- package/dist/composed/content-card.js.map +1 -1
- package/dist/composed/date-picker/index.js +4 -4
- package/dist/composed/date-picker/index.js.map +1 -1
- package/dist/composed/diff.d.ts +23 -2
- package/dist/composed/diff.d.ts.map +1 -1
- package/dist/composed/diff.js +341 -260
- package/dist/composed/diff.js.map +1 -1
- package/dist/composed/emoji-picker.js +3 -3
- package/dist/composed/emoji-picker.js.map +1 -1
- package/dist/composed/error-boundary.js +2 -2
- package/dist/composed/error-boundary.js.map +1 -1
- package/dist/composed/extensions/emoji-node.js +8 -7
- package/dist/composed/extensions/emoji-node.js.map +1 -1
- package/dist/composed/file-preview.js +1 -1
- package/dist/composed/file-preview.js.map +1 -1
- package/dist/composed/filter-bar.js +1 -1
- package/dist/composed/filter-bar.js.map +1 -1
- package/dist/composed/inline-edit.js +1 -1
- package/dist/composed/inline-edit.js.map +1 -1
- package/dist/composed/loading-skeleton.js +3 -3
- package/dist/composed/loading-skeleton.js.map +1 -1
- package/dist/composed/master-detail.js +1 -1
- package/dist/composed/master-detail.js.map +1 -1
- package/dist/composed/member-picker.js +1 -1
- package/dist/composed/member-picker.js.map +1 -1
- package/dist/composed/multi-select-popover.js +1 -1
- package/dist/composed/multi-select-popover.js.map +1 -1
- package/dist/composed/page-skeletons.js +4 -4
- package/dist/composed/page-skeletons.js.map +1 -1
- package/dist/composed/responsive-modal.js +1 -1
- package/dist/composed/responsive-modal.js.map +1 -1
- package/dist/composed/rich-chat-input.js +549 -540
- package/dist/composed/rich-chat-input.js.map +1 -1
- package/dist/composed/rich-text-editor.js +206 -198
- package/dist/composed/rich-text-editor.js.map +1 -1
- package/dist/composed/schedule-view.js +5 -5
- package/dist/composed/schedule-view.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -3
- package/dist/hooks/use-container-size.d.ts +18 -0
- package/dist/hooks/use-container-size.d.ts.map +1 -0
- package/dist/hooks/use-container-size.js +28 -0
- package/dist/hooks/use-container-size.js.map +1 -0
- package/dist/shell/app-shell.d.ts +101 -0
- package/dist/shell/app-shell.d.ts.map +1 -0
- package/dist/shell/app-shell.js +76 -0
- package/dist/shell/app-shell.js.map +1 -0
- package/dist/shell/bottom-navbar.js +2 -2
- package/dist/shell/bottom-navbar.js.map +1 -1
- package/dist/shell/index.d.ts +1 -0
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +6 -5
- package/dist/shell/notification-center.js +4 -4
- package/dist/shell/notification-center.js.map +1 -1
- package/dist/shell/notification-preferences.js +1 -1
- package/dist/shell/notification-preferences.js.map +1 -1
- package/dist/shell/top-bar.js +7 -7
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/tokens/generate-scale.d.ts +49 -0
- package/dist/tokens/generate-scale.d.ts.map +1 -1
- package/dist/tokens/primitives.css +29 -26
- package/dist/tokens/semantic.css +98 -39
- package/dist/tokens/variants.css +1 -1
- package/dist/ui/accordion.js +1 -1
- package/dist/ui/accordion.js.map +1 -1
- package/dist/ui/alert-dialog.js +1 -1
- package/dist/ui/alert-dialog.js.map +1 -1
- package/dist/ui/alert.d.ts.map +1 -1
- package/dist/ui/alert.js +36 -36
- package/dist/ui/alert.js.map +1 -1
- package/dist/ui/avatar.js +44 -44
- package/dist/ui/avatar.js.map +1 -1
- package/dist/ui/badge-indicator.js +1 -1
- package/dist/ui/badge-indicator.js.map +1 -1
- package/dist/ui/badge.d.ts +47 -33
- package/dist/ui/badge.d.ts.map +1 -1
- package/dist/ui/banner.js +26 -26
- package/dist/ui/banner.js.map +1 -1
- package/dist/ui/button.js +35 -35
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/card.js +42 -42
- package/dist/ui/card.js.map +1 -1
- package/dist/ui/chat/date-separator.d.ts +8 -1
- package/dist/ui/chat/date-separator.d.ts.map +1 -1
- package/dist/ui/checkbox.js +1 -1
- package/dist/ui/checkbox.js.map +1 -1
- package/dist/ui/code.js +2 -2
- package/dist/ui/code.js.map +1 -1
- package/dist/ui/color-input.js +3 -3
- package/dist/ui/color-input.js.map +1 -1
- package/dist/ui/combobox.js +139 -139
- package/dist/ui/combobox.js.map +1 -1
- package/dist/ui/context-menu.js +4 -4
- package/dist/ui/context-menu.js.map +1 -1
- package/dist/ui/data-table-body.d.ts +3 -1
- package/dist/ui/data-table-body.d.ts.map +1 -1
- package/dist/ui/data-table-body.js +93 -92
- package/dist/ui/data-table-body.js.map +1 -1
- package/dist/ui/data-table-bulk-actions.d.ts +7 -1
- package/dist/ui/data-table-bulk-actions.d.ts.map +1 -1
- package/dist/ui/data-table-bulk-actions.js +14 -8
- package/dist/ui/data-table-bulk-actions.js.map +1 -1
- package/dist/ui/data-table-card.d.ts.map +1 -1
- package/dist/ui/data-table-card.js +66 -47
- package/dist/ui/data-table-card.js.map +1 -1
- package/dist/ui/data-table-context.d.ts +4 -0
- package/dist/ui/data-table-context.d.ts.map +1 -1
- package/dist/ui/data-table-context.js.map +1 -1
- package/dist/ui/data-table-header.d.ts.map +1 -1
- package/dist/ui/data-table-header.js +18 -15
- package/dist/ui/data-table-header.js.map +1 -1
- package/dist/ui/data-table-pagination.js +3 -3
- package/dist/ui/data-table-pagination.js.map +1 -1
- package/dist/ui/data-table-toolbar.d.ts +11 -1
- package/dist/ui/data-table-toolbar.d.ts.map +1 -1
- package/dist/ui/data-table-toolbar.js +7 -7
- package/dist/ui/data-table-toolbar.js.map +1 -1
- package/dist/ui/data-table.d.ts +52 -5
- package/dist/ui/data-table.d.ts.map +1 -1
- package/dist/ui/data-table.js +181 -169
- package/dist/ui/data-table.js.map +1 -1
- package/dist/ui/dialog.js +1 -1
- package/dist/ui/dialog.js.map +1 -1
- package/dist/ui/dot.js +13 -13
- package/dist/ui/dot.js.map +1 -1
- package/dist/ui/dropdown-menu.js +4 -4
- package/dist/ui/dropdown-menu.js.map +1 -1
- package/dist/ui/file-upload.js +3 -3
- package/dist/ui/file-upload.js.map +1 -1
- package/dist/ui/input-otp.js +1 -1
- package/dist/ui/input-otp.js.map +1 -1
- package/dist/ui/input.js +30 -30
- package/dist/ui/input.js.map +1 -1
- package/dist/ui/lib/utils.js +2 -1
- package/dist/ui/lib/utils.js.map +1 -1
- package/dist/ui/menubar.js +5 -5
- package/dist/ui/menubar.js.map +1 -1
- package/dist/ui/navigation-menu.js +1 -1
- package/dist/ui/navigation-menu.js.map +1 -1
- package/dist/ui/number-input.js +36 -36
- package/dist/ui/number-input.js.map +1 -1
- package/dist/ui/pagination.js +1 -1
- package/dist/ui/pagination.js.map +1 -1
- package/dist/ui/progress-ring.js +2 -2
- package/dist/ui/progress-ring.js.map +1 -1
- package/dist/ui/progress.d.ts.map +1 -1
- package/dist/ui/progress.js +66 -62
- package/dist/ui/progress.js.map +1 -1
- package/dist/ui/radio.js +1 -1
- package/dist/ui/radio.js.map +1 -1
- package/dist/ui/select.js +61 -61
- package/dist/ui/select.js.map +1 -1
- package/dist/ui/sheet.js +47 -47
- package/dist/ui/sheet.js.map +1 -1
- package/dist/ui/sidebar.js +132 -132
- package/dist/ui/sidebar.js.map +1 -1
- package/dist/ui/skeleton.js +39 -39
- package/dist/ui/slider.js +18 -18
- package/dist/ui/slider.js.map +1 -1
- package/dist/ui/split-button.js +3 -3
- package/dist/ui/split-button.js.map +1 -1
- package/dist/ui/stat-card.js +1 -1
- package/dist/ui/stat-card.js.map +1 -1
- package/dist/ui/stepper.js +2 -2
- package/dist/ui/stepper.js.map +1 -1
- package/dist/ui/surface.d.ts.map +1 -1
- package/dist/ui/surface.js +11 -11
- package/dist/ui/surface.js.map +1 -1
- package/dist/ui/switch.js +1 -1
- package/dist/ui/switch.js.map +1 -1
- package/dist/ui/table.js +1 -1
- package/dist/ui/table.js.map +1 -1
- package/dist/ui/tabs.d.ts.map +1 -1
- package/dist/ui/tabs.js +48 -48
- package/dist/ui/tabs.js.map +1 -1
- package/dist/ui/text.js +8 -8
- package/dist/ui/textarea.js +17 -17
- package/dist/ui/textarea.js.map +1 -1
- package/dist/ui/toast.js +1 -1
- package/dist/ui/toast.js.map +1 -1
- package/dist/ui/toggle.js +15 -15
- package/dist/ui/toggle.js.map +1 -1
- package/docs/components/composed/avatar-group.md +4 -4
- package/docs/components/composed/diff.md +10 -2
- package/docs/components/composed/schedule-view.md +1 -1
- package/docs/components/shell/app-shell.md +132 -0
- package/docs/components/ui/avatar.md +1 -1
- package/docs/components/ui/button.md +1 -1
- package/docs/components/ui/chat.md +7 -1
- package/docs/components/ui/color-swatch.md +1 -1
- package/docs/components/ui/data-table-toolbar.md +6 -2
- package/docs/components/ui/data-table.md +41 -7
- package/docs/components/ui/dot.md +1 -1
- package/docs/components/ui/progress.md +1 -0
- package/docs/components/ui/surface.md +3 -3
- package/docs/components/ui/table.md +2 -2
- package/docs/recipes/install-astro.md +3 -2
- package/docs/recipes/install-next-app-router.md +3 -2
- package/docs/recipes/install-remix.md +3 -2
- package/docs/recipes/install-tanstack-start.md +3 -2
- package/docs/recipes/install-vite.md +3 -2
- package/docs/recipes/troubleshoot.md +26 -5
- package/llms.txt +2 -1
- package/make-kit/Guidelines.md +2 -2
- package/make-kit/components/button.md +1 -1
- package/make-kit/components/card.md +6 -6
- package/make-kit/components/surface.md +4 -4
- package/make-kit/foundations/color.md +4 -4
- package/make-kit/foundations/dark-mode.md +1 -1
- package/make-kit/foundations/radius.md +1 -1
- package/make-kit/foundations/surfaces.md +9 -9
- package/mcp-manifest.json +373 -34
- package/package.json +43 -13
- package/skill/SKILL.md +12 -6
- package/skill/references/components.md +2 -1
- package/skill/references/setup-astro.md +3 -2
- package/skill/references/setup-next-app-router.md +3 -2
- package/skill/references/setup-remix.md +3 -2
- package/skill/references/setup-tanstack-start.md +3 -2
- package/skill/references/setup-vite.md +3 -2
- package/skill/references/troubleshoot.md +26 -5
- package/dist/_chunks/tiptap.js +0 -18809
- package/dist/_chunks/tiptap.js.map +0 -1
- package/dist/_chunks/vendor-utils.js +0 -1766
- package/dist/_chunks/vendor-utils.js.map +0 -1
- package/dist/primitives/react-toast.d.ts +0 -120
- package/dist/ui/charts/_internal/scales.d.ts +0 -5
- package/dist/ui/charts/_internal/scales.d.ts.map +0 -1
- package/dist/ui/lib/slot.d.ts +0 -8
- package/dist/ui/lib/slot.d.ts.map +0 -1
- package/scripts/welcome.mjs +0 -312
package/dist/ui/stat-card.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stat-card.js","names":[],"sources":["../../src/ui/stat-card.tsx"],"sourcesContent":["'use client'\n\nimport { IconMinus, IconTrendingDown, IconTrendingUp } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Card, CardContent, CardFooter, type CardSize } from './card'\nimport { Icon } from './icon'\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { useLink } from './lib/link-context'\nimport { springs, tweens } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\nimport { Progress, type ProgressColor } from './progress'\nimport { Skeleton } from './skeleton'\nimport { type FlashPreset, type FlashSpec, type FlashSpeed,StatFlash } from './stat-flash'\n\n/**\n * Props for StatCard — a dashboard metric tile displaying a label, a large numeric value,\n * an optional trend delta (with directional arrow icon), and an optional header icon.\n *\n * **Surface:** delegated to `Card` via `variant` — `default` (tonal hairline, no shadow) |\n * `elevated` (shadow) | `outline` (strong border, no shadow) | `flat`. StatCard composes `<Card>`, so the surface,\n * padding (gap model), and elevation all live in one place.\n *\n * **Accent:** `accentStyle=\"none\"` (default) is neutral; `\"icon\"` wraps `icon` in an accent chip\n * (`iconFill=\"soft\" | \"solid\"`); `\"tint\"` applies a subtle accent surface wash + accent value.\n * (Replaces the removed `accent` left-rail prop — see CHANGELOG migration.)\n *\n * **Delta direction:** `'up'` renders a green trending-up arrow, `'down'` renders a red\n * trending-down arrow, `'neutral'` renders a grey dash. The `delta.value` is a formatted\n * string (e.g. `\"+8%\"` or `\"−120\"`).\n *\n * **Loading state:** Pass `loading={true}` to render a pulse-skeleton placeholder instead of data.\n *\n * @example\n * // Revenue metric with a positive trend:\n * <StatCard\n * label=\"Monthly Revenue\"\n * value=\"$48,200\"\n * delta={{ value: \"+12%\", direction: \"up\" }}\n * icon={<IconCurrencyDollar />}\n * />\n *\n * @example\n * // Open ticket count with a downward trend (good for support queues):\n * <StatCard\n * label=\"Open Tickets\"\n * value={142}\n * delta={{ value: \"−18\", direction: \"down\" }}\n * />\n *\n * @example\n * // Loading skeleton while data is fetching:\n * <StatCard label=\"Users\" value={0} loading={isFetching} />\n *\n * @example\n * // Simple metric with no trend (stable, neutral):\n * <StatCard\n * label=\"Storage Used\"\n * value=\"4.2 GB\"\n * delta={{ value: \"No change\", direction: \"neutral\" }}\n * />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface StatCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'> {\n /** Heading text for the metric. `title` is an alias for `label`. */\n label?: string\n /** Alias for `label` — use whichever feels natural. */\n title?: string\n value: string | number\n /** Prefix before value, e.g. \"$\" */\n prefix?: string\n /** Suffix after value, e.g. \"users\" */\n suffix?: string\n delta?: {\n value: string\n direction: 'up' | 'down' | 'neutral'\n }\n /** Where the delta sits relative to the value: `block` (below — default) or `inline` (on the value's baseline, to its right). @default 'block' */\n deltaPlacement?: 'block' | 'inline'\n icon?: IconInput\n loading?: boolean\n /** Comparison period label shown after delta, e.g. \"vs last month\" */\n comparisonLabel?: string\n /** Secondary metric below main value, e.g. \"of $50,000 target\" */\n secondaryLabel?: string\n /** Progress toward a target (0-100). Renders a thin progress bar below the value. */\n progress?: number\n /** Surface variant, delegated to Card: `default` (tonal hairline, no shadow) | `elevated` (shadow) | `outline` (strong border, no shadow) | `flat`. @default 'default' */\n variant?: 'default' | 'elevated' | 'outline' | 'flat'\n /**\n * Tile density, delegated to Card's size axis. `sm` tightens padding to 16px and steps\n * the value down to `text-heading-md` — use it for dense KPI rows and narrow stat grids\n * (e.g. stat-row's 140px tiles). @default 'md'\n */\n size?: CardSize\n /** How the brand accent reads: `none` (neutral — default), `icon` (accent chip around `icon`), or `tint` (subtle accent surface wash + accent value). */\n accentStyle?: 'none' | 'icon' | 'tint'\n /** Animate value/label in on mount (subtle settle; content stays visible either way, never opacity-gated). @default false */\n reveal?: boolean\n /** When `accentStyle=\"icon\"`: `soft` (tinted chip) or `solid` (filled accent chip). @default 'soft' */\n iconFill?: 'soft' | 'solid'\n /**\n * Opt-in entrance flash: the icon chip mounts showing a transient state (e.g. a green up-arrow)\n * then settles to `icon`. A preset (`'up' | 'down' | 'goal' | 'record' | 'alert' | 'live'`) or an\n * explicit `{ tone, icon }`. Requires `icon`; implies the icon chip. Off by default.\n */\n flash?: FlashPreset | FlashSpec\n /** Speed of the entrance flash: `fast` | `normal` | `slow`. @default 'normal' */\n flashSpeed?: FlashSpeed\n /** Sparkline data points. Renders a mini SVG line chart in the card. */\n sparkline?: number[]\n /** Make the card clickable with hover state */\n onClick?: () => void\n /** Href — makes the card a link (uses the LinkContext Link component) */\n href?: string\n /** Footer content rendered below the card body, e.g. \"View details →\" */\n footer?: React.ReactNode\n}\n\nfunction buildSparklinePath(raw: number[], width: number, height: number): string {\n const data = raw.filter((v) => Number.isFinite(v))\n if (data.length < 2) return ''\n const min = Math.min(...data)\n const max = Math.max(...data)\n const range = max - min || 1\n const stepX = width / (data.length - 1)\n return data\n .map((v, i) => {\n const x = i * stepX\n const y = height - ((v - min) / range) * height\n return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`\n })\n .join(' ')\n}\n\nfunction Sparkline({\n data,\n colorClass,\n}: {\n data: number[]\n colorClass: string\n}) {\n const id = React.useId()\n const pathRef = React.useRef<SVGPathElement>(null)\n const [pathLength, setPathLength] = React.useState(0)\n const svgWidth = 80\n const svgHeight = 32\n const path = buildSparklinePath(data, svgWidth, svgHeight)\n\n React.useEffect(() => {\n if (pathRef.current) {\n setPathLength(pathRef.current.getTotalLength())\n }\n }, [path])\n\n if (!path) return null\n\n // Build area fill path (close to bottom-right then bottom-left)\n const areaPath = `${path} L${svgWidth.toFixed(1)},${svgHeight.toFixed(1)} L0,${svgHeight.toFixed(1)} Z`\n\n return (\n <div className={cn('w-20 h-8', colorClass)} aria-hidden=\"true\">\n <svg\n viewBox={`0 0 ${svgWidth} ${svgHeight}`}\n className=\"w-full h-full overflow-visible\"\n preserveAspectRatio=\"none\"\n >\n <defs>\n <linearGradient id={`sparkline-fill-${id}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor=\"currentColor\" stopOpacity=\"0.1\" />\n <stop offset=\"100%\" stopColor=\"currentColor\" stopOpacity=\"0\" />\n </linearGradient>\n </defs>\n <path d={areaPath} fill={`url(#sparkline-fill-${id})`} />\n <path\n ref={pathRef}\n d={path}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{\n opacity: pathLength === 0 ? 0 : 1,\n strokeDasharray: pathLength,\n strokeDashoffset: pathLength,\n animation: pathLength > 0 ? `sparkline-draw-${id.replace(/:/g, '')} 1s ease-out forwards` : 'none',\n }}\n />\n </svg>\n {pathLength > 0 && (\n <style>{`@keyframes sparkline-draw-${id.replace(/:/g, '')} { to { stroke-dashoffset: 0; } }`}</style>\n )}\n </div>\n )\n}\n\nfunction ProgressBar({ progress, label }: { progress: number; label: string }) {\n const clamped = Math.max(0, Math.min(100, progress))\n // StatCard's own thresholds (90 = success, 70 = warning) — deliberately distinct\n // from Progress's autoColor (85/60), so we pass an explicit color.\n const barColor: ProgressColor =\n clamped >= 90 ? 'success' : clamped >= 70 ? 'warning' : 'accent'\n\n return (\n <Progress.Root value={clamped} size=\"sm\">\n <Progress.Track aria-label={`${label} progress`}>\n <Progress.Indicator color={barColor} />\n </Progress.Track>\n </Progress.Root>\n )\n}\n\nconst StatCard = React.forwardRef<HTMLDivElement, StatCardProps>(\n (\n {\n className,\n label,\n title,\n value,\n prefix,\n suffix,\n delta,\n deltaPlacement = 'block',\n icon,\n loading,\n comparisonLabel,\n secondaryLabel,\n progress,\n variant = 'default',\n size = 'md',\n accentStyle = 'none',\n reveal = false,\n iconFill = 'soft',\n flash,\n flashSpeed,\n sparkline,\n onClick,\n href,\n footer,\n ...props\n },\n ref,\n ) => {\n const Link = useLink()\n const resolvedLabel = title ?? label ?? ''\n const isClickable = !!(onClick || href)\n const computedAriaLabel =\n isClickable && !props['aria-label'] ? `View ${resolvedLabel}` : props['aria-label']\n\n if (loading) {\n return (\n <Card ref={ref} variant={variant} size={size} className={className} aria-busy=\"true\" {...props}>\n <CardContent className=\"flex flex-col gap-ds-03\">\n <Skeleton className=\"h-ds-04 w-24 rounded-control-inner\" />\n <Skeleton className=\"h-ds-sm w-32\" />\n <Skeleton className=\"h-3 w-16 rounded-control-inner\" />\n </CardContent>\n </Card>\n )\n }\n\n const DeltaIcon =\n delta?.direction === 'up'\n ? IconTrendingUp\n : delta?.direction === 'down'\n ? IconTrendingDown\n : IconMinus\n\n const deltaColour =\n delta?.direction === 'up'\n ? 'text-success-11'\n : delta?.direction === 'down'\n ? 'text-error-11'\n : 'text-surface-fg-muted'\n\n const sparklineColor =\n delta?.direction === 'up'\n ? 'text-success-11'\n : delta?.direction === 'down'\n ? 'text-error-11'\n : 'text-accent-11'\n\n const deltaNode = delta ? (\n <motion.div\n className={cn(\n // Rhythm comes from CardContent's flex gap — no placement margins.\n 'flex items-center gap-ds-02 text-body-sm font-medium',\n deltaColour,\n )}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ ...springs.smooth, delay: 0.2 }}\n >\n <motion.span\n className=\"inline-flex\"\n initial={{ opacity: 0.5, scale: 1.4 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.smooth}\n >\n <Icon icon={DeltaIcon} size=\"sm\" />\n </motion.span>\n <span>{delta.value}</span>\n {comparisonLabel && (\n <span className=\"text-surface-fg-subtle font-normal\">{comparisonLabel}</span>\n )}\n </motion.div>\n ) : null\n\n // reveal=false → static (content-safe default). reveal=true → subtle settle; opacity NEVER gated.\n const revealProps = reveal\n ? { initial: { y: 8 }, animate: { y: 0 }, transition: springs.smooth }\n : { initial: false as const }\n\n const cardContent = (\n <>\n <div className=\"flex items-center justify-between\">\n <motion.p\n className=\"text-body-md font-medium text-surface-fg-muted\"\n {...revealProps}\n >\n {resolvedLabel}\n </motion.p>\n <div className=\"flex items-center gap-ds-03\">\n {sparkline && sparkline.length >= 2 && (\n <Sparkline data={sparkline} colorClass={sparklineColor} />\n )}\n {icon && flash ? (\n <StatFlash icon={icon} flash={flash} fill={iconFill} speed={flashSpeed} />\n ) : icon ? (\n accentStyle === 'icon' ? (\n <motion.span\n className={cn(\n 'inline-flex items-center justify-center rounded-control p-ds-02',\n iconFill === 'solid'\n ? 'bg-accent-9 text-accent-fg'\n : 'bg-accent-3 text-accent-11',\n )}\n {...revealProps}\n aria-hidden=\"true\"\n >\n <IconProvider size=\"md\">{normalizeIcon(icon, 'md')}</IconProvider>\n </motion.span>\n ) : (\n <motion.span\n className=\"text-surface-fg-muted\"\n {...revealProps}\n aria-hidden=\"true\"\n >\n <IconProvider size=\"lg\">{normalizeIcon(icon, 'lg')}</IconProvider>\n </motion.span>\n )\n ) : null}\n </div>\n </div>\n <div className=\"flex flex-col gap-ds-01\">\n <div className={cn(deltaPlacement === 'inline' && delta && 'flex items-baseline gap-ds-03')}>\n <div className=\"overflow-hidden\">\n <motion.p\n className={cn(\n 'inline-block font-semibold',\n size === 'sm' ? 'text-heading-md' : 'text-heading-lg',\n accentStyle === 'tint' ? 'text-accent-11' : 'text-surface-fg',\n )}\n {...revealProps}\n >\n {prefix && (\n <span className={cn('text-surface-fg-muted', size === 'sm' ? 'text-body-md' : 'text-heading-xs')}>{prefix}</span>\n )}\n <span className=\"tabular-nums\">{value}</span>\n {suffix && (\n <span className={cn('text-surface-fg-muted', size === 'sm' ? 'text-body-md' : 'text-heading-xs')}>{suffix}</span>\n )}\n </motion.p>\n </div>\n {deltaPlacement === 'inline' && deltaNode}\n </div>\n {secondaryLabel && (\n <motion.p\n className=\"text-body-sm text-surface-fg-subtle\"\n {...revealProps}\n >\n {secondaryLabel}\n </motion.p>\n )}\n </div>\n {progress != null && (\n <motion.div\n {...revealProps}\n >\n <ProgressBar progress={progress} label={resolvedLabel} />\n </motion.div>\n )}\n {deltaPlacement === 'block' && deltaNode}\n </>\n )\n\n // accentStyle=\"tint\" → a subtle accent gradient wash over Card's surface.\n const tintClass =\n accentStyle === 'tint' ? 'bg-linear-to-t from-accent-2 to-surface-raised' : undefined\n // Footer sits outside CardContent behind a full-width divider (both are direct\n // children of Card, so the rule spans edge-to-edge — no inset border-t).\n const body = (\n <>\n <CardContent className=\"flex flex-col gap-ds-03\">{cardContent}</CardContent>\n {footer && (\n <>\n <div aria-hidden=\"true\" className=\"h-px w-full bg-surface-border-subtle\" />\n <CardFooter className=\"text-body-sm\">\n <motion.div\n className=\"w-full\"\n initial={false}\n animate={{ opacity: 1 }}\n transition={{ ...tweens.fade, delay: 0.25 }}\n >\n {footer}\n </motion.div>\n </CardFooter>\n </>\n )}\n </>\n )\n\n // href → wrap Card in the framework Link (Card stays the shell; hover-lift inside).\n if (href) {\n return (\n <Link\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={href}\n onClick={onClick}\n className=\"block no-underline\"\n aria-label={computedAriaLabel}\n {...(props as React.AnchorHTMLAttributes<HTMLAnchorElement>)}\n >\n <Card variant={variant} size={size} interactive className={tintClass}>\n {body}\n </Card>\n </Link>\n )\n }\n\n // onClick → interactive Card with button semantics + keyboard activation.\n if (onClick) {\n return (\n <Card\n ref={ref}\n variant={variant}\n size={size}\n interactive\n className={cn(tintClass, className)}\n role=\"button\"\n tabIndex={0}\n aria-label={computedAriaLabel}\n onClick={onClick}\n onKeyDown={(e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onClick()\n }\n }}\n {...props}\n >\n {body}\n </Card>\n )\n }\n\n return (\n <Card ref={ref} variant={variant} size={size} className={cn(tintClass, className)} {...props}>\n {body}\n </Card>\n )\n },\n)\n\nStatCard.displayName = 'StatCard'\n\nexport { StatCard }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stat-card.js","names":[],"sources":["../../src/ui/stat-card.tsx"],"sourcesContent":["'use client'\n\nimport { IconMinus, IconTrendingDown, IconTrendingUp } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Card, CardContent, CardFooter, type CardSize } from './card'\nimport { Icon } from './icon'\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { useLink } from './lib/link-context'\nimport { springs, tweens } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\nimport { Progress, type ProgressColor } from './progress'\nimport { Skeleton } from './skeleton'\nimport { type FlashPreset, type FlashSpec, type FlashSpeed,StatFlash } from './stat-flash'\n\n/**\n * Props for StatCard — a dashboard metric tile displaying a label, a large numeric value,\n * an optional trend delta (with directional arrow icon), and an optional header icon.\n *\n * **Surface:** delegated to `Card` via `variant` — `default` (tonal hairline, no shadow) |\n * `elevated` (shadow) | `outline` (strong border, no shadow) | `flat`. StatCard composes `<Card>`, so the surface,\n * padding (gap model), and elevation all live in one place.\n *\n * **Accent:** `accentStyle=\"none\"` (default) is neutral; `\"icon\"` wraps `icon` in an accent chip\n * (`iconFill=\"soft\" | \"solid\"`); `\"tint\"` applies a subtle accent surface wash + accent value.\n * (Replaces the removed `accent` left-rail prop — see CHANGELOG migration.)\n *\n * **Delta direction:** `'up'` renders a green trending-up arrow, `'down'` renders a red\n * trending-down arrow, `'neutral'` renders a grey dash. The `delta.value` is a formatted\n * string (e.g. `\"+8%\"` or `\"−120\"`).\n *\n * **Loading state:** Pass `loading={true}` to render a pulse-skeleton placeholder instead of data.\n *\n * @example\n * // Revenue metric with a positive trend:\n * <StatCard\n * label=\"Monthly Revenue\"\n * value=\"$48,200\"\n * delta={{ value: \"+12%\", direction: \"up\" }}\n * icon={<IconCurrencyDollar />}\n * />\n *\n * @example\n * // Open ticket count with a downward trend (good for support queues):\n * <StatCard\n * label=\"Open Tickets\"\n * value={142}\n * delta={{ value: \"−18\", direction: \"down\" }}\n * />\n *\n * @example\n * // Loading skeleton while data is fetching:\n * <StatCard label=\"Users\" value={0} loading={isFetching} />\n *\n * @example\n * // Simple metric with no trend (stable, neutral):\n * <StatCard\n * label=\"Storage Used\"\n * value=\"4.2 GB\"\n * delta={{ value: \"No change\", direction: \"neutral\" }}\n * />\n * // These are just a few ways — feel free to combine props creatively!\n */\nexport interface StatCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'> {\n /** Heading text for the metric. `title` is an alias for `label`. */\n label?: string\n /** Alias for `label` — use whichever feels natural. */\n title?: string\n value: string | number\n /** Prefix before value, e.g. \"$\" */\n prefix?: string\n /** Suffix after value, e.g. \"users\" */\n suffix?: string\n delta?: {\n value: string\n direction: 'up' | 'down' | 'neutral'\n }\n /** Where the delta sits relative to the value: `block` (below — default) or `inline` (on the value's baseline, to its right). @default 'block' */\n deltaPlacement?: 'block' | 'inline'\n icon?: IconInput\n loading?: boolean\n /** Comparison period label shown after delta, e.g. \"vs last month\" */\n comparisonLabel?: string\n /** Secondary metric below main value, e.g. \"of $50,000 target\" */\n secondaryLabel?: string\n /** Progress toward a target (0-100). Renders a thin progress bar below the value. */\n progress?: number\n /** Surface variant, delegated to Card: `default` (tonal hairline, no shadow) | `elevated` (shadow) | `outline` (strong border, no shadow) | `flat`. @default 'default' */\n variant?: 'default' | 'elevated' | 'outline' | 'flat'\n /**\n * Tile density, delegated to Card's size axis. `sm` tightens padding to 16px and steps\n * the value down to `text-heading-md` — use it for dense KPI rows and narrow stat grids\n * (e.g. stat-row's 140px tiles). @default 'md'\n */\n size?: CardSize\n /** How the brand accent reads: `none` (neutral — default), `icon` (accent chip around `icon`), or `tint` (subtle accent surface wash + accent value). */\n accentStyle?: 'none' | 'icon' | 'tint'\n /** Animate value/label in on mount (subtle settle; content stays visible either way, never opacity-gated). @default false */\n reveal?: boolean\n /** When `accentStyle=\"icon\"`: `soft` (tinted chip) or `solid` (filled accent chip). @default 'soft' */\n iconFill?: 'soft' | 'solid'\n /**\n * Opt-in entrance flash: the icon chip mounts showing a transient state (e.g. a green up-arrow)\n * then settles to `icon`. A preset (`'up' | 'down' | 'goal' | 'record' | 'alert' | 'live'`) or an\n * explicit `{ tone, icon }`. Requires `icon`; implies the icon chip. Off by default.\n */\n flash?: FlashPreset | FlashSpec\n /** Speed of the entrance flash: `fast` | `normal` | `slow`. @default 'normal' */\n flashSpeed?: FlashSpeed\n /** Sparkline data points. Renders a mini SVG line chart in the card. */\n sparkline?: number[]\n /** Make the card clickable with hover state */\n onClick?: () => void\n /** Href — makes the card a link (uses the LinkContext Link component) */\n href?: string\n /** Footer content rendered below the card body, e.g. \"View details →\" */\n footer?: React.ReactNode\n}\n\nfunction buildSparklinePath(raw: number[], width: number, height: number): string {\n const data = raw.filter((v) => Number.isFinite(v))\n if (data.length < 2) return ''\n const min = Math.min(...data)\n const max = Math.max(...data)\n const range = max - min || 1\n const stepX = width / (data.length - 1)\n return data\n .map((v, i) => {\n const x = i * stepX\n const y = height - ((v - min) / range) * height\n return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`\n })\n .join(' ')\n}\n\nfunction Sparkline({\n data,\n colorClass,\n}: {\n data: number[]\n colorClass: string\n}) {\n const id = React.useId()\n const pathRef = React.useRef<SVGPathElement>(null)\n const [pathLength, setPathLength] = React.useState(0)\n const svgWidth = 80\n const svgHeight = 32\n const path = buildSparklinePath(data, svgWidth, svgHeight)\n\n React.useEffect(() => {\n if (pathRef.current) {\n setPathLength(pathRef.current.getTotalLength())\n }\n }, [path])\n\n if (!path) return null\n\n // Build area fill path (close to bottom-right then bottom-left)\n const areaPath = `${path} L${svgWidth.toFixed(1)},${svgHeight.toFixed(1)} L0,${svgHeight.toFixed(1)} Z`\n\n return (\n <div className={cn('w-20 h-8', colorClass)} aria-hidden=\"true\">\n <svg\n viewBox={`0 0 ${svgWidth} ${svgHeight}`}\n className=\"w-full h-full overflow-visible\"\n preserveAspectRatio=\"none\"\n >\n <defs>\n <linearGradient id={`sparkline-fill-${id}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor=\"currentColor\" stopOpacity=\"0.1\" />\n <stop offset=\"100%\" stopColor=\"currentColor\" stopOpacity=\"0\" />\n </linearGradient>\n </defs>\n <path d={areaPath} fill={`url(#sparkline-fill-${id})`} />\n <path\n ref={pathRef}\n d={path}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{\n opacity: pathLength === 0 ? 0 : 1,\n strokeDasharray: pathLength,\n strokeDashoffset: pathLength,\n animation: pathLength > 0 ? `sparkline-draw-${id.replace(/:/g, '')} 1s ease-out forwards` : 'none',\n }}\n />\n </svg>\n {pathLength > 0 && (\n <style>{`@keyframes sparkline-draw-${id.replace(/:/g, '')} { to { stroke-dashoffset: 0; } }`}</style>\n )}\n </div>\n )\n}\n\nfunction ProgressBar({ progress, label }: { progress: number; label: string }) {\n const clamped = Math.max(0, Math.min(100, progress))\n // StatCard's own thresholds (90 = success, 70 = warning) — deliberately distinct\n // from Progress's autoColor (85/60), so we pass an explicit color.\n const barColor: ProgressColor =\n clamped >= 90 ? 'success' : clamped >= 70 ? 'warning' : 'accent'\n\n return (\n <Progress.Root value={clamped} size=\"sm\">\n <Progress.Track aria-label={`${label} progress`}>\n <Progress.Indicator color={barColor} />\n </Progress.Track>\n </Progress.Root>\n )\n}\n\nconst StatCard = React.forwardRef<HTMLDivElement, StatCardProps>(\n (\n {\n className,\n label,\n title,\n value,\n prefix,\n suffix,\n delta,\n deltaPlacement = 'block',\n icon,\n loading,\n comparisonLabel,\n secondaryLabel,\n progress,\n variant = 'default',\n size = 'md',\n accentStyle = 'none',\n reveal = false,\n iconFill = 'soft',\n flash,\n flashSpeed,\n sparkline,\n onClick,\n href,\n footer,\n ...props\n },\n ref,\n ) => {\n const Link = useLink()\n const resolvedLabel = title ?? label ?? ''\n const isClickable = !!(onClick || href)\n const computedAriaLabel =\n isClickable && !props['aria-label'] ? `View ${resolvedLabel}` : props['aria-label']\n\n if (loading) {\n return (\n <Card ref={ref} variant={variant} size={size} className={className} aria-busy=\"true\" {...props}>\n <CardContent className=\"flex flex-col gap-ds-03\">\n <Skeleton className=\"h-ds-04 w-24 rounded-control-inner\" />\n <Skeleton className=\"h-ds-sm w-32\" />\n <Skeleton className=\"h-3 w-16 rounded-control-inner\" />\n </CardContent>\n </Card>\n )\n }\n\n const DeltaIcon =\n delta?.direction === 'up'\n ? IconTrendingUp\n : delta?.direction === 'down'\n ? IconTrendingDown\n : IconMinus\n\n const deltaColour =\n delta?.direction === 'up'\n ? 'text-success-11'\n : delta?.direction === 'down'\n ? 'text-error-11'\n : 'text-surface-fg-muted'\n\n const sparklineColor =\n delta?.direction === 'up'\n ? 'text-success-11'\n : delta?.direction === 'down'\n ? 'text-error-11'\n : 'text-accent-11'\n\n const deltaNode = delta ? (\n <motion.div\n className={cn(\n // Rhythm comes from CardContent's flex gap — no placement margins.\n 'flex items-center gap-ds-02 text-body-sm font-medium',\n deltaColour,\n )}\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ ...springs.smooth, delay: 0.2 }}\n >\n <motion.span\n className=\"inline-flex\"\n initial={{ opacity: 0.5, scale: 1.4 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.smooth}\n >\n <Icon icon={DeltaIcon} size=\"sm\" />\n </motion.span>\n <span>{delta.value}</span>\n {comparisonLabel && (\n <span className=\"text-surface-fg-subtle font-normal\">{comparisonLabel}</span>\n )}\n </motion.div>\n ) : null\n\n // reveal=false → static (content-safe default). reveal=true → subtle settle; opacity NEVER gated.\n const revealProps = reveal\n ? { initial: { y: 8 }, animate: { y: 0 }, transition: springs.smooth }\n : { initial: false as const }\n\n const cardContent = (\n <>\n <div className=\"flex items-center justify-between\">\n <motion.p\n className=\"text-body-md font-medium text-surface-fg-muted\"\n {...revealProps}\n >\n {resolvedLabel}\n </motion.p>\n <div className=\"flex items-center gap-ds-03\">\n {sparkline && sparkline.length >= 2 && (\n <Sparkline data={sparkline} colorClass={sparklineColor} />\n )}\n {icon && flash ? (\n <StatFlash icon={icon} flash={flash} fill={iconFill} speed={flashSpeed} />\n ) : icon ? (\n accentStyle === 'icon' ? (\n <motion.span\n className={cn(\n 'inline-flex items-center justify-center rounded-control p-ds-02',\n iconFill === 'solid'\n ? 'bg-accent-9 text-accent-fg'\n : 'bg-accent-3 text-accent-11',\n )}\n {...revealProps}\n aria-hidden=\"true\"\n >\n <IconProvider size=\"md\">{normalizeIcon(icon, 'md')}</IconProvider>\n </motion.span>\n ) : (\n <motion.span\n className=\"text-surface-fg-muted\"\n {...revealProps}\n aria-hidden=\"true\"\n >\n <IconProvider size=\"lg\">{normalizeIcon(icon, 'lg')}</IconProvider>\n </motion.span>\n )\n ) : null}\n </div>\n </div>\n <div className=\"flex flex-col gap-ds-01\">\n <div className={cn(deltaPlacement === 'inline' && delta && 'flex items-baseline gap-ds-03')}>\n <div className=\"overflow-hidden\">\n <motion.p\n className={cn(\n 'inline-block font-semibold',\n size === 'sm' ? 'text-heading-md' : 'text-heading-lg',\n accentStyle === 'tint' ? 'text-accent-11' : 'text-surface-fg',\n )}\n {...revealProps}\n >\n {prefix && (\n <span className={cn('text-surface-fg-muted', size === 'sm' ? 'text-body-md' : 'text-heading-xs')}>{prefix}</span>\n )}\n <span className=\"tabular-nums\">{value}</span>\n {suffix && (\n <span className={cn('text-surface-fg-muted', size === 'sm' ? 'text-body-md' : 'text-heading-xs')}>{suffix}</span>\n )}\n </motion.p>\n </div>\n {deltaPlacement === 'inline' && deltaNode}\n </div>\n {secondaryLabel && (\n <motion.p\n className=\"text-body-sm text-surface-fg-subtle\"\n {...revealProps}\n >\n {secondaryLabel}\n </motion.p>\n )}\n </div>\n {progress != null && (\n <motion.div\n {...revealProps}\n >\n <ProgressBar progress={progress} label={resolvedLabel} />\n </motion.div>\n )}\n {deltaPlacement === 'block' && deltaNode}\n </>\n )\n\n // accentStyle=\"tint\" → a subtle accent gradient wash over Card's surface.\n const tintClass =\n accentStyle === 'tint' ? 'bg-linear-to-t from-accent-2 to-surface-panel' : undefined\n // Footer sits outside CardContent behind a full-width divider (both are direct\n // children of Card, so the rule spans edge-to-edge — no inset border-t).\n const body = (\n <>\n <CardContent className=\"flex flex-col gap-ds-03\">{cardContent}</CardContent>\n {footer && (\n <>\n <div aria-hidden=\"true\" className=\"h-px w-full bg-surface-border-subtle\" />\n <CardFooter className=\"text-body-sm\">\n <motion.div\n className=\"w-full\"\n initial={false}\n animate={{ opacity: 1 }}\n transition={{ ...tweens.fade, delay: 0.25 }}\n >\n {footer}\n </motion.div>\n </CardFooter>\n </>\n )}\n </>\n )\n\n // href → wrap Card in the framework Link (Card stays the shell; hover-lift inside).\n if (href) {\n return (\n <Link\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={href}\n onClick={onClick}\n className=\"block no-underline\"\n aria-label={computedAriaLabel}\n {...(props as React.AnchorHTMLAttributes<HTMLAnchorElement>)}\n >\n <Card variant={variant} size={size} interactive className={tintClass}>\n {body}\n </Card>\n </Link>\n )\n }\n\n // onClick → interactive Card with button semantics + keyboard activation.\n if (onClick) {\n return (\n <Card\n ref={ref}\n variant={variant}\n size={size}\n interactive\n className={cn(tintClass, className)}\n role=\"button\"\n tabIndex={0}\n aria-label={computedAriaLabel}\n onClick={onClick}\n onKeyDown={(e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onClick()\n }\n }}\n {...props}\n >\n {body}\n </Card>\n )\n }\n\n return (\n <Card ref={ref} variant={variant} size={size} className={cn(tintClass, className)} {...props}>\n {body}\n </Card>\n )\n },\n)\n\nStatCard.displayName = 'StatCard'\n\nexport { StatCard }\n"],"mappings":"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"}
|
package/dist/ui/stepper.js
CHANGED
|
@@ -42,7 +42,7 @@ var u = i.createContext({
|
|
|
42
42
|
});
|
|
43
43
|
}), f = i.forwardRef(({ label: t, description: c, icon: l, className: d, ...f }, p) => {
|
|
44
44
|
let { _index: m = 0, ...h } = f, { activeStep: g, orientation: _, stepperId: v, onStepClick: y } = i.useContext(u), b = m < g ? "completed" : m === g ? "active" : "pending", x = b === "completed" && !!y, S = /* @__PURE__ */ s(a, { children: [/* @__PURE__ */ o("div", {
|
|
45
|
-
className: n("relative shrink-0 flex items-center justify-center w-ds-sm h-ds-sm rounded-pill text-body-sm font-semibold", "transition-[background-color,border-color,color] duration-moderate-01 ease-productive-standard", b === "completed" && "bg-accent-9 text-accent-fg", b === "active" && "bg-accent-9 text-accent-fg", b === "pending" && "bg-surface-
|
|
45
|
+
className: n("relative shrink-0 flex items-center justify-center w-ds-sm h-ds-sm rounded-pill text-body-sm font-semibold", "transition-[background-color,border-color,color] duration-moderate-01 ease-productive-standard", b === "completed" && "bg-accent-9 text-accent-fg", b === "active" && "bg-accent-9 text-accent-fg", b === "pending" && "bg-surface-panel text-surface-fg-subtle border border-surface-border-strong"),
|
|
46
46
|
children: l ? /* @__PURE__ */ o(e, {
|
|
47
47
|
size: "sm",
|
|
48
48
|
children: r(l)
|
|
@@ -78,7 +78,7 @@ var u = i.createContext({
|
|
|
78
78
|
tabIndex: 0,
|
|
79
79
|
"aria-label": `Go to step ${m + 1}: ${t}`,
|
|
80
80
|
onClick: () => y(m),
|
|
81
|
-
className: n("flex items-center gap-ds-03 cursor-pointer select-none", "rounded-control px-ds-02 py-ds-01 -mx-ds-02 -my-ds-01", "bg-transparent border-0", "hover:bg-surface-
|
|
81
|
+
className: n("flex items-center gap-ds-03 cursor-pointer select-none", "rounded-control px-ds-02 py-ds-01 -mx-ds-02 -my-ds-01", "bg-transparent border-0", "hover:bg-surface-panel-hover active:bg-surface-panel-active", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-8 focus-visible:ring-offset-2", "transition-colors duration-fast-01 ease-productive-standard"),
|
|
82
82
|
children: S
|
|
83
83
|
})
|
|
84
84
|
}) : /* @__PURE__ */ o("div", {
|
package/dist/ui/stepper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stepper.js","names":[],"sources":["../../src/ui/stepper.tsx"],"sourcesContent":["'use client'\n\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { springs } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\n\ntype StepState = 'completed' | 'active' | 'pending'\n\ntype StepperContextValue = {\n activeStep: number\n orientation: 'horizontal' | 'vertical'\n stepperId: string\n onStepClick?: (stepIndex: number) => void\n}\n\nconst StepperContext = React.createContext<StepperContextValue>({\n activeStep: 0,\n orientation: 'horizontal',\n stepperId: '',\n})\n\n/**\n * Props for Stepper — a multi-step progress indicator that renders a connected series of `<Step>`\n * children with connector lines, showing completed/active/pending states automatically.\n *\n * **`activeStep`:** 0-indexed. Step at `activeStep` is \"active\", steps before it are \"completed\",\n * steps after it are \"pending\". Update this value to advance the stepper.\n *\n * **Orientation:** `'horizontal'` (default, left-to-right) | `'vertical'` (top-to-bottom, for sidebars).\n *\n * **Children:** Must be `<Step>` components. The Stepper injects `_index` into each child automatically.\n *\n * @example\n * // Horizontal 3-step form wizard:\n * <Stepper activeStep={currentStep}>\n * <Step label=\"Account\" description=\"Create your credentials\" />\n * <Step label=\"Profile\" description=\"Add your details\" />\n * <Step label=\"Review\" description=\"Confirm and submit\" />\n * </Stepper>\n *\n * @example\n * // Vertical onboarding checklist in a sidebar:\n * <Stepper activeStep={completedSteps} orientation=\"vertical\">\n * <Step label=\"Connect workspace\" />\n * <Step label=\"Invite teammates\" />\n * <Step label=\"Set up billing\" />\n * </Stepper>\n *\n * @example\n * // Clickable completed steps — user can navigate back:\n * <Stepper activeStep={step} onStepClick={(i) => setStep(i)}>\n * <Step label=\"Account\" />\n * <Step label=\"Profile\" />\n * <Step label=\"Review\" />\n * </Stepper>\n * // These are just a few ways — feel free to combine props creatively!\n */\ninterface StepperProps extends React.HTMLAttributes<HTMLDivElement> {\n activeStep: number\n orientation?: 'horizontal' | 'vertical'\n /** Called when a completed step is clicked. Receives the 0-based step index. */\n onStepClick?: (stepIndex: number) => void\n children: React.ReactNode\n}\n\nconst Stepper = React.forwardRef<HTMLDivElement, StepperProps>(\n ({ activeStep, orientation = 'horizontal', onStepClick, className, children, ...props }, ref) => {\n const steps = React.Children.toArray(children)\n const stepperId = React.useId()\n const contextValue = React.useMemo(\n () => ({ activeStep, orientation, stepperId, onStepClick }),\n [activeStep, orientation, stepperId, onStepClick],\n )\n return (\n <StepperContext.Provider value={contextValue}>\n <div\n ref={ref}\n className={cn(\n 'flex gap-ds-02',\n orientation === 'vertical' ? 'flex-col' : 'flex-row items-center',\n className,\n )}\n role=\"list\"\n {...props}\n >\n {steps.map((child, index) => (\n <React.Fragment key={index}>\n {React.isValidElement<StepInternalProps>(child)\n ? React.cloneElement(child, { _index: index })\n : child}\n {index < steps.length - 1 && (\n <div\n className={cn(\n 'relative flex-1 overflow-hidden',\n orientation === 'vertical'\n ? 'ml-ds-04 w-ds-01 min-h-ds-05'\n : 'h-ds-01 min-w-ds-05',\n 'bg-surface-border',\n )}\n aria-hidden=\"true\"\n >\n {/* Filled portion — smooth CSS transition */}\n <div\n className={cn(\n 'absolute inset-0 bg-accent-9 transition-transform duration-moderate-02 ease-productive-standard',\n orientation === 'vertical' ? 'origin-top' : 'origin-left',\n )}\n style={{\n transform: `${orientation === 'vertical' ? 'scaleY' : 'scaleX'}(${index < activeStep ? 1 : 0})`,\n }}\n />\n </div>\n )}\n </React.Fragment>\n ))}\n </div>\n </StepperContext.Provider>\n )\n },\n)\n\n/**\n * Props for Step — a single step within a `<Stepper>`. The visual state (completed/active/pending)\n * is derived from the parent Stepper's `activeStep` and this step's position — you don't set it manually.\n *\n * @example\n * // Basic step with label and description:\n * <Step label=\"Payment\" description=\"Enter your card details\" />\n *\n * @example\n * // Step with a custom icon (overrides the default number/checkmark):\n * <Step label=\"Verified\" icon={<IconShieldCheck className=\"h-ico-sm w-ico-sm\" />} />\n */\ninterface StepProps extends React.HTMLAttributes<HTMLDivElement> {\n label: string\n description?: string\n icon?: IconInput\n}\n\ntype StepInternalProps = StepProps & { _index?: number }\n\nconst Step = React.forwardRef<HTMLDivElement, StepProps>(\n ({ label, description, icon, className, ...props }, ref) => {\n const { _index = 0, ...restProps } = props as StepInternalProps\n const { activeStep, orientation, stepperId, onStepClick } = React.useContext(StepperContext)\n const state: StepState = _index < activeStep ? 'completed' : _index === activeStep ? 'active' : 'pending'\n const isClickable = state === 'completed' && !!onStepClick\n\n const stepContent = (\n <>\n <div\n className={cn(\n 'relative shrink-0 flex items-center justify-center w-ds-sm h-ds-sm rounded-pill text-body-sm font-semibold',\n 'transition-[background-color,border-color,color] duration-moderate-01 ease-productive-standard',\n state === 'completed' && 'bg-accent-9 text-accent-fg',\n state === 'active' && 'bg-accent-9 text-accent-fg',\n state === 'pending' && 'bg-surface-raised text-surface-fg-subtle border border-surface-border-strong',\n )}\n >\n {icon ? <IconProvider size=\"sm\">{normalizeIcon(icon)}</IconProvider> : (state === 'completed' ? (\n <svg\n className=\"w-ico-sm h-ico-sm\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n ) : (\n _index + 1\n ))}\n </div>\n <div className=\"flex flex-col\">\n <span\n className={cn(\n 'text-body-md font-medium leading-ds-snug',\n state === 'pending'\n ? 'text-surface-fg-subtle'\n : 'text-surface-fg',\n )}\n >\n {label}\n </span>\n {description && (\n <span className=\"text-body-sm text-surface-fg-muted leading-ds-relaxed\">\n {description}\n </span>\n )}\n </div>\n </>\n )\n\n if (isClickable) {\n return (\n <div\n ref={ref}\n data-step=\"\"\n data-state={state}\n role=\"listitem\"\n className={cn(\n orientation === 'vertical' && 'py-ds-02',\n className,\n )}\n {...restProps}\n >\n <button\n type=\"button\"\n tabIndex={0}\n aria-label={`Go to step ${_index + 1}: ${label}`}\n onClick={() => onStepClick(_index)}\n className={cn(\n 'flex items-center gap-ds-03 cursor-pointer select-none',\n 'rounded-control px-ds-02 py-ds-01 -mx-ds-02 -my-ds-01',\n 'bg-transparent border-0',\n 'hover:bg-surface-raised-hover active:bg-surface-raised-active',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-8 focus-visible:ring-offset-2',\n 'transition-colors duration-fast-01 ease-productive-standard',\n )}\n >\n {stepContent}\n </button>\n </div>\n )\n }\n\n return (\n <div\n ref={ref}\n data-step=\"\"\n data-state={state}\n role=\"listitem\"\n aria-current={state === 'active' ? 'step' : undefined}\n aria-label={`Step ${_index + 1}: ${label}, ${state === 'completed' ? 'completed' : state === 'active' ? 'current' : 'upcoming'}`}\n className={cn(\n 'flex items-center gap-ds-03',\n orientation === 'vertical' && 'py-ds-02',\n className,\n )}\n {...restProps}\n >\n {stepContent}\n </div>\n )\n },\n)\n\n/**\n * Props for StepperContent — wraps the content panel for the active step, animating\n * slide transitions when `activeStep` changes. Detects forward vs backward navigation\n * and slides content accordingly.\n *\n * @example\n * <StepperContent activeStep={currentStep}>\n * {currentStep === 0 && <AccountForm />}\n * {currentStep === 1 && <ProfileForm />}\n * {currentStep === 2 && <ReviewPanel />}\n * </StepperContent>\n */\ninterface StepperContentProps {\n activeStep: number\n children: React.ReactNode\n className?: string\n}\n\nconst SLIDE_OFFSET = 40\n\nconst stepContentVariants = {\n enter: (direction: number) => ({\n opacity: 0,\n x: direction * SLIDE_OFFSET,\n }),\n center: {\n opacity: 1,\n x: 0,\n },\n exit: (direction: number) => ({\n opacity: 0,\n x: direction * -SLIDE_OFFSET,\n }),\n}\n\nfunction StepperContent({ activeStep, children, className }: StepperContentProps) {\n const prevStepRef = React.useRef(activeStep)\n const direction = activeStep >= prevStepRef.current ? 1 : -1\n\n React.useEffect(() => {\n prevStepRef.current = activeStep\n }, [activeStep])\n\n return (\n <div className={cn('relative overflow-hidden', className)}>\n <AnimatePresence mode=\"wait\" custom={direction}>\n <motion.div\n key={activeStep}\n custom={direction}\n variants={stepContentVariants}\n initial=\"enter\"\n animate=\"center\"\n exit=\"exit\"\n transition={springs.smooth}\n >\n {children}\n </motion.div>\n </AnimatePresence>\n </div>\n )\n}\n\nStepper.displayName = 'Stepper'\nStep.displayName = 'Step'\nStepperContent.displayName = 'StepperContent'\n\nexport { Step, Stepper, StepperContent, type StepperContentProps,type StepperProps, type StepProps }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stepper.js","names":[],"sources":["../../src/ui/stepper.tsx"],"sourcesContent":["'use client'\n\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { IconProvider } from './icon-context'\nimport type { IconInput } from './lib/icon-input'\nimport { springs } from './lib/motion'\nimport { normalizeIcon } from './lib/normalize-icon'\nimport { cn } from './lib/utils'\n\ntype StepState = 'completed' | 'active' | 'pending'\n\ntype StepperContextValue = {\n activeStep: number\n orientation: 'horizontal' | 'vertical'\n stepperId: string\n onStepClick?: (stepIndex: number) => void\n}\n\nconst StepperContext = React.createContext<StepperContextValue>({\n activeStep: 0,\n orientation: 'horizontal',\n stepperId: '',\n})\n\n/**\n * Props for Stepper — a multi-step progress indicator that renders a connected series of `<Step>`\n * children with connector lines, showing completed/active/pending states automatically.\n *\n * **`activeStep`:** 0-indexed. Step at `activeStep` is \"active\", steps before it are \"completed\",\n * steps after it are \"pending\". Update this value to advance the stepper.\n *\n * **Orientation:** `'horizontal'` (default, left-to-right) | `'vertical'` (top-to-bottom, for sidebars).\n *\n * **Children:** Must be `<Step>` components. The Stepper injects `_index` into each child automatically.\n *\n * @example\n * // Horizontal 3-step form wizard:\n * <Stepper activeStep={currentStep}>\n * <Step label=\"Account\" description=\"Create your credentials\" />\n * <Step label=\"Profile\" description=\"Add your details\" />\n * <Step label=\"Review\" description=\"Confirm and submit\" />\n * </Stepper>\n *\n * @example\n * // Vertical onboarding checklist in a sidebar:\n * <Stepper activeStep={completedSteps} orientation=\"vertical\">\n * <Step label=\"Connect workspace\" />\n * <Step label=\"Invite teammates\" />\n * <Step label=\"Set up billing\" />\n * </Stepper>\n *\n * @example\n * // Clickable completed steps — user can navigate back:\n * <Stepper activeStep={step} onStepClick={(i) => setStep(i)}>\n * <Step label=\"Account\" />\n * <Step label=\"Profile\" />\n * <Step label=\"Review\" />\n * </Stepper>\n * // These are just a few ways — feel free to combine props creatively!\n */\ninterface StepperProps extends React.HTMLAttributes<HTMLDivElement> {\n activeStep: number\n orientation?: 'horizontal' | 'vertical'\n /** Called when a completed step is clicked. Receives the 0-based step index. */\n onStepClick?: (stepIndex: number) => void\n children: React.ReactNode\n}\n\nconst Stepper = React.forwardRef<HTMLDivElement, StepperProps>(\n ({ activeStep, orientation = 'horizontal', onStepClick, className, children, ...props }, ref) => {\n const steps = React.Children.toArray(children)\n const stepperId = React.useId()\n const contextValue = React.useMemo(\n () => ({ activeStep, orientation, stepperId, onStepClick }),\n [activeStep, orientation, stepperId, onStepClick],\n )\n return (\n <StepperContext.Provider value={contextValue}>\n <div\n ref={ref}\n className={cn(\n 'flex gap-ds-02',\n orientation === 'vertical' ? 'flex-col' : 'flex-row items-center',\n className,\n )}\n role=\"list\"\n {...props}\n >\n {steps.map((child, index) => (\n <React.Fragment key={index}>\n {React.isValidElement<StepInternalProps>(child)\n ? React.cloneElement(child, { _index: index })\n : child}\n {index < steps.length - 1 && (\n <div\n className={cn(\n 'relative flex-1 overflow-hidden',\n orientation === 'vertical'\n ? 'ml-ds-04 w-ds-01 min-h-ds-05'\n : 'h-ds-01 min-w-ds-05',\n 'bg-surface-border',\n )}\n aria-hidden=\"true\"\n >\n {/* Filled portion — smooth CSS transition */}\n <div\n className={cn(\n 'absolute inset-0 bg-accent-9 transition-transform duration-moderate-02 ease-productive-standard',\n orientation === 'vertical' ? 'origin-top' : 'origin-left',\n )}\n style={{\n transform: `${orientation === 'vertical' ? 'scaleY' : 'scaleX'}(${index < activeStep ? 1 : 0})`,\n }}\n />\n </div>\n )}\n </React.Fragment>\n ))}\n </div>\n </StepperContext.Provider>\n )\n },\n)\n\n/**\n * Props for Step — a single step within a `<Stepper>`. The visual state (completed/active/pending)\n * is derived from the parent Stepper's `activeStep` and this step's position — you don't set it manually.\n *\n * @example\n * // Basic step with label and description:\n * <Step label=\"Payment\" description=\"Enter your card details\" />\n *\n * @example\n * // Step with a custom icon (overrides the default number/checkmark):\n * <Step label=\"Verified\" icon={<IconShieldCheck className=\"h-ico-sm w-ico-sm\" />} />\n */\ninterface StepProps extends React.HTMLAttributes<HTMLDivElement> {\n label: string\n description?: string\n icon?: IconInput\n}\n\ntype StepInternalProps = StepProps & { _index?: number }\n\nconst Step = React.forwardRef<HTMLDivElement, StepProps>(\n ({ label, description, icon, className, ...props }, ref) => {\n const { _index = 0, ...restProps } = props as StepInternalProps\n const { activeStep, orientation, stepperId, onStepClick } = React.useContext(StepperContext)\n const state: StepState = _index < activeStep ? 'completed' : _index === activeStep ? 'active' : 'pending'\n const isClickable = state === 'completed' && !!onStepClick\n\n const stepContent = (\n <>\n <div\n className={cn(\n 'relative shrink-0 flex items-center justify-center w-ds-sm h-ds-sm rounded-pill text-body-sm font-semibold',\n 'transition-[background-color,border-color,color] duration-moderate-01 ease-productive-standard',\n state === 'completed' && 'bg-accent-9 text-accent-fg',\n state === 'active' && 'bg-accent-9 text-accent-fg',\n state === 'pending' && 'bg-surface-panel text-surface-fg-subtle border border-surface-border-strong',\n )}\n >\n {icon ? <IconProvider size=\"sm\">{normalizeIcon(icon)}</IconProvider> : (state === 'completed' ? (\n <svg\n className=\"w-ico-sm h-ico-sm\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n ) : (\n _index + 1\n ))}\n </div>\n <div className=\"flex flex-col\">\n <span\n className={cn(\n 'text-body-md font-medium leading-ds-snug',\n state === 'pending'\n ? 'text-surface-fg-subtle'\n : 'text-surface-fg',\n )}\n >\n {label}\n </span>\n {description && (\n <span className=\"text-body-sm text-surface-fg-muted leading-ds-relaxed\">\n {description}\n </span>\n )}\n </div>\n </>\n )\n\n if (isClickable) {\n return (\n <div\n ref={ref}\n data-step=\"\"\n data-state={state}\n role=\"listitem\"\n className={cn(\n orientation === 'vertical' && 'py-ds-02',\n className,\n )}\n {...restProps}\n >\n <button\n type=\"button\"\n tabIndex={0}\n aria-label={`Go to step ${_index + 1}: ${label}`}\n onClick={() => onStepClick(_index)}\n className={cn(\n 'flex items-center gap-ds-03 cursor-pointer select-none',\n 'rounded-control px-ds-02 py-ds-01 -mx-ds-02 -my-ds-01',\n 'bg-transparent border-0',\n 'hover:bg-surface-panel-hover active:bg-surface-panel-active',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-8 focus-visible:ring-offset-2',\n 'transition-colors duration-fast-01 ease-productive-standard',\n )}\n >\n {stepContent}\n </button>\n </div>\n )\n }\n\n return (\n <div\n ref={ref}\n data-step=\"\"\n data-state={state}\n role=\"listitem\"\n aria-current={state === 'active' ? 'step' : undefined}\n aria-label={`Step ${_index + 1}: ${label}, ${state === 'completed' ? 'completed' : state === 'active' ? 'current' : 'upcoming'}`}\n className={cn(\n 'flex items-center gap-ds-03',\n orientation === 'vertical' && 'py-ds-02',\n className,\n )}\n {...restProps}\n >\n {stepContent}\n </div>\n )\n },\n)\n\n/**\n * Props for StepperContent — wraps the content panel for the active step, animating\n * slide transitions when `activeStep` changes. Detects forward vs backward navigation\n * and slides content accordingly.\n *\n * @example\n * <StepperContent activeStep={currentStep}>\n * {currentStep === 0 && <AccountForm />}\n * {currentStep === 1 && <ProfileForm />}\n * {currentStep === 2 && <ReviewPanel />}\n * </StepperContent>\n */\ninterface StepperContentProps {\n activeStep: number\n children: React.ReactNode\n className?: string\n}\n\nconst SLIDE_OFFSET = 40\n\nconst stepContentVariants = {\n enter: (direction: number) => ({\n opacity: 0,\n x: direction * SLIDE_OFFSET,\n }),\n center: {\n opacity: 1,\n x: 0,\n },\n exit: (direction: number) => ({\n opacity: 0,\n x: direction * -SLIDE_OFFSET,\n }),\n}\n\nfunction StepperContent({ activeStep, children, className }: StepperContentProps) {\n const prevStepRef = React.useRef(activeStep)\n const direction = activeStep >= prevStepRef.current ? 1 : -1\n\n React.useEffect(() => {\n prevStepRef.current = activeStep\n }, [activeStep])\n\n return (\n <div className={cn('relative overflow-hidden', className)}>\n <AnimatePresence mode=\"wait\" custom={direction}>\n <motion.div\n key={activeStep}\n custom={direction}\n variants={stepContentVariants}\n initial=\"enter\"\n animate=\"center\"\n exit=\"exit\"\n transition={springs.smooth}\n >\n {children}\n </motion.div>\n </AnimatePresence>\n </div>\n )\n}\n\nStepper.displayName = 'Stepper'\nStep.displayName = 'Step'\nStepperContent.displayName = 'StepperContent'\n\nexport { Step, Stepper, StepperContent, type StepperContentProps,type StepperProps, type StepProps }\n"],"mappings":"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"}
|
package/dist/ui/surface.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/ui/surface.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,QAAA,MAAM,eAAe;;;;;
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/ui/surface.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,QAAA,MAAM,eAAe;;;;;8EA6CnB,CAAA;AAEF,MAAM,WAAW,YACf,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAC1C,YAAY,CAAC,OAAO,eAAe,CAAC;IACtC;iEAC6D;IAC7D,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,QAAA,MAAM,OAAO,qFA2BZ,CAAA;AAGD,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,CAAA"}
|
package/dist/ui/surface.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Slot as e } from "../_chunks/primitives-internal.js";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
2
|
+
import { cn as t } from "./lib/utils.js";
|
|
3
|
+
import * as n from "react";
|
|
4
|
+
import { jsx as r } from "react/jsx-runtime";
|
|
5
|
+
import { cva as i } from "class-variance-authority";
|
|
6
6
|
//#region src/ui/surface.tsx
|
|
7
|
-
var a =
|
|
7
|
+
var a = i("", {
|
|
8
8
|
variants: {
|
|
9
9
|
elevation: {
|
|
10
|
-
flat: "bg-surface-
|
|
11
|
-
raised: "bg-surface-
|
|
10
|
+
flat: "bg-surface-panel",
|
|
11
|
+
raised: "bg-surface-panel shadow-raised",
|
|
12
12
|
floating: "bg-surface-overlay shadow-floating",
|
|
13
13
|
overlay: "bg-surface-overlay shadow-overlay"
|
|
14
14
|
},
|
|
@@ -36,14 +36,14 @@ var a = t("", {
|
|
|
36
36
|
radius: "surface",
|
|
37
37
|
bordered: !1
|
|
38
38
|
}
|
|
39
|
-
}), o =
|
|
39
|
+
}), o = n.forwardRef(({ className: n, elevation: i, padding: o, radius: s, bordered: c, asChild: l = !1, ...u }, d) => (typeof process < "u" && process.env.NODE_ENV !== "production" && c && (i ?? "raised") !== "flat" && console.warn("[shilp-sutra] <Surface> combines `bordered` with a raised `elevation` — the double-edge anti-pattern. Pick one: use elevation=\"flat\" with `bordered`, or drop `bordered` and let the elevation carry the edge."), /* @__PURE__ */ r(l ? e : "div", {
|
|
40
40
|
ref: d,
|
|
41
|
-
className:
|
|
42
|
-
elevation:
|
|
41
|
+
className: t(a({
|
|
42
|
+
elevation: i,
|
|
43
43
|
padding: o,
|
|
44
44
|
radius: s,
|
|
45
45
|
bordered: c
|
|
46
|
-
}),
|
|
46
|
+
}), n),
|
|
47
47
|
...u
|
|
48
48
|
})));
|
|
49
49
|
o.displayName = "Surface";
|
package/dist/ui/surface.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface.js","names":[],"sources":["../../src/ui/surface.tsx"],"sourcesContent":["// @server-safe\nimport { Slot } from '@primitives/react-slot'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\n\n// Bundlers (Vite, Next.js, webpack) define process.env.NODE_ENV; guard for raw ESM.\ndeclare const process: { env: { NODE_ENV?: string } } | undefined\n\nconst surfaceVariants = cva('', {\n variants: {\n // Elevation binds a surface-bg token to a shadow token — the one axis that\n // matters. `flat` sits on the page with no shadow (pair with `bordered` for a\n // Carbon-Tile edge); `raised` is the card level; `floating`/`overlay` are the\n // popover/menu/toast levels. bg + shadow always move together, so there is no\n // way to produce an un-tokened surface.\n elevation: {\n flat: 'bg-surface-
|
|
1
|
+
{"version":3,"file":"surface.js","names":[],"sources":["../../src/ui/surface.tsx"],"sourcesContent":["// @server-safe\nimport { Slot } from '@primitives/react-slot'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\n\n// Bundlers (Vite, Next.js, webpack) define process.env.NODE_ENV; guard for raw ESM.\ndeclare const process: { env: { NODE_ENV?: string } } | undefined\n\nconst surfaceVariants = cva('', {\n variants: {\n // Elevation binds a surface-bg token to a shadow token — the one axis that\n // matters. `flat` sits on the page with no shadow (pair with `bordered` for a\n // Carbon-Tile edge); `raised` is the card level; `floating`/`overlay` are the\n // popover/menu/toast levels. bg + shadow always move together, so there is no\n // way to produce an un-tokened surface.\n elevation: {\n flat: 'bg-surface-panel',\n // opt-in elevation: the consumer explicitly asked for a lift, the same way\n // Card's `elevated` variant does. Panels that merely sit on the page get\n // `flat` and are defined by their edge.\n raised: 'bg-surface-panel shadow-raised',\n floating: 'bg-surface-overlay shadow-floating',\n overlay: 'bg-surface-overlay shadow-overlay',\n },\n // Simple, symmetric all-side padding — NOT Card's gap model. This is what keeps\n // Surface low-level: a plain box you can drop content into. Reach for Card when\n // you want the header/content/footer slot rhythm.\n padding: {\n none: '',\n sm: 'p-ds-04',\n md: 'p-ds-05',\n lg: 'p-ds-06',\n },\n radius: {\n none: 'rounded-none',\n control: 'rounded-control',\n surface: 'rounded-surface',\n overlay: 'rounded-overlay',\n pill: 'rounded-pill',\n },\n // Border-led edge (Carbon-Tile style). Meant for `elevation=\"flat\"`; combining it\n // with a shadowed elevation is the double-edge anti-pattern and dev-warns below.\n bordered: {\n true: 'border border-surface-border-strong',\n false: '',\n },\n },\n defaultVariants: {\n elevation: 'raised',\n padding: 'none',\n radius: 'surface',\n bordered: false,\n },\n})\n\nexport interface SurfaceProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof surfaceVariants> {\n /** Render as the passed child element (Radix Slot) instead of a `div` — wrap a link,\n * section, or `motion.div` without adding a wrapper node. */\n asChild?: boolean\n}\n\n/**\n * Surface — the low-level elevated container primitive.\n *\n * The building block every panel, card, popover, and toast surface sits on. It owns\n * exactly one job: paint a tokened surface (background + shadow), with optional radius,\n * padding, and border. It has no slots, no color axis, and no motion — those belong to\n * the components composed on top of it (Card = Surface + gap-model padding + slots).\n *\n * **Elevation:** `flat` (bg, no shadow — pair with `bordered`) | `raised` (card level,\n * default) | `floating` (toasts, floating toolbars) | `overlay` (popovers, menus, dialogs).\n *\n * **Padding:** `none` (default) | `sm` (12px) | `md` (16px) | `lg` (24px) — simple all-side.\n *\n * **Edge vs. elevation:** use a shadow (`raised`/`floating`/`overlay`) OR a `bordered`\n * `flat` — never both (the double-edge anti-pattern; dev-warns).\n *\n * @example\n * // A raised panel with comfortable padding:\n * <Surface elevation=\"raised\" padding=\"md\">…</Surface>\n *\n * @example\n * // An on-page, border-led tile (no shadow):\n * <Surface elevation=\"flat\" bordered padding=\"sm\">…</Surface>\n *\n * @example\n * // Wrap a link as the surface itself:\n * <Surface asChild elevation=\"raised\" padding=\"sm\">\n * <a href=\"/upgrade\">…</a>\n * </Surface>\n */\nconst Surface = React.forwardRef<HTMLDivElement, SurfaceProps>(\n (\n { className, elevation, padding, radius, bordered, asChild = false, ...props },\n ref,\n ) => {\n if (\n typeof process !== 'undefined' &&\n process?.env.NODE_ENV !== 'production' &&\n bordered &&\n (elevation ?? 'raised') !== 'flat'\n ) {\n console.warn(\n '[shilp-sutra] <Surface> combines `bordered` with a raised `elevation` — the double-edge anti-pattern. Pick one: use elevation=\"flat\" with `bordered`, or drop `bordered` and let the elevation carry the edge.',\n )\n }\n const Comp = asChild ? Slot : 'div'\n return (\n <Comp\n ref={ref}\n className={cn(\n surfaceVariants({ elevation, padding, radius, bordered }),\n className,\n )}\n {...props}\n />\n )\n },\n)\nSurface.displayName = 'Surface'\n\nexport { Surface, surfaceVariants }\n"],"mappings":";;;;;;AAUA,IAAM,IAAkB,EAAI,IAAI;CAC9B,UAAU;EAMR,WAAW;GACT,MAAM;GAIN,QAAQ;GACR,UAAU;GACV,SAAS;EACX;EAIA,SAAS;GACP,MAAM;GACN,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EACA,QAAQ;GACN,MAAM;GACN,SAAS;GACT,SAAS;GACT,SAAS;GACT,MAAM;EACR;EAGA,UAAU;GACR,MAAM;GACN,OAAO;EACT;CACF;CACA,iBAAiB;EACf,WAAW;EACX,SAAS;EACT,QAAQ;EACR,UAAU;CACZ;AACF,CAAC,GAwCK,IAAU,EAAM,YAElB,EAAE,cAAW,cAAW,YAAS,WAAQ,aAAU,aAAU,IAAO,GAAG,KACvE,OAGE,OAAO,UAAY,OAAA,QAAA,IAAA,aACO,gBAC1B,MACC,KAAa,cAAc,UAE5B,QAAQ,KACN,kNACF,GAIA,kBAFW,IAAU,IAAO,OAE5B;CACO;CACL,WAAW,EACT,EAAgB;EAAE;EAAW;EAAS;EAAQ;CAAS,CAAC,GACxD,CACF;CACA,GAAI;AACL,CAAA,EAGP;AACA,EAAQ,cAAc"}
|
package/dist/ui/switch.js
CHANGED
|
@@ -37,7 +37,7 @@ var u = {
|
|
|
37
37
|
v === void 0 && O(e), b?.(e);
|
|
38
38
|
}, [v, b]);
|
|
39
39
|
return /* @__PURE__ */ s(i, {
|
|
40
|
-
className: t("touch-target peer inline-flex shrink-0 cursor-pointer items-center rounded-pill border-2 border-surface-border-
|
|
40
|
+
className: t("touch-target peer inline-flex shrink-0 cursor-pointer items-center rounded-pill border-2 border-surface-border-interactive shadow-raised transition-colors duration-fast-01 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-action-disabled data-[state=checked]:border-transparent data-[state=unchecked]:bg-surface-border-strong data-[state=unchecked]:hover:bg-surface-panel-active", M, f[g], w && d[w], p),
|
|
41
41
|
checked: v,
|
|
42
42
|
defaultChecked: y,
|
|
43
43
|
onCheckedChange: F,
|
package/dist/ui/switch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switch.js","names":[],"sources":["../../src/ui/switch.tsx"],"sourcesContent":["'use client'\n\nimport * as SwitchPrimitives from \"@primitives/react-switch\"\nimport { motion, useReducedMotion } from 'framer-motion'\nimport * as React from \"react\"\n\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { springs } from './lib/motion'\nimport { cn } from \"./lib/utils\"\n\nconst sizeConfig = {\n sm: { track: 'h-6 w-[38px]', thumb: 'h-5 w-5', travel: 16 },\n md: { track: 'h-6 w-11', thumb: 'h-ico-md w-ico-md', travel: 20 },\n lg: { track: 'h-7 w-[52px]', thumb: 'h-6 w-6', travel: 24 },\n} as const\n\n/** Validation border + checked-track tint per state (overrides `color` when set). */\nconst stateTintClasses: Record<Exclude<FieldState, 'default'>, string> = {\n error: 'border-error-7 data-[state=checked]:bg-error-9',\n warning: 'border-warning-7 data-[state=checked]:bg-warning-9',\n success: 'border-success-7 data-[state=checked]:bg-success-9',\n}\n\nconst colorMap = {\n accent: 'data-[state=checked]:bg-accent-9',\n success: 'data-[state=checked]:bg-success-9',\n warning: 'data-[state=checked]:bg-warning-9',\n} as const\n\nexport interface SwitchProps extends React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> {\n /** Validation/feedback state. `'error'` also sets `aria-invalid`. Inherited from `FormField` when omitted. Distinct from `color` (the ON-track tint). */\n state?: FieldState\n size?: 'sm' | 'md' | 'lg'\n color?: 'accent' | 'success' | 'warning'\n thumbIcon?: React.ReactNode\n}\n\nconst Switch = React.forwardRef<\n React.ElementRef<typeof SwitchPrimitives.Root>,\n SwitchProps\n>(({ className, state: stateProp, size = 'md', color = 'accent', thumbIcon, checked, defaultChecked, onCheckedChange, ...props }, ref) => {\n const fieldCtx = useFormField()\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n // Track checked state internally to drive Framer Motion animation\n const [internalChecked, setInternalChecked] = React.useState(defaultChecked ?? false)\n const isChecked = checked !== undefined ? checked : internalChecked\n const reduced = useReducedMotion()\n // Thumb travels toward the inline-end — mirror it under RTL.\n const isRtl =\n typeof document !== 'undefined' &&\n (document.dir === 'rtl' || document.documentElement.dir === 'rtl')\n const { track, thumb, travel } = sizeConfig[size]\n\n const handleCheckedChange = React.useCallback(\n (value: boolean) => {\n if (checked === undefined) {\n setInternalChecked(value)\n }\n onCheckedChange?.(value)\n },\n [checked, onCheckedChange],\n )\n\n return (\n <SwitchPrimitives.Root\n className={cn(\n \"touch-target peer inline-flex shrink-0 cursor-pointer items-center rounded-pill border-2 border-surface-border-
|
|
1
|
+
{"version":3,"file":"switch.js","names":[],"sources":["../../src/ui/switch.tsx"],"sourcesContent":["'use client'\n\nimport * as SwitchPrimitives from \"@primitives/react-switch\"\nimport { motion, useReducedMotion } from 'framer-motion'\nimport * as React from \"react\"\n\nimport { useFormField } from './form'\nimport { type FieldState, resolveFieldState } from './lib/field-state'\nimport { springs } from './lib/motion'\nimport { cn } from \"./lib/utils\"\n\nconst sizeConfig = {\n sm: { track: 'h-6 w-[38px]', thumb: 'h-5 w-5', travel: 16 },\n md: { track: 'h-6 w-11', thumb: 'h-ico-md w-ico-md', travel: 20 },\n lg: { track: 'h-7 w-[52px]', thumb: 'h-6 w-6', travel: 24 },\n} as const\n\n/** Validation border + checked-track tint per state (overrides `color` when set). */\nconst stateTintClasses: Record<Exclude<FieldState, 'default'>, string> = {\n error: 'border-error-7 data-[state=checked]:bg-error-9',\n warning: 'border-warning-7 data-[state=checked]:bg-warning-9',\n success: 'border-success-7 data-[state=checked]:bg-success-9',\n}\n\nconst colorMap = {\n accent: 'data-[state=checked]:bg-accent-9',\n success: 'data-[state=checked]:bg-success-9',\n warning: 'data-[state=checked]:bg-warning-9',\n} as const\n\nexport interface SwitchProps extends React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> {\n /** Validation/feedback state. `'error'` also sets `aria-invalid`. Inherited from `FormField` when omitted. Distinct from `color` (the ON-track tint). */\n state?: FieldState\n size?: 'sm' | 'md' | 'lg'\n color?: 'accent' | 'success' | 'warning'\n thumbIcon?: React.ReactNode\n}\n\nconst Switch = React.forwardRef<\n React.ElementRef<typeof SwitchPrimitives.Root>,\n SwitchProps\n>(({ className, state: stateProp, size = 'md', color = 'accent', thumbIcon, checked, defaultChecked, onCheckedChange, ...props }, ref) => {\n const fieldCtx = useFormField()\n const state = resolveFieldState(stateProp, fieldCtx.state)\n const ariaDescribedBy = props['aria-describedby'] ?? fieldCtx.helperTextId\n const ariaRequired = props['aria-required'] ?? fieldCtx.required\n\n // Track checked state internally to drive Framer Motion animation\n const [internalChecked, setInternalChecked] = React.useState(defaultChecked ?? false)\n const isChecked = checked !== undefined ? checked : internalChecked\n const reduced = useReducedMotion()\n // Thumb travels toward the inline-end — mirror it under RTL.\n const isRtl =\n typeof document !== 'undefined' &&\n (document.dir === 'rtl' || document.documentElement.dir === 'rtl')\n const { track, thumb, travel } = sizeConfig[size]\n\n const handleCheckedChange = React.useCallback(\n (value: boolean) => {\n if (checked === undefined) {\n setInternalChecked(value)\n }\n onCheckedChange?.(value)\n },\n [checked, onCheckedChange],\n )\n\n return (\n <SwitchPrimitives.Root\n className={cn(\n \"touch-target peer inline-flex shrink-0 cursor-pointer items-center rounded-pill border-2 border-surface-border-interactive shadow-raised transition-colors duration-fast-01 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-action-disabled data-[state=checked]:border-transparent data-[state=unchecked]:bg-surface-border-strong data-[state=unchecked]:hover:bg-surface-panel-active\",\n track,\n colorMap[color],\n state && stateTintClasses[state],\n className\n )}\n checked={checked}\n defaultChecked={defaultChecked}\n onCheckedChange={handleCheckedChange}\n aria-invalid={state === 'error' || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required={ariaRequired || undefined}\n {...props}\n // Explicit id wins; otherwise adopt FormField's inputId so <Label htmlFor>\n // resolves. Without this the label points at nothing and the switch has NO\n // accessible name, despite the consumer wiring FormField + Label correctly.\n id={props.id ?? fieldCtx.inputId}\n ref={ref}\n >\n <SwitchPrimitives.Thumb asChild>\n <motion.span\n className={cn(\n \"pointer-events-none flex items-center justify-center rounded-pill bg-accent-fg shadow-raised-hover ring-0\",\n thumb\n )}\n animate={{ x: (isChecked ? travel : 0) * (isRtl ? -1 : 1) }}\n whileTap={reduced ? undefined : { scale: 0.85 }}\n transition={reduced ? { duration: 0 } : springs.snappy}\n >\n {thumbIcon}\n </motion.span>\n </SwitchPrimitives.Thumb>\n </SwitchPrimitives.Root>\n )\n})\nSwitch.displayName = SwitchPrimitives.Root.displayName\n\nexport { Switch }\n"],"mappings":";;;;;;;;;;AAWA,IAAM,IAAa;CACjB,IAAI;EAAE,OAAO;EAAgB,OAAO;EAAW,QAAQ;CAAG;CAC1D,IAAI;EAAE,OAAO;EAAY,OAAO;EAAqB,QAAQ;CAAG;CAChE,IAAI;EAAE,OAAO;EAAgB,OAAO;EAAW,QAAQ;CAAG;AAC5D,GAGM,IAAmE;CACvE,OAAO;CACP,SAAS;CACT,SAAS;AACX,GAEM,IAAW;CACf,QAAQ;CACR,SAAS;CACT,SAAS;AACX,GAUM,IAAS,EAAM,YAGlB,EAAE,cAAW,OAAO,GAAW,UAAO,MAAM,WAAQ,UAAU,cAAW,YAAS,mBAAgB,oBAAiB,GAAG,KAAS,MAAQ;CACxI,IAAM,IAAW,EAAa,GACxB,IAAQ,EAAkB,GAAW,EAAS,KAAK,GACnD,IAAkB,EAAM,uBAAuB,EAAS,cACxD,IAAe,EAAM,oBAAoB,EAAS,UAGlD,CAAC,GAAiB,KAAsB,EAAM,SAAS,KAAkB,EAAK,GAC9E,IAAY,MAAY,KAAA,IAAsB,IAAV,GACpC,IAAU,EAAiB,GAE3B,IACJ,OAAO,WAAa,QACnB,SAAS,QAAQ,SAAS,SAAS,gBAAgB,QAAQ,QACxD,EAAE,UAAO,UAAO,cAAW,EAAW,IAEtC,IAAsB,EAAM,aAC/B,MAAmB;EAIlB,AAHI,MAAY,KAAA,KACd,EAAmB,CAAK,GAE1B,IAAkB,CAAK;CACzB,GACA,CAAC,GAAS,CAAe,CAC3B;CAEA,OACE,kBAAC,GAAD;EACE,WAAW,EACT,meACA,GACA,EAAS,IACT,KAAS,EAAiB,IAC1B,CACF;EACS;EACO;EAChB,iBAAiB;EACjB,gBAAc,MAAU,WAAW,KAAA;EACnC,oBAAkB;EAClB,iBAAe,KAAgB,KAAA;EAC/B,GAAI;EAIJ,IAAI,EAAM,MAAM,EAAS;EACpB;YAEL,kBAAC,GAAD;GAAwB,SAAA;aACtB,kBAAC,EAAO,MAAR;IACE,WAAW,EACT,6GACA,CACF;IACA,SAAS,EAAE,IAAI,IAAY,IAAS,MAAM,IAAQ,KAAK,GAAG;IAC1D,UAAU,IAAU,KAAA,IAAY,EAAE,OAAO,IAAK;IAC9C,YAAY,IAAU,EAAE,UAAU,EAAE,IAAI,EAAQ;cAE/C;GACU,CAAA;EACS,CAAA;CACH,CAAA;AAE3B,CAAC;AACD,EAAO,cAAA,EAAoC"}
|
package/dist/ui/table.js
CHANGED
|
@@ -35,7 +35,7 @@ var s = t.forwardRef(({ className: t, ...r }, i) => /* @__PURE__ */ n("tfoot", {
|
|
|
35
35
|
s.displayName = "TableFooter";
|
|
36
36
|
var c = t.forwardRef(({ className: t, ...r }, i) => /* @__PURE__ */ n("tr", {
|
|
37
37
|
ref: i,
|
|
38
|
-
className: e("group/row border-b border-surface-border-subtle transition-colors hover:bg-surface-
|
|
38
|
+
className: e("group/row border-b border-surface-border-subtle transition-colors hover:bg-surface-panel-hover data-[state=selected]:bg-accent-3 data-[state=selected]:hover:bg-accent-4 data-[state=selected]:forced-colors:outline data-[state=selected]:forced-colors:outline-1", "has-[[data-slot=row-link]:focus-visible]:outline-2 has-[[data-slot=row-link]:focus-visible]:outline-accent-9 has-[[data-slot=row-link]:focus-visible]:-outline-offset-2", t),
|
|
39
39
|
...r
|
|
40
40
|
}));
|
|
41
41
|
c.displayName = "TableRow";
|
package/dist/ui/table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.js","names":[],"sources":["../../src/ui/table.tsx"],"sourcesContent":["// @server-safe\nimport * as React from \"react\"\n\nimport { cn } from \"./lib/utils\"\n\n/** Row density — sets --table-py, which header and body cells both read. */\ntype TableDensity = 'compact' | 'standard' | 'comfortable'\n\n// One variable pair drives the table's spacing (same pattern as Card):\n// --table-py vertical cell padding per density (4 / 8 / 12px → rows ≈ 29 / 37 / 45px)\n// --table-edge first/last-cell inline padding — inherits --card-spacing when the table\n// sits inside a Card, so edge columns align with the card's slots;\n// falls back to ds-04 (12px) standalone.\nconst densityClasses: Record<TableDensity, string> = {\n compact: '[--table-py:var(--spacing-ds-02)]',\n standard: '[--table-py:var(--spacing-ds-03)]',\n comfortable: '[--table-py:var(--spacing-ds-04)]',\n}\n\nexport interface TableCellBaseProps {\n /**\n * Quantitative column: right-aligns and uses tabular figures so digits line up.\n * Keep decimal places consistent per column; identifier-numbers (dates, phones,\n * IDs) stay left-aligned — they're names, not quantities.\n */\n numeric?: boolean\n}\n\nexport interface TableProps extends React.HTMLAttributes<HTMLTableElement> {\n /** Row density — vertical cell padding for header + body. @default 'standard' */\n density?: TableDensity\n /**\n * Zebra striping (even body rows get the faintest surface step). Opt-in only —\n * for very wide/dense tables; hairline separators are the default row cue.\n */\n striped?: boolean\n}\n\nconst Table = React.forwardRef<HTMLTableElement, TableProps>(\n ({ className, density = 'standard', striped, ...props }, ref) => (\n <div className=\"relative w-full overflow-auto\">\n <table\n ref={ref}\n className={cn(\n // overflow-x-clip contains TableRowLink's 100vw stretch pseudo-element\n // without creating a horizontal scrollbar (the wrapper owns scrolling).\n \"w-full caption-bottom text-body-md overflow-x-clip [--table-edge:var(--card-spacing,var(--spacing-ds-04))]\",\n densityClasses[density],\n striped && \"[&_tbody_tr:nth-child(even)]:bg-surface-base\",\n className,\n )}\n {...props}\n />\n </div>\n ),\n)\nTable.displayName = \"Table\"\n\nconst TableHeader = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <thead ref={ref} className={cn(\"[&_tr]:border-b [&_tr]:border-surface-border-subtle\", className)} {...props} />\n))\nTableHeader.displayName = \"TableHeader\"\n\nconst TableBody = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tbody\n ref={ref}\n className={cn(\"[&_tr:last-child]:border-0\", className)}\n {...props}\n />\n))\nTableBody.displayName = \"TableBody\"\n\nconst TableFooter = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tfoot\n ref={ref}\n className={cn(\n // surface-base band, not raised@50% — the footer must read against the\n // card surface the table lives on (same mis-mapped shadcn muted/50 family\n // as the row-hover bug).\n \"border-t border-surface-border-subtle bg-surface-base font-medium [&>tr]:last:border-b-0\",\n className\n )}\n {...props}\n />\n))\nTableFooter.displayName = \"TableFooter\"\n\nconst TableRow = React.forwardRef<\n HTMLTableRowElement,\n React.HTMLAttributes<HTMLTableRowElement>\n>(({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\n //
|
|
1
|
+
{"version":3,"file":"table.js","names":[],"sources":["../../src/ui/table.tsx"],"sourcesContent":["// @server-safe\nimport * as React from \"react\"\n\nimport { cn } from \"./lib/utils\"\n\n/** Row density — sets --table-py, which header and body cells both read. */\ntype TableDensity = 'compact' | 'standard' | 'comfortable'\n\n// One variable pair drives the table's spacing (same pattern as Card):\n// --table-py vertical cell padding per density (4 / 8 / 12px → rows ≈ 29 / 37 / 45px)\n// --table-edge first/last-cell inline padding — inherits --card-spacing when the table\n// sits inside a Card, so edge columns align with the card's slots;\n// falls back to ds-04 (12px) standalone.\nconst densityClasses: Record<TableDensity, string> = {\n compact: '[--table-py:var(--spacing-ds-02)]',\n standard: '[--table-py:var(--spacing-ds-03)]',\n comfortable: '[--table-py:var(--spacing-ds-04)]',\n}\n\nexport interface TableCellBaseProps {\n /**\n * Quantitative column: right-aligns and uses tabular figures so digits line up.\n * Keep decimal places consistent per column; identifier-numbers (dates, phones,\n * IDs) stay left-aligned — they're names, not quantities.\n */\n numeric?: boolean\n}\n\nexport interface TableProps extends React.HTMLAttributes<HTMLTableElement> {\n /** Row density — vertical cell padding for header + body. @default 'standard' */\n density?: TableDensity\n /**\n * Zebra striping (even body rows get the faintest surface step). Opt-in only —\n * for very wide/dense tables; hairline separators are the default row cue.\n */\n striped?: boolean\n}\n\nconst Table = React.forwardRef<HTMLTableElement, TableProps>(\n ({ className, density = 'standard', striped, ...props }, ref) => (\n <div className=\"relative w-full overflow-auto\">\n <table\n ref={ref}\n className={cn(\n // overflow-x-clip contains TableRowLink's 100vw stretch pseudo-element\n // without creating a horizontal scrollbar (the wrapper owns scrolling).\n \"w-full caption-bottom text-body-md overflow-x-clip [--table-edge:var(--card-spacing,var(--spacing-ds-04))]\",\n densityClasses[density],\n striped && \"[&_tbody_tr:nth-child(even)]:bg-surface-base\",\n className,\n )}\n {...props}\n />\n </div>\n ),\n)\nTable.displayName = \"Table\"\n\nconst TableHeader = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <thead ref={ref} className={cn(\"[&_tr]:border-b [&_tr]:border-surface-border-subtle\", className)} {...props} />\n))\nTableHeader.displayName = \"TableHeader\"\n\nconst TableBody = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tbody\n ref={ref}\n className={cn(\"[&_tr:last-child]:border-0\", className)}\n {...props}\n />\n))\nTableBody.displayName = \"TableBody\"\n\nconst TableFooter = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tfoot\n ref={ref}\n className={cn(\n // surface-base band, not raised@50% — the footer must read against the\n // card surface the table lives on (same mis-mapped shadcn muted/50 family\n // as the row-hover bug).\n \"border-t border-surface-border-subtle bg-surface-base font-medium [&>tr]:last:border-b-0\",\n className\n )}\n {...props}\n />\n))\nTableFooter.displayName = \"TableFooter\"\n\nconst TableRow = React.forwardRef<\n HTMLTableRowElement,\n React.HTMLAttributes<HTMLTableRowElement>\n>(({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\n // panel-hover, NOT panel — tables live on cards (surface-panel), so a\n // surface-panel hover would be invisible (the 0.44-era port bug).\n // selected+hover gets its own explicit step — without it the hover and\n // selected classes tie on specificity and stylesheet order decides.\n // `group/row` lets TableRowActions reveal on row hover/focus; the has-[]\n // rule draws a row-level focus ring when a TableRowLink inside is\n // keyboard-focused (the anchor itself suppresses its own ring).\n \"group/row border-b border-surface-border-subtle transition-colors hover:bg-surface-panel-hover data-[state=selected]:bg-accent-3 data-[state=selected]:hover:bg-accent-4 data-[state=selected]:forced-colors:outline data-[state=selected]:forced-colors:outline-1\",\n \"has-[[data-slot=row-link]:focus-visible]:outline-2 has-[[data-slot=row-link]:focus-visible]:outline-accent-9 has-[[data-slot=row-link]:focus-visible]:-outline-offset-2\",\n className,\n )}\n {...props}\n />\n))\nTableRow.displayName = \"TableRow\"\n\nconst TableHead = React.forwardRef<\n HTMLTableCellElement,\n React.ThHTMLAttributes<HTMLTableCellElement> & TableCellBaseProps\n>(({ className, numeric, ...props }, ref) => (\n <th\n ref={ref}\n scope=\"col\"\n className={cn(\n // Header is quieter than the data: one step smaller, medium, muted.\n // Height tracks density via the same --table-py the body cells read.\n \"py-(--table-py) px-ds-04 first:pl-(--table-edge) last:pr-(--table-edge) text-left align-middle text-body-sm font-medium text-surface-fg-muted [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n numeric && \"text-right\",\n className,\n )}\n {...props}\n />\n))\nTableHead.displayName = \"TableHead\"\n\nconst TableCell = React.forwardRef<\n HTMLTableCellElement,\n React.TdHTMLAttributes<HTMLTableCellElement> & TableCellBaseProps\n>(({ className, numeric, ...props }, ref) => (\n <td\n ref={ref}\n className={cn(\n \"py-(--table-py) px-ds-04 first:pl-(--table-edge) last:pr-(--table-edge) align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n numeric && \"text-right tabular-nums\",\n className,\n )}\n {...props}\n />\n))\nTableCell.displayName = \"TableCell\"\n\nexport interface TableRowActionsProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Always show the actions instead of revealing on row hover/focus. Use for\n * tables where actions must be permanently discoverable (the GitLab stance),\n * or when the row has few columns and the density win doesn't matter.\n */\n persist?: boolean\n}\n\n/**\n * Right-aligned action cluster for a table row, revealed on row hover — and,\n * critically, on keyboard focus: the buttons stay in the tab order permanently\n * (opacity reveal, never display:none) and appear the moment focus enters the\n * row (WCAG 1.4.13). On touch devices (no hover) they are always visible.\n *\n * Give the actions column a visually-hidden header: `<TableHead><span className=\"sr-only\">Actions</span></TableHead>`.\n *\n * @example\n * <TableCell>\n * <TableRowActions>\n * <IconButton size=\"xs\" variant=\"ghost\" aria-label={`Download ${name}`} icon={<IconDownload />} />\n * <IconButton size=\"xs\" variant=\"ghost\" aria-label={`Delete ${name}`} icon={<IconTrash />} />\n * </TableRowActions>\n * </TableCell>\n */\nconst TableRowActions = React.forwardRef<HTMLDivElement, TableRowActionsProps>(\n ({ className, persist, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"flex items-center justify-end gap-ds-01 transition-opacity duration-fast-01 ease-productive-standard\",\n persist\n ? \"opacity-100\"\n : \"opacity-0 group-hover/row:opacity-100 group-focus-within/row:opacity-100 pointer-coarse:opacity-100\",\n className,\n )}\n {...props}\n />\n ),\n)\nTableRowActions.displayName = \"TableRowActions\"\n\nconst TableCaption = React.forwardRef<\n HTMLTableCaptionElement,\n React.HTMLAttributes<HTMLTableCaptionElement>\n>(({ className, ...props }, ref) => (\n <caption\n ref={ref}\n className={cn(\"mt-ds-05 text-body-md text-surface-fg-muted\", className)}\n {...props}\n />\n))\nTableCaption.displayName = \"TableCaption\"\n\nexport type TableRowProps = React.HTMLAttributes<HTMLTableRowElement>\nexport type TableCellProps = React.TdHTMLAttributes<HTMLTableCellElement>\nexport type { TableDensity }\n\nexport {\n Table,\n TableBody,\n TableCaption,\n TableCell,\n TableFooter,\n TableHead,\n TableHeader,\n TableRow,\n TableRowActions,\n}\n"],"mappings":";;;;AAaA,IAAM,IAA+C;CACnD,SAAS;CACT,UAAU;CACV,aAAa;AACf,GAqBM,IAAQ,EAAM,YACjB,EAAE,cAAW,aAAU,YAAY,YAAS,GAAG,KAAS,MACvD,kBAAC,OAAD;CAAK,WAAU;WACb,kBAAC,SAAD;EACO;EACL,WAAW,EAGT,8GACA,EAAe,IACf,KAAW,gDACX,CACF;EACA,GAAI;CACL,CAAA;AACE,CAAA,CAET;AACA,EAAM,cAAc;AAEpB,IAAM,IAAc,EAAM,YAGvB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,SAAD;CAAY;CAAK,WAAW,EAAG,uDAAuD,CAAS;CAAG,GAAI;AAAQ,CAAA,CAC/G;AACD,EAAY,cAAc;AAE1B,IAAM,IAAY,EAAM,YAGrB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,SAAD;CACO;CACL,WAAW,EAAG,8BAA8B,CAAS;CACrD,GAAI;AACL,CAAA,CACF;AACD,EAAU,cAAc;AAExB,IAAM,IAAc,EAAM,YAGvB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,SAAD;CACO;CACL,WAAW,EAIT,4FACA,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAY,cAAc;AAE1B,IAAM,IAAW,EAAM,YAGpB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,MAAD;CACO;CACL,WAAW,EAQT,sQACA,2KACA,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAS,cAAc;AAEvB,IAAM,IAAY,EAAM,YAGrB,EAAE,cAAW,YAAS,GAAG,KAAS,MACnC,kBAAC,MAAD;CACO;CACL,OAAM;CACN,WAAW,EAGT,qNACA,KAAW,cACX,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAU,cAAc;AAExB,IAAM,IAAY,EAAM,YAGrB,EAAE,cAAW,YAAS,GAAG,KAAS,MACnC,kBAAC,MAAD;CACO;CACL,WAAW,EACT,4JACA,KAAW,2BACX,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAU,cAAc;AA2BxB,IAAM,IAAkB,EAAM,YAC3B,EAAE,cAAW,YAAS,GAAG,KAAS,MACjC,kBAAC,OAAD;CACO;CACL,WAAW,EACT,wGACA,IACI,gBACA,uGACJ,CACF;CACA,GAAI;AACL,CAAA,CAEL;AACA,EAAgB,cAAc;AAE9B,IAAM,IAAe,EAAM,YAGxB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,WAAD;CACO;CACL,WAAW,EAAG,+CAA+C,CAAS;CACtE,GAAI;AACL,CAAA,CACF;AACD,EAAa,cAAc"}
|
package/dist/ui/tabs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/ui/tabs.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,aAAa,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,KAAK,eAAe,GAAG,YAAY,GAAG,UAAU,CAAA;AAIhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,QAAA,MAAM,IAAI,mJAyCR,CAAA;AAGF,0EAA0E;AAC1E,MAAM,MAAM,SAAS,GAAG,KAAK,CAAC,wBAAwB,CAAC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;AAGjF,KAAK,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAClC,KAAK,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAA;AAgBrC,QAAA,MAAM,gBAAgB;;;;
|
|
1
|
+
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/ui/tabs.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,aAAa,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,KAAK,eAAe,GAAG,YAAY,GAAG,UAAU,CAAA;AAIhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,QAAA,MAAM,IAAI,mJAyCR,CAAA;AAGF,0EAA0E;AAC1E,MAAM,MAAM,SAAS,GAAG,KAAK,CAAC,wBAAwB,CAAC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;AAGjF,KAAK,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAClC,KAAK,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAA;AAgBrC,QAAA,MAAM,gBAAgB;;;;8EA0BpB,CAAA;AAEF,QAAA,MAAM,mBAAmB;;;8EAuBxB,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,aACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,aAAa,CAAC,IAAI,CAAC,EAC/D,YAAY,CAAC,OAAO,gBAAgB,CAAC;IACvC,kFAAkF;IAClF,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB;AAED,QAAA,MAAM,QAAQ,sFA6BZ,CAAA;AAGF,MAAM,WAAW,gBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,aAAa,CAAC,OAAO,CAAC,EAClE,YAAY,CAAC,OAAO,mBAAmB,CAAC;CAAG;AAa/C,QAAA,MAAM,WAAW,4FAsDf,CAAA;AAGF,QAAA,MAAM,WAAW,0JA0Bf,CAAA;AAGF,kFAAkF;AAClF,MAAM,MAAM,gBAAgB,GAAG,KAAK,CAAC,wBAAwB,CAAC,OAAO,aAAa,CAAC,OAAO,CAAC,CAAA;AAE3F,OAAO,EAAE,IAAI,EAAE,WAAW,EAAC,QAAQ,EAAE,WAAW,EAAE,CAAA;AAClD,YAAY,EAAE,SAAS,EAAE,eAAe,EAAC,QAAQ,EAAE,CAAA"}
|
package/dist/ui/tabs.js
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { springs as e, tweens as t } from "./lib/motion.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
3
|
+
import { cn as n } from "./lib/utils.js";
|
|
4
|
+
import { Content as r, List as i, Root2 as a, Trigger as o } from "../_chunks/primitives/react-tabs.js";
|
|
5
|
+
import * as s from "react";
|
|
6
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
7
|
+
import { LayoutGroup as u, motion as d } from "framer-motion";
|
|
8
|
+
import { cva as f } from "class-variance-authority";
|
|
9
9
|
//#region src/ui/tabs.tsx
|
|
10
|
-
var p =
|
|
11
|
-
let [u, d] =
|
|
12
|
-
|
|
10
|
+
var p = s.createContext(void 0), m = s.createContext("horizontal"), h = s.forwardRef(({ value: e, defaultValue: t, onValueChange: r, orientation: i, ...o }, l) => {
|
|
11
|
+
let [u, d] = s.useState(e ?? t ?? "");
|
|
12
|
+
s.useEffect(() => {
|
|
13
13
|
e !== void 0 && d(e);
|
|
14
14
|
}, [e]);
|
|
15
|
-
let f =
|
|
16
|
-
d(e),
|
|
17
|
-
}, [
|
|
18
|
-
return /* @__PURE__ */
|
|
15
|
+
let f = s.useCallback((e) => {
|
|
16
|
+
d(e), r?.(e);
|
|
17
|
+
}, [r]), h = i ?? "horizontal";
|
|
18
|
+
return /* @__PURE__ */ c(p.Provider, {
|
|
19
19
|
value: u,
|
|
20
|
-
children: /* @__PURE__ */
|
|
20
|
+
children: /* @__PURE__ */ c(m.Provider, {
|
|
21
21
|
value: h,
|
|
22
|
-
children: /* @__PURE__ */
|
|
23
|
-
ref:
|
|
22
|
+
children: /* @__PURE__ */ c(a, {
|
|
23
|
+
ref: l,
|
|
24
24
|
value: e,
|
|
25
25
|
defaultValue: e === void 0 ? t : void 0,
|
|
26
26
|
onValueChange: f,
|
|
27
27
|
orientation: i,
|
|
28
|
-
...
|
|
29
|
-
className:
|
|
28
|
+
...o,
|
|
29
|
+
className: n(h === "vertical" && "flex flex-row gap-ds-05", o.className)
|
|
30
30
|
})
|
|
31
31
|
})
|
|
32
32
|
});
|
|
33
33
|
});
|
|
34
34
|
h.displayName = "Tabs";
|
|
35
|
-
var g =
|
|
35
|
+
var g = s.createContext({
|
|
36
36
|
variant: "line",
|
|
37
37
|
size: "md",
|
|
38
38
|
color: "accent",
|
|
39
39
|
layoutId: "tab-indicator"
|
|
40
|
-
}), _ =
|
|
40
|
+
}), _ = f("inline-flex items-center", {
|
|
41
41
|
variants: {
|
|
42
42
|
variant: {
|
|
43
43
|
line: "border-b border-surface-border-strong w-full gap-0",
|
|
44
|
-
contained: "bg-
|
|
44
|
+
contained: "bg-segment-track p-ds-02 rounded-surface gap-ds-02"
|
|
45
45
|
},
|
|
46
46
|
size: {
|
|
47
47
|
sm: "h-8",
|
|
@@ -67,7 +67,7 @@ var g = c.createContext({
|
|
|
67
67
|
size: "md",
|
|
68
68
|
orientation: "horizontal"
|
|
69
69
|
}
|
|
70
|
-
}), v =
|
|
70
|
+
}), v = f("relative inline-flex items-center justify-center gap-ds-02 whitespace-nowrap font-sans font-medium transition-colors duration-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-action-disabled", {
|
|
71
71
|
variants: {
|
|
72
72
|
variant: {
|
|
73
73
|
line: ["-mb-px", "text-surface-fg-muted hover:text-surface-fg"],
|
|
@@ -87,8 +87,8 @@ var g = c.createContext({
|
|
|
87
87
|
variant: "line",
|
|
88
88
|
size: "md"
|
|
89
89
|
}
|
|
90
|
-
}), y =
|
|
91
|
-
let
|
|
90
|
+
}), y = s.forwardRef(({ className: e, variant: t, size: r, color: a, ...o }, l) => {
|
|
91
|
+
let d = s.useContext(m), f = t ?? "line", p = r ?? "md", h = a ?? "accent", v = s.useId(), y = s.useMemo(() => ({
|
|
92
92
|
variant: f,
|
|
93
93
|
size: p,
|
|
94
94
|
color: h,
|
|
@@ -99,68 +99,68 @@ var g = c.createContext({
|
|
|
99
99
|
h,
|
|
100
100
|
v
|
|
101
101
|
]);
|
|
102
|
-
return /* @__PURE__ */
|
|
102
|
+
return /* @__PURE__ */ c(g.Provider, {
|
|
103
103
|
value: y,
|
|
104
|
-
children: /* @__PURE__ */
|
|
105
|
-
ref:
|
|
106
|
-
className:
|
|
104
|
+
children: /* @__PURE__ */ c(u, { children: /* @__PURE__ */ c(i, {
|
|
105
|
+
ref: l,
|
|
106
|
+
className: n(_({
|
|
107
107
|
variant: f,
|
|
108
108
|
size: p,
|
|
109
|
-
orientation:
|
|
109
|
+
orientation: d
|
|
110
110
|
}), e),
|
|
111
111
|
...o
|
|
112
112
|
}) })
|
|
113
113
|
});
|
|
114
114
|
});
|
|
115
|
-
y.displayName =
|
|
115
|
+
y.displayName = i.displayName;
|
|
116
116
|
var b = {
|
|
117
117
|
accent: "data-[state=active]:text-accent-11",
|
|
118
118
|
neutral: "data-[state=active]:text-surface-fg"
|
|
119
119
|
}, x = {
|
|
120
120
|
accent: "bg-accent-9",
|
|
121
121
|
neutral: "bg-surface-fg"
|
|
122
|
-
}, S =
|
|
123
|
-
let
|
|
124
|
-
return /* @__PURE__ */
|
|
125
|
-
ref:
|
|
126
|
-
className:
|
|
122
|
+
}, S = s.forwardRef(({ className: t, variant: r, children: i, ...a }, u) => {
|
|
123
|
+
let f = s.useContext(g), h = s.useContext(p), _ = s.useContext(m), y = r ?? f.variant, S = f.size, C = f.color, w = a.value === h, T = _ === "vertical";
|
|
124
|
+
return /* @__PURE__ */ l(o, {
|
|
125
|
+
ref: u,
|
|
126
|
+
className: n(v({
|
|
127
127
|
variant: y,
|
|
128
128
|
size: S
|
|
129
129
|
}), y === "line" && b[C], T && "w-full justify-start", y === "line" && T && "-ml-px -mb-0", t),
|
|
130
130
|
...a,
|
|
131
131
|
children: [
|
|
132
|
-
y === "contained" && w && /* @__PURE__ */
|
|
133
|
-
layoutId: `${
|
|
132
|
+
y === "contained" && w && /* @__PURE__ */ c(d.span, {
|
|
133
|
+
layoutId: `${f.layoutId}-contained`,
|
|
134
134
|
className: "absolute inset-0 rounded-control bg-surface-overlay shadow-raised",
|
|
135
135
|
transition: e.smooth
|
|
136
136
|
}),
|
|
137
|
-
/* @__PURE__ */
|
|
137
|
+
/* @__PURE__ */ c("span", {
|
|
138
138
|
className: "relative z-[1] inline-flex min-w-0 items-center gap-ds-02",
|
|
139
|
-
children: /* @__PURE__ */
|
|
139
|
+
children: /* @__PURE__ */ c("span", {
|
|
140
140
|
className: "truncate",
|
|
141
141
|
children: i
|
|
142
142
|
})
|
|
143
143
|
}),
|
|
144
|
-
y === "line" && w && /* @__PURE__ */
|
|
145
|
-
layoutId: `${
|
|
146
|
-
className:
|
|
144
|
+
y === "line" && w && /* @__PURE__ */ c(d.span, {
|
|
145
|
+
layoutId: `${f.layoutId}-line`,
|
|
146
|
+
className: n("absolute", T ? "left-0 top-0 bottom-0 w-0.5" : "bottom-0 left-0 right-0 h-0.5", x[C]),
|
|
147
147
|
transition: e.smooth
|
|
148
148
|
})
|
|
149
149
|
]
|
|
150
150
|
});
|
|
151
151
|
});
|
|
152
|
-
S.displayName =
|
|
153
|
-
var C =
|
|
152
|
+
S.displayName = o.displayName;
|
|
153
|
+
var C = s.forwardRef(({ className: e, children: i, ...a }, o) => /* @__PURE__ */ c(r, {
|
|
154
154
|
ref: o,
|
|
155
|
-
className:
|
|
155
|
+
className: n("ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2", s.useContext(m) === "vertical" ? "mt-0 flex-1 min-w-0" : "mt-ds-05", e),
|
|
156
156
|
...a,
|
|
157
|
-
children: /* @__PURE__ */
|
|
157
|
+
children: /* @__PURE__ */ c(d.div, {
|
|
158
158
|
initial: { opacity: 0 },
|
|
159
159
|
animate: { opacity: 1 },
|
|
160
160
|
transition: t.fade,
|
|
161
|
-
children:
|
|
161
|
+
children: i
|
|
162
162
|
})
|
|
163
163
|
}));
|
|
164
|
-
C.displayName =
|
|
164
|
+
C.displayName = r.displayName;
|
|
165
165
|
//#endregion
|
|
166
166
|
export { h as Tabs, C as TabsContent, y as TabsList, S as TabsTrigger };
|