reshaped 3.4.6 → 3.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +17 -2
  2. package/dist/bundle.css +1 -1
  3. package/dist/bundle.d.ts +2 -0
  4. package/dist/bundle.js +11 -11
  5. package/dist/components/Autocomplete/Autocomplete.js +25 -8
  6. package/dist/components/Autocomplete/Autocomplete.module.css +1 -0
  7. package/dist/components/Autocomplete/tests/Autocomplete.stories.d.ts +15 -1
  8. package/dist/components/Autocomplete/tests/Autocomplete.stories.js +193 -64
  9. package/dist/components/Calendar/Calendar.module.css +1 -1
  10. package/dist/components/Checkbox/Checkbox.js +17 -4
  11. package/dist/components/Checkbox/Checkbox.module.css +1 -1
  12. package/dist/components/Checkbox/Checkbox.types.d.ts +1 -0
  13. package/dist/components/Checkbox/tests/Checkbox.stories.d.ts +1 -0
  14. package/dist/components/Checkbox/tests/Checkbox.stories.js +40 -0
  15. package/dist/components/Dismissible/Dismissible.js +1 -0
  16. package/dist/components/DropdownMenu/DropdownMenu.js +1 -1
  17. package/dist/components/FormControl/FormControlLabel.js +2 -7
  18. package/dist/components/NumberField/NumberField.d.ts +6 -0
  19. package/dist/components/NumberField/NumberField.js +11 -0
  20. package/dist/components/NumberField/NumberField.module.css +1 -0
  21. package/dist/components/NumberField/NumberField.types.d.ts +19 -0
  22. package/dist/components/NumberField/NumberField.types.js +1 -0
  23. package/dist/components/NumberField/NumberFieldControlled.d.ts +6 -0
  24. package/dist/components/NumberField/NumberFieldControlled.js +146 -0
  25. package/dist/components/NumberField/NumberFieldUncontrolled.d.ts +6 -0
  26. package/dist/components/NumberField/NumberFieldUncontrolled.js +16 -0
  27. package/dist/components/NumberField/index.d.ts +2 -0
  28. package/dist/components/NumberField/index.js +1 -0
  29. package/dist/components/NumberField/tests/NumberField.stories.d.ts +29 -0
  30. package/dist/components/NumberField/tests/NumberField.stories.js +215 -0
  31. package/dist/components/PinField/PinField.module.css +1 -1
  32. package/dist/components/PinField/PinField.types.d.ts +1 -1
  33. package/dist/components/PinField/PinFieldControlled.js +1 -0
  34. package/dist/components/PinField/tests/PinField.stories.js +8 -0
  35. package/dist/components/Radio/Radio.js +11 -4
  36. package/dist/components/Radio/Radio.module.css +1 -1
  37. package/dist/components/Radio/Radio.types.d.ts +1 -0
  38. package/dist/components/Radio/tests/Radio.stories.d.ts +1 -0
  39. package/dist/components/Radio/tests/Radio.stories.js +31 -0
  40. package/dist/components/Select/Select.module.css +1 -1
  41. package/dist/components/Select/Select.types.d.ts +1 -1
  42. package/dist/components/Select/tests/Select.stories.js +42 -16
  43. package/dist/components/Switch/Switch.js +10 -4
  44. package/dist/components/Switch/Switch.module.css +1 -1
  45. package/dist/components/Switch/Switch.types.d.ts +1 -1
  46. package/dist/components/Switch/tests/Switch.stories.js +30 -15
  47. package/dist/components/TextField/TextField.js +1 -1
  48. package/dist/components/TextField/TextField.module.css +1 -1
  49. package/dist/components/TextField/TextField.types.d.ts +2 -2
  50. package/dist/components/TextField/index.d.ts +1 -1
  51. package/dist/components/TextField/tests/TextField.stories.js +4 -0
  52. package/dist/hooks/tests/useScrollLock.stories.d.ts +1 -0
  53. package/dist/hooks/tests/useScrollLock.stories.js +29 -0
  54. package/dist/icons/ChevronUp.d.ts +2 -0
  55. package/dist/icons/ChevronUp.js +5 -0
  56. package/dist/icons/Minus.d.ts +2 -0
  57. package/dist/icons/Minus.js +3 -0
  58. package/dist/icons/Plus.d.ts +2 -2
  59. package/dist/icons/Plus.js +2 -2
  60. package/dist/index.d.ts +2 -0
  61. package/dist/index.js +1 -0
  62. package/dist/utilities/scroll/lock.js +15 -7
  63. package/dist/utilities/scroll/lockStandard.d.ts +1 -2
  64. package/dist/utilities/scroll/lockStandard.js +2 -9
  65. package/package.json +1 -1
  66. package/dist/components/Autocomplete/tests/Autocomplete.test.stories.d.ts +0 -27
  67. package/dist/components/Autocomplete/tests/Autocomplete.test.stories.js +0 -86
@@ -0,0 +1,19 @@
1
+ import type { TextFieldBaseProps } from "../TextField";
2
+ import type * as G from "../../types/global";
3
+ export type BaseProps = Omit<TextFieldBaseProps, "endSlot" | "onChange"> & {
4
+ onChange?: G.ChangeHandler<number>;
5
+ increaseAriaLabel: string;
6
+ decreaseAriaLabel: string;
7
+ min?: number;
8
+ max?: number;
9
+ step?: number;
10
+ };
11
+ export type ControlledProps = BaseProps & {
12
+ value: number | null;
13
+ defaultValue?: never;
14
+ };
15
+ export type UncontrolledProps = BaseProps & {
16
+ value?: never;
17
+ defaultValue?: number;
18
+ };
19
+ export type Props = ControlledProps | UncontrolledProps;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import type * as T from "./NumberField.types";
2
+ declare const NumberFieldControlled: {
3
+ (props: T.ControlledProps): import("react/jsx-runtime").JSX.Element;
4
+ displayName: string;
5
+ };
6
+ export default NumberFieldControlled;
@@ -0,0 +1,146 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import Actionable from "../Actionable/index.js";
5
+ import Icon from "../Icon/index.js";
6
+ import TextField from "../TextField/index.js";
7
+ import IconChevronUp from "../../icons/ChevronUp.js";
8
+ import IconChevronDown from "../../icons/ChevronDown.js";
9
+ import IconPlus from "../../icons/Plus.js";
10
+ import IconMinus from "../../icons/Minus.js";
11
+ import useElementId from "../../hooks/useElementId.js";
12
+ import useHotkeys from "../../hooks/useHotkeys.js";
13
+ import * as keys from "../../constants/keys.js";
14
+ import s from "./NumberField.module.css";
15
+ import useHandlerRef from "../../hooks/useHandlerRef.js";
16
+ import { useFormControl } from "../FormControl/index.js";
17
+ const NumberFieldControlled = (props) => {
18
+ const { increaseAriaLabel, decreaseAriaLabel, min, max, step = 1, name, value, onChange, ...textFieldProps } = props;
19
+ const formControl = useFormControl();
20
+ const id = useElementId(textFieldProps.id);
21
+ const inputId = formControl?.attributes.id || props.inputAttributes?.id || id;
22
+ const disabled = formControl?.disabled || props.disabled;
23
+ const hasError = formControl?.hasError || props.hasError;
24
+ const increaseDisabled = disabled || (value && max ? value >= max : false);
25
+ const decreaseDisabled = disabled || (value && min ? value <= min : false);
26
+ const inputRef = React.useRef(null);
27
+ const rootRef = React.useRef(null);
28
+ const [textValue, setTextValue] = React.useState(value?.toString() || "");
29
+ // Sync value to a ref to handle holding controlss pressed
30
+ // And changing it without waiting for a rerender
31
+ const valueRef = React.useRef(value);
32
+ const onChangeRef = useHandlerRef(onChange);
33
+ const pressedTimeoutRef = React.useRef(null);
34
+ const changeIntervalRef = React.useRef(null);
35
+ const calculateDirectionalChange = React.useCallback((direction) => {
36
+ const delta = step * direction;
37
+ const value = valueRef.current;
38
+ let nextValue = value === null ? delta : value + delta;
39
+ if (max !== undefined && nextValue > max)
40
+ nextValue = max;
41
+ if (min !== undefined && nextValue < min)
42
+ nextValue = min;
43
+ // Keep the right precision and avoid JS rounding errors
44
+ const floatPartLength = value?.toString().split(".")[1]?.length || 0;
45
+ return Number(nextValue.toFixed(floatPartLength));
46
+ }, [step, min, max]);
47
+ const commitValue = React.useCallback((value, options) => {
48
+ onChangeRef.current?.({ value, name });
49
+ // Only update the ref here when typing in the input
50
+ // Otherwise it will be updated when value changes
51
+ if (!options?.programmatic)
52
+ valueRef.current = value;
53
+ }, [name, onChangeRef]);
54
+ const handleIncrease = React.useCallback(() => {
55
+ const nextValue = calculateDirectionalChange(1);
56
+ commitValue(nextValue, { programmatic: true });
57
+ }, [calculateDirectionalChange, commitValue]);
58
+ const handleDecrease = React.useCallback(() => {
59
+ const nextValue = calculateDirectionalChange(-1);
60
+ commitValue(nextValue, { programmatic: true });
61
+ }, [calculateDirectionalChange, commitValue]);
62
+ const handleChange = (args) => {
63
+ if (!args.value.match(/^(-?)[0-9]*(\.?)[0-9]*$/))
64
+ return;
65
+ setTextValue(args.value);
66
+ const numberValue = parseFloat(args.value);
67
+ if (isNaN(numberValue))
68
+ return;
69
+ commitValue(numberValue);
70
+ };
71
+ const handleControlPointerDown = (e, callback) => {
72
+ if (disabled)
73
+ return;
74
+ callback();
75
+ if (e.pointerType !== "touch") {
76
+ inputRef.current?.focus();
77
+ }
78
+ pressedTimeoutRef.current = setTimeout(() => {
79
+ changeIntervalRef.current = setInterval(() => {
80
+ callback();
81
+ }, 50);
82
+ }, 500);
83
+ };
84
+ const handleControlPointerUp = () => {
85
+ if (disabled)
86
+ return;
87
+ if (pressedTimeoutRef.current) {
88
+ clearTimeout(pressedTimeoutRef.current);
89
+ pressedTimeoutRef.current = null;
90
+ }
91
+ if (changeIntervalRef.current) {
92
+ clearTimeout(changeIntervalRef.current);
93
+ changeIntervalRef.current = null;
94
+ }
95
+ };
96
+ useHotkeys({
97
+ [keys.UP]: handleIncrease,
98
+ [keys.DOWN]: handleDecrease,
99
+ }, [handleIncrease, handleDecrease], {
100
+ preventDefault: true,
101
+ ref: rootRef,
102
+ });
103
+ React.useEffect(() => {
104
+ valueRef.current = value;
105
+ setTextValue(value?.toString() ?? "");
106
+ }, [value]);
107
+ const controlsNode = (_jsxs("span", { className: s.controls, children: [_jsxs(Actionable, { className: s.control, disabled: increaseDisabled, disableFocusRing: true, as: "span", attributes: {
108
+ "aria-label": increaseAriaLabel,
109
+ "aria-controls": id,
110
+ role: "button",
111
+ tabIndex: increaseDisabled ? undefined : -1,
112
+ onPointerDown: (e) => handleControlPointerDown(e, handleIncrease),
113
+ onPointerUp: handleControlPointerUp,
114
+ onPointerLeave: handleControlPointerUp,
115
+ // Prevent menu from opening on long press
116
+ onContextMenu: (e) => e.preventDefault(),
117
+ }, children: [_jsx(Icon, { svg: IconChevronUp, size: 3, className: s["icon--mouse"] }), _jsx(Icon, { svg: IconPlus, size: 4, className: s["icon--touch"] })] }), _jsxs(Actionable, { className: s.control, disabled: decreaseDisabled, disableFocusRing: true, as: "span", attributes: {
118
+ "aria-label": decreaseAriaLabel,
119
+ "aria-controls": id,
120
+ role: "button",
121
+ tabIndex: decreaseDisabled ? undefined : -1,
122
+ onPointerDown: (e) => handleControlPointerDown(e, handleDecrease),
123
+ onPointerUp: handleControlPointerUp,
124
+ onPointerLeave: handleControlPointerUp,
125
+ // Prevent menu from opening on long press
126
+ onContextMenu: (e) => e.preventDefault(),
127
+ }, children: [_jsx(Icon, { svg: IconChevronDown, size: 3, className: s["icon--mouse"] }), _jsx(Icon, { svg: IconMinus, size: 4, className: s["icon--touch"] })] })] }));
128
+ return (_jsx(TextField, { attributes: {
129
+ ...textFieldProps.attributes,
130
+ role: "group",
131
+ ref: rootRef,
132
+ }, id: inputId, hasError: hasError, inputAttributes: {
133
+ ...textFieldProps.inputAttributes,
134
+ ref: inputRef,
135
+ inputMode: "numeric",
136
+ autoComplete: "off",
137
+ autoCorrect: "off",
138
+ spellCheck: "false",
139
+ min,
140
+ max,
141
+ step,
142
+ className: s.field,
143
+ }, ...textFieldProps, disabled: disabled, value: textValue, onChange: handleChange, name: name, endSlot: controlsNode }));
144
+ };
145
+ NumberFieldControlled.displayName = "NumberFieldControlled";
146
+ export default NumberFieldControlled;
@@ -0,0 +1,6 @@
1
+ import type * as T from "./NumberField.types";
2
+ declare const NumberFieldUncontrolled: {
3
+ (props: T.UncontrolledProps): import("react/jsx-runtime").JSX.Element;
4
+ displayName: string;
5
+ };
6
+ export default NumberFieldUncontrolled;
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import NumberFieldControlled from "./NumberFieldControlled.js";
5
+ const NumberFieldUncontrolled = (props) => {
6
+ const { defaultValue, onChange } = props;
7
+ const [value, setValue] = React.useState(defaultValue ?? null);
8
+ const handleChange = (args) => {
9
+ setValue(args.value);
10
+ if (onChange)
11
+ onChange(args);
12
+ };
13
+ return (_jsx(NumberFieldControlled, { ...props, value: value, defaultValue: undefined, onChange: handleChange }));
14
+ };
15
+ NumberFieldUncontrolled.displayName = "NumberFieldUncontrolled";
16
+ export default NumberFieldUncontrolled;
@@ -0,0 +1,2 @@
1
+ export { default } from "./NumberField";
2
+ export type { Props as NumberFieldProps } from "./NumberField.types";
@@ -0,0 +1 @@
1
+ export { default } from "./NumberField.js";
@@ -0,0 +1,29 @@
1
+ import { StoryObj } from "@storybook/react";
2
+ import { Mock } from "@storybook/test";
3
+ declare const _default: {
4
+ title: string;
5
+ component: {
6
+ (props: import("./..").NumberFieldProps): import("react").JSX.Element;
7
+ displayName: string;
8
+ };
9
+ parameters: {
10
+ iframe: {
11
+ url: string;
12
+ };
13
+ };
14
+ };
15
+ export default _default;
16
+ export declare const base: StoryObj;
17
+ export declare const disabled: StoryObj<{
18
+ handleChange: Mock;
19
+ }>;
20
+ export declare const defaultValue: StoryObj<{
21
+ handleChange: Mock;
22
+ }>;
23
+ export declare const value: StoryObj<{
24
+ handleChange: Mock;
25
+ }>;
26
+ export declare const minMax: StoryObj;
27
+ export declare const className: StoryObj;
28
+ export declare const formControl: StoryObj;
29
+ export declare const valueChanges: StoryObj;
@@ -0,0 +1,215 @@
1
+ import { expect, fn, userEvent } from "@storybook/test";
2
+ import FormControl from "../../FormControl/index.js";
3
+ import NumberField from "../index.js";
4
+ import { Example } from "../../../utilities/storybook/index.js";
5
+ export default {
6
+ title: "Components/NumberField",
7
+ component: NumberField,
8
+ parameters: {
9
+ iframe: {
10
+ url: "https://reshaped.so/docs/components/number-field",
11
+ },
12
+ },
13
+ };
14
+ export const base = {
15
+ name: "base",
16
+ render: () => {
17
+ return (<Example>
18
+ <Example.Item title="base">
19
+ <NumberField name="test-name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>
20
+ </Example.Item>
21
+ </Example>);
22
+ },
23
+ play: async ({ canvas }) => {
24
+ const input = canvas.getByRole("textbox");
25
+ const [increaseButton, decreaseButton] = canvas.getAllByRole("button");
26
+ expect(input).toHaveAttribute("name", "test-name");
27
+ expect(increaseButton).toHaveAccessibleName("Increase");
28
+ expect(decreaseButton).toHaveAccessibleName("Decrease");
29
+ },
30
+ };
31
+ export const disabled = {
32
+ name: "disabled",
33
+ args: {
34
+ handleChange: fn(),
35
+ },
36
+ render: (args) => (<NumberField name="test-name" onChange={args.handleChange} increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" disabled inputAttributes={{ "aria-label": "Label" }}/>),
37
+ play: async ({ canvas, args }) => {
38
+ const input = canvas.getByRole("textbox");
39
+ const increaseButton = document.querySelector('[aria-label="Increase"]');
40
+ const decreaseButton = document.querySelector('[aria-label="Decrease"]');
41
+ expect(input).toBeDisabled();
42
+ await userEvent.click(increaseButton);
43
+ expect(args.handleChange).not.toHaveBeenCalled();
44
+ await userEvent.click(decreaseButton);
45
+ expect(args.handleChange).not.toHaveBeenCalled();
46
+ },
47
+ };
48
+ export const defaultValue = {
49
+ name: "defaultValue, uncontrolled",
50
+ args: {
51
+ handleChange: fn(),
52
+ },
53
+ render: (args) => (<NumberField name="test-name" defaultValue={2} onChange={args.handleChange} increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>),
54
+ play: async ({ canvas, args }) => {
55
+ const input = canvas.getByRole("textbox");
56
+ const [increaseButton, decreaseButton] = canvas.getAllByRole("button");
57
+ expect(input).toHaveValue("2");
58
+ input.focus();
59
+ await userEvent.keyboard("3");
60
+ expect(args.handleChange).toBeCalledTimes(1);
61
+ expect(args.handleChange).toHaveBeenLastCalledWith({
62
+ name: "test-name",
63
+ value: 23,
64
+ });
65
+ expect(input).toHaveValue("23");
66
+ await userEvent.click(increaseButton);
67
+ expect(args.handleChange).toBeCalledTimes(2);
68
+ expect(args.handleChange).toHaveBeenLastCalledWith({
69
+ name: "test-name",
70
+ value: 24,
71
+ });
72
+ expect(input).toHaveValue("24");
73
+ await userEvent.click(decreaseButton);
74
+ expect(args.handleChange).toBeCalledTimes(3);
75
+ expect(args.handleChange).toHaveBeenLastCalledWith({
76
+ name: "test-name",
77
+ value: 23,
78
+ });
79
+ expect(input).toHaveValue("23");
80
+ },
81
+ };
82
+ export const value = {
83
+ name: "value, controlled",
84
+ args: {
85
+ handleChange: fn(),
86
+ },
87
+ render: (args) => (<NumberField name="test-name" value={2} onChange={args.handleChange} increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>),
88
+ play: async ({ canvas, args }) => {
89
+ const input = canvas.getByRole("textbox");
90
+ const [increaseButton, decreaseButton] = canvas.getAllByRole("button");
91
+ expect(input).toHaveValue("2");
92
+ input.focus();
93
+ await userEvent.keyboard("3");
94
+ expect(args.handleChange).toBeCalledTimes(1);
95
+ expect(args.handleChange).toHaveBeenLastCalledWith({
96
+ name: "test-name",
97
+ value: 23,
98
+ });
99
+ expect(input).toHaveValue("23");
100
+ await userEvent.click(increaseButton);
101
+ expect(args.handleChange).toBeCalledTimes(2);
102
+ expect(args.handleChange).toHaveBeenLastCalledWith({
103
+ name: "test-name",
104
+ value: 24,
105
+ });
106
+ expect(input).toHaveValue("23");
107
+ await userEvent.click(decreaseButton);
108
+ expect(args.handleChange).toBeCalledTimes(3);
109
+ expect(args.handleChange).toHaveBeenLastCalledWith({
110
+ name: "test-name",
111
+ value: 22,
112
+ });
113
+ expect(input).toHaveValue("23");
114
+ },
115
+ };
116
+ export const minMax = {
117
+ name: "min, max, step",
118
+ render: () => (<NumberField name="test-name" defaultValue={6} min={5} max={15} step={5} increaseAriaLabel="Increase" decreaseAriaLabel="Decrease" inputAttributes={{ "aria-label": "Label" }}/>),
119
+ play: async ({ canvas }) => {
120
+ const input = canvas.getByRole("textbox");
121
+ const [increaseButton, decreaseButton] = canvas.getAllByRole("button");
122
+ expect(input).toHaveValue("6");
123
+ await userEvent.click(increaseButton);
124
+ expect(input).toHaveValue("11");
125
+ await userEvent.click(decreaseButton);
126
+ await userEvent.click(decreaseButton);
127
+ expect(input).toHaveValue("5");
128
+ await userEvent.click(increaseButton);
129
+ await userEvent.click(increaseButton);
130
+ await userEvent.click(increaseButton);
131
+ expect(input).toHaveValue("15");
132
+ },
133
+ };
134
+ export const className = {
135
+ name: "className, attributes",
136
+ render: () => (<div data-testid="root">
137
+ <NumberField className="test-classname" attributes={{ id: "test-id" }} name="name" inputAttributes={{ id: "test-input-id", "aria-label": "Label" }} increaseAriaLabel="Increase" decreaseAriaLabel="Decrease"/>
138
+ </div>),
139
+ play: async ({ canvas }) => {
140
+ const root = canvas.getByTestId("root").firstChild;
141
+ const input = canvas.getByRole("textbox");
142
+ expect(root).toHaveClass("test-classname");
143
+ expect(root).toHaveAttribute("id", "test-id");
144
+ expect(input).toHaveAttribute("id", "test-input-id");
145
+ },
146
+ };
147
+ export const formControl = {
148
+ name: "test: FormControl",
149
+ render: () => (<Example>
150
+ <Example.Item title="FormControl">
151
+ <FormControl>
152
+ <FormControl.Label>Label</FormControl.Label>
153
+ <NumberField name="name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease"/>
154
+ <FormControl.Helper>Helper</FormControl.Helper>
155
+ </FormControl>
156
+ </Example.Item>
157
+
158
+ <Example.Item title="FormControl, disabled">
159
+ <FormControl disabled>
160
+ <FormControl.Label>Label</FormControl.Label>
161
+ <NumberField name="name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease"/>
162
+ <FormControl.Helper>Helper</FormControl.Helper>
163
+ </FormControl>
164
+ </Example.Item>
165
+
166
+ <Example.Item title="FormControl, error">
167
+ <FormControl hasError>
168
+ <FormControl.Label>Label</FormControl.Label>
169
+ <NumberField name="name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease"/>
170
+ <FormControl.Error>Error</FormControl.Error>
171
+ </FormControl>
172
+ </Example.Item>
173
+ </Example>),
174
+ };
175
+ export const valueChanges = {
176
+ name: "test: keyboard",
177
+ render: () => (<Example>
178
+ <Example.Item title="keyboard">
179
+ <NumberField name="name" increaseAriaLabel="Increase" decreaseAriaLabel="Decrease"/>
180
+ </Example.Item>
181
+ </Example>),
182
+ play: async ({ canvas }) => {
183
+ const input = canvas.getByRole("textbox");
184
+ input.focus();
185
+ await userEvent.keyboard("-");
186
+ expect(input).toHaveValue("-");
187
+ await userEvent.keyboard("1");
188
+ expect(input).toHaveValue("-1");
189
+ await userEvent.keyboard("-");
190
+ expect(input).toHaveValue("-1");
191
+ await userEvent.keyboard("2");
192
+ expect(input).toHaveValue("-12");
193
+ await userEvent.keyboard("{ArrowUp}");
194
+ expect(input).toHaveValue("-11");
195
+ await userEvent.keyboard("{ArrowDown}");
196
+ expect(input).toHaveValue("-12");
197
+ await userEvent.keyboard(".");
198
+ expect(input).toHaveValue("-12.");
199
+ await userEvent.keyboard("3");
200
+ expect(input).toHaveValue("-12.3");
201
+ await userEvent.keyboard(".");
202
+ expect(input).toHaveValue("-12.3");
203
+ await userEvent.keyboard("-");
204
+ expect(input).toHaveValue("-12.3");
205
+ await userEvent.keyboard("{ArrowUp}");
206
+ await userEvent.keyboard("{ArrowUp}");
207
+ await userEvent.keyboard("{ArrowUp}");
208
+ await userEvent.keyboard("{ArrowUp}");
209
+ await userEvent.keyboard("{ArrowUp}");
210
+ expect(input).toHaveValue("-7.3");
211
+ await userEvent.keyboard("{ArrowDown}");
212
+ expect(input).toHaveValue("-8.3");
213
+ },
214
+ };
215
+ // Value change edge cases
@@ -1 +1 @@
1
- .root{display:inline-flex;margin:-1px 0;overflow-y:clip;padding:1px 0}.input,.root{vertical-align:top}.input{background:transparent;border:transparent;caret-color:transparent;color:transparent;font-size:16px;inset:0;outline:none;padding-left:100%;position:absolute}.item{cursor:text}.item--focused{border-color:var(--rs-color-border-primary);box-shadow:0 0 0 1px var(--rs-color-border-primary)}.item--focused:empty:before{animation:rs-pin-field-caret 1s ease-out infinite;background:var(--rs-color-foreground-neutral);border-radius:999px;content:"";height:var(--rs-font-size-body-2);left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:1px}@media (hover:hover){.root:hover .input{pointer-events:none}}@keyframes rs-pin-field-caret{0%,49.9%,to{opacity:1}50%,99.9%{opacity:0}}
1
+ .root{display:inline-flex}.input,.root{vertical-align:top}.input{background:transparent;border:transparent;caret-color:transparent;color:transparent;font-size:16px;inset:0;outline:none;padding-left:100%;position:absolute}.item{box-sizing:border-box;cursor:text}.item--focused{border-color:var(--rs-color-border-primary);box-shadow:0 0 0 1px var(--rs-color-border-primary)}.item--focused:empty:before{animation:rs-pin-field-caret 1s ease-out infinite;background:var(--rs-color-foreground-neutral);border-radius:999px;content:"";height:var(--rs-font-size-body-2);left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:1px}@media (hover:hover){.root:hover .input{pointer-events:none}}@keyframes rs-pin-field-caret{0%,49.9%,to{opacity:1}50%,99.9%{opacity:0}}
@@ -1,5 +1,5 @@
1
1
  import type * as G from "../../types/global";
2
- export type Size = "medium" | "large" | "xlarge";
2
+ export type Size = "small" | "medium" | "large" | "xlarge";
3
3
  type BaseProps = {
4
4
  name: string;
5
5
  valueLength?: number;
@@ -11,6 +11,7 @@ import * as keys from "../../constants/keys.js";
11
11
  import { regExpNumericChar, regExpAlphabeticChar, regExpAlphaNumericChar, } from "./PinField.constants.js";
12
12
  import s from "./PinField.module.css";
13
13
  const sizeMap = {
14
+ small: 7,
14
15
  medium: 9,
15
16
  large: 12,
16
17
  xlarge: 14,
@@ -40,6 +40,14 @@ export const variant = () => (<Example>
40
40
  </Example.Item>
41
41
  </Example>);
42
42
  export const size = () => (<Example>
43
+ <Example.Item title="size: small">
44
+ <PinField name="pin" size="small" inputAttributes={{ "aria-label": "Pin" }}/>
45
+ </Example.Item>
46
+
47
+ <Example.Item title="size: medium">
48
+ <PinField name="pin" size="medium" inputAttributes={{ "aria-label": "Pin" }}/>
49
+ </Example.Item>
50
+
43
51
  <Example.Item title="size: large">
44
52
  <PinField name="pin" size="large" inputAttributes={{ "aria-label": "Pin" }}/>
45
53
  </Example.Item>
@@ -1,12 +1,13 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { classNames } from "../../utilities/helpers.js";
3
+ import { classNames, responsiveClassNames, responsivePropDependency } from "../../utilities/helpers.js";
4
4
  import HiddenInput from "../_private/HiddenInput/index.js";
5
+ import Text from "../Text/index.js";
5
6
  import { useRadioGroup } from "../RadioGroup/index.js";
6
7
  import { useFormControl } from "../FormControl/index.js";
7
8
  import s from "./Radio.module.css";
8
9
  const Radio = (props) => {
9
- const { children, value, onChange, onFocus, onBlur, className, attributes, inputAttributes } = props;
10
+ const { children, value, onChange, onFocus, onBlur, size = "medium", className, attributes, inputAttributes, } = props;
10
11
  const formControl = useFormControl();
11
12
  const radioGroup = useRadioGroup();
12
13
  const hasError = formControl?.hasError || props.hasError || radioGroup?.hasError;
@@ -14,7 +15,7 @@ const Radio = (props) => {
14
15
  const checked = radioGroup ? radioGroup.value === value : props.checked;
15
16
  const defaultChecked = radioGroup ? undefined : props.defaultChecked;
16
17
  const name = radioGroup ? radioGroup.name : props.name;
17
- const rootClassName = classNames(s.root, className, hasError && s["--error"], disabled && s["--disabled"]);
18
+ const rootClassName = classNames(s.root, className, hasError && s["--error"], disabled && s["--disabled"], size && responsiveClassNames(s, "--size", size));
18
19
  const handleChange = (event) => {
19
20
  if (!name)
20
21
  return;
@@ -25,7 +26,13 @@ const Radio = (props) => {
25
26
  if (radioGroup?.onChange)
26
27
  radioGroup.onChange(changeArgs);
27
28
  };
28
- return (_jsxs("label", { ...attributes, className: rootClassName, children: [_jsxs("span", { className: s.field, children: [_jsx(HiddenInput, { className: s.input, type: "radio", checked: checked, defaultChecked: defaultChecked, name: name, disabled: disabled, value: value, onChange: handleChange, onFocus: onFocus, onBlur: onBlur, attributes: inputAttributes }), _jsx("div", { className: s.decorator })] }), children && _jsx("span", { className: s.text, children: children })] }));
29
+ return (_jsxs("label", { ...attributes, className: rootClassName, children: [_jsxs("span", { className: s.field, children: [_jsx(HiddenInput, { className: s.input, type: "radio", checked: checked, defaultChecked: defaultChecked, name: name, disabled: disabled, value: value, onChange: handleChange, onFocus: onFocus, onBlur: onBlur, attributes: inputAttributes }), _jsx("div", { className: s.decorator })] }), children && (_jsx(Text, { as: "span", variant: responsivePropDependency(size, (size) => {
30
+ if (size === "large")
31
+ return "body-2";
32
+ if (size === "small")
33
+ return "caption-1";
34
+ return "body-3";
35
+ }), children: children }))] }));
29
36
  };
30
37
  Radio.displayName = "Radio";
31
38
  export default Radio;
@@ -1 +1 @@
1
- .root{align-items:center;cursor:pointer;display:inline-flex;user-select:none;vertical-align:top;-webkit-tap-highlight-color:transparent}.root:hover .input:not(:checked)+.decorator{background:var(--rs-color-background-neutral-faded)}.field{position:relative}.decorator{--rs-radio-decorator-size:var(--rs-line-height-body-3);background:var(--rs-color-background-elevation-base);border:1px solid var(--rs-color-border-neutral);border-radius:50%;height:var(--rs-radio-decorator-size);transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:background-color,border-color;width:var(--rs-radio-decorator-size)}.decorator:after{background:var(--rs-color-on-background-primary);border-radius:50%;content:"";height:calc(var(--rs-radio-decorator-size) * .4);left:50%;opacity:0;position:absolute;top:50%;transform:translate(-50%,-50%) scale(0);transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:opacity,transform;width:calc(var(--rs-radio-decorator-size) * .4)}[data-rs-keyboard] .input:focus+.decorator{box-shadow:var(--rs-focus-shadow)}.input:checked+.decorator,.root.--error .input:checked+.decorator,.root.--error:hover .input:checked+.decorator{background:var(--rs-color-background-primary);border-color:var(--rs-color-background-primary);border-width:2px}.input:checked+.decorator:after,.root.--error .input:checked+.decorator:after,.root.--error:hover .input:checked+.decorator:after{opacity:1;transform:translate(-50%,-50%) scale(1)}.text{margin-inline-start:var(--rs-unit-x2)}.root.--error .decorator,.root.--error:hover .input+.decorator{border-color:var(--rs-color-border-critical)}.root.--disabled{color:var(--rs-color-foreground-disabled);cursor:not-allowed}.root.--disabled .decorator,.root.--disabled .input:checked+.decorator,.root.--disabled:hover .input+.decorator{background:var(--rs-color-background-disabled-faded);border-color:var(--rs-color-border-disabled)}.root.--disabled .input:checked+.decorator{border-color:transparent}.root.--disabled .input:checked+.decorator:after{background:var(--rs-color-border-disabled)}
1
+ @layer rs.radio.base;@layer rs.radio.error;@layer rs.radio.checked;@layer rs.radio.disabled;@layer rs.radio.base{.root{align-items:center;cursor:pointer;display:inline-flex;gap:var(--rs-radio-gap);user-select:none;vertical-align:top;-webkit-tap-highlight-color:transparent}.root:hover .decorator{background:var(--rs-color-background-neutral-faded)}.field{position:relative}.decorator{background:var(--rs-color-background-elevation-base);border:1px solid var(--rs-color-border-neutral);border-radius:50%;height:var(--rs-radio-line-height);transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:background-color,border-color;width:var(--rs-radio-line-height)}.decorator:after{background:var(--rs-color-on-background-primary);border-radius:50%;content:"";height:calc(var(--rs-radio-line-height) * .4);left:50%;opacity:0;position:absolute;top:50%;transform:translate(-50%,-50%) scale(0);transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:opacity,transform;width:calc(var(--rs-radio-line-height) * .4)}.--size-small{--rs-radio-line-height:var(--rs-line-height-caption-1);--rs-radio-gap:var(--rs-unit-x1)}.--size-medium{--rs-radio-line-height:var(--rs-line-height-body-3);--rs-radio-gap:var(--rs-unit-x2)}.--size-large{--rs-radio-line-height:var(--rs-line-height-body-2);--rs-radio-gap:var(--rs-unit-x2)}[data-rs-keyboard] .input:focus+.decorator{box-shadow:var(--rs-focus-shadow)}}@layer rs.radio.error{.root.--error .decorator{border-color:var(--rs-color-border-critical)}}@layer rs.radio.checked{.input:checked+.decorator{background:var(--rs-color-background-primary);border-color:var(--rs-color-background-primary)}.input:checked+.decorator:after{opacity:1;transform:translate(-50%,-50%) scale(1)}}@layer rs.radio.disabled{.root.--disabled{color:var(--rs-color-foreground-disabled);cursor:not-allowed}.root.--disabled .decorator{background:var(--rs-color-background-disabled-faded);border-color:var(--rs-color-border-disabled)}.root.--disabled .decorator:after{background-color:var(--rs-color-foreground-disabled)}.root.--disabled .input:checked+.decorator{background:var(--rs-color-background-disabled);border-color:transparent}}@media (--rs-viewport-m ){.--size-small--m{--rs-radio-line-height:var(--rs-line-height-caption-1);--rs-radio-gap:var(--rs-unit-x1)}.--size-medium--m{--rs-radio-line-height:var(--rs-line-height-body-3);--rs-radio-gap:var(--rs-unit-x2)}.--size-large--m{--rs-radio-line-height:var(--rs-line-height-body-2);--rs-radio-gap:var(--rs-unit-x2)}}@media (--rs-viewport-l ){.--size-small--l{--rs-radio-line-height:var(--rs-line-height-caption-1);--rs-radio-gap:var(--rs-unit-x1)}.--size-medium--l{--rs-radio-line-height:var(--rs-line-height-body-3);--rs-radio-gap:var(--rs-unit-x2)}.--size-large--l{--rs-radio-line-height:var(--rs-line-height-body-2);--rs-radio-gap:var(--rs-unit-x2)}}@media (--rs-viewport-xl ){.--size-small--xl{--rs-radio-line-height:var(--rs-line-height-caption-1);--rs-radio-gap:var(--rs-unit-x1)}.--size-medium--xl{--rs-radio-line-height:var(--rs-line-height-body-3);--rs-radio-gap:var(--rs-unit-x2)}.--size-large--xl{--rs-radio-line-height:var(--rs-line-height-body-2);--rs-radio-gap:var(--rs-unit-x2)}}
@@ -6,6 +6,7 @@ type BaseProps = {
6
6
  disabled?: boolean;
7
7
  hasError?: boolean;
8
8
  value: string;
9
+ size?: G.Responsive<"small" | "medium" | "large">;
9
10
  onChange?: G.ChangeHandler<boolean>;
10
11
  onFocus?: (e: React.FocusEvent) => void;
11
12
  onBlur?: (e: React.FocusEvent) => void;
@@ -12,5 +12,6 @@ declare const _default: {
12
12
  };
13
13
  export default _default;
14
14
  export declare const selection: () => import("react").JSX.Element;
15
+ export declare const size: () => import("react").JSX.Element;
15
16
  export declare const error: () => import("react").JSX.Element;
16
17
  export declare const disabled: () => import("react").JSX.Element;
@@ -1,5 +1,6 @@
1
1
  import { Example } from "../../../utilities/storybook/index.js";
2
2
  import Radio from "../index.js";
3
+ import View from "../../View/index.js";
3
4
  export default {
4
5
  title: "Components/Radio",
5
6
  component: Radio,
@@ -28,6 +29,36 @@ export const selection = () => (<Example>
28
29
  </Radio>
29
30
  </Example.Item>
30
31
  </Example>);
32
+ export const size = () => (<Example>
33
+ <Example.Item title="size: small">
34
+ <View gap={4} direction="row">
35
+ <Radio name="animal" value="dog" size="small" defaultChecked>
36
+ Radio
37
+ </Radio>
38
+ </View>
39
+ </Example.Item>
40
+ <Example.Item title="size: medium">
41
+ <View gap={4} direction="row">
42
+ <Radio name="animal" value="dog" size="medium">
43
+ Radio
44
+ </Radio>
45
+ </View>
46
+ </Example.Item>
47
+ <Example.Item title="size: large">
48
+ <View gap={4} direction="row">
49
+ <Radio name="animal" value="dog" size="large">
50
+ Radio
51
+ </Radio>
52
+ </View>
53
+ </Example.Item>
54
+ <Example.Item title="size: responsive, s: small, m: large">
55
+ <View gap={4} direction="row">
56
+ <Radio name="animal" value="dog" size={{ s: "small", m: "large" }}>
57
+ Radio
58
+ </Radio>
59
+ </View>
60
+ </Example.Item>
61
+ </Example>);
31
62
  export const error = () => (<Example>
32
63
  <Example.Item title="error">
33
64
  <Radio name="error" value="dog" hasError>
@@ -1 +1 @@
1
- .root{background:var(--rs-color-background-elevation-base);border:1px solid var(--rs-color-border-neutral);display:flex;overflow:hidden;padding:calc(var(--rs-unit-x1) - 1px) 0;position:relative;z-index:0}.root:focus-within{border-color:var(--rs-color-border-primary);box-shadow:0 0 0 1px var(--rs-color-border-primary)}.input{align-items:center;-webkit-appearance:none;appearance:none;background:none;border:0;box-sizing:border-box;color:var(--rs-color-foreground-neutral);cursor:pointer;display:flex;flex-grow:1;font-family:var(--rs-font-family-body);font-weight:var(--rs-font-weight-regular);outline:none;padding-inline-end:calc(var(--rs-select-chevron-size) + var(--rs-select-gap) * 2 + var(--rs-unit-x1));padding-inline-start:var(--rs-select-gap);position:relative;width:100%;z-index:1}.input::-ms-expand{display:none}.slot{align-items:center;display:flex;flex-shrink:0;padding-inline-start:var(--rs-select-gap);position:relative;z-index:5}.input .slot{padding-inline-end:var(--rs-select-gap);padding-inline-start:0}.arrow{color:var(--rs-color-foreground-neutral-faded);display:flex;inset-block-start:50%;inset-inline-end:var(--rs-select-gap);pointer-events:none;position:absolute;transform:translateY(-50%);z-index:5}.--size-medium{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-medium .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-bottom:var(--rs-unit-x1);padding-top:var(--rs-unit-x1)}.--size-large{--rs-select-gap:var(--rs-unit-x3);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-large .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-bottom:var(--rs-unit-x2);padding-top:var(--rs-unit-x2)}.--size-xlarge{--rs-select-gap:var(--rs-unit-x4);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-xlarge .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-bottom:var(--rs-unit-x3);padding-top:var(--rs-unit-x3)}.root.--variant-faded{background:var(--rs-color-background-neutral-faded);border-color:transparent}.root.--variant-faded:focus-within{border-color:var(--rs-color-border-primary)}.root.--variant-headless{background:transparent;border-color:transparent}.root.--variant-headless.--status-error,.root.--variant-headless.--status-error:focus-within,.root.--variant-headless:focus-within{border-color:transparent;box-shadow:none}.root.--status-error{border-color:var(--rs-color-border-critical)}.root.--status-error:focus-within{border-color:var(--rs-color-border-primary)}.root.--placeholder .input{color:var(--rs-color-foreground-neutral-faded)}.root.--disabled{background:var(--rs-color-background-disabled-faded);border-color:var(--rs-color-border-disabled)}.root.--disabled .arrow,.root.--disabled .input{color:var(--rs-color-foreground-disabled);cursor:not-allowed}@media (--rs-viewport-s ) and (hover:none){.input{font-size:var(--rs-font-size-body-2)!important}}@media (--rs-viewport-m ){.--size-medium--m{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-medium--m .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-bottom:var(--rs-unit-x1);padding-top:var(--rs-unit-x1)}.--size-large--m{--rs-select-gap:var(--rs-unit-x3);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-large--m .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-bottom:var(--rs-unit-x2);padding-top:var(--rs-unit-x2)}.--size-xlarge--m{--rs-select-gap:var(--rs-unit-x4);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-xlarge--m .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-bottom:var(--rs-unit-x3);padding-top:var(--rs-unit-x3)}}@media (--rs-viewport-l ){.--size-medium--l{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-medium--l .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-bottom:var(--rs-unit-x1);padding-top:var(--rs-unit-x1)}.--size-large--l{--rs-select-gap:var(--rs-unit-x3);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-large--l .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-bottom:var(--rs-unit-x2);padding-top:var(--rs-unit-x2)}.--size-xlarge--l{--rs-select-gap:var(--rs-unit-x4);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-xlarge--l .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-bottom:var(--rs-unit-x3);padding-top:var(--rs-unit-x3)}}@media (--rs-viewport-xl ){.--size-medium--xl{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-medium--xl .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-bottom:var(--rs-unit-x1);padding-top:var(--rs-unit-x1)}.--size-large--xl{--rs-select-gap:var(--rs-unit-x3);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-large--xl .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-bottom:var(--rs-unit-x2);padding-top:var(--rs-unit-x2)}.--size-xlarge--xl{--rs-select-gap:var(--rs-unit-x4);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-xlarge--xl .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-bottom:var(--rs-unit-x3);padding-top:var(--rs-unit-x3)}}
1
+ .root{background:var(--rs-color-background-elevation-base);border:1px solid var(--rs-color-border-neutral);display:flex;overflow:hidden;padding:calc(var(--rs-unit-x1) - 1px) 0;position:relative;z-index:0}.root:focus-within{border-color:var(--rs-color-border-primary);box-shadow:0 0 0 1px var(--rs-color-border-primary)}.input{align-items:center;-webkit-appearance:none;appearance:none;background:none;border:0;box-sizing:border-box;color:var(--rs-color-foreground-neutral);cursor:pointer;display:flex;flex-grow:1;font-family:var(--rs-font-family-body);font-weight:var(--rs-font-weight-regular);outline:none;padding-inline-end:calc(var(--rs-select-chevron-size) + var(--rs-select-gap) * 2 + var(--rs-unit-x1));padding-inline-start:var(--rs-select-gap);position:relative;width:100%;z-index:1}.input::-ms-expand{display:none}.slot{align-items:center;display:flex;flex-shrink:0;padding-inline-start:var(--rs-select-gap);position:relative;z-index:5}.input .slot{padding-inline-end:var(--rs-select-gap);padding-inline-start:0}.arrow{color:var(--rs-color-foreground-neutral-faded);display:flex;inset-block-start:50%;inset-inline-end:var(--rs-select-gap);pointer-events:none;position:absolute;transform:translateY(-50%);z-index:5}.--size-small{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-small .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-block:0}.--size-medium{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-medium .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-block:var(--rs-unit-x1)}.--size-large{--rs-select-gap:var(--rs-unit-x3);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-large .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-block:var(--rs-unit-x2)}.--size-xlarge{--rs-select-gap:var(--rs-unit-x4);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-xlarge .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-block:var(--rs-unit-x3)}.root.--variant-faded{background:var(--rs-color-background-neutral-faded);border-color:transparent}.root.--variant-faded:focus-within{border-color:var(--rs-color-border-primary)}.root.--variant-headless{background:transparent;border-color:transparent}.root.--variant-headless.--status-error,.root.--variant-headless.--status-error:focus-within,.root.--variant-headless:focus-within{border-color:transparent;box-shadow:none}.root.--status-error{border-color:var(--rs-color-border-critical)}.root.--status-error:focus-within{border-color:var(--rs-color-border-primary)}.root.--placeholder .input{color:var(--rs-color-foreground-neutral-faded)}.root.--disabled{background:var(--rs-color-background-disabled-faded);border-color:var(--rs-color-border-disabled)}.root.--disabled .arrow,.root.--disabled .input{color:var(--rs-color-foreground-disabled);cursor:not-allowed}@media (--rs-viewport-s ) and (hover:none){.input{font-size:var(--rs-font-size-body-2)!important}}@media (--rs-viewport-m ){.--size-small--m{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-small--m .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-block:0}.--size-medium--m{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-medium--m .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-block:var(--rs-unit-x1)}.--size-large--m{--rs-select-gap:var(--rs-unit-x3);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-large--m .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-block:var(--rs-unit-x2)}.--size-xlarge--m{--rs-select-gap:var(--rs-unit-x4);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-xlarge--m .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-block:var(--rs-unit-x3)}}@media (--rs-viewport-l ){.--size-small--l{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-small--l .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-block:0}.--size-medium--l{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-medium--l .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-block:var(--rs-unit-x1)}.--size-large--l{--rs-select-gap:var(--rs-unit-x3);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-large--l .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-block:var(--rs-unit-x2)}.--size-xlarge--l{--rs-select-gap:var(--rs-unit-x4);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-xlarge--l .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-block:var(--rs-unit-x3)}}@media (--rs-viewport-xl ){.--size-small--xl{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-small--xl .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-block:0}.--size-medium--xl{--rs-select-gap:var(--rs-unit-x2);--rs-select-chevron-size:var(--rs-unit-x4);border-radius:var(--rs-radius-small)}.--size-medium--xl .input{font-size:var(--rs-font-size-body-3);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3);padding-block:var(--rs-unit-x1)}.--size-large--xl{--rs-select-gap:var(--rs-unit-x3);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-large--xl .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-block:var(--rs-unit-x2)}.--size-xlarge--xl{--rs-select-gap:var(--rs-unit-x4);--rs-select-chevron-size:var(--rs-unit-x5);border-radius:var(--rs-radius-medium)}.--size-xlarge--xl .input{font-size:var(--rs-font-size-body-2);letter-spacing:var(--rs-letter-spacing-body-2);line-height:var(--rs-line-height-body-2);padding-block:var(--rs-unit-x3)}}