Skip to content

Commit bd44e84

Browse files
feat(filter): migrate plugin WIP
This commit is at the moment work-in-progress. It will be changed multiple times in the future using amend.
1 parent 63116ad commit bd44e84

File tree

7 files changed

+332
-0
lines changed

7 files changed

+332
-0
lines changed

src/core/types/plugin.ts

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

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

8993
/** @internal */
9094
export type BundledPluginId =
95+
| typeof FilterPluginId
9196
| typeof FooterPluginId
9297
| typeof FullscreenPluginId
9398
| typeof GeoLocationPluginId
@@ -109,6 +114,7 @@ type GetPluginStore<
109114

110115
/** @internal */
111116
export type BundledPluginStores<T extends BundledPluginId> =
117+
| GetPluginStore<T, typeof FilterPluginId, typeof FilterStore>
112118
| GetPluginStore<T, typeof FooterPluginId, typeof FooterStore>
113119
| GetPluginStore<T, typeof FullscreenPluginId, typeof FullscreenStore>
114120
| GetPluginStore<T, typeof GeoLocationPluginId, typeof GeoLocationStore>
@@ -131,6 +137,7 @@ type GetPluginResources<
131137

132138
/** @internal */
133139
export type BundledPluginLocaleResources<T extends BundledPluginId> =
140+
| GetPluginResources<T, typeof FilterPluginId, typeof FilterResources>
134141
| GetPluginResources<T, typeof FooterPluginId, typeof FooterResources>
135142
| GetPluginResources<T, typeof FullscreenPluginId, typeof FullscreenResources>
136143
| GetPluginResources<
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<template>
2+
<p>Hier könnte Ihr Filter-Plugin existieren.</p>
3+
</template>
4+
5+
<script setup lang="ts">
6+
import { useFilterStore } from '../store'
7+
8+
// eslint-disable-next-line
9+
const filterStore = useFilterStore()
10+
</script>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { test as _test, vi } from 'vitest'
2+
import { mount, VueWrapper } from '@vue/test-utils'
3+
import { createTestingPinia } from '@pinia/testing'
4+
import { useFilterStore } from '../store'
5+
import FilterUI from './FilterUI.ce.vue'
6+
import { mockedT } from '@/test/utils/mockI18n'
7+
8+
/* eslint-disable no-empty-pattern */
9+
const test = _test.extend<{
10+
wrapper: VueWrapper
11+
store: ReturnType<typeof useFilterStore>
12+
}>({
13+
wrapper: async ({}, use) => {
14+
vi.mock('i18next', () => ({
15+
t: (key, { ns, context }) => `$t(${ns}:${key}_${context})`,
16+
}))
17+
const wrapper = mount(FilterUI, {
18+
global: {
19+
plugins: [createTestingPinia({ createSpy: vi.fn })],
20+
mocks: {
21+
$t: mockedT,
22+
},
23+
},
24+
})
25+
await use(wrapper)
26+
},
27+
store: async ({}, use) => {
28+
const store = useFilterStore()
29+
await use(store)
30+
},
31+
})
32+
/* eslint-enable no-empty-pattern */
33+
34+
// eslint-disable-next-line
35+
test('wip', async ({ wrapper, store }) => {
36+
// TODO
37+
})

src/plugins/filter/index.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* eslint-disable tsdoc/syntax */
2+
/**
3+
* @module \@polar/polar/plugins/filter
4+
*/
5+
/* eslint-enable tsdoc/syntax */
6+
7+
import component from './components/FilterUI.ce.vue'
8+
import locales from './locales'
9+
import { useFilterStore } from './store'
10+
import { PluginId, type FilterPluginOptions } from './types'
11+
import type { PluginContainer, PolarPluginStore } from '@/core'
12+
13+
/**
14+
* Creates a plugin which provides a filter for features.
15+
*
16+
* @returns Plugin for use with {@link addPlugin}
17+
*/
18+
export default function pluginFilter(
19+
options: FilterPluginOptions
20+
): PluginContainer {
21+
return {
22+
id: PluginId,
23+
component,
24+
locales,
25+
storeModule: useFilterStore as PolarPluginStore,
26+
options,
27+
}
28+
}
29+
30+
export * from './types'

src/plugins/filter/locales.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* eslint-disable tsdoc/syntax */
2+
/**
3+
* This is the documentation for the locales keys in the filter plugin.
4+
* These locales are *NOT* exported, but documented only.
5+
*
6+
* @module locales/plugins/filter
7+
*/
8+
/* eslint-enable tsdoc/syntax */
9+
10+
import type { Locale } from '@/core'
11+
12+
/**
13+
* German locales for filter plugin.
14+
* For overwriting these values, use the plugin's ID as namespace.
15+
*/
16+
export const resourcesDe = {} as const
17+
18+
/**
19+
* English locales for filter plugin.
20+
* For overwriting these values, use the plugin's ID as namespace.
21+
*/
22+
export const resourcesEn = {} as const
23+
24+
/**
25+
* Filter plugin locales.
26+
*
27+
* @privateRemarks
28+
* The first entry will be used as fallback.
29+
*
30+
* @internal
31+
*/
32+
const locales: Locale[] = [
33+
{
34+
type: 'de',
35+
resources: resourcesDe,
36+
},
37+
{
38+
type: 'en',
39+
resources: resourcesEn,
40+
},
41+
]
42+
43+
export default locales

src/plugins/filter/store.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/* eslint-disable tsdoc/syntax */
2+
/**
3+
* @module \@polar/polar/plugins/filter/store
4+
*/
5+
/* eslint-enable tsdoc/syntax */
6+
7+
import { acceptHMRUpdate, defineStore } from 'pinia'
8+
import { computed } from 'vue'
9+
import type { Reactive } from 'vue'
10+
import { PluginId, type FilterPluginOptions } from './types'
11+
import { useCoreStore } from '@/core/stores/export'
12+
13+
/* eslint-disable tsdoc/syntax */
14+
/**
15+
* @function
16+
*
17+
* Plugin store for filtering features.
18+
*/
19+
/* eslint-enable tsdoc/syntax */
20+
export const useFilterStore = defineStore('plugins/filter', () => {
21+
const coreStore = useCoreStore()
22+
23+
// eslint-disable-next-line
24+
const configuration = computed(
25+
() => coreStore.configuration[PluginId] as FilterPluginOptions
26+
)
27+
28+
function setupPlugin() {}
29+
30+
function teardownPlugin() {}
31+
32+
return {
33+
/** @internal */
34+
setupPlugin,
35+
36+
/** @internal */
37+
teardownPlugin,
38+
}
39+
})
40+
41+
if (import.meta.vitest) {
42+
const { test: _test, vi } = import.meta.vitest
43+
const { createPinia, setActivePinia } = await import('pinia')
44+
const { reactive } = await import('vue')
45+
const useCoreStoreFile = await import('@/core/stores/export')
46+
47+
/* eslint-disable no-empty-pattern */
48+
const test = _test.extend<{
49+
coreStore: Reactive<Record<string, unknown>>
50+
store: ReturnType<typeof useFilterStore>
51+
}>({
52+
coreStore: [
53+
async ({}, use) => {
54+
const coreStore = reactive({
55+
configuration: { [PluginId]: {} },
56+
})
57+
// @ts-expect-error | Mocking useCoreStore
58+
vi.spyOn(useCoreStoreFile, 'useCoreStore').mockReturnValue(coreStore)
59+
await use(coreStore)
60+
},
61+
{ auto: true },
62+
],
63+
store: async ({}, use) => {
64+
setActivePinia(createPinia())
65+
const store = useFilterStore()
66+
store.setupPlugin()
67+
await use(store)
68+
store.teardownPlugin()
69+
},
70+
})
71+
/* eslint-enable no-empty-pattern */
72+
73+
// eslint-disable-next-line
74+
test('wip', ({ store, coreStore }) => {
75+
// TODO
76+
})
77+
}
78+
79+
if (import.meta.hot) {
80+
import.meta.hot.accept(acceptHMRUpdate(useFilterStore, import.meta.hot))
81+
}

src/plugins/filter/types.ts

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import type { PluginOptions } from '@/core'
2+
3+
/**
4+
* Plugin identifier.
5+
*/
6+
export const PluginId = 'filter'
7+
8+
/**
9+
* Category-based filter configuration for a layer.
10+
*/
11+
export interface Category {
12+
/**
13+
* Known values for the target property to filter by.
14+
* Values not listed here cannot be filtered.
15+
*
16+
* @remarks
17+
* The values listed here can be localized.
18+
* ```
19+
* filter: {
20+
* category: {
21+
* haus: {
22+
* shed: 'Schuppen',
23+
* mansion: 'Villa',
24+
* fortress: 'Festung',
25+
* },
26+
* },
27+
* },
28+
* ```
29+
*
30+
* @example ['shed', 'mansion', 'fortress']
31+
*/
32+
knownValues: string[]
33+
34+
/**
35+
* Key of the feature property to filter by.
36+
*/
37+
targetProperty: string
38+
39+
/**
40+
* If `true`, a checkbox is provided to enable or disable all `knownValues` at once.
41+
*
42+
* @example true
43+
* @defaultValue false
44+
*/
45+
selectAll?: boolean
46+
}
47+
48+
/**
49+
* Time-based filter configuration for a layer.
50+
*/
51+
export interface Time {
52+
/**
53+
* Key of the feature property to filter by.
54+
*/
55+
targetProperty: string
56+
57+
/**
58+
* Defines if the time filter is freely selectable by the user.
59+
* If set to `'until'`, every time range until the current day (inclusive) can be selected.
60+
* If set to `'from'`, every time range from the current day (inclusive) can be selected.
61+
* If not set, this feature is disabled.
62+
*
63+
* @example 'until'
64+
*/
65+
freeSelection?: 'until' | 'from'
66+
67+
/**
68+
* Configuration for preset time ranges in the past.
69+
* A configuration of `[5, 10]` adds the options `Last 5 days` and `Last 10 days`.
70+
*
71+
* @example [7, 30, 90]
72+
*/
73+
last?: number[]
74+
75+
/**
76+
* Configuration for preset time ranges in the future.
77+
* A configuration of `[5, 10]` adds the options `Next 5 days` and `Next 10 days`.
78+
*
79+
* @example [7, 30, 90]
80+
*/
81+
next?: number[]
82+
83+
/**
84+
* A pattern that specifies the date format used in the feature properties.
85+
* The pattern definition allows the following tokens:
86+
* - `YYYY`: 4-digit year
87+
* - `MM`: 2-digit month (01-12)
88+
* - `DD`: 2-digit day of month (01-31)
89+
* - `-`: ignored character
90+
*
91+
* @privateRemarks
92+
* All characters that are not tokens are handled as ignored characters.
93+
* This behavior may change in future versions without a breaking change!
94+
*
95+
* @example '--YYYYDD-MM'
96+
* @defaultValue 'YYYY-MM-DD'
97+
*/
98+
pattern?: string
99+
}
100+
101+
/**
102+
* Filter configuration for a layer.
103+
*/
104+
export interface FilterConfiguration {
105+
/**
106+
* A definition of different categories to filter features based on their properties.
107+
*/
108+
categories?: Category[]
109+
110+
/**
111+
* Filter features based on a time property.
112+
*/
113+
time?: Time
114+
}
115+
116+
/**
117+
* Plugin options for filter plugin.
118+
*/
119+
export interface FilterPluginOptions extends PluginOptions {
120+
/**
121+
* Maps a layer ID to its filter configuration.
122+
*/
123+
layers: Record<string, FilterConfiguration>
124+
}

0 commit comments

Comments
 (0)