|
10 | 10 | * governing permissions and limitations under the License.
|
11 | 11 | */
|
12 | 12 |
|
13 |
| -import {ButtonHTMLAttributes, KeyboardEvent} from 'react'; |
| 13 | +import {AriaButtonProps} from '@react-types/button'; |
| 14 | +import {chain, filterDOMProps, mergeProps, useId} from '@react-aria/utils'; |
14 | 15 | import {DOMAttributes} from '@react-types/shared';
|
15 |
| -import {filterDOMProps, mergeProps, useId} from '@react-aria/utils'; |
16 |
| -import {GridState} from '@react-stately/grid'; |
17 | 16 | // @ts-ignore
|
18 | 17 | import intlMessages from '../intl/*.json';
|
| 18 | +import {KeyboardEvent} 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 {
|
25 | 26 | labelProps: DOMAttributes,
|
26 | 27 | tagProps: DOMAttributes,
|
27 | 28 | tagRowProps: DOMAttributes,
|
28 |
| - clearButtonProps: ButtonHTMLAttributes<HTMLButtonElement> |
| 29 | + clearButtonProps: AriaButtonProps |
29 | 30 | }
|
30 | 31 |
|
31 |
| -export function useTag(props: TagProps<any>, state: GridState<any, any>): TagAria { |
32 |
| - let {isFocused} = props; |
33 |
| - const { |
| 32 | +/** |
| 33 | + * Provides the behavior and accessibility implementation for a tag component. |
| 34 | + * @param props - Props to be applied to the tag. |
| 35 | + * @param state - State for the tag group, as returned by `useTagGroupState`. |
| 36 | + */ |
| 37 | +export function useTag<T>(props: TagProps<T>, state: TagGroupState<T>): TagAria { |
| 38 | + let { |
| 39 | + isFocused, |
34 | 40 | allowsRemoving,
|
35 |
| - onRemove, |
36 | 41 | item,
|
37 |
| - tagRef, |
38 | 42 | tagRowRef
|
39 | 43 | } = props;
|
40 |
| - const stringFormatter = useLocalizedStringFormatter(intlMessages); |
41 |
| - const removeString = stringFormatter.format('remove'); |
42 |
| - const labelId = useId(); |
43 |
| - const buttonId = useId(); |
| 44 | + let stringFormatter = useLocalizedStringFormatter(intlMessages); |
| 45 | + let removeString = stringFormatter.format('remove'); |
| 46 | + let labelId = useId(); |
| 47 | + let buttonId = useId(); |
44 | 48 |
|
45 |
| - let {rowProps} = useGridRow({ |
| 49 | + let {rowProps, gridCellProps} = useGridListItem({ |
46 | 50 | node: item
|
47 | 51 | }, 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; |
51 | 52 |
|
52 |
| - let {gridCellProps} = useGridCell({ |
53 |
| - node: [...item.childNodes][0], |
54 |
| - focusMode: 'cell' |
55 |
| - }, state, tagRef); |
| 53 | + // We want the group to handle keyboard navigation between tags. |
| 54 | + delete rowProps.onKeyDownCapture; |
| 55 | + |
| 56 | + let onRemove = chain(props.onRemove, state.onRemove); |
56 | 57 |
|
57 |
| - function onKeyDown(e: KeyboardEvent) { |
| 58 | + let onKeyDown = (e: KeyboardEvent) => { |
58 | 59 | if (e.key === 'Delete' || e.key === 'Backspace' || e.key === ' ') {
|
59 |
| - onRemove(item.childNodes[0].key); |
| 60 | + onRemove(item.key); |
60 | 61 | e.preventDefault();
|
61 | 62 | }
|
62 |
| - } |
63 |
| - const pressProps = { |
64 |
| - onPress: () => onRemove?.(item.childNodes[0].key) |
65 | 63 | };
|
66 | 64 |
|
67 |
| - isFocused = isFocused || state.selectionManager.focusedKey === item.childNodes[0].key; |
| 65 | + isFocused = isFocused || state.selectionManager.focusedKey === item.key; |
68 | 66 | let domProps = filterDOMProps(props);
|
69 | 67 | return {
|
70 |
| - clearButtonProps: mergeProps(pressProps, { |
| 68 | + clearButtonProps: { |
71 | 69 | 'aria-label': removeString,
|
72 | 70 | 'aria-labelledby': `${buttonId} ${labelId}`,
|
73 |
| - id: buttonId |
74 |
| - }), |
| 71 | + id: buttonId, |
| 72 | + onPress: () => allowsRemoving && onRemove ? onRemove(item.key) : null |
| 73 | + }, |
75 | 74 | labelProps: {
|
76 | 75 | id: labelId
|
77 | 76 | },
|
78 |
| - tagRowProps: otherRowProps, |
| 77 | + tagRowProps: { |
| 78 | + ...rowProps, |
| 79 | + tabIndex: (isFocused || state.selectionManager.focusedKey == null) ? 0 : -1, |
| 80 | + onKeyDown: allowsRemoving ? onKeyDown : null |
| 81 | + }, |
79 | 82 | tagProps: mergeProps(domProps, gridCellProps, {
|
80 | 83 | '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 |
| 84 | + 'aria-label': props['aria-label'] |
84 | 85 | })
|
85 | 86 | };
|
86 | 87 | }
|
0 commit comments