mirror of
https://github.com/koloml/furbooru-tagging-assistant.git
synced 2026-03-24 23:02:58 +00:00
Presets: Added flag for making it "exclusive"
This will make it so only one tag will be active from marked preset. This can be useful for some tags that cannot be together in the editor, for example, rating tags.
This commit is contained in:
@@ -18,3 +18,9 @@
|
||||
<DetailsBlock title="Tags">
|
||||
<TagsList tags={sortedTagsList}></TagsList>
|
||||
</DetailsBlock>
|
||||
{#if preset.settings.exclusive}
|
||||
<DetailsBlock title="Exclusivity">
|
||||
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.
|
||||
</DetailsBlock>
|
||||
{/if}
|
||||
|
||||
@@ -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<string>) {
|
||||
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';
|
||||
}
|
||||
|
||||
@@ -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<TagEditorPresetSettings> {
|
||||
@@ -10,6 +11,7 @@ export default class TagEditorPreset extends StorageEntity<TagEditorPresetSettin
|
||||
super(id, {
|
||||
name: settings.name || '',
|
||||
tags: settings.tags || [],
|
||||
exclusive: settings.exclusive ?? false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ const entitiesExporters: ExportersMap = {
|
||||
id: entity.id,
|
||||
name: entity.settings.name,
|
||||
tags: entity.settings.tags,
|
||||
exclusive: entity.settings.exclusive,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import FormControl from "$components/ui/forms/FormControl.svelte";
|
||||
import TextField from "$components/ui/forms/TextField.svelte";
|
||||
import TagsEditor from "$components/tags/TagsEditor.svelte";
|
||||
import CheckboxField from "$components/ui/forms/CheckboxField.svelte";
|
||||
|
||||
let presetId = $derived(page.params.id);
|
||||
|
||||
@@ -23,6 +24,7 @@
|
||||
|
||||
let presetName = $state('');
|
||||
let tagsList = $state<string[]>([]);
|
||||
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 @@
|
||||
<FormControl label="Tags">
|
||||
<TagsEditor bind:tags={tagsList}></TagsEditor>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<CheckboxField bind:checked={isExclusive}>
|
||||
Keep only one tag from this preset active at a time.
|
||||
</CheckboxField>
|
||||
</FormControl>
|
||||
</FormContainer>
|
||||
<Menu>
|
||||
<hr>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -13,4 +13,11 @@
|
||||
background: booru-vars.$resolved-tag-color;
|
||||
}
|
||||
}
|
||||
|
||||
.block.block--fixed.block--warning {
|
||||
margin: {
|
||||
top: booru-vars.$block-spacing;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user