-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Expand file tree
/
Copy pathuseList.ts
More file actions
117 lines (104 loc) · 3.79 KB
/
useList.ts
File metadata and controls
117 lines (104 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
'use client';
import * as React from 'react';
import {
getIntrinsicElementProps,
OnSelectionChangeData,
slot,
useControllableState,
useEventCallback,
} from '@fluentui/react-utilities';
import { useArrowNavigationGroup, useFocusFinders } from '@fluentui/react-tabster';
import { ListBaseProps, ListBaseState, ListProps, ListState } from './List.types';
import { useListSelection } from '../../hooks/useListSelection';
import {
calculateListItemRoleForListRole,
calculateListRole,
validateGridCellsArePresent,
validateProperElementTypes,
validateProperRolesAreUsed,
} from '../../utils';
const DEFAULT_ROOT_EL_TYPE = 'ul';
/**
* Create the state required to render List.
*
* The returned state can be modified with hooks such as useListStyles_unstable,
* before being passed to renderList_unstable.
*
* @param props - props from this instance of List
* @param ref - reference to root HTMLElement of List
*/
export const useList_unstable = (
props: ListProps,
ref: React.Ref<HTMLDivElement | HTMLUListElement | HTMLOListElement>,
): ListState => {
return useListBase_unstable(props, ref);
};
/**
* Base hook for List component, which manages state related to ARIA, keyboard navigation,
* selection, and slot structure. List has no design-specific props, so this hook contains
* the full component logic.
*
* @param props - User provided props to the List component.
* @param ref - User provided ref to be passed to the List component.
*/
export const useListBase_unstable = (
props: ListBaseProps,
ref: React.Ref<HTMLDivElement | HTMLUListElement | HTMLOListElement>,
): ListBaseState => {
const { navigationMode, selectionMode, selectedItems, defaultSelectedItems, onSelectionChange } = props;
const as = props.as || navigationMode === 'composite' ? 'div' : DEFAULT_ROOT_EL_TYPE;
const arrowNavigationAttributes = useArrowNavigationGroup({
axis: 'vertical',
memorizeCurrent: true,
});
const [selectionState, setSelectionState] = useControllableState({
state: selectedItems,
defaultState: defaultSelectedItems,
initialState: [],
});
const onChange = useEventCallback((e: React.SyntheticEvent, data: OnSelectionChangeData) => {
const selectedItemsAsArray = Array.from(data.selectedItems);
setSelectionState(selectedItemsAsArray);
onSelectionChange?.(e, { event: e, type: 'change', selectedItems: selectedItemsAsArray });
});
const selection = useListSelection({
onSelectionChange: onChange,
selectionMode: selectionMode || 'multiselect',
selectedItems: selectionState,
});
const listRole = props.role || calculateListRole(navigationMode, !!selectionMode);
const listItemRole = calculateListItemRoleForListRole(listRole);
const { findAllFocusable } = useFocusFinders();
const validateListItem = useEventCallback((listItemEl: HTMLElement) => {
if (process.env.NODE_ENV === 'production') {
return;
}
const itemRole = listItemEl.getAttribute('role') || '';
const focusable = findAllFocusable(listItemEl);
validateProperElementTypes(as, listItemEl.tagName.toLocaleLowerCase());
validateProperRolesAreUsed(listRole, itemRole, !!selectionMode, focusable.length > 0);
validateGridCellsArePresent(listRole, listItemEl);
});
return {
components: {
root: as,
},
root: slot.always(
getIntrinsicElementProps(as, {
ref,
role: listRole,
...(selectionMode && {
'aria-multiselectable': selectionMode === 'multiselect' ? true : undefined,
}),
...arrowNavigationAttributes,
...props,
}),
{ elementType: as },
),
listItemRole,
validateListItem,
navigationMode,
// only pass down selection state if its handled internally, otherwise just report the events
selection: selectionMode ? selection : undefined,
};
};