Files
dcalc/design/index-web.html
T
2026-08-19 19:45:04 +08:00

640 lines
29 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=1920, initial-scale=1" />
<title>DNF UI Component Library — Abstracted Preview</title>
<link rel="icon" href="src-web/shared/img/sprite_interface.NPK/windowcommon.chn.img/301.PNG" />
<link rel="stylesheet" href="src-web/index.css" />
</head>
<body>
<section class="board" id="board-1">
<header class="board-header">
<div>
<div class="board-kicker">DNF UI Component Library / Abstracted Preview</div>
<h1 class="board-title">Foundations & Construction Anatomy</h1>
<div class="board-subtitle">截图定义视觉语法,素材定义构造方式;组件不包含任何游戏截图裁片</div>
</div>
<div class="board-index"><strong>01 / 06</strong>FOUNDATIONS · TOKENS · ASSETS</div>
</header>
<div class="content">
<div class="component-reference" data-component-src="src-web/foundations/index.html#colors"></div>
<div class="grid cols-3" style="height:350px;margin-bottom:18px;">
<div class="component-reference" data-component-src="src-web/button/index.html#construction"></div>
<div class="component-reference" data-component-src="src-web/dialog/index.html#construction"></div>
<div class="component-reference" data-component-src="src-web/shared/index.html#glyphs"></div>
</div>
<div class="grid cols-2" style="height:276px;">
<div class="component-reference" data-component-src="src-web/foundations/index.html#typography"></div>
<div class="component-reference" data-component-src="src-web/foundations/index.html#architecture"></div>
</div>
</div>
<div class="footer-note"><span>PREVIEW V4 · TOKEN-FIRST</span><span>SEMANTIC STATES ≠ ITEM QUALITY</span></div>
</section>
<section class="board" id="board-2">
<header class="board-header">
<div>
<div class="board-kicker">DNF UI Component Library / Abstracted Preview</div>
<h1 class="board-title">Core Controls</h1>
<div class="board-subtitle">可替换文案、图标和状态;保持深蓝动作面、黄铜边线与紧凑密度</div>
</div>
<div class="board-index"><strong>02 / 06</strong>BUTTON · FIELD · SELECTION · RATE</div>
</header>
<div class="content">
<div class="component-reference" data-component-src="src-web/button/index.html#showcase"></div>
<div class="grid cols-2" style="height:430px;margin-bottom:18px;">
<div class="component-reference" data-component-src="src-web/input/index.html#showcase"></div>
<div class="component-reference" data-component-src="src-web/select/index.html#showcase"></div>
</div>
<div class="grid cols-2" style="height:292px;">
<div class="component-reference" data-component-src="src-web/form-controls/index.html#showcase"></div>
<div class="component-reference" data-component-src="src-web/slider/index.html#showcase"></div>
</div>
</div>
<div class="footer-note"><span>PREVIEW V4 · NORMALIZED COMPONENTS</span><span>TREE SELECTED = OUTLINE ONLY · QUALITY TONE PRESERVED</span></div>
</section>
<section class="board" id="board-3">
<header class="board-header">
<div>
<div class="board-kicker">DNF UI Component Library / Abstracted Preview</div>
<h1 class="board-title">Navigation & Overlays</h1>
<div class="board-subtitle">相同行为共享 primitive,不同 anatomy 保持独立 visual family</div>
</div>
<div class="board-index"><strong>03 / 06</strong>TABS · DISCLOSURE · MENU · WINDOW</div>
</header>
<div class="content">
<div class="grid cols-2" style="height:275px;margin-bottom:18px;">
<div class="component-reference" data-component-src="src-web/tabs/index.html#showcase"></div>
<div class="component-reference" data-component-src="src-web/menu/index.html#showcase"></div>
</div>
<div class="grid cols-3" style="height:265px;margin-bottom:18px;">
<div class="component-reference" data-component-src="src-web/navigation/index.html#favorite-drawer"></div>
<div class="component-reference" data-component-src="src-web/navigation/index.html#settings-nav"></div>
<div class="component-reference" data-component-src="src-web/data-section/index.html#showcase"></div>
</div>
<div class="grid cols-2" style="height:442px;">
<div class="component-reference" data-component-src="src-web/dialog/index.html#showcase"></div>
<div class="component-reference" data-component-src="src-web/scrollbar/index.html#showcase"></div>
</div>
</div>
<div class="footer-note"><span>PREVIEW V4 · VISUAL FAMILY BOUNDARIES</span><span>DIALOG BODY SHELL ≠ CONTENT WELL</span></div>
</section>
<section class="board" id="board-4">
<header class="board-header">
<div>
<div class="board-kicker">DNF UI Component Library / Abstracted Preview</div>
<h1 class="board-title">Data Display & Product Patterns</h1>
<div class="board-subtitle">由小组件组成设置表单、数据表、稀有度槽位和状态反馈,不把整张界面做成组件</div>
</div>
<div class="board-index"><strong>04 / 06</strong>FORM ROW · TABLE · BADGE · QUALITY</div>
</header>
<div class="content">
<div class="grid cols-2" style="height:560px;margin-bottom:18px;grid-template-columns:.92fr 1.28fr;">
<div class="component-reference" data-component-src="src-web/settings/index.html#showcase"></div>
<div class="component-reference" data-component-src="src-web/data-table/index.html#composition"></div>
</div>
<div class="grid cols-3" style="height:390px;grid-template-columns:1.25fr .8fr 1fr;">
<div class="component-reference" data-component-src="src-web/rarity/index.html#showcase"></div>
<div class="component-reference" data-component-src="src-web/rarity/index.html#badges"></div>
<div class="component-reference" data-component-src="src-web/foundations/index.html#evidence"></div>
</div>
</div>
<div class="footer-note"><span>PREVIEW V4 · PRODUCT PATTERNS</span><span>SMALL COMPONENTS → COMPOSED DNF INTERFACES</span></div>
</section>
<section class="board" id="board-5">
<header class="board-header">
<div>
<div class="board-kicker">DNF UI Component Library / Abstracted Preview</div>
<h1 class="board-title">Search & Complex Dialog Composition</h1>
<div class="board-subtitle">搜索组件保持原生输入语法;复杂弹窗由 WindowDialog、List、Table 与 Plain Scrollbar 组合</div>
</div>
<div class="board-index"><strong>05 / 06</strong>AUTOCOMPLETE · SEARCH TREE · DIALOG PRESET</div>
</header>
<div class="content">
<div class="component-reference" data-component-src="src-web/autocomplete/index.html#showcase"></div>
<div class="component-reference" data-component-src="src-web/complex-dialog/index.html#preset"></div>
</div>
<div class="footer-note"><span>PREVIEW V4 · COMPOSITION TEMPLATE</span><span>WINDOW PRIMITIVE + LIST + TABLE + PLAIN SCROLLBAR</span></div>
</section>
<section class="board" id="board-6">
<header class="board-header">
<div>
<div class="board-kicker">DNF UI Component Library / Abstracted Preview</div>
<h1 class="board-title">DataTable Density & Item Variants</h1>
<div class="board-subtitle">同一表格语义,拆分技能明细、紧凑记录、DataTable ItemRow 与无表头 ShopItemRow</div>
</div>
<div class="board-index"><strong>06 / 06</strong>SORT · COMPACT · ITEM ROW · SHOP ITEM</div>
</header>
<div class="content table-variant-board">
<div class="component-reference" data-component-src="src-web/data-table/index.html#skill"></div>
<div class="grid cols-3 table-variant-grid">
<div class="component-reference" data-component-src="src-web/data-table/index.html#compact"></div>
<div class="component-reference" data-component-src="src-web/data-table/index.html#item"></div>
<div class="component-reference" data-component-src="src-web/shop-item-row/index.html#showcase"></div>
</div>
</div>
<div class="footer-note"><span>PREVIEW V4 · DATATABLE VARIANT FAMILIES</span><span>SORTABLE HEADER + SELECTABLE ROW + SUMMARY + GROUP NOTE</span></div>
</section>
<script src="src-web/shared/component-loader.js"></script>
<script>
window.componentsReady.then(() => {
document.querySelectorAll('.panel').forEach((panel) => {
const windows = [...panel.querySelectorAll('.window')];
if (!windows.length) return;
const focusWindow = (activeWindow) => {
windows.forEach((windowNode) => {
const isActive = windowNode === activeWindow;
windowNode.classList.toggle('is-focused', isActive);
windowNode.classList.toggle('is-unfocused', !isActive);
});
};
windows.forEach((windowNode) => {
const titlebar = windowNode.querySelector(':scope > .window-titlebar');
if (titlebar && !titlebar.querySelector('.window-title-effect')) {
const effect = document.createElement('img');
effect.className = 'window-title-effect';
effect.src = 'src-web/dialog/img/sprite_interface.NPK/windowheadereffect.img/0.PNG';
effect.alt = '';
effect.setAttribute('aria-hidden', 'true');
titlebar.appendChild(effect);
}
windowNode.addEventListener('pointerdown', () => focusWindow(windowNode));
});
focusWindow(windows[0]);
});
let windowHeaderEffectFrame = 0;
window.setInterval(() => {
windowHeaderEffectFrame = (windowHeaderEffectFrame + 1) % 45;
document.querySelectorAll('.window.is-focused > .window-titlebar .window-title-effect').forEach((effect) => {
effect.src = `src-web/dialog/img/sprite_interface.NPK/windowheadereffect.img/${windowHeaderEffectFrame}.PNG`;
});
}, 66);
document.querySelectorAll('[data-tabs]').forEach((tabs) => {
const marker = tabs.querySelector(':scope > .tab-marker');
const equalizeItems = () => {
const labels = [...tabs.querySelectorAll('.game-tab .tab-label')];
if (!labels.length) return;
const widestLabel = Math.max(0, ...labels.map((label) => label.getBoundingClientRect().width));
const tabsStyle = getComputedStyle(tabs);
const buttonStyle = getComputedStyle(labels[0].closest('.game-tab'));
const groupPadding = parseFloat(tabsStyle.paddingLeft) + parseFloat(tabsStyle.paddingRight);
const itemPadding = parseFloat(buttonStyle.paddingLeft) + parseFloat(buttonStyle.paddingRight);
const contentWidth = Math.ceil(widestLabel + itemPadding);
const parentWidth = tabs.parentElement?.clientWidth ?? tabs.clientWidth;
const availableWidth = Math.max(0, parentWidth - groupPadding);
const itemWidth = Math.min(contentWidth, availableWidth / labels.length);
tabs.style.setProperty('--tab-item-width', `${itemWidth}px`);
tabs.style.setProperty('--tab-group-width', `${itemWidth * labels.length + groupPadding}px`);
};
const positionMarker = (tab, animate = true) => {
if (!marker || !tab) return;
const tabsBox = tabs.getBoundingClientRect();
const tabBox = tab.getBoundingClientRect();
if (!animate) marker.classList.remove('is-ready');
marker.style.left = `${tabBox.left - tabsBox.left + tabBox.width / 2}px`;
if (!marker.classList.contains('is-ready')) {
requestAnimationFrame(() => marker.classList.add('is-ready'));
}
};
equalizeItems();
positionMarker(tabs.querySelector('.game-tab.selected'), false);
document.fonts?.ready.then(() => {
equalizeItems();
positionMarker(tabs.querySelector('.game-tab.selected'), false);
});
tabs.querySelectorAll('.game-tab').forEach((tab) => {
tab.addEventListener('click', () => {
tabs.querySelectorAll('.game-tab').forEach((item) => {
item.classList.remove('selected');
});
tab.classList.add('selected');
positionMarker(tab);
});
});
if ('ResizeObserver' in window) {
const resizeObserver = new ResizeObserver(() => {
equalizeItems();
positionMarker(tabs.querySelector('.game-tab.selected'), false);
});
resizeObserver.observe(tabs);
if (tabs.parentElement) resizeObserver.observe(tabs.parentElement);
}
});
const onKeyboardActivate = (element, action) => {
element?.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
action(event);
}
});
};
document.querySelectorAll('[data-select-demo]').forEach((select) => {
const trigger = select.querySelector('[role="combobox"]');
const value = select.querySelector('[data-select-value]');
const setOpen = (open) => {
select.classList.toggle('is-open', open);
trigger?.setAttribute('aria-expanded', String(open));
};
const toggle = () => setOpen(!select.classList.contains('is-open'));
trigger?.addEventListener('click', toggle);
onKeyboardActivate(trigger, toggle);
select.querySelectorAll('[role="option"]').forEach((option) => {
const choose = () => {
select.querySelectorAll('[role="option"]').forEach((item) => {
item.classList.toggle('selected', item === option);
item.classList.remove('focused');
item.setAttribute('aria-selected', String(item === option));
});
if (value) value.textContent = option.textContent;
setOpen(false);
trigger?.focus();
};
option.addEventListener('click', choose);
onKeyboardActivate(option, choose);
});
});
document.querySelectorAll('[data-tree-select]').forEach((treeSelect) => {
const trigger = treeSelect.querySelector('[role="combobox"]');
const value = treeSelect.querySelector('[data-tree-select-value]');
const parent = treeSelect.querySelector('.tree-parent');
const setOpen = (open) => {
treeSelect.classList.toggle('is-open', open);
trigger?.setAttribute('aria-expanded', String(open));
};
const toggle = () => setOpen(!treeSelect.classList.contains('is-open'));
trigger?.addEventListener('click', toggle);
onKeyboardActivate(trigger, toggle);
const toggleBranch = () => {
const collapsed = !treeSelect.classList.contains('is-tree-collapsed');
treeSelect.classList.toggle('is-tree-collapsed', collapsed);
parent?.setAttribute('aria-expanded', String(!collapsed));
};
parent?.addEventListener('click', toggleBranch);
onKeyboardActivate(parent, toggleBranch);
treeSelect.querySelectorAll('.tree-select-item:not(.tree-parent)').forEach((item) => {
const choose = () => {
treeSelect.querySelectorAll('.tree-select-item:not(.tree-parent)').forEach((option) => {
option.classList.toggle('is-selected', option === item);
option.setAttribute('aria-selected', String(option === item));
});
if (value) value.textContent = item.textContent;
setOpen(false);
trigger?.focus();
};
item.addEventListener('click', choose);
onKeyboardActivate(item, choose);
});
});
document.querySelectorAll('[data-checkbox]').forEach((checkbox) => {
const toggle = () => {
const checked = checkbox.getAttribute('aria-checked') !== 'true';
checkbox.setAttribute('aria-checked', String(checked));
checkbox.querySelector('.checkbox-control')?.classList.toggle('is-checked', checked);
const label = checkbox.querySelector('[data-control-label]');
if (label) label.textContent = `交互示例 · ${checked ? '已选择' : '未选择'}`;
};
checkbox.addEventListener('click', toggle);
onKeyboardActivate(checkbox, toggle);
});
document.querySelectorAll('[data-radio-group]').forEach((group) => {
const radios = [...group.querySelectorAll('[data-radio]')];
const selectRadio = (radio) => {
radios.forEach((item) => {
const selected = item === radio;
item.setAttribute('aria-checked', String(selected));
item.tabIndex = selected ? 0 : -1;
});
radio.focus();
};
radios.forEach((radio, index) => {
radio.addEventListener('click', () => selectRadio(radio));
radio.addEventListener('keydown', (event) => {
if (!['ArrowDown', 'ArrowRight', 'ArrowUp', 'ArrowLeft'].includes(event.key)) return;
event.preventDefault();
const direction = event.key === 'ArrowDown' || event.key === 'ArrowRight' ? 1 : -1;
selectRadio(radios[(index + direction + radios.length) % radios.length]);
});
onKeyboardActivate(radio, () => selectRadio(radio));
});
});
document.querySelectorAll('[data-switch]').forEach((switchControl) => {
const label = switchControl.querySelector('[data-control-label]');
const render = (checked) => {
if (label) label.textContent = `Interactive · ${checked ? 'On' : 'Off'}`;
};
const toggle = () => {
const checked = switchControl.getAttribute('aria-checked') !== 'true';
switchControl.setAttribute('aria-checked', String(checked));
render(checked);
};
switchControl.addEventListener('click', toggle);
onKeyboardActivate(switchControl, toggle);
});
document.querySelectorAll('[data-rate]').forEach((rate) => {
const stars = [...rate.querySelectorAll('[data-rate-item]')];
const render = (value) => stars.forEach((star, index) => {
star.src = `src-web/form-controls/img/sprite_interface2_popup_menu.NPK/popup_icon_npc.img/${index < value ? 126 : 149}.PNG`;
});
const setValue = (value) => {
const next = Math.max(0, Math.min(5, value));
rate.setAttribute('aria-valuenow', String(next));
render(next);
};
stars.forEach((star, index) => {
star.addEventListener('pointerenter', () => render(index + 1));
star.addEventListener('click', () => setValue(index + 1));
});
rate.addEventListener('pointerleave', () => render(Number(rate.getAttribute('aria-valuenow'))));
rate.addEventListener('keydown', (event) => {
const current = Number(rate.getAttribute('aria-valuenow'));
if (event.key === 'ArrowRight' || event.key === 'ArrowUp') setValue(current + 1);
else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') setValue(current - 1);
else if (event.key === 'Home') setValue(0);
else if (event.key === 'End') setValue(5);
else return;
event.preventDefault();
});
});
document.querySelectorAll('[data-slider]').forEach((slider) => {
const track = slider.querySelector('.slider-track');
const valueLabel = slider.querySelector('.slider-value');
const setValue = (value) => {
const next = Math.max(0, Math.min(100, Math.round(value)));
slider.style.setProperty('--value', `${next}%`);
slider.setAttribute('aria-valuenow', String(next));
if (valueLabel) valueLabel.textContent = `${next}%`;
};
const setFromPointer = (event) => {
const rect = track.getBoundingClientRect();
setValue(((event.clientX - rect.left) / rect.width) * 100);
};
slider.addEventListener('pointerdown', (event) => {
slider.classList.add('is-dragging');
slider.setPointerCapture(event.pointerId);
setFromPointer(event);
});
slider.addEventListener('pointermove', (event) => {
if (slider.classList.contains('is-dragging')) setFromPointer(event);
});
const stopDragging = () => slider.classList.remove('is-dragging');
slider.addEventListener('pointerup', stopDragging);
slider.addEventListener('pointercancel', stopDragging);
slider.addEventListener('keydown', (event) => {
const current = Number(slider.getAttribute('aria-valuenow'));
if (event.key === 'ArrowRight' || event.key === 'ArrowUp') setValue(current + 1);
else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') setValue(current - 1);
else if (event.key === 'Home') setValue(0);
else if (event.key === 'End') setValue(100);
else return;
event.preventDefault();
});
});
document.querySelectorAll('[data-autocomplete]').forEach((autocomplete) => {
const trigger = autocomplete.querySelector('[role="combobox"]');
const menu = autocomplete.querySelector('[role="listbox"]');
const value = autocomplete.querySelector('[data-autocomplete-value]');
const setOpen = (open) => {
if (menu) menu.hidden = !open;
trigger?.setAttribute('aria-expanded', String(open));
};
const toggle = () => setOpen(trigger?.getAttribute('aria-expanded') !== 'true');
trigger?.addEventListener('click', toggle);
onKeyboardActivate(trigger, toggle);
autocomplete.querySelectorAll('[role="option"]').forEach((option) => {
const choose = () => {
autocomplete.querySelectorAll('[role="option"]').forEach((item) => {
item.classList.toggle('selected', item === option);
item.setAttribute('aria-selected', String(item === option));
});
if (value) value.textContent = option.textContent;
setOpen(false);
trigger?.focus();
};
option.addEventListener('click', choose);
onKeyboardActivate(option, choose);
});
});
document.querySelectorAll('[data-cascader]').forEach((cascader) => {
const trigger = cascader.querySelector('.cascader-trigger.primary');
const value = cascader.querySelector('.cascader-value');
trigger?.addEventListener('click', () => cascader.classList.toggle('is-open'));
cascader.querySelectorAll('.cascader-option').forEach((option) => {
option.addEventListener('click', () => {
cascader.querySelectorAll('.cascader-option').forEach((item) => item.classList.remove('focused'));
option.classList.add('focused');
value.textContent = option.textContent;
cascader.classList.remove('is-open');
});
});
});
const setDisclosureState = (section, expanded) => {
const trigger = section.querySelector('.disclosure-head');
const body = section.querySelector('.disclosure-body');
const icon = trigger?.querySelector('img');
const state = trigger?.querySelector('.disclosure-state');
trigger?.setAttribute('aria-expanded', String(expanded));
section.classList.toggle('is-collapsed', !expanded);
if (body) body.hidden = !expanded;
if (icon) icon.src = expanded ? icon.dataset.expandedIcon : icon.dataset.collapsedIcon;
if (state) state.textContent = expanded ? '已展开' : '未展开';
};
document.querySelectorAll('[data-disclosure]').forEach((section) => {
const trigger = section.querySelector('.disclosure-head');
trigger?.addEventListener('click', () => {
const expanded = trigger.getAttribute('aria-expanded') !== 'true';
if (expanded) {
section.closest('.data-section-stack')?.querySelectorAll('[data-disclosure]').forEach((sibling) => {
if (sibling !== section) setDisclosureState(sibling, false);
});
}
setDisclosureState(section, expanded);
});
});
document.querySelectorAll('[data-favorite-drawer]').forEach((drawer) => {
const trigger = drawer.querySelector('.disclosure-head');
const body = drawer.querySelector('.disclosure-body');
const state = drawer.querySelector('.favorite-drawer-state');
const arrow = drawer.querySelector('.favorite-drawer-arrow');
trigger?.addEventListener('click', () => {
const expanded = trigger.getAttribute('aria-expanded') !== 'true';
trigger.setAttribute('aria-expanded', String(expanded));
drawer.classList.toggle('is-collapsed', !expanded);
if (body) body.hidden = !expanded;
if (state) state.textContent = expanded ? '已展开' : '已收起';
if (arrow) arrow.textContent = expanded ? '▲' : '▼';
});
});
document.querySelectorAll('[data-number-input]').forEach((numberInput) => {
const value = numberInput.querySelector('[data-number-value]');
numberInput.querySelectorAll('[data-step]').forEach((control) => {
control.addEventListener('click', () => {
const next = Number(value?.textContent || 0) + Number(control.dataset.step || 0);
if (value) value.textContent = String(Math.max(0, next));
});
});
});
document.querySelectorAll('[data-sortable-table]').forEach((table) => {
table.querySelectorAll('.sortable-header').forEach((header) => {
header.addEventListener('click', () => {
const wasSorted = header.classList.contains('is-sorted');
const wasDescending = header.getAttribute('aria-sort') === 'descending';
table.querySelectorAll('.sortable-header').forEach((item) => {
item.classList.remove('is-sorted');
item.removeAttribute('aria-sort');
const glyph = item.querySelector('.sort-glyph');
if (glyph) glyph.textContent = '△';
});
const descending = wasSorted && !wasDescending;
header.classList.add('is-sorted');
header.setAttribute('aria-sort', descending ? 'descending' : 'ascending');
const glyph = header.querySelector('.sort-glyph');
if (glyph) glyph.textContent = descending ? '▽' : '△';
});
});
});
const selectTableRow = (row) => {
row.closest('.data-table')?.querySelectorAll('[data-selectable-row]').forEach((item) => item.classList.remove('is-selected'));
row.classList.add('is-selected');
};
document.querySelectorAll('[data-selectable-row]').forEach((row) => {
row.addEventListener('click', () => selectTableRow(row));
row.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
selectTableRow(row);
}
});
});
document.querySelectorAll('[data-selection-theme]').forEach((button) => {
button.addEventListener('click', () => {
const panel = button.closest('.panel-body');
const table = panel?.querySelector('[data-data-table]');
panel?.querySelectorAll('[data-selection-theme]').forEach((item) => item.classList.toggle('is-active', item === button));
table?.classList.toggle('selection-gold', button.dataset.selectionTheme === 'gold');
table?.classList.toggle('selection-blue', button.dataset.selectionTheme === 'blue');
});
});
const updateDialogStatus = (control, message) => {
const dialog = control.closest('.window');
const status = dialog?.querySelector('.dialog-status');
if (!status) return;
status.textContent = message;
status.classList.remove('is-updated');
requestAnimationFrame(() => status.classList.add('is-updated'));
};
document.querySelectorAll('[data-dialog-action]').forEach((control) => {
control.addEventListener('click', () => {
const message = control.dataset.dialogAction || '操作已执行';
updateDialogStatus(control, message);
if (message.includes('关闭')) {
const dialog = control.closest('.window');
dialog?.classList.remove('is-demo-dismissed');
requestAnimationFrame(() => dialog?.classList.add('is-demo-dismissed'));
window.setTimeout(() => dialog?.classList.remove('is-demo-dismissed'), 300);
}
});
});
document.querySelectorAll('[data-dialog-toggle]').forEach((control) => {
control.addEventListener('click', () => {
const next = control.getAttribute('aria-pressed') !== 'true';
control.setAttribute('aria-pressed', String(next));
const checkbox = control.querySelector('.checkbox-control');
checkbox?.classList.toggle('is-checked', next);
const label = control.classList.contains('sheet-filter-button')
? `筛选${next ? '已展开' : '已收起'}`
: control.classList.contains('show-all')
? `查看全部${next ? '已开启' : '已关闭'}`
: `显示模式${next ? '已切换' : '已还原'}`;
updateDialogStatus(control, label);
});
});
document.querySelectorAll('[data-preset-edit]').forEach((control) => {
control.addEventListener('click', (event) => {
event.stopPropagation();
const row = control.closest('.preset-row');
const active = !row?.classList.contains('is-renaming');
row?.classList.toggle('is-renaming', active);
control.setAttribute('aria-pressed', String(active));
updateDialogStatus(control, active ? '预设已进入重命名状态' : '预设名称已确认');
});
});
document.querySelectorAll('[data-preset-delete]').forEach((control) => {
control.addEventListener('click', (event) => {
event.stopPropagation();
const row = control.closest('.preset-row');
const removed = !row?.classList.contains('is-demo-deleted');
row?.classList.toggle('is-demo-deleted', removed);
control.setAttribute('aria-pressed', String(removed));
updateDialogStatus(control, removed ? '预设已标记移除,再次点击可恢复' : '预设已恢复');
});
});
});
</script>
</body>
</html>