@primer/react 38.34.0-rc.f86dc8614 → 38.35.0-rc.f27c5cca1

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 (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/ActionList/Group.js +1 -1
  3. package/dist/ActionList/List.js +20 -14
  4. package/dist/AnchoredOverlay/AnchoredOverlay.js +6 -3
  5. package/dist/Autocomplete/AutocompleteInput.js +10 -7
  6. package/dist/ButtonGroup/ButtonGroup.js +15 -8
  7. package/dist/Checkbox/Checkbox.js +16 -9
  8. package/dist/Dialog/Dialog.js +12 -5
  9. package/dist/FeatureFlags/DefaultFeatureFlags.js +2 -1
  10. package/dist/FilteredActionList/FilteredActionList.js +31 -20
  11. package/dist/FilteredActionList/FilteredActionListInput.d.ts +1 -1
  12. package/dist/Link/Link.js +33 -33
  13. package/dist/Overlay/Overlay.js +1 -1
  14. package/dist/PageHeader/PageHeader.js +30 -21
  15. package/dist/Popover/Popover.d.ts +1 -0
  16. package/dist/Popover/Popover.js +84 -47
  17. package/dist/Radio/Radio.js +1 -1
  18. package/dist/SegmentedControl/{SegmentedControl-a004561f.css → SegmentedControl-de65527b.css} +2 -2
  19. package/dist/SegmentedControl/{SegmentedControl-a004561f.css.map → SegmentedControl-de65527b.css.map} +1 -1
  20. package/dist/SegmentedControl/SegmentedControl.module.css.js +1 -1
  21. package/dist/SegmentedControl/SegmentedControlButton.js +1 -1
  22. package/dist/SegmentedControl/SegmentedControlIconButton.js +1 -1
  23. package/dist/SelectPanel/SelectPanel.js +1 -1
  24. package/dist/TextInput/TextInput.js +13 -6
  25. package/dist/TooltipV2/Tooltip.js +18 -11
  26. package/dist/experimental/Tabs/Tabs.js +3 -2
  27. package/dist/experimental/Tabs/types.d.ts +10 -3
  28. package/dist/experimental/Tabs/useTabList.js +18 -10
  29. package/dist/experimental/UnderlinePanels/UnderlinePanels.js +69 -42
  30. package/dist/experimental/index.js +5 -5
  31. package/dist/hooks/useMergedRefs.d.ts +4 -4
  32. package/dist/hooks/useMergedRefs.js +8 -5
  33. package/dist/hooks/useProvidedRefOrCreate.d.ts +7 -10
  34. package/dist/hooks/useProvidedRefOrCreate.js +7 -10
  35. package/generated/components.json +21 -2
  36. package/package.json +8 -9
@@ -1,5 +1,7 @@
1
+ import { useMergedRefs } from "../hooks/useMergedRefs.js";
1
2
  import { useProvidedRefOrCreate } from "../hooks/useProvidedRefOrCreate.js";
2
3
  import { AriaStatus } from "../live-region/AriaStatus.js";
4
+ import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
3
5
  import Text_default from "../Text/Text.js";
4
6
  import _VisuallyHidden_module_css_default from "../_VisuallyHidden.module.css.js";
5
7
  import VisuallyHidden from "../_VisuallyHidden.js";
@@ -11,14 +13,19 @@ import UnstyledTextInput from "../internal/components/UnstyledTextInput.js";
11
13
  import { getCharacterCountState } from "../utils/character-counter.js";
12
14
  import { clsx } from "clsx";
13
15
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
14
- import React, { useCallback, useId, useState } from "react";
16
+ import React, { useCallback, useId, useRef, useState } from "react";
15
17
  import { isValidElementType } from "react-is";
16
18
  import { AlertFillIcon } from "@primer/octicons-react";
17
19
  //#region src/TextInput/TextInput.tsx
18
20
  const TextInput = /*#__PURE__*/ React.forwardRef(({ icon: IconComponent, leadingVisual: LeadingVisual, trailingVisual: TrailingVisual, trailingAction, block, className, contrast, disabled, loading, loaderPosition = "auto", loaderText = "Loading", monospace, validationStatus, size: sizeProp, onFocus, onBlur, variant: variantProp, width: widthProp, minWidth: minWidthProp, maxWidth: maxWidthProp, type = "text", required, characterLimit, onChange, value, defaultValue, "data-component": dataComponent, ...inputProps }, ref) => {
19
21
  var _counter$isOverLimit;
20
22
  const [isInputFocused, setIsInputFocused] = useState(false);
21
- const inputRef = useProvidedRefOrCreate(ref);
23
+ const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
24
+ const inputRef = useRef(null);
25
+ const mergedRef = useMergedRefs(inputRef, ref);
26
+ const providedOrCreatedRef = useProvidedRefOrCreate(ref);
27
+ const readRef = mergedRefEnabled ? inputRef : providedOrCreatedRef;
28
+ const appliedRef = mergedRefEnabled ? mergedRef : providedOrCreatedRef;
22
29
  const isControlled = value !== void 0;
23
30
  const [uncontrolledLength, setUncontrolledLength] = useState(() => defaultValue !== void 0 ? String(defaultValue).length : 0);
24
31
  const currentLength = isControlled ? String(value).length : uncontrolledLength;
@@ -29,9 +36,9 @@ const TextInput = /*#__PURE__*/ React.forwardRef(({ icon: IconComponent, leading
29
36
  const showTrailingLoadingIndicator = loading && (loaderPosition === "trailing" || Boolean(loaderPosition === "auto" && !LeadingVisual));
30
37
  const isSegmentedInputType = type === "date" || type === "time" || type === "datetime-local";
31
38
  const focusInput = (e) => {
32
- if (e.target !== inputRef.current || !isSegmentedInputType) {
33
- var _inputRef$current;
34
- (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.focus();
39
+ if (e.target !== readRef.current || !isSegmentedInputType) {
40
+ var _readRef$current;
41
+ (_readRef$current = readRef.current) === null || _readRef$current === void 0 || _readRef$current.focus();
35
42
  }
36
43
  };
37
44
  const leadingVisualId = useId();
@@ -88,7 +95,7 @@ const TextInput = /*#__PURE__*/ React.forwardRef(({ icon: IconComponent, leading
88
95
  children: typeof LeadingVisual !== "string" && isValidElementType(LeadingVisual) ? /*#__PURE__*/ jsx(LeadingVisual, {}) : LeadingVisual
89
96
  }),
90
97
  /*#__PURE__*/ jsx(UnstyledTextInput, {
91
- ref: inputRef,
98
+ ref: appliedRef,
92
99
  disabled,
93
100
  onFocus: handleInputFocus,
94
101
  onBlur: handleInputBlur,
@@ -1,7 +1,9 @@
1
+ import { useMergedRefs } from "../hooks/useMergedRefs.js";
1
2
  import { useProvidedRefOrCreate } from "../hooks/useProvidedRefOrCreate.js";
2
3
  import { useOnEscapePress } from "../hooks/useOnEscapePress.js";
3
4
  import { useId as useId$1 } from "../hooks/useId.js";
4
5
  import { warning } from "../utils/warning.js";
6
+ import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
5
7
  import { invariant } from "../utils/invariant.js";
6
8
  import Tooltip_module_css_default from "./Tooltip.module.css.js";
7
9
  import VisuallyHidden from "../_VisuallyHidden.js";
@@ -80,7 +82,12 @@ const emptyKeybindingHints = [];
80
82
  const Tooltip = /*#__PURE__*/ React.forwardRef(({ direction = "s", text, type = "description", children, id, className, keybindingHint = emptyKeybindingHints, delay = "short", _privateDisableTooltip = false, ...rest }, forwardedRef) => {
81
83
  const tooltipId = useId$1(id);
82
84
  const child = Children.only(children);
83
- const triggerRef = useProvidedRefOrCreate(forwardedRef);
85
+ const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
86
+ const triggerRef = useRef(null);
87
+ const mergedTriggerRef = useMergedRefs(triggerRef, forwardedRef);
88
+ const providedOrCreatedRef = useProvidedRefOrCreate(forwardedRef);
89
+ const readTriggerRef = mergedRefEnabled ? triggerRef : providedOrCreatedRef;
90
+ const appliedTriggerRef = mergedRefEnabled ? mergedTriggerRef : providedOrCreatedRef;
84
91
  const tooltipElRef = useRef(null);
85
92
  const [calculatedDirection, setCalculatedDirection] = useState(direction);
86
93
  const [isPopoverOpen, setIsPopoverOpen] = useState(false);
@@ -88,9 +95,9 @@ const Tooltip = /*#__PURE__*/ React.forwardRef(({ direction = "s", text, type =
88
95
  const { safeSetTimeout, safeClearTimeout } = useSafeTimeout();
89
96
  const openTooltip = () => {
90
97
  try {
91
- if (tooltipElRef.current && triggerRef.current && tooltipElRef.current.hasAttribute("popover") && !tooltipElRef.current.matches(":popover-open") && !_privateDisableTooltip) {
98
+ if (tooltipElRef.current && readTriggerRef.current && tooltipElRef.current.hasAttribute("popover") && !tooltipElRef.current.matches(":popover-open") && !_privateDisableTooltip) {
92
99
  const tooltip = tooltipElRef.current;
93
- const trigger = triggerRef.current;
100
+ const trigger = readTriggerRef.current;
94
101
  tooltip.showPopover();
95
102
  setIsPopoverOpen(true);
96
103
  const { top, left, anchorAlign, anchorSide } = getAnchoredPosition(tooltip, trigger, {
@@ -112,7 +119,7 @@ const Tooltip = /*#__PURE__*/ React.forwardRef(({ direction = "s", text, type =
112
119
  openTimeoutRef.current = null;
113
120
  }
114
121
  try {
115
- if (tooltipElRef.current && triggerRef.current && tooltipElRef.current.hasAttribute("popover") && tooltipElRef.current.matches(":popover-open")) {
122
+ if (tooltipElRef.current && readTriggerRef.current && tooltipElRef.current.hasAttribute("popover") && tooltipElRef.current.matches(":popover-open")) {
116
123
  tooltipElRef.current.hidePopover();
117
124
  setIsPopoverOpen(false);
118
125
  } else setIsPopoverOpen(false);
@@ -122,16 +129,16 @@ const Tooltip = /*#__PURE__*/ React.forwardRef(({ direction = "s", text, type =
122
129
  };
123
130
  const value = useMemo(() => ({ tooltipId }), [tooltipId]);
124
131
  useEffect(() => {
125
- if (!tooltipElRef.current || !triggerRef.current) return;
126
- const isTriggerInteractive = isInteractive(triggerRef.current);
127
- const triggerChildren = triggerRef.current.childNodes;
132
+ if (!tooltipElRef.current || !readTriggerRef.current) return;
133
+ const isTriggerInteractive = isInteractive(readTriggerRef.current);
134
+ const triggerChildren = readTriggerRef.current.childNodes;
128
135
  const hasInteractiveDescendant = Array.from(triggerChildren).some((child) => {
129
136
  return child instanceof HTMLElement && isInteractive(child) || Array.from(child.childNodes).some((grandChild) => grandChild instanceof HTMLElement && isInteractive(grandChild));
130
137
  });
131
138
  !(isTriggerInteractive || hasInteractiveDescendant) && invariant(false, "The `Tooltip` component expects a single React element that contains interactive content. Consider using a `<button>` or equivalent interactive element instead.");
132
139
  if (type === "label") {
133
- const hasAriaLabel = triggerRef.current.hasAttribute("aria-label");
134
- const hasAriaLabelInChildren = Array.from(triggerRef.current.childNodes).some((child) => child instanceof HTMLElement && child.hasAttribute("aria-label"));
140
+ const hasAriaLabel = readTriggerRef.current.hasAttribute("aria-label");
141
+ const hasAriaLabelInChildren = Array.from(readTriggerRef.current.childNodes).some((child) => child instanceof HTMLElement && child.hasAttribute("aria-label"));
135
142
  warning(hasAriaLabel || hasAriaLabelInChildren, "The label type `Tooltip` is going to be used here to label the trigger element. Please remove the aria-label from the trigger element.");
136
143
  }
137
144
  if (typeof window !== "undefined") {
@@ -140,7 +147,7 @@ const Tooltip = /*#__PURE__*/ React.forwardRef(({ direction = "s", text, type =
140
147
  tooltipElRef.current.setAttribute("popover", "auto");
141
148
  }, [
142
149
  tooltipElRef,
143
- triggerRef,
150
+ readTriggerRef,
144
151
  direction,
145
152
  type
146
153
  ]);
@@ -157,7 +164,7 @@ const Tooltip = /*#__PURE__*/ React.forwardRef(({ direction = "s", text, type =
157
164
  return /*#__PURE__*/ jsx(TooltipContext.Provider, {
158
165
  value,
159
166
  children: /*#__PURE__*/ jsxs(Fragment, { children: [/*#__PURE__*/ React.isValidElement(child) && /*#__PURE__*/ React.cloneElement(child, {
160
- ref: triggerRef,
167
+ ref: appliedTriggerRef,
161
168
  "aria-describedby": (() => {
162
169
  if (type !== "description") return child.props["aria-describedby"];
163
170
  const existingDescribedBy = child.props["aria-describedby"];
@@ -13,10 +13,11 @@ import React, { useId } from "react";
13
13
  * The `Tab`, `TabList`, and `TabPanel` components are provided for convenience to showcase the API & implementation.
14
14
  */
15
15
  function Tabs(props) {
16
- var _props$defaultValue;
16
+ var _props$id, _props$defaultValue;
17
17
  const $ = c(13);
18
18
  const { children, onValueChange } = props;
19
- const groupId = useId();
19
+ const generatedId = useId();
20
+ const groupId = (_props$id = props.id) !== null && _props$id !== void 0 ? _props$id : generatedId;
20
21
  const t0 = (_props$defaultValue = props.defaultValue) !== null && _props$defaultValue !== void 0 ? _props$defaultValue : props.value;
21
22
  let t1;
22
23
  if ($[0] !== props.value || $[1] !== t0) {
@@ -45,7 +45,14 @@ type UncontrolledTabsProps = {
45
45
  value: string;
46
46
  }) => void;
47
47
  };
48
- type TabsProps = PropsWithChildren<ControlledTabsProps | UncontrolledTabsProps>;
48
+ type CommonTabsProps = {
49
+ /**
50
+ * Optional id used as the base for generated tab and panel ids. If omitted, a
51
+ * unique id is generated automatically.
52
+ */
53
+ id?: string;
54
+ };
55
+ type TabsProps = PropsWithChildren<(ControlledTabsProps | UncontrolledTabsProps) & CommonTabsProps>;
49
56
  type Label = {
50
57
  'aria-label': string;
51
58
  };
@@ -73,7 +80,7 @@ type TabPanelProps = {
73
80
  value: string;
74
81
  };
75
82
  type TabListHookProps<T extends HTMLElement> = TabListProps & {
76
- /** Optional ref to use for the tablist. If none is provided, one will be generated automatically */ref?: React.RefObject<T>;
83
+ /** Optional ref to use for the tablist. If none is provided, one will be generated automatically */ref?: React.Ref<T | null>;
77
84
  };
78
85
  type TabListHookResult<T extends HTMLElement> = {
79
86
  /** Props to be spread onto the tablist element */tabListProps: {
@@ -81,7 +88,7 @@ type TabListHookResult<T extends HTMLElement> = {
81
88
  'aria-orientation': AriaAttributes['aria-orientation'];
82
89
  'aria-label': AriaAttributes['aria-label'];
83
90
  'aria-labelledby': AriaAttributes['aria-labelledby'];
84
- ref: React.RefObject<T | null>;
91
+ ref: React.Ref<T | null>;
85
92
  role: 'tablist';
86
93
  };
87
94
  };
@@ -1,14 +1,22 @@
1
+ import { useMergedRefs } from "../../hooks/useMergedRefs.js";
1
2
  import { useProvidedRefOrCreate } from "../../hooks/useProvidedRefOrCreate.js";
3
+ import { useFeatureFlag } from "../../FeatureFlags/useFeatureFlag.js";
2
4
  import { c } from "react-compiler-runtime";
5
+ import { useRef } from "react";
3
6
  //#region src/experimental/Tabs/useTabList.ts
4
7
  function useTabList(props) {
5
8
  const $ = c(9);
6
9
  const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-orientation": ariaOrientation } = props;
7
- const ref = useProvidedRefOrCreate(props.ref);
10
+ const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
11
+ const tabListRef = useRef(null);
12
+ const mergedRef = useMergedRefs(tabListRef, props.ref);
13
+ const providedOrCreatedRef = useProvidedRefOrCreate(props.ref);
14
+ const readRef = mergedRefEnabled ? tabListRef : providedOrCreatedRef;
15
+ const appliedRef = mergedRefEnabled ? mergedRef : providedOrCreatedRef;
8
16
  let t0;
9
- if ($[0] !== ariaOrientation || $[1] !== ref) {
17
+ if ($[0] !== ariaOrientation || $[1] !== readRef) {
10
18
  t0 = (event) => {
11
- const { current: tablist } = ref;
19
+ const { current: tablist } = readRef;
12
20
  if (tablist === null) return;
13
21
  const tabs = getFocusableTabs(tablist);
14
22
  const isVertical = ariaOrientation === "vertical";
@@ -33,25 +41,25 @@ function useTabList(props) {
33
41
  }
34
42
  };
35
43
  $[0] = ariaOrientation;
36
- $[1] = ref;
44
+ $[1] = readRef;
37
45
  $[2] = t0;
38
46
  } else t0 = $[2];
39
47
  const onKeyDown = t0;
40
48
  const t1 = ariaOrientation !== null && ariaOrientation !== void 0 ? ariaOrientation : "horizontal";
41
49
  let t2;
42
- if ($[3] !== ariaLabel || $[4] !== ariaLabelledby || $[5] !== onKeyDown || $[6] !== ref || $[7] !== t1) {
50
+ if ($[3] !== appliedRef || $[4] !== ariaLabel || $[5] !== ariaLabelledby || $[6] !== onKeyDown || $[7] !== t1) {
43
51
  t2 = { tabListProps: {
44
- ref,
52
+ ref: appliedRef,
45
53
  "aria-label": ariaLabel,
46
54
  "aria-labelledby": ariaLabelledby,
47
55
  "aria-orientation": t1,
48
56
  role: "tablist",
49
57
  onKeyDown
50
58
  } };
51
- $[3] = ariaLabel;
52
- $[4] = ariaLabelledby;
53
- $[5] = onKeyDown;
54
- $[6] = ref;
59
+ $[3] = appliedRef;
60
+ $[4] = ariaLabel;
61
+ $[5] = ariaLabelledby;
62
+ $[6] = onKeyDown;
55
63
  $[7] = t1;
56
64
  $[8] = t2;
57
65
  } else t2 = $[8];
@@ -3,15 +3,16 @@ import { useResizeObserver } from "../../hooks/useResizeObserver.js";
3
3
  import { useId as useId$1 } from "../../hooks/useId.js";
4
4
  import { isSlot } from "../../utils/is-slot.js";
5
5
  import { invariant } from "../../utils/invariant.js";
6
- import { createComponent } from "../../utils/create-component.js";
7
6
  import { UnderlineItem, UnderlineItemList, UnderlineWrapper } from "../../internal/components/UnderlineTabbedInterface.js";
8
7
  import UnderlinePanels_module_css_default from "./UnderlinePanels.module.css.js";
8
+ import { useTab } from "../Tabs/useTab.js";
9
+ import { useTabList } from "../Tabs/useTabList.js";
10
+ import { useTabPanel } from "../Tabs/useTabPanel.js";
11
+ import { Tabs } from "../Tabs/Tabs.js";
9
12
  import { clsx } from "clsx";
10
13
  import { jsx, jsxs } from "react/jsx-runtime";
11
14
  import React, { Children, cloneElement, createContext, isValidElement, useContext, useMemo, useRef, useState } from "react";
12
- import { TabContainerElement } from "@github/tab-container-element";
13
15
  //#region src/experimental/UnderlinePanels/UnderlinePanels.tsx
14
- const TabContainerComponent = createComponent(TabContainerElement, "tab-container");
15
16
  const UnderlinePanelsContext = /*#__PURE__*/ createContext({
16
17
  iconsVisible: true,
17
18
  loadingCounters: void 0
@@ -21,27 +22,43 @@ const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabel
21
22
  const wrapperRef = useRef(null);
22
23
  const listRef = useRef(null);
23
24
  const parentId = useId$1(props.id);
24
- const [tabs, tabPanels, tabsHaveIcons] = useMemo(() => {
25
+ const [tabs, tabPanels, tabsHaveIcons, selectedFromProps] = useMemo(() => {
25
26
  let tabIndex = 0;
26
27
  let panelIndex = 0;
27
28
  const childrenWithProps = Children.map(children, (child) => {
28
- if (/*#__PURE__*/ isValidElement(child) && (child.type === Tab || isSlot(child, Tab))) return /*#__PURE__*/ cloneElement(child, { id: `${parentId}-tab-${tabIndex++}` });
29
- if (/*#__PURE__*/ isValidElement(child) && (child.type === Panel || isSlot(child, Panel))) return /*#__PURE__*/ cloneElement(child, { "aria-labelledby": `${parentId}-tab-${panelIndex++}` });
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++}` });
30
31
  return child;
31
32
  });
32
33
  const tabs = [];
33
34
  const tabPanels = [];
35
+ let selectedFromProps;
34
36
  for (const child of Children.toArray(childrenWithProps)) {
35
37
  if (!/*#__PURE__*/ isValidElement(child)) continue;
36
- if (child.type === Tab || isSlot(child, Tab)) tabs.push(child);
37
- else if (child.type === Panel || isSlot(child, Panel)) tabPanels.push(child);
38
+ if (child.type === Tab || isSlot(child, Tab)) {
39
+ const ariaSelected = child.props["aria-selected"];
40
+ if (ariaSelected === true || ariaSelected === "true") selectedFromProps = `${tabs.length}`;
41
+ tabs.push(child);
42
+ } else if (child.type === Panel || isSlot(child, Panel)) tabPanels.push(child);
38
43
  }
39
44
  return [
40
45
  tabs,
41
46
  tabPanels,
42
- tabs.some((tab) => /*#__PURE__*/ React.isValidElement(tab) && tab.props.icon)
47
+ tabs.some((tab) => /*#__PURE__*/ React.isValidElement(tab) && tab.props.icon),
48
+ selectedFromProps
43
49
  ];
44
- }, [children, parentId]);
50
+ }, [children]);
51
+ const [selectedValue, setSelectedValue] = useState(() => selectedFromProps !== null && selectedFromProps !== void 0 ? selectedFromProps : "0");
52
+ const [prevSelectedFromProps, setPrevSelectedFromProps] = useState(selectedFromProps);
53
+ if (selectedFromProps !== prevSelectedFromProps) {
54
+ setPrevSelectedFromProps(selectedFromProps);
55
+ if (selectedFromProps !== void 0 && selectedFromProps !== selectedValue) setSelectedValue(selectedFromProps);
56
+ }
57
+ const { tabListProps } = useTabList({
58
+ "aria-label": ariaLabel,
59
+ "aria-labelledby": ariaLabelledBy,
60
+ ref: listRef
61
+ });
45
62
  const contextValue = useMemo(() => ({
46
63
  iconsVisible,
47
64
  loadingCounters
@@ -68,55 +85,65 @@ const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabel
68
85
  !(tabs.length === tabPanels.length) && invariant(false, `The number of tabs and panels must be equal. Counted ${tabs.length} tabs and ${tabPanels.length} panels.`);
69
86
  return /*#__PURE__*/ jsx(UnderlinePanelsContext.Provider, {
70
87
  value: contextValue,
71
- children: /*#__PURE__*/ jsxs(TabContainerComponent, { children: [/*#__PURE__*/ jsx(UnderlineWrapper, {
72
- ref: wrapperRef,
73
- slot: "tablist-wrapper",
74
- "data-icons-visible": iconsVisible,
75
- className: clsx(className, UnderlinePanels_module_css_default.StyledUnderlineWrapper),
76
- ...props,
77
- children: /*#__PURE__*/ jsx(UnderlineItemList, {
78
- ref: listRef,
79
- "aria-label": ariaLabel,
80
- "aria-labelledby": ariaLabelledBy,
81
- role: "tablist",
82
- children: tabs
83
- })
84
- }), tabPanels] })
88
+ children: /*#__PURE__*/ jsxs(Tabs, {
89
+ id: parentId,
90
+ value: selectedValue,
91
+ onValueChange: ({ value }) => setSelectedValue(value),
92
+ children: [/*#__PURE__*/ jsx(UnderlineWrapper, {
93
+ ref: wrapperRef,
94
+ "data-icons-visible": iconsVisible,
95
+ className: clsx(className, UnderlinePanels_module_css_default.StyledUnderlineWrapper),
96
+ ...props,
97
+ children: /*#__PURE__*/ jsx(UnderlineItemList, {
98
+ ref: listRef,
99
+ role: tabListProps.role,
100
+ "aria-label": tabListProps["aria-label"],
101
+ "aria-labelledby": tabListProps["aria-labelledby"],
102
+ "aria-orientation": tabListProps["aria-orientation"],
103
+ onKeyDown: tabListProps.onKeyDown,
104
+ children: tabs
105
+ })
106
+ }), tabPanels]
107
+ })
85
108
  });
86
109
  };
87
110
  UnderlinePanels.displayName = "UnderlinePanels";
88
- const TabImpl = ({ "aria-selected": ariaSelected, onSelect, ...props }) => {
111
+ const TabImpl = ({ onSelect, value, ...itemProps }) => {
89
112
  const { loadingCounters } = useContext(UnderlinePanelsContext);
90
- const clickHandler = React.useCallback((event) => {
91
- if (!event.defaultPrevented && typeof onSelect === "function") onSelect(event);
92
- }, [onSelect]);
93
- const keyDownHandler = React.useCallback((event) => {
94
- if ((event.key === " " || event.key === "Enter") && !event.defaultPrevented && typeof onSelect === "function") onSelect(event);
95
- }, [onSelect]);
113
+ const { tabProps } = useTab({ value: value !== null && value !== void 0 ? value : "" });
114
+ const { onKeyDown: tabOnKeyDown, onMouseDown: tabOnMouseDown, onFocus: tabOnFocus, ...restTabProps } = tabProps;
96
115
  return /*#__PURE__*/ jsx(UnderlineItem, {
97
116
  as: "button",
98
- role: "tab",
99
- tabIndex: ariaSelected ? 0 : -1,
100
- "aria-selected": ariaSelected,
117
+ ...itemProps,
118
+ ...restTabProps,
101
119
  type: "button",
102
- onClick: clickHandler,
103
- onKeyDown: keyDownHandler,
104
- loadingCounters,
105
- ...props
120
+ onClick: (event) => {
121
+ if (!event.defaultPrevented && typeof onSelect === "function") onSelect(event);
122
+ },
123
+ onKeyDown: (event) => {
124
+ tabOnKeyDown === null || tabOnKeyDown === void 0 || tabOnKeyDown(event);
125
+ if ((event.key === " " || event.key === "Enter") && !event.defaultPrevented && typeof onSelect === "function") onSelect(event);
126
+ },
127
+ onMouseDown: tabOnMouseDown,
128
+ onFocus: tabOnFocus,
129
+ loadingCounters
106
130
  });
107
131
  };
108
132
  TabImpl.displayName = "TabImpl";
109
133
  TabImpl.displayName = "UnderlinePanels.Tab";
110
134
  const Tab = /*#__PURE__*/ React.memo(TabImpl);
111
135
  Tab.displayName = "UnderlinePanels.Tab";
112
- const Panel = ({ children, ...rest }) => {
136
+ const PanelImpl = ({ children, value, ...panelRest }) => {
137
+ const { tabPanelProps } = useTabPanel({ value: value !== null && value !== void 0 ? value : "" });
113
138
  return /*#__PURE__*/ jsx("div", {
114
- role: "tabpanel",
115
- ...rest,
139
+ ...panelRest,
140
+ ...tabPanelProps,
116
141
  children
117
142
  });
118
143
  };
119
- Panel.displayName = "Panel";
144
+ PanelImpl.displayName = "PanelImpl";
145
+ PanelImpl.displayName = "UnderlinePanels.Panel";
146
+ const Panel = PanelImpl;
120
147
  Panel.displayName = "UnderlinePanels.Panel";
121
148
  var UnderlinePanels_default = Object.assign(UnderlinePanels, {
122
149
  Panel,
@@ -4,15 +4,15 @@ import { Announce } from "../live-region/Announce.js";
4
4
  import { AriaAlert } from "../live-region/AriaAlert.js";
5
5
  import { AriaStatus } from "../live-region/AriaStatus.js";
6
6
  import { ButtonBase } from "../Button/ButtonBase.js";
7
+ import { DefaultFeatureFlags } from "../FeatureFlags/DefaultFeatureFlags.js";
8
+ import { FeatureFlags } from "../FeatureFlags/FeatureFlags.js";
9
+ import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
7
10
  import { KeybindingHint } from "../KeybindingHint/KeybindingHint.js";
8
11
  import { getAccessibleKeybindingHintString } from "../KeybindingHint/utils.js";
9
12
  import { Tooltip } from "../TooltipV2/Tooltip.js";
10
13
  import { useOverflow } from "../hooks/useOverflow.js";
11
14
  import { ScrollableRegion } from "../ScrollableRegion/ScrollableRegion.js";
12
15
  import { Dialog } from "../Dialog/Dialog.js";
13
- import { DefaultFeatureFlags } from "../FeatureFlags/DefaultFeatureFlags.js";
14
- import { FeatureFlags } from "../FeatureFlags/FeatureFlags.js";
15
- import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
16
16
  import { Stack } from "../Stack/index.js";
17
17
  import { SkeletonBox } from "../Skeleton/SkeletonBox.js";
18
18
  import { FilteredActionListLoadingTypes } from "../FilteredActionList/constants.js";
@@ -30,12 +30,12 @@ import { Table } from "../DataTable/index.js";
30
30
  import { InlineMessage } from "../InlineMessage/InlineMessage.js";
31
31
  import { Hidden } from "../Hidden/Hidden.js";
32
32
  import { SelectPanel } from "./SelectPanel2/SelectPanel.js";
33
- import UnderlinePanels_default from "./UnderlinePanels/UnderlinePanels.js";
34
- import { IssueLabel } from "./IssueLabel/IssueLabel.js";
35
33
  import { useTab } from "./Tabs/useTab.js";
36
34
  import { useTabList } from "./Tabs/useTabList.js";
37
35
  import { useTabPanel } from "./Tabs/useTabPanel.js";
38
36
  import { Tabs } from "./Tabs/Tabs.js";
37
+ import UnderlinePanels_default from "./UnderlinePanels/UnderlinePanels.js";
38
+ import { IssueLabel } from "./IssueLabel/IssueLabel.js";
39
39
  import { TopicTag } from "../TopicTag/index.js";
40
40
  //#region src/experimental/index.ts
41
41
  /**
@@ -17,18 +17,18 @@ import { ForwardedRef, Ref } from "react";
17
17
  * // React 18
18
18
  * const Example = forwardRef<HTMLButtonElement, {}>((props, forwardedRef) => {
19
19
  * const ref = useRef<HTMLButtonElement>(null)
20
- * const combinedRef = useMergedRefs(forwardedRef, ref)
20
+ * const mergedRef = useMergedRefs(forwardedRef, ref)
21
21
  *
22
- * return <button ref={combinedRef} />
22
+ * return <button ref={mergedRef} />
23
23
  * })
24
24
  *
25
25
  * @example
26
26
  * // React 19
27
27
  * const Example = ({ref: externalRef}: {ref?: Ref<HTMLButtonElement>}) => {
28
28
  * const ref = useRef<HTMLButtonElement>(null)
29
- * const combinedRef = useMergedRefs(externalRef, ref)
29
+ * const mergedRef = useMergedRefs(externalRef, ref)
30
30
  *
31
- * return <button ref={combinedRef} />
31
+ * return <button ref={mergedRef} />
32
32
  * }
33
33
  */
34
34
  declare function useMergedRefs<T>(refA: Ref$1<T | null>, refB: Ref$1<T | null>): (value: T | null) => (() => void) | undefined;
@@ -1,7 +1,9 @@
1
1
  import { isExperimentalReactVersion, reactMajorVersion } from "../utils/environment.js";
2
2
  import { c } from "react-compiler-runtime";
3
- import "react";
3
+ import { version } from "react";
4
4
  //#region src/hooks/useMergedRefs.ts
5
+ var _version$split$;
6
+ const majorReactVersion = parseInt((_version$split$ = version.split(".")[0]) !== null && _version$split$ !== void 0 ? _version$split$ : "18", 10);
5
7
  /**
6
8
  * Cleanup functions for refs were introduced in React 19. For feature detection,
7
9
  * we look to see if current version of React is >= 19 or if it is an
@@ -26,18 +28,18 @@ const supportsRefCleanup = reactMajorVersion >= 19 || isExperimentalReactVersion
26
28
  * // React 18
27
29
  * const Example = forwardRef<HTMLButtonElement, {}>((props, forwardedRef) => {
28
30
  * const ref = useRef<HTMLButtonElement>(null)
29
- * const combinedRef = useMergedRefs(forwardedRef, ref)
31
+ * const mergedRef = useMergedRefs(forwardedRef, ref)
30
32
  *
31
- * return <button ref={combinedRef} />
33
+ * return <button ref={mergedRef} />
32
34
  * })
33
35
  *
34
36
  * @example
35
37
  * // React 19
36
38
  * const Example = ({ref: externalRef}: {ref?: Ref<HTMLButtonElement>}) => {
37
39
  * const ref = useRef<HTMLButtonElement>(null)
38
- * const combinedRef = useMergedRefs(externalRef, ref)
40
+ * const mergedRef = useMergedRefs(externalRef, ref)
39
41
  *
40
- * return <button ref={combinedRef} />
42
+ * return <button ref={mergedRef} />
41
43
  * }
42
44
  */
43
45
  function useMergedRefs(refA, refB) {
@@ -48,6 +50,7 @@ function useMergedRefs(refA, refB) {
48
50
  const cleanupA = setRef(refA, value);
49
51
  const cleanupB = setRef(refB, value);
50
52
  if (!supportsRefCleanup) return;
53
+ if (majorReactVersion <= 18) return;
51
54
  return () => {
52
55
  if (cleanupA) cleanupA();
53
56
  else setRef(refA, null);
@@ -9,17 +9,14 @@ import React from "react";
9
9
  * @param providedRef The ref to use - if undefined, will use the ref from a call to React.useRef
10
10
  * @type TRef The type of the RefObject which should be created.
11
11
  *
12
- * @deprecated This hook is incompatible with forwarded callback refs. Prefer `useMergedRefs` with an internally
13
- * created ref instead.
12
+ * @note This hook is overly restrictive in that it forces the provided ref to be a ref object rather than a callback.
13
+ * In particular, it should **not** be used to merge internal refs with forwarded refs. For this, use
14
+ * {@linkcode useMergedRefs} instead.
14
15
  *
15
- * ```diff
16
- * - const ref = useProvidedRefOrCreate(forwardedRef as RefObject<...>)
17
- * + const ref = useRef(null)
18
- * + const mergedRef = useMergedRefs(forwardedRef, ref)
19
- *
20
- * - return <div ref={ref} />
21
- * + return <div ref={mergedRef} />
22
- * ```
16
+ * The primary valid use case for this hook is for when the consumer is using the provided ref to point to an external
17
+ * element, like an externally rendered anchor. This is 'backwards' from forwarded refs with respect to data flow; the
18
+ * consumer is passing a reference to an external element, rather than passing a handle to obtain a reference to an
19
+ * internal element.
23
20
  */
24
21
  declare function useProvidedRefOrCreate<TRef>(providedRef?: React.RefObject<TRef | null>): React.RefObject<TRef | null>;
25
22
  //#endregion
@@ -8,17 +8,14 @@ import React from "react";
8
8
  * @param providedRef The ref to use - if undefined, will use the ref from a call to React.useRef
9
9
  * @type TRef The type of the RefObject which should be created.
10
10
  *
11
- * @deprecated This hook is incompatible with forwarded callback refs. Prefer `useMergedRefs` with an internally
12
- * created ref instead.
11
+ * @note This hook is overly restrictive in that it forces the provided ref to be a ref object rather than a callback.
12
+ * In particular, it should **not** be used to merge internal refs with forwarded refs. For this, use
13
+ * {@linkcode useMergedRefs} instead.
13
14
  *
14
- * ```diff
15
- * - const ref = useProvidedRefOrCreate(forwardedRef as RefObject<...>)
16
- * + const ref = useRef(null)
17
- * + const mergedRef = useMergedRefs(forwardedRef, ref)
18
- *
19
- * - return <div ref={ref} />
20
- * + return <div ref={mergedRef} />
21
- * ```
15
+ * The primary valid use case for this hook is for when the consumer is using the provided ref to point to an external
16
+ * element, like an externally rendered anchor. This is 'backwards' from forwarded refs with respect to data flow; the
17
+ * consumer is passing a reference to an external element, rather than passing a handle to obtain a reference to an
18
+ * internal element.
22
19
  */
23
20
  function useProvidedRefOrCreate(providedRef) {
24
21
  const createdRef = React.useRef(null);
@@ -2656,11 +2656,18 @@
2656
2656
  ],
2657
2657
  "importPath": "@primer/react",
2658
2658
  "props": [
2659
+ {
2660
+ "name": "variant",
2661
+ "type": "'primary' | 'secondary'",
2662
+ "defaultValue": "",
2663
+ "description": "Pass in 'primary' for a darker background and inverse text, or 'secondary' for a lighter background and primary text. Omitting the variant prop renders the default counter scheme"
2664
+ },
2659
2665
  {
2660
2666
  "name": "scheme",
2661
2667
  "type": "'primary' | 'secondary'",
2662
2668
  "defaultValue": "",
2663
- "description": "Pass in 'primary' for a darker background and inverse text, or 'secondary' for a lighter background and primary text. Omitting the scheme prop renders the default counter scheme"
2669
+ "description": "Use `variant` instead.",
2670
+ "deprecated": true
2664
2671
  },
2665
2672
  {
2666
2673
  "name": "className",
@@ -5652,6 +5659,11 @@
5652
5659
  "type": "(event: MouseEvent | TouchEvent) => void",
5653
5660
  "description": "Callback fired when a click is detected outside the popover content"
5654
5661
  },
5662
+ {
5663
+ "name": "onEscape",
5664
+ "type": "(event: KeyboardEvent) => void",
5665
+ "description": "Callback fired when the Escape key is pressed while the popover is open"
5666
+ },
5655
5667
  {
5656
5668
  "name": "ignoreClickRefs",
5657
5669
  "type": "React.RefObject<HTMLElement>[]",
@@ -7439,10 +7451,17 @@
7439
7451
  ],
7440
7452
  "importPath": "@primer/react",
7441
7453
  "props": [
7454
+ {
7455
+ "name": "size",
7456
+ "type": "'small' | 'medium'",
7457
+ "defaultValue": "'medium'"
7458
+ },
7442
7459
  {
7443
7460
  "name": "variant",
7444
7461
  "type": "'small' | 'normal'",
7445
- "defaultValue": "'normal'"
7462
+ "defaultValue": "'normal'",
7463
+ "description": "Use `size` instead.",
7464
+ "deprecated": true
7446
7465
  },
7447
7466
  {
7448
7467
  "name": "status",