reshaped 3.6.0-canary.8 → 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.
@@ -1,2 +1,2 @@
1
1
  export { default } from "./Button";
2
- export type { Props as ButtonProps, AlignerProps as ButtonAlignerProps } from "./Button.types";
2
+ export type { Props as ButtonProps, AlignerProps as ButtonAlignerProps, GroupProps as ButtonGroupProps, } from "./Button.types";
@@ -72,7 +72,7 @@ const FlyoutControlled = (props) => {
72
72
  active: isRendered && triggerType !== "hover",
73
73
  contentRef: flyoutElRef,
74
74
  triggerRef: triggerElRef,
75
- blocking: trapFocusMode === "dialog",
75
+ blocking: false,
76
76
  });
77
77
  const clearTimer = React.useCallback(() => {
78
78
  if (timerRef.current)
@@ -246,12 +246,6 @@ export const testForm = {
246
246
  args.handleFormChange({ formData: nextState });
247
247
  setData(nextState);
248
248
  };
249
- React.useEffect(() => {
250
- const formData = new FormData(formRef.current);
251
- const nextState = [...formData.entries()];
252
- args.handleFormInit({ formData: nextState });
253
- setData(nextState);
254
- }, []);
255
249
  return (<View paddingTop={10} gap={4}>
256
250
  <form onChange={handleChange} ref={formRef}>
257
251
  <Slider range minName="slider-min" maxName="slider-max" defaultMinValue={30} defaultMaxValue={50}/>
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import type * as T from "./Tabs.types";
2
3
  declare const TabsPanel: React.FC<T.PanelProps>;
3
4
  export default TabsPanel;
@@ -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)}}
@@ -16,7 +16,7 @@ const ToggleButtonControlled = (props) => {
16
16
  onChange?.(changeArgs);
17
17
  }
18
18
  };
19
- return _jsx(Button, { ...buttonProps, variant: variant, onClick: handleClick, highlighted: checked });
19
+ return (_jsx(Button, { ...buttonProps, variant: variant, onClick: handleClick, highlighted: checked, attributes: { ...buttonProps.attributes, "aria-pressed": checked } }));
20
20
  };
21
21
  ToggleButtonControlled.displayName = "ToggleButtonControlled";
22
22
  export default ToggleButtonControlled;
@@ -1,13 +1,13 @@
1
1
  import type React from "react";
2
+ import type { ButtonGroupProps } from "../Button";
2
3
  import type { ToggleButtonProps } from "../ToggleButton";
3
4
  type BaseProps = {
4
- children?: React.ReactNode;
5
5
  selectionMode?: "single" | "multiple";
6
6
  onChange?: (args: {
7
7
  value: string[];
8
8
  event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>;
9
9
  }) => void;
10
- };
10
+ } & ButtonGroupProps;
11
11
  export type ControlledProps = BaseProps & {
12
12
  value: string[];
13
13
  defaultValue?: never;
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import type * as T from "./ToggleButtonGroup.types";
2
3
  declare const ToggleButtonGroupControlled: React.FC<T.ControlledProps>;
3
4
  export default ToggleButtonGroupControlled;
@@ -1,11 +1,34 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React from "react";
3
4
  import Button from "../Button/index.js";
4
5
  import useHotkeys from "../../hooks/useHotkeys.js";
5
6
  import Context from "./ToggleButtonGroup.context.js";
6
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
+ };
7
17
  const ToggleButtonGroupControlled = (props) => {
8
- const { onChange, value, children, selectionMode = "single" } = 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
+ });
9
32
  const handleChange = (args) => {
10
33
  const { event, value: itemValue, checked } = args;
11
34
  if (!itemValue)
@@ -38,7 +61,7 @@ const ToggleButtonGroupControlled = (props) => {
38
61
  }, [], {
39
62
  preventDefault: true,
40
63
  });
41
- return (_jsx(Context.Provider, { value: { onChange: handleChange, value }, children: _jsx(Button.Group, { attributes: { ref: hotkeysRef }, children: children }) }));
64
+ return (_jsx(Context.Provider, { value: { onChange: handleChange, value }, children: _jsx(Button.Group, { ...buttonGroupProps, attributes: { ref: hotkeysRef, ...buttonGroupProps?.attributes }, children: renderedChildren }) }));
42
65
  };
43
66
  ToggleButtonGroupControlled.displayName = "ToggleButtonGroupControlled";
44
67
  export default ToggleButtonGroupControlled;
@@ -19,3 +19,4 @@ export declare const multiple: StoryObj<{
19
19
  handleUncontrolledChange: Mock;
20
20
  handleControlledChange: Mock;
21
21
  }>;
22
+ export declare const className: StoryObj;
@@ -113,3 +113,17 @@ export const multiple = {
113
113
  });
114
114
  },
115
115
  };
116
+ export const className = {
117
+ name: "className, attributes",
118
+ render: () => (<div data-testid="root">
119
+ <ToggleButtonGroup className="test-classname" attributes={{ id: "test-id" }}>
120
+ <ToggleButton>Button 1</ToggleButton>
121
+ <ToggleButton>Button 1</ToggleButton>
122
+ </ToggleButtonGroup>
123
+ </div>),
124
+ play: async ({ canvas }) => {
125
+ const root = canvas.getByTestId("root").firstChild;
126
+ expect(root).toHaveClass("test-classname");
127
+ expect(root).toHaveAttribute("id", "test-id");
128
+ },
129
+ };
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ export type { AvatarProps } from "./components/Avatar";
12
12
  export { default as Badge } from "./components/Badge";
13
13
  export type { BadgeProps } from "./components/Badge";
14
14
  export { default as Button } from "./components/Button";
15
- export type { ButtonProps, ButtonAlignerProps } from "./components/Button";
15
+ export type { ButtonProps, ButtonAlignerProps, ButtonGroupProps } from "./components/Button";
16
16
  export { default as Breadcrumbs } from "./components/Breadcrumbs";
17
17
  export type { BreadcrumbsProps } from "./components/Breadcrumbs";
18
18
  export { default as Calendar } from "./components/Calendar";
@@ -1,4 +1,4 @@
1
- export { focusableSelector, getActiveElement, focusNextElement, focusPreviousElement, focusFirstElement, focusLastElement, } from "./focus";
1
+ export { focusableSelector, getActiveElement, getFocusableElements, focusNextElement, focusPreviousElement, focusFirstElement, focusLastElement, } from "./focus";
2
2
  export { activateKeyboardMode, deactivateKeyboardMode, checkKeyboardMode } from "./keyboardMode";
3
3
  export { default as TrapFocus } from "./TrapFocus";
4
4
  export type { TrapMode, FocusableElement } from "./types";
@@ -1,3 +1,3 @@
1
- export { focusableSelector, getActiveElement, focusNextElement, focusPreviousElement, focusFirstElement, focusLastElement, } from "./focus.js";
1
+ export { focusableSelector, getActiveElement, getFocusableElements, focusNextElement, focusPreviousElement, focusFirstElement, focusLastElement, } from "./focus.js";
2
2
  export { activateKeyboardMode, deactivateKeyboardMode, checkKeyboardMode } from "./keyboardMode.js";
3
3
  export { default as TrapFocus } from "./TrapFocus.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "reshaped",
3
3
  "description": "Professionally crafted design system in React & Figma for building products of any scale and complexity",
4
- "version": "3.6.0-canary.8",
4
+ "version": "3.6.0",
5
5
  "license": "MIT",
6
6
  "email": "hello@reshaped.so",
7
7
  "homepage": "https://reshaped.so",