html-combobox-element 0.0.2 → 0.0.3
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/combobox/Combobox.markup.js +178 -102
- package/dist/cjs/combobox/HTML.combobox.element.js +262 -113
- package/dist/cjs/combobox/HTML.combobox.option.element.js +70 -5
- package/dist/cjs/combobox/HTML.combobox.tag.element.js +26 -7
- package/dist/cjs/index.js +0 -66
- package/dist/esm/combobox/Combobox.markup.js +178 -102
- package/dist/esm/combobox/HTML.combobox.element.js +262 -113
- package/dist/esm/combobox/HTML.combobox.option.element.js +70 -5
- package/dist/esm/combobox/HTML.combobox.tag.element.js +26 -7
- package/dist/esm/index.js +0 -66
- package/dist/types/combobox/Combobox.markup.d.ts +13 -8
- package/dist/types/combobox/Combobox.markup.d.ts.map +1 -1
- package/dist/types/combobox/HTML.combobox.element.d.ts +10 -4
- package/dist/types/combobox/HTML.combobox.element.d.ts.map +1 -1
- package/dist/types/combobox/HTML.combobox.option.element.d.ts +11 -0
- package/dist/types/combobox/HTML.combobox.option.element.d.ts.map +1 -1
- package/dist/types/combobox/HTML.combobox.tag.element.d.ts +9 -1
- package/dist/types/combobox/HTML.combobox.tag.element.d.ts.map +1 -1
- package/dist/types/index.d.ts +102 -61
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +11 -3
- package/dist/types/types.d.ts +0 -198
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# html-combobox-element
|
|
2
|
+
|
|
3
|
+
The `<combo-box>` is a web component that provides dropdown/combobox functionality through `<select>` fully compatible API.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
```sh
|
|
7
|
+
npm install html-combobox-element
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
// app entry point
|
|
12
|
+
import 'html-combobox-element';
|
|
13
|
+
```
|
|
14
|
+
This registers the `<combo-box>`, `<box-option>` and `<box-tag>` custom elements with full TS support for plain DOM/vanilla-JS usage.
|
|
15
|
+
|
|
16
|
+
### TypeScript + JSX
|
|
17
|
+
Using `<combo-box>` in JSX or a Vue template needs one extra step, due to a TypeScript limitation: JSX/template type-checking only picks up custom element typings from a
|
|
18
|
+
file in your own project - not from a package's shipped `.d.ts`.
|
|
19
|
+
|
|
20
|
+
This isn't specific to this package; it affects any library adding custom elements to `JSX.IntrinsicElements` under the modern automatic JSX runtime.
|
|
21
|
+
|
|
22
|
+
So, in addition to `import 'html-combobox-element'`, create and include one small file tsconfig.json, with the snippet for your framework:
|
|
23
|
+
|
|
24
|
+
<details>
|
|
25
|
+
<summary><strong>React</strong> (<code>jsx: "react-jsx"</code>)</summary>
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// combo-box.d.ts
|
|
29
|
+
declare module 'react/jsx-runtime' {
|
|
30
|
+
namespace JSX {
|
|
31
|
+
interface IntrinsicElements {
|
|
32
|
+
'combo-box': ComboboxElementAttributes;
|
|
33
|
+
'box-option': ComboboxOptionElementAttributes;
|
|
34
|
+
'box-tag': ComboboxTagElementAttributes;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
declare module 'react/jsx-dev-runtime' {
|
|
39
|
+
namespace JSX {
|
|
40
|
+
interface IntrinsicElements {
|
|
41
|
+
'combo-box': ComboboxElementAttributes;
|
|
42
|
+
'box-option': ComboboxOptionElementAttributes;
|
|
43
|
+
'box-tag': ComboboxTagElementAttributes;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
</details>
|
|
50
|
+
|
|
51
|
+
<details>
|
|
52
|
+
<summary><strong>Preact</strong> (<code>jsx: "react-jsx"</code>, <code>jsxImportSource: "preact"</code>)</summary>
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
// combo-box.d.ts
|
|
56
|
+
declare module 'preact/jsx-runtime' {
|
|
57
|
+
namespace JSX {
|
|
58
|
+
interface IntrinsicElements {
|
|
59
|
+
'combo-box': ComboboxElementAttributes;
|
|
60
|
+
'box-option': ComboboxOptionElementAttributes;
|
|
61
|
+
'box-tag': ComboboxTagElementAttributes;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
</details>
|
|
68
|
+
|
|
69
|
+
<details>
|
|
70
|
+
<summary><strong>Solid</strong> (<code>jsx: "preserve"</code>, <code>jsxImportSource: "solid-js"</code>)</summary>
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// combo-box.d.ts
|
|
74
|
+
declare module 'solid-js/jsx-runtime' {
|
|
75
|
+
namespace JSX {
|
|
76
|
+
interface IntrinsicElements {
|
|
77
|
+
'combo-box': ComboboxElementAttributes;
|
|
78
|
+
'box-option': ComboboxOptionElementAttributes;
|
|
79
|
+
'box-tag': ComboboxTagElementAttributes;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
</details>
|
|
86
|
+
|
|
87
|
+
<details>
|
|
88
|
+
<summary><strong>Vue</strong> (SFC <code><template></code>, checked via <code>vue-tsc</code>)</summary>
|
|
89
|
+
|
|
90
|
+
Vue templates aren't JSX, so they're type-checked through `GlobalComponents` instead:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
// combo-box.d.ts
|
|
94
|
+
declare module 'vue' {
|
|
95
|
+
export interface GlobalComponents {
|
|
96
|
+
'combo-box': new () => { $props: ComboboxElementAttributes };
|
|
97
|
+
'box-option': new () => { $props: ComboboxOptionElementAttributes };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
This checks required props and value types for props you pass; like any native custom
|
|
104
|
+
element, Vue still allows extra/unrecognized attributes through (that's expected Vue
|
|
105
|
+
behavior for custom elements, not a gap in this typing). The trailing `export {};` may be
|
|
106
|
+
required here (without it the file is treated as a script, not a module, and the
|
|
107
|
+
augmentation breaks Vue's own types).
|
|
108
|
+
|
|
109
|
+
</details>
|
|
110
|
+
|
|
111
|
+
### Exported types
|
|
112
|
+
|
|
113
|
+
- `HTMLComboboxElement`, `HTMLComboboxOptionElement`, `HTMLComboboxTagElement` - element
|
|
114
|
+
interfaces, also usable via `document.querySelector('combo-box')` etc.
|
|
115
|
+
- `ComboboxEvent`, `ComboboxInputEvent`, `ComboboxFocusEvent` - typed event objects
|
|
116
|
+
(`event.target` is a properly typed `HTMLComboboxElement`).
|
|
117
|
+
- `ComboboxJsxAttributes`, `ComboboxOptionJsxAttributes` - the component's own props
|
|
118
|
+
(`value`, `multiple`, `placeholder`, `clearable`, `searchable`, `filterable`,
|
|
119
|
+
`required`, `disabled`, etc.), framework-independent.
|
|
120
|
+
- `ComboboxElementAttributes`, `ComboboxOptionElementAttributes`,
|
|
121
|
+
`ComboboxTagElementAttributes` - the above plus common passthrough attributes
|
|
122
|
+
(`class`/`className`, `style`, `ref`, `children`, `data-*`, `aria-*`, `on*`). These
|
|
123
|
+
are what the snippets above use for `'combo-box'` / `'box-option'` / `'box-tag'`.
|
|
@@ -12,36 +12,74 @@ class ComboboxMarkup {
|
|
|
12
12
|
placeholder = null;
|
|
13
13
|
searchInput = null;
|
|
14
14
|
tagTemplate = null;
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
#rso;
|
|
16
|
+
#hostWidth = 0;
|
|
17
|
+
#hostHeight = 0;
|
|
18
|
+
#hostTop = 0;
|
|
17
19
|
constructor(shadowRoot, internals) {
|
|
18
20
|
this.#shadowRoot = shadowRoot;
|
|
19
21
|
this.#internals = internals;
|
|
20
|
-
|
|
21
|
-
this.#shadowRoot.host.addEventListener('
|
|
22
|
-
this.#shadowRoot.host.addEventListener('
|
|
23
|
-
|
|
24
|
-
document.addEventListener('scroll', this.onPageScroll);
|
|
22
|
+
window.addEventListener('click', this.onClickOutside);
|
|
23
|
+
this.#shadowRoot.host.addEventListener('focusin', this.onFocusIn);
|
|
24
|
+
this.#shadowRoot.host.addEventListener('focusout', this.onFocusOut);
|
|
25
|
+
this.#rso = new ResizeObserver(this.#onDimensionsChanges);
|
|
25
26
|
}
|
|
26
27
|
connect() {
|
|
27
|
-
const placeholder = this.#shadowRoot.host.getAttribute('placeholder') ||
|
|
28
|
+
const placeholder = this.#shadowRoot.host.getAttribute('placeholder') || `Выбрать`;
|
|
28
29
|
this.tagsContainer = this.#shadowRoot.querySelector('#tags');
|
|
30
|
+
this.#rso.observe(this.#shadowRoot.host, { box: 'border-box' });
|
|
29
31
|
this.optionsContainer = this.#shadowRoot.querySelector('[part*="options"]');
|
|
30
32
|
this.clearAllButton = this.#shadowRoot.querySelector('[part*="clear-all"]');
|
|
33
|
+
this.clearAllButton?.addEventListener('focusout', this.onClearAllFocusOut);
|
|
31
34
|
this.dropdown = this.#shadowRoot.querySelector('#dropdown');
|
|
32
35
|
this.placeholder = this.#shadowRoot.querySelector('#placeholder');
|
|
33
36
|
this.placeholder.innerText = placeholder;
|
|
34
37
|
this.searchInput = this.#shadowRoot.querySelector('[part*="search-input"]');
|
|
35
38
|
this.searchInput.value = this.#shadowRoot.host.getAttribute('query');
|
|
36
|
-
this.searchInput.placeholder
|
|
39
|
+
this.searchInput.style.setProperty('--search-input-placeholder-color', this.placeholder.style.color);
|
|
37
40
|
const innerTemplate = this.#shadowRoot.querySelector('#tag-template');
|
|
38
41
|
const doc = document.importNode(innerTemplate.content, true);
|
|
39
42
|
this.tagTemplate = doc.querySelector('box-tag');
|
|
40
|
-
this.connected = true;
|
|
41
43
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
onHostConnected() {
|
|
45
|
+
const host = this.#shadowRoot.host;
|
|
46
|
+
const clearable = host.hasAttribute('clearable') || host.hasAttribute('multiple');
|
|
47
|
+
this.tagTemplate.toggleAttribute('visible', clearable);
|
|
44
48
|
}
|
|
49
|
+
disconnect() {
|
|
50
|
+
this.#shadowRoot.host.removeEventListener('focusin', this.onFocusIn);
|
|
51
|
+
this.#shadowRoot.host.removeEventListener('focusout', this.onFocusOut);
|
|
52
|
+
this.clearAllButton?.removeEventListener('focusout', this.onClearAllFocusOut);
|
|
53
|
+
window.removeEventListener('click', this.onClickOutside);
|
|
54
|
+
this.#rso.unobserve(this.#shadowRoot.host);
|
|
55
|
+
}
|
|
56
|
+
setPlaceholder(value) {
|
|
57
|
+
if (this.placeholder) {
|
|
58
|
+
this.placeholder.innerText = value.trim() || `Выбрать`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
setInputValue(value) {
|
|
62
|
+
if (this.searchInput) {
|
|
63
|
+
this.searchInput.value = value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
onClearAllFocusOut = (event) => {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
event.stopPropagation();
|
|
69
|
+
if (event.relatedTarget == null) {
|
|
70
|
+
const host = this.#shadowRoot.host;
|
|
71
|
+
if (!host.hasAttribute('searchable') && !host.hasAttribute('filterable')) {
|
|
72
|
+
this.closeDropdown();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
onClickOutside = (ev) => {
|
|
77
|
+
if (this.#internals.ariaExpanded !== 'true')
|
|
78
|
+
return;
|
|
79
|
+
if (ev.target === this.#shadowRoot.host)
|
|
80
|
+
return;
|
|
81
|
+
this.closeDropdown();
|
|
82
|
+
};
|
|
45
83
|
#timer = undefined;
|
|
46
84
|
sort(query) {
|
|
47
85
|
clearTimeout(this.#timer);
|
|
@@ -56,68 +94,63 @@ class ComboboxMarkup {
|
|
|
56
94
|
}
|
|
57
95
|
});
|
|
58
96
|
this.dropdown.scrollTo({ top: 0, behavior: 'smooth' });
|
|
59
|
-
},
|
|
97
|
+
}, 400);
|
|
60
98
|
}
|
|
61
|
-
|
|
62
|
-
this
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
document.removeEventListener('scroll', this.onPageScroll);
|
|
67
|
-
this.connected = false;
|
|
99
|
+
get options() {
|
|
100
|
+
return this.optionsContainer.querySelectorAll('box-option');
|
|
101
|
+
}
|
|
102
|
+
getTagControl(tag) {
|
|
103
|
+
return tag.querySelector('[part*="clear-tag"]');
|
|
68
104
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
105
|
+
#onDimensionsChanges = (entries) => {
|
|
106
|
+
const entry = entries[0];
|
|
107
|
+
if (entry) {
|
|
108
|
+
this.#hostWidth = entry.borderBoxSize[0].inlineSize;
|
|
109
|
+
this.#hostHeight = entry.borderBoxSize[0].blockSize;
|
|
72
110
|
}
|
|
111
|
+
this.setDropdownPosition();
|
|
73
112
|
};
|
|
74
|
-
setDropdownPosition(
|
|
113
|
+
setDropdownPosition() {
|
|
75
114
|
const dropdown = this.dropdown;
|
|
76
115
|
const vh = window.innerHeight;
|
|
77
|
-
const sh =
|
|
78
|
-
const sy =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
dropdown.style.maxHeight = '50vh';
|
|
116
|
+
const sh = this.#hostHeight;
|
|
117
|
+
const sy = this.#hostTop;
|
|
118
|
+
// Calculate available space below and above
|
|
119
|
+
const spaceBelow = vh - (sy + sh);
|
|
120
|
+
const spaceAbove = sy;
|
|
121
|
+
// Determine if dropdown should go below or above
|
|
122
|
+
const shouldGoBelow = spaceBelow >= spaceAbove;
|
|
123
|
+
// Set dropdown to max 50vh or available space, whichever is smaller
|
|
124
|
+
const maxHeight = Math.min(50 * vh / 100, shouldGoBelow ? spaceBelow - 10 : spaceAbove - 10);
|
|
125
|
+
dropdown.style.width = this.#hostWidth + 'px';
|
|
126
|
+
dropdown.style.maxHeight = `${Math.max(100, maxHeight)}px`;
|
|
127
|
+
}
|
|
128
|
+
showDropdown() {
|
|
129
|
+
this.onFocusIn();
|
|
92
130
|
}
|
|
93
|
-
|
|
131
|
+
onFocusIn = () => {
|
|
132
|
+
if (this.#internals.ariaExpanded === "true")
|
|
133
|
+
return;
|
|
134
|
+
this.#hostTop = this.#shadowRoot.host.getBoundingClientRect().top;
|
|
94
135
|
try {
|
|
95
|
-
this.setDropdownPosition(this.#shadowRoot.host.getBoundingClientRect());
|
|
96
|
-
this.dropdown.style.display = 'flex';
|
|
97
136
|
this.dropdown.showPopover();
|
|
98
137
|
this.#internals.ariaExpanded = "true";
|
|
99
|
-
if (this.tagsContainer?.children.length === 0) {
|
|
100
|
-
this.searchInput?.focus();
|
|
101
|
-
}
|
|
102
|
-
this.placeholder.innerText = '';
|
|
103
138
|
}
|
|
104
|
-
catch {
|
|
139
|
+
catch (e) {
|
|
105
140
|
this.#internals.ariaExpanded = "false";
|
|
106
141
|
}
|
|
107
142
|
};
|
|
108
143
|
closeDropdown() {
|
|
109
144
|
try {
|
|
110
145
|
this.dropdown.hidePopover();
|
|
111
|
-
this.dropdown.style.display = 'none';
|
|
112
146
|
this.#internals.ariaExpanded = "false";
|
|
113
|
-
this.placeholder.innerText = this.#shadowRoot.host.getAttribute('placeholder');
|
|
114
147
|
}
|
|
115
148
|
catch (e) {
|
|
116
149
|
this.#internals.ariaExpanded = "true";
|
|
117
150
|
}
|
|
118
151
|
}
|
|
119
|
-
|
|
120
|
-
if (
|
|
152
|
+
onFocusOut = () => {
|
|
153
|
+
if (this.#internals.ariaExpanded !== "true")
|
|
121
154
|
return;
|
|
122
155
|
this.closeDropdown();
|
|
123
156
|
};
|
|
@@ -147,12 +180,13 @@ class ComboboxMarkup {
|
|
|
147
180
|
const template = this.tagTemplate;
|
|
148
181
|
tag = template.cloneNode(true);
|
|
149
182
|
const label = tag.querySelector('[part="tag-label"]');
|
|
150
|
-
label.textContent = option.textContent;
|
|
183
|
+
label.textContent = option.label || option.textContent;
|
|
151
184
|
button = tag.querySelector('[part="clear-tag"]');
|
|
152
185
|
}
|
|
153
186
|
button?.setAttribute('value', value);
|
|
154
187
|
tag.setAttribute('value', value);
|
|
155
188
|
this.tagsContainer.appendChild(tag);
|
|
189
|
+
this.clearAllButton?.removeAttribute('disabled');
|
|
156
190
|
return button;
|
|
157
191
|
}
|
|
158
192
|
getTagByValue(value) {
|
|
@@ -162,8 +196,7 @@ class ComboboxMarkup {
|
|
|
162
196
|
return this.optionsContainer.querySelector(`box-option[value="${value}"]`);
|
|
163
197
|
}
|
|
164
198
|
get selectedOptions() {
|
|
165
|
-
return this.optionsContainer
|
|
166
|
-
.querySelectorAll('box-option[selected]');
|
|
199
|
+
return this.optionsContainer.querySelectorAll('box-option[selected]');
|
|
167
200
|
}
|
|
168
201
|
static importCSS(urls) {
|
|
169
202
|
ComboboxMarkup.template = ComboboxMarkup.template.replace(':host {', `
|
|
@@ -177,35 +210,64 @@ ${urls.map(url => `@import "${url}";`)}
|
|
|
177
210
|
:host {
|
|
178
211
|
font-size: inherit;
|
|
179
212
|
font-family: inherit;
|
|
213
|
+
display: flow-root;
|
|
214
|
+
position: relative;
|
|
215
|
+
anchor-name: --position;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
#internal {
|
|
221
|
+
min-block-size: 1lh;
|
|
180
222
|
display: grid;
|
|
181
223
|
grid-template-columns: minmax(0, max-content) 1fr;
|
|
182
224
|
align-items: center;
|
|
183
225
|
gap: 1px;
|
|
184
|
-
|
|
226
|
+
border-radius: inherit;
|
|
185
227
|
}
|
|
186
228
|
|
|
187
|
-
:host([multiple]) {
|
|
229
|
+
:host([multiple]) #internal {
|
|
188
230
|
grid-template-columns: minmax(0, max-content) 1fr auto;
|
|
189
231
|
}
|
|
190
232
|
|
|
191
233
|
#dropdown {
|
|
234
|
+
contain: layout style paint;
|
|
192
235
|
inset: unset;
|
|
193
236
|
margin: 0;
|
|
237
|
+
/*position: absolute;*/
|
|
194
238
|
box-sizing: border-box;
|
|
195
239
|
overflow-y: scroll;
|
|
196
240
|
flex-direction: column;
|
|
197
241
|
border-radius: inherit;
|
|
198
242
|
border-color: ButtonFace;
|
|
199
243
|
border-width: inherit;
|
|
244
|
+
background-color: Canvas;
|
|
245
|
+
outline: none;
|
|
246
|
+
display: none;
|
|
247
|
+
top: calc(anchor(bottom) + 2px);
|
|
248
|
+
left: anchor(left);
|
|
249
|
+
position-anchor: --position;
|
|
250
|
+
position-try: normal flip-block;
|
|
200
251
|
}
|
|
201
252
|
|
|
202
|
-
|
|
253
|
+
#dropdown:popover-open {
|
|
254
|
+
display: flex;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#dropdown::-webkit-scrollbar {
|
|
258
|
+
display: none;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
#options {
|
|
203
262
|
display: flex;
|
|
204
263
|
flex-direction: column;
|
|
205
264
|
justify-content: start;
|
|
206
265
|
gap: 2px;
|
|
207
|
-
|
|
208
|
-
|
|
266
|
+
border-radius: inherit;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
:host([filterable]) #options, :host([searchable]) #options {
|
|
270
|
+
padding-top: .5rem;
|
|
209
271
|
}
|
|
210
272
|
|
|
211
273
|
box-option {
|
|
@@ -213,8 +275,8 @@ ${urls.map(url => `@import "${url}";`)}
|
|
|
213
275
|
border-radius: inherit;
|
|
214
276
|
content-visibility: auto;
|
|
215
277
|
cursor: pointer;
|
|
216
|
-
padding
|
|
217
|
-
padding-
|
|
278
|
+
padding: inherit;
|
|
279
|
+
padding-inline: .5lh;
|
|
218
280
|
}
|
|
219
281
|
|
|
220
282
|
box-option:hover {
|
|
@@ -227,29 +289,27 @@ ${urls.map(url => `@import "${url}";`)}
|
|
|
227
289
|
pointer-events: none;
|
|
228
290
|
}
|
|
229
291
|
|
|
230
|
-
[part="search-input"] {
|
|
292
|
+
#dropdown > [part="search-input"] {
|
|
231
293
|
display: none;
|
|
232
294
|
position: sticky;
|
|
233
295
|
top: 0;
|
|
234
296
|
z-index: 2;
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
border-width: inherit;
|
|
238
|
-
border-color: inherit;
|
|
239
|
-
padding: inherit;
|
|
297
|
+
font-size: inherit;
|
|
298
|
+
outline: inherit;
|
|
240
299
|
}
|
|
241
300
|
|
|
242
|
-
:host([searchable]) [part="search-input"],
|
|
243
|
-
:host([filterable]) [part="search-input"] {
|
|
244
|
-
display:
|
|
301
|
+
:host([searchable]) #dropdown > [part="search-input"],
|
|
302
|
+
:host([filterable]) #dropdown > [part="search-input"] {
|
|
303
|
+
display: flow-root;
|
|
245
304
|
}
|
|
246
305
|
|
|
247
306
|
#placeholder {
|
|
248
307
|
text-align: left;
|
|
249
308
|
overflow: hidden;
|
|
250
309
|
padding-inline-start: 2px;
|
|
251
|
-
font-size: smaller;
|
|
252
310
|
color: dimgrey;
|
|
311
|
+
outline: none;
|
|
312
|
+
user-select: none;
|
|
253
313
|
}
|
|
254
314
|
|
|
255
315
|
#tags:not(:empty) + #placeholder {
|
|
@@ -266,7 +326,6 @@ ${urls.map(url => `@import "${url}";`)}
|
|
|
266
326
|
flex-wrap: wrap;
|
|
267
327
|
overflow: hidden;
|
|
268
328
|
gap: 2px;
|
|
269
|
-
border-radius: inherit;
|
|
270
329
|
}
|
|
271
330
|
|
|
272
331
|
box-tag {
|
|
@@ -275,13 +334,11 @@ ${urls.map(url => `@import "${url}";`)}
|
|
|
275
334
|
box-sizing: border-box;
|
|
276
335
|
display: flex;
|
|
277
336
|
align-items: center;
|
|
278
|
-
|
|
279
|
-
padding-inline-
|
|
280
|
-
padding-inline-end: .2rem;
|
|
337
|
+
padding-inline-start: 0.4lh;
|
|
338
|
+
padding-inline-end: 0.2lh;
|
|
281
339
|
background-color: transparent;
|
|
282
340
|
gap: 5px;
|
|
283
|
-
|
|
284
|
-
text-transform: uppercase;
|
|
341
|
+
border-radius: 1lh;
|
|
285
342
|
}
|
|
286
343
|
|
|
287
344
|
:host([multiple]) box-tag {
|
|
@@ -290,80 +347,99 @@ ${urls.map(url => `@import "${url}";`)}
|
|
|
290
347
|
max-width: 100%;
|
|
291
348
|
}
|
|
292
349
|
|
|
293
|
-
|
|
350
|
+
[part*="tag-label"] {
|
|
294
351
|
white-space: nowrap;
|
|
295
352
|
text-overflow: ellipsis;
|
|
296
353
|
overflow: hidden;
|
|
297
354
|
user-select: none;
|
|
298
|
-
font-size: 95%;
|
|
299
355
|
flex-grow: 1;
|
|
300
356
|
}
|
|
301
357
|
|
|
302
|
-
:host([multiple])
|
|
358
|
+
:host([multiple]) [part*="tag-label"] {
|
|
303
359
|
flex-grow: unset;
|
|
304
360
|
}
|
|
305
361
|
|
|
306
|
-
[part*="clear-tag"], [part*="clear-all"] {
|
|
362
|
+
[part*="clear-tag"], #internal > [part*="clear-all"] {
|
|
307
363
|
border-radius: 100%;
|
|
308
364
|
border: none;
|
|
309
365
|
aspect-ratio: 1;
|
|
310
|
-
line-height: 0;
|
|
311
366
|
padding: 0!important;
|
|
312
367
|
user-select: none;
|
|
313
368
|
background-color: transparent;
|
|
314
369
|
}
|
|
315
370
|
|
|
316
371
|
[part*="clear-tag"] {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
372
|
+
block-size: 1lh;
|
|
373
|
+
font-size: .7lh;
|
|
374
|
+
text-transform: uppercase;
|
|
320
375
|
display: none;
|
|
321
376
|
}
|
|
377
|
+
|
|
378
|
+
[part*="clear-tag"]:focus-visible, [part*="clear-tag"]:focus {
|
|
379
|
+
outline-style: solid;
|
|
380
|
+
outline-width: 1px;
|
|
381
|
+
}
|
|
322
382
|
|
|
323
|
-
:host([multiple]) [part*="clear-all"] {
|
|
383
|
+
:host([multiple]) #internal > [part*="clear-all"] {
|
|
324
384
|
display: block;
|
|
325
385
|
}
|
|
326
386
|
|
|
327
|
-
:host([clearable]) [part*="clear-tag"]
|
|
328
|
-
|
|
387
|
+
:host([clearable]) [part*="clear-tag"],
|
|
388
|
+
:host([multiple]) [part*="clear-tag"] {
|
|
389
|
+
display: grid;
|
|
390
|
+
place-content: center;
|
|
329
391
|
}
|
|
330
392
|
|
|
331
|
-
[part*="clear-all"] {
|
|
393
|
+
#internal > [part*="clear-all"] {
|
|
332
394
|
font-size: inherit;
|
|
333
|
-
|
|
334
|
-
block-size:
|
|
395
|
+
text-transform: uppercase;
|
|
396
|
+
block-size: 1lh;
|
|
335
397
|
display: none;
|
|
336
398
|
}
|
|
399
|
+
|
|
400
|
+
#tags:empty + [part*="clear-all"] {
|
|
401
|
+
outline: none;
|
|
402
|
+
}
|
|
337
403
|
|
|
338
|
-
[part*="clear-all"]:hover,
|
|
404
|
+
#internal > [part*="clear-all"]:hover,
|
|
339
405
|
[part*="clear-tag"]:hover {
|
|
340
406
|
color: ActiveText;
|
|
341
407
|
cursor: pointer;
|
|
342
408
|
}
|
|
343
409
|
|
|
344
|
-
[part*="clear-all"]:hover {
|
|
410
|
+
#internal >[part*="clear-all"]:hover {
|
|
345
411
|
background-color: ButtonFace;
|
|
346
412
|
}
|
|
347
413
|
|
|
348
|
-
|
|
414
|
+
#tags:empty + [part*="clear-all"] {
|
|
349
415
|
pointer-events: none;
|
|
350
|
-
|
|
416
|
+
outline: none;
|
|
351
417
|
}
|
|
418
|
+
|
|
352
419
|
</style>
|
|
420
|
+
<div id='internal' part='internal'>
|
|
421
|
+
<div id="tags" part='tags'></div>
|
|
422
|
+
<div id="placeholder" part='placeholder' tabindex='0'>Выбрать</div>
|
|
423
|
+
<button part="clear-all">✕</button>
|
|
424
|
+
</div>
|
|
353
425
|
|
|
354
|
-
<div id="
|
|
355
|
-
<
|
|
356
|
-
<
|
|
357
|
-
<div id="dropdown" popover="manual">
|
|
358
|
-
<input name="search-input" part="search-input" />
|
|
359
|
-
<div part="options"></div>
|
|
426
|
+
<div id="dropdown" popover="manual" part='dropdown' >
|
|
427
|
+
<input placeholder='Поиск' name="search-input" part="search-input" inputmode='search' autofocus />
|
|
428
|
+
<div part="options" id='options'></div>
|
|
360
429
|
</div>
|
|
430
|
+
|
|
361
431
|
<template id='tag-template'>
|
|
362
432
|
<box-tag>
|
|
363
433
|
<span part="tag-label"></span>
|
|
364
|
-
<
|
|
434
|
+
<div part="clear-tag" role='button' tabindex='0'>✕</div>
|
|
365
435
|
</box-tag>
|
|
366
436
|
</template>
|
|
367
437
|
`;
|
|
438
|
+
static markup;
|
|
439
|
+
static {
|
|
440
|
+
const template = document.createElement('template');
|
|
441
|
+
template.innerHTML = ComboboxMarkup.template;
|
|
442
|
+
ComboboxMarkup.markup = template;
|
|
443
|
+
}
|
|
368
444
|
}
|
|
369
445
|
exports.ComboboxMarkup = ComboboxMarkup;
|