reshaped 3.4.6 → 3.5.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +0 -13
  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 +14 -1
  8. package/dist/components/Autocomplete/tests/Autocomplete.stories.js +181 -64
  9. package/dist/components/Badge/Badge.types.d.ts +4 -2
  10. package/dist/components/Button/Button.types.d.ts +4 -2
  11. package/dist/components/Calendar/Calendar.module.css +1 -1
  12. package/dist/components/Checkbox/Checkbox.js +17 -4
  13. package/dist/components/Checkbox/Checkbox.module.css +1 -1
  14. package/dist/components/Checkbox/Checkbox.types.d.ts +1 -0
  15. package/dist/components/Checkbox/tests/Checkbox.stories.d.ts +1 -0
  16. package/dist/components/Checkbox/tests/Checkbox.stories.js +40 -0
  17. package/dist/components/Dismissible/Dismissible.js +1 -0
  18. package/dist/components/DropdownMenu/DropdownMenu.js +1 -1
  19. package/dist/components/FormControl/FormControlLabel.js +2 -7
  20. package/dist/components/MenuItem/MenuItem.types.d.ts +4 -2
  21. package/dist/components/NumberField/NumberField.d.ts +6 -0
  22. package/dist/components/NumberField/NumberField.js +11 -0
  23. package/dist/components/NumberField/NumberField.module.css +1 -0
  24. package/dist/components/NumberField/NumberField.types.d.ts +19 -0
  25. package/dist/components/NumberField/NumberField.types.js +1 -0
  26. package/dist/components/NumberField/NumberFieldControlled.d.ts +6 -0
  27. package/dist/components/NumberField/NumberFieldControlled.js +162 -0
  28. package/dist/components/NumberField/NumberFieldUncontrolled.d.ts +6 -0
  29. package/dist/components/NumberField/NumberFieldUncontrolled.js +16 -0
  30. package/dist/components/NumberField/index.d.ts +2 -0
  31. package/dist/components/NumberField/index.js +1 -0
  32. package/dist/components/NumberField/tests/NumberField.stories.d.ts +30 -0
  33. package/dist/components/NumberField/tests/NumberField.stories.js +240 -0
  34. package/dist/components/Overlay/Overlay.js +2 -2
  35. package/dist/components/PinField/PinField.module.css +1 -1
  36. package/dist/components/PinField/PinField.types.d.ts +1 -1
  37. package/dist/components/PinField/PinFieldControlled.js +1 -0
  38. package/dist/components/PinField/tests/PinField.stories.js +8 -0
  39. package/dist/components/Radio/Radio.js +11 -4
  40. package/dist/components/Radio/Radio.module.css +1 -1
  41. package/dist/components/Radio/Radio.types.d.ts +1 -0
  42. package/dist/components/Radio/tests/Radio.stories.d.ts +1 -0
  43. package/dist/components/Radio/tests/Radio.stories.js +31 -0
  44. package/dist/components/Select/Select.module.css +1 -1
  45. package/dist/components/Select/Select.types.d.ts +1 -1
  46. package/dist/components/Select/tests/Select.stories.js +42 -16
  47. package/dist/components/Switch/Switch.js +10 -4
  48. package/dist/components/Switch/Switch.module.css +1 -1
  49. package/dist/components/Switch/Switch.types.d.ts +1 -1
  50. package/dist/components/Switch/tests/Switch.stories.js +30 -15
  51. package/dist/components/TextField/TextField.js +1 -1
  52. package/dist/components/TextField/TextField.module.css +1 -1
  53. package/dist/components/TextField/TextField.types.d.ts +2 -2
  54. package/dist/components/TextField/index.d.ts +1 -1
  55. package/dist/components/TextField/tests/TextField.stories.js +4 -0
  56. package/dist/components/Toast/ToastContainer.js +2 -2
  57. package/dist/components/_private/Flyout/FlyoutControlled.js +2 -2
  58. package/dist/hooks/tests/useScrollLock.stories.d.ts +1 -0
  59. package/dist/hooks/tests/useScrollLock.stories.js +29 -0
  60. package/dist/icons/ChevronUp.d.ts +2 -0
  61. package/dist/icons/ChevronUp.js +5 -0
  62. package/dist/icons/Minus.d.ts +2 -0
  63. package/dist/icons/Minus.js +3 -0
  64. package/dist/icons/Plus.d.ts +2 -2
  65. package/dist/icons/Plus.js +2 -2
  66. package/dist/index.d.ts +2 -0
  67. package/dist/index.js +1 -0
  68. package/dist/utilities/a11y/TrapFocus.d.ts +3 -17
  69. package/dist/utilities/a11y/TrapFocus.js +54 -49
  70. package/dist/utilities/a11y/tests/TrapFocus.stories.js +20 -20
  71. package/dist/utilities/scroll/lock.js +15 -7
  72. package/dist/utilities/scroll/lockStandard.d.ts +1 -2
  73. package/dist/utilities/scroll/lockStandard.js +2 -9
  74. package/package.json +31 -31
  75. package/dist/components/Autocomplete/tests/Autocomplete.test.stories.d.ts +0 -27
  76. package/dist/components/Autocomplete/tests/Autocomplete.test.stories.js +0 -86
@@ -7,6 +7,7 @@ import { getActiveElement } from "../../utilities/a11y/index.js";
7
7
  import * as keys from "../../constants/keys.js";
8
8
  import useHotkeys from "../../hooks/useHotkeys.js";
9
9
  import useHandlerRef from "../../hooks/useHandlerRef.js";
10
+ import s from "./Autocomplete.module.css";
10
11
  const AutocompleteContext = React.createContext({});
11
12
  const Autocomplete = (props) => {
12
13
  const { children, onChange, onInput, onItemSelect, name, containerRef, instanceRef, onBackspace, active, onOpen, onClose, ...textFieldProps } = props;
@@ -22,6 +23,14 @@ const Autocomplete = (props) => {
22
23
  const onOpenRef = useHandlerRef(onOpen);
23
24
  const onCloseRef = useHandlerRef(onClose);
24
25
  const isDropdownActive = hasChildren && (active ?? internalActive);
26
+ const lockDropdown = () => {
27
+ // Prevent dropdown from re-opening when clicked on item with mouse
28
+ // and focus moves to the item and back to the input
29
+ lockedRef.current = true;
30
+ setTimeout(() => {
31
+ lockedRef.current = false;
32
+ }, 100);
33
+ };
25
34
  const handleOpen = React.useCallback(() => {
26
35
  if (lockedRef.current)
27
36
  return;
@@ -35,12 +44,7 @@ const Autocomplete = (props) => {
35
44
  const handleItemClick = (args) => {
36
45
  onChange?.({ value: args.value, name });
37
46
  onItemSelect?.(args);
38
- // Prevent dropdown from re-opening when clicked on item with mouse
39
- // and focus moves to the item and back to the input
40
- lockedRef.current = true;
41
- setTimeout(() => {
42
- lockedRef.current = false;
43
- }, 100);
47
+ lockDropdown();
44
48
  };
45
49
  const handleChange = (args) => {
46
50
  onChange?.(args);
@@ -50,6 +54,15 @@ const Autocomplete = (props) => {
50
54
  onInput?.({ value: e.currentTarget.value, name, event: e });
51
55
  textFieldProps.inputAttributes?.onInput?.(e);
52
56
  };
57
+ /**
58
+ * Make sure focus stays on the input even after user clicks on the content
59
+ * but outside the items
60
+ */
61
+ const handleContentClick = () => {
62
+ // Prevent the content from being selected
63
+ lockDropdown();
64
+ inputRef.current?.focus();
65
+ };
53
66
  useHotkeys({
54
67
  [keys.BACKSPACE]: () => {
55
68
  onBackspaceRef.current?.();
@@ -79,6 +92,7 @@ const Autocomplete = (props) => {
79
92
  onFocus: (e) => {
80
93
  attributes.onFocus?.();
81
94
  textFieldProps.onFocus?.(e);
95
+ // Only select the value when user clicks on the input
82
96
  if (!lockedRef.current)
83
97
  inputRef.current?.select();
84
98
  },
@@ -86,7 +100,7 @@ const Autocomplete = (props) => {
86
100
  onClick: attributes.onFocus,
87
101
  ref: inputRef,
88
102
  role: "combobox",
89
- } })) }), _jsx(DropdownMenu.Content, { children: children })] }) }));
103
+ } })) }), _jsx(DropdownMenu.Content, { attributes: { onClick: handleContentClick }, children: children })] }) }));
90
104
  };
91
105
  const AutocompleteItem = (props) => {
92
106
  const { value, data, onClick, ...menuItemProps } = props;
@@ -95,7 +109,10 @@ const AutocompleteItem = (props) => {
95
109
  onClick?.(e);
96
110
  onItemClick({ value, data });
97
111
  };
98
- return (_jsx(DropdownMenu.Item, { ...menuItemProps, attributes: { ...menuItemProps.attributes, role: "option" }, onClick: handleClick }));
112
+ return (_jsx(DropdownMenu.Item, { ...menuItemProps, className: [menuItemProps.disabled && s["item--disabled"], menuItemProps.className], attributes: {
113
+ ...menuItemProps.attributes,
114
+ role: "option",
115
+ }, onClick: handleClick }));
99
116
  };
100
117
  Autocomplete.Item = AutocompleteItem;
101
118
  Autocomplete.displayName = "Autocomplete";
@@ -0,0 +1 @@
1
+ .item--disabled{pointer-events:none}
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { StoryObj } from "@storybook/react";
3
+ import { Mock } from "@storybook/test";
3
4
  declare const _default: {
4
5
  title: string;
5
6
  component: {
@@ -17,7 +18,19 @@ declare const _default: {
17
18
  };
18
19
  };
19
20
  export default _default;
20
- export declare const base: StoryObj;
21
+ export declare const active: StoryObj<{
22
+ handleOpen: Mock;
23
+ handleClose: Mock;
24
+ }>;
25
+ export declare const base: StoryObj<{
26
+ handleInput: Mock;
27
+ handleItemSelect: Mock;
28
+ handleBackspace: Mock;
29
+ }>;
30
+ export declare const itemData: StoryObj<{
31
+ handleItemSelect: Mock;
32
+ }>;
33
+ export declare const itemDisabled: StoryObj;
21
34
  export declare const multiselect: {
22
35
  name: string;
23
36
  render: () => React.JSX.Element;
@@ -1,16 +1,12 @@
1
1
  import React from "react";
2
+ import { fn, expect, within, waitFor, userEvent, fireEvent } from "@storybook/test";
2
3
  import { Example } from "../../../utilities/storybook/index.js";
3
4
  import Autocomplete from "../index.js";
4
5
  import View from "../../View/index.js";
5
6
  import Badge from "../../Badge/index.js";
6
7
  import useToggle from "../../../hooks/useToggle.js";
7
- import Modal from "../../Modal/index.js";
8
- import TextField from "../../TextField/index.js";
9
- import Text from "../../Text/index.js";
10
- import Button from "../../Button/index.js";
11
- import PlusIcon from "../../../icons/Plus.js";
12
- import Dismissible from "../../Dismissible/index.js";
13
8
  import FormControl from "../../FormControl/index.js";
9
+ import { sleep } from "../../../utilities/helpers.js";
14
10
  export default {
15
11
  title: "Components/Autocomplete",
16
12
  component: Autocomplete,
@@ -20,16 +16,68 @@ export default {
20
16
  },
21
17
  },
22
18
  };
19
+ export const active = {
20
+ name: "active, onOpen, onClose",
21
+ args: {
22
+ handleClose: fn(),
23
+ handleOpen: fn(),
24
+ },
25
+ render: (args) => {
26
+ const toggle = useToggle(true);
27
+ return (<Example>
28
+ <Example.Item title="active, onOpen, onClose">
29
+ <FormControl>
30
+ <FormControl.Label>Label</FormControl.Label>
31
+ <Autocomplete name="fruit" placeholder="Pick your food" active={toggle.active} onOpen={() => {
32
+ args.handleOpen();
33
+ toggle.activate();
34
+ }} onClose={() => {
35
+ args.handleClose();
36
+ toggle.deactivate();
37
+ }}>
38
+ {["Pizza", "Pie", "Ice-cream"].map((v, i) => {
39
+ return (<Autocomplete.Item key={v} value={v}>
40
+ {v}
41
+ </Autocomplete.Item>);
42
+ })}
43
+ </Autocomplete>
44
+ </FormControl>
45
+ </Example.Item>
46
+ </Example>);
47
+ },
48
+ play: async ({ canvasElement, args }) => {
49
+ const canvas = within(canvasElement.ownerDocument.body);
50
+ const input = canvas.getByRole("combobox");
51
+ const list = await canvas.findByRole("listbox");
52
+ expect(list).toBeInTheDocument();
53
+ expect(args.handleOpen).not.toHaveBeenCalled();
54
+ await userEvent.click(document.body);
55
+ await waitFor(() => {
56
+ expect(args.handleClose).toHaveBeenCalledTimes(1);
57
+ expect(args.handleClose).toHaveBeenLastCalledWith();
58
+ });
59
+ await userEvent.click(input);
60
+ await waitFor(() => {
61
+ expect(args.handleOpen).toHaveBeenCalledTimes(1);
62
+ expect(args.handleOpen).toHaveBeenLastCalledWith();
63
+ });
64
+ },
65
+ };
23
66
  export const base = {
24
- name: "base",
25
- render: () => {
67
+ name: "onInput, onItemSelect, onBackspace",
68
+ args: {
69
+ handleInput: fn(),
70
+ handleBackspace: fn(),
71
+ handleItemSelect: fn(),
72
+ },
73
+ render: (args) => {
26
74
  const [value, setValue] = React.useState("");
27
75
  return (<Example>
28
76
  <Example.Item title="Base">
29
77
  <FormControl>
30
78
  <FormControl.Label>Food</FormControl.Label>
31
- <Autocomplete name="fruit" placeholder="Pick your food" value={value} onChange={(args) => setValue(args.value)}>
32
- {["Pizza", "Pie", "Ice-cream"].map((v) => {
79
+ <Autocomplete name="fruit" placeholder="Pick your food" value={value} onChange={(args) => setValue(args.value)} onBackspace={args.handleBackspace} onItemSelect={args.handleItemSelect}>
80
+ {["Pizza", "Pie", "Ice-cream"].map((v, i) => {
33
81
  return (<Autocomplete.Item key={v} value={v}>
34
82
  {v}
35
83
  </Autocomplete.Item>);
@@ -39,54 +87,148 @@ export const base = {
39
87
  </Example.Item>
40
88
  </Example>);
41
89
  },
90
+ play: async ({ canvasElement, args }) => {
91
+ const canvas = within(canvasElement.ownerDocument.body);
92
+ const input = canvas.getByRole("combobox");
93
+ // Reset the focus
94
+ document.body.focus();
95
+ // Test keyboard selection after focusing the input
96
+ input.focus();
97
+ let options = [];
98
+ await waitFor(() => {
99
+ options = canvas.getAllByRole("option");
100
+ });
101
+ expect(options).toHaveLength(3);
102
+ await waitFor(() => {
103
+ expect(options[0]).toHaveAttribute("data-rs-focus");
104
+ });
105
+ expect(options[1]).not.toHaveAttribute("data-rs-focus");
106
+ await userEvent.keyboard("{ArrowDown/}");
107
+ await userEvent.keyboard("{Enter/}");
108
+ expect(input).toHaveValue("Pie");
109
+ expect(args.handleItemSelect).toHaveBeenCalledTimes(1);
110
+ expect(args.handleItemSelect).toHaveBeenCalledWith({
111
+ value: "Pie",
112
+ data: undefined,
113
+ });
114
+ // Give browser time to focus on the input
115
+ await sleep(100);
116
+ // Test click selection after opening with down arrow
117
+ await userEvent.keyboard("{ArrowDown/}");
118
+ await waitFor(() => {
119
+ options = canvas.getAllByRole("option");
120
+ });
121
+ await userEvent.click(options[0]);
122
+ expect(input).toHaveValue("Pizza");
123
+ expect(args.handleItemSelect).toHaveBeenCalledTimes(2);
124
+ expect(args.handleItemSelect).toHaveBeenCalledWith({
125
+ value: "Pizza",
126
+ });
127
+ input.focus();
128
+ await userEvent.keyboard("{Backspace/}");
129
+ await waitFor(() => {
130
+ expect(args.handleBackspace).toHaveBeenCalledTimes(1);
131
+ expect(args.handleBackspace).toHaveBeenCalledWith();
132
+ });
133
+ },
134
+ };
135
+ export const itemData = {
136
+ name: "item data",
137
+ args: {
138
+ handleItemSelect: fn(),
139
+ },
140
+ render: (args) => {
141
+ return (<Example>
142
+ <Example.Item title="item data">
143
+ <FormControl>
144
+ <FormControl.Label>Label</FormControl.Label>
145
+ <Autocomplete name="fruit" placeholder="Pick your food" onItemSelect={args.handleItemSelect} active>
146
+ {["Pizza", "Pie", "Ice-cream"].map((v, i) => {
147
+ return (<Autocomplete.Item key={v} value={v} data={i === 1 ? { foo: true } : undefined}>
148
+ {v}
149
+ </Autocomplete.Item>);
150
+ })}
151
+ </Autocomplete>
152
+ </FormControl>
153
+ </Example.Item>
154
+ </Example>);
155
+ },
156
+ play: async ({ canvasElement, args }) => {
157
+ const canvas = within(canvasElement.ownerDocument.body);
158
+ const options = await canvas.findAllByRole("option");
159
+ await userEvent.click(options[0]);
160
+ expect(args.handleItemSelect).toHaveBeenLastCalledWith({ value: "Pizza" });
161
+ await userEvent.click(options[1]);
162
+ expect(args.handleItemSelect).toHaveBeenLastCalledWith({ value: "Pie", data: { foo: true } });
163
+ },
164
+ };
165
+ export const itemDisabled = {
166
+ name: "item disabled",
167
+ render: () => {
168
+ return (<Example>
169
+ <Example.Item title="item disabled">
170
+ <FormControl>
171
+ <FormControl.Label>Label</FormControl.Label>
172
+ <Autocomplete name="fruit" placeholder="Pick your food" active>
173
+ {["Pizza", "Pie", "Ice-cream"].map((v, i) => {
174
+ return (<Autocomplete.Item key={v} value={v} disabled={i === 1}>
175
+ {v}
176
+ </Autocomplete.Item>);
177
+ })}
178
+ </Autocomplete>
179
+ </FormControl>
180
+ </Example.Item>
181
+ </Example>);
182
+ },
183
+ play: async ({ canvasElement }) => {
184
+ const canvas = within(canvasElement.ownerDocument.body);
185
+ const input = canvas.getByRole("combobox");
186
+ const options = await canvas.findAllByRole("option");
187
+ await fireEvent.click(options[1]);
188
+ expect(options[1]).toBeDisabled();
189
+ // Check that focus stays on input when clicking on disabled elements
190
+ expect(document.activeElement).toBe(input);
191
+ },
42
192
  };
43
193
  export const multiselect = {
44
- name: "multiselect",
194
+ name: "test: multiselect",
45
195
  render: () => {
196
+ const options = [
197
+ "Pizza",
198
+ "Pie",
199
+ "Ice-cream",
200
+ "Fries",
201
+ "Salad",
202
+ "Option 4",
203
+ "Option 5",
204
+ "Option 6",
205
+ ];
46
206
  const inputRef = React.useRef(null);
47
- const [active, setActive] = React.useState(false);
48
- const [values, setValues] = React.useState(["foo", "bar"]);
207
+ const [values, setValues] = React.useState([
208
+ "Option 4",
209
+ "Option 5",
210
+ "Option 6",
211
+ "Pizza",
212
+ "Ice-cream",
213
+ ]);
49
214
  const [query, setQuery] = React.useState("");
50
- const [customValueQuery, setCustomValueQuery] = React.useState("");
51
- const customValueToggle = useToggle();
52
215
  const handleDismiss = (dismissedValue) => {
53
216
  const nextValues = values.filter((value) => value !== dismissedValue);
54
217
  setValues(nextValues);
55
218
  inputRef.current?.focus();
56
219
  };
57
- const handleAddCustomValue = () => {
58
- if (customValueQuery.length) {
59
- setValues((prev) => [...prev, customValueQuery]);
60
- }
61
- customValueToggle.deactivate();
62
- setCustomValueQuery("");
63
- };
64
220
  const valuesNode = !!values.length && (<View direction="row" gap={1}>
65
- {values.map((value) => (<Badge dismissAriaLabel="Dismiss value" onDismiss={() => handleDismiss(value)} key={value} size="small">
221
+ {values.map((value) => (<Badge dismissAriaLabel="Dismiss value" onDismiss={() => handleDismiss(value)} key={value}>
66
222
  {value}
67
223
  </Badge>))}
68
224
  </View>);
69
225
  return (<FormControl>
70
226
  <FormControl.Label>Food</FormControl.Label>
71
- <Autocomplete name="fruit" value={query} placeholder="Pick your food" startSlot={valuesNode} inputAttributes={{ ref: inputRef }} onBackspace={() => {
72
- if (!query.length)
73
- handleDismiss(values[values.length - 1]);
74
- }} onOpen={() => {
75
- setActive(true);
76
- }} onClose={(args) => {
77
- if (args.reason === "item-selection")
78
- return;
79
- setActive(false);
80
- }} active={active} multiline onChange={(args) => setQuery(args.value)} onItemSelect={(args) => {
81
- setCustomValueQuery(query);
227
+ <Autocomplete name="fruit" value={query} placeholder="Pick your food" startSlot={valuesNode} multiline inputAttributes={{ ref: inputRef }} onChange={(args) => setQuery(args.value)} onItemSelect={(args) => {
82
228
  setQuery("");
83
- if (args.value === "_custom") {
84
- customValueToggle.activate();
85
- return;
86
- }
87
229
  setValues((prev) => [...prev, args.value]);
88
230
  }}>
89
- {["Pizza", "Pie", "Ice-cream"].map((v) => {
231
+ {options.map((v) => {
90
232
  if (!v.toLowerCase().includes(query.toLowerCase()))
91
233
  return;
92
234
  if (values.includes(v))
@@ -95,32 +237,7 @@ export const multiselect = {
95
237
  {v}
96
238
  </Autocomplete.Item>);
97
239
  })}
98
- {!!query.length && (<Autocomplete.Item value="_custom" icon={PlusIcon}>
99
- Add a custom value
100
- </Autocomplete.Item>)}
101
240
  </Autocomplete>
102
- <Modal onClose={customValueToggle.deactivate} active={customValueToggle.active}>
103
- <View gap={4}>
104
- <Dismissible onClose={customValueToggle.deactivate} closeAriaLabel="Close modal">
105
- <Modal.Title>
106
- <Text variant="body-3" weight="medium">
107
- Add a custom value
108
- </Text>
109
- </Modal.Title>
110
- </Dismissible>
111
- <View direction="row" gap={3} as="form" attributes={{
112
- onSubmit: (e) => {
113
- e.preventDefault();
114
- handleAddCustomValue();
115
- },
116
- }}>
117
- <View.Item grow>
118
- <TextField name="custom" onChange={(args) => setCustomValueQuery(args.value)} value={customValueQuery}/>
119
- </View.Item>
120
- <Button type="submit">Add</Button>
121
- </View>
122
- </View>
123
- </Modal>
124
241
  </FormControl>);
125
242
  },
126
243
  };
@@ -1,5 +1,5 @@
1
1
  import type React from "react";
2
- import type { ActionableProps } from "../Actionable";
2
+ import type { ActionableProps, ActionableRef } from "../Actionable";
3
3
  import type { IconProps } from "../Icon";
4
4
  import type * as G from "../../types/global";
5
5
  type BaseProps = {
@@ -36,7 +36,9 @@ export type ContainerProps = {
36
36
  className?: G.ClassName;
37
37
  attributes?: G.Attributes<"div">;
38
38
  };
39
- export type Export = React.ForwardRefExoticComponent<Props> & {
39
+ export type Export = React.ForwardRefExoticComponent<Props & {
40
+ ref?: ActionableRef;
41
+ }> & {
40
42
  Container: React.ComponentType<ContainerProps>;
41
43
  };
42
44
  export {};
@@ -1,6 +1,6 @@
1
1
  import type React from "react";
2
2
  import type { IconProps } from "../Icon";
3
- import type { ActionableProps } from "../Actionable";
3
+ import type { ActionableProps, ActionableRef } from "../Actionable";
4
4
  import type { AlignerProps as BaseAlignerProps } from "../_private/Aligner";
5
5
  import type * as G from "../../types/global";
6
6
  export type Size = "xlarge" | "large" | "medium" | "small";
@@ -28,7 +28,9 @@ export type AlignerProps = BaseAlignerProps & {
28
28
  */
29
29
  position?: BaseAlignerProps["side"];
30
30
  };
31
- export type Export = React.ForwardRefExoticComponent<Props> & {
31
+ export type Export = React.ForwardRefExoticComponent<Props & {
32
+ ref?: ActionableRef;
33
+ }> & {
32
34
  Aligner: React.ComponentType<AlignerProps>;
33
35
  Group: React.ComponentType<GroupProps>;
34
36
  };
@@ -1 +1 @@
1
- .selection{table-layout:fixed;width:100%}.weekday{color:var(--rs-color-foreground-neutral-faded);font-weight:var(--rs-font-weight-regular);padding-bottom:var(--rs-unit-x2)}[dir=rtl] .control{transform:scaleX(-1)}.cell{isolation:isolate;padding:2px}.cell:hover .cell-button{background-color:rgba(var(--rs-color-rgb-background-neutral),32%)}.cell.--active .cell-button{background-color:var(--rs-color-background-primary);color:var(--rs-color-on-background-primary)}.cell.--selection-range .cell-button{background-color:rgba(var(--rs-color-rgb-background-neutral),32%)}.cell.--selection-range:not(:last-child) .cell-button,.cell.--selection-start:not(:last-child) .cell-button{border-end-end-radius:0;border-start-end-radius:0}.cell.--selection-end:not(:first-child) .cell-button,.cell.--selection-range+:hover .cell-button,.cell.--selection-range:not(:first-child) .cell-button,.cell.--selection-start+:hover .cell-button{border-end-start-radius:0;border-start-start-radius:0}.cell.--selection-range+.--selection-end .cell-button:before,.cell.--selection-range+.--selection-range .cell-button:before,.cell.--selection-range+:hover .cell-button:before,.cell.--selection-start+.--selection-range .cell-button:before,.cell.--selection-start+:hover .cell-button:before{background-color:rgba(var(--rs-color-rgb-background-neutral),32%)}.cell-button{border-radius:var(--rs-radius-small);padding:var(--rs-unit-x2);position:relative;text-align:center;transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:color,background-color,border-radius}.cell-button:before{content:"";inset-block:0;inset-inline-start:-4px;position:absolute;transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:background-color;width:4px}.cell-button[disabled]{background-color:transparent!important;color:var(--rs-color-foreground-disabled)!important}
1
+ @layer rs.calendar.base;@layer rs.calendar.range;@layer rs.calendar.disabled;@layer rs.calendar.base{.selection{table-layout:fixed;width:100%}.weekday{color:var(--rs-color-foreground-neutral-faded);font-weight:var(--rs-font-weight-regular);padding-bottom:var(--rs-unit-x2)}[dir=rtl] .control{transform:scaleX(-1)}.cell{isolation:isolate;padding:2px}.cell:hover .cell-button{background-color:rgba(var(--rs-color-rgb-background-neutral),32%)}.cell.--active .cell-button{background-color:var(--rs-color-background-primary);color:var(--rs-color-on-background-primary)}.cell-button{border-radius:var(--rs-radius-small);padding:var(--rs-unit-x2);position:relative;text-align:center;transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:color,background-color,border-radius}.cell-button:before{content:"";inset-block:0;inset-inline-start:-4px;position:absolute;transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:background-color;width:4px}}@layer rs.calendar.selection{.cell.--selection-range .cell-button{background-color:rgba(var(--rs-color-rgb-background-neutral),32%);color:var(--rs-color-foreground-neutral)}.cell.--selection-range:not(:last-child) .cell-button,.cell.--selection-start:not(:last-child) .cell-button{border-end-end-radius:0;border-start-end-radius:0}.cell.--selection-end:not(:first-child) .cell-button,.cell.--selection-range+:hover .cell-button,.cell.--selection-range:not(:first-child) .cell-button,.cell.--selection-start+:hover .cell-button{border-end-start-radius:0;border-start-start-radius:0}.cell.--selection-range+.--selection-end .cell-button:before,.cell.--selection-range+.--selection-range .cell-button:before,.cell.--selection-range+:hover .cell-button:before,.cell.--selection-start+.--selection-range .cell-button:before,.cell.--selection-start+:hover .cell-button:before{background-color:rgba(var(--rs-color-rgb-background-neutral),32%)}}@layer rs.calendar.disabled{.cell-button[disabled]{background-color:transparent;color:var(--rs-color-foreground-disabled)}}
@@ -1,16 +1,17 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React from "react";
4
- import { classNames } from "../../utilities/helpers.js";
4
+ import { classNames, responsiveClassNames, responsivePropDependency } from "../../utilities/helpers.js";
5
5
  import useIsomorphicLayoutEffect from "../../hooks/useIsomorphicLayoutEffect.js";
6
6
  import HiddenInput from "../_private/HiddenInput/index.js";
7
7
  import { useFormControl } from "../FormControl/index.js";
8
8
  import { useCheckboxGroup } from "../CheckboxGroup/index.js";
9
9
  import Icon from "../Icon/index.js";
10
+ import Text from "../Text/index.js";
10
11
  import IconCheckmark from "../../icons/Checkmark.js";
11
12
  import s from "./Checkbox.module.css";
12
13
  const Checkbox = (props) => {
13
- const { children, value, onChange, onFocus, onBlur, indeterminate, className, attributes, inputAttributes, } = props;
14
+ const { children, value, onChange, onFocus, onBlur, indeterminate, size = "medium", className, attributes, inputAttributes, } = props;
14
15
  const checkboxGroup = useCheckboxGroup();
15
16
  const formControl = useFormControl();
16
17
  const hasError = formControl?.hasError || props.hasError || checkboxGroup?.hasError;
@@ -19,7 +20,7 @@ const Checkbox = (props) => {
19
20
  const defaultChecked = checkboxGroup ? undefined : props.defaultChecked;
20
21
  const name = checkboxGroup ? checkboxGroup.name : props.name;
21
22
  const inputRef = React.useRef(null);
22
- const rootClassName = classNames(s.root, className, hasError && s["--error"], disabled && s["--disabled"]);
23
+ const rootClassName = classNames(s.root, className, size && hasError && s["--error"], disabled && s["--disabled"], size && responsiveClassNames(s, "--size", size));
23
24
  const handleChange = (event) => {
24
25
  if (!name)
25
26
  return;
@@ -36,7 +37,19 @@ const Checkbox = (props) => {
36
37
  return (_jsxs("label", { ...attributes, className: rootClassName, children: [_jsxs("span", { className: s.field, children: [_jsx(HiddenInput, { className: s.input, type: "checkbox", checked: checked, defaultChecked: defaultChecked, name: name, disabled: disabled, value: value, onChange: handleChange, onFocus: onFocus, onBlur: onBlur, attributes: {
37
38
  ...inputAttributes,
38
39
  ref: inputRef,
39
- } }), _jsx("div", { className: s.decorator, children: _jsx(Icon, { svg: IconCheckmark, className: s.icon }) })] }), children && _jsx("span", { className: s.text, children: children })] }));
40
+ } }), _jsx("div", { className: s.decorator, children: _jsx(Icon, { svg: IconCheckmark, className: s.icon, size: responsivePropDependency(size, (size) => {
41
+ if (size === "large")
42
+ return 5;
43
+ if (size === "small")
44
+ return 3;
45
+ return 4;
46
+ }) }) })] }), children && (_jsx(Text, { as: "span", variant: responsivePropDependency(size, (size) => {
47
+ if (size === "large")
48
+ return "body-2";
49
+ if (size === "small")
50
+ return "caption-1";
51
+ return "body-3";
52
+ }), children: children }))] }));
40
53
  };
41
54
  Checkbox.displayName = "Checkbox";
42
55
  export default Checkbox;
@@ -1 +1 @@
1
- .root{align-items:center;cursor:pointer;display:inline-flex;gap:var(--rs-unit-x2);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:var(--rs-radius-small);color:var(--rs-color-on-background-primary);height:var(--rs-line-height-body-3);transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:background-color,border-color;width:var(--rs-line-height-body-3)}.decorator:before{background:var(--rs-color-on-background-primary);border-radius:999px;content:"";height:1.5px;width:calc(var(--rs-line-height-body-3) * .4)}.decorator:before,.icon{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}[data-rs-keyboard] .input:focus+.decorator{box-shadow:var(--rs-focus-shadow)}.input:indeterminate+.decorator,.root.--error .input:indeterminate+.decorator,.root.--error:hover .input:indeterminate+.decorator,.root:hover .input:indeterminate+.decorator{background:var(--rs-color-background-primary);border-color:var(--rs-color-background-primary)}.input:indeterminate+.decorator:before,.root.--error .input:indeterminate+.decorator:before,.root.--error:hover .input:indeterminate+.decorator:before,.root:hover .input:indeterminate+.decorator:before{opacity:1;transform:translate(-50%,-50%) scale(1)}.input:checked+.decorator,.root.--error .input:checked+.decorator,.root.--error:hover .input:checked+.decorator,.root:hover .input:checked+.decorator{background:var(--rs-color-background-primary);border-color:var(--rs-color-background-primary)}.input:checked+.decorator .icon,.root.--error .input:checked+.decorator .icon,.root.--error:hover .input:checked+.decorator .icon,.root:hover .input:checked+.decorator .icon{opacity:1;transform:translate(-50%,-50%) scale(1)}.root.--error .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 .decorator,.root.--disabled:hover .input:checked+.decorator{background:var(--rs-color-background-disabled-faded);border-color:var(--rs-color-border-disabled);color:var(--rs-color-foreground-disabled)}.root.--disabled .input:checked+.decorator,.root.--disabled .input:indeterminate+.decorator,.root.--disabled:hover .input:checked+.decorator,.root.--disabled:hover .input:indeterminate+.decorator{background:var(--rs-color-background-disabled);border-color:transparent}.root.--disabled .input:indeterminate+.decorator:before{background:var(--rs-color-foreground-disabled)}.root.--disabled .input:checked+.decorator:after{background:var(--rs-color-border-disabled)}
1
+ @layer rs.checkbox.base;@layer rs.checkbox.error;@layer rs.checkbox.checked;@layer rs.checkbox.disabled;@layer rs.checkbox.base{.root{align-items:center;cursor:pointer;display:inline-flex;gap:var(--rs-checkbox-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:var(--rs-radius-small);color:var(--rs-color-on-background-primary);height:var(--rs-checkbox-line-height);transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:background-color,border-color;width:var(--rs-checkbox-line-height)}.decorator:before{background:var(--rs-color-on-background-primary);border-radius:999px;content:"";height:1.5px;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-checkbox-line-height) * .5)}.--size-small{--rs-checkbox-line-height:var(--rs-line-height-caption-1);--rs-checkbox-gap:var(--rs-unit-x1)}.--size-medium{--rs-checkbox-line-height:var(--rs-line-height-body-3);--rs-checkbox-gap:var(--rs-unit-x2)}.--size-large{--rs-checkbox-line-height:var(--rs-line-height-body-2);--rs-checkbox-gap:var(--rs-unit-x2)}.icon{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}[data-rs-keyboard] .input:focus-visible+.decorator{box-shadow:var(--rs-focus-shadow)}}@layer rs.checkbox.error{.root.--error .decorator{border-color:var(--rs-color-border-critical)}}@layer rs.checkbox.checked{.input:checked+.decorator,.input:indeterminate+.decorator{background:var(--rs-color-background-primary);border-color:var(--rs-color-background-primary)}.input:checked+.decorator .icon,.input:indeterminate+.decorator:before{opacity:1;transform:translate(-50%,-50%) scale(1)}}@layer rs.checkbox.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);color:var(--rs-color-foreground-disabled)}.root.--disabled .input:checked+.decorator,.root.--disabled .input:indeterminate+.decorator{background:var(--rs-color-background-disabled);border-color:transparent}.root.--disabled .input:checked+.decorator:before,.root.--disabled .input:indeterminate+.decorator:before{background-color:var(--rs-color-foreground-disabled)}}@media (--rs-viewport-m ){.--size-small--m{--rs-checkbox-line-height:var(--rs-line-height-caption-1);--rs-checkbox-gap:var(--rs-unit-x1)}.--size-medium--m{--rs-checkbox-line-height:var(--rs-line-height-body-3);--rs-checkbox-gap:var(--rs-unit-x2)}.--size-large--m{--rs-checkbox-line-height:var(--rs-line-height-body-2);--rs-checkbox-gap:var(--rs-unit-x2)}}@media (--rs-viewport-l ){.--size-small--l{--rs-checkbox-line-height:var(--rs-line-height-caption-1);--rs-checkbox-gap:var(--rs-unit-x1)}.--size-medium--l{--rs-checkbox-line-height:var(--rs-line-height-body-3);--rs-checkbox-gap:var(--rs-unit-x2)}.--size-large--l{--rs-checkbox-line-height:var(--rs-line-height-body-2);--rs-checkbox-gap:var(--rs-unit-x2)}}@media (--rs-viewport-xl ){.--size-small--xl{--rs-checkbox-line-height:var(--rs-line-height-caption-1);--rs-checkbox-gap:var(--rs-unit-x1)}.--size-medium--xl{--rs-checkbox-line-height:var(--rs-line-height-body-3);--rs-checkbox-gap:var(--rs-unit-x2)}.--size-large--xl{--rs-checkbox-line-height:var(--rs-line-height-body-2);--rs-checkbox-gap:var(--rs-unit-x2)}}
@@ -7,6 +7,7 @@ type BaseProps = {
7
7
  hasError?: boolean;
8
8
  value?: string;
9
9
  indeterminate?: boolean;
10
+ size?: G.Responsive<"small" | "medium" | "large">;
10
11
  onChange?: G.ChangeHandler<boolean>;
11
12
  onFocus?: (e: React.FocusEvent) => void;
12
13
  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 Checkbox from "../index.js";
3
+ import View from "../../View/index.js";
3
4
  export default {
4
5
  title: "Components/Checkbox",
5
6
  component: Checkbox,
@@ -34,6 +35,45 @@ export const selection = () => (<Example>
34
35
  </Checkbox>
35
36
  </Example.Item>
36
37
  </Example>);
38
+ export const size = () => (<Example>
39
+ <Example.Item title="size: small">
40
+ <View gap={4} direction="row">
41
+ <Checkbox name="animal" value="dog" size="small" defaultChecked>
42
+ Checkbox
43
+ </Checkbox>
44
+ <Checkbox name="animal" value="dog" size="small" indeterminate>
45
+ Checkbox
46
+ </Checkbox>
47
+ </View>
48
+ </Example.Item>
49
+ <Example.Item title="size: medium">
50
+ <View gap={4} direction="row">
51
+ <Checkbox name="animal" value="dog" size="medium" defaultChecked>
52
+ Checkbox
53
+ </Checkbox>
54
+ <Checkbox name="animal" value="dog" size="medium" indeterminate>
55
+ Checkbox
56
+ </Checkbox>
57
+ </View>
58
+ </Example.Item>
59
+ <Example.Item title="size: large">
60
+ <View gap={4} direction="row">
61
+ <Checkbox name="animal" value="dog" size="large" defaultChecked>
62
+ Checkbox
63
+ </Checkbox>
64
+ <Checkbox name="animal" value="dog" size="large" indeterminate>
65
+ Checkbox
66
+ </Checkbox>
67
+ </View>
68
+ </Example.Item>
69
+ <Example.Item title="size: responsive, s: small, m: large">
70
+ <View gap={4} direction="row">
71
+ <Checkbox name="animal" value="dog" size={{ s: "small", m: "large" }} defaultChecked>
72
+ Checkbox
73
+ </Checkbox>
74
+ </View>
75
+ </Example.Item>
76
+ </Example>);
37
77
  export const error = () => (<Example>
38
78
  <Example.Item title="error">
39
79
  <Checkbox name="animal" value="dog" hasError>
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { classNames } from "../../utilities/helpers.js";
3
4
  import Button from "../Button/index.js";
@@ -50,7 +50,7 @@ const DropdownMenuItem = (props) => {
50
50
  if (onClick)
51
51
  onClick(e);
52
52
  };
53
- return (_jsx(MenuItem, { ...props, roundedCorners: true, className: s.item, attributes: { role: "menuitem", ...props.attributes }, onClick: handleClick }));
53
+ return (_jsx(MenuItem, { ...props, roundedCorners: true, className: [s.item, props.className], attributes: { role: "menuitem", ...props.attributes }, onClick: handleClick }));
54
54
  };
55
55
  const DropdownMenuSubMenu = (props) => {
56
56
  const { children } = props;
@@ -7,13 +7,8 @@ const FormControlLabel = (props) => {
7
7
  const { children } = props;
8
8
  const { attributes, required, group, disabled, size } = useFormControlPrivate();
9
9
  const id = `${attributes.id}-label`;
10
- const tagProps = group
11
- ? { as: "legend", attributes: { id } }
12
- : {
13
- as: "label",
14
- attributes: { id, htmlFor: attributes.id },
15
- };
16
- return (_jsxs(Text, { ...tagProps, variant: size === "large" ? "body-2" : "body-3", weight: "medium", className: s.label, color: disabled ? "disabled" : undefined, "aria-disabled": disabled, children: [children, required && (_jsx(Text, { color: disabled ? "disabled" : "critical", as: "span", children: "*" }))] }));
10
+ const TagName = group ? "legend" : "label";
11
+ return (_jsxs(Text, { variant: size === "large" ? "body-2" : "body-3", weight: "medium", className: s.label, color: disabled ? "disabled" : undefined, "aria-disabled": disabled, children: [_jsx(TagName, { id: id, htmlFor: group ? undefined : attributes.id, children: children }), required && (_jsx(Text, { color: disabled ? "disabled" : "critical", as: "span", children: "*" }))] }));
17
12
  };
18
13
  FormControlLabel.displayName = "FormControl.Label";
19
14
  export default FormControlLabel;
@@ -1,6 +1,6 @@
1
1
  import type React from "react";
2
2
  import type { IconProps } from "../Icon";
3
- import type { ActionableProps } from "../Actionable";
3
+ import type { ActionableProps, ActionableRef } from "../Actionable";
4
4
  import type * as G from "../../types/global";
5
5
  export type Size = "small" | "medium" | "large";
6
6
  export type Props = Pick<ActionableProps, "attributes" | "className" | "disabled" | "children" | "href" | "onClick" | "as" | "stopPropagation"> & {
@@ -18,6 +18,8 @@ export type AlignerProps = {
18
18
  className?: G.ClassName;
19
19
  attributes?: G.Attributes<"div">;
20
20
  };
21
- export type Export = React.ForwardRefExoticComponent<Props> & {
21
+ export type Export = React.ForwardRefExoticComponent<Props & {
22
+ ref?: ActionableRef;
23
+ }> & {
22
24
  Aligner: React.ComponentType<AlignerProps>;
23
25
  };