@rescui/chip-list 0.13.5 → 0.14.0

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.
@@ -20,10 +20,16 @@ export declare type ChipListMultiProps<ValueType> = {
20
20
  compareValues?: (activeValue: ValueType[], value: ValueType) => boolean;
21
21
  /** Identifier for e2e tests */
22
22
  'data-e2e'?: string;
23
+ /** Optional name of all `input[type="checkbox"]` children. */
24
+ name?: string;
25
+ /** Optional a11y label for the chip list. Recommended to use either that or `aria-labelledby`. */
26
+ 'aria-label'?: string;
27
+ /** Optional a11y label reference for the chip list. Recommended to use either that or `aria-label`. */
28
+ 'aria-labelledby'?: string;
23
29
  /** CSS style object */
24
30
  style?: React.CSSProperties;
25
31
  };
26
- export declare function ChipListMulti<ValueType>({ onChange, compareValues, value, children, size, sparse, compact, disabled, mode, alignItems, ...props }: ChipListMultiProps<ValueType>): JSX.Element;
32
+ export declare function ChipListMulti<ValueType>({ onChange, compareValues, value, children, size, sparse, compact, disabled, mode, alignItems, name, ...props }: ChipListMultiProps<ValueType>): JSX.Element;
27
33
  export declare namespace ChipListMulti {
28
34
  var displayName: string;
29
35
  var propTypes: {
@@ -38,6 +44,9 @@ export declare namespace ChipListMulti {
38
44
  value: PropTypes.Validator<any[]>;
39
45
  onChange: PropTypes.Validator<(...args: any[]) => any>;
40
46
  compareValues: PropTypes.Requireable<(...args: any[]) => any>;
47
+ name: PropTypes.Requireable<string>;
48
+ 'aria-label': PropTypes.Requireable<string>;
49
+ 'aria-labelledby': PropTypes.Requireable<string>;
41
50
  'data-e2e': PropTypes.Requireable<string>;
42
51
  style: PropTypes.Requireable<object>;
43
52
  };
@@ -3,6 +3,7 @@ import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/ins
3
3
  import React from 'react';
4
4
  import PropTypes from 'prop-types';
5
5
  import { ChipListBase } from './parts/chip-list-base.js';
6
+ import { useSafeId } from './parts/use-safe-id.js';
6
7
 
7
8
  const compareValuesDefault = (activeValues, value) => Array.isArray(activeValues) && _includesInstanceProperty(activeValues).call(activeValues, value);
8
9
 
@@ -18,8 +19,10 @@ function ChipListMulti(_ref) {
18
19
  disabled = false,
19
20
  mode = 'classic',
20
21
  alignItems = 'left',
22
+ name,
21
23
  ...props
22
24
  } = _ref;
25
+ const uniqueId = useSafeId();
23
26
  return /*#__PURE__*/React.createElement(ChipListBase, {
24
27
  compareValues: compareValues,
25
28
  value: value,
@@ -33,7 +36,9 @@ function ChipListMulti(_ref) {
33
36
  disabled: disabled,
34
37
  mode: mode,
35
38
  alignItems: alignItems,
36
- ...props
39
+ ...props,
40
+ pattern: "group",
41
+ name: name ?? `rs-chip-list-multi-${uniqueId}`
37
42
  }, children);
38
43
  }
39
44
 
@@ -50,6 +55,9 @@ ChipListMulti.propTypes = {
50
55
  value: PropTypes.array.isRequired,
51
56
  onChange: PropTypes.func.isRequired,
52
57
  compareValues: PropTypes.func,
58
+ name: PropTypes.string,
59
+ 'aria-label': PropTypes.string,
60
+ 'aria-labelledby': PropTypes.string,
53
61
  'data-e2e': PropTypes.string,
54
62
  style: PropTypes.object
55
63
  };
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
+ import PropTypes from 'prop-types';
2
3
  import { Theme } from '@rescui/ui-contexts';
3
- import { ChipListSizeType, ChipListModeType, ChipListAlignItemsType } from './parts/types';
4
+ import { ChipListSizeType, ChipListModeType, ChipListAlignItemsType, ChipListPatternType } from './parts/types';
4
5
  export declare const compareValuesDefault: <V>(activeValue: V, value: V) => boolean;
5
6
  export declare type ChipListProps<ValueType> = {
6
7
  className?: string;
@@ -13,17 +14,43 @@ export declare type ChipListProps<ValueType> = {
13
14
  theme?: Theme;
14
15
  mode?: ChipListModeType;
15
16
  alignItems?: ChipListAlignItemsType;
17
+ /** Behavior pattern. `auto` renders chips with `aria-controls` as `tablist`, links as `navigation`, and chips without links as `radiogroup`. */
18
+ pattern?: ChipListPatternType;
16
19
  value: ValueType;
17
20
  onChange: (value: ValueType) => void;
18
21
  children: React.ReactNode;
19
22
  compareValues?: (activeValue: ValueType, value: ValueType) => boolean;
23
+ /** Optional name of all `input[type="radio"]` children. */
24
+ name?: string;
25
+ /** Optional a11y label for the chip list. Recommended to use either that or `aria-labelledby`. */
26
+ 'aria-label'?: string;
27
+ /** Optional a11y label reference for the chip list. Recommended to use either that or `aria-label`. */
28
+ 'aria-labelledby'?: string;
20
29
  /** Identifier for e2e tests */
21
30
  'data-e2e'?: string;
22
31
  /** CSS style object */
23
32
  style?: React.CSSProperties;
24
33
  };
25
- export declare function ChipList<ValueType>({ children, compareValues, value, onChange, size, sparse, compact, disabled, mode, alignItems, ...props }: ChipListProps<ValueType>): JSX.Element;
34
+ export declare function ChipList<ValueType>({ children, compareValues, value, onChange, size, sparse, compact, disabled, mode, alignItems, pattern, name, ...props }: ChipListProps<ValueType>): JSX.Element;
26
35
  export declare namespace ChipList {
27
36
  var displayName: string;
28
- var propTypes: {};
37
+ var propTypes: {
38
+ size: PropTypes.Requireable<string>;
39
+ sparse: PropTypes.Requireable<boolean>;
40
+ compact: PropTypes.Requireable<boolean>;
41
+ disabled: PropTypes.Requireable<boolean>;
42
+ theme: PropTypes.Requireable<string>;
43
+ className: PropTypes.Requireable<string>;
44
+ mode: PropTypes.Requireable<string>;
45
+ alignItems: PropTypes.Requireable<string>;
46
+ pattern: PropTypes.Requireable<string>;
47
+ value: PropTypes.Validator<any>;
48
+ onChange: PropTypes.Validator<(...args: any[]) => any>;
49
+ compareValues: PropTypes.Requireable<(...args: any[]) => any>;
50
+ name: PropTypes.Requireable<string>;
51
+ 'aria-label': PropTypes.Requireable<string>;
52
+ 'aria-labelledby': PropTypes.Requireable<string>;
53
+ 'data-e2e': PropTypes.Requireable<string>;
54
+ style: PropTypes.Requireable<object>;
55
+ };
29
56
  }
package/lib/chip-list.js CHANGED
@@ -1,11 +1,15 @@
1
1
  "use client";
2
+ import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
2
3
  import React from 'react';
3
4
  import PropTypes from 'prop-types';
4
5
  import { ChipListBase } from './parts/chip-list-base.js';
6
+ import { useSafeId } from './parts/use-safe-id.js';
5
7
 
6
8
  const compareValuesDefault = (activeValue, value) => activeValue === value;
7
9
 
8
10
  function ChipList(_ref) {
11
+ var _context;
12
+
9
13
  let {
10
14
  children,
11
15
  compareValues = compareValuesDefault,
@@ -17,8 +21,11 @@ function ChipList(_ref) {
17
21
  disabled = false,
18
22
  mode = 'classic',
19
23
  alignItems = 'left',
24
+ pattern = 'auto',
25
+ name,
20
26
  ...props
21
27
  } = _ref;
28
+ const uniqueId = useSafeId();
22
29
  return /*#__PURE__*/React.createElement(ChipListBase, {
23
30
  compareValues: (activeValues, compareVal) => compareValues(activeValues[0], compareVal),
24
31
  onChange: onChange,
@@ -29,6 +36,8 @@ function ChipList(_ref) {
29
36
  disabled: disabled,
30
37
  mode: mode,
31
38
  alignItems: alignItems,
39
+ pattern: _includesInstanceProperty(_context = ['auto', 'radiogroup', 'navigation', 'tablist']).call(_context, pattern) ? pattern : 'auto',
40
+ name: name ?? `rs-chip-list-${uniqueId}`,
32
41
  ...props
33
42
  }, children);
34
43
  }
@@ -43,9 +52,13 @@ ChipList.propTypes = {
43
52
  className: PropTypes.string,
44
53
  mode: PropTypes.oneOf(['classic', 'rock']),
45
54
  alignItems: PropTypes.oneOf(['left', 'center', 'right']),
55
+ pattern: PropTypes.oneOf(['auto', 'radiogroup', 'navigation', 'tablist']),
46
56
  value: PropTypes.any.isRequired,
47
57
  onChange: PropTypes.func.isRequired,
48
58
  compareValues: PropTypes.func,
59
+ name: PropTypes.string,
60
+ 'aria-label': PropTypes.string,
61
+ 'aria-labelledby': PropTypes.string,
49
62
  'data-e2e': PropTypes.string,
50
63
  style: PropTypes.object
51
64
  };
package/lib/index.css CHANGED
@@ -1,19 +1,19 @@
1
- ._light_nm7yw3_8{
1
+ ._light_13vk6i6_8{
2
2
  --rs-theme-dark:0;
3
3
  }
4
- ._dark_nm7yw3_11{
4
+ ._dark_13vk6i6_11{
5
5
  --rs-theme-dark:1;
6
6
  }
7
- ._alignLeft_nm7yw3_15{
7
+ ._alignLeft_13vk6i6_15{
8
8
  --_rs-cl-align:left;
9
9
  }
10
- ._alignCenter_nm7yw3_18{
10
+ ._alignCenter_13vk6i6_18{
11
11
  --_rs-cl-align:center;
12
12
  }
13
- ._alignRight_nm7yw3_21{
13
+ ._alignRight_13vk6i6_21{
14
14
  --_rs-cl-align:right;
15
15
  }
16
- ._sizeXs_nm7yw3_25{
16
+ ._sizeXs_13vk6i6_25{
17
17
  --_rs-cl-chip-base-border-radius:4px;
18
18
  --_rs-cl-chip-compact-border-radius:var(--_rs-cl-chip-base-border-radius);
19
19
  --_rs-cl-chip-vertical-padding:2px;
@@ -27,7 +27,7 @@
27
27
  --_rs-cl-compact-horizontal-gap:4px;
28
28
  --_rs-cl-sparse-horizontal-gap:12px;
29
29
  }
30
- ._sizeXs_nm7yw3_25 [data-rs-internal='chip-list__chip']{
30
+ ._sizeXs_13vk6i6_25 [data-rs-internal='chip-list__chip']{
31
31
  --_rs-typography-letter-spacing:var(--rs-text-3-letter-spacing, 0.0045em);
32
32
  --_rs-typography-text-transform:initial;
33
33
  --_rs-typography-font-variant-numeric:initial;
@@ -62,7 +62,7 @@
62
62
  --_rs-typography-link-external-standalone-border-offset-from-text-base:1.02em;
63
63
  --_rs-typography-link-border-bottom-width-from-text:1px;
64
64
  }
65
- ._sizeS_nm7yw3_28{
65
+ ._sizeS_13vk6i6_28{
66
66
  --_rs-cl-chip-base-border-radius:16px;
67
67
  --_rs-cl-chip-compact-border-radius:4px;
68
68
  --_rs-cl-chip-vertical-padding:6px;
@@ -76,7 +76,7 @@
76
76
  --_rs-cl-compact-horizontal-gap:6px;
77
77
  --_rs-cl-sparse-horizontal-gap:12px;
78
78
  }
79
- ._sizeS_nm7yw3_28 [data-rs-internal='chip-list__chip']{
79
+ ._sizeS_13vk6i6_28 [data-rs-internal='chip-list__chip']{
80
80
  --_rs-typography-letter-spacing:var(--rs-text-3-letter-spacing, 0.0045em);
81
81
  --_rs-typography-text-transform:initial;
82
82
  --_rs-typography-font-variant-numeric:initial;
@@ -111,7 +111,7 @@
111
111
  --_rs-typography-link-external-standalone-border-offset-from-text-base:1.02em;
112
112
  --_rs-typography-link-border-bottom-width-from-text:1px;
113
113
  }
114
- ._sizeM_nm7yw3_31{
114
+ ._sizeM_13vk6i6_31{
115
115
  --_rs-cl-chip-base-border-radius:20px;
116
116
  --_rs-cl-chip-compact-border-radius:8px;
117
117
  --_rs-cl-chip-vertical-padding:8px;
@@ -125,7 +125,7 @@
125
125
  --_rs-cl-compact-horizontal-gap:6px;
126
126
  --_rs-cl-sparse-horizontal-gap:16px;
127
127
  }
128
- ._sizeM_nm7yw3_31 [data-rs-internal='chip-list__chip']{
128
+ ._sizeM_13vk6i6_31 [data-rs-internal='chip-list__chip']{
129
129
  --_rs-typography-letter-spacing:var(--rs-text-2-letter-spacing, 0.0015em);
130
130
  --_rs-typography-text-transform:initial;
131
131
  --_rs-typography-font-variant-numeric:initial;
@@ -164,11 +164,11 @@
164
164
  --_rs-typography-link-border-bottom-width-from-text:1px;
165
165
  }
166
166
  @media screen and (max-width: 640px){
167
- ._sizeM_nm7yw3_31 [data-rs-internal='chip-list__chip']{
167
+ ._sizeM_13vk6i6_31 [data-rs-internal='chip-list__chip']{
168
168
  --_rs-typography-text-2-sm-list-li-margin-top-from-text:12px;
169
169
  }
170
170
  }
171
- ._sizeL_nm7yw3_34{
171
+ ._sizeL_13vk6i6_34{
172
172
  --_rs-cl-chip-base-border-radius:26px;
173
173
  --_rs-cl-chip-compact-border-radius:12px;
174
174
  --_rs-cl-chip-vertical-padding:12px;
@@ -183,11 +183,11 @@
183
183
  --_rs-cl-sparse-horizontal-gap:16px;
184
184
  }
185
185
  @media screen and (max-width: 640px){
186
- ._sizeL_nm7yw3_34{
186
+ ._sizeL_13vk6i6_34{
187
187
  --_rs-cl-vertical-gap:12px;
188
188
  }
189
189
  }
190
- ._sizeL_nm7yw3_34 [data-rs-internal='chip-list__chip']{
190
+ ._sizeL_13vk6i6_34 [data-rs-internal='chip-list__chip']{
191
191
  --_rs-typography-letter-spacing:var(--rs-text-1-letter-spacing, normal);
192
192
  --_rs-typography-text-transform:initial;
193
193
  --_rs-typography-font-variant-numeric:initial;
@@ -226,34 +226,34 @@
226
226
  --_rs-typography-link-border-bottom-width-from-text:1px;
227
227
  }
228
228
  @media screen and (max-width: 640px){
229
- ._sizeL_nm7yw3_34 [data-rs-internal='chip-list__chip']{
229
+ ._sizeL_13vk6i6_34 [data-rs-internal='chip-list__chip']{
230
230
  --_rs-typography-text-1-sm-list-li-margin-top-from-text:16px;
231
231
  }
232
232
  }
233
- ._innerOffsetL_nm7yw3_38{
233
+ ._innerOffsetL_13vk6i6_38{
234
234
  --_rs-cl-horizontal-gap-from-sparse:var(--_rs-cl-sparse-horizontal-gap);
235
235
  }
236
236
  @media screen and (max-width: 640px){
237
- ._innerOffsetL_nm7yw3_38{
237
+ ._innerOffsetL_13vk6i6_38{
238
238
  --_rs-cl-horizontal-gap-from-sparse:var(--_rs-cl-base-horizontal-gap);
239
239
  }
240
240
  }
241
- ._innerOffsetM_nm7yw3_41{
241
+ ._innerOffsetM_13vk6i6_41{
242
242
  --_rs-cl-horizontal-gap-from-sparse:initial;
243
243
  }
244
- ._classic_nm7yw3_45{
244
+ ._classic_13vk6i6_45{
245
245
  --_rs-cl-chip-active-border-color:var(--_rs-color-primary);
246
246
  --_rs-cl-chip-selected-border-color:var(--_rs-color-primary);
247
247
  --_rs-cl-chip-hover-background:var(--_rs-color-primary-t-fog);
248
248
  --_rs-cl-chip-active-background:var(--_rs-color-primary-t-fog);
249
249
  }
250
- ._rock_nm7yw3_48{
250
+ ._rock_13vk6i6_48{
251
251
  --_rs-cl-chip-active-border-color:rgba(calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227));
252
252
  --_rs-cl-chip-selected-border-color:rgba(calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227));
253
253
  --_rs-cl-chip-hover-background:rgba(calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227), 0.1);
254
254
  --_rs-cl-chip-active-background:rgba(calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227), 0.1);
255
255
  }
256
- ._compact_nm7yw3_52{
256
+ ._compact_13vk6i6_52{
257
257
  --_rs-cl-chip-border-radius-from-compact:var(
258
258
  --_rs-cl-chip-compact-border-radius
259
259
  );
@@ -262,7 +262,7 @@
262
262
  );
263
263
  --_rs-cl-horizontal-gap-from-compact:var(--_rs-cl-compact-horizontal-gap);
264
264
  }
265
- ._main_nm7yw3_56{
265
+ ._main_13vk6i6_56{
266
266
  --_rs-theme-dark:var(
267
267
  --_rs-internal-force-theme-dark-consult-rescui-before-using,
268
268
  var(--rs-theme-dark, 0)
@@ -272,7 +272,7 @@
272
272
  --_rs-theme-light-coefficient:calc(1 - var(--_rs-theme-dark-coefficient));
273
273
  }
274
274
  @supports (color: color-mix(in srgb, #fff 50%, #000 50%)){
275
- ._main_nm7yw3_56{
275
+ ._main_13vk6i6_56{
276
276
  --_rs-color-primary:color-mix(in srgb, var(--rs-color-primary-light-theme, #6B57FF) calc(100%*var(--_rs-theme-light-coefficient, 1)), var(--rs-color-primary-dark-theme, #8473FF) calc(100%*var(--_rs-theme-dark-coefficient, 0)));
277
277
  --_rs-color-primary-dim:color-mix(in srgb, var(--rs-color-primary-dim-light-theme, #8979ff) calc(100%*var(--_rs-theme-light-coefficient, 1)), var(--rs-color-primary-dim-dark-theme, #6f61d2) calc(100%*var(--_rs-theme-dark-coefficient, 0)));
278
278
  --_rs-color-primary-fog:color-mix(in srgb, var(--rs-color-primary-fog-light-theme, #e1ddff) calc(100%*var(--_rs-theme-light-coefficient, 1)), var(--rs-color-primary-fog-dark-theme, #2e2b49) calc(100%*var(--_rs-theme-dark-coefficient, 0)));
@@ -281,7 +281,7 @@
281
281
  }
282
282
  }
283
283
  @supports not (color: color-mix(in srgb, #fff 50%, #000 50%)){
284
- ._main_nm7yw3_56{
284
+ ._main_13vk6i6_56{
285
285
  --_rs-color-primary:rgb(calc(var(--_rs-theme-light-coefficient, 1)*107 + var(--_rs-theme-dark-coefficient, 0)*132), calc(var(--_rs-theme-light-coefficient, 1)*87 + var(--_rs-theme-dark-coefficient, 0)*115), 255);
286
286
  --_rs-color-primary-dim:rgb(calc(var(--_rs-theme-light-coefficient, 1)*137 + var(--_rs-theme-dark-coefficient, 0)*111), calc(var(--_rs-theme-light-coefficient, 1)*121 + var(--_rs-theme-dark-coefficient, 0)*97), calc(var(--_rs-theme-light-coefficient, 1)*255 + var(--_rs-theme-dark-coefficient, 0)*210));
287
287
  --_rs-color-primary-fog:rgb(calc(var(--_rs-theme-light-coefficient, 1)*225 + var(--_rs-theme-dark-coefficient, 0)*46), calc(var(--_rs-theme-light-coefficient, 1)*221 + var(--_rs-theme-dark-coefficient, 0)*43), calc(var(--_rs-theme-light-coefficient, 1)*255 + var(--_rs-theme-dark-coefficient, 0)*73));
@@ -289,7 +289,7 @@
289
289
  --_rs-color-primary-t-fog:rgba(calc(var(--_rs-theme-light-coefficient, 1)*107 + var(--_rs-theme-dark-coefficient, 0)*132), calc(var(--_rs-theme-light-coefficient, 1)*87 + var(--_rs-theme-dark-coefficient, 0)*115), 255, calc(var(--_rs-theme-light-coefficient, 1)*0.2 + var(--_rs-theme-dark-coefficient, 0)*0.3));
290
290
  }
291
291
  }
292
- ._main_nm7yw3_56 {
292
+ ._main_13vk6i6_56 {
293
293
  --_rs-cl-chip-border-radius:var(
294
294
  --_rs-cl-chip-border-radius-from-compact,
295
295
  var(--_rs-cl-chip-base-border-radius)
@@ -303,7 +303,7 @@
303
303
  var(--_rs-cl-horizontal-gap-from-sparse, var(--_rs-cl-base-horizontal-gap))
304
304
  );
305
305
  }
306
- ._chipsContainer_nm7yw3_62{
306
+ ._chipsContainer_13vk6i6_62{
307
307
  position:relative;
308
308
 
309
309
  display:flex;
@@ -312,17 +312,17 @@
312
312
 
313
313
  gap:var(--_rs-cl-vertical-gap) var(--_rs-cl-horizontal-gap);
314
314
  }
315
- ._alignIconLeft_nm7yw3_72{
315
+ ._alignIconLeft_13vk6i6_72{
316
316
  --_rs-cl-chip-icon-flex-direction:row;
317
317
  --_rs-cl-chip-icon-position-multiplier-left:1;
318
318
  --_rs-cl-chip-icon-position-multiplier-right:0;
319
319
  }
320
- ._alignIconRight_nm7yw3_75{
320
+ ._alignIconRight_13vk6i6_75{
321
321
  --_rs-cl-chip-icon-flex-direction:row-reverse;
322
322
  --_rs-cl-chip-icon-position-multiplier-left:0;
323
323
  --_rs-cl-chip-icon-position-multiplier-right:1;
324
324
  }
325
- ._chip_nm7yw3_62{
325
+ ._chip_13vk6i6_62{
326
326
  letter-spacing:var(
327
327
  --rs-text-base-letter-spacing,
328
328
  var(--_rs-typography-letter-spacing)
@@ -369,27 +369,30 @@
369
369
  background:transparent;
370
370
  box-shadow:none;
371
371
 
372
- transition:color 100ms, background-color 100ms;
372
+ transition:color 100ms var(--rs-transition-function, ease), background-color 100ms var(--rs-transition-function, ease);
373
373
  -webkit-font-smoothing:inherit;
374
374
  -moz-osx-font-smoothing:inherit;
375
375
  -webkit-appearance:none;
376
376
  }
377
- ._chip_nm7yw3_62::-moz-focus-inner{
377
+ ._chip_13vk6i6_62::-moz-focus-inner{
378
378
  padding:0;
379
379
 
380
380
  border:none;
381
381
  }
382
- ._chip_nm7yw3_62:hover,
383
- ._chip_nm7yw3_62:focus,
384
- ._chip_nm7yw3_62:active{
382
+ ._chip_13vk6i6_62:hover,
383
+ ._chip_13vk6i6_62:focus,
384
+ ._chip_13vk6i6_62:active{
385
385
  outline:none;
386
386
 
387
387
  text-decoration:none;
388
388
  }
389
- ._chip_nm7yw3_62:focus[data-focus-method='key']{
389
+ ._chip_13vk6i6_62:focus[data-focus-method='key']{
390
390
  box-shadow:var(--_rs-color-primary-t-dim) 0 0 0 4px;
391
391
  }
392
- ._chip_nm7yw3_62:focus{
392
+ ._chip_13vk6i6_62:has(:focus[data-focus-method='key']){
393
+ box-shadow:var(--_rs-color-primary-t-dim) 0 0 0 4px;
394
+ }
395
+ ._chip_13vk6i6_62:focus{
393
396
  --_rs-cl-chip-border-color:rgba(
394
397
  calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227),
395
398
  calc(0.3 + var(--_rs-theme-dark-coefficient)*0.1)
@@ -398,7 +401,7 @@
398
401
  color:rgba(calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227));
399
402
  }
400
403
  @media (hover: hover){
401
- ._chip_nm7yw3_62:hover{
404
+ ._chip_13vk6i6_62:hover{
402
405
  --_rs-cl-chip-border-color:rgba(
403
406
  calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227),
404
407
  calc(0.3 + var(--_rs-theme-dark-coefficient)*0.1)
@@ -409,20 +412,20 @@
409
412
  background:var(--_rs-cl-chip-hover-background);
410
413
  }
411
414
  }
412
- ._chip_nm7yw3_62:active{
415
+ ._chip_13vk6i6_62:active{
413
416
  --_rs-cl-chip-border-color:var(--_rs-cl-chip-active-border-color);
414
417
 
415
418
  color:rgba(calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227));
416
419
 
417
420
  background:var(--_rs-cl-chip-active-background);
418
421
  }
419
- ._chip_nm7yw3_62._selected_nm7yw3_172{
422
+ ._chip_13vk6i6_62._selected_13vk6i6_176{
420
423
  --_rs-cl-chip-border-color:var(--_rs-cl-chip-selected-border-color);
421
424
  --_rs-cl-chip-border-width:2px;
422
425
 
423
426
  color:rgba(calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227));
424
427
  }
425
- ._disabled_nm7yw3_179 ._chip_nm7yw3_62{
428
+ ._disabled_13vk6i6_183 ._chip_13vk6i6_62{
426
429
  --_rs-cl-chip-border-color:rgba(calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227), 0.2);
427
430
 
428
431
  pointer-events:none;
@@ -431,10 +434,10 @@
431
434
 
432
435
  background:transparent;
433
436
  }
434
- ._disabled_nm7yw3_179 ._chip_nm7yw3_62._selected_nm7yw3_172{
437
+ ._disabled_13vk6i6_183 ._chip_13vk6i6_62._selected_13vk6i6_176{
435
438
  --_rs-cl-chip-border-color:rgba(calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227), 0.4);
436
439
  }
437
- ._chip_nm7yw3_62::before{
440
+ ._chip_13vk6i6_62::before{
438
441
  position:absolute;
439
442
  top:0;
440
443
  right:0;
@@ -449,9 +452,9 @@
449
452
  content:'';
450
453
  pointer-events:none;
451
454
 
452
- transition:border-color 100ms;
455
+ transition:border-color 100ms var(--rs-transition-function, ease);
453
456
  }
454
- ._chip_nm7yw3_62 ._icon_nm7yw3_212{
457
+ ._chip_13vk6i6_62 ._icon_13vk6i6_216{
455
458
  flex:0 0 auto;
456
459
 
457
460
  width:auto;
@@ -460,7 +463,23 @@
460
463
  margin:0;
461
464
  fill:currentColor;
462
465
  }
463
- ._withoutText_nm7yw3_223{
466
+ ._hiddenInput_13vk6i6_227{
467
+ position:absolute;
468
+
469
+ overflow:hidden;
470
+ clip:rect(0 0 0 0);
471
+
472
+ width:1px;
473
+ height:1px;
474
+ margin:-1px;
475
+ padding:0;
476
+
477
+ border:none;
478
+
479
+ white-space:nowrap;
480
+ clip-path:inset(50%);
481
+ }
482
+ ._withoutText_13vk6i6_244{
464
483
  --_rs-cl-chip-horizontal-real-padding:var(--_rs-cl-chip-icon-only-padding);
465
484
  --_rs-cl-chip-vertical-real-padding:var(--_rs-cl-chip-icon-only-padding);
466
485
  }
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Theme } from '@rescui/ui-contexts';
3
- import { ChipListSizeType, ChipListModeType, ChipListAlignItemsType } from './types';
3
+ import { ChipListSizeType, ChipListModeType, ChipListAlignItemsType, ChipListBasePatternType } from './types';
4
4
  export declare type ChipListBaseProps<V> = {
5
5
  className?: string;
6
6
  size?: ChipListSizeType;
@@ -12,13 +12,20 @@ export declare type ChipListBaseProps<V> = {
12
12
  theme?: Theme;
13
13
  mode?: ChipListModeType;
14
14
  alignItems?: ChipListAlignItemsType;
15
+ pattern?: ChipListBasePatternType;
15
16
  value: V[];
16
17
  onChange: (value: V) => void;
17
18
  children: React.ReactNode;
18
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;
19
26
  /** Identifier for e2e tests */
20
27
  'data-e2e'?: string;
21
28
  /** CSS style object */
22
29
  style?: React.CSSProperties;
23
30
  };
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;
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>): 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
@@ -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_nm7yw3_8",
3
- "dark": "_dark_nm7yw3_11",
4
- "alignLeft": "_alignLeft_nm7yw3_15",
5
- "alignCenter": "_alignCenter_nm7yw3_18",
6
- "alignRight": "_alignRight_nm7yw3_21",
7
- "sizeXs": "_sizeXs_nm7yw3_25",
8
- "sizeS": "_sizeS_nm7yw3_28",
9
- "sizeM": "_sizeM_nm7yw3_31",
10
- "sizeL": "_sizeL_nm7yw3_34",
11
- "innerOffsetL": "_innerOffsetL_nm7yw3_38",
12
- "innerOffsetM": "_innerOffsetM_nm7yw3_41",
13
- "classic": "_classic_nm7yw3_45",
14
- "rock": "_rock_nm7yw3_48",
15
- "compact": "_compact_nm7yw3_52",
16
- "main": "_main_nm7yw3_56",
17
- "chipsContainer": "_chipsContainer_nm7yw3_62",
18
- "alignIconLeft": "_alignIconLeft_nm7yw3_72",
19
- "alignIconRight": "_alignIconRight_nm7yw3_75",
20
- "chip": "_chip_nm7yw3_62",
21
- "selected": "_selected_nm7yw3_172",
22
- "disabled": "_disabled_nm7yw3_179",
23
- "icon": "_icon_nm7yw3_212",
24
- "withoutText": "_withoutText_nm7yw3_223"
2
+ "light": "_light_13vk6i6_8",
3
+ "dark": "_dark_13vk6i6_11",
4
+ "alignLeft": "_alignLeft_13vk6i6_15",
5
+ "alignCenter": "_alignCenter_13vk6i6_18",
6
+ "alignRight": "_alignRight_13vk6i6_21",
7
+ "sizeXs": "_sizeXs_13vk6i6_25",
8
+ "sizeS": "_sizeS_13vk6i6_28",
9
+ "sizeM": "_sizeM_13vk6i6_31",
10
+ "sizeL": "_sizeL_13vk6i6_34",
11
+ "innerOffsetL": "_innerOffsetL_13vk6i6_38",
12
+ "innerOffsetM": "_innerOffsetM_13vk6i6_41",
13
+ "classic": "_classic_13vk6i6_45",
14
+ "rock": "_rock_13vk6i6_48",
15
+ "compact": "_compact_13vk6i6_52",
16
+ "main": "_main_13vk6i6_56",
17
+ "chipsContainer": "_chipsContainer_13vk6i6_62",
18
+ "alignIconLeft": "_alignIconLeft_13vk6i6_72",
19
+ "alignIconRight": "_alignIconRight_13vk6i6_75",
20
+ "chip": "_chip_13vk6i6_62",
21
+ "selected": "_selected_13vk6i6_176",
22
+ "disabled": "_disabled_13vk6i6_183",
23
+ "icon": "_icon_13vk6i6_216",
24
+ "hiddenInput": "_hiddenInput_13vk6i6_227",
25
+ "withoutText": "_withoutText_13vk6i6_244"
25
26
  };
26
27
  export { styles as default };
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { ChipIconPosition } from './types';
3
- export declare type ChipExplicitProps = {
3
+ declare type InheritType<Main, Parent> = Main & Omit<Parent, keyof Main>;
4
+ export declare type ChipOwnProps = {
4
5
  className?: string;
5
6
  /**
6
7
  * Chip value
@@ -27,8 +28,9 @@ export declare type ChipExplicitProps = {
27
28
  icon?: never;
28
29
  iconPosition?: never;
29
30
  });
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>>;
31
+ declare type ChipLabelProps = InheritType<ChipOwnProps, React.LabelHTMLAttributes<HTMLLabelElement>>;
32
+ declare type ChipButtonProps = InheritType<ChipOwnProps, React.ButtonHTMLAttributes<HTMLButtonElement>>;
33
+ declare type ChipAnchorProps = InheritType<ChipOwnProps, React.AnchorHTMLAttributes<HTMLAnchorElement>>;
34
+ export declare type ChipProps = ChipLabelProps | ChipButtonProps | ChipAnchorProps;
35
+ declare const Chip: React.ForwardRefExoticComponent<ChipProps & React.RefAttributes<HTMLLabelElement | HTMLButtonElement | HTMLAnchorElement>>;
34
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,9 +1,19 @@
1
+ import type React from 'react';
1
2
  export declare type ChipListSizeType = 'l' | 'm' | 's' | 'xs';
2
3
  export declare type ChipListModeType = 'classic' | 'rock';
3
4
  export declare type ChipListAlignItemsType = 'left' | 'center' | 'right';
5
+ export declare type ChipListResolvedPatternType = 'radiogroup' | 'group' | 'navigation' | 'tablist';
6
+ export declare type ChipListPatternType = 'radiogroup' | 'navigation' | 'tablist' | 'auto';
7
+ export declare type ChipListBasePatternType = ChipListPatternType | 'group';
4
8
  export declare type ChipIconPosition = 'left' | 'right';
5
9
  export declare type ChipListContextType<V> = {
6
10
  onChange: (value: V) => void;
7
11
  selectedIds?: number[];
8
12
  disabled?: boolean;
13
+ pattern?: ChipListResolvedPatternType;
14
+ name?: string;
15
+ rovingTabIndex?: {
16
+ currentTabStopId: number;
17
+ onTabKeyDown: (e: React.KeyboardEvent<HTMLElement>, itemId: number) => void;
18
+ };
9
19
  };
@@ -7,4 +7,8 @@ export declare type UseItemsIdsOpts<V> = {
7
7
  export declare const useItemsIds: <V>({ children, compareValues, value }: UseItemsIdsOpts<V>) => {
8
8
  childrenWithValues: (string | number | React.ReactElement<any, string | React.JSXElementConstructor<any>> | React.ReactFragment | React.ReactPortal)[];
9
9
  selectedIds: number[];
10
+ hasHref: boolean;
11
+ hasControls: boolean;
12
+ allItemsHaveControls: boolean;
13
+ tabRefs: React.MutableRefObject<Map<number, HTMLElement>>;
10
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,31 +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
 
17
- const hasElValue = el => {
18
- var _el$props, _el$props2;
19
-
20
- return ((_el$props = el.props) === null || _el$props === void 0 ? void 0 : _el$props.value) !== null && typeof ((_el$props2 = el.props) === null || _el$props2 === void 0 ? void 0 : _el$props2.value) !== 'undefined';
19
+ const setRef = (ref, value) => {
20
+ if (typeof ref === 'function') {
21
+ ref(value);
22
+ } else if (ref) {
23
+ ref.current = value;
24
+ }
21
25
  };
22
26
 
27
+ const getElementRef = child => child.ref;
28
+
29
+ const hasElValue = el => el.props?.value !== null && typeof el.props?.value !== 'undefined';
30
+
31
+ const hasElHref = el => typeof el.props?.href !== 'undefined';
32
+
33
+ const hasElAriaControls = el => typeof el.props?.['aria-controls'] !== 'undefined';
34
+
23
35
  const useItemsIds = _ref2 => {
24
36
  let {
25
37
  children,
26
38
  compareValues,
27
39
  value
28
40
  } = _ref2;
29
- 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(() => {
30
58
  let counter = 0;
31
59
  const currentSelectedItemIds = [];
60
+ let currentHasHref = false;
61
+ let currentHasControls = false;
62
+ let currentAllItemsHaveControls = true;
32
63
  const items = deepMap(children, child => {
33
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
+
34
75
  const childValue = hasElValue(child) ? child.props.value : counter;
35
76
  const isSelectedItem = compareValues(value, childValue);
36
77
 
@@ -41,7 +82,8 @@ const useItemsIds = _ref2 => {
41
82
  const patchedItem = patchItemIfNeeded({
42
83
  child,
43
84
  itemId: counter,
44
- childValue
85
+ childValue,
86
+ ref: createChipRef(counter, getElementRef(child))
45
87
  });
46
88
  counter += 1;
47
89
  return patchedItem;
@@ -49,11 +91,21 @@ const useItemsIds = _ref2 => {
49
91
  return child;
50
92
  }
51
93
  });
52
- return [React.Children.toArray(items), currentSelectedItemIds];
53
- }, [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]);
54
102
  return {
55
103
  childrenWithValues,
56
- selectedIds
104
+ selectedIds,
105
+ hasHref,
106
+ hasControls,
107
+ allItemsHaveControls,
108
+ tabRefs
57
109
  };
58
110
  };
59
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
+ };
@@ -0,0 +1,73 @@
1
+ import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
2
+ import React from 'react'; // eslint-disable-next-line complexity
3
+
4
+ const getNextItemId = _ref => {
5
+ let {
6
+ currentItemId,
7
+ itemsCount,
8
+ key
9
+ } = _ref;
10
+
11
+ if (itemsCount <= 0) {
12
+ return currentItemId;
13
+ }
14
+
15
+ if (key === 'Home') {
16
+ return 0;
17
+ }
18
+
19
+ if (key === 'End') {
20
+ return itemsCount - 1;
21
+ }
22
+
23
+ if (key === 'ArrowLeft') {
24
+ return currentItemId <= 0 ? itemsCount - 1 : currentItemId - 1;
25
+ }
26
+
27
+ if (key === 'ArrowRight') {
28
+ return currentItemId >= itemsCount - 1 ? 0 : currentItemId + 1;
29
+ }
30
+
31
+ return currentItemId;
32
+ };
33
+
34
+ const useRovingTabIndex = _ref2 => {
35
+ let {
36
+ enabled,
37
+ selectedId,
38
+ tabRefs
39
+ } = _ref2;
40
+ const [currentTabStopId, setCurrentTabStopId] = React.useState(selectedId ?? 0);
41
+ React.useEffect(() => {
42
+ if (typeof selectedId !== 'undefined') {
43
+ setCurrentTabStopId(selectedId);
44
+ }
45
+ }, [selectedId]);
46
+ const onTabKeyDown = React.useCallback((e, itemId) => {
47
+ var _context;
48
+
49
+ if (!enabled) {
50
+ return;
51
+ }
52
+
53
+ if (!_includesInstanceProperty(_context = ['ArrowLeft', 'ArrowRight', 'Home', 'End']).call(_context, e.key)) {
54
+ return;
55
+ }
56
+
57
+ const itemsCount = tabRefs?.current.size ?? 0;
58
+ const nextItemId = getNextItemId({
59
+ currentItemId: itemId,
60
+ itemsCount,
61
+ key: e.key
62
+ });
63
+ e.preventDefault();
64
+ setCurrentTabStopId(nextItemId);
65
+ tabRefs?.current.get(nextItemId)?.focus();
66
+ }, [enabled, tabRefs]);
67
+ return {
68
+ currentTabStopId,
69
+ onTabKeyDown
70
+ };
71
+ };
72
+
73
+ export { useRovingTabIndex };
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const useSafeId: typeof React.useId;
@@ -0,0 +1,15 @@
1
+ import React, { useRef } from 'react';
2
+
3
+ const useFallbackId = () => {
4
+ const id = useRef(null);
5
+
6
+ if (id.current === null) {
7
+ id.current = useFallbackId.counter++;
8
+ }
9
+
10
+ return id.current.toString();
11
+ };
12
+
13
+ useFallbackId.counter = 0;
14
+ const useSafeId = React.useId ?? useFallbackId;
15
+ export { useSafeId };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rescui/chip-list",
3
- "version": "0.13.5",
3
+ "version": "0.14.0",
4
4
  "description": "",
5
5
  "license": "Apache-2.0",
6
6
  "author": "JetBrains",
@@ -26,7 +26,7 @@
26
26
  "devDependencies": {
27
27
  "@rescui/colors": "^0.3.0",
28
28
  "@rescui/scripts": "^0.5.1",
29
- "@rescui/typography": "^0.25.0",
29
+ "@rescui/typography": "^0.27.0",
30
30
  "@rescui/visual-regression": "^0.1.4"
31
31
  },
32
32
  "scripts": {
@@ -34,5 +34,5 @@
34
34
  "build-pcss-api": "rescui-scripts build-pcss-api --file public-api.p.css"
35
35
  },
36
36
  "nx": {},
37
- "gitHead": "59db2e6ea5eabdcabc035779df2611d0df5a0cbe"
37
+ "gitHead": "e9aedd97a4cc372540ec8d75dda4f122baacca10"
38
38
  }