@carbon/react 1.61.0 → 1.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,7 +17,7 @@ var debounce = require('lodash.debounce');
17
17
  var PropTypes = require('prop-types');
18
18
  var React = require('react');
19
19
  require('../Grid/FlexGrid.js');
20
- require('../Grid/Grid.js');
20
+ var Grid = require('../Grid/Grid.js');
21
21
  require('../Grid/Row.js');
22
22
  require('../Grid/Column.js');
23
23
  require('../Grid/ColumnHang.js');
@@ -49,6 +49,7 @@ var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
49
49
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
50
50
 
51
51
  var _ChevronLeft, _ChevronRight;
52
+ const verticalTabHeight = 64;
52
53
 
53
54
  // Used to manage the overall state of the Tabs
54
55
 
@@ -69,7 +70,7 @@ const TabContext = /*#__PURE__*/React__default["default"].createContext({
69
70
  hasSecondaryLabel: false
70
71
  });
71
72
  const lgMediaQuery = `(min-width: ${layout.breakpoints.lg.width})`;
72
- `(max-width: ${layout.breakpoints.md.width})`;
73
+ const smMediaQuery = `(max-width: ${layout.breakpoints.md.width})`;
73
74
 
74
75
  // Used to keep track of position in a list of tab panels
75
76
  const TabPanelContext = /*#__PURE__*/React__default["default"].createContext(0);
@@ -148,7 +149,47 @@ Tabs.propTypes = {
148
149
  */
149
150
  selectedIndex: PropTypes__default["default"].number
150
151
  };
151
- ({
152
+ function TabsVertical(_ref2) {
153
+ let {
154
+ children,
155
+ height,
156
+ defaultSelectedIndex = 0,
157
+ onChange,
158
+ selectedIndex: controlledSelectedIndex,
159
+ ...rest
160
+ } = _ref2;
161
+ const [selectedIndex, setSelectedIndex] = useControllableState.useControllableState({
162
+ value: controlledSelectedIndex,
163
+ defaultValue: defaultSelectedIndex,
164
+ onChange: value => onChange?.({
165
+ selectedIndex: value
166
+ })
167
+ });
168
+ const props = {
169
+ ...rest,
170
+ selectedIndex,
171
+ onChange: _ref3 => {
172
+ let {
173
+ selectedIndex
174
+ } = _ref3;
175
+ return setSelectedIndex(selectedIndex);
176
+ }
177
+ };
178
+ const isSm = useMatchMedia.useMatchMedia(smMediaQuery);
179
+ if (!isSm) {
180
+ return (
181
+ /*#__PURE__*/
182
+ // eslint-disable-next-line react/forbid-component-props
183
+ React__default["default"].createElement(Grid.Grid, {
184
+ style: {
185
+ height: height
186
+ }
187
+ }, /*#__PURE__*/React__default["default"].createElement(Tabs, props, children))
188
+ );
189
+ }
190
+ return /*#__PURE__*/React__default["default"].createElement(Tabs, props, children);
191
+ }
192
+ TabsVertical.propTypes = {
152
193
  /**
153
194
  * Provide child elements to be rendered inside the `TabsVertical`.
154
195
  * These elements should render either `TabsListVertical` or `TabsPanels`
@@ -173,7 +214,7 @@ Tabs.propTypes = {
173
214
  * in a controlled mode and should be used along with `onChange`
174
215
  */
175
216
  selectedIndex: PropTypes__default["default"].number
176
- });
217
+ };
177
218
 
178
219
  /**
179
220
  * Get the next index for a given keyboard event
@@ -194,6 +235,25 @@ function getNextIndex(event, total, index) {
194
235
  }
195
236
  }
196
237
 
238
+ /**
239
+ * Get the next index for a given keyboard event
240
+ * given a count of the total items and the current index
241
+ */
242
+ function getNextIndexVertical(event, total, index) {
243
+ switch (true) {
244
+ case match.match(event, keys.ArrowDown):
245
+ return (index + 1) % total;
246
+ case match.match(event, keys.ArrowUp):
247
+ return (total + index - 1) % total;
248
+ case match.match(event, keys.Home):
249
+ return 0;
250
+ case match.match(event, keys.End):
251
+ return total - 1;
252
+ default:
253
+ return index;
254
+ }
255
+ }
256
+
197
257
  /**
198
258
  * TabList
199
259
  */
@@ -488,7 +548,133 @@ TabList.propTypes = {
488
548
  */
489
549
  scrollIntoView: PropTypes__default["default"].bool
490
550
  };
491
- ({
551
+
552
+ /**
553
+ * TabListVertical
554
+ */
555
+
556
+ // type TabElement = HTMLElement & { disabled?: boolean };
557
+
558
+ function TabListVertical(_ref7) {
559
+ let {
560
+ activation = 'automatic',
561
+ 'aria-label': label,
562
+ children,
563
+ className: customClassName,
564
+ scrollIntoView,
565
+ ...rest
566
+ } = _ref7;
567
+ const {
568
+ activeIndex,
569
+ selectedIndex,
570
+ setSelectedIndex,
571
+ setActiveIndex
572
+ } = React__default["default"].useContext(TabsContext);
573
+ const prefix = usePrefix.usePrefix();
574
+ const ref = React.useRef(null);
575
+ const [isOverflowingBottom, setIsOverflowingBottom] = React.useState(false);
576
+ const [isOverflowingTop, setIsOverflowingTop] = React.useState(false);
577
+ const isSm = useMatchMedia.useMatchMedia(smMediaQuery);
578
+ const className = cx__default["default"](`${prefix}--tabs`, `${prefix}--tabs--vertical`, `${prefix}--tabs--contained`, customClassName);
579
+ const tabs = React.useRef([]);
580
+ function onKeyDown(event) {
581
+ if (match.matches(event, [keys.ArrowDown, keys.ArrowUp, keys.Home, keys.End])) {
582
+ event.preventDefault();
583
+ const filtredTabs = tabs.current.filter(tab => tab !== null);
584
+ const activeTabs = filtredTabs.filter(tab => !tab.disabled);
585
+ const currentIndex = activeTabs.indexOf(tabs.current[activation === 'automatic' ? selectedIndex : activeIndex]);
586
+ const nextIndex = tabs.current.indexOf(activeTabs[getNextIndexVertical(event, activeTabs.length, currentIndex)]);
587
+ if (activation === 'automatic') {
588
+ setSelectedIndex(nextIndex);
589
+ } else if (activation === 'manual') {
590
+ setActiveIndex(nextIndex);
591
+ }
592
+ tabs.current[nextIndex]?.focus();
593
+ }
594
+ }
595
+ useEffectOnce.useEffectOnce(() => {
596
+ if (tabs.current[selectedIndex]?.disabled) {
597
+ const activeTabs = tabs.current.filter(tab => {
598
+ return !tab.disabled;
599
+ });
600
+ if (activeTabs.length > 0) {
601
+ const tab = activeTabs[0];
602
+ setSelectedIndex(tabs.current.indexOf(tab));
603
+ }
604
+ }
605
+ });
606
+ React.useEffect(() => {
607
+ function handler() {
608
+ const containerHeight = ref.current?.offsetHeight;
609
+ const containerTop = ref.current?.getBoundingClientRect().top;
610
+ const selectedPositionTop = tabs.current[selectedIndex]?.getBoundingClientRect().top;
611
+ const halfTabHeight = verticalTabHeight / 2;
612
+ if (containerTop && containerHeight) {
613
+ // scrolls so selected tab is in view
614
+ if (selectedPositionTop - halfTabHeight < containerTop || selectedPositionTop - containerTop + verticalTabHeight + halfTabHeight > containerHeight) {
615
+ ref.current.scrollTo({
616
+ top: (selectedIndex - 1) * verticalTabHeight,
617
+ behavior: 'smooth'
618
+ });
619
+ }
620
+ }
621
+ }
622
+ window.addEventListener('resize', handler);
623
+ handler();
624
+ return () => {
625
+ window.removeEventListener('resize', handler);
626
+ };
627
+ }, [selectedIndex, scrollIntoView]);
628
+ React.useEffect(() => {
629
+ const element = ref.current;
630
+ if (!element) {
631
+ return;
632
+ }
633
+ const handler = () => {
634
+ const halfTabHeight = verticalTabHeight / 2;
635
+ setIsOverflowingBottom(element.scrollTop + element.clientHeight + halfTabHeight <= element.scrollHeight);
636
+ setIsOverflowingTop(element.scrollTop > halfTabHeight);
637
+ };
638
+ const resizeObserver = new ResizeObserver(() => handler());
639
+ resizeObserver.observe(element);
640
+ element.addEventListener('scroll', handler);
641
+ return () => {
642
+ resizeObserver.disconnect();
643
+ element.removeEventListener('scroll', handler);
644
+ };
645
+ });
646
+ if (isSm) {
647
+ return /*#__PURE__*/React__default["default"].createElement(TabList, _rollupPluginBabelHelpers["extends"]({}, rest, {
648
+ "aria-label": label,
649
+ contained: true
650
+ }), children);
651
+ }
652
+ return /*#__PURE__*/React__default["default"].createElement("div", {
653
+ className: className
654
+ }, isOverflowingTop && /*#__PURE__*/React__default["default"].createElement("div", {
655
+ className: `${prefix}--tab--list-gradient_top`
656
+ }), /*#__PURE__*/React__default["default"].createElement("div", _rollupPluginBabelHelpers["extends"]({}, rest, {
657
+ "aria-label": label,
658
+ ref: ref,
659
+ role: "tablist",
660
+ className: `${prefix}--tab--list`,
661
+ onKeyDown: onKeyDown
662
+ }), React__default["default"].Children.map(children, (child, index) => {
663
+ return !reactIs.isElement(child) ? null : /*#__PURE__*/React__default["default"].createElement(TabContext.Provider, {
664
+ value: {
665
+ index,
666
+ hasSecondaryLabel: false
667
+ }
668
+ }, /*#__PURE__*/React__default["default"].cloneElement(child, {
669
+ ref: node => {
670
+ tabs.current[index] = node;
671
+ }
672
+ }));
673
+ })), isOverflowingBottom && /*#__PURE__*/React__default["default"].createElement("div", {
674
+ className: `${prefix}--tab--list-gradient_bottom`
675
+ }));
676
+ }
677
+ TabListVertical.propTypes = {
492
678
  /**
493
679
  * Specify whether the content tab should be activated automatically or
494
680
  * manually
@@ -508,7 +694,7 @@ TabList.propTypes = {
508
694
  * Specify an optional className to be added to the container node
509
695
  */
510
696
  className: PropTypes__default["default"].string
511
- });
697
+ };
512
698
 
513
699
  /**
514
700
  * Helper function to set up the behavior when a button is "long pressed".
@@ -614,7 +800,7 @@ const Tab = /*#__PURE__*/React.forwardRef(function Tab(_ref8, forwardRef) {
614
800
  useEvent.useEvent(dismissIconRef, 'mouseleave', onDismissIconMouseLeave);
615
801
  React.useLayoutEffect(() => {
616
802
  function handler() {
617
- const elementTabId = document.getElementById(`${id}`);
803
+ const elementTabId = document.getElementById(`${id}`) || tabRef.current;
618
804
  const newElement = elementTabId?.getElementsByClassName(`${prefix}--tabs__nav-item-label`)[0];
619
805
  isEllipsisActive(newElement);
620
806
  }
@@ -1000,6 +1186,8 @@ TabPanels.propTypes = {
1000
1186
  exports.IconTab = IconTab;
1001
1187
  exports.Tab = Tab;
1002
1188
  exports.TabList = TabList;
1189
+ exports.TabListVertical = TabListVertical;
1003
1190
  exports.TabPanel = TabPanel;
1004
1191
  exports.TabPanels = TabPanels;
1005
1192
  exports.Tabs = Tabs;
1193
+ exports.TabsVertical = TabsVertical;
@@ -7,4 +7,4 @@
7
7
  import { Tabs } from './Tabs';
8
8
  export default Tabs;
9
9
  export { default as TabsSkeleton } from './Tabs.Skeleton';
10
- export { TabPanels, type TabPanelsProps, TabPanel, type TabPanelProps, TabList, type TabListProps, IconTab, type IconTabProps, Tabs, type TabsProps, } from './Tabs';
10
+ export { TabPanels, type TabPanelsProps, TabPanel, type TabPanelProps, TabList, type TabListProps, TabListVertical, type TabListVerticalProps, IconTab, type IconTabProps, Tabs, type TabsProps, TabsVertical, type TabsVerticalProps, } from './Tabs';
@@ -400,6 +400,7 @@ const ExpandableTile = /*#__PURE__*/React__default["default"].forwardRef(functio
400
400
  const [interactive, setInteractive] = React.useState(true);
401
401
  const aboveTheFold = React.useRef(null);
402
402
  const belowTheFold = React.useRef(null);
403
+ const chevronInteractiveRef = React.useRef(null);
403
404
  const tileContent = React.useRef(null);
404
405
  const tile = React.useRef(null);
405
406
  const ref = useMergedRefs.useMergedRefs([forwardRef, tile]);
@@ -431,7 +432,7 @@ const ExpandableTile = /*#__PURE__*/React__default["default"].forwardRef(functio
431
432
  setMaxHeight();
432
433
  }
433
434
  function handleKeyUp(evt) {
434
- if (evt.target !== tile.current) {
435
+ if (evt.target !== tile.current && evt.target !== chevronInteractiveRef.current) {
435
436
  if (match.matches(evt, [keys.Enter, keys.Space])) {
436
437
  evt.preventDefault();
437
438
  }
@@ -525,6 +526,7 @@ const ExpandableTile = /*#__PURE__*/React__default["default"].forwardRef(functio
525
526
  onKeyUp: events.composeEventHandlers([onKeyUp, handleKeyUp]),
526
527
  onClick: events.composeEventHandlers([onClick, handleClick]),
527
528
  "aria-label": isExpanded ? tileExpandedIconText : tileCollapsedIconText,
529
+ ref: chevronInteractiveRef,
528
530
  className: chevronInteractiveClassNames
529
531
  }, _ChevronDown || (_ChevronDown = /*#__PURE__*/React__default["default"].createElement(iconsReact.ChevronDown, null))), /*#__PURE__*/React__default["default"].createElement("div", {
530
532
  ref: belowTheFold,
package/lib/index.js CHANGED
@@ -361,9 +361,11 @@ exports.IconSwitch = IconSwitch["default"];
361
361
  exports.IconTab = Tabs.IconTab;
362
362
  exports.Tab = Tabs.Tab;
363
363
  exports.TabList = Tabs.TabList;
364
+ exports.TabListVertical = Tabs.TabListVertical;
364
365
  exports.TabPanel = Tabs.TabPanel;
365
366
  exports.TabPanels = Tabs.TabPanels;
366
367
  exports.Tabs = Tabs.Tabs;
368
+ exports.TabsVertical = Tabs.TabsVertical;
367
369
  exports.TabContent = TabContent["default"];
368
370
  exports.TabsSkeleton = Tabs_Skeleton["default"];
369
371
  exports.Tag = Tag["default"];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbon/react",
3
3
  "description": "React components for the Carbon Design System",
4
- "version": "1.61.0",
4
+ "version": "1.62.0",
5
5
  "license": "Apache-2.0",
6
6
  "main": "lib/index.js",
7
7
  "module": "es/index.js",
@@ -81,7 +81,7 @@
81
81
  "@babel/preset-typescript": "^7.21.5",
82
82
  "@carbon/test-utils": "^10.30.0",
83
83
  "@carbon/themes": "^11.37.0",
84
- "@figma/code-connect": "^1.0.1",
84
+ "@figma/code-connect": "^1.0.2",
85
85
  "@rollup/plugin-babel": "^6.0.0",
86
86
  "@rollup/plugin-commonjs": "^26.0.0",
87
87
  "@rollup/plugin-node-resolve": "^15.0.0",
@@ -141,5 +141,5 @@
141
141
  "**/*.scss",
142
142
  "**/*.css"
143
143
  ],
144
- "gitHead": "4e8fc6f3f11f2946d6a4b7f3462be833a63115dc"
144
+ "gitHead": "cc284f3cb6f7c289c95cf56dd602e67e6044a796"
145
145
  }