Client - handle keyboard navigation on workouts displayed on calendar

This commit is contained in:
Sam 2024-02-04 13:40:52 +01:00
parent f94f0073b5
commit ebb3fb91de
5 changed files with 84 additions and 18 deletions

View File

@ -15,6 +15,7 @@
:workouts="filterWorkouts(day, workouts)"
:sports="sports"
:displayHARecord="displayHARecord"
:index="i"
/>
<div class="calendar-cell-day">
{{ format(day, 'd') }}

View File

@ -1,9 +1,7 @@
<template>
<div
<router-link
class="calendar-workout"
@click="
$router.push({ name: 'Workout', params: { workoutId: workout.id } })
"
:to="{ name: 'Workout', params: { workoutId: workout.id } }"
>
<SportImage
:sport-label="sportLabel"
@ -26,7 +24,7 @@
"
/>
</sup>
</div>
</router-link>
</template>
<script setup lang="ts">

View File

@ -21,6 +21,7 @@
:datasets="chartDatasets"
:colors="colors"
:displayHARecord="displayHARecord"
:index="index"
/>
</div>
</div>
@ -32,6 +33,7 @@
:datasets="chartDatasets"
:colors="colors"
:displayHARecord="displayHARecord"
:index="index"
/>
</div>
</div>
@ -52,10 +54,11 @@
displayHARecord: boolean
workouts: IWorkout[]
sports: ISport[]
index: number
}
const props = defineProps<Props>()
const { displayHARecord, workouts, sports } = toRefs(props)
const { displayHARecord, index, sports, workouts } = toRefs(props)
const chartDatasets = computed(() => getDonutDatasets(props.workouts))
const colors = computed(() => sportIdColors(props.sports))
const displayedWorkoutCount = 6

View File

@ -1,16 +1,22 @@
<template>
<div class="calendar-workouts-chart">
<div class="workouts-chart" @click="togglePane">
<button
class="workouts-chart transparent"
:id="`workouts-donut-${index}`"
@click="togglePane"
>
<div class="workouts-count">{{ workouts.length }}</div>
<DonutChart :datasets="datasets" :colors="colors" />
</div>
</button>
<div class="workouts-pane" v-if="!isHidden">
<div class="more-workouts" v-click-outside="togglePane">
<i
class="fa fa-times calendar-more"
aria-hidden="true"
@click="togglePane"
/>
<div
class="more-workouts"
:id="`workouts-pane-${index}`"
v-click-outside="togglePane"
>
<button class="calendar-more-close transparent" @click="togglePane">
<i class="fa fa-times" aria-hidden="true" />
</button>
<CalendarWorkout
v-for="(workout, index) in workouts"
:key="index"
@ -25,7 +31,7 @@
</template>
<script setup lang="ts">
import { ref, toRefs } from 'vue'
import { nextTick, onMounted, onUnmounted, ref, toRefs } from 'vue'
import CalendarWorkout from '@/components/Dashboard/UserCalendar/CalendarWorkout.vue'
import DonutChart from '@/components/Dashboard/UserCalendar/DonutChart.vue'
@ -39,16 +45,71 @@
sports: ISport[]
workouts: IWorkout[]
displayHARecord: boolean
index: number
}
const props = defineProps<Props>()
let tabbableElementIndex = 0
const { colors, datasets, sports, workouts } = toRefs(props)
const { colors, datasets, index, sports, workouts } = toRefs(props)
const isHidden = ref(true)
function togglePane(event: Event) {
function isWorkoutsMorePaneDisplayed() {
const pane = document.getElementById(`workouts-pane-${index.value}`)
return pane?.children && pane?.children.length > 0 ? pane : null
}
async function togglePane(event: Event) {
event.preventDefault()
event.stopPropagation()
isHidden.value = !isHidden.value
await nextTick()
const pane = isWorkoutsMorePaneDisplayed()
if (!isHidden.value) {
;(pane?.children[0] as HTMLButtonElement).focus()
} else {
document.getElementById(`workouts-donut-${index.value}`)?.focus()
}
}
function handleKey(e: KeyboardEvent) {
if (!isHidden.value) {
// focusTrap
if (!isHidden.value && (e.key === 'Tab' || e.keyCode === 9)) {
e.preventDefault()
e.stopPropagation()
const pane = isWorkoutsMorePaneDisplayed()
if (pane) {
if (e.shiftKey) {
tabbableElementIndex -= 1
if (tabbableElementIndex < 0) {
tabbableElementIndex = pane.children.length - 1
}
} else {
tabbableElementIndex += 1
if (tabbableElementIndex >= pane.children.length) {
tabbableElementIndex = 0
}
}
// children are only links or buttons
;(
pane.children[tabbableElementIndex] as
| HTMLButtonElement
| HTMLLinkElement
).focus()
}
}
// close pane on Escape
if (e.key === 'Escape') {
togglePane(e)
}
}
}
onMounted(() => {
document.addEventListener('keydown', handleKey)
})
onUnmounted(() => {
document.removeEventListener('keydown', handleKey)
})
</script>
<style lang="scss" scoped>
@ -58,6 +119,7 @@
.workouts-chart {
position: relative;
padding: 0;
.workouts-count {
display: flex;
justify-content: center;
@ -108,11 +170,12 @@
flex-wrap: wrap;
z-index: 1000;
.calendar-more {
.calendar-more-close {
position: absolute;
font-size: 0.9em;
top: 5px;
right: 5px;
padding: 0;
}
}
}

View File

@ -95,6 +95,7 @@ button {
&.transparent {
font-family: 'PT Sans', Helvetica, Arial, sans-serif;
font-size: 1em;
background: transparent;
border-color: transparent;
box-shadow: none;