diff options
Diffstat (limited to 'web_src/js/modules')
-rw-r--r-- | web_src/js/modules/fomantic/modal.ts | 28 | ||||
-rw-r--r-- | web_src/js/modules/tippy.ts | 3 | ||||
-rw-r--r-- | web_src/js/modules/toast.ts | 2 |
3 files changed, 29 insertions, 4 deletions
diff --git a/web_src/js/modules/fomantic/modal.ts b/web_src/js/modules/fomantic/modal.ts index b07b941590..a96c7785e1 100644 --- a/web_src/js/modules/fomantic/modal.ts +++ b/web_src/js/modules/fomantic/modal.ts @@ -9,8 +9,9 @@ const fomanticModalFn = $.fn.modal; export function initAriaModalPatch() { if ($.fn.modal === ariaModalFn) throw new Error('initAriaModalPatch could only be called once'); $.fn.modal = ariaModalFn; - $.fn.fomanticExt.onModalBeforeHidden = onModalBeforeHidden; (ariaModalFn as FomanticInitFunction).settings = fomanticModalFn.settings; + $.fn.fomanticExt.onModalBeforeHidden = onModalBeforeHidden; + $.fn.modal.settings.onApprove = onModalApproveDefault; } // the patched `$.fn.modal` modal function @@ -34,6 +35,29 @@ function ariaModalFn(this: any, ...args: Parameters<FomanticInitFunction>) { function onModalBeforeHidden(this: any) { const $modal = $(this); const elModal = $modal[0]; - queryElems(elModal, 'form', (form: HTMLFormElement) => form.reset()); hideToastsFrom(elModal.closest('.ui.dimmer') ?? document.body); + + // reset the form after the modal is hidden, after other modal events and handlers (e.g. "onApprove", form submit) + setTimeout(() => { + queryElems(elModal, 'form', (form: HTMLFormElement) => form.reset()); + }, 0); +} + +function onModalApproveDefault(this: any) { + const $modal = $(this); + const selectors = $modal.modal('setting', 'selector'); + const elModal = $modal[0]; + const elApprove = elModal.querySelector(selectors.approve); + const elForm = elApprove?.closest('form'); + if (!elForm) return true; // no form, just allow closing the modal + + // "form-fetch-action" can handle network errors gracefully, + // so keep the modal dialog to make users can re-submit the form if anything wrong happens. + if (elForm.matches('.form-fetch-action')) return false; + + // There is an abuse for the "modal" + "form" combination, the "Approve" button is a traditional form submit button in the form. + // Then "approve" and "submit" occur at the same time, the modal will be closed immediately before the form is submitted. + // So here we prevent the modal from closing automatically by returning false, add the "is-loading" class to the form element. + elForm.classList.add('is-loading'); + return false; } diff --git a/web_src/js/modules/tippy.ts b/web_src/js/modules/tippy.ts index f7a4b3723b..2a1d998d76 100644 --- a/web_src/js/modules/tippy.ts +++ b/web_src/js/modules/tippy.ts @@ -2,6 +2,7 @@ import tippy, {followCursor} from 'tippy.js'; import {isDocumentFragmentOrElementNode} from '../utils/dom.ts'; import {formatDatetime} from '../utils/time.ts'; import type {Content, Instance, Placement, Props} from 'tippy.js'; +import {html} from '../utils/html.ts'; type TippyOpts = { role?: string, @@ -9,7 +10,7 @@ type TippyOpts = { } & Partial<Props>; const visibleInstances = new Set<Instance>(); -const arrowSvg = `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`; +const arrowSvg = html`<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`; export function createTippy(target: Element, opts: TippyOpts = {}): Instance { // the callback functions should be destructured from opts, diff --git a/web_src/js/modules/toast.ts b/web_src/js/modules/toast.ts index b0afc343c3..ed807a4977 100644 --- a/web_src/js/modules/toast.ts +++ b/web_src/js/modules/toast.ts @@ -1,4 +1,4 @@ -import {htmlEscape} from 'escape-goat'; +import {htmlEscape} from '../utils/html.ts'; import {svg} from '../svg.ts'; import {animateOnce, queryElems, showElem} from '../utils/dom.ts'; import Toastify from 'toastify-js'; // don't use "async import", because when network error occurs, the "async import" also fails and nothing is shown |