@optionfactory/fml 9.0.0-rc16 → 9.0.0-rc18
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 +17 -3
- package/dist/fml.css +9 -9
- package/dist/fml.css.map +1 -1
- package/dist/fml.iife.js +138 -38
- 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/ful.css +9 -9
- package/dist/ful.css.map +1 -1
- package/dist/ful.d.mts +43 -13
- package/dist/ful.iife.js +138 -38
- 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 +138 -38
- package/dist/ful.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +1 -1
- package/dist/web-types.json +12 -4
- package/package.json +1 -1
package/dist/fml.iife.js
CHANGED
|
@@ -7848,6 +7848,8 @@ var fml = (function (exports) {
|
|
|
7848
7848
|
|
|
7849
7849
|
const INPUT_MODES = { numeric: 'numeric', decimal: 'decimal' };
|
|
7850
7850
|
|
|
7851
|
+
const SEGMENTED = new Set(['date', 'time', 'datetime-local', 'month', 'week']);
|
|
7852
|
+
|
|
7851
7853
|
const signed = (v, digits) => (v.startsWith('-') ? '-' : '') + digits(v);
|
|
7852
7854
|
|
|
7853
7855
|
const NUMERIC_DIGITS = {
|
|
@@ -7909,14 +7911,18 @@ var fml = (function (exports) {
|
|
|
7909
7911
|
* `autocomplete` attribute of the nearest enclosing `<form>`, which the
|
|
7910
7912
|
* platform does not pass on because the control carries `form=""`.
|
|
7911
7913
|
* - `uppercase`, `trim` and `v-type` shape the value as it is read; see `value`.
|
|
7912
|
-
*
|
|
7913
|
-
*
|
|
7914
|
+
* - The control's native `change` is stopped and the field dispatches its own
|
|
7915
|
+
* once the user commits a new value, as a text input does. A date, time,
|
|
7916
|
+
* datetime-local, month or week control fires its native `change` while its
|
|
7917
|
+
* segments are typed, as soon as they make a valid value; for those the field
|
|
7918
|
+
* reports when the control is left or Enter is pressed, at once for a value
|
|
7919
|
+
* picked from the browser's picker, and never a half-typed value.
|
|
7914
7920
|
*
|
|
7915
7921
|
* A subclass template may render more than one control, as the compare filters
|
|
7916
7922
|
* render the two bounds of a range: the first is `_input`, the one the field is
|
|
7917
|
-
* named by
|
|
7918
|
-
*
|
|
7919
|
-
*
|
|
7923
|
+
* named by, and every one of them gets the inputmode, `autocomplete`, the
|
|
7924
|
+
* `input-` passthrough, the keystroke filter, the placeholder and the `change`
|
|
7925
|
+
* reporting. The passthrough leaves `input-id` to the first alone, so no
|
|
7920
7926
|
* two controls share an id.
|
|
7921
7927
|
*/
|
|
7922
7928
|
class Input extends Field {
|
|
@@ -7983,10 +7989,6 @@ var fml = (function (exports) {
|
|
|
7983
7989
|
for (const control of this.#controls) {
|
|
7984
7990
|
this.#configure(control, mode, autocomplete, strip);
|
|
7985
7991
|
}
|
|
7986
|
-
this._input.addEventListener('change', (evt) => {
|
|
7987
|
-
evt.stopPropagation();
|
|
7988
|
-
this._notifyChange();
|
|
7989
|
-
});
|
|
7990
7992
|
return {
|
|
7991
7993
|
fragment,
|
|
7992
7994
|
control: this._input,
|
|
@@ -8010,6 +8012,7 @@ var fml = (function (exports) {
|
|
|
8010
8012
|
if (control !== this._input) {
|
|
8011
8013
|
Attributes.set(control, 'id', id || null);
|
|
8012
8014
|
}
|
|
8015
|
+
this.#reportChanges(control);
|
|
8013
8016
|
control.addEventListener('input', () => {
|
|
8014
8017
|
if (!strip) {
|
|
8015
8018
|
return;
|
|
@@ -8028,14 +8031,55 @@ var fml = (function (exports) {
|
|
|
8028
8031
|
control.setSelectionRange(caret, caret);
|
|
8029
8032
|
});
|
|
8030
8033
|
}
|
|
8034
|
+
/** @param {HTMLInputElement|HTMLTextAreaElement} control */
|
|
8035
|
+
#reportChanges(control) {
|
|
8036
|
+
if (!SEGMENTED.has(control.type)) {
|
|
8037
|
+
control.addEventListener('change', (evt) => {
|
|
8038
|
+
evt.stopPropagation();
|
|
8039
|
+
this._notifyChange();
|
|
8040
|
+
});
|
|
8041
|
+
return;
|
|
8042
|
+
}
|
|
8043
|
+
/** @type {string|null} */
|
|
8044
|
+
let committed = null;
|
|
8045
|
+
let typed = false;
|
|
8046
|
+
const commit = () => {
|
|
8047
|
+
typed = false;
|
|
8048
|
+
const now = JSON.stringify(this.value);
|
|
8049
|
+
if (now === committed) {
|
|
8050
|
+
return;
|
|
8051
|
+
}
|
|
8052
|
+
committed = now;
|
|
8053
|
+
this._notifyChange();
|
|
8054
|
+
};
|
|
8055
|
+
control.addEventListener('focus', () => {
|
|
8056
|
+
committed = JSON.stringify(this.value);
|
|
8057
|
+
});
|
|
8058
|
+
control.addEventListener('keydown', (/** @type any */ evt) => {
|
|
8059
|
+
if (evt.key === 'Enter' && !evt.isComposing) {
|
|
8060
|
+
commit();
|
|
8061
|
+
return;
|
|
8062
|
+
}
|
|
8063
|
+
typed = true;
|
|
8064
|
+
});
|
|
8065
|
+
control.addEventListener('blur', commit);
|
|
8066
|
+
control.addEventListener('change', (evt) => {
|
|
8067
|
+
evt.stopPropagation();
|
|
8068
|
+
if (!typed) {
|
|
8069
|
+
commit();
|
|
8070
|
+
}
|
|
8071
|
+
});
|
|
8072
|
+
}
|
|
8031
8073
|
/**
|
|
8032
8074
|
* The control's text, shaped in this order: upper cased under `uppercase`,
|
|
8033
8075
|
* trimmed under `trim`, `null` when that leaves it empty, every `,` read as
|
|
8034
8076
|
* `.` for `type="decimal"`, and decoded to a number under
|
|
8035
8077
|
* `v-type="number"`, where text that does not decode is answered as it is.
|
|
8036
8078
|
* Writing `null`, `undefined` or `''` empties the control; anything else is
|
|
8037
|
-
* written as its string.
|
|
8038
|
-
*
|
|
8079
|
+
* written as its string. Writing the text the control already holds leaves
|
|
8080
|
+
* it untouched, so a date or time control keeps the segments being typed.
|
|
8081
|
+
* Answers a `string`, a `number` or `null`; typed `any` because subclasses
|
|
8082
|
+
* answer other values.
|
|
8039
8083
|
* @type {any}
|
|
8040
8084
|
*/
|
|
8041
8085
|
get value() {
|
|
@@ -8055,7 +8099,11 @@ var fml = (function (exports) {
|
|
|
8055
8099
|
return normalized;
|
|
8056
8100
|
}
|
|
8057
8101
|
set value(value) {
|
|
8058
|
-
|
|
8102
|
+
const text = value === '' || value === undefined || value === null ? '' : `${value}`;
|
|
8103
|
+
if (this._input.value === text) {
|
|
8104
|
+
return;
|
|
8105
|
+
}
|
|
8106
|
+
this._input.value = text;
|
|
8059
8107
|
}
|
|
8060
8108
|
/**
|
|
8061
8109
|
* The control's placeholder, `null` when there is none. The control always
|
|
@@ -8324,7 +8372,7 @@ var fml = (function (exports) {
|
|
|
8324
8372
|
}
|
|
8325
8373
|
/** @param {string|null|undefined} v an ISO instant; a falsy value empties the input */
|
|
8326
8374
|
set value(v) {
|
|
8327
|
-
|
|
8375
|
+
super.value = v ? Instant.isoToLocal(v) : '';
|
|
8328
8376
|
}
|
|
8329
8377
|
/**
|
|
8330
8378
|
* The lower bound as a UTC ISO instant.
|
|
@@ -8357,7 +8405,6 @@ var fml = (function (exports) {
|
|
|
8357
8405
|
}
|
|
8358
8406
|
/** @param {string|null} v */
|
|
8359
8407
|
set step(v) {
|
|
8360
|
-
|
|
8361
8408
|
this._input.step = v ?? '';
|
|
8362
8409
|
}
|
|
8363
8410
|
}
|
|
@@ -10234,6 +10281,15 @@ var fml = (function (exports) {
|
|
|
10234
10281
|
#query() {
|
|
10235
10282
|
return this.#editing ? this.#input.value : '';
|
|
10236
10283
|
}
|
|
10284
|
+
/**
|
|
10285
|
+
* The text typed to search the vocabulary: what the dropdown was last
|
|
10286
|
+
* asked to match. `''` while the field is not being searched, its control
|
|
10287
|
+
* then showing the selection's label rather than a query.
|
|
10288
|
+
* @returns {string}
|
|
10289
|
+
*/
|
|
10290
|
+
get query() {
|
|
10291
|
+
return this.#query();
|
|
10292
|
+
}
|
|
10237
10293
|
#display() {
|
|
10238
10294
|
const entry = this.#values.values().next().value;
|
|
10239
10295
|
this.#input.value = this.#multiple ? '' : (entry?.label ?? '');
|
|
@@ -11910,10 +11966,6 @@ var fml = (function (exports) {
|
|
|
11910
11966
|
},
|
|
11911
11967
|
});
|
|
11912
11968
|
this.operators = this.declared('operators');
|
|
11913
|
-
this._value2.addEventListener('change', (evt) => {
|
|
11914
|
-
evt.stopPropagation();
|
|
11915
|
-
this._notifyChange();
|
|
11916
|
-
});
|
|
11917
11969
|
if (this._operator.value === null) {
|
|
11918
11970
|
this._showDefaultOperator();
|
|
11919
11971
|
}
|
|
@@ -12391,9 +12443,7 @@ var fml = (function (exports) {
|
|
|
12391
12443
|
*/
|
|
12392
12444
|
get criterion() {
|
|
12393
12445
|
const token = this._value.value;
|
|
12394
|
-
return asCriterion(labelTextOf(this), this._operator.value, [
|
|
12395
|
-
token === '' ? '' : booleanValueLabel(token),
|
|
12396
|
-
]);
|
|
12446
|
+
return asCriterion(labelTextOf(this), this._operator.value, [token === '' ? '' : booleanValueLabel(token)]);
|
|
12397
12447
|
}
|
|
12398
12448
|
/**
|
|
12399
12449
|
* The field's own disabled claim. Setting it disables the value button and
|
|
@@ -12682,6 +12732,18 @@ var fml = (function (exports) {
|
|
|
12682
12732
|
get section() {
|
|
12683
12733
|
return this.#section;
|
|
12684
12734
|
}
|
|
12735
|
+
/**
|
|
12736
|
+
* Dispatches the dismissal on the host and calls `close` unless a listener
|
|
12737
|
+
* prevented it. A dismissal that is not cancelable announces a close the
|
|
12738
|
+
* platform makes whatever the page does, so `close` is not called for it.
|
|
12739
|
+
* @param {CustomEvent} asked
|
|
12740
|
+
* @param {() => void} close
|
|
12741
|
+
*/
|
|
12742
|
+
dismiss(asked, close) {
|
|
12743
|
+
if (this.#host.dispatchEvent(asked) && asked.cancelable) {
|
|
12744
|
+
close();
|
|
12745
|
+
}
|
|
12746
|
+
}
|
|
12685
12747
|
/**
|
|
12686
12748
|
* Calls `dismiss` when a press and its release both land on the backdrop,
|
|
12687
12749
|
* so a selection dragged out of the panel and released outside does not.
|
|
@@ -12820,6 +12882,18 @@ var fml = (function (exports) {
|
|
|
12820
12882
|
* and a press and release both on the backdrop close the dialog, each as a
|
|
12821
12883
|
* dismissal, as Escape does.
|
|
12822
12884
|
*
|
|
12885
|
+
* Before any of those three gestures closes it, the element dispatches a
|
|
12886
|
+
* cancelable, non-bubbling `dialog:dismiss` with `detail: { reason }`, the
|
|
12887
|
+
* reason being `button`, `backdrop` or `escape`; a listener calling
|
|
12888
|
+
* `preventDefault()` keeps it open, to guard unsaved edits. A `data-result`
|
|
12889
|
+
* button, `close-on-submit` and `close()` answer rather than dismiss, and
|
|
12890
|
+
* dispatch none.
|
|
12891
|
+
*
|
|
12892
|
+
* The platform lets a page refuse Escape once per user interaction: a second
|
|
12893
|
+
* Escape with no click or keystroke in between closes the dialog whatever the
|
|
12894
|
+
* page does, `requires-answer` included. That close is still announced by a
|
|
12895
|
+
* `dialog:dismiss`, which is then not cancelable.
|
|
12896
|
+
*
|
|
12823
12897
|
* `requires-answer` withholds the close button and refuses Escape and the
|
|
12824
12898
|
* backdrop, so the dialog is closed only by a button carrying `data-result`
|
|
12825
12899
|
* or from code through `close()`.
|
|
@@ -12892,21 +12966,32 @@ var fml = (function (exports) {
|
|
|
12892
12966
|
this.#dialog.close(result);
|
|
12893
12967
|
}
|
|
12894
12968
|
});
|
|
12969
|
+
const dismiss = (/** @type {'button'|'backdrop'|'escape'} */ reason, platformCloses = false) =>
|
|
12970
|
+
this.#sections.dismiss(
|
|
12971
|
+
new CustomEvent('dialog:dismiss', { cancelable: !platformCloses, detail: { reason } }),
|
|
12972
|
+
() => this.#dialog.close(''),
|
|
12973
|
+
);
|
|
12895
12974
|
if (!requiresAnswer) {
|
|
12896
|
-
this.#sections.onBackdrop(() =>
|
|
12975
|
+
this.#sections.onBackdrop(() => dismiss('backdrop'));
|
|
12897
12976
|
}
|
|
12898
|
-
fragment
|
|
12899
|
-
.querySelector('[data-ref=close]')
|
|
12900
|
-
?.addEventListener('click', () => this.#dialog.close(''));
|
|
12977
|
+
fragment.querySelector('[data-ref=close]')?.addEventListener('click', () => dismiss('button'));
|
|
12901
12978
|
if (closeOnSubmit) {
|
|
12902
12979
|
this.#sections.onSubmitted((response) => {
|
|
12903
12980
|
this.#answer = { dismissed: false, result: null, response };
|
|
12904
12981
|
this.#dialog.close('submitted');
|
|
12905
12982
|
});
|
|
12906
12983
|
}
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12984
|
+
this.#dialog.addEventListener('cancel', (/** @type any */ e) => {
|
|
12985
|
+
if (!e.cancelable) {
|
|
12986
|
+
this.#dialog.returnValue = '';
|
|
12987
|
+
dismiss('escape', true);
|
|
12988
|
+
return;
|
|
12989
|
+
}
|
|
12990
|
+
e.preventDefault();
|
|
12991
|
+
if (!requiresAnswer) {
|
|
12992
|
+
dismiss('escape');
|
|
12993
|
+
}
|
|
12994
|
+
});
|
|
12910
12995
|
this.replaceChildren(fragment);
|
|
12911
12996
|
wireTargets();
|
|
12912
12997
|
}
|
|
@@ -13107,7 +13192,14 @@ var fml = (function (exports) {
|
|
|
13107
13192
|
* default, when absent) or `start`, following the writing direction.
|
|
13108
13193
|
*
|
|
13109
13194
|
* The close button, Escape, and a press and release both on the backdrop all
|
|
13110
|
-
* go through `close()
|
|
13195
|
+
* go through `close()`, once a cancelable, non-bubbling `drawer:dismiss` with
|
|
13196
|
+
* `detail: { reason }` (`button`, `backdrop` or `escape`) has been dispatched
|
|
13197
|
+
* on the element and no listener prevented it: `preventDefault()` keeps the
|
|
13198
|
+
* drawer open, to guard unsaved edits. `close()` and `close-on-submit`
|
|
13199
|
+
* dispatch none. The platform lets a page refuse Escape once per user
|
|
13200
|
+
* interaction: a second Escape with no click or keystroke in between closes
|
|
13201
|
+
* the drawer at once, without the slide, and its `drawer:dismiss` is then not
|
|
13202
|
+
* cancelable. Every close dispatches a non-bubbling `close` event on
|
|
13111
13203
|
* the element with `detail: { dismissed, response }`: `{ dismissed: false,
|
|
13112
13204
|
* response }` when a form closed it under `close-on-submit`, and
|
|
13113
13205
|
* `{ dismissed: true, response: null }` for any other close.
|
|
@@ -13163,13 +13255,23 @@ var fml = (function (exports) {
|
|
|
13163
13255
|
if (placement) {
|
|
13164
13256
|
this.#dialog.setAttribute('placement', placement);
|
|
13165
13257
|
}
|
|
13258
|
+
const dismiss = (/** @type {'button'|'backdrop'|'escape'} */ reason, platformCloses = false) =>
|
|
13259
|
+
this.#sections.dismiss(
|
|
13260
|
+
new CustomEvent('drawer:dismiss', { cancelable: !platformCloses, detail: { reason } }),
|
|
13261
|
+
() => this.close(),
|
|
13262
|
+
);
|
|
13166
13263
|
/** @type {HTMLElement} */ (fragment.querySelector('[data-ref=close]')).addEventListener('click', () =>
|
|
13167
|
-
|
|
13264
|
+
dismiss('button'),
|
|
13168
13265
|
);
|
|
13169
|
-
this.#sections.onBackdrop(() =>
|
|
13266
|
+
this.#sections.onBackdrop(() => dismiss('backdrop'));
|
|
13170
13267
|
this.#dialog.addEventListener('cancel', (e) => {
|
|
13268
|
+
if (!e.cancelable) {
|
|
13269
|
+
this.#stopSlidingOut();
|
|
13270
|
+
dismiss('escape', true);
|
|
13271
|
+
return;
|
|
13272
|
+
}
|
|
13171
13273
|
e.preventDefault();
|
|
13172
|
-
|
|
13274
|
+
dismiss('escape');
|
|
13173
13275
|
});
|
|
13174
13276
|
this.#dialog.addEventListener('close', () => {
|
|
13175
13277
|
this.dispatchEvent(
|
|
@@ -13852,11 +13954,9 @@ var fml = (function (exports) {
|
|
|
13852
13954
|
}
|
|
13853
13955
|
/**
|
|
13854
13956
|
* Moves to the section carrying `data-step="<ref>"`. A move makes that step
|
|
13855
|
-
* and section current, moves the focus to the section, dispatches
|
|
13856
|
-
* on the host (not bubbling) with detail `{ index, step }`,
|
|
13857
|
-
* section requests for it.
|
|
13858
|
-
* bubbles through the host under the same name, so a listener tells the
|
|
13859
|
-
* two apart by `e.target`.
|
|
13957
|
+
* and section current, moves the focus to the section, dispatches
|
|
13958
|
+
* `wizard:change` on the host (not bubbling) with detail `{ index, step }`,
|
|
13959
|
+
* then fires the section requests for it.
|
|
13860
13960
|
* @param {string} ref the section's `data-step`
|
|
13861
13961
|
* @returns {Promise<any[] | undefined> | undefined} a promise resolving to
|
|
13862
13962
|
* the answers once they are delivered, or to undefined when nobody
|
|
@@ -13913,7 +14013,7 @@ var fml = (function (exports) {
|
|
|
13913
14013
|
this.#apply(clamped);
|
|
13914
14014
|
this.#sections[this.#index].focus();
|
|
13915
14015
|
if (this.rendered) {
|
|
13916
|
-
this.dispatchEvent(new CustomEvent('change', { detail: { index: this.#index, step: this.step } }));
|
|
14016
|
+
this.dispatchEvent(new CustomEvent('wizard:change', { detail: { index: this.#index, step: this.step } }));
|
|
13917
14017
|
}
|
|
13918
14018
|
return this.#enter(clamped);
|
|
13919
14019
|
}
|