Refactoring: use theme instead of layout in a lot of places

This commit is contained in:
Pieter Vander Vennet 2024-10-17 04:06:03 +02:00
parent 9427083939
commit bdc9ba52a6
104 changed files with 445 additions and 449 deletions

View file

@ -12,7 +12,7 @@
import Constants from "../Models/Constants"
import { ImmutableStore, Store, Stores, UIEventSource } from "../Logic/UIEventSource"
import ThemesList from "./BigComponents/ThemesList.svelte"
import { MinimalLayoutInformation } from "../Models/ThemeConfig/LayoutConfig"
import { MinimalThemeInformation } from "../Models/ThemeConfig/ThemeConfig"
import Eye from "../assets/svg/Eye.svelte"
import LoginButton from "./Base/LoginButton.svelte"
import Mastodon from "../assets/svg/Mastodon.svelte"
@ -46,16 +46,16 @@
let searchIsFocused = new UIEventSource(true)
const officialThemes: MinimalLayoutInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === false)
const hiddenThemes: MinimalLayoutInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === true)
let visitedHiddenThemes: Store<MinimalLayoutInformation[]> = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection)
const officialThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === false)
const hiddenThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === true)
let visitedHiddenThemes: Store<MinimalThemeInformation[]> = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection)
.map((knownIds) => hiddenThemes.filter((theme) =>
knownIds.indexOf(theme.id) >= 0 || state.osmConnection.userDetails.data.name === "Pieter Vander Vennet"
))
const customThemes: Store<MinimalLayoutInformation[]> = Stores.ListStabilized<string>(state.installedUserThemes)
const customThemes: Store<MinimalThemeInformation[]> = Stores.ListStabilized<string>(state.installedUserThemes)
.mapD(stableIds => Utils.NoNullInplace(stableIds.map(id => state.getUnofficialTheme(id))))
function filtered(themes: Store<MinimalLayoutInformation[]>): Store<MinimalLayoutInformation[]> {
function filtered(themes: Store<MinimalThemeInformation[]>): Store<MinimalThemeInformation[]> {
return searchStable.map(search => {
if (!search) {
return themes.data
@ -74,9 +74,9 @@
}
let officialSearched : Store<MinimalLayoutInformation[]>= filtered(new ImmutableStore(officialThemes))
let hiddenSearched: Store<MinimalLayoutInformation[]> = filtered(visitedHiddenThemes)
let customSearched: Store<MinimalLayoutInformation[]> = filtered(customThemes)
let officialSearched : Store<MinimalThemeInformation[]>= filtered(new ImmutableStore(officialThemes))
let hiddenSearched: Store<MinimalThemeInformation[]> = filtered(visitedHiddenThemes)
let customSearched: Store<MinimalThemeInformation[]> = filtered(customThemes)
let searchIsFocussed = new UIEventSource(false)

View file

@ -6,7 +6,7 @@
export let state: SpecialVisualizationState
let layoutToUse = state.layout
let layoutToUse = state.theme
let iconAttributions: string[] = layoutToUse.getUsedImages()
const allLicenses = {}

View file

@ -18,7 +18,7 @@
export let state: SpecialVisualizationState
const t = Translations.t.general.attribution
const layoutToUse = state.layout
const layoutToUse = state.theme
let maintainer: Translation = undefined
if (layoutToUse.credits !== undefined && layoutToUse.credits !== "") {
@ -122,7 +122,7 @@
{/if}
{#if maintainer !== undefined}
<div class="flex items-center gap-x-2">
<Marker icons={state.layout.icon} size="h-8 w-8 shrink-0" />
<Marker icons={state.theme.icon} size="h-8 w-8 shrink-0" />
<Tr t={maintainer} />
</div>
{/if}

View file

@ -9,8 +9,8 @@
import Icon from "../Map/Icon.svelte"
export let state: SpecialVisualizationState
let theme = state.layout?.id ?? ""
let config: ExtraLinkConfig = state.layout.extraLink
let theme = state.theme?.id ?? ""
let config: ExtraLinkConfig = state.theme.extraLink
let basepath = window.location.host
let showWelcomeMessageSwitch = state.featureSwitches.featureSwitchWelcomeMessage
const isIframe = Utils.isIframe
@ -42,7 +42,7 @@
{#if config.text}
<Tr t={config.text} />
{:else}
<Tr t={t.screenToSmall.Subs({ theme: state.layout.title })} />
<Tr t={t.screenToSmall.Subs({ theme: state.theme.title })} />
{/if}
</a>
</div>

View file

@ -14,7 +14,7 @@
export let state: ThemeViewState
export let onlyLink: boolean
let layout = state.layout
let theme = state.theme
let allEnabled: boolean
let allDisabled: boolean
@ -70,7 +70,7 @@
</div>
</div>
{#each layout.layers as layer}
{#each theme.layers as layer}
<Filterview
{state}
zoomlevel={state.mapProperties.zoom}
@ -79,7 +79,7 @@
/>
{/each}
{#each layout.tileLayerSources as tilesource}
{#each theme.tileLayerSources as tilesource}
<OverlayToggle
layerproperties={tilesource}
state={state.overlayLayerStates.get(tilesource.id)}

View file

@ -56,7 +56,7 @@
let usersettingslayer = new LayerConfig(<LayerConfigJson>usersettings, "usersettings", true)
let layout = state.layout
let theme = state.theme
let featureSwitches = state.featureSwitches
let showHome = featureSwitches.featureSwitchBackToThemeOverview
let pg = state.guistate.pageStates
@ -108,7 +108,7 @@
<!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it -->
<LoginToggle {state}>
<div class="flex flex-col" slot="not-logged-in">
<LanguagePicker availableLanguages={layout.language} />
<LanguagePicker availableLanguages={theme.language} />
<Tr cls="alert" t={Translations.t.userinfo.notLoggedIn} />
<LoginButton clss="primary" osmConnection={state.osmConnection} />
</div>
@ -161,12 +161,12 @@
<Page {onlyLink} shown={pg.about_theme}>
<svelte:fragment slot="link">
<Marker size="h-7 w-7" icons={layout.icon} />
<Marker size="h-7 w-7" icons={theme.icon} />
<Tr t={t.showIntroduction} />
</svelte:fragment>
<svelte:fragment slot="header">
<Marker size="h-8 w-8 mr-3" icons={layout.icon} />
<Tr t={layout.title} />
<Marker size="h-8 w-8 mr-3" icons={theme.icon} />
<Tr t={theme.title} />
</svelte:fragment>
<ThemeIntroPanel {state} />
</Page>
@ -193,25 +193,25 @@
</Page>
{/if}
{#if layout.official}
{#if theme.official}
<a
class="flex"
href={"https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/" +
layout.id +
theme.id +
".md"}
target="_blank"
>
<DocumentMagnifyingGlass class="h-6 w-6" />
<Tr
t={Translations.t.general.attribution.openThemeDocumentation.Subs({
name: layout.title,
name: theme.title,
})}
/>
</a>
<a class="flex" href={Utils.OsmChaLinkFor(31, layout.id)} target="_blank">
<a class="flex" href={Utils.OsmChaLinkFor(31, theme.id)} target="_blank">
<DocumentChartBar class="h-6 w-6" />
<Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: layout.title })} />
<Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: theme.title })} />
</a>
{/if}
</SidebarUnit>

View file

@ -25,8 +25,8 @@
* In some cases (local deploys, custom themes), we need to set the URL to `/theme.html?layout=xyz` instead of `/xyz?...`
* Note that the 'layout='-param will be included automatically
*/
let needsThemeRedirect = url.port !== "" || url.hostname.match(/^[0-9]/) || !state.layout.official
let layoutId = state.layout.id
let needsThemeRedirect = url.port !== "" || url.hostname.match(/^[0-9]/) || !state.theme.official
let layoutId = state.theme.id
let baseLink = `${url.protocol}//${url.host}/${needsThemeRedirect ? "theme.html" : layoutId}?`
let showWelcomeMessage = true
@ -44,7 +44,7 @@
enableBackground: boolean,
enableGeolocation: boolean
) {
const layout = state.layout
const layout = state.theme
let excluded = Utils.NoNull([
showWelcomeMessage ? undefined : "fs-welcome-message",
enableLogin ? undefined : "fs-enable-login",
@ -99,7 +99,7 @@
${
enableGeolocation ? 'allow="geolocation"' : ""
} width="100%" height="100%" style="min-width: 250px; min-height: 250px"
title="${state.layout.title?.txt ?? "MapComplete"} with MapComplete">
title="${state.theme.title?.txt ?? "MapComplete"} with MapComplete">
</iframe>`
Array.from(state.layerState.filteredLayers.values()).forEach((flayer) =>
@ -163,7 +163,7 @@
<Tr t={tr.stateIsIncluded} />
<a
class="inline-block w-fit cursor-pointer"
on:click={() => state.guistate.filtersPanelIsOpened.set(true)}
on:click={() => state.guistate.pageStates.filter.set(true)}
>
<Tr t={tr.openLayers} />
</a>

View file

@ -10,7 +10,7 @@
export let i: number = undefined
let id = feature.properties.id
let tags = state.featureProperties.getStore(id)
let layer: LayerConfig = state.layout.getMatchingLayer(tags.data)
let layer: LayerConfig = state.theme.getMatchingLayer(tags.data)
</script>
<span class="inline-flex gap-x-1">

View file

@ -1,12 +1,12 @@
<script lang="ts">
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import type { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
import type { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations"
import Marker from "../Map/Marker.svelte"
export let theme: MinimalLayoutInformation & {isOfficial?: boolean}
export let theme: MinimalThemeInformation & {isOfficial?: boolean}
let isCustom: boolean = theme.id.startsWith("https://") || theme.id.startsWith("http://")
export let state: { layoutToUse?: { id: string }; osmConnection: OsmConnection }

View file

@ -14,7 +14,7 @@
* The theme introduction panel
*/
export let state: ThemeViewState
let layout = state.layout
let theme = state.theme
let geolocation = state.geolocation.geolocationState
let geopermission: Store<GeolocationPermissionState> = geolocation.permission
@ -42,16 +42,16 @@
<div>
<!-- Intro, description, ... -->
<Tr t={layout.description} />
<Tr t={theme.description} />
<If condition={state.featureSwitches.featureSwitchEnableLogin}>
<Tr t={Translations.t.general.welcomeExplanation.general} />
{#if layout.layers.some((l) => l.presets?.length > 0)}
{#if theme.layers.some((l) => l.presets?.length > 0)}
<Tr t={Translations.t.general.welcomeExplanation.addNew} />
{/if}
</If>
<Tr t={layout.descriptionTail} />
<Tr t={theme.descriptionTail} />
<!-- Buttons: open map, go to location, search -->
<NextButton

View file

@ -4,12 +4,12 @@
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { UIEventSource } from "../../Logic/UIEventSource"
import ThemeButton from "./ThemeButton.svelte"
import { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
import { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
export let search: UIEventSource<string>
export let themes: MinimalLayoutInformation[]
export let themes: MinimalThemeInformation[]
export let state: { osmConnection: OsmConnection }
export let hasSelection : boolean = true

View file

@ -3,7 +3,7 @@
import Translations from "../i18n/Translations"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { Translation } from "../i18n/Translation"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import Invalid from "../../assets/svg/Invalid.svelte"
import Tr from "../Base/Tr.svelte"
@ -17,7 +17,7 @@
export let trace: (title: string) => string
export let state: {
layout: LayoutConfig
layout: ThemeConfig
osmConnection: OsmConnection
readonly featureSwitchUserbadge: Store<boolean>
}

View file

@ -37,7 +37,7 @@
new Tag(key, externalProperties[key]),
tags.data,
{
theme: state.layout.id,
theme: state.theme.id,
changeType: "import",
}
)

View file

@ -42,7 +42,7 @@
currentStep = "applying_all"
const tagsToApply = missing.data.map((k) => new Tag(k, externalProperties[k]))
const change = new ChangeTagAction(tags.data.id, new And(tagsToApply), tags.data, {
theme: state.layout.id,
theme: state.theme.id,
changeType: "import",
})
await state.changes.applyChanges(await change.CreateChangeDescriptions())

View file

@ -32,7 +32,7 @@
const gpsIsDisplayed = gpsLayer.isDisplayed.data
try {
gpsLayer.isDisplayed.setData(false)
const name = state.layout.id
const name = state.theme.id
const title = `MapComplete_${name}_export_${new Date()
.toISOString()

View file

@ -156,7 +156,7 @@ export default class DownloadHelper {
private getCleanGeoJsonPerLayer(includeMetaData: boolean): Map<string, Feature[]> {
const state = this._state
const featuresPerLayer = new Map<string, any[]>()
const neededLayers = state.layout.layers.filter((l) => l.source !== null).map((l) => l.id)
const neededLayers = state.theme.layers.filter((l) => l.source !== null).map((l) => l.id)
const bbox = state.mapProperties.bounds.data
for (const neededLayer of neededLayers) {
@ -186,7 +186,7 @@ export default class DownloadHelper {
createImage(key: string, width: string, height: string): HTMLImageElement {
const img = document.createElement("img")
const sources = {
layouticon: this._state.layout.icon,
layouticon: this._state.theme.icon,
}
img.src = sources[key]
if (!img.src) {

View file

@ -38,10 +38,10 @@
return downloadHelper.createImage(key, width, height)
},
textSubstitutions: <Record<string, string | Translation>>{
"layout.title": state.layout.title,
layoutid: state.layout.id,
title: state.layout.title,
layoutImg: state.layout.icon,
"layout.title": state.theme.title,
layoutid: state.theme.id,
title: state.theme.title,
layoutImg: state.theme.icon,
version: Constants.vNumber,
date: new Date().toISOString().substring(0, 16),
background: new Translation(bg.properties.name).txt,

View file

@ -6,7 +6,7 @@ import { Tag } from "../../Logic/Tags/Tag"
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
import { Changes } from "../../Logic/Osm/Changes"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import SvelteUIElement from "../Base/SvelteUIElement"
import Delete_icon from "../../assets/svg/Delete_icon.svelte"
@ -14,7 +14,7 @@ export default class DeleteImage extends Toggle {
constructor(
key: string,
tags: Store<any>,
state: { layout: LayoutConfig; changes?: Changes; osmConnection?: OsmConnection }
state: { theme: ThemeConfig; changes?: Changes; osmConnection?: OsmConnection }
) {
const oldValue = tags.data[key]
const isDeletedBadge = Translations.t.image.isDeleted
@ -25,7 +25,7 @@ export default class DeleteImage extends Toggle {
await state?.changes?.applyAction(
new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data, {
changeType: "delete-image",
theme: state.layout.id,
theme: state.theme.id,
})
)
})
@ -40,7 +40,7 @@ export default class DeleteImage extends Toggle {
await state?.changes?.applyAction(
new ChangeTagAction(tags.data.id, new Tag(key, ""), tags.data, {
changeType: "answer",
theme: state.layout.id,
theme: state.theme.id,
})
)
})

View file

@ -7,7 +7,7 @@ import Toggle from "../Input/Toggle"
import ImageProvider, { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { Changes } from "../../Logic/Osm/Changes"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import SvelteUIElement from "../Base/SvelteUIElement"
import AttributedImage from "./AttributedImage.svelte"
@ -18,7 +18,7 @@ export class ImageCarousel extends Toggle {
state: {
osmConnection?: OsmConnection
changes?: Changes
layout: LayoutConfig
theme: ThemeConfig
previewedImage?: UIEventSource<ProvidedImage>
}
) {

View file

@ -45,7 +45,7 @@
const url = targetValue
if (isLinked) {
const action = new LinkImageAction(currentTags.id, key, url, tags, {
theme: tags.data._orig_theme ?? state.layout.id,
theme: tags.data._orig_theme ?? state.theme.id,
changeType: "link-image"
})
await state.changes.applyAction(action)
@ -54,7 +54,7 @@
const v = currentTags[k]
if (v === url) {
const action = new ChangeTagAction(currentTags.id, new Tag(k, ""), currentTags, {
theme: tags.data._orig_theme ?? state.layout.id,
theme: tags.data._orig_theme ?? state.theme.id,
changeType: "remove-image"
})
state.changes.applyAction(action)

View file

@ -119,7 +119,7 @@
ShowDataLayer.showMultipleLayers(
map,
new StaticFeatureSource([feature]),
state.layout.layers,
state.theme.layers,
)
onDestroy(

View file

@ -39,7 +39,7 @@
const maproulette_id = tags.data[maproulette_id_key] ?? tags.data.mr_taskId ?? tags.data.id
try {
await Maproulette.singleton.closeTask(Number(maproulette_id), Number(statusToSet), {
tags: `MapComplete MapComplete:${state.layout.id}`,
tags: `MapComplete MapComplete:${state.theme.id}`,
comment: feedback,
})
tags.data["mr_taskStatus"] = Maproulette.STATUS_MEANING[Number(statusToSet)]

View file

@ -114,7 +114,7 @@
}
const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {
theme: state.layout?.id ?? "unkown",
theme: state.theme?.id ?? "unkown",
changeType: "create",
snapOnto: snapToWay,
reusePointWithinMeters: 1,

View file

@ -1,5 +1,5 @@
<script lang="ts">
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { createEventDispatcher } from "svelte"
import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig"
import Tr from "../../Base/Tr.svelte"
@ -19,7 +19,7 @@
* This component lists all the presets and allows the user to select one
*/
export let state: SpecialVisualizationState
let layout: LayoutConfig = state.layout
let layout: ThemeConfig = state.theme
let presets: {
preset: PresetConfig
layer: LayerConfig

View file

@ -10,7 +10,7 @@ import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeature
import { VariableUiElement } from "../Base/VariableUIElement"
import Loading from "../Base/Loading"
import Translations from "../i18n/Translations"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../Logic/Osm/Changes"
import { UIElement } from "../UIElement"
import FilteredLayer from "../../Models/FilteredLayer"
@ -32,7 +32,7 @@ export interface AutoAction extends SpecialVisualization {
applyActionOn(
feature: Feature,
state: {
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
},
@ -282,7 +282,7 @@ export default class AutoApplyButton implements SpecialVisualization {
argument: string[]
): BaseUIElement {
try {
if (!state.layout.official && !state.featureSwitchIsTesting.data) {
if (!state.theme.official && !state.featureSwitchIsTesting.data) {
const t = Translations.t.general.add.import
return new Combine([
new FixedUiElement(

View file

@ -65,7 +65,7 @@
featureId,
deleteConfig.softDeletionTags,
{
theme: state?.layout?.id ?? "unknown",
theme: state?.theme?.id ?? "unknown",
specialMotivation: deleteReason,
},
canBeDeleted.data
@ -73,7 +73,7 @@
} else {
// no _delete_reason is given, which implies that this is _not_ a deletion but merely a retagging via a nonDeleteMapping
actionToTake = new ChangeTagAction(featureId, selectedTags, tags.data, {
theme: state?.layout?.id ?? "unkown",
theme: state?.theme?.id ?? "unkown",
changeType: "special-delete",
})
}

View file

@ -1,7 +1,6 @@
import { SpecialVisualization, SpecialVisualizationState } from "../../SpecialVisualization"
import { UIEventSource } from "../../../Logic/UIEventSource"
import { Feature, Geometry, LineString, Polygon } from "geojson"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import BaseUIElement from "../../BaseUIElement"
import { ImportFlowArguments, ImportFlowUtils } from "./ImportFlow"
import Translations from "../../i18n/Translations"
@ -12,7 +11,7 @@ import ConflateImportFlowState from "./ConflateImportFlowState"
import { AutoAction } from "../AutoApplyButton"
import { IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource"
import { Changes } from "../../../Logic/Osm/Changes"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { OsmConnection } from "../../../Logic/Osm/OsmConnection"
export interface ConflateFlowArguments extends ImportFlowArguments {
@ -47,7 +46,7 @@ export default class ConflateImportButtonViz implements SpecialVisualization, Au
feature: Feature<Geometry, { [name: string]: any }>,
state: {
osmConnection: OsmConnection
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
},

View file

@ -10,7 +10,7 @@ import { GeoOperations } from "../../../Logic/GeoOperations"
import { TagUtils } from "../../../Logic/Tags/TagUtils"
import { MergePointConfig } from "../../../Logic/Osm/Actions/CreateWayWithPointReuseAction"
import { And } from "../../../Logic/Tags/And"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../../Logic/Osm/Changes"
import { FeatureSource, IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource"
import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
@ -61,7 +61,7 @@ export default class ConflateImportFlowState extends ImportFlow<ConflateFlowArgu
args: ConflateFlowArguments,
state: {
osmConnection: OsmConnection
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource
@ -88,7 +88,7 @@ export default class ConflateImportFlowState extends ImportFlow<ConflateFlowArgu
GeoOperations.removeOvernoding(feature),
idOfFeatureToReplaceGeometry,
{
theme: state.layout.id,
theme: state.theme.id,
newTags: tagsToApply.data,
}
)

View file

@ -174,7 +174,7 @@ export default abstract class ImportFlow<ArgT extends ImportFlowArguments> {
return { error: t.hasBeenImported }
}
if (!state.layout.official && !isTesting) {
if (!state.theme.official && !isTesting) {
// Unofficial theme - imports not allowed
return {
error: t.officialThemesOnly,
@ -183,7 +183,7 @@ export default abstract class ImportFlow<ArgT extends ImportFlowArguments> {
}
if (this.targetLayer === undefined) {
const e = `Target layer not defined: error in import button for theme: ${this.state.layout.id}: layer ${this.args.targetLayer} not found`
const e = `Target layer not defined: error in import button for theme: ${this.state.theme.id}: layer ${this.args.targetLayer} not found`
console.error(e)
return { error: new Translation({ "*": e }) }
}

View file

@ -15,7 +15,7 @@
// The following variables are used for the map
const targetLayers: LayerConfig[] = args.targetLayer
.split(" ")
.map((tl) => state.layout.layers.find((l) => l.id === tl))
.map((tl) => state.theme.layers.find((l) => l.id === tl))
const snapToLayers: string[] | undefined =
args.snap_onto_layers?.split(",")?.map((l) => l.trim()) ?? []
const maxSnapDistance: number = Number(args.max_snap_distance ?? 25) ?? 25

View file

@ -63,7 +63,7 @@ export class PointImportFlowState extends ImportFlow<PointImportFlowArguments> {
}
const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {
theme: this.state.layout.id,
theme: this.state.theme.id,
changeType: "import",
snapOnto: <OsmWay>snapOnto,
specialMotivation: specialMotivation,

View file

@ -10,7 +10,7 @@ import { FixedUiElement } from "../../Base/FixedUiElement"
import WayImportFlow from "./WayImportFlow.svelte"
import WayImportFlowState, { WayImportFlowArguments } from "./WayImportFlowState"
import { Utils } from "../../../Utils"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../../Logic/Osm/Changes"
import { IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource"
import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
@ -87,7 +87,7 @@ export default class WayImportButtonViz implements AutoAction, SpecialVisualizat
public async applyActionOn(
feature: Feature,
state: {
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
fullNodeDatabase: FullNodeDatabaseSource

View file

@ -32,7 +32,7 @@
ShowDataLayer.showMultipleLayers(
map,
new StaticFeatureSource([importFlow.originalFeature]),
state.layout.layers,
state.theme.layers,
{ zoomToFeatures: false }
)

View file

@ -11,7 +11,7 @@ import { TagUtils } from "../../../Logic/Tags/TagUtils"
import { OsmCreateAction, PreviewableAction } from "../../../Logic/Osm/Actions/OsmChangeAction"
import { FeatureSource, IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource"
import CreateMultiPolygonWithPointReuseAction from "../../../Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../../Logic/Osm/Changes"
import FullNodeDatabaseSource from "../../../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
@ -52,7 +52,7 @@ export default class WayImportFlowState extends ImportFlow<WayImportFlowArgument
feature: Feature<LineString | Polygon>,
args: WayImportFlowArguments,
state: {
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource

View file

@ -60,7 +60,7 @@
} else if (state.changes) {
await state.changes.applyAction(
new ChangeTagAction(tags.data.id, new And(selection), tags.data, {
theme: state?.layout?.id ?? "unkown",
theme: state?.theme?.id ?? "unkown",
changeType: "answer",
})
)

View file

@ -19,7 +19,7 @@
const t = Translations.t.favouritePoi
function markFavourite(isFavourite: boolean) {
state.favourites.markAsFavourite(feature, layer.id, state.layout.id, tags, isFavourite)
state.favourites.markAsFavourite(feature, layer.id, state.theme.id, tags, isFavourite)
}
</script>

View file

@ -20,7 +20,7 @@
const t = Translations.t.favouritePoi
function markFavourite(isFavourite: boolean) {
state.favourites.markAsFavourite(feature, layer.id, state.layout.id, tags, isFavourite)
state.favourites.markAsFavourite(feature, layer.id, state.theme.id, tags, isFavourite)
}
</script>

View file

@ -72,7 +72,7 @@
ShowDataLayer.showMultipleLayers(
mlmap,
new StaticFeatureSource(featuresToShow),
state.layout.layers,
state.theme.layers,
{ zoomToFeatures: true }
)
</script>

View file

@ -94,7 +94,7 @@ export class MoveWizardState {
const matchingPresets = this.layer.presets.filter(preset => preset.preciseInput.snapToLayers && new And(preset.tags).matchesProperties(tags))
const matchingPreset = matchingPresets.flatMap(pr => pr.preciseInput?.snapToLayers)
for (const layerId of matchingPreset) {
const snapOntoLayer = this._state.layout.getLayer(layerId)
const snapOntoLayer = this._state.theme.getLayer(layerId)
const text = <Translation> t.reasons.reasonSnapTo.PartialSubsTr("name", snapOntoLayer.snapName)
reasons.push({
text,
@ -133,7 +133,7 @@ export class MoveWizardState {
snappedTo,
{
reason: reason.changesetCommentValue,
theme: state.layout.id,
theme: state.theme.id,
}),
)
featureToMove.properties._lat = loc.lat
@ -152,7 +152,7 @@ export class MoveWizardState {
featureToMove.properties,
{
changeType: "relocated",
theme: state.layout.id,
theme: state.theme.id,
},
),
)

View file

@ -64,7 +64,7 @@ class MultiApplyExecutor {
const keysToChange = this.params.keysToApply
const overwrite = this.params.overwrite
const selfTags = this.params.tagsSource.data
const theme = this.params.state.layout.id
const theme = this.params.state.theme.id
for (const id of featuresToChange) {
const tagsToApply: Tag[] = []
const otherFeatureTags = allElements.getStore(id).data

View file

@ -47,7 +47,7 @@
return
}
const loc = coordinate.data
txt += "\n\n #MapComplete #" + state?.layout?.id
txt += "\n\n #MapComplete #" + state?.theme?.id
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt)
console.log("Created a note, got id", id)
const feature = <Feature<Point, OsmTags>>{

View file

@ -49,7 +49,7 @@ export class PlantNetDetectionViz implements SpecialVisualization {
]),
tags.data,
{
theme: state.layout.id,
theme: state.theme.id,
changeType: "plantnet-ai-detection",
}
)

View file

@ -17,7 +17,7 @@
let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
const includeLayout = window.location.pathname.split("/").at(-1).startsWith("theme")
const layout = includeLayout ? "layout=" + state.layout.id + "&" : ""
const layout = includeLayout ? "layout=" + state.theme.id + "&" : ""
let id: Store<string> = tags.mapD((tags) => tags.id)
let url = id.mapD(
(id) =>

View file

@ -30,9 +30,9 @@ export class ShareLinkViz implements SpecialVisualization {
const text = args[1]
const generateShareData = () => {
const title = state?.layout?.title?.txt ?? "MapComplete"
const title = state?.theme?.title?.txt ?? "MapComplete"
let matchingLayer: LayerConfig = state?.layout?.getMatchingLayer(tagSource?.data)
let matchingLayer: LayerConfig = state?.theme?.getMatchingLayer(tagSource?.data)
let name =
matchingLayer?.title?.GetRenderValue(tagSource.data)?.Subs(tagSource.data)?.txt ??
tagSource.data?.name ??
@ -49,7 +49,7 @@ export class ShareLinkViz implements SpecialVisualization {
return {
title: name,
url: url,
text: state?.layout?.shortDescription?.txt ?? "MapComplete",
text: state?.theme?.shortDescription?.txt ?? "MapComplete",
}
}

View file

@ -58,7 +58,7 @@
id,
splitPoints.data.map((ff) => <[number, number]>(<Point>ff.geometry).coordinates),
{
theme: state?.layout?.id,
theme: state?.theme?.id,
},
5
)

View file

@ -144,7 +144,7 @@ export default class TagApplyButton implements AutoAction, SpecialVisualization
new And(tagsToApply.data),
tags.data, // We pass in the tags of the selected element, not the tags of the target element!
{
theme: state.layout.id,
theme: state.theme.id,
changeType: "answer",
}
)

View file

@ -283,7 +283,7 @@
}
dispatch("saved", { config, applied: selectedTags })
const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, {
theme: tags.data["_orig_theme"] ?? state.layout.id,
theme: tags.data["_orig_theme"] ?? state.theme.id,
changeType: "answer",
})
freeformInput.set(undefined)
@ -327,7 +327,7 @@
function clearAnswer() {
const tagsToSet = settableKeys.data.map(k => new Tag(k, ""))
const change = new ChangeTagAction(tags.data.id, new And(tagsToSet), tags.data, {
theme: tags.data["_orig_theme"] ?? state.layout.id,
theme: tags.data["_orig_theme"] ?? state.theme.id,
changeType: "answer",
})
freeformInput.set(undefined)

View file

@ -5,7 +5,7 @@ import List from "./Base/List"
import Translations from "./i18n/Translations"
import { QueryParameters } from "../Logic/Web/QueryParameters"
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
import MarkdownUtils from "../Utils/MarkdownUtils"
@ -27,7 +27,7 @@ export default class QueryParameterDocumentation {
]
public static UrlParamDocs(): Map<string, string> {
const dummyLayout = new LayoutConfig(<any>{
const dummyLayout = new ThemeConfig(<any>{
id: "&gt;theme&lt;",
title: { en: "<theme>" },
description: "A theme to generate docs with",

View file

@ -20,7 +20,7 @@
let tags: UIEventSource<Record<string, string>>
let descriptionTr: TagRenderingConfig = undefined
if (entry.feature?.properties?.id) {
layer = state.layout.getMatchingLayer(entry.feature.properties)
layer = state.theme.getMatchingLayer(entry.feature.properties)
tags = state.featureProperties.getStore(entry.feature.properties.id)
descriptionTr = layer?.tagRenderings?.find(tr => tr.labels.indexOf("description") >= 0)
}

View file

@ -1,11 +1,11 @@
<script lang="ts">
import { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
import { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import { Translation } from "../i18n/Translation"
import Icon from "../Map/Icon.svelte"
import Tr from "../Base/Tr.svelte"
import ThemeSearch from "../../Logic/Search/ThemeSearch"
export let entry: MinimalLayoutInformation
export let entry: MinimalThemeInformation
let otherTheme = entry
</script>
{#if entry}

View file

@ -14,7 +14,7 @@
export let state: SpecialVisualizationState
let searchTerm = state.searchState.searchTerm
let recentThemes = state.userRelatedState.recentlyVisitedThemes.value.map(themes => themes.filter(th => th !== state.layout.id).slice(0, 6))
let recentThemes = state.userRelatedState.recentlyVisitedThemes.value.map(themes => themes.filter(th => th !== state.theme.id).slice(0, 6))
let themeResults = state.searchState.themeSuggestions
const t =Translations.t.general.search

View file

@ -1,6 +1,6 @@
import { Store, UIEventSource } from "../Logic/UIEventSource"
import BaseUIElement from "./BaseUIElement"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
import { Changes } from "../Logic/Osm/Changes"
@ -19,7 +19,7 @@ import FavouritesFeatureSource from "../Logic/FeatureSource/Sources/FavouritesFe
import { ProvidedImage } from "../Logic/ImageProviders/ImageProvider"
import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler"
import { SummaryTileSourceRewriter } from "../Logic/FeatureSource/TiledFeatureSource/SummaryTileSource"
import LayoutSource from "../Logic/FeatureSource/Sources/LayoutSource"
import ThemeSource from "../Logic/FeatureSource/Sources/ThemeSource"
import { Map as MlMap } from "maplibre-gl"
import ShowDataLayer from "./Map/ShowDataLayer"
import { CombinedFetcher } from "../Logic/Web/NearbyImagesSearch"
@ -33,13 +33,13 @@ import FeaturePropertiesStore from "../Logic/FeatureSource/Actors/FeaturePropert
*/
export interface SpecialVisualizationState {
readonly guistate: MenuState
readonly layout: LayoutConfig
readonly theme: ThemeConfig
readonly featureSwitches: FeatureSwitchState
readonly layerState: LayerState
readonly featureProperties: FeaturePropertiesStore
readonly indexedFeatures: IndexedFeatureSource & LayoutSource
readonly indexedFeatures: IndexedFeatureSource & ThemeSource
/**
* Some features will create a new element that should be displayed.
* These can be injected by appending them to this featuresource (and pinging it)

View file

@ -164,7 +164,7 @@ class StealViz implements SpecialVisualization {
const tagRenderings: [LayerConfig, TagRenderingConfig][] = []
for (const layerAndTagRenderingId of layerAndtagRenderingIds.split(";")) {
const [layerId, tagRenderingId] = layerAndTagRenderingId.trim().split(".")
const layer = state.layout.layers.find((l) => l.id === layerId)
const layer = state.theme.layers.find((l) => l.id === layerId)
const tagRendering = layer.tagRenderings.find((tr) => tr.id === tagRenderingId)
tagRenderings.push([layer, tagRendering])
}
@ -444,7 +444,7 @@ export default class SpecialVisualizations {
Locale.showLinkToWeblate.map((showTranslations) => {
const languages = showTranslations
? LanguageUtils.usedLanguagesSorted
: state.layout.language
: state.theme.language
return new SvelteUIElement(LanguagePicker, {
assignTo: state.userRelatedState.language,
availableLanguages: languages,
@ -971,7 +971,7 @@ export default class SpecialVisualizations {
return undefined
}
const allUnits: Unit[] = [].concat(
...(state?.layout?.layers?.map((lyr) => lyr.units) ?? [])
...(state?.theme?.layers?.map((lyr) => lyr.units) ?? [])
)
const unit = allUnits.filter((unit) =>
unit.isApplicableToKey(key)
@ -1125,7 +1125,7 @@ export default class SpecialVisualizations {
) =>
new VariableUiElement(
tagsSource.map((tags) => {
if (state.layout === undefined) {
if (state.theme === undefined) {
return "<feature title>"
}
const title = layer?.title?.GetRenderValue(tags)
@ -1276,7 +1276,7 @@ export default class SpecialVisualizations {
constr: (state) => {
return new Combine(
state.layout.layers
state.theme.layers
.filter(
(l) =>
l.name !== null &&
@ -1995,7 +1995,7 @@ export default class SpecialVisualizations {
layer: LayerConfig
): BaseUIElement {
const translation = tagSource.map((tags) => {
const layer = state.layout.getMatchingLayer(tags)
const layer = state.theme.getMatchingLayer(tags)
return layer?.getMostMatchingPreset(tags)?.description
})
return new VariableUiElement(translation)

View file

@ -11,7 +11,7 @@ import BaseUIElement from "./BaseUIElement"
import Title from "./Base/Title"
import { FixedUiElement } from "./Base/FixedUiElement"
import List from "./Base/List"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import mcChanges from "../../src/assets/generated/themes/mapcomplete-changes.json"
import SvelteUIElement from "./Base/SvelteUIElement"
import Filterview from "./BigComponents/Filterview.svelte"
@ -24,7 +24,7 @@ import { Feature } from "geojson"
class StatsticsForOverviewFile extends Combine {
constructor(homeUrl: string, paths: string[]) {
paths = paths.filter((p) => !p.endsWith("file-overview.json"))
const layer = new LayoutConfig(<any>mcChanges, true).layers[0]
const layer = new ThemeConfig(<any>mcChanges, true).layers[0]
const filteredLayer = new FilteredLayer(layer)
const filterPanel = new Combine([
new Title("Filters"),

View file

@ -18,7 +18,7 @@ import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import { OsmTags } from "../../Models/OsmFeature"
import { Feature, Point } from "geojson"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import { LayoutConfigJson } from "../../Models/ThemeConfig/Json/LayoutConfigJson"
import { ThemeConfigJson } from "../../Models/ThemeConfig/Json/ThemeConfigJson"
import { PrepareTheme } from "../../Models/ThemeConfig/Conversion/PrepareTheme"
import { ConversionContext } from "../../Models/ThemeConfig/Conversion/ConversionContext"
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
@ -334,7 +334,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
return 0
}
}
public readonly layout: { getMatchingLayer: (key: any) => LayerConfig }
public readonly theme: { getMatchingLayer: (key: any) => LayerConfig }
public readonly featureSwitches: {
featureSwitchIsDebugging: UIEventSource<boolean>
}
@ -359,7 +359,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
options: { expertMode: UIEventSource<boolean> }
) {
super(schema, server, "layers", osmConnection, options)
this.layout = {
this.theme = {
getMatchingLayer: () => {
try {
return new LayerConfig(<LayerConfigJson>this.configuration.data, "dynamic")
@ -458,7 +458,8 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
}
const state: DesugaringContext = {
tagRenderings: sharedQuestions,
sharedLayers: layers
sharedLayers: layers,
tagRenderingOrder: []
}
const prepare = this.buildValidation(state)
const context = ConversionContext.construct([], ["prepare"])
@ -472,7 +473,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
}
}
export class EditThemeState extends EditJsonState<LayoutConfigJson> {
export class EditThemeState extends EditJsonState<ThemeConfigJson> {
constructor(
schema: ConfigMeta[],
server: StudioServer,
@ -483,7 +484,7 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
this.setupFixers()
}
protected buildValidation(state: DesugaringContext): Conversion<LayoutConfigJson, any> {
protected buildValidation(state: DesugaringContext): Conversion<ThemeConfigJson, any> {
return new Pipe(
new PrevalidateTheme(),
new Pipe(
@ -513,7 +514,7 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
})
}
protected async validate(configuration: Partial<LayoutConfigJson>) {
protected async validate(configuration: Partial<ThemeConfigJson>) {
const layers = AllSharedLayers.getSharedLayersConfigs()
for (const l of configuration.layers ?? []) {
@ -534,7 +535,8 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
}
const state: DesugaringContext = {
tagRenderings: sharedQuestions,
sharedLayers: layers
sharedLayers: layers,
tagRenderingOrder: []
}
const prepare = this.buildValidation(state)
const context = ConversionContext.construct([], ["prepare"])
@ -542,7 +544,7 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
Utils.NoNullInplace(configuration.layers)
}
try {
prepare.convert(<LayoutConfigJson>configuration, context)
prepare.convert(<ThemeConfigJson>configuration, context)
} catch (e) {
console.error(e)
context.err(e)

View file

@ -2,7 +2,7 @@ import { Utils } from "../../Utils"
import Constants from "../../Models/Constants"
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { LayoutConfigJson } from "../../Models/ThemeConfig/Json/LayoutConfigJson"
import { ThemeConfigJson } from "../../Models/ThemeConfig/Json/ThemeConfigJson"
/**
* A small class wrapping around the Server API.
@ -71,12 +71,12 @@ export default class StudioServer {
}
async fetch(layerId: string, category: "layers", uid?: number): Promise<LayerConfigJson>
async fetch(layerId: string, category: "themes", uid?: number): Promise<LayoutConfigJson>
async fetch(layerId: string, category: "themes", uid?: number): Promise<ThemeConfigJson>
async fetch(
layerId: string,
category: "layers" | "themes",
uid?: number
): Promise<LayerConfigJson | LayoutConfigJson> {
): Promise<LayerConfigJson | ThemeConfigJson> {
try {
return <any>await Utils.downloadJson(this.urlFor(layerId, category, uid))
} catch (e) {

View file

@ -53,7 +53,7 @@
export let state: ThemeViewState
let layout = state.layout
let theme = state.theme
let maplibremap: UIEventSource<MlMap> = state.map
let state_selectedElement = state.selectedElement
let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined)
@ -65,7 +65,7 @@
let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation
let debug = state.featureSwitches.featureSwitchIsDebugging
let featureSwitches: FeatureSwitchState = state.featureSwitches
let currentViewLayer: LayerConfig = layout.layers.find((l) => l.id === "current_view")
let currentViewLayer: LayerConfig = theme.layers.find((l) => l.id === "current_view")
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
let currentZoom = state.mapProperties.zoom
let showCrosshair = state.userRelatedState.showCrosshair
@ -213,7 +213,7 @@
<div class="flex w-full items-end justify-between px-4">
<div class="flex flex-col">
<If condition={featureSwitches.featureSwitchEnableLogin}>
{#if $addNewFeatureMode.indexOf("button") >= 0 && ((state.layout.hasPresets() && state.layout.enableAddNewPoints) || state.layout.hasNoteLayer())}
{#if $addNewFeatureMode.indexOf("button") >= 0 && ((state.theme.hasPresets() && state.theme.enableAddNewPoints) || state.theme.hasNoteLayer())}
<button
class="low-interaction pointer-events-auto w-fit"
class:disabled={$currentZoom < Constants.minZoomLevelToAddNewPoint}
@ -224,7 +224,7 @@
>
{#if $currentZoom < Constants.minZoomLevelToAddNewPoint}
<Tr t={Translations.t.general.add.zoomInFurther} />
{:else if state.layout.hasPresets()}
{:else if state.theme.hasPresets()}
<Tr t={Translations.t.general.add.title} />
{:else}
<Tr t={Translations.t.notes.addAComment} />
@ -351,9 +351,9 @@
<div
class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 mr-2"
>
<Marker icons={layout.icon} size="h-6 w-6 shrink-0 mr-0.5 sm:mr-1 md:mr-2" />
<Marker icons={theme.icon} size="h-6 w-6 shrink-0 mr-0.5 sm:mr-1 md:mr-2" />
<b class="mr-1">
<Tr t={layout.title} />
<Tr t={theme.title} />
</b>
</div>
</MapControlButton>