@breadstone/mosaik-elements-foundation 0.0.151 → 0.0.152
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/Controls/Behaviors/AutoCompleteable.d.ts +45 -7
- package/Controls/Behaviors/AutoCompleteable.d.ts.map +1 -1
- package/Controls/Behaviors/AutoCompleteable.js +230 -21
- package/Controls/Behaviors/AutoCompleteable.js.map +1 -1
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElement.d.ts +236 -3
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElement.d.ts.map +1 -1
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElement.js +647 -9
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElement.js.map +1 -1
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElementIntl.d.ts +31 -0
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElementIntl.d.ts.map +1 -0
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElementIntl.js +50 -0
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElementIntl.js.map +1 -0
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElementTemplate.d.ts.map +1 -1
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElementTemplate.js +64 -10
- package/Controls/Components/Inputs/AutoCompleteBox/AutoCompleteBoxElementTemplate.js.map +1 -1
- package/Controls/Components/Inputs/AutoCompleteBox/IAutoCompleteBoxElementProps.d.ts +15 -1
- package/Controls/Components/Inputs/AutoCompleteBox/IAutoCompleteBoxElementProps.d.ts.map +1 -1
- package/Controls/Controllers/AutoCompleteController.d.ts +1 -0
- package/Controls/Controllers/AutoCompleteController.d.ts.map +1 -1
- package/Controls/Controllers/AutoCompleteController.js +22 -5
- package/Controls/Controllers/AutoCompleteController.js.map +1 -1
- package/Index.d.ts +1 -1
- package/Index.d.ts.map +1 -1
- package/Index.js.map +1 -1
- package/Routing/PathToRegexp.d.ts +1 -1
- package/custom-elements.json +707 -28
- package/package.json +3 -3
|
@@ -8,6 +8,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
+
var AutoCompleteBoxElement_1;
|
|
12
|
+
import { CssLength } from '@breadstone/mosaik-themes';
|
|
13
|
+
import { html } from '../../../../Dom/Html';
|
|
11
14
|
import { AutoCompleteable } from '../../../Behaviors/AutoCompleteable';
|
|
12
15
|
import { Attribute } from '../../../Decorators/AttributeDecorator';
|
|
13
16
|
import { Component } from '../../../Decorators/ComponentDecorator';
|
|
@@ -17,6 +20,17 @@ import { autoCompleteBoxElementJoyStyle } from './Themes/AutoCompleteBoxElement.
|
|
|
17
20
|
import { autoCompleteBoxElementRetroStyle } from './Themes/AutoCompleteBoxElement.Retro';
|
|
18
21
|
import { autoCompleteBoxElementCosmopolitanStyle } from './Themes/AutoCompleteBoxElement.Cosmopolitan';
|
|
19
22
|
import { Themeable } from '../../../Behaviors/Themeable';
|
|
23
|
+
import { Placement } from '../../../Types/Placement';
|
|
24
|
+
import { Strategy } from '../../../Types/Strategy';
|
|
25
|
+
import { FloatingSync } from '../../../Types/FloatingSync';
|
|
26
|
+
import { PortalElement } from '../../Primitives/Portal/PortalElement';
|
|
27
|
+
import { FloatingElement } from '../../Primitives/Floating/FloatingElement';
|
|
28
|
+
import { DropDownable } from '../../../Behaviors/DropDownable';
|
|
29
|
+
import { Disableable } from '../../../Behaviors/Disableable';
|
|
30
|
+
import { Focusable } from '../../../Behaviors/Focusable';
|
|
31
|
+
import { Watch } from '../../../Decorators/WatchDecorator';
|
|
32
|
+
import { IntlController } from '../../../../Intl/IntlController';
|
|
33
|
+
import { AutoCompleteBoxElementIntl } from './AutoCompleteBoxElementIntl';
|
|
20
34
|
// #endregion
|
|
21
35
|
/**
|
|
22
36
|
* Auto Complete Box - An input field with auto-completion functionality for enhanced user experience.
|
|
@@ -34,6 +48,8 @@ import { Themeable } from '../../../Behaviors/Themeable';
|
|
|
34
48
|
* @csspart input - the input field.
|
|
35
49
|
* @csspart list - the suggestions list.
|
|
36
50
|
* @csspart item - individual suggestion items.
|
|
51
|
+
* @csspart portal - the floating portal container for the suggestions.
|
|
52
|
+
* @csspart popup - the floating popup container for the suggestions.
|
|
37
53
|
*
|
|
38
54
|
* @cssprop {String} --auto-complete-box-background-color - The background color.
|
|
39
55
|
* @cssprop {String} --auto-complete-box-border-color - The border color.
|
|
@@ -50,6 +66,9 @@ import { Themeable } from '../../../Behaviors/Themeable';
|
|
|
50
66
|
* @cssprop {String} --auto-complete-box-shadow - The shadow.
|
|
51
67
|
* @cssprop {String} --auto-complete-box-width - The width.
|
|
52
68
|
*
|
|
69
|
+
* @dependency mosaik-portal - Provides a portal host for rendering the suggestion popup.
|
|
70
|
+
* @dependency mosaik-floating - Positions the suggestion popup relative to the host element.
|
|
71
|
+
*
|
|
53
72
|
* @example
|
|
54
73
|
* ```html
|
|
55
74
|
* <mosaik-autocompletebox
|
|
@@ -60,9 +79,23 @@ import { Themeable } from '../../../Behaviors/Themeable';
|
|
|
60
79
|
*
|
|
61
80
|
* @public
|
|
62
81
|
*/
|
|
63
|
-
let AutoCompleteBoxElement = class AutoCompleteBoxElement extends Themeable(AutoCompleteable(CustomElement)) {
|
|
82
|
+
let AutoCompleteBoxElement = class AutoCompleteBoxElement extends Themeable(DropDownable(Focusable(Disableable(AutoCompleteable(CustomElement))))) {
|
|
83
|
+
static { AutoCompleteBoxElement_1 = this; }
|
|
64
84
|
// #region Fields
|
|
65
|
-
|
|
85
|
+
static _idCounter = 0;
|
|
86
|
+
static _floatingFlipFallbackPlacements = Object.freeze([
|
|
87
|
+
Placement.Top,
|
|
88
|
+
Placement.Bottom
|
|
89
|
+
]);
|
|
90
|
+
_text;
|
|
91
|
+
_shouldSyncSearchFromText;
|
|
92
|
+
_highlightedIndex;
|
|
93
|
+
_lastFilterText;
|
|
94
|
+
_inputId;
|
|
95
|
+
_listboxId;
|
|
96
|
+
_intlController;
|
|
97
|
+
_closeTimeoutHandle;
|
|
98
|
+
_suggestionSync;
|
|
66
99
|
// #endregion
|
|
67
100
|
// #region Ctor
|
|
68
101
|
/**
|
|
@@ -70,6 +103,22 @@ let AutoCompleteBoxElement = class AutoCompleteBoxElement extends Themeable(Auto
|
|
|
70
103
|
*/
|
|
71
104
|
constructor() {
|
|
72
105
|
super();
|
|
106
|
+
this._text = '';
|
|
107
|
+
this._shouldSyncSearchFromText = true;
|
|
108
|
+
this._highlightedIndex = -1;
|
|
109
|
+
this._lastFilterText = '';
|
|
110
|
+
this._closeTimeoutHandle = null;
|
|
111
|
+
this._inputId = AutoCompleteBoxElement_1.generateId('mosaik-autocomplete-input');
|
|
112
|
+
this._listboxId = AutoCompleteBoxElement_1.generateId('mosaik-autocomplete-list');
|
|
113
|
+
this._suggestionSync = FloatingSync.Width;
|
|
114
|
+
this.dropDownPlacement = Placement.BottomStart;
|
|
115
|
+
this.dropDownStrategy = Strategy.Fixed;
|
|
116
|
+
this.dropDownDistance = 8;
|
|
117
|
+
this.dropDownSkidding = 0;
|
|
118
|
+
this.maxDropDownHeight = '280px';
|
|
119
|
+
this._intlController = new IntlController(this, {
|
|
120
|
+
factory: () => new AutoCompleteBoxElementIntl()
|
|
121
|
+
});
|
|
73
122
|
}
|
|
74
123
|
// #endregion
|
|
75
124
|
// #region Properties
|
|
@@ -97,17 +146,561 @@ let AutoCompleteBoxElement = class AutoCompleteBoxElement extends Themeable(Auto
|
|
|
97
146
|
if (this._text !== value) {
|
|
98
147
|
this._text = value;
|
|
99
148
|
this.requestUpdate('text');
|
|
149
|
+
if (this._shouldSyncSearchFromText) {
|
|
150
|
+
this._lastFilterText = value;
|
|
151
|
+
this.updateSearchText(value);
|
|
152
|
+
}
|
|
100
153
|
}
|
|
101
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Gets the identifier of the input element.
|
|
157
|
+
*
|
|
158
|
+
* @public
|
|
159
|
+
* @readonly
|
|
160
|
+
*/
|
|
161
|
+
get inputId() {
|
|
162
|
+
return this._inputId;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Gets the identifier of the suggestion list element.
|
|
166
|
+
*
|
|
167
|
+
* @public
|
|
168
|
+
* @readonly
|
|
169
|
+
*/
|
|
170
|
+
get listboxId() {
|
|
171
|
+
return this._listboxId;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Gets the index of the highlighted suggestion.
|
|
175
|
+
*
|
|
176
|
+
* @public
|
|
177
|
+
* @readonly
|
|
178
|
+
*/
|
|
179
|
+
get highlightedIndex() {
|
|
180
|
+
return this._highlightedIndex;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Gets a value indicating whether the suggestion list should be rendered.
|
|
184
|
+
*
|
|
185
|
+
* @public
|
|
186
|
+
* @readonly
|
|
187
|
+
*/
|
|
188
|
+
get shouldRenderSuggestions() {
|
|
189
|
+
if (!this.isFocused) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (!this.isDropDownOpen) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
return this.hasSuggestions || this.shouldShowNoResultsMessage || this.shouldShowLoadingState;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Gets a value indicating whether suggestions are available.
|
|
199
|
+
*
|
|
200
|
+
* @public
|
|
201
|
+
* @readonly
|
|
202
|
+
*/
|
|
203
|
+
get hasSuggestions() {
|
|
204
|
+
return this.filteredItems.length > 0;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Gets the active descendant identifier for accessibility.
|
|
208
|
+
*
|
|
209
|
+
* @public
|
|
210
|
+
* @readonly
|
|
211
|
+
*/
|
|
212
|
+
get activeDescendantId() {
|
|
213
|
+
return this._highlightedIndex >= 0
|
|
214
|
+
? this.getOptionId(this._highlightedIndex)
|
|
215
|
+
: null;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Gets a value indicating whether the no-results message should be displayed.
|
|
219
|
+
*
|
|
220
|
+
* @public
|
|
221
|
+
* @readonly
|
|
222
|
+
*/
|
|
223
|
+
get shouldShowNoResultsMessage() {
|
|
224
|
+
return this.isFocused && !this.isPopulating && this._lastFilterText.length >= this.minimumPrefixLength && this.filteredItems.length === 0;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Gets a value indicating whether the loading state should be displayed.
|
|
228
|
+
*
|
|
229
|
+
* @public
|
|
230
|
+
* @readonly
|
|
231
|
+
*/
|
|
232
|
+
get shouldShowLoadingState() {
|
|
233
|
+
return this.isPopulating && !this.hasSuggestions;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Gets or sets the preferred placement for the suggestion popup.
|
|
237
|
+
*
|
|
238
|
+
* @public
|
|
239
|
+
* @attr
|
|
240
|
+
*/
|
|
241
|
+
get suggestionPlacement() {
|
|
242
|
+
return this.dropDownPlacement;
|
|
243
|
+
}
|
|
244
|
+
set suggestionPlacement(value) {
|
|
245
|
+
if (this.dropDownPlacement !== value) {
|
|
246
|
+
this.dropDownPlacement = value;
|
|
247
|
+
this.requestUpdate('suggestionPlacement');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Gets or sets the positioning strategy for the suggestion popup.
|
|
252
|
+
*
|
|
253
|
+
* @public
|
|
254
|
+
* @attr
|
|
255
|
+
*/
|
|
256
|
+
get suggestionStrategy() {
|
|
257
|
+
return this.dropDownStrategy;
|
|
258
|
+
}
|
|
259
|
+
set suggestionStrategy(value) {
|
|
260
|
+
if (this.dropDownStrategy !== value) {
|
|
261
|
+
this.dropDownStrategy = value;
|
|
262
|
+
this.requestUpdate('suggestionStrategy');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Gets or sets the distance in pixels between the host and the suggestion popup.
|
|
267
|
+
*
|
|
268
|
+
* @public
|
|
269
|
+
* @attr
|
|
270
|
+
*/
|
|
271
|
+
get suggestionDistance() {
|
|
272
|
+
return this.dropDownDistance;
|
|
273
|
+
}
|
|
274
|
+
set suggestionDistance(value) {
|
|
275
|
+
if (this.dropDownDistance !== value) {
|
|
276
|
+
this.dropDownDistance = value;
|
|
277
|
+
this.requestUpdate('suggestionDistance');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Gets or sets the horizontal skidding offset for the suggestion popup.
|
|
282
|
+
*
|
|
283
|
+
* @public
|
|
284
|
+
* @attr
|
|
285
|
+
*/
|
|
286
|
+
get suggestionSkidding() {
|
|
287
|
+
return this.dropDownSkidding;
|
|
288
|
+
}
|
|
289
|
+
set suggestionSkidding(value) {
|
|
290
|
+
if (this.dropDownSkidding !== value) {
|
|
291
|
+
this.dropDownSkidding = value;
|
|
292
|
+
this.requestUpdate('suggestionSkidding');
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Gets or sets the size synchronization mode for the suggestion popup.
|
|
297
|
+
*
|
|
298
|
+
* @public
|
|
299
|
+
* @attr
|
|
300
|
+
*/
|
|
301
|
+
get suggestionSync() {
|
|
302
|
+
return this._suggestionSync;
|
|
303
|
+
}
|
|
304
|
+
set suggestionSync(value) {
|
|
305
|
+
if (this._suggestionSync !== value) {
|
|
306
|
+
this._suggestionSync = value;
|
|
307
|
+
this.requestUpdate('suggestionSync');
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Gets or sets the maximum height for the suggestion popup.
|
|
312
|
+
*
|
|
313
|
+
* @public
|
|
314
|
+
* @attr
|
|
315
|
+
*/
|
|
316
|
+
get maxSuggestionHeight() {
|
|
317
|
+
return this.maxDropDownHeight;
|
|
318
|
+
}
|
|
319
|
+
set maxSuggestionHeight(value) {
|
|
320
|
+
if (this.maxDropDownHeight !== value) {
|
|
321
|
+
this.maxDropDownHeight = value;
|
|
322
|
+
this.requestUpdate('maxSuggestionHeight');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Gets the fallback placements used when positioning the suggestion popup.
|
|
327
|
+
*
|
|
328
|
+
* @public
|
|
329
|
+
* @readonly
|
|
330
|
+
*/
|
|
331
|
+
get suggestionFlipFallbackPlacements() {
|
|
332
|
+
return Array.from(AutoCompleteBoxElement_1._floatingFlipFallbackPlacements);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Returns the `intl` property.
|
|
336
|
+
*
|
|
337
|
+
* @public
|
|
338
|
+
* @readonly
|
|
339
|
+
*/
|
|
340
|
+
get intl() {
|
|
341
|
+
return this._intlController.intl;
|
|
342
|
+
}
|
|
102
343
|
// #endregion
|
|
103
344
|
// #region Methods
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
345
|
+
/**
|
|
346
|
+
* @internal
|
|
347
|
+
*/
|
|
348
|
+
onFilterCallback(items) {
|
|
349
|
+
super.onFilterCallback?.(items);
|
|
350
|
+
if (items.length > 0) {
|
|
351
|
+
this.openSuggestions();
|
|
352
|
+
this.setHighlightedIndex(0);
|
|
353
|
+
}
|
|
354
|
+
else if (this.shouldShowNoResultsMessage && this.isFocused) {
|
|
355
|
+
this.openSuggestions();
|
|
356
|
+
this._highlightedIndex = -1;
|
|
357
|
+
}
|
|
358
|
+
else if (!this.isFocused) {
|
|
359
|
+
this.closeSuggestions();
|
|
360
|
+
}
|
|
361
|
+
this.applyTextCompletion();
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Synchronizes the suggestion visibility with the floating popup state.
|
|
365
|
+
*
|
|
366
|
+
* @public
|
|
367
|
+
* @param isActive - Indicates whether the floating popup is active.
|
|
368
|
+
*/
|
|
369
|
+
onFloatingActiveChanged(isActive) {
|
|
370
|
+
if (!isActive) {
|
|
371
|
+
this.closeSuggestions();
|
|
372
|
+
}
|
|
108
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* @public
|
|
376
|
+
* @override
|
|
377
|
+
*/
|
|
109
378
|
onSelectCallback(item) {
|
|
110
|
-
|
|
379
|
+
const displayValue = this.getDisplayValue(item);
|
|
380
|
+
this.executeWithoutSearchSync(() => {
|
|
381
|
+
this.text = displayValue;
|
|
382
|
+
});
|
|
383
|
+
this._lastFilterText = displayValue;
|
|
384
|
+
this.closeSuggestions();
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* @public
|
|
388
|
+
* @override
|
|
389
|
+
*/
|
|
390
|
+
disconnectedCallback() {
|
|
391
|
+
this.clearCloseTimeout();
|
|
392
|
+
super.disconnectedCallback();
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Handles input events raised by the native text box.
|
|
396
|
+
*
|
|
397
|
+
* @public
|
|
398
|
+
* @param event - The event that triggered the handler.
|
|
399
|
+
*/
|
|
400
|
+
onInput(event) {
|
|
401
|
+
if (this.disabled) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const value = event.target.value;
|
|
405
|
+
this.cancelCloseSuggestions();
|
|
406
|
+
this._shouldSyncSearchFromText = true;
|
|
407
|
+
this._lastFilterText = value;
|
|
408
|
+
this.text = value;
|
|
409
|
+
if (value.length === 0) {
|
|
410
|
+
this._highlightedIndex = -1;
|
|
411
|
+
}
|
|
412
|
+
this.openSuggestions();
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Handles focus events raised by the native input element.
|
|
416
|
+
*
|
|
417
|
+
* @public
|
|
418
|
+
*/
|
|
419
|
+
onFocus(_event) {
|
|
420
|
+
this.isFocused = true;
|
|
421
|
+
this.cancelCloseSuggestions();
|
|
422
|
+
if (this.hasSuggestions || this.shouldShowNoResultsMessage) {
|
|
423
|
+
this.openSuggestions();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Handles blur events raised by the native input element.
|
|
428
|
+
*
|
|
429
|
+
* @public
|
|
430
|
+
*/
|
|
431
|
+
onBlur(_event) {
|
|
432
|
+
this.isFocused = false;
|
|
433
|
+
this.scheduleCloseSuggestions();
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Handles keyboard interactions for navigating the suggestion list.
|
|
437
|
+
*
|
|
438
|
+
* @public
|
|
439
|
+
* @param event - The keyboard event that triggered the handler.
|
|
440
|
+
*/
|
|
441
|
+
onKeyDown(event) {
|
|
442
|
+
if (this.disabled) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
switch (event.key) {
|
|
446
|
+
case 'ArrowDown': {
|
|
447
|
+
event.preventDefault();
|
|
448
|
+
this.cancelCloseSuggestions();
|
|
449
|
+
this.openSuggestions();
|
|
450
|
+
this.moveHighlight(1);
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
case 'ArrowUp': {
|
|
454
|
+
event.preventDefault();
|
|
455
|
+
this.cancelCloseSuggestions();
|
|
456
|
+
this.openSuggestions();
|
|
457
|
+
this.moveHighlight(-1);
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
case 'Home': {
|
|
461
|
+
if (this.filteredItems.length > 0) {
|
|
462
|
+
event.preventDefault();
|
|
463
|
+
this.setHighlightedIndex(0);
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
case 'End': {
|
|
468
|
+
if (this.filteredItems.length > 0) {
|
|
469
|
+
event.preventDefault();
|
|
470
|
+
this.setHighlightedIndex(this.filteredItems.length - 1);
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
case 'Enter': {
|
|
475
|
+
if (this._highlightedIndex >= 0 && this._highlightedIndex < this.filteredItems.length) {
|
|
476
|
+
event.preventDefault();
|
|
477
|
+
const item = this.filteredItems[this._highlightedIndex];
|
|
478
|
+
this.commitSelection(item);
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
case 'Tab': {
|
|
483
|
+
if (this._highlightedIndex >= 0 && this._highlightedIndex < this.filteredItems.length) {
|
|
484
|
+
const item = this.filteredItems[this._highlightedIndex];
|
|
485
|
+
this.commitSelection(item);
|
|
486
|
+
}
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
case 'Escape': {
|
|
490
|
+
if (this.isDropDownOpen) {
|
|
491
|
+
event.preventDefault();
|
|
492
|
+
this.closeSuggestions();
|
|
493
|
+
}
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
default:
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Handles pointer events to keep the suggestion list open while interacting with it.
|
|
502
|
+
*
|
|
503
|
+
* @public
|
|
504
|
+
* @param event - The mouse event that triggered the handler.
|
|
505
|
+
*/
|
|
506
|
+
onSuggestionMouseDown(event) {
|
|
507
|
+
event.preventDefault();
|
|
508
|
+
this.cancelCloseSuggestions();
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Handles click events on suggestion items.
|
|
512
|
+
*
|
|
513
|
+
* @public
|
|
514
|
+
* @param index - The index of the clicked suggestion.
|
|
515
|
+
*/
|
|
516
|
+
onSuggestionClick(index) {
|
|
517
|
+
if (this.disabled) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const item = this.filteredItems[index];
|
|
521
|
+
if (item !== undefined) {
|
|
522
|
+
this.commitSelection(item);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Renders a suggestion with highlighted text.
|
|
527
|
+
*
|
|
528
|
+
* @public
|
|
529
|
+
* @param item - The item to render.
|
|
530
|
+
*/
|
|
531
|
+
renderHighlightedItem(item) {
|
|
532
|
+
const value = this.getDisplayValue(item);
|
|
533
|
+
const searchText = this._lastFilterText;
|
|
534
|
+
if (!searchText) {
|
|
535
|
+
return html `${value}`;
|
|
536
|
+
}
|
|
537
|
+
const lowerValue = value.toLowerCase();
|
|
538
|
+
const lowerSearch = searchText.toLowerCase();
|
|
539
|
+
const segments = [];
|
|
540
|
+
let cursor = 0;
|
|
541
|
+
let matchIndex = lowerValue.indexOf(lowerSearch, cursor);
|
|
542
|
+
while (matchIndex !== -1) {
|
|
543
|
+
const unmatched = value.slice(cursor, matchIndex);
|
|
544
|
+
if (unmatched) {
|
|
545
|
+
segments.push(unmatched);
|
|
546
|
+
}
|
|
547
|
+
const matched = value.slice(matchIndex, matchIndex + searchText.length);
|
|
548
|
+
segments.push(html `<span class="match">${matched}</span>`);
|
|
549
|
+
cursor = matchIndex + searchText.length;
|
|
550
|
+
matchIndex = lowerValue.indexOf(lowerSearch, cursor);
|
|
551
|
+
}
|
|
552
|
+
const tail = value.slice(cursor);
|
|
553
|
+
if (tail) {
|
|
554
|
+
segments.push(tail);
|
|
555
|
+
}
|
|
556
|
+
if (segments.length === 0) {
|
|
557
|
+
segments.push(value);
|
|
558
|
+
}
|
|
559
|
+
return html `${segments}`;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Gets the identifier for the suggestion option at the specified index.
|
|
563
|
+
*
|
|
564
|
+
* @public
|
|
565
|
+
* @param index - The index of the option.
|
|
566
|
+
*/
|
|
567
|
+
getOptionId(index) {
|
|
568
|
+
return `${this._listboxId}-option-${index}`;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Synchronizes internal state when the dropdown open state changes.
|
|
572
|
+
*
|
|
573
|
+
* @protected
|
|
574
|
+
*/
|
|
575
|
+
onDropDownOpenChanged(_previous, next) {
|
|
576
|
+
if (!next) {
|
|
577
|
+
this._highlightedIndex = -1;
|
|
578
|
+
this.clearCloseTimeout();
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* @protected
|
|
583
|
+
*/
|
|
584
|
+
onDisabledChanged(_previous, next) {
|
|
585
|
+
if (next) {
|
|
586
|
+
this.closeSuggestions();
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
static generateId(prefix) {
|
|
590
|
+
AutoCompleteBoxElement_1._idCounter += 1;
|
|
591
|
+
return `${prefix}-${AutoCompleteBoxElement_1._idCounter}`;
|
|
592
|
+
}
|
|
593
|
+
getInputElement() {
|
|
594
|
+
return this.renderRoot.querySelector(`#${this._inputId}`) ?? null;
|
|
595
|
+
}
|
|
596
|
+
executeWithoutSearchSync(action) {
|
|
597
|
+
const previous = this._shouldSyncSearchFromText;
|
|
598
|
+
this._shouldSyncSearchFromText = false;
|
|
599
|
+
try {
|
|
600
|
+
action();
|
|
601
|
+
}
|
|
602
|
+
finally {
|
|
603
|
+
this._shouldSyncSearchFromText = previous;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
openSuggestions() {
|
|
607
|
+
if (!this.isDropDownOpen) {
|
|
608
|
+
this.open();
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
closeSuggestions() {
|
|
612
|
+
if (this.isDropDownOpen) {
|
|
613
|
+
this.close();
|
|
614
|
+
}
|
|
615
|
+
this._highlightedIndex = -1;
|
|
616
|
+
this.clearCloseTimeout();
|
|
617
|
+
}
|
|
618
|
+
scheduleCloseSuggestions() {
|
|
619
|
+
this.clearCloseTimeout();
|
|
620
|
+
this._closeTimeoutHandle = window.setTimeout(() => {
|
|
621
|
+
this._closeTimeoutHandle = null;
|
|
622
|
+
this.closeSuggestions();
|
|
623
|
+
}, 150);
|
|
624
|
+
}
|
|
625
|
+
cancelCloseSuggestions() {
|
|
626
|
+
this.clearCloseTimeout();
|
|
627
|
+
}
|
|
628
|
+
clearCloseTimeout() {
|
|
629
|
+
if (this._closeTimeoutHandle !== null) {
|
|
630
|
+
window.clearTimeout(this._closeTimeoutHandle);
|
|
631
|
+
this._closeTimeoutHandle = null;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
commitSelection(item) {
|
|
635
|
+
this.updateSelectedItem(item);
|
|
636
|
+
const input = this.getInputElement();
|
|
637
|
+
if (input) {
|
|
638
|
+
const value = this.text;
|
|
639
|
+
input.value = value;
|
|
640
|
+
input.setSelectionRange(value.length, value.length);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
applyTextCompletion() {
|
|
644
|
+
if (!this.isTextCompletionEnabled) {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
if (!this.isFocused) {
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (!this._lastFilterText) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (this.filteredItems.length === 0) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const firstItem = this.filteredItems[0];
|
|
657
|
+
const completion = this.getDisplayValue(firstItem);
|
|
658
|
+
if (!completion.toLowerCase().startsWith(this._lastFilterText.toLowerCase())) {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
const typedLength = this._lastFilterText.length;
|
|
662
|
+
this.executeWithoutSearchSync(() => {
|
|
663
|
+
this.text = completion;
|
|
664
|
+
});
|
|
665
|
+
const input = this.getInputElement();
|
|
666
|
+
if (input) {
|
|
667
|
+
input.value = completion;
|
|
668
|
+
input.setSelectionRange(typedLength, completion.length);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
moveHighlight(step) {
|
|
672
|
+
if (this.filteredItems.length === 0) {
|
|
673
|
+
this._highlightedIndex = -1;
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
const length = this.filteredItems.length;
|
|
677
|
+
const currentIndex = this._highlightedIndex;
|
|
678
|
+
const nextIndex = currentIndex === -1
|
|
679
|
+
? step > 0 ? 0 : length - 1
|
|
680
|
+
: (currentIndex + step + length) % length;
|
|
681
|
+
this.setHighlightedIndex(nextIndex);
|
|
682
|
+
}
|
|
683
|
+
setHighlightedIndex(index) {
|
|
684
|
+
if (this.filteredItems.length === 0) {
|
|
685
|
+
this._highlightedIndex = -1;
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const normalizedIndex = Math.max(0, Math.min(index, this.filteredItems.length - 1));
|
|
689
|
+
if (this._highlightedIndex !== normalizedIndex) {
|
|
690
|
+
this._highlightedIndex = normalizedIndex;
|
|
691
|
+
this.requestUpdate();
|
|
692
|
+
this.scrollHighlightedOptionIntoView();
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
scrollHighlightedOptionIntoView() {
|
|
696
|
+
if (this._highlightedIndex < 0) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
this.scrollOptionIntoView(this._highlightedIndex);
|
|
700
|
+
}
|
|
701
|
+
scrollOptionIntoView(index) {
|
|
702
|
+
const option = this.renderRoot.querySelector(`#${this.getOptionId(index)}`);
|
|
703
|
+
option?.scrollIntoView({ block: 'nearest' });
|
|
111
704
|
}
|
|
112
705
|
};
|
|
113
706
|
__decorate([
|
|
@@ -115,7 +708,49 @@ __decorate([
|
|
|
115
708
|
__metadata("design:type", String),
|
|
116
709
|
__metadata("design:paramtypes", [String])
|
|
117
710
|
], AutoCompleteBoxElement.prototype, "text", null);
|
|
118
|
-
|
|
711
|
+
__decorate([
|
|
712
|
+
Attribute({ type: Placement }),
|
|
713
|
+
__metadata("design:type", String),
|
|
714
|
+
__metadata("design:paramtypes", [String])
|
|
715
|
+
], AutoCompleteBoxElement.prototype, "suggestionPlacement", null);
|
|
716
|
+
__decorate([
|
|
717
|
+
Attribute({ type: Strategy }),
|
|
718
|
+
__metadata("design:type", String),
|
|
719
|
+
__metadata("design:paramtypes", [String])
|
|
720
|
+
], AutoCompleteBoxElement.prototype, "suggestionStrategy", null);
|
|
721
|
+
__decorate([
|
|
722
|
+
Attribute({ type: Number }),
|
|
723
|
+
__metadata("design:type", Number),
|
|
724
|
+
__metadata("design:paramtypes", [Number])
|
|
725
|
+
], AutoCompleteBoxElement.prototype, "suggestionDistance", null);
|
|
726
|
+
__decorate([
|
|
727
|
+
Attribute({ type: Number }),
|
|
728
|
+
__metadata("design:type", Number),
|
|
729
|
+
__metadata("design:paramtypes", [Number])
|
|
730
|
+
], AutoCompleteBoxElement.prototype, "suggestionSkidding", null);
|
|
731
|
+
__decorate([
|
|
732
|
+
Attribute({ type: FloatingSync }),
|
|
733
|
+
__metadata("design:type", String),
|
|
734
|
+
__metadata("design:paramtypes", [String])
|
|
735
|
+
], AutoCompleteBoxElement.prototype, "suggestionSync", null);
|
|
736
|
+
__decorate([
|
|
737
|
+
Attribute({ type: CssLength }),
|
|
738
|
+
__metadata("design:type", Object),
|
|
739
|
+
__metadata("design:paramtypes", [Object])
|
|
740
|
+
], AutoCompleteBoxElement.prototype, "maxSuggestionHeight", null);
|
|
741
|
+
__decorate([
|
|
742
|
+
Watch('isDropDownOpen'),
|
|
743
|
+
__metadata("design:type", Function),
|
|
744
|
+
__metadata("design:paramtypes", [Boolean, Boolean]),
|
|
745
|
+
__metadata("design:returntype", void 0)
|
|
746
|
+
], AutoCompleteBoxElement.prototype, "onDropDownOpenChanged", null);
|
|
747
|
+
__decorate([
|
|
748
|
+
Watch('disabled'),
|
|
749
|
+
__metadata("design:type", Function),
|
|
750
|
+
__metadata("design:paramtypes", [Boolean, Boolean]),
|
|
751
|
+
__metadata("design:returntype", void 0)
|
|
752
|
+
], AutoCompleteBoxElement.prototype, "onDisabledChanged", null);
|
|
753
|
+
AutoCompleteBoxElement = AutoCompleteBoxElement_1 = __decorate([
|
|
119
754
|
Component({
|
|
120
755
|
selector: 'mosaik-autocompletebox',
|
|
121
756
|
template: autoCompleteBoxElementTemplate,
|
|
@@ -128,7 +763,10 @@ AutoCompleteBoxElement = __decorate([
|
|
|
128
763
|
mode: 'open',
|
|
129
764
|
delegatesFocus: true
|
|
130
765
|
},
|
|
131
|
-
imports: [
|
|
766
|
+
imports: [
|
|
767
|
+
PortalElement,
|
|
768
|
+
FloatingElement
|
|
769
|
+
]
|
|
132
770
|
}),
|
|
133
771
|
__metadata("design:paramtypes", [])
|
|
134
772
|
], AutoCompleteBoxElement);
|