forked from MapComplete/MapComplete
Studio: improvements after user test
This commit is contained in:
parent
449c1adb00
commit
e79a0fc81d
59 changed files with 1312 additions and 2920 deletions
|
|
@ -22,11 +22,14 @@
|
|||
|
||||
export let highlightedRendering: UIEventSource<string> = undefined;
|
||||
export let showQuestionIfUnknown: boolean = false;
|
||||
let editMode = false;
|
||||
/**
|
||||
* Indicates if this tagRendering currently shows the attribute or asks the question to _change_ the property
|
||||
*/
|
||||
export let editMode = !config.IsKnown(tags) || showQuestionIfUnknown;
|
||||
if (tags) {
|
||||
onDestroy(
|
||||
tags.addCallbackAndRunD((tags) => {
|
||||
editMode = showQuestionIfUnknown && !config.IsKnown(tags);
|
||||
tags.addCallbackD((tags) => {
|
||||
editMode = !config.IsKnown(tags)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@
|
|||
|
||||
function onSave() {
|
||||
if (selectedTags === undefined) {
|
||||
console.log("SelectedTags is undefined, ignoring 'onSave'-event")
|
||||
return;
|
||||
}
|
||||
if (layer === undefined || layer?.source === null) {
|
||||
|
|
@ -197,20 +198,20 @@
|
|||
</span>
|
||||
<slot name="upper-right" />
|
||||
</div>
|
||||
|
||||
{#if config.questionhint}
|
||||
<div>
|
||||
<SpecialTranslation
|
||||
t={config.questionhint}
|
||||
{tags}
|
||||
{state}
|
||||
{layer}
|
||||
feature={selectedElement}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if config.questionhint}
|
||||
<div class="max-h-60 overflow-y-auto">
|
||||
<SpecialTranslation
|
||||
t={config.questionhint}
|
||||
{tags}
|
||||
{state}
|
||||
{layer}
|
||||
feature={selectedElement}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if config.mappings?.length >= 8}
|
||||
<div class="sticky flex w-full">
|
||||
<img src="./assets/svg/search.svg" class="h-6 w-6" />
|
||||
|
|
|
|||
|
|
@ -1,20 +1,10 @@
|
|||
<script lang="ts">
|
||||
import Marker from "../Map/Marker.svelte";
|
||||
import NextButton from "../Base/NextButton.svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { AllSharedLayers } from "../../Customizations/AllSharedLayers";
|
||||
import { AllKnownLayouts, AllKnownLayoutsLazy } from "../../Customizations/AllKnownLayouts";
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection";
|
||||
import EditItemButton from "./EditItemButton.svelte";
|
||||
|
||||
export let layerIds: { id: string }[];
|
||||
export let layerIds: { id: string, owner: number }[];
|
||||
export let category: "layers" | "themes" = "layers";
|
||||
const dispatch = createEventDispatcher<{ layerSelected: string }>();
|
||||
|
||||
function fetchIconDescription(layerId): any {
|
||||
if(category === "themes"){
|
||||
return AllKnownLayouts.allKnownLayouts.get(layerId).icon
|
||||
}
|
||||
return AllSharedLayers.getSharedLayersConfigs().get(layerId)?._layerIcon;
|
||||
}
|
||||
export let osmConnection: OsmConnection;
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -22,12 +12,7 @@
|
|||
<slot name="title" />
|
||||
<div class="flex flex-wrap">
|
||||
{#each Array.from(layerIds) as layer}
|
||||
<NextButton clss="small" on:click={() => dispatch("layerSelected", layer)}>
|
||||
<div class="w-4 h-4 mr-1">
|
||||
<Marker icons={fetchIconDescription(layer.id)} />
|
||||
</div>
|
||||
{layer.id}
|
||||
</NextButton>
|
||||
<EditItemButton info={layer} {category} {osmConnection} on:layerSelected/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
36
src/UI/Studio/EditItemButton.svelte
Normal file
36
src/UI/Studio/EditItemButton.svelte
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<script lang="ts">
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection";
|
||||
import Marker from "../Map/Marker.svelte";
|
||||
import NextButton from "../Base/NextButton.svelte";
|
||||
import { AllKnownLayouts } from "../../Customizations/AllKnownLayouts";
|
||||
import { AllSharedLayers } from "../../Customizations/AllSharedLayers";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
export let info: { id: string, owner: number };
|
||||
export let category: "layers" | "themes";
|
||||
export let osmConnection: OsmConnection;
|
||||
|
||||
let displayName = UIEventSource.FromPromise(osmConnection.getInformationAboutUser(info.owner)).mapD(response => response.display_name);
|
||||
|
||||
let selfId = osmConnection.userDetails.mapD(ud => ud.uid)
|
||||
function fetchIconDescription(layerId): any {
|
||||
if (category === "themes") {
|
||||
return AllKnownLayouts.allKnownLayouts.get(layerId).icon;
|
||||
}
|
||||
return AllSharedLayers.getSharedLayersConfigs().get(layerId)?._layerIcon;
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher<{ layerSelected: string }>();
|
||||
|
||||
</script>
|
||||
|
||||
<NextButton clss="small" on:click={() => dispatch("layerSelected", info)}>
|
||||
<div class="w-4 h-4 mr-1">
|
||||
<Marker icons={fetchIconDescription(info.id)} />
|
||||
</div>
|
||||
<b class="px-1"> {info.id}</b>
|
||||
{#if info.owner && info.owner !== $selfId}
|
||||
(made by {$displayName ?? info.owner})
|
||||
{/if}
|
||||
</NextButton>
|
||||
|
|
@ -21,8 +21,8 @@
|
|||
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw;
|
||||
|
||||
export let state: EditLayerState;
|
||||
const messages = state.messages;
|
||||
const hasErrors = messages.map((m: ConversionMessage[]) => m.filter(m => m.level === "error").length);
|
||||
let messages = state.messages;
|
||||
let hasErrors = messages.mapD((m: ConversionMessage[]) => m.filter(m => m.level === "error").length);
|
||||
const configuration = state.configuration;
|
||||
|
||||
const allNames = Utils.Dedup(layerSchema.map(meta => meta.hints.group));
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
}
|
||||
|
||||
|
||||
const title: Store<string> = state.getStoreFor(["id"]);
|
||||
let title: Store<string> = state.getStoreFor(["id"]);
|
||||
const wl = window.location;
|
||||
const baseUrl = wl.protocol + "//" + wl.host + "/theme.html?userlayout=";
|
||||
|
||||
|
|
@ -53,13 +53,15 @@
|
|||
let config = layerSchema.find(config => config.path.length === 1 && config.path[0] === id);
|
||||
config = Utils.Clone(config);
|
||||
config.required = true;
|
||||
console.log(">>>", config);
|
||||
config.hints.ifunset = undefined;
|
||||
return config;
|
||||
}
|
||||
|
||||
let requiredFields = ["id", "name", "description"];
|
||||
let currentlyMissing = state.configuration.map(config => {
|
||||
if(!config){
|
||||
return []
|
||||
}
|
||||
const missing = [];
|
||||
for (const requiredField of requiredFields) {
|
||||
if (!config[requiredField]) {
|
||||
|
|
@ -160,7 +162,9 @@
|
|||
</div>
|
||||
{#if $highlightedItem !== undefined}
|
||||
<FloatOver on:close={() => highlightedItem.setData(undefined)}>
|
||||
<TagRenderingInput path={$highlightedItem.path} {state} schema={$highlightedItem.schema} />
|
||||
<div class="mt-16">
|
||||
<TagRenderingInput path={$highlightedItem.path} {state} schema={$highlightedItem.schema} />
|
||||
</div>
|
||||
</FloatOver>
|
||||
{/if}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
|||
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||
import {
|
||||
Conversion,
|
||||
ConversionContext,
|
||||
ConversionMessage,
|
||||
DesugaringContext,
|
||||
Pipe,
|
||||
|
|
@ -21,6 +20,7 @@ import { Feature, Point } from "geojson"
|
|||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import { LayoutConfigJson } from "../../Models/ThemeConfig/Json/LayoutConfigJson"
|
||||
import { PrepareTheme } from "../../Models/ThemeConfig/Conversion/PrepareTheme"
|
||||
import { ConversionContext } from "../../Models/ThemeConfig/Conversion/ConversionContext";
|
||||
|
||||
export interface HighlightedTagRendering {
|
||||
path: ReadonlyArray<string | number>
|
||||
|
|
@ -41,7 +41,9 @@ export abstract class EditJsonState<T> {
|
|||
public readonly highlightedItem: UIEventSource<HighlightedTagRendering> = new UIEventSource(
|
||||
undefined
|
||||
)
|
||||
sendingUpdates = false
|
||||
private readonly _stores = new Map<string, UIEventSource<any>>()
|
||||
private boolean
|
||||
|
||||
constructor(schema: ConfigMeta[], server: StudioServer, category: "layers" | "themes") {
|
||||
this.schema = schema
|
||||
|
|
@ -52,7 +54,13 @@ export abstract class EditJsonState<T> {
|
|||
|
||||
const layerId = this.getId()
|
||||
this.configuration
|
||||
.mapD((config) => JSON.stringify(config, null, " "))
|
||||
.mapD((config) => {
|
||||
if (!this.sendingUpdates) {
|
||||
console.log("Not sending updates yet! Trigger 'startSendingUpdates' first")
|
||||
return undefined
|
||||
}
|
||||
return JSON.stringify(config, null, " ")
|
||||
})
|
||||
.stabilized(100)
|
||||
.addCallbackD(async (config) => {
|
||||
const id = layerId.data
|
||||
|
|
@ -60,10 +68,17 @@ export abstract class EditJsonState<T> {
|
|||
console.warn("No id found in layer, not updating")
|
||||
return
|
||||
}
|
||||
await server.update(id, config, category)
|
||||
await this.server.update(id, config, this.category)
|
||||
})
|
||||
}
|
||||
|
||||
public startSavingUpdates(enabled = true) {
|
||||
this.sendingUpdates = enabled
|
||||
if (enabled) {
|
||||
this.configuration.ping()
|
||||
}
|
||||
}
|
||||
|
||||
public getCurrentValueFor(path: ReadonlyArray<string | number>): any | undefined {
|
||||
// Walk the path down to see if we find something
|
||||
let entry = this.configuration.data
|
||||
|
|
@ -96,7 +111,7 @@ export abstract class EditJsonState<T> {
|
|||
public register(
|
||||
path: ReadonlyArray<string | number>,
|
||||
value: Store<any>,
|
||||
noInitialSync: boolean = false
|
||||
noInitialSync: boolean = true
|
||||
): () => void {
|
||||
const unsync = value.addCallback((v) => {
|
||||
this.setValueAt(path, v)
|
||||
|
|
@ -260,6 +275,18 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
}
|
||||
|
||||
this.addMissingTagRenderingIds()
|
||||
|
||||
this.configuration.addCallbackAndRunD((layer) => {
|
||||
if (layer.tagRenderings) {
|
||||
// A bit of cleanup
|
||||
const lBefore = layer.tagRenderings.length
|
||||
const cleaned = Utils.NoNull(layer.tagRenderings)
|
||||
if (cleaned.length != lBefore) {
|
||||
layer.tagRenderings = cleaned
|
||||
this.configuration.ping()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected buildValidation(state: DesugaringContext) {
|
||||
|
|
@ -300,6 +327,10 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
}
|
||||
|
||||
export class EditThemeState extends EditJsonState<LayoutConfigJson> {
|
||||
constructor(schema: ConfigMeta[], server: StudioServer) {
|
||||
super(schema, server, "themes")
|
||||
}
|
||||
|
||||
protected buildValidation(state: DesugaringContext): Conversion<LayoutConfigJson, any> {
|
||||
return new Pipe(
|
||||
new PrepareTheme(state),
|
||||
|
|
@ -307,10 +338,6 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
|
|||
)
|
||||
}
|
||||
|
||||
constructor(schema: ConfigMeta[], server: StudioServer) {
|
||||
super(schema, server, "themes")
|
||||
}
|
||||
|
||||
protected getId(): Store<string> {
|
||||
return this.configuration.mapD((config) => config.id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
export let state: EditThemeState;
|
||||
let schema: ConfigMeta[] = state.schema.filter(schema => schema.path.length > 0);
|
||||
let config = state.configuration;
|
||||
const messages = state.messages;
|
||||
const hasErrors = messages.map((m: ConversionMessage[]) => m.filter(m => m.level === "error").length);
|
||||
let messages = state.messages;
|
||||
let hasErrors = messages.map((m: ConversionMessage[]) => m.filter(m => m.level === "error").length);
|
||||
let title = state.getStoreFor(["id"]);
|
||||
const wl = window.location;
|
||||
const baseUrl = wl.protocol + "//" + wl.host + "/theme.html?userlayout=";
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@
|
|||
import { ImmutableStore, Store } from "../../Logic/UIEventSource";
|
||||
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte";
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig";
|
||||
import * as nmd from "nano-markdown";
|
||||
import nmd from "nano-markdown";
|
||||
import type {
|
||||
QuestionableTagRenderingConfigJson
|
||||
} from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.js";
|
||||
import type { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderingConfigJson";
|
||||
import FromHtml from "../Base/FromHtml.svelte";
|
||||
import { Utils } from "../../Utils";
|
||||
|
||||
export let state: EditLayerState;
|
||||
export let path: ReadonlyArray<string | number>;
|
||||
|
|
@ -34,9 +35,15 @@
|
|||
return [x];
|
||||
}
|
||||
});
|
||||
let configs: Store<TagRenderingConfig[]> = configJson.mapD(configs => configs.map(config => new TagRenderingConfig(config)));
|
||||
let configs: Store<TagRenderingConfig[]> =configJson.mapD(configs => Utils.NoNull( configs.map(config => {
|
||||
try{
|
||||
return new TagRenderingConfig(config);
|
||||
}catch (e) {
|
||||
return undefined
|
||||
}
|
||||
})));
|
||||
let id: Store<string> = value.mapD(c => {
|
||||
if (c.id) {
|
||||
if (c?.id) {
|
||||
return c.id;
|
||||
}
|
||||
if (typeof c === "string") {
|
||||
|
|
@ -49,6 +56,14 @@
|
|||
|
||||
let messages = state.messagesFor(path);
|
||||
|
||||
let description = schema.description
|
||||
if(description){
|
||||
try{
|
||||
description = nmd(description)
|
||||
}catch (e) {
|
||||
console.error("Could not convert description to markdown", {description})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex">
|
||||
|
|
@ -63,8 +78,8 @@
|
|||
{schema.hints.question}
|
||||
{/if}
|
||||
</button>
|
||||
{#if schema.description}
|
||||
<FromHtml src={nmd(schema.description)} />
|
||||
{#if description}
|
||||
<FromHtml src={description} />
|
||||
{/if}
|
||||
{#each $messages as message}
|
||||
<div class="alert">
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
export let state: EditLayerState;
|
||||
export let path: (string | number)[] = [];
|
||||
export let schema: ConfigMeta;
|
||||
export let startInEditModeIfUnset: boolean = false
|
||||
let value = new UIEventSource<string | any>(undefined);
|
||||
|
||||
const isTranslation = schema.hints.typehint === "translation" || schema.hints.typehint === "rendered" || ConfigMetaUtils.isTranslation(schema);
|
||||
|
|
@ -118,6 +119,7 @@
|
|||
}
|
||||
let startValue = state.getCurrentValueFor(path);
|
||||
const tags = new UIEventSource<Record<string, string>>({ value: startValue });
|
||||
let startInEditMode = !startValue && startInEditModeIfUnset
|
||||
try {
|
||||
onDestroy(state.register(path, tags.map(tgs => {
|
||||
const v = tgs["value"];
|
||||
|
|
@ -157,7 +159,7 @@
|
|||
<span class="alert">{err}</span>
|
||||
{:else}
|
||||
<div class="w-full flex flex-col">
|
||||
<TagRenderingEditable {config} selectedElement={undefined} showQuestionIfUnknown={true} {state} {tags} />
|
||||
<TagRenderingEditable editMode={startInEditMode} {config} selectedElement={undefined} showQuestionIfUnknown={true} {state} {tags} />
|
||||
{#if $messages.length > 0}
|
||||
{#each $messages as msg}
|
||||
<div class="alert">{msg.message}</div>
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@
|
|||
}
|
||||
return tags["value"] === "true";
|
||||
});
|
||||
onDestroy(state.register(path, directValue, true));
|
||||
onDestroy(state.register(path, directValue));
|
||||
}
|
||||
|
||||
let subSchemas: ConfigMeta[] = [];
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ const configBuiltin = new TagRenderingConfig(<QuestionableTagRenderingConfigJson
|
|||
|
||||
|
||||
const tags = new UIEventSource({ value });
|
||||
|
||||
const store = state.getStoreFor(path);
|
||||
tags.addCallbackAndRunD(tgs => {
|
||||
store.setData(tgs["value"]);
|
||||
|
|
@ -112,7 +113,7 @@ const missing: string[] = questionableTagRenderingSchemaRaw.filter(schema => sch
|
|||
<slot name="upper-right" />
|
||||
</div>
|
||||
{#if $allowQuestions}
|
||||
<SchemaBasedField {state} path={[...path,"question"]} schema={topLevelItems["question"]} />
|
||||
<SchemaBasedField startInEditModeIfUnset={true} {state} path={[...path,"question"]} schema={topLevelItems["question"]} />
|
||||
<SchemaBasedField {state} path={[...path,"questionHint"]} schema={topLevelItems["questionHint"]} />
|
||||
{/if}
|
||||
{#each ($mappings ?? []) as mapping, i (mapping)}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@
|
|||
import { QuestionMarkCircleIcon } from "@babeard/svelte-heroicons/mini";
|
||||
import type { ConfigMeta } from "./Studio/configMeta";
|
||||
import EditTheme from "./Studio/EditTheme.svelte";
|
||||
|
||||
export let studioUrl = window.location.hostname === "127.0.0.1" ? "http://127.0.0.1:1235" : "https://studio.mapcomplete.org";
|
||||
import * as meta from "../../package.json"
|
||||
|
||||
export let studioUrl = window.location.hostname === "127.0.0.2" ? "http://127.0.0.1:1235" : "https://studio.mapcomplete.org";
|
||||
|
||||
let osmConnection = new OsmConnection(new OsmConnection({
|
||||
oauth_token: QueryParameters.GetQueryParameter(
|
||||
|
|
@ -61,18 +62,22 @@
|
|||
let layerId = editLayerState.configuration.map(layerConfig => layerConfig.id);
|
||||
|
||||
let showIntro = UIEventSource.asBoolean(LocalStorageSource.Get("studio-show-intro", "true"));
|
||||
|
||||
const version = meta.version
|
||||
async function editLayer(event: Event) {
|
||||
const layerId: {owner: number, id: string} = event.detail;
|
||||
state = "loading";
|
||||
editLayerState.startSavingUpdates(false)
|
||||
editLayerState.configuration.setData(await studio.fetch(layerId.id, "layers", layerId.owner));
|
||||
editLayerState.startSavingUpdates()
|
||||
state = "editing_layer";
|
||||
}
|
||||
|
||||
async function editTheme(event: Event) {
|
||||
const id : {id: string, owner: number} = event.detail;
|
||||
state = "loading";
|
||||
editThemeState.startSavingUpdates(false)
|
||||
editThemeState.configuration.setData(await studio.fetch(id.id, "themes", id.owner));
|
||||
editThemeState.startSavingUpdates()
|
||||
state = "editing_theme";
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +158,7 @@
|
|||
Show the introduction again
|
||||
</NextButton>
|
||||
</div>
|
||||
<span class="subtle">MapComplete version {version}</span>
|
||||
</div>
|
||||
{:else if state === "edit_layer"}
|
||||
|
||||
|
|
@ -160,14 +166,14 @@
|
|||
<BackButton clss="small p-1" imageClass="w-8 h-8" on:click={() => {state =undefined}}>MapComplete Studio
|
||||
</BackButton>
|
||||
<h2>Choose a layer to edit</h2>
|
||||
<ChooseLayerToEdit layerIds={$selfLayers} on:layerSelected={editLayer}>
|
||||
<ChooseLayerToEdit {osmConnection} layerIds={$selfLayers} on:layerSelected={editLayer}>
|
||||
<h3 slot="title">Your layers</h3>
|
||||
</ChooseLayerToEdit>
|
||||
<h3>Layers by other contributors</h3>
|
||||
<ChooseLayerToEdit layerIds={$otherLayers} on:layerSelected={editLayer} />
|
||||
<ChooseLayerToEdit {osmConnection} layerIds={$otherLayers} on:layerSelected={editLayer} />
|
||||
|
||||
<h3>Official layers by MapComplete</h3>
|
||||
<ChooseLayerToEdit layerIds={$officialLayers} on:layerSelected={editLayer} />
|
||||
<ChooseLayerToEdit {osmConnection} layerIds={$officialLayers} on:layerSelected={editLayer} />
|
||||
</div>
|
||||
{:else if state === "edit_theme"}
|
||||
|
||||
|
|
@ -175,13 +181,13 @@
|
|||
<BackButton clss="small p-1" imageClass="w-8 h-8" on:click={() => {state =undefined}}>MapComplete Studio
|
||||
</BackButton>
|
||||
<h2>Choose a theme to edit</h2>
|
||||
<ChooseLayerToEdit layerIds={$selfThemes} on:layerSelected={editTheme}>
|
||||
<ChooseLayerToEdit {osmConnection} layerIds={$selfThemes} on:layerSelected={editTheme}>
|
||||
<h3 slot="title">Your themes</h3>
|
||||
</ChooseLayerToEdit>
|
||||
<h3>Themes by other contributors</h3>
|
||||
<ChooseLayerToEdit layerIds={$otherThemes} on:layerSelected={editTheme} />
|
||||
<ChooseLayerToEdit {osmConnection} layerIds={$otherThemes} on:layerSelected={editTheme} />
|
||||
<h3>Official themes by MapComplete</h3>
|
||||
<ChooseLayerToEdit layerIds={$officialThemes} on:layerSelected={editTheme} />
|
||||
<ChooseLayerToEdit {osmConnection} layerIds={$officialThemes} on:layerSelected={editTheme} />
|
||||
|
||||
</div>
|
||||
{:else if state === "loading"}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ export class Translation extends BaseUIElement {
|
|||
"Constructing a translation, but the object containing translations is empty " +
|
||||
(context ?? "No context given")
|
||||
)
|
||||
throw `Constructing a translation, but the object containing translations is empty (${context})`
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue