@optionfactory/fml 9.0.0-rc22 → 9.0.0-rc23
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 +21 -7
- package/dist/fml.css +3 -3
- package/dist/fml.css.map +1 -1
- package/dist/fml.iife.js +256 -66
- 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 +3 -3
- package/dist/ful.css.map +1 -1
- package/dist/ful.d.mts +102 -32
- package/dist/ful.iife.js +256 -66
- 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 +256 -66
- package/dist/ful.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +11 -3
- package/dist/web-types.json +24 -8
- package/package.json +1 -1
package/dist/fml.iife.js
CHANGED
|
@@ -7733,11 +7733,13 @@ var fml = (function (exports) {
|
|
|
7733
7733
|
* A call while a submit is in flight returns at once, doing nothing.
|
|
7734
7734
|
* @param {HTMLElement} [submitter] the button that submitted, if any; it is
|
|
7735
7735
|
* the only button whose name and value are submitted
|
|
7736
|
-
* @returns {Promise<
|
|
7736
|
+
* @returns {Promise<boolean>} settling once the submit has finished: true
|
|
7737
|
+
* after `submit:success`, false when it failed, was cancelled or was dropped
|
|
7738
|
+
* for one in flight
|
|
7737
7739
|
*/
|
|
7738
7740
|
async submit(submitter) {
|
|
7739
7741
|
if (this.#submitting) {
|
|
7740
|
-
return;
|
|
7742
|
+
return false;
|
|
7741
7743
|
}
|
|
7742
7744
|
this.#submitting = true;
|
|
7743
7745
|
this.spinner(true);
|
|
@@ -7754,7 +7756,7 @@ var fml = (function (exports) {
|
|
|
7754
7756
|
detail: { submitter, values, request },
|
|
7755
7757
|
});
|
|
7756
7758
|
if (!this.dispatchEvent(se)) {
|
|
7757
|
-
return;
|
|
7759
|
+
return false;
|
|
7758
7760
|
}
|
|
7759
7761
|
this.errors = [];
|
|
7760
7762
|
const sre = new CustomEvent('submit:requested', {
|
|
@@ -7777,6 +7779,7 @@ var fml = (function (exports) {
|
|
|
7777
7779
|
detail: { submitter, values, request, response: mapped },
|
|
7778
7780
|
}),
|
|
7779
7781
|
);
|
|
7782
|
+
return true;
|
|
7780
7783
|
} catch (e) {
|
|
7781
7784
|
this.dispatchEvent(
|
|
7782
7785
|
new CustomEvent('submit:failure', {
|
|
@@ -7790,11 +7793,27 @@ var fml = (function (exports) {
|
|
|
7790
7793
|
} else {
|
|
7791
7794
|
console.warn('failed to submit form', this, 'reason:', e);
|
|
7792
7795
|
}
|
|
7796
|
+
return false;
|
|
7793
7797
|
} finally {
|
|
7794
7798
|
this.#submitting = false;
|
|
7795
7799
|
this.spinner(false);
|
|
7796
7800
|
}
|
|
7797
7801
|
}
|
|
7802
|
+
/**
|
|
7803
|
+
* Saves the unsaved edits, for a page action that would lose them, such as
|
|
7804
|
+
* one reloading the page: under `track-changes` it submits only while the
|
|
7805
|
+
* form is dirty, and without it always submits.
|
|
7806
|
+
* @param {HTMLElement} [submitter] as `submit` takes it
|
|
7807
|
+
* @returns {Promise<boolean>} whether no unsaved edit is left: false when
|
|
7808
|
+
* the submit did not succeed, or when the user edited the form while it
|
|
7809
|
+
* was in flight
|
|
7810
|
+
*/
|
|
7811
|
+
async save(submitter) {
|
|
7812
|
+
if (this.#tracking && !this.dirty) {
|
|
7813
|
+
return true;
|
|
7814
|
+
}
|
|
7815
|
+
return (await this.submit(submitter)) && !this.dirty;
|
|
7816
|
+
}
|
|
7798
7817
|
/**
|
|
7799
7818
|
* Resets the native form, each field returning to its default value. Under
|
|
7800
7819
|
* `track-changes` every control counts as edited: the form is dirty if the
|
|
@@ -7867,12 +7886,14 @@ var fml = (function (exports) {
|
|
|
7867
7886
|
}
|
|
7868
7887
|
}
|
|
7869
7888
|
/**
|
|
7870
|
-
* Counts
|
|
7889
|
+
* Counts controls as edited by the user, for values the page writes on
|
|
7871
7890
|
* the user's behalf: assigning `value` reports nothing, as 0019 wants.
|
|
7872
|
-
* @param {Element}
|
|
7891
|
+
* @param {...Element} els each a control, or an element inside one
|
|
7873
7892
|
*/
|
|
7874
|
-
edited(
|
|
7875
|
-
|
|
7893
|
+
edited(...els) {
|
|
7894
|
+
for (const el of els) {
|
|
7895
|
+
this.#touch(el);
|
|
7896
|
+
}
|
|
7876
7897
|
}
|
|
7877
7898
|
/**
|
|
7878
7899
|
* @param {Map<string, string>} saved
|
|
@@ -12974,6 +12995,8 @@ var fml = (function (exports) {
|
|
|
12974
12995
|
#error;
|
|
12975
12996
|
#requests = new SectionRequests();
|
|
12976
12997
|
#updates = new Claims();
|
|
12998
|
+
/** @type {Promise<boolean>|null} */
|
|
12999
|
+
#confirming = null;
|
|
12977
13000
|
/**
|
|
12978
13001
|
* @param {HTMLElement} host the element the section requests are dispatched on
|
|
12979
13002
|
* @param {DocumentFragment} fragment the rendered template, holding a
|
|
@@ -12997,17 +13020,53 @@ var fml = (function (exports) {
|
|
|
12997
13020
|
get section() {
|
|
12998
13021
|
return this.#section;
|
|
12999
13022
|
}
|
|
13023
|
+
/**
|
|
13024
|
+
* Whether a `ful-form` of this dialog, not one of a dialog nested in it,
|
|
13025
|
+
* holds unsaved edits (see `Form.dirty`).
|
|
13026
|
+
* @returns {boolean}
|
|
13027
|
+
*/
|
|
13028
|
+
get dirty() {
|
|
13029
|
+
return Array.from(this.#dialog.querySelectorAll('ful-form')).some(
|
|
13030
|
+
(form) => form.closest('dialog') === this.#dialog && /** @type {any} */ (form).dirty,
|
|
13031
|
+
);
|
|
13032
|
+
}
|
|
13000
13033
|
/**
|
|
13001
13034
|
* Dispatches the dismissal on the host and calls `close` unless a listener
|
|
13002
|
-
* prevented it
|
|
13003
|
-
*
|
|
13035
|
+
* prevented it or, with `confirm` given and the dialog `dirty`, `confirm`
|
|
13036
|
+
* answers false. A dismissal that is not cancelable announces a close the
|
|
13037
|
+
* platform makes whatever the page does, so nothing is asked and `close` is
|
|
13038
|
+
* not called for it. While `confirm` is pending a further dismissal waits
|
|
13039
|
+
* for the same answer, and a dialog closed meanwhile is not closed again.
|
|
13004
13040
|
* @param {CustomEvent} asked
|
|
13005
13041
|
* @param {() => void} close
|
|
13042
|
+
* @param {(() => Promise<boolean>)|null} confirm asks whether to drop the unsaved edits
|
|
13043
|
+
* @returns {Promise<boolean>} whether the dismissal closes the dialog
|
|
13006
13044
|
*/
|
|
13007
|
-
dismiss(asked, close) {
|
|
13008
|
-
if (this.#
|
|
13045
|
+
dismiss(asked, close, confirm) {
|
|
13046
|
+
if (this.#confirming) {
|
|
13047
|
+
return this.#confirming;
|
|
13048
|
+
}
|
|
13049
|
+
if (!this.#host.dispatchEvent(asked)) {
|
|
13050
|
+
return Promise.resolve(false);
|
|
13051
|
+
}
|
|
13052
|
+
if (!asked.cancelable) {
|
|
13053
|
+
return Promise.resolve(true);
|
|
13054
|
+
}
|
|
13055
|
+
if (!confirm || !this.dirty) {
|
|
13009
13056
|
close();
|
|
13057
|
+
return Promise.resolve(true);
|
|
13010
13058
|
}
|
|
13059
|
+
this.#confirming = confirm()
|
|
13060
|
+
.then((drop) => {
|
|
13061
|
+
if (drop && this.#dialog.open) {
|
|
13062
|
+
close();
|
|
13063
|
+
}
|
|
13064
|
+
return drop;
|
|
13065
|
+
})
|
|
13066
|
+
.finally(() => {
|
|
13067
|
+
this.#confirming = null;
|
|
13068
|
+
});
|
|
13069
|
+
return this.#confirming;
|
|
13011
13070
|
}
|
|
13012
13071
|
/**
|
|
13013
13072
|
* Calls `dismiss` when a press and its release both land on the backdrop,
|
|
@@ -13147,21 +13206,24 @@ var fml = (function (exports) {
|
|
|
13147
13206
|
* and a press and release both on the backdrop close the dialog, each as a
|
|
13148
13207
|
* dismissal, as Escape does.
|
|
13149
13208
|
*
|
|
13150
|
-
*
|
|
13151
|
-
*
|
|
13152
|
-
*
|
|
13153
|
-
* `
|
|
13154
|
-
*
|
|
13155
|
-
*
|
|
13209
|
+
* Those three gestures go through `dismiss(reason)`, the reason being
|
|
13210
|
+
* `button`, `backdrop` or `escape`: before it closes the dialog, the element
|
|
13211
|
+
* dispatches a cancelable, non-bubbling `dialog:dismiss` with
|
|
13212
|
+
* `detail: { reason }`, and a listener calling `preventDefault()` keeps it
|
|
13213
|
+
* open. Under `guard-dismiss`, a dismissal nobody refused, while a `ful-form`
|
|
13214
|
+
* of the dialog holds unsaved edits (see `Form.dirty`), first asks the user,
|
|
13215
|
+
* through `Dialog.confirm`, whether to close and drop them. A `data-result`
|
|
13216
|
+
* button, `close-on-submit`, `close()` and `update()` are answers or the page
|
|
13217
|
+
* deciding: they dispatch nothing and ask nothing.
|
|
13156
13218
|
*
|
|
13157
13219
|
* The platform lets a page refuse Escape once per user interaction: a second
|
|
13158
13220
|
* Escape with no click or keystroke in between closes the dialog whatever the
|
|
13159
|
-
* page does, `requires-answer` included. That close is
|
|
13160
|
-
* `dialog:dismiss`, which is then not cancelable.
|
|
13221
|
+
* page does, `requires-answer` and `guard-dismiss` included. That close is
|
|
13222
|
+
* still announced by a `dialog:dismiss`, which is then not cancelable.
|
|
13161
13223
|
*
|
|
13162
13224
|
* `requires-answer` withholds the close button and refuses Escape and the
|
|
13163
13225
|
* backdrop, so the dialog is closed only by a button carrying `data-result`
|
|
13164
|
-
* or from code through `close()`.
|
|
13226
|
+
* or from code, through `close()` or `dismiss()`.
|
|
13165
13227
|
*
|
|
13166
13228
|
* `close-on-submit` closes the dialog when a `ful-form` that is a direct child
|
|
13167
13229
|
* of the body dispatches `submit:success`, answering with its
|
|
@@ -13181,7 +13243,7 @@ var fml = (function (exports) {
|
|
|
13181
13243
|
* in a form needs.
|
|
13182
13244
|
*/
|
|
13183
13245
|
class Dialog extends ParsedElement {
|
|
13184
|
-
static attributes = ['header', 'requires-answer:presence', 'close-on-submit:presence'];
|
|
13246
|
+
static attributes = ['header', 'requires-answer:presence', 'close-on-submit:presence', 'guard-dismiss:presence'];
|
|
13185
13247
|
static slots = true;
|
|
13186
13248
|
static template = `
|
|
13187
13249
|
<dialog data-ref="dialog" class="ful-dialog">
|
|
@@ -13231,15 +13293,10 @@ var fml = (function (exports) {
|
|
|
13231
13293
|
this.#dialog.close(result);
|
|
13232
13294
|
}
|
|
13233
13295
|
});
|
|
13234
|
-
const dismiss = (/** @type {'button'|'backdrop'|'escape'} */ reason, platformCloses = false) =>
|
|
13235
|
-
this.#sections.dismiss(
|
|
13236
|
-
new CustomEvent('dialog:dismiss', { cancelable: !platformCloses, detail: { reason } }),
|
|
13237
|
-
() => this.#dialog.close(''),
|
|
13238
|
-
);
|
|
13239
13296
|
if (!requiresAnswer) {
|
|
13240
|
-
this.#sections.onBackdrop(() => dismiss('backdrop'));
|
|
13297
|
+
this.#sections.onBackdrop(() => this.dismiss('backdrop'));
|
|
13241
13298
|
}
|
|
13242
|
-
fragment.querySelector('[data-ref=close]')?.addEventListener('click', () => dismiss('button'));
|
|
13299
|
+
fragment.querySelector('[data-ref=close]')?.addEventListener('click', () => this.dismiss('button'));
|
|
13243
13300
|
if (closeOnSubmit) {
|
|
13244
13301
|
this.#sections.onSubmitted((response) => {
|
|
13245
13302
|
this.#answer = { dismissed: false, result: null, response };
|
|
@@ -13249,12 +13306,12 @@ var fml = (function (exports) {
|
|
|
13249
13306
|
this.#dialog.addEventListener('cancel', (/** @type any */ e) => {
|
|
13250
13307
|
if (!e.cancelable) {
|
|
13251
13308
|
this.#dialog.returnValue = '';
|
|
13252
|
-
dismiss('escape',
|
|
13309
|
+
this.#dismiss('escape', false);
|
|
13253
13310
|
return;
|
|
13254
13311
|
}
|
|
13255
13312
|
e.preventDefault();
|
|
13256
13313
|
if (!requiresAnswer) {
|
|
13257
|
-
dismiss('escape');
|
|
13314
|
+
this.dismiss('escape');
|
|
13258
13315
|
}
|
|
13259
13316
|
});
|
|
13260
13317
|
this.replaceChildren(fragment);
|
|
@@ -13346,6 +13403,34 @@ var fml = (function (exports) {
|
|
|
13346
13403
|
refresh() {
|
|
13347
13404
|
return this.#sections.request();
|
|
13348
13405
|
}
|
|
13406
|
+
/**
|
|
13407
|
+
* Dismisses the dialog as the user would: dispatches the cancelable
|
|
13408
|
+
* `dialog:dismiss` with `detail: { reason }` and, under `guard-dismiss`,
|
|
13409
|
+
* asks before dropping unsaved edits, closing the dialog as a dismissal
|
|
13410
|
+
* unless either refused. `requires-answer` does not refuse it, as it does
|
|
13411
|
+
* not refuse `close()`. Does nothing on a closed dialog.
|
|
13412
|
+
* @param {string} reason why it is dismissed, such as `navigation`
|
|
13413
|
+
* @returns {Promise<boolean>} whether the dialog is closed, false when a
|
|
13414
|
+
* listener or the user kept it open
|
|
13415
|
+
*/
|
|
13416
|
+
dismiss(reason) {
|
|
13417
|
+
if (!this.#dialog.open) {
|
|
13418
|
+
return Promise.resolve(true);
|
|
13419
|
+
}
|
|
13420
|
+
return this.#dismiss(reason, true);
|
|
13421
|
+
}
|
|
13422
|
+
/**
|
|
13423
|
+
* @param {string} reason
|
|
13424
|
+
* @param {boolean} cancelable false for a close the platform makes anyway
|
|
13425
|
+
* @returns {Promise<boolean>}
|
|
13426
|
+
*/
|
|
13427
|
+
#dismiss(reason, cancelable) {
|
|
13428
|
+
return this.#sections.dismiss(
|
|
13429
|
+
new CustomEvent('dialog:dismiss', { cancelable, detail: { reason } }),
|
|
13430
|
+
() => this.#dialog.close(''),
|
|
13431
|
+
this.declared('guard-dismiss') ? discardEdits : null,
|
|
13432
|
+
);
|
|
13433
|
+
}
|
|
13349
13434
|
/**
|
|
13350
13435
|
* Closes an open dialog, doing nothing on a closed one. A non-empty result
|
|
13351
13436
|
* answers as the button carrying that `data-result` would; no result, or
|
|
@@ -13445,6 +13530,19 @@ var fml = (function (exports) {
|
|
|
13445
13530
|
}
|
|
13446
13531
|
}
|
|
13447
13532
|
|
|
13533
|
+
/**
|
|
13534
|
+
* Asks, through `Dialog.confirm`, whether to close and drop the unsaved edits,
|
|
13535
|
+
* with the localized `dialog.discard.*` texts.
|
|
13536
|
+
* @returns {Promise<boolean>} true to drop them
|
|
13537
|
+
*/
|
|
13538
|
+
const discardEdits = () => {
|
|
13539
|
+
const l10n = Localization.of();
|
|
13540
|
+
return Dialog.confirm(l10n.t('dialog.discard.header'), l10n.t('dialog.discard.body'), {
|
|
13541
|
+
confirm: l10n.t('dialog.discard.confirm'),
|
|
13542
|
+
cancel: l10n.t('dialog.discard.cancel'),
|
|
13543
|
+
});
|
|
13544
|
+
};
|
|
13545
|
+
|
|
13448
13546
|
/**
|
|
13449
13547
|
* A side panel on the native `<dialog>`, shown as a modal: a header holding
|
|
13450
13548
|
* the title and a localized close button, then loading, error and content
|
|
@@ -13457,17 +13555,23 @@ var fml = (function (exports) {
|
|
|
13457
13555
|
* default, when absent) or `start`, following the writing direction.
|
|
13458
13556
|
*
|
|
13459
13557
|
* The close button, Escape, and a press and release both on the backdrop all
|
|
13460
|
-
* go through `
|
|
13461
|
-
* `
|
|
13462
|
-
*
|
|
13463
|
-
*
|
|
13464
|
-
*
|
|
13558
|
+
* go through `dismiss(reason)`, the reason being `button`, `backdrop` or
|
|
13559
|
+
* `escape`: before it closes the drawer, the element dispatches a cancelable,
|
|
13560
|
+
* non-bubbling `drawer:dismiss` with `detail: { reason }`, and a listener
|
|
13561
|
+
* calling `preventDefault()` keeps it open. Under `guard-dismiss`, a dismissal
|
|
13562
|
+
* nobody refused, while a `ful-form` of the drawer holds unsaved edits (see
|
|
13563
|
+
* `Form.dirty`), first asks the user, through `Dialog.confirm`, whether to
|
|
13564
|
+
* close and drop them. A `data-result` button, `close-on-submit`, `close()`
|
|
13565
|
+
* and `update()` are answers or the page deciding: they dispatch nothing and
|
|
13566
|
+
* ask nothing. The platform lets a page refuse Escape once per user
|
|
13465
13567
|
* interaction: a second Escape with no click or keystroke in between closes
|
|
13466
|
-
* the drawer at once, without the slide,
|
|
13467
|
-
*
|
|
13468
|
-
*
|
|
13469
|
-
*
|
|
13470
|
-
*
|
|
13568
|
+
* the drawer at once, without the slide, `guard-dismiss` included, and its
|
|
13569
|
+
* `drawer:dismiss` is then not cancelable.
|
|
13570
|
+
*
|
|
13571
|
+
* Any button inside the drawer carrying `data-result` closes it with that
|
|
13572
|
+
* result, an empty one being a dismissal. Every close dispatches a
|
|
13573
|
+
* non-bubbling `close` event on the element carrying the `DialogOutcome` as
|
|
13574
|
+
* `detail`, before the pending `ask()` calls resolve.
|
|
13471
13575
|
*
|
|
13472
13576
|
* `close-on-submit` closes the drawer when a `ful-form` that is a direct child
|
|
13473
13577
|
* of the content section dispatches `submit:success`, carrying its
|
|
@@ -13479,7 +13583,7 @@ var fml = (function (exports) {
|
|
|
13479
13583
|
* element's id calls `open()` on click.
|
|
13480
13584
|
*/
|
|
13481
13585
|
class Drawer extends ParsedElement {
|
|
13482
|
-
static attributes = ['header', 'placement', 'close-on-submit:presence'];
|
|
13586
|
+
static attributes = ['header', 'placement', 'close-on-submit:presence', 'guard-dismiss:presence'];
|
|
13483
13587
|
static slots = true;
|
|
13484
13588
|
static template = `
|
|
13485
13589
|
<dialog data-ref="dialog" class="ful-drawer">
|
|
@@ -13496,8 +13600,9 @@ var fml = (function (exports) {
|
|
|
13496
13600
|
#dialog;
|
|
13497
13601
|
#title;
|
|
13498
13602
|
#sections;
|
|
13499
|
-
/** @type {{ dismissed: boolean, response: any }|null} */
|
|
13603
|
+
/** @type {{ dismissed: boolean, result: string|null, response: any }|null} */
|
|
13500
13604
|
#answer = null;
|
|
13605
|
+
#resolvers = [];
|
|
13501
13606
|
#closing = false;
|
|
13502
13607
|
#slideOutEnded = (/** @type AnimationEvent */ e) => {
|
|
13503
13608
|
if (e.target === this.#dialog) {
|
|
@@ -13520,32 +13625,33 @@ var fml = (function (exports) {
|
|
|
13520
13625
|
if (placement) {
|
|
13521
13626
|
this.#dialog.setAttribute('placement', placement);
|
|
13522
13627
|
}
|
|
13523
|
-
const dismiss = (/** @type {'button'|'backdrop'|'escape'} */ reason, platformCloses = false) =>
|
|
13524
|
-
this.#sections.dismiss(
|
|
13525
|
-
new CustomEvent('drawer:dismiss', { cancelable: !platformCloses, detail: { reason } }),
|
|
13526
|
-
() => this.close(),
|
|
13527
|
-
);
|
|
13528
13628
|
/** @type {HTMLElement} */ (fragment.querySelector('[data-ref=close]')).addEventListener('click', () =>
|
|
13529
|
-
dismiss('button'),
|
|
13629
|
+
this.dismiss('button'),
|
|
13530
13630
|
);
|
|
13531
|
-
this.#sections.onBackdrop(() => dismiss('backdrop'));
|
|
13631
|
+
this.#sections.onBackdrop(() => this.dismiss('backdrop'));
|
|
13532
13632
|
this.#dialog.addEventListener('cancel', (e) => {
|
|
13533
13633
|
if (!e.cancelable) {
|
|
13534
13634
|
this.#stopSlidingOut();
|
|
13535
|
-
dismiss('escape',
|
|
13635
|
+
this.#dismiss('escape', false);
|
|
13536
13636
|
return;
|
|
13537
13637
|
}
|
|
13538
13638
|
e.preventDefault();
|
|
13539
|
-
dismiss('escape');
|
|
13639
|
+
this.dismiss('escape');
|
|
13640
|
+
});
|
|
13641
|
+
this.#dialog.addEventListener('click', (/** @type any */ e) => {
|
|
13642
|
+
const result = e.target.closest('button[data-result]')?.dataset.result;
|
|
13643
|
+
if (result !== undefined) {
|
|
13644
|
+
this.close(result);
|
|
13645
|
+
}
|
|
13540
13646
|
});
|
|
13541
13647
|
this.#dialog.addEventListener('close', () => {
|
|
13542
|
-
this
|
|
13543
|
-
|
|
13544
|
-
);
|
|
13648
|
+
const outcome = this.#outcome();
|
|
13649
|
+
this.dispatchEvent(new CustomEvent('close', { detail: outcome }));
|
|
13650
|
+
this.#settle(outcome);
|
|
13545
13651
|
});
|
|
13546
13652
|
if (this.declared('close-on-submit')) {
|
|
13547
13653
|
this.#sections.onSubmitted((response) => {
|
|
13548
|
-
this.#answer = { dismissed: false, response };
|
|
13654
|
+
this.#answer = { dismissed: false, result: null, response };
|
|
13549
13655
|
this.close();
|
|
13550
13656
|
});
|
|
13551
13657
|
}
|
|
@@ -13593,9 +13699,36 @@ var fml = (function (exports) {
|
|
|
13593
13699
|
refresh() {
|
|
13594
13700
|
return this.#sections.request();
|
|
13595
13701
|
}
|
|
13702
|
+
/** @returns {{ dismissed: boolean, result: string|null, response: any }} */
|
|
13703
|
+
#outcome() {
|
|
13704
|
+
return this.#answer ?? { dismissed: true, result: null, response: null };
|
|
13705
|
+
}
|
|
13706
|
+
/** @param {{ dismissed: boolean, result: string|null, response: any }} outcome */
|
|
13707
|
+
#settle(outcome) {
|
|
13708
|
+
const resolvers = this.#resolvers;
|
|
13709
|
+
this.#resolvers = [];
|
|
13710
|
+
for (const resolve of resolvers) {
|
|
13711
|
+
resolve(outcome);
|
|
13712
|
+
}
|
|
13713
|
+
}
|
|
13714
|
+
/**
|
|
13715
|
+
* Resolves every pending `ask()` and `open()` with the outcome so far,
|
|
13716
|
+
* a dismissal for a drawer still open, so that no caller waits on a drawer
|
|
13717
|
+
* that left the document. The native dialog is not closed and no `close`
|
|
13718
|
+
* event is dispatched.
|
|
13719
|
+
*/
|
|
13720
|
+
disconnectedCallback() {
|
|
13721
|
+
this.#settle(this.#outcome());
|
|
13722
|
+
}
|
|
13723
|
+
/**
|
|
13724
|
+
* The same as `ask()`.
|
|
13725
|
+
* @returns {Promise<{ dismissed: boolean, result: string|null, response: any }>}
|
|
13726
|
+
*/
|
|
13727
|
+
open() {
|
|
13728
|
+
return this.ask();
|
|
13729
|
+
}
|
|
13596
13730
|
/**
|
|
13597
|
-
* Shows the drawer as a modal
|
|
13598
|
-
* except that one sliding out stops and stays open.
|
|
13731
|
+
* Shows the drawer as a modal and resolves with how it ends.
|
|
13599
13732
|
*
|
|
13600
13733
|
* The call that opens the drawer resets the outcome of the previous
|
|
13601
13734
|
* opening, hides the error and loading sections, and dispatches
|
|
@@ -13603,26 +13736,67 @@ var fml = (function (exports) {
|
|
|
13603
13736
|
* `detail: { section, first, name: null, index: null }`, `section` being
|
|
13604
13737
|
* the content section and `first` true only on the first answered request),
|
|
13605
13738
|
* so a listener can fill the content. A failed request is painted into the
|
|
13606
|
-
* content section.
|
|
13739
|
+
* content section and does not reject. On a drawer already open nothing is
|
|
13740
|
+
* shown or requested, except that one sliding out stops and stays open, and
|
|
13741
|
+
* the call waits for the same ending as the calls before it, which is how
|
|
13742
|
+
* a caller learns the outcome of the opening an `update()` made.
|
|
13743
|
+
* @returns {Promise<{ dismissed: boolean, result: string|null, response: any }>} resolving on
|
|
13744
|
+
* close, or on removal from the document with a dismissal
|
|
13607
13745
|
*/
|
|
13608
|
-
|
|
13609
|
-
if (
|
|
13610
|
-
|
|
13746
|
+
ask() {
|
|
13747
|
+
if (this.#show()) {
|
|
13748
|
+
this.#sections.restChrome();
|
|
13749
|
+
this.#sections.request();
|
|
13611
13750
|
}
|
|
13612
|
-
|
|
13613
|
-
|
|
13751
|
+
return new Promise((resolve) => {
|
|
13752
|
+
this.#resolvers.push(resolve);
|
|
13753
|
+
});
|
|
13754
|
+
}
|
|
13755
|
+
/**
|
|
13756
|
+
* Dismisses the drawer as the user would: dispatches the cancelable
|
|
13757
|
+
* `drawer:dismiss` with `detail: { reason }` and, under `guard-dismiss`,
|
|
13758
|
+
* asks before dropping unsaved edits, sliding the drawer out as a
|
|
13759
|
+
* dismissal unless either refused. Does nothing on a drawer that is closed
|
|
13760
|
+
* or already sliding out.
|
|
13761
|
+
* @param {string} reason why it is dismissed, such as `navigation`
|
|
13762
|
+
* @returns {Promise<boolean>} whether the drawer is closed or closing,
|
|
13763
|
+
* false when a listener or the user kept it open
|
|
13764
|
+
*/
|
|
13765
|
+
dismiss(reason) {
|
|
13766
|
+
if (this.#closing || !this.#dialog.open) {
|
|
13767
|
+
return Promise.resolve(true);
|
|
13768
|
+
}
|
|
13769
|
+
return this.#dismiss(reason, true);
|
|
13770
|
+
}
|
|
13771
|
+
/**
|
|
13772
|
+
* @param {string} reason
|
|
13773
|
+
* @param {boolean} cancelable false for a close the platform makes anyway
|
|
13774
|
+
* @returns {Promise<boolean>}
|
|
13775
|
+
*/
|
|
13776
|
+
#dismiss(reason, cancelable) {
|
|
13777
|
+
return this.#sections.dismiss(
|
|
13778
|
+
new CustomEvent('drawer:dismiss', { cancelable, detail: { reason } }),
|
|
13779
|
+
() => this.close(),
|
|
13780
|
+
this.declared('guard-dismiss') ? discardEdits : null,
|
|
13781
|
+
);
|
|
13614
13782
|
}
|
|
13615
13783
|
/**
|
|
13616
13784
|
* Slides the drawer out and closes it when the animation ends, setting the
|
|
13617
13785
|
* `closing` attribute on the native dialog meanwhile; the `close` event is
|
|
13618
13786
|
* dispatched then, after this call has returned. Where the user prefers
|
|
13619
|
-
* reduced motion, or no animation runs, it closes at once.
|
|
13620
|
-
*
|
|
13787
|
+
* reduced motion, or no animation runs, it closes at once. A non-empty
|
|
13788
|
+
* result answers as the button carrying that `data-result` would; no
|
|
13789
|
+
* result, or an empty one, is a dismissal. Does nothing on a drawer that is
|
|
13790
|
+
* closed or already sliding out.
|
|
13791
|
+
* @param {string} [result]
|
|
13621
13792
|
*/
|
|
13622
|
-
close() {
|
|
13793
|
+
close(result) {
|
|
13623
13794
|
if (this.#closing || !this.#dialog.open) {
|
|
13624
13795
|
return;
|
|
13625
13796
|
}
|
|
13797
|
+
if (result) {
|
|
13798
|
+
this.#answer = { dismissed: false, result, response: null };
|
|
13799
|
+
}
|
|
13626
13800
|
if (matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
13627
13801
|
this.#dialog.close();
|
|
13628
13802
|
return;
|
|
@@ -14334,6 +14508,10 @@ var fml = (function (exports) {
|
|
|
14334
14508
|
'dialog.close': 'Close',
|
|
14335
14509
|
'dialog.confirm': 'Confirm',
|
|
14336
14510
|
'dialog.cancel': 'Cancel',
|
|
14511
|
+
'dialog.discard.header': 'Unsaved changes',
|
|
14512
|
+
'dialog.discard.body': 'There are unsaved changes: close anyway?',
|
|
14513
|
+
'dialog.discard.confirm': 'Close without saving',
|
|
14514
|
+
'dialog.discard.cancel': 'Keep editing',
|
|
14337
14515
|
'drawer.close': 'Close',
|
|
14338
14516
|
'spinner.loading': 'Loading…',
|
|
14339
14517
|
'toast.region': 'Notifications',
|
|
@@ -14379,6 +14557,10 @@ var fml = (function (exports) {
|
|
|
14379
14557
|
'dialog.close': 'Chiudi',
|
|
14380
14558
|
'dialog.confirm': 'Conferma',
|
|
14381
14559
|
'dialog.cancel': 'Annulla',
|
|
14560
|
+
'dialog.discard.header': 'Modifiche non salvate',
|
|
14561
|
+
'dialog.discard.body': 'Ci sono modifiche non salvate: chiudere comunque?',
|
|
14562
|
+
'dialog.discard.confirm': 'Chiudi senza salvare',
|
|
14563
|
+
'dialog.discard.cancel': 'Continua a modificare',
|
|
14382
14564
|
'drawer.close': 'Chiudi',
|
|
14383
14565
|
'spinner.loading': 'Caricamento…',
|
|
14384
14566
|
'toast.region': 'Notifiche',
|
|
@@ -14427,6 +14609,10 @@ var fml = (function (exports) {
|
|
|
14427
14609
|
'dialog.close': 'Cerrar',
|
|
14428
14610
|
'dialog.confirm': 'Confirmar',
|
|
14429
14611
|
'dialog.cancel': 'Cancelar',
|
|
14612
|
+
'dialog.discard.header': 'Cambios sin guardar',
|
|
14613
|
+
'dialog.discard.body': 'Hay cambios sin guardar: ¿cerrar de todos modos?',
|
|
14614
|
+
'dialog.discard.confirm': 'Cerrar sin guardar',
|
|
14615
|
+
'dialog.discard.cancel': 'Seguir editando',
|
|
14430
14616
|
'drawer.close': 'Cerrar',
|
|
14431
14617
|
'spinner.loading': 'Cargando…',
|
|
14432
14618
|
'toast.region': 'Notificaciones',
|
|
@@ -14475,6 +14661,10 @@ var fml = (function (exports) {
|
|
|
14475
14661
|
'dialog.close': 'Fermer',
|
|
14476
14662
|
'dialog.confirm': 'Confirmer',
|
|
14477
14663
|
'dialog.cancel': 'Annuler',
|
|
14664
|
+
'dialog.discard.header': 'Modifications non enregistrées',
|
|
14665
|
+
'dialog.discard.body': 'Des modifications ne sont pas enregistrées : fermer quand même ?',
|
|
14666
|
+
'dialog.discard.confirm': 'Fermer sans enregistrer',
|
|
14667
|
+
'dialog.discard.cancel': 'Continuer à modifier',
|
|
14478
14668
|
'drawer.close': 'Fermer',
|
|
14479
14669
|
'spinner.loading': 'Chargement…',
|
|
14480
14670
|
'toast.region': 'Notifications',
|