generated from alphane/template
127 lines
3.1 KiB
Vue
127 lines
3.1 KiB
Vue
<template>
|
|
<div>
|
|
<div
|
|
class="deck-node d-flex align-center px-3 py-2 rounded"
|
|
:class="{ selected: isSelected }"
|
|
@click="$emit('select', deck)"
|
|
>
|
|
<v-btn
|
|
icon
|
|
variant="text"
|
|
density="comfortable"
|
|
size="default"
|
|
class="mr-1"
|
|
@click.stop="expanded = !expanded"
|
|
>
|
|
<v-icon size="default">
|
|
{{ expanded ? "mdi-chevron-down" : "mdi-chevron-right" }}
|
|
</v-icon>
|
|
</v-btn>
|
|
<v-icon
|
|
size="default"
|
|
class="mr-2 text-medium-emphasis"
|
|
:icon="expanded && hasChildren ? 'mdi-folder-open-outline' : 'mdi-folder-outline'"
|
|
/>
|
|
<span
|
|
class="flex-grow-1 text-body-1"
|
|
:class="{ 'font-weight-medium': isSelected }"
|
|
>
|
|
{{ deck.name }}
|
|
</span>
|
|
<div
|
|
class="kebab-btn"
|
|
@click.stop
|
|
>
|
|
<FlashcardDeckKebab
|
|
:flashcard-deck-id="deck.id"
|
|
@created="$emit('created', $event)"
|
|
@updated="$emit('updated', $event)"
|
|
@deleted="$emit('deleted', $event)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-show="expanded"
|
|
class="pl-5"
|
|
>
|
|
<draggable
|
|
v-model="children"
|
|
:group="{ name: 'decks' }"
|
|
item-key="id"
|
|
@change="onChange"
|
|
>
|
|
<template #item="{ element }">
|
|
<FlashcardDeckTreeNode
|
|
v-model:children="element.children"
|
|
:deck="element"
|
|
:selected-id="selectedId"
|
|
@select="$emit('select', $event)"
|
|
@move="$emit('move', $event)"
|
|
@created="$emit('created', $event)"
|
|
@updated="$emit('updated', $event)"
|
|
@deleted="$emit('deleted', $event)"
|
|
/>
|
|
</template>
|
|
</draggable>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed } from "vue"
|
|
import draggable from "vuedraggable"
|
|
|
|
import type { DeckNode } from "@/components/flashcard-decks/FlashcardDeckTree.vue"
|
|
import FlashcardDeckKebab from "@/components/flashcard-decks/FlashcardDeckKebab.vue"
|
|
|
|
const props = defineProps<{
|
|
deck: DeckNode
|
|
selectedId?: number | null
|
|
}>()
|
|
|
|
const children = defineModel<DeckNode[]>("children", { required: true })
|
|
|
|
const emit = defineEmits<{
|
|
select: [deck: DeckNode]
|
|
move: [payload: { deckId: number; newParentId: number | null }]
|
|
created: [flashcardDeckId: number]
|
|
updated: [flashcardDeckId: number]
|
|
deleted: [flashcardDeckId: number]
|
|
}>()
|
|
|
|
const expanded = ref(false)
|
|
const isSelected = computed(() => props.selectedId === props.deck.id)
|
|
const hasChildren = computed(() => children.value.length > 0)
|
|
|
|
function onChange(event: { added?: { element: DeckNode } }) {
|
|
if (event.added) {
|
|
emit("move", { deckId: event.added.element.id, newParentId: props.deck.id })
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.deck-node {
|
|
cursor: grab;
|
|
user-select: none;
|
|
}
|
|
|
|
.deck-node:hover {
|
|
background: rgb(var(--v-theme-hoverColor));
|
|
}
|
|
|
|
.deck-node.selected {
|
|
background: rgb(var(--v-theme-lightprimary));
|
|
}
|
|
|
|
.kebab-btn {
|
|
opacity: 0;
|
|
transition: opacity 0.15s;
|
|
}
|
|
|
|
.deck-node:hover .kebab-btn {
|
|
opacity: 1;
|
|
}
|
|
</style>
|