Skip to content

Commit 7580133

Browse files
committed
Keyboard menu navigation
1 parent e8fdfd6 commit 7580133

File tree

7 files changed

+65
-9
lines changed

7 files changed

+65
-9
lines changed

frontend/src/App.vue

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,27 @@ img {
186186
}
187187
}
188188
189+
.icon-button,
190+
.text-button,
191+
.popover-button,
192+
.checkbox-input label,
193+
.color-input .swatch .swatch-button,
194+
.dropdown-input .dropdown-box,
195+
.font-input .dropdown-box,
196+
.radio-input button,
197+
.menu-list {
198+
&:focus {
199+
outline: 1px solid var(--color-accent);
200+
outline-offset: 2px;
201+
}
202+
}
203+
204+
.menu-list {
205+
&:focus {
206+
outline-offset: -1px;
207+
}
208+
}
209+
189210
// For placeholder messages (remove eventually)
190211
.floating-menu {
191212
h1,

frontend/src/components/widgets/floating-menus/DialogModal.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,11 @@ export default defineComponent({
9090
this.dialog.dismissDialog();
9191
},
9292
},
93+
mounted() {
94+
// Focus the first button in the popup
95+
const element = this.$el as Element | null;
96+
const emphasizedOrFirstButton = (element?.querySelector("button.emphasized") as HTMLButtonElement | null) || element?.querySelector("button");
97+
emphasizedOrFirstButton?.focus();
98+
},
9399
});
94100
</script>

frontend/src/components/widgets/floating-menus/MenuList.vue

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<FloatingMenu class="menu-list" :direction="direction" :type="'Dropdown'" ref="floatingMenu" :windowEdgeMargin="0" :scrollableY="scrollableY" data-hover-menu-keep-open>
33
<template v-for="(section, sectionIndex) in menuEntries" :key="sectionIndex">
44
<Separator :type="'List'" :direction="'Vertical'" v-if="sectionIndex > 0" />
5-
<LayoutRow
5+
<button
66
v-for="(entry, entryIndex) in section"
77
:key="entryIndex"
88
class="row"
@@ -31,7 +31,7 @@
3131
v-bind="{ defaultAction, minWidth, drawIcon, scrollableY }"
3232
:ref="(ref: any) => setEntryRefs(entry, ref)"
3333
/>
34-
</LayoutRow>
34+
</button>
3535
</template>
3636
</FloatingMenu>
3737
</template>
@@ -42,6 +42,15 @@
4242
padding: 4px 0;
4343
4444
.row {
45+
display: flex;
46+
flex-direction: row;
47+
flex-grow: 1; // TODO: Note, this is overridden by the flex shorthand rule below
48+
min-width: 0;
49+
min-height: 0;
50+
border: 0;
51+
padding: 0;
52+
text-align: left;
53+
background: none;
4554
height: 20px;
4655
align-items: center;
4756
white-space: nowrap;
@@ -133,7 +142,6 @@ import { defineComponent, PropType } from "vue";
133142
134143
import { IconName } from "@/utilities/icons";
135144
136-
import LayoutRow from "@/components/layout/LayoutRow.vue";
137145
import FloatingMenu, { MenuDirection } from "@/components/widgets/floating-menus/FloatingMenu.vue";
138146
import CheckboxInput from "@/components/widgets/inputs/CheckboxInput.vue";
139147
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
@@ -277,7 +285,6 @@ const MenuList = defineComponent({
277285
IconLabel,
278286
CheckboxInput,
279287
UserInputLabel,
280-
LayoutRow,
281288
},
282289
});
283290
export default MenuList;

frontend/src/components/widgets/inputs/CheckboxInput.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<LayoutRow class="checkbox-input" :class="{ 'outline-style': outlineStyle }">
33
<input type="checkbox" :id="`checkbox-input-${id}`" :checked="checked" @change="(e) => $emit('update:checked', (e.target as HTMLInputElement).checked)" />
4-
<label :for="`checkbox-input-${id}`">
4+
<label :for="`checkbox-input-${id}`" tabindex="0" @keydown.enter="(e) => ((e.target as HTMLElement).previousSibling as HTMLInputElement).click()">
55
<LayoutRow class="checkbox-box">
66
<IconLabel :icon="icon" />
77
</LayoutRow>
@@ -21,6 +21,8 @@
2121
label {
2222
display: flex;
2323
height: 16px;
24+
// Provides rounded corners for the :focus outline
25+
border-radius: 2px;
2426
2527
.checkbox-box {
2628
flex: 0 0 auto;

frontend/src/components/widgets/inputs/DropdownInput.vue

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<template>
22
<LayoutRow class="dropdown-input">
3-
<LayoutRow class="dropdown-box" :class="{ disabled }" :style="{ minWidth: `${minWidth}px` }" @click="() => clickDropdownBox()" data-hover-menu-spawner>
3+
<button class="dropdown-box" :class="{ disabled }" :style="{ minWidth: `${minWidth}px` }" @click="() => clickDropdownBox()" data-hover-menu-spawner>
44
<IconLabel class="dropdown-icon" :icon="activeEntry.icon" v-if="activeEntry.icon" />
55
<span>{{ activeEntry.label }}</span>
66
<IconLabel class="dropdown-arrow" :icon="'DropdownArrow'" />
7-
</LayoutRow>
7+
</button>
88
<MenuList
99
v-model:activeEntry="activeEntry"
1010
@update:activeEntry="(newActiveEntry: typeof MENU_LIST_ENTRY) => activeEntryChanged(newActiveEntry)"
@@ -23,6 +23,15 @@
2323
position: relative;
2424
2525
.dropdown-box {
26+
display: flex;
27+
flex-direction: row;
28+
flex-grow: 1;
29+
min-width: 0;
30+
min-height: 0;
31+
border: 0;
32+
padding: 0;
33+
text-align: left;
34+
2635
align-items: center;
2736
white-space: nowrap;
2837
background: var(--color-1-nearblack);

frontend/src/components/widgets/inputs/FontInput.vue

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<template>
22
<LayoutRow class="font-input">
3-
<LayoutRow class="dropdown-box" :class="{ disabled }" :style="{ minWidth: `${minWidth}px` }" @click="() => clickDropdownBox()" data-hover-menu-spawner>
3+
<button class="dropdown-box" :class="{ disabled }" :style="{ minWidth: `${minWidth}px` }" @click="() => clickDropdownBox()" data-hover-menu-spawner>
44
<span>{{ activeEntry.label }}</span>
55
<IconLabel class="dropdown-arrow" :icon="'DropdownArrow'" />
6-
</LayoutRow>
6+
</button>
77
<MenuList
88
v-model:activeEntry="activeEntry"
99
@widthChanged="(newWidth: number) => onWidthChanged(newWidth)"
@@ -20,6 +20,15 @@
2020
position: relative;
2121
2222
.dropdown-box {
23+
display: flex;
24+
flex-direction: row;
25+
flex-grow: 1;
26+
min-width: 0;
27+
min-height: 0;
28+
border: 0;
29+
padding: 0;
30+
text-align: left;
31+
2332
align-items: center;
2433
white-space: nowrap;
2534
background: var(--color-1-nearblack);

frontend/src/components/widgets/separators/Separator.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
margin: 0 4px;
6666
}
6767
}
68+
69+
min-height: auto !important;
6870
}
6971
</style>
7072

0 commit comments

Comments
 (0)