@frontify/fondue 12.0.0-beta.219 → 12.0.0-beta.220
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/SegmentedControls/SegmentedControls.es.js +70 -58
- package/dist/components/SegmentedControls/SegmentedControls.es.js.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { useMemoizedId as
|
|
2
|
-
import { useFocusRing as
|
|
1
|
+
import { useMemoizedId as $ } from "../../hooks/useMemoizedId.es.js";
|
|
2
|
+
import { useFocusRing as k } from "@react-aria/focus";
|
|
3
3
|
import { setInteractionModality as G } from "@react-aria/interactions";
|
|
4
4
|
import { useRadio as V, useRadioGroup as F } from "@react-aria/radio";
|
|
5
5
|
import { VisuallyHidden as L } from "@react-aria/visually-hidden";
|
|
6
6
|
import { useRadioGroupState as M } from "@react-stately/radio";
|
|
7
7
|
import { FOCUS_STYLE as P } from "../../utilities/focusStyle.es.js";
|
|
8
|
-
import { merge as
|
|
8
|
+
import { merge as x } from "../../utilities/merge.es.js";
|
|
9
9
|
import { motion as T } from "framer-motion";
|
|
10
|
-
import t, { forwardRef as j, useRef as
|
|
11
|
-
const
|
|
12
|
-
const { id:
|
|
10
|
+
import t, { forwardRef as j, useRef as S, useMemo as D } from "react";
|
|
11
|
+
const g = (c) => c.icon !== void 0, E = j((c, l) => {
|
|
12
|
+
const { id: u, item: e, disabled: a, radioGroupState: o } = c, n = S(null), i = e.id === o.selectedValue, { inputProps: f } = V(
|
|
13
13
|
{
|
|
14
14
|
value: e.id,
|
|
15
|
-
"aria-label":
|
|
15
|
+
"aria-label": g(e) ? e.ariaLabel : e.value.toString(),
|
|
16
16
|
isDisabled: a,
|
|
17
|
-
id: i ?
|
|
17
|
+
id: i ? u : void 0
|
|
18
18
|
},
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
), { isFocusVisible:
|
|
19
|
+
o,
|
|
20
|
+
n
|
|
21
|
+
), { isFocusVisible: d, focusProps: b } = k(), w = () => {
|
|
22
22
|
var r;
|
|
23
|
-
a || (
|
|
23
|
+
a || (o.setSelectedValue(e.id), (r = n.current) == null || r.focus(), G("pointer"));
|
|
24
24
|
}, v = () => {
|
|
25
25
|
switch (!0) {
|
|
26
|
-
case
|
|
26
|
+
case g(e):
|
|
27
27
|
return "fondue-segmented-controls-item-icon";
|
|
28
28
|
case typeof e.value == "string":
|
|
29
29
|
return "fondue-segmented-controls-item-text";
|
|
@@ -31,80 +31,92 @@ const b = (d) => d.icon !== void 0, E = j((d, s) => {
|
|
|
31
31
|
return "fondue-segmented-controls-item-number";
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
-
return /* @__PURE__ */ t.createElement(
|
|
34
|
+
return /* @__PURE__ */ t.createElement(
|
|
35
35
|
"div",
|
|
36
36
|
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"tw-
|
|
42
|
-
|
|
43
|
-
a ? "" : "hover:tw-text-black hover:tw-cursor-pointer"
|
|
37
|
+
key: e.id,
|
|
38
|
+
ref: l,
|
|
39
|
+
className: x([
|
|
40
|
+
"tw-relative",
|
|
41
|
+
"after:tw-content-[''] after:tw-border-r after:tw-border-solid after:tw-border-line-strong after:tw-absolute after:tw-right-0 after:tw-h-full last:after:tw-hidden",
|
|
42
|
+
d && P
|
|
44
43
|
])
|
|
45
44
|
},
|
|
46
|
-
/* @__PURE__ */ t.createElement(
|
|
47
|
-
"
|
|
45
|
+
/* @__PURE__ */ t.createElement(
|
|
46
|
+
"div",
|
|
48
47
|
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"data-test-id":
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
role: "none",
|
|
49
|
+
onClick: w,
|
|
50
|
+
"data-test-id": v(),
|
|
51
|
+
className: x([
|
|
52
|
+
"tw-relative tw-w-full tw-px-4 tw-py-2 tw-inline-flex tw-justify-center tw-items-center tw-font-sans tw-font-normal tw-h-full tw-text-center",
|
|
53
|
+
i && !a ? "tw-text-text" : "tw-text-text-weak",
|
|
54
|
+
a ? "tw-text-box-disabled-inverse hover:tw-cursor-not-allowed" : "hover:tw-text-text hover:tw-cursor-pointer"
|
|
55
|
+
])
|
|
56
|
+
},
|
|
57
|
+
/* @__PURE__ */ t.createElement(L, null, /* @__PURE__ */ t.createElement(
|
|
58
|
+
"input",
|
|
59
|
+
{
|
|
60
|
+
...f,
|
|
61
|
+
...b,
|
|
62
|
+
"data-test-id": "fondue-segmented-controls-input",
|
|
63
|
+
ref: n
|
|
64
|
+
}
|
|
65
|
+
)),
|
|
66
|
+
/* @__PURE__ */ t.createElement("span", { className: "tw-overflow-hidden tw-text-ellipsis tw-whitespace-nowrap tw-flex" }, g(e) && /* @__PURE__ */ t.createElement("span", { className: "tw-leading-3", "aria-label": e.ariaLabel }, e.icon), e.value && /* @__PURE__ */ t.createElement("span", { className: g(e) ? "tw-ml-2" : "" }, e.value.toString()))
|
|
67
|
+
)
|
|
68
|
+
);
|
|
57
69
|
});
|
|
58
70
|
E.displayName = "SegmentedControlsItem";
|
|
59
71
|
const X = ({
|
|
60
|
-
id:
|
|
61
|
-
items:
|
|
62
|
-
activeItemId:
|
|
72
|
+
id: c,
|
|
73
|
+
items: l,
|
|
74
|
+
activeItemId: u,
|
|
63
75
|
onChange: e,
|
|
64
76
|
ariaLabel: a = "SegmentedControls",
|
|
65
|
-
disabled:
|
|
66
|
-
hugWidth:
|
|
77
|
+
disabled: o = !1,
|
|
78
|
+
hugWidth: n = !1
|
|
67
79
|
}) => {
|
|
68
|
-
var
|
|
69
|
-
const i =
|
|
80
|
+
var h;
|
|
81
|
+
const i = $(c), f = { onChange: e, value: u, label: a, isDisabled: o }, d = M(f), { radioGroupProps: b } = F(f, d), w = S([]), v = D(() => l.map((s, p) => /* @__PURE__ */ t.createElement(
|
|
70
82
|
E,
|
|
71
83
|
{
|
|
72
84
|
id: i,
|
|
73
|
-
item:
|
|
74
|
-
disabled:
|
|
75
|
-
radioGroupState:
|
|
76
|
-
ref: (
|
|
77
|
-
key: `fondue-segmented-controls-${i}-item-${
|
|
85
|
+
item: s,
|
|
86
|
+
disabled: o,
|
|
87
|
+
radioGroupState: d,
|
|
88
|
+
ref: (m) => w.current[p] = m,
|
|
89
|
+
key: `fondue-segmented-controls-${i}-item-${s.id}`
|
|
78
90
|
}
|
|
79
|
-
)), [
|
|
91
|
+
)), [l, i, o, d]), r = l.findIndex((s) => s.id === d.selectedValue), y = n ? "" : "tw-w-full", N = n ? "tw-flex" : "tw-grid tw-grid-flow-col tw-auto-cols-fr tw-justify-evenly", C = () => {
|
|
80
92
|
var p;
|
|
81
|
-
let
|
|
82
|
-
for (let
|
|
83
|
-
|
|
84
|
-
return `${
|
|
85
|
-
},
|
|
93
|
+
let s = -1;
|
|
94
|
+
for (let m = 0; m < r; m++)
|
|
95
|
+
s += ((p = w.current[m]) == null ? void 0 : p.clientWidth) || 0;
|
|
96
|
+
return `${s}px`;
|
|
97
|
+
}, R = n ? `${(h = w.current[r]) == null ? void 0 : h.clientWidth}px` : `${100 / l.length}%`, I = n ? C() : `calc(${100 * r}% - ${2 * r}px)`;
|
|
86
98
|
return /* @__PURE__ */ t.createElement("div", { className: "tw-flex" }, /* @__PURE__ */ t.createElement(
|
|
87
99
|
"fieldset",
|
|
88
100
|
{
|
|
89
|
-
...
|
|
101
|
+
...b,
|
|
90
102
|
"data-test-id": "fondue-segmented-controls",
|
|
91
|
-
className:
|
|
92
|
-
"tw-relative tw-h-9 tw-p-0 tw-border tw-border-
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
className: x([
|
|
104
|
+
"tw-relative tw-h-9 tw-p-0 tw-border tw-border-solid tw-border-line-strong tw-m-0 tw-bg-base-alt tw-rounded tw-font-sans tw-text-s tw-select-none",
|
|
105
|
+
y,
|
|
106
|
+
N
|
|
95
107
|
])
|
|
96
108
|
},
|
|
97
109
|
/* @__PURE__ */ t.createElement(
|
|
98
110
|
T.div,
|
|
99
111
|
{
|
|
100
112
|
"aria-hidden": "true",
|
|
101
|
-
animate: { x:
|
|
113
|
+
animate: { x: I, width: R },
|
|
102
114
|
initial: !1,
|
|
103
115
|
transition: { type: "tween", duration: 0.3 },
|
|
104
|
-
hidden: !
|
|
105
|
-
className:
|
|
116
|
+
hidden: !u,
|
|
117
|
+
className: x([
|
|
106
118
|
"tw-absolute tw--inset-px tw-h-full tw-box-content tw-border tw-rounded tw-pointer-events-none",
|
|
107
|
-
|
|
119
|
+
o ? "tw-border-line-x-strong tw-bg-box-disabled" : "tw-border-line-xx-strong tw-bg-base"
|
|
108
120
|
])
|
|
109
121
|
}
|
|
110
122
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SegmentedControls.es.js","sources":["../../../src/components/SegmentedControls/SegmentedControls.tsx"],"sourcesContent":["/* (c) Copyright Frontify Ltd., all rights reserved. */\n\nimport { IconProps } from '@foundation/Icon/IconProps';\nimport { useMemoizedId } from '@hooks/useMemoizedId';\nimport { useFocusRing } from '@react-aria/focus';\nimport { setInteractionModality } from '@react-aria/interactions';\nimport { useRadio, useRadioGroup } from '@react-aria/radio';\nimport { VisuallyHidden } from '@react-aria/visually-hidden';\nimport { RadioGroupState, useRadioGroupState } from '@react-stately/radio';\nimport { FOCUS_STYLE } from '@utilities/focusStyle';\nimport { merge } from '@utilities/merge';\nimport { motion } from 'framer-motion';\nimport React, { ReactElement, forwardRef, useMemo, useRef } from 'react';\n\nexport type IconItem = {\n id: string;\n icon: ReactElement<IconProps>;\n value?: string;\n ariaLabel: string;\n};\n\nexport type TextOrNumberItem = {\n id: string;\n value: string | number;\n};\n\nexport type SegmentedControlsProps = {\n id?: string;\n items: (TextOrNumberItem | IconItem)[];\n activeItemId?: string;\n onChange: (id: string) => void;\n ariaLabel?: string;\n disabled?: boolean;\n hugWidth?: boolean;\n};\n\nconst isIconItem = (item: TextOrNumberItem | IconItem): item is IconItem => (item as IconItem).icon !== undefined;\n\ninterface SegmentedControlsItemProps {\n id: string;\n item: TextOrNumberItem | IconItem;\n disabled: boolean;\n radioGroupState: RadioGroupState;\n}\n\nconst SegmentedControlsItem = forwardRef<HTMLDivElement, SegmentedControlsItemProps>((props, ref) => {\n const { id, item, disabled, radioGroupState } = props;\n const inputRef = useRef<HTMLInputElement | null>(null);\n const isActive = item.id === radioGroupState.selectedValue;\n const { inputProps } = useRadio(\n {\n value: item.id,\n 'aria-label': isIconItem(item) ? item.ariaLabel : item.value.toString(),\n isDisabled: disabled,\n id: isActive ? id : undefined,\n },\n radioGroupState,\n inputRef,\n );\n const { isFocusVisible, focusProps } = useFocusRing();\n\n const handleMockLabelClick = () => {\n if (!disabled) {\n radioGroupState.setSelectedValue(item.id);\n inputRef.current?.focus();\n setInteractionModality('pointer');\n }\n };\n\n const getSegmentedControlsItemTestId = () => {\n switch (true) {\n case isIconItem(item):\n return 'fondue-segmented-controls-item-icon';\n case typeof item.value === 'string':\n return 'fondue-segmented-controls-item-text';\n default:\n return 'fondue-segmented-controls-item-number';\n }\n };\n\n return (\n <div key={item.id} ref={ref} className={merge(['tw-relative', isFocusVisible && FOCUS_STYLE])}>\n <div\n // TODO: Change element back to label when bug #2380 from @react-aria is fixed\n // https://github.com/adobe/react-spectrum/issues/2380\n role=\"none\"\n onClick={handleMockLabelClick}\n data-test-id={getSegmentedControlsItemTestId()}\n className={merge([\n 'tw-relative tw-w-full tw-px-4 tw-py-2 tw-inline-flex tw-justify-center tw-items-center tw-font-sans tw-font-normal tw-h-full tw-text-center',\n isActive && !disabled ? 'tw-text-black' : 'tw-text-black-80',\n !disabled ? 'hover:tw-text-black hover:tw-cursor-pointer' : '',\n ])}\n >\n <VisuallyHidden>\n <input\n {...inputProps}\n {...focusProps}\n data-test-id=\"fondue-segmented-controls-input\"\n ref={inputRef}\n />\n </VisuallyHidden>\n <span className=\"tw-overflow-hidden tw-text-ellipsis tw-whitespace-nowrap tw-flex\">\n {isIconItem(item) && (\n <span className=\"tw-leading-3\" aria-label={item.ariaLabel}>\n {item.icon}\n </span>\n )}\n {item.value && <span className={isIconItem(item) ? 'tw-ml-2' : ''}>{item.value.toString()}</span>}\n </span>\n </div>\n </div>\n );\n});\n\nSegmentedControlsItem.displayName = 'SegmentedControlsItem';\n\nexport const SegmentedControls = ({\n id: propId,\n items,\n activeItemId,\n onChange,\n ariaLabel = 'SegmentedControls',\n disabled = false,\n hugWidth = false,\n}: SegmentedControlsProps): ReactElement => {\n const id = useMemoizedId(propId);\n const groupProps = { onChange, value: activeItemId, label: ariaLabel, isDisabled: disabled };\n const radioGroupState = useRadioGroupState(groupProps);\n const { radioGroupProps } = useRadioGroup(groupProps, radioGroupState);\n const itemsRef = useRef<(HTMLDivElement | null)[]>([]);\n const itemElements = useMemo(() => {\n return items.map((item, index) => (\n <SegmentedControlsItem\n id={id}\n item={item}\n disabled={disabled}\n radioGroupState={radioGroupState}\n ref={(el) => (itemsRef.current[index] = el)}\n key={`fondue-segmented-controls-${id}-item-${item.id}`}\n />\n ));\n }, [items, id, disabled, radioGroupState]);\n const selectedIndex = items.findIndex((item) => item.id === radioGroupState.selectedValue);\n const width = hugWidth ? '' : 'tw-w-full';\n const alignment = hugWidth ? 'tw-flex' : 'tw-grid tw-grid-flow-col tw-auto-cols-fr tw-justify-evenly';\n\n const getSliderX = () => {\n let translateX = selectedIndex ? -1 : 0;\n for (let i = 0; i < selectedIndex; i++) {\n translateX += itemsRef.current[i]?.clientWidth || 0;\n }\n return `${translateX}px`;\n };\n\n const sliderWidth = hugWidth ? `${itemsRef.current[selectedIndex]?.clientWidth}px` : `${100 / items.length}%`;\n const sliderTranslation = hugWidth ? getSliderX() : `calc(${100 * selectedIndex}% - ${2 * selectedIndex}px)`;\n\n return (\n <div className=\"tw-flex\">\n <fieldset\n {...radioGroupProps}\n data-test-id=\"fondue-segmented-controls\"\n className={merge([\n 'tw-relative tw-h-9 tw-p-0 tw-border tw-border-black-20 tw-m-0 tw-bg-black-0 tw-rounded tw-font-sans tw-text-s tw-select-none',\n width,\n alignment,\n ])}\n >\n <motion.div\n aria-hidden=\"true\"\n // div border is not included in width so it must be subtracted from translation.\n animate={{ x: sliderTranslation, width: sliderWidth }}\n initial={false}\n transition={{ type: 'tween', duration: 0.3 }}\n hidden={!activeItemId}\n className={merge([\n 'tw-absolute tw--inset-px tw-h-full tw-box-content tw-border tw-rounded tw-pointer-events-none',\n disabled\n ? 'tw-border-line-x-strong tw-border-opacity-30 tw-bg-black-0'\n : 'tw-border-black tw-bg-white',\n ])}\n />\n {itemElements}\n </fieldset>\n </div>\n );\n};\nSegmentedControls.displayName = 'FondueSegmentedControls';\n"],"names":["isIconItem","item","SegmentedControlsItem","forwardRef","props","ref","id","disabled","radioGroupState","inputRef","useRef","isActive","inputProps","useRadio","isFocusVisible","focusProps","useFocusRing","handleMockLabelClick","_a","setInteractionModality","getSegmentedControlsItemTestId","React","merge","FOCUS_STYLE","VisuallyHidden","SegmentedControls","propId","items","activeItemId","onChange","ariaLabel","hugWidth","useMemoizedId","groupProps","useRadioGroupState","radioGroupProps","useRadioGroup","itemsRef","itemElements","useMemo","index","el","selectedIndex","width","alignment","getSliderX","translateX","i","sliderWidth","sliderTranslation","motion"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SegmentedControls.es.js","sources":["../../../src/components/SegmentedControls/SegmentedControls.tsx"],"sourcesContent":["/* (c) Copyright Frontify Ltd., all rights reserved. */\n\nimport { IconProps } from '@foundation/Icon/IconProps';\nimport { useMemoizedId } from '@hooks/useMemoizedId';\nimport { useFocusRing } from '@react-aria/focus';\nimport { setInteractionModality } from '@react-aria/interactions';\nimport { useRadio, useRadioGroup } from '@react-aria/radio';\nimport { VisuallyHidden } from '@react-aria/visually-hidden';\nimport { RadioGroupState, useRadioGroupState } from '@react-stately/radio';\nimport { FOCUS_STYLE } from '@utilities/focusStyle';\nimport { merge } from '@utilities/merge';\nimport { motion } from 'framer-motion';\nimport React, { ReactElement, forwardRef, useMemo, useRef } from 'react';\n\nexport type IconItem = {\n id: string;\n icon: ReactElement<IconProps>;\n value?: string;\n ariaLabel: string;\n};\n\nexport type TextOrNumberItem = {\n id: string;\n value: string | number;\n};\n\nexport type SegmentedControlsProps = {\n id?: string;\n items: (TextOrNumberItem | IconItem)[];\n activeItemId?: string;\n onChange: (id: string) => void;\n ariaLabel?: string;\n disabled?: boolean;\n hugWidth?: boolean;\n};\n\nconst isIconItem = (item: TextOrNumberItem | IconItem): item is IconItem => (item as IconItem).icon !== undefined;\n\ninterface SegmentedControlsItemProps {\n id: string;\n item: TextOrNumberItem | IconItem;\n disabled: boolean;\n radioGroupState: RadioGroupState;\n}\n\nconst SegmentedControlsItem = forwardRef<HTMLDivElement, SegmentedControlsItemProps>((props, ref) => {\n const { id, item, disabled, radioGroupState } = props;\n const inputRef = useRef<HTMLInputElement | null>(null);\n const isActive = item.id === radioGroupState.selectedValue;\n const { inputProps } = useRadio(\n {\n value: item.id,\n 'aria-label': isIconItem(item) ? item.ariaLabel : item.value.toString(),\n isDisabled: disabled,\n id: isActive ? id : undefined,\n },\n radioGroupState,\n inputRef,\n );\n const { isFocusVisible, focusProps } = useFocusRing();\n\n const handleMockLabelClick = () => {\n if (!disabled) {\n radioGroupState.setSelectedValue(item.id);\n inputRef.current?.focus();\n setInteractionModality('pointer');\n }\n };\n\n const getSegmentedControlsItemTestId = () => {\n switch (true) {\n case isIconItem(item):\n return 'fondue-segmented-controls-item-icon';\n case typeof item.value === 'string':\n return 'fondue-segmented-controls-item-text';\n default:\n return 'fondue-segmented-controls-item-number';\n }\n };\n\n return (\n <div\n key={item.id}\n ref={ref}\n className={merge([\n 'tw-relative',\n \"after:tw-content-[''] after:tw-border-r after:tw-border-solid after:tw-border-line-strong after:tw-absolute after:tw-right-0 after:tw-h-full last:after:tw-hidden\",\n isFocusVisible && FOCUS_STYLE,\n ])}\n >\n <div\n // TODO: Change element back to label when bug #2380 from @react-aria is fixed\n // https://github.com/adobe/react-spectrum/issues/2380\n role=\"none\"\n onClick={handleMockLabelClick}\n data-test-id={getSegmentedControlsItemTestId()}\n className={merge([\n 'tw-relative tw-w-full tw-px-4 tw-py-2 tw-inline-flex tw-justify-center tw-items-center tw-font-sans tw-font-normal tw-h-full tw-text-center',\n isActive && !disabled ? 'tw-text-text' : 'tw-text-text-weak',\n !disabled\n ? 'hover:tw-text-text hover:tw-cursor-pointer'\n : 'tw-text-box-disabled-inverse hover:tw-cursor-not-allowed',\n ])}\n >\n <VisuallyHidden>\n <input\n {...inputProps}\n {...focusProps}\n data-test-id=\"fondue-segmented-controls-input\"\n ref={inputRef}\n />\n </VisuallyHidden>\n <span className=\"tw-overflow-hidden tw-text-ellipsis tw-whitespace-nowrap tw-flex\">\n {isIconItem(item) && (\n <span className=\"tw-leading-3\" aria-label={item.ariaLabel}>\n {item.icon}\n </span>\n )}\n {item.value && <span className={isIconItem(item) ? 'tw-ml-2' : ''}>{item.value.toString()}</span>}\n </span>\n </div>\n </div>\n );\n});\n\nSegmentedControlsItem.displayName = 'SegmentedControlsItem';\n\nexport const SegmentedControls = ({\n id: propId,\n items,\n activeItemId,\n onChange,\n ariaLabel = 'SegmentedControls',\n disabled = false,\n hugWidth = false,\n}: SegmentedControlsProps): ReactElement => {\n const id = useMemoizedId(propId);\n const groupProps = { onChange, value: activeItemId, label: ariaLabel, isDisabled: disabled };\n const radioGroupState = useRadioGroupState(groupProps);\n const { radioGroupProps } = useRadioGroup(groupProps, radioGroupState);\n const itemsRef = useRef<(HTMLDivElement | null)[]>([]);\n const itemElements = useMemo(() => {\n return items.map((item, index) => (\n <SegmentedControlsItem\n id={id}\n item={item}\n disabled={disabled}\n radioGroupState={radioGroupState}\n ref={(el) => (itemsRef.current[index] = el)}\n key={`fondue-segmented-controls-${id}-item-${item.id}`}\n />\n ));\n }, [items, id, disabled, radioGroupState]);\n const selectedIndex = items.findIndex((item) => item.id === radioGroupState.selectedValue);\n const width = hugWidth ? '' : 'tw-w-full';\n const alignment = hugWidth ? 'tw-flex' : 'tw-grid tw-grid-flow-col tw-auto-cols-fr tw-justify-evenly';\n\n const getSliderX = () => {\n let translateX = -1;\n for (let i = 0; i < selectedIndex; i++) {\n translateX += itemsRef.current[i]?.clientWidth || 0;\n }\n return `${translateX}px`;\n };\n\n const sliderWidth = hugWidth ? `${itemsRef.current[selectedIndex]?.clientWidth}px` : `${100 / items.length}%`;\n const sliderTranslation = hugWidth ? getSliderX() : `calc(${100 * selectedIndex}% - ${2 * selectedIndex}px)`;\n\n return (\n <div className=\"tw-flex\">\n <fieldset\n {...radioGroupProps}\n data-test-id=\"fondue-segmented-controls\"\n className={merge([\n 'tw-relative tw-h-9 tw-p-0 tw-border tw-border-solid tw-border-line-strong tw-m-0 tw-bg-base-alt tw-rounded tw-font-sans tw-text-s tw-select-none',\n width,\n alignment,\n ])}\n >\n <motion.div\n aria-hidden=\"true\"\n // div border is not included in width so it must be subtracted from translation.\n animate={{ x: sliderTranslation, width: sliderWidth }}\n initial={false}\n transition={{ type: 'tween', duration: 0.3 }}\n hidden={!activeItemId}\n className={merge([\n 'tw-absolute tw--inset-px tw-h-full tw-box-content tw-border tw-rounded tw-pointer-events-none',\n disabled ? 'tw-border-line-x-strong tw-bg-box-disabled' : 'tw-border-line-xx-strong tw-bg-base',\n ])}\n />\n {itemElements}\n </fieldset>\n </div>\n );\n};\nSegmentedControls.displayName = 'FondueSegmentedControls';\n"],"names":["isIconItem","item","SegmentedControlsItem","forwardRef","props","ref","id","disabled","radioGroupState","inputRef","useRef","isActive","inputProps","useRadio","isFocusVisible","focusProps","useFocusRing","handleMockLabelClick","_a","setInteractionModality","getSegmentedControlsItemTestId","React","merge","FOCUS_STYLE","VisuallyHidden","SegmentedControls","propId","items","activeItemId","onChange","ariaLabel","hugWidth","useMemoizedId","groupProps","useRadioGroupState","radioGroupProps","useRadioGroup","itemsRef","itemElements","useMemo","index","el","selectedIndex","width","alignment","getSliderX","translateX","i","sliderWidth","sliderTranslation","motion"],"mappings":"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"}
|