aboutsummaryrefslogtreecommitdiff
path: root/viewer/src/views/layout/left/LayoutTagInput.vue
diff options
context:
space:
mode:
Diffstat (limited to 'viewer/src/views/layout/left/LayoutTagInput.vue')
-rw-r--r--viewer/src/views/layout/left/LayoutTagInput.vue93
1 files changed, 35 insertions, 58 deletions
diff --git a/viewer/src/views/layout/left/LayoutTagInput.vue b/viewer/src/views/layout/left/LayoutTagInput.vue
index 7ad3ed0..a37c546 100644
--- a/viewer/src/views/layout/left/LayoutTagInput.vue
+++ b/viewer/src/views/layout/left/LayoutTagInput.vue
@@ -27,42 +27,38 @@
27 @keypress.enter="inputEnter" 27 @keypress.enter="inputEnter"
28 @keydown.backspace="inputBackspace" 28 @keydown.backspace="inputBackspace"
29 /> 29 />
30 <div style="position:relative;"> 30 <LdDropdown
31 <Transition name="fade"> 31 ref="dropdown"
32 v-model="showDropdown"
33 :list="filteredTags"
34 list-key="tagfiltered"
35 :tabindex-root="51"
36 :class="$style.dropdown"
37 :style="dropdownStyle"
38 @select="addTag"
39 @opening="emit('opening')"
40 @closing="cleanSearch(); emit('closing');"
41 >
42 <template #option="{option}:{option:TagSearch}">
43 <div v-text="option.display" />
44 <div v-text="option.items.length" />
45 </template>
46 <template #empty>
32 <div 47 <div
33 v-if="openDropdown" 48 :class="$style.nomatch"
34 ref="dropdown" 49 v-text="t('tagInput.nomatch')"
35 class="scrollbar" 50 />
36 :class="$style.dropdown" 51 </template>
37 :style="dropdownStyle" 52 </LdDropdown>
38 >
39 <div
40 v-for="(tag,idx) in filteredTags"
41 :key="tag.tagfiltered"
42 :tabindex="51 + idx"
43 @click="addTag(tag)"
44 @keypress.enter.space="addTag(tag)"
45 >
46 <div v-text="tag.display" />
47 <div v-text="tag.items.length" />
48 </div>
49 <div
50 v-if="!filteredTags.length"
51 class="disaled"
52 :class="$style.nomatch"
53 v-text="t('tagInput.nomatch')"
54 />
55 </div>
56 </Transition>
57 </div>
58</template> 53</template>
59 54
60<script setup lang="ts"> 55<script setup lang="ts">
61import { TagSearch } from '@/@types/tag'; 56import { TagSearch } from '@/@types/tag';
57import LdDropdown from '@/components/LdDropdown.vue';
62import LdInput from '@/components/LdInput.vue'; 58import LdInput from '@/components/LdInput.vue';
63import { useIndexFactory } from '@/services/indexFactory'; 59import { useIndexFactory } from '@/services/indexFactory';
64import { useGalleryStore } from '@/store/galleryStore'; 60import { useGalleryStore } from '@/store/galleryStore';
65import { computedEager, onClickOutside, onKeyStroke, useElementBounding, useFocus, useVModel } from '@vueuse/core'; 61import { computedEager, useElementBounding, useFocus, useVModel } from '@vueuse/core';
66import { computed, ref, StyleValue, watchEffect } from 'vue'; 62import { computed, ref, StyleValue, watchEffect } from 'vue';
67import { useI18n } from 'vue-i18n'; 63import { useI18n } from 'vue-i18n';
68 64
@@ -77,19 +73,15 @@ const galeryStore = useGalleryStore();
77const indexFactory = useIndexFactory(); 73const indexFactory = useIndexFactory();
78 74
79const search = ref(''); 75const search = ref('');
80const openDropdown = computedEager<boolean>(() => !!search.value); 76const showDropdown = ref(false);
81watchEffect(() => { 77
82 if (openDropdown.value) emit('opening'); 78watchEffect(() => (showDropdown.value = !!search.value));
83 else emit('closing');
84});
85 79
86// --- 80// ---
87 81
88const dropdown = ref(); 82const dropdown = ref();
89const { top } = useElementBounding(dropdown); 83const { top } = useElementBounding(dropdown);
90const dropdownStyle = computedEager<StyleValue>(() => ({ height: `calc(100vh - 8px - ${top.value}px)` })); 84const dropdownStyle = computedEager<StyleValue>(() => ({ height: `calc(100vh - 8px - ${top.value}px)` }));
91onClickOutside(dropdown, closeDropdown);
92onKeyStroke('Escape', closeDropdown);
93 85
94const input = ref(); 86const input = ref();
95const { focused } = useFocus(input); 87const { focused } = useFocus(input);
@@ -111,16 +103,16 @@ function addTag(tag?: TagSearch) {
111 const toPush = tag ?? filteredTags.value[0]; 103 const toPush = tag ?? filteredTags.value[0];
112 if (!toPush) return; 104 if (!toPush) return;
113 model.value.push(toPush); 105 model.value.push(toPush);
114 closeDropdown(); 106 cleanSearch();
115} 107}
116function inputEnter() { 108function inputEnter() {
117 if (search.value) addTag(); 109 if (search.value) addTag();
118 else emit('search'); 110 else emit('search');
119} 111}
120function inputBackspace() { 112function inputBackspace() {
121 !openDropdown.value && model.value.pop(); 113 !showDropdown.value && model.value.pop();
122} 114}
123function closeDropdown() { 115function cleanSearch() {
124 search.value = ''; 116 search.value = '';
125 focused.value = true; 117 focused.value = true;
126} 118}
@@ -130,35 +122,20 @@ function closeDropdown() {
130@import "~@/assets/scss/theme"; 122@import "~@/assets/scss/theme";
131 123
132.dropdown { 124.dropdown {
133 position: absolute;
134 left: 0;
135 z-index: 10;
136 width: $layout-left;
137 color: $input-color;
138 background-color: $dropdown-item-color;
139 padding: 4px 0px;
140 > div { 125 > div {
141 display: flex; 126 display: flex;
142 justify-content: space-between; 127 justify-content: space-between;
143 padding: 4px 0;
144 margin: 2px; // For the focus border
145 cursor: pointer;
146 > div { 128 > div {
147 padding: 0 4px; 129 padding: 0 4px;
148 } 130 }
149 > div:last-child { 131 > div:last-child {
150 color: $text-light; 132 color: $text-light;
151 } 133 }
152 &:hover { 134 }
153 background-color: $dropdown-item-hover-color; 135 .nomatch {
154 } 136 color: $disabled-color;
155 &:focus { 137 justify-content: center;
156 outline: solid 1px $button-active-color; 138 cursor: default;
157 }
158 &.nomatch {
159 color: $text-light;
160 justify-content: center;
161 }
162 } 139 }
163} 140}
164</style> 141</style>