forked from microsoft/fluentui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuseOptionCollection.ts
76 lines (66 loc) · 2.44 KB
/
useOptionCollection.ts
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
import * as React from 'react';
import type { OptionCollectionState, OptionValue } from './OptionCollection.types';
/**
* A hook for managing a collection of child Options
*/
export const useOptionCollection = (): OptionCollectionState => {
const nodes = React.useRef<{ option: OptionValue; element: HTMLElement }[]>([]);
const collectionAPI = React.useMemo(() => {
const getCount = () => nodes.current.length;
const getOptionAtIndex = (index: number) => nodes.current[index]?.option;
const getIndexOfId = (id: string) => nodes.current.findIndex(node => node.option.id === id);
const getOptionById = (id: string) => {
const item = nodes.current.find(node => node.option.id === id);
return item?.option;
};
const getOptionsMatchingText = (matcher: (text: string) => boolean) => {
return nodes.current.filter(node => matcher(node.option.text)).map(node => node.option);
};
const getOptionsMatchingValue = (matcher: (value: string) => boolean) => {
return nodes.current.filter(node => matcher(node.option.value)).map(node => node.option);
};
return {
getCount,
getOptionAtIndex,
getIndexOfId,
getOptionById,
getOptionsMatchingText,
getOptionsMatchingValue,
};
}, []);
const registerOption = React.useCallback((option: OptionValue, element: HTMLElement) => {
const index = nodes.current.findIndex(node => {
if (!node.element || !element) {
return false;
}
if (node.option.id === option.id) {
return true;
}
// use the DOM method compareDocumentPosition to order the current node against registered nodes
// eslint-disable-next-line no-bitwise
return node.element.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_PRECEDING;
});
// do not register the option if it already exists
if (nodes.current[index]?.option.id !== option.id) {
const newItem = {
element,
option,
};
// If an index is not found we will push the element to the end.
if (index === -1) {
nodes.current = [...nodes.current, newItem];
} else {
nodes.current.splice(index, 0, newItem);
}
}
// return the unregister function
return () => {
nodes.current = nodes.current.filter(node => node.option.id !== option.id);
};
}, []);
return {
...collectionAPI,
options: nodes.current.map(node => node.option),
registerOption,
};
};