@epilot/spark-ui 0.26.1 → 0.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/dist/components/alert-dialog.cjs +1 -1
  2. package/dist/components/alert-dialog.js +1 -1
  3. package/dist/components/avatar.d.cts +1 -1
  4. package/dist/components/avatar.d.ts +1 -1
  5. package/dist/components/badge.cjs +10 -2
  6. package/dist/components/badge.cjs.map +1 -1
  7. package/dist/components/badge.d.cts +2 -1
  8. package/dist/components/badge.d.ts +2 -1
  9. package/dist/components/badge.js +10 -2
  10. package/dist/components/badge.js.map +1 -1
  11. package/dist/components/banner.d.cts +1 -1
  12. package/dist/components/banner.d.ts +1 -1
  13. package/dist/components/box.d.cts +1 -1
  14. package/dist/components/box.d.ts +1 -1
  15. package/dist/components/bubble.cjs +1 -1
  16. package/dist/components/bubble.cjs.map +1 -1
  17. package/dist/components/bubble.d.cts +1 -1
  18. package/dist/components/bubble.d.ts +1 -1
  19. package/dist/components/bubble.js +1 -1
  20. package/dist/components/bubble.js.map +1 -1
  21. package/dist/components/button-group.d.cts +1 -1
  22. package/dist/components/button-group.d.ts +1 -1
  23. package/dist/components/button.cjs +28 -6
  24. package/dist/components/button.cjs.map +1 -1
  25. package/dist/components/button.d.cts +7 -6
  26. package/dist/components/button.d.ts +7 -6
  27. package/dist/components/button.js +28 -6
  28. package/dist/components/button.js.map +1 -1
  29. package/dist/components/callout.d.cts +1 -1
  30. package/dist/components/callout.d.ts +1 -1
  31. package/dist/components/card.cjs +8 -4
  32. package/dist/components/card.cjs.map +1 -1
  33. package/dist/components/card.d.cts +6 -2
  34. package/dist/components/card.d.ts +6 -2
  35. package/dist/components/card.js +8 -4
  36. package/dist/components/card.js.map +1 -1
  37. package/dist/components/checkbox-group.cjs +31 -0
  38. package/dist/components/checkbox-group.cjs.map +1 -0
  39. package/dist/components/checkbox-group.d.cts +27 -0
  40. package/dist/components/checkbox-group.d.ts +27 -0
  41. package/dist/components/checkbox-group.js +28 -0
  42. package/dist/components/checkbox-group.js.map +1 -0
  43. package/dist/components/checkbox.d.cts +1 -1
  44. package/dist/components/checkbox.d.ts +1 -1
  45. package/dist/components/dialog.cjs +1 -1
  46. package/dist/components/dialog.js +1 -1
  47. package/dist/components/empty-state.d.cts +2 -2
  48. package/dist/components/empty-state.d.ts +2 -2
  49. package/dist/components/field.cjs +110 -15
  50. package/dist/components/field.cjs.map +1 -1
  51. package/dist/components/field.d.cts +16 -3
  52. package/dist/components/field.d.ts +16 -3
  53. package/dist/components/field.js +111 -17
  54. package/dist/components/field.js.map +1 -1
  55. package/dist/components/input-group.cjs +9 -9
  56. package/dist/components/input-group.cjs.map +1 -1
  57. package/dist/components/input-group.js +9 -9
  58. package/dist/components/input-group.js.map +1 -1
  59. package/dist/components/input.d.cts +1 -1
  60. package/dist/components/input.d.ts +1 -1
  61. package/dist/components/list.cjs +21 -5
  62. package/dist/components/list.cjs.map +1 -1
  63. package/dist/components/list.d.cts +13 -5
  64. package/dist/components/list.d.ts +13 -5
  65. package/dist/components/list.js +21 -5
  66. package/dist/components/list.js.map +1 -1
  67. package/dist/components/marker.cjs +1 -1
  68. package/dist/components/marker.cjs.map +1 -1
  69. package/dist/components/marker.d.cts +1 -1
  70. package/dist/components/marker.d.ts +1 -1
  71. package/dist/components/marker.js +1 -1
  72. package/dist/components/marker.js.map +1 -1
  73. package/dist/components/pill.d.cts +1 -1
  74. package/dist/components/pill.d.ts +1 -1
  75. package/dist/components/radio.cjs +6 -2
  76. package/dist/components/radio.cjs.map +1 -1
  77. package/dist/components/radio.d.cts +4 -2
  78. package/dist/components/radio.d.ts +4 -2
  79. package/dist/components/radio.js +6 -2
  80. package/dist/components/radio.js.map +1 -1
  81. package/dist/components/rating.cjs +167 -0
  82. package/dist/components/rating.cjs.map +1 -0
  83. package/dist/components/rating.d.cts +50 -0
  84. package/dist/components/rating.d.ts +50 -0
  85. package/dist/components/rating.js +162 -0
  86. package/dist/components/rating.js.map +1 -0
  87. package/dist/components/scroll-area.cjs +9 -2
  88. package/dist/components/scroll-area.cjs.map +1 -1
  89. package/dist/components/scroll-area.d.cts +1 -1
  90. package/dist/components/scroll-area.d.ts +1 -1
  91. package/dist/components/scroll-area.js +10 -3
  92. package/dist/components/scroll-area.js.map +1 -1
  93. package/dist/components/segmented-control.d.cts +1 -1
  94. package/dist/components/segmented-control.d.ts +1 -1
  95. package/dist/components/select.d.cts +1 -1
  96. package/dist/components/select.d.ts +1 -1
  97. package/dist/components/selection-item.cjs +42 -0
  98. package/dist/components/selection-item.cjs.map +1 -0
  99. package/dist/components/selection-item.d.cts +25 -0
  100. package/dist/components/selection-item.d.ts +25 -0
  101. package/dist/components/selection-item.js +39 -0
  102. package/dist/components/selection-item.js.map +1 -0
  103. package/dist/components/sheet.cjs +1 -1
  104. package/dist/components/sheet.js +1 -1
  105. package/dist/components/skeleton.cjs +1 -1
  106. package/dist/components/skeleton.cjs.map +1 -1
  107. package/dist/components/skeleton.js +1 -1
  108. package/dist/components/skeleton.js.map +1 -1
  109. package/dist/components/slider.cjs +1 -1
  110. package/dist/components/slider.cjs.map +1 -1
  111. package/dist/components/slider.js +1 -1
  112. package/dist/components/slider.js.map +1 -1
  113. package/dist/components/spinner.cjs +81 -8
  114. package/dist/components/spinner.cjs.map +1 -1
  115. package/dist/components/spinner.js +81 -8
  116. package/dist/components/spinner.js.map +1 -1
  117. package/dist/components/stepper.cjs +3 -2
  118. package/dist/components/stepper.cjs.map +1 -1
  119. package/dist/components/stepper.js +3 -2
  120. package/dist/components/stepper.js.map +1 -1
  121. package/dist/components/switch-group.cjs +32 -0
  122. package/dist/components/switch-group.cjs.map +1 -0
  123. package/dist/components/switch-group.d.cts +26 -0
  124. package/dist/components/switch-group.d.ts +26 -0
  125. package/dist/components/switch-group.js +29 -0
  126. package/dist/components/switch-group.js.map +1 -0
  127. package/dist/components/switch.d.cts +1 -1
  128. package/dist/components/switch.d.ts +1 -1
  129. package/dist/components/tabs.cjs +32 -23
  130. package/dist/components/tabs.cjs.map +1 -1
  131. package/dist/components/tabs.d.cts +5 -5
  132. package/dist/components/tabs.d.ts +5 -5
  133. package/dist/components/tabs.js +32 -23
  134. package/dist/components/tabs.js.map +1 -1
  135. package/dist/components/textarea.d.cts +1 -1
  136. package/dist/components/textarea.d.ts +1 -1
  137. package/dist/components/theme.cjs +44 -2
  138. package/dist/components/theme.cjs.map +1 -1
  139. package/dist/components/theme.d.cts +4 -2
  140. package/dist/components/theme.d.ts +4 -2
  141. package/dist/components/theme.js +44 -2
  142. package/dist/components/theme.js.map +1 -1
  143. package/dist/components/toast.cjs +1 -1
  144. package/dist/components/toast.js +1 -1
  145. package/dist/components/toggle-group.d.cts +1 -1
  146. package/dist/components/toggle-group.d.ts +1 -1
  147. package/dist/components/toggle.d.cts +2 -2
  148. package/dist/components/toggle.d.ts +2 -2
  149. package/dist/lib/accent-theme.d.cts +1 -1
  150. package/dist/lib/accent-theme.d.ts +1 -1
  151. package/dist/lib/container-chrome.cjs +4 -4
  152. package/dist/lib/container-chrome.cjs.map +1 -1
  153. package/dist/lib/container-chrome.d.cts +1 -1
  154. package/dist/lib/container-chrome.d.ts +1 -1
  155. package/dist/lib/container-chrome.js +4 -4
  156. package/dist/lib/container-chrome.js.map +1 -1
  157. package/dist/lib/input-box-variants.cjs +8 -8
  158. package/dist/lib/input-box-variants.cjs.map +1 -1
  159. package/dist/lib/input-box-variants.d.cts +1 -1
  160. package/dist/lib/input-box-variants.d.ts +1 -1
  161. package/dist/lib/input-box-variants.js +8 -8
  162. package/dist/lib/input-box-variants.js.map +1 -1
  163. package/dist/lib/selection-list.cjs +17 -0
  164. package/dist/lib/selection-list.cjs.map +1 -0
  165. package/dist/lib/selection-list.d.cts +11 -0
  166. package/dist/lib/selection-list.d.ts +11 -0
  167. package/dist/lib/selection-list.js +15 -0
  168. package/dist/lib/selection-list.js.map +1 -0
  169. package/dist/lib/theme-types.cjs.map +1 -1
  170. package/dist/lib/theme-types.d.cts +2 -2
  171. package/dist/lib/theme-types.d.ts +2 -2
  172. package/dist/lib/theme-types.js.map +1 -1
  173. package/dist/lib/toggle-group-context.d.cts +2 -1
  174. package/dist/lib/toggle-group-context.d.ts +2 -1
  175. package/dist/styles/spark-default.css +12 -1
  176. package/dist/styles/theme.css +98 -0
  177. package/dist/{theme-types-Bn5E15-P.d.ts → theme-types-CKZzOmSa.d.cts} +11 -2
  178. package/dist/{theme-types-Bn5E15-P.d.cts → theme-types-CKZzOmSa.d.ts} +11 -2
  179. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"field.cjs","names":["React","useTheme","Skeleton","Separator"],"sources":["../../src/components/field.tsx"],"sourcesContent":["'use client'\n\nimport { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { useId } from 'react'\nimport type {\n SparkInputColor,\n SparkInputStyle,\n SparkLabelPosition,\n} from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { Separator } from './separator'\nimport { Skeleton } from './skeleton'\nimport { useTheme } from './theme'\n\ntype FieldContextValue = {\n id: string\n labelId: string\n descriptionId: string\n hasDescription: boolean\n setHasDescription: (has: boolean) => void\n errorId: string\n hasError: boolean\n setHasError: (has: boolean) => void\n readOnly: boolean\n labelPosition: SparkLabelPosition\n inputStyle: SparkInputStyle\n inputColor: SparkInputColor\n}\n\nconst FieldContext = React.createContext<FieldContextValue | null>(null)\n\nexport function useFieldContext() {\n return React.useContext(FieldContext)\n}\n\n// Field→Theme→default resolution + id/aria-describedby wiring, shared by every\n// control that can sit inside a Field (Input, SelectTrigger, …) so they can't\n// drift on how a style/color/id resolves. `variant` is the control's own\n// per-instance override (already normalized — e.g. Input maps its 'none' to\n// undefined before calling this).\nexport function useFieldControl(options?: {\n id?: string\n ariaDescribedBy?: string\n variant?: SparkInputStyle\n}) {\n const field = useFieldContext()\n const theme = useTheme()\n const id = options?.id ?? field?.id\n const errorId = field?.hasError ? field.errorId : undefined\n const descriptionId = field?.hasDescription ? field.descriptionId : undefined\n // error before hint: the actionable message should be announced first\n const ariaDescribedBy =\n [errorId, descriptionId, options?.ariaDescribedBy]\n .filter(Boolean)\n .join(' ') || undefined\n const inputStyle =\n options?.variant ?? field?.inputStyle ?? theme.inputStyle ?? 'soft'\n const inputColor = field?.inputColor ?? theme.inputColor ?? 'brand'\n const ariaInvalid = field?.hasError || undefined\n return { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor }\n}\n\nfunction FieldSet({ className, ...props }: React.ComponentProps<'fieldset'>) {\n return (\n <fieldset\n data-slot=\"field-set\"\n className={cn(\n 'flex flex-col gap-[calc(var(--spark-layout-gap)*4)]',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction FieldLegend({\n className,\n variant = 'legend',\n ...props\n}: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {\n return (\n <legend\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(\n 'mb-1.5 font-medium text-gray data-[variant=label]:text-sm data-[variant=legend]:text-base',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(\n 'group/field-group flex w-full flex-col gap-[calc(var(--spark-layout-gap)*4)]',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldVariants = cva(\n 'group/field relative flex w-full flex-col data-[invalid=true]:text-error-active',\n {\n variants: {\n labelPosition: {\n // inset puts label + input inside one shell, so the shell IS the input's box —\n // rounded-md tracks Input/Select/etc.'s own control radius, not the container one.\n // focus inside inset is the classic underline cue, not a shell ring\n inset: 'gap-y-1 rounded-md px-4 py-3',\n outset: 'gap-y-2',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n labelPosition: 'inset',\n inputStyle: 'soft',\n inputColor: 'brand',\n class:\n 'bg-input-base has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-input-base-hover',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'soft',\n inputColor: 'gray',\n class:\n 'bg-base-gray-soft has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-gray-a4',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: 'border border-input bg-base-accent-surface',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: 'border border-input-gray bg-base-gray-surface',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: 'border border-input',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: 'border border-input-gray',\n },\n // ghost inset = bare label + underlined input; the input carries the line\n {\n labelPosition: 'inset',\n inputStyle: 'ghost',\n class: 'rounded-none px-0',\n },\n // shared inset states: filled shells swap bg, bordered shells swap border\n // invalid hover deepens the error tint instead of falling back to accent\n {\n labelPosition: 'inset',\n inputStyle: ['soft', 'surface'],\n class:\n 'data-[disabled=true]:bg-base-gray-soft data-[invalid=true]:bg-base-error-soft data-[invalid=true]:has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-red-a4',\n },\n {\n labelPosition: 'inset',\n inputStyle: ['surface', 'outlined'],\n class:\n 'data-[disabled=true]:border-input-disabled data-[invalid=true]:border-input-invalid',\n },\n ],\n },\n)\n\ninterface FieldProps extends React.ComponentProps<'div'> {\n labelPosition?: SparkLabelPosition\n inputStyle?: SparkInputStyle\n inputColor?: SparkInputColor\n readOnly?: boolean\n 'data-invalid'?: boolean | 'true' | 'false'\n}\n\nfunction Field({\n className,\n children,\n labelPosition: labelPositionProp,\n inputStyle: inputStyleProp,\n inputColor: inputColorProp,\n readOnly = false,\n 'data-invalid': dataInvalid,\n ...props\n}: FieldProps) {\n const id = useId()\n const [descriptionCount, setDescriptionCount] = React.useState(0)\n const [errorCount, setErrorCount] = React.useState(0)\n // counts, not flags: one slot unmounting must not clear what siblings still need\n const setHasDescription = React.useCallback(\n (has: boolean) => setDescriptionCount((n) => n + (has ? 1 : -1)),\n [],\n )\n const setHasError = React.useCallback(\n (has: boolean) => setErrorCount((n) => n + (has ? 1 : -1)),\n [],\n )\n const hasDescription = descriptionCount > 0\n const hasError = errorCount > 0\n // knobs are structural (they move the label / pick the box carrier), so they\n // resolve here via the <Theme> context; props override per instance\n const theme = useTheme()\n const labelPosition = labelPositionProp ?? theme.labelPosition ?? 'inset'\n const inputStyle = inputStyleProp ?? theme.inputStyle ?? 'soft'\n const inputColor = inputColorProp ?? theme.inputColor ?? 'brand'\n const fieldId = `field-${id}`\n const labelId = `${fieldId}-label`\n const value = React.useMemo<FieldContextValue>(\n () => ({\n id: fieldId,\n labelId,\n descriptionId: `${fieldId}-description`,\n hasDescription,\n setHasDescription,\n errorId: `${fieldId}-error`,\n hasError,\n setHasError,\n readOnly,\n labelPosition,\n inputStyle,\n inputColor,\n }),\n [\n fieldId,\n labelId,\n hasDescription,\n setHasDescription,\n hasError,\n setHasError,\n readOnly,\n labelPosition,\n inputStyle,\n inputColor,\n ],\n )\n\n return (\n <FieldContext.Provider value={value}>\n <div\n role=\"group\"\n data-slot=\"field\"\n data-label-position={labelPosition}\n data-input-style={inputStyle}\n data-input-color={inputColor}\n data-invalid={dataInvalid ?? (hasError || undefined)}\n aria-labelledby={readOnly ? labelId : undefined}\n className={cn(\n fieldVariants({ labelPosition, inputStyle, inputColor }),\n className,\n )}\n {...props}\n >\n {children}\n </div>\n </FieldContext.Provider>\n )\n}\n\nfunction FieldContent({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-content\"\n className={cn(\n 'flex min-w-0 flex-1 flex-col gap-1 leading-snug',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldLabelClassName =\n 'text-sm leading-5 font-normal text-gray transition-all group-data-[invalid=true]/field:text-error-active! motion-reduce:transition-none'\n\nfunction FieldLabel({\n className,\n htmlFor,\n srOnly,\n children,\n ...props\n}: React.ComponentProps<'label'> & { srOnly?: boolean }) {\n const field = useFieldContext()\n const classNames = cn(\n // labels stay full-contrast even when disabled — the control itself\n // (dimmed text, gray shell) signals the state\n fieldLabelClassName,\n // visually hidden but kept in the a11y tree (still names + focuses the control)\n srOnly && 'sr-only',\n className,\n )\n\n if (field?.readOnly) {\n return (\n <div data-slot=\"field-label\" id={field.labelId} className={classNames}>\n {children}\n </div>\n )\n }\n\n return (\n <label\n data-slot=\"field-label\"\n className={classNames}\n htmlFor={htmlFor ?? field?.id}\n {...props}\n >\n {children}\n </label>\n )\n}\n\nfunction FieldValue({\n className,\n id,\n title,\n truncate,\n children,\n ...props\n}: React.ComponentProps<'div'> & { truncate?: boolean }) {\n return (\n <div\n data-slot=\"field-value\"\n id={id}\n // truncated string values keep the full text reachable on hover\n title={\n title ??\n (truncate && typeof children === 'string' ? children : undefined)\n }\n className={cn(\n // values are content, not controls — disabled dims but stays ≥4.5:1\n 'min-h-8 w-full text-base text-gray tabular-nums group-data-[disabled=true]/field:opacity-70',\n truncate\n ? 'block truncate leading-8'\n : 'inline-flex items-center wrap-anywhere whitespace-pre-wrap',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nfunction FieldSkeleton({\n className,\n labelClassName,\n valueClassName,\n truncate,\n ...props\n}: React.ComponentProps<'div'> & {\n truncate?: boolean\n labelClassName?: string\n valueClassName?: string\n}) {\n return (\n <div\n data-slot=\"field-skeleton\"\n aria-hidden=\"true\"\n className={cn('flex w-full flex-col gap-y-1', className)}\n {...props}\n >\n <Skeleton\n data-slot=\"field-skeleton-label\"\n className={cn('h-5 w-24', labelClassName)}\n />\n <Skeleton\n data-slot=\"field-skeleton-value\"\n className={cn(\n 'h-8 min-h-8 w-full',\n truncate && 'max-w-full truncate',\n valueClassName,\n )}\n />\n </div>\n )\n}\n\nfunction FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-title\"\n className={cn(\n 'flex w-fit items-center gap-2 text-sm font-medium text-gray',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction FieldDescription({\n className,\n id,\n ...props\n}: React.ComponentProps<'div'>) {\n const field = useFieldContext()\n const setHasDescription = field?.setHasDescription\n\n React.useEffect(() => {\n if (!setHasDescription) return\n setHasDescription(true)\n return () => setHasDescription(false)\n }, [setHasDescription])\n\n return (\n <div\n data-slot=\"field-description\"\n id={id ?? field?.descriptionId}\n className={cn('text-sm leading-normal text-gray-light', className)}\n {...props}\n />\n )\n}\n\nfunction FieldSeparator({\n children,\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-separator\"\n data-content={!!children}\n className={cn('relative -my-2 h-5 text-sm', className)}\n {...props}\n >\n <Separator className=\"absolute inset-0 top-1/2\" />\n {children && (\n <span\n className=\"relative mx-auto block w-fit bg-(--spark-background) px-2 text-gray-light\"\n data-slot=\"field-separator-content\"\n >\n {children}\n </span>\n )}\n </div>\n )\n}\n\nfunction FieldError({\n className,\n children,\n errors,\n id,\n ...props\n}: React.ComponentProps<'div'> & {\n errors?: Array<{ message?: string } | undefined>\n}) {\n const field = useFieldContext()\n const setHasError = field?.setHasError\n const content = React.useMemo(() => {\n if (children) return children\n if (!errors?.length) return null\n const unique = [\n ...new Map(errors.map((error) => [error?.message, error])).values(),\n ]\n if (unique.length === 1) return unique[0]?.message\n return (\n <ul className=\"ml-4 flex list-disc flex-col gap-1\">\n {unique.map(\n (error) =>\n error?.message && <li key={error.message}>{error.message}</li>,\n )}\n </ul>\n )\n }, [children, errors])\n\n const hasContent = Boolean(content)\n // a custom id means no element carries field.errorId, so don't advertise it\n const hasCustomId = id != null\n\n // must run before the empty-content early return to keep hook order stable\n React.useEffect(() => {\n if (!setHasError || !hasContent || hasCustomId) return\n setHasError(true)\n return () => setHasError(false)\n }, [setHasError, hasContent, hasCustomId])\n\n if (!content) return null\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n id={id ?? field?.errorId}\n className={cn('text-sm font-normal text-error', className)}\n {...props}\n >\n {content}\n </div>\n )\n}\n\nexport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldLegend,\n FieldSeparator,\n FieldSet,\n FieldSkeleton,\n FieldTitle,\n FieldValue,\n}\nexport type { FieldProps }\n"],"mappings":"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"}
1
+ {"version":3,"file":"field.cjs","names":["React","useTheme","Skeleton","Separator","Callout"],"sources":["../../src/components/field.tsx"],"sourcesContent":["'use client'\n\nimport { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { useId } from 'react'\nimport type {\n SparkInputColor,\n SparkInputStyle,\n SparkLabelPosition,\n} from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { Callout } from './callout'\nimport { Separator } from './separator'\nimport { Skeleton } from './skeleton'\nimport { useTheme } from './theme'\n\ntype FieldContextValue = {\n id: string\n labelId: string\n descriptionId: string\n hasDescription: boolean\n setHasDescription: (has: boolean) => void\n errorId: string\n hasError: boolean\n setHasError: (has: boolean) => void\n readOnly: boolean\n labelPosition: SparkLabelPosition\n inputStyle: SparkInputStyle\n inputColor: SparkInputColor\n}\n\nconst FieldContext = React.createContext<FieldContextValue | null>(null)\n\nexport function useFieldContext() {\n return React.useContext(FieldContext)\n}\n\n// Field→Theme→default resolution + id/aria-describedby wiring, shared by every\n// control that can sit inside a Field (Input, SelectTrigger, …) so they can't\n// drift on how a style/color/id resolves. `variant` is the control's own\n// per-instance override (already normalized — e.g. Input maps its 'none' to\n// undefined before calling this).\nexport function useFieldControl(options?: {\n id?: string\n ariaDescribedBy?: string\n variant?: SparkInputStyle\n}) {\n const field = useFieldContext()\n const theme = useTheme()\n const id = options?.id ?? field?.id\n const errorId = field?.hasError ? field.errorId : undefined\n const descriptionId = field?.hasDescription ? field.descriptionId : undefined\n // error before hint: the actionable message should be announced first\n const ariaDescribedBy =\n [errorId, descriptionId, options?.ariaDescribedBy]\n .filter(Boolean)\n .join(' ') || undefined\n const inputStyle =\n options?.variant ?? field?.inputStyle ?? theme.inputStyle ?? 'soft'\n const inputColor = field?.inputColor ?? theme.inputColor ?? 'brand'\n const ariaInvalid = field?.hasError || undefined\n return { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor }\n}\n\ntype FieldSetIndicator = 'required' | 'optional'\n\ntype FieldSetContextValue = {\n descriptionId: string\n hasDescription: boolean\n setHasDescription: (has: boolean) => void\n errorId: string\n hasError: boolean\n setHasError: (has: boolean) => void\n /** Marks the set invalid without linking `errorId` (error has a custom id). */\n setHasUnlinkedError: (has: boolean) => void\n invalid: boolean\n indicator?: FieldSetIndicator\n indicatorLabel?: string\n}\n\nconst FieldSetContext = React.createContext<FieldSetContextValue | null>(null)\n\n/** True when the nearest enclosing FieldSet has an error; ignores outer sets. */\nfunction useFieldSetInvalid() {\n return React.useContext(FieldSetContext)?.invalid || undefined\n}\n\nfunction useSlotCount() {\n const [count, setCount] = React.useState(0)\n const setHas = React.useCallback(\n (has: boolean) => setCount((n) => n + (has ? 1 : -1)),\n [],\n )\n return [count > 0, setHas] as const\n}\n\ninterface FieldSetProps extends React.ComponentProps<'fieldset'> {\n /** Marks the legend: `*` plus hidden text, or a visible \"(optional)\". */\n indicator?: FieldSetIndicator\n /** Text for the mark; defaults to `'required'` or `'optional'`. */\n indicatorLabel?: string\n}\n\nfunction FieldSet({\n className,\n indicator,\n indicatorLabel,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n}: FieldSetProps) {\n const id = useId()\n const [hasDescription, setHasDescription] = useSlotCount()\n const [hasError, setHasError] = useSlotCount()\n const [hasUnlinkedError, setHasUnlinkedError] = useSlotCount()\n const invalid = hasError || hasUnlinkedError\n const fieldSetId = `field-set-${id}`\n const descriptionId = `${fieldSetId}-description`\n const errorId = `${fieldSetId}-error`\n const value = React.useMemo<FieldSetContextValue>(\n () => ({\n descriptionId,\n hasDescription,\n setHasDescription,\n errorId,\n hasError,\n setHasError,\n setHasUnlinkedError,\n invalid,\n indicator,\n indicatorLabel,\n }),\n [\n descriptionId,\n hasDescription,\n setHasDescription,\n errorId,\n hasError,\n setHasError,\n setHasUnlinkedError,\n invalid,\n indicator,\n indicatorLabel,\n ],\n )\n const ariaDescribedBy =\n [hasError && errorId, hasDescription && descriptionId, ariaDescribedByProp]\n .filter(Boolean)\n .join(' ') || undefined\n\n return (\n <FieldSetContext.Provider value={value}>\n <fieldset\n data-slot=\"field-set\"\n data-invalid={invalid || undefined}\n aria-describedby={ariaDescribedBy}\n className={cn(\n 'group/field-set flex flex-col gap-[calc(var(--spark-layout-gap)*4)]',\n className,\n )}\n {...props}\n />\n </FieldSetContext.Provider>\n )\n}\n\nfunction FieldLegend({\n className,\n variant = 'legend',\n children,\n ...props\n}: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {\n const fieldSet = React.useContext(FieldSetContext)\n const indicator = fieldSet?.indicator\n const indicatorLabel = fieldSet?.indicatorLabel ?? indicator\n return (\n <legend\n data-invalid={fieldSet?.invalid || undefined}\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(\n 'mb-1.5 font-medium text-gray data-[invalid=true]:text-error-active! data-[variant=label]:text-sm data-[variant=legend]:text-base',\n className,\n )}\n {...props}\n >\n {children}\n {indicator === 'required' && (\n <>\n {' '}\n <span aria-hidden=\"true\">*</span>\n <span className=\"sr-only\">{indicatorLabel}</span>\n </>\n )}\n {indicator === 'optional' && (\n <>\n {' '}\n <span className=\"font-normal text-gray-light\">\n ({indicatorLabel})\n </span>\n </>\n )}\n </legend>\n )\n}\n\nfunction FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(\n 'group/field-group flex w-full flex-col gap-[calc(var(--spark-layout-gap)*4)]',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldVariants = cva(\n 'group/field relative flex w-full flex-col data-[invalid=true]:text-error-active',\n {\n variants: {\n labelPosition: {\n // inset puts label + input inside one shell, so the shell IS the input's box —\n // rounded-md tracks Input/Select/etc.'s own control radius, not the container one.\n // focus inside inset is the classic underline cue, not a shell ring\n inset: 'gap-y-1 rounded-md px-4 py-3',\n outset: 'gap-y-2',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n labelPosition: 'inset',\n inputStyle: 'soft',\n inputColor: 'brand',\n class:\n 'bg-input-base has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-input-base-hover',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'soft',\n inputColor: 'gray',\n class:\n 'bg-base-gray-soft has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-gray-a4',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: 'border border-input bg-base-accent-surface',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: 'border border-input-gray bg-base-gray-surface',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: 'border border-input',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: 'border border-input-gray',\n },\n // ghost inset = bare label + underlined input; the input carries the line\n {\n labelPosition: 'inset',\n inputStyle: 'ghost',\n class: 'rounded-none px-0',\n },\n // shared inset states: filled shells swap bg, bordered shells swap border\n // invalid hover deepens the error tint instead of falling back to accent\n {\n labelPosition: 'inset',\n inputStyle: ['soft', 'surface'],\n class:\n 'data-[disabled=true]:bg-base-gray-soft data-[invalid=true]:bg-base-error-soft data-[invalid=true]:has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-red-a4',\n },\n {\n labelPosition: 'inset',\n inputStyle: ['surface', 'outlined'],\n class:\n 'data-[disabled=true]:border-input-disabled data-[invalid=true]:border-input-invalid',\n },\n ],\n },\n)\n\ninterface FieldProps extends React.ComponentProps<'div'> {\n labelPosition?: SparkLabelPosition\n inputStyle?: SparkInputStyle\n inputColor?: SparkInputColor\n readOnly?: boolean\n 'data-invalid'?: boolean | 'true' | 'false'\n}\n\nfunction Field({\n className,\n children,\n labelPosition: labelPositionProp,\n inputStyle: inputStyleProp,\n inputColor: inputColorProp,\n readOnly = false,\n 'data-invalid': dataInvalid,\n ...props\n}: FieldProps) {\n const id = useId()\n const [descriptionCount, setDescriptionCount] = React.useState(0)\n const [errorCount, setErrorCount] = React.useState(0)\n // counts, not flags: one slot unmounting must not clear what siblings still need\n const setHasDescription = React.useCallback(\n (has: boolean) => setDescriptionCount((n) => n + (has ? 1 : -1)),\n [],\n )\n const setHasError = React.useCallback(\n (has: boolean) => setErrorCount((n) => n + (has ? 1 : -1)),\n [],\n )\n const hasDescription = descriptionCount > 0\n const hasError = errorCount > 0\n // knobs are structural (they move the label / pick the box carrier), so they\n // resolve here via the <Theme> context; props override per instance\n const theme = useTheme()\n const labelPosition = labelPositionProp ?? theme.labelPosition ?? 'inset'\n const inputStyle = inputStyleProp ?? theme.inputStyle ?? 'soft'\n const inputColor = inputColorProp ?? theme.inputColor ?? 'brand'\n const fieldId = `field-${id}`\n const labelId = `${fieldId}-label`\n const value = React.useMemo<FieldContextValue>(\n () => ({\n id: fieldId,\n labelId,\n descriptionId: `${fieldId}-description`,\n hasDescription,\n setHasDescription,\n errorId: `${fieldId}-error`,\n hasError,\n setHasError,\n readOnly,\n labelPosition,\n inputStyle,\n inputColor,\n }),\n [\n fieldId,\n labelId,\n hasDescription,\n setHasDescription,\n hasError,\n setHasError,\n readOnly,\n labelPosition,\n inputStyle,\n inputColor,\n ],\n )\n\n return (\n <FieldContext.Provider value={value}>\n <div\n role=\"group\"\n data-slot=\"field\"\n data-label-position={labelPosition}\n data-input-style={inputStyle}\n data-input-color={inputColor}\n data-invalid={dataInvalid ?? (hasError || undefined)}\n aria-labelledby={readOnly ? labelId : undefined}\n className={cn(\n fieldVariants({ labelPosition, inputStyle, inputColor }),\n className,\n )}\n {...props}\n >\n {children}\n </div>\n </FieldContext.Provider>\n )\n}\n\nfunction FieldContent({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-content\"\n className={cn(\n 'flex min-w-0 flex-1 flex-col gap-1 leading-snug',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldLabelClassName =\n 'text-sm leading-5 font-normal text-gray transition-all group-data-[invalid=true]/field:text-error-active! motion-reduce:transition-none'\n\nfunction FieldLabel({\n className,\n htmlFor,\n srOnly,\n children,\n ...props\n}: React.ComponentProps<'label'> & { srOnly?: boolean }) {\n const field = useFieldContext()\n const classNames = cn(\n // labels stay full-contrast even when disabled — the control itself\n // (dimmed text, gray shell) signals the state\n fieldLabelClassName,\n // visually hidden but kept in the a11y tree (still names + focuses the control)\n srOnly && 'sr-only',\n className,\n )\n\n if (field?.readOnly) {\n return (\n <div data-slot=\"field-label\" id={field.labelId} className={classNames}>\n {children}\n </div>\n )\n }\n\n return (\n <label\n data-slot=\"field-label\"\n className={classNames}\n htmlFor={htmlFor ?? field?.id}\n {...props}\n >\n {children}\n </label>\n )\n}\n\nfunction FieldValue({\n className,\n id,\n title,\n truncate,\n children,\n ...props\n}: React.ComponentProps<'div'> & { truncate?: boolean }) {\n return (\n <div\n data-slot=\"field-value\"\n id={id}\n // truncated string values keep the full text reachable on hover\n title={\n title ??\n (truncate && typeof children === 'string' ? children : undefined)\n }\n className={cn(\n // values are content, not controls — disabled dims but stays ≥4.5:1\n 'min-h-8 w-full text-base text-gray tabular-nums group-data-[disabled=true]/field:opacity-70',\n truncate\n ? 'block truncate leading-8'\n : 'inline-flex items-center wrap-anywhere whitespace-pre-wrap',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nfunction FieldSkeleton({\n className,\n labelClassName,\n valueClassName,\n truncate,\n ...props\n}: React.ComponentProps<'div'> & {\n truncate?: boolean\n labelClassName?: string\n valueClassName?: string\n}) {\n return (\n <div\n data-slot=\"field-skeleton\"\n aria-hidden=\"true\"\n className={cn('flex w-full flex-col gap-y-1', className)}\n {...props}\n >\n <Skeleton\n data-slot=\"field-skeleton-label\"\n className={cn('h-5 w-24', labelClassName)}\n />\n <Skeleton\n data-slot=\"field-skeleton-value\"\n className={cn(\n 'h-8 min-h-8 w-full',\n truncate && 'max-w-full truncate',\n valueClassName,\n )}\n />\n </div>\n )\n}\n\nfunction FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-title\"\n className={cn(\n 'flex w-fit items-center gap-2 text-sm font-medium text-gray',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction FieldDescription({\n className,\n id,\n ...props\n}: React.ComponentProps<'div'>) {\n const field = useFieldContext()\n const fieldSet = React.useContext(FieldSetContext)\n const owner = field ?? fieldSet\n const setHasDescription = owner?.setHasDescription\n\n React.useEffect(() => {\n if (!setHasDescription) return\n setHasDescription(true)\n return () => setHasDescription(false)\n }, [setHasDescription])\n\n return (\n <div\n data-slot=\"field-description\"\n id={id ?? owner?.descriptionId}\n className={cn('text-sm leading-normal text-gray-light', className)}\n {...props}\n />\n )\n}\n\nfunction FieldSeparator({\n children,\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-separator\"\n data-content={!!children}\n className={cn('relative -my-2 h-5 text-sm', className)}\n {...props}\n >\n <Separator className=\"absolute inset-0 top-1/2\" />\n {children && (\n <span\n className=\"relative mx-auto block w-fit bg-(--spark-background) px-2 text-gray-light\"\n data-slot=\"field-separator-content\"\n >\n {children}\n </span>\n )}\n </div>\n )\n}\n\nfunction FieldError({\n className,\n children,\n errors,\n id,\n ...props\n}: React.ComponentProps<'div'> & {\n errors?: Array<{ message?: string } | undefined>\n}) {\n const field = useFieldContext()\n const fieldSet = React.useContext(FieldSetContext)\n const owner = field ?? fieldSet\n const setHasError = owner?.setHasError\n const content = React.useMemo(() => {\n if (children) return children\n if (!errors?.length) return null\n const unique = [\n ...new Map(errors.map((error) => [error?.message, error])).values(),\n ]\n if (unique.length === 1) return unique[0]?.message\n return (\n <ul className=\"ml-4 flex list-disc flex-col gap-1\">\n {unique.map(\n (error) =>\n error?.message && <li key={error.message}>{error.message}</li>,\n )}\n </ul>\n )\n }, [children, errors])\n\n const hasContent = Boolean(content)\n // a custom id means no element carries owner.errorId, so don't advertise it\n const hasCustomId = id != null\n const setSetUnlinked = !field ? fieldSet?.setHasUnlinkedError : undefined\n\n // must run before the empty-content early return to keep hook order stable\n React.useEffect(() => {\n if (!hasContent) return\n const set = hasCustomId ? setSetUnlinked : setHasError\n if (!set) return\n set(true)\n return () => set(false)\n }, [setHasError, setSetUnlinked, hasContent, hasCustomId])\n\n if (!content) return null\n\n if (!field && fieldSet) {\n // straight into Callout: CalloutDescription's <p> cannot hold the <ul> above\n return (\n <Callout\n variant=\"error\"\n role=\"alert\"\n data-slot=\"field-error\"\n id={id ?? fieldSet.errorId}\n className={className}\n {...props}\n >\n {content}\n </Callout>\n )\n }\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n id={id ?? field?.errorId}\n className={cn('text-sm font-normal text-error', className)}\n {...props}\n >\n {content}\n </div>\n )\n}\n\nexport {\n useFieldSetInvalid,\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldLegend,\n FieldSeparator,\n FieldSet,\n FieldSkeleton,\n FieldTitle,\n FieldValue,\n}\nexport type { FieldProps, FieldSetProps }\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { a as SparkInputStyle, i as SparkInputColor, o as SparkLabelPosition } from "../theme-types-Bn5E15-P.cjs";
3
+ import { c as SparkInputColor, l as SparkInputStyle, u as SparkLabelPosition } from "../theme-types-CKZzOmSa.cjs";
4
4
  import * as React$1 from "react";
5
5
 
6
6
  //#region src/components/field.d.ts
@@ -31,13 +31,26 @@ declare function useFieldControl(options?: {
31
31
  inputStyle: SparkInputStyle;
32
32
  inputColor: SparkInputColor;
33
33
  };
34
+ type FieldSetIndicator = 'required' | 'optional';
35
+ /** True when the nearest enclosing FieldSet has an error; ignores outer sets. */
36
+ declare function useFieldSetInvalid(): true | undefined;
37
+ interface FieldSetProps extends React$1.ComponentProps<'fieldset'> {
38
+ /** Marks the legend: `*` plus hidden text, or a visible "(optional)". */
39
+ indicator?: FieldSetIndicator;
40
+ /** Text for the mark; defaults to `'required'` or `'optional'`. */
41
+ indicatorLabel?: string;
42
+ }
34
43
  declare function FieldSet({
35
44
  className,
45
+ indicator,
46
+ indicatorLabel,
47
+ 'aria-describedby': ariaDescribedByProp,
36
48
  ...props
37
- }: React$1.ComponentProps<'fieldset'>): import("react/jsx-runtime").JSX.Element;
49
+ }: FieldSetProps): import("react/jsx-runtime").JSX.Element;
38
50
  declare function FieldLegend({
39
51
  className,
40
52
  variant,
53
+ children,
41
54
  ...props
42
55
  }: React$1.ComponentProps<'legend'> & {
43
56
  variant?: 'legend' | 'label';
@@ -123,5 +136,5 @@ declare function FieldError({
123
136
  } | undefined>;
124
137
  }): import("react/jsx-runtime").JSX.Element | null;
125
138
  //#endregion
126
- export { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, type FieldProps, FieldSeparator, FieldSet, FieldSkeleton, FieldTitle, FieldValue, useFieldContext, useFieldControl };
139
+ export { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, type FieldProps, FieldSeparator, FieldSet, type FieldSetProps, FieldSkeleton, FieldTitle, FieldValue, useFieldContext, useFieldControl, useFieldSetInvalid };
127
140
  //# sourceMappingURL=field.d.cts.map
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { a as SparkInputStyle, i as SparkInputColor, o as SparkLabelPosition } from "../theme-types-Bn5E15-P.js";
3
+ import { c as SparkInputColor, l as SparkInputStyle, u as SparkLabelPosition } from "../theme-types-CKZzOmSa.js";
4
4
  import * as React$1 from "react";
5
5
 
6
6
  //#region src/components/field.d.ts
@@ -31,13 +31,26 @@ declare function useFieldControl(options?: {
31
31
  inputStyle: SparkInputStyle;
32
32
  inputColor: SparkInputColor;
33
33
  };
34
+ type FieldSetIndicator = 'required' | 'optional';
35
+ /** True when the nearest enclosing FieldSet has an error; ignores outer sets. */
36
+ declare function useFieldSetInvalid(): true | undefined;
37
+ interface FieldSetProps extends React$1.ComponentProps<'fieldset'> {
38
+ /** Marks the legend: `*` plus hidden text, or a visible "(optional)". */
39
+ indicator?: FieldSetIndicator;
40
+ /** Text for the mark; defaults to `'required'` or `'optional'`. */
41
+ indicatorLabel?: string;
42
+ }
34
43
  declare function FieldSet({
35
44
  className,
45
+ indicator,
46
+ indicatorLabel,
47
+ 'aria-describedby': ariaDescribedByProp,
36
48
  ...props
37
- }: React$1.ComponentProps<'fieldset'>): import("react/jsx-runtime").JSX.Element;
49
+ }: FieldSetProps): import("react/jsx-runtime").JSX.Element;
38
50
  declare function FieldLegend({
39
51
  className,
40
52
  variant,
53
+ children,
41
54
  ...props
42
55
  }: React$1.ComponentProps<'legend'> & {
43
56
  variant?: 'legend' | 'label';
@@ -123,5 +136,5 @@ declare function FieldError({
123
136
  } | undefined>;
124
137
  }): import("react/jsx-runtime").JSX.Element | null;
125
138
  //#endregion
126
- export { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, type FieldProps, FieldSeparator, FieldSet, FieldSkeleton, FieldTitle, FieldValue, useFieldContext, useFieldControl };
139
+ export { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, type FieldProps, FieldSeparator, FieldSet, type FieldSetProps, FieldSkeleton, FieldTitle, FieldValue, useFieldContext, useFieldControl, useFieldSetInvalid };
127
140
  //# sourceMappingURL=field.d.ts.map
@@ -2,11 +2,12 @@
2
2
  "use client";
3
3
  import { cn } from "../utils/cn.js";
4
4
  import { useTheme } from "./theme.js";
5
+ import { Callout } from "./callout.js";
5
6
  import { Separator } from "./separator.js";
6
7
  import { Skeleton } from "./skeleton.js";
7
8
  import * as React$1 from "react";
8
9
  import { useId } from "react";
9
- import { jsx, jsxs } from "react/jsx-runtime";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
11
  import { cva } from "class-variance-authority";
11
12
  //#region src/components/field.tsx
12
13
  const FieldContext = React$1.createContext(null);
@@ -33,19 +34,96 @@ function useFieldControl(options) {
33
34
  inputColor
34
35
  };
35
36
  }
36
- function FieldSet({ className, ...props }) {
37
- return /* @__PURE__ */ jsx("fieldset", {
38
- "data-slot": "field-set",
39
- className: cn("flex flex-col gap-[calc(var(--spark-layout-gap)*4)]", className),
40
- ...props
37
+ const FieldSetContext = React$1.createContext(null);
38
+ /** True when the nearest enclosing FieldSet has an error; ignores outer sets. */
39
+ function useFieldSetInvalid() {
40
+ return React$1.useContext(FieldSetContext)?.invalid || void 0;
41
+ }
42
+ function useSlotCount() {
43
+ const [count, setCount] = React$1.useState(0);
44
+ const setHas = React$1.useCallback((has) => setCount((n) => n + (has ? 1 : -1)), []);
45
+ return [count > 0, setHas];
46
+ }
47
+ function FieldSet({ className, indicator, indicatorLabel, "aria-describedby": ariaDescribedByProp, ...props }) {
48
+ const id = useId();
49
+ const [hasDescription, setHasDescription] = useSlotCount();
50
+ const [hasError, setHasError] = useSlotCount();
51
+ const [hasUnlinkedError, setHasUnlinkedError] = useSlotCount();
52
+ const invalid = hasError || hasUnlinkedError;
53
+ const fieldSetId = `field-set-${id}`;
54
+ const descriptionId = `${fieldSetId}-description`;
55
+ const errorId = `${fieldSetId}-error`;
56
+ const value = React$1.useMemo(() => ({
57
+ descriptionId,
58
+ hasDescription,
59
+ setHasDescription,
60
+ errorId,
61
+ hasError,
62
+ setHasError,
63
+ setHasUnlinkedError,
64
+ invalid,
65
+ indicator,
66
+ indicatorLabel
67
+ }), [
68
+ descriptionId,
69
+ hasDescription,
70
+ setHasDescription,
71
+ errorId,
72
+ hasError,
73
+ setHasError,
74
+ setHasUnlinkedError,
75
+ invalid,
76
+ indicator,
77
+ indicatorLabel
78
+ ]);
79
+ const ariaDescribedBy = [
80
+ hasError && errorId,
81
+ hasDescription && descriptionId,
82
+ ariaDescribedByProp
83
+ ].filter(Boolean).join(" ") || void 0;
84
+ return /* @__PURE__ */ jsx(FieldSetContext.Provider, {
85
+ value,
86
+ children: /* @__PURE__ */ jsx("fieldset", {
87
+ "data-slot": "field-set",
88
+ "data-invalid": invalid || void 0,
89
+ "aria-describedby": ariaDescribedBy,
90
+ className: cn("group/field-set flex flex-col gap-[calc(var(--spark-layout-gap)*4)]", className),
91
+ ...props
92
+ })
41
93
  });
42
94
  }
43
- function FieldLegend({ className, variant = "legend", ...props }) {
44
- return /* @__PURE__ */ jsx("legend", {
95
+ function FieldLegend({ className, variant = "legend", children, ...props }) {
96
+ const fieldSet = React$1.useContext(FieldSetContext);
97
+ const indicator = fieldSet?.indicator;
98
+ const indicatorLabel = fieldSet?.indicatorLabel ?? indicator;
99
+ return /* @__PURE__ */ jsxs("legend", {
100
+ "data-invalid": fieldSet?.invalid || void 0,
45
101
  "data-slot": "field-legend",
46
102
  "data-variant": variant,
47
- className: cn("mb-1.5 font-medium text-gray data-[variant=label]:text-sm data-[variant=legend]:text-base", className),
48
- ...props
103
+ className: cn("mb-1.5 font-medium text-gray data-[invalid=true]:text-error-active! data-[variant=label]:text-sm data-[variant=legend]:text-base", className),
104
+ ...props,
105
+ children: [
106
+ children,
107
+ indicator === "required" && /* @__PURE__ */ jsxs(Fragment, { children: [
108
+ " ",
109
+ /* @__PURE__ */ jsx("span", {
110
+ "aria-hidden": "true",
111
+ children: "*"
112
+ }),
113
+ /* @__PURE__ */ jsx("span", {
114
+ className: "sr-only",
115
+ children: indicatorLabel
116
+ })
117
+ ] }),
118
+ indicator === "optional" && /* @__PURE__ */ jsxs(Fragment, { children: [" ", /* @__PURE__ */ jsxs("span", {
119
+ className: "font-normal text-gray-light",
120
+ children: [
121
+ "(",
122
+ indicatorLabel,
123
+ ")"
124
+ ]
125
+ })] })
126
+ ]
49
127
  });
50
128
  }
51
129
  function FieldGroup({ className, ...props }) {
@@ -244,7 +322,9 @@ function FieldTitle({ className, ...props }) {
244
322
  }
245
323
  function FieldDescription({ className, id, ...props }) {
246
324
  const field = useFieldContext();
247
- const setHasDescription = field?.setHasDescription;
325
+ const fieldSet = React$1.useContext(FieldSetContext);
326
+ const owner = field ?? fieldSet;
327
+ const setHasDescription = owner?.setHasDescription;
248
328
  React$1.useEffect(() => {
249
329
  if (!setHasDescription) return;
250
330
  setHasDescription(true);
@@ -252,7 +332,7 @@ function FieldDescription({ className, id, ...props }) {
252
332
  }, [setHasDescription]);
253
333
  return /* @__PURE__ */ jsx("div", {
254
334
  "data-slot": "field-description",
255
- id: id ?? field?.descriptionId,
335
+ id: id ?? owner?.descriptionId,
256
336
  className: cn("text-sm leading-normal text-gray-light", className),
257
337
  ...props
258
338
  });
@@ -272,7 +352,8 @@ function FieldSeparator({ children, className, ...props }) {
272
352
  }
273
353
  function FieldError({ className, children, errors, id, ...props }) {
274
354
  const field = useFieldContext();
275
- const setHasError = field?.setHasError;
355
+ const fieldSet = React$1.useContext(FieldSetContext);
356
+ const setHasError = (field ?? fieldSet)?.setHasError;
276
357
  const content = React$1.useMemo(() => {
277
358
  if (children) return children;
278
359
  if (!errors?.length) return null;
@@ -285,16 +366,29 @@ function FieldError({ className, children, errors, id, ...props }) {
285
366
  }, [children, errors]);
286
367
  const hasContent = Boolean(content);
287
368
  const hasCustomId = id != null;
369
+ const setSetUnlinked = !field ? fieldSet?.setHasUnlinkedError : void 0;
288
370
  React$1.useEffect(() => {
289
- if (!setHasError || !hasContent || hasCustomId) return;
290
- setHasError(true);
291
- return () => setHasError(false);
371
+ if (!hasContent) return;
372
+ const set = hasCustomId ? setSetUnlinked : setHasError;
373
+ if (!set) return;
374
+ set(true);
375
+ return () => set(false);
292
376
  }, [
293
377
  setHasError,
378
+ setSetUnlinked,
294
379
  hasContent,
295
380
  hasCustomId
296
381
  ]);
297
382
  if (!content) return null;
383
+ if (!field && fieldSet) return /* @__PURE__ */ jsx(Callout, {
384
+ variant: "error",
385
+ role: "alert",
386
+ "data-slot": "field-error",
387
+ id: id ?? fieldSet.errorId,
388
+ className,
389
+ ...props,
390
+ children: content
391
+ });
298
392
  return /* @__PURE__ */ jsx("div", {
299
393
  role: "alert",
300
394
  "data-slot": "field-error",
@@ -305,6 +399,6 @@ function FieldError({ className, children, errors, id, ...props }) {
305
399
  });
306
400
  }
307
401
  //#endregion
308
- export { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldSkeleton, FieldTitle, FieldValue, useFieldContext, useFieldControl };
402
+ export { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldSkeleton, FieldTitle, FieldValue, useFieldContext, useFieldControl, useFieldSetInvalid };
309
403
 
310
404
  //# sourceMappingURL=field.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"field.js","names":["React"],"sources":["../../src/components/field.tsx"],"sourcesContent":["'use client'\n\nimport { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { useId } from 'react'\nimport type {\n SparkInputColor,\n SparkInputStyle,\n SparkLabelPosition,\n} from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { Separator } from './separator'\nimport { Skeleton } from './skeleton'\nimport { useTheme } from './theme'\n\ntype FieldContextValue = {\n id: string\n labelId: string\n descriptionId: string\n hasDescription: boolean\n setHasDescription: (has: boolean) => void\n errorId: string\n hasError: boolean\n setHasError: (has: boolean) => void\n readOnly: boolean\n labelPosition: SparkLabelPosition\n inputStyle: SparkInputStyle\n inputColor: SparkInputColor\n}\n\nconst FieldContext = React.createContext<FieldContextValue | null>(null)\n\nexport function useFieldContext() {\n return React.useContext(FieldContext)\n}\n\n// Field→Theme→default resolution + id/aria-describedby wiring, shared by every\n// control that can sit inside a Field (Input, SelectTrigger, …) so they can't\n// drift on how a style/color/id resolves. `variant` is the control's own\n// per-instance override (already normalized — e.g. Input maps its 'none' to\n// undefined before calling this).\nexport function useFieldControl(options?: {\n id?: string\n ariaDescribedBy?: string\n variant?: SparkInputStyle\n}) {\n const field = useFieldContext()\n const theme = useTheme()\n const id = options?.id ?? field?.id\n const errorId = field?.hasError ? field.errorId : undefined\n const descriptionId = field?.hasDescription ? field.descriptionId : undefined\n // error before hint: the actionable message should be announced first\n const ariaDescribedBy =\n [errorId, descriptionId, options?.ariaDescribedBy]\n .filter(Boolean)\n .join(' ') || undefined\n const inputStyle =\n options?.variant ?? field?.inputStyle ?? theme.inputStyle ?? 'soft'\n const inputColor = field?.inputColor ?? theme.inputColor ?? 'brand'\n const ariaInvalid = field?.hasError || undefined\n return { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor }\n}\n\nfunction FieldSet({ className, ...props }: React.ComponentProps<'fieldset'>) {\n return (\n <fieldset\n data-slot=\"field-set\"\n className={cn(\n 'flex flex-col gap-[calc(var(--spark-layout-gap)*4)]',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction FieldLegend({\n className,\n variant = 'legend',\n ...props\n}: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {\n return (\n <legend\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(\n 'mb-1.5 font-medium text-gray data-[variant=label]:text-sm data-[variant=legend]:text-base',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(\n 'group/field-group flex w-full flex-col gap-[calc(var(--spark-layout-gap)*4)]',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldVariants = cva(\n 'group/field relative flex w-full flex-col data-[invalid=true]:text-error-active',\n {\n variants: {\n labelPosition: {\n // inset puts label + input inside one shell, so the shell IS the input's box —\n // rounded-md tracks Input/Select/etc.'s own control radius, not the container one.\n // focus inside inset is the classic underline cue, not a shell ring\n inset: 'gap-y-1 rounded-md px-4 py-3',\n outset: 'gap-y-2',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n labelPosition: 'inset',\n inputStyle: 'soft',\n inputColor: 'brand',\n class:\n 'bg-input-base has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-input-base-hover',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'soft',\n inputColor: 'gray',\n class:\n 'bg-base-gray-soft has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-gray-a4',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: 'border border-input bg-base-accent-surface',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: 'border border-input-gray bg-base-gray-surface',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: 'border border-input',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: 'border border-input-gray',\n },\n // ghost inset = bare label + underlined input; the input carries the line\n {\n labelPosition: 'inset',\n inputStyle: 'ghost',\n class: 'rounded-none px-0',\n },\n // shared inset states: filled shells swap bg, bordered shells swap border\n // invalid hover deepens the error tint instead of falling back to accent\n {\n labelPosition: 'inset',\n inputStyle: ['soft', 'surface'],\n class:\n 'data-[disabled=true]:bg-base-gray-soft data-[invalid=true]:bg-base-error-soft data-[invalid=true]:has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-red-a4',\n },\n {\n labelPosition: 'inset',\n inputStyle: ['surface', 'outlined'],\n class:\n 'data-[disabled=true]:border-input-disabled data-[invalid=true]:border-input-invalid',\n },\n ],\n },\n)\n\ninterface FieldProps extends React.ComponentProps<'div'> {\n labelPosition?: SparkLabelPosition\n inputStyle?: SparkInputStyle\n inputColor?: SparkInputColor\n readOnly?: boolean\n 'data-invalid'?: boolean | 'true' | 'false'\n}\n\nfunction Field({\n className,\n children,\n labelPosition: labelPositionProp,\n inputStyle: inputStyleProp,\n inputColor: inputColorProp,\n readOnly = false,\n 'data-invalid': dataInvalid,\n ...props\n}: FieldProps) {\n const id = useId()\n const [descriptionCount, setDescriptionCount] = React.useState(0)\n const [errorCount, setErrorCount] = React.useState(0)\n // counts, not flags: one slot unmounting must not clear what siblings still need\n const setHasDescription = React.useCallback(\n (has: boolean) => setDescriptionCount((n) => n + (has ? 1 : -1)),\n [],\n )\n const setHasError = React.useCallback(\n (has: boolean) => setErrorCount((n) => n + (has ? 1 : -1)),\n [],\n )\n const hasDescription = descriptionCount > 0\n const hasError = errorCount > 0\n // knobs are structural (they move the label / pick the box carrier), so they\n // resolve here via the <Theme> context; props override per instance\n const theme = useTheme()\n const labelPosition = labelPositionProp ?? theme.labelPosition ?? 'inset'\n const inputStyle = inputStyleProp ?? theme.inputStyle ?? 'soft'\n const inputColor = inputColorProp ?? theme.inputColor ?? 'brand'\n const fieldId = `field-${id}`\n const labelId = `${fieldId}-label`\n const value = React.useMemo<FieldContextValue>(\n () => ({\n id: fieldId,\n labelId,\n descriptionId: `${fieldId}-description`,\n hasDescription,\n setHasDescription,\n errorId: `${fieldId}-error`,\n hasError,\n setHasError,\n readOnly,\n labelPosition,\n inputStyle,\n inputColor,\n }),\n [\n fieldId,\n labelId,\n hasDescription,\n setHasDescription,\n hasError,\n setHasError,\n readOnly,\n labelPosition,\n inputStyle,\n inputColor,\n ],\n )\n\n return (\n <FieldContext.Provider value={value}>\n <div\n role=\"group\"\n data-slot=\"field\"\n data-label-position={labelPosition}\n data-input-style={inputStyle}\n data-input-color={inputColor}\n data-invalid={dataInvalid ?? (hasError || undefined)}\n aria-labelledby={readOnly ? labelId : undefined}\n className={cn(\n fieldVariants({ labelPosition, inputStyle, inputColor }),\n className,\n )}\n {...props}\n >\n {children}\n </div>\n </FieldContext.Provider>\n )\n}\n\nfunction FieldContent({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-content\"\n className={cn(\n 'flex min-w-0 flex-1 flex-col gap-1 leading-snug',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldLabelClassName =\n 'text-sm leading-5 font-normal text-gray transition-all group-data-[invalid=true]/field:text-error-active! motion-reduce:transition-none'\n\nfunction FieldLabel({\n className,\n htmlFor,\n srOnly,\n children,\n ...props\n}: React.ComponentProps<'label'> & { srOnly?: boolean }) {\n const field = useFieldContext()\n const classNames = cn(\n // labels stay full-contrast even when disabled — the control itself\n // (dimmed text, gray shell) signals the state\n fieldLabelClassName,\n // visually hidden but kept in the a11y tree (still names + focuses the control)\n srOnly && 'sr-only',\n className,\n )\n\n if (field?.readOnly) {\n return (\n <div data-slot=\"field-label\" id={field.labelId} className={classNames}>\n {children}\n </div>\n )\n }\n\n return (\n <label\n data-slot=\"field-label\"\n className={classNames}\n htmlFor={htmlFor ?? field?.id}\n {...props}\n >\n {children}\n </label>\n )\n}\n\nfunction FieldValue({\n className,\n id,\n title,\n truncate,\n children,\n ...props\n}: React.ComponentProps<'div'> & { truncate?: boolean }) {\n return (\n <div\n data-slot=\"field-value\"\n id={id}\n // truncated string values keep the full text reachable on hover\n title={\n title ??\n (truncate && typeof children === 'string' ? children : undefined)\n }\n className={cn(\n // values are content, not controls — disabled dims but stays ≥4.5:1\n 'min-h-8 w-full text-base text-gray tabular-nums group-data-[disabled=true]/field:opacity-70',\n truncate\n ? 'block truncate leading-8'\n : 'inline-flex items-center wrap-anywhere whitespace-pre-wrap',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nfunction FieldSkeleton({\n className,\n labelClassName,\n valueClassName,\n truncate,\n ...props\n}: React.ComponentProps<'div'> & {\n truncate?: boolean\n labelClassName?: string\n valueClassName?: string\n}) {\n return (\n <div\n data-slot=\"field-skeleton\"\n aria-hidden=\"true\"\n className={cn('flex w-full flex-col gap-y-1', className)}\n {...props}\n >\n <Skeleton\n data-slot=\"field-skeleton-label\"\n className={cn('h-5 w-24', labelClassName)}\n />\n <Skeleton\n data-slot=\"field-skeleton-value\"\n className={cn(\n 'h-8 min-h-8 w-full',\n truncate && 'max-w-full truncate',\n valueClassName,\n )}\n />\n </div>\n )\n}\n\nfunction FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-title\"\n className={cn(\n 'flex w-fit items-center gap-2 text-sm font-medium text-gray',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction FieldDescription({\n className,\n id,\n ...props\n}: React.ComponentProps<'div'>) {\n const field = useFieldContext()\n const setHasDescription = field?.setHasDescription\n\n React.useEffect(() => {\n if (!setHasDescription) return\n setHasDescription(true)\n return () => setHasDescription(false)\n }, [setHasDescription])\n\n return (\n <div\n data-slot=\"field-description\"\n id={id ?? field?.descriptionId}\n className={cn('text-sm leading-normal text-gray-light', className)}\n {...props}\n />\n )\n}\n\nfunction FieldSeparator({\n children,\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-separator\"\n data-content={!!children}\n className={cn('relative -my-2 h-5 text-sm', className)}\n {...props}\n >\n <Separator className=\"absolute inset-0 top-1/2\" />\n {children && (\n <span\n className=\"relative mx-auto block w-fit bg-(--spark-background) px-2 text-gray-light\"\n data-slot=\"field-separator-content\"\n >\n {children}\n </span>\n )}\n </div>\n )\n}\n\nfunction FieldError({\n className,\n children,\n errors,\n id,\n ...props\n}: React.ComponentProps<'div'> & {\n errors?: Array<{ message?: string } | undefined>\n}) {\n const field = useFieldContext()\n const setHasError = field?.setHasError\n const content = React.useMemo(() => {\n if (children) return children\n if (!errors?.length) return null\n const unique = [\n ...new Map(errors.map((error) => [error?.message, error])).values(),\n ]\n if (unique.length === 1) return unique[0]?.message\n return (\n <ul className=\"ml-4 flex list-disc flex-col gap-1\">\n {unique.map(\n (error) =>\n error?.message && <li key={error.message}>{error.message}</li>,\n )}\n </ul>\n )\n }, [children, errors])\n\n const hasContent = Boolean(content)\n // a custom id means no element carries field.errorId, so don't advertise it\n const hasCustomId = id != null\n\n // must run before the empty-content early return to keep hook order stable\n React.useEffect(() => {\n if (!setHasError || !hasContent || hasCustomId) return\n setHasError(true)\n return () => setHasError(false)\n }, [setHasError, hasContent, hasCustomId])\n\n if (!content) return null\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n id={id ?? field?.errorId}\n className={cn('text-sm font-normal text-error', className)}\n {...props}\n >\n {content}\n </div>\n )\n}\n\nexport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldLegend,\n FieldSeparator,\n FieldSet,\n FieldSkeleton,\n FieldTitle,\n FieldValue,\n}\nexport type { FieldProps }\n"],"mappings":"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"}
1
+ {"version":3,"file":"field.js","names":["React"],"sources":["../../src/components/field.tsx"],"sourcesContent":["'use client'\n\nimport { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { useId } from 'react'\nimport type {\n SparkInputColor,\n SparkInputStyle,\n SparkLabelPosition,\n} from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { Callout } from './callout'\nimport { Separator } from './separator'\nimport { Skeleton } from './skeleton'\nimport { useTheme } from './theme'\n\ntype FieldContextValue = {\n id: string\n labelId: string\n descriptionId: string\n hasDescription: boolean\n setHasDescription: (has: boolean) => void\n errorId: string\n hasError: boolean\n setHasError: (has: boolean) => void\n readOnly: boolean\n labelPosition: SparkLabelPosition\n inputStyle: SparkInputStyle\n inputColor: SparkInputColor\n}\n\nconst FieldContext = React.createContext<FieldContextValue | null>(null)\n\nexport function useFieldContext() {\n return React.useContext(FieldContext)\n}\n\n// Field→Theme→default resolution + id/aria-describedby wiring, shared by every\n// control that can sit inside a Field (Input, SelectTrigger, …) so they can't\n// drift on how a style/color/id resolves. `variant` is the control's own\n// per-instance override (already normalized — e.g. Input maps its 'none' to\n// undefined before calling this).\nexport function useFieldControl(options?: {\n id?: string\n ariaDescribedBy?: string\n variant?: SparkInputStyle\n}) {\n const field = useFieldContext()\n const theme = useTheme()\n const id = options?.id ?? field?.id\n const errorId = field?.hasError ? field.errorId : undefined\n const descriptionId = field?.hasDescription ? field.descriptionId : undefined\n // error before hint: the actionable message should be announced first\n const ariaDescribedBy =\n [errorId, descriptionId, options?.ariaDescribedBy]\n .filter(Boolean)\n .join(' ') || undefined\n const inputStyle =\n options?.variant ?? field?.inputStyle ?? theme.inputStyle ?? 'soft'\n const inputColor = field?.inputColor ?? theme.inputColor ?? 'brand'\n const ariaInvalid = field?.hasError || undefined\n return { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor }\n}\n\ntype FieldSetIndicator = 'required' | 'optional'\n\ntype FieldSetContextValue = {\n descriptionId: string\n hasDescription: boolean\n setHasDescription: (has: boolean) => void\n errorId: string\n hasError: boolean\n setHasError: (has: boolean) => void\n /** Marks the set invalid without linking `errorId` (error has a custom id). */\n setHasUnlinkedError: (has: boolean) => void\n invalid: boolean\n indicator?: FieldSetIndicator\n indicatorLabel?: string\n}\n\nconst FieldSetContext = React.createContext<FieldSetContextValue | null>(null)\n\n/** True when the nearest enclosing FieldSet has an error; ignores outer sets. */\nfunction useFieldSetInvalid() {\n return React.useContext(FieldSetContext)?.invalid || undefined\n}\n\nfunction useSlotCount() {\n const [count, setCount] = React.useState(0)\n const setHas = React.useCallback(\n (has: boolean) => setCount((n) => n + (has ? 1 : -1)),\n [],\n )\n return [count > 0, setHas] as const\n}\n\ninterface FieldSetProps extends React.ComponentProps<'fieldset'> {\n /** Marks the legend: `*` plus hidden text, or a visible \"(optional)\". */\n indicator?: FieldSetIndicator\n /** Text for the mark; defaults to `'required'` or `'optional'`. */\n indicatorLabel?: string\n}\n\nfunction FieldSet({\n className,\n indicator,\n indicatorLabel,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n}: FieldSetProps) {\n const id = useId()\n const [hasDescription, setHasDescription] = useSlotCount()\n const [hasError, setHasError] = useSlotCount()\n const [hasUnlinkedError, setHasUnlinkedError] = useSlotCount()\n const invalid = hasError || hasUnlinkedError\n const fieldSetId = `field-set-${id}`\n const descriptionId = `${fieldSetId}-description`\n const errorId = `${fieldSetId}-error`\n const value = React.useMemo<FieldSetContextValue>(\n () => ({\n descriptionId,\n hasDescription,\n setHasDescription,\n errorId,\n hasError,\n setHasError,\n setHasUnlinkedError,\n invalid,\n indicator,\n indicatorLabel,\n }),\n [\n descriptionId,\n hasDescription,\n setHasDescription,\n errorId,\n hasError,\n setHasError,\n setHasUnlinkedError,\n invalid,\n indicator,\n indicatorLabel,\n ],\n )\n const ariaDescribedBy =\n [hasError && errorId, hasDescription && descriptionId, ariaDescribedByProp]\n .filter(Boolean)\n .join(' ') || undefined\n\n return (\n <FieldSetContext.Provider value={value}>\n <fieldset\n data-slot=\"field-set\"\n data-invalid={invalid || undefined}\n aria-describedby={ariaDescribedBy}\n className={cn(\n 'group/field-set flex flex-col gap-[calc(var(--spark-layout-gap)*4)]',\n className,\n )}\n {...props}\n />\n </FieldSetContext.Provider>\n )\n}\n\nfunction FieldLegend({\n className,\n variant = 'legend',\n children,\n ...props\n}: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {\n const fieldSet = React.useContext(FieldSetContext)\n const indicator = fieldSet?.indicator\n const indicatorLabel = fieldSet?.indicatorLabel ?? indicator\n return (\n <legend\n data-invalid={fieldSet?.invalid || undefined}\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(\n 'mb-1.5 font-medium text-gray data-[invalid=true]:text-error-active! data-[variant=label]:text-sm data-[variant=legend]:text-base',\n className,\n )}\n {...props}\n >\n {children}\n {indicator === 'required' && (\n <>\n {' '}\n <span aria-hidden=\"true\">*</span>\n <span className=\"sr-only\">{indicatorLabel}</span>\n </>\n )}\n {indicator === 'optional' && (\n <>\n {' '}\n <span className=\"font-normal text-gray-light\">\n ({indicatorLabel})\n </span>\n </>\n )}\n </legend>\n )\n}\n\nfunction FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(\n 'group/field-group flex w-full flex-col gap-[calc(var(--spark-layout-gap)*4)]',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldVariants = cva(\n 'group/field relative flex w-full flex-col data-[invalid=true]:text-error-active',\n {\n variants: {\n labelPosition: {\n // inset puts label + input inside one shell, so the shell IS the input's box —\n // rounded-md tracks Input/Select/etc.'s own control radius, not the container one.\n // focus inside inset is the classic underline cue, not a shell ring\n inset: 'gap-y-1 rounded-md px-4 py-3',\n outset: 'gap-y-2',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n labelPosition: 'inset',\n inputStyle: 'soft',\n inputColor: 'brand',\n class:\n 'bg-input-base has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-input-base-hover',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'soft',\n inputColor: 'gray',\n class:\n 'bg-base-gray-soft has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-gray-a4',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: 'border border-input bg-base-accent-surface',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: 'border border-input-gray bg-base-gray-surface',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: 'border border-input',\n },\n {\n labelPosition: 'inset',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: 'border border-input-gray',\n },\n // ghost inset = bare label + underlined input; the input carries the line\n {\n labelPosition: 'inset',\n inputStyle: 'ghost',\n class: 'rounded-none px-0',\n },\n // shared inset states: filled shells swap bg, bordered shells swap border\n // invalid hover deepens the error tint instead of falling back to accent\n {\n labelPosition: 'inset',\n inputStyle: ['soft', 'surface'],\n class:\n 'data-[disabled=true]:bg-base-gray-soft data-[invalid=true]:bg-base-error-soft data-[invalid=true]:has-[:is(input,textarea,[data-slot=select-trigger]):not(:disabled)]:hover:bg-red-a4',\n },\n {\n labelPosition: 'inset',\n inputStyle: ['surface', 'outlined'],\n class:\n 'data-[disabled=true]:border-input-disabled data-[invalid=true]:border-input-invalid',\n },\n ],\n },\n)\n\ninterface FieldProps extends React.ComponentProps<'div'> {\n labelPosition?: SparkLabelPosition\n inputStyle?: SparkInputStyle\n inputColor?: SparkInputColor\n readOnly?: boolean\n 'data-invalid'?: boolean | 'true' | 'false'\n}\n\nfunction Field({\n className,\n children,\n labelPosition: labelPositionProp,\n inputStyle: inputStyleProp,\n inputColor: inputColorProp,\n readOnly = false,\n 'data-invalid': dataInvalid,\n ...props\n}: FieldProps) {\n const id = useId()\n const [descriptionCount, setDescriptionCount] = React.useState(0)\n const [errorCount, setErrorCount] = React.useState(0)\n // counts, not flags: one slot unmounting must not clear what siblings still need\n const setHasDescription = React.useCallback(\n (has: boolean) => setDescriptionCount((n) => n + (has ? 1 : -1)),\n [],\n )\n const setHasError = React.useCallback(\n (has: boolean) => setErrorCount((n) => n + (has ? 1 : -1)),\n [],\n )\n const hasDescription = descriptionCount > 0\n const hasError = errorCount > 0\n // knobs are structural (they move the label / pick the box carrier), so they\n // resolve here via the <Theme> context; props override per instance\n const theme = useTheme()\n const labelPosition = labelPositionProp ?? theme.labelPosition ?? 'inset'\n const inputStyle = inputStyleProp ?? theme.inputStyle ?? 'soft'\n const inputColor = inputColorProp ?? theme.inputColor ?? 'brand'\n const fieldId = `field-${id}`\n const labelId = `${fieldId}-label`\n const value = React.useMemo<FieldContextValue>(\n () => ({\n id: fieldId,\n labelId,\n descriptionId: `${fieldId}-description`,\n hasDescription,\n setHasDescription,\n errorId: `${fieldId}-error`,\n hasError,\n setHasError,\n readOnly,\n labelPosition,\n inputStyle,\n inputColor,\n }),\n [\n fieldId,\n labelId,\n hasDescription,\n setHasDescription,\n hasError,\n setHasError,\n readOnly,\n labelPosition,\n inputStyle,\n inputColor,\n ],\n )\n\n return (\n <FieldContext.Provider value={value}>\n <div\n role=\"group\"\n data-slot=\"field\"\n data-label-position={labelPosition}\n data-input-style={inputStyle}\n data-input-color={inputColor}\n data-invalid={dataInvalid ?? (hasError || undefined)}\n aria-labelledby={readOnly ? labelId : undefined}\n className={cn(\n fieldVariants({ labelPosition, inputStyle, inputColor }),\n className,\n )}\n {...props}\n >\n {children}\n </div>\n </FieldContext.Provider>\n )\n}\n\nfunction FieldContent({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-content\"\n className={cn(\n 'flex min-w-0 flex-1 flex-col gap-1 leading-snug',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldLabelClassName =\n 'text-sm leading-5 font-normal text-gray transition-all group-data-[invalid=true]/field:text-error-active! motion-reduce:transition-none'\n\nfunction FieldLabel({\n className,\n htmlFor,\n srOnly,\n children,\n ...props\n}: React.ComponentProps<'label'> & { srOnly?: boolean }) {\n const field = useFieldContext()\n const classNames = cn(\n // labels stay full-contrast even when disabled — the control itself\n // (dimmed text, gray shell) signals the state\n fieldLabelClassName,\n // visually hidden but kept in the a11y tree (still names + focuses the control)\n srOnly && 'sr-only',\n className,\n )\n\n if (field?.readOnly) {\n return (\n <div data-slot=\"field-label\" id={field.labelId} className={classNames}>\n {children}\n </div>\n )\n }\n\n return (\n <label\n data-slot=\"field-label\"\n className={classNames}\n htmlFor={htmlFor ?? field?.id}\n {...props}\n >\n {children}\n </label>\n )\n}\n\nfunction FieldValue({\n className,\n id,\n title,\n truncate,\n children,\n ...props\n}: React.ComponentProps<'div'> & { truncate?: boolean }) {\n return (\n <div\n data-slot=\"field-value\"\n id={id}\n // truncated string values keep the full text reachable on hover\n title={\n title ??\n (truncate && typeof children === 'string' ? children : undefined)\n }\n className={cn(\n // values are content, not controls — disabled dims but stays ≥4.5:1\n 'min-h-8 w-full text-base text-gray tabular-nums group-data-[disabled=true]/field:opacity-70',\n truncate\n ? 'block truncate leading-8'\n : 'inline-flex items-center wrap-anywhere whitespace-pre-wrap',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n )\n}\n\nfunction FieldSkeleton({\n className,\n labelClassName,\n valueClassName,\n truncate,\n ...props\n}: React.ComponentProps<'div'> & {\n truncate?: boolean\n labelClassName?: string\n valueClassName?: string\n}) {\n return (\n <div\n data-slot=\"field-skeleton\"\n aria-hidden=\"true\"\n className={cn('flex w-full flex-col gap-y-1', className)}\n {...props}\n >\n <Skeleton\n data-slot=\"field-skeleton-label\"\n className={cn('h-5 w-24', labelClassName)}\n />\n <Skeleton\n data-slot=\"field-skeleton-value\"\n className={cn(\n 'h-8 min-h-8 w-full',\n truncate && 'max-w-full truncate',\n valueClassName,\n )}\n />\n </div>\n )\n}\n\nfunction FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-title\"\n className={cn(\n 'flex w-fit items-center gap-2 text-sm font-medium text-gray',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction FieldDescription({\n className,\n id,\n ...props\n}: React.ComponentProps<'div'>) {\n const field = useFieldContext()\n const fieldSet = React.useContext(FieldSetContext)\n const owner = field ?? fieldSet\n const setHasDescription = owner?.setHasDescription\n\n React.useEffect(() => {\n if (!setHasDescription) return\n setHasDescription(true)\n return () => setHasDescription(false)\n }, [setHasDescription])\n\n return (\n <div\n data-slot=\"field-description\"\n id={id ?? owner?.descriptionId}\n className={cn('text-sm leading-normal text-gray-light', className)}\n {...props}\n />\n )\n}\n\nfunction FieldSeparator({\n children,\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"field-separator\"\n data-content={!!children}\n className={cn('relative -my-2 h-5 text-sm', className)}\n {...props}\n >\n <Separator className=\"absolute inset-0 top-1/2\" />\n {children && (\n <span\n className=\"relative mx-auto block w-fit bg-(--spark-background) px-2 text-gray-light\"\n data-slot=\"field-separator-content\"\n >\n {children}\n </span>\n )}\n </div>\n )\n}\n\nfunction FieldError({\n className,\n children,\n errors,\n id,\n ...props\n}: React.ComponentProps<'div'> & {\n errors?: Array<{ message?: string } | undefined>\n}) {\n const field = useFieldContext()\n const fieldSet = React.useContext(FieldSetContext)\n const owner = field ?? fieldSet\n const setHasError = owner?.setHasError\n const content = React.useMemo(() => {\n if (children) return children\n if (!errors?.length) return null\n const unique = [\n ...new Map(errors.map((error) => [error?.message, error])).values(),\n ]\n if (unique.length === 1) return unique[0]?.message\n return (\n <ul className=\"ml-4 flex list-disc flex-col gap-1\">\n {unique.map(\n (error) =>\n error?.message && <li key={error.message}>{error.message}</li>,\n )}\n </ul>\n )\n }, [children, errors])\n\n const hasContent = Boolean(content)\n // a custom id means no element carries owner.errorId, so don't advertise it\n const hasCustomId = id != null\n const setSetUnlinked = !field ? fieldSet?.setHasUnlinkedError : undefined\n\n // must run before the empty-content early return to keep hook order stable\n React.useEffect(() => {\n if (!hasContent) return\n const set = hasCustomId ? setSetUnlinked : setHasError\n if (!set) return\n set(true)\n return () => set(false)\n }, [setHasError, setSetUnlinked, hasContent, hasCustomId])\n\n if (!content) return null\n\n if (!field && fieldSet) {\n // straight into Callout: CalloutDescription's <p> cannot hold the <ul> above\n return (\n <Callout\n variant=\"error\"\n role=\"alert\"\n data-slot=\"field-error\"\n id={id ?? fieldSet.errorId}\n className={className}\n {...props}\n >\n {content}\n </Callout>\n )\n }\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n id={id ?? field?.errorId}\n className={cn('text-sm font-normal text-error', className)}\n {...props}\n >\n {content}\n </div>\n )\n}\n\nexport {\n useFieldSetInvalid,\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldLegend,\n FieldSeparator,\n FieldSet,\n FieldSkeleton,\n FieldTitle,\n FieldValue,\n}\nexport type { FieldProps, FieldSetProps }\n"],"mappings":"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"}
@@ -11,7 +11,7 @@ let react_jsx_runtime = require("react/jsx-runtime");
11
11
  let class_variance_authority = require("class-variance-authority");
12
12
  let cn = require("cn");
13
13
  //#region src/components/input-group.tsx
14
- const GHOST = "rounded-none border-0 border-b-2 px-0 focus-within:border-b-input-active has-[[data-slot=input][aria-invalid=true]]:border-b-input-invalid has-[[data-slot=input][aria-invalid=true]]:focus-within:border-b-red-a8! group-data-[invalid=true]/field:border-b-input-invalid! group-data-[invalid=true]/field:focus-within:border-b-red-a8!";
14
+ const GHOST = "rounded-none border-0 border-b-2 px-0 has-[:focus-visible]:border-b-input-active has-[[data-slot=input][aria-invalid=true]]:border-b-input-invalid has-[[data-slot=input][aria-invalid=true]]:has-[:focus-visible]:border-b-red-a8! group-data-[invalid=true]/field:border-b-input-invalid! group-data-[invalid=true]/field:has-[:focus-visible]:border-b-red-a8!";
15
15
  const inputGroupVariants = (0, class_variance_authority.cva)("group/input-group relative flex h-10 w-full min-w-0 items-center gap-2 bg-transparent transition-[color,border-color] outline-none motion-reduce:transition-none", {
16
16
  variants: {
17
17
  chrome: {
@@ -33,48 +33,48 @@ const inputGroupVariants = (0, class_variance_authority.cva)("group/input-group
33
33
  {
34
34
  chrome: "box",
35
35
  inputStyle: "soft",
36
- class: "group-data-[invalid=true]/field:bg-base-error-soft focus-within:ring-2 focus-within:ring-offset-2 group-data-[invalid=true]/field:focus-within:ring-red-a8 has-[[data-slot=input][aria-invalid=true]]:bg-base-error-soft has-[[data-slot=input][aria-invalid=true]]:focus-within:ring-red-a8 has-[[data-slot=input][aria-invalid=true]:not(:disabled)]:hover:bg-red-a4 group-data-[invalid=true]/field:has-[input:not(:disabled)]:hover:bg-red-a4"
36
+ class: "group-data-[invalid=true]/field:bg-base-error-soft has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-offset-2 group-data-[invalid=true]/field:has-[:focus-visible]:ring-(--color-error-active) has-[[data-slot=input][aria-invalid=true]]:bg-base-error-soft has-[[data-slot=input][aria-invalid=true]]:has-[:focus-visible]:ring-(--color-error-active) has-[[data-slot=input][aria-invalid=true]:not(:disabled)]:hover:bg-red-a4 group-data-[invalid=true]/field:has-[input:not(:disabled)]:hover:bg-red-a4"
37
37
  },
38
38
  {
39
39
  chrome: "box",
40
40
  inputStyle: "soft",
41
41
  inputColor: "brand",
42
- class: "bg-input-base focus-within:ring-input-active has-[input:not(:disabled)]:hover:bg-input-base-hover"
42
+ class: "bg-input-base has-[:focus-visible]:ring-(--color-focus-root) has-[input:not(:disabled)]:hover:bg-input-base-hover"
43
43
  },
44
44
  {
45
45
  chrome: "box",
46
46
  inputStyle: "soft",
47
47
  inputColor: "gray",
48
- class: "bg-base-gray-soft focus-within:ring-gray-a8 has-[input:not(:disabled)]:hover:bg-gray-a4"
48
+ class: "bg-base-gray-soft has-[:focus-visible]:ring-(--color-focus-root) has-[input:not(:disabled)]:hover:bg-gray-a4"
49
49
  },
50
50
  {
51
51
  chrome: "box",
52
52
  inputStyle: ["surface", "outlined"],
53
- class: "border group-data-[invalid=true]/field:border-input-invalid focus-within:ring-1 group-data-[invalid=true]/field:focus-within:border-input-invalid! group-data-[invalid=true]/field:focus-within:ring-red-a4! has-[[data-slot=input][aria-invalid=true]]:border-input-invalid has-[[data-slot=input][aria-invalid=true]]:focus-within:border-input-invalid! has-[[data-slot=input][aria-invalid=true]]:focus-within:ring-red-a4!"
53
+ class: "border group-data-[invalid=true]/field:border-input-invalid has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-offset-2 group-data-[invalid=true]/field:has-[:focus-visible]:border-input-invalid! group-data-[invalid=true]/field:has-[:focus-visible]:ring-(--color-error-active)! has-[[data-slot=input][aria-invalid=true]]:border-input-invalid has-[[data-slot=input][aria-invalid=true]]:has-[:focus-visible]:border-input-invalid! has-[[data-slot=input][aria-invalid=true]]:has-[:focus-visible]:ring-(--color-error-active)!"
54
54
  },
55
55
  {
56
56
  chrome: "box",
57
57
  inputStyle: "surface",
58
58
  inputColor: "brand",
59
- class: "border-input bg-base-accent-surface focus-within:border-input-active focus-within:ring-spark-a4"
59
+ class: "border-input bg-base-accent-surface has-[:focus-visible]:border-input-active has-[:focus-visible]:ring-(--color-focus-root)"
60
60
  },
61
61
  {
62
62
  chrome: "box",
63
63
  inputStyle: "surface",
64
64
  inputColor: "gray",
65
- class: "border-input-gray bg-base-gray-surface focus-within:border-gray-a8 focus-within:ring-gray-a4"
65
+ class: "border-input-gray bg-base-gray-surface has-[:focus-visible]:border-gray-a8 has-[:focus-visible]:ring-(--color-focus-root)"
66
66
  },
67
67
  {
68
68
  chrome: "box",
69
69
  inputStyle: "outlined",
70
70
  inputColor: "brand",
71
- class: "border-input focus-within:border-input-active focus-within:ring-spark-a4"
71
+ class: "border-input has-[:focus-visible]:border-input-active has-[:focus-visible]:ring-(--color-focus-root)"
72
72
  },
73
73
  {
74
74
  chrome: "box",
75
75
  inputStyle: "outlined",
76
76
  inputColor: "gray",
77
- class: "border-input-gray focus-within:border-gray-a8 focus-within:ring-gray-a4"
77
+ class: "border-input-gray has-[:focus-visible]:border-gray-a8 has-[:focus-visible]:ring-(--color-focus-root)"
78
78
  },
79
79
  {
80
80
  chrome: "box",