@sproutsocial/seeds-react-tabs 1.3.11 → 1.4.11

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.
@@ -8,14 +8,14 @@ $ tsup --dts
8
8
  CLI Cleaning output folder
9
9
  CJS Build start
10
10
  ESM Build start
11
- CJS dist/index.js 7.72 KB
12
- CJS dist/index.js.map 12.13 KB
13
- CJS ⚡️ Build success in 71ms
14
- ESM dist/esm/index.js 5.75 KB
15
- ESM dist/esm/index.js.map 12.06 KB
16
- ESM ⚡️ Build success in 71ms
11
+ CJS dist/index.js 11.88 KB
12
+ CJS dist/index.js.map 19.36 KB
13
+ CJS ⚡️ Build success in 75ms
14
+ ESM dist/esm/index.js 9.89 KB
15
+ ESM dist/esm/index.js.map 19.31 KB
16
+ ESM ⚡️ Build success in 75ms
17
17
  DTS Build start
18
- DTS ⚡️ Build success in 5763ms
19
- DTS dist/index.d.ts 2.63 KB
20
- DTS dist/index.d.mts 2.63 KB
21
- Done in 7.39s.
18
+ DTS ⚡️ Build success in 4933ms
19
+ DTS dist/index.d.ts 3.38 KB
20
+ DTS dist/index.d.mts 3.38 KB
21
+ Done in 7.10s.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,104 @@
1
1
  # @sproutsocial/seeds-react-tabs
2
2
 
3
+ ## 1.4.11
4
+
5
+ ### Patch Changes
6
+
7
+ - @sproutsocial/seeds-react-button@2.2.1
8
+
9
+ ## 1.4.10
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [456a20f]
14
+ - Updated dependencies [5892f44]
15
+ - @sproutsocial/seeds-react-button@2.2.0
16
+ - @sproutsocial/seeds-react-system-props@3.1.1
17
+
18
+ ## 1.4.9
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies [5114a32]
23
+ - Updated dependencies [a0fe3d3]
24
+ - @sproutsocial/seeds-react-theme@4.1.0
25
+ - @sproutsocial/seeds-react-button@2.1.3
26
+
27
+ ## 1.4.8
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies [cb27f07]
32
+ - @sproutsocial/seeds-react-button@2.1.2
33
+
34
+ ## 1.4.7
35
+
36
+ ### Patch Changes
37
+
38
+ - Updated dependencies [40d2dfe]
39
+ - @sproutsocial/seeds-react-button@2.1.1
40
+
41
+ ## 1.4.6
42
+
43
+ ### Patch Changes
44
+
45
+ - Updated dependencies [132ef9d]
46
+ - Updated dependencies [132ef9d]
47
+ - Updated dependencies [132ef9d]
48
+ - @sproutsocial/seeds-react-button@2.1.0
49
+ - @sproutsocial/seeds-react-theme@4.0.0
50
+ - @sproutsocial/seeds-react-system-props@3.1.0
51
+
52
+ ## 1.4.5
53
+
54
+ ### Patch Changes
55
+
56
+ - @sproutsocial/seeds-react-button@2.0.8
57
+
58
+ ## 1.4.4
59
+
60
+ ### Patch Changes
61
+
62
+ - Updated dependencies [47c62b3]
63
+ - @sproutsocial/seeds-react-theme@3.7.1
64
+ - @sproutsocial/seeds-react-button@2.0.7
65
+
66
+ ## 1.4.3
67
+
68
+ ### Patch Changes
69
+
70
+ - Updated dependencies [06da9c2]
71
+ - @sproutsocial/seeds-react-theme@3.7.0
72
+ - @sproutsocial/seeds-react-button@2.0.6
73
+
74
+ ## 1.4.2
75
+
76
+ ### Patch Changes
77
+
78
+ - 9c60744: Fix Safari-only tab collapse not addressed by 1.4.1:
79
+
80
+ Inside a flex parent with `overflow: auto/hidden`, Safari resolves the Container's `min-height: min-content` to `0` when the same element also has `overflow-x: auto; overflow-y: hidden`. Tabs would paint once, then collapse to their `border-bottom` (1px). Adds `flex-shrink: 0` on the Container so the tablist preserves its intrinsic height regardless of how Safari handles `min-content` in that combination. Chrome behavior is unchanged.
81
+
82
+ Reported in [BUGS-28687](https://sprout.atlassian.net/browse/BUGS-28687) across every Profiles-by-Network report.
83
+
84
+ ## 1.4.1
85
+
86
+ ### Patch Changes
87
+
88
+ - 679806a: Fix layout regression introduced in 1.4.0:
89
+
90
+ - **Tabs no longer collapse to a 1px-tall row inside flex-column parents.** Container's `overflow-x: auto; overflow-y: hidden` was triggering the flex `min-height: auto → 0` override, allowing an ancestor `display: flex; flex-direction: column` with a bounded height (e.g. `ContentWithDrillDown`'s `Content.Body`) to shrink the tablist to its border-bottom. Container now sets `min-height: min-content`, restoring the pre-1.4.0 minimum-size behavior without changing scroll semantics.
91
+
92
+ No API changes.
93
+
94
+ ## 1.4.0
95
+
96
+ ### Minor Changes
97
+
98
+ - 412cc1e: Tabs now scrolls horizontally when the tab list overflows its container, with a fade indicator at each edge that auto-adapts to the parent's background via CSS `mask-image`. Vertical mouse-wheel input is redirected to horizontal scroll (with release-at-ends behavior so page scrolling isn't trapped), and arrow-key and click activation auto-scroll the selected tab into view. Adds an optional `aria-label` prop that's forwarded to the `role="tablist"` element per the WAI-ARIA Authoring Practices Tabs pattern.
99
+
100
+ Spacing change for non-`fullWidth` Tabs: the inter-tab `margin-right` between adjacent tab buttons has been removed in favor of horizontal padding on each tab button. Visual spacing is preserved (each tab now contributes its own breathing room via padding rather than the previous tab pushing it via margin), and clickable hit-areas are slightly larger. The `fullWidth` case is unchanged. Consumers depending on the exact pixel values of the old margin should re-check their layouts.
101
+
3
102
  ## 1.3.11
4
103
 
5
104
  ### Patch Changes
package/dist/esm/index.js CHANGED
@@ -12,17 +12,71 @@ import "react";
12
12
  // src/styles.ts
13
13
  var Container = styled.div`
14
14
  display: ${(props) => props.fullWidth ? "flex" : "inline-flex"};
15
- justify-content: space-between;
16
15
  margin: 0;
17
16
  padding: 0;
17
+ min-width: 0;
18
+ max-width: 100%;
19
+ min-height: min-content;
20
+ flex-shrink: 0;
21
+ overflow-x: auto;
22
+ overflow-y: hidden;
23
+ scrollbar-width: none;
24
+ scroll-padding-inline: 48px;
25
+ scroll-behavior: smooth;
18
26
  border-bottom: ${(props) => props.theme.borders[500]}
19
27
  ${(props) => props.theme.colors.container.border.base};
20
28
 
29
+ &::-webkit-scrollbar {
30
+ display: none;
31
+ }
32
+
33
+ &[data-fade-right] {
34
+ -webkit-mask-image: linear-gradient(
35
+ to right,
36
+ black 0,
37
+ black calc(100% - 48px),
38
+ transparent
39
+ );
40
+ mask-image: linear-gradient(
41
+ to right,
42
+ black 0,
43
+ black calc(100% - 48px),
44
+ transparent
45
+ );
46
+ }
47
+
48
+ &[data-fade-left] {
49
+ -webkit-mask-image: linear-gradient(
50
+ to right,
51
+ transparent,
52
+ black 48px,
53
+ black 100%
54
+ );
55
+ mask-image: linear-gradient(to right, transparent, black 48px, black 100%);
56
+ }
57
+
58
+ &[data-fade-left][data-fade-right] {
59
+ -webkit-mask-image: linear-gradient(
60
+ to right,
61
+ transparent,
62
+ black 48px,
63
+ black calc(100% - 48px),
64
+ transparent
65
+ );
66
+ mask-image: linear-gradient(
67
+ to right,
68
+ transparent,
69
+ black 48px,
70
+ black calc(100% - 48px),
71
+ transparent
72
+ );
73
+ }
74
+
21
75
  ${COMMON}
22
76
  `;
23
77
  var TabButton = styled(Button)`
24
78
  margin-bottom: -1px;
25
- padding: ${(props) => `${props.theme.space[350]} 0`};
79
+ padding: ${(props) => `${props.theme.space[350]} ${props.fullWidth ? 0 : props.theme.space[350]}`};
26
80
  color: ${(props) => props.theme.colors.text.headline};
27
81
  border-radius: 0;
28
82
  border-width: 0 0 3px;
@@ -32,12 +86,6 @@ var TabButton = styled(Button)`
32
86
  flex-grow: 1;
33
87
  `};
34
88
 
35
- &:not(:last-child) {
36
- ${(props) => !props.fullWidth && css`
37
- margin-right: ${(props2) => props2.theme.space[350]};
38
- `};
39
- }
40
-
41
89
  ${(props) => props.isSelected && css`
42
90
  color: ${(props2) => props2.theme.colors.link.base};
43
91
  border-color: transparent transparent
@@ -62,7 +110,7 @@ var TabButton = styled(Button)`
62
110
  var styles_default = Container;
63
111
 
64
112
  // src/Tabs.tsx
65
- import { jsx } from "react/jsx-runtime";
113
+ import { jsx, jsxs } from "react/jsx-runtime";
66
114
  var TabButtonContext = React2.createContext({});
67
115
  var TabItemButton = class extends React2.Component {
68
116
  static contextType = TabButtonContext;
@@ -116,8 +164,70 @@ var Tabs = class extends React2.Component {
116
164
  static Panel = TabPanel;
117
165
  buttonRefs = {};
118
166
  tabList = [];
167
+ containerRef = React2.createRef();
168
+ startSentinelRef = React2.createRef();
169
+ endSentinelRef = React2.createRef();
170
+ startObserver = null;
171
+ endObserver = null;
172
+ componentDidMount() {
173
+ const container = this.containerRef.current;
174
+ if (!container) return;
175
+ container.addEventListener("wheel", this.handleWheel, { passive: false });
176
+ const startSentinel = this.startSentinelRef.current;
177
+ const endSentinel = this.endSentinelRef.current;
178
+ if (!startSentinel || !endSentinel) return;
179
+ if (typeof IntersectionObserver === "undefined") return;
180
+ this.startObserver = new IntersectionObserver(
181
+ ([entry]) => {
182
+ container.toggleAttribute("data-fade-left", !entry?.isIntersecting);
183
+ },
184
+ { root: container, threshold: 1, rootMargin: "0px 0px 0px 1px" }
185
+ );
186
+ this.endObserver = new IntersectionObserver(
187
+ ([entry]) => {
188
+ container.toggleAttribute("data-fade-right", !entry?.isIntersecting);
189
+ },
190
+ { root: container, threshold: 1, rootMargin: "0px 1px 0px 0px" }
191
+ );
192
+ this.startObserver.observe(startSentinel);
193
+ this.endObserver.observe(endSentinel);
194
+ }
195
+ componentWillUnmount() {
196
+ this.containerRef.current?.removeEventListener("wheel", this.handleWheel);
197
+ this.startObserver?.disconnect();
198
+ this.endObserver?.disconnect();
199
+ this.startObserver = null;
200
+ this.endObserver = null;
201
+ }
202
+ handleWheel = (e) => {
203
+ const container = this.containerRef.current;
204
+ if (!container) return;
205
+ if (e.deltaY === 0 || e.deltaX !== 0) return;
206
+ if (container.scrollWidth <= container.clientWidth) return;
207
+ const atStart = container.scrollLeft <= 0;
208
+ const atEnd = container.scrollLeft + container.clientWidth >= container.scrollWidth;
209
+ if (atEnd && e.deltaY > 0 || atStart && e.deltaY < 0) return;
210
+ e.preventDefault();
211
+ const lineHeight = 16;
212
+ const pixelDelta = e.deltaMode === 1 ? e.deltaY * lineHeight : e.deltaMode === 2 ? e.deltaY * container.clientHeight : e.deltaY;
213
+ container.scrollBy({ left: pixelDelta, behavior: "instant" });
214
+ };
119
215
  getSelectedId = () => this.props.selectedId;
120
- onActivate = (id) => this.props.onSelect(id);
216
+ onActivate = (id) => {
217
+ this.props.onSelect(id);
218
+ this.scrollTabIntoView(id);
219
+ };
220
+ scrollTabIntoView = (id) => {
221
+ const button = this.buttonRefs[id]?.current;
222
+ const container = this.containerRef.current;
223
+ if (!button || !container) return;
224
+ const buttonRect = button.getBoundingClientRect();
225
+ const containerRect = container.getBoundingClientRect();
226
+ const fadePadding = 48;
227
+ const isFullyVisible = buttonRect.left >= containerRect.left + fadePadding && buttonRect.right <= containerRect.right - fadePadding;
228
+ if (isFullyVisible) return;
229
+ button.scrollIntoView?.({ block: "nearest", inline: "nearest" });
230
+ };
121
231
  onTabMount = (id, ref) => {
122
232
  if (!this.tabList.includes(id)) {
123
233
  this.tabList.push(id);
@@ -138,15 +248,16 @@ var Tabs = class extends React2.Component {
138
248
  }
139
249
  };
140
250
  selectNextTab = (id) => {
141
- const buttonRef = this.buttonRefs[id];
142
251
  this.props.onSelect(id);
143
- buttonRef && buttonRef.current && buttonRef.current.focus && buttonRef.current.focus();
252
+ this.buttonRefs[id]?.current?.focus?.({ preventScroll: true });
253
+ this.scrollTabIntoView(id);
144
254
  };
145
- keyHandler = ({ keyCode }) => {
146
- switch (keyCode) {
255
+ keyHandler = (e) => {
256
+ switch (e.keyCode) {
147
257
  // left arrow
148
258
  case 37:
149
259
  this.tabList.forEach((id, index) => {
260
+ e.preventDefault();
150
261
  if (id === this.getSelectedId()) {
151
262
  const count = this.tabList.length;
152
263
  const nextIndex = index - 1 >= 0 ? index - 1 : count - 1;
@@ -158,6 +269,7 @@ var Tabs = class extends React2.Component {
158
269
  // right arrow
159
270
  case 39:
160
271
  this.tabList.forEach((id, index) => {
272
+ e.preventDefault();
161
273
  if (id === this.getSelectedId()) {
162
274
  const count = this.tabList.length;
163
275
  const nextIndex = index + 1 < count ? index + 1 : 0;
@@ -171,30 +283,56 @@ var Tabs = class extends React2.Component {
171
283
  }
172
284
  };
173
285
  render() {
174
- const { children, qa, onSelect, ...rest } = this.props;
175
- return /* @__PURE__ */ jsx(
286
+ const {
287
+ children,
288
+ qa,
289
+ onSelect,
290
+ "aria-label": ariaLabel,
291
+ ...rest
292
+ } = this.props;
293
+ return /* @__PURE__ */ jsxs(
176
294
  styles_default,
177
295
  {
296
+ ref: this.containerRef,
178
297
  "data-qa-tabs": "",
179
- onKeyUp: this.keyHandler,
298
+ onKeyDown: this.keyHandler,
180
299
  onSelect,
181
300
  role: "tablist",
301
+ "aria-label": ariaLabel,
182
302
  ...qa,
183
303
  ...rest,
184
- children: /* @__PURE__ */ jsx(
185
- TabButtonContext.Provider,
186
- {
187
- value: {
188
- selectedId: this.getSelectedId(),
189
- fullWidth: this.props.fullWidth,
190
- onActivate: this.onActivate,
191
- onTabMount: this.onTabMount,
192
- onTabUpdate: this.onTabUpdate,
193
- onTabUnmount: this.onTabUnmount
194
- },
195
- children
196
- }
197
- )
304
+ children: [
305
+ /* @__PURE__ */ jsx(
306
+ "div",
307
+ {
308
+ ref: this.startSentinelRef,
309
+ "aria-hidden": "true",
310
+ style: { flexShrink: 0, width: 1 }
311
+ }
312
+ ),
313
+ /* @__PURE__ */ jsx(
314
+ TabButtonContext.Provider,
315
+ {
316
+ value: {
317
+ selectedId: this.getSelectedId(),
318
+ fullWidth: this.props.fullWidth,
319
+ onActivate: this.onActivate,
320
+ onTabMount: this.onTabMount,
321
+ onTabUpdate: this.onTabUpdate,
322
+ onTabUnmount: this.onTabUnmount
323
+ },
324
+ children
325
+ }
326
+ ),
327
+ /* @__PURE__ */ jsx(
328
+ "div",
329
+ {
330
+ ref: this.endSentinelRef,
331
+ "aria-hidden": "true",
332
+ style: { flexShrink: 0, width: 1 }
333
+ }
334
+ )
335
+ ]
198
336
  }
199
337
  );
200
338
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Tabs.tsx","../../src/styles.ts","../../src/TabsTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport Container, { TabButton } from \"./styles\";\nimport type {\n TypeTabButtonsProps,\n TypeTabsProps,\n TypeTabPanelProps,\n} from \"./TabsTypes\";\n\ninterface TypeTabButtonRef {\n current: null | React.ElementRef<\"button\">;\n}\ninterface TypeTabButtonContext {\n onActivate: (arg0: string) => void;\n onTabMount: (id: string, ref: TypeTabButtonRef) => void;\n onTabUpdate: (previousId: string, nextId: string) => void;\n onTabUnmount: (id: string) => void;\n selectedId: string;\n fullWidth?: boolean;\n}\n\nconst TabButtonContext = React.createContext<Partial<TypeTabButtonContext>>({});\n\nclass TabItemButton extends React.Component<TypeTabButtonsProps> {\n static override contextType = TabButtonContext;\n // @ts-notes - using the legacy syntax here because `declare` does not play well with babel\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n //@ts-ignore\n context!: React.ContextType<typeof TabButtonContext>;\n\n buttonRef: TypeTabButtonRef = React.createRef<React.ElementRef<\"button\">>();\n\n override componentDidMount() {\n this.context.onTabMount?.(this.props.id, this.buttonRef);\n }\n\n override componentDidUpdate(prevProps: TypeTabButtonsProps) {\n if (prevProps.id !== this.props.id) {\n this.context.onTabUpdate?.(prevProps.id, this.props.id);\n }\n }\n\n override componentWillUnmount() {\n this.context.onTabUnmount?.(this.props.id);\n }\n\n override render() {\n const { children, id, ...rest } = this.props;\n const { selectedId, onActivate, fullWidth } = this.context;\n const isSelected = selectedId === id;\n\n return (\n <TabButton\n key={id}\n innerRef={this.buttonRef}\n id={id}\n onClick={() => onActivate?.(id)}\n isSelected={isSelected}\n tabIndex={isSelected ? 0 : -1}\n fullWidth={fullWidth}\n data-qa-tab-button={id}\n data-qa-tab-button-state={isSelected}\n aria-selected={isSelected}\n role=\"tab\"\n aria-controls={`${id}-panel`}\n {...rest}\n >\n {children}\n </TabButton>\n );\n }\n}\n\nconst TabPanel: React.FC<TypeTabPanelProps> = ({ children, id, ...rest }) => {\n const panelId = `${id}-panel`;\n\n return (\n <div id={panelId} role=\"tabpanel\" aria-labelledby={id} {...rest}>\n {children}\n </div>\n );\n};\n\n/**\n * Render a group of buttons in a tab-heading style\n */\nclass Tabs extends React.Component<TypeTabsProps> {\n static Button = TabItemButton;\n static Panel = TabPanel;\n buttonRefs: Record<string, TypeTabButtonRef | null | undefined> = {};\n tabList: string[] = [];\n\n getSelectedId = () => this.props.selectedId;\n onActivate = (id: string) => this.props.onSelect(id);\n onTabMount = (id: string, ref: TypeTabButtonRef) => {\n if (!this.tabList.includes(id)) {\n this.tabList.push(id);\n this.buttonRefs[id] = ref;\n }\n };\n\n onTabUpdate = (previousId: string, newId: string) => {\n if (this.tabList.includes(previousId)) {\n this.tabList = this.tabList.map((id) => (id === previousId ? newId : id));\n this.buttonRefs[newId] = this.buttonRefs[previousId];\n this.buttonRefs[previousId] = undefined;\n }\n };\n\n onTabUnmount = (id: string) => {\n if (this.tabList.includes(id)) {\n this.tabList = this.tabList.filter((currentId) => currentId !== id);\n this.buttonRefs[id] = undefined;\n }\n };\n\n selectNextTab = (id: string) => {\n const buttonRef = this.buttonRefs[id];\n this.props.onSelect(id);\n buttonRef &&\n buttonRef.current &&\n buttonRef.current.focus &&\n buttonRef.current.focus();\n };\n\n keyHandler = ({ keyCode }: React.KeyboardEvent<HTMLDivElement>) => {\n switch (keyCode) {\n // left arrow\n case 37:\n this.tabList.forEach((id, index) => {\n if (id === this.getSelectedId()) {\n const count = this.tabList.length;\n const nextIndex = index - 1 >= 0 ? index - 1 : count - 1;\n const nextId = this.tabList[nextIndex];\n this.selectNextTab(nextId ? nextId : \"\");\n }\n });\n break;\n\n // right arrow\n case 39:\n this.tabList.forEach((id, index) => {\n if (id === this.getSelectedId()) {\n const count = this.tabList.length;\n const nextIndex = index + 1 < count ? index + 1 : 0;\n const nextId = this.tabList[nextIndex];\n this.selectNextTab(nextId ? nextId : \"\");\n }\n });\n break;\n\n default:\n break;\n }\n };\n\n override render() {\n const { children, qa, onSelect, ...rest } = this.props;\n return (\n <Container\n data-qa-tabs=\"\"\n onKeyUp={this.keyHandler}\n onSelect={onSelect}\n role=\"tablist\"\n {...qa}\n {...rest}\n >\n <TabButtonContext.Provider\n value={{\n selectedId: this.getSelectedId(),\n fullWidth: this.props.fullWidth,\n onActivate: this.onActivate,\n onTabMount: this.onTabMount,\n onTabUpdate: this.onTabUpdate,\n onTabUnmount: this.onTabUnmount,\n }}\n >\n {children}\n </TabButtonContext.Provider>\n </Container>\n );\n }\n}\n\nexport default Tabs;\n","import styled, { css } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport { Button } from \"@sproutsocial/seeds-react-button\";\nimport { type TypeTabsProps } from \"./TabsTypes\";\n\ninterface TypeTabState {\n isSelected: boolean;\n}\ntype TypeFullWidth = Pick<TypeTabsProps, \"fullWidth\">;\ninterface TypeStyleProps extends TypeFullWidth, TypeTabState {}\n\nconst Container = styled.div<TypeTabsProps>`\n display: ${(props) => (props.fullWidth ? \"flex\" : \"inline-flex\")};\n justify-content: space-between;\n margin: 0;\n padding: 0;\n border-bottom: ${(props) => props.theme.borders[500]}\n ${(props) => props.theme.colors.container.border.base};\n\n ${COMMON}\n`;\n\nexport const TabButton = styled(Button)<TypeStyleProps>`\n margin-bottom: -1px;\n padding: ${(props) => `${props.theme.space[350]} 0`};\n color: ${(props) => props.theme.colors.text.headline};\n border-radius: 0;\n border-width: 0 0 3px;\n width: 100%;\n\n ${(props) =>\n props.fullWidth &&\n css`\n flex-grow: 1;\n `};\n\n &:not(:last-child) {\n ${(props) =>\n !props.fullWidth &&\n css`\n margin-right: ${(props) => props.theme.space[350]};\n `};\n }\n\n ${(props) =>\n props.isSelected &&\n css`\n color: ${(props) => props.theme.colors.link.base};\n border-color: transparent transparent\n ${(props) => props.theme.colors.button.primary.background.base};\n `};\n\n &:hover {\n ${(props) =>\n props.isSelected &&\n css`\n color: ${(props) => props.theme.colors.link.hover};\n `};\n }\n\n &:focus,\n &:active {\n box-shadow: none;\n }\n\n &:active {\n transform: none;\n }\n`;\nexport default Container;\n","import * as React from \"react\";\nimport type {\n TypeStyledComponentsCommonProps,\n TypeSystemCommonProps,\n} from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeButtonProps } from \"@sproutsocial/seeds-react-button\";\n\nexport interface TypeTabButtonsProps extends TypeButtonProps {\n children: React.ReactNode;\n\n /** Should be unique among sibling elements */\n id: string;\n}\n\nexport interface TypeTabPanelProps\n extends React.ComponentPropsWithoutRef<\"div\"> {\n children: React.ReactNode;\n\n /** Should match the corresponding tab button id */\n id: string;\n}\n\nexport interface TypeTabsProps\n extends TypeStyledComponentsCommonProps,\n TypeSystemCommonProps,\n Omit<\n React.ComponentPropsWithoutRef<\"ul\">,\n keyof TypeSystemCommonProps | \"onSelect\"\n > {\n children: React.ReactNode;\n onSelect: (arg0: string) => void;\n\n /** Whether or not the tabs should stretch to fill the width of their container */\n fullWidth?: boolean;\n qa?: object;\n\n /** ID of the selected tab */\n selectedId: string;\n}\n","import Tabs from \"./Tabs\";\n\nexport default Tabs;\nexport { Tabs };\nexport * from \"./TabsTypes\";\n"],"mappings":"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","names":["React","props"]}
1
+ {"version":3,"sources":["../../src/Tabs.tsx","../../src/styles.ts","../../src/TabsTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport Container, { TabButton } from \"./styles\";\nimport type {\n TypeTabButtonsProps,\n TypeTabsProps,\n TypeTabPanelProps,\n} from \"./TabsTypes\";\n\ninterface TypeTabButtonRef {\n current: null | React.ElementRef<\"button\">;\n}\ninterface TypeTabButtonContext {\n onActivate: (arg0: string) => void;\n onTabMount: (id: string, ref: TypeTabButtonRef) => void;\n onTabUpdate: (previousId: string, nextId: string) => void;\n onTabUnmount: (id: string) => void;\n selectedId: string;\n fullWidth?: boolean;\n}\n\nconst TabButtonContext = React.createContext<Partial<TypeTabButtonContext>>({});\n\nclass TabItemButton extends React.Component<TypeTabButtonsProps> {\n static override contextType = TabButtonContext;\n // @ts-notes - using the legacy syntax here because `declare` does not play well with babel\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n //@ts-ignore\n context!: React.ContextType<typeof TabButtonContext>;\n\n buttonRef: TypeTabButtonRef = React.createRef<React.ElementRef<\"button\">>();\n\n override componentDidMount() {\n this.context.onTabMount?.(this.props.id, this.buttonRef);\n }\n\n override componentDidUpdate(prevProps: TypeTabButtonsProps) {\n if (prevProps.id !== this.props.id) {\n this.context.onTabUpdate?.(prevProps.id, this.props.id);\n }\n }\n\n override componentWillUnmount() {\n this.context.onTabUnmount?.(this.props.id);\n }\n\n override render() {\n const { children, id, ...rest } = this.props;\n const { selectedId, onActivate, fullWidth } = this.context;\n const isSelected = selectedId === id;\n\n return (\n <TabButton\n key={id}\n innerRef={this.buttonRef}\n id={id}\n onClick={() => onActivate?.(id)}\n isSelected={isSelected}\n tabIndex={isSelected ? 0 : -1}\n fullWidth={fullWidth}\n data-qa-tab-button={id}\n data-qa-tab-button-state={isSelected}\n aria-selected={isSelected}\n role=\"tab\"\n aria-controls={`${id}-panel`}\n {...rest}\n >\n {children}\n </TabButton>\n );\n }\n}\n\nconst TabPanel: React.FC<TypeTabPanelProps> = ({ children, id, ...rest }) => {\n const panelId = `${id}-panel`;\n\n return (\n <div id={panelId} role=\"tabpanel\" aria-labelledby={id} {...rest}>\n {children}\n </div>\n );\n};\n\n/**\n * Render a group of buttons in a tab-heading style\n */\nclass Tabs extends React.Component<TypeTabsProps> {\n static Button = TabItemButton;\n static Panel = TabPanel;\n buttonRefs: Record<string, TypeTabButtonRef | null | undefined> = {};\n tabList: string[] = [];\n\n containerRef = React.createRef<HTMLDivElement>();\n startSentinelRef = React.createRef<HTMLDivElement>();\n endSentinelRef = React.createRef<HTMLDivElement>();\n startObserver: IntersectionObserver | null = null;\n endObserver: IntersectionObserver | null = null;\n\n override componentDidMount() {\n const container = this.containerRef.current;\n if (!container) return;\n container.addEventListener(\"wheel\", this.handleWheel, { passive: false });\n\n const startSentinel = this.startSentinelRef.current;\n const endSentinel = this.endSentinelRef.current;\n if (!startSentinel || !endSentinel) return;\n if (typeof IntersectionObserver === \"undefined\") return;\n this.startObserver = new IntersectionObserver(\n ([entry]) => {\n container.toggleAttribute(\"data-fade-left\", !entry?.isIntersecting);\n },\n { root: container, threshold: 1.0, rootMargin: \"0px 0px 0px 1px\" }\n );\n this.endObserver = new IntersectionObserver(\n ([entry]) => {\n container.toggleAttribute(\"data-fade-right\", !entry?.isIntersecting);\n },\n { root: container, threshold: 1.0, rootMargin: \"0px 1px 0px 0px\" }\n );\n this.startObserver.observe(startSentinel);\n this.endObserver.observe(endSentinel);\n }\n\n override componentWillUnmount() {\n this.containerRef.current?.removeEventListener(\"wheel\", this.handleWheel);\n this.startObserver?.disconnect();\n this.endObserver?.disconnect();\n this.startObserver = null;\n this.endObserver = null;\n }\n\n handleWheel = (e: WheelEvent) => {\n const container = this.containerRef.current;\n if (!container) return;\n // Only redirect pure-vertical wheel input. Trackpad horizontal swipes\n // (deltaX !== 0) already scroll natively — don't fight them.\n if (e.deltaY === 0 || e.deltaX !== 0) return;\n // No horizontal overflow means there's nothing to scroll.\n if (container.scrollWidth <= container.clientWidth) return;\n // Release at ends so the page can scroll past the tab bar instead of\n // trapping the user. WCAG 2.1.1 / 2.1.2 spirit.\n const atStart = container.scrollLeft <= 0;\n const atEnd =\n container.scrollLeft + container.clientWidth >= container.scrollWidth;\n if ((atEnd && e.deltaY > 0) || (atStart && e.deltaY < 0)) return;\n\n e.preventDefault();\n // Normalize deltaY across deltaMode units (Firefox can emit LINE/PAGE).\n const lineHeight = 16;\n const pixelDelta =\n e.deltaMode === 1\n ? e.deltaY * lineHeight\n : e.deltaMode === 2\n ? e.deltaY * container.clientHeight\n : e.deltaY;\n container.scrollBy({ left: pixelDelta, behavior: \"instant\" });\n };\n\n getSelectedId = () => this.props.selectedId;\n onActivate = (id: string) => {\n this.props.onSelect(id);\n this.scrollTabIntoView(id);\n };\n\n scrollTabIntoView = (id: string) => {\n const button = this.buttonRefs[id]?.current;\n const container = this.containerRef.current;\n if (!button || !container) return;\n const buttonRect = button.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const fadePadding = 48;\n const isFullyVisible =\n buttonRect.left >= containerRect.left + fadePadding &&\n buttonRect.right <= containerRect.right - fadePadding;\n if (isFullyVisible) return;\n button.scrollIntoView?.({ block: \"nearest\", inline: \"nearest\" });\n };\n onTabMount = (id: string, ref: TypeTabButtonRef) => {\n if (!this.tabList.includes(id)) {\n this.tabList.push(id);\n this.buttonRefs[id] = ref;\n }\n };\n\n onTabUpdate = (previousId: string, newId: string) => {\n if (this.tabList.includes(previousId)) {\n this.tabList = this.tabList.map((id) => (id === previousId ? newId : id));\n this.buttonRefs[newId] = this.buttonRefs[previousId];\n this.buttonRefs[previousId] = undefined;\n }\n };\n\n onTabUnmount = (id: string) => {\n if (this.tabList.includes(id)) {\n this.tabList = this.tabList.filter((currentId) => currentId !== id);\n this.buttonRefs[id] = undefined;\n }\n };\n\n selectNextTab = (id: string) => {\n this.props.onSelect(id);\n this.buttonRefs[id]?.current?.focus?.({ preventScroll: true });\n this.scrollTabIntoView(id);\n };\n\n keyHandler = (e: React.KeyboardEvent<HTMLDivElement>) => {\n switch (e.keyCode) {\n // left arrow\n case 37:\n this.tabList.forEach((id, index) => {\n e.preventDefault();\n if (id === this.getSelectedId()) {\n const count = this.tabList.length;\n const nextIndex = index - 1 >= 0 ? index - 1 : count - 1;\n const nextId = this.tabList[nextIndex];\n this.selectNextTab(nextId ? nextId : \"\");\n }\n });\n break;\n\n // right arrow\n case 39:\n this.tabList.forEach((id, index) => {\n e.preventDefault();\n if (id === this.getSelectedId()) {\n const count = this.tabList.length;\n const nextIndex = index + 1 < count ? index + 1 : 0;\n const nextId = this.tabList[nextIndex];\n this.selectNextTab(nextId ? nextId : \"\");\n }\n });\n break;\n\n default:\n break;\n }\n };\n\n override render() {\n const {\n children,\n qa,\n onSelect,\n \"aria-label\": ariaLabel,\n ...rest\n } = this.props;\n return (\n <Container\n ref={this.containerRef}\n data-qa-tabs=\"\"\n onKeyDown={this.keyHandler}\n onSelect={onSelect}\n role=\"tablist\"\n aria-label={ariaLabel}\n {...qa}\n {...rest}\n >\n <div\n ref={this.startSentinelRef}\n aria-hidden=\"true\"\n style={{ flexShrink: 0, width: 1 }}\n />\n <TabButtonContext.Provider\n value={{\n selectedId: this.getSelectedId(),\n fullWidth: this.props.fullWidth,\n onActivate: this.onActivate,\n onTabMount: this.onTabMount,\n onTabUpdate: this.onTabUpdate,\n onTabUnmount: this.onTabUnmount,\n }}\n >\n {children}\n </TabButtonContext.Provider>\n <div\n ref={this.endSentinelRef}\n aria-hidden=\"true\"\n style={{ flexShrink: 0, width: 1 }}\n />\n </Container>\n );\n }\n}\n\nexport default Tabs;\n","import styled, { css } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport { Button } from \"@sproutsocial/seeds-react-button\";\nimport { type TypeTabsProps } from \"./TabsTypes\";\n\ninterface TypeTabState {\n isSelected: boolean;\n}\ntype TypeFullWidth = Pick<TypeTabsProps, \"fullWidth\">;\ninterface TypeStyleProps extends TypeFullWidth, TypeTabState {}\n\nconst Container = styled.div<TypeTabsProps>`\n display: ${(props) => (props.fullWidth ? \"flex\" : \"inline-flex\")};\n margin: 0;\n padding: 0;\n min-width: 0;\n max-width: 100%;\n min-height: min-content;\n flex-shrink: 0;\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n scroll-padding-inline: 48px;\n scroll-behavior: smooth;\n border-bottom: ${(props) => props.theme.borders[500]}\n ${(props) => props.theme.colors.container.border.base};\n\n &::-webkit-scrollbar {\n display: none;\n }\n\n &[data-fade-right] {\n -webkit-mask-image: linear-gradient(\n to right,\n black 0,\n black calc(100% - 48px),\n transparent\n );\n mask-image: linear-gradient(\n to right,\n black 0,\n black calc(100% - 48px),\n transparent\n );\n }\n\n &[data-fade-left] {\n -webkit-mask-image: linear-gradient(\n to right,\n transparent,\n black 48px,\n black 100%\n );\n mask-image: linear-gradient(to right, transparent, black 48px, black 100%);\n }\n\n &[data-fade-left][data-fade-right] {\n -webkit-mask-image: linear-gradient(\n to right,\n transparent,\n black 48px,\n black calc(100% - 48px),\n transparent\n );\n mask-image: linear-gradient(\n to right,\n transparent,\n black 48px,\n black calc(100% - 48px),\n transparent\n );\n }\n\n ${COMMON}\n`;\n\nexport const TabButton = styled(Button)<TypeStyleProps>`\n margin-bottom: -1px;\n padding: ${(props) =>\n `${props.theme.space[350]} ${\n props.fullWidth ? 0 : props.theme.space[350]\n }`};\n color: ${(props) => props.theme.colors.text.headline};\n border-radius: 0;\n border-width: 0 0 3px;\n width: 100%;\n\n ${(props) =>\n props.fullWidth &&\n css`\n flex-grow: 1;\n `};\n\n ${(props) =>\n props.isSelected &&\n css`\n color: ${(props) => props.theme.colors.link.base};\n border-color: transparent transparent\n ${(props) => props.theme.colors.button.primary.background.base};\n `};\n\n &:hover {\n ${(props) =>\n props.isSelected &&\n css`\n color: ${(props) => props.theme.colors.link.hover};\n `};\n }\n\n &:focus,\n &:active {\n box-shadow: none;\n }\n\n &:active {\n transform: none;\n }\n`;\nexport default Container;\n","import * as React from \"react\";\nimport type {\n TypeStyledComponentsCommonProps,\n TypeSystemCommonProps,\n} from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeButtonProps } from \"@sproutsocial/seeds-react-button\";\n\nexport interface TypeTabButtonsProps extends TypeButtonProps {\n children: React.ReactNode;\n\n /** Should be unique among sibling elements */\n id: string;\n}\n\nexport interface TypeTabPanelProps\n extends React.ComponentPropsWithoutRef<\"div\"> {\n children: React.ReactNode;\n\n /** Should match the corresponding tab button id */\n id: string;\n}\n\nexport interface TypeTabsProps\n extends TypeStyledComponentsCommonProps,\n TypeSystemCommonProps,\n Omit<\n React.ComponentPropsWithoutRef<\"ul\">,\n keyof TypeSystemCommonProps | \"onSelect\"\n > {\n children: React.ReactNode;\n onSelect: (arg0: string) => void;\n\n /** Whether or not the tabs should stretch to fill the width of their container */\n fullWidth?: boolean;\n qa?: object;\n\n /** ID of the selected tab */\n selectedId: string;\n\n /**\n * Accessible label for the tab list. Forwarded to the `role=\"tablist\"`\n * element. Recommended by the WAI-ARIA Tabs pattern so screen readers\n * can announce what the tabs represent (e.g. \"Report sections\").\n * Use `aria-labelledby` instead via `{...rest}` if you have a visible\n * heading to reference.\n */\n \"aria-label\"?: string;\n}\n","import Tabs from \"./Tabs\";\n\nexport default Tabs;\nexport { Tabs };\nexport * from \"./TabsTypes\";\n"],"mappings":"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","names":["React","props"]}
package/dist/index.d.mts CHANGED
@@ -21,6 +21,14 @@ interface TypeTabsProps extends TypeStyledComponentsCommonProps, TypeSystemCommo
21
21
  qa?: object;
22
22
  /** ID of the selected tab */
23
23
  selectedId: string;
24
+ /**
25
+ * Accessible label for the tab list. Forwarded to the `role="tablist"`
26
+ * element. Recommended by the WAI-ARIA Tabs pattern so screen readers
27
+ * can announce what the tabs represent (e.g. "Report sections").
28
+ * Use `aria-labelledby` instead via `{...rest}` if you have a visible
29
+ * heading to reference.
30
+ */
31
+ "aria-label"?: string;
24
32
  }
25
33
 
26
34
  interface TypeTabButtonRef {
@@ -52,13 +60,22 @@ declare class Tabs extends React.Component<TypeTabsProps> {
52
60
  static Panel: React.FC<TypeTabPanelProps>;
53
61
  buttonRefs: Record<string, TypeTabButtonRef | null | undefined>;
54
62
  tabList: string[];
63
+ containerRef: React.RefObject<HTMLDivElement>;
64
+ startSentinelRef: React.RefObject<HTMLDivElement>;
65
+ endSentinelRef: React.RefObject<HTMLDivElement>;
66
+ startObserver: IntersectionObserver | null;
67
+ endObserver: IntersectionObserver | null;
68
+ componentDidMount(): void;
69
+ componentWillUnmount(): void;
70
+ handleWheel: (e: WheelEvent) => void;
55
71
  getSelectedId: () => string;
56
72
  onActivate: (id: string) => void;
73
+ scrollTabIntoView: (id: string) => void;
57
74
  onTabMount: (id: string, ref: TypeTabButtonRef) => void;
58
75
  onTabUpdate: (previousId: string, newId: string) => void;
59
76
  onTabUnmount: (id: string) => void;
60
77
  selectNextTab: (id: string) => void;
61
- keyHandler: ({ keyCode }: React.KeyboardEvent<HTMLDivElement>) => void;
78
+ keyHandler: (e: React.KeyboardEvent<HTMLDivElement>) => void;
62
79
  render(): react_jsx_runtime.JSX.Element;
63
80
  }
64
81
 
package/dist/index.d.ts CHANGED
@@ -21,6 +21,14 @@ interface TypeTabsProps extends TypeStyledComponentsCommonProps, TypeSystemCommo
21
21
  qa?: object;
22
22
  /** ID of the selected tab */
23
23
  selectedId: string;
24
+ /**
25
+ * Accessible label for the tab list. Forwarded to the `role="tablist"`
26
+ * element. Recommended by the WAI-ARIA Tabs pattern so screen readers
27
+ * can announce what the tabs represent (e.g. "Report sections").
28
+ * Use `aria-labelledby` instead via `{...rest}` if you have a visible
29
+ * heading to reference.
30
+ */
31
+ "aria-label"?: string;
24
32
  }
25
33
 
26
34
  interface TypeTabButtonRef {
@@ -52,13 +60,22 @@ declare class Tabs extends React.Component<TypeTabsProps> {
52
60
  static Panel: React.FC<TypeTabPanelProps>;
53
61
  buttonRefs: Record<string, TypeTabButtonRef | null | undefined>;
54
62
  tabList: string[];
63
+ containerRef: React.RefObject<HTMLDivElement>;
64
+ startSentinelRef: React.RefObject<HTMLDivElement>;
65
+ endSentinelRef: React.RefObject<HTMLDivElement>;
66
+ startObserver: IntersectionObserver | null;
67
+ endObserver: IntersectionObserver | null;
68
+ componentDidMount(): void;
69
+ componentWillUnmount(): void;
70
+ handleWheel: (e: WheelEvent) => void;
55
71
  getSelectedId: () => string;
56
72
  onActivate: (id: string) => void;
73
+ scrollTabIntoView: (id: string) => void;
57
74
  onTabMount: (id: string, ref: TypeTabButtonRef) => void;
58
75
  onTabUpdate: (previousId: string, newId: string) => void;
59
76
  onTabUnmount: (id: string) => void;
60
77
  selectNextTab: (id: string) => void;
61
- keyHandler: ({ keyCode }: React.KeyboardEvent<HTMLDivElement>) => void;
78
+ keyHandler: (e: React.KeyboardEvent<HTMLDivElement>) => void;
62
79
  render(): react_jsx_runtime.JSX.Element;
63
80
  }
64
81