@optionfactory/fml 8.0.2 → 8.0.3
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/custom-elements.json +11 -11
- package/dist/fml.css +1 -1
- package/dist/fml.css.map +1 -1
- package/dist/fml.d.mts +36 -13
- package/dist/fml.iife.js +104 -61
- 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 +104 -61
- package/dist/fml.mjs.map +1 -1
- package/dist/ftl.d.mts +24 -1
- package/dist/ftl.iife.js +27 -26
- 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 +27 -26
- package/dist/ftl.mjs.map +1 -1
- package/dist/ful.d.mts +12 -12
- package/dist/ful.iife.js +77 -35
- 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 +77 -35
- package/dist/ful.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +11 -11
- package/dist/web-types.json +12 -12
- package/package.json +1 -1
package/dist/fml.mjs
CHANGED
|
@@ -4694,27 +4694,30 @@ class ParsedElement extends HTMLElement {
|
|
|
4694
4694
|
}
|
|
4695
4695
|
this[attr] = this.unmarshal(attr, newValue);
|
|
4696
4696
|
}
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4697
|
+
/**
|
|
4698
|
+
* The disabled protocol follows the semantics of a native form control:
|
|
4699
|
+
*
|
|
4700
|
+
* - the `disabled` attribute on the host is the field's own claim, and nothing
|
|
4701
|
+
* but its author ever writes or removes it, in markup or through the property.
|
|
4702
|
+
* The framework never claims on the form's behalf, so there is nothing to
|
|
4703
|
+
* unclaim and nothing to lose: a field declared disabled inside a disabled
|
|
4704
|
+
* `<fieldset>` stays disabled when the fieldset comes back, exactly like a
|
|
4705
|
+
* native input keeps its attribute.
|
|
4706
|
+
* - the effective state is the claim OR a disabled fieldset ancestry, which the
|
|
4707
|
+
* platform maintains on its own: `:disabled` matches both, a disabled field is
|
|
4708
|
+
* left out of the submitted values, and the inner native controls are reached
|
|
4709
|
+
* by the ancestry as descendants of the fieldset.
|
|
4710
|
+
* - the `disabled` property reflects the claim only, like a native input's: a
|
|
4711
|
+
* field disabled by its ancestry reads `false` while `matches(':disabled')`
|
|
4712
|
+
* tells the effective state. Un-claiming inside a disabled fieldset cannot
|
|
4713
|
+
* enable the field.
|
|
4714
|
+
* - the inner controls mirror the claim and nothing else: the ancestry state is
|
|
4715
|
+
* never written anywhere, so it can never go stale, and the browser composes
|
|
4716
|
+
* the two on its own when it disables and re-enables a fieldset's descendants.
|
|
4717
|
+
*
|
|
4718
|
+
* Because of this, formDisabledCallback carries nothing the framework needs to
|
|
4719
|
+
* apply, and the protocol does not define it.
|
|
4720
|
+
*/
|
|
4718
4721
|
async upgrade() {
|
|
4719
4722
|
if (this.#parsed) {
|
|
4720
4723
|
return;
|
|
@@ -4727,11 +4730,9 @@ class ParsedElement extends HTMLElement {
|
|
|
4727
4730
|
this.unmarshal(attribute, this.getAttribute(attribute)),
|
|
4728
4731
|
]),
|
|
4729
4732
|
);
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
this.#unclaimFormDisabled();
|
|
4734
|
-
}
|
|
4733
|
+
//the declared claim is what render receives: the ancestry state is not
|
|
4734
|
+
//passed around, it is already where it needs to be
|
|
4735
|
+
await this.render({ slots, observed, disabled: this.hasAttribute('disabled') });
|
|
4735
4736
|
}
|
|
4736
4737
|
render(c) {}
|
|
4737
4738
|
reflect(fn) {
|
|
@@ -6328,15 +6329,17 @@ class Input extends ParsedElement {
|
|
|
6328
6329
|
});
|
|
6329
6330
|
}
|
|
6330
6331
|
get disabled() {
|
|
6331
|
-
|
|
6332
|
+
//the claim only, like a native input: the effective state, claim or disabled
|
|
6333
|
+
//ancestry, is what :disabled matches
|
|
6334
|
+
return this.hasAttribute('disabled');
|
|
6332
6335
|
}
|
|
6333
6336
|
set disabled(d) {
|
|
6334
|
-
|
|
6335
|
-
//also on the host: a form associated element only matches :disabled through its
|
|
6336
|
-
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
6337
|
-
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
6338
|
-
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
6337
|
+
//the claim belongs to the author alone, nothing else ever writes it
|
|
6339
6338
|
Attributes.toggle(this, 'disabled', d);
|
|
6339
|
+
//the inner control carries the claim as a native input would: a disabled
|
|
6340
|
+
//fieldset ancestry is left to the browser, which reaches the inner control
|
|
6341
|
+
//as a descendant of the fieldset and re-enables it on its own
|
|
6342
|
+
Attributes.toggle(this._input, 'disabled', d);
|
|
6340
6343
|
}
|
|
6341
6344
|
get required() {
|
|
6342
6345
|
return this._input.getAttribute('aria-required') === 'true';
|
|
@@ -7256,7 +7259,9 @@ class Select extends ParsedElement {
|
|
|
7256
7259
|
if (e.target.matches('input')) {
|
|
7257
7260
|
return;
|
|
7258
7261
|
}
|
|
7259
|
-
|
|
7262
|
+
//badges and other chrome are not form controls, the guard must ask the
|
|
7263
|
+
//effective state
|
|
7264
|
+
if (this.matches(':disabled') || this.readonly) {
|
|
7260
7265
|
return;
|
|
7261
7266
|
}
|
|
7262
7267
|
if (this.#ddmenu.shown) {
|
|
@@ -7272,7 +7277,7 @@ class Select extends ParsedElement {
|
|
|
7272
7277
|
if (!e.target.closest('button')) {
|
|
7273
7278
|
return;
|
|
7274
7279
|
}
|
|
7275
|
-
if (this.disabled || this.readonly) {
|
|
7280
|
+
if (this.matches(':disabled') || this.readonly) {
|
|
7276
7281
|
return;
|
|
7277
7282
|
}
|
|
7278
7283
|
const idx = [...this.#items.children].indexOf(e.target.closest('ful-item'));
|
|
@@ -7285,7 +7290,7 @@ class Select extends ParsedElement {
|
|
|
7285
7290
|
});
|
|
7286
7291
|
this.#badges.addEventListener('click', (e) => {
|
|
7287
7292
|
e.stopPropagation();
|
|
7288
|
-
if (this.disabled || this.readonly) {
|
|
7293
|
+
if (this.matches(':disabled') || this.readonly) {
|
|
7289
7294
|
return;
|
|
7290
7295
|
}
|
|
7291
7296
|
const idx = [...this.#badges.children].indexOf(e.target);
|
|
@@ -7310,7 +7315,7 @@ class Select extends ParsedElement {
|
|
|
7310
7315
|
this.#input.value = '';
|
|
7311
7316
|
});
|
|
7312
7317
|
this.#input.addEventListener('keydown', (e) => {
|
|
7313
|
-
if (this.disabled || this.readonly) {
|
|
7318
|
+
if (this.matches(':disabled') || this.readonly) {
|
|
7314
7319
|
return;
|
|
7315
7320
|
}
|
|
7316
7321
|
switch (e.code) {
|
|
@@ -7363,7 +7368,7 @@ class Select extends ParsedElement {
|
|
|
7363
7368
|
});
|
|
7364
7369
|
this.#input.addEventListener('input', (e) => {
|
|
7365
7370
|
e.stopPropagation();
|
|
7366
|
-
if (this.disabled || this.readonly) {
|
|
7371
|
+
if (this.matches(':disabled') || this.readonly) {
|
|
7367
7372
|
return;
|
|
7368
7373
|
}
|
|
7369
7374
|
dload();
|
|
@@ -7373,7 +7378,7 @@ class Select extends ParsedElement {
|
|
|
7373
7378
|
if (!this.#multiple) {
|
|
7374
7379
|
this.#values.clear();
|
|
7375
7380
|
}
|
|
7376
|
-
this.#values.set(e.detail.data[0], e.detail.data.slice(1));
|
|
7381
|
+
this.#values.set(this.#coerceKey(e.detail.data[0]), e.detail.data.slice(1));
|
|
7377
7382
|
this.#changed();
|
|
7378
7383
|
this.#syncBadges();
|
|
7379
7384
|
this.#input.focus();
|
|
@@ -7424,10 +7429,36 @@ class Select extends ParsedElement {
|
|
|
7424
7429
|
this.#items.replaceChildren();
|
|
7425
7430
|
this.template('items').withOverlay({ entries: this.#values.entries() }).renderTo(this.#items);
|
|
7426
7431
|
}
|
|
7432
|
+
/**
|
|
7433
|
+
* Coerces a key to the type declared by `k-type`. Keys reach the element from
|
|
7434
|
+
* both worlds: the `value` attribute is text, a loader returns whatever its
|
|
7435
|
+
* endpoint carries. One canonical type keeps the internal Map, which compares
|
|
7436
|
+
* keys strictly, consistent. A key that does not decode is left as it is.
|
|
7437
|
+
*/
|
|
7438
|
+
#coerceKey(k) {
|
|
7439
|
+
switch (this.getAttribute('k-type')) {
|
|
7440
|
+
case 'number': {
|
|
7441
|
+
const n = k === '' ? Number.NaN : Number(k);
|
|
7442
|
+
return Number.isNaN(n) ? k : n;
|
|
7443
|
+
}
|
|
7444
|
+
case 'boolean': {
|
|
7445
|
+
if (k === true || k === 'true') {
|
|
7446
|
+
return true;
|
|
7447
|
+
}
|
|
7448
|
+
if (k === false || k === 'false') {
|
|
7449
|
+
return false;
|
|
7450
|
+
}
|
|
7451
|
+
return k;
|
|
7452
|
+
}
|
|
7453
|
+
default:
|
|
7454
|
+
return String(k);
|
|
7455
|
+
}
|
|
7456
|
+
}
|
|
7457
|
+
|
|
7427
7458
|
set value(vs) {
|
|
7428
7459
|
//the csvm mapper yields [] for a missing multiple value, an empty string is
|
|
7429
7460
|
//left alone: it is a usable key for an <option value="">
|
|
7430
|
-
const keys = vs == null ? [] : Array.isArray(vs) ? vs : [vs];
|
|
7461
|
+
const keys = (vs == null ? [] : Array.isArray(vs) ? vs : [vs]).map((k) => this.#coerceKey(k));
|
|
7431
7462
|
//the keys are known synchronously and are all `value` reads, so they are applied
|
|
7432
7463
|
//now: only the labels need the loader, until then a key stands in for its own
|
|
7433
7464
|
this.#values = new Map(keys.map((k) => [k, [k]]));
|
|
@@ -7450,7 +7481,8 @@ class Select extends ParsedElement {
|
|
|
7450
7481
|
}
|
|
7451
7482
|
//label the keys that are still selected: a removal made while the lookup was in
|
|
7452
7483
|
//flight must not be undone by it, and a key the loader does not know is dropped
|
|
7453
|
-
|
|
7484
|
+
//the loader keys are coerced too, so they line up with the assigned ones
|
|
7485
|
+
const resolved = new Map(entries.map((e) => [this.#coerceKey(e[0]), e.slice(1)]));
|
|
7454
7486
|
for (const key of keys) {
|
|
7455
7487
|
if (!this.#values.has(key)) {
|
|
7456
7488
|
continue;
|
|
@@ -7476,15 +7508,17 @@ class Select extends ParsedElement {
|
|
|
7476
7508
|
return [...this.#values.entries()][0] ?? null;
|
|
7477
7509
|
}
|
|
7478
7510
|
get disabled() {
|
|
7479
|
-
|
|
7511
|
+
//the claim only, like a native input: the effective state, claim or disabled
|
|
7512
|
+
//ancestry, is what :disabled matches
|
|
7513
|
+
return this.hasAttribute('disabled');
|
|
7480
7514
|
}
|
|
7481
7515
|
set disabled(d) {
|
|
7482
|
-
|
|
7483
|
-
//also on the host: a form associated element only matches :disabled through its
|
|
7484
|
-
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
7485
|
-
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
7486
|
-
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
7516
|
+
//the claim belongs to the author alone, nothing else ever writes it
|
|
7487
7517
|
Attributes.toggle(this, 'disabled', d);
|
|
7518
|
+
//the inner control carries the claim as a native input would: a disabled
|
|
7519
|
+
//fieldset ancestry is left to the browser, which reaches the inner control
|
|
7520
|
+
//as a descendant of the fieldset and re-enables it on its own
|
|
7521
|
+
Attributes.toggle(this.#input, 'disabled', d);
|
|
7488
7522
|
}
|
|
7489
7523
|
get readonly() {
|
|
7490
7524
|
return this.#input.readOnly;
|
|
@@ -7632,15 +7666,17 @@ class RadioGroup extends ParsedElement {
|
|
|
7632
7666
|
});
|
|
7633
7667
|
}
|
|
7634
7668
|
get disabled() {
|
|
7635
|
-
|
|
7669
|
+
//the claim only, like a native input: the effective state, claim or disabled
|
|
7670
|
+
//ancestry, is what :disabled matches
|
|
7671
|
+
return this.hasAttribute('disabled');
|
|
7636
7672
|
}
|
|
7637
7673
|
set disabled(d) {
|
|
7638
|
-
|
|
7639
|
-
//also on the host: a form associated element only matches :disabled through its
|
|
7640
|
-
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
7641
|
-
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
7642
|
-
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
7674
|
+
//the claim belongs to the author alone, nothing else ever writes it
|
|
7643
7675
|
Attributes.toggle(this, 'disabled', d);
|
|
7676
|
+
//the group disables through its own fieldset, which carries the claim like
|
|
7677
|
+
//a native input would: a disabled outer ancestry is left to the browser,
|
|
7678
|
+
//which reaches the radios as descendants and re-enables them on its own
|
|
7679
|
+
this.#fieldset.disabled = d;
|
|
7644
7680
|
}
|
|
7645
7681
|
get required() {
|
|
7646
7682
|
return this.#fieldset.getAttribute('aria-required') === 'true';
|
|
@@ -7715,7 +7751,8 @@ class Checkbox extends ParsedElement {
|
|
|
7715
7751
|
const label = fragment.querySelector('label');
|
|
7716
7752
|
label.addEventListener('click', () => {
|
|
7717
7753
|
this.focus();
|
|
7718
|
-
|
|
7754
|
+
//a label is not a form control, the guard must ask the effective state
|
|
7755
|
+
if (this.matches(':disabled') || this.readonly) {
|
|
7719
7756
|
return;
|
|
7720
7757
|
}
|
|
7721
7758
|
this.value = !this.value;
|
|
@@ -7750,15 +7787,17 @@ class Checkbox extends ParsedElement {
|
|
|
7750
7787
|
});
|
|
7751
7788
|
}
|
|
7752
7789
|
get disabled() {
|
|
7753
|
-
|
|
7790
|
+
//the claim only, like a native input: the effective state, claim or disabled
|
|
7791
|
+
//ancestry, is what :disabled matches
|
|
7792
|
+
return this.hasAttribute('disabled');
|
|
7754
7793
|
}
|
|
7755
7794
|
set disabled(d) {
|
|
7756
|
-
|
|
7757
|
-
//also on the host: a form associated element only matches :disabled through its
|
|
7758
|
-
//own attribute, and that is what keeps it out of the submitted values. no reflect
|
|
7759
|
-
//is needed, disabled is deliberately not observed: the platform delivers it
|
|
7760
|
-
//through formDisabledCallback, which also covers a disabled ancestor fieldset
|
|
7795
|
+
//the claim belongs to the author alone, nothing else ever writes it
|
|
7761
7796
|
Attributes.toggle(this, 'disabled', d);
|
|
7797
|
+
//the inner control carries the claim as a native input would: a disabled
|
|
7798
|
+
//fieldset ancestry is left to the browser, which reaches the inner control
|
|
7799
|
+
//as a descendant of the fieldset and re-enables it on its own
|
|
7800
|
+
Attributes.toggle(this.#input, 'disabled', d);
|
|
7762
7801
|
}
|
|
7763
7802
|
get required() {
|
|
7764
7803
|
return this.#input.getAttribute('aria-required') === 'true';
|
|
@@ -8396,8 +8435,10 @@ class InstantFilter extends Input {
|
|
|
8396
8435
|
return super.disabled;
|
|
8397
8436
|
}
|
|
8398
8437
|
set disabled(d) {
|
|
8399
|
-
|
|
8438
|
+
//the claim and the first operand are the base's, the second operand mirrors
|
|
8439
|
+
//the claim like the first one does
|
|
8400
8440
|
super.disabled = d;
|
|
8441
|
+
Attributes.toggle(this.#value2, 'disabled', d);
|
|
8401
8442
|
}
|
|
8402
8443
|
}
|
|
8403
8444
|
|
|
@@ -8512,8 +8553,10 @@ class LocalDateFilter extends Input {
|
|
|
8512
8553
|
return super.disabled;
|
|
8513
8554
|
}
|
|
8514
8555
|
set disabled(d) {
|
|
8515
|
-
|
|
8556
|
+
//the claim and the first operand are the base's, the second operand mirrors
|
|
8557
|
+
//the claim like the first one does
|
|
8516
8558
|
super.disabled = d;
|
|
8559
|
+
Attributes.toggle(this.#value2, 'disabled', d);
|
|
8517
8560
|
}
|
|
8518
8561
|
}
|
|
8519
8562
|
|