tyrell-components 1.0.0-TC57 → 1.0.0-TC58
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/dist/tyrell.js +1 -1
- package/dist/tyrell.js.map +1 -0
- package/lib/base/ty-component.d.ts +12 -3
- package/lib/base/ty-component.js +21 -17
- package/lib/components/button.d.ts +4 -0
- package/lib/components/button.js +33 -6
- package/lib/components/calendar-month.js +3 -10
- package/lib/components/calendar.d.ts +3 -1
- package/lib/components/calendar.js +19 -34
- package/lib/components/checkbox.d.ts +1 -1
- package/lib/components/checkbox.js +2 -1
- package/lib/components/date-picker.d.ts +60 -8
- package/lib/components/date-picker.js +268 -134
- package/lib/components/file-upload.d.ts +2 -1
- package/lib/components/file-upload.js +2 -1
- package/lib/components/input.d.ts +13 -6
- package/lib/components/input.js +58 -60
- package/lib/components/modal.js +37 -1
- package/lib/components/option.js +2 -5
- package/lib/components/popup.js +43 -6
- package/lib/components/radio.d.ts +2 -1
- package/lib/components/radio.js +2 -1
- package/lib/components/resize-observer.js +2 -0
- package/lib/components/scroll-container.js +2 -0
- package/lib/components/select.d.ts +32 -3
- package/lib/components/select.js +123 -11
- package/lib/components/switch.d.ts +3 -1
- package/lib/components/switch.js +2 -1
- package/lib/components/textarea.d.ts +44 -4
- package/lib/components/textarea.js +54 -10
- package/lib/styles/button.js +20 -19
- package/lib/styles/date-picker.js +17 -0
- package/lib/styles/select-base.js +26 -0
- package/lib/styles/textarea.d.ts +1 -1
- package/lib/styles/textarea.js +7 -0
- package/lib/utils/error-flavor.d.ts +8 -0
- package/lib/utils/error-flavor.js +1 -0
- package/lib/utils/upgrade-properties.d.ts +21 -0
- package/lib/utils/upgrade-properties.js +47 -0
- package/lib/utils/validity.d.ts +63 -5
- package/lib/utils/validity.js +145 -24
- 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
|
@@ -81,7 +81,8 @@ export declare class TyFileUpload extends TyComponent<FileUploadState> implement
|
|
|
81
81
|
protected onFormReset(): void;
|
|
82
82
|
protected updateFormValue(): void;
|
|
83
83
|
/** Report `required` to the owning form, like ty-input and ty-textarea
|
|
84
|
-
do — without this the asterisk showed but submission went through.
|
|
84
|
+
do — without this the asterisk showed but submission went through.
|
|
85
|
+
The message is a native file input's. */
|
|
85
86
|
private updateValidity;
|
|
86
87
|
protected getFormValue(): FormData | string | null;
|
|
87
88
|
/** Focus the drop zone: the host itself isn't focusable. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TyComponent } from "../base/ty-component.js";
|
|
2
2
|
import { ensureStyles } from "../utils/styles.js";
|
|
3
3
|
import { fileUploadStyles } from "../styles/file-upload.js";
|
|
4
|
+
import { nativeMessage } from "../utils/validity.js";
|
|
4
5
|
const UPLOAD_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>`;
|
|
5
6
|
const FILE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>`;
|
|
6
7
|
const X_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`;
|
|
@@ -52,7 +53,7 @@ this.updateValidity();
|
|
|
52
53
|
updateValidity() {
|
|
53
54
|
if (this.required && !this.disabled && this._files.length === 0) {
|
|
54
55
|
const anchor = this.shadowRoot?.querySelector(".drop-zone");
|
|
55
|
-
this.applyValidity({ valueMissing: true }, "
|
|
56
|
+
this.applyValidity({ valueMissing: true }, nativeMessage("input", { type: "file", required: true, multiple: this.multiple }), anchor ?? undefined);
|
|
56
57
|
}
|
|
57
58
|
else {
|
|
58
59
|
this.applyValidity({});
|
|
@@ -137,10 +137,7 @@ export declare class TyInput extends TyComponent<InputState> implements TyInputE
|
|
|
137
137
|
};
|
|
138
138
|
private _shadowValue;
|
|
139
139
|
private _isFocused;
|
|
140
|
-
/** The user typed since the field was created: blur then shows its validation message. */
|
|
141
|
-
private _userEdited;
|
|
142
140
|
private _listenersSetup;
|
|
143
|
-
private _errorAutoDanger;
|
|
144
141
|
private _customFlavorSheet;
|
|
145
142
|
private _inputHandler;
|
|
146
143
|
private _changeHandler;
|
|
@@ -154,6 +151,14 @@ export declare class TyInput extends TyComponent<InputState> implements TyInputE
|
|
|
154
151
|
protected onConnect(): void;
|
|
155
152
|
/** Custom (non-built-in) flavors — see utils/flavor-sheet.ts. */
|
|
156
153
|
private _syncCustomFlavor;
|
|
154
|
+
/**
|
|
155
|
+
* `error` turns a neutral field danger; clearing it (the author, or
|
|
156
|
+
* validation dropping the message it showed) puts back exactly the flavor
|
|
157
|
+
* attribute the author had, or none, kept meanwhile in ERROR_FLAVOR (same
|
|
158
|
+
* as ty-select and ty-date-picker). Runs once connected: while the element
|
|
159
|
+
* upgrades, `error` may come before the `flavor` written next to it.
|
|
160
|
+
*/
|
|
161
|
+
private syncErrorFlavor;
|
|
157
162
|
protected onDisconnect(): void;
|
|
158
163
|
/** Handle property changes — called BEFORE render. */
|
|
159
164
|
protected onPropertiesChanged(changes: PropertyChange[]): void;
|
|
@@ -204,6 +209,7 @@ export declare class TyInput extends TyComponent<InputState> implements TyInputE
|
|
|
204
209
|
set size(v: Size);
|
|
205
210
|
get flavor(): Flavor;
|
|
206
211
|
set flavor(v: Flavor);
|
|
212
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
207
213
|
get currency(): string;
|
|
208
214
|
set currency(v: string);
|
|
209
215
|
get locale(): string;
|
|
@@ -235,10 +241,11 @@ export declare class TyInput extends TyComponent<InputState> implements TyInputE
|
|
|
235
241
|
*/
|
|
236
242
|
private setupPasswordToggle;
|
|
237
243
|
/**
|
|
238
|
-
* pattern and maxlength go on the inner <input>: pattern for the
|
|
239
|
-
* own patternMismatch check,
|
|
244
|
+
* pattern, minlength and maxlength go on the inner <input>: pattern for the
|
|
245
|
+
* browser's own patternMismatch check, minlength for its tooShort message
|
|
246
|
+
* after the user types, maxlength so typing stops at the limit like a
|
|
240
247
|
* native field. Formatted (numeric) types show a display string, so they
|
|
241
|
-
* get
|
|
248
|
+
* get none.
|
|
242
249
|
*/
|
|
243
250
|
private syncConstraintAttributes;
|
|
244
251
|
/**
|
package/lib/components/input.js
CHANGED
|
@@ -2,10 +2,12 @@ import { DEFAULT_SIZE } from '../types/common.js';
|
|
|
2
2
|
import { TyComponent } from '../base/ty-component.js';
|
|
3
3
|
import { ensureStyles } from '../utils/styles.js';
|
|
4
4
|
import { syncCustomFlavorSheet } from '../utils/flavor-sheet.js';
|
|
5
|
+
import { ERROR_FLAVOR } from '../utils/error-flavor.js';
|
|
5
6
|
import { inputStyles, inputCustomFlavorCss } from '../styles/input.js';
|
|
6
7
|
import { formatNumber, parseNumericValue, shouldFormat as shouldFormatType } from '../utils/number-format.js';
|
|
7
8
|
import { getEffectiveLocale, observeLocaleChanges } from '../utils/locale.js';
|
|
8
9
|
import { REQUIRED_ICON_SVG } from '../utils/icons.js';
|
|
10
|
+
import { nativeMessage, nativeValidity, lengthValidity, toLimit } from '../utils/validity.js';
|
|
9
11
|
const EYE_ICON_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/><circle cx="12" cy="12" r="3"/></svg>`;
|
|
10
12
|
const EYE_OFF_ICON_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"/><path d="M14.084 14.158a3 3 0 0 1-4.242-4.242"/><path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"/><path d="m2 2 20 20"/></svg>`;
|
|
11
13
|
export class TyInput extends TyComponent {
|
|
@@ -13,9 +15,7 @@ constructor() {
|
|
|
13
15
|
super();
|
|
14
16
|
this._shadowValue = null;
|
|
15
17
|
this._isFocused = false;
|
|
16
|
-
this._userEdited = false;
|
|
17
18
|
this._listenersSetup = false;
|
|
18
|
-
this._errorAutoDanger = false;
|
|
19
19
|
this._customFlavorSheet = null;
|
|
20
20
|
this._inputHandler = null;
|
|
21
21
|
this._changeHandler = null;
|
|
@@ -60,10 +60,32 @@ this._localeObserver = observeLocaleChanges(this, () => {
|
|
|
60
60
|
this.render();
|
|
61
61
|
});
|
|
62
62
|
this._syncCustomFlavor();
|
|
63
|
+
this.syncErrorFlavor();
|
|
63
64
|
}
|
|
64
65
|
_syncCustomFlavor() {
|
|
65
66
|
this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, this.flavor, ({ base }) => inputCustomFlavorCss(base));
|
|
66
67
|
}
|
|
68
|
+
syncErrorFlavor() {
|
|
69
|
+
if (!this._connected)
|
|
70
|
+
return;
|
|
71
|
+
const flavor = this.getProperty('flavor');
|
|
72
|
+
const replaced = this.getAttribute(ERROR_FLAVOR);
|
|
73
|
+
if (this.getProperty('error')) {
|
|
74
|
+
if (replaced === null && flavor === 'neutral') {
|
|
75
|
+
this.setAttribute(ERROR_FLAVOR, this.getAttribute('flavor') ?? '');
|
|
76
|
+
this.setProperty('flavor', 'danger');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (replaced !== null) {
|
|
80
|
+
this.removeAttribute(ERROR_FLAVOR);
|
|
81
|
+
if (flavor !== 'danger')
|
|
82
|
+
return;
|
|
83
|
+
if (!replaced)
|
|
84
|
+
this.removeAttribute('flavor');
|
|
85
|
+
else
|
|
86
|
+
this.setProperty('flavor', replaced);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
67
89
|
onDisconnect() {
|
|
68
90
|
this.removeEventListeners();
|
|
69
91
|
if (this._localeObserver) {
|
|
@@ -83,6 +105,10 @@ onPropertiesChanged(changes) {
|
|
|
83
105
|
for (const { name, newValue } of changes) {
|
|
84
106
|
switch (name) {
|
|
85
107
|
case 'flavor':
|
|
108
|
+
if (newValue !== 'danger') {
|
|
109
|
+
this.removeAttribute(ERROR_FLAVOR);
|
|
110
|
+
this.syncErrorFlavor();
|
|
111
|
+
}
|
|
86
112
|
this._syncCustomFlavor();
|
|
87
113
|
break;
|
|
88
114
|
case 'value':
|
|
@@ -93,18 +119,7 @@ const currentValue = this.getProperty('value') || '';
|
|
|
93
119
|
this._shadowValue = this.parseShadowValue(currentValue);
|
|
94
120
|
break;
|
|
95
121
|
case 'error':
|
|
96
|
-
|
|
97
|
-
if (this.getProperty('flavor') === 'neutral') {
|
|
98
|
-
this.setProperty('flavor', 'danger');
|
|
99
|
-
this._errorAutoDanger = true;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
else if (this._errorAutoDanger) {
|
|
103
|
-
if (this.getProperty('flavor') === 'danger') {
|
|
104
|
-
this.setProperty('flavor', 'neutral');
|
|
105
|
-
}
|
|
106
|
-
this._errorAutoDanger = false;
|
|
107
|
-
}
|
|
122
|
+
this.syncErrorFlavor();
|
|
108
123
|
break;
|
|
109
124
|
}
|
|
110
125
|
}
|
|
@@ -211,7 +226,15 @@ set error(v) { this.setProperty('error', v); }
|
|
|
211
226
|
get size() { return this.getProperty('size'); }
|
|
212
227
|
set size(v) { this.setProperty('size', v); }
|
|
213
228
|
get flavor() { return this.getProperty('flavor'); }
|
|
214
|
-
set flavor(v) {
|
|
229
|
+
set flavor(v) {
|
|
230
|
+
this.removeAttribute(ERROR_FLAVOR);
|
|
231
|
+
this.setProperty('flavor', v);
|
|
232
|
+
}
|
|
233
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
234
|
+
if (name === 'flavor' && newValue === 'danger' && oldValue === newValue)
|
|
235
|
+
this.removeAttribute(ERROR_FLAVOR);
|
|
236
|
+
super.attributeChangedCallback(name, oldValue, newValue);
|
|
237
|
+
}
|
|
215
238
|
get currency() { return this.getProperty('currency'); }
|
|
216
239
|
set currency(v) { this.setProperty('currency', v); }
|
|
217
240
|
get locale() {
|
|
@@ -404,6 +427,7 @@ const inputMode = shouldFormatType(this.type) ? 'decimal'
|
|
|
404
427
|
const displayValue = this.getDisplayValue();
|
|
405
428
|
if (existingInput && existingWrapper) {
|
|
406
429
|
existingInput.type = inputType;
|
|
430
|
+
if (existingInput.value !== displayValue)
|
|
407
431
|
existingInput.value = displayValue;
|
|
408
432
|
existingInput.placeholder = this.placeholder;
|
|
409
433
|
existingInput.name = this.name;
|
|
@@ -527,6 +551,7 @@ syncConstraintAttributes(inputEl) {
|
|
|
527
551
|
const text = !shouldFormatType(this.type);
|
|
528
552
|
const set = (name, v) => text && v !== '' ? inputEl.setAttribute(name, v) : inputEl.removeAttribute(name);
|
|
529
553
|
set('pattern', this.pattern);
|
|
554
|
+
set('minlength', toLimit(this.getProperty('minlength')) != null ? String(this.minLength) : '');
|
|
530
555
|
set('maxlength', toLimit(this.getProperty('maxlength')) != null ? String(this.maxLength) : '');
|
|
531
556
|
}
|
|
532
557
|
updateValidity() {
|
|
@@ -538,7 +563,7 @@ return;
|
|
|
538
563
|
const current = inputEl ? inputEl.value : String(this.getProperty('value') ?? '');
|
|
539
564
|
if (current.trim() === '') {
|
|
540
565
|
if (this.required) {
|
|
541
|
-
this.applyValidity({ valueMissing: true }, nativeMessage('
|
|
566
|
+
this.applyValidity({ valueMissing: true }, nativeMessage('input', { type: 'text', required: true }), inputEl ?? undefined);
|
|
542
567
|
}
|
|
543
568
|
else {
|
|
544
569
|
this.applyValidity({});
|
|
@@ -553,29 +578,29 @@ messages.push(message);
|
|
|
553
578
|
};
|
|
554
579
|
if (shouldFormatType(this.type)) {
|
|
555
580
|
const n = typeof this._shadowValue === 'number' ? this._shadowValue : null;
|
|
556
|
-
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
581
|
+
if (n !== null && (this.min || this.max)) {
|
|
582
|
+
const { validity, message } = nativeValidity('input', { type: 'number', step: 'any', min: this.min, max: this.max }, String(n));
|
|
583
|
+
if (validity.rangeUnderflow)
|
|
584
|
+
fail('rangeUnderflow', message);
|
|
585
|
+
if (validity.rangeOverflow)
|
|
586
|
+
fail('rangeOverflow', message);
|
|
587
|
+
}
|
|
563
588
|
}
|
|
564
589
|
else {
|
|
565
590
|
if (inputEl?.validity.typeMismatch)
|
|
566
591
|
fail('typeMismatch', inputEl.validationMessage);
|
|
567
592
|
if (inputEl?.validity.patternMismatch)
|
|
568
593
|
fail('patternMismatch', inputEl.validationMessage);
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
594
|
+
const { tooShort, tooLong } = lengthValidity(current, {
|
|
595
|
+
minlength: this.getProperty('minlength'),
|
|
596
|
+
maxlength: this.getProperty('maxlength'),
|
|
597
|
+
tooShortMessage: this.tooShortMessage,
|
|
598
|
+
tooLongMessage: this.tooLongMessage,
|
|
599
|
+
}, inputEl);
|
|
600
|
+
if (tooShort)
|
|
601
|
+
fail('tooShort', tooShort);
|
|
602
|
+
if (tooLong)
|
|
603
|
+
fail('tooLong', tooLong);
|
|
579
604
|
}
|
|
580
605
|
this.applyValidity(flags, messages[0], inputEl ?? undefined);
|
|
581
606
|
}
|
|
@@ -737,33 +762,6 @@ return Math.max(0, Math.min(5000, Math.floor(num)));
|
|
|
737
762
|
}
|
|
738
763
|
}
|
|
739
764
|
};
|
|
740
|
-
let probe = null;
|
|
741
|
-
function nativeMessage(type, attrs, value) {
|
|
742
|
-
probe ?? (probe = document.createElement('input'));
|
|
743
|
-
for (const name of Array.from(probe.getAttributeNames()))
|
|
744
|
-
probe.removeAttribute(name);
|
|
745
|
-
probe.type = type;
|
|
746
|
-
for (const [name, v] of Object.entries(attrs))
|
|
747
|
-
if (v !== '' || name === 'required')
|
|
748
|
-
probe.setAttribute(name, v);
|
|
749
|
-
probe.value = value;
|
|
750
|
-
return probe.validationMessage;
|
|
751
|
-
}
|
|
752
|
-
function fillMessage(template, vars) {
|
|
753
|
-
return template.replace(/\{(\w+)\}/g, (match, key) => vars[key] != null ? String(vars[key]) : match);
|
|
754
|
-
}
|
|
755
|
-
function toLimit(v) {
|
|
756
|
-
if (v === null || v === undefined || String(v).trim() === '')
|
|
757
|
-
return null;
|
|
758
|
-
const n = Number(v);
|
|
759
|
-
return Number.isInteger(n) && n >= 0 ? n : null;
|
|
760
|
-
}
|
|
761
|
-
function toNumber(v) {
|
|
762
|
-
if (v === null || v === undefined || String(v).trim() === '')
|
|
763
|
-
return null;
|
|
764
|
-
const n = Number(v);
|
|
765
|
-
return Number.isFinite(n) ? n : null;
|
|
766
|
-
}
|
|
767
765
|
if (!customElements.get('ty-input')) {
|
|
768
766
|
customElements.define('ty-input', TyInput);
|
|
769
767
|
}
|
package/lib/components/modal.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { lockScroll, unlockScroll } from '../utils/scroll-lock.js';
|
|
2
2
|
import { coerceBoolean } from '../utils/property-manager.js';
|
|
3
3
|
import { ensureStyles } from '../utils/styles.js';
|
|
4
|
+
import { upgradeProperties } from '../utils/upgrade-properties.js';
|
|
4
5
|
import { modalStyles } from '../styles/modal.js';
|
|
5
6
|
const backdropClickHandlers = new WeakMap();
|
|
6
7
|
const escapeKeyHandlers = new WeakMap();
|
|
@@ -98,7 +99,39 @@ event.preventDefault();
|
|
|
98
99
|
closeModal(el, 'backdrop');
|
|
99
100
|
}
|
|
100
101
|
}
|
|
102
|
+
function fromNestedDialog(dialog, event) {
|
|
103
|
+
for (const node of event.composedPath()) {
|
|
104
|
+
if (node === dialog)
|
|
105
|
+
return false;
|
|
106
|
+
if (node instanceof HTMLDialogElement && node.matches(':modal'))
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
function closeNestedDialogs(root, seen = new WeakSet()) {
|
|
112
|
+
const close = (node) => {
|
|
113
|
+
if (seen.has(node))
|
|
114
|
+
return;
|
|
115
|
+
seen.add(node);
|
|
116
|
+
if (node instanceof TyModal && node.open)
|
|
117
|
+
node.removeAttribute('open');
|
|
118
|
+
if (node instanceof HTMLDialogElement && node.matches(':modal'))
|
|
119
|
+
node.close();
|
|
120
|
+
if (node.shadowRoot)
|
|
121
|
+
closeNestedDialogs(node.shadowRoot, seen);
|
|
122
|
+
if (node instanceof HTMLSlotElement) {
|
|
123
|
+
for (const assigned of node.assignedElements({ flatten: true })) {
|
|
124
|
+
close(assigned);
|
|
125
|
+
closeNestedDialogs(assigned, seen);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
root.querySelectorAll('*').forEach(close);
|
|
130
|
+
}
|
|
101
131
|
function handleEscapeKey(el, event) {
|
|
132
|
+
const dialog = event.currentTarget;
|
|
133
|
+
if (fromNestedDialog(dialog, event))
|
|
134
|
+
return;
|
|
102
135
|
event.stopPropagation();
|
|
103
136
|
if (event.key === 'Escape') {
|
|
104
137
|
event.preventDefault();
|
|
@@ -155,7 +188,7 @@ dialog.addEventListener('keydown', handler);
|
|
|
155
188
|
}
|
|
156
189
|
else {
|
|
157
190
|
const handler = (e) => {
|
|
158
|
-
if (e.key === 'Escape')
|
|
191
|
+
if (e.key === 'Escape' && !fromNestedDialog(dialog, e))
|
|
159
192
|
e.preventDefault();
|
|
160
193
|
};
|
|
161
194
|
escapeKeyHandlers.set(el, handler);
|
|
@@ -253,6 +286,7 @@ dispatchModalEvent(el, 'open', {});
|
|
|
253
286
|
else {
|
|
254
287
|
if (dialog.open) {
|
|
255
288
|
unlockScroll(modalId);
|
|
289
|
+
closeNestedDialogs(el);
|
|
256
290
|
dialog.close();
|
|
257
291
|
syncBackdropZoom();
|
|
258
292
|
const reason = pendingCloseReasons.get(el) ?? 'programmatic';
|
|
@@ -265,6 +299,7 @@ dialog.onclose = (event) => {
|
|
|
265
299
|
if (event.target !== dialog)
|
|
266
300
|
return;
|
|
267
301
|
unlockScroll(modalId);
|
|
302
|
+
closeNestedDialogs(el);
|
|
268
303
|
if (el.hasAttribute('open')) {
|
|
269
304
|
el.removeAttribute('open');
|
|
270
305
|
dispatchModalEvent(el, 'close', {
|
|
@@ -349,6 +384,7 @@ super();
|
|
|
349
384
|
this.attachShadow({ mode: 'open' });
|
|
350
385
|
}
|
|
351
386
|
connectedCallback() {
|
|
387
|
+
upgradeProperties(this);
|
|
352
388
|
render(this);
|
|
353
389
|
this.show = () => openModal(this);
|
|
354
390
|
this.hide = (opts) => closeModal(this, 'programmatic', opts);
|
package/lib/components/option.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ensureStyles } from '../utils/styles.js';
|
|
2
2
|
import { coerceBoolean } from '../utils/property-manager.js';
|
|
3
|
+
import { upgradeProperties } from '../utils/upgrade-properties.js';
|
|
3
4
|
import { optionStyles } from '../styles/option.js';
|
|
4
5
|
const CHECK_SVG = `
|
|
5
6
|
<svg class="option-check" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
@@ -23,11 +24,7 @@ static get observedAttributes() {
|
|
|
23
24
|
return ['value', 'disabled', 'selected', 'highlighted', 'hidden'];
|
|
24
25
|
}
|
|
25
26
|
connectedCallback() {
|
|
26
|
-
|
|
27
|
-
if (instanceValue) {
|
|
28
|
-
this._value = instanceValue.value;
|
|
29
|
-
delete this.value;
|
|
30
|
-
}
|
|
27
|
+
upgradeProperties(this);
|
|
31
28
|
this.render();
|
|
32
29
|
}
|
|
33
30
|
attributeChangedCallback(name, oldValue, newValue) {
|
package/lib/components/popup.js
CHANGED
|
@@ -9,6 +9,7 @@ const closeTimers = new WeakMap();
|
|
|
9
9
|
const escapeHandlers = new WeakMap();
|
|
10
10
|
const closeRequestHandlers = new WeakMap();
|
|
11
11
|
const popupIds = new WeakMap();
|
|
12
|
+
const pointerPresses = new WeakSet();
|
|
12
13
|
function parseBoolAttr(el, name) {
|
|
13
14
|
return el.hasAttribute(name);
|
|
14
15
|
}
|
|
@@ -89,19 +90,21 @@ const shadowRoot = el.shadowRoot;
|
|
|
89
90
|
const dialog = shadowRoot ? getPopupDialog(shadowRoot) : null;
|
|
90
91
|
if (!dialog)
|
|
91
92
|
return;
|
|
93
|
+
const anchor = getAnchorElement(el);
|
|
92
94
|
if (dialog.open) {
|
|
93
95
|
const pending = closeTimers.get(el);
|
|
94
96
|
if (pending !== undefined) {
|
|
95
97
|
clearTimeout(pending);
|
|
96
98
|
closeTimers.delete(el);
|
|
97
99
|
dialog.classList.add('open');
|
|
100
|
+
if (anchor?.hasAttribute('aria-haspopup'))
|
|
101
|
+
anchor.setAttribute('aria-expanded', 'true');
|
|
98
102
|
}
|
|
99
103
|
return;
|
|
100
104
|
}
|
|
101
105
|
const popupId = getPopupId(el);
|
|
102
106
|
lockScroll(popupId);
|
|
103
107
|
dialog.showModal();
|
|
104
|
-
const anchor = getAnchorElement(el);
|
|
105
108
|
if (anchor?.hasAttribute('aria-haspopup'))
|
|
106
109
|
anchor.setAttribute('aria-expanded', 'true');
|
|
107
110
|
requestAnimationFrame(() => {
|
|
@@ -140,7 +143,19 @@ event.preventDefault();
|
|
|
140
143
|
closePopup(el);
|
|
141
144
|
}
|
|
142
145
|
}
|
|
146
|
+
function fromNestedDialog(el, event) {
|
|
147
|
+
const dialog = el.shadowRoot ? getPopupDialog(el.shadowRoot) : null;
|
|
148
|
+
for (const node of event.composedPath()) {
|
|
149
|
+
if (node === dialog)
|
|
150
|
+
return false;
|
|
151
|
+
if (node instanceof HTMLDialogElement && node.matches(':modal'))
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
143
156
|
function handleEscape(el, event) {
|
|
157
|
+
if (fromNestedDialog(el, event))
|
|
158
|
+
return;
|
|
144
159
|
event.stopPropagation();
|
|
145
160
|
if (event.key === 'Escape') {
|
|
146
161
|
const { disableClose } = getPopupAttributes(el);
|
|
@@ -172,11 +187,21 @@ const existingKeyHandler = anchorKeyClickHandlers.get(el);
|
|
|
172
187
|
if (existingKeyHandler) {
|
|
173
188
|
anchor.removeEventListener('click', existingKeyHandler);
|
|
174
189
|
}
|
|
175
|
-
const handler = (e) =>
|
|
190
|
+
const handler = (e) => {
|
|
191
|
+
pointerPresses.add(el);
|
|
192
|
+
handleAnchorClick(el, e);
|
|
193
|
+
};
|
|
176
194
|
anchorClickHandlers.set(el, handler);
|
|
177
195
|
anchor.addEventListener('pointerdown', handler);
|
|
178
196
|
const keyHandler = (e) => {
|
|
179
|
-
if (
|
|
197
|
+
if (pointerPresses.delete(el))
|
|
198
|
+
return;
|
|
199
|
+
const d = e.detail;
|
|
200
|
+
const click = typeof d === 'number' ? e : d?.originalEvent ?? e;
|
|
201
|
+
if (click.detail !== 0)
|
|
202
|
+
return;
|
|
203
|
+
if (click.composedPath().includes(el))
|
|
204
|
+
return;
|
|
180
205
|
handleAnchorClick(el, e);
|
|
181
206
|
};
|
|
182
207
|
anchorKeyClickHandlers.set(el, keyHandler);
|
|
@@ -221,17 +246,29 @@ dialog.addEventListener('pointerdown', outsideHandler);
|
|
|
221
246
|
const escapeHandler = (e) => handleEscape(el, e);
|
|
222
247
|
escapeHandlers.set(el, escapeHandler);
|
|
223
248
|
el.addEventListener('keydown', escapeHandler);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
249
|
+
new MutationObserver(() => {
|
|
250
|
+
if (dialog.open)
|
|
251
|
+
return;
|
|
252
|
+
clearTimeout(closeTimers.get(el));
|
|
253
|
+
closeTimers.delete(el);
|
|
254
|
+
pointerPresses.delete(el);
|
|
255
|
+
unlockScroll(getPopupId(el));
|
|
227
256
|
dialog.classList.remove('open');
|
|
228
257
|
dialog.classList.remove('preparing-animation');
|
|
258
|
+
const anchor = getAnchorElement(el);
|
|
259
|
+
if (anchor?.hasAttribute('aria-haspopup'))
|
|
260
|
+
anchor.setAttribute('aria-expanded', 'false');
|
|
261
|
+
}).observe(dialog, { attributes: true, attributeFilter: ['open'] });
|
|
262
|
+
dialog.addEventListener('close', () => {
|
|
229
263
|
el.dispatchEvent(new CustomEvent('close'));
|
|
230
264
|
});
|
|
231
265
|
}
|
|
232
266
|
el.closePopup = () => closePopup(el, true);
|
|
233
267
|
el.openPopup = () => openPopup(el);
|
|
234
268
|
el.togglePopup = () => togglePopup(el);
|
|
269
|
+
const existingCloseRequest = closeRequestHandlers.get(el);
|
|
270
|
+
if (existingCloseRequest)
|
|
271
|
+
el.removeEventListener('ty:close-popup', existingCloseRequest);
|
|
235
272
|
const closeRequestHandler = (e) => handleCloseRequest(el, e);
|
|
236
273
|
closeRequestHandlers.set(el, closeRequestHandler);
|
|
237
274
|
el.addEventListener('ty:close-popup', closeRequestHandler);
|
|
@@ -165,7 +165,8 @@ export declare class TyRadioGroup extends TyComponent<RadioGroupState> implement
|
|
|
165
165
|
private getRadios;
|
|
166
166
|
private syncChildren;
|
|
167
167
|
/** Report `required` to the owning form — without this the asterisk
|
|
168
|
-
showed but an empty group submitted anyway.
|
|
168
|
+
showed but an empty group submitted anyway. The message is a native
|
|
169
|
+
radio's; it needs a name, a nameless radio never reports valueMissing. */
|
|
169
170
|
private updateValidity;
|
|
170
171
|
private handleRadioSelect;
|
|
171
172
|
/**
|
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();
|
|
@@ -223,7 +224,7 @@ updateValidity() {
|
|
|
223
224
|
if (this.required && !this.disabled && !this.value) {
|
|
224
225
|
const anchor = this.getRadios().find((r) => !r.disabled);
|
|
225
226
|
const target = anchor?.shadowRoot?.querySelector(".radio-container");
|
|
226
|
-
this.applyValidity({ valueMissing: true }, "
|
|
227
|
+
this.applyValidity({ valueMissing: true }, nativeMessage("input", { type: "radio", name: "ty", required: true }), target ?? undefined);
|
|
227
228
|
}
|
|
228
229
|
else {
|
|
229
230
|
this.applyValidity({});
|
|
@@ -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,11 +195,19 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
190
195
|
private _childObserver;
|
|
191
196
|
private _selectedClone;
|
|
192
197
|
private _customFlavorSheet;
|
|
198
|
+
private _ariaTrigger;
|
|
193
199
|
constructor();
|
|
194
200
|
/** TyComponent handles property capture automatically. */
|
|
195
201
|
protected onConnect(): void;
|
|
196
202
|
/** Custom (non-built-in) flavors — see utils/flavor-sheet.ts. */
|
|
197
203
|
private _syncCustomFlavor;
|
|
204
|
+
/**
|
|
205
|
+
* `error` turns a neutral select danger, like ty-input; clearing it puts
|
|
206
|
+
* back exactly the flavor attribute the author had, or none, kept meanwhile
|
|
207
|
+
* in ERROR_FLAVOR. Runs once connected: while the element upgrades, `error`
|
|
208
|
+
* may come before the `flavor` written next to it.
|
|
209
|
+
*/
|
|
210
|
+
private syncErrorFlavor;
|
|
198
211
|
protected onDisconnect(): void;
|
|
199
212
|
/** Handle state synchronization BEFORE render. */
|
|
200
213
|
protected onPropertiesChanged(changes: PropertyChange[]): void;
|
|
@@ -369,8 +382,9 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
369
382
|
* Reflect constraint validation to the owning <form> via ElementInternals.
|
|
370
383
|
* Without this `required` is only cosmetic — form.checkValidity() (and a
|
|
371
384
|
* 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
|
|
385
|
+
* ty-input's updateValidity(): empty + required => valueMissing, with a
|
|
386
|
+
* native <select>'s message; disabled fields are barred from constraint
|
|
387
|
+
* validation.
|
|
374
388
|
*/
|
|
375
389
|
private updateValidity;
|
|
376
390
|
/**
|
|
@@ -383,6 +397,16 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
383
397
|
private renderDesktop;
|
|
384
398
|
private renderMobile;
|
|
385
399
|
private setupMobileEventListeners;
|
|
400
|
+
/**
|
|
401
|
+
* The dialog was closed by someone else — a ty-modal around this select
|
|
402
|
+
* closes the dialogs still open inside it. Run our own close too, so the
|
|
403
|
+
* open state, aria-expanded and the scroll lock follow. Watches the `open`
|
|
404
|
+
* attribute rather than the `close` event: that one comes a task later,
|
|
405
|
+
* so right after modal.hide() the stub would still say expanded. Our own
|
|
406
|
+
* close() calls land here with the state already closed, and a dialog
|
|
407
|
+
* reopened since then is open again: both are no-ops.
|
|
408
|
+
*/
|
|
409
|
+
private syncOnOutsideClose;
|
|
386
410
|
private handleMobileStubClick;
|
|
387
411
|
/** Keyboard-open the (closed) mobile stub — see handleStubKeydown for why this is needed. */
|
|
388
412
|
private handleMobileStubKeydown;
|
|
@@ -444,7 +468,9 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
444
468
|
* Clear the entire selection programmatically WITH a change event
|
|
445
469
|
* (action: 'clear'). No-op if nothing is selected. Exists so slot="trigger"
|
|
446
470
|
* consumers (custom chrome, built-in clear UI hidden by design) can wire
|
|
447
|
-
* their own clear icon's click handler to it.
|
|
471
|
+
* their own clear icon's click handler to it. Shows no validation
|
|
472
|
+
* message (a code reset after a save shouldn't flag the field); the
|
|
473
|
+
* built-in × does.
|
|
448
474
|
*/
|
|
449
475
|
clear(): void;
|
|
450
476
|
get value(): string;
|
|
@@ -469,6 +495,8 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
469
495
|
set noClear(value: boolean);
|
|
470
496
|
get required(): boolean;
|
|
471
497
|
set required(value: boolean);
|
|
498
|
+
get error(): string;
|
|
499
|
+
set error(value: string);
|
|
472
500
|
get notSearchable(): boolean;
|
|
473
501
|
set notSearchable(value: boolean);
|
|
474
502
|
get externalSearch(): boolean;
|
|
@@ -483,6 +511,7 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
483
511
|
set placement(value: Placement | "");
|
|
484
512
|
get flavor(): Flavor;
|
|
485
513
|
set flavor(value: Flavor);
|
|
514
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
486
515
|
get form(): HTMLFormElement | null;
|
|
487
516
|
}
|
|
488
517
|
export {};
|