@react-aria/tag 3.0.0-beta.0 → 3.0.0-beta.2

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/dist/types.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { GridCollection } from "@react-types/grid";
2
- import { GridKeyboardDelegate } from "@react-aria/grid";
3
- import { Key, ButtonHTMLAttributes, ReactNode } from "react";
4
- import { DOMAttributes, DOMProps } from "@react-types/shared";
5
- import { GridState } from "@react-stately/grid";
6
- import { TagProps } from "@react-types/tag";
7
- export class TagKeyboardDelegate<T> extends GridKeyboardDelegate<T, GridCollection<T>> {
1
+ import { Collection, Direction, KeyboardDelegate, DOMAttributes, FocusableElement, AriaLabelingProps, DOMProps, Validation } from "@react-types/shared";
2
+ import { Key, RefObject } from "react";
3
+ import { AriaButtonProps } from "@react-types/button";
4
+ import { TagGroupState } from "@react-stately/tag";
5
+ import { TagProps, TagGroupProps } from "@react-types/tag";
6
+ export class TagKeyboardDelegate<T> implements KeyboardDelegate {
7
+ constructor(collection: Collection<T>, direction: Direction);
8
8
  getFirstKey(): Key;
9
9
  getLastKey(): Key;
10
10
  getKeyRightOf(key: Key): any;
@@ -15,21 +15,47 @@ export class TagKeyboardDelegate<T> extends GridKeyboardDelegate<T, GridCollecti
15
15
  getKeyPageBelow(key: any): any;
16
16
  }
17
17
  export interface TagAria {
18
+ /** Props for the tag visible label (if any). */
18
19
  labelProps: DOMAttributes;
20
+ /** Props for the tag cell element. */
19
21
  tagProps: DOMAttributes;
22
+ /** Props for the tag row element. */
20
23
  tagRowProps: DOMAttributes;
21
- clearButtonProps: ButtonHTMLAttributes<HTMLButtonElement>;
22
- }
23
- export function useTag(props: TagProps<any>, state: GridState<any, any>): TagAria;
24
- export interface AriaTagGroupProps extends DOMProps {
25
- children: ReactNode;
26
- isReadOnly?: boolean;
27
- validationState?: 'valid' | 'invalid';
24
+ /** Props for the tag clear button. */
25
+ clearButtonProps: AriaButtonProps;
28
26
  }
27
+ /**
28
+ * Provides the behavior and accessibility implementation for a tag component.
29
+ * @param props - Props to be applied to the tag.
30
+ * @param state - State for the tag group, as returned by `useTagGroupState`.
31
+ * @param ref - A ref to a DOM element for the tag.
32
+ */
33
+ export function useTag<T>(props: TagProps<T>, state: TagGroupState<T>, ref: RefObject<FocusableElement>): TagAria;
29
34
  export interface TagGroupAria {
35
+ /** Props for the tag grouping element. */
30
36
  tagGroupProps: DOMAttributes;
37
+ /** Props for the tag group's visible label (if any). */
38
+ labelProps: DOMAttributes;
39
+ /** Props for the tag group description element, if any. */
40
+ descriptionProps: DOMAttributes;
41
+ /** Props for the tag group error message element, if any. */
42
+ errorMessageProps: DOMAttributes;
43
+ }
44
+ export interface AriaTagGroupProps<T> extends TagGroupProps<T>, DOMProps, AriaLabelingProps, Validation {
45
+ /**
46
+ * An optional keyboard delegate to handle arrow key navigation,
47
+ * to override the default.
48
+ */
49
+ keyboardDelegate?: TagKeyboardDelegate<T>;
31
50
  }
32
- export function useTagGroup(props: AriaTagGroupProps): TagGroupAria;
51
+ /**
52
+ * Provides the behavior and accessibility implementation for a tag group component.
53
+ * Tags allow users to categorize content. They can represent keywords or people, and are grouped to describe an item or a search request.
54
+ * @param props - Props to be applied to the tag group.
55
+ * @param state - State for the tag group, as returned by `useTagGroupState`.
56
+ * @param ref - A ref to a DOM element for the tag group.
57
+ */
58
+ export function useTagGroup<T>(props: AriaTagGroupProps<T>, state: TagGroupState<T>, ref: RefObject<HTMLElement>): TagGroupAria;
33
59
  export type { TagProps } from '@react-types/tag';
34
60
 
35
61
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"mappings":";;;;;;AAgBA,iCAAiC,CAAC,CAAE,SAAQ,qBAAqB,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC;IACpF,WAAW;IAOX,UAAU;IAOV,aAAa,CAAC,GAAG,EAAE,GAAG;IAItB,YAAY,CAAC,GAAG,EAAE,GAAG;IAIrB,WAAW,CAAC,GAAG,KAAA;IA8Bf,WAAW,CAAC,GAAG,KAAA;IA6Bf,eAAe,CAAC,GAAG,KAAA;IAInB,eAAe,CAAC,GAAG,KAAA;CAGpB;AClFD;IACE,UAAU,EAAE,aAAa,CAAC;IAC1B,QAAQ,EAAE,aAAa,CAAC;IACxB,WAAW,EAAE,aAAa,CAAC;IAC3B,gBAAgB,EAAE,qBAAqB,iBAAiB,CAAC,CAAA;CAC1D;AAED,uBAAuB,KAAK,EAAE,SAAS,GAAG,CAAC,EAAE,KAAK,EAAE,UAAU,GAAG,EAAE,GAAG,CAAC,GAAG,OAAO,CAuDhF;ACpED,kCAAmC,SAAQ,QAAQ;IACjD,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CACtC;AAED;IACE,aAAa,EAAE,aAAa,CAAA;CAC7B;AAED,4BAA4B,KAAK,EAAE,iBAAiB,GAAG,YAAY,CAclE;ACzBD,YAAY,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAC","sources":["packages/@react-aria/tag/src/packages/@react-aria/tag/src/TagKeyboardDelegate.ts","packages/@react-aria/tag/src/packages/@react-aria/tag/src/useTag.ts","packages/@react-aria/tag/src/packages/@react-aria/tag/src/useTagGroup.ts","packages/@react-aria/tag/src/packages/@react-aria/tag/src/index.ts","packages/@react-aria/tag/src/index.ts"],"sourcesContent":[null,null,null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport {TagKeyboardDelegate} from './TagKeyboardDelegate';\nexport {useTag} from './useTag';\nexport {useTagGroup} from './useTagGroup';\n\nexport type {TagProps} from '@react-types/tag';\nexport type {AriaTagGroupProps, TagGroupAria} from './useTagGroup';\nexport type {TagAria} from './useTag';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
1
+ {"mappings":";;;;;AAeA,iCAAiC,CAAC,CAAE,YAAW,gBAAgB;gBAIjD,UAAU,EAAE,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS;IAK3D,WAAW;IAIX,UAAU;IAIV,aAAa,CAAC,GAAG,EAAE,GAAG;IAItB,YAAY,CAAC,GAAG,EAAE,GAAG;IAIrB,WAAW,CAAC,GAAG,KAAA;IAgBf,WAAW,CAAC,GAAG,KAAA;IAef,eAAe,CAAC,GAAG,KAAA;IAInB,eAAe,CAAC,GAAG,KAAA;CAGpB;ACtDD;IACE,gDAAgD;IAChD,UAAU,EAAE,aAAa,CAAC;IAC1B,sCAAsC;IACtC,QAAQ,EAAE,aAAa,CAAC;IACxB,qCAAqC;IACrC,WAAW,EAAE,aAAa,CAAC;IAC3B,sCAAsC;IACtC,gBAAgB,EAAE,eAAe,CAAA;CAClC;AAED;;;;;GAKG;AACH,uBAAuB,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,gBAAgB,CAAC,GAAG,OAAO,CAiDhH;ACnED;IACE,0CAA0C;IAC1C,aAAa,EAAE,aAAa,CAAC;IAC7B,wDAAwD;IACxD,UAAU,EAAE,aAAa,CAAC;IAC1B,2DAA2D;IAC3D,gBAAgB,EAAE,aAAa,CAAC;IAChC,6DAA6D;IAC7D,iBAAiB,EAAE,aAAa,CAAA;CACjC;AAED,mCAAmC,CAAC,CAAE,SAAQ,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,UAAU;IACrG;;;OAGG;IACH,gBAAgB,CAAC,EAAE,oBAAoB,CAAC,CAAC,CAAA;CAC1C;AAED;;;;;;GAMG;AACH,4BAA4B,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,WAAW,CAAC,GAAG,YAAY,CA2B9H;AC5DD,YAAY,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAC","sources":["packages/@react-aria/tag/src/packages/@react-aria/tag/src/TagKeyboardDelegate.ts","packages/@react-aria/tag/src/packages/@react-aria/tag/src/useTag.ts","packages/@react-aria/tag/src/packages/@react-aria/tag/src/useTagGroup.ts","packages/@react-aria/tag/src/packages/@react-aria/tag/src/index.ts","packages/@react-aria/tag/src/index.ts"],"sourcesContent":[null,null,null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport {TagKeyboardDelegate} from './TagKeyboardDelegate';\nexport {useTag} from './useTag';\nexport {useTagGroup} from './useTagGroup';\n\nexport type {TagProps} from '@react-types/tag';\nexport type {TagGroupAria, AriaTagGroupProps} from './useTagGroup';\nexport type {TagAria} from './useTag';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
package/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "@react-aria/tag",
3
- "version": "3.0.0-beta.0",
3
+ "version": "3.0.0-beta.2",
4
4
  "description": "Spectrum UI components in React",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
7
7
  "module": "dist/module.js",
8
+ "exports": {
9
+ "types": "./dist/types.d.ts",
10
+ "import": "./dist/import.mjs",
11
+ "require": "./dist/main.js"
12
+ },
8
13
  "types": "dist/types.d.ts",
9
14
  "source": "src/index.ts",
10
15
  "files": [
@@ -17,15 +22,17 @@
17
22
  "url": "https://github.com/adobe/react-spectrum"
18
23
  },
19
24
  "dependencies": {
20
- "@babel/runtime": "^7.6.2",
21
- "@react-aria/grid": "^3.5.1",
22
- "@react-aria/i18n": "^3.6.2",
23
- "@react-aria/interactions": "^3.13.0",
24
- "@react-aria/utils": "^3.14.1",
25
- "@react-stately/grid": "^3.4.1",
26
- "@react-types/grid": "^3.1.5",
27
- "@react-types/shared": "^3.16.0",
28
- "@react-types/tag": "3.0.0-beta.0"
25
+ "@react-aria/gridlist": "^3.2.0",
26
+ "@react-aria/i18n": "^3.7.0",
27
+ "@react-aria/interactions": "^3.14.0",
28
+ "@react-aria/label": "^3.5.0",
29
+ "@react-aria/utils": "^3.15.0",
30
+ "@react-stately/list": "^3.7.0",
31
+ "@react-stately/tag": "3.0.0-beta.0",
32
+ "@react-types/button": "^3.7.1",
33
+ "@react-types/shared": "^3.17.0",
34
+ "@react-types/tag": "3.0.0-beta.2",
35
+ "@swc/helpers": "^0.4.14"
29
36
  },
30
37
  "peerDependencies": {
31
38
  "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
@@ -33,5 +40,5 @@
33
40
  "publishConfig": {
34
41
  "access": "public"
35
42
  },
36
- "gitHead": "2954307ddbefe149241685440c81f80ece6b2c83"
43
+ "gitHead": "a0efee84aa178cb1a202951dfd6d8de02b292307"
37
44
  }
@@ -10,25 +10,26 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {GridCollection} from '@react-types/grid';
14
- import {GridKeyboardDelegate} from '@react-aria/grid';
13
+ import {Collection, Direction, KeyboardDelegate} from '@react-types/shared';
15
14
  import {Key} from 'react';
16
15
 
17
- export class TagKeyboardDelegate<T> extends GridKeyboardDelegate<T, GridCollection<T>> {
18
- getFirstKey() {
19
- let key = this.collection.getFirstKey();
20
- let item = this.collection.getItem(key);
16
+ export class TagKeyboardDelegate<T> implements KeyboardDelegate {
17
+ private collection: Collection<T>;
18
+ private direction: Direction;
21
19
 
22
- return [...item.childNodes][0].key;
20
+ constructor(collection: Collection<T>, direction: Direction) {
21
+ this.collection = collection;
22
+ this.direction = direction;
23
23
  }
24
24
 
25
- getLastKey() {
26
- let key = this.collection.getLastKey();
27
- let item = this.collection.getItem(key);
28
-
29
- return [...item.childNodes][0].key;
25
+ getFirstKey() {
26
+ return this.collection.getFirstKey();
30
27
  }
31
28
 
29
+ getLastKey() {
30
+ return this.collection.getLastKey();
31
+ }
32
+
32
33
  getKeyRightOf(key: Key) {
33
34
  return this.direction === 'rtl' ? this.getKeyAbove(key) : this.getKeyBelow(key);
34
35
  }
@@ -43,27 +44,13 @@ export class TagKeyboardDelegate<T> extends GridKeyboardDelegate<T, GridCollecti
43
44
  return;
44
45
  }
45
46
 
46
- // If focus was on a cell, start searching from the parent row
47
- if (this.isCell(startItem)) {
48
- key = startItem.parentKey;
49
- }
50
-
51
47
  // Find the next item
52
- key = this.findNextKey(key);
53
- if (key != null) {
54
- // If focus was on a cell, focus the cell with the same index in the next row.
55
- if (this.isCell(startItem)) {
56
- let item = this.collection.getItem(key);
48
+ key = this.collection.getKeyAfter(key);
57
49
 
58
- return [...item.childNodes][startItem.index].key;
59
- }
60
-
61
- // Otherwise, focus the next row
62
- if (this.focusMode === 'row') {
63
- return key;
64
- }
50
+ if (key != null) {
51
+ return key;
65
52
  } else {
66
- return this.getFirstKey();
53
+ return this.collection.getFirstKey();
67
54
  }
68
55
  }
69
56
 
@@ -73,26 +60,12 @@ export class TagKeyboardDelegate<T> extends GridKeyboardDelegate<T, GridCollecti
73
60
  return;
74
61
  }
75
62
 
76
- // If focus is on a cell, start searching from the parent row
77
- if (this.isCell(startItem)) {
78
- key = startItem.parentKey;
79
- }
80
-
81
63
  // Find the previous item
82
- key = this.findPreviousKey(key);
64
+ key = this.collection.getKeyBefore(key);
83
65
  if (key != null) {
84
- // If focus was on a cell, focus the cell with the same index in the previous row.
85
- if (this.isCell(startItem)) {
86
- let item = this.collection.getItem(key);
87
- return [...item.childNodes][startItem.index].key;
88
- }
89
-
90
- // Otherwise, focus the previous row
91
- if (this.focusMode === 'row') {
92
- return key;
93
- }
66
+ return key;
94
67
  } else {
95
- return this.getLastKey();
68
+ return this.collection.getLastKey();
96
69
  }
97
70
  }
98
71
 
package/src/index.ts CHANGED
@@ -15,5 +15,5 @@ export {useTag} from './useTag';
15
15
  export {useTagGroup} from './useTagGroup';
16
16
 
17
17
  export type {TagProps} from '@react-types/tag';
18
- export type {AriaTagGroupProps, TagGroupAria} from './useTagGroup';
18
+ export type {TagGroupAria, AriaTagGroupProps} from './useTagGroup';
19
19
  export type {TagAria} from './useTag';
package/src/useTag.ts CHANGED
@@ -10,77 +10,82 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {ButtonHTMLAttributes, KeyboardEvent} from 'react';
14
- import {DOMAttributes} from '@react-types/shared';
15
- import {filterDOMProps, mergeProps, useId} from '@react-aria/utils';
16
- import {GridState} from '@react-stately/grid';
13
+ import {AriaButtonProps} from '@react-types/button';
14
+ import {chain, filterDOMProps, mergeProps, useId} from '@react-aria/utils';
15
+ import {DOMAttributes, FocusableElement} from '@react-types/shared';
17
16
  // @ts-ignore
18
17
  import intlMessages from '../intl/*.json';
18
+ import {KeyboardEvent, RefObject} from 'react';
19
+ import type {TagGroupState} from '@react-stately/tag';
19
20
  import {TagProps} from '@react-types/tag';
20
- import {useGridCell, useGridRow} from '@react-aria/grid';
21
+ import {useGridListItem} from '@react-aria/gridlist';
21
22
  import {useLocalizedStringFormatter} from '@react-aria/i18n';
22
23
 
23
24
 
24
25
  export interface TagAria {
26
+ /** Props for the tag visible label (if any). */
25
27
  labelProps: DOMAttributes,
28
+ /** Props for the tag cell element. */
26
29
  tagProps: DOMAttributes,
30
+ /** Props for the tag row element. */
27
31
  tagRowProps: DOMAttributes,
28
- clearButtonProps: ButtonHTMLAttributes<HTMLButtonElement>
32
+ /** Props for the tag clear button. */
33
+ clearButtonProps: AriaButtonProps
29
34
  }
30
35
 
31
- export function useTag(props: TagProps<any>, state: GridState<any, any>): TagAria {
32
- let {isFocused} = props;
33
- const {
36
+ /**
37
+ * Provides the behavior and accessibility implementation for a tag component.
38
+ * @param props - Props to be applied to the tag.
39
+ * @param state - State for the tag group, as returned by `useTagGroupState`.
40
+ * @param ref - A ref to a DOM element for the tag.
41
+ */
42
+ export function useTag<T>(props: TagProps<T>, state: TagGroupState<T>, ref: RefObject<FocusableElement>): TagAria {
43
+ let {
44
+ isFocused,
34
45
  allowsRemoving,
35
- onRemove,
36
- item,
37
- tagRef,
38
- tagRowRef
46
+ item
39
47
  } = props;
40
- const stringFormatter = useLocalizedStringFormatter(intlMessages);
41
- const removeString = stringFormatter.format('remove');
42
- const labelId = useId();
43
- const buttonId = useId();
48
+ let stringFormatter = useLocalizedStringFormatter(intlMessages);
49
+ let removeString = stringFormatter.format('remove');
50
+ let labelId = useId();
51
+ let buttonId = useId();
44
52
 
45
- let {rowProps} = useGridRow({
53
+ let {rowProps, gridCellProps} = useGridListItem({
46
54
  node: item
47
- }, state, tagRowRef);
48
- // Don't want the row to be focusable or accessible via keyboard
49
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
50
- let {tabIndex, ...otherRowProps} = rowProps;
55
+ }, state, ref);
56
+
57
+ // We want the group to handle keyboard navigation between tags.
58
+ delete rowProps.onKeyDownCapture;
51
59
 
52
- let {gridCellProps} = useGridCell({
53
- node: [...item.childNodes][0],
54
- focusMode: 'cell'
55
- }, state, tagRef);
60
+ let onRemove = chain(props.onRemove, state.onRemove);
56
61
 
57
- function onKeyDown(e: KeyboardEvent) {
62
+ let onKeyDown = (e: KeyboardEvent) => {
58
63
  if (e.key === 'Delete' || e.key === 'Backspace' || e.key === ' ') {
59
- onRemove(item.childNodes[0].key);
64
+ onRemove(item.key);
60
65
  e.preventDefault();
61
66
  }
62
- }
63
- const pressProps = {
64
- onPress: () => onRemove?.(item.childNodes[0].key)
65
67
  };
66
68
 
67
- isFocused = isFocused || state.selectionManager.focusedKey === item.childNodes[0].key;
69
+ isFocused = isFocused || state.selectionManager.focusedKey === item.key;
68
70
  let domProps = filterDOMProps(props);
69
71
  return {
70
- clearButtonProps: mergeProps(pressProps, {
72
+ clearButtonProps: {
71
73
  'aria-label': removeString,
72
74
  'aria-labelledby': `${buttonId} ${labelId}`,
73
- id: buttonId
74
- }),
75
+ id: buttonId,
76
+ onPress: () => allowsRemoving && onRemove ? onRemove(item.key) : null
77
+ },
75
78
  labelProps: {
76
79
  id: labelId
77
80
  },
78
- tagRowProps: otherRowProps,
81
+ tagRowProps: {
82
+ ...rowProps,
83
+ tabIndex: (isFocused || state.selectionManager.focusedKey == null) ? 0 : -1,
84
+ onKeyDown: allowsRemoving ? onKeyDown : null
85
+ },
79
86
  tagProps: mergeProps(domProps, gridCellProps, {
80
87
  'aria-errormessage': props['aria-errormessage'],
81
- 'aria-label': props['aria-label'],
82
- onKeyDown: allowsRemoving ? onKeyDown : null,
83
- tabIndex: (isFocused || state.selectionManager.focusedKey == null) ? 0 : -1
88
+ 'aria-label': props['aria-label']
84
89
  })
85
90
  };
86
91
  }
@@ -10,33 +10,68 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {DOMAttributes, DOMProps} from '@react-types/shared';
13
+ import {AriaLabelingProps, DOMAttributes, DOMProps, Validation} from '@react-types/shared';
14
14
  import {filterDOMProps, mergeProps} from '@react-aria/utils';
15
- import {ReactNode, useState} from 'react';
15
+ import {RefObject, useState} from 'react';
16
+ import {TagGroupProps} from '@react-types/tag';
17
+ import type {TagGroupState} from '@react-stately/tag';
18
+ import {TagKeyboardDelegate} from './TagKeyboardDelegate';
19
+ import {useField} from '@react-aria/label';
16
20
  import {useFocusWithin} from '@react-aria/interactions';
21
+ import {useGridList} from '@react-aria/gridlist';
22
+ import {useLocale} from '@react-aria/i18n';
17
23
 
18
- export interface AriaTagGroupProps extends DOMProps {
19
- children: ReactNode,
20
- isReadOnly?: boolean, // removes close button
21
- validationState?: 'valid' | 'invalid'
24
+ export interface TagGroupAria {
25
+ /** Props for the tag grouping element. */
26
+ tagGroupProps: DOMAttributes,
27
+ /** Props for the tag group's visible label (if any). */
28
+ labelProps: DOMAttributes,
29
+ /** Props for the tag group description element, if any. */
30
+ descriptionProps: DOMAttributes,
31
+ /** Props for the tag group error message element, if any. */
32
+ errorMessageProps: DOMAttributes
22
33
  }
23
34
 
24
- export interface TagGroupAria {
25
- tagGroupProps: DOMAttributes
35
+ export interface AriaTagGroupProps<T> extends TagGroupProps<T>, DOMProps, AriaLabelingProps, Validation {
36
+ /**
37
+ * An optional keyboard delegate to handle arrow key navigation,
38
+ * to override the default.
39
+ */
40
+ keyboardDelegate?: TagKeyboardDelegate<T>
26
41
  }
27
42
 
28
- export function useTagGroup(props: AriaTagGroupProps): TagGroupAria {
43
+ /**
44
+ * Provides the behavior and accessibility implementation for a tag group component.
45
+ * Tags allow users to categorize content. They can represent keywords or people, and are grouped to describe an item or a search request.
46
+ * @param props - Props to be applied to the tag group.
47
+ * @param state - State for the tag group, as returned by `useTagGroupState`.
48
+ * @param ref - A ref to a DOM element for the tag group.
49
+ */
50
+ export function useTagGroup<T>(props: AriaTagGroupProps<T>, state: TagGroupState<T>, ref: RefObject<HTMLElement>): TagGroupAria {
51
+ let {direction} = useLocale();
52
+ let keyboardDelegate = props.keyboardDelegate || new TagKeyboardDelegate(state.collection, direction);
53
+ let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useField(props);
54
+ let {gridProps} = useGridList({...props, ...fieldProps, keyboardDelegate}, state, ref);
55
+
56
+ // Don't want the grid to be focusable or accessible via keyboard
57
+ delete gridProps.tabIndex;
58
+
29
59
  let [isFocusWithin, setFocusWithin] = useState(false);
30
60
  let {focusWithinProps} = useFocusWithin({
31
61
  onFocusWithinChange: setFocusWithin
32
62
  });
33
63
  let domProps = filterDOMProps(props);
34
64
  return {
35
- tagGroupProps: mergeProps(domProps, {
65
+ tagGroupProps: mergeProps(gridProps, domProps, {
66
+ role: state.collection.size ? 'grid' : null,
36
67
  'aria-atomic': false,
37
68
  'aria-relevant': 'additions',
38
69
  'aria-live': isFocusWithin ? 'polite' : 'off',
39
- ...focusWithinProps
40
- } as DOMAttributes)
70
+ ...focusWithinProps,
71
+ ...fieldProps
72
+ }),
73
+ labelProps,
74
+ descriptionProps,
75
+ errorMessageProps
41
76
  };
42
77
  }