Experimenting with Svelte: build a wrapper to convert 'old' components into Svelte, add a community index overview

This commit is contained in:
Pieter Vander Vennet 2023-02-02 17:57:07 +01:00
parent dfc7ba2114
commit 02da80c311
11 changed files with 250 additions and 55 deletions

View file

@ -18,6 +18,7 @@ export default class FeatureSwitchState {
public readonly featureSwitchBackgroundSelection: UIEventSource<boolean>
public readonly featureSwitchAddNew: UIEventSource<boolean>
public readonly featureSwitchWelcomeMessage: UIEventSource<boolean>
public readonly featureSwitchCommunityIndex: UIEventSource<boolean>
public readonly featureSwitchExtraLinkEnabled: UIEventSource<boolean>
public readonly featureSwitchMoreQuests: UIEventSource<boolean>
public readonly featureSwitchShareScreen: UIEventSource<boolean>
@ -91,6 +92,11 @@ export default class FeatureSwitchState {
() => true,
"Disables/enables the help menu or welcome message"
)
this.featureSwitchCommunityIndex = featSw(
"fs-community-index",
() => true,
"Disables/enables the button to get in touch with the community"
)
this.featureSwitchExtraLinkEnabled = featSw(
"fs-iframe-popout",
(_) => true,

17
UI/Base/ToSvelte.svelte Normal file
View file

@ -0,0 +1,17 @@
<script lang="ts">
import BaseUIElement from "../BaseUIElement.js";
import { onMount } from "svelte";
export let construct: BaseUIElement | (() => BaseUIElement);
let elem: HTMLElement;
onMount(() => {
let html: HTMLElement
if (typeof construct === "function") {
html = construct().ConstructElement();
} else {
html = construct.ConstructElement();
}
elem.appendChild(html)
});
</script>
<span bind:this={elem}></span>

View file

@ -1,14 +1,44 @@
<script lang="ts">
import {BBox} from "../../Logic/BBox";
import {Store} from "../../Logic/UIEventSource";
import Loc from "../../Models/Loc";
import { Store, UIEventSource } from "../../Logic/UIEventSource";
import { Tiles } from "../../Models/TileRange";
import { Utils } from "../../Utils";
import global_community from "../../assets/community_index_global_resources.json";
import ContactLink from "./ContactLink.svelte";
import { GeoOperations } from "../../Logic/GeoOperations";
import Translations from "../i18n/Translations";
import ToSvelte from "../Base/ToSvelte.svelte";
import { ImmutableStore } from "../../Logic/UIEventSource.js";
export let bbox: Store<BBox>
export let currentLocation: Store<Loc>
bbox.mapD(bbox => {
if(currentLocation.data.zoom <= 6){
// only return the global data
export let locationControl: Store<{ lat: number, lon: number }>;
const tileToFetch: Store<string> = locationControl.mapD(l => {
const t = Tiles.embedded_tile(l.lat, l.lon, 6);
return `https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/community_index/tile_${t.z}_${t.x}_${t.y}.geojson`;
});
const t = Translations.t.communityIndex
const resources = new UIEventSource<[]>([]);
tileToFetch.addCallbackAndRun(async url => {
const data = await Utils.downloadJsonCached(url, 24 * 60 * 60);
if (data === undefined) {
return;
}
return bbox.expandToTileBounds(6);
}, [currentLocation])
resources.setData(data.features);
}
);
const filteredResources = resources.map(features => features.filter(f => {
return GeoOperations.inside([locationControl.data.lon, locationControl.data.lat], f)
}),
[locationControl]);
</script>
<div>
<ToSvelte construct={t.intro} />
{#each $filteredResources as feature}
<ContactLink country={feature.properties} />
{/each}
<ContactLink country={{resources:global_community, nameEn: "Global resources"}} />
</div>

View file

@ -1,22 +1,28 @@
<script lang="ts">
import {Store} from "../../Logic/UIEventSource";
// A contact link indicates how a mapper can contact their local community
// The _properties_ of a community feature
export let country: Store<{ resources; nameEn: string }>
let resources : Store<{ id: string, resolved: Record<string, string> }[]> = country.map(country => {
if(country === undefined){
return []
}
return Array.from(Object.values(country?.resources ?? {}))
})
import Locale from "../i18n/Locale.js";
import Translations from "../i18n/Translations";
import ToSvelte from "../Base/ToSvelte.svelte";
import * as native from "../../assets/language_native.json";
import { TypedTranslation } from "../i18n/Translation";
const availableTranslationTyped: TypedTranslation<{ native: string }> = Translations.t.communityIndex.available;
const availableTranslation = availableTranslationTyped.OnEveryLanguage((s, ln) => s.replace("{native}", native[ln] ?? ln));
export let country: { resources; nameEn: string };
let resources: { id: string, resolved: Record<string, string>, languageCodes: string[] }[] = []
$: resources = Array.from(Object.values(country?.resources ?? {}));
const language = Locale.language;
</script>
<div>
{#if $country?.nameEn}
<h3>{$country?.nameEn}</h3>
{#if country?.nameEn}
<h3>{country?.nameEn}</h3>
{/if}
{#each $resources as resource}
<div class="flex link-underline items-center">
{#each resources as resource}
<div class="flex link-underline items-center my-4">
<img
class="w-8 h-8 m-2"
src={"https://raw.githubusercontent.com/osmlab/osm-community-index/main/dist/img/" +
@ -24,10 +30,16 @@
".svg"}
/>
<div class="flex flex-col">
<a href={resource.resolved.url} target="_blank" class="font-bold">
<a href={resource.resolved.url} target="_blank" rel="noreferrer nofollow" class="font-bold">
{resource.resolved.name ?? resource.resolved.url}
</a>
{resource.resolved?.description}
{#if (resource.languageCodes?.indexOf($language) >= 0)}
<span class="border-2 rounded-full border-lime-500 text-sm w-fit px-2">
<ToSvelte construct={() => availableTranslation.Clone()} />
</span>
{/if}
</div>
</div>
{/each}

View file

@ -34,6 +34,8 @@ import { GeoLocationState } from "../Logic/State/GeoLocationState"
import Hotkeys from "./Base/Hotkeys"
import AvailableBaseLayers from "../Logic/Actors/AvailableBaseLayers"
import CopyrightPanel from "./BigComponents/CopyrightPanel"
import SvelteUIElement from "./Base/SvelteUIElement"
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
/**
* The default MapComplete GUI initializer
@ -237,6 +239,20 @@ export default class DefaultGUI {
const welcomeMessageMapControl = Toggle.If(state.featureSwitchWelcomeMessage, () =>
self.InitWelcomeMessage()
)
const communityIndex = Toggle.If(state.featureSwitchCommunityIndex, () => {
const communityIndexControl = new MapControlButton(Svg.community_svg())
const communityIndex = new ScrollableFullScreen(
() => Translations.t.communityIndex.title,
() => new SvelteUIElement(CommunityIndexView, { ...state }),
"community_index"
)
communityIndexControl.onClick(() => {
communityIndex.Activate()
})
return communityIndexControl
})
const testingBadge = Toggle.If(state.featureSwitchIsTesting, () =>
new FixedUiElement("TESTING").SetClass("alert m-2 border-2 border-black")
)
@ -253,6 +269,7 @@ export default class DefaultGUI {
welcomeMessageMapControl,
userInfoMapControl,
copyright,
communityIndex,
extraLink,
testingBadge,
])

View file

@ -2,6 +2,7 @@ import Locale from "./Locale"
import { Utils } from "../../Utils"
import BaseUIElement from "../BaseUIElement"
import LinkToWeblate from "../Base/LinkToWeblate"
import { SvelteComponent } from "svelte"
export class Translation extends BaseUIElement {
public static forcedLanguage = undefined

66
assets/svg/community.svg Normal file
View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1124.975mm"
height="1111.9373mm"
viewBox="0 0 1124.975 1111.9373"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
sodipodi:docname="community.svg"
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">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-bbox="true"
inkscape:object-paths="true"
inkscape:zoom="0.077809868"
inkscape:cx="2448.2756"
inkscape:cy="3180.8305"
inkscape:window-width="1717"
inkscape:window-height="885"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(429.38456,373.85159)">
<path
id="path858"
style="color:#000000;fill:#000000;stroke:none;stroke-width:18.8976;stroke-linejoin:round;stroke-miterlimit:10.6207;stroke-dasharray:none;stroke-opacity:1"
d="m 465.50195,-1412.9824 c -48.13018,0 -95.14167,6.6605 -140.92968,19.3242 -517.78906,36.4545 -1004.59215,263.4542 -1364.78127,639.11132 -125.578,130.97093 -232.0356,276.80492 -318.709,432.875 l -3.0996,0.006 0.012,5.62891 c -169.1451,306.7883664 -260.6208,653.52706 -260.8652,1009.62695 v 0.0645 c 0,555.45592 220.6943,1088.26072 613.4609,1481.02732 378.0703,378.0702 885.96964,596.0875 1418.85161,611.92 18.51351,1.9864 37.20113,3.0136 56.06054,3.0136 17.25834,0 34.36078,-0.9283 51.33008,-2.5937 224.45873,-4.8504 444.64455,-45.7063 652.83597,-119.1836 -60.8675,-43.0776 -118.1478,-91.0122 -171.27933,-143.334 -121.72268,40.4323 -230.24225,90.5328 -428.68164,58.6602 l -3.38086,-1775.19732 233.78516,-0.44532 c 42.42213,-67.09791 90.54812,-130.41487 143.84375,-189.24804 l -377.98828,0.7207 -1.43946,-755.89648 634.26176,-1.38672 c 32.7782,154.928763 56.3858,319.47347 69.9629,489.41797 58.6612,-33.17515 119.6711,-62.01386 182.541,-86.28516 -13.1747,-138.65014 -32.7954,-273.7956044 -58.7852,-403.55664 l 791.918,-1.73242 c 54.2995,111.507047 97.5037,228.170813 129.2012,348.26953 72.3897,18.82425 143.1313,43.49582 211.5273,73.77148 -80.6791,-402.00209 -278.3137,-774.77273 -572.5098,-1068.96875 -363.7812,-363.78173 -847.7515,-579.57553 -1358.52731,-609.49993 -41.8967,-10.5343 -84.78727,-16.1094 -128.61524,-16.1094 z m 96.98438,204.4375 c 159.67283,49.2174 326.54513,218.8615 462.03907,516.93552 49.6393,109.20202 92.1295,232.60044 128.4531,364.44727 l -588.81053,1.28711 z m -188.97461,0.9179 1.67969,882.16411 -597.98243,1.30664 c 36.4911,-132.99204 79.25432,-257.42426 129.269536,-367.45313 133.780665,-294.30513 298.164144,-463.66612 455.992184,-515.27932 3.68603,-0.2115 7.36574,-0.4597 11.04102,-0.7383 z m 591.27148,61.1954 c 320.2851,85.8427 615.6781,254.12924 854.2344,492.68551 99.0716,99.07158 185.6177,208.20254 259.6738,324.5625 l -729.584,1.59375 c -41.2536,-159.09493 -91.7465,-308.46744 -152.5469,-442.22266 -67.1207,-147.65948 -144.9895,-275.0596 -231.7773,-376.6191 z m -1003.50195,5.9433 c -84.79247,100.5848 -161.00089,225.84047 -226.83789,370.6758 -61.29809,134.85015 -112.17438,285.51379 -153.59961,446.08594 l -720.14645,1.57422 c 67.8012,-107.37947 146.33969,-208.61822 235.49997,-301.60743 238.54174,-248.78579 538.10225,-425.81263 865.08398,-516.72853 z m 414.26953,1004.00002 1.43945,755.8418 -727.68554,1.38671 c 4.20418,-264.24481 32.77031,-520.983169 82.2207,-755.82031 z m -837.92773,1.83203 c -47.33699,237.75431 -73.46184,493.4318 -77.33789,755.75781 l -892.42186,1.70117 c 10.0141,-264.11069 74.8439,-521.07351 188.0937,-755.75 z m 839.72656,942.98437 1.3457,707.00198 -646.08984,1.414 c -46.71986,-220.2353 -75.01919,-459.8106 -82.07617,-707.03121 z m -915.85156,1.74415 c 6.57514,245.41033 32.61831,484.16193 77.38867,707.09573 l -784.53902,1.7148 c -105.8005,-220.1735 -168.8074,-460.0874 -184.1446,-707.11327 z m 917.55859,894.23433 1.68359,884.2715 C 217.38802,2544.4462 45.439072,2373.9426 -93.521484,2068.2422 -142.93255,1959.5424 -185.25941,1836.7732 -221.48047,1705.6211 Z m -796.66211,1.7422 c 41.17164,158.3984 91.48351,307.1603 152.04883,440.3985 69.28935,152.4302 150.07483,283.1451 240.259765,386.2558 C -344.1327,2446.5457 -638.16035,2278.6776 -875.7832,2041.0547 -977.36504,1939.4729 -1065.801,1827.3298 -1141.0586,1707.6328 Z"
transform="scale(0.26458333)"
sodipodi:nodetypes="scscccssscsccccccccccccccccccscsccccccscccsccsccsccscccccccccccccccccccccccscccscscc" />
<path
style="fill:#000000;stroke-width:5.60043"
id="path928"
d="" />
<path
style="fill:#000000;stroke-width:5.60043"
id="path890"
d="" />
<path
id="path3123"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2.68756;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 500.42418,99.52416 c -162.9348,1.72512 -108.06353,223.75011 -69.65465,274.06343 l 1.09709,35.29034 c -176.038,13.83721 -191.80907,75.90897 -198.43798,158.82719 -2.02223,25.29521 3.89762,50.2628 9.81667,80.72646 97.56502,-0.71585 177.50757,-0.62612 275.07185,-0.49796 84.27329,-45.47436 147.33813,-122.20106 175.63579,-213.68411 -28.16643,-12.43076 -66.9608,-21.14889 -120.68876,-25.37209 l 1.09762,-35.29036 C 613.0731,322.8775 663.35897,97.79884 500.42418,99.52416 Z"
sodipodi:nodetypes="sccscccccs" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

@ -803,6 +803,11 @@ video {
margin-bottom: 0.5rem;
}
.my-4 {
margin-top: 1rem;
margin-bottom: 1rem;
}
.mx-10 {
margin-left: 2.5rem;
margin-right: 2.5rem;
@ -813,11 +818,6 @@ video {
margin-bottom: 0.75rem;
}
.my-4 {
margin-top: 1rem;
margin-bottom: 1rem;
}
.mb-4 {
margin-bottom: 1rem;
}
@ -1123,6 +1123,12 @@ video {
width: 2.75rem;
}
.w-fit {
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
}
.w-max {
width: -webkit-max-content;
width: max-content;
@ -1187,6 +1193,11 @@ video {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.\!transform {
-webkit-transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important;
}
@-webkit-keyframes spin {
to {
-webkit-transform: rotate(360deg);
@ -1417,6 +1428,11 @@ video {
border-style: dotted;
}
.border-subtle {
--tw-border-opacity: 1;
border-color: rgb(219 234 254 / var(--tw-border-opacity));
}
.border-black {
--tw-border-opacity: 1;
border-color: rgb(0 0 0 / var(--tw-border-opacity));
@ -1437,6 +1453,11 @@ video {
border-color: rgb(252 165 165 / var(--tw-border-opacity));
}
.border-lime-500 {
--tw-border-opacity: 1;
border-color: rgb(132 204 22 / var(--tw-border-opacity));
}
.border-blue-500 {
--tw-border-opacity: 1;
border-color: rgb(59 130 246 / var(--tw-border-opacity));
@ -1461,11 +1482,21 @@ video {
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}
.bg-unsubtle {
--tw-bg-opacity: 1;
background-color: rgb(191 219 254 / var(--tw-bg-opacity));
}
.bg-red-400 {
--tw-bg-opacity: 1;
background-color: rgb(248 113 113 / var(--tw-bg-opacity));
}
.bg-subtle {
--tw-bg-opacity: 1;
background-color: rgb(219 234 254 / var(--tw-bg-opacity));
}
.bg-gray-400 {
--tw-bg-opacity: 1;
background-color: rgb(156 163 175 / var(--tw-bg-opacity));
@ -1530,6 +1561,11 @@ video {
padding: 0.125rem;
}
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.px-0 {
padding-left: 0px;
padding-right: 0px;
@ -1708,6 +1744,11 @@ video {
letter-spacing: -0.025em;
}
.text-black {
--tw-text-opacity: 1;
color: rgb(0 0 0 / var(--tw-text-opacity));
}
.text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
@ -1741,6 +1782,10 @@ video {
text-decoration-line: line-through;
}
.no-underline {
text-decoration-line: none;
}
.opacity-50 {
opacity: 0.5;
}
@ -1815,12 +1860,6 @@ video {
transition-duration: 150ms;
}
.transition-shadow {
transition-property: box-shadow;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.transition-opacity {
transition-property: opacity;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
@ -2591,11 +2630,6 @@ input {
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.hover\:bg-unsubtle:hover {
background-color: var(--unsubtle-detail-color);
color: var(--unsubtle-detail-color-contrast);
}
@media (min-width: 640px) {
.sm\:top-3 {
top: 0.75rem;

View file

@ -5,6 +5,12 @@
"retrying": "Loading data failed. Trying again in {count} seconds…",
"zoomIn": "Zoom in to view or edit the data"
},
"communityIndex": {
"available": "This community speaks {native}",
"intro": "Get in touch with other people to get to know them, learn from them, ...",
"notAvailable": "This community does not speak {native}",
"title": "Community index"
},
"delete": {
"cancel": "Cancel",
"cannotBeDeleted": "This feature can not be deleted",

View file

@ -5,6 +5,12 @@
"retrying": "Data inladen mislukt - wordt opnieuw geprobeerd over {count} seconden",
"zoomIn": "Zoom in om de data te zien en te bewerken"
},
"communityIndex": {
"available": "Op dit communicatiekanaal spreekt men {native}",
"intro": "Contacteer anderen die bezig zijn met OpenStreetMap om kennis te maken, tips uit te wisselen of van elkaar bij te leren.",
"notAvailable": "Op dit communicatiekanaal spreekt men geen {native}",
"title": "Community index"
},
"delete": {
"cancel": "Annuleren",
"cannotBeDeleted": "Dit object kan niet van de kaart verwijderd worden",

View file

@ -49,7 +49,7 @@
"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-fix-heavy": "git remote rm weblate-layers; git remote add weblate-layers https://hosted.weblate.org/git/mapcomplete/layers/; git remote update weblate-layers; git merge weblate-layers/master",
"housekeeping": "npm run generate && npm run generate:docs && npm run generate:contributor-list && vite-node scripts/fetchLanguages.ts && npm run format && git add assets/ langs/ Docs/ **/*.ts Docs/* && git commit -m 'Housekeeping...'",
"parseSchools": "vite-node scripts/schools/amendSchoolData.ts",
"parseSchools": "vite-node scripts/schools/amendSchoolData.ts"
},
"keywords": [
"OpenStreetMap",