-
-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathTagAssign.vue
More file actions
129 lines (120 loc) · 3.66 KB
/
TagAssign.vue
File metadata and controls
129 lines (120 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<template>
<modal
:active="active"
:title="t('modal.tags')"
size="xl2"
:child="true"
@closed="emit('closed')"
>
<div class="grow grid grid-cols-1 grid-rows-2 xs:grid-cols-2 xs:grid-rows-1 gap-4">
<div class="max-h-[calc(50vh_-_6rem)] xs:max-h-[calc(66.666667vh_-_8.25rem)] flex flex-col gap-4">
<label class="relative">
<icon-filter class="absolute top-2 left-2 w-5 h-5 stroke-1.5 text-blade-500" />
<input
type="search"
v-model="searchInput"
class="w-full pl-8"
:placeholder="t('placeholder.searchTagName')"
/>
</label>
<div class="overflow-y-scroll flex flex-col gap-2">
<label v-for="tag in filteredTags" :key="tag.key" class="flex items-center gap-2">
<input
v-model="selectedTags"
:value="tag.key"
type="checkbox"
class="w-6 h-6 ml-2"
/>
{{ tag[loc] ? tag[loc] : tag.key }}
</label>
</div>
</div>
<div>
<div v-if="selectedTags.length == 0" class="flex flex-col items-center gap-2">
<icon-tags class="w-12 h-12 stroke-1 text-blade-500" />
<div class="text-lg">{{ t('text.noTagsSelected') }}</div>
<div class="text-blade-500 text-center w-4/5">{{ t('text.selectSomeTags') }}</div>
</div>
<div v-else class="flex flex-col gap-2">
<div class="text-lg text-center mb-2">{{ t('text.selection') }}</div>
<div v-for="tag in sortedSelectedTags" :key="tag" class="flex items-center gap-2">
<icon-tag class="w-4 h-4" />
{{ tags[tag][loc] ? tags[tag][loc] : tag }}
<button
class="ml-auto"
@click="selectedTags = selectedTags.filter(k => k !== tag)"
>
<icon-x class="w-4 h-4 text-blade-500" />
</button>
</div>
</div>
</div>
</div>
<div class="flex flex-col justify-end items-center gap-4 2xs:flex-row">
<button class="px-3 py-2 text-blade-500" aria-label="Cancel" @click.prevent="emit('closed')">
{{ t('button.cancel') }}
</button>
<primary-button @click="emit('assign', selectedTags)">
{{ t('button.assign') }}
<icon-arrow-back class="w-6 h-6 stroke-1.5" />
</primary-button>
</div>
</modal>
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import Modal from '@/elements/Modal.vue';
import PrimaryButton from '@/elements/PrimaryButton.vue';
// icons
import {
IconArrowBack,
IconTags,
IconTag,
IconX,
IconFilter
} from '@tabler/icons-vue';
// component constants
const { t, locale } = useI18n();
const loc = locale.value.substring(0, 2);
// user input properties
const selectedTags = ref([]);
const searchInput = ref('');
// inherited properties
const props = defineProps({
active: Boolean, // state of modal display, true to show modal
tags: Object, // list of all available tags
assignedTags: Array, // already assigned tags
});
const initInput = () => {
selectedTags.value = props.assignedTags ?? [];
};
onMounted(() => initInput());
watch(() => props.active, () => initInput());
// emits
const emit = defineEmits(['closed', 'assign']);
// computed: filter song list by search query
const filteredTags = computed(() => {
let tags = {};
if (searchInput.value != '') {
for (const key in props.tags) {
if (props.tags.hasOwnProperty(key)) {
const tag = props.tags[key];
let search = searchInput.value.toLowerCase();
// search in tag labels
let label = tag[loc] ? tag[loc] : key;
if (label.toLowerCase().indexOf(search) !== -1) {
tags[key] = tag;
}
}
}
return tags;
} else {
return props.tags;
}
});
// show selected tags sorted alphabeticaly
const sortedSelectedTags = computed(() => {
return selectedTags.value.sort();
});
</script>