@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/ful.iife.js
CHANGED
|
@@ -1463,11 +1463,13 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1463
1463
|
* A call while a submit is in flight returns at once, doing nothing.
|
|
1464
1464
|
* @param {HTMLElement} [submitter] the button that submitted, if any; it is
|
|
1465
1465
|
* the only button whose name and value are submitted
|
|
1466
|
-
* @returns {Promise<
|
|
1466
|
+
* @returns {Promise<boolean>} settling once the submit has finished: true
|
|
1467
|
+
* after `submit:success`, false when it failed, was cancelled or was dropped
|
|
1468
|
+
* for one in flight
|
|
1467
1469
|
*/
|
|
1468
1470
|
async submit(submitter) {
|
|
1469
1471
|
if (this.#submitting) {
|
|
1470
|
-
return;
|
|
1472
|
+
return false;
|
|
1471
1473
|
}
|
|
1472
1474
|
this.#submitting = true;
|
|
1473
1475
|
this.spinner(true);
|
|
@@ -1484,7 +1486,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1484
1486
|
detail: { submitter, values, request },
|
|
1485
1487
|
});
|
|
1486
1488
|
if (!this.dispatchEvent(se)) {
|
|
1487
|
-
return;
|
|
1489
|
+
return false;
|
|
1488
1490
|
}
|
|
1489
1491
|
this.errors = [];
|
|
1490
1492
|
const sre = new CustomEvent('submit:requested', {
|
|
@@ -1507,6 +1509,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1507
1509
|
detail: { submitter, values, request, response: mapped },
|
|
1508
1510
|
}),
|
|
1509
1511
|
);
|
|
1512
|
+
return true;
|
|
1510
1513
|
} catch (e) {
|
|
1511
1514
|
this.dispatchEvent(
|
|
1512
1515
|
new CustomEvent('submit:failure', {
|
|
@@ -1520,11 +1523,27 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1520
1523
|
} else {
|
|
1521
1524
|
console.warn('failed to submit form', this, 'reason:', e);
|
|
1522
1525
|
}
|
|
1526
|
+
return false;
|
|
1523
1527
|
} finally {
|
|
1524
1528
|
this.#submitting = false;
|
|
1525
1529
|
this.spinner(false);
|
|
1526
1530
|
}
|
|
1527
1531
|
}
|
|
1532
|
+
/**
|
|
1533
|
+
* Saves the unsaved edits, for a page action that would lose them, such as
|
|
1534
|
+
* one reloading the page: under `track-changes` it submits only while the
|
|
1535
|
+
* form is dirty, and without it always submits.
|
|
1536
|
+
* @param {HTMLElement} [submitter] as `submit` takes it
|
|
1537
|
+
* @returns {Promise<boolean>} whether no unsaved edit is left: false when
|
|
1538
|
+
* the submit did not succeed, or when the user edited the form while it
|
|
1539
|
+
* was in flight
|
|
1540
|
+
*/
|
|
1541
|
+
async save(submitter) {
|
|
1542
|
+
if (this.#tracking && !this.dirty) {
|
|
1543
|
+
return true;
|
|
1544
|
+
}
|
|
1545
|
+
return (await this.submit(submitter)) && !this.dirty;
|
|
1546
|
+
}
|
|
1528
1547
|
/**
|
|
1529
1548
|
* Resets the native form, each field returning to its default value. Under
|
|
1530
1549
|
* `track-changes` every control counts as edited: the form is dirty if the
|
|
@@ -1597,12 +1616,14 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
1597
1616
|
}
|
|
1598
1617
|
}
|
|
1599
1618
|
/**
|
|
1600
|
-
* Counts
|
|
1619
|
+
* Counts controls as edited by the user, for values the page writes on
|
|
1601
1620
|
* the user's behalf: assigning `value` reports nothing, as 0019 wants.
|
|
1602
|
-
* @param {Element}
|
|
1621
|
+
* @param {...Element} els each a control, or an element inside one
|
|
1603
1622
|
*/
|
|
1604
|
-
edited(
|
|
1605
|
-
|
|
1623
|
+
edited(...els) {
|
|
1624
|
+
for (const el of els) {
|
|
1625
|
+
this.#touch(el);
|
|
1626
|
+
}
|
|
1606
1627
|
}
|
|
1607
1628
|
/**
|
|
1608
1629
|
* @param {Map<string, string>} saved
|
|
@@ -6704,6 +6725,8 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
6704
6725
|
#error;
|
|
6705
6726
|
#requests = new SectionRequests();
|
|
6706
6727
|
#updates = new Claims();
|
|
6728
|
+
/** @type {Promise<boolean>|null} */
|
|
6729
|
+
#confirming = null;
|
|
6707
6730
|
/**
|
|
6708
6731
|
* @param {HTMLElement} host the element the section requests are dispatched on
|
|
6709
6732
|
* @param {DocumentFragment} fragment the rendered template, holding a
|
|
@@ -6727,17 +6750,53 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
6727
6750
|
get section() {
|
|
6728
6751
|
return this.#section;
|
|
6729
6752
|
}
|
|
6753
|
+
/**
|
|
6754
|
+
* Whether a `ful-form` of this dialog, not one of a dialog nested in it,
|
|
6755
|
+
* holds unsaved edits (see `Form.dirty`).
|
|
6756
|
+
* @returns {boolean}
|
|
6757
|
+
*/
|
|
6758
|
+
get dirty() {
|
|
6759
|
+
return Array.from(this.#dialog.querySelectorAll('ful-form')).some(
|
|
6760
|
+
(form) => form.closest('dialog') === this.#dialog && /** @type {any} */ (form).dirty,
|
|
6761
|
+
);
|
|
6762
|
+
}
|
|
6730
6763
|
/**
|
|
6731
6764
|
* Dispatches the dismissal on the host and calls `close` unless a listener
|
|
6732
|
-
* prevented it
|
|
6733
|
-
*
|
|
6765
|
+
* prevented it or, with `confirm` given and the dialog `dirty`, `confirm`
|
|
6766
|
+
* answers false. A dismissal that is not cancelable announces a close the
|
|
6767
|
+
* platform makes whatever the page does, so nothing is asked and `close` is
|
|
6768
|
+
* not called for it. While `confirm` is pending a further dismissal waits
|
|
6769
|
+
* for the same answer, and a dialog closed meanwhile is not closed again.
|
|
6734
6770
|
* @param {CustomEvent} asked
|
|
6735
6771
|
* @param {() => void} close
|
|
6772
|
+
* @param {(() => Promise<boolean>)|null} confirm asks whether to drop the unsaved edits
|
|
6773
|
+
* @returns {Promise<boolean>} whether the dismissal closes the dialog
|
|
6736
6774
|
*/
|
|
6737
|
-
dismiss(asked, close) {
|
|
6738
|
-
if (this.#
|
|
6775
|
+
dismiss(asked, close, confirm) {
|
|
6776
|
+
if (this.#confirming) {
|
|
6777
|
+
return this.#confirming;
|
|
6778
|
+
}
|
|
6779
|
+
if (!this.#host.dispatchEvent(asked)) {
|
|
6780
|
+
return Promise.resolve(false);
|
|
6781
|
+
}
|
|
6782
|
+
if (!asked.cancelable) {
|
|
6783
|
+
return Promise.resolve(true);
|
|
6784
|
+
}
|
|
6785
|
+
if (!confirm || !this.dirty) {
|
|
6739
6786
|
close();
|
|
6787
|
+
return Promise.resolve(true);
|
|
6740
6788
|
}
|
|
6789
|
+
this.#confirming = confirm()
|
|
6790
|
+
.then((drop) => {
|
|
6791
|
+
if (drop && this.#dialog.open) {
|
|
6792
|
+
close();
|
|
6793
|
+
}
|
|
6794
|
+
return drop;
|
|
6795
|
+
})
|
|
6796
|
+
.finally(() => {
|
|
6797
|
+
this.#confirming = null;
|
|
6798
|
+
});
|
|
6799
|
+
return this.#confirming;
|
|
6741
6800
|
}
|
|
6742
6801
|
/**
|
|
6743
6802
|
* Calls `dismiss` when a press and its release both land on the backdrop,
|
|
@@ -6877,21 +6936,24 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
6877
6936
|
* and a press and release both on the backdrop close the dialog, each as a
|
|
6878
6937
|
* dismissal, as Escape does.
|
|
6879
6938
|
*
|
|
6880
|
-
*
|
|
6881
|
-
*
|
|
6882
|
-
*
|
|
6883
|
-
* `
|
|
6884
|
-
*
|
|
6885
|
-
*
|
|
6939
|
+
* Those three gestures go through `dismiss(reason)`, the reason being
|
|
6940
|
+
* `button`, `backdrop` or `escape`: before it closes the dialog, the element
|
|
6941
|
+
* dispatches a cancelable, non-bubbling `dialog:dismiss` with
|
|
6942
|
+
* `detail: { reason }`, and a listener calling `preventDefault()` keeps it
|
|
6943
|
+
* open. Under `guard-dismiss`, a dismissal nobody refused, while a `ful-form`
|
|
6944
|
+
* of the dialog holds unsaved edits (see `Form.dirty`), first asks the user,
|
|
6945
|
+
* through `Dialog.confirm`, whether to close and drop them. A `data-result`
|
|
6946
|
+
* button, `close-on-submit`, `close()` and `update()` are answers or the page
|
|
6947
|
+
* deciding: they dispatch nothing and ask nothing.
|
|
6886
6948
|
*
|
|
6887
6949
|
* The platform lets a page refuse Escape once per user interaction: a second
|
|
6888
6950
|
* Escape with no click or keystroke in between closes the dialog whatever the
|
|
6889
|
-
* page does, `requires-answer` included. That close is
|
|
6890
|
-
* `dialog:dismiss`, which is then not cancelable.
|
|
6951
|
+
* page does, `requires-answer` and `guard-dismiss` included. That close is
|
|
6952
|
+
* still announced by a `dialog:dismiss`, which is then not cancelable.
|
|
6891
6953
|
*
|
|
6892
6954
|
* `requires-answer` withholds the close button and refuses Escape and the
|
|
6893
6955
|
* backdrop, so the dialog is closed only by a button carrying `data-result`
|
|
6894
|
-
* or from code through `close()`.
|
|
6956
|
+
* or from code, through `close()` or `dismiss()`.
|
|
6895
6957
|
*
|
|
6896
6958
|
* `close-on-submit` closes the dialog when a `ful-form` that is a direct child
|
|
6897
6959
|
* of the body dispatches `submit:success`, answering with its
|
|
@@ -6911,7 +6973,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
6911
6973
|
* in a form needs.
|
|
6912
6974
|
*/
|
|
6913
6975
|
class Dialog extends index_mjs.ParsedElement {
|
|
6914
|
-
static attributes = ['header', 'requires-answer:presence', 'close-on-submit:presence'];
|
|
6976
|
+
static attributes = ['header', 'requires-answer:presence', 'close-on-submit:presence', 'guard-dismiss:presence'];
|
|
6915
6977
|
static slots = true;
|
|
6916
6978
|
static template = `
|
|
6917
6979
|
<dialog data-ref="dialog" class="ful-dialog">
|
|
@@ -6961,15 +7023,10 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
6961
7023
|
this.#dialog.close(result);
|
|
6962
7024
|
}
|
|
6963
7025
|
});
|
|
6964
|
-
const dismiss = (/** @type {'button'|'backdrop'|'escape'} */ reason, platformCloses = false) =>
|
|
6965
|
-
this.#sections.dismiss(
|
|
6966
|
-
new CustomEvent('dialog:dismiss', { cancelable: !platformCloses, detail: { reason } }),
|
|
6967
|
-
() => this.#dialog.close(''),
|
|
6968
|
-
);
|
|
6969
7026
|
if (!requiresAnswer) {
|
|
6970
|
-
this.#sections.onBackdrop(() => dismiss('backdrop'));
|
|
7027
|
+
this.#sections.onBackdrop(() => this.dismiss('backdrop'));
|
|
6971
7028
|
}
|
|
6972
|
-
fragment.querySelector('[data-ref=close]')?.addEventListener('click', () => dismiss('button'));
|
|
7029
|
+
fragment.querySelector('[data-ref=close]')?.addEventListener('click', () => this.dismiss('button'));
|
|
6973
7030
|
if (closeOnSubmit) {
|
|
6974
7031
|
this.#sections.onSubmitted((response) => {
|
|
6975
7032
|
this.#answer = { dismissed: false, result: null, response };
|
|
@@ -6979,12 +7036,12 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
6979
7036
|
this.#dialog.addEventListener('cancel', (/** @type any */ e) => {
|
|
6980
7037
|
if (!e.cancelable) {
|
|
6981
7038
|
this.#dialog.returnValue = '';
|
|
6982
|
-
dismiss('escape',
|
|
7039
|
+
this.#dismiss('escape', false);
|
|
6983
7040
|
return;
|
|
6984
7041
|
}
|
|
6985
7042
|
e.preventDefault();
|
|
6986
7043
|
if (!requiresAnswer) {
|
|
6987
|
-
dismiss('escape');
|
|
7044
|
+
this.dismiss('escape');
|
|
6988
7045
|
}
|
|
6989
7046
|
});
|
|
6990
7047
|
this.replaceChildren(fragment);
|
|
@@ -7076,6 +7133,34 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
7076
7133
|
refresh() {
|
|
7077
7134
|
return this.#sections.request();
|
|
7078
7135
|
}
|
|
7136
|
+
/**
|
|
7137
|
+
* Dismisses the dialog as the user would: dispatches the cancelable
|
|
7138
|
+
* `dialog:dismiss` with `detail: { reason }` and, under `guard-dismiss`,
|
|
7139
|
+
* asks before dropping unsaved edits, closing the dialog as a dismissal
|
|
7140
|
+
* unless either refused. `requires-answer` does not refuse it, as it does
|
|
7141
|
+
* not refuse `close()`. Does nothing on a closed dialog.
|
|
7142
|
+
* @param {string} reason why it is dismissed, such as `navigation`
|
|
7143
|
+
* @returns {Promise<boolean>} whether the dialog is closed, false when a
|
|
7144
|
+
* listener or the user kept it open
|
|
7145
|
+
*/
|
|
7146
|
+
dismiss(reason) {
|
|
7147
|
+
if (!this.#dialog.open) {
|
|
7148
|
+
return Promise.resolve(true);
|
|
7149
|
+
}
|
|
7150
|
+
return this.#dismiss(reason, true);
|
|
7151
|
+
}
|
|
7152
|
+
/**
|
|
7153
|
+
* @param {string} reason
|
|
7154
|
+
* @param {boolean} cancelable false for a close the platform makes anyway
|
|
7155
|
+
* @returns {Promise<boolean>}
|
|
7156
|
+
*/
|
|
7157
|
+
#dismiss(reason, cancelable) {
|
|
7158
|
+
return this.#sections.dismiss(
|
|
7159
|
+
new CustomEvent('dialog:dismiss', { cancelable, detail: { reason } }),
|
|
7160
|
+
() => this.#dialog.close(''),
|
|
7161
|
+
this.declared('guard-dismiss') ? discardEdits : null,
|
|
7162
|
+
);
|
|
7163
|
+
}
|
|
7079
7164
|
/**
|
|
7080
7165
|
* Closes an open dialog, doing nothing on a closed one. A non-empty result
|
|
7081
7166
|
* answers as the button carrying that `data-result` would; no result, or
|
|
@@ -7175,6 +7260,19 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
7175
7260
|
}
|
|
7176
7261
|
}
|
|
7177
7262
|
|
|
7263
|
+
/**
|
|
7264
|
+
* Asks, through `Dialog.confirm`, whether to close and drop the unsaved edits,
|
|
7265
|
+
* with the localized `dialog.discard.*` texts.
|
|
7266
|
+
* @returns {Promise<boolean>} true to drop them
|
|
7267
|
+
*/
|
|
7268
|
+
const discardEdits = () => {
|
|
7269
|
+
const l10n = index_mjs.Localization.of();
|
|
7270
|
+
return Dialog.confirm(l10n.t('dialog.discard.header'), l10n.t('dialog.discard.body'), {
|
|
7271
|
+
confirm: l10n.t('dialog.discard.confirm'),
|
|
7272
|
+
cancel: l10n.t('dialog.discard.cancel'),
|
|
7273
|
+
});
|
|
7274
|
+
};
|
|
7275
|
+
|
|
7178
7276
|
/**
|
|
7179
7277
|
* A side panel on the native `<dialog>`, shown as a modal: a header holding
|
|
7180
7278
|
* the title and a localized close button, then loading, error and content
|
|
@@ -7187,17 +7285,23 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
7187
7285
|
* default, when absent) or `start`, following the writing direction.
|
|
7188
7286
|
*
|
|
7189
7287
|
* The close button, Escape, and a press and release both on the backdrop all
|
|
7190
|
-
* go through `
|
|
7191
|
-
* `
|
|
7192
|
-
*
|
|
7193
|
-
*
|
|
7194
|
-
*
|
|
7288
|
+
* go through `dismiss(reason)`, the reason being `button`, `backdrop` or
|
|
7289
|
+
* `escape`: before it closes the drawer, the element dispatches a cancelable,
|
|
7290
|
+
* non-bubbling `drawer:dismiss` with `detail: { reason }`, and a listener
|
|
7291
|
+
* calling `preventDefault()` keeps it open. Under `guard-dismiss`, a dismissal
|
|
7292
|
+
* nobody refused, while a `ful-form` of the drawer holds unsaved edits (see
|
|
7293
|
+
* `Form.dirty`), first asks the user, through `Dialog.confirm`, whether to
|
|
7294
|
+
* close and drop them. A `data-result` button, `close-on-submit`, `close()`
|
|
7295
|
+
* and `update()` are answers or the page deciding: they dispatch nothing and
|
|
7296
|
+
* ask nothing. The platform lets a page refuse Escape once per user
|
|
7195
7297
|
* interaction: a second Escape with no click or keystroke in between closes
|
|
7196
|
-
* the drawer at once, without the slide,
|
|
7197
|
-
*
|
|
7198
|
-
*
|
|
7199
|
-
*
|
|
7200
|
-
*
|
|
7298
|
+
* the drawer at once, without the slide, `guard-dismiss` included, and its
|
|
7299
|
+
* `drawer:dismiss` is then not cancelable.
|
|
7300
|
+
*
|
|
7301
|
+
* Any button inside the drawer carrying `data-result` closes it with that
|
|
7302
|
+
* result, an empty one being a dismissal. Every close dispatches a
|
|
7303
|
+
* non-bubbling `close` event on the element carrying the `DialogOutcome` as
|
|
7304
|
+
* `detail`, before the pending `ask()` calls resolve.
|
|
7201
7305
|
*
|
|
7202
7306
|
* `close-on-submit` closes the drawer when a `ful-form` that is a direct child
|
|
7203
7307
|
* of the content section dispatches `submit:success`, carrying its
|
|
@@ -7209,7 +7313,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
7209
7313
|
* element's id calls `open()` on click.
|
|
7210
7314
|
*/
|
|
7211
7315
|
class Drawer extends index_mjs.ParsedElement {
|
|
7212
|
-
static attributes = ['header', 'placement', 'close-on-submit:presence'];
|
|
7316
|
+
static attributes = ['header', 'placement', 'close-on-submit:presence', 'guard-dismiss:presence'];
|
|
7213
7317
|
static slots = true;
|
|
7214
7318
|
static template = `
|
|
7215
7319
|
<dialog data-ref="dialog" class="ful-drawer">
|
|
@@ -7226,8 +7330,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
7226
7330
|
#dialog;
|
|
7227
7331
|
#title;
|
|
7228
7332
|
#sections;
|
|
7229
|
-
/** @type {{ dismissed: boolean, response: any }|null} */
|
|
7333
|
+
/** @type {{ dismissed: boolean, result: string|null, response: any }|null} */
|
|
7230
7334
|
#answer = null;
|
|
7335
|
+
#resolvers = [];
|
|
7231
7336
|
#closing = false;
|
|
7232
7337
|
#slideOutEnded = (/** @type AnimationEvent */ e) => {
|
|
7233
7338
|
if (e.target === this.#dialog) {
|
|
@@ -7250,32 +7355,33 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
7250
7355
|
if (placement) {
|
|
7251
7356
|
this.#dialog.setAttribute('placement', placement);
|
|
7252
7357
|
}
|
|
7253
|
-
const dismiss = (/** @type {'button'|'backdrop'|'escape'} */ reason, platformCloses = false) =>
|
|
7254
|
-
this.#sections.dismiss(
|
|
7255
|
-
new CustomEvent('drawer:dismiss', { cancelable: !platformCloses, detail: { reason } }),
|
|
7256
|
-
() => this.close(),
|
|
7257
|
-
);
|
|
7258
7358
|
/** @type {HTMLElement} */ (fragment.querySelector('[data-ref=close]')).addEventListener('click', () =>
|
|
7259
|
-
dismiss('button'),
|
|
7359
|
+
this.dismiss('button'),
|
|
7260
7360
|
);
|
|
7261
|
-
this.#sections.onBackdrop(() => dismiss('backdrop'));
|
|
7361
|
+
this.#sections.onBackdrop(() => this.dismiss('backdrop'));
|
|
7262
7362
|
this.#dialog.addEventListener('cancel', (e) => {
|
|
7263
7363
|
if (!e.cancelable) {
|
|
7264
7364
|
this.#stopSlidingOut();
|
|
7265
|
-
dismiss('escape',
|
|
7365
|
+
this.#dismiss('escape', false);
|
|
7266
7366
|
return;
|
|
7267
7367
|
}
|
|
7268
7368
|
e.preventDefault();
|
|
7269
|
-
dismiss('escape');
|
|
7369
|
+
this.dismiss('escape');
|
|
7370
|
+
});
|
|
7371
|
+
this.#dialog.addEventListener('click', (/** @type any */ e) => {
|
|
7372
|
+
const result = e.target.closest('button[data-result]')?.dataset.result;
|
|
7373
|
+
if (result !== undefined) {
|
|
7374
|
+
this.close(result);
|
|
7375
|
+
}
|
|
7270
7376
|
});
|
|
7271
7377
|
this.#dialog.addEventListener('close', () => {
|
|
7272
|
-
this
|
|
7273
|
-
|
|
7274
|
-
);
|
|
7378
|
+
const outcome = this.#outcome();
|
|
7379
|
+
this.dispatchEvent(new CustomEvent('close', { detail: outcome }));
|
|
7380
|
+
this.#settle(outcome);
|
|
7275
7381
|
});
|
|
7276
7382
|
if (this.declared('close-on-submit')) {
|
|
7277
7383
|
this.#sections.onSubmitted((response) => {
|
|
7278
|
-
this.#answer = { dismissed: false, response };
|
|
7384
|
+
this.#answer = { dismissed: false, result: null, response };
|
|
7279
7385
|
this.close();
|
|
7280
7386
|
});
|
|
7281
7387
|
}
|
|
@@ -7323,9 +7429,36 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
7323
7429
|
refresh() {
|
|
7324
7430
|
return this.#sections.request();
|
|
7325
7431
|
}
|
|
7432
|
+
/** @returns {{ dismissed: boolean, result: string|null, response: any }} */
|
|
7433
|
+
#outcome() {
|
|
7434
|
+
return this.#answer ?? { dismissed: true, result: null, response: null };
|
|
7435
|
+
}
|
|
7436
|
+
/** @param {{ dismissed: boolean, result: string|null, response: any }} outcome */
|
|
7437
|
+
#settle(outcome) {
|
|
7438
|
+
const resolvers = this.#resolvers;
|
|
7439
|
+
this.#resolvers = [];
|
|
7440
|
+
for (const resolve of resolvers) {
|
|
7441
|
+
resolve(outcome);
|
|
7442
|
+
}
|
|
7443
|
+
}
|
|
7444
|
+
/**
|
|
7445
|
+
* Resolves every pending `ask()` and `open()` with the outcome so far,
|
|
7446
|
+
* a dismissal for a drawer still open, so that no caller waits on a drawer
|
|
7447
|
+
* that left the document. The native dialog is not closed and no `close`
|
|
7448
|
+
* event is dispatched.
|
|
7449
|
+
*/
|
|
7450
|
+
disconnectedCallback() {
|
|
7451
|
+
this.#settle(this.#outcome());
|
|
7452
|
+
}
|
|
7453
|
+
/**
|
|
7454
|
+
* The same as `ask()`.
|
|
7455
|
+
* @returns {Promise<{ dismissed: boolean, result: string|null, response: any }>}
|
|
7456
|
+
*/
|
|
7457
|
+
open() {
|
|
7458
|
+
return this.ask();
|
|
7459
|
+
}
|
|
7326
7460
|
/**
|
|
7327
|
-
* Shows the drawer as a modal
|
|
7328
|
-
* except that one sliding out stops and stays open.
|
|
7461
|
+
* Shows the drawer as a modal and resolves with how it ends.
|
|
7329
7462
|
*
|
|
7330
7463
|
* The call that opens the drawer resets the outcome of the previous
|
|
7331
7464
|
* opening, hides the error and loading sections, and dispatches
|
|
@@ -7333,26 +7466,67 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
7333
7466
|
* `detail: { section, first, name: null, index: null }`, `section` being
|
|
7334
7467
|
* the content section and `first` true only on the first answered request),
|
|
7335
7468
|
* so a listener can fill the content. A failed request is painted into the
|
|
7336
|
-
* content section.
|
|
7469
|
+
* content section and does not reject. On a drawer already open nothing is
|
|
7470
|
+
* shown or requested, except that one sliding out stops and stays open, and
|
|
7471
|
+
* the call waits for the same ending as the calls before it, which is how
|
|
7472
|
+
* a caller learns the outcome of the opening an `update()` made.
|
|
7473
|
+
* @returns {Promise<{ dismissed: boolean, result: string|null, response: any }>} resolving on
|
|
7474
|
+
* close, or on removal from the document with a dismissal
|
|
7337
7475
|
*/
|
|
7338
|
-
|
|
7339
|
-
if (
|
|
7340
|
-
|
|
7476
|
+
ask() {
|
|
7477
|
+
if (this.#show()) {
|
|
7478
|
+
this.#sections.restChrome();
|
|
7479
|
+
this.#sections.request();
|
|
7341
7480
|
}
|
|
7342
|
-
|
|
7343
|
-
|
|
7481
|
+
return new Promise((resolve) => {
|
|
7482
|
+
this.#resolvers.push(resolve);
|
|
7483
|
+
});
|
|
7484
|
+
}
|
|
7485
|
+
/**
|
|
7486
|
+
* Dismisses the drawer as the user would: dispatches the cancelable
|
|
7487
|
+
* `drawer:dismiss` with `detail: { reason }` and, under `guard-dismiss`,
|
|
7488
|
+
* asks before dropping unsaved edits, sliding the drawer out as a
|
|
7489
|
+
* dismissal unless either refused. Does nothing on a drawer that is closed
|
|
7490
|
+
* or already sliding out.
|
|
7491
|
+
* @param {string} reason why it is dismissed, such as `navigation`
|
|
7492
|
+
* @returns {Promise<boolean>} whether the drawer is closed or closing,
|
|
7493
|
+
* false when a listener or the user kept it open
|
|
7494
|
+
*/
|
|
7495
|
+
dismiss(reason) {
|
|
7496
|
+
if (this.#closing || !this.#dialog.open) {
|
|
7497
|
+
return Promise.resolve(true);
|
|
7498
|
+
}
|
|
7499
|
+
return this.#dismiss(reason, true);
|
|
7500
|
+
}
|
|
7501
|
+
/**
|
|
7502
|
+
* @param {string} reason
|
|
7503
|
+
* @param {boolean} cancelable false for a close the platform makes anyway
|
|
7504
|
+
* @returns {Promise<boolean>}
|
|
7505
|
+
*/
|
|
7506
|
+
#dismiss(reason, cancelable) {
|
|
7507
|
+
return this.#sections.dismiss(
|
|
7508
|
+
new CustomEvent('drawer:dismiss', { cancelable, detail: { reason } }),
|
|
7509
|
+
() => this.close(),
|
|
7510
|
+
this.declared('guard-dismiss') ? discardEdits : null,
|
|
7511
|
+
);
|
|
7344
7512
|
}
|
|
7345
7513
|
/**
|
|
7346
7514
|
* Slides the drawer out and closes it when the animation ends, setting the
|
|
7347
7515
|
* `closing` attribute on the native dialog meanwhile; the `close` event is
|
|
7348
7516
|
* dispatched then, after this call has returned. Where the user prefers
|
|
7349
|
-
* reduced motion, or no animation runs, it closes at once.
|
|
7350
|
-
*
|
|
7517
|
+
* reduced motion, or no animation runs, it closes at once. A non-empty
|
|
7518
|
+
* result answers as the button carrying that `data-result` would; no
|
|
7519
|
+
* result, or an empty one, is a dismissal. Does nothing on a drawer that is
|
|
7520
|
+
* closed or already sliding out.
|
|
7521
|
+
* @param {string} [result]
|
|
7351
7522
|
*/
|
|
7352
|
-
close() {
|
|
7523
|
+
close(result) {
|
|
7353
7524
|
if (this.#closing || !this.#dialog.open) {
|
|
7354
7525
|
return;
|
|
7355
7526
|
}
|
|
7527
|
+
if (result) {
|
|
7528
|
+
this.#answer = { dismissed: false, result, response: null };
|
|
7529
|
+
}
|
|
7356
7530
|
if (matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
7357
7531
|
this.#dialog.close();
|
|
7358
7532
|
return;
|
|
@@ -8064,6 +8238,10 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
8064
8238
|
'dialog.close': 'Close',
|
|
8065
8239
|
'dialog.confirm': 'Confirm',
|
|
8066
8240
|
'dialog.cancel': 'Cancel',
|
|
8241
|
+
'dialog.discard.header': 'Unsaved changes',
|
|
8242
|
+
'dialog.discard.body': 'There are unsaved changes: close anyway?',
|
|
8243
|
+
'dialog.discard.confirm': 'Close without saving',
|
|
8244
|
+
'dialog.discard.cancel': 'Keep editing',
|
|
8067
8245
|
'drawer.close': 'Close',
|
|
8068
8246
|
'spinner.loading': 'Loading…',
|
|
8069
8247
|
'toast.region': 'Notifications',
|
|
@@ -8109,6 +8287,10 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
8109
8287
|
'dialog.close': 'Chiudi',
|
|
8110
8288
|
'dialog.confirm': 'Conferma',
|
|
8111
8289
|
'dialog.cancel': 'Annulla',
|
|
8290
|
+
'dialog.discard.header': 'Modifiche non salvate',
|
|
8291
|
+
'dialog.discard.body': 'Ci sono modifiche non salvate: chiudere comunque?',
|
|
8292
|
+
'dialog.discard.confirm': 'Chiudi senza salvare',
|
|
8293
|
+
'dialog.discard.cancel': 'Continua a modificare',
|
|
8112
8294
|
'drawer.close': 'Chiudi',
|
|
8113
8295
|
'spinner.loading': 'Caricamento…',
|
|
8114
8296
|
'toast.region': 'Notifiche',
|
|
@@ -8157,6 +8339,10 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
8157
8339
|
'dialog.close': 'Cerrar',
|
|
8158
8340
|
'dialog.confirm': 'Confirmar',
|
|
8159
8341
|
'dialog.cancel': 'Cancelar',
|
|
8342
|
+
'dialog.discard.header': 'Cambios sin guardar',
|
|
8343
|
+
'dialog.discard.body': 'Hay cambios sin guardar: ¿cerrar de todos modos?',
|
|
8344
|
+
'dialog.discard.confirm': 'Cerrar sin guardar',
|
|
8345
|
+
'dialog.discard.cancel': 'Seguir editando',
|
|
8160
8346
|
'drawer.close': 'Cerrar',
|
|
8161
8347
|
'spinner.loading': 'Cargando…',
|
|
8162
8348
|
'toast.region': 'Notificaciones',
|
|
@@ -8205,6 +8391,10 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
8205
8391
|
'dialog.close': 'Fermer',
|
|
8206
8392
|
'dialog.confirm': 'Confirmer',
|
|
8207
8393
|
'dialog.cancel': 'Annuler',
|
|
8394
|
+
'dialog.discard.header': 'Modifications non enregistrées',
|
|
8395
|
+
'dialog.discard.body': 'Des modifications ne sont pas enregistrées : fermer quand même ?',
|
|
8396
|
+
'dialog.discard.confirm': 'Fermer sans enregistrer',
|
|
8397
|
+
'dialog.discard.cancel': 'Continuer à modifier',
|
|
8208
8398
|
'drawer.close': 'Fermer',
|
|
8209
8399
|
'spinner.loading': 'Chargement…',
|
|
8210
8400
|
'toast.region': 'Notifications',
|