fix(ui): command palette accessibility, theme handling, and edge cases
This commit is contained in:
parent
d75f0e3196
commit
7e56fc5030
3 changed files with 33 additions and 6 deletions
|
|
@ -1505,10 +1505,15 @@
|
||||||
placeholder="Search commands, music, settings..."
|
placeholder="Search commands, music, settings..."
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
|
aria-label="Command palette search"
|
||||||
|
role="combobox"
|
||||||
|
aria-expanded="true"
|
||||||
|
aria-controls="command-palette-results"
|
||||||
|
aria-autocomplete="list"
|
||||||
/>
|
/>
|
||||||
<kbd class="command-palette-kbd">ESC</kbd>
|
<kbd class="command-palette-kbd">ESC</kbd>
|
||||||
</div>
|
</div>
|
||||||
<div id="command-palette-results" class="command-palette-results"></div>
|
<div id="command-palette-results" class="command-palette-results" role="listbox"></div>
|
||||||
<div class="command-palette-footer">
|
<div class="command-palette-footer">
|
||||||
<span class="command-palette-hint"><kbd>↑↓</kbd> navigate</span>
|
<span class="command-palette-hint"><kbd>↑↓</kbd> navigate</span>
|
||||||
<span class="command-palette-hint"><kbd>↵</kbd> select</span>
|
<span class="command-palette-hint"><kbd>↵</kbd> select</span>
|
||||||
|
|
|
||||||
|
|
@ -642,6 +642,7 @@ class CommandPalette {
|
||||||
icon: 'search',
|
icon: 'search',
|
||||||
label: 'Search Settings...',
|
label: 'Search Settings...',
|
||||||
keywords: ['setting', 'find', 'search', 'preference', 'option', 'configure'],
|
keywords: ['setting', 'find', 'search', 'preference', 'option', 'configure'],
|
||||||
|
keepOpen: true,
|
||||||
action: () => this.enterSettingsMode(),
|
action: () => this.enterSettingsMode(),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -923,6 +924,7 @@ class CommandPalette {
|
||||||
|
|
||||||
appendMusicGroups(musicGroups) {
|
appendMusicGroups(musicGroups) {
|
||||||
this.removeMusicLoading();
|
this.removeMusicLoading();
|
||||||
|
this.resultsContainer.querySelector('.cmdk-empty')?.remove();
|
||||||
this.resultsContainer.querySelectorAll('[data-music-group]').forEach((el) => el.remove());
|
this.resultsContainer.querySelectorAll('[data-music-group]').forEach((el) => el.remove());
|
||||||
|
|
||||||
let index = this.flatItems.length;
|
let index = this.flatItems.length;
|
||||||
|
|
@ -1000,7 +1002,10 @@ class CommandPalette {
|
||||||
createItemElement(item, index) {
|
createItemElement(item, index) {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
el.className = 'cmdk-item';
|
el.className = 'cmdk-item';
|
||||||
|
el.id = `cmdk-item-${index}`;
|
||||||
|
el.setAttribute('role', 'option');
|
||||||
el.setAttribute('data-index', index);
|
el.setAttribute('data-index', index);
|
||||||
|
el.setAttribute('aria-selected', index === this.selectedIndex ? 'true' : 'false');
|
||||||
if (index === this.selectedIndex) el.setAttribute('data-selected', 'true');
|
if (index === this.selectedIndex) el.setAttribute('data-selected', 'true');
|
||||||
|
|
||||||
let iconHtml = '';
|
let iconHtml = '';
|
||||||
|
|
@ -1041,18 +1046,34 @@ class CommandPalette {
|
||||||
const idx = parseInt(item.getAttribute('data-index'));
|
const idx = parseInt(item.getAttribute('data-index'));
|
||||||
if (idx === this.selectedIndex) {
|
if (idx === this.selectedIndex) {
|
||||||
item.setAttribute('data-selected', 'true');
|
item.setAttribute('data-selected', 'true');
|
||||||
|
item.setAttribute('aria-selected', 'true');
|
||||||
item.scrollIntoView({ block: 'nearest' });
|
item.scrollIntoView({ block: 'nearest' });
|
||||||
} else {
|
} else {
|
||||||
item.removeAttribute('data-selected');
|
item.removeAttribute('data-selected');
|
||||||
|
item.setAttribute('aria-selected', 'false');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.input.setAttribute('aria-activedescendant', `cmdk-item-${this.selectedIndex}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
executeSelected() {
|
async executeSelected() {
|
||||||
const item = this.flatItems[this.selectedIndex];
|
const item = this.flatItems[this.selectedIndex];
|
||||||
if (!item || !item.action) return;
|
if (!item || !item.action) return;
|
||||||
|
|
||||||
item.action();
|
if (item.keepOpen) {
|
||||||
|
try {
|
||||||
|
await item.action();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Command palette action error:', e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await item.action();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Command palette action error:', e);
|
||||||
|
}
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1132,9 +1153,9 @@ class CommandPalette {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTheme(theme) {
|
async setTheme(theme) {
|
||||||
document.documentElement.setAttribute('data-theme', theme);
|
const { themeManager } = await import('./storage.js');
|
||||||
localStorage.setItem('theme', theme);
|
themeManager.setTheme(theme);
|
||||||
const themeOptions = document.querySelectorAll('.theme-option');
|
const themeOptions = document.querySelectorAll('.theme-option');
|
||||||
themeOptions.forEach((opt) => {
|
themeOptions.forEach((opt) => {
|
||||||
if (opt.dataset.theme === theme) opt.classList.add('active');
|
if (opt.dataset.theme === theme) opt.classList.add('active');
|
||||||
|
|
|
||||||
|
|
@ -8527,6 +8527,7 @@ body:has(#side-panel.active) #close-fullscreen-cover-btn {
|
||||||
color: var(--muted-foreground);
|
color: var(--muted-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* stylelint-disable-next-line no-descending-specificity */
|
||||||
.cmdk-item-icon img {
|
.cmdk-item-icon img {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue