@leavittsoftware/web 11.0.1 → 11.1.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/CLAUDE.md +63 -0
- package/package.json +2 -2
- package/titanium/data-table/data-table-core.js +4 -1
- package/titanium/data-table/data-table-core.js.map +1 -1
- package/titanium/multi-select-base/multi-select-base.d.ts +274 -0
- package/titanium/multi-select-base/multi-select-base.js +991 -0
- package/titanium/multi-select-base/multi-select-base.js.map +1 -0
|
@@ -0,0 +1,991 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import '@material/web/icon/icon';
|
|
3
|
+
import '@material/web/iconbutton/icon-button';
|
|
4
|
+
import '@material/web/menu/menu';
|
|
5
|
+
import '@material/web/menu/menu-item';
|
|
6
|
+
import '@material/web/progress/circular-progress';
|
|
7
|
+
import '../chip/chip';
|
|
8
|
+
import '../input-validator/filled-input-validator';
|
|
9
|
+
import Fuse from 'fuse.js';
|
|
10
|
+
import { css, html, LitElement, nothing } from 'lit';
|
|
11
|
+
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
12
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
13
|
+
import { Debouncer } from '../helpers/debouncer';
|
|
14
|
+
import { ellipsis } from '../styles/ellipsis';
|
|
15
|
+
import { promiseTracking } from '../helpers/promise-tracking';
|
|
16
|
+
import { redispatchEvent } from '@material/web/internal/events/redispatch-event';
|
|
17
|
+
/**
|
|
18
|
+
* Generic multi select combobox. Selected items are displayed as chips inside the field
|
|
19
|
+
* and the search input lives in the popover above the selectable items.
|
|
20
|
+
*
|
|
21
|
+
* Use directly for locally searchable lists or extend it to supply remote search results.
|
|
22
|
+
*
|
|
23
|
+
* @element titanium-multi-select-base
|
|
24
|
+
*
|
|
25
|
+
* @fires selected-changed - Fired when the user adds or removes an item. Read the new value from `event.target.selected`.
|
|
26
|
+
*
|
|
27
|
+
* @slot leading-icon - Icon displayed at the start of the field. Requires `has-leading-icon`.
|
|
28
|
+
*
|
|
29
|
+
* @cssprop --titanium-multi-select-menu-max-height - Max height of the popover. Defaults to 460px.
|
|
30
|
+
* @cssprop --titanium-multi-select-search-height - Height of the sticky search row. Defaults to 52px.
|
|
31
|
+
*/
|
|
32
|
+
let TitaniumMultiSelectBase = class TitaniumMultiSelectBase extends LitElement {
|
|
33
|
+
#isLoading_accessor_storage = false;
|
|
34
|
+
get isLoading() { return this.#isLoading_accessor_storage; }
|
|
35
|
+
set isLoading(value) { this.#isLoading_accessor_storage = value; }
|
|
36
|
+
#label_accessor_storage = 'Multi select';
|
|
37
|
+
/**
|
|
38
|
+
* Sets floating label value.
|
|
39
|
+
*/
|
|
40
|
+
get label() { return this.#label_accessor_storage; }
|
|
41
|
+
set label(value) { this.#label_accessor_storage = value; }
|
|
42
|
+
#placeholder_accessor_storage = 'Select items';
|
|
43
|
+
/**
|
|
44
|
+
* Text displayed in the field when nothing is selected.
|
|
45
|
+
*/
|
|
46
|
+
get placeholder() { return this.#placeholder_accessor_storage; }
|
|
47
|
+
set placeholder(value) { this.#placeholder_accessor_storage = value; }
|
|
48
|
+
#searchPlaceholder_accessor_storage = 'Search';
|
|
49
|
+
/**
|
|
50
|
+
* Placeholder text of the search input inside the popover.
|
|
51
|
+
*/
|
|
52
|
+
get searchPlaceholder() { return this.#searchPlaceholder_accessor_storage; }
|
|
53
|
+
set searchPlaceholder(value) { this.#searchPlaceholder_accessor_storage = value; }
|
|
54
|
+
#selected_accessor_storage = [];
|
|
55
|
+
/**
|
|
56
|
+
* The items selected by the user.
|
|
57
|
+
*/
|
|
58
|
+
get selected() { return this.#selected_accessor_storage; }
|
|
59
|
+
set selected(value) { this.#selected_accessor_storage = value; }
|
|
60
|
+
#items_accessor_storage = [];
|
|
61
|
+
/**
|
|
62
|
+
* Every selectable item. Searched locally with fuse.js and rendered when `sections` is empty.
|
|
63
|
+
*/
|
|
64
|
+
get items() { return this.#items_accessor_storage; }
|
|
65
|
+
set items(value) { this.#items_accessor_storage = value; }
|
|
66
|
+
#sections_accessor_storage = [];
|
|
67
|
+
/**
|
|
68
|
+
* Optional grouping of items rendered when no search term is entered. Search results are always
|
|
69
|
+
* rendered as a flat list.
|
|
70
|
+
*/
|
|
71
|
+
get sections() { return this.#sections_accessor_storage; }
|
|
72
|
+
set sections(value) { this.#sections_accessor_storage = value; }
|
|
73
|
+
#pathToSelectedText_accessor_storage = 'Name';
|
|
74
|
+
/**
|
|
75
|
+
* Key on an item holding the text to display in chips and menu items.
|
|
76
|
+
*/
|
|
77
|
+
get pathToSelectedText() { return this.#pathToSelectedText_accessor_storage; }
|
|
78
|
+
set pathToSelectedText(value) { this.#pathToSelectedText_accessor_storage = value; }
|
|
79
|
+
#pathToIcon_accessor_storage = '';
|
|
80
|
+
/**
|
|
81
|
+
* Optional key on an item holding a Material Symbols icon name.
|
|
82
|
+
*/
|
|
83
|
+
get pathToIcon() { return this.#pathToIcon_accessor_storage; }
|
|
84
|
+
set pathToIcon(value) { this.#pathToIcon_accessor_storage = value; }
|
|
85
|
+
#searchKeys_accessor_storage = [];
|
|
86
|
+
/**
|
|
87
|
+
* Keys searched by the local fuse.js search. Defaults to `[pathToSelectedText]`.
|
|
88
|
+
*/
|
|
89
|
+
get searchKeys() { return this.#searchKeys_accessor_storage; }
|
|
90
|
+
set searchKeys(value) { this.#searchKeys_accessor_storage = value; }
|
|
91
|
+
#maxSelected_accessor_storage = 0;
|
|
92
|
+
/**
|
|
93
|
+
* Maximum number of items the user is allowed to select. Zero allows an unlimited number.
|
|
94
|
+
*/
|
|
95
|
+
get maxSelected() { return this.#maxSelected_accessor_storage; }
|
|
96
|
+
set maxSelected(value) { this.#maxSelected_accessor_storage = value; }
|
|
97
|
+
#required_accessor_storage = false;
|
|
98
|
+
/**
|
|
99
|
+
* Displays error state when nothing is selected.
|
|
100
|
+
*/
|
|
101
|
+
get required() { return this.#required_accessor_storage; }
|
|
102
|
+
set required(value) { this.#required_accessor_storage = value; }
|
|
103
|
+
#disabled_accessor_storage = false;
|
|
104
|
+
/**
|
|
105
|
+
* Whether or not the input should be disabled.
|
|
106
|
+
*/
|
|
107
|
+
get disabled() { return this.#disabled_accessor_storage; }
|
|
108
|
+
set disabled(value) { this.#disabled_accessor_storage = value; }
|
|
109
|
+
#error_accessor_storage = false;
|
|
110
|
+
/**
|
|
111
|
+
* Gets or sets whether or not the field is in a visually invalid state.
|
|
112
|
+
*/
|
|
113
|
+
get error() { return this.#error_accessor_storage; }
|
|
114
|
+
set error(value) { this.#error_accessor_storage = value; }
|
|
115
|
+
#errorText_accessor_storage = 'Please select at least one item';
|
|
116
|
+
/**
|
|
117
|
+
* Error message displayed below the field when it is in an invalid state.
|
|
118
|
+
*/
|
|
119
|
+
get errorText() { return this.#errorText_accessor_storage; }
|
|
120
|
+
set errorText(value) { this.#errorText_accessor_storage = value; }
|
|
121
|
+
#supportingText_accessor_storage = '';
|
|
122
|
+
/**
|
|
123
|
+
* Conveys additional information below the field, such as how it should be used.
|
|
124
|
+
*/
|
|
125
|
+
get supportingText() { return this.#supportingText_accessor_storage; }
|
|
126
|
+
set supportingText(value) { this.#supportingText_accessor_storage = value; }
|
|
127
|
+
#noAsterisk_accessor_storage = false;
|
|
128
|
+
/**
|
|
129
|
+
* Disables the asterisk on the floating label when the field is required.
|
|
130
|
+
*/
|
|
131
|
+
get noAsterisk() { return this.#noAsterisk_accessor_storage; }
|
|
132
|
+
set noAsterisk(value) { this.#noAsterisk_accessor_storage = value; }
|
|
133
|
+
#hasLeadingIcon_accessor_storage = false;
|
|
134
|
+
/**
|
|
135
|
+
* Renders the `leading-icon` slot at the start of the field.
|
|
136
|
+
*/
|
|
137
|
+
get hasLeadingIcon() { return this.#hasLeadingIcon_accessor_storage; }
|
|
138
|
+
set hasLeadingIcon(value) { this.#hasLeadingIcon_accessor_storage = value; }
|
|
139
|
+
#noItemsText_accessor_storage = 'No items';
|
|
140
|
+
/**
|
|
141
|
+
* Text displayed in the popover when there is nothing to select.
|
|
142
|
+
*/
|
|
143
|
+
get noItemsText() { return this.#noItemsText_accessor_storage; }
|
|
144
|
+
set noItemsText(value) { this.#noItemsText_accessor_storage = value; }
|
|
145
|
+
#noResultsText_accessor_storage = 'No matches found';
|
|
146
|
+
/**
|
|
147
|
+
* Text displayed in the popover when a search returns no results.
|
|
148
|
+
*/
|
|
149
|
+
get noResultsText() { return this.#noResultsText_accessor_storage; }
|
|
150
|
+
set noResultsText(value) { this.#noResultsText_accessor_storage = value; }
|
|
151
|
+
#disableClearAll_accessor_storage = false;
|
|
152
|
+
/**
|
|
153
|
+
* Hides the clear all button that appears in the field once something is selected.
|
|
154
|
+
*/
|
|
155
|
+
get disableClearAll() { return this.#disableClearAll_accessor_storage; }
|
|
156
|
+
set disableClearAll(value) { this.#disableClearAll_accessor_storage = value; }
|
|
157
|
+
#positioning_accessor_storage = 'popover';
|
|
158
|
+
/**
|
|
159
|
+
* Positioning strategy of the popover. `popover` falls back to `fixed` in browsers without popover support.
|
|
160
|
+
*/
|
|
161
|
+
get positioning() { return this.#positioning_accessor_storage; }
|
|
162
|
+
set positioning(value) { this.#positioning_accessor_storage = value; }
|
|
163
|
+
#matchInputWidth_accessor_storage = false;
|
|
164
|
+
/**
|
|
165
|
+
* When enabled, uses a ResizeObserver to keep the popover width in sync with the field width.
|
|
166
|
+
*/
|
|
167
|
+
get matchInputWidth() { return this.#matchInputWidth_accessor_storage; }
|
|
168
|
+
set matchInputWidth(value) { this.#matchInputWidth_accessor_storage = value; }
|
|
169
|
+
#menuOpen_accessor_storage = false;
|
|
170
|
+
get menuOpen() { return this.#menuOpen_accessor_storage; }
|
|
171
|
+
set menuOpen(value) { this.#menuOpen_accessor_storage = value; }
|
|
172
|
+
#searchTerm_accessor_storage = '';
|
|
173
|
+
get searchTerm() { return this.#searchTerm_accessor_storage; }
|
|
174
|
+
set searchTerm(value) { this.#searchTerm_accessor_storage = value; }
|
|
175
|
+
#suggestions_accessor_storage = [];
|
|
176
|
+
/**
|
|
177
|
+
* Results of the last search. Rendered instead of `items` / `sections` while a search term is entered.
|
|
178
|
+
*/
|
|
179
|
+
get suggestions() { return this.#suggestions_accessor_storage; }
|
|
180
|
+
set suggestions(value) { this.#suggestions_accessor_storage = value; }
|
|
181
|
+
#count_accessor_storage = 0;
|
|
182
|
+
get count() { return this.#count_accessor_storage; }
|
|
183
|
+
set count(value) { this.#count_accessor_storage = value; }
|
|
184
|
+
#menuWidth_accessor_storage;
|
|
185
|
+
get menuWidth() { return this.#menuWidth_accessor_storage; }
|
|
186
|
+
set menuWidth(value) { this.#menuWidth_accessor_storage = value; }
|
|
187
|
+
#menu_accessor_storage;
|
|
188
|
+
get menu() { return this.#menu_accessor_storage; }
|
|
189
|
+
set menu(value) { this.#menu_accessor_storage = value; }
|
|
190
|
+
#field_accessor_storage;
|
|
191
|
+
get field() { return this.#field_accessor_storage; }
|
|
192
|
+
set field(value) { this.#field_accessor_storage = value; }
|
|
193
|
+
#searchInput_accessor_storage;
|
|
194
|
+
get searchInput() { return this.#searchInput_accessor_storage; }
|
|
195
|
+
set searchInput(value) { this.#searchInput_accessor_storage = value; }
|
|
196
|
+
#resizeObserver = null;
|
|
197
|
+
#doSearchDebouncer = new Debouncer((searchTerm) => this.#doSearch(searchTerm));
|
|
198
|
+
/**
|
|
199
|
+
* Whether or not the popover is open.
|
|
200
|
+
*/
|
|
201
|
+
get isOpen() {
|
|
202
|
+
return this.menuOpen;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Whether or not `maxSelected` has been reached.
|
|
206
|
+
*/
|
|
207
|
+
get isAtMaxSelected() {
|
|
208
|
+
return this.maxSelected > 0 && this.selected.length >= this.maxSelected;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Items currently rendered in the popover, flattened across sections.
|
|
212
|
+
*/
|
|
213
|
+
get visibleItems() {
|
|
214
|
+
if (this.searchTerm) {
|
|
215
|
+
return this.suggestions;
|
|
216
|
+
}
|
|
217
|
+
return this.sections.length ? this.sections.flatMap((section) => section.items) : this.items;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Items searched by the local search. Falls back to the items of every section when `items` is empty.
|
|
221
|
+
*/
|
|
222
|
+
get searchPool() {
|
|
223
|
+
if (this.items.length) {
|
|
224
|
+
return this.items;
|
|
225
|
+
}
|
|
226
|
+
const seen = new Set();
|
|
227
|
+
return this.sections
|
|
228
|
+
.flatMap((section) => section.items)
|
|
229
|
+
.filter((item) => {
|
|
230
|
+
const key = this.getItemKey(item);
|
|
231
|
+
if (seen.has(key)) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
seen.add(key);
|
|
235
|
+
return true;
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
connectedCallback() {
|
|
239
|
+
super.connectedCallback();
|
|
240
|
+
if (this.matchInputWidth) {
|
|
241
|
+
this.#observeInputWidth();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
disconnectedCallback() {
|
|
245
|
+
super.disconnectedCallback();
|
|
246
|
+
this.#stopObservingInputWidth();
|
|
247
|
+
}
|
|
248
|
+
update(changed) {
|
|
249
|
+
// Firefox does not support popover. Fall-back to using fixed.
|
|
250
|
+
if (changed.has('positioning') && this.positioning === 'popover' && !this.showPopover) {
|
|
251
|
+
this.positioning = 'fixed';
|
|
252
|
+
}
|
|
253
|
+
if (changed.has('matchInputWidth')) {
|
|
254
|
+
if (this.matchInputWidth) {
|
|
255
|
+
this.#observeInputWidth();
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
this.#stopObservingInputWidth();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
super.update(changed);
|
|
262
|
+
}
|
|
263
|
+
updated(changed) {
|
|
264
|
+
// md-menu measures its surface once when it opens. Filtering the list or adding a chip
|
|
265
|
+
// (which grows the anchor) changes those measurements, so ask it to measure again.
|
|
266
|
+
const affectsLayout = ['suggestions', 'searchTerm', 'items', 'sections', 'selected', 'isLoading'];
|
|
267
|
+
const changedProperties = changed;
|
|
268
|
+
if (this.menuOpen && affectsLayout.some((property) => changedProperties.has(property))) {
|
|
269
|
+
this.menu?.reposition();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
#observeInputWidth() {
|
|
273
|
+
this.#resizeObserver?.disconnect();
|
|
274
|
+
this.#resizeObserver = new ResizeObserver((entries) => {
|
|
275
|
+
for (const entry of entries) {
|
|
276
|
+
this.menuWidth = entry.contentRect.width;
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
this.#resizeObserver.observe(this);
|
|
280
|
+
}
|
|
281
|
+
#stopObservingInputWidth() {
|
|
282
|
+
this.#resizeObserver?.disconnect();
|
|
283
|
+
this.#resizeObserver = null;
|
|
284
|
+
this.menuWidth = undefined;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Opens the popover and focuses the search input.
|
|
288
|
+
*/
|
|
289
|
+
open() {
|
|
290
|
+
if (this.disabled) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
this.menu?.show();
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Closes the popover.
|
|
297
|
+
*/
|
|
298
|
+
close() {
|
|
299
|
+
this.menu?.close();
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Opens the popover when closed, closes it when open.
|
|
303
|
+
*/
|
|
304
|
+
toggleMenu() {
|
|
305
|
+
if (this.menuOpen) {
|
|
306
|
+
this.close();
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
this.open();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Sets focus on the field.
|
|
314
|
+
*/
|
|
315
|
+
async focus() {
|
|
316
|
+
this.field?.focus();
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Returns true when the given item is selected.
|
|
320
|
+
*/
|
|
321
|
+
isSelected(item) {
|
|
322
|
+
const key = this.getItemKey(item);
|
|
323
|
+
return this.selected.some((selected) => this.getItemKey(selected) === key);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Adds an item to the selection. Does nothing when it is already selected or `maxSelected` is reached.
|
|
327
|
+
*/
|
|
328
|
+
selectItem(item) {
|
|
329
|
+
if (this.isSelected(item) || this.isAtMaxSelected) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
this.selected = [...this.selected, item];
|
|
333
|
+
this.#notifySelectedChanged();
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Removes an item from the selection.
|
|
337
|
+
*/
|
|
338
|
+
deselectItem(item) {
|
|
339
|
+
const key = this.getItemKey(item);
|
|
340
|
+
if (!this.isSelected(item)) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
this.selected = this.selected.filter((selected) => this.getItemKey(selected) !== key);
|
|
344
|
+
this.#notifySelectedChanged();
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Adds the item when it is not selected, removes it when it is.
|
|
348
|
+
*/
|
|
349
|
+
toggleItem(item) {
|
|
350
|
+
if (this.isSelected(item)) {
|
|
351
|
+
this.deselectItem(item);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
this.selectItem(item);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Removes every selected item.
|
|
359
|
+
*/
|
|
360
|
+
clear() {
|
|
361
|
+
if (!this.selected.length) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
this.selected = [];
|
|
365
|
+
this.#notifySelectedChanged();
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Clears the selection, the search term and the validity state.
|
|
369
|
+
*/
|
|
370
|
+
reset() {
|
|
371
|
+
this.selected = [];
|
|
372
|
+
this.softReset();
|
|
373
|
+
this.error = false;
|
|
374
|
+
this.field?.reset();
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Clears the search term and its results.
|
|
378
|
+
*/
|
|
379
|
+
softReset() {
|
|
380
|
+
this.searchTerm = '';
|
|
381
|
+
this.suggestions = [];
|
|
382
|
+
this.count = 0;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Returns true if the field passes validity checks.
|
|
386
|
+
*/
|
|
387
|
+
checkValidity() {
|
|
388
|
+
return this.field?.checkValidity() ?? true;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Runs checkValidity() method, and if it returns false, then it reports to the user that the field is invalid.
|
|
392
|
+
*/
|
|
393
|
+
reportValidity() {
|
|
394
|
+
return this.field?.reportValidity() ?? true;
|
|
395
|
+
}
|
|
396
|
+
#notifySelectedChanged() {
|
|
397
|
+
// Only re-run validation once the field is already complaining so a required
|
|
398
|
+
// field does not error before the user has interacted with it.
|
|
399
|
+
if (this.field?.error) {
|
|
400
|
+
this.reportValidity();
|
|
401
|
+
}
|
|
402
|
+
this.dispatchEvent(new Event('selected-changed', { composed: true }));
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Called with the current search term after every keystroke. Override to supply remote results
|
|
406
|
+
* and call `showSuggestions()` when they arrive.
|
|
407
|
+
*/
|
|
408
|
+
onInputChanged(searchTerm) {
|
|
409
|
+
this.#doSearchDebouncer.debounce(searchTerm);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Renders the given search results in the popover.
|
|
413
|
+
*/
|
|
414
|
+
async showSuggestions(suggestions, totalSuggestionCount) {
|
|
415
|
+
this.suggestions = suggestions;
|
|
416
|
+
this.count = totalSuggestionCount;
|
|
417
|
+
await this.updateComplete;
|
|
418
|
+
// Results replace the list in place, so bring the top of it back into view.
|
|
419
|
+
this.shadowRoot?.querySelector('md-menu-item')?.scrollIntoView({ block: 'nearest' });
|
|
420
|
+
}
|
|
421
|
+
async #doSearch(searchTerm) {
|
|
422
|
+
// The base may have been cleared while the search was debouncing.
|
|
423
|
+
if (!this.searchTerm || !searchTerm) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const keys = this.searchKeys.length ? this.searchKeys : [this.pathToSelectedText];
|
|
427
|
+
const fuse = new Fuse(this.searchPool, { includeScore: true, keys, shouldSort: true, ignoreLocation: true, threshold: 0.3 });
|
|
428
|
+
const results = fuse.search(searchTerm).map((result) => result.item);
|
|
429
|
+
this.showSuggestions(results, results.length);
|
|
430
|
+
}
|
|
431
|
+
async #onSearchInput(searchTerm) {
|
|
432
|
+
this.searchTerm = searchTerm;
|
|
433
|
+
this.suggestions = [];
|
|
434
|
+
this.count = 0;
|
|
435
|
+
await this.updateComplete;
|
|
436
|
+
this.onInputChanged(searchTerm);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Unique key of an item. Used for list diffing and selection comparisons.
|
|
440
|
+
*/
|
|
441
|
+
getItemKey(item) {
|
|
442
|
+
return String(item?.Id ?? this.getItemText(item));
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Text of an item shown in its chip and menu item.
|
|
446
|
+
*/
|
|
447
|
+
getItemText(item) {
|
|
448
|
+
return String(item?.[this.pathToSelectedText] ?? '');
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Material Symbols icon name of an item, or an empty string when it has none.
|
|
452
|
+
*/
|
|
453
|
+
getItemIcon(item) {
|
|
454
|
+
return this.pathToIcon ? String(item?.[this.pathToIcon] ?? '').trim() : '';
|
|
455
|
+
}
|
|
456
|
+
#menuItemFromEvent(event) {
|
|
457
|
+
for (const target of event.composedPath()) {
|
|
458
|
+
if (target instanceof HTMLElement && target.hasAttribute('md-menu-item')) {
|
|
459
|
+
return target.hasAttribute('disabled') ? null : target;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
#onFieldKeydown(event) {
|
|
465
|
+
// Chips and the clear button live inside the field. Let them handle their own keys.
|
|
466
|
+
if (this.disabled || event.target !== this.field) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {
|
|
470
|
+
event.preventDefault();
|
|
471
|
+
this.open();
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (event.key === 'Backspace') {
|
|
475
|
+
const last = this.selected.at(-1);
|
|
476
|
+
if (last) {
|
|
477
|
+
event.preventDefault();
|
|
478
|
+
this.deselectItem(last);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
#onSearchKeydown(event) {
|
|
483
|
+
// md-menu listens for keydown on itself to drive list navigation. Stop text
|
|
484
|
+
// editing keys from moving focus out of the search input.
|
|
485
|
+
event.stopPropagation();
|
|
486
|
+
switch (event.key) {
|
|
487
|
+
case 'ArrowDown':
|
|
488
|
+
event.preventDefault();
|
|
489
|
+
this.menu?.activateNextItem();
|
|
490
|
+
break;
|
|
491
|
+
case 'ArrowUp':
|
|
492
|
+
event.preventDefault();
|
|
493
|
+
this.menu?.activatePreviousItem();
|
|
494
|
+
break;
|
|
495
|
+
case 'Enter': {
|
|
496
|
+
event.preventDefault();
|
|
497
|
+
const first = this.visibleItems.at(0);
|
|
498
|
+
if (first) {
|
|
499
|
+
this.toggleItem(first);
|
|
500
|
+
}
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
case 'Escape':
|
|
504
|
+
event.preventDefault();
|
|
505
|
+
this.close();
|
|
506
|
+
this.focus();
|
|
507
|
+
break;
|
|
508
|
+
case 'Tab':
|
|
509
|
+
this.close();
|
|
510
|
+
break;
|
|
511
|
+
case 'Backspace': {
|
|
512
|
+
if (this.searchTerm) {
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
const last = this.selected.at(-1);
|
|
516
|
+
if (last) {
|
|
517
|
+
this.deselectItem(last);
|
|
518
|
+
}
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
default:
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
#onMenuClick(event) {
|
|
526
|
+
const menuItem = this.#menuItemFromEvent(event);
|
|
527
|
+
if (!menuItem?.item) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
this.toggleItem(menuItem.item);
|
|
531
|
+
this.searchInput?.focus();
|
|
532
|
+
}
|
|
533
|
+
#onMenuKeydown(event) {
|
|
534
|
+
const menuItem = this.#menuItemFromEvent(event);
|
|
535
|
+
if (!menuItem) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
// Menu items are rendered with `keep-open` so md-menu-item swallows the selection
|
|
539
|
+
// keys instead of closing the menu. Toggle from here so subclasses only render markup.
|
|
540
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
541
|
+
event.preventDefault();
|
|
542
|
+
if (menuItem.item) {
|
|
543
|
+
this.toggleItem(menuItem.item);
|
|
544
|
+
}
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (event.key === 'ArrowUp' && this.menu?.items[0] === menuItem) {
|
|
548
|
+
event.preventDefault();
|
|
549
|
+
event.stopPropagation();
|
|
550
|
+
this.searchInput?.focus();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
static { this.styles = [
|
|
554
|
+
ellipsis,
|
|
555
|
+
css `
|
|
556
|
+
:host {
|
|
557
|
+
display: block;
|
|
558
|
+
position: relative;
|
|
559
|
+
width: 100%;
|
|
560
|
+
|
|
561
|
+
--md-filled-field-container-shape: 16px;
|
|
562
|
+
|
|
563
|
+
--md-filled-field-active-indicator-height: 0;
|
|
564
|
+
--md-filled-field-error-active-indicator-height: 0;
|
|
565
|
+
--md-filled-field-hover-active-indicator-height: 0;
|
|
566
|
+
--md-filled-field-focus-active-indicator-height: 0;
|
|
567
|
+
--md-filled-field-disabled-active-indicator-height: 0;
|
|
568
|
+
|
|
569
|
+
--md-menu-container-shape: 16px;
|
|
570
|
+
--md-menu-top-space: 0;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
titanium-filled-input-validator {
|
|
574
|
+
display: block;
|
|
575
|
+
width: 100%;
|
|
576
|
+
cursor: pointer;
|
|
577
|
+
outline: none;
|
|
578
|
+
--md-filled-field-with-label-bottom-space: 12px;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
:host([disabled]) titanium-filled-input-validator {
|
|
582
|
+
cursor: default;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
chips-container {
|
|
586
|
+
display: flex;
|
|
587
|
+
flex-wrap: wrap;
|
|
588
|
+
align-items: center;
|
|
589
|
+
gap: 8px;
|
|
590
|
+
margin-top: 6px;
|
|
591
|
+
min-height: 32px;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
span[placeholder] {
|
|
595
|
+
font-size: 14px;
|
|
596
|
+
line-height: 32px;
|
|
597
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
div[trailing] {
|
|
601
|
+
display: flex;
|
|
602
|
+
align-items: center;
|
|
603
|
+
gap: 4px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
md-circular-progress {
|
|
607
|
+
--md-circular-progress-size: 24px;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
md-icon-button {
|
|
611
|
+
--md-icon-button-icon-size: 20px;
|
|
612
|
+
--md-icon-button-state-layer-height: 32px;
|
|
613
|
+
--md-icon-button-state-layer-width: 32px;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
md-menu {
|
|
617
|
+
min-width: 280px;
|
|
618
|
+
max-height: var(--titanium-multi-select-menu-max-height, 460px);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
md-menu-item,
|
|
622
|
+
div[section-header] {
|
|
623
|
+
/* Keyboard navigation scrolls items into view — leave room for the sticky search row. */
|
|
624
|
+
scroll-margin-top: var(--titanium-multi-select-search-height, 52px);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
md-menu-item {
|
|
628
|
+
min-width: 280px;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
search-container {
|
|
632
|
+
display: flex;
|
|
633
|
+
align-items: center;
|
|
634
|
+
gap: 12px;
|
|
635
|
+
box-sizing: border-box;
|
|
636
|
+
height: var(--titanium-multi-select-search-height, 52px);
|
|
637
|
+
padding: 0 16px;
|
|
638
|
+
|
|
639
|
+
position: sticky;
|
|
640
|
+
top: 0;
|
|
641
|
+
z-index: 2;
|
|
642
|
+
|
|
643
|
+
background-color: var(--md-menu-container-color, var(--md-sys-color-surface-container));
|
|
644
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
search-container md-icon {
|
|
648
|
+
--md-icon-size: 20px;
|
|
649
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
input[search] {
|
|
653
|
+
flex: 1 1 auto;
|
|
654
|
+
min-width: 0;
|
|
655
|
+
appearance: none;
|
|
656
|
+
border: none;
|
|
657
|
+
outline: none;
|
|
658
|
+
background: none;
|
|
659
|
+
padding: 0;
|
|
660
|
+
margin: 0;
|
|
661
|
+
|
|
662
|
+
/* md-menu sets user-select: none on its surface. */
|
|
663
|
+
user-select: text;
|
|
664
|
+
|
|
665
|
+
font-family: var(--titanium-styles-p-font-family, Roboto, Noto, sans-serif);
|
|
666
|
+
font-size: 14px;
|
|
667
|
+
line-height: 20px;
|
|
668
|
+
color: var(--md-sys-color-on-surface);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
input[search]::placeholder {
|
|
672
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
input[search]::-webkit-search-cancel-button {
|
|
676
|
+
display: none;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
div[section-header] {
|
|
680
|
+
display: flex;
|
|
681
|
+
align-items: center;
|
|
682
|
+
gap: 12px;
|
|
683
|
+
padding: 8px 16px;
|
|
684
|
+
|
|
685
|
+
position: sticky;
|
|
686
|
+
top: var(--titanium-multi-select-search-height, 52px);
|
|
687
|
+
z-index: 1;
|
|
688
|
+
|
|
689
|
+
background-color: var(--md-sys-color-surface-container-high);
|
|
690
|
+
color: var(--md-sys-color-on-surface);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
div[section-header] md-icon {
|
|
694
|
+
--md-icon-size: 18px;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
div[section-header] [headline] {
|
|
698
|
+
font-size: 13px;
|
|
699
|
+
font-weight: 500;
|
|
700
|
+
line-height: 18px;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
div[section-header] [supporting-text] {
|
|
704
|
+
font-size: 12px;
|
|
705
|
+
line-height: 16px;
|
|
706
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
div[summary],
|
|
710
|
+
div[empty] {
|
|
711
|
+
font-family: var(--titanium-styles-p-font-family, Roboto, Noto, sans-serif);
|
|
712
|
+
font-size: 13px;
|
|
713
|
+
line-height: 18px;
|
|
714
|
+
padding: 12px 16px;
|
|
715
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
[hidden] {
|
|
719
|
+
display: none !important;
|
|
720
|
+
}
|
|
721
|
+
`,
|
|
722
|
+
]; }
|
|
723
|
+
/**
|
|
724
|
+
* Renders the chip of a selected item. Override to customize.
|
|
725
|
+
*/
|
|
726
|
+
renderChip(item) {
|
|
727
|
+
const icon = this.getItemIcon(item);
|
|
728
|
+
return html `<titanium-chip input-chip ?disabled=${this.disabled} label=${this.getItemText(item)} @remove=${() => this.deselectItem(item)}>
|
|
729
|
+
${icon ? html `<md-icon slot="icon">${icon}</md-icon>` : nothing}
|
|
730
|
+
</titanium-chip>`;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Renders a selectable item. Override to customize. Bind the item to the `item` property of the
|
|
734
|
+
* `md-menu-item` and keep `keep-open` so the popover stays open on selection.
|
|
735
|
+
*/
|
|
736
|
+
renderSuggestion(item) {
|
|
737
|
+
const isSelected = this.isSelected(item);
|
|
738
|
+
const icon = this.getItemIcon(item);
|
|
739
|
+
return html `<md-menu-item keep-open .item=${item} ?selected=${isSelected} ?disabled=${!isSelected && this.isAtMaxSelected}>
|
|
740
|
+
${icon ? html `<md-icon slot="start">${icon}</md-icon>` : nothing}
|
|
741
|
+
<span slot="headline" ellipsis>${this.getItemText(item)}</span>
|
|
742
|
+
${isSelected ? html `<md-icon slot="end">check</md-icon>` : nothing}
|
|
743
|
+
</md-menu-item>`;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Renders the header of a section. Override to customize.
|
|
747
|
+
*/
|
|
748
|
+
renderSectionHeader(section) {
|
|
749
|
+
return html `<div section-header>
|
|
750
|
+
${section.icon ? html `<md-icon>${section.icon}</md-icon>` : nothing}
|
|
751
|
+
<div>
|
|
752
|
+
<div headline>${section.headline}</div>
|
|
753
|
+
${section.supportingText ? html `<div supporting-text>${section.supportingText}</div>` : nothing}
|
|
754
|
+
</div>
|
|
755
|
+
</div>`;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Renders content at the bottom of the popover, below the selectable items.
|
|
759
|
+
*/
|
|
760
|
+
renderTrailingMenuSlot() {
|
|
761
|
+
return nothing;
|
|
762
|
+
}
|
|
763
|
+
#renderSuggestions() {
|
|
764
|
+
if (this.searchTerm) {
|
|
765
|
+
return html `${this.isLoading || this.suggestions.length
|
|
766
|
+
? html `<div summary>Showing ${this.suggestions.length} of ${this.count} result${this.count === 1 ? '' : 's'} for '${this.searchTerm}'</div>`
|
|
767
|
+
: html `<div empty>${this.noResultsText}</div>`}
|
|
768
|
+
${repeat(this.suggestions, (item) => this.getItemKey(item), (item) => this.renderSuggestion(item))}`;
|
|
769
|
+
}
|
|
770
|
+
if (this.sections.length) {
|
|
771
|
+
return repeat(this.sections, (section) => section.key, (section) => html `${this.renderSectionHeader(section)}
|
|
772
|
+
${repeat(section.items, (item) => this.getItemKey(item), (item) => this.renderSuggestion(item))}`);
|
|
773
|
+
}
|
|
774
|
+
if (!this.items.length) {
|
|
775
|
+
return html `<div empty>${this.noItemsText}</div>`;
|
|
776
|
+
}
|
|
777
|
+
return repeat(this.items, (item) => this.getItemKey(item), (item) => this.renderSuggestion(item));
|
|
778
|
+
}
|
|
779
|
+
render() {
|
|
780
|
+
return html `
|
|
781
|
+
<titanium-filled-input-validator
|
|
782
|
+
id="field"
|
|
783
|
+
role="combobox"
|
|
784
|
+
aria-haspopup="listbox"
|
|
785
|
+
aria-controls="menu"
|
|
786
|
+
aria-expanded=${this.menuOpen ? 'true' : 'false'}
|
|
787
|
+
tabindex=${this.disabled ? '-1' : '0'}
|
|
788
|
+
populated
|
|
789
|
+
?has-start=${this.hasLeadingIcon}
|
|
790
|
+
has-end
|
|
791
|
+
?disabled=${this.disabled}
|
|
792
|
+
?error=${this.error}
|
|
793
|
+
?required=${this.required}
|
|
794
|
+
?no-asterisk=${this.noAsterisk}
|
|
795
|
+
.label=${this.label}
|
|
796
|
+
.errorText=${this.errorText}
|
|
797
|
+
.supportingText=${this.supportingText}
|
|
798
|
+
.evaluator=${() => !this.required || this.selected.length > 0}
|
|
799
|
+
@click=${() => this.toggleMenu()}
|
|
800
|
+
@keydown=${this.#onFieldKeydown}
|
|
801
|
+
>
|
|
802
|
+
${this.hasLeadingIcon ? html `<slot name="leading-icon" slot="start"></slot>` : nothing}
|
|
803
|
+
<chips-container>
|
|
804
|
+
${this.selected.length
|
|
805
|
+
? repeat(this.selected, (item) => this.getItemKey(item), (item) => this.renderChip(item))
|
|
806
|
+
: html `<span placeholder>${this.placeholder}</span>`}
|
|
807
|
+
</chips-container>
|
|
808
|
+
<div slot="end" trailing>
|
|
809
|
+
${this.isLoading ? html `<md-circular-progress indeterminate></md-circular-progress>` : nothing}
|
|
810
|
+
${this.selected.length && !this.disableClearAll && !this.disabled
|
|
811
|
+
? html `<md-icon-button
|
|
812
|
+
title="Clear selection"
|
|
813
|
+
@click=${(e) => {
|
|
814
|
+
e.preventDefault();
|
|
815
|
+
e.stopPropagation();
|
|
816
|
+
this.clear();
|
|
817
|
+
this.focus();
|
|
818
|
+
}}
|
|
819
|
+
>
|
|
820
|
+
<md-icon>close</md-icon>
|
|
821
|
+
</md-icon-button>`
|
|
822
|
+
: nothing}
|
|
823
|
+
<md-icon>${this.menuOpen ? 'arrow_drop_up' : 'arrow_drop_down'}</md-icon>
|
|
824
|
+
</div>
|
|
825
|
+
</titanium-filled-input-validator>
|
|
826
|
+
|
|
827
|
+
<md-menu
|
|
828
|
+
part="menu"
|
|
829
|
+
id="menu"
|
|
830
|
+
anchor="field"
|
|
831
|
+
anchor-corner="end-start"
|
|
832
|
+
default-focus="none"
|
|
833
|
+
no-navigation-wrap
|
|
834
|
+
stay-open-on-focusout
|
|
835
|
+
.positioning=${this.positioning}
|
|
836
|
+
style=${this.matchInputWidth && this.menuWidth ? `min-width: ${this.menuWidth}px; max-width: ${this.menuWidth}px` : ''}
|
|
837
|
+
@click=${this.#onMenuClick}
|
|
838
|
+
@keydown=${this.#onMenuKeydown}
|
|
839
|
+
@opening=${async (e) => {
|
|
840
|
+
this.menuOpen = true;
|
|
841
|
+
redispatchEvent(this, e);
|
|
842
|
+
await this.updateComplete;
|
|
843
|
+
// md-menu drives typeahead from its own keydown listener which would steal
|
|
844
|
+
// keystrokes meant for the search input.
|
|
845
|
+
this.searchInput?.dispatchEvent(new Event('deactivate-typeahead', { bubbles: true, composed: true }));
|
|
846
|
+
// The menu surface is still growing into place; scrolling the sticky search
|
|
847
|
+
// row into view here would leave the list scrolled under it.
|
|
848
|
+
this.searchInput?.focus({ preventScroll: true });
|
|
849
|
+
}}
|
|
850
|
+
@opened=${(e) => redispatchEvent(this, e)}
|
|
851
|
+
@closing=${(e) => {
|
|
852
|
+
this.menuOpen = false;
|
|
853
|
+
this.softReset();
|
|
854
|
+
redispatchEvent(this, e);
|
|
855
|
+
}}
|
|
856
|
+
@closed=${(e) => {
|
|
857
|
+
this.reportValidity();
|
|
858
|
+
redispatchEvent(this, e);
|
|
859
|
+
}}
|
|
860
|
+
>
|
|
861
|
+
<search-container>
|
|
862
|
+
<md-icon>search</md-icon>
|
|
863
|
+
<input
|
|
864
|
+
search
|
|
865
|
+
id="search"
|
|
866
|
+
type="search"
|
|
867
|
+
autocomplete="off"
|
|
868
|
+
spellcheck="false"
|
|
869
|
+
aria-label=${this.searchPlaceholder}
|
|
870
|
+
placeholder=${this.searchPlaceholder}
|
|
871
|
+
.value=${this.searchTerm}
|
|
872
|
+
@input=${(e) => this.#onSearchInput(e.target.value)}
|
|
873
|
+
@keydown=${this.#onSearchKeydown}
|
|
874
|
+
/>
|
|
875
|
+
${this.isLoading ? html `<md-circular-progress indeterminate></md-circular-progress>` : nothing}
|
|
876
|
+
${this.searchTerm && !this.isLoading
|
|
877
|
+
? html `<md-icon-button
|
|
878
|
+
title="Clear search"
|
|
879
|
+
@click=${() => {
|
|
880
|
+
this.#onSearchInput('');
|
|
881
|
+
this.searchInput?.focus();
|
|
882
|
+
}}
|
|
883
|
+
>
|
|
884
|
+
<md-icon>close</md-icon>
|
|
885
|
+
</md-icon-button>`
|
|
886
|
+
: nothing}
|
|
887
|
+
</search-container>
|
|
888
|
+
${this.#renderSuggestions()} ${this.renderTrailingMenuSlot()}
|
|
889
|
+
</md-menu>
|
|
890
|
+
`;
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
__decorate([
|
|
894
|
+
promiseTracking('trackLoadingPromise'),
|
|
895
|
+
state()
|
|
896
|
+
], TitaniumMultiSelectBase.prototype, "isLoading", null);
|
|
897
|
+
__decorate([
|
|
898
|
+
property({ type: String })
|
|
899
|
+
], TitaniumMultiSelectBase.prototype, "label", null);
|
|
900
|
+
__decorate([
|
|
901
|
+
property({ type: String })
|
|
902
|
+
], TitaniumMultiSelectBase.prototype, "placeholder", null);
|
|
903
|
+
__decorate([
|
|
904
|
+
property({ type: String, attribute: 'search-placeholder' })
|
|
905
|
+
], TitaniumMultiSelectBase.prototype, "searchPlaceholder", null);
|
|
906
|
+
__decorate([
|
|
907
|
+
property({ type: Array })
|
|
908
|
+
], TitaniumMultiSelectBase.prototype, "selected", null);
|
|
909
|
+
__decorate([
|
|
910
|
+
property({ type: Array })
|
|
911
|
+
], TitaniumMultiSelectBase.prototype, "items", null);
|
|
912
|
+
__decorate([
|
|
913
|
+
property({ type: Array })
|
|
914
|
+
], TitaniumMultiSelectBase.prototype, "sections", null);
|
|
915
|
+
__decorate([
|
|
916
|
+
property({ type: String })
|
|
917
|
+
], TitaniumMultiSelectBase.prototype, "pathToSelectedText", null);
|
|
918
|
+
__decorate([
|
|
919
|
+
property({ type: String })
|
|
920
|
+
], TitaniumMultiSelectBase.prototype, "pathToIcon", null);
|
|
921
|
+
__decorate([
|
|
922
|
+
property({ type: Array })
|
|
923
|
+
], TitaniumMultiSelectBase.prototype, "searchKeys", null);
|
|
924
|
+
__decorate([
|
|
925
|
+
property({ type: Number, attribute: 'max-selected' })
|
|
926
|
+
], TitaniumMultiSelectBase.prototype, "maxSelected", null);
|
|
927
|
+
__decorate([
|
|
928
|
+
property({ type: Boolean })
|
|
929
|
+
], TitaniumMultiSelectBase.prototype, "required", null);
|
|
930
|
+
__decorate([
|
|
931
|
+
property({ type: Boolean, reflect: true })
|
|
932
|
+
], TitaniumMultiSelectBase.prototype, "disabled", null);
|
|
933
|
+
__decorate([
|
|
934
|
+
property({ type: Boolean })
|
|
935
|
+
], TitaniumMultiSelectBase.prototype, "error", null);
|
|
936
|
+
__decorate([
|
|
937
|
+
property({ type: String })
|
|
938
|
+
], TitaniumMultiSelectBase.prototype, "errorText", null);
|
|
939
|
+
__decorate([
|
|
940
|
+
property({ type: String })
|
|
941
|
+
], TitaniumMultiSelectBase.prototype, "supportingText", null);
|
|
942
|
+
__decorate([
|
|
943
|
+
property({ type: Boolean, attribute: 'no-asterisk' })
|
|
944
|
+
], TitaniumMultiSelectBase.prototype, "noAsterisk", null);
|
|
945
|
+
__decorate([
|
|
946
|
+
property({ type: Boolean, attribute: 'has-leading-icon' })
|
|
947
|
+
], TitaniumMultiSelectBase.prototype, "hasLeadingIcon", null);
|
|
948
|
+
__decorate([
|
|
949
|
+
property({ type: String, attribute: 'no-items-text' })
|
|
950
|
+
], TitaniumMultiSelectBase.prototype, "noItemsText", null);
|
|
951
|
+
__decorate([
|
|
952
|
+
property({ type: String, attribute: 'no-results-text' })
|
|
953
|
+
], TitaniumMultiSelectBase.prototype, "noResultsText", null);
|
|
954
|
+
__decorate([
|
|
955
|
+
property({ type: Boolean, attribute: 'disable-clear-all' })
|
|
956
|
+
], TitaniumMultiSelectBase.prototype, "disableClearAll", null);
|
|
957
|
+
__decorate([
|
|
958
|
+
property()
|
|
959
|
+
], TitaniumMultiSelectBase.prototype, "positioning", null);
|
|
960
|
+
__decorate([
|
|
961
|
+
property({ type: Boolean, attribute: 'match-input-width' })
|
|
962
|
+
], TitaniumMultiSelectBase.prototype, "matchInputWidth", null);
|
|
963
|
+
__decorate([
|
|
964
|
+
property({ type: Boolean, attribute: 'menu-open', reflect: true })
|
|
965
|
+
], TitaniumMultiSelectBase.prototype, "menuOpen", null);
|
|
966
|
+
__decorate([
|
|
967
|
+
state()
|
|
968
|
+
], TitaniumMultiSelectBase.prototype, "searchTerm", null);
|
|
969
|
+
__decorate([
|
|
970
|
+
state()
|
|
971
|
+
], TitaniumMultiSelectBase.prototype, "suggestions", null);
|
|
972
|
+
__decorate([
|
|
973
|
+
state()
|
|
974
|
+
], TitaniumMultiSelectBase.prototype, "count", null);
|
|
975
|
+
__decorate([
|
|
976
|
+
state()
|
|
977
|
+
], TitaniumMultiSelectBase.prototype, "menuWidth", null);
|
|
978
|
+
__decorate([
|
|
979
|
+
query('md-menu')
|
|
980
|
+
], TitaniumMultiSelectBase.prototype, "menu", null);
|
|
981
|
+
__decorate([
|
|
982
|
+
query('titanium-filled-input-validator')
|
|
983
|
+
], TitaniumMultiSelectBase.prototype, "field", null);
|
|
984
|
+
__decorate([
|
|
985
|
+
query('input[search]')
|
|
986
|
+
], TitaniumMultiSelectBase.prototype, "searchInput", null);
|
|
987
|
+
TitaniumMultiSelectBase = __decorate([
|
|
988
|
+
customElement('titanium-multi-select-base')
|
|
989
|
+
], TitaniumMultiSelectBase);
|
|
990
|
+
export { TitaniumMultiSelectBase };
|
|
991
|
+
//# sourceMappingURL=multi-select-base.js.map
|