forked from MapComplete/MapComplete
chore: automated housekeeping...
This commit is contained in:
parent
8ef7af613f
commit
00151afdea
114 changed files with 2857 additions and 2135 deletions
|
@ -50,7 +50,7 @@
|
|||
}
|
||||
|
||||
:global(.dots-menu > path) {
|
||||
fill: var(--button-background-hover);
|
||||
fill: var(--button-background-hover);
|
||||
transition: fill 350ms linear;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -73,9 +73,9 @@
|
|||
background-color: white;
|
||||
}
|
||||
|
||||
.transition-background {
|
||||
transition: background-color 150ms linear;
|
||||
}
|
||||
.transition-background {
|
||||
transition: background-color 150ms linear;
|
||||
}
|
||||
|
||||
.transition-background.collapsed {
|
||||
background-color: #00000000;
|
||||
|
|
|
@ -65,7 +65,10 @@
|
|||
|
||||
{#if $value.length > 0}
|
||||
<Backspace
|
||||
on:click={(e) =>{ value.set("") ; e.preventDefault()}}
|
||||
on:click={(e) => {
|
||||
value.set("")
|
||||
e.preventDefault()
|
||||
}}
|
||||
color="var(--button-background)"
|
||||
class="mr-3 h-6 w-6 cursor-pointer"
|
||||
/>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
(s) =>
|
||||
(s === "yes" &&
|
||||
state?.userRelatedState?.osmConnection?.userDetails?.data?.csCount >=
|
||||
Constants.userJourney.tagsVisibleAt) ||
|
||||
Constants.userJourney.tagsVisibleAt) ||
|
||||
s === "always" ||
|
||||
s === "full"
|
||||
)
|
||||
|
@ -64,9 +64,9 @@
|
|||
<Tr t={filteredLayer.layerDef.name} />
|
||||
|
||||
{#if $zoomlevel < layer.minzoom}
|
||||
<span class="alert">
|
||||
<Tr t={Translations.t.general.layerSelection.zoomInToSeeThisLayer} />
|
||||
</span>
|
||||
<span class="alert">
|
||||
<Tr t={Translations.t.general.layerSelection.zoomInToSeeThisLayer} />
|
||||
</span>
|
||||
{/if}
|
||||
</Checkbox>
|
||||
{/if}
|
||||
|
|
|
@ -65,22 +65,24 @@
|
|||
export let onlyLink: boolean
|
||||
const t = Translations.t.general.menu
|
||||
let shown = new UIEventSource(state.guistate.pageStates.menu.data || !onlyLink)
|
||||
state.guistate.pageStates.menu.addCallback(isShown => {
|
||||
if(!onlyLink){
|
||||
state.guistate.pageStates.menu.addCallback((isShown) => {
|
||||
if (!onlyLink) {
|
||||
return true
|
||||
}
|
||||
if(isShown){
|
||||
if (isShown) {
|
||||
shown.setData(true)
|
||||
}else{
|
||||
} else {
|
||||
Utils.waitFor(250).then(() => {
|
||||
shown.setData(state.guistate.pageStates.menu.data)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div class="low-interaction flex h-screen flex-col gap-y-2 overflow-y-auto p-2 sm:gap-y-3 sm:p-3" class:hidden={!$shown}>
|
||||
<div
|
||||
class="low-interaction flex h-screen flex-col gap-y-2 overflow-y-auto p-2 sm:gap-y-3 sm:p-3"
|
||||
class:hidden={!$shown}
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h2>
|
||||
<Tr t={t.title} />
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
/**
|
||||
* Reuse a point if the clicked location is within this amount of meter
|
||||
*/
|
||||
export let snapTolerance: number = 5
|
||||
export let snapTolerance: number = 5
|
||||
|
||||
let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
|
||||
let adaptor = new MapLibreAdaptor(map, mapProperties)
|
||||
|
@ -101,7 +101,8 @@
|
|||
})
|
||||
let id = 0
|
||||
adaptor.lastClickLocation.addCallbackD(({ lon, lat }) => {
|
||||
let projected: Feature<Point, {index:number, id?: number, reuse?: string}> = GeoOperations.nearestPoint(wayGeojson, [lon, lat])
|
||||
let projected: Feature<Point, { index: number; id?: number; reuse?: string }> =
|
||||
GeoOperations.nearestPoint(wayGeojson, [lon, lat])
|
||||
|
||||
console.log("Added splitpoint", projected, id)
|
||||
|
||||
|
@ -110,36 +111,36 @@
|
|||
const i = projected.properties.index
|
||||
const p = projected.geometry.coordinates
|
||||
const way = wayGeojson.geometry.coordinates
|
||||
const nextPoint = <[number,number]> way[i + 1]
|
||||
const nextPoint = <[number, number]>way[i + 1]
|
||||
const nextDistance = GeoOperations.distanceBetween(nextPoint, p)
|
||||
const previousPoint = <[number,number]> way[i]
|
||||
const previousPoint = <[number, number]>way[i]
|
||||
const previousDistance = GeoOperations.distanceBetween(previousPoint, p)
|
||||
|
||||
console.log("ND", nextDistance, "PD", previousDistance)
|
||||
if(nextDistance <= snapTolerance && previousDistance >= nextDistance){
|
||||
if (nextDistance <= snapTolerance && previousDistance >= nextDistance) {
|
||||
projected = {
|
||||
type:"Feature",
|
||||
type: "Feature",
|
||||
geometry: {
|
||||
type:"Point",
|
||||
coordinates: nextPoint
|
||||
type: "Point",
|
||||
coordinates: nextPoint,
|
||||
},
|
||||
properties: {
|
||||
index: i+1,
|
||||
reuse: "yes"
|
||||
}
|
||||
index: i + 1,
|
||||
reuse: "yes",
|
||||
},
|
||||
}
|
||||
}
|
||||
if (previousDistance <= snapTolerance && previousDistance < nextDistance){
|
||||
if (previousDistance <= snapTolerance && previousDistance < nextDistance) {
|
||||
projected = {
|
||||
type:"Feature",
|
||||
type: "Feature",
|
||||
geometry: {
|
||||
type:"Point",
|
||||
coordinates: previousPoint
|
||||
type: "Point",
|
||||
coordinates: previousPoint,
|
||||
},
|
||||
properties: {
|
||||
index: i,
|
||||
reuse: "yes"
|
||||
}
|
||||
reuse: "yes",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
let errors = new UIEventSource<Translation[]>([])
|
||||
|
||||
async function handleFiles(files: FileList, ignoreGps: boolean= false) {
|
||||
async function handleFiles(files: FileList, ignoreGps: boolean = false) {
|
||||
const errs = []
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files.item(i)
|
||||
|
@ -102,7 +102,11 @@
|
|||
capture="environment"
|
||||
cls="button border-2 flex flex-col"
|
||||
multiple={true}
|
||||
on:submit={(e) =>{ handleFiles(e.detail) ; e.preventDefault(); e.stopPropagation()}}
|
||||
on:submit={(e) => {
|
||||
handleFiles(e.detail)
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<div class="flex w-full items-center justify-center text-2xl">
|
||||
{#if image !== undefined}
|
||||
|
@ -114,7 +118,7 @@
|
|||
{labelText}
|
||||
{:else}
|
||||
<div class="flex flex-col">
|
||||
<Tr t={t.addPicture}/>
|
||||
<Tr t={t.addPicture} />
|
||||
{#if noBlur}
|
||||
<span class="subtle text-sm">
|
||||
<Tr t={t.upload.noBlur} />
|
||||
|
@ -128,9 +132,13 @@
|
|||
accept=".jpg, .jpeg"
|
||||
cls="flex justify-center md:hidden button"
|
||||
multiple={true}
|
||||
on:submit={(e) =>{ return handleFiles(e.detail, true) ; e.preventDefault(); e.stopPropagation()}}
|
||||
on:submit={(e) => {
|
||||
return handleFiles(e.detail, true)
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<Tr t={t.selectFile}/>
|
||||
<Tr t={t.selectFile} />
|
||||
</FileSelector>
|
||||
<div class="subtle text-xs italic">
|
||||
<Tr t={Translations.t.general.attribution.panoramaxLicenseCCBYSA} />
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import maplibregl, { Map as MLMap, Map as MlMap, ScaleControl, SourceSpecification } from "maplibre-gl"
|
||||
import maplibregl, {
|
||||
Map as MLMap,
|
||||
Map as MlMap,
|
||||
ScaleControl,
|
||||
SourceSpecification,
|
||||
} from "maplibre-gl"
|
||||
import { RasterLayerPolygon } from "../../Models/RasterLayers"
|
||||
import { Utils } from "../../Utils"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
|
@ -43,10 +48,16 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
readonly allowRotating: UIEventSource<true | boolean | undefined>
|
||||
readonly allowZooming: UIEventSource<true | boolean | undefined>
|
||||
readonly lastClickLocation: Store<
|
||||
undefined | { lon: number; lat: number; mode: "left" | "right" | "middle" , /**
|
||||
* The nearest feature from a MapComplete layer
|
||||
*/
|
||||
nearestFeature?: Feature }
|
||||
| undefined
|
||||
| {
|
||||
lon: number
|
||||
lat: number
|
||||
mode: "left" | "right" | "middle"
|
||||
/**
|
||||
* The nearest feature from a MapComplete layer
|
||||
*/
|
||||
nearestFeature?: Feature
|
||||
}
|
||||
>
|
||||
readonly minzoom: UIEventSource<number>
|
||||
readonly maxzoom: UIEventSource<number>
|
||||
|
@ -64,9 +75,13 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
|
||||
private readonly _maplibreMap: Store<MLMap>
|
||||
|
||||
constructor(maplibreMap: Store<MLMap>, state?: Partial<MapProperties>, options?:{
|
||||
correctClick?: number
|
||||
}) {
|
||||
constructor(
|
||||
maplibreMap: Store<MLMap>,
|
||||
state?: Partial<MapProperties>,
|
||||
options?: {
|
||||
correctClick?: number
|
||||
}
|
||||
) {
|
||||
if (!MapLibreAdaptor.pmtilesInited) {
|
||||
maplibregl.addProtocol("pmtiles", new Protocol().tile)
|
||||
MapLibreAdaptor.pmtilesInited = true
|
||||
|
@ -106,7 +121,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
const lastClickLocation = new UIEventSource<{
|
||||
lat: number
|
||||
lon: number
|
||||
mode: "left" | "right" | "middle",
|
||||
mode: "left" | "right" | "middle"
|
||||
nearestFeature?: Feature
|
||||
}>(undefined)
|
||||
this.lastClickLocation = lastClickLocation
|
||||
|
@ -126,30 +141,32 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
const mouseEvent: MouseEvent = e.originalEvent
|
||||
mode = mode ?? clickmodes[mouseEvent.button]
|
||||
let nearestFeature: Feature = undefined
|
||||
if(options?.correctClick && maplibreMap.data){
|
||||
if (options?.correctClick && maplibreMap.data) {
|
||||
const map = maplibreMap.data
|
||||
const point = e.point
|
||||
const buffer = options?.correctClick
|
||||
const features = map.queryRenderedFeatures([
|
||||
[point.x - buffer, point.y - buffer],
|
||||
[point.x + buffer, point.y + buffer]
|
||||
]).filter(f => f.source.startsWith("mapcomplete_"))
|
||||
if(features.length === 1){
|
||||
const features = map
|
||||
.queryRenderedFeatures([
|
||||
[point.x - buffer, point.y - buffer],
|
||||
[point.x + buffer, point.y + buffer],
|
||||
])
|
||||
.filter((f) => f.source.startsWith("mapcomplete_"))
|
||||
if (features.length === 1) {
|
||||
nearestFeature = features[0]
|
||||
}else{
|
||||
} else {
|
||||
let nearestD: number = undefined
|
||||
for (const feature of features) {
|
||||
let d: number // in meter
|
||||
if(feature.geometry.type === "LineString"){
|
||||
const way = <Feature<LineString>> feature
|
||||
const lngLat:[number,number] = [e.lngLat.lng, e.lngLat.lat]
|
||||
if (feature.geometry.type === "LineString") {
|
||||
const way = <Feature<LineString>>feature
|
||||
const lngLat: [number, number] = [e.lngLat.lng, e.lngLat.lat]
|
||||
const p = GeoOperations.nearestPoint(way, lngLat)
|
||||
console.log(">>>",p, way, lngLat)
|
||||
if(!p){
|
||||
console.log(">>>", p, way, lngLat)
|
||||
if (!p) {
|
||||
continue
|
||||
}
|
||||
d = p.properties.dist * 1000
|
||||
if(nearestFeature === undefined || d < nearestD){
|
||||
if (nearestFeature === undefined || d < nearestD) {
|
||||
nearestFeature = way
|
||||
nearestD = d
|
||||
}
|
||||
|
@ -158,7 +175,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
}
|
||||
}
|
||||
lastClickLocation.setData({ lon, lat, mode, nearestFeature })
|
||||
|
||||
}
|
||||
|
||||
maplibreMap.addCallbackAndRunD((map) => {
|
||||
|
|
|
@ -39,7 +39,7 @@ class PointRenderingLayer {
|
|||
visibility?: Store<boolean>,
|
||||
fetchStore?: (id: string) => Store<Record<string, string>>,
|
||||
onClick?: (feature: Feature) => void,
|
||||
selectedElement?: Store<{ properties: { id?: string } }>,
|
||||
selectedElement?: Store<{ properties: { id?: string } }>
|
||||
) {
|
||||
this._visibility = visibility
|
||||
this._config = config
|
||||
|
@ -98,7 +98,7 @@ class PointRenderingLayer {
|
|||
" while rendering",
|
||||
location,
|
||||
"of",
|
||||
this._config,
|
||||
this._config
|
||||
)
|
||||
}
|
||||
const id = feature.properties.id + "-" + location
|
||||
|
@ -110,7 +110,10 @@ class PointRenderingLayer {
|
|||
this.addPoint(feature, <[number, number]>loc)
|
||||
}
|
||||
}
|
||||
if (feature.geometry.type === "MultiLineString" || feature.geometry.type === "Polygon") {
|
||||
if (
|
||||
feature.geometry.type === "MultiLineString" ||
|
||||
feature.geometry.type === "Polygon"
|
||||
) {
|
||||
for (const coors of feature.geometry.coordinates) {
|
||||
for (const loc of coors) {
|
||||
this.addPoint(feature, <[number, number]>loc)
|
||||
|
@ -122,7 +125,7 @@ class PointRenderingLayer {
|
|||
|
||||
const loc = GeoOperations.featureToCoordinateWithRenderingType(
|
||||
<any>feature,
|
||||
location,
|
||||
location
|
||||
)
|
||||
if (loc === undefined) {
|
||||
continue
|
||||
|
@ -251,7 +254,7 @@ class LineRenderingLayer {
|
|||
config: LineRenderingConfig,
|
||||
visibility?: Store<boolean>,
|
||||
fetchStore?: (id: string) => Store<Record<string, string>>,
|
||||
onClick?: (feature: Feature) => void,
|
||||
onClick?: (feature: Feature) => void
|
||||
) {
|
||||
this._layername = layername
|
||||
this._map = map
|
||||
|
@ -271,7 +274,7 @@ class LineRenderingLayer {
|
|||
|
||||
private async addSymbolLayer(
|
||||
sourceId: string,
|
||||
imageAlongWay: { if?: TagsFilter; then: string }[],
|
||||
imageAlongWay: { if?: TagsFilter; then: string }[]
|
||||
) {
|
||||
const map = this._map
|
||||
await Promise.allSettled(
|
||||
|
@ -301,7 +304,7 @@ class LineRenderingLayer {
|
|||
spec.filter = filter
|
||||
}
|
||||
map.addLayer(spec)
|
||||
}),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -311,7 +314,7 @@ class LineRenderingLayer {
|
|||
* @private
|
||||
*/
|
||||
private calculatePropsFor(
|
||||
properties: Record<string, string>,
|
||||
properties: Record<string, string>
|
||||
): Partial<Record<(typeof LineRenderingLayer.lineConfigKeys)[number], string>> {
|
||||
const config = this._config
|
||||
|
||||
|
@ -393,7 +396,7 @@ class LineRenderingLayer {
|
|||
} catch (e) {
|
||||
console.error(
|
||||
`Invalid dasharray in layer ${this._layername}:`,
|
||||
this._config.dashArray,
|
||||
this._config.dashArray
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -410,11 +413,11 @@ class LineRenderingLayer {
|
|||
}
|
||||
map.setFeatureState(
|
||||
{ source: this._layername, id: feature.properties.id },
|
||||
this.calculatePropsFor(feature.properties),
|
||||
this.calculatePropsFor(feature.properties)
|
||||
)
|
||||
}
|
||||
|
||||
if(this._onClick){
|
||||
if (this._onClick) {
|
||||
map.on("click", linelayer, (e) => {
|
||||
// line-layer-listener
|
||||
e.originalEvent["consumed"] = true
|
||||
|
@ -455,7 +458,7 @@ class LineRenderingLayer {
|
|||
"Error while setting visibility of layers ",
|
||||
linelayer,
|
||||
polylayer,
|
||||
e,
|
||||
e
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -476,7 +479,7 @@ class LineRenderingLayer {
|
|||
console.trace(
|
||||
"Got a feature without ID; this causes rendering bugs:",
|
||||
feature,
|
||||
"from",
|
||||
"from"
|
||||
)
|
||||
LineRenderingLayer.missingIdTriggered = true
|
||||
}
|
||||
|
@ -488,7 +491,7 @@ class LineRenderingLayer {
|
|||
if (this._fetchStore === undefined) {
|
||||
map.setFeatureState(
|
||||
{ source: this._layername, id },
|
||||
this.calculatePropsFor(feature.properties),
|
||||
this.calculatePropsFor(feature.properties)
|
||||
)
|
||||
} else {
|
||||
const tags = this._fetchStore(id)
|
||||
|
@ -505,7 +508,7 @@ class LineRenderingLayer {
|
|||
}
|
||||
map.setFeatureState(
|
||||
{ source: this._layername, id },
|
||||
this.calculatePropsFor(properties),
|
||||
this.calculatePropsFor(properties)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -529,7 +532,7 @@ export default class ShowDataLayer {
|
|||
layer: LayerConfig
|
||||
drawMarkers?: true | boolean
|
||||
drawLines?: true | boolean
|
||||
},
|
||||
}
|
||||
) {
|
||||
this._options = options
|
||||
this.onDestroy.push(map.addCallbackAndRunD((map) => this.initDrawFeatures(map)))
|
||||
|
@ -539,7 +542,7 @@ export default class ShowDataLayer {
|
|||
mlmap: UIEventSource<MlMap>,
|
||||
features: FeatureSource,
|
||||
layers: LayerConfig[],
|
||||
options?: Partial<ShowDataLayerOptions>,
|
||||
options?: Partial<ShowDataLayerOptions>
|
||||
) {
|
||||
const perLayer: PerLayerFeatureSourceSplitter<FeatureSourceForLayer> =
|
||||
new PerLayerFeatureSourceSplitter(
|
||||
|
@ -547,7 +550,7 @@ export default class ShowDataLayer {
|
|||
features,
|
||||
{
|
||||
constructStore: (features, layer) => new SimpleFeatureSource(layer, features),
|
||||
},
|
||||
}
|
||||
)
|
||||
if (options?.zoomToFeatures) {
|
||||
options.zoomToFeatures = false
|
||||
|
@ -571,7 +574,7 @@ export default class ShowDataLayer {
|
|||
public static showRange(
|
||||
map: Store<MlMap>,
|
||||
features: FeatureSource,
|
||||
doShowLayer?: Store<boolean>,
|
||||
doShowLayer?: Store<boolean>
|
||||
): ShowDataLayer {
|
||||
return new ShowDataLayer(map, {
|
||||
layer: ShowDataLayer.rangeLayer,
|
||||
|
@ -580,8 +583,7 @@ export default class ShowDataLayer {
|
|||
})
|
||||
}
|
||||
|
||||
public destruct() {
|
||||
}
|
||||
public destruct() {}
|
||||
|
||||
private static zoomToCurrentFeatures(map: MlMap, features: Feature[]) {
|
||||
if (!features || !map || features.length == 0) {
|
||||
|
@ -605,8 +607,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++) {
|
||||
|
@ -618,7 +620,7 @@ export default class ShowDataLayer {
|
|||
lineRenderingConfig,
|
||||
doShowLayer,
|
||||
fetchStore,
|
||||
onClick,
|
||||
onClick
|
||||
)
|
||||
this.onDestroy.push(l.destruct)
|
||||
}
|
||||
|
@ -634,13 +636,13 @@ export default class ShowDataLayer {
|
|||
doShowLayer,
|
||||
fetchStore,
|
||||
onClick,
|
||||
selectedElement,
|
||||
selectedElement
|
||||
)
|
||||
}
|
||||
}
|
||||
if (this._options.zoomToFeatures) {
|
||||
features.features.addCallbackAndRunD((features) =>
|
||||
ShowDataLayer.zoomToCurrentFeatures(map, features),
|
||||
ShowDataLayer.zoomToCurrentFeatures(map, features)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,11 +61,11 @@
|
|||
const changedProperties = TagUtils.changeAsProperties(selectedTags.asChange(tags?.data ?? {}))
|
||||
const deleteReason = changedProperties[DeleteConfig.deleteReasonKey]
|
||||
if (deleteReason) {
|
||||
|
||||
let softDeletionTags: UploadableTag
|
||||
if(hasSoftDeletion){
|
||||
softDeletionTags = new And([deleteConfig.softDeletionTags,
|
||||
...layer.tagRenderings.flatMap(tr => tr.onSoftDelete ?? []),
|
||||
if (hasSoftDeletion) {
|
||||
softDeletionTags = new And([
|
||||
deleteConfig.softDeletionTags,
|
||||
...layer.tagRenderings.flatMap((tr) => tr.onSoftDelete ?? []),
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
* Shows _all_ disabled questions
|
||||
*/
|
||||
export let state
|
||||
let layers = state.layout.layers.filter(l => l.isNormal())
|
||||
let layers = state.layout.layers.filter((l) => l.isNormal())
|
||||
|
||||
let allDisabled = Stores.concat<string>(layers.map(l => state.userRelatedState.getThemeDisabled(state.layout.id, l.id))).map(l => [].concat(...l))
|
||||
let allDisabled = Stores.concat<string>(
|
||||
layers.map((l) => state.userRelatedState.getThemeDisabled(state.layout.id, l.id))
|
||||
).map((l) => [].concat(...l))
|
||||
const t = Translations.t.general.questions
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
<script lang="ts">/**
|
||||
* Gives an overview of questions which are disabled for the given theme
|
||||
*/
|
||||
import UserRelatedState from "../../Logic/State/UserRelatedState"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import { Translation } from "../i18n/Translation"
|
||||
import { XMarkIcon } from "@babeard/svelte-heroicons/mini"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Gives an overview of questions which are disabled for the given theme
|
||||
*/
|
||||
import UserRelatedState from "../../Logic/State/UserRelatedState"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import { Translation } from "../i18n/Translation"
|
||||
import { XMarkIcon } from "@babeard/svelte-heroicons/mini"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
|
||||
export let layer: LayerConfig
|
||||
export let state: ThemeViewState
|
||||
export let layer: LayerConfig
|
||||
export let state: ThemeViewState
|
||||
|
||||
let disabledQuestions = state.userRelatedState.getThemeDisabled(state.layout.id, layer.id)
|
||||
let disabledQuestions = state.userRelatedState.getThemeDisabled(state.layout.id, layer.id)
|
||||
|
||||
function getQuestion(id: string): Translation {
|
||||
return layer.tagRenderings.find(q => q.id === id).question.Subs({})
|
||||
}
|
||||
function getQuestion(id: string): Translation {
|
||||
return layer.tagRenderings.find((q) => q.id === id).question.Subs({})
|
||||
}
|
||||
|
||||
function enable(idToEnable: string) {
|
||||
const newList = disabledQuestions.data.filter(id => id !== idToEnable)
|
||||
disabledQuestions.set(newList)
|
||||
}
|
||||
function enable(idToEnable: string) {
|
||||
const newList = disabledQuestions.data.filter((id) => id !== idToEnable)
|
||||
disabledQuestions.set(newList)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $disabledQuestions.length > 0}
|
||||
<div class="low-interaction p-2">
|
||||
|
||||
<h4 class="flex my-2">
|
||||
<h4 class="my-2 flex">
|
||||
<div class="no-image-background block h-6 w-6">
|
||||
<ToSvelte construct={() => layer.defaultIcon()} />
|
||||
</div>
|
||||
|
@ -37,7 +37,7 @@ function enable(idToEnable: string) {
|
|||
{#each $disabledQuestions as id}
|
||||
<button class="badge button-unstyled" on:click={() => enable(id)}>
|
||||
<Tr cls="ml-2" t={getQuestion(id)} />
|
||||
<XMarkIcon class="w-4 h-4 mr-2" />
|
||||
<XMarkIcon class="mr-2 h-4 w-4" />
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
@ -88,7 +88,13 @@
|
|||
{:else if step === "splitting"}
|
||||
<div class="interactive border-interactive flex flex-col p-2">
|
||||
<div class="h-80 w-full">
|
||||
<WaySplitMap {state} {splitPoints} {osmWay} {snapTolerance} mapProperties={{rasterLayer: state.mapProperties.rasterLayer}}/>
|
||||
<WaySplitMap
|
||||
{state}
|
||||
{splitPoints}
|
||||
{osmWay}
|
||||
{snapTolerance}
|
||||
mapProperties={{ rasterLayer: state.mapProperties.rasterLayer }}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex w-full flex-wrap-reverse md:flex-nowrap">
|
||||
<BackButton
|
||||
|
|
|
@ -45,16 +45,15 @@
|
|||
}
|
||||
|
||||
const baseQuestions = (layer?.tagRenderings ?? [])?.filter(
|
||||
(tr) => allowed(tr.labels) && tr.question !== undefined,
|
||||
(tr) => allowed(tr.labels) && tr.question !== undefined
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* Ids of skipped questions
|
||||
*/
|
||||
let skippedQuestions = new UIEventSource<Set<string>>(new Set<string>())
|
||||
let layerDisabledForTheme = state.userRelatedState.getThemeDisabled(state.theme.id, layer.id)
|
||||
layerDisabledForTheme.addCallbackAndRunD(disabled => {
|
||||
layerDisabledForTheme.addCallbackAndRunD((disabled) => {
|
||||
skippedQuestions.set(new Set(disabled.concat(Array.from(skippedQuestions.data))))
|
||||
})
|
||||
let questionboxElem: HTMLDivElement
|
||||
|
@ -78,10 +77,10 @@
|
|||
}
|
||||
return questionsToAsk
|
||||
},
|
||||
[skippedQuestions],
|
||||
[skippedQuestions]
|
||||
)
|
||||
let firstQuestion: UIEventSource<TagRenderingConfig> = new UIEventSource<TagRenderingConfig>(
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
let allQuestionsToAsk: UIEventSource<TagRenderingConfig[]> = new UIEventSource<
|
||||
TagRenderingConfig[]
|
||||
|
@ -106,7 +105,6 @@
|
|||
let loginEnabled = state.featureSwitches.featureSwitchEnableLogin
|
||||
let debug = state.featureSwitches.featureSwitchIsDebugging
|
||||
|
||||
|
||||
function skip(question: { id: string }, didAnswer: boolean = false) {
|
||||
skippedQuestions.data.add(question.id) // Must use ID, the config object might be a copy of the original
|
||||
skippedQuestions.ping()
|
||||
|
@ -131,13 +129,7 @@
|
|||
{#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}
|
||||
|
@ -148,14 +140,14 @@
|
|||
{state}
|
||||
{tags}
|
||||
on:saved={() => {
|
||||
skip($firstQuestion, true)
|
||||
}}
|
||||
skip($firstQuestion, true)
|
||||
}}
|
||||
>
|
||||
<button
|
||||
class="secondary"
|
||||
on:click={() => {
|
||||
skip($firstQuestion)
|
||||
}}
|
||||
skip($firstQuestion)
|
||||
}}
|
||||
slot="cancel"
|
||||
>
|
||||
<Tr t={Translations.t.general.skip} />
|
||||
|
@ -170,7 +162,6 @@
|
|||
{/if}
|
||||
|
||||
<div class="mt-4 mb-8">
|
||||
|
||||
{#if skipped + answered > 0}
|
||||
<div class="flex justify-center">
|
||||
{#if answered === 0}
|
||||
|
@ -198,9 +189,9 @@
|
|||
{:else}
|
||||
<Tr
|
||||
t={Translations.t.general.questionBox.answeredMultipleSkippedMultiple.Subs({
|
||||
answered,
|
||||
skipped,
|
||||
})}
|
||||
answered,
|
||||
skipped,
|
||||
})}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -215,7 +206,6 @@
|
|||
>
|
||||
<Tr t={Translations.t.general.questionBox.reactivate} />
|
||||
</button>
|
||||
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
|
@ -223,13 +213,12 @@
|
|||
<button
|
||||
class="w-full"
|
||||
on:click={() => {
|
||||
skippedQuestions.setData(new Set())
|
||||
skipped = 0
|
||||
}}
|
||||
skippedQuestions.setData(new Set())
|
||||
skipped = 0
|
||||
}}
|
||||
>
|
||||
Show the disabled questions for this object
|
||||
</button>
|
||||
|
||||
{/if}
|
||||
{#if $debug}
|
||||
Skipped questions are {Array.from($skippedQuestions).join(", ")}
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
return !m.hideInAnswer.matchesProperties(tgs)
|
||||
})
|
||||
selectedMapping = mappings?.findIndex(
|
||||
(mapping) => mapping.if.matchesProperties(tgs) || mapping.alsoShowIf?.matchesProperties(tgs),
|
||||
(mapping) => mapping.if.matchesProperties(tgs) || mapping.alsoShowIf?.matchesProperties(tgs)
|
||||
)
|
||||
if (selectedMapping < 0) {
|
||||
selectedMapping = undefined
|
||||
|
@ -201,7 +201,7 @@
|
|||
if (freeformValue?.length > 0) {
|
||||
selectedMapping = config.mappings.length
|
||||
}
|
||||
}),
|
||||
})
|
||||
)
|
||||
|
||||
$: {
|
||||
|
@ -219,7 +219,7 @@
|
|||
$freeformInput,
|
||||
selectedMapping,
|
||||
checkedMappings,
|
||||
tags.data,
|
||||
tags.data
|
||||
)
|
||||
if (featureSwitchIsDebugging?.data) {
|
||||
console.log(
|
||||
|
@ -231,7 +231,7 @@
|
|||
currentTags: tags.data,
|
||||
},
|
||||
" --> ",
|
||||
selectedTags,
|
||||
selectedTags
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -253,7 +253,7 @@
|
|||
selectedTags = new And([...selectedTags.and, ...extraTagsArray])
|
||||
} else {
|
||||
console.error(
|
||||
"selectedTags is not of type Tag or And, it is a " + JSON.stringify(selectedTags),
|
||||
"selectedTags is not of type Tag or And, it is a " + JSON.stringify(selectedTags)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -322,7 +322,7 @@
|
|||
onDestroy(
|
||||
state.osmConnection?.userDetails?.addCallbackAndRun((ud) => {
|
||||
numberOfCs = ud.csCount
|
||||
}),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -351,7 +351,7 @@
|
|||
}
|
||||
|
||||
function enableQuestion() {
|
||||
const newList = disabledInTheme.data?.filter(id => id !== config.id)
|
||||
const newList = disabledInTheme.data?.filter((id) => id !== config.id)
|
||||
disabledInTheme.set(newList)
|
||||
menuIsOpened.set(false)
|
||||
}
|
||||
|
@ -359,7 +359,6 @@
|
|||
|
||||
{#if question !== undefined}
|
||||
<div class={clss}>
|
||||
|
||||
{#if layer.isNormal()}
|
||||
<LoginToggle {state}>
|
||||
<DotMenu hideBackground={true} open={menuIsOpened}>
|
||||
|
|
|
@ -2116,7 +2116,6 @@ export default class SpecialVisualizations {
|
|||
constr(state) {
|
||||
return new SvelteUIElement(DisabledQuestions, { state })
|
||||
},
|
||||
|
||||
},
|
||||
]
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
import { DownloadIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
|
||||
|
||||
export let paths: string[]
|
||||
|
||||
let downloaded = 0
|
||||
|
@ -26,40 +25,42 @@
|
|||
const filteredLayer = new FilteredLayer(layer)
|
||||
|
||||
let allData = <UIEventSource<(ChangeSetData & OsmFeature)[]>>UIEventSource.FromPromise(
|
||||
Promise.all(paths.map(async p => {
|
||||
const r = await Utils.downloadJson<FeatureCollection>(p)
|
||||
downloaded++
|
||||
return r
|
||||
}))
|
||||
).mapD(list => [].concat(...list.map(f => f.features)))
|
||||
|
||||
let overview = allData.mapD(data =>
|
||||
ChangesetsOverview.fromDirtyData(data)
|
||||
.filter((cs) => filteredLayer.isShown(<any>cs.properties)), [filteredLayer.currentFilter])
|
||||
|
||||
const trs = layer.tagRenderings.filter(
|
||||
(tr) => tr.mappings?.length > 0 || tr.freeform?.key !== undefined
|
||||
).filter(tr => tr.question !== undefined)
|
||||
|
||||
let diffInDays = overview.mapD(overview => {
|
||||
const dateStrings = Utils.NoNull(
|
||||
overview._meta.map((cs) => cs.properties.date)
|
||||
Promise.all(
|
||||
paths.map(async (p) => {
|
||||
const r = await Utils.downloadJson<FeatureCollection>(p)
|
||||
downloaded++
|
||||
return r
|
||||
})
|
||||
)
|
||||
).mapD((list) => [].concat(...list.map((f) => f.features)))
|
||||
|
||||
let overview = allData.mapD(
|
||||
(data) =>
|
||||
ChangesetsOverview.fromDirtyData(data).filter((cs) =>
|
||||
filteredLayer.isShown(<any>cs.properties)
|
||||
),
|
||||
[filteredLayer.currentFilter]
|
||||
)
|
||||
|
||||
const trs = layer.tagRenderings
|
||||
.filter((tr) => tr.mappings?.length > 0 || tr.freeform?.key !== undefined)
|
||||
.filter((tr) => tr.question !== undefined)
|
||||
|
||||
let diffInDays = overview.mapD((overview) => {
|
||||
const dateStrings = Utils.NoNull(overview._meta.map((cs) => cs.properties.date))
|
||||
const dates: number[] = dateStrings.map((d) => new Date(d).getTime())
|
||||
const mindate = Math.min(...dates)
|
||||
const maxdate = Math.max(...dates)
|
||||
return (maxdate - mindate) / (1000 * 60 * 60 * 24)
|
||||
|
||||
})
|
||||
|
||||
function offerAsDownload(){
|
||||
const data = GeoOperations.toCSV($overview._meta, {
|
||||
ignoreTags:
|
||||
/^((deletion:node)|(import:node)|(move:node)|(soft-delete:))/,
|
||||
})
|
||||
Utils.offerContentsAsDownloadableFile(data, "statistics.csv", {
|
||||
mimetype: "text/csv",
|
||||
})
|
||||
function offerAsDownload() {
|
||||
const data = GeoOperations.toCSV($overview._meta, {
|
||||
ignoreTags: /^((deletion:node)|(import:node)|(move:node)|(soft-delete:))/,
|
||||
})
|
||||
Utils.offerContentsAsDownloadableFile(data, "statistics.csv", {
|
||||
mimetype: "text/csv",
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -73,15 +74,15 @@
|
|||
<Accordion>
|
||||
{#each trs as tr}
|
||||
<AccordionItem paddingDefault="p-0" inactiveClass="text-black">
|
||||
<span slot="header" class={"w-full p-2 text-base"}>
|
||||
{tr.question ?? tr.id}
|
||||
</span>
|
||||
<span slot="header" class={"w-full p-2 text-base"}>
|
||||
{tr.question ?? tr.id}
|
||||
</span>
|
||||
<SingleStat {tr} overview={$overview} diffInDays={$diffInDays} />
|
||||
</AccordionItem>
|
||||
{/each}
|
||||
</Accordion>
|
||||
<button on:click={() => offerAsDownload()}>
|
||||
<DownloadIcon class="w-6 h-6" />
|
||||
<DownloadIcon class="h-6 w-6" />
|
||||
Download as CSV
|
||||
</button>
|
||||
{/if}
|
||||
|
|
|
@ -27,9 +27,9 @@ export interface ChangeSetData extends Feature<Polygon> {
|
|||
delete: number
|
||||
area: number
|
||||
is_suspect: boolean
|
||||
// harmful: any
|
||||
// harmful: any
|
||||
checked: boolean
|
||||
// check_date: any
|
||||
// check_date: any
|
||||
host: string
|
||||
theme: string
|
||||
imagery: string
|
||||
|
@ -61,7 +61,7 @@ export class ChangesetsOverview {
|
|||
"testing mapcomplete 0.0.0": "buurtnatuur",
|
||||
entrances: "indoor",
|
||||
"https://raw.githubusercontent.com/osmbe/play/master/mapcomplete/geveltuinen/geveltuinen.json":
|
||||
"geveltuintjes"
|
||||
"geveltuintjes",
|
||||
}
|
||||
|
||||
public static readonly valuesToSum: ReadonlyArray<string> = [
|
||||
|
@ -88,7 +88,7 @@ export class ChangesetsOverview {
|
|||
return new ChangesetsOverview(meta?.map((cs) => ChangesetsOverview.cleanChangesetData(cs)))
|
||||
}
|
||||
|
||||
private static cleanChangesetData(cs: ChangeSetData & OsmFeature): (ChangeSetData & OsmFeature) {
|
||||
private static cleanChangesetData(cs: ChangeSetData & OsmFeature): ChangeSetData & OsmFeature {
|
||||
if (cs === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script lang="ts">
|
||||
|
||||
/**
|
||||
* Shows the statistics for a single item
|
||||
*/
|
||||
|
@ -14,9 +13,7 @@
|
|||
|
||||
let total: number = undefined
|
||||
if (tr.freeform?.key !== undefined) {
|
||||
total = new Set(
|
||||
overview._meta.map((f) => f.properties[tr.freeform.key])
|
||||
).size
|
||||
total = new Set(overview._meta.map((f) => f.properties[tr.freeform.key])).size
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -26,31 +23,28 @@
|
|||
<h3>By number of changesets</h3>
|
||||
|
||||
<div class="flex">
|
||||
|
||||
<ToSvelte construct={ new TagRenderingChart(overview._meta, tr, {
|
||||
groupToOtherCutoff:
|
||||
total > 50 ? 25 : total > 10 ? 3 : 0,
|
||||
chartstyle: "width: 24rem; height: 24rem",
|
||||
chartType: "doughnut",
|
||||
sort: true,
|
||||
})} />
|
||||
<ToSvelte
|
||||
construct={new TagRenderingChart(overview._meta, tr, {
|
||||
groupToOtherCutoff: total > 50 ? 25 : total > 10 ? 3 : 0,
|
||||
chartstyle: "width: 24rem; height: 24rem",
|
||||
chartType: "doughnut",
|
||||
sort: true,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<ToSvelte construct={new StackedRenderingChart(tr, overview._meta, {
|
||||
period: diffInDays <= 367 ? "day" : "month",
|
||||
groupToOtherCutoff:
|
||||
total > 50 ? 25 : total > 10 ? 3 : 0,
|
||||
})} />
|
||||
|
||||
<ToSvelte
|
||||
construct={new StackedRenderingChart(tr, overview._meta, {
|
||||
period: diffInDays <= 367 ? "day" : "month",
|
||||
groupToOtherCutoff: total > 50 ? 25 : total > 10 ? 3 : 0,
|
||||
})}
|
||||
/>
|
||||
|
||||
<h3>By number of modifications</h3>
|
||||
<ToSvelte construct={ new StackedRenderingChart( tr, overview._meta,
|
||||
{
|
||||
period: diffInDays <= 367 ? "day" : "month",
|
||||
groupToOtherCutoff: total > 50 ? 10 : 0,
|
||||
sumFields: ChangesetsOverview. valuesToSum,
|
||||
}
|
||||
)} />
|
||||
|
||||
|
||||
<ToSvelte
|
||||
construct={new StackedRenderingChart(tr, overview._meta, {
|
||||
period: diffInDays <= 367 ? "day" : "month",
|
||||
groupToOtherCutoff: total > 50 ? 10 : 0,
|
||||
sumFields: ChangesetsOverview.valuesToSum,
|
||||
})}
|
||||
/>
|
||||
|
|
|
@ -8,23 +8,19 @@
|
|||
"https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/changeset-metadata/"
|
||||
let stats_files = "file-overview.json"
|
||||
|
||||
let indexFile = UIEventSource.FromPromise(
|
||||
Utils.downloadJson<string[]>(homeUrl + stats_files)
|
||||
)
|
||||
|
||||
let indexFile = UIEventSource.FromPromise(Utils.downloadJson<string[]>(homeUrl + stats_files))
|
||||
</script>
|
||||
|
||||
<div class="m-4">
|
||||
<div class="flex justify-between">
|
||||
|
||||
<h2>Statistics of changes made with MapComplete</h2>
|
||||
<a href="/" class="button">Back to index</a>
|
||||
</div>
|
||||
{#if $indexFile === undefined}
|
||||
<Loading>Loading index file...</Loading>
|
||||
{:else}
|
||||
<AllStats paths={$indexFile.filter(p => p.startsWith("stats")).map(p => homeUrl+"/"+p)} />
|
||||
<AllStats
|
||||
paths={$indexFile.filter((p) => p.startsWith("stats")).map((p) => homeUrl + "/" + p)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -28,18 +28,23 @@
|
|||
|
||||
const [[latD], [latM], [latS, latSDenom]] = <
|
||||
[[number, number], [number, number], [number, number]]
|
||||
>tags?.GPSLatitude?.value
|
||||
>tags?.GPSLatitude?.value
|
||||
const [[lonD], [lonM], [lonS, lonSDenom]] = <
|
||||
[[number, number], [number, number], [number, number]]
|
||||
>tags?.GPSLongitude?.value
|
||||
>tags?.GPSLongitude?.value
|
||||
const exifLat = latD + latM / 60 + latS / (3600 * latSDenom)
|
||||
const exifLon = lonD + lonM / 60 + lonS / (3600 * lonSDenom)
|
||||
if (typeof exifLat === "number" && !isNaN(exifLat) && typeof exifLon === "number" && !isNaN(exifLon)
|
||||
&& !(exifLat === 0 && exifLon === 0)) {
|
||||
if (
|
||||
typeof exifLat === "number" &&
|
||||
!isNaN(exifLat) &&
|
||||
typeof exifLon === "number" &&
|
||||
!isNaN(exifLon) &&
|
||||
!(exifLat === 0 && exifLon === 0)
|
||||
) {
|
||||
lat = exifLat
|
||||
lon = exifLon
|
||||
l("Using EXIFLAT + EXIFLON")
|
||||
}else{
|
||||
} else {
|
||||
l("NOT using exifLat and exifLon: invalid value detected")
|
||||
}
|
||||
l("Lat and lon are", lat, lon)
|
||||
|
@ -54,11 +59,7 @@
|
|||
}
|
||||
|
||||
try {
|
||||
|
||||
const p = new AuthorizedPanoramax(
|
||||
Constants.panoramax.url,
|
||||
Constants.panoramax.token,
|
||||
)
|
||||
const p = new AuthorizedPanoramax(Constants.panoramax.url, Constants.panoramax.token)
|
||||
const sequenceId = "7f34cf53-27ff-46c9-ac22-78511fa8457a" // test-sequence
|
||||
l("Fetching sequence number...")
|
||||
const sequence: { id: string; "stats:items": { count: number } } = (
|
||||
|
@ -71,7 +72,7 @@
|
|||
datetime,
|
||||
isBlurred: false,
|
||||
exifOverride: {
|
||||
"Artist": "TEST ACCOUNT",
|
||||
Artist: "TEST ACCOUNT",
|
||||
},
|
||||
})
|
||||
l("Upload completed. Adding meta")
|
||||
|
@ -87,7 +88,6 @@
|
|||
<div class="border border-black p-1">Select file</div>
|
||||
</FileSelector>
|
||||
<div class="flex flex-col">
|
||||
|
||||
{#each $log as logl}
|
||||
<div>{logl}</div>
|
||||
{/each}
|
||||
|
|
|
@ -479,11 +479,7 @@
|
|||
state.selectedElement.setData(undefined)
|
||||
}}
|
||||
>
|
||||
<SelectedElementView
|
||||
{state}
|
||||
layer={$selectedLayer}
|
||||
selectedElement={$selectedElement}
|
||||
/>
|
||||
<SelectedElementView {state} layer={$selectedLayer} selectedElement={$selectedElement} />
|
||||
</FloatOver>
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue