diff options
Diffstat (limited to 'viewer/src/components/LdProposition.vue')
-rw-r--r-- | viewer/src/components/LdProposition.vue | 141 |
1 files changed, 0 insertions, 141 deletions
diff --git a/viewer/src/components/LdProposition.vue b/viewer/src/components/LdProposition.vue deleted file mode 100644 index fe3af07..0000000 --- a/viewer/src/components/LdProposition.vue +++ /dev/null | |||
@@ -1,141 +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 && proposedTags.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 | <div class="disabled" :title="$t('tag-propositions.item-count')">{{proposed.count}}</div> | ||
48 | </div> | ||
49 | </div> | ||
50 | </template> | ||
51 | |||
52 | <script lang="ts"> | ||
53 | import { Component, Vue, Prop, PropSync } from "vue-property-decorator"; | ||
54 | import { Operation } from "@/@types/Operation"; | ||
55 | |||
56 | @Component | ||
57 | export default class LdProposition extends Vue { | ||
58 | @Prop() readonly category?: Tag.Node; | ||
59 | @Prop({ type: Boolean, required: true }) readonly showCategory!: boolean; | ||
60 | @Prop({ type: Array, required: true }) readonly currentTags!: string[]; | ||
61 | @Prop({ required: true }) readonly tagsIndex!: Tag.Index; | ||
62 | @PropSync("searchFilters", { type: Array, required: true }) model!: Tag.Search[]; | ||
63 | |||
64 | get Operation() { | ||
65 | return Operation; | ||
66 | } | ||
67 | |||
68 | get proposedTags() { | ||
69 | let propositions: { [index: string]: number } = {}; | ||
70 | if (this.model.length > 0) { | ||
71 | // Tags count from current search | ||
72 | this.extractDistinctItems(this.model) | ||
73 | .flatMap(item => item.tags) | ||
74 | .map(this.rightmost) | ||
75 | .filter(rawTag => this.tagsIndex[rawTag] && !this.model.find(search => search.tag === rawTag)) | ||
76 | .forEach(rawTag => (propositions[rawTag] = (propositions[rawTag] ?? 0) + 1)); | ||
77 | } else { | ||
78 | // Tags count from the current directory | ||
79 | this.currentTags | ||
80 | .flatMap(tag => tag.split(":")) | ||
81 | .map(tag => this.tagsIndex[tag]) | ||
82 | .filter(Boolean) | ||
83 | .forEach(tagindex => (propositions[tagindex.tag] = tagindex.items.length)); | ||
84 | } | ||
85 | |||
86 | return Object.entries(propositions) | ||
87 | .sort((a, b) => b[1] - a[1]) | ||
88 | .map(entry => ({ rawTag: entry[0], count: entry[1] })); | ||
89 | } | ||
90 | |||
91 | get title() { | ||
92 | return this.category?.tag ?? this.$t("panelLeft.propositions.other"); | ||
93 | } | ||
94 | |||
95 | extractDistinctItems(currentTags: Tag.Search[]): Gallery.Item[] { | ||
96 | return [...new Set(currentTags.flatMap(tag => tag.items))]; | ||
97 | } | ||
98 | |||
99 | rightmost(tag: Gallery.RawTag): Gallery.RawTag { | ||
100 | const dot = tag.lastIndexOf(":"); | ||
101 | return dot <= 0 ? tag : tag.substr(dot + 1); | ||
102 | } | ||
103 | |||
104 | add(operation: Operation, rawTag: Gallery.RawTag) { | ||
105 | const node = this.tagsIndex[rawTag]; | ||
106 | const display = this.category ? `${operation}${this.category.tag}:${node.tag}` : `${operation}${node.tag}`; | ||
107 | this.model.push({ ...node, parent: this.category, operation, display }); | ||
108 | } | ||
109 | } | ||
110 | </script> | ||
111 | |||
112 | <style lang="scss"> | ||
113 | @import "~@/assets/scss/theme.scss"; | ||
114 | |||
115 | .proposition { | ||
116 | .subtitle { | ||
117 | background-color: $proposed-category-bgcolor; | ||
118 | width: 100%; | ||
119 | padding: 0 0 6px 0; | ||
120 | margin: 0; | ||
121 | text-align: center; | ||
122 | font-variant: small-caps; | ||
123 | } | ||
124 | > div { | ||
125 | display: flex; | ||
126 | align-items: center; | ||
127 | padding-right: 7px; | ||
128 | .operation-tag { | ||
129 | text-overflow: ellipsis; | ||
130 | white-space: nowrap; | ||
131 | overflow: hidden; | ||
132 | flex-grow: 1; | ||
133 | cursor: pointer; | ||
134 | } | ||
135 | .operation-btns { | ||
136 | padding: 2px 7px; | ||
137 | cursor: pointer; | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | </style> | ||