1
0
mirror of https://github.com/koloml/philomena-tagging-assistant.git synced 2026-05-09 07:12:19 +00:00

Merge pull request #169 from koloml/feature/conditional-presets

Presets: Added option to make presets conditional
This commit is contained in:
2026-04-05 10:45:49 -04:00
committed by GitHub
7 changed files with 75 additions and 6 deletions

View File

@@ -10,6 +10,7 @@
let { preset }: PresetViewProps = $props();
const sortedTagsList = $derived(preset.settings.tags.toSorted((a, b) => a.localeCompare(b)));
const requiredTagsList = $derived(preset.settings.requiredTags.toSorted((a, b) => a.localeCompare(b)));
</script>
<DetailsBlock title="Preset Name">
@@ -24,3 +25,11 @@
be automatically removed from the editor.
</DetailsBlock>
{/if}
{#if preset.settings.conditional}
<DetailsBlock title="Conditional">
This preset will only appear when one of the tags below are present on image.
</DetailsBlock>
<DetailsBlock title="Conditional Tags">
<TagsList tags={requiredTagsList}></TagsList>
</DetailsBlock>
{/if}

View File

@@ -5,11 +5,13 @@ import { EVENT_PRESET_TAG_CHANGE_APPLIED } from "$content/components/events/pres
import { createFontAwesomeIcon } from "$lib/dom-utils";
export default class PresetTableRow extends BaseComponent {
#preset: TagEditorPreset;
readonly #preset: TagEditorPreset;
readonly #applyAllButton = document.createElement('button');
readonly #removeAllButton = document.createElement('button');
readonly #exclusiveWarning = document.createElement('div');
readonly #alternateColorDummy = document.createElement('span');
#tagsList: HTMLElement[] = [];
#applyAllButton = document.createElement('button');
#removeAllButton = document.createElement('button');
#exclusiveWarning = document.createElement('div');
constructor(container: HTMLElement, preset: TagEditorPreset) {
super(container);
@@ -78,6 +80,8 @@ export default class PresetTableRow extends BaseComponent {
tagsCell,
actionsCell,
);
this.#alternateColorDummy.style.display = 'none';
}
protected init() {
@@ -146,6 +150,26 @@ export default class PresetTableRow extends BaseComponent {
});
}
#maybeRefreshVisibilityFromTags(sourceTags: Set<string>) {
if (!this.#preset.settings.conditional || this.#isMatchesConditional(sourceTags)) {
this.container.style.display = '';
this.#alternateColorDummy.remove();
return;
}
this.container.style.display = 'none';
this.container.after(this.#alternateColorDummy);
}
#isMatchesConditional(sourceTags: Set<string>): boolean {
const listOfRequiredTags = this.#preset.settings.requiredTags;
return Boolean(
listOfRequiredTags.length
&& listOfRequiredTags.some(tagName => sourceTags.has(tagName))
);
}
updateTags(tags: Set<string>) {
let presentTagsAmount = 0;
@@ -171,6 +195,8 @@ export default class PresetTableRow extends BaseComponent {
this.#exclusiveWarning.style.display = 'none';
}
}
this.#maybeRefreshVisibilityFromTags(tags);
}
remove() {

View File

@@ -4,6 +4,8 @@ interface TagEditorPresetSettings {
name: string;
tags: string[];
exclusive: boolean;
conditional: boolean;
requiredTags: string[];
}
export default class TagEditorPreset extends StorageEntity<TagEditorPresetSettings> {
@@ -11,7 +13,9 @@ export default class TagEditorPreset extends StorageEntity<TagEditorPresetSettin
super(id, {
name: settings.name || '',
tags: settings.tags || [],
exclusive: settings.exclusive ?? false
exclusive: settings.exclusive ?? false,
conditional: settings.conditional || false,
requiredTags: settings.requiredTags || [],
});
}

View File

@@ -43,6 +43,8 @@ const entitiesExporters: ExportersMap = {
name: entity.settings.name,
tags: entity.settings.tags,
exclusive: entity.settings.exclusive,
conditional: entity.settings.conditional,
requiredTags: entity.settings.requiredTags,
}
}
};

View File

@@ -29,7 +29,15 @@ function validateRequiredString(value: unknown): boolean {
* @param value Value to be checked.
*/
function validateOptionalArray(value: unknown): boolean {
return typeof value === 'undefined' || value === null || Array.isArray(value);
return value === undefined || value === null || Array.isArray(value);
}
/**
* Check if the following value is not set or is a valid boolean.
* @param value Value to be checked.
*/
function validateOptionalBoolean(value: unknown): boolean {
return value === undefined || typeof value === 'boolean';
}
/**
@@ -73,6 +81,9 @@ const entitiesValidators: EntitiesValidationMap = {
!validateRequiredString(importedObject?.id)
|| !validateRequiredString(importedObject?.name)
|| !validateOptionalArray(importedObject?.tags)
|| !validateOptionalBoolean(importedObject?.exclusive)
|| !validateOptionalBoolean(importedObject?.conditional)
|| !validateOptionalArray(importedObject?.requiredTags)
) {
throw new Error('Invalid preset format detected!');
}

View File

@@ -25,6 +25,8 @@
let presetName = $state('');
let tagsList = $state<string[]>([]);
let isExclusive = $state(false);
let isConditional = $state<boolean>(false);
let requiredTags = $state<string[]>([]);
$effect(() => {
if (presetId === 'new') {
@@ -42,6 +44,8 @@
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));
});
async function savePreset() {
@@ -53,6 +57,8 @@
targetPreset.settings.name = presetName;
targetPreset.settings.tags = [...tagsList];
targetPreset.settings.exclusive = isExclusive;
targetPreset.settings.conditional = isConditional;
targetPreset.settings.requiredTags = [...requiredTags];
await targetPreset.save();
await goto(`/features/presets/${targetPreset.id}`);
@@ -76,6 +82,16 @@
Keep only one tag from this preset active at a time.
</CheckboxField>
</FormControl>
<FormControl>
<CheckboxField bind:checked={isConditional}>
Show this preset only when any of specified tags are provided.
</CheckboxField>
</FormControl>
{#if isConditional}
<FormControl label="Required Tags">
<TagsEditor bind:tags={requiredTags}></TagsEditor>
</FormControl>
{/if}
</FormContainer>
<Menu>
<hr>

View File

@@ -3,6 +3,7 @@
.block.tag-presets {
.tag {
cursor: pointer;
user-select: none;
&.is-missing {
opacity: 0.5;