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.
*/