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.vue93
1 files changed, 93 insertions, 0 deletions
diff --git a/viewer/src/components/LdProposition.vue b/viewer/src/components/LdProposition.vue
new file mode 100644
index 0000000..9a32e0a
--- /dev/null
+++ b/viewer/src/components/LdProposition.vue
@@ -0,0 +1,93 @@
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--
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 <div v-for="proposed in proposedTags" :key="proposed.rawTag" class="proposition">
23 <fa-icon icon="minus" @click="add(Operation.SUBSTRACTION, proposed.rawTag)" />
24 <span
25 @click="add(Operation.INTERSECTION, proposed.rawTag)"
26 >{{proposed.rawTag}}&nbsp;x{{proposed.count}}</span>
27 <fa-icon icon="plus" @click="add(Operation.ADDITION, proposed.rawTag)" />
28 </div>
29 </div>
30</template>
31
32<script lang="ts">
33import { Component, Vue } from "vue-property-decorator";
34import { Operation } from "@/@types/tag/Operation";
35
36@Component
37export default class LdTagInput extends Vue {
38 get Operation() {
39 return Operation;
40 }
41
42 get proposedTags() {
43 const currentTags = this.$uiStore.currentTags;
44 let propositions: { [index: string]: number } = {};
45 if (currentTags.length > 0) {
46 // Tags count from current search
47 this.extractDistinctItems(currentTags)
48 .flatMap(item => item.tags)
49 .map(this.rightmost)
50 .filter(rawTag => !currentTags.find(currentTag => currentTag.tag === rawTag))
51 .forEach(rawTag => (propositions[rawTag] = (propositions[rawTag] ?? 0) + 1));
52 } else {
53 // Tags count from the whole gallery
54 Object.entries(this.$galleryStore.tags)
55 .forEach(entry => (propositions[entry[0]] = entry[1].items.length));
56 }
57
58 return Object.entries(propositions)
59 .sort((a,b) => b[1] - a[1])
60 .map(entry => ({rawTag: entry[0], count: entry[1]}));
61 }
62
63 extractDistinctItems(currentTags: Tag.Search[]): Gallery.Item[] {
64 return [...new Set(currentTags.flatMap(tag => tag.items))];
65 }
66
67 rightmost(tag: Gallery.RawTag): Gallery.RawTag {
68 const dot = tag.lastIndexOf(".");
69 return dot <= 0 ? tag : tag.substr(dot + 1);
70 }
71
72 add(operation: Operation, rawTag: Gallery.RawTag) {
73 const node = this.$galleryStore.tags[rawTag];
74 const search: Tag.Search = { ...node, operation, display: `${operation}${node.tag}` };
75 this.$uiStore.currentTags.push(search);
76 this.$uiStore.mode = "search";
77 }
78}
79</script>
80
81<style lang="scss">
82.proposition {
83 display: flex;
84 justify-content: space-between;
85 align-items: center;
86 margin: 10px;
87 color: lightcyan;
88 cursor: pointer;
89}
90.proposition span {
91 padding: 0 10px;
92}
93</style>