Files
chrono/src/components/CollectionList.vue
T
2026-07-07 20:58:24 -04:00

156 lines
4.2 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue'
import { CollectionObject } from '@ChronoManager/models/collection';
// Props
const props = defineProps<{
collections: CollectionObject[]
selectedCollection?: CollectionObject | null
type?: 'calendar' | 'tasklist'
}>()
// Emits
const emit = defineEmits<{
'create': []
'select': [collection: CollectionObject]
'edit': [collection: CollectionObject]
'toggle-visibility': [collection: CollectionObject]
}>()
// Computed
const filteredCollections = computed(() => {
if (!props.type) return props.collections
return props.collections.filter(collection => {
if (props.type === 'calendar') {
return collection.properties.content?.includes('event')
} else if (props.type === 'tasklist') {
return collection.properties.content?.includes('task')
}
return true
})
})
const displayTitle = computed(() => {
if (props.type === 'calendar') return 'Calendars'
if (props.type === 'tasklist') return 'Task Lists'
return 'Collections'
})
const displayIcon = computed(() => {
if (props.type === 'calendar') return 'mdi-calendar'
if (props.type === 'tasklist') return 'mdi-checkbox-marked-outline'
return 'mdi-folder'
})
const createLabel = computed(() => {
if (props.type === 'calendar') return 'New Calendar'
if (props.type === 'tasklist') return 'New Task List'
return 'New Collection'
})
// Functions
const onCollectionSelect = (collection: CollectionObject) => {
console.log('[Chrono] - Collection selected', collection)
emit('select', collection)
}
const onCollectionEdit = (collection: CollectionObject) => {
emit('edit', collection)
}
const onToggleVisibility = (collection: CollectionObject) => {
emit('toggle-visibility', collection)
}
</script>
<template>
<div class="collection-selector">
<div class="collection-selector-header">
<div class="d-flex align-center justify-space-between mb-3">
<div class="d-flex align-center">
<v-icon :icon="displayIcon" size="small" class="mr-2" />
<span class="text-subtitle-2 font-weight-bold">{{ displayTitle }}</span>
</div>
<v-btn
icon="mdi-plus"
size="x-small"
variant="text"
:aria-label="createLabel"
:title="createLabel"
@click="emit('create')"
/>
</div>
</div>
<v-divider class="my-2" />
<div class="collection-selector-content">
<v-list density="compact" nav class="pa-0">
<v-list-item
v-for="collection in filteredCollections"
:key="collection.identifier"
:value="collection.identifier"
:active="selectedCollection?.identifier === collection.identifier"
@click="onCollectionSelect(collection)"
rounded="lg"
class="mb-1"
>
<template #prepend>
<v-checkbox-btn
:model-value="collection.properties.visibility !== false"
:color="collection.properties.color || 'primary'"
hide-details
@click.stop="onToggleVisibility(collection)"
/>
</template>
<v-list-item-title>{{ collection.properties.label || 'Unnamed Collection' }}</v-list-item-title>
<template #append>
<div class="d-flex align-center">
<v-icon
:color="collection.properties.color || 'primary'"
size="small"
class="mr-1"
>mdi-circle</v-icon>
<v-btn
icon="mdi-pencil"
size="x-small"
variant="text"
@click.stop="onCollectionEdit(collection)"
/>
</div>
</template>
</v-list-item>
</v-list>
<v-alert v-if="filteredCollections.length === 0" type="info" variant="tonal" density="compact" class="mt-2">
No {{ displayTitle.toLowerCase() }} found
</v-alert>
</div>
</div>
</template>
<style scoped>
.collection-selector {
display: flex;
flex-direction: column;
height: 100%;
}
.collection-selector-header {
flex-shrink: 0;
}
.collection-selector-content {
flex: 1;
overflow-y: auto;
min-height: 0;
}
.v-list-item {
cursor: pointer;
}
</style>