diff options
Diffstat (limited to 'viewer/src/services')
-rw-r--r-- | viewer/src/services/dragscrollclickfix.ts | 3 | ||||
-rw-r--r-- | viewer/src/services/indexfactory.ts | 26 | ||||
-rw-r--r-- | viewer/src/services/indexsearch.ts | 7 | ||||
-rw-r--r-- | viewer/src/services/itemComparators.ts | 73 | ||||
-rw-r--r-- | viewer/src/services/ldzoom.ts | 6 | ||||
-rw-r--r-- | viewer/src/services/navigation.ts | 41 |
6 files changed, 125 insertions, 31 deletions
diff --git a/viewer/src/services/dragscrollclickfix.ts b/viewer/src/services/dragscrollclickfix.ts index 38eb106..7125510 100644 --- a/viewer/src/services/dragscrollclickfix.ts +++ b/viewer/src/services/dragscrollclickfix.ts | |||
@@ -19,7 +19,6 @@ | |||
19 | 19 | ||
20 | // https://github.com/donmbelembe/vue-dragscroll/issues/61 | 20 | // https://github.com/donmbelembe/vue-dragscroll/issues/61 |
21 | export default class DragScrollClickFix { | 21 | export default class DragScrollClickFix { |
22 | |||
23 | readonly DRAG_DELAY = 250; // This is the minimal delay to consider a click to be a drag, mostly usefull for touch devices | 22 | readonly DRAG_DELAY = 250; // This is the minimal delay to consider a click to be a drag, mostly usefull for touch devices |
24 | 23 | ||
25 | timer: NodeJS.Timeout | null = null; | 24 | timer: NodeJS.Timeout | null = null; |
@@ -39,7 +38,7 @@ export default class DragScrollClickFix { | |||
39 | clearTimeout(this.timer); | 38 | clearTimeout(this.timer); |
40 | this.timer = null; | 39 | this.timer = null; |
41 | } | 40 | } |
42 | setTimeout(() => this.dragging = false); | 41 | setTimeout(() => (this.dragging = false)); |
43 | } | 42 | } |
44 | 43 | ||
45 | onClickCapture(e: MouseEvent) { | 44 | onClickCapture(e: MouseEvent) { |
diff --git a/viewer/src/services/indexfactory.ts b/viewer/src/services/indexfactory.ts index e402185..4b28a60 100644 --- a/viewer/src/services/indexfactory.ts +++ b/viewer/src/services/indexfactory.ts | |||
@@ -18,19 +18,19 @@ | |||
18 | */ | 18 | */ |
19 | 19 | ||
20 | import { Operation } from "@/@types/Operation"; | 20 | import { Operation } from "@/@types/Operation"; |
21 | import { ItemType } from "@/@types/ItemType"; | ||
21 | import Navigation from "@/services/navigation"; | 22 | import Navigation from "@/services/navigation"; |
22 | 23 | ||
23 | export default class IndexFactory { | 24 | export default class IndexFactory { |
24 | |||
25 | public static generateTags(root: Gallery.Item | null): Tag.Index { | 25 | public static generateTags(root: Gallery.Item | null): Tag.Index { |
26 | let tagsIndex: Tag.Index = {}; | 26 | const tagsIndex: Tag.Index = {}; |
27 | if (root) IndexFactory.pushTagsForItem(tagsIndex, root); | 27 | if (root) IndexFactory.pushTagsForItem(tagsIndex, root); |
28 | return tagsIndex; | 28 | return tagsIndex; |
29 | } | 29 | } |
30 | 30 | ||
31 | // Pushes all tags for a root item (and its children) to the index | 31 | // Pushes all tags for a root item (and its children) to the index |
32 | private static pushTagsForItem(tagsIndex: Tag.Index, item: Gallery.Item): void { | 32 | private static pushTagsForItem(tagsIndex: Tag.Index, item: Gallery.Item): void { |
33 | if (item.properties.type === "directory") { | 33 | if (item.properties.type === ItemType.DIRECTORY) { |
34 | item.properties.items.forEach(item => this.pushTagsForItem(tagsIndex, item)); | 34 | item.properties.items.forEach(item => this.pushTagsForItem(tagsIndex, item)); |
35 | return; // Directories are not indexed | 35 | return; // Directories are not indexed |
36 | } | 36 | } |
@@ -50,7 +50,15 @@ export default class IndexFactory { | |||
50 | } | 50 | } |
51 | 51 | ||
52 | private static pushPartToIndex(index: Tag.Node, part: string, item: Gallery.Item, rootPart: boolean): Tag.Node { | 52 | private static pushPartToIndex(index: Tag.Node, part: string, item: Gallery.Item, rootPart: boolean): Tag.Node { |
53 | if (!index) index = { tag: part, tagfiltered: Navigation.normalize(part), rootPart, childPart: !rootPart, items: [], children: {} }; | 53 | if (!index) |
54 | index = { | ||
55 | tag: part, | ||
56 | tagfiltered: Navigation.normalize(part), | ||
57 | rootPart, | ||
58 | childPart: !rootPart, | ||
59 | items: [], | ||
60 | children: {}, | ||
61 | }; | ||
54 | else if (rootPart) index.rootPart = true; | 62 | else if (rootPart) index.rootPart = true; |
55 | else index.childPart = true; | 63 | else index.childPart = true; |
56 | 64 | ||
@@ -60,7 +68,6 @@ export default class IndexFactory { | |||
60 | 68 | ||
61 | // --- | 69 | // --- |
62 | 70 | ||
63 | |||
64 | public static searchTags(tagsIndex: Tag.Index, filter: string, strict: boolean): Tag.Search[] { | 71 | public static searchTags(tagsIndex: Tag.Index, filter: string, strict: boolean): Tag.Search[] { |
65 | let search: Tag.Search[] = []; | 72 | let search: Tag.Search[] = []; |
66 | if (tagsIndex && filter) { | 73 | if (tagsIndex && filter) { |
@@ -105,7 +112,12 @@ export default class IndexFactory { | |||
105 | ); | 112 | ); |
106 | } | 113 | } |
107 | 114 | ||
108 | private static searchTagsFromFilter(tagsIndex: Tag.Index, operation: Operation, filter: string, strict: boolean): Tag.Search[] { | 115 | private static searchTagsFromFilter( |
116 | tagsIndex: Tag.Index, | ||
117 | operation: Operation, | ||
118 | filter: string, | ||
119 | strict: boolean | ||
120 | ): Tag.Search[] { | ||
109 | filter = Navigation.normalize(filter); | 121 | filter = Navigation.normalize(filter); |
110 | return Object.values(tagsIndex) | 122 | return Object.values(tagsIndex) |
111 | .filter(node => IndexFactory.matches(node, filter, strict)) | 123 | .filter(node => IndexFactory.matches(node, filter, strict)) |
@@ -114,7 +126,7 @@ export default class IndexFactory { | |||
114 | 126 | ||
115 | private static matches(node: Tag.Node, filter: string, strict: boolean): boolean { | 127 | private static matches(node: Tag.Node, filter: string, strict: boolean): boolean { |
116 | if (strict) return node.tagfiltered === filter; | 128 | if (strict) return node.tagfiltered === filter; |
117 | return node.tagfiltered.includes(filter) | 129 | return node.tagfiltered.includes(filter); |
118 | } | 130 | } |
119 | 131 | ||
120 | // --- | 132 | // --- |
diff --git a/viewer/src/services/indexsearch.ts b/viewer/src/services/indexsearch.ts index a55a829..00f8cfc 100644 --- a/viewer/src/services/indexsearch.ts +++ b/viewer/src/services/indexsearch.ts | |||
@@ -20,7 +20,6 @@ | |||
20 | import { Operation } from "@/@types/Operation"; | 20 | import { Operation } from "@/@types/Operation"; |
21 | 21 | ||
22 | export default class IndexSearch { | 22 | export default class IndexSearch { |
23 | |||
24 | // Results of the search (by tags) | 23 | // Results of the search (by tags) |
25 | public static search(searchTags: Tag.Search[]): Gallery.Item[] { | 24 | public static search(searchTags: Tag.Search[]): Gallery.Item[] { |
26 | const byOperation = this.extractTagsByOperation(searchTags); | 25 | const byOperation = this.extractTagsByOperation(searchTags); |
@@ -30,7 +29,7 @@ export default class IndexSearch { | |||
30 | } | 29 | } |
31 | 30 | ||
32 | private static extractTagsByOperation(searchTags: Tag.Search[]): Tag.SearchByOperation { | 31 | private static extractTagsByOperation(searchTags: Tag.Search[]): Tag.SearchByOperation { |
33 | let byOperation: Tag.SearchByOperation = {}; | 32 | const byOperation: Tag.SearchByOperation = {}; |
34 | Object.values(Operation).forEach( | 33 | Object.values(Operation).forEach( |
35 | operation => (byOperation[operation] = searchTags.filter(tag => tag.operation === operation)) | 34 | operation => (byOperation[operation] = searchTags.filter(tag => tag.operation === operation)) |
36 | ); | 35 | ); |
@@ -38,7 +37,7 @@ export default class IndexSearch { | |||
38 | } | 37 | } |
39 | 38 | ||
40 | private static extractIntersection(byOperation: Tag.SearchByOperation): Set<Gallery.Item> { | 39 | private static extractIntersection(byOperation: Tag.SearchByOperation): Set<Gallery.Item> { |
41 | let intersection = new Set<Gallery.Item>(); | 40 | const intersection = new Set<Gallery.Item>(); |
42 | if (byOperation[Operation.INTERSECTION].length > 0) { | 41 | if (byOperation[Operation.INTERSECTION].length > 0) { |
43 | byOperation[Operation.INTERSECTION] | 42 | byOperation[Operation.INTERSECTION] |
44 | .map(tag => tag.items) | 43 | .map(tag => tag.items) |
@@ -50,7 +49,7 @@ export default class IndexSearch { | |||
50 | } | 49 | } |
51 | 50 | ||
52 | private static extractSubstraction(byOperation: Tag.SearchByOperation): Set<Gallery.Item> { | 51 | private static extractSubstraction(byOperation: Tag.SearchByOperation): Set<Gallery.Item> { |
53 | let substraction = new Set<Gallery.Item>(); | 52 | const substraction = new Set<Gallery.Item>(); |
54 | if (byOperation[Operation.SUBSTRACTION].length > 0) { | 53 | if (byOperation[Operation.SUBSTRACTION].length > 0) { |
55 | byOperation[Operation.SUBSTRACTION].flatMap(tag => tag.items).forEach(item => substraction.add(item)); | 54 | byOperation[Operation.SUBSTRACTION].flatMap(tag => tag.items).forEach(item => substraction.add(item)); |
56 | } | 55 | } |
diff --git a/viewer/src/services/itemComparators.ts b/viewer/src/services/itemComparators.ts new file mode 100644 index 0000000..bd9accb --- /dev/null +++ b/viewer/src/services/itemComparators.ts | |||
@@ -0,0 +1,73 @@ | |||
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 | import { TranslateResult } from "vue-i18n"; | ||
20 | import i18n from "@/plugins/i18n"; | ||
21 | |||
22 | export type ItemComparator = (left: Gallery.Item, right: Gallery.Item) => number; | ||
23 | export type ItemSort = { text: TranslateResult; fn: ItemComparator }; | ||
24 | |||
25 | export default class ItemComparators { | ||
26 | static readonly ITEM_SORTS: Record<Gallery.ItemSortStr, ItemSort> = { | ||
27 | title_asc: { | ||
28 | text: i18n.t("command.sort.byTitleAsc"), | ||
29 | fn: ItemComparators.chain(ItemComparators.sortByTitleAsc, ItemComparators.sortByPathAsc), | ||
30 | }, | ||
31 | date_asc: { | ||
32 | text: i18n.t("command.sort.byDateAsc"), | ||
33 | fn: ItemComparators.chain(ItemComparators.sortByDateAsc, ItemComparators.sortByPathAsc), | ||
34 | }, | ||
35 | date_desc: { | ||
36 | text: i18n.t("command.sort.byDateDesc"), | ||
37 | fn: ItemComparators.reverse(ItemComparators.chain(ItemComparators.sortByDateAsc, ItemComparators.sortByPathAsc)), | ||
38 | }, | ||
39 | }; | ||
40 | |||
41 | static readonly DEFAULT = ItemComparators.ITEM_SORTS.date_asc; | ||
42 | |||
43 | static sortByPathAsc(left: Gallery.Item, right: Gallery.Item): number { | ||
44 | return left.path.localeCompare(right.path, undefined, { | ||
45 | sensitivity: "base", | ||
46 | ignorePunctuation: true, | ||
47 | numeric: true, | ||
48 | }); | ||
49 | } | ||
50 | |||
51 | static sortByTitleAsc(left: Gallery.Item, right: Gallery.Item): number { | ||
52 | return left.title.localeCompare(right.title, undefined, { | ||
53 | sensitivity: "base", | ||
54 | ignorePunctuation: true, | ||
55 | numeric: true, | ||
56 | }); | ||
57 | } | ||
58 | |||
59 | static sortByDateAsc(left: Gallery.Item, right: Gallery.Item): number { | ||
60 | return left.datetime.localeCompare(right.datetime); // TODO: handle timezones | ||
61 | } | ||
62 | |||