2023-06-16 02:36:11 +02:00
|
|
|
<script lang="ts">
|
2023-08-08 13:52:58 +02:00
|
|
|
import EditLayerState from "./EditLayerState";
|
|
|
|
import type { ConfigMeta } from "./configMeta";
|
|
|
|
import { UIEventSource } from "../../Logic/UIEventSource";
|
|
|
|
import SchemaBasedInput from "./SchemaBasedInput.svelte";
|
|
|
|
import SchemaBasedField from "./SchemaBasedField.svelte";
|
|
|
|
import { TrashIcon } from "@babeard/svelte-heroicons/mini";
|
2023-10-25 00:03:51 +02:00
|
|
|
import QuestionPreview from "./QuestionPreview.svelte";
|
|
|
|
import { Utils } from "../../Utils";
|
2023-10-30 13:45:44 +01:00
|
|
|
import SchemaBasedMultiType from "./SchemaBasedMultiType.svelte";
|
2023-11-03 02:04:42 +01:00
|
|
|
import ShowConversionMessage from "./ShowConversionMessage.svelte";
|
2023-06-18 00:44:57 +02:00
|
|
|
|
2023-08-08 13:52:58 +02:00
|
|
|
export let state: EditLayerState;
|
|
|
|
export let schema: ConfigMeta;
|
2023-06-20 01:32:24 +02:00
|
|
|
|
2023-10-25 00:03:51 +02:00
|
|
|
|
2023-08-08 13:52:58 +02:00
|
|
|
let title = schema.path.at(-1);
|
|
|
|
let singular = title;
|
2023-08-23 11:11:53 +02:00
|
|
|
if (title?.endsWith("s")) {
|
2023-08-08 13:52:58 +02:00
|
|
|
singular = title.slice(0, title.length - 1);
|
|
|
|
}
|
|
|
|
let article = "a";
|
2023-08-23 11:11:53 +02:00
|
|
|
if (singular?.match(/^[aeoui]/)) {
|
2023-08-08 13:52:58 +02:00
|
|
|
article = "an";
|
|
|
|
}
|
|
|
|
export let path: (string | number)[] = [];
|
2023-08-23 11:11:53 +02:00
|
|
|
const isTagRenderingBlock = path.length === 1 && path[0] === "tagRenderings";
|
2023-06-18 00:44:57 +02:00
|
|
|
|
2023-10-25 00:03:51 +02:00
|
|
|
if (isTagRenderingBlock) {
|
|
|
|
schema = { ...schema };
|
|
|
|
schema.description = undefined;
|
|
|
|
}
|
|
|
|
|
2023-10-06 23:56:50 +02:00
|
|
|
const subparts: ConfigMeta = state.getSchemaStartingWith(schema.path)
|
|
|
|
.filter(part => part.path.length - 1 === schema.path.length);
|
2023-08-08 13:52:58 +02:00
|
|
|
/**
|
|
|
|
* Store the _indices_
|
|
|
|
*/
|
|
|
|
export let values: UIEventSource<number[]> = new UIEventSource<number[]>([]);
|
2023-06-18 00:44:57 +02:00
|
|
|
|
2023-08-08 13:52:58 +02:00
|
|
|
const currentValue = <[]>state.getCurrentValueFor(path);
|
|
|
|
if (currentValue) {
|
|
|
|
if (!Array.isArray(currentValue)) {
|
|
|
|
console.error("SchemaBaseArray for path", path, "expected an array as initial value, but got a", typeof currentValue, currentValue);
|
|
|
|
} else {
|
|
|
|
values.setData(currentValue.map((_, i) => i));
|
2023-06-23 16:14:43 +02:00
|
|
|
}
|
2023-08-08 13:52:58 +02:00
|
|
|
}
|
|
|
|
let createdItems = values.data.length;
|
2023-11-03 02:04:42 +01:00
|
|
|
let messages = state.messagesFor(path)
|
2023-06-23 16:14:43 +02:00
|
|
|
|
2023-06-18 00:44:57 +02:00
|
|
|
|
2023-08-23 11:11:53 +02:00
|
|
|
function createItem(valueToSet?: any) {
|
2023-08-08 13:52:58 +02:00
|
|
|
values.data.push(createdItems);
|
2023-08-23 11:11:53 +02:00
|
|
|
if (valueToSet) {
|
2023-11-05 12:05:00 +01:00
|
|
|
state.getStoreFor([...path, createdItems]).setData(valueToSet);
|
2023-08-23 11:11:53 +02:00
|
|
|
}
|
2023-08-08 13:52:58 +02:00
|
|
|
createdItems++;
|
|
|
|
values.ping();
|
2023-11-05 12:05:00 +01:00
|
|
|
|
|
|
|
if(isTagRenderingBlock){
|
|
|
|
if(typeof valueToSet === "string"){
|
|
|
|
// THis is very broken state.highlightedItem.setData({path: [...path, createdItems], schema})
|
|
|
|
}else{
|
|
|
|
state.highlightedItem.setData({path: [...path, createdItems], schema})
|
|
|
|
}
|
|
|
|
}
|
2023-08-08 13:52:58 +02:00
|
|
|
}
|
2023-06-20 01:32:24 +02:00
|
|
|
|
2023-08-08 13:52:58 +02:00
|
|
|
function fusePath(i: number, subpartPath: string[]): (string | number)[] {
|
|
|
|
const newPath = [...path, i];
|
|
|
|
const toAdd = [...subpartPath];
|
|
|
|
for (const part of path) {
|
|
|
|
if (toAdd[0] === part) {
|
|
|
|
toAdd.splice(0, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newPath.push(...toAdd);
|
|
|
|
return newPath;
|
|
|
|
}
|
2023-06-16 02:36:11 +02:00
|
|
|
|
2023-11-05 12:05:00 +01:00
|
|
|
function del(i) {
|
|
|
|
const index = i;
|
|
|
|
console.log("Deleting", index);
|
2023-10-11 04:16:52 +02:00
|
|
|
values.data.splice(index, 1);
|
2023-10-25 00:03:51 +02:00
|
|
|
values.ping();
|
|
|
|
|
2023-10-11 04:16:52 +02:00
|
|
|
const store = <UIEventSource<[]>>state.getStoreFor(path);
|
2023-10-25 00:03:51 +02:00
|
|
|
store.data.splice(index, 1);
|
|
|
|
store.setData(Utils.NoNull(store.data));
|
|
|
|
state.configuration.ping();
|
|
|
|
}
|
|
|
|
|
|
|
|
function swap(indexA, indexB) {
|
|
|
|
const valueA = values.data[indexA];
|
|
|
|
const valueB = values.data[indexB];
|
|
|
|
|
|
|
|
values.data[indexA] = valueB;
|
|
|
|
values.data[indexB] = valueA;
|
2023-08-08 13:52:58 +02:00
|
|
|
values.ping();
|
2023-10-25 00:03:51 +02:00
|
|
|
|
|
|
|
const store = <UIEventSource<[]>>state.getStoreFor(path);
|
|
|
|
const svalueA = store.data[indexA];
|
|
|
|
const svalueB = store.data[indexB];
|
|
|
|
store.data[indexA] = svalueB;
|
|
|
|
store.data[indexB] = svalueA;
|
|
|
|
store.ping();
|
|
|
|
state.configuration.ping();
|
|
|
|
}
|
|
|
|
|
|
|
|
function moveTo(currentIndex, targetIndex) {
|
|
|
|
const direction = currentIndex > targetIndex ? -1 : +1;
|
|
|
|
do {
|
|
|
|
swap(currentIndex, currentIndex + direction);
|
|
|
|
currentIndex = currentIndex + direction;
|
|
|
|
} while (currentIndex !== targetIndex);
|
2023-08-08 13:52:58 +02:00
|
|
|
}
|
2023-06-18 00:44:57 +02:00
|
|
|
|
2023-10-30 13:45:44 +01:00
|
|
|
function schemaForMultitype() {
|
|
|
|
const sch = {...schema}
|
|
|
|
sch.hints.typehint = undefined
|
|
|
|
return sch
|
|
|
|
}
|
|
|
|
|
2023-10-25 00:03:51 +02:00
|
|
|
|
2023-08-08 13:52:58 +02:00
|
|
|
</script>
|
2023-06-18 00:44:57 +02:00
|
|
|
<div class="pl-2">
|
2023-08-08 13:52:58 +02:00
|
|
|
<h3>{schema.path.at(-1)}</h3>
|
2023-06-18 00:44:57 +02:00
|
|
|
|
2023-08-08 13:52:58 +02:00
|
|
|
{#if subparts.length > 0}
|
2023-06-20 01:32:24 +02:00
|
|
|
<span class="subtle">
|
|
|
|
{schema.description}
|
|
|
|
</span>
|
2023-08-08 13:52:58 +02:00
|
|
|
{/if}
|
2023-06-18 00:44:57 +02:00
|
|
|
|
2023-08-08 13:52:58 +02:00
|
|
|
{#if $values.length === 0}
|
|
|
|
No values are defined
|
2023-11-03 02:04:42 +01:00
|
|
|
{#if $messages.length > 0}
|
|
|
|
{#each $messages as message}
|
|
|
|
<ShowConversionMessage {message}/>
|
|
|
|
{/each}
|
|
|
|
{/if}
|
2023-08-08 13:52:58 +02:00
|
|
|
{:else if subparts.length === 0}
|
|
|
|
<!-- We need an array of values, so we use the typehint of the _parent_ element as field -->
|
2023-11-05 12:05:00 +01:00
|
|
|
{#each $values as value, i (value)}
|
2023-08-08 13:52:58 +02:00
|
|
|
<div class="flex w-full">
|
|
|
|
<SchemaBasedField {state} {schema} path={[...path, value]} />
|
|
|
|
<button class="border-black border rounded-full p-1 w-fit h-fit"
|
2023-11-05 12:05:00 +01:00
|
|
|
on:click={() => {del(i)}}>
|
2023-08-08 13:52:58 +02:00
|
|
|
<TrashIcon class="w-4 h-4" />
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
{/each}
|
|
|
|
{:else}
|
2023-10-25 00:03:51 +02:00
|
|
|
{#each $values as value, i (value)}
|
2023-08-23 11:11:53 +02:00
|
|
|
|
|
|
|
{#if !isTagRenderingBlock}
|
|
|
|
<div class="flex justify-between items-center">
|
|
|
|
<h3 class="m-0">{singular} {value}</h3>
|
|
|
|
<button class="border-black border rounded-full p-1 w-fit h-fit"
|
2023-11-05 12:05:00 +01:00
|
|
|
on:click={() => {del(i)}}>
|
2023-08-23 11:11:53 +02:00
|
|
|
<TrashIcon class="w-4 h-4" />
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
{/if}
|
2023-08-08 13:52:58 +02:00
|
|
|
<div class="border border-black">
|
2023-08-23 11:11:53 +02:00
|
|
|
{#if isTagRenderingBlock}
|
2023-10-25 00:03:51 +02:00
|
|
|
<QuestionPreview {state} path={[...path, value]} {schema}>
|
|
|
|
<button on:click={() => {del(i)}}>
|
2023-08-23 11:11:53 +02:00
|
|
|
<TrashIcon class="w-4 h-4" />
|
2023-10-25 00:03:51 +02:00
|
|
|
Delete this question
|
2023-08-23 11:11:53 +02:00
|
|
|
</button>
|
2023-10-25 00:03:51 +02:00
|
|
|
|
|
|
|
{#if i > 0}
|
|
|
|
<button on:click={() => {moveTo(i, 0)}}>
|
|
|
|
Move to front
|
|
|
|
</button>
|
|
|
|
|
|
|
|
<button on:click={() => {swap(i, i-1)}}>
|
|
|
|
Move up
|
|
|
|
</button>
|
|
|
|
{/if}
|
|
|
|
{#if i + 1 < $values.length}
|
|
|
|
<button on:click={() => {swap(i, i+1)}}>
|
|
|
|
Move down
|
|
|
|
</button>
|
|
|
|
<button on:click={() => {moveTo(i, $values.length-1)}}>
|
|
|
|
Move to back
|
|
|
|
</button>
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
</QuestionPreview>
|
2023-10-30 13:45:44 +01:00
|
|
|
{:else if schema.hints.types}
|
|
|
|
<SchemaBasedMultiType {state} path={fusePath(value, [])} schema={schemaForMultitype()}/>
|
2023-08-08 13:52:58 +02:00
|
|
|
{:else}
|
|
|
|
{#each subparts as subpart}
|
|
|
|
<SchemaBasedInput {state} path={fusePath(value, subpart.path)} schema={subpart} />
|
|
|
|
{/each}
|
|
|
|
{/if}
|
|
|
|
</div>
|
|
|
|
{/each}
|
|
|
|
{/if}
|
2023-08-23 11:11:53 +02:00
|
|
|
<div class="flex">
|
|
|
|
<button on:click={() => createItem()}>Add {article} {singular}</button>
|
|
|
|
{#if path.length === 1 && path[0] === "tagRenderings"}
|
2023-09-15 01:16:33 +02:00
|
|
|
<button on:click={() => {createItem("images");}}>Add a builtin tagRendering</button>
|
2023-08-23 11:11:53 +02:00
|
|
|
{/if}
|
|
|
|
<slot name="extra-button" />
|
|
|
|
</div>
|
2023-06-18 00:44:57 +02:00
|
|
|
</div>
|