Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Allow enabling or disabling interactivity of waveform #531

Merged
merged 3 commits into from
Dec 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/AudioDetails/Related.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{{ $t('audio-details.related-audios') }}
</h4>
<template v-if="!$fetchState.error">
<AudioTrack
<VAudioTrack
v-for="audio in audios"
:key="audio.id"
:audio="audio"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="audio-controller">
<Waveform
<VWaveform
v-bind="waveformProps"
:message="message ? $t(`audio-track.messages.${message}`) : null"
:current-time="currentTime"
Expand All @@ -27,9 +27,15 @@
</template>

<script>
import { computed, ref, useStore, watch } from '@nuxtjs/composition-api'
import {
computed,
defineComponent,
ref,
useStore,
watch,
} from '@nuxtjs/composition-api'

import Waveform from '~/components/AudioTrack/Waveform'
import VWaveform from '~/components/VAudioTrack/VWaveform.vue'

import { ACTIVE } from '~/constants/store-modules.js'
import {
Expand All @@ -42,9 +48,9 @@ import {
* HTMLAudioElement and the Active Media Store. Also displays the waveform that
* is deeply linked to timekeeping for the HTMLAudioElement.
*/
export default {
name: 'AudioController',
components: { Waveform },
export default defineComponent({
name: 'VAudioController',
components: { VWaveform },
inheritAttrs: false,
model: {
prop: 'status',
Expand Down Expand Up @@ -201,5 +207,5 @@ export default {
handleSeeked,
}
},
}
})
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
>
<Component :is="layoutComponent" :audio="audio" :size="size">
<template #controller="controllerProps">
<AudioController
<VAudioController
v-model="status"
v-bind="controllerProps"
:audio="audio"
Expand All @@ -17,7 +17,7 @@
</template>

<template #play-pause="playPauseProps">
<PlayPause
<VPlayPause
v-model="status"
v-bind="playPauseProps"
:disabled="!isReady"
Expand All @@ -28,31 +28,31 @@
</template>

<script>
import { computed, ref } from '@nuxtjs/composition-api'
import { computed, defineComponent, ref } from '@nuxtjs/composition-api'

import PlayPause from '~/components/AudioTrack/PlayPause.vue'
import AudioController from '~/components/AudioTrack/AudioController.vue'
import VPlayPause from '~/components/VAudioTrack/VPlayPause.vue'
import VAudioController from '~/components/VAudioTrack/VAudioController.vue'

import Full from '~/components/AudioTrack/layouts/Full.vue'
import Row from '~/components/AudioTrack/layouts/Row.vue'
import Box from '~/components/AudioTrack/layouts/Box.vue'
import Global from '~/components/AudioTrack/layouts/Global.vue'
import VFullLayout from '~/components/VAudioTrack/layouts/VFullLayout.vue'
import VRowLayout from '~/components/VAudioTrack/layouts/VRowLayout.vue'
import VBoxLayout from '~/components/VAudioTrack/layouts/VBoxLayout.vue'
import VGlobalLayout from '~/components/VAudioTrack/layouts/VGlobalLayout.vue'

/**
* Displays the waveform and basic information about the track, along with
* controls to play, pause or seek to a point on the track.
*/
export default {
name: 'AudioTrack',
export default defineComponent({
name: 'VAudioTrack',
components: {
AudioController,
PlayPause,
VAudioController,
VPlayPause,

// Layouts
Full,
Row,
Box,
Global,
VFullLayout,
VRowLayout,
VBoxLayout,
VGlobalLayout,
},
props: {
/**
Expand Down Expand Up @@ -105,10 +105,10 @@ export default {
/* Layout */

const layoutMappings = {
full: 'Full',
row: 'Row',
box: 'Box',
global: 'Global',
full: 'VFullLayout',
row: 'VRowLayout',
box: 'VBoxLayout',
global: 'VGlobalLayout',
}
const layoutComponent = computed(() => layoutMappings[props.layout])

Expand All @@ -123,5 +123,5 @@ export default {
layoutComponent,
}
},
}
})
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
</template>

<script>
import { defineComponent } from '@nuxtjs/composition-api'

import VIconButton from '~/components/VIconButton/VIconButton.vue'

import playIcon from '~/assets/icons/play.svg'
Expand All @@ -19,8 +21,8 @@ import replayIcon from '~/assets/icons/replay.svg'
* Displays the control for switching between the playing and paused states of
* a media file.
*/
export default {
name: 'PlayPause',
export default defineComponent({
name: 'VPlayPause',
components: { VIconButton },
inheritAttrs: false,
model: {
Expand Down Expand Up @@ -72,5 +74,5 @@ export default {
this.$emit('toggle', this.isPlaying ? 'paused' : 'playing')
},
},
}
})
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,20 @@
'--usable-height': `${Math.floor(usableFrac * 100)}%`,
'--unusable-height': `${Math.floor((1 - usableFrac) * 100)}%`,
}"
:tabIndex="!message && isReady ? 0 : -1"
:role="!message && isReady ? 'slider' : null"
:aria-disabled="!(!message && isReady)"
:tabIndex="isInteractive ? 0 : -1"
:role="isInteractive ? 'slider' : null"
:aria-disabled="!isInteractive"
:aria-label="$t('waveform.label')"
aria-orientation="horizontal"
aria-valuemin="0"
:aria-valuemax="duration"
:aria-valuenow="currentTime"
:aria-valuetext="currentTimeText"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@mouseleave="handleMouseLeave"
@keydown.arrow-left.prevent="handleArrowKeys"
@keydown.arrow-right.prevent="handleArrowKeys"
@keydown.home.prevent="handlePosKeys(0)"
@keydown.end.prevent="handlePosKeys(1)"
v-on="eventHandlers"
>
<!-- Focus ring -->
<svg
v-if="isInteractive"
class="hidden group-focus:block absolute inset-0 w-full h-full z-20 shadow-ring-1"
xmlns="http://www.w3.org/2000/svg"
:viewBox="viewBox"
Expand Down Expand Up @@ -94,8 +88,9 @@
/>
</svg>

<!-- Keyboard focus -->
<!-- Focus bar -->
<div
v-if="isInteractive"
class="focus-indicator hidden absolute z-30 top-0 flex flex-col items-center justify-between bg-black h-full"
:style="{ width: `${barWidth}px`, left: `${seekSpaceBefore}px` }"
>
Expand Down Expand Up @@ -158,6 +153,7 @@
<script>
import {
computed,
defineComponent,
onBeforeUnmount,
onMounted,
ref,
Expand All @@ -168,8 +164,8 @@ import { downsampleArray, upsampleArray } from '~/utils/resampling'
* Renders an SVG representation of the waveform given a list of heights for the
* bars.
*/
export default {
name: 'Waveform',
export default defineComponent({
name: 'VWaveform',
props: {
/**
* an array of heights of the bars; The waveform will be generated with
Expand Down Expand Up @@ -212,11 +208,11 @@ export default {
},
/**
* selectively enable features in the waveform; Available features are
* `'timestamp'` and `'duration'`.
* `'timestamp'`, `'duration'`, `'seek'`.
*/
features: {
type: Array,
default: () => ['timestamps'],
default: () => ['timestamps', 'seek'],
},
},
setup(props, { emit }) {
Expand Down Expand Up @@ -286,10 +282,12 @@ export default {

const showDuration = computed(() => props.features.includes('duration'))
const showTimestamps = computed(() => props.features.includes('timestamps'))
const isSeekable = computed(() => props.features.includes('seek'))

/* State */

const isReady = computed(() => !props.message)
const isInteractive = computed(() => isSeekable.value && isReady.value)

/* Resampling */

Expand Down Expand Up @@ -437,6 +435,29 @@ export default {
emit('seeked', currentFrac.value + delta)
}
}
const handleKeys = (event) => {
event.preventDefault()
if (['ArrowLeft', 'ArrowRight'].includes(event.key))
return handleArrowKeys(event)
if (event.key === 'Home') return handlePosKeys(0)
if (event.key === 'End') return handlePosKeys(1)
}

/* v-on */

const eventHandlers = computed(() => {
if (isInteractive.value) {
return {
mousedown: handleMouseDown,
mousemove: handleMouseMove,
mouseup: handleMouseUp,
mouseleave: handleMouseLeave,
keydown: handleKeys,
}
} else {
return {}
}
})

return {
timeFmt,
Expand All @@ -445,8 +466,10 @@ export default {

showDuration,
showTimestamps,
isSeekable,

isReady,
isInteractive,

barWidth,
normalizedPeaks,
Expand Down Expand Up @@ -477,6 +500,8 @@ export default {

handlePosKeys,
handleArrowKeys,

eventHandlers,
}
},
computed: {
Expand All @@ -489,7 +514,7 @@ export default {
return this.$t('waveform.current-time', { time })
},
},
}
})
</script>

<style scoped lang="css">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
</template>

<script>
import { computed } from '@nuxtjs/composition-api'
import { computed, defineComponent } from '@nuxtjs/composition-api'

import VLicense from '~/components/License/VLicense.vue'

export default {
name: 'Box',
components: { VLicense },
export default defineComponent({
name: 'VBoxLayout',
components: {
VLicense,
},
props: ['audio', 'size'],
setup(props) {
const isSmall = computed(() => props.size === 's')
Expand All @@ -54,7 +56,7 @@ export default {
width,
}
},
}
})
</script>

<style>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@
</template>

<script>
import { computed } from '@nuxtjs/composition-api'
import { computed, defineComponent } from '@nuxtjs/composition-api'

import DownloadButton from '~/components/DownloadButton.vue'

export default {
name: 'Full',
export default defineComponent({
name: 'VFullLayout',
components: {
DownloadButton,
},
Expand Down Expand Up @@ -119,7 +119,7 @@ export default {
isSmall,
}
},
}
})
</script>

<style>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@
</template>

<script>
import { defineComponent } from '@nuxtjs/composition-api'

import AudioThumbnail from '~/components/AudioThumbnail/AudioThumbnail.vue'

export default {
name: 'Global',
components: { AudioThumbnail },
export default defineComponent({
name: 'VGlobalLayout',
components: {
AudioThumbnail,
},
props: ['audio'],
}
})
</script>

<style>
Expand Down
Loading