kopia lustrzana https://github.com/FacilMap/facilmap
298 wiersze
8.1 KiB
Vue
298 wiersze
8.1 KiB
Vue
<script setup lang="ts">
|
|
import { filterHasError, getOrderedTypes } from "facilmap-utils";
|
|
import ModalDialog from "./ui/modal-dialog.vue";
|
|
import { computed, ref } from "vue";
|
|
import { injectContextRequired, requireClientContext, requireMapContext } from "./facil-map-context-provider/facil-map-context-provider.vue";
|
|
import ValidatedField from "./ui/validated-form/validated-field.vue";
|
|
|
|
const context = injectContextRequired();
|
|
const mapContext = requireMapContext(context);
|
|
const client = requireClientContext(context);
|
|
|
|
const emit = defineEmits<{
|
|
hidden: [];
|
|
}>();
|
|
|
|
const modalRef = ref<InstanceType<typeof ModalDialog>>();
|
|
const filter = ref(mapContext.value.filter ?? "");
|
|
|
|
const types = computed(() => getOrderedTypes(client.value.types));
|
|
|
|
function validateFilter(filter: string) {
|
|
return filterHasError(filter)?.message;
|
|
}
|
|
|
|
const isModified = computed(() => {
|
|
return filter.value != (mapContext.value.filter ?? "");
|
|
});
|
|
|
|
function save(): void {
|
|
mapContext.value.components.map.setFmFilter(filter.value || undefined);
|
|
modalRef.value?.modal.hide();
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<ModalDialog
|
|
title="Filter"
|
|
class="fm-edit-filter"
|
|
:isModified="isModified"
|
|
@submit="save"
|
|
:okLabel="isModified ? 'Apply' : undefined"
|
|
ref="modalRef"
|
|
@hidden="emit('hidden')"
|
|
>
|
|
<p>Here you can set an advanced expression to show/hide certain markers/lines based on their attributes. The filter expression only applies to your view of the map, but it can be persisted as part of a saved view or a shared link.</p>
|
|
|
|
<ValidatedField
|
|
:value="filter"
|
|
:validators="[
|
|
validateFilter
|
|
]"
|
|
:reportValid="!!filter"
|
|
immediate
|
|
>
|
|
<template #default="slotProps">
|
|
<textarea
|
|
class="form-control text-monospace"
|
|
v-model="filter"
|
|
rows="5"
|
|
:ref="slotProps.inputRef"
|
|
></textarea>
|
|
<div class="invalid-feedback" v-if="slotProps.validationError">
|
|
<pre>{{slotProps.validationError}}</pre>
|
|
</div>
|
|
</template>
|
|
</ValidatedField>
|
|
|
|
<hr />
|
|
|
|
<div class="fm-edit-filter-syntax">
|
|
<h3>Syntax</h3>
|
|
<table class="table table-condensed table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Variable</th>
|
|
<th>Description</th>
|
|
<th>Example</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>name</code></td>
|
|
<td>Marker/Line name</td>
|
|
<td><code>name == "Berlin"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>type</code></td>
|
|
<td><code>marker</code> / <code>line</code></td>
|
|
<td><code>type == "marker"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>typeId</code></td>
|
|
<td>
|
|
<span v-for="(type, idx) in types" :key="type.id">
|
|
<span v-if="idx != 0"> / </span> <code>{{type.id}}</code> <span class="text-break">({{type.name}})</span>
|
|
</span>
|
|
</td>
|
|
<td><code>typeId == {{types[0]?.id || 1}}</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>data.<field></code> / <code>prop(data, <field>)</code></td>
|
|
<td>
|
|
Field values (example: <code>data.Description</code> or <code>prop(data, "Description")</code>).<br />
|
|
For checkbox fields, the value is <code>0</code> (unchecked) or <code>1</code> (checked).
|
|
</td>
|
|
<td><code>lower(data.Description) ~= "camp"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>lat</code>, <code>lon</code></td>
|
|
<td>Marker coordinates</td>
|
|
<td><code>lat < 50</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>colour</code></td>
|
|
<td>Marker/line colour</td>
|
|
<td><code>colour == "ff0000"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>size</code></td>
|
|
<td>Marker size</td>
|
|
<td><code>size > 30</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>symbol</code></td>
|
|
<td>Marker icon</td>
|
|
<td><code>symbol == "accommodation_camping"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>shape</code></td>
|
|
<td>Marker shape</td>
|
|
<td><code>shape == "circle"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>ele</code></td>
|
|
<td>Marker elevation</td>
|
|
<td><code>ele > 500</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>mode</code></td>
|
|
<td>Line routing mode (<code>""</code> / <code>"car"</code> / <code>"bicycle"</code> / <code>"pedestrian"</code> / <code>"track"</code>)</td>
|
|
<td><code>mode in ("bicycle", "pedestrian")</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>width</code></td>
|
|
<td>Line width</td>
|
|
<td><code>width > 10</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>stroke</code></td>
|
|
<td>Line stroke (<code>`""`</code> (solid) / <code>`"dashed"`</code> / <code>"dotted"</code>)</td>
|
|
<td><code>shape == "dotted"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>distance</code></td>
|
|
<td>Line distance in kilometers</td>
|
|
<td><code>distance < 50</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>time</code></td>
|
|
<td>Line routing time in seconds</td>
|
|
<td><code>time > 3600</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>ascent</code>, <code>descent</code></td>
|
|
<td>Total ascent/descent of line</td>
|
|
<td><code>ascent > 1000</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>routePoints</code></td>
|
|
<td>Line point coordinates</td>
|
|
<td><code>routePoints.0.lon > 60 and routePoints.2.lat < 50</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<th>Operator</th>
|
|
<th>Description</th>
|
|
<th>Example</th>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>number</code></td>
|
|
<td>Numerical value</td>
|
|
<td><code>distance < 1.5</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>"text"</code></td>
|
|
<td>Text value</td>
|
|
<td><code>name == "Athens"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code>, <code>^</code></td>
|
|
<td>Mathematical operations (<code>%</code>: modulo, <code>^</code>: power)</td>
|
|
<td><code>distance / time > 30</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>and</code>, <code>or</code>, <code>not</code>, <code>()</code></td>
|
|
<td>Logical operators</td>
|
|
<td><code>not (size>10) or (type=="line" and length<=10)</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>? :</code></td>
|
|
<td>if/then/else operator</td>
|
|
<td><code>(type=="marker" ? size : width) > 10</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>==</code>, <code>!=</code>, <code><</code>, <code><=</code>, <code>></code>, <code>>=</code></td>
|
|
<td>Comparison (<code>!=</code>: not equal) (case sensitive)</td>
|
|
<td><code>type != "marker"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>in</code>, <code>not in</code></td>
|
|
<td>List operator (case sensitive)</td>
|
|
<td><code>typeId not in (1,2)</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>~=</code></td>
|
|
<td>Regular expression match (case sensitive)</td>
|
|
<td><code>name ~= "^[Cc]amp$"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>lower()</code></td>
|
|
<td>Convert to lower case</td>
|
|
<td><code>lower(name) ~= "untitled"</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>ceil()</code>, <code>floor()</code>, <code>round()</code></td>
|
|
<td>Round (<code>ceil</code>: up, <code>floor</code>: down)</td>
|
|
<td><code>floor(distance/100) == 5</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>abs()</code>, <code>log()</code>, <code>sqrt()</code></td>
|
|
<td>Mathematical functions</td>
|
|
<td><code>abs(lat) < 30</code></td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><code>min()</code>, <code>max()</code></td>
|
|
<td>Smallest/highest value</td>
|
|
<td><code>min(routePoints.0.lat,routePoints.1.lat) < 50</code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</ModalDialog>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.fm-edit-filter {
|
|
.modal-body.modal-body, form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
hr {
|
|
width: 100%;
|
|
}
|
|
|
|
.fm-edit-filter-syntax {
|
|
overflow: auto;
|
|
margin-right: -16px;
|
|
padding-right: 16px;
|
|
min-height: 150px;
|
|
max-width: 100%;
|
|
}
|
|
|
|
pre {
|
|
color: inherit;
|
|
font-size: inherit;
|
|
}
|
|
}
|
|
</style> |