2023-06-16 02:36:11 +02:00
|
|
|
<script lang="ts">
|
2023-10-25 00:03:51 +02:00
|
|
|
import type { HighlightedTagRendering } from "./EditLayerState";
|
2023-10-26 13:58:45 +02:00
|
|
|
import EditLayerState from "./EditLayerState";
|
2023-09-15 01:16:33 +02:00
|
|
|
import layerSchemaRaw from "../../assets/schemas/layerconfigmeta.json";
|
|
|
|
import Region from "./Region.svelte";
|
|
|
|
import TabbedGroup from "../Base/TabbedGroup.svelte";
|
2023-10-25 00:03:51 +02:00
|
|
|
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
2023-09-15 01:16:33 +02:00
|
|
|
import type { ConfigMeta } from "./configMeta";
|
|
|
|
import { Utils } from "../../Utils";
|
2023-10-16 15:06:50 +02:00
|
|
|
import type { ConversionMessage } from "../../Models/ThemeConfig/Conversion/Conversion";
|
2023-10-17 16:06:58 +02:00
|
|
|
import ErrorIndicatorForRegion from "./ErrorIndicatorForRegion.svelte";
|
2023-10-21 09:35:54 +02:00
|
|
|
import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid";
|
2023-10-24 22:01:10 +02:00
|
|
|
import SchemaBasedInput from "./SchemaBasedInput.svelte";
|
2023-10-25 00:03:51 +02:00
|
|
|
import FloatOver from "../Base/FloatOver.svelte";
|
|
|
|
import TagRenderingInput from "./TagRenderingInput.svelte";
|
|
|
|
import FromHtml from "../Base/FromHtml.svelte";
|
|
|
|
import AllTagsPanel from "../Popup/AllTagsPanel.svelte";
|
|
|
|
import QuestionPreview from "./QuestionPreview.svelte";
|
2023-10-26 13:58:45 +02:00
|
|
|
import ShowConversionMessages from "./ShowConversionMessages.svelte";
|
2023-06-16 02:36:11 +02:00
|
|
|
|
2023-09-15 01:16:33 +02:00
|
|
|
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw;
|
2023-10-13 18:46:56 +02:00
|
|
|
|
2023-10-16 15:06:50 +02:00
|
|
|
export let state: EditLayerState;
|
2023-11-02 04:35:32 +01:00
|
|
|
let messages = state.messages;
|
|
|
|
let hasErrors = messages.mapD((m: ConversionMessage[]) => m.filter(m => m.level === "error").length);
|
2023-09-15 01:16:33 +02:00
|
|
|
const configuration = state.configuration;
|
2023-11-05 12:05:00 +01:00
|
|
|
|
2023-09-15 01:16:33 +02:00
|
|
|
const allNames = Utils.Dedup(layerSchema.map(meta => meta.hints.group));
|
2023-06-16 02:36:11 +02:00
|
|
|
|
2023-09-15 01:16:33 +02:00
|
|
|
const perRegion: Record<string, ConfigMeta[]> = {};
|
|
|
|
for (const region of allNames) {
|
|
|
|
perRegion[region] = layerSchema.filter(meta => meta.hints.group === region);
|
|
|
|
}
|
2023-06-16 02:36:11 +02:00
|
|
|
|
2023-10-24 22:01:10 +02:00
|
|
|
|
2023-11-02 04:35:32 +01:00
|
|
|
let title: Store<string> = state.getStoreFor(["id"]);
|
2023-10-17 16:06:58 +02:00
|
|
|
const wl = window.location;
|
|
|
|
const baseUrl = wl.protocol + "//" + wl.host + "/theme.html?userlayout=";
|
|
|
|
|
|
|
|
function firstPathsFor(...regionNames: string[]): Set<string> {
|
|
|
|
const pathNames = new Set<string>();
|
|
|
|
for (const regionName of regionNames) {
|
2023-10-24 22:01:10 +02:00
|
|
|
const region: ConfigMeta[] = perRegion[regionName];
|
2023-10-17 16:06:58 +02:00
|
|
|
for (const configMeta of region) {
|
2023-10-24 22:01:10 +02:00
|
|
|
pathNames.add(configMeta.path[0]);
|
2023-10-17 16:06:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return pathNames;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-10-25 00:03:51 +02:00
|
|
|
function configForRequiredField(id: string): ConfigMeta {
|
|
|
|
let config = layerSchema.find(config => config.path.length === 1 && config.path[0] === id);
|
|
|
|
config = Utils.Clone(config);
|
|
|
|
config.required = true;
|
|
|
|
config.hints.ifunset = undefined;
|
|
|
|
return config;
|
2023-10-24 22:01:10 +02:00
|
|
|
}
|
|
|
|
|
2023-11-05 12:05:00 +01:00
|
|
|
let requiredFields = ["id", "name", "description", "source"];
|
2023-10-24 22:01:10 +02:00
|
|
|
let currentlyMissing = state.configuration.map(config => {
|
2023-11-05 12:05:00 +01:00
|
|
|
if (!config) {
|
|
|
|
return [];
|
2023-11-02 04:35:32 +01:00
|
|
|
}
|
2023-10-24 22:01:10 +02:00
|
|
|
const missing = [];
|
|
|
|
for (const requiredField of requiredFields) {
|
|
|
|
if (!config[requiredField]) {
|
|
|
|
missing.push(requiredField);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return missing;
|
|
|
|
});
|
|
|
|
|
2023-10-25 00:03:51 +02:00
|
|
|
let highlightedItem: UIEventSource<HighlightedTagRendering> = state.highlightedItem;
|
2023-06-16 02:36:11 +02:00
|
|
|
</script>
|
2023-11-05 12:05:00 +01:00
|
|
|
<div class="h-screen flex flex-col">
|
2023-06-16 02:36:11 +02:00
|
|
|
|
2023-11-05 12:05:00 +01:00
|
|
|
<div class="w-full flex justify-between my-2">
|
|
|
|
<slot />
|
|
|
|
{#if $title === undefined}
|
|
|
|
<h3>Creating a new layer</h3>
|
|
|
|
{:else}
|
|
|
|
<h3>Editing layer {$title}</h3>
|
|
|
|
{/if}
|
|
|
|
{#if $currentlyMissing.length > 0}
|
|
|
|
<div class="w-16"/> <!-- Empty div, simply hide this -->
|
|
|
|
{:else if $hasErrors > 0}
|
|
|
|
<div class="alert">{$hasErrors} errors detected</div>
|
|
|
|
{:else}
|
|
|
|
<a class="primary button" href={baseUrl+state.server.layerUrl(title.data)} target="_blank" rel="noopener">
|
|
|
|
Try it out
|
|
|
|
<ChevronRightIcon class="h-6 w-6 shrink-0" />
|
|
|
|
</a>
|
|
|
|
{/if}
|
|
|
|
</div>
|
2023-10-24 22:01:10 +02:00
|
|
|
|
2023-11-05 12:05:00 +01:00
|
|
|
{#if $currentlyMissing.length > 0}
|
2023-10-25 00:03:51 +02:00
|
|
|
|
2023-11-05 12:05:00 +01:00
|
|
|
{#each requiredFields as required}
|
|
|
|
<SchemaBasedInput {state}
|
|
|
|
schema={configForRequiredField(required)}
|
|
|
|
path={[required]} />
|
|
|
|
{/each}
|
|
|
|
{:else}
|
2023-10-25 00:03:51 +02:00
|
|
|
<div class="m4 h-full overflow-y-auto">
|
|
|
|
<TabbedGroup>
|
|
|
|
<div slot="title0" class="flex">General properties
|
|
|
|
<ErrorIndicatorForRegion firstPaths={firstPathsFor("Basic")} {state} />
|
|
|
|
</div>
|
|
|
|
<div class="flex flex-col" slot="content0">
|
|
|
|
<Region {state} configs={perRegion["Basic"]} />
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div slot="title1" class="flex">Information panel (questions and answers)
|
|
|
|
<ErrorIndicatorForRegion firstPaths={firstPathsFor("title","tagrenderings","editing")} {state} />
|
|
|
|
</div>
|
|
|
|
<div slot="content1">
|
|
|
|
<QuestionPreview path={["title"]} {state} schema={perRegion["title"][0]}></QuestionPreview>
|
|
|
|
<Region configs={perRegion["tagrenderings"]} {state} title="Popup contents" />
|
|
|
|
<Region configs={perRegion["editing"]} {state} title="Other editing elements" />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div slot="title2">
|
|
|
|
Creating a new point
|
2023-11-03 02:04:42 +01:00
|
|
|
<ErrorIndicatorForRegion firstPaths={firstPathsFor("presets")} {state} />
|
2023-10-25 00:03:51 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div slot="content2">
|
|
|
|
<Region {state} configs={perRegion["presets"]} />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div slot="title3" class="flex">Rendering on the map
|
|
|
|
<ErrorIndicatorForRegion firstPaths={firstPathsFor("linerendering","pointrendering")} {state} />
|
2023-10-24 22:01:10 +02:00
|
|
|
</div>
|
2023-10-25 00:03:51 +02:00
|
|
|
<div slot="content3">
|
|
|
|
<Region configs={perRegion["linerendering"]} {state} />
|
|
|
|
<Region configs={perRegion["pointrendering"]} {state} />
|
2023-10-24 22:01:10 +02:00
|
|
|
</div>
|
2023-06-23 16:14:43 +02:00
|
|
|
|
2023-10-25 00:03:51 +02:00
|
|
|
<div slot="title4" class="flex">Advanced functionality
|
|
|
|
<ErrorIndicatorForRegion firstPaths={firstPathsFor("advanced","expert")} {state} />
|
|
|
|
</div>
|
|
|
|
<div slot="content4">
|
|
|
|
<Region configs={perRegion["advanced"]} {state} />
|
|
|
|
<Region configs={perRegion["expert"]} {state} />
|
|
|
|
</div>
|
|
|
|
<div slot="title5">Configuration file</div>
|
|
|
|
<div slot="content5">
|
|
|
|
<div>
|
|
|
|
Below, you'll find the raw configuration file in `.json`-format.
|
|
|
|
This is mostly for debugging purposes
|
|
|
|
</div>
|
|
|
|
<div class="literal-code">
|
|
|
|
<FromHtml src={JSON.stringify($configuration, null, " ").replaceAll("\n","</br>")} />
|
|
|
|
</div>
|
|
|
|
|
2023-11-05 12:05:00 +01:00
|
|
|
<ShowConversionMessages messages={$messages} />
|
2023-10-26 13:58:45 +02:00
|
|
|
<div>
|
2023-11-05 12:05:00 +01:00
|
|
|
The testobject (which is used to render the questions in the 'information panel' item has the following
|
|
|
|
tags:
|
2023-10-26 13:58:45 +02:00
|
|
|
</div>
|
2023-11-05 12:05:00 +01:00
|
|
|
|
2023-10-25 00:03:51 +02:00
|
|
|
<AllTagsPanel tags={state.testTags}></AllTagsPanel>
|
|
|
|
</div>
|
|
|
|
</TabbedGroup>
|
|
|
|
</div>
|
2023-11-05 12:05:00 +01:00
|
|
|
{#if $highlightedItem !== undefined}
|
|
|
|
<FloatOver on:close={() => highlightedItem.setData(undefined)}>
|
2023-11-07 18:51:50 +01:00
|
|
|
<div>
|
2023-11-05 12:05:00 +01:00
|
|
|
<TagRenderingInput path={$highlightedItem.path} {state} schema={$highlightedItem.schema} />
|
|
|
|
</div>
|
|
|
|
</FloatOver>
|
|
|
|
{/if}
|
2023-10-25 00:03:51 +02:00
|
|
|
|
2023-11-05 12:05:00 +01:00
|
|
|
{/if}
|
|
|
|
</div>
|