@spear-ai/spectral 1.14.3 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Button.d.ts +0 -6
- package/dist/Button.d.ts.map +1 -1
- package/dist/Button.js +36 -51
- package/dist/Button.js.map +1 -1
- package/dist/Checkbox.js +1 -1
- package/dist/Checkbox.js.map +1 -1
- package/dist/Combobox.js +1 -1
- package/dist/Combobox.js.map +1 -1
- package/dist/ControlGroup/ControlGroupSelect.d.ts +1 -1
- package/dist/ControlGroup/ControlGroupSelect.js +1 -1
- package/dist/ControlGroup/ControlGroupSelect.js.map +1 -1
- package/dist/ControlGroup.d.ts.map +1 -1
- package/dist/ControlGroup.js +1 -1
- package/dist/ControlGroup.js.map +1 -1
- package/dist/DateTimePicker.js +1 -1
- package/dist/DateTimePicker.js.map +1 -1
- package/dist/FormFieldMessage.js +1 -1
- package/dist/FormFieldMessage.js.map +1 -1
- package/dist/Input.js +1 -1
- package/dist/Input.js.map +1 -1
- package/dist/InputNumeric.d.ts +4 -2
- package/dist/InputNumeric.d.ts.map +1 -1
- package/dist/InputNumeric.js +3 -1
- package/dist/InputNumeric.js.map +1 -1
- package/dist/InputOTP.js +1 -1
- package/dist/InputOTP.js.map +1 -1
- package/dist/MultiSelect/MultiSelectBase.js +1 -1
- package/dist/MultiSelect/MultiSelectBase.js.map +1 -1
- package/dist/RadioGroup.js +1 -1
- package/dist/RadioGroup.js.map +1 -1
- package/dist/Select.js +1 -1
- package/dist/Select.js.map +1 -1
- package/dist/Switch.js +1 -1
- package/dist/Switch.js.map +1 -1
- package/dist/Textarea.d.ts +1 -1
- package/dist/Textarea.js +1 -1
- package/dist/Textarea.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/styles/spectral.css +1 -1
- package/package.json +1 -1
package/dist/Button.d.ts
CHANGED
|
@@ -15,10 +15,7 @@ interface ButtonProps extends AsChildProp, Omit<ButtonHTMLAttributes<HTMLButtonE
|
|
|
15
15
|
dataTestId?: string;
|
|
16
16
|
disabled?: boolean;
|
|
17
17
|
endIcon?: ReactNode;
|
|
18
|
-
errorMessage?: string | ReactNode;
|
|
19
18
|
label?: string;
|
|
20
|
-
messageReserveLines?: number;
|
|
21
|
-
messageReserveSpace?: boolean;
|
|
22
19
|
onClick?: (event: MouseEvent<HTMLElement>) => void;
|
|
23
20
|
size?: 'small' | 'default' | 'fullWidth';
|
|
24
21
|
startIcon?: ReactNode;
|
|
@@ -33,11 +30,8 @@ declare function Button({
|
|
|
33
30
|
dataTestId,
|
|
34
31
|
disabled,
|
|
35
32
|
endIcon,
|
|
36
|
-
errorMessage,
|
|
37
33
|
id,
|
|
38
34
|
label,
|
|
39
|
-
messageReserveLines,
|
|
40
|
-
messageReserveSpace,
|
|
41
35
|
onClick,
|
|
42
36
|
ref,
|
|
43
37
|
size,
|
package/dist/Button.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.d.ts","names":[],"sources":["../src/components/Button/Button.tsx"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"Button.d.ts","names":[],"sources":["../src/components/Button/Button.tsx"],"mappings":";;;;;;;;cAMM,cAAA,GAAc,KAAA;;;;IA4BnB,iCAAA,CAAA,SAAA;AAAA,UAEgB,WAAA,SAAoB,WAAA,EAAa,IAAA,CAAK,oBAAA,CAAqB,iBAAA,aAA8B,YAAA,QAAoB,cAAA;EAC5H,UAAA;EACA,QAAA;EACA,OAAA,GAAU,SAAA;EACV,KAAA;EACA,OAAA,IAAW,KAAA,EAAO,UAAA,CAAW,WAAA;EAC7B,IAAA;EACA,SAAA,GAAY,SAAA;EACZ,KAAA;EACA,IAAA;EACA,OAAA;AAAA;AAAA;EAIA,OAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,OAAA;EACA,EAAA;EACA,KAAA;EACA,OAAA;EACA,GAAA;EACA,IAAA;EACA,SAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;EAAA,GACG;AAAA,GACF,WAAA;EACD,GAAA,GAAM,GAAA,CAAI,iBAAA;AAAA,IACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA"}
|
package/dist/Button.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
import { LoaderIcon } from "./Icons/LoaderIcon.js";
|
|
3
3
|
import { cn } from "./utils/twUtils.js";
|
|
4
4
|
import { Slot } from "./primitives/slot.js";
|
|
5
|
-
import {
|
|
6
|
-
import { Children, isValidElement, useId } from "react";
|
|
5
|
+
import { Children, isValidElement } from "react";
|
|
7
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
7
|
import { cva } from "class-variance-authority";
|
|
9
8
|
|
|
@@ -34,7 +33,7 @@ const buttonVariants = cva(`gap-2 rounded-lg font-semibold [&_svg]:size-4 relati
|
|
|
34
33
|
size: "default"
|
|
35
34
|
}
|
|
36
35
|
});
|
|
37
|
-
const Button = ({ asChild = false, children, className, dataTestId, disabled, endIcon,
|
|
36
|
+
const Button = ({ asChild = false, children, className, dataTestId, disabled, endIcon, id, label, onClick, ref, size, startIcon, state, type = "button", variant = "primary", ...props }) => {
|
|
38
37
|
const stateStyles = {
|
|
39
38
|
error: {
|
|
40
39
|
primary: "bg-button-danger border-button-danger text-text-primary hover:bg-button-danger--hover hover:text-text-primary",
|
|
@@ -56,62 +55,48 @@ const Button = ({ asChild = false, children, className, dataTestId, disabled, en
|
|
|
56
55
|
state,
|
|
57
56
|
size
|
|
58
57
|
}), state === "error" && stateStyles.error[variant], state === "loading" && stateStyles.loading[variant], className);
|
|
59
|
-
const generatedId = useId();
|
|
60
|
-
const errorMessageId = id ? `${id}-error` : `${generatedId}-error`;
|
|
61
58
|
const canUseAsChild = asChild && isValidElement(children) && Children.count(children) === 1;
|
|
62
59
|
const content = canUseAsChild ? void 0 : children ?? label;
|
|
63
|
-
const errorMessageValue = state === "error" && typeof errorMessage === "string" ? errorMessage : null;
|
|
64
|
-
const customErrorMessageNode = state === "error" && isValidElement(errorMessage) ? errorMessage : null;
|
|
65
60
|
if (!canUseAsChild && !content) throw new Error("Button requires either `label` or `children`");
|
|
66
61
|
const handleDisabledClickCapture = (event) => {
|
|
67
62
|
if (!disabled) return;
|
|
68
63
|
event.preventDefault();
|
|
69
64
|
event.stopPropagation();
|
|
70
65
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
className: cn("
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
children: endIcon
|
|
106
|
-
})
|
|
107
|
-
] })
|
|
108
|
-
}), customErrorMessageNode ?? /* @__PURE__ */ jsx(ErrorMessage, {
|
|
109
|
-
dataTestId: "spectral-button-error-message",
|
|
110
|
-
id: errorMessageId,
|
|
111
|
-
message: errorMessageValue,
|
|
112
|
-
messageReserveLines,
|
|
113
|
-
messageReserveSpace
|
|
114
|
-
})]
|
|
66
|
+
return /* @__PURE__ */ jsx(canUseAsChild ? Slot : "button", {
|
|
67
|
+
"aria-disabled": disabled,
|
|
68
|
+
className: cn(classes, canUseAsChild && "no-underline! before:content-none after:content-none", canUseAsChild && disabled && "pointer-events-none opacity-50"),
|
|
69
|
+
"data-as-child": canUseAsChild ? "true" : void 0,
|
|
70
|
+
"data-state": state,
|
|
71
|
+
"data-testid": dataTestId ?? `spectral-button-${variant}`,
|
|
72
|
+
"data-variant": variant,
|
|
73
|
+
disabled: canUseAsChild ? void 0 : disabled,
|
|
74
|
+
id,
|
|
75
|
+
onClick,
|
|
76
|
+
onClickCapture: canUseAsChild ? handleDisabledClickCapture : void 0,
|
|
77
|
+
ref,
|
|
78
|
+
tabIndex: canUseAsChild && disabled ? -1 : void 0,
|
|
79
|
+
type: canUseAsChild ? void 0 : type,
|
|
80
|
+
...props,
|
|
81
|
+
children: canUseAsChild ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
82
|
+
startIcon && /* @__PURE__ */ jsx("span", {
|
|
83
|
+
className: cn("flex", variant !== "unstyled" && "pr-1"),
|
|
84
|
+
"aria-hidden": true,
|
|
85
|
+
"data-testid": "spectral-button-start-icon",
|
|
86
|
+
children: startIcon
|
|
87
|
+
}),
|
|
88
|
+
state === "loading" && /* @__PURE__ */ jsx(LoaderIcon, {
|
|
89
|
+
className: "ml-2 motion-safe:animate-spin",
|
|
90
|
+
size: 16
|
|
91
|
+
}),
|
|
92
|
+
content,
|
|
93
|
+
endIcon && state !== "loading" && /* @__PURE__ */ jsx("span", {
|
|
94
|
+
className: cn("flex", variant !== "unstyled" && "pl-2"),
|
|
95
|
+
"aria-hidden": true,
|
|
96
|
+
"data-testid": "spectral-button-end-icon",
|
|
97
|
+
children: endIcon
|
|
98
|
+
})
|
|
99
|
+
] })
|
|
115
100
|
});
|
|
116
101
|
};
|
|
117
102
|
Button.displayName = "Button";
|
package/dist/Button.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","names":[],"sources":["../src/components/Button/Button.tsx"],"sourcesContent":["import { LoaderIcon } from '@components/Icons'\nimport { Slot, type AsChildProp } from '@primitives/slot'\nimport {
|
|
1
|
+
{"version":3,"file":"Button.js","names":[],"sources":["../src/components/Button/Button.tsx"],"sourcesContent":["import { LoaderIcon } from '@components/Icons'\nimport { Slot, type AsChildProp } from '@primitives/slot'\nimport { cn } from '@utils/twUtils'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { Children, isValidElement, type ButtonHTMLAttributes, type MouseEvent, type ReactNode, type Ref } from 'react'\n\nconst buttonVariants = cva(\n `gap-2 rounded-lg font-semibold [&_svg]:size-4 relative flex cursor-pointer items-center justify-center border font-sans! whitespace-nowrap transition-colors focus:outline-none focus-visible:outline-1 focus-visible:outline-offset-2 disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0`,\n {\n variants: {\n variant: {\n primary: `border-button-primary-border bg-button-primary-bg text-button-primary-text hover:border-button-primary-border--hover hover:bg-button-primary-bg--hover hover:text-button-primary-text--hover focus-visible:outline-button-primary-border disabled:border-button-primary-border--disabled disabled:bg-button-primary-bg--disabled disabled:text-button-primary-text--disabled`,\n secondary: `border-button-secondary-border bg-button-secondary-bg text-button-secondary-text hover:bg-button-secondary-bg--hover hover:text-button-secondary-text--hover focus-visible:outline-button-secondary-border disabled:border-button-secondary-border--disabled disabled:bg-button-secondary-bg--disabled disabled:text-button-secondary-text--disabled`,\n outline: `border-button-outline-border bg-button-outline-bg text-button-outline-text hover:bg-button-outline-bg--hover hover:text-button-outline-text--hover focus-visible:outline-button-outline-border disabled:border-button-outline-border--disabled disabled:text-button-outline-text--disabled`,\n ghost: 'border-none bg-button-ghost-bg text-button-ghost-text hover:bg-button-ghost-bg--hover hover:text-button-ghost-text--hover focus-visible:outline-none disabled:text-button-ghost-text--disabled',\n unstyled: 'font-inherit! font-normal gap-0 p-0! inline-flex w-auto! justify-start rounded-none! border-none! bg-transparent text-inherit shadow-none! hover:cursor-pointer hover:bg-transparent focus-visible:outline-button-outline-border disabled:opacity-50',\n },\n size: {\n small: 'py-1 px-4 text-sm! w-fit',\n default: 'py-2 px-6 text-base! w-fit',\n fullWidth: 'py-2 px-6 text-base! w-full',\n },\n state: {\n default: '',\n error: 'bg-button-danger text-text-primary transition focus-visible:outline-button-danger',\n loading: 'pointer-events-none cursor-wait',\n },\n },\n defaultVariants: {\n variant: 'primary',\n state: 'default',\n size: 'default',\n },\n },\n)\n\nexport interface ButtonProps extends AsChildProp, Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'color'>, VariantProps<typeof buttonVariants> {\n dataTestId?: string\n disabled?: boolean\n endIcon?: ReactNode\n label?: string\n onClick?: (event: MouseEvent<HTMLElement>) => void\n size?: 'small' | 'default' | 'fullWidth'\n startIcon?: ReactNode\n state?: 'default' | 'error' | 'loading'\n type?: 'button' | 'submit' | 'reset'\n variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'unstyled'\n}\n\nexport const Button = ({\n asChild = false,\n children,\n className,\n dataTestId,\n disabled,\n endIcon,\n id,\n label,\n onClick,\n ref,\n size,\n startIcon,\n state,\n type = 'button',\n variant = 'primary',\n ...props\n}: ButtonProps & {\n ref?: Ref<HTMLButtonElement>\n}) => {\n const stateStyles = {\n error: {\n primary: 'bg-button-danger border-button-danger text-text-primary hover:bg-button-danger--hover hover:text-text-primary',\n secondary: 'bg-button-danger border-button-danger text-text-primary hover:bg-button-danger--hover hover:text-text-primary',\n outline: 'bg-transparent border-button-danger text-button-danger hover:border-button-danger--hover hover:text-button-danger/80',\n ghost: 'bg-transparent text-button-danger hover:text-button-danger/80',\n unstyled: 'bg-transparent text-button-danger',\n },\n loading: {\n primary: 'bg-button-primary-bg--disabled border-button-primary-border--disabled text-button-primary-text--disabled pointer-events-none',\n secondary: 'bg-button-secondary-bg--disabled border-button-secondary-border--disabled text-button-secondary-text--disabled pointer-events-none',\n outline: 'bg-button-outline-bg--disabled border-button-outline-border--disabled text-button-outline-text--disabled pointer-events-none',\n ghost: 'bg-transparent text-button-ghost-text--disabled pointer-events-none',\n unstyled: 'bg-transparent opacity-50 pointer-events-none',\n },\n }\n\n const classes = cn(buttonVariants({ variant, state, size }), state === 'error' && stateStyles.error[variant as keyof typeof stateStyles.error], state === 'loading' && stateStyles.loading[variant as keyof typeof stateStyles.loading], className)\n\n const canUseAsChild = asChild && isValidElement(children) && Children.count(children) === 1\n const content = canUseAsChild ? undefined : (children ?? label)\n\n if (!canUseAsChild && !content) {\n throw new Error('Button requires either `label` or `children`')\n }\n\n const handleDisabledClickCapture = (event: MouseEvent<HTMLElement>) => {\n if (!disabled) return\n event.preventDefault()\n event.stopPropagation()\n }\n\n const Comp = canUseAsChild ? Slot : 'button'\n\n return (\n <Comp\n aria-disabled={disabled}\n className={cn(classes, canUseAsChild && 'no-underline! before:content-none after:content-none', canUseAsChild && disabled && 'pointer-events-none opacity-50')}\n data-as-child={canUseAsChild ? 'true' : undefined}\n data-state={state}\n data-testid={dataTestId ?? `spectral-button-${variant}`}\n data-variant={variant}\n disabled={canUseAsChild ? undefined : disabled}\n id={id}\n onClick={onClick}\n onClickCapture={canUseAsChild ? handleDisabledClickCapture : undefined}\n ref={ref}\n tabIndex={canUseAsChild && disabled ? -1 : undefined}\n type={canUseAsChild ? undefined : type}\n {...props}\n >\n {canUseAsChild ? (\n children\n ) : (\n <>\n {startIcon && (\n <span className={cn('flex', variant !== 'unstyled' && 'pr-1')} aria-hidden data-testid='spectral-button-start-icon'>\n {startIcon}\n </span>\n )}\n {state === 'loading' && <LoaderIcon className='ml-2 motion-safe:animate-spin' size={16} />}\n {content}\n {endIcon && state !== 'loading' && (\n <span className={cn('flex', variant !== 'unstyled' && 'pl-2')} aria-hidden data-testid='spectral-button-end-icon'>\n {endIcon}\n </span>\n )}\n </>\n )}\n </Comp>\n )\n}\nButton.displayName = 'Button'\n"],"mappings":";;;;;;;;;AAMA,MAAM,iBAAiB,IACrB,oTACA;CACE,UAAU;EACR,SAAS;GACP,SAAS;GACT,WAAW;GACX,SAAS;GACT,OAAO;GACP,UAAU;GACX;EACD,MAAM;GACJ,OAAO;GACP,SAAS;GACT,WAAW;GACZ;EACD,OAAO;GACL,SAAS;GACT,OAAO;GACP,SAAS;GACV;EACF;CACD,iBAAiB;EACf,SAAS;EACT,OAAO;EACP,MAAM;EACP;CACF,CACF;AAeD,MAAa,UAAU,EACrB,UAAU,OACV,UACA,WACA,YACA,UACA,SACA,IACA,OACA,SACA,KACA,MACA,WACA,OACA,OAAO,UACP,UAAU,WACV,GAAG,YAGC;CACJ,MAAM,cAAc;EAClB,OAAO;GACL,SAAS;GACT,WAAW;GACX,SAAS;GACT,OAAO;GACP,UAAU;GACX;EACD,SAAS;GACP,SAAS;GACT,WAAW;GACX,SAAS;GACT,OAAO;GACP,UAAU;GACX;EACF;CAED,MAAM,UAAU,GAAG,eAAe;EAAE;EAAS;EAAO;EAAM,CAAC,EAAE,UAAU,WAAW,YAAY,MAAM,UAA4C,UAAU,aAAa,YAAY,QAAQ,UAA8C,UAAU;CAEnP,MAAM,gBAAgB,WAAW,eAAe,SAAS,IAAI,SAAS,MAAM,SAAS,KAAK;CAC1F,MAAM,UAAU,gBAAgB,SAAa,YAAY;AAEzD,KAAI,CAAC,iBAAiB,CAAC,QACrB,OAAM,IAAI,MAAM,+CAA+C;CAGjE,MAAM,8BAA8B,UAAmC;AACrE,MAAI,CAAC,SAAU;AACf,QAAM,gBAAgB;AACtB,QAAM,iBAAiB;;AAKzB,QACE,oBAHW,gBAAgB,OAAO,UAGlC;EACE,iBAAe;EACf,WAAW,GAAG,SAAS,iBAAiB,wDAAwD,iBAAiB,YAAY,iCAAiC;EAC9J,iBAAe,gBAAgB,SAAS;EACxC,cAAY;EACZ,eAAa,cAAc,mBAAmB;EAC9C,gBAAc;EACd,UAAU,gBAAgB,SAAY;EAClC;EACK;EACT,gBAAgB,gBAAgB,6BAA6B;EACxD;EACL,UAAU,iBAAiB,WAAW,KAAK;EAC3C,MAAM,gBAAgB,SAAY;EAClC,GAAI;YAEH,gBACC,WAEA;GACG,aACC,oBAAC,QAAD;IAAM,WAAW,GAAG,QAAQ,YAAY,cAAc,OAAO;IAAE;IAAY,eAAY;cACpF;IACI;GAER,UAAU,aAAa,oBAAC,YAAD;IAAY,WAAU;IAAgC,MAAM;IAAM;GACzF;GACA,WAAW,UAAU,aACpB,oBAAC,QAAD;IAAM,WAAW,GAAG,QAAQ,YAAY,cAAc,OAAO;IAAE;IAAY,eAAY;cACpF;IACI;GAER;EAEA;;AAGX,OAAO,cAAc"}
|
package/dist/Checkbox.js
CHANGED
|
@@ -9,7 +9,7 @@ import "react";
|
|
|
9
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
|
|
11
11
|
//#region src/components/Checkbox/Checkbox.tsx
|
|
12
|
-
const Checkbox = ({ checked, className, errorMessage, id, label, labelText, messageReserveLines = 1, messageReserveSpace =
|
|
12
|
+
const Checkbox = ({ checked, className, errorMessage, id, label, labelText, messageReserveLines = 1, messageReserveSpace = false, onCheckedChange, ref, required, state = "default", warningMessage, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, ...props }) => {
|
|
13
13
|
const inputId = useFormFieldId(id, props.name);
|
|
14
14
|
const resolvedLabelText = labelText ?? label;
|
|
15
15
|
const errorMessageId = `${inputId}-error`;
|
package/dist/Checkbox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","names":[],"sources":["../src/components/Checkbox/Checkbox.tsx"],"sourcesContent":["import { CheckmarkIcon, MinusIcon } from '@components/Icons'\nimport { ErrorMessage, WarningMessage, useFormFieldId, type BaseFormFieldProps, type FormFieldState } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { type ComponentPropsWithoutRef, type ComponentRef, type Ref } from 'react'\nimport { CheckboxBase, CheckboxIndicator } from './CheckboxBase'\n\nexport interface CheckboxProps extends Omit<ComponentPropsWithoutRef<typeof CheckboxBase>, 'onCheckedChange'> {\n 'aria-describedby'?: string\n 'aria-label'?: string\n checked?: boolean | 'indeterminate'\n disabled?: boolean\n errorMessage?: BaseFormFieldProps['errorMessage']\n id?: string\n label?: string\n labelText?: string\n messageReserveLines?: number\n messageReserveSpace?: boolean\n name?: string\n onCheckedChange: (value: boolean | 'indeterminate') => void\n required?: boolean\n state?: FormFieldState\n value?: string\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nexport const Checkbox = ({\n checked,\n className,\n errorMessage,\n id,\n label,\n labelText,\n messageReserveLines = 1,\n messageReserveSpace =
|
|
1
|
+
{"version":3,"file":"Checkbox.js","names":[],"sources":["../src/components/Checkbox/Checkbox.tsx"],"sourcesContent":["import { CheckmarkIcon, MinusIcon } from '@components/Icons'\nimport { ErrorMessage, WarningMessage, useFormFieldId, type BaseFormFieldProps, type FormFieldState } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { type ComponentPropsWithoutRef, type ComponentRef, type Ref } from 'react'\nimport { CheckboxBase, CheckboxIndicator } from './CheckboxBase'\n\nexport interface CheckboxProps extends Omit<ComponentPropsWithoutRef<typeof CheckboxBase>, 'onCheckedChange'> {\n 'aria-describedby'?: string\n 'aria-label'?: string\n checked?: boolean | 'indeterminate'\n disabled?: boolean\n errorMessage?: BaseFormFieldProps['errorMessage']\n id?: string\n label?: string\n labelText?: string\n messageReserveLines?: number\n messageReserveSpace?: boolean\n name?: string\n onCheckedChange: (value: boolean | 'indeterminate') => void\n required?: boolean\n state?: FormFieldState\n value?: string\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nexport const Checkbox = ({\n checked,\n className,\n errorMessage,\n id,\n label,\n labelText,\n messageReserveLines = 1,\n messageReserveSpace = false,\n onCheckedChange,\n ref,\n required,\n state = 'default',\n warningMessage,\n 'aria-describedby': ariaDescribedBy,\n 'aria-label': ariaLabel,\n ...props\n}: CheckboxProps & {\n ref?: Ref<ComponentRef<typeof CheckboxBase>>\n}) => {\n const inputId = useFormFieldId(id, props.name)\n const resolvedLabelText = labelText ?? label\n const errorMessageId = `${inputId}-error`\n const warningMessageId = `${inputId}-warning`\n const messageId = state === 'error' && errorMessage && errorMessageId ? errorMessageId : state === 'warning' && warningMessage && warningMessageId ? warningMessageId : undefined\n\n return (\n <div>\n <div className='gap-2 flex items-center'>\n <CheckboxBase\n aria-describedby={[messageId, ariaDescribedBy].filter(Boolean).join(' ') || undefined}\n aria-invalid={state === 'error' ? true : undefined}\n aria-label={ariaLabel ?? (!resolvedLabelText ? 'Checkbox' : undefined)}\n aria-required={required ?? undefined}\n checked={checked}\n className={cn(\n 'checkbox peer h-5 w-5 rounded-sm shrink-0 border border-checkbox-border hover:opacity-80 focus:outline-none focus-visible:outline-1 focus-visible:outline-checkbox-border--focus',\n 'focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-checkbox-border--checked',\n className,\n )}\n data-field-state={state}\n data-testid='spectral-checkbox'\n id={inputId}\n onCheckedChange={onCheckedChange}\n ref={ref}\n {...props}\n >\n <CheckboxIndicator data-testid='spectral-checkbox-indicator' className={cn('checkbox-indicator flex items-center justify-center text-checkbox-indicator--checked')}>\n {checked === 'indeterminate' ? <MinusIcon className='checkbox-indeterminate' size={16} strokeWidth={4} /> : <CheckmarkIcon className='checkbox-check' size={16} strokeWidth={4} />}\n </CheckboxIndicator>\n </CheckboxBase>\n {resolvedLabelText && (\n <label className='text-md peer-disabled:text-neutral-400 text-text-primary' data-testid='spectral-checkbox-label' htmlFor={inputId}>\n {resolvedLabelText}\n </label>\n )}\n </div>\n <ErrorMessage dataTestId='spectral-checkbox-error-message' id={errorMessageId} message={state === 'error' ? errorMessage : null} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace && state === 'error'} />\n <WarningMessage dataTestId='spectral-checkbox-warning-message' id={warningMessageId} message={state === 'warning' ? warningMessage : null} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace && state === 'warning'} />\n </div>\n )\n}\nCheckbox.displayName = 'Checkbox'\n"],"mappings":";;;;;;;;;;;AAyBA,MAAa,YAAY,EACvB,SACA,WACA,cACA,IACA,OACA,WACA,sBAAsB,GACtB,sBAAsB,OACtB,iBACA,KACA,UACA,QAAQ,WACR,gBACA,oBAAoB,iBACpB,cAAc,WACd,GAAG,YAGC;CACJ,MAAM,UAAU,eAAe,IAAI,MAAM,KAAK;CAC9C,MAAM,oBAAoB,aAAa;CACvC,MAAM,iBAAiB,GAAG,QAAQ;CAClC,MAAM,mBAAmB,GAAG,QAAQ;AAGpC,QACE,qBAAC,OAAD;EACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,cAAD;IACE,oBAAkB,CANR,UAAU,WAAW,gBAAgB,iBAAiB,iBAAiB,UAAU,aAAa,kBAAkB,mBAAmB,mBAAmB,QAMlI,gBAAgB,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,IAAI;IAC5E,gBAAc,UAAU,UAAU,OAAO;IACzC,cAAY,cAAc,CAAC,oBAAoB,aAAa;IAC5D,iBAAe,YAAY;IAClB;IACT,WAAW,GACT,oLACA,uIACA,UACD;IACD,oBAAkB;IAClB,eAAY;IACZ,IAAI;IACa;IACZ;IACL,GAAI;cAEJ,oBAAC,mBAAD;KAAmB,eAAY;KAA8B,WAAW,GAAG,uFAAuF;eAC/J,YAAY,kBAAkB,oBAAC,WAAD;MAAW,WAAU;MAAyB,MAAM;MAAI,aAAa;MAAK,IAAG,oBAAC,eAAD;MAAe,WAAU;MAAiB,MAAM;MAAI,aAAa;MAAK;KAChK;IACP,GACd,qBACC,oBAAC,SAAD;IAAO,WAAU;IAA2D,eAAY;IAA0B,SAAS;cACxH;IACK,EAEN;;EACN,oBAAC,cAAD;GAAc,YAAW;GAAkC,IAAI;GAAgB,SAAS,UAAU,UAAU,eAAe;GAA2B;GAAqB,qBAAqB,uBAAuB,UAAU;GAAW;EAC5O,oBAAC,gBAAD;GAAgB,YAAW;GAAoC,IAAI;GAAkB,SAAS,UAAU,YAAY,iBAAiB;GAA2B;GAAqB,qBAAqB,uBAAuB,UAAU;GAAa;EACpP;;AAGV,SAAS,cAAc"}
|
package/dist/Combobox.js
CHANGED
|
@@ -13,7 +13,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
import { Combobox as Combobox$1 } from "@base-ui/react";
|
|
14
14
|
|
|
15
15
|
//#region src/components/Combobox/Combobox.tsx
|
|
16
|
-
const Combobox = ({ className, disabled, defaultValue = "", dropdownWidth = "trigger", emptyMessage = "No options found.", errorMessage, id, label, labelClassName, loadingMessage = "Loading…", messageReserveLines = 1, messageReserveSpace =
|
|
16
|
+
const Combobox = ({ className, disabled, defaultValue = "", dropdownWidth = "trigger", emptyMessage = "No options found.", errorMessage, id, label, labelClassName, loadingMessage = "Loading…", messageReserveLines = 1, messageReserveSpace = false, name, onChange, onValueChange, options, placeholder = "Search…", ref, required, state = "default", value: valueProp, warningMessage, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel }) => {
|
|
17
17
|
if (!label && !ariaLabel) console.warn("Combobox: provide either `label` or `aria-label` for an accessible name.");
|
|
18
18
|
const comboboxId = useFormFieldId(id, name);
|
|
19
19
|
const listboxId = `${comboboxId}-listbox`;
|
package/dist/Combobox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.js","names":["ComboboxPrimitive"],"sources":["../src/components/Combobox/Combobox.tsx"],"sourcesContent":["import { Combobox as ComboboxPrimitive } from '@base-ui/react'\nimport { CheckmarkIcon, ChevronDownIcon, LoaderIcon } from '@components/Icons'\nimport { Label } from '@components/Label/Label'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { InputGroup, InputGroupAddon } from '@primitives/input-group'\nimport {\n EmptyState,\n ErrorMessage,\n getAriaProps,\n getDropdownWidthStyles,\n getDropdownSurfaceClasses,\n getErrorMessageId,\n getFormFieldCSSProperties,\n getOptionClasses,\n getTriggerClasses,\n LoadingState,\n WarningMessage,\n useFormFieldId,\n type BaseFormFieldProps,\n type BaseOption,\n type DropdownWidth,\n type FormFieldState,\n} from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useMemo, useRef, useState, type CSSProperties, type MouseEvent, type Ref } from 'react'\n\nexport type ComboboxOption = BaseOption\n\nexport interface ComboboxProps extends Omit<BaseFormFieldProps, 'onChange' | 'state'> {\n className?: string\n defaultValue?: string\n dropdownWidth?: DropdownWidth\n emptyMessage?: string\n labelClassName?: string\n loadingMessage?: string\n onChange?: (value: string) => void\n onValueChange?: (value: string) => void\n options: ComboboxOption[]\n placeholder?: string\n state?: Exclude<FormFieldState, 'disabled'>\n value?: string\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nexport const Combobox = ({\n className,\n disabled,\n defaultValue = '',\n dropdownWidth = 'trigger',\n emptyMessage = 'No options found.',\n errorMessage,\n id,\n label,\n labelClassName,\n loadingMessage = 'Loading…',\n messageReserveLines = 1,\n messageReserveSpace = true,\n name,\n onChange,\n onValueChange,\n options,\n placeholder = 'Search…',\n ref,\n required,\n state = 'default',\n value: valueProp,\n warningMessage,\n 'aria-describedby': ariaDescribedBy,\n 'aria-label': ariaLabel,\n}: ComboboxProps & { ref?: Ref<HTMLDivElement> }) => {\n if (process.env.NODE_ENV !== 'production' && !label && !ariaLabel) {\n console.warn('Combobox: provide either `label` or `aria-label` for an accessible name.')\n }\n\n const comboboxId = useFormFieldId(id, name)\n const listboxId = `${comboboxId}-listbox`\n const errorMessageId = getErrorMessageId(comboboxId)\n const warningMessageId = `${comboboxId}-warning`\n const messageId = state === 'error' ? errorMessageId : state === 'warning' && warningMessage ? warningMessageId : undefined\n const isDisabled = Boolean(disabled)\n const isLoading = state === 'loading'\n const isInvalid = state === 'error'\n const ariaProps = getAriaProps(state, ariaDescribedBy, required, messageId)\n\n const [open, setOpen] = useState(false)\n const [inputValue, setInputValue] = useState('')\n const inputRef = useRef<HTMLInputElement>(null)\n const suppressNextInputFocusOpenRef = useRef(false)\n const clearFocusSuppression = () => {\n suppressNextInputFocusOpenRef.current = false\n }\n const [value, setValue] = useUncontrolledState<string>({\n value: valueProp,\n defaultValue,\n onChange: (nextValue) => {\n if (onChange) {\n onChange(nextValue)\n } else {\n onValueChange?.(nextValue)\n }\n },\n })\n\n const selectedOption = useMemo(() => options.find((o) => o.value === value) ?? null, [options, value])\n const filteredOptions = useMemo(() => {\n const query = inputValue.trim().toLowerCase()\n if (!query) return options\n return options.filter((option) => option.label.toLowerCase().includes(query))\n }, [inputValue, options])\n const showsSelectedValueAsPlaceholder = Boolean(selectedOption && inputValue.length === 0)\n const { dropdownWidthMode, dropdownWidthStyle } = getDropdownWidthStyles({\n dropdownWidth,\n triggerWidth: 'var(--anchor-width)',\n })\n\n const handleValueChange = (nextOption: ComboboxOption | null, eventDetails: { reason?: string }) => {\n const nextValue = nextOption?.value ?? ''\n const shouldClearSelection = eventDetails.reason === 'item-press' && nextValue === value\n setValue(shouldClearSelection ? '' : nextValue)\n setInputValue('')\n setOpen(false)\n }\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (isDisabled || isLoading) {\n setOpen(false)\n return\n }\n\n if (!nextOpen) {\n setInputValue('')\n }\n\n setOpen(nextOpen)\n }\n\n const handleTriggerContainerClick = (event: MouseEvent<HTMLDivElement>) => {\n if (isDisabled || isLoading) return\n\n const target = event.target as HTMLElement\n if (target === inputRef.current) return\n if (target.closest('[data-slot=combobox-trigger-button]')) return\n inputRef.current?.focus()\n setOpen(true)\n }\n\n return (\n <div className='w-full' ref={ref}>\n {label && (\n <Label className={cn('mb-2 block text-text-primary', labelClassName, isDisabled && 'text-text-secondary')} data-testid='spectral-combobox-label' htmlFor={comboboxId}>\n {label}\n </Label>\n )}\n\n <ComboboxPrimitive.Root\n autoHighlight\n disabled={isDisabled || isLoading}\n filter={null}\n inputValue={inputValue}\n itemToStringLabel={(option: ComboboxOption) => option.label}\n itemToStringValue={(option: ComboboxOption) => option.value}\n openOnInputClick\n onInputValueChange={(nextInputValue, eventDetails) => {\n if (eventDetails.reason !== 'input-change' && eventDetails.reason !== 'input-clear') return\n setInputValue(nextInputValue)\n }}\n name={name}\n onOpenChange={handleOpenChange}\n onValueChange={handleValueChange}\n open={open}\n required={required}\n value={selectedOption}\n >\n <ComboboxPrimitive.InputGroup\n render={\n <InputGroup\n data-slot='combobox-content'\n data-state={state}\n data-testid='spectral-combobox-trigger'\n className={cn(getTriggerClasses(open, state), 'ring-0! outline-none focus-within:outline-none has-[[data-slot=input-group-control]:focus-visible]:outline-0', isDisabled && 'pointer-events-none cursor-not-allowed', className)}\n onClick={handleTriggerContainerClick}\n style={getFormFieldCSSProperties() as CSSProperties}\n />\n }\n >\n <ComboboxPrimitive.Input\n aria-controls={listboxId}\n aria-expanded={open}\n aria-label={ariaLabel ?? label}\n autoComplete='off'\n className={cn(\n 'min-w-0 text-base flex-1 truncate overflow-hidden border-0 bg-transparent whitespace-nowrap text-input-text outline-hidden outline-0 placeholder:opacity-100 focus-visible:ring-0 focus-visible:outline-none',\n showsSelectedValueAsPlaceholder ? 'placeholder:text-input-text!' : 'placeholder:text-input-text-placeholder!',\n )}\n data-slot='input-group-control'\n id={comboboxId}\n onFocus={() => {\n if (suppressNextInputFocusOpenRef.current) {\n suppressNextInputFocusOpenRef.current = false\n return\n }\n\n if (!isDisabled && !isLoading) {\n setOpen(true)\n }\n }}\n ref={inputRef}\n placeholder={selectedOption?.label ?? placeholder}\n role='combobox'\n {...ariaProps}\n />\n <InputGroupAddon align='inline-end' className='cursor-pointer'>\n <ComboboxPrimitive.Trigger\n aria-label='Toggle options'\n className='cursor-pointer'\n data-slot='combobox-trigger-button'\n onPointerDownCapture={() => {\n const inputElement = inputRef.current\n if (!inputElement) {\n clearFocusSuppression()\n return\n }\n\n const activeElement = inputElement.ownerDocument.activeElement\n suppressNextInputFocusOpenRef.current = activeElement !== inputElement\n }}\n onPointerUpCapture={clearFocusSuppression}\n onPointerCancelCapture={clearFocusSuppression}\n onTouchEndCapture={clearFocusSuppression}\n >\n {isLoading ? <LoaderIcon className='size-5 motion-safe:animate-spin' /> : <ChevronDownIcon className={cn('size-5 shrink-0 transition-transform duration-200', open && 'rotate-180')} />}\n </ComboboxPrimitive.Trigger>\n </InputGroupAddon>\n </ComboboxPrimitive.InputGroup>\n\n <ComboboxPrimitive.Portal>\n <ComboboxPrimitive.Positioner align='start' className='isolate z-50' sideOffset={4}>\n <ComboboxPrimitive.Popup\n className={cn(\n 'p-1 z-50 motion-safe:data-closed:animate-out motion-safe:data-open:animate-in',\n getDropdownSurfaceClasses(),\n 'motion-safe:data-closed:fade-out-0 motion-safe:data-closed:zoom-out-95 motion-safe:data-open:fade-in-0 motion-safe:data-open:zoom-in-95',\n 'max-h-[min(var(--available-height),18rem)] motion-safe:data-[side=bottom]:slide-in-from-top-2 motion-safe:data-[side=top]:slide-in-from-bottom-2',\n // Keep a single scroll container (the list) to avoid dual scrollbar styles.\n 'min-w-32 origin-(--transform-origin) overflow-hidden',\n )}\n data-dropdown-width-mode={dropdownWidthMode}\n data-dropdown-width-value={dropdownWidthMode === 'custom' ? dropdownWidth : undefined}\n data-testid='spectral-combobox-content'\n style={dropdownWidthStyle}\n >\n {isLoading ? (\n <LoadingState message={loadingMessage} />\n ) : filteredOptions.length === 0 ? (\n <EmptyState message={emptyMessage} />\n ) : (\n <ComboboxPrimitive.List className='max-h-[min(var(--available-height),18rem)] overflow-y-auto' id={listboxId}>\n {filteredOptions.map((option) => (\n <ComboboxPrimitive.Item\n className={cn(getOptionClasses(!!option.disabled, false, value === option.value), 'group/command-item relative flex w-full items-center data-highlighted:bg-input-bg--hover')}\n data-testid='spectral-combobox-item'\n disabled={option.disabled}\n key={option.value}\n value={option}\n >\n <span className='min-w-0 flex-1 truncate whitespace-nowrap'>{option.label}</span>\n <ComboboxPrimitive.ItemIndicator\n render={\n <span className='right-2 h-4 w-4 absolute flex items-center justify-center'>\n <CheckmarkIcon size={16} />\n </span>\n }\n />\n </ComboboxPrimitive.Item>\n ))}\n </ComboboxPrimitive.List>\n )}\n </ComboboxPrimitive.Popup>\n </ComboboxPrimitive.Positioner>\n </ComboboxPrimitive.Portal>\n </ComboboxPrimitive.Root>\n\n <ErrorMessage dataTestId='spectral-combobox-error-message' id={errorMessageId} message={isInvalid ? (errorMessage ?? null) : null} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace && state === 'error'} />\n <WarningMessage dataTestId='spectral-combobox-warning-message' id={warningMessageId} message={state === 'warning' ? (warningMessage ?? null) : null} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace && state === 'warning'} />\n </div>\n )\n}\nCombobox.displayName = 'Combobox'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Combobox.js","names":["ComboboxPrimitive"],"sources":["../src/components/Combobox/Combobox.tsx"],"sourcesContent":["import { Combobox as ComboboxPrimitive } from '@base-ui/react'\nimport { CheckmarkIcon, ChevronDownIcon, LoaderIcon } from '@components/Icons'\nimport { Label } from '@components/Label/Label'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { InputGroup, InputGroupAddon } from '@primitives/input-group'\nimport {\n EmptyState,\n ErrorMessage,\n getAriaProps,\n getDropdownWidthStyles,\n getDropdownSurfaceClasses,\n getErrorMessageId,\n getFormFieldCSSProperties,\n getOptionClasses,\n getTriggerClasses,\n LoadingState,\n WarningMessage,\n useFormFieldId,\n type BaseFormFieldProps,\n type BaseOption,\n type DropdownWidth,\n type FormFieldState,\n} from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useMemo, useRef, useState, type CSSProperties, type MouseEvent, type Ref } from 'react'\n\nexport type ComboboxOption = BaseOption\n\nexport interface ComboboxProps extends Omit<BaseFormFieldProps, 'onChange' | 'state'> {\n className?: string\n defaultValue?: string\n dropdownWidth?: DropdownWidth\n emptyMessage?: string\n labelClassName?: string\n loadingMessage?: string\n onChange?: (value: string) => void\n onValueChange?: (value: string) => void\n options: ComboboxOption[]\n placeholder?: string\n state?: Exclude<FormFieldState, 'disabled'>\n value?: string\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nexport const Combobox = ({\n className,\n disabled,\n defaultValue = '',\n dropdownWidth = 'trigger',\n emptyMessage = 'No options found.',\n errorMessage,\n id,\n label,\n labelClassName,\n loadingMessage = 'Loading…',\n messageReserveLines = 1,\n messageReserveSpace = false,\n name,\n onChange,\n onValueChange,\n options,\n placeholder = 'Search…',\n ref,\n required,\n state = 'default',\n value: valueProp,\n warningMessage,\n 'aria-describedby': ariaDescribedBy,\n 'aria-label': ariaLabel,\n}: ComboboxProps & { ref?: Ref<HTMLDivElement> }) => {\n if (process.env.NODE_ENV !== 'production' && !label && !ariaLabel) {\n console.warn('Combobox: provide either `label` or `aria-label` for an accessible name.')\n }\n\n const comboboxId = useFormFieldId(id, name)\n const listboxId = `${comboboxId}-listbox`\n const errorMessageId = getErrorMessageId(comboboxId)\n const warningMessageId = `${comboboxId}-warning`\n const messageId = state === 'error' ? errorMessageId : state === 'warning' && warningMessage ? warningMessageId : undefined\n const isDisabled = Boolean(disabled)\n const isLoading = state === 'loading'\n const isInvalid = state === 'error'\n const ariaProps = getAriaProps(state, ariaDescribedBy, required, messageId)\n\n const [open, setOpen] = useState(false)\n const [inputValue, setInputValue] = useState('')\n const inputRef = useRef<HTMLInputElement>(null)\n const suppressNextInputFocusOpenRef = useRef(false)\n const clearFocusSuppression = () => {\n suppressNextInputFocusOpenRef.current = false\n }\n const [value, setValue] = useUncontrolledState<string>({\n value: valueProp,\n defaultValue,\n onChange: (nextValue) => {\n if (onChange) {\n onChange(nextValue)\n } else {\n onValueChange?.(nextValue)\n }\n },\n })\n\n const selectedOption = useMemo(() => options.find((o) => o.value === value) ?? null, [options, value])\n const filteredOptions = useMemo(() => {\n const query = inputValue.trim().toLowerCase()\n if (!query) return options\n return options.filter((option) => option.label.toLowerCase().includes(query))\n }, [inputValue, options])\n const showsSelectedValueAsPlaceholder = Boolean(selectedOption && inputValue.length === 0)\n const { dropdownWidthMode, dropdownWidthStyle } = getDropdownWidthStyles({\n dropdownWidth,\n triggerWidth: 'var(--anchor-width)',\n })\n\n const handleValueChange = (nextOption: ComboboxOption | null, eventDetails: { reason?: string }) => {\n const nextValue = nextOption?.value ?? ''\n const shouldClearSelection = eventDetails.reason === 'item-press' && nextValue === value\n setValue(shouldClearSelection ? '' : nextValue)\n setInputValue('')\n setOpen(false)\n }\n\n const handleOpenChange = (nextOpen: boolean) => {\n if (isDisabled || isLoading) {\n setOpen(false)\n return\n }\n\n if (!nextOpen) {\n setInputValue('')\n }\n\n setOpen(nextOpen)\n }\n\n const handleTriggerContainerClick = (event: MouseEvent<HTMLDivElement>) => {\n if (isDisabled || isLoading) return\n\n const target = event.target as HTMLElement\n if (target === inputRef.current) return\n if (target.closest('[data-slot=combobox-trigger-button]')) return\n inputRef.current?.focus()\n setOpen(true)\n }\n\n return (\n <div className='w-full' ref={ref}>\n {label && (\n <Label className={cn('mb-2 block text-text-primary', labelClassName, isDisabled && 'text-text-secondary')} data-testid='spectral-combobox-label' htmlFor={comboboxId}>\n {label}\n </Label>\n )}\n\n <ComboboxPrimitive.Root\n autoHighlight\n disabled={isDisabled || isLoading}\n filter={null}\n inputValue={inputValue}\n itemToStringLabel={(option: ComboboxOption) => option.label}\n itemToStringValue={(option: ComboboxOption) => option.value}\n openOnInputClick\n onInputValueChange={(nextInputValue, eventDetails) => {\n if (eventDetails.reason !== 'input-change' && eventDetails.reason !== 'input-clear') return\n setInputValue(nextInputValue)\n }}\n name={name}\n onOpenChange={handleOpenChange}\n onValueChange={handleValueChange}\n open={open}\n required={required}\n value={selectedOption}\n >\n <ComboboxPrimitive.InputGroup\n render={\n <InputGroup\n data-slot='combobox-content'\n data-state={state}\n data-testid='spectral-combobox-trigger'\n className={cn(getTriggerClasses(open, state), 'ring-0! outline-none focus-within:outline-none has-[[data-slot=input-group-control]:focus-visible]:outline-0', isDisabled && 'pointer-events-none cursor-not-allowed', className)}\n onClick={handleTriggerContainerClick}\n style={getFormFieldCSSProperties() as CSSProperties}\n />\n }\n >\n <ComboboxPrimitive.Input\n aria-controls={listboxId}\n aria-expanded={open}\n aria-label={ariaLabel ?? label}\n autoComplete='off'\n className={cn(\n 'min-w-0 text-base flex-1 truncate overflow-hidden border-0 bg-transparent whitespace-nowrap text-input-text outline-hidden outline-0 placeholder:opacity-100 focus-visible:ring-0 focus-visible:outline-none',\n showsSelectedValueAsPlaceholder ? 'placeholder:text-input-text!' : 'placeholder:text-input-text-placeholder!',\n )}\n data-slot='input-group-control'\n id={comboboxId}\n onFocus={() => {\n if (suppressNextInputFocusOpenRef.current) {\n suppressNextInputFocusOpenRef.current = false\n return\n }\n\n if (!isDisabled && !isLoading) {\n setOpen(true)\n }\n }}\n ref={inputRef}\n placeholder={selectedOption?.label ?? placeholder}\n role='combobox'\n {...ariaProps}\n />\n <InputGroupAddon align='inline-end' className='cursor-pointer'>\n <ComboboxPrimitive.Trigger\n aria-label='Toggle options'\n className='cursor-pointer'\n data-slot='combobox-trigger-button'\n onPointerDownCapture={() => {\n const inputElement = inputRef.current\n if (!inputElement) {\n clearFocusSuppression()\n return\n }\n\n const activeElement = inputElement.ownerDocument.activeElement\n suppressNextInputFocusOpenRef.current = activeElement !== inputElement\n }}\n onPointerUpCapture={clearFocusSuppression}\n onPointerCancelCapture={clearFocusSuppression}\n onTouchEndCapture={clearFocusSuppression}\n >\n {isLoading ? <LoaderIcon className='size-5 motion-safe:animate-spin' /> : <ChevronDownIcon className={cn('size-5 shrink-0 transition-transform duration-200', open && 'rotate-180')} />}\n </ComboboxPrimitive.Trigger>\n </InputGroupAddon>\n </ComboboxPrimitive.InputGroup>\n\n <ComboboxPrimitive.Portal>\n <ComboboxPrimitive.Positioner align='start' className='isolate z-50' sideOffset={4}>\n <ComboboxPrimitive.Popup\n className={cn(\n 'p-1 z-50 motion-safe:data-closed:animate-out motion-safe:data-open:animate-in',\n getDropdownSurfaceClasses(),\n 'motion-safe:data-closed:fade-out-0 motion-safe:data-closed:zoom-out-95 motion-safe:data-open:fade-in-0 motion-safe:data-open:zoom-in-95',\n 'max-h-[min(var(--available-height),18rem)] motion-safe:data-[side=bottom]:slide-in-from-top-2 motion-safe:data-[side=top]:slide-in-from-bottom-2',\n // Keep a single scroll container (the list) to avoid dual scrollbar styles.\n 'min-w-32 origin-(--transform-origin) overflow-hidden',\n )}\n data-dropdown-width-mode={dropdownWidthMode}\n data-dropdown-width-value={dropdownWidthMode === 'custom' ? dropdownWidth : undefined}\n data-testid='spectral-combobox-content'\n style={dropdownWidthStyle}\n >\n {isLoading ? (\n <LoadingState message={loadingMessage} />\n ) : filteredOptions.length === 0 ? (\n <EmptyState message={emptyMessage} />\n ) : (\n <ComboboxPrimitive.List className='max-h-[min(var(--available-height),18rem)] overflow-y-auto' id={listboxId}>\n {filteredOptions.map((option) => (\n <ComboboxPrimitive.Item\n className={cn(getOptionClasses(!!option.disabled, false, value === option.value), 'group/command-item relative flex w-full items-center data-highlighted:bg-input-bg--hover')}\n data-testid='spectral-combobox-item'\n disabled={option.disabled}\n key={option.value}\n value={option}\n >\n <span className='min-w-0 flex-1 truncate whitespace-nowrap'>{option.label}</span>\n <ComboboxPrimitive.ItemIndicator\n render={\n <span className='right-2 h-4 w-4 absolute flex items-center justify-center'>\n <CheckmarkIcon size={16} />\n </span>\n }\n />\n </ComboboxPrimitive.Item>\n ))}\n </ComboboxPrimitive.List>\n )}\n </ComboboxPrimitive.Popup>\n </ComboboxPrimitive.Positioner>\n </ComboboxPrimitive.Portal>\n </ComboboxPrimitive.Root>\n\n <ErrorMessage dataTestId='spectral-combobox-error-message' id={errorMessageId} message={isInvalid ? (errorMessage ?? null) : null} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace && state === 'error'} />\n <WarningMessage dataTestId='spectral-combobox-warning-message' id={warningMessageId} message={state === 'warning' ? (warningMessage ?? null) : null} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace && state === 'warning'} />\n </div>\n )\n}\nCombobox.displayName = 'Combobox'\n"],"mappings":"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"}
|
|
@@ -35,7 +35,7 @@ type ControlGroupSelectBaseProps = ComponentProps<typeof Select> & Pick<ControlG
|
|
|
35
35
|
inputAriaLabel?: string;
|
|
36
36
|
inputPlaceholder?: string;
|
|
37
37
|
maxAmount?: number; /** Number of message lines to reserve (default: 1). */
|
|
38
|
-
messageReserveLines?: number; /** Whether to keep message space reserved when hidden (default:
|
|
38
|
+
messageReserveLines?: number; /** Whether to keep message space reserved when hidden (default: false). */
|
|
39
39
|
messageReserveSpace?: boolean;
|
|
40
40
|
minAmount?: number;
|
|
41
41
|
selectPlaceholder?: string; /** When `captionLayout` is `inline` and `selectPlaceholder` is empty, sets the select trigger's accessible name. */
|
|
@@ -23,7 +23,7 @@ const getReadableLabel = (label, fallback) => {
|
|
|
23
23
|
const normalizedLabel = label?.trim();
|
|
24
24
|
return normalizedLabel && normalizedLabel.length > 0 ? normalizedLabel : fallback;
|
|
25
25
|
};
|
|
26
|
-
const ControlGroupSelect = ({ amountStep, ariaLabel, captionLayout = "inline", className, dataTestId = "spectral-control-group-select", disabled, errorMessage, id, dropdownWidth = "trigger", inputAriaLabel, inputPlaceholder, inputItemClassName, inputValue, messageReserveLines = 1, messageReserveSpace =
|
|
26
|
+
const ControlGroupSelect = ({ amountStep, ariaLabel, captionLayout = "inline", className, dataTestId = "spectral-control-group-select", disabled, errorMessage, id, dropdownWidth = "trigger", inputAriaLabel, inputPlaceholder, inputItemClassName, inputValue, messageReserveLines = 1, messageReserveSpace = false, maxAmount = 1e6, minAmount = 0, onInputChange, orientation = "horizontal", selectAriaLabel, selectItemClassName, selectOptions, selectPlaceholder, state = "default", type = "number", warningMessage, ...selectProps }) => {
|
|
27
27
|
const baseId = useId();
|
|
28
28
|
const idPrefix = id ?? baseId;
|
|
29
29
|
const inputId = `${idPrefix}-amount`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlGroupSelect.js","names":[],"sources":["../../src/components/ControlGroup/ControlGroupSelect.tsx"],"sourcesContent":["import { Label } from '@components/Label/Label'\nimport { Input } from '@primitives/input'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@primitives/select'\nimport { getStateClasses, type DropdownWidth, type FormFieldState } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useId, type ComponentProps } from 'react'\nimport { ControlGroup, ControlGroupItem, useControlGroup, type ControlGroupProps } from './ControlGroup'\n\nconst numberInputNoSpinner = '[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none'\nconst defaultInputLabel = 'Amount'\nconst defaultSelectLabel = 'Select an option'\n\nexport interface SelectOptionType {\n disabled?: boolean\n label: string\n value: string\n}\n\n/** `inline`: `inputPlaceholder` / `selectPlaceholder` show inside the fields. `above`: same strings render as labels above each control (no inner placeholders). */\nexport type ControlGroupSelectCaptionLayout = 'above' | 'inline'\n\ntype ControlGroupSelectInputControlProps =\n | {\n inputValue: ComponentProps<typeof Input>['value']\n onInputChange: ComponentProps<typeof Input>['onChange']\n }\n | {\n inputValue?: undefined\n onInputChange?: undefined\n }\n\ntype ControlGroupSelectBaseProps = ComponentProps<typeof Select> &\n Pick<ControlGroupProps, 'orientation'> & {\n amountStep?: number\n /** Accessible name for the group wrapper (use when there is no visible group heading). */\n ariaLabel?: string\n captionLayout?: ControlGroupSelectCaptionLayout\n className?: string\n dataTestId?: string\n disabled?: boolean\n dropdownWidth?: DropdownWidth\n errorMessage?: string | string[] | Record<string, unknown> | null\n id?: string\n /** Class applied to the input `ControlGroupItem` segment for width/split composition. */\n inputItemClassName?: string\n /** When `captionLayout` is `inline`, overrides the input's accessible name (defaults to `inputPlaceholder` when set). */\n inputAriaLabel?: string\n inputPlaceholder?: string\n maxAmount?: number\n /** Number of message lines to reserve (default: 1). */\n messageReserveLines?: number\n /** Whether to keep message space reserved when hidden (default: true). */\n messageReserveSpace?: boolean\n minAmount?: number\n selectPlaceholder?: string\n /** When `captionLayout` is `inline` and `selectPlaceholder` is empty, sets the select trigger's accessible name. */\n selectAriaLabel?: string\n /** Class applied to the select `ControlGroupItem` segment for width/split composition. */\n selectItemClassName?: string\n selectOptions: SelectOptionType[]\n state?: Exclude<FormFieldState, 'disabled'>\n type?: 'number' | 'text'\n warningMessage?: string | string[] | Record<string, unknown> | null\n }\n\nexport type ControlGroupSelectProps = ControlGroupSelectBaseProps & ControlGroupSelectInputControlProps\n\nconst fieldStackClass = 'flex w-full min-w-0 flex-col gap-1.5'\nconst groupedFieldBaseClasses =\n 'h-12! min-h-12! border-2! border-input-border bg-input-bg text-base text-input-text transition duration-200 placeholder:text-input-text-placeholder hover:border-input-border--hover focus-visible:border-input-border--focus focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-input-border--focus disabled:pointer-events-none disabled:border-input-border--disabled disabled:bg-input-bg--disabled disabled:text-input-text--disabled'\n\nconst getFieldMinWidth = (caption?: string, extraCharacters: number = 0): string | undefined => {\n const normalizedCaption = caption?.trim()\n if (!normalizedCaption) return undefined\n return `${Math.max(normalizedCaption.length + extraCharacters, 8)}ch`\n}\n\nconst getReadableLabel = (label: string | undefined, fallback: string): string => {\n const normalizedLabel = label?.trim()\n return normalizedLabel && normalizedLabel.length > 0 ? normalizedLabel : fallback\n}\n\nexport const ControlGroupSelect = ({\n amountStep,\n ariaLabel,\n captionLayout = 'inline',\n className,\n dataTestId = 'spectral-control-group-select',\n disabled,\n errorMessage,\n id,\n dropdownWidth = 'trigger',\n inputAriaLabel,\n inputPlaceholder,\n inputItemClassName,\n inputValue,\n messageReserveLines = 1,\n messageReserveSpace = true,\n maxAmount = 1000000,\n minAmount = 0,\n onInputChange,\n orientation = 'horizontal',\n selectAriaLabel,\n selectItemClassName,\n selectOptions,\n selectPlaceholder,\n state = 'default',\n type = 'number',\n warningMessage,\n ...selectProps\n}: ControlGroupSelectProps) => {\n const baseId = useId()\n const idPrefix = id ?? baseId\n const inputId = `${idPrefix}-amount`\n const selectTriggerId = `${idPrefix}-select`\n const inputLabelId = `${inputId}-label`\n const selectLabelId = `${selectTriggerId}-label`\n\n const useAboveLabels = captionLayout === 'above'\n const inputCaption = useAboveLabels ? undefined : inputPlaceholder\n const selectCaption = useAboveLabels ? undefined : selectPlaceholder\n const inputLabelText = getReadableLabel(inputPlaceholder, defaultInputLabel)\n const selectLabelText = getReadableLabel(selectPlaceholder, defaultSelectLabel)\n\n const inputAccessibleName = inputAriaLabel ?? (useAboveLabels ? undefined : inputLabelText)\n const selectTriggerAriaLabel = selectAriaLabel ?? (useAboveLabels ? undefined : selectLabelText)\n const inputMinWidth = getFieldMinWidth(inputPlaceholder, 3)\n const selectTriggerMinWidth = getFieldMinWidth(selectPlaceholder, 5)\n const groupAriaLabel = ariaLabel ?? `${inputLabelText} and ${selectLabelText}`\n\n const isDisabled = !!disabled\n const isInputControlled = inputValue !== undefined && onInputChange !== undefined\n\n return (\n <ControlGroup\n aria-label={groupAriaLabel}\n className={className}\n data-testid={dataTestId}\n disabled={disabled}\n errorMessage={errorMessage}\n id={idPrefix}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace}\n orientation={orientation}\n state={state}\n warningMessage={warningMessage}\n >\n <ControlGroupSelectInner\n amountStep={amountStep}\n dataTestId={dataTestId}\n inputAccessibleName={inputAccessibleName}\n inputCaption={inputCaption}\n inputId={inputId}\n inputLabelId={inputLabelId}\n inputLabelText={inputLabelText}\n inputItemClassName={inputItemClassName}\n inputMinWidth={inputMinWidth}\n inputValue={isInputControlled ? inputValue : undefined}\n isDisabled={isDisabled}\n maxAmount={maxAmount}\n minAmount={minAmount}\n onInputChange={isInputControlled ? onInputChange : undefined}\n dropdownWidth={dropdownWidth}\n selectCaption={selectCaption}\n selectOptions={selectOptions}\n selectLabelId={selectLabelId}\n selectLabelText={selectLabelText}\n selectItemClassName={selectItemClassName}\n selectProps={selectProps}\n selectTriggerAriaLabel={selectTriggerAriaLabel}\n selectTriggerId={selectTriggerId}\n selectTriggerMinWidth={selectTriggerMinWidth}\n type={type}\n useAboveLabels={useAboveLabels}\n />\n </ControlGroup>\n )\n}\n\ninterface ControlGroupSelectInnerProps {\n amountStep?: number\n dataTestId: string\n dropdownWidth: DropdownWidth\n inputAccessibleName?: string\n inputCaption?: string\n inputId: string\n inputLabelId: string\n inputLabelText: string\n inputItemClassName?: string\n inputMinWidth?: string\n inputValue?: ComponentProps<typeof Input>['value']\n isDisabled: boolean\n maxAmount: number\n minAmount: number\n onInputChange?: ComponentProps<typeof Input>['onChange']\n selectCaption?: string\n selectLabelId: string\n selectLabelText: string\n selectItemClassName?: string\n selectOptions: SelectOptionType[]\n selectProps: Omit<ComponentProps<typeof Select>, 'children'>\n selectTriggerAriaLabel?: string\n selectTriggerId: string\n selectTriggerMinWidth?: string\n type: 'number' | 'text'\n useAboveLabels: boolean\n}\n\nconst ControlGroupSelectInner = ({\n amountStep,\n dataTestId,\n dropdownWidth,\n inputAccessibleName,\n inputCaption,\n inputId,\n inputLabelId,\n inputLabelText,\n inputItemClassName,\n inputMinWidth,\n inputValue,\n isDisabled,\n maxAmount,\n minAmount,\n onInputChange,\n selectCaption,\n selectLabelId,\n selectLabelText,\n selectItemClassName,\n selectOptions,\n selectProps,\n selectTriggerAriaLabel,\n selectTriggerId,\n selectTriggerMinWidth,\n type,\n useAboveLabels,\n}: ControlGroupSelectInnerProps) => {\n const { messageId, orientation, state } = useControlGroup()\n const isSelectOpenControlled = selectProps.open !== undefined\n const handleSelectOpenChange: NonNullable<ComponentProps<typeof Select>['onOpenChange']> = (nextOpen) => {\n selectProps.onOpenChange?.(nextOpen)\n }\n\n const inputRadiusClasses = orientation === 'horizontal' ? 'rounded-s-md rounded-e-none' : 'rounded-ss-md rounded-se-md rounded-es-none rounded-ee-none'\n const selectRadiusClasses = orientation === 'horizontal' ? 'rounded-s-none rounded-e-md' : 'rounded-ss-none rounded-se-none rounded-es-md rounded-ee-md'\n const firstItemAboveLabelClasses = orientation === 'horizontal' ? 'me-0' : 'mbe-0'\n const secondItemAboveLabelClasses = orientation === 'horizontal' ? 'me-0 [&>*:last-child]:-ms-0.5' : 'mbe-0 [&>*:last-child]:-mt-0.5'\n\n const inputElement = (\n <Input\n aria-describedby={messageId}\n aria-label={inputAccessibleName}\n aria-labelledby={useAboveLabels ? inputLabelId : undefined}\n className={cn(groupedFieldBaseClasses, inputRadiusClasses, getStateClasses(state), type === 'number' && numberInputNoSpinner, type === 'number' && 'tabular-nums')}\n data-testid={`${dataTestId}-input`}\n disabled={isDisabled}\n id={useAboveLabels ? inputId : undefined}\n type={type === 'number' ? 'number' : 'text'}\n placeholder={inputCaption}\n style={inputMinWidth ? { minWidth: inputMinWidth } : undefined}\n min={minAmount ?? 0}\n max={maxAmount ?? 1000000}\n {...(inputValue !== undefined && onInputChange !== undefined ? { value: inputValue, onChange: onInputChange } : {})}\n step={amountStep ?? 1}\n />\n )\n\n const selectTriggerElement = (\n <SelectTrigger\n aria-describedby={messageId}\n aria-label={selectTriggerAriaLabel}\n aria-labelledby={useAboveLabels ? selectLabelId : undefined}\n className={cn(\n 'text-input-text data-placeholder:text-input-text-placeholder!',\n selectRadiusClasses,\n 'px-4 w-full justify-between focus-visible:border-input-border--focus focus-visible:ring-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-input-border--focus',\n getStateClasses(state),\n )}\n data-testid={`${dataTestId}-select-trigger`}\n disabled={isDisabled}\n id={useAboveLabels ? selectTriggerId : undefined}\n style={selectTriggerMinWidth ? { minWidth: selectTriggerMinWidth } : undefined}\n >\n <SelectValue className='min-w-0 block max-w-full truncate text-left whitespace-nowrap text-input-text! data-placeholder:text-input-text-placeholder!' placeholder={selectCaption} />\n </SelectTrigger>\n )\n\n return (\n <>\n <ControlGroupItem className={cn(useAboveLabels ? firstItemAboveLabelClasses : undefined, inputItemClassName)}>\n {useAboveLabels ? (\n <div className={fieldStackClass}>\n <Label className='text-text-primary' htmlFor={inputId} id={inputLabelId}>\n {inputLabelText}\n </Label>\n {inputElement}\n </div>\n ) : (\n inputElement\n )}\n </ControlGroupItem>\n <Select {...selectProps} data-testid={`${dataTestId}-select-root`} disabled={isDisabled} {...(isSelectOpenControlled ? { open: selectProps.open } : {})} onOpenChange={handleSelectOpenChange}>\n <ControlGroupItem className={cn(useAboveLabels ? secondItemAboveLabelClasses : undefined, selectItemClassName)}>\n {useAboveLabels ? (\n <div className={fieldStackClass}>\n <Label className='text-text-primary' htmlFor={selectTriggerId} id={selectLabelId}>\n {selectLabelText}\n </Label>\n {selectTriggerElement}\n </div>\n ) : (\n selectTriggerElement\n )}\n </ControlGroupItem>\n <SelectContent data-testid={`${dataTestId}-select-content`} dropdownWidth={dropdownWidth}>\n {selectOptions.map((option) => (\n <SelectItem disabled={option.disabled} key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ControlGroupSelect.js","names":[],"sources":["../../src/components/ControlGroup/ControlGroupSelect.tsx"],"sourcesContent":["import { Label } from '@components/Label/Label'\nimport { Input } from '@primitives/input'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@primitives/select'\nimport { getStateClasses, type DropdownWidth, type FormFieldState } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useId, type ComponentProps } from 'react'\nimport { ControlGroup, ControlGroupItem, useControlGroup, type ControlGroupProps } from './ControlGroup'\n\nconst numberInputNoSpinner = '[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none'\nconst defaultInputLabel = 'Amount'\nconst defaultSelectLabel = 'Select an option'\n\nexport interface SelectOptionType {\n disabled?: boolean\n label: string\n value: string\n}\n\n/** `inline`: `inputPlaceholder` / `selectPlaceholder` show inside the fields. `above`: same strings render as labels above each control (no inner placeholders). */\nexport type ControlGroupSelectCaptionLayout = 'above' | 'inline'\n\ntype ControlGroupSelectInputControlProps =\n | {\n inputValue: ComponentProps<typeof Input>['value']\n onInputChange: ComponentProps<typeof Input>['onChange']\n }\n | {\n inputValue?: undefined\n onInputChange?: undefined\n }\n\ntype ControlGroupSelectBaseProps = ComponentProps<typeof Select> &\n Pick<ControlGroupProps, 'orientation'> & {\n amountStep?: number\n /** Accessible name for the group wrapper (use when there is no visible group heading). */\n ariaLabel?: string\n captionLayout?: ControlGroupSelectCaptionLayout\n className?: string\n dataTestId?: string\n disabled?: boolean\n dropdownWidth?: DropdownWidth\n errorMessage?: string | string[] | Record<string, unknown> | null\n id?: string\n /** Class applied to the input `ControlGroupItem` segment for width/split composition. */\n inputItemClassName?: string\n /** When `captionLayout` is `inline`, overrides the input's accessible name (defaults to `inputPlaceholder` when set). */\n inputAriaLabel?: string\n inputPlaceholder?: string\n maxAmount?: number\n /** Number of message lines to reserve (default: 1). */\n messageReserveLines?: number\n /** Whether to keep message space reserved when hidden (default: false). */\n messageReserveSpace?: boolean\n minAmount?: number\n selectPlaceholder?: string\n /** When `captionLayout` is `inline` and `selectPlaceholder` is empty, sets the select trigger's accessible name. */\n selectAriaLabel?: string\n /** Class applied to the select `ControlGroupItem` segment for width/split composition. */\n selectItemClassName?: string\n selectOptions: SelectOptionType[]\n state?: Exclude<FormFieldState, 'disabled'>\n type?: 'number' | 'text'\n warningMessage?: string | string[] | Record<string, unknown> | null\n }\n\nexport type ControlGroupSelectProps = ControlGroupSelectBaseProps & ControlGroupSelectInputControlProps\n\nconst fieldStackClass = 'flex w-full min-w-0 flex-col gap-1.5'\nconst groupedFieldBaseClasses =\n 'h-12! min-h-12! border-2! border-input-border bg-input-bg text-base text-input-text transition duration-200 placeholder:text-input-text-placeholder hover:border-input-border--hover focus-visible:border-input-border--focus focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-input-border--focus disabled:pointer-events-none disabled:border-input-border--disabled disabled:bg-input-bg--disabled disabled:text-input-text--disabled'\n\nconst getFieldMinWidth = (caption?: string, extraCharacters: number = 0): string | undefined => {\n const normalizedCaption = caption?.trim()\n if (!normalizedCaption) return undefined\n return `${Math.max(normalizedCaption.length + extraCharacters, 8)}ch`\n}\n\nconst getReadableLabel = (label: string | undefined, fallback: string): string => {\n const normalizedLabel = label?.trim()\n return normalizedLabel && normalizedLabel.length > 0 ? normalizedLabel : fallback\n}\n\nexport const ControlGroupSelect = ({\n amountStep,\n ariaLabel,\n captionLayout = 'inline',\n className,\n dataTestId = 'spectral-control-group-select',\n disabled,\n errorMessage,\n id,\n dropdownWidth = 'trigger',\n inputAriaLabel,\n inputPlaceholder,\n inputItemClassName,\n inputValue,\n messageReserveLines = 1,\n messageReserveSpace = false,\n maxAmount = 1000000,\n minAmount = 0,\n onInputChange,\n orientation = 'horizontal',\n selectAriaLabel,\n selectItemClassName,\n selectOptions,\n selectPlaceholder,\n state = 'default',\n type = 'number',\n warningMessage,\n ...selectProps\n}: ControlGroupSelectProps) => {\n const baseId = useId()\n const idPrefix = id ?? baseId\n const inputId = `${idPrefix}-amount`\n const selectTriggerId = `${idPrefix}-select`\n const inputLabelId = `${inputId}-label`\n const selectLabelId = `${selectTriggerId}-label`\n\n const useAboveLabels = captionLayout === 'above'\n const inputCaption = useAboveLabels ? undefined : inputPlaceholder\n const selectCaption = useAboveLabels ? undefined : selectPlaceholder\n const inputLabelText = getReadableLabel(inputPlaceholder, defaultInputLabel)\n const selectLabelText = getReadableLabel(selectPlaceholder, defaultSelectLabel)\n\n const inputAccessibleName = inputAriaLabel ?? (useAboveLabels ? undefined : inputLabelText)\n const selectTriggerAriaLabel = selectAriaLabel ?? (useAboveLabels ? undefined : selectLabelText)\n const inputMinWidth = getFieldMinWidth(inputPlaceholder, 3)\n const selectTriggerMinWidth = getFieldMinWidth(selectPlaceholder, 5)\n const groupAriaLabel = ariaLabel ?? `${inputLabelText} and ${selectLabelText}`\n\n const isDisabled = !!disabled\n const isInputControlled = inputValue !== undefined && onInputChange !== undefined\n\n return (\n <ControlGroup\n aria-label={groupAriaLabel}\n className={className}\n data-testid={dataTestId}\n disabled={disabled}\n errorMessage={errorMessage}\n id={idPrefix}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace}\n orientation={orientation}\n state={state}\n warningMessage={warningMessage}\n >\n <ControlGroupSelectInner\n amountStep={amountStep}\n dataTestId={dataTestId}\n inputAccessibleName={inputAccessibleName}\n inputCaption={inputCaption}\n inputId={inputId}\n inputLabelId={inputLabelId}\n inputLabelText={inputLabelText}\n inputItemClassName={inputItemClassName}\n inputMinWidth={inputMinWidth}\n inputValue={isInputControlled ? inputValue : undefined}\n isDisabled={isDisabled}\n maxAmount={maxAmount}\n minAmount={minAmount}\n onInputChange={isInputControlled ? onInputChange : undefined}\n dropdownWidth={dropdownWidth}\n selectCaption={selectCaption}\n selectOptions={selectOptions}\n selectLabelId={selectLabelId}\n selectLabelText={selectLabelText}\n selectItemClassName={selectItemClassName}\n selectProps={selectProps}\n selectTriggerAriaLabel={selectTriggerAriaLabel}\n selectTriggerId={selectTriggerId}\n selectTriggerMinWidth={selectTriggerMinWidth}\n type={type}\n useAboveLabels={useAboveLabels}\n />\n </ControlGroup>\n )\n}\n\ninterface ControlGroupSelectInnerProps {\n amountStep?: number\n dataTestId: string\n dropdownWidth: DropdownWidth\n inputAccessibleName?: string\n inputCaption?: string\n inputId: string\n inputLabelId: string\n inputLabelText: string\n inputItemClassName?: string\n inputMinWidth?: string\n inputValue?: ComponentProps<typeof Input>['value']\n isDisabled: boolean\n maxAmount: number\n minAmount: number\n onInputChange?: ComponentProps<typeof Input>['onChange']\n selectCaption?: string\n selectLabelId: string\n selectLabelText: string\n selectItemClassName?: string\n selectOptions: SelectOptionType[]\n selectProps: Omit<ComponentProps<typeof Select>, 'children'>\n selectTriggerAriaLabel?: string\n selectTriggerId: string\n selectTriggerMinWidth?: string\n type: 'number' | 'text'\n useAboveLabels: boolean\n}\n\nconst ControlGroupSelectInner = ({\n amountStep,\n dataTestId,\n dropdownWidth,\n inputAccessibleName,\n inputCaption,\n inputId,\n inputLabelId,\n inputLabelText,\n inputItemClassName,\n inputMinWidth,\n inputValue,\n isDisabled,\n maxAmount,\n minAmount,\n onInputChange,\n selectCaption,\n selectLabelId,\n selectLabelText,\n selectItemClassName,\n selectOptions,\n selectProps,\n selectTriggerAriaLabel,\n selectTriggerId,\n selectTriggerMinWidth,\n type,\n useAboveLabels,\n}: ControlGroupSelectInnerProps) => {\n const { messageId, orientation, state } = useControlGroup()\n const isSelectOpenControlled = selectProps.open !== undefined\n const handleSelectOpenChange: NonNullable<ComponentProps<typeof Select>['onOpenChange']> = (nextOpen) => {\n selectProps.onOpenChange?.(nextOpen)\n }\n\n const inputRadiusClasses = orientation === 'horizontal' ? 'rounded-s-md rounded-e-none' : 'rounded-ss-md rounded-se-md rounded-es-none rounded-ee-none'\n const selectRadiusClasses = orientation === 'horizontal' ? 'rounded-s-none rounded-e-md' : 'rounded-ss-none rounded-se-none rounded-es-md rounded-ee-md'\n const firstItemAboveLabelClasses = orientation === 'horizontal' ? 'me-0' : 'mbe-0'\n const secondItemAboveLabelClasses = orientation === 'horizontal' ? 'me-0 [&>*:last-child]:-ms-0.5' : 'mbe-0 [&>*:last-child]:-mt-0.5'\n\n const inputElement = (\n <Input\n aria-describedby={messageId}\n aria-label={inputAccessibleName}\n aria-labelledby={useAboveLabels ? inputLabelId : undefined}\n className={cn(groupedFieldBaseClasses, inputRadiusClasses, getStateClasses(state), type === 'number' && numberInputNoSpinner, type === 'number' && 'tabular-nums')}\n data-testid={`${dataTestId}-input`}\n disabled={isDisabled}\n id={useAboveLabels ? inputId : undefined}\n type={type === 'number' ? 'number' : 'text'}\n placeholder={inputCaption}\n style={inputMinWidth ? { minWidth: inputMinWidth } : undefined}\n min={minAmount ?? 0}\n max={maxAmount ?? 1000000}\n {...(inputValue !== undefined && onInputChange !== undefined ? { value: inputValue, onChange: onInputChange } : {})}\n step={amountStep ?? 1}\n />\n )\n\n const selectTriggerElement = (\n <SelectTrigger\n aria-describedby={messageId}\n aria-label={selectTriggerAriaLabel}\n aria-labelledby={useAboveLabels ? selectLabelId : undefined}\n className={cn(\n 'text-input-text data-placeholder:text-input-text-placeholder!',\n selectRadiusClasses,\n 'px-4 w-full justify-between focus-visible:border-input-border--focus focus-visible:ring-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-input-border--focus',\n getStateClasses(state),\n )}\n data-testid={`${dataTestId}-select-trigger`}\n disabled={isDisabled}\n id={useAboveLabels ? selectTriggerId : undefined}\n style={selectTriggerMinWidth ? { minWidth: selectTriggerMinWidth } : undefined}\n >\n <SelectValue className='min-w-0 block max-w-full truncate text-left whitespace-nowrap text-input-text! data-placeholder:text-input-text-placeholder!' placeholder={selectCaption} />\n </SelectTrigger>\n )\n\n return (\n <>\n <ControlGroupItem className={cn(useAboveLabels ? firstItemAboveLabelClasses : undefined, inputItemClassName)}>\n {useAboveLabels ? (\n <div className={fieldStackClass}>\n <Label className='text-text-primary' htmlFor={inputId} id={inputLabelId}>\n {inputLabelText}\n </Label>\n {inputElement}\n </div>\n ) : (\n inputElement\n )}\n </ControlGroupItem>\n <Select {...selectProps} data-testid={`${dataTestId}-select-root`} disabled={isDisabled} {...(isSelectOpenControlled ? { open: selectProps.open } : {})} onOpenChange={handleSelectOpenChange}>\n <ControlGroupItem className={cn(useAboveLabels ? secondItemAboveLabelClasses : undefined, selectItemClassName)}>\n {useAboveLabels ? (\n <div className={fieldStackClass}>\n <Label className='text-text-primary' htmlFor={selectTriggerId} id={selectLabelId}>\n {selectLabelText}\n </Label>\n {selectTriggerElement}\n </div>\n ) : (\n selectTriggerElement\n )}\n </ControlGroupItem>\n <SelectContent data-testid={`${dataTestId}-select-content`} dropdownWidth={dropdownWidth}>\n {selectOptions.map((option) => (\n <SelectItem disabled={option.disabled} key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </>\n )\n}\n"],"mappings":";;;;;;;;;;;AAQA,MAAM,uBAAuB;AAC7B,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AAyD3B,MAAM,kBAAkB;AACxB,MAAM,0BACJ;AAEF,MAAM,oBAAoB,SAAkB,kBAA0B,MAA0B;CAC9F,MAAM,oBAAoB,SAAS,MAAM;AACzC,KAAI,CAAC,kBAAmB,QAAO;AAC/B,QAAO,GAAG,KAAK,IAAI,kBAAkB,SAAS,iBAAiB,EAAE,CAAC;;AAGpE,MAAM,oBAAoB,OAA2B,aAA6B;CAChF,MAAM,kBAAkB,OAAO,MAAM;AACrC,QAAO,mBAAmB,gBAAgB,SAAS,IAAI,kBAAkB;;AAG3E,MAAa,sBAAsB,EACjC,YACA,WACA,gBAAgB,UAChB,WACA,aAAa,iCACb,UACA,cACA,IACA,gBAAgB,WAChB,gBACA,kBACA,oBACA,YACA,sBAAsB,GACtB,sBAAsB,OACtB,YAAY,KACZ,YAAY,GACZ,eACA,cAAc,cACd,iBACA,qBACA,eACA,mBACA,QAAQ,WACR,OAAO,UACP,gBACA,GAAG,kBAC0B;CAC7B,MAAM,SAAS,OAAO;CACtB,MAAM,WAAW,MAAM;CACvB,MAAM,UAAU,GAAG,SAAS;CAC5B,MAAM,kBAAkB,GAAG,SAAS;CACpC,MAAM,eAAe,GAAG,QAAQ;CAChC,MAAM,gBAAgB,GAAG,gBAAgB;CAEzC,MAAM,iBAAiB,kBAAkB;CACzC,MAAM,eAAe,iBAAiB,SAAY;CAClD,MAAM,gBAAgB,iBAAiB,SAAY;CACnD,MAAM,iBAAiB,iBAAiB,kBAAkB,kBAAkB;CAC5E,MAAM,kBAAkB,iBAAiB,mBAAmB,mBAAmB;CAE/E,MAAM,sBAAsB,mBAAmB,iBAAiB,SAAY;CAC5E,MAAM,yBAAyB,oBAAoB,iBAAiB,SAAY;CAChF,MAAM,gBAAgB,iBAAiB,kBAAkB,EAAE;CAC3D,MAAM,wBAAwB,iBAAiB,mBAAmB,EAAE;CACpE,MAAM,iBAAiB,aAAa,GAAG,eAAe,OAAO;CAE7D,MAAM,aAAa,CAAC,CAAC;CACrB,MAAM,oBAAoB,eAAe,UAAa,kBAAkB;AAExE,QACE,oBAAC,cAAD;EACE,cAAY;EACD;EACX,eAAa;EACH;EACI;EACd,IAAI;EACiB;EACA;EACR;EACN;EACS;YAEhB,oBAAC,yBAAD;GACc;GACA;GACS;GACP;GACL;GACK;GACE;GACI;GACL;GACf,YAAY,oBAAoB,aAAa;GACjC;GACD;GACA;GACX,eAAe,oBAAoB,gBAAgB;GACpC;GACA;GACA;GACA;GACE;GACI;GACR;GACW;GACP;GACM;GACjB;GACU;GAChB;EACW;;AAiCnB,MAAM,2BAA2B,EAC/B,YACA,YACA,eACA,qBACA,cACA,SACA,cACA,gBACA,oBACA,eACA,YACA,YACA,WACA,WACA,eACA,eACA,eACA,iBACA,qBACA,eACA,aACA,wBACA,iBACA,uBACA,MACA,qBACkC;CAClC,MAAM,EAAE,WAAW,aAAa,UAAU,iBAAiB;CAC3D,MAAM,yBAAyB,YAAY,SAAS;CACpD,MAAM,0BAAsF,aAAa;AACvG,cAAY,eAAe,SAAS;;CAGtC,MAAM,qBAAqB,gBAAgB,eAAe,gCAAgC;CAC1F,MAAM,sBAAsB,gBAAgB,eAAe,gCAAgC;CAC3F,MAAM,6BAA6B,gBAAgB,eAAe,SAAS;CAC3E,MAAM,8BAA8B,gBAAgB,eAAe,kCAAkC;CAErG,MAAM,eACJ,oBAAC,OAAD;EACE,oBAAkB;EAClB,cAAY;EACZ,mBAAiB,iBAAiB,eAAe;EACjD,WAAW,GAAG,yBAAyB,oBAAoB,gBAAgB,MAAM,EAAE,SAAS,YAAY,sBAAsB,SAAS,YAAY,eAAe;EAClK,eAAa,GAAG,WAAW;EAC3B,UAAU;EACV,IAAI,iBAAiB,UAAU;EAC/B,MAAM,SAAS,WAAW,WAAW;EACrC,aAAa;EACb,OAAO,gBAAgB,EAAE,UAAU,eAAe,GAAG;EACrD,KAAK,aAAa;EAClB,KAAK,aAAa;EAClB,GAAK,eAAe,UAAa,kBAAkB,SAAY;GAAE,OAAO;GAAY,UAAU;GAAe,GAAG,EAAE;EAClH,MAAM,cAAc;EACpB;CAGJ,MAAM,uBACJ,oBAAC,eAAD;EACE,oBAAkB;EAClB,cAAY;EACZ,mBAAiB,iBAAiB,gBAAgB;EAClD,WAAW,GACT,iEACA,qBACA,8LACA,gBAAgB,MAAM,CACvB;EACD,eAAa,GAAG,WAAW;EAC3B,UAAU;EACV,IAAI,iBAAiB,kBAAkB;EACvC,OAAO,wBAAwB,EAAE,UAAU,uBAAuB,GAAG;YAErE,oBAAC,aAAD;GAAa,WAAU;GAA+H,aAAa;GAAiB;EACtK;AAGlB,QACE,4CACE,oBAAC,kBAAD;EAAkB,WAAW,GAAG,iBAAiB,6BAA6B,QAAW,mBAAmB;YACzG,iBACC,qBAAC,OAAD;GAAK,WAAW;aAAhB,CACE,oBAAC,OAAD;IAAO,WAAU;IAAoB,SAAS;IAAS,IAAI;cACxD;IACK,GACP,aACG;OAEN;EAEe,GACnB,qBAAC,QAAD;EAAQ,GAAI;EAAa,eAAa,GAAG,WAAW;EAAe,UAAU;EAAY,GAAK,yBAAyB,EAAE,MAAM,YAAY,MAAM,GAAG,EAAE;EAAG,cAAc;YAAvK,CACE,oBAAC,kBAAD;GAAkB,WAAW,GAAG,iBAAiB,8BAA8B,QAAW,oBAAoB;aAC3G,iBACC,qBAAC,OAAD;IAAK,WAAW;cAAhB,CACE,oBAAC,OAAD;KAAO,WAAU;KAAoB,SAAS;KAAiB,IAAI;eAChE;KACK,GACP,qBACG;QAEN;GAEe,GACnB,oBAAC,eAAD;GAAe,eAAa,GAAG,WAAW;GAAiC;aACxE,cAAc,KAAK,WAClB,oBAAC,YAAD;IAAY,UAAU,OAAO;IAA6B,OAAO,OAAO;cACrE,OAAO;IACG,EAF+B,OAAO,MAEtC,CACb;GACY,EACT;IACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlGroup.d.ts","names":[],"sources":["../src/components/ControlGroup/ControlGroup.tsx"],"mappings":";;;;;;;UAKU,wBAAA;EACR,SAAA;EACA,UAAA;EACA,SAAA;EACA,SAAA;EACA,WAAA;EACA,KAAA,EAAO,cAAA;AAAA;AAAA,cAKI,eAAA,QAAe,wBAAA;AAAA,UAQX,iBAAA,SAA0B,cAAA;EACzC,QAAA;EACA,YAAA,uBAAmC,MAAA;EACnC,EAAA;EACA,mBAAA;EACA,mBAAA;EACA,IAAA;EACA,WAAA;EACA,KAAA,GAAQ,cAAA;EACR,cAAA,uBAAqC,MAAA;AAAA;AAAA,cAG1B,YAAA;EAAY,SAAA;EAAA,QAAA;EAAA,YAAA;EAAA,EAAA;EAAA,mBAAA;EAAA,mBAAA;EAAA,IAAA;EAAA,WAAA;EAAA,KAAA;EAAA,cAAA;EAAA,GAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"ControlGroup.d.ts","names":[],"sources":["../src/components/ControlGroup/ControlGroup.tsx"],"mappings":";;;;;;;UAKU,wBAAA;EACR,SAAA;EACA,UAAA;EACA,SAAA;EACA,SAAA;EACA,WAAA;EACA,KAAA,EAAO,cAAA;AAAA;AAAA,cAKI,eAAA,QAAe,wBAAA;AAAA,UAQX,iBAAA,SAA0B,cAAA;EACzC,QAAA;EACA,YAAA,uBAAmC,MAAA;EACnC,EAAA;EACA,mBAAA;EACA,mBAAA;EACA,IAAA;EACA,WAAA;EACA,KAAA,GAAQ,cAAA;EACR,cAAA,uBAAqC,MAAA;AAAA;AAAA,cAG1B,YAAA;EAAY,SAAA;EAAA,QAAA;EAAA,YAAA;EAAA,EAAA;EAAA,mBAAA;EAAA,mBAAA;EAAA,IAAA;EAAA,WAAA;EAAA,KAAA;EAAA,cAAA;EAAA,GAAA;AAAA,GAAoL,iBAAA,KAAiB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,cA2BjN,gBAAA;EAAgB,SAAA;EAAA,GAAA;AAAA,GAA6B,cAAA,QAAsB,IAAA,MAAK,oBAAA,CAAA,GAAA,CAAA,OAAA"}
|
package/dist/ControlGroup.js
CHANGED
|
@@ -13,7 +13,7 @@ const useControlGroup = () => {
|
|
|
13
13
|
if (context === null) throw new Error("useControlGroup must be used within a ControlGroup");
|
|
14
14
|
return context;
|
|
15
15
|
};
|
|
16
|
-
const ControlGroup = ({ className, disabled, errorMessage, id, messageReserveLines = 1, messageReserveSpace =
|
|
16
|
+
const ControlGroup = ({ className, disabled, errorMessage, id, messageReserveLines = 1, messageReserveSpace = false, name, orientation = "horizontal", state = "default", warningMessage, ...props }) => {
|
|
17
17
|
const groupId = useFormFieldId(id, name);
|
|
18
18
|
const errorMessageId = getErrorMessageId(groupId);
|
|
19
19
|
const warningMessageId = `${groupId}-warning`;
|
package/dist/ControlGroup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ControlGroup.js","names":[],"sources":["../src/components/ControlGroup/ControlGroup.tsx"],"sourcesContent":["import { Slot } from '@primitives/slot'\nimport { ErrorMessage, getErrorMessageId, useFormFieldId, useFormFieldState, WarningMessage, type FormFieldState } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { createContext, useContext, type ComponentProps } from 'react'\n\ninterface ControlGroupContextValue {\n messageId?: string\n isDisabled: boolean\n isInvalid: boolean\n isLoading: boolean\n orientation: 'horizontal' | 'vertical'\n state: FormFieldState\n}\n\nconst ControlGroupContext = createContext<ControlGroupContextValue | null>(null)\n\nexport const useControlGroup = () => {\n const context = useContext(ControlGroupContext)\n if (context === null) {\n throw new Error('useControlGroup must be used within a ControlGroup')\n }\n return context\n}\n\nexport interface ControlGroupProps extends ComponentProps<'div'> {\n disabled?: boolean\n errorMessage?: string | string[] | Record<string, unknown> | null\n id?: string\n messageReserveLines?: number\n messageReserveSpace?: boolean\n name?: string\n orientation?: 'horizontal' | 'vertical'\n state?: FormFieldState\n warningMessage?: string | string[] | Record<string, unknown> | null\n}\n\nexport const ControlGroup = ({ className, disabled, errorMessage, id, messageReserveLines = 1, messageReserveSpace =
|
|
1
|
+
{"version":3,"file":"ControlGroup.js","names":[],"sources":["../src/components/ControlGroup/ControlGroup.tsx"],"sourcesContent":["import { Slot } from '@primitives/slot'\nimport { ErrorMessage, getErrorMessageId, useFormFieldId, useFormFieldState, WarningMessage, type FormFieldState } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { createContext, useContext, type ComponentProps } from 'react'\n\ninterface ControlGroupContextValue {\n messageId?: string\n isDisabled: boolean\n isInvalid: boolean\n isLoading: boolean\n orientation: 'horizontal' | 'vertical'\n state: FormFieldState\n}\n\nconst ControlGroupContext = createContext<ControlGroupContextValue | null>(null)\n\nexport const useControlGroup = () => {\n const context = useContext(ControlGroupContext)\n if (context === null) {\n throw new Error('useControlGroup must be used within a ControlGroup')\n }\n return context\n}\n\nexport interface ControlGroupProps extends ComponentProps<'div'> {\n disabled?: boolean\n errorMessage?: string | string[] | Record<string, unknown> | null\n id?: string\n messageReserveLines?: number\n messageReserveSpace?: boolean\n name?: string\n orientation?: 'horizontal' | 'vertical'\n state?: FormFieldState\n warningMessage?: string | string[] | Record<string, unknown> | null\n}\n\nexport const ControlGroup = ({ className, disabled, errorMessage, id, messageReserveLines = 1, messageReserveSpace = false, name, orientation = 'horizontal', state = 'default', warningMessage, ...props }: ControlGroupProps) => {\n const groupId = useFormFieldId(id, name)\n const errorMessageId = getErrorMessageId(groupId)\n const warningMessageId = `${groupId}-warning`\n const { isDisabled, isLoading, isInvalid } = useFormFieldState(disabled, state)\n const messageId = state === 'error' && errorMessage ? errorMessageId : state === 'warning' && warningMessage ? warningMessageId : undefined\n\n return (\n <ControlGroupContext.Provider value={{ messageId, isDisabled, isInvalid, isLoading, orientation, state }}>\n <div data-slot='control-group-field' className='space-y-1.5 w-full'>\n <div\n data-slot='control-group'\n data-orientation={orientation}\n data-state={state}\n id={groupId}\n role='group'\n aria-describedby={messageId}\n className={cn('group flex w-full', orientation === 'vertical' && 'flex-col', isDisabled && 'pointer-events-none opacity-50', className)}\n {...props}\n />\n <ErrorMessage dataTestId='spectral-control-group-error-message' id={errorMessageId} message={isInvalid ? errorMessage : null} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace && state === 'error'} />\n <WarningMessage dataTestId='spectral-control-group-warning-message' id={warningMessageId} message={state === 'warning' ? warningMessage : null} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace && state === 'warning'} />\n </div>\n </ControlGroupContext.Provider>\n )\n}\n\nexport const ControlGroupItem = ({ className, ...props }: ComponentProps<typeof Slot>) => {\n const { orientation } = useControlGroup()\n\n return (\n <Slot\n data-slot='control-group-item'\n className={cn(\n 'rounded-none focus-within:z-10 hover:z-10',\n orientation === 'horizontal' && 'first:rounded-s-md last:me-0 last:rounded-e-md -me-0.5 h-auto',\n orientation === 'vertical' && 'first:rounded-ss-md first:rounded-se-md last:rounded-ee-md last:rounded-es-md last:mbe-0 -mbe-0.5 w-auto',\n className,\n )}\n {...props}\n />\n )\n}\n"],"mappings":";;;;;;;;;AAcA,MAAM,sBAAsB,cAA+C,KAAK;AAEhF,MAAa,wBAAwB;CACnC,MAAM,UAAU,WAAW,oBAAoB;AAC/C,KAAI,YAAY,KACd,OAAM,IAAI,MAAM,qDAAqD;AAEvE,QAAO;;AAeT,MAAa,gBAAgB,EAAE,WAAW,UAAU,cAAc,IAAI,sBAAsB,GAAG,sBAAsB,OAAO,MAAM,cAAc,cAAc,QAAQ,WAAW,gBAAgB,GAAG,YAA+B;CACjO,MAAM,UAAU,eAAe,IAAI,KAAK;CACxC,MAAM,iBAAiB,kBAAkB,QAAQ;CACjD,MAAM,mBAAmB,GAAG,QAAQ;CACpC,MAAM,EAAE,YAAY,WAAW,cAAc,kBAAkB,UAAU,MAAM;CAC/E,MAAM,YAAY,UAAU,WAAW,eAAe,iBAAiB,UAAU,aAAa,iBAAiB,mBAAmB;AAElI,QACE,oBAAC,oBAAoB,UAArB;EAA8B,OAAO;GAAE;GAAW;GAAY;GAAW;GAAW;GAAa;GAAO;YACtG,qBAAC,OAAD;GAAK,aAAU;GAAsB,WAAU;aAA/C;IACE,oBAAC,OAAD;KACE,aAAU;KACV,oBAAkB;KAClB,cAAY;KACZ,IAAI;KACJ,MAAK;KACL,oBAAkB;KAClB,WAAW,GAAG,qBAAqB,gBAAgB,cAAc,YAAY,cAAc,kCAAkC,UAAU;KACvI,GAAI;KACJ;IACF,oBAAC,cAAD;KAAc,YAAW;KAAuC,IAAI;KAAgB,SAAS,YAAY,eAAe;KAA2B;KAAqB,qBAAqB,uBAAuB,UAAU;KAAW;IACzO,oBAAC,gBAAD;KAAgB,YAAW;KAAyC,IAAI;KAAkB,SAAS,UAAU,YAAY,iBAAiB;KAA2B;KAAqB,qBAAqB,uBAAuB,UAAU;KAAa;IACzP;;EACuB;;AAInC,MAAa,oBAAoB,EAAE,WAAW,GAAG,YAAyC;CACxF,MAAM,EAAE,gBAAgB,iBAAiB;AAEzC,QACE,oBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,6CACA,gBAAgB,gBAAgB,iEAChC,gBAAgB,cAAc,4GAC9B,UACD;EACD,GAAI;EACJ"}
|
package/dist/DateTimePicker.js
CHANGED
|
@@ -16,7 +16,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
16
16
|
import "react-day-picker";
|
|
17
17
|
|
|
18
18
|
//#region src/components/DateTimePicker/DateTimePicker.tsx
|
|
19
|
-
const DateTimePicker = ({ calendarProps, className, defaultValue, disabled = false, disablePastDates = true, errorMessage, hourFormat, label, locale, messageReserveLines = 1, messageReserveSpace =
|
|
19
|
+
const DateTimePicker = ({ calendarProps, className, defaultValue, disabled = false, disablePastDates = true, errorMessage, hourFormat, label, locale, messageReserveLines = 1, messageReserveSpace = false, onChange, showTimePicker = true, state = "default", timeLocale, timeTranslations, value, id, ...props }) => {
|
|
20
20
|
const fieldId = useFormFieldId(id);
|
|
21
21
|
const errorMessageId = getErrorMessageId(fieldId);
|
|
22
22
|
const describedBy = state === "error" && errorMessage ? errorMessageId : void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateTimePicker.js","names":[],"sources":["../src/components/DateTimePicker/DateTimePicker.tsx"],"sourcesContent":["import { CalendarIcon } from '@components/Icons'\nimport { Popover, PopoverContent, PopoverTrigger } from '@components/Popover/Popover'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { ErrorMessage, getErrorMessageId, useFormFieldId } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useCallback, useMemo, type ComponentProps } from 'react'\nimport { type Locale } from 'react-day-picker'\nimport { Calendar, type CalendarProps } from './Calendar'\nimport { DateTimeDisplayInput } from './DateTimeDisplayInput'\nimport { detectHourFormat, getResolvedLocale, type HourFormat, type Period, type TimePickerTranslations } from './DateTimeUtils'\nimport { TimePicker } from './TimePicker'\n\nexport interface DateTimePickerProps extends Omit<ComponentProps<'div'>, 'onChange' | 'defaultValue'> {\n calendarProps?: Omit<CalendarProps, 'mode' | 'selected' | 'onSelect' | 'disablePastDates' | 'locale'>\n defaultValue?: Date\n disabled?: boolean\n disablePastDates?: boolean\n errorMessage?: string | string[] | Record<string, unknown> | null\n hourFormat?: HourFormat\n label?: string\n locale?: Partial<Locale>\n messageReserveLines?: number\n messageReserveSpace?: boolean\n onChange?: (date: Date | undefined) => void\n showTimePicker?: boolean\n state?: 'default' | 'disabled' | 'error'\n timeLocale?: string\n /** Override translation strings for time picker ARIA labels */\n timeTranslations?: Partial<TimePickerTranslations>\n value?: Date\n}\n\nexport const DateTimePicker = ({\n calendarProps,\n className,\n defaultValue,\n disabled = false,\n disablePastDates = true,\n errorMessage,\n hourFormat,\n label,\n locale,\n messageReserveLines = 1,\n messageReserveSpace =
|
|
1
|
+
{"version":3,"file":"DateTimePicker.js","names":[],"sources":["../src/components/DateTimePicker/DateTimePicker.tsx"],"sourcesContent":["import { CalendarIcon } from '@components/Icons'\nimport { Popover, PopoverContent, PopoverTrigger } from '@components/Popover/Popover'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { ErrorMessage, getErrorMessageId, useFormFieldId } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useCallback, useMemo, type ComponentProps } from 'react'\nimport { type Locale } from 'react-day-picker'\nimport { Calendar, type CalendarProps } from './Calendar'\nimport { DateTimeDisplayInput } from './DateTimeDisplayInput'\nimport { detectHourFormat, getResolvedLocale, type HourFormat, type Period, type TimePickerTranslations } from './DateTimeUtils'\nimport { TimePicker } from './TimePicker'\n\nexport interface DateTimePickerProps extends Omit<ComponentProps<'div'>, 'onChange' | 'defaultValue'> {\n calendarProps?: Omit<CalendarProps, 'mode' | 'selected' | 'onSelect' | 'disablePastDates' | 'locale'>\n defaultValue?: Date\n disabled?: boolean\n disablePastDates?: boolean\n errorMessage?: string | string[] | Record<string, unknown> | null\n hourFormat?: HourFormat\n label?: string\n locale?: Partial<Locale>\n messageReserveLines?: number\n messageReserveSpace?: boolean\n onChange?: (date: Date | undefined) => void\n showTimePicker?: boolean\n state?: 'default' | 'disabled' | 'error'\n timeLocale?: string\n /** Override translation strings for time picker ARIA labels */\n timeTranslations?: Partial<TimePickerTranslations>\n value?: Date\n}\n\nexport const DateTimePicker = ({\n calendarProps,\n className,\n defaultValue,\n disabled = false,\n disablePastDates = true,\n errorMessage,\n hourFormat,\n label,\n locale,\n messageReserveLines = 1,\n messageReserveSpace = false,\n onChange,\n showTimePicker = true,\n state = 'default',\n timeLocale,\n timeTranslations,\n value,\n id,\n ...props\n}: DateTimePickerProps) => {\n const fieldId = useFormFieldId(id)\n const errorMessageId = getErrorMessageId(fieldId)\n const describedBy = state === 'error' && errorMessage ? errorMessageId : undefined\n const [date, setDate] = useUncontrolledState<Date | undefined>({\n defaultValue,\n onChange,\n value,\n })\n\n // Resolve time locale with fallback chain\n const resolvedTimeLocale = useMemo(() => getResolvedLocale(timeLocale), [timeLocale])\n\n // Auto-detect hour format from locale if not explicitly provided\n const effectiveHourFormat = useMemo(() => hourFormat ?? detectHourFormat(resolvedTimeLocale), [hourFormat, resolvedTimeLocale])\n\n const handleDateSelect = (selectedDate: Date | undefined) => {\n if (!selectedDate) {\n setDate(undefined)\n return\n }\n\n // Preserve the time from the existing date\n if (date) {\n selectedDate.setHours(date.getHours(), date.getMinutes(), 0, 0)\n }\n\n setDate(selectedDate)\n }\n\n const handleTimeChange = useCallback(\n (newDate: Date | undefined) => {\n setDate(newDate)\n },\n [setDate],\n )\n\n const handlePeriodChange = (period: Period) => {\n // Period change is already handled in TimePicker\n // This callback is for external consumers who want to react to period changes\n void period\n }\n\n const handleInputDateChange = (newDate: Date | undefined) => {\n setDate(newDate)\n }\n\n return (\n <Popover>\n <div className={cn('w-full', className)} data-slot='datetime-picker' data-testid='spectral-datetime-picker' {...props}>\n <DateTimeDisplayInput\n aria-describedby={describedBy}\n aria-invalid={state === 'error'}\n className={cn('gap-4 pr-12 flex w-full justify-start', !date && 'text-text-secondary')}\n data-testid='spectral-datetime-picker-input'\n disabled={disabled}\n endIcon={\n <PopoverTrigger asChild disabled={disabled}>\n <CalendarIcon\n aria-label='Open date picker'\n className={cn('right-4 text-input-icon hover:text-input-icon--hover absolute top-1/2 -translate-y-1/2 cursor-pointer focus:outline-none', disabled ? 'pointer-events-none cursor-not-allowed opacity-50' : 'hover:text-input-icon--hover cursor-pointer')}\n data-testid='spectral-datetime-picker-trigger'\n disabled={disabled}\n />\n </PopoverTrigger>\n }\n hourFormat={effectiveHourFormat}\n id={fieldId}\n label={label}\n onChange={handleInputDateChange}\n showTime={showTimePicker}\n state={state}\n value={date}\n />\n <ErrorMessage dataTestId='spectral-datetime-picker-error-message' id={errorMessageId} message={state === 'error' ? errorMessage : null} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace} />\n </div>\n\n <PopoverContent\n align='start'\n className={cn('rounded-lg py-4 px-6 flex', !showTimePicker && 'w-[330px]', showTimePicker && effectiveHourFormat === '24' && 'w-[486px]', showTimePicker && effectiveHourFormat === '12' && 'w-[560px]')}\n data-testid='spectral-datetime-picker-popover'\n onOpenAutoFocus={(e) => e.preventDefault()}\n >\n <Calendar {...calendarProps} disablePastDates={disablePastDates} locale={locale} mode='single' onSelect={handleDateSelect} selected={date} />\n {showTimePicker && (\n <div className='pl-6 border-l border-border-secondary'>\n <TimePicker date={date} hourFormat={effectiveHourFormat} locale={resolvedTimeLocale} onChange={handleTimeChange} onPeriodChange={handlePeriodChange} translations={timeTranslations} />\n </div>\n )}\n </PopoverContent>\n </Popover>\n )\n}\n\nDateTimePicker.displayName = 'DateTimePicker'\n\n// Re-export sub-components for granular usage\nexport { Calendar } from './Calendar'\nexport { DateTimeDisplayInput } from './DateTimeDisplayInput'\nexport { DateTimeInput } from './DateTimeInput'\nexport { TimePeriodSelect } from './TimePeriodSelect'\nexport { TimePicker } from './TimePicker'\nexport * from './DateTimeUtils'\nexport { type Locale } from 'react-day-picker'\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgCA,MAAa,kBAAkB,EAC7B,eACA,WACA,cACA,WAAW,OACX,mBAAmB,MACnB,cACA,YACA,OACA,QACA,sBAAsB,GACtB,sBAAsB,OACtB,UACA,iBAAiB,MACjB,QAAQ,WACR,YACA,kBACA,OACA,IACA,GAAG,YACsB;CACzB,MAAM,UAAU,eAAe,GAAG;CAClC,MAAM,iBAAiB,kBAAkB,QAAQ;CACjD,MAAM,cAAc,UAAU,WAAW,eAAe,iBAAiB;CACzE,MAAM,CAAC,MAAM,WAAW,qBAAuC;EAC7D;EACA;EACA;EACD,CAAC;CAGF,MAAM,qBAAqB,cAAc,kBAAkB,WAAW,EAAE,CAAC,WAAW,CAAC;CAGrF,MAAM,sBAAsB,cAAc,cAAc,iBAAiB,mBAAmB,EAAE,CAAC,YAAY,mBAAmB,CAAC;CAE/H,MAAM,oBAAoB,iBAAmC;AAC3D,MAAI,CAAC,cAAc;AACjB,WAAQ,OAAU;AAClB;;AAIF,MAAI,KACF,cAAa,SAAS,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,GAAG,EAAE;AAGjE,UAAQ,aAAa;;CAGvB,MAAM,mBAAmB,aACtB,YAA8B;AAC7B,UAAQ,QAAQ;IAElB,CAAC,QAAQ,CACV;CAED,MAAM,sBAAsB,WAAmB;CAM/C,MAAM,yBAAyB,YAA8B;AAC3D,UAAQ,QAAQ;;AAGlB,QACE,qBAAC,SAAD,aACE,qBAAC,OAAD;EAAK,WAAW,GAAG,UAAU,UAAU;EAAE,aAAU;EAAkB,eAAY;EAA2B,GAAI;YAAhH,CACE,oBAAC,sBAAD;GACE,oBAAkB;GAClB,gBAAc,UAAU;GACxB,WAAW,GAAG,yCAAyC,CAAC,QAAQ,sBAAsB;GACtF,eAAY;GACF;GACV,SACE,oBAAC,gBAAD;IAAgB;IAAkB;cAChC,oBAAC,cAAD;KACE,cAAW;KACX,WAAW,GAAG,4HAA4H,WAAW,sDAAsD,8CAA8C;KACzP,eAAY;KACF;KACV;IACa;GAEnB,YAAY;GACZ,IAAI;GACG;GACP,UAAU;GACV,UAAU;GACH;GACP,OAAO;GACP,GACF,oBAAC,cAAD;GAAc,YAAW;GAAyC,IAAI;GAAgB,SAAS,UAAU,UAAU,eAAe;GAA2B;GAA0C;GAAuB,EAC1N;KAEN,qBAAC,gBAAD;EACE,OAAM;EACN,WAAW,GAAG,6BAA6B,CAAC,kBAAkB,aAAa,kBAAkB,wBAAwB,QAAQ,aAAa,kBAAkB,wBAAwB,QAAQ,YAAY;EACxM,eAAY;EACZ,kBAAkB,MAAM,EAAE,gBAAgB;YAJ5C,CAME,oBAAC,UAAD;GAAU,GAAI;GAAiC;GAA0B;GAAQ,MAAK;GAAS,UAAU;GAAkB,UAAU;GAAQ,GAC5I,kBACC,oBAAC,OAAD;GAAK,WAAU;aACb,oBAAC,YAAD;IAAkB;IAAM,YAAY;IAAqB,QAAQ;IAAoB,UAAU;IAAkB,gBAAgB;IAAoB,cAAc;IAAoB;GACnL,EAEO;IACT;;AAId,eAAe,cAAc"}
|
package/dist/FormFieldMessage.js
CHANGED
|
@@ -20,7 +20,7 @@ const renderFieldMessageContent = (message) => {
|
|
|
20
20
|
}
|
|
21
21
|
return String(message);
|
|
22
22
|
};
|
|
23
|
-
const FormFieldMessage = ({ ariaLive, className, containerClassName, dataTestId, id, message, messageReserveLines = 1, messageReserveSpace =
|
|
23
|
+
const FormFieldMessage = ({ ariaLive, className, containerClassName, dataTestId, id, message, messageReserveLines = 1, messageReserveSpace = false, role, tone }) => {
|
|
24
24
|
const content = renderFieldMessageContent(message);
|
|
25
25
|
const isVisible = Boolean(content);
|
|
26
26
|
const reservedHeight = messageReserveSpace && messageReserveLines > 0 ? `${Math.max(messageReserveLines, 1) * 1.25}rem` : void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormFieldMessage.js","names":[],"sources":["../src/components/FormFieldMessage/FormFieldMessage.tsx"],"sourcesContent":["import { cn } from '@utils/twUtils'\nimport { type ReactNode } from 'react'\n\nexport type FormFieldMessageValue = string | string[] | Record<string, unknown> | null | undefined\n\ninterface BaseFieldMessageProps {\n className?: string\n containerClassName?: string\n dataTestId?: string\n id: string\n message: FormFieldMessageValue\n messageReserveLines?: number\n messageReserveSpace?: boolean\n}\n\nconst renderFieldMessageContent = (message: FormFieldMessageValue): ReactNode => {\n if (!message) return null\n\n if (typeof message === 'string') {\n return message\n }\n\n if (Array.isArray(message)) {\n return message.join(', ')\n }\n\n if (typeof message === 'object') {\n if ('message' in message && typeof message.message === 'string') return message.message\n if ('error' in message && typeof message.error === 'string') return message.error\n if ('details' in message && typeof message.details === 'string') return message.details\n try {\n return JSON.stringify(message)\n } catch {\n return '[Circular]'\n }\n }\n\n return String(message)\n}\n\nconst FormFieldMessage = ({\n ariaLive,\n className,\n containerClassName,\n dataTestId,\n id,\n message,\n messageReserveLines = 1,\n messageReserveSpace =
|
|
1
|
+
{"version":3,"file":"FormFieldMessage.js","names":[],"sources":["../src/components/FormFieldMessage/FormFieldMessage.tsx"],"sourcesContent":["import { cn } from '@utils/twUtils'\nimport { type ReactNode } from 'react'\n\nexport type FormFieldMessageValue = string | string[] | Record<string, unknown> | null | undefined\n\ninterface BaseFieldMessageProps {\n className?: string\n containerClassName?: string\n dataTestId?: string\n id: string\n message: FormFieldMessageValue\n messageReserveLines?: number\n messageReserveSpace?: boolean\n}\n\nconst renderFieldMessageContent = (message: FormFieldMessageValue): ReactNode => {\n if (!message) return null\n\n if (typeof message === 'string') {\n return message\n }\n\n if (Array.isArray(message)) {\n return message.join(', ')\n }\n\n if (typeof message === 'object') {\n if ('message' in message && typeof message.message === 'string') return message.message\n if ('error' in message && typeof message.error === 'string') return message.error\n if ('details' in message && typeof message.details === 'string') return message.details\n try {\n return JSON.stringify(message)\n } catch {\n return '[Circular]'\n }\n }\n\n return String(message)\n}\n\nconst FormFieldMessage = ({\n ariaLive,\n className,\n containerClassName,\n dataTestId,\n id,\n message,\n messageReserveLines = 1,\n messageReserveSpace = false,\n role,\n tone,\n}: BaseFieldMessageProps & {\n ariaLive: 'assertive' | 'polite'\n role: 'alert' | 'status'\n tone: 'danger' | 'warning'\n}) => {\n const content = renderFieldMessageContent(message)\n const isVisible = Boolean(content)\n const reservedHeight = messageReserveSpace && messageReserveLines > 0 ? `${Math.max(messageReserveLines, 1) * 1.25}rem` : undefined\n const toneClasses = tone === 'danger' ? 'text-danger-400!' : 'text-warning-400!'\n\n return (\n <div\n aria-hidden={!isVisible}\n className={cn(\n 'transition-[opacity,transform] duration-150 ease-out motion-reduce:transition-none',\n messageReserveSpace ? 'pt-1' : 'pt-0',\n isVisible ? 'translate-y-0 opacity-100' : '-translate-y-0.5 opacity-0',\n !messageReserveSpace && !isVisible && 'hidden',\n containerClassName,\n )}\n style={reservedHeight ? { minHeight: reservedHeight } : undefined}\n >\n <p aria-atomic={isVisible ? 'true' : undefined} aria-live={isVisible ? ariaLive : undefined} className={cn('m-0! text-sm leading-5 overflow-hidden', toneClasses, className)} data-testid={isVisible ? dataTestId : undefined} id={id} role={isVisible ? role : undefined}>\n {content}\n </p>\n </div>\n )\n}\n\nexport const ErrorMessage = ({ className, containerClassName, dataTestId = 'spectral-form-field-error-message', id, message, messageReserveLines, messageReserveSpace }: BaseFieldMessageProps) => {\n return <FormFieldMessage ariaLive='polite' className={className} containerClassName={containerClassName} dataTestId={dataTestId} id={id} message={message} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace} role='status' tone='danger' />\n}\n\nexport const WarningMessage = ({ className, containerClassName, dataTestId = 'spectral-form-field-warning-message', id, message, messageReserveLines, messageReserveSpace }: BaseFieldMessageProps) => {\n return <FormFieldMessage ariaLive='polite' className={className} containerClassName={containerClassName} dataTestId={dataTestId} id={id} message={message} messageReserveLines={messageReserveLines} messageReserveSpace={messageReserveSpace} role='status' tone='warning' />\n}\n"],"mappings":";;;;;;AAeA,MAAM,6BAA6B,YAA8C;AAC/E,KAAI,CAAC,QAAS,QAAO;AAErB,KAAI,OAAO,YAAY,SACrB,QAAO;AAGT,KAAI,MAAM,QAAQ,QAAQ,CACxB,QAAO,QAAQ,KAAK,KAAK;AAG3B,KAAI,OAAO,YAAY,UAAU;AAC/B,MAAI,aAAa,WAAW,OAAO,QAAQ,YAAY,SAAU,QAAO,QAAQ;AAChF,MAAI,WAAW,WAAW,OAAO,QAAQ,UAAU,SAAU,QAAO,QAAQ;AAC5E,MAAI,aAAa,WAAW,OAAO,QAAQ,YAAY,SAAU,QAAO,QAAQ;AAChF,MAAI;AACF,UAAO,KAAK,UAAU,QAAQ;UACxB;AACN,UAAO;;;AAIX,QAAO,OAAO,QAAQ;;AAGxB,MAAM,oBAAoB,EACxB,UACA,WACA,oBACA,YACA,IACA,SACA,sBAAsB,GACtB,sBAAsB,OACtB,MACA,WAKI;CACJ,MAAM,UAAU,0BAA0B,QAAQ;CAClD,MAAM,YAAY,QAAQ,QAAQ;CAClC,MAAM,iBAAiB,uBAAuB,sBAAsB,IAAI,GAAG,KAAK,IAAI,qBAAqB,EAAE,GAAG,KAAK,OAAO;CAC1H,MAAM,cAAc,SAAS,WAAW,qBAAqB;AAE7D,QACE,oBAAC,OAAD;EACE,eAAa,CAAC;EACd,WAAW,GACT,sFACA,sBAAsB,SAAS,QAC/B,YAAY,8BAA8B,8BAC1C,CAAC,uBAAuB,CAAC,aAAa,UACtC,mBACD;EACD,OAAO,iBAAiB,EAAE,WAAW,gBAAgB,GAAG;YAExD,oBAAC,KAAD;GAAG,eAAa,YAAY,SAAS;GAAW,aAAW,YAAY,WAAW;GAAW,WAAW,GAAG,0CAA0C,aAAa,UAAU;GAAE,eAAa,YAAY,aAAa;GAAe;GAAI,MAAM,YAAY,OAAO;aAC7P;GACC;EACA;;AAIV,MAAa,gBAAgB,EAAE,WAAW,oBAAoB,aAAa,qCAAqC,IAAI,SAAS,qBAAqB,0BAAiD;AACjM,QAAO,oBAAC,kBAAD;EAAkB,UAAS;EAAoB;EAA+B;EAAgC;EAAgB;EAAa;EAA8B;EAA0C;EAAqB,MAAK;EAAS,MAAK;EAAW;;AAG/Q,MAAa,kBAAkB,EAAE,WAAW,oBAAoB,aAAa,uCAAuC,IAAI,SAAS,qBAAqB,0BAAiD;AACrM,QAAO,oBAAC,kBAAD;EAAkB,UAAS;EAAoB;EAA+B;EAAgC;EAAgB;EAAa;EAA8B;EAA0C;EAAqB,MAAK;EAAS,MAAK;EAAY"}
|
package/dist/Input.js
CHANGED
|
@@ -38,7 +38,7 @@ const getAutoCompleteValue = (type) => {
|
|
|
38
38
|
}[type] || "off";
|
|
39
39
|
};
|
|
40
40
|
const Input = (allProps) => {
|
|
41
|
-
const { className, clearOnFocus = false, defaultValue, disabled, endIcon, errorMessage, id, label, labelClassName, messageReserveLines = 1, messageReserveSpace =
|
|
41
|
+
const { className, clearOnFocus = false, defaultValue, disabled, endIcon, errorMessage, id, label, labelClassName, messageReserveLines = 1, messageReserveSpace = false, name, onBlur, onChange, onFocus, placeholder, prefix, ref, required, showClearButton = false, showStateIcon = true, startIcon, state = "default", suppressHydrationWarning = true, type = "text", value: valueProp, warningMessage, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, ...props } = allProps;
|
|
42
42
|
const inputId = useFormFieldId(id, name);
|
|
43
43
|
const errorMessageId = `${inputId}-error`;
|
|
44
44
|
const warningMessageId = `${inputId}-warning`;
|