@digigov/ui 2.0.0-cbc56209 → 2.0.0-fec6f1f7
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/cjs/content/SafeHTML/index.js +5 -8
- package/cjs/form/AutoComplete/AutoComplete.stories/index.js +44 -8
- package/cjs/form/AutoComplete/AutoComplete.stories.playwright.json +33 -0
- package/cjs/form/AutoComplete/Status/index.js +12 -5
- package/cjs/form/AutoComplete/__stories__/Multiple/index.js +24 -0
- package/cjs/form/AutoComplete/__stories__/{WithInLine → MultipleShowingTwoSelected}/index.js +7 -6
- package/cjs/form/AutoComplete/__stories__/MultipleWithDefaultValues/index.js +23 -0
- package/cjs/form/AutoComplete/__stories__/MultipleWithMinLength/index.js +26 -0
- package/cjs/form/AutoComplete/__stories__/WithShowAllValues/index.js +0 -1
- package/cjs/form/AutoComplete/index.js +312 -202
- package/cjs/form/AutoComplete/index.mdx +0 -8
- package/cjs/form/AutoComplete/utils/index.js +2 -1
- package/cjs/govgr/Footer/index.mdx +0 -2
- package/cjs/i18n/locales/el/index.js +2 -2
- package/cjs/i18n/locales/en/index.js +2 -2
- package/cjs/lazy/index.js +26 -8
- package/cjs/navigation/Dropdown/Dropdown.stories/index.js +12 -0
- package/cjs/navigation/Dropdown/Dropdown.stories.playwright.json +16 -0
- package/cjs/navigation/Dropdown/__stories__/ScrollableContent/index.js +68 -0
- package/content/SafeHTML/index.d.ts +1 -1
- package/content/SafeHTML/index.js +5 -8
- package/form/AutoComplete/AutoComplete.stories/index.js +4 -1
- package/form/AutoComplete/AutoComplete.stories.d.ts +4 -1
- package/form/AutoComplete/AutoComplete.stories.playwright.json +33 -0
- package/form/AutoComplete/Status/index.d.ts +4 -2
- package/form/AutoComplete/Status/index.js +11 -4
- package/form/AutoComplete/__stories__/Multiple/index.js +17 -0
- package/form/AutoComplete/__stories__/{WithInLine → Multiple}/package.json +1 -1
- package/form/AutoComplete/__stories__/Multiple.d.ts +3 -0
- package/form/AutoComplete/__stories__/{WithInLine → MultipleShowingTwoSelected}/index.js +6 -5
- package/form/AutoComplete/__stories__/MultipleShowingTwoSelected/package.json +6 -0
- package/form/AutoComplete/__stories__/MultipleShowingTwoSelected.d.ts +3 -0
- package/form/AutoComplete/__stories__/MultipleWithDefaultValues/index.js +16 -0
- package/form/AutoComplete/__stories__/MultipleWithDefaultValues/package.json +6 -0
- package/form/AutoComplete/__stories__/MultipleWithDefaultValues.d.ts +3 -0
- package/form/AutoComplete/__stories__/MultipleWithMinLength/index.js +19 -0
- package/form/AutoComplete/__stories__/MultipleWithMinLength/package.json +6 -0
- package/form/AutoComplete/__stories__/MultipleWithMinLength.d.ts +3 -0
- package/form/AutoComplete/__stories__/WithShowAllValues/index.js +0 -1
- package/form/AutoComplete/index.d.ts +12 -18
- package/form/AutoComplete/index.js +295 -197
- package/form/AutoComplete/index.mdx +0 -8
- package/form/AutoComplete/utils/index.js +2 -1
- package/form/AutoComplete/utils.d.ts +1 -0
- package/govgr/Footer/index.mdx +0 -2
- package/i18n/locales/el/index.js +2 -2
- package/i18n/locales/en/index.js +2 -2
- package/index.js +1 -1
- package/lazy/index.js +22 -8
- package/lazy.d.ts +8 -6
- package/navigation/Dropdown/Dropdown.stories/index.js +1 -0
- package/navigation/Dropdown/Dropdown.stories.d.ts +1 -0
- package/navigation/Dropdown/Dropdown.stories.playwright.json +16 -0
- package/navigation/Dropdown/__stories__/ScrollableContent/index.js +61 -0
- package/navigation/Dropdown/__stories__/ScrollableContent/package.json +6 -0
- package/navigation/Dropdown/__stories__/ScrollableContent.d.ts +3 -0
- package/navigation/NavList/types.d.ts +1 -2
- package/package.json +4 -6
- package/src/content/SafeHTML/index.tsx +3 -4
- package/src/form/AutoComplete/AutoComplete.stories.js +4 -1
- package/src/form/AutoComplete/AutoComplete.stories.playwright.json +33 -0
- package/src/form/AutoComplete/Status/index.tsx +10 -6
- package/src/form/AutoComplete/__stories__/Multiple.tsx +18 -0
- package/src/form/AutoComplete/__stories__/{WithInLine.tsx → MultipleShowingTwoSelected.tsx} +3 -8
- package/src/form/AutoComplete/__stories__/MultipleWithDefaultValues.tsx +16 -0
- package/src/form/AutoComplete/__stories__/MultipleWithMinLength.tsx +29 -0
- package/src/form/AutoComplete/__stories__/WithShowAllValues.tsx +0 -1
- package/src/form/AutoComplete/index.mdx +0 -8
- package/src/form/AutoComplete/index.tsx +351 -213
- package/src/form/AutoComplete/utils.ts +3 -2
- package/src/govgr/Footer/index.mdx +0 -2
- package/src/i18n/locales/el.ts +2 -2
- package/src/i18n/locales/en.ts +2 -2
- package/src/lazy.js +4 -2
- package/src/navigation/Dropdown/Dropdown.stories.js +1 -0
- package/src/navigation/Dropdown/Dropdown.stories.playwright.json +16 -0
- package/src/navigation/Dropdown/__stories__/ScrollableContent.tsx +82 -0
- package/src/navigation/NavList/types.tsx +1 -2
- package/form/AutoComplete/__stories__/WithInLine.d.ts +0 -3
|
@@ -5,7 +5,9 @@ import AutoCompleteInput from '@digigov/react-core/AutoCompleteInput';
|
|
|
5
5
|
import AutoCompleteInputTypeahead from '@digigov/react-core/AutoCompleteInputTypeahead';
|
|
6
6
|
import AutoCompleteResultList from '@digigov/react-core/AutoCompleteResultList';
|
|
7
7
|
import AutoCompleteResultListItem from '@digigov/react-core/AutoCompleteResultListItem';
|
|
8
|
-
import
|
|
8
|
+
import AutoCompleteContainer from '@digigov/react-core/AutoCompleteContainer';
|
|
9
|
+
import AutoCompleteMultipleInputContainer from '@digigov/react-core/AutoCompleteMultipleInputContainer';
|
|
10
|
+
import AutoCompleteMultipleInput from '@digigov/react-core/AutoCompleteMultipleInput';
|
|
9
11
|
|
|
10
12
|
import { CaretIcon } from '@digigov/react-icons/CaretIcon';
|
|
11
13
|
import Status from '@digigov/ui/form/AutoComplete/Status';
|
|
@@ -14,10 +16,15 @@ import {
|
|
|
14
16
|
keyCodes,
|
|
15
17
|
isPrintableKeyCode,
|
|
16
18
|
} from '@digigov/ui/form/AutoComplete/utils';
|
|
19
|
+
import Checkbox, { CheckboxItem } from '../Checkbox';
|
|
20
|
+
import { Chip, ChipContainer } from '@digigov/ui/content/Chip';
|
|
21
|
+
import Button from '@digigov/ui/form/Button';
|
|
22
|
+
import { NormalText } from '@digigov/ui/typography/NormalText';
|
|
17
23
|
|
|
18
24
|
export interface AutoCompleteProps {
|
|
19
25
|
source: (query: string, syncResults: (options: string[]) => void) => void;
|
|
20
26
|
id: string;
|
|
27
|
+
multiple?: boolean;
|
|
21
28
|
tStatusResults?: (x: number, y: string) => string;
|
|
22
29
|
tStatusNoResults?: () => string;
|
|
23
30
|
tStatusQueryTooShort?: (x: number) => string;
|
|
@@ -25,22 +32,19 @@ export interface AutoCompleteProps {
|
|
|
25
32
|
tNoResults?: () => string;
|
|
26
33
|
tAssistiveHint?: () => string;
|
|
27
34
|
templates?: {
|
|
28
|
-
suggestion?: (value: any) => any;
|
|
35
|
+
suggestion?: (value: any, query: string | string[]) => any;
|
|
29
36
|
inputValue?: (value: any) => string;
|
|
30
37
|
};
|
|
31
38
|
width?: '25%' | '33.3%' | '50%' | '66.6%' | '75%' | '100%' | 'full';
|
|
32
39
|
autoselect?: boolean;
|
|
33
|
-
defaultValue?: string;
|
|
34
|
-
displayMenu?: 'overlay' | 'inline';
|
|
40
|
+
defaultValue?: string | string[];
|
|
35
41
|
minLength?: number;
|
|
36
42
|
name?: string;
|
|
37
43
|
placeholder?: string;
|
|
38
44
|
onConfirm?: (x: any) => void;
|
|
39
45
|
confirmOnBlur?: boolean;
|
|
40
|
-
showNoOptionsFound?: boolean;
|
|
41
|
-
showAllValues?: boolean;
|
|
42
46
|
required?: boolean;
|
|
43
|
-
|
|
47
|
+
numberOfSelected?: 1 | 2 | 3 | 'all';
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
export type State = {
|
|
@@ -49,6 +53,7 @@ export type State = {
|
|
|
49
53
|
menuOpen: boolean;
|
|
50
54
|
options: string[];
|
|
51
55
|
query: string;
|
|
56
|
+
selectedValues: string[];
|
|
52
57
|
validChoiceMade: boolean;
|
|
53
58
|
selected: any;
|
|
54
59
|
ariaHint: boolean;
|
|
@@ -58,10 +63,8 @@ export type NewQueryProps = { menuOpen?: boolean; query?: string };
|
|
|
58
63
|
|
|
59
64
|
export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
60
65
|
static defaultProps = {
|
|
61
|
-
width: '
|
|
66
|
+
width: 'full',
|
|
62
67
|
autoselect: false,
|
|
63
|
-
defaultValue: '',
|
|
64
|
-
displayMenu: 'overlay',
|
|
65
68
|
minLength: 0,
|
|
66
69
|
name: 'ds-input-autocomplete',
|
|
67
70
|
placeholder: '',
|
|
@@ -69,15 +72,10 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
69
72
|
return;
|
|
70
73
|
},
|
|
71
74
|
confirmOnBlur: false,
|
|
72
|
-
showNoOptionsFound: true,
|
|
73
|
-
showAllValues: false,
|
|
74
75
|
required: false,
|
|
75
76
|
tNoResults: (): string => 'No results found',
|
|
76
77
|
tAssistiveHint: (): string =>
|
|
77
78
|
'When autocomplete results are available use up and down arrows to review and enter to select. Touch device users, explore by touch or with swipe gestures.',
|
|
78
|
-
dropdownArrow: (props) => (
|
|
79
|
-
<CaretIcon direction="down" size="sm" {...props} />
|
|
80
|
-
),
|
|
81
79
|
};
|
|
82
80
|
|
|
83
81
|
elementReferences = {};
|
|
@@ -85,19 +83,27 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
85
83
|
|
|
86
84
|
constructor(props: AutoCompleteProps) {
|
|
87
85
|
super(props);
|
|
86
|
+
const customState: any = {};
|
|
87
|
+
if (Array.isArray(props.defaultValue)) {
|
|
88
|
+
customState.selectedValues = props.defaultValue as string[];
|
|
89
|
+
} else if (props.defaultValue) {
|
|
90
|
+
customState.selectedValues = [props.defaultValue as string];
|
|
91
|
+
}
|
|
88
92
|
|
|
89
93
|
this.state = {
|
|
90
94
|
focused: null,
|
|
91
95
|
hovered: null,
|
|
92
96
|
menuOpen: false,
|
|
93
|
-
options:
|
|
94
|
-
query: props.defaultValue as string,
|
|
97
|
+
options: [],
|
|
98
|
+
query: this.props.multiple ? '' : (props.defaultValue as string) || '',
|
|
99
|
+
selectedValues: [],
|
|
95
100
|
validChoiceMade: false,
|
|
96
101
|
selected: null,
|
|
97
102
|
ariaHint: true,
|
|
103
|
+
...customState,
|
|
98
104
|
};
|
|
99
|
-
|
|
100
105
|
this.handleComponentBlur = this.handleComponentBlur.bind(this);
|
|
106
|
+
this.handleAutoCompleteBlur = this.handleAutoCompleteBlur.bind(this);
|
|
101
107
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
|
102
108
|
this.handleUpArrow = this.handleUpArrow.bind(this);
|
|
103
109
|
this.handleDownArrow = this.handleDownArrow.bind(this);
|
|
@@ -106,17 +112,18 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
106
112
|
|
|
107
113
|
this.handleListMouseLeave = this.handleListMouseLeave.bind(this);
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
if (this.props.multiple) {
|
|
116
|
+
this.handleOptionClick = this.handleOptionClickMultiple.bind(this);
|
|
117
|
+
} else {
|
|
118
|
+
this.handleOptionClick = this.handleOptionClick.bind(this);
|
|
119
|
+
}
|
|
111
120
|
this.handleOptionFocus = this.handleOptionFocus.bind(this);
|
|
112
121
|
this.handleOptionMouseDown = this.handleOptionMouseDown.bind(this);
|
|
113
122
|
this.handleOptionMouseEnter = this.handleOptionMouseEnter.bind(this);
|
|
114
123
|
|
|
115
|
-
this.handleInputBlur = this.handleInputBlur.bind(this);
|
|
116
124
|
this.handleInputChange = this.handleInputChange.bind(this);
|
|
117
125
|
this.handleInputFocus = this.handleInputFocus.bind(this);
|
|
118
126
|
|
|
119
|
-
this.pollInputElement = this.pollInputElement.bind(this);
|
|
120
127
|
this.getDirectInputChanges = this.getDirectInputChanges.bind(this);
|
|
121
128
|
}
|
|
122
129
|
|
|
@@ -129,29 +136,14 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
129
136
|
}
|
|
130
137
|
|
|
131
138
|
componentDidMount(): void {
|
|
132
|
-
this.pollInputElement();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
componentWillUnmount(): void {
|
|
136
|
-
clearTimeout(this.$pollInput);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Applications like Dragon NaturallySpeaking will modify the
|
|
140
|
-
// `input` field by directly changing its `.value`. These events
|
|
141
|
-
// don't trigger our JavaScript event listeners, so we need to poll
|
|
142
|
-
// to handle when and if they occur.
|
|
143
|
-
pollInputElement(): void {
|
|
144
139
|
this.getDirectInputChanges();
|
|
145
|
-
|
|
146
|
-
this.pollInputElement();
|
|
147
|
-
}, 100);
|
|
140
|
+
|
|
148
141
|
}
|
|
149
142
|
|
|
150
143
|
getDirectInputChanges(): void {
|
|
151
144
|
const inputReference = this.elementReferences[-1];
|
|
152
145
|
const queryHasChanged =
|
|
153
|
-
inputReference && inputReference.value !== this.state.query;
|
|
154
|
-
|
|
146
|
+
inputReference && inputReference.value !== (this.state.query ?? '');
|
|
155
147
|
if (queryHasChanged) {
|
|
156
148
|
this.handleInputChange({ target: { value: inputReference.value } });
|
|
157
149
|
}
|
|
@@ -191,21 +183,24 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
191
183
|
templateSuggestion(value: string): string {
|
|
192
184
|
const suggestionTemplate =
|
|
193
185
|
this.props.templates && this.props.templates.suggestion;
|
|
194
|
-
|
|
186
|
+
|
|
187
|
+
return suggestionTemplate
|
|
188
|
+
? suggestionTemplate(value, this.state.query)
|
|
189
|
+
: value;
|
|
195
190
|
}
|
|
196
191
|
|
|
197
|
-
handleComponentBlur(newState
|
|
192
|
+
handleComponentBlur(newState?: NewQueryProps): void {
|
|
198
193
|
const { options, query, selected } = this.state;
|
|
199
194
|
let newQuery: string;
|
|
200
195
|
if (this.props.confirmOnBlur) {
|
|
201
|
-
newQuery = newState
|
|
196
|
+
newQuery = newState?.query || query;
|
|
202
197
|
this.props.onConfirm?.(options[selected as number]);
|
|
203
198
|
} else {
|
|
204
199
|
newQuery = query;
|
|
205
200
|
}
|
|
206
201
|
this.setState({
|
|
207
202
|
focused: null,
|
|
208
|
-
menuOpen: newState
|
|
203
|
+
menuOpen: newState?.menuOpen || false,
|
|
209
204
|
query: newQuery,
|
|
210
205
|
selected: null,
|
|
211
206
|
validChoiceMade: this.isQueryAnOption(newQuery, options),
|
|
@@ -218,72 +213,60 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
218
213
|
});
|
|
219
214
|
}
|
|
220
215
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
216
|
+
handleAutoCompleteBlur(event, index?: number): void {
|
|
217
|
+
const { focused, options, query, selected } = this.state;
|
|
218
|
+
let focusingOutsideComponent = false;
|
|
219
|
+
if (event.relatedTarget === null) {
|
|
220
|
+
focusingOutsideComponent = true;
|
|
221
|
+
} else {
|
|
222
|
+
// This affects if we have multiple autocompletes in same page and we click the first button-arrow and then another button-arrow
|
|
223
|
+
focusingOutsideComponent =
|
|
224
|
+
this.elementReferences['button-arrow'] !== event.relatedTarget;
|
|
225
|
+
}
|
|
227
226
|
const focusingInput = event.relatedTarget === this.elementReferences[-1];
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const keepMenuOpen = menuOpen && isIosDevice();
|
|
234
|
-
this.handleComponentBlur({
|
|
235
|
-
menuOpen: keepMenuOpen,
|
|
236
|
-
query: this.templateInputValue(options[selected as number]),
|
|
237
|
-
});
|
|
227
|
+
const focusingButtonArrow =
|
|
228
|
+
this.elementReferences['button-arrow'] === event.relatedTarget || false;
|
|
229
|
+
let focusingAnotherOption = focused !== null && focused !== -1;
|
|
230
|
+
if (index !== undefined) {
|
|
231
|
+
focusingAnotherOption = focused !== index && focused !== -1;
|
|
238
232
|
}
|
|
239
|
-
|
|
233
|
+
// Check if the user clicks outside and not either on option, either on input or on button arrow
|
|
234
|
+
const blurComponent =
|
|
235
|
+
focusingOutsideComponent &&
|
|
236
|
+
!(focusingAnotherOption || focusingInput || focusingButtonArrow);
|
|
240
237
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const focusingAnOption = focused !== -1;
|
|
244
|
-
if (!focusingAnOption) {
|
|
245
|
-
const keepMenuOpen = menuOpen && isIosDevice();
|
|
238
|
+
if (blurComponent) {
|
|
239
|
+
// In handleInputBlur:
|
|
246
240
|
const newQuery = isIosDevice()
|
|
247
241
|
? query
|
|
248
242
|
: this.templateInputValue(options[selected as number]);
|
|
249
243
|
this.handleComponentBlur({
|
|
250
|
-
menuOpen:
|
|
244
|
+
menuOpen: false,
|
|
251
245
|
query: newQuery,
|
|
252
246
|
});
|
|
253
247
|
}
|
|
254
248
|
}
|
|
255
249
|
|
|
256
250
|
handleInputChange(event: { target: any }): void {
|
|
257
|
-
const {
|
|
251
|
+
const { source, minLength } = this.props;
|
|
258
252
|
const autoselect = this.hasAutoselect();
|
|
259
253
|
const query = event.target.value;
|
|
260
254
|
const queryEmpty = query.length === 0;
|
|
261
|
-
const queryChanged = this.state.query.length !== query.length;
|
|
262
|
-
const queryLongEnough = query.length >= (minLength as number);
|
|
263
|
-
|
|
264
255
|
this.setState({
|
|
265
256
|
query,
|
|
266
257
|
ariaHint: queryEmpty,
|
|
267
258
|
});
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
source(query, (options) => {
|
|
273
|
-
const optionsAvailable = options.length > 0;
|
|
274
|
-
this.setState({
|
|
275
|
-
menuOpen: optionsAvailable,
|
|
276
|
-
options,
|
|
277
|
-
selected: autoselect && optionsAvailable ? 0 : -1,
|
|
278
|
-
validChoiceMade: false,
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
} else if (queryEmpty || !queryLongEnough) {
|
|
259
|
+
const minLengthQuery =
|
|
260
|
+
minLength != undefined && minLength > query.length ? '' : query;
|
|
261
|
+
source(minLengthQuery, (options) => {
|
|
262
|
+
const optionsAvailable = options.length > 0;
|
|
282
263
|
this.setState({
|
|
283
|
-
menuOpen:
|
|
284
|
-
options
|
|
264
|
+
menuOpen: optionsAvailable,
|
|
265
|
+
options,
|
|
266
|
+
selected: autoselect && optionsAvailable ? 0 : -1,
|
|
267
|
+
validChoiceMade: false,
|
|
285
268
|
});
|
|
286
|
-
}
|
|
269
|
+
});
|
|
287
270
|
}
|
|
288
271
|
|
|
289
272
|
handleInputClick(
|
|
@@ -293,13 +276,13 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
293
276
|
}
|
|
294
277
|
|
|
295
278
|
handleInputFocus(): void {
|
|
279
|
+
const { source } = this.props;
|
|
296
280
|
const { query, validChoiceMade, options } = this.state;
|
|
297
281
|
const { minLength } = this.props;
|
|
298
282
|
const shouldReopenMenu =
|
|
299
283
|
!validChoiceMade &&
|
|
300
284
|
query.length >= (minLength as number) &&
|
|
301
285
|
options.length > 0;
|
|
302
|
-
|
|
303
286
|
if (shouldReopenMenu) {
|
|
304
287
|
this.setState(({ menuOpen }) => ({
|
|
305
288
|
focused: -1,
|
|
@@ -307,7 +290,12 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
307
290
|
selected: -1,
|
|
308
291
|
}));
|
|
309
292
|
} else {
|
|
310
|
-
|
|
293
|
+
source(query, (options) => {
|
|
294
|
+
this.setState({
|
|
295
|
+
focused: -1,
|
|
296
|
+
options,
|
|
297
|
+
});
|
|
298
|
+
});
|
|
311
299
|
}
|
|
312
300
|
}
|
|
313
301
|
|
|
@@ -329,15 +317,43 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
329
317
|
}
|
|
330
318
|
}
|
|
331
319
|
|
|
320
|
+
handleOptionClickMultiple(index: number): void {
|
|
321
|
+
const selectedOption = this.state.options[index];
|
|
322
|
+
let selectedValues = this.state.selectedValues;
|
|
323
|
+
if (this.state.selectedValues.indexOf(selectedOption) === -1) {
|
|
324
|
+
selectedValues = selectedValues.concat([selectedOption]);
|
|
325
|
+
} else {
|
|
326
|
+
selectedValues.splice(
|
|
327
|
+
this.state.selectedValues.indexOf(selectedOption),
|
|
328
|
+
1
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
this.props.onConfirm?.(selectedValues);
|
|
332
|
+
this.setState({
|
|
333
|
+
focused: index,
|
|
334
|
+
hovered: null,
|
|
335
|
+
menuOpen: true,
|
|
336
|
+
query: '',
|
|
337
|
+
selectedValues,
|
|
338
|
+
selected: index,
|
|
339
|
+
validChoiceMade: true,
|
|
340
|
+
});
|
|
341
|
+
this.forceUpdate();
|
|
342
|
+
}
|
|
343
|
+
|
|
332
344
|
handleOptionClick(index: number): void {
|
|
333
345
|
const selectedOption = this.state.options[index];
|
|
334
346
|
const newQuery = this.templateInputValue(selectedOption);
|
|
335
347
|
this.props.onConfirm?.(selectedOption);
|
|
348
|
+
const selectedValues = this.props.multiple
|
|
349
|
+
? this.state.selectedValues.concat(selectedOption)
|
|
350
|
+
: [selectedOption];
|
|
336
351
|
this.setState({
|
|
337
352
|
focused: -1,
|
|
338
353
|
hovered: null,
|
|
339
354
|
menuOpen: false,
|
|
340
355
|
query: newQuery,
|
|
356
|
+
selectedValues,
|
|
341
357
|
selected: -1,
|
|
342
358
|
validChoiceMade: true,
|
|
343
359
|
});
|
|
@@ -367,7 +383,7 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
367
383
|
handleDownArrow(event: { preventDefault: () => void }): void {
|
|
368
384
|
event.preventDefault();
|
|
369
385
|
// if not open, open
|
|
370
|
-
if (this.
|
|
386
|
+
if (this.state.menuOpen === false) {
|
|
371
387
|
event.preventDefault();
|
|
372
388
|
this.props.source('', (options) => {
|
|
373
389
|
this.setState({
|
|
@@ -390,11 +406,7 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
390
406
|
|
|
391
407
|
handleSpace(event: { preventDefault: () => void }): void {
|
|
392
408
|
// if not open, open
|
|
393
|
-
if (
|
|
394
|
-
this.props.showAllValues &&
|
|
395
|
-
this.state.menuOpen === false &&
|
|
396
|
-
this.state.query === ''
|
|
397
|
-
) {
|
|
409
|
+
if (this.state.menuOpen === false && this.state.query === '') {
|
|
398
410
|
event.preventDefault();
|
|
399
411
|
this.props.source('', (options) => {
|
|
400
412
|
this.setState({
|
|
@@ -416,6 +428,12 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
416
428
|
const hasSelectedOption = this.state.selected >= 0;
|
|
417
429
|
if (hasSelectedOption) {
|
|
418
430
|
this.handleOptionClick(this.state.selected);
|
|
431
|
+
if (this.props.multiple) {
|
|
432
|
+
this.setState({
|
|
433
|
+
menuOpen: false,
|
|
434
|
+
focused: -1,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
419
437
|
}
|
|
420
438
|
}
|
|
421
439
|
}
|
|
@@ -454,6 +472,16 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
454
472
|
query: this.state.query,
|
|
455
473
|
});
|
|
456
474
|
break;
|
|
475
|
+
case 'backspace':
|
|
476
|
+
if (this.props.multiple) {
|
|
477
|
+
if (this.state.query.length === 0) {
|
|
478
|
+
this.setState({
|
|
479
|
+
selectedValues: this.state.selectedValues.slice(0, -1),
|
|
480
|
+
});
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
457
485
|
default:
|
|
458
486
|
if (isPrintableKeyCode(event.keyCode)) {
|
|
459
487
|
this.handlePrintableKey(event);
|
|
@@ -466,44 +494,37 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
466
494
|
const {
|
|
467
495
|
id,
|
|
468
496
|
width,
|
|
469
|
-
displayMenu,
|
|
470
497
|
minLength,
|
|
471
498
|
name,
|
|
472
499
|
placeholder,
|
|
473
500
|
required,
|
|
474
|
-
showAllValues,
|
|
475
501
|
tNoResults,
|
|
476
502
|
tStatusQueryTooShort,
|
|
477
503
|
tStatusNoResults,
|
|
478
504
|
tStatusSelectedOption,
|
|
479
505
|
tStatusResults,
|
|
480
506
|
tAssistiveHint,
|
|
481
|
-
|
|
507
|
+
multiple,
|
|
508
|
+
source,
|
|
482
509
|
} = this.props;
|
|
483
510
|
const {
|
|
484
511
|
focused,
|
|
485
512
|
hovered,
|
|
486
513
|
menuOpen,
|
|
487
514
|
options,
|
|
488
|
-
query,
|
|
489
515
|
selected,
|
|
490
516
|
ariaHint,
|
|
491
517
|
validChoiceMade,
|
|
492
518
|
} = this.state;
|
|
493
519
|
const autoselect = this.hasAutoselect();
|
|
494
|
-
|
|
520
|
+
const query = this.state.query ?? '';
|
|
495
521
|
const inputFocused = focused === -1;
|
|
496
522
|
const noOptionsAvailable = options.length === 0;
|
|
497
523
|
const queryNotEmpty = query.length !== 0;
|
|
498
524
|
const queryLongEnough = query.length >= (minLength as number);
|
|
499
525
|
const showNoOptionsFound =
|
|
500
|
-
|
|
501
|
-
inputFocused &&
|
|
502
|
-
noOptionsAvailable &&
|
|
503
|
-
queryNotEmpty &&
|
|
504
|
-
queryLongEnough;
|
|
526
|
+
inputFocused && noOptionsAvailable && queryNotEmpty && queryLongEnough;
|
|
505
527
|
const componentIsFocused = focused !== null;
|
|
506
|
-
const dropdownArrowClassName = `ds-autocomplete__dropdown-arrow-down`;
|
|
507
528
|
const optionFocused = focused !== -1 && focused !== null;
|
|
508
529
|
const menuIsVisible = menuOpen || showNoOptionsFound;
|
|
509
530
|
const selectedOptionText = this.templateInputValue(options[selected]);
|
|
@@ -522,8 +543,74 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
522
543
|
}
|
|
523
544
|
: null;
|
|
524
545
|
|
|
546
|
+
const showAutoCompleteResultListItems = (
|
|
547
|
+
options: string[],
|
|
548
|
+
multiple: boolean
|
|
549
|
+
) => {
|
|
550
|
+
{
|
|
551
|
+
return options.map((option, index) => {
|
|
552
|
+
const showFocused =
|
|
553
|
+
focused === -1 ? selected === index : focused === index;
|
|
554
|
+
const iosPosinsetHtml = isIosDevice() ? (
|
|
555
|
+
<span
|
|
556
|
+
id={`${id}__option-suffix--${index}`}
|
|
557
|
+
style={{
|
|
558
|
+
border: '0',
|
|
559
|
+
clip: 'rect(0 0 0 0)',
|
|
560
|
+
height: '1px',
|
|
561
|
+
marginBottom: '-1px',
|
|
562
|
+
marginRight: '-1px',
|
|
563
|
+
overflow: 'hidden',
|
|
564
|
+
padding: '0',
|
|
565
|
+
position: 'absolute',
|
|
566
|
+
whiteSpace: 'nowrap',
|
|
567
|
+
width: '1px',
|
|
568
|
+
}}
|
|
569
|
+
>
|
|
570
|
+
{index + 1} από {options.length}
|
|
571
|
+
</span>
|
|
572
|
+
) : (
|
|
573
|
+
''
|
|
574
|
+
);
|
|
575
|
+
return (
|
|
576
|
+
<AutoCompleteResultListItem
|
|
577
|
+
isFocused={showFocused}
|
|
578
|
+
isHovered={hovered}
|
|
579
|
+
index={index}
|
|
580
|
+
aria-selected={focused === index ? 'true' : 'false'}
|
|
581
|
+
key={index}
|
|
582
|
+
onBlur={(event) => this.handleAutoCompleteBlur(event, index)}
|
|
583
|
+
onClick={() => this.handleOptionClick(index)}
|
|
584
|
+
onMouseDown={this.handleOptionMouseDown}
|
|
585
|
+
onMouseEnter={() => this.handleOptionMouseEnter(index)}
|
|
586
|
+
ref={(optionEl) => {
|
|
587
|
+
this.elementReferences[index] = optionEl;
|
|
588
|
+
}}
|
|
589
|
+
role="option"
|
|
590
|
+
tabIndex={-1}
|
|
591
|
+
aria-posinset={index + 1}
|
|
592
|
+
aria-setsize={options.length}
|
|
593
|
+
>
|
|
594
|
+
{multiple ? (
|
|
595
|
+
<CheckboxItem
|
|
596
|
+
value={option}
|
|
597
|
+
checked={this.state.selectedValues.includes(option)}
|
|
598
|
+
onChange={() => { }}
|
|
599
|
+
>
|
|
600
|
+
{this.templateSuggestion(option)}
|
|
601
|
+
</CheckboxItem>
|
|
602
|
+
) : (
|
|
603
|
+
this.templateSuggestion(option)
|
|
604
|
+
)}
|
|
605
|
+
{iosPosinsetHtml}
|
|
606
|
+
</AutoCompleteResultListItem>
|
|
607
|
+
);
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
525
612
|
return (
|
|
526
|
-
<
|
|
613
|
+
<AutoCompleteContainer onKeyDown={this.handleKeyDown} width={width}>
|
|
527
614
|
<Status
|
|
528
615
|
id={id}
|
|
529
616
|
length={options.length}
|
|
@@ -539,114 +626,163 @@ export default class AutoComplete extends Component<AutoCompleteProps, State> {
|
|
|
539
626
|
tResults={tStatusResults}
|
|
540
627
|
/>
|
|
541
628
|
|
|
542
|
-
{hintValue &&
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
629
|
+
{hintValue && (placeholder === '' || !placeholder) && (
|
|
630
|
+
<AutoCompleteInputTypeahead value={hintValue} />
|
|
631
|
+
)}
|
|
632
|
+
|
|
633
|
+
{multiple ? (
|
|
634
|
+
<AutoCompleteMultipleInputContainer
|
|
635
|
+
isFocused={componentIsFocused}
|
|
636
|
+
numberOfSelected={this.props.numberOfSelected}
|
|
637
|
+
>
|
|
638
|
+
<ChipContainer>
|
|
639
|
+
{this.state.selectedValues.map((value) => {
|
|
640
|
+
return (
|
|
641
|
+
<Chip
|
|
642
|
+
key={value}
|
|
643
|
+
onDelete={() => {
|
|
644
|
+
this.setState({
|
|
645
|
+
selectedValues: this.state.selectedValues.filter(
|
|
646
|
+
(v) => v !== value
|
|
647
|
+
),
|
|
648
|
+
});
|
|
649
|
+
}}
|
|
650
|
+
>
|
|
651
|
+
{value}
|
|
652
|
+
</Chip>
|
|
653
|
+
);
|
|
654
|
+
})}
|
|
655
|
+
{!componentIsFocused &&
|
|
656
|
+
typeof this.props.numberOfSelected === 'number' &&
|
|
657
|
+
this.state.selectedValues.length >
|
|
658
|
+
this.props.numberOfSelected && (
|
|
659
|
+
<NormalText fontWeight="bold" margin={0}>
|
|
660
|
+
+
|
|
661
|
+
{this.state.selectedValues.length -
|
|
662
|
+
this.props.numberOfSelected}
|
|
663
|
+
</NormalText>
|
|
664
|
+
)}
|
|
665
|
+
</ChipContainer>
|
|
666
|
+
<AutoCompleteMultipleInput
|
|
667
|
+
aria-expanded={menuOpen ? 'true' : 'false'}
|
|
668
|
+
aria-activedescendant={
|
|
669
|
+
optionFocused ? `${id}__option--${focused}` : ''
|
|
670
|
+
}
|
|
671
|
+
aria-owns={`${id}__listbox`}
|
|
672
|
+
aria-autocomplete={this.hasAutoselect() ? 'both' : 'list'}
|
|
673
|
+
{...ariaDescribedProp}
|
|
674
|
+
autoComplete="off"
|
|
675
|
+
// isFocused={componentIsFocused}
|
|
676
|
+
id={id}
|
|
677
|
+
onClick={(event) => this.handleInputClick(event)}
|
|
678
|
+
onBlur={(event) => this.handleAutoCompleteBlur(event)}
|
|
679
|
+
onChange={this.handleInputChange}
|
|
680
|
+
onFocus={this.handleInputFocus}
|
|
681
|
+
name={name}
|
|
682
|
+
placeholder={placeholder}
|
|
683
|
+
ref={(inputElement) => {
|
|
684
|
+
this.elementReferences[-1] = inputElement;
|
|
685
|
+
}}
|
|
686
|
+
role="combobox"
|
|
687
|
+
required={required}
|
|
688
|
+
value={query}
|
|
689
|
+
/>
|
|
690
|
+
<Button
|
|
691
|
+
ref={(buttonElement) => {
|
|
692
|
+
this.elementReferences['button-arrow'] = buttonElement;
|
|
693
|
+
}}
|
|
694
|
+
variant="link"
|
|
574
695
|
onClick={() => {
|
|
575
|
-
this.
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
696
|
+
if (!this.state.menuOpen) {
|
|
697
|
+
source(query, (options) => {
|
|
698
|
+
const optionsAvailable = options.length > 0;
|
|
699
|
+
this.setState({
|
|
700
|
+
menuOpen: optionsAvailable,
|
|
701
|
+
options,
|
|
702
|
+
selected: autoselect && optionsAvailable ? 0 : -1,
|
|
703
|
+
validChoiceMade: false,
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
} else {
|
|
707
|
+
this.setState({
|
|
708
|
+
menuOpen: false,
|
|
709
|
+
focused: null,
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
}}
|
|
713
|
+
onBlur={(event) => this.handleAutoCompleteBlur(event)}
|
|
714
|
+
>
|
|
715
|
+
<CaretIcon direction="down" size="md" />
|
|
716
|
+
</Button>
|
|
717
|
+
|
|
718
|
+
<AutoCompleteResultList
|
|
719
|
+
isVisible={menuIsVisible}
|
|
720
|
+
onMouseLeave={() => this.handleListMouseLeave()}
|
|
721
|
+
id={`${id}__listbox`}
|
|
722
|
+
role="listbox"
|
|
723
|
+
>
|
|
724
|
+
<Checkbox>
|
|
725
|
+
{showAutoCompleteResultListItems(options, multiple)}
|
|
726
|
+
</Checkbox>
|
|
727
|
+
|
|
728
|
+
{showNoOptionsFound && (
|
|
729
|
+
<AutoCompleteResultListItem empty>
|
|
730
|
+
{tNoResults?.()}
|
|
731
|
+
</AutoCompleteResultListItem>
|
|
732
|
+
)}
|
|
733
|
+
</AutoCompleteResultList>
|
|
734
|
+
|
|
735
|
+
<AutoCompleteAssistiveHint id={assistiveHintID}>
|
|
736
|
+
{tAssistiveHint?.()}
|
|
737
|
+
</AutoCompleteAssistiveHint>
|
|
738
|
+
</AutoCompleteMultipleInputContainer>
|
|
739
|
+
) : (
|
|
740
|
+
<>
|
|
741
|
+
<AutoCompleteInput
|
|
742
|
+
aria-expanded={menuOpen ? 'true' : 'false'}
|
|
743
|
+
aria-activedescendant={
|
|
744
|
+
optionFocused ? `${id}__option--${focused}` : ''
|
|
745
|
+
}
|
|
746
|
+
aria-owns={`${id}__listbox`}
|
|
747
|
+
aria-autocomplete={this.hasAutoselect() ? 'both' : 'list'}
|
|
748
|
+
{...ariaDescribedProp}
|
|
749
|
+
autoComplete="off"
|
|
750
|
+
isFocused={componentIsFocused}
|
|
751
|
+
id={id}
|
|
752
|
+
onClick={(event) => this.handleInputClick(event)}
|
|
753
|
+
onBlur={this.handleAutoCompleteBlur}
|
|
754
|
+
onChange={this.handleInputChange}
|
|
755
|
+
onFocus={this.handleInputFocus}
|
|
756
|
+
name={name}
|
|
757
|
+
placeholder={placeholder}
|
|
758
|
+
ref={(inputElement) => {
|
|
759
|
+
this.elementReferences[-1] = inputElement;
|
|
579
760
|
}}
|
|
580
|
-
|
|
761
|
+
role="combobox"
|
|
762
|
+
required={required}
|
|
763
|
+
value={query}
|
|
581
764
|
/>
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
overflow: 'hidden',
|
|
604
|
-
padding: '0',
|
|
605
|
-
position: 'absolute',
|
|
606
|
-
whiteSpace: 'nowrap',
|
|
607
|
-
width: '1px',
|
|
608
|
-
}}
|
|
609
|
-
>
|
|
610
|
-
{index + 1} από {options.length}
|
|
611
|
-
</span>
|
|
612
|
-
) : (
|
|
613
|
-
''
|
|
614
|
-
);
|
|
615
|
-
|
|
616
|
-
return (
|
|
617
|
-
<AutoCompleteResultListItem
|
|
618
|
-
isFocused={showFocused}
|
|
619
|
-
isHovered={hovered}
|
|
620
|
-
index={index}
|
|
621
|
-
aria-selected={focused === index ? 'true' : 'false'}
|
|
622
|
-
key={index}
|
|
623
|
-
onBlur={(event) => this.handleOptionBlur(event, index)}
|
|
624
|
-
onClick={() => this.handleOptionClick(index)}
|
|
625
|
-
onMouseDown={this.handleOptionMouseDown}
|
|
626
|
-
onMouseEnter={() => this.handleOptionMouseEnter(index)}
|
|
627
|
-
ref={(optionEl) => {
|
|
628
|
-
this.elementReferences[index] = optionEl;
|
|
629
|
-
}}
|
|
630
|
-
role="option"
|
|
631
|
-
tabIndex={-1}
|
|
632
|
-
aria-posinset={index + 1}
|
|
633
|
-
aria-setsize={options.length}
|
|
634
|
-
>
|
|
635
|
-
{this.templateSuggestion(option)} {iosPosinsetHtml}
|
|
636
|
-
</AutoCompleteResultListItem>
|
|
637
|
-
);
|
|
638
|
-
})}
|
|
639
|
-
{showNoOptionsFound && (
|
|
640
|
-
<AutoCompleteResultListItem empty>
|
|
641
|
-
{tNoResults?.()}
|
|
642
|
-
</AutoCompleteResultListItem>
|
|
643
|
-
)}
|
|
644
|
-
</AutoCompleteResultList>
|
|
645
|
-
|
|
646
|
-
<AutoCompleteAssistiveHint id={assistiveHintID}>
|
|
647
|
-
{tAssistiveHint?.()}
|
|
648
|
-
</AutoCompleteAssistiveHint>
|
|
649
|
-
</AutoCompleteWrapper>
|
|
765
|
+
<CaretIcon direction="down" size="md" />
|
|
766
|
+
|
|
767
|
+
<AutoCompleteResultList
|
|
768
|
+
isVisible={menuIsVisible}
|
|
769
|
+
onMouseLeave={() => this.handleListMouseLeave()}
|
|
770
|
+
id={`${id}__listbox`}
|
|
771
|
+
role="listbox"
|
|
772
|
+
>
|
|
773
|
+
{showAutoCompleteResultListItems(options, false)}
|
|
774
|
+
{showNoOptionsFound && (
|
|
775
|
+
<AutoCompleteResultListItem empty>
|
|
776
|
+
{tNoResults?.()}
|
|
777
|
+
</AutoCompleteResultListItem>
|
|
778
|
+
)}
|
|
779
|
+
</AutoCompleteResultList>
|
|
780
|
+
<AutoCompleteAssistiveHint id={assistiveHintID}>
|
|
781
|
+
{tAssistiveHint?.()}
|
|
782
|
+
</AutoCompleteAssistiveHint>
|
|
783
|
+
</>
|
|
784
|
+
)}
|
|
785
|
+
</AutoCompleteContainer>
|
|
650
786
|
);
|
|
651
787
|
}
|
|
652
788
|
}
|
|
@@ -657,6 +793,8 @@ export {
|
|
|
657
793
|
AutoCompleteInputTypeahead,
|
|
658
794
|
AutoCompleteResultList,
|
|
659
795
|
AutoCompleteResultListItem,
|
|
660
|
-
|
|
796
|
+
AutoCompleteContainer,
|
|
661
797
|
AutoCompleteAssistiveHint,
|
|
798
|
+
AutoCompleteMultipleInputContainer,
|
|
799
|
+
AutoCompleteMultipleInput,
|
|
662
800
|
};
|