@primer/react 38.34.0-rc.e4bb4194f → 38.34.0-rc.f86dc8614

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.
@@ -3,16 +3,15 @@ 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";
6
7
  import { UnderlineItem, UnderlineItemList, UnderlineWrapper } from "../../internal/components/UnderlineTabbedInterface.js";
7
8
  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";
12
9
  import { clsx } from "clsx";
13
10
  import { jsx, jsxs } from "react/jsx-runtime";
14
11
  import React, { Children, cloneElement, createContext, isValidElement, useContext, useMemo, useRef, useState } from "react";
12
+ import { TabContainerElement } from "@github/tab-container-element";
15
13
  //#region src/experimental/UnderlinePanels/UnderlinePanels.tsx
14
+ const TabContainerComponent = createComponent(TabContainerElement, "tab-container");
16
15
  const UnderlinePanelsContext = /*#__PURE__*/ createContext({
17
16
  iconsVisible: true,
18
17
  loadingCounters: void 0
@@ -22,43 +21,27 @@ const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabel
22
21
  const wrapperRef = useRef(null);
23
22
  const listRef = useRef(null);
24
23
  const parentId = useId$1(props.id);
25
- const [tabs, tabPanels, tabsHaveIcons, selectedFromProps] = useMemo(() => {
24
+ const [tabs, tabPanels, tabsHaveIcons] = useMemo(() => {
26
25
  let tabIndex = 0;
27
26
  let panelIndex = 0;
28
27
  const childrenWithProps = Children.map(children, (child) => {
29
- if (/*#__PURE__*/ isValidElement(child) && (child.type === Tab || isSlot(child, Tab))) return /*#__PURE__*/ cloneElement(child, { value: `${tabIndex++}` });
30
- if (/*#__PURE__*/ isValidElement(child) && (child.type === Panel || isSlot(child, Panel))) return /*#__PURE__*/ cloneElement(child, { value: `${panelIndex++}` });
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++}` });
31
30
  return child;
32
31
  });
33
32
  const tabs = [];
34
33
  const tabPanels = [];
35
- let selectedFromProps;
36
34
  for (const child of Children.toArray(childrenWithProps)) {
37
35
  if (!/*#__PURE__*/ isValidElement(child)) continue;
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);
36
+ if (child.type === Tab || isSlot(child, Tab)) tabs.push(child);
37
+ else if (child.type === Panel || isSlot(child, Panel)) tabPanels.push(child);
43
38
  }
44
39
  return [
45
40
  tabs,
46
41
  tabPanels,
47
- tabs.some((tab) => /*#__PURE__*/ React.isValidElement(tab) && tab.props.icon),
48
- selectedFromProps
42
+ tabs.some((tab) => /*#__PURE__*/ React.isValidElement(tab) && tab.props.icon)
49
43
  ];
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
- });
44
+ }, [children, parentId]);
62
45
  const contextValue = useMemo(() => ({
63
46
  iconsVisible,
64
47
  loadingCounters
@@ -85,65 +68,55 @@ const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabel
85
68
  !(tabs.length === tabPanels.length) && invariant(false, `The number of tabs and panels must be equal. Counted ${tabs.length} tabs and ${tabPanels.length} panels.`);
86
69
  return /*#__PURE__*/ jsx(UnderlinePanelsContext.Provider, {
87
70
  value: contextValue,
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
- })
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] })
108
85
  });
109
86
  };
110
87
  UnderlinePanels.displayName = "UnderlinePanels";
111
- const TabImpl = ({ onSelect, value, ...itemProps }) => {
88
+ const TabImpl = ({ "aria-selected": ariaSelected, onSelect, ...props }) => {
112
89
  const { loadingCounters } = useContext(UnderlinePanelsContext);
113
- const { tabProps } = useTab({ value: value !== null && value !== void 0 ? value : "" });
114
- const { onKeyDown: tabOnKeyDown, onMouseDown: tabOnMouseDown, onFocus: tabOnFocus, ...restTabProps } = tabProps;
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]);
115
96
  return /*#__PURE__*/ jsx(UnderlineItem, {
116
97
  as: "button",
117
- ...itemProps,
118
- ...restTabProps,
98
+ role: "tab",
99
+ tabIndex: ariaSelected ? 0 : -1,
100
+ "aria-selected": ariaSelected,
119
101
  type: "button",
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
102
+ onClick: clickHandler,
103
+ onKeyDown: keyDownHandler,
104
+ loadingCounters,
105
+ ...props
130
106
  });
131
107
  };
132
108
  TabImpl.displayName = "TabImpl";
133
109
  TabImpl.displayName = "UnderlinePanels.Tab";
134
110
  const Tab = /*#__PURE__*/ React.memo(TabImpl);
135
111
  Tab.displayName = "UnderlinePanels.Tab";
136
- const PanelImpl = ({ children, value, ...panelRest }) => {
137
- const { tabPanelProps } = useTabPanel({ value: value !== null && value !== void 0 ? value : "" });
112
+ const Panel = ({ children, ...rest }) => {
138
113
  return /*#__PURE__*/ jsx("div", {
139
- ...panelRest,
140
- ...tabPanelProps,
114
+ role: "tabpanel",
115
+ ...rest,
141
116
  children
142
117
  });
143
118
  };
144
- PanelImpl.displayName = "PanelImpl";
145
- PanelImpl.displayName = "UnderlinePanels.Panel";
146
- const Panel = PanelImpl;
119
+ Panel.displayName = "Panel";
147
120
  Panel.displayName = "UnderlinePanels.Panel";
148
121
  var UnderlinePanels_default = Object.assign(UnderlinePanels, {
149
122
  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";
10
7
  import { KeybindingHint } from "../KeybindingHint/KeybindingHint.js";
11
8
  import { getAccessibleKeybindingHintString } from "../KeybindingHint/utils.js";
12
9
  import { Tooltip } from "../TooltipV2/Tooltip.js";
13
10
  import { useOverflow } from "../hooks/useOverflow.js";
14
11
  import { ScrollableRegion } from "../ScrollableRegion/ScrollableRegion.js";
15
12
  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";
33
35
  import { useTab } from "./Tabs/useTab.js";
34
36
  import { useTabList } from "./Tabs/useTabList.js";
35
37
  import { useTabPanel } from "./Tabs/useTabPanel.js";
36
38
  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 mergedRef = useMergedRefs(forwardedRef, ref)
20
+ * const combinedRef = useMergedRefs(forwardedRef, ref)
21
21
  *
22
- * return <button ref={mergedRef} />
22
+ * return <button ref={combinedRef} />
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 mergedRef = useMergedRefs(externalRef, ref)
29
+ * const combinedRef = useMergedRefs(externalRef, ref)
30
30
  *
31
- * return <button ref={mergedRef} />
31
+ * return <button ref={combinedRef} />
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,9 +1,7 @@
1
1
  import { isExperimentalReactVersion, reactMajorVersion } from "../utils/environment.js";
2
2
  import { c } from "react-compiler-runtime";
3
- import { version } from "react";
3
+ import "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);
7
5
  /**
8
6
  * Cleanup functions for refs were introduced in React 19. For feature detection,
9
7
  * we look to see if current version of React is >= 19 or if it is an
@@ -28,18 +26,18 @@ const supportsRefCleanup = reactMajorVersion >= 19 || isExperimentalReactVersion
28
26
  * // React 18
29
27
  * const Example = forwardRef<HTMLButtonElement, {}>((props, forwardedRef) => {
30
28
  * const ref = useRef<HTMLButtonElement>(null)
31
- * const mergedRef = useMergedRefs(forwardedRef, ref)
29
+ * const combinedRef = useMergedRefs(forwardedRef, ref)
32
30
  *
33
- * return <button ref={mergedRef} />
31
+ * return <button ref={combinedRef} />
34
32
  * })
35
33
  *
36
34
  * @example
37
35
  * // React 19
38
36
  * const Example = ({ref: externalRef}: {ref?: Ref<HTMLButtonElement>}) => {
39
37
  * const ref = useRef<HTMLButtonElement>(null)
40
- * const mergedRef = useMergedRefs(externalRef, ref)
38
+ * const combinedRef = useMergedRefs(externalRef, ref)
41
39
  *
42
- * return <button ref={mergedRef} />
40
+ * return <button ref={combinedRef} />
43
41
  * }
44
42
  */
45
43
  function useMergedRefs(refA, refB) {
@@ -50,7 +48,6 @@ function useMergedRefs(refA, refB) {
50
48
  const cleanupA = setRef(refA, value);
51
49
  const cleanupB = setRef(refB, value);
52
50
  if (!supportsRefCleanup) return;
53
- if (majorReactVersion <= 18) return;
54
51
  return () => {
55
52
  if (cleanupA) cleanupA();
56
53
  else setRef(refA, null);
@@ -9,14 +9,17 @@ 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
- * @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.
12
+ * @deprecated This hook is incompatible with forwarded callback refs. Prefer `useMergedRefs` with an internally
13
+ * created ref instead.
15
14
  *
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.
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
+ * ```
20
23
  */
21
24
  declare function useProvidedRefOrCreate<TRef>(providedRef?: React.RefObject<TRef | null>): React.RefObject<TRef | null>;
22
25
  //#endregion
@@ -8,14 +8,17 @@ 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
- * @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.
11
+ * @deprecated This hook is incompatible with forwarded callback refs. Prefer `useMergedRefs` with an internally
12
+ * created ref instead.
14
13
  *
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.
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
+ * ```
19
22
  */
20
23
  function useProvidedRefOrCreate(providedRef) {
21
24
  const createdRef = React.useRef(null);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@primer/react",
3
3
  "type": "module",
4
- "version": "38.34.0-rc.e4bb4194f",
4
+ "version": "38.34.0-rc.f86dc8614",
5
5
  "description": "An implementation of GitHub's Primer Design System using React",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -77,6 +77,7 @@
77
77
  "dependencies": {
78
78
  "@github/mini-throttle": "^2.1.1",
79
79
  "@github/relative-time-element": "^5.0.0",
80
+ "@github/tab-container-element": "^4.8.2",
80
81
  "@lit-labs/react": "1.2.1",
81
82
  "@oddbird/popover-polyfill": "^0.5.2",
82
83
  "@primer/behaviors": "^1.10.3",