@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/fml.iife.js
CHANGED
|
@@ -3460,7 +3460,7 @@ var fml = (function (exports) {
|
|
|
3460
3460
|
case '<=':
|
|
3461
3461
|
return lhs <= rhs;
|
|
3462
3462
|
default:
|
|
3463
|
-
throw new Error(
|
|
3463
|
+
throw new Error(`unknown cmp op ${node.op}`);
|
|
3464
3464
|
}
|
|
3465
3465
|
}
|
|
3466
3466
|
[nodes.call](node) {
|
|
@@ -3471,7 +3471,7 @@ var fml = (function (exports) {
|
|
|
3471
3471
|
}
|
|
3472
3472
|
const fn = module[fnRef.value];
|
|
3473
3473
|
if (!fn) {
|
|
3474
|
-
throw new Error(`Function "#${fnRef.module === null ? '' : fnRef.module
|
|
3474
|
+
throw new Error(`Function "#${fnRef.module === null ? '' : `${fnRef.module}:`}${fnRef.value}" not found`);
|
|
3475
3475
|
}
|
|
3476
3476
|
const args = node.args.map((arg) => this.visit(arg));
|
|
3477
3477
|
return fn.apply(this.#resolve_proxy(), args);
|
|
@@ -3507,14 +3507,14 @@ var fml = (function (exports) {
|
|
|
3507
3507
|
return cond ? cond : this.visit(node.ifFalse);
|
|
3508
3508
|
}
|
|
3509
3509
|
[nodes.access](node) {
|
|
3510
|
-
let prev
|
|
3510
|
+
let prev ;
|
|
3511
3511
|
let cur = this.visit(node.lhs);
|
|
3512
3512
|
for (let i = 0; i !== node.rhs.length; ++i) {
|
|
3513
3513
|
const rhs = node.rhs[i];
|
|
3514
3514
|
if (rhs.ns && cur == null) {
|
|
3515
3515
|
return undefined;
|
|
3516
3516
|
}
|
|
3517
|
-
let value
|
|
3517
|
+
let value ;
|
|
3518
3518
|
switch (rhs.type) {
|
|
3519
3519
|
case nodes.member: {
|
|
3520
3520
|
value = cur[rhs.rhs];
|
|
@@ -3555,7 +3555,7 @@ var fml = (function (exports) {
|
|
|
3555
3555
|
case nodes.templated.ten:
|
|
3556
3556
|
return { type: nodes.dom.n, value: this.visit(node.value) };
|
|
3557
3557
|
default:
|
|
3558
|
-
throw new Error(
|
|
3558
|
+
throw new Error(`unknown node type ${node.type.toString()}`);
|
|
3559
3559
|
}
|
|
3560
3560
|
});
|
|
3561
3561
|
}
|
|
@@ -3731,7 +3731,7 @@ var fml = (function (exports) {
|
|
|
3731
3731
|
.forEach((a) => {
|
|
3732
3732
|
const target = a.substring(prefix.length);
|
|
3733
3733
|
if (target === 'class') {
|
|
3734
|
-
const classes = from.getAttribute(prefix
|
|
3734
|
+
const classes = from.getAttribute(`${prefix}class`)?.split(/\s+/).filter((a) => a.length) ?? [];
|
|
3735
3735
|
to.classList.add(...classes);
|
|
3736
3736
|
return;
|
|
3737
3737
|
}
|
|
@@ -3823,8 +3823,7 @@ var fml = (function (exports) {
|
|
|
3823
3823
|
* @returns
|
|
3824
3824
|
*/
|
|
3825
3825
|
static isParsed(el) {
|
|
3826
|
-
|
|
3827
|
-
for (let c = el; c; c = c.parentNode) {
|
|
3826
|
+
for (let c = /** @type {Node | null} */ (el); c; c = c.parentNode) {
|
|
3828
3827
|
if (c.nextSibling) {
|
|
3829
3828
|
return true;
|
|
3830
3829
|
}
|
|
@@ -3887,19 +3886,18 @@ var fml = (function (exports) {
|
|
|
3887
3886
|
}
|
|
3888
3887
|
|
|
3889
3888
|
class NodeOperations {
|
|
3890
|
-
#forRemoval;
|
|
3891
|
-
constructor() {
|
|
3892
|
-
this.#forRemoval = [];
|
|
3893
|
-
}
|
|
3889
|
+
#forRemoval = new Set();
|
|
3894
3890
|
removed(node) {
|
|
3895
|
-
return this.#forRemoval.
|
|
3891
|
+
return this.#forRemoval.has(node);
|
|
3896
3892
|
}
|
|
3897
3893
|
remove(node) {
|
|
3898
3894
|
node.replaceChildren?.();
|
|
3899
3895
|
Object.keys(node.dataset || {})
|
|
3900
3896
|
.filter((k) => k.startsWith('tpl'))
|
|
3901
|
-
.forEach((k) =>
|
|
3902
|
-
|
|
3897
|
+
.forEach((k) => {
|
|
3898
|
+
delete node.dataset[k];
|
|
3899
|
+
});
|
|
3900
|
+
this.#forRemoval.add(node);
|
|
3903
3901
|
}
|
|
3904
3902
|
popData(node, key) {
|
|
3905
3903
|
const v = node.dataset[key];
|
|
@@ -3917,9 +3915,10 @@ var fml = (function (exports) {
|
|
|
3917
3915
|
this.remove(ref);
|
|
3918
3916
|
}
|
|
3919
3917
|
cleanup() {
|
|
3920
|
-
|
|
3921
|
-
|
|
3918
|
+
for (const node of this.#forRemoval) {
|
|
3919
|
+
node.remove();
|
|
3922
3920
|
}
|
|
3921
|
+
this.#forRemoval.clear();
|
|
3923
3922
|
}
|
|
3924
3923
|
}
|
|
3925
3924
|
|
|
@@ -3974,7 +3973,7 @@ var fml = (function (exports) {
|
|
|
3974
3973
|
}
|
|
3975
3974
|
static tplRemove(node, value, ops, modules, dataStack) {
|
|
3976
3975
|
switch (value.toLowerCase()) {
|
|
3977
|
-
case 'tag':
|
|
3976
|
+
case 'tag': {
|
|
3978
3977
|
const fragment = new DocumentFragment();
|
|
3979
3978
|
while (node.firstChild) {
|
|
3980
3979
|
fragment.appendChild(node.firstChild);
|
|
@@ -3988,6 +3987,7 @@ var fml = (function (exports) {
|
|
|
3988
3987
|
ops.remove(node);
|
|
3989
3988
|
}
|
|
3990
3989
|
break;
|
|
3990
|
+
}
|
|
3991
3991
|
case 'body':
|
|
3992
3992
|
node.replaceChildren();
|
|
3993
3993
|
break;
|
|
@@ -4053,8 +4053,10 @@ var fml = (function (exports) {
|
|
|
4053
4053
|
}
|
|
4054
4054
|
}
|
|
4055
4055
|
|
|
4056
|
-
// Module-isolated string cache for dataset-to-attribute conversions
|
|
4056
|
+
// Module-isolated string cache for dataset-to-attribute conversions, bounded with
|
|
4057
|
+
// FIFO eviction like the expression ast cache
|
|
4057
4058
|
const attributeCache = new Map();
|
|
4059
|
+
const ATTRIBUTE_CACHE_MAX_SIZE = 1000;
|
|
4058
4060
|
|
|
4059
4061
|
/**
|
|
4060
4062
|
* Converts a tpl camelCase dataset key into a kebab-case attribute name.
|
|
@@ -4065,6 +4067,9 @@ var fml = (function (exports) {
|
|
|
4065
4067
|
function toAttr(dataSetKey) {
|
|
4066
4068
|
let cached = attributeCache.get(dataSetKey);
|
|
4067
4069
|
if (!cached) {
|
|
4070
|
+
if (attributeCache.size >= ATTRIBUTE_CACHE_MAX_SIZE) {
|
|
4071
|
+
attributeCache.delete(attributeCache.keys().next().value);
|
|
4072
|
+
}
|
|
4068
4073
|
cached = dataSetKey
|
|
4069
4074
|
.substring(3)
|
|
4070
4075
|
.split(/(?=[A-Z])/)
|
|
@@ -4199,9 +4204,10 @@ var fml = (function (exports) {
|
|
|
4199
4204
|
* @param {string} expression
|
|
4200
4205
|
* @param {(typeof Expressions.MODE_EXPRESSION | typeof Expressions.MODE_TEMPLATED)?} [mode]
|
|
4201
4206
|
* @param {...*} data
|
|
4207
|
+
* @returns the evaluated expression result
|
|
4202
4208
|
*/
|
|
4203
4209
|
evaluate(expression, mode, ...data) {
|
|
4204
|
-
new ExpressionEvaluator(this.#modules, this.#dataStack).withOverlay(...data).evaluate(expression, mode);
|
|
4210
|
+
return new ExpressionEvaluator(this.#modules, this.#dataStack).withOverlay(...data).evaluate(expression, mode);
|
|
4205
4211
|
}
|
|
4206
4212
|
/**
|
|
4207
4213
|
* Returns an expression evaluator with bound modules and dataStack.
|
|
@@ -4346,8 +4352,8 @@ var fml = (function (exports) {
|
|
|
4346
4352
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
4347
4353
|
node.nodeValue = node.nodeValue.trim();
|
|
4348
4354
|
}
|
|
4349
|
-
for (
|
|
4350
|
-
|
|
4355
|
+
for (let n = 0; n < node.childNodes.length; n++) {
|
|
4356
|
+
const child = node.childNodes[n];
|
|
4351
4357
|
if (child.nodeType === Node.TEXT_NODE) {
|
|
4352
4358
|
child.nodeValue = child.nodeValue.trim();
|
|
4353
4359
|
if (child.nodeValue.length === 0) {
|
|
@@ -4366,8 +4372,7 @@ var fml = (function (exports) {
|
|
|
4366
4372
|
#q = new Map();
|
|
4367
4373
|
constructor() {
|
|
4368
4374
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
4369
|
-
|
|
4370
|
-
await Promise.all(pending);
|
|
4375
|
+
await this.settle();
|
|
4371
4376
|
document.dispatchEvent(
|
|
4372
4377
|
new CustomEvent('ftl:ready', {
|
|
4373
4378
|
bubbles: false,
|
|
@@ -4387,6 +4392,16 @@ var fml = (function (exports) {
|
|
|
4387
4392
|
.finally(() => this.#q.delete(el));
|
|
4388
4393
|
this.#q.set(el, promise);
|
|
4389
4394
|
}
|
|
4395
|
+
/**
|
|
4396
|
+
* Waits for every queued upgrade, including the ones enqueued while waiting:
|
|
4397
|
+
* a component is only queued once its parent connects it, so a single pass would
|
|
4398
|
+
* miss everything nested.
|
|
4399
|
+
*/
|
|
4400
|
+
async settle() {
|
|
4401
|
+
while (this.#q.size !== 0) {
|
|
4402
|
+
await Promise.all(Array.from(this.#q.values()));
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4390
4405
|
get entries() {
|
|
4391
4406
|
return this.#q.entries();
|
|
4392
4407
|
}
|
|
@@ -4581,18 +4596,30 @@ var fml = (function (exports) {
|
|
|
4581
4596
|
}
|
|
4582
4597
|
}
|
|
4583
4598
|
|
|
4584
|
-
|
|
4585
|
-
|
|
4599
|
+
/**
|
|
4600
|
+
* Waits for the queued upgrades matching the filter, including the ones enqueued while
|
|
4601
|
+
* waiting: a component is only queued once its parent connects it, so a single pass
|
|
4602
|
+
* would miss everything nested.
|
|
4603
|
+
* @param {(el: Element) => boolean} accept
|
|
4604
|
+
*/
|
|
4605
|
+
const settle = async (accept) => {
|
|
4606
|
+
for (;;) {
|
|
4586
4607
|
const pending = Array.from(registry.upgrades)
|
|
4587
|
-
.filter(([child
|
|
4588
|
-
.map(([
|
|
4608
|
+
.filter(([child]) => accept(child))
|
|
4609
|
+
.map(([, promise]) => promise);
|
|
4610
|
+
if (pending.length === 0) {
|
|
4611
|
+
return;
|
|
4612
|
+
}
|
|
4589
4613
|
await Promise.all(pending);
|
|
4590
4614
|
}
|
|
4615
|
+
};
|
|
4616
|
+
|
|
4617
|
+
class Rendering {
|
|
4618
|
+
static async waitFor(el) {
|
|
4619
|
+
await settle((child) => el.contains(child));
|
|
4620
|
+
}
|
|
4591
4621
|
static async waitForChildren(el) {
|
|
4592
|
-
|
|
4593
|
-
.filter(([child, promise]) => el !== child && el.contains(child))
|
|
4594
|
-
.map(([child, promise]) => promise);
|
|
4595
|
-
await Promise.all(pending);
|
|
4622
|
+
await settle((child) => el !== child && el.contains(child));
|
|
4596
4623
|
}
|
|
4597
4624
|
}
|
|
4598
4625
|
|
|
@@ -4634,7 +4661,7 @@ var fml = (function (exports) {
|
|
|
4634
4661
|
template(name) {
|
|
4635
4662
|
const { modules, data } = registry.context();
|
|
4636
4663
|
let t = this.#bits().TEMPLATES[name ?? 'default'].withData(data).withModules(modules);
|
|
4637
|
-
for (
|
|
4664
|
+
for (const k of ['l10n', 'config']) {
|
|
4638
4665
|
const v = this.constructor[k];
|
|
4639
4666
|
if (v) {
|
|
4640
4667
|
t = t.withOverlay({ [k]: v });
|
|
@@ -4775,13 +4802,22 @@ var fml = (function (exports) {
|
|
|
4775
4802
|
|
|
4776
4803
|
let vi = 0;
|
|
4777
4804
|
let si = 0;
|
|
4778
|
-
|
|
4805
|
+
//every group yields three bytes, but a padded input stops short of the last
|
|
4806
|
+
//one or two: writing them anyway would rely on typed arrays dropping writes
|
|
4807
|
+
//past their length
|
|
4808
|
+
while (vi < nbytes) {
|
|
4779
4809
|
const v1 = d.indexOf(str.charAt(si++));
|
|
4780
4810
|
const v2 = d.indexOf(str.charAt(si++));
|
|
4781
4811
|
const v3 = d.indexOf(str.charAt(si++));
|
|
4782
4812
|
const v4 = d.indexOf(str.charAt(si++));
|
|
4783
4813
|
view[vi++] = (v1 << 2) | (v2 >> 4);
|
|
4814
|
+
if (vi === nbytes) {
|
|
4815
|
+
break;
|
|
4816
|
+
}
|
|
4784
4817
|
view[vi++] = ((v2 & 15) << 4) | (v3 >> 2);
|
|
4818
|
+
if (vi === nbytes) {
|
|
4819
|
+
break;
|
|
4820
|
+
}
|
|
4785
4821
|
view[vi++] = ((v3 & 3) << 6) | v4;
|
|
4786
4822
|
}
|
|
4787
4823
|
|
|
@@ -4808,7 +4844,7 @@ var fml = (function (exports) {
|
|
|
4808
4844
|
return Array.from(bytes)
|
|
4809
4845
|
.map((b) => b.toString(16))
|
|
4810
4846
|
.map((b) => (upper ? b.toUpperCase() : b))
|
|
4811
|
-
.map((o) => o.padStart(2, 0))
|
|
4847
|
+
.map((o) => o.padStart(2, '0'))
|
|
4812
4848
|
.join('');
|
|
4813
4849
|
}
|
|
4814
4850
|
}
|
|
@@ -5138,6 +5174,26 @@ var fml = (function (exports) {
|
|
|
5138
5174
|
}
|
|
5139
5175
|
};
|
|
5140
5176
|
|
|
5177
|
+
/**
|
|
5178
|
+
* Yields the entries of a headers or params initializer preserving nullish values:
|
|
5179
|
+
* normalizing through `Headers`/`URLSearchParams` first would stringify them to
|
|
5180
|
+
* "null"/"undefined" instead of removing the key.
|
|
5181
|
+
* @param {any} source
|
|
5182
|
+
* @returns {Iterable<[string, any]>}
|
|
5183
|
+
*/
|
|
5184
|
+
const rawEntries = (source) => {
|
|
5185
|
+
if (source == null) {
|
|
5186
|
+
return [];
|
|
5187
|
+
}
|
|
5188
|
+
if (typeof source === 'string') {
|
|
5189
|
+
return new URLSearchParams(source);
|
|
5190
|
+
}
|
|
5191
|
+
if (typeof source[Symbol.iterator] === 'function') {
|
|
5192
|
+
return source;
|
|
5193
|
+
}
|
|
5194
|
+
return Object.entries(source);
|
|
5195
|
+
};
|
|
5196
|
+
|
|
5141
5197
|
class HttpRequestBuilder {
|
|
5142
5198
|
#client;
|
|
5143
5199
|
#method;
|
|
@@ -5190,11 +5246,11 @@ var fml = (function (exports) {
|
|
|
5190
5246
|
}
|
|
5191
5247
|
/**
|
|
5192
5248
|
* Add all passed headers to the request, overriding existing ones if that key already exists. Null and undefined values cause the key to be removed.
|
|
5193
|
-
* @param {HeadersInit} hs
|
|
5249
|
+
* @param {HeadersInit|Record<string,string|null|undefined>} hs
|
|
5194
5250
|
* @returns {HttpRequestBuilder} this builder
|
|
5195
5251
|
*/
|
|
5196
5252
|
headers(hs) {
|
|
5197
|
-
for (const [k, v] of
|
|
5253
|
+
for (const [k, v] of rawEntries(hs)) {
|
|
5198
5254
|
if (v == null) {
|
|
5199
5255
|
this.#headers.delete(k);
|
|
5200
5256
|
} else {
|
|
@@ -5219,11 +5275,11 @@ var fml = (function (exports) {
|
|
|
5219
5275
|
}
|
|
5220
5276
|
/**
|
|
5221
5277
|
* Add all query parameters to the request, overriding existing ones if that key already exists. Null and undefined values cause the key to be removed
|
|
5222
|
-
* @param {URLSearchParams|Record<string,string>|string[][]|string} ps
|
|
5278
|
+
* @param {URLSearchParams|Record<string,string|null|undefined>|string[][]|string} ps
|
|
5223
5279
|
* @returns {HttpRequestBuilder} this builder
|
|
5224
5280
|
*/
|
|
5225
5281
|
params(ps) {
|
|
5226
|
-
for (const [k, v] of
|
|
5282
|
+
for (const [k, v] of rawEntries(ps)) {
|
|
5227
5283
|
if (v == null) {
|
|
5228
5284
|
this.#params.delete(k);
|
|
5229
5285
|
} else {
|
|
@@ -5239,8 +5295,10 @@ var fml = (function (exports) {
|
|
|
5239
5295
|
* @returns {HttpRequestBuilder} this builder
|
|
5240
5296
|
*/
|
|
5241
5297
|
param(k, ...vs) {
|
|
5298
|
+
//overriding, as header, headers and params all do: pass every value in one
|
|
5299
|
+
//call to get a multi valued parameter
|
|
5300
|
+
this.#params.delete(k);
|
|
5242
5301
|
if (vs.length === 0 || vs[0] == null) {
|
|
5243
|
-
this.#params.delete(k);
|
|
5244
5302
|
return this;
|
|
5245
5303
|
}
|
|
5246
5304
|
for (const v of vs) {
|
|
@@ -5273,7 +5331,7 @@ var fml = (function (exports) {
|
|
|
5273
5331
|
/**
|
|
5274
5332
|
* Sets the request body as a FormData configured using the callback.
|
|
5275
5333
|
* `Content-Type: multipart/form-data` header is automatically added by fetch if not explicitly set.
|
|
5276
|
-
* @param {
|
|
5334
|
+
* @param {(c: HttpMultipartRequestCustomizer) => void} callback
|
|
5277
5335
|
*/
|
|
5278
5336
|
multipart(callback) {
|
|
5279
5337
|
const formData = new FormData();
|
|
@@ -5438,7 +5496,7 @@ var fml = (function (exports) {
|
|
|
5438
5496
|
* @returns this builder
|
|
5439
5497
|
*/
|
|
5440
5498
|
blobs(name, values) {
|
|
5441
|
-
for (
|
|
5499
|
+
for (const v of values) {
|
|
5442
5500
|
this.#formData.append(name, v);
|
|
5443
5501
|
}
|
|
5444
5502
|
return this;
|
|
@@ -5473,7 +5531,16 @@ var fml = (function (exports) {
|
|
|
5473
5531
|
}
|
|
5474
5532
|
static load(k) {
|
|
5475
5533
|
const got = localStorage.getItem(k);
|
|
5476
|
-
|
|
5534
|
+
if (got === null) {
|
|
5535
|
+
return undefined;
|
|
5536
|
+
}
|
|
5537
|
+
try {
|
|
5538
|
+
return JSON.parse(got);
|
|
5539
|
+
} catch {
|
|
5540
|
+
//not what save wrote: drop it, otherwise every later read fails the same way
|
|
5541
|
+
localStorage.removeItem(k);
|
|
5542
|
+
return undefined;
|
|
5543
|
+
}
|
|
5477
5544
|
}
|
|
5478
5545
|
static remove(k) {
|
|
5479
5546
|
localStorage.removeItem(k);
|
|
@@ -5491,7 +5558,16 @@ var fml = (function (exports) {
|
|
|
5491
5558
|
}
|
|
5492
5559
|
static load(k) {
|
|
5493
5560
|
const got = sessionStorage.getItem(k);
|
|
5494
|
-
|
|
5561
|
+
if (got === null) {
|
|
5562
|
+
return undefined;
|
|
5563
|
+
}
|
|
5564
|
+
try {
|
|
5565
|
+
return JSON.parse(got);
|
|
5566
|
+
} catch {
|
|
5567
|
+
//not what save wrote: drop it, otherwise every later read fails the same way
|
|
5568
|
+
sessionStorage.removeItem(k);
|
|
5569
|
+
return undefined;
|
|
5570
|
+
}
|
|
5495
5571
|
}
|
|
5496
5572
|
static remove(k) {
|
|
5497
5573
|
sessionStorage.removeItem(k);
|
|
@@ -5513,7 +5589,7 @@ var fml = (function (exports) {
|
|
|
5513
5589
|
return undefined;
|
|
5514
5590
|
}
|
|
5515
5591
|
if (stored.revision !== revision) {
|
|
5516
|
-
|
|
5592
|
+
LocalStorage.remove(key);
|
|
5517
5593
|
return undefined;
|
|
5518
5594
|
}
|
|
5519
5595
|
return stored.data;
|
|
@@ -5530,7 +5606,7 @@ var fml = (function (exports) {
|
|
|
5530
5606
|
return undefined;
|
|
5531
5607
|
}
|
|
5532
5608
|
if (stored.revision !== revision) {
|
|
5533
|
-
|
|
5609
|
+
SessionStorage.remove(key);
|
|
5534
5610
|
return undefined;
|
|
5535
5611
|
}
|
|
5536
5612
|
return stored.data;
|
|
@@ -5665,7 +5741,7 @@ var fml = (function (exports) {
|
|
|
5665
5741
|
*/
|
|
5666
5742
|
static debounce(timeoutMs, func, options) {
|
|
5667
5743
|
const opts = options ?? Timing.DEBOUNCE_DEFAULT;
|
|
5668
|
-
let tid = null;
|
|
5744
|
+
let tid = /** @type {number | null} */ (null);
|
|
5669
5745
|
let args = [];
|
|
5670
5746
|
let previousTimestamp = 0;
|
|
5671
5747
|
|
|
@@ -5685,8 +5761,8 @@ var fml = (function (exports) {
|
|
|
5685
5761
|
}
|
|
5686
5762
|
};
|
|
5687
5763
|
|
|
5688
|
-
const debounced =
|
|
5689
|
-
args =
|
|
5764
|
+
const debounced = (...called) => {
|
|
5765
|
+
args = called;
|
|
5690
5766
|
previousTimestamp = performance.now();
|
|
5691
5767
|
if (tid === null) {
|
|
5692
5768
|
tid = setTimeout(later, timeoutMs);
|
|
@@ -5695,7 +5771,11 @@ var fml = (function (exports) {
|
|
|
5695
5771
|
}
|
|
5696
5772
|
}
|
|
5697
5773
|
};
|
|
5698
|
-
const abort = () =>
|
|
5774
|
+
const abort = () => {
|
|
5775
|
+
clearTimeout(tid ?? undefined);
|
|
5776
|
+
tid = null;
|
|
5777
|
+
args = [];
|
|
5778
|
+
};
|
|
5699
5779
|
return [debounced, abort];
|
|
5700
5780
|
}
|
|
5701
5781
|
static THROTTLE_DEFAULT = 0;
|
|
@@ -5710,7 +5790,7 @@ var fml = (function (exports) {
|
|
|
5710
5790
|
*/
|
|
5711
5791
|
static throttle(timeoutMs, func, options) {
|
|
5712
5792
|
const opts = options ?? Timing.THROTTLE_DEFAULT;
|
|
5713
|
-
let tid = null;
|
|
5793
|
+
let tid = /** @type {number | null} */ (null);
|
|
5714
5794
|
let args = [];
|
|
5715
5795
|
let previousTimestamp = 0;
|
|
5716
5796
|
|
|
@@ -5722,13 +5802,13 @@ var fml = (function (exports) {
|
|
|
5722
5802
|
args = [];
|
|
5723
5803
|
}
|
|
5724
5804
|
};
|
|
5725
|
-
const throttled =
|
|
5805
|
+
const throttled = (...called) => {
|
|
5726
5806
|
const now = performance.now();
|
|
5727
5807
|
if (!previousTimestamp && opts & Timing.THROTTLE_NO_LEADING) {
|
|
5728
5808
|
previousTimestamp = now;
|
|
5729
5809
|
}
|
|
5730
5810
|
const remaining = previousTimestamp === 0 ? 0 : timeoutMs - (now - previousTimestamp);
|
|
5731
|
-
args =
|
|
5811
|
+
args = called;
|
|
5732
5812
|
if (remaining <= 0 || remaining > timeoutMs) {
|
|
5733
5813
|
if (tid !== null) {
|
|
5734
5814
|
clearTimeout(tid);
|
|
@@ -5743,7 +5823,11 @@ var fml = (function (exports) {
|
|
|
5743
5823
|
tid = setTimeout(later, remaining);
|
|
5744
5824
|
}
|
|
5745
5825
|
};
|
|
5746
|
-
const abort = () =>
|
|
5826
|
+
const abort = () => {
|
|
5827
|
+
clearTimeout(tid ?? undefined);
|
|
5828
|
+
tid = null;
|
|
5829
|
+
args = [];
|
|
5830
|
+
};
|
|
5747
5831
|
return [throttled, abort];
|
|
5748
5832
|
}
|
|
5749
5833
|
}
|
|
@@ -5757,7 +5841,7 @@ var fml = (function (exports) {
|
|
|
5757
5841
|
*/
|
|
5758
5842
|
static flatten(obj, prefix, stops) {
|
|
5759
5843
|
return Object.keys(obj).reduce((acc, k) => {
|
|
5760
|
-
const pre = prefix.length ? prefix
|
|
5844
|
+
const pre = prefix.length ? `${prefix}.${k}` : k;
|
|
5761
5845
|
if (!stops.has(pre) && typeof obj[k] === 'object' && obj[k] !== null) {
|
|
5762
5846
|
Object.assign(acc, Bindings.flatten(obj[k], pre, stops));
|
|
5763
5847
|
} else {
|
|
@@ -5775,7 +5859,7 @@ var fml = (function (exports) {
|
|
|
5775
5859
|
static providePath(result, path, value) {
|
|
5776
5860
|
const keys = path.split('.').map((k) => (/^[0-9]+$/.test(k) ? +k : k));
|
|
5777
5861
|
let current = result ?? {};
|
|
5778
|
-
let previous = null;
|
|
5862
|
+
let previous = /** @type {any} */ (null);
|
|
5779
5863
|
for (let i = 0; ; ++i) {
|
|
5780
5864
|
const ckey = keys[i];
|
|
5781
5865
|
const pkey = keys[i - 1];
|
|
@@ -5808,12 +5892,12 @@ var fml = (function (exports) {
|
|
|
5808
5892
|
if (!el.checked) {
|
|
5809
5893
|
return undefined;
|
|
5810
5894
|
}
|
|
5811
|
-
return el.dataset
|
|
5895
|
+
return el.dataset.fulBindType === 'boolean' ? el.value === 'true' : el.value;
|
|
5812
5896
|
}
|
|
5813
5897
|
if (el.getAttribute('type') === 'checkbox') {
|
|
5814
5898
|
return el.checked;
|
|
5815
5899
|
}
|
|
5816
|
-
if (el.dataset
|
|
5900
|
+
if (el.dataset.fulBindType === 'boolean') {
|
|
5817
5901
|
return !el.value ? null : el.value === 'true';
|
|
5818
5902
|
}
|
|
5819
5903
|
if (el.tagName === 'INPUT' || el.tagName === 'SELECT' || el.tagName === 'TEXTAREA') {
|
|
@@ -5846,12 +5930,14 @@ var fml = (function (exports) {
|
|
|
5846
5930
|
|
|
5847
5931
|
/**
|
|
5848
5932
|
*
|
|
5849
|
-
* @param {Element
|
|
5933
|
+
* @param {Element & {dataset?: any} & {checked?: boolean} & {value?: any}} el
|
|
5850
5934
|
* @returns
|
|
5851
5935
|
*/
|
|
5852
5936
|
static mutate(el, raw) {
|
|
5853
5937
|
if (el.getAttribute('type') === 'radio') {
|
|
5854
|
-
|
|
5938
|
+
//values are matched as strings, as ful-radio-group does: extract decodes
|
|
5939
|
+
//boolean radios, and payloads carry numbers where the attribute is text
|
|
5940
|
+
el.checked = raw != null && el.getAttribute('value') === String(raw);
|
|
5855
5941
|
return;
|
|
5856
5942
|
}
|
|
5857
5943
|
if (el.getAttribute('type') === 'checkbox') {
|
|
@@ -5875,7 +5961,9 @@ var fml = (function (exports) {
|
|
|
5875
5961
|
static errors(form, es, scrollOnError) {
|
|
5876
5962
|
const fieldErrors = es.filter((e) => e.type === 'FIELD_ERROR' || e.type === 'INVALID_FORMAT');
|
|
5877
5963
|
const globalErrors = es.filter((e) => e.type !== 'FIELD_ERROR' && e.type !== 'INVALID_FORMAT');
|
|
5878
|
-
form.querySelectorAll(`[name]`).forEach((el) =>
|
|
5964
|
+
form.querySelectorAll(`[name]`).forEach((el) => {
|
|
5965
|
+
el.setCustomValidity?.('');
|
|
5966
|
+
});
|
|
5879
5967
|
form.querySelectorAll('ful-errors').forEach((el) => {
|
|
5880
5968
|
el.replaceChildren();
|
|
5881
5969
|
el.setAttribute('hidden', '');
|
|
@@ -5883,12 +5971,12 @@ var fml = (function (exports) {
|
|
|
5883
5971
|
fieldErrors.forEach((e) => {
|
|
5884
5972
|
const name = e.context.replace(/\[/g, '.').replace(/\]\./g, '.').replace(/\]/g, '');
|
|
5885
5973
|
const parts = name.split('.');
|
|
5886
|
-
for (let i = parts.length; i
|
|
5974
|
+
for (let i = parts.length; i !== 0; --i) {
|
|
5887
5975
|
const prefix = parts.slice(0, i).join('.');
|
|
5888
5976
|
const suffix = parts.slice(i, parts.length).join('.');
|
|
5889
|
-
form.querySelectorAll(`[name='${CSS.escape(prefix)}']`).forEach((input) =>
|
|
5890
|
-
input.setCustomValidity?.(e.reason, suffix)
|
|
5891
|
-
);
|
|
5977
|
+
form.querySelectorAll(`[name='${CSS.escape(prefix)}']`).forEach((input) => {
|
|
5978
|
+
input.setCustomValidity?.(e.reason, suffix);
|
|
5979
|
+
});
|
|
5892
5980
|
}
|
|
5893
5981
|
});
|
|
5894
5982
|
form.querySelectorAll('ful-errors').forEach((el) => {
|
|
@@ -5898,7 +5986,7 @@ var fml = (function (exports) {
|
|
|
5898
5986
|
el.removeAttribute('hidden');
|
|
5899
5987
|
}
|
|
5900
5988
|
});
|
|
5901
|
-
if (es.length
|
|
5989
|
+
if (es.length === 0 || !scrollOnError) {
|
|
5902
5990
|
return;
|
|
5903
5991
|
}
|
|
5904
5992
|
Array.from(form.querySelectorAll(`:invalid`))
|
|
@@ -5970,7 +6058,8 @@ var fml = (function (exports) {
|
|
|
5970
6058
|
class Form extends ParsedElement {
|
|
5971
6059
|
form;
|
|
5972
6060
|
render() {
|
|
5973
|
-
const form =
|
|
6061
|
+
const form = document.createElement('form');
|
|
6062
|
+
this.form = form;
|
|
5974
6063
|
form.setAttribute('novalidate', '');
|
|
5975
6064
|
Attributes.forward('form-', this, form);
|
|
5976
6065
|
form.replaceChildren(...this.childNodes);
|
|
@@ -5993,50 +6082,53 @@ var fml = (function (exports) {
|
|
|
5993
6082
|
*/
|
|
5994
6083
|
async submit(submitter) {
|
|
5995
6084
|
this.spinner(true);
|
|
6085
|
+
//one try: building the loader and preparing the request are as much part of a
|
|
6086
|
+
//submit as sending it, and a mapper that throws is how a caller reports a
|
|
6087
|
+
//problem with the values
|
|
6088
|
+
let values;
|
|
6089
|
+
let request;
|
|
5996
6090
|
try {
|
|
5997
6091
|
const loader = registry.component(this.getAttribute('loader') ?? 'loaders:form').create(this);
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6092
|
+
values = Bindings.extractFrom(this.form, submitter);
|
|
6093
|
+
request = await loader.prepare(values, this);
|
|
6094
|
+
const se = new CustomEvent('submit', {
|
|
6095
|
+
bubbles: true,
|
|
6096
|
+
cancelable: true,
|
|
6097
|
+
detail: { submitter, values, request },
|
|
6098
|
+
});
|
|
6099
|
+
if (!this.dispatchEvent(se)) {
|
|
6100
|
+
return;
|
|
6101
|
+
}
|
|
6102
|
+
this.errors = [];
|
|
6103
|
+
const sre = new CustomEvent('submit:requested', {
|
|
6104
|
+
bubbles: true,
|
|
6105
|
+
cancelable: false,
|
|
6106
|
+
detail: { submitter, values: se.detail.values, request: se.detail.request },
|
|
6107
|
+
});
|
|
6108
|
+
let response = await AsyncEvents.fireAsync(this, sre, { mode: 'pipeline' });
|
|
6109
|
+
request = sre.detail.request;
|
|
6110
|
+
|
|
6111
|
+
response = await loader.submit(request, this, response);
|
|
6112
|
+
const mapped = await loader.transform(response, this);
|
|
6113
|
+
this.dispatchEvent(
|
|
6114
|
+
new CustomEvent('submit:success', {
|
|
6002
6115
|
bubbles: true,
|
|
6003
|
-
cancelable:
|
|
6004
|
-
detail: { submitter, values, request },
|
|
6005
|
-
})
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
const sre = new CustomEvent('submit:requested', {
|
|
6116
|
+
cancelable: false,
|
|
6117
|
+
detail: { submitter, values, request, response: mapped },
|
|
6118
|
+
}),
|
|
6119
|
+
);
|
|
6120
|
+
} catch (e) {
|
|
6121
|
+
this.dispatchEvent(
|
|
6122
|
+
new CustomEvent('submit:failure', {
|
|
6011
6123
|
bubbles: true,
|
|
6012
6124
|
cancelable: false,
|
|
6013
|
-
detail: { submitter, values
|
|
6014
|
-
})
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
response = await loader.submit(request, this, response);
|
|
6019
|
-
const mapped = await loader.transform(response, this);
|
|
6020
|
-
this.dispatchEvent(
|
|
6021
|
-
new CustomEvent('submit:success', {
|
|
6022
|
-
bubbles: true,
|
|
6023
|
-
cancelable: false,
|
|
6024
|
-
detail: { submitter, values, request, response: mapped },
|
|
6025
|
-
}),
|
|
6026
|
-
);
|
|
6027
|
-
} catch (e) {
|
|
6028
|
-
this.dispatchEvent(
|
|
6029
|
-
new CustomEvent('submit:failure', {
|
|
6030
|
-
bubbles: true,
|
|
6031
|
-
cancelable: false,
|
|
6032
|
-
detail: { submitter, values, request, exception: e },
|
|
6033
|
-
}),
|
|
6034
|
-
);
|
|
6035
|
-
if (e instanceof Failure) {
|
|
6036
|
-
this.errors = e.problems;
|
|
6037
|
-
}
|
|
6038
|
-
console.warn('failed to submit form', this, 'reason:', e);
|
|
6125
|
+
detail: { submitter, values, request, exception: e },
|
|
6126
|
+
}),
|
|
6127
|
+
);
|
|
6128
|
+
if (e instanceof Failure) {
|
|
6129
|
+
this.errors = e.problems;
|
|
6039
6130
|
}
|
|
6131
|
+
console.warn('failed to submit form', this, 'reason:', e);
|
|
6040
6132
|
} finally {
|
|
6041
6133
|
this.spinner(false);
|
|
6042
6134
|
}
|
|
@@ -6044,7 +6136,20 @@ var fml = (function (exports) {
|
|
|
6044
6136
|
reset() {
|
|
6045
6137
|
this.form.reset();
|
|
6046
6138
|
}
|
|
6139
|
+
#spinning = 0;
|
|
6047
6140
|
spinner(spin) {
|
|
6141
|
+
//submits can overlap: only the outermost one saves and restores the button states
|
|
6142
|
+
if (spin) {
|
|
6143
|
+
++this.#spinning;
|
|
6144
|
+
if (this.#spinning !== 1) {
|
|
6145
|
+
return;
|
|
6146
|
+
}
|
|
6147
|
+
} else {
|
|
6148
|
+
this.#spinning = Math.max(0, this.#spinning - 1);
|
|
6149
|
+
if (this.#spinning !== 0) {
|
|
6150
|
+
return;
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6048
6153
|
this.querySelectorAll('ful-spinner').forEach((el) => {
|
|
6049
6154
|
const hel = /** @type HTMLElement */ (el);
|
|
6050
6155
|
hel.hidden = !spin;
|
|
@@ -6075,7 +6180,7 @@ var fml = (function (exports) {
|
|
|
6075
6180
|
}
|
|
6076
6181
|
|
|
6077
6182
|
class Input extends ParsedElement {
|
|
6078
|
-
static observed = ['value', 'readonly:presence', 'required:presence'];
|
|
6183
|
+
static observed = ['value', 'readonly:presence', 'required:presence', 'placeholder'];
|
|
6079
6184
|
static slots = true;
|
|
6080
6185
|
static template = `
|
|
6081
6186
|
<div class="form-label">
|
|
@@ -6112,12 +6217,6 @@ var fml = (function (exports) {
|
|
|
6112
6217
|
this._input = fragment.querySelector('input,textarea');
|
|
6113
6218
|
|
|
6114
6219
|
Attributes.forward('input-', this, this._input);
|
|
6115
|
-
if (!skipObservedSetup) {
|
|
6116
|
-
this.disabled = disabled;
|
|
6117
|
-
this.readonly = observed.readonly;
|
|
6118
|
-
this.required = observed.required;
|
|
6119
|
-
this.value = observed.value;
|
|
6120
|
-
}
|
|
6121
6220
|
this._input.addEventListener('keydown', (evt) => {
|
|
6122
6221
|
if (evt.key !== 'Enter' || this._type() === 'textarea') {
|
|
6123
6222
|
return;
|
|
@@ -6133,19 +6232,26 @@ var fml = (function (exports) {
|
|
|
6133
6232
|
form.requestSubmit(submitter);
|
|
6134
6233
|
});
|
|
6135
6234
|
this._input.addEventListener('input', (evt) => {
|
|
6136
|
-
const
|
|
6137
|
-
if (!
|
|
6235
|
+
const mask = this.getAttribute('mask');
|
|
6236
|
+
if (!mask) {
|
|
6138
6237
|
return;
|
|
6139
6238
|
}
|
|
6239
|
+
const strip = (v) => v.replace(new RegExp(mask, 'g'), '');
|
|
6140
6240
|
const before = evt.target.value;
|
|
6141
|
-
const after = before
|
|
6241
|
+
const after = strip(before);
|
|
6142
6242
|
if (before === after) {
|
|
6143
6243
|
return;
|
|
6144
6244
|
}
|
|
6145
6245
|
const start = evt.target.selectionStart;
|
|
6146
|
-
const offset = before.length - after.length;
|
|
6147
6246
|
evt.target.value = after;
|
|
6148
|
-
|
|
6247
|
+
if (start === null) {
|
|
6248
|
+
//email, number and the date types have no selection to restore
|
|
6249
|
+
return;
|
|
6250
|
+
}
|
|
6251
|
+
//the caret keeps its place among the characters that survived, so only the
|
|
6252
|
+
//ones stripped before it count
|
|
6253
|
+
const caret = strip(before.slice(0, start)).length;
|
|
6254
|
+
evt.target.setSelectionRange(caret, caret);
|
|
6149
6255
|
});
|
|
6150
6256
|
this._input.addEventListener('change', (evt) => {
|
|
6151
6257
|
evt.stopPropagation();
|
|
@@ -6165,6 +6271,15 @@ var fml = (function (exports) {
|
|
|
6165
6271
|
this._input.ariaDescribedByElements = [this._fieldError];
|
|
6166
6272
|
this._input.ariaLabelledByElements = [label];
|
|
6167
6273
|
this.replaceChildren(fragment);
|
|
6274
|
+
if (!skipObservedSetup) {
|
|
6275
|
+
// biome-ignore lint/complexity/noUselessThisAlias: keeps checkJs from seeing these as class fields
|
|
6276
|
+
const el = this;
|
|
6277
|
+
el.disabled = disabled;
|
|
6278
|
+
el.readonly = observed.readonly;
|
|
6279
|
+
el.required = observed.required;
|
|
6280
|
+
el.placeholder = observed.placeholder;
|
|
6281
|
+
el.value = observed.value;
|
|
6282
|
+
}
|
|
6168
6283
|
}
|
|
6169
6284
|
get value() {
|
|
6170
6285
|
const uppercase = this.hasAttribute('uppercase');
|
|
@@ -6191,6 +6306,11 @@ var fml = (function (exports) {
|
|
|
6191
6306
|
}
|
|
6192
6307
|
set disabled(d) {
|
|
6193
6308
|
Attributes.toggle(this._input, 'disabled', d);
|
|
6309
|
+
//also on the host: a form associated element only matches :disabled through its
|
|
6310
|
+
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
6311
|
+
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
6312
|
+
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
6313
|
+
Attributes.toggle(this, 'disabled', d);
|
|
6194
6314
|
}
|
|
6195
6315
|
get required() {
|
|
6196
6316
|
return this._input.getAttribute('aria-required') === 'true';
|
|
@@ -6201,6 +6321,18 @@ var fml = (function (exports) {
|
|
|
6201
6321
|
Attributes.toggle(this, 'required', d);
|
|
6202
6322
|
});
|
|
6203
6323
|
}
|
|
6324
|
+
get placeholder() {
|
|
6325
|
+
const v = this._input.getAttribute('placeholder');
|
|
6326
|
+
return v === ' ' ? null : v;
|
|
6327
|
+
}
|
|
6328
|
+
set placeholder(d) {
|
|
6329
|
+
//without a placeholder :placeholder-shown never matches, and floating labels
|
|
6330
|
+
//rely on it, so a blank one stands in for none
|
|
6331
|
+
Attributes.set(this._input, 'placeholder', d ?? ' ');
|
|
6332
|
+
this.reflect(() => {
|
|
6333
|
+
Attributes.set(this, 'placeholder', d);
|
|
6334
|
+
});
|
|
6335
|
+
}
|
|
6204
6336
|
focus(options) {
|
|
6205
6337
|
this._input.focus(options);
|
|
6206
6338
|
}
|
|
@@ -6220,23 +6352,23 @@ var fml = (function (exports) {
|
|
|
6220
6352
|
|
|
6221
6353
|
class LocalDate extends ParsedElement {
|
|
6222
6354
|
render() {
|
|
6223
|
-
const content = this.
|
|
6355
|
+
const content = this.textContent.trim();
|
|
6224
6356
|
if (content === '') {
|
|
6225
|
-
this.
|
|
6357
|
+
this.replaceChildren(this.getAttribute('default') ?? '');
|
|
6226
6358
|
return;
|
|
6227
6359
|
}
|
|
6228
6360
|
const locale = this.getAttribute('locale') ?? Intl.DateTimeFormat().resolvedOptions().locale;
|
|
6229
6361
|
const formatter = new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'numeric', day: 'numeric' });
|
|
6230
6362
|
const [y, m, d] = content.split('-').map(Number);
|
|
6231
|
-
this.
|
|
6363
|
+
this.replaceChildren(formatter.format(new Date(y, m - 1, d)));
|
|
6232
6364
|
}
|
|
6233
6365
|
}
|
|
6234
6366
|
|
|
6235
6367
|
class Instant extends ParsedElement {
|
|
6236
6368
|
render() {
|
|
6237
|
-
const content = this.
|
|
6369
|
+
const content = this.textContent.trim();
|
|
6238
6370
|
if (content === '') {
|
|
6239
|
-
this.
|
|
6371
|
+
this.replaceChildren(this.getAttribute('default') ?? '');
|
|
6240
6372
|
return;
|
|
6241
6373
|
}
|
|
6242
6374
|
const locale = this.getAttribute('locale') ?? Intl.DateTimeFormat().resolvedOptions().locale;
|
|
@@ -6249,7 +6381,7 @@ var fml = (function (exports) {
|
|
|
6249
6381
|
second: 'numeric',
|
|
6250
6382
|
hour12: false,
|
|
6251
6383
|
});
|
|
6252
|
-
this.
|
|
6384
|
+
this.replaceChildren(format.format(new Date(Instant.isoToLocal(content))));
|
|
6253
6385
|
}
|
|
6254
6386
|
static isoToLocal(iso) {
|
|
6255
6387
|
//this is so sad
|
|
@@ -6262,16 +6394,17 @@ var fml = (function (exports) {
|
|
|
6262
6394
|
}
|
|
6263
6395
|
|
|
6264
6396
|
class InputLocalDate extends Input {
|
|
6265
|
-
static observed = ['value', 'readonly:presence', 'required:presence', 'min', 'max', 'step'];
|
|
6397
|
+
static observed = ['value', 'readonly:presence', 'required:presence', 'placeholder', 'min', 'max', 'step'];
|
|
6266
6398
|
_type() {
|
|
6267
6399
|
return 'date';
|
|
6268
6400
|
}
|
|
6269
6401
|
render(conf) {
|
|
6270
6402
|
const { observed } = conf;
|
|
6271
6403
|
super.render(conf);
|
|
6404
|
+
//step first: on a time input min and max are snapped to its grid
|
|
6405
|
+
this.step = observed.step;
|
|
6272
6406
|
this.min = observed.min;
|
|
6273
6407
|
this.max = observed.max;
|
|
6274
|
-
this.step = observed.step;
|
|
6275
6408
|
}
|
|
6276
6409
|
get min() {
|
|
6277
6410
|
const v = this._input.min;
|
|
@@ -6317,13 +6450,14 @@ var fml = (function (exports) {
|
|
|
6317
6450
|
case 'd':
|
|
6318
6451
|
r.setDate(r.getDate() + offset * sign);
|
|
6319
6452
|
break;
|
|
6320
|
-
case 'm':
|
|
6453
|
+
case 'm': {
|
|
6321
6454
|
const originalDay = r.getDate();
|
|
6322
6455
|
r.setMonth(r.getMonth() + offset * sign);
|
|
6323
6456
|
if (r.getDate() !== originalDay) {
|
|
6324
6457
|
r.setDate(0);
|
|
6325
6458
|
}
|
|
6326
6459
|
break;
|
|
6460
|
+
}
|
|
6327
6461
|
case 'y':
|
|
6328
6462
|
r.setFullYear(r.getFullYear() + offset * sign);
|
|
6329
6463
|
break;
|
|
@@ -6336,10 +6470,62 @@ var fml = (function (exports) {
|
|
|
6336
6470
|
_type() {
|
|
6337
6471
|
return 'time';
|
|
6338
6472
|
}
|
|
6473
|
+
get min() {
|
|
6474
|
+
const v = this._input.min;
|
|
6475
|
+
return v === '' ? null : v;
|
|
6476
|
+
}
|
|
6477
|
+
set min(v) {
|
|
6478
|
+
this._input.min = this.#fromNowOrOffset(v);
|
|
6479
|
+
}
|
|
6480
|
+
get max() {
|
|
6481
|
+
const v = this._input.max;
|
|
6482
|
+
return v === '' ? null : v;
|
|
6483
|
+
}
|
|
6484
|
+
set max(v) {
|
|
6485
|
+
this._input.max = this.#fromNowOrOffset(v);
|
|
6486
|
+
}
|
|
6487
|
+
/**
|
|
6488
|
+
* Resolves `now` and hour or minute offsets against the current time, wrapping
|
|
6489
|
+
* around midnight. `m` is minutes here, unlike the date offsets of the parent where
|
|
6490
|
+
* it is months: months mean nothing on a time. Anything else is passed through.
|
|
6491
|
+
*/
|
|
6492
|
+
#fromNowOrOffset(v) {
|
|
6493
|
+
if (!v) {
|
|
6494
|
+
return '';
|
|
6495
|
+
}
|
|
6496
|
+
const resolved = new Date();
|
|
6497
|
+
if (v !== 'now') {
|
|
6498
|
+
const re = /^([+-])(\d+)([hm])$/;
|
|
6499
|
+
const match = re.exec(v);
|
|
6500
|
+
if (!match) {
|
|
6501
|
+
return v;
|
|
6502
|
+
}
|
|
6503
|
+
const sign = match[1] === '-' ? -1 : 1;
|
|
6504
|
+
const offset = +match[2] * sign;
|
|
6505
|
+
if (match[3] === 'h') {
|
|
6506
|
+
resolved.setHours(resolved.getHours() + offset);
|
|
6507
|
+
} else {
|
|
6508
|
+
resolved.setMinutes(resolved.getMinutes() + offset);
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
return InputLocalTime.#snapped(resolved, Number(this._input.step) || 60);
|
|
6512
|
+
}
|
|
6513
|
+
/**
|
|
6514
|
+
* Truncates a time to the step grid: min anchors that grid, so a bound that is not
|
|
6515
|
+
* on it makes every value on it invalid.
|
|
6516
|
+
*/
|
|
6517
|
+
static #snapped(date, stepSeconds) {
|
|
6518
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
6519
|
+
const seconds = date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds();
|
|
6520
|
+
const snapped = Math.floor(seconds / stepSeconds) * stepSeconds;
|
|
6521
|
+
const hh = pad(Math.floor(snapped / 3600));
|
|
6522
|
+
const mm = pad(Math.floor((snapped % 3600) / 60));
|
|
6523
|
+
return stepSeconds % 60 === 0 ? `${hh}:${mm}` : `${hh}:${mm}:${pad(snapped % 60)}`;
|
|
6524
|
+
}
|
|
6339
6525
|
}
|
|
6340
6526
|
|
|
6341
6527
|
class InputInstant extends Input {
|
|
6342
|
-
static observed = ['value', 'readonly:presence', 'required:presence', 'min', 'max', 'step'];
|
|
6528
|
+
static observed = ['value', 'readonly:presence', 'required:presence', 'placeholder', 'min', 'max', 'step'];
|
|
6343
6529
|
_type() {
|
|
6344
6530
|
return 'datetime-local';
|
|
6345
6531
|
}
|
|
@@ -6384,28 +6570,28 @@ var fml = (function (exports) {
|
|
|
6384
6570
|
static l10n = {
|
|
6385
6571
|
en: {
|
|
6386
6572
|
dropzonelabel: 'Click or drop your files here',
|
|
6387
|
-
|
|
6573
|
+
unacceptablefiletype: 'Only files of type {0} are supported',
|
|
6388
6574
|
maxfilesizeexceeded: 'Maximum supported file size is {0}',
|
|
6389
6575
|
maxtotalsizeexceeded: 'Maximum supported total file size is {0}',
|
|
6390
6576
|
maxfilesexceeded: 'Maximum files count exceeded',
|
|
6391
6577
|
},
|
|
6392
6578
|
it: {
|
|
6393
6579
|
dropzonelabel: 'Clicca o trascina i file qui',
|
|
6394
|
-
|
|
6580
|
+
unacceptablefiletype: 'Solo i file di tipo {0} sono supportati',
|
|
6395
6581
|
maxfilesizeexceeded: 'La dimensione massima di un file è di {0}',
|
|
6396
6582
|
maxtotalsizeexceeded: 'La dimensione massima complessiva dei file è di {0}',
|
|
6397
6583
|
maxfilesexceeded: 'Numero massimo di file superato',
|
|
6398
6584
|
},
|
|
6399
6585
|
es: {
|
|
6400
6586
|
dropzonelabel: 'Haz clic o arrastra tus archivos aquí',
|
|
6401
|
-
|
|
6587
|
+
unacceptablefiletype: 'Solo se admiten archivos de tipo {0}',
|
|
6402
6588
|
maxfilesizeexceeded: 'El tamaño máximo de archivo admitido es {0}',
|
|
6403
6589
|
maxtotalsizeexceeded: 'El tamaño total máximo admitido es {0}',
|
|
6404
6590
|
maxfilesexceeded: 'Se ha superado el número máximo de archivos',
|
|
6405
6591
|
},
|
|
6406
6592
|
fr: {
|
|
6407
6593
|
dropzonelabel: 'Cliquez ou déposez vos fichiers ici',
|
|
6408
|
-
|
|
6594
|
+
unacceptablefiletype: 'Seuls les fichiers de type {0} sont pris en charge',
|
|
6409
6595
|
maxfilesizeexceeded: 'La taille maximale de fichier prise en charge est {0}',
|
|
6410
6596
|
maxtotalsizeexceeded: 'La taille totale maximale prise en charge est {0}',
|
|
6411
6597
|
maxfilesexceeded: 'Nombre maximal de fichiers dépassé',
|
|
@@ -6415,6 +6601,7 @@ var fml = (function (exports) {
|
|
|
6415
6601
|
'value',
|
|
6416
6602
|
'readonly:presence',
|
|
6417
6603
|
'required:presence',
|
|
6604
|
+
'placeholder',
|
|
6418
6605
|
'accept:csv',
|
|
6419
6606
|
'multiple:presence',
|
|
6420
6607
|
'itemlist:presence',
|
|
@@ -6464,7 +6651,7 @@ var fml = (function (exports) {
|
|
|
6464
6651
|
};
|
|
6465
6652
|
render(conf) {
|
|
6466
6653
|
const { observed } = conf;
|
|
6467
|
-
super.render(conf);
|
|
6654
|
+
super.render({ ...conf, skipObservedSetup: true });
|
|
6468
6655
|
this.#items = this.querySelector('ful-item-list');
|
|
6469
6656
|
this.#dropzone = this.querySelector('[data-ref=dropzone]');
|
|
6470
6657
|
this.#warnings = this.querySelector('ful-field-warnings');
|
|
@@ -6475,6 +6662,12 @@ var fml = (function (exports) {
|
|
|
6475
6662
|
this.maxfiles = observed.maxfiles;
|
|
6476
6663
|
this.maxfilesize = observed.maxfilesize;
|
|
6477
6664
|
this.maxtotalsize = observed.maxtotalsize;
|
|
6665
|
+
|
|
6666
|
+
this.disabled = conf.disabled;
|
|
6667
|
+
this.readonly = observed.readonly;
|
|
6668
|
+
this.required = observed.required;
|
|
6669
|
+
this.placeholder = observed.placeholder;
|
|
6670
|
+
this.value = observed.value;
|
|
6478
6671
|
this.#warnings.addEventListener('animationend', (e) => {
|
|
6479
6672
|
e.target.remove();
|
|
6480
6673
|
});
|
|
@@ -6482,11 +6675,17 @@ var fml = (function (exports) {
|
|
|
6482
6675
|
if (!e.target.closest('button')) {
|
|
6483
6676
|
return;
|
|
6484
6677
|
}
|
|
6485
|
-
const
|
|
6678
|
+
const idx = [...this.#items.children].indexOf(e.target.closest('ful-item'));
|
|
6679
|
+
if (idx === -1) {
|
|
6680
|
+
return;
|
|
6681
|
+
}
|
|
6486
6682
|
const dt = new DataTransfer();
|
|
6487
|
-
[...this.files]
|
|
6683
|
+
[...this.files]
|
|
6684
|
+
.filter((f, i) => i !== idx)
|
|
6685
|
+
.forEach((f) => {
|
|
6686
|
+
dt.items.add(f);
|
|
6687
|
+
});
|
|
6488
6688
|
this.files = dt.files;
|
|
6489
|
-
this.#update();
|
|
6490
6689
|
});
|
|
6491
6690
|
this.#dropzone.addEventListener('click', (e) => {
|
|
6492
6691
|
this.querySelector('input')?.click();
|
|
@@ -6497,10 +6696,15 @@ var fml = (function (exports) {
|
|
|
6497
6696
|
});
|
|
6498
6697
|
this.#dropzone.addEventListener('drop', (e) => {
|
|
6499
6698
|
e.preventDefault();
|
|
6699
|
+
const dropped = [...e.dataTransfer.items].filter((i) => i.kind === 'file');
|
|
6700
|
+
if (dropped.length === 0) {
|
|
6701
|
+
return;
|
|
6702
|
+
}
|
|
6500
6703
|
const dt = new DataTransfer();
|
|
6501
|
-
|
|
6704
|
+
dropped.forEach((i) => {
|
|
6705
|
+
dt.items.add(i.getAsFile());
|
|
6706
|
+
});
|
|
6502
6707
|
this.files = dt.files;
|
|
6503
|
-
this.#update();
|
|
6504
6708
|
});
|
|
6505
6709
|
this._input.addEventListener('change', (e) => {
|
|
6506
6710
|
this.#update();
|
|
@@ -6515,6 +6719,7 @@ var fml = (function (exports) {
|
|
|
6515
6719
|
}
|
|
6516
6720
|
#update() {
|
|
6517
6721
|
this.setCustomValidity();
|
|
6722
|
+
this.#warnings.replaceChildren();
|
|
6518
6723
|
this.#ensureAcceptable();
|
|
6519
6724
|
this.#ensureFileSizes();
|
|
6520
6725
|
this.#ensureTotalSize();
|
|
@@ -6525,7 +6730,7 @@ var fml = (function (exports) {
|
|
|
6525
6730
|
.renderTo(this.#items);
|
|
6526
6731
|
}
|
|
6527
6732
|
warning(key, args) {
|
|
6528
|
-
this.template('warning').withOverlay({ key, args }).
|
|
6733
|
+
this.template('warning').withOverlay({ key, args }).appendTo(this.#warnings);
|
|
6529
6734
|
}
|
|
6530
6735
|
#ensureAcceptable() {
|
|
6531
6736
|
if (!this.#accept.length) {
|
|
@@ -6538,10 +6743,14 @@ var fml = (function (exports) {
|
|
|
6538
6743
|
if (unacceptable.length === 0) {
|
|
6539
6744
|
return;
|
|
6540
6745
|
}
|
|
6541
|
-
this.warning('
|
|
6746
|
+
this.warning('unacceptablefiletype', this.#accept.join(', '));
|
|
6542
6747
|
const dt = new DataTransfer();
|
|
6543
|
-
[...this.files]
|
|
6544
|
-
|
|
6748
|
+
[...this.files]
|
|
6749
|
+
.filter((f) => !unacceptable.includes(f))
|
|
6750
|
+
.forEach((f) => {
|
|
6751
|
+
dt.items.add(f);
|
|
6752
|
+
});
|
|
6753
|
+
this._input.files = dt.files;
|
|
6545
6754
|
}
|
|
6546
6755
|
#ensureFilesCount() {
|
|
6547
6756
|
if (this.#maxfiles === null) {
|
|
@@ -6551,8 +6760,7 @@ var fml = (function (exports) {
|
|
|
6551
6760
|
return;
|
|
6552
6761
|
}
|
|
6553
6762
|
this.warning('maxfilesexceeded');
|
|
6554
|
-
|
|
6555
|
-
this.files = dt.files;
|
|
6763
|
+
this._input.files = new DataTransfer().files;
|
|
6556
6764
|
}
|
|
6557
6765
|
|
|
6558
6766
|
#ensureFileSizes() {
|
|
@@ -6565,8 +6773,12 @@ var fml = (function (exports) {
|
|
|
6565
6773
|
}
|
|
6566
6774
|
this.warning('maxfilesizeexceeded', this.#formatByteSize(this.#maxfilesize));
|
|
6567
6775
|
const dt = new DataTransfer();
|
|
6568
|
-
[...this.files]
|
|
6569
|
-
|
|
6776
|
+
[...this.files]
|
|
6777
|
+
.filter((f) => !oversized.includes(f))
|
|
6778
|
+
.forEach((f) => {
|
|
6779
|
+
dt.items.add(f);
|
|
6780
|
+
});
|
|
6781
|
+
this._input.files = dt.files;
|
|
6570
6782
|
}
|
|
6571
6783
|
#ensureTotalSize() {
|
|
6572
6784
|
if (this.#maxtotalsize === null) {
|
|
@@ -6577,7 +6789,7 @@ var fml = (function (exports) {
|
|
|
6577
6789
|
return;
|
|
6578
6790
|
}
|
|
6579
6791
|
this.warning('maxtotalsizeexceeded', this.#formatByteSize(this.#maxtotalsize));
|
|
6580
|
-
this.files = new DataTransfer().files;
|
|
6792
|
+
this._input.files = new DataTransfer().files;
|
|
6581
6793
|
}
|
|
6582
6794
|
get accept() {
|
|
6583
6795
|
return this.#accept;
|
|
@@ -6603,6 +6815,7 @@ var fml = (function (exports) {
|
|
|
6603
6815
|
}
|
|
6604
6816
|
set files(vs) {
|
|
6605
6817
|
this._input.files = vs;
|
|
6818
|
+
this.#update();
|
|
6606
6819
|
}
|
|
6607
6820
|
get file() {
|
|
6608
6821
|
return this.files[0] ?? null;
|
|
@@ -6623,7 +6836,6 @@ var fml = (function (exports) {
|
|
|
6623
6836
|
return;
|
|
6624
6837
|
}
|
|
6625
6838
|
this.files = new DataTransfer().files;
|
|
6626
|
-
this.#update();
|
|
6627
6839
|
}
|
|
6628
6840
|
get totalsize() {
|
|
6629
6841
|
return Array.from(this.files).reduce((a, f) => a + f.size, 0);
|
|
@@ -6790,7 +7002,7 @@ var fml = (function (exports) {
|
|
|
6790
7002
|
const http = registry.component('http-client');
|
|
6791
7003
|
const responseMapper = SelectLoader.#responseMapperFrom(el);
|
|
6792
7004
|
|
|
6793
|
-
if ('chunked'
|
|
7005
|
+
if ('chunked' === el.getAttribute('mode')) {
|
|
6794
7006
|
return new PartialRemoteLoader({
|
|
6795
7007
|
http,
|
|
6796
7008
|
url: el.getAttribute('src'),
|
|
@@ -6866,8 +7078,14 @@ var fml = (function (exports) {
|
|
|
6866
7078
|
});
|
|
6867
7079
|
this.replaceChildren(fragment);
|
|
6868
7080
|
}
|
|
7081
|
+
#selected() {
|
|
7082
|
+
return this.#menu?.querySelector('[selected]') ?? this.#menu?.firstElementChild ?? null;
|
|
7083
|
+
}
|
|
6869
7084
|
acceptSelection() {
|
|
6870
|
-
const selected = this.#
|
|
7085
|
+
const selected = this.#selected();
|
|
7086
|
+
if (!selected) {
|
|
7087
|
+
return;
|
|
7088
|
+
}
|
|
6871
7089
|
this.#change(selected);
|
|
6872
7090
|
}
|
|
6873
7091
|
update(values) {
|
|
@@ -6903,6 +7121,9 @@ var fml = (function (exports) {
|
|
|
6903
7121
|
try {
|
|
6904
7122
|
const data = await loader();
|
|
6905
7123
|
this.update(data);
|
|
7124
|
+
} catch (e) {
|
|
7125
|
+
this.hide();
|
|
7126
|
+
throw e;
|
|
6906
7127
|
} finally {
|
|
6907
7128
|
this.#spinner.setAttribute('hidden', '');
|
|
6908
7129
|
this.#menu.removeAttribute('hidden');
|
|
@@ -6910,9 +7131,9 @@ var fml = (function (exports) {
|
|
|
6910
7131
|
}
|
|
6911
7132
|
async moveOrShow(forward, loader) {
|
|
6912
7133
|
if (this.shown) {
|
|
6913
|
-
const selected = this.#
|
|
6914
|
-
const candidate = selected[`${forward ? 'next' : 'previous'}ElementSibling`];
|
|
6915
|
-
if (candidate) {
|
|
7134
|
+
const selected = this.#selected();
|
|
7135
|
+
const candidate = selected?.[`${forward ? 'next' : 'previous'}ElementSibling`];
|
|
7136
|
+
if (selected && candidate) {
|
|
6916
7137
|
selected.removeAttribute('selected');
|
|
6917
7138
|
candidate.setAttribute('selected', '');
|
|
6918
7139
|
candidate.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
@@ -6965,6 +7186,7 @@ var fml = (function (exports) {
|
|
|
6965
7186
|
#multiple;
|
|
6966
7187
|
#fieldError;
|
|
6967
7188
|
#values = new Map();
|
|
7189
|
+
#token = 0;
|
|
6968
7190
|
constructor() {
|
|
6969
7191
|
super();
|
|
6970
7192
|
this.internals = this.attachInternals();
|
|
@@ -6977,7 +7199,11 @@ var fml = (function (exports) {
|
|
|
6977
7199
|
.create(this, { options: slots.options });
|
|
6978
7200
|
|
|
6979
7201
|
this.#multiple = this.hasAttribute('multiple');
|
|
6980
|
-
|
|
7202
|
+
try {
|
|
7203
|
+
await this.#loader.prefetch?.();
|
|
7204
|
+
} catch (/** @type any */ e) {
|
|
7205
|
+
console.warn('failed to prefetch select options', this, 'reason:', e);
|
|
7206
|
+
}
|
|
6981
7207
|
const fragment = this.template().withOverlay({ slots, name }).render();
|
|
6982
7208
|
this.#input = fragment.querySelector('input');
|
|
6983
7209
|
this.#items = fragment.querySelector('ful-item-list');
|
|
@@ -6996,11 +7222,9 @@ var fml = (function (exports) {
|
|
|
6996
7222
|
this.#fieldError = fragment.querySelector('ful-field-error');
|
|
6997
7223
|
this.#input.ariaDescribedByElements = [this.#fieldError];
|
|
6998
7224
|
this.#input.ariaLabelledByElements = [label];
|
|
6999
|
-
|
|
7000
|
-
const self = this;
|
|
7001
7225
|
const [dload, abortdload] = Timing.throttle(400, () => {
|
|
7002
|
-
|
|
7003
|
-
|
|
7226
|
+
this.#input.setAttribute('aria-expanded', 'true');
|
|
7227
|
+
this.#ddmenu.show(() => this.#loader.load(this.#input.value));
|
|
7004
7228
|
});
|
|
7005
7229
|
this.addEventListener('click', (/** @type any */ e) => {
|
|
7006
7230
|
if (e.target.matches('input')) {
|
|
@@ -7067,13 +7291,13 @@ var fml = (function (exports) {
|
|
|
7067
7291
|
case 'ArrowUp': {
|
|
7068
7292
|
e.preventDefault();
|
|
7069
7293
|
this.#input.setAttribute('aria-expanded', 'true');
|
|
7070
|
-
this.#ddmenu.moveOrShow(false, () =>
|
|
7294
|
+
this.#ddmenu.moveOrShow(false, () => this.#loader.load(this.#input.value));
|
|
7071
7295
|
break;
|
|
7072
7296
|
}
|
|
7073
7297
|
case 'ArrowDown': {
|
|
7074
7298
|
e.preventDefault();
|
|
7075
7299
|
this.#input.setAttribute('aria-expanded', 'true');
|
|
7076
|
-
this.#ddmenu.moveOrShow(true, () =>
|
|
7300
|
+
this.#ddmenu.moveOrShow(true, () => this.#loader.load(this.#input.value));
|
|
7077
7301
|
break;
|
|
7078
7302
|
}
|
|
7079
7303
|
case 'Escape': {
|
|
@@ -7082,6 +7306,12 @@ var fml = (function (exports) {
|
|
|
7082
7306
|
break;
|
|
7083
7307
|
}
|
|
7084
7308
|
case 'Enter': {
|
|
7309
|
+
if (!this.#ddmenu.shown) {
|
|
7310
|
+
//nothing to accept: submit the form as ful-input does. the inner
|
|
7311
|
+
//input carries form="" so it never submits one on its own
|
|
7312
|
+
this.#requestSubmit();
|
|
7313
|
+
return;
|
|
7314
|
+
}
|
|
7085
7315
|
e.preventDefault();
|
|
7086
7316
|
this.#input.setAttribute('aria-expanded', 'false');
|
|
7087
7317
|
this.#ddmenu.acceptSelection();
|
|
@@ -7130,6 +7360,16 @@ var fml = (function (exports) {
|
|
|
7130
7360
|
async withLoader(fn) {
|
|
7131
7361
|
return await fn(this.#loader);
|
|
7132
7362
|
}
|
|
7363
|
+
#requestSubmit() {
|
|
7364
|
+
const form = this.internals.form;
|
|
7365
|
+
if (!form) {
|
|
7366
|
+
return;
|
|
7367
|
+
}
|
|
7368
|
+
const candidates = /** @type [HTMLButtonElement|HTMLInputElement] */ (
|
|
7369
|
+
Array.from(form.querySelectorAll('button:not(:disabled), input:not(:disabled)'))
|
|
7370
|
+
);
|
|
7371
|
+
form.requestSubmit(candidates.find((el) => el.type === 'submit'));
|
|
7372
|
+
}
|
|
7133
7373
|
#changed() {
|
|
7134
7374
|
const selection = [...this.#values.entries()].map((e) => ({
|
|
7135
7375
|
key: e[0],
|
|
@@ -7159,16 +7399,43 @@ var fml = (function (exports) {
|
|
|
7159
7399
|
this.template('items').withOverlay({ entries: this.#values.entries() }).renderTo(this.#items);
|
|
7160
7400
|
}
|
|
7161
7401
|
set value(vs) {
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7402
|
+
//the csvm mapper yields [] for a missing multiple value, an empty string is
|
|
7403
|
+
//left alone: it is a usable key for an <option value="">
|
|
7404
|
+
const keys = vs == null ? [] : Array.isArray(vs) ? vs : [vs];
|
|
7405
|
+
//the keys are known synchronously and are all `value` reads, so they are applied
|
|
7406
|
+
//now: only the labels need the loader, until then a key stands in for its own
|
|
7407
|
+
this.#values = new Map(keys.map((k) => [k, [k]]));
|
|
7408
|
+
this.#syncBadges();
|
|
7409
|
+
const token = ++this.#token;
|
|
7410
|
+
if (keys.length === 0) {
|
|
7165
7411
|
return;
|
|
7166
7412
|
}
|
|
7167
|
-
(
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7413
|
+
this.#resolve(keys, token);
|
|
7414
|
+
}
|
|
7415
|
+
/**
|
|
7416
|
+
* Resolves the labels of the assigned keys. A failed lookup is left to reject so
|
|
7417
|
+
* that it is reported like any other failure: the keys stay applied either way.
|
|
7418
|
+
*/
|
|
7419
|
+
async #resolve(keys, token) {
|
|
7420
|
+
const entries = await this.#loader.exact(...keys);
|
|
7421
|
+
if (token !== this.#token) {
|
|
7422
|
+
//a newer assignment has been made in the meantime
|
|
7423
|
+
return;
|
|
7424
|
+
}
|
|
7425
|
+
//label the keys that are still selected: a removal made while the lookup was in
|
|
7426
|
+
//flight must not be undone by it, and a key the loader does not know is dropped
|
|
7427
|
+
const resolved = new Map(entries.map((e) => [e[0], e.slice(1)]));
|
|
7428
|
+
for (const key of keys) {
|
|
7429
|
+
if (!this.#values.has(key)) {
|
|
7430
|
+
continue;
|
|
7431
|
+
}
|
|
7432
|
+
if (resolved.has(key)) {
|
|
7433
|
+
this.#values.set(key, resolved.get(key));
|
|
7434
|
+
} else {
|
|
7435
|
+
this.#values.delete(key);
|
|
7436
|
+
}
|
|
7437
|
+
}
|
|
7438
|
+
this.#syncBadges();
|
|
7172
7439
|
}
|
|
7173
7440
|
get value() {
|
|
7174
7441
|
if (this.#multiple) {
|
|
@@ -7187,6 +7454,11 @@ var fml = (function (exports) {
|
|
|
7187
7454
|
}
|
|
7188
7455
|
set disabled(d) {
|
|
7189
7456
|
Attributes.toggle(this.#input, 'disabled', d);
|
|
7457
|
+
//also on the host: a form associated element only matches :disabled through its
|
|
7458
|
+
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
7459
|
+
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
7460
|
+
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
7461
|
+
Attributes.toggle(this, 'disabled', d);
|
|
7190
7462
|
}
|
|
7191
7463
|
get readonly() {
|
|
7192
7464
|
return this.#input.readOnly;
|
|
@@ -7292,7 +7564,9 @@ var fml = (function (exports) {
|
|
|
7292
7564
|
return [input, label];
|
|
7293
7565
|
});
|
|
7294
7566
|
|
|
7295
|
-
radioEls.forEach((el) =>
|
|
7567
|
+
radioEls.forEach((el) => {
|
|
7568
|
+
el.remove();
|
|
7569
|
+
});
|
|
7296
7570
|
this.template().withOverlay({ name, slots, inputsAndLabels }).renderTo(this);
|
|
7297
7571
|
this.#fieldset = this.firstElementChild;
|
|
7298
7572
|
this.disabled = disabled;
|
|
@@ -7336,6 +7610,11 @@ var fml = (function (exports) {
|
|
|
7336
7610
|
}
|
|
7337
7611
|
set disabled(d) {
|
|
7338
7612
|
Attributes.toggle(this.#fieldset, 'disabled', d);
|
|
7613
|
+
//also on the host: a form associated element only matches :disabled through its
|
|
7614
|
+
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
7615
|
+
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
7616
|
+
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
7617
|
+
Attributes.toggle(this, 'disabled', d);
|
|
7339
7618
|
}
|
|
7340
7619
|
get required() {
|
|
7341
7620
|
return this.#fieldset.getAttribute('aria-required') === 'true';
|
|
@@ -7385,7 +7664,7 @@ var fml = (function (exports) {
|
|
|
7385
7664
|
this.internals.role = 'presentation';
|
|
7386
7665
|
}
|
|
7387
7666
|
render({ slots, observed, disabled }) {
|
|
7388
|
-
const isSwitch = this.getAttribute('type')
|
|
7667
|
+
const isSwitch = this.getAttribute('type') === 'switch';
|
|
7389
7668
|
const klass = isSwitch ? 'form-check form-switch' : 'form-check';
|
|
7390
7669
|
const fragment = this.template().withOverlay({ slots, klass, isSwitch }).render();
|
|
7391
7670
|
this.#container = fragment.firstElementChild;
|
|
@@ -7449,6 +7728,11 @@ var fml = (function (exports) {
|
|
|
7449
7728
|
}
|
|
7450
7729
|
set disabled(d) {
|
|
7451
7730
|
Attributes.toggle(this.#input, 'disabled', d);
|
|
7731
|
+
//also on the host: a form associated element only matches :disabled through its
|
|
7732
|
+
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
7733
|
+
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
7734
|
+
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
7735
|
+
Attributes.toggle(this, 'disabled', d);
|
|
7452
7736
|
}
|
|
7453
7737
|
get required() {
|
|
7454
7738
|
return this.#input.getAttribute('aria-required') === 'true';
|
|
@@ -7489,9 +7773,10 @@ var fml = (function (exports) {
|
|
|
7489
7773
|
class SortButton extends ParsedElement {
|
|
7490
7774
|
static observed = ['order'];
|
|
7491
7775
|
#order;
|
|
7492
|
-
render() {
|
|
7776
|
+
render({ observed }) {
|
|
7493
7777
|
const sorter = this.getAttribute('sorter');
|
|
7494
7778
|
const orders = ['asc', 'desc', null];
|
|
7779
|
+
this.order = observed.order;
|
|
7495
7780
|
this.addEventListener('click', () => {
|
|
7496
7781
|
const nextOrder = orders[(orders.indexOf(this.order) + 1) % 3];
|
|
7497
7782
|
this.dispatchEvent(
|
|
@@ -7581,10 +7866,12 @@ var fml = (function (exports) {
|
|
|
7581
7866
|
#total = 0;
|
|
7582
7867
|
#current = 0;
|
|
7583
7868
|
render({ observed }) {
|
|
7584
|
-
this.
|
|
7869
|
+
this.total = observed.total ?? 0;
|
|
7870
|
+
this.current = observed.current ?? 0;
|
|
7585
7871
|
this.addEventListener('click', (/** @type any */ evt) => {
|
|
7586
7872
|
const el = evt.target.closest('a');
|
|
7587
|
-
if (!el) {
|
|
7873
|
+
if (!el || el.classList.contains('disabled')) {
|
|
7874
|
+
//a disabled link leads nowhere: the page it would ask for does not exist
|
|
7588
7875
|
return;
|
|
7589
7876
|
}
|
|
7590
7877
|
this.dispatchEvent(
|
|
@@ -7600,25 +7887,20 @@ var fml = (function (exports) {
|
|
|
7600
7887
|
}
|
|
7601
7888
|
update(current, total) {
|
|
7602
7889
|
const maxRender = Number(this.getAttribute('pages') ?? '5');
|
|
7603
|
-
const
|
|
7890
|
+
const hasPrev = current > 0;
|
|
7891
|
+
const hasNext = current + 1 < total;
|
|
7892
|
+
//a disabled arrow carries no page: there is nothing valid for it to point at
|
|
7893
|
+
const prev = { index: hasPrev ? current - 1 : null, enabled: hasPrev };
|
|
7604
7894
|
const curr = { index: current, label: current + 1 };
|
|
7605
|
-
const next = { index:
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
if (p >= 0) {
|
|
7615
|
-
pages.unshift({ index: p, label: p + 1 });
|
|
7616
|
-
}
|
|
7617
|
-
const n = mid + offset;
|
|
7618
|
-
if (n < total) {
|
|
7619
|
-
pages.push({ index: n, label: n + 1 });
|
|
7620
|
-
}
|
|
7621
|
-
}
|
|
7895
|
+
const next = { index: hasNext ? current + 1 : null, enabled: hasNext };
|
|
7896
|
+
//the window holds at most maxRender pages, centered on the current one and slid
|
|
7897
|
+
//back towards the end so it stays full on the last pages
|
|
7898
|
+
const rendered = Math.max(1, Math.min(maxRender, total));
|
|
7899
|
+
const first = Math.max(0, Math.min(current - Math.floor((rendered - 1) / 2), total - rendered));
|
|
7900
|
+
const pages = Array.from({ length: rendered }, (_, offset) => ({
|
|
7901
|
+
index: first + offset,
|
|
7902
|
+
label: first + offset + 1,
|
|
7903
|
+
}));
|
|
7622
7904
|
this.template().withOverlay({ total, prev, curr, next, pages }).renderTo(this);
|
|
7623
7905
|
}
|
|
7624
7906
|
get total() {
|
|
@@ -7645,9 +7927,10 @@ var fml = (function (exports) {
|
|
|
7645
7927
|
|
|
7646
7928
|
class TableSchemaParser {
|
|
7647
7929
|
static parse(nodeOrFragment, template) {
|
|
7648
|
-
|
|
7930
|
+
//nodeOrFragment is undefined when the slot is missing altogether
|
|
7931
|
+
const schema = nodeOrFragment ? Nodes.queryChildren(nodeOrFragment, 'schema') : null;
|
|
7649
7932
|
if (!schema) {
|
|
7650
|
-
throw new Error(
|
|
7933
|
+
throw new Error('missing expected <schema>: ful-table needs a <template slot="schema"> holding one');
|
|
7651
7934
|
}
|
|
7652
7935
|
const headersTr = document.createElement('tr');
|
|
7653
7936
|
const rowsTr = document.createElement('tr');
|
|
@@ -7866,9 +8149,10 @@ var fml = (function (exports) {
|
|
|
7866
8149
|
this.#noAutoload = table.querySelector(':scope > tbody[data-ref=initial]');
|
|
7867
8150
|
this.#feedback = table.querySelector(':scope > tbody[data-ref=feedback]');
|
|
7868
8151
|
this.#paginator = Nodes.queryChildren(fragment, 'ful-pagination');
|
|
7869
|
-
this.#sorters = table.querySelectorAll(':scope > thead ful-sorter') ?? [];
|
|
7870
8152
|
this.replaceChildren(fragment);
|
|
7871
|
-
|
|
8153
|
+
const thead = /** @type HTMLTableSectionElement */ (this.querySelector('thead'));
|
|
8154
|
+
schema.headersTemplate.renderTo(thead);
|
|
8155
|
+
this.#sorters = thead.querySelectorAll('ful-sorter');
|
|
7872
8156
|
await Rendering.waitForChildren(this);
|
|
7873
8157
|
|
|
7874
8158
|
const maybeForm = /** @type any */ (Nodes.queryChildren(this, 'ful-form'));
|
|
@@ -7903,11 +8187,16 @@ var fml = (function (exports) {
|
|
|
7903
8187
|
this.addEventListener('sort-requested', async (/** @type any */ e) => {
|
|
7904
8188
|
const sortRequest = e.detail.value.order ? e.detail.value : null;
|
|
7905
8189
|
await this.load(this.#latestRequest.pageRequest, sortRequest, this.#latestRequest.filterRequest);
|
|
7906
|
-
this.#sorters.forEach((s) =>
|
|
8190
|
+
this.#sorters.forEach((s) => {
|
|
8191
|
+
s.order = null;
|
|
8192
|
+
});
|
|
7907
8193
|
e.target.order = e.detail.value.order;
|
|
7908
8194
|
});
|
|
7909
8195
|
if (this.hasAttribute('autoload')) {
|
|
7910
|
-
|
|
8196
|
+
//not awaited: the first load must not hold up the upgrade, and a loader that
|
|
8197
|
+
//fails or never answers must not keep ftl:ready from firing for the page.
|
|
8198
|
+
//load renders its own error state and lets the failure reject, so it is reported
|
|
8199
|
+
this.reload();
|
|
7911
8200
|
}
|
|
7912
8201
|
}
|
|
7913
8202
|
|
|
@@ -7920,7 +8209,7 @@ var fml = (function (exports) {
|
|
|
7920
8209
|
}
|
|
7921
8210
|
async load(pageRequest, sortRequest, filterRequest) {
|
|
7922
8211
|
this.#body.replaceChildren();
|
|
7923
|
-
this.#loading.removeAttribute('hidden'
|
|
8212
|
+
this.#loading.removeAttribute('hidden');
|
|
7924
8213
|
this.#feedback.setAttribute('hidden', '');
|
|
7925
8214
|
this.#noAutoload.setAttribute('hidden', '');
|
|
7926
8215
|
try {
|
|
@@ -7929,7 +8218,7 @@ var fml = (function (exports) {
|
|
|
7929
8218
|
this.#update(pageRequest, sortRequest, filterRequest, pageResponse);
|
|
7930
8219
|
} catch (/** @type any */ error) {
|
|
7931
8220
|
this.#loading.setAttribute('hidden', '');
|
|
7932
|
-
this.#feedback.removeAttribute('hidden'
|
|
8221
|
+
this.#feedback.removeAttribute('hidden');
|
|
7933
8222
|
if (!error.problems) {
|
|
7934
8223
|
this.#feedback.querySelector('[data-ref=feedback-error]').textContent = error;
|
|
7935
8224
|
} else {
|
|
@@ -7971,7 +8260,7 @@ var fml = (function (exports) {
|
|
|
7971
8260
|
}
|
|
7972
8261
|
|
|
7973
8262
|
class InstantFilter extends Input {
|
|
7974
|
-
static observed = ['value:json', 'readonly:presence', 'required:presence'];
|
|
8263
|
+
static observed = ['value:json', 'readonly:presence', 'required:presence', 'placeholder'];
|
|
7975
8264
|
static template = `
|
|
7976
8265
|
<div class="form-label">
|
|
7977
8266
|
<label>{{{{ slots.default }}}}</label>
|
|
@@ -8005,10 +8294,16 @@ var fml = (function (exports) {
|
|
|
8005
8294
|
this.#operator = this.querySelector('[data-ref=operator]');
|
|
8006
8295
|
this.#value1 = this.querySelector('[data-ref=value1]');
|
|
8007
8296
|
this.#value2 = this.querySelector('[data-ref=value2]');
|
|
8297
|
+
//Input.render only re-dispatches changes coming from the first operand
|
|
8298
|
+
this.#value2.addEventListener('change', (evt) => {
|
|
8299
|
+
evt.stopPropagation();
|
|
8300
|
+
this.#notifyChange();
|
|
8301
|
+
});
|
|
8008
8302
|
|
|
8009
8303
|
this.disabled = conf.disabled;
|
|
8010
8304
|
this.readonly = conf.observed.readonly;
|
|
8011
8305
|
this.required = conf.observed.required;
|
|
8306
|
+
this.placeholder = conf.observed.placeholder;
|
|
8012
8307
|
this.value = conf.observed.value;
|
|
8013
8308
|
|
|
8014
8309
|
this.addEventListener('click', (evt) => {
|
|
@@ -8018,9 +8313,13 @@ var fml = (function (exports) {
|
|
|
8018
8313
|
}
|
|
8019
8314
|
const btn = /** @type HTMLButtonElement */ (target.closest('ul')?.previousElementSibling);
|
|
8020
8315
|
const value = /** @type String */ (target.getAttribute('value'));
|
|
8316
|
+
const previous = btn.getAttribute('value');
|
|
8021
8317
|
Attributes.toggle(this.#value2, 'hidden', value !== 'BETWEEN');
|
|
8022
8318
|
btn.setAttribute('value', value);
|
|
8023
8319
|
btn.innerHTML = target.innerHTML;
|
|
8320
|
+
if (previous !== value) {
|
|
8321
|
+
this.#notifyChange();
|
|
8322
|
+
}
|
|
8024
8323
|
});
|
|
8025
8324
|
}
|
|
8026
8325
|
|
|
@@ -8036,14 +8335,40 @@ var fml = (function (exports) {
|
|
|
8036
8335
|
return;
|
|
8037
8336
|
}
|
|
8038
8337
|
const [operator, ...values] = v;
|
|
8039
|
-
this.#
|
|
8338
|
+
this.#showOperator(operator);
|
|
8040
8339
|
this.#value1.value = values[0] ? Instant.isoToLocal(values[0]) : values[0];
|
|
8041
8340
|
this.#value2.value = values[1] ? Instant.isoToLocal(values[1]) : values[1];
|
|
8042
8341
|
}
|
|
8342
|
+
#showOperator(operator) {
|
|
8343
|
+
this.#operator.setAttribute('value', operator);
|
|
8344
|
+
const items = Array.from(this.#operator.nextElementSibling?.querySelectorAll('li > a[value]') ?? []);
|
|
8345
|
+
const item = items.find((a) => a.getAttribute('value') === operator);
|
|
8346
|
+
if (item) {
|
|
8347
|
+
this.#operator.innerHTML = item.innerHTML;
|
|
8348
|
+
}
|
|
8349
|
+
Attributes.toggle(this.#value2, 'hidden', operator !== 'BETWEEN');
|
|
8350
|
+
}
|
|
8351
|
+
#notifyChange() {
|
|
8352
|
+
this.dispatchEvent(
|
|
8353
|
+
new CustomEvent('change', {
|
|
8354
|
+
bubbles: true,
|
|
8355
|
+
cancelable: false,
|
|
8356
|
+
detail: {
|
|
8357
|
+
value: this.value,
|
|
8358
|
+
},
|
|
8359
|
+
}),
|
|
8360
|
+
);
|
|
8361
|
+
}
|
|
8362
|
+
get readonly() {
|
|
8363
|
+
return super.readonly;
|
|
8364
|
+
}
|
|
8043
8365
|
set readonly(v) {
|
|
8044
8366
|
this.#value2.readOnly = v;
|
|
8045
8367
|
super.readonly = v;
|
|
8046
8368
|
}
|
|
8369
|
+
get disabled() {
|
|
8370
|
+
return super.disabled;
|
|
8371
|
+
}
|
|
8047
8372
|
set disabled(d) {
|
|
8048
8373
|
Attributes.toggle(this.#value2, 'disabled', d);
|
|
8049
8374
|
super.disabled = d;
|
|
@@ -8051,7 +8376,7 @@ var fml = (function (exports) {
|
|
|
8051
8376
|
}
|
|
8052
8377
|
|
|
8053
8378
|
class LocalDateFilter extends Input {
|
|
8054
|
-
static observed = ['value:json', 'readonly:presence', 'required:presence'];
|
|
8379
|
+
static observed = ['value:json', 'readonly:presence', 'required:presence', 'placeholder'];
|
|
8055
8380
|
static template = `
|
|
8056
8381
|
<div class="form-label">
|
|
8057
8382
|
<label>{{{{ slots.default }}}}</label>
|
|
@@ -8086,10 +8411,16 @@ var fml = (function (exports) {
|
|
|
8086
8411
|
this.#operator = this.querySelector('[data-ref=operator]');
|
|
8087
8412
|
this.#value1 = this.querySelector('[data-ref=value1]');
|
|
8088
8413
|
this.#value2 = this.querySelector('[data-ref=value2]');
|
|
8414
|
+
//Input.render only re-dispatches changes coming from the first operand
|
|
8415
|
+
this.#value2.addEventListener('change', (evt) => {
|
|
8416
|
+
evt.stopPropagation();
|
|
8417
|
+
this.#notifyChange();
|
|
8418
|
+
});
|
|
8089
8419
|
|
|
8090
8420
|
this.disabled = conf.disabled;
|
|
8091
8421
|
this.readonly = conf.observed.readonly;
|
|
8092
8422
|
this.required = conf.observed.required;
|
|
8423
|
+
this.placeholder = conf.observed.placeholder;
|
|
8093
8424
|
this.value = conf.observed.value;
|
|
8094
8425
|
|
|
8095
8426
|
this.addEventListener('click', (evt) => {
|
|
@@ -8099,14 +8430,18 @@ var fml = (function (exports) {
|
|
|
8099
8430
|
}
|
|
8100
8431
|
const btn = /** @type HTMLButtonElement */ (target.closest('ul')?.previousElementSibling);
|
|
8101
8432
|
const value = /** @type String */ (target.getAttribute('value'));
|
|
8433
|
+
const previous = btn.getAttribute('value');
|
|
8102
8434
|
Attributes.toggle(this.#value2, 'hidden', value !== 'BETWEEN');
|
|
8103
8435
|
btn.setAttribute('value', value);
|
|
8104
8436
|
btn.innerHTML = target.innerHTML;
|
|
8437
|
+
if (previous !== value) {
|
|
8438
|
+
this.#notifyChange();
|
|
8439
|
+
}
|
|
8105
8440
|
});
|
|
8106
8441
|
}
|
|
8107
8442
|
get value() {
|
|
8108
8443
|
const operator = this.#operator.getAttribute('value');
|
|
8109
|
-
const values = operator
|
|
8444
|
+
const values = operator === 'BETWEEN' ? [this.#value1.value, this.#value2.value] : [this.#value1.value];
|
|
8110
8445
|
return values.some((v) => v === '') ? undefined : [operator, ...values];
|
|
8111
8446
|
}
|
|
8112
8447
|
set value(v) {
|
|
@@ -8116,14 +8451,40 @@ var fml = (function (exports) {
|
|
|
8116
8451
|
return;
|
|
8117
8452
|
}
|
|
8118
8453
|
const [operator, ...values] = v;
|
|
8119
|
-
this.#
|
|
8454
|
+
this.#showOperator(operator);
|
|
8120
8455
|
this.#value1.value = values[0];
|
|
8121
8456
|
this.#value2.value = values[1];
|
|
8122
8457
|
}
|
|
8458
|
+
#showOperator(operator) {
|
|
8459
|
+
this.#operator.setAttribute('value', operator);
|
|
8460
|
+
const items = Array.from(this.#operator.nextElementSibling?.querySelectorAll('li > a[value]') ?? []);
|
|
8461
|
+
const item = items.find((a) => a.getAttribute('value') === operator);
|
|
8462
|
+
if (item) {
|
|
8463
|
+
this.#operator.innerHTML = item.innerHTML;
|
|
8464
|
+
}
|
|
8465
|
+
Attributes.toggle(this.#value2, 'hidden', operator !== 'BETWEEN');
|
|
8466
|
+
}
|
|
8467
|
+
#notifyChange() {
|
|
8468
|
+
this.dispatchEvent(
|
|
8469
|
+
new CustomEvent('change', {
|
|
8470
|
+
bubbles: true,
|
|
8471
|
+
cancelable: false,
|
|
8472
|
+
detail: {
|
|
8473
|
+
value: this.value,
|
|
8474
|
+
},
|
|
8475
|
+
}),
|
|
8476
|
+
);
|
|
8477
|
+
}
|
|
8478
|
+
get readonly() {
|
|
8479
|
+
return super.readonly;
|
|
8480
|
+
}
|
|
8123
8481
|
set readonly(v) {
|
|
8124
8482
|
this.#value2.readOnly = v;
|
|
8125
8483
|
super.readonly = v;
|
|
8126
8484
|
}
|
|
8485
|
+
get disabled() {
|
|
8486
|
+
return super.disabled;
|
|
8487
|
+
}
|
|
8127
8488
|
set disabled(d) {
|
|
8128
8489
|
Attributes.toggle(this.#value2, 'disabled', d);
|
|
8129
8490
|
super.disabled = d;
|
|
@@ -8131,7 +8492,7 @@ var fml = (function (exports) {
|
|
|
8131
8492
|
}
|
|
8132
8493
|
|
|
8133
8494
|
class TextFilter extends Input {
|
|
8134
|
-
static observed = ['value:json', 'readonly:presence', 'required:presence'];
|
|
8495
|
+
static observed = ['value:json', 'readonly:presence', 'required:presence', 'placeholder'];
|
|
8135
8496
|
static template = `
|
|
8136
8497
|
<div class="form-label">
|
|
8137
8498
|
<label>{{{{ slots.default }}}}</label>
|
|
@@ -8155,6 +8516,8 @@ var fml = (function (exports) {
|
|
|
8155
8516
|
`;
|
|
8156
8517
|
#operator;
|
|
8157
8518
|
#value;
|
|
8519
|
+
//the sensitivity has no control of its own: it is carried through from whoever set the value
|
|
8520
|
+
#sensitivity = 'IGNORE_CASE';
|
|
8158
8521
|
render(conf) {
|
|
8159
8522
|
super.render({ ...conf, skipObservedSetup: true });
|
|
8160
8523
|
|
|
@@ -8164,6 +8527,7 @@ var fml = (function (exports) {
|
|
|
8164
8527
|
this.disabled = conf.disabled;
|
|
8165
8528
|
this.readonly = conf.observed.readonly;
|
|
8166
8529
|
this.required = conf.observed.required;
|
|
8530
|
+
this.placeholder = conf.observed.placeholder;
|
|
8167
8531
|
this.value = conf.observed.value;
|
|
8168
8532
|
|
|
8169
8533
|
this.addEventListener('click', (evt) => {
|
|
@@ -8173,13 +8537,17 @@ var fml = (function (exports) {
|
|
|
8173
8537
|
}
|
|
8174
8538
|
const btn = /** @type HTMLButtonElement */ (target.closest('ul')?.previousElementSibling);
|
|
8175
8539
|
const value = /** @type String */ (target.getAttribute('value'));
|
|
8540
|
+
const previous = btn.getAttribute('value');
|
|
8176
8541
|
btn.setAttribute('value', value);
|
|
8177
8542
|
btn.innerHTML = target.innerHTML;
|
|
8543
|
+
if (previous !== value) {
|
|
8544
|
+
this.#notifyChange();
|
|
8545
|
+
}
|
|
8178
8546
|
});
|
|
8179
8547
|
}
|
|
8180
8548
|
get value() {
|
|
8181
8549
|
const operator = this.#operator.getAttribute('value');
|
|
8182
|
-
return this.#value.value === '' ? undefined : [operator,
|
|
8550
|
+
return this.#value.value === '' ? undefined : [operator, this.#sensitivity, this.#value.value];
|
|
8183
8551
|
}
|
|
8184
8552
|
set value(v) {
|
|
8185
8553
|
if (v == null) {
|
|
@@ -8187,15 +8555,35 @@ var fml = (function (exports) {
|
|
|
8187
8555
|
return;
|
|
8188
8556
|
}
|
|
8189
8557
|
const [operator, sensitivity, value] = v;
|
|
8190
|
-
this.#
|
|
8558
|
+
this.#showOperator(operator);
|
|
8559
|
+
this.#sensitivity = sensitivity ?? 'IGNORE_CASE';
|
|
8191
8560
|
this.#value.value = value;
|
|
8192
8561
|
}
|
|
8562
|
+
#showOperator(operator) {
|
|
8563
|
+
this.#operator.setAttribute('value', operator);
|
|
8564
|
+
const items = Array.from(this.#operator.nextElementSibling?.querySelectorAll('li > a[value]') ?? []);
|
|
8565
|
+
const item = items.find((a) => a.getAttribute('value') === operator);
|
|
8566
|
+
if (item) {
|
|
8567
|
+
this.#operator.innerHTML = item.innerHTML;
|
|
8568
|
+
}
|
|
8569
|
+
}
|
|
8570
|
+
#notifyChange() {
|
|
8571
|
+
this.dispatchEvent(
|
|
8572
|
+
new CustomEvent('change', {
|
|
8573
|
+
bubbles: true,
|
|
8574
|
+
cancelable: false,
|
|
8575
|
+
detail: {
|
|
8576
|
+
value: this.value,
|
|
8577
|
+
},
|
|
8578
|
+
}),
|
|
8579
|
+
);
|
|
8580
|
+
}
|
|
8193
8581
|
}
|
|
8194
8582
|
|
|
8195
8583
|
class LocalizationModule {
|
|
8196
8584
|
static t(k, ...args) {
|
|
8197
|
-
//@ts-
|
|
8198
|
-
const format = this.l10n?.[this.language]?.[k] ?? this.l10n?.
|
|
8585
|
+
//@ts-expect-error l10n and language come from the element class and the data stack
|
|
8586
|
+
const format = this.l10n?.[this.language]?.[k] ?? this.l10n?.en?.[k] ?? k;
|
|
8199
8587
|
if (args.length === 0) {
|
|
8200
8588
|
return format;
|
|
8201
8589
|
}
|
|
@@ -8278,9 +8666,11 @@ var fml = (function (exports) {
|
|
|
8278
8666
|
});
|
|
8279
8667
|
|
|
8280
8668
|
if (typeof window !== 'undefined') {
|
|
8669
|
+
// biome-ignore-start lint/complexity/useLiteralKeys: checkJs rejects unknown properties on Window
|
|
8281
8670
|
window['ftl'] = ftl;
|
|
8282
8671
|
window['httpc'] = httpc;
|
|
8283
8672
|
window['ful'] = ful;
|
|
8673
|
+
// biome-ignore-end lint/complexity/useLiteralKeys: checkJs rejects unknown properties on Window
|
|
8284
8674
|
}
|
|
8285
8675
|
|
|
8286
8676
|
exports.AsyncEvents = AsyncEvents;
|