2023-06-22 15:07:14 +02:00
|
|
|
<script lang="ts">/**
|
|
|
|
* Allows to create `and` and `or` expressions graphically
|
|
|
|
*/
|
|
|
|
|
|
|
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
|
|
|
import type {TagConfigJson} from "../../Models/ThemeConfig/Json/TagConfigJson";
|
|
|
|
import BasicTagInput from "./TagInput/BasicTagInput.svelte";
|
|
|
|
import TagInput from "./TagInput/TagInput.svelte";
|
2023-06-23 16:14:43 +02:00
|
|
|
import {TrashIcon} from "@babeard/svelte-heroicons/mini";
|
2023-06-22 15:07:14 +02:00
|
|
|
|
|
|
|
export let tag: UIEventSource<TagConfigJson>
|
|
|
|
let mode: "and" | "or" = "and"
|
|
|
|
|
|
|
|
let basicTags: UIEventSource<UIEventSource<string>[]> = new UIEventSource([])
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sub-expressions
|
|
|
|
*/
|
|
|
|
let expressions: UIEventSource<UIEventSource<TagConfigJson>[]> = new UIEventSource([])
|
|
|
|
export let uploadableOnly: boolean
|
|
|
|
export let overpassSupportNeeded: boolean
|
|
|
|
|
|
|
|
function update(_) {
|
|
|
|
let config: TagConfigJson = <any>{}
|
|
|
|
if (!mode) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const tags = []
|
|
|
|
|
|
|
|
const subpartSources = (<UIEventSource<string | TagConfigJson>[]>basicTags.data).concat(expressions.data)
|
|
|
|
for (const src of subpartSources) {
|
|
|
|
const t = src.data
|
|
|
|
if (!t) {
|
|
|
|
// We indicate upstream that this value is invalid
|
|
|
|
tag.setData(undefined)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
tags.push(t)
|
|
|
|
}
|
2023-06-23 16:14:43 +02:00
|
|
|
if (tags.length === 1) {
|
|
|
|
tag.setData(tags[0])
|
|
|
|
} else {
|
|
|
|
config[mode] = tags
|
|
|
|
tag.setData(config)
|
|
|
|
}
|
2023-06-22 15:07:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-23 16:14:43 +02:00
|
|
|
function addBasicTag(value?: string) {
|
|
|
|
const src = new UIEventSource(value)
|
2023-06-22 15:07:14 +02:00
|
|
|
basicTags.data.push(src);
|
|
|
|
basicTags.ping()
|
|
|
|
src.addCallbackAndRunD(_ => update(_))
|
|
|
|
}
|
|
|
|
|
2023-06-23 16:14:43 +02:00
|
|
|
function removeTag(basicTag: UIEventSource<any>) {
|
|
|
|
const index = basicTags.data.indexOf(basicTag)
|
|
|
|
console.log("Removing", index, basicTag)
|
|
|
|
if (index >= 0) {
|
|
|
|
basicTag.setData(undefined)
|
|
|
|
basicTags.data.splice(index, 1)
|
|
|
|
basicTags.ping()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function removeExpression(expr: UIEventSource<any>) {
|
|
|
|
const index = expressions.data.indexOf(expr)
|
|
|
|
if (index >= 0) {
|
|
|
|
expr.setData(undefined)
|
|
|
|
expressions.data.splice(index, 1)
|
|
|
|
expressions.ping()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function addExpression(expr?: TagConfigJson) {
|
|
|
|
const src = new UIEventSource(expr)
|
2023-06-22 15:07:14 +02:00
|
|
|
expressions.data.push(src);
|
|
|
|
expressions.ping()
|
|
|
|
src.addCallbackAndRunD(_ => update(_))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$: update(mode)
|
2023-06-23 16:14:43 +02:00
|
|
|
expressions.addCallback(_ => update(_))
|
|
|
|
basicTags.addCallback(_ => update(_))
|
2023-06-22 15:07:14 +02:00
|
|
|
|
2023-06-23 16:14:43 +02:00
|
|
|
let initialTag: TagConfigJson = tag.data
|
|
|
|
|
|
|
|
function initWith(initialTag: TagConfigJson) {
|
|
|
|
if (typeof initialTag === "string") {
|
|
|
|
addBasicTag(initialTag)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
mode = <"or" | "and">Object.keys(initialTag)[0]
|
|
|
|
const subExprs = (<TagConfigJson[]>initialTag[mode])
|
|
|
|
if (subExprs.length == 0) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (subExprs.length == 1) {
|
|
|
|
initWith(subExprs[0])
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (const subExpr of subExprs) {
|
|
|
|
if (typeof subExpr === "string") {
|
|
|
|
addBasicTag(subExpr)
|
|
|
|
} else {
|
|
|
|
addExpression(subExpr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!initialTag) {
|
|
|
|
addBasicTag()
|
|
|
|
} else {
|
|
|
|
initWith(initialTag)
|
|
|
|
}
|
2023-06-22 15:07:14 +02:00
|
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
|
|
|
<select bind:value={mode}>
|
|
|
|
<option value="and">and</option>
|
|
|
|
{#if !uploadableOnly}
|
|
|
|
<option value="or">or</option>
|
|
|
|
{/if}
|
|
|
|
</select>
|
|
|
|
<div class="border-l-4 border-black flex flex-col ml-1 pl-1">
|
2023-06-23 16:14:43 +02:00
|
|
|
{#each $basicTags as basicTag (basicTag)}
|
|
|
|
<div class="flex">
|
|
|
|
<BasicTagInput {overpassSupportNeeded} {uploadableOnly} tag={basicTag}/>
|
|
|
|
<button class="border border-black rounded-full w-fit h-fit p-0" on:click={() => removeTag(basicTag)}>
|
|
|
|
<TrashIcon class="w-4 h-4 p-1"/>
|
|
|
|
</button>
|
|
|
|
</div>
|
2023-06-22 15:07:14 +02:00
|
|
|
{/each}
|
|
|
|
{#each $expressions as expression}
|
2023-06-23 16:14:43 +02:00
|
|
|
<TagInput {overpassSupportNeeded} {uploadableOnly} tag={expression}>
|
|
|
|
<button slot="delete" on:click={() => removeExpression(expression)}>
|
|
|
|
<TrashIcon class="w-4 h-4 p-1"/>
|
|
|
|
Delete subexpression
|
|
|
|
</button>
|
|
|
|
</TagInput>
|
2023-06-22 15:07:14 +02:00
|
|
|
{/each}
|
|
|
|
<div class="flex">
|
2023-06-23 16:14:43 +02:00
|
|
|
<button class="w-fit" on:click={() => addBasicTag()}>
|
2023-06-22 15:07:14 +02:00
|
|
|
Add a tag
|
|
|
|
</button>
|
2023-06-23 16:14:43 +02:00
|
|
|
<button class="w-fit" on:click={() => addExpression()}>
|
2023-06-22 15:07:14 +02:00
|
|
|
Add an expression
|
|
|
|
</button>
|
2023-06-23 16:14:43 +02:00
|
|
|
<slot name="delete"/>
|
2023-06-22 15:07:14 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|