@epilot/spark-ui 0.15.1 → 0.15.2

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 (35) hide show
  1. package/dist/components/bubble.d.cts +1 -1
  2. package/dist/components/bubble.d.ts +1 -1
  3. package/dist/components/button.d.cts +3 -3
  4. package/dist/components/button.d.ts +3 -3
  5. package/dist/components/callout.d.cts +1 -1
  6. package/dist/components/callout.d.ts +1 -1
  7. package/dist/components/field.cjs +42 -7
  8. package/dist/components/field.cjs.map +1 -1
  9. package/dist/components/field.d.cts +7 -0
  10. package/dist/components/field.d.ts +7 -0
  11. package/dist/components/field.js +42 -7
  12. package/dist/components/field.js.map +1 -1
  13. package/dist/components/info-block.d.cts +1 -1
  14. package/dist/components/info-block.d.ts +1 -1
  15. package/dist/components/input-group.d.cts +1 -1
  16. package/dist/components/input-group.d.ts +1 -1
  17. package/dist/components/input-otp.cjs +2 -1
  18. package/dist/components/input-otp.cjs.map +1 -1
  19. package/dist/components/input-otp.js +2 -1
  20. package/dist/components/input-otp.js.map +1 -1
  21. package/dist/components/input.cjs +2 -1
  22. package/dist/components/input.cjs.map +1 -1
  23. package/dist/components/input.js +2 -1
  24. package/dist/components/input.js.map +1 -1
  25. package/dist/components/marker.d.cts +1 -1
  26. package/dist/components/marker.d.ts +1 -1
  27. package/dist/components/select.cjs +2 -1
  28. package/dist/components/select.cjs.map +1 -1
  29. package/dist/components/select.js +2 -1
  30. package/dist/components/select.js.map +1 -1
  31. package/dist/components/textarea.cjs +2 -1
  32. package/dist/components/textarea.cjs.map +1 -1
  33. package/dist/components/textarea.js +2 -1
  34. package/dist/components/textarea.js.map +1 -1
  35. package/package.json +1 -1
@@ -10,7 +10,7 @@ declare function BubbleGroup({
10
10
  ...props
11
11
  }: React$1.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
12
12
  declare const bubbleVariants: (props?: ({
13
- variant?: "secondary" | "destructive" | "default" | "ghost" | "outline" | "muted" | "tinted" | null | undefined;
13
+ variant?: "default" | "secondary" | "muted" | "tinted" | "outline" | "ghost" | "destructive" | null | undefined;
14
14
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
15
15
  declare function Bubble({
16
16
  variant,
@@ -10,7 +10,7 @@ declare function BubbleGroup({
10
10
  ...props
11
11
  }: React$1.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
12
12
  declare const bubbleVariants: (props?: ({
13
- variant?: "secondary" | "destructive" | "default" | "ghost" | "outline" | "muted" | "tinted" | null | undefined;
13
+ variant?: "default" | "secondary" | "muted" | "tinted" | "outline" | "ghost" | "destructive" | null | undefined;
14
14
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
15
15
  declare function Bubble({
16
16
  variant,
@@ -7,9 +7,9 @@ import { VariantProps } from "class-variance-authority";
7
7
 
8
8
  //#region src/components/button.d.ts
9
9
  declare const buttonVariants: (props?: ({
10
- variant?: "primary" | "secondary" | "tertiary" | "bare" | "destructive" | null | undefined;
11
- color?: "accent" | "neutral" | "danger" | null | undefined;
12
- size?: "small" | "default" | "medium" | "icon-small" | "icon" | "icon-medium" | null | undefined;
10
+ variant?: "secondary" | "destructive" | "primary" | "tertiary" | "bare" | null | undefined;
11
+ color?: "neutral" | "accent" | "danger" | null | undefined;
12
+ size?: "small" | "icon" | "default" | "medium" | "icon-small" | "icon-medium" | null | undefined;
13
13
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
14
14
  type SparkButtonColor = 'accent' | 'neutral' | 'danger';
15
15
  interface ButtonProps extends Omit<React$1.ComponentProps<typeof Button$1>, 'ref'>, Omit<VariantProps<typeof buttonVariants>, 'variant' | 'color'> {
@@ -7,9 +7,9 @@ import { VariantProps } from "class-variance-authority";
7
7
 
8
8
  //#region src/components/button.d.ts
9
9
  declare const buttonVariants: (props?: ({
10
- variant?: "primary" | "secondary" | "tertiary" | "bare" | "destructive" | null | undefined;
11
- color?: "accent" | "neutral" | "danger" | null | undefined;
12
- size?: "small" | "default" | "medium" | "icon-small" | "icon" | "icon-medium" | null | undefined;
10
+ variant?: "secondary" | "destructive" | "primary" | "tertiary" | "bare" | null | undefined;
11
+ color?: "neutral" | "accent" | "danger" | null | undefined;
12
+ size?: "small" | "icon" | "default" | "medium" | "icon-small" | "icon-medium" | null | undefined;
13
13
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
14
14
  type SparkButtonColor = 'accent' | 'neutral' | 'danger';
15
15
  interface ButtonProps extends Omit<React$1.ComponentProps<typeof Button$1>, 'ref'>, Omit<VariantProps<typeof buttonVariants>, 'variant' | 'color'> {
@@ -8,7 +8,7 @@ import { VariantProps } from "class-variance-authority";
8
8
  //#region src/components/callout.d.ts
9
9
  type CalloutVariant = 'error' | 'info' | 'neutral' | 'success' | 'warning';
10
10
  declare const calloutVariants: (props?: ({
11
- variant?: "neutral" | "success" | "warning" | "info" | "error" | null | undefined;
11
+ variant?: "info" | "warning" | "success" | "error" | "neutral" | null | undefined;
12
12
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
13
13
  interface CalloutProps extends React$1.HTMLAttributes<HTMLDivElement>, VariantProps<typeof calloutVariants> {
14
14
  /** Overrides the variant's default icon; the variant's color is kept. `false` renders no icon. */
@@ -8,7 +8,7 @@ import { VariantProps } from "class-variance-authority";
8
8
  //#region src/components/callout.d.ts
9
9
  type CalloutVariant = 'error' | 'info' | 'neutral' | 'success' | 'warning';
10
10
  declare const calloutVariants: (props?: ({
11
- variant?: "neutral" | "success" | "warning" | "info" | "error" | null | undefined;
11
+ variant?: "info" | "warning" | "success" | "error" | "neutral" | null | undefined;
12
12
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
13
13
  interface CalloutProps extends React$1.HTMLAttributes<HTMLDivElement>, VariantProps<typeof calloutVariants> {
14
14
  /** Overrides the variant's default icon; the variant's color is kept. `false` renders no icon. */
@@ -18,12 +18,21 @@ function useFieldContext() {
18
18
  function useFieldControl(options) {
19
19
  const field = useFieldContext();
20
20
  const theme = require_components_theme.useTheme();
21
+ const id = options?.id ?? field?.id;
22
+ const ariaDescribedBy = [
23
+ field?.hasError ? field.errorId : void 0,
24
+ field?.hasDescription ? field.descriptionId : void 0,
25
+ options?.ariaDescribedBy
26
+ ].filter(Boolean).join(" ") || void 0;
27
+ const inputStyle = options?.variant ?? field?.inputStyle ?? theme.inputStyle ?? "soft";
28
+ const inputColor = field?.inputColor ?? theme.inputColor ?? "brand";
21
29
  return {
22
30
  field,
23
- id: options?.id ?? field?.id,
24
- ariaDescribedBy: [field?.hasDescription ? field.descriptionId : void 0, options?.ariaDescribedBy].filter(Boolean).join(" ") || void 0,
25
- inputStyle: options?.variant ?? field?.inputStyle ?? theme.inputStyle ?? "soft",
26
- inputColor: field?.inputColor ?? theme.inputColor ?? "brand"
31
+ id,
32
+ ariaDescribedBy,
33
+ ariaInvalid: field?.hasError || void 0,
34
+ inputStyle,
35
+ inputColor
27
36
  };
28
37
  }
29
38
  function FieldSet({ className, ...props }) {
@@ -119,9 +128,14 @@ const fieldVariants = (0, class_variance_authority.cva)("group/field relative fl
119
128
  }
120
129
  ]
121
130
  });
122
- function Field({ className, children, labelPosition: labelPositionProp, inputStyle: inputStyleProp, inputColor: inputColorProp, readOnly = false, ...props }) {
131
+ function Field({ className, children, labelPosition: labelPositionProp, inputStyle: inputStyleProp, inputColor: inputColorProp, readOnly = false, "data-invalid": dataInvalid, ...props }) {
123
132
  const id = (0, react.useId)();
124
- const [hasDescription, setHasDescription] = react.useState(false);
133
+ const [descriptionCount, setDescriptionCount] = react.useState(0);
134
+ const [errorCount, setErrorCount] = react.useState(0);
135
+ const setHasDescription = react.useCallback((has) => setDescriptionCount((n) => n + (has ? 1 : -1)), []);
136
+ const setHasError = react.useCallback((has) => setErrorCount((n) => n + (has ? 1 : -1)), []);
137
+ const hasDescription = descriptionCount > 0;
138
+ const hasError = errorCount > 0;
125
139
  const theme = require_components_theme.useTheme();
126
140
  const labelPosition = labelPositionProp ?? theme.labelPosition ?? "inset";
127
141
  const inputStyle = inputStyleProp ?? theme.inputStyle ?? "soft";
@@ -134,6 +148,9 @@ function Field({ className, children, labelPosition: labelPositionProp, inputSty
134
148
  descriptionId: `${fieldId}-description`,
135
149
  hasDescription,
136
150
  setHasDescription,
151
+ errorId: `${fieldId}-error`,
152
+ hasError,
153
+ setHasError,
137
154
  readOnly,
138
155
  labelPosition,
139
156
  inputStyle,
@@ -142,6 +159,9 @@ function Field({ className, children, labelPosition: labelPositionProp, inputSty
142
159
  fieldId,
143
160
  labelId,
144
161
  hasDescription,
162
+ setHasDescription,
163
+ hasError,
164
+ setHasError,
145
165
  readOnly,
146
166
  labelPosition,
147
167
  inputStyle,
@@ -155,6 +175,7 @@ function Field({ className, children, labelPosition: labelPositionProp, inputSty
155
175
  "data-label-position": labelPosition,
156
176
  "data-input-style": inputStyle,
157
177
  "data-input-color": inputColor,
178
+ "data-invalid": dataInvalid ?? (hasError || void 0),
158
179
  "aria-labelledby": readOnly ? labelId : void 0,
159
180
  className: (0, cnfast.cn)(fieldVariants({
160
181
  labelPosition,
@@ -251,7 +272,9 @@ function FieldSeparator({ children, className, ...props }) {
251
272
  })]
252
273
  });
253
274
  }
254
- function FieldError({ className, children, errors, ...props }) {
275
+ function FieldError({ className, children, errors, id, ...props }) {
276
+ const field = useFieldContext();
277
+ const setHasError = field?.setHasError;
255
278
  const content = react.useMemo(() => {
256
279
  if (children) return children;
257
280
  if (!errors?.length) return null;
@@ -262,10 +285,22 @@ function FieldError({ className, children, errors, ...props }) {
262
285
  children: unique.map((error, index) => error?.message && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: error.message }, index))
263
286
  });
264
287
  }, [children, errors]);
288
+ const hasContent = Boolean(content);
289
+ const hasCustomId = id != null;
290
+ react.useEffect(() => {
291
+ if (!setHasError || !hasContent || hasCustomId) return;
292
+ setHasError(true);
293
+ return () => setHasError(false);
294
+ }, [
295
+ setHasError,
296
+ hasContent,
297
+ hasCustomId
298
+ ]);
265
299
  if (!content) return null;
266
300
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
267
301
  role: "alert",
268
302
  "data-slot": "field-error",
303
+ id: id ?? field?.errorId,
269
304
  className: (0, cnfast.cn)("mt-0.5 text-sm font-normal text-error", className),
270
305
  ...props,
271
306
  children: content
@@ -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 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 descriptionId = field?.hasDescription ? field.descriptionId : undefined\n const ariaDescribedBy =\n [descriptionId, options?.ariaDescribedBy].filter(Boolean).join(' ') ||\n undefined\n const inputStyle =\n options?.variant ?? field?.inputStyle ?? theme.inputStyle ?? 'soft'\n const inputColor = field?.inputColor ?? theme.inputColor ?? 'brand'\n return { field, id, ariaDescribedBy, 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 box\n // focus inside inset is the classic underline cue, not a shell ring\n inset: 'gap-y-1 rounded-lg 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}\n\nfunction Field({\n className,\n children,\n labelPosition: labelPositionProp,\n inputStyle: inputStyleProp,\n inputColor: inputColorProp,\n readOnly = false,\n ...props\n}: FieldProps) {\n const id = useId()\n const [hasDescription, setHasDescription] = React.useState(false)\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 readOnly,\n labelPosition,\n inputStyle,\n inputColor,\n }),\n [\n fieldId,\n labelId,\n hasDescription,\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 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-0.5 leading-snug',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldLabelClassName =\n 'text-sm leading-5 font-normal text-gray-light 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('mt-0.5 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 ...props\n}: React.ComponentProps<'div'> & {\n errors?: Array<{ message?: string } | undefined>\n}) {\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, index) =>\n error?.message && <li key={index}>{error.message}</li>,\n )}\n </ul>\n )\n }, [children, errors])\n\n if (!content) return null\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n className={cn('mt-0.5 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"],"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 box\n // focus inside inset is the classic underline cue, not a shell ring\n inset: 'gap-y-1 rounded-lg 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-0.5 leading-snug',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldLabelClassName =\n 'text-sm leading-5 font-normal text-gray-light 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('mt-0.5 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, index) =>\n error?.message && <li key={index}>{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('mt-0.5 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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"}
@@ -10,6 +10,9 @@ type FieldContextValue = {
10
10
  descriptionId: string;
11
11
  hasDescription: boolean;
12
12
  setHasDescription: (has: boolean) => void;
13
+ errorId: string;
14
+ hasError: boolean;
15
+ setHasError: (has: boolean) => void;
13
16
  readOnly: boolean;
14
17
  labelPosition: SparkLabelPosition;
15
18
  inputStyle: SparkInputStyle;
@@ -24,6 +27,7 @@ declare function useFieldControl(options?: {
24
27
  field: FieldContextValue | null;
25
28
  id: string | undefined;
26
29
  ariaDescribedBy: string | undefined;
30
+ ariaInvalid: true | undefined;
27
31
  inputStyle: SparkInputStyle;
28
32
  inputColor: SparkInputColor;
29
33
  };
@@ -47,6 +51,7 @@ interface FieldProps extends React$1.ComponentProps<'div'> {
47
51
  inputStyle?: SparkInputStyle;
48
52
  inputColor?: SparkInputColor;
49
53
  readOnly?: boolean;
54
+ 'data-invalid'?: boolean | 'true' | 'false';
50
55
  }
51
56
  declare function Field({
52
57
  className,
@@ -55,6 +60,7 @@ declare function Field({
55
60
  inputStyle: inputStyleProp,
56
61
  inputColor: inputColorProp,
57
62
  readOnly,
63
+ 'data-invalid': dataInvalid,
58
64
  ...props
59
65
  }: FieldProps): import("react/jsx-runtime").JSX.Element;
60
66
  declare function FieldContent({
@@ -109,6 +115,7 @@ declare function FieldError({
109
115
  className,
110
116
  children,
111
117
  errors,
118
+ id,
112
119
  ...props
113
120
  }: React$1.ComponentProps<'div'> & {
114
121
  errors?: Array<{
@@ -10,6 +10,9 @@ type FieldContextValue = {
10
10
  descriptionId: string;
11
11
  hasDescription: boolean;
12
12
  setHasDescription: (has: boolean) => void;
13
+ errorId: string;
14
+ hasError: boolean;
15
+ setHasError: (has: boolean) => void;
13
16
  readOnly: boolean;
14
17
  labelPosition: SparkLabelPosition;
15
18
  inputStyle: SparkInputStyle;
@@ -24,6 +27,7 @@ declare function useFieldControl(options?: {
24
27
  field: FieldContextValue | null;
25
28
  id: string | undefined;
26
29
  ariaDescribedBy: string | undefined;
30
+ ariaInvalid: true | undefined;
27
31
  inputStyle: SparkInputStyle;
28
32
  inputColor: SparkInputColor;
29
33
  };
@@ -47,6 +51,7 @@ interface FieldProps extends React$1.ComponentProps<'div'> {
47
51
  inputStyle?: SparkInputStyle;
48
52
  inputColor?: SparkInputColor;
49
53
  readOnly?: boolean;
54
+ 'data-invalid'?: boolean | 'true' | 'false';
50
55
  }
51
56
  declare function Field({
52
57
  className,
@@ -55,6 +60,7 @@ declare function Field({
55
60
  inputStyle: inputStyleProp,
56
61
  inputColor: inputColorProp,
57
62
  readOnly,
63
+ 'data-invalid': dataInvalid,
58
64
  ...props
59
65
  }: FieldProps): import("react/jsx-runtime").JSX.Element;
60
66
  declare function FieldContent({
@@ -109,6 +115,7 @@ declare function FieldError({
109
115
  className,
110
116
  children,
111
117
  errors,
118
+ id,
112
119
  ...props
113
120
  }: React$1.ComponentProps<'div'> & {
114
121
  errors?: Array<{
@@ -16,12 +16,21 @@ function useFieldContext() {
16
16
  function useFieldControl(options) {
17
17
  const field = useFieldContext();
18
18
  const theme = useTheme();
19
+ const id = options?.id ?? field?.id;
20
+ const ariaDescribedBy = [
21
+ field?.hasError ? field.errorId : void 0,
22
+ field?.hasDescription ? field.descriptionId : void 0,
23
+ options?.ariaDescribedBy
24
+ ].filter(Boolean).join(" ") || void 0;
25
+ const inputStyle = options?.variant ?? field?.inputStyle ?? theme.inputStyle ?? "soft";
26
+ const inputColor = field?.inputColor ?? theme.inputColor ?? "brand";
19
27
  return {
20
28
  field,
21
- id: options?.id ?? field?.id,
22
- ariaDescribedBy: [field?.hasDescription ? field.descriptionId : void 0, options?.ariaDescribedBy].filter(Boolean).join(" ") || void 0,
23
- inputStyle: options?.variant ?? field?.inputStyle ?? theme.inputStyle ?? "soft",
24
- inputColor: field?.inputColor ?? theme.inputColor ?? "brand"
29
+ id,
30
+ ariaDescribedBy,
31
+ ariaInvalid: field?.hasError || void 0,
32
+ inputStyle,
33
+ inputColor
25
34
  };
26
35
  }
27
36
  function FieldSet({ className, ...props }) {
@@ -117,9 +126,14 @@ const fieldVariants = cva("group/field relative flex w-full flex-col data-[inval
117
126
  }
118
127
  ]
119
128
  });
120
- function Field({ className, children, labelPosition: labelPositionProp, inputStyle: inputStyleProp, inputColor: inputColorProp, readOnly = false, ...props }) {
129
+ function Field({ className, children, labelPosition: labelPositionProp, inputStyle: inputStyleProp, inputColor: inputColorProp, readOnly = false, "data-invalid": dataInvalid, ...props }) {
121
130
  const id = useId();
122
- const [hasDescription, setHasDescription] = React$1.useState(false);
131
+ const [descriptionCount, setDescriptionCount] = React$1.useState(0);
132
+ const [errorCount, setErrorCount] = React$1.useState(0);
133
+ const setHasDescription = React$1.useCallback((has) => setDescriptionCount((n) => n + (has ? 1 : -1)), []);
134
+ const setHasError = React$1.useCallback((has) => setErrorCount((n) => n + (has ? 1 : -1)), []);
135
+ const hasDescription = descriptionCount > 0;
136
+ const hasError = errorCount > 0;
123
137
  const theme = useTheme();
124
138
  const labelPosition = labelPositionProp ?? theme.labelPosition ?? "inset";
125
139
  const inputStyle = inputStyleProp ?? theme.inputStyle ?? "soft";
@@ -132,6 +146,9 @@ function Field({ className, children, labelPosition: labelPositionProp, inputSty
132
146
  descriptionId: `${fieldId}-description`,
133
147
  hasDescription,
134
148
  setHasDescription,
149
+ errorId: `${fieldId}-error`,
150
+ hasError,
151
+ setHasError,
135
152
  readOnly,
136
153
  labelPosition,
137
154
  inputStyle,
@@ -140,6 +157,9 @@ function Field({ className, children, labelPosition: labelPositionProp, inputSty
140
157
  fieldId,
141
158
  labelId,
142
159
  hasDescription,
160
+ setHasDescription,
161
+ hasError,
162
+ setHasError,
143
163
  readOnly,
144
164
  labelPosition,
145
165
  inputStyle,
@@ -153,6 +173,7 @@ function Field({ className, children, labelPosition: labelPositionProp, inputSty
153
173
  "data-label-position": labelPosition,
154
174
  "data-input-style": inputStyle,
155
175
  "data-input-color": inputColor,
176
+ "data-invalid": dataInvalid ?? (hasError || void 0),
156
177
  "aria-labelledby": readOnly ? labelId : void 0,
157
178
  className: cn(fieldVariants({
158
179
  labelPosition,
@@ -249,7 +270,9 @@ function FieldSeparator({ children, className, ...props }) {
249
270
  })]
250
271
  });
251
272
  }
252
- function FieldError({ className, children, errors, ...props }) {
273
+ function FieldError({ className, children, errors, id, ...props }) {
274
+ const field = useFieldContext();
275
+ const setHasError = field?.setHasError;
253
276
  const content = React$1.useMemo(() => {
254
277
  if (children) return children;
255
278
  if (!errors?.length) return null;
@@ -260,10 +283,22 @@ function FieldError({ className, children, errors, ...props }) {
260
283
  children: unique.map((error, index) => error?.message && /* @__PURE__ */ jsx("li", { children: error.message }, index))
261
284
  });
262
285
  }, [children, errors]);
286
+ const hasContent = Boolean(content);
287
+ const hasCustomId = id != null;
288
+ React$1.useEffect(() => {
289
+ if (!setHasError || !hasContent || hasCustomId) return;
290
+ setHasError(true);
291
+ return () => setHasError(false);
292
+ }, [
293
+ setHasError,
294
+ hasContent,
295
+ hasCustomId
296
+ ]);
263
297
  if (!content) return null;
264
298
  return /* @__PURE__ */ jsx("div", {
265
299
  role: "alert",
266
300
  "data-slot": "field-error",
301
+ id: id ?? field?.errorId,
267
302
  className: cn("mt-0.5 text-sm font-normal text-error", className),
268
303
  ...props,
269
304
  children: content
@@ -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 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 descriptionId = field?.hasDescription ? field.descriptionId : undefined\n const ariaDescribedBy =\n [descriptionId, options?.ariaDescribedBy].filter(Boolean).join(' ') ||\n undefined\n const inputStyle =\n options?.variant ?? field?.inputStyle ?? theme.inputStyle ?? 'soft'\n const inputColor = field?.inputColor ?? theme.inputColor ?? 'brand'\n return { field, id, ariaDescribedBy, 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 box\n // focus inside inset is the classic underline cue, not a shell ring\n inset: 'gap-y-1 rounded-lg 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}\n\nfunction Field({\n className,\n children,\n labelPosition: labelPositionProp,\n inputStyle: inputStyleProp,\n inputColor: inputColorProp,\n readOnly = false,\n ...props\n}: FieldProps) {\n const id = useId()\n const [hasDescription, setHasDescription] = React.useState(false)\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 readOnly,\n labelPosition,\n inputStyle,\n inputColor,\n }),\n [\n fieldId,\n labelId,\n hasDescription,\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 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-0.5 leading-snug',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldLabelClassName =\n 'text-sm leading-5 font-normal text-gray-light 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('mt-0.5 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 ...props\n}: React.ComponentProps<'div'> & {\n errors?: Array<{ message?: string } | undefined>\n}) {\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, index) =>\n error?.message && <li key={index}>{error.message}</li>,\n )}\n </ul>\n )\n }, [children, errors])\n\n if (!content) return null\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n className={cn('mt-0.5 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 { 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 box\n // focus inside inset is the classic underline cue, not a shell ring\n inset: 'gap-y-1 rounded-lg 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-0.5 leading-snug',\n className,\n )}\n {...props}\n />\n )\n}\n\nconst fieldLabelClassName =\n 'text-sm leading-5 font-normal text-gray-light 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('mt-0.5 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, index) =>\n error?.message && <li key={index}>{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('mt-0.5 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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"}
@@ -5,7 +5,7 @@ import { VariantProps } from "class-variance-authority";
5
5
 
6
6
  //#region src/components/info-block.d.ts
7
7
  declare const InfoBlock: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
8
- variant?: "bare" | "default" | null | undefined;
8
+ variant?: "default" | "bare" | null | undefined;
9
9
  } & import("class-variance-authority/types").ClassProp) | undefined) => string> & React$1.RefAttributes<HTMLDivElement>>;
10
10
  declare const InfoBlockIcon: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLDivElement> & React$1.RefAttributes<HTMLDivElement>>;
11
11
  declare const InfoBlockContent: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLDivElement> & React$1.RefAttributes<HTMLDivElement>>;
@@ -5,7 +5,7 @@ import { VariantProps } from "class-variance-authority";
5
5
 
6
6
  //#region src/components/info-block.d.ts
7
7
  declare const InfoBlock: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
8
- variant?: "bare" | "default" | null | undefined;
8
+ variant?: "default" | "bare" | null | undefined;
9
9
  } & import("class-variance-authority/types").ClassProp) | undefined) => string> & React$1.RefAttributes<HTMLDivElement>>;
10
10
  declare const InfoBlockIcon: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLDivElement> & React$1.RefAttributes<HTMLDivElement>>;
11
11
  declare const InfoBlockContent: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLDivElement> & React$1.RefAttributes<HTMLDivElement>>;
@@ -10,7 +10,7 @@ declare function InputGroup({
10
10
  ...props
11
11
  }: React$1.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
12
12
  declare const inputGroupAddonVariants: (props?: ({
13
- align?: "inline-start" | "inline-end" | null | undefined;
13
+ align?: "inline-end" | "inline-start" | null | undefined;
14
14
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
15
15
  declare function InputGroupAddon({
16
16
  className,
@@ -10,7 +10,7 @@ declare function InputGroup({
10
10
  ...props
11
11
  }: React$1.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
12
12
  declare const inputGroupAddonVariants: (props?: ({
13
- align?: "inline-start" | "inline-end" | null | undefined;
13
+ align?: "inline-end" | "inline-start" | null | undefined;
14
14
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
15
15
  declare function InputGroupAddon({
16
16
  className,
@@ -86,7 +86,7 @@ const slotVariants = (0, class_variance_authority.cva)("flex w-11 min-w-0 items-
86
86
  ]
87
87
  });
88
88
  function InputOtp({ length = 6, className, id: idProp, onComplete, slotAriaLabel = (index) => `Character ${index + 1}`, validationType = "alphanumeric", ...props }) {
89
- const { field, id, ariaDescribedBy, inputStyle, inputColor } = require_components_field.useFieldControl({ id: idProp });
89
+ const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } = require_components_field.useFieldControl({ id: idProp });
90
90
  const chrome = field?.labelPosition === "inset" ? "inset" : "box";
91
91
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_otp_field.OTPField.Root, {
92
92
  "data-slot": "input-otp",
@@ -99,6 +99,7 @@ function InputOtp({ length = 6, className, id: idProp, onComplete, slotAriaLabel
99
99
  children: Array.from({ length }, (_, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_otp_field.OTPField.Input, {
100
100
  "aria-label": index === 0 ? void 0 : slotAriaLabel(index),
101
101
  "aria-describedby": index === 0 ? ariaDescribedBy : void 0,
102
+ "aria-invalid": index === 0 ? ariaInvalid : void 0,
102
103
  className: slotVariants({
103
104
  chrome,
104
105
  inputStyle,
@@ -1 +1 @@
1
- {"version":3,"file":"input-otp.cjs","names":["GHOST_UNDERLINE","boxChrome","useFieldControl","OTPField"],"sources":["../../src/components/input-otp.tsx"],"sourcesContent":["import { OTPField, type OTPFieldRootProps } from '@base-ui/react/otp-field'\nimport { cva } from 'class-variance-authority'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\n// One code box per slot. Mirrors Input's box/underline carrier model so the\n// slots read like the rest of the input family across every style/color knob.\nconst slotVariants = cva(\n 'flex w-11 min-w-0 items-center justify-center bg-transparent text-center text-base font-medium text-gray tabular-nums outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none',\n {\n variants: {\n chrome: {\n // inside an inset field the shell is the box; slots keep the classic\n // underline (the pre-knob default look)\n inset: GHOST_UNDERLINE,\n box: 'h-11 rounded-lg',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n ],\n },\n)\n\ninterface InputOtpProps extends Pick<\n OTPFieldRootProps,\n | 'value'\n | 'defaultValue'\n | 'onValueChange'\n | 'disabled'\n | 'className'\n | 'id'\n | 'validationType'\n> {\n /** Number of code slots. */\n length?: number\n /** Fired when every slot is filled. */\n onComplete?: OTPFieldRootProps['onValueComplete']\n /** Accessible name per slot (for localization). */\n slotAriaLabel?: (index: number) => string\n}\n\nfunction InputOtp({\n length = 6,\n className,\n id: idProp,\n onComplete,\n slotAriaLabel = (index) => `Character ${index + 1}`,\n validationType = 'alphanumeric',\n ...props\n}: InputOtpProps) {\n const { field, id, ariaDescribedBy, inputStyle, inputColor } =\n useFieldControl({ id: idProp })\n const chrome = field?.labelPosition === 'inset' ? 'inset' : 'box'\n\n return (\n <OTPField.Root\n data-slot=\"input-otp\"\n length={length}\n id={id}\n validationType={validationType}\n onValueComplete={onComplete}\n className={cn('flex items-center gap-2', className)}\n {...props}\n >\n {Array.from({ length }, (_, index) => (\n <OTPField.Input\n key={index}\n // the first slot is named by the Field label (Base UI ignores\n // aria-label there); the rest get a positional name for a11y\n aria-label={index === 0 ? undefined : slotAriaLabel(index)}\n aria-describedby={index === 0 ? ariaDescribedBy : undefined}\n className={slotVariants({ chrome, inputStyle, inputColor })}\n />\n ))}\n </OTPField.Root>\n )\n}\n\nexport { InputOtp }\nexport type { InputOtpProps }\n"],"mappings":";;;;;;;;;AAQA,MAAM,gBAAA,GAAA,yBAAA,KACJ,+UACA;CACE,UAAU;EACR,QAAQ;GAGN,OAAOA,+BAAAA;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;CACpC;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOC,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;CACtE;AACF,CACF;AAoBA,SAAS,SAAS,EAChB,SAAS,GACT,WACA,IAAI,QACJ,YACA,iBAAiB,UAAU,aAAa,QAAQ,KAChD,iBAAiB,gBACjB,GAAG,SACa;CAChB,MAAM,EAAE,OAAO,IAAI,iBAAiB,YAAY,eAC9CC,yBAAAA,gBAAgB,EAAE,IAAI,OAAO,CAAC;CAChC,MAAM,SAAS,OAAO,kBAAkB,UAAU,UAAU;CAE5D,OACE,iBAAA,GAAA,kBAAA,KAACC,yBAAAA,SAAS,MAAV;EACE,aAAU;EACF;EACJ;EACY;EAChB,iBAAiB;EACjB,YAAA,GAAA,OAAA,IAAc,2BAA2B,SAAS;EAClD,GAAI;YAEH,MAAM,KAAK,EAAE,OAAO,IAAI,GAAG,UAC1B,iBAAA,GAAA,kBAAA,KAACA,yBAAAA,SAAS,OAAV;GAIE,cAAY,UAAU,IAAI,KAAA,IAAY,cAAc,KAAK;GACzD,oBAAkB,UAAU,IAAI,kBAAkB,KAAA;GAClD,WAAW,aAAa;IAAE;IAAQ;IAAY;GAAW,CAAC;EAC3D,GANM,KAMN,CACF;CACY,CAAA;AAEnB"}
1
+ {"version":3,"file":"input-otp.cjs","names":["GHOST_UNDERLINE","boxChrome","useFieldControl","OTPField"],"sources":["../../src/components/input-otp.tsx"],"sourcesContent":["import { OTPField, type OTPFieldRootProps } from '@base-ui/react/otp-field'\nimport { cva } from 'class-variance-authority'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\n// One code box per slot. Mirrors Input's box/underline carrier model so the\n// slots read like the rest of the input family across every style/color knob.\nconst slotVariants = cva(\n 'flex w-11 min-w-0 items-center justify-center bg-transparent text-center text-base font-medium text-gray tabular-nums outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none',\n {\n variants: {\n chrome: {\n // inside an inset field the shell is the box; slots keep the classic\n // underline (the pre-knob default look)\n inset: GHOST_UNDERLINE,\n box: 'h-11 rounded-lg',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n ],\n },\n)\n\ninterface InputOtpProps extends Pick<\n OTPFieldRootProps,\n | 'value'\n | 'defaultValue'\n | 'onValueChange'\n | 'disabled'\n | 'className'\n | 'id'\n | 'validationType'\n> {\n /** Number of code slots. */\n length?: number\n /** Fired when every slot is filled. */\n onComplete?: OTPFieldRootProps['onValueComplete']\n /** Accessible name per slot (for localization). */\n slotAriaLabel?: (index: number) => string\n}\n\nfunction InputOtp({\n length = 6,\n className,\n id: idProp,\n onComplete,\n slotAriaLabel = (index) => `Character ${index + 1}`,\n validationType = 'alphanumeric',\n ...props\n}: InputOtpProps) {\n const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } =\n useFieldControl({ id: idProp })\n const chrome = field?.labelPosition === 'inset' ? 'inset' : 'box'\n\n return (\n <OTPField.Root\n data-slot=\"input-otp\"\n length={length}\n id={id}\n validationType={validationType}\n onValueComplete={onComplete}\n className={cn('flex items-center gap-2', className)}\n {...props}\n >\n {Array.from({ length }, (_, index) => (\n <OTPField.Input\n key={index}\n // the first slot is named by the Field label (Base UI ignores\n // aria-label there); the rest get a positional name for a11y\n aria-label={index === 0 ? undefined : slotAriaLabel(index)}\n aria-describedby={index === 0 ? ariaDescribedBy : undefined}\n aria-invalid={index === 0 ? ariaInvalid : undefined}\n className={slotVariants({ chrome, inputStyle, inputColor })}\n />\n ))}\n </OTPField.Root>\n )\n}\n\nexport { InputOtp }\nexport type { InputOtpProps }\n"],"mappings":";;;;;;;;;AAQA,MAAM,gBAAA,GAAA,yBAAA,KACJ,+UACA;CACE,UAAU;EACR,QAAQ;GAGN,OAAOA,+BAAAA;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;CACpC;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOC,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;CACtE;AACF,CACF;AAoBA,SAAS,SAAS,EAChB,SAAS,GACT,WACA,IAAI,QACJ,YACA,iBAAiB,UAAU,aAAa,QAAQ,KAChD,iBAAiB,gBACjB,GAAG,SACa;CAChB,MAAM,EAAE,OAAO,IAAI,iBAAiB,aAAa,YAAY,eAC3DC,yBAAAA,gBAAgB,EAAE,IAAI,OAAO,CAAC;CAChC,MAAM,SAAS,OAAO,kBAAkB,UAAU,UAAU;CAE5D,OACE,iBAAA,GAAA,kBAAA,KAACC,yBAAAA,SAAS,MAAV;EACE,aAAU;EACF;EACJ;EACY;EAChB,iBAAiB;EACjB,YAAA,GAAA,OAAA,IAAc,2BAA2B,SAAS;EAClD,GAAI;YAEH,MAAM,KAAK,EAAE,OAAO,IAAI,GAAG,UAC1B,iBAAA,GAAA,kBAAA,KAACA,yBAAAA,SAAS,OAAV;GAIE,cAAY,UAAU,IAAI,KAAA,IAAY,cAAc,KAAK;GACzD,oBAAkB,UAAU,IAAI,kBAAkB,KAAA;GAClD,gBAAc,UAAU,IAAI,cAAc,KAAA;GAC1C,WAAW,aAAa;IAAE;IAAQ;IAAY;GAAW,CAAC;EAC3D,GAPM,KAON,CACF;CACY,CAAA;AAEnB"}
@@ -85,7 +85,7 @@ const slotVariants = cva("flex w-11 min-w-0 items-center justify-center bg-trans
85
85
  ]
86
86
  });
87
87
  function InputOtp({ length = 6, className, id: idProp, onComplete, slotAriaLabel = (index) => `Character ${index + 1}`, validationType = "alphanumeric", ...props }) {
88
- const { field, id, ariaDescribedBy, inputStyle, inputColor } = useFieldControl({ id: idProp });
88
+ const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } = useFieldControl({ id: idProp });
89
89
  const chrome = field?.labelPosition === "inset" ? "inset" : "box";
90
90
  return /* @__PURE__ */ jsx(OTPField.Root, {
91
91
  "data-slot": "input-otp",
@@ -98,6 +98,7 @@ function InputOtp({ length = 6, className, id: idProp, onComplete, slotAriaLabel
98
98
  children: Array.from({ length }, (_, index) => /* @__PURE__ */ jsx(OTPField.Input, {
99
99
  "aria-label": index === 0 ? void 0 : slotAriaLabel(index),
100
100
  "aria-describedby": index === 0 ? ariaDescribedBy : void 0,
101
+ "aria-invalid": index === 0 ? ariaInvalid : void 0,
101
102
  className: slotVariants({
102
103
  chrome,
103
104
  inputStyle,
@@ -1 +1 @@
1
- {"version":3,"file":"input-otp.js","names":[],"sources":["../../src/components/input-otp.tsx"],"sourcesContent":["import { OTPField, type OTPFieldRootProps } from '@base-ui/react/otp-field'\nimport { cva } from 'class-variance-authority'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\n// One code box per slot. Mirrors Input's box/underline carrier model so the\n// slots read like the rest of the input family across every style/color knob.\nconst slotVariants = cva(\n 'flex w-11 min-w-0 items-center justify-center bg-transparent text-center text-base font-medium text-gray tabular-nums outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none',\n {\n variants: {\n chrome: {\n // inside an inset field the shell is the box; slots keep the classic\n // underline (the pre-knob default look)\n inset: GHOST_UNDERLINE,\n box: 'h-11 rounded-lg',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n ],\n },\n)\n\ninterface InputOtpProps extends Pick<\n OTPFieldRootProps,\n | 'value'\n | 'defaultValue'\n | 'onValueChange'\n | 'disabled'\n | 'className'\n | 'id'\n | 'validationType'\n> {\n /** Number of code slots. */\n length?: number\n /** Fired when every slot is filled. */\n onComplete?: OTPFieldRootProps['onValueComplete']\n /** Accessible name per slot (for localization). */\n slotAriaLabel?: (index: number) => string\n}\n\nfunction InputOtp({\n length = 6,\n className,\n id: idProp,\n onComplete,\n slotAriaLabel = (index) => `Character ${index + 1}`,\n validationType = 'alphanumeric',\n ...props\n}: InputOtpProps) {\n const { field, id, ariaDescribedBy, inputStyle, inputColor } =\n useFieldControl({ id: idProp })\n const chrome = field?.labelPosition === 'inset' ? 'inset' : 'box'\n\n return (\n <OTPField.Root\n data-slot=\"input-otp\"\n length={length}\n id={id}\n validationType={validationType}\n onValueComplete={onComplete}\n className={cn('flex items-center gap-2', className)}\n {...props}\n >\n {Array.from({ length }, (_, index) => (\n <OTPField.Input\n key={index}\n // the first slot is named by the Field label (Base UI ignores\n // aria-label there); the rest get a positional name for a11y\n aria-label={index === 0 ? undefined : slotAriaLabel(index)}\n aria-describedby={index === 0 ? ariaDescribedBy : undefined}\n className={slotVariants({ chrome, inputStyle, inputColor })}\n />\n ))}\n </OTPField.Root>\n )\n}\n\nexport { InputOtp }\nexport type { InputOtpProps }\n"],"mappings":";;;;;;;;AAQA,MAAM,eAAe,IACnB,+UACA;CACE,UAAU;EACR,QAAQ;GAGN,OAAO;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;CACpC;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;CACtE;AACF,CACF;AAoBA,SAAS,SAAS,EAChB,SAAS,GACT,WACA,IAAI,QACJ,YACA,iBAAiB,UAAU,aAAa,QAAQ,KAChD,iBAAiB,gBACjB,GAAG,SACa;CAChB,MAAM,EAAE,OAAO,IAAI,iBAAiB,YAAY,eAC9C,gBAAgB,EAAE,IAAI,OAAO,CAAC;CAChC,MAAM,SAAS,OAAO,kBAAkB,UAAU,UAAU;CAE5D,OACE,oBAAC,SAAS,MAAV;EACE,aAAU;EACF;EACJ;EACY;EAChB,iBAAiB;EACjB,WAAW,GAAG,2BAA2B,SAAS;EAClD,GAAI;YAEH,MAAM,KAAK,EAAE,OAAO,IAAI,GAAG,UAC1B,oBAAC,SAAS,OAAV;GAIE,cAAY,UAAU,IAAI,KAAA,IAAY,cAAc,KAAK;GACzD,oBAAkB,UAAU,IAAI,kBAAkB,KAAA;GAClD,WAAW,aAAa;IAAE;IAAQ;IAAY;GAAW,CAAC;EAC3D,GANM,KAMN,CACF;CACY,CAAA;AAEnB"}
1
+ {"version":3,"file":"input-otp.js","names":[],"sources":["../../src/components/input-otp.tsx"],"sourcesContent":["import { OTPField, type OTPFieldRootProps } from '@base-ui/react/otp-field'\nimport { cva } from 'class-variance-authority'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\n// One code box per slot. Mirrors Input's box/underline carrier model so the\n// slots read like the rest of the input family across every style/color knob.\nconst slotVariants = cva(\n 'flex w-11 min-w-0 items-center justify-center bg-transparent text-center text-base font-medium text-gray tabular-nums outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none',\n {\n variants: {\n chrome: {\n // inside an inset field the shell is the box; slots keep the classic\n // underline (the pre-knob default look)\n inset: GHOST_UNDERLINE,\n box: 'h-11 rounded-lg',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n ],\n },\n)\n\ninterface InputOtpProps extends Pick<\n OTPFieldRootProps,\n | 'value'\n | 'defaultValue'\n | 'onValueChange'\n | 'disabled'\n | 'className'\n | 'id'\n | 'validationType'\n> {\n /** Number of code slots. */\n length?: number\n /** Fired when every slot is filled. */\n onComplete?: OTPFieldRootProps['onValueComplete']\n /** Accessible name per slot (for localization). */\n slotAriaLabel?: (index: number) => string\n}\n\nfunction InputOtp({\n length = 6,\n className,\n id: idProp,\n onComplete,\n slotAriaLabel = (index) => `Character ${index + 1}`,\n validationType = 'alphanumeric',\n ...props\n}: InputOtpProps) {\n const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } =\n useFieldControl({ id: idProp })\n const chrome = field?.labelPosition === 'inset' ? 'inset' : 'box'\n\n return (\n <OTPField.Root\n data-slot=\"input-otp\"\n length={length}\n id={id}\n validationType={validationType}\n onValueComplete={onComplete}\n className={cn('flex items-center gap-2', className)}\n {...props}\n >\n {Array.from({ length }, (_, index) => (\n <OTPField.Input\n key={index}\n // the first slot is named by the Field label (Base UI ignores\n // aria-label there); the rest get a positional name for a11y\n aria-label={index === 0 ? undefined : slotAriaLabel(index)}\n aria-describedby={index === 0 ? ariaDescribedBy : undefined}\n aria-invalid={index === 0 ? ariaInvalid : undefined}\n className={slotVariants({ chrome, inputStyle, inputColor })}\n />\n ))}\n </OTPField.Root>\n )\n}\n\nexport { InputOtp }\nexport type { InputOtpProps }\n"],"mappings":";;;;;;;;AAQA,MAAM,eAAe,IACnB,+UACA;CACE,UAAU;EACR,QAAQ;GAGN,OAAO;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;CACpC;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;CACtE;AACF,CACF;AAoBA,SAAS,SAAS,EAChB,SAAS,GACT,WACA,IAAI,QACJ,YACA,iBAAiB,UAAU,aAAa,QAAQ,KAChD,iBAAiB,gBACjB,GAAG,SACa;CAChB,MAAM,EAAE,OAAO,IAAI,iBAAiB,aAAa,YAAY,eAC3D,gBAAgB,EAAE,IAAI,OAAO,CAAC;CAChC,MAAM,SAAS,OAAO,kBAAkB,UAAU,UAAU;CAE5D,OACE,oBAAC,SAAS,MAAV;EACE,aAAU;EACF;EACJ;EACY;EAChB,iBAAiB;EACjB,WAAW,GAAG,2BAA2B,SAAS;EAClD,GAAI;YAEH,MAAM,KAAK,EAAE,OAAO,IAAI,GAAG,UAC1B,oBAAC,SAAS,OAAV;GAIE,cAAY,UAAU,IAAI,KAAA,IAAY,cAAc,KAAK;GACzD,oBAAkB,UAAU,IAAI,kBAAkB,KAAA;GAClD,gBAAc,UAAU,IAAI,cAAc,KAAA;GAC1C,WAAW,aAAa;IAAE;IAAQ;IAAY;GAAW,CAAC;EAC3D,GAPM,KAON,CACF;CACY,CAAA;AAEnB"}
@@ -89,7 +89,7 @@ const inputVariants = (0, class_variance_authority.cva)("flex h-10 w-full min-w-
89
89
  ]
90
90
  });
91
91
  const Input = react.forwardRef(function Input({ variant, className, id: idProp, "aria-describedby": ariaDescribedByProp, ...props }, ref) {
92
- const { field, id, ariaDescribedBy, inputStyle, inputColor } = require_components_field.useFieldControl({
92
+ const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } = require_components_field.useFieldControl({
93
93
  id: idProp,
94
94
  ariaDescribedBy: ariaDescribedByProp,
95
95
  variant: variant !== "none" ? variant : void 0
@@ -103,6 +103,7 @@ const Input = react.forwardRef(function Input({ variant, className, id: idProp,
103
103
  }), className),
104
104
  id,
105
105
  ref,
106
+ "aria-invalid": ariaInvalid,
106
107
  ...props,
107
108
  "aria-describedby": ariaDescribedBy
108
109
  });
@@ -1 +1 @@
1
- {"version":3,"file":"input.cjs","names":["GHOST_UNDERLINE","boxChrome","React","useFieldControl"],"sources":["../../src/components/input.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\ntype InputVariant = SparkInputStyle | 'none'\n\nconst inputVariants = cva(\n 'flex h-10 w-full min-w-0 [appearance:textfield] bg-transparent text-gray outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',\n {\n variants: {\n chrome: {\n none: 'border-0 px-0',\n // inside an inset field the shell is the box; the input keeps the\n // classic underline (the pre-knob default look)\n inset: GHOST_UNDERLINE,\n box: 'h-11 rounded-lg px-3 py-1 text-base transition-all motion-reduce:transition-none',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n ],\n },\n)\n\ninterface InputProps extends React.ComponentProps<'input'> {\n variant?: InputVariant\n}\n\n// forwardRef keeps refs working for React 18 consumers (ref-as-prop is 19-only)\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(function Input(\n {\n variant,\n className,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n },\n ref,\n) {\n const { field, id, ariaDescribedBy, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant: variant !== 'none' ? variant : undefined,\n })\n // an explicit variant prop keeps the box even inside an inset field\n const chrome =\n variant === 'none'\n ? 'none'\n : !variant && field?.labelPosition === 'inset'\n ? 'inset'\n : 'box'\n\n return (\n <input\n data-slot=\"input\"\n className={cn(\n inputVariants({ chrome, inputStyle, inputColor }),\n className,\n )}\n id={id}\n ref={ref}\n {...props}\n aria-describedby={ariaDescribedBy}\n />\n )\n})\n\nexport { Input }\nexport type { InputProps, InputVariant }\n"],"mappings":";;;;;;;;;;;AASA,MAAM,iBAAA,GAAA,yBAAA,KACJ,gYACA;CACE,UAAU;EACR,QAAQ;GACN,MAAM;GAGN,OAAOA,+BAAAA;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;CACpC;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOC,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;CACtE;AACF,CACF;AAOA,MAAM,QAAQC,MAAM,WAAyC,SAAS,MACpE,EACE,SACA,WACA,IAAI,QACJ,oBAAoB,qBACpB,GAAG,SAEL,KACA;CACA,MAAM,EAAE,OAAO,IAAI,iBAAiB,YAAY,eAC9CC,yBAAAA,gBAAgB;EACd,IAAI;EACJ,iBAAiB;EACjB,SAAS,YAAY,SAAS,UAAU,KAAA;CAC1C,CAAC;CASH,OACE,iBAAA,GAAA,kBAAA,KAAC,SAAD;EACE,aAAU;EACV,YAAA,GAAA,OAAA,IACE,cAAc;GAAE,QAVpB,YAAY,SACR,SACA,CAAC,WAAW,OAAO,kBAAkB,UACnC,UACA;GAMsB;GAAY;EAAW,CAAC,GAChD,SACF;EACI;EACC;EACL,GAAI;EACJ,oBAAkB;CACnB,CAAA;AAEL,CAAC"}
1
+ {"version":3,"file":"input.cjs","names":["GHOST_UNDERLINE","boxChrome","React","useFieldControl"],"sources":["../../src/components/input.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\ntype InputVariant = SparkInputStyle | 'none'\n\nconst inputVariants = cva(\n 'flex h-10 w-full min-w-0 [appearance:textfield] bg-transparent text-gray outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',\n {\n variants: {\n chrome: {\n none: 'border-0 px-0',\n // inside an inset field the shell is the box; the input keeps the\n // classic underline (the pre-knob default look)\n inset: GHOST_UNDERLINE,\n box: 'h-11 rounded-lg px-3 py-1 text-base transition-all motion-reduce:transition-none',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n ],\n },\n)\n\ninterface InputProps extends React.ComponentProps<'input'> {\n variant?: InputVariant\n}\n\n// forwardRef keeps refs working for React 18 consumers (ref-as-prop is 19-only)\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(function Input(\n {\n variant,\n className,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n },\n ref,\n) {\n const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant: variant !== 'none' ? variant : undefined,\n })\n // an explicit variant prop keeps the box even inside an inset field\n const chrome =\n variant === 'none'\n ? 'none'\n : !variant && field?.labelPosition === 'inset'\n ? 'inset'\n : 'box'\n\n return (\n <input\n data-slot=\"input\"\n className={cn(\n inputVariants({ chrome, inputStyle, inputColor }),\n className,\n )}\n id={id}\n ref={ref}\n aria-invalid={ariaInvalid}\n {...props}\n aria-describedby={ariaDescribedBy}\n />\n )\n})\n\nexport { Input }\nexport type { InputProps, InputVariant }\n"],"mappings":";;;;;;;;;;;AASA,MAAM,iBAAA,GAAA,yBAAA,KACJ,gYACA;CACE,UAAU;EACR,QAAQ;GACN,MAAM;GAGN,OAAOA,+BAAAA;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;CACpC;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOC,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;CACtE;AACF,CACF;AAOA,MAAM,QAAQC,MAAM,WAAyC,SAAS,MACpE,EACE,SACA,WACA,IAAI,QACJ,oBAAoB,qBACpB,GAAG,SAEL,KACA;CACA,MAAM,EAAE,OAAO,IAAI,iBAAiB,aAAa,YAAY,eAC3DC,yBAAAA,gBAAgB;EACd,IAAI;EACJ,iBAAiB;EACjB,SAAS,YAAY,SAAS,UAAU,KAAA;CAC1C,CAAC;CASH,OACE,iBAAA,GAAA,kBAAA,KAAC,SAAD;EACE,aAAU;EACV,YAAA,GAAA,OAAA,IACE,cAAc;GAAE,QAVpB,YAAY,SACR,SACA,CAAC,WAAW,OAAO,kBAAkB,UACnC,UACA;GAMsB;GAAY;EAAW,CAAC,GAChD,SACF;EACI;EACC;EACL,gBAAc;EACd,GAAI;EACJ,oBAAkB;CACnB,CAAA;AAEL,CAAC"}
@@ -86,7 +86,7 @@ const inputVariants = cva("flex h-10 w-full min-w-0 [appearance:textfield] bg-tr
86
86
  ]
87
87
  });
88
88
  const Input = React$1.forwardRef(function Input({ variant, className, id: idProp, "aria-describedby": ariaDescribedByProp, ...props }, ref) {
89
- const { field, id, ariaDescribedBy, inputStyle, inputColor } = useFieldControl({
89
+ const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } = useFieldControl({
90
90
  id: idProp,
91
91
  ariaDescribedBy: ariaDescribedByProp,
92
92
  variant: variant !== "none" ? variant : void 0
@@ -100,6 +100,7 @@ const Input = React$1.forwardRef(function Input({ variant, className, id: idProp
100
100
  }), className),
101
101
  id,
102
102
  ref,
103
+ "aria-invalid": ariaInvalid,
103
104
  ...props,
104
105
  "aria-describedby": ariaDescribedBy
105
106
  });
@@ -1 +1 @@
1
- {"version":3,"file":"input.js","names":["React"],"sources":["../../src/components/input.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\ntype InputVariant = SparkInputStyle | 'none'\n\nconst inputVariants = cva(\n 'flex h-10 w-full min-w-0 [appearance:textfield] bg-transparent text-gray outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',\n {\n variants: {\n chrome: {\n none: 'border-0 px-0',\n // inside an inset field the shell is the box; the input keeps the\n // classic underline (the pre-knob default look)\n inset: GHOST_UNDERLINE,\n box: 'h-11 rounded-lg px-3 py-1 text-base transition-all motion-reduce:transition-none',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n ],\n },\n)\n\ninterface InputProps extends React.ComponentProps<'input'> {\n variant?: InputVariant\n}\n\n// forwardRef keeps refs working for React 18 consumers (ref-as-prop is 19-only)\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(function Input(\n {\n variant,\n className,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n },\n ref,\n) {\n const { field, id, ariaDescribedBy, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant: variant !== 'none' ? variant : undefined,\n })\n // an explicit variant prop keeps the box even inside an inset field\n const chrome =\n variant === 'none'\n ? 'none'\n : !variant && field?.labelPosition === 'inset'\n ? 'inset'\n : 'box'\n\n return (\n <input\n data-slot=\"input\"\n className={cn(\n inputVariants({ chrome, inputStyle, inputColor }),\n className,\n )}\n id={id}\n ref={ref}\n {...props}\n aria-describedby={ariaDescribedBy}\n />\n )\n})\n\nexport { Input }\nexport type { InputProps, InputVariant }\n"],"mappings":";;;;;;;;AASA,MAAM,gBAAgB,IACpB,gYACA;CACE,UAAU;EACR,QAAQ;GACN,MAAM;GAGN,OAAO;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;CACpC;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;CACtE;AACF,CACF;AAOA,MAAM,QAAQA,QAAM,WAAyC,SAAS,MACpE,EACE,SACA,WACA,IAAI,QACJ,oBAAoB,qBACpB,GAAG,SAEL,KACA;CACA,MAAM,EAAE,OAAO,IAAI,iBAAiB,YAAY,eAC9C,gBAAgB;EACd,IAAI;EACJ,iBAAiB;EACjB,SAAS,YAAY,SAAS,UAAU,KAAA;CAC1C,CAAC;CASH,OACE,oBAAC,SAAD;EACE,aAAU;EACV,WAAW,GACT,cAAc;GAAE,QAVpB,YAAY,SACR,SACA,CAAC,WAAW,OAAO,kBAAkB,UACnC,UACA;GAMsB;GAAY;EAAW,CAAC,GAChD,SACF;EACI;EACC;EACL,GAAI;EACJ,oBAAkB;CACnB,CAAA;AAEL,CAAC"}
1
+ {"version":3,"file":"input.js","names":["React"],"sources":["../../src/components/input.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\ntype InputVariant = SparkInputStyle | 'none'\n\nconst inputVariants = cva(\n 'flex h-10 w-full min-w-0 [appearance:textfield] bg-transparent text-gray outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',\n {\n variants: {\n chrome: {\n none: 'border-0 px-0',\n // inside an inset field the shell is the box; the input keeps the\n // classic underline (the pre-knob default look)\n inset: GHOST_UNDERLINE,\n box: 'h-11 rounded-lg px-3 py-1 text-base transition-all motion-reduce:transition-none',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n ],\n },\n)\n\ninterface InputProps extends React.ComponentProps<'input'> {\n variant?: InputVariant\n}\n\n// forwardRef keeps refs working for React 18 consumers (ref-as-prop is 19-only)\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(function Input(\n {\n variant,\n className,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n },\n ref,\n) {\n const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant: variant !== 'none' ? variant : undefined,\n })\n // an explicit variant prop keeps the box even inside an inset field\n const chrome =\n variant === 'none'\n ? 'none'\n : !variant && field?.labelPosition === 'inset'\n ? 'inset'\n : 'box'\n\n return (\n <input\n data-slot=\"input\"\n className={cn(\n inputVariants({ chrome, inputStyle, inputColor }),\n className,\n )}\n id={id}\n ref={ref}\n aria-invalid={ariaInvalid}\n {...props}\n aria-describedby={ariaDescribedBy}\n />\n )\n})\n\nexport { Input }\nexport type { InputProps, InputVariant }\n"],"mappings":";;;;;;;;AASA,MAAM,gBAAgB,IACpB,gYACA;CACE,UAAU;EACR,QAAQ;GACN,MAAM;GAGN,OAAO;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;CACpC;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;CACtE;AACF,CACF;AAOA,MAAM,QAAQA,QAAM,WAAyC,SAAS,MACpE,EACE,SACA,WACA,IAAI,QACJ,oBAAoB,qBACpB,GAAG,SAEL,KACA;CACA,MAAM,EAAE,OAAO,IAAI,iBAAiB,aAAa,YAAY,eAC3D,gBAAgB;EACd,IAAI;EACJ,iBAAiB;EACjB,SAAS,YAAY,SAAS,UAAU,KAAA;CAC1C,CAAC;CASH,OACE,oBAAC,SAAD;EACE,aAAU;EACV,WAAW,GACT,cAAc;GAAE,QAVpB,YAAY,SACR,SACA,CAAC,WAAW,OAAO,kBAAkB,UACnC,UACA;GAMsB;GAAY;EAAW,CAAC,GAChD,SACF;EACI;EACC;EACL,gBAAc;EACd,GAAI;EACJ,oBAAkB;CACnB,CAAA;AAEL,CAAC"}
@@ -6,7 +6,7 @@ import { useRender } from "@base-ui/react/use-render";
6
6
 
7
7
  //#region src/components/marker.d.ts
8
8
  declare const markerVariants: (props?: ({
9
- variant?: "default" | "separator" | "border" | null | undefined;
9
+ variant?: "separator" | "default" | "border" | null | undefined;
10
10
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
11
  declare function Marker({
12
12
  className,
@@ -6,7 +6,7 @@ import { useRender } from "@base-ui/react/use-render";
6
6
 
7
7
  //#region src/components/marker.d.ts
8
8
  declare const markerVariants: (props?: ({
9
- variant?: "default" | "separator" | "border" | null | undefined;
9
+ variant?: "separator" | "default" | "border" | null | undefined;
10
10
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
11
  declare function Marker({
12
12
  className,
@@ -31,7 +31,7 @@ const TRIGGER_SIZE = {
31
31
  sm: "my-1 h-9 before:absolute before:inset-x-0 before:top-1/2 before:min-h-[2.75rem] before:-translate-y-1/2 before:content-['']"
32
32
  };
33
33
  function SelectTrigger({ className, size = "default", variant, id: idProp, children, "aria-describedby": ariaDescribedByProp, ...props }) {
34
- const { field, id, ariaDescribedBy, inputStyle, inputColor } = require_components_field.useFieldControl({
34
+ const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } = require_components_field.useFieldControl({
35
35
  id: idProp,
36
36
  ariaDescribedBy: ariaDescribedByProp,
37
37
  variant
@@ -42,6 +42,7 @@ function SelectTrigger({ className, size = "default", variant, id: idProp, child
42
42
  "data-size": size,
43
43
  id,
44
44
  className: (0, cnfast.cn)(TRIGGER_BASE, field ? "w-full" : "w-fit", !isGhost && TRIGGER_SIZE[size], require_lib_input_box_variants.boxChrome[isGhost ? "ghost" : inputStyle][inputColor], className),
45
+ "aria-invalid": ariaInvalid,
45
46
  ...props,
46
47
  "aria-describedby": ariaDescribedBy,
47
48
  children: [children, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react_select.Select.Icon, { render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons_material_symbol.MaterialSymbol, {
@@ -1 +1 @@
1
- {"version":3,"file":"select.cjs","names":["SelectPrimitive","useFieldControl","boxChrome","MaterialSymbol"],"sources":["../../src/components/select.tsx"],"sourcesContent":["'use client'\n\nimport { Select as SelectPrimitive } from '@base-ui/react/select'\nimport * as React from 'react'\nimport { MaterialSymbol } from '../icons/material-symbol'\nimport { boxChrome } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\n// bare re-export: Root renders no element and its props are generic over Value\nconst Select = SelectPrimitive.Root\n\nfunction SelectGroup({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return (\n <SelectPrimitive.Group\n data-slot=\"select-group\"\n className={cn('scroll-my-1 p-1', className)}\n {...props}\n />\n )\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />\n}\n\nconst TRIGGER_BASE =\n 'relative flex items-center justify-between gap-1.5 rounded-lg px-3 text-base whitespace-nowrap text-gray outline-hidden transition-colors select-none data-placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4'\n\n// box-chrome sizing only — a ghost/inset trigger sizes via boxChrome.ghost's\n// GHOST_UNDERLINE (h-10), matching Input's underline row exactly (no competing\n// height utility). `min-h-[2.75rem]` is a literal, non-knob-scaled WCAG 2.5.5\n// floor; `sm` stays visually compact but restores the floor via an invisible\n// `before:` hit overlay rather than growing the box, with `my-1` to keep that\n// overlay out of an ancestor scroller's overflow.\nconst TRIGGER_SIZE = {\n default: 'h-11 min-h-[2.75rem]',\n sm: \"my-1 h-9 before:absolute before:inset-x-0 before:top-1/2 before:min-h-[2.75rem] before:-translate-y-1/2 before:content-['']\",\n} as const\n\nfunction SelectTrigger({\n className,\n size = 'default',\n variant,\n id: idProp,\n children,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: 'sm' | 'default'\n // input-style override; falls back to Field / <Theme>, then soft (like Input)\n variant?: SparkInputStyle\n}) {\n const { field, id, ariaDescribedBy, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant,\n })\n // inside an inset field the shell is the box, so the trigger goes bare\n // (underline row), matching Input's inset chrome; an explicit variant keeps\n // the box even inside an inset field (mirrors Input)\n const isGhost =\n inputStyle === 'ghost' || (!variant && field?.labelPosition === 'inset')\n\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n id={id}\n className={cn(\n TRIGGER_BASE,\n // fills the field shell like an Input; sizes to content when standalone\n field ? 'w-full' : 'w-fit',\n !isGhost && TRIGGER_SIZE[size],\n boxChrome[isGhost ? 'ghost' : inputStyle][inputColor],\n className,\n )}\n {...props}\n aria-describedby={ariaDescribedBy}\n >\n {children}\n <SelectPrimitive.Icon\n render={\n <MaterialSymbol\n className=\"pointer-events-none text-[14px] text-gray-light\"\n name=\"expand_more\"\n />\n }\n />\n </SelectPrimitive.Trigger>\n )\n}\n\ntype SelectContentProps = React.ComponentProps<typeof SelectPrimitive.Popup> &\n Pick<\n SelectPrimitive.Positioner.Props,\n 'align' | 'alignOffset' | 'side' | 'sideOffset' | 'alignItemWithTrigger'\n > &\n Pick<SelectPrimitive.Portal.Props, 'container'>\n\nfunction SelectContent({\n className,\n children,\n align = 'start',\n alignOffset,\n side,\n sideOffset = 4,\n // drop the menu below the trigger (left-aligned) like a conventional form\n // dropdown; the native-<select>-style overlay (Base UI's inherited default)\n // shifts the popup to align item text with trigger text, exposing a sliver of\n // the trigger's fill at the edge. Opt back in per instance if you want it.\n alignItemWithTrigger = false,\n container,\n ...props\n}: SelectContentProps) {\n return (\n <SelectPrimitive.Portal container={container}>\n <SelectPrimitive.Positioner\n align={align}\n alignItemWithTrigger={alignItemWithTrigger}\n alignOffset={alignOffset}\n side={side}\n sideOffset={sideOffset}\n >\n <SelectPrimitive.Popup\n data-slot=\"select-content\"\n data-align-trigger={alignItemWithTrigger}\n className={cn(\n // match the trigger's width (never narrower) so the drop-below menu\n // lines up under it; --anchor-width is the trigger width, floored at\n // 9rem for very narrow triggers\n 'relative isolate z-50 max-h-(--available-height) min-w-[max(9rem,var(--anchor-width))] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-gray-a1 bg-gray-1 text-gray-a12 shadow-lg',\n 'transition-[opacity,scale,translate] duration-100 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',\n 'data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2',\n 'data-[align-trigger=true]:transition-none',\n className,\n )}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.List>{children}</SelectPrimitive.List>\n <SelectScrollDownButton />\n </SelectPrimitive.Popup>\n </SelectPrimitive.Positioner>\n </SelectPrimitive.Portal>\n )\n}\n\nfunction SelectLabel({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.GroupLabel>) {\n return (\n <SelectPrimitive.GroupLabel\n data-slot=\"select-label\"\n className={cn('px-1.5 py-1 text-xs text-gray-light', className)}\n {...props}\n />\n )\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n 'relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1.5 pr-8 pl-1.5 text-sm text-gray outline-hidden transition-colors select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-gray-a3 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2',\n className,\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute right-2 flex size-4 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <MaterialSymbol className=\"text-[11px]\" name=\"check\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\n// plain div, not Select.Separator: role=\"separator\" isn't a permitted child of a\n// listbox (aria-required-children), and the divider is decorative — the groups\n// carry the semantics. Hidden from assistive tech.\nfunction SelectSeparator({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n aria-hidden=\"true\"\n data-slot=\"select-separator\"\n className={cn(\n 'pointer-events-none -mx-1 my-1 h-px bg-gray-a3',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {\n return (\n <SelectPrimitive.ScrollUpArrow\n data-slot=\"select-scroll-up-button\"\n className={cn(\n 'top-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <MaterialSymbol className=\"rotate-180 text-[14px]\" name=\"expand_more\" />\n </SelectPrimitive.ScrollUpArrow>\n )\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {\n return (\n <SelectPrimitive.ScrollDownArrow\n data-slot=\"select-scroll-down-button\"\n className={cn(\n 'bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <MaterialSymbol className=\"text-[14px]\" name=\"expand_more\" />\n </SelectPrimitive.ScrollDownArrow>\n )\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"select.cjs","names":["SelectPrimitive","useFieldControl","boxChrome","MaterialSymbol"],"sources":["../../src/components/select.tsx"],"sourcesContent":["'use client'\n\nimport { Select as SelectPrimitive } from '@base-ui/react/select'\nimport * as React from 'react'\nimport { MaterialSymbol } from '../icons/material-symbol'\nimport { boxChrome } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\n// bare re-export: Root renders no element and its props are generic over Value\nconst Select = SelectPrimitive.Root\n\nfunction SelectGroup({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return (\n <SelectPrimitive.Group\n data-slot=\"select-group\"\n className={cn('scroll-my-1 p-1', className)}\n {...props}\n />\n )\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />\n}\n\nconst TRIGGER_BASE =\n 'relative flex items-center justify-between gap-1.5 rounded-lg px-3 text-base whitespace-nowrap text-gray outline-hidden transition-colors select-none data-placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4'\n\n// box-chrome sizing only — a ghost/inset trigger sizes via boxChrome.ghost's\n// GHOST_UNDERLINE (h-10), matching Input's underline row exactly (no competing\n// height utility). `min-h-[2.75rem]` is a literal, non-knob-scaled WCAG 2.5.5\n// floor; `sm` stays visually compact but restores the floor via an invisible\n// `before:` hit overlay rather than growing the box, with `my-1` to keep that\n// overlay out of an ancestor scroller's overflow.\nconst TRIGGER_SIZE = {\n default: 'h-11 min-h-[2.75rem]',\n sm: \"my-1 h-9 before:absolute before:inset-x-0 before:top-1/2 before:min-h-[2.75rem] before:-translate-y-1/2 before:content-['']\",\n} as const\n\nfunction SelectTrigger({\n className,\n size = 'default',\n variant,\n id: idProp,\n children,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: 'sm' | 'default'\n // input-style override; falls back to Field / <Theme>, then soft (like Input)\n variant?: SparkInputStyle\n}) {\n const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant,\n })\n // inside an inset field the shell is the box, so the trigger goes bare\n // (underline row), matching Input's inset chrome; an explicit variant keeps\n // the box even inside an inset field (mirrors Input)\n const isGhost =\n inputStyle === 'ghost' || (!variant && field?.labelPosition === 'inset')\n\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n id={id}\n className={cn(\n TRIGGER_BASE,\n // fills the field shell like an Input; sizes to content when standalone\n field ? 'w-full' : 'w-fit',\n !isGhost && TRIGGER_SIZE[size],\n boxChrome[isGhost ? 'ghost' : inputStyle][inputColor],\n className,\n )}\n aria-invalid={ariaInvalid}\n {...props}\n aria-describedby={ariaDescribedBy}\n >\n {children}\n <SelectPrimitive.Icon\n render={\n <MaterialSymbol\n className=\"pointer-events-none text-[14px] text-gray-light\"\n name=\"expand_more\"\n />\n }\n />\n </SelectPrimitive.Trigger>\n )\n}\n\ntype SelectContentProps = React.ComponentProps<typeof SelectPrimitive.Popup> &\n Pick<\n SelectPrimitive.Positioner.Props,\n 'align' | 'alignOffset' | 'side' | 'sideOffset' | 'alignItemWithTrigger'\n > &\n Pick<SelectPrimitive.Portal.Props, 'container'>\n\nfunction SelectContent({\n className,\n children,\n align = 'start',\n alignOffset,\n side,\n sideOffset = 4,\n // drop the menu below the trigger (left-aligned) like a conventional form\n // dropdown; the native-<select>-style overlay (Base UI's inherited default)\n // shifts the popup to align item text with trigger text, exposing a sliver of\n // the trigger's fill at the edge. Opt back in per instance if you want it.\n alignItemWithTrigger = false,\n container,\n ...props\n}: SelectContentProps) {\n return (\n <SelectPrimitive.Portal container={container}>\n <SelectPrimitive.Positioner\n align={align}\n alignItemWithTrigger={alignItemWithTrigger}\n alignOffset={alignOffset}\n side={side}\n sideOffset={sideOffset}\n >\n <SelectPrimitive.Popup\n data-slot=\"select-content\"\n data-align-trigger={alignItemWithTrigger}\n className={cn(\n // match the trigger's width (never narrower) so the drop-below menu\n // lines up under it; --anchor-width is the trigger width, floored at\n // 9rem for very narrow triggers\n 'relative isolate z-50 max-h-(--available-height) min-w-[max(9rem,var(--anchor-width))] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-gray-a1 bg-gray-1 text-gray-a12 shadow-lg',\n 'transition-[opacity,scale,translate] duration-100 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',\n 'data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2',\n 'data-[align-trigger=true]:transition-none',\n className,\n )}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.List>{children}</SelectPrimitive.List>\n <SelectScrollDownButton />\n </SelectPrimitive.Popup>\n </SelectPrimitive.Positioner>\n </SelectPrimitive.Portal>\n )\n}\n\nfunction SelectLabel({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.GroupLabel>) {\n return (\n <SelectPrimitive.GroupLabel\n data-slot=\"select-label\"\n className={cn('px-1.5 py-1 text-xs text-gray-light', className)}\n {...props}\n />\n )\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n 'relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1.5 pr-8 pl-1.5 text-sm text-gray outline-hidden transition-colors select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-gray-a3 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2',\n className,\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute right-2 flex size-4 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <MaterialSymbol className=\"text-[11px]\" name=\"check\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\n// plain div, not Select.Separator: role=\"separator\" isn't a permitted child of a\n// listbox (aria-required-children), and the divider is decorative — the groups\n// carry the semantics. Hidden from assistive tech.\nfunction SelectSeparator({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n aria-hidden=\"true\"\n data-slot=\"select-separator\"\n className={cn(\n 'pointer-events-none -mx-1 my-1 h-px bg-gray-a3',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {\n return (\n <SelectPrimitive.ScrollUpArrow\n data-slot=\"select-scroll-up-button\"\n className={cn(\n 'top-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <MaterialSymbol className=\"rotate-180 text-[14px]\" name=\"expand_more\" />\n </SelectPrimitive.ScrollUpArrow>\n )\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {\n return (\n <SelectPrimitive.ScrollDownArrow\n data-slot=\"select-scroll-down-button\"\n className={cn(\n 'bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <MaterialSymbol className=\"text-[14px]\" name=\"expand_more\" />\n </SelectPrimitive.ScrollDownArrow>\n )\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n"],"mappings":"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"}
@@ -28,7 +28,7 @@ const TRIGGER_SIZE = {
28
28
  sm: "my-1 h-9 before:absolute before:inset-x-0 before:top-1/2 before:min-h-[2.75rem] before:-translate-y-1/2 before:content-['']"
29
29
  };
30
30
  function SelectTrigger({ className, size = "default", variant, id: idProp, children, "aria-describedby": ariaDescribedByProp, ...props }) {
31
- const { field, id, ariaDescribedBy, inputStyle, inputColor } = useFieldControl({
31
+ const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } = useFieldControl({
32
32
  id: idProp,
33
33
  ariaDescribedBy: ariaDescribedByProp,
34
34
  variant
@@ -39,6 +39,7 @@ function SelectTrigger({ className, size = "default", variant, id: idProp, child
39
39
  "data-size": size,
40
40
  id,
41
41
  className: cn(TRIGGER_BASE, field ? "w-full" : "w-fit", !isGhost && TRIGGER_SIZE[size], boxChrome[isGhost ? "ghost" : inputStyle][inputColor], className),
42
+ "aria-invalid": ariaInvalid,
42
43
  ...props,
43
44
  "aria-describedby": ariaDescribedBy,
44
45
  children: [children, /* @__PURE__ */ jsx(Select$1.Icon, { render: /* @__PURE__ */ jsx(MaterialSymbol, {
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","names":["SelectPrimitive"],"sources":["../../src/components/select.tsx"],"sourcesContent":["'use client'\n\nimport { Select as SelectPrimitive } from '@base-ui/react/select'\nimport * as React from 'react'\nimport { MaterialSymbol } from '../icons/material-symbol'\nimport { boxChrome } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\n// bare re-export: Root renders no element and its props are generic over Value\nconst Select = SelectPrimitive.Root\n\nfunction SelectGroup({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return (\n <SelectPrimitive.Group\n data-slot=\"select-group\"\n className={cn('scroll-my-1 p-1', className)}\n {...props}\n />\n )\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />\n}\n\nconst TRIGGER_BASE =\n 'relative flex items-center justify-between gap-1.5 rounded-lg px-3 text-base whitespace-nowrap text-gray outline-hidden transition-colors select-none data-placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4'\n\n// box-chrome sizing only — a ghost/inset trigger sizes via boxChrome.ghost's\n// GHOST_UNDERLINE (h-10), matching Input's underline row exactly (no competing\n// height utility). `min-h-[2.75rem]` is a literal, non-knob-scaled WCAG 2.5.5\n// floor; `sm` stays visually compact but restores the floor via an invisible\n// `before:` hit overlay rather than growing the box, with `my-1` to keep that\n// overlay out of an ancestor scroller's overflow.\nconst TRIGGER_SIZE = {\n default: 'h-11 min-h-[2.75rem]',\n sm: \"my-1 h-9 before:absolute before:inset-x-0 before:top-1/2 before:min-h-[2.75rem] before:-translate-y-1/2 before:content-['']\",\n} as const\n\nfunction SelectTrigger({\n className,\n size = 'default',\n variant,\n id: idProp,\n children,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: 'sm' | 'default'\n // input-style override; falls back to Field / <Theme>, then soft (like Input)\n variant?: SparkInputStyle\n}) {\n const { field, id, ariaDescribedBy, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant,\n })\n // inside an inset field the shell is the box, so the trigger goes bare\n // (underline row), matching Input's inset chrome; an explicit variant keeps\n // the box even inside an inset field (mirrors Input)\n const isGhost =\n inputStyle === 'ghost' || (!variant && field?.labelPosition === 'inset')\n\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n id={id}\n className={cn(\n TRIGGER_BASE,\n // fills the field shell like an Input; sizes to content when standalone\n field ? 'w-full' : 'w-fit',\n !isGhost && TRIGGER_SIZE[size],\n boxChrome[isGhost ? 'ghost' : inputStyle][inputColor],\n className,\n )}\n {...props}\n aria-describedby={ariaDescribedBy}\n >\n {children}\n <SelectPrimitive.Icon\n render={\n <MaterialSymbol\n className=\"pointer-events-none text-[14px] text-gray-light\"\n name=\"expand_more\"\n />\n }\n />\n </SelectPrimitive.Trigger>\n )\n}\n\ntype SelectContentProps = React.ComponentProps<typeof SelectPrimitive.Popup> &\n Pick<\n SelectPrimitive.Positioner.Props,\n 'align' | 'alignOffset' | 'side' | 'sideOffset' | 'alignItemWithTrigger'\n > &\n Pick<SelectPrimitive.Portal.Props, 'container'>\n\nfunction SelectContent({\n className,\n children,\n align = 'start',\n alignOffset,\n side,\n sideOffset = 4,\n // drop the menu below the trigger (left-aligned) like a conventional form\n // dropdown; the native-<select>-style overlay (Base UI's inherited default)\n // shifts the popup to align item text with trigger text, exposing a sliver of\n // the trigger's fill at the edge. Opt back in per instance if you want it.\n alignItemWithTrigger = false,\n container,\n ...props\n}: SelectContentProps) {\n return (\n <SelectPrimitive.Portal container={container}>\n <SelectPrimitive.Positioner\n align={align}\n alignItemWithTrigger={alignItemWithTrigger}\n alignOffset={alignOffset}\n side={side}\n sideOffset={sideOffset}\n >\n <SelectPrimitive.Popup\n data-slot=\"select-content\"\n data-align-trigger={alignItemWithTrigger}\n className={cn(\n // match the trigger's width (never narrower) so the drop-below menu\n // lines up under it; --anchor-width is the trigger width, floored at\n // 9rem for very narrow triggers\n 'relative isolate z-50 max-h-(--available-height) min-w-[max(9rem,var(--anchor-width))] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-gray-a1 bg-gray-1 text-gray-a12 shadow-lg',\n 'transition-[opacity,scale,translate] duration-100 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',\n 'data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2',\n 'data-[align-trigger=true]:transition-none',\n className,\n )}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.List>{children}</SelectPrimitive.List>\n <SelectScrollDownButton />\n </SelectPrimitive.Popup>\n </SelectPrimitive.Positioner>\n </SelectPrimitive.Portal>\n )\n}\n\nfunction SelectLabel({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.GroupLabel>) {\n return (\n <SelectPrimitive.GroupLabel\n data-slot=\"select-label\"\n className={cn('px-1.5 py-1 text-xs text-gray-light', className)}\n {...props}\n />\n )\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n 'relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1.5 pr-8 pl-1.5 text-sm text-gray outline-hidden transition-colors select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-gray-a3 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2',\n className,\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute right-2 flex size-4 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <MaterialSymbol className=\"text-[11px]\" name=\"check\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\n// plain div, not Select.Separator: role=\"separator\" isn't a permitted child of a\n// listbox (aria-required-children), and the divider is decorative — the groups\n// carry the semantics. Hidden from assistive tech.\nfunction SelectSeparator({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n aria-hidden=\"true\"\n data-slot=\"select-separator\"\n className={cn(\n 'pointer-events-none -mx-1 my-1 h-px bg-gray-a3',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {\n return (\n <SelectPrimitive.ScrollUpArrow\n data-slot=\"select-scroll-up-button\"\n className={cn(\n 'top-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <MaterialSymbol className=\"rotate-180 text-[14px]\" name=\"expand_more\" />\n </SelectPrimitive.ScrollUpArrow>\n )\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {\n return (\n <SelectPrimitive.ScrollDownArrow\n data-slot=\"select-scroll-down-button\"\n className={cn(\n 'bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <MaterialSymbol className=\"text-[14px]\" name=\"expand_more\" />\n </SelectPrimitive.ScrollDownArrow>\n )\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"select.js","names":["SelectPrimitive"],"sources":["../../src/components/select.tsx"],"sourcesContent":["'use client'\n\nimport { Select as SelectPrimitive } from '@base-ui/react/select'\nimport * as React from 'react'\nimport { MaterialSymbol } from '../icons/material-symbol'\nimport { boxChrome } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\n// bare re-export: Root renders no element and its props are generic over Value\nconst Select = SelectPrimitive.Root\n\nfunction SelectGroup({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return (\n <SelectPrimitive.Group\n data-slot=\"select-group\"\n className={cn('scroll-my-1 p-1', className)}\n {...props}\n />\n )\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />\n}\n\nconst TRIGGER_BASE =\n 'relative flex items-center justify-between gap-1.5 rounded-lg px-3 text-base whitespace-nowrap text-gray outline-hidden transition-colors select-none data-placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4'\n\n// box-chrome sizing only — a ghost/inset trigger sizes via boxChrome.ghost's\n// GHOST_UNDERLINE (h-10), matching Input's underline row exactly (no competing\n// height utility). `min-h-[2.75rem]` is a literal, non-knob-scaled WCAG 2.5.5\n// floor; `sm` stays visually compact but restores the floor via an invisible\n// `before:` hit overlay rather than growing the box, with `my-1` to keep that\n// overlay out of an ancestor scroller's overflow.\nconst TRIGGER_SIZE = {\n default: 'h-11 min-h-[2.75rem]',\n sm: \"my-1 h-9 before:absolute before:inset-x-0 before:top-1/2 before:min-h-[2.75rem] before:-translate-y-1/2 before:content-['']\",\n} as const\n\nfunction SelectTrigger({\n className,\n size = 'default',\n variant,\n id: idProp,\n children,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: 'sm' | 'default'\n // input-style override; falls back to Field / <Theme>, then soft (like Input)\n variant?: SparkInputStyle\n}) {\n const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant,\n })\n // inside an inset field the shell is the box, so the trigger goes bare\n // (underline row), matching Input's inset chrome; an explicit variant keeps\n // the box even inside an inset field (mirrors Input)\n const isGhost =\n inputStyle === 'ghost' || (!variant && field?.labelPosition === 'inset')\n\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n id={id}\n className={cn(\n TRIGGER_BASE,\n // fills the field shell like an Input; sizes to content when standalone\n field ? 'w-full' : 'w-fit',\n !isGhost && TRIGGER_SIZE[size],\n boxChrome[isGhost ? 'ghost' : inputStyle][inputColor],\n className,\n )}\n aria-invalid={ariaInvalid}\n {...props}\n aria-describedby={ariaDescribedBy}\n >\n {children}\n <SelectPrimitive.Icon\n render={\n <MaterialSymbol\n className=\"pointer-events-none text-[14px] text-gray-light\"\n name=\"expand_more\"\n />\n }\n />\n </SelectPrimitive.Trigger>\n )\n}\n\ntype SelectContentProps = React.ComponentProps<typeof SelectPrimitive.Popup> &\n Pick<\n SelectPrimitive.Positioner.Props,\n 'align' | 'alignOffset' | 'side' | 'sideOffset' | 'alignItemWithTrigger'\n > &\n Pick<SelectPrimitive.Portal.Props, 'container'>\n\nfunction SelectContent({\n className,\n children,\n align = 'start',\n alignOffset,\n side,\n sideOffset = 4,\n // drop the menu below the trigger (left-aligned) like a conventional form\n // dropdown; the native-<select>-style overlay (Base UI's inherited default)\n // shifts the popup to align item text with trigger text, exposing a sliver of\n // the trigger's fill at the edge. Opt back in per instance if you want it.\n alignItemWithTrigger = false,\n container,\n ...props\n}: SelectContentProps) {\n return (\n <SelectPrimitive.Portal container={container}>\n <SelectPrimitive.Positioner\n align={align}\n alignItemWithTrigger={alignItemWithTrigger}\n alignOffset={alignOffset}\n side={side}\n sideOffset={sideOffset}\n >\n <SelectPrimitive.Popup\n data-slot=\"select-content\"\n data-align-trigger={alignItemWithTrigger}\n className={cn(\n // match the trigger's width (never narrower) so the drop-below menu\n // lines up under it; --anchor-width is the trigger width, floored at\n // 9rem for very narrow triggers\n 'relative isolate z-50 max-h-(--available-height) min-w-[max(9rem,var(--anchor-width))] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border border-gray-a1 bg-gray-1 text-gray-a12 shadow-lg',\n 'transition-[opacity,scale,translate] duration-100 data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0 motion-reduce:transition-none',\n 'data-[side=bottom]:data-starting-style:-translate-y-2 data-[side=left]:data-starting-style:translate-x-2 data-[side=right]:data-starting-style:-translate-x-2 data-[side=top]:data-starting-style:translate-y-2',\n 'data-[align-trigger=true]:transition-none',\n className,\n )}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.List>{children}</SelectPrimitive.List>\n <SelectScrollDownButton />\n </SelectPrimitive.Popup>\n </SelectPrimitive.Positioner>\n </SelectPrimitive.Portal>\n )\n}\n\nfunction SelectLabel({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.GroupLabel>) {\n return (\n <SelectPrimitive.GroupLabel\n data-slot=\"select-label\"\n className={cn('px-1.5 py-1 text-xs text-gray-light', className)}\n {...props}\n />\n )\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n 'relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1.5 pr-8 pl-1.5 text-sm text-gray outline-hidden transition-colors select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-gray-a3 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2',\n className,\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute right-2 flex size-4 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <MaterialSymbol className=\"text-[11px]\" name=\"check\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\n// plain div, not Select.Separator: role=\"separator\" isn't a permitted child of a\n// listbox (aria-required-children), and the divider is decorative — the groups\n// carry the semantics. Hidden from assistive tech.\nfunction SelectSeparator({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n aria-hidden=\"true\"\n data-slot=\"select-separator\"\n className={cn(\n 'pointer-events-none -mx-1 my-1 h-px bg-gray-a3',\n className,\n )}\n {...props}\n />\n )\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {\n return (\n <SelectPrimitive.ScrollUpArrow\n data-slot=\"select-scroll-up-button\"\n className={cn(\n 'top-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <MaterialSymbol className=\"rotate-180 text-[14px]\" name=\"expand_more\" />\n </SelectPrimitive.ScrollUpArrow>\n )\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {\n return (\n <SelectPrimitive.ScrollDownArrow\n data-slot=\"select-scroll-down-button\"\n className={cn(\n 'bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-gray-1 py-1 [&_svg:not([class*=size-])]:size-4',\n className,\n )}\n {...props}\n >\n <MaterialSymbol className=\"text-[14px]\" name=\"expand_more\" />\n </SelectPrimitive.ScrollDownArrow>\n )\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n}\n"],"mappings":"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"}
@@ -102,7 +102,7 @@ const textareaVariants = (0, class_variance_authority.cva)("flex w-full min-w-0
102
102
  ]
103
103
  });
104
104
  const Textarea = react.forwardRef(function Textarea({ variant, autoResize = false, rows = 3, className, id: idProp, "aria-describedby": ariaDescribedByProp, ...props }, ref) {
105
- const { field, id, ariaDescribedBy, inputStyle, inputColor } = require_components_field.useFieldControl({
105
+ const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } = require_components_field.useFieldControl({
106
106
  id: idProp,
107
107
  ariaDescribedBy: ariaDescribedByProp,
108
108
  variant: variant !== "none" ? variant : void 0
@@ -118,6 +118,7 @@ const Textarea = react.forwardRef(function Textarea({ variant, autoResize = fals
118
118
  id,
119
119
  rows,
120
120
  ref,
121
+ "aria-invalid": ariaInvalid,
121
122
  ...props,
122
123
  "aria-describedby": ariaDescribedBy
123
124
  });
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.cjs","names":["GHOST_UNDERLINE","boxChrome","React","useFieldControl"],"sources":["../../src/components/textarea.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\ntype TextareaVariant = SparkInputStyle | 'none'\n\nconst textareaVariants = cva(\n 'flex w-full min-w-0 bg-transparent text-gray outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none',\n {\n variants: {\n chrome: {\n none: 'border-0 px-0',\n inset: GHOST_UNDERLINE,\n box: 'min-h-11 rounded-lg px-3 py-2 text-base transition-all motion-reduce:transition-none',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n autoResize: {\n true: 'field-sizing-content resize-none',\n false: 'resize-y',\n },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n // GHOST_UNDERLINE and boxChrome.ghost.* hardcode h-10; un-clamp for\n // textarea's min-height model — must come after those compounds above\n { chrome: 'inset', class: 'h-auto min-h-10' },\n { chrome: 'box', inputStyle: 'ghost', class: 'h-auto' },\n ],\n },\n)\n\ninterface TextareaProps extends React.ComponentProps<'textarea'> {\n variant?: TextareaVariant\n autoResize?: boolean\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n function Textarea(\n {\n variant,\n autoResize = false,\n rows = 3,\n className,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n },\n ref,\n ) {\n const { field, id, ariaDescribedBy, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant: variant !== 'none' ? variant : undefined,\n })\n // an explicit variant prop keeps the box even inside an inset field\n const chrome =\n variant === 'none'\n ? 'none'\n : !variant && field?.labelPosition === 'inset'\n ? 'inset'\n : 'box'\n\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n textareaVariants({ chrome, inputStyle, inputColor, autoResize }),\n className,\n )}\n id={id}\n rows={rows}\n ref={ref}\n {...props}\n aria-describedby={ariaDescribedBy}\n />\n )\n },\n)\n\nexport { Textarea }\nexport type { TextareaProps, TextareaVariant }\n"],"mappings":";;;;;;;;;;;AASA,MAAM,oBAAA,GAAA,yBAAA,KACJ,sQACA;CACE,UAAU;EACR,QAAQ;GACN,MAAM;GACN,OAAOA,+BAAAA;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;EAClC,YAAY;GACV,MAAM;GACN,OAAO;EACT;CACF;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOC,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;EAGpE;GAAE,QAAQ;GAAS,OAAO;EAAkB;EAC5C;GAAE,QAAQ;GAAO,YAAY;GAAS,OAAO;EAAS;CACxD;AACF,CACF;AAOA,MAAM,WAAWC,MAAM,WACrB,SAAS,SACP,EACE,SACA,aAAa,OACb,OAAO,GACP,WACA,IAAI,QACJ,oBAAoB,qBACpB,GAAG,SAEL,KACA;CACA,MAAM,EAAE,OAAO,IAAI,iBAAiB,YAAY,eAC9CC,yBAAAA,gBAAgB;EACd,IAAI;EACJ,iBAAiB;EACjB,SAAS,YAAY,SAAS,UAAU,KAAA;CAC1C,CAAC;CASH,OACE,iBAAA,GAAA,kBAAA,KAAC,YAAD;EACE,aAAU;EACV,YAAA,GAAA,OAAA,IACE,iBAAiB;GAAE,QAVvB,YAAY,SACR,SACA,CAAC,WAAW,OAAO,kBAAkB,UACnC,UACA;GAMyB;GAAY;GAAY;EAAW,CAAC,GAC/D,SACF;EACI;EACE;EACD;EACL,GAAI;EACJ,oBAAkB;CACnB,CAAA;AAEL,CACF"}
1
+ {"version":3,"file":"textarea.cjs","names":["GHOST_UNDERLINE","boxChrome","React","useFieldControl"],"sources":["../../src/components/textarea.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\ntype TextareaVariant = SparkInputStyle | 'none'\n\nconst textareaVariants = cva(\n 'flex w-full min-w-0 bg-transparent text-gray outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none',\n {\n variants: {\n chrome: {\n none: 'border-0 px-0',\n inset: GHOST_UNDERLINE,\n box: 'min-h-11 rounded-lg px-3 py-2 text-base transition-all motion-reduce:transition-none',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n autoResize: {\n true: 'field-sizing-content resize-none',\n false: 'resize-y',\n },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n // GHOST_UNDERLINE and boxChrome.ghost.* hardcode h-10; un-clamp for\n // textarea's min-height model — must come after those compounds above\n { chrome: 'inset', class: 'h-auto min-h-10' },\n { chrome: 'box', inputStyle: 'ghost', class: 'h-auto' },\n ],\n },\n)\n\ninterface TextareaProps extends React.ComponentProps<'textarea'> {\n variant?: TextareaVariant\n autoResize?: boolean\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n function Textarea(\n {\n variant,\n autoResize = false,\n rows = 3,\n className,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n },\n ref,\n ) {\n const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant: variant !== 'none' ? variant : undefined,\n })\n // an explicit variant prop keeps the box even inside an inset field\n const chrome =\n variant === 'none'\n ? 'none'\n : !variant && field?.labelPosition === 'inset'\n ? 'inset'\n : 'box'\n\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n textareaVariants({ chrome, inputStyle, inputColor, autoResize }),\n className,\n )}\n id={id}\n rows={rows}\n ref={ref}\n aria-invalid={ariaInvalid}\n {...props}\n aria-describedby={ariaDescribedBy}\n />\n )\n },\n)\n\nexport { Textarea }\nexport type { TextareaProps, TextareaVariant }\n"],"mappings":";;;;;;;;;;;AASA,MAAM,oBAAA,GAAA,yBAAA,KACJ,sQACA;CACE,UAAU;EACR,QAAQ;GACN,MAAM;GACN,OAAOA,+BAAAA;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;EAClC,YAAY;GACV,MAAM;GACN,OAAO;EACT;CACF;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOC,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAOA,+BAAAA,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;EAGpE;GAAE,QAAQ;GAAS,OAAO;EAAkB;EAC5C;GAAE,QAAQ;GAAO,YAAY;GAAS,OAAO;EAAS;CACxD;AACF,CACF;AAOA,MAAM,WAAWC,MAAM,WACrB,SAAS,SACP,EACE,SACA,aAAa,OACb,OAAO,GACP,WACA,IAAI,QACJ,oBAAoB,qBACpB,GAAG,SAEL,KACA;CACA,MAAM,EAAE,OAAO,IAAI,iBAAiB,aAAa,YAAY,eAC3DC,yBAAAA,gBAAgB;EACd,IAAI;EACJ,iBAAiB;EACjB,SAAS,YAAY,SAAS,UAAU,KAAA;CAC1C,CAAC;CASH,OACE,iBAAA,GAAA,kBAAA,KAAC,YAAD;EACE,aAAU;EACV,YAAA,GAAA,OAAA,IACE,iBAAiB;GAAE,QAVvB,YAAY,SACR,SACA,CAAC,WAAW,OAAO,kBAAkB,UACnC,UACA;GAMyB;GAAY;GAAY;EAAW,CAAC,GAC/D,SACF;EACI;EACE;EACD;EACL,gBAAc;EACd,GAAI;EACJ,oBAAkB;CACnB,CAAA;AAEL,CACF"}
@@ -99,7 +99,7 @@ const textareaVariants = cva("flex w-full min-w-0 bg-transparent text-gray outli
99
99
  ]
100
100
  });
101
101
  const Textarea = React$1.forwardRef(function Textarea({ variant, autoResize = false, rows = 3, className, id: idProp, "aria-describedby": ariaDescribedByProp, ...props }, ref) {
102
- const { field, id, ariaDescribedBy, inputStyle, inputColor } = useFieldControl({
102
+ const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } = useFieldControl({
103
103
  id: idProp,
104
104
  ariaDescribedBy: ariaDescribedByProp,
105
105
  variant: variant !== "none" ? variant : void 0
@@ -115,6 +115,7 @@ const Textarea = React$1.forwardRef(function Textarea({ variant, autoResize = fa
115
115
  id,
116
116
  rows,
117
117
  ref,
118
+ "aria-invalid": ariaInvalid,
118
119
  ...props,
119
120
  "aria-describedby": ariaDescribedBy
120
121
  });
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.js","names":["React"],"sources":["../../src/components/textarea.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\ntype TextareaVariant = SparkInputStyle | 'none'\n\nconst textareaVariants = cva(\n 'flex w-full min-w-0 bg-transparent text-gray outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none',\n {\n variants: {\n chrome: {\n none: 'border-0 px-0',\n inset: GHOST_UNDERLINE,\n box: 'min-h-11 rounded-lg px-3 py-2 text-base transition-all motion-reduce:transition-none',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n autoResize: {\n true: 'field-sizing-content resize-none',\n false: 'resize-y',\n },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n // GHOST_UNDERLINE and boxChrome.ghost.* hardcode h-10; un-clamp for\n // textarea's min-height model — must come after those compounds above\n { chrome: 'inset', class: 'h-auto min-h-10' },\n { chrome: 'box', inputStyle: 'ghost', class: 'h-auto' },\n ],\n },\n)\n\ninterface TextareaProps extends React.ComponentProps<'textarea'> {\n variant?: TextareaVariant\n autoResize?: boolean\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n function Textarea(\n {\n variant,\n autoResize = false,\n rows = 3,\n className,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n },\n ref,\n ) {\n const { field, id, ariaDescribedBy, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant: variant !== 'none' ? variant : undefined,\n })\n // an explicit variant prop keeps the box even inside an inset field\n const chrome =\n variant === 'none'\n ? 'none'\n : !variant && field?.labelPosition === 'inset'\n ? 'inset'\n : 'box'\n\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n textareaVariants({ chrome, inputStyle, inputColor, autoResize }),\n className,\n )}\n id={id}\n rows={rows}\n ref={ref}\n {...props}\n aria-describedby={ariaDescribedBy}\n />\n )\n },\n)\n\nexport { Textarea }\nexport type { TextareaProps, TextareaVariant }\n"],"mappings":";;;;;;;;AASA,MAAM,mBAAmB,IACvB,sQACA;CACE,UAAU;EACR,QAAQ;GACN,MAAM;GACN,OAAO;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;EAClC,YAAY;GACV,MAAM;GACN,OAAO;EACT;CACF;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;EAGpE;GAAE,QAAQ;GAAS,OAAO;EAAkB;EAC5C;GAAE,QAAQ;GAAO,YAAY;GAAS,OAAO;EAAS;CACxD;AACF,CACF;AAOA,MAAM,WAAWA,QAAM,WACrB,SAAS,SACP,EACE,SACA,aAAa,OACb,OAAO,GACP,WACA,IAAI,QACJ,oBAAoB,qBACpB,GAAG,SAEL,KACA;CACA,MAAM,EAAE,OAAO,IAAI,iBAAiB,YAAY,eAC9C,gBAAgB;EACd,IAAI;EACJ,iBAAiB;EACjB,SAAS,YAAY,SAAS,UAAU,KAAA;CAC1C,CAAC;CASH,OACE,oBAAC,YAAD;EACE,aAAU;EACV,WAAW,GACT,iBAAiB;GAAE,QAVvB,YAAY,SACR,SACA,CAAC,WAAW,OAAO,kBAAkB,UACnC,UACA;GAMyB;GAAY;GAAY;EAAW,CAAC,GAC/D,SACF;EACI;EACE;EACD;EACL,GAAI;EACJ,oBAAkB;CACnB,CAAA;AAEL,CACF"}
1
+ {"version":3,"file":"textarea.js","names":["React"],"sources":["../../src/components/textarea.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority'\nimport * as React from 'react'\nimport { boxChrome, GHOST_UNDERLINE } from '../lib/input-box-variants'\nimport type { SparkInputStyle } from '../lib/theme-types'\nimport { cn } from '../utils/cn'\nimport { useFieldControl } from './field'\n\ntype TextareaVariant = SparkInputStyle | 'none'\n\nconst textareaVariants = cva(\n 'flex w-full min-w-0 bg-transparent text-gray outline-hidden transition-all group-data-[invalid=true]/field:text-error placeholder:text-gray-disabled disabled:cursor-not-allowed disabled:text-gray-disabled aria-invalid:text-error motion-reduce:transition-none',\n {\n variants: {\n chrome: {\n none: 'border-0 px-0',\n inset: GHOST_UNDERLINE,\n box: 'min-h-11 rounded-lg px-3 py-2 text-base transition-all motion-reduce:transition-none',\n },\n inputStyle: { soft: '', surface: '', outlined: '', ghost: '' },\n inputColor: { brand: '', gray: '' },\n autoResize: {\n true: 'field-sizing-content resize-none',\n false: 'resize-y',\n },\n },\n compoundVariants: [\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'brand',\n class: boxChrome.soft.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'soft',\n inputColor: 'gray',\n class: boxChrome.soft.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'brand',\n class: boxChrome.surface.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'surface',\n inputColor: 'gray',\n class: boxChrome.surface.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'brand',\n class: boxChrome.outlined.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'outlined',\n inputColor: 'gray',\n class: boxChrome.outlined.gray,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'brand',\n class: boxChrome.ghost.brand,\n },\n {\n chrome: 'box',\n inputStyle: 'ghost',\n inputColor: 'gray',\n class: boxChrome.ghost.gray,\n },\n { chrome: 'inset', inputColor: 'brand', class: 'border-b-input' },\n { chrome: 'inset', inputColor: 'gray', class: 'border-b-input-gray' },\n // GHOST_UNDERLINE and boxChrome.ghost.* hardcode h-10; un-clamp for\n // textarea's min-height model — must come after those compounds above\n { chrome: 'inset', class: 'h-auto min-h-10' },\n { chrome: 'box', inputStyle: 'ghost', class: 'h-auto' },\n ],\n },\n)\n\ninterface TextareaProps extends React.ComponentProps<'textarea'> {\n variant?: TextareaVariant\n autoResize?: boolean\n}\n\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n function Textarea(\n {\n variant,\n autoResize = false,\n rows = 3,\n className,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n ...props\n },\n ref,\n ) {\n const { field, id, ariaDescribedBy, ariaInvalid, inputStyle, inputColor } =\n useFieldControl({\n id: idProp,\n ariaDescribedBy: ariaDescribedByProp,\n variant: variant !== 'none' ? variant : undefined,\n })\n // an explicit variant prop keeps the box even inside an inset field\n const chrome =\n variant === 'none'\n ? 'none'\n : !variant && field?.labelPosition === 'inset'\n ? 'inset'\n : 'box'\n\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n textareaVariants({ chrome, inputStyle, inputColor, autoResize }),\n className,\n )}\n id={id}\n rows={rows}\n ref={ref}\n aria-invalid={ariaInvalid}\n {...props}\n aria-describedby={ariaDescribedBy}\n />\n )\n },\n)\n\nexport { Textarea }\nexport type { TextareaProps, TextareaVariant }\n"],"mappings":";;;;;;;;AASA,MAAM,mBAAmB,IACvB,sQACA;CACE,UAAU;EACR,QAAQ;GACN,MAAM;GACN,OAAO;GACP,KAAK;EACP;EACA,YAAY;GAAE,MAAM;GAAI,SAAS;GAAI,UAAU;GAAI,OAAO;EAAG;EAC7D,YAAY;GAAE,OAAO;GAAI,MAAM;EAAG;EAClC,YAAY;GACV,MAAM;GACN,OAAO;EACT;CACF;CACA,kBAAkB;EAChB;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,KAAK;EACxB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,QAAQ;EAC3B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,SAAS;EAC5B;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GACE,QAAQ;GACR,YAAY;GACZ,YAAY;GACZ,OAAO,UAAU,MAAM;EACzB;EACA;GAAE,QAAQ;GAAS,YAAY;GAAS,OAAO;EAAiB;EAChE;GAAE,QAAQ;GAAS,YAAY;GAAQ,OAAO;EAAsB;EAGpE;GAAE,QAAQ;GAAS,OAAO;EAAkB;EAC5C;GAAE,QAAQ;GAAO,YAAY;GAAS,OAAO;EAAS;CACxD;AACF,CACF;AAOA,MAAM,WAAWA,QAAM,WACrB,SAAS,SACP,EACE,SACA,aAAa,OACb,OAAO,GACP,WACA,IAAI,QACJ,oBAAoB,qBACpB,GAAG,SAEL,KACA;CACA,MAAM,EAAE,OAAO,IAAI,iBAAiB,aAAa,YAAY,eAC3D,gBAAgB;EACd,IAAI;EACJ,iBAAiB;EACjB,SAAS,YAAY,SAAS,UAAU,KAAA;CAC1C,CAAC;CASH,OACE,oBAAC,YAAD;EACE,aAAU;EACV,WAAW,GACT,iBAAiB;GAAE,QAVvB,YAAY,SACR,SACA,CAAC,WAAW,OAAO,kBAAkB,UACnC,UACA;GAMyB;GAAY;GAAY;EAAW,CAAC,GAC/D,SACF;EACI;EACE;EACD;EACL,gBAAc;EACd,GAAI;EACJ,oBAAkB;CACnB,CAAA;AAEL,CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epilot/spark-ui",
3
- "version": "0.15.1",
3
+ "version": "0.15.2",
4
4
  "description": "React UI component library from Spark SDK",
5
5
  "homepage": "https://spark-ui.epilot.io/docs/ui",
6
6
  "license": "Apache-2.0",