MapComplete/src/UI/Studio/SchemaBasedArray.svelte

184 lines
5.4 KiB
Svelte
Raw Normal View History

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";
import QuestionPreview from "./QuestionPreview.svelte";
2023-10-30 13:45:44 +01:00
import SchemaBasedMultiType from "./SchemaBasedMultiType.svelte";
import ShowConversionMessage from "./ShowConversionMessage.svelte";
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-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";
if (isTagRenderingBlock) {
schema = { ...schema };
schema.description = undefined;
}
const subparts: ConfigMeta = state.getSchemaStartingWith(schema.path)
.filter(part => part.path.length - 1 === schema.path.length);
let messages = state.messagesFor(path)
const currentValue : UIEventSource<any[]> = state.getStoreFor(path);
if(currentValue.data === undefined){
currentValue.setData([])
}
2023-08-23 11:11:53 +02:00
function createItem(valueToSet?: any) {
if(currentValue.data === undefined){
currentValue.setData([])
2023-08-23 11:11:53 +02:00
}
currentValue.data.push(valueToSet)
currentValue.ping()
2023-11-05 12:05:00 +01:00
if(isTagRenderingBlock){
state.highlightedItem.setData({path: [...path, currentValue.data.length - 1], schema})
2023-11-05 12:05:00 +01:00
}
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);
}else{
break
2023-08-08 13:52:58 +02:00
}
}
newPath.push(...toAdd);
console.log({newPath})
2023-08-08 13:52:58 +02:00
return newPath;
}
2023-06-16 02:36:11 +02:00
2023-10-30 13:45:44 +01:00
function schemaForMultitype() {
const sch = {...schema}
sch.hints.typehint = undefined
return sch
}
function del(i: number){
currentValue.data.splice(i, 1)
currentValue.ping()
}
function swap(i: number, j: number) {
const x = currentValue.data[i]
currentValue.data[i] = currentValue.data[j]
currentValue.data[j] = x
currentValue.ping()
}
function moveTo(source: number, target: number){
const x = currentValue.data[source]
currentValue.data.splice(source, 1)
currentValue.data.splice(target, 0, x)
currentValue.ping()
}
2023-10-30 13:45:44 +01:00
2023-08-08 13:52:58 +02:00
</script>
<div class="pl-2">
2023-08-08 13:52:58 +02:00
<h3>{schema.path.at(-1)}</h3>
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}
{#if $currentValue === undefined}
No array defined
{:else if $currentValue.length === 0}
2023-08-08 13:52:58 +02:00
No values are defined
{#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 -->
{#each $currentValue as value, i}
2023-08-08 13:52:58 +02:00
<div class="flex w-full">
<SchemaBasedField {state} {schema} path={[...path, i]} />
2023-08-08 13:52:58 +02:00
<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}
{#each $currentValue as value, i}
2023-08-23 11:11:53 +02:00
{#if !isTagRenderingBlock}
<div class="flex justify-between items-center">
<h3 class="m-0">{singular} {i}</h3>
2023-08-23 11:11:53 +02:00
<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}
<QuestionPreview {state} path={[...path, i]} {schema}>
<button on:click={() => {del(i)}}>
2023-08-23 11:11:53 +02:00
<TrashIcon class="w-4 h-4" />
Delete this question
2023-08-23 11:11:53 +02:00
</button>
{#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 < $currentValue.length}
<button on:click={() => {swap(i, i+1)}}>
Move down
</button>
<button on:click={() => {moveTo(i, $currentValue.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(i, [])} schema={schemaForMultitype()}/>
2023-08-08 13:52:58 +02:00
{:else}
{#each subparts as subpart}
<SchemaBasedInput {state} path={fusePath(i, subpart.path)} schema={subpart} />
2023-08-08 13:52:58 +02:00
{/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>
</div>