forked from MapComplete/MapComplete
		
	Refactoring: fix tests
This commit is contained in:
		
							parent
							
								
									a3242a3c7d
								
							
						
					
					
						commit
						670d2f0b32
					
				
					 4 changed files with 213 additions and 234 deletions
				
			
		|  | @ -1,153 +0,0 @@ | ||||||
| import { UIEventSource } from "../../Logic/UIEventSource" |  | ||||||
| import BaseUIElement from "../BaseUIElement" |  | ||||||
| import Translations from "../i18n/Translations" |  | ||||||
| import { GeoOperations } from "../../Logic/GeoOperations" |  | ||||||
| import NearbyImages, { NearbyImageOptions, P4CPicture, SelectOneNearbyImage } from "./NearbyImages" |  | ||||||
| import { SubstitutedTranslation } from "../SubstitutedTranslation" |  | ||||||
| import { Tag } from "../../Logic/Tags/Tag" |  | ||||||
| import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction" |  | ||||||
| import { And } from "../../Logic/Tags/And" |  | ||||||
| import { SaveButton } from "./SaveButton" |  | ||||||
| import Lazy from "../Base/Lazy" |  | ||||||
| import { CheckBox } from "../Input/Checkboxes" |  | ||||||
| import Slider from "../Input/Slider" |  | ||||||
| import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders" |  | ||||||
| import Combine from "../Base/Combine" |  | ||||||
| import { VariableUiElement } from "../Base/VariableUIElement" |  | ||||||
| import Toggle from "../Input/Toggle" |  | ||||||
| import Title from "../Base/Title" |  | ||||||
| import { MapillaryLinkVis } from "./MapillaryLinkVis" |  | ||||||
| import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization" |  | ||||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" |  | ||||||
| import { Feature } from "geojson" |  | ||||||
| 
 |  | ||||||
| export class NearbyImageVis implements SpecialVisualization { |  | ||||||
|     args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [ |  | ||||||
|         { |  | ||||||
|             name: "mode", |  | ||||||
|             defaultValue: "expandable", |  | ||||||
|             doc: "Indicates how this component is initialized. Options are: \n\n- `open`: always show and load the pictures\n- `collapsable`: show the pictures, but a user can collapse them\n- `expandable`: shown by default; but a user can collapse them.", |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             name: "mapillary", |  | ||||||
|             defaultValue: "true", |  | ||||||
|             doc: "If 'true', includes a link to mapillary on this location.", |  | ||||||
|         }, |  | ||||||
|     ] |  | ||||||
|     docs = |  | ||||||
|         "A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature" |  | ||||||
|     funcName = "nearby_images" |  | ||||||
| 
 |  | ||||||
|     constr( |  | ||||||
|         state: SpecialVisualizationState, |  | ||||||
|         tagSource: UIEventSource<Record<string, string>>, |  | ||||||
|         args: string[], |  | ||||||
|         feature: Feature, |  | ||||||
|         layer: LayerConfig |  | ||||||
|     ): BaseUIElement { |  | ||||||
|         const t = Translations.t.image.nearbyPictures |  | ||||||
|         const mode: "open" | "expandable" | "collapsable" = <any>args[0] |  | ||||||
|         const [lon, lat] = GeoOperations.centerpointCoordinates(feature) |  | ||||||
|         const id: string = tagSource.data["id"] |  | ||||||
|         const canBeEdited: boolean = !!id?.match("(node|way|relation)/-?[0-9]+") |  | ||||||
|         const selectedImage = new UIEventSource<P4CPicture>(undefined) |  | ||||||
| 
 |  | ||||||
|         let saveButton: BaseUIElement = undefined |  | ||||||
|         if (canBeEdited) { |  | ||||||
|             const confirmText: BaseUIElement = new SubstitutedTranslation( |  | ||||||
|                 t.confirm, |  | ||||||
|                 tagSource, |  | ||||||
|                 state |  | ||||||
|             ) |  | ||||||
| 
 |  | ||||||
|             const onSave = async () => { |  | ||||||
|                 console.log("Selected a picture...", selectedImage.data) |  | ||||||
|                 const osmTags = selectedImage.data.osmTags |  | ||||||
|                 const tags: Tag[] = [] |  | ||||||
|                 for (const key in osmTags) { |  | ||||||
|                     tags.push(new Tag(key, osmTags[key])) |  | ||||||
|                 } |  | ||||||
|                 await state?.changes?.applyAction( |  | ||||||
|                     new ChangeTagAction(id, new And(tags), tagSource.data, { |  | ||||||
|                         theme: state?.layout.id, |  | ||||||
|                         changeType: "link-image", |  | ||||||
|                     }) |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|             saveButton = new SaveButton( |  | ||||||
|                 selectedImage, |  | ||||||
|                 state.osmConnection, |  | ||||||
|                 confirmText, |  | ||||||
|                 t.noImageSelected |  | ||||||
|             ) |  | ||||||
|                 .onClick(onSave) |  | ||||||
|                 .SetClass("flex justify-end") |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const nearby = new Lazy(() => { |  | ||||||
|             const towardsCenter = new CheckBox(t.onlyTowards, false) |  | ||||||
| 
 |  | ||||||
|             const maxSearchRadius = 100 |  | ||||||
|             const stepSize = 10 |  | ||||||
|             const defaultValue = Math.floor(maxSearchRadius / (2 * stepSize)) * stepSize |  | ||||||
|             const fromOsmPreferences = state?.osmConnection |  | ||||||
|                 ?.GetPreference("nearby-images-radius", "" + defaultValue) |  | ||||||
|                 .sync( |  | ||||||
|                     (s) => Number(s), |  | ||||||
|                     [], |  | ||||||
|                     (i) => "" + i |  | ||||||
|                 ) |  | ||||||
|             const radiusValue = new UIEventSource(fromOsmPreferences.data) |  | ||||||
|             radiusValue.addCallbackAndRunD((v) => fromOsmPreferences.setData(v)) |  | ||||||
| 
 |  | ||||||
|             const radius = new Slider(stepSize, maxSearchRadius, { |  | ||||||
|                 value: radiusValue, |  | ||||||
|                 step: 10, |  | ||||||
|             }) |  | ||||||
|             const alreadyInTheImage = AllImageProviders.LoadImagesFor(tagSource) |  | ||||||
|             const options: NearbyImageOptions & { value } = { |  | ||||||
|                 lon, |  | ||||||
|                 lat, |  | ||||||
|                 searchRadius: maxSearchRadius, |  | ||||||
|                 shownRadius: radius.GetValue(), |  | ||||||
|                 value: selectedImage, |  | ||||||
|                 blacklist: alreadyInTheImage, |  | ||||||
|                 towardscenter: towardsCenter.GetValue(), |  | ||||||
|                 maxDaysOld: 365 * 3, |  | ||||||
|             } |  | ||||||
|             const slideshow = canBeEdited |  | ||||||
|                 ? new SelectOneNearbyImage(options, state.indexedFeatures) |  | ||||||
|                 : new NearbyImages(options, state.indexedFeatures) |  | ||||||
|             const controls = new Combine([ |  | ||||||
|                 towardsCenter, |  | ||||||
|                 new Combine([ |  | ||||||
|                     new VariableUiElement( |  | ||||||
|                         radius.GetValue().map((radius) => t.withinRadius.Subs({ radius })) |  | ||||||
|                     ), |  | ||||||
|                     radius, |  | ||||||
|                 ]).SetClass("flex justify-between"), |  | ||||||
|             ]).SetClass("flex flex-col") |  | ||||||
|             return new Combine([ |  | ||||||
|                 slideshow, |  | ||||||
|                 controls, |  | ||||||
|                 saveButton, |  | ||||||
|                 new MapillaryLinkVis().constr(state, tagSource, [], feature).SetClass("mt-6"), |  | ||||||
|             ]) |  | ||||||
|         }) |  | ||||||
| 
 |  | ||||||
|         let withEdit: BaseUIElement = nearby |  | ||||||
|         if (canBeEdited) { |  | ||||||
|             withEdit = new Combine([t.hasMatchingPicture, nearby]).SetClass("flex flex-col") |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (mode === "open") { |  | ||||||
|             return withEdit |  | ||||||
|         } |  | ||||||
|         const toggleState = new UIEventSource<boolean>(mode === "collapsable") |  | ||||||
|         return new Toggle( |  | ||||||
|             new Combine([new Title(t.title), withEdit]), |  | ||||||
|             new Title(t.browseNearby).onClick(() => toggleState.setData(true)), |  | ||||||
|             toggleState |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,70 +0,0 @@ | ||||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" |  | ||||||
| import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" |  | ||||||
| import { VariableUiElement } from "../Base/VariableUIElement" |  | ||||||
| import BaseUIElement from "../BaseUIElement" |  | ||||||
| import EditableTagRendering from "./EditableTagRendering" |  | ||||||
| import Combine from "../Base/Combine" |  | ||||||
| import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization" |  | ||||||
| 
 |  | ||||||
| export class StealViz implements SpecialVisualization { |  | ||||||
|     funcName = "steal" |  | ||||||
|     docs = "Shows a tagRendering from a different object as if this was the object itself" |  | ||||||
|     args = [ |  | ||||||
|         { |  | ||||||
|             name: "featureId", |  | ||||||
|             doc: "The key of the attribute which contains the id of the feature from which to use the tags", |  | ||||||
|             required: true, |  | ||||||
|         }, |  | ||||||
|         { |  | ||||||
|             name: "tagRenderingId", |  | ||||||
|             doc: "The layer-id and tagRenderingId to render. Can be multiple value if ';'-separated (in which case every value must also contain the layerId, e.g. `layerId.tagRendering0; layerId.tagRendering1`). Note: this can cause layer injection", |  | ||||||
|             required: true, |  | ||||||
|         }, |  | ||||||
|     ] |  | ||||||
|     constr(state: SpecialVisualizationState, featureTags, args) { |  | ||||||
|         const [featureIdKey, layerAndtagRenderingIds] = args |  | ||||||
|         const tagRenderings: [LayerConfig, TagRenderingConfig][] = [] |  | ||||||
|         for (const layerAndTagRenderingId of layerAndtagRenderingIds.split(";")) { |  | ||||||
|             const [layerId, tagRenderingId] = layerAndTagRenderingId.trim().split(".") |  | ||||||
|             const layer = state.layout.layers.find((l) => l.id === layerId) |  | ||||||
|             const tagRendering = layer.tagRenderings.find((tr) => tr.id === tagRenderingId) |  | ||||||
|             tagRenderings.push([layer, tagRendering]) |  | ||||||
|         } |  | ||||||
|         if (tagRenderings.length === 0) { |  | ||||||
|             throw "Could not create stolen tagrenddering: tagRenderings not found" |  | ||||||
|         } |  | ||||||
|         return new VariableUiElement( |  | ||||||
|             featureTags.map((tags) => { |  | ||||||
|                 const featureId = tags[featureIdKey] |  | ||||||
|                 if (featureId === undefined) { |  | ||||||
|                     return undefined |  | ||||||
|                 } |  | ||||||
|                 const otherTags = state.featureProperties.getStore(featureId) |  | ||||||
|                 const elements: BaseUIElement[] = [] |  | ||||||
|                 for (const [layer, tagRendering] of tagRenderings) { |  | ||||||
|                     const el = new EditableTagRendering( |  | ||||||
|                         otherTags, |  | ||||||
|                         tagRendering, |  | ||||||
|                         layer.units, |  | ||||||
|                         state, |  | ||||||
|                         {} |  | ||||||
|                     ) |  | ||||||
|                     elements.push(el) |  | ||||||
|                 } |  | ||||||
|                 if (elements.length === 1) { |  | ||||||
|                     return elements[0] |  | ||||||
|                 } |  | ||||||
|                 return new Combine(elements).SetClass("flex flex-col") |  | ||||||
|             }) |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     getLayerDependencies(args): string[] { |  | ||||||
|         const [_, tagRenderingId] = args |  | ||||||
|         if (tagRenderingId.indexOf(".") < 0) { |  | ||||||
|             throw "Error: argument 'layerId.tagRenderingId' of special visualisation 'steal' should contain a dot" |  | ||||||
|         } |  | ||||||
|         const [layerId, __] = tagRenderingId.split(".") |  | ||||||
|         return [layerId] |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,12 +1,7 @@ | ||||||
| import { Store, UIEventSource } from "../Logic/UIEventSource" | import { Store, UIEventSource } from "../Logic/UIEventSource" | ||||||
| import BaseUIElement from "./BaseUIElement" | import BaseUIElement from "./BaseUIElement" | ||||||
| import { DefaultGuiState } from "./DefaultGuiState" |  | ||||||
| import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" | import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" | ||||||
| import { | import { IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource" | ||||||
|     FeatureSource, |  | ||||||
|     IndexedFeatureSource, |  | ||||||
|     WritableFeatureSource, |  | ||||||
| } from "../Logic/FeatureSource/FeatureSource" |  | ||||||
| import { OsmConnection } from "../Logic/Osm/OsmConnection" | import { OsmConnection } from "../Logic/Osm/OsmConnection" | ||||||
| import { Changes } from "../Logic/Osm/Changes" | import { Changes } from "../Logic/Osm/Changes" | ||||||
| import { MapProperties } from "../Models/MapProperties" | import { MapProperties } from "../Models/MapProperties" | ||||||
|  | @ -17,7 +12,6 @@ import { MangroveIdentity } from "../Logic/Web/MangroveReviews" | ||||||
| import { GeoIndexedStoreForLayer } from "../Logic/FeatureSource/Actors/GeoIndexedStore" | import { GeoIndexedStoreForLayer } from "../Logic/FeatureSource/Actors/GeoIndexedStore" | ||||||
| import LayerConfig from "../Models/ThemeConfig/LayerConfig" | import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||||
| import FeatureSwitchState from "../Logic/State/FeatureSwitchState" | import FeatureSwitchState from "../Logic/State/FeatureSwitchState" | ||||||
| import SimpleFeatureSource from "../Logic/FeatureSource/Sources/SimpleFeatureSource" |  | ||||||
| import { MenuState } from "../Models/MenuState" | import { MenuState } from "../Models/MenuState" | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ import { | ||||||
|     SpecialVisualizationState, |     SpecialVisualizationState, | ||||||
| } from "./SpecialVisualization" | } from "./SpecialVisualization" | ||||||
| import { HistogramViz } from "./Popup/HistogramViz" | import { HistogramViz } from "./Popup/HistogramViz" | ||||||
| import { StealViz } from "./Popup/StealViz" |  | ||||||
| import { MinimapViz } from "./Popup/MinimapViz" | import { MinimapViz } from "./Popup/MinimapViz" | ||||||
| import { ShareLinkViz } from "./Popup/ShareLinkViz" | import { ShareLinkViz } from "./Popup/ShareLinkViz" | ||||||
| import { UploadToOsmViz } from "./Popup/UploadToOsmViz" | import { UploadToOsmViz } from "./Popup/UploadToOsmViz" | ||||||
|  | @ -20,7 +19,6 @@ import { PlantNetDetectionViz } from "./Popup/PlantNetDetectionViz" | ||||||
| import { ConflateButton, ImportPointButton, ImportWayButton } from "./Popup/ImportButton" | import { ConflateButton, ImportPointButton, ImportWayButton } from "./Popup/ImportButton" | ||||||
| import TagApplyButton from "./Popup/TagApplyButton" | import TagApplyButton from "./Popup/TagApplyButton" | ||||||
| import { CloseNoteButton } from "./Popup/CloseNoteButton" | import { CloseNoteButton } from "./Popup/CloseNoteButton" | ||||||
| import { NearbyImageVis } from "./Popup/NearbyImageVis" |  | ||||||
| import { MapillaryLinkVis } from "./Popup/MapillaryLinkVis" | import { MapillaryLinkVis } from "./Popup/MapillaryLinkVis" | ||||||
| import { Stores, UIEventSource } from "../Logic/UIEventSource" | import { Stores, UIEventSource } from "../Logic/UIEventSource" | ||||||
| import AllTagsPanel from "./Popup/AllTagsPanel.svelte" | import AllTagsPanel from "./Popup/AllTagsPanel.svelte" | ||||||
|  | @ -63,7 +61,218 @@ import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte" | ||||||
| import UserProfile from "./BigComponents/UserProfile.svelte" | import UserProfile from "./BigComponents/UserProfile.svelte" | ||||||
| import LanguagePicker from "./LanguagePicker" | import LanguagePicker from "./LanguagePicker" | ||||||
| import Link from "./Base/Link" | import Link from "./Base/Link" | ||||||
|  | import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||||
|  | import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" | ||||||
|  | import EditableTagRendering from "./Popup/EditableTagRendering" | ||||||
|  | import NearbyImages, { | ||||||
|  |     NearbyImageOptions, | ||||||
|  |     P4CPicture, | ||||||
|  |     SelectOneNearbyImage, | ||||||
|  | } from "./Popup/NearbyImages" | ||||||
|  | import { Tag } from "../Logic/Tags/Tag" | ||||||
|  | import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction" | ||||||
|  | import { And } from "../Logic/Tags/And" | ||||||
|  | import { SaveButton } from "./Popup/SaveButton" | ||||||
|  | import Lazy from "./Base/Lazy" | ||||||
|  | import { CheckBox } from "./Input/Checkboxes" | ||||||
|  | import Slider from "./Input/Slider" | ||||||
| 
 | 
 | ||||||
|  | class NearbyImageVis implements SpecialVisualization { | ||||||
|  |     // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
 | ||||||
|  |     args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [ | ||||||
|  |         { | ||||||
|  |             name: "mode", | ||||||
|  |             defaultValue: "expandable", | ||||||
|  |             doc: "Indicates how this component is initialized. Options are: \n\n- `open`: always show and load the pictures\n- `collapsable`: show the pictures, but a user can collapse them\n- `expandable`: shown by default; but a user can collapse them.", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             name: "mapillary", | ||||||
|  |             defaultValue: "true", | ||||||
|  |             doc: "If 'true', includes a link to mapillary on this location.", | ||||||
|  |         }, | ||||||
|  |     ] | ||||||
|  |     docs = | ||||||
|  |         "A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature" | ||||||
|  |     funcName = "nearby_images" | ||||||
|  | 
 | ||||||
|  |     constr( | ||||||
|  |         state: SpecialVisualizationState, | ||||||
|  |         tagSource: UIEventSource<Record<string, string>>, | ||||||
|  |         args: string[], | ||||||
|  |         feature: Feature, | ||||||
|  |         layer: LayerConfig | ||||||
|  |     ): BaseUIElement { | ||||||
|  |         const t = Translations.t.image.nearbyPictures | ||||||
|  |         const mode: "open" | "expandable" | "collapsable" = <any>args[0] | ||||||
|  |         const [lon, lat] = GeoOperations.centerpointCoordinates(feature) | ||||||
|  |         const id: string = tagSource.data["id"] | ||||||
|  |         const canBeEdited: boolean = !!id?.match("(node|way|relation)/-?[0-9]+") | ||||||
|  |         const selectedImage = new UIEventSource<P4CPicture>(undefined) | ||||||
|  | 
 | ||||||
|  |         let saveButton: BaseUIElement = undefined | ||||||
|  |         if (canBeEdited) { | ||||||
|  |             const confirmText: BaseUIElement = new SubstitutedTranslation( | ||||||
|  |                 t.confirm, | ||||||
|  |                 tagSource, | ||||||
|  |                 state | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             const onSave = async () => { | ||||||
|  |                 console.log("Selected a picture...", selectedImage.data) | ||||||
|  |                 const osmTags = selectedImage.data.osmTags | ||||||
|  |                 const tags: Tag[] = [] | ||||||
|  |                 for (const key in osmTags) { | ||||||
|  |                     tags.push(new Tag(key, osmTags[key])) | ||||||
|  |                 } | ||||||
|  |                 await state?.changes?.applyAction( | ||||||
|  |                     new ChangeTagAction(id, new And(tags), tagSource.data, { | ||||||
|  |                         theme: state?.layout.id, | ||||||
|  |                         changeType: "link-image", | ||||||
|  |                     }) | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |             saveButton = new SaveButton( | ||||||
|  |                 selectedImage, | ||||||
|  |                 state.osmConnection, | ||||||
|  |                 confirmText, | ||||||
|  |                 t.noImageSelected | ||||||
|  |             ) | ||||||
|  |                 .onClick(onSave) | ||||||
|  |                 .SetClass("flex justify-end") | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const nearby = new Lazy(() => { | ||||||
|  |             const towardsCenter = new CheckBox(t.onlyTowards, false) | ||||||
|  | 
 | ||||||
|  |             const maxSearchRadius = 100 | ||||||
|  |             const stepSize = 10 | ||||||
|  |             const defaultValue = Math.floor(maxSearchRadius / (2 * stepSize)) * stepSize | ||||||
|  |             const fromOsmPreferences = state?.osmConnection | ||||||
|  |                 ?.GetPreference("nearby-images-radius", "" + defaultValue) | ||||||
|  |                 .sync( | ||||||
|  |                     (s) => Number(s), | ||||||
|  |                     [], | ||||||
|  |                     (i) => "" + i | ||||||
|  |                 ) | ||||||
|  |             const radiusValue = new UIEventSource(fromOsmPreferences.data) | ||||||
|  |             radiusValue.addCallbackAndRunD((v) => fromOsmPreferences.setData(v)) | ||||||
|  | 
 | ||||||
|  |             const radius = new Slider(stepSize, maxSearchRadius, { | ||||||
|  |                 value: radiusValue, | ||||||
|  |                 step: 10, | ||||||
|  |             }) | ||||||
|  |             const alreadyInTheImage = AllImageProviders.LoadImagesFor(tagSource) | ||||||
|  |             const options: NearbyImageOptions & { value } = { | ||||||
|  |                 lon, | ||||||
|  |                 lat, | ||||||
|  |                 searchRadius: maxSearchRadius, | ||||||
|  |                 shownRadius: radius.GetValue(), | ||||||
|  |                 value: selectedImage, | ||||||
|  |                 blacklist: alreadyInTheImage, | ||||||
|  |                 towardscenter: towardsCenter.GetValue(), | ||||||
|  |                 maxDaysOld: 365 * 3, | ||||||
|  |             } | ||||||
|  |             const slideshow = canBeEdited | ||||||
|  |                 ? new SelectOneNearbyImage(options, state.indexedFeatures) | ||||||
|  |                 : new NearbyImages(options, state.indexedFeatures) | ||||||
|  |             const controls = new Combine([ | ||||||
|  |                 towardsCenter, | ||||||
|  |                 new Combine([ | ||||||
|  |                     new VariableUiElement( | ||||||
|  |                         radius.GetValue().map((radius) => t.withinRadius.Subs({ radius })) | ||||||
|  |                     ), | ||||||
|  |                     radius, | ||||||
|  |                 ]).SetClass("flex justify-between"), | ||||||
|  |             ]).SetClass("flex flex-col") | ||||||
|  |             return new Combine([ | ||||||
|  |                 slideshow, | ||||||
|  |                 controls, | ||||||
|  |                 saveButton, | ||||||
|  |                 new MapillaryLinkVis().constr(state, tagSource, [], feature).SetClass("mt-6"), | ||||||
|  |             ]) | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         let withEdit: BaseUIElement = nearby | ||||||
|  |         if (canBeEdited) { | ||||||
|  |             withEdit = new Combine([t.hasMatchingPicture, nearby]).SetClass("flex flex-col") | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (mode === "open") { | ||||||
|  |             return withEdit | ||||||
|  |         } | ||||||
|  |         const toggleState = new UIEventSource<boolean>(mode === "collapsable") | ||||||
|  |         return new Toggle( | ||||||
|  |             new Combine([new Title(t.title), withEdit]), | ||||||
|  |             new Title(t.browseNearby).onClick(() => toggleState.setData(true)), | ||||||
|  |             toggleState | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class StealViz implements SpecialVisualization { | ||||||
|  |     // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
 | ||||||
|  | 
 | ||||||
|  |     funcName = "steal" | ||||||
|  |     docs = "Shows a tagRendering from a different object as if this was the object itself" | ||||||
|  |     args = [ | ||||||
|  |         { | ||||||
|  |             name: "featureId", | ||||||
|  |             doc: "The key of the attribute which contains the id of the feature from which to use the tags", | ||||||
|  |             required: true, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             name: "tagRenderingId", | ||||||
|  |             doc: "The layer-id and tagRenderingId to render. Can be multiple value if ';'-separated (in which case every value must also contain the layerId, e.g. `layerId.tagRendering0; layerId.tagRendering1`). Note: this can cause layer injection", | ||||||
|  |             required: true, | ||||||
|  |         }, | ||||||
|  |     ] | ||||||
|  |     constr(state: SpecialVisualizationState, featureTags, args) { | ||||||
|  |         const [featureIdKey, layerAndtagRenderingIds] = args | ||||||
|  |         const tagRenderings: [LayerConfig, TagRenderingConfig][] = [] | ||||||
|  |         for (const layerAndTagRenderingId of layerAndtagRenderingIds.split(";")) { | ||||||
|  |             const [layerId, tagRenderingId] = layerAndTagRenderingId.trim().split(".") | ||||||
|  |             const layer = state.layout.layers.find((l) => l.id === layerId) | ||||||
|  |             const tagRendering = layer.tagRenderings.find((tr) => tr.id === tagRenderingId) | ||||||
|  |             tagRenderings.push([layer, tagRendering]) | ||||||
|  |         } | ||||||
|  |         if (tagRenderings.length === 0) { | ||||||
|  |             throw "Could not create stolen tagrenddering: tagRenderings not found" | ||||||
|  |         } | ||||||
|  |         return new VariableUiElement( | ||||||
|  |             featureTags.map((tags) => { | ||||||
|  |                 const featureId = tags[featureIdKey] | ||||||
|  |                 if (featureId === undefined) { | ||||||
|  |                     return undefined | ||||||
|  |                 } | ||||||
|  |                 const otherTags = state.featureProperties.getStore(featureId) | ||||||
|  |                 const elements: BaseUIElement[] = [] | ||||||
|  |                 for (const [layer, tagRendering] of tagRenderings) { | ||||||
|  |                     const el = new EditableTagRendering( | ||||||
|  |                         otherTags, | ||||||
|  |                         tagRendering, | ||||||
|  |                         layer.units, | ||||||
|  |                         state, | ||||||
|  |                         {} | ||||||
|  |                     ) | ||||||
|  |                     elements.push(el) | ||||||
|  |                 } | ||||||
|  |                 if (elements.length === 1) { | ||||||
|  |                     return elements[0] | ||||||
|  |                 } | ||||||
|  |                 return new Combine(elements).SetClass("flex flex-col") | ||||||
|  |             }) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getLayerDependencies(args): string[] { | ||||||
|  |         const [_, tagRenderingId] = args | ||||||
|  |         if (tagRenderingId.indexOf(".") < 0) { | ||||||
|  |             throw "Error: argument 'layerId.tagRenderingId' of special visualisation 'steal' should contain a dot" | ||||||
|  |         } | ||||||
|  |         const [layerId, __] = tagRenderingId.split(".") | ||||||
|  |         return [layerId] | ||||||
|  |     } | ||||||
|  | } | ||||||
| export default class SpecialVisualizations { | export default class SpecialVisualizations { | ||||||
|     public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.initList() |     public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.initList() | ||||||
| 
 | 
 | ||||||
|  | @ -987,8 +1196,7 @@ export default class SpecialVisualizations { | ||||||
|                 constr( |                 constr( | ||||||
|                     state: SpecialVisualizationState, |                     state: SpecialVisualizationState, | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     args: string[], |                     args: string[] | ||||||
|                     feature: Feature |  | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const [text, href] = args |                     const [text, href] = args | ||||||
|                     return new VariableUiElement( |                     return new VariableUiElement( | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue