tyrell-components 1.0.0-TC57 → 1.0.0-TC59
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/css/tyrell-colors-static.css +14 -0
- package/css/tyrell-theme.css +1 -0
- package/css/tyrell.css +8 -6
- package/dist/tyrell-colors-static.css +1 -1
- package/dist/tyrell-theme.css +1 -1
- package/dist/tyrell.js +1 -1
- package/lib/base/ty-component.d.ts +20 -15
- package/lib/base/ty-component.js +58 -25
- package/lib/components/button.d.ts +14 -0
- package/lib/components/button.js +55 -7
- package/lib/components/calendar-month.d.ts +7 -1
- package/lib/components/calendar-month.js +35 -28
- package/lib/components/calendar-navigation.js +19 -6
- package/lib/components/calendar.d.ts +25 -4
- package/lib/components/calendar.js +98 -67
- package/lib/components/checkbox.d.ts +6 -1
- package/lib/components/checkbox.js +20 -6
- package/lib/components/copy.d.ts +2 -0
- package/lib/components/copy.js +15 -31
- package/lib/components/date-picker.d.ts +52 -19
- package/lib/components/date-picker.js +211 -147
- package/lib/components/file-upload.d.ts +2 -1
- package/lib/components/file-upload.js +8 -3
- package/lib/components/input.d.ts +4 -6
- package/lib/components/input.js +37 -62
- package/lib/components/modal.js +86 -9
- package/lib/components/option.js +2 -5
- package/lib/components/popup.js +99 -11
- package/lib/components/radio.d.ts +8 -1
- package/lib/components/radio.js +14 -29
- package/lib/components/resize-observer.js +2 -0
- package/lib/components/scroll-container.js +2 -0
- package/lib/components/select.d.ts +53 -6
- package/lib/components/select.js +127 -29
- package/lib/components/switch.d.ts +7 -1
- package/lib/components/switch.js +20 -6
- package/lib/components/tabs.js +1 -1
- package/lib/components/textarea.d.ts +45 -4
- package/lib/components/textarea.js +75 -18
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/styles/button.js +21 -19
- package/lib/styles/calendar-month.js +1 -1
- package/lib/styles/calendar.js +3 -0
- package/lib/styles/checkbox.js +11 -5
- package/lib/styles/date-picker.js +15 -3
- package/lib/styles/input.js +5 -9
- package/lib/styles/option.d.ts +1 -1
- package/lib/styles/option.js +4 -0
- package/lib/styles/popup.d.ts +1 -1
- package/lib/styles/popup.js +2 -0
- package/lib/styles/radio.js +10 -4
- package/lib/styles/select-base.js +24 -3
- package/lib/styles/select.d.ts +1 -1
- package/lib/styles/select.js +4 -0
- package/lib/styles/switch.js +10 -5
- package/lib/styles/tag.js +2 -1
- package/lib/styles/textarea.d.ts +1 -1
- package/lib/styles/textarea.js +7 -0
- package/lib/utils/calendar-utils.d.ts +49 -0
- package/lib/utils/calendar-utils.js +63 -6
- package/lib/utils/scroll-lock.d.ts +10 -4
- package/lib/utils/scroll-lock.js +102 -2
- package/lib/utils/upgrade-properties.d.ts +21 -0
- package/lib/utils/upgrade-properties.js +47 -0
- package/lib/utils/validity.d.ts +89 -5
- package/lib/utils/validity.js +198 -25
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -1
- package/lib/base/ty-component.d.ts.map +0 -1
- package/lib/base/ty-component.js.map +0 -1
- package/lib/components/button.d.ts.map +0 -1
- package/lib/components/button.js.map +0 -1
- package/lib/components/calendar-month.d.ts.map +0 -1
- package/lib/components/calendar-month.js.map +0 -1
- package/lib/components/calendar-navigation.d.ts.map +0 -1
- package/lib/components/calendar-navigation.js.map +0 -1
- package/lib/components/calendar.d.ts.map +0 -1
- package/lib/components/calendar.js.map +0 -1
- package/lib/components/checkbox.d.ts.map +0 -1
- package/lib/components/checkbox.js.map +0 -1
- package/lib/components/copy.d.ts.map +0 -1
- package/lib/components/copy.js.map +0 -1
- package/lib/components/date-picker.d.ts.map +0 -1
- package/lib/components/date-picker.js.map +0 -1
- package/lib/components/dropdown.d.ts +0 -403
- package/lib/components/dropdown.d.ts.map +0 -1
- package/lib/components/dropdown.js +0 -1315
- package/lib/components/dropdown.js.map +0 -1
- package/lib/components/icon.d.ts.map +0 -1
- package/lib/components/icon.js.map +0 -1
- package/lib/components/input.d.ts.map +0 -1
- package/lib/components/input.js.map +0 -1
- package/lib/components/modal.d.ts.map +0 -1
- package/lib/components/modal.js.map +0 -1
- package/lib/components/multiselect.d.ts +0 -389
- package/lib/components/multiselect.d.ts.map +0 -1
- package/lib/components/multiselect.js +0 -1301
- package/lib/components/multiselect.js.map +0 -1
- package/lib/components/option.d.ts.map +0 -1
- package/lib/components/option.js.map +0 -1
- package/lib/components/popup.d.ts.map +0 -1
- package/lib/components/popup.js.map +0 -1
- package/lib/components/resize-observer.d.ts.map +0 -1
- package/lib/components/resize-observer.js.map +0 -1
- package/lib/components/scroll-container.d.ts.map +0 -1
- package/lib/components/scroll-container.js.map +0 -1
- package/lib/components/step.d.ts.map +0 -1
- package/lib/components/step.js.map +0 -1
- package/lib/components/tab.d.ts.map +0 -1
- package/lib/components/tab.js.map +0 -1
- package/lib/components/tabs.d.ts.map +0 -1
- package/lib/components/tabs.js.map +0 -1
- package/lib/components/tag.d.ts.map +0 -1
- package/lib/components/tag.js.map +0 -1
- package/lib/components/textarea.d.ts.map +0 -1
- package/lib/components/textarea.js.map +0 -1
- package/lib/components/tooltip.d.ts.map +0 -1
- package/lib/components/tooltip.js.map +0 -1
- package/lib/components/wizard.d.ts.map +0 -1
- package/lib/components/wizard.js.map +0 -1
- package/lib/icons/fontawesome/brands.d.ts.map +0 -1
- package/lib/icons/fontawesome/brands.js.map +0 -1
- package/lib/icons/fontawesome/regular.d.ts.map +0 -1
- package/lib/icons/fontawesome/regular.js.map +0 -1
- package/lib/icons/fontawesome/solid.d.ts.map +0 -1
- package/lib/icons/fontawesome/solid.js.map +0 -1
- package/lib/icons/heroicons/micro.d.ts.map +0 -1
- package/lib/icons/heroicons/micro.js.map +0 -1
- package/lib/icons/heroicons/mini.d.ts.map +0 -1
- package/lib/icons/heroicons/mini.js.map +0 -1
- package/lib/icons/heroicons/outline.d.ts.map +0 -1
- package/lib/icons/heroicons/outline.js.map +0 -1
- package/lib/icons/heroicons/solid.d.ts.map +0 -1
- package/lib/icons/heroicons/solid.js.map +0 -1
- package/lib/icons/lucide.d.ts.map +0 -1
- package/lib/icons/lucide.js.map +0 -1
- package/lib/icons/material/filled.d.ts.map +0 -1
- package/lib/icons/material/filled.js.map +0 -1
- package/lib/icons/material/outlined.d.ts.map +0 -1
- package/lib/icons/material/outlined.js.map +0 -1
- package/lib/icons/material/round.d.ts.map +0 -1
- package/lib/icons/material/round.js.map +0 -1
- package/lib/icons/material/sharp.d.ts.map +0 -1
- package/lib/icons/material/sharp.js.map +0 -1
- package/lib/icons/material/two-tone.d.ts.map +0 -1
- package/lib/icons/material/two-tone.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/styles/button.d.ts.map +0 -1
- package/lib/styles/button.js.map +0 -1
- package/lib/styles/calendar-month.d.ts.map +0 -1
- package/lib/styles/calendar-month.js.map +0 -1
- package/lib/styles/calendar-navigation.d.ts.map +0 -1
- package/lib/styles/calendar-navigation.js.map +0 -1
- package/lib/styles/calendar.d.ts.map +0 -1
- package/lib/styles/calendar.js.map +0 -1
- package/lib/styles/copy.d.ts.map +0 -1
- package/lib/styles/copy.js.map +0 -1
- package/lib/styles/date-picker.d.ts.map +0 -1
- package/lib/styles/date-picker.js.map +0 -1
- package/lib/styles/dropdown.d.ts +0 -12
- package/lib/styles/dropdown.d.ts.map +0 -1
- package/lib/styles/dropdown.js +0 -725
- package/lib/styles/dropdown.js.map +0 -1
- package/lib/styles/icon.d.ts.map +0 -1
- package/lib/styles/icon.js.map +0 -1
- package/lib/styles/input.d.ts.map +0 -1
- package/lib/styles/input.js.map +0 -1
- package/lib/styles/modal.d.ts.map +0 -1
- package/lib/styles/modal.js.map +0 -1
- package/lib/styles/multiselect.d.ts +0 -6
- package/lib/styles/multiselect.d.ts.map +0 -1
- package/lib/styles/multiselect.js +0 -568
- package/lib/styles/multiselect.js.map +0 -1
- package/lib/styles/option.d.ts.map +0 -1
- package/lib/styles/option.js.map +0 -1
- package/lib/styles/popup.d.ts.map +0 -1
- package/lib/styles/popup.js.map +0 -1
- package/lib/styles/resize-observer.d.ts.map +0 -1
- package/lib/styles/resize-observer.js.map +0 -1
- package/lib/styles/scroll-container.d.ts.map +0 -1
- package/lib/styles/scroll-container.js.map +0 -1
- package/lib/styles/step.d.ts.map +0 -1
- package/lib/styles/step.js.map +0 -1
- package/lib/styles/tab.d.ts.map +0 -1
- package/lib/styles/tab.js.map +0 -1
- package/lib/styles/tabs.d.ts.map +0 -1
- package/lib/styles/tabs.js.map +0 -1
- package/lib/styles/tag.d.ts.map +0 -1
- package/lib/styles/tag.js.map +0 -1
- package/lib/styles/textarea.d.ts.map +0 -1
- package/lib/styles/textarea.js.map +0 -1
- package/lib/styles/tooltip.d.ts.map +0 -1
- package/lib/styles/tooltip.js.map +0 -1
- package/lib/styles/wizard.d.ts.map +0 -1
- package/lib/styles/wizard.js.map +0 -1
- package/lib/types/common.d.ts.map +0 -1
- package/lib/types/common.js.map +0 -1
- package/lib/utils/calendar-utils.d.ts.map +0 -1
- package/lib/utils/calendar-utils.js.map +0 -1
- package/lib/utils/icon-registry.d.ts.map +0 -1
- package/lib/utils/icon-registry.js.map +0 -1
- package/lib/utils/locale.d.ts.map +0 -1
- package/lib/utils/locale.js.map +0 -1
- package/lib/utils/number-format.d.ts.map +0 -1
- package/lib/utils/number-format.js.map +0 -1
- package/lib/utils/parse-boolean.d.ts +0 -39
- package/lib/utils/parse-boolean.d.ts.map +0 -1
- package/lib/utils/parse-boolean.js +0 -23
- package/lib/utils/parse-boolean.js.map +0 -1
- package/lib/utils/positioning.d.ts.map +0 -1
- package/lib/utils/positioning.js.map +0 -1
- package/lib/utils/property-capture.d.ts.map +0 -1
- package/lib/utils/property-capture.js.map +0 -1
- package/lib/utils/property-manager.d.ts.map +0 -1
- package/lib/utils/property-manager.js.map +0 -1
- package/lib/utils/resize-observer.d.ts.map +0 -1
- package/lib/utils/resize-observer.js.map +0 -1
- package/lib/utils/scroll-lock.d.ts.map +0 -1
- package/lib/utils/scroll-lock.js.map +0 -1
- package/lib/utils/styles.d.ts.map +0 -1
- package/lib/utils/styles.js.map +0 -1
- package/lib/version.d.ts.map +0 -1
- package/lib/version.js.map +0 -1
|
@@ -17,6 +17,7 @@ export interface TyRadioElement extends HTMLElement {
|
|
|
17
17
|
flavor: Flavor;
|
|
18
18
|
}
|
|
19
19
|
export declare class TyRadio extends TyComponent<RadioState> implements TyRadioElement {
|
|
20
|
+
protected static labelClicks: boolean;
|
|
20
21
|
protected static properties: {
|
|
21
22
|
value: {
|
|
22
23
|
type: "string";
|
|
@@ -165,9 +166,15 @@ export declare class TyRadioGroup extends TyComponent<RadioGroupState> implement
|
|
|
165
166
|
private getRadios;
|
|
166
167
|
private syncChildren;
|
|
167
168
|
/** Report `required` to the owning form — without this the asterisk
|
|
168
|
-
showed but an empty group submitted anyway.
|
|
169
|
+
showed but an empty group submitted anyway. The message is a native
|
|
170
|
+
radio's; it needs a name, a nameless radio never reports valueMissing. */
|
|
169
171
|
private updateValidity;
|
|
170
172
|
private handleRadioSelect;
|
|
173
|
+
/**
|
|
174
|
+
* Select a value as the user did: input + change, like a native radio,
|
|
175
|
+
* whether by click, arrow keys or Space. Nothing fires when it is already selected.
|
|
176
|
+
*/
|
|
177
|
+
private select;
|
|
171
178
|
/**
|
|
172
179
|
* Arrow-key navigation within group (W3C radio practice: arrow keys move
|
|
173
180
|
* focus and selection together).
|
package/lib/components/radio.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ensureStyles } from "../utils/styles.js";
|
|
|
4
4
|
import { syncCustomFlavorSheet } from "../utils/flavor-sheet.js";
|
|
5
5
|
import { radioStyles, radioCustomFlavorCss } from "../styles/radio.js";
|
|
6
6
|
import { REQUIRED_ICON_SVG } from "../utils/icons.js";
|
|
7
|
+
import { nativeMessage } from "../utils/validity.js";
|
|
7
8
|
export class TyRadio extends TyComponent {
|
|
8
9
|
constructor() {
|
|
9
10
|
super();
|
|
@@ -133,6 +134,7 @@ set size(v) { this.setProperty("size", v); }
|
|
|
133
134
|
get flavor() { return this.getProperty("flavor"); }
|
|
134
135
|
set flavor(v) { this.setProperty("flavor", v); }
|
|
135
136
|
}
|
|
137
|
+
TyRadio.labelClicks = true;
|
|
136
138
|
TyRadio.properties = {
|
|
137
139
|
value: { type: "string", default: "" },
|
|
138
140
|
checked: {
|
|
@@ -223,7 +225,7 @@ updateValidity() {
|
|
|
223
225
|
if (this.required && !this.disabled && !this.value) {
|
|
224
226
|
const anchor = this.getRadios().find((r) => !r.disabled);
|
|
225
227
|
const target = anchor?.shadowRoot?.querySelector(".radio-container");
|
|
226
|
-
this.applyValidity({ valueMissing: true }, "
|
|
228
|
+
this.applyValidity({ valueMissing: true }, nativeMessage("input", { type: "radio", name: "ty", required: true }), target ?? undefined);
|
|
227
229
|
}
|
|
228
230
|
else {
|
|
229
231
|
this.applyValidity({});
|
|
@@ -236,16 +238,17 @@ const detail = e.detail;
|
|
|
236
238
|
const newValue = detail?.value;
|
|
237
239
|
if (typeof newValue !== "string" || newValue === this.value)
|
|
238
240
|
return;
|
|
239
|
-
this.
|
|
241
|
+
this.select(newValue, e);
|
|
242
|
+
}
|
|
243
|
+
select(value, originalEvent) {
|
|
244
|
+
if (value === this.value)
|
|
245
|
+
return;
|
|
246
|
+
this.value = value;
|
|
240
247
|
this.syncChildren();
|
|
248
|
+
const detail = { value, formValue: value, originalEvent };
|
|
241
249
|
setTimeout(() => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
formValue: newValue,
|
|
245
|
-
originalEvent: e,
|
|
246
|
-
};
|
|
247
|
-
this.dispatchEvent(new CustomEvent("input", { detail: eventDetail, bubbles: true, composed: true }));
|
|
248
|
-
this.dispatchEvent(new CustomEvent("change", { detail: eventDetail, bubbles: true, composed: true }));
|
|
250
|
+
this.dispatchEvent(new CustomEvent("input", { detail, bubbles: true, composed: true }));
|
|
251
|
+
this.dispatchEvent(new CustomEvent("change", { detail, bubbles: true, composed: true }));
|
|
249
252
|
}, 0);
|
|
250
253
|
}
|
|
251
254
|
handleKeydown(e) {
|
|
@@ -270,16 +273,7 @@ nextIndex = current
|
|
|
270
273
|
else if (["Space", " ", "Enter"].includes(e.key)) {
|
|
271
274
|
if (current) {
|
|
272
275
|
e.preventDefault();
|
|
273
|
-
this.
|
|
274
|
-
this.syncChildren();
|
|
275
|
-
const eventDetail = {
|
|
276
|
-
value: current.value,
|
|
277
|
-
formValue: current.value,
|
|
278
|
-
originalEvent: e,
|
|
279
|
-
};
|
|
280
|
-
setTimeout(() => {
|
|
281
|
-
this.dispatchEvent(new CustomEvent("change", { detail: eventDetail, bubbles: true, composed: true }));
|
|
282
|
-
}, 0);
|
|
276
|
+
this.select(current.value, e);
|
|
283
277
|
}
|
|
284
278
|
return;
|
|
285
279
|
}
|
|
@@ -289,16 +283,7 @@ return;
|
|
|
289
283
|
const next = radios[nextIndex];
|
|
290
284
|
if (next) {
|
|
291
285
|
next.focus();
|
|
292
|
-
this.
|
|
293
|
-
this.syncChildren();
|
|
294
|
-
const eventDetail = {
|
|
295
|
-
value: next.value,
|
|
296
|
-
formValue: next.value,
|
|
297
|
-
originalEvent: e,
|
|
298
|
-
};
|
|
299
|
-
setTimeout(() => {
|
|
300
|
-
this.dispatchEvent(new CustomEvent("change", { detail: eventDetail, bubbles: true, composed: true }));
|
|
301
|
-
}, 0);
|
|
286
|
+
this.select(next.value, e);
|
|
302
287
|
}
|
|
303
288
|
}
|
|
304
289
|
removeEventListeners() {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ensureStyles } from '../utils/styles.js';
|
|
2
2
|
import { resizeObserverStyles } from '../styles/resize-observer.js';
|
|
3
3
|
import { updateSize, removeSize } from '../utils/resize-observer.js';
|
|
4
|
+
import { upgradeProperties } from '../utils/upgrade-properties.js';
|
|
4
5
|
export class TyResizeObserver extends HTMLElement {
|
|
5
6
|
static get observedAttributes() {
|
|
6
7
|
return ['id', 'debounce'];
|
|
@@ -20,6 +21,7 @@ ensureStyles(shadow, { css: resizeObserverStyles, id: 'ty-resize-observer' });
|
|
|
20
21
|
shadow.innerHTML = '<slot></slot>';
|
|
21
22
|
}
|
|
22
23
|
connectedCallback() {
|
|
24
|
+
upgradeProperties(this);
|
|
23
25
|
this.setupObserver();
|
|
24
26
|
}
|
|
25
27
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ensureStyles } from '../utils/styles.js';
|
|
2
2
|
import { scrollContainerStyles } from '../styles/scroll-container.js';
|
|
3
3
|
import { CustomScrollbar } from '../utils/custom-scrollbar.js';
|
|
4
|
+
import { upgradeProperties } from '../utils/upgrade-properties.js';
|
|
4
5
|
export class TyScrollContainer extends HTMLElement {
|
|
5
6
|
static get observedAttributes() {
|
|
6
7
|
return ['no-shadow', 'max-height', 'hide-scrollbar', 'custom-scrollbar', 'overflow-x', 'scroll-anchoring'];
|
|
@@ -112,6 +113,7 @@ this._shadowLeft = shadow.querySelector('.shadow-left');
|
|
|
112
113
|
this._shadowRight = shadow.querySelector('.shadow-right');
|
|
113
114
|
}
|
|
114
115
|
connectedCallback() {
|
|
116
|
+
upgradeProperties(this);
|
|
115
117
|
this._scrollWrapper?.addEventListener('scroll', this._onScroll, { passive: true });
|
|
116
118
|
this._resizeObserver = new ResizeObserver(() => {
|
|
117
119
|
this._scrollbar?.update();
|
|
@@ -73,6 +73,11 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
73
73
|
visual: boolean;
|
|
74
74
|
default: boolean;
|
|
75
75
|
};
|
|
76
|
+
error: {
|
|
77
|
+
type: "string";
|
|
78
|
+
visual: boolean;
|
|
79
|
+
default: string;
|
|
80
|
+
};
|
|
76
81
|
noClear: {
|
|
77
82
|
type: "boolean";
|
|
78
83
|
visual: boolean;
|
|
@@ -190,6 +195,8 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
190
195
|
private _childObserver;
|
|
191
196
|
private _selectedClone;
|
|
192
197
|
private _customFlavorSheet;
|
|
198
|
+
private _ariaTrigger;
|
|
199
|
+
private _ariaObserver;
|
|
193
200
|
constructor();
|
|
194
201
|
/** TyComponent handles property capture automatically. */
|
|
195
202
|
protected onConnect(): void;
|
|
@@ -207,7 +214,11 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
207
214
|
private applyLoadingState;
|
|
208
215
|
/** Form value: a plain entry for single, FormData with repeated entries for multiple (HTMX standard). */
|
|
209
216
|
protected getFormValue(): FormDataEntryValue | FormData | null;
|
|
210
|
-
/**
|
|
217
|
+
/**
|
|
218
|
+
* Focus the trigger: a slotted one, else the field. The host itself isn't
|
|
219
|
+
* focusable. A slotted trigger that can't take focus (a <span>, a disabled
|
|
220
|
+
* button) leaves it to the field.
|
|
221
|
+
*/
|
|
211
222
|
focus(options?: FocusOptions): void;
|
|
212
223
|
private parseValue;
|
|
213
224
|
/** Initialize component state from the value property / pre-selected children. */
|
|
@@ -239,6 +250,12 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
239
250
|
* Get all option elements from the component (ty-option preferred, ty-tag accepted)
|
|
240
251
|
*/
|
|
241
252
|
private getTagElements;
|
|
253
|
+
/**
|
|
254
|
+
* The options on offer: all but the ones the author hid (`hidden`, like a
|
|
255
|
+
* native <option hidden>). The search filters these and the keyboard walks
|
|
256
|
+
* them; a hidden option can still be the value.
|
|
257
|
+
*/
|
|
258
|
+
private getOfferedTags;
|
|
242
259
|
private getTagData;
|
|
243
260
|
/**
|
|
244
261
|
* Select an option - attribute only. The option stays in the list with
|
|
@@ -337,11 +354,17 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
337
354
|
/**
|
|
338
355
|
* Next tag index in `direction` (-1/1) that isn't disabled, wrapping
|
|
339
356
|
* around — mirrors handleTagClick's disabled check so keyboard nav can
|
|
340
|
-
* never land on/select what a click already refuses to.
|
|
341
|
-
*
|
|
357
|
+
* never land on/select what a click already refuses to. Nor on one out of
|
|
358
|
+
* sight: `tags` is the list as it was offered, and an option can get
|
|
359
|
+
* `hidden` while it's open (a server patch). Returns -1 if every tag is
|
|
360
|
+
* skipped (or there are none).
|
|
342
361
|
*/
|
|
343
362
|
private nextEnabledTagIndex;
|
|
344
363
|
private filterTags;
|
|
364
|
+
/**
|
|
365
|
+
* Search filtering marks the options it leaves out `filtered-out`, never
|
|
366
|
+
* `hidden`: that one is the author's (see getOfferedTags).
|
|
367
|
+
*/
|
|
345
368
|
private updateTagVisibility;
|
|
346
369
|
private updateOptionsVisibility;
|
|
347
370
|
private clearHighlights;
|
|
@@ -365,12 +388,22 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
365
388
|
* `compact` after connect never showed up.
|
|
366
389
|
*/
|
|
367
390
|
private syncFieldChrome;
|
|
391
|
+
/**
|
|
392
|
+
* A slotted trigger is what takes focus (focus()), so it carries the error
|
|
393
|
+
* state too. aria-describedby can't reach into our shadow root from the
|
|
394
|
+
* light DOM: the message goes in aria-description instead. The trigger is
|
|
395
|
+
* the author's markup, so a morph can strip these while the error stays:
|
|
396
|
+
* a watch on them puts them back. Writes only on a real change, or the
|
|
397
|
+
* watch would feed itself.
|
|
398
|
+
*/
|
|
399
|
+
private syncTriggerAria;
|
|
368
400
|
/**
|
|
369
401
|
* Reflect constraint validation to the owning <form> via ElementInternals.
|
|
370
402
|
* Without this `required` is only cosmetic — form.checkValidity() (and a
|
|
371
403
|
* real submit) never actually blocks on an empty required select. Mirrors
|
|
372
|
-
* ty-input's updateValidity(): empty + required => valueMissing
|
|
373
|
-
* fields are barred from constraint
|
|
404
|
+
* ty-input's updateValidity(): empty + required => valueMissing, with a
|
|
405
|
+
* native <select>'s message; disabled fields are barred from constraint
|
|
406
|
+
* validation.
|
|
374
407
|
*/
|
|
375
408
|
private updateValidity;
|
|
376
409
|
/**
|
|
@@ -383,6 +416,16 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
383
416
|
private renderDesktop;
|
|
384
417
|
private renderMobile;
|
|
385
418
|
private setupMobileEventListeners;
|
|
419
|
+
/**
|
|
420
|
+
* The dialog was closed by someone else — a ty-modal around this select
|
|
421
|
+
* closes the dialogs still open inside it. Run our own close too, so the
|
|
422
|
+
* open state, aria-expanded and the scroll lock follow. Watches the `open`
|
|
423
|
+
* attribute rather than the `close` event: that one comes a task later,
|
|
424
|
+
* so right after modal.hide() the stub would still say expanded. Our own
|
|
425
|
+
* close() calls land here with the state already closed, and a dialog
|
|
426
|
+
* reopened since then is open again: both are no-ops.
|
|
427
|
+
*/
|
|
428
|
+
private syncOnOutsideClose;
|
|
386
429
|
private handleMobileStubClick;
|
|
387
430
|
/** Keyboard-open the (closed) mobile stub — see handleStubKeydown for why this is needed. */
|
|
388
431
|
private handleMobileStubKeydown;
|
|
@@ -444,7 +487,9 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
444
487
|
* Clear the entire selection programmatically WITH a change event
|
|
445
488
|
* (action: 'clear'). No-op if nothing is selected. Exists so slot="trigger"
|
|
446
489
|
* consumers (custom chrome, built-in clear UI hidden by design) can wire
|
|
447
|
-
* their own clear icon's click handler to it.
|
|
490
|
+
* their own clear icon's click handler to it. Shows no validation
|
|
491
|
+
* message (a code reset after a save shouldn't flag the field); the
|
|
492
|
+
* built-in × does.
|
|
448
493
|
*/
|
|
449
494
|
clear(): void;
|
|
450
495
|
get value(): string;
|
|
@@ -469,6 +514,8 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
469
514
|
set noClear(value: boolean);
|
|
470
515
|
get required(): boolean;
|
|
471
516
|
set required(value: boolean);
|
|
517
|
+
get error(): string;
|
|
518
|
+
set error(value: string);
|
|
472
519
|
get notSearchable(): boolean;
|
|
473
520
|
set notSearchable(value: boolean);
|
|
474
521
|
get externalSearch(): boolean;
|
package/lib/components/select.js
CHANGED
|
@@ -20,6 +20,7 @@ elementIds.set(element, id);
|
|
|
20
20
|
return id;
|
|
21
21
|
}
|
|
22
22
|
import { REQUIRED_ICON_SVG } from "../utils/icons.js";
|
|
23
|
+
import { nativeMessage } from "../utils/validity.js";
|
|
23
24
|
const SEARCH_AUTO_THRESHOLD = 7;
|
|
24
25
|
const CHEVRON_DOWN_SVG = `<svg viewBox="0 0 20 20" fill="currentColor">
|
|
25
26
|
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
|
@@ -81,6 +82,8 @@ this._optionsScrollbar = null;
|
|
|
81
82
|
this._childObserver = null;
|
|
82
83
|
this._selectedClone = null;
|
|
83
84
|
this._customFlavorSheet = null;
|
|
85
|
+
this._ariaTrigger = null;
|
|
86
|
+
this._ariaObserver = null;
|
|
84
87
|
const shadow = this.shadowRoot;
|
|
85
88
|
ensureStyles(shadow, { css: selectBaseStyles, id: "ty-select-base" });
|
|
86
89
|
ensureStyles(shadow, { css: selectStyles, id: "ty-select" });
|
|
@@ -109,11 +112,21 @@ this.updateMobileSelectedState();
|
|
|
109
112
|
this._childObserver.observe(this, { childList: true });
|
|
110
113
|
this.ensureOptionRoles();
|
|
111
114
|
this._syncCustomFlavor();
|
|
115
|
+
if (!this._keyboardHandler) {
|
|
116
|
+
this._keyboardHandler = this.handleKeyboard.bind(this);
|
|
117
|
+
document.addEventListener("keydown", this._keyboardHandler, true);
|
|
118
|
+
}
|
|
112
119
|
}
|
|
113
120
|
_syncCustomFlavor() {
|
|
114
121
|
this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, this.getProperty("flavor"), ({ base }) => selectCustomFlavorCss(base));
|
|
115
122
|
}
|
|
116
123
|
onDisconnect() {
|
|
124
|
+
if (this._state.open) {
|
|
125
|
+
if (this._state.mode === "mobile")
|
|
126
|
+
this.closeMobileModal();
|
|
127
|
+
else
|
|
128
|
+
this.closeDropdown();
|
|
129
|
+
}
|
|
117
130
|
const shadow = this.shadowRoot;
|
|
118
131
|
const dialogs = shadow.querySelectorAll("dialog");
|
|
119
132
|
dialogs.forEach((dialog) => {
|
|
@@ -122,7 +135,7 @@ dialog.close();
|
|
|
122
135
|
}
|
|
123
136
|
});
|
|
124
137
|
if (this._keyboardHandler) {
|
|
125
|
-
document.removeEventListener("keydown", this._keyboardHandler);
|
|
138
|
+
document.removeEventListener("keydown", this._keyboardHandler, true);
|
|
126
139
|
this._keyboardHandler = null;
|
|
127
140
|
}
|
|
128
141
|
if (this._searchDebounceTimer !== null) {
|
|
@@ -256,11 +269,18 @@ return "";
|
|
|
256
269
|
return null;
|
|
257
270
|
}
|
|
258
271
|
focus(options) {
|
|
272
|
+
if (this._disabled)
|
|
273
|
+
return;
|
|
259
274
|
const trigger = this.querySelector(':scope > [slot="trigger"]');
|
|
275
|
+
if (trigger) {
|
|
276
|
+
trigger.focus(options);
|
|
277
|
+
const active = trigger.getRootNode().activeElement;
|
|
278
|
+
if (active && trigger.contains(active))
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
260
281
|
const stub = this.shadowRoot?.querySelector(".select-stub");
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
target.focus(options);
|
|
282
|
+
if (stub)
|
|
283
|
+
stub.focus(options);
|
|
264
284
|
else
|
|
265
285
|
super.focus(options);
|
|
266
286
|
}
|
|
@@ -304,6 +324,11 @@ this.resolveDeclarativeSelection(); }, { once: true });
|
|
|
304
324
|
getTagElements() {
|
|
305
325
|
return Array.from(this.querySelectorAll("ty-option:not([cloned]), ty-tag:not([cloned])"));
|
|
306
326
|
}
|
|
327
|
+
getOfferedTags() {
|
|
328
|
+
return this.getTagElements()
|
|
329
|
+
.filter((el) => !el.hasAttribute("hidden"))
|
|
330
|
+
.map((el) => this.getTagData(el));
|
|
331
|
+
}
|
|
307
332
|
getTagData(element) {
|
|
308
333
|
const value = element.value ||
|
|
309
334
|
element.getAttribute("value") ||
|
|
@@ -345,7 +370,7 @@ if (!tag.hasAttribute("aria-selected"))
|
|
|
345
370
|
tag.setAttribute("aria-selected", "false");
|
|
346
371
|
});
|
|
347
372
|
}
|
|
348
|
-
updateComponentValue(newValues, dispatchChange = false, action = "set", item = null) {
|
|
373
|
+
updateComponentValue(newValues, dispatchChange = false, action = "set", item = null, reveal = dispatchChange) {
|
|
349
374
|
const oldValues = this.getSelectedValues();
|
|
350
375
|
const valueStr = newValues.join(",");
|
|
351
376
|
if (JSON.stringify(newValues.sort()) !== JSON.stringify(oldValues.sort())) {
|
|
@@ -359,6 +384,8 @@ items: this.getSelectedItems(newValues),
|
|
|
359
384
|
action,
|
|
360
385
|
item,
|
|
361
386
|
});
|
|
387
|
+
if (reveal)
|
|
388
|
+
this.revealValidity();
|
|
362
389
|
}
|
|
363
390
|
}
|
|
364
391
|
}
|
|
@@ -468,8 +495,7 @@ chevron.classList.add("open");
|
|
|
468
495
|
const searchChevron = shadow.querySelector(".dropdown-search-chevron");
|
|
469
496
|
if (searchChevron)
|
|
470
497
|
searchChevron.classList.add("open");
|
|
471
|
-
|
|
472
|
-
this._state.filteredTags = tags;
|
|
498
|
+
this._state.filteredTags = this.getOfferedTags();
|
|
473
499
|
this._state.highlightedIndex = -1;
|
|
474
500
|
this.updateOptionsVisibility(true);
|
|
475
501
|
this._setupOptionsScrollbar();
|
|
@@ -521,7 +547,7 @@ this.fireSearchEvent("");
|
|
|
521
547
|
}
|
|
522
548
|
else {
|
|
523
549
|
const allTags = this.getTagElements().map((el) => this.getTagData(el));
|
|
524
|
-
this._state.filteredTags =
|
|
550
|
+
this._state.filteredTags = this.getOfferedTags();
|
|
525
551
|
this.updateTagVisibility(allTags, allTags);
|
|
526
552
|
this.clearHighlights(allTags);
|
|
527
553
|
}
|
|
@@ -543,7 +569,7 @@ dialog.getBoundingClientRect();
|
|
|
543
569
|
dialog.classList.add("open");
|
|
544
570
|
this._state.open = true;
|
|
545
571
|
const tags = this.getTagElements().map((el) => this.getTagData(el));
|
|
546
|
-
this._state.filteredTags =
|
|
572
|
+
this._state.filteredTags = this.getOfferedTags();
|
|
547
573
|
this.updateTagVisibility(tags, tags);
|
|
548
574
|
requestAnimationFrame(() => {
|
|
549
575
|
this.updateMobileSelectedState();
|
|
@@ -583,7 +609,7 @@ this.fireSearchEvent("");
|
|
|
583
609
|
}
|
|
584
610
|
else {
|
|
585
611
|
const allTags = this.getTagElements();
|
|
586
|
-
allTags.forEach((el) => el.removeAttribute("
|
|
612
|
+
allTags.forEach((el) => el.removeAttribute("filtered-out"));
|
|
587
613
|
}
|
|
588
614
|
this.fireCloseEvent();
|
|
589
615
|
}
|
|
@@ -602,6 +628,7 @@ if (this._disabled || this._readonly) {
|
|
|
602
628
|
return;
|
|
603
629
|
}
|
|
604
630
|
this.clear();
|
|
631
|
+
this.revealValidity();
|
|
605
632
|
}
|
|
606
633
|
handleStubKeydown(e) {
|
|
607
634
|
if (this._disabled || this._readonly || this._state.open)
|
|
@@ -702,7 +729,7 @@ desktopInput.value = "";
|
|
|
702
729
|
if (mobileInput)
|
|
703
730
|
mobileInput.value = "";
|
|
704
731
|
const allTags = this.getTagElements().map((el) => this.getTagData(el));
|
|
705
|
-
this._state.filteredTags =
|
|
732
|
+
this._state.filteredTags = this.getOfferedTags();
|
|
706
733
|
this.updateTagVisibility(allTags, allTags);
|
|
707
734
|
this.updateOptionsVisibility(true);
|
|
708
735
|
this.clearHighlights(allTags);
|
|
@@ -723,7 +750,7 @@ if (this._externalSearch) {
|
|
|
723
750
|
this.dispatchSearchEvent(query);
|
|
724
751
|
return;
|
|
725
752
|
}
|
|
726
|
-
const allTags = this.
|
|
753
|
+
const allTags = this.getOfferedTags();
|
|
727
754
|
const filtered = this.filterTags(allTags, query);
|
|
728
755
|
this._state.filteredTags = filtered;
|
|
729
756
|
this._state.highlightedIndex = -1;
|
|
@@ -754,6 +781,7 @@ this.closeMobileModal();
|
|
|
754
781
|
else {
|
|
755
782
|
this.closeDropdown();
|
|
756
783
|
}
|
|
784
|
+
this.focus();
|
|
757
785
|
break;
|
|
758
786
|
case "Enter":
|
|
759
787
|
e.preventDefault();
|
|
@@ -790,7 +818,8 @@ let index = fromIndex === -1
|
|
|
790
818
|
? (direction === 1 ? 0 : count - 1)
|
|
791
819
|
: (fromIndex + direction + count) % count;
|
|
792
820
|
for (let i = 0; i < count; i++) {
|
|
793
|
-
|
|
821
|
+
const el = tags[index].element;
|
|
822
|
+
if (!el.hasAttribute("disabled") && !el.hasAttribute("hidden") && !el.hasAttribute("filtered-out"))
|
|
794
823
|
return index;
|
|
795
824
|
index = (index + direction + count) % count;
|
|
796
825
|
}
|
|
@@ -806,12 +835,7 @@ return tags.filter(({ text }) => text.toLowerCase().includes(searchLower));
|
|
|
806
835
|
updateTagVisibility(filteredTags, allTags) {
|
|
807
836
|
const visibleValues = new Set(filteredTags.map((tag) => tag.value));
|
|
808
837
|
allTags.forEach(({ value, element }) => {
|
|
809
|
-
|
|
810
|
-
element.removeAttribute("hidden");
|
|
811
|
-
}
|
|
812
|
-
else {
|
|
813
|
-
element.setAttribute("hidden", "");
|
|
814
|
-
}
|
|
838
|
+
element.toggleAttribute("filtered-out", !visibleValues.has(value));
|
|
815
839
|
});
|
|
816
840
|
}
|
|
817
841
|
updateOptionsVisibility(hasOptions) {
|
|
@@ -915,10 +939,26 @@ label.innerHTML = `${this._label}${this._required ? `<span class="required-icon"
|
|
|
915
939
|
else {
|
|
916
940
|
label?.remove();
|
|
917
941
|
}
|
|
942
|
+
const error = this.getProperty("error") || "";
|
|
943
|
+
const errorId = `select-error-${getElementHash(this)}`;
|
|
944
|
+
let errorLine = container.querySelector(":scope > .error-message");
|
|
945
|
+
if (error) {
|
|
946
|
+
if (!errorLine) {
|
|
947
|
+
errorLine = document.createElement("div");
|
|
948
|
+
errorLine.className = "error-message";
|
|
949
|
+
errorLine.id = errorId;
|
|
950
|
+
container.append(errorLine);
|
|
951
|
+
}
|
|
952
|
+
errorLine.textContent = error;
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
errorLine?.remove();
|
|
956
|
+
}
|
|
918
957
|
const stub = shadow.querySelector(".select-stub");
|
|
919
958
|
if (stub) {
|
|
920
959
|
stub.classList.toggle("disabled", this._disabled);
|
|
921
960
|
stub.classList.toggle("compact", this._compact);
|
|
961
|
+
stub.classList.toggle("error", !!error);
|
|
922
962
|
stub.tabIndex = this._disabled ? -1 : 0;
|
|
923
963
|
stub.toggleAttribute("disabled", this._disabled);
|
|
924
964
|
if (this._disabled)
|
|
@@ -929,11 +969,45 @@ if (this._label)
|
|
|
929
969
|
stub.setAttribute("aria-labelledby", labelId);
|
|
930
970
|
else
|
|
931
971
|
stub.removeAttribute("aria-labelledby");
|
|
972
|
+
if (error) {
|
|
973
|
+
stub.setAttribute("aria-invalid", "true");
|
|
974
|
+
stub.setAttribute("aria-describedby", errorId);
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
stub.removeAttribute("aria-invalid");
|
|
978
|
+
stub.removeAttribute("aria-describedby");
|
|
979
|
+
}
|
|
932
980
|
}
|
|
981
|
+
this.syncTriggerAria();
|
|
933
982
|
shadow
|
|
934
983
|
.querySelectorAll(".dropdown-search-input, .mobile-search-input")
|
|
935
984
|
.forEach((input) => (input.disabled = this._disabled));
|
|
936
985
|
}
|
|
986
|
+
syncTriggerAria() {
|
|
987
|
+
const error = this.getProperty("error") || "";
|
|
988
|
+
const trigger = error
|
|
989
|
+
? this.querySelector(':scope > [slot="trigger"]')
|
|
990
|
+
: null;
|
|
991
|
+
if (this._ariaTrigger && this._ariaTrigger !== trigger) {
|
|
992
|
+
this._ariaTrigger.removeAttribute("aria-invalid");
|
|
993
|
+
this._ariaTrigger.removeAttribute("aria-description");
|
|
994
|
+
}
|
|
995
|
+
if (trigger) {
|
|
996
|
+
if (trigger.getAttribute("aria-invalid") !== "true")
|
|
997
|
+
trigger.setAttribute("aria-invalid", "true");
|
|
998
|
+
if (trigger.getAttribute("aria-description") !== error)
|
|
999
|
+
trigger.setAttribute("aria-description", error);
|
|
1000
|
+
}
|
|
1001
|
+
this._ariaTrigger = trigger;
|
|
1002
|
+
this._ariaObserver ?? (this._ariaObserver = new MutationObserver(() => this.syncTriggerAria()));
|
|
1003
|
+
this._ariaObserver.disconnect();
|
|
1004
|
+
if (trigger) {
|
|
1005
|
+
this._ariaObserver.observe(trigger, {
|
|
1006
|
+
attributes: true,
|
|
1007
|
+
attributeFilter: ["aria-invalid", "aria-description"],
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
937
1011
|
updateValidity() {
|
|
938
1012
|
if (this.disabled) {
|
|
939
1013
|
this.applyValidity({});
|
|
@@ -941,7 +1015,7 @@ return;
|
|
|
941
1015
|
}
|
|
942
1016
|
if (this.required && this.getSelectedValues().length === 0) {
|
|
943
1017
|
const stub = this.shadowRoot?.querySelector(".select-stub");
|
|
944
|
-
this.applyValidity({ valueMissing: true }, "
|
|
1018
|
+
this.applyValidity({ valueMissing: true }, nativeMessage("select", { required: true, multiple: this._multiple }), stub ?? undefined);
|
|
945
1019
|
}
|
|
946
1020
|
else {
|
|
947
1021
|
this.applyValidity({});
|
|
@@ -958,7 +1032,10 @@ const hasTrigger = slot.assignedElements().length > 0;
|
|
|
958
1032
|
stub.classList.toggle("custom-trigger", hasTrigger);
|
|
959
1033
|
this.toggleAttribute("has-trigger", hasTrigger);
|
|
960
1034
|
};
|
|
961
|
-
slot.addEventListener("slotchange",
|
|
1035
|
+
slot.addEventListener("slotchange", () => {
|
|
1036
|
+
sync();
|
|
1037
|
+
this.syncFieldChrome();
|
|
1038
|
+
});
|
|
962
1039
|
sync();
|
|
963
1040
|
}
|
|
964
1041
|
setupEventListeners() {
|
|
@@ -991,9 +1068,13 @@ if (e.target === dialog) {
|
|
|
991
1068
|
this.closeDropdown();
|
|
992
1069
|
}
|
|
993
1070
|
});
|
|
1071
|
+
dialog.addEventListener("cancel", (e) => {
|
|
1072
|
+
e.preventDefault();
|
|
1073
|
+
this.closeDropdown();
|
|
1074
|
+
this.focus();
|
|
1075
|
+
});
|
|
1076
|
+
this.syncOnOutsideClose(dialog, () => this.closeDropdown());
|
|
994
1077
|
}
|
|
995
|
-
this._keyboardHandler = this.handleKeyboard.bind(this);
|
|
996
|
-
document.addEventListener("keydown", this._keyboardHandler);
|
|
997
1078
|
}
|
|
998
1079
|
buildStubClasses() {
|
|
999
1080
|
const classes = [];
|
|
@@ -1187,10 +1268,6 @@ this.setupMobileEventListeners();
|
|
|
1187
1268
|
this.updateSelectionDisplay();
|
|
1188
1269
|
}
|
|
1189
1270
|
setupMobileEventListeners() {
|
|
1190
|
-
if (!this._keyboardHandler) {
|
|
1191
|
-
this._keyboardHandler = this.handleKeyboard.bind(this);
|
|
1192
|
-
document.addEventListener("keydown", this._keyboardHandler);
|
|
1193
|
-
}
|
|
1194
1271
|
const shadow = this.shadowRoot;
|
|
1195
1272
|
const stub = shadow.querySelector(".select-stub");
|
|
1196
1273
|
const optionsSlot = shadow.querySelector("#options-slot");
|
|
@@ -1225,8 +1302,16 @@ this.closeMobileModal();
|
|
|
1225
1302
|
dialog.addEventListener("cancel", (e) => {
|
|
1226
1303
|
e.preventDefault();
|
|
1227
1304
|
this.closeMobileModal();
|
|
1305
|
+
this.focus();
|
|
1228
1306
|
});
|
|
1307
|
+
this.syncOnOutsideClose(dialog, () => this.closeMobileModal());
|
|
1308
|
+
}
|
|
1229
1309
|
}
|
|
1310
|
+
syncOnOutsideClose(dialog, close) {
|
|
1311
|
+
new MutationObserver(() => {
|
|
1312
|
+
if (this._state.open && !dialog.open)
|
|
1313
|
+
close();
|
|
1314
|
+
}).observe(dialog, { attributes: true, attributeFilter: ["open"] });
|
|
1230
1315
|
}
|
|
1231
1316
|
handleMobileStubClick(e) {
|
|
1232
1317
|
e.preventDefault();
|
|
@@ -1254,7 +1339,7 @@ const shadow = this.shadowRoot;
|
|
|
1254
1339
|
this.renderMobileSelectedChips();
|
|
1255
1340
|
const availableSection = shadow.querySelector(".mobile-available-section");
|
|
1256
1341
|
if (availableSection) {
|
|
1257
|
-
const visibleAvailable = this.
|
|
1342
|
+
const visibleAvailable = this.getOfferedTags().filter(({ element }) => !element.hasAttribute("filtered-out") && !element.hasAttribute("selected")).length;
|
|
1258
1343
|
availableSection.setAttribute("data-empty", String(visibleAvailable === 0));
|
|
1259
1344
|
this.setMobileSectionTitle(availableSection, this._availableLabel, visibleAvailable);
|
|
1260
1345
|
}
|
|
@@ -1399,6 +1484,8 @@ const option = this.getTagElements().find((el) => this.getTagData(el).value ===
|
|
|
1399
1484
|
if (option) {
|
|
1400
1485
|
const clone = option.cloneNode(true);
|
|
1401
1486
|
clone.removeAttribute("selected");
|
|
1487
|
+
clone.removeAttribute("hidden");
|
|
1488
|
+
clone.removeAttribute("filtered-out");
|
|
1402
1489
|
clone.setAttribute("slot", "selected");
|
|
1403
1490
|
clone.setAttribute("cloned", "true");
|
|
1404
1491
|
this.appendChild(clone);
|
|
@@ -1421,7 +1508,7 @@ return;
|
|
|
1421
1508
|
this.updateComponentValue(current.filter((v) => v !== value), true, "remove", value);
|
|
1422
1509
|
}
|
|
1423
1510
|
clear() {
|
|
1424
|
-
this.updateComponentValue([], true, "clear", null);
|
|
1511
|
+
this.updateComponentValue([], true, "clear", null, false);
|
|
1425
1512
|
}
|
|
1426
1513
|
get value() {
|
|
1427
1514
|
return this.getSelectedValues().join(",");
|
|
@@ -1489,6 +1576,12 @@ return this.getProperty("required");
|
|
|
1489
1576
|
set required(value) {
|
|
1490
1577
|
this.setProperty("required", value);
|
|
1491
1578
|
}
|
|
1579
|
+
get error() {
|
|
1580
|
+
return this.getProperty("error");
|
|
1581
|
+
}
|
|
1582
|
+
set error(value) {
|
|
1583
|
+
this.setProperty("error", value);
|
|
1584
|
+
}
|
|
1492
1585
|
get notSearchable() {
|
|
1493
1586
|
return this.getProperty("notSearchable");
|
|
1494
1587
|
}
|
|
@@ -1592,6 +1685,11 @@ type: "boolean",
|
|
|
1592
1685
|
visual: true,
|
|
1593
1686
|
default: false,
|
|
1594
1687
|
},
|
|
1688
|
+
error: {
|
|
1689
|
+
type: "string",
|
|
1690
|
+
visual: true,
|
|
1691
|
+
default: "",
|
|
1692
|
+
},
|
|
1595
1693
|
noClear: {
|
|
1596
1694
|
type: "boolean",
|
|
1597
1695
|
visual: true,
|
|
@@ -24,6 +24,8 @@ export interface TySwitchElement extends HTMLElement {
|
|
|
24
24
|
}
|
|
25
25
|
export declare class TySwitch extends TyComponent<SwitchState> implements TySwitchElement {
|
|
26
26
|
static formAssociated: boolean;
|
|
27
|
+
protected static delegatesFocus: boolean;
|
|
28
|
+
protected static labelClicks: boolean;
|
|
27
29
|
protected static properties: {
|
|
28
30
|
checked: {
|
|
29
31
|
type: "boolean";
|
|
@@ -78,13 +80,17 @@ export declare class TySwitch extends TyComponent<SwitchState> implements TySwit
|
|
|
78
80
|
protected getFormValue(): FormDataEntryValue | null;
|
|
79
81
|
private buildClassList;
|
|
80
82
|
private handleSwitchClick;
|
|
83
|
+
/** Keys show the ring, a click hides it (see checkbox.ts). */
|
|
84
|
+
private showFocusRing;
|
|
81
85
|
private handleSwitchKeydown;
|
|
82
86
|
private removeEventListeners;
|
|
83
87
|
private setupEventListeners;
|
|
84
88
|
protected render(): void;
|
|
85
89
|
/** Focus the switch itself (the host is not focusable; its role element is). */
|
|
86
90
|
focus(options?: FocusOptions): void;
|
|
87
|
-
/** required + unchecked => valueMissing; otherwise valid.
|
|
91
|
+
/** required + unchecked => valueMissing; otherwise valid. The message is a
|
|
92
|
+
* native checkbox's, as a switch where the browser has one (Safari's
|
|
93
|
+
* `switch` attribute). */
|
|
88
94
|
private updateValidity;
|
|
89
95
|
get checked(): boolean;
|
|
90
96
|
set checked(value: boolean);
|