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 4e6a453..0de7860 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'); #alternateColorDummy = document.createElement('span'); constructor(container: HTMLElement, preset: TagEditorPreset) { @@ -33,6 +34,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'); @@ -53,6 +55,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, @@ -88,6 +102,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]) }); @@ -132,11 +170,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'; + } } this.#maybeRefreshVisibilityFromTags(tags); @@ -151,4 +207,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 681fc61..360bf9a 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; conditional: boolean; requiredTags: string[]; } @@ -12,6 +13,7 @@ export default class TagEditorPreset extends StorageEntity([]); + let isExclusive = $state(false); let isConditional = $state(false); let requiredTags = $state([]); @@ -42,6 +43,7 @@ presetName = targetPreset.settings.name; tagsList = [...targetPreset.settings.tags].sort((a, b) => a.localeCompare(b)); + isExclusive = targetPreset.settings.exclusive; isConditional = targetPreset.settings.conditional; requiredTags = [...targetPreset.settings.requiredTags].sort((a, b) => a.localeCompare(b)); }); @@ -54,6 +56,7 @@ targetPreset.settings.name = presetName; targetPreset.settings.tags = [...tagsList]; + targetPreset.settings.exclusive = isExclusive; targetPreset.settings.conditional = isConditional; targetPreset.settings.requiredTags = [...requiredTags]; @@ -74,6 +77,11 @@ + + + Keep only one tag from this preset active at a time. + + Show this preset only when specified tags are provided. 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 9b7bdcc..db66a8e 100644 --- a/src/styles/content/tag-presets.scss +++ b/src/styles/content/tag-presets.scss @@ -14,4 +14,11 @@ background: booru-vars.$resolved-tag-color; } } + + .block.block--fixed.block--warning { + margin: { + top: booru-vars.$block-spacing; + bottom: 0; + } + } }