@sproutsocial/seeds-react-segmented-control 1.0.13 → 1.1.1

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.
@@ -3,19 +3,19 @@ $ tsup --dts
3
3
  CLI Building entry: src/index.ts
4
4
  CLI Using tsconfig: tsconfig.json
5
5
  CLI tsup v8.5.0
6
- CLI Using tsup config: /home/runner/work/seeds/seeds/seeds-react/seeds-react-segmented-control/tsup.config.ts
6
+ CLI Using tsup config: /home/runner/_work/seeds/seeds/seeds-react/seeds-react-segmented-control/tsup.config.ts
7
7
  CLI Target: es2022
8
8
  CLI Cleaning output folder
9
9
  CJS Build start
10
10
  ESM Build start
11
- ESM dist/esm/index.js 4.47 KB
12
- ESM dist/esm/index.js.map 8.81 KB
13
- ESM ⚡️ Build success in 218ms
14
- CJS dist/index.js 6.73 KB
15
- CJS dist/index.js.map 8.90 KB
16
- CJS ⚡️ Build success in 254ms
11
+ CJS dist/index.js 7.22 KB
12
+ CJS dist/index.js.map 10.70 KB
13
+ CJS ⚡️ Build success in 73ms
14
+ ESM dist/esm/index.js 5.06 KB
15
+ ESM dist/esm/index.js.map 10.62 KB
16
+ ESM ⚡️ Build success in 74ms
17
17
  DTS Build start
18
- DTS ⚡️ Build success in 30853ms
19
- DTS dist/index.d.ts 1.47 KB
20
- DTS dist/index.d.mts 1.47 KB
21
- Done in 38.59s.
18
+ DTS ⚡️ Build success in 4439ms
19
+ DTS dist/index.d.ts 1.66 KB
20
+ DTS dist/index.d.mts 1.66 KB
21
+ Done in 5.94s.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @sproutsocial/seeds-react-segmented-control
2
2
 
3
+ ## 1.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 598356f: Correct SegmentedControl to always have a value
8
+
9
+ ## 1.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 1f356b4: Correct SegmentedControll accessibility, switch to radix, introduce simpler onValueChange callback
14
+
15
+ ## 1.0.15
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [17d4f12]
20
+ - @sproutsocial/seeds-react-theme@3.6.0
21
+ - @sproutsocial/seeds-react-box@1.1.14
22
+ - @sproutsocial/seeds-react-mixins@4.3.1
23
+
24
+ ## 1.0.14
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies [5bb63e1]
29
+ - @sproutsocial/seeds-react-mixins@4.3.0
30
+
3
31
  ## 1.0.13
4
32
 
5
33
  ### Patch Changes
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/SegmentedControl.tsx
2
2
  import * as React from "react";
3
- import { useState, useContext } from "react";
4
- import Box2 from "@sproutsocial/seeds-react-box";
3
+ import { useState } from "react";
4
+ import * as ToggleGroup from "@radix-ui/react-toggle-group";
5
5
 
6
6
  // src/styles.ts
7
7
  import styled, { css } from "styled-components";
@@ -11,18 +11,42 @@ import {
11
11
  disabled
12
12
  } from "@sproutsocial/seeds-react-mixins";
13
13
  import Box from "@sproutsocial/seeds-react-box";
14
- import Text from "@sproutsocial/seeds-react-text";
14
+ import Button from "@sproutsocial/seeds-react-button";
15
15
  var SegmentedControlContainer = styled(Box)`
16
16
  border: 1px solid
17
17
  ${(props) => props.theme.colors.button.secondary.border.base};
18
18
  border-radius: ${(props) => props.theme.radii.outer};
19
19
  padding: ${(props) => props.theme.space[100]};
20
+ display: flex;
20
21
 
21
- legend {
22
- ${visuallyHidden}
22
+ ${(props) => props.disabled && disabled}
23
+ `;
24
+ var ToggleButton = styled(Button)`
25
+ /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */
26
+ padding: calc(${(props) => props.theme.space[350]} - 6px);
27
+ flex: 1 1 auto;
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ text-align: center;
32
+ border: 0;
33
+
34
+ & + & {
35
+ margin-left: ${(props) => props.theme.space[100]};
23
36
  }
24
37
 
25
- ${(props) => props.disabled && disabled}
38
+ &:hover {
39
+ background-color: ${(props) => props.theme.colors.listItem.background.hover};
40
+ }
41
+
42
+ &[data-state="on"] {
43
+ color: ${(props) => props.theme.colors.text.inverse};
44
+ background-color: ${(props) => props.theme.colors.listItem.background.selected};
45
+
46
+ &:hover {
47
+ background-color: ${(props) => props.theme.colors.listItem.background.selected};
48
+ }
49
+ }
26
50
  `;
27
51
  var SegmentedControlItemContainer = styled(Box)`
28
52
  flex: 1 1 auto;
@@ -43,7 +67,7 @@ var SegmentedControlItemContainer = styled(Box)`
43
67
 
44
68
  ${(props) => props.disabled && disabled}
45
69
  `;
46
- var SegmentedControlLabel = styled(Text)`
70
+ var SegmentedControlLabel = styled(Button)`
47
71
  flex: 1 1 auto;
48
72
  display: flex;
49
73
  align-items: center;
@@ -79,9 +103,8 @@ var SegmentedControlLabel = styled(Text)`
79
103
  `;
80
104
 
81
105
  // src/SegmentedControl.tsx
82
- import { jsx, jsxs } from "react/jsx-runtime";
106
+ import { jsx } from "react/jsx-runtime";
83
107
  var nameCounter = 0;
84
- var idCounter = 0;
85
108
  var SegmentedControlContext = React.createContext(null);
86
109
  var SegmentedControlItem = ({
87
110
  value,
@@ -89,36 +112,22 @@ var SegmentedControlItem = ({
89
112
  disabled: disabled2,
90
113
  ...rest
91
114
  }) => {
92
- const context = useContext(SegmentedControlContext);
93
- if (!context) {
94
- return null;
95
- }
96
- const { name, selectedValue, onChange } = context;
97
- const [id] = useState(`${name}-${idCounter++}`);
98
- const isChecked = value === selectedValue;
99
- return /* @__PURE__ */ jsxs(
100
- SegmentedControlItemContainer,
115
+ return /* @__PURE__ */ jsx(
116
+ ToggleGroup.Item,
101
117
  {
102
- "data-segmentedcontrol-isactive": isChecked,
103
- "data-qa-segmentedcontrol-item": value,
104
- "data-qa-segmentedcontrol-ischecked": isChecked === true,
118
+ value,
119
+ "aria-label": "Left aligned",
105
120
  disabled: disabled2,
106
- ...rest,
107
- children: [
108
- /* @__PURE__ */ jsx(
109
- "input",
110
- {
111
- type: "radio",
112
- id,
113
- name,
114
- value,
115
- checked: isChecked,
116
- onChange,
117
- disabled: disabled2
118
- }
119
- ),
120
- /* @__PURE__ */ jsx(SegmentedControlLabel, { as: "label", htmlFor: id, isActive: isChecked, children })
121
- ]
121
+ asChild: true,
122
+ children: /* @__PURE__ */ jsx(
123
+ ToggleButton,
124
+ {
125
+ "data-qa-segmentedcontrol-item": value,
126
+ disabled: disabled2,
127
+ ...rest,
128
+ children
129
+ }
130
+ )
122
131
  }
123
132
  );
124
133
  };
@@ -126,29 +135,41 @@ var SegmentedControl = ({
126
135
  selectedValue,
127
136
  label,
128
137
  onChange,
138
+ onValueChange,
129
139
  children,
140
+ disabled: disabled2,
130
141
  ...rest
131
142
  }) => {
132
143
  const [name] = useState(`Racine-segmented-control-${nameCounter++}`);
133
144
  return /* @__PURE__ */ jsx(
134
- SegmentedControlContext.Provider,
145
+ ToggleGroup.Root,
135
146
  {
136
- value: {
137
- name,
138
- selectedValue,
139
- onChange
147
+ className: "inline-flex space-x-px rounded bg-mauve6 shadow-[0_2px_10px] shadow-blackA4",
148
+ type: "single",
149
+ value: selectedValue,
150
+ "aria-label": label,
151
+ disabled: disabled2,
152
+ onValueChange: (e) => {
153
+ if (!e) return;
154
+ onValueChange?.(e);
155
+ const mockEvent = new Event("change", {
156
+ bubbles: true
157
+ });
158
+ Object.defineProperty(mockEvent, "target", { value: { value: e } });
159
+ Object.defineProperty(mockEvent, "currentTarget", {
160
+ value: { value: e }
161
+ });
162
+ onChange?.(mockEvent);
140
163
  },
141
- children: /* @__PURE__ */ jsxs(
164
+ asChild: true,
165
+ children: /* @__PURE__ */ jsx(
142
166
  SegmentedControlContainer,
143
167
  {
144
- forwardedAs: "fieldset",
145
168
  "data-qa-segmentedcontrol": label,
146
169
  "data-qa-segmentedcontrol-value": selectedValue,
170
+ disabled: disabled2,
147
171
  ...rest,
148
- children: [
149
- /* @__PURE__ */ jsx("legend", { children: label }),
150
- /* @__PURE__ */ jsx(Box2, { display: "flex", children })
151
- ]
172
+ children
152
173
  }
153
174
  )
154
175
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/SegmentedControl.tsx","../../src/styles.ts","../../src/SegmentedControlTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport { useState, useContext } from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport {\n SegmentedControlContainer,\n SegmentedControlItemContainer,\n SegmentedControlLabel,\n} from \"./styles\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nlet nameCounter = 0;\nlet idCounter = 0;\n\ninterface TypeSegmentedControlContext {\n name: string;\n selectedValue: string;\n onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n}\n\nconst SegmentedControlContext = React.createContext<\n TypeSegmentedControlContext | null | undefined\n>(null);\n\nconst SegmentedControlItem = ({\n value,\n children,\n disabled,\n ...rest\n}: TypeSegmentedControlItemProps) => {\n const context = useContext(SegmentedControlContext);\n\n if (!context) {\n return null;\n }\n\n const { name, selectedValue, onChange } = context;\n\n /* eslint-disable-next-line */\n const [id] = useState(`${name}-${idCounter++}`);\n const isChecked = value === selectedValue;\n\n return (\n <SegmentedControlItemContainer\n data-segmentedcontrol-isactive={isChecked}\n data-qa-segmentedcontrol-item={value}\n data-qa-segmentedcontrol-ischecked={isChecked === true}\n disabled={disabled}\n {...rest}\n >\n <input\n type=\"radio\"\n id={id}\n name={name}\n value={value}\n checked={isChecked}\n onChange={onChange}\n disabled={disabled}\n />\n <SegmentedControlLabel as=\"label\" htmlFor={id} isActive={isChecked}>\n {children}\n </SegmentedControlLabel>\n </SegmentedControlItemContainer>\n );\n};\n\nconst SegmentedControl = ({\n selectedValue,\n label,\n onChange,\n children,\n ...rest\n}: TypeSegmentedControlProps) => {\n const [name] = useState(`Racine-segmented-control-${nameCounter++}`);\n return (\n <SegmentedControlContext.Provider\n value={{\n name,\n selectedValue,\n onChange,\n }}\n >\n <SegmentedControlContainer\n forwardedAs=\"fieldset\"\n data-qa-segmentedcontrol={label}\n data-qa-segmentedcontrol-value={selectedValue}\n {...rest}\n >\n <legend>{label}</legend>\n\n <Box display=\"flex\">{children}</Box>\n </SegmentedControlContainer>\n </SegmentedControlContext.Provider>\n );\n};\n\nSegmentedControlItem.displayName = \"SegmentedControl.Item\";\nSegmentedControl.Item = SegmentedControlItem;\n\nexport default SegmentedControl;\n","import styled, { css } from \"styled-components\";\nimport {\n visuallyHidden,\n focusRing,\n disabled,\n} from \"@sproutsocial/seeds-react-mixins\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nexport const SegmentedControlContainer = styled(Box)<\n Pick<TypeSegmentedControlProps, \"disabled\">\n>`\n border: 1px solid\n ${(props) => props.theme.colors.button.secondary.border.base};\n border-radius: ${(props) => props.theme.radii.outer};\n padding: ${(props) => props.theme.space[100]};\n\n legend {\n ${visuallyHidden}\n }\n\n ${(props) => props.disabled && disabled}\n`;\n\nexport const SegmentedControlItemContainer = styled(Box)<\n Pick<TypeSegmentedControlItemProps, \"disabled\">\n>`\n flex: 1 1 auto;\n display: flex;\n cursor: pointer;\n\n & + & {\n margin-left: ${(props) => props.theme.space[100]};\n }\n\n &:focus-within label {\n ${focusRing}\n }\n\n input {\n ${visuallyHidden}\n }\n\n ${(props) => props.disabled && disabled}\n`;\n\ninterface TypeSegmentedControlState {\n isActive: boolean;\n}\n\nexport const SegmentedControlLabel = styled(Text)<TypeSegmentedControlState>`\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: ${(props) => props.theme.colors.text.body};\n cursor: pointer;\n font-size: ${(props) => props.theme.typography[200].fontSize};\n /**\n\t* Matches default line height of Icon. Also matches the overall height of\n\t* Input, Select, and Button.\n\t*/\n line-height: 16px;\n font-weight: ${(props) => props.theme.fontWeights.semibold};\n\n border-radius: ${(props) => props.theme.radii.inner};\n /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */\n padding: calc(${(props) => props.theme.space[350]} - 6px);\n transition: all ${(props) => props.theme.duration.fast};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.hover};\n }\n\n ${(props) =>\n props.isActive &&\n css`\n color: ${(props) => props.theme.colors.text.inverse};\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n }\n `}\n`;\n","import type { TypeContainerProps } from \"@sproutsocial/seeds-react-box\";\nimport * as React from \"react\";\n\nexport interface TypeSegmentedControlItemProps {\n /** The value of this item. Should be unique among sibling items. */\n value: string;\n children: React.ReactNode;\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n}\n\nexport interface TypeSegmentedControlProps extends TypeContainerProps {\n /** The value of the currently selected item. Should match the value prop of one of the child items */\n selectedValue: string;\n\n /** The title of the segmented control, used for accessibility purposes */\n label: string;\n\n /** Called when the user selects a new item. You can access the value of the newly selected item using \"event.target.value\" */\n onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n children: React.ReactNode;\n}\n","import SegmentedControl from \"./SegmentedControl\";\n\nexport default SegmentedControl;\nexport { SegmentedControl };\nexport * from \"./SegmentedControlTypes\";\n"],"mappings":"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","names":["Box","props","disabled","Box"]}
1
+ {"version":3,"sources":["../../src/SegmentedControl.tsx","../../src/styles.ts","../../src/SegmentedControlTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport { useState } from \"react\";\nimport * as ToggleGroup from \"@radix-ui/react-toggle-group\";\nimport { SegmentedControlContainer, ToggleButton } from \"./styles\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nlet nameCounter = 0;\nlet idCounter = 0;\n\ninterface TypeSegmentedControlContext {\n name: string;\n selectedValue: string;\n onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n}\n\nconst SegmentedControlContext = React.createContext<\n TypeSegmentedControlContext | null | undefined\n>(null);\n\nconst SegmentedControlItem = ({\n value,\n children,\n disabled,\n ...rest\n}: TypeSegmentedControlItemProps) => {\n return (\n <ToggleGroup.Item\n value={value}\n aria-label=\"Left aligned\"\n disabled={disabled}\n asChild\n >\n <ToggleButton\n // TODO: Discuss dropping these\n // We'd need to keep context, which seems unnecessary now\n // data-segmentedcontrol-isactive={isChecked}\n data-qa-segmentedcontrol-item={value}\n // data-qa-segmentedcontrol-ischecked={isChecked === true}\n disabled={disabled}\n {...rest}\n >\n {children}\n </ToggleButton>\n </ToggleGroup.Item>\n );\n};\n\nconst SegmentedControl = ({\n selectedValue,\n label,\n onChange,\n onValueChange,\n children,\n disabled,\n ...rest\n}: TypeSegmentedControlProps) => {\n const [name] = useState(`Racine-segmented-control-${nameCounter++}`);\n return (\n <ToggleGroup.Root\n className=\"inline-flex space-x-px rounded bg-mauve6 shadow-[0_2px_10px] shadow-blackA4\"\n type=\"single\"\n value={selectedValue}\n aria-label={label}\n disabled={disabled}\n onValueChange={(e) => {\n // Radix allows deselecting a value by clicking on it when it's already selected,\n // but we don't want to allow that behavior in our SegmentedControl.\n if (!e) return;\n\n onValueChange?.(e);\n // Create a mock event to pass to onChange for backwards compatibility.\n // We want to move towards onValueChange, but onChange is used by consumers currently so we need to support both for now.\n const mockEvent = new Event(\"change\", {\n bubbles: true,\n }) as unknown as React.SyntheticEvent<HTMLInputElement>;\n Object.defineProperty(mockEvent, \"target\", { value: { value: e } });\n Object.defineProperty(mockEvent, \"currentTarget\", {\n value: { value: e },\n });\n onChange?.(mockEvent);\n }}\n asChild\n >\n <SegmentedControlContainer\n data-qa-segmentedcontrol={label}\n data-qa-segmentedcontrol-value={selectedValue}\n disabled={disabled}\n {...rest}\n >\n {children}\n </SegmentedControlContainer>\n </ToggleGroup.Root>\n );\n};\n\nSegmentedControlItem.displayName = \"SegmentedControl.Item\";\nSegmentedControl.Item = SegmentedControlItem;\n\nexport default SegmentedControl;\n","import styled, { css } from \"styled-components\";\nimport {\n visuallyHidden,\n focusRing,\n disabled,\n} from \"@sproutsocial/seeds-react-mixins\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Button from \"@sproutsocial/seeds-react-button\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nexport const SegmentedControlContainer = styled(Box)<\n Pick<TypeSegmentedControlProps, \"disabled\">\n>`\n border: 1px solid\n ${(props) => props.theme.colors.button.secondary.border.base};\n border-radius: ${(props) => props.theme.radii.outer};\n padding: ${(props) => props.theme.space[100]};\n display: flex;\n\n ${(props) => props.disabled && disabled}\n`;\n\nexport const ToggleButton = styled(Button)`\n /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */\n padding: calc(${(props) => props.theme.space[350]} - 6px);\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n border: 0;\n\n & + & {\n margin-left: ${(props) => props.theme.space[100]};\n }\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.hover};\n }\n\n &[data-state=\"on\"] {\n color: ${(props) => props.theme.colors.text.inverse};\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n }\n }\n`;\n\nexport const SegmentedControlItemContainer = styled(Box)<\n Pick<TypeSegmentedControlItemProps, \"disabled\">\n>`\n flex: 1 1 auto;\n display: flex;\n cursor: pointer;\n\n & + & {\n margin-left: ${(props) => props.theme.space[100]};\n }\n\n &:focus-within label {\n ${focusRing}\n }\n\n input {\n ${visuallyHidden}\n }\n\n ${(props) => props.disabled && disabled}\n`;\n\ninterface TypeSegmentedControlState {\n isActive: boolean;\n}\n\nexport const SegmentedControlLabel = styled(Button)<TypeSegmentedControlState>`\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: ${(props) => props.theme.colors.text.body};\n cursor: pointer;\n font-size: ${(props) => props.theme.typography[200].fontSize};\n /**\n\t* Matches default line height of Icon. Also matches the overall height of\n\t* Input, Select, and Button.\n\t*/\n line-height: 16px;\n font-weight: ${(props) => props.theme.fontWeights.semibold};\n\n border-radius: ${(props) => props.theme.radii.inner};\n /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */\n padding: calc(${(props) => props.theme.space[350]} - 6px);\n transition: all ${(props) => props.theme.duration.fast};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.hover};\n }\n\n ${(props) =>\n props.isActive &&\n css`\n color: ${(props) => props.theme.colors.text.inverse};\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n }\n `}\n`;\n","import type { TypeContainerProps } from \"@sproutsocial/seeds-react-box\";\nimport * as React from \"react\";\n\nexport interface TypeSegmentedControlItemProps {\n /** The value of this item. Should be unique among sibling items. */\n value: string;\n children: React.ReactNode;\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n}\n\nexport interface TypeSegmentedControlProps extends TypeContainerProps {\n /** The value of the currently selected item. Should match the value prop of one of the child items */\n selectedValue: string;\n\n /** The title of the segmented control, used for accessibility purposes */\n label: string;\n\n /** Called when the user selects a new item. You can access the value of the newly selected item using \"event.target.value\" */\n onChange?: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n\n /** Called when the user selects a new item. You can access the value of the newly selected item using \"event.target.value\" */\n onValueChange?: (value: string) => void;\n\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n children: React.ReactNode;\n}\n","import SegmentedControl from \"./SegmentedControl\";\n\nexport default SegmentedControl;\nexport { SegmentedControl };\nexport * from \"./SegmentedControlTypes\";\n"],"mappings":"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","names":["props","disabled"]}
package/dist/index.d.mts CHANGED
@@ -15,16 +15,18 @@ interface TypeSegmentedControlProps extends TypeContainerProps {
15
15
  /** The title of the segmented control, used for accessibility purposes */
16
16
  label: string;
17
17
  /** Called when the user selects a new item. You can access the value of the newly selected item using "event.target.value" */
18
- onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;
18
+ onChange?: (e: React.SyntheticEvent<HTMLInputElement>) => void;
19
+ /** Called when the user selects a new item. You can access the value of the newly selected item using "event.target.value" */
20
+ onValueChange?: (value: string) => void;
19
21
  /** Disables user action and applies a disabled style on the component */
20
22
  disabled?: boolean;
21
23
  children: React.ReactNode;
22
24
  }
23
25
 
24
26
  declare const SegmentedControl: {
25
- ({ selectedValue, label, onChange, children, ...rest }: TypeSegmentedControlProps): react_jsx_runtime.JSX.Element;
27
+ ({ selectedValue, label, onChange, onValueChange, children, disabled, ...rest }: TypeSegmentedControlProps): react_jsx_runtime.JSX.Element;
26
28
  Item: {
27
- ({ value, children, disabled, ...rest }: TypeSegmentedControlItemProps): react_jsx_runtime.JSX.Element | null;
29
+ ({ value, children, disabled, ...rest }: TypeSegmentedControlItemProps): react_jsx_runtime.JSX.Element;
28
30
  displayName: string;
29
31
  };
30
32
  };
package/dist/index.d.ts CHANGED
@@ -15,16 +15,18 @@ interface TypeSegmentedControlProps extends TypeContainerProps {
15
15
  /** The title of the segmented control, used for accessibility purposes */
16
16
  label: string;
17
17
  /** Called when the user selects a new item. You can access the value of the newly selected item using "event.target.value" */
18
- onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;
18
+ onChange?: (e: React.SyntheticEvent<HTMLInputElement>) => void;
19
+ /** Called when the user selects a new item. You can access the value of the newly selected item using "event.target.value" */
20
+ onValueChange?: (value: string) => void;
19
21
  /** Disables user action and applies a disabled style on the component */
20
22
  disabled?: boolean;
21
23
  children: React.ReactNode;
22
24
  }
23
25
 
24
26
  declare const SegmentedControl: {
25
- ({ selectedValue, label, onChange, children, ...rest }: TypeSegmentedControlProps): react_jsx_runtime.JSX.Element;
27
+ ({ selectedValue, label, onChange, onValueChange, children, disabled, ...rest }: TypeSegmentedControlProps): react_jsx_runtime.JSX.Element;
26
28
  Item: {
27
- ({ value, children, disabled, ...rest }: TypeSegmentedControlItemProps): react_jsx_runtime.JSX.Element | null;
29
+ ({ value, children, disabled, ...rest }: TypeSegmentedControlItemProps): react_jsx_runtime.JSX.Element;
28
30
  displayName: string;
29
31
  };
30
32
  };
package/dist/index.js CHANGED
@@ -38,24 +38,48 @@ module.exports = __toCommonJS(index_exports);
38
38
  // src/SegmentedControl.tsx
39
39
  var React = __toESM(require("react"));
40
40
  var import_react = require("react");
41
- var import_seeds_react_box2 = __toESM(require("@sproutsocial/seeds-react-box"));
41
+ var ToggleGroup = __toESM(require("@radix-ui/react-toggle-group"));
42
42
 
43
43
  // src/styles.ts
44
44
  var import_styled_components = __toESM(require("styled-components"));
45
45
  var import_seeds_react_mixins = require("@sproutsocial/seeds-react-mixins");
46
46
  var import_seeds_react_box = __toESM(require("@sproutsocial/seeds-react-box"));
47
- var import_seeds_react_text = __toESM(require("@sproutsocial/seeds-react-text"));
47
+ var import_seeds_react_button = __toESM(require("@sproutsocial/seeds-react-button"));
48
48
  var SegmentedControlContainer = (0, import_styled_components.default)(import_seeds_react_box.default)`
49
49
  border: 1px solid
50
50
  ${(props) => props.theme.colors.button.secondary.border.base};
51
51
  border-radius: ${(props) => props.theme.radii.outer};
52
52
  padding: ${(props) => props.theme.space[100]};
53
+ display: flex;
53
54
 
54
- legend {
55
- ${import_seeds_react_mixins.visuallyHidden}
55
+ ${(props) => props.disabled && import_seeds_react_mixins.disabled}
56
+ `;
57
+ var ToggleButton = (0, import_styled_components.default)(import_seeds_react_button.default)`
58
+ /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */
59
+ padding: calc(${(props) => props.theme.space[350]} - 6px);
60
+ flex: 1 1 auto;
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ text-align: center;
65
+ border: 0;
66
+
67
+ & + & {
68
+ margin-left: ${(props) => props.theme.space[100]};
56
69
  }
57
70
 
58
- ${(props) => props.disabled && import_seeds_react_mixins.disabled}
71
+ &:hover {
72
+ background-color: ${(props) => props.theme.colors.listItem.background.hover};
73
+ }
74
+
75
+ &[data-state="on"] {
76
+ color: ${(props) => props.theme.colors.text.inverse};
77
+ background-color: ${(props) => props.theme.colors.listItem.background.selected};
78
+
79
+ &:hover {
80
+ background-color: ${(props) => props.theme.colors.listItem.background.selected};
81
+ }
82
+ }
59
83
  `;
60
84
  var SegmentedControlItemContainer = (0, import_styled_components.default)(import_seeds_react_box.default)`
61
85
  flex: 1 1 auto;
@@ -76,7 +100,7 @@ var SegmentedControlItemContainer = (0, import_styled_components.default)(import
76
100
 
77
101
  ${(props) => props.disabled && import_seeds_react_mixins.disabled}
78
102
  `;
79
- var SegmentedControlLabel = (0, import_styled_components.default)(import_seeds_react_text.default)`
103
+ var SegmentedControlLabel = (0, import_styled_components.default)(import_seeds_react_button.default)`
80
104
  flex: 1 1 auto;
81
105
  display: flex;
82
106
  align-items: center;
@@ -114,7 +138,6 @@ var SegmentedControlLabel = (0, import_styled_components.default)(import_seeds_r
114
138
  // src/SegmentedControl.tsx
115
139
  var import_jsx_runtime = require("react/jsx-runtime");
116
140
  var nameCounter = 0;
117
- var idCounter = 0;
118
141
  var SegmentedControlContext = React.createContext(null);
119
142
  var SegmentedControlItem = ({
120
143
  value,
@@ -122,36 +145,22 @@ var SegmentedControlItem = ({
122
145
  disabled: disabled2,
123
146
  ...rest
124
147
  }) => {
125
- const context = (0, import_react.useContext)(SegmentedControlContext);
126
- if (!context) {
127
- return null;
128
- }
129
- const { name, selectedValue, onChange } = context;
130
- const [id] = (0, import_react.useState)(`${name}-${idCounter++}`);
131
- const isChecked = value === selectedValue;
132
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
133
- SegmentedControlItemContainer,
148
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
149
+ ToggleGroup.Item,
134
150
  {
135
- "data-segmentedcontrol-isactive": isChecked,
136
- "data-qa-segmentedcontrol-item": value,
137
- "data-qa-segmentedcontrol-ischecked": isChecked === true,
151
+ value,
152
+ "aria-label": "Left aligned",
138
153
  disabled: disabled2,
139
- ...rest,
140
- children: [
141
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
142
- "input",
143
- {
144
- type: "radio",
145
- id,
146
- name,
147
- value,
148
- checked: isChecked,
149
- onChange,
150
- disabled: disabled2
151
- }
152
- ),
153
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SegmentedControlLabel, { as: "label", htmlFor: id, isActive: isChecked, children })
154
- ]
154
+ asChild: true,
155
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
156
+ ToggleButton,
157
+ {
158
+ "data-qa-segmentedcontrol-item": value,
159
+ disabled: disabled2,
160
+ ...rest,
161
+ children
162
+ }
163
+ )
155
164
  }
156
165
  );
157
166
  };
@@ -159,29 +168,41 @@ var SegmentedControl = ({
159
168
  selectedValue,
160
169
  label,
161
170
  onChange,
171
+ onValueChange,
162
172
  children,
173
+ disabled: disabled2,
163
174
  ...rest
164
175
  }) => {
165
176
  const [name] = (0, import_react.useState)(`Racine-segmented-control-${nameCounter++}`);
166
177
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
167
- SegmentedControlContext.Provider,
178
+ ToggleGroup.Root,
168
179
  {
169
- value: {
170
- name,
171
- selectedValue,
172
- onChange
180
+ className: "inline-flex space-x-px rounded bg-mauve6 shadow-[0_2px_10px] shadow-blackA4",
181
+ type: "single",
182
+ value: selectedValue,
183
+ "aria-label": label,
184
+ disabled: disabled2,
185
+ onValueChange: (e) => {
186
+ if (!e) return;
187
+ onValueChange?.(e);
188
+ const mockEvent = new Event("change", {
189
+ bubbles: true
190
+ });
191
+ Object.defineProperty(mockEvent, "target", { value: { value: e } });
192
+ Object.defineProperty(mockEvent, "currentTarget", {
193
+ value: { value: e }
194
+ });
195
+ onChange?.(mockEvent);
173
196
  },
174
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
197
+ asChild: true,
198
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
175
199
  SegmentedControlContainer,
176
200
  {
177
- forwardedAs: "fieldset",
178
201
  "data-qa-segmentedcontrol": label,
179
202
  "data-qa-segmentedcontrol-value": selectedValue,
203
+ disabled: disabled2,
180
204
  ...rest,
181
- children: [
182
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("legend", { children: label }),
183
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_box2.default, { display: "flex", children })
184
- ]
205
+ children
185
206
  }
186
207
  )
187
208
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/SegmentedControl.tsx","../src/styles.ts","../src/SegmentedControlTypes.ts"],"sourcesContent":["import SegmentedControl from \"./SegmentedControl\";\n\nexport default SegmentedControl;\nexport { SegmentedControl };\nexport * from \"./SegmentedControlTypes\";\n","import * as React from \"react\";\nimport { useState, useContext } from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport {\n SegmentedControlContainer,\n SegmentedControlItemContainer,\n SegmentedControlLabel,\n} from \"./styles\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nlet nameCounter = 0;\nlet idCounter = 0;\n\ninterface TypeSegmentedControlContext {\n name: string;\n selectedValue: string;\n onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n}\n\nconst SegmentedControlContext = React.createContext<\n TypeSegmentedControlContext | null | undefined\n>(null);\n\nconst SegmentedControlItem = ({\n value,\n children,\n disabled,\n ...rest\n}: TypeSegmentedControlItemProps) => {\n const context = useContext(SegmentedControlContext);\n\n if (!context) {\n return null;\n }\n\n const { name, selectedValue, onChange } = context;\n\n /* eslint-disable-next-line */\n const [id] = useState(`${name}-${idCounter++}`);\n const isChecked = value === selectedValue;\n\n return (\n <SegmentedControlItemContainer\n data-segmentedcontrol-isactive={isChecked}\n data-qa-segmentedcontrol-item={value}\n data-qa-segmentedcontrol-ischecked={isChecked === true}\n disabled={disabled}\n {...rest}\n >\n <input\n type=\"radio\"\n id={id}\n name={name}\n value={value}\n checked={isChecked}\n onChange={onChange}\n disabled={disabled}\n />\n <SegmentedControlLabel as=\"label\" htmlFor={id} isActive={isChecked}>\n {children}\n </SegmentedControlLabel>\n </SegmentedControlItemContainer>\n );\n};\n\nconst SegmentedControl = ({\n selectedValue,\n label,\n onChange,\n children,\n ...rest\n}: TypeSegmentedControlProps) => {\n const [name] = useState(`Racine-segmented-control-${nameCounter++}`);\n return (\n <SegmentedControlContext.Provider\n value={{\n name,\n selectedValue,\n onChange,\n }}\n >\n <SegmentedControlContainer\n forwardedAs=\"fieldset\"\n data-qa-segmentedcontrol={label}\n data-qa-segmentedcontrol-value={selectedValue}\n {...rest}\n >\n <legend>{label}</legend>\n\n <Box display=\"flex\">{children}</Box>\n </SegmentedControlContainer>\n </SegmentedControlContext.Provider>\n );\n};\n\nSegmentedControlItem.displayName = \"SegmentedControl.Item\";\nSegmentedControl.Item = SegmentedControlItem;\n\nexport default SegmentedControl;\n","import styled, { css } from \"styled-components\";\nimport {\n visuallyHidden,\n focusRing,\n disabled,\n} from \"@sproutsocial/seeds-react-mixins\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nexport const SegmentedControlContainer = styled(Box)<\n Pick<TypeSegmentedControlProps, \"disabled\">\n>`\n border: 1px solid\n ${(props) => props.theme.colors.button.secondary.border.base};\n border-radius: ${(props) => props.theme.radii.outer};\n padding: ${(props) => props.theme.space[100]};\n\n legend {\n ${visuallyHidden}\n }\n\n ${(props) => props.disabled && disabled}\n`;\n\nexport const SegmentedControlItemContainer = styled(Box)<\n Pick<TypeSegmentedControlItemProps, \"disabled\">\n>`\n flex: 1 1 auto;\n display: flex;\n cursor: pointer;\n\n & + & {\n margin-left: ${(props) => props.theme.space[100]};\n }\n\n &:focus-within label {\n ${focusRing}\n }\n\n input {\n ${visuallyHidden}\n }\n\n ${(props) => props.disabled && disabled}\n`;\n\ninterface TypeSegmentedControlState {\n isActive: boolean;\n}\n\nexport const SegmentedControlLabel = styled(Text)<TypeSegmentedControlState>`\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: ${(props) => props.theme.colors.text.body};\n cursor: pointer;\n font-size: ${(props) => props.theme.typography[200].fontSize};\n /**\n\t* Matches default line height of Icon. Also matches the overall height of\n\t* Input, Select, and Button.\n\t*/\n line-height: 16px;\n font-weight: ${(props) => props.theme.fontWeights.semibold};\n\n border-radius: ${(props) => props.theme.radii.inner};\n /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */\n padding: calc(${(props) => props.theme.space[350]} - 6px);\n transition: all ${(props) => props.theme.duration.fast};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.hover};\n }\n\n ${(props) =>\n props.isActive &&\n css`\n color: ${(props) => props.theme.colors.text.inverse};\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n }\n `}\n`;\n","import type { TypeContainerProps } from \"@sproutsocial/seeds-react-box\";\nimport * as React from \"react\";\n\nexport interface TypeSegmentedControlItemProps {\n /** The value of this item. Should be unique among sibling items. */\n value: string;\n children: React.ReactNode;\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n}\n\nexport interface TypeSegmentedControlProps extends TypeContainerProps {\n /** The value of the currently selected item. Should match the value prop of one of the child items */\n selectedValue: string;\n\n /** The title of the segmented control, used for accessibility purposes */\n label: string;\n\n /** Called when the user selects a new item. You can access the value of the newly selected item using \"event.target.value\" */\n onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n children: React.ReactNode;\n}\n"],"mappings":"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","names":["import_seeds_react_box","styled","Box","Text","props","disabled","Box","React"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/SegmentedControl.tsx","../src/styles.ts","../src/SegmentedControlTypes.ts"],"sourcesContent":["import SegmentedControl from \"./SegmentedControl\";\n\nexport default SegmentedControl;\nexport { SegmentedControl };\nexport * from \"./SegmentedControlTypes\";\n","import * as React from \"react\";\nimport { useState } from \"react\";\nimport * as ToggleGroup from \"@radix-ui/react-toggle-group\";\nimport { SegmentedControlContainer, ToggleButton } from \"./styles\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nlet nameCounter = 0;\nlet idCounter = 0;\n\ninterface TypeSegmentedControlContext {\n name: string;\n selectedValue: string;\n onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n}\n\nconst SegmentedControlContext = React.createContext<\n TypeSegmentedControlContext | null | undefined\n>(null);\n\nconst SegmentedControlItem = ({\n value,\n children,\n disabled,\n ...rest\n}: TypeSegmentedControlItemProps) => {\n return (\n <ToggleGroup.Item\n value={value}\n aria-label=\"Left aligned\"\n disabled={disabled}\n asChild\n >\n <ToggleButton\n // TODO: Discuss dropping these\n // We'd need to keep context, which seems unnecessary now\n // data-segmentedcontrol-isactive={isChecked}\n data-qa-segmentedcontrol-item={value}\n // data-qa-segmentedcontrol-ischecked={isChecked === true}\n disabled={disabled}\n {...rest}\n >\n {children}\n </ToggleButton>\n </ToggleGroup.Item>\n );\n};\n\nconst SegmentedControl = ({\n selectedValue,\n label,\n onChange,\n onValueChange,\n children,\n disabled,\n ...rest\n}: TypeSegmentedControlProps) => {\n const [name] = useState(`Racine-segmented-control-${nameCounter++}`);\n return (\n <ToggleGroup.Root\n className=\"inline-flex space-x-px rounded bg-mauve6 shadow-[0_2px_10px] shadow-blackA4\"\n type=\"single\"\n value={selectedValue}\n aria-label={label}\n disabled={disabled}\n onValueChange={(e) => {\n // Radix allows deselecting a value by clicking on it when it's already selected,\n // but we don't want to allow that behavior in our SegmentedControl.\n if (!e) return;\n\n onValueChange?.(e);\n // Create a mock event to pass to onChange for backwards compatibility.\n // We want to move towards onValueChange, but onChange is used by consumers currently so we need to support both for now.\n const mockEvent = new Event(\"change\", {\n bubbles: true,\n }) as unknown as React.SyntheticEvent<HTMLInputElement>;\n Object.defineProperty(mockEvent, \"target\", { value: { value: e } });\n Object.defineProperty(mockEvent, \"currentTarget\", {\n value: { value: e },\n });\n onChange?.(mockEvent);\n }}\n asChild\n >\n <SegmentedControlContainer\n data-qa-segmentedcontrol={label}\n data-qa-segmentedcontrol-value={selectedValue}\n disabled={disabled}\n {...rest}\n >\n {children}\n </SegmentedControlContainer>\n </ToggleGroup.Root>\n );\n};\n\nSegmentedControlItem.displayName = \"SegmentedControl.Item\";\nSegmentedControl.Item = SegmentedControlItem;\n\nexport default SegmentedControl;\n","import styled, { css } from \"styled-components\";\nimport {\n visuallyHidden,\n focusRing,\n disabled,\n} from \"@sproutsocial/seeds-react-mixins\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Button from \"@sproutsocial/seeds-react-button\";\nimport type {\n TypeSegmentedControlProps,\n TypeSegmentedControlItemProps,\n} from \"./SegmentedControlTypes\";\n\nexport const SegmentedControlContainer = styled(Box)<\n Pick<TypeSegmentedControlProps, \"disabled\">\n>`\n border: 1px solid\n ${(props) => props.theme.colors.button.secondary.border.base};\n border-radius: ${(props) => props.theme.radii.outer};\n padding: ${(props) => props.theme.space[100]};\n display: flex;\n\n ${(props) => props.disabled && disabled}\n`;\n\nexport const ToggleButton = styled(Button)`\n /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */\n padding: calc(${(props) => props.theme.space[350]} - 6px);\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n border: 0;\n\n & + & {\n margin-left: ${(props) => props.theme.space[100]};\n }\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.hover};\n }\n\n &[data-state=\"on\"] {\n color: ${(props) => props.theme.colors.text.inverse};\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n }\n }\n`;\n\nexport const SegmentedControlItemContainer = styled(Box)<\n Pick<TypeSegmentedControlItemProps, \"disabled\">\n>`\n flex: 1 1 auto;\n display: flex;\n cursor: pointer;\n\n & + & {\n margin-left: ${(props) => props.theme.space[100]};\n }\n\n &:focus-within label {\n ${focusRing}\n }\n\n input {\n ${visuallyHidden}\n }\n\n ${(props) => props.disabled && disabled}\n`;\n\ninterface TypeSegmentedControlState {\n isActive: boolean;\n}\n\nexport const SegmentedControlLabel = styled(Button)<TypeSegmentedControlState>`\n flex: 1 1 auto;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n color: ${(props) => props.theme.colors.text.body};\n cursor: pointer;\n font-size: ${(props) => props.theme.typography[200].fontSize};\n /**\n\t* Matches default line height of Icon. Also matches the overall height of\n\t* Input, Select, and Button.\n\t*/\n line-height: 16px;\n font-weight: ${(props) => props.theme.fontWeights.semibold};\n\n border-radius: ${(props) => props.theme.radii.inner};\n /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */\n padding: calc(${(props) => props.theme.space[350]} - 6px);\n transition: all ${(props) => props.theme.duration.fast};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.hover};\n }\n\n ${(props) =>\n props.isActive &&\n css`\n color: ${(props) => props.theme.colors.text.inverse};\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n\n &:hover {\n background-color: ${(props) =>\n props.theme.colors.listItem.background.selected};\n }\n `}\n`;\n","import type { TypeContainerProps } from \"@sproutsocial/seeds-react-box\";\nimport * as React from \"react\";\n\nexport interface TypeSegmentedControlItemProps {\n /** The value of this item. Should be unique among sibling items. */\n value: string;\n children: React.ReactNode;\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n}\n\nexport interface TypeSegmentedControlProps extends TypeContainerProps {\n /** The value of the currently selected item. Should match the value prop of one of the child items */\n selectedValue: string;\n\n /** The title of the segmented control, used for accessibility purposes */\n label: string;\n\n /** Called when the user selects a new item. You can access the value of the newly selected item using \"event.target.value\" */\n onChange?: (e: React.SyntheticEvent<HTMLInputElement>) => void;\n\n /** Called when the user selects a new item. You can access the value of the newly selected item using \"event.target.value\" */\n onValueChange?: (value: string) => void;\n\n /** Disables user action and applies a disabled style on the component */\n disabled?: boolean;\n children: React.ReactNode;\n}\n"],"mappings":"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","names":["styled","Box","Button","props","disabled","React"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-segmented-control",
3
- "version": "1.0.13",
3
+ "version": "1.1.1",
4
4
  "description": "Seeds React SegmentedControl",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
@@ -18,11 +18,13 @@
18
18
  "test:watch": "jest --watch --coverage=false"
19
19
  },
20
20
  "dependencies": {
21
- "@sproutsocial/seeds-react-theme": "^3.5.1",
21
+ "@radix-ui/react-toggle-group": "^1.0.0",
22
+ "@sproutsocial/seeds-react-theme": "^3.6.0",
22
23
  "@sproutsocial/seeds-react-system-props": "^3.0.1",
23
- "@sproutsocial/seeds-react-box": "^1.1.13",
24
+ "@sproutsocial/seeds-react-box": "^1.1.14",
25
+ "@sproutsocial/seeds-react-button": "^1.3.20",
24
26
  "@sproutsocial/seeds-react-text": "^1.4.0",
25
- "@sproutsocial/seeds-react-mixins": "^4.2.5"
27
+ "@sproutsocial/seeds-react-mixins": "^4.3.1"
26
28
  },
27
29
  "devDependencies": {
28
30
  "@types/react": "^18.0.0",
@@ -15,11 +15,15 @@ type Story = StoryObj<typeof SegmentedControl>;
15
15
  // @ts-ignore IDK what props should be
16
16
  const StatefulSegmentedControl = (props) => {
17
17
  const [value, setValue] = useState("1");
18
+
18
19
  return (
19
20
  <SegmentedControl
20
21
  selectedValue={value}
21
22
  label="Segmented control component"
22
- onChange={(e) => setValue((e.target as HTMLInputElement).value)}
23
+ onChange={(e) => {
24
+ console.log(e);
25
+ setValue((e.target as HTMLInputElement).value);
26
+ }}
23
27
  {...props}
24
28
  >
25
29
  <SegmentedControl.Item value="1">Test 1</SegmentedControl.Item>
@@ -55,7 +59,7 @@ export const DisabledItem: Story = {
55
59
  <SegmentedControl
56
60
  selectedValue={value}
57
61
  label="Segmented control component"
58
- onChange={(e) => setValue((e.target as HTMLInputElement).value)}
62
+ onValueChange={setValue}
59
63
  flex="1"
60
64
  >
61
65
  <SegmentedControl.Item value="1">Test 1</SegmentedControl.Item>
@@ -1,11 +1,7 @@
1
1
  import * as React from "react";
2
- import { useState, useContext } from "react";
3
- import Box from "@sproutsocial/seeds-react-box";
4
- import {
5
- SegmentedControlContainer,
6
- SegmentedControlItemContainer,
7
- SegmentedControlLabel,
8
- } from "./styles";
2
+ import { useState } from "react";
3
+ import * as ToggleGroup from "@radix-ui/react-toggle-group";
4
+ import { SegmentedControlContainer, ToggleButton } from "./styles";
9
5
  import type {
10
6
  TypeSegmentedControlProps,
11
7
  TypeSegmentedControlItemProps,
@@ -30,39 +26,25 @@ const SegmentedControlItem = ({
30
26
  disabled,
31
27
  ...rest
32
28
  }: TypeSegmentedControlItemProps) => {
33
- const context = useContext(SegmentedControlContext);
34
-
35
- if (!context) {
36
- return null;
37
- }
38
-
39
- const { name, selectedValue, onChange } = context;
40
-
41
- /* eslint-disable-next-line */
42
- const [id] = useState(`${name}-${idCounter++}`);
43
- const isChecked = value === selectedValue;
44
-
45
29
  return (
46
- <SegmentedControlItemContainer
47
- data-segmentedcontrol-isactive={isChecked}
48
- data-qa-segmentedcontrol-item={value}
49
- data-qa-segmentedcontrol-ischecked={isChecked === true}
30
+ <ToggleGroup.Item
31
+ value={value}
32
+ aria-label="Left aligned"
50
33
  disabled={disabled}
51
- {...rest}
34
+ asChild
52
35
  >
53
- <input
54
- type="radio"
55
- id={id}
56
- name={name}
57
- value={value}
58
- checked={isChecked}
59
- onChange={onChange}
36
+ <ToggleButton
37
+ // TODO: Discuss dropping these
38
+ // We'd need to keep context, which seems unnecessary now
39
+ // data-segmentedcontrol-isactive={isChecked}
40
+ data-qa-segmentedcontrol-item={value}
41
+ // data-qa-segmentedcontrol-ischecked={isChecked === true}
60
42
  disabled={disabled}
61
- />
62
- <SegmentedControlLabel as="label" htmlFor={id} isActive={isChecked}>
43
+ {...rest}
44
+ >
63
45
  {children}
64
- </SegmentedControlLabel>
65
- </SegmentedControlItemContainer>
46
+ </ToggleButton>
47
+ </ToggleGroup.Item>
66
48
  );
67
49
  };
68
50
 
@@ -70,29 +52,47 @@ const SegmentedControl = ({
70
52
  selectedValue,
71
53
  label,
72
54
  onChange,
55
+ onValueChange,
73
56
  children,
57
+ disabled,
74
58
  ...rest
75
59
  }: TypeSegmentedControlProps) => {
76
60
  const [name] = useState(`Racine-segmented-control-${nameCounter++}`);
77
61
  return (
78
- <SegmentedControlContext.Provider
79
- value={{
80
- name,
81
- selectedValue,
82
- onChange,
62
+ <ToggleGroup.Root
63
+ className="inline-flex space-x-px rounded bg-mauve6 shadow-[0_2px_10px] shadow-blackA4"
64
+ type="single"
65
+ value={selectedValue}
66
+ aria-label={label}
67
+ disabled={disabled}
68
+ onValueChange={(e) => {
69
+ // Radix allows deselecting a value by clicking on it when it's already selected,
70
+ // but we don't want to allow that behavior in our SegmentedControl.
71
+ if (!e) return;
72
+
73
+ onValueChange?.(e);
74
+ // Create a mock event to pass to onChange for backwards compatibility.
75
+ // We want to move towards onValueChange, but onChange is used by consumers currently so we need to support both for now.
76
+ const mockEvent = new Event("change", {
77
+ bubbles: true,
78
+ }) as unknown as React.SyntheticEvent<HTMLInputElement>;
79
+ Object.defineProperty(mockEvent, "target", { value: { value: e } });
80
+ Object.defineProperty(mockEvent, "currentTarget", {
81
+ value: { value: e },
82
+ });
83
+ onChange?.(mockEvent);
83
84
  }}
85
+ asChild
84
86
  >
85
87
  <SegmentedControlContainer
86
- forwardedAs="fieldset"
87
88
  data-qa-segmentedcontrol={label}
88
89
  data-qa-segmentedcontrol-value={selectedValue}
90
+ disabled={disabled}
89
91
  {...rest}
90
92
  >
91
- <legend>{label}</legend>
92
-
93
- <Box display="flex">{children}</Box>
93
+ {children}
94
94
  </SegmentedControlContainer>
95
- </SegmentedControlContext.Provider>
95
+ </ToggleGroup.Root>
96
96
  );
97
97
  };
98
98
 
@@ -17,7 +17,10 @@ export interface TypeSegmentedControlProps extends TypeContainerProps {
17
17
  label: string;
18
18
 
19
19
  /** Called when the user selects a new item. You can access the value of the newly selected item using "event.target.value" */
20
- onChange: (e: React.SyntheticEvent<HTMLInputElement>) => void;
20
+ onChange?: (e: React.SyntheticEvent<HTMLInputElement>) => void;
21
+
22
+ /** Called when the user selects a new item. You can access the value of the newly selected item using "event.target.value" */
23
+ onValueChange?: (value: string) => void;
21
24
 
22
25
  /** Disables user action and applies a disabled style on the component */
23
26
  disabled?: boolean;
@@ -32,53 +32,26 @@ describe("SegmentedControl", () => {
32
32
  const item2 = screen.getByText("Test 2");
33
33
  const item3 = screen.getByText("Test 3");
34
34
 
35
- expect(item1?.parentNode).toHaveAttribute(
36
- "data-segmentedcontrol-isactive",
37
- "true"
38
- );
35
+ expect(item1).toHaveAttribute("data-state", "on");
39
36
 
40
- expect(item2?.parentNode).toHaveAttribute(
41
- "data-segmentedcontrol-isactive",
42
- "false"
43
- );
37
+ expect(item2).toHaveAttribute("data-state", "off");
44
38
 
45
- expect(item3?.parentNode).toHaveAttribute(
46
- "data-segmentedcontrol-isactive",
47
- "false"
48
- );
39
+ expect(item3).toHaveAttribute("data-state", "off");
49
40
 
50
41
  fireEvent.click(item3);
51
42
 
52
- expect(item1?.parentNode).toHaveAttribute(
53
- "data-segmentedcontrol-isactive",
54
- "false"
55
- );
43
+ expect(item1).toHaveAttribute("data-state", "off");
56
44
 
57
- expect(item2?.parentNode).toHaveAttribute(
58
- "data-segmentedcontrol-isactive",
59
- "false"
60
- );
45
+ expect(item2).toHaveAttribute("data-state", "off");
61
46
 
62
- expect(item3?.parentNode).toHaveAttribute(
63
- "data-segmentedcontrol-isactive",
64
- "true"
65
- );
47
+ expect(item3).toHaveAttribute("data-state", "on");
66
48
 
67
49
  fireEvent.click(item2);
68
50
 
69
- expect(item1?.parentNode).toHaveAttribute(
70
- "data-segmentedcontrol-isactive",
71
- "false"
72
- );
51
+ expect(item1).toHaveAttribute("data-state", "off");
73
52
 
74
- expect(item2?.parentNode).toHaveAttribute(
75
- "data-segmentedcontrol-isactive",
76
- "true"
77
- );
53
+ expect(item2).toHaveAttribute("data-state", "on");
78
54
 
79
- expect(item3?.parentNode).toHaveAttribute(
80
- "data-segmentedcontrol-isactive",
81
- "false"
82
- );
55
+ expect(item3).toHaveAttribute("data-state", "off");
83
56
  });
84
57
  });
package/src/styles.ts CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  disabled,
6
6
  } from "@sproutsocial/seeds-react-mixins";
7
7
  import Box from "@sproutsocial/seeds-react-box";
8
- import Text from "@sproutsocial/seeds-react-text";
8
+ import Button from "@sproutsocial/seeds-react-button";
9
9
  import type {
10
10
  TypeSegmentedControlProps,
11
11
  TypeSegmentedControlItemProps,
@@ -18,12 +18,40 @@ export const SegmentedControlContainer = styled(Box)<
18
18
  ${(props) => props.theme.colors.button.secondary.border.base};
19
19
  border-radius: ${(props) => props.theme.radii.outer};
20
20
  padding: ${(props) => props.theme.space[100]};
21
+ display: flex;
21
22
 
22
- legend {
23
- ${visuallyHidden}
23
+ ${(props) => props.disabled && disabled}
24
+ `;
25
+
26
+ export const ToggleButton = styled(Button)`
27
+ /* To match the height of buttons... 350 padding - 2px top and bottom padding of the parent - 1px border on top and bottom */
28
+ padding: calc(${(props) => props.theme.space[350]} - 6px);
29
+ flex: 1 1 auto;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ text-align: center;
34
+ border: 0;
35
+
36
+ & + & {
37
+ margin-left: ${(props) => props.theme.space[100]};
24
38
  }
25
39
 
26
- ${(props) => props.disabled && disabled}
40
+ &:hover {
41
+ background-color: ${(props) =>
42
+ props.theme.colors.listItem.background.hover};
43
+ }
44
+
45
+ &[data-state="on"] {
46
+ color: ${(props) => props.theme.colors.text.inverse};
47
+ background-color: ${(props) =>
48
+ props.theme.colors.listItem.background.selected};
49
+
50
+ &:hover {
51
+ background-color: ${(props) =>
52
+ props.theme.colors.listItem.background.selected};
53
+ }
54
+ }
27
55
  `;
28
56
 
29
57
  export const SegmentedControlItemContainer = styled(Box)<
@@ -52,7 +80,7 @@ interface TypeSegmentedControlState {
52
80
  isActive: boolean;
53
81
  }
54
82
 
55
- export const SegmentedControlLabel = styled(Text)<TypeSegmentedControlState>`
83
+ export const SegmentedControlLabel = styled(Button)<TypeSegmentedControlState>`
56
84
  flex: 1 1 auto;
57
85
  display: flex;
58
86
  align-items: center;