@optionfactory/fml 8.0.0 → 8.0.1
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/client-errors.iife.js +11 -8
- package/dist/client-errors.iife.js.map +1 -1
- package/dist/client-errors.iife.min.js +1 -1
- package/dist/client-errors.iife.min.js.map +1 -1
- package/dist/fml.css +10 -1
- package/dist/fml.css.map +1 -1
- package/dist/fml.d.mts +73 -13
- package/dist/fml.iife.js +598 -208
- package/dist/fml.iife.js.map +1 -1
- package/dist/fml.iife.min.js +1 -1
- package/dist/fml.iife.min.js.map +1 -1
- package/dist/fml.min.mjs +1 -1
- package/dist/fml.min.mjs.map +1 -1
- package/dist/fml.mjs +598 -208
- package/dist/fml.mjs.map +1 -1
- package/dist/ftl.d.mts +2 -1
- package/dist/ftl.iife.js +60 -33
- package/dist/ftl.iife.js.map +1 -1
- package/dist/ftl.iife.min.js +1 -1
- package/dist/ftl.iife.min.js.map +1 -1
- package/dist/ftl.min.mjs +1 -1
- package/dist/ftl.min.mjs.map +1 -1
- package/dist/ftl.mjs +60 -33
- package/dist/ftl.mjs.map +1 -1
- package/dist/ful.css +10 -1
- package/dist/ful.css.map +1 -1
- package/dist/ful.d.mts +22 -6
- package/dist/ful.iife.js +496 -166
- package/dist/ful.iife.js.map +1 -1
- package/dist/ful.iife.min.js +1 -1
- package/dist/ful.iife.min.js.map +1 -1
- package/dist/ful.min.mjs +1 -1
- package/dist/ful.min.mjs.map +1 -1
- package/dist/ful.mjs +496 -166
- package/dist/ful.mjs.map +1 -1
- package/dist/httpc.d.mts +49 -6
- package/dist/httpc.iife.js +40 -9
- package/dist/httpc.iife.js.map +1 -1
- package/dist/httpc.iife.min.js +1 -1
- package/dist/httpc.iife.min.js.map +1 -1
- package/dist/httpc.min.mjs +1 -1
- package/dist/httpc.min.mjs.map +1 -1
- package/dist/httpc.mjs +40 -9
- package/dist/httpc.mjs.map +1 -1
- package/package.json +6 -7
package/dist/ful.iife.js
CHANGED
|
@@ -7,7 +7,16 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
7
7
|
}
|
|
8
8
|
static load(k) {
|
|
9
9
|
const got = localStorage.getItem(k);
|
|
10
|
-
|
|
10
|
+
if (got === null) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(got);
|
|
15
|
+
} catch {
|
|
16
|
+
//not what save wrote: drop it, otherwise every later read fails the same way
|
|
17
|
+
localStorage.removeItem(k);
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
11
20
|
}
|
|
12
21
|
static remove(k) {
|
|
13
22
|
localStorage.removeItem(k);
|
|
@@ -25,7 +34,16 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
25
34
|
}
|
|
26
35
|
static load(k) {
|
|
27
36
|
const got = sessionStorage.getItem(k);
|
|
28
|
-
|
|
37
|
+
if (got === null) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(got);
|
|
42
|
+
} catch {
|
|
43
|
+
//not what save wrote: drop it, otherwise every later read fails the same way
|
|
44
|
+
sessionStorage.removeItem(k);
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
29
47
|
}
|
|
30
48
|
static remove(k) {
|
|
31
49
|
sessionStorage.removeItem(k);
|
|
@@ -47,7 +65,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
47
65
|
return undefined;
|
|
48
66
|
}
|
|
49
67
|
if (stored.revision !== revision) {
|
|
50
|
-
|
|
68
|
+
LocalStorage.remove(key);
|
|
51
69
|
return undefined;
|
|
52
70
|
}
|
|
53
71
|
return stored.data;
|
|
@@ -64,7 +82,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
64
82
|
return undefined;
|
|
65
83
|
}
|
|
66
84
|
if (stored.revision !== revision) {
|
|
67
|
-
|
|
85
|
+
SessionStorage.remove(key);
|
|
68
86
|
return undefined;
|
|
69
87
|
}
|
|
70
88
|
return stored.data;
|
|
@@ -199,7 +217,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
199
217
|
*/
|
|
200
218
|
static debounce(timeoutMs, func, options) {
|
|
201
219
|
const opts = options ?? Timing.DEBOUNCE_DEFAULT;
|
|
202
|
-
let tid = null;
|
|
220
|
+
let tid = /** @type {number | null} */ (null);
|
|
203
221
|
let args = [];
|
|
204
222
|
let previousTimestamp = 0;
|
|
205
223
|
|
|
@@ -219,8 +237,8 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
219
237
|
}
|
|
220
238
|
};
|
|
221
239
|
|
|
222
|
-
const debounced =
|
|
223
|
-
args =
|
|
240
|
+
const debounced = (...called) => {
|
|
241
|
+
args = called;
|
|
224
242
|
previousTimestamp = performance.now();
|
|
225
243
|
if (tid === null) {
|
|
226
244
|
tid = setTimeout(later, timeoutMs);
|
|
@@ -229,7 +247,11 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
229
247
|
}
|
|
230
248
|
}
|
|
231
249
|
};
|
|
232
|
-
const abort = () =>
|
|
250
|
+
const abort = () => {
|
|
251
|
+
clearTimeout(tid ?? undefined);
|
|
252
|
+
tid = null;
|
|
253
|
+
args = [];
|
|
254
|
+
};
|
|
233
255
|
return [debounced, abort];
|
|
234
256
|
}
|
|
235
257
|
static THROTTLE_DEFAULT = 0;
|
|
@@ -244,7 +266,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
244
266
|
*/
|
|
245
267
|
static throttle(timeoutMs, func, options) {
|
|
246
268
|
const opts = options ?? Timing.THROTTLE_DEFAULT;
|
|
247
|
-
let tid = null;
|
|
269
|
+
let tid = /** @type {number | null} */ (null);
|
|
248
270
|
let args = [];
|
|
249
271
|
let previousTimestamp = 0;
|
|
250
272
|
|
|
@@ -256,13 +278,13 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
256
278
|
args = [];
|
|
257
279
|
}
|
|
258
280
|
};
|
|
259
|
-
const throttled =
|
|
281
|
+
const throttled = (...called) => {
|
|
260
282
|
const now = performance.now();
|
|
261
283
|
if (!previousTimestamp && opts & Timing.THROTTLE_NO_LEADING) {
|
|
262
284
|
previousTimestamp = now;
|
|
263
285
|
}
|
|
264
286
|
const remaining = previousTimestamp === 0 ? 0 : timeoutMs - (now - previousTimestamp);
|
|
265
|
-
args =
|
|
287
|
+
args = called;
|
|
266
288
|
if (remaining <= 0 || remaining > timeoutMs) {
|
|
267
289
|
if (tid !== null) {
|
|
268
290
|
clearTimeout(tid);
|
|
@@ -277,7 +299,11 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
277
299
|
tid = setTimeout(later, remaining);
|
|
278
300
|
}
|
|
279
301
|
};
|
|
280
|
-
const abort = () =>
|
|
302
|
+
const abort = () => {
|
|
303
|
+
clearTimeout(tid ?? undefined);
|
|
304
|
+
tid = null;
|
|
305
|
+
args = [];
|
|
306
|
+
};
|
|
281
307
|
return [throttled, abort];
|
|
282
308
|
}
|
|
283
309
|
}
|
|
@@ -291,7 +317,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
291
317
|
*/
|
|
292
318
|
static flatten(obj, prefix, stops) {
|
|
293
319
|
return Object.keys(obj).reduce((acc, k) => {
|
|
294
|
-
const pre = prefix.length ? prefix
|
|
320
|
+
const pre = prefix.length ? `${prefix}.${k}` : k;
|
|
295
321
|
if (!stops.has(pre) && typeof obj[k] === 'object' && obj[k] !== null) {
|
|
296
322
|
Object.assign(acc, Bindings.flatten(obj[k], pre, stops));
|
|
297
323
|
} else {
|
|
@@ -309,7 +335,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
309
335
|
static providePath(result, path, value) {
|
|
310
336
|
const keys = path.split('.').map((k) => (/^[0-9]+$/.test(k) ? +k : k));
|
|
311
337
|
let current = result ?? {};
|
|
312
|
-
let previous = null;
|
|
338
|
+
let previous = /** @type {any} */ (null);
|
|
313
339
|
for (let i = 0; ; ++i) {
|
|
314
340
|
const ckey = keys[i];
|
|
315
341
|
const pkey = keys[i - 1];
|
|
@@ -342,12 +368,12 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
342
368
|
if (!el.checked) {
|
|
343
369
|
return undefined;
|
|
344
370
|
}
|
|
345
|
-
return el.dataset
|
|
371
|
+
return el.dataset.fulBindType === 'boolean' ? el.value === 'true' : el.value;
|
|
346
372
|
}
|
|
347
373
|
if (el.getAttribute('type') === 'checkbox') {
|
|
348
374
|
return el.checked;
|
|
349
375
|
}
|
|
350
|
-
if (el.dataset
|
|
376
|
+
if (el.dataset.fulBindType === 'boolean') {
|
|
351
377
|
return !el.value ? null : el.value === 'true';
|
|
352
378
|
}
|
|
353
379
|
if (el.tagName === 'INPUT' || el.tagName === 'SELECT' || el.tagName === 'TEXTAREA') {
|
|
@@ -380,12 +406,14 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
380
406
|
|
|
381
407
|
/**
|
|
382
408
|
*
|
|
383
|
-
* @param {Element
|
|
409
|
+
* @param {Element & {dataset?: any} & {checked?: boolean} & {value?: any}} el
|
|
384
410
|
* @returns
|
|
385
411
|
*/
|
|
386
412
|
static mutate(el, raw) {
|
|
387
413
|
if (el.getAttribute('type') === 'radio') {
|
|
388
|
-
|
|
414
|
+
//values are matched as strings, as ful-radio-group does: extract decodes
|
|
415
|
+
//boolean radios, and payloads carry numbers where the attribute is text
|
|
416
|
+
el.checked = raw != null && el.getAttribute('value') === String(raw);
|
|
389
417
|
return;
|
|
390
418
|
}
|
|
391
419
|
if (el.getAttribute('type') === 'checkbox') {
|
|
@@ -409,7 +437,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
409
437
|
static errors(form, es, scrollOnError) {
|
|
410
438
|
const fieldErrors = es.filter((e) => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT');
|
|
411
439
|
const globalErrors = es.filter((e) => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
|
|
412
|
-
form.querySelectorAll(`[name]`).forEach((el) =>
|
|
440
|
+
form.querySelectorAll(`[name]`).forEach((el) => {
|
|
441
|
+
el.setCustomValidity?.('');
|
|
442
|
+
});
|
|
413
443
|
form.querySelectorAll('ful-errors').forEach((el) => {
|
|
414
444
|
el.replaceChildren();
|
|
415
445
|
el.setAttribute('hidden', '');
|
|
@@ -417,12 +447,12 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
417
447
|
fieldErrors.forEach((e) => {
|
|
418
448
|
const name = e.context.replace(/\[/g, '.').replace(/\]\./g, '.').replace(/\]/g, '');
|
|
419
449
|
const parts = name.split('.');
|
|
420
|
-
for (let i = parts.length; i
|
|
450
|
+
for (let i = parts.length; i !== 0; --i) {
|
|
421
451
|
const prefix = parts.slice(0, i).join('.');
|
|
422
452
|
const suffix = parts.slice(i, parts.length).join('.');
|
|
423
|
-
form.querySelectorAll(`[name='${CSS.escape(prefix)}']`).forEach((input) =>
|
|
424
|
-
input.setCustomValidity?.(e.reason, suffix)
|
|
425
|
-
);
|
|
453
|
+
form.querySelectorAll(`[name='${CSS.escape(prefix)}']`).forEach((input) => {
|
|
454
|
+
input.setCustomValidity?.(e.reason, suffix);
|
|
455
|
+
});
|
|
426
456
|
}
|
|
427
457
|
});
|
|
428
458
|
form.querySelectorAll('ful-errors').forEach((el) => {
|
|
@@ -432,7 +462,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
432
462
|
el.removeAttribute('hidden');
|
|
433
463
|
}
|
|
434
464
|
});
|
|
435
|
-
if (es.length
|
|
465
|
+
if (es.length === 0 || !scrollOnError) {
|
|
436
466
|
return;
|
|
437
467
|
}
|
|
438
468
|
Array.from(form.querySelectorAll(`:invalid`))
|
|
@@ -504,7 +534,8 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
504
534
|
class Form extends index_mjs.ParsedElement {
|
|
505
535
|
form;
|
|
506
536
|
render() {
|
|
507
|
-
const form =
|
|
537
|
+
const form = document.createElement('form');
|
|
538
|
+
this.form = form;
|
|
508
539
|
form.setAttribute('novalidate', '');
|
|
509
540
|
index_mjs.Attributes.forward('form-', this, form);
|
|
510
541
|
form.replaceChildren(...this.childNodes);
|
|
@@ -527,50 +558,53 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
527
558
|
*/
|
|
528
559
|
async submit(submitter) {
|
|
529
560
|
this.spinner(true);
|
|
561
|
+
//one try: building the loader and preparing the request are as much part of a
|
|
562
|
+
//submit as sending it, and a mapper that throws is how a caller reports a
|
|
563
|
+
//problem with the values
|
|
564
|
+
let values;
|
|
565
|
+
let request;
|
|
530
566
|
try {
|
|
531
567
|
const loader = index_mjs.registry.component(this.getAttribute('loader') ?? 'loaders:form').create(this);
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
568
|
+
values = Bindings.extractFrom(this.form, submitter);
|
|
569
|
+
request = await loader.prepare(values, this);
|
|
570
|
+
const se = new CustomEvent('submit', {
|
|
571
|
+
bubbles: true,
|
|
572
|
+
cancelable: true,
|
|
573
|
+
detail: { submitter, values, request },
|
|
574
|
+
});
|
|
575
|
+
if (!this.dispatchEvent(se)) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
this.errors = [];
|
|
579
|
+
const sre = new CustomEvent('submit:requested', {
|
|
580
|
+
bubbles: true,
|
|
581
|
+
cancelable: false,
|
|
582
|
+
detail: { submitter, values: se.detail.values, request: se.detail.request },
|
|
583
|
+
});
|
|
584
|
+
let response = await AsyncEvents.fireAsync(this, sre, { mode: 'pipeline' });
|
|
585
|
+
request = sre.detail.request;
|
|
586
|
+
|
|
587
|
+
response = await loader.submit(request, this, response);
|
|
588
|
+
const mapped = await loader.transform(response, this);
|
|
589
|
+
this.dispatchEvent(
|
|
590
|
+
new CustomEvent('submit:success', {
|
|
536
591
|
bubbles: true,
|
|
537
|
-
cancelable:
|
|
538
|
-
detail: { submitter, values, request },
|
|
539
|
-
})
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
const sre = new CustomEvent('submit:requested', {
|
|
592
|
+
cancelable: false,
|
|
593
|
+
detail: { submitter, values, request, response: mapped },
|
|
594
|
+
}),
|
|
595
|
+
);
|
|
596
|
+
} catch (e) {
|
|
597
|
+
this.dispatchEvent(
|
|
598
|
+
new CustomEvent('submit:failure', {
|
|
545
599
|
bubbles: true,
|
|
546
600
|
cancelable: false,
|
|
547
|
-
detail: { submitter, values
|
|
548
|
-
})
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
response = await loader.submit(request, this, response);
|
|
553
|
-
const mapped = await loader.transform(response, this);
|
|
554
|
-
this.dispatchEvent(
|
|
555
|
-
new CustomEvent('submit:success', {
|
|
556
|
-
bubbles: true,
|
|
557
|
-
cancelable: false,
|
|
558
|
-
detail: { submitter, values, request, response: mapped },
|
|
559
|
-
}),
|
|
560
|
-
);
|
|
561
|
-
} catch (e) {
|
|
562
|
-
this.dispatchEvent(
|
|
563
|
-
new CustomEvent('submit:failure', {
|
|
564
|
-
bubbles: true,
|
|
565
|
-
cancelable: false,
|
|
566
|
-
detail: { submitter, values, request, exception: e },
|
|
567
|
-
}),
|
|
568
|
-
);
|
|
569
|
-
if (e instanceof index_mjs$1.Failure) {
|
|
570
|
-
this.errors = e.problems;
|
|
571
|
-
}
|
|
572
|
-
console.warn('failed to submit form', this, 'reason:', e);
|
|
601
|
+
detail: { submitter, values, request, exception: e },
|
|
602
|
+
}),
|
|
603
|
+
);
|
|
604
|
+
if (e instanceof index_mjs$1.Failure) {
|
|
605
|
+
this.errors = e.problems;
|
|
573
606
|
}
|
|
607
|
+
console.warn('failed to submit form', this, 'reason:', e);
|
|
574
608
|
} finally {
|
|
575
609
|
this.spinner(false);
|
|
576
610
|
}
|
|
@@ -578,7 +612,20 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
578
612
|
reset() {
|
|
579
613
|
this.form.reset();
|
|
580
614
|
}
|
|
615
|
+
#spinning = 0;
|
|
581
616
|
spinner(spin) {
|
|
617
|
+
//submits can overlap: only the outermost one saves and restores the button states
|
|
618
|
+
if (spin) {
|
|
619
|
+
++this.#spinning;
|
|
620
|
+
if (this.#spinning !== 1) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
} else {
|
|
624
|
+
this.#spinning = Math.max(0, this.#spinning - 1);
|
|
625
|
+
if (this.#spinning !== 0) {
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
582
629
|
this.querySelectorAll('ful-spinner').forEach((el) => {
|
|
583
630
|
const hel = /** @type HTMLElement */ (el);
|
|
584
631
|
hel.hidden = !spin;
|
|
@@ -609,7 +656,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
609
656
|
}
|
|
610
657
|
|
|
611
658
|
class Input extends index_mjs.ParsedElement {
|
|
612
|
-
static observed = ['value', 'readonly:presence', 'required:presence'];
|
|
659
|
+
static observed = ['value', 'readonly:presence', 'required:presence', 'placeholder'];
|
|
613
660
|
static slots = true;
|
|
614
661
|
static template = `
|
|
615
662
|
<div class="form-label">
|
|
@@ -646,12 +693,6 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
646
693
|
this._input = fragment.querySelector('input,textarea');
|
|
647
694
|
|
|
648
695
|
index_mjs.Attributes.forward('input-', this, this._input);
|
|
649
|
-
if (!skipObservedSetup) {
|
|
650
|
-
this.disabled = disabled;
|
|
651
|
-
this.readonly = observed.readonly;
|
|
652
|
-
this.required = observed.required;
|
|
653
|
-
this.value = observed.value;
|
|
654
|
-
}
|
|
655
696
|
this._input.addEventListener('keydown', (evt) => {
|
|
656
697
|
if (evt.key !== 'Enter' || this._type() === 'textarea') {
|
|
657
698
|
return;
|
|
@@ -667,19 +708,26 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
667
708
|
form.requestSubmit(submitter);
|
|
668
709
|
});
|
|
669
710
|
this._input.addEventListener('input', (evt) => {
|
|
670
|
-
const
|
|
671
|
-
if (!
|
|
711
|
+
const mask = this.getAttribute('mask');
|
|
712
|
+
if (!mask) {
|
|
672
713
|
return;
|
|
673
714
|
}
|
|
715
|
+
const strip = (v) => v.replace(new RegExp(mask, 'g'), '');
|
|
674
716
|
const before = evt.target.value;
|
|
675
|
-
const after = before
|
|
717
|
+
const after = strip(before);
|
|
676
718
|
if (before === after) {
|
|
677
719
|
return;
|
|
678
720
|
}
|
|
679
721
|
const start = evt.target.selectionStart;
|
|
680
|
-
const offset = before.length - after.length;
|
|
681
722
|
evt.target.value = after;
|
|
682
|
-
|
|
723
|
+
if (start === null) {
|
|
724
|
+
//email, number and the date types have no selection to restore
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
//the caret keeps its place among the characters that survived, so only the
|
|
728
|
+
//ones stripped before it count
|
|
729
|
+
const caret = strip(before.slice(0, start)).length;
|
|
730
|
+
evt.target.setSelectionRange(caret, caret);
|
|
683
731
|
});
|
|
684
732
|
this._input.addEventListener('change', (evt) => {
|
|
685
733
|
evt.stopPropagation();
|
|
@@ -699,6 +747,15 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
699
747
|
this._input.ariaDescribedByElements = [this._fieldError];
|
|
700
748
|
this._input.ariaLabelledByElements = [label];
|
|
701
749
|
this.replaceChildren(fragment);
|
|
750
|
+
if (!skipObservedSetup) {
|
|
751
|
+
// biome-ignore lint/complexity/noUselessThisAlias: keeps checkJs from seeing these as class fields
|
|
752
|
+
const el = this;
|
|
753
|
+
el.disabled = disabled;
|
|
754
|
+
el.readonly = observed.readonly;
|
|
755
|
+
el.required = observed.required;
|
|
756
|
+
el.placeholder = observed.placeholder;
|
|
757
|
+
el.value = observed.value;
|
|
758
|
+
}
|
|
702
759
|
}
|
|
703
760
|
get value() {
|
|
704
761
|
const uppercase = this.hasAttribute('uppercase');
|
|
@@ -725,6 +782,11 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
725
782
|
}
|
|
726
783
|
set disabled(d) {
|
|
727
784
|
index_mjs.Attributes.toggle(this._input, 'disabled', d);
|
|
785
|
+
//also on the host: a form associated element only matches :disabled through its
|
|
786
|
+
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
787
|
+
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
788
|
+
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
789
|
+
index_mjs.Attributes.toggle(this, 'disabled', d);
|
|
728
790
|
}
|
|
729
791
|
get required() {
|
|
730
792
|
return this._input.getAttribute('aria-required') === 'true';
|
|
@@ -735,6 +797,18 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
735
797
|
index_mjs.Attributes.toggle(this, 'required', d);
|
|
736
798
|
});
|
|
737
799
|
}
|
|
800
|
+
get placeholder() {
|
|
801
|
+
const v = this._input.getAttribute('placeholder');
|
|
802
|
+
return v === ' ' ? null : v;
|
|
803
|
+
}
|
|
804
|
+
set placeholder(d) {
|
|
805
|
+
//without a placeholder :placeholder-shown never matches, and floating labels
|
|
806
|
+
//rely on it, so a blank one stands in for none
|
|
807
|
+
index_mjs.Attributes.set(this._input, 'placeholder', d ?? ' ');
|
|
808
|
+
this.reflect(() => {
|
|
809
|
+
index_mjs.Attributes.set(this, 'placeholder', d);
|
|
810
|
+
});
|
|
811
|
+
}
|
|
738
812
|
focus(options) {
|
|
739
813
|
this._input.focus(options);
|
|
740
814
|
}
|
|
@@ -754,23 +828,23 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
754
828
|
|
|
755
829
|
class LocalDate extends index_mjs.ParsedElement {
|
|
756
830
|
render() {
|
|
757
|
-
const content = this.
|
|
831
|
+
const content = this.textContent.trim();
|
|
758
832
|
if (content === '') {
|
|
759
|
-
this.
|
|
833
|
+
this.replaceChildren(this.getAttribute('default') ?? '');
|
|
760
834
|
return;
|
|
761
835
|
}
|
|
762
836
|
const locale = this.getAttribute('locale') ?? Intl.DateTimeFormat().resolvedOptions().locale;
|
|
763
837
|
const formatter = new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'numeric', day: 'numeric' });
|
|
764
838
|
const [y, m, d] = content.split('-').map(Number);
|
|
765
|
-
this.
|
|
839
|
+
this.replaceChildren(formatter.format(new Date(y, m - 1, d)));
|
|
766
840
|
}
|
|
767
841
|
}
|
|
768
842
|
|
|
769
843
|
class Instant extends index_mjs.ParsedElement {
|
|
770
844
|
render() {
|
|
771
|
-
const content = this.
|
|
845
|
+
const content = this.textContent.trim();
|
|
772
846
|
if (content === '') {
|
|
773
|
-
this.
|
|
847
|
+
this.replaceChildren(this.getAttribute('default') ?? '');
|
|
774
848
|
return;
|
|
775
849
|
}
|
|
776
850
|
const locale = this.getAttribute('locale') ?? Intl.DateTimeFormat().resolvedOptions().locale;
|
|
@@ -783,7 +857,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
783
857
|
second: 'numeric',
|
|
784
858
|
hour12: false,
|
|
785
859
|
});
|
|
786
|
-
this.
|
|
860
|
+
this.replaceChildren(format.format(new Date(Instant.isoToLocal(content))));
|
|
787
861
|
}
|
|
788
862
|
static isoToLocal(iso) {
|
|
789
863
|
//this is so sad
|
|
@@ -796,16 +870,17 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
796
870
|
}
|
|
797
871
|
|
|
798
872
|
class InputLocalDate extends Input {
|
|
799
|
-
static observed = ['value', 'readonly:presence', 'required:presence', 'min', 'max', 'step'];
|
|
873
|
+
static observed = ['value', 'readonly:presence', 'required:presence', 'placeholder', 'min', 'max', 'step'];
|
|
800
874
|
_type() {
|
|
801
875
|
return 'date';
|
|
802
876
|
}
|
|
803
877
|
render(conf) {
|
|
804
878
|
const { observed } = conf;
|
|
805
879
|
super.render(conf);
|
|
880
|
+
//step first: on a time input min and max are snapped to its grid
|
|
881
|
+
this.step = observed.step;
|
|
806
882
|
this.min = observed.min;
|
|
807
883
|
this.max = observed.max;
|
|
808
|
-
this.step = observed.step;
|
|
809
884
|
}
|
|
810
885
|
get min() {
|
|
811
886
|
const v = this._input.min;
|
|
@@ -851,13 +926,14 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
851
926
|
case 'd':
|
|
852
927
|
r.setDate(r.getDate() + offset * sign);
|
|
853
928
|
break;
|
|
854
|
-
case 'm':
|
|
929
|
+
case 'm': {
|
|
855
930
|
const originalDay = r.getDate();
|
|
856
931
|
r.setMonth(r.getMonth() + offset * sign);
|
|
857
932
|
if (r.getDate() !== originalDay) {
|
|
858
933
|
r.setDate(0);
|
|
859
934
|
}
|
|
860
935
|
break;
|
|
936
|
+
}
|
|
861
937
|
case 'y':
|
|
862
938
|
r.setFullYear(r.getFullYear() + offset * sign);
|
|
863
939
|
break;
|
|
@@ -870,10 +946,62 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
870
946
|
_type() {
|
|
871
947
|
return 'time';
|
|
872
948
|
}
|
|
949
|
+
get min() {
|
|
950
|
+
const v = this._input.min;
|
|
951
|
+
return v === '' ? null : v;
|
|
952
|
+
}
|
|
953
|
+
set min(v) {
|
|
954
|
+
this._input.min = this.#fromNowOrOffset(v);
|
|
955
|
+
}
|
|
956
|
+
get max() {
|
|
957
|
+
const v = this._input.max;
|
|
958
|
+
return v === '' ? null : v;
|
|
959
|
+
}
|
|
960
|
+
set max(v) {
|
|
961
|
+
this._input.max = this.#fromNowOrOffset(v);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Resolves `now` and hour or minute offsets against the current time, wrapping
|
|
965
|
+
* around midnight. `m` is minutes here, unlike the date offsets of the parent where
|
|
966
|
+
* it is months: months mean nothing on a time. Anything else is passed through.
|
|
967
|
+
*/
|
|
968
|
+
#fromNowOrOffset(v) {
|
|
969
|
+
if (!v) {
|
|
970
|
+
return '';
|
|
971
|
+
}
|
|
972
|
+
const resolved = new Date();
|
|
973
|
+
if (v !== 'now') {
|
|
974
|
+
const re = /^([+-])(\d+)([hm])$/;
|
|
975
|
+
const match = re.exec(v);
|
|
976
|
+
if (!match) {
|
|
977
|
+
return v;
|
|
978
|
+
}
|
|
979
|
+
const sign = match[1] === '-' ? -1 : 1;
|
|
980
|
+
const offset = +match[2] * sign;
|
|
981
|
+
if (match[3] === 'h') {
|
|
982
|
+
resolved.setHours(resolved.getHours() + offset);
|
|
983
|
+
} else {
|
|
984
|
+
resolved.setMinutes(resolved.getMinutes() + offset);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
return InputLocalTime.#snapped(resolved, Number(this._input.step) || 60);
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Truncates a time to the step grid: min anchors that grid, so a bound that is not
|
|
991
|
+
* on it makes every value on it invalid.
|
|
992
|
+
*/
|
|
993
|
+
static #snapped(date, stepSeconds) {
|
|
994
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
995
|
+
const seconds = date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds();
|
|
996
|
+
const snapped = Math.floor(seconds / stepSeconds) * stepSeconds;
|
|
997
|
+
const hh = pad(Math.floor(snapped / 3600));
|
|
998
|
+
const mm = pad(Math.floor((snapped % 3600) / 60));
|
|
999
|
+
return stepSeconds % 60 === 0 ? `${hh}:${mm}` : `${hh}:${mm}:${pad(snapped % 60)}`;
|
|
1000
|
+
}
|
|
873
1001
|
}
|
|
874
1002
|
|
|
875
1003
|
class InputInstant extends Input {
|
|
876
|
-
static observed = ['value', 'readonly:presence', 'required:presence', 'min', 'max', 'step'];
|
|
1004
|
+
static observed = ['value', 'readonly:presence', 'required:presence', 'placeholder', 'min', 'max', 'step'];
|
|
877
1005
|
_type() {
|
|
878
1006
|
return 'datetime-local';
|
|
879
1007
|
}
|
|
@@ -918,28 +1046,28 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
918
1046
|
static l10n = {
|
|
919
1047
|
en: {
|
|
920
1048
|
dropzonelabel: 'Click or drop your files here',
|
|
921
|
-
|
|
1049
|
+
unacceptablefiletype: 'Only files of type {0} are supported',
|
|
922
1050
|
maxfilesizeexceeded: 'Maximum supported file size is {0}',
|
|
923
1051
|
maxtotalsizeexceeded: 'Maximum supported total file size is {0}',
|
|
924
1052
|
maxfilesexceeded: 'Maximum files count exceeded',
|
|
925
1053
|
},
|
|
926
1054
|
it: {
|
|
927
1055
|
dropzonelabel: 'Clicca o trascina i file qui',
|
|
928
|
-
|
|
1056
|
+
unacceptablefiletype: 'Solo i file di tipo {0} sono supportati',
|
|
929
1057
|
maxfilesizeexceeded: 'La dimensione massima di un file è di {0}',
|
|
930
1058
|
maxtotalsizeexceeded: 'La dimensione massima complessiva dei file è di {0}',
|
|
931
1059
|
maxfilesexceeded: 'Numero massimo di file superato',
|
|
932
1060
|
},
|
|
933
1061
|
es: {
|
|
934
1062
|
dropzonelabel: 'Haz clic o arrastra tus archivos aquí',
|
|
935
|
-
|
|
1063
|
+
unacceptablefiletype: 'Solo se admiten archivos de tipo {0}',
|
|
936
1064
|
maxfilesizeexceeded: 'El tamaño máximo de archivo admitido es {0}',
|
|
937
1065
|
maxtotalsizeexceeded: 'El tamaño total máximo admitido es {0}',
|
|
938
1066
|
maxfilesexceeded: 'Se ha superado el número máximo de archivos',
|
|
939
1067
|
},
|
|
940
1068
|
fr: {
|
|
941
1069
|
dropzonelabel: 'Cliquez ou déposez vos fichiers ici',
|
|
942
|
-
|
|
1070
|
+
unacceptablefiletype: 'Seuls les fichiers de type {0} sont pris en charge',
|
|
943
1071
|
maxfilesizeexceeded: 'La taille maximale de fichier prise en charge est {0}',
|
|
944
1072
|
maxtotalsizeexceeded: 'La taille totale maximale prise en charge est {0}',
|
|
945
1073
|
maxfilesexceeded: 'Nombre maximal de fichiers dépassé',
|
|
@@ -949,6 +1077,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
949
1077
|
'value',
|
|
950
1078
|
'readonly:presence',
|
|
951
1079
|
'required:presence',
|
|
1080
|
+
'placeholder',
|
|
952
1081
|
'accept:csv',
|
|
953
1082
|
'multiple:presence',
|
|
954
1083
|
'itemlist:presence',
|
|
@@ -998,7 +1127,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
998
1127
|
};
|
|
999
1128
|
render(conf) {
|
|
1000
1129
|
const { observed } = conf;
|
|
1001
|
-
super.render(conf);
|
|
1130
|
+
super.render({ ...conf, skipObservedSetup: true });
|
|
1002
1131
|
this.#items = this.querySelector('ful-item-list');
|
|
1003
1132
|
this.#dropzone = this.querySelector('[data-ref=dropzone]');
|
|
1004
1133
|
this.#warnings = this.querySelector('ful-field-warnings');
|
|
@@ -1009,6 +1138,12 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1009
1138
|
this.maxfiles = observed.maxfiles;
|
|
1010
1139
|
this.maxfilesize = observed.maxfilesize;
|
|
1011
1140
|
this.maxtotalsize = observed.maxtotalsize;
|
|
1141
|
+
|
|
1142
|
+
this.disabled = conf.disabled;
|
|
1143
|
+
this.readonly = observed.readonly;
|
|
1144
|
+
this.required = observed.required;
|
|
1145
|
+
this.placeholder = observed.placeholder;
|
|
1146
|
+
this.value = observed.value;
|
|
1012
1147
|
this.#warnings.addEventListener('animationend', (e) => {
|
|
1013
1148
|
e.target.remove();
|
|
1014
1149
|
});
|
|
@@ -1016,11 +1151,17 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1016
1151
|
if (!e.target.closest('button')) {
|
|
1017
1152
|
return;
|
|
1018
1153
|
}
|
|
1019
|
-
const
|
|
1154
|
+
const idx = [...this.#items.children].indexOf(e.target.closest('ful-item'));
|
|
1155
|
+
if (idx === -1) {
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1020
1158
|
const dt = new DataTransfer();
|
|
1021
|
-
[...this.files]
|
|
1159
|
+
[...this.files]
|
|
1160
|
+
.filter((f, i) => i !== idx)
|
|
1161
|
+
.forEach((f) => {
|
|
1162
|
+
dt.items.add(f);
|
|
1163
|
+
});
|
|
1022
1164
|
this.files = dt.files;
|
|
1023
|
-
this.#update();
|
|
1024
1165
|
});
|
|
1025
1166
|
this.#dropzone.addEventListener('click', (e) => {
|
|
1026
1167
|
this.querySelector('input')?.click();
|
|
@@ -1031,10 +1172,15 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1031
1172
|
});
|
|
1032
1173
|
this.#dropzone.addEventListener('drop', (e) => {
|
|
1033
1174
|
e.preventDefault();
|
|
1175
|
+
const dropped = [...e.dataTransfer.items].filter((i) => i.kind === 'file');
|
|
1176
|
+
if (dropped.length === 0) {
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1034
1179
|
const dt = new DataTransfer();
|
|
1035
|
-
|
|
1180
|
+
dropped.forEach((i) => {
|
|
1181
|
+
dt.items.add(i.getAsFile());
|
|
1182
|
+
});
|
|
1036
1183
|
this.files = dt.files;
|
|
1037
|
-
this.#update();
|
|
1038
1184
|
});
|
|
1039
1185
|
this._input.addEventListener('change', (e) => {
|
|
1040
1186
|
this.#update();
|
|
@@ -1049,6 +1195,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1049
1195
|
}
|
|
1050
1196
|
#update() {
|
|
1051
1197
|
this.setCustomValidity();
|
|
1198
|
+
this.#warnings.replaceChildren();
|
|
1052
1199
|
this.#ensureAcceptable();
|
|
1053
1200
|
this.#ensureFileSizes();
|
|
1054
1201
|
this.#ensureTotalSize();
|
|
@@ -1059,7 +1206,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1059
1206
|
.renderTo(this.#items);
|
|
1060
1207
|
}
|
|
1061
1208
|
warning(key, args) {
|
|
1062
|
-
this.template('warning').withOverlay({ key, args }).
|
|
1209
|
+
this.template('warning').withOverlay({ key, args }).appendTo(this.#warnings);
|
|
1063
1210
|
}
|
|
1064
1211
|
#ensureAcceptable() {
|
|
1065
1212
|
if (!this.#accept.length) {
|
|
@@ -1072,10 +1219,14 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1072
1219
|
if (unacceptable.length === 0) {
|
|
1073
1220
|
return;
|
|
1074
1221
|
}
|
|
1075
|
-
this.warning('
|
|
1222
|
+
this.warning('unacceptablefiletype', this.#accept.join(', '));
|
|
1076
1223
|
const dt = new DataTransfer();
|
|
1077
|
-
[...this.files]
|
|
1078
|
-
|
|
1224
|
+
[...this.files]
|
|
1225
|
+
.filter((f) => !unacceptable.includes(f))
|
|
1226
|
+
.forEach((f) => {
|
|
1227
|
+
dt.items.add(f);
|
|
1228
|
+
});
|
|
1229
|
+
this._input.files = dt.files;
|
|
1079
1230
|
}
|
|
1080
1231
|
#ensureFilesCount() {
|
|
1081
1232
|
if (this.#maxfiles === null) {
|
|
@@ -1085,8 +1236,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1085
1236
|
return;
|
|
1086
1237
|
}
|
|
1087
1238
|
this.warning('maxfilesexceeded');
|
|
1088
|
-
|
|
1089
|
-
this.files = dt.files;
|
|
1239
|
+
this._input.files = new DataTransfer().files;
|
|
1090
1240
|
}
|
|
1091
1241
|
|
|
1092
1242
|
#ensureFileSizes() {
|
|
@@ -1099,8 +1249,12 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1099
1249
|
}
|
|
1100
1250
|
this.warning('maxfilesizeexceeded', this.#formatByteSize(this.#maxfilesize));
|
|
1101
1251
|
const dt = new DataTransfer();
|
|
1102
|
-
[...this.files]
|
|
1103
|
-
|
|
1252
|
+
[...this.files]
|
|
1253
|
+
.filter((f) => !oversized.includes(f))
|
|
1254
|
+
.forEach((f) => {
|
|
1255
|
+
dt.items.add(f);
|
|
1256
|
+
});
|
|
1257
|
+
this._input.files = dt.files;
|
|
1104
1258
|
}
|
|
1105
1259
|
#ensureTotalSize() {
|
|
1106
1260
|
if (this.#maxtotalsize === null) {
|
|
@@ -1111,7 +1265,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1111
1265
|
return;
|
|
1112
1266
|
}
|
|
1113
1267
|
this.warning('maxtotalsizeexceeded', this.#formatByteSize(this.#maxtotalsize));
|
|
1114
|
-
this.files = new DataTransfer().files;
|
|
1268
|
+
this._input.files = new DataTransfer().files;
|
|
1115
1269
|
}
|
|
1116
1270
|
get accept() {
|
|
1117
1271
|
return this.#accept;
|
|
@@ -1137,6 +1291,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1137
1291
|
}
|
|
1138
1292
|
set files(vs) {
|
|
1139
1293
|
this._input.files = vs;
|
|
1294
|
+
this.#update();
|
|
1140
1295
|
}
|
|
1141
1296
|
get file() {
|
|
1142
1297
|
return this.files[0] ?? null;
|
|
@@ -1157,7 +1312,6 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1157
1312
|
return;
|
|
1158
1313
|
}
|
|
1159
1314
|
this.files = new DataTransfer().files;
|
|
1160
|
-
this.#update();
|
|
1161
1315
|
}
|
|
1162
1316
|
get totalsize() {
|
|
1163
1317
|
return Array.from(this.files).reduce((a, f) => a + f.size, 0);
|
|
@@ -1324,7 +1478,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1324
1478
|
const http = index_mjs.registry.component('http-client');
|
|
1325
1479
|
const responseMapper = SelectLoader.#responseMapperFrom(el);
|
|
1326
1480
|
|
|
1327
|
-
if ('chunked'
|
|
1481
|
+
if ('chunked' === el.getAttribute('mode')) {
|
|
1328
1482
|
return new PartialRemoteLoader({
|
|
1329
1483
|
http,
|
|
1330
1484
|
url: el.getAttribute('src'),
|
|
@@ -1400,8 +1554,14 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1400
1554
|
});
|
|
1401
1555
|
this.replaceChildren(fragment);
|
|
1402
1556
|
}
|
|
1557
|
+
#selected() {
|
|
1558
|
+
return this.#menu?.querySelector('[selected]') ?? this.#menu?.firstElementChild ?? null;
|
|
1559
|
+
}
|
|
1403
1560
|
acceptSelection() {
|
|
1404
|
-
const selected = this.#
|
|
1561
|
+
const selected = this.#selected();
|
|
1562
|
+
if (!selected) {
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1405
1565
|
this.#change(selected);
|
|
1406
1566
|
}
|
|
1407
1567
|
update(values) {
|
|
@@ -1437,6 +1597,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1437
1597
|
try {
|
|
1438
1598
|
const data = await loader();
|
|
1439
1599
|
this.update(data);
|
|
1600
|
+
} catch (e) {
|
|
1601
|
+
this.hide();
|
|
1602
|
+
throw e;
|
|
1440
1603
|
} finally {
|
|
1441
1604
|
this.#spinner.setAttribute('hidden', '');
|
|
1442
1605
|
this.#menu.removeAttribute('hidden');
|
|
@@ -1444,9 +1607,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1444
1607
|
}
|
|
1445
1608
|
async moveOrShow(forward, loader) {
|
|
1446
1609
|
if (this.shown) {
|
|
1447
|
-
const selected = this.#
|
|
1448
|
-
const candidate = selected[`${forward ? 'next' : 'previous'}ElementSibling`];
|
|
1449
|
-
if (candidate) {
|
|
1610
|
+
const selected = this.#selected();
|
|
1611
|
+
const candidate = selected?.[`${forward ? 'next' : 'previous'}ElementSibling`];
|
|
1612
|
+
if (selected && candidate) {
|
|
1450
1613
|
selected.removeAttribute('selected');
|
|
1451
1614
|
candidate.setAttribute('selected', '');
|
|
1452
1615
|
candidate.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
@@ -1499,6 +1662,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1499
1662
|
#multiple;
|
|
1500
1663
|
#fieldError;
|
|
1501
1664
|
#values = new Map();
|
|
1665
|
+
#token = 0;
|
|
1502
1666
|
constructor() {
|
|
1503
1667
|
super();
|
|
1504
1668
|
this.internals = this.attachInternals();
|
|
@@ -1511,7 +1675,11 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1511
1675
|
.create(this, { options: slots.options });
|
|
1512
1676
|
|
|
1513
1677
|
this.#multiple = this.hasAttribute('multiple');
|
|
1514
|
-
|
|
1678
|
+
try {
|
|
1679
|
+
await this.#loader.prefetch?.();
|
|
1680
|
+
} catch (/** @type any */ e) {
|
|
1681
|
+
console.warn('failed to prefetch select options', this, 'reason:', e);
|
|
1682
|
+
}
|
|
1515
1683
|
const fragment = this.template().withOverlay({ slots, name }).render();
|
|
1516
1684
|
this.#input = fragment.querySelector('input');
|
|
1517
1685
|
this.#items = fragment.querySelector('ful-item-list');
|
|
@@ -1530,11 +1698,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1530
1698
|
this.#fieldError = fragment.querySelector('ful-field-error');
|
|
1531
1699
|
this.#input.ariaDescribedByElements = [this.#fieldError];
|
|
1532
1700
|
this.#input.ariaLabelledByElements = [label];
|
|
1533
|
-
|
|
1534
|
-
const self = this;
|
|
1535
1701
|
const [dload, abortdload] = Timing.throttle(400, () => {
|
|
1536
|
-
|
|
1537
|
-
|
|
1702
|
+
this.#input.setAttribute('aria-expanded', 'true');
|
|
1703
|
+
this.#ddmenu.show(() => this.#loader.load(this.#input.value));
|
|
1538
1704
|
});
|
|
1539
1705
|
this.addEventListener('click', (/** @type any */ e) => {
|
|
1540
1706
|
if (e.target.matches('input')) {
|
|
@@ -1601,13 +1767,13 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1601
1767
|
case 'ArrowUp': {
|
|
1602
1768
|
e.preventDefault();
|
|
1603
1769
|
this.#input.setAttribute('aria-expanded', 'true');
|
|
1604
|
-
this.#ddmenu.moveOrShow(false, () =>
|
|
1770
|
+
this.#ddmenu.moveOrShow(false, () => this.#loader.load(this.#input.value));
|
|
1605
1771
|
break;
|
|
1606
1772
|
}
|
|
1607
1773
|
case 'ArrowDown': {
|
|
1608
1774
|
e.preventDefault();
|
|
1609
1775
|
this.#input.setAttribute('aria-expanded', 'true');
|
|
1610
|
-
this.#ddmenu.moveOrShow(true, () =>
|
|
1776
|
+
this.#ddmenu.moveOrShow(true, () => this.#loader.load(this.#input.value));
|
|
1611
1777
|
break;
|
|
1612
1778
|
}
|
|
1613
1779
|
case 'Escape': {
|
|
@@ -1616,6 +1782,12 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1616
1782
|
break;
|
|
1617
1783
|
}
|
|
1618
1784
|
case 'Enter': {
|
|
1785
|
+
if (!this.#ddmenu.shown) {
|
|
1786
|
+
//nothing to accept: submit the form as ful-input does. the inner
|
|
1787
|
+
//input carries form="" so it never submits one on its own
|
|
1788
|
+
this.#requestSubmit();
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1619
1791
|
e.preventDefault();
|
|
1620
1792
|
this.#input.setAttribute('aria-expanded', 'false');
|
|
1621
1793
|
this.#ddmenu.acceptSelection();
|
|
@@ -1664,6 +1836,16 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1664
1836
|
async withLoader(fn) {
|
|
1665
1837
|
return await fn(this.#loader);
|
|
1666
1838
|
}
|
|
1839
|
+
#requestSubmit() {
|
|
1840
|
+
const form = this.internals.form;
|
|
1841
|
+
if (!form) {
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
const candidates = /** @type [HTMLButtonElement|HTMLInputElement] */ (
|
|
1845
|
+
Array.from(form.querySelectorAll('button:not(:disabled), input:not(:disabled)'))
|
|
1846
|
+
);
|
|
1847
|
+
form.requestSubmit(candidates.find((el) => el.type === 'submit'));
|
|
1848
|
+
}
|
|
1667
1849
|
#changed() {
|
|
1668
1850
|
const selection = [...this.#values.entries()].map((e) => ({
|
|
1669
1851
|
key: e[0],
|
|
@@ -1693,16 +1875,43 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1693
1875
|
this.template('items').withOverlay({ entries: this.#values.entries() }).renderTo(this.#items);
|
|
1694
1876
|
}
|
|
1695
1877
|
set value(vs) {
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1878
|
+
//the csvm mapper yields [] for a missing multiple value, an empty string is
|
|
1879
|
+
//left alone: it is a usable key for an <option value="">
|
|
1880
|
+
const keys = vs == null ? [] : Array.isArray(vs) ? vs : [vs];
|
|
1881
|
+
//the keys are known synchronously and are all `value` reads, so they are applied
|
|
1882
|
+
//now: only the labels need the loader, until then a key stands in for its own
|
|
1883
|
+
this.#values = new Map(keys.map((k) => [k, [k]]));
|
|
1884
|
+
this.#syncBadges();
|
|
1885
|
+
const token = ++this.#token;
|
|
1886
|
+
if (keys.length === 0) {
|
|
1699
1887
|
return;
|
|
1700
1888
|
}
|
|
1701
|
-
(
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1889
|
+
this.#resolve(keys, token);
|
|
1890
|
+
}
|
|
1891
|
+
/**
|
|
1892
|
+
* Resolves the labels of the assigned keys. A failed lookup is left to reject so
|
|
1893
|
+
* that it is reported like any other failure: the keys stay applied either way.
|
|
1894
|
+
*/
|
|
1895
|
+
async #resolve(keys, token) {
|
|
1896
|
+
const entries = await this.#loader.exact(...keys);
|
|
1897
|
+
if (token !== this.#token) {
|
|
1898
|
+
//a newer assignment has been made in the meantime
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
//label the keys that are still selected: a removal made while the lookup was in
|
|
1902
|
+
//flight must not be undone by it, and a key the loader does not know is dropped
|
|
1903
|
+
const resolved = new Map(entries.map((e) => [e[0], e.slice(1)]));
|
|
1904
|
+
for (const key of keys) {
|
|
1905
|
+
if (!this.#values.has(key)) {
|
|
1906
|
+
continue;
|
|
1907
|
+
}
|
|
1908
|
+
if (resolved.has(key)) {
|
|
1909
|
+
this.#values.set(key, resolved.get(key));
|
|
1910
|
+
} else {
|
|
1911
|
+
this.#values.delete(key);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
this.#syncBadges();
|
|
1706
1915
|
}
|
|
1707
1916
|
get value() {
|
|
1708
1917
|
if (this.#multiple) {
|
|
@@ -1721,6 +1930,11 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1721
1930
|
}
|
|
1722
1931
|
set disabled(d) {
|
|
1723
1932
|
index_mjs.Attributes.toggle(this.#input, 'disabled', d);
|
|
1933
|
+
//also on the host: a form associated element only matches :disabled through its
|
|
1934
|
+
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
1935
|
+
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
1936
|
+
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
1937
|
+
index_mjs.Attributes.toggle(this, 'disabled', d);
|
|
1724
1938
|
}
|
|
1725
1939
|
get readonly() {
|
|
1726
1940
|
return this.#input.readOnly;
|
|
@@ -1826,7 +2040,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1826
2040
|
return [input, label];
|
|
1827
2041
|
});
|
|
1828
2042
|
|
|
1829
|
-
radioEls.forEach((el) =>
|
|
2043
|
+
radioEls.forEach((el) => {
|
|
2044
|
+
el.remove();
|
|
2045
|
+
});
|
|
1830
2046
|
this.template().withOverlay({ name, slots, inputsAndLabels }).renderTo(this);
|
|
1831
2047
|
this.#fieldset = this.firstElementChild;
|
|
1832
2048
|
this.disabled = disabled;
|
|
@@ -1870,6 +2086,11 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1870
2086
|
}
|
|
1871
2087
|
set disabled(d) {
|
|
1872
2088
|
index_mjs.Attributes.toggle(this.#fieldset, 'disabled', d);
|
|
2089
|
+
//also on the host: a form associated element only matches :disabled through its
|
|
2090
|
+
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
2091
|
+
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
2092
|
+
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
2093
|
+
index_mjs.Attributes.toggle(this, 'disabled', d);
|
|
1873
2094
|
}
|
|
1874
2095
|
get required() {
|
|
1875
2096
|
return this.#fieldset.getAttribute('aria-required') === 'true';
|
|
@@ -1919,7 +2140,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1919
2140
|
this.internals.role = 'presentation';
|
|
1920
2141
|
}
|
|
1921
2142
|
render({ slots, observed, disabled }) {
|
|
1922
|
-
const isSwitch = this.getAttribute('type')
|
|
2143
|
+
const isSwitch = this.getAttribute('type') === 'switch';
|
|
1923
2144
|
const klass = isSwitch ? 'form-check form-switch' : 'form-check';
|
|
1924
2145
|
const fragment = this.template().withOverlay({ slots, klass, isSwitch }).render();
|
|
1925
2146
|
this.#container = fragment.firstElementChild;
|
|
@@ -1983,6 +2204,11 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1983
2204
|
}
|
|
1984
2205
|
set disabled(d) {
|
|
1985
2206
|
index_mjs.Attributes.toggle(this.#input, 'disabled', d);
|
|
2207
|
+
//also on the host: a form associated element only matches :disabled through its
|
|
2208
|
+
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
2209
|
+
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
2210
|
+
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
2211
|
+
index_mjs.Attributes.toggle(this, 'disabled', d);
|
|
1986
2212
|
}
|
|
1987
2213
|
get required() {
|
|
1988
2214
|
return this.#input.getAttribute('aria-required') === 'true';
|
|
@@ -2023,9 +2249,10 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2023
2249
|
class SortButton extends index_mjs.ParsedElement {
|
|
2024
2250
|
static observed = ['order'];
|
|
2025
2251
|
#order;
|
|
2026
|
-
render() {
|
|
2252
|
+
render({ observed }) {
|
|
2027
2253
|
const sorter = this.getAttribute('sorter');
|
|
2028
2254
|
const orders = ['asc', 'desc', null];
|
|
2255
|
+
this.order = observed.order;
|
|
2029
2256
|
this.addEventListener('click', () => {
|
|
2030
2257
|
const nextOrder = orders[(orders.indexOf(this.order) + 1) % 3];
|
|
2031
2258
|
this.dispatchEvent(
|
|
@@ -2115,10 +2342,12 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2115
2342
|
#total = 0;
|
|
2116
2343
|
#current = 0;
|
|
2117
2344
|
render({ observed }) {
|
|
2118
|
-
this.
|
|
2345
|
+
this.total = observed.total ?? 0;
|
|
2346
|
+
this.current = observed.current ?? 0;
|
|
2119
2347
|
this.addEventListener('click', (/** @type any */ evt) => {
|
|
2120
2348
|
const el = evt.target.closest('a');
|
|
2121
|
-
if (!el) {
|
|
2349
|
+
if (!el || el.classList.contains('disabled')) {
|
|
2350
|
+
//a disabled link leads nowhere: the page it would ask for does not exist
|
|
2122
2351
|
return;
|
|
2123
2352
|
}
|
|
2124
2353
|
this.dispatchEvent(
|
|
@@ -2134,25 +2363,20 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2134
2363
|
}
|
|
2135
2364
|
update(current, total) {
|
|
2136
2365
|
const maxRender = Number(this.getAttribute('pages') ?? '5');
|
|
2137
|
-
const
|
|
2366
|
+
const hasPrev = current > 0;
|
|
2367
|
+
const hasNext = current + 1 < total;
|
|
2368
|
+
//a disabled arrow carries no page: there is nothing valid for it to point at
|
|
2369
|
+
const prev = { index: hasPrev ? current - 1 : null, enabled: hasPrev };
|
|
2138
2370
|
const curr = { index: current, label: current + 1 };
|
|
2139
|
-
const next = { index:
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
if (p >= 0) {
|
|
2149
|
-
pages.unshift({ index: p, label: p + 1 });
|
|
2150
|
-
}
|
|
2151
|
-
const n = mid + offset;
|
|
2152
|
-
if (n < total) {
|
|
2153
|
-
pages.push({ index: n, label: n + 1 });
|
|
2154
|
-
}
|
|
2155
|
-
}
|
|
2371
|
+
const next = { index: hasNext ? current + 1 : null, enabled: hasNext };
|
|
2372
|
+
//the window holds at most maxRender pages, centered on the current one and slid
|
|
2373
|
+
//back towards the end so it stays full on the last pages
|
|
2374
|
+
const rendered = Math.max(1, Math.min(maxRender, total));
|
|
2375
|
+
const first = Math.max(0, Math.min(current - Math.floor((rendered - 1) / 2), total - rendered));
|
|
2376
|
+
const pages = Array.from({ length: rendered }, (_, offset) => ({
|
|
2377
|
+
index: first + offset,
|
|
2378
|
+
label: first + offset + 1,
|
|
2379
|
+
}));
|
|
2156
2380
|
this.template().withOverlay({ total, prev, curr, next, pages }).renderTo(this);
|
|
2157
2381
|
}
|
|
2158
2382
|
get total() {
|
|
@@ -2179,9 +2403,10 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2179
2403
|
|
|
2180
2404
|
class TableSchemaParser {
|
|
2181
2405
|
static parse(nodeOrFragment, template) {
|
|
2182
|
-
|
|
2406
|
+
//nodeOrFragment is undefined when the slot is missing altogether
|
|
2407
|
+
const schema = nodeOrFragment ? index_mjs.Nodes.queryChildren(nodeOrFragment, 'schema') : null;
|
|
2183
2408
|
if (!schema) {
|
|
2184
|
-
throw new Error(
|
|
2409
|
+
throw new Error('missing expected <schema>: ful-table needs a <template slot="schema"> holding one');
|
|
2185
2410
|
}
|
|
2186
2411
|
const headersTr = document.createElement('tr');
|
|
2187
2412
|
const rowsTr = document.createElement('tr');
|
|
@@ -2400,9 +2625,10 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2400
2625
|
this.#noAutoload = table.querySelector(':scope > tbody[data-ref=initial]');
|
|
2401
2626
|
this.#feedback = table.querySelector(':scope > tbody[data-ref=feedback]');
|
|
2402
2627
|
this.#paginator = index_mjs.Nodes.queryChildren(fragment, 'ful-pagination');
|
|
2403
|
-
this.#sorters = table.querySelectorAll(':scope > thead ful-sorter') ?? [];
|
|
2404
2628
|
this.replaceChildren(fragment);
|
|
2405
|
-
|
|
2629
|
+
const thead = /** @type HTMLTableSectionElement */ (this.querySelector('thead'));
|
|
2630
|
+
schema.headersTemplate.renderTo(thead);
|
|
2631
|
+
this.#sorters = thead.querySelectorAll('ful-sorter');
|
|
2406
2632
|
await index_mjs.Rendering.waitForChildren(this);
|
|
2407
2633
|
|
|
2408
2634
|
const maybeForm = /** @type any */ (index_mjs.Nodes.queryChildren(this, 'ful-form'));
|
|
@@ -2437,11 +2663,16 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2437
2663
|
this.addEventListener('sort-requested', async (/** @type any */ e) => {
|
|
2438
2664
|
const sortRequest = e.detail.value.order ? e.detail.value : null;
|
|
2439
2665
|
await this.load(this.#latestRequest.pageRequest, sortRequest, this.#latestRequest.filterRequest);
|
|
2440
|
-
this.#sorters.forEach((s) =>
|
|
2666
|
+
this.#sorters.forEach((s) => {
|
|
2667
|
+
s.order = null;
|
|
2668
|
+
});
|
|
2441
2669
|
e.target.order = e.detail.value.order;
|
|
2442
2670
|
});
|
|
2443
2671
|
if (this.hasAttribute('autoload')) {
|
|
2444
|
-
|
|
2672
|
+
//not awaited: the first load must not hold up the upgrade, and a loader that
|
|
2673
|
+
//fails or never answers must not keep ftl:ready from firing for the page.
|
|
2674
|
+
//load renders its own error state and lets the failure reject, so it is reported
|
|
2675
|
+
this.reload();
|
|
2445
2676
|
}
|
|
2446
2677
|
}
|
|
2447
2678
|
|
|
@@ -2454,7 +2685,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2454
2685
|
}
|
|
2455
2686
|
async load(pageRequest, sortRequest, filterRequest) {
|
|
2456
2687
|
this.#body.replaceChildren();
|
|
2457
|
-
this.#loading.removeAttribute('hidden'
|
|
2688
|
+
this.#loading.removeAttribute('hidden');
|
|
2458
2689
|
this.#feedback.setAttribute('hidden', '');
|
|
2459
2690
|
this.#noAutoload.setAttribute('hidden', '');
|
|
2460
2691
|
try {
|
|
@@ -2463,7 +2694,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2463
2694
|
this.#update(pageRequest, sortRequest, filterRequest, pageResponse);
|
|
2464
2695
|
} catch (/** @type any */ error) {
|
|
2465
2696
|
this.#loading.setAttribute('hidden', '');
|
|
2466
|
-
this.#feedback.removeAttribute('hidden'
|
|
2697
|
+
this.#feedback.removeAttribute('hidden');
|
|
2467
2698
|
if (!error.problems) {
|
|
2468
2699
|
this.#feedback.querySelector('[data-ref=feedback-error]').textContent = error;
|
|
2469
2700
|
} else {
|
|
@@ -2505,7 +2736,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2505
2736
|
}
|
|
2506
2737
|
|
|
2507
2738
|
class InstantFilter extends Input {
|
|
2508
|
-
static observed = ['value:json', 'readonly:presence', 'required:presence'];
|
|
2739
|
+
static observed = ['value:json', 'readonly:presence', 'required:presence', 'placeholder'];
|
|
2509
2740
|
static template = `
|
|
2510
2741
|
<div class="form-label">
|
|
2511
2742
|
<label>{{{{ slots.default }}}}</label>
|
|
@@ -2539,10 +2770,16 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2539
2770
|
this.#operator = this.querySelector('[data-ref=operator]');
|
|
2540
2771
|
this.#value1 = this.querySelector('[data-ref=value1]');
|
|
2541
2772
|
this.#value2 = this.querySelector('[data-ref=value2]');
|
|
2773
|
+
//Input.render only re-dispatches changes coming from the first operand
|
|
2774
|
+
this.#value2.addEventListener('change', (evt) => {
|
|
2775
|
+
evt.stopPropagation();
|
|
2776
|
+
this.#notifyChange();
|
|
2777
|
+
});
|
|
2542
2778
|
|
|
2543
2779
|
this.disabled = conf.disabled;
|
|
2544
2780
|
this.readonly = conf.observed.readonly;
|
|
2545
2781
|
this.required = conf.observed.required;
|
|
2782
|
+
this.placeholder = conf.observed.placeholder;
|
|
2546
2783
|
this.value = conf.observed.value;
|
|
2547
2784
|
|
|
2548
2785
|
this.addEventListener('click', (evt) => {
|
|
@@ -2552,9 +2789,13 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2552
2789
|
}
|
|
2553
2790
|
const btn = /** @type HTMLButtonElement */ (target.closest('ul')?.previousElementSibling);
|
|
2554
2791
|
const value = /** @type String */ (target.getAttribute('value'));
|
|
2792
|
+
const previous = btn.getAttribute('value');
|
|
2555
2793
|
index_mjs.Attributes.toggle(this.#value2, 'hidden', value !== 'BETWEEN');
|
|
2556
2794
|
btn.setAttribute('value', value);
|
|
2557
2795
|
btn.innerHTML = target.innerHTML;
|
|
2796
|
+
if (previous !== value) {
|
|
2797
|
+
this.#notifyChange();
|
|
2798
|
+
}
|
|
2558
2799
|
});
|
|
2559
2800
|
}
|
|
2560
2801
|
|
|
@@ -2570,14 +2811,40 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2570
2811
|
return;
|
|
2571
2812
|
}
|
|
2572
2813
|
const [operator, ...values] = v;
|
|
2573
|
-
this.#
|
|
2814
|
+
this.#showOperator(operator);
|
|
2574
2815
|
this.#value1.value = values[0] ? Instant.isoToLocal(values[0]) : values[0];
|
|
2575
2816
|
this.#value2.value = values[1] ? Instant.isoToLocal(values[1]) : values[1];
|
|
2576
2817
|
}
|
|
2818
|
+
#showOperator(operator) {
|
|
2819
|
+
this.#operator.setAttribute('value', operator);
|
|
2820
|
+
const items = Array.from(this.#operator.nextElementSibling?.querySelectorAll('li > a[value]') ?? []);
|
|
2821
|
+
const item = items.find((a) => a.getAttribute('value') === operator);
|
|
2822
|
+
if (item) {
|
|
2823
|
+
this.#operator.innerHTML = item.innerHTML;
|
|
2824
|
+
}
|
|
2825
|
+
index_mjs.Attributes.toggle(this.#value2, 'hidden', operator !== 'BETWEEN');
|
|
2826
|
+
}
|
|
2827
|
+
#notifyChange() {
|
|
2828
|
+
this.dispatchEvent(
|
|
2829
|
+
new CustomEvent('change', {
|
|
2830
|
+
bubbles: true,
|
|
2831
|
+
cancelable: false,
|
|
2832
|
+
detail: {
|
|
2833
|
+
value: this.value,
|
|
2834
|
+
},
|
|
2835
|
+
}),
|
|
2836
|
+
);
|
|
2837
|
+
}
|
|
2838
|
+
get readonly() {
|
|
2839
|
+
return super.readonly;
|
|
2840
|
+
}
|
|
2577
2841
|
set readonly(v) {
|
|
2578
2842
|
this.#value2.readOnly = v;
|
|
2579
2843
|
super.readonly = v;
|
|
2580
2844
|
}
|
|
2845
|
+
get disabled() {
|
|
2846
|
+
return super.disabled;
|
|
2847
|
+
}
|
|
2581
2848
|
set disabled(d) {
|
|
2582
2849
|
index_mjs.Attributes.toggle(this.#value2, 'disabled', d);
|
|
2583
2850
|
super.disabled = d;
|
|
@@ -2585,7 +2852,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2585
2852
|
}
|
|
2586
2853
|
|
|
2587
2854
|
class LocalDateFilter extends Input {
|
|
2588
|
-
static observed = ['value:json', 'readonly:presence', 'required:presence'];
|
|
2855
|
+
static observed = ['value:json', 'readonly:presence', 'required:presence', 'placeholder'];
|
|
2589
2856
|
static template = `
|
|
2590
2857
|
<div class="form-label">
|
|
2591
2858
|
<label>{{{{ slots.default }}}}</label>
|
|
@@ -2620,10 +2887,16 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2620
2887
|
this.#operator = this.querySelector('[data-ref=operator]');
|
|
2621
2888
|
this.#value1 = this.querySelector('[data-ref=value1]');
|
|
2622
2889
|
this.#value2 = this.querySelector('[data-ref=value2]');
|
|
2890
|
+
//Input.render only re-dispatches changes coming from the first operand
|
|
2891
|
+
this.#value2.addEventListener('change', (evt) => {
|
|
2892
|
+
evt.stopPropagation();
|
|
2893
|
+
this.#notifyChange();
|
|
2894
|
+
});
|
|
2623
2895
|
|
|
2624
2896
|
this.disabled = conf.disabled;
|
|
2625
2897
|
this.readonly = conf.observed.readonly;
|
|
2626
2898
|
this.required = conf.observed.required;
|
|
2899
|
+
this.placeholder = conf.observed.placeholder;
|
|
2627
2900
|
this.value = conf.observed.value;
|
|
2628
2901
|
|
|
2629
2902
|
this.addEventListener('click', (evt) => {
|
|
@@ -2633,14 +2906,18 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2633
2906
|
}
|
|
2634
2907
|
const btn = /** @type HTMLButtonElement */ (target.closest('ul')?.previousElementSibling);
|
|
2635
2908
|
const value = /** @type String */ (target.getAttribute('value'));
|
|
2909
|
+
const previous = btn.getAttribute('value');
|
|
2636
2910
|
index_mjs.Attributes.toggle(this.#value2, 'hidden', value !== 'BETWEEN');
|
|
2637
2911
|
btn.setAttribute('value', value);
|
|
2638
2912
|
btn.innerHTML = target.innerHTML;
|
|
2913
|
+
if (previous !== value) {
|
|
2914
|
+
this.#notifyChange();
|
|
2915
|
+
}
|
|
2639
2916
|
});
|
|
2640
2917
|
}
|
|
2641
2918
|
get value() {
|
|
2642
2919
|
const operator = this.#operator.getAttribute('value');
|
|
2643
|
-
const values = operator
|
|
2920
|
+
const values = operator === 'BETWEEN' ? [this.#value1.value, this.#value2.value] : [this.#value1.value];
|
|
2644
2921
|
return values.some((v) => v === '') ? undefined : [operator, ...values];
|
|
2645
2922
|
}
|
|
2646
2923
|
set value(v) {
|
|
@@ -2650,14 +2927,40 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2650
2927
|
return;
|
|
2651
2928
|
}
|
|
2652
2929
|
const [operator, ...values] = v;
|
|
2653
|
-
this.#
|
|
2930
|
+
this.#showOperator(operator);
|
|
2654
2931
|
this.#value1.value = values[0];
|
|
2655
2932
|
this.#value2.value = values[1];
|
|
2656
2933
|
}
|
|
2934
|
+
#showOperator(operator) {
|
|
2935
|
+
this.#operator.setAttribute('value', operator);
|
|
2936
|
+
const items = Array.from(this.#operator.nextElementSibling?.querySelectorAll('li > a[value]') ?? []);
|
|
2937
|
+
const item = items.find((a) => a.getAttribute('value') === operator);
|
|
2938
|
+
if (item) {
|
|
2939
|
+
this.#operator.innerHTML = item.innerHTML;
|
|
2940
|
+
}
|
|
2941
|
+
index_mjs.Attributes.toggle(this.#value2, 'hidden', operator !== 'BETWEEN');
|
|
2942
|
+
}
|
|
2943
|
+
#notifyChange() {
|
|
2944
|
+
this.dispatchEvent(
|
|
2945
|
+
new CustomEvent('change', {
|
|
2946
|
+
bubbles: true,
|
|
2947
|
+
cancelable: false,
|
|
2948
|
+
detail: {
|
|
2949
|
+
value: this.value,
|
|
2950
|
+
},
|
|
2951
|
+
}),
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
get readonly() {
|
|
2955
|
+
return super.readonly;
|
|
2956
|
+
}
|
|
2657
2957
|
set readonly(v) {
|
|
2658
2958
|
this.#value2.readOnly = v;
|
|
2659
2959
|
super.readonly = v;
|
|
2660
2960
|
}
|
|
2961
|
+
get disabled() {
|
|
2962
|
+
return super.disabled;
|
|
2963
|
+
}
|
|
2661
2964
|
set disabled(d) {
|
|
2662
2965
|
index_mjs.Attributes.toggle(this.#value2, 'disabled', d);
|
|
2663
2966
|
super.disabled = d;
|
|
@@ -2665,7 +2968,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2665
2968
|
}
|
|
2666
2969
|
|
|
2667
2970
|
class TextFilter extends Input {
|
|
2668
|
-
static observed = ['value:json', 'readonly:presence', 'required:presence'];
|
|
2971
|
+
static observed = ['value:json', 'readonly:presence', 'required:presence', 'placeholder'];
|
|
2669
2972
|
static template = `
|
|
2670
2973
|
<div class="form-label">
|
|
2671
2974
|
<label>{{{{ slots.default }}}}</label>
|
|
@@ -2689,6 +2992,8 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2689
2992
|
`;
|
|
2690
2993
|
#operator;
|
|
2691
2994
|
#value;
|
|
2995
|
+
//the sensitivity has no control of its own: it is carried through from whoever set the value
|
|
2996
|
+
#sensitivity = 'IGNORE_CASE';
|
|
2692
2997
|
render(conf) {
|
|
2693
2998
|
super.render({ ...conf, skipObservedSetup: true });
|
|
2694
2999
|
|
|
@@ -2698,6 +3003,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2698
3003
|
this.disabled = conf.disabled;
|
|
2699
3004
|
this.readonly = conf.observed.readonly;
|
|
2700
3005
|
this.required = conf.observed.required;
|
|
3006
|
+
this.placeholder = conf.observed.placeholder;
|
|
2701
3007
|
this.value = conf.observed.value;
|
|
2702
3008
|
|
|
2703
3009
|
this.addEventListener('click', (evt) => {
|
|
@@ -2707,13 +3013,17 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2707
3013
|
}
|
|
2708
3014
|
const btn = /** @type HTMLButtonElement */ (target.closest('ul')?.previousElementSibling);
|
|
2709
3015
|
const value = /** @type String */ (target.getAttribute('value'));
|
|
3016
|
+
const previous = btn.getAttribute('value');
|
|
2710
3017
|
btn.setAttribute('value', value);
|
|
2711
3018
|
btn.innerHTML = target.innerHTML;
|
|
3019
|
+
if (previous !== value) {
|
|
3020
|
+
this.#notifyChange();
|
|
3021
|
+
}
|
|
2712
3022
|
});
|
|
2713
3023
|
}
|
|
2714
3024
|
get value() {
|
|
2715
3025
|
const operator = this.#operator.getAttribute('value');
|
|
2716
|
-
return this.#value.value === '' ? undefined : [operator,
|
|
3026
|
+
return this.#value.value === '' ? undefined : [operator, this.#sensitivity, this.#value.value];
|
|
2717
3027
|
}
|
|
2718
3028
|
set value(v) {
|
|
2719
3029
|
if (v == null) {
|
|
@@ -2721,15 +3031,35 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
2721
3031
|
return;
|
|
2722
3032
|
}
|
|
2723
3033
|
const [operator, sensitivity, value] = v;
|
|
2724
|
-
this.#
|
|
3034
|
+
this.#showOperator(operator);
|
|
3035
|
+
this.#sensitivity = sensitivity ?? 'IGNORE_CASE';
|
|
2725
3036
|
this.#value.value = value;
|
|
2726
3037
|
}
|
|
3038
|
+
#showOperator(operator) {
|
|
3039
|
+
this.#operator.setAttribute('value', operator);
|
|
3040
|
+
const items = Array.from(this.#operator.nextElementSibling?.querySelectorAll('li > a[value]') ?? []);
|
|
3041
|
+
const item = items.find((a) => a.getAttribute('value') === operator);
|
|
3042
|
+
if (item) {
|
|
3043
|
+
this.#operator.innerHTML = item.innerHTML;
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
#notifyChange() {
|
|
3047
|
+
this.dispatchEvent(
|
|
3048
|
+
new CustomEvent('change', {
|
|
3049
|
+
bubbles: true,
|
|
3050
|
+
cancelable: false,
|
|
3051
|
+
detail: {
|
|
3052
|
+
value: this.value,
|
|
3053
|
+
},
|
|
3054
|
+
}),
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
2727
3057
|
}
|
|
2728
3058
|
|
|
2729
3059
|
class LocalizationModule {
|
|
2730
3060
|
static t(k, ...args) {
|
|
2731
|
-
//@ts-
|
|
2732
|
-
const format = this.l10n?.[this.language]?.[k] ?? this.l10n?.
|
|
3061
|
+
//@ts-expect-error l10n and language come from the element class and the data stack
|
|
3062
|
+
const format = this.l10n?.[this.language]?.[k] ?? this.l10n?.en?.[k] ?? k;
|
|
2733
3063
|
if (args.length === 0) {
|
|
2734
3064
|
return format;
|
|
2735
3065
|
}
|