diff --git a/examples/snowbox/index.html b/examples/snowbox/index.html index 4358f22741..d72fb0e112 100644 --- a/examples/snowbox/index.html +++ b/examples/snowbox/index.html @@ -61,6 +61,7 @@

POLAR map client

+
Coordinates of currently selected feature:
diff --git a/examples/snowbox/index.js b/examples/snowbox/index.js index 764b14f1e5..7d4014b7ff 100644 --- a/examples/snowbox/index.js +++ b/examples/snowbox/index.js @@ -394,6 +394,7 @@ addPlugin( layoutTag: 'BOTTOM_LEFT', }) ) +let filterItem addPlugin( map, pluginIconMenu({ @@ -409,7 +410,7 @@ addPlugin( plugin: pluginLayerChooser({}), }, ], - [ + (filterItem = [ { plugin: pluginFilter({ layers: { @@ -473,7 +474,7 @@ addPlugin( }, }), }, - ], + ]), [ { plugin: pluginGeoLocation({ @@ -583,3 +584,17 @@ document colorScheme = colorScheme === 'light' ? 'dark' : 'light' updateState(map, 'core', 'colorScheme', colorScheme) }) + +let hasFilter = true +document + .getElementById('toggle-filter') + .addEventListener('click', ({ target }) => { + const store = getStore(map, 'iconMenu') + if (hasFilter) { + store.removePlugin('filter') + hasFilter = false + } else { + store.addPlugin(filterItem) + hasFilter = true + } + }) diff --git a/src/plugins/filter/store.ts b/src/plugins/filter/store.ts index 4524aa6c8a..3211e7f478 100644 --- a/src/plugins/filter/store.ts +++ b/src/plugins/filter/store.ts @@ -50,10 +50,12 @@ export const useFilterStore = defineStore('plugins/filter', () => { { deep: true, immediate: true } ) ) + teardownCallbacks.push(callback) }) } function teardownPlugin() { + filterMainStore.state = {} teardownCallbacks.forEach((callback) => { callback() }) diff --git a/src/plugins/iconMenu/components/NineRegionsButton.ce.vue b/src/plugins/iconMenu/components/NineRegionsButton.ce.vue index 8bb25a50d5..cd22f8db35 100644 --- a/src/plugins/iconMenu/components/NineRegionsButton.ce.vue +++ b/src/plugins/iconMenu/components/NineRegionsButton.ce.vue @@ -13,7 +13,6 @@ import { storeToRefs } from 'pinia' import { computed, inject } from 'vue' import PolarIconButton from '@/components/PolarIconButton.ce.vue' -import { useCoreStore } from '@/core/stores' import { useIconMenuStore } from '../store' @@ -30,13 +29,7 @@ const active = computed(() => open.value === props.id) const updateMaxWidth = inject('updateMaxWidth') as () => void function toggle() { - if (open.value === props.id) { - open.value = null - useCoreStore().setMoveHandle(null) - } else { - open.value = props.id - iconMenuStore.openInMoveHandle(props.id) - } + iconMenuStore.openMenuById(open.value === props.id ? null : props.id) updateMaxWidth() } diff --git a/src/plugins/iconMenu/components/StandardFocusMenu.ce.vue b/src/plugins/iconMenu/components/StandardFocusMenu.ce.vue index 9080f88540..03d03c0aa2 100644 --- a/src/plugins/iconMenu/components/StandardFocusMenu.ce.vue +++ b/src/plugins/iconMenu/components/StandardFocusMenu.ce.vue @@ -75,16 +75,14 @@ const maxHeight = computed(() => function toggle(id: string) { if (iconMenuStore.focusOpen === id) { - iconMenuStore.focusOpen = null + iconMenuStore.openFocusMenuById(null) pluginComponent.value = null - coreStore.setMoveHandle(null) } else { - iconMenuStore.focusOpen = id + iconMenuStore.openFocusMenuById(id) pluginComponent.value = markRaw( (props.menus.find(({ plugin }) => plugin.id === id) as Menu).plugin .component as Component ) - iconMenuStore.openInMoveHandle(id, true) } } diff --git a/src/plugins/iconMenu/components/StandardMenuList.ce.vue b/src/plugins/iconMenu/components/StandardMenuList.ce.vue index 1b0df4448f..8e11f5abde 100644 --- a/src/plugins/iconMenu/components/StandardMenuList.ce.vue +++ b/src/plugins/iconMenu/components/StandardMenuList.ce.vue @@ -122,13 +122,7 @@ function updateMaxWidth() { } function toggle(id: string) { - if (open.value === id) { - open.value = null - coreStore.setMoveHandle(null) - } else { - open.value = id - iconMenuStore.openInMoveHandle(id) - } + iconMenuStore.openMenuById(open.value === id ? null : id) updateMaxWidth() } diff --git a/src/plugins/iconMenu/store.ts b/src/plugins/iconMenu/store.ts index f0fd4ac858..54eba7fd3c 100644 --- a/src/plugins/iconMenu/store.ts +++ b/src/plugins/iconMenu/store.ts @@ -4,14 +4,12 @@ */ /* eslint-enable tsdoc/syntax */ -import type { Component } from 'vue' -import type { Icon } from '@/core' -import type { Menu } from './types' +import type { FocusMenu, Menu } from './types' import { toMerged } from 'es-toolkit' import { t } from 'i18next' import { acceptHMRUpdate, defineStore } from 'pinia' -import { computed, markRaw, ref, toRaw } from 'vue' +import { computed, markRaw, readonly, ref, toRaw } from 'vue' import { useCoreStore } from '@/core/stores' @@ -28,7 +26,7 @@ export const useIconMenuStore = defineStore('plugins/iconMenu', () => { const coreStore = useCoreStore() const menus = ref>([]) - const focusMenus = ref<(Menu & { icon: Icon })[]>([]) + const focusMenus = ref([]) const open = ref(null) const focusOpen = ref(null) @@ -42,6 +40,50 @@ export const useIconMenuStore = defineStore('plugins/iconMenu', () => { () => coreStore.configuration.iconMenu?.layoutTag ?? '' ) + function isPluginInIconMenu(pluginId: string) { + const display = coreStore.configuration[pluginId]?.displayComponent + return typeof display === 'boolean' ? display : true + } + function addPlugin(menuGroup: Menu[]) { + const filteredMenuGroup = menuGroup.filter(({ plugin: { id } }) => + isPluginInIconMenu(id) + ) + filteredMenuGroup.forEach(({ plugin }) => { + if (plugin.component) { + markRaw(toRaw(plugin.component)) + } + coreStore.addPlugin(toMerged(plugin, { independent: false })) + }) + menus.value.push(filteredMenuGroup) + } + function addFocusPlugin(menu: FocusMenu) { + if (!isPluginInIconMenu(menu.plugin.id)) { + return + } + if (menu.plugin.component) { + markRaw(toRaw(menu.plugin.component)) + } + coreStore.addPlugin(toMerged(menu.plugin, { independent: false })) + focusMenus.value.push(menu) + } + function removePlugin(pluginId: string) { + if (open.value === pluginId) { + open.value = null + } + menus.value = menus.value + .map((menuGroup) => + menuGroup.filter(({ plugin: { id } }) => id !== pluginId) + ) + .filter((menuGroup) => menuGroup.length > 0) + if (focusOpen.value === pluginId) { + focusOpen.value = null + } + focusMenus.value = focusMenus.value.filter( + ({ plugin: { id } }) => id !== pluginId + ) + coreStore.removePlugin(pluginId) + } + const visibleMenus = computed(() => menus.value.map((menuGroup) => menuGroup.filter( @@ -56,41 +98,12 @@ export const useIconMenuStore = defineStore('plugins/iconMenu', () => { ) function setupPlugin() { - // Components are marked raw so they themselves are not made reactive - menus.value = (coreStore.configuration.iconMenu?.menus || []).map( - (menuGroup) => - menuGroup - .filter(({ plugin: { id } }) => { - const display = coreStore.configuration[id]?.displayComponent - return typeof display === 'boolean' ? display : true - }) - .map((menuItem) => ({ - ...menuItem, - plugin: { - ...menuItem.plugin, - component: markRaw(toRaw(menuItem.plugin.component as Component)), - }, - })) - ) - focusMenus.value = (coreStore.configuration.iconMenu?.focusMenus || []) - .filter(({ plugin: { id } }) => { - const display = coreStore.configuration[id]?.displayComponent - return typeof display === 'boolean' ? display : true - }) - .map((menuItem) => ({ - ...menuItem, - plugin: { - ...menuItem.plugin, - component: markRaw(toRaw(menuItem.plugin.component as Component)), - }, - })) - - menus.value - .concat(focusMenus.value) - .flat() - .forEach(({ plugin }) => { - coreStore.addPlugin(toMerged(plugin, { independent: false })) - }) + ;(coreStore.configuration.iconMenu?.menus || []).forEach((menuGroup) => { + addPlugin(menuGroup) + }) + ;(coreStore.configuration.iconMenu?.focusMenus || []).forEach((menu) => { + addFocusPlugin(menu) + }) const initiallyOpen = coreStore.configuration.iconMenu?.initiallyOpen if ( @@ -112,21 +125,25 @@ export const useIconMenuStore = defineStore('plugins/iconMenu', () => { } function teardownPlugin() {} - function openMenuById(openId: string) { + function openMenuById(openId: string | null) { const entry = menus.value.flat().find(({ plugin: { id } }) => id === openId) - - if (entry) { + if (openId && entry) { open.value = openId openInMoveHandle(openId) + } else { + open.value = null + coreStore.setMoveHandle(null) } } - function openFocusMenuById(openId: string) { + function openFocusMenuById(openId: string | null) { const entry = focusMenus.value.find(({ plugin: { id } }) => id === openId) - - if (entry) { + if (openId && entry) { focusOpen.value = openId openInMoveHandle(openId, true) + } else { + focusOpen.value = null + coreStore.setMoveHandle(null) } } @@ -172,13 +189,55 @@ export const useIconMenuStore = defineStore('plugins/iconMenu', () => { return { visibleMenus, visibleFocusMenus, - open, - focusOpen, + + /** + * Determines which menu is currently open. + * + * To change the open menu, use {@link openMenuById}. + * + * @readonly + * @alpha + */ + open: readonly(open), + + /** + * Determines which focus menu is currently open. + * + * To change the open focus menu, use {@link openFocusMenuById}. + * + * @readonly + * @alpha + */ + focusOpen: readonly(focusOpen), + buttonComponent, - openInMoveHandle, openMenuById, openFocusMenuById, + /** + * Appends a group of plugins to the icon menu. + * + * @param menu - The menu item group to add. + * @alpha + */ + addPlugin, + + /** + * Appends a plugin to the icon menu as a focus menu. + * + * @param menu - The focus menu item to add. + * @alpha + */ + addFocusPlugin, + + /** + * Removes a plugin from the icon menu. + * + * @param pluginId - The ID of the plugin to remove. + * @alpha + */ + removePlugin, + /** @alpha */ layoutTag, diff --git a/src/plugins/iconMenu/types.ts b/src/plugins/iconMenu/types.ts index d8de83629e..ed64916d05 100644 --- a/src/plugins/iconMenu/types.ts +++ b/src/plugins/iconMenu/types.ts @@ -26,6 +26,10 @@ export interface Menu { icon?: Icon } +export interface FocusMenu extends Menu { + icon: Icon +} + /** * Plugin options for iconMenu plugin. */