aboutsummaryrefslogtreecommitdiff
path: root/viewer/src/components/LdProposition.vue
diff options
context:
space:
mode:
authorZéro~Informatique2022-07-26 08:44:34 +0200
committerpacien2022-09-03 01:30:42 +0200
commit00510820a2794efcadbc83f7f8b54318fe198ecb (patch)
treea894d99c22a601197869c7a6928d40bb4ae2c392 /viewer/src/components/LdProposition.vue
parent88aa098c07e067f9f737fbeba1f52a9bd5042e53 (diff)
downloadldgallery-00510820a2794efcadbc83f7f8b54318fe198ecb.tar.gz
viewer: migrate to vue 3, general refactoring and cleanup
Non-exhaustive list of fixes and improvements done at the same time: - html default background to grey (avoids white flash during init) - unified links behavior - added more theme variables - removed the flex-expand transition (it wasn't working) and replaced it with a slide - fixed LdLoading not centered on the content - title on removable tags - fixed an issue with encoded URI from vue-router - unified Item resource URLs - removed the iframe for PlainTextViewer (it wasn't working properly) and replaced it with a pre - fixed clear and search buttons tabindex - fixed the information panel bumping up during the fade animation of tag's dropdown - fixed some focus outlines not appearing correctly - moved CSS variables to the :root context - Code cleaning GitHub: closes #217 GitHub: closes #300 GitHub: closes #297 GitHub: closes #105 GitHub: closes #267 GitHub: closes #275 GitHub: closes #228 GitHub: closes #215 GitHub: closes #112
Diffstat (limited to 'viewer/src/components/LdProposition.vue')
-rw-r--r--viewer/src/components/LdProposition.vue186
1 files changed, 0 insertions, 186 deletions
diff --git a/viewer/src/components/LdProposition.vue b/viewer/src/components/LdProposition.vue
deleted file mode 100644
index 07b27f5..0000000
--- a/viewer/src/components/LdProposition.vue
+++ /dev/null
@@ -1,186 +0,0 @@
1<!-- ldgallery - A static generator which turns a collection of tagged
2-- pictures into a searchable web gallery.
3--
4-- Copyright (C) 2019-2020 Guillaume FOUET
5-- 2020 Pacien TRAN-GIRARD
6--
7-- This program is free software: you can redistribute it and/or modify
8-- it under the terms of the GNU Affero General Public License as
9-- published by the Free Software Foundation, either version 3 of the
10-- License, or (at your option) any later version.
11--
12-- This program is distributed in the hope that it will be useful,
13-- but WITHOUT ANY WARRANTY; without even the implied warranty of
14-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15-- GNU Affero General Public License for more details.
16--
17-- You should have received a copy of the GNU Affero General Public License
18-- along with this program. If not, see <https://www.gnu.org/licenses/>.
19-->
20
21<template>
22 <div :class="$style.proposition">
23 <h2 v-if="showCategory && Object.keys(propositions).length" :class="[$style.subtitle, $style.category]">
24 {{ title }}
25 </h2>
26 <div v-for="proposed in proposedTags" :key="proposed.rawTag">
27 <a
28 class="link"
29 :class="$style.operationBtns"
30 :title="$t('tag-propositions.substraction')"
31 @click="add(Operation.SUBSTRACTION, proposed.rawTag)"
32 >
33 <fa-icon icon="minus" alt="[-]" />
34 </a>
35
36 <a
37 class="link"
38 :class="$style.operationBtns"
39 :title="$t('tag-propositions.addition')"
40 @click="add(Operation.ADDITION, proposed.rawTag)"
41 >
42 <fa-icon icon="plus" alt="[+]" />
43 </a>
44
45 <a
46 class="link"
47 :class="$style.operationTag"
48 :title="$t('tag-propositions.intersection')"
49 @click="add(Operation.INTERSECTION, proposed.rawTag)"
50 >{{ proposed.rawTag }}</a
51 >
52
53 <div class="disabled" :title="$t('tag-propositions.item-count')">{{ proposed.count }}</div>
54 </div>
55 <div v-if="showMoreCount > 0" :class="$style.showmore" @click="limit += showMoreCount">
56 {{ $t("tag-propositions.showmore", [showMoreCount]) }}<fa-icon icon="angle-double-down" />
57 </div>
58 </div>
59</template>
60
61<script lang="ts">
62import { Item, RawTag } from "@/@types/gallery";
63import { Operation } from "@/@types/Operation";
64import { TagIndex, TagNode, TagSearch } from "@/@types/tag";
65import { Component, Prop, PropSync, Vue, Watch } from "vue-property-decorator";
66
67@Component
68export default class LdProposition extends Vue {
69 @Prop() readonly category?: TagNode;
70 @Prop({ type: Boolean, required: true }) readonly showCategory!: boolean;
71 @Prop({ type: Array, required: true }) readonly currentTags!: string[];
72 @Prop({ required: true }) readonly tagsIndex!: TagIndex;
73 @PropSync("searchFilters", { type: Array, required: true }) model!: TagSearch[];
74
75 readonly INITIAL_TAG_DISPLAY_LIMIT = this.getInitialTagDisplayLimit();
76
77 limit: number = this.INITIAL_TAG_DISPLAY_LIMIT;
78
79 getInitialTagDisplayLimit() {
80 const limit = this.$galleryStore.config?.initialTagDisplayLimit ?? 10;
81 return limit >= 0 ? limit : 1000;
82 }
83
84 @Watch("$route")
85 onRouteChange() {
86 this.limit = this.INITIAL_TAG_DISPLAY_LIMIT;
87 }
88
89 get Operation() {
90 return Operation;
91 }
92
93 get propositions(): Record<string, number> {
94 const propositions: Record<string, number> = {};
95 if (this.model.length > 0) {
96 // Tags count from current search
97 this.extractDistinctItems(this.model)
98 .flatMap(item => item.tags)
99 .map(this.rightmost)
100 .filter(rawTag => this.tagsIndex[rawTag] && !this.model.find(search => search.tag === rawTag))
101 .forEach(rawTag => (propositions[rawTag] = (propositions[rawTag] ?? 0) + 1));
102 } else {
103 // Tags count from the current directory
104 this.currentTags
105 .flatMap(tag => tag.split(":"))
106 .map(tag => this.tagsIndex[tag])
107 .filter(Boolean)
108 .forEach(tagindex => (propositions[tagindex.tag] = tagindex.items.length));
109 }
110 return propositions;
111 }
112
113 get proposedTags() {
114 return Object.entries(this.propositions)
115 .sort((a, b) => b[1] - a[1])
116 .slice(0, this.limit)
117 .map(entry => ({ rawTag: entry[0], count: entry[1] }));
118 }
119
120 get showMoreCount(): number {
121 return Object.keys(this.propositions).length - Object.keys(this.proposedTags).length;
122 }
123
124 get title() {
125 return this.category?.tag ?? this.$t("panelLeft.propositions.other");
126 }
127
128 extractDistinctItems(currentTags: TagSearch[]): Item[] {
129 return [...new Set(currentTags.flatMap(tag => tag.items))];
130 }
131
132 rightmost(tag: RawTag): RawTag {
133 const dot = tag.lastIndexOf(":");
134 return dot <= 0 ? tag : tag.substr(dot + 1);
135 }
136
137 add(operation: Operation, rawTag: RawTag) {
138 const node = this.tagsIndex[rawTag];
139 const display = this.category ? `${operation}${this.category.tag}:${node.tag}` : `${operation}${node.tag}`;
140 this.model.push({ ...node, parent: this.category, operation, display });
141 }
142}
143</script>
144
145<style lang="scss" module>
146@import "~@/assets/scss/theme.scss";
147
148.proposition {
149 .subtitle {
150 background-color: $proposed-category-bgcolor;
151 width: 100%;
152 padding: 0 0 6px 0;
153 margin: 0;
154 text-align: center;
155 font-variant: small-caps;
156 }
157 > div {
158 display: flex;
159 align-items: center;
160 padding-right: 7px;
161 .operationTag {
162 text-overflow: ellipsis;
163 white-space: nowrap;
164 overflow: hidden;
165 flex-grow: 1;
166 cursor: pointer;
167 }
168 .operationBtns {
169 padding: 2px 7px;
170 cursor: pointer;
171 }
172 }
173 .showmore {
174 display: block;
175 text-align: right;
176 color: $palette-300;
177 cursor: pointer;
178 > svg {
179 margin-left: 10px;
180 }
181 &:hover {
182 color: $link-hover;
183 }
184 }
185}
186</style>