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
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* - Property change events via 'prop:change' (separate from user 'change' events)
|
|
10
10
|
*/
|
|
11
11
|
import { PropertyManager, PropertyConfig, PropertyChange } from '../utils/property-manager.js';
|
|
12
|
+
import { upgradeProperties } from '../utils/upgrade-properties.js';
|
|
13
|
+
export { upgradeProperties };
|
|
12
14
|
/**
|
|
13
15
|
* Base class for all Ty components with unified property lifecycle
|
|
14
16
|
*/
|
|
@@ -20,6 +22,9 @@ export declare abstract class TyComponent<T = any> extends HTMLElement {
|
|
|
20
22
|
private _validity;
|
|
21
23
|
protected _connected: boolean;
|
|
22
24
|
protected _rendered: boolean;
|
|
25
|
+
/** The user edited the field since it was created or its form reset:
|
|
26
|
+
* blur then shows its validation message (like native :user-invalid). */
|
|
27
|
+
protected _userEdited: boolean;
|
|
23
28
|
constructor();
|
|
24
29
|
/**
|
|
25
30
|
* Define which attributes to observe
|
|
@@ -56,10 +61,11 @@ export declare abstract class TyComponent<T = any> extends HTMLElement {
|
|
|
56
61
|
*/
|
|
57
62
|
disconnectedCallback(): void;
|
|
58
63
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
64
|
+
* upgradeProperties(), plus declared properties without an accessor of
|
|
65
|
+
* their own name (e.g. ty-input's `minlength`): those go straight to
|
|
66
|
+
* setProperty().
|
|
61
67
|
*/
|
|
62
|
-
private
|
|
68
|
+
private _upgradeProperties;
|
|
63
69
|
/**
|
|
64
70
|
* THE CORE LIFECYCLE METHOD
|
|
65
71
|
* All property changes flow through here
|
|
@@ -157,6 +163,9 @@ export declare abstract class TyComponent<T = any> extends HTMLElement {
|
|
|
157
163
|
* semantics). Non-form state (e.g. a checkbox's submission `value`, a
|
|
158
164
|
* ty-radio's option `value`) is deliberately left alone — the old
|
|
159
165
|
* blanket 'value' reset corrupted radio groups by wiping child values.
|
|
166
|
+
* Like a native control the field also looks untouched again: the
|
|
167
|
+
* validation message it showed is gone (an `error` the author set stays)
|
|
168
|
+
* and a blur shows nothing until the user edits it.
|
|
160
169
|
*/
|
|
161
170
|
formResetCallback(): void;
|
|
162
171
|
/**
|
package/lib/base/ty-component.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { PropertyManager } from '../utils/property-manager.js';
|
|
2
2
|
import { ValidityController } from '../utils/validity.js';
|
|
3
|
+
import { upgradeProperties } from '../utils/upgrade-properties.js';
|
|
4
|
+
export { upgradeProperties };
|
|
3
5
|
export class TyComponent extends HTMLElement {
|
|
4
6
|
constructor() {
|
|
5
7
|
super();
|
|
6
8
|
this._connected = false;
|
|
7
9
|
this._rendered = false;
|
|
10
|
+
this._userEdited = false;
|
|
8
11
|
this._handlingActivationClick = false;
|
|
9
12
|
this._resetDefaults = null;
|
|
10
13
|
const ctor = this.constructor;
|
|
@@ -81,7 +84,6 @@ this._handlePropertyChanges([change]);
|
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
connectedCallback() {
|
|
84
|
-
this._connected = true;
|
|
85
87
|
if (!this._resetDefaults) {
|
|
86
88
|
this._resetDefaults = new Map();
|
|
87
89
|
const ctor = this.constructor;
|
|
@@ -92,10 +94,8 @@ const attr = this.getAttribute(this._toKebabCase(name));
|
|
|
92
94
|
this._resetDefaults.set(name, attr ?? (typeof config.default === 'function' ? config.default() : config.default) ?? null);
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this._handlePropertyChanges(preSetProps);
|
|
98
|
-
}
|
|
97
|
+
this._upgradeProperties();
|
|
98
|
+
this._connected = true;
|
|
99
99
|
this.onConnect();
|
|
100
100
|
if (!this._rendered) {
|
|
101
101
|
this.render();
|
|
@@ -106,19 +106,20 @@ disconnectedCallback() {
|
|
|
106
106
|
this._connected = false;
|
|
107
107
|
this.onDisconnect();
|
|
108
108
|
}
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
109
|
+
_upgradeProperties() {
|
|
110
|
+
for (const name of Object.keys(this.constructor.properties)) {
|
|
111
|
+
if (!Object.prototype.hasOwnProperty.call(this, name))
|
|
112
|
+
continue;
|
|
113
|
+
const value = this[name];
|
|
114
|
+
delete this[name];
|
|
115
|
+
if (value === undefined)
|
|
116
|
+
continue;
|
|
117
|
+
if (name in this)
|
|
118
|
+
this[name] = value;
|
|
119
|
+
else
|
|
120
|
+
this.setProperty(name, value);
|
|
120
121
|
}
|
|
121
|
-
|
|
122
|
+
upgradeProperties(this);
|
|
122
123
|
}
|
|
123
124
|
_handlePropertyChanges(changes) {
|
|
124
125
|
if (changes.length === 0)
|
|
@@ -220,6 +221,9 @@ queueMicrotask(() => { this._handlingActivationClick = false; });
|
|
|
220
221
|
return false;
|
|
221
222
|
}
|
|
222
223
|
formResetCallback() {
|
|
224
|
+
this._userEdited = false;
|
|
225
|
+
if (this._validates)
|
|
226
|
+
this._validity.reset();
|
|
223
227
|
for (const [name, def] of this._resetDefaults ?? []) {
|
|
224
228
|
this.setProperty(name, def);
|
|
225
229
|
}
|
|
@@ -89,11 +89,15 @@ export declare class TyButton extends TyComponent<ButtonState> implements TyButt
|
|
|
89
89
|
};
|
|
90
90
|
};
|
|
91
91
|
private _structureInitialized;
|
|
92
|
+
private _formDisabled;
|
|
92
93
|
private _customFlavorSheet;
|
|
94
|
+
private _ownClick;
|
|
93
95
|
constructor();
|
|
94
96
|
protected onConnect(): void;
|
|
95
97
|
protected onDisconnect(): void;
|
|
96
98
|
protected onPropertiesChanged(changes: PropertyChange[]): void;
|
|
99
|
+
formDisabledCallback(disabled: boolean): void;
|
|
100
|
+
private get _isDisabled();
|
|
97
101
|
/**
|
|
98
102
|
* Custom flavors: adopt generated per-flavor rules pointed at the user's
|
|
99
103
|
* design tokens (--ty-solid-X / --ty-color-X / --ty-bg-X), falling back to
|
package/lib/components/button.js
CHANGED
|
@@ -8,8 +8,19 @@ export class TyButton extends TyComponent {
|
|
|
8
8
|
constructor() {
|
|
9
9
|
super();
|
|
10
10
|
this._structureInitialized = false;
|
|
11
|
+
this._formDisabled = false;
|
|
11
12
|
this._customFlavorSheet = null;
|
|
13
|
+
this._ownClick = null;
|
|
12
14
|
ensureStyles(this.shadowRoot, { css: buttonStyles, id: "ty-button" });
|
|
15
|
+
this.addEventListener("click", (e) => {
|
|
16
|
+
const button = this.shadowRoot.querySelector("button");
|
|
17
|
+
if (!button || e === this._ownClick)
|
|
18
|
+
return;
|
|
19
|
+
if (e.composedPath().includes(button))
|
|
20
|
+
return;
|
|
21
|
+
e.stopImmediatePropagation();
|
|
22
|
+
button.click();
|
|
23
|
+
}, true);
|
|
13
24
|
}
|
|
14
25
|
onConnect() {
|
|
15
26
|
this._syncCustomFlavor();
|
|
@@ -20,6 +31,14 @@ if (changes.some((c) => c.name === "flavor")) {
|
|
|
20
31
|
this._syncCustomFlavor();
|
|
21
32
|
}
|
|
22
33
|
}
|
|
34
|
+
formDisabledCallback(disabled) {
|
|
35
|
+
this._formDisabled = disabled;
|
|
36
|
+
if (this._structureInitialized)
|
|
37
|
+
this.render();
|
|
38
|
+
}
|
|
39
|
+
get _isDisabled() {
|
|
40
|
+
return this.disabled || this._formDisabled;
|
|
41
|
+
}
|
|
23
42
|
_syncCustomFlavor() {
|
|
24
43
|
const { base } = this.parseFlavor();
|
|
25
44
|
this._customFlavorSheet = syncCustomFlavorSheet(this.shadowRoot, this._customFlavorSheet, base, () => buttonCustomFlavorCss(base));
|
|
@@ -136,7 +155,7 @@ initializeButtonStructure() {
|
|
|
136
155
|
const shadow = this.shadowRoot;
|
|
137
156
|
const classes = this.buildClasses();
|
|
138
157
|
const button = document.createElement("button");
|
|
139
|
-
button.disabled = this.
|
|
158
|
+
button.disabled = this._isDisabled;
|
|
140
159
|
button.className = classes;
|
|
141
160
|
button.setAttribute("part", "button");
|
|
142
161
|
const loader = document.createElement("span");
|
|
@@ -155,15 +174,21 @@ button.appendChild(startSlot);
|
|
|
155
174
|
button.appendChild(defaultSlot);
|
|
156
175
|
button.appendChild(endSlot);
|
|
157
176
|
button.addEventListener("click", (e) => {
|
|
158
|
-
|
|
177
|
+
const path = e.composedPath();
|
|
178
|
+
if (path.slice(0, path.indexOf(button)).some((n) => n instanceof Element && n.localName === "ty-popup"))
|
|
159
179
|
return;
|
|
160
180
|
e.stopPropagation();
|
|
161
|
-
this.
|
|
162
|
-
|
|
181
|
+
if (this._isDisabled || this.loading)
|
|
182
|
+
return;
|
|
183
|
+
const click = new CustomEvent("click", {
|
|
163
184
|
bubbles: true,
|
|
164
185
|
composed: true,
|
|
186
|
+
cancelable: true,
|
|
165
187
|
detail: { originalEvent: e },
|
|
166
|
-
})
|
|
188
|
+
});
|
|
189
|
+
this._ownClick = click;
|
|
190
|
+
if (this.dispatchEvent(click))
|
|
191
|
+
this.handleFormAction();
|
|
167
192
|
});
|
|
168
193
|
this.applyLoadingState(button);
|
|
169
194
|
shadow.appendChild(button);
|
|
@@ -174,12 +199,14 @@ const isLoading = this.loading;
|
|
|
174
199
|
button.classList.toggle("loading", isLoading);
|
|
175
200
|
if (isLoading) {
|
|
176
201
|
button.setAttribute("aria-busy", "true");
|
|
202
|
+
button.setAttribute("aria-disabled", "true");
|
|
177
203
|
const loader = button.querySelector(".loader-icon");
|
|
178
204
|
if (loader)
|
|
179
205
|
loader.innerHTML = getLoaderSvg();
|
|
180
206
|
}
|
|
181
207
|
else {
|
|
182
208
|
button.removeAttribute("aria-busy");
|
|
209
|
+
button.removeAttribute("aria-disabled");
|
|
183
210
|
}
|
|
184
211
|
}
|
|
185
212
|
render() {
|
|
@@ -191,7 +218,7 @@ return;
|
|
|
191
218
|
const classes = this.buildClasses();
|
|
192
219
|
const button = shadow.querySelector("button");
|
|
193
220
|
if (button) {
|
|
194
|
-
button.disabled = this.
|
|
221
|
+
button.disabled = this._isDisabled;
|
|
195
222
|
button.className = classes;
|
|
196
223
|
this.applyLoadingState(button);
|
|
197
224
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ensureStyles } from '../utils/styles.js';
|
|
2
|
+
import { liftProperties, upgradeProperties } from '../utils/upgrade-properties.js';
|
|
2
3
|
import { syncCustomFlavorSheet } from '../utils/flavor-sheet.js';
|
|
3
4
|
import { calendarMonthStyles, calendarMonthCustomFlavorCss } from '../styles/calendar-month.js';
|
|
4
5
|
import { getCalendarMonthDays, getLocalizedWeekdays, parseISODate } from '../utils/calendar-utils.js';
|
|
@@ -53,6 +54,7 @@ const today = new Date();
|
|
|
53
54
|
this._displayYear = today.getFullYear();
|
|
54
55
|
this._displayMonth = today.getMonth() + 1;
|
|
55
56
|
this.attachShadow({ mode: 'open' });
|
|
57
|
+
liftProperties(this);
|
|
56
58
|
}
|
|
57
59
|
connectedCallback() {
|
|
58
60
|
if (!this._displayMonth) {
|
|
@@ -68,16 +70,7 @@ const sizeAttr = this.getAttribute('size');
|
|
|
68
70
|
if (sizeAttr && (sizeAttr === 'sm' || sizeAttr === 'md' || sizeAttr === 'lg')) {
|
|
69
71
|
this._size = sizeAttr;
|
|
70
72
|
}
|
|
71
|
-
|
|
72
|
-
if (plainDayContentFn && !this._dayContentFn) {
|
|
73
|
-
this._dayContentFn = plainDayContentFn;
|
|
74
|
-
delete this.dayContentFn;
|
|
75
|
-
}
|
|
76
|
-
const plainCustomCSS = this.customCSS;
|
|
77
|
-
if (plainCustomCSS && !this._customCSS) {
|
|
78
|
-
this._customCSS = plainCustomCSS;
|
|
79
|
-
delete this.customCSS;
|
|
80
|
-
}
|
|
73
|
+
upgradeProperties(this);
|
|
81
74
|
this._localeObserver = observeLocaleChanges(this, () => {
|
|
82
75
|
this.render();
|
|
83
76
|
});
|
|
@@ -50,6 +50,7 @@ export declare class TyCalendar extends HTMLElement {
|
|
|
50
50
|
private _internals?;
|
|
51
51
|
private _validity?;
|
|
52
52
|
private _initialValue;
|
|
53
|
+
private _valueSets;
|
|
53
54
|
private _localeObserver?;
|
|
54
55
|
static formAssociated: boolean;
|
|
55
56
|
static get observedAttributes(): string[];
|
|
@@ -94,7 +95,8 @@ export declare class TyCalendar extends HTMLElement {
|
|
|
94
95
|
private syncChildComponents;
|
|
95
96
|
private updateFormValue;
|
|
96
97
|
/**
|
|
97
|
-
* Selection outside [min, max] => rangeUnderflow/rangeOverflow
|
|
98
|
+
* Selection outside [min, max] => rangeUnderflow/rangeOverflow, with a
|
|
99
|
+
* native date input's message.
|
|
98
100
|
* UI can't produce this (days are disabled), but programmatic values can.
|
|
99
101
|
*/
|
|
100
102
|
private updateValidity;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { ensureStyles } from '../utils/styles.js';
|
|
2
|
+
import { liftProperties, upgradeProperties } from '../utils/upgrade-properties.js';
|
|
2
3
|
import { calendarStyles } from '../styles/calendar.js';
|
|
3
4
|
import './calendar-month.js';
|
|
4
5
|
import './calendar-navigation.js';
|
|
5
6
|
import { parseISODate, getMonthName } from '../utils/calendar-utils.js';
|
|
6
7
|
import { getEffectiveLocale, observeLocaleChanges } from '../utils/locale.js';
|
|
7
|
-
import { ValidityController } from '../utils/validity.js';
|
|
8
|
+
import { ValidityController, nativeMessage } from '../utils/validity.js';
|
|
8
9
|
function getCurrentDate() {
|
|
9
10
|
const now = new Date();
|
|
10
11
|
return {
|
|
@@ -49,12 +50,14 @@ this._flavor = 'primary';
|
|
|
49
50
|
this._view = 'days';
|
|
50
51
|
this._dayGridHeight = 0;
|
|
51
52
|
this._initialValue = '';
|
|
53
|
+
this._valueSets = 0;
|
|
52
54
|
const current = getCurrentDate();
|
|
53
55
|
this._state = {
|
|
54
56
|
displayYear: current.year,
|
|
55
57
|
displayMonth: current.month,
|
|
56
58
|
};
|
|
57
59
|
this.attachShadow({ mode: 'open' });
|
|
60
|
+
liftProperties(this);
|
|
58
61
|
if ('attachInternals' in this) {
|
|
59
62
|
this._internals = this.attachInternals();
|
|
60
63
|
this._validity = new ValidityController(this, this._internals);
|
|
@@ -62,36 +65,7 @@ this._validity = new ValidityController(this, this._internals);
|
|
|
62
65
|
}
|
|
63
66
|
connectedCallback() {
|
|
64
67
|
this.initializeFromAttributes();
|
|
65
|
-
|
|
66
|
-
if (plainDayContentFn && !this._dayContentFn) {
|
|
67
|
-
this._dayContentFn = plainDayContentFn;
|
|
68
|
-
delete this.dayContentFn;
|
|
69
|
-
}
|
|
70
|
-
const plainCustomCSS = this.customCSS;
|
|
71
|
-
if (plainCustomCSS && !this._customCSS) {
|
|
72
|
-
this._customCSS = plainCustomCSS;
|
|
73
|
-
delete this.customCSS;
|
|
74
|
-
}
|
|
75
|
-
const plainValue = this.value;
|
|
76
|
-
if (plainValue && typeof plainValue === 'string' && plainValue !== this.value) {
|
|
77
|
-
const match = plainValue.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
78
|
-
if (match) {
|
|
79
|
-
const year = parseInt(match[1], 10);
|
|
80
|
-
const month = parseInt(match[2], 10);
|
|
81
|
-
const day = parseInt(match[3], 10);
|
|
82
|
-
this._state.selectedYear = year;
|
|
83
|
-
this._state.selectedMonth = month;
|
|
84
|
-
this._state.selectedDay = day;
|
|
85
|
-
this._state.displayYear = year;
|
|
86
|
-
this._state.displayMonth = month;
|
|
87
|
-
if (!this._stateless) {
|
|
88
|
-
this.setAttribute('year', year.toString());
|
|
89
|
-
this.setAttribute('month', month.toString());
|
|
90
|
-
this.setAttribute('day', day.toString());
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
delete this.value;
|
|
94
|
-
}
|
|
68
|
+
upgradeProperties(this);
|
|
95
69
|
this.render();
|
|
96
70
|
this.updateFormValue();
|
|
97
71
|
this._localeObserver = observeLocaleChanges(this, () => {
|
|
@@ -337,18 +311,24 @@ return formatDateISO(selectedYear, selectedMonth, selectedDay);
|
|
|
337
311
|
return '';
|
|
338
312
|
}
|
|
339
313
|
set value(isoDate) {
|
|
314
|
+
const set = ++this._valueSets;
|
|
315
|
+
const superseded = () => set !== this._valueSets;
|
|
340
316
|
if (!isoDate) {
|
|
341
317
|
delete this._state.selectedYear;
|
|
342
318
|
delete this._state.selectedMonth;
|
|
343
319
|
delete this._state.selectedDay;
|
|
344
320
|
if (!this._stateless) {
|
|
345
321
|
this.removeAttribute('year');
|
|
322
|
+
if (superseded())
|
|
323
|
+
return;
|
|
346
324
|
this.removeAttribute('month');
|
|
325
|
+
if (superseded())
|
|
326
|
+
return;
|
|
347
327
|
this.removeAttribute('day');
|
|
348
328
|
}
|
|
349
329
|
}
|
|
350
330
|
else {
|
|
351
|
-
const match = isoDate.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
331
|
+
const match = typeof isoDate === 'string' && isoDate.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
352
332
|
if (match) {
|
|
353
333
|
const year = parseInt(match[1], 10);
|
|
354
334
|
const month = parseInt(match[2], 10);
|
|
@@ -360,7 +340,11 @@ this._state.displayYear = year;
|
|
|
360
340
|
this._state.displayMonth = month;
|
|
361
341
|
if (!this._stateless) {
|
|
362
342
|
this.setAttribute('year', year.toString());
|
|
343
|
+
if (superseded())
|
|
344
|
+
return;
|
|
363
345
|
this.setAttribute('month', month.toString());
|
|
346
|
+
if (superseded())
|
|
347
|
+
return;
|
|
364
348
|
this.setAttribute('day', day.toString());
|
|
365
349
|
}
|
|
366
350
|
}
|
|
@@ -503,12 +487,13 @@ if (selectedYear && selectedMonth && selectedDay) {
|
|
|
503
487
|
const sel = Date.UTC(selectedYear, selectedMonth - 1, selectedDay);
|
|
504
488
|
const min = this._min ? parseISODate(this._min) : null;
|
|
505
489
|
const max = this._max ? parseISODate(this._max) : null;
|
|
490
|
+
const message = () => nativeMessage('input', { type: 'date', min: this._min, max: this._max }, formatDateISO(selectedYear, selectedMonth, selectedDay));
|
|
506
491
|
if (min && sel < Date.UTC(min.year, min.month - 1, min.day)) {
|
|
507
|
-
this._validity.set({ rangeUnderflow: true },
|
|
492
|
+
this._validity.set({ rangeUnderflow: true }, message());
|
|
508
493
|
return;
|
|
509
494
|
}
|
|
510
495
|
if (max && sel > Date.UTC(max.year, max.month - 1, max.day)) {
|
|
511
|
-
this._validity.set({ rangeOverflow: true },
|
|
496
|
+
this._validity.set({ rangeOverflow: true }, message());
|
|
512
497
|
return;
|
|
513
498
|
}
|
|
514
499
|
}
|
|
@@ -112,7 +112,7 @@ export declare class TyCheckbox extends TyComponent<CheckboxState> implements Ty
|
|
|
112
112
|
protected render(): void;
|
|
113
113
|
/** Focus the checkbox itself (the host is not focusable; its role element is). */
|
|
114
114
|
focus(options?: FocusOptions): void;
|
|
115
|
-
/** required + unchecked => valueMissing; otherwise valid. */
|
|
115
|
+
/** required + unchecked => valueMissing (the browser's own message); otherwise valid. */
|
|
116
116
|
private updateValidity;
|
|
117
117
|
get checked(): boolean;
|
|
118
118
|
set checked(value: boolean);
|
|
@@ -3,6 +3,7 @@ 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
5
|
import { checkboxStyles, checkboxCustomFlavorCss } from "../styles/checkbox.js";
|
|
6
|
+
import { nativeMessage } from "../utils/validity.js";
|
|
6
7
|
const CHECK_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>`;
|
|
7
8
|
const DASH_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/></svg>`;
|
|
8
9
|
export class TyCheckbox extends TyComponent {
|
|
@@ -179,7 +180,7 @@ this.applyValidity({});
|
|
|
179
180
|
return;
|
|
180
181
|
}
|
|
181
182
|
if (this.required && !this.checked) {
|
|
182
|
-
this.applyValidity({ valueMissing: true }, "
|
|
183
|
+
this.applyValidity({ valueMissing: true }, nativeMessage("input", { type: "checkbox", required: true }), anchor ?? undefined);
|
|
183
184
|
}
|
|
184
185
|
else {
|
|
185
186
|
this.applyValidity({});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TyDatePicker Web Component — read-only input stub + calendar dropdown
|
|
3
3
|
* (modal <dialog>), optional time input, form participation via
|
|
4
|
-
* ElementInternals. Value is UTC
|
|
4
|
+
* ElementInternals. Value is a UTC instant (wall-clock with `native`);
|
|
5
|
+
* display is local.
|
|
5
6
|
*/
|
|
6
7
|
import { type Placement } from '../utils/positioning.js';
|
|
7
8
|
import { TyComponent } from '../base/ty-component.js';
|
|
@@ -16,6 +17,8 @@ interface DateComponents {
|
|
|
16
17
|
day?: number;
|
|
17
18
|
hour?: number;
|
|
18
19
|
minute?: number;
|
|
20
|
+
second?: number;
|
|
21
|
+
epoch?: number;
|
|
19
22
|
}
|
|
20
23
|
/**
|
|
21
24
|
* Internal date picker state
|
|
@@ -99,6 +102,11 @@ export declare class TyDatePicker extends TyComponent<DatePickerState> {
|
|
|
99
102
|
visual: boolean;
|
|
100
103
|
default: boolean;
|
|
101
104
|
};
|
|
105
|
+
error: {
|
|
106
|
+
type: "string";
|
|
107
|
+
visual: boolean;
|
|
108
|
+
default: string;
|
|
109
|
+
};
|
|
102
110
|
format: {
|
|
103
111
|
type: "string";
|
|
104
112
|
visual: boolean;
|
|
@@ -123,12 +131,19 @@ export declare class TyDatePicker extends TyComponent<DatePickerState> {
|
|
|
123
131
|
visual: boolean;
|
|
124
132
|
default: boolean;
|
|
125
133
|
};
|
|
134
|
+
native: {
|
|
135
|
+
type: "boolean";
|
|
136
|
+
visual: boolean;
|
|
137
|
+
default: boolean;
|
|
138
|
+
};
|
|
126
139
|
};
|
|
127
140
|
private _state;
|
|
128
141
|
private _clickListener?;
|
|
129
142
|
private _keydownListener?;
|
|
130
143
|
private _dialogClickListener?;
|
|
131
144
|
private _closeTimestamp;
|
|
145
|
+
private _initialized;
|
|
146
|
+
private _syncing;
|
|
132
147
|
private _timeInput?;
|
|
133
148
|
private _localeObserver?;
|
|
134
149
|
private _customFlavorSheet;
|
|
@@ -142,16 +157,21 @@ export declare class TyDatePicker extends TyComponent<DatePickerState> {
|
|
|
142
157
|
/** Handle state updates BEFORE render. */
|
|
143
158
|
protected onPropertiesChanged(changes: PropertyChange[]): void;
|
|
144
159
|
/**
|
|
145
|
-
* Get form value -
|
|
146
|
-
* TyComponent calls this automatically when
|
|
160
|
+
* Get form value - the submitted string from current state (see
|
|
161
|
+
* componentsToOutputValue). TyComponent calls this automatically when the
|
|
162
|
+
* value property changes.
|
|
147
163
|
*/
|
|
148
164
|
protected getFormValue(): FormDataEntryValue | null;
|
|
165
|
+
private outputValue;
|
|
149
166
|
protected updateFormValue(): void;
|
|
150
|
-
/**
|
|
167
|
+
/** The field's focusable element: the stub, or the native input on touch devices. */
|
|
168
|
+
private focusTarget;
|
|
169
|
+
/** Focus the field: the host itself isn't focusable. Disabled: nothing, like a native input. */
|
|
151
170
|
focus(options?: FocusOptions): void;
|
|
152
171
|
/**
|
|
153
172
|
* `required` used to be cosmetic: nothing reported valueMissing, so an empty
|
|
154
173
|
* required picker never blocked its form. Disabled pickers don't validate.
|
|
174
|
+
* The message is a native date (datetime-local with time) input's.
|
|
155
175
|
*/
|
|
156
176
|
private updateValidity;
|
|
157
177
|
private getComponents;
|
|
@@ -173,6 +193,14 @@ export declare class TyDatePicker extends TyComponent<DatePickerState> {
|
|
|
173
193
|
* Compares before setting to prevent circular triggers
|
|
174
194
|
*/
|
|
175
195
|
private syncFormValue;
|
|
196
|
+
/**
|
|
197
|
+
* `error` turns an unflavored field ('default'/'neutral') danger, like
|
|
198
|
+
* ty-input; clearing it puts back exactly the flavor attribute the author
|
|
199
|
+
* had, or none, kept meanwhile in ERROR_FLAVOR. Runs once connected: while
|
|
200
|
+
* the element upgrades, `error` may come before the `flavor` written next
|
|
201
|
+
* to it.
|
|
202
|
+
*/
|
|
203
|
+
private syncErrorFlavor;
|
|
176
204
|
/**
|
|
177
205
|
* Handle time changes from the footer's hour/minute fields
|
|
178
206
|
*/
|
|
@@ -186,6 +214,7 @@ export declare class TyDatePicker extends TyComponent<DatePickerState> {
|
|
|
186
214
|
set placement(v: Placement | '');
|
|
187
215
|
get flavor(): DatePickerFlavor;
|
|
188
216
|
set flavor(v: DatePickerFlavor);
|
|
217
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
189
218
|
get label(): string;
|
|
190
219
|
set label(v: string);
|
|
191
220
|
get placeholder(): string;
|
|
@@ -208,17 +237,23 @@ export declare class TyDatePicker extends TyComponent<DatePickerState> {
|
|
|
208
237
|
set max(v: string);
|
|
209
238
|
get withTime(): boolean;
|
|
210
239
|
set withTime(v: boolean);
|
|
240
|
+
get native(): boolean;
|
|
241
|
+
set native(v: boolean);
|
|
242
|
+
get error(): string;
|
|
243
|
+
set error(v: string);
|
|
211
244
|
/**
|
|
212
|
-
* Get current value
|
|
245
|
+
* Get current value — the string the form submits: a UTC ISO instant, or
|
|
246
|
+
* wall-clock ('YYYY-MM-DD' / 'YYYY-MM-DDTHH:mm') with `native`. null when
|
|
247
|
+
* empty (the form then submits name=, like a native date input).
|
|
213
248
|
*/
|
|
214
249
|
get value(): string | null;
|
|
215
250
|
/**
|
|
216
|
-
* Set value (
|
|
251
|
+
* Set value: any format parseValue() reads, a Date, epoch milliseconds,
|
|
252
|
+
* or null. It's normalized to the submitted form.
|
|
217
253
|
*
|
|
218
254
|
* When set to null/undefined/empty string, the attribute is removed.
|
|
219
|
-
* When set to a valid date, the attribute is set to ISO UTC string.
|
|
220
255
|
*/
|
|
221
|
-
set value(val: string | Date | null);
|
|
256
|
+
set value(val: string | number | Date | null);
|
|
222
257
|
/**
|
|
223
258
|
* Clear the date value programmatically WITH a change event (source:
|
|
224
259
|
* 'clear'). Works regardless of `no-clear` — lets any
|
|
@@ -232,15 +267,32 @@ export declare class TyDatePicker extends TyComponent<DatePickerState> {
|
|
|
232
267
|
/** Footer: clock + "Time" on the left, HH : MM on the right, summary below. */
|
|
233
268
|
private renderTimeSection;
|
|
234
269
|
private renderCalendarDropdown;
|
|
270
|
+
/**
|
|
271
|
+
* Match the popup calendar to the stub's flavor — the one the user set, not
|
|
272
|
+
* the danger an error swapped in. 'default' is the stub's own unstyled
|
|
273
|
+
* state, not a real flavor: the calendar stays on its own default.
|
|
274
|
+
*/
|
|
275
|
+
private calendarFlavor;
|
|
235
276
|
/**
|
|
236
277
|
* Render native date input for mobile touch devices.
|
|
237
278
|
* Uses <input type="date"> or <input type="datetime-local"> when with-time.
|
|
238
279
|
* Reuses .date-picker-stub styling and existing event/state infrastructure.
|
|
239
280
|
*/
|
|
240
281
|
private renderNativeInput;
|
|
282
|
+
/**
|
|
283
|
+
* The touch devices' native input follows the picker: type (date, or
|
|
284
|
+
* datetime-local with time), disabled, required, min/max (datetime-local
|
|
285
|
+
* wants a time suffix) and value — also when they change at runtime.
|
|
286
|
+
*/
|
|
287
|
+
private syncNativeInput;
|
|
241
288
|
private calculateCalendarPosition;
|
|
242
289
|
private renderContainer;
|
|
243
290
|
protected render(): void;
|
|
291
|
+
/**
|
|
292
|
+
* The `error` line under the field — same markup as ty-input's; the field
|
|
293
|
+
* (stub, or the native input on touch devices) is described by it.
|
|
294
|
+
*/
|
|
295
|
+
private syncErrorLine;
|
|
244
296
|
/**
|
|
245
297
|
* Update display without destroying DOM (for open dialog)
|
|
246
298
|
*/
|