From 812583f33c50ad8b9a32d4c3347784ba9d2a9d86 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 1 Feb 2026 01:19:39 -0700 Subject: [PATCH] Improve the multi-select behaviour/appearance and display how many selections are made/required --- langs/en-ca.json | 3 +++ module/apps/ArtBrowser.mjs | 42 ++++++++++++++++++++++++----- styles/apps/ArtBrowser.css | 18 +++++++++---- styles/elements/checkbox.css | 3 +++ styles/main.css | 1 + templates/ArtBrowser/image/grid.hbs | 14 +++++----- templates/ArtBrowser/images.hbs | 14 ++++++++-- 7 files changed, 75 insertions(+), 20 deletions(-) create mode 100644 styles/elements/checkbox.css diff --git a/langs/en-ca.json b/langs/en-ca.json index d32b3ba..3c0f95a 100644 --- a/langs/en-ca.json +++ b/langs/en-ca.json @@ -4,6 +4,9 @@ "unsaved": "This change hasn't been saved, if you close without saving it will be undone." }, "apps": { + "ArtBrowser": { + "selected": "{current}/{required} Selected" + }, "ArtistApp": { "title": { "create": "Create New Artist", diff --git a/module/apps/ArtBrowser.mjs b/module/apps/ArtBrowser.mjs index 0960d30..c28f958 100644 --- a/module/apps/ArtBrowser.mjs +++ b/module/apps/ArtBrowser.mjs @@ -8,7 +8,7 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { FormDataExtended } = foundry.applications.ux; const { deepClone } = foundry.utils; -const PAGE_SIZE = 52; +const PAGE_SIZE = 8; export class ArtBrowser extends HandlebarsApplicationMixin(ApplicationV2) { // #region Options @@ -129,6 +129,26 @@ export class ArtBrowser extends HandlebarsApplicationMixin(ApplicationV2) { this.#onFilterSubmit.bind(this), ); }; + + if (options.parts?.includes(`images`)) { + this._updateSelectedCount(); + }; + }; + + _updateSelectedCount() { + if (!this.rendered || this.selectMode === `single`) return; + + const element = this.element.querySelector(`.selected-count`); + if (element) { + element.innerText = `${this.#selected.size} Selected`; + element.innerText = _loc( + `TB.apps.ArtBrowser.selected`, + { + current: this.#selected.size, + required: this.#selectCount, + }, + ); + }; }; async close(options) { @@ -157,7 +177,13 @@ export class ArtBrowser extends HandlebarsApplicationMixin(ApplicationV2) { meta: { idp: this.id, }, - selectMode: this.selectMode, + is: { + multi: this.selectMode === `multi`, + single: this.selectMode === `single`, + }, + can: { + upload: true, + }, }; }; @@ -194,8 +220,6 @@ export class ArtBrowser extends HandlebarsApplicationMixin(ApplicationV2) { const images = []; for (const [id, image] of allImages) { - image.id = id; - // Check if it matches the required filters if (this.filters.name && !image.name.includes(this.filters.name)) { continue; @@ -206,6 +230,10 @@ export class ArtBrowser extends HandlebarsApplicationMixin(ApplicationV2) { const hasAllTags = this.filters.tags.every(tag => image.tags.includes(tag)); if ((!hasAnArtist && hasArtistFilter) || !hasAllTags) { continue }; + // Populate ephemeral data for rendering + image.id = id; + image.selected = this.#selected.has(imagePath(image)); + // Convert all of the artist IDs into the actual data image.artists = image.artists .map(artistID => { @@ -271,15 +299,15 @@ export class ArtBrowser extends HandlebarsApplicationMixin(ApplicationV2) { const path = imagePath(image); if (this.#selectCount > 1) { - if (this.#selected.has(path)) { - target.ariaPressed = false; + if (!target.checked) { this.#selected.delete(path); } else { - target.ariaPressed = true; this.#selected.add(path); }; if (this.#selected.size >= this.#selectCount) { await this.#submit(Array.from(this.#selected)); + } else { + this._updateSelectedCount(); }; } else { await this.#submit(path); diff --git a/styles/apps/ArtBrowser.css b/styles/apps/ArtBrowser.css index 22a1324..a6e677d 100644 --- a/styles/apps/ArtBrowser.css +++ b/styles/apps/ArtBrowser.css @@ -30,18 +30,22 @@ gap: 8px; border-radius: 4px; overflow: hidden; - transition: background 100ms ease-in-out; + position: relative; + transition: all 100ms ease-in-out; - &.grid { - &:hover { - background: var(--color-cool-4); - } + &:hover, &:has(input[type="checkbox"]:checked) { + background: var(--color-warm-3); } img { width: 100%; aspect-ratio: 1; object-fit: contain; + transition: all 100ms ease-in-out; + } + + &:has(input[type="checkbox"]:checked) img { + transform: scale(0.85); } .details { @@ -50,6 +54,10 @@ gap: 4px; padding: 0px 4px 4px; } + + input[type="checkbox"] { + position: absolute; + } } .paginated { diff --git a/styles/elements/checkbox.css b/styles/elements/checkbox.css new file mode 100644 index 0000000..0cc5257 --- /dev/null +++ b/styles/elements/checkbox.css @@ -0,0 +1,3 @@ +.token-browser > .window-content input[type="checkbox"] { + --checkbox-checked-color: var(--color-level-success-border); +} diff --git a/styles/main.css b/styles/main.css index 8dc4d08..6dfb3c3 100644 --- a/styles/main.css +++ b/styles/main.css @@ -6,6 +6,7 @@ /* Elements */ @import url("./elements/utils.css") layer(elements); +@import url("./elements/checkbox.css") layer(elements); @import url("./elements/lists.css") layer(elements); /* Apps */ diff --git a/templates/ArtBrowser/image/grid.hbs b/templates/ArtBrowser/image/grid.hbs index 595b998..6744778 100644 --- a/templates/ArtBrowser/image/grid.hbs +++ b/templates/ArtBrowser/image/grid.hbs @@ -6,18 +6,20 @@ src="{{tb-filePath image.path}}" alt="" > - {{#if (eq selectMode "single")}} + {{#if is.single}} - {{else if (eq selectMode "multi")}} - {{/if}} + + diff --git a/templates/ArtBrowser/images.hbs b/templates/ArtBrowser/images.hbs index bdc3c7b..effff3d 100644 --- a/templates/ArtBrowser/images.hbs +++ b/templates/ArtBrowser/images.hbs @@ -1,11 +1,21 @@
- + {{#if can.upload}} + + {{/if}} + {{#if is.multi}} +
+
+ {{/if}}
{{#if images}} {{else}}