|
10 | 10 | * governing permissions and limitations under the License.
|
11 | 11 | */
|
12 | 12 |
|
13 |
| -import {ButtonHTMLAttributes, HTMLAttributes, KeyboardEvent, ReactNode} from 'react'; |
14 |
| -import {DOMProps, Removable} from '@react-types/shared'; |
| 13 | +import {ButtonHTMLAttributes, HTMLAttributes, KeyboardEvent} from 'react'; |
15 | 14 | import {filterDOMProps, mergeProps, useId} from '@react-aria/utils';
|
| 15 | +import {GridState} from '@react-stately/grid'; |
16 | 16 | // @ts-ignore
|
17 | 17 | import intlMessages from '../intl/*.json';
|
| 18 | +import {TagProps} from '@react-types/tag'; |
| 19 | +import {useGridCell, useGridRow} from '@react-aria/grid'; |
18 | 20 | import {useMessageFormatter} from '@react-aria/i18n';
|
19 | 21 |
|
20 | 22 |
|
21 |
| -export interface AriaTagProps extends Removable<ReactNode, void>, DOMProps { |
22 |
| - children?: ReactNode, |
23 |
| - isDisabled?: boolean, |
24 |
| - validationState?: 'invalid' | 'valid', |
25 |
| - isSelected?: boolean, |
26 |
| - role?: string |
27 |
| -} |
28 |
| - |
29 | 23 | export interface TagAria {
|
30 |
| - tagProps: HTMLAttributes<HTMLElement>, |
31 | 24 | labelProps: HTMLAttributes<HTMLElement>,
|
| 25 | + tagProps: HTMLAttributes<HTMLElement>, |
| 26 | + tagRowProps: HTMLAttributes<HTMLElement>, |
32 | 27 | clearButtonProps: ButtonHTMLAttributes<HTMLButtonElement>
|
33 | 28 | }
|
34 | 29 |
|
35 |
| -export function useTag(props: AriaTagProps): TagAria { |
| 30 | +export function useTag(props: TagProps<any>, state: GridState<any, any>): TagAria { |
| 31 | + let {isFocused} = props; |
36 | 32 | const {
|
37 | 33 | isDisabled,
|
38 |
| - validationState, |
39 | 34 | isRemovable,
|
40 |
| - isSelected, |
41 | 35 | onRemove,
|
42 | 36 | children,
|
43 |
| - role |
| 37 | + item, |
| 38 | + tagRef, |
| 39 | + tagRowRef |
44 | 40 | } = props;
|
45 | 41 | const formatMessage = useMessageFormatter(intlMessages);
|
46 | 42 | const removeString = formatMessage('remove');
|
47 |
| - const tagId = useId(); |
| 43 | + const labelId = useId(); |
48 | 44 | const buttonId = useId();
|
49 | 45 |
|
| 46 | + let {rowProps} = useGridRow({ |
| 47 | + node: item |
| 48 | + }, state, tagRowRef); |
| 49 | + // Don't want the row to be focusable or accessible via keyboard |
| 50 | + // eslint-disable-next-line @typescript-eslint/no-unused-vars |
| 51 | + let {tabIndex, ...otherRowProps} = rowProps; |
| 52 | + |
| 53 | + let {gridCellProps} = useGridCell({ |
| 54 | + node: [...item.childNodes][0], |
| 55 | + focusMode: 'cell' |
| 56 | + }, state, tagRef); |
| 57 | + |
50 | 58 | function onKeyDown(e: KeyboardEvent<HTMLElement>) {
|
51 | 59 | if (e.key === 'Delete' || e.key === 'Backspace') {
|
52 | 60 | onRemove(children, e);
|
53 | 61 | e.preventDefault();
|
54 | 62 | }
|
55 | 63 | }
|
56 | 64 | const pressProps = {
|
57 |
| - onPress: e => onRemove(children, e) |
| 65 | + onPress: e => onRemove?.(children, e) |
58 | 66 | };
|
59 | 67 |
|
| 68 | + isFocused = isFocused || state.selectionManager.focusedKey === item.childNodes[0].key; |
60 | 69 | let domProps = filterDOMProps(props);
|
61 | 70 | return {
|
62 |
| - tagProps: mergeProps(domProps, { |
63 |
| - 'aria-selected': !isDisabled && isSelected, |
64 |
| - 'aria-invalid': validationState === 'invalid' || undefined, |
65 |
| - 'aria-errormessage': props['aria-errormessage'], |
66 |
| - onKeyDown: !isDisabled && isRemovable ? onKeyDown : null, |
67 |
| - role: role === 'gridcell' ? 'row' : null, |
68 |
| - tabIndex: isDisabled ? -1 : 0 |
69 |
| - }), |
70 |
| - labelProps: { |
71 |
| - id: tagId, |
72 |
| - role |
73 |
| - }, |
74 | 71 | clearButtonProps: mergeProps(pressProps, {
|
75 | 72 | 'aria-label': removeString,
|
76 |
| - 'aria-labelledby': `${buttonId} ${tagId}`, |
| 73 | + 'aria-labelledby': `${buttonId} ${labelId}`, |
77 | 74 | id: buttonId,
|
78 |
| - title: removeString, |
79 |
| - isDisabled, |
80 |
| - role |
| 75 | + isDisabled |
| 76 | + }), |
| 77 | + labelProps: { |
| 78 | + id: labelId |
| 79 | + }, |
| 80 | + tagRowProps: otherRowProps, |
| 81 | + tagProps: mergeProps(domProps, gridCellProps, { |
| 82 | + 'aria-errormessage': props['aria-errormessage'], |
| 83 | + 'aria-label': props['aria-label'], |
| 84 | + onKeyDown: !isDisabled && isRemovable ? onKeyDown : null, |
| 85 | + tabIndex: (isFocused || state.selectionManager.focusedKey == null) && !isDisabled ? 0 : -1, |
| 86 | + onFocus() { |
| 87 | + state.selectionManager.setFocusedKey(item.childNodes[0].key); |
| 88 | + }, |
| 89 | + ref: tagRef |
81 | 90 | })
|
82 | 91 | };
|
83 | 92 | }
|
0 commit comments