@dhis2-ui/select 10.9.2 → 10.10.0-alpha.1
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/build/cjs/index.js +18 -4
- package/build/cjs/locales/en/translations.json +7 -5
- package/build/cjs/simple-single-select/__stories__/DefaultPosition.js +38 -0
- package/build/cjs/simple-single-select/__stories__/Dense.js +22 -0
- package/build/cjs/simple-single-select/__stories__/Empty.js +20 -0
- package/build/cjs/simple-single-select/__stories__/EmptyWithEmptyNode.js +28 -0
- package/build/cjs/simple-single-select/__stories__/EmptyWithEmptyText.js +21 -0
- package/build/cjs/simple-single-select/__stories__/FlippedPosition.js +37 -0
- package/build/cjs/simple-single-select/__stories__/InfiniteLoading.js +61 -0
- package/build/cjs/simple-single-select/__stories__/Loading.js +20 -0
- package/build/cjs/simple-single-select/__stories__/ShiftedIntoView.js +28 -0
- package/build/cjs/simple-single-select/__stories__/WithClearButton.js +25 -0
- package/build/cjs/simple-single-select/__stories__/WithCustomLowMaxHeight.js +22 -0
- package/build/cjs/simple-single-select/__stories__/WithCustomOptions.js +48 -0
- package/build/cjs/simple-single-select/__stories__/WithDisabledOption.js +28 -0
- package/build/cjs/simple-single-select/__stories__/WithFilterField.js +36 -0
- package/build/cjs/simple-single-select/__stories__/WithInitialFocus.js +38 -0
- package/build/cjs/simple-single-select/__stories__/WithManyOptions.js +24 -0
- package/build/cjs/simple-single-select/__stories__/WithNoMatchText.js +30 -0
- package/build/cjs/simple-single-select/__stories__/WithOnBlur.js +23 -0
- package/build/cjs/simple-single-select/__stories__/WithOnFocus.js +23 -0
- package/build/cjs/simple-single-select/__stories__/WithOptionsAndDisabled.js +22 -0
- package/build/cjs/simple-single-select/__stories__/WithOptionsAndLoading.js +34 -0
- package/build/cjs/simple-single-select/__stories__/WithOptionsAndLoadingText.js +35 -0
- package/build/cjs/simple-single-select/__stories__/WithPlaceholder.js +23 -0
- package/build/cjs/simple-single-select/__stories__/WithPlaceholderAndSelection.js +25 -0
- package/build/cjs/simple-single-select/__stories__/WithPrefix.js +22 -0
- package/build/cjs/simple-single-select/__stories__/WithPrefixAndSelection.js +25 -0
- package/build/cjs/simple-single-select/__stories__/WithRTL.js +34 -0
- package/build/cjs/simple-single-select/__stories__/WithSelection.js +24 -0
- package/build/cjs/simple-single-select/__stories__/WithSelectionAndDisabled.js +25 -0
- package/build/cjs/simple-single-select/__stories__/WithoutOptionsAndLoading.js +21 -0
- package/build/cjs/simple-single-select/__stories__/WithoutSelection.js +22 -0
- package/build/cjs/simple-single-select/__stories__/options.js +77 -0
- package/build/cjs/simple-single-select/features/keyboard-interactions.test.e2e.js +441 -0
- package/build/cjs/simple-single-select/features/menu-positioning.test.e2e.js +152 -0
- package/build/cjs/simple-single-select/index.js +19 -0
- package/build/cjs/simple-single-select/is-option-hidden.js +22 -0
- package/build/cjs/simple-single-select/menu/empty.js +26 -0
- package/build/cjs/simple-single-select/menu/filter.js +60 -0
- package/build/cjs/simple-single-select/menu/index.js +12 -0
- package/build/cjs/simple-single-select/menu/loading.js +30 -0
- package/build/cjs/simple-single-select/menu/menu.js +158 -0
- package/build/cjs/simple-single-select/menu/no-match.js +28 -0
- package/build/cjs/simple-single-select/menu/option.js +120 -0
- package/build/cjs/simple-single-select/menu/options-list.js +97 -0
- package/build/cjs/simple-single-select/selected-value/clear-button.js +74 -0
- package/build/cjs/simple-single-select/selected-value/container.js +100 -0
- package/build/cjs/simple-single-select/selected-value/index.js +12 -0
- package/build/cjs/simple-single-select/selected-value/placeholder.js +30 -0
- package/build/cjs/simple-single-select/selected-value/prefix.js +30 -0
- package/build/cjs/simple-single-select/selected-value/selected-value.js +119 -0
- package/build/cjs/simple-single-select/shared-prop-types.js +14 -0
- package/build/cjs/simple-single-select/simple-single-select.e2e.stories.js +156 -0
- package/build/cjs/simple-single-select/simple-single-select.js +319 -0
- package/build/cjs/simple-single-select/simple-single-select.prod.stories.js +228 -0
- package/build/cjs/simple-single-select/simple-single-select.test.js +1106 -0
- package/build/cjs/simple-single-select/use-handle-key-press/index.js +19 -0
- package/build/cjs/simple-single-select/use-handle-key-press/use-focus-option.js +24 -0
- package/build/cjs/simple-single-select/use-handle-key-press/use-handle-key-press-on-combobox.js +177 -0
- package/build/cjs/simple-single-select/use-handle-key-press/use-handle-key-press-on-filter-input.js +106 -0
- package/build/cjs/simple-single-select/use-handle-key-press/use-handle-typing.js +81 -0
- package/build/cjs/simple-single-select/use-handle-key-press/use-highlight-first-option-on-previous-page.js +50 -0
- package/build/cjs/simple-single-select/use-handle-key-press/use-highlight-first-visible-option.js +30 -0
- package/build/cjs/simple-single-select/use-handle-key-press/use-highlight-last-option-on-next-page.js +50 -0
- package/build/cjs/simple-single-select/use-handle-key-press/use-highlight-last-visible-option.js +42 -0
- package/build/cjs/simple-single-select/use-handle-key-press/use-page-down.js +45 -0
- package/build/cjs/simple-single-select/use-handle-key-press/use-page-up.js +44 -0
- package/build/cjs/simple-single-select/use-handle-key-press/utils.js +46 -0
- package/build/cjs/simple-single-select-field/index.js +12 -0
- package/build/cjs/simple-single-select-field/simple-single-select-field.js +138 -0
- package/build/cjs/simple-single-select-field/simple-single-select-field.prod.stories.js +188 -0
- package/build/cjs/simple-single-select-field/simple-single-select-field.test.js +133 -0
- package/build/es/index.js +2 -0
- package/build/es/locales/en/translations.json +7 -5
- package/build/es/simple-single-select/__stories__/DefaultPosition.js +29 -0
- package/build/es/simple-single-select/__stories__/Dense.js +13 -0
- package/build/es/simple-single-select/__stories__/Empty.js +11 -0
- package/build/es/simple-single-select/__stories__/EmptyWithEmptyNode.js +19 -0
- package/build/es/simple-single-select/__stories__/EmptyWithEmptyText.js +12 -0
- package/build/es/simple-single-select/__stories__/FlippedPosition.js +28 -0
- package/build/es/simple-single-select/__stories__/InfiniteLoading.js +53 -0
- package/build/es/simple-single-select/__stories__/Loading.js +12 -0
- package/build/es/simple-single-select/__stories__/ShiftedIntoView.js +18 -0
- package/build/es/simple-single-select/__stories__/WithClearButton.js +16 -0
- package/build/es/simple-single-select/__stories__/WithCustomLowMaxHeight.js +13 -0
- package/build/es/simple-single-select/__stories__/WithCustomOptions.js +38 -0
- package/build/es/simple-single-select/__stories__/WithDisabledOption.js +19 -0
- package/build/es/simple-single-select/__stories__/WithFilterField.js +27 -0
- package/build/es/simple-single-select/__stories__/WithInitialFocus.js +29 -0
- package/build/es/simple-single-select/__stories__/WithManyOptions.js +15 -0
- package/build/es/simple-single-select/__stories__/WithNoMatchText.js +21 -0
- package/build/es/simple-single-select/__stories__/WithOnBlur.js +14 -0
- package/build/es/simple-single-select/__stories__/WithOnFocus.js +14 -0
- package/build/es/simple-single-select/__stories__/WithOptionsAndDisabled.js +13 -0
- package/build/es/simple-single-select/__stories__/WithOptionsAndLoading.js +25 -0
- package/build/es/simple-single-select/__stories__/WithOptionsAndLoadingText.js +26 -0
- package/build/es/simple-single-select/__stories__/WithPlaceholder.js +14 -0
- package/build/es/simple-single-select/__stories__/WithPlaceholderAndSelection.js +16 -0
- package/build/es/simple-single-select/__stories__/WithPrefix.js +13 -0
- package/build/es/simple-single-select/__stories__/WithPrefixAndSelection.js +16 -0
- package/build/es/simple-single-select/__stories__/WithRTL.js +25 -0
- package/build/es/simple-single-select/__stories__/WithSelection.js +15 -0
- package/build/es/simple-single-select/__stories__/WithSelectionAndDisabled.js +16 -0
- package/build/es/simple-single-select/__stories__/WithoutOptionsAndLoading.js +12 -0
- package/build/es/simple-single-select/__stories__/WithoutSelection.js +13 -0
- package/build/es/simple-single-select/__stories__/options.js +71 -0
- package/build/es/simple-single-select/features/keyboard-interactions.test.e2e.js +439 -0
- package/build/es/simple-single-select/features/menu-positioning.test.e2e.js +150 -0
- package/build/es/simple-single-select/index.js +2 -0
- package/build/es/simple-single-select/is-option-hidden.js +16 -0
- package/build/es/simple-single-select/menu/empty.js +19 -0
- package/build/es/simple-single-select/menu/filter.js +53 -0
- package/build/es/simple-single-select/menu/index.js +1 -0
- package/build/es/simple-single-select/menu/loading.js +23 -0
- package/build/es/simple-single-select/menu/menu.js +149 -0
- package/build/es/simple-single-select/menu/no-match.js +21 -0
- package/build/es/simple-single-select/menu/option.js +111 -0
- package/build/es/simple-single-select/menu/options-list.js +88 -0
- package/build/es/simple-single-select/selected-value/clear-button.js +68 -0
- package/build/es/simple-single-select/selected-value/container.js +91 -0
- package/build/es/simple-single-select/selected-value/index.js +1 -0
- package/build/es/simple-single-select/selected-value/placeholder.js +23 -0
- package/build/es/simple-single-select/selected-value/prefix.js +23 -0
- package/build/es/simple-single-select/selected-value/selected-value.js +112 -0
- package/build/es/simple-single-select/shared-prop-types.js +7 -0
- package/build/es/simple-single-select/simple-single-select.e2e.stories.js +142 -0
- package/build/es/simple-single-select/simple-single-select.js +310 -0
- package/build/es/simple-single-select/simple-single-select.prod.stories.js +36 -0
- package/build/es/simple-single-select/simple-single-select.test.js +1103 -0
- package/build/es/simple-single-select/use-handle-key-press/index.js +2 -0
- package/build/es/simple-single-select/use-handle-key-press/use-focus-option.js +18 -0
- package/build/es/simple-single-select/use-handle-key-press/use-handle-key-press-on-combobox.js +171 -0
- package/build/es/simple-single-select/use-handle-key-press/use-handle-key-press-on-filter-input.js +100 -0
- package/build/es/simple-single-select/use-handle-key-press/use-handle-typing.js +75 -0
- package/build/es/simple-single-select/use-handle-key-press/use-highlight-first-option-on-previous-page.js +44 -0
- package/build/es/simple-single-select/use-handle-key-press/use-highlight-first-visible-option.js +24 -0
- package/build/es/simple-single-select/use-handle-key-press/use-highlight-last-option-on-next-page.js +44 -0
- package/build/es/simple-single-select/use-handle-key-press/use-highlight-last-visible-option.js +36 -0
- package/build/es/simple-single-select/use-handle-key-press/use-page-down.js +39 -0
- package/build/es/simple-single-select/use-handle-key-press/use-page-up.js +38 -0
- package/build/es/simple-single-select/use-handle-key-press/utils.js +36 -0
- package/build/es/simple-single-select-field/index.js +1 -0
- package/build/es/simple-single-select-field/simple-single-select-field.js +131 -0
- package/build/es/simple-single-select-field/simple-single-select-field.prod.stories.js +172 -0
- package/build/es/simple-single-select-field/simple-single-select-field.test.js +130 -0
- package/package.json +15 -15
- package/types/index.d.ts +184 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
export function useFocusOption(findIndexCallback, _ref) {
|
|
3
|
+
let {
|
|
4
|
+
options,
|
|
5
|
+
focussedOptionIndex,
|
|
6
|
+
setFocussedOptionIndex
|
|
7
|
+
} = _ref;
|
|
8
|
+
return useCallback(() => {
|
|
9
|
+
const nextFocussedIndex = findIndexCallback({
|
|
10
|
+
options,
|
|
11
|
+
activeIndex: focussedOptionIndex
|
|
12
|
+
});
|
|
13
|
+
if (nextFocussedIndex === -1) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
setFocussedOptionIndex(nextFocussedIndex);
|
|
17
|
+
}, [findIndexCallback, options, focussedOptionIndex, setFocussedOptionIndex]);
|
|
18
|
+
}
|
package/build/es/simple-single-select/use-handle-key-press/use-handle-key-press-on-combobox.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useFocusOption } from './use-focus-option.js';
|
|
3
|
+
import { useHandleTyping } from './use-handle-typing.js';
|
|
4
|
+
import { usePageDown } from './use-page-down.js';
|
|
5
|
+
import { usePageUp } from './use-page-up.js';
|
|
6
|
+
import { findNextOptionIndex, findPrevOptionIndex, findFirstOptionIndex, findLastOptionIndex } from './utils.js';
|
|
7
|
+
function useSelectOption(findIndexCallback, _ref) {
|
|
8
|
+
let {
|
|
9
|
+
options,
|
|
10
|
+
onChange,
|
|
11
|
+
value
|
|
12
|
+
} = _ref;
|
|
13
|
+
return useCallback(() => {
|
|
14
|
+
const currentOptionIndex = options.findIndex(option => option.value === value);
|
|
15
|
+
const nextSelectedOptionIndex = findIndexCallback({
|
|
16
|
+
options,
|
|
17
|
+
activeIndex: currentOptionIndex
|
|
18
|
+
});
|
|
19
|
+
if (nextSelectedOptionIndex === -1) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const nextOption = options[nextSelectedOptionIndex];
|
|
23
|
+
onChange({
|
|
24
|
+
label: nextOption.label,
|
|
25
|
+
value: nextOption.value
|
|
26
|
+
});
|
|
27
|
+
}, [findIndexCallback, options, onChange, value]);
|
|
28
|
+
}
|
|
29
|
+
export function useHandleKeyPressOnCombobox(_ref2) {
|
|
30
|
+
let {
|
|
31
|
+
value,
|
|
32
|
+
disabled,
|
|
33
|
+
expanded,
|
|
34
|
+
loading,
|
|
35
|
+
options,
|
|
36
|
+
openMenu,
|
|
37
|
+
closeMenu,
|
|
38
|
+
listBoxRef,
|
|
39
|
+
focussedOptionIndex,
|
|
40
|
+
setFocussedOptionIndex,
|
|
41
|
+
selectFocussedOption,
|
|
42
|
+
onChange
|
|
43
|
+
} = _ref2;
|
|
44
|
+
const {
|
|
45
|
+
onTyping,
|
|
46
|
+
typing
|
|
47
|
+
} = useHandleTyping({
|
|
48
|
+
expanded,
|
|
49
|
+
options,
|
|
50
|
+
setFocussedOptionIndex,
|
|
51
|
+
onChange
|
|
52
|
+
});
|
|
53
|
+
const pageDown = usePageDown({
|
|
54
|
+
options,
|
|
55
|
+
focussedOptionIndex,
|
|
56
|
+
setFocussedOptionIndex,
|
|
57
|
+
listBoxRef
|
|
58
|
+
});
|
|
59
|
+
const pageUp = usePageUp({
|
|
60
|
+
options,
|
|
61
|
+
listBoxRef,
|
|
62
|
+
focussedOptionIndex,
|
|
63
|
+
setFocussedOptionIndex
|
|
64
|
+
});
|
|
65
|
+
const selectNextOption = useSelectOption(findNextOptionIndex, {
|
|
66
|
+
options,
|
|
67
|
+
onChange,
|
|
68
|
+
value
|
|
69
|
+
});
|
|
70
|
+
const selectPrevOption = useSelectOption(findPrevOptionIndex, {
|
|
71
|
+
options,
|
|
72
|
+
onChange,
|
|
73
|
+
value
|
|
74
|
+
});
|
|
75
|
+
const focusNextOption = useFocusOption(findNextOptionIndex, {
|
|
76
|
+
options,
|
|
77
|
+
focussedOptionIndex,
|
|
78
|
+
setFocussedOptionIndex
|
|
79
|
+
});
|
|
80
|
+
const focusPrevOption = useFocusOption(findPrevOptionIndex, {
|
|
81
|
+
options,
|
|
82
|
+
focussedOptionIndex,
|
|
83
|
+
setFocussedOptionIndex
|
|
84
|
+
});
|
|
85
|
+
const focusFirstOption = useFocusOption(findFirstOptionIndex, {
|
|
86
|
+
options,
|
|
87
|
+
focussedOptionIndex,
|
|
88
|
+
setFocussedOptionIndex
|
|
89
|
+
});
|
|
90
|
+
const focusLastOption = useFocusOption(findLastOptionIndex, {
|
|
91
|
+
options,
|
|
92
|
+
focussedOptionIndex,
|
|
93
|
+
setFocussedOptionIndex
|
|
94
|
+
});
|
|
95
|
+
const handleKeyPress = useCallback(_ref3 => {
|
|
96
|
+
let {
|
|
97
|
+
key,
|
|
98
|
+
altKey,
|
|
99
|
+
ctrlKey,
|
|
100
|
+
metaKey
|
|
101
|
+
} = _ref3;
|
|
102
|
+
if (disabled || loading) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/*
|
|
107
|
+
* The menu won't be an ancestor / sibling of the combobox,
|
|
108
|
+
* so when pressing tab, it's highly unlikely that any element
|
|
109
|
+
* in the menu would receive focus (happens only in storybook,
|
|
110
|
+
* where there's no other focussable element).
|
|
111
|
+
*
|
|
112
|
+
* Assistive technology will know about the menu as the combobox
|
|
113
|
+
* has the appropriate aria attributes that will point to the
|
|
114
|
+
* correct HTML elements.
|
|
115
|
+
*/
|
|
116
|
+
if (expanded && key === 'Tab') {
|
|
117
|
+
closeMenu();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (expanded && (key === 'Escape' || key === 'Enter' || key === ' ' && !typing || key === 'ArrowUp' && altKey || key === 'ArrowDown' && altKey)) {
|
|
121
|
+
if (value !== options[focussedOptionIndex].value) {
|
|
122
|
+
selectFocussedOption();
|
|
123
|
+
}
|
|
124
|
+
closeMenu();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!expanded && (key === ' ' && !typing || key === 'Enter' || key === 'ArrowUp' && altKey || key === 'ArrowDown' && altKey)) {
|
|
128
|
+
openMenu();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (key === 'ArrowDown' && !expanded) {
|
|
132
|
+
selectNextOption();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (key === 'ArrowDown') {
|
|
136
|
+
focusNextOption();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (key === 'ArrowUp' && !expanded) {
|
|
140
|
+
selectPrevOption();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (key === 'ArrowUp') {
|
|
144
|
+
focusPrevOption();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (key === 'Home') {
|
|
148
|
+
focusFirstOption();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (key === 'End') {
|
|
152
|
+
focusLastOption();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (key === 'Backspace' || key === 'Clear' || key.length === 1 && key !== ' ' && !altKey && !ctrlKey && !metaKey || key === ' ' && typing) {
|
|
156
|
+
onTyping(key);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (expanded && key === 'PageUp') {
|
|
160
|
+
pageUp();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (expanded && key === 'PageDown') {
|
|
164
|
+
pageDown();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Do nothing
|
|
169
|
+
}, [closeMenu, disabled, expanded, loading, openMenu, options, value, typing, focussedOptionIndex, selectFocussedOption, selectNextOption, selectPrevOption, focusNextOption, focusPrevOption, focusFirstOption, focusLastOption, pageDown, pageUp, onTyping]);
|
|
170
|
+
return handleKeyPress;
|
|
171
|
+
}
|
package/build/es/simple-single-select/use-handle-key-press/use-handle-key-press-on-filter-input.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useFocusOption } from './use-focus-option.js';
|
|
3
|
+
import { usePageDown } from './use-page-down.js';
|
|
4
|
+
import { usePageUp } from './use-page-up.js';
|
|
5
|
+
import { findNextOptionIndex, findPrevOptionIndex, findFirstOptionIndex, findLastOptionIndex } from './utils.js';
|
|
6
|
+
export function useHandleKeyPressOnFilterInput(_ref) {
|
|
7
|
+
let {
|
|
8
|
+
value,
|
|
9
|
+
options,
|
|
10
|
+
loading,
|
|
11
|
+
closeMenu,
|
|
12
|
+
listBoxRef,
|
|
13
|
+
focusComboBox,
|
|
14
|
+
focussedOptionIndex,
|
|
15
|
+
setFocussedOptionIndex,
|
|
16
|
+
selectFocussedOption
|
|
17
|
+
} = _ref;
|
|
18
|
+
const pageDown = usePageDown({
|
|
19
|
+
options,
|
|
20
|
+
focussedOptionIndex,
|
|
21
|
+
setFocussedOptionIndex,
|
|
22
|
+
listBoxRef
|
|
23
|
+
});
|
|
24
|
+
const pageUp = usePageUp({
|
|
25
|
+
options,
|
|
26
|
+
listBoxRef,
|
|
27
|
+
focussedOptionIndex,
|
|
28
|
+
setFocussedOptionIndex
|
|
29
|
+
});
|
|
30
|
+
const focusNextOption = useFocusOption(findNextOptionIndex, {
|
|
31
|
+
options,
|
|
32
|
+
focussedOptionIndex,
|
|
33
|
+
setFocussedOptionIndex
|
|
34
|
+
});
|
|
35
|
+
const focusPrevOption = useFocusOption(findPrevOptionIndex, {
|
|
36
|
+
options,
|
|
37
|
+
focussedOptionIndex,
|
|
38
|
+
setFocussedOptionIndex
|
|
39
|
+
});
|
|
40
|
+
const focusFirstOption = useFocusOption(findFirstOptionIndex, {
|
|
41
|
+
options,
|
|
42
|
+
focussedOptionIndex,
|
|
43
|
+
setFocussedOptionIndex
|
|
44
|
+
});
|
|
45
|
+
const focusLastOption = useFocusOption(findLastOptionIndex, {
|
|
46
|
+
options,
|
|
47
|
+
focussedOptionIndex,
|
|
48
|
+
setFocussedOptionIndex
|
|
49
|
+
});
|
|
50
|
+
const handleKeyPress = useCallback(e => {
|
|
51
|
+
if (loading) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const {
|
|
55
|
+
key,
|
|
56
|
+
altKey
|
|
57
|
+
} = e;
|
|
58
|
+
if (key === 'Escape' || key === 'Enter' || key === 'ArrowUp' && altKey || key === 'ArrowDown' && altKey) {
|
|
59
|
+
if (value !== options[focussedOptionIndex].value) {
|
|
60
|
+
selectFocussedOption();
|
|
61
|
+
}
|
|
62
|
+
closeMenu();
|
|
63
|
+
|
|
64
|
+
// As the filter is being focused, we want to move focus back to the combobox
|
|
65
|
+
focusComboBox();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (key === 'ArrowDown') {
|
|
69
|
+
e.preventDefault(); // Disable moving cursor to end
|
|
70
|
+
focusNextOption();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (key === 'ArrowUp') {
|
|
74
|
+
e.preventDefault(); // Disable moving cursor to start
|
|
75
|
+
focusPrevOption();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (key === 'Home') {
|
|
79
|
+
e.preventDefault(); // Disable moving cursor to start
|
|
80
|
+
focusFirstOption();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (key === 'End') {
|
|
84
|
+
e.preventDefault(); // Disable moving cursor to end
|
|
85
|
+
focusLastOption();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (key === 'PageUp') {
|
|
89
|
+
pageUp();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (key === 'PageDown') {
|
|
93
|
+
pageDown();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Do nothing
|
|
98
|
+
}, [closeMenu, options, value, loading, focussedOptionIndex, selectFocussedOption, focusComboBox, focusNextOption, focusPrevOption, focusFirstOption, focusLastOption, pageDown, pageUp]);
|
|
99
|
+
return handleKeyPress;
|
|
100
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
const TYPING_DEBOUNCE_TIME = 300; // ms
|
|
3
|
+
|
|
4
|
+
export function useHandleTyping(_ref) {
|
|
5
|
+
let {
|
|
6
|
+
expanded,
|
|
7
|
+
options,
|
|
8
|
+
setFocussedOptionIndex,
|
|
9
|
+
onChange
|
|
10
|
+
} = _ref;
|
|
11
|
+
const [value, setValue] = useState('');
|
|
12
|
+
const [typing, setTyping] = useState(false);
|
|
13
|
+
|
|
14
|
+
// This will reset the typed value after a given time
|
|
15
|
+
const timeoutRef = useRef();
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (timeoutRef.current) {
|
|
18
|
+
clearTimeout(timeoutRef.current);
|
|
19
|
+
timeoutRef.current = null;
|
|
20
|
+
}
|
|
21
|
+
if (value) {
|
|
22
|
+
timeoutRef.current = setTimeout(() => {
|
|
23
|
+
setValue('');
|
|
24
|
+
setTyping(false);
|
|
25
|
+
}, TYPING_DEBOUNCE_TIME);
|
|
26
|
+
} else {
|
|
27
|
+
setTyping(false);
|
|
28
|
+
}
|
|
29
|
+
}, [
|
|
30
|
+
// adding value to the dependencies array so that the hooks runs every time the value changes
|
|
31
|
+
value]);
|
|
32
|
+
|
|
33
|
+
// This will focus the first option with a label starting with the typed sequence
|
|
34
|
+
const prevValueRef = useRef();
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (value && value !== prevValueRef.current) {
|
|
37
|
+
// We only want to do this when the value changed
|
|
38
|
+
prevValueRef.current = value;
|
|
39
|
+
const optionIndex = options.findIndex(_ref2 => {
|
|
40
|
+
let {
|
|
41
|
+
disabled,
|
|
42
|
+
label
|
|
43
|
+
} = _ref2;
|
|
44
|
+
return !disabled && label.toLowerCase().startsWith(value.toLowerCase());
|
|
45
|
+
});
|
|
46
|
+
if (optionIndex !== -1) {
|
|
47
|
+
if (expanded) {
|
|
48
|
+
setFocussedOptionIndex(optionIndex);
|
|
49
|
+
} else {
|
|
50
|
+
const nextSelectedOption = options[optionIndex];
|
|
51
|
+
onChange({
|
|
52
|
+
label: nextSelectedOption.label,
|
|
53
|
+
value: nextSelectedOption.value
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}, [value, options, setFocussedOptionIndex, expanded, onChange]);
|
|
59
|
+
const onTyping = useCallback(key => {
|
|
60
|
+
setTyping(true);
|
|
61
|
+
if (key === 'Backspace') {
|
|
62
|
+
setValue(prevValue => prevValue.slice(0, -1));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (key === 'Clear') {
|
|
66
|
+
setValue('');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
setValue(prevValue => `${prevValue}${key}`);
|
|
70
|
+
}, []);
|
|
71
|
+
return {
|
|
72
|
+
onTyping,
|
|
73
|
+
typing
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
export function useHighlightFirstOptionOnPreviousPage(_ref) {
|
|
3
|
+
let {
|
|
4
|
+
options,
|
|
5
|
+
focussedOptionIndex,
|
|
6
|
+
setFocussedOptionIndex,
|
|
7
|
+
listBoxRef
|
|
8
|
+
} = _ref;
|
|
9
|
+
return useCallback(() => {
|
|
10
|
+
const listBoxParent = listBoxRef.current.parentNode;
|
|
11
|
+
const optionElements = Array.from(listBoxRef.current.childNodes);
|
|
12
|
+
const visibleOptionsAmount = Math.floor(listBoxParent.offsetHeight / optionElements[0].offsetHeight);
|
|
13
|
+
const nextTopOptionIndex = Math.max(0, focussedOptionIndex - visibleOptionsAmount);
|
|
14
|
+
|
|
15
|
+
// If there's no next option and we already have the last option in the list highlighted
|
|
16
|
+
if (!options[nextTopOptionIndex]) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!options[nextTopOptionIndex].disabled) {
|
|
20
|
+
const nextTopOption = optionElements[nextTopOptionIndex];
|
|
21
|
+
const scrollPosition = nextTopOption.offsetTop;
|
|
22
|
+
listBoxParent.scrollTop = scrollPosition;
|
|
23
|
+
setFocussedOptionIndex(nextTopOptionIndex);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const lowerEnabledOptionIndex = options.slice(0, nextTopOptionIndex).findLastIndex(option => !option.disabled);
|
|
27
|
+
if (lowerEnabledOptionIndex !== -1) {
|
|
28
|
+
const lowerEnabledOption = optionElements[lowerEnabledOptionIndex];
|
|
29
|
+
const lowerScrollPosition = lowerEnabledOption.offsetTop;
|
|
30
|
+
listBoxParent.scrollTop = lowerScrollPosition;
|
|
31
|
+
setFocussedOptionIndex(lowerEnabledOptionIndex);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const inbetweenEnabledOptionIndex = nextTopOptionIndex + options.slice(nextTopOptionIndex, focussedOptionIndex).findIndex(option => !option.disabled);
|
|
35
|
+
if (inbetweenEnabledOptionIndex === -1) {
|
|
36
|
+
// We're already on the first enabled option
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const inbetweenTopOption = optionElements[inbetweenEnabledOptionIndex];
|
|
40
|
+
const scrollPosition = inbetweenTopOption.offsetTop;
|
|
41
|
+
listBoxParent.scrollTop = scrollPosition;
|
|
42
|
+
setFocussedOptionIndex(inbetweenEnabledOptionIndex);
|
|
43
|
+
}, [options, focussedOptionIndex, setFocussedOptionIndex, listBoxRef]);
|
|
44
|
+
}
|
package/build/es/simple-single-select/use-handle-key-press/use-highlight-first-visible-option.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
export function useHighlightFirstVisibleOption(_ref) {
|
|
3
|
+
let {
|
|
4
|
+
options,
|
|
5
|
+
focussedOptionIndex,
|
|
6
|
+
setFocussedOptionIndex
|
|
7
|
+
} = _ref;
|
|
8
|
+
return useCallback(lowestVisibleIndex => {
|
|
9
|
+
if (!options[lowestVisibleIndex].disabled) {
|
|
10
|
+
setFocussedOptionIndex(lowestVisibleIndex);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const previousEnabledOptionIndex = options.slice(0, lowestVisibleIndex).findLastIndex(option => !option.disabled);
|
|
14
|
+
if (previousEnabledOptionIndex !== -1) {
|
|
15
|
+
setFocussedOptionIndex(previousEnabledOptionIndex);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const nextEnabledOptionIndex = lowestVisibleIndex + options.slice(lowestVisibleIndex, focussedOptionIndex).findIndex(option => !option.disabled);
|
|
19
|
+
if (nextEnabledOptionIndex !== -1) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
setFocussedOptionIndex(nextEnabledOptionIndex);
|
|
23
|
+
}, [options, focussedOptionIndex, setFocussedOptionIndex]);
|
|
24
|
+
}
|
package/build/es/simple-single-select/use-handle-key-press/use-highlight-last-option-on-next-page.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
export function useHighlightLastOptionOnNextPage(_ref) {
|
|
3
|
+
let {
|
|
4
|
+
options,
|
|
5
|
+
focussedOptionIndex,
|
|
6
|
+
setFocussedOptionIndex,
|
|
7
|
+
listBoxRef
|
|
8
|
+
} = _ref;
|
|
9
|
+
return useCallback(() => {
|
|
10
|
+
const listBoxParent = listBoxRef.current.parentNode;
|
|
11
|
+
const optionElements = Array.from(listBoxRef.current.childNodes);
|
|
12
|
+
const visibleOptionsAmount = Math.floor(listBoxParent.offsetHeight / optionElements[0].offsetHeight);
|
|
13
|
+
const nextHighlightedOptionIndex = Math.min(options.length - 1, focussedOptionIndex + visibleOptionsAmount);
|
|
14
|
+
|
|
15
|
+
// If there's no next option and we already have the last option in the list highlighted
|
|
16
|
+
if (!options[nextHighlightedOptionIndex]) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!options[nextHighlightedOptionIndex].disabled) {
|
|
20
|
+
// This will be the first option in the list
|
|
21
|
+
const {
|
|
22
|
+
offsetTop: scrollPosition
|
|
23
|
+
} = optionElements[nextHighlightedOptionIndex - visibleOptionsAmount + 1];
|
|
24
|
+
listBoxParent.scrollTop = scrollPosition;
|
|
25
|
+
setFocussedOptionIndex(nextHighlightedOptionIndex);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const followingEnabledOptionIndex = nextHighlightedOptionIndex + options.slice(nextHighlightedOptionIndex).findIndex(option => !option.disabled);
|
|
29
|
+
|
|
30
|
+
// There is no enabled option after the disabled option that's at the end of the next page
|
|
31
|
+
// So we stay where we are
|
|
32
|
+
if (followingEnabledOptionIndex === -1) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// There is an enabled option after the disabled option that's at the end of the next page
|
|
37
|
+
// So that'll be the new highlighted option and the bottom of the next displayed page
|
|
38
|
+
const {
|
|
39
|
+
offsetTop: adjustedScrollPosition
|
|
40
|
+
} = optionElements[followingEnabledOptionIndex - visibleOptionsAmount + 1];
|
|
41
|
+
listBoxParent.scrollTop = adjustedScrollPosition;
|
|
42
|
+
setFocussedOptionIndex(followingEnabledOptionIndex);
|
|
43
|
+
}, [options, focussedOptionIndex, setFocussedOptionIndex, listBoxRef]);
|
|
44
|
+
}
|
package/build/es/simple-single-select/use-handle-key-press/use-highlight-last-visible-option.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
export function useHighlightLastVisibleOption(_ref) {
|
|
3
|
+
let {
|
|
4
|
+
options,
|
|
5
|
+
focussedOptionIndex,
|
|
6
|
+
setFocussedOptionIndex
|
|
7
|
+
} = _ref;
|
|
8
|
+
return useCallback(highestVisibleIndex => {
|
|
9
|
+
if (!options[highestVisibleIndex].disabled) {
|
|
10
|
+
setFocussedOptionIndex(highestVisibleIndex);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Last option on page is disabled, find next option that's not disabled
|
|
15
|
+
const followingEnabledOptionIndex = options.slice(highestVisibleIndex).findIndex(option => !option.disabled);
|
|
16
|
+
if (followingEnabledOptionIndex >= 0) {
|
|
17
|
+
// We need to add the highest visible index because the index is lower due to slicing the array
|
|
18
|
+
setFocussedOptionIndex(followingEnabledOptionIndex + highestVisibleIndex);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// No following enabled option, trying to find the closest previous sibling of the last option on the current page
|
|
23
|
+
const closestToEndOfPageEnabledOptionIndex = options.slice(
|
|
24
|
+
// We don't include the currently highlighted option
|
|
25
|
+
focussedOptionIndex + 1, highestVisibleIndex).findLastIndex(option => !option.disabled);
|
|
26
|
+
if (closestToEndOfPageEnabledOptionIndex >= 0) {
|
|
27
|
+
setFocussedOptionIndex(closestToEndOfPageEnabledOptionIndex +
|
|
28
|
+
// We need to add the focused index and 1 because the index is lower due to slicing the array
|
|
29
|
+
focussedOptionIndex + 1);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// The currently highlighted option is the last enabled option
|
|
34
|
+
return;
|
|
35
|
+
}, [options, focussedOptionIndex, setFocussedOptionIndex]);
|
|
36
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { isOptionHidden } from '../is-option-hidden.js';
|
|
3
|
+
import { useHighlightLastOptionOnNextPage } from './use-highlight-last-option-on-next-page.js';
|
|
4
|
+
import { useHighlightLastVisibleOption } from './use-highlight-last-visible-option.js';
|
|
5
|
+
export function usePageDown(_ref) {
|
|
6
|
+
let {
|
|
7
|
+
options,
|
|
8
|
+
focussedOptionIndex,
|
|
9
|
+
setFocussedOptionIndex,
|
|
10
|
+
listBoxRef
|
|
11
|
+
} = _ref;
|
|
12
|
+
const highlightLastVisibleOption = useHighlightLastVisibleOption({
|
|
13
|
+
options,
|
|
14
|
+
focussedOptionIndex,
|
|
15
|
+
setFocussedOptionIndex
|
|
16
|
+
});
|
|
17
|
+
const highlightLastOptionOnNextPage = useHighlightLastOptionOnNextPage({
|
|
18
|
+
options,
|
|
19
|
+
focussedOptionIndex,
|
|
20
|
+
setFocussedOptionIndex,
|
|
21
|
+
listBoxRef
|
|
22
|
+
});
|
|
23
|
+
return useCallback(() => {
|
|
24
|
+
const listBoxParent = listBoxRef.current.parentNode;
|
|
25
|
+
const options = Array.from(listBoxRef.current.childNodes);
|
|
26
|
+
const highestVisibleIndex = options.findLastIndex(option => !isOptionHidden(option, listBoxParent));
|
|
27
|
+
if (highestVisibleIndex > focussedOptionIndex) {
|
|
28
|
+
highlightLastVisibleOption(highestVisibleIndex);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (highestVisibleIndex > -1) {
|
|
32
|
+
highlightLastOptionOnNextPage();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// No visible option (e.g. when menu is empty)
|
|
37
|
+
return;
|
|
38
|
+
}, [focussedOptionIndex, listBoxRef, highlightLastVisibleOption, highlightLastOptionOnNextPage]);
|
|
39
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { isOptionHidden } from '../is-option-hidden.js';
|
|
3
|
+
import { useHighlightFirstOptionOnPreviousPage } from './use-highlight-first-option-on-previous-page.js';
|
|
4
|
+
import { useHighlightFirstVisibleOption } from './use-highlight-first-visible-option.js';
|
|
5
|
+
export function usePageUp(_ref) {
|
|
6
|
+
let {
|
|
7
|
+
options,
|
|
8
|
+
listBoxRef,
|
|
9
|
+
focussedOptionIndex,
|
|
10
|
+
setFocussedOptionIndex
|
|
11
|
+
} = _ref;
|
|
12
|
+
const highlightFirstVisibleOption = useHighlightFirstVisibleOption({
|
|
13
|
+
options,
|
|
14
|
+
focussedOptionIndex,
|
|
15
|
+
setFocussedOptionIndex
|
|
16
|
+
});
|
|
17
|
+
const highlightFirstOptionOnPreviousPage = useHighlightFirstOptionOnPreviousPage({
|
|
18
|
+
options,
|
|
19
|
+
focussedOptionIndex,
|
|
20
|
+
setFocussedOptionIndex,
|
|
21
|
+
listBoxRef
|
|
22
|
+
});
|
|
23
|
+
return useCallback(() => {
|
|
24
|
+
const listBoxParent = listBoxRef.current.parentNode;
|
|
25
|
+
const optionElements = Array.from(listBoxRef.current.childNodes);
|
|
26
|
+
const lowestVisibleIndex = optionElements.findIndex(optionElement => !isOptionHidden(optionElement, listBoxParent));
|
|
27
|
+
|
|
28
|
+
// No visible option (e.g. when menu is empty)
|
|
29
|
+
if (lowestVisibleIndex === -1) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (lowestVisibleIndex < focussedOptionIndex) {
|
|
33
|
+
highlightFirstVisibleOption(lowestVisibleIndex);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
highlightFirstOptionOnPreviousPage();
|
|
37
|
+
}, [focussedOptionIndex, listBoxRef, highlightFirstOptionOnPreviousPage, highlightFirstVisibleOption]);
|
|
38
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function isEnabled(_ref) {
|
|
2
|
+
let {
|
|
3
|
+
disabled
|
|
4
|
+
} = _ref;
|
|
5
|
+
return !disabled;
|
|
6
|
+
}
|
|
7
|
+
export function findNextOptionIndex(_ref2) {
|
|
8
|
+
let {
|
|
9
|
+
options,
|
|
10
|
+
activeIndex
|
|
11
|
+
} = _ref2;
|
|
12
|
+
const startIndex = activeIndex + 1;
|
|
13
|
+
const optionsToSearch = options.slice(startIndex);
|
|
14
|
+
|
|
15
|
+
// Need to add back the count we removed by slicing the options array
|
|
16
|
+
return startIndex + optionsToSearch.findIndex(isEnabled);
|
|
17
|
+
}
|
|
18
|
+
export function findPrevOptionIndex(_ref3) {
|
|
19
|
+
let {
|
|
20
|
+
options,
|
|
21
|
+
activeIndex
|
|
22
|
+
} = _ref3;
|
|
23
|
+
return options.slice(0, activeIndex).findLastIndex(isEnabled);
|
|
24
|
+
}
|
|
25
|
+
export function findFirstOptionIndex(_ref4) {
|
|
26
|
+
let {
|
|
27
|
+
options
|
|
28
|
+
} = _ref4;
|
|
29
|
+
return options.findIndex(isEnabled);
|
|
30
|
+
}
|
|
31
|
+
export function findLastOptionIndex(_ref5) {
|
|
32
|
+
let {
|
|
33
|
+
options
|
|
34
|
+
} = _ref5;
|
|
35
|
+
return options.findLastIndex(isEnabled);
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SimpleSingleSelectField } from './simple-single-select-field.js';
|