218 lines
4.1 KiB
Vue
218 lines
4.1 KiB
Vue
<template>
|
|
<recycle-scroller
|
|
class="file-explorer-list"
|
|
:items="items"
|
|
:item-size="44"
|
|
key-field="path"
|
|
>
|
|
<template #before>
|
|
<div aria-hidden="true" class="spacer header"></div>
|
|
</template>
|
|
<template #default="{ item }">
|
|
<t-swipe-cell :disabled="!isSwipeActionVisible(item)" :left="resolveLeftActions(item)">
|
|
<t-cell
|
|
class="cell"
|
|
:title="item.name"
|
|
:arrow="item.type === 'dir'"
|
|
@click="handleOpen(item)"
|
|
>
|
|
<template #note>
|
|
<div class="health">
|
|
<t-loading v-if="item.path === pendingPath" size="1rem" />
|
|
<span v-if="item.type === 'file'" v-text="item.size"></span>
|
|
</div>
|
|
</template>
|
|
<template #left-icon>
|
|
<t-icon :name="item.type === 'dir' ? 'folder' : item.icon" />
|
|
</template>
|
|
</t-cell>
|
|
</t-swipe-cell>
|
|
</template>
|
|
<template #after>
|
|
<div aria-hidden="true" class="spacer content"></div>
|
|
</template>
|
|
</recycle-scroller>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { h, resolveComponent } from "vue";
|
|
import { RecycleScroller } from "vue-virtual-scroller";
|
|
import { type ExplorerItem } from "./fileExplorer.shared";
|
|
import { isBrowserSupportedAudio } from "./fileAudio.shared";
|
|
|
|
const props = defineProps<{
|
|
items: ExplorerItem[];
|
|
pendingPath?: string;
|
|
}>();
|
|
|
|
const emit = defineEmits(["open", "queue", "play"]);
|
|
|
|
function isSwipeActionVisible(item: ExplorerItem): boolean {
|
|
return isBrowserSupportedAudio(item);
|
|
}
|
|
|
|
function resolveLeftActions(item: ExplorerItem) {
|
|
if (!isSwipeActionVisible(item)) {
|
|
return undefined;
|
|
}
|
|
|
|
const buttonComponent = resolveComponent("t-button");
|
|
const iconComponent = resolveComponent("t-icon");
|
|
|
|
return () => h("div", { class: "actions" }, [
|
|
h(buttonComponent, {
|
|
class: "action-btn queue-action",
|
|
size: "small",
|
|
shape: "square",
|
|
theme: "primary",
|
|
variant: "outline",
|
|
onClick: () => handleQueue(item)
|
|
}, {
|
|
icon: () => h(iconComponent, { name: "queue" })
|
|
}),
|
|
h(buttonComponent, {
|
|
class: "action-btn play-action",
|
|
size: "small",
|
|
shape: "square",
|
|
theme: "primary",
|
|
onClick: () => handlePlay(item)
|
|
}, {
|
|
icon: () => h(iconComponent, { name: "play" })
|
|
})
|
|
]);
|
|
}
|
|
|
|
function handleOpen(item: ExplorerItem): void {
|
|
if (props.pendingPath) {
|
|
return;
|
|
}
|
|
|
|
emit("open", item);
|
|
}
|
|
|
|
function handleQueue(item: ExplorerItem): void {
|
|
if (props.pendingPath) {
|
|
return;
|
|
}
|
|
|
|
emit("queue", item);
|
|
}
|
|
|
|
function handlePlay(item: ExplorerItem): void {
|
|
console.log("play", item);
|
|
if (props.pendingPath) {
|
|
return;
|
|
}
|
|
emit("play", item);
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.file-explorer-list {
|
|
--top-gap: var(--page-top-gap, 1rem);
|
|
--bottom-gap: 5.5rem;
|
|
|
|
flex: 1 1 auto;
|
|
height: 100%;
|
|
padding: 0 1rem;
|
|
display: flex;
|
|
overflow: hidden;
|
|
min-height: 0;
|
|
flex-direction: column;
|
|
|
|
&:deep(.vue-recycle-scroller__item-view) {
|
|
|
|
&:first-child {
|
|
|
|
.cell {
|
|
border-radius: var(--td-radius-large) var(--td-radius-large) 0 0;
|
|
}
|
|
}
|
|
|
|
&:last-child {
|
|
|
|
.cell {
|
|
border-radius: 0 0 var(--td-radius-large) var(--td-radius-large);
|
|
|
|
&:after {
|
|
border-bottom: 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.spacer {
|
|
width: 100%;
|
|
pointer-events: none;
|
|
|
|
&.header {
|
|
height: var(--top-gap);
|
|
}
|
|
|
|
&.content {
|
|
height: var(--bottom-gap);
|
|
}
|
|
}
|
|
|
|
.health {
|
|
gap: .375rem;
|
|
display: flex;
|
|
flex: 0 0 auto;
|
|
min-height: 1rem;
|
|
align-items: center;
|
|
white-space: nowrap;
|
|
color: var(--app-sub);
|
|
}
|
|
|
|
&:deep(.actions) {
|
|
gap: .5rem;
|
|
height: 100%;
|
|
display: flex;
|
|
padding: 0 .75rem;
|
|
align-items: center;
|
|
}
|
|
|
|
&:deep(.queue-action) {
|
|
background: rgba(255, 255, 255, .92);
|
|
}
|
|
|
|
&:deep(.play-action) {
|
|
background: var(--td-brand-color);
|
|
}
|
|
|
|
&:deep(.action-btn) {
|
|
width: 2.25rem;
|
|
height: 2.25rem;
|
|
border-radius: .75rem;
|
|
}
|
|
|
|
.cell {
|
|
|
|
&:deep(.t-cell__content) {
|
|
flex: 1 1 auto;
|
|
overflow: hidden;
|
|
min-width: 0;
|
|
}
|
|
|
|
&:deep(.t-cell__title) {
|
|
flex: 1 1 auto;
|
|
overflow: hidden;
|
|
min-width: 0;
|
|
}
|
|
|
|
&:deep(.t-cell__title-text) {
|
|
width: 100%;
|
|
display: block;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
&:deep(.t-cell__note) {
|
|
flex: 0 0 auto;
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
}
|
|
</style>
|