reshaped 3.6.0-canary.7 → 3.6.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 (102) hide show
  1. package/CHANGELOG.md +14 -1
  2. package/dist/bundle.css +1 -1
  3. package/dist/bundle.d.ts +5 -1
  4. package/dist/bundle.js +11 -11
  5. package/dist/cjs/cli/theming/reshaped.config.js +0 -1
  6. package/dist/cjs/themes/_generator/definitions/slate.d.ts +2 -2
  7. package/dist/cjs/themes/_generator/tokens/color/color.transforms.js +7 -5
  8. package/dist/cjs/themes/_generator/tokens/color/color.types.d.ts +3 -2
  9. package/dist/cjs/themes/_generator/tokens/color/utilities/convert.d.ts +1 -1
  10. package/dist/cjs/themes/_generator/tokens/color/utilities/generateColors.d.ts +2 -2
  11. package/dist/cjs/themes/_generator/tokens/color/utilities/generateColors.js +1 -1
  12. package/dist/cjs/themes/_generator/tokens/color/utilities/generateMetaColors.d.ts +2 -2
  13. package/dist/cjs/themes/_generator/tokens/shadow/shadow.transforms.js +1 -1
  14. package/dist/cjs/themes/_generator/tokens/types.d.ts +7 -4
  15. package/dist/cjs/themes/_generator/transform.js +4 -1
  16. package/dist/cjs/themes/slate/theme.css +1 -1
  17. package/dist/cjs/types/config.d.ts +1 -0
  18. package/dist/cli/theming/reshaped.config.js +0 -1
  19. package/dist/components/Badge/Badge.module.css +1 -1
  20. package/dist/components/Button/Button.module.css +1 -1
  21. package/dist/components/Button/index.d.ts +1 -1
  22. package/dist/components/Button/tests/Button.stories.js +39 -6
  23. package/dist/components/Calendar/Calendar.module.css +1 -1
  24. package/dist/components/Card/Card.module.css +1 -1
  25. package/dist/components/Checkbox/Checkbox.module.css +1 -1
  26. package/dist/components/FileUpload/FileUpload.module.css +1 -1
  27. package/dist/components/Flyout/FlyoutControlled.js +1 -1
  28. package/dist/components/Link/Link.module.css +1 -1
  29. package/dist/components/MenuItem/MenuItem.module.css +1 -1
  30. package/dist/components/Modal/tests/Modal.test.stories.js +1 -1
  31. package/dist/components/NumberField/NumberField.module.css +1 -1
  32. package/dist/components/NumberField/NumberFieldControlled.js +3 -1
  33. package/dist/components/NumberField/tests/NumberField.stories.d.ts +1 -0
  34. package/dist/components/NumberField/tests/NumberField.stories.js +24 -6
  35. package/dist/components/Overlay/tests/Overlay.stories.js +3 -3
  36. package/dist/components/Overlay/tests/Overlay.test.stories.js +1 -1
  37. package/dist/components/Radio/Radio.module.css +1 -1
  38. package/dist/components/Resizable/Resizable.js +5 -27
  39. package/dist/components/Resizable/Resizable.module.css +1 -1
  40. package/dist/components/Resizable/Resizable.types.d.ts +5 -2
  41. package/dist/components/Resizable/ResizableHandle.d.ts +5 -0
  42. package/dist/components/Resizable/ResizableHandle.js +32 -0
  43. package/dist/components/Resizable/tests/Resizable.stories.d.ts +22 -1
  44. package/dist/components/Resizable/tests/Resizable.stories.js +154 -65
  45. package/dist/components/Select/Select.js +1 -1
  46. package/dist/components/Select/Select.module.css +1 -1
  47. package/dist/components/Slider/tests/Slider.stories.js +0 -6
  48. package/dist/components/Tabs/Tabs.module.css +1 -1
  49. package/dist/components/Tabs/TabsPanel.d.ts +1 -0
  50. package/dist/components/Tabs/TabsPanel.js +22 -1
  51. package/dist/components/Tabs/tests/Tabs.stories.js +24 -1
  52. package/dist/components/TextField/TextField.module.css +1 -1
  53. package/dist/components/ToggleButton/ToggleButton.d.ts +3 -0
  54. package/dist/components/ToggleButton/ToggleButton.js +11 -0
  55. package/dist/components/ToggleButton/ToggleButton.types.d.ts +20 -0
  56. package/dist/components/ToggleButton/ToggleButton.types.js +1 -0
  57. package/dist/components/ToggleButton/ToggleButtonControlled.d.ts +3 -0
  58. package/dist/components/ToggleButton/ToggleButtonControlled.js +22 -0
  59. package/dist/components/ToggleButton/ToggleButtonUncontrolled.d.ts +3 -0
  60. package/dist/components/ToggleButton/ToggleButtonUncontrolled.js +15 -0
  61. package/dist/components/ToggleButton/index.d.ts +2 -0
  62. package/dist/components/ToggleButton/index.js +1 -0
  63. package/dist/components/ToggleButton/tests/ToggleButton.stories.d.ts +23 -0
  64. package/dist/components/ToggleButton/tests/ToggleButton.stories.js +87 -0
  65. package/dist/components/ToggleButtonGroup/ToggleButtonGroup.context.d.ts +5 -0
  66. package/dist/components/ToggleButtonGroup/ToggleButtonGroup.context.js +5 -0
  67. package/dist/components/ToggleButtonGroup/ToggleButtonGroup.d.ts +3 -0
  68. package/dist/components/ToggleButtonGroup/ToggleButtonGroup.js +11 -0
  69. package/dist/components/ToggleButtonGroup/ToggleButtonGroup.types.d.ts +24 -0
  70. package/dist/components/ToggleButtonGroup/ToggleButtonGroup.types.js +1 -0
  71. package/dist/components/ToggleButtonGroup/ToggleButtonGroupControlled.d.ts +4 -0
  72. package/dist/components/ToggleButtonGroup/ToggleButtonGroupControlled.js +67 -0
  73. package/dist/components/ToggleButtonGroup/ToggleButtonGroupUncontrolled.d.ts +4 -0
  74. package/dist/components/ToggleButtonGroup/ToggleButtonGroupUncontrolled.js +18 -0
  75. package/dist/components/ToggleButtonGroup/index.d.ts +3 -0
  76. package/dist/components/ToggleButtonGroup/index.js +2 -0
  77. package/dist/components/ToggleButtonGroup/tests/ToggleButtonGroup.stories.d.ts +22 -0
  78. package/dist/components/ToggleButtonGroup/tests/ToggleButtonGroup.stories.js +129 -0
  79. package/dist/hooks/tests/useDrag.stories.js +1 -1
  80. package/dist/hooks/tests/useToggle.stories.js +1 -1
  81. package/dist/hooks/useToggle.d.ts +1 -1
  82. package/dist/hooks/useToggle.js +2 -2
  83. package/dist/index.d.ts +5 -1
  84. package/dist/index.js +2 -0
  85. package/dist/styles/bleed/bleed.module.css +1 -1
  86. package/dist/themes/_generator/definitions/slate.d.ts +2 -2
  87. package/dist/themes/_generator/tokens/color/color.transforms.js +7 -5
  88. package/dist/themes/_generator/tokens/color/color.types.d.ts +3 -2
  89. package/dist/themes/_generator/tokens/color/utilities/convert.d.ts +1 -1
  90. package/dist/themes/_generator/tokens/color/utilities/generateColors.d.ts +2 -2
  91. package/dist/themes/_generator/tokens/color/utilities/generateColors.js +1 -1
  92. package/dist/themes/_generator/tokens/color/utilities/generateMetaColors.d.ts +2 -2
  93. package/dist/themes/_generator/tokens/shadow/shadow.transforms.js +1 -1
  94. package/dist/themes/_generator/tokens/types.d.ts +7 -4
  95. package/dist/themes/_generator/transform.js +4 -1
  96. package/dist/themes/slate/theme.css +1 -1
  97. package/dist/types/config.d.ts +1 -0
  98. package/dist/utilities/a11y/index.d.ts +1 -1
  99. package/dist/utilities/a11y/index.js +1 -1
  100. package/package.json +27 -27
  101. package/dist/components/Resizable/tests/Resizable.test.stories.d.ts +0 -18
  102. package/dist/components/Resizable/tests/Resizable.test.stories.js +0 -25
@@ -1,14 +1,35 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React from "react";
3
4
  import { classNames } from "../../utilities/props.js";
5
+ import { getFocusableElements } from "../../utilities/a11y/index.js";
4
6
  import { useTabs } from "./TabsContext.js";
5
7
  import s from "./Tabs.module.css";
6
8
  const TabsPanel = (props) => {
7
9
  const { value: panelValue, children, className, attributes } = props;
8
10
  const { value, panelId, buttonId } = useTabs(panelValue);
11
+ const [needsTabIndex, setNeedsTabIndex] = React.useState(true);
12
+ const rootRef = React.useRef(null);
9
13
  const active = panelValue === value;
10
14
  const panelClassNames = classNames(s.panel, !active && s["--panel-hidden"], className);
11
- return (_jsx("div", { ...attributes, className: panelClassNames, tabIndex: 0, role: "tabpanel", id: panelId, "aria-labelledby": buttonId, children: active && children }));
15
+ React.useEffect(() => {
16
+ const el = rootRef.current;
17
+ if (!el)
18
+ return;
19
+ const updateTabIndex = () => {
20
+ setNeedsTabIndex(!getFocusableElements(el).length);
21
+ };
22
+ updateTabIndex();
23
+ const observer = new MutationObserver(updateTabIndex);
24
+ observer.observe(el, {
25
+ childList: true,
26
+ subtree: true,
27
+ attributes: true,
28
+ attributeFilter: ["tabindex", "disabled", "href"],
29
+ });
30
+ return () => observer.disconnect();
31
+ }, []);
32
+ return (_jsx("div", { ...attributes, ref: rootRef, className: panelClassNames, tabIndex: needsTabIndex ? 0 : undefined, role: "tabpanel", id: panelId, "aria-labelledby": buttonId, children: active && children }));
12
33
  };
13
34
  TabsPanel.displayName = "Tabs.Panel";
14
35
  export default TabsPanel;
@@ -5,6 +5,7 @@ import View from "../../View/index.js";
5
5
  import Text from "../../Text/index.js";
6
6
  import ScrollArea from "../../ScrollArea/index.js";
7
7
  import IconZap from "../../../icons/Zap.js";
8
+ import Button from "../../Button/index.js";
8
9
  export default {
9
10
  title: "Components/Tabs",
10
11
  component: Tabs,
@@ -127,7 +128,7 @@ export const direction = () => (<Example>
127
128
  </Example.Item>
128
129
  </Example>);
129
130
  export const composition = () => (<Example>
130
- <Example.Item title="switching panels">
131
+ <Example.Item title="panels without focusable content">
131
132
  <Tabs>
132
133
  <View gap={4}>
133
134
  <Tabs.List>
@@ -142,6 +143,28 @@ export const composition = () => (<Example>
142
143
  </View>
143
144
  </Tabs>
144
145
  </Example.Item>
146
+
147
+ <Example.Item title="panels with focusable content">
148
+ <Tabs>
149
+ <View gap={4}>
150
+ <Tabs.List>
151
+ <Tabs.Item value="0">Item 1</Tabs.Item>
152
+ <Tabs.Item value="1">Long item 2</Tabs.Item>
153
+ <Tabs.Item value="2">Very long item 3</Tabs.Item>
154
+ </Tabs.List>
155
+
156
+ <Tabs.Panel value="0">
157
+ <Button onClick={() => { }}>Tab 1 action</Button>
158
+ </Tabs.Panel>
159
+ <Tabs.Panel value="1">
160
+ <Button onClick={() => { }}>Tab 2 action</Button>
161
+ </Tabs.Panel>
162
+ <Tabs.Panel value="2">
163
+ <Button onClick={() => { }}>Tab 3 action</Button>
164
+ </Tabs.Panel>
165
+ </View>
166
+ </Tabs>
167
+ </Example.Item>
145
168
  </Example>);
146
169
  export const icon = () => (<Example>
147
170
  <Example.Item title="icon">
@@ -1 +1 @@
1
- .root{--rs-p-h:var(--rs-text-field-gap);align-items:center;background:var(--rs-color-background-elevation-base);border:1px solid var(--rs-color-border-neutral);border-radius:var(--rs-text-field-radius);column-gap:var(--rs-text-field-gap);display:flex;padding:0 calc(var(--rs-text-field-gap) - 1px);position:relative;z-index:0}.root.--focused,.root:has(label:active),.root:not(:has(button:focus,a:focus,[tabindex="0"]:focus,[role=button]:focus)):focus-within{border-color:var(--rs-color-border-primary);box-shadow:0 0 0 1px var(--rs-color-border-primary)}.root.--multiline{flex-wrap:wrap}.root.--multiline .input{width:auto}.root.--rounded{border-radius:999px}.root.--rounded .affix:first-child,.root.--rounded .icon:first-child{padding-inline-start:var(--rs-unit-x1)}.root.--rounded .input:first-child{padding-inline-start:calc(var(--rs-text-field-gap) + var(--rs-unit-x1))}.inner{column-gap:var(--rs-text-field-gap);display:inline-flex;flex-grow:1}.input{background:none;border:none;box-sizing:border-box;color:var(--rs-color-foreground-neutral);flex-grow:1;font-family:var(--rs-font-family-body);font-weight:var(--rs-font-weight-regular);margin:0 calc(var(--rs-text-field-gap) * -1);outline:none;padding-block:calc(var(--rs-text-field-p-v) - 1px);padding-inline:var(--rs-text-field-gap);position:relative;width:100%;z-index:1}.input:-webkit-autofill{-webkit-background-clip:text;-webkit-text-fill-color:var(--rs-color-foreground-neutral)}.affix,.input{font-size:var(--rs-text-field-font-size);letter-spacing:var(--rs-text-field-letter-spacing);line-height:var(--rs-text-field-line-height)}.affix,.icon{cursor:text}.affix,.icon,.slot{align-items:center;display:flex;flex-shrink:0;max-width:100%;min-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px);position:relative;z-index:5}.input+.affix--position-end,.input+.icon--position-end,.input+.slot--position-end{margin-inline-start:auto}.slot--position-end:last-child{margin-inline-end:calc(var(--rs-text-field-gap) * -1);padding-inline-end:var(--rs-text-field-action-inset)}.affix{color:var(--rs-color-foreground-neutral-faded)}.affix.affix--position-start{padding-inline-end:var(--rs-text-field-gap)}.affix.affix--position-start:after{border-inline-end:1px solid var(--rs-color-border-neutral-faded);content:"";inset-block:var(--rs-unit-x1);inset-inline-end:0;position:absolute}.affix.affix--position-end{padding-inline-start:var(--rs-text-field-gap)}.affix.affix--position-end:after{border-inline-start:1px solid var(--rs-color-border-neutral-faded);content:"";inset-block:var(--rs-unit-x1);inset-inline-start:0;position:absolute}.root.--disabled{background:var(--rs-color-background-disabled-faded);border-color:var(--rs-color-border-disabled)}.root.--disabled,.root.--disabled .input{color:var(--rs-color-foreground-disabled);cursor:not-allowed}.--size-small{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-medium{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-large{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2)}.--size-xlarge{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1)}.root.--variant-faded{background:var(--rs-color-background-neutral-faded);border-color:transparent}.root.--variant-faded.--focused,.root.--variant-faded:has(label:active),.root.--variant-faded:not(:has(button:focus,a:focus,[tabindex="0"]:focus,[role=button]:focus)):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.--focused,.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.--focused,.root.--status-error:focus-within{border-color:var(--rs-color-border-primary)}@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-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-medium--m{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-large--m{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2)}.--size-xlarge--m{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1)}}@media (--rs-viewport-l ){.--size-small--l{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-medium--l{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-large--l{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2)}.--size-xlarge--l{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1)}}@media (--rs-viewport-xl ){.--size-small--xl{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-medium--xl{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1)}.--size-large--xl{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2)}.--size-xlarge--xl{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1)}}
1
+ .root{--rs-p-h:var(--rs-text-field-gap);align-items:center;background:var(--rs-color-background-elevation-base);border:1px solid var(--rs-color-border-neutral);border-radius:var(--rs-text-field-radius);column-gap:var(--rs-text-field-gap);display:flex;padding:0 calc(var(--rs-text-field-gap) - 1px);position:relative;z-index:0}.root.--focused,.root:has(label:active),.root:not(:has(button:focus,a:focus,[tabindex="0"]:focus,[role=button]:focus)):focus-within{border-color:var(--rs-color-border-primary);box-shadow:0 0 0 1px var(--rs-color-border-primary)}.root.--multiline{flex-wrap:wrap}.root.--multiline .input{width:auto}.root.--rounded{border-radius:999px}.root.--rounded .affix:first-child,.root.--rounded .icon:first-child{padding-inline-start:var(--rs-unit-x1)}.root.--rounded .input:first-child{padding-inline-start:calc(var(--rs-text-field-gap) + var(--rs-unit-x1))}.inner{column-gap:var(--rs-text-field-gap);display:inline-flex;flex-grow:1}.input{background:none;border:none;box-sizing:border-box;color:var(--rs-color-foreground-neutral);flex-grow:1;font-family:var(--rs-font-family-body);font-weight:var(--rs-font-weight-regular);margin:0 calc(var(--rs-text-field-gap) * -1);outline:none;padding-block:calc(var(--rs-text-field-p-v) - 1px);padding-inline:var(--rs-text-field-gap);position:relative;width:100%;z-index:1}.input:-webkit-autofill{-webkit-background-clip:text;-webkit-text-fill-color:var(--rs-color-foreground-neutral)}.affix,.input{font-size:var(--rs-text-field-font-size);letter-spacing:var(--rs-text-field-letter-spacing);line-height:var(--rs-text-field-line-height)}.affix,.icon{cursor:text}.affix,.icon,.slot{align-items:center;display:flex;flex-shrink:0;max-width:100%;min-height:var(--rs-text-field-attachment-height);position:relative;z-index:5}.input+.affix--position-end,.input+.icon--position-end,.input+.slot--position-end{margin-inline-start:auto}.slot--position-end:last-child{margin-inline-end:calc(var(--rs-text-field-gap) * -1);padding-inline-end:var(--rs-text-field-action-inset)}.affix{color:var(--rs-color-foreground-neutral-faded)}.affix.affix--position-start{padding-inline-end:var(--rs-text-field-gap)}.affix.affix--position-start:after{border-inline-end:1px solid var(--rs-color-border-neutral-faded);content:"";inset-block:var(--rs-unit-x1);inset-inline-end:0;position:absolute}.affix.affix--position-end{padding-inline-start:var(--rs-text-field-gap)}.affix.affix--position-end:after{border-inline-start:1px solid var(--rs-color-border-neutral-faded);content:"";inset-block:var(--rs-unit-x1);inset-inline-start:0;position:absolute}.root.--disabled{background:var(--rs-color-background-disabled-faded);border-color:var(--rs-color-border-disabled)}.root.--disabled,.root.--disabled .input{color:var(--rs-color-foreground-disabled);cursor:not-allowed}.--size-small{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) - 2px)}.--size-medium{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}.--size-large{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}.--size-xlarge{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}.root.--variant-faded{background:var(--rs-color-background-neutral-faded);border-color:transparent}.root.--variant-faded.--focused,.root.--variant-faded:has(label:active),.root.--variant-faded:not(:has(button:focus,a:focus,[tabindex="0"]:focus,[role=button]:focus)):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.--focused,.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.--focused,.root.--status-error:focus-within{border-color:var(--rs-color-border-primary)}@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-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) - 2px)}.--size-medium--m{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}.--size-large--m{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}.--size-xlarge--m{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}}@media (--rs-viewport-l ){.--size-small--l{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) - 2px)}.--size-medium--l{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}.--size-large--l{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}.--size-xlarge--l{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}}@media (--rs-viewport-xl ){.--size-small--xl{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x1);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x2) - 2px)}.--size-medium--xl{--rs-text-field-gap:var(--rs-unit-x2);--rs-text-field-radius:var(--rs-radius-small);--rs-text-field-p-v:var(--rs-unit-x2);--rs-text-field-font-size:var(--rs-font-size-body-3);--rs-text-field-line-height:var(--rs-line-height-body-3);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-3);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}.--size-large--xl{--rs-text-field-gap:var(--rs-unit-x3);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x3);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x2);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}.--size-xlarge--xl{--rs-text-field-gap:var(--rs-unit-x4);--rs-text-field-radius:var(--rs-radius-medium);--rs-text-field-p-v:var(--rs-unit-x4);--rs-text-field-font-size:var(--rs-font-size-body-2);--rs-text-field-line-height:var(--rs-line-height-body-2);--rs-text-field-letter-spacing:var(--rs-letter-spacing-body-2);--rs-text-field-action-inset:var(--rs-unit-x1);--rs-text-field-attachment-height:calc(var(--rs-text-field-line-height) + var(--rs-unit-x4) - 2px)}}
@@ -0,0 +1,3 @@
1
+ import type * as T from "./ToggleButton.types";
2
+ declare const ToggleButton: React.FC<T.Props>;
3
+ export default ToggleButton;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import ToggleButtonControlled from "./ToggleButtonControlled.js";
3
+ import ToggleButtonUncontrolled from "./ToggleButtonUncontrolled.js";
4
+ const ToggleButton = (props) => {
5
+ const { checked } = props;
6
+ if (checked !== undefined)
7
+ return _jsx(ToggleButtonControlled, { ...props });
8
+ return _jsx(ToggleButtonUncontrolled, { ...props });
9
+ };
10
+ ToggleButton.displayName = "ToggleButton";
11
+ export default ToggleButton;
@@ -0,0 +1,20 @@
1
+ import type { ButtonProps } from "../Button";
2
+ type BaseProps = Omit<ButtonProps, "variant" | "higlighted"> & {
3
+ variant?: Extract<ButtonProps["variant"], "outline" | "ghost">;
4
+ value?: string;
5
+ onChange?: (args: {
6
+ checked: boolean;
7
+ value: string;
8
+ event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>;
9
+ }) => void;
10
+ };
11
+ export type ControlledProps = BaseProps & {
12
+ defaultChecked?: never;
13
+ checked: boolean;
14
+ };
15
+ export type UncontrolledProps = BaseProps & {
16
+ defaultChecked?: boolean;
17
+ checked?: never;
18
+ };
19
+ export type Props = ControlledProps | UncontrolledProps;
20
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type * as T from "./ToggleButton.types";
2
+ declare const ToggleButtonControlled: React.FC<T.ControlledProps>;
3
+ export default ToggleButtonControlled;
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import Button from "../Button/index.js";
4
+ import { useToggleButtonGroup } from "../ToggleButtonGroup/index.js";
5
+ const ToggleButtonControlled = (props) => {
6
+ const { variant = "outline", value, onChange, onClick, ...buttonProps } = props;
7
+ const toggleButtonGroup = useToggleButtonGroup();
8
+ const checked = (value ? toggleButtonGroup?.value?.includes(value) : undefined) ?? props.checked;
9
+ const handleClick = (event) => {
10
+ const changeArgs = { checked: !checked, value: value ?? "", event };
11
+ onClick?.(event);
12
+ if (toggleButtonGroup) {
13
+ toggleButtonGroup?.onChange?.(changeArgs);
14
+ }
15
+ else {
16
+ onChange?.(changeArgs);
17
+ }
18
+ };
19
+ return (_jsx(Button, { ...buttonProps, variant: variant, onClick: handleClick, highlighted: checked, attributes: { ...buttonProps.attributes, "aria-pressed": checked } }));
20
+ };
21
+ ToggleButtonControlled.displayName = "ToggleButtonControlled";
22
+ export default ToggleButtonControlled;
@@ -0,0 +1,3 @@
1
+ import type * as T from "./ToggleButton.types";
2
+ declare const ToggleButtonUncontrolled: React.FC<T.UncontrolledProps>;
3
+ export default ToggleButtonUncontrolled;
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import useToggle from "../../hooks/useToggle.js";
4
+ import ToggleButtonControlled from "./ToggleButtonControlled.js";
5
+ const ToggleButtonUncontrolled = (props) => {
6
+ const { defaultChecked, onChange, ...buttonProps } = props;
7
+ const buttonToggle = useToggle(defaultChecked);
8
+ const handleChange = (args) => {
9
+ onChange?.(args);
10
+ buttonToggle.toggle(args.checked);
11
+ };
12
+ return (_jsx(ToggleButtonControlled, { ...buttonProps, onChange: handleChange, checked: buttonToggle.active }));
13
+ };
14
+ ToggleButtonUncontrolled.displayName = "ToggleButtonUncontrolled";
15
+ export default ToggleButtonUncontrolled;
@@ -0,0 +1,2 @@
1
+ export { default } from "./ToggleButton";
2
+ export type { Props as ToggleButtonProps } from "./ToggleButton.types";
@@ -0,0 +1 @@
1
+ export { default } from "./ToggleButton.js";
@@ -0,0 +1,23 @@
1
+ import { type StoryObj } from "@storybook/react-vite";
2
+ import { type Mock } from "storybook/test";
3
+ declare const _default: {
4
+ title: string;
5
+ component: import("react").FC<import("..").ToggleButtonProps>;
6
+ parameters: {
7
+ iframe: {
8
+ url: string;
9
+ };
10
+ };
11
+ };
12
+ export default _default;
13
+ export declare const variant: {
14
+ name: string;
15
+ render: () => import("react").JSX.Element;
16
+ };
17
+ export declare const onChange: StoryObj<{
18
+ handleUncontrolledChange: Mock;
19
+ handleControlledChange: Mock;
20
+ }>;
21
+ export declare const onClick: StoryObj<{
22
+ handleClick: Mock;
23
+ }>;
@@ -0,0 +1,87 @@
1
+ import { expect, fn, userEvent } from "storybook/test";
2
+ import { Example } from "../../../utilities/storybook/index.js";
3
+ import ToggleButton from "../ToggleButton.js";
4
+ export default {
5
+ title: "Components/ToggleButton",
6
+ component: ToggleButton,
7
+ parameters: {
8
+ iframe: {
9
+ url: "https://reshaped.so/docs/components/toggle-button",
10
+ },
11
+ },
12
+ };
13
+ export const variant = {
14
+ name: "variant",
15
+ render: () => (<Example>
16
+ <Example.Item title="outline">
17
+ <ToggleButton>Button</ToggleButton>
18
+ </Example.Item>
19
+ <Example.Item title="ghost">
20
+ <ToggleButton variant="ghost">Button</ToggleButton>
21
+ </Example.Item>
22
+ </Example>),
23
+ };
24
+ export const onChange = {
25
+ name: "checked, defaultChecked, onChange",
26
+ args: {
27
+ handleUncontrolledChange: fn(),
28
+ handleControlledChange: fn(),
29
+ },
30
+ render: (args) => (<Example>
31
+ <Example.Item title="defaultChecked, onChange">
32
+ <ToggleButton defaultChecked onChange={args.handleUncontrolledChange} value="1">
33
+ Button
34
+ </ToggleButton>
35
+ </Example.Item>
36
+ <Example.Item title="checked, onChange">
37
+ <ToggleButton checked onChange={args.handleControlledChange} value="2">
38
+ Button
39
+ </ToggleButton>
40
+ </Example.Item>
41
+ </Example>),
42
+ play: async ({ canvas, args }) => {
43
+ const [uncontrolledButton, controlledButton] = canvas.getAllByRole("button");
44
+ await userEvent.click(uncontrolledButton);
45
+ expect(args.handleUncontrolledChange).toHaveBeenCalledTimes(1);
46
+ expect(args.handleUncontrolledChange).toHaveBeenLastCalledWith({
47
+ checked: false,
48
+ value: "1",
49
+ event: expect.objectContaining({ target: uncontrolledButton }),
50
+ });
51
+ await userEvent.click(uncontrolledButton);
52
+ expect(args.handleUncontrolledChange).toHaveBeenCalledTimes(2);
53
+ expect(args.handleUncontrolledChange).toHaveBeenLastCalledWith({
54
+ checked: true,
55
+ value: "1",
56
+ event: expect.objectContaining({ target: uncontrolledButton }),
57
+ });
58
+ await userEvent.click(controlledButton);
59
+ expect(args.handleControlledChange).toHaveBeenCalledTimes(1);
60
+ expect(args.handleControlledChange).toHaveBeenLastCalledWith({
61
+ checked: false,
62
+ value: "2",
63
+ event: expect.objectContaining({ target: controlledButton }),
64
+ });
65
+ await userEvent.click(controlledButton);
66
+ // Stayed checked after the first click
67
+ expect(args.handleControlledChange).toHaveBeenCalledTimes(2);
68
+ expect(args.handleControlledChange).toHaveBeenLastCalledWith({
69
+ checked: false,
70
+ value: "2",
71
+ event: expect.objectContaining({ target: uncontrolledButton }),
72
+ });
73
+ },
74
+ };
75
+ export const onClick = {
76
+ name: "onClick",
77
+ args: {
78
+ handleClick: fn(),
79
+ },
80
+ render: (args) => <ToggleButton onClick={args.handleClick}>Button</ToggleButton>,
81
+ play: async ({ canvas, args }) => {
82
+ const [button] = canvas.getAllByRole("button");
83
+ await userEvent.click(button);
84
+ expect(args.handleClick).toHaveBeenCalledOnce();
85
+ expect(args.handleClick).toHaveBeenLastCalledWith(expect.objectContaining({ target: button }));
86
+ },
87
+ };
@@ -0,0 +1,5 @@
1
+ import React from "react";
2
+ import type * as T from "./ToggleButtonGroup.types";
3
+ declare const ToggleButtonGroupContext: React.Context<T.Context | null>;
4
+ export declare const useToggleButtonGroup: () => T.Context | null;
5
+ export default ToggleButtonGroupContext;
@@ -0,0 +1,5 @@
1
+ "use client";
2
+ import React from "react";
3
+ const ToggleButtonGroupContext = React.createContext(null);
4
+ export const useToggleButtonGroup = () => React.useContext(ToggleButtonGroupContext);
5
+ export default ToggleButtonGroupContext;
@@ -0,0 +1,3 @@
1
+ import type * as T from "./ToggleButtonGroup.types";
2
+ declare const ToggleButtonGroup: React.FC<T.Props>;
3
+ export default ToggleButtonGroup;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import ToggleButtonGroupControlled from "./ToggleButtonGroupControlled.js";
3
+ import ToggleButtonGroupUncontrolled from "./ToggleButtonGroupUncontrolled.js";
4
+ const ToggleButtonGroup = (props) => {
5
+ const { value } = props;
6
+ if (value !== undefined)
7
+ return _jsx(ToggleButtonGroupControlled, { ...props });
8
+ return _jsx(ToggleButtonGroupUncontrolled, { ...props });
9
+ };
10
+ ToggleButtonGroup.displayName = "ToggleButtonGroup";
11
+ export default ToggleButtonGroup;
@@ -0,0 +1,24 @@
1
+ import type React from "react";
2
+ import type { ButtonGroupProps } from "../Button";
3
+ import type { ToggleButtonProps } from "../ToggleButton";
4
+ type BaseProps = {
5
+ selectionMode?: "single" | "multiple";
6
+ onChange?: (args: {
7
+ value: string[];
8
+ event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>;
9
+ }) => void;
10
+ } & ButtonGroupProps;
11
+ export type ControlledProps = BaseProps & {
12
+ value: string[];
13
+ defaultValue?: never;
14
+ };
15
+ export type UncontrolledProps = BaseProps & {
16
+ value?: never;
17
+ defaultValue?: string[];
18
+ };
19
+ export type Props = ControlledProps | UncontrolledProps;
20
+ export type Context = {
21
+ onChange: ToggleButtonProps["onChange"];
22
+ value?: string[];
23
+ };
24
+ export {};
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import type * as T from "./ToggleButtonGroup.types";
3
+ declare const ToggleButtonGroupControlled: React.FC<T.ControlledProps>;
4
+ export default ToggleButtonGroupControlled;
@@ -0,0 +1,67 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import Button from "../Button/index.js";
5
+ import useHotkeys from "../../hooks/useHotkeys.js";
6
+ import Context from "./ToggleButtonGroup.context.js";
7
+ import { focusFirstElement, focusLastElement, focusNextElement, focusPreviousElement, } from "../../utilities/a11y/index.js";
8
+ import ToggleButton from "../ToggleButton/index.js";
9
+ const ToggleButtonGroupItem = (props) => {
10
+ const { focusable, onFocus, ...buttonProps } = props;
11
+ return (_jsx(ToggleButton, { ...buttonProps, attributes: {
12
+ ...buttonProps.attributes,
13
+ tabIndex: focusable ? 0 : -1,
14
+ onFocus,
15
+ } }));
16
+ };
17
+ const ToggleButtonGroupControlled = (props) => {
18
+ const { onChange, value, selectionMode = "single", children, ...buttonGroupProps } = props;
19
+ const focusableIndexRef = React.useRef(0);
20
+ let toggleButtonIndex = 0;
21
+ const renderedChildren = React.Children.map(children, (child) => {
22
+ if (!React.isValidElement(child) || child.type !== ToggleButton || !child.props) {
23
+ return child;
24
+ }
25
+ const boundIndex = toggleButtonIndex;
26
+ toggleButtonIndex += 1;
27
+ const focusable = focusableIndexRef.current === boundIndex;
28
+ return (_jsx(ToggleButtonGroupItem, { ...child.props, focusable: focusable, onFocus: () => {
29
+ focusableIndexRef.current = boundIndex;
30
+ } }));
31
+ });
32
+ const handleChange = (args) => {
33
+ const { event, value: itemValue, checked } = args;
34
+ if (!itemValue)
35
+ return;
36
+ let nextValue = selectionMode === "single" ? [itemValue] : [...value];
37
+ if (selectionMode === "multiple") {
38
+ if (checked) {
39
+ nextValue.push(itemValue);
40
+ }
41
+ else {
42
+ nextValue = nextValue.filter((v) => v !== itemValue);
43
+ }
44
+ }
45
+ if (onChange)
46
+ onChange({ value: nextValue, event });
47
+ };
48
+ const { ref: hotkeysRef } = useHotkeys({
49
+ "ArrowLeft, ArrowUp": () => {
50
+ focusPreviousElement(hotkeysRef.current);
51
+ },
52
+ "ArrowRight, ArrowDown": () => {
53
+ focusNextElement(hotkeysRef.current);
54
+ },
55
+ Home: () => {
56
+ focusFirstElement(hotkeysRef.current);
57
+ },
58
+ End: () => {
59
+ focusLastElement(hotkeysRef.current);
60
+ },
61
+ }, [], {
62
+ preventDefault: true,
63
+ });
64
+ return (_jsx(Context.Provider, { value: { onChange: handleChange, value }, children: _jsx(Button.Group, { ...buttonGroupProps, attributes: { ref: hotkeysRef, ...buttonGroupProps?.attributes }, children: renderedChildren }) }));
65
+ };
66
+ ToggleButtonGroupControlled.displayName = "ToggleButtonGroupControlled";
67
+ export default ToggleButtonGroupControlled;
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import type * as T from "./ToggleButtonGroup.types";
3
+ declare const ToggleButtonGroupUncontrolled: React.FC<T.UncontrolledProps>;
4
+ export default ToggleButtonGroupUncontrolled;
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import ToggleButtonGroupControlled from "./ToggleButtonGroupControlled.js";
5
+ const ToggleButtonGroupUncontrolled = (props) => {
6
+ const { defaultValue, onChange } = props;
7
+ const [value, setValue] = React.useState(defaultValue || []);
8
+ const handleChange = (args) => {
9
+ if (!args.value)
10
+ return;
11
+ setValue(args.value);
12
+ if (onChange)
13
+ onChange(args);
14
+ };
15
+ return (_jsx(ToggleButtonGroupControlled, { ...props, value: value, defaultValue: undefined, onChange: handleChange }));
16
+ };
17
+ ToggleButtonGroupUncontrolled.displayName = "ToggleButtonGroupUncontrolled";
18
+ export default ToggleButtonGroupUncontrolled;
@@ -0,0 +1,3 @@
1
+ export { default } from "./ToggleButtonGroup";
2
+ export type { Props as ToggleButtonGroupProps } from "./ToggleButtonGroup.types";
3
+ export { useToggleButtonGroup } from "./ToggleButtonGroup.context";
@@ -0,0 +1,2 @@
1
+ export { default } from "./ToggleButtonGroup.js";
2
+ export { useToggleButtonGroup } from "./ToggleButtonGroup.context.js";
@@ -0,0 +1,22 @@
1
+ import { StoryObj } from "@storybook/react-vite";
2
+ import { Mock } from "storybook/test";
3
+ declare const _default: {
4
+ title: string;
5
+ component: import("react").FC<import("./..").ToggleButtonGroupProps>;
6
+ parameters: {
7
+ iframe: {
8
+ url: string;
9
+ };
10
+ };
11
+ };
12
+ export default _default;
13
+ export declare const single: StoryObj<{
14
+ handleUncontrolledChange: Mock;
15
+ handleControlledChange: Mock;
16
+ handleItemChange: Mock;
17
+ }>;
18
+ export declare const multiple: StoryObj<{
19
+ handleUncontrolledChange: Mock;
20
+ handleControlledChange: Mock;
21
+ }>;
22
+ export declare const className: StoryObj;