Skip to content
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
9 changes: 3 additions & 6 deletions src/components/common/VideoCardMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -139,23 +139,20 @@
</v-list-item>

<template v-if="$store.getters['isSuperuser']">
<!-- <v-list-item> -->
<v-lazy>
<watch-quick-editor :video="video" />
</v-lazy>
<!-- </v-list-item> -->
<watch-quick-editor :video="video" />
</template>
</v-list>
</template>

<script>
import { dayjs } from "@/utils/time";
import copyToClipboard from "@/mixins/copyToClipboard";
import WatchQuickEditor from "@/components/watch/WatchQuickEditor.vue";
import VideoQuickPlaylist from "@/components/playlist/VideoQuickPlaylist.vue";

export default {
components: {
WatchQuickEditor: () => import("@/components/watch/WatchQuickEditor.vue"),
WatchQuickEditor,
VideoQuickPlaylist,
},
mixins: [copyToClipboard],
Expand Down
57 changes: 46 additions & 11 deletions src/components/video/VideoCard.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<a
<div
class="video-card no-decoration d-flex"
:class="{
'video-card-fluid': fluid,
Expand All @@ -9,24 +9,24 @@
'video-card-multiview-active': inMultiViewActiveVideos,
'flex-column': !horizontal && !denseList,
}"
:target="redirectMode ? '_blank' : ''"
:href="href"
rel="noopener"
draggable="true"
style="position: relative"
@click.exact="onThumbnailClicked"
@dragstart="drag"
>
<!-- Video Image with Duration -->
<div
<a
v-if="!denseList"
:target="redirectMode ? '_blank' : ''"
:href="href"
rel="noopener"
style="position: relative; width: 100%"
class="video-thumbnail white--text rounded flex-shrink-0 d-flex"
:style="
horizontal &&
!shouldHideThumbnail &&
`background: url(${imageSrc}) center/cover;`
"
@click.exact="onThumbnailClicked"
>
<PlaceholderOverlay
v-if="shouldShowPlaceholderOverlay"
Expand Down Expand Up @@ -135,7 +135,7 @@
width="100%"
:aspect-ratio="60 / 9"
/>
</div>
</a>
<a
class="d-flex flex-row flex-grow-1 no-decoration video-card-text"
:href="watchLink"
Expand Down Expand Up @@ -249,12 +249,14 @@
</div>
<!-- Vertical dots menu -->
<v-menu
ref="videoMenu"
v-model="showMenu"
bottom
left
:close-on-content-click="false"
nudge-top="20px"
nudge-left="40px"
content-class="video-card-menu-content"
>
<template #activator="{ on, attrs }">
<v-btn
Expand All @@ -279,17 +281,23 @@
>
<template v-if="activePlaylistItem">
<button @click.stop.prevent="move(data.id, 'up')">
<v-icon small> {{ icons.mdiChevronUp }} </v-icon>
<v-icon small>
{{ icons.mdiChevronUp }}
</v-icon>
</button>
<button
@click.stop.prevent="
$store.commit('playlist/removeVideoByID', data.id)
"
>
<v-icon small> {{ icons.mdiDelete }} </v-icon>
<v-icon small>
{{ icons.mdiDelete }}
</v-icon>
</button>
<button @click.stop.prevent="move(data.id, 'down')">
<v-icon small> {{ icons.mdiChevronDown }} </v-icon>
<v-icon small>
{{ icons.mdiChevronDown }}
</v-icon>
</button>
</template>
<slot name="action" />
Expand All @@ -301,7 +309,7 @@
v-model="placeholderOpen"
:video="data"
/>
</a>
</div>
</template>

<script lang="ts">
Expand Down Expand Up @@ -586,6 +594,20 @@ export default {
// created() {
// this.data = this.video || this.source;
// },
watch: {
showMenu(val) {
if (val) {
this.$nextTick(() => {
// Menu keyboard navigation works as long as either the activator element or the generated content div has focus.
// The activator element is initially be focused when the menu opens, but it loses its focus when
// menu items or another video card are hovered, page is scrolled, page loses focus, and possibly other cases.
// It also can't regain focus because it's hidden after menu opening, unless it's temporarily made visible again.
// Instead, it's easier to just focus the generated content div when the activator element loses focus.
this.$refs.videoMenu?.getActivator().addEventListener("blur", this.ensureVideoMenuFocusHandler);
});
}
},
},
created() {
this.$store.getters["history/hasWatched"](this.data.id)
.then((x) => {
Expand Down Expand Up @@ -614,6 +636,7 @@ export default {
clearInterval(this.updatecycle);
this.updatecycle = null;
}
this.$refs.videoMenu?.getActivator()?.removeEventListener("blur", this.ensureVideoMenuFocusHandler);
},
methods: {
formatDuration,
Expand Down Expand Up @@ -703,6 +726,13 @@ export default {
}
this.$store.commit("playlist/reorder", { from: curIdx, to: toIdx });
},
ensureVideoMenuFocusHandler() {
// note: this is its own method so that multiple addEventListener(..., this handler) is idempotent
const menuContent = this.$refs.videoMenu?.$refs.content;
if (!menuContent) return;
if (menuContent.tabIndex === -1) menuContent.tabIndex = 0; // ensure its focusable
menuContent.focus({ preventScroll: true });
},
},
};
</script>
Expand Down Expand Up @@ -949,4 +979,9 @@ export default {
.plain-button:hover:before {
background-color: transparent;
}

/* prevent focus ring on menu */
.video-card-menu-content:focus-visible {
outline: none;
}
</style>
2 changes: 2 additions & 0 deletions src/components/watch/WatchQuickEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
label="Add Mentioned Channels"
no-filter
style="min-width: 300px"
@keydown.stop
>
<template #selection="selection">
<ChannelChip
Expand Down Expand Up @@ -168,6 +169,7 @@
:append-outer-icon="mdiContentSave"
@click="loadTopics"
@click:append-outer="saveTopic"
@keydown.stop
/>
</v-col>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/views/channel_views/ChannelVideos.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
dense
/>
<!-- Render skeleton items when data hasn't loaded yet -->
<SkeletonCardList v-if="isLoading" :cols="cols" dense />
<SkeletonCardList v-if="isLoading" :cols="colSizes" dense />
</generic-list-loader>
</template>

Expand Down
Loading