@rescui/chip-list 0.13.6 → 0.14.1-RUI-296-Build-docs-md-files-for-each-docs-section-2a311c841.18

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.
@@ -1,24 +1,31 @@
1
- import React from 'react';
2
- import { Theme } from '@rescui/ui-contexts';
3
- import { ChipListSizeType, ChipListModeType, ChipListAlignItemsType } from './types';
4
- export declare type ChipListBaseProps<V> = {
5
- className?: string;
6
- size?: ChipListSizeType;
7
- /** Increase inner horizontal offset between the chips. Does nothing when `compact={true}` */
8
- sparse?: boolean;
9
- /** Decrease horizontal paddings, gap and border-radius values */
10
- compact?: boolean;
11
- disabled?: boolean;
12
- theme?: Theme;
13
- mode?: ChipListModeType;
14
- alignItems?: ChipListAlignItemsType;
15
- value: V[];
16
- onChange: (value: V) => void;
17
- children: React.ReactNode;
18
- compareValues?: (activeValue: V[], value: V) => boolean;
19
- /** Identifier for e2e tests */
20
- 'data-e2e'?: string;
21
- /** CSS style object */
22
- style?: React.CSSProperties;
23
- };
24
- export declare function ChipListBase<V>({ size, sparse, compact, disabled, className, mode, alignItems, theme: themeFromProps, value, onChange, compareValues, children, ['data-e2e']: e2eId, style }: ChipListBaseProps<V>): JSX.Element;
1
+ import React from 'react';
2
+ import { Theme } from '@rescui/ui-contexts';
3
+ import { ChipListSizeType, ChipListModeType, ChipListAlignItemsType, ChipListBasePatternType } from './types';
4
+ export type ChipListBaseProps<V> = {
5
+ className?: string;
6
+ size?: ChipListSizeType;
7
+ /** Increase inner horizontal offset between the chips. Does nothing when `compact={true}` */
8
+ sparse?: boolean;
9
+ /** Decrease horizontal paddings, gap and border-radius values */
10
+ compact?: boolean;
11
+ disabled?: boolean;
12
+ theme?: Theme;
13
+ mode?: ChipListModeType;
14
+ alignItems?: ChipListAlignItemsType;
15
+ pattern?: ChipListBasePatternType;
16
+ value: V[];
17
+ onChange: (value: V) => void;
18
+ children: React.ReactNode;
19
+ compareValues?: (activeValue: V[], value: V) => boolean;
20
+ /** Optional name of all `input[type="radio"]` children. */
21
+ name?: string;
22
+ /** Optional a11y label for the chip list. Recommended to use either that or `aria-labelledby`. */
23
+ 'aria-label'?: string;
24
+ /** Optional a11y label reference for the chip list. Recommended to use either that or `aria-label`. */
25
+ 'aria-labelledby'?: string;
26
+ /** Identifier for e2e tests */
27
+ 'data-e2e'?: string;
28
+ /** CSS style object */
29
+ style?: React.CSSProperties;
30
+ };
31
+ export declare function ChipListBase<V>({ size, sparse, compact, disabled, className, mode, alignItems, pattern, theme: themeFromProps, value, onChange, compareValues, children, name, ['aria-label']: ariaLabel, ['aria-labelledby']: ariaLabelledBy, ['data-e2e']: e2eId, style }: ChipListBaseProps<V>): React.JSX.Element;
@@ -5,6 +5,8 @@ import { useThemeWithUndefined } from '@rescui/ui-contexts';
5
5
  import { ChipListContext } from './chip-list-context.js';
6
6
  import styles from './chip-list.p.module.css.js';
7
7
  import { useItemsIds } from './use-items-ids.js';
8
+ import { useRovingTabIndex } from './use-roving-tab-index.js';
9
+ const IS_PRODUCTION = process.env.NODE_ENV === 'production';
8
10
  const STYLES_SIZES = {
9
11
  xs: styles.sizeXs,
10
12
  s: styles.sizeS,
@@ -17,7 +19,21 @@ const ALIGN_ITEMS_STYLES = {
17
19
  right: styles.alignRight
18
20
  };
19
21
 
20
- function ChipListBase(_ref) {
22
+ const calculateAutoPattern = _ref => {
23
+ let {
24
+ hasHref,
25
+ hasControls
26
+ } = _ref;
27
+
28
+ if (hasControls) {
29
+ return 'tablist';
30
+ }
31
+
32
+ return hasHref ? 'navigation' : 'radiogroup';
33
+ }; // eslint-disable-next-line complexity
34
+
35
+
36
+ function ChipListBase(_ref2) {
21
37
  let {
22
38
  size = 'm',
23
39
  sparse = false,
@@ -26,34 +42,74 @@ function ChipListBase(_ref) {
26
42
  className,
27
43
  mode = 'classic',
28
44
  alignItems = 'left',
45
+ pattern = 'auto',
29
46
  theme: themeFromProps,
30
47
  value,
31
48
  onChange,
32
49
  compareValues,
33
50
  children,
51
+ name,
52
+ ['aria-label']: ariaLabel,
53
+ ['aria-labelledby']: ariaLabelledBy,
34
54
  ['data-e2e']: e2eId,
35
55
  style
36
- } = _ref;
56
+ } = _ref2;
37
57
  const theme = useThemeWithUndefined(themeFromProps);
38
58
  const {
39
59
  childrenWithValues,
40
- selectedIds
60
+ selectedIds,
61
+ hasHref,
62
+ hasControls,
63
+ allItemsHaveControls,
64
+ tabRefs
41
65
  } = useItemsIds({
42
66
  children,
43
67
  compareValues,
44
68
  value
45
69
  });
70
+ const resolvedPattern = pattern === 'auto' ? calculateAutoPattern({
71
+ hasHref,
72
+ hasControls
73
+ }) : pattern;
74
+ const rovingTabIndex = useRovingTabIndex({
75
+ enabled: resolvedPattern === 'tablist' && !disabled,
76
+ selectedId: selectedIds[0],
77
+ tabRefs
78
+ }); // eslint-disable-next-line complexity
79
+
80
+ React.useEffect(() => {
81
+ if (!IS_PRODUCTION && resolvedPattern === 'radiogroup' && hasHref) {
82
+ // eslint-disable-next-line no-console
83
+ console.warn('[RescUI] ChipList with pattern="radiogroup" contains Chip with href. href is ignored. Use pattern="navigation" or pattern="auto" for link chips.');
84
+ }
85
+
86
+ if (!IS_PRODUCTION && resolvedPattern !== 'tablist' && hasControls) {
87
+ // eslint-disable-next-line no-console
88
+ console.warn('[RescUI] ChipList with pattern other than "tablist" contains Chip with aria-controls. Use pattern="tablist" or pattern="auto" for controlled tab panels.');
89
+ }
90
+
91
+ if (!IS_PRODUCTION && resolvedPattern === 'tablist' && !allItemsHaveControls) {
92
+ // eslint-disable-next-line no-console
93
+ console.warn('[RescUI] ChipList with pattern="tablist" requires every Chip to have aria-controls. Every tab should control a panel.');
94
+ }
95
+ }, [allItemsHaveControls, hasControls, hasHref, resolvedPattern]);
46
96
  return /*#__PURE__*/React.createElement(ChipListContext.Provider, {
47
97
  value: {
48
98
  onChange,
49
99
  selectedIds,
50
- disabled
100
+ disabled,
101
+ pattern: resolvedPattern,
102
+ name,
103
+ rovingTabIndex: resolvedPattern === 'tablist' ? rovingTabIndex : undefined
51
104
  }
52
105
  }, /*#__PURE__*/React.createElement("div", {
53
106
  className: cn(styles.main, styles[theme], styles[mode], STYLES_SIZES[size], sparse ? styles.innerOffsetL : styles.innerOffsetM, compact && styles.compact, disabled && styles.disabled, ALIGN_ITEMS_STYLES[alignItems], className),
54
107
  "data-e2e": e2eId ? `${e2eId}__chips-container` : null,
55
108
  "data-rs-internal": 'chip-list',
56
109
  "data-test": 'chip-list',
110
+ role: resolvedPattern,
111
+ "aria-label": ariaLabel,
112
+ "aria-labelledby": ariaLabelledBy,
57
113
  style: style
58
114
  }, /*#__PURE__*/React.createElement("div", {
59
115
  className: styles.chipsContainer
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
- import { ChipListContextType } from './types';
3
- export declare const ChipListContext: import("react").Context<ChipListContextType<unknown>>;
4
- export declare const useChipListContext: () => ChipListContextType<unknown>;
1
+ import { ChipListContextType } from './types';
2
+ export declare const ChipListContext: import("react").Context<ChipListContextType<unknown>>;
3
+ export declare const useChipListContext: () => ChipListContextType<unknown>;
@@ -4,7 +4,8 @@ const ChipListContext = /*#__PURE__*/createContext({
4
4
  // eslint-disable-next-line @typescript-eslint/no-empty-function
5
5
  onChange: () => {},
6
6
  selectedIds: [],
7
- disabled: false
7
+ disabled: false,
8
+ pattern: 'navigation'
8
9
  });
9
10
 
10
11
  const useChipListContext = () => useContext(ChipListContext);
@@ -1,26 +1,27 @@
1
1
  var styles = {
2
- "light": "_light_1257dbr_8",
3
- "dark": "_dark_1257dbr_11",
4
- "alignLeft": "_alignLeft_1257dbr_15",
5
- "alignCenter": "_alignCenter_1257dbr_18",
6
- "alignRight": "_alignRight_1257dbr_21",
7
- "sizeXs": "_sizeXs_1257dbr_25",
8
- "sizeS": "_sizeS_1257dbr_28",
9
- "sizeM": "_sizeM_1257dbr_31",
10
- "sizeL": "_sizeL_1257dbr_34",
11
- "innerOffsetL": "_innerOffsetL_1257dbr_38",
12
- "innerOffsetM": "_innerOffsetM_1257dbr_41",
13
- "classic": "_classic_1257dbr_45",
14
- "rock": "_rock_1257dbr_48",
15
- "compact": "_compact_1257dbr_52",
16
- "main": "_main_1257dbr_56",
17
- "chipsContainer": "_chipsContainer_1257dbr_62",
18
- "alignIconLeft": "_alignIconLeft_1257dbr_72",
19
- "alignIconRight": "_alignIconRight_1257dbr_75",
20
- "chip": "_chip_1257dbr_62",
21
- "selected": "_selected_1257dbr_172",
22
- "disabled": "_disabled_1257dbr_179",
23
- "icon": "_icon_1257dbr_212",
24
- "withoutText": "_withoutText_1257dbr_223"
2
+ "light": "_light_1dvs8ac_8",
3
+ "dark": "_dark_1dvs8ac_11",
4
+ "alignLeft": "_alignLeft_1dvs8ac_15",
5
+ "alignCenter": "_alignCenter_1dvs8ac_18",
6
+ "alignRight": "_alignRight_1dvs8ac_21",
7
+ "sizeXs": "_sizeXs_1dvs8ac_25",
8
+ "sizeS": "_sizeS_1dvs8ac_28",
9
+ "sizeM": "_sizeM_1dvs8ac_31",
10
+ "sizeL": "_sizeL_1dvs8ac_34",
11
+ "innerOffsetL": "_innerOffsetL_1dvs8ac_38",
12
+ "innerOffsetM": "_innerOffsetM_1dvs8ac_41",
13
+ "classic": "_classic_1dvs8ac_45",
14
+ "rock": "_rock_1dvs8ac_48",
15
+ "compact": "_compact_1dvs8ac_52",
16
+ "main": "_main_1dvs8ac_56",
17
+ "chipsContainer": "_chipsContainer_1dvs8ac_62",
18
+ "alignIconLeft": "_alignIconLeft_1dvs8ac_72",
19
+ "alignIconRight": "_alignIconRight_1dvs8ac_75",
20
+ "chip": "_chip_1dvs8ac_62",
21
+ "selected": "_selected_1dvs8ac_176",
22
+ "disabled": "_disabled_1dvs8ac_183",
23
+ "icon": "_icon_1dvs8ac_216",
24
+ "hiddenInput": "_hiddenInput_1dvs8ac_227",
25
+ "withoutText": "_withoutText_1dvs8ac_244"
25
26
  };
26
27
  export { styles as default };
@@ -1,34 +1,36 @@
1
- import React from 'react';
2
- import { ChipIconPosition } from './types';
3
- export declare type ChipExplicitProps = {
4
- className?: string;
5
- /**
6
- * Chip value
7
- * @type ValueType
8
- */
9
- value?: unknown;
10
- /** @ignore */
11
- itemId?: number;
12
- /** @ignore */
13
- onClick?: React.MouseEventHandler<HTMLElement>;
14
- /** @ignore */
15
- onKeyDown?: React.KeyboardEventHandler<HTMLElement>;
16
- /** Identifier for e2e tests */
17
- 'data-e2e'?: string;
18
- /** Href for link-type Chip */
19
- href?: string;
20
- children?: React.ReactNode;
21
- } & ({
22
- /** Additional icon */
23
- icon: React.ReactNode;
24
- /** Additional icon position */
25
- iconPosition?: ChipIconPosition;
26
- } | {
27
- icon?: never;
28
- iconPosition?: never;
29
- });
30
- declare type ChipButtonProps = ChipExplicitProps & React.ButtonHTMLAttributes<HTMLButtonElement>;
31
- declare type ChipAnchorProps = ChipExplicitProps & React.AnchorHTMLAttributes<HTMLAnchorElement>;
32
- export declare type ChipProps = ChipButtonProps | ChipAnchorProps;
33
- declare const Chip: React.ForwardRefExoticComponent<ChipProps & React.RefAttributes<HTMLButtonElement | HTMLAnchorElement>>;
34
- export default Chip;
1
+ import React from 'react';
2
+ import { ChipIconPosition } from './types';
3
+ type InheritType<Main, Parent> = Main & Omit<Parent, keyof Main>;
4
+ export type ChipOwnProps = {
5
+ className?: string;
6
+ /**
7
+ * Chip value
8
+ * @type ValueType
9
+ */
10
+ value?: unknown;
11
+ /** @ignore */
12
+ itemId?: number;
13
+ /** @ignore */
14
+ onClick?: React.MouseEventHandler<HTMLElement>;
15
+ /** @ignore */
16
+ onKeyDown?: React.KeyboardEventHandler<HTMLElement>;
17
+ /** Identifier for e2e tests */
18
+ 'data-e2e'?: string;
19
+ /** Href for link-type Chip */
20
+ href?: string;
21
+ children?: React.ReactNode;
22
+ } & ({
23
+ /** Additional icon */
24
+ icon: React.ReactNode;
25
+ /** Additional icon position */
26
+ iconPosition?: ChipIconPosition;
27
+ } | {
28
+ icon?: never;
29
+ iconPosition?: never;
30
+ });
31
+ type ChipLabelProps = InheritType<ChipOwnProps, React.LabelHTMLAttributes<HTMLLabelElement>>;
32
+ type ChipButtonProps = InheritType<ChipOwnProps, React.ButtonHTMLAttributes<HTMLButtonElement>>;
33
+ type ChipAnchorProps = InheritType<ChipOwnProps, React.AnchorHTMLAttributes<HTMLAnchorElement>>;
34
+ export type ChipProps = ChipLabelProps | ChipButtonProps | ChipAnchorProps;
35
+ declare const Chip: React.ForwardRefExoticComponent<ChipProps & React.RefAttributes<HTMLLabelElement | HTMLButtonElement | HTMLAnchorElement>>;
36
+ export default Chip;
package/lib/parts/chip.js CHANGED
@@ -5,31 +5,108 @@ import PropTypes from 'prop-types';
5
5
  import cn from 'classnames';
6
6
  import styles from './chip-list.p.module.css.js';
7
7
  import { useChipListContext } from './chip-list-context.js';
8
- const ChipTag = /*#__PURE__*/forwardRef((props, ref) => {
8
+ const ChipTag = /*#__PURE__*/forwardRef( // eslint-disable-next-line complexity
9
+ (_ref, ref) => {
10
+ let {
11
+ selected,
12
+ onValueChange,
13
+ itemId,
14
+ tabIndex,
15
+ children,
16
+ value: chipValue,
17
+ onClick,
18
+ onKeyDown,
19
+ href,
20
+ ...restProps
21
+ } = _ref;
9
22
  const {
10
- disabled
23
+ disabled,
24
+ pattern,
25
+ name: hiddenInputName,
26
+ rovingTabIndex
11
27
  } = useChipListContext();
28
+ let resolvedTabIndex = disabled ? -1 : tabIndex;
29
+
30
+ if (pattern === 'radiogroup' || pattern === 'group') {
31
+ const labelProps = { ...restProps
32
+ };
33
+ return /*#__PURE__*/React.createElement("label", {
34
+ ref: ref,
35
+ ...labelProps
36
+ }, /*#__PURE__*/React.createElement("input", {
37
+ type: pattern === 'group' ? 'checkbox' : 'radio',
38
+ value: String(chipValue),
39
+ onChange: onValueChange,
40
+ onClick: onClick,
41
+ onKeyDown: onKeyDown,
42
+ className: styles.hiddenInput,
43
+ checked: selected,
44
+ disabled: disabled,
45
+ name: hiddenInputName,
46
+ tabIndex: resolvedTabIndex
47
+ }), children);
48
+ }
49
+
50
+ const handleNavigationClick = e => {
51
+ onClick?.(e);
52
+ onValueChange();
53
+ };
12
54
 
13
- if (typeof props.href !== 'undefined') {
55
+ const handleNavigationKeyDown = e => {
56
+ onKeyDown?.(e);
57
+
58
+ if (pattern === 'tablist' && typeof itemId !== 'undefined') {
59
+ rovingTabIndex?.onTabKeyDown(e, itemId);
60
+ }
61
+
62
+ if (e.key === ' ') {
63
+ e.target.click();
64
+ e.preventDefault();
65
+ }
66
+ };
67
+
68
+ const isTablist = pattern === 'tablist';
69
+ const autoRole = isTablist ? 'tab' : undefined;
70
+ const autoAriaCurrent = selected && !isTablist ? 'page' : undefined;
71
+ const autoAriaSelected = isTablist ? selected : undefined;
72
+
73
+ if (isTablist && !disabled) {
74
+ resolvedTabIndex = itemId === rovingTabIndex?.currentTabStopId ? 0 : -1;
75
+ }
76
+
77
+ if (typeof href !== 'undefined') {
14
78
  return /*#__PURE__*/React.createElement("a", {
15
79
  ref: ref,
16
- ...props,
17
- tabIndex: disabled ? -1 : props.tabIndex
18
- }, props.children);
80
+ ...restProps,
81
+ href: href,
82
+ role: restProps.role ?? autoRole,
83
+ "aria-current": restProps['aria-current'] ?? autoAriaCurrent,
84
+ "aria-selected": restProps['aria-selected'] ?? autoAriaSelected,
85
+ "aria-disabled": disabled || undefined,
86
+ onClick: handleNavigationClick,
87
+ onKeyDown: handleNavigationKeyDown,
88
+ tabIndex: resolvedTabIndex
89
+ }, children);
19
90
  }
20
91
 
21
92
  return /*#__PURE__*/React.createElement("button", {
22
93
  ref: ref,
23
- ...props,
94
+ ...restProps,
95
+ role: restProps.role ?? autoRole,
96
+ "aria-current": restProps['aria-current'] ?? autoAriaCurrent,
97
+ "aria-selected": restProps['aria-selected'] ?? autoAriaSelected,
24
98
  disabled: disabled,
99
+ onClick: handleNavigationClick,
100
+ onKeyDown: handleNavigationKeyDown,
101
+ tabIndex: resolvedTabIndex,
25
102
  type: "button"
26
- }, props.children);
103
+ }, children);
27
104
  });
28
105
  ChipTag.displayName = 'ChipTag'; // check for ignored jsx values and empty string, more info https://reactjs.org/docs/jsx-in-depth.html#booleans-null-and-undefined-are-ignored
29
106
 
30
107
  const isWithoutChildren = children => children === null || typeof children === 'undefined' || children === false || children === true || children === '';
31
108
 
32
- const Chip = /*#__PURE__*/forwardRef((_ref, ref) => {
109
+ const Chip = /*#__PURE__*/forwardRef((_ref2, ref) => {
33
110
  let {
34
111
  value,
35
112
  className,
@@ -37,11 +114,9 @@ const Chip = /*#__PURE__*/forwardRef((_ref, ref) => {
37
114
  icon,
38
115
  iconPosition = 'left',
39
116
  itemId,
40
- onClick,
41
- onKeyDown,
42
117
  ['data-e2e']: e2eId,
43
118
  ...restProps
44
- } = _ref;
119
+ } = _ref2;
45
120
  const {
46
121
  onChange,
47
122
  selectedIds
@@ -55,24 +130,10 @@ const Chip = /*#__PURE__*/forwardRef((_ref, ref) => {
55
130
  [styles.selected]: selected,
56
131
  [styles.withoutText]: isWithoutText
57
132
  }, iconPosition === 'right' ? styles.alignIconRight : styles.alignIconLeft, className),
58
- onClick: e => {
59
- if (onClick) {
60
- onClick(e);
61
- }
62
-
63
- onChange(value);
64
- },
65
- onKeyDown: e => {
66
- if (e.key === ' ') {
67
- e.target.click();
68
- e.preventDefault();
69
- }
70
-
71
- if (onKeyDown) {
72
- onKeyDown(e);
73
- }
74
- },
75
- role: "tab",
133
+ onValueChange: () => onChange(value),
134
+ itemId: itemId,
135
+ selected: selected,
136
+ value: value,
76
137
  tabIndex: restProps.tabIndex ?? 0,
77
138
  ref: ref,
78
139
  "data-e2e": e2eId,
@@ -1,2 +1,2 @@
1
- import React from 'react';
2
- export declare function deepMap(children: React.ReactNode, fn: (child: React.ReactNode) => React.ReactNode): React.ReactNode;
1
+ import React from 'react';
2
+ export declare function deepMap(children: React.ReactNode, fn: (child: React.ReactNode) => React.ReactNode): React.ReactNode;
@@ -1,9 +1,19 @@
1
- export declare type ChipListSizeType = 'l' | 'm' | 's' | 'xs';
2
- export declare type ChipListModeType = 'classic' | 'rock';
3
- export declare type ChipListAlignItemsType = 'left' | 'center' | 'right';
4
- export declare type ChipIconPosition = 'left' | 'right';
5
- export declare type ChipListContextType<V> = {
6
- onChange: (value: V) => void;
7
- selectedIds?: number[];
8
- disabled?: boolean;
9
- };
1
+ import type React from 'react';
2
+ export type ChipListSizeType = 'l' | 'm' | 's' | 'xs';
3
+ export type ChipListModeType = 'classic' | 'rock';
4
+ export type ChipListAlignItemsType = 'left' | 'center' | 'right';
5
+ export type ChipListResolvedPatternType = 'radiogroup' | 'group' | 'navigation' | 'tablist';
6
+ export type ChipListPatternType = 'radiogroup' | 'navigation' | 'tablist' | 'auto';
7
+ export type ChipListBasePatternType = ChipListPatternType | 'group';
8
+ export type ChipIconPosition = 'left' | 'right';
9
+ export type ChipListContextType<V> = {
10
+ onChange: (value: V) => void;
11
+ selectedIds?: number[];
12
+ disabled?: boolean;
13
+ pattern?: ChipListResolvedPatternType;
14
+ name?: string;
15
+ rovingTabIndex?: {
16
+ currentTabStopId: number;
17
+ onTabKeyDown: (e: React.KeyboardEvent<HTMLElement>, itemId: number) => void;
18
+ };
19
+ };
@@ -1,10 +1,14 @@
1
- import React from 'react';
2
- export declare type UseItemsIdsOpts<V> = {
3
- children: React.ReactNode;
4
- compareValues: (activeValue: V[], value: V) => boolean;
5
- value: V[];
6
- };
7
- export declare const useItemsIds: <V>({ children, compareValues, value }: UseItemsIdsOpts<V>) => {
8
- childrenWithValues: (string | number | React.ReactElement<any, string | React.JSXElementConstructor<any>> | React.ReactFragment | React.ReactPortal)[];
9
- selectedIds: number[];
10
- };
1
+ import React from 'react';
2
+ export type UseItemsIdsOpts<V> = {
3
+ children: React.ReactNode;
4
+ compareValues: (activeValue: V[], value: V) => boolean;
5
+ value: V[];
6
+ };
7
+ export declare const useItemsIds: <V>({ children, compareValues, value }: UseItemsIdsOpts<V>) => {
8
+ childrenWithValues: (string | number | bigint | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode>>)[];
9
+ selectedIds: number[];
10
+ hasHref: boolean;
11
+ hasControls: boolean;
12
+ allItemsHaveControls: boolean;
13
+ tabRefs: React.RefObject<Map<number, HTMLElement>>;
14
+ };
@@ -1,4 +1,4 @@
1
- import React, { useMemo, cloneElement } from 'react';
1
+ import React, { useRef, useCallback, useMemo, cloneElement } from 'react';
2
2
  import { deepMap } from './deep-map.js';
3
3
  import Chip from './chip.js';
4
4
 
@@ -6,27 +6,72 @@ function patchItemIfNeeded(_ref) {
6
6
  let {
7
7
  child,
8
8
  itemId,
9
- childValue
9
+ childValue,
10
+ ref
10
11
  } = _ref;
11
12
  return /*#__PURE__*/cloneElement(child, {
12
13
  itemId,
13
- value: childValue
14
+ value: childValue,
15
+ ref
14
16
  });
15
17
  }
16
18
 
19
+ const setRef = (ref, value) => {
20
+ if (typeof ref === 'function') {
21
+ ref(value);
22
+ } else if (ref) {
23
+ ref.current = value;
24
+ }
25
+ };
26
+
27
+ const getElementRef = child => child.ref;
28
+
17
29
  const hasElValue = el => el.props?.value !== null && typeof el.props?.value !== 'undefined';
18
30
 
31
+ const hasElHref = el => typeof el.props?.href !== 'undefined';
32
+
33
+ const hasElAriaControls = el => typeof el.props?.['aria-controls'] !== 'undefined';
34
+
19
35
  const useItemsIds = _ref2 => {
20
36
  let {
21
37
  children,
22
38
  compareValues,
23
39
  value
24
40
  } = _ref2;
25
- const [childrenWithValues, selectedIds] = useMemo(() => {
41
+ const tabRefs = useRef(new Map());
42
+ const createChipRef = useCallback((itemId, childRef) => node => {
43
+ if (node) {
44
+ tabRefs.current.set(itemId, node);
45
+ } else {
46
+ tabRefs.current.delete(itemId);
47
+ }
48
+
49
+ setRef(childRef, node);
50
+ }, []);
51
+ const {
52
+ childrenWithValues,
53
+ selectedIds,
54
+ hasHref,
55
+ hasControls,
56
+ allItemsHaveControls
57
+ } = useMemo(() => {
26
58
  let counter = 0;
27
59
  const currentSelectedItemIds = [];
60
+ let currentHasHref = false;
61
+ let currentHasControls = false;
62
+ let currentAllItemsHaveControls = true;
28
63
  const items = deepMap(children, child => {
29
64
  if (child.type === Chip) {
65
+ if (hasElHref(child)) {
66
+ currentHasHref = true;
67
+ }
68
+
69
+ if (hasElAriaControls(child)) {
70
+ currentHasControls = true;
71
+ } else {
72
+ currentAllItemsHaveControls = false;
73
+ }
74
+
30
75
  const childValue = hasElValue(child) ? child.props.value : counter;
31
76
  const isSelectedItem = compareValues(value, childValue);
32
77
 
@@ -37,7 +82,8 @@ const useItemsIds = _ref2 => {
37
82
  const patchedItem = patchItemIfNeeded({
38
83
  child,
39
84
  itemId: counter,
40
- childValue
85
+ childValue,
86
+ ref: createChipRef(counter, getElementRef(child))
41
87
  });
42
88
  counter += 1;
43
89
  return patchedItem;
@@ -45,11 +91,21 @@ const useItemsIds = _ref2 => {
45
91
  return child;
46
92
  }
47
93
  });
48
- return [React.Children.toArray(items), currentSelectedItemIds];
49
- }, [children, value, compareValues]);
94
+ return {
95
+ childrenWithValues: React.Children.toArray(items),
96
+ selectedIds: currentSelectedItemIds,
97
+ hasHref: currentHasHref,
98
+ hasControls: currentHasControls,
99
+ allItemsHaveControls: counter > 0 && currentAllItemsHaveControls
100
+ };
101
+ }, [children, value, compareValues, createChipRef]);
50
102
  return {
51
103
  childrenWithValues,
52
- selectedIds
104
+ selectedIds,
105
+ hasHref,
106
+ hasControls,
107
+ allItemsHaveControls,
108
+ tabRefs
53
109
  };
54
110
  };
55
111
 
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ export declare const useRovingTabIndex: ({ enabled, selectedId, tabRefs }: {
3
+ enabled: boolean;
4
+ selectedId?: number;
5
+ tabRefs?: React.MutableRefObject<Map<number, HTMLElement>>;
6
+ }) => {
7
+ currentTabStopId: number;
8
+ onTabKeyDown: (e: React.KeyboardEvent<HTMLElement>, itemId: number) => void;
9
+ };