From 3468837e0bddfc87432399c117401924e03c8dd0 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@posteo.net> Date: Tue, 4 Mar 2025 22:21:24 +0100 Subject: [PATCH] Refactoring: fix statistics in onwheels theme --- assets/themes/onwheels/onwheels.json | 69 ------------ public/css/index-tailwind-output.css | 100 +----------------- src/Models/ThemeViewState/WithGuiState.ts | 1 - src/UI/Base/ChartJs.ts | 56 +--------- src/UI/BigComponents/StatisticsPanel.ts | 10 +- src/UI/BigComponents/TagRenderingChart.ts | 10 +- src/UI/SpecialVisualizations.ts | 26 +---- .../Statistics/AllFeaturesStatistics.svelte | 19 ++++ src/UI/Statistics/LayerStatistics.svelte | 47 ++++++++ tailwind.config.cjs | 2 +- 10 files changed, 84 insertions(+), 256 deletions(-) create mode 100644 src/UI/Statistics/AllFeaturesStatistics.svelte create mode 100644 src/UI/Statistics/LayerStatistics.svelte diff --git a/assets/themes/onwheels/onwheels.json b/assets/themes/onwheels/onwheels.json index 3e601ffae..7ac1def38 100644 --- a/assets/themes/onwheels/onwheels.json +++ b/assets/themes/onwheels/onwheels.json @@ -511,75 +511,6 @@ } ] } - }, - { - "builtin": "maproulette_challenge", - "override": { - "source": { - "geoJson": "https://maproulette.org/api/v2/challenge/view/28012" - }, - "calculatedTags": [ - "_closest_osm_hotel=closest(feat)('tourism_accomodation')?.properties?.id", - "_closest_osm_hotel_distance=distanceTo(feat)(feat.properties._closest_osm_hotel)", - "_has_closeby_feature=Number(feat.properties._closest_osm_hotel_distance) < 50 ? 'yes' : 'no'" - ], - "+tagRenderings": [ - { - "id": "import-button", - "condition": "_has_closeby_feature=no", - "render": { - "special": { - "type": "import_button", - "targetLayer": "tourism_accomodation", - "tags": "tags", - "text": { - "en": "Import", - "de": "Import", - "fr": "Importation", - "da": "Importere", - "nb_NO": "Importer", - "ca": "Importar", - "pa_PK": "ایمپورٹ کرو", - "nl": "Importeren", - "cs": "Dovoz", - "es": "Importar", - "eu": "Inportatu", - "pl": "Import", - "zh_Hant": "匯入", - "ko": "불러오기" - }, - "icon": "./assets/svg/addSmall.svg", - "maproulette_id": "mr_taskId" - } - } - }, - { - "id": "tag-apply-button", - "condition": "_has_closeby_feature=yes", - "render": { - "special": { - "type": "tag_apply", - "tags_to_apply": "$tags", - "message": { - "en": "Add all the suggested tags", - "de": "Alle vorgeschlagenen Tags hinzufügen", - "fr": "Ajouter tous les attributs suggérés", - "da": "Tilføj alle de foreslåede tags", - "nb_NO": "Legg til alle foreslåtte", - "nl": "Voeg alle gesuggereerde tags toe", - "cs": "Přidat všechny navrhované značky", - "es": "Agregar todas las etiquetas sugeridas", - "ca": "Afegiu totes les etiquetes suggerides", - "pl": "Dodaj wszystkie sugerowane znaczniki", - "ko": "제안된 모든 태그 추가" - }, - "image": "./assets/svg/addSmall.svg", - "id_of_object_to_apply_this_one": "_closest_osm_hotel" - } - } - } - ] - } } ], "overrideAll": { diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index b167e1deb..72cb63208 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -1019,59 +1019,35 @@ input[type="range"].range-lg::-moz-range-thumb { width: 1.5rem; } -.\!container { - width: 100% !important; -} - .container { width: 100%; } @media (min-width: 640px) { - .\!container { - max-width: 640px !important; - } - .container { max-width: 640px; } } @media (min-width: 768px) { - .\!container { - max-width: 768px !important; - } - .container { max-width: 768px; } } @media (min-width: 1024px) { - .\!container { - max-width: 1024px !important; - } - .container { max-width: 1024px; } } @media (min-width: 1280px) { - .\!container { - max-width: 1280px !important; - } - .container { max-width: 1280px; } } @media (min-width: 1536px) { - .\!container { - max-width: 1536px !important; - } - .container { max-width: 1536px; } @@ -1108,10 +1084,6 @@ input[type="range"].range-lg::-moz-range-thumb { pointer-events: auto; } -.\!visible { - visibility: visible !important; -} - .visible { visibility: visible; } @@ -1128,10 +1100,6 @@ input[type="range"].range-lg::-moz-range-thumb { position: static; } -.\!fixed { - position: fixed !important; -} - .fixed { position: fixed; } @@ -1391,74 +1359,22 @@ input[type="range"].range-lg::-moz-range-thumb { margin: 0.25rem; } -.m-11 { - margin: 2.75rem; -} - -.m-14 { - margin: 3.5rem; -} - .m-2 { margin: 0.5rem; } -.m-20 { - margin: 5rem; -} - -.m-28 { - margin: 7rem; -} - .m-3 { margin: 0.75rem; } -.m-32 { - margin: 8rem; -} - -.m-36 { - margin: 9rem; -} - .m-4 { margin: 1rem; } -.m-44 { - margin: 11rem; -} - -.m-5 { - margin: 1.25rem; -} - -.m-52 { - margin: 13rem; -} - -.m-6 { - margin: 1.5rem; -} - -.m-7 { - margin: 1.75rem; -} - -.m-72 { - margin: 18rem; -} - .m-8 { margin: 2rem; } -.m-9 { - margin: 2.25rem; -} - .-mx-1\.5 { margin-left: -0.375rem; margin-right: -0.375rem; @@ -2202,6 +2118,10 @@ input[type="range"].range-lg::-moz-range-thumb { width: 75%; } +.w-96 { + width: 24rem; +} + .w-\[10px\] { width: 10px; } @@ -2463,10 +2383,6 @@ input[type="range"].range-lg::-moz-range-thumb { 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 { - 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 { 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)); } @@ -5204,18 +5120,10 @@ input[type="range"].range-lg::-moz-range-thumb { transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } -.\[_\:string\] { - _: string; -} - .\[a-zA-Z0-9\:_\] { a-z-a--z0-9: ; } -.\[key\:string\] { - key: string; -} - .\[out\:json\] { out: json; } diff --git a/src/Models/ThemeViewState/WithGuiState.ts b/src/Models/ThemeViewState/WithGuiState.ts index 2fd242315..f43ffcdff 100644 --- a/src/Models/ThemeViewState/WithGuiState.ts +++ b/src/Models/ThemeViewState/WithGuiState.ts @@ -66,7 +66,6 @@ export class WithGuiState extends WithSpecialLayers { } public selectCurrentView() { - this.guistate.closeAll() this.selectedElement.setData(this.currentView.features?.data?.[0]) } } diff --git a/src/UI/Base/ChartJs.ts b/src/UI/Base/ChartJs.ts index bb48910bf..138eca609 100644 --- a/src/UI/Base/ChartJs.ts +++ b/src/UI/Base/ChartJs.ts @@ -9,8 +9,8 @@ export class ChartJsColours { public static readonly otherColor = "rgba(128, 128, 128, 0.2)" public static readonly otherBorderColor = "rgba(128, 128, 255)" - public static readonly notApplicableColor = "rgba(128, 128, 128, 0.2)" - public static readonly notApplicableBorderColor = "rgba(255, 0, 0)" + public static readonly notApplicableColor = "#fff" // "rgba(128, 128, 128, 0.2)" + public static readonly notApplicableBorderColor = "rgb(241,132,132)" public static readonly backgroundColors = [ "rgba(255, 99, 132, 0.2)", @@ -42,58 +42,6 @@ export default class ChartJs< this._config = config } - public static ConstructDoughnut(data: Record<string, number>) { - const borderColor = [ - // ChartJsColours.unkownBorderColor, - // ChartJsColours.otherBorderColor, - // ChartJsColours.notApplicableBorderColor, - ] - const backgroundColor = [ - // ChartJsColours.unkownColor, - // ChartJsColours.otherColor, - // ChartJsColours.notApplicableColor, - ] - - let i = 0 - const borders = ChartJsColours.borderColors - const bg = ChartJsColours.backgroundColors - - for (const key in data) { - if (key === "") { - borderColor.push(ChartJsColours.unknownBorderColor) - backgroundColor.push(ChartJsColours.unknownColor) - } else { - borderColor.push(borders[i % borders.length]) - backgroundColor.push(bg[i % bg.length]) - i++ - } - } - - const config = <ChartConfiguration>{ - type: "doughnut", - data: { - labels: Object.keys(data), - datasets: [ - { - data: Object.values(data), - backgroundColor, - borderColor, - borderWidth: 1, - label: undefined, - }, - ], - }, - options: { - plugins: { - legend: { - display: false, - }, - }, - }, - } - return new ChartJs(config) - } - protected InnerConstructElement(): HTMLElement { const canvas = document.createElement("canvas") // A bit exceptional: we apply the styles before giving them to 'chartJS' diff --git a/src/UI/BigComponents/StatisticsPanel.ts b/src/UI/BigComponents/StatisticsPanel.ts index f8eb293aa..906c5c1b3 100644 --- a/src/UI/BigComponents/StatisticsPanel.ts +++ b/src/UI/BigComponents/StatisticsPanel.ts @@ -1,5 +1,4 @@ import { VariableUiElement } from "../Base/VariableUIElement" -import Loading from "../Base/Loading" import Title from "../Base/Title" import TagRenderingChart from "./TagRenderingChart" import Combine from "../Base/Combine" @@ -13,14 +12,7 @@ export default class StatisticsForLayerPanel extends VariableUiElement { super( elementsInview.features.stabilized(1000).map( (features) => { - if (features === undefined) { - return new Loading("Loading data") - } - if (features.length === 0) { - return new Combine(["No elements in view for layer ", layer.id]).SetClass( - "block" - ) - } + const els: BaseUIElement[] = [] const featuresForLayer = features if (featuresForLayer.length === 0) { diff --git a/src/UI/BigComponents/TagRenderingChart.ts b/src/UI/BigComponents/TagRenderingChart.ts index c3994596a..3a9b9447c 100644 --- a/src/UI/BigComponents/TagRenderingChart.ts +++ b/src/UI/BigComponents/TagRenderingChart.ts @@ -5,6 +5,7 @@ import Combine from "../Base/Combine" import { TagUtils } from "../../Logic/Tags/TagUtils" import { Utils } from "../../Utils" import { OsmFeature } from "../../Models/OsmFeature" +import Title from "../Base/Title" export interface TagRenderingChartOptions { groupToOtherCutoff?: 3 | number @@ -275,8 +276,13 @@ export default class TagRenderingChart extends Combine { } super([ - options?.includeTitle ? tagRendering.question.Clone() ?? tagRendering.id : undefined, - chart, + new Title( + options?.includeTitle ? tagRendering.question ?? tagRendering.id : undefined + ), + new Combine([ + + chart + ]).SetClass("flex flex-col justify-center h-full") ]) this.SetClass("block") diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index 68996e52c..30cb9fa7d 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -1,4 +1,3 @@ -import Combine from "./Base/Combine" import { FixedUiElement } from "./Base/FixedUiElement" import BaseUIElement from "./BaseUIElement" import { default as FeatureTitle } from "./Popup/Title.svelte" @@ -12,11 +11,9 @@ import { VariableUiElement } from "./Base/VariableUIElement" import { Translation } from "./i18n/Translation" import Translations from "./i18n/Translations" import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization" -import StatisticsPanel from "./BigComponents/StatisticsPanel" import AutoApplyButton from "./Popup/AutoApplyButton" import { LanguageElement } from "./Popup/LanguageElement/LanguageElement" import SvelteUIElement from "./Base/SvelteUIElement" -import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" import { Feature, LineString } from "geojson" import { GeoOperations } from "../Logic/GeoOperations" import LayerConfig from "../Models/ThemeConfig/LayerConfig" @@ -45,6 +42,7 @@ import { WebAndCommunicationSpecialVisualisations } from "./SpecialVisualisations/WebAndCommunicationSpecialVisualisations" import ClearGPSHistory from "./BigComponents/ClearGPSHistory.svelte" +import AllFeaturesStatistics from "./Statistics/AllFeaturesStatistics.svelte" export default class SpecialVisualizations { public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.initList() @@ -407,27 +405,7 @@ export default class SpecialVisualizations { docs: "Show general statistics about the elements currently in view. Intended to use on the `current_view`-layer", args: [], - constr: (state) => { - return new Combine( - state.theme.layers - .filter( - (l) => - l.name !== null && - l.title && - state.perLayer.get(l.id) !== undefined - ) - .map( - (l) => { - const fs = state.perLayer.get(l.id) - console.log(">>>", l.id, fs) - const bbox = state.mapProperties.bounds - const fsBboxed = new BBoxFeatureSourceForLayer(fs, bbox) - return new StatisticsPanel(fsBboxed) - }, - [state.mapProperties.bounds] - ) - ) - }, + constr: (state) => new SvelteUIElement(AllFeaturesStatistics, { state }) }, { diff --git a/src/UI/Statistics/AllFeaturesStatistics.svelte b/src/UI/Statistics/AllFeaturesStatistics.svelte new file mode 100644 index 000000000..1357349e2 --- /dev/null +++ b/src/UI/Statistics/AllFeaturesStatistics.svelte @@ -0,0 +1,19 @@ +<script lang="ts"> + import ThemeViewState from "../../Models/ThemeViewState" + import { Accordion } from "flowbite-svelte" + import LayerStatistics from "./LayerStatistics.svelte" + + /** + * An element showing s + */ + export let state: ThemeViewState + let layers = state.theme.layers.filter(l => l.isNormal()) + +</script> + +<Accordion> + + {#each layers as layer (layer.id)} + <LayerStatistics {state} {layer} /> + {/each} +</Accordion> diff --git a/src/UI/Statistics/LayerStatistics.svelte b/src/UI/Statistics/LayerStatistics.svelte new file mode 100644 index 000000000..0f5c29cd5 --- /dev/null +++ b/src/UI/Statistics/LayerStatistics.svelte @@ -0,0 +1,47 @@ +<script lang="ts"> + /** + * Statistics, based on the tagRendering, for a single layer + */ + import LayerConfig from "../../Models/ThemeConfig/LayerConfig" + import Tr from "../Base/Tr.svelte" + import Loading from "../Base/Loading.svelte" + import ToSvelte from "../Base/ToSvelte.svelte" + import TagRenderingChart from "../BigComponents/TagRenderingChart" + import type { Feature } from "geojson" + import { AccordionItem } from "flowbite-svelte" + import ThemeViewState from "../../Models/ThemeViewState" + import DefaultIcon from "../Map/DefaultIcon.svelte" + + export let layer: LayerConfig + export let state: ThemeViewState + let bbox = state.mapProperties.bounds + let elements: Feature[] = state.perLayer.get(layer.id).GetFeaturesWithin($bbox) + $: elements = state.perLayer.get(layer.id).GetFeaturesWithin($bbox) + + let trs = layer.tagRenderings.filter(tr => tr.question) +</script> + +<AccordionItem paddingDefault="p-2" inactiveClass="text-black" defaultClass="w-full flex-grow justify-start"> + <div slot="header" class="flex items-center gap-x-2"> + <div class="w-8 h-8 inline-block"> + <DefaultIcon {layer} /> + </div> + <Tr t={layer.name} /> + ({elements.length} elements in view) + </div> + + {#if elements === undefined} + <Loading /> + {:else if elements.length === 0} + No features in view + {:else} + <div class="flex flex-wrap w-full gap-y-4 gap-x-4"> + + {#each trs as tr} + <ToSvelte construct={() => new TagRenderingChart(elements, tr, { + chartclasses: "w-full self-center",includeTitle: true + }).SetClass(tr.multiAnswer ? "w-128": "w-96") } /> + {/each} + </div> + {/if} +</AccordionItem> diff --git a/tailwind.config.cjs b/tailwind.config.cjs index a2294bd3a..39838e759 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -3,7 +3,7 @@ const plugin = require("tailwindcss/plugin") const flowbitePlugin = require("flowbite/plugin") module.exports = { - content: ["./**/*.{html,ts,svelte}", './node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}'], + content: ["*.html", "src/**/*.{html,ts,svelte}", "app/**/*.{html,ts,svelte}", "./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}"], theme: { extend: {