Skip to content

Commit 2fa72f1

Browse files
feat(filter): migrate plugin
1 parent 07048a5 commit 2fa72f1

39 files changed

+1549
-1398
lines changed

examples/snowbox/index.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
subscribe,
77
updateState,
88
} from '@polar/polar'
9+
import pluginFilter from '@polar/polar/plugins/filter'
910
import pluginFooter from '@polar/polar/plugins/footer'
1011
import pluginFullscreen from '@polar/polar/plugins/fullscreen'
1112
import pluginGeoLocation from '@polar/polar/plugins/geoLocation'
@@ -178,6 +179,29 @@ const map = await createMap(
178179
{
179180
type: 'de',
180181
resources: {
182+
filter: {
183+
layer: {
184+
[reports]: {
185+
category: {
186+
skat: {
187+
title: 'Schadensart',
188+
knownValue: {
189+
100: 'Wege und Straßen',
190+
101: 'Schlagloch und Wegeschaden',
191+
102: 'Verunreinigung und Vandalismus',
192+
},
193+
},
194+
statu: {
195+
title: 'Bearbeitungsstatus',
196+
knownValue: {
197+
'In Bearbeitung': 'In Bearbeitung',
198+
abgeschlossen: 'Abgeschlossen',
199+
},
200+
},
201+
},
202+
},
203+
},
204+
},
181205
fullscreen: {
182206
button: {
183207
label_on: 'Mach groß',
@@ -311,6 +335,55 @@ addPlugin(
311335
icon: 'kern-icon-fill--share',
312336
},
313337
],
338+
[
339+
{
340+
plugin: pluginFilter({
341+
layers: {
342+
[reports]: {
343+
categories: [
344+
{
345+
targetProperty: 'skat',
346+
knownValues: [
347+
{
348+
value: '100',
349+
icon: 'kern-icon--road',
350+
},
351+
{
352+
value: '101',
353+
icon: 'kern-icon--remove-road',
354+
},
355+
{
356+
value: '102',
357+
icon: 'kern-icon--destruction',
358+
},
359+
],
360+
selectAll: true,
361+
},
362+
{
363+
targetProperty: 'statu',
364+
knownValues: [
365+
{
366+
value: 'In Bearbeitung',
367+
icon: 'kern-icon--assignment',
368+
},
369+
{
370+
value: 'abgeschlossen',
371+
icon: 'kern-icon--check',
372+
},
373+
],
374+
},
375+
],
376+
time: {
377+
targetProperty: 'start',
378+
freeSelection: 'until',
379+
last: [0, 7, 30],
380+
pattern: 'YYYYMMDD',
381+
},
382+
},
383+
},
384+
}),
385+
},
386+
],
314387
[
315388
{
316389
plugin: pluginLayerChooser({}),
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<template>
2+
<button
3+
class="kern-btn kern-btn--block kern-btn--tertiary"
4+
@click="emit('click')"
5+
>
6+
<span
7+
v-if="props.icon"
8+
:class="{ 'kern-icon': true, [props.icon]: true }"
9+
aria-hidden="true"
10+
></span>
11+
<span class="kern-label">{{ props.label }}</span>
12+
</button>
13+
</template>
14+
15+
<script setup lang="ts">
16+
import type { Icon } from '@/core'
17+
18+
const props = defineProps<{
19+
icon?: Icon | false
20+
label: string
21+
}>()
22+
23+
const emit = defineEmits<{
24+
click: []
25+
}>()
26+
</script>
27+
28+
<style scoped>
29+
.kern-btn--tertiary {
30+
background-color: #edf1fa;
31+
justify-content: left;
32+
33+
.kern-label {
34+
text-decoration: none;
35+
}
36+
}
37+
</style>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<template>
2+
<input :id="id" v-model="model" type="checkbox" />
3+
<label :for="id" class="kern-btn kern-btn--block kern-btn--tertiary">
4+
<span
5+
v-if="props.icon"
6+
:class="{ 'kern-icon': true, [props.icon]: true }"
7+
aria-hidden="true"
8+
></span>
9+
<span class="kern-label">{{ props.label }}</span>
10+
</label>
11+
</template>
12+
13+
<script setup lang="ts">
14+
import { useId } from 'vue'
15+
import type { Icon } from '@/core'
16+
17+
const props = defineProps<{
18+
icon?: Icon | false
19+
label: string
20+
}>()
21+
22+
const model = defineModel<boolean>({ required: true })
23+
24+
const id = useId()
25+
</script>
26+
27+
<style scoped>
28+
.kern-btn--tertiary {
29+
background-color: #edf1fa;
30+
justify-content: left;
31+
32+
.kern-label {
33+
text-decoration: none;
34+
}
35+
}
36+
37+
input[type='checkbox'] {
38+
display: none;
39+
40+
&:checked + label {
41+
border: var(--kern-metric-border-width-default) solid
42+
var(--kern-color-action-default);
43+
}
44+
}
45+
</style>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<template>
2+
<template v-for="item of props.items" :key="item.value">
3+
<input
4+
:id="id + '*' + item.value"
5+
v-model="model"
6+
type="radio"
7+
:name="id"
8+
:value="item.value"
9+
/>
10+
<label
11+
:for="id + '*' + item.value"
12+
class="kern-btn kern-btn--block kern-btn--tertiary"
13+
>
14+
<span
15+
v-if="item.icon"
16+
:class="{ 'kern-icon': true, [item.icon]: true }"
17+
aria-hidden="true"
18+
></span>
19+
<span class="kern-label">{{ item.label }}</span>
20+
</label>
21+
</template>
22+
</template>
23+
24+
<script setup lang="ts">
25+
import { useId } from 'vue'
26+
import type { Icon } from '@/core'
27+
28+
const props = defineProps<{
29+
items: {
30+
value: string
31+
label: string
32+
icon?: Icon | false
33+
}[]
34+
}>()
35+
36+
const model = defineModel<string>({ required: true })
37+
38+
const id = useId()
39+
</script>
40+
41+
<style scoped>
42+
.kern-btn--tertiary {
43+
background-color: #edf1fa;
44+
justify-content: left;
45+
46+
.kern-label {
47+
text-decoration: none;
48+
}
49+
}
50+
51+
input[type='radio'] {
52+
display: none;
53+
54+
&:checked + label {
55+
border: var(--kern-metric-border-width-default) solid
56+
var(--kern-color-action-default);
57+
}
58+
}
59+
</style>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<template>
2+
<input
3+
v-model="inputModel"
4+
type="date"
5+
class="kern-form-input__input"
6+
:min="dateToString(props.min ?? null)"
7+
:max="dateToString(props.max ?? null)"
8+
/>
9+
</template>
10+
11+
<script setup lang="ts">
12+
import { computed } from 'vue'
13+
import { dateToString, stringToDate } from '@/lib/dateUtils'
14+
15+
const props = defineProps<{
16+
min?: Date
17+
max?: Date
18+
}>()
19+
20+
const model = defineModel<Date | null>({ required: true })
21+
22+
const inputModel = computed({
23+
get: () => dateToString(model.value),
24+
set: (value) => {
25+
model.value = stringToDate(value)
26+
},
27+
})
28+
</script>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<template>
2+
<div class="range-picker">
3+
<KernDatePicker v-model="start" v-bind="props" />
4+
5+
<KernDatePicker v-model="end" v-bind="props" />
6+
</div>
7+
</template>
8+
9+
<script setup lang="ts">
10+
import KernDatePicker from './KernDatePicker.ce.vue'
11+
12+
const props = defineProps<{
13+
min?: Date
14+
max?: Date
15+
}>()
16+
17+
const start = defineModel<Date | null>('start', { required: true })
18+
const end = defineModel<Date | null>('end', { required: true })
19+
</script>
20+
21+
<style scoped>
22+
.range-picker {
23+
display: flex;
24+
align-items: center;
25+
justify-content: space-between;
26+
margin: 0.3em 0;
27+
}
28+
</style>

src/core/types/plugin.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import type { NineLayoutTag } from '../utils/NineLayoutTag'
44
import type { Locale } from './locales'
55
import type { Icon } from './theme'
66

7+
import type { PluginId as FilterPluginId } from '@/plugins/filter'
8+
import type { useFilterStore as FilterStore } from '@/plugins/filter/store'
9+
import type { resourcesEn as FilterResources } from '@/plugins/filter/locales'
10+
711
import type { PluginId as FooterPluginId } from '@/plugins/footer'
812
import type { useFooterStore as FooterStore } from '@/plugins/footer/store'
913
import type { resourcesEn as FooterResources } from '@/plugins/footer/locales'
@@ -89,6 +93,7 @@ export type PolarPluginStore<
8993

9094
/** @internal */
9195
export type BundledPluginId =
96+
| typeof FilterPluginId
9297
| typeof FooterPluginId
9398
| typeof FullscreenPluginId
9499
| typeof GeoLocationPluginId
@@ -110,6 +115,7 @@ type GetPluginStore<
110115

111116
/** @internal */
112117
export type BundledPluginStores<T extends BundledPluginId> =
118+
| GetPluginStore<T, typeof FilterPluginId, typeof FilterStore>
113119
| GetPluginStore<T, typeof FooterPluginId, typeof FooterStore>
114120
| GetPluginStore<T, typeof FullscreenPluginId, typeof FullscreenStore>
115121
| GetPluginStore<T, typeof GeoLocationPluginId, typeof GeoLocationStore>
@@ -132,6 +138,7 @@ type GetPluginResources<
132138

133139
/** @internal */
134140
export type BundledPluginLocaleResources<T extends BundledPluginId> =
141+
| GetPluginResources<T, typeof FilterPluginId, typeof FilterResources>
135142
| GetPluginResources<T, typeof FooterPluginId, typeof FooterResources>
136143
| GetPluginResources<T, typeof FullscreenPluginId, typeof FullscreenResources>
137144
| GetPluginResources<

src/lib/dateUtils.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export function dateToString(date: Date | null) {
2+
if (date === null) {
3+
return ''
4+
}
5+
return [date.getFullYear(), date.getMonth() + 1, date.getDate()]
6+
.map((v) => v.toString().padStart(2, '0'))
7+
.join('-')
8+
}
9+
10+
export function stringToDate(date: string) {
11+
if (date.length === 0) {
12+
return null
13+
}
14+
const [y, m, d] = date.split('-')
15+
return new Date(Number(y), Number(m) - 1, Number(d))
16+
}

src/lib/invisibleStyle.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,25 @@ export const isInvisible = (feature: Feature) =>
3232
*/
3333
export const isVisible = (feature: Feature) =>
3434
feature.getStyle() !== InvisibleStyle
35+
36+
/**
37+
* Hides a feature.
38+
*
39+
* @param feature - The feature to hide.
40+
*/
41+
export const hideFeature = (feature: Feature) => {
42+
if (isVisible(feature)) {
43+
feature.setStyle(InvisibleStyle)
44+
}
45+
}
46+
47+
/**
48+
* Shows a feature.
49+
*
50+
* @param feature - The feature to show.
51+
*/
52+
export const showFeature = (feature: Feature) => {
53+
if (isInvisible(feature)) {
54+
feature.setStyle()
55+
}
56+
}

0 commit comments

Comments
 (0)