refactor: share instant action binding

This commit is contained in:
Dariusz L
2026-08-05 02:06:57 +02:00
parent 151c246cde
commit fec8004573
4 changed files with 47 additions and 41 deletions
+24
View File
@@ -9,6 +9,7 @@ import {
setTextIfChanged,
syncElementAttributes,
} from '../web/resolver/utils/dom_patch_utils.js';
import * as domPatchUtils from '../web/resolver/utils/dom_patch_utils.js';
const projectRoot = path.resolve(import.meta.dirname, '..');
const resolveDownloadMethodsSource = fs.readFileSync(
@@ -92,6 +93,29 @@ test('shared DOM helpers synchronize attributes, text, and one-time listeners',
assert.equal(clicks, 1);
});
test('instant actions handle pointer and click as one idempotent action', () => {
assert.equal(typeof domPatchUtils.bindInstantAction, 'function');
const window = new Window();
const button = window.document.createElement('button');
let calls = 0;
const events = [];
assert.equal(domPatchUtils.bindInstantAction(button, event => {
calls += 1;
events.push(event.type);
}), true);
assert.equal(domPatchUtils.bindInstantAction(button, () => {
calls += 100;
}), false);
button.dispatchEvent(new window.Event('pointerdown', { bubbles: true, cancelable: true }));
button.dispatchEvent(new window.Event('click', { bubbles: true, cancelable: true }));
assert.equal(calls, 1);
assert.deepEqual(events, ['pointerdown']);
});
function createDialog(window) {
return {
syncSearchElementAttributes,
+1 -21
View File
@@ -2,7 +2,7 @@ import { app } from "../../../../../scripts/app.js";
import { $el } from "../../../../../scripts/ui.js";
import { getSvgIcon } from "../../utils/icon_utils.js";
import { startSplitterDrag } from "../utils/splitter_drag.js";
import { syncElementAttributes } from "../utils/dom_patch_utils.js";
import { bindInstantAction, syncElementAttributes } from "../utils/dom_patch_utils.js";
const FOOTER_VERSION_REFRESH_INTERVAL_MS = 6 * 60 * 60 * 1000;
const FOOTER_VERSION_FAILURE_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
@@ -1115,26 +1115,6 @@ export const queueMethods = {
},
wireDownloadsPanelControls() {
const bindInstantAction = (button, handler) => {
if (!button || button._hasListener) return;
button._hasListener = true;
const run = (event) => {
event?.preventDefault?.();
event?.stopPropagation?.();
if (button.disabled) return;
if (event?.type === 'click' && button._handledPointerAction) {
button._handledPointerAction = false;
return;
}
if (event?.type === 'pointerdown') {
button._handledPointerAction = true;
}
handler();
};
button.addEventListener('pointerdown', run);
button.addEventListener('click', run);
};
const getDownloadContext = (button) => {
const downloadId = button?.dataset?.downloadId || '';
const info = this.activeDownloads?.[downloadId];
@@ -4,7 +4,7 @@ import { getModelCardUrl, parseHuggingFaceFileUrl } from "../utils/url_utils.js"
import { getCivitaiModelUrl } from "../globals.js";
import { normalizeSha256 } from "../utils/hash_utils.js";
import { normalizePathIdentity } from "../utils/html_utils.js";
import { bindEventOnce, syncElementAttributes } from "../utils/dom_patch_utils.js";
import { bindEventOnce, bindInstantAction, syncElementAttributes } from "../utils/dom_patch_utils.js";
const log = createModuleLogger('resolve_download_methods');
export const resolveDownloadMethods = {
@@ -1108,25 +1108,6 @@ export const resolveDownloadMethods = {
attachDownloadActionHandlers(progressDiv, downloadId) {
if (!progressDiv) return;
const bindInstantAction = (button, handler) => {
if (!button || button._hasListener) return;
button._hasListener = true;
const run = (event) => {
event?.preventDefault?.();
event?.stopPropagation?.();
if (button.disabled) return;
if (event?.type === 'click' && button._handledPointerAction) {
button._handledPointerAction = false;
return;
}
if (event?.type === 'pointerdown') {
button._handledPointerAction = true;
}
handler(event);
};
button.addEventListener('pointerdown', run);
button.addEventListener('click', run);
};
progressDiv.querySelectorAll('.cancel-download-btn, .cancel-download-btn-pending, .mr-download-queue-cancel').forEach(cancelBtn => {
bindInstantAction(cancelBtn, () => {
const targetDownloadId = cancelBtn.dataset.downloadId || downloadId;
+21
View File
@@ -42,3 +42,24 @@ export function bindEventOnce(element, eventName, handler, key = eventName) {
element.addEventListener(eventName, handler);
return true;
}
export function bindInstantAction(button, handler) {
if (!button || button._hasListener) return false;
button._hasListener = true;
const run = (event) => {
event?.preventDefault?.();
event?.stopPropagation?.();
if (button.disabled) return;
if (event?.type === 'click' && button._handledPointerAction) {
button._handledPointerAction = false;
return;
}
if (event?.type === 'pointerdown') {
button._handledPointerAction = true;
}
handler(event);
};
button.addEventListener('pointerdown', run);
button.addEventListener('click', run);
return true;
}