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.
+
+