Client - handle keyboard navigation on workouts displayed on calendar
This commit is contained in:
parent
f94f0073b5
commit
ebb3fb91de
@ -15,6 +15,7 @@
|
||||
:workouts="filterWorkouts(day, workouts)"
|
||||
:sports="sports"
|
||||
:displayHARecord="displayHARecord"
|
||||
:index="i"
|
||||
/>
|
||||
<div class="calendar-cell-day">
|
||||
{{ format(day, 'd') }}
|
||||
|
@ -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">
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user