mirror of
https://github.com/koloml/furbooru-tagging-assistant.git
synced 2026-03-24 23:02:58 +00:00
Compare commits
1 Commits
0.7.0
...
feature/ex
| Author | SHA1 | Date | |
|---|---|---|---|
| 83c7608e99 |
@@ -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