Refactoring: split icons into proper layered icons, fix and rerun shops-thief

This commit is contained in:
Pieter Vander Vennet 2023-10-06 23:56:50 +02:00
parent d6cd0516bb
commit 19a760178b
307 changed files with 26845 additions and 116541 deletions

View file

@ -52,11 +52,8 @@
<SimpleTagInput { value } />
{:else if $construct !== undefined}
{#if isBaseUIElement}
<ToSvelte
construct={() =>
<ToSvelte
construct={() =>
new VariableUiElement(construct.mapD((construct) => construct(value, properties)))}
/>
{/if}
/>
{/if}

86
src/UI/Map/Icon.svelte Normal file
View file

@ -0,0 +1,86 @@
<script lang="ts">
import { IconConfig } from "../../Models/ThemeConfig/PointRenderingConfig";
import { Store } from "../../Logic/UIEventSource";
import Pin from "../../assets/svg/Pin.svelte";
import Square from "../../assets/svg/Square.svelte";
import Circle from "../../assets/svg/Circle.svelte";
import Checkmark from "../../assets/svg/Checkmark.svelte";
import Clock from "../../assets/svg/Clock.svelte";
import Close from "../../assets/svg/Close.svelte";
import Crosshair from "../../assets/svg/Crosshair.svelte";
import Help from "../../assets/svg/Help.svelte";
import Home from "../../assets/svg/Home.svelte";
import Invalid from "../../assets/svg/Invalid.svelte";
import Location from "../../assets/svg/Location.svelte";
import Location_empty from "../../assets/svg/Location_empty.svelte";
import Location_locked from "../../assets/svg/Location_locked.svelte";
import Note from "../../assets/svg/Note.svelte";
import Resolved from "../../assets/svg/Resolved.svelte";
import Ring from "../../assets/svg/Ring.svelte";
import Scissors from "../../assets/svg/Scissors.svelte";
import Teardrop from "../../assets/svg/Teardrop.svelte";
import Teardrop_with_hole_green from "../../assets/svg/Teardrop_with_hole_green.svelte";
import Triangle from "../../assets/svg/Triangle.svelte";
/**
* Renders a single icon.
*
* Icons -placed on top of each other- form a 'Marker' together
*/
export let icon: IconConfig;
export let tags: Store<Record<string, string>>;
let iconItem = icon.icon?.GetRenderValue(tags)?.txt;
$: iconItem = icon.icon?.GetRenderValue($tags)?.txt;
let color = icon.color?.GetRenderValue(tags)?.txt ?? "#000000";
$: color = icon.color?.GetRenderValue($tags)?.txt ?? "#000000";
</script>
{#if iconItem}
<div class="absolute top-0 left-0 w-full h-full">
{#if iconItem === "pin"}
<Pin {color} />
{:else if iconItem === "square"}
<Square {color} />
{:else if iconItem === "circle"}
<Circle {color} />
{:else if iconItem === "checkmark"}
<Checkmark {color} />
{:else if iconItem === "clock"}
<Clock {color} />
{:else if iconItem === "close"}
<Close {color} />
{:else if iconItem === "crosshair"}
<Crosshair {color} />
{:else if iconItem === "help"}
<Help {color} />
{:else if iconItem === "home"}
<Home {color} />
{:else if iconItem === "invalid"}
<Invalid {color} />
{:else if iconItem === "location"}
<Location {color} />
{:else if iconItem === "location_empty"}
<Location_empty {color} />
{:else if iconItem === "location_locked"}
<Location_locked {color} />
{:else if iconItem === "note"}
<Note {color} />
{:else if iconItem === "resolved"}
<Resolved {color} />
{:else if iconItem === "ring"}
<Ring {color} />
{:else if iconItem === "scissors"}
<Scissors {color} />
{:else if iconItem === "teardrop"}
<Teardrop {color} />
{:else if iconItem === "teardrop_with_hole_green"}
<Teardrop_with_hole_green {color} />
{:else if iconItem === "triangle"}
<Triangle {color} />
{:else}
<img class="w-full h-full" src={iconItem} />
{/if}
</div>
{/if}

20
src/UI/Map/Marker.svelte Normal file
View file

@ -0,0 +1,20 @@
<script lang="ts">
import PointRenderingConfig, { IconConfig } from "../../Models/ThemeConfig/PointRenderingConfig";
import Icon from "./Icon.svelte";
import { Store } from "../../Logic/UIEventSource";
/**
* Renders a 'marker', which consists of multiple 'icons'
*/
export let config : PointRenderingConfig
let icons: IconConfig[] = config.marker;
export let tags: Store<Record<string, string>>;
</script>
<div class="relative w-full h-full">
{#each icons as icon}
<Icon {icon} {tags} />
{/each}
</div>

View file

@ -142,7 +142,7 @@ class PointRenderingLayer {
} else {
store = new ImmutableStore(<OsmTags>feature.properties)
}
const { html, iconAnchor } = this._config.RenderIcon(store, true)
const { html, iconAnchor } = this._config.RenderIcon(store)
html.SetClass("marker")
if (this._onClick !== undefined) {
html.SetClass("cursor-pointer")

View file

@ -44,7 +44,7 @@
const tags = TagUtils.KVtoProperties(preset.tags ?? [])
const icon: string = layer.mapRendering[0]
.RenderIcon(new ImmutableStore<any>(tags), false)
.RenderIcon(new ImmutableStore<any>(tags))
.html.SetClass("w-12 h-12 block relative")
.ConstructElement().innerHTML

View file

@ -0,0 +1,43 @@
<script lang="ts">
import type { ConfigMeta } from "./configMeta";
import EditLayerState from "./EditLayerState";
import type { QuestionableTagRenderingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson";
import { UIEventSource } from "../../Logic/UIEventSource";
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte";
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig";
export let schema: ConfigMeta;
export let state: EditLayerState;
export let path: (string | number)[] = [];
const configJson: QuestionableTagRenderingConfigJson = {
mappings: schema.hints.suggestions,
multiAnswer: true,
id: "multi_anwser_"+path.join("_"),
question: schema.hints.question
}
const tags = new UIEventSource({})
{
// Setting the initial value
const v = <string[]> state.getCurrentValueFor(path)
if(v && v.length > 0){
tags.setData({value: v.join(";")})
}
}
tags.addCallbackD(tags => {
const values = tags["value"]?.split(";")
if(!values){
return
}
state.setValueAt(path, values)
})
const config = new TagRenderingConfig(configJson)
</script>
<div>
<TagRenderingEditable {config} selectedElement={undefined} showQuestionIfUnknown={true} {state} {tags} />
</div>

View file

@ -58,7 +58,8 @@
<div slot="title2">Rendering on the map</div>
<div slot="content2">
<Region configs={perRegion["maprendering"]} {state} />
<Region configs={perRegion["linerendering"]} {state} />
<Region configs={perRegion["pointrendering"]} {state} />
</div>
<div slot="title3">Configuration file</div>
<div slot="content3">

View file

@ -120,21 +120,20 @@ export default class EditLayerState {
}
public setValueAt(path: ReadonlyArray<string | number>, v: any) {
{
let entry = this.configuration.data
for (let i = 0; i < path.length - 1; i++) {
const breadcrumb = path[i]
if (entry[breadcrumb] === undefined) {
entry[breadcrumb] = typeof path[i + 1] === "number" ? [] : {}
}
entry = entry[breadcrumb]
let entry = this.configuration.data
console.log("Setting value", v, "to", path, "in entry", entry)
for (let i = 0; i < path.length - 1; i++) {
const breadcrumb = path[i]
if (entry[breadcrumb] === undefined) {
entry[breadcrumb] = typeof path[i + 1] === "number" ? [] : {}
}
if (v) {
entry[path.at(-1)] = v
} else if (entry) {
delete entry[path.at(-1)]
}
this.configuration.ping()
entry = entry[breadcrumb]
}
if (v) {
entry[path.at(-1)] = v
} else if (entry) {
delete entry[path.at(-1)]
}
this.configuration.ping()
}
}

View file

@ -23,8 +23,10 @@
export let path: (string | number)[] = [];
const isTagRenderingBlock = path.length === 1 && path[0] === "tagRenderings";
const subparts = state.getSchemaStartingWith(schema.path);
const subparts: ConfigMeta = state.getSchemaStartingWith(schema.path)
.filter(part => part.path.length - 1 === schema.path.length);
console.log("For ", schema.path, "got subparts", subparts)
/**
* Store the _indices_
*/

View file

@ -100,28 +100,33 @@
startValue = JSON.stringify(startValue)
}
const tags = new UIEventSource<Record<string, string>>({value: startValue ?? ""})
onDestroy(state.register(path, tags.map(tgs => {
const v = tgs["value"];
if (schema.type === "boolan") {
return v === "true" || v === "yes" || v === "1"
}
if (schema.type === "number") {
return Number(v)
}
if(isTranslation) {
if(v === ""){
return {}
try {
onDestroy(state.register(path, tags.map(tgs => {
const v = tgs["value"];
if (schema.type === "boolan") {
return v === "true" || v === "yes" || v === "1"
}
return JSON.parse(v)
}
return v
}), isTranslation))
if (schema.type === "number") {
return Number(v)
}
if (isTranslation) {
if (v === "") {
return {}
}
return JSON.parse(v)
}
return v
}), isTranslation))
}catch (e) {
console.error("Could not register", path,"due to",e)
}
</script>
{#if err !== undefined}
<span class="alert">{err}</span>
{:else}
<div class="w-full flex flex-col">
<span class="subtle">{path.join(".")}</span>
<TagRenderingEditable {config} selectedElement={undefined} showQuestionIfUnknown={true} {state} {tags}/>
</div>
{/if}

View file

@ -6,6 +6,7 @@
import SchemaBasedMultiType from "./SchemaBasedMultiType.svelte";
import SchemaBasedTranslationInput from "./SchemaBasedTranslationInput.svelte";
import { ConfigMetaUtils } from "./configMeta.ts"
import ArrayMultiAnswer from "./ArrayMultiAnswer.svelte";
export let schema: ConfigMeta;
export let state: EditLayerState;
export let path: (string | number)[] = [];
@ -14,6 +15,8 @@
{#if schema.hints.typehint === "tagrendering[]"}
<!-- We cheat a bit here by matching this 'magical' type... -->
<SchemaBasedArray {path} {state} {schema} />
{:else if schema.type === "array" && schema.hints.multianswer === "true"}
<ArrayMultiAnswer {path} {state} {schema}/>
{:else if schema.type === "array"}
<SchemaBasedArray {path} {state} {schema} />
{:else if schema.hints.types}

View file

@ -29,49 +29,81 @@
types.splice(hasBooleanOption);
}
let lastIsString = false;
{
const types: string | string[] = Array.isArray(schema.type) ? schema.type[schema.type.length - 1].type : [];
lastIsString = types === "string" || (Array.isArray(types) && types.some(i => i === "string"));
}
if (lastIsString) {
types.splice(types.length - 1, 1);
}
const configJson: QuestionableTagRenderingConfigJson = {
id: "TYPE_OF:" + path.join("_"),
question: "Which subcategory is needed?",
question: "Which subcategory is needed for "+schema.path.at(-1)+"?",
questionHint: nmd(schema.description),
mappings: types.map(opt => opt.trim()).filter(opt => opt.length > 0).map((opt, i) => ({
if: "value=" + i,
addExtraTags: ["direct="],
if: "chosen_type_index=" + i,
addExtraTags: ["value="],
then: opt + (i === defaultOption ? " (Default)" : "")
}))
})),
render: !lastIsString ? undefined : (schema.hints.inline ?? "Use a hardcoded value: <b>{value}</b>"),
freeform: !lastIsString ? undefined : {
key: "value",
inline: true,
type: schema.hints.typehint,
addExtraTags: ["chosen_type_index="]
}
};
let tags = new UIEventSource<Record<string, string>>({});
if (schema.hints.ifunset) {
configJson.mappings.push(
{
if: { and: ["value=", "chosen_type_index="] },
then: schema.hints.ifunset
}
);
}
if (schema.hints.suggestions) {
configJson.mappings.push(...schema.hints.suggestions);
}
if (hasBooleanOption >= 0) {
configJson.mappings.unshift(
{
if: "direct=true",
if: "value=true",
then: (schema.hints.iftrue ?? "Yes"),
addExtraTags: ["value="]
addExtraTags: ["chosen_type_index="]
},
{
if: "direct=false",
if: "value=false",
then: (schema.hints.iffalse ?? "No"),
addExtraTags: ["value="]
addExtraTags: ["chosen_type_index="]
}
);
}
const config = new TagRenderingConfig(configJson, "config based on " + schema.path.join("."));
let chosenOption: number = defaultOption;
const existingValue = state.getCurrentValueFor(path);
console.log("Setting direct: ", hasBooleanOption, path.join("."), existingValue);
console.log("Initial value is", existingValue);
if (hasBooleanOption >= 0 && (existingValue === true || existingValue === false)) {
tags.setData({ direct: "" + existingValue });
tags.setData({ value: "" + existingValue });
} else if (lastIsString && typeof existingValue === "string") {
tags.setData({ value: existingValue });
chosenOption = undefined;
} else if (existingValue) {
// We found an existing value. Let's figure out what type it matches and select that one
// We run over all possibilities and check what is required
const possibleTypes: { index: number, matchingPropertiesCount: number, optionalMatches: number }[] = [];
outer: for (let i = 0; i < (<[]>schema.type).length; i++) {
const type = schema.type[i];
let optionalMatches = 0
let optionalMatches = 0;
for (const key of Object.keys(type.properties ?? {})) {
if(!!existingValue[key]){
optionalMatches++
if (!!existingValue[key]) {
optionalMatches++;
}
}
if (type.required) {
@ -85,51 +117,56 @@
}
numberOfMatches++;
}
possibleTypes.push({ index: i, matchingPropertiesCount: numberOfMatches , optionalMatches});
possibleTypes.push({ index: i, matchingPropertiesCount: numberOfMatches, optionalMatches });
} else {
possibleTypes.push({ index: i, matchingPropertiesCount: 0, optionalMatches });
}
}
possibleTypes.sort((a, b) => b.optionalMatches - a.optionalMatches);
possibleTypes.sort((a, b) => b.matchingPropertiesCount - a.matchingPropertiesCount);
if (possibleTypes.length > 0) {
tags.setData({ value: "" + possibleTypes[0].index });
tags.setData({ chosen_type_index: "" + possibleTypes[0].index });
}
} else if (defaultOption !== undefined) {
tags.setData({ value: "" + defaultOption });
tags.setData({ chosen_type_index: "" + defaultOption });
}
if (hasBooleanOption >= 0) {
if (hasBooleanOption >= 0 || lastIsString) {
const directValue = tags.mapD(tags => {
if (tags["value"]) {
return undefined;
if (tags["chosen_type_index"]) {
return "";
}
return tags["direct"] === "true";
if (lastIsString) {
return tags["value"];
}
return tags["value"] === "true";
});
onDestroy(state.register(path, directValue, true));
}
let chosenOption: number = defaultOption;
let subSchemas: ConfigMeta[] = [];
let subpath = path;
console.log("Initial chosen option is", chosenOption);
onDestroy(tags.addCallbackAndRun(tags => {
if (tags["value"] !== "") {
chosenOption = undefined;
return;
}
const oldOption = chosenOption;
chosenOption = tags["value"] ? Number(tags["value"]) : defaultOption;
chosenOption = tags["chosen_type_index"] ? Number(tags["chosen_type_index"]) : defaultOption;
const type = schema.type[chosenOption];
if (chosenOption !== oldOption) {
// Reset the values beneath
subSchemas = [];
const o = state.getCurrentValueFor(path) ?? {}
console.log({o})
for(const key of type?.required ?? []){
console.log(key)
o[key] ??= {}
const o = state.getCurrentValueFor(path) ?? {};
console.log({ o });
for (const key of type?.required ?? []) {
console.log(key);
o[key] ??= {};
}
state.setValueAt(path, o);
}
@ -152,13 +189,16 @@
for (const crumble of Object.keys(type.properties)) {
subSchemas.push(...(state.getSchema([...cleanPath, crumble])));
}
console.log("Got subschemas for", path, ":", subSchemas)
}));
</script>
<div class="p-2 border-2 border-dashed border-gray-300 flex flex-col gap-y-2">
<div class="p-2 border-2 border-dashed border-gray-300 flex flex-col gap-y-2 m-1">
{#if schema.hints.title !== undefined}
<h3>{schema.hints.title}</h3>
<div> {schema.description} </div>
{/if}
<div>
<TagRenderingEditable {config} selectedElement={undefined} showQuestionIfUnknown={true} {state} {tags} />
</div>

View file

@ -18,6 +18,8 @@ export interface ConfigMeta {
default?: string
typesdefault?: string
suggestions?: []
title?: string
multianswer?: "true" | string
}
required: boolean
description: string