forked from MapComplete/MapComplete
Merge develop
This commit is contained in:
commit
0969558b97
74 changed files with 1785 additions and 637 deletions
|
@ -43,11 +43,11 @@
|
|||
}
|
||||
}}
|
||||
>
|
||||
<div class="interactive sticky top-0 flex items-center justify-between">
|
||||
<TabList class="flex flex-wrap">
|
||||
<div class="tablist sticky top-0 flex items-center justify-center">
|
||||
<TabList class="flex items-center justify-center">
|
||||
{#if $$slots.title0}
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition0 && "hidden")}
|
||||
class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition0 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[0]} class="flex">
|
||||
<slot name="title0">Tab 0</slot>
|
||||
|
@ -56,7 +56,7 @@
|
|||
{/if}
|
||||
{#if $$slots.title1}
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition1 && "hidden")}
|
||||
class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition1 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[1]} class="flex">
|
||||
<slot name="title1" />
|
||||
|
@ -65,7 +65,7 @@
|
|||
{/if}
|
||||
{#if $$slots.title2}
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition2 && "hidden")}
|
||||
class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition2 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[2]} class="flex">
|
||||
<slot name="title2" />
|
||||
|
@ -74,7 +74,7 @@
|
|||
{/if}
|
||||
{#if $$slots.title3}
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition3 && "hidden")}
|
||||
class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition3 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[3]} class="flex">
|
||||
<slot name="title3" />
|
||||
|
@ -83,7 +83,7 @@
|
|||
{/if}
|
||||
{#if $$slots.title4}
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition4 && "hidden")}
|
||||
class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition4 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[4]} class="flex">
|
||||
<slot name="title4" />
|
||||
|
@ -92,7 +92,7 @@
|
|||
{/if}
|
||||
{#if $$slots.title5}
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition5 && "hidden")}
|
||||
class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition5 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[5]} class="flex">
|
||||
<slot name="title5" />
|
||||
|
@ -101,7 +101,7 @@
|
|||
{/if}
|
||||
{#if $$slots.title6}
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition6 && "hidden")}
|
||||
class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition6 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[6]} class="flex">
|
||||
<slot name="title6" />
|
||||
|
@ -167,19 +167,6 @@
|
|||
height: calc(100% - 2rem);
|
||||
}
|
||||
|
||||
:global(.tab) {
|
||||
margin: 0.25rem;
|
||||
padding: 0.25rem;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
:global(.tab .flex) {
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
:global(.tab span|div) {
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
|
@ -190,8 +177,4 @@
|
|||
fill: var(--interactive-contrast);
|
||||
}
|
||||
|
||||
:global(.tab-unselected) {
|
||||
background-color: var(--background-color) !important;
|
||||
color: var(--foreground-color) !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -55,15 +55,13 @@
|
|||
import ImageUploadQueue from "../../Logic/ImageProviders/ImageUploadQueue"
|
||||
import QueuedImagesView from "../Image/QueuedImagesView.svelte"
|
||||
import InsetSpacer from "../Base/InsetSpacer.svelte"
|
||||
import UserCircle from "@rgossiaux/svelte-heroicons/solid/UserCircle"
|
||||
import OfflineManagement from "./OfflineManagement.svelte"
|
||||
import { GlobeEuropeAfrica } from "@babeard/svelte-heroicons/solid/GlobeEuropeAfrica"
|
||||
import { onDestroy } from "svelte"
|
||||
import Avatar from "../Base/Avatar.svelte"
|
||||
import { SpecialVisualizationSvelte } from "../SpecialVisualization"
|
||||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import PendingChangesView from "./PendingChangesView.svelte"
|
||||
import { DevicePhoneMobileIcon } from "@babeard/svelte-heroicons/solid"
|
||||
|
||||
export let state: {
|
||||
favourites: FavouritesFeatureSource
|
||||
|
@ -236,6 +234,12 @@
|
|||
<Tr t={Translations.t.general.attribution.emailCreators} />
|
||||
</a>
|
||||
|
||||
{#if !$isAndroid}
|
||||
<a href="https://app.mapcomplete.org">
|
||||
<DevicePhoneMobileIcon class="h-6 w-6"/>
|
||||
<Tr t={Translations.t.general.menu.downloadApp}/>
|
||||
</a>
|
||||
{/if}
|
||||
<a class="flex" href="https://en.osm.town/@MapComplete" target="_blank">
|
||||
<Mastodon class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.attribution.followOnMastodon} />
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
import Translations from "../i18n/Translations"
|
||||
import type { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderingConfigJson"
|
||||
import { Or } from "../../Logic/Tags/Or"
|
||||
import { Utils } from "../../Utils"
|
||||
import ChartJs from "../Base/ChartJs.svelte"
|
||||
import { ChartJsUtils } from "../Base/ChartJsUtils"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export let onlyShowUsername: string[]
|
||||
export let features: Feature[]
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
import GeocodeResults from "./Search/GeocodeResults.svelte"
|
||||
import MagnifyingGlassCircle from "@babeard/svelte-heroicons/mini/MagnifyingGlassCircle"
|
||||
import type { GeocodeResult } from "../Logic/Search/GeocodingProvider"
|
||||
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui"
|
||||
import WikipediaTitle from "./Wikipedia/WikipediaTitle.svelte"
|
||||
import WikipediaArticle from "./Wikipedia/WikipediaArticle.svelte"
|
||||
|
||||
console.log("Loading inspector GUI")
|
||||
let username = QueryParameters.GetQueryParameter("user", undefined, "Inspect this user")
|
||||
|
@ -49,7 +52,7 @@
|
|||
new CoordinateSearch(),
|
||||
new OpenLocationCodeSearch(),
|
||||
new PhotonSearch(true, 2),
|
||||
new PhotonSearch()
|
||||
new PhotonSearch(),
|
||||
)
|
||||
let showSearchDrawer = new UIEventSource(true)
|
||||
let searchIsFocussed = new UIEventSource(false)
|
||||
|
@ -138,7 +141,7 @@
|
|||
const overpass = new Overpass(
|
||||
Constants.defaultOverpassUrls[0],
|
||||
undefined,
|
||||
user.split(";").map((user) => 'nw(user_touched:"' + user + '");')
|
||||
user.split(";").map((user) => "nw(user_touched:\"" + user + "\");"),
|
||||
)
|
||||
if (!maplibremap.bounds.data) {
|
||||
return
|
||||
|
@ -161,8 +164,6 @@
|
|||
return true
|
||||
})
|
||||
|
||||
let mode: "map" | "table" | "aggregate" | "images" = "map"
|
||||
|
||||
let showPreviouslyVisited = new UIEventSource(true)
|
||||
const t = Translations.t.inspector
|
||||
|
||||
|
@ -181,8 +182,8 @@
|
|||
|
||||
<div class="flex h-screen w-full flex-col">
|
||||
<div class="low-interaction flex flex-wrap items-center gap-x-2 p-2">
|
||||
<MagnifyingGlassCircle class="h-12 w-12" />
|
||||
<h1 class="m-0 mx-2 flex-shrink-0">
|
||||
<MagnifyingGlassCircle class="h-6 w-6" />
|
||||
<Tr t={t.title} />
|
||||
</h1>
|
||||
<ValidatedInput
|
||||
|
@ -207,89 +208,98 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<button class:primary={mode === "map"} on:click={() => (mode = "map")}>
|
||||
<Tr t={t.mapView} />
|
||||
</button>
|
||||
<button class:primary={mode === "table"} on:click={() => (mode = "table")}>
|
||||
<Tr t={t.tableView} />
|
||||
</button>
|
||||
<button class:primary={mode === "aggregate"} on:click={() => (mode = "aggregate")}>
|
||||
<Tr t={t.aggregateView} />
|
||||
</button>
|
||||
<button class:primary={mode === "images"} on:click={() => (mode = "images")}>
|
||||
<Tr t={t.images} />
|
||||
</button>
|
||||
</div>
|
||||
<TabGroup class="flex-grow flex flex-col">
|
||||
<TabList class="tablist">
|
||||
<Tab class={({ selected }) => ("tab "+ (selected ? "tab-selected" : "tab-unselected"))}>
|
||||
|
||||
{#if mode === "map"}
|
||||
{#if $selectedElement !== undefined}
|
||||
<!-- right modal with the selected element view -->
|
||||
<Drawer
|
||||
placement="right"
|
||||
transitionType="fly"
|
||||
activateClickOutside={false}
|
||||
backdrop={false}
|
||||
id="drawer-right"
|
||||
width="w-full md:w-6/12 lg:w-5/12 xl:w-4/12"
|
||||
rightOffset="inset-y-0 right-0"
|
||||
transitionParams={{
|
||||
<Tr t={t.mapView} />
|
||||
</Tab>
|
||||
<Tab class={({ selected }) => ("tab "+ (selected ? "tab-selected" : "tab-unselected"))}>
|
||||
|
||||
<Tr t={t.tableView} />
|
||||
</Tab>
|
||||
<Tab class={({ selected }) => ("tab "+ (selected ? "tab-selected" : "tab-unselected"))}>
|
||||
|
||||
<Tr t={t.aggregateView} />
|
||||
</Tab>
|
||||
<Tab class={({ selected }) => ("tab "+ (selected ? "tab-selected" : "tab-unselected"))}>
|
||||
|
||||
<Tr t={t.images} />
|
||||
</Tab>
|
||||
</TabList>
|
||||
<TabPanels class="flex-grow flex flex-col">
|
||||
|
||||
|
||||
<TabPanel class="flex-grow">
|
||||
{#if $selectedElement !== undefined}
|
||||
<!-- right modal with the selected element view -->
|
||||
<Drawer
|
||||
placement="right"
|
||||
transitionType="fly"
|
||||
activateClickOutside={false}
|
||||
backdrop={false}
|
||||
id="drawer-right"
|
||||
width="w-full md:w-6/12 lg:w-5/12 xl:w-4/12"
|
||||
rightOffset="inset-y-0 right-0"
|
||||
transitionParams={{
|
||||
x: 640,
|
||||
duration: 0,
|
||||
easing: linear,
|
||||
}}
|
||||
divClass="overflow-y-auto z-50 bg-white"
|
||||
hidden={$selectedElement === undefined}
|
||||
on:close={() => {
|
||||
divClass="overflow-y-auto z-50 bg-white"
|
||||
hidden={$selectedElement === undefined}
|
||||
on:close={() => {
|
||||
selectedElement.setData(undefined)
|
||||
}}
|
||||
>
|
||||
<TitledPanel>
|
||||
<div slot="title" class="flex justify-between">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href={"https://osm.org/" + $selectedElement.properties.id}
|
||||
>
|
||||
{$selectedElement.properties.id}
|
||||
</a>
|
||||
<XCircleIcon class="h-6 w-6" on:click={() => selectedElement.set(undefined)} />
|
||||
</div>
|
||||
>
|
||||
<TitledPanel>
|
||||
<div slot="title" class="flex justify-between">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href={"https://osm.org/" + $selectedElement.properties.id}
|
||||
>
|
||||
{$selectedElement.properties.id}
|
||||
</a>
|
||||
<XCircleIcon class="h-6 w-6" on:click={() => selectedElement.set(undefined)} />
|
||||
</div>
|
||||
|
||||
<History onlyShowChangesBy={$username.split(";")} id={$selectedElement.properties.id} />
|
||||
</TitledPanel>
|
||||
</Drawer>
|
||||
{/if}
|
||||
|
||||
<div class="relative m-1 flex-grow overflow-hidden rounded-xl">
|
||||
<MaplibreMap {map} mapProperties={maplibremap} autorecovery={true} />
|
||||
<div class="absolute right-0 top-0 w-1/4 p-4">
|
||||
<Searchbar
|
||||
on:search={() => search()}
|
||||
isFocused={searchIsFocussed}
|
||||
value={searchvalue}
|
||||
on:focus={() => state.searchState.showSearchDrawer.set(true)}
|
||||
/>
|
||||
{#if $searchSuggestions?.length > 0 || $searchIsFocussed}
|
||||
<GeocodeResults {state} on:select={(event) => search(event.detail)} />
|
||||
<History onlyShowChangesBy={$username.split(";")} id={$selectedElement.properties.id} />
|
||||
</TitledPanel>
|
||||
</Drawer>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else if mode === "table"}
|
||||
<div class="m-2 h-full overflow-y-auto">
|
||||
{#each $featuresStore as f}
|
||||
<History onlyShowChangesBy={$username?.split(";")} id={f.properties.id} />
|
||||
{/each}
|
||||
</div>
|
||||
{:else if mode === "aggregate"}
|
||||
<div class="m-2 h-full overflow-y-auto">
|
||||
<AggregateView features={$featuresStore} onlyShowUsername={$username?.split(";")} />
|
||||
</div>
|
||||
{:else if mode === "images"}
|
||||
<div class="m-2 h-full overflow-y-auto">
|
||||
<AggregateImages features={$featuresStore} onlyShowUsername={$username?.split(";")} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="relative m-1 h-full flex-grow overflow-hidden rounded-xl">
|
||||
<MaplibreMap {map} mapProperties={maplibremap} autorecovery={true} />
|
||||
<div class="absolute right-0 top-0 w-1/4 p-4">
|
||||
<Searchbar
|
||||
on:search={() => search()}
|
||||
isFocused={searchIsFocussed}
|
||||
value={searchvalue}
|
||||
on:focus={() => state.searchState.showSearchDrawer.set(true)}
|
||||
/>
|
||||
{#if $searchSuggestions?.length > 0 || $searchIsFocussed}
|
||||
<GeocodeResults {state} on:select={(event) => search(event.detail)} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel class="overflow-auto grow-0">
|
||||
{#each $featuresStore as f}
|
||||
<History onlyShowChangesBy={$username?.split(";")} id={f.properties.id} />
|
||||
{/each}
|
||||
</TabPanel>
|
||||
<TabPanel >
|
||||
<AggregateView features={$featuresStore} onlyShowUsername={$username?.split(";")} />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<AggregateImages features={$featuresStore} onlyShowUsername={$username?.split(";")} />
|
||||
</TabPanel>
|
||||
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
</div>
|
||||
|
||||
<Page shown={showPreviouslyVisited}>
|
||||
|
|
|
@ -30,6 +30,8 @@ import Tr from "../Base/Tr.svelte"
|
|||
import Combine from "../Base/Combine"
|
||||
import Marker from "../Map/Marker.svelte"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
class DirectionIndicatorVis extends SpecialVisualizationSvelte {
|
||||
funcName = "direction_indicator"
|
||||
|
@ -243,17 +245,24 @@ class PresetTypeSelect extends SpecialVisualizationSvelte {
|
|||
console.warn("Trying to use the _original_ layer")
|
||||
layer = state.theme.layers.find((l) => l.id === layer._basedOn) ?? layer
|
||||
}
|
||||
|
||||
const allKeys = Lists.dedup(layer.presets.flatMap(preset => preset.tags.flatMap(tag => tag.usedKeys())))
|
||||
|
||||
const question: QuestionableTagRenderingConfigJson = {
|
||||
id: layer.id + "-type",
|
||||
question: t.question.translations,
|
||||
mappings: layer.presets.map((pr) => ({
|
||||
if: new And(pr.tags).asJson(),
|
||||
icon: "auto",
|
||||
then: (pr.description ? t.typeDescription : t.typeTitle).Subs({
|
||||
title: pr.title,
|
||||
description: pr.description,
|
||||
}).translations,
|
||||
})),
|
||||
mappings: layer.presets.map((pr) => {
|
||||
const presetKeys = new Set(pr.tags.flatMap(t => t.key))
|
||||
const keysToRemove = allKeys.filter(k => !presetKeys.has(k)).map(k => new Tag(k, ""))
|
||||
return ({
|
||||
if: new And([...pr.tags, ...keysToRemove]).asJson(),
|
||||
icon: "auto",
|
||||
then: (pr.description ? t.typeDescription : t.typeTitle).Subs({
|
||||
title: pr.title,
|
||||
description: pr.description,
|
||||
}).translations,
|
||||
})
|
||||
}),
|
||||
}
|
||||
if (question.mappings.length === 0) {
|
||||
console.error("No mappings for preset_type_select, something went wrong")
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
import { Utils } from "../../../Utils"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import EditButton from "./EditButton.svelte"
|
||||
import { Strings } from "../../../Utils/Strings"
|
||||
|
||||
export let config: TagRenderingConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
|
@ -83,7 +84,7 @@
|
|||
onDestroy(highlightedRendering?.addCallbackAndRun(() => setHighlighting()))
|
||||
onDestroy(_htmlElement.addCallbackAndRun(() => setHighlighting()))
|
||||
}
|
||||
let answerId = "answer-" + Utils.randomString(5)
|
||||
let answerId = "answer-" + Strings.randomString(5)
|
||||
let debug = state?.featureSwitches?.featureSwitchIsDebugging ?? new ImmutableStore(false)
|
||||
|
||||
let apiState: Store<string> = state?.osmConnection?.apiIsOnline ?? new ImmutableStore("online")
|
||||
|
|
|
@ -44,8 +44,9 @@
|
|||
}
|
||||
|
||||
function getAutoIcon(mapping: { readonly if?: TagsFilter }): Readonly<Record<string, string>> {
|
||||
const ifTags = TagUtils.removeEmptyParts(mapping.if)
|
||||
for (const preset of layer.presets) {
|
||||
if (!new And(preset.tags).shadows(mapping.if)) {
|
||||
if (!new And(preset.tags).shadows(ifTags)) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { ConfigMeta } from "./configMeta"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||
import {
|
||||
Conversion,
|
||||
ConversionMessage,
|
||||
DesugaringContext,
|
||||
Pipe,
|
||||
} from "../../Models/ThemeConfig/Conversion/Conversion"
|
||||
import { Conversion, ConversionMessage, DesugaringContext, Pipe } from "../../Models/ThemeConfig/Conversion/Conversion"
|
||||
import { PrepareLayer } from "../../Models/ThemeConfig/Conversion/PrepareLayer"
|
||||
import { PrevalidateTheme, ValidateLayer } from "../../Models/ThemeConfig/Conversion/Validation"
|
||||
import { AllSharedLayers } from "../../Customizations/AllSharedLayers"
|
||||
|
@ -26,6 +21,7 @@ import { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderi
|
|||
import { ValidateTheme } from "../../Models/ThemeConfig/Conversion/ValidateTheme"
|
||||
import * as questions from "../../../public/assets/generated/layers/questions.json"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
import { Strings } from "../../Utils/Strings"
|
||||
|
||||
export interface HighlightedTagRendering {
|
||||
path: ReadonlyArray<string | number>
|
||||
|
@ -431,7 +427,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
}
|
||||
if (!tr["id"] && !tr["override"]) {
|
||||
const qtr = <QuestionableTagRenderingConfigJson>tr
|
||||
let id = "" + i + "_" + Utils.randomString(5)
|
||||
let id = "" + i + "_" + Strings.randomString(5)
|
||||
if (qtr?.freeform?.key) {
|
||||
id = qtr?.freeform?.key
|
||||
} else if (qtr.mappings?.[0]?.if) {
|
||||
|
|
|
@ -31,9 +31,9 @@
|
|||
<WikipediaArticle wikipediaDetails={_wikipediaStores[0]} />
|
||||
{:else}
|
||||
<TabGroup>
|
||||
<TabList>
|
||||
<TabList class="tablist">
|
||||
{#each _wikipediaStores as store (store.tag)}
|
||||
<Tab class={({ selected }) => (selected ? "tab-selected" : "tab-unselected")}>
|
||||
<Tab class={({ selected }) => ("tab "+ (selected ? "tab-selected" : "tab-unselected"))}>
|
||||
<WikipediaTitle wikipediaDetails={store} />
|
||||
</Tab>
|
||||
{/each}
|
||||
|
@ -51,14 +51,5 @@
|
|||
|
||||
<style>
|
||||
/* Actually used, don't remove*/
|
||||
.tab-selected {
|
||||
background-color: rgb(59 130 246);
|
||||
color: rgb(255 255 255);
|
||||
}
|
||||
|
||||
/* Actually used, don't remove*/
|
||||
.tab-unselected {
|
||||
background-color: rgb(255 255 255);
|
||||
color: rgb(0 0 0);
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue