diff --git a/src/components/features/PresetView.svelte b/src/components/features/PresetView.svelte index 18c24ca..4dcc1b4 100644 --- a/src/components/features/PresetView.svelte +++ b/src/components/features/PresetView.svelte @@ -18,3 +18,9 @@ +{#if preset.settings.exclusive} + + Only one tag in this preset should be active at a time. If you will click on other non-active tag, other tags will + be automatically removed from the editor. + +{/if} diff --git a/src/content/components/extension/presets/PresetTableRow.ts b/src/content/components/extension/presets/PresetTableRow.ts index 61bcafd..025e3da 100644 --- a/src/content/components/extension/presets/PresetTableRow.ts +++ b/src/content/components/extension/presets/PresetTableRow.ts @@ -9,6 +9,7 @@ export default class PresetTableRow extends BaseComponent { #tagsList: HTMLElement[] = []; #applyAllButton = document.createElement('button'); #removeAllButton = document.createElement('button'); + #exclusiveWarning = document.createElement('div'); constructor(container: HTMLElement, preset: TagEditorPreset) { super(container); @@ -32,6 +33,7 @@ export default class PresetTableRow extends BaseComponent { nameCell.textContent = this.#preset.settings.name; const tagsCell = document.createElement('td'); + tagsCell.style.width = '70%'; const tagsListContainer = document.createElement('div'); tagsListContainer.classList.add('tag-list'); @@ -52,6 +54,18 @@ export default class PresetTableRow extends BaseComponent { this.#removeAllButton.append(createFontAwesomeIcon('circle-minus')); this.#removeAllButton.title = 'Remove all tags from this preset from the editor'; + if (this.#preset.settings.exclusive) { + this.#applyAllButton.disabled = true; + this.#applyAllButton.title = "You can't add all tags from this preset since it only allows one tag to be active"; + + this.#exclusiveWarning.classList.add('block', 'block--fixed', 'block--warning'); + this.#exclusiveWarning.textContent = ' Multiple tags from this preset present in the editor! If you will click one of the tags here, other tags will be cleared automatically.' + this.#exclusiveWarning.prepend(createFontAwesomeIcon('triangle-exclamation')); + this.#exclusiveWarning.style.display = 'none'; + + tagsCell.append(this.#exclusiveWarning); + } + actionsContainer.append( this.#applyAllButton, this.#removeAllButton, @@ -85,6 +99,30 @@ export default class PresetTableRow extends BaseComponent { const tagName = targetElement.dataset.tagName; const isMissing = targetElement.classList.contains(PresetTableRow.#tagMissingClassName); + if (!tagName) { + return; + } + + // If a user clicks on the tag which was missing, then we have to remove all other active tags that are in this + // preset. But only when clicking on a tag which is missing, just so they will be able to remove any cases where + // multiple tags from exclusive present are active. + if (this.#preset.settings.exclusive && isMissing) { + const tagNamesToRemove = this.#tagsList + .filter( + tagElement => tagElement !== targetElement + && !tagElement.classList.contains(PresetTableRow.#tagMissingClassName) + ) + .map(tagElement => tagElement.dataset.tagName) + .filter(tagName => typeof tagName === 'string'); + + emit(this, EVENT_PRESET_TAG_CHANGE_APPLIED, { + addedTags: new Set([tagName]), + removedTags: new Set(tagNamesToRemove) + }); + + return; + } + emit(this, EVENT_PRESET_TAG_CHANGE_APPLIED, { [isMissing ? 'addedTags' : 'removedTags']: new Set([tagName]) }); @@ -109,11 +147,29 @@ export default class PresetTableRow extends BaseComponent { } updateTags(tags: Set) { + let presentTagsAmount = 0; + for (const tagElement of this.#tagsList) { - tagElement.classList.toggle( + const isTagMissing = tagElement.classList.toggle( PresetTableRow.#tagMissingClassName, !tags.has(tagElement.dataset.tagName || ''), ); + + if (!isTagMissing) { + presentTagsAmount++; + } + } + + if (this.#preset.settings.exclusive) { + const multipleTagsInExclusivePreset = presentTagsAmount > 1; + + this.container.classList.toggle(PresetTableRow.#presetWarningClassName, multipleTagsInExclusivePreset); + + if (multipleTagsInExclusivePreset) { + this.#exclusiveWarning.style.removeProperty('display'); + } else { + this.#exclusiveWarning.style.display = 'none'; + } } } @@ -126,4 +182,5 @@ export default class PresetTableRow extends BaseComponent { } static #tagMissingClassName = 'is-missing'; + static #presetWarningClassName = 'has-warning'; } diff --git a/src/lib/extension/entities/TagEditorPreset.ts b/src/lib/extension/entities/TagEditorPreset.ts index d904d5f..3932862 100644 --- a/src/lib/extension/entities/TagEditorPreset.ts +++ b/src/lib/extension/entities/TagEditorPreset.ts @@ -3,6 +3,7 @@ import StorageEntity from "$lib/extension/base/StorageEntity"; interface TagEditorPresetSettings { name: string; tags: string[]; + exclusive: boolean; } export default class TagEditorPreset extends StorageEntity { @@ -10,6 +11,7 @@ export default class TagEditorPreset extends StorageEntity([]); + let isExclusive = $state(false); $effect(() => { if (presetId === 'new') { @@ -39,6 +41,7 @@ presetName = targetPreset.settings.name; tagsList = [...targetPreset.settings.tags].sort((a, b) => a.localeCompare(b)); + isExclusive = targetPreset.settings.exclusive; }); async function savePreset() { @@ -49,6 +52,7 @@ targetPreset.settings.name = presetName; targetPreset.settings.tags = [...tagsList]; + targetPreset.settings.exclusive = isExclusive; await targetPreset.save(); await goto(`/features/presets/${targetPreset.id}`); @@ -67,6 +71,11 @@ + + + Keep only one tag from this preset active at a time. + +
diff --git a/src/styles/booru-vars.scss b/src/styles/booru-vars.scss index 462d9c5..1f9d5f6 100644 --- a/src/styles/booru-vars.scss +++ b/src/styles/booru-vars.scss @@ -4,6 +4,7 @@ $media-box-color: var(--media-box-color); $padding-small: var(--padding-small); $padding-normal: var(--padding-normal); $padding-large: var(--padding-large); +$block-spacing: var(--block-spacing); // These variables are defined dynamically based on the category of the tag $resolved-tag-background: var(--tag-background); diff --git a/src/styles/content/tag-presets.scss b/src/styles/content/tag-presets.scss index cf24ef6..30450c3 100644 --- a/src/styles/content/tag-presets.scss +++ b/src/styles/content/tag-presets.scss @@ -13,4 +13,11 @@ background: booru-vars.$resolved-tag-color; } } + + .block.block--fixed.block--warning { + margin: { + top: booru-vars.$block-spacing; + bottom: 0; + } + } }