@primer/react 38.37.0-rc.05923388d → 38.37.0-rc.0b6dd7e38

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 CHANGED
@@ -4,12 +4,8 @@
4
4
 
5
5
  ### Minor Changes
6
6
 
7
- - [#8306](https://github.com/primer/react/pull/8306) [`a3afd7b`](https://github.com/primer/react/commit/a3afd7baf05ea6c4a272a40ccb636f20a48f87dc) Thanks [@llastflowers](https://github.com/llastflowers)! - Deprecate the `Flash` component and move to `@primer/react/deprecated`
8
-
9
7
  - [#8295](https://github.com/primer/react/pull/8295) [`bfebb8a`](https://github.com/primer/react/commit/bfebb8a538e6f068a457722b154799876bda3ab4) Thanks [@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)! - Textarea: Add an `autoSize` option that allows the field to grow with its content
10
8
 
11
- - [#8233](https://github.com/primer/react/pull/8233) [`80a68cf`](https://github.com/primer/react/commit/80a68cf05226323ad8cbc0531f244d60ea6747e3) Thanks [@rickyzhangca](https://github.com/rickyzhangca)! - UnderlinePanels: add controlled `value`/`defaultValue`/`onChange` and `activationMode` props, keyed to a per-`Tab`/`Panel` `value`, for data-driven tabs. Behind the `primer_react_underline_panels_controlled` feature flag; the existing `aria-selected`/`onSelect` API is unchanged.
12
-
13
9
  ### Patch Changes
14
10
 
15
11
  - [#8304](https://github.com/primer/react/pull/8304) [`88feb65`](https://github.com/primer/react/commit/88feb658c13fec154da1a8aa199250514f4fdba3) Thanks [@iansan5653](https://github.com/iansan5653)! - Animate `SegmentedControl` when switching between items
@@ -8,8 +8,7 @@ const DefaultFeatureFlags = FeatureFlagScope.create({
8
8
  primer_react_action_list_group_heading_trailing_action: false,
9
9
  primer_react_action_list_item_gap: false,
10
10
  primer_react_timeline_list_semantics: false,
11
- primer_react_merged_forwarded_refs: false,
12
- primer_react_underline_panels_controlled: false
11
+ primer_react_merged_forwarded_refs: false
13
12
  });
14
13
  //#endregion
15
14
  export { DefaultFeatureFlags };
@@ -2,17 +2,11 @@ import { ForwardRefComponent } from "../utils/polymorphic.js";
2
2
  import React from "react";
3
3
 
4
4
  //#region src/Flash/Flash.d.ts
5
- /**
6
- * @deprecated Use `Banner` instead. If migration is not yet possible, import `Flash` from `@primer/react/deprecated`.
7
- */
8
5
  type FlashProps = React.ComponentPropsWithoutRef<'div'> & {
9
6
  className?: string;
10
7
  variant?: 'default' | 'warning' | 'success' | 'danger';
11
8
  full?: boolean;
12
9
  };
13
- /**
14
- * @deprecated Use `Banner` instead. If migration is not yet possible, import `Flash` from `@primer/react/deprecated`.
15
- */
16
10
  declare const Flash: ForwardRefComponent<"div", FlashProps>;
17
11
  //#endregion
18
12
  export { FlashProps, Flash as default };
@@ -4,12 +4,6 @@ import { clsx } from "clsx";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import React from "react";
6
6
  //#region src/Flash/Flash.tsx
7
- /**
8
- * @deprecated Use `Banner` instead. If migration is not yet possible, import `Flash` from `@primer/react/deprecated`.
9
- */
10
- /**
11
- * @deprecated Use `Banner` instead. If migration is not yet possible, import `Flash` from `@primer/react/deprecated`.
12
- */
13
7
  const Flash = /*#__PURE__*/ React.forwardRef(function Flash(t0, ref) {
14
8
  const $ = c(15);
15
9
  let className;
@@ -3,7 +3,6 @@ import { ItemProps } from "./ActionList/Item.js";
3
3
  import { GroupedListProps, ItemInput, ListProps } from "./ActionList/List.js";
4
4
  import { ActionList } from "./ActionList/index.js";
5
5
  import Octicon, { OcticonProps } from "../Octicon/Octicon.js";
6
- import Flash, { FlashProps } from "../Flash/Flash.js";
7
6
  import { ActionMenu, ActionMenuProps } from "./ActionMenu.js";
8
7
  import FilteredSearch, { FilteredSearchProps } from "./FilteredSearch/FilteredSearch.js";
9
8
  import _default$2, { UnderlineNavLinkProps, UnderlineNavProps } from "./UnderlineNav/UnderlineNav.js";
@@ -11,4 +10,4 @@ import _default, { DialogHeaderProps, DialogProps } from "./DialogV1/Dialog.js";
11
10
  import Pagehead, { PageheadProps } from "../Pagehead/Pagehead.js";
12
11
  import _default$1, { TabNavLinkProps, TabNavProps } from "../TabNav/TabNav.js";
13
12
  import Tooltip, { TooltipProps } from "../Tooltip/Tooltip.js";
14
- export { ActionList, type GroupProps as ActionListGroupProps, type GroupedListProps as ActionListGroupedListProps, type ItemInput as ActionListItemInput, type ItemProps as ActionListItemProps, type ListProps as ActionListProps, ActionMenu, type ActionMenuProps, _default as Dialog, type DialogHeaderProps, type DialogProps, FilteredSearch, type FilteredSearchProps, Flash, type FlashProps, Octicon, type OcticonProps, Pagehead, type PageheadProps, _default$1 as TabNav, type TabNavLinkProps, type TabNavProps, Tooltip, type TooltipProps, _default$2 as UnderlineNav, type UnderlineNavLinkProps, type UnderlineNavProps };
13
+ export { ActionList, type GroupProps as ActionListGroupProps, type GroupedListProps as ActionListGroupedListProps, type ItemInput as ActionListItemInput, type ItemProps as ActionListItemProps, type ListProps as ActionListProps, ActionMenu, type ActionMenuProps, _default as Dialog, type DialogHeaderProps, type DialogProps, FilteredSearch, type FilteredSearchProps, Octicon, type OcticonProps, Pagehead, type PageheadProps, _default$1 as TabNav, type TabNavLinkProps, type TabNavProps, Tooltip, type TooltipProps, _default$2 as UnderlineNav, type UnderlineNavLinkProps, type UnderlineNavProps };
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
  import Octicon from "../Octicon/Octicon.js";
3
- import Flash from "../Flash/Flash.js";
4
3
  import { ActionList } from "./ActionList/index.js";
5
4
  import { ActionMenu } from "./ActionMenu.js";
6
5
  import FilteredSearch from "./FilteredSearch/FilteredSearch.js";
@@ -18,4 +17,4 @@ import Tooltip from "../Tooltip/Tooltip.js";
18
17
  * example: import {FormGroup} from '@primer/react/deprecated
19
18
  */
20
19
  //#endregion
21
- export { ActionList, ActionMenu, Dialog_default as Dialog, FilteredSearch, Flash, Octicon, Pagehead, TabNav_default as TabNav, Tooltip, UnderlineNav_default as UnderlineNav };
20
+ export { ActionList, ActionMenu, Dialog_default as Dialog, FilteredSearch, Octicon, Pagehead, TabNav_default as TabNav, Tooltip, UnderlineNav_default as UnderlineNav };
@@ -1,5 +1,4 @@
1
1
  import useIsomorphicLayoutEffect from "../../utils/useIsomorphicLayoutEffect.js";
2
- import { useFeatureFlag } from "../../FeatureFlags/useFeatureFlag.js";
3
2
  import { useControllableState } from "../../hooks/useControllableState.js";
4
3
  import { TabsContext } from "./TabsContext.js";
5
4
  import { c } from "react-compiler-runtime";
@@ -14,13 +13,11 @@ import React, { useId } from "react";
14
13
  * The `Tab`, `TabList`, and `TabPanel` components are provided for convenience to showcase the API & implementation.
15
14
  */
16
15
  function Tabs(props) {
17
- var _props$id, _props$activationMode, _props$defaultValue;
18
- const $ = c(16);
16
+ var _props$id, _props$defaultValue;
17
+ const $ = c(13);
19
18
  const { children, onValueChange } = props;
20
19
  const generatedId = useId();
21
20
  const groupId = (_props$id = props.id) !== null && _props$id !== void 0 ? _props$id : generatedId;
22
- const controlledApiEnabled = useFeatureFlag("primer_react_underline_panels_controlled");
23
- const activationMode = controlledApiEnabled ? (_props$activationMode = props.activationMode) !== null && _props$activationMode !== void 0 ? _props$activationMode : "automatic" : "automatic";
24
21
  const t0 = (_props$defaultValue = props.defaultValue) !== null && _props$defaultValue !== void 0 ? _props$defaultValue : props.value;
25
22
  let t1;
26
23
  if ($[0] !== props.value || $[1] !== t0) {
@@ -34,60 +31,49 @@ function Tabs(props) {
34
31
  $[2] = t1;
35
32
  } else t1 = $[2];
36
33
  const [selectedValue, setSelectedValue] = useControllableState(t1);
37
- const [focusedValue, setFocusedValue] = React.useState();
38
34
  const savedOnValueChange = React.useRef(onValueChange);
39
35
  let t2;
40
- if ($[3] !== activationMode || $[4] !== controlledApiEnabled || $[5] !== focusedValue || $[6] !== groupId || $[7] !== selectedValue || $[8] !== setSelectedValue) {
36
+ if ($[3] !== groupId || $[4] !== selectedValue || $[5] !== setSelectedValue) {
41
37
  t2 = {
42
38
  groupId,
43
39
  selectedValue,
44
- activationMode,
45
- focusedValue,
46
40
  selectTab(value) {
47
41
  var _savedOnValueChange$c;
48
- setFocusedValue(void 0);
49
- if (controlledApiEnabled && value === selectedValue) return;
50
42
  setSelectedValue(value);
51
43
  (_savedOnValueChange$c = savedOnValueChange.current) === null || _savedOnValueChange$c === void 0 || _savedOnValueChange$c.call(savedOnValueChange, { value });
52
- },
53
- focusTab(value_0) {
54
- setFocusedValue(value_0);
55
44
  }
56
45
  };
57
- $[3] = activationMode;
58
- $[4] = controlledApiEnabled;
59
- $[5] = focusedValue;
60
- $[6] = groupId;
61
- $[7] = selectedValue;
62
- $[8] = setSelectedValue;
63
- $[9] = t2;
64
- } else t2 = $[9];
46
+ $[3] = groupId;
47
+ $[4] = selectedValue;
48
+ $[5] = setSelectedValue;
49
+ $[6] = t2;
50
+ } else t2 = $[6];
65
51
  const contextValue = t2;
66
52
  let t3;
67
53
  let t4;
68
- if ($[10] !== onValueChange) {
54
+ if ($[7] !== onValueChange) {
69
55
  t3 = () => {
70
56
  savedOnValueChange.current = onValueChange;
71
57
  };
72
58
  t4 = [onValueChange];
73
- $[10] = onValueChange;
74
- $[11] = t3;
75
- $[12] = t4;
59
+ $[7] = onValueChange;
60
+ $[8] = t3;
61
+ $[9] = t4;
76
62
  } else {
77
- t3 = $[11];
78
- t4 = $[12];
63
+ t3 = $[8];
64
+ t4 = $[9];
79
65
  }
80
66
  useIsomorphicLayoutEffect(t3, t4);
81
67
  let t5;
82
- if ($[13] !== children || $[14] !== contextValue) {
68
+ if ($[10] !== children || $[11] !== contextValue) {
83
69
  t5 = /*#__PURE__*/ jsx(TabsContext.Provider, {
84
70
  value: contextValue,
85
71
  children
86
72
  });
87
- $[13] = children;
88
- $[14] = contextValue;
89
- $[15] = t5;
90
- } else t5 = $[15];
73
+ $[10] = children;
74
+ $[11] = contextValue;
75
+ $[12] = t5;
76
+ } else t5 = $[12];
91
77
  return t5;
92
78
  }
93
79
  //#endregion
@@ -51,12 +51,6 @@ type CommonTabsProps = {
51
51
  * unique id is generated automatically.
52
52
  */
53
53
  id?: string;
54
- /**
55
- * `'automatic'` (default) selects on focus, so Arrow/Home/End keys select immediately. `'manual'`
56
- * only moves focus; selection commits on Enter/Space/click. Prefer `'manual'` when displaying a
57
- * panel is not instant (e.g. it triggers a network request).
58
- */
59
- activationMode?: 'automatic' | 'manual';
60
54
  };
61
55
  type TabsProps = PropsWithChildren<(ControlledTabsProps | UncontrolledTabsProps) & CommonTabsProps>;
62
56
  type Label = {
@@ -12,7 +12,6 @@ function useTab(props) {
12
12
  const selected = tabs.selectedValue === value;
13
13
  const id = `${tabs.groupId}-tab-${value}`;
14
14
  const panelId = `${tabs.groupId}-panel-${value}`;
15
- const isTabStop = tabs.activationMode === "manual" && tabs.focusedValue !== void 0 ? tabs.focusedValue === value : selected;
16
15
  let t0;
17
16
  if ($[0] !== tabs || $[1] !== value) {
18
17
  t0 = function onKeyDown(event) {
@@ -38,10 +37,6 @@ function useTab(props) {
38
37
  let t2;
39
38
  if ($[7] !== disabled || $[8] !== selected || $[9] !== tabs || $[10] !== value) {
40
39
  t2 = function onFocus() {
41
- if (tabs.activationMode === "manual") {
42
- if (!disabled) tabs.focusTab(value);
43
- return;
44
- }
45
40
  if (!selected && !disabled) tabs.selectTab(value);
46
41
  };
47
42
  $[7] = disabled;
@@ -52,7 +47,7 @@ function useTab(props) {
52
47
  } else t2 = $[11];
53
48
  const onFocus = t2;
54
49
  const t3 = disabled ? true : void 0;
55
- const t4 = isTabStop ? 0 : -1;
50
+ const t4 = selected ? 0 : -1;
56
51
  let t5;
57
52
  if ($[12] !== id || $[13] !== onFocus || $[14] !== onKeyDown || $[15] !== onMouseDown || $[16] !== panelId || $[17] !== selected || $[18] !== t3 || $[19] !== t4) {
58
53
  t5 = { tabProps: {
@@ -5,29 +5,20 @@ import { c } from "react-compiler-runtime";
5
5
  import { useRef } from "react";
6
6
  //#region src/experimental/Tabs/useTabList.ts
7
7
  function useTabList(props) {
8
- const $ = c(10);
8
+ const $ = c(9);
9
9
  const { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-orientation": ariaOrientation } = props;
10
10
  const mergedRefEnabled = useFeatureFlag("primer_react_merged_forwarded_refs");
11
- const controlledApiEnabled = useFeatureFlag("primer_react_underline_panels_controlled");
12
11
  const tabListRef = useRef(null);
13
12
  const mergedRef = useMergedRefs(tabListRef, props.ref);
14
13
  const providedOrCreatedRef = useProvidedRefOrCreate(props.ref);
15
14
  const readRef = mergedRefEnabled ? tabListRef : providedOrCreatedRef;
16
15
  const appliedRef = mergedRefEnabled ? mergedRef : providedOrCreatedRef;
17
16
  let t0;
18
- if ($[0] !== ariaOrientation || $[1] !== controlledApiEnabled || $[2] !== readRef) {
17
+ if ($[0] !== ariaOrientation || $[1] !== readRef) {
19
18
  t0 = (event) => {
20
19
  const { current: tablist } = readRef;
21
20
  if (tablist === null) return;
22
21
  const tabs = getFocusableTabs(tablist);
23
- const getCurrentIndex = () => {
24
- if (controlledApiEnabled) {
25
- const activeElement = tablist.ownerDocument.activeElement;
26
- const focusedIndex = activeElement instanceof HTMLElement ? tabs.indexOf(activeElement) : -1;
27
- if (focusedIndex !== -1) return focusedIndex;
28
- }
29
- return tabs.findIndex(_temp);
30
- };
31
22
  const isVertical = ariaOrientation === "vertical";
32
23
  const nextKey = isVertical ? "ArrowDown" : "ArrowRight";
33
24
  const prevKey = isVertical ? "ArrowUp" : "ArrowLeft";
@@ -36,13 +27,13 @@ function useTabList(props) {
36
27
  event.stopPropagation();
37
28
  }
38
29
  if (event.key === nextKey) {
39
- const currentTabIndex = getCurrentIndex();
40
- if (currentTabIndex === -1) return;
41
- tabs[(currentTabIndex + 1) % tabs.length].focus();
30
+ const selectedTabIndex = tabs.findIndex(_temp);
31
+ if (selectedTabIndex === -1) return;
32
+ tabs[(selectedTabIndex + 1) % tabs.length].focus();
42
33
  } else if (event.key === prevKey) {
43
- const currentTabIndex_0 = getCurrentIndex();
44
- if (currentTabIndex_0 === -1) return;
45
- tabs[(tabs.length + currentTabIndex_0 - 1) % tabs.length].focus();
34
+ const selectedTabIndex_0 = tabs.findIndex(_temp2);
35
+ if (selectedTabIndex_0 === -1) return;
36
+ tabs[(tabs.length + selectedTabIndex_0 - 1) % tabs.length].focus();
46
37
  } else if (event.key === "Home") {
47
38
  if (tabs[0]) tabs[0].focus();
48
39
  } else if (event.key === "End") {
@@ -50,14 +41,13 @@ function useTabList(props) {
50
41
  }
51
42
  };
52
43
  $[0] = ariaOrientation;
53
- $[1] = controlledApiEnabled;
54
- $[2] = readRef;
55
- $[3] = t0;
56
- } else t0 = $[3];
44
+ $[1] = readRef;
45
+ $[2] = t0;
46
+ } else t0 = $[2];
57
47
  const onKeyDown = t0;
58
48
  const t1 = ariaOrientation !== null && ariaOrientation !== void 0 ? ariaOrientation : "horizontal";
59
49
  let t2;
60
- if ($[4] !== appliedRef || $[5] !== ariaLabel || $[6] !== ariaLabelledby || $[7] !== onKeyDown || $[8] !== t1) {
50
+ if ($[3] !== appliedRef || $[4] !== ariaLabel || $[5] !== ariaLabelledby || $[6] !== onKeyDown || $[7] !== t1) {
61
51
  t2 = { tabListProps: {
62
52
  ref: appliedRef,
63
53
  "aria-label": ariaLabel,
@@ -66,15 +56,18 @@ function useTabList(props) {
66
56
  role: "tablist",
67
57
  onKeyDown
68
58
  } };
69
- $[4] = appliedRef;
70
- $[5] = ariaLabel;
71
- $[6] = ariaLabelledby;
72
- $[7] = onKeyDown;
73
- $[8] = t1;
74
- $[9] = t2;
75
- } else t2 = $[9];
59
+ $[3] = appliedRef;
60
+ $[4] = ariaLabel;
61
+ $[5] = ariaLabelledby;
62
+ $[6] = onKeyDown;
63
+ $[7] = t1;
64
+ $[8] = t2;
65
+ } else t2 = $[8];
76
66
  return t2;
77
67
  }
68
+ function _temp2(tab_0) {
69
+ return tab_0.getAttribute("aria-selected") === "true";
70
+ }
78
71
  function _temp(tab) {
79
72
  return tab.getAttribute("aria-selected") === "true";
80
73
  }
@@ -16,31 +16,6 @@ type UnderlinePanelsProps = {
16
16
  * ID of the element containing the name for the tab list
17
17
  */
18
18
  'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
19
- /**
20
- * The value of the selected tab, keyed to each `UnderlinePanels.Tab`/`UnderlinePanels.Panel`
21
- * `value`. Provide this (with `onChange`) for a controlled component where the selected tab is
22
- * the single source of truth.
23
- */
24
- value?: string;
25
- /**
26
- * The value of the tab that should be selected by default, keyed to each tab's `value`. Use this
27
- * for an uncontrolled component. Cannot be combined with `value`.
28
- */
29
- defaultValue?: string;
30
- /**
31
- * Fires whenever the selected tab changes, for every selection method including arrow keys.
32
- * Unlike `UnderlinePanels.Tab`'s `onSelect`, which only fires on click and Enter/Space.
33
- */
34
- onChange?: ({
35
- value
36
- }: {
37
- value: string;
38
- }) => void;
39
- /**
40
- * `'automatic'` (default) selects on focus/arrow keys; `'manual'` only moves focus until
41
- * Enter/Space/click. Prefer `'manual'` when displaying a panel is not instant.
42
- */
43
- activationMode?: 'automatic' | 'manual';
44
19
  /**
45
20
  * Custom string to use when generating the IDs of tabs and `aria-labelledby` for the panels
46
21
  */
@@ -59,12 +34,6 @@ type UnderlinePanelsProps = {
59
34
  as?: React.ElementType;
60
35
  };
61
36
  type TabProps = PropsWithChildren<{
62
- /**
63
- * A value that uniquely identifies this tab, paired with the `UnderlinePanels.Panel` of the same
64
- * `value`. Provide this to use domain values (e.g. `'branch'`) with the container's controlled
65
- * `value`/`onChange` API. When omitted, tabs and panels are paired by their DOM order.
66
- */
67
- value?: string;
68
37
  /**
69
38
  * Whether this is the selected tab
70
39
  */
@@ -82,13 +51,7 @@ type TabProps = PropsWithChildren<{
82
51
  */
83
52
  icon?: FC<IconProps>;
84
53
  }>;
85
- type PanelProps = React.HTMLAttributes<HTMLDivElement> & {
86
- /**
87
- * A value that uniquely identifies this panel, paired with the `UnderlinePanels.Tab` of the same
88
- * `value`. When omitted, tabs and panels are paired by their DOM order.
89
- */
90
- value?: string;
91
- };
54
+ type PanelProps = React.HTMLAttributes<HTMLDivElement>;
92
55
  declare const _default: React.FC<UnderlinePanelsProps> & SlotMarker & {
93
56
  Panel: FCWithSlotMarker<PanelProps>;
94
57
  Tab: FCWithSlotMarker<TabProps>;
@@ -1,9 +1,7 @@
1
1
  import useIsomorphicLayoutEffect from "../../utils/useIsomorphicLayoutEffect.js";
2
2
  import { useResizeObserver } from "../../hooks/useResizeObserver.js";
3
3
  import { useId as useId$1 } from "../../hooks/useId.js";
4
- import { warning } from "../../utils/warning.js";
5
4
  import { isSlot } from "../../utils/is-slot.js";
6
- import { useFeatureFlag } from "../../FeatureFlags/useFeatureFlag.js";
7
5
  import { invariant } from "../../utils/invariant.js";
8
6
  import { UnderlineItem, UnderlineItemList, UnderlineWrapper } from "../../internal/components/UnderlineTabbedInterface.js";
9
7
  import UnderlinePanels_module_css_default from "./UnderlinePanels.module.css.js";
@@ -19,78 +17,43 @@ const UnderlinePanelsContext = /*#__PURE__*/ createContext({
19
17
  iconsVisible: true,
20
18
  loadingCounters: void 0
21
19
  });
22
- const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, children, loadingCounters, className, value: valueProp, defaultValue: defaultValueProp, onChange: onChangeProp, activationMode = "automatic", ...props }) => {
23
- var _tabValues$;
24
- const controlledApiEnabled = useFeatureFlag("primer_react_underline_panels_controlled");
25
- const controlledValue = controlledApiEnabled ? valueProp : void 0;
26
- const defaultValue = controlledApiEnabled ? defaultValueProp : void 0;
27
- const onChange = controlledApiEnabled ? onChangeProp : void 0;
20
+ const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, children, loadingCounters, className, ...props }) => {
28
21
  const [iconsVisible, setIconsVisible] = useState(true);
29
22
  const wrapperRef = useRef(null);
30
23
  const listRef = useRef(null);
31
24
  const parentId = useId$1(props.id);
32
- const [tabs, tabPanels, tabsHaveIcons, selectedFromProps, tabValues, panelValues] = useMemo(() => {
25
+ const [tabs, tabPanels, tabsHaveIcons, selectedFromProps] = useMemo(() => {
33
26
  let tabIndex = 0;
34
27
  let panelIndex = 0;
35
28
  const childrenWithProps = Children.map(children, (child) => {
36
- if (/*#__PURE__*/ isValidElement(child) && (child.type === Tab || isSlot(child, Tab))) {
37
- var _ref;
38
- const value = (_ref = controlledApiEnabled ? child.props.value : void 0) !== null && _ref !== void 0 ? _ref : `${tabIndex}`;
39
- tabIndex++;
40
- return /*#__PURE__*/ cloneElement(child, { value });
41
- }
42
- if (/*#__PURE__*/ isValidElement(child) && (child.type === Panel || isSlot(child, Panel))) {
43
- var _ref2;
44
- const value = (_ref2 = controlledApiEnabled ? child.props.value : void 0) !== null && _ref2 !== void 0 ? _ref2 : `${panelIndex}`;
45
- panelIndex++;
46
- return /*#__PURE__*/ cloneElement(child, { value });
47
- }
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++}` });
48
31
  return child;
49
32
  });
50
33
  const tabs = [];
51
34
  const tabPanels = [];
52
- const tabValues = [];
53
- const panelValues = [];
54
35
  let selectedFromProps;
55
36
  for (const child of Children.toArray(childrenWithProps)) {
56
37
  if (!/*#__PURE__*/ isValidElement(child)) continue;
57
38
  if (child.type === Tab || isSlot(child, Tab)) {
58
- const value = child.props.value;
59
- if (value !== void 0) tabValues.push(value);
60
39
  const ariaSelected = child.props["aria-selected"];
61
- if (ariaSelected === true || ariaSelected === "true") selectedFromProps = value;
40
+ if (ariaSelected === true || ariaSelected === "true") selectedFromProps = `${tabs.length}`;
62
41
  tabs.push(child);
63
- } else if (child.type === Panel || isSlot(child, Panel)) {
64
- const value = child.props.value;
65
- if (value !== void 0) panelValues.push(value);
66
- tabPanels.push(child);
67
- }
42
+ } else if (child.type === Panel || isSlot(child, Panel)) tabPanels.push(child);
68
43
  }
69
44
  return [
70
45
  tabs,
71
46
  tabPanels,
72
47
  tabs.some((tab) => /*#__PURE__*/ React.isValidElement(tab) && tab.props.icon),
73
- selectedFromProps,
74
- tabValues,
75
- panelValues
48
+ selectedFromProps
76
49
  ];
77
- }, [children, controlledApiEnabled]);
78
- const isControlled = controlledValue !== void 0;
79
- const [uncontrolledValue, setUncontrolledValue] = useState(() => {
80
- var _ref3;
81
- return (_ref3 = defaultValue !== null && defaultValue !== void 0 ? defaultValue : selectedFromProps) !== null && _ref3 !== void 0 ? _ref3 : "0";
82
- });
50
+ }, [children]);
51
+ const [selectedValue, setSelectedValue] = useState(() => selectedFromProps !== null && selectedFromProps !== void 0 ? selectedFromProps : "0");
83
52
  const [prevSelectedFromProps, setPrevSelectedFromProps] = useState(selectedFromProps);
84
- if (!isControlled && selectedFromProps !== prevSelectedFromProps) {
53
+ if (selectedFromProps !== prevSelectedFromProps) {
85
54
  setPrevSelectedFromProps(selectedFromProps);
86
- if (selectedFromProps !== void 0 && selectedFromProps !== uncontrolledValue) setUncontrolledValue(selectedFromProps);
55
+ if (selectedFromProps !== void 0 && selectedFromProps !== selectedValue) setSelectedValue(selectedFromProps);
87
56
  }
88
- const selectedValue = isControlled ? controlledValue : uncontrolledValue;
89
- const effectiveValue = tabValues.includes(selectedValue) ? selectedValue : (_tabValues$ = tabValues[0]) !== null && _tabValues$ !== void 0 ? _tabValues$ : selectedValue;
90
- const handleValueChange = (value) => {
91
- if (!isControlled) setUncontrolledValue(value);
92
- onChange === null || onChange === void 0 || onChange({ value });
93
- };
94
57
  const { tabListProps } = useTabList({
95
58
  "aria-label": ariaLabel,
96
59
  "aria-labelledby": ariaLabelledBy,
@@ -115,31 +78,17 @@ const UnderlinePanels = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabel
115
78
  const wrapperWidth = resizeObserverEntries[0].contentRect.width;
116
79
  setIconsVisible(wrapperWidth > listWidthRef.current);
117
80
  }, wrapperRef, []);
118
- {
119
- const selectedTabs = tabs.filter((tab) => {
120
- const ariaSelected = /*#__PURE__*/ React.isValidElement(tab) && tab.props["aria-selected"];
121
- return ariaSelected === true || ariaSelected === "true";
122
- });
123
- !(selectedTabs.length <= 1) && invariant(false, "Only one tab can be selected at a time.");
124
- !(tabs.length === tabPanels.length) && invariant(false, `The number of tabs and panels must be equal. Counted ${tabs.length} tabs and ${tabPanels.length} panels.`);
125
- const duplicateTabValue = tabValues.find((value, index) => tabValues.indexOf(value) !== index);
126
- !(duplicateTabValue === void 0) && invariant(false, `Every tab must have a unique \`value\`. Found duplicate "${duplicateTabValue}".`);
127
- const duplicatePanelValue = panelValues.find((value, index) => panelValues.indexOf(value) !== index);
128
- !(duplicatePanelValue === void 0) && invariant(false, `Every panel must have a unique \`value\`. Found duplicate "${duplicatePanelValue}".`);
129
- const unmatchedTabValue = tabValues.find((value) => !panelValues.includes(value));
130
- !(unmatchedTabValue === void 0) && invariant(false, `Tab with \`value\` "${unmatchedTabValue}" has no matching panel. Give a \`UnderlinePanels.Panel\` the same \`value\`.`);
131
- warning(isControlled && selectedTabs.length > 0, "UnderlinePanels: do not combine the controlled `value` prop with `aria-selected` on a tab. `value` takes precedence; use `value`/`onChange` (or `defaultValue`) to drive selection.");
132
- warning(controlledValue !== void 0 && defaultValue !== void 0, "UnderlinePanels: do not combine the controlled `value` prop with `defaultValue`. `value` takes precedence; use one or the other.");
133
- const providedValue = controlledValue !== null && controlledValue !== void 0 ? controlledValue : defaultValue;
134
- warning(providedValue !== void 0 && tabValues.length > 0 && !tabValues.includes(providedValue), `UnderlinePanels: the selected value "${providedValue}" does not match any tab, so the first tab is selected instead. Provide a \`value\`/\`defaultValue\` that matches a \`UnderlinePanels.Tab\` \`value\`.`);
135
- }
81
+ !(tabs.filter((tab) => {
82
+ const ariaSelected = /*#__PURE__*/ React.isValidElement(tab) && tab.props["aria-selected"];
83
+ return ariaSelected === true || ariaSelected === "true";
84
+ }).length <= 1) && invariant(false, "Only one tab can be selected at a time.");
85
+ !(tabs.length === tabPanels.length) && invariant(false, `The number of tabs and panels must be equal. Counted ${tabs.length} tabs and ${tabPanels.length} panels.`);
136
86
  return /*#__PURE__*/ jsx(UnderlinePanelsContext.Provider, {
137
87
  value: contextValue,
138
88
  children: /*#__PURE__*/ jsxs(Tabs, {
139
89
  id: parentId,
140
- value: effectiveValue,
141
- activationMode,
142
- onValueChange: ({ value }) => handleValueChange(value),
90
+ value: selectedValue,
91
+ onValueChange: ({ value }) => setSelectedValue(value),
143
92
  children: [/*#__PURE__*/ jsx(UnderlineWrapper, {
144
93
  ref: wrapperRef,
145
94
  "data-icons-visible": iconsVisible,
@@ -3394,39 +3394,39 @@
3394
3394
  "source": "https://github.com/primer/react/tree/main/packages/react/src/Flash",
3395
3395
  "id": "flash",
3396
3396
  "name": "Flash",
3397
- "status": "deprecated",
3397
+ "status": "alpha",
3398
3398
  "a11yReviewed": "2025-01-08",
3399
3399
  "stories": [
3400
3400
  {
3401
- "id": "deprecated-components-flash--default",
3401
+ "id": "components-flash--default",
3402
3402
  "code": "() => <Flash>Default</Flash>"
3403
3403
  },
3404
3404
  {
3405
- "id": "deprecated-components-flash-features--success",
3405
+ "id": "components-flash-features--success",
3406
3406
  "code": "() => (\n <Flash\n variant=\"success\"\n style={{\n display: 'grid',\n gridTemplateColumns: 'min-content 1fr minmax(0, auto)',\n gridTemplateAreas: `'visual message actions'`,\n }}\n >\n <div className={classes.Visual}>\n <CheckCircleIcon aria-label=\"Success\" />\n </div>\n <div className={classes.Message}>Success</div>\n </Flash>\n)"
3407
3407
  },
3408
3408
  {
3409
- "id": "deprecated-components-flash-features--danger",
3409
+ "id": "components-flash-features--danger",
3410
3410
  "code": "() => (\n <Flash\n variant=\"danger\"\n style={{\n display: 'grid',\n gridTemplateColumns: 'min-content 1fr minmax(0, auto)',\n gridTemplateAreas: `'visual message actions'`,\n }}\n >\n <div className={classes.Visual}>\n <InfoIcon aria-label=\"Danger\" />\n </div>\n <div className={classes.Message}>Danger</div>\n </Flash>\n)"
3411
3411
  },
3412
3412
  {
3413
- "id": "deprecated-components-flash-features--warning",
3413
+ "id": "components-flash-features--warning",
3414
3414
  "code": "() => (\n <Flash\n variant=\"warning\"\n style={{\n display: 'grid',\n gridTemplateColumns: 'min-content 1fr minmax(0, auto)',\n gridTemplateAreas: `'visual message actions'`,\n }}\n >\n <div className={classes.Visual}>\n <AlertIcon aria-label=\"Warning\" />\n </div>\n <div className={classes.Message}>Warning</div>\n </Flash>\n)"
3415
3415
  },
3416
3416
  {
3417
- "id": "deprecated-components-flash-features--full",
3417
+ "id": "components-flash-features--full",
3418
3418
  "code": "() => (\n <Flash\n full\n style={{\n display: 'grid',\n gridTemplateColumns: 'min-content 1fr minmax(0, auto)',\n gridTemplateAreas: `'visual message actions'`,\n }}\n >\n <div className={classes.Visual}>\n <InfoIcon aria-label=\"Info\" />\n </div>\n <div className={classes.Message}>Full</div>\n </Flash>\n)"
3419
3419
  },
3420
3420
  {
3421
- "id": "deprecated-components-flash-features--with-icon-and-action",
3421
+ "id": "components-flash-features--with-icon-and-action",
3422
3422
  "code": "() => (\n <Flash className={classes.WithIconAndAction}>\n <div className={classes.Visual}>\n <InfoIcon aria-label=\"Info\" />\n </div>\n <div className={classes.Message}>\n This is a flash message with an icon and an action.\n <Link href=\"/\"> Learn more.</Link>\n </div>\n <div className={classes.ActionsResponsive}>\n <Button>Join waitlist</Button>\n </div>\n </Flash>\n)"
3423
3423
  },
3424
3424
  {
3425
- "id": "deprecated-components-flash-features--with-icon-action-dismiss",
3425
+ "id": "components-flash-features--with-icon-action-dismiss",
3426
3426
  "code": "() => (\n <Flash className={classes.WithIconActionDismiss}>\n <div className={classes.Visual}>\n <InfoIcon aria-label=\"Info\" />\n </div>\n <div className={classes.Message}>\n This is a flash message with an icon and an action.\n <Link href=\"/\"> Learn more.</Link>\n </div>\n <div className={classes.ActionsResponsive}>\n <Button>Join waitlist</Button>\n </div>\n <div className={classes.Close}>\n <IconButton variant=\"invisible\" icon={XIcon} aria-label=\"Dismiss\" />\n </div>\n </Flash>\n)"
3427
3427
  }
3428
3428
  ],
3429
- "importPath": "@primer/react/deprecated",
3429
+ "importPath": "@primer/react",
3430
3430
  "props": [
3431
3431
  {
3432
3432
  "name": "full",
@@ -9605,22 +9605,6 @@
9605
9605
  {
9606
9606
  "id": "experimental-components-underlinepanels-features--with-icons-hidden-on-narrow-screen",
9607
9607
  "code": "() => (\n <UnderlinePanels aria-label=\"Tabs with icons\">\n <UnderlinePanels.Tab icon={CodeIcon}>Tab 1</UnderlinePanels.Tab>\n <UnderlinePanels.Tab icon={EyeIcon}>Tab 2</UnderlinePanels.Tab>\n <UnderlinePanels.Tab icon={GitPullRequestIcon}>Tab 3</UnderlinePanels.Tab>\n <UnderlinePanels.Tab icon={CommentDiscussionIcon}>\n Tab 4\n </UnderlinePanels.Tab>\n <UnderlinePanels.Tab icon={PlayIcon}>Tab 5</UnderlinePanels.Tab>\n <UnderlinePanels.Tab icon={ProjectIcon}>Tab 6</UnderlinePanels.Tab>\n <UnderlinePanels.Tab icon={GraphIcon}>Tab 7</UnderlinePanels.Tab>\n <UnderlinePanels.Tab icon={GearIcon}>Tab 8</UnderlinePanels.Tab>\n <UnderlinePanels.Tab icon={ShieldLockIcon}>Tab 9</UnderlinePanels.Tab>\n <UnderlinePanels.Panel>Panel 1</UnderlinePanels.Panel>\n <UnderlinePanels.Panel>Panel 2</UnderlinePanels.Panel>\n <UnderlinePanels.Panel>Panel 3</UnderlinePanels.Panel>\n <UnderlinePanels.Panel>Panel 4</UnderlinePanels.Panel>\n <UnderlinePanels.Panel>Panel 5</UnderlinePanels.Panel>\n <UnderlinePanels.Panel>Panel 6</UnderlinePanels.Panel>\n <UnderlinePanels.Panel>Panel 7</UnderlinePanels.Panel>\n <UnderlinePanels.Panel>Panel 8</UnderlinePanels.Panel>\n <UnderlinePanels.Panel>Panel 9</UnderlinePanels.Panel>\n </UnderlinePanels>\n)"
9608
- },
9609
- {
9610
- "id": "experimental-components-underlinepanels-features--controlled",
9611
- "code": "() => {\n const [refType, setRefType] = useState('branch')\n return (\n <>\n <FlagState />\n <UnderlinePanels\n aria-label=\"Ref type\"\n value={refType}\n onChange={({ value }) => {\n action('onChange')({\n value,\n })\n setRefType(value)\n }}\n >\n <UnderlinePanels.Tab value=\"branch\" icon={GitBranchIcon}>\n Branches\n </UnderlinePanels.Tab>\n <UnderlinePanels.Tab value=\"tag\" icon={TagIcon}>\n Tags\n </UnderlinePanels.Tab>\n <UnderlinePanels.Panel value=\"branch\">\n Find or create a branch…\n </UnderlinePanels.Panel>\n <UnderlinePanels.Panel value=\"tag\">\n Search or create a new tag…\n </UnderlinePanels.Panel>\n </UnderlinePanels>\n <p>\n Selected ref type: <strong>{refType}</strong>\n </p>\n </>\n )\n}"
9612
- },
9613
- {
9614
- "id": "experimental-components-underlinepanels-features--uncontrolled",
9615
- "code": "() => (\n <>\n <FlagState />\n <UnderlinePanels\n aria-label=\"Ref type\"\n defaultValue=\"tag\"\n onChange={({ value }) => {\n action('onChange')({\n value,\n })\n }}\n >\n <UnderlinePanels.Tab value=\"branch\">Branches</UnderlinePanels.Tab>\n <UnderlinePanels.Tab value=\"tag\">Tags</UnderlinePanels.Tab>\n <UnderlinePanels.Panel value=\"branch\">\n Find or create a branch…\n </UnderlinePanels.Panel>\n <UnderlinePanels.Panel value=\"tag\">\n Search or create a new tag…\n </UnderlinePanels.Panel>\n </UnderlinePanels>\n </>\n)"
9616
- },
9617
- {
9618
- "id": "experimental-components-underlinepanels-features--manual-activation",
9619
- "code": "() => {\n const [refType, setRefType] = useState('branch')\n return (\n <>\n <FlagState />\n <p>\n With <code>activationMode=&quot;manual&quot;</code>, arrow keys only\n move focus; press Enter or Space (or click) to commit selection. Prefer\n this when switching tabs triggers async work like a fetch.\n </p>\n <UnderlinePanels\n aria-label=\"Ref type\"\n value={refType}\n activationMode=\"manual\"\n onChange={({ value }) => {\n action('onChange')({\n value,\n })\n setRefType(value)\n }}\n >\n <UnderlinePanels.Tab value=\"branch\">Branches</UnderlinePanels.Tab>\n <UnderlinePanels.Tab value=\"tag\">Tags</UnderlinePanels.Tab>\n <UnderlinePanels.Panel value=\"branch\">\n Find or create a branch…\n </UnderlinePanels.Panel>\n <UnderlinePanels.Panel value=\"tag\">\n Search or create a new tag…\n </UnderlinePanels.Panel>\n </UnderlinePanels>\n </>\n )\n}"
9620
- },
9621
- {
9622
- "id": "experimental-components-underlinepanels-features--in-overlay",
9623
- "code": "() => {\n const [open, setOpen] = useState(false)\n const [refType, setRefType] = useState('branch')\n return (\n <>\n <FlagState />\n <AnchoredOverlay\n open={open}\n onOpen={() => setOpen(true)}\n onClose={() => setOpen(false)}\n renderAnchor={(props) => <Button {...props}>Select ref type</Button>}\n overlayProps={{\n role: 'dialog',\n 'aria-modal': true,\n 'aria-label': 'Select a ref type',\n style: {\n width: '320px',\n },\n }}\n focusZoneSettings={{\n disabled: true,\n }}\n >\n <UnderlinePanels\n aria-label=\"Ref type\"\n value={refType}\n onChange={({ value }) => {\n action('onChange')({\n value,\n })\n setRefType(value)\n }}\n >\n <UnderlinePanels.Tab value=\"branch\">Branches</UnderlinePanels.Tab>\n <UnderlinePanels.Tab value=\"tag\">Tags</UnderlinePanels.Tab>\n <UnderlinePanels.Panel value=\"branch\">\n Find or create a branch…\n </UnderlinePanels.Panel>\n <UnderlinePanels.Panel value=\"tag\">\n Search or create a new tag…\n </UnderlinePanels.Panel>\n </UnderlinePanels>\n </AnchoredOverlay>\n </>\n )\n}"
9624
9608
  }
9625
9609
  ],
9626
9610
  "importPath": "@primer/react/experimental",
@@ -9637,30 +9621,6 @@
9637
9621
  "defaultValue": "",
9638
9622
  "description": "ID of the element containing the name for the tab list"
9639
9623
  },
9640
- {
9641
- "name": "value",
9642
- "type": "string",
9643
- "defaultValue": "",
9644
- "description": "The value of the selected tab, keyed to each `UnderlinePanels.Tab`/`UnderlinePanels.Panel` `value`. Provide this (with `onChange`) for a controlled component where the selected tab is the single source of truth. Requires the `primer_react_underline_panels_controlled` feature flag."
9645
- },
9646
- {
9647
- "name": "defaultValue",
9648
- "type": "string",
9649
- "defaultValue": "",
9650
- "description": "The value of the tab selected by default, for an uncontrolled component. Cannot be combined with `value`."
9651
- },
9652
- {
9653
- "name": "onChange",
9654
- "type": "({value}: {value: string}) => void",
9655
- "defaultValue": "",
9656
- "description": "Callback fired whenever the selected tab changes, for every selection method — pointer, Enter/Space, and Arrow/Home/End keys. Unlike `UnderlinePanels.Tab`'s `onSelect` (which only fires on click and Enter/Space), this makes the selected value usable as a single source of truth for data-driven tabs."
9657
- },
9658
- {
9659
- "name": "activationMode",
9660
- "type": "'automatic' | 'manual'",
9661
- "defaultValue": "'automatic'",
9662
- "description": "Controls how tabs are activated with the keyboard. `'automatic'` selects on focus (Arrow/Home/End select immediately); `'manual'` moves focus only and commits selection on Enter, Space, or click. Prefer `'manual'` when displaying a panel is not instant (e.g. it triggers a network request). Requires the `primer_react_underline_panels_controlled` feature flag."
9663
- },
9664
9624
  {
9665
9625
  "name": "children",
9666
9626
  "type": "Array<UnderlinePanels.Tab | UnderlinePanels.Panel>",
@@ -9691,12 +9651,6 @@
9691
9651
  {
9692
9652
  "name": "UnderlinePanels.Tab",
9693
9653
  "props": [
9694
- {
9695
- "name": "value",
9696
- "type": "string",
9697
- "defaultValue": "",
9698
- "description": "A value that uniquely identifies this tab, paired with the `UnderlinePanels.Panel` of the same `value`. Provide this to use domain values (e.g. `'branch'`) with the container's controlled `value`/`onChange` API. When omitted, tabs and panels are paired by DOM order."
9699
- },
9700
9654
  {
9701
9655
  "name": "aria-selected",
9702
9656
  "type": "| boolean | 'true' | 'false'",
@@ -9707,7 +9661,7 @@
9707
9661
  "name": "onSelect",
9708
9662
  "type": "(event) => void",
9709
9663
  "defaultValue": "",
9710
- "description": "The handler that gets called when the tab is selected. Note: this fires only on click and Enter/Space — not on arrow-key navigation. For a callback that fires for every selection method, use the container's `onChange` prop instead."
9664
+ "description": "The handler that gets called when the tab is selected"
9711
9665
  },
9712
9666
  {
9713
9667
  "name": "counter",
@@ -9725,14 +9679,7 @@
9725
9679
  },
9726
9680
  {
9727
9681
  "name": "UnderlinePanels.Panel",
9728
- "props": [
9729
- {
9730
- "name": "value",
9731
- "type": "string",
9732
- "defaultValue": "",
9733
- "description": "A value that uniquely identifies this panel, paired with the `UnderlinePanels.Tab` of the same `value`. When omitted, tabs and panels are paired by DOM order."
9734
- }
9735
- ],
9682
+ "props": [],
9736
9683
  "passthrough": {
9737
9684
  "element": "div",
9738
9685
  "url": "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div#Attributes"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@primer/react",
3
3
  "type": "module",
4
- "version": "38.37.0-rc.05923388d",
4
+ "version": "38.37.0-rc.0b6dd7e38",
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",