201 lines
7.5 KiB
JavaScript
201 lines
7.5 KiB
JavaScript
(function () {
|
|
if (window.appDialog) return;
|
|
|
|
const state = {
|
|
root: null,
|
|
panel: null,
|
|
title: null,
|
|
message: null,
|
|
field: null,
|
|
input: null,
|
|
cancel: null,
|
|
confirm: null,
|
|
lastFocused: null,
|
|
queue: Promise.resolve()
|
|
};
|
|
|
|
function ensureRoot() {
|
|
if (state.root) return;
|
|
|
|
const root = document.createElement('div');
|
|
root.className = 'tm-dialog-root';
|
|
root.hidden = true;
|
|
root.innerHTML = [
|
|
'<div class="tm-dialog-backdrop" data-dialog-close="cancel"></div>',
|
|
'<div class="tm-dialog-panel" role="dialog" aria-modal="true" aria-labelledby="tmDialogTitle">',
|
|
' <div class="tm-dialog-kicker">FSE RAILWAY</div>',
|
|
' <h3 class="tm-dialog-title" id="tmDialogTitle">系统提示</h3>',
|
|
' <div class="tm-dialog-message"></div>',
|
|
' <label class="tm-dialog-field" hidden>',
|
|
' <span class="tm-dialog-field-label">输入内容</span>',
|
|
' <input class="tm-dialog-input" type="text" />',
|
|
' </label>',
|
|
' <div class="tm-dialog-actions">',
|
|
' <button type="button" class="btn tm-dialog-cancel">取消</button>',
|
|
' <button type="button" class="btn primary tm-dialog-confirm">确定</button>',
|
|
' </div>',
|
|
'</div>'
|
|
].join('');
|
|
|
|
document.body.appendChild(root);
|
|
|
|
state.root = root;
|
|
state.panel = root.querySelector('.tm-dialog-panel');
|
|
state.title = root.querySelector('.tm-dialog-title');
|
|
state.message = root.querySelector('.tm-dialog-message');
|
|
state.field = root.querySelector('.tm-dialog-field');
|
|
state.input = root.querySelector('.tm-dialog-input');
|
|
state.cancel = root.querySelector('.tm-dialog-cancel');
|
|
state.confirm = root.querySelector('.tm-dialog-confirm');
|
|
}
|
|
|
|
function whenReady() {
|
|
if (document.body) return Promise.resolve();
|
|
return new Promise((resolve) => {
|
|
document.addEventListener('DOMContentLoaded', resolve, { once: true });
|
|
});
|
|
}
|
|
|
|
function normalizeOptions(type, value, fallbackValue) {
|
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
return {
|
|
type,
|
|
title: value.title || (type === 'confirm' ? '请确认' : type === 'prompt' ? '请输入内容' : '系统提示'),
|
|
message: value.message || '',
|
|
confirmText: value.confirmText || (type === 'alert' ? '知道了' : '确定'),
|
|
cancelText: value.cancelText || '取消',
|
|
defaultValue: value.defaultValue == null ? '' : String(value.defaultValue),
|
|
placeholder: value.placeholder || ''
|
|
};
|
|
}
|
|
|
|
return {
|
|
type,
|
|
title: type === 'confirm' ? '请确认' : type === 'prompt' ? '请输入内容' : '系统提示',
|
|
message: value == null ? '' : String(value),
|
|
confirmText: type === 'alert' ? '知道了' : '确定',
|
|
cancelText: '取消',
|
|
defaultValue: fallbackValue == null ? '' : String(fallbackValue),
|
|
placeholder: ''
|
|
};
|
|
}
|
|
|
|
function setOpen(open) {
|
|
ensureRoot();
|
|
state.root.hidden = !open;
|
|
state.root.classList.toggle('is-open', open);
|
|
}
|
|
|
|
async function showDialog(options) {
|
|
await whenReady();
|
|
ensureRoot();
|
|
|
|
return new Promise((resolve) => {
|
|
setOpen(true);
|
|
|
|
state.lastFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
state.title.textContent = options.title;
|
|
state.message.textContent = options.message;
|
|
state.confirm.textContent = options.confirmText;
|
|
state.cancel.textContent = options.cancelText;
|
|
|
|
const isPrompt = options.type === 'prompt';
|
|
const showCancel = options.type !== 'alert';
|
|
|
|
state.field.hidden = !isPrompt;
|
|
state.input.value = options.defaultValue || '';
|
|
state.input.placeholder = options.placeholder || '';
|
|
state.cancel.hidden = !showCancel;
|
|
|
|
let settled = false;
|
|
|
|
const close = (result, shouldRestoreFocus = true) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
document.removeEventListener('keydown', onKeydown, true);
|
|
state.root.removeEventListener('click', onRootClick, true);
|
|
setOpen(false);
|
|
if (shouldRestoreFocus && state.lastFocused && typeof state.lastFocused.focus === 'function') {
|
|
window.setTimeout(() => state.lastFocused.focus(), 0);
|
|
}
|
|
resolve(result);
|
|
};
|
|
|
|
const onKeydown = (event) => {
|
|
if (event.key === 'Escape') {
|
|
event.preventDefault();
|
|
if (options.type === 'alert') close(undefined);
|
|
else close(null);
|
|
return;
|
|
}
|
|
if (event.key === 'Enter') {
|
|
const target = event.target;
|
|
if (target === state.cancel) return;
|
|
event.preventDefault();
|
|
if (isPrompt) close(state.input.value);
|
|
else if (options.type === 'confirm') close(true);
|
|
else close(undefined);
|
|
}
|
|
};
|
|
|
|
const onRootClick = (event) => {
|
|
const action = event.target && event.target.getAttribute && event.target.getAttribute('data-dialog-close');
|
|
if (action === 'cancel') {
|
|
if (options.type === 'alert') close(undefined);
|
|
else close(null);
|
|
return;
|
|
}
|
|
if (event.target === state.cancel) {
|
|
close(null);
|
|
return;
|
|
}
|
|
if (event.target === state.confirm) {
|
|
if (isPrompt) close(state.input.value);
|
|
else if (options.type === 'confirm') close(true);
|
|
else close(undefined);
|
|
}
|
|
};
|
|
|
|
document.addEventListener('keydown', onKeydown, true);
|
|
state.root.addEventListener('click', onRootClick, true);
|
|
|
|
window.setTimeout(() => {
|
|
if (isPrompt) state.input.focus();
|
|
else state.confirm.focus();
|
|
}, 0);
|
|
}).then((result) => {
|
|
if (options.type === 'confirm') return result === true;
|
|
if (options.type === 'prompt') return result == null ? null : String(result);
|
|
return undefined;
|
|
});
|
|
}
|
|
|
|
function enqueue(task) {
|
|
state.queue = state.queue.then(task, task);
|
|
return state.queue;
|
|
}
|
|
|
|
const api = {
|
|
alert(message) {
|
|
return enqueue(() => showDialog(normalizeOptions('alert', message)));
|
|
},
|
|
confirm(message) {
|
|
return enqueue(() => showDialog(normalizeOptions('confirm', message)));
|
|
},
|
|
prompt(message, defaultValue) {
|
|
return enqueue(() => showDialog(normalizeOptions('prompt', message, defaultValue)));
|
|
}
|
|
};
|
|
|
|
window.appDialog = api;
|
|
window.alert = function (message) {
|
|
return api.alert(message);
|
|
};
|
|
window.confirm = function (message) {
|
|
return api.confirm(message);
|
|
};
|
|
window.prompt = function (message, defaultValue) {
|
|
return api.prompt(message, defaultValue);
|
|
};
|
|
})();
|