aboutsummaryrefslogtreecommitdiff
path: root/viewer/src/views/layout
diff options
context:
space:
mode:
authorZéro~Informatique2022-09-03 04:41:03 +0200
committerZéro~Informatique2022-09-03 04:41:03 +0200
commite704198437fb589ec8c954d12700d2a1f911522c (patch)
tree2413a96880367c55e7f736e902bf2944d90ef0ec /viewer/src/views/layout
parenta8452594c6571e8003baa2aca14747eeeee08152 (diff)
downloadldgallery-e704198437fb589ec8c954d12700d2a1f911522c.tar.gz
viewer: refactoring for tag and sort dropdowns
Diffstat (limited to 'viewer/src/views/layout')
-rw-r--r--viewer/src/views/layout/left/LayoutTagInput.vue93
-rw-r--r--viewer/src/views/layout/top/LayoutCommandSort.vue60
2 files changed, 49 insertions, 104 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>
diff --git a/viewer/src/views/layout/top/LayoutCommandSort.vue b/viewer/src/views/layout/top/LayoutCommandSort.vue
index bb9744e..8336621 100644
--- a/viewer/src/views/layout/top/LayoutCommandSort.vue
+++ b/viewer/src/views/layout/top/LayoutCommandSort.vue
@@ -22,41 +22,35 @@
22 <LdLink 22 <LdLink
23 :title="t('command.sort.title')" 23 :title="t('command.sort.title')"
24 :tabindex="props.tabindex" 24 :tabindex="props.tabindex"
25 @click="openDropdown" 25 @click="showDropdown=!showDropdown"
26 > 26 >
27 <fa-icon 27 <fa-icon
28 :icon="faSortAmountDown" 28 :icon="faSortAmountDown"
29 size="lg" 29 size="lg"
30 /> 30 />
31 <teleport to="body"> 31 <teleport to="body">
32 <Transition name="fade"> 32 <LdDropdown
33 <div 33 v-model="showDropdown"
34 v-if="showDropdown" 34 :list="itemComparator.ITEM_SORTS"
35 ref="dropdown" 35 :tabindex-root="props.tabindex + 1"
36 :class="$style.dropdown" 36 :class="$style.dropdown"
37 > 37 @select="(sort: ItemSort) => selectedSort=sort"
38 <div 38 >
39 v-for="(sort,idx) in itemComparator.ITEM_SORTS" 39 <template #option="{option}:{option:ItemSort}">
40 :key="sort.name" 40 <fa-icon :icon="option.name == selectedSort.name ? faDotCircle : faCircle" />
41 :tabindex="props.tabindex + idx + 1" 41 <span v-text="option.text" />
42 @click="selectedSort=sort" 42 </template>
43 @keypress.enter.space="selectedSort=sort" 43 </LdDropdown>
44 >
45 <fa-icon :icon="sort.name == selectedSort.name ? faDotCircle : faCircle" />
46 <span v-text="sort.text" />
47 </div>
48 </div>
49 </Transition>
50 </teleport> 44 </teleport>
51 </LdLink>