@primer/react 38.34.0-rc.f86dc8614 → 38.34.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +6 -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/SegmentedControl/{SegmentedControl-a004561f.css → SegmentedControl-de65527b.css} +2 -2
  16. package/dist/SegmentedControl/{SegmentedControl-a004561f.css.map → SegmentedControl-de65527b.css.map} +1 -1
  17. package/dist/SegmentedControl/SegmentedControl.module.css.js +1 -1
  18. package/dist/SegmentedControl/SegmentedControlButton.js +1 -1
  19. package/dist/SegmentedControl/SegmentedControlIconButton.js +1 -1
  20. package/dist/SelectPanel/SelectPanel.js +1 -1
  21. package/dist/TextInput/TextInput.js +13 -6
  22. package/dist/TooltipV2/Tooltip.js +18 -11
  23. package/dist/experimental/Tabs/Tabs.js +3 -2
  24. package/dist/experimental/Tabs/types.d.ts +10 -3
  25. package/dist/experimental/Tabs/useTabList.js +18 -10
  26. package/dist/experimental/UnderlinePanels/UnderlinePanels.js +69 -42
  27. package/dist/experimental/index.js +5 -5
  28. package/dist/hooks/useMergedRefs.d.ts +4 -4
  29. package/dist/hooks/useMergedRefs.js +8 -5
  30. package/dist/hooks/useProvidedRefOrCreate.d.ts +7 -10
  31. package/dist/hooks/useProvidedRefOrCreate.js +7 -10
  32. package/package.json +1 -2
package/CHANGELOG.md CHANGED
@@ -8,8 +8,14 @@
8
8
 
9
9
  ### Patch Changes
10
10
 
11
+ - [#8211](https://github.com/primer/react/pull/8211) [`a3e669f`](https://github.com/primer/react/commit/a3e669fa551a7d07841ce20224199d172905860f) Thanks [@francinelucca](https://github.com/francinelucca)! - Link: Remove unnecessary `data-hover-color` attribute from rendered DOM element
12
+
13
+ - [#8196](https://github.com/primer/react/pull/8196) [`e24effd`](https://github.com/primer/react/commit/e24effd440d69f0068a89aba26bae9e39fb3254e) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Use `aria-pressed` instead of `aria-current` on `SegmentedControl.Button` and `SegmentedControl.IconButton` so JAWS announces the selected state when a segment is activated.
14
+
11
15
  - [#8169](https://github.com/primer/react/pull/8169) [`1d6f559`](https://github.com/primer/react/commit/1d6f5593f5e482d1d03027522bc882560c7b1f06) Thanks [@liuliu-dev](https://github.com/liuliu-dev)! - Timeline: `Timeline.Actions` now wraps onto a new row below `Timeline.Body`, left-aligned with the body content, when `Timeline.Item` is narrower than 480px.
12
16
 
17
+ - [#8214](https://github.com/primer/react/pull/8214) [`41705f4`](https://github.com/primer/react/commit/41705f430ce0f64eb61c2665313f36a88cf6f16c) Thanks [@rickyzhangca](https://github.com/rickyzhangca)! - UnderlinePanels: The experimental `UnderlinePanels` component is now built on the experimental `Tabs` component instead of `@github/tab-container-element`. Its public API and behavior are unchanged.
18
+
13
19
  ## 38.33.0
14
20
 
15
21
  ### Minor Changes
@@ -1,9 +1,9 @@
1
1
  import { useId as useId$1 } from "../hooks/useId.js";
2
2
  import { useSlots } from "../hooks/useSlots.js";
3
+ import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
3
4
  import { invariant } from "../utils/invariant.js";
4
5
  import { ListContext } from "./shared.js";
5
6
  import ActionList_module_css_default from "./ActionList.module.css.js";
6
- import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
7
7
  import Group_module_css_default from "./Group.module.css.js";
8
8
  import { GroupHeadingTrailingAction } from "./GroupHeadingTrailingAction.js";
9
9
  import { GroupContext } from "./GroupContext.js";
@@ -1,18 +1,19 @@
1
+ import { useMergedRefs } from "../hooks/useMergedRefs.js";
1
2
  import { useProvidedRefOrCreate } from "../hooks/useProvidedRefOrCreate.js";
2
3
  import useIsomorphicLayoutEffect from "../utils/useIsomorphicLayoutEffect.js";
3
4
  import { useId as useId$1 } from "../hooks/useId.js";
4
5
  import { useSlots } from "../hooks/useSlots.js";
6
+ import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
5
7
  import { fixedForwardRef } from "../utils/modern-polymorphic.js";
6
8
  import { FocusKeys, useFocusZone } from "../hooks/useFocusZone.js";
7
9
  import { ActionListContainerContext } from "./ActionListContainerContext.js";
8
10
  import { ListContext } from "./shared.js";
9
11
  import { Heading } from "./Heading.js";
10
12
  import ActionList_module_css_default from "./ActionList.module.css.js";
11
- import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
12
13
  import { c } from "react-compiler-runtime";
13
14
  import { clsx } from "clsx";
14
15
  import { jsx, jsxs } from "react/jsx-runtime";
15
- import React from "react";
16
+ import React, { useRef } from "react";
16
17
  //#region src/ActionList/List.tsx
17
18
  const UnwrappedList = (props, forwardedRef) => {
18
19
  var _slots$heading$props$;
@@ -60,7 +61,12 @@ const UnwrappedList = (props, forwardedRef) => {
60
61
  const { listRole: listRoleFromContainer, listLabelledBy, selectionVariant: containerSelectionVariant, enableFocusZone: enableFocusZoneFromContainer, container } = React.useContext(ActionListContainerContext);
61
62
  const ariaLabelledBy = slots.heading ? (_slots$heading$props$ = slots.heading.props.id) !== null && _slots$heading$props$ !== void 0 ? _slots$heading$props$ : headingId : listLabelledBy;
62
63
  const listRole = role || listRoleFromContainer;
63
- const listRef = useProvidedRefOrCreate(forwardedRef);
64
+ const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
65
+ const listRef = useRef(null);
66
+ const mergedRef = useMergedRefs(listRef, forwardedRef);
67
+ const providedOrCreatedRef = useProvidedRefOrCreate(forwardedRef);
68
+ const readRef = mergedRefEnabled ? listRef : providedOrCreatedRef;
69
+ const appliedRef = mergedRefEnabled ? mergedRef : providedOrCreatedRef;
64
70
  const itemGapEnabled = useFeatureFlag("primer_react_action_list_item_gap") && container === "NavList";
65
71
  let enableFocusZone = false;
66
72
  if (enableFocusZoneFromContainer !== void 0) enableFocusZone = enableFocusZoneFromContainer;
@@ -79,14 +85,14 @@ const UnwrappedList = (props, forwardedRef) => {
79
85
  const t5 = !enableFocusZone;
80
86
  const t6 = listRole === "menu" || container === "SelectPanel" || container === "FilteredActionList" ? "wrap" : void 0;
81
87
  let t7;
82
- if ($[11] !== listRef || $[12] !== t5 || $[13] !== t6) {
88
+ if ($[11] !== readRef || $[12] !== t5 || $[13] !== t6) {
83
89
  t7 = {
84
90
  disabled: t5,
85
- containerRef: listRef,
91
+ containerRef: readRef,
86
92
  bindKeys: FocusKeys.ArrowVertical | FocusKeys.HomeAndEnd | FocusKeys.PageUpDown,
87
93
  focusOutBehavior: t6
88
94
  };
89
- $[11] = listRef;
95
+ $[11] = readRef;
90
96
  $[12] = t5;
91
97
  $[13] = t6;
92
98
  $[14] = t7;
@@ -111,16 +117,16 @@ const UnwrappedList = (props, forwardedRef) => {
111
117
  } else t9 = $[20];
112
118
  const listContextValue = t9;
113
119
  let t10;
114
- if ($[21] !== listRef) {
120
+ if ($[21] !== readRef.current) {
115
121
  t10 = () => {
116
- const list = listRef.current;
122
+ const list = readRef.current;
117
123
  if (!list) return;
118
124
  const hasMixed = list.querySelector("[data-has-description=\"true\"]") !== null && list.querySelector("[data-has-description=\"false\"]") !== null;
119
125
  const current = list.getAttribute("data-mixed-descriptions");
120
126
  if (hasMixed && current !== "true") list.setAttribute("data-mixed-descriptions", "true");
121
127
  else if (!hasMixed && current !== null) list.removeAttribute("data-mixed-descriptions");
122
128
  };
123
- $[21] = listRef;
129
+ $[21] = readRef.current;
124
130
  $[22] = t10;
125
131
  } else t10 = $[22];
126
132
  useIsomorphicLayoutEffect(t10);
@@ -133,12 +139,12 @@ const UnwrappedList = (props, forwardedRef) => {
133
139
  } else t12 = $[24];
134
140
  const t13 = itemGapEnabled ? "" : void 0;
135
141
  let t14;
136
- if ($[25] !== Component || $[26] !== ariaLabelledBy || $[27] !== childrenWithoutSlots || $[28] !== listRef || $[29] !== listRole || $[30] !== restProps || $[31] !== showDividers || $[32] !== t12 || $[33] !== t13 || $[34] !== variant) {
142
+ if ($[25] !== Component || $[26] !== appliedRef || $[27] !== ariaLabelledBy || $[28] !== childrenWithoutSlots || $[29] !== listRole || $[30] !== restProps || $[31] !== showDividers || $[32] !== t12 || $[33] !== t13 || $[34] !== variant) {
137
143
  t14 = /*#__PURE__*/ jsx(Component, {
138
144
  className: t12,
139
145
  role: listRole,
140
146
  "aria-labelledby": ariaLabelledBy,
141
- ref: listRef,
147
+ ref: appliedRef,
142
148
  "data-component": "ActionList",
143
149
  "data-dividers": showDividers,
144
150
  "data-variant": variant,
@@ -147,9 +153,9 @@ const UnwrappedList = (props, forwardedRef) => {
147
153
  children: childrenWithoutSlots
148
154
  });
149
155
  $[25] = Component;
150
- $[26] = ariaLabelledBy;
151
- $[27] = childrenWithoutSlots;
152
- $[28] = listRef;
156
+ $[26] = appliedRef;
157
+ $[27] = ariaLabelledBy;
158
+ $[28] = childrenWithoutSlots;
153
159
  $[29] = listRole;
154
160
  $[30] = restProps;
155
161
  $[31] = showDividers;
@@ -1,12 +1,13 @@
1
1
  import { reactMajorVersion } from "../utils/environment.js";
2
+ import { useMergedRefs } from "../hooks/useMergedRefs.js";
2
3
  import { useProvidedRefOrCreate } from "../hooks/useProvidedRefOrCreate.js";
3
4
  import { useAnchoredPosition } from "../hooks/useAnchoredPosition.js";
4
5
  import { useRenderForcingRef } from "../hooks/useRenderForcingRef.js";
5
6
  import { useId as useId$1 } from "../hooks/useId.js";
7
+ import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
6
8
  import { IconButton } from "../Button/IconButton.js";
7
9
  import { useFocusTrap } from "../hooks/useFocusTrap.js";
8
10
  import { useFocusZone } from "../hooks/useFocusZone.js";
9
- import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
10
11
  import Overlay from "../Overlay/Overlay.js";
11
12
  import AnchoredOverlay_module_css_default from "./AnchoredOverlay.module.css.js";
12
13
  import { widthMap } from "../Overlay/constants.js";
@@ -33,7 +34,9 @@ const AnchoredOverlay = ({ renderAnchor, anchorRef: externalAnchorRef, anchorId:
33
34
  const [anchorElement, setAnchorElement] = useState(null);
34
35
  if (anchorRef.current !== anchorElement) setAnchorElement(anchorRef.current);
35
36
  const [overlayRef, updateOverlayRef] = useRenderForcingRef();
37
+ const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
36
38
  const [overlayElement, setOverlayElement] = useState(null);
39
+ const mergedOverlayRef = useMergedRefs(updateOverlayRef, useMergedRefs(overlayProps === null || overlayProps === void 0 ? void 0 : overlayProps.ref, setOverlayElement));
37
40
  const anchorId = useId$1(externalAnchorId);
38
41
  const onClickOutside = useCallback(() => onClose === null || onClose === void 0 ? void 0 : onClose("click-outside"), [onClose]);
39
42
  const onEscape = useCallback(() => onClose === null || onClose === void 0 ? void 0 : onClose("escape"), [onClose]);
@@ -190,10 +193,10 @@ const AnchoredOverlay = ({ renderAnchor, anchorRef: externalAnchorRef, anchorId:
190
193
  ...shouldRenderAsPopover ? { popover: "manual" } : {},
191
194
  ...restOverlayProps,
192
195
  ...shouldRenderAsPopover ? { id: popoverId } : {},
193
- ref: (node) => {
196
+ ref: mergedRefEnabled ? mergedOverlayRef : (node) => {
197
+ setOverlayElement(node);
194
198
  if (overlayProps !== null && overlayProps !== void 0 && overlayProps.ref) assignRef(overlayProps.ref, node);
195
199
  updateOverlayRef(node);
196
- setOverlayElement(node);
197
200
  },
198
201
  "data-anchor-position": cssAnchorPositioning,
199
202
  "data-side": cssAnchorPositioning ? side : position === null || position === void 0 ? void 0 : position.anchorSide,
@@ -20,9 +20,11 @@ const AutocompleteInput = /*#__PURE__*/ React.forwardRef(({ as: Component = Text
20
20
  if (openOnFocus) setShowMenu(true);
21
21
  };
22
22
  const handleInputBlur = useCallback((event) => {
23
- onBlur && onBlur(event);
23
+ onBlur === null || onBlur === void 0 || onBlur(event);
24
24
  safeSetTimeout(() => {
25
- if (document.activeElement !== inputRef.current) {
25
+ const nextFocusedElement = event.relatedTarget;
26
+ const menuElement = document.getElementById(`${id}-listbox`);
27
+ if (!nextFocusedElement || nextFocusedElement !== menuElement && !(menuElement !== null && menuElement !== void 0 && menuElement.contains(nextFocusedElement))) {
26
28
  setShowMenu(false);
27
29
  if (inputRef.current && autocompleteSuggestion && inputRef.current.value !== inputValue) inputRef.current.value = inputValue;
28
30
  }
@@ -33,16 +35,17 @@ const AutocompleteInput = /*#__PURE__*/ React.forwardRef(({ as: Component = Text
33
35
  inputRef,
34
36
  safeSetTimeout,
35
37
  autocompleteSuggestion,
36
- inputValue
38
+ inputValue,
39
+ id
37
40
  ]);
38
41
  const handleInputChange = (event) => {
39
- onChange && onChange(event);
42
+ onChange === null || onChange === void 0 || onChange(event);
40
43
  setInputValue(event.currentTarget.value);
41
44
  if (!showMenu) setShowMenu(true);
42
45
  };
43
46
  const handleInputKeyDown = useCallback((event) => {
44
47
  var _inputRef$current;
45
- onKeyDown && onKeyDown(event);
48
+ onKeyDown === null || onKeyDown === void 0 || onKeyDown(event);
46
49
  if (event.key === "Backspace") setHighlightRemainingText(false);
47
50
  if (event.key === "Escape" && (_inputRef$current = inputRef.current) !== null && _inputRef$current !== void 0 && _inputRef$current.value) {
48
51
  setInputValue("");
@@ -58,11 +61,11 @@ const AutocompleteInput = /*#__PURE__*/ React.forwardRef(({ as: Component = Text
58
61
  setShowMenu
59
62
  ]);
60
63
  const handleInputKeyUp = useCallback((event) => {
61
- onKeyUp && onKeyUp(event);
64
+ onKeyUp === null || onKeyUp === void 0 || onKeyUp(event);
62
65
  if (event.key === "Backspace") setHighlightRemainingText(true);
63
66
  }, [setHighlightRemainingText, onKeyUp]);
64
67
  const onInputKeyPress = useCallback((event) => {
65
- onKeyPress && onKeyPress(event);
68
+ onKeyPress === null || onKeyPress === void 0 || onKeyPress(event);
66
69
  if (showMenu && event.key === "Enter" && activeDescendantRef.current) {
67
70
  event.preventDefault();
68
71
  event.nativeEvent.stopImmediatePropagation();
@@ -1,10 +1,12 @@
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 { FocusKeys, useFocusZone } from "../hooks/useFocusZone.js";
3
5
  import ButtonGroup_module_css_default from "./ButtonGroup.module.css.js";
4
6
  import { c } from "react-compiler-runtime";
5
7
  import { clsx } from "clsx";
6
8
  import { jsx } from "react/jsx-runtime";
7
- import React from "react";
9
+ import React, { useRef } from "react";
8
10
  //#region src/ButtonGroup/ButtonGroup.tsx
9
11
  const ButtonGroup = /*#__PURE__*/ React.forwardRef(function ButtonGroup(t0, forwardRef) {
10
12
  const $ = c(20);
@@ -29,6 +31,12 @@ const ButtonGroup = /*#__PURE__*/ React.forwardRef(function ButtonGroup(t0, forw
29
31
  t1 = $[5];
30
32
  }
31
33
  const BaseComponent = t1 === void 0 ? "div" : t1;
34
+ const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
35
+ const buttonRef = useRef(null);
36
+ const mergedRef = useMergedRefs(buttonRef, forwardRef);
37
+ const providedOrCreatedRef = useProvidedRefOrCreate(forwardRef);
38
+ const readRef = mergedRefEnabled ? buttonRef : providedOrCreatedRef;
39
+ const appliedRef = mergedRefEnabled ? mergedRef : providedOrCreatedRef;
32
40
  let t2;
33
41
  if ($[6] !== children) {
34
42
  t2 = React.Children.map(children, _temp);
@@ -36,17 +44,16 @@ const ButtonGroup = /*#__PURE__*/ React.forwardRef(function ButtonGroup(t0, forw
36
44
  $[7] = t2;
37
45
  } else t2 = $[7];
38
46
  const buttons = t2;
39
- const buttonRef = useProvidedRefOrCreate(forwardRef);
40
47
  const t3 = role !== "toolbar";
41
48
  let t4;
42
- if ($[8] !== buttonRef || $[9] !== t3) {
49
+ if ($[8] !== readRef || $[9] !== t3) {
43
50
  t4 = {
44
- containerRef: buttonRef,
51
+ containerRef: readRef,
45
52
  disabled: t3,
46
53
  bindKeys: FocusKeys.ArrowHorizontal,
47
54
  focusOutBehavior: "wrap"
48
55
  };
49
- $[8] = buttonRef;
56
+ $[8] = readRef;
50
57
  $[9] = t3;
51
58
  $[10] = t4;
52
59
  } else t4 = $[10];
@@ -58,9 +65,9 @@ const ButtonGroup = /*#__PURE__*/ React.forwardRef(function ButtonGroup(t0, forw
58
65
  $[12] = t5;
59
66
  } else t5 = $[12];
60
67
  let t6;
61
- if ($[13] !== BaseComponent || $[14] !== buttonRef || $[15] !== buttons || $[16] !== rest || $[17] !== role || $[18] !== t5) {
68
+ if ($[13] !== BaseComponent || $[14] !== appliedRef || $[15] !== buttons || $[16] !== rest || $[17] !== role || $[18] !== t5) {
62
69
  t6 = /*#__PURE__*/ jsx(BaseComponent, {
63
- ref: buttonRef,
70
+ ref: appliedRef,
64
71
  className: t5,
65
72
  role,
66
73
  ...rest,
@@ -68,7 +75,7 @@ const ButtonGroup = /*#__PURE__*/ React.forwardRef(function ButtonGroup(t0, forw
68
75
  children: buttons
69
76
  });
70
77
  $[13] = BaseComponent;
71
- $[14] = buttonRef;
78
+ $[14] = appliedRef;
72
79
  $[15] = buttons;
73
80
  $[16] = rest;
74
81
  $[17] = role;
@@ -1,30 +1,37 @@
1
+ import { useMergedRefs } from "../hooks/useMergedRefs.js";
1
2
  import { useProvidedRefOrCreate } from "../hooks/useProvidedRefOrCreate.js";
2
3
  import useIsomorphicLayoutEffect from "../utils/useIsomorphicLayoutEffect.js";
4
+ import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
3
5
  import shared_module_css_default from "./shared.module.css.js";
4
6
  import { CheckboxGroupContext } from "../CheckboxGroup/CheckboxGroupContext.js";
5
7
  import Checkbox_module_css_default from "./Checkbox.module.css.js";
6
8
  import { clsx } from "clsx";
7
9
  import { jsx } from "react/jsx-runtime";
8
- import React, { useContext, useEffect } from "react";
10
+ import React, { useContext, useEffect, useRef } from "react";
9
11
  //#region src/Checkbox/Checkbox.tsx
10
12
  /**
11
13
  * An accessible, native checkbox component
12
14
  */
13
15
  const Checkbox = /*#__PURE__*/ React.forwardRef(({ checked, className, defaultChecked, indeterminate, disabled, onChange, required, validationStatus, value, ["data-component"]: dataComponent, ...rest }, ref) => {
14
- const checkboxRef = useProvidedRefOrCreate(ref);
16
+ const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
17
+ const checkboxRef = useRef(null);
18
+ const mergedRef = useMergedRefs(checkboxRef, ref);
19
+ const providedOrCreatedRef = useProvidedRefOrCreate(ref);
20
+ const readRef = mergedRefEnabled ? checkboxRef : providedOrCreatedRef;
21
+ const appliedRef = mergedRefEnabled ? mergedRef : providedOrCreatedRef;
15
22
  const checkboxGroupContext = useContext(CheckboxGroupContext);
16
23
  const handleOnChange = (e) => {
17
24
  checkboxGroupContext.onChange && checkboxGroupContext.onChange(e);
18
25
  onChange && onChange(e);
19
- if (indeterminate && checkboxRef.current) {
20
- checkboxRef.current.indeterminate = true;
21
- checkboxRef.current.setAttribute("aria-checked", "mixed");
26
+ if (indeterminate && readRef.current) {
27
+ readRef.current.indeterminate = true;
28
+ readRef.current.setAttribute("aria-checked", "mixed");
22
29
  }
23
30
  };
24
31
  const inputProps = {
25
32
  type: "checkbox",
26
33
  disabled,
27
- ref: checkboxRef,
34
+ ref: appliedRef,
28
35
  checked: indeterminate ? false : checked,
29
36
  defaultChecked,
30
37
  required,
@@ -36,14 +43,14 @@ const Checkbox = /*#__PURE__*/ React.forwardRef(({ checked, className, defaultCh
36
43
  ...rest
37
44
  };
38
45
  useIsomorphicLayoutEffect(() => {
39
- if (checkboxRef.current) checkboxRef.current.indeterminate = indeterminate || false;
46
+ if (readRef.current) readRef.current.indeterminate = indeterminate || false;
40
47
  }, [
41
48
  indeterminate,
42
49
  checked,
43
- checkboxRef
50
+ readRef
44
51
  ]);
45
52
  useEffect(() => {
46
- const { current: checkbox } = checkboxRef;
53
+ const { current: checkbox } = readRef;
47
54
  if (!checkbox) return;
48
55
  if (indeterminate) checkbox.setAttribute("aria-checked", "mixed");
49
56
  else checkbox.setAttribute("aria-checked", checkbox.checked ? "true" : "false");
@@ -4,6 +4,7 @@ import { useOnEscapePress } from "../hooks/useOnEscapePress.js";
4
4
  import { useResizeObserver } from "../hooks/useResizeObserver.js";
5
5
  import { useId as useId$1 } from "../hooks/useId.js";
6
6
  import { useSlots } from "../hooks/useSlots.js";
7
+ import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
7
8
  import { IconButton } from "../Button/IconButton.js";
8
9
  import { ButtonComponent } from "../Button/Button.js";
9
10
  import { useFocusTrap } from "../hooks/useFocusTrap.js";
@@ -244,22 +245,28 @@ const Footer = /*#__PURE__*/ React.forwardRef(function Footer({ className, ...re
244
245
  Footer.displayName = "Dialog.Footer";
245
246
  const Buttons = ({ buttons }) => {
246
247
  var _buttons$find;
247
- const autoFocusRef = useProvidedRefOrCreate((_buttons$find = buttons.find((button) => button.autoFocus)) === null || _buttons$find === void 0 ? void 0 : _buttons$find.ref);
248
+ const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
249
+ const providedButtonRef = (_buttons$find = buttons.find((button) => button.autoFocus)) === null || _buttons$find === void 0 ? void 0 : _buttons$find.ref;
250
+ const autoFocusRef = useRef(null);
251
+ const mergedRef = useMergedRefs(autoFocusRef, providedButtonRef);
252
+ const providedOrCreatedRef = useProvidedRefOrCreate(providedButtonRef);
253
+ const readRef = mergedRefEnabled ? autoFocusRef : providedOrCreatedRef;
254
+ const appliedRef = mergedRefEnabled ? mergedRef : providedOrCreatedRef;
248
255
  let autoFocusCount = 0;
249
256
  const [hasRendered, setHasRendered] = useState(0);
250
257
  useEffect(() => {
251
258
  if (hasRendered === 1) {
252
- var _autoFocusRef$current;
253
- (_autoFocusRef$current = autoFocusRef.current) === null || _autoFocusRef$current === void 0 || _autoFocusRef$current.focus();
259
+ var _readRef$current;
260
+ (_readRef$current = readRef.current) === null || _readRef$current === void 0 || _readRef$current.focus();
254
261
  } else setHasRendered(hasRendered + 1);
255
- }, [autoFocusRef, hasRendered]);
262
+ }, [readRef, hasRendered]);
256
263
  return /*#__PURE__*/ jsx(Fragment, { children: buttons.map((dialogButtonProps, index) => {
257
264
  const { content, buttonType = "default", autoFocus = false, ...buttonProps } = dialogButtonProps;
258
265
  return /*#__PURE__*/ jsx(ButtonComponent, {
259
266
  "data-component": "Dialog.FooterButton",
260
267
  ...buttonProps,
261
268
  variant: buttonType === "normal" ? "default" : buttonType,
262
- ref: autoFocus && autoFocusCount === 0 ? (autoFocusCount++, autoFocusRef) : null,
269
+ ref: autoFocus && autoFocusCount === 0 ? (autoFocusCount++, appliedRef) : null,
263
270
  children: content
264
271
  }, index);
265
272
  }) });
@@ -7,7 +7,8 @@ const DefaultFeatureFlags = FeatureFlagScope.create({
7
7
  primer_react_styled_react_use_primer_theme_providers: false,
8
8
  primer_react_action_list_group_heading_trailing_action: false,
9
9
  primer_react_action_list_item_gap: false,
10
- primer_react_timeline_list_semantics: false
10
+ primer_react_timeline_list_semantics: false,
11
+ primer_react_merged_forwarded_refs: false
11
12
  });
12
13
  //#endregion
13
14
  export { DefaultFeatureFlags };
@@ -1,7 +1,9 @@
1
+ import { useMergedRefs } from "../hooks/useMergedRefs.js";
1
2
  import { VisuallyHidden } from "../VisuallyHidden/VisuallyHidden.js";
2
3
  import { useProvidedRefOrCreate } from "../hooks/useProvidedRefOrCreate.js";
3
4
  import { useProvidedStateOrCreate } from "../hooks/useProvidedStateOrCreate.js";
4
5
  import { useId as useId$1 } from "../hooks/useId.js";
6
+ import { useFeatureFlag } from "../FeatureFlags/useFeatureFlag.js";
5
7
  import { useFocusZone } from "../hooks/useFocusZone.js";
6
8
  import { ActionListContainerContext } from "../ActionList/ActionListContainerContext.js";
7
9
  import { ActionList } from "../ActionList/index.js";
@@ -34,8 +36,17 @@ function FilteredActionList({ loading = false, placeholderText, filterValue: ext
34
36
  }, [onFilterChange, setInternalFilterValue]);
35
37
  const inputAndListContainerRef = useRef(null);
36
38
  const listRef = useRef(null);
37
- const scrollContainerRef = useProvidedRefOrCreate(providedScrollContainerRef);
38
- const inputRef = useProvidedRefOrCreate(providedInputRef);
39
+ const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
40
+ const scrollContainerRef = useRef(null);
41
+ const mergedScrollContainerRef = useMergedRefs(scrollContainerRef, providedScrollContainerRef);
42
+ const providedOrCreatedScrollContainerRef = useProvidedRefOrCreate(providedScrollContainerRef);
43
+ const readScrollContainerRef = mergedRefEnabled ? scrollContainerRef : providedOrCreatedScrollContainerRef;
44
+ const appliedScrollContainerRef = mergedRefEnabled ? mergedScrollContainerRef : providedOrCreatedScrollContainerRef;
45
+ const inputRef = useRef(null);
46
+ const mergedInputRef = useMergedRefs(inputRef, providedInputRef);
47
+ const providedOrCreatedInputRef = useProvidedRefOrCreate(providedInputRef);
48
+ const readInputRef = mergedRefEnabled ? inputRef : providedOrCreatedInputRef;
49
+ const appliedInputRef = mergedRefEnabled ? mergedInputRef : providedOrCreatedInputRef;
39
50
  const usingRovingTabindex = _PrivateFocusManagement === "roving-tabindex";
40
51
  const [listContainerElement, setListContainerElement] = useState(null);
41
52
  const activeDescendantRef = useRef();
@@ -89,12 +100,12 @@ function FilteredActionList({ loading = false, placeholderText, filterValue: ext
89
100
  onListContainerRefChanged === null || onListContainerRefChanged === void 0 || onListContainerRefChanged(node);
90
101
  }, [onListContainerRefChanged]);
91
102
  useEffect(() => {
92
- onInputRefChanged === null || onInputRefChanged === void 0 || onInputRefChanged(inputRef);
93
- }, [inputRef, onInputRefChanged]);
103
+ onInputRefChanged === null || onInputRefChanged === void 0 || onInputRefChanged(readInputRef);
104
+ }, [readInputRef, onInputRefChanged]);
94
105
  const DEFAULT_VIRTUAL_ITEM_HEIGHT = 32;
95
106
  const virtualizer = useVirtualizer({
96
107
  count: items.length,
97
- getScrollElement: () => scrollContainerRef.current,
108
+ getScrollElement: () => readScrollContainerRef.current,
98
109
  estimateSize: () => DEFAULT_VIRTUAL_ITEM_HEIGHT,
99
110
  overscan: 10,
100
111
  enabled: isVirtualized,
@@ -128,7 +139,7 @@ function FilteredActionList({ loading = false, placeholderText, filterValue: ext
128
139
  focusableElementFilter: (element) => {
129
140
  return !(element instanceof HTMLInputElement);
130
141
  },
131
- activeDescendantFocus: inputRef,
142
+ activeDescendantFocus: readInputRef,
132
143
  onActiveDescendantChanged: (current, previous, directlyActivated) => {
133
144
  activeDescendantRef.current = current;
134
145
  if (isVirtualized && current) {
@@ -136,7 +147,7 @@ function FilteredActionList({ loading = false, placeholderText, filterValue: ext
136
147
  const range = virtualizer.range;
137
148
  if (index !== null && range && (Number(index) < range.startIndex || Number(index) >= range.endIndex)) virtualizer.scrollToIndex(Number(index), { align: "auto" });
138
149
  }
139
- if (current && scrollContainerRef.current && (directlyActivated || focusPrependedElements)) scrollIntoView(current, scrollContainerRef.current, {
150
+ if (current && readScrollContainerRef.current && (directlyActivated || focusPrependedElements)) scrollIntoView(current, readScrollContainerRef.current, {
140
151
  ...menuScrollMargins,
141
152
  behavior: scrollBehavior
142
153
  });
@@ -153,14 +164,14 @@ function FilteredActionList({ loading = false, placeholderText, filterValue: ext
153
164
  isVirtualized
154
165
  ]);
155
166
  useEffect(() => {
156
- if (activeDescendantRef.current && scrollContainerRef.current) scrollIntoView(activeDescendantRef.current, scrollContainerRef.current, {
167
+ if (activeDescendantRef.current && readScrollContainerRef.current) scrollIntoView(activeDescendantRef.current, readScrollContainerRef.current, {
157
168
  ...menuScrollMargins,
158
169
  behavior: scrollBehavior
159
170
  });
160
171
  }, [
161
172
  items,
162
- inputRef,
163
- scrollContainerRef,
173
+ readInputRef,
174
+ readScrollContainerRef,
164
175
  scrollBehavior
165
176
  ]);
166
177
  useEffect(() => {
@@ -170,7 +181,7 @@ function FilteredActionList({ loading = false, placeholderText, filterValue: ext
170
181
  const list = listRef.current;
171
182
  if (!list) return;
172
183
  const handleFocusIn = (event) => {
173
- if (event.target === inputRef.current || list.contains(event.target)) setIsInputFocused(inputRef.current && inputRef.current === document.activeElement ? true : false);
184
+ if (event.target === readInputRef.current || list.contains(event.target)) setIsInputFocused(readInputRef.current && readInputRef.current === document.activeElement ? true : false);
174
185
  };
175
186
  inputAndListContainerElement.addEventListener("focusin", handleFocusIn);
176
187
  return () => {
@@ -179,26 +190,26 @@ function FilteredActionList({ loading = false, placeholderText, filterValue: ext
179
190
  }
180
191
  }, [
181
192
  items,
182
- inputRef,
193
+ readInputRef,
183
194
  listContainerElement,
184
195
  usingRovingTabindex
185
196
  ]);
186
197
  useEffect(() => {
187
- if (usingRovingTabindex && !loading) setIsInputFocused(inputRef.current && inputRef.current === document.activeElement ? true : false);
198
+ if (usingRovingTabindex && !loading) setIsInputFocused(readInputRef.current && readInputRef.current === document.activeElement ? true : false);
188
199
  }, [
189
200
  loading,
190
- inputRef,
201
+ readInputRef,
191
202
  usingRovingTabindex
192
203
  ]);
193
- useAnnouncements(items, usingRovingTabindex ? listRef : { current: listContainerElement }, inputRef, announcementsEnabled, loading, messageText, _PrivateFocusManagement);
194
- useScrollFlash(scrollContainerRef);
204
+ useAnnouncements(items, usingRovingTabindex ? listRef : { current: listContainerElement }, readInputRef, announcementsEnabled, loading, messageText, _PrivateFocusManagement);
205
+ useScrollFlash(readScrollContainerRef);
195
206
  const handleSelectAllChange = useCallback((e) => {
196
207
  if (onSelectAllChange) onSelectAllChange(e.target.checked);
197
208
  }, [onSelectAllChange]);
198
209
  function getBodyContent() {
199
- if (loading && scrollContainerRef.current && loadingType.appearsInBody) return /*#__PURE__*/ jsx(FilteredActionListBodyLoader, {
210
+ if (loading && readScrollContainerRef.current && loadingType.appearsInBody) return /*#__PURE__*/ jsx(FilteredActionListBodyLoader, {
200
211
  loadingType,
201
- height: scrollContainerRef.current.clientHeight
212
+ height: readScrollContainerRef.current.clientHeight
202
213
  });
203
214
  if (message) return message;
204
215
  let firstGroupIndex = 0;
@@ -290,7 +301,7 @@ function FilteredActionList({ loading = false, placeholderText, filterValue: ext
290
301
  "data-component": "FilteredActionList",
291
302
  children: [
292
303
  /*#__PURE__*/ jsx(FilteredActionListInput, {
293
- inputRef,
304
+ inputRef: appliedInputRef,
294
305
  value: filterValue,
295
306
  onInputChange,
296
307
  onInputKeyPress,
@@ -324,7 +335,7 @@ function FilteredActionList({ loading = false, placeholderText, filterValue: ext
324
335
  })]
325
336
  }),
326
337
  /*#__PURE__*/ jsx("div", {
327
- ref: scrollContainerRef,
338
+ ref: appliedScrollContainerRef,
328
339
  className: FilteredActionList_module_css_default.Container,
329
340
  children: getBodyContent()
330
341
  })
@@ -3,7 +3,7 @@ import React from "react";
3
3
 
4
4
  //#region src/FilteredActionList/FilteredActionListInput.d.ts
5
5
  interface FilteredActionListInputProps extends Partial<Omit<TextInputProps, 'onChange' | 'onKeyDown'>> {
6
- inputRef: React.RefObject<HTMLInputElement | null>;
6
+ inputRef: React.Ref<HTMLInputElement | null>;
7
7
  onInputChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
8
8
  onInputKeyPress?: React.KeyboardEventHandler<HTMLInputElement>;
9
9
  onInputKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;