From c4a51940295d514dd52f48b6f18638ac554224f4 Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Wed, 29 Jan 2020 05:35:35 +0100
Subject: viewer: Tag auto-completion should be more flexible. Resolves #38

---
 viewer/src/components/LdTagInput.vue | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index 71131e6..7c9981f 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -42,6 +42,7 @@
 <script lang="ts">
 import { Component, Vue } from "vue-property-decorator";
 import { Operation } from "@/@types/tag/Operation";
+import Tools from "@/tools";
 
 @Component
 export default class LdTagInput extends Vue {
@@ -92,18 +93,20 @@ export default class LdTagInput extends Vue {
     category: string,
     disambiguation: string
   ): Tag.Search[] {
+    disambiguation = Tools.normalize(disambiguation);
     return Object.values(tags)
       .filter(node => node.tag.includes(category))
       .flatMap(node =>
         Object.values(node.children)
-          .filter(child => child.tag.includes(disambiguation))
+          .filter(child => child.tagfiltered.includes(disambiguation))
           .map(child => ({ ...child, parent: node, operation, display: `${operation}${node.tag}:${child.tag}` }))
       );
   }
 
   searchTagsFromFilter(tags: Tag.Index, operation: Operation, filter: string): Tag.Search[] {
+    filter = Tools.normalize(filter);
     return Object.values(tags)
-      .filter(node => node.tag.includes(filter))
+      .filter(node => node.tagfiltered.includes(filter))
       .map(node => ({ ...node, operation, display: `${operation}${node.tag}` }));
   }
 
-- 
cgit v1.2.3


From 17b1598c5a5a375e6eaa0763767368c05ba0f56e Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Thu, 30 Jan 2020 22:58:27 +0100
Subject: viewer: Improved the proposition layout

---
 viewer/src/components/LdTagInput.vue | 1 -
 1 file changed, 1 deletion(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index 7c9981f..ff354c6 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -27,7 +27,6 @@
     :data="filteredTags"
     field="display"
     type="is-black"
-    icon="tag"
     size="is-medium"
     class="panelTagInput"
     @typing="searchTags"
-- 
cgit v1.2.3


From 914aa63cb65f869be9b1707c160847be16f79a68 Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Fri, 31 Jan 2020 06:55:37 +0100
Subject: viewer: unified CSS classes to kebab case

---
 viewer/src/components/LdTagInput.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index ff354c6..7bbecec 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -28,7 +28,7 @@
     field="display"
     type="is-black"
     size="is-medium"
-    class="panelTagInput"
+    class="paneltag-input"
     @typing="searchTags"
     @add="onAdd"
     @remove="onRemove"
@@ -119,7 +119,7 @@ export default class LdTagInput extends Vue {
 </script>
 
 <style lang="scss">
-.panelTagInput .autocomplete .dropdown-content {
+.paneltag-input .autocomplete .dropdown-content {
   max-height: 300px;
 }
 </style>
-- 
cgit v1.2.3


From 170da2d55fec0359cbac780786383710b734eda7 Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Fri, 31 Jan 2020 09:17:57 +0100
Subject: viewer: code cleaning, moved some views to components

---
 viewer/src/components/LdTagInput.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index 7bbecec..eff02e6 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -40,7 +40,7 @@
 
 <script lang="ts">
 import { Component, Vue } from "vue-property-decorator";
-import { Operation } from "@/@types/tag/Operation";
+import { Operation } from "@/@types/Operation";
 import Tools from "@/tools";
 
 @Component
-- 
cgit v1.2.3


From 370e3db3455f548699ff5e046e0f8dcc304991ac Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Fri, 14 Feb 2020 09:19:53 +0100
Subject: viewer: major code and search mode overhaul

Updated libraries to the lastest version
SCSS Formatter as suggested VSC extensions
Renamed toolbar-color by scrollbar-color
LD components use Props in favor of touching the stores directly (when possible)
Moved most common algorithms to a "services" folder
Complete search overhaul (lots of code change)
---
 viewer/src/components/LdTagInput.vue | 75 +++++-------------------------------
 1 file changed, 9 insertions(+), 66 deletions(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index eff02e6..982abe4 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -19,7 +19,7 @@
 
 <template>
   <b-taginput
-    v-model="$uiStore.currentTags"
+    v-model="model"
     :placeholder="$t('tagInput.placeholder')"
     autocomplete
     ellipsis
@@ -30,8 +30,6 @@
     size="is-medium"
     class="paneltag-input"
     @typing="searchTags"
-    @add="onAdd"
-    @remove="onRemove"
   >
     <template slot-scope="props">{{displayOption(props.option)}}</template>
     <template slot="empty">{{$t('tagInput.nomatch')}}</template>
@@ -39,80 +37,25 @@
 </template>
 
 <script lang="ts">
-import { Component, Vue } from "vue-property-decorator";
+import { Component, Vue, Model, Prop } from "vue-property-decorator";
 import { Operation } from "@/@types/Operation";
-import Tools from "@/tools";
+import Navigation from "@/services/navigation";
+import IndexFactory from "../services/indexfactory";
 
 @Component
 export default class LdTagInput extends Vue {
-  filteredTags: Tag.Search[] = [];
-
-  onAdd(e: any) {
-    this.$uiStore.mode = "search";
-  }
+  @Prop({ required: true }) readonly tagsIndex!: Tag.Index;
+  @Model() model!: Tag.Search[];
 
-  onRemove() {
-    if (this.$uiStore.currentTags.length === 0) this.$uiStore.mode = "navigation";
-  }
+  filteredTags: Tag.Search[] = [];
 
   displayOption(option: Tag.Search): string {
     return `${option.display} (${option.items.length})`;
   }
 
-  extractOperation(filter: string): Operation {
-    const first = filter.slice(0, 1);
-    switch (first) {
-      case Operation.ADDITION:
-      case Operation.SUBSTRACTION:
-        return first;
-      default:
-        return Operation.INTERSECTION;
-    }
-  }
-
   searchTags(filter: string) {
-    const tags = this.$galleryStore.tags;
-    let search: Tag.Search[] = [];
-    if (tags && filter) {
-      const operation = this.extractOperation(filter);
-      if (operation !== Operation.INTERSECTION) filter = filter.slice(1);
-      if (filter.includes(":")) {
-        const filterParts = filter.split(":");
-        search = this.searchTagsFromFilterWithCategory(tags, operation, filterParts[0], filterParts[1]);
-      } else {
-        search = this.searchTagsFromFilter(tags, operation, filter);
-      }
-    }
-    this.filteredTags = this.cleanupAndSort(search);
-  }
-
-  searchTagsFromFilterWithCategory(
-    tags: Tag.Index,
-    operation: Operation,
-    category: string,
-    disambiguation: string
-  ): Tag.Search[] {
-    disambiguation = Tools.normalize(disambiguation);
-    return Object.values(tags)
-      .filter(node => node.tag.includes(category))
-      .flatMap(node =>
-        Object.values(node.children)
-          .filter(child => child.tagfiltered.includes(disambiguation))
-          .map(child => ({ ...child, parent: node, operation, display: `${operation}${node.tag}:${child.tag}` }))
-      );
-  }
-
-  searchTagsFromFilter(tags: Tag.Index, operation: Operation, filter: string): Tag.Search[] {
-    filter = Tools.normalize(filter);
-    return Object.values(tags)
-      .filter(node => node.tagfiltered.includes(filter))
-      .map(node => ({ ...node, operation, display: `${operation}${node.tag}` }));
-  }
-
-  cleanupAndSort(search: Tag.Search[]): Tag.Search[] {
-    const currentTags = this.$uiStore.currentTags;
-    return search
-      .filter(node => !currentTags.find(currentTag => currentTag.tag === node.tag))
+    this.filteredTags = IndexFactory.searchTags(this.tagsIndex, filter)
+      .filter(newSearch => !this.model.find(currentSearch => currentSearch.tag === newSearch.tag))
       .sort((a, b) => b.items.length - a.items.length);
   }
 }
-- 
cgit v1.2.3


From eb00c2a7874608f70ec7768eae8d006a22bc0a54 Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Sun, 23 Feb 2020 21:33:20 +0100
Subject: viewer: absolute path fix

---
 viewer/src/components/LdTagInput.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index 982abe4..d9d932f 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -40,7 +40,7 @@
 import { Component, Vue, Model, Prop } from "vue-property-decorator";
 import { Operation } from "@/@types/Operation";
 import Navigation from "@/services/navigation";
-import IndexFactory from "../services/indexfactory";
+import IndexFactory from "@/services/indexfactory";
 
 @Component
 export default class LdTagInput extends Vue {
-- 
cgit v1.2.3


From 0f90f85347c393d24ee39947041028f567512af5 Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Tue, 25 Feb 2020 16:39:50 +0100
Subject: viewer: removing a tag from the filters opens the keyboard on mobile

GitHub: Resolves #93
---
 viewer/src/components/LdTagInput.vue | 8 ++++++++
 1 file changed, 8 insertions(+)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index d9d932f..34d96bd 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -30,6 +30,7 @@
     size="is-medium"
     class="paneltag-input"
     @typing="searchTags"
+    @click.capture.native="onClick"
   >
     <template slot-scope="props">{{displayOption(props.option)}}</template>
     <template slot="empty">{{$t('tagInput.nomatch')}}</template>
@@ -58,6 +59,13 @@ export default class LdTagInput extends Vue {
       .filter(newSearch => !this.model.find(currentSearch => currentSearch.tag === newSearch.tag))
       .sort((a, b) => b.items.length - a.items.length);
   }
+
+  // Prevents the keyboard from opening on mobile when removing a tag
+  onClick(e: MouseEvent) {
+    const target = e.target;
+    if (target instanceof HTMLAnchorElement)
+      target.addEventListener("click", e => e.stopPropagation());
+  }
 }
 </script>
 
-- 
cgit v1.2.3


From 4565feaf9ccdc9b215e85f22917bbe300f3a57c4 Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Thu, 27 Feb 2020 12:58:19 +0100
Subject: viewer: removing a tag from the filters opens the keyboard on mobile

Changed the anchor event to trigger on capture instead of bubbling. This fixes a race-condition on some browsers.
---
 viewer/src/components/LdTagInput.vue | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index 34d96bd..ad0845e 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -63,8 +63,7 @@ export default class LdTagInput extends Vue {
   // Prevents the keyboard from opening on mobile when removing a tag
   onClick(e: MouseEvent) {
     const target = e.target;
-    if (target instanceof HTMLAnchorElement)
-      target.addEventListener("click", e => e.stopPropagation());
+    if (target instanceof HTMLAnchorElement) target.addEventListener("click", e => e.stopPropagation(), true);
   }
 }
 </script>
-- 
cgit v1.2.3


From 7c2a2ff46469d5e8f44fb3ec7feae5f798e0baf8 Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Thu, 27 Feb 2020 17:23:32 +0100
Subject: viewer: architectural fixes and improvements

Make use of VueX's strict mode (which is different from vuex-class-component strict mode)
Fixed issues and bad-practices with search filter tags mutations
Correctly implement the new index.json format
---
 viewer/src/components/LdTagInput.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index ad0845e..b2a2c58 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -38,7 +38,7 @@
 </template>
 
 <script lang="ts">
-import { Component, Vue, Model, Prop } from "vue-property-decorator";
+import { Component, Vue, Prop, PropSync } from "vue-property-decorator";
 import { Operation } from "@/@types/Operation";
 import Navigation from "@/services/navigation";
 import IndexFactory from "@/services/indexfactory";
@@ -46,7 +46,7 @@ import IndexFactory from "@/services/indexfactory";
 @Component
 export default class LdTagInput extends Vue {
   @Prop({ required: true }) readonly tagsIndex!: Tag.Index;
-  @Model() model!: Tag.Search[];
+  @PropSync("searchFilters", { type: Array, required: true }) model!: Tag.Search[];
 
   filteredTags: Tag.Search[] = [];
 
-- 
cgit v1.2.3


From f2ff937fe4a5782741886ef4920fd0e284775463 Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Thu, 27 Feb 2020 23:26:00 +0100
Subject: viewer: tag index bugfix

Search from the URL requires a strict match instead of a loose match
Category search was case sensitive
Category + disambiguation was matching like an intersection of both tags instead of being hard-coupled
Removed the logs for the release (coming soon)
---
 viewer/src/components/LdTagInput.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index b2a2c58..bdb07bc 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -55,7 +55,7 @@ export default class LdTagInput extends Vue {
   }
 
   searchTags(filter: string) {
-    this.filteredTags = IndexFactory.searchTags(this.tagsIndex, filter)
+    this.filteredTags = IndexFactory.searchTags(this.tagsIndex, filter, false)
       .filter(newSearch => !this.model.find(currentSearch => currentSearch.tag === newSearch.tag))
       .sort((a, b) => b.items.length - a.items.length);
   }
-- 
cgit v1.2.3


From 332b208d3fdc91d29181c8f42ef5ff9b1fd1f09a Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Thu, 2 Apr 2020 18:45:20 +0200
Subject: viewer: press "enter" in the empty tagInput to trigger a search

revert: viewer: removing a tag from the filters opens the keyboard on mobile (fixed in Buefy)

GitHub: Resolves #168
---
 viewer/src/components/LdTagInput.vue | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index bdb07bc..b1b9e3e 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -30,7 +30,9 @@
     size="is-medium"
     class="paneltag-input"
     @typing="searchTags"
-    @click.capture.native="onClick"
+    @add="clearCurrentFilter"
+    @remove="clearCurrentFilter"
+    @keydown.enter.native="onKeyEnter"
   >
     <template slot-scope="props">{{displayOption(props.option)}}</template>
     <template slot="empty">{{$t('tagInput.nomatch')}}</template>
@@ -38,7 +40,7 @@
 </template>
 
 <script lang="ts">
-import { Component, Vue, Prop, PropSync } from "vue-property-decorator";
+import { Component, Vue, Prop, PropSync, Emit } from "vue-property-decorator";
 import { Operation } from "@/@types/Operation";
 import Navigation from "@/services/navigation";
 import IndexFactory from "@/services/indexfactory";
@@ -48,6 +50,7 @@ export default class LdTagInput extends Vue {
   @Prop({ required: true }) readonly tagsIndex!: Tag.Index;
   @PropSync("searchFilters", { type: Array, required: true }) model!: Tag.Search[];
 
+  currentFilter: string = "";
   filteredTags: Tag.Search[] = [];
 
   displayOption(option: Tag.Search): string {
@@ -55,15 +58,27 @@ export default class LdTagInput extends Vue {
   }
 
   searchTags(filter: string) {
+    this.currentFilter = filter;
     this.filteredTags = IndexFactory.searchTags(this.tagsIndex, filter, false)
       .filter(newSearch => !this.model.find(currentSearch => currentSearch.tag === newSearch.tag))
       .sort((a, b) => b.items.length - a.items.length);
   }
 
-  // Prevents the keyboard from opening on mobile when removing a tag
-  onClick(e: MouseEvent) {
-    const target = e.target;
-    if (target instanceof HTMLAnchorElement) target.addEventListener("click", e => e.stopPropagation(), true);
+  clearCurrentFilter() {
+    // Necessary for @keydown.enter.native, nexttick is too short
+    setTimeout(() => {
+      this.currentFilter = "";
+      this.filteredTags = [];
+    });
+  }
+
+  onKeyEnter(e: KeyboardEvent) {
+    if (!this.currentFilter) this.onkeyenterEmpty(e);
+  }
+
+  @Emit()
+  onkeyenterEmpty(e: KeyboardEvent) {
+    return e;
   }
 }
 </script>
-- 
cgit v1.2.3


From 09ec37a772802980d68264f2fed040be36e14c82 Mon Sep 17 00:00:00 2001
From: Zero~Informatique
Date: Fri, 3 Apr 2020 21:57:19 +0200
Subject: viewer: tag categories implementation

fixed disambiguation by categories following code review

GitHub: Resolves #29
---
 viewer/src/components/LdTagInput.vue | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

(limited to 'viewer/src/components/LdTagInput.vue')

diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue
index bdb07bc..865db96 100644
--- a/viewer/src/components/LdTagInput.vue
+++ b/viewer/src/components/LdTagInput.vue
@@ -54,9 +54,16 @@ export default class LdTagInput extends Vue {
     return `${option.display} (${option.items.length})`;
   }
 
+  filterAlreadyPresent(newSearch: Tag.Search) {
+    return !this.model.find(
+      currentSearch =>
+        currentSearch.tag === newSearch.tag && (!currentSearch.parent || currentSearch.parent === newSearch.parent)
+    );
+  }
+
   searchTags(filter: string) {
     this.filteredTags = IndexFactory.searchTags(this.tagsIndex, filter, false)
-      .filter(newSearch => !this.model.find(currentSearch => currentSearch.tag === newSearch.tag))
+      .filter(this.filterAlreadyPresent)
       .sort((a, b) => b.items.length - a.items.length);
   }
 
-- 
cgit v1.2.3

