aboutsummaryrefslogtreecommitdiff
path: root/viewer/src/components/LdProposition.vue
diff options
context:
space:
mode:
Diffstat (limited to 'viewer/src/components/LdProposition.vue')
-rw-r--r--viewer/src/components/LdProposition.vue179
1 files changed, 0 insertions, 179 deletions
diff --git a/viewer/src/components/LdProposition.vue b/viewer/src/components/LdProposition.vue
deleted file mode 100644
index aa44943..0000000
--- a/viewer/src/components/LdProposition.vue
+++ /dev/null
@@ -1,179 +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="proposition">
23 <h2 v-if="showCategory && Object.keys(propositions).length" class="subtitle category">{{ title }}</h2>
24 <div v-for="proposed in proposedTags" :key="proposed.rawTag">
25 <a
26 class="operation-btns link"
27 :title="$t('tag-propositions.substraction')"
28 @click="add(Operation.SUBSTRACTION, proposed.rawTag)"
29 >
30 <fa-icon icon="minus" alt="[-]" />
31 </a>
32
33 <a
34 class="operation-btns link"
35 :title="$t('tag-propositions.addition')"
36 @click="add(Operation.ADDITION, proposed.rawTag)"
37 >
38 <fa-icon icon="plus" alt="[+]" />
39 </a>
40
41 <a
42 class="operation-tag link"
43 :title="$t('tag-propositions.intersection')"
44 @click="add(Operation.INTERSECTION, proposed.rawTag)"
45 >{{ proposed.rawTag }}</a
46 >
47
48 <div class="disabled" :title="$t('tag-propositions.item-count')">{{ proposed.count }}</div>
49 </div>
50 <div v-if="showMoreCount > 0" class="showmore" @click="limit += showMoreCount">
51 {{ $t("tag-propositions.showmore", [showMoreCount]) }}<fa-icon icon="angle-double-down" />
52 </div>
53 </div>
54</template>
55
56<script lang="ts">
57import { Component, Vue, Prop, PropSync, Watch } from "vue-property-decorator";
58import { Operation } from "@/@types/Operation";
59
60@Component
61export default class LdProposition extends Vue {
62 @Prop() readonly category?: Tag.Node;
63 @Prop({ type: Boolean, required: true }) readonly showCategory!: boolean;
64 @Prop({ type: Array, required: true }) readonly currentTags!: string[];
65 @Prop({ required: true }) readonly tagsIndex!: Tag.Index;
66 @PropSync("searchFilters", { type: Array, required: true }) model!: Tag.Search[];
67
68 readonly INITIAL_TAG_DISPLAY_LIMIT = this.getInitialTagDisplayLimit();
69
70 limit: number = this.INITIAL_TAG_DISPLAY_LIMIT;
71
72 getInitialTagDisplayLimit() {
73 const limit = this.$galleryStore.config?.initialTagDisplayLimit ?? 10;
74 return limit >= 0 ? limit : 1000;
75 }
76
77 @Watch("$route")
78 onRouteChange() {
79 this.limit = this.INITIAL_TAG_DISPLAY_LIMIT;
80 }
81
82 get Operation() {
83 return Operation;
84 }
85
86 get propositions(): Record<string, number> {
87 const propositions: Record<string, number> = {};
88 if (this.model.length > 0) {
89 // Tags count from current search
90 this.extractDistinctItems(this.model)
91 .flatMap(item => item.tags)
92 .map(this.rightmost)
93 .filter(rawTag => this.tagsIndex[rawTag] && !this.model.find(search => search.tag === rawTag))
94 .forEach(rawTag => (propositions[rawTag] = (propositions[rawTag] ?? 0) + 1));
95 } else {
96 // Tags count from the current directory
97 this.currentTags
98 .flatMap(tag => tag.split(":"))
99 .map(tag => this.tagsIndex[tag])
100 .filter(Boolean)
101 .forEach(tagindex => (propositions[tagindex.tag] = tagindex.items.length));
102 }
103 return propositions;
104 }
105
106 get proposedTags() {
107 return Object.entries(this.propositions)
108 .sort((a, b) => b[1] - a[1])
109 .slice(0, this.limit)
110 .map(entry => ({ rawTag: entry[0], count: entry[1] }));
111 }
112
113 get showMoreCount(): number {
114 return Object.keys(this.propositions).length - Object.keys(this.proposedTags).length;
115 }
116
117 get title() {
118 return this.category?.tag ?? this.$t("panelLeft.propositions.other");
119 }
120
121 extractDistinctItems(currentTags: Tag.Search[]): Gallery.Item[] {
122 return [...new Set(currentTags.flatMap(tag => tag.items))];
123 }
124
125 rightmost(tag: Gallery.RawTag): Gallery.RawTag {
126 const dot = tag.lastIndexOf(":");
127 return dot <= 0 ? tag : tag.substr(dot + 1);
128 }
129
130 add(operation: Operation, rawTag: Gallery.RawTag) {
131 const node = this.tagsIndex[rawTag];
132 const display = this.category ? `${operation}${this.category.tag}:${node.tag}` : `${operation}${node.tag}`;
133 this.model.push({ ...node, parent: this.category, operation, display });
134 }
135}
136</script>
137
138<style lang="scss">
139@import "~@/assets/scss/theme.scss";
140
141.proposition {
142 .subtitle {
143 background-color: $proposed-category-bgcolor;
144 width: 100%;
145 padding: 0 0 6px 0;
146 margin: 0;
147 text-align: center;
148 font-variant: small-caps;
149 }
150 > div {
151 display: flex;
152 align-items: center;
153 padding-right: 7px;
154 .operation-tag {
155 text-overflow: ellipsis;
156 white-space: nowrap;
157 overflow: hidden;
158 flex-grow: 1;
159 cursor: pointer;
160 }
161 .operation-btns {
162 padding: 2px 7px;
163 cursor: pointer;
164 }
165 }
166 .showmore {
167 display: block;
168 text-align: right;
169 color: $palette-300;
170 cursor: pointer;
171 > svg {
172 margin-left: 10px;
173 }
174 &:hover {
175 color: $link-hover;
176 }
177 }
178}
179</style>