@primer/react 38.36.0 → 38.37.0-rc.05923388d

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/ActionList/Item.js +5 -4
  3. package/dist/AnchoredOverlay/AnchoredOverlay.js +5 -3
  4. package/dist/FeatureFlags/DefaultFeatureFlags.js +2 -1
  5. package/dist/Flash/Flash.d.ts +6 -0
  6. package/dist/Flash/Flash.js +6 -0
  7. package/dist/InlineMessage/InlineMessage.js +11 -10
  8. package/dist/LabelGroup/LabelGroup.js +6 -4
  9. package/dist/SegmentedControl/SegmentedControl-4ef72733.css +2 -0
  10. package/dist/SegmentedControl/SegmentedControl-4ef72733.css.map +1 -0
  11. package/dist/SegmentedControl/SegmentedControl.js +9 -5
  12. package/dist/SegmentedControl/SegmentedControl.module.css.js +1 -1
  13. package/dist/Textarea/{TextArea-53e27580.css → TextArea-2dc51d35.css} +2 -2
  14. package/dist/Textarea/TextArea-2dc51d35.css.map +1 -0
  15. package/dist/Textarea/TextArea.module.css.js +1 -1
  16. package/dist/Textarea/Textarea.d.ts +8 -0
  17. package/dist/Textarea/Textarea.js +128 -121
  18. package/dist/deprecated/index.d.ts +2 -1
  19. package/dist/deprecated/index.js +2 -1
  20. package/dist/experimental/Tabs/Tabs.js +33 -19
  21. package/dist/experimental/Tabs/types.d.ts +6 -0
  22. package/dist/experimental/Tabs/useTab.js +6 -1
  23. package/dist/experimental/Tabs/useTabList.js +29 -22
  24. package/dist/experimental/UnderlinePanels/UnderlinePanels.d.ts +38 -1
  25. package/dist/experimental/UnderlinePanels/UnderlinePanels.js +69 -18
  26. package/dist/hooks/useFocusTrap.js +9 -7
  27. package/dist/hooks/useFocusZone.js +16 -14
  28. package/dist/hooks/useMenuInitialFocus.js +4 -3
  29. package/generated/components.json +70 -11
  30. package/package.json +7 -7
  31. package/dist/SegmentedControl/SegmentedControl-de65527b.css +0 -2
  32. package/dist/SegmentedControl/SegmentedControl-de65527b.css.map +0 -1
  33. package/dist/Textarea/TextArea-53e27580.css.map +0 -1
@@ -1,4 +1,5 @@
1
1
  import useIsomorphicLayoutEffect from "../../utils/useIsomorphicLayoutEffect.js";
2
+ import { useFeatureFlag } from "../../FeatureFlags/useFeatureFlag.js";
2
3
  import { useControllableState } from "../../hooks/useControllableState.js";
3
4
  import { TabsContext } from "./TabsContext.js";
4
5
  import { c } from "react-compiler-runtime";
@@ -13,11 +14,13 @@ import React, { useId } from "react";
13
14
  * The `Tab`, `TabList`, and `TabPanel` components are provided for convenience to showcase the API & implementation.
14
15
  */
15
16
  function Tabs(props) {
16
- var _props$id, _props$defaultValue;
17
- const $ = c(13);
17
+ var _props$id, _props$activationMode, _props$defaultValue;
18
+ const $ = c(16);
18
19
  const { children, onValueChange } = props;
19
20
  const generatedId = useId();
20
21
  const groupId = (_props$id = props.id) !== null && _props$id !== void 0 ? _props$id : generatedId;
22
+ const controlledApiEnabled = useFeatureFlag("primer_react_underline_panels_controlled");
23
+ const activationMode = controlledApiEnabled ? (_props$activationMode = props.activationMode) !== null && _props$activationMode !== void 0 ? _props$activationMode : "automatic" : "automatic";
21
24
  const t0 = (_props$defaultValue = props.defaultValue) !== null && _props$defaultValue !== void 0 ? _props$defaultValue : props.value;
22
25
  let t1;
23
26
  if ($[0] !== props.value || $[1] !== t0) {
@@ -31,49 +34,60 @@ function Tabs(props) {
31
34
  $[2] = t1;
32
35
  } else t1 = $[2];
33
36
  const [selectedValue, setSelectedValue] = useControllableState(t1);
37
+ const [focusedValue, setFocusedValue] = React.useState();
34
38
  const savedOnValueChange = React.useRef(onValueChange);
35
39
  let t2;
36
- if ($[3] !== groupId || $[4] !== selectedValue || $[5] !== setSelectedValue) {
40
+ if ($[3] !== activationMode || $[4] !== controlledApiEnabled || $[5] !== focusedValue || $[6] !== groupId || $[7] !== selectedValue || $[8] !== setSelectedValue) {
37
41
  t2 = {
38
42
  groupId,
39
43
  selectedValue,
44
+ activationMode,
45
+ focusedValue,
40
46
  selectTab(value) {
41
47
  var _savedOnValueChange$c;
48
+ setFocusedValue(void 0);
49
+ if (controlledApiEnabled && value === selectedValue) return;
42
50
  setSelectedValue(value);
43
51
  (_savedOnValueChange$c = savedOnValueChange.current) === null || _savedOnValueChange$c === void 0 || _savedOnValueChange$c.call(savedOnValueChange, { value });
52
+ },
53
+ focusTab(value_0) {
54
+ setFocusedValue(value_0);
44
55
  }
45
56
  };
46
- $[3] = groupId;
47
- $[4] = selectedValue;
48
- $[5] = setSelectedValue;
49
- $[6] = t2;
50
- } else t2 = $[6];
57
+ $[3] = activationMode;
58
+ $[4] = controlledApiEnabled;
59
+ $[5] = focusedValue;
60
+ $[6] = groupId;
61
+ $[7] = selectedValue;
62
+ $[8] = setSelectedValue;
63
+ $[9] = t2;
64
+ } else t2 = $[9];
51
65
  const contextValue = t2;
52
66
  let t3;
53
67
  let t4;
54
- if ($[7] !== onValueChange) {
68
+ if ($[10] !== onValueChange) {
55
69
  t3 = () => {
56
70
  savedOnValueChange.current = onValueChange;
57
71
  };
58
72
  t4 = [onValueChange];
59
- $[7] = onValueChange;
60
- $[8] = t3;
61
- $[9] = t4;
73
+ $[10] = onValueChange;
74
+ $[11] = t3;
75
+ $[12] = t4;
62
76
  } else {
63
- t3 = $[8];
64
- t4 = $[9];
77
+ t3 = $[11];
78
+ t4 = $[12];
65
79
  }
66
80
  useIsomorphicLayoutEffect(t3, t4);
67
81
  let t5;
68
- if ($[10] !== children || $[11] !== contextValue) {
82
+ if ($[13] !== children || $[14] !== contextValue) {
69
83
  t5 = /*#__PURE__*/ jsx(TabsContext.Provider, {
70
84
  value: contextValue,
71
85
  children
72
86
  });
73
- $[10] = children;
74
- $[11] = contextValue;
75
- $[12] = t5;
76
- } else t5 = $[12];
87
+ $[13] = children;
88
+ $[14] = contextValue;
89
+ $[15] = t5;
90
+ } else t5 = $[15];
77
91
  return t5;
78
92
  }
79
93
  //#endregion
@@ -51,6 +51,12 @@ type CommonTabsProps = {
51
51
  * unique id is generated automatically.
52
52
  */
53
53
  id?: string;
54
+ /**
55
+ * `'automatic'` (default) selects on focus, so Arrow/Home/End keys select immediately. `'manual'`
56
+ * only moves focus; selection commits on Enter/Space/click. Prefer `'manual'` when displaying a
57
+ * panel is not instant (e.g. it triggers a network request).
58
+ */
59
+ activationMode?: 'automatic' | 'manual';
54
60
  };
55
61
  type TabsProps = PropsWithChildren<(ControlledTabsProps | UncontrolledTabsProps) & CommonTabsProps>;
56
62
  type Label = {
@@ -12,6 +12,7 @@ function useTab(props) {
12
12
  const selected = tabs.selectedValue === value;
13
13
  const id = `${tabs.groupId}-tab-${value}`;
14
14
  const panelId = `${tabs.groupId}-panel-${value}`;
15
+ const isTabStop = tabs.activationMode === "manual" && tabs.focusedValue !== void 0 ? tabs.focusedValue === value : selected;
15
16
  let t0;
16
17
  if ($[0] !== tabs || $[1] !== value) {
17
18
  t0 = function onKeyDown(event) {
@@ -37,6 +38,10 @@ function useTab(props) {
37
38
  let t2;
38
39
  if ($[7] !== disabled || $[8] !== selected || $[9] !== tabs || $[10] !== value) {
39
40
  t2 = function onFocus() {
41
+ if (tabs.activationMode === "manual") {
42
+ if (!disabled) tabs.focusTab(value);
43
+ return;
44
+ }
40
45
  if (!selected && !disabled) tabs.selectTab(value);
41
46
  };
42
47
  $[7] = disabled;
@@ -47,7 +52,7 @@ function useTab(props) {
47
52
  } else t2 = $[11];
48
53
  const onFocus = t2;
49
54
  const t3 = disabled ? true : void 0;
50
- const t4 = selected ? 0 : -1;
55
+ const t4 = isTabStop ? 0 : -1;
51
56
  let t5;
52
57
  if ($[12] !== id || $[13] !== onFocus || $[14] !== onKeyDown || $[15] !== onMouseDown || $[16] !== panelId || $[17] !== selected || $[18] !== t3 || $[19] !== t4) {
53
58
  t5 = { tabProps: {
@@ -5,20 +5,29 @@ import { c } from "react-compiler-runtime";
5
5
  import { useRef } from "react";
6
6
  //#region src/experimental/Tabs/useTabList.ts
7
7
  function useTabList(props) {
8
- const $ = c(9);
8
+ const $ = c(10);
9
9
  const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-orientation": ariaOrientation } = props;
10
10
  const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
11
+ const controlledApiEnabled = useFeatureFlag("primer_react_underline_panels_controlled");
11
12
  const tabListRef = useRef(null);
12
13
  const mergedRef = useMergedRefs(tabListRef, props.ref);
13
14
  const providedOrCreatedRef = useProvidedRefOrCreate(props.ref);
14
15
  const readRef = mergedRefEnabled ? tabListRef : providedOrCreatedRef;
15
16
  const appliedRef = mergedRefEnabled ? mergedRef : providedOrCreatedRef;
16
17
  let t0;
17
- if ($[0] !== ariaOrientation || $[1] !== readRef) {
18
+ if ($[0] !== ariaOrientation || $[1] !== controlledApiEnabled || $[2] !== readRef) {
18
19
  t0 = (event) => {
19
20
  const { current: tablist } = readRef;
20
21
  if (tablist === null) return;
21
22
  const tabs = getFocusableTabs(tablist);
23
+ const getCurrentIndex = () => {
24
+ if (controlledApiEnabled) {
25
+ const activeElement = tablist.ownerDocument.activeElement;
26
+ const focusedIndex = activeElement instanceof HTMLElement ? tabs.indexOf(activeElement) : -1;
27
+ if (focusedIndex !== -1) return focusedIndex;
28
+ }
29
+ return tabs.findIndex(_temp);
30
+ };
22
31
  const isVertical = ariaOrientation === "vertical";
23
32
  const nextKey = isVertical ? "ArrowDown" : "ArrowRight";
24
33
  const prevKey = isVertical ? "ArrowUp" : "ArrowLeft";
@@ -27,13 +36,13 @@ function useTabList(props) {
27
36
  event.stopPropagation();
28
37
  }
29
38
  if (event.key === nextKey) {
30
- const selectedTabIndex = tabs.findIndex(_temp);
31
- if (selectedTabIndex === -1) return;
32
- tabs[(selectedTabIndex + 1) % tabs.length].focus();
39
+ const currentTabIndex = getCurrentIndex();
40
+ if (currentTabIndex === -1) return;
41
+ tabs[(currentTabIndex + 1) % tabs.length].focus();
33
42
  } else if (event.key === prevKey) {
34
- const selectedTabIndex_0 = tabs.findIndex(_temp2);
35
- if (selectedTabIndex_0 === -1) return;
36
- tabs[(tabs.length + selectedTabIndex_0 - 1) % tabs.length].focus();
43
+ const currentTabIndex_0 = getCurrentIndex();
44
+ if (currentTabIndex_0 === -1) return;
45
+ tabs[(tabs.length + currentTabIndex_0 - 1) % tabs.length].focus();
37
46
  } else if (event.key === "Home") {
38
47
  if (tabs[0]) tabs[0].focus();
39
48
  } else if (event.key === "End") {
@@ -41,13 +50,14 @@ function useTabList(props) {
41
50
  }
42
51
  };
43
52
  $[0] = ariaOrientation;
44
- $[1] = readRef;
45
- $[2] = t0;
46
- } else t0 = $[2];
53
+ $[1] = controlledApiEnabled;
54
+ $[2] = readRef;
55
+ $[3] = t0;
56
+ } else t0 = $[3];
47
57
  const onKeyDown = t0;
48
58
  const t1 = ariaOrientation !== null && ariaOrientation !== void 0 ? ariaOrientation : "horizontal";
49
59
  let t2;
50
- if ($[3] !== appliedRef || $[4] !== ariaLabel || $[5] !== ariaLabelledby || $[6] !== onKeyDown || $[7] !== t1) {
60
+ if ($[4] !== appliedRef || $[5] !== ariaLabel || $[6] !== ariaLabelledby || $[7] !== onKeyDown || $[8] !== t1) {
51
61
  t2 = { tabListProps: {
52
62
  ref: appliedRef,
53
63
  "aria-label": ariaLabel,
@@ -56,18 +66,15 @@ function useTabList(props) {
56
66
  role: "tablist",
57
67
  onKeyDown
58
68
  } };
59
- $[3] = appliedRef;
60
- $[4] = ariaLabel;
61
- $[5] = ariaLabelledby;
62
- $[6] = onKeyDown;
63
- $[7] = t1;
64
- $[8] = t2;
65
- } else t2 = $[8];
69
+ $[4] = appliedRef;
70
+ $[5] = ariaLabel;
71
+ $[6] = ariaLabelledby;
72
+ $[7] = onKeyDown;
73
+ $[8] = t1;
74
+ $[9] = t2;
75
+ } else t2 = $[9];
66
76
  return t2;
67
77
  }
68
- function _temp2(tab_0) {
69
- return tab_0.getAttribute("aria-selected") === "true";
70
- }
71
78
  function _temp(tab) {
72
79
  return tab.getAttribute("aria-selected") === "true";
73
80
  }
@@ -16,6 +16,31 @@ type UnderlinePanelsProps = {
16
16
  * ID of the element containing the name for the tab list
17
17
  */
18
18
  'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
19
+ /**
20
+ * The value of the selected tab, keyed to each `UnderlinePanels.Tab`/`UnderlinePanels.Panel`
21
+ * `value`. Provide this (with `onChange`) for a controlled component where the selected tab is
22
+ * the single source of truth.
23
+ */
24
+ value?: string;
25
+ /**
26
+ * The value of the tab that should be selected by default, keyed to each tab's `value`. Use this
27
+ * for an uncontrolled component. Cannot be combined with `value`.
28
+ */
29
+ defaultValue?: string;
30
+ /**
31
+ * Fires whenever the selected tab changes, for every selection method including arrow keys.
32
+ * Unlike `UnderlinePanels.Tab`'s `onSelect`, which only fires on click and Enter/Space.
33
+ */
34
+ onChange?: ({
35
+ value
36
+ }: {
37
+ value: string;
38
+ }) => void;
39
+ /**
40
+ * `'automatic'` (default) selects on focus/arrow keys; `'manual'` only moves focus until
41
+ * Enter/Space/click. Prefer `'manual'` when displaying a panel is not instant.
42
+ */
43
+ activationMode?: 'automatic' | 'manual';
19
44
  /**
20
45
  * Custom string to use when generating the IDs of tabs and `aria-labelledby` for the panels
21
46
  */
@@ -34,6 +59,12 @@ type UnderlinePanelsProps = {
34
59
  as?: React.ElementType;
35
60
  };
36
61
  type TabProps = PropsWithChildren<{
62
+ /**
63
+ * A value that uniquely identifies this tab, paired with the `UnderlinePanels.Panel` of the same
64
+ * `value`. Provide this to use domain values (e.g. `'branch'`) with the container's controlled
65
+ * `value`/`onChange` API. When omitted, tabs and panels are paired by their DOM order.
66
+ */
67
+ value?: string;
37
68
  /**
38
69
  * Whether this is the selected tab
39
70
  */
@@ -51,7 +82,13 @@ type TabProps = PropsWithChildren<{
51
82
  */
52
83
  icon?: FC<IconProps>;
53
84
  }>;
54
- type PanelProps = React.HTMLAttributes<HTMLDivElement>;
85
+ type PanelProps = React.HTMLAttributes<HTMLDivElement> & {
86
+ /**
87
+ * A value that uniquely identifies this panel, paired with the `UnderlinePanels.Tab` of the same
88
+ * `value`. When omitted, tabs and panels are paired by their DOM order.
89
+ */
90
+ value?: string;
91
+ };
55
92
  declare const _default: React.FC<UnderlinePanelsProps> & SlotMarker & {
56
93
  Panel: FCWithSlotMarker<PanelProps>;
57
94
  Tab: FCWithSlotMarker<TabProps>;
@@ -1,7 +1,9 @@
1
1
  import useIsomorphicLayoutEffect from "../../utils/useIsomorphicLayoutEffect.js";
2
2
  import { useResizeObserver } from "../../hooks/useResizeObserver.js";
3
3
  import { useId as useId$1 } from "../../hooks/useId.js";
4
+ import { warning } from "../../utils/warning.js";
4
5
  import { isSlot } from "../../utils/is-slot.js";
6
+ import { useFeatureFlag } from "../../FeatureFlags/useFeatureFlag.js";
5
7
  import { invariant } from "../../utils/invariant.js";
6
8
  import { UnderlineItem, UnderlineItemList, UnderlineWrapper } from "../../internal/components/UnderlineTabbedInterface.js";
7
9
  import UnderlinePanels_module_css_default from "./UnderlinePanels.module.css.js";
@@ -17,43 +19,78 @@ const UnderlinePanelsContext = /*#__PURE__*/ createContext({
17
19
  iconsVisible: true,
18
20
  loadingCounters: void 0
19
21
  });
20
- const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, children, loadingCounters, className, ...props }) => {
22
+ const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, children, loadingCounters, className, value: valueProp, defaultValue: defaultValueProp, onChange: onChangeProp, activationMode = "automatic", ...props }) => {
23
+ var _tabValues$;
24
+ const controlledApiEnabled = useFeatureFlag("primer_react_underline_panels_controlled");
25
+ const controlledValue = controlledApiEnabled ? valueProp : void 0;
26
+ const defaultValue = controlledApiEnabled ? defaultValueProp : void 0;
27
+ const onChange = controlledApiEnabled ? onChangeProp : void 0;
21
28
  const [iconsVisible, setIconsVisible] = useState(true);
22
29
  const wrapperRef = useRef(null);
23
30
  const listRef = useRef(null);
24
31
  const parentId = useId$1(props.id);
25
- const [tabs, tabPanels, tabsHaveIcons, selectedFromProps] = useMemo(() => {
32
+ const [tabs, tabPanels, tabsHaveIcons, selectedFromProps, tabValues, panelValues] = useMemo(() => {
26
33
  let tabIndex = 0;
27
34
  let panelIndex = 0;
28
35
  const childrenWithProps = Children.map(children, (child) => {
29
- if (/*#__PURE__*/ isValidElement(child) && (child.type === Tab || isSlot(child, Tab))) return /*#__PURE__*/ cloneElement(child, { value: `${tabIndex++}` });
30
- if (/*#__PURE__*/ isValidElement(child) && (child.type === Panel || isSlot(child, Panel))) return /*#__PURE__*/ cloneElement(child, { value: `${panelIndex++}` });
36
+ if (/*#__PURE__*/ isValidElement(child) && (child.type === Tab || isSlot(child, Tab))) {
37
+ var _ref;
38
+ const value = (_ref = controlledApiEnabled ? child.props.value : void 0) !== null && _ref !== void 0 ? _ref : `${tabIndex}`;
39
+ tabIndex++;
40
+ return /*#__PURE__*/ cloneElement(child, { value });
41
+ }
42
+ if (/*#__PURE__*/ isValidElement(child) && (child.type === Panel || isSlot(child, Panel))) {
43
+ var _ref2;
44
+ const value = (_ref2 = controlledApiEnabled ? child.props.value : void 0) !== null && _ref2 !== void 0 ? _ref2 : `${panelIndex}`;
45
+ panelIndex++;
46
+ return /*#__PURE__*/ cloneElement(child, { value });
47
+ }
31
48
  return child;
32
49
  });
33
50
  const tabs = [];
34
51
  const tabPanels = [];
52
+ const tabValues = [];
53
+ const panelValues = [];
35
54
  let selectedFromProps;
36
55
  for (const child of Children.toArray(childrenWithProps)) {
37
56
  if (!/*#__PURE__*/ isValidElement(child)) continue;
38
57
  if (child.type === Tab || isSlot(child, Tab)) {
58
+ const value = child.props.value;
59
+ if (value !== void 0) tabValues.push(value);
39
60
  const ariaSelected = child.props["aria-selected"];
40
- if (ariaSelected === true || ariaSelected === "true") selectedFromProps = `${tabs.length}`;
61
+ if (ariaSelected === true || ariaSelected === "true") selectedFromProps = value;
41
62
  tabs.push(child);
42
- } else if (child.type === Panel || isSlot(child, Panel)) tabPanels.push(child);
63
+ } else if (child.type === Panel || isSlot(child, Panel)) {
64
+ const value = child.props.value;
65
+ if (value !== void 0) panelValues.push(value);
66
+ tabPanels.push(child);
67
+ }
43
68
  }
44
69
  return [
45
70
  tabs,
46
71
  tabPanels,
47
72
  tabs.some((tab) => /*#__PURE__*/ React.isValidElement(tab) && tab.props.icon),
48
- selectedFromProps
73
+ selectedFromProps,
74
+ tabValues,
75
+ panelValues
49
76
  ];
50
- }, [children]);
51
- const [selectedValue, setSelectedValue] = useState(() => selectedFromProps !== null && selectedFromProps !== void 0 ? selectedFromProps : "0");
77
+ }, [children, controlledApiEnabled]);
78
+ const isControlled = controlledValue !== void 0;
79
+ const [uncontrolledValue, setUncontrolledValue] = useState(() => {
80
+ var _ref3;
81
+ return (_ref3 = defaultValue !== null && defaultValue !== void 0 ? defaultValue : selectedFromProps) !== null && _ref3 !== void 0 ? _ref3 : "0";
82
+ });
52
83
  const [prevSelectedFromProps, setPrevSelectedFromProps] = useState(selectedFromProps);
53
- if (selectedFromProps !== prevSelectedFromProps) {
84
+ if (!isControlled && selectedFromProps !== prevSelectedFromProps) {
54
85
  setPrevSelectedFromProps(selectedFromProps);
55
- if (selectedFromProps !== void 0 && selectedFromProps !== selectedValue) setSelectedValue(selectedFromProps);
86
+ if (selectedFromProps !== void 0 && selectedFromProps !== uncontrolledValue) setUncontrolledValue(selectedFromProps);
56
87
  }
88
+ const selectedValue = isControlled ? controlledValue : uncontrolledValue;
89
+ const effectiveValue = tabValues.includes(selectedValue) ? selectedValue : (_tabValues$ = tabValues[0]) !== null && _tabValues$ !== void 0 ? _tabValues$ : selectedValue;
90
+ const handleValueChange = (value) => {
91
+ if (!isControlled) setUncontrolledValue(value);
92
+ onChange === null || onChange === void 0 || onChange({ value });
93
+ };
57
94
  const { tabListProps } = useTabList({
58
95
  "aria-label": ariaLabel,
59
96
  "aria-labelledby": ariaLabelledBy,
@@ -78,17 +115,31 @@ const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabel
78
115
  const wrapperWidth = resizeObserverEntries[0].contentRect.width;
79
116
  setIconsVisible(wrapperWidth > listWidthRef.current);
80
117
  }, wrapperRef, []);
81
- !(tabs.filter((tab) => {
82
- const ariaSelected = /*#__PURE__*/ React.isValidElement(tab) && tab.props["aria-selected"];
83
- return ariaSelected === true || ariaSelected === "true";
84
- }).length <= 1) && invariant(false, "Only one tab can be selected at a time.");
85
- !(tabs.length === tabPanels.length) && invariant(false, `The number of tabs and panels must be equal. Counted ${tabs.length} tabs and ${tabPanels.length} panels.`);
118
+ {
119
+ const selectedTabs = tabs.filter((tab) => {
120
+ const ariaSelected = /*#__PURE__*/ React.isValidElement(tab) && tab.props["aria-selected"];
121
+ return ariaSelected === true || ariaSelected === "true";
122
+ });
123
+ !(selectedTabs.length <= 1) && invariant(false, "Only one tab can be selected at a time.");
124
+ !(tabs.length === tabPanels.length) && invariant(false, `The number of tabs and panels must be equal. Counted ${tabs.length} tabs and ${tabPanels.length} panels.`);
125
+ const duplicateTabValue = tabValues.find((value, index) => tabValues.indexOf(value) !== index);
126
+ !(duplicateTabValue === void 0) && invariant(false, `Every tab must have a unique \`value\`. Found duplicate "${duplicateTabValue}".`);
127
+ const duplicatePanelValue = panelValues.find((value, index) => panelValues.indexOf(value) !== index);
128
+ !(duplicatePanelValue === void 0) && invariant(false, `Every panel must have a unique \`value\`. Found duplicate "${duplicatePanelValue}".`);
129
+ const unmatchedTabValue = tabValues.find((value) => !panelValues.includes(value));
130
+ !(unmatchedTabValue === void 0) && invariant(false, `Tab with \`value\` "${unmatchedTabValue}" has no matching panel. Give a \`UnderlinePanels.Panel\` the same \`value\`.`);
131
+ warning(isControlled && selectedTabs.length > 0, "UnderlinePanels: do not combine the controlled `value` prop with `aria-selected` on a tab. `value` takes precedence; use `value`/`onChange` (or `defaultValue`) to drive selection.");
132
+ warning(controlledValue !== void 0 && defaultValue !== void 0, "UnderlinePanels: do not combine the controlled `value` prop with `defaultValue`. `value` takes precedence; use one or the other.");
133
+ const providedValue = controlledValue !== null && controlledValue !== void 0 ? controlledValue : defaultValue;
134
+ warning(providedValue !== void 0 && tabValues.length > 0 && !tabValues.includes(providedValue), `UnderlinePanels: the selected value "${providedValue}" does not match any tab, so the first tab is selected instead. Provide a \`value\`/\`defaultValue\` that matches a \`UnderlinePanels.Tab\` \`value\`.`);
135
+ }
86
136
  return /*#__PURE__*/ jsx(UnderlinePanelsContext.Provider, {
87
137
  value: contextValue,
88
138
  children: /*#__PURE__*/ jsxs(Tabs, {
89
139
  id: parentId,
90
- value: selectedValue,
91
- onValueChange: ({ value }) => setSelectedValue(value),
140
+ value: effectiveValue,
141
+ activationMode,
142
+ onValueChange: ({ value }) => handleValueChange(value),
92
143
  children: [/*#__PURE__*/ jsx(UnderlineWrapper, {
93
144
  ref: wrapperRef,
94
145
  "data-icons-visible": iconsVisible,
@@ -27,13 +27,15 @@ function useFocusTrap(settings, dependencies = []) {
27
27
  }
28
28
  }
29
29
  React.useEffect(() => {
30
- if (containerRef.current instanceof HTMLElement) if (!disabled) {
31
- var _initialFocusRef$curr;
32
- abortController.current = focusTrap(containerRef.current, (_initialFocusRef$curr = initialFocusRef.current) !== null && _initialFocusRef$curr !== void 0 ? _initialFocusRef$curr : void 0);
33
- return () => {
34
- disableTrap();
35
- };
36
- } else disableTrap();
30
+ if (containerRef.current instanceof HTMLElement) {
31
+ if (!disabled) {
32
+ var _initialFocusRef$curr;
33
+ abortController.current = focusTrap(containerRef.current, (_initialFocusRef$curr = initialFocusRef.current) !== null && _initialFocusRef$curr !== void 0 ? _initialFocusRef$curr : void 0);
34
+ return () => {
35
+ disableTrap();
36
+ };
37
+ } else disableTrap();
38
+ }
37
39
  }, [
38
40
  containerRef,
39
41
  initialFocusRef,
@@ -10,20 +10,22 @@ function useFocusZone(settings = {}, dependencies = []) {
10
10
  const disabled = settings.disabled;
11
11
  const abortController = React.useRef();
12
12
  useEffect(() => {
13
- if (containerRef.current instanceof HTMLElement && (!useActiveDescendant || activeDescendantControlRef.current instanceof HTMLElement)) if (!disabled) {
14
- var _activeDescendantCont;
15
- const vanillaSettings = {
16
- ...settings,
17
- activeDescendantControl: (_activeDescendantCont = activeDescendantControlRef.current) !== null && _activeDescendantCont !== void 0 ? _activeDescendantCont : void 0
18
- };
19
- abortController.current = focusZone(containerRef.current, vanillaSettings);
20
- return () => {
21
- var _abortController$curr;
22
- (_abortController$curr = abortController.current) === null || _abortController$curr === void 0 || _abortController$curr.abort();
23
- };
24
- } else {
25
- var _abortController$curr2;
26
- (_abortController$curr2 = abortController.current) === null || _abortController$curr2 === void 0 || _abortController$curr2.abort();
13
+ if (containerRef.current instanceof HTMLElement && (!useActiveDescendant || activeDescendantControlRef.current instanceof HTMLElement)) {
14
+ if (!disabled) {
15
+ var _activeDescendantCont;
16
+ const vanillaSettings = {
17
+ ...settings,
18
+ activeDescendantControl: (_activeDescendantCont = activeDescendantControlRef.current) !== null && _activeDescendantCont !== void 0 ? _activeDescendantCont : void 0
19
+ };
20
+ abortController.current = focusZone(containerRef.current, vanillaSettings);
21
+ return () => {
22
+ var _abortController$curr;
23
+ (_abortController$curr = abortController.current) === null || _abortController$curr === void 0 || _abortController$curr.abort();
24
+ };
25
+ } else {
26
+ var _abortController$curr2;
27
+ (_abortController$curr2 = abortController.current) === null || _abortController$curr2 === void 0 || _abortController$curr2.abort();
28
+ }
27
29
  }
28
30
  }, [disabled, ...dependencies]);
29
31
  return {
@@ -43,9 +43,10 @@ const useMenuInitialFocus = (open, containerRef, anchorRef) => {
43
43
  t2 = function moveFocusOnOpen() {
44
44
  if (!open || !(containerRef !== null && containerRef !== void 0 && containerRef.current)) return;
45
45
  const iterable = iterateFocusableElements(containerRef.current);
46
- if (openingGesture === "mouse-click") if (anchorRef !== null && anchorRef !== void 0 && anchorRef.current) anchorRef.current.focus();
47
- else throw new Error("For focus management, please attach anchorRef");
48
- else if (openingGesture && [
46
+ if (openingGesture === "mouse-click") {
47
+ if (anchorRef !== null && anchorRef !== void 0 && anchorRef.current) anchorRef.current.focus();
48
+ else throw new Error("For focus management, please attach anchorRef");
49
+ } else if (openingGesture && [
49
50
  "ArrowDown",
50
51
  "Space",
51
52
  "Enter"