@rescui/chip-list 0.13.6 → 0.14.1
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/lib/chip-list-multi.d.ts +54 -45
- package/lib/chip-list-multi.js +9 -1
- package/lib/chip-list.d.ts +56 -29
- package/lib/chip-list.js +13 -0
- package/lib/index.css +65 -46
- package/lib/index.d.ts +6 -6
- package/lib/parts/chip-list-base.d.ts +31 -24
- package/lib/parts/chip-list-base.js +60 -4
- package/lib/parts/chip-list-context.d.ts +3 -4
- package/lib/parts/chip-list-context.js +2 -1
- package/lib/parts/chip-list.p.module.css.js +24 -23
- package/lib/parts/chip.d.ts +36 -34
- package/lib/parts/chip.js +91 -30
- package/lib/parts/deep-map.d.ts +2 -2
- package/lib/parts/types.d.ts +19 -9
- package/lib/parts/use-items-ids.d.ts +14 -10
- package/lib/parts/use-items-ids.js +64 -8
- package/lib/parts/use-roving-tab-index.d.ts +9 -0
- package/lib/parts/use-roving-tab-index.js +73 -0
- package/lib/parts/use-safe-id.d.ts +2 -0
- package/lib/parts/use-safe-id.js +15 -0
- package/package.json +5 -5
|
@@ -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
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
} =
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
export declare const
|
|
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": "
|
|
3
|
-
"dark": "
|
|
4
|
-
"alignLeft": "
|
|
5
|
-
"alignCenter": "
|
|
6
|
-
"alignRight": "
|
|
7
|
-
"sizeXs": "
|
|
8
|
-
"sizeS": "
|
|
9
|
-
"sizeM": "
|
|
10
|
-
"sizeL": "
|
|
11
|
-
"innerOffsetL": "
|
|
12
|
-
"innerOffsetM": "
|
|
13
|
-
"classic": "
|
|
14
|
-
"rock": "
|
|
15
|
-
"compact": "
|
|
16
|
-
"main": "
|
|
17
|
-
"chipsContainer": "
|
|
18
|
-
"alignIconLeft": "
|
|
19
|
-
"alignIconRight": "
|
|
20
|
-
"chip": "
|
|
21
|
-
"selected": "
|
|
22
|
-
"disabled": "
|
|
23
|
-
"icon": "
|
|
24
|
-
"
|
|
2
|
+
"light": "_light_clap9i_8",
|
|
3
|
+
"dark": "_dark_clap9i_11",
|
|
4
|
+
"alignLeft": "_alignLeft_clap9i_15",
|
|
5
|
+
"alignCenter": "_alignCenter_clap9i_18",
|
|
6
|
+
"alignRight": "_alignRight_clap9i_21",
|
|
7
|
+
"sizeXs": "_sizeXs_clap9i_25",
|
|
8
|
+
"sizeS": "_sizeS_clap9i_28",
|
|
9
|
+
"sizeM": "_sizeM_clap9i_31",
|
|
10
|
+
"sizeL": "_sizeL_clap9i_34",
|
|
11
|
+
"innerOffsetL": "_innerOffsetL_clap9i_38",
|
|
12
|
+
"innerOffsetM": "_innerOffsetM_clap9i_41",
|
|
13
|
+
"classic": "_classic_clap9i_45",
|
|
14
|
+
"rock": "_rock_clap9i_48",
|
|
15
|
+
"compact": "_compact_clap9i_52",
|
|
16
|
+
"main": "_main_clap9i_56",
|
|
17
|
+
"chipsContainer": "_chipsContainer_clap9i_62",
|
|
18
|
+
"alignIconLeft": "_alignIconLeft_clap9i_72",
|
|
19
|
+
"alignIconRight": "_alignIconRight_clap9i_75",
|
|
20
|
+
"chip": "_chip_clap9i_62",
|
|
21
|
+
"selected": "_selected_clap9i_176",
|
|
22
|
+
"disabled": "_disabled_clap9i_183",
|
|
23
|
+
"icon": "_icon_clap9i_216",
|
|
24
|
+
"hiddenInput": "_hiddenInput_clap9i_227",
|
|
25
|
+
"withoutText": "_withoutText_clap9i_244"
|
|
25
26
|
};
|
|
26
27
|
export { styles as default };
|
package/lib/parts/chip.d.ts
CHANGED
|
@@ -1,34 +1,36 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ChipIconPosition } from './types';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
icon
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export
|
|
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(
|
|
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
|
-
|
|
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
|
-
...
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
...
|
|
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
|
-
},
|
|
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((
|
|
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
|
-
} =
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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,
|
package/lib/parts/deep-map.d.ts
CHANGED
|
@@ -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;
|
package/lib/parts/types.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
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<
|
|
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
|
|
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
|
|
49
|
-
|
|
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
|
+
};
|