gridsmith-ui 0.29.1 → 0.30.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +5 -1
  3. package/dist/DropdownMenu-DqT7ZQ0D.d.ts +39 -0
  4. package/dist/charts.js +4 -2
  5. package/dist/charts.js.map +1 -1
  6. package/dist/chat.js +4 -4
  7. package/dist/{chunk-OYNUU346.js → chunk-7QLXYRPD.js} +3 -12
  8. package/dist/chunk-7QLXYRPD.js.map +1 -0
  9. package/dist/chunk-F2BNJSON.js +1348 -0
  10. package/dist/chunk-F2BNJSON.js.map +1 -0
  11. package/dist/{chunk-37WOC7F7.js → chunk-GQVBZ4GW.js} +29 -8
  12. package/dist/chunk-GQVBZ4GW.js.map +1 -0
  13. package/dist/{chunk-MFAWJSMJ.js → chunk-INGF2CPW.js} +3 -22
  14. package/dist/chunk-INGF2CPW.js.map +1 -0
  15. package/dist/{chunk-I3MTQ3CS.js → chunk-KTPGMJUK.js} +5 -5
  16. package/dist/{chunk-I3MTQ3CS.js.map → chunk-KTPGMJUK.js.map} +1 -1
  17. package/dist/{chunk-MKFWHWBK.js → chunk-L6HWACK7.js} +7 -7
  18. package/dist/chunk-L6HWACK7.js.map +1 -0
  19. package/dist/chunk-LJYSVZMI.js +25 -0
  20. package/dist/chunk-LJYSVZMI.js.map +1 -0
  21. package/dist/{chunk-FUKSLQ77.js → chunk-NI5542ZM.js} +39 -35
  22. package/dist/chunk-NI5542ZM.js.map +1 -0
  23. package/dist/chunk-NKUCS67T.js +13 -0
  24. package/dist/chunk-NKUCS67T.js.map +1 -0
  25. package/dist/{chunk-RECCCA7Z.js → chunk-NLXQ323J.js} +4 -4
  26. package/dist/{chunk-RECCCA7Z.js.map → chunk-NLXQ323J.js.map} +1 -1
  27. package/dist/{chunk-ZI3OQDET.js → chunk-OIOII5L6.js} +93 -4
  28. package/dist/chunk-OIOII5L6.js.map +1 -0
  29. package/dist/{chunk-RJEUQ7RL.js → chunk-Q36JVJX4.js} +46 -89
  30. package/dist/chunk-Q36JVJX4.js.map +1 -0
  31. package/dist/{chunk-FD4YED7O.js → chunk-RHWTMTU5.js} +3 -3
  32. package/dist/{chunk-FD4YED7O.js.map → chunk-RHWTMTU5.js.map} +1 -1
  33. package/dist/chunk-TYNDXMBV.js +105 -0
  34. package/dist/chunk-TYNDXMBV.js.map +1 -0
  35. package/dist/{chunk-4TUJXDRU.js → chunk-UNS4TGGK.js} +20 -11
  36. package/dist/chunk-UNS4TGGK.js.map +1 -0
  37. package/dist/chunk-Z4MCD73B.js +316 -0
  38. package/dist/chunk-Z4MCD73B.js.map +1 -0
  39. package/dist/data-grid.d.ts +25 -6
  40. package/dist/data-grid.js +177 -55
  41. package/dist/data-grid.js.map +1 -1
  42. package/dist/editor-mentions.js +1 -1
  43. package/dist/editor.js +3 -3
  44. package/dist/{feather-FNBQ74RR.js → feather-XFFEFLTV.js} +5 -3
  45. package/dist/feather-XFFEFLTV.js.map +1 -0
  46. package/dist/{heroicons-DODCSYWD.js → heroicons-L6DOHBEA.js} +5 -3
  47. package/dist/heroicons-L6DOHBEA.js.map +1 -0
  48. package/dist/icons.d.ts +1 -1
  49. package/dist/icons.js +1 -1
  50. package/dist/index.d.ts +138 -39
  51. package/dist/index.js +660 -1568
  52. package/dist/index.js.map +1 -1
  53. package/dist/kanban.d.ts +26 -8
  54. package/dist/kanban.js +200 -76
  55. package/dist/kanban.js.map +1 -1
  56. package/dist/{lucide-ZWE3EPGP.js → lucide-JOORDTO6.js} +5 -3
  57. package/dist/lucide-JOORDTO6.js.map +1 -0
  58. package/dist/{phosphor-DIHPA4AJ.js → phosphor-TKGDYNGR.js} +5 -3
  59. package/dist/phosphor-TKGDYNGR.js.map +1 -0
  60. package/dist/resizable.js +10 -9
  61. package/dist/resizable.js.map +1 -1
  62. package/dist/roots.css +12 -8
  63. package/dist/{table-utils-CxnpFHRF.d.ts → table-utils-BXkP-2Hx.d.ts} +3 -1
  64. package/dist/{tabler-D3VYTSGI.js → tabler-YGETLYRU.js} +5 -3
  65. package/dist/tabler-YGETLYRU.js.map +1 -0
  66. package/dist/tokens.css +14 -0
  67. package/dist/utils.js.map +1 -1
  68. package/dist/virtualized.js +8 -7
  69. package/dist/virtualized.js.map +1 -1
  70. package/dist-static/tokens.css +14 -0
  71. package/package.json +1 -1
  72. package/dist/chunk-37WOC7F7.js.map +0 -1
  73. package/dist/chunk-4TUJXDRU.js.map +0 -1
  74. package/dist/chunk-FUKSLQ77.js.map +0 -1
  75. package/dist/chunk-MFAWJSMJ.js.map +0 -1
  76. package/dist/chunk-MKFWHWBK.js.map +0 -1
  77. package/dist/chunk-OYNUU346.js.map +0 -1
  78. package/dist/chunk-QM4SJKWV.js +0 -51
  79. package/dist/chunk-QM4SJKWV.js.map +0 -1
  80. package/dist/chunk-QNHVPE2B.js +0 -117
  81. package/dist/chunk-QNHVPE2B.js.map +0 -1
  82. package/dist/chunk-RJEUQ7RL.js.map +0 -1
  83. package/dist/chunk-ZI3OQDET.js.map +0 -1
  84. package/dist/feather-FNBQ74RR.js.map +0 -1
  85. package/dist/heroicons-DODCSYWD.js.map +0 -1
  86. package/dist/lucide-ZWE3EPGP.js.map +0 -1
  87. package/dist/phosphor-DIHPA4AJ.js.map +0 -1
  88. package/dist/tabler-D3VYTSGI.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { Badge } from './chunk-RECCCA7Z.js';
3
- import { slotIcon, Icon } from './chunk-MKFWHWBK.js';
2
+ import { Badge } from './chunk-NLXQ323J.js';
3
+ import { slotIcon, Icon } from './chunk-L6HWACK7.js';
4
4
  import { cn, FOCUS_RING } from './chunk-JTU7OBYJ.js';
5
5
  import { forwardRef, useState, useCallback } from 'react';
6
6
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
@@ -54,7 +54,7 @@ function CodeBlock({
54
54
  ),
55
55
  "aria-label": copied ? "Copied" : "Copy code",
56
56
  children: [
57
- /* @__PURE__ */ jsx(Icon, { name: copied ? "check" : "folder", size: 14 }),
57
+ /* @__PURE__ */ jsx(Icon, { name: copied ? "check" : "copy", size: 14 }),
58
58
  copied ? "Copied" : "Copy"
59
59
  ]
60
60
  }
@@ -159,5 +159,5 @@ function Divider({
159
159
  }
160
160
 
161
161
  export { CodeBlock, Divider, Link };
162
- //# sourceMappingURL=chunk-I3MTQ3CS.js.map
163
- //# sourceMappingURL=chunk-I3MTQ3CS.js.map
162
+ //# sourceMappingURL=chunk-KTPGMJUK.js.map
163
+ //# sourceMappingURL=chunk-KTPGMJUK.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../showcase/src/components/CodeBlock.tsx","../../../showcase/src/components/Link.tsx","../../../showcase/src/components/Divider.tsx"],"names":["Link","jsxs","jsx"],"mappings":"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","file":"chunk-I3MTQ3CS.js","sourcesContent":["import { useState, useCallback } from \"react\";\nimport { cn, FOCUS_RING } from \"../lib/utils\";\nimport { Icon } from \"../lib/icons\";\nimport { Badge } from \"./Badge\";\n\ninterface CodeBlockProps {\n code: string;\n language?: string;\n showLineNumbers?: boolean;\n /** Wrap long lines instead of scrolling sideways. Default: on for a single-line snippet (a tracking tag, a command), off for multi-line code. */\n wrap?: boolean;\n className?: string;\n}\n\nexport function CodeBlock({\n code,\n language,\n showLineNumbers = false,\n wrap,\n className,\n}: CodeBlockProps) {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = useCallback(async () => {\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch {\n /* clipboard not available */\n }\n }, [code]);\n\n const lines = code.split(\"\\n\");\n const wraps = wrap ?? lines.length === 1;\n const gutterWidth = showLineNumbers ? `${String(lines.length).length + 1}ch` : undefined;\n\n return (\n <div\n className={cn(\n \"relative group\",\n \"bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]\",\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"rounded-[var(--ds-radius-md)]\",\n \"shadow-[var(--ds-shadow-sm)]\",\n \"overflow-hidden\",\n className,\n )}\n >\n <div className=\"flex items-center justify-between px-ds-4 py-ds-2 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] bg-[var(--ds-bg-muted)]\">\n <div className=\"flex items-center gap-ds-2\">\n {language && <Badge variant=\"outline\" size=\"sm\">{language}</Badge>}\n </div>\n <button\n type=\"button\"\n onClick={handleCopy}\n className={cn(\n \"inline-flex items-center gap-ds-1.5 px-ds-2 py-ds-1 text-xs font-medium cursor-pointer\",\n \"text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]\",\n \"rounded-[var(--ds-radius-sm)]\",\n \"hover:bg-[var(--ds-bg-surface)]\",\n \"transition-colors\",\n FOCUS_RING,\n )}\n aria-label={copied ? \"Copied\" : \"Copy code\"}\n >\n <Icon name={copied ? \"check\" : \"folder\"} size={14} />\n {copied ? \"Copied\" : \"Copy\"}\n </button>\n </div>\n\n <div className={wraps ? undefined : \"overflow-x-auto\"} data-wrap={wraps ? \"true\" : undefined}>\n <pre className={cn(\"p-ds-4 text-sm leading-relaxed font-[family-name:var(--ds-font-mono)]\", wraps && \"whitespace-pre-wrap break-words\")}>\n <code>\n {lines.map((line, i) => (\n // with line numbers each line is a two-column row, so a wrapped line continues under its text, not under the gutter\n <span key={i} className={showLineNumbers ? \"grid grid-cols-[max-content_1fr]\" : \"block\"}>\n {showLineNumbers && (\n <span\n className=\"text-right text-[var(--ds-text-muted)] select-none mr-ds-4 opacity-50\"\n style={{ width: gutterWidth }}\n >\n {i + 1}\n </span>\n )}\n <span className=\"min-w-0\">{line || \" \"}</span>\n </span>\n ))}\n </code>\n </pre>\n </div>\n </div>\n );\n}\n","import { forwardRef, type AnchorHTMLAttributes, type ElementType, type ReactNode } from \"react\";\nimport { cn, FOCUS_RING } from \"../lib/utils\";\nimport { slotIcon, type IconSlot } from \"../lib/icons\";\n\ntype LinkUnderline = \"hover\" | \"always\" | \"none\";\ntype LinkTone = \"accent\" | \"muted\" | \"inherit\";\n\nexport interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string;\n children: ReactNode;\n /** When to show the underline. Default \"hover\". */\n underline?: LinkUnderline;\n /** Colour: accent (default), muted for secondary links, inherit to match surrounding text. */\n tone?: LinkTone;\n /** Opens in a new tab with rel=\"noopener noreferrer\". Default false. */\n external?: boolean;\n /**\n * Render through a router link component instead of a plain anchor\n * (e.g. `component={RouterLink}` from react-router or next/link). The\n * component receives the same props; `href` is also passed as `to` for\n * react-router compatibility. Any element type: a router's Link requires\n * its own props (react-router's `to`), which a narrower type refused under\n * strict TypeScript (Sprint 25.16 A19).\n */\n component?: ElementType;\n /** An icon name (\"info\"), drawn as the theme's icon at 14 px, or an element. Not text: use children. */\n iconLeft?: IconSlot;\n /** As iconLeft, after the label. */\n iconRight?: IconSlot;\n}\n\nconst toneStyles: Record<LinkTone, string> = {\n accent: \"text-[var(--ds-accent-ink)] hover:text-[var(--ds-accent-primary-hover)] active:text-[var(--ds-accent-primary-active)]\",\n muted: \"text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)]\",\n inherit: \"text-inherit hover:text-[var(--ds-accent-ink)]\",\n};\n\nconst underlineStyles: Record<LinkUnderline, string> = {\n hover: \"no-underline hover:underline\",\n always: \"underline underline-offset-2\",\n none: \"no-underline\",\n};\n\n/**\n * Navigation link. Renders a real <a> (or a router link via `component`) so\n * middle-click, open-in-new-tab, right-click \"copy link\" and screen readers\n * all behave correctly. Visually identical to Button variant=\"link\".\n *\n * Use Link for NAVIGATION (to a page or URL). Use Button variant=\"link\" for an\n * ACTION that should look like a link (show more, resend code). Never use a\n * ghost Button for inline text — its padding breaks the line.\n */\nexport const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(\n { href, children, underline = \"hover\", tone = \"accent\", external = false, component: Component, iconLeft, iconRight, className, target, rel, ...props },\n ref,\n) {\n const classes = cn(\n \"inline-flex items-center gap-ds-1 font-medium cursor-pointer\",\n \"transition-colors duration-[var(--ds-transition-speed)]\",\n FOCUS_RING,\n \"rounded-[var(--ds-radius-sm)]\",\n toneStyles[tone],\n underlineStyles[underline],\n className,\n );\n const resolvedTarget = external ? \"_blank\" : target;\n const resolvedRel = external ? cn(\"noopener noreferrer\", rel) : rel;\n const content = (\n <>\n {slotIcon(iconLeft, 14, \"Link's iconLeft\")}\n {children}\n {slotIcon(iconRight, 14, \"Link's iconRight\")}\n </>\n );\n\n if (Component) {\n return (\n <Component ref={ref} href={href} to={href} className={classes} target={resolvedTarget} rel={resolvedRel} {...props}>\n {content}\n </Component>\n );\n }\n\n return (\n <a ref={ref} href={href} className={classes} target={resolvedTarget} rel={resolvedRel} {...props}>\n {content}\n </a>\n );\n});\n","import { cn } from \"../lib/utils\";\nimport type { ReactNode } from \"react\";\n\ntype Orientation = \"horizontal\" | \"vertical\";\n\ninterface DividerProps {\n orientation?: Orientation;\n label?: ReactNode;\n className?: string;\n}\n\nexport function Divider({\n orientation = \"horizontal\",\n label,\n className,\n}: DividerProps) {\n if (orientation === \"vertical\") {\n return (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n className={cn(\n \"inline-block self-stretch min-h-[1em]\",\n \"w-[var(--ds-divider-width)]\",\n \"bg-[var(--ds-border-default)]\",\n className,\n )}\n />\n );\n }\n\n if (label) {\n return (\n <div\n role=\"separator\"\n className={cn(\"flex items-center gap-ds-3\", className)}\n >\n <span className=\"flex-1 h-[var(--ds-divider-width)] bg-[var(--ds-border-default)]\" />\n <span className=\"text-xs font-medium text-[var(--ds-text-muted)] shrink-0\">\n {label}\n </span>\n <span className=\"flex-1 h-[var(--ds-divider-width)] bg-[var(--ds-border-default)]\" />\n </div>\n );\n }\n\n return (\n <hr\n role=\"separator\"\n className={cn(\n \"border-none w-full\",\n \"h-[var(--ds-divider-width)]\",\n \"bg-[var(--ds-border-default)]\",\n className,\n )}\n />\n );\n}\n"]}
1
+ {"version":3,"sources":["../../../showcase/src/components/CodeBlock.tsx","../../../showcase/src/components/Link.tsx","../../../showcase/src/components/Divider.tsx"],"names":["Link","jsxs","jsx"],"mappings":"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","file":"chunk-KTPGMJUK.js","sourcesContent":["import { useState, useCallback } from \"react\";\nimport { cn, FOCUS_RING } from \"../lib/utils\";\nimport { Icon } from \"../lib/icons\";\nimport { Badge } from \"./Badge\";\n\ninterface CodeBlockProps {\n code: string;\n language?: string;\n showLineNumbers?: boolean;\n /** Wrap long lines instead of scrolling sideways. Default: on for a single-line snippet (a tracking tag, a command), off for multi-line code. */\n wrap?: boolean;\n className?: string;\n}\n\nexport function CodeBlock({\n code,\n language,\n showLineNumbers = false,\n wrap,\n className,\n}: CodeBlockProps) {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = useCallback(async () => {\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch {\n /* clipboard not available */\n }\n }, [code]);\n\n const lines = code.split(\"\\n\");\n const wraps = wrap ?? lines.length === 1;\n const gutterWidth = showLineNumbers ? `${String(lines.length).length + 1}ch` : undefined;\n\n return (\n <div\n className={cn(\n \"relative group\",\n \"bg-[var(--ds-bg-surface)] text-[var(--ds-text-primary)]\",\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"rounded-[var(--ds-radius-md)]\",\n \"shadow-[var(--ds-shadow-sm)]\",\n \"overflow-hidden\",\n className,\n )}\n >\n <div className=\"flex items-center justify-between px-ds-4 py-ds-2 border-b-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] bg-[var(--ds-bg-muted)]\">\n <div className=\"flex items-center gap-ds-2\">\n {language && <Badge variant=\"outline\" size=\"sm\">{language}</Badge>}\n </div>\n <button\n type=\"button\"\n onClick={handleCopy}\n className={cn(\n \"inline-flex items-center gap-ds-1.5 px-ds-2 py-ds-1 text-xs font-medium cursor-pointer\",\n \"text-[var(--ds-text-muted)] hover:text-[var(--ds-text-primary)]\",\n \"rounded-[var(--ds-radius-sm)]\",\n \"hover:bg-[var(--ds-bg-surface)]\",\n \"transition-colors\",\n FOCUS_RING,\n )}\n aria-label={copied ? \"Copied\" : \"Copy code\"}\n >\n {/* Sprint 25.8.7 item 12: the copy icon, where a folder stood */}\n <Icon name={copied ? \"check\" : \"copy\"} size={14} />\n {copied ? \"Copied\" : \"Copy\"}\n </button>\n </div>\n\n <div className={wraps ? undefined : \"overflow-x-auto\"} data-wrap={wraps ? \"true\" : undefined}>\n <pre className={cn(\"p-ds-4 text-sm leading-relaxed font-[family-name:var(--ds-font-mono)]\", wraps && \"whitespace-pre-wrap break-words\")}>\n <code>\n {lines.map((line, i) => (\n // with line numbers each line is a two-column row, so a wrapped line continues under its text, not under the gutter\n <span key={i} className={showLineNumbers ? \"grid grid-cols-[max-content_1fr]\" : \"block\"}>\n {showLineNumbers && (\n <span\n className=\"text-right text-[var(--ds-text-muted)] select-none mr-ds-4 opacity-50\"\n style={{ width: gutterWidth }}\n >\n {i + 1}\n </span>\n )}\n <span className=\"min-w-0\">{line || \" \"}</span>\n </span>\n ))}\n </code>\n </pre>\n </div>\n </div>\n );\n}\n","import { forwardRef, type AnchorHTMLAttributes, type ElementType, type ReactNode } from \"react\";\nimport { cn, FOCUS_RING } from \"../lib/utils\";\nimport { slotIcon, type IconSlot } from \"../lib/icons\";\n\ntype LinkUnderline = \"hover\" | \"always\" | \"none\";\ntype LinkTone = \"accent\" | \"muted\" | \"inherit\";\n\nexport interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string;\n children: ReactNode;\n /** When to show the underline. Default \"hover\". */\n underline?: LinkUnderline;\n /** Colour: accent (default), muted for secondary links, inherit to match surrounding text. */\n tone?: LinkTone;\n /** Opens in a new tab with rel=\"noopener noreferrer\". Default false. */\n external?: boolean;\n /**\n * Render through a router link component instead of a plain anchor\n * (e.g. `component={RouterLink}` from react-router or next/link). The\n * component receives the same props; `href` is also passed as `to` for\n * react-router compatibility. Any element type: a router's Link requires\n * its own props (react-router's `to`), which a narrower type refused under\n * strict TypeScript (Sprint 25.16 A19).\n */\n component?: ElementType;\n /** An icon name (\"info\"), drawn as the theme's icon at 14 px, or an element. Not text: use children. */\n iconLeft?: IconSlot;\n /** As iconLeft, after the label. */\n iconRight?: IconSlot;\n}\n\nconst toneStyles: Record<LinkTone, string> = {\n accent: \"text-[var(--ds-accent-ink)] hover:text-[var(--ds-accent-primary-hover)] active:text-[var(--ds-accent-primary-active)]\",\n muted: \"text-[var(--ds-text-secondary)] hover:text-[var(--ds-text-primary)]\",\n inherit: \"text-inherit hover:text-[var(--ds-accent-ink)]\",\n};\n\nconst underlineStyles: Record<LinkUnderline, string> = {\n hover: \"no-underline hover:underline\",\n always: \"underline underline-offset-2\",\n none: \"no-underline\",\n};\n\n/**\n * Navigation link. Renders a real <a> (or a router link via `component`) so\n * middle-click, open-in-new-tab, right-click \"copy link\" and screen readers\n * all behave correctly. Visually identical to Button variant=\"link\".\n *\n * Use Link for NAVIGATION (to a page or URL). Use Button variant=\"link\" for an\n * ACTION that should look like a link (show more, resend code). Never use a\n * ghost Button for inline text — its padding breaks the line.\n */\nexport const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(\n { href, children, underline = \"hover\", tone = \"accent\", external = false, component: Component, iconLeft, iconRight, className, target, rel, ...props },\n ref,\n) {\n const classes = cn(\n \"inline-flex items-center gap-ds-1 font-medium cursor-pointer\",\n \"transition-colors duration-[var(--ds-transition-speed)]\",\n FOCUS_RING,\n \"rounded-[var(--ds-radius-sm)]\",\n toneStyles[tone],\n underlineStyles[underline],\n className,\n );\n const resolvedTarget = external ? \"_blank\" : target;\n const resolvedRel = external ? cn(\"noopener noreferrer\", rel) : rel;\n const content = (\n <>\n {slotIcon(iconLeft, 14, \"Link's iconLeft\")}\n {children}\n {slotIcon(iconRight, 14, \"Link's iconRight\")}\n </>\n );\n\n if (Component) {\n return (\n <Component ref={ref} href={href} to={href} className={classes} target={resolvedTarget} rel={resolvedRel} {...props}>\n {content}\n </Component>\n );\n }\n\n return (\n <a ref={ref} href={href} className={classes} target={resolvedTarget} rel={resolvedRel} {...props}>\n {content}\n </a>\n );\n});\n","import { cn } from \"../lib/utils\";\nimport type { ReactNode } from \"react\";\n\ntype Orientation = \"horizontal\" | \"vertical\";\n\ninterface DividerProps {\n orientation?: Orientation;\n label?: ReactNode;\n className?: string;\n}\n\nexport function Divider({\n orientation = \"horizontal\",\n label,\n className,\n}: DividerProps) {\n if (orientation === \"vertical\") {\n return (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n className={cn(\n \"inline-block self-stretch min-h-[1em]\",\n \"w-[var(--ds-divider-width)]\",\n \"bg-[var(--ds-border-default)]\",\n className,\n )}\n />\n );\n }\n\n if (label) {\n return (\n <div\n role=\"separator\"\n className={cn(\"flex items-center gap-ds-3\", className)}\n >\n <span className=\"flex-1 h-[var(--ds-divider-width)] bg-[var(--ds-border-default)]\" />\n <span className=\"text-xs font-medium text-[var(--ds-text-muted)] shrink-0\">\n {label}\n </span>\n <span className=\"flex-1 h-[var(--ds-divider-width)] bg-[var(--ds-border-default)]\" />\n </div>\n );\n }\n\n return (\n <hr\n role=\"separator\"\n className={cn(\n \"border-none w-full\",\n \"h-[var(--ds-divider-width)]\",\n \"bg-[var(--ds-border-default)]\",\n className,\n )}\n />\n );\n}\n"]}
@@ -88,19 +88,19 @@ async function loadPack(pack) {
88
88
  try {
89
89
  switch (pack) {
90
90
  case "lucide":
91
- mod = await import('./lucide-ZWE3EPGP.js');
91
+ mod = await import('./lucide-JOORDTO6.js');
92
92
  break;
93
93
  case "phosphor":
94
- mod = await import('./phosphor-DIHPA4AJ.js');
94
+ mod = await import('./phosphor-TKGDYNGR.js');
95
95
  break;
96
96
  case "heroicons":
97
- mod = await import('./heroicons-DODCSYWD.js');
97
+ mod = await import('./heroicons-L6DOHBEA.js');
98
98
  break;
99
99
  case "tabler":
100
- mod = await import('./tabler-D3VYTSGI.js');
100
+ mod = await import('./tabler-YGETLYRU.js');
101
101
  break;
102
102
  case "feather":
103
- mod = await import('./feather-FNBQ74RR.js');
103
+ mod = await import('./feather-XFFEFLTV.js');
104
104
  break;
105
105
  }
106
106
  curated = await mod.loadCurated(load);
@@ -205,5 +205,5 @@ function NotAnIcon({ text, from }) {
205
205
  }
206
206
 
207
207
  export { Icon, IconProvider, preloadPack, registerIconPack, slotDraws, slotIcon, warnOnce };
208
- //# sourceMappingURL=chunk-MKFWHWBK.js.map
209
- //# sourceMappingURL=chunk-MKFWHWBK.js.map
208
+ //# sourceMappingURL=chunk-L6HWACK7.js.map
209
+ //# sourceMappingURL=chunk-L6HWACK7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/lib/dev-warn.ts","../../../showcase/src/lib/icon-pack-imports.ts","../../../showcase/src/lib/icons.tsx"],"names":[],"mappings":"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","file":"chunk-L6HWACK7.js","sourcesContent":["// Bundlers set process.env.NODE_ENV. Where none did (a page with no build step), reading it throws and warnings stay off.\ndeclare const process: { env: { NODE_ENV?: string } };\nconst warned = new Set<string>();\n\n/** A mistake in a component's props, told to the developer once a page, in a development build. */\nexport function warnOnce(component: string, message: string): void {\n let dev = false;\n try {\n dev = process.env.NODE_ENV !== \"production\";\n } catch {\n dev = false;\n }\n const text = `${component}: ${message}`;\n if (!dev || warned.has(text)) return;\n warned.add(text);\n console.warn(text);\n}\n","/**\n * The import each icon pack takes, as an app writes it when it registers its theme's pack (Sprint 25.21 P1:\n * `registerIconPack('feather', () => import('react-icons/fi'))`). One table for the library's warning, the shells\n * `get_app_shell` writes and the engine's fix. Lucide is the library's own: every app installs it, as the fallback.\n * No React here, so the platform reads it as it reads the other showcase modules.\n */\nexport const ICON_PACK_IMPORTS = {\n feather: \"react-icons/fi\",\n heroicons: \"@heroicons/react/24/outline\",\n phosphor: \"@phosphor-icons/react\",\n tabler: \"@tabler/icons-react\",\n} as const;\n\n/** A pack an app registers: every pack but Lucide. */\nexport type RegisteredIconPack = keyof typeof ICON_PACK_IMPORTS;\n\n/** The two lines that register a pack, at the top of an app's shell, before any preloadPack. */\nexport function iconPackRegistration(pack: RegisteredIconPack): { importLine: string; call: string } {\n return {\n importLine: \"import { registerIconPack } from 'gridsmith-ui/icons'\",\n call: `registerIconPack('${pack}', () => import('${ICON_PACK_IMPORTS[pack]}'))`,\n };\n}\n","import { createContext, useContext, useEffect, useSyncExternalStore, type ReactElement, type ReactNode, type ComponentType } from \"react\";\nimport { warnOnce } from \"./dev-warn\";\nimport { iconPackRegistration, type RegisteredIconPack } from \"./icon-pack-imports\";\nimport type { PackLoad } from \"./icon-packs/types\";\n\n/* ── Types ────────────────────────────────────────────────────── */\n\n/**\n * Runtime list of curated icon names. The CuratedIconName type derives from\n * this array, so adding a name here extends autocomplete automatically.\n * Every name must have an alias (or auto-kebab match) in ALL five packs —\n * icon-completeness.test.tsx enforces this.\n */\nexport const CURATED_ICON_NAMES = [\n \"home\", \"folder\", \"chart\", \"settings\",\n \"chevron-right\", \"close\", \"plus\", \"search\",\n \"bell\", \"user\", \"check\", \"warning\", \"info\", \"error\",\n \"clock\", \"calendar\",\n \"bold\", \"italic\", \"underline\", \"strikethrough\",\n \"align-left\", \"align-center\", \"align-right\",\n \"list\", \"list-ordered\", \"list-checks\",\n \"link\", \"image\", \"table\", \"code\", \"quote\", \"minus\",\n \"superscript\", \"subscript\", \"highlight\", \"type\",\n \"chevron-up\", \"chevron-down\", \"chevron-left\", \"trash\",\n \"undo\", \"redo\",\n \"arrow-left\", \"grip-vertical\",\n \"mic\", \"paperclip\", \"send\",\n \"log-out\", \"edit\", \"menu\", \"more-horizontal\",\n \"star\", \"bookmark\", \"download\", \"upload\", \"sparkles\",\n \"file\", \"file-text\", \"eye\", \"eye-off\", \"copy\", \"external-link\", \"filter\", \"lock\",\n \"map-pin\", \"play\", \"pause\",\n] as const;\n\ntype CuratedIconName = (typeof CURATED_ICON_NAMES)[number];\n\n/**\n * Curated names provide autocomplete and work identically across all 5 packs.\n * Any kebab-case string is also accepted — it resolves directly against the\n * active pack's native exports (e.g. \"clipboard-copy\" → Lucide's ClipboardCopy).\n */\nexport type IconName = CuratedIconName | (string & {});\n\nexport type IconPack = \"lucide\" | \"phosphor\" | \"heroicons\" | \"tabler\" | \"feather\";\n\ntype IconComponent = ComponentType<Record<string, unknown>>;\ntype IconMap = Record<string, IconComponent>;\n\n/* ── External store ──────────────────────────────────────────── */\n\ntype Subscriber = () => void;\nconst subscribers = new Set<Subscriber>();\nfunction notifySubscribers() {\n subscribers.forEach((fn) => fn());\n}\nfunction subscribe(cb: Subscriber): () => void {\n subscribers.add(cb);\n return () => { subscribers.delete(cb); };\n}\n\nconst curatedCache = new Map<IconPack, IconMap>();\nconst extendedCache = new Map<IconPack, IconMap>();\n\nfunction useCuratedMap(pack: IconPack): IconMap | null {\n return useSyncExternalStore(\n subscribe,\n () => curatedCache.get(pack) ?? null,\n () => null,\n );\n}\n\nfunction useExtendedMap(pack: IconPack): IconMap | null {\n return useSyncExternalStore(\n subscribe,\n () => extendedCache.get(pack) ?? null,\n () => null,\n );\n}\n\n/* ── Pack loading ────────────────────────────────────────────────\n Each pack is isolated in its own file under ./icon-packs/, and reads\n its package through an import handed to it: the app's own for its\n theme's pack (registerIconPack, Sprint 25.21 P1), the library's own\n for Lucide. No file the library publishes names a pack the app may\n not have installed, so no bundler looks for one: Next.js's webpack\n resolved every pack the library named, and failed the build.\n\n Loading is two-phase:\n 1. Curated map (50 static named imports) — available as soon as\n the pack chunk loads, cached and notified immediately.\n 2. Extended map (full barrel import + kebab-case iteration) —\n loaded in the background, cached and notified when ready. */\n\nconst loadingPacks = new Set<IconPack>();\nconst failedPacks = new Set<IconPack>();\n\ntype PackModule = {\n loadCurated(load: PackLoad): Promise<IconMap>;\n loadExtended(load: PackLoad): Promise<IconMap>;\n};\n\n/** Each pack's import: Lucide's is the library's own (every app installs it); the others are the app's, registered. */\nconst packImports = new Map<IconPack, PackLoad>([[\"lucide\", () => import(\"lucide-react\")]]);\n\n/**\n * The theme's icon pack, as the app's own import (Sprint 25.21 P1): `registerIconPack('feather', () =>\n * import('react-icons/fi'))` at the top of the shell, before any preloadPack. The library names no pack itself, so an\n * app's bundler only ever meets the pack the app installed. A pack drawn before it is registered draws Lucide's icons,\n * and draws its own once registered. Lucide needs no line.\n */\nexport function registerIconPack(pack: RegisteredIconPack, load: PackLoad): void {\n packImports.set(pack, load);\n // a draw that met the pack unregistered loads it now: its icons redraw once the pack's map arrives (a notice alone\n // changes no snapshot, so nothing would redraw to ask)\n if (failedPacks.delete(pack)) void loadPack(pack);\n}\n\n/** The npm package each pack needs — named in the error when a pack is missing. */\nconst PEER_PACKAGE: Record<IconPack, string> = {\n lucide: \"lucide-react\",\n phosphor: \"@phosphor-icons/react\",\n heroicons: \"@heroicons/react\",\n tabler: \"@tabler/icons-react\",\n feather: \"react-icons\",\n};\n\nasync function loadPack(pack: IconPack): Promise<void> {\n if (curatedCache.has(pack) || loadingPacks.has(pack) || failedPacks.has(pack)) return;\n loadingPacks.add(pack);\n\n // Each pack module reaches its package only through the import handed to it\n // (see icon-packs/*.ts), so the chunk import below always succeeds and it is\n // loadCurated() that rejects when the package is not installed.\n const load = packImports.get(pack);\n if (!load) {\n // not registered: Lucide's icons stand in (Icon's third tier), and the developer is told the line to add\n failedPacks.add(pack);\n loadingPacks.delete(pack);\n const lines = iconPackRegistration(pack as RegisteredIconPack);\n warnOnce(\"IconProvider\", `the ${pack} icon pack isn't registered, so its icons are drawn from Lucide. At the top of the shell, before any preloadPack: ${lines.importLine}; ${lines.call}`);\n return;\n }\n let mod: PackModule;\n let curated: IconMap;\n try {\n switch (pack) {\n case \"lucide\": mod = await import(\"./icon-packs/lucide\"); break;\n case \"phosphor\": mod = await import(\"./icon-packs/phosphor\"); break;\n case \"heroicons\": mod = await import(\"./icon-packs/heroicons\"); break;\n case \"tabler\": mod = await import(\"./icon-packs/tabler\"); break;\n case \"feather\": mod = await import(\"./icon-packs/feather\"); break;\n }\n curated = await mod!.loadCurated(load);\n } catch {\n loadingPacks.delete(pack);\n failedPacks.add(pack);\n console.error(`gridsmith-ui: ${PEER_PACKAGE[pack]} is not installed. Run: npm install ${PEER_PACKAGE[pack]}`);\n return;\n }\n\n curatedCache.set(pack, curated);\n notifySubscribers();\n\n mod!.loadExtended(load).then((extended) => {\n extendedCache.set(pack, extended);\n notifySubscribers();\n }).catch(() => {\n // Extended map failed (e.g. network error loading the full barrel).\n // Curated icons still work; extended names will not resolve.\n });\n}\n\n/** A pack's registered import, for the tests that load the packs directly; they register them first. */\nfunction importOf(pack: IconPack): PackLoad {\n const load = packImports.get(pack);\n if (!load) throw new Error(`gridsmith-ui: register the ${pack} icon pack first (registerIconPack)`);\n return load;\n}\n\n/** Exported for the icon-completeness test — not part of the public API. */\nexport const packLoaders: Record<IconPack, {\n curated: () => Promise<IconMap>;\n extended: () => Promise<IconMap>;\n}> = {\n lucide: { curated: () => import(\"./icon-packs/lucide\").then((m) => m.loadCurated(importOf(\"lucide\"))), extended: () => import(\"./icon-packs/lucide\").then((m) => m.loadExtended(importOf(\"lucide\"))) },\n phosphor: { curated: () => import(\"./icon-packs/phosphor\").then((m) => m.loadCurated(importOf(\"phosphor\"))), extended: () => import(\"./icon-packs/phosphor\").then((m) => m.loadExtended(importOf(\"phosphor\"))) },\n heroicons: { curated: () => import(\"./icon-packs/heroicons\").then((m) => m.loadCurated(importOf(\"heroicons\"))), extended: () => import(\"./icon-packs/heroicons\").then((m) => m.loadExtended(importOf(\"heroicons\"))) },\n tabler: { curated: () => import(\"./icon-packs/tabler\").then((m) => m.loadCurated(importOf(\"tabler\"))), extended: () => import(\"./icon-packs/tabler\").then((m) => m.loadExtended(importOf(\"tabler\"))) },\n feather: { curated: () => import(\"./icon-packs/feather\").then((m) => m.loadCurated(importOf(\"feather\"))), extended: () => import(\"./icon-packs/feather\").then((m) => m.loadExtended(importOf(\"feather\"))) },\n};\n\n/* ── Context ──────────────────────────────────────────────────── */\n\ninterface IconContextValue {\n pack: IconPack;\n weight: number;\n}\n\nconst IconCtx = createContext<IconContextValue>({ pack: \"lucide\", weight: 1.5 });\n\n/**\n * Sets the icon pack and stroke weight for every Icon inside it, and starts loading the pack. The app shell's generated\n * code puts it around the app with the theme's pack, so a page doesn't add its own.\n */\nexport function IconProvider({ pack, weight, children }: { pack: IconPack; weight: number; children: ReactNode }) {\n useEffect(() => {\n loadPack(pack);\n if (pack !== \"lucide\") loadPack(\"lucide\");\n }, [pack]);\n\n return <IconCtx.Provider value={{ pack, weight }}>{children}</IconCtx.Provider>;\n}\n\n/**\n * Start loading a pack's icon chunk before React mounts. Call this in your\n * app's entry point or shell for the fastest possible cold start, after the\n * pack is registered (every pack but Lucide, Sprint 25.21 P1):\n *\n * import { preloadPack, registerIconPack } from 'gridsmith-ui/icons'\n * registerIconPack('feather', () => import('react-icons/fi'))\n * preloadPack('feather')\n */\nexport function preloadPack(pack: IconPack): void {\n loadPack(pack);\n}\n\n/* ── Phosphor weight mapping ──────────────────────────────────── */\n\n/**\n * A stroke weight maps to a stroke weight only. `fill` and `duotone` change the\n * glyph itself (Phosphor's filled Check is a check inside a filled square), so\n * a theme's heaviest stroke must never reach them — the Sprint 22 bench found\n * every tick on a 2.5-weight theme rendered as a filled box (14 Sep 2026).\n */\nexport function getPhosphorWeight(numericWeight: number): \"thin\" | \"light\" | \"regular\" | \"bold\" {\n if (numericWeight <= 0.75) return \"thin\";\n if (numericWeight <= 1.25) return \"light\";\n if (numericWeight <= 1.75) return \"regular\";\n return \"bold\";\n}\n\n/* ── Icon component ───────────────────────────────────────────── */\n\n/**\n * `from`: the component slot that drew the icon (\"Button's iconLeft\"), named in the development warning when the theme's\n * pack has no icon by that name. A name no pack has draws nothing (Sprint 25.17 A26: an arrow or a word passed as an\n * icon was printed as text before slots took names).\n */\n/**\n * Whether a map holds an icon of its own by this name, never one its prototype lends: a slot now takes any string, and\n * \"constructor\", \"toString\" or \"__proto__\" read off a plain object drew junk or threw (Sprint 25.17's review). The icon\n * is then read off the map itself, so the compiler sees a component the map holds, not one made while rendering.\n */\nfunction owns(map: Record<string, IconComponent> | null | undefined, name: string): map is Record<string, IconComponent> {\n return !!map && Object.prototype.hasOwnProperty.call(map, name);\n}\n\n/**\n * An icon by name, drawn from the pack the IconProvider around it sets (the theme's, through the app shell), with Lucide\n * as the fallback when the pack lacks the name; `size` in pixels, 16 by default. A name neither has draws nothing and\n * warns once in development, where `from` names the caller.\n */\nexport function Icon({ name, size = 16, className, from }: { name: IconName; size?: number; className?: string; from?: string }) {\n const { pack, weight } = useContext(IconCtx);\n\n const curatedMap = useCuratedMap(pack);\n const extendedMap = useExtendedMap(pack);\n const lucideCurated = useCuratedMap(\"lucide\");\n const lucideExtended = useExtendedMap(\"lucide\");\n\n // Ensure the active pack is loading (covers cases where Icon mounts\n // before IconProvider's useEffect fires, e.g. during SSR hydration)\n if (!curatedCache.has(pack) && !loadingPacks.has(pack)) {\n loadPack(pack);\n }\n\n let Comp: IconComponent | undefined | null = null;\n let renderPack: IconPack = pack;\n\n // Tier 1: curated map (available once pack chunk has loaded)\n if (owns(curatedMap, name)) {\n Comp = curatedMap[name];\n }\n\n // Tier 2: extended map (available after barrel import completes)\n if (!Comp && owns(extendedMap, name)) {\n Comp = extendedMap[name];\n }\n\n // Tier 3: lucide fallback (curated first, then extended)\n if (!Comp && pack !== \"lucide\") {\n const fallback = owns(lucideCurated, name) ? lucideCurated[name] : owns(lucideExtended, name) ? lucideExtended[name] : undefined;\n if (fallback) {\n Comp = fallback;\n renderPack = \"lucide\";\n }\n }\n\n const allMapsLoaded = !!(curatedMap && extendedMap && (pack === \"lucide\" || (lucideCurated && lucideExtended)));\n const missing = !Comp && allMapsLoaded;\n useEffect(() => {\n if (missing) warnOnce(\"Icon\", `\"${name}\" is not an icon name in the ${pack} pack${pack === \"lucide\" ? \"\" : \" or Lucide\"}, so ${from ?? \"it\"} draws nothing. Pass an icon name, or an element.`);\n }, [missing, name, pack, from]);\n\n if (!Comp) {\n if (allMapsLoaded) return null;\n return <span style={{ display: \"inline-block\", width: size, height: size }} className={className} />;\n }\n\n if (renderPack === \"phosphor\") {\n return <Comp size={size} weight={getPhosphorWeight(weight)} className={className} />;\n }\n\n if (renderPack === \"heroicons\") {\n return <Comp className={className} style={{ width: size, height: size, strokeWidth: weight }} />;\n }\n\n if (renderPack === \"tabler\") {\n return <Comp size={size} stroke={weight} className={className} />;\n }\n\n if (renderPack === \"feather\") {\n return <Comp size={size} className={className} strokeWidth={weight} />;\n }\n\n return <Comp size={size} strokeWidth={weight} className={className} />;\n}\n\n/* ── Icon slots ───────────────────────────────────────────────── */\n\n/**\n * What an icon slot takes (Sprint 25.17 A26): an icon name, drawn as the theme's icon at the slot's size, or an element,\n * drawn as given. `IconName` accepts any string, so text type-checks: in development a name the theme's pack lacks warns,\n * naming the slot, and draws nothing; the engine flags a literal that is no icon name (gs:icon-slot-not-an-icon).\n */\nexport type IconSlot = IconName | ReactElement | false | null | undefined;\n\n/** What an icon slot holds, drawn: a name as the theme's icon at `size`, anything else as given. */\nexport function slotIcon(icon: IconSlot | ReactNode, size: number, from: string): ReactNode {\n if (typeof icon === \"string\") {\n // \"\" is no icon (`saved ? \"check\" : \"\"`): nothing drawn, nothing warned\n if (!icon) return null;\n return ICON_NAME_SHAPE.test(icon) ? <Icon name={icon} size={size} from={from} /> : <NotAnIcon text={icon} from={from} />;\n }\n return icon;\n}\n\n/** The shape of an icon name: kebab case, as every pack's names are written (\"arrow-right\", \"file-text\"). */\nconst ICON_NAME_SHAPE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * Whether a slot draws something: an element, or a string shaped as an icon name. Text (\"→\", \"Save\") draws nothing, so\n * a button keeps its even padding for it (Sprint 25.17's review); a name the pack lacks still warns, once its maps load.\n */\nexport function slotDraws(icon: IconSlot | ReactNode): boolean {\n if (typeof icon === \"string\") return ICON_NAME_SHAPE.test(icon);\n return icon != null && icon !== false && icon !== true;\n}\n\n/** Text given to an icon slot: it draws nothing, and says so in development, naming the slot. */\nfunction NotAnIcon({ text, from }: { text: string; from: string }) {\n useEffect(() => {\n warnOnce(\"Icon\", `\"${text}\" is not an icon name, so ${from} draws nothing. Words go in the label; pass an icon name, or an element.`);\n }, [text, from]);\n return null;\n}\n"]}
@@ -0,0 +1,25 @@
1
+ "use client";
2
+ import { FIXED_FORM, localeFor, browserLanguages, formatLocale } from './chunk-7QLXYRPD.js';
3
+ import { createContext, useSyncExternalStore, useContext } from 'react';
4
+
5
+ var noSubscription = () => () => {
6
+ };
7
+ var inBrowser = () => true;
8
+ var onServer = () => false;
9
+ function useHydrated() {
10
+ return useSyncExternalStore(noSubscription, inBrowser, onServer);
11
+ }
12
+ function useToday() {
13
+ return useHydrated() ? /* @__PURE__ */ new Date() : null;
14
+ }
15
+ var LocaleContext = createContext(null);
16
+ function useFormatLocale() {
17
+ const choice = useContext(LocaleContext);
18
+ const hydrated = useHydrated();
19
+ if (!hydrated) return choice ? choice.server : FIXED_FORM;
20
+ return choice ? localeFor(choice.lang, browserLanguages()) ?? choice.server : formatLocale();
21
+ }
22
+
23
+ export { LocaleContext, useFormatLocale, useHydrated, useToday };
24
+ //# sourceMappingURL=chunk-LJYSVZMI.js.map
25
+ //# sourceMappingURL=chunk-LJYSVZMI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/lib/hydrated.ts","../../../showcase/src/lib/locale.ts"],"names":[],"mappings":";;;;AAEA,IAAM,cAAA,GAAiB,MAAM,MAAM;AAAC,CAAA;AACpC,IAAM,YAAY,MAAM,IAAA;AACxB,IAAM,WAAW,MAAM,KAAA;AAQhB,SAAS,WAAA,GAAuB;AACrC,EAAA,OAAO,oBAAA,CAAqB,cAAA,EAAgB,SAAA,EAAW,QAAQ,CAAA;AACjE;AAMO,SAAS,QAAA,GAAwB;AACtC,EAAA,OAAO,WAAA,EAAY,mBAAI,IAAI,IAAA,EAAK,GAAI,IAAA;AACtC;ACfO,IAAM,aAAA,GAAgB,cAAmC,IAAI;AAS7D,SAAS,eAAA,GAAsC;AACpD,EAAA,MAAM,MAAA,GAAS,WAAW,aAAa,CAAA;AACvC,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,IAAI,CAAC,QAAA,EAAU,OAAO,MAAA,GAAS,OAAO,MAAA,GAAS,UAAA;AAC/C,EAAA,OAAO,MAAA,GAAS,UAAU,MAAA,CAAO,IAAA,EAAM,kBAAkB,CAAA,IAAK,MAAA,CAAO,MAAA,GAAS,YAAA,EAAa;AAC7F","file":"chunk-LJYSVZMI.js","sourcesContent":["import { useSyncExternalStore } from \"react\";\n\nconst noSubscription = () => () => {};\nconst inBrowser = () => true;\nconst onServer = () => false;\n\n/**\n * Whether the browser has taken the page over (Sprint 25.20 D3): false on a server, and in the render that hydrates the\n * server's HTML (it must draw what the server drew), true straight after. A render with no server HTML (a Vite app, a\n * component mounted later) is true from its first render. What depends on the visitor's clock or time zone, which no\n * server knows, waits for it.\n */\nexport function useHydrated(): boolean {\n return useSyncExternalStore(noSubscription, inBrowser, onServer);\n}\n\n/**\n * Today on the visitor's device, once the browser has the page (null on a server and while hydrating): the day a date\n * grid marks and starts its keyboard on. Read on each render, so a grid drawn again after midnight marks the new day.\n */\nexport function useToday(): Date | null {\n return useHydrated() ? new Date() : null;\n}\n","import { createContext, useContext } from \"react\";\nimport { browserLanguages, formatLocale, localeFor, FIXED_FORM, type LocaleChoice } from \"./format-locale\";\nimport { useHydrated } from \"./hydrated\";\n\n// the choice itself needs no React, so server code can make it too (Sprint 25.22's review: requestLocale, on utils)\nexport { localeChoice, FIXED_FORM, type LocaleChoice } from \"./format-locale\";\n\nexport const LocaleContext = createContext<LocaleChoice | null>(null);\n\n/**\n * The locale a component writes its dates and numbers in (Sprint 25.20 L1). On a server, and while the browser hydrates\n * the server's HTML, the one the server chose: LocaleProvider's (the visitor's form of the page's language, from the\n * request), else \"en\", never a machine's own default, so both sides write the same. Once the page has loaded, the\n * browser's own reading (the visitor's form of the page's `lang`, as formatLocale() reads it), usually the same. A render\n * with no server HTML reads the browser's from its first render. Undefined is the browser's own language.\n */\nexport function useFormatLocale(): string | undefined {\n const choice = useContext(LocaleContext);\n const hydrated = useHydrated();\n if (!hydrated) return choice ? choice.server : FIXED_FORM;\n return choice ? localeFor(choice.lang, browserLanguages()) ?? choice.server : formatLocale();\n}\n"]}
@@ -1,13 +1,13 @@
1
1
  "use client";
2
- import { ControlSizeContext, triggerType, Button, useShellPreferences, PREFERENCE_HOUSE_DEFAULTS } from './chunk-RJEUQ7RL.js';
2
+ import { useShellPreferences, PREFERENCE_HOUSE_DEFAULTS } from './chunk-TYNDXMBV.js';
3
3
  import { useInputModality } from './chunk-7RKC5C7G.js';
4
4
  import { useReportedChange, useOptionalFormContext, FormChangeCtx } from './chunk-72SDAX54.js';
5
- import { RowsCtx } from './chunk-QM4SJKWV.js';
5
+ import { ControlSizeContext, triggerType, RowsCtx, Button } from './chunk-Q36JVJX4.js';
6
6
  import { useRevealFocus } from './chunk-HTXP5BMM.js';
7
- import { usePopupFocus, focusAfterRemoval, usePressMarks, focusInitial } from './chunk-ZI3OQDET.js';
8
- import { useTextOverflow, AutoTooltip } from './chunk-37WOC7F7.js';
7
+ import { useFocusLeave, usePopupFocus, focusAfterRemoval, usePressMarks, focusInitial } from './chunk-OIOII5L6.js';
8
+ import { useTextOverflow, AutoTooltip } from './chunk-GQVBZ4GW.js';
9
9
  import { Slot } from './chunk-7Q46JXSK.js';
10
- import { Icon, warnOnce } from './chunk-MKFWHWBK.js';
10
+ import { Icon, warnOnce } from './chunk-L6HWACK7.js';
11
11
  import { usePosition } from './chunk-HP7WUZPK.js';
12
12
  import { useLatest } from './chunk-WH2FUFAC.js';
13
13
  import { cn, ERROR_HAS_FOCUS_RING, FIELD_HAS_FOCUS_RING, ERROR_FOCUS_RING, FIELD_FOCUS_RING, FOCUS_RING, ERROR_ACTIVE_RING, ACTIVE_RING, ERROR_FOCUS_WITHIN_RING, FIELD_FOCUS_WITHIN_RING, GLASS_DROPDOWN } from './chunk-JTU7OBYJ.js';
@@ -281,6 +281,7 @@ function useFieldControl(own, options = {}) {
281
281
  const ids = [own["aria-describedby"], field?.describedBy, noteId].filter(Boolean).join(" ").split(/\s+/).filter(Boolean);
282
282
  return {
283
283
  id,
284
+ label: own["aria-label"],
284
285
  labelId: own["aria-labelledby"] ?? field?.labelId,
285
286
  describedBy: [...new Set(ids)].join(" ") || void 0,
286
287
  invalid,
@@ -303,40 +304,26 @@ function Field({ label, children, hint, error, required, labelAction, hideLabel
303
304
  const child = typeof children === "function" ? null : Children.only(children);
304
305
  const childProps = child && isValidElement(child) ? child.props : null;
305
306
  const latest = useLatest({ validate, childProps, id, mode, left, showing: !!checkError, form });
306
- const check = useCallback(() => {
307
+ const check = () => {
307
308
  const { validate: fn, childProps: props, id: controlId } = latest.current;
308
309
  if (!fn) return void 0;
309
310
  const value = props && "value" in props ? props.value : props && "checked" in props ? props.checked : document.getElementById(controlId)?.value;
310
311
  const message = fn(value);
311
312
  return typeof message === "string" && message.length > 0 ? message : void 0;
312
- }, [latest]);
313
- const show = useCallback((message) => {
313
+ };
314
+ const show = (message) => {
314
315
  const { form: f, id: controlId } = latest.current;
315
316
  if (f) f.setError(controlId, message);
316
317
  else setLocalError(message);
317
- }, [latest]);
318
+ };
318
319
  const register = form?.register;
319
320
  const checks = !!validate;
320
- useEffect(() => register && checks ? register(id, check) : void 0, [register, checks, id, check]);
321
- const leaving = useRef(null);
322
- useEffect(() => () => {
323
- if (leaving.current) clearTimeout(leaving.current);
324
- }, []);
325
- const onFocusCapture = () => {
326
- if (leaving.current) {
327
- clearTimeout(leaving.current);
328
- leaving.current = null;
329
- }
330
- };
331
- const onBlurCapture = () => {
332
- if (!validate) return;
333
- if (leaving.current) clearTimeout(leaving.current);
334
- leaving.current = setTimeout(() => {
335
- leaving.current = null;
336
- setLeft(true);
337
- if (latest.current.mode !== "onSubmit" || latest.current.showing) show(check());
338
- }, 0);
339
- };
321
+ const checkNow = useLatest(check);
322
+ useEffect(() => register && checks ? register(id, () => checkNow.current()) : void 0, [register, checks, id, checkNow]);
323
+ const leave = useFocusLeave(() => {
324
+ setLeft(true);
325
+ if (latest.current.mode !== "onSubmit" || latest.current.showing) show(check());
326
+ });
340
327
  const recheck = () => {
341
328
  if (!validate) return;
342
329
  setTimeout(() => {
@@ -379,7 +366,7 @@ function Field({ label, children, hint, error, required, labelAction, hideLabel
379
366
  "div",
380
367
  {
381
368
  className: cn("flex flex-col gap-ds-1.5 max-w-[var(--ds-form-max,none)]", className),
382
- ...validate ? { onFocusCapture, onBlurCapture, onInputCapture: recheck } : {},
369
+ ...validate ? { ...leave, onInputCapture: recheck } : {},
383
370
  children: [
384
371
  hideLabel && !labelAction ? labelNode : /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-ds-2", children: [
385
372
  labelNode,
@@ -785,7 +772,8 @@ function NumberInput({
785
772
  className,
786
773
  error = false,
787
774
  allowDecimals,
788
- id
775
+ id,
776
+ "aria-label": ariaLabel
789
777
  }) {
790
778
  const onChange = useReportedChange(onChangeProp, value);
791
779
  useInputModality();
@@ -872,12 +860,23 @@ function NumberInput({
872
860
  e.preventDefault();
873
861
  decrement();
874
862
  }
863
+ const plain = !e.shiftKey && !e.ctrlKey && !e.metaKey && !e.altKey;
864
+ if (e.key === "Home" && plain && Number.isFinite(min)) {
865
+ e.preventDefault();
866
+ commitValue(min);
867
+ }
868
+ if (e.key === "End" && plain && Number.isFinite(max)) {
869
+ e.preventDefault();
870
+ commitValue(max);
871
+ }
875
872
  },
876
- [increment, decrement]
873
+ [increment, decrement, commitValue, min, max]
877
874
  );
878
875
  const s = sizes[size];
879
876
  const atMin = value <= min;
880
877
  const atMax = value >= max;
878
+ const shown = focused ? draft : String(value);
879
+ const holdsNumber = Number.isFinite(parseFloat(shown)) && Number.isFinite(value);
881
880
  return /* @__PURE__ */ jsxs(
882
881
  "div",
883
882
  {
@@ -934,9 +933,14 @@ function NumberInput({
934
933
  ref: inputRef,
935
934
  id,
936
935
  type: "text",
936
+ role: "spinbutton",
937
+ "aria-label": ariaLabel,
938
+ "aria-valuenow": holdsNumber ? value : void 0,
939
+ "aria-valuemin": Number.isFinite(min) ? min : void 0,
940
+ "aria-valuemax": Number.isFinite(max) ? max : void 0,
937
941
  inputMode: decimals ? "decimal" : "numeric",
938
942
  "aria-invalid": error || void 0,
939
- value: focused ? draft : String(value),
943
+ value: shown,
940
944
  onChange: handleInput,
941
945
  onFocus: handleFocus,
942
946
  onBlur: handleBlur,
@@ -1370,5 +1374,5 @@ function useWholeListCheck(component, length, pager) {
1370
1374
  }
1371
1375
 
1372
1376
  export { AddAnotherRowCtx, CHECK_FILL, Dropdown, Field, FilterRowContext, InlineFieldError, Input, Label, NumberInput, OutsideField, Pagination, Popover, SWITCH_KNOB_MOTION, SWITCH_KNOB_OFF, SWITCH_KNOB_ON, SWITCH_TRACK_OFF, SWITCH_TRACK_ON, Textarea, Toggle, pagerPart, useField, useFieldControl, usePagination, useWholeListCheck };
1373
- //# sourceMappingURL=chunk-FUKSLQ77.js.map
1374
- //# sourceMappingURL=chunk-FUKSLQ77.js.map
1377
+ //# sourceMappingURL=chunk-NI5542ZM.js.map
1378
+ //# sourceMappingURL=chunk-NI5542ZM.js.map