mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-03-26 19:03:08 +00:00
264 lines
7.8 KiB
Text
264 lines
7.8 KiB
Text
<div id="likwid-toasts" class="ui-toasts" aria-live="polite" aria-atomic="true"></div>
|
||
|
||
<dialog id="likwid-dialog" class="ui-dialog">
|
||
<form method="dialog" class="ui-dialog-form">
|
||
<div class="ui-dialog-head">
|
||
<h3 id="likwid-dialog-title" class="ui-dialog-title"></h3>
|
||
<p id="likwid-dialog-message" class="ui-dialog-message"></p>
|
||
</div>
|
||
|
||
<div id="likwid-dialog-input-wrap" class="ui-dialog-input" style="display: none;">
|
||
<label class="ui-label" for="likwid-dialog-textarea" id="likwid-dialog-label"></label>
|
||
<textarea id="likwid-dialog-textarea" class="ui-textarea" rows="4"></textarea>
|
||
</div>
|
||
|
||
<div class="ui-dialog-actions">
|
||
<button id="likwid-dialog-cancel" class="ui-btn ui-btn-secondary" value="cancel" type="submit">Cancel</button>
|
||
<button id="likwid-dialog-confirm" class="ui-btn ui-btn-primary" value="confirm" type="submit">Confirm</button>
|
||
</div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<script is:inline>
|
||
(function() {
|
||
const toastsEl = document.getElementById('likwid-toasts');
|
||
const dialogEl = document.getElementById('likwid-dialog');
|
||
const dialogTitleEl = document.getElementById('likwid-dialog-title');
|
||
const dialogMessageEl = document.getElementById('likwid-dialog-message');
|
||
const dialogInputWrapEl = document.getElementById('likwid-dialog-input-wrap');
|
||
const dialogLabelEl = document.getElementById('likwid-dialog-label');
|
||
const dialogTextareaEl = document.getElementById('likwid-dialog-textarea');
|
||
const dialogConfirmEl = document.getElementById('likwid-dialog-confirm');
|
||
const dialogCancelEl = document.getElementById('likwid-dialog-cancel');
|
||
|
||
function toast(type, message, opts) {
|
||
if (!toastsEl) return;
|
||
|
||
const options = opts || {};
|
||
const id = 't_' + Math.random().toString(16).slice(2);
|
||
const el = document.createElement('div');
|
||
el.className = 'ui-toast ui-toast-' + (type || 'info');
|
||
el.setAttribute('role', type === 'error' ? 'alert' : 'status');
|
||
el.dataset.toastId = id;
|
||
|
||
const text = document.createElement('div');
|
||
text.className = 'ui-toast-text';
|
||
text.textContent = String(message || '');
|
||
|
||
const close = document.createElement('button');
|
||
close.type = 'button';
|
||
close.className = 'ui-toast-close';
|
||
close.setAttribute('aria-label', 'Dismiss notification');
|
||
close.textContent = '×';
|
||
close.addEventListener('click', () => {
|
||
el.remove();
|
||
});
|
||
|
||
el.appendChild(text);
|
||
el.appendChild(close);
|
||
toastsEl.appendChild(el);
|
||
|
||
const timeoutMs = typeof options.timeoutMs === 'number' ? options.timeoutMs : 4500;
|
||
if (timeoutMs > 0) {
|
||
window.setTimeout(() => {
|
||
el.remove();
|
||
}, timeoutMs);
|
||
}
|
||
}
|
||
|
||
function openDialog(mode, opts) {
|
||
const options = opts || {};
|
||
|
||
if (!dialogEl || !dialogTitleEl || !dialogMessageEl || !dialogConfirmEl || !dialogCancelEl) {
|
||
return Promise.resolve(mode === 'prompt' ? null : false);
|
||
}
|
||
|
||
if (typeof dialogEl.showModal !== 'function') {
|
||
if (mode === 'prompt') {
|
||
return Promise.resolve(window.prompt(options.message || options.title || '', ''));
|
||
}
|
||
return Promise.resolve(window.confirm(options.message || options.title || 'Confirm?'));
|
||
}
|
||
|
||
dialogTitleEl.textContent = String(options.title || '');
|
||
dialogMessageEl.textContent = String(options.message || '');
|
||
|
||
const isPrompt = mode === 'prompt';
|
||
if (dialogInputWrapEl && dialogTextareaEl && dialogLabelEl) {
|
||
dialogInputWrapEl.style.display = isPrompt ? 'block' : 'none';
|
||
dialogLabelEl.textContent = String(options.label || '');
|
||
dialogTextareaEl.value = String(options.defaultValue || '');
|
||
dialogTextareaEl.placeholder = String(options.placeholder || '');
|
||
}
|
||
|
||
dialogConfirmEl.textContent = String(options.confirmText || (isPrompt ? 'Submit' : 'Confirm'));
|
||
dialogCancelEl.textContent = String(options.cancelText || 'Cancel');
|
||
|
||
return new Promise((resolve) => {
|
||
function cleanup() {
|
||
dialogEl.removeEventListener('close', onClose);
|
||
}
|
||
|
||
function onClose() {
|
||
const rv = dialogEl.returnValue;
|
||
if (mode === 'prompt') {
|
||
if (rv !== 'confirm') {
|
||
cleanup();
|
||
resolve(null);
|
||
return;
|
||
}
|
||
const v = dialogTextareaEl ? dialogTextareaEl.value : '';
|
||
cleanup();
|
||
resolve(v);
|
||
return;
|
||
}
|
||
|
||
cleanup();
|
||
resolve(rv === 'confirm');
|
||
}
|
||
|
||
dialogEl.addEventListener('close', onClose);
|
||
dialogEl.showModal();
|
||
if (mode === 'prompt' && dialogTextareaEl) {
|
||
dialogTextareaEl.focus();
|
||
} else {
|
||
dialogConfirmEl.focus();
|
||
}
|
||
});
|
||
}
|
||
|
||
window.likwidUi = window.likwidUi || {};
|
||
window.likwidUi.toast = toast;
|
||
window.likwidUi.confirm = function(opts) { return openDialog('confirm', opts); };
|
||
window.likwidUi.prompt = function(opts) { return openDialog('prompt', opts); };
|
||
})();
|
||
</script>
|
||
|
||
<style>
|
||
.ui-toasts {
|
||
position: fixed;
|
||
top: 1rem;
|
||
right: 1rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.75rem;
|
||
z-index: 9999;
|
||
width: min(420px, calc(100vw - 2rem));
|
||
pointer-events: none;
|
||
}
|
||
|
||
.ui-toast {
|
||
pointer-events: auto;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 0.75rem;
|
||
padding: 0.875rem 1rem;
|
||
border-radius: 12px;
|
||
border: 1px solid var(--color-border);
|
||
background: color-mix(in srgb, var(--color-surface) 92%, transparent);
|
||
backdrop-filter: blur(10px);
|
||
-webkit-backdrop-filter: blur(10px);
|
||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.22);
|
||
}
|
||
|
||
.ui-toast-text {
|
||
color: var(--color-text);
|
||
font-size: 0.9375rem;
|
||
line-height: 1.35;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.ui-toast-close {
|
||
appearance: none;
|
||
background: transparent;
|
||
border: 0;
|
||
color: var(--color-text-muted);
|
||
font-size: 1.25rem;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
padding: 0;
|
||
margin: -2px 0 0 0;
|
||
}
|
||
|
||
.ui-toast-close:hover {
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.ui-toast-success {
|
||
border-color: color-mix(in srgb, var(--color-success) 45%, var(--color-border));
|
||
background: color-mix(in srgb, var(--color-success-muted) 65%, var(--color-surface));
|
||
}
|
||
|
||
.ui-toast-error {
|
||
border-color: color-mix(in srgb, var(--color-error) 45%, var(--color-border));
|
||
background: color-mix(in srgb, var(--color-error-muted) 65%, var(--color-surface));
|
||
}
|
||
|
||
.ui-toast-info {
|
||
border-color: color-mix(in srgb, var(--color-info) 45%, var(--color-border));
|
||
background: color-mix(in srgb, var(--color-info-muted) 65%, var(--color-surface));
|
||
}
|
||
|
||
.ui-dialog {
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 14px;
|
||
padding: 0;
|
||
background: var(--color-surface);
|
||
color: var(--color-text);
|
||
width: min(560px, calc(100vw - 2rem));
|
||
}
|
||
|
||
.ui-dialog::backdrop {
|
||
background: var(--color-overlay);
|
||
}
|
||
|
||
.ui-dialog-form {
|
||
padding: 1rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.ui-dialog-title {
|
||
margin: 0;
|
||
font-size: 1.05rem;
|
||
}
|
||
|
||
.ui-dialog-message {
|
||
margin: 0.25rem 0 0 0;
|
||
color: var(--color-text-muted);
|
||
font-size: 0.9375rem;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.ui-dialog-input {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.ui-label {
|
||
font-weight: 600;
|
||
font-size: 0.9375rem;
|
||
}
|
||
|
||
.ui-textarea {
|
||
width: 100%;
|
||
border-radius: 12px;
|
||
border: 1px solid var(--color-border);
|
||
background: var(--color-field-bg);
|
||
color: var(--color-text);
|
||
padding: 0.75rem;
|
||
font-size: 0.9375rem;
|
||
resize: vertical;
|
||
min-height: 88px;
|
||
}
|
||
|
||
.ui-dialog-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 0.75rem;
|
||
}
|
||
</style>
|