Refactoring: port AutoApplyButton to svelte

This commit is contained in:
Pieter Vander Vennet 2025-06-02 17:03:07 +02:00
parent ab88afc397
commit 56020adc2f
8 changed files with 318 additions and 353 deletions

View file

@ -3,6 +3,10 @@ import Translations from "../i18n/Translations"
import BaseUIElement from "../BaseUIElement" import BaseUIElement from "../BaseUIElement"
import SvelteUIElement from "./SvelteUIElement" import SvelteUIElement from "./SvelteUIElement"
import { default as LoadingSvg } from "../../assets/svg/Loading.svelte" import { default as LoadingSvg } from "../../assets/svg/Loading.svelte"
/**
* @deprecated
*/
export default class Loading extends Combine { export default class Loading extends Combine {
constructor(msg?: BaseUIElement | string) { constructor(msg?: BaseUIElement | string) {
const t = Translations.W(msg) ?? Translations.t.general.loading const t = Translations.W(msg) ?? Translations.t.general.loading

View file

@ -0,0 +1,179 @@
<script lang="ts">
import Loading from "../Base/Loading.svelte"
import MaplibreMap from "../Map/MaplibreMap.svelte"
import { Utils } from "../../Utils"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import FilteredLayer from "../../Models/FilteredLayer"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { MapLibreAdaptor } from "../Map/MapLibreAdaptor"
import ShowDataLayer from "../Map/ShowDataLayer"
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
import SpecialVisualizations from "../SpecialVisualizations"
import type { AutoAction } from "./AutoApplyButtonVis"
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations"
/**
* The ids to handle. Might be data from an external dataset, we cannot assume an OSM-id
*/
export let ids: Store<string[] | undefined>
export let state: SpecialVisualizationState
export let options: {
target_layer_id: string,
targetTagRendering: string,
text: string,
icon: string,
}
let buttonState: UIEventSource<
"idle" | "running" | "done" | { error: string }
> = new UIEventSource<
"idle" | "running" | "done" | { error: string }
>("idle")
let tagRenderingConfig: TagRenderingConfig
let appliedNumberOfFeatures = new UIEventSource<number>(0)
let layer: FilteredLayer = state.layerState.filteredLayers.get(options.target_layer_id)
tagRenderingConfig = layer.layerDef.tagRenderings.find(
(tr) => tr.id === options.targetTagRendering
)
const mlmap = new UIEventSource(undefined)
const mla = new MapLibreAdaptor(mlmap, {
rasterLayer: state.mapProperties.rasterLayer
})
mla.allowZooming.setData(false)
mla.allowMoving.setData(false)
const features = ids.mapD(ids => ids.map((id) =>
state.indexedFeatures.featuresById.data.get(id)
))
new ShowDataLayer(mlmap, {
features: StaticFeatureSource.fromGeojson(features),
zoomToFeatures: true,
layer: layer.layerDef
})
features.addCallbackAndRunD(f => console.log("Features are now", f))
async function applyAllChanges() {
buttonState.set("running")
try {
const target_feature_ids = ids.data
console.log("Applying auto-action on " + target_feature_ids.length + " features")
const appliedOn: string[] = []
for (let i = 0; i < target_feature_ids.length; i++) {
const targetFeatureId = target_feature_ids[i]
console.log("Handling", targetFeatureId)
const feature = state.indexedFeatures.featuresById.data.get(targetFeatureId)
const featureTags = state.featureProperties.getStore(targetFeatureId)
const rendering = tagRenderingConfig.GetRenderValue(featureTags.data).txt
const specialRenderings = Utils.NoNull(
SpecialVisualizations.constructSpecification(rendering)
).filter((v) => typeof v !== "string" && v.func["supportsAutoAction"] === true)
if (specialRenderings.length == 0) {
console.warn(
"AutoApply: feature " +
targetFeatureId +
" got a rendering without supported auto actions:",
rendering
)
}
for (const specialRendering of specialRenderings) {
if (typeof specialRendering === "string") {
continue
}
const action = <AutoAction>specialRendering.func
await action.applyActionOn(
feature,
state,
featureTags,
specialRendering.args
)
}
appliedOn.push(targetFeatureId)
if (i % 50 === 0) {
await state.changes.flushChanges("Auto button: intermediate save")
}
appliedNumberOfFeatures.setData(i + 1)
}
console.log("Flushing changes...")
await state.changes.flushChanges("Auto button: done")
buttonState.setData("done")
console.log(
"Applied changes onto",
appliedOn.length,
"items, unique IDs:",
new Set(appliedOn).size
)
} catch (e) {
console.error("Error while running autoApply: ", e)
buttonState.setData({ error: e })
}
}
const t = Translations.t.general.add.import
</script>
{#if !state.theme.official && !state.featureSwitchIsTesting.data}
<div class="alert">The auto-apply button is only available in official themes (or in testing mode)</div>
<Tr t={t.howToTest} />
{:else if ids === undefined}
<Loading>Gathering which elements support auto-apply...</Loading>
{:else if tagRenderingConfig === undefined}
<div class="alert">Target tagrendering {options.targetTagRendering} not found"</div>
{:else if $ids.length === 0}
<div>No elements found to perform action</div>
{:else if $buttonState.error !== undefined}
<div class="flex flex-col">
<div class="alert">Something went wrong</div>
<div>{$buttonState.error}</div>
</div>
{:else if $buttonState === "done"}
<div class="thanks">All done!</div>
{:else if $buttonState === "running"}
<Loading>
Applying changes, currently at {$appliedNumberOfFeatures} / {$ids.length}
</Loading>
{:else if $buttonState === "idle"}
<div class="flex flex-col">
<button on:click={() => {applyAllChanges()}}>
<img class="h-8 w-8" alt="" src={options.icon} />
{options.text}
</button>
<div class="h-48 w-full">
<MaplibreMap mapProperties={mla} map={mlmap} />
</div>
<div class="subtle link-underline">
The following objects will be updated:
<div class="flex flex-wrap gap-x-2">
{#each $ids as featId}
{#if layer.layerDef.source.geojsonSource === undefined}
<a href={"https://openstreetmap.org/" + featId} target="_blank">{featId}</a>
{:else}
<div>
{featId}
</div>
{/if}
{/each}
</div>
</div>
</div>
{:else}
<div>Not supposed to show this... AutoApplyButton has invalid buttonstate: {$buttonState}</div>
{/if}

View file

@ -1,341 +0,0 @@
import BaseUIElement from "../BaseUIElement"
import { Stores, UIEventSource } from "../../Logic/UIEventSource"
import { SubtleButton } from "../Base/SubtleButton"
import Img from "../Base/Img"
import { FixedUiElement } from "../Base/FixedUiElement"
import Combine from "../Base/Combine"
import Link from "../Base/Link"
import { Utils } from "../../Utils"
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
import { VariableUiElement } from "../Base/VariableUIElement"
import Loading from "../Base/Loading"
import Translations from "../i18n/Translations"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../Logic/Osm/Changes"
import { UIElement } from "../UIElement"
import FilteredLayer from "../../Models/FilteredLayer"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import Lazy from "../Base/Lazy"
import { SpecialVisualization, SpecialVisualizationState } from "../SpecialVisualization"
import { IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource"
import { MapLibreAdaptor } from "../Map/MapLibreAdaptor"
import ShowDataLayer from "../Map/ShowDataLayer"
import SvelteUIElement from "../Base/SvelteUIElement"
import MaplibreMap from "../Map/MaplibreMap.svelte"
import SpecialVisualizations from "../SpecialVisualizations"
import { Feature } from "geojson"
export interface AutoAction extends SpecialVisualization {
supportsAutoAction: boolean
applyActionOn(
feature: Feature,
state: {
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
},
tagSource: UIEventSource<any>,
argument: string[]
): Promise<void>
}
/**
* @deprecated
*/
class ApplyButton extends UIElement {
private readonly icon: string
private readonly text: string
private readonly targetTagRendering: string
private readonly target_layer_id: string
private readonly state: SpecialVisualizationState
private readonly target_feature_ids: string[]
private readonly buttonState = new UIEventSource<
"idle" | "running" | "done" | { error: string }
>("idle")
private readonly layer: FilteredLayer
private readonly tagRenderingConfig: TagRenderingConfig
private readonly appliedNumberOfFeatures = new UIEventSource<number>(0)
constructor(
state: SpecialVisualizationState,
target_feature_ids: string[],
options: {
target_layer_id: string
targetTagRendering: string
text: string
icon: string
}
) {
super()
this.state = state
this.target_feature_ids = target_feature_ids
this.target_layer_id = options.target_layer_id
this.targetTagRendering = options.targetTagRendering
this.text = options.text
this.icon = options.icon
this.layer = this.state.layerState.filteredLayers.get(this.target_layer_id)
this.tagRenderingConfig = this.layer.layerDef.tagRenderings.find(
(tr) => tr.id === this.targetTagRendering
)
}
protected InnerRender(): string | BaseUIElement {
if (this.target_feature_ids.length === 0) {
return new FixedUiElement("No elements found to perform action")
}
if (this.tagRenderingConfig === undefined) {
return new FixedUiElement(
"Target tagrendering " + this.targetTagRendering + " not found"
).SetClass("alert")
}
const self = this
const button = new SubtleButton(new Img(this.icon), this.text).onClick(() => {
this.buttonState.setData("running")
window.setTimeout(() => {
self.Run()
}, 50)
})
const explanation = new Combine([
"The following objects will be updated: ",
...this.target_feature_ids.map(
(id) => new Combine([new Link(id, "https:/ /openstreetmap.org/" + id, true), ", "])
),
]).SetClass("subtle")
const mlmap = new UIEventSource(undefined)
const mla = new MapLibreAdaptor(mlmap, {
rasterLayer: this.state.mapProperties.rasterLayer,
})
mla.allowZooming.setData(false)
mla.allowMoving.setData(false)
const previewMap = new SvelteUIElement(MaplibreMap, {
mapProperties: mla,
map: mlmap,
}).SetClass("h-48")
const features = this.target_feature_ids.map((id) =>
this.state.indexedFeatures.featuresById.data.get(id)
)
new ShowDataLayer(mlmap, {
features: StaticFeatureSource.fromGeojson(features),
zoomToFeatures: true,
layer: this.layer.layerDef,
})
return new VariableUiElement(
this.buttonState.map((st) => {
if (st === "idle") {
return new Combine([button, previewMap, explanation])
}
if (st === "done") {
return new FixedUiElement("All done!").SetClass("thanks")
}
if (st === "running") {
return new Loading(
new VariableUiElement(
this.appliedNumberOfFeatures.map((appliedTo) => {
return (
"Applying changes, currently at " +
appliedTo +
"/" +
this.target_feature_ids.length
)
})
)
)
}
const error = st.error
return new Combine([
new FixedUiElement("Something went wrong...").SetClass("alert"),
new FixedUiElement(error).SetClass("subtle"),
]).SetClass("flex flex-col")
})
)
}
/**
* Actually applies all the changes...
*/
private async Run() {
try {
console.log("Applying auto-action on " + this.target_feature_ids.length + " features")
const appliedOn: string[] = []
for (let i = 0; i < this.target_feature_ids.length; i++) {
const targetFeatureId = this.target_feature_ids[i]
const feature = this.state.indexedFeatures.featuresById.data.get(targetFeatureId)
const featureTags = this.state.featureProperties.getStore(targetFeatureId)
const rendering = this.tagRenderingConfig.GetRenderValue(featureTags.data).txt
const specialRenderings = Utils.NoNull(
SpecialVisualizations.constructSpecification(rendering)
).filter((v) => typeof v !== "string" && v.func["supportsAutoAction"] === true)
if (specialRenderings.length == 0) {
console.warn(
"AutoApply: feature " +
targetFeatureId +
" got a rendering without supported auto actions:",
rendering
)
}
for (const specialRendering of specialRenderings) {
if (typeof specialRendering === "string") {
continue
}
const action = <AutoAction>specialRendering.func
await action.applyActionOn(
feature,
this.state,
featureTags,
specialRendering.args
)
}
appliedOn.push(targetFeatureId)
if (i % 50 === 0) {
await this.state.changes.flushChanges("Auto button: intermediate save")
}
this.appliedNumberOfFeatures.setData(i + 1)
}
console.log("Flushing changes...")
await this.state.changes.flushChanges("Auto button: done")
this.buttonState.setData("done")
console.log(
"Applied changes onto",
appliedOn.length,
"items, unique IDs:",
new Set(appliedOn).size
)
} catch (e) {
console.error("Error while running autoApply: ", e)
this.buttonState.setData({ error: e })
}
}
}
export default class AutoApplyButton implements SpecialVisualization {
public readonly docs: string
public readonly funcName: string = "auto_apply"
public readonly needsUrls = []
public readonly args: {
name: string
defaultValue?: string
doc: string
required?: boolean
}[] = [
{
name: "target_layer",
doc: "The layer that the target features will reside in",
required: true,
},
{
name: "target_feature_ids",
doc: "The key, of which the value contains a list of ids",
required: true,
},
{
name: "tag_rendering_id",
doc: "The ID of the tagRendering containing the autoAction. This tagrendering will be calculated. The embedded actions will be executed",
required: true,
},
{
name: "text",
doc: "The text to show on the button",
required: true,
},
{
name: "icon",
doc: "The icon to show on the button",
defaultValue: "./assets/svg/robot.svg",
},
]
constructor(allSpecialVisualisations: SpecialVisualization[]) {
this.docs = AutoApplyButton.generateDocs(
allSpecialVisualisations
.filter((sv) => sv["supportsAutoAction"] === true)
.map((sv) => sv.funcName)
)
}
private static generateDocs(supportedActions: string[]): string {
return `
A button to run many actions for many features at once.
To effectively use this button, you'll need some ingredients:
1. A target layer with features for which an action is defined in a tag rendering. The following special visualisations support an autoAction: ${supportedActions.join(
", "
)}
2. A host feature to place the auto-action on. This can be a big outline (such as a city). Another good option for this is the layer [current_view](./BuiltinLayers.md#current_view)
3. Then, use a calculated tag on the host feature to determine the overlapping object ids
4. At last, add this component`
}
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
argument: string[]
): BaseUIElement {
try {
if (!state.theme.official && !state.featureSwitchIsTesting.data) {
const t = Translations.t.general.add.import
return new Combine([
new FixedUiElement(
"The auto-apply button is only available in official themes (or in testing mode)"
).SetClass("alert"),
t.howToTest,
])
}
const target_layer_id = argument[0]
const targetTagRendering = argument[2]
const text = argument[3]
const icon = argument[4]
const options = {
target_layer_id,
targetTagRendering,
text,
icon,
}
return new Lazy(() => {
const to_parse = new UIEventSource<string[]>(undefined)
// Very ugly hack: read the value every 500ms
Stores.Chronic(500, () => to_parse.data === undefined).addCallback(() => {
let applicable = <string | string[]>tagSource.data[argument[1]]
if (typeof applicable === "string") {
applicable = JSON.parse(applicable)
}
to_parse.setData(<string[]>applicable)
})
const loading = new Loading("Gathering which elements support auto-apply... ")
return new VariableUiElement(
Stores.ListStabilized(to_parse).map((ids) => {
if (ids === undefined) {
return loading
}
if (typeof ids === "string") {
ids = JSON.parse(ids)
}
return new ApplyButton(state, ids, options)
})
)
})
} catch (e) {
return new FixedUiElement(
"Could not generate a auto_apply-button for key " + argument[0] + " due to " + e
).SetClass("alert")
}
}
getLayerDependencies(args: string[]): string[] {
return [args[0]]
}
}

View file

@ -0,0 +1,124 @@
import { Store, Stores, UIEventSource } from "../../Logic/UIEventSource"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { Changes } from "../../Logic/Osm/Changes"
import { SpecialVisualization, SpecialVisualizationState, SpecialVisualizationSvelte } from "../SpecialVisualization"
import { IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource"
import SvelteUIElement from "../Base/SvelteUIElement"
import { Feature } from "geojson"
import AutoApplyButton from "./AutoApplyButton.svelte"
export interface AutoAction extends SpecialVisualization {
supportsAutoAction: boolean
applyActionOn(
feature: Feature,
state: {
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
},
tagSource: UIEventSource<Record<string, string>>,
argument: string[]
): Promise<void>
}
export default class AutoApplyButtonVis implements SpecialVisualizationSvelte {
public readonly docs: string
public readonly funcName: string = "auto_apply"
public readonly needsUrls = []
public readonly group = "import"
public readonly args: {
name: string
defaultValue?: string
doc: string
required?: boolean
}[] = [
{
name: "target_layer",
doc: "The layer that the target features will reside in",
required: true,
},
{
name: "target_feature_ids",
doc: "The key, of which the value contains a list of ids",
required: true,
},
{
name: "tag_rendering_id",
doc: "The ID of the tagRendering containing the autoAction. This tagrendering will be calculated. The embedded actions will be executed",
required: true,
},
{
name: "text",
doc: "The text to show on the button",
required: true,
},
{
name: "icon",
doc: "The icon to show on the button",
defaultValue: "./assets/svg/robot.svg",
},
]
constructor(allSpecialVisualisations: SpecialVisualization[]) {
this.docs = AutoApplyButtonVis.generateDocs(
allSpecialVisualisations
.filter((sv) => sv["supportsAutoAction"] === true)
.map((sv) => sv.funcName)
)
}
private static generateDocs(supportedActions: string[]): string {
return `
A button to run many actions for many features at once.
To effectively use this button, you'll need some ingredients:
1. A target layer with features for which an action is defined in a tag rendering. The following special visualisations support an autoAction: ${supportedActions.join(
", "
)}
2. A host feature to place the auto-action on. This can be a big outline (such as a city). Another good option for this is the layer [current_view](./BuiltinLayers.md#current_view)
3. Then, use a calculated tag on the host feature to determine the overlapping object ids
4. At last, add this component`
}
constr(
state: SpecialVisualizationState,
tagSource: UIEventSource<Record<string, string>>,
argument: string[]
): SvelteUIElement {
const target_layer_id = argument[0]
const targetTagRendering = argument[2]
const text = argument[3]
const icon = argument[4]
const options = {
target_layer_id,
targetTagRendering,
text,
icon
}
const to_parse: UIEventSource<string[]> = new UIEventSource<string[]>(undefined)
Stores.Chronic(500, () => to_parse.data === undefined).map(() => {
const applicable = <string | string[]>tagSource.data[argument[1]]
if (typeof applicable === "string") {
return <string[]>JSON.parse(applicable)
} else {
return applicable
}
}).addCallbackAndRunD(data => {
to_parse.set(data)
})
const stableIds: Store<string[]> = Stores.ListStabilized(to_parse).map((ids) => {
if (typeof ids === "string") {
ids = JSON.parse(ids)
}
return ids.map(id => id)
})
return new SvelteUIElement(AutoApplyButton, { state, ids: stableIds, options })
}
getLayerDependencies(args: string[]): string[] {
return [args[0]]
}
}

View file

@ -8,7 +8,7 @@ import { Utils } from "../../../Utils"
import SvelteUIElement from "../../Base/SvelteUIElement" import SvelteUIElement from "../../Base/SvelteUIElement"
import WayImportFlow from "./WayImportFlow.svelte" import WayImportFlow from "./WayImportFlow.svelte"
import ConflateImportFlowState from "./ConflateImportFlowState" import ConflateImportFlowState from "./ConflateImportFlowState"
import { AutoAction } from "../AutoApplyButton" import { AutoAction } from "../AutoApplyButtonVis"
import { IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource" import { IndexedFeatureSource } from "../../../Logic/FeatureSource/FeatureSource"
import { Changes } from "../../../Logic/Osm/Changes" import { Changes } from "../../../Logic/Osm/Changes"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig" import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"

View file

@ -1,5 +1,5 @@
import { SpecialVisualization, SpecialVisualizationState } from "../../SpecialVisualization" import { SpecialVisualization, SpecialVisualizationState } from "../../SpecialVisualization"
import { AutoAction } from "../AutoApplyButton" import { AutoAction } from "../AutoApplyButtonVis"
import { Feature, LineString, Polygon } from "geojson" import { Feature, LineString, Polygon } from "geojson"
import { UIEventSource } from "../../../Logic/UIEventSource" import { UIEventSource } from "../../../Logic/UIEventSource"
import BaseUIElement from "../../BaseUIElement" import BaseUIElement from "../../BaseUIElement"

View file

@ -1,4 +1,4 @@
import { AutoAction } from "./AutoApplyButton" import { AutoAction } from "./AutoApplyButtonVis"
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
import { VariableUiElement } from "../Base/VariableUIElement" import { VariableUiElement } from "../Base/VariableUIElement"
import BaseUIElement from "../BaseUIElement" import BaseUIElement from "../BaseUIElement"

View file

@ -1,11 +1,7 @@
import { FixedUiElement } from "./Base/FixedUiElement" import { FixedUiElement } from "./Base/FixedUiElement"
import BaseUIElement from "./BaseUIElement" import BaseUIElement from "./BaseUIElement"
import { default as FeatureTitle } from "./Popup/Title.svelte" import { default as FeatureTitle } from "./Popup/Title.svelte"
import { import { RenderingSpecification, SpecialVisualization, SpecialVisualizationState } from "./SpecialVisualization"
RenderingSpecification,
SpecialVisualization,
SpecialVisualizationState,
} from "./SpecialVisualization"
import { HistogramViz } from "./Popup/HistogramViz" import { HistogramViz } from "./Popup/HistogramViz"
import { UploadToOsmViz } from "./Popup/UploadToOsmViz" import { UploadToOsmViz } from "./Popup/UploadToOsmViz"
import { MultiApplyViz } from "./Popup/MultiApplyViz" import { MultiApplyViz } from "./Popup/MultiApplyViz"
@ -15,7 +11,7 @@ import { VariableUiElement } from "./Base/VariableUIElement"
import { Translation } from "./i18n/Translation" import { Translation } from "./i18n/Translation"
import Translations from "./i18n/Translations" import Translations from "./i18n/Translations"
import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization" import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization"
import AutoApplyButton from "./Popup/AutoApplyButton" import AutoApplyButtonVis from "./Popup/AutoApplyButtonVis"
import { LanguageElement } from "./Popup/LanguageElement/LanguageElement" import { LanguageElement } from "./Popup/LanguageElement/LanguageElement"
import SvelteUIElement from "./Base/SvelteUIElement" import SvelteUIElement from "./Base/SvelteUIElement"
import { Feature, LineString } from "geojson" import { Feature, LineString } from "geojson"
@ -40,8 +36,11 @@ import { UISpecialVisualisations } from "./SpecialVisualisations/UISpecialVisual
import { SettingsVisualisations } from "./SpecialVisualisations/SettingsVisualisations" import { SettingsVisualisations } from "./SpecialVisualisations/SettingsVisualisations"
import { ReviewSpecialVisualisations } from "./SpecialVisualisations/ReviewSpecialVisualisations" import { ReviewSpecialVisualisations } from "./SpecialVisualisations/ReviewSpecialVisualisations"
import { DataImportSpecialVisualisations } from "./SpecialVisualisations/DataImportSpecialVisualisations" import { DataImportSpecialVisualisations } from "./SpecialVisualisations/DataImportSpecialVisualisations"
import TagrenderingManipulationSpecialVisualisations from "./SpecialVisualisations/TagrenderingManipulationSpecialVisualisations" import TagrenderingManipulationSpecialVisualisations
import { WebAndCommunicationSpecialVisualisations } from "./SpecialVisualisations/WebAndCommunicationSpecialVisualisations" from "./SpecialVisualisations/TagrenderingManipulationSpecialVisualisations"
import {
WebAndCommunicationSpecialVisualisations
} from "./SpecialVisualisations/WebAndCommunicationSpecialVisualisations"
import ClearGPSHistory from "./BigComponents/ClearGPSHistory.svelte" import ClearGPSHistory from "./BigComponents/ClearGPSHistory.svelte"
import AllFeaturesStatistics from "./Statistics/AllFeaturesStatistics.svelte" import AllFeaturesStatistics from "./Statistics/AllFeaturesStatistics.svelte"
@ -609,7 +608,7 @@ export default class SpecialVisualizations {
}, },
] ]
specialVisualizations.push(new AutoApplyButton(specialVisualizations)) specialVisualizations.push(new AutoApplyButtonVis(specialVisualizations))
const regex = /[a-zA-Z_]+/ const regex = /[a-zA-Z_]+/
const invalid = specialVisualizations const invalid = specialVisualizations