More style tweaks

This commit is contained in:
Pieter Vander Vennet 2023-05-11 17:29:25 +02:00
parent 63a6fb2dd5
commit 07f9f1bb15
18 changed files with 304 additions and 188 deletions

View file

@ -256,7 +256,7 @@ export default class UserRelatedState {
_theme: layout?.id, _theme: layout?.id,
_backend: this.osmConnection.Backend(), _backend: this.osmConnection.Backend(),
_applicationOpened: new Date().toISOString(), _applicationOpened: new Date().toISOString(),
_supports_sharing: window.navigator.share ? "yes" : "no" _supports_sharing: "yes" // TODO window.navigator.share ? "yes" : "no"
}) })
for (const key in Constants.userJourney) { for (const key in Constants.userJourney) {

View file

@ -0,0 +1,34 @@
<script lang="ts">
import ToSvelte from "./ToSvelte.svelte";
import Svg from "../../Svg";
export let generateShareData: () => {
text: string
title: string
url: string
}
function share(){
alert("Sharing...")
if (!navigator.share) {
console.log("web share not supported")
return;
}
navigator
.share(this._shareData())
.then(() => {
console.log("Thanks for sharing!")
})
.catch((err) => {
console.log(`Couldn't share because of`, err.message)
})
}
</script>
<button on:click={share} class="secondary w-8 h-8 m-0 p-0">
<slot name="content">
<ToSvelte construct={Svg.share_svg().SetClass("w-7 h-7 p-1")}/>
</slot>
</button>

View file

@ -6,103 +6,122 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
import ToSvelte from "../Base/ToSvelte.svelte"; import ToSvelte from "../Base/ToSvelte.svelte";
import Checkbox from "../Base/Checkbox.svelte"; import Checkbox from "../Base/Checkbox.svelte";
import FilterConfig from "../../Models/ThemeConfig/FilterConfig"; import FilterConfig from "../../Models/ThemeConfig/FilterConfig";
import type { Writable } from "svelte/store"; import type {Writable} from "svelte/store";
import If from "../Base/If.svelte"; import If from "../Base/If.svelte";
import Dropdown from "../Base/Dropdown.svelte"; import Dropdown from "../Base/Dropdown.svelte";
import { onDestroy } from "svelte"; import {onDestroy} from "svelte";
import { ImmutableStore, Store } from "../../Logic/UIEventSource"; import {ImmutableStore, Store} from "../../Logic/UIEventSource";
import FilterviewWithFields from "./FilterviewWithFields.svelte"; import FilterviewWithFields from "./FilterviewWithFields.svelte";
import Tr from "../Base/Tr.svelte"; import Tr from "../Base/Tr.svelte";
import Translations from "../i18n/Translations"; import Translations from "../i18n/Translations";
export let filteredLayer: FilteredLayer; export let filteredLayer: FilteredLayer;
export let highlightedLayer: Store<string | undefined> = new ImmutableStore(undefined); export let highlightedLayer: Store<string | undefined> = new ImmutableStore(undefined);
export let zoomlevel: Store<number> = new ImmutableStore(22); export let zoomlevel: Store<number> = new ImmutableStore(22);
let layer: LayerConfig = filteredLayer.layerDef; let layer: LayerConfig = filteredLayer.layerDef;
let isDisplayed: boolean = filteredLayer.isDisplayed.data; let isDisplayed: boolean = filteredLayer.isDisplayed.data;
onDestroy(filteredLayer.isDisplayed.addCallbackAndRunD(d => { onDestroy(filteredLayer.isDisplayed.addCallbackAndRunD(d => {
isDisplayed = d; isDisplayed = d;
return false; return false;
})); }));
/** /**
* Gets a UIEventSource as boolean for the given option, to be used with a checkbox * Gets a UIEventSource as boolean for the given option, to be used with a checkbox
*/ */
function getBooleanStateFor(option: FilterConfig): Writable<boolean> { function getBooleanStateFor(option: FilterConfig): Writable<boolean> {
const state = filteredLayer.appliedFilters.get(option.id); const state = filteredLayer.appliedFilters.get(option.id);
return state.sync(f => f === 0, [], (b) => b ? 0 : undefined); return state.sync(f => f === 0, [], (b) => b ? 0 : undefined);
} }
/** /**
* Gets a UIEventSource as number for the given option, to be used with a dropdown or radiobutton * Gets a UIEventSource as number for the given option, to be used with a dropdown or radiobutton
*/ */
function getStateFor(option: FilterConfig): Writable<number> { function getStateFor(option: FilterConfig): Writable<number> {
return filteredLayer.appliedFilters.get(option.id); return filteredLayer.appliedFilters.get(option.id);
} }
let mainElem: HTMLElement; let mainElem: HTMLElement;
$: onDestroy( $: onDestroy(
highlightedLayer.addCallbackAndRun(highlightedLayer => { highlightedLayer.addCallbackAndRun(highlightedLayer => {
if (highlightedLayer === filteredLayer.layerDef.id) { if (highlightedLayer === filteredLayer.layerDef.id) {
mainElem?.classList?.add("glowing-shadow"); mainElem?.classList?.add("glowing-shadow");
} else { } else {
mainElem?.classList?.remove("glowing-shadow"); mainElem?.classList?.remove("glowing-shadow");
} }
}) })
); );
</script> </script>
{#if filteredLayer.layerDef.name} {#if filteredLayer.layerDef.name}
<div bind:this={mainElem}> <div bind:this={mainElem}>
<label class="flex gap-1"> <label class="flex gap-1">
<Checkbox selected={filteredLayer.isDisplayed} /> <Checkbox selected={filteredLayer.isDisplayed}/>
<If condition={filteredLayer.isDisplayed}> <If condition={filteredLayer.isDisplayed}>
<ToSvelte construct={() => layer.defaultIcon()?.SetClass("block h-6 w-6")}></ToSvelte> <ToSvelte
<ToSvelte slot="else" construct={() => layer.defaultIcon()?.SetClass("block h-6 w-6 opacity-50")}></ToSvelte> construct={() => layer.defaultIcon()?.SetClass("block h-6 w-6 no-image-background")}></ToSvelte>
</If> <ToSvelte slot="else"
construct={() => layer.defaultIcon()?.SetClass("block h-6 w-6 no-image-background opacity-50")}></ToSvelte>
</If>
{filteredLayer.layerDef.name} {filteredLayer.layerDef.name}
{#if $zoomlevel < layer.minzoom} {#if $zoomlevel < layer.minzoom}
<span class="alert"> <span class="alert">
<Tr t={Translations.t.general.layerSelection.zoomInToSeeThisLayer} /> <Tr t={Translations.t.general.layerSelection.zoomInToSeeThisLayer}/>
</span> </span>
{/if}
</label>
<If condition={filteredLayer.isDisplayed}>
<div id="subfilters" class="flex flex-col gap-y-1 mb-4 ml-4">
{#each filteredLayer.layerDef.filters as filter}
<div>
<!-- There are three (and a half) modes of filters: a single checkbox, a radio button/dropdown or with fields -->
{#if filter.options.length === 1 && filter.options[0].fields.length === 0}
<label>
<Checkbox selected={getBooleanStateFor(filter)} />
{filter.options[0].question}
</label>
{/if} {/if}
{#if filter.options.length === 1 && filter.options[0].fields.length > 0} </label>
<FilterviewWithFields id={filter.id} filteredLayer={filteredLayer}
option={filter.options[0]}></FilterviewWithFields>
{/if}
{#if filter.options.length > 1} <If condition={filteredLayer.isDisplayed}>
<Dropdown value={getStateFor(filter)}> <div id="subfilters" class="flex flex-col gap-y-1 mb-4 ml-4">
{#each filter.options as option, i} {#each filteredLayer.layerDef.filters as filter}
<option value={i}> <div>
{ option.question}
</option> <!-- There are three (and a half) modes of filters: a single checkbox, a radio button/dropdown or with searchable fields -->
{#if filter.options.length === 1 && filter.options[0].fields.length === 0}
<label>
<Checkbox selected={getBooleanStateFor(filter)}/>
{filter.options[0].question}
</label>
{/if}
{#if filter.options.length === 1 && filter.options[0].fields.length > 0}
<FilterviewWithFields id={filter.id} filteredLayer={filteredLayer}
option={filter.options[0]}></FilterviewWithFields>
{/if}
{#if filter.options.length > 1}
<Dropdown value={getStateFor(filter)}>
{#each filter.options as option, i}
<option value={i}>
{ option.question}
</option>
{/each}
</Dropdown>
{/if}
</div>
{/each} {/each}
</Dropdown> </div>
{/if} </If>
</div>
</div>
{/each}
</div>
</If>
</div>
{/if} {/if}
<style>
:global(.no-image-background * img) {
background: none;
margin: 0;
padding: 0;
}
:global(.no-image-background * svg) {
background: none;
margin: 0;
padding: 0;
}
</style>

View file

@ -31,7 +31,7 @@
{#if _tags._deleted === "yes"} {#if _tags._deleted === "yes"}
<Tr t={ Translations.t.delete.isDeleted}/> <Tr t={ Translations.t.delete.isDeleted}/>
{:else} {:else}
<div class="flex border-b-2 border-black shadow justify-between items-center"> <div class="flex border-b-2 border-black drop-shadow-md justify-between items-center low-interaction px-3 active-links">
<div class="flex flex-col"> <div class="flex flex-col">
<!-- Title element--> <!-- Title element-->
@ -40,12 +40,12 @@
{layer}></TagRenderingAnswer> {layer}></TagRenderingAnswer>
</h3> </h3>
<div class="flex flex-row flex-wrap pt-0.5 sm:pt-1 items-center mr-2"> <div class="flex flex-row flex-wrap pt-0.5 sm:pt-1 items-center mr-2 gap-x-0.5 p-1">
{#each layer.titleIcons as titleIconConfig} {#each layer.titleIcons as titleIconConfig}
{#if (titleIconConfig.condition?.matchesProperties(_tags) ?? true) && (titleIconConfig.metacondition?.matchesProperties({..._metatags, ..._tags}) ?? true) && titleIconConfig.IsKnown(_tags)} {#if (titleIconConfig.condition?.matchesProperties(_tags) ?? true) && (titleIconConfig.metacondition?.matchesProperties({..._metatags, ..._tags}) ?? true) && titleIconConfig.IsKnown(_tags)}
<div class="w-8 h-8"> <div class="w-8 h-8 flex items-center">
<TagRenderingAnswer config={titleIconConfig} {tags} {selectedElement} {state} <TagRenderingAnswer config={titleIconConfig} {tags} {selectedElement} {state}
{layer}></TagRenderingAnswer> {layer} extraClasses="h-full justify-center" ></TagRenderingAnswer>
</div> </div>
{/if} {/if}
{/each} {/each}
@ -55,3 +55,7 @@
<XCircleIcon class="w-8 h-8 cursor-pointer" on:click={() => state.selectedElement.setData(undefined)}/> <XCircleIcon class="w-8 h-8 cursor-pointer" on:click={() => state.selectedElement.setData(undefined)}/>
</div> </div>
{/if} {/if}
<style>
</style>

View file

@ -30,7 +30,7 @@
{#if _tags._deleted === "yes"} {#if _tags._deleted === "yes"}
<Tr t={ Translations.t.delete.isDeleted}/> <Tr t={ Translations.t.delete.isDeleted}/>
{:else} {:else}
<div class="flex flex-col overflow-y-auto"> <div class="flex flex-col overflow-y-auto p-1 px-2 gap-y-2">
{#each layer.tagRenderings as config (config.id)} {#each layer.tagRenderings as config (config.id)}
{#if (config.condition === undefined || config.condition.matchesProperties(_tags)) && (config.metacondition === undefined || config.metacondition.matchesProperties({..._tags, ..._metatags}))} {#if (config.condition === undefined || config.condition.matchesProperties(_tags)) && (config.metacondition === undefined || config.metacondition.matchesProperties({..._tags, ..._metatags}))}
{#if config.IsKnown(_tags)} {#if config.IsKnown(_tags)}

View file

@ -1,43 +0,0 @@
import BaseUIElement from "../BaseUIElement"
export default class ShareButton extends BaseUIElement {
private _embedded: BaseUIElement
private _shareData: () => { text: string; title: string; url: string }
constructor(
embedded: BaseUIElement,
generateShareData: () => {
text: string
title: string
url: string
}
) {
super()
this._embedded = embedded
this._shareData = generateShareData
this.SetClass("share-button")
}
protected InnerConstructElement(): HTMLElement {
const e = document.createElement("button")
e.type = "button"
e.appendChild(this._embedded.ConstructElement())
e.addEventListener("click", () => {
if (navigator.share) {
navigator
.share(this._shareData())
.then(() => {
console.log("Thanks for sharing!")
})
.catch((err) => {
console.log(`Couldn't share because of`, err.message)
})
} else {
console.log("web share not supported")
}
})
return e
}
}

View file

@ -1,20 +1,20 @@
import { Store, UIEventSource } from "../../Logic/UIEventSource" import {Store, UIEventSource} from "../../Logic/UIEventSource"
import Combine from "../Base/Combine" import Combine from "../Base/Combine"
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
import Svg from "../../Svg" import Svg from "../../Svg"
import { Tag } from "../../Logic/Tags/Tag" import {Tag} from "../../Logic/Tags/Tag"
import BaseUIElement from "../BaseUIElement" import BaseUIElement from "../BaseUIElement"
import Toggle from "../Input/Toggle" import Toggle from "../Input/Toggle"
import FileSelectorButton from "../Input/FileSelectorButton" import FileSelectorButton from "../Input/FileSelectorButton"
import ImgurUploader from "../../Logic/ImageProviders/ImgurUploader" import ImgurUploader from "../../Logic/ImageProviders/ImgurUploader"
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction" import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import { FixedUiElement } from "../Base/FixedUiElement" import {FixedUiElement} from "../Base/FixedUiElement"
import { VariableUiElement } from "../Base/VariableUIElement" import {VariableUiElement} from "../Base/VariableUIElement"
import Loading from "../Base/Loading" import Loading from "../Base/Loading"
import { LoginToggle } from "../Popup/LoginButton" import {LoginToggle} from "../Popup/LoginButton"
import Constants from "../../Models/Constants" import Constants from "../../Models/Constants"
import { SpecialVisualizationState } from "../SpecialVisualization" import {SpecialVisualizationState} from "../SpecialVisualization"
export class ImageUploadFlow extends Toggle { export class ImageUploadFlow extends Toggle {
private static readonly uploadCountsPerId = new Map<string, UIEventSource<number>>() private static readonly uploadCountsPerId = new Map<string, UIEventSource<number>>()
@ -70,17 +70,22 @@ export class ImageUploadFlow extends Toggle {
const label = new Combine([ const label = new Combine([
Svg.camera_plus_svg().SetClass("block w-12 h-12 p-1 text-4xl "), Svg.camera_plus_svg().SetClass("block w-12 h-12 p-1 text-4xl "),
labelContent, labelContent,
]) ]).SetClass("w-full flex justify-center items-center")
.SetClass(
"p-2 border-4 border-detail rounded-full font-bold h-full align-middle w-full flex justify-center"
)
.SetStyle(" border-color: var(--foreground-color);")
const licenseStore = state?.osmConnection?.GetPreference( const licenseStore = state?.osmConnection?.GetPreference(
Constants.OsmPreferenceKeyPicturesLicense, Constants.OsmPreferenceKeyPicturesLicense,
"CC0" "CC0"
) )
const fileSelector = new FileSelectorButton(label) const fileSelector = new FileSelectorButton(label, {
acceptType: "image/*",
allowMultiple: true,
labelClasses: "rounded-full border-2 border-black font-bold"
})
/* fileSelector.SetClass(
"p-2 border-4 border-detail rounded-full font-bold h-full align-middle w-full flex justify-center"
)
.SetStyle(" border-color: var(--foreground-color);")*/
fileSelector.GetValue().addCallback((filelist) => { fileSelector.GetValue().addCallback((filelist) => {
if (filelist === undefined || filelist.length === 0) { if (filelist === undefined || filelist.length === 0) {
return return
@ -139,7 +144,7 @@ export class ImageUploadFlow extends Toggle {
return new Loading(t.uploadingPicture).SetClass("alert") return new Loading(t.uploadingPicture).SetClass("alert")
} else { } else {
return new Loading( return new Loading(
t.uploadingMultiple.Subs({ count: "" + l }) t.uploadingMultiple.Subs({count: "" + l})
).SetClass("alert") ).SetClass("alert")
} }
}) })
@ -163,7 +168,7 @@ export class ImageUploadFlow extends Toggle {
if (l == 1) { if (l == 1) {
return t.uploadDone.Clone().SetClass("thanks block") return t.uploadDone.Clone().SetClass("thanks block")
} }
return t.uploadMultipleDone.Subs({ count: l }).SetClass("thanks block") return t.uploadMultipleDone.Subs({count: l}).SetClass("thanks block")
}) })
), ),
@ -172,7 +177,7 @@ export class ImageUploadFlow extends Toggle {
Translations.t.image.respectPrivacy, Translations.t.image.respectPrivacy,
new VariableUiElement( new VariableUiElement(
licenseStore.map((license) => licenseStore.map((license) =>
Translations.t.image.currentLicense.Subs({ license }) Translations.t.image.currentLicense.Subs({license})
) )
) )
.onClick(() => { .onClick(() => {

View file

@ -11,17 +11,20 @@ export default class FileSelectorButton extends InputElement<FileList> {
private readonly _label: BaseUIElement private readonly _label: BaseUIElement
private readonly _acceptType: string private readonly _acceptType: string
private readonly allowMultiple: boolean private readonly allowMultiple: boolean
private readonly _labelClasses: string;
constructor( constructor(
label: BaseUIElement, label: BaseUIElement,
options?: { options?: {
acceptType: "image/*" | string acceptType: "image/*" | string
allowMultiple: true | boolean allowMultiple: true | boolean,
labelClasses?: string
} }
) { ) {
super() super()
this._label = label this._label = label
this._acceptType = options?.acceptType ?? "image/*" this._acceptType = options?.acceptType ?? "image/*"
this._labelClasses= options?.labelClasses ?? ""
this.SetClass("block cursor-pointer") this.SetClass("block cursor-pointer")
label.SetClass("cursor-pointer") label.SetClass("cursor-pointer")
this.allowMultiple = options?.allowMultiple ?? true this.allowMultiple = options?.allowMultiple ?? true
@ -40,6 +43,7 @@ export default class FileSelectorButton extends InputElement<FileList> {
const el = document.createElement("form") const el = document.createElement("form")
const label = document.createElement("label") const label = document.createElement("label")
label.appendChild(this._label.ConstructElement()) label.appendChild(this._label.ConstructElement())
label.classList.add(...this._labelClasses.split(" ").filter(t => t !== ""))
el.appendChild(label) el.appendChild(label)
const actualInputElement = document.createElement("input") const actualInputElement = document.createElement("input")

View file

@ -22,7 +22,6 @@
// The type changed -> reset some values // The type changed -> reset some values
validator = Validators.get(type) validator = Validators.get(type)
_value.setData(value.data ?? "") _value.setData(value.data ?? "")
console.log("REseting validated input, _value is ", _value.data, validator?.getFeedback(_value.data, getCountry))
feedback = feedback?.setData(validator?.getFeedback(_value.data, getCountry)); feedback = feedback?.setData(validator?.getFeedback(_value.data, getCountry));
_placeholder = placeholder ?? validator?.getPlaceholder() ?? type _placeholder = placeholder ?? validator?.getPlaceholder() ?? type
} }

View file

@ -1,9 +1,10 @@
import { UIEventSource } from "../../Logic/UIEventSource" import { UIEventSource } from "../../Logic/UIEventSource"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import ShareButton from "../BigComponents/ShareButton"
import Svg from "../../Svg" import Svg from "../../Svg"
import { FixedUiElement } from "../Base/FixedUiElement" import { FixedUiElement } from "../Base/FixedUiElement"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"; import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization";
import SvelteUIElement from "../Base/SvelteUIElement";
import ShareButton from "../Base/ShareButton.svelte";
export class ShareLinkViz implements SpecialVisualization { export class ShareLinkViz implements SpecialVisualization {
funcName = "share_link" funcName = "share_link"
@ -18,7 +19,6 @@ export class ShareLinkViz implements SpecialVisualization {
] ]
public constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, args: string[]) { public constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, args: string[]) {
if (window.navigator.share) {
const generateShareData = () => { const generateShareData = () => {
const title = state?.layout?.title?.txt ?? "MapComplete" const title = state?.layout?.title?.txt ?? "MapComplete"
@ -45,9 +45,7 @@ export class ShareLinkViz implements SpecialVisualization {
} }
} }
return new ShareButton(Svg.share_svg().SetClass("w-8 h-8"), generateShareData) return new SvelteUIElement(ShareButton, {generateShareData})
} else { //return new ShareButton(Svg.share_svg().SetClass("w-8 h-8"), generateShareData)
return new FixedUiElement("")
}
} }
} }

View file

@ -25,12 +25,13 @@
$:{ $:{
trs = Utils.NoNull(config?.GetRenderValues(_tags)); trs = Utils.NoNull(config?.GetRenderValues(_tags));
} }
export let extraClasses: string= ""
let classes = "" let classes = ""
$:classes = config?.classes?.join(" ") ?? ""; $:classes = config?.classes?.join(" ") ?? "";
</script> </script>
{#if config !== undefined && (config?.condition === undefined || config.condition.matchesProperties(_tags))} {#if config !== undefined && (config?.condition === undefined || config.condition.matchesProperties(_tags))}
<div class={"flex flex-col w-full "+classes}> <div class={"flex flex-col w-full "+classes+" "+extraClasses}>
{#if trs.length === 1} {#if trs.length === 1}
<TagRenderingMapping mapping={trs[0]} {tags} {state} {selectedElement} {layer}></TagRenderingMapping> <TagRenderingMapping mapping={trs[0]} {tags} {state} {selectedElement} {layer}></TagRenderingMapping>
{/if} {/if}

View file

@ -71,25 +71,22 @@
<div bind:this={htmlElem} class=""> <div bind:this={htmlElem} class="">
{#if config.question && $editingEnabled} {#if config.question && $editingEnabled}
{#if editMode} {#if editMode}
<div class="m-1 mx-2"> <TagRenderingQuestion {config} {tags} {selectedElement} {state} {layer}>
<TagRenderingQuestion {config} {tags} {selectedElement} {state} {layer}> <button slot="cancel" class="secondary" on:click={() => {editMode = false}}>
<button slot="cancel" class="secondary" on:click={() => {editMode = false}}> <Tr t={Translations.t.general.cancel}/>
<Tr t={Translations.t.general.cancel}/> </button>
</button> <XCircleIcon slot="upper-right" class="w-8 h-8" on:click={() => {editMode = false}}/>
<XCircleIcon slot="upper-right" class="w-8 h-8" on:click={() => {editMode = false}}/> </TagRenderingQuestion>
</TagRenderingQuestion>
</div>
{:else} {:else}
<div class="flex justify-between low-interaction items-center m-1 mx-2 p-1 px-2 rounded"> <div class="flex justify-between low-interaction items-center rounded px-2">
<TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer}/> <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer}/>
<button on:click={() => {editMode = true}} class="shrink-0 w-8 h-8 rounded-full p-1 secondary self-start"> <button on:click={() => {editMode = true}}
class="shrink-0 w-8 h-8 rounded-full p-1 secondary self-start">
<PencilAltIcon/> <PencilAltIcon/>
</button> </button>
</div> </div>
{/if} {/if}
{:else } {:else }
<div class="m-1 p-1 px-2 mx-2"> <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer}/>
<TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer}/>
</div>
{/if} {/if}
</div> </div>

View file

@ -26,7 +26,6 @@
export let layer: LayerConfig; export let layer: LayerConfig;
let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined); let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined);
feedback.addCallbackAndRunD(f => console.trace("Feedback is now", f.txt))
// Will be bound if a freeform is available // Will be bound if a freeform is available
let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key]); let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key]);

View file

@ -1,9 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="374px" height="365px" viewBox="0 0 374 365" version="1.1"> <svg
<g id="surface1"> width="374px"
<path style="fill:none;stroke-width:2.438635;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 19.212394 278.175203 L 7.243369 283.695664 L 19.18221 289.202531 " transform="matrix(18.635995,0,0,18.679091,-62.085051,-5111.07542)"/> height="365px"
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 145.808594 188.101562 C 145.808594 228.460938 113.167969 261.175781 72.902344 261.175781 C 32.640625 261.175781 0 228.460938 0 188.101562 C 0 147.746094 32.640625 115.03125 72.902344 115.03125 C 113.167969 115.03125 145.808594 147.746094 145.808594 188.101562 Z M 145.808594 188.101562 "/> viewBox="0 0 374 365"
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 374 291.453125 C 374 331.8125 341.359375 364.527344 301.097656 364.527344 C 260.832031 364.527344 228.191406 331.8125 228.191406 291.453125 C 228.191406 251.097656 260.832031 218.382812 301.097656 218.382812 C 341.359375 218.382812 374 251.097656 374 291.453125 Z M 374 291.453125 "/> version="1.1"
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 374 73.546875 C 374 113.902344 341.359375 146.617188 301.097656 146.617188 C 260.832031 146.617188 228.191406 113.902344 228.191406 73.546875 C 228.191406 33.1875 260.832031 0.472656 301.097656 0.472656 C 341.359375 0.472656 374 33.1875 374 73.546875 Z M 374 73.546875 "/> id="svg11"
</g> sodipodi:docname="share.svg"
</svg> inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs15" />
<sodipodi:namedview
id="namedview13"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="2.0958904"
inkscape:cx="187.03268"
inkscape:cy="182.5"
inkscape:window-width="1920"
inkscape:window-height="995"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg11" />
<path
id="path2"
style="color:#000000;fill:#000000;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="M 301.09766 0.47265625 C 260.83203 0.47265625 228.19141 33.1875 228.19141 73.546875 C 228.19141 79.329826 228.8815 84.948384 230.14844 90.34375 L 126.28711 138.35938 C 112.97544 124.01509 93.990177 115.03125 72.902344 115.03125 C 32.640625 115.03125 0 147.74609 0 188.10156 C -9.4739031e-15 228.46094 32.640625 261.17578 72.902344 261.17578 C 93.990661 261.17578 112.97735 252.19308 126.28906 237.84766 L 228.48438 285.09375 C 228.30377 287.19139 228.19141 289.30857 228.19141 291.45312 C 228.19141 331.8125 260.83203 364.52734 301.09766 364.52734 C 341.35937 364.52734 374 331.8125 374 291.45312 C 374 251.09766 341.35937 218.38281 301.09766 218.38281 C 279.27697 218.38281 259.71055 228.00393 246.34961 243.22852 L 145.3125 196.51562 C 145.62871 193.75286 145.80859 190.94925 145.80859 188.10156 C 145.80859 185.2553 145.62846 182.45285 145.3125 179.69141 L 254.10547 129.39648 C 266.79684 140.13431 283.18811 146.61719 301.09766 146.61719 C 341.35937 146.61719 374 113.90234 374 73.546875 C 374 33.1875 341.35937 0.47265625 301.09766 0.47265625 z " />
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

@ -34,7 +34,7 @@
--interactive-foreground: black; --interactive-foreground: black;
--interactive-contrast: #ff00ff; --interactive-contrast: #ff00ff;
--button-background: #737373; --button-background: black;
--button-foreground: white; --button-foreground: white;
/** /**
@ -172,7 +172,6 @@ button.disabled:hover {
color: unset; color: unset;
} }
button:hover { button:hover {
border: 2px solid var(--catch-detail-color-contrast); border: 2px solid var(--catch-detail-color-contrast);
background-color: var(--catch-detail-color); background-color: var(--catch-detail-color);
@ -184,7 +183,6 @@ button:hover img {
border-radius: 100rem; border-radius: 100rem;
} }
button { button {
display: inline-flex; display: inline-flex;
line-height: 1.25rem; line-height: 1.25rem;
@ -319,6 +317,34 @@ label.checked {
border: 2px solid var(--foreground-color); border: 2px solid var(--foreground-color);
} }
.active-links a{
/*
* Let a 'link' mimick a secondary button, but not entirely
*/
display: block;
width: 100%;
height: 100%;
padding: 3px;
margin: 0;
background: var(--low-interaction-background);
color: var(--low-interaction-foreground);
border: 2px solid var(--interactive-background);
border-radius: 0.5rem;
}
.active-links a:hover {
background-color: var(--interactive-background);
color: var(--catch-detail-foregroundcolor);
border-color: var(--catch-detail-color-contrast);
}
.active-links a:hover svg path {
fill: var(--catch-detail-foregroundcolor) !important;
}
/************************* OTHER CATEGORIES ********************************/ /************************* OTHER CATEGORIES ********************************/
/** /**

20
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "mapcomplete", "name": "mapcomplete",
"version": "0.25.1", "version": "0.30.5",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mapcomplete", "name": "mapcomplete",
"version": "0.25.1", "version": "0.30.5",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"dependencies": { "dependencies": {
"@onsvisual/svelte-maps": "^1.1.6", "@onsvisual/svelte-maps": "^1.1.6",
@ -4465,9 +4465,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001445", "version": "1.0.30001486",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001486.tgz",
"integrity": "sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg==", "integrity": "sha512-uv7/gXuHi10Whlj0pp5q/tsK/32J2QSqVRKQhs2j8VsDCjgyruAh/eEXHF822VqO9yT6iZKw3nRwZRSPBE9OQg==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -4477,6 +4477,10 @@
{ {
"type": "tidelift", "type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite" "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
} }
] ]
}, },
@ -15387,9 +15391,9 @@
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001445", "version": "1.0.30001486",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001445.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001486.tgz",
"integrity": "sha512-8sdQIdMztYmzfTMO6KfLny878Ln9c2M0fc7EH60IjlP4Dc4PiCy7K2Vl3ITmWgOyPgVQKa5x+UP/KqFsxj4mBg==", "integrity": "sha512-uv7/gXuHi10Whlj0pp5q/tsK/32J2QSqVRKQhs2j8VsDCjgyruAh/eEXHF822VqO9yT6iZKw3nRwZRSPBE9OQg==",
"dev": true "dev": true
}, },
"canvg": { "canvg": {

View file

@ -40,7 +40,7 @@
"prepare-deploy": "npm run generate:service-worker && ./scripts/build.sh", "prepare-deploy": "npm run generate:service-worker && ./scripts/build.sh",
"format": "npx prettier --write --svelte-bracket-new-line=false --html-whitespace-sensitivity=ignore '**/*.ts' '**/*.svelte'", "format": "npx prettier --write --svelte-bracket-new-line=false --html-whitespace-sensitivity=ignore '**/*.ts' '**/*.svelte'",
"clean:tests": "(find . -type f -name \"*.doctest.ts\" | xargs -r rm)", "clean:tests": "(find . -type f -name \"*.doctest.ts\" | xargs -r rm)",
"clean": "rm -rf .cache/ && (find *.html | grep -v \"^\\(404\\|index\\|land\\|test\\|preferences\\|customGenerator\\|professional\\|automaton\\|import_helper\\|import_viewer\\|theme\\).html\" | xargs -r rm) && (ls | grep \"^index_[a-zA-Z_-]\\+\\.ts$\" | xargs -r rm) && (ls | grep \".*.webmanifest$\" | grep -v \"manifest.webmanifest\" | xargs -r rm)", "clean": "rm -rf .cache/ && (find *.html | grep -v \"^\\(404\\|index\\|land\\|test\\|preferences\\|customGenerator\\|professional\\|automaton\\|import_helper\\|import_viewer\\|theme\\|style_test\\).html\" | xargs -r rm) && (ls | grep \"^index_[a-zA-Z_-]\\+\\.ts$\" | xargs -r rm) && (ls | grep \".*.webmanifest$\" | grep -v \"manifest.webmanifest\" | xargs -r rm)",
"generate:dependency-graph": "node_modules/.bin/depcruise --exclude \"^node_modules\" --output-type dot Logic/State/MapState.ts > dependencies.dot && dot dependencies.dot -T svg -o dependencies.svg && rm dependencies.dot", "generate:dependency-graph": "node_modules/.bin/depcruise --exclude \"^node_modules\" --output-type dot Logic/State/MapState.ts > dependencies.dot && dot dependencies.dot -T svg -o dependencies.svg && rm dependencies.dot",
"weblate-add-upstream": "git remote add weblate-github git@github.com:weblate/MapComplete.git && git remote add weblate-hosted-core https://hosted.weblate.org/git/mapcomplete/core/ && git remote add weblate-hosted-layers https://hosted.weblate.org/git/mapcomplete/layers/", "weblate-add-upstream": "git remote add weblate-github git@github.com:weblate/MapComplete.git && git remote add weblate-hosted-core https://hosted.weblate.org/git/mapcomplete/core/ && git remote add weblate-hosted-layers https://hosted.weblate.org/git/mapcomplete/layers/",
"weblate-merge": "git remote update weblate-github; git merge weblate-github/weblate-mapcomplete-core weblate-github/weblate-mapcomplete-layers weblate-github/weblate-mapcomplete-layer-translations", "weblate-merge": "git remote update weblate-github; git merge weblate-github/weblate-mapcomplete-core weblate-github/weblate-mapcomplete-layers weblate-github/weblate-mapcomplete-layer-translations",

View file

@ -825,11 +825,6 @@ video {
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.mr-2 { .mr-2 {
margin-right: 0.5rem; margin-right: 0.5rem;
} }
@ -1024,6 +1019,10 @@ video {
height: fit-content; height: fit-content;
} }
.h-7 {
height: 1.75rem;
}
.h-11 { .h-11 {
height: 2.75rem; height: 2.75rem;
} }
@ -1048,8 +1047,8 @@ video {
height: 20rem; height: 20rem;
} }
.max-h-32 { .max-h-12 {
max-height: 8rem; max-height: 3rem;
} }
.max-h-7 { .max-h-7 {
@ -1060,14 +1059,6 @@ video {
max-height: 6rem; max-height: 6rem;
} }
.max-h-12 {
max-height: 3rem;
}
.min-h-\[8rem\] {
min-height: 8rem;
}
.w-full { .w-full {
width: 100%; width: 100%;
} }
@ -1114,6 +1105,10 @@ video {
width: fit-content; width: fit-content;
} }
.w-7 {
width: 1.75rem;
}
.w-11 { .w-11 {
width: 2.75rem; width: 2.75rem;
} }
@ -1290,6 +1285,20 @@ video {
row-gap: 0.25rem; row-gap: 0.25rem;
} }
.gap-x-0\.5 {
-webkit-column-gap: 0.125rem;
column-gap: 0.125rem;
}
.gap-x-0 {
-webkit-column-gap: 0px;
column-gap: 0px;
}
.gap-y-2 {
row-gap: 0.5rem;
}
.self-start { .self-start {
align-self: flex-start; align-self: flex-start;
} }
@ -1533,6 +1542,11 @@ video {
padding-right: 0.5rem; padding-right: 0.5rem;
} }
.px-3 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
.px-4 { .px-4 {
padding-left: 1rem; padding-left: 1rem;
padding-right: 1rem; padding-right: 1rem;
@ -1702,11 +1716,6 @@ video {
color: rgb(22 163 74 / var(--tw-text-opacity)); color: rgb(22 163 74 / var(--tw-text-opacity));
} }
.text-\[\#999\] {
--tw-text-opacity: 1;
color: rgb(153 153 153 / var(--tw-text-opacity));
}
.text-white { .text-white {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));
@ -1761,6 +1770,12 @@ video {
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
} }
.drop-shadow-md {
--tw-drop-shadow: drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06));
-webkit-filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
}
.grayscale { .grayscale {
--tw-grayscale: grayscale(100%); --tw-grayscale: grayscale(100%);
-webkit-filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); -webkit-filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
@ -1845,7 +1860,7 @@ video {
--interactive-background: #dddddd; --interactive-background: #dddddd;
--interactive-foreground: black; --interactive-foreground: black;
--interactive-contrast: #ff00ff; --interactive-contrast: #ff00ff;
--button-background: #737373; --button-background: black;
--button-foreground: white; --button-foreground: white;
/** /**
* Base colour of interactive elements, mainly the 'subtle button' * Base colour of interactive elements, mainly the 'subtle button'
@ -2120,6 +2135,31 @@ label.checked {
border: 2px solid var(--foreground-color); border: 2px solid var(--foreground-color);
} }
.active-links a{
/*
* Let a 'link' mimick a secondary button, but not entirely
*/
display: block;
width: 100%;
height: 100%;
padding: 3px;
margin: 0;
background: var(--low-interaction-background);
color: var(--low-interaction-foreground);
border: 2px solid var(--interactive-background);
border-radius: 0.5rem;
}
.active-links a:hover {
background-color: var(--interactive-background);
color: var(--catch-detail-foregroundcolor);
border-color: var(--catch-detail-color-contrast);
}
.active-links a:hover svg path {
fill: var(--catch-detail-foregroundcolor) !important;
}
/************************* OTHER CATEGORIES ********************************/ /************************* OTHER CATEGORIES ********************************/
/** /**