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.vue114
1 files changed, 81 insertions, 33 deletions
diff --git a/viewer/src/components/LdProposition.vue b/viewer/src/components/LdProposition.vue
index 9a32e0a..fe3af07 100644
--- a/viewer/src/components/LdProposition.vue
+++ b/viewer/src/components/LdProposition.vue
@@ -2,6 +2,7 @@
2-- pictures into a searchable web gallery. 2-- pictures into a searchable web gallery.
3-- 3--
4-- Copyright (C) 2019-2020 Guillaume FOUET 4-- Copyright (C) 2019-2020 Guillaume FOUET
5-- 2020 Pacien TRAN-GIRARD
5-- 6--
6-- This program is free software: you can redistribute it and/or modify 7-- 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-- it under the terms of the GNU Affero General Public License as
@@ -18,46 +19,77 @@
18--> 19-->
19 20
20<template> 21<template>
21 <div> 22 <div class="proposition">
22 <div v-for="proposed in proposedTags" :key="proposed.rawTag" class="proposition"> 23 <h2 v-if="showCategory && proposedTags.length" class="subtitle category">{{title}}</h2>
23 <fa-icon icon="minus" @click="add(Operation.SUBSTRACTION, proposed.rawTag)" /> 24 <div v-for="proposed in proposedTags" :key="proposed.rawTag">
24 <span 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')"
25 @click="add(Operation.INTERSECTION, proposed.rawTag)" 44 @click="add(Operation.INTERSECTION, proposed.rawTag)"
26 >{{proposed.rawTag}}&nbsp;x{{proposed.count}}</span> 45 >{{proposed.rawTag}}</a>
27 <fa-icon icon="plus" @click="add(Operation.ADDITION, proposed.rawTag)" /> 46
47 <div class="disabled" :title="$t('tag-propositions.item-count')">{{proposed.count}}</div>
28 </div> 48 </div>
29 </div> 49 </div>
30</template> 50</template>
31 51
32<script lang="ts"> 52<script lang="ts">
33import { Component, Vue } from "vue-property-decorator"; 53import { Component, Vue, Prop, PropSync } from "vue-property-decorator";
34import { Operation } from "@/@types/tag/Operation"; 54import { Operation } from "@/@types/Operation";
35 55
36@Component 56@Component
37export default class LdTagInput extends Vue { 57export 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
38 get Operation() { 64 get Operation() {
39 return Operation; 65 return Operation;
40 } 66 }
41 67
42 get proposedTags() { 68 get proposedTags() {
43 const currentTags = this.$uiStore.currentTags;
44 let propositions: { [index: string]: number } = {}; 69 let propositions: { [index: string]: number } = {};
45 if (currentTags.length > 0) { 70 if (this.model.length > 0) {
46 // Tags count from current search 71 // Tags count from current search
47 this.extractDistinctItems(currentTags) 72 this.extractDistinctItems(this.model)
48 .flatMap(item => item.tags) 73 .flatMap(item => item.tags)
49 .map(this.rightmost) 74 .map(this.rightmost)
50 .filter(rawTag => !currentTags.find(currentTag => currentTag.tag === rawTag)) 75 .filter(rawTag => this.tagsIndex[rawTag] && !this.model.find(search => search.tag === rawTag))
51 .forEach(rawTag => (propositions[rawTag] = (propositions[rawTag] ?? 0) + 1)); 76 .forEach(rawTag => (propositions[rawTag] = (propositions[rawTag] ?? 0) + 1));
52 } else { 77 } else {
53 // Tags count from the whole gallery 78 // Tags count from the current directory
54 Object.entries(this.$galleryStore.tags) 79 this.currentTags
55 .forEach(entry => (propositions[entry[0]] = entry[1].items.length)); 80 .flatMap(tag => tag.split(":"))
81 .map(tag => this.tagsIndex[tag])
82 .filter(Boolean)
83 .forEach(tagindex => (propositions[tagindex.tag] = tagindex.items.length));
56 } 84 }
57 85
58 return Object.entries(propositions) 86 return Object.entries(propositions)
59 .sort((a,b) => b[1] - a[1]) 87 .sort((a, b) => b[1] - a[1])
60 .map(entry => ({rawTag: entry[0], count: entry[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");
61 } 93 }
62 94
63 extractDistinctItems(currentTags: Tag.Search[]): Gallery.Item[] { 95 extractDistinctItems(currentTags: Tag.Search[]): Gallery.Item[] {
@@ -65,29 +97,45 @@ export default class LdTagInput extends Vue {
65 } 97 }
66 98
67 rightmost(tag: Gallery.RawTag): Gallery.RawTag { 99 rightmost(tag: Gallery.RawTag): Gallery.RawTag {
68 const dot = tag.lastIndexOf("."); 100 const dot = tag.lastIndexOf(":");
69 return dot <= 0 ? tag : tag.substr(dot + 1); 101 return dot <= 0 ? tag : tag.substr(dot + 1);
70 } 102 }
71 103
72 add(operation: Operation, rawTag: Gallery.RawTag) { 104 add(operation: Operation, rawTag: Gallery.RawTag) {
73 const node = this.$galleryStore.tags[rawTag]; 105 const node = this.tagsIndex[rawTag];
74 const search: Tag.Search = { ...node, operation, display: `${operation}${node.tag}` }; 106 const display = this.category ? `${operation}${this.category.tag}:${node.tag}` : `${operation}${node.tag}`;
75 this.$uiStore.currentTags.push(search); 107 this.model.push({ ...node, parent: this.category, operation, display });
76 this.$uiStore.mode = "search";
77 } 108 }
78} 109}
79</script> 110</script>
80 111
81<style lang="scss"> 112<style lang="scss">
113@import "~@/assets/scss/theme.scss";
114
82.proposition { 115.proposition {
83 display: flex; 116 .subtitle {
84 justify-content: space-between; 117 background-color: $proposed-category-bgcolor;
85 align-items: center; 118 width: 100%;
86 margin: 10px; 119 padding: 0 0 6px 0;
87 color: lightcyan; 120 margin: 0;
88 cursor: pointer; 121 text-align: center;
89} 122 font-variant: small-caps;
90.proposition span { 123 }
91 padding: 0 10px; 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 }
92} 140}
93</style> 141</style>