@antadesign/anta 0.3.16 → 0.3.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -10
- package/dist/anta_helpers.d.ts +7 -12
- package/dist/anta_helpers.js +11 -3
- package/dist/components/ButtonCopy.d.ts +9 -6
- package/dist/components/ButtonCopy.js +5 -3
- package/dist/components/InputTime.d.ts +5 -4
- package/dist/components/Loader.d.ts +39 -0
- package/dist/components/Loader.js +33 -0
- package/dist/components/Progress.d.ts +17 -8
- package/dist/components/Progress.js +12 -9
- package/dist/components/Slider.d.ts +89 -0
- package/dist/components/Slider.js +144 -0
- package/dist/components/copy-props.d.ts +2 -0
- package/dist/components/copy-props.js +2 -1
- package/dist/elements/a-button.css +1 -1
- package/dist/elements/a-copy.d.ts +1 -1
- package/dist/elements/a-copy.js +103 -109
- package/dist/elements/a-icon.d.ts +1 -0
- package/dist/elements/a-icon.js +1 -0
- package/dist/elements/a-input-time.css +1 -1
- package/dist/elements/a-input-time.js +297 -377
- package/dist/elements/a-input.css +1 -1
- package/dist/elements/a-input.d.ts +0 -3
- package/dist/elements/a-input.js +13 -45
- package/dist/elements/a-loader.css +1 -0
- package/dist/elements/a-loader.d.ts +12 -0
- package/dist/elements/a-loader.js +15 -0
- package/dist/elements/a-progress.css +1 -1
- package/dist/elements/a-progress.js +32 -2
- package/dist/elements/a-slider.css +1 -0
- package/dist/elements/a-slider.d.ts +24 -0
- package/dist/elements/a-slider.js +479 -0
- package/dist/elements/copy-behavior.d.ts +3 -3
- package/dist/elements/index.d.ts +2 -0
- package/dist/elements/index.js +6 -0
- package/dist/general_types.d.ts +82 -6
- package/dist/index.d.ts +8 -1
- package/dist/index.js +4 -0
- package/dist/jsx-runtime.d.ts +3 -1
- package/dist/reset.css +1 -1
- package/dist/theme-anta.css +1 -1
- package/package.json +1 -1
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { HTMLElementBase } from "../anta_helpers";
|
|
2
2
|
import "./a-input-time.css";
|
|
3
|
-
const FORWARDED = ["name", "aria-label", "required"];
|
|
4
3
|
const CLEAR_TRIGGER = "clearrequest";
|
|
5
4
|
const CLEAR_INPUT_EVENT = "clearinput";
|
|
6
5
|
const pad2 = (n) => String(n).padStart(2, "0");
|
|
6
|
+
const inputColumns = (value) => Array.from(value).reduce((columns, char) => columns + (/[ᄀ-ᅟ⺀-가-힣豈-︐-!-⦆¢-₩]/u.test(char) ? 2 : 1), 0);
|
|
7
7
|
const to24 = (h12, period) => period === "pm" ? h12 % 12 + 12 : h12 % 12;
|
|
8
|
+
const parseTime = (value) => {
|
|
9
|
+
const match = /^(\d{1,2}):(\d{2})$/.exec((value ?? "").trim());
|
|
10
|
+
return match ? { h: Math.min(23, Number(match[1])), min: Math.min(59, Number(match[2])) } : null;
|
|
11
|
+
};
|
|
8
12
|
const SHADOW_STYLE = `
|
|
9
13
|
:host {
|
|
10
|
-
--_fs: 15px;
|
|
11
|
-
--_lh: 20px;
|
|
12
|
-
|
|
13
14
|
display: grid;
|
|
14
15
|
grid-template-columns: minmax(0, 1fr);
|
|
15
16
|
row-gap: 4px;
|
|
@@ -20,11 +21,10 @@ const SHADOW_STYLE = `
|
|
|
20
21
|
display: none;
|
|
21
22
|
color: var(--input-time-label);
|
|
22
23
|
font-family: var(--sans-serif);
|
|
23
|
-
font-size: var(--
|
|
24
|
-
line-height: var(--
|
|
24
|
+
font-size: var(--input-time-font-size);
|
|
25
|
+
line-height: var(--input-time-line-height);
|
|
25
26
|
font-weight: 500;
|
|
26
27
|
}
|
|
27
|
-
.label.has-label { display: block; }
|
|
28
28
|
|
|
29
29
|
.field {
|
|
30
30
|
--_bc: var(--input-time-border);
|
|
@@ -33,24 +33,23 @@ const SHADOW_STYLE = `
|
|
|
33
33
|
display: flex;
|
|
34
34
|
align-items: center;
|
|
35
35
|
box-sizing: border-box;
|
|
36
|
-
min-height:
|
|
36
|
+
min-height: var(--input-time-field-height);
|
|
37
37
|
background: var(--input-time-bg);
|
|
38
|
-
border-radius:
|
|
38
|
+
border-radius: var(--input-time-field-radius);
|
|
39
39
|
box-shadow: inset 0 0 0 var(--_bw) var(--_bc);
|
|
40
40
|
transition: box-shadow 120ms ease;
|
|
41
41
|
}
|
|
42
42
|
:host([status]) .field { --_bw: 1px; }
|
|
43
|
-
:host([size="small"]) { --_fs: 13px; --_lh: 16px; }
|
|
44
|
-
:host([size="large"]) { --_fs: 17px; --_lh: 22px; }
|
|
45
|
-
:host([size="small"]) .field { min-height: 24px; }
|
|
46
|
-
:host([size="large"]) .field { min-height: 32px; }
|
|
47
|
-
:host([round]) .field { border-radius: var(--input-time-round, 999px); }
|
|
48
43
|
|
|
49
44
|
@media (hover: hover) and (pointer: fine) {
|
|
50
|
-
:host(:not(:disabled)) .field:hover {
|
|
45
|
+
:host(:not(:disabled)) .field:hover {
|
|
46
|
+
--_bc: var(--input-time-border-hover);
|
|
47
|
+
--_bw: 1px;
|
|
48
|
+
}
|
|
51
49
|
}
|
|
52
|
-
.field:
|
|
50
|
+
.field:focus-within {
|
|
53
51
|
--_bc: var(--input-time-border-hover);
|
|
52
|
+
--_bw: 1px;
|
|
54
53
|
outline: 1px solid var(--focus-ring);
|
|
55
54
|
outline-offset: 1px;
|
|
56
55
|
}
|
|
@@ -59,16 +58,9 @@ const SHADOW_STYLE = `
|
|
|
59
58
|
slot[name="leading"] {
|
|
60
59
|
display: none;
|
|
61
60
|
color: var(--input-time-adornment);
|
|
62
|
-
font-size: var(--
|
|
63
|
-
line-height: var(--
|
|
61
|
+
font-size: var(--input-time-font-size);
|
|
62
|
+
line-height: var(--input-time-line-height);
|
|
64
63
|
}
|
|
65
|
-
.field.has-leading slot[name="leading"] {
|
|
66
|
-
display: flex;
|
|
67
|
-
align-items: center;
|
|
68
|
-
flex-shrink: 0;
|
|
69
|
-
margin-inline-start: 7px;
|
|
70
|
-
}
|
|
71
|
-
.field.has-leading .segments { padding-inline-start: 5px; }
|
|
72
64
|
:host([dim-actions]) slot[name="leading"] { opacity: 0.6; transition: opacity 120ms ease; }
|
|
73
65
|
:host([dim-actions]:not(:disabled)) .field:hover slot[name="leading"],
|
|
74
66
|
:host([dim-actions]:not(:disabled)) .field:focus-within slot[name="leading"] { opacity: 1; }
|
|
@@ -84,53 +76,50 @@ const SHADOW_STYLE = `
|
|
|
84
76
|
font-family: var(--sans-serif);
|
|
85
77
|
font-feature-settings: 'ss02', 'ss05', 'tnum';
|
|
86
78
|
font-variation-settings: 'wdth' 100, 'slnt' 0, 'ital' 0;
|
|
87
|
-
font-size: var(--
|
|
88
|
-
line-height: var(--
|
|
79
|
+
font-size: var(--input-time-font-size);
|
|
80
|
+
line-height: var(--input-time-line-height);
|
|
89
81
|
font-weight: 400;
|
|
90
82
|
}
|
|
91
83
|
|
|
92
84
|
.seg {
|
|
85
|
+
appearance: none;
|
|
93
86
|
display: inline-block;
|
|
94
87
|
box-sizing: border-box;
|
|
88
|
+
min-width: 0;
|
|
95
89
|
padding-inline: 1px;
|
|
90
|
+
border: 0;
|
|
96
91
|
border-radius: 2px;
|
|
92
|
+
background: transparent;
|
|
93
|
+
color: inherit;
|
|
94
|
+
font: inherit;
|
|
95
|
+
font-feature-settings: 'ss02', 'ss05', 'tnum';
|
|
96
|
+
font-variation-settings: 'wdth' 100, 'slnt' 0, 'ital' 0;
|
|
97
|
+
line-height: inherit;
|
|
97
98
|
text-align: center;
|
|
98
99
|
white-space: nowrap;
|
|
99
100
|
outline: none;
|
|
100
|
-
cursor:
|
|
101
|
+
cursor: text;
|
|
101
102
|
font-variant-numeric: tabular-nums;
|
|
102
|
-
caret-color: transparent;
|
|
103
103
|
}
|
|
104
|
-
.seg
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
.seg--period {
|
|
105
|
+
field-sizing: content;
|
|
106
|
+
width: auto;
|
|
107
|
+
color: var(--input-time-period-color);
|
|
108
108
|
}
|
|
109
|
-
.seg
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
.seg::placeholder {
|
|
110
|
+
color: var(--input-time-placeholder);
|
|
111
|
+
opacity: 1;
|
|
112
|
+
}
|
|
113
|
+
.lit {
|
|
114
|
+
margin-inline: -1px;
|
|
115
|
+
white-space: pre;
|
|
116
|
+
color: var(--input-time-text);
|
|
112
117
|
}
|
|
113
|
-
.seg[data-placeholder] { color: var(--input-time-placeholder); }
|
|
114
|
-
.lit { white-space: pre; color: var(--input-time-text); }
|
|
115
|
-
|
|
116
|
-
/* No text-selection highlight inside the field: the segments are spinbuttons,
|
|
117
|
-
and a selection painted over the focused segment's tint compounds into an
|
|
118
|
-
unreadable block. The focus tint alone marks the active segment. Scoped to
|
|
119
|
-
the shadow, so page selection is unaffected. */
|
|
120
|
-
::selection { background: transparent; }
|
|
121
118
|
|
|
122
119
|
:host(:disabled) .segments { cursor: not-allowed; }
|
|
123
120
|
:host(:disabled) .seg { cursor: not-allowed; }
|
|
124
121
|
|
|
125
122
|
slot[name="trailing"] { display: none; }
|
|
126
|
-
.field.has-trailing slot[name="trailing"] {
|
|
127
|
-
display: flex;
|
|
128
|
-
align-items: center;
|
|
129
|
-
gap: 2px;
|
|
130
|
-
flex-shrink: 0;
|
|
131
|
-
color: var(--input-time-adornment);
|
|
132
|
-
font-size: var(--_fs);
|
|
133
|
-
}
|
|
134
123
|
:host([dim-actions]) slot[name="trailing"],
|
|
135
124
|
:host([dim-actions]) slot[name="clear"] { opacity: 0.6; transition: opacity 120ms ease; }
|
|
136
125
|
:host([dim-actions]:not(:disabled)) .field:hover slot[name="trailing"],
|
|
@@ -144,7 +133,7 @@ const SHADOW_STYLE = `
|
|
|
144
133
|
flex-shrink: 0;
|
|
145
134
|
visibility: hidden;
|
|
146
135
|
}
|
|
147
|
-
|
|
136
|
+
:host(:state(filled)) slot[name="clear"] { visibility: visible; }
|
|
148
137
|
:host(:disabled) slot[name="clear"] { display: none; }
|
|
149
138
|
|
|
150
139
|
.hint {
|
|
@@ -154,33 +143,59 @@ const SHADOW_STYLE = `
|
|
|
154
143
|
padding-inline-start: 1px;
|
|
155
144
|
color: var(--input-time-hint);
|
|
156
145
|
font-family: var(--sans-serif);
|
|
157
|
-
font-size: calc(var(--
|
|
158
|
-
line-height: calc(var(--
|
|
146
|
+
font-size: calc(var(--input-time-font-size) - 1px);
|
|
147
|
+
line-height: calc(var(--input-time-line-height) - 2px);
|
|
159
148
|
}
|
|
160
|
-
.hint.has-hint { display: flex; }
|
|
161
149
|
`;
|
|
150
|
+
const INPUT_TIME_TEMPLATE = typeof document === "undefined" ? void 0 : (() => {
|
|
151
|
+
const template = document.createElement("template");
|
|
152
|
+
const style = document.createElement("style");
|
|
153
|
+
style.textContent = SHADOW_STYLE;
|
|
154
|
+
const label = document.createElement("slot");
|
|
155
|
+
label.className = "label";
|
|
156
|
+
label.part.add("label");
|
|
157
|
+
label.name = "label";
|
|
158
|
+
const field = document.createElement("div");
|
|
159
|
+
field.className = "field";
|
|
160
|
+
field.part.add("field");
|
|
161
|
+
const leadingSlot = document.createElement("slot");
|
|
162
|
+
leadingSlot.name = "leading";
|
|
163
|
+
leadingSlot.part.add("leading");
|
|
164
|
+
const segments = document.createElement("div");
|
|
165
|
+
segments.className = "segments";
|
|
166
|
+
segments.part.add("segments");
|
|
167
|
+
segments.setAttribute("role", "group");
|
|
168
|
+
const clearSlot = document.createElement("slot");
|
|
169
|
+
clearSlot.name = "clear";
|
|
170
|
+
clearSlot.part.add("clear");
|
|
171
|
+
const trailingSlot = document.createElement("slot");
|
|
172
|
+
trailingSlot.name = "trailing";
|
|
173
|
+
trailingSlot.part.add("trailing");
|
|
174
|
+
field.append(leadingSlot, segments, clearSlot, trailingSlot);
|
|
175
|
+
const hint = document.createElement("slot");
|
|
176
|
+
hint.className = "hint";
|
|
177
|
+
hint.part.add("hint");
|
|
178
|
+
hint.name = "hint";
|
|
179
|
+
template.content.append(style, label, field, document.createElement("slot"), hint);
|
|
180
|
+
return template;
|
|
181
|
+
})();
|
|
162
182
|
class AInputTimeElement extends HTMLElementBase {
|
|
163
183
|
static formAssociated = true;
|
|
164
184
|
static observedAttributes = [
|
|
165
|
-
...FORWARDED,
|
|
166
185
|
"value",
|
|
167
186
|
"defaultvalue",
|
|
168
187
|
"locale",
|
|
169
188
|
"hour12",
|
|
170
189
|
"status",
|
|
171
190
|
"disabled",
|
|
172
|
-
"size",
|
|
173
191
|
"min",
|
|
174
|
-
"max"
|
|
192
|
+
"max",
|
|
193
|
+
"aria-label",
|
|
194
|
+
"required"
|
|
175
195
|
];
|
|
176
196
|
#internals;
|
|
177
197
|
#field;
|
|
178
|
-
#labelBox;
|
|
179
198
|
#labelSlot;
|
|
180
|
-
#hintBox;
|
|
181
|
-
#hintSlot;
|
|
182
|
-
#leadingSlot;
|
|
183
|
-
#trailingSlot;
|
|
184
199
|
#segRow;
|
|
185
200
|
#ready = false;
|
|
186
201
|
#formDisabled = false;
|
|
@@ -197,9 +212,10 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
197
212
|
#minute = null;
|
|
198
213
|
#period = null;
|
|
199
214
|
#segs = [];
|
|
200
|
-
|
|
201
|
-
//
|
|
202
|
-
|
|
215
|
+
#literals = [];
|
|
216
|
+
// Pointer-focused inputs keep the browser's caret placement. Keyboard focus
|
|
217
|
+
// selects the segment so a new value can be typed immediately.
|
|
218
|
+
#pointerSegment;
|
|
203
219
|
constructor() {
|
|
204
220
|
super();
|
|
205
221
|
try {
|
|
@@ -208,51 +224,20 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
208
224
|
console.warn("a-input-time: ElementInternals unavailable \u2014 form association disabled.", err);
|
|
209
225
|
}
|
|
210
226
|
const shadow = this.attachShadow({ mode: "open", delegatesFocus: true });
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
this.#
|
|
214
|
-
this.#labelBox.className = "label";
|
|
215
|
-
this.#labelBox.setAttribute("part", "label");
|
|
216
|
-
this.#labelSlot = document.createElement("slot");
|
|
217
|
-
this.#labelSlot.name = "label";
|
|
218
|
-
this.#labelBox.append(this.#labelSlot);
|
|
227
|
+
if (!INPUT_TIME_TEMPLATE) throw new Error("a-input-time requires a DOM document");
|
|
228
|
+
shadow.append(INPUT_TIME_TEMPLATE.content.cloneNode(true));
|
|
229
|
+
this.#labelSlot = shadow.querySelector(".label");
|
|
219
230
|
this.#labelSlot.addEventListener("click", () => this.#segs[0]?.el.focus());
|
|
220
231
|
this.#labelSlot.addEventListener("slotchange", this.#onLabelSlotChange);
|
|
221
|
-
this.#field =
|
|
222
|
-
this.#
|
|
223
|
-
this.#field.
|
|
224
|
-
this.#segRow
|
|
225
|
-
this.#segRow.
|
|
226
|
-
this.#segRow.
|
|
227
|
-
this.#segRow.
|
|
228
|
-
this.#
|
|
229
|
-
if (e.target === this.#field || e.target === this.#segRow) {
|
|
230
|
-
e.preventDefault();
|
|
231
|
-
this.#segs[0]?.el.focus();
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
this.#leadingSlot = document.createElement("slot");
|
|
235
|
-
this.#leadingSlot.name = "leading";
|
|
236
|
-
this.#leadingSlot.setAttribute("part", "leading");
|
|
237
|
-
this.#leadingSlot.addEventListener("slotchange", () => this.#field.classList.toggle("has-leading", this.#leadingSlot.assignedNodes().length > 0));
|
|
238
|
-
const clearSlot = document.createElement("slot");
|
|
239
|
-
clearSlot.name = "clear";
|
|
240
|
-
clearSlot.setAttribute("part", "clear");
|
|
241
|
-
this.#trailingSlot = document.createElement("slot");
|
|
242
|
-
this.#trailingSlot.name = "trailing";
|
|
243
|
-
this.#trailingSlot.setAttribute("part", "trailing");
|
|
244
|
-
this.#trailingSlot.addEventListener("slotchange", () => this.#field.classList.toggle("has-trailing", this.#trailingSlot.assignedNodes().length > 0));
|
|
245
|
-
this.#field.append(this.#leadingSlot, this.#segRow, clearSlot, this.#trailingSlot);
|
|
232
|
+
this.#field = shadow.querySelector(".field");
|
|
233
|
+
this.#segRow = shadow.querySelector(".segments");
|
|
234
|
+
this.#field.addEventListener("mousedown", this.#onFieldMouseDown);
|
|
235
|
+
this.#segRow.addEventListener("input", this.#onInput);
|
|
236
|
+
this.#segRow.addEventListener("keydown", this.#onKeyDown);
|
|
237
|
+
this.#segRow.addEventListener("focusin", this.#onFocusIn);
|
|
238
|
+
this.#segRow.addEventListener("focusout", this.#onFocusOut);
|
|
239
|
+
this.#segRow.addEventListener("paste", this.#onPaste);
|
|
246
240
|
this.addEventListener(CLEAR_TRIGGER, () => this.clear());
|
|
247
|
-
this.#hintBox = document.createElement("div");
|
|
248
|
-
this.#hintBox.className = "hint";
|
|
249
|
-
this.#hintBox.setAttribute("part", "hint");
|
|
250
|
-
this.#hintSlot = document.createElement("slot");
|
|
251
|
-
this.#hintSlot.name = "hint";
|
|
252
|
-
this.#hintSlot.addEventListener("slotchange", this.#onHintSlotChange);
|
|
253
|
-
this.#hintBox.append(this.#hintSlot);
|
|
254
|
-
const extrasSlot = document.createElement("slot");
|
|
255
|
-
shadow.append(style, this.#labelBox, this.#field, extrasSlot, this.#hintBox);
|
|
256
241
|
}
|
|
257
242
|
getAnchorRect() {
|
|
258
243
|
return this.#field.getBoundingClientRect();
|
|
@@ -292,13 +277,7 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
292
277
|
this.#syncDisabled();
|
|
293
278
|
return;
|
|
294
279
|
}
|
|
295
|
-
if (name === "size") return;
|
|
296
280
|
if (name === "min" || name === "max") {
|
|
297
|
-
this.#clampIfComplete();
|
|
298
|
-
this.#iso = this.value;
|
|
299
|
-
this.#render();
|
|
300
|
-
this.#internals?.setFormValue(this.value);
|
|
301
|
-
this.#updateFilled();
|
|
302
281
|
this.#updateValidity();
|
|
303
282
|
return;
|
|
304
283
|
}
|
|
@@ -350,161 +329,211 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
350
329
|
{ type: "minute", value: "05" }
|
|
351
330
|
];
|
|
352
331
|
}
|
|
332
|
+
this.#amText = amText;
|
|
333
|
+
this.#pmText = pmText;
|
|
353
334
|
this.#segRow.replaceChildren();
|
|
354
335
|
this.#segs = [];
|
|
336
|
+
this.#literals = [];
|
|
355
337
|
for (const part of parts) {
|
|
356
338
|
if (part.type === "hour" || part.type === "minute" || part.type === "dayPeriod") {
|
|
357
|
-
const el = document.createElement("
|
|
358
|
-
el.className = "seg";
|
|
359
|
-
el.tabIndex = 0;
|
|
360
|
-
el.setAttribute("role", "spinbutton");
|
|
361
|
-
el.contentEditable = "true";
|
|
362
|
-
el.spellcheck = false;
|
|
363
|
-
el.setAttribute("autocorrect", "off");
|
|
364
|
-
el.setAttribute("autocapitalize", "off");
|
|
339
|
+
const el = document.createElement("input");
|
|
365
340
|
const kind = part.type === "dayPeriod" ? "period" : part.type;
|
|
341
|
+
el.className = `seg seg--${kind}`;
|
|
342
|
+
el.type = "text";
|
|
343
|
+
el.spellcheck = false;
|
|
344
|
+
el.part.add("segment", kind);
|
|
345
|
+
el.autocomplete = "off";
|
|
346
|
+
el.autocorrect = false;
|
|
347
|
+
el.autocapitalize = "off";
|
|
366
348
|
el.setAttribute("aria-label", kind === "hour" ? "Hour" : kind === "minute" ? "Minute" : "AM/PM");
|
|
367
|
-
el.
|
|
349
|
+
el.inputMode = kind === "period" ? "text" : "numeric";
|
|
350
|
+
if (kind === "period") {
|
|
351
|
+
const length = Math.max(2, inputColumns(this.#amText), inputColumns(this.#pmText));
|
|
352
|
+
el.maxLength = length;
|
|
353
|
+
el.size = length;
|
|
354
|
+
} else {
|
|
355
|
+
el.maxLength = 2;
|
|
356
|
+
el.size = 2;
|
|
357
|
+
}
|
|
368
358
|
const seg = kind === "hour" ? { kind, el, min: hour12 ? 1 : 0, max: hour12 ? 12 : 23 } : kind === "minute" ? { kind, el, min: 0, max: 59 } : { kind, el, min: 0, max: 1 };
|
|
369
|
-
el.addEventListener("keydown", (e) => this.#onSegKeyDown(e, seg));
|
|
370
|
-
el.addEventListener("beforeinput", (e) => this.#onSegBeforeInput(e, seg));
|
|
371
|
-
el.addEventListener("focus", () => {
|
|
372
|
-
this.#buf = "";
|
|
373
|
-
});
|
|
374
|
-
el.addEventListener("blur", () => this.#onSegBlur());
|
|
375
359
|
this.#segRow.append(el);
|
|
376
360
|
this.#segs.push(seg);
|
|
377
361
|
} else {
|
|
362
|
+
if (!part.value.trim()) continue;
|
|
378
363
|
const lit = document.createElement("span");
|
|
379
364
|
lit.className = "lit";
|
|
365
|
+
lit.part.add("literal");
|
|
380
366
|
lit.setAttribute("aria-hidden", "true");
|
|
381
367
|
lit.textContent = part.value;
|
|
382
368
|
this.#segRow.append(lit);
|
|
369
|
+
this.#literals.push(part.value);
|
|
383
370
|
}
|
|
384
371
|
}
|
|
385
|
-
this.#amText = amText;
|
|
386
|
-
this.#pmText = pmText;
|
|
387
372
|
this.#applyValue(keep);
|
|
388
373
|
this.#applyGroupLabel();
|
|
389
374
|
this.#syncStatus();
|
|
390
375
|
this.#syncDisabled();
|
|
391
|
-
this.#
|
|
392
|
-
this.#internals?.setFormValue(this.value);
|
|
393
|
-
this.#updateFilled();
|
|
394
|
-
this.#updateValidity();
|
|
376
|
+
this.#commitEdit({ dispatch: false });
|
|
395
377
|
}
|
|
396
378
|
#amText = "AM";
|
|
397
379
|
#pmText = "PM";
|
|
398
|
-
// ---
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
380
|
+
// --- Native segment editing + navigation ---
|
|
381
|
+
#segFromTarget(target) {
|
|
382
|
+
if (!(target instanceof HTMLInputElement)) return void 0;
|
|
383
|
+
return this.#segs.find((seg) => seg.el === target);
|
|
384
|
+
}
|
|
385
|
+
// Clicking the field's padding (rather than a native input) moves focus into
|
|
386
|
+
// the editor. A real pointer interaction leaves caret placement to the browser.
|
|
387
|
+
#onFieldMouseDown = (e) => {
|
|
388
|
+
const seg = this.#segFromTarget(e.target);
|
|
389
|
+
if (seg) {
|
|
390
|
+
this.#pointerSegment = seg.el;
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (e.target === this.#field || e.target === this.#segRow) {
|
|
407
394
|
e.preventDefault();
|
|
408
|
-
this.#
|
|
409
|
-
}
|
|
395
|
+
this.#focusSegment(this.#segs[0]);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
#onFocusIn = (e) => {
|
|
399
|
+
const seg = this.#segFromTarget(e.target);
|
|
400
|
+
if (!seg) return;
|
|
401
|
+
const pointerFocused = this.#pointerSegment === seg.el;
|
|
402
|
+
this.#pointerSegment = void 0;
|
|
403
|
+
if (!pointerFocused) {
|
|
404
|
+
queueMicrotask(() => {
|
|
405
|
+
if (this.shadowRoot?.activeElement === seg.el) seg.el.select();
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
#onFocusOut = (_e) => {
|
|
410
|
+
queueMicrotask(() => {
|
|
411
|
+
const active = this.shadowRoot?.activeElement;
|
|
412
|
+
if (this.#segs.some((seg) => seg.el === active)) return;
|
|
413
|
+
if (this.#clampIfComplete()) this.#commitEdit();
|
|
414
|
+
this.#dispatch("change");
|
|
415
|
+
});
|
|
416
|
+
};
|
|
417
|
+
#onKeyDown = (e) => {
|
|
418
|
+
const seg = this.#segFromTarget(e.target);
|
|
419
|
+
if (!seg) return;
|
|
420
|
+
const { el } = seg;
|
|
421
|
+
const key = e.key;
|
|
422
|
+
if (key === "ArrowUp" || key === "ArrowDown") {
|
|
410
423
|
e.preventDefault();
|
|
411
|
-
this.#step(seg,
|
|
412
|
-
|
|
424
|
+
this.#step(seg, key === "ArrowUp" ? 1 : -1);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (key === "PageUp" || key === "PageDown") {
|
|
413
428
|
e.preventDefault();
|
|
414
|
-
this.#
|
|
415
|
-
|
|
429
|
+
this.#step(seg, (key === "PageUp" ? 1 : -1) * (seg.kind === "minute" ? 5 : 1));
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (key === "ArrowLeft" || key === "ArrowRight") {
|
|
433
|
+
const start = el.selectionStart ?? 0;
|
|
434
|
+
const end = el.selectionEnd ?? start;
|
|
435
|
+
const boundary = key === "ArrowLeft" ? start === 0 && end === 0 : start === el.value.length && end === el.value.length;
|
|
436
|
+
if (boundary) {
|
|
437
|
+
e.preventDefault();
|
|
438
|
+
this.#render();
|
|
439
|
+
this.#moveFocus(seg, key === "ArrowRight" ? 1 : -1);
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (key === "Backspace" && el.value === "") {
|
|
416
444
|
e.preventDefault();
|
|
417
|
-
this.#
|
|
418
|
-
|
|
445
|
+
this.#moveFocus(seg, -1);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (seg.kind !== "period" && this.#literals.some((literal) => literal.includes(key))) {
|
|
419
449
|
e.preventDefault();
|
|
420
|
-
this.#
|
|
450
|
+
this.#render();
|
|
451
|
+
this.#moveFocus(seg, 1, { numeric: true });
|
|
421
452
|
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
#onSegBeforeInput(e, seg) {
|
|
453
|
+
};
|
|
454
|
+
#onPaste = (e) => {
|
|
455
|
+
const text = e.clipboardData?.getData("text/plain").trim() ?? "";
|
|
456
|
+
if (!/^(?:[01]?\d|2[0-3]):[0-5]\d$/.test(text)) return;
|
|
427
457
|
e.preventDefault();
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
458
|
+
this.#applyValue(text);
|
|
459
|
+
this.#commitEdit();
|
|
460
|
+
this.#dispatch("change");
|
|
461
|
+
};
|
|
462
|
+
#onInput = (e) => {
|
|
463
|
+
const seg = this.#segFromTarget(e.target);
|
|
464
|
+
if (!seg || e.isComposing) return;
|
|
465
|
+
if (seg.kind === "period") this.#onPeriodInput(e, seg);
|
|
466
|
+
else this.#onNumericInput(seg);
|
|
467
|
+
};
|
|
468
|
+
#onNumericInput(seg) {
|
|
469
|
+
const { el } = seg;
|
|
470
|
+
const typed = el.value.replace(/\D/g, "");
|
|
471
|
+
if (typed !== el.value) el.value = typed;
|
|
472
|
+
const hour12Hour = seg.kind === "hour" && this.#hour12;
|
|
473
|
+
const typeMax = hour12Hour ? 23 : seg.max;
|
|
474
|
+
if (typed === "") {
|
|
475
|
+
if (seg.kind === "hour") this.#hour = null;
|
|
476
|
+
else this.#minute = null;
|
|
477
|
+
this.#commitEdit({ dispatch: false, preserve: el });
|
|
434
478
|
return;
|
|
435
479
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
this.#commitEdit();
|
|
444
|
-
this.#dispatch("change");
|
|
445
|
-
} else {
|
|
446
|
-
for (const ch of text) this.#typeChar(seg, ch);
|
|
447
|
-
}
|
|
448
|
-
return;
|
|
480
|
+
let raw = typed.slice(0, 2);
|
|
481
|
+
let value = Number(raw);
|
|
482
|
+
let normalized = false;
|
|
483
|
+
if (value > typeMax) {
|
|
484
|
+
raw = raw.at(-1);
|
|
485
|
+
value = Number(raw);
|
|
486
|
+
normalized = true;
|
|
449
487
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
let s = (text ?? "").trim().toLowerCase();
|
|
460
|
-
if (!s) return null;
|
|
461
|
-
let mer = null;
|
|
462
|
-
const m = s.match(/([ap])\.?m?\.?$/);
|
|
463
|
-
if (m) {
|
|
464
|
-
mer = m[1] === "p" ? "pm" : "am";
|
|
465
|
-
s = s.slice(0, m.index).trim();
|
|
466
|
-
}
|
|
467
|
-
let h;
|
|
468
|
-
let min;
|
|
469
|
-
if (/[:.]/.test(s)) {
|
|
470
|
-
const [hp, mp] = s.split(/[:.]/);
|
|
471
|
-
h = Number(hp);
|
|
472
|
-
min = Number(mp ?? "0");
|
|
473
|
-
} else {
|
|
474
|
-
const d = s.replace(/\D/g, "");
|
|
475
|
-
if (!d) return null;
|
|
476
|
-
if (d.length <= 2) {
|
|
477
|
-
h = Number(d);
|
|
478
|
-
min = 0;
|
|
488
|
+
const advance = raw.length === 2 || Number(`${value}0`) > typeMax;
|
|
489
|
+
if (hour12Hour) {
|
|
490
|
+
if (value === 0 && raw.length === 1) this.#hour = null;
|
|
491
|
+
else if (value === 0) {
|
|
492
|
+
this.#hour = 12;
|
|
493
|
+
this.#period = "am";
|
|
494
|
+
} else if (value > 12) {
|
|
495
|
+
this.#hour = value - 12;
|
|
496
|
+
this.#period = "pm";
|
|
479
497
|
} else {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
min = Number(d.slice(cut));
|
|
498
|
+
this.#hour = value;
|
|
499
|
+
if (this.#period == null) this.#period = "am";
|
|
483
500
|
}
|
|
501
|
+
} else {
|
|
502
|
+
this.#assign(seg, Math.max(seg.min, value));
|
|
484
503
|
}
|
|
485
|
-
|
|
486
|
-
if (
|
|
487
|
-
if (mer === "am" && h === 12) h = 0;
|
|
488
|
-
if (h < 0 || h > 23 || min < 0 || min > 59) return null;
|
|
489
|
-
return { h, min };
|
|
504
|
+
this.#commitEdit({ dispatch: false, preserve: normalized || advance ? void 0 : el });
|
|
505
|
+
if (advance) this.#moveFocus(seg, 1);
|
|
490
506
|
}
|
|
491
|
-
#
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
507
|
+
#onPeriodInput(e, seg) {
|
|
508
|
+
const { el } = seg;
|
|
509
|
+
const raw = el.value.trim();
|
|
510
|
+
if (!raw) {
|
|
511
|
+
this.#period = null;
|
|
512
|
+
this.#commitEdit({ dispatch: false, preserve: el });
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const lower = raw.toLowerCase();
|
|
516
|
+
const am = this.#amText.toLowerCase();
|
|
517
|
+
const pm = this.#pmText.toLowerCase();
|
|
518
|
+
const period = lower === "a" || lower === "am" || lower === am ? "am" : lower === "p" || lower === "pm" || lower === pm ? "pm" : null;
|
|
519
|
+
if (period) {
|
|
520
|
+
this.#period = period;
|
|
521
|
+
this.#commitEdit({ dispatch: false });
|
|
522
|
+
this.#moveFocus(seg, 1);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (am.startsWith(lower) || pm.startsWith(lower)) {
|
|
526
|
+
this.#period = null;
|
|
527
|
+
this.#commitEdit({ dispatch: false, preserve: el });
|
|
499
528
|
return;
|
|
500
529
|
}
|
|
501
|
-
|
|
530
|
+
e.stopPropagation();
|
|
531
|
+
el.value = this.#period === "pm" ? this.#pmText : this.#period === "am" ? this.#amText : "";
|
|
502
532
|
}
|
|
503
|
-
/** Arrow / page
|
|
533
|
+
/** Arrow / page increments wrap within a segment's [min, max];
|
|
504
534
|
* from empty, ↑ lands on min and ↓ on max. Clamps the resulting complete value
|
|
505
535
|
* into the field's `min`/`max` range (so ↑ can't step past `max`). */
|
|
506
536
|
#step(seg, delta) {
|
|
507
|
-
this.#buf = "";
|
|
508
537
|
if (seg.kind === "period") {
|
|
509
538
|
this.#period = this.#period == null ? "am" : this.#period === "am" ? "pm" : "am";
|
|
510
539
|
this.#clampIfComplete();
|
|
@@ -520,22 +549,16 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
520
549
|
this.#clampIfComplete();
|
|
521
550
|
this.#commitEdit();
|
|
522
551
|
}
|
|
523
|
-
/** Mutate a segment
|
|
552
|
+
/** Mutate a numeric segment without committing (so callers can clamp first). */
|
|
524
553
|
#assign(seg, val) {
|
|
525
|
-
if (seg.kind === "period") return;
|
|
526
554
|
if (seg.kind === "hour") this.#hour = val;
|
|
527
555
|
else this.#minute = val;
|
|
528
556
|
if (seg.kind === "hour" && this.#hour12 && this.#period == null) this.#period = "am";
|
|
529
557
|
}
|
|
530
|
-
#set(seg, val) {
|
|
531
|
-
this.#assign(seg, val);
|
|
532
|
-
this.#commitEdit();
|
|
533
|
-
}
|
|
534
558
|
// --- min / max range (total minutes since 00:00, or null when unbounded) ---
|
|
535
559
|
#parseTotal(v) {
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
return Math.min(23, Number(m[1])) * 60 + Math.min(59, Number(m[2]));
|
|
560
|
+
const time = parseTime(v);
|
|
561
|
+
return time ? time.h * 60 + time.min : null;
|
|
539
562
|
}
|
|
540
563
|
get #minTotal() {
|
|
541
564
|
return this.#parseTotal(this.getAttribute("min"));
|
|
@@ -562,75 +585,20 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
562
585
|
this.#applyValue(`${pad2(Math.floor(clamped / 60))}:${pad2(clamped % 60)}`);
|
|
563
586
|
return true;
|
|
564
587
|
}
|
|
565
|
-
#
|
|
566
|
-
if (seg
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const candidate = Number(this.#buf + d);
|
|
577
|
-
let raw;
|
|
578
|
-
let advance = false;
|
|
579
|
-
if (candidate > typeMax) {
|
|
580
|
-
raw = Number(d);
|
|
581
|
-
this.#buf = d;
|
|
582
|
-
} else {
|
|
583
|
-
raw = candidate;
|
|
584
|
-
this.#buf += d;
|
|
585
|
-
}
|
|
586
|
-
if (Number(`${raw}0`) > typeMax || this.#buf.length >= String(typeMax).length) {
|
|
587
|
-
advance = true;
|
|
588
|
-
this.#buf = "";
|
|
589
|
-
}
|
|
590
|
-
if (hour12Hour) {
|
|
591
|
-
if (raw === 0 && !advance) {
|
|
592
|
-
seg.el.textContent = "0";
|
|
593
|
-
seg.el.setAttribute("data-placeholder", "");
|
|
594
|
-
seg.el.removeAttribute("aria-valuenow");
|
|
595
|
-
seg.el.removeAttribute("aria-valuetext");
|
|
588
|
+
#focusSegment(seg) {
|
|
589
|
+
if (!seg) return;
|
|
590
|
+
seg.el.focus();
|
|
591
|
+
seg.el.select();
|
|
592
|
+
}
|
|
593
|
+
#moveFocus(from, dir, options = {}) {
|
|
594
|
+
const index = this.#segs.indexOf(from);
|
|
595
|
+
for (let i = index + dir; i >= 0 && i < this.#segs.length; i += dir) {
|
|
596
|
+
const next = this.#segs[i];
|
|
597
|
+
if (!options.numeric || next.kind !== "period") {
|
|
598
|
+
this.#focusSegment(next);
|
|
596
599
|
return;
|
|
597
600
|
}
|
|
598
|
-
if (raw === 0) {
|
|
599
|
-
this.#hour = 12;
|
|
600
|
-
this.#period = "am";
|
|
601
|
-
} else if (raw > 12) {
|
|
602
|
-
this.#hour = raw - 12;
|
|
603
|
-
this.#period = "pm";
|
|
604
|
-
} else {
|
|
605
|
-
this.#hour = raw;
|
|
606
|
-
if (this.#period == null) this.#period = "am";
|
|
607
|
-
}
|
|
608
|
-
this.#commitEdit();
|
|
609
|
-
if (advance) this.#moveFocus(seg, 1);
|
|
610
|
-
return;
|
|
611
601
|
}
|
|
612
|
-
this.#assign(seg, Math.max(seg.min, raw));
|
|
613
|
-
this.#commitEdit();
|
|
614
|
-
if (advance) this.#moveFocus(seg, 1);
|
|
615
|
-
}
|
|
616
|
-
#moveFocus(from, dir) {
|
|
617
|
-
const i = this.#segs.indexOf(from);
|
|
618
|
-
const next = this.#segs[i + dir];
|
|
619
|
-
if (next) next.el.focus();
|
|
620
|
-
}
|
|
621
|
-
/** On losing a segment, reset the digit buffer; when focus has left the whole
|
|
622
|
-
* group (not just hopped to a sibling segment), clamp into `min`/`max` and
|
|
623
|
-
* fire `change`. `shadowRoot.activeElement` reliably reports the focused
|
|
624
|
-
* segment even across the shadow boundary (blur `relatedTarget` is retargeted
|
|
625
|
-
* to null), so defer one microtask to read where focus landed. */
|
|
626
|
-
#onSegBlur() {
|
|
627
|
-
this.#buf = "";
|
|
628
|
-
queueMicrotask(() => {
|
|
629
|
-
const active = this.shadowRoot?.activeElement;
|
|
630
|
-
if (this.#segs.some((s) => s.el === active)) return;
|
|
631
|
-
if (this.#clampIfComplete()) this.#commitEdit();
|
|
632
|
-
this.#dispatch("change");
|
|
633
|
-
});
|
|
634
602
|
}
|
|
635
603
|
// --- Value + rendering ---
|
|
636
604
|
/** Canonical 0–23 hour from the display state, or null when incomplete. */
|
|
@@ -640,70 +608,48 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
640
608
|
if (this.#period == null) return null;
|
|
641
609
|
return to24(this.#hour, this.#period);
|
|
642
610
|
}
|
|
643
|
-
#commitEdit() {
|
|
644
|
-
this.#render();
|
|
611
|
+
#commitEdit({ dispatch = true, preserve } = {}) {
|
|
612
|
+
this.#render(preserve);
|
|
645
613
|
this.#iso = this.value;
|
|
646
614
|
this.#internals?.setFormValue(this.value);
|
|
647
615
|
this.#updateFilled();
|
|
648
616
|
this.#updateValidity();
|
|
649
|
-
this.#dispatch("input");
|
|
617
|
+
if (dispatch) this.#dispatch("input");
|
|
650
618
|
}
|
|
651
|
-
#render() {
|
|
619
|
+
#render(preserve) {
|
|
652
620
|
for (const seg of this.#segs) {
|
|
653
|
-
|
|
654
|
-
else this.#renderSeg(seg, seg.kind === "hour" ? this.#hour : this.#minute);
|
|
621
|
+
this.#renderSeg(seg, preserve === seg.el);
|
|
655
622
|
}
|
|
656
623
|
}
|
|
657
|
-
/** Paint
|
|
658
|
-
*
|
|
659
|
-
#renderSeg(seg,
|
|
624
|
+
/** Paint a segment's committed value without disturbing a focused native input
|
|
625
|
+
* that already owns valid in-progress text and selection. */
|
|
626
|
+
#renderSeg(seg, preserve) {
|
|
660
627
|
const el = seg.el;
|
|
661
628
|
if (seg.kind === "period") {
|
|
662
|
-
const
|
|
663
|
-
el.
|
|
664
|
-
el.
|
|
665
|
-
el.setAttribute("aria-valuemin", "0");
|
|
666
|
-
el.setAttribute("aria-valuemax", "1");
|
|
667
|
-
if (set2) {
|
|
668
|
-
el.setAttribute("aria-valuenow", this.#period === "pm" ? "1" : "0");
|
|
669
|
-
el.setAttribute("aria-valuetext", this.#period === "pm" ? this.#pmText : this.#amText);
|
|
670
|
-
} else {
|
|
671
|
-
el.removeAttribute("aria-valuenow");
|
|
672
|
-
el.removeAttribute("aria-valuetext");
|
|
673
|
-
}
|
|
629
|
+
const value2 = this.#period === "pm" ? this.#pmText : this.#period === "am" ? this.#amText : "";
|
|
630
|
+
if (!preserve) el.value = value2;
|
|
631
|
+
el.placeholder = this.#amText;
|
|
674
632
|
return;
|
|
675
633
|
}
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
el.
|
|
679
|
-
el.toggleAttribute("data-placeholder", !set);
|
|
680
|
-
el.setAttribute("aria-valuemin", String(seg.min));
|
|
681
|
-
el.setAttribute("aria-valuemax", String(seg.max));
|
|
682
|
-
if (set) {
|
|
683
|
-
el.setAttribute("aria-valuenow", String(val));
|
|
684
|
-
el.setAttribute("aria-valuetext", pad2(val));
|
|
685
|
-
} else {
|
|
686
|
-
el.removeAttribute("aria-valuenow");
|
|
687
|
-
el.removeAttribute("aria-valuetext");
|
|
688
|
-
}
|
|
634
|
+
const value = seg.kind === "hour" ? this.#hour : this.#minute;
|
|
635
|
+
if (!preserve) el.value = value == null ? "" : pad2(value);
|
|
636
|
+
el.placeholder = "\u2013\u2013";
|
|
689
637
|
}
|
|
690
638
|
/** Parse a `"HH:mm"` value into the display segments (24h → display units). */
|
|
691
639
|
#applyValue(v) {
|
|
692
|
-
const
|
|
693
|
-
if (!
|
|
640
|
+
const time = parseTime(v);
|
|
641
|
+
if (!time) {
|
|
694
642
|
this.#hour = null;
|
|
695
643
|
this.#minute = null;
|
|
696
644
|
this.#period = null;
|
|
697
645
|
return;
|
|
698
646
|
}
|
|
699
|
-
|
|
700
|
-
const min = Math.min(59, Number(m[2]));
|
|
701
|
-
this.#minute = min;
|
|
647
|
+
this.#minute = time.min;
|
|
702
648
|
if (this.#hour12) {
|
|
703
|
-
this.#hour = (h + 11) % 12 + 1;
|
|
704
|
-
this.#period = h < 12 ? "am" : "pm";
|
|
649
|
+
this.#hour = (time.h + 11) % 12 + 1;
|
|
650
|
+
this.#period = time.h < 12 ? "am" : "pm";
|
|
705
651
|
} else {
|
|
706
|
-
this.#hour = h;
|
|
652
|
+
this.#hour = time.h;
|
|
707
653
|
this.#period = null;
|
|
708
654
|
}
|
|
709
655
|
}
|
|
@@ -716,20 +662,11 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
716
662
|
else this.#segRow.removeAttribute("aria-label");
|
|
717
663
|
}
|
|
718
664
|
#onLabelSlotChange = () => {
|
|
719
|
-
this.#labelBox.classList.toggle("has-label", this.#labelSlot.assignedNodes().length > 0);
|
|
720
665
|
this.#applyGroupLabel();
|
|
721
666
|
};
|
|
722
|
-
#onHintSlotChange = () => {
|
|
723
|
-
this.#hintBox.classList.toggle("has-hint", this.#hintSlot.assignedNodes().length > 0);
|
|
724
|
-
};
|
|
725
667
|
#syncDisabled() {
|
|
726
668
|
const off = this.hasAttribute("disabled") || this.#formDisabled;
|
|
727
|
-
for (const seg of this.#segs)
|
|
728
|
-
if (off) seg.el.setAttribute("aria-disabled", "true");
|
|
729
|
-
else seg.el.removeAttribute("aria-disabled");
|
|
730
|
-
seg.el.tabIndex = off ? -1 : 0;
|
|
731
|
-
seg.el.contentEditable = off ? "false" : "true";
|
|
732
|
-
}
|
|
669
|
+
for (const seg of this.#segs) seg.el.disabled = off;
|
|
733
670
|
}
|
|
734
671
|
#syncStatus() {
|
|
735
672
|
const critical = this.getAttribute("status") === "critical";
|
|
@@ -740,13 +677,8 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
740
677
|
}
|
|
741
678
|
}
|
|
742
679
|
#updateFilled() {
|
|
743
|
-
|
|
744
|
-
this.#
|
|
745
|
-
try {
|
|
746
|
-
if (filled) this.#internals?.states.add("filled");
|
|
747
|
-
else this.#internals?.states.delete("filled");
|
|
748
|
-
} catch {
|
|
749
|
-
}
|
|
680
|
+
if (this.value) this.#internals?.states.add("filled");
|
|
681
|
+
else this.#internals?.states.delete("filled");
|
|
750
682
|
}
|
|
751
683
|
#updateValidity() {
|
|
752
684
|
if (!this.#internals) return;
|
|
@@ -779,12 +711,10 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
779
711
|
const next = v ?? "";
|
|
780
712
|
if (next === this.value) return;
|
|
781
713
|
this.#applyValue(next);
|
|
782
|
-
this.#iso = this.value;
|
|
783
714
|
if (this.#ready) {
|
|
784
|
-
this.#
|
|
785
|
-
|
|
786
|
-
this.#
|
|
787
|
-
this.#updateValidity();
|
|
715
|
+
this.#commitEdit({ dispatch: false });
|
|
716
|
+
} else {
|
|
717
|
+
this.#iso = this.value;
|
|
788
718
|
}
|
|
789
719
|
}
|
|
790
720
|
/** Empty the field, refocus the first segment, and fire input + change. */
|
|
@@ -792,12 +722,7 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
792
722
|
this.#hour = null;
|
|
793
723
|
this.#minute = null;
|
|
794
724
|
this.#period = null;
|
|
795
|
-
this.#
|
|
796
|
-
this.#render();
|
|
797
|
-
this.#internals?.setFormValue("");
|
|
798
|
-
this.#updateFilled();
|
|
799
|
-
this.#updateValidity();
|
|
800
|
-
this.#dispatch("input");
|
|
725
|
+
this.#commitEdit();
|
|
801
726
|
this.#dispatch("change");
|
|
802
727
|
this.dispatchEvent(new CustomEvent(CLEAR_INPUT_EVENT, { bubbles: true }));
|
|
803
728
|
this.#segs[0]?.el.focus();
|
|
@@ -829,12 +754,7 @@ class AInputTimeElement extends HTMLElementBase {
|
|
|
829
754
|
}
|
|
830
755
|
formResetCallback() {
|
|
831
756
|
this.#applyValue(this.getAttribute("defaultvalue") ?? "");
|
|
832
|
-
this.#
|
|
833
|
-
this.#render();
|
|
834
|
-
this.#internals?.setFormValue(this.value);
|
|
835
|
-
this.#updateFilled();
|
|
836
|
-
this.#updateValidity();
|
|
837
|
-
this.#dispatch("input");
|
|
757
|
+
this.#commitEdit();
|
|
838
758
|
this.#dispatch("change");
|
|
839
759
|
}
|
|
840
760
|
formDisabledCallback(disabled) {
|