forked from MapComplete/MapComplete
Merge branch 'feature/levels' into develop
This commit is contained in:
commit
64d0aac88f
20 changed files with 976 additions and 522 deletions
|
@ -1,10 +1,9 @@
|
||||||
import {UIEventSource} from "../../UIEventSource";
|
import {Store, UIEventSource} from "../../UIEventSource";
|
||||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
import FilteredLayer, {FilterState} from "../../../Models/FilteredLayer";
|
||||||
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
|
||||||
import {BBox} from "../../BBox";
|
import {BBox} from "../../BBox";
|
||||||
import {ElementStorage} from "../../ElementStorage";
|
import {ElementStorage} from "../../ElementStorage";
|
||||||
import {TagsFilter} from "../../Tags/TagsFilter";
|
import {TagsFilter} from "../../Tags/TagsFilter";
|
||||||
import {tag} from "@turf/turf";
|
|
||||||
import {OsmFeature} from "../../../Models/OsmFeature";
|
import {OsmFeature} from "../../../Models/OsmFeature";
|
||||||
|
|
||||||
export default class FilteringFeatureSource implements FeatureSourceForLayer, Tiled {
|
export default class FilteringFeatureSource implements FeatureSourceForLayer, Tiled {
|
||||||
|
@ -16,7 +15,9 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
||||||
public readonly bbox: BBox
|
public readonly bbox: BBox
|
||||||
private readonly upstream: FeatureSourceForLayer;
|
private readonly upstream: FeatureSourceForLayer;
|
||||||
private readonly state: {
|
private readonly state: {
|
||||||
locationControl: UIEventSource<{ zoom: number }>; selectedElement: UIEventSource<any>,
|
locationControl: Store<{ zoom: number }>;
|
||||||
|
selectedElement: Store<any>,
|
||||||
|
globalFilters: Store<{ filter: FilterState }[]>,
|
||||||
allElements: ElementStorage
|
allElements: ElementStorage
|
||||||
};
|
};
|
||||||
private readonly _alreadyRegistered = new Set<UIEventSource<any>>();
|
private readonly _alreadyRegistered = new Set<UIEventSource<any>>();
|
||||||
|
@ -25,9 +26,10 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
state: {
|
state: {
|
||||||
locationControl: UIEventSource<{ zoom: number }>,
|
locationControl: Store<{ zoom: number }>,
|
||||||
selectedElement: UIEventSource<any>,
|
selectedElement: Store<any>,
|
||||||
allElements: ElementStorage
|
allElements: ElementStorage,
|
||||||
|
globalFilters: Store<{ filter: FilterState }[]>
|
||||||
},
|
},
|
||||||
tileIndex,
|
tileIndex,
|
||||||
upstream: FeatureSourceForLayer,
|
upstream: FeatureSourceForLayer,
|
||||||
|
@ -60,6 +62,10 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
||||||
metataggingUpdated?.addCallback(_ => {
|
metataggingUpdated?.addCallback(_ => {
|
||||||
self._is_dirty.setData(true)
|
self._is_dirty.setData(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
state.globalFilters.addCallback(_ => {
|
||||||
|
self.update()
|
||||||
|
})
|
||||||
|
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
@ -69,6 +75,7 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
||||||
const layer = this.upstream.layer;
|
const layer = this.upstream.layer;
|
||||||
const features: { feature: OsmFeature; freshness: Date }[] = (this.upstream.features.data ?? []);
|
const features: { feature: OsmFeature; freshness: Date }[] = (this.upstream.features.data ?? []);
|
||||||
const includedFeatureIds = new Set<string>();
|
const includedFeatureIds = new Set<string>();
|
||||||
|
const globalFilters = self.state.globalFilters.data.map(f => f.filter);
|
||||||
const newFeatures = (features ?? []).filter((f) => {
|
const newFeatures = (features ?? []).filter((f) => {
|
||||||
|
|
||||||
self.registerCallback(f.feature)
|
self.registerCallback(f.feature)
|
||||||
|
@ -88,6 +95,14 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const filter of globalFilters) {
|
||||||
|
const neededTags: TagsFilter = filter?.currentFilter
|
||||||
|
if (neededTags !== undefined && !neededTags.matchesProperties(f.feature.properties)) {
|
||||||
|
// Hidden by the filter on the layer itself - we want to hide it no matter what
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
includedFeatureIds.add(f.feature.properties.id)
|
includedFeatureIds.add(f.feature.properties.id)
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -72,7 +72,7 @@ export default class CreateNewNodeAction extends OsmCreateAction {
|
||||||
this.setElementId(id)
|
this.setElementId(id)
|
||||||
for (const kv of this._basicTags) {
|
for (const kv of this._basicTags) {
|
||||||
if (typeof kv.value !== "string") {
|
if (typeof kv.value !== "string") {
|
||||||
throw "Invalid value: don't use a regex in a preset"
|
throw "Invalid value: don't use non-string value in a preset. The tag "+kv.key+"="+kv.value+" is not a string, the value is a "+typeof kv.value
|
||||||
}
|
}
|
||||||
properties[kv.key] = kv.value;
|
properties[kv.key] = kv.value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {FixedUiElement} from "../UI/Base/FixedUiElement";
|
||||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||||
import {CountryCoder} from "latlon2country"
|
import {CountryCoder} from "latlon2country"
|
||||||
import Constants from "../Models/Constants";
|
import Constants from "../Models/Constants";
|
||||||
|
import {TagUtils} from "./Tags/TagUtils";
|
||||||
|
|
||||||
|
|
||||||
export class SimpleMetaTagger {
|
export class SimpleMetaTagger {
|
||||||
|
@ -32,7 +33,7 @@ export class SimpleMetaTagger {
|
||||||
if (!docs.cleanupRetagger) {
|
if (!docs.cleanupRetagger) {
|
||||||
for (const key of docs.keys) {
|
for (const key of docs.keys) {
|
||||||
if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) {
|
if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) {
|
||||||
throw `Incorrect metakey ${key}: it should start with underscore (_)`
|
throw `Incorrect key for a calculated meta value '${key}': it should start with underscore (_)`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,6 +212,27 @@ export default class SimpleMetaTaggers {
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
private static levels = new SimpleMetaTagger(
|
||||||
|
{
|
||||||
|
doc: "Extract the 'level'-tag into a normalized, ';'-separated value",
|
||||||
|
keys: ["_level"]
|
||||||
|
},
|
||||||
|
((feature) => {
|
||||||
|
if (feature.properties["level"] === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const l = feature.properties["level"]
|
||||||
|
const newValue = TagUtils.LevelsParser(l).join(";")
|
||||||
|
if(l === newValue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
feature.properties["level"] = newValue
|
||||||
|
return true
|
||||||
|
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
private static canonicalize = new SimpleMetaTagger(
|
private static canonicalize = new SimpleMetaTagger(
|
||||||
{
|
{
|
||||||
doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`)",
|
doc: "If 'units' is defined in the layoutConfig, then this metatagger will rewrite the specified keys to have the canonical form (e.g. `1meter` will be rewritten to `1m`)",
|
||||||
|
@ -218,7 +240,7 @@ export default class SimpleMetaTaggers {
|
||||||
|
|
||||||
},
|
},
|
||||||
((feature, _, __, state) => {
|
((feature, _, __, state) => {
|
||||||
const units = Utils.NoNull([].concat(...state?.layoutToUse?.layers?.map(layer => layer.units )?? []));
|
const units = Utils.NoNull([].concat(...state?.layoutToUse?.layers?.map(layer => layer.units) ?? []));
|
||||||
if (units.length == 0) {
|
if (units.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -317,7 +339,7 @@ export default class SimpleMetaTaggers {
|
||||||
country_code: tags._country.toLowerCase(),
|
country_code: tags._country.toLowerCase(),
|
||||||
state: undefined
|
state: undefined
|
||||||
}
|
}
|
||||||
}, <any> {tag_key: "opening_hours"});
|
}, <any>{tag_key: "opening_hours"});
|
||||||
|
|
||||||
// Recalculate!
|
// Recalculate!
|
||||||
return oh.getState() ? "yes" : "no";
|
return oh.getState() ? "yes" : "no";
|
||||||
|
@ -327,12 +349,12 @@ export default class SimpleMetaTaggers {
|
||||||
delete tags._isOpen
|
delete tags._isOpen
|
||||||
tags["_isOpen"] = "parse_error";
|
tags["_isOpen"] = "parse_error";
|
||||||
}
|
}
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const tagsSource = state.allElements.getEventSourceById(feature.properties.id);
|
const tagsSource = state.allElements.getEventSourceById(feature.properties.id);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -400,7 +422,8 @@ export default class SimpleMetaTaggers {
|
||||||
SimpleMetaTaggers.currentTime,
|
SimpleMetaTaggers.currentTime,
|
||||||
SimpleMetaTaggers.objectMetaInfo,
|
SimpleMetaTaggers.objectMetaInfo,
|
||||||
SimpleMetaTaggers.noBothButLeftRight,
|
SimpleMetaTaggers.noBothButLeftRight,
|
||||||
SimpleMetaTaggers.geometryType
|
SimpleMetaTaggers.geometryType,
|
||||||
|
SimpleMetaTaggers.levels
|
||||||
|
|
||||||
];
|
];
|
||||||
public static readonly lazyTags: string[] = [].concat(...SimpleMetaTaggers.metatags.filter(tagger => tagger.isLazy)
|
public static readonly lazyTags: string[] = [].concat(...SimpleMetaTaggers.metatags.filter(tagger => tagger.isLazy)
|
||||||
|
|
|
@ -19,6 +19,19 @@ import TitleHandler from "../Actors/TitleHandler";
|
||||||
import {BBox} from "../BBox";
|
import {BBox} from "../BBox";
|
||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||||
import {TiledStaticFeatureSource} from "../FeatureSource/Sources/StaticFeatureSource";
|
import {TiledStaticFeatureSource} from "../FeatureSource/Sources/StaticFeatureSource";
|
||||||
|
import {Translation, TypedTranslation} from "../../UI/i18n/Translation";
|
||||||
|
import {Tag} from "../Tags/Tag";
|
||||||
|
|
||||||
|
|
||||||
|
export interface GlobalFilter {
|
||||||
|
filter: FilterState,
|
||||||
|
id: string,
|
||||||
|
onNewPoint: {
|
||||||
|
safetyCheck: Translation,
|
||||||
|
confirmAddNew: TypedTranslation<{ preset: Translation }>
|
||||||
|
tags: Tag[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all the leaflet-map related state
|
* Contains all the leaflet-map related state
|
||||||
|
@ -78,6 +91,12 @@ export default class MapState extends UserRelatedState {
|
||||||
* Which layers are enabled in the current theme and what filters are applied onto them
|
* Which layers are enabled in the current theme and what filters are applied onto them
|
||||||
*/
|
*/
|
||||||
public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>([], "filteredLayers");
|
public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>([], "filteredLayers");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters which apply onto all layers
|
||||||
|
*/
|
||||||
|
public globalFilters: UIEventSource<GlobalFilter[]> = new UIEventSource([], "globalFilters")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Which overlays are shown
|
* Which overlays are shown
|
||||||
*/
|
*/
|
||||||
|
@ -121,9 +140,9 @@ export default class MapState extends UserRelatedState {
|
||||||
this.overlayToggles = this.layoutToUse?.tileLayerSources
|
this.overlayToggles = this.layoutToUse?.tileLayerSources
|
||||||
?.filter(c => c.name !== undefined)
|
?.filter(c => c.name !== undefined)
|
||||||
?.map(c => ({
|
?.map(c => ({
|
||||||
config: c,
|
config: c,
|
||||||
isDisplayed: QueryParameters.GetBooleanQueryParameter("overlay-" + c.id, c.defaultState, "Wether or not the overlay " + c.id + " is shown")
|
isDisplayed: QueryParameters.GetBooleanQueryParameter("overlay-" + c.id, c.defaultState, "Wether or not the overlay " + c.id + " is shown")
|
||||||
})) ?? []
|
})) ?? []
|
||||||
this.filteredLayers = this.InitializeFilteredLayers()
|
this.filteredLayers = this.InitializeFilteredLayers()
|
||||||
|
|
||||||
|
|
||||||
|
@ -206,7 +225,7 @@ export default class MapState extends UserRelatedState {
|
||||||
return [feature]
|
return [feature]
|
||||||
})
|
})
|
||||||
|
|
||||||
this.currentView = new TiledStaticFeatureSource(features, currentViewLayer);
|
this.currentView = new TiledStaticFeatureSource(features, currentViewLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private initGpsLocation() {
|
private initGpsLocation() {
|
||||||
|
@ -335,15 +354,15 @@ export default class MapState extends UserRelatedState {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPref(key: string, layer: LayerConfig): UIEventSource<boolean> {
|
private getPref(key: string, layer: LayerConfig): UIEventSource<boolean> {
|
||||||
const pref = this.osmConnection
|
const pref = this.osmConnection
|
||||||
.GetPreference(key)
|
.GetPreference(key)
|
||||||
.sync(v => {
|
.sync(v => {
|
||||||
if(v === undefined){
|
if (v === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return v === "true";
|
return v === "true";
|
||||||
}, [], b => {
|
}, [], b => {
|
||||||
if(b === undefined){
|
if (b === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return "" + b;
|
return "" + b;
|
||||||
|
@ -354,7 +373,7 @@ export default class MapState extends UserRelatedState {
|
||||||
|
|
||||||
private InitializeFilteredLayers() {
|
private InitializeFilteredLayers() {
|
||||||
const layoutToUse = this.layoutToUse;
|
const layoutToUse = this.layoutToUse;
|
||||||
if(layoutToUse === undefined){
|
if (layoutToUse === undefined) {
|
||||||
return new UIEventSource<FilteredLayer[]>([])
|
return new UIEventSource<FilteredLayer[]>([])
|
||||||
}
|
}
|
||||||
const flayers: FilteredLayer[] = [];
|
const flayers: FilteredLayer[] = [];
|
||||||
|
@ -363,11 +382,11 @@ export default class MapState extends UserRelatedState {
|
||||||
if (layer.syncSelection === "local") {
|
if (layer.syncSelection === "local") {
|
||||||
isDisplayed = LocalStorageSource.GetParsed(layoutToUse.id + "-layer-" + layer.id + "-enabled", layer.shownByDefault)
|
isDisplayed = LocalStorageSource.GetParsed(layoutToUse.id + "-layer-" + layer.id + "-enabled", layer.shownByDefault)
|
||||||
} else if (layer.syncSelection === "theme-only") {
|
} else if (layer.syncSelection === "theme-only") {
|
||||||
isDisplayed = this.getPref(layoutToUse.id+ "-layer-" + layer.id + "-enabled", layer)
|
isDisplayed = this.getPref(layoutToUse.id + "-layer-" + layer.id + "-enabled", layer)
|
||||||
} else if (layer.syncSelection === "global") {
|
} else if (layer.syncSelection === "global") {
|
||||||
isDisplayed = this.getPref("layer-" + layer.id + "-enabled", layer)
|
isDisplayed = this.getPref("layer-" + layer.id + "-enabled", layer)
|
||||||
} else {
|
} else {
|
||||||
isDisplayed = QueryParameters.GetBooleanQueryParameter("layer-" + layer.id, layer.shownByDefault, "Wether or not layer "+layer.id+" is shown")
|
isDisplayed = QueryParameters.GetBooleanQueryParameter("layer-" + layer.id, layer.shownByDefault, "Wether or not layer " + layer.id + " is shown")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ export class TagUtils {
|
||||||
* }
|
* }
|
||||||
* ]})
|
* ]})
|
||||||
* TagUtils.FlattenMultiAnswer([tag]) // => TagUtils.Tag({and:["x=a;b", "y=0;1;2;3"] })
|
* TagUtils.FlattenMultiAnswer([tag]) // => TagUtils.Tag({and:["x=a;b", "y=0;1;2;3"] })
|
||||||
*
|
*
|
||||||
* TagUtils.FlattenMultiAnswer(([new Tag("x","y"), new Tag("a","b")])) // => new And([new Tag("x","y"), new Tag("a","b")])
|
* TagUtils.FlattenMultiAnswer(([new Tag("x","y"), new Tag("a","b")])) // => new And([new Tag("x","y"), new Tag("a","b")])
|
||||||
* TagUtils.FlattenMultiAnswer(([new Tag("x","")])) // => new And([new Tag("x","")])
|
* TagUtils.FlattenMultiAnswer(([new Tag("x","")])) // => new And([new Tag("x","")])
|
||||||
*/
|
*/
|
||||||
|
@ -240,7 +240,7 @@ export class TagUtils {
|
||||||
*
|
*
|
||||||
* TagUtils.Tag("xyz<5").matchesProperties({xyz: 4}) // => true
|
* TagUtils.Tag("xyz<5").matchesProperties({xyz: 4}) // => true
|
||||||
* TagUtils.Tag("xyz<5").matchesProperties({xyz: 5}) // => false
|
* TagUtils.Tag("xyz<5").matchesProperties({xyz: 5}) // => false
|
||||||
*
|
*
|
||||||
* // RegexTags must match values with newlines
|
* // RegexTags must match values with newlines
|
||||||
* TagUtils.Tag("note~.*aed.*").matchesProperties({note: "Hier bevindt zich wss een defibrillator. \\n\\n De aed bevindt zich op de 5de verdieping"}) // => true
|
* TagUtils.Tag("note~.*aed.*").matchesProperties({note: "Hier bevindt zich wss een defibrillator. \\n\\n De aed bevindt zich op de 5de verdieping"}) // => true
|
||||||
* TagUtils.Tag("note~i~.*aed.*").matchesProperties({note: "Hier bevindt zich wss een defibrillator. \\n\\n De AED bevindt zich op de 5de verdieping"}) // => true
|
* TagUtils.Tag("note~i~.*aed.*").matchesProperties({note: "Hier bevindt zich wss een defibrillator. \\n\\n De AED bevindt zich op de 5de verdieping"}) // => true
|
||||||
|
@ -264,13 +264,13 @@ export class TagUtils {
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
public static TagD(json?: TagConfigJson, context: string = ""): TagsFilter | undefined {
|
public static TagD(json?: TagConfigJson, context: string = ""): TagsFilter | undefined {
|
||||||
if(json === undefined){
|
if (json === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return TagUtils.Tag(json, context)
|
return TagUtils.Tag(json, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INLINE sort of the given list
|
* INLINE sort of the given list
|
||||||
*/
|
*/
|
||||||
|
@ -492,6 +492,16 @@ export class TagUtils {
|
||||||
}
|
}
|
||||||
return " (" + joined + ") "
|
return " (" + joined + ") "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ExtractSimpleTags(tf: TagsFilter) : Tag[] {
|
||||||
|
const result: Tag[] = []
|
||||||
|
tf.visit(t => {
|
||||||
|
if(t instanceof Tag){
|
||||||
|
result.push(t)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns 'true' is opposite tags are detected.
|
* Returns 'true' is opposite tags are detected.
|
||||||
|
@ -581,4 +591,38 @@ export class TagUtils {
|
||||||
return listToFilter.some(tf => guards.some(guard => guard.shadows(tf)))
|
return listToFilter.some(tf => guards.some(guard => guard.shadows(tf)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a level specifier to the various available levels
|
||||||
|
*
|
||||||
|
* TagUtils.LevelsParser("0") // => ["0"]
|
||||||
|
* TagUtils.LevelsParser("1") // => ["1"]
|
||||||
|
* TagUtils.LevelsParser("0;2") // => ["0","2"]
|
||||||
|
* TagUtils.LevelsParser("0-5") // => ["0","1","2","3","4","5"]
|
||||||
|
* TagUtils.LevelsParser("0") // => ["0"]
|
||||||
|
* TagUtils.LevelsParser("-1") // => ["-1"]
|
||||||
|
* TagUtils.LevelsParser("0;-1") // => ["0", "-1"]
|
||||||
|
*/
|
||||||
|
public static LevelsParser(level: string): string[] {
|
||||||
|
let spec = Utils.NoNull([level])
|
||||||
|
spec = [].concat(...spec.map(s => s?.split(";")))
|
||||||
|
spec = [].concat(...spec.map(s => {
|
||||||
|
s = s.trim()
|
||||||
|
if (s.indexOf("-") < 0 || s.startsWith("-")) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
const [start, end] = s.split("-").map(s => Number(s.trim()))
|
||||||
|
if (isNaN(start) || isNaN(end)) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const values = []
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
values.push(i + "")
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}))
|
||||||
|
return Utils.NoNull(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import {Utils} from "../Utils";
|
import { Utils } from "../Utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A thin wrapper around a html element, which allows to generate a HTML-element.
|
* A thin wrapper around a html element, which allows to generate a HTML-element.
|
||||||
|
@ -39,9 +39,9 @@ export default abstract class BaseUIElement {
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScrollToTop(){
|
public ScrollToTop() {
|
||||||
this._constructedHtmlElement?.scrollTo(0,0)
|
this._constructedHtmlElement?.scrollTo(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,10 +70,13 @@ export default abstract class BaseUIElement {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RemoveClass(clss: string): BaseUIElement {
|
public RemoveClass(classes: string): BaseUIElement {
|
||||||
if (this.clss.has(clss)) {
|
const all = classes.split(" ").map(clsName => clsName.trim());
|
||||||
this.clss.delete(clss);
|
for (let clss of all) {
|
||||||
this._constructedHtmlElement?.classList.remove(clss)
|
if (this.clss.has(clss)) {
|
||||||
|
this.clss.delete(clss);
|
||||||
|
this._constructedHtmlElement?.classList.remove(clss)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,22 @@ import Toggle from "../Input/Toggle";
|
||||||
import MapControlButton from "../MapControlButton";
|
import MapControlButton from "../MapControlButton";
|
||||||
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler";
|
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import MapState from "../../Logic/State/MapState";
|
import MapState, {GlobalFilter} from "../../Logic/State/MapState";
|
||||||
|
import LevelSelector from "../Input/LevelSelector";
|
||||||
|
import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline";
|
||||||
|
import {Utils} from "../../Utils";
|
||||||
|
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
||||||
|
import {RegexTag} from "../../Logic/Tags/RegexTag";
|
||||||
|
import {Or} from "../../Logic/Tags/Or";
|
||||||
|
import {Tag} from "../../Logic/Tags/Tag";
|
||||||
|
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
||||||
|
import Translations from "../i18n/Translations";
|
||||||
|
import {BBox} from "../../Logic/BBox";
|
||||||
|
import {OsmFeature} from "../../Models/OsmFeature";
|
||||||
|
|
||||||
export default class RightControls extends Combine {
|
export default class RightControls extends Combine {
|
||||||
|
|
||||||
constructor(state: MapState) {
|
constructor(state: MapState & { featurePipeline: FeaturePipeline }) {
|
||||||
|
|
||||||
const geolocatioHandler = new GeoLocationHandler(
|
const geolocatioHandler = new GeoLocationHandler(
|
||||||
state
|
state
|
||||||
|
@ -38,7 +49,91 @@ export default class RightControls extends Combine {
|
||||||
state.locationControl.ping();
|
state.locationControl.ping();
|
||||||
});
|
});
|
||||||
|
|
||||||
super([plus, min, geolocationButton].map(el => el.SetClass("m-0.5 md:m-1")))
|
const levelsInView = state.currentBounds.map(bbox => {
|
||||||
|
if (bbox === undefined) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const allElementsUnfiltered: OsmFeature[] = [].concat(... state.featurePipeline.GetAllFeaturesAndMetaWithin(bbox).map(ff => ff.features))
|
||||||
|
const allElements = allElementsUnfiltered.filter(f => BBox.get(f).overlapsWith(bbox))
|
||||||
|
const allLevelsRaw: string[] = allElements.map(f => f.properties["level"])
|
||||||
|
const allLevels = [].concat(...allLevelsRaw.map(l => TagUtils.LevelsParser(l)))
|
||||||
|
if (allLevels.indexOf("0") < 0) {
|
||||||
|
allLevels.push("0")
|
||||||
|
}
|
||||||
|
allLevels.sort((a, b) => a < b ? -1 : 1)
|
||||||
|
return Utils.Dedup(allLevels)
|
||||||
|
})
|
||||||
|
state.globalFilters.data.push({
|
||||||
|
filter: {
|
||||||
|
currentFilter: undefined,
|
||||||
|
state: undefined,
|
||||||
|
|
||||||
|
},
|
||||||
|
id: "level",
|
||||||
|
onNewPoint: undefined
|
||||||
|
})
|
||||||
|
const levelSelect = new LevelSelector(levelsInView)
|
||||||
|
|
||||||
|
const isShown = levelsInView.map(levelsInView => {
|
||||||
|
if (levelsInView.length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (state.locationControl.data.zoom <= 16) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (levelsInView.length == 1 && levelsInView[0] == "0") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[state.locationControl])
|
||||||
|
|
||||||
|
function setLevelFilter() {
|
||||||
|
console.log("Updating levels filter")
|
||||||
|
const filter: GlobalFilter = state.globalFilters.data.find(gf => gf.id === "level")
|
||||||
|
if (!isShown.data) {
|
||||||
|
filter.filter = {
|
||||||
|
state: "*",
|
||||||
|
currentFilter: undefined,
|
||||||
|
}
|
||||||
|
filter.onNewPoint = undefined
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
const l = levelSelect.GetValue().data
|
||||||
|
let neededLevel: TagsFilter = new RegexTag("level", new RegExp("(^|;)" + l + "(;|$)"));
|
||||||
|
if (l === "0") {
|
||||||
|
neededLevel = new Or([neededLevel, new Tag("level", "")])
|
||||||
|
}
|
||||||
|
filter.filter = {
|
||||||
|
state: l,
|
||||||
|
currentFilter: neededLevel
|
||||||
|
}
|
||||||
|
const t = Translations.t.general.levelSelection
|
||||||
|
filter.onNewPoint = {
|
||||||
|
confirmAddNew: t.confirmLevel.PartialSubs({level: l}),
|
||||||
|
safetyCheck: t.addNewOnLevel.Subs({level: l}),
|
||||||
|
tags: [new Tag("level", l)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.globalFilters.ping();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isShown.addCallbackAndRun(shown => {
|
||||||
|
console.log("Is level selector shown?", shown)
|
||||||
|
setLevelFilter()
|
||||||
|
if (shown) {
|
||||||
|
levelSelect.RemoveClass("invisible")
|
||||||
|
} else {
|
||||||
|
levelSelect.SetClass("invisible")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
levelSelect.GetValue().addCallback(_ => setLevelFilter())
|
||||||
|
|
||||||
|
super([new Combine([levelSelect]).SetClass(""), plus, min, geolocationButton].map(el => el.SetClass("m-0.5 md:m-1")))
|
||||||
this.SetClass("flex flex-col items-center")
|
this.SetClass("flex flex-col items-center")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import ConfirmLocationOfPoint from "../NewPoint/ConfirmLocationOfPoint";
|
||||||
import BaseLayer from "../../Models/BaseLayer";
|
import BaseLayer from "../../Models/BaseLayer";
|
||||||
import Loading from "../Base/Loading";
|
import Loading from "../Base/Loading";
|
||||||
import Hash from "../../Logic/Web/Hash";
|
import Hash from "../../Logic/Web/Hash";
|
||||||
|
import {GlobalFilter} from "../../Logic/State/MapState";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The SimpleAddUI is a single panel, which can have multiple states:
|
* The SimpleAddUI is a single panel, which can have multiple states:
|
||||||
|
@ -66,7 +67,8 @@ export default class SimpleAddUI extends Toggle {
|
||||||
locationControl: UIEventSource<Loc>,
|
locationControl: UIEventSource<Loc>,
|
||||||
filteredLayers: UIEventSource<FilteredLayer[]>,
|
filteredLayers: UIEventSource<FilteredLayer[]>,
|
||||||
featureSwitchFilter: UIEventSource<boolean>,
|
featureSwitchFilter: UIEventSource<boolean>,
|
||||||
backgroundLayer: UIEventSource<BaseLayer>
|
backgroundLayer: UIEventSource<BaseLayer>,
|
||||||
|
globalFilters: UIEventSource<GlobalFilter[]>
|
||||||
},
|
},
|
||||||
takeLocationFrom?: UIEventSource<{lat: number, lon: number}>
|
takeLocationFrom?: UIEventSource<{lat: number, lon: number}>
|
||||||
) {
|
) {
|
||||||
|
|
70
UI/Input/LevelSelector.ts
Normal file
70
UI/Input/LevelSelector.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import {InputElement} from "./InputElement";
|
||||||
|
import {Store, Stores, UIEventSource} from "../../Logic/UIEventSource";
|
||||||
|
import Combine from "../Base/Combine";
|
||||||
|
import Slider from "./Slider";
|
||||||
|
import {ClickableToggle} from "./Toggle";
|
||||||
|
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
|
|
||||||
|
export default class LevelSelector extends VariableUiElement implements InputElement<string> {
|
||||||
|
|
||||||
|
private readonly _value: UIEventSource<string>;
|
||||||
|
|
||||||
|
constructor(currentLevels: Store<string[]>, options?: {
|
||||||
|
value?: UIEventSource<string>
|
||||||
|
}) {
|
||||||
|
const value = options?.value ?? new UIEventSource<string>(undefined)
|
||||||
|
super(Stores.ListStabilized(currentLevels).map(levels => {
|
||||||
|
console.log("CUrrent levels are", levels)
|
||||||
|
let slider = new Slider(0, levels.length - 1, {vertical: true});
|
||||||
|
const toggleClass = "flex border-2 border-blue-500 w-10 h-10 place-content-center items-center border-box"
|
||||||
|
slider.SetClass("flex elevator w-10").SetStyle(`height: ${2.5 * levels.length}rem; background: #00000000`)
|
||||||
|
|
||||||
|
const values = levels.map((data, i) => new ClickableToggle(
|
||||||
|
new FixedUiElement(data).SetClass("font-bold active bg-subtle " + toggleClass),
|
||||||
|
new FixedUiElement(data).SetClass("normal-background " + toggleClass),
|
||||||
|
slider.GetValue().sync(
|
||||||
|
(sliderVal) => {
|
||||||
|
return sliderVal === i
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
(isSelected) => {
|
||||||
|
return isSelected ? i : slider.GetValue().data
|
||||||
|
}
|
||||||
|
))
|
||||||
|
.ToggleOnClick()
|
||||||
|
.SetClass("flex w-10 h-10"))
|
||||||
|
|
||||||
|
values.reverse(/* This is a new list, no side-effects */)
|
||||||
|
const combine = new Combine([new Combine(values), slider])
|
||||||
|
combine.SetClass("flex flex-row overflow-hidden");
|
||||||
|
|
||||||
|
|
||||||
|
slider.GetValue().addCallbackAndRun(i => {
|
||||||
|
if (currentLevels?.data === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
value.setData(currentLevels?.data[i]);
|
||||||
|
})
|
||||||
|
value.addCallback(level => {
|
||||||
|
const i = currentLevels?.data?.findIndex(l => l === level)
|
||||||
|
slider.GetValue().setData(i)
|
||||||
|
})
|
||||||
|
return combine
|
||||||
|
}))
|
||||||
|
|
||||||
|
this._value = value
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GetValue(): UIEventSource<string> {
|
||||||
|
return this._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsValid(t: string): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -4,9 +4,10 @@ import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
export default class Slider extends InputElement<number> {
|
export default class Slider extends InputElement<number> {
|
||||||
|
|
||||||
private readonly _value: UIEventSource<number>
|
private readonly _value: UIEventSource<number>
|
||||||
private min: number;
|
private readonly min: number;
|
||||||
private max: number;
|
private readonly max: number;
|
||||||
private step: number;
|
private readonly step: number;
|
||||||
|
private readonly vertical: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a slider input element for natural numbers
|
* Constructs a slider input element for natural numbers
|
||||||
|
@ -16,13 +17,15 @@ export default class Slider extends InputElement<number> {
|
||||||
*/
|
*/
|
||||||
constructor(min: number, max: number, options?: {
|
constructor(min: number, max: number, options?: {
|
||||||
value?: UIEventSource<number>,
|
value?: UIEventSource<number>,
|
||||||
step?: 1 | number
|
step?: 1 | number,
|
||||||
|
vertical?: false | boolean
|
||||||
}) {
|
}) {
|
||||||
super();
|
super();
|
||||||
this.max = max;
|
this.max = max;
|
||||||
this.min = min;
|
this.min = min;
|
||||||
this._value = options?.value ?? new UIEventSource<number>(min)
|
this._value = options?.value ?? new UIEventSource<number>(min)
|
||||||
this.step = options?.step ?? 1;
|
this.step = options?.step ?? 1;
|
||||||
|
this.vertical = options?.vertical ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetValue(): UIEventSource<number> {
|
GetValue(): UIEventSource<number> {
|
||||||
|
@ -39,6 +42,10 @@ export default class Slider extends InputElement<number> {
|
||||||
el.oninput = () => {
|
el.oninput = () => {
|
||||||
valuestore.setData(Number(el.value))
|
valuestore.setData(Number(el.value))
|
||||||
}
|
}
|
||||||
|
if(this.vertical){
|
||||||
|
el.classList.add("vertical")
|
||||||
|
el.setAttribute('orient','vertical'); // firefox only workaround...
|
||||||
|
}
|
||||||
valuestore.addCallbackAndRunD(v => el.value = ""+valuestore.data)
|
valuestore.addCallbackAndRunD(v => el.value = ""+valuestore.data)
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,16 @@ import SimpleAddUI, {PresetInfo} from "../BigComponents/SimpleAddUI";
|
||||||
import BaseLayer from "../../Models/BaseLayer";
|
import BaseLayer from "../../Models/BaseLayer";
|
||||||
import Img from "../Base/Img";
|
import Img from "../Base/Img";
|
||||||
import Title from "../Base/Title";
|
import Title from "../Base/Title";
|
||||||
|
import {GlobalFilter} from "../../Logic/State/MapState";
|
||||||
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
|
import {Tag} from "../../Logic/Tags/Tag";
|
||||||
|
|
||||||
export default class ConfirmLocationOfPoint extends Combine {
|
export default class ConfirmLocationOfPoint extends Combine {
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
state: {
|
state: {
|
||||||
|
globalFilters: UIEventSource<GlobalFilter[]>;
|
||||||
featureSwitchIsTesting: UIEventSource<boolean>;
|
featureSwitchIsTesting: UIEventSource<boolean>;
|
||||||
osmConnection: OsmConnection,
|
osmConnection: OsmConnection,
|
||||||
featurePipeline: FeaturePipeline,
|
featurePipeline: FeaturePipeline,
|
||||||
|
@ -38,8 +42,8 @@ export default class ConfirmLocationOfPoint extends Combine {
|
||||||
let preciseInput: LocationInput = undefined
|
let preciseInput: LocationInput = undefined
|
||||||
if (preset.preciseInput !== undefined) {
|
if (preset.preciseInput !== undefined) {
|
||||||
// Create location input
|
// Create location input
|
||||||
|
|
||||||
|
|
||||||
// We uncouple the event source
|
// We uncouple the event source
|
||||||
const zloc = {...loc, zoom: 19}
|
const zloc = {...loc, zoom: 19}
|
||||||
const locationSrc = new UIEventSource(zloc);
|
const locationSrc = new UIEventSource(zloc);
|
||||||
|
@ -106,7 +110,11 @@ export default class ConfirmLocationOfPoint extends Combine {
|
||||||
).SetClass("font-bold break-words")
|
).SetClass("font-bold break-words")
|
||||||
.onClick(() => {
|
.onClick(() => {
|
||||||
console.log("The confirmLocationPanel - precise input yielded ", preciseInput?.GetValue()?.data)
|
console.log("The confirmLocationPanel - precise input yielded ", preciseInput?.GetValue()?.data)
|
||||||
confirm(preset.tags, preciseInput?.GetValue()?.data ?? loc, preciseInput?.snappedOnto?.data?.properties?.id);
|
const globalFilterTagsToAdd: Tag[][] = state.globalFilters.data.filter(gf => gf.onNewPoint !== undefined)
|
||||||
|
.map(gf => gf.onNewPoint.tags)
|
||||||
|
const globalTags : Tag[] = [].concat(...globalFilterTagsToAdd)
|
||||||
|
console.log("Global tags to add are: ", globalTags)
|
||||||
|
confirm([...preset.tags, ...globalTags], preciseInput?.GetValue()?.data ?? loc, preciseInput?.snappedOnto?.data?.properties?.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (preciseInput !== undefined) {
|
if (preciseInput !== undefined) {
|
||||||
|
@ -126,7 +134,7 @@ export default class ConfirmLocationOfPoint extends Combine {
|
||||||
.onClick(() => filterViewIsOpened.setData(true))
|
.onClick(() => filterViewIsOpened.setData(true))
|
||||||
|
|
||||||
|
|
||||||
const openLayerOrConfirm = new Toggle(
|
let openLayerOrConfirm = new Toggle(
|
||||||
confirmButton,
|
confirmButton,
|
||||||
openLayerControl,
|
openLayerControl,
|
||||||
preset.layerToAddTo.isDisplayed
|
preset.layerToAddTo.isDisplayed
|
||||||
|
@ -152,6 +160,29 @@ export default class ConfirmLocationOfPoint extends Combine {
|
||||||
closePopup()
|
closePopup()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// We assume the number of global filters won't change during the run of the program
|
||||||
|
for (let i = 0; i < state.globalFilters.data.length; i++) {
|
||||||
|
const hasBeenCheckedOf = new UIEventSource(false);
|
||||||
|
|
||||||
|
const filterConfirmPanel = new VariableUiElement(
|
||||||
|
state.globalFilters.map(gfs => {
|
||||||
|
const gf = gfs[i]
|
||||||
|
const confirm = gf.onNewPoint?.confirmAddNew?.Subs({preset: preset.title})
|
||||||
|
return new Combine([
|
||||||
|
gf.onNewPoint?.safetyCheck,
|
||||||
|
new SubtleButton(Svg.confirm_svg(), confirm).onClick(() => hasBeenCheckedOf.setData(true))
|
||||||
|
])
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
openLayerOrConfirm = new Toggle(
|
||||||
|
openLayerOrConfirm, filterConfirmPanel,
|
||||||
|
state.globalFilters.map(f => hasBeenCheckedOf.data || f[i]?.onNewPoint === undefined, [hasBeenCheckedOf])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const hasActiveFilter = preset.layerToAddTo.appliedFilters
|
const hasActiveFilter = preset.layerToAddTo.appliedFilters
|
||||||
.map(appliedFilters => {
|
.map(appliedFilters => {
|
||||||
const activeFilters = Array.from(appliedFilters.values()).filter(f => f?.currentFilter !== undefined);
|
const activeFilters = Array.from(appliedFilters.values()).filter(f => f?.currentFilter !== undefined);
|
||||||
|
@ -171,16 +202,16 @@ export default class ConfirmLocationOfPoint extends Combine {
|
||||||
Translations.t.general.cancel
|
Translations.t.general.cancel
|
||||||
).onClick(cancel)
|
).onClick(cancel)
|
||||||
|
|
||||||
|
|
||||||
let examples : BaseUIElement = undefined;
|
let examples: BaseUIElement = undefined;
|
||||||
if(preset.exampleImages !== undefined && preset.exampleImages.length > 0){
|
if (preset.exampleImages !== undefined && preset.exampleImages.length > 0) {
|
||||||
examples = new Combine([
|
examples = new Combine([
|
||||||
new Title( preset.exampleImages.length == 1 ? Translations.t.general.example : Translations.t.general.examples),
|
new Title(preset.exampleImages.length == 1 ? Translations.t.general.example : Translations.t.general.examples),
|
||||||
new Combine(preset.exampleImages.map(img => new Img(img).SetClass("h-64 m-1 w-auto rounded-lg"))).SetClass("flex flex-wrap items-stretch")
|
new Combine(preset.exampleImages.map(img => new Img(img).SetClass("h-64 m-1 w-auto rounded-lg"))).SetClass("flex flex-wrap items-stretch")
|
||||||
])
|
])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super([
|
super([
|
||||||
new Toggle(
|
new Toggle(
|
||||||
Translations.t.general.testing.SetClass("alert"),
|
Translations.t.general.testing.SetClass("alert"),
|
||||||
|
|
|
@ -317,6 +317,19 @@ export class TypedTranslation<T> extends Translation {
|
||||||
return Utils.SubstituteKeys(template, text, lang);
|
return Utils.SubstituteKeys(template, text, lang);
|
||||||
}, context)
|
}, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PartialSubs<X extends string>(text: Partial<T> & Record<X, string>): TypedTranslation<Omit<T, X>> {
|
||||||
|
const newTranslations : Record<string, string> = {}
|
||||||
|
for (const lang in this.translations) {
|
||||||
|
const template = this.translations[lang]
|
||||||
|
if(lang === "_context"){
|
||||||
|
newTranslations[lang] = template
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newTranslations[lang] = Utils.SubstituteKeys(template, text, lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TypedTranslation<Omit<T, X>>(newTranslations, this.context)
|
||||||
|
}
|
||||||
}
|
}
|
78
assets/svg/elevator.svg
Normal file
78
assets/svg/elevator.svg
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="图层_1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 65.9 66"
|
||||||
|
style="enable-background:new 0 0 65.9 66;"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:docname="elevator.svg"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||||
|
id="defs33">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</defs><sodipodi:namedview
|
||||||
|
id="namedview31"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#111111"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="61.063924"
|
||||||
|
inkscape:cx="32.105699"
|
||||||
|
inkscape:cy="40.138265"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1007"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="图层_1" />
|
||||||
|
<path
|
||||||
|
d="M 48.2,15.6 H 19.3 c -1.6,0 -2.8,1.3 -2.8,2.8 v 29 c 0,1.6 1.3,2.8 2.8,2.8 h 28.9 c 1.6,0 2.8,-1.3 2.8,-2.8 V 18.5 C 51,17 49.7,15.6 48.2,15.6 Z m 0.3,30.7 c 0,0.8 -0.6,1.4 -1.4,1.4 H 20.4 C 19.6,47.7 19,47.1 19,46.3 V 19.6 c 0,-0.8 0.6,-1.4 1.4,-1.4 h 26.7 c 0.8,0 1.4,0.6 1.4,1.4 z"
|
||||||
|
id="path2" /><polygon
|
||||||
|
points="29.4,9.5 33.7,5.5 38.1,9.5 35.3,9.5 35.3,13.5 32.2,13.5 32.2,9.5 "
|
||||||
|
id="polygon8" /><polygon
|
||||||
|
points="38.1,56.6 33.7,60.5 29.4,56.6 32.2,56.6 32.2,52.5 35.3,52.5 35.3,56.6 "
|
||||||
|
id="polygon10" /><circle
|
||||||
|
cx="28.6"
|
||||||
|
cy="21.4"
|
||||||
|
r="1.9"
|
||||||
|
id="circle12" /><path
|
||||||
|
d="m 29.1,35.6 c 0,-0.2 -0.2,-0.5 -0.5,-0.5 -0.3,0 -0.5,0.2 -0.5,0.5 v 1.7 3 5 c 0,0.6 -0.6,1.2 -1.2,1.2 -0.6,0 -1.2,-0.5 -1.2,-1.2 V 40.5 37.3 33.2 32 28.2 28.3 c 0,-0.2 -0.1,-0.4 -0.4,-0.4 -0.2,0 -0.4,0.2 -0.4,0.4 v 1.3 3.5 1.8 c 0,0.4 -0.3,0.7 -0.7,0.7 -0.4,0 -0.7,-0.3 -0.7,-0.7 v -1 -3.5 -4.2 c 0,-1 0.8,-1.9 1.9,-1.9 h 5.2 0.3 c 0.2,0 0.5,0.1 0.6,0.3 l 2.1,2.1 2.5,-2.5 c 0.4,-0.4 1,-0.4 1.3,0 0.4,0.4 0.4,1 0,1.3 l -3.2,3.2 C 34,28.9 33.8,29 33.5,29 33.3,29 33,28.9 32.8,28.7 L 32,27.9 31.8,27.8 h -0.2 c -0.2,0 -0.4,0.2 -0.4,0.4 v 1.6 10.6 l 7.68e-4,4.906766 c 9.4e-5,0.6 -0.6,1.2 -1.2,1.2 -0.6,0 -0.957071,-0.484801 -0.957071,-1.184801 0,0 0.0563,-6.496244 0.0563,-9.721965 z"
|
||||||
|
id="path14"
|
||||||
|
sodipodi:nodetypes="cssccsssccccccssccsssccsscsccsccsssccssccsscc" /><circle
|
||||||
|
cx="42.400002"
|
||||||
|
cy="21"
|
||||||
|
r="1.5"
|
||||||
|
id="circle16" /><circle
|
||||||
|
cx="42.400002"
|
||||||
|
cy="24.5"
|
||||||
|
r="1.5"
|
||||||
|
id="circle18" /><circle
|
||||||
|
cx="42.400002"
|
||||||
|
cy="28.200001"
|
||||||
|
r="1.5"
|
||||||
|
id="circle20" />
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -1 +1,76 @@
|
||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 228.57 239.29"><defs><style>.cls-1{fill:#1d1d1b;}</style></defs><path class="cls-1" d="M208.18,119.08V11.93c0-5.24,0-5.24,5.15-5.24,2.43,0,4.87,0,7.31,0,2.05,0,3.12.76,3,2.92-.07,1.13,0,2.27,0,3.4q0,106.5,0,213c0,.69-.06,1.39,0,2.09.13,1.9-.73,2.68-2.62,2.64-3.39-.05-6.79,0-10.19,0-1.84,0-2.73-.76-2.66-2.67s0-4,0-6Z"/><path class="cls-1" d="M20,119.06V226.75c0,3.9,0,3.92-3.91,4-3,0-5.93-.05-8.89,0-1.76,0-2.58-.7-2.55-2.47s0-3.31,0-5q0-106.12,0-212.25c0-4.17.22-4.39,4.45-4.36,2.52,0,5.05.08,7.58,0,2.26-.09,3.47.67,3.32,3.13-.1,1.47,0,3,0,4.44q0,52.41,0,104.82Z"/><path class="cls-1" d="M135.46,156.52a4.35,4.35,0,0,0-4.86-3.21,12.64,12.64,0,0,0-2.44.73c-2.07.72-4.13,1.47-6.22,2.21l-.26-.54q-1.94-4.52-3.88-9c-1.77-4.11-3.53-8.22-5.32-12.31a4.21,4.21,0,0,0-4.06-2.71c-4.84-.06-9.68-.07-14.52-.11L81,131.46c-.18,0-.5-.09-.52-.19-.16-.78-.27-1.58-.4-2.42H99.2c.35,0,.69,0,1,0a4.41,4.41,0,0,0,3.41-6.75,4.58,4.58,0,0,0-4.22-2.1q-10,0-20,0h-.72c-.52-3.24-1-6.39-1.54-9.56h-9c.55,3.45,1.11,6.9,1.66,10.35.83,5.11,1.71,10.2,2.47,15.32a4.76,4.76,0,0,0,4.92,4.29c9.22,0,18.45.11,27.68.15a.86.86,0,0,1,.91.61q4.72,11.05,9.48,22.08a4.49,4.49,0,0,0,6.12,2.69c3.69-1.31,7.39-2.61,11.07-3.95a6.74,6.74,0,0,0,1.67-1,4.71,4.71,0,0,0,1.49-2.56v-1.48C135.55,156.8,135.5,156.66,135.46,156.52Z"/><path class="cls-1" d="M53.61,145.09c.2-1.23.33-2.47.59-3.69a26.58,26.58,0,0,1,6.42-12.56,4.42,4.42,0,0,1,6.63,5.85,18.7,18.7,0,0,0-4.46,8.95,18.32,18.32,0,0,0,34.75,10.84,4.62,4.62,0,0,1,3.67-3,4.41,4.41,0,0,1,4.76,5.84,23.53,23.53,0,0,1-3.45,6.12,26.77,26.77,0,0,1-14.83,10c-1.46.38-3,.53-4.47.79l-.56.11H78.9c-.14,0-.29-.08-.43-.1a34.79,34.79,0,0,1-3.7-.6c-11-2.89-17.86-9.91-20.58-20.95-.29-1.19-.39-2.43-.58-3.64Z"/><ellipse class="cls-1" cx="72.62" cy="109.74" rx="4.51" ry="3.94"/><circle class="cls-1" cx="71.76" cy="95.49" r="8.93"/><path class="cls-1" d="M157.33,131.79H154v1.75q0,18,0,36c0,2.4-1,3.89-2.94,4.52a4.17,4.17,0,0,1-5.43-3.17,10.86,10.86,0,0,1-.13-2q0-31,0-61.93v-2.49h-2c0,.48-.07,1-.07,1.47,0,7.32,0,14.65,0,22,0,2.28-1.42,3.66-3.4,3.39a3,3,0,0,1-2.63-3.33c0-5.24,0-10.47,0-15.7,0-4.38,0-8.75,0-13.13,0-5.09,2.76-7.89,7.83-7.92q10.48-.06,21,0c4.89,0,7.67,2.88,7.67,7.77q0,14.49,0,29c0,2.3-1.56,3.7-3.6,3.31-1.57-.3-2.41-1.44-2.41-3.35q0-10.84,0-21.69v-1.76h-2v1.76q0,31.32,0,62.64a10.81,10.81,0,0,1-.17,2.13,4.23,4.23,0,0,1-4.41,3.27,4.18,4.18,0,0,1-3.9-4c0-2.95,0-5.9,0-8.84V131.79Z"/><path class="cls-1" d="M155.74,89.56a9,9,0,0,1-9.1-9,9,9,0,0,1,18,0A9,9,0,0,1,155.74,89.56Z"/><path class="cls-1" d="M191.93,36.94h-71V24.12h10l-17-17-17,17h10V36.94h-71a5.83,5.83,0,0,0-5.83,5.83V197.05a5.83,5.83,0,0,0,5.83,5.83h71V215.7h-10l17,17,17-17h-10V202.88h71a5.83,5.83,0,0,0,5.83-5.83V42.77A5.83,5.83,0,0,0,191.93,36.94Zm-7.5,147a5.83,5.83,0,0,1-5.83,5.84H49.22a5.83,5.83,0,0,1-5.83-5.84V56a5.83,5.83,0,0,1,5.83-5.83H178.6A5.83,5.83,0,0,1,184.43,56Z"/></svg>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
id="Layer_1"
|
||||||
|
data-name="Layer 1"
|
||||||
|
viewBox="0 0 228.57 239.29"
|
||||||
|
version="1.1"
|
||||||
|
sodipodi:docname="elevator_wheelchair.svg"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview25"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#111111"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.9145025"
|
||||||
|
inkscape:cx="60.851318"
|
||||||
|
inkscape:cy="68.425087"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1007"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="Layer_1" />
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<style
|
||||||
|
id="style2">.cls-1{fill:#1d1d1b;}</style>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
class="cls-1"
|
||||||
|
d="m 146.05301,172.90366 a 6.4729946,6.4729946 0 0 0 -7.2319,-4.77662 18.808885,18.808885 0 0 0 -3.63083,1.08627 c -3.08025,1.07139 -6.14562,2.18743 -9.25563,3.28858 l -0.3869,-0.80354 q -2.8868,-6.72597 -5.77361,-13.39241 c -2.63384,-6.11586 -5.2528,-12.23172 -7.9164,-18.31783 a 6.2646683,6.2646683 0 0 0 -6.04146,-4.0326 c -7.202135,-0.0893 -14.404272,-0.10416 -21.606408,-0.16368 l -19.195777,-0.17857 c -0.267848,0 -0.744022,-0.13392 -0.773783,-0.28273 -0.238087,-1.16067 -0.401772,-2.35111 -0.595218,-3.60107 h 28.451415 c 0.520816,0 1.026751,0 1.488045,0 a 6.5622772,6.5622772 0 0 0 5.074232,-10.0443 6.8152448,6.8152448 0 0 0 -6.279548,-3.12489 q -14.880448,0 -29.760895,0 H 61.546951 C 60.773168,113.739 60.058906,109.05166 59.255362,104.33456 H 45.862959 c 0.818425,5.13375 1.65173,10.26751 2.470155,15.40126 1.235077,7.60391 2.544556,15.17806 3.67547,22.79685 a 7.0830929,7.0830929 0 0 0 7.32118,6.38371 c 13.719773,0 27.454425,0.16369 41.189076,0.22321 a 1.2797185,1.2797185 0 0 1 1.35412,0.9077 q 7.02357,16.4429 14.10667,32.85603 a 6.6813208,6.6813208 0 0 0 9.10683,4.00284 c 5.49089,-1.94934 10.99665,-3.88379 16.47266,-5.87777 a 10.029421,10.029421 0 0 0 2.48503,-1.48805 7.0086907,7.0086907 0 0 0 2.21719,-3.80939 v -2.20231 c -0.0744,-0.20833 -0.14881,-0.41665 -0.20833,-0.62498 z"
|
||||||
|
id="path10"
|
||||||
|
style="stroke-width:1.48804" />
|
||||||
|
<path
|
||||||
|
class="cls-1"
|
||||||
|
d="m 24.25655,155.89531 c 0.297609,-1.83029 0.491055,-3.67547 0.877946,-5.49088 a 39.552229,39.552229 0 0 1 9.553248,-18.68985 6.5785825,6.5785825 0 0 1 9.865736,8.70507 27.826436,27.826436 0 0 0 -6.636679,13.318 27.260979,27.260979 0 0 0 51.709554,16.1304 6.8747666,6.8747666 0 0 1 5.461124,-4.46413 6.5622772,6.5622772 0 0 1 7.083091,8.69018 35.013692,35.013692 0 0 1 -5.133752,9.10683 39.834957,39.834957 0 0 1 -22.067704,14.88045 c -2.172545,0.56546 -4.464134,0.78866 -6.65156,1.17555 l -0.833305,0.16369 h -5.595048 c -0.208326,0 -0.431533,-0.11905 -0.639859,-0.14881 a 51.769076,51.769076 0 0 1 -5.505766,-0.89282 c -16.368492,-4.30045 -26.576478,-14.74653 -30.62396,-31.17454 -0.431533,-1.77077 -0.580337,-3.61595 -0.863066,-5.41648 z"
|
||||||
|
id="path12"
|
||||||
|
style="stroke-width:1.48804" />
|
||||||
|
<ellipse
|
||||||
|
class="cls-1"
|
||||||
|
cx="52.544285"
|
||||||
|
cy="103.29292"
|
||||||
|
rx="6.711082"
|
||||||
|
ry="5.8628964"
|
||||||
|
id="ellipse14"
|
||||||
|
style="stroke-width:1.48804" />
|
||||||
|
<circle
|
||||||
|
class="cls-1"
|
||||||
|
cx="51.264565"
|
||||||
|
cy="82.088287"
|
||||||
|
r="13.288239"
|
||||||
|
id="circle16"
|
||||||
|
style="stroke-width:1.48804" />
|
||||||
|
<path
|
||||||
|
class="cls-1"
|
||||||
|
d="m 178.59655,136.10432 h -4.95519 v 2.60407 q 0,26.78481 0,53.56961 c 0,3.57131 -1.48804,5.7885 -4.37485,6.72597 a 6.2051465,6.2051465 0 0 1 -8.08008,-4.71711 16.160166,16.160166 0 0 1 -0.19345,-2.97609 q 0,-46.12938 0,-92.154605 v -3.705232 h -2.97609 c 0,0.714262 -0.10416,1.488045 -0.10416,2.187426 0,10.892491 0,21.799851 0,32.736981 0,3.39275 -2.11303,5.44625 -5.05935,5.04447 a 4.4641342,4.4641342 0 0 1 -3.91356,-4.95518 c 0,-7.79736 0,-15.57983 0,-23.36231 0,-6.51763 0,-13.020388 0,-19.538024 0,-7.574147 4.107,-11.740672 11.65139,-11.785314 q 15.59471,-0.08928 31.24894,0 c 7.27654,0 11.4133,4.285569 11.4133,11.562108 q 0,21.56177 0,43.1533 c 0,3.4225 -2.32135,5.50576 -5.35696,4.92542 -2.33623,-0.44641 -3.58619,-2.14278 -3.58619,-4.98495 q 0,-16.1304 0,-32.275685 v -2.618959 h -2.97609 v 2.618959 q 0,46.605565 0,93.211125 a 16.085763,16.085763 0 0 1 -0.25297,3.16953 6.2944292,6.2944292 0 0 1 -6.56227,4.86591 6.2200269,6.2200269 0 0 1 -5.80338,-5.95218 c 0,-4.38973 0,-8.77946 0,-13.15432 v -44.19492 z"
|
||||||
|
id="path18"
|
||||||
|
style="stroke-width:1.48804" />
|
||||||
|
<path
|
||||||
|
class="cls-1"
|
||||||
|
d="m 176.23056,73.264187 a 13.392403,13.392403 0 0 1 -13.54121,-13.392403 13.392405,13.392405 0 0 1 26.78481,0 13.392403,13.392403 0 0 1 -13.2436,13.392403 z"
|
||||||
|
id="path20"
|
||||||
|
style="stroke-width:1.48804" />
|
||||||
|
<path
|
||||||
|
class="cls-1"
|
||||||
|
d="M 226.27477,0.4068479 H 124.01222 V -18.058024 H 138.4154 L 113.93,-42.543423 89.444601,-18.058024 H 103.84778 V 0.4068479 H 1.5852264 A 8.3970517,8.3970517 0 0 0 -6.8118252,8.8038996 V 231.0161 a 8.3970517,8.3970517 0 0 0 8.3970516,8.39705 H 103.84778 v 18.46487 H 89.444601 l 24.485399,24.4854 24.4854,-24.4854 h -14.40318 v -18.46487 h 102.26255 a 8.3970517,8.3970517 0 0 0 8.39706,-8.39705 V 8.8038996 A 8.3970517,8.3970517 0 0 0 226.27477,0.4068479 Z M 215.47239,212.13354 a 8.3970517,8.3970517 0 0 1 -8.39705,8.41145 H 20.727047 a 8.3970517,8.3970517 0 0 1 -8.397051,-8.41145 V 27.859302 A 8.3970517,8.3970517 0 0 1 20.727047,19.46225 H 207.07534 a 8.3970517,8.3970517 0 0 1 8.39705,8.397052 z"
|
||||||
|
id="path22"
|
||||||
|
style="stroke-width:1.44032" />
|
||||||
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 5.6 KiB |
|
@ -387,9 +387,19 @@
|
||||||
],
|
],
|
||||||
"sources": []
|
"sources": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "elevator.svg",
|
||||||
|
"license": "CC-BY-SA 4.0",
|
||||||
|
"authors": [
|
||||||
|
"Yveltal"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"https://commons.wikimedia.org/wiki/File:HZM_elevator_icon.svg"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "elevator_wheelchair.svg",
|
"path": "elevator_wheelchair.svg",
|
||||||
"license": "CC-BY_SA",
|
"license": "CC-BY-SA",
|
||||||
"authors": [
|
"authors": [
|
||||||
"Robin Julien"
|
"Robin Julien"
|
||||||
],
|
],
|
||||||
|
|
|
@ -1110,10 +1110,6 @@ video {
|
||||||
height: 12rem;
|
height: 12rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.max-h-screen {
|
|
||||||
max-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.max-h-7 {
|
.max-h-7 {
|
||||||
max-height: 1.75rem;
|
max-height: 1.75rem;
|
||||||
}
|
}
|
||||||
|
@ -1302,6 +1298,10 @@ video {
|
||||||
flex-wrap: wrap-reverse;
|
flex-wrap: wrap-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.place-content-center {
|
||||||
|
place-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.content-start {
|
.content-start {
|
||||||
align-content: flex-start;
|
align-content: flex-start;
|
||||||
}
|
}
|
||||||
|
@ -1481,6 +1481,11 @@ video {
|
||||||
border-color: rgba(252, 165, 165, var(--tw-border-opacity));
|
border-color: rgba(252, 165, 165, var(--tw-border-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-blue-500 {
|
||||||
|
--tw-border-opacity: 1;
|
||||||
|
border-color: rgba(59, 130, 246, var(--tw-border-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.border-gray-200 {
|
.border-gray-200 {
|
||||||
--tw-border-opacity: 1;
|
--tw-border-opacity: 1;
|
||||||
border-color: rgba(229, 231, 235, var(--tw-border-opacity));
|
border-color: rgba(229, 231, 235, var(--tw-border-opacity));
|
||||||
|
@ -1510,6 +1515,11 @@ video {
|
||||||
background-color: rgba(224, 231, 255, var(--tw-bg-opacity));
|
background-color: rgba(224, 231, 255, var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-red-500 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.bg-black {
|
.bg-black {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgba(0, 0, 0, var(--tw-bg-opacity));
|
background-color: rgba(0, 0, 0, var(--tw-bg-opacity));
|
||||||
|
@ -1530,11 +1540,6 @@ video {
|
||||||
background-color: rgba(209, 213, 219, var(--tw-bg-opacity));
|
background-color: rgba(209, 213, 219, var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-red-500 {
|
|
||||||
--tw-bg-opacity: 1;
|
|
||||||
background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-red-200 {
|
.bg-red-200 {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgba(254, 202, 202, var(--tw-bg-opacity));
|
background-color: rgba(254, 202, 202, var(--tw-bg-opacity));
|
||||||
|
@ -1857,15 +1862,11 @@ video {
|
||||||
}
|
}
|
||||||
|
|
||||||
.z-above-map {
|
.z-above-map {
|
||||||
z-index: 10000
|
z-index: 10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.z-above-controls {
|
.z-above-controls {
|
||||||
z-index: 10001
|
z-index: 10001;
|
||||||
}
|
|
||||||
|
|
||||||
.w-160 {
|
|
||||||
width: 40rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-subtle {
|
.bg-subtle {
|
||||||
|
@ -1892,14 +1893,14 @@ video {
|
||||||
* Base colour of interactive elements, mainly the 'subtle button'
|
* Base colour of interactive elements, mainly the 'subtle button'
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
--subtle-detail-color: #DBEAFE;
|
--subtle-detail-color: #dbeafe;
|
||||||
--subtle-detail-color-contrast: black;
|
--subtle-detail-color-contrast: black;
|
||||||
--subtle-detail-color-light-contrast: lightgrey;
|
--subtle-detail-color-light-contrast: lightgrey;
|
||||||
/**
|
/**
|
||||||
* A stronger variant of the 'subtle-detail-colour'
|
* A stronger variant of the 'subtle-detail-colour'
|
||||||
* Used as subtle button hover
|
* Used as subtle button hover
|
||||||
*/
|
*/
|
||||||
--unsubtle-detail-color: #BFDBFE;
|
--unsubtle-detail-color: #bfdbfe;
|
||||||
--unsubtle-detail-color-contrast: black;
|
--unsubtle-detail-color-contrast: black;
|
||||||
--catch-detail-color: #3a3aeb;
|
--catch-detail-color: #3a3aeb;
|
||||||
--catch-detail-color-contrast: white;
|
--catch-detail-color-contrast: white;
|
||||||
|
@ -1913,7 +1914,8 @@ video {
|
||||||
--variable-title-height: 0px;
|
--variable-title-height: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html,
|
||||||
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
min-height: -webkit-fill-available;
|
min-height: -webkit-fill-available;
|
||||||
|
@ -1921,7 +1923,7 @@ html, body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
color: var(--foreground-color);
|
color: var(--foreground-color);
|
||||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.leaflet-overlay-pane .leaflet-zoom-animated {
|
.leaflet-overlay-pane .leaflet-zoom-animated {
|
||||||
|
@ -1944,7 +1946,8 @@ html, body {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg, img {
|
svg,
|
||||||
|
img {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -2048,6 +2051,35 @@ a {
|
||||||
height: min-content;
|
height: min-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* slider */
|
||||||
|
|
||||||
|
input[type="range"].vertical {
|
||||||
|
-webkit-writing-mode: bt-lr;
|
||||||
|
-ms-writing-mode: bt-lr;
|
||||||
|
writing-mode: bt-lr;
|
||||||
|
/* IE */
|
||||||
|
-webkit-appearance: slider-vertical;
|
||||||
|
/* Chromium */
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
input[type="range"].elevator::-moz-range-thumb {
|
||||||
|
background-color: #00000000 !important;
|
||||||
|
background-image: url("/assets/svg/elevator_wheelchair.svg");
|
||||||
|
width: 150px !important;
|
||||||
|
height: 30px !important;
|
||||||
|
border: 2px;
|
||||||
|
border-style: solid;
|
||||||
|
background-size: contain;
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
cursor: pointer;
|
||||||
|
border-image: linear-gradient(to right, black 50%, transparent 50%) 100% 1;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.border-detail {
|
.border-detail {
|
||||||
border-color: var(--foreground-color);
|
border-color: var(--foreground-color);
|
||||||
}
|
}
|
||||||
|
@ -2117,7 +2149,7 @@ p {
|
||||||
}
|
}
|
||||||
|
|
||||||
li::marker {
|
li::marker {
|
||||||
content: "•"
|
content: "•";
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtle-background {
|
.subtle-background {
|
||||||
|
@ -2127,7 +2159,7 @@ li::marker {
|
||||||
|
|
||||||
.normal-background {
|
.normal-background {
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
color: var(--foreground-color)
|
color: var(--foreground-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtle-lighter {
|
.subtle-lighter {
|
||||||
|
@ -2205,7 +2237,8 @@ li::marker {
|
||||||
color: unset !important;
|
color: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disable-links a.must-link, .disable-links .must-link a {
|
.disable-links a.must-link,
|
||||||
|
.disable-links .must-link a {
|
||||||
/* Hide links if they are disabled */
|
/* Hide links if they are disabled */
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -2424,7 +2457,7 @@ li::marker {
|
||||||
/***************** Info box (box containing features and questions ******************/
|
/***************** Info box (box containing features and questions ******************/
|
||||||
|
|
||||||
input {
|
input {
|
||||||
color: var(--foreground-color)
|
color: var(--foreground-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.leaflet-popup-content {
|
.leaflet-popup-content {
|
||||||
|
@ -2485,7 +2518,7 @@ input {
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-height {
|
.animate-height {
|
||||||
transition: max-height .5s ease-in-out;
|
transition: max-height 0.5s ease-in-out;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2549,7 +2582,7 @@ input {
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapping-icon-large{
|
.mapping-icon-large {
|
||||||
/* A mapping icon type */
|
/* A mapping icon type */
|
||||||
width: 6rem;
|
width: 6rem;
|
||||||
max-height: 5rem;
|
max-height: 5rem;
|
||||||
|
@ -2855,4 +2888,3 @@ input {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,10 @@
|
||||||
"title": "Select layers",
|
"title": "Select layers",
|
||||||
"zoomInToSeeThisLayer": "Zoom in to see this layer"
|
"zoomInToSeeThisLayer": "Zoom in to see this layer"
|
||||||
},
|
},
|
||||||
|
"levelSelection": {
|
||||||
|
"addNewOnLevel": "Is the new point location on level {level}?",
|
||||||
|
"confirmLevel": "Yes, add {preset} on level {level}"
|
||||||
|
},
|
||||||
"loading": "Loading…",
|
"loading": "Loading…",
|
||||||
"loadingTheme": "Loading {theme}…",
|
"loadingTheme": "Loading {theme}…",
|
||||||
"loginFailed": "Logging in into OpenStreetMap failed",
|
"loginFailed": "Logging in into OpenStreetMap failed",
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
||||||
import RelationsTracker from "../Logic/Osm/RelationsTracker";
|
import RelationsTracker from "../Logic/Osm/RelationsTracker";
|
||||||
import * as OsmToGeoJson from "osmtogeojson";
|
import * as OsmToGeoJson from "osmtogeojson";
|
||||||
import MetaTagging from "../Logic/MetaTagging";
|
import MetaTagging from "../Logic/MetaTagging";
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import {ImmutableStore, UIEventSource} from "../Logic/UIEventSource";
|
||||||
import {TileRange, Tiles} from "../Models/TileRange";
|
import {TileRange, Tiles} from "../Models/TileRange";
|
||||||
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
|
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
|
||||||
import ScriptUtils from "./ScriptUtils";
|
import ScriptUtils from "./ScriptUtils";
|
||||||
|
@ -250,9 +250,10 @@ function sliceToTiles(allFeatures: FeatureSource, theme: LayoutConfig, relations
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredTile = new FilteringFeatureSource({
|
const filteredTile = new FilteringFeatureSource({
|
||||||
locationControl: new UIEventSource<Loc>(undefined),
|
locationControl: new ImmutableStore<Loc>(undefined),
|
||||||
allElements: undefined,
|
allElements: undefined,
|
||||||
selectedElement: new UIEventSource<any>(undefined)
|
selectedElement: new ImmutableStore<any>(undefined),
|
||||||
|
globalFilters: new ImmutableStore([])
|
||||||
},
|
},
|
||||||
tileIndex,
|
tileIndex,
|
||||||
tile,
|
tile,
|
||||||
|
@ -323,9 +324,10 @@ function sliceToTiles(allFeatures: FeatureSource, theme: LayoutConfig, relations
|
||||||
if (pointsOnlyLayers.indexOf(layer.id) >= 0) {
|
if (pointsOnlyLayers.indexOf(layer.id) >= 0) {
|
||||||
|
|
||||||
const filtered = new FilteringFeatureSource({
|
const filtered = new FilteringFeatureSource({
|
||||||
locationControl: new UIEventSource<Loc>(undefined),
|
locationControl: new ImmutableStore<Loc>(undefined),
|
||||||
allElements: undefined,
|
allElements: undefined,
|
||||||
selectedElement: new UIEventSource<any>(undefined)
|
selectedElement: new ImmutableStore<any>(undefined),
|
||||||
|
globalFilters: new ImmutableStore([])
|
||||||
},
|
},
|
||||||
Tiles.tile_index(0, 0, 0),
|
Tiles.tile_index(0, 0, 0),
|
||||||
source,
|
source,
|
||||||
|
|
76
test.ts
76
test.ts
|
@ -1,76 +1,4 @@
|
||||||
import ChartJs from "./UI/Base/ChartJs";
|
import LevelSelector from "./UI/Input/LevelSelector";
|
||||||
import TagRenderingChart from "./UI/BigComponents/TagRenderingChart";
|
|
||||||
import {OsmFeature} from "./Models/OsmFeature";
|
|
||||||
import * as food from "./assets/generated/layers/food.json"
|
|
||||||
import TagRenderingConfig from "./Models/ThemeConfig/TagRenderingConfig";
|
|
||||||
import {UIEventSource} from "./Logic/UIEventSource";
|
import {UIEventSource} from "./Logic/UIEventSource";
|
||||||
import Combine from "./UI/Base/Combine";
|
|
||||||
const data = new UIEventSource<OsmFeature[]>([
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
id: "node/1234",
|
|
||||||
cuisine:"pizza",
|
|
||||||
"payment:cash":"yes"
|
|
||||||
},
|
|
||||||
geometry:{
|
|
||||||
type: "Point",
|
|
||||||
coordinates: [0,0]
|
|
||||||
},
|
|
||||||
id: "node/1234",
|
|
||||||
type: "Feature"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
id: "node/42",
|
|
||||||
cuisine:"pizza",
|
|
||||||
"payment:cash":"yes"
|
|
||||||
},
|
|
||||||
geometry:{
|
|
||||||
type: "Point",
|
|
||||||
coordinates: [1,0]
|
|
||||||
},
|
|
||||||
id: "node/42",
|
|
||||||
type: "Feature"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
id: "node/452",
|
|
||||||
cuisine:"pasta",
|
|
||||||
"payment:cash":"yes",
|
|
||||||
"payment:cards":"yes"
|
|
||||||
},
|
|
||||||
geometry:{
|
|
||||||
type: "Point",
|
|
||||||
coordinates: [2,0]
|
|
||||||
},
|
|
||||||
id: "node/452",
|
|
||||||
type: "Feature"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
id: "node/4542",
|
|
||||||
cuisine:"something_comletely_invented",
|
|
||||||
"payment:cards":"yes"
|
|
||||||
},
|
|
||||||
geometry:{
|
|
||||||
type: "Point",
|
|
||||||
coordinates: [3,0]
|
|
||||||
},
|
|
||||||
id: "node/4542",
|
|
||||||
type: "Feature"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
properties: {
|
|
||||||
id: "node/45425",
|
|
||||||
},
|
|
||||||
geometry:{
|
|
||||||
type: "Point",
|
|
||||||
coordinates: [3,0]
|
|
||||||
},
|
|
||||||
id: "node/45425",
|
|
||||||
type: "Feature"
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
new Combine(food.tagRenderings.map(tr => new TagRenderingChart(data, new TagRenderingConfig(tr, "test"), {chartclasses: "w-160 h-160"})))
|
new LevelSelector(new UIEventSource(["0","1","2","2.5","x","3"])).AttachTo("maindiv")
|
||||||
.AttachTo("maindiv")
|
|
Loading…
Reference in a new issue