diff options
Diffstat (limited to 'viewer/src/views/layout/left/LayoutLeft.vue')
-rw-r--r-- | viewer/src/views/layout/left/LayoutLeft.vue | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/viewer/src/views/layout/left/LayoutLeft.vue b/viewer/src/views/layout/left/LayoutLeft.vue new file mode 100644 index 0000000..bb3e747 --- /dev/null +++ b/viewer/src/views/layout/left/LayoutLeft.vue | |||
@@ -0,0 +1,158 @@ | |||
1 | <!-- ldgallery - A static generator which turns a collection of tagged | ||
2 | -- pictures into a searchable web gallery. | ||
3 | -- | ||
4 | -- Copyright (C) 2019-2022 Guillaume FOUET | ||
5 | -- | ||
6 | -- This program is free software: you can redistribute it and/or modify | ||
7 | -- it under the terms of the GNU Affero General Public License as | ||
8 | -- published by the Free Software Foundation, either version 3 of the | ||
9 | -- License, or (at your option) any later version. | ||
10 | -- | ||
11 | -- This program is distributed in the hope that it will be useful, | ||
12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | -- GNU Affero General Public License for more details. | ||
15 | -- | ||
16 | -- You should have received a copy of the GNU Affero General Public License | ||
17 | -- along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
18 | --> | ||
19 | |||
20 | <template> | ||
21 | <div | ||
22 | class="flex-column" | ||
23 | :class="$style.sidebar" | ||
24 | > | ||
25 | <LayoutTagList v-model="searchFilters" /> | ||
26 | <LayoutTagInput | ||
27 | v-model="searchFilters" | ||
28 | @search="search" | ||
29 | @opening="isOpenSearch=true" | ||
30 | @closing="isOpenSearch=false" | ||
31 | /> | ||
32 | <!-- We disable the components beneath the Dropdown for accessibility purposes --> | ||
33 | <LayoutCommandSearch | ||
34 | v-show="!isOpenSearch" | ||
35 | @clear="clear" | ||
36 | @search="search" | ||
37 | /> | ||
38 | <h1 | ||
39 | v-show="!isOpenSearch" | ||
40 | :class="$style.title" | ||
41 | > | ||
42 | {{ t("panelLeft.propositions.related") }} | ||
43 | </h1> | ||
44 | <div | ||
45 | v-show="!isOpenSearch" | ||
46 | class="scrollbar no-scroll-x" | ||
47 | :class="$style.flexShrinkFully" | ||
48 | > | ||
49 | <LayoutProposition | ||
50 | v-for="category in galleryStore.tagsCategories" | ||
51 | :key="category.tag" | ||
52 | v-model:search-filters="searchFilters" | ||
53 | :category="galleryStore.tagsIndex[category.tag]" | ||
54 | :show-category="galleryStore.tagsCategories.length > 1" | ||
55 | :tags-index="category.index" | ||
56 | :current-tags="currentTags" | ||
57 | /> | ||
58 | </div> | ||
59 | |||
60 | <template v-if="galleryStore.currentItem"> | ||
61 | <div class="flex-grow-1" /> | ||
62 | <h1 | ||
63 | :class="[$style.infoPanelTitleBar, $style.title]" | ||
64 | class="flex" | ||
65 | @click="infoOpen = !infoOpen" | ||
66 | > | ||
67 | {{ t("panelLeft.information.title") }} | ||
68 | <fa-icon :icon="infoOpen ? faCaretDown : faCaretUp" /> | ||
69 | </h1> | ||
70 | <transition name="slide"> | ||
71 | <LayoutInformation | ||
72 | v-show="infoOpen" | ||
73 | :item="galleryStore.currentItem" | ||
74 | class="scrollbar no-scroll-x" | ||
75 | /> | ||
76 | </transition> | ||
77 | </template> | ||
78 | </div> | ||
79 | </template> | ||
80 | |||
81 | <script setup lang="ts"> | ||
82 | import { TagSearch } from '@/@types/tag'; | ||
83 | import { useNavigation } from '@/services/navigation'; | ||
84 | import { useGalleryStore } from '@/store/galleryStore'; | ||
85 | import { faCaretDown, faCaretUp } from '@fortawesome/free-solid-svg-icons'; | ||
86 | import { computed, ref, watch } from 'vue'; | ||
87 | import { useI18n } from 'vue-i18n'; | ||
88 | import { useRoute, useRouter } from 'vue-router'; | ||
89 | import LayoutCommandSearch from './LayoutCommandSearch.vue'; | ||
90 | import LayoutInformation from './LayoutInformation.vue'; | ||
91 | import LayoutProposition from './LayoutProposition.vue'; | ||
92 | import LayoutTagInput from './LayoutTagInput.vue'; | ||
93 | import LayoutTagList from './LayoutTagList.vue'; | ||
94 | |||
95 | const { t } = useI18n(); | ||
96 | const route = useRoute(); | ||
97 | const router = useRouter(); | ||
98 | const navigation = useNavigation(); | ||
99 | const galleryStore = useGalleryStore(); | ||
100 | |||
101 | const searchFilters = ref<TagSearch[]>([]); | ||
102 | const infoOpen = ref(true); | ||
103 | const isOpenSearch = ref(false); | ||
104 | |||
105 | function clear() { | ||
106 | searchFilters.value = []; | ||
107 | search(); | ||
108 | } | ||
109 | |||
110 | function search() { | ||
111 | const lastDirectory = navigation.getLastDirectory(galleryStore.currentItemPath); | ||
112 | router.push({ path: lastDirectory.path, query: serializeSearch() }).catch(err => { | ||
113 | if (err.name !== 'NavigationDuplicated') throw err; | ||
114 | }); | ||
115 | } | ||
116 | |||
117 | function serializeSearch() { | ||
118 | const query: Record<string, null> = {}; | ||
119 | searchFilters.value.forEach(filter => (query[filter.display] = null)); | ||
120 | return query; | ||
121 | } | ||
122 | |||
123 | const currentTags = computed(() => galleryStore.currentItem?.tags ?? []); | ||
124 | |||
125 | watch(() => route.query, (query) => { | ||
126 | const filters = Object.keys(query); | ||
127 | if (filters.length > 0) galleryStore.search(filters).then(search => (searchFilters.value = [...search])); | ||
128 | }, { immediate: true }); | ||
129 | </script> | ||
130 | |||
131 | <style lang="scss" module> | ||
132 | @import "~@/assets/scss/theme"; | ||
133 | |||
134 | .sidebar { | ||
135 | .title { | ||
136 | background-color: $proposed-category-bgcolor; | ||
137 | color: $title-color; | ||
138 | padding: 0.2em 0.5em; | ||
139 | margin: 0 0 1px 0; | ||
140 | font-variant: small-caps; | ||
141 | justify-content: space-between; | ||
142 | user-select: none; | ||
143 | |||
144 | >svg { | ||
145 | color: $link; | ||
146 | margin-top: 2px; // Fixes a vertical centering issue with the carret | ||
147 | } | ||
148 | } | ||
149 | |||
150 | .infoPanelTitleBar { | ||
151 | cursor: pointer; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | .flexShrinkFully { | ||
156 | flex-shrink: 1000; | ||
157 | } | ||
158 | </style> | ||