@duskmoon-dev/el-autocomplete 0.5.0 → 0.7.0
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/README.md +123 -0
- package/dist/cjs/index.js +100 -28
- package/dist/cjs/register.js +100 -28
- package/dist/esm/index.js +98 -26
- package/dist/esm/register.js +98 -26
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/el-dm-autocomplete.d.ts +4 -10
- package/dist/types/el-dm-autocomplete.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/el-dm-autocomplete.test.ts +216 -0
- package/src/el-dm-autocomplete.ts +129 -27
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# @duskmoon-dev/el-autocomplete
|
|
2
|
+
|
|
3
|
+
An input component with autocomplete suggestions built with Web Components.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @duskmoon-dev/el-autocomplete
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Auto-Register
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import '@duskmoon-dev/el-autocomplete/register';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<el-dm-autocomplete placeholder="Search..."></el-dm-autocomplete>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Manual Registration
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { ElDmAutocomplete, register } from '@duskmoon-dev/el-autocomplete';
|
|
27
|
+
|
|
28
|
+
// Register with default tag name
|
|
29
|
+
register();
|
|
30
|
+
|
|
31
|
+
// Or register with custom tag name
|
|
32
|
+
customElements.define('my-autocomplete', ElDmAutocomplete);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Setting Options via JavaScript
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
const autocomplete = document.querySelector('el-dm-autocomplete');
|
|
39
|
+
autocomplete.options = [
|
|
40
|
+
{ value: 'apple', label: 'Apple' },
|
|
41
|
+
{ value: 'banana', label: 'Banana' },
|
|
42
|
+
{ value: 'cherry', label: 'Cherry' },
|
|
43
|
+
];
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Sizes
|
|
47
|
+
|
|
48
|
+
| Size | Description |
|
|
49
|
+
|------|-------------|
|
|
50
|
+
| `sm` | Small input |
|
|
51
|
+
| `md` | Medium input (default) |
|
|
52
|
+
| `lg` | Large input |
|
|
53
|
+
|
|
54
|
+
## Attributes
|
|
55
|
+
|
|
56
|
+
| Attribute | Type | Default | Description |
|
|
57
|
+
|-----------|------|---------|-------------|
|
|
58
|
+
| `value` | `string` | `''` | Current value |
|
|
59
|
+
| `placeholder` | `string` | `''` | Placeholder text |
|
|
60
|
+
| `disabled` | `boolean` | `false` | Disable the input |
|
|
61
|
+
| `multiple` | `boolean` | `false` | Allow multiple selections |
|
|
62
|
+
| `clearable` | `boolean` | `false` | Show clear button |
|
|
63
|
+
| `size` | `string` | `'md'` | Size variant: `sm`, `md`, `lg` |
|
|
64
|
+
| `loading` | `boolean` | `false` | Show loading state |
|
|
65
|
+
| `no-results-text` | `string` | `'No results'` | Text when no results |
|
|
66
|
+
|
|
67
|
+
## Events
|
|
68
|
+
|
|
69
|
+
| Event | Detail | Description |
|
|
70
|
+
|-------|--------|-------------|
|
|
71
|
+
| `change` | `{ value }` | Fired when value changes |
|
|
72
|
+
| `input` | `{ value }` | Fired during typing |
|
|
73
|
+
| `clear` | - | Fired when cleared |
|
|
74
|
+
|
|
75
|
+
## Examples
|
|
76
|
+
|
|
77
|
+
### Basic
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<el-dm-autocomplete placeholder="Search fruits..."></el-dm-autocomplete>
|
|
81
|
+
<script>
|
|
82
|
+
const ac = document.querySelector('el-dm-autocomplete');
|
|
83
|
+
ac.options = [
|
|
84
|
+
{ value: 'apple', label: 'Apple' },
|
|
85
|
+
{ value: 'banana', label: 'Banana' },
|
|
86
|
+
{ value: 'cherry', label: 'Cherry' },
|
|
87
|
+
];
|
|
88
|
+
</script>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Clearable
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<el-dm-autocomplete clearable></el-dm-autocomplete>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Multiple Selection
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<el-dm-autocomplete multiple></el-dm-autocomplete>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Custom No Results Text
|
|
104
|
+
|
|
105
|
+
```html
|
|
106
|
+
<el-dm-autocomplete no-results-text="Nothing found"></el-dm-autocomplete>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### With Loading State
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
const ac = document.querySelector('el-dm-autocomplete');
|
|
113
|
+
ac.addEventListener('input', async (e) => {
|
|
114
|
+
ac.loading = true;
|
|
115
|
+
const results = await fetchSuggestions(e.detail.value);
|
|
116
|
+
ac.options = results;
|
|
117
|
+
ac.loading = false;
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT
|
package/dist/cjs/index.js
CHANGED
|
@@ -35,10 +35,10 @@ __export(exports_src, {
|
|
|
35
35
|
module.exports = __toCommonJS(exports_src);
|
|
36
36
|
|
|
37
37
|
// src/el-dm-autocomplete.ts
|
|
38
|
-
var
|
|
38
|
+
var import_el_base = require("@duskmoon-dev/el-base");
|
|
39
39
|
var import_autocomplete = require("@duskmoon-dev/core/components/autocomplete");
|
|
40
40
|
var strippedCss = import_autocomplete.css.replace(/@layer\s+components\s*\{/, "").replace(/\}[\s]*$/, "");
|
|
41
|
-
var styles =
|
|
41
|
+
var styles = import_el_base.css`
|
|
42
42
|
${strippedCss}
|
|
43
43
|
|
|
44
44
|
:host {
|
|
@@ -52,33 +52,44 @@ var styles = import_el_core.css`
|
|
|
52
52
|
.autocomplete {
|
|
53
53
|
width: 100%;
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
/* Popover API styles for dropdown — override core's absolute positioning
|
|
57
|
+
and visibility rules since the top layer breaks parent-child CSS selectors */
|
|
58
|
+
.autocomplete-dropdown {
|
|
59
|
+
position: fixed;
|
|
60
|
+
margin: 0;
|
|
61
|
+
border: none;
|
|
62
|
+
padding: 0;
|
|
63
|
+
inset: unset;
|
|
64
|
+
/* Override core's hidden defaults — popover API controls visibility */
|
|
65
|
+
opacity: 1;
|
|
66
|
+
visibility: visible;
|
|
67
|
+
transform: none;
|
|
68
|
+
transition: none;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.autocomplete-dropdown:popover-open {
|
|
72
|
+
display: block;
|
|
73
|
+
}
|
|
55
74
|
`;
|
|
56
75
|
|
|
57
|
-
class ElDmAutocomplete extends
|
|
76
|
+
class ElDmAutocomplete extends import_el_base.BaseElement {
|
|
58
77
|
static properties = {
|
|
59
|
-
value: { type: String, reflect: true
|
|
60
|
-
options: { type: String, reflect: true
|
|
61
|
-
multiple: { type: Boolean, reflect: true
|
|
62
|
-
disabled: { type: Boolean, reflect: true
|
|
63
|
-
clearable: { type: Boolean, reflect: true
|
|
64
|
-
placeholder: { type: String, reflect: true
|
|
65
|
-
size: { type: String, reflect: true
|
|
66
|
-
loading: { type: Boolean, reflect: true
|
|
67
|
-
noResultsText: { type: String, reflect: true,
|
|
78
|
+
value: { type: String, reflect: true },
|
|
79
|
+
options: { type: String, reflect: true },
|
|
80
|
+
multiple: { type: Boolean, reflect: true },
|
|
81
|
+
disabled: { type: Boolean, reflect: true },
|
|
82
|
+
clearable: { type: Boolean, reflect: true },
|
|
83
|
+
placeholder: { type: String, reflect: true },
|
|
84
|
+
size: { type: String, reflect: true },
|
|
85
|
+
loading: { type: Boolean, reflect: true },
|
|
86
|
+
noResultsText: { type: String, reflect: true, attribute: "no-results-text" }
|
|
68
87
|
};
|
|
69
|
-
value;
|
|
70
|
-
options;
|
|
71
|
-
multiple;
|
|
72
|
-
disabled;
|
|
73
|
-
clearable;
|
|
74
|
-
placeholder;
|
|
75
|
-
size;
|
|
76
|
-
loading;
|
|
77
|
-
noResultsText;
|
|
78
88
|
_isOpen = false;
|
|
79
89
|
_searchValue = "";
|
|
80
90
|
_highlightedIndex = -1;
|
|
81
91
|
_selectedValues = [];
|
|
92
|
+
_scrollHandler = null;
|
|
82
93
|
constructor() {
|
|
83
94
|
super();
|
|
84
95
|
this.attachStyles(styles);
|
|
@@ -91,6 +102,11 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
91
102
|
disconnectedCallback() {
|
|
92
103
|
super.disconnectedCallback();
|
|
93
104
|
document.removeEventListener("click", this._handleOutsideClick);
|
|
105
|
+
if (this._scrollHandler) {
|
|
106
|
+
window.removeEventListener("scroll", this._scrollHandler, true);
|
|
107
|
+
window.removeEventListener("resize", this._scrollHandler);
|
|
108
|
+
this._scrollHandler = null;
|
|
109
|
+
}
|
|
94
110
|
}
|
|
95
111
|
_handleOutsideClick = (e) => {
|
|
96
112
|
if (!this.contains(e.target)) {
|
|
@@ -110,7 +126,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
110
126
|
}
|
|
111
127
|
_getOptions() {
|
|
112
128
|
try {
|
|
113
|
-
return JSON.parse(this.options);
|
|
129
|
+
return JSON.parse(this.options || "[]");
|
|
114
130
|
} catch {
|
|
115
131
|
return [];
|
|
116
132
|
}
|
|
@@ -128,13 +144,57 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
128
144
|
this._isOpen = true;
|
|
129
145
|
this._highlightedIndex = -1;
|
|
130
146
|
this.update();
|
|
147
|
+
const dropdown = this.shadowRoot?.querySelector(".autocomplete-dropdown");
|
|
148
|
+
const trigger = this.shadowRoot?.querySelector(".autocomplete-input, .autocomplete-tags");
|
|
149
|
+
if (dropdown && trigger) {
|
|
150
|
+
try {
|
|
151
|
+
dropdown.showPopover();
|
|
152
|
+
this._positionDropdown(dropdown, trigger);
|
|
153
|
+
} catch {}
|
|
154
|
+
this._scrollHandler = () => {
|
|
155
|
+
this._positionDropdown(dropdown, trigger);
|
|
156
|
+
};
|
|
157
|
+
window.addEventListener("scroll", this._scrollHandler, true);
|
|
158
|
+
window.addEventListener("resize", this._scrollHandler);
|
|
159
|
+
}
|
|
131
160
|
}
|
|
132
161
|
_close() {
|
|
162
|
+
if (!this._isOpen)
|
|
163
|
+
return;
|
|
133
164
|
this._isOpen = false;
|
|
134
165
|
this._searchValue = "";
|
|
135
166
|
this._highlightedIndex = -1;
|
|
167
|
+
if (this._scrollHandler) {
|
|
168
|
+
window.removeEventListener("scroll", this._scrollHandler, true);
|
|
169
|
+
window.removeEventListener("resize", this._scrollHandler);
|
|
170
|
+
this._scrollHandler = null;
|
|
171
|
+
}
|
|
172
|
+
const dropdown = this.shadowRoot?.querySelector(".autocomplete-dropdown");
|
|
173
|
+
if (dropdown) {
|
|
174
|
+
try {
|
|
175
|
+
dropdown.hidePopover();
|
|
176
|
+
} catch {}
|
|
177
|
+
}
|
|
136
178
|
this.update();
|
|
137
179
|
}
|
|
180
|
+
_positionDropdown(dropdown, trigger) {
|
|
181
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
182
|
+
const dropdownRect = dropdown.getBoundingClientRect();
|
|
183
|
+
let top = triggerRect.bottom + 4;
|
|
184
|
+
let left = triggerRect.left;
|
|
185
|
+
if (top + dropdownRect.height > window.innerHeight) {
|
|
186
|
+
top = triggerRect.top - dropdownRect.height - 4;
|
|
187
|
+
}
|
|
188
|
+
if (left + triggerRect.width > window.innerWidth) {
|
|
189
|
+
left = window.innerWidth - triggerRect.width - 8;
|
|
190
|
+
}
|
|
191
|
+
if (left < 8) {
|
|
192
|
+
left = 8;
|
|
193
|
+
}
|
|
194
|
+
dropdown.style.top = `${top}px`;
|
|
195
|
+
dropdown.style.left = `${left}px`;
|
|
196
|
+
dropdown.style.width = `${triggerRect.width}px`;
|
|
197
|
+
}
|
|
138
198
|
_toggle() {
|
|
139
199
|
if (this._isOpen) {
|
|
140
200
|
this._close();
|
|
@@ -282,7 +342,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
282
342
|
}
|
|
283
343
|
if (filteredOptions.length === 0) {
|
|
284
344
|
return `
|
|
285
|
-
<div class="autocomplete-no-results">${this.noResultsText}</div>
|
|
345
|
+
<div class="autocomplete-no-results">${this.noResultsText || "No results found"}</div>
|
|
286
346
|
`;
|
|
287
347
|
}
|
|
288
348
|
const groups = new Map;
|
|
@@ -340,10 +400,12 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
340
400
|
`;
|
|
341
401
|
}
|
|
342
402
|
render() {
|
|
343
|
-
const
|
|
403
|
+
const size = this.size || "md";
|
|
404
|
+
const sizeClass = size !== "md" ? `autocomplete-${size}` : "";
|
|
344
405
|
const openClass = this._isOpen ? "autocomplete-open" : "";
|
|
345
406
|
const clearableClass = this.clearable ? "autocomplete-clearable" : "";
|
|
346
407
|
const showClear = this.clearable && this._selectedValues.length > 0 && !this.disabled;
|
|
408
|
+
const placeholder = this.placeholder || "";
|
|
347
409
|
if (this.multiple) {
|
|
348
410
|
return `
|
|
349
411
|
<div class="autocomplete ${sizeClass} ${openClass} ${clearableClass}">
|
|
@@ -352,7 +414,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
352
414
|
<input
|
|
353
415
|
type="text"
|
|
354
416
|
class="autocomplete-tags-input"
|
|
355
|
-
placeholder="${this._selectedValues.length === 0 ?
|
|
417
|
+
placeholder="${this._selectedValues.length === 0 ? placeholder : ""}"
|
|
356
418
|
value="${this._searchValue}"
|
|
357
419
|
${this.disabled ? "disabled" : ""}
|
|
358
420
|
role="combobox"
|
|
@@ -364,7 +426,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
364
426
|
${showClear ? `
|
|
365
427
|
<button type="button" class="autocomplete-clear" aria-label="Clear selection">×</button>
|
|
366
428
|
` : ""}
|
|
367
|
-
<div class="autocomplete-dropdown" role="listbox">
|
|
429
|
+
<div class="autocomplete-dropdown" role="listbox" popover="manual">
|
|
368
430
|
${this._renderOptions()}
|
|
369
431
|
</div>
|
|
370
432
|
</div>
|
|
@@ -375,7 +437,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
375
437
|
<input
|
|
376
438
|
type="text"
|
|
377
439
|
class="autocomplete-input"
|
|
378
|
-
placeholder="${
|
|
440
|
+
placeholder="${placeholder}"
|
|
379
441
|
value="${this._isOpen ? this._searchValue : this._getDisplayValue()}"
|
|
380
442
|
${this.disabled ? "disabled" : ""}
|
|
381
443
|
role="combobox"
|
|
@@ -386,7 +448,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
386
448
|
${showClear ? `
|
|
387
449
|
<button type="button" class="autocomplete-clear" aria-label="Clear selection">×</button>
|
|
388
450
|
` : ""}
|
|
389
|
-
<div class="autocomplete-dropdown" role="listbox">
|
|
451
|
+
<div class="autocomplete-dropdown" role="listbox" popover="manual">
|
|
390
452
|
${this._renderOptions()}
|
|
391
453
|
</div>
|
|
392
454
|
</div>
|
|
@@ -395,6 +457,16 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
395
457
|
update() {
|
|
396
458
|
super.update();
|
|
397
459
|
this._attachEventListeners();
|
|
460
|
+
if (this._isOpen) {
|
|
461
|
+
const dropdown = this.shadowRoot?.querySelector(".autocomplete-dropdown");
|
|
462
|
+
const trigger = this.shadowRoot?.querySelector(".autocomplete-input, .autocomplete-tags");
|
|
463
|
+
if (dropdown && trigger) {
|
|
464
|
+
try {
|
|
465
|
+
dropdown.showPopover();
|
|
466
|
+
this._positionDropdown(dropdown, trigger);
|
|
467
|
+
} catch {}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
398
470
|
}
|
|
399
471
|
_attachEventListeners() {
|
|
400
472
|
const input = this.shadowRoot?.querySelector(".autocomplete-input, .autocomplete-tags-input");
|
package/dist/cjs/register.js
CHANGED
|
@@ -35,10 +35,10 @@ __export(exports_src, {
|
|
|
35
35
|
module.exports = __toCommonJS(exports_src);
|
|
36
36
|
|
|
37
37
|
// src/el-dm-autocomplete.ts
|
|
38
|
-
var
|
|
38
|
+
var import_el_base = require("@duskmoon-dev/el-base");
|
|
39
39
|
var import_autocomplete = require("@duskmoon-dev/core/components/autocomplete");
|
|
40
40
|
var strippedCss = import_autocomplete.css.replace(/@layer\s+components\s*\{/, "").replace(/\}[\s]*$/, "");
|
|
41
|
-
var styles =
|
|
41
|
+
var styles = import_el_base.css`
|
|
42
42
|
${strippedCss}
|
|
43
43
|
|
|
44
44
|
:host {
|
|
@@ -52,33 +52,44 @@ var styles = import_el_core.css`
|
|
|
52
52
|
.autocomplete {
|
|
53
53
|
width: 100%;
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
/* Popover API styles for dropdown — override core's absolute positioning
|
|
57
|
+
and visibility rules since the top layer breaks parent-child CSS selectors */
|
|
58
|
+
.autocomplete-dropdown {
|
|
59
|
+
position: fixed;
|
|
60
|
+
margin: 0;
|
|
61
|
+
border: none;
|
|
62
|
+
padding: 0;
|
|
63
|
+
inset: unset;
|
|
64
|
+
/* Override core's hidden defaults — popover API controls visibility */
|
|
65
|
+
opacity: 1;
|
|
66
|
+
visibility: visible;
|
|
67
|
+
transform: none;
|
|
68
|
+
transition: none;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.autocomplete-dropdown:popover-open {
|
|
72
|
+
display: block;
|
|
73
|
+
}
|
|
55
74
|
`;
|
|
56
75
|
|
|
57
|
-
class ElDmAutocomplete extends
|
|
76
|
+
class ElDmAutocomplete extends import_el_base.BaseElement {
|
|
58
77
|
static properties = {
|
|
59
|
-
value: { type: String, reflect: true
|
|
60
|
-
options: { type: String, reflect: true
|
|
61
|
-
multiple: { type: Boolean, reflect: true
|
|
62
|
-
disabled: { type: Boolean, reflect: true
|
|
63
|
-
clearable: { type: Boolean, reflect: true
|
|
64
|
-
placeholder: { type: String, reflect: true
|
|
65
|
-
size: { type: String, reflect: true
|
|
66
|
-
loading: { type: Boolean, reflect: true
|
|
67
|
-
noResultsText: { type: String, reflect: true,
|
|
78
|
+
value: { type: String, reflect: true },
|
|
79
|
+
options: { type: String, reflect: true },
|
|
80
|
+
multiple: { type: Boolean, reflect: true },
|
|
81
|
+
disabled: { type: Boolean, reflect: true },
|
|
82
|
+
clearable: { type: Boolean, reflect: true },
|
|
83
|
+
placeholder: { type: String, reflect: true },
|
|
84
|
+
size: { type: String, reflect: true },
|
|
85
|
+
loading: { type: Boolean, reflect: true },
|
|
86
|
+
noResultsText: { type: String, reflect: true, attribute: "no-results-text" }
|
|
68
87
|
};
|
|
69
|
-
value;
|
|
70
|
-
options;
|
|
71
|
-
multiple;
|
|
72
|
-
disabled;
|
|
73
|
-
clearable;
|
|
74
|
-
placeholder;
|
|
75
|
-
size;
|
|
76
|
-
loading;
|
|
77
|
-
noResultsText;
|
|
78
88
|
_isOpen = false;
|
|
79
89
|
_searchValue = "";
|
|
80
90
|
_highlightedIndex = -1;
|
|
81
91
|
_selectedValues = [];
|
|
92
|
+
_scrollHandler = null;
|
|
82
93
|
constructor() {
|
|
83
94
|
super();
|
|
84
95
|
this.attachStyles(styles);
|
|
@@ -91,6 +102,11 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
91
102
|
disconnectedCallback() {
|
|
92
103
|
super.disconnectedCallback();
|
|
93
104
|
document.removeEventListener("click", this._handleOutsideClick);
|
|
105
|
+
if (this._scrollHandler) {
|
|
106
|
+
window.removeEventListener("scroll", this._scrollHandler, true);
|
|
107
|
+
window.removeEventListener("resize", this._scrollHandler);
|
|
108
|
+
this._scrollHandler = null;
|
|
109
|
+
}
|
|
94
110
|
}
|
|
95
111
|
_handleOutsideClick = (e) => {
|
|
96
112
|
if (!this.contains(e.target)) {
|
|
@@ -110,7 +126,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
110
126
|
}
|
|
111
127
|
_getOptions() {
|
|
112
128
|
try {
|
|
113
|
-
return JSON.parse(this.options);
|
|
129
|
+
return JSON.parse(this.options || "[]");
|
|
114
130
|
} catch {
|
|
115
131
|
return [];
|
|
116
132
|
}
|
|
@@ -128,13 +144,57 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
128
144
|
this._isOpen = true;
|
|
129
145
|
this._highlightedIndex = -1;
|
|
130
146
|
this.update();
|
|
147
|
+
const dropdown = this.shadowRoot?.querySelector(".autocomplete-dropdown");
|
|
148
|
+
const trigger = this.shadowRoot?.querySelector(".autocomplete-input, .autocomplete-tags");
|
|
149
|
+
if (dropdown && trigger) {
|
|
150
|
+
try {
|
|
151
|
+
dropdown.showPopover();
|
|
152
|
+
this._positionDropdown(dropdown, trigger);
|
|
153
|
+
} catch {}
|
|
154
|
+
this._scrollHandler = () => {
|
|
155
|
+
this._positionDropdown(dropdown, trigger);
|
|
156
|
+
};
|
|
157
|
+
window.addEventListener("scroll", this._scrollHandler, true);
|
|
158
|
+
window.addEventListener("resize", this._scrollHandler);
|
|
159
|
+
}
|
|
131
160
|
}
|
|
132
161
|
_close() {
|
|
162
|
+
if (!this._isOpen)
|
|
163
|
+
return;
|
|
133
164
|
this._isOpen = false;
|
|
134
165
|
this._searchValue = "";
|
|
135
166
|
this._highlightedIndex = -1;
|
|
167
|
+
if (this._scrollHandler) {
|
|
168
|
+
window.removeEventListener("scroll", this._scrollHandler, true);
|
|
169
|
+
window.removeEventListener("resize", this._scrollHandler);
|
|
170
|
+
this._scrollHandler = null;
|
|
171
|
+
}
|
|
172
|
+
const dropdown = this.shadowRoot?.querySelector(".autocomplete-dropdown");
|
|
173
|
+
if (dropdown) {
|
|
174
|
+
try {
|
|
175
|
+
dropdown.hidePopover();
|
|
176
|
+
} catch {}
|
|
177
|
+
}
|
|
136
178
|
this.update();
|
|
137
179
|
}
|
|
180
|
+
_positionDropdown(dropdown, trigger) {
|
|
181
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
182
|
+
const dropdownRect = dropdown.getBoundingClientRect();
|
|
183
|
+
let top = triggerRect.bottom + 4;
|
|
184
|
+
let left = triggerRect.left;
|
|
185
|
+
if (top + dropdownRect.height > window.innerHeight) {
|
|
186
|
+
top = triggerRect.top - dropdownRect.height - 4;
|
|
187
|
+
}
|
|
188
|
+
if (left + triggerRect.width > window.innerWidth) {
|
|
189
|
+
left = window.innerWidth - triggerRect.width - 8;
|
|
190
|
+
}
|
|
191
|
+
if (left < 8) {
|
|
192
|
+
left = 8;
|
|
193
|
+
}
|
|
194
|
+
dropdown.style.top = `${top}px`;
|
|
195
|
+
dropdown.style.left = `${left}px`;
|
|
196
|
+
dropdown.style.width = `${triggerRect.width}px`;
|
|
197
|
+
}
|
|
138
198
|
_toggle() {
|
|
139
199
|
if (this._isOpen) {
|
|
140
200
|
this._close();
|
|
@@ -282,7 +342,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
282
342
|
}
|
|
283
343
|
if (filteredOptions.length === 0) {
|
|
284
344
|
return `
|
|
285
|
-
<div class="autocomplete-no-results">${this.noResultsText}</div>
|
|
345
|
+
<div class="autocomplete-no-results">${this.noResultsText || "No results found"}</div>
|
|
286
346
|
`;
|
|
287
347
|
}
|
|
288
348
|
const groups = new Map;
|
|
@@ -340,10 +400,12 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
340
400
|
`;
|
|
341
401
|
}
|
|
342
402
|
render() {
|
|
343
|
-
const
|
|
403
|
+
const size = this.size || "md";
|
|
404
|
+
const sizeClass = size !== "md" ? `autocomplete-${size}` : "";
|
|
344
405
|
const openClass = this._isOpen ? "autocomplete-open" : "";
|
|
345
406
|
const clearableClass = this.clearable ? "autocomplete-clearable" : "";
|
|
346
407
|
const showClear = this.clearable && this._selectedValues.length > 0 && !this.disabled;
|
|
408
|
+
const placeholder = this.placeholder || "";
|
|
347
409
|
if (this.multiple) {
|
|
348
410
|
return `
|
|
349
411
|
<div class="autocomplete ${sizeClass} ${openClass} ${clearableClass}">
|
|
@@ -352,7 +414,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
352
414
|
<input
|
|
353
415
|
type="text"
|
|
354
416
|
class="autocomplete-tags-input"
|
|
355
|
-
placeholder="${this._selectedValues.length === 0 ?
|
|
417
|
+
placeholder="${this._selectedValues.length === 0 ? placeholder : ""}"
|
|
356
418
|
value="${this._searchValue}"
|
|
357
419
|
${this.disabled ? "disabled" : ""}
|
|
358
420
|
role="combobox"
|
|
@@ -364,7 +426,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
364
426
|
${showClear ? `
|
|
365
427
|
<button type="button" class="autocomplete-clear" aria-label="Clear selection">×</button>
|
|
366
428
|
` : ""}
|
|
367
|
-
<div class="autocomplete-dropdown" role="listbox">
|
|
429
|
+
<div class="autocomplete-dropdown" role="listbox" popover="manual">
|
|
368
430
|
${this._renderOptions()}
|
|
369
431
|
</div>
|
|
370
432
|
</div>
|
|
@@ -375,7 +437,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
375
437
|
<input
|
|
376
438
|
type="text"
|
|
377
439
|
class="autocomplete-input"
|
|
378
|
-
placeholder="${
|
|
440
|
+
placeholder="${placeholder}"
|
|
379
441
|
value="${this._isOpen ? this._searchValue : this._getDisplayValue()}"
|
|
380
442
|
${this.disabled ? "disabled" : ""}
|
|
381
443
|
role="combobox"
|
|
@@ -386,7 +448,7 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
386
448
|
${showClear ? `
|
|
387
449
|
<button type="button" class="autocomplete-clear" aria-label="Clear selection">×</button>
|
|
388
450
|
` : ""}
|
|
389
|
-
<div class="autocomplete-dropdown" role="listbox">
|
|
451
|
+
<div class="autocomplete-dropdown" role="listbox" popover="manual">
|
|
390
452
|
${this._renderOptions()}
|
|
391
453
|
</div>
|
|
392
454
|
</div>
|
|
@@ -395,6 +457,16 @@ class ElDmAutocomplete extends import_el_core.BaseElement {
|
|
|
395
457
|
update() {
|
|
396
458
|
super.update();
|
|
397
459
|
this._attachEventListeners();
|
|
460
|
+
if (this._isOpen) {
|
|
461
|
+
const dropdown = this.shadowRoot?.querySelector(".autocomplete-dropdown");
|
|
462
|
+
const trigger = this.shadowRoot?.querySelector(".autocomplete-input, .autocomplete-tags");
|
|
463
|
+
if (dropdown && trigger) {
|
|
464
|
+
try {
|
|
465
|
+
dropdown.showPopover();
|
|
466
|
+
this._positionDropdown(dropdown, trigger);
|
|
467
|
+
} catch {}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
398
470
|
}
|
|
399
471
|
_attachEventListeners() {
|
|
400
472
|
const input = this.shadowRoot?.querySelector(".autocomplete-input, .autocomplete-tags-input");
|