forked from MapComplete/MapComplete
Chore: formatting
This commit is contained in:
parent
35eff07c80
commit
c08fe03ed0
422 changed files with 31594 additions and 43019 deletions
|
|
@ -66,104 +66,104 @@
|
|||
</script>
|
||||
|
||||
<main>
|
||||
<div class="m-4 flex flex-col">
|
||||
<LanguagePicker
|
||||
clss="self-end max-w-full"
|
||||
assignTo={state.language}
|
||||
availableLanguages={t.title.SupportedLanguages()}
|
||||
preferredLanguages={userLanguages}
|
||||
/>
|
||||
<div class="m-4 flex flex-col">
|
||||
<LanguagePicker
|
||||
clss="self-end max-w-full"
|
||||
assignTo={state.language}
|
||||
availableLanguages={t.title.SupportedLanguages()}
|
||||
preferredLanguages={userLanguages}
|
||||
/>
|
||||
|
||||
<div class="mt-4 flex">
|
||||
<div class="m-3 flex-none">
|
||||
<Logo alt="MapComplete Logo" class="h-12 w-12 sm:h-24 sm:w-24" />
|
||||
<div class="mt-4 flex">
|
||||
<div class="m-3 flex-none">
|
||||
<Logo alt="MapComplete Logo" class="h-12 w-12 sm:h-24 sm:w-24" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<h1 class="m-0 font-extrabold tracking-tight md:text-6xl">
|
||||
<Tr t={t.title} />
|
||||
</h1>
|
||||
<Tr
|
||||
cls="my-4 mr-4 text-base font-semibold sm:text-lg md:mt-5 md:text-xl lg:mx-0"
|
||||
t={Translations.t.index.intro}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<h1 class="m-0 font-extrabold tracking-tight md:text-6xl">
|
||||
<Tr t={t.title} />
|
||||
</h1>
|
||||
<Tr
|
||||
cls="my-4 mr-4 text-base font-semibold sm:text-lg md:mt-5 md:text-xl lg:mx-0"
|
||||
t={Translations.t.index.intro}
|
||||
/>
|
||||
<form
|
||||
class="flex justify-center"
|
||||
on:submit|preventDefault={(_) => MoreScreen.applySearch(themeSearchText.data)}
|
||||
>
|
||||
<label
|
||||
class="neutral-label my-2 flex w-full items-center rounded-full border-2 border-black sm:w-1/2"
|
||||
>
|
||||
<SearchIcon aria-hidden="true" class="h-8 w-8" />
|
||||
<input
|
||||
autofocus
|
||||
bind:value={$themeSearchText}
|
||||
class="mr-4 w-full outline-none"
|
||||
id="theme-search"
|
||||
type="search"
|
||||
use:placeholder={tr.searchForATheme}
|
||||
/>
|
||||
</label>
|
||||
</form>
|
||||
|
||||
<ThemesList search={themeSearchText} {state} themes={MoreScreen.officialThemes} />
|
||||
|
||||
<LoginToggle {state}>
|
||||
<ThemesList
|
||||
hideThemes={false}
|
||||
isCustom={false}
|
||||
search={themeSearchText}
|
||||
{state}
|
||||
themes={$visitedHiddenThemes}
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<h3>
|
||||
<Tr t={tr.previouslyHiddenTitle} />
|
||||
</h3>
|
||||
<p>
|
||||
<Tr
|
||||
t={tr.hiddenExplanation.Subs({
|
||||
hidden_discovered: $visitedHiddenThemes.length.toString(),
|
||||
total_hidden: hiddenThemes.length.toString(),
|
||||
})}
|
||||
/>
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
</ThemesList>
|
||||
|
||||
<UnofficialThemeList search={themeSearchText} {state} />
|
||||
|
||||
<div slot="not-logged-in">
|
||||
<button class="m-0 my-2 w-full" on:click={() => osmConnection.AttemptLogin()}>
|
||||
<Login class="mr-2 h-6 w-6 " />
|
||||
<Tr t={Translations.t.index.logIn} />
|
||||
</button>
|
||||
</div>
|
||||
<a
|
||||
class="button h-fit w-full"
|
||||
href={window.location.protocol + "//" + window.location.host + "/studio.html"}
|
||||
>
|
||||
<Pencil class="mr-2 h-6 w-6" />
|
||||
<Tr t={Translations.t.general.morescreen.createYourOwnTheme} />
|
||||
</a>
|
||||
|
||||
<a
|
||||
class="button h-fit w-full"
|
||||
href={window.location.protocol + "//" + window.location.host + "/privacy.html"}
|
||||
>
|
||||
<Eye class="mr-2 h-6 w-6" />
|
||||
<Tr t={Translations.t.privacy.title} />
|
||||
</a>
|
||||
</LoginToggle>
|
||||
|
||||
<Tr cls="link-underline" t={Translations.t.general.aboutMapComplete.intro} />
|
||||
<Tr t={tr.streetcomplete} />
|
||||
|
||||
<div class="subtle mb-16 self-end">
|
||||
v{Constants.vNumber}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form
|
||||
class="flex justify-center"
|
||||
on:submit|preventDefault={(_) => MoreScreen.applySearch(themeSearchText.data)}
|
||||
>
|
||||
<label
|
||||
class="neutral-label my-2 flex w-full items-center rounded-full border-2 border-black sm:w-1/2"
|
||||
>
|
||||
<SearchIcon aria-hidden="true" class="h-8 w-8" />
|
||||
<input
|
||||
autofocus
|
||||
bind:value={$themeSearchText}
|
||||
class="mr-4 w-full outline-none"
|
||||
id="theme-search"
|
||||
type="search"
|
||||
use:placeholder={tr.searchForATheme}
|
||||
/>
|
||||
</label>
|
||||
</form>
|
||||
|
||||
<ThemesList search={themeSearchText} {state} themes={MoreScreen.officialThemes} />
|
||||
|
||||
<LoginToggle {state}>
|
||||
<ThemesList
|
||||
hideThemes={false}
|
||||
isCustom={false}
|
||||
search={themeSearchText}
|
||||
{state}
|
||||
themes={$visitedHiddenThemes}
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<h3>
|
||||
<Tr t={tr.previouslyHiddenTitle} />
|
||||
</h3>
|
||||
<p>
|
||||
<Tr
|
||||
t={tr.hiddenExplanation.Subs({
|
||||
hidden_discovered: $visitedHiddenThemes.length.toString(),
|
||||
total_hidden: hiddenThemes.length.toString(),
|
||||
})}
|
||||
/>
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
</ThemesList>
|
||||
|
||||
<UnofficialThemeList search={themeSearchText} {state} />
|
||||
|
||||
<div slot="not-logged-in">
|
||||
<button class="m-0 my-2 w-full" on:click={() => osmConnection.AttemptLogin()}>
|
||||
<Login class="mr-2 h-6 w-6 " />
|
||||
<Tr t={Translations.t.index.logIn} />
|
||||
</button>
|
||||
</div>
|
||||
<a
|
||||
class="button h-fit w-full"
|
||||
href={window.location.protocol + "//" + window.location.host + "/studio.html"}
|
||||
>
|
||||
<Pencil class="mr-2 h-6 w-6" />
|
||||
<Tr t={Translations.t.general.morescreen.createYourOwnTheme} />
|
||||
</a>
|
||||
|
||||
<a
|
||||
class="button h-fit w-full"
|
||||
href={window.location.protocol + "//" + window.location.host + "/privacy.html"}
|
||||
>
|
||||
<Eye class="mr-2 h-6 w-6" />
|
||||
<Tr t={Translations.t.privacy.title} />
|
||||
</a>
|
||||
</LoginToggle>
|
||||
|
||||
<Tr cls="link-underline" t={Translations.t.general.aboutMapComplete.intro} />
|
||||
<Tr t={tr.streetcomplete} />
|
||||
|
||||
<div class="subtle mb-16 self-end">
|
||||
v{Constants.vNumber}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -42,16 +42,16 @@ export default class ChartJs<
|
|||
this._config = config
|
||||
}
|
||||
|
||||
public static ConstructDoughnut(data: Record<string, number>){
|
||||
public static ConstructDoughnut(data: Record<string, number>) {
|
||||
const borderColor = [
|
||||
// ChartJsColours.unkownBorderColor,
|
||||
// ChartJsColours.otherBorderColor,
|
||||
// ChartJsColours.notApplicableBorderColor,
|
||||
// ChartJsColours.unkownBorderColor,
|
||||
// ChartJsColours.otherBorderColor,
|
||||
// ChartJsColours.notApplicableBorderColor,
|
||||
]
|
||||
const backgroundColor = [
|
||||
// ChartJsColours.unkownColor,
|
||||
// ChartJsColours.otherColor,
|
||||
// ChartJsColours.notApplicableColor,
|
||||
// ChartJsColours.unkownColor,
|
||||
// ChartJsColours.otherColor,
|
||||
// ChartJsColours.notApplicableColor,
|
||||
]
|
||||
|
||||
let i = 0
|
||||
|
|
@ -59,10 +59,10 @@ export default class ChartJs<
|
|||
const bg = ChartJsColours.backgroundColors
|
||||
|
||||
for (const key in data) {
|
||||
if(key === ""){
|
||||
if (key === "") {
|
||||
borderColor.push(ChartJsColours.unknownBorderColor)
|
||||
backgroundColor.push(ChartJsColours.unknownColor)
|
||||
}else{
|
||||
} else {
|
||||
borderColor.push(borders[i % borders.length])
|
||||
backgroundColor.push(bg[i % bg.length])
|
||||
i++
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
export let isOpened: Store<boolean>
|
||||
export let moveTo: Store<HTMLElement>
|
||||
|
||||
export let debug : string
|
||||
export let debug: string
|
||||
function copySizeOf(htmlElem: HTMLElement) {
|
||||
const target = htmlElem.getBoundingClientRect()
|
||||
elem.style.left = target.x + "px"
|
||||
|
|
@ -31,18 +31,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
onDestroy(isOpened.addCallback(opened => animate(opened)))
|
||||
onMount(() => requestAnimationFrame(() => animate(isOpened.data)))
|
||||
|
||||
|
||||
onDestroy(isOpened.addCallback((opened) => animate(opened)))
|
||||
onMount(() => requestAnimationFrame(() => animate(isOpened.data)))
|
||||
</script>
|
||||
<div class={"absolute bottom-0 right-0 h-full w-screen p-4 md:p-6 pointer-events-none invisible"}>
|
||||
|
||||
<div class={"pointer-events-none invisible absolute bottom-0 right-0 h-full w-screen p-4 md:p-6"}>
|
||||
<div class="content h-full" bind:this={targetOuter} style="background: red" />
|
||||
</div>
|
||||
|
||||
<div bind:this={elem} class="pointer-events-none absolute bottom-0 right-0 low-interaction rounded-2xl"
|
||||
style="transition: all 0.5s ease-out, background-color 1.4s ease-out; background: var(--background-color);">
|
||||
<div
|
||||
bind:this={elem}
|
||||
class="low-interaction pointer-events-none absolute bottom-0 right-0 rounded-2xl"
|
||||
style="transition: all 0.5s ease-out, background-color 1.4s ease-out; background: var(--background-color);"
|
||||
>
|
||||
<!-- Classes should be the same as the 'floatoaver' -->
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
* The slotted element will be shown on top, with a lower-opacity border
|
||||
*/
|
||||
const dispatch = createEventDispatcher<{ close }>()
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Draw the background over the total screen -->
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
export let osmConnection: OsmConnection
|
||||
export let clss: string | undefined = undefined
|
||||
|
||||
if(osmConnection === undefined){
|
||||
if (osmConnection === undefined) {
|
||||
console.error("No osmConnection passed into loginButton")
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
/**
|
||||
* Only show the 'successful' state, don't show loading or error messages
|
||||
*/
|
||||
export let silentFail : boolean = false
|
||||
export let silentFail: boolean = false
|
||||
let loadingStatus = state?.osmConnection?.loadingStatus ?? new ImmutableStore("logged-in")
|
||||
let badge = state?.featureSwitches?.featureSwitchEnableLogin ?? new ImmutableStore(true)
|
||||
const t = Translations.t.general
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
export let enabled: Store<boolean> = new ImmutableStore(true)
|
||||
export let arialabel: Translation = undefined
|
||||
export let htmlElem: UIEventSource<HTMLElement> = undefined
|
||||
let _htmlElem : HTMLElement
|
||||
let _htmlElem: HTMLElement
|
||||
$: {
|
||||
htmlElem?.setData(_htmlElem)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,15 @@
|
|||
|
||||
export let src: string = undefined
|
||||
export let srcWritable: Store<string> = undefined
|
||||
srcWritable?.addCallbackAndRunD(t => {
|
||||
srcWritable?.addCallbackAndRunD((t) => {
|
||||
src = t
|
||||
})
|
||||
if(src !== undefined && typeof src !== "string") {
|
||||
if (src !== undefined && typeof src !== "string") {
|
||||
console.trace("Got a non-string object in Markdown", src)
|
||||
throw "Markdown.svelte got a non-string object"
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if src?.length > 0}
|
||||
{@html marked.parse(src)}
|
||||
{/if}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import { default as turndown } from "turndown"
|
|||
import { Utils } from "../../Utils"
|
||||
|
||||
export default class TableOfContents {
|
||||
|
||||
|
||||
private static asLinkableId(text: string): string {
|
||||
return text
|
||||
?.replace(/ /g, "-")
|
||||
?.replace(/[?#.;:/]/, "")
|
||||
?.toLowerCase() ?? ""
|
||||
return (
|
||||
text
|
||||
?.replace(/ /g, "-")
|
||||
?.replace(/[?#.;:/]/, "")
|
||||
?.toLowerCase() ?? ""
|
||||
)
|
||||
}
|
||||
|
||||
private static mergeLevel(
|
||||
|
|
@ -33,7 +33,7 @@ export default class TableOfContents {
|
|||
if (running.length !== undefined) {
|
||||
result.push({
|
||||
content: new List(running),
|
||||
level: maxLevel - 1
|
||||
level: maxLevel - 1,
|
||||
})
|
||||
running = []
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ export default class TableOfContents {
|
|||
if (running.length !== undefined) {
|
||||
result.push({
|
||||
content: new List(running),
|
||||
level: maxLevel - 1
|
||||
level: maxLevel - 1,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -56,13 +56,16 @@ export default class TableOfContents {
|
|||
const firstTitle = structure[1]
|
||||
let minDepth = undefined
|
||||
do {
|
||||
minDepth = Math.min(...structure.map(s => s.depth))
|
||||
const minDepthCount = structure.filter(s => s.depth === minDepth)
|
||||
minDepth = Math.min(...structure.map((s) => s.depth))
|
||||
const minDepthCount = structure.filter((s) => s.depth === minDepth)
|
||||
if (minDepthCount.length > 1) {
|
||||
break
|
||||
}
|
||||
// Erase a single top level heading
|
||||
structure.splice(structure.findIndex(s => s.depth === minDepth), 1)
|
||||
structure.splice(
|
||||
structure.findIndex((s) => s.depth === minDepth),
|
||||
1
|
||||
)
|
||||
} while (structure.length > 0)
|
||||
|
||||
if (structure.length <= 1) {
|
||||
|
|
@ -71,7 +74,7 @@ export default class TableOfContents {
|
|||
const separators = {
|
||||
1: " -",
|
||||
2: " +",
|
||||
3: " *"
|
||||
3: " *",
|
||||
}
|
||||
|
||||
let toc = ""
|
||||
|
|
@ -96,15 +99,16 @@ export default class TableOfContents {
|
|||
const splitPoint = intro.lastIndexOf("\n")
|
||||
|
||||
return md.substring(0, splitPoint) + toc + md.substring(splitPoint)
|
||||
|
||||
}
|
||||
|
||||
public static generateStructure(html: Element): { depth: number, title: string, el: Element }[] {
|
||||
public static generateStructure(
|
||||
html: Element
|
||||
): { depth: number; title: string; el: Element }[] {
|
||||
if (html === undefined) {
|
||||
return []
|
||||
}
|
||||
return [].concat(...Array.from(html.childNodes ?? []).map(
|
||||
child => {
|
||||
return [].concat(
|
||||
...Array.from(html.childNodes ?? []).map((child) => {
|
||||
const tag: string = child["tagName"]?.toLowerCase()
|
||||
if (!tag) {
|
||||
return []
|
||||
|
|
@ -114,7 +118,7 @@ export default class TableOfContents {
|
|||
return [{ depth, title: child.textContent, el: child }]
|
||||
}
|
||||
return TableOfContents.generateStructure(<Element>child)
|
||||
}
|
||||
))
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
txt = t?.current
|
||||
lang = t?.currentLang
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if $txt}
|
||||
|
|
|
|||
|
|
@ -40,11 +40,9 @@ export default class CopyrightPanel extends Combine {
|
|||
const t = Translations.t.general.attribution
|
||||
const layoutToUse = state.layout
|
||||
|
||||
|
||||
|
||||
const iconAttributions: BaseUIElement[] = (layoutToUse.getUsedImages()).map(
|
||||
CopyrightPanel.IconAttribution
|
||||
)
|
||||
const iconAttributions: BaseUIElement[] = layoutToUse
|
||||
.getUsedImages()
|
||||
.map(CopyrightPanel.IconAttribution)
|
||||
|
||||
let maintainer: BaseUIElement = undefined
|
||||
if (layoutToUse.credits !== undefined && layoutToUse.credits !== "") {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
import Tr from "../Base/Tr.svelte"
|
||||
import Icon from "../Map/Icon.svelte"
|
||||
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
let theme = state.layout?.id ?? ""
|
||||
let config: ExtraLinkConfig = state.layout.extraLink
|
||||
|
|
@ -23,7 +22,7 @@
|
|||
...loc,
|
||||
theme: theme,
|
||||
basepath,
|
||||
language: Locale.language.data
|
||||
language: Locale.language.data,
|
||||
}
|
||||
return Utils.SubstituteKeys(config.href, subs)
|
||||
},
|
||||
|
|
@ -31,25 +30,21 @@
|
|||
)
|
||||
</script>
|
||||
|
||||
{#if config !== undefined && !(config.requirements.has("iframe") && !isIframe) && !(config.requirements.has("no-iframe") && isIframe) && !(config.requirements.has("welcome-message") && !$showWelcomeMessageSwitch) && !(config.requirements.has("no-welcome-message") && $showWelcomeMessageSwitch)}
|
||||
<div class="links-as-button">
|
||||
<a
|
||||
href={$href}
|
||||
target={config.newTab ? "_blank" : ""}
|
||||
rel="noopener"
|
||||
class="pointer-events-auto flex rounded-full border-black"
|
||||
>
|
||||
<Icon icon={config.icon} clss="w-6 h-6 m-2" />
|
||||
|
||||
{#if config !== undefined &&
|
||||
!(config.requirements.has("iframe") && !isIframe) &&
|
||||
!(config.requirements.has("no-iframe") && isIframe) &&
|
||||
!(config.requirements.has("welcome-message") && !$showWelcomeMessageSwitch) &&
|
||||
!(config.requirements.has("no-welcome-message") && $showWelcomeMessageSwitch)}
|
||||
<div class="links-as-button">
|
||||
|
||||
<a href={$href} target={config.newTab ? "_blank" : ""} rel="noopener"
|
||||
class="flex pointer-events-auto rounded-full border-black">
|
||||
|
||||
<Icon icon={config.icon} clss="w-6 h-6 m-2"/>
|
||||
|
||||
{#if config.text}
|
||||
<Tr t={config.text} />
|
||||
{:else}
|
||||
<Tr t={t.screenToSmall.Subs({theme: state.layout.title})} />
|
||||
{/if}
|
||||
|
||||
</a>
|
||||
</div>
|
||||
{#if config.text}
|
||||
<Tr t={config.text} />
|
||||
{:else}
|
||||
<Tr t={t.screenToSmall.Subs({ theme: state.layout.title })} />
|
||||
{/if}
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -48,12 +48,12 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="flex h-full flex-col">
|
||||
<h2 class="low-interaction m-0 flex items-center p-4 drop-shadow-md">
|
||||
<Filter class="h-6 w-6 pr-2" />
|
||||
<Tr t={Translations.t.general.menu.filter} />
|
||||
</h2>
|
||||
<div class="flex h-full flex-col overflow-auto p-4 border-b-2">
|
||||
<div class="flex h-full flex-col overflow-auto border-b-2 p-4">
|
||||
{#each layout.layers as layer}
|
||||
<Filterview
|
||||
zoomlevel={state.mapProperties.zoom}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
export let state: ThemeViewState
|
||||
export let map: Store<MlMap> = undefined
|
||||
export let hideTooltip = false
|
||||
export let htmlElem : UIEventSource<HTMLElement> = undefined
|
||||
export let htmlElem: UIEventSource<HTMLElement> = undefined
|
||||
</script>
|
||||
|
||||
<MapControlButton
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
const t = Translations.t.privacy
|
||||
export let state: SpecialVisualizationState
|
||||
const usersettings = UserRelatedState.usersettingsConfig
|
||||
const editPrivacy = usersettings.tagRenderings.find(tr => tr.id === "more_privacy")
|
||||
const editPrivacy = usersettings.tagRenderings.find((tr) => tr.id === "more_privacy")
|
||||
const isLoggedIn = state.osmConnection.isLoggedIn
|
||||
</script>
|
||||
|
||||
|
|
@ -48,16 +48,19 @@
|
|||
|
||||
<li>
|
||||
{#if $isLoggedIn}
|
||||
<TagRenderingEditable config={editPrivacy} selectedElement={{
|
||||
type: "Feature",
|
||||
properties: { id: "settings" },
|
||||
geometry: { type: "Point", coordinates: [0, 0] },
|
||||
}}
|
||||
{state}
|
||||
tags={state.userRelatedState.preferencesAsTags} />
|
||||
{:else}
|
||||
<Tr t={t.items.distanceIndicator} />
|
||||
{/if}
|
||||
<TagRenderingEditable
|
||||
config={editPrivacy}
|
||||
selectedElement={{
|
||||
type: "Feature",
|
||||
properties: { id: "settings" },
|
||||
geometry: { type: "Point", coordinates: [0, 0] },
|
||||
}}
|
||||
{state}
|
||||
tags={state.userRelatedState.preferencesAsTags}
|
||||
/>
|
||||
{:else}
|
||||
<Tr t={t.items.distanceIndicator} />
|
||||
{/if}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -185,8 +185,6 @@ export class StackedRenderingChart extends ChartJs {
|
|||
}
|
||||
|
||||
export default class TagRenderingChart extends Combine {
|
||||
|
||||
|
||||
/**
|
||||
* Creates a chart about this tagRendering for the given data
|
||||
*/
|
||||
|
|
@ -223,9 +221,9 @@ export default class TagRenderingChart extends Combine {
|
|||
ChartJsColours.notApplicableBorderColor,
|
||||
]
|
||||
const backgroundColor = [
|
||||
ChartJsColours.unknownColor,
|
||||
ChartJsColours.otherColor,
|
||||
ChartJsColours.notApplicableColor,
|
||||
ChartJsColours.unknownColor,
|
||||
ChartJsColours.otherColor,
|
||||
ChartJsColours.notApplicableColor,
|
||||
]
|
||||
|
||||
while (borderColor.length < data.length) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
|
||||
|
||||
export let externalProperties: Record<string, string>
|
||||
delete externalProperties["@context"]
|
||||
console.log("External properties are", externalProperties)
|
||||
|
|
@ -33,51 +32,60 @@
|
|||
let externalKeys: string[] = Object.keys(externalProperties).sort()
|
||||
|
||||
const imageKeyRegex = /image|image:[0-9]+/
|
||||
let knownImages: Store<Set<string>> = tags.map(osmProperties => new Set(
|
||||
Object.keys(osmProperties)
|
||||
let knownImages: Store<Set<string>> = tags.map(
|
||||
(osmProperties) =>
|
||||
new Set(
|
||||
Object.keys(osmProperties)
|
||||
.filter((k) => k.match(imageKeyRegex))
|
||||
.map((k) => osmProperties[k])
|
||||
)
|
||||
)
|
||||
let unknownImages: Store<string[]> = knownImages.map((images) =>
|
||||
externalKeys
|
||||
.filter((k) => k.match(imageKeyRegex))
|
||||
.map((k) => osmProperties[k])
|
||||
))
|
||||
let unknownImages: Store<string[]> = knownImages.map(images => externalKeys
|
||||
.filter((k) => k.match(imageKeyRegex))
|
||||
.map((k) => externalProperties[k])
|
||||
.filter((i) => !images.has(i)))
|
||||
|
||||
.map((k) => externalProperties[k])
|
||||
.filter((i) => !images.has(i))
|
||||
)
|
||||
|
||||
let propertyKeysExternal = externalKeys.filter((k) => k.match(imageKeyRegex) === null)
|
||||
let missing: Store<string[]> = tags.map(osmProperties => propertyKeysExternal.filter((k) => {
|
||||
if (k.startsWith("_")) {
|
||||
return false
|
||||
}
|
||||
return osmProperties[k] === undefined && typeof externalProperties[k] === "string"
|
||||
}))
|
||||
// let same = propertyKeysExternal.filter((key) => osmProperties[key] === externalProperties[key])
|
||||
let different: Store<string[]> = tags.map(osmProperties => propertyKeysExternal.filter((key) => {
|
||||
if (key.startsWith("_")) {
|
||||
return false
|
||||
}
|
||||
if (osmProperties[key] === undefined) {
|
||||
return false
|
||||
}
|
||||
if (typeof externalProperties[key] !== "string") {
|
||||
return false
|
||||
}
|
||||
if (osmProperties[key] === externalProperties[key]) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (key === "website") {
|
||||
const osmCanon = new URL(osmProperties[key]).toString()
|
||||
const externalCanon = new URL(externalProperties[key]).toString()
|
||||
if (osmCanon === externalCanon) {
|
||||
let missing: Store<string[]> = tags.map((osmProperties) =>
|
||||
propertyKeysExternal.filter((k) => {
|
||||
if (k.startsWith("_")) {
|
||||
return false
|
||||
}
|
||||
return osmProperties[k] === undefined && typeof externalProperties[k] === "string"
|
||||
})
|
||||
)
|
||||
// let same = propertyKeysExternal.filter((key) => osmProperties[key] === externalProperties[key])
|
||||
let different: Store<string[]> = tags.map((osmProperties) =>
|
||||
propertyKeysExternal.filter((key) => {
|
||||
if (key.startsWith("_")) {
|
||||
return false
|
||||
}
|
||||
if (osmProperties[key] === undefined) {
|
||||
return false
|
||||
}
|
||||
if (typeof externalProperties[key] !== "string") {
|
||||
return false
|
||||
}
|
||||
if (osmProperties[key] === externalProperties[key]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}))
|
||||
if (key === "website") {
|
||||
const osmCanon = new URL(osmProperties[key]).toString()
|
||||
const externalCanon = new URL(externalProperties[key]).toString()
|
||||
if (osmCanon === externalCanon) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
let hasDifferencesAtStart = (different.data.length + missing.data.length + unknownImages.data.length) > 0
|
||||
return true
|
||||
})
|
||||
)
|
||||
|
||||
let hasDifferencesAtStart =
|
||||
different.data.length + missing.data.length + unknownImages.data.length > 0
|
||||
|
||||
let currentStep: "init" | "applying_all" | "all_applied" = "init"
|
||||
let applyAllHovered = false
|
||||
|
|
@ -87,23 +95,23 @@
|
|||
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,
|
||||
changeType: "import"
|
||||
changeType: "import",
|
||||
})
|
||||
await state.changes.applyChanges(await change.CreateChangeDescriptions())
|
||||
currentStep = "all_applied"
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if propertyKeysExternal.length === 0 && $knownImages.size + $unknownImages.length === 0}
|
||||
<Tr cls="subtle" t={t.noDataLoaded} />
|
||||
{:else if !hasDifferencesAtStart}
|
||||
<span class="subtle text-sm">
|
||||
<Tr t={t.allIncluded.Subs({source:sourceUrl})}/>
|
||||
<Tr t={t.allIncluded.Subs({ source: sourceUrl })} />
|
||||
</span>
|
||||
|
||||
{:else if $unknownImages.length === 0 && $missing.length === 0 && $different.length === 0}
|
||||
<div class="thanks m-0 flex items-center gap-x-2 px-2">
|
||||
<Party class="h-8 w-8 shrink-0" />
|
||||
<Tr t={t.allIncluded.Subs({source: sourceUrl})} />
|
||||
<Tr t={t.allIncluded.Subs({ source: sourceUrl })} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="low-interaction border-interactive p-1">
|
||||
|
|
@ -112,7 +120,6 @@
|
|||
{/if}
|
||||
|
||||
<div class="flex flex-col" class:gap-y-8={!readonly}>
|
||||
|
||||
{#if $different.length > 0}
|
||||
{#if !readonly}
|
||||
<h3>
|
||||
|
|
@ -137,9 +144,9 @@
|
|||
|
||||
{#if $missing.length > 0}
|
||||
{#if !readonly}
|
||||
<h3 class="m-0">
|
||||
<Tr t={t.missing.title} />
|
||||
</h3>
|
||||
<h3 class="m-0">
|
||||
<Tr t={t.missing.title} />
|
||||
</h3>
|
||||
|
||||
<Tr t={t.missing.intro} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -69,12 +69,12 @@
|
|||
previewedImage={state.previewedImage}
|
||||
/>
|
||||
</div>
|
||||
<LoginToggle {state} silentFail={true} >
|
||||
{#if linkable}
|
||||
<label>
|
||||
<input bind:checked={$isLinked} type="checkbox" />
|
||||
<SpecialTranslation t={t.link} {tags} {state} {layer} {feature} />
|
||||
</label>
|
||||
{/if}
|
||||
<LoginToggle {state} silentFail={true}>
|
||||
{#if linkable}
|
||||
<label>
|
||||
<input bind:checked={$isLinked} type="checkbox" />
|
||||
<SpecialTranslation t={t.link} {tags} {state} {layer} {feature} />
|
||||
</label>
|
||||
{/if}
|
||||
</LoginToggle>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
lon,
|
||||
lat,
|
||||
allowSpherical: new UIEventSource<boolean>(false),
|
||||
blacklist: AllImageProviders.LoadImagesFor(tags)
|
||||
blacklist: AllImageProviders.LoadImagesFor(tags),
|
||||
},
|
||||
state.indexedFeatures
|
||||
)
|
||||
|
|
@ -53,9 +53,9 @@
|
|||
{:else}
|
||||
<div class="flex w-full space-x-1 overflow-x-auto" style="scroll-snap-type: x proximity">
|
||||
{#each $images as image (image.pictureUrl)}
|
||||
<span class="w-fit shrink-0" style="scroll-snap-align: start">
|
||||
<LinkableImage {tags} {image} {state} {feature} {layer} {linkable} />
|
||||
</span>
|
||||
<span class="w-fit shrink-0" style="scroll-snap-align: start">
|
||||
<LinkableImage {tags} {image} {state} {feature} {layer} {linkable} />
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -25,31 +25,31 @@
|
|||
let expanded = false
|
||||
</script>
|
||||
|
||||
<div class="my-4">
|
||||
{#if expanded}
|
||||
<NearbyImages {tags} {state} {lon} {lat} {feature} {linkable} {layer}>
|
||||
<button
|
||||
slot="corner"
|
||||
class="no-image-background h-6 w-6 cursor-pointer border-none p-0"
|
||||
use:ariaLabel={t.close}
|
||||
on:click={() => {
|
||||
expanded = false
|
||||
}}
|
||||
>
|
||||
<XCircleIcon />
|
||||
</button>
|
||||
</NearbyImages>
|
||||
{:else}
|
||||
<div class="my-4">
|
||||
{#if expanded}
|
||||
<NearbyImages {tags} {state} {lon} {lat} {feature} {linkable} {layer}>
|
||||
<button
|
||||
class="flex w-full items-center"
|
||||
style="margin-left: 0; margin-right: 0"
|
||||
slot="corner"
|
||||
class="no-image-background h-6 w-6 cursor-pointer border-none p-0"
|
||||
use:ariaLabel={t.close}
|
||||
on:click={() => {
|
||||
expanded = true
|
||||
expanded = false
|
||||
}}
|
||||
aria-expanded={expanded}
|
||||
>
|
||||
<Camera_plus class="mr-2 block h-8 w-8 p-1" />
|
||||
<Tr t={t.seeNearby} />
|
||||
<XCircleIcon />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</NearbyImages>
|
||||
{:else}
|
||||
<button
|
||||
class="flex w-full items-center"
|
||||
style="margin-left: 0; margin-right: 0"
|
||||
on:click={() => {
|
||||
expanded = true
|
||||
}}
|
||||
aria-expanded={expanded}
|
||||
>
|
||||
<Camera_plus class="mr-2 block h-8 w-8 p-1" />
|
||||
<Tr t={t.seeNearby} />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export default class Validators {
|
|||
"slope",
|
||||
"velopark",
|
||||
"nsi",
|
||||
"currency"
|
||||
"currency",
|
||||
] as const
|
||||
|
||||
public static readonly AllValidators: ReadonlyArray<Validator> = [
|
||||
|
|
@ -94,7 +94,7 @@ export default class Validators {
|
|||
new SlopeValidator(),
|
||||
new VeloparkValidator(),
|
||||
new NameSuggestionIndexValidator(),
|
||||
new CurrencyValidator()
|
||||
new CurrencyValidator(),
|
||||
]
|
||||
|
||||
private static _byType = Validators._byTypeConstructor()
|
||||
|
|
|
|||
|
|
@ -13,26 +13,26 @@ export default class CurrencyValidator extends Validator {
|
|||
return
|
||||
}
|
||||
let locale = "en-US"
|
||||
if(!Utils.runningFromConsole){
|
||||
locale??= navigator.language
|
||||
if (!Utils.runningFromConsole) {
|
||||
locale ??= navigator.language
|
||||
}
|
||||
this.segmenter = new Intl.Segmenter(locale, {
|
||||
granularity: "word"
|
||||
granularity: "word",
|
||||
})
|
||||
|
||||
const mapping: Map<string, string> = new Map<string, string>()
|
||||
const supportedCurrencies: Set<string> = new Set(Intl.supportedValuesOf("currency"))
|
||||
this.supportedCurrencies = supportedCurrencies
|
||||
for (const currency of supportedCurrencies) {
|
||||
const symbol = (0).toLocaleString(
|
||||
locale,
|
||||
{
|
||||
const symbol = (0)
|
||||
.toLocaleString(locale, {
|
||||
style: "currency",
|
||||
currency: currency,
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0
|
||||
}
|
||||
).replace(/\d/g, "").trim()
|
||||
maximumFractionDigits: 0,
|
||||
})
|
||||
.replace(/\d/g, "")
|
||||
.trim()
|
||||
|
||||
mapping.set(symbol.toLowerCase(), currency)
|
||||
}
|
||||
|
|
@ -44,8 +44,10 @@ export default class CurrencyValidator extends Validator {
|
|||
return s
|
||||
}
|
||||
|
||||
const parts = Array.from(this.segmenter.segment(s)).map(i => i.segment).filter(part => part.trim().length > 0)
|
||||
if(parts.length !== 2){
|
||||
const parts = Array.from(this.segmenter.segment(s))
|
||||
.map((i) => i.segment)
|
||||
.filter((part) => part.trim().length > 0)
|
||||
if (parts.length !== 2) {
|
||||
return s
|
||||
}
|
||||
const mapping = this.symbolToCurrencyMapping
|
||||
|
|
@ -64,10 +66,10 @@ export default class CurrencyValidator extends Validator {
|
|||
}
|
||||
amount = part
|
||||
}
|
||||
if(amount === undefined || currency === undefined){
|
||||
if (amount === undefined || currency === undefined) {
|
||||
return s
|
||||
}
|
||||
|
||||
return amount+" "+currency
|
||||
return amount + " " + currency
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ import UrlValidator from "./UrlValidator"
|
|||
|
||||
export default class VeloparkValidator extends UrlValidator {
|
||||
constructor() {
|
||||
super("velopark", "A special URL-validator that checks the domain name and rewrites to the correct velopark format.")
|
||||
super(
|
||||
"velopark",
|
||||
"A special URL-validator that checks the domain name and rewrites to the correct velopark format."
|
||||
)
|
||||
}
|
||||
|
||||
getFeedback(s: string): Translation {
|
||||
|
|
|
|||
|
|
@ -30,41 +30,42 @@
|
|||
}
|
||||
> = UIEventSource.FromPromise(Utils.downloadJsonCached(source))
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<h1>Contributed images with MapComplete: leaderboard</h1>
|
||||
<h1>Contributed images with MapComplete: leaderboard</h1>
|
||||
|
||||
{#if $data}
|
||||
<table>
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Contributor</th>
|
||||
<th>Number of images contributed</th>
|
||||
</tr>
|
||||
{#each $data.leaderboard as contributor}
|
||||
{#if $data}
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
{contributor.rank}
|
||||
</td>
|
||||
<td>
|
||||
{#if $loggedInContributor === contributor.name}
|
||||
<a class="thanks" href={contributor.account}>{contributor.name}</a>
|
||||
{:else}
|
||||
<a href={contributor.account}>{contributor.name}</a>
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
<b>{contributor.nrOfImages}</b>
|
||||
total images
|
||||
</td>
|
||||
<th>Rank</th>
|
||||
<th>Contributor</th>
|
||||
<th>Number of images contributed</th>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
Statistics generated on {$data.date}
|
||||
{:else}
|
||||
<Loading />
|
||||
{/if}
|
||||
{#each $data.leaderboard as contributor}
|
||||
<tr>
|
||||
<td>
|
||||
{contributor.rank}
|
||||
</td>
|
||||
<td>
|
||||
{#if $loggedInContributor === contributor.name}
|
||||
<a class="thanks" href={contributor.account}>{contributor.name}</a>
|
||||
{:else}
|
||||
<a href={contributor.account}>{contributor.name}</a>
|
||||
{/if}
|
||||
</td>
|
||||
<td>
|
||||
<b>{contributor.nrOfImages}</b>
|
||||
total images
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
Statistics generated on {$data.date}
|
||||
{:else}
|
||||
<Loading />
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
Logged in as {$loggedInContributor}
|
||||
</div>
|
||||
<div>
|
||||
Logged in as {$loggedInContributor}
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -126,7 +126,6 @@
|
|||
<LinkIcon style="--svg-color: {color}" class={twMerge(clss, "apply-fill")} />
|
||||
{:else if icon === "popout"}
|
||||
<LinkIcon style="--svg-color: {color}" />
|
||||
|
||||
{:else}
|
||||
<img class={clss ?? "h-full w-full"} src={icon} aria-hidden="true" alt="" />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ class PointRenderingLayer {
|
|||
|
||||
if (this._onClick) {
|
||||
const self = this
|
||||
el.addEventListener("click", function(ev) {
|
||||
el.addEventListener("click", function (ev) {
|
||||
ev.preventDefault()
|
||||
self._onClick(feature)
|
||||
// Workaround to signal the MapLibreAdaptor to ignore this click
|
||||
|
|
@ -200,7 +200,7 @@ class LineRenderingLayer {
|
|||
"lineCap",
|
||||
"offset",
|
||||
"fill",
|
||||
"fillColor"
|
||||
"fillColor",
|
||||
] as const
|
||||
|
||||
private static readonly lineConfigKeysColor = ["color", "fillColor"] as const
|
||||
|
|
@ -264,8 +264,8 @@ class LineRenderingLayer {
|
|||
"icon-rotation-alignment": "map",
|
||||
"icon-pitch-alignment": "map",
|
||||
"icon-image": imgId,
|
||||
"icon-size": 0.055
|
||||
}
|
||||
"icon-size": 0.055,
|
||||
},
|
||||
}
|
||||
const filter = img.if?.asMapboxExpression()
|
||||
if (filter) {
|
||||
|
|
@ -338,9 +338,9 @@ class LineRenderingLayer {
|
|||
type: "geojson",
|
||||
data: {
|
||||
type: "FeatureCollection",
|
||||
features
|
||||
features,
|
||||
},
|
||||
promoteId: "id"
|
||||
promoteId: "id",
|
||||
})
|
||||
const linelayer = this._layername + "_line"
|
||||
const layer: AddLayerObject = {
|
||||
|
|
@ -351,19 +351,21 @@ class LineRenderingLayer {
|
|||
"line-color": ["feature-state", "color"],
|
||||
"line-opacity": ["feature-state", "color-opacity"],
|
||||
"line-width": ["feature-state", "width"],
|
||||
"line-offset": ["feature-state", "offset"]
|
||||
"line-offset": ["feature-state", "offset"],
|
||||
},
|
||||
layout: {
|
||||
"line-cap": "round"
|
||||
}
|
||||
"line-cap": "round",
|
||||
},
|
||||
}
|
||||
if (this._config.dashArray) {
|
||||
try{
|
||||
|
||||
layer.paint["line-dasharray"] =
|
||||
this._config.dashArray?.split(" ")?.map((s) => Number(s)) ?? null
|
||||
}catch (e) {
|
||||
console.error(`Invalid dasharray in layer ${this._layername}:`, this._config.dashArray)
|
||||
try {
|
||||
layer.paint["line-dasharray"] =
|
||||
this._config.dashArray?.split(" ")?.map((s) => Number(s)) ?? null
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Invalid dasharray in layer ${this._layername}:`,
|
||||
this._config.dashArray
|
||||
)
|
||||
}
|
||||
}
|
||||
map.addLayer(layer)
|
||||
|
|
@ -398,8 +400,8 @@ class LineRenderingLayer {
|
|||
layout: {},
|
||||
paint: {
|
||||
"fill-color": ["feature-state", "fillColor"],
|
||||
"fill-opacity": ["feature-state", "fillColor-opacity"]
|
||||
}
|
||||
"fill-opacity": ["feature-state", "fillColor-opacity"],
|
||||
},
|
||||
})
|
||||
if (this._onClick) {
|
||||
map.on("click", polylayer, (e) => {
|
||||
|
|
@ -430,7 +432,7 @@ class LineRenderingLayer {
|
|||
this.currentSourceData = features
|
||||
src.setData({
|
||||
type: "FeatureCollection",
|
||||
features: this.currentSourceData
|
||||
features: this.currentSourceData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -513,15 +515,15 @@ export default class ShowDataLayer {
|
|||
layers.filter((l) => l.source !== null).map((l) => new FilteredLayer(l)),
|
||||
features,
|
||||
{
|
||||
constructStore: (features, layer) => new SimpleFeatureSource(layer, features)
|
||||
constructStore: (features, layer) => new SimpleFeatureSource(layer, features),
|
||||
}
|
||||
)
|
||||
if (options?.zoomToFeatures) {
|
||||
options.zoomToFeatures = false
|
||||
features.features.addCallbackD(features => {
|
||||
features.features.addCallbackD((features) => {
|
||||
ShowDataLayer.zoomToCurrentFeatures(mlmap.data, features)
|
||||
})
|
||||
mlmap.addCallbackD(map => {
|
||||
mlmap.addCallbackD((map) => {
|
||||
ShowDataLayer.zoomToCurrentFeatures(map, features.features.data)
|
||||
})
|
||||
}
|
||||
|
|
@ -530,7 +532,7 @@ export default class ShowDataLayer {
|
|||
new ShowDataLayer(mlmap, {
|
||||
layer: fs.layer.layerDef,
|
||||
features: fs,
|
||||
...(options ?? {})
|
||||
...(options ?? {}),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -543,12 +545,11 @@ export default class ShowDataLayer {
|
|||
return new ShowDataLayer(map, {
|
||||
layer: ShowDataLayer.rangeLayer,
|
||||
features,
|
||||
doShowLayer
|
||||
doShowLayer,
|
||||
})
|
||||
}
|
||||
|
||||
public destruct() {
|
||||
}
|
||||
public destruct() {}
|
||||
|
||||
private static zoomToCurrentFeatures(map: MlMap, features: Feature[]) {
|
||||
if (!features || !map || features.length == 0) {
|
||||
|
|
@ -560,7 +561,7 @@ export default class ShowDataLayer {
|
|||
map.resize()
|
||||
map.fitBounds(bbox.toLngLat(), {
|
||||
padding: { top: 10, bottom: 10, left: 10, right: 10 },
|
||||
animate: false
|
||||
animate: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -573,8 +574,8 @@ export default class ShowDataLayer {
|
|||
this._options.layer.title === undefined
|
||||
? undefined
|
||||
: (feature: Feature) => {
|
||||
selectedElement?.setData(feature)
|
||||
}
|
||||
selectedElement?.setData(feature)
|
||||
}
|
||||
}
|
||||
if (this._options.drawLines !== false) {
|
||||
for (let i = 0; i < this._options.layer.lineRendering.length; i++) {
|
||||
|
|
@ -606,7 +607,9 @@ export default class ShowDataLayer {
|
|||
}
|
||||
}
|
||||
if (this._options.zoomToFeatures) {
|
||||
features.features.addCallbackAndRunD((features) => ShowDataLayer.zoomToCurrentFeatures(map, features))
|
||||
features.features.addCallbackAndRunD((features) =>
|
||||
ShowDataLayer.zoomToCurrentFeatures(map, features)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,17 +6,17 @@
|
|||
</script>
|
||||
|
||||
<main>
|
||||
<div class="flex flex-col">
|
||||
<Tr t={Translations.t.general["404"]} />
|
||||
<BackButton
|
||||
clss="m-8"
|
||||
on:click={() => {
|
||||
window.location = "index.html"
|
||||
}}
|
||||
>
|
||||
<div class="flex w-full justify-center">
|
||||
<Tr t={Translations.t.general.backToIndex} />
|
||||
</div>
|
||||
</BackButton>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<Tr t={Translations.t.general["404"]} />
|
||||
<BackButton
|
||||
clss="m-8"
|
||||
on:click={() => {
|
||||
window.location = "index.html"
|
||||
}}
|
||||
>
|
||||
<div class="flex w-full justify-center">
|
||||
<Tr t={Translations.t.general.backToIndex} />
|
||||
</div>
|
||||
</BackButton>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -21,16 +21,14 @@
|
|||
}
|
||||
let knownValues: UIEventSource<string[]> = new UIEventSource<string[]>([])
|
||||
|
||||
tags.addCallbackAndRunD(tags => {
|
||||
tags.addCallbackAndRunD((tags) => {
|
||||
knownValues.setData(Object.keys(tags))
|
||||
})
|
||||
|
||||
function reEvalKnownValues(){
|
||||
function reEvalKnownValues() {
|
||||
knownValues.setData(Object.keys(tags.data))
|
||||
|
||||
}
|
||||
|
||||
|
||||
const metaKeys: string[] = [].concat(...SimpleMetaTaggers.metatags.map((k) => k.keys))
|
||||
let allCalculatedTags = new Set<string>([...calculatedTags, ...metaKeys])
|
||||
</script>
|
||||
|
|
@ -54,7 +52,7 @@
|
|||
{:else if $tags[key] === ""}
|
||||
<i>Empty string</i>
|
||||
{:else if typeof $tags[key] === "object"}
|
||||
<div class="literal-code" >{JSON.stringify($tags[key])}</div>
|
||||
<div class="literal-code">{JSON.stringify($tags[key])}</div>
|
||||
{:else}
|
||||
{$tags[key]}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ export class MinimapViz implements SpecialVisualization {
|
|||
{
|
||||
doc: "The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close",
|
||||
name: "zoomlevel",
|
||||
defaultValue: "18"
|
||||
defaultValue: "18",
|
||||
},
|
||||
{
|
||||
doc: "(Matches all resting arguments) This argument should be the key of a property of the feature. The corresponding value is interpreted as either the id or the a list of ID's. The features with these ID's will be shown on this minimap. (Note: if the key is 'id', list interpration is disabled)",
|
||||
name: "idKey",
|
||||
defaultValue: "id"
|
||||
}
|
||||
defaultValue: "id",
|
||||
},
|
||||
]
|
||||
example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`"
|
||||
|
||||
|
|
@ -82,13 +82,12 @@ export class MinimapViz implements SpecialVisualization {
|
|||
const mla = new MapLibreAdaptor(mlmap, {
|
||||
rasterLayer: state.mapProperties.rasterLayer,
|
||||
zoom: new UIEventSource<number>(17),
|
||||
maxzoom: new UIEventSource<number>(17)
|
||||
maxzoom: new UIEventSource<number>(17),
|
||||
})
|
||||
|
||||
mla.allowMoving.setData(false)
|
||||
mla.allowZooming.setData(false)
|
||||
mla.location.setData({lon, lat})
|
||||
|
||||
mla.location.setData({ lon, lat })
|
||||
|
||||
if (args[0]) {
|
||||
const parsed = Number(args[0])
|
||||
|
|
@ -96,19 +95,19 @@ export class MinimapViz implements SpecialVisualization {
|
|||
mla.zoom.setData(parsed)
|
||||
}
|
||||
}
|
||||
mlmap.addCallbackAndRun(map => console.log("Map for minimap vis is now", map))
|
||||
mlmap.addCallbackAndRun((map) => console.log("Map for minimap vis is now", map))
|
||||
|
||||
ShowDataLayer.showMultipleLayers(
|
||||
mlmap,
|
||||
new StaticFeatureSource(featuresToShow),
|
||||
state.layout.layers,
|
||||
{zoomToFeatures: true}
|
||||
{ zoomToFeatures: true }
|
||||
)
|
||||
|
||||
return new SvelteUIElement(MaplibreMap, {
|
||||
interactive: false,
|
||||
map: mlmap,
|
||||
mapProperties: mla
|
||||
mapProperties: mla,
|
||||
})
|
||||
.SetClass("h-40 rounded")
|
||||
.SetStyle("overflow: hidden; pointer-events: none;")
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@
|
|||
let questionboxElem: HTMLDivElement
|
||||
let questionsToAsk = tags.map(
|
||||
(tags) => {
|
||||
|
||||
const questionsToAsk: TagRenderingConfig[] = []
|
||||
for (const baseQuestion of baseQuestions) {
|
||||
if (skippedQuestions.data.has(baseQuestion.id)) {
|
||||
|
|
@ -164,7 +163,13 @@
|
|||
{#if $showAllQuestionsAtOnce}
|
||||
<div class="flex flex-col gap-y-1">
|
||||
{#each $allQuestionsToAsk as question (question.id)}
|
||||
<TagRenderingQuestionDynamic config={question} {tags} {selectedElement} {state} {layer} />
|
||||
<TagRenderingQuestionDynamic
|
||||
config={question}
|
||||
{tags}
|
||||
{selectedElement}
|
||||
{state}
|
||||
{layer}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if $firstQuestion !== undefined}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import Locale from "../../i18n/Locale"
|
||||
import type {
|
||||
RenderingSpecification,
|
||||
SpecialVisualizationState
|
||||
SpecialVisualizationState,
|
||||
} from "../../SpecialVisualization"
|
||||
import { Utils } from "../../../Utils.js"
|
||||
import type { Feature } from "geojson"
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
{#each specs as specpart}
|
||||
{#if typeof specpart === "string"}
|
||||
<span>
|
||||
{@html Utils.purify(Utils.SubstituteKeys(specpart, $tags)) }
|
||||
{@html Utils.purify(Utils.SubstituteKeys(specpart, $tags))}
|
||||
<WeblateLink context={t.context} />
|
||||
</span>
|
||||
{:else if $tags !== undefined}
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
{#each specs as specpart}
|
||||
{#if typeof specpart === "string"}
|
||||
<span>
|
||||
{@html Utils.purify(Utils.SubstituteKeys(specpart, $tags)) }
|
||||
{@html Utils.purify(Utils.SubstituteKeys(specpart, $tags))}
|
||||
<WeblateLink context={t.context} />
|
||||
</span>
|
||||
{:else if $tags !== undefined}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<script lang="ts">
|
||||
import TagRenderingConfig, { TagRenderingConfigUtils } from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import TagRenderingConfig, {
|
||||
TagRenderingConfigUtils,
|
||||
} from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import type { Feature } from "geojson"
|
||||
import { UIEventSource } from "../../../Logic/UIEventSource"
|
||||
|
|
@ -16,7 +18,14 @@
|
|||
|
||||
export let id: string = undefined
|
||||
let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement)
|
||||
|
||||
</script>
|
||||
|
||||
<TagRenderingAnswer {selectedElement} {layer} config={$dynamicConfig} {extraClasses} {id} {tags} {state} />
|
||||
<TagRenderingAnswer
|
||||
{selectedElement}
|
||||
{layer}
|
||||
config={$dynamicConfig}
|
||||
{extraClasses}
|
||||
{id}
|
||||
{tags}
|
||||
{state}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<script lang="ts">
|
||||
import TagRenderingConfig, { TagRenderingConfigUtils } from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import TagRenderingConfig, {
|
||||
TagRenderingConfigUtils,
|
||||
} from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import { Store, UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import type { Feature } from "geojson"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
|
|
@ -19,8 +21,16 @@
|
|||
export let editMode = !config.IsKnown(tags.data)
|
||||
|
||||
let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement)
|
||||
|
||||
</script>
|
||||
|
||||
<TagRenderingEditable config={$dynamicConfig} {editMode} {clss} {highlightedRendering} {editingEnabled} {layer} {state}
|
||||
{selectedElement} {tags} />
|
||||
<TagRenderingEditable
|
||||
config={$dynamicConfig}
|
||||
{editMode}
|
||||
{clss}
|
||||
{highlightedRendering}
|
||||
{editingEnabled}
|
||||
{layer}
|
||||
{state}
|
||||
{selectedElement}
|
||||
{tags}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
(search) => {
|
||||
search = search?.trim()
|
||||
if (!search) {
|
||||
if(hideUnlessSearched){
|
||||
if (hideUnlessSearched) {
|
||||
if (mapping.priorityIf?.matchesProperties(tags.data)) {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,11 @@
|
|||
|
||||
onDestroy(
|
||||
freeformInput.subscribe((freeformValue) => {
|
||||
if (!config?.mappings || config?.mappings?.length == 0 || config.freeform?.key === undefined) {
|
||||
if (
|
||||
!config?.mappings ||
|
||||
config?.mappings?.length == 0 ||
|
||||
config.freeform?.key === undefined
|
||||
) {
|
||||
return
|
||||
}
|
||||
// If a freeform value is given, mark the 'mapping' as marked
|
||||
|
|
@ -232,7 +236,9 @@
|
|||
// Add the extraTags to the existing And
|
||||
selectedTags = new And([...selectedTags.and, ...extraTagsArray])
|
||||
} else {
|
||||
console.error("selectedTags is not of type Tag or And, it is a "+JSON.stringify(selectedTags))
|
||||
console.error(
|
||||
"selectedTags is not of type Tag or And, it is a " + JSON.stringify(selectedTags)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -289,7 +295,8 @@
|
|||
let showTags = state?.userRelatedState?.showTags ?? new ImmutableStore(undefined)
|
||||
let numberOfCs = state?.osmConnection?.userDetails?.data?.csCount ?? 0
|
||||
let question = config.question
|
||||
let hideMappingsUnlessSearchedFor = config.mappings.length > 8 && config.mappings.some(m => m.priorityIf)
|
||||
let hideMappingsUnlessSearchedFor =
|
||||
config.mappings.length > 8 && config.mappings.some((m) => m.priorityIf)
|
||||
$: question = config.question
|
||||
if (state?.osmConnection) {
|
||||
onDestroy(
|
||||
|
|
@ -343,15 +350,13 @@
|
|||
/>
|
||||
</div>
|
||||
{#if hideMappingsUnlessSearchedFor}
|
||||
<div class="rounded border border-black border-dashed p-1 px-2 m-1">
|
||||
<Tr t={Translations.t.general.mappingsAreHidden}/>
|
||||
<div class="m-1 rounded border border-dashed border-black p-1 px-2">
|
||||
<Tr t={Translations.t.general.mappingsAreHidden} />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
|
||||
|
||||
{#if config.freeform?.key && !(config?.mappings?.filter(m => m.hideInAnswer != true)?.length > 0)}
|
||||
{#if config.freeform?.key && !(config?.mappings?.filter((m) => m.hideInAnswer != true)?.length > 0)}
|
||||
<!-- There are no options to choose from, simply show the input element: fill out the text field -->
|
||||
<FreeformInput
|
||||
{config}
|
||||
|
|
@ -505,7 +510,7 @@
|
|||
<span class="flex flex-wrap">
|
||||
{#if $featureSwitchIsTesting}
|
||||
<button class="small" on:click={() => console.log("Configuration is ", config)}>
|
||||
Testmode
|
||||
Testmode
|
||||
</button>
|
||||
{/if}
|
||||
{#if $featureSwitchIsTesting || $featureSwitchIsDebugging}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,36 @@
|
|||
<script lang="ts">/**
|
||||
* Wrapper around 'tagRenderingEditable' but might add mappings dynamically
|
||||
*
|
||||
* Note: does not forward the 'save-button'-slot
|
||||
*/
|
||||
import TagRenderingConfig, { TagRenderingConfigUtils } from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import { UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import type { Feature } from "geojson"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import TagRenderingQuestion from "./TagRenderingQuestion.svelte"
|
||||
import type { UploadableTag } from "../../../Logic/Tags/TagUtils"
|
||||
import { writable } from "svelte/store"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Wrapper around 'tagRenderingEditable' but might add mappings dynamically
|
||||
*
|
||||
* Note: does not forward the 'save-button'-slot
|
||||
*/
|
||||
import TagRenderingConfig, {
|
||||
TagRenderingConfigUtils,
|
||||
} from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import { UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import type { Feature } from "geojson"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import TagRenderingQuestion from "./TagRenderingQuestion.svelte"
|
||||
import type { UploadableTag } from "../../../Logic/Tags/TagUtils"
|
||||
import { writable } from "svelte/store"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
|
||||
export let config: TagRenderingConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
export let config: TagRenderingConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
|
||||
export let selectedElement: Feature
|
||||
export let state: SpecialVisualizationState
|
||||
export let layer: LayerConfig | undefined
|
||||
export let selectedTags: UploadableTag = undefined
|
||||
export let extraTags: UIEventSource<Record<string, string>> = new UIEventSource({})
|
||||
export let selectedElement: Feature
|
||||
export let state: SpecialVisualizationState
|
||||
export let layer: LayerConfig | undefined
|
||||
export let selectedTags: UploadableTag = undefined
|
||||
export let extraTags: UIEventSource<Record<string, string>> = new UIEventSource({})
|
||||
|
||||
export let allowDeleteOfFreeform: boolean = false
|
||||
export let allowDeleteOfFreeform: boolean = false
|
||||
|
||||
|
||||
let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement)
|
||||
let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement)
|
||||
</script>
|
||||
|
||||
<TagRenderingQuestion
|
||||
|
|
|
|||
|
|
@ -13,24 +13,25 @@
|
|||
const osmConnection = new OsmConnection()
|
||||
let state: SpecialVisualizationState = {
|
||||
osmConnection,
|
||||
userRelatedState: new UserRelatedState(osmConnection)
|
||||
userRelatedState: new UserRelatedState(osmConnection),
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div class="flex h-screen flex-col overflow-hidden px-4">
|
||||
<div class="flex justify-between">
|
||||
<h2 class="flex items-center">
|
||||
<EyeIcon class="w-6 pr-2" />
|
||||
<Tr t={Translations.t.privacy.title} />
|
||||
</h2>
|
||||
<LanguagePicker availableLanguages={Translations.t.privacy.intro.SupportedLanguages()} />
|
||||
<div class="flex h-screen flex-col overflow-hidden px-4">
|
||||
<div class="flex justify-between">
|
||||
<h2 class="flex items-center">
|
||||
<EyeIcon class="w-6 pr-2" />
|
||||
<Tr t={Translations.t.privacy.title} />
|
||||
</h2>
|
||||
<LanguagePicker availableLanguages={Translations.t.privacy.intro.SupportedLanguages()} />
|
||||
</div>
|
||||
<div class="h-full overflow-auto border border-gray-500 p-4">
|
||||
<PrivacyPolicy {state} />
|
||||
</div>
|
||||
<a class="button flex" href={Utils.HomepageLink()}>
|
||||
<Add class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.backToIndex} />
|
||||
</a>
|
||||
</div>
|
||||
<div class="h-full overflow-auto border border-gray-500 p-4">
|
||||
<PrivacyPolicy {state} />
|
||||
</div>
|
||||
<a class="button flex" href={Utils.HomepageLink()}>
|
||||
<Add class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.backToIndex} />
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||
import BaseUIElement from "./BaseUIElement"
|
||||
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
|
||||
import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource"
|
||||
import {
|
||||
FeatureSource,
|
||||
IndexedFeatureSource,
|
||||
WritableFeatureSource,
|
||||
} from "../Logic/FeatureSource/FeatureSource"
|
||||
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
||||
import { Changes } from "../Logic/Osm/Changes"
|
||||
import { ExportableMap, MapProperties } from "../Models/MapProperties"
|
||||
|
|
@ -64,7 +68,6 @@ export interface SpecialVisualizationState {
|
|||
readonly currentView: FeatureSource<Feature<Polygon>>
|
||||
readonly favourites: FavouritesFeatureSource
|
||||
|
||||
|
||||
/**
|
||||
* If data is currently being fetched from external sources
|
||||
*/
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,18 +18,26 @@
|
|||
|
||||
<div class="mt-12">
|
||||
{#if deleteState === "init"}
|
||||
<button on:click={() => {deleteState = "confirm"}} class="small">
|
||||
<button
|
||||
on:click={() => {
|
||||
deleteState = "confirm"
|
||||
}}
|
||||
class="small"
|
||||
>
|
||||
<TrashIcon class="h-6 w-6" />
|
||||
Delete this {objectType}
|
||||
Delete this {objectType}
|
||||
</button>
|
||||
{:else if deleteState === "confirm"}
|
||||
<div class="flex">
|
||||
<BackButton on:click={() => {deleteState = "init"}}>
|
||||
<BackButton
|
||||
on:click={() => {
|
||||
deleteState = "init"
|
||||
}}
|
||||
>
|
||||
Don't delete
|
||||
</BackButton>
|
||||
<NextButton clss="primary" on:click={() => deleteLayer()}>
|
||||
<div class="alert flex p-2">
|
||||
|
||||
<TrashIcon class="h-6 w-6" />
|
||||
Do delete this {objectType}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -81,8 +81,6 @@
|
|||
})
|
||||
|
||||
let highlightedItem: UIEventSource<HighlightedTagRendering> = state.highlightedItem
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="flex h-screen flex-col">
|
||||
|
|
@ -136,7 +134,7 @@
|
|||
General properties
|
||||
<ErrorIndicatorForRegion firstPaths={firstPathsFor("Basic")} {state} />
|
||||
</div>
|
||||
<div class="flex flex-col mb-8" slot="content0">
|
||||
<div class="mb-8 flex flex-col" slot="content0">
|
||||
<Region {state} configs={perRegion["Basic"]} />
|
||||
<DeleteButton {state} {backToStudio} objectType="layer" />
|
||||
</div>
|
||||
|
|
@ -189,15 +187,15 @@
|
|||
Below, you'll find the raw configuration file in `.json`-format. This is mostly for
|
||||
debugging purposes, but you can also edit the file directly if you want.
|
||||
</div>
|
||||
<div class="literal-code overflow-y-auto h-full" style="min-height: 75%">
|
||||
<div class="literal-code h-full overflow-y-auto" style="min-height: 75%">
|
||||
<RawEditor {state} />
|
||||
</div>
|
||||
<ShowConversionMessages messages={$messages} />
|
||||
|
||||
<div class="flex w-full flex-col">
|
||||
<div>
|
||||
The testobject (which is used to render the questions in the 'information panel'
|
||||
item has the following tags:
|
||||
The testobject (which is used to render the questions in the 'information panel' item
|
||||
has the following tags:
|
||||
</div>
|
||||
|
||||
<AllTagsPanel tags={state.testTags} />
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
Conversion,
|
||||
ConversionMessage,
|
||||
DesugaringContext,
|
||||
Pipe
|
||||
Pipe,
|
||||
} from "../../Models/ThemeConfig/Conversion/Conversion"
|
||||
import { PrepareLayer } from "../../Models/ThemeConfig/Conversion/PrepareLayer"
|
||||
import { ValidateLayer, ValidateTheme } from "../../Models/ThemeConfig/Conversion/Validation"
|
||||
|
|
@ -69,7 +69,6 @@ export abstract class EditJsonState<T> {
|
|||
this.category = category
|
||||
this.expertMode = options?.expertMode ?? new UIEventSource<boolean>(false)
|
||||
|
||||
|
||||
const layerId = this.getId()
|
||||
this.configuration
|
||||
.mapD((config) => {
|
||||
|
|
@ -89,7 +88,6 @@ export abstract class EditJsonState<T> {
|
|||
await this.server.update(id, config, this.category)
|
||||
})
|
||||
this.messages = this.createMessagesStore()
|
||||
|
||||
}
|
||||
|
||||
public startSavingUpdates(enabled = true) {
|
||||
|
|
@ -158,10 +156,10 @@ export abstract class EditJsonState<T> {
|
|||
path,
|
||||
type: "translation",
|
||||
hints: {
|
||||
typehint: "translation"
|
||||
typehint: "translation",
|
||||
},
|
||||
required: origConfig.required ?? false,
|
||||
description: origConfig.description ?? "A translatable object"
|
||||
description: origConfig.description ?? "A translatable object",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,19 +231,21 @@ export abstract class EditJsonState<T> {
|
|||
|
||||
protected abstract getId(): Store<string>
|
||||
|
||||
protected abstract validate(configuration: Partial<T>): Promise<ConversionMessage[]>;
|
||||
protected abstract validate(configuration: Partial<T>): Promise<ConversionMessage[]>
|
||||
|
||||
/**
|
||||
* Creates a store that validates the configuration and which contains all relevant (error)-messages
|
||||
* @private
|
||||
*/
|
||||
private createMessagesStore(): Store<ConversionMessage[]> {
|
||||
return this.configuration.mapAsyncD(async (config) => {
|
||||
if(!this.validate){
|
||||
return []
|
||||
}
|
||||
return await this.validate(config)
|
||||
}).map(messages => messages ?? [])
|
||||
return this.configuration
|
||||
.mapAsyncD(async (config) => {
|
||||
if (!this.validate) {
|
||||
return []
|
||||
}
|
||||
return await this.validate(config)
|
||||
})
|
||||
.map((messages) => messages ?? [])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -311,7 +311,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
public readonly imageUploadManager = {
|
||||
getCountsFor() {
|
||||
return 0
|
||||
}
|
||||
},
|
||||
}
|
||||
public readonly layout: { getMatchingLayer: (key: any) => LayerConfig }
|
||||
public readonly featureSwitches: {
|
||||
|
|
@ -327,8 +327,8 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
properties: this.testTags.data,
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [3.21, 51.2]
|
||||
}
|
||||
coordinates: [3.21, 51.2],
|
||||
},
|
||||
}
|
||||
|
||||
constructor(
|
||||
|
|
@ -346,10 +346,10 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
} catch (e) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
this.featureSwitches = {
|
||||
featureSwitchIsDebugging: new UIEventSource<boolean>(true)
|
||||
featureSwitchIsDebugging: new UIEventSource<boolean>(true),
|
||||
}
|
||||
|
||||
this.addMissingTagRenderingIds()
|
||||
|
|
@ -426,8 +426,9 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
})
|
||||
}
|
||||
|
||||
protected async validate(configuration: Partial<LayerConfigJson>): Promise<ConversionMessage[]> {
|
||||
|
||||
protected async validate(
|
||||
configuration: Partial<LayerConfigJson>
|
||||
): Promise<ConversionMessage[]> {
|
||||
const layers = AllSharedLayers.getSharedLayersConfigs()
|
||||
|
||||
const questions = layers.get("questions")
|
||||
|
|
@ -437,7 +438,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
}
|
||||
const state: DesugaringContext = {
|
||||
tagRenderings: sharedQuestions,
|
||||
sharedLayers: layers
|
||||
sharedLayers: layers,
|
||||
}
|
||||
const prepare = this.buildValidation(state)
|
||||
const context = ConversionContext.construct([], ["prepare"])
|
||||
|
|
@ -475,7 +476,7 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
|
|||
/** Applies a few bandaids to get everything smoothed out in case of errors; a big bunch of hacks basically
|
||||
*/
|
||||
public setupFixers() {
|
||||
this.configuration.addCallbackAndRunD(config => {
|
||||
this.configuration.addCallbackAndRunD((config) => {
|
||||
if (config.layers) {
|
||||
// Remove 'null' and 'undefined' values from the layer array if any are found
|
||||
for (let i = config.layers.length; i >= 0; i--) {
|
||||
|
|
@ -488,17 +489,16 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
|
|||
}
|
||||
|
||||
protected async validate(configuration: Partial<LayoutConfigJson>) {
|
||||
|
||||
const layers = AllSharedLayers.getSharedLayersConfigs()
|
||||
|
||||
for (const l of configuration.layers ?? []) {
|
||||
if(typeof l !== "string"){
|
||||
if (typeof l !== "string") {
|
||||
continue
|
||||
}
|
||||
if (!l.startsWith("https://")) {
|
||||
continue
|
||||
}
|
||||
const config = <LayerConfigJson> await Utils.downloadJsonCached(l, 1000*60*10)
|
||||
const config = <LayerConfigJson>await Utils.downloadJsonCached(l, 1000 * 60 * 10)
|
||||
layers.set(l, config)
|
||||
}
|
||||
|
||||
|
|
@ -509,11 +509,11 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
|
|||
}
|
||||
const state: DesugaringContext = {
|
||||
tagRenderings: sharedQuestions,
|
||||
sharedLayers: layers
|
||||
sharedLayers: layers,
|
||||
}
|
||||
const prepare = this.buildValidation(state)
|
||||
const context = ConversionContext.construct([], ["prepare"])
|
||||
if(configuration.layers){
|
||||
if (configuration.layers) {
|
||||
Utils.NoNullInplace(configuration.layers)
|
||||
}
|
||||
try {
|
||||
|
|
@ -524,5 +524,4 @@ export class EditThemeState extends EditJsonState<LayoutConfigJson> {
|
|||
}
|
||||
return context.messages
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,40 +22,37 @@
|
|||
export let selfLayers: { owner: number; id: string }[]
|
||||
export let otherLayers: { owner: number; id: string }[]
|
||||
{
|
||||
|
||||
/**
|
||||
* We modify the schema and inject options for self-declared layers
|
||||
*/
|
||||
|
||||
const layerSchema = schema.find(l => l.path.join(".") === "layers")
|
||||
const suggestions: { if: string, then: string }[] = layerSchema.hints.suggestions
|
||||
suggestions.unshift(...selfLayers.map(
|
||||
l => ({
|
||||
const layerSchema = schema.find((l) => l.path.join(".") === "layers")
|
||||
const suggestions: { if: string; then: string }[] = layerSchema.hints.suggestions
|
||||
suggestions.unshift(
|
||||
...selfLayers.map((l) => ({
|
||||
if: `value=https://studio.mapcomplete.org/${l.owner}/layers/${l.id}/${l.id}.json`,
|
||||
then: `<b>${l.id}</b> (made by you)`
|
||||
})
|
||||
))
|
||||
then: `<b>${l.id}</b> (made by you)`,
|
||||
}))
|
||||
)
|
||||
|
||||
for (let i = 0; i < otherLayers.length; i++) {
|
||||
const l = otherLayers[i]
|
||||
const mapping = {
|
||||
if: `value=https://studio.mapcomplete.org/${l.owner}/layers/${l.id}/${l.id}.json`,
|
||||
then: `<b>${l.id}</b> (made by ${l.owner})`
|
||||
then: `<b>${l.id}</b> (made by ${l.owner})`,
|
||||
}
|
||||
/**
|
||||
* This is a filthy hack which is time-sensitive and will break
|
||||
* It downloads the username and patches the suggestion, assuming that the list with all layers will be shown a while _after_ loading the view.
|
||||
* Caching in 'getInformationAboutUser' helps with this as well
|
||||
*/
|
||||
osmConnection.getInformationAboutUser(l.owner).then(userInfo => {
|
||||
osmConnection.getInformationAboutUser(l.owner).then((userInfo) => {
|
||||
mapping.then = `<b>${l.id}</b> (made by ${userInfo.display_name})`
|
||||
})
|
||||
suggestions.push(mapping)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
let messages = state.messages
|
||||
let hasErrors = messages.map(
|
||||
(m: ConversionMessage[]) => m.filter((m) => m.level === "error").length
|
||||
|
|
@ -102,8 +99,7 @@
|
|||
<div slot="content0" class="mb-8">
|
||||
<Region configs={perRegion["basic"]} path={[]} {state} title="Basic properties" />
|
||||
<Region configs={perRegion["start_location"]} path={[]} {state} title="Start location" />
|
||||
<DeleteButton {state} {backToStudio} objectType="theme"/>
|
||||
|
||||
<DeleteButton {state} {backToStudio} objectType="theme" />
|
||||
</div>
|
||||
|
||||
<div slot="title1">Layers</div>
|
||||
|
|
@ -126,10 +122,11 @@
|
|||
Below, you'll find the raw configuration file in `.json`-format. This is mostly for
|
||||
debugging purposes, but you can also edit the file directly if you want.
|
||||
</div>
|
||||
<div class="literal-code overflow-y-auto h-full" style="min-height: 75%">
|
||||
<div class="literal-code h-full overflow-y-auto" style="min-height: 75%">
|
||||
<RawEditor {state} />
|
||||
</div>
|
||||
<ShowConversionMessages messages={$messages} />
|
||||
</div>
|
||||
</TabbedGroup>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
{/if}
|
||||
</NextButton>
|
||||
{#if description}
|
||||
<Markdown src={description}/>
|
||||
<Markdown src={description} />
|
||||
{/if}
|
||||
{#each $messages as message}
|
||||
<ShowConversionMessage {message} />
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@
|
|||
<h3>{schema.path.at(-1)}</h3>
|
||||
|
||||
{#if subparts.length > 0}
|
||||
<Markdown src={schema.description}/>
|
||||
<Markdown src={schema.description} />
|
||||
{/if}
|
||||
{#if $currentValue === undefined}
|
||||
No array defined
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@
|
|||
type = type.substring(0, type.length - 2)
|
||||
}
|
||||
|
||||
const configJson: QuestionableTagRenderingConfigJson & {questionHintIsMd: boolean} = {
|
||||
const configJson: QuestionableTagRenderingConfigJson & { questionHintIsMd: boolean } = {
|
||||
id: path.join("_"),
|
||||
render: rendervalue,
|
||||
question: schema.hints.question,
|
||||
|
|
|
|||
|
|
@ -40,9 +40,10 @@
|
|||
if (lastIsString) {
|
||||
types.splice(types.length - 1, 1)
|
||||
}
|
||||
const configJson: QuestionableTagRenderingConfigJson & {questionHintIsMd: boolean}= {
|
||||
const configJson: QuestionableTagRenderingConfigJson & { questionHintIsMd: boolean } = {
|
||||
id: "TYPE_OF:" + path.join("_"),
|
||||
question: schema.hints.question ?? "Which subcategory is needed for " + schema.path.at(-1) + "?",
|
||||
question:
|
||||
schema.hints.question ?? "Which subcategory is needed for " + schema.path.at(-1) + "?",
|
||||
questionHint: schema.description,
|
||||
questionHintIsMd: true,
|
||||
mappings: types
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ import Hash from "../../Logic/Web/Hash"
|
|||
|
||||
export default class StudioHashSetter {
|
||||
constructor(mode: "layer" | "theme", tab: Store<number>, name: Store<string>) {
|
||||
tab.mapD(tab => {
|
||||
tab.mapD(
|
||||
(tab) => {
|
||||
Hash.hash.setData(mode + "/" + name.data + "/" + tab)
|
||||
}
|
||||
, [name])
|
||||
},
|
||||
[name]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,13 @@ import { LayoutConfigJson } from "../../Models/ThemeConfig/Json/LayoutConfigJson
|
|||
export default class StudioServer {
|
||||
private readonly url: string
|
||||
private readonly _userId: Store<number>
|
||||
private readonly overview: UIEventSource<{
|
||||
success: { id: string; owner: number; category: "layers" | "themes" }[]
|
||||
} | { error: any } | undefined>
|
||||
private readonly overview: UIEventSource<
|
||||
| {
|
||||
success: { id: string; owner: number; category: "layers" | "themes" }[]
|
||||
}
|
||||
| { error: any }
|
||||
| undefined
|
||||
>
|
||||
|
||||
constructor(url: string, userId: Store<number>) {
|
||||
this.url = url
|
||||
|
|
@ -21,9 +25,13 @@ export default class StudioServer {
|
|||
this.overview = UIEventSource.FromPromiseWithErr(this.fetchOverviewRaw())
|
||||
}
|
||||
|
||||
public fetchOverview(): Store<{
|
||||
success: { id: string; owner: number; category: "layers" | "themes" }[]
|
||||
} | { error } | undefined> {
|
||||
public fetchOverview(): Store<
|
||||
| {
|
||||
success: { id: string; owner: number; category: "layers" | "themes" }[]
|
||||
}
|
||||
| { error }
|
||||
| undefined
|
||||
> {
|
||||
return this.overview
|
||||
}
|
||||
|
||||
|
|
@ -80,11 +88,15 @@ export default class StudioServer {
|
|||
return
|
||||
}
|
||||
await fetch(this.urlFor(id, category), {
|
||||
method: "DELETE"
|
||||
method: "DELETE",
|
||||
})
|
||||
const overview: { id: string; owner: number; category: "layers" | "themes" }[] = this.overview.data?.["success"]
|
||||
const overview: { id: string; owner: number; category: "layers" | "themes" }[] =
|
||||
this.overview.data?.["success"]
|
||||
if (overview) {
|
||||
const index = overview.findIndex(obj => obj.id === id && obj.category === category && obj.owner === this._userId.data)
|
||||
const index = overview.findIndex(
|
||||
(obj) =>
|
||||
obj.id === id && obj.category === category && obj.owner === this._userId.data
|
||||
)
|
||||
if (index >= 0) {
|
||||
overview.splice(index, 1)
|
||||
this.overview.ping()
|
||||
|
|
@ -99,9 +111,9 @@ export default class StudioServer {
|
|||
await fetch(this.urlFor(id, category), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json;charset=utf-8"
|
||||
"Content-Type": "application/json;charset=utf-8",
|
||||
},
|
||||
body: config
|
||||
body: config,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
import type { ConfigMeta } from "./configMeta"
|
||||
import type {
|
||||
MappingConfigJson,
|
||||
QuestionableTagRenderingConfigJson
|
||||
QuestionableTagRenderingConfigJson,
|
||||
} from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"
|
||||
|
|
@ -59,8 +59,8 @@
|
|||
labelMapping = {
|
||||
if: "value=" + label,
|
||||
then: {
|
||||
en: "Builtin collection <b>" + label + "</b>:"
|
||||
}
|
||||
en: "Builtin collection <b>" + label + "</b>:",
|
||||
},
|
||||
}
|
||||
perLabel[label] = labelMapping
|
||||
mappingsBuiltin.push(labelMapping)
|
||||
|
|
@ -72,14 +72,14 @@
|
|||
mappingsBuiltin.push({
|
||||
if: "value=" + tr["id"],
|
||||
then: {
|
||||
en: "Builtin <b>" + tr["id"] + "</b> <div class='subtle'>" + description + "</div>"
|
||||
}
|
||||
en: "Builtin <b>" + tr["id"] + "</b> <div class='subtle'>" + description + "</div>",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const configBuiltin = new TagRenderingConfig(<QuestionableTagRenderingConfigJson>{
|
||||
question: "Which builtin element should be shown?",
|
||||
mappings: mappingsBuiltin
|
||||
mappings: mappingsBuiltin,
|
||||
})
|
||||
|
||||
const tags = new UIEventSource({ value })
|
||||
|
|
@ -112,7 +112,7 @@
|
|||
"condition",
|
||||
"metacondition",
|
||||
"mappings",
|
||||
"icon"
|
||||
"icon",
|
||||
])
|
||||
const ignored = new Set(["labels", "description", "classes"])
|
||||
|
||||
|
|
|
|||
|
|
@ -44,11 +44,11 @@
|
|||
)
|
||||
let osmConnection = new OsmConnection({
|
||||
oauth_token,
|
||||
checkOnlineRegularly: true
|
||||
checkOnlineRegularly: true,
|
||||
})
|
||||
const expertMode = UIEventSource.asBoolean(
|
||||
osmConnection.GetPreference("studio-expert-mode", "false", {
|
||||
documentation: "Indicates if more options are shown in mapcomplete studio"
|
||||
documentation: "Indicates if more options are shown in mapcomplete studio",
|
||||
})
|
||||
)
|
||||
expertMode.addCallbackAndRunD((expert) => console.log("Expert mode is", expert))
|
||||
|
|
@ -165,18 +165,18 @@
|
|||
marker: [
|
||||
{
|
||||
icon: "circle",
|
||||
color: "white"
|
||||
}
|
||||
]
|
||||
}
|
||||
color: "white",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
tagRenderings: ["images"],
|
||||
lineRendering: [
|
||||
{
|
||||
width: 1,
|
||||
color: "blue"
|
||||
}
|
||||
]
|
||||
color: "blue",
|
||||
},
|
||||
],
|
||||
}
|
||||
editLayerState.configuration.setData(initialLayerConfig)
|
||||
editLayerState.startSavingUpdates()
|
||||
|
|
@ -194,10 +194,11 @@
|
|||
const event = {
|
||||
detail: {
|
||||
id,
|
||||
owner: uid.data
|
||||
}
|
||||
owner: uid.data,
|
||||
},
|
||||
}
|
||||
const statePromise: Promise<EditJsonState<any>> = mode === "layer" ? editLayer(event) : editTheme(event)
|
||||
const statePromise: Promise<EditJsonState<any>> =
|
||||
mode === "layer" ? editLayer(event) : editTheme(event)
|
||||
const state = await statePromise
|
||||
state.selectedTab.setData(Number(tab))
|
||||
}
|
||||
|
|
@ -221,8 +222,8 @@
|
|||
<li>Try again in a few minutes</li>
|
||||
<li>
|
||||
Contact <a href="https://app.element.io/#/room/#MapComplete:matrix.org">
|
||||
the MapComplete community via the chat.
|
||||
</a>
|
||||
the MapComplete community via the chat.
|
||||
</a>
|
||||
Someone might be able to help you
|
||||
</li>
|
||||
<li>
|
||||
|
|
@ -284,11 +285,7 @@
|
|||
</div>
|
||||
{:else if state === "edit_layer"}
|
||||
<div class="m-4 flex flex-col">
|
||||
<BackButton
|
||||
clss="small p-1"
|
||||
imageClass="w-8 h-8"
|
||||
on:click={() => backToStudio()}
|
||||
>
|
||||
<BackButton clss="small p-1" imageClass="w-8 h-8" on:click={() => backToStudio()}>
|
||||
MapComplete Studio
|
||||
</BackButton>
|
||||
<h2>Choose a layer to edit</h2>
|
||||
|
|
@ -331,11 +328,7 @@
|
|||
</div>
|
||||
{:else if state === "edit_theme"}
|
||||
<div class="m-4 flex flex-col">
|
||||
<BackButton
|
||||
clss="small p-1"
|
||||
imageClass="w-8 h-8"
|
||||
on:click={() => backToStudio()}
|
||||
>
|
||||
<BackButton clss="small p-1" imageClass="w-8 h-8" on:click={() => backToStudio()}>
|
||||
MapComplete Studio
|
||||
</BackButton>
|
||||
<h2>Choose a theme to edit</h2>
|
||||
|
|
@ -372,26 +365,20 @@
|
|||
<Loading />
|
||||
</div>
|
||||
{:else if state === "editing_layer"}
|
||||
<EditLayer
|
||||
state={editLayerState}
|
||||
{backToStudio}
|
||||
>
|
||||
<BackButton
|
||||
clss="small p-1"
|
||||
imageClass="w-8 h-8"
|
||||
on:click={() => backToStudio()}
|
||||
>
|
||||
<EditLayer state={editLayerState} {backToStudio}>
|
||||
<BackButton clss="small p-1" imageClass="w-8 h-8" on:click={() => backToStudio()}>
|
||||
MapComplete Studio
|
||||
</BackButton>
|
||||
</EditLayer>
|
||||
{:else if state === "editing_theme"}
|
||||
<EditTheme state={editThemeState} selfLayers={$selfLayers} otherLayers={$otherLayers} {osmConnection}
|
||||
{backToStudio}>
|
||||
<BackButton
|
||||
clss="small p-1"
|
||||
imageClass="w-8 h-8"
|
||||
on:click={() => backToStudio()}
|
||||
>
|
||||
<EditTheme
|
||||
state={editThemeState}
|
||||
selfLayers={$selfLayers}
|
||||
otherLayers={$otherLayers}
|
||||
{osmConnection}
|
||||
{backToStudio}
|
||||
>
|
||||
<BackButton clss="small p-1" imageClass="w-8 h-8" on:click={() => backToStudio()}>
|
||||
MapComplete Studio
|
||||
</BackButton>
|
||||
</EditTheme>
|
||||
|
|
|
|||
|
|
@ -7,165 +7,165 @@
|
|||
</script>
|
||||
|
||||
<main>
|
||||
<div>
|
||||
<h1>Stylesheet testing grounds</h1>
|
||||
<div>
|
||||
<h1>Stylesheet testing grounds</h1>
|
||||
|
||||
This document exists to explore the style hierarchy.
|
||||
This document exists to explore the style hierarchy.
|
||||
|
||||
<div class="normal-background">
|
||||
<h2>Normal background</h2>
|
||||
There are a few styles, such as the
|
||||
<span class="literal-code">normal-background</span>
|
||||
-style which is used if there is nothing special going on. Some general information, with at most
|
||||
<a href="https://example.com" target="_blank">a link to someplace</a>
|
||||
<div class="subtle">Subtle</div>
|
||||
<div class="normal-background">
|
||||
<h2>Normal background</h2>
|
||||
There are a few styles, such as the
|
||||
<span class="literal-code">normal-background</span>
|
||||
-style which is used if there is nothing special going on. Some general information, with at most
|
||||
<a href="https://example.com" target="_blank">a link to someplace</a>
|
||||
<div class="subtle">Subtle</div>
|
||||
|
||||
<div class="alert">Alert: something went wrong</div>
|
||||
<div class="warning">Warning</div>
|
||||
<div class="information">Some important information</div>
|
||||
<div class="thanks">Thank you! Operation successful</div>
|
||||
<div class="alert">Alert: something went wrong</div>
|
||||
<div class="warning">Warning</div>
|
||||
<div class="information">Some important information</div>
|
||||
<div class="thanks">Thank you! Operation successful</div>
|
||||
|
||||
<Login class="h-12 w-12" />
|
||||
<Loading>Loading...</Loading>
|
||||
<Login class="h-12 w-12" />
|
||||
<Loading>Loading...</Loading>
|
||||
</div>
|
||||
|
||||
<div class="low-interaction flex flex-col">
|
||||
<h2>Low interaction</h2>
|
||||
<p>
|
||||
There are <span class="literal-code">low-interaction</span>
|
||||
areas, where some buttons might appear.
|
||||
</p>
|
||||
|
||||
<div class="border-interactive interactive">
|
||||
Highly interactive area (mostly: active question)
|
||||
</div>
|
||||
<div class="subtle">Subtle</div>
|
||||
|
||||
<div class="flex">
|
||||
<button class="primary">
|
||||
<Community class="h-6 w-6" />
|
||||
Main action
|
||||
</button>
|
||||
<button class="primary disabled">
|
||||
<Community class="h-6 w-6" />
|
||||
Main action (disabled)
|
||||
</button>
|
||||
|
||||
<button class="small">
|
||||
<Community class="h-6 w-6" />
|
||||
Small button
|
||||
</button>
|
||||
|
||||
<button class="small primary">Small button</button>
|
||||
<button class="small primary disabled">Small, disabled button</button>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<button>
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action
|
||||
</button>
|
||||
<button class="disabled">
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action (disabled)
|
||||
</button>
|
||||
</div>
|
||||
<input type="text" />
|
||||
|
||||
<div class="flex flex-col">
|
||||
<label class="checked" for="html">
|
||||
<input id="html" name="fav_language" type="radio" value="HTML" />
|
||||
HTML (mimicks a
|
||||
<span class="literal-code">checked</span>
|
||||
-element)
|
||||
<Dropdown value={new UIEventSource("abc")}>
|
||||
<option>abc</option>
|
||||
<option>def</option>
|
||||
</Dropdown>
|
||||
</label>
|
||||
<label for="css">
|
||||
<input id="css" name="fav_language" type="radio" value="CSS" />
|
||||
CSS
|
||||
</label>
|
||||
<label for="javascript">
|
||||
<input id="javascript" name="fav_language" type="radio" value="JavaScript" />
|
||||
<Community class="h-8 w-8" />
|
||||
JavaScript
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="alert">Alert: something went wrong</div>
|
||||
<div class="warning">Warning</div>
|
||||
<div class="information">Some important information</div>
|
||||
<div class="thanks">Thank you! Operation successful</div>
|
||||
|
||||
<Login class="h-12 w-12" />
|
||||
<Loading>Loading...</Loading>
|
||||
</div>
|
||||
|
||||
<div class="interactive flex flex-col">
|
||||
<h2>Interactive area</h2>
|
||||
<p>
|
||||
There are <span class="literal-code">interactive</span>
|
||||
areas, where many buttons and input elements will appear.
|
||||
</p>
|
||||
<div class="subtle">Subtle</div>
|
||||
|
||||
<div class="flex">
|
||||
<button class="primary">
|
||||
<Community class="h-6 w-6" />
|
||||
Main action
|
||||
</button>
|
||||
<button class="primary disabled">
|
||||
<Community class="h-6 w-6" />
|
||||
Main action (disabled)
|
||||
</button>
|
||||
<button class="small">
|
||||
<Community class="h-6 w-6" />
|
||||
Small button
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<button>
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action
|
||||
</button>
|
||||
<button class="disabled">
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action (disabled)
|
||||
</button>
|
||||
</div>
|
||||
<div class="alert">Alert: something went wrong</div>
|
||||
<div class="warning">Warning</div>
|
||||
<div class="information">Some important information</div>
|
||||
<div class="thanks">Thank you! Operation successful</div>
|
||||
|
||||
<Login class="h-12 w-12" />
|
||||
<Loading>Loading...</Loading>
|
||||
<div>
|
||||
<label for="html0">
|
||||
<input id="html0" name="fav_language" type="radio" value="HTML" />
|
||||
HTML
|
||||
</label>
|
||||
<label for="css0">
|
||||
<input id="css0" name="fav_language" type="radio" value="CSS" />
|
||||
CSS
|
||||
</label>
|
||||
<label for="javascript0">
|
||||
<input id="javascript0" name="fav_language" type="radio" value="JavaScript" />
|
||||
JavaScript
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="border-interactive">
|
||||
Area with extreme high interactivity due to `border-interactive`
|
||||
</div>
|
||||
|
||||
<select>
|
||||
<option value="A">A</option>
|
||||
<option value="B">B</option>
|
||||
<option value="C">C</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="low-interaction flex flex-col">
|
||||
<h2>Low interaction</h2>
|
||||
<p>
|
||||
There are <span class="literal-code">low-interaction</span>
|
||||
areas, where some buttons might appear.
|
||||
</p>
|
||||
|
||||
<div class="border-interactive interactive">
|
||||
Highly interactive area (mostly: active question)
|
||||
</div>
|
||||
<div class="subtle">Subtle</div>
|
||||
|
||||
<div class="flex">
|
||||
<button class="primary">
|
||||
<Community class="h-6 w-6" />
|
||||
Main action
|
||||
</button>
|
||||
<button class="primary disabled">
|
||||
<Community class="h-6 w-6" />
|
||||
Main action (disabled)
|
||||
</button>
|
||||
|
||||
<button class="small">
|
||||
<Community class="h-6 w-6" />
|
||||
Small button
|
||||
</button>
|
||||
|
||||
<button class="small primary">Small button</button>
|
||||
<button class="small primary disabled">Small, disabled button</button>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<button>
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action
|
||||
</button>
|
||||
<button class="disabled">
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action (disabled)
|
||||
</button>
|
||||
</div>
|
||||
<input type="text" />
|
||||
|
||||
<div class="flex flex-col">
|
||||
<label class="checked" for="html">
|
||||
<input id="html" name="fav_language" type="radio" value="HTML" />
|
||||
HTML (mimicks a
|
||||
<span class="literal-code">checked</span>
|
||||
-element)
|
||||
<Dropdown value={new UIEventSource("abc")}>
|
||||
<option>abc</option>
|
||||
<option>def</option>
|
||||
</Dropdown>
|
||||
</label>
|
||||
<label for="css">
|
||||
<input id="css" name="fav_language" type="radio" value="CSS" />
|
||||
CSS
|
||||
</label>
|
||||
<label for="javascript">
|
||||
<input id="javascript" name="fav_language" type="radio" value="JavaScript" />
|
||||
<Community class="h-8 w-8" />
|
||||
JavaScript
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="alert">Alert: something went wrong</div>
|
||||
<div class="warning">Warning</div>
|
||||
<div class="information">Some important information</div>
|
||||
<div class="thanks">Thank you! Operation successful</div>
|
||||
|
||||
<Login class="h-12 w-12" />
|
||||
<Loading>Loading...</Loading>
|
||||
</div>
|
||||
|
||||
<div class="interactive flex flex-col">
|
||||
<h2>Interactive area</h2>
|
||||
<p>
|
||||
There are <span class="literal-code">interactive</span>
|
||||
areas, where many buttons and input elements will appear.
|
||||
</p>
|
||||
<div class="subtle">Subtle</div>
|
||||
|
||||
<div class="flex">
|
||||
<button class="primary">
|
||||
<Community class="h-6 w-6" />
|
||||
Main action
|
||||
</button>
|
||||
<button class="primary disabled">
|
||||
<Community class="h-6 w-6" />
|
||||
Main action (disabled)
|
||||
</button>
|
||||
<button class="small">
|
||||
<Community class="h-6 w-6" />
|
||||
Small button
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<button>
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action
|
||||
</button>
|
||||
<button class="disabled">
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action (disabled)
|
||||
</button>
|
||||
</div>
|
||||
<div class="alert">Alert: something went wrong</div>
|
||||
<div class="warning">Warning</div>
|
||||
<div class="information">Some important information</div>
|
||||
<div class="thanks">Thank you! Operation successful</div>
|
||||
|
||||
<Login class="h-12 w-12" />
|
||||
<Loading>Loading...</Loading>
|
||||
<div>
|
||||
<label for="html0">
|
||||
<input id="html0" name="fav_language" type="radio" value="HTML" />
|
||||
HTML
|
||||
</label>
|
||||
<label for="css0">
|
||||
<input id="css0" name="fav_language" type="radio" value="CSS" />
|
||||
CSS
|
||||
</label>
|
||||
<label for="javascript0">
|
||||
<input id="javascript0" name="fav_language" type="radio" value="JavaScript" />
|
||||
JavaScript
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="border-interactive">
|
||||
Area with extreme high interactivity due to `border-interactive`
|
||||
</div>
|
||||
|
||||
<select>
|
||||
<option value="A">A</option>
|
||||
<option value="B">B</option>
|
||||
<option value="C">C</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<main>
|
||||
</main>
|
||||
<main />
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -11,7 +11,7 @@
|
|||
</script>
|
||||
|
||||
<div class="link-underline flex h-full w-full flex-col justify-between">
|
||||
<div class="overflow-y-auto h-full">
|
||||
<div class="h-full overflow-y-auto">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import Link from "../Base/Link"
|
|||
import BaseUIElement from "../BaseUIElement"
|
||||
import { Utils } from "../../Utils"
|
||||
import SvelteUIElement from "../Base/SvelteUIElement"
|
||||
import {default as Wikidata_icon} from "../../assets/svg/Wikidata.svelte"
|
||||
import { default as Wikidata_icon } from "../../assets/svg/Wikidata.svelte"
|
||||
import Gender_male from "../../assets/svg/Gender_male.svelte"
|
||||
import Gender_female from "../../assets/svg/Gender_female.svelte"
|
||||
import Gender_inter from "../../assets/svg/Gender_inter.svelte"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@
|
|||
* Shows a wikipedia-article + wikidata preview for the given item
|
||||
*/
|
||||
export let wikipediaDetails: Store<FullWikipediaDetails>
|
||||
let titleOnly = wikipediaDetails.mapD(details => Object.keys(details).length === 1 && details.title !== undefined)
|
||||
let titleOnly = wikipediaDetails.mapD(
|
||||
(details) => Object.keys(details).length === 1 && details.title !== undefined
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if $titleOnly}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,14 @@ export class Translation extends BaseUIElement {
|
|||
*/
|
||||
private _strictLanguages: boolean
|
||||
|
||||
constructor(translations: string | Record<string, string>, context?: string, strictLanguages?: boolean) {
|
||||
constructor(
|
||||
translations: string | Record<string, string>,
|
||||
context?: string,
|
||||
strictLanguages?: boolean
|
||||
) {
|
||||
super()
|
||||
this._strictLanguages = strictLanguages
|
||||
if(strictLanguages){
|
||||
if (strictLanguages) {
|
||||
console.log(">>> strict:", translations)
|
||||
}
|
||||
if (translations === undefined) {
|
||||
|
|
@ -85,7 +89,7 @@ export class Translation extends BaseUIElement {
|
|||
* Indicates what language is effectively returned by `current`.
|
||||
* In most cases, this will be the language of choice, but if no translation is available, this will probably be `en`
|
||||
*/
|
||||
get currentLang(): Store<string>{
|
||||
get currentLang(): Store<string> {
|
||||
if (!this._currentLanguage) {
|
||||
this._currentLanguage = Locale.language.map(
|
||||
(l) => this.actualLanguage(l),
|
||||
|
|
@ -100,7 +104,7 @@ export class Translation extends BaseUIElement {
|
|||
|
||||
get current(): Store<string> {
|
||||
if (!this._current) {
|
||||
this._current = this.currentLang.map(l => this.translations[l])
|
||||
this._current = this.currentLang.map((l) => this.translations[l])
|
||||
}
|
||||
return this._current
|
||||
}
|
||||
|
|
@ -164,10 +168,10 @@ export class Translation extends BaseUIElement {
|
|||
return "*"
|
||||
}
|
||||
const txt = this.translations[language]
|
||||
if(txt === undefined && this._strictLanguages){
|
||||
if (txt === undefined && this._strictLanguages) {
|
||||
return undefined
|
||||
}
|
||||
if (txt !== undefined ) {
|
||||
if (txt !== undefined) {
|
||||
return language
|
||||
}
|
||||
if (this.translations["en"] !== undefined) {
|
||||
|
|
@ -186,7 +190,7 @@ export class Translation extends BaseUIElement {
|
|||
InnerConstructElement(): HTMLElement {
|
||||
const el = document.createElement("span")
|
||||
const self = this
|
||||
if(self.txt){
|
||||
if (self.txt) {
|
||||
el.innerHTML = self.txt
|
||||
}
|
||||
if (self.translations["*"] !== undefined) {
|
||||
|
|
@ -197,9 +201,9 @@ export class Translation extends BaseUIElement {
|
|||
if (self.isDestroyed) {
|
||||
return true
|
||||
}
|
||||
if(self.txt === undefined){
|
||||
if (self.txt === undefined) {
|
||||
el.innerHTML = ""
|
||||
}else{
|
||||
} else {
|
||||
el.innerHTML = self.txt
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue