diff options
Diffstat (limited to 'viewer/src')
-rw-r--r-- | viewer/src/@types/gallery.d.ts | 3 | ||||
-rw-r--r-- | viewer/src/@types/tag.d.ts | 6 | ||||
-rw-r--r-- | viewer/src/assets/scss/global.scss | 4 | ||||
-rw-r--r-- | viewer/src/assets/scss/theme.scss | 4 | ||||
-rw-r--r-- | viewer/src/components/LdProposition.vue | 52 | ||||
-rw-r--r-- | viewer/src/components/LdTagInput.vue | 9 | ||||
-rw-r--r-- | viewer/src/locales/en.json | 5 | ||||
-rw-r--r-- | viewer/src/services/indexfactory.ts | 30 | ||||
-rw-r--r-- | viewer/src/store/galleryStore.ts | 15 | ||||
-rw-r--r-- | viewer/src/views/PanelLeft.vue | 24 |
10 files changed, 117 insertions, 35 deletions
diff --git a/viewer/src/@types/gallery.d.ts b/viewer/src/@types/gallery.d.ts index 4fa544f..de1c0dd 100644 --- a/viewer/src/@types/gallery.d.ts +++ b/viewer/src/@types/gallery.d.ts | |||
@@ -23,7 +23,8 @@ declare namespace Gallery { | |||
23 | } | 23 | } |
24 | 24 | ||
25 | interface GalleryProperties { | 25 | interface GalleryProperties { |
26 | galleryTitle: string | 26 | galleryTitle: string, |
27 | tagCategories: RawTag[] | ||
27 | } | 28 | } |
28 | interface Index { | 29 | interface Index { |
29 | properties: GalleryProperties, | 30 | properties: GalleryProperties, |
diff --git a/viewer/src/@types/tag.d.ts b/viewer/src/@types/tag.d.ts index a390c80..229c418 100644 --- a/viewer/src/@types/tag.d.ts +++ b/viewer/src/@types/tag.d.ts | |||
@@ -21,6 +21,7 @@ declare namespace Tag { | |||
21 | interface Node { | 21 | interface Node { |
22 | tag: Gallery.RawTag; | 22 | tag: Gallery.RawTag; |
23 | tagfiltered: Gallery.RawTag; | 23 | tagfiltered: Gallery.RawTag; |
24 | rootPart: boolean; | ||
24 | items: Gallery.Item[]; | 25 | items: Gallery.Item[]; |
25 | children: Index; | 26 | children: Index; |
26 | } | 27 | } |
@@ -31,4 +32,9 @@ declare namespace Tag { | |||
31 | } | 32 | } |
32 | type SearchByOperation = { [index: string]: Tag.Search[] }; | 33 | type SearchByOperation = { [index: string]: Tag.Search[] }; |
33 | type Index = { [index: string]: Node }; | 34 | type Index = { [index: string]: Node }; |
35 | |||
36 | interface Category { | ||
37 | tag: string; | ||
38 | index: Index; | ||
39 | } | ||
34 | } | 40 | } |
diff --git a/viewer/src/assets/scss/global.scss b/viewer/src/assets/scss/global.scss index bd7a7e7..9e61b8b 100644 --- a/viewer/src/assets/scss/global.scss +++ b/viewer/src/assets/scss/global.scss | |||
@@ -85,8 +85,8 @@ button svg + span { | |||
85 | overflow: auto; | 85 | overflow: auto; |
86 | } | 86 | } |
87 | .scrollbar::-webkit-scrollbar { | 87 | .scrollbar::-webkit-scrollbar { |
88 | width: 10px; | 88 | width: $scrollbar-width; |
89 | height: 10px; | 89 | height: $scrollbar-width; |
90 | } | 90 | } |
91 | .scrollbar::-webkit-scrollbar-corner { | 91 | .scrollbar::-webkit-scrollbar-corner { |
92 | background-color: transparent; | 92 | background-color: transparent; |
diff --git a/viewer/src/assets/scss/theme.scss b/viewer/src/assets/scss/theme.scss index 26cb355..feb8543 100644 --- a/viewer/src/assets/scss/theme.scss +++ b/viewer/src/assets/scss/theme.scss | |||
@@ -42,6 +42,8 @@ $radius: 0; | |||
42 | $loading-background: $palette-800; | 42 | $loading-background: $palette-800; |
43 | $title-color: $palette-200; | 43 | $title-color: $palette-200; |
44 | $title-size: $size-5; | 44 | $title-size: $size-5; |
45 | $subtitle-color: $palette-200; | ||
46 | $subtitle-size: $size-5; | ||
45 | $tag-background-color: $palette-800; | 47 | $tag-background-color: $palette-800; |
46 | $button-color: $palette-100; | 48 | $button-color: $palette-100; |
47 | $button-active-color: $palette-100; | 49 | $button-active-color: $palette-100; |
@@ -61,11 +63,13 @@ $panel-left-txtcolor: $primary; | |||
61 | $command-buttons-bgcolor: $palette-700; | 63 | $command-buttons-bgcolor: $palette-700; |
62 | $content-bgcolor: $palette-900; | 64 | $content-bgcolor: $palette-900; |
63 | $scrollbar-color: $palette-300; | 65 | $scrollbar-color: $palette-300; |
66 | $scrollbar-width: 10px; | ||
64 | $loader-color: $palette-800; | 67 | $loader-color: $palette-800; |
65 | $input-tag-delete-background-color: $palette-700; | 68 | $input-tag-delete-background-color: $palette-700; |
66 | $breadcrumb-margins: 12px; | 69 | $breadcrumb-margins: 12px; |
67 | $breadcrumb-overflow-mask-size: $breadcrumb-margins + 60px; | 70 | $breadcrumb-overflow-mask-size: $breadcrumb-margins + 60px; |
68 | $thumbnail-other-size: 120px; | 71 | $thumbnail-other-size: 120px; |
72 | $proposed-category-bgcolor: $palette-700; | ||
69 | 73 | ||
70 | // Layout | 74 | // Layout |
71 | 75 | ||
diff --git a/viewer/src/components/LdProposition.vue b/viewer/src/components/LdProposition.vue index 3357777..c69a7c3 100644 --- a/viewer/src/components/LdProposition.vue +++ b/viewer/src/components/LdProposition.vue | |||
@@ -19,8 +19,9 @@ | |||
19 | --> | 19 | --> |
20 | 20 | ||
21 | <template> | 21 | <template> |
22 | <div> | 22 | <div class="proposition"> |
23 | <div v-for="proposed in proposedTags" :key="proposed.rawTag" class="proposition"> | 23 | <h2 v-if="showCategory && proposedTags.length" class="subtitle category">{{title}}</h2> |
24 | <div v-for="proposed in proposedTags" :key="proposed.rawTag"> | ||
24 | <a | 25 | <a |
25 | class="operation-btns link" | 26 | class="operation-btns link" |
26 | :title="$t('tag-propositions.substraction')" | 27 | :title="$t('tag-propositions.substraction')" |
@@ -54,6 +55,8 @@ import { Operation } from "@/@types/Operation"; | |||
54 | 55 | ||
55 | @Component | 56 | @Component |
56 | export default class LdProposition extends Vue { | 57 | export default class LdProposition extends Vue { |
58 | @Prop() readonly category?: Tag.Node; | ||
59 | @Prop({ type: Boolean, required: true }) readonly showCategory!: boolean; | ||
57 | @Prop({ type: Array, required: true }) readonly currentTags!: string[]; | 60 | @Prop({ type: Array, required: true }) readonly currentTags!: string[]; |
58 | @Prop({ required: true }) readonly tagsIndex!: Tag.Index; | 61 | @Prop({ required: true }) readonly tagsIndex!: Tag.Index; |
59 | @PropSync("searchFilters", { type: Array, required: true }) model!: Tag.Search[]; | 62 | @PropSync("searchFilters", { type: Array, required: true }) model!: Tag.Search[]; |
@@ -69,13 +72,14 @@ export default class LdProposition extends Vue { | |||
69 | this.extractDistinctItems(this.model) | 72 | this.extractDistinctItems(this.model) |
70 | .flatMap(item => item.tags) | 73 | .flatMap(item => item.tags) |
71 | .map(this.rightmost) | 74 | .map(this.rightmost) |
72 | .filter(rawTag => !this.model.find(search => search.tag === rawTag)) | 75 | .filter(rawTag => this.tagsIndex[rawTag] && !this.model.find(search => search.tag === rawTag)) |
73 | .forEach(rawTag => (propositions[rawTag] = (propositions[rawTag] ?? 0) + 1)); | 76 | .forEach(rawTag => (propositions[rawTag] = (propositions[rawTag] ?? 0) + 1)); |
74 | } else { | 77 | } else { |
75 | // Tags count from the current directory | 78 | // Tags count from the current directory |
76 | this.currentTags | 79 | this.currentTags |
77 | .flatMap(tag => tag.split(":")) | 80 | .flatMap(tag => tag.split(":")) |
78 | .map(tag => this.tagsIndex[tag]) | 81 | .map(tag => this.tagsIndex[tag]) |
82 | .filter(Boolean) | ||
79 | .forEach(tagindex => (propositions[tagindex.tag] = tagindex.items.length)); | 83 | .forEach(tagindex => (propositions[tagindex.tag] = tagindex.items.length)); |
80 | } | 84 | } |
81 | 85 | ||
@@ -84,6 +88,10 @@ export default class LdProposition extends Vue { | |||
84 | .map(entry => ({ rawTag: entry[0], count: entry[1] })); | 88 | .map(entry => ({ rawTag: entry[0], count: entry[1] })); |
85 | } | 89 | } |
86 | 90 | ||
91 | get title() { | ||
92 | return this.category?.tag ?? this.$t("panelLeft.propositions.other"); | ||
93 | } | ||
94 | |||
87 | extractDistinctItems(currentTags: Tag.Search[]): Gallery.Item[] { | 95 | extractDistinctItems(currentTags: Tag.Search[]): Gallery.Item[] { |
88 | return [...new Set(currentTags.flatMap(tag => tag.items))]; | 96 | return [...new Set(currentTags.flatMap(tag => tag.items))]; |
89 | } | 97 | } |
@@ -95,8 +103,8 @@ export default class LdProposition extends Vue { | |||
95 | 103 | ||
96 | add(operation: Operation, rawTag: Gallery.RawTag) { | 104 | add(operation: Operation, rawTag: Gallery.RawTag) { |
97 | const node = this.tagsIndex[rawTag]; | 105 | const node = this.tagsIndex[rawTag]; |
98 | const search: Tag.Search = { ...node, operation, display: `${operation}${node.tag}` }; | 106 | const display = this.category ? `${operation}${this.category.tag}:${node.tag}` : `${operation}${node.tag}`; |
99 | this.model.push(search); | 107 | this.model.push({ ...node, parent: this.category, operation, display }); |
100 | } | 108 | } |
101 | } | 109 | } |
102 | </script> | 110 | </script> |
@@ -105,19 +113,29 @@ export default class LdProposition extends Vue { | |||
105 | @import "@/assets/scss/theme.scss"; | 113 | @import "@/assets/scss/theme.scss"; |
106 | 114 | ||
107 | .proposition { | 115 | .proposition { |
108 | display: flex; | 116 | .subtitle { |
109 | align-items: center; | 117 | background-color: $proposed-category-bgcolor; |
110 | padding-right: 7px; | 118 | width: 100%; |
111 | .operation-tag { | 119 | padding: 0 0 6px 0; |
112 | text-overflow: ellipsis; | 120 | margin: 0; |
113 | white-space: nowrap; | 121 | text-align: center; |
114 | overflow: hidden; | 122 | font-variant: small-caps; |
115 | flex-grow: 1; | ||
116 | cursor: pointer; | ||
117 | } | 123 | } |
118 | .operation-btns { | 124 | > div { |
119 | padding: 2px 7px; | 125 | display: flex; |
120 | cursor: pointer; | 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 | } | ||
121 | } | 139 | } |
122 | } | 140 | } |
123 | </style> | 141 | </style> |
diff --git a/viewer/src/components/LdTagInput.vue b/viewer/src/components/LdTagInput.vue index b1b9e3e..6b6e749 100644 --- a/viewer/src/components/LdTagInput.vue +++ b/viewer/src/components/LdTagInput.vue | |||
@@ -57,10 +57,17 @@ export default class LdTagInput extends Vue { | |||
57 | return `${option.display} (${option.items.length})`; | 57 | return `${option.display} (${option.items.length})`; |
58 | } | 58 | } |
59 | 59 | ||
60 | filterAlreadyPresent(newSearch: Tag.Search) { | ||
61 | return !this.model.find( | ||
62 | currentSearch => | ||
63 | currentSearch.tag === newSearch.tag && (!currentSearch.parent || currentSearch.parent === newSearch.parent) | ||
64 | ); | ||
65 | } | ||
66 | |||
60 | searchTags(filter: string) { | 67 | searchTags(filter: string) { |
61 | this.currentFilter = filter; | 68 | this.currentFilter = filter; |