@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.
- package/CHANGELOG.md +12 -0
- package/dist/ActionList/Group.js +1 -1
- package/dist/ActionList/List.js +20 -14
- package/dist/AnchoredOverlay/AnchoredOverlay.js +6 -3
- package/dist/Autocomplete/AutocompleteInput.js +10 -7
- package/dist/ButtonGroup/ButtonGroup.js +15 -8
- package/dist/Checkbox/Checkbox.js +16 -9
- package/dist/Dialog/Dialog.js +12 -5
- package/dist/FeatureFlags/DefaultFeatureFlags.js +2 -1
- package/dist/FilteredActionList/FilteredActionList.js +31 -20
- package/dist/FilteredActionList/FilteredActionListInput.d.ts +1 -1
- package/dist/Link/Link.js +33 -33
- package/dist/Overlay/Overlay.js +1 -1
- package/dist/PageHeader/PageHeader.js +30 -21
- package/dist/Popover/Popover.d.ts +1 -0
- package/dist/Popover/Popover.js +84 -47
- package/dist/Radio/Radio.js +1 -1
- package/dist/SegmentedControl/{SegmentedControl-a004561f.css → SegmentedControl-de65527b.css} +2 -2
- package/dist/SegmentedControl/{SegmentedControl-a004561f.css.map → SegmentedControl-de65527b.css.map} +1 -1
- package/dist/SegmentedControl/SegmentedControl.module.css.js +1 -1
- package/dist/SegmentedControl/SegmentedControlButton.js +1 -1
- package/dist/SegmentedControl/SegmentedControlIconButton.js +1 -1
- package/dist/SelectPanel/SelectPanel.js +1 -1
- package/dist/TextInput/TextInput.js +13 -6
- package/dist/TooltipV2/Tooltip.js +18 -11
- package/dist/experimental/Tabs/Tabs.js +3 -2
- package/dist/experimental/Tabs/types.d.ts +10 -3
- package/dist/experimental/Tabs/useTabList.js +18 -10
- package/dist/experimental/UnderlinePanels/UnderlinePanels.js +69 -42
- package/dist/experimental/index.js +5 -5
- package/dist/hooks/useMergedRefs.d.ts +4 -4
- package/dist/hooks/useMergedRefs.js +8 -5
- package/dist/hooks/useProvidedRefOrCreate.d.ts +7 -10
- package/dist/hooks/useProvidedRefOrCreate.js +7 -10
- package/generated/components.json +21 -2
- 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
|
|
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 !==
|
|
33
|
-
var
|
|
34
|
-
(
|
|
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:
|
|
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
|
|
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 &&
|
|
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 =
|
|
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 &&
|
|
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 || !
|
|
126
|
-
const isTriggerInteractive = isInteractive(
|
|
127
|
-
const triggerChildren =
|
|
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 =
|
|
134
|
-
const hasAriaLabelInChildren = Array.from(
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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] !==
|
|
17
|
+
if ($[0] !== ariaOrientation || $[1] !== readRef) {
|
|
10
18
|
t0 = (event) => {
|
|
11
|
-
const { current: tablist } =
|
|
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] =
|
|
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] !==
|
|
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] =
|
|
52
|
-
$[4] =
|
|
53
|
-
$[5] =
|
|
54
|
-
$[6] =
|
|
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, {
|
|
29
|
-
if (/*#__PURE__*/ isValidElement(child) && (child.type === Panel || isSlot(child, Panel))) return /*#__PURE__*/ cloneElement(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++}` });
|
|
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))
|
|
37
|
-
|
|
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
|
|
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(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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 = ({
|
|
111
|
+
const TabImpl = ({ onSelect, value, ...itemProps }) => {
|
|
89
112
|
const { loadingCounters } = useContext(UnderlinePanelsContext);
|
|
90
|
-
const
|
|
91
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
"aria-selected": ariaSelected,
|
|
117
|
+
...itemProps,
|
|
118
|
+
...restTabProps,
|
|
101
119
|
type: "button",
|
|
102
|
-
onClick:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
|
136
|
+
const PanelImpl = ({ children, value, ...panelRest }) => {
|
|
137
|
+
const { tabPanelProps } = useTabPanel({ value: value !== null && value !== void 0 ? value : "" });
|
|
113
138
|
return /*#__PURE__*/ jsx("div", {
|
|
114
|
-
|
|
115
|
-
...
|
|
139
|
+
...panelRest,
|
|
140
|
+
...tabPanelProps,
|
|
116
141
|
children
|
|
117
142
|
});
|
|
118
143
|
};
|
|
119
|
-
|
|
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
|
|
20
|
+
* const mergedRef = useMergedRefs(forwardedRef, ref)
|
|
21
21
|
*
|
|
22
|
-
* return <button ref={
|
|
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
|
|
29
|
+
* const mergedRef = useMergedRefs(externalRef, ref)
|
|
30
30
|
*
|
|
31
|
-
* return <button ref={
|
|
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
|
|
31
|
+
* const mergedRef = useMergedRefs(forwardedRef, ref)
|
|
30
32
|
*
|
|
31
|
-
* return <button ref={
|
|
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
|
|
40
|
+
* const mergedRef = useMergedRefs(externalRef, ref)
|
|
39
41
|
*
|
|
40
|
-
* return <button ref={
|
|
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
|
-
* @
|
|
13
|
-
*
|
|
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
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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
|
-
* @
|
|
12
|
-
*
|
|
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
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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": "
|
|
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",
|