refactoring: fix basic flow to add a new point

This commit is contained in:
Pieter Vander Vennet 2023-04-06 01:33:08 +02:00
parent 52a0810ea9
commit 0241f89d3d
109 changed files with 1931 additions and 1446 deletions

View file

@ -0,0 +1,239 @@
<script lang="ts">
/**
* This component ties together all the steps that are needed to create a new point.
* There are many subcomponents which help with that
*/
import type { SpecialVisualizationState } from "../../SpecialVisualization";
import PresetList from "./PresetList.svelte";
import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig";
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
import Tr from "../../Base/Tr.svelte";
import SubtleButton from "../../Base/SubtleButton.svelte";
import FromHtml from "../../Base/FromHtml.svelte";
import Translations from "../../i18n/Translations.js";
import TagHint from "../TagHint.svelte";
import { And } from "../../../Logic/Tags/And.js";
import LoginToggle from "../../Base/LoginToggle.svelte";
import Constants from "../../../Models/Constants.js";
import FilteredLayer from "../../../Models/FilteredLayer";
import { Store, UIEventSource } from "../../../Logic/UIEventSource";
import { EyeIcon, EyeOffIcon } from "@rgossiaux/svelte-heroicons/solid";
import LoginButton from "../../Base/LoginButton.svelte";
import NewPointLocationInput from "../../BigComponents/NewPointLocationInput.svelte";
import CreateNewNodeAction from "../../../Logic/Osm/Actions/CreateNewNodeAction";
import { OsmObject } from "../../../Logic/Osm/OsmObject";
import { Tag } from "../../../Logic/Tags/Tag";
import type { WayId } from "../../../Models/OsmFeature";
import { TagUtils } from "../../../Logic/Tags/TagUtils";
import Loading from "../../Base/Loading.svelte";
export let coordinate: { lon: number, lat: number };
export let state: SpecialVisualizationState;
let selectedPreset: { preset: PresetConfig, layer: LayerConfig, icon: string, tags: Record<string, string> } = undefined;
let confirmedCategory = false;
$: if (selectedPreset === undefined) {
confirmedCategory = false;
creating = false
}
let flayer: FilteredLayer = undefined;
let layerIsDisplayed: UIEventSource<boolean> | undefined = undefined;
let layerHasFilters: Store<boolean> | undefined = undefined;
$:{
flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id);
layerIsDisplayed = flayer?.isDisplayed;
layerHasFilters = flayer?.hasFilter;
}
const t = Translations.t.general.add;
const zoom = state.mapProperties.zoom;
let preciseCoordinate: UIEventSource<{ lon: number, lat: number }> = new UIEventSource(undefined);
let snappedToObject: UIEventSource<string> = new UIEventSource<string>(undefined);
let creating = false;
/**
* Call when the user should restart the flow by clicking on the map, e.g. because they disabled filters.
* Will delete the lastclick-location
*/
function abort() {
state.selectedElement.setData(undefined);
// When aborted, we force the contributors to place the pin _again_
// This is because there might be a nearby object that was disabled; this forces them to re-evaluate the map
state.lastClickObject.features.setData([]);
}
async function confirm() {
creating = true;
const location: { lon: number; lat: number } = preciseCoordinate.data;
const snapTo: WayId | undefined = <WayId>snappedToObject.data;
const tags: Tag[] = selectedPreset.preset.tags;
console.log("Creating new point at", location, "snapped to", snapTo, "with tags", tags);
const snapToWay = snapTo === undefined ? undefined : await OsmObject.DownloadObjectAsync(snapTo, 0);
const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {
theme: state.layout?.id ?? "unkown",
changeType: "create",
snapOnto: snapToWay
});
await state.changes.applyAction(newElementAction);
const newId = newElementAction.newElementId;
state.newFeatures.features.data.push({
type: "Feature",
properties: {
id: newId,
...TagUtils.KVtoProperties(tags)
},
geometry: {
type: "Point",
coordinates: [location.lon, location.lat]
}
});
state.newFeatures.features.ping();
console.log("New features:", state.newFeatures.features.data )
{
// Set some metainfo
const tagsStore = state.featureProperties.getStore(newId);
const properties = tagsStore.data;
if (snapTo) {
// metatags (starting with underscore) are not uploaded, so we can safely mark this
properties["_referencing_ways"] = `["${snapTo}"]`;
}
properties["_last_edit:timestamp"] = new Date().toISOString();
const userdetails = state.osmConnection.userDetails.data;
properties["_last_edit:contributor"] = userdetails.name;
properties["_last_edit:uid"] = "" + userdetails.uid;
tagsStore.ping();
}
const feature = state.indexedFeatures.featuresById.data.get(newId);
abort();
state.selectedElement.setData(feature);
state.selectedLayer.setData(selectedPreset.layer);
}
</script>
<LoginToggle ignoreLoading={true} {state}>
<LoginButton osmConnection={state.osmConnection} slot="not-logged-in">
<Tr slot="message" t={Translations.t.general.add.pleaseLogin} />
</LoginButton>
{#if $zoom < Constants.minZoomLevelToAddNewPoint}
<div class="alert">
<Tr t={Translations.t.general.add.zoomInFurther}></Tr>
</div>
{:else if selectedPreset === undefined}
<!-- First, select the correct preset -->
<PresetList {state} on:select={event => {selectedPreset = event.detail}}></PresetList>
{:else if !$layerIsDisplayed}
<!-- Check that the layer is enabled, so that we don't add a duplicate -->
<div class="alert flex justify-center items-center">
<EyeOffIcon class="w-8" />
<Tr t={Translations.t.general.add.layerNotEnabled
.Subs({ layer: selectedPreset.layer.name })
} />
</div>
<SubtleButton on:click={() => {
layerIsDisplayed.setData(true)
abort()
}}>
<EyeIcon slot="image" class="w-8" />
<Tr slot="message" t={Translations.t.general.add.enableLayer.Subs({name: selectedPreset.layer.name})} />
</SubtleButton>
<SubtleButton on:click={() => {
abort()
state.guistate.openFilterView(selectedPreset.layer) } }>
<img src="./assets/svg/layers.svg" slot="image" class="w-6">
<Tr slot="message" t={Translations.t.general.add.openLayerControl}></Tr>
</SubtleButton>
{:else if $layerHasFilters}
<!-- Some filters are enabled. The feature to add might already be mapped, but hiddne -->
<div class="alert flex justify-center items-center">
<EyeOffIcon class="w-8" />
<Tr t={Translations.t.general.add.disableFiltersExplanation} />
</div>
<SubtleButton on:click={() => {
abort()
const flayer = state.layerState.filteredLayers.get(selectedPreset.layer.id)
flayer.disableAllFilters()
}
}>
<EyeOffIcon class="w-8" />
<Tr slot="message" t={Translations.t.general.add.disableFilters}></Tr>
</SubtleButton>
<SubtleButton on:click={() => {
abort()
state.guistate.openFilterView(selectedPreset.layer)
}
}>
<img src="./assets/svg/layers.svg" slot="image" class="w-6">
<Tr slot="message" t={Translations.t.general.add.openLayerControl}></Tr>
</SubtleButton>
{:else if !confirmedCategory }
<!-- Second, confirm the category -->
<Tr t={Translations.t.general.add.confirmIntro.Subs({title: selectedPreset.preset.title})}></Tr>
{#if selectedPreset.preset.description}
<Tr t={selectedPreset.preset.description} />
{/if}
{#if selectedPreset.preset.exampleImages}
<h4>
{#if selectedPreset.preset.exampleImages.length == 1}
<Tr t={Translations.t.general.example} />
{:else}
<Tr t={Translations.t.general.examples } />
{/if}
</h4>
<span class="flex flex-wrap items-stretch">
{#each selectedPreset.preset.exampleImages as src}
<img {src} class="h-64 m-1 w-auto rounded-lg">
{/each}
</span>
{/if}
<TagHint embedIn={tags => t.presetInfo.Subs({tags})} osmConnection={state.osmConnection}
tags={new And(selectedPreset.preset.tags)}></TagHint>
<SubtleButton on:click={() => confirmedCategory = true}>
<div slot="image" class="relative">
<FromHtml src={selectedPreset.icon}></FromHtml>
<img class="absolute bottom-0 right-0 w-4 h-4" src="./assets/svg/confirm.svg">
</div>
<div slot="message">
<Tr t={selectedPreset.text}></Tr>
</div>
</SubtleButton>
<SubtleButton on:click={() => selectedPreset = undefined}>
<img src="./assets/svg/back.svg" class="w-8 h-8" slot="image">
<div slot="message">
<Tr t={t.backToSelect} />
</div>
</SubtleButton>
{:else if !creating}
<NewPointLocationInput value={preciseCoordinate} snappedTo={snappedToObject} {state} {coordinate}
targetLayer={selectedPreset.layer}
snapToLayers={selectedPreset.preset.preciseInput.snapToLayers}></NewPointLocationInput>
<SubtleButton on:click={confirm}>
<span slot="message">Confirm location</span>
</SubtleButton>
{:else}
<Loading>Creating point...</Loading>
{/if}
</LoginToggle>

View file

@ -0,0 +1,88 @@
<script lang="ts">
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig";
import { createEventDispatcher } from "svelte";
import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig";
import Tr from "../../Base/Tr.svelte";
import Translations from "../../i18n/Translations.js";
import SubtleButton from "../../Base/SubtleButton.svelte";
import { Translation } from "../../i18n/Translation";
import type { SpecialVisualizationState } from "../../SpecialVisualization";
import { ImmutableStore } from "../../../Logic/UIEventSource";
import { TagUtils } from "../../../Logic/Tags/TagUtils";
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
import FromHtml from "../../Base/FromHtml.svelte";
/**
* This component lists all the presets and allows the user to select one
*/
export let state: SpecialVisualizationState;
let layout: LayoutConfig = state.layout;
let presets: {
preset: PresetConfig,
layer: LayerConfig,
text: Translation,
icon: string,
tags: Record<string, string>
}[] = [];
for (const layer of layout.layers) {
const flayer = state.layerState.filteredLayers.get(layer.id);
if (flayer.isDisplayed.data === false) {
// The layer is not displayed...
if (!state.featureSwitches.featureSwitchFilter.data) {
// ...and we cannot enable the layer control -> we skip, as these presets can never be shown anyway
continue;
}
if (layer.name === undefined) {
// this layer can never be toggled on in any case, so we skip the presets
continue;
}
}
for (const preset of layer.presets) {
const tags = TagUtils.KVtoProperties(preset.tags ?? []);
const icon: string =
layer.mapRendering[0]
.RenderIcon(new ImmutableStore<any>(tags), false)
.html.SetClass("w-12 h-12 block relative")
.ConstructElement().innerHTML;
const description = preset.description?.FirstSentence();
const simplified = {
preset,
layer,
icon,
description,
tags,
text: Translations.t.general.add.addNew.Subs({ category: preset.title }, preset.title["context"])
};
presets.push(simplified);
}
}
const dispatch = createEventDispatcher<{ select: {preset: PresetConfig, layer: LayerConfig, icon: string, tags: Record<string, string>} }>();
</script>
<div>
<Tr t={Translations.t.general.add.intro} />
{#each presets as preset}
<SubtleButton on:click={() => dispatch("select", preset)}>
<FromHtml slot="image" src={preset.icon}></FromHtml>
<div slot="message">
<b>
<Tr t={preset.text} />
</b>
{#if preset.description}
<Tr t={preset.description}/>
{/if}
</div>
</SubtleButton>
{/each}
</div>

View file

@ -0,0 +1,139 @@
<script lang="ts">
/**
* UIcomponent to create a new note at the given location
*/
import type { SpecialVisualizationState } from "../SpecialVisualization";
import { UIEventSource } from "../../Logic/UIEventSource";
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource";
import ValidatedInput from "../InputElement/ValidatedInput.svelte";
import SubtleButton from "../Base/SubtleButton.svelte";
import Tr from "../Base/Tr.svelte";
import Translations from "../i18n/Translations.js";
import type { Feature, Point } from "geojson";
import LoginToggle from "../Base/LoginToggle.svelte";
import FilteredLayer from "../../Models/FilteredLayer";
export let coordinate: { lon: number, lat: number };
export let state: SpecialVisualizationState;
let comment: UIEventSource<string> = LocalStorageSource.Get("note-text");
let created = false;
let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note");
let hasFilter = notelayer?.hasFilter;
let isDisplayed = notelayer?.isDisplayed;
function enableNoteLayer() {
state.guistate.closeAll();
isDisplayed.setData(true);
}
async function uploadNote() {
let txt = comment.data;
if (txt === undefined || txt === "") {
return;
}
const loc = coordinate;
txt += "\n\n #MapComplete #" + state?.layout?.id;
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt);
console.log("Created a note, got id",id)
const feature = <Feature<Point>>{
type: "Feature",
geometry: {
type: "Point",
coordinates: [loc.lon, loc.lat]
},
properties: {
id: "" + id.id,
date_created: new Date().toISOString(),
_first_comment: txt,
comments: JSON.stringify([
{
text: txt,
html: txt,
user: state.osmConnection?.userDetails?.data?.name,
uid: state.osmConnection?.userDetails?.data?.uid
}
])
}
};
state.newFeatures.features.data.push(feature);
state.newFeatures.features.ping();
state.selectedElement?.setData(feature);
comment.setData("");
created = true;
}
</script>
{#if notelayer === undefined}
<div class="alert">
This theme does not include the layer 'note'. As a result, no nodes can be created
</div>
{:else if created}
<div class="thanks">
<Tr t={Translations.t.notes.isCreated} />
</div>
{:else}
<h3>
<Tr t={Translations.t.notes.createNoteTitle}></Tr>
</h3>
{#if $isDisplayed}
<!-- The layer is displayed, so we can add a note without worrying for duplicates -->
{#if $hasFilter}
<div class="flex flex-col">
<!-- ...but a filter is set ...-->
<div class="alert">
<Tr t={ Translations.t.notes.noteLayerHasFilters}></Tr>
</div>
<SubtleButton on:click={() => notelayer.disableAllFilters()}>
<img slot="image" src="./assets/svg/filter.svg" class="w-8 h-8 mr-4">
<Tr slot="message" t={Translations.t.notes.disableAllNoteFilters}></Tr>
</SubtleButton>
</div>
{:else}
<div>
<Tr t={Translations.t.notes.createNoteIntro}></Tr>
<div class="border rounded-sm border-grey-500">
<div class="w-full p-1">
<ValidatedInput type="text" value={comment}></ValidatedInput>
</div>
<LoginToggle {state}>
<span slot="loading"><!--empty: don't show a loading message--></span>
<div slot="not-logged-in" class="alert">
<Tr t={Translations.t.notes.warnAnonymous} />
</div>
</LoginToggle>
{#if $comment.length >= 3}
<SubtleButton on:click={uploadNote}>
<img slot="image" src="./assets/svg/addSmall.svg" class="w-8 h-8 mr-4">
<Tr slot="message" t={ Translations.t.notes.createNote}></Tr>
</SubtleButton>
{:else}
<div class="alert">
<Tr t={ Translations.t.notes.textNeeded}></Tr>
</div>
{/if}
</div>
</div>
{/if}
{:else}
<div class="flex flex-col">
<div class="alert">
<Tr t={Translations.t.notes.noteLayerNotEnabled}></Tr>
</div>
<SubtleButton on:click={enableNoteLayer}>
<img slot="image" src="./assets/svg/layers.svg" class="w-8 h-8 mr-4">
<Tr slot="message" t={Translations.t.notes.noteLayerDoEnable}></Tr>
</SubtleButton>
</div>
{/if}
{/if}

View file

@ -127,7 +127,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
const allRenderings: BaseUIElement[] = [
new VariableUiElement(
tags
.map((data) => data[Tag.newlyCreated.key])
.map((data) => data["_newly_created"])
.map((isCreated) => {
if (isCreated === undefined) {
return undefined

View file

@ -20,7 +20,7 @@ import CreateWayWithPointReuseAction, {
MergePointConfig,
} from "../../Logic/Osm/Actions/CreateWayWithPointReuseAction"
import OsmChangeAction, { OsmCreateAction } from "../../Logic/Osm/Actions/OsmChangeAction"
import FeatureSource from "../../Logic/FeatureSource/FeatureSource"
import { FeatureSource } from "../../Logic/FeatureSource/FeatureSource"
import { OsmObject, OsmWay } from "../../Logic/Osm/OsmObject"
import { PresetInfo } from "../BigComponents/SimpleAddUI"
import { TagUtils } from "../../Logic/Tags/TagUtils"

View file

@ -36,6 +36,9 @@ export class MinimapViz implements SpecialVisualization {
keys.splice(0, 1)
const featuresToShow: Store<Feature[]> = state.indexedFeatures.featuresById.map(
(featuresById) => {
if (featuresById === undefined) {
return []
}
const properties = tagSource.data
const features: Feature[] = []
for (const key of keys) {

View file

@ -1,124 +0,0 @@
import Combine from "../Base/Combine"
import { UIEventSource } from "../../Logic/UIEventSource"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import Translations from "../i18n/Translations"
import Title from "../Base/Title"
import ValidatedTextField from "../Input/ValidatedTextField"
import { SubtleButton } from "../Base/SubtleButton"
import Svg from "../../Svg"
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
import Toggle from "../Input/Toggle"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"
import FilteredLayer from "../../Models/FilteredLayer"
import Hash from "../../Logic/Web/Hash"
export default class NewNoteUi extends Toggle {
constructor(
noteLayer: FilteredLayer,
isShown: UIEventSource<boolean>,
state: {
LastClickLocation: UIEventSource<{ lat: number; lon: number }>
osmConnection: OsmConnection
layoutToUse: LayoutConfig
featurePipeline: FeaturePipeline
selectedElement: UIEventSource<any>
}
) {
const t = Translations.t.notes
const isCreated = new UIEventSource(false)
state.LastClickLocation.addCallbackAndRun((_) => isCreated.setData(false)) // Reset 'isCreated' on every click
const text = ValidatedTextField.ForType("text").ConstructInputElement({
value: LocalStorageSource.Get("note-text"),
})
text.SetClass("border rounded-sm border-grey-500")
const postNote = new SubtleButton(Svg.addSmall_svg().SetClass("max-h-7"), t.createNote)
postNote.OnClickWithLoading(t.creating, async () => {
let txt = text.GetValue().data
if (txt === undefined || txt === "") {
return
}
txt += "\n\n #MapComplete #" + state?.layoutToUse?.id
const loc = state.LastClickLocation.data
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt)
const feature = {
type: "Feature",
geometry: {
type: "Point",
coordinates: [loc.lon, loc.lat],
},
properties: {
id: "" + id.id,
date_created: new Date().toISOString(),
_first_comment: txt,
comments: JSON.stringify([
{
text: txt,
html: txt,
user: state.osmConnection?.userDetails?.data?.name,
uid: state.osmConnection?.userDetails?.data?.uid,
},
]),
},
}
state?.featurePipeline?.InjectNewPoint(feature)
state.selectedElement?.setData(feature)
Hash.hash.setData(feature.properties.id)
text.GetValue().setData("")
isCreated.setData(true)
})
const createNoteDialog = new Combine([
new Title(t.createNoteTitle),
t.createNoteIntro,
text,
new Combine([
new Toggle(
undefined,
t.warnAnonymous.SetClass("block alert"),
state?.osmConnection?.isLoggedIn
),
new Toggle(
postNote,
t.textNeeded.SetClass("block alert"),
text.GetValue().map((txt) => txt?.length > 3)
),
]).SetClass("flex justify-end items-center"),
]).SetClass("flex flex-col border-2 border-black rounded-xl p-4")
const newNoteUi = new Toggle(
new Toggle(t.isCreated.SetClass("thanks"), createNoteDialog, isCreated),
undefined,
new UIEventSource<boolean>(true)
)
super(
new Toggle(
new Combine([
t.noteLayerHasFilters.SetClass("alert"),
new SubtleButton(Svg.filter_svg(), t.disableAllNoteFilters).onClick(() => {
const filters = noteLayer.appliedFilters.data
for (const key of Array.from(filters.keys())) {
filters.set(key, undefined)
}
noteLayer.appliedFilters.ping()
isShown.setData(false)
}),
]).SetClass("flex flex-col"),
newNoteUi,
noteLayer.appliedFilters.map((filters) => {
console.log("Applied filters for notes are: ", filters)
return Array.from(filters.values()).some((v) => v?.currentFilter !== undefined)
})
),
new Combine([
t.noteLayerNotEnabled.SetClass("alert"),
new SubtleButton(Svg.layers_svg(), t.noteLayerDoEnable).onClick(() => {
noteLayer.isDisplayed.setData(true)
isShown.setData(false)
}),
]).SetClass("flex flex-col"),
noteLayer.isDisplayed
)
}
}

35
UI/Popup/TagHint.svelte Normal file
View file

@ -0,0 +1,35 @@
<script lang="ts">
import { OsmConnection } from "../../Logic/Osm/OsmConnection";
import { TagsFilter } from "../../Logic/Tags/TagsFilter";
import FromHtml from "../Base/FromHtml.svelte";
import Constants from "../../Models/Constants.js";
import { Translation } from "../i18n/Translation";
import Tr from "../Base/Tr.svelte";
import { onDestroy } from "svelte";
/**
* A 'TagHint' will show the given tags in a human readable form.
* Depending on the options, it'll link through to the wiki or might be completely hidden
*/
export let osmConnection: OsmConnection;
/**
* If given, this function will be called to embed the given tags hint into this translation
*/
export let embedIn: (() => Translation) | undefined = undefined;
const userDetails = osmConnection.userDetails;
export let tags: TagsFilter;
let linkToWiki = false;
onDestroy(osmConnection.userDetails.addCallbackAndRunD(userdetails => {
linkToWiki = userdetails.csCount > Constants.userJourney.tagsVisibleAndWikiLinked;
}));
let tagsExplanation = "";
$: tagsExplanation = tags?.asHumanString(linkToWiki, false, {});
</script>
{#if $userDetails.loggedIn}
{#if embedIn === undefined}
<FromHtml src={tagsExplanation} />
{:else}
<Tr t={embedIn(tagsExplanation)} />
{/if}
{/if}

View file

@ -18,17 +18,23 @@
export let state: SpecialVisualizationState;
export let tags: UIEventSource<Record<string, string>>;
export let feature: Feature;
export let layer: LayerConfig
export let layer: LayerConfig;
let txt: string;
onDestroy(Locale.language.addCallbackAndRunD(l => {
$: onDestroy(Locale.language.addCallbackAndRunD(l => {
txt = t.textFor(l);
}));
let specs: RenderingSpecification[] = SpecialVisualizations.constructSpecification(txt);
let specs: RenderingSpecification[] = [];
$: {
if (txt !== undefined) {
specs = SpecialVisualizations.constructSpecification(txt);
}
}
</script>
{#each specs as specpart}
{#if typeof specpart === "string"}
<FromHtml src= {Utils.SubstituteKeys(specpart, $tags)}></FromHtml>
<FromHtml src={Utils.SubstituteKeys(specpart, $tags)}></FromHtml>
{:else if $tags !== undefined }
<ToSvelte construct={specpart.func.constr(state, tags, specpart.args, feature, layer)}></ToSvelte>
{/if}

View file

@ -17,6 +17,9 @@
export let state: SpecialVisualizationState;
export let selectedElement: Feature;
export let config: TagRenderingConfig;
if(config === undefined){
throw "Config is undefined in tagRenderingAnswer"
}
export let layer: LayerConfig
let trs: { then: Translation; icon?: string; iconClass?: string }[];
$: trs = Utils.NoNull(config?.GetRenderValues(_tags));

View file

@ -16,6 +16,7 @@
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
import { ExclamationIcon } from "@rgossiaux/svelte-heroicons/solid";
import SpecialTranslation from "./SpecialTranslation.svelte";
import TagHint from "../TagHint.svelte";
export let config: TagRenderingConfig;
export let tags: UIEventSource<Record<string, string>>;
@ -87,7 +88,9 @@
<div class="border border-black subtle-background flex flex-col">
<If condition={state.featureSwitchIsTesting}>
<div class="flex justify-between">
<SpecialTranslation t={config.question} {tags} {state} {layer} feature={selectedElement}></SpecialTranslation>
<span>
<SpecialTranslation t={config.question} {tags} {state} {layer} feature={selectedElement}></SpecialTranslation>
</span>
<span class="alert">{config.id}</span>
</div>
<SpecialTranslation slot="else" t={config.question} {tags} {state} {layer} feature={selectedElement}></SpecialTranslation>
@ -149,8 +152,7 @@
</div>
{/if}
<FromHtml src={selectedTags?.asHumanString(true, true, {})} />
<TagHint osmConnection={state.osmConnection} tags={selectedTags}></TagHint>
<div>
<!-- TagRenderingQuestion-buttons -->
<slot name="cancel"></slot>