Skip to content

Commit

Permalink
feat(i18n): Add KeyGroupNavigation directive quasarframework#5266, qu…
Browse files Browse the repository at this point in the history
…asarframework#4068, quasarframework#6736

- allow unique TAB target point in a group
- allow key navigation in group
- improve initial focusing on QMenu and QDialog
  • Loading branch information
pdanpdan committed Apr 24, 2020
1 parent e79b697 commit cd4a803
Show file tree
Hide file tree
Showing 23 changed files with 891 additions and 33 deletions.
5 changes: 5 additions & 0 deletions docs/src/assets/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,11 @@ const directives = [
name: 'Go Back (Handling Back Button)',
path: 'go-back'
},
{
name: 'Key Group Navigation',
badge: 'new',
path: 'key-group-navigation'
},
{
name: 'Intersection',
path: 'intersection'
Expand Down
91 changes: 91 additions & 0 deletions docs/src/examples/KeyGroupNavigation/Bar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<template>
<div class="q-pa-md column no-wrap q-gutter-y-md" style="max-width: 500px">
<q-btn color="black" label="First focusable element" />

<q-bar v-key-group-navigation>
<q-btn flat dense color="black" no-caps label="File" class="q-px-sm">
<q-menu>
<q-list dense style="min-width: 100px" v-key-group-navigation>
<q-item clickable v-close-popup>
<q-item-section>Open...</q-item-section>
</q-item>
<q-item clickable v-close-popup>
<q-item-section>New</q-item-section>
</q-item>

<q-separator />

<q-item clickable>
<q-item-section>Preferences</q-item-section>
<q-item-section side>
<q-icon name="keyboard_arrow_right" />
</q-item-section>

<q-menu anchor="top right" self="top left">
<q-list v-key-group-navigation>
<q-item
v-for="n in 3"
:key="n"
dense
clickable
>
<q-item-section>Submenu Label</q-item-section>
<q-item-section side>
<q-icon name="keyboard_arrow_right" />
</q-item-section>
<q-menu auto-close anchor="top right" self="top left">
<q-list v-key-group-navigation>
<q-item
v-for="n in 3"
:key="n"
dense
clickable
>
<q-item-section>3rd level Label</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-item>
</q-list>
</q-menu>
</q-item>

<q-separator />

<q-item clickable v-close-popup>
<q-item-section>Quit</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>

<q-btn flat dense color="black" no-caps label="Edit" class="q-px-sm q-ml-md">
<q-menu auto-close>
<q-list dense style="min-width: 100px" v-key-group-navigation>
<q-item clickable>
<q-item-section>Cut</q-item-section>
</q-item>
<q-item clickable>
<q-item-section>Copy</q-item-section>
</q-item>
<q-item clickable>
<q-item-section>Paste</q-item-section>
</q-item>
<q-separator />
<q-item clickable>
<q-item-section>Select All</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>

<q-space />

<q-btn dense flat icon="minimize" />
<q-btn dense flat icon="crop_square" />
<q-btn dense flat icon="close" />
</q-bar>

<q-btn color="black" label="Last focusable element" />
</div>
</template>
24 changes: 24 additions & 0 deletions docs/src/examples/KeyGroupNavigation/FormControls.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<div class="q-pa-md column no-wrap q-gutter-y-md" style="max-width: 500px">
<q-btn color="black" label="First focusable element" />

<!-- QEditor already uses the keyboard navigation inside -->
<q-editor v-model="editor" min-height="5rem" />

<!-- QDate already uses the keyboard navigation inside -->
<q-date v-model="date" />

<q-btn color="black" label="Last focusable element" />
</div>
</template>

<script>
export default {
data () {
return {
editor: 'What you see is <b>what</b> you get.',
date: '2019/02/01'
}
}
}
</script>
71 changes: 71 additions & 0 deletions docs/src/examples/KeyGroupNavigation/List.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<template>
<div class="q-pa-md column no-wrap q-gutter-y-md" style="max-width: 500px">
<q-btn color="black" label="First focusable element" />

<q-list bordered padding v-key-group-navigation.vertical>
<q-item-label header>List with controls</q-item-label>

<q-item clickable v-ripple>
<q-item-section>
<q-item-label>Vertical navigation</q-item-label>
<q-item-label caption>
Navigate using <kbd>PG_UP</kbd>, <kbd>ARROW_UP</kbd>, <kbd>ARROW_DOWN</kbd>, <kbd>PG_DOWN</kbd>
</q-item-label>
</q-item-section>
</q-item>

<q-item clickable v-ripple>
<q-item-section>
<q-item-label>Horizontal navigation</q-item-label>
<q-item-label caption>
Navigate using <kbd>HOME</kbd>, <kbd>ARROW_LEFT</kbd>, <kbd>ARROW_RIGHT</kbd>, <kbd>END</kbd>
</q-item-label>
</q-item-section>
</q-item>

<q-item clickable v-ripple>
<q-item-section>
<q-item-label>Default</q-item-label>
<q-item-label caption>
Navigate using any of the above keys
</q-item-label>
</q-item-section>
</q-item>

<q-item tag="label" v-ripple>
<q-item-section side top>
<q-checkbox v-model="check" />
</q-item-section>

<q-item-section>
<q-item-label>Notifications</q-item-label>
<q-item-label caption>
Notify me about updates to apps or games that I downloaded
</q-item-label>
</q-item-section>
</q-item>

<q-item tag="label" v-ripple>
<q-item-section>
<q-item-label>Battery too low</q-item-label>
</q-item-section>
<q-item-section side >
<q-toggle color="blue" v-model="notif" val="battery" />
</q-item-section>
</q-item>
</q-list>

<q-btn color="black" label="Last focusable element" />
</div>
</template>

<script>
export default {
data () {
return {
check: true,
notif: true
}
}
}
</script>
76 changes: 76 additions & 0 deletions docs/src/examples/KeyGroupNavigation/Toolbar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<template>
<div class="q-pa-md column no-wrap q-gutter-y-md" style="max-width: 500px">
<q-btn color="black" label="First focusable element" />

<q-toolbar class="bg-primary text-white q-my-md shadow-2" v-key-group-navigation>
<q-btn flat round dense icon="menu" class="q-mr-sm" />
<q-separator dark vertical inset />
<q-btn stretch flat label="Link" />

<q-space />

<q-btn-dropdown stretch flat label="Dropdown">
<q-list v-key-group-navigation>
<q-item-label header>Folders</q-item-label>
<q-item v-for="n in 3" :key="`x.${n}`" clickable v-close-popup tabindex="0">
<q-item-section avatar>
<q-avatar icon="folder" color="secondary" text-color="white" />
</q-item-section>
<q-item-section>
<q-item-label>Photos</q-item-label>
<q-item-label caption>February 22, 2016</q-item-label>
</q-item-section>
<q-item-section side>
<q-icon name="info" />
</q-item-section>
</q-item>
<q-separator inset spaced />
<q-item-label header>Files</q-item-label>
<q-item v-for="n in 3" :key="`y.${n}`" clickable v-close-popup tabindex="0">
<q-item-section avatar>
<q-avatar icon="assignment" color="primary" text-color="white" />
</q-item-section>
<q-item-section>
<q-item-label>Vacation</q-item-label>
<q-item-label caption>February 22, 2016</q-item-label>
</q-item-section>
<q-item-section side>
<q-icon name="info" />
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
<q-separator dark vertical />
<q-btn stretch flat label="Link" />
<q-separator dark vertical />
<q-btn stretch flat label="Link" />
</q-toolbar>

<div class="q-mt-md qmb-sm">
Use <kbd>SHIFT</kbd>+<kbd>TAB</kbd> to get out of the tabs group
</div>

<q-toolbar class="bg-purple text-white shadow-2 rounded-borders" v-key-group-navigation>
<q-btn flat label="Homepage" />
<q-space />

<q-tabs v-model="tab" shrink>
<q-tab name="tab1" label="Tab 1" />
<q-tab name="tab2" label="Tab 2" />
<q-tab name="tab3" label="Tab 3" />
</q-tabs>
</q-toolbar>

<q-btn color="black" label="Last focusable element" />
</div>
</template>

<script>
export default {
data () {
return {
tab: null
}
}
}
</script>
39 changes: 39 additions & 0 deletions docs/src/pages/vue-directives/key-group-navigation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
title: Handling Keyboard Navigation in Groups of Controls
desc: How to improve keyboard accessibility when using groups of controls in a Quasar app.
---
Quasar offers a simple way to improve keyboard accessibility when using a large number of controls that can be grouped.

## Installation
<doc-installation directives="KeyGroupNavigation" />

## Usage
Attach the directive on a group wrapping component or DOM element (like QList, QBar, QToolbar).
Keyboard navigation using `TAB` or `SHIFT` + `TAB` keys will only select one tabbable element inside the group:
- the first / last tabbable element depending on navigation direction when first entering the group
- the last selected tabbable element when the group was visited before
- pressing the `TAB` or `SHIFT` + `TAB` keys when an element is focused inside the group will focus the next tabbable element after the group or the previous une before the group
Keyboard navigation inside the group can be performed using:
- `HOME`, `ARROW_LEFT`, `ARROW_RIGHT` and `END` keys when `horizontal` modifier is used
- `PG_UP`, `ARROW_UP`, `ARROW_DOWN` and `PG_DOWN` keys when `vertical` modifier is used
- any of the above keys when neither `horizontal` nor `vertical` modifiers are used (default)
The navigation wraps at the start / end, moving to the last / first tabbable element.

::: tip
To skip processing key events for some elements set a `q-key-group-navigation--ignore-key` class on them or on a parent of them.
:::

::: warning
Try not to mix keyboard controlled components (like QTab, QTabs, QKnob, QRange, QSlider, QRating, QTime) in key navigation groups as it might get confusing to the user.
:::

<doc-example title="List navigation" file="KeyGroupNavigation/List" />

<doc-example title="Bar navigation" file="KeyGroupNavigation/Bar" />

<doc-example title="Toolbar navigation" file="KeyGroupNavigation/Toolbar" />

<doc-example title="Form controls navigation" file="KeyGroupNavigation/FormControls" />

## KeyGroupNavigation API
<doc-api file="KeyGroupNavigation" />
13 changes: 12 additions & 1 deletion ui/dev/src/pages/components/list-item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@
</q-item-section>
</q-item>

<q-list bordered padding class="q-my-md" :dark="dark" :separator="separator">
<q-item tag="label" dark class="q-my-sm bg-secondary shadow-1" style="border-radius: 30px">
<q-item-section>
<q-item-label>Group Key Navigation in first list</q-item-label>
</q-item-section>

<q-item-section side>
<q-toggle v-model="keyNavEnabled" color="accent" :dark="dark" />
</q-item-section>
</q-item>

<q-list bordered padding class="q-my-md" :dark="dark" :separator="separator" v-key-group-navigation.vertical="keyNavEnabled">
<q-item clickable class="text-primary" @click="onClick">
<q-item-section>Single line item</q-item-section>
</q-item>
Expand Down Expand Up @@ -792,6 +802,7 @@ export default {
return {
dark: null,
separator: false,
keyNavEnabled: true,
check1: true,
check2: false,
Expand Down
Loading

0 comments on commit cd4a803

Please sign in to comment.