@conduction/nextcloud-vue 2.101.0 → 2.102.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +22 -0
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +8 -6
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  6. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +29 -4
  7. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  8. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +9 -1
  9. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  10. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  11. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +20 -0
  12. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  13. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +1 -1
  14. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  15. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +70 -4
  16. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  17. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +6 -4
  18. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  19. package/dist/esm/components/CnIndexPage/useNamedSource.js +26 -2
  20. package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
  21. package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue.js +1 -1
  22. package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue2.js +25 -1
  23. package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue2.js.map +1 -1
  24. package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue3.js.map +1 -1
  25. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue.js +1 -1
  26. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +106 -4
  27. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
  28. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +70 -25
  29. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
  30. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
  31. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +2 -23
  32. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  33. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  34. package/dist/esm/composables/indexSources.js +9 -0
  35. package/dist/esm/composables/indexSources.js.map +1 -1
  36. package/dist/esm/l10n/en.json.js +5 -1
  37. package/dist/esm/l10n/en.json.js.map +1 -1
  38. package/dist/esm/l10n/nl.json.js +5 -1
  39. package/dist/esm/l10n/nl.json.js.map +1 -1
  40. package/dist/esm/library-style.js +1 -1
  41. package/dist/esm/nextcloud-vue.css +232 -187
  42. package/dist/esm/utils/builtInFormatters.js +24 -4
  43. package/dist/esm/utils/builtInFormatters.js.map +1 -1
  44. package/dist/esm/utils/rowClassRules.js +43 -0
  45. package/dist/esm/utils/rowClassRules.js.map +1 -0
  46. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  47. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  48. package/dist/esm/utils/visibleWhen.js +19 -2
  49. package/dist/esm/utils/visibleWhen.js.map +1 -1
  50. package/dist/nextcloud-vue.cjs.js +1464 -1053
  51. package/dist/nextcloud-vue.cjs.js.map +2 -2
  52. package/dist/nextcloud-vue.css +232 -187
  53. package/l10n/en.json +5 -1
  54. package/l10n/nl.json +5 -1
  55. package/package.json +1 -1
  56. package/src/components/CnActionsBar/CnActionsBar.md +1 -0
  57. package/src/components/CnActionsBar/CnActionsBar.vue +31 -4
  58. package/src/components/CnChartWidget/CnChartWidget.md +3 -2
  59. package/src/components/CnChartWidget/CnChartWidget.vue +42 -4
  60. package/src/components/CnDashboardPage/CnDashboardPage.vue +21 -1
  61. package/src/components/CnIndexPage/CnIndexPage.md +5 -1
  62. package/src/components/CnIndexPage/CnIndexPage.vue +74 -6
  63. package/src/components/CnIndexPage/useNamedSource.js +26 -2
  64. package/src/components/CnMenuWidget/CnMenuWidget.vue +25 -1
  65. package/src/components/CnQuickFilterBar/CnQuickFilterBar.md +1 -0
  66. package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +159 -5
  67. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +2 -23
  68. package/src/composables/indexSources.js +9 -0
  69. package/src/css/look-board-index.css +10 -0
  70. package/src/schemas/app-manifest-v2.schema.json +55 -3
  71. package/src/utils/builtInFormatters.js +24 -4
  72. package/src/utils/rowClassRules.js +39 -0
  73. package/src/utils/validateManifestV2.compiled.js +3 -3
  74. package/src/utils/visibleWhen.js +19 -2
@@ -106,6 +106,9 @@ function useNamedSource(props, options = {}) {
106
106
  const tabs = manifestTabs || sourceTabs;
107
107
  const activeIndex = options.activeQuickFilterIndex || null;
108
108
  const activeFilters = options.activeFilters || null;
109
+ // A source that pages (`pages: true`) gets the page as `limit` and
110
+ // `offset`, from the pager the page draws for it.
111
+ const paging = source.pages === true ? (options.paging || null) : null;
109
112
 
110
113
  // Seed the default tab before the watcher below registers: the mount load
111
114
  // already carries this tab's filter, so the seed must not fire a second
@@ -166,6 +169,7 @@ function useNamedSource(props, options = {}) {
166
169
  activeFilters ? activeFilters.value : null,
167
170
  props.entitySource,
168
171
  ),
172
+ ...(paging ? { limit: paging.value.limit, offset: (paging.value.page - 1) * paging.value.limit } : {}),
169
173
  },
170
174
  tokenCtx(),
171
175
  );
@@ -183,15 +187,35 @@ function useNamedSource(props, options = {}) {
183
187
 
184
188
  // Registered AFTER the seed above, so only a person switching tabs
185
189
  // reloads. Without tabs there is nothing to watch.
190
+ /**
191
+ * Reload for a new query: on a paging source back to page 1, which
192
+ * reloads through the paging watcher below.
193
+ *
194
+ * @return {void}
195
+ */
196
+ function reloadFromFirstPage() {
197
+ if (paging && paging.value.page !== 1) {
198
+ paging.value = { ...paging.value, page: 1 };
199
+ return
200
+ }
201
+ loadActive();
202
+ }
203
+
186
204
  if (tabs && activeIndex) {
187
- watch(activeIndex, loadActive);
205
+ watch(activeIndex, reloadFromFirstPage);
188
206
  }
189
207
 
190
208
  // A sidebar choice is a new QUERY, not a new view of the page already
191
209
  // fetched, so it reloads. `deep` because the map is rewritten key by key
192
210
  // and a shallow watch misses a second value added to the same field.
193
211
  if (activeFilters) {
194
- watch(activeFilters, loadActive, { deep: true });
212
+ watch(activeFilters, reloadFromFirstPage, { deep: true });
213
+ }
214
+
215
+ // A new page, or a new page size, is a new request. A new tab or filter
216
+ // first returns to page 1, which reloads through here.
217
+ if (paging) {
218
+ watch(paging, loadActive, { deep: true });
195
219
  }
196
220
 
197
221
  // No manual invalidation tick here on purpose. The adapters read from a
@@ -1 +1 @@
1
- {"version":3,"file":"useNamedSource.js","sources":["../../../../src/components/CnIndexPage/useNamedSource.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n */\n\nimport { computed, inject, onMounted, watch } from 'vue'\nimport { resolveIndexSource } from '../../composables/indexSources.js'\nimport { withPersonalLenses } from '../../utils/personalLenses.js'\nimport { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { searchFieldParams } from '../../utils/searchFieldParams.js'\n\n/**\n * Third data mode for CnIndexPage: a NAMED source.\n *\n * The page already knew two ways to get rows — fetch OpenRegister objects from\n * `register` + `schema`, or accept them as `:objects`. Neither reaches a list\n * that is not an OpenRegister object and not already in a parent's hands, so\n * those lists became `type: \"custom\"` pages whose only real job was loading\n * rows from somewhere else.\n *\n * `source` closes that: the manifest names a source and the index loads it.\n *\n * Precedence is deliberate and narrow. NON-EMPTY `:objects` still wins, so a\n * parent that hands rows down is never overridden by a source it did not ask\n * for; and a source wins over `register`/`schema`, because naming both is a\n * contradiction and silently self-fetching would render the WRONG list rather\n * than an obviously empty one.\n *\n * Note the test is \"has rows\", not \"was the prop passed\" — `useSelfFetchList`\n * uses the latter. The difference only shows for `:objects=\"[]\"` alongside a\n * source, where the source wins; that is the useful reading of an empty array\n * next to an explicit source name.\n *\n * Quick filters (cn-tasks-entity-source): a source may supply its own tabs\n * (`quickFilters`, same `{ label, filter, default? }` shape the manifest\n * uses). A manifest that declares `config.quickFilters` still wins. The\n * active tab's filter is merged OVER `sourceConfig` into every load, tab\n * winning on a colliding key, the same precedence self-fetch gives its tabs.\n * The default tab is seeded BEFORE the watcher registers, so mounting issues\n * exactly one request. Single mode only: `quickFilterMultiple` unions filter\n * maps for an OpenRegister query, which a source loader has no grammar for.\n *\n * 🔑 TOKENS RESOLVE HERE TOO. A tab filter is a filter, and a manifest author\n * writing `{ dueBefore: '@today+7d' }` has no way of knowing that the page\n * behind the label happens to be a named source rather than a self-fetch.\n * Without this the literal string `@today+7d` went over the wire, and a\n * source's server either rejected it or, worse, quietly answered the wrong\n * window: dossiq's \"Due this week\" lens is exactly that shape. Self-fetch\n * resolved tokens from the first day; this closes the gap rather than\n * teaching manifests which pages may use them.\n *\n * @param {object} props The CnIndexPage props.\n * @param {object} [options] Wiring handed down by CnIndexPage's setup.\n * @param {import('vue').Ref<number|null>} [options.activeQuickFilterIndex]\n * The shared tab-index ref `useSelfFetchList` owns (it exists even when\n * self-fetch is off); this composable seeds and watches it.\n * @param {import('vue').Ref<object>} [options.activeFilters] The sidebar's\n * chosen values, `{ fieldKey: values }`. Mapped through the source's\n * `searchFields` declaration into loader arguments and merged into every\n * load, so a manifest page's sidebar narrows the SERVER's query.\n *\n * @return {object} { isNamedSource, namedSource, namedRows, namedLoading, namedQuickFilters }\n */\nexport function useNamedSource(props, options = {}) {\n\tconst objectsProvided = !!(props.objects && props.objects.length > 0)\n\tconst isNamedSource = !!props.entitySource && !objectsProvided\n\n\tif (!isNamedSource) {\n\t\treturn {\n\t\t\tisNamedSource: false,\n\t\t\tnamedSource: null,\n\t\t\tnamedRows: computed(() => []),\n\t\t\tnamedLoading: computed(() => false),\n\t\t\tnamedQuickFilters: null,\n\t\t}\n\t}\n\n\tconst cnTranslate = inject('cnTranslate', null)\n\tconst source = resolveIndexSource(props.entitySource, props.sourceConfig, {\n\t\ttranslate: (text) => (typeof cnTranslate === 'function' ? cnTranslate(text) : text),\n\t})\n\n\t// An unknown source name resolves to null and was already warned about.\n\t// The page then behaves as an empty index rather than throwing.\n\tif (!source) {\n\t\treturn {\n\t\t\tisNamedSource: true,\n\t\t\tnamedSource: null,\n\t\t\tnamedRows: computed(() => []),\n\t\t\tnamedLoading: computed(() => false),\n\t\t\tnamedQuickFilters: null,\n\t\t}\n\t}\n\n\t// Effective tabs: the manifest's own strip wins, the source's is the\n\t// default. Both flow into the SAME load-merge below, so a manifest tab\n\t// can steer a source loader too.\n\tconst ownTabs = withPersonalLenses(props.quickFilters, props.personalLenses)\n\tconst manifestTabs = (Array.isArray(ownTabs) && ownTabs.length > 0)\n\t\t? ownTabs\n\t\t: null\n\tconst sourceTabs = (Array.isArray(source.quickFilters) && source.quickFilters.length > 0)\n\t\t? source.quickFilters\n\t\t: null\n\tconst tabs = manifestTabs || sourceTabs\n\tconst activeIndex = options.activeQuickFilterIndex || null\n\tconst activeFilters = options.activeFilters || null\n\n\t// Seed the default tab before the watcher below registers: the mount load\n\t// already carries this tab's filter, so the seed must not fire a second\n\t// request. `useSelfFetchList` resolved the index from the PROP only, so\n\t// source-supplied tabs arrive with the ref still null.\n\tif (tabs && activeIndex && activeIndex.value === null) {\n\t\tconst di = tabs.findIndex((tab) => tab && tab.default === true)\n\t\tactiveIndex.value = di >= 0 ? di : 0\n\t}\n\n\t// The same three bags `useSelfFetchList` injects, so a token means the\n\t// same thing on both kinds of page. They default to null/absent, so a\n\t// host that provides none is unaffected and `@today` still works: that\n\t// one needs no context at all.\n\tconst objectCtxRaw = inject('cnObjectContext', null)\n\tconst workspaceCtxRaw = inject('cnWorkspaceContext', null)\n\tconst appConfigRaw = inject('cnAppConfig', null)\n\tconst unwrapCtx = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\n\t/**\n\t * Build the token-resolution ctx from the injected bags, unwrapping refs.\n\t *\n\t * @return {object} The ctx `{ objectId?, object?, workspace, config }`.\n\t */\n\tconst tokenCtx = () => {\n\t\tconst objCtx = unwrapCtx(objectCtxRaw)\n\t\tconst base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {}\n\t\tbase.workspace = unwrapCtx(workspaceCtxRaw) || {}\n\t\tbase.config = unwrapCtx(appConfigRaw) || {}\n\t\treturn base\n\t}\n\n\t/**\n\t * Load the source with the active tab's filter merged over the page's\n\t * `sourceConfig` (tab wins on a colliding key), tokens resolved.\n\t *\n\t * The ctx is rebuilt on every load rather than captured once, the same\n\t * way `useSelfFetchList` does it: `@today` has to mean today at the\n\t * moment of the fetch, not at the moment the page mounted, and a tab\n\t * left open across midnight would otherwise keep asking for yesterday.\n\t *\n\t * @return {Promise<void>} Resolves when the load settles.\n\t */\n\tconst loadActive = async () => {\n\t\tconst idx = activeIndex ? activeIndex.value : null\n\t\tconst tab = (tabs && idx !== null && idx !== undefined) ? tabs[idx] : null\n\t\t// 🔑 THE FIELD NARROWS WITHIN THE LENS, SO IT IS MERGED LAST. A lens\n\t\t// sets the question (\"unclaimed work\"); a field narrows the answer\n\t\t// (\"on this case\"). On the rare colliding key the field wins, because\n\t\t// it is the thing the person just chose and can see selected. Both\n\t\t// reach the SERVER: nothing here reduces a fetched page.\n\t\tconst config = resolveFilterTokens(\n\t\t\t{\n\t\t\t\t...(props.sourceConfig || {}),\n\t\t\t\t...((tab && tab.filter) || {}),\n\t\t\t\t...searchFieldParams(\n\t\t\t\t\tsource.searchFields,\n\t\t\t\t\tactiveFilters ? activeFilters.value : null,\n\t\t\t\t\tprops.entitySource,\n\t\t\t\t),\n\t\t\t},\n\t\t\ttokenCtx(),\n\t\t)\n\t\ttry {\n\t\t\tawait source.load(config)\n\t\t} catch (error) {\n\t\t\t// Surfaced, not swallowed: a failed load and an empty source look\n\t\t\t// identical in the table, and only one of them is a problem.\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(`[CnIndexPage] entitySource \"${props.entitySource}\" failed to load`, error)\n\t\t}\n\t}\n\n\tonMounted(loadActive)\n\n\t// Registered AFTER the seed above, so only a person switching tabs\n\t// reloads. Without tabs there is nothing to watch.\n\tif (tabs && activeIndex) {\n\t\twatch(activeIndex, loadActive)\n\t}\n\n\t// A sidebar choice is a new QUERY, not a new view of the page already\n\t// fetched, so it reloads. `deep` because the map is rewritten key by key\n\t// and a shallow watch misses a second value added to the same field.\n\tif (activeFilters) {\n\t\twatch(activeFilters, loadActive, { deep: true })\n\t}\n\n\t// No manual invalidation tick here on purpose. The adapters read from a\n\t// reactive store, so a computed that calls into them tracks it the ordinary\n\t// way. A hand-cranked counter would REPLACE that reactivity rather than add\n\t// to it, and would then miss every update that did not go through load() —\n\t// a flow renamed in the editor, for instance.\n\treturn {\n\t\tisNamedSource: true,\n\t\tnamedSource: source,\n\t\tnamedRows: computed(() => source.rows() || []),\n\t\tnamedLoading: computed(() => source.loading()),\n\t\t// The tabs the page should RENDER: the manifest's when it declared\n\t\t// any (the strip then also drives self-authored filters), else the\n\t\t// source's own. Null when neither exists.\n\t\tnamedQuickFilters: tabs,\n\t}\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useNamedSource.js","sources":["../../../../src/components/CnIndexPage/useNamedSource.js"],"sourcesContent":["/**\n * SPDX-License-Identifier: EUPL-1.2\n * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n */\n\nimport { computed, inject, onMounted, watch } from 'vue'\nimport { resolveIndexSource } from '../../composables/indexSources.js'\nimport { withPersonalLenses } from '../../utils/personalLenses.js'\nimport { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { searchFieldParams } from '../../utils/searchFieldParams.js'\n\n/**\n * Third data mode for CnIndexPage: a NAMED source.\n *\n * The page already knew two ways to get rows — fetch OpenRegister objects from\n * `register` + `schema`, or accept them as `:objects`. Neither reaches a list\n * that is not an OpenRegister object and not already in a parent's hands, so\n * those lists became `type: \"custom\"` pages whose only real job was loading\n * rows from somewhere else.\n *\n * `source` closes that: the manifest names a source and the index loads it.\n *\n * Precedence is deliberate and narrow. NON-EMPTY `:objects` still wins, so a\n * parent that hands rows down is never overridden by a source it did not ask\n * for; and a source wins over `register`/`schema`, because naming both is a\n * contradiction and silently self-fetching would render the WRONG list rather\n * than an obviously empty one.\n *\n * Note the test is \"has rows\", not \"was the prop passed\" — `useSelfFetchList`\n * uses the latter. The difference only shows for `:objects=\"[]\"` alongside a\n * source, where the source wins; that is the useful reading of an empty array\n * next to an explicit source name.\n *\n * Quick filters (cn-tasks-entity-source): a source may supply its own tabs\n * (`quickFilters`, same `{ label, filter, default? }` shape the manifest\n * uses). A manifest that declares `config.quickFilters` still wins. The\n * active tab's filter is merged OVER `sourceConfig` into every load, tab\n * winning on a colliding key, the same precedence self-fetch gives its tabs.\n * The default tab is seeded BEFORE the watcher registers, so mounting issues\n * exactly one request. Single mode only: `quickFilterMultiple` unions filter\n * maps for an OpenRegister query, which a source loader has no grammar for.\n *\n * 🔑 TOKENS RESOLVE HERE TOO. A tab filter is a filter, and a manifest author\n * writing `{ dueBefore: '@today+7d' }` has no way of knowing that the page\n * behind the label happens to be a named source rather than a self-fetch.\n * Without this the literal string `@today+7d` went over the wire, and a\n * source's server either rejected it or, worse, quietly answered the wrong\n * window: dossiq's \"Due this week\" lens is exactly that shape. Self-fetch\n * resolved tokens from the first day; this closes the gap rather than\n * teaching manifests which pages may use them.\n *\n * @param {object} props The CnIndexPage props.\n * @param {object} [options] Wiring handed down by CnIndexPage's setup.\n * @param {import('vue').Ref<number|null>} [options.activeQuickFilterIndex]\n * The shared tab-index ref `useSelfFetchList` owns (it exists even when\n * self-fetch is off); this composable seeds and watches it.\n * @param {import('vue').Ref<object>} [options.activeFilters] The sidebar's\n * chosen values, `{ fieldKey: values }`. Mapped through the source's\n * `searchFields` declaration into loader arguments and merged into every\n * load, so a manifest page's sidebar narrows the SERVER's query.\n *\n * @return {object} { isNamedSource, namedSource, namedRows, namedLoading, namedQuickFilters }\n */\nexport function useNamedSource(props, options = {}) {\n\tconst objectsProvided = !!(props.objects && props.objects.length > 0)\n\tconst isNamedSource = !!props.entitySource && !objectsProvided\n\n\tif (!isNamedSource) {\n\t\treturn {\n\t\t\tisNamedSource: false,\n\t\t\tnamedSource: null,\n\t\t\tnamedRows: computed(() => []),\n\t\t\tnamedLoading: computed(() => false),\n\t\t\tnamedQuickFilters: null,\n\t\t}\n\t}\n\n\tconst cnTranslate = inject('cnTranslate', null)\n\tconst source = resolveIndexSource(props.entitySource, props.sourceConfig, {\n\t\ttranslate: (text) => (typeof cnTranslate === 'function' ? cnTranslate(text) : text),\n\t})\n\n\t// An unknown source name resolves to null and was already warned about.\n\t// The page then behaves as an empty index rather than throwing.\n\tif (!source) {\n\t\treturn {\n\t\t\tisNamedSource: true,\n\t\t\tnamedSource: null,\n\t\t\tnamedRows: computed(() => []),\n\t\t\tnamedLoading: computed(() => false),\n\t\t\tnamedQuickFilters: null,\n\t\t}\n\t}\n\n\t// Effective tabs: the manifest's own strip wins, the source's is the\n\t// default. Both flow into the SAME load-merge below, so a manifest tab\n\t// can steer a source loader too.\n\tconst ownTabs = withPersonalLenses(props.quickFilters, props.personalLenses)\n\tconst manifestTabs = (Array.isArray(ownTabs) && ownTabs.length > 0)\n\t\t? ownTabs\n\t\t: null\n\tconst sourceTabs = (Array.isArray(source.quickFilters) && source.quickFilters.length > 0)\n\t\t? source.quickFilters\n\t\t: null\n\tconst tabs = manifestTabs || sourceTabs\n\tconst activeIndex = options.activeQuickFilterIndex || null\n\tconst activeFilters = options.activeFilters || null\n\t// A source that pages (`pages: true`) gets the page as `limit` and\n\t// `offset`, from the pager the page draws for it.\n\tconst paging = source.pages === true ? (options.paging || null) : null\n\n\t// Seed the default tab before the watcher below registers: the mount load\n\t// already carries this tab's filter, so the seed must not fire a second\n\t// request. `useSelfFetchList` resolved the index from the PROP only, so\n\t// source-supplied tabs arrive with the ref still null.\n\tif (tabs && activeIndex && activeIndex.value === null) {\n\t\tconst di = tabs.findIndex((tab) => tab && tab.default === true)\n\t\tactiveIndex.value = di >= 0 ? di : 0\n\t}\n\n\t// The same three bags `useSelfFetchList` injects, so a token means the\n\t// same thing on both kinds of page. They default to null/absent, so a\n\t// host that provides none is unaffected and `@today` still works: that\n\t// one needs no context at all.\n\tconst objectCtxRaw = inject('cnObjectContext', null)\n\tconst workspaceCtxRaw = inject('cnWorkspaceContext', null)\n\tconst appConfigRaw = inject('cnAppConfig', null)\n\tconst unwrapCtx = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\n\t/**\n\t * Build the token-resolution ctx from the injected bags, unwrapping refs.\n\t *\n\t * @return {object} The ctx `{ objectId?, object?, workspace, config }`.\n\t */\n\tconst tokenCtx = () => {\n\t\tconst objCtx = unwrapCtx(objectCtxRaw)\n\t\tconst base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {}\n\t\tbase.workspace = unwrapCtx(workspaceCtxRaw) || {}\n\t\tbase.config = unwrapCtx(appConfigRaw) || {}\n\t\treturn base\n\t}\n\n\t/**\n\t * Load the source with the active tab's filter merged over the page's\n\t * `sourceConfig` (tab wins on a colliding key), tokens resolved.\n\t *\n\t * The ctx is rebuilt on every load rather than captured once, the same\n\t * way `useSelfFetchList` does it: `@today` has to mean today at the\n\t * moment of the fetch, not at the moment the page mounted, and a tab\n\t * left open across midnight would otherwise keep asking for yesterday.\n\t *\n\t * @return {Promise<void>} Resolves when the load settles.\n\t */\n\tconst loadActive = async () => {\n\t\tconst idx = activeIndex ? activeIndex.value : null\n\t\tconst tab = (tabs && idx !== null && idx !== undefined) ? tabs[idx] : null\n\t\t// 🔑 THE FIELD NARROWS WITHIN THE LENS, SO IT IS MERGED LAST. A lens\n\t\t// sets the question (\"unclaimed work\"); a field narrows the answer\n\t\t// (\"on this case\"). On the rare colliding key the field wins, because\n\t\t// it is the thing the person just chose and can see selected. Both\n\t\t// reach the SERVER: nothing here reduces a fetched page.\n\t\tconst config = resolveFilterTokens(\n\t\t\t{\n\t\t\t\t...(props.sourceConfig || {}),\n\t\t\t\t...((tab && tab.filter) || {}),\n\t\t\t\t...searchFieldParams(\n\t\t\t\t\tsource.searchFields,\n\t\t\t\t\tactiveFilters ? activeFilters.value : null,\n\t\t\t\t\tprops.entitySource,\n\t\t\t\t),\n\t\t\t\t...(paging ? { limit: paging.value.limit, offset: (paging.value.page - 1) * paging.value.limit } : {}),\n\t\t\t},\n\t\t\ttokenCtx(),\n\t\t)\n\t\ttry {\n\t\t\tawait source.load(config)\n\t\t} catch (error) {\n\t\t\t// Surfaced, not swallowed: a failed load and an empty source look\n\t\t\t// identical in the table, and only one of them is a problem.\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(`[CnIndexPage] entitySource \"${props.entitySource}\" failed to load`, error)\n\t\t}\n\t}\n\n\tonMounted(loadActive)\n\n\t// Registered AFTER the seed above, so only a person switching tabs\n\t// reloads. Without tabs there is nothing to watch.\n\t/**\n\t * Reload for a new query: on a paging source back to page 1, which\n\t * reloads through the paging watcher below.\n\t *\n\t * @return {void}\n\t */\n\tfunction reloadFromFirstPage() {\n\t\tif (paging && paging.value.page !== 1) {\n\t\t\tpaging.value = { ...paging.value, page: 1 }\n\t\t\treturn\n\t\t}\n\t\tloadActive()\n\t}\n\n\tif (tabs && activeIndex) {\n\t\twatch(activeIndex, reloadFromFirstPage)\n\t}\n\n\t// A sidebar choice is a new QUERY, not a new view of the page already\n\t// fetched, so it reloads. `deep` because the map is rewritten key by key\n\t// and a shallow watch misses a second value added to the same field.\n\tif (activeFilters) {\n\t\twatch(activeFilters, reloadFromFirstPage, { deep: true })\n\t}\n\n\t// A new page, or a new page size, is a new request. A new tab or filter\n\t// first returns to page 1, which reloads through here.\n\tif (paging) {\n\t\twatch(paging, loadActive, { deep: true })\n\t}\n\n\t// No manual invalidation tick here on purpose. The adapters read from a\n\t// reactive store, so a computed that calls into them tracks it the ordinary\n\t// way. A hand-cranked counter would REPLACE that reactivity rather than add\n\t// to it, and would then miss every update that did not go through load() —\n\t// a flow renamed in the editor, for instance.\n\treturn {\n\t\tisNamedSource: true,\n\t\tnamedSource: source,\n\t\tnamedRows: computed(() => source.rows() || []),\n\t\tnamedLoading: computed(() => source.loading()),\n\t\t// The tabs the page should RENDER: the manifest's when it declared\n\t\t// any (the strip then also drives self-authored filters), else the\n\t\t// source's own. Null when neither exists.\n\t\tnamedQuickFilters: tabs,\n\t}\n}\n"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import script from './CnMenuWidget.vue2.js';
2
2
  import { render } from './CnMenuWidget.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-2a4e786e";
5
+ script.__scopeId = "data-v-2edac2f0";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -35,6 +35,11 @@ var script = {
35
35
  CnMenuItemIcon: __sfc_main$1,
36
36
  },
37
37
 
38
+ inject: {
39
+ /** Host translate function provided by CnAppRoot, for the item labels. */
40
+ cnTranslate: { default: () => (key) => key },
41
+ },
42
+
38
43
  props: {
39
44
  /**
40
45
  * Persisted widget content `{items, style, orientation, showIcons,
@@ -84,7 +89,26 @@ var script = {
84
89
  */
85
90
  items() {
86
91
  const arr = this.content?.items;
87
- return Array.isArray(arr) ? arr : []
92
+ if (!Array.isArray(arr)) {
93
+ return []
94
+ }
95
+ // Labels are manifest strings: they go through the app's translate,
96
+ // children included, so "Case types" reads "Zaaktypen" (DqCatalogus).
97
+ const translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key;
98
+ const translated = (list) => list.map((item) => {
99
+ if (!item || typeof item !== 'object') {
100
+ return item
101
+ }
102
+ const out = { ...item };
103
+ if (typeof item.label === 'string' && item.label !== '') {
104
+ out.label = translate(item.label);
105
+ }
106
+ if (Array.isArray(item.children)) {
107
+ out.children = translated(item.children);
108
+ }
109
+ return out
110
+ });
111
+ return translated(arr)
88
112
  },
89
113
 
90
114
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"CnMenuWidget.vue2.js","sources":["../../../../src/components/CnMenuWidget/CnMenuWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-menu-widget\"\n\t\t:class=\"rootClasses\"\n\t\t@keydown.esc.stop=\"closeAll\">\n\t\t<!-- Empty state -->\n\t\t<div\n\t\t\tv-if=\"isEmpty\"\n\t\t\tclass=\"cn-menu-widget__empty\"\n\t\t\t:class=\"{ 'cn-menu-widget__empty--clickable': isInEditMode }\"\n\t\t\t:role=\"isInEditMode ? 'button' : null\"\n\t\t\t:tabindex=\"isInEditMode ? 0 : -1\"\n\t\t\t@click=\"onEmptyStateClick\"\n\t\t\t@keyup.enter=\"onEmptyStateClick\"\n\t\t\t@keyup.space=\"onEmptyStateClick\">\n\t\t\t<span class=\"cn-menu-widget__empty-icon\" aria-hidden=\"true\">⚙</span>\n\t\t\t<span>{{ t('nextcloud-vue', 'No menu items yet — click the gear icon to add some.') }}</span>\n\t\t</div>\n\n\t\t<!-- Tree style -->\n\t\t<ul\n\t\t\tv-else-if=\"effectiveStyle === 'tree'\"\n\t\t\tclass=\"cn-menu-widget__tree\"\n\t\t\trole=\"tree\">\n\t\t\t<CnMenuTreeNode\n\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t:key=\"`tree-${idx}`\"\n\t\t\t\t:item=\"item\"\n\t\t\t\t:depth=\"1\"\n\t\t\t\t:showIcons=\"showIcons\"\n\t\t\t\t:expandedByDefault=\"expandedByDefault\"\n\t\t\t\t:activePath=\"activePath\"\n\t\t\t\t:activeLeafKey=\"activeLeafKey\"\n\t\t\t\t:currentKey=\"`${idx}`\"\n\t\t\t\t:activeHighlight=\"activeItemHighlight\"\n\t\t\t\t@navigate=\"onTreeNavigate\" />\n\t\t</ul>\n\n\t\t<!-- Megamenu style -->\n\t\t<div\n\t\t\tv-else-if=\"effectiveStyle === 'megamenu'\"\n\t\t\tclass=\"cn-menu-widget__megamenu\">\n\t\t\t<ul\n\t\t\t\tclass=\"cn-menu-widget__bar\"\n\t\t\t\trole=\"menubar\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t\t:key=\"`mega-top-${idx}`\"\n\t\t\t\t\trole=\"none\"\n\t\t\t\t\tclass=\"cn-menu-widget__bar-item\">\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"isTopLink(item) ? 'a' : 'button'\"\n\t\t\t\t\t\t:ref=\"(el) => setTopRef(el, idx)\"\n\t\t\t\t\t\tv-bind=\"isTopLink(item) ? linkAttrs(item) : { type: 'button' }\"\n\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__bar-button\"\n\t\t\t\t\t\t:class=\"topItemClass(`${idx}`)\"\n\t\t\t\t\t\t:aria-haspopup=\"hasChildren(item) ? 'menu' : null\"\n\t\t\t\t\t\t:aria-expanded=\"megaOpenIndex === idx ? 'true' : 'false'\"\n\t\t\t\t\t\t@click=\"isTopLink(item) ? onLinkClick($event, item) : onMegaTopClick(idx, item)\"\n\t\t\t\t\t\t@keydown=\"onMegaTopKey($event, idx, item)\">\n\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !item.icon }\">\n\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"item.icon\" :icon=\"item.icon\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ item.label }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t\t<div\n\t\t\t\tv-if=\"megaOpenIndex !== null && hasChildren(items[megaOpenIndex])\"\n\t\t\t\tref=\"megaPanel\"\n\t\t\t\tclass=\"cn-menu-widget__mega-panel\"\n\t\t\t\trole=\"menu\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(child, childIdx) in items[megaOpenIndex].children\"\n\t\t\t\t\t:key=\"`mega-group-${childIdx}`\"\n\t\t\t\t\tclass=\"cn-menu-widget__mega-group\">\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"isLink(child) ? 'a' : 'button'\"\n\t\t\t\t\t\tv-bind=\"itemAttrs(child)\"\n\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__mega-group-title\"\n\t\t\t\t\t\t:class=\"topItemClass(`${megaOpenIndex}.${childIdx}`)\"\n\t\t\t\t\t\t@click=\"onLinkClick($event, child)\">\n\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !child.icon }\">\n\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"child.icon\" :icon=\"child.icon\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ child.label }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t\t<ul\n\t\t\t\t\t\tv-if=\"hasChildren(child)\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__mega-leaves\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"(leaf, leafIdx) in child.children\"\n\t\t\t\t\t\t\t:key=\"`mega-leaf-${leafIdx}`\"\n\t\t\t\t\t\t\trole=\"none\">\n\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t:is=\"isLink(leaf) ? 'a' : 'button'\"\n\t\t\t\t\t\t\t\tv-bind=\"itemAttrs(leaf)\"\n\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\tclass=\"cn-menu-widget__mega-leaf\"\n\t\t\t\t\t\t\t\t:class=\"topItemClass(`${megaOpenIndex}.${childIdx}.${leafIdx}`)\"\n\t\t\t\t\t\t\t\t@click=\"onLinkClick($event, leaf)\">\n\t\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !leaf.icon }\">\n\t\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"leaf.icon\" :icon=\"leaf.icon\" />\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ leaf.label }}</span>\n\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Dropdown style (default) -->\n\t\t<ul\n\t\t\tv-else\n\t\t\tclass=\"cn-menu-widget__bar\"\n\t\t\t:class=\"{ 'cn-menu-widget__bar--vertical': orientation === 'vertical' }\"\n\t\t\trole=\"menubar\">\n\t\t\t<li\n\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t:key=\"`drop-top-${idx}`\"\n\t\t\t\t:ref=\"(el) => setBarItemRef(el, idx)\"\n\t\t\t\trole=\"none\"\n\t\t\t\tclass=\"cn-menu-widget__bar-item\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"isTopLink(item) ? 'a' : 'button'\"\n\t\t\t\t\t:ref=\"(el) => setTopRef(el, idx)\"\n\t\t\t\t\tv-bind=\"isTopLink(item) ? linkAttrs(item) : { type: 'button' }\"\n\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\tclass=\"cn-menu-widget__bar-button\"\n\t\t\t\t\t:class=\"topItemClass(`${idx}`)\"\n\t\t\t\t\t:aria-haspopup=\"hasChildren(item) ? 'menu' : null\"\n\t\t\t\t\t:aria-expanded=\"dropOpenIndex === idx ? 'true' : 'false'\"\n\t\t\t\t\t@click=\"isTopLink(item) ? onLinkClick($event, item) : onDropdownTopClick(idx, item)\"\n\t\t\t\t\t@keydown.tab=\"closeAll\"\n\t\t\t\t\t@keydown.enter=\"onDropdownTopEnter($event, idx, item)\"\n\t\t\t\t\t@keydown.space.prevent=\"onDropdownTopClick(idx, item)\"\n\t\t\t\t\t@keydown.down.prevent=\"openDropdown(idx)\">\n\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !item.icon }\">\n\t\t\t\t\t\t<CnMenuItemIcon v-if=\"item.icon\" :icon=\"item.icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ item.label }}</span>\n\t\t\t\t\t<span v-if=\"hasChildren(item)\" class=\"cn-menu-widget__caret\" aria-hidden=\"true\">▾</span>\n\t\t\t\t</component>\n\t\t\t\t<ul\n\t\t\t\t\tv-if=\"dropOpenIndex === idx && hasChildren(item)\"\n\t\t\t\t\tclass=\"cn-menu-widget__dropdown\"\n\t\t\t\t\trole=\"menu\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"(child, childIdx) in item.children\"\n\t\t\t\t\t\t:key=\"`drop-child-${childIdx}`\"\n\t\t\t\t\t\trole=\"none\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item-wrap\"\n\t\t\t\t\t\t@mouseenter=\"hasChildren(child) && (flyoutOpenKey = `${idx}.${childIdx}`)\">\n\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t:is=\"isLink(child) ? 'a' : 'button'\"\n\t\t\t\t\t\t\tv-bind=\"itemAttrs(child)\"\n\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item\"\n\t\t\t\t\t\t\t:class=\"topItemClass(`${idx}.${childIdx}`)\"\n\t\t\t\t\t\t\t@click=\"onLinkClick($event, child)\">\n\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !child.icon }\">\n\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"child.icon\" :icon=\"child.icon\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ child.label }}</span>\n\t\t\t\t\t\t\t<span v-if=\"hasChildren(child)\" class=\"cn-menu-widget__caret cn-menu-widget__caret--right\" aria-hidden=\"true\">▸</span>\n\t\t\t\t\t\t</component>\n\t\t\t\t\t\t<ul\n\t\t\t\t\t\t\tv-if=\"flyoutOpenKey === `${idx}.${childIdx}` && hasChildren(child)\"\n\t\t\t\t\t\t\tclass=\"cn-menu-widget__flyout\"\n\t\t\t\t\t\t\trole=\"menu\">\n\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\tv-for=\"(leaf, leafIdx) in child.children\"\n\t\t\t\t\t\t\t\t:key=\"`drop-leaf-${leafIdx}`\"\n\t\t\t\t\t\t\t\trole=\"none\">\n\t\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t\t:is=\"isLink(leaf) ? 'a' : 'button'\"\n\t\t\t\t\t\t\t\t\tv-bind=\"itemAttrs(leaf)\"\n\t\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item\"\n\t\t\t\t\t\t\t\t\t:class=\"topItemClass(`${idx}.${childIdx}.${leafIdx}`)\"\n\t\t\t\t\t\t\t\t\t@click=\"onLinkClick($event, leaf)\">\n\t\t\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !leaf.icon }\">\n\t\t\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"leaf.icon\" :icon=\"leaf.icon\" />\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ leaf.label }}</span>\n\t\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport CnMenuItemIcon from './CnMenuItemIcon.vue'\nimport CnMenuTreeNode from './CnMenuTreeNode.vue'\nimport { followLinkClick } from '../../utils/linkNavigation.js'\nimport { computeActivePath, isActiveItem, isRouterPath, menuLinkAttrs } from './menuActive.js'\n\nconst VALID_STYLES = ['dropdown', 'megamenu', 'tree']\nconst VALID_ORIENTATIONS = ['horizontal', 'vertical']\nconst VALID_HIGHLIGHTS = ['background', 'underline', 'left-bar', 'none']\n\n/**\n * CnMenuWidget — renders a hierarchical navigation widget with three visual\n * styles (dropdown, megamenu, tree) and active-item highlighting based on\n * `window.location.pathname`.\n *\n * Active-item detection runs on mount and whenever `items` or the URL change;\n * ancestors of the active leaf are flagged \"in-path\" so the configured\n * highlight (`underline`, `background`, `left-bar`, `none`) renders\n * consistently. Keyboard navigation follows the WAI-ARIA Menu/Menubar pattern.\n *\n * Open dropdown/megamenu panels close on: re-clicking the toggle, Escape,\n * Tab, navigating a leaf, and clicking ANYWHERE else — including empty space\n * elsewhere in the widget itself, not just outside it entirely (a\n * document-level listener that checks containment against the open toggle\n * button + its own panel specifically, since the panels are plain in-DOM\n * markup, not a teleported popover with its own outside-click handling).\n *\n * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n */\nexport default {\n\tname: 'CnMenuWidget',\n\n\tcomponents: {\n\t\tCnMenuTreeNode,\n\t\tCnMenuItemIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content `{items, style, orientation, showIcons,\n\t\t * expandedByDefault, activeItemHighlight}`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Whether the current user is an admin (pairs with `canEdit`). */\n\t\tisAdmin: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether the surrounding shell is in edit mode. */\n\t\tcanEdit: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: [\n\t\t/** Fired when the empty-state gear is clicked in edit mode. */\n\t\t'edit-request',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tdropOpenIndex: null,\n\t\t\tflyoutOpenKey: null,\n\t\t\tmegaOpenIndex: null,\n\t\t\tcurrentLocation: this.readLocation(),\n\t\t\ttopRefs: [],\n\t\t\tbarItemRefs: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The configured menu items.\n\t\t *\n\t\t * @return {object[]} the items array.\n\t\t */\n\t\titems() {\n\t\t\tconst arr = this.content?.items\n\t\t\treturn Array.isArray(arr) ? arr : []\n\t\t},\n\n\t\t/**\n\t\t * Whether the menu has no items.\n\t\t *\n\t\t * @return {boolean} true when empty.\n\t\t */\n\t\tisEmpty() {\n\t\t\treturn this.items.length === 0\n\t\t},\n\n\t\t/**\n\t\t * The validated visual style (default `dropdown`).\n\t\t *\n\t\t * @return {string} one of dropdown/megamenu/tree.\n\t\t */\n\t\teffectiveStyle() {\n\t\t\tconst s = this.content?.style\n\t\t\treturn VALID_STYLES.includes(s) ? s : 'dropdown'\n\t\t},\n\n\t\t/**\n\t\t * The validated orientation (tree is always vertical).\n\t\t *\n\t\t * @return {string} `horizontal` or `vertical`.\n\t\t */\n\t\torientation() {\n\t\t\tconst o = this.content?.orientation\n\t\t\tif (this.effectiveStyle === 'tree') {\n\t\t\t\treturn 'vertical'\n\t\t\t}\n\t\t\treturn VALID_ORIENTATIONS.includes(o) ? o : 'horizontal'\n\t\t},\n\n\t\t/**\n\t\t * Whether item icons render (default true).\n\t\t *\n\t\t * @return {boolean} the showIcons flag.\n\t\t */\n\t\tshowIcons() {\n\t\t\treturn this.content?.showIcons !== false\n\t\t},\n\n\t\t/**\n\t\t * Whether tree nodes start expanded.\n\t\t *\n\t\t * @return {boolean} the expandedByDefault flag.\n\t\t */\n\t\texpandedByDefault() {\n\t\t\treturn this.content?.expandedByDefault === true\n\t\t},\n\n\t\t/**\n\t\t * The validated active-item highlight style (default `underline`).\n\t\t *\n\t\t * @return {string} one of background/underline/left-bar/none.\n\t\t */\n\t\tactiveItemHighlight() {\n\t\t\tconst h = this.content?.activeItemHighlight\n\t\t\treturn VALID_HIGHLIGHTS.includes(h) ? h : 'underline'\n\t\t},\n\n\t\t/**\n\t\t * Whether the widget is in edit mode (empty-state gear clickable).\n\t\t *\n\t\t * @return {boolean} true when admin + canEdit.\n\t\t */\n\t\tisInEditMode() {\n\t\t\treturn this.isAdmin === true && this.canEdit === true\n\t\t},\n\n\t\t/**\n\t\t * The root style/highlight/orientation CSS classes.\n\t\t *\n\t\t * @return {object} the class map.\n\t\t */\n\t\trootClasses() {\n\t\t\treturn {\n\t\t\t\t[`cn-menu-widget--style-${this.effectiveStyle}`]: true,\n\t\t\t\t[`cn-menu-widget--highlight-${this.activeItemHighlight}`]: true,\n\t\t\t\t[`cn-menu-widget--orientation-${this.orientation}`]: true,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The dotted-key path map for the current location.\n\t\t *\n\t\t * @return {{path: object, leafKey: string|null}} the active-path map.\n\t\t */\n\t\tactivePathMap() {\n\t\t\treturn computeActivePath({\n\t\t\t\titems: this.items,\n\t\t\t\tcurrentLocation: this.currentLocation,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The dotted-key → state map.\n\t\t *\n\t\t * @return {Record<string, string>} the path map.\n\t\t */\n\t\tactivePath() {\n\t\t\treturn this.activePathMap.path\n\t\t},\n\n\t\t/**\n\t\t * The key of the deepest active leaf.\n\t\t *\n\t\t * @return {string|null} the leaf key.\n\t\t */\n\t\tactiveLeafKey() {\n\t\t\treturn this.activePathMap.leafKey\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.boundLocationListener = () => {\n\t\t\tthis.currentLocation = this.readLocation()\n\t\t}\n\t\tif (typeof window !== 'undefined') {\n\t\t\twindow.addEventListener('popstate', this.boundLocationListener)\n\t\t\twindow.addEventListener('hashchange', this.boundLocationListener)\n\t\t}\n\t\tthis.boundOutsideClickListener = this.onOutsideClick\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.addEventListener('click', this.boundOutsideClickListener)\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tif (typeof window !== 'undefined' && this.boundLocationListener) {\n\t\t\twindow.removeEventListener('popstate', this.boundLocationListener)\n\t\t\twindow.removeEventListener('hashchange', this.boundLocationListener)\n\t\t}\n\t\tif (typeof document !== 'undefined' && this.boundOutsideClickListener) {\n\t\t\tdocument.removeEventListener('click', this.boundOutsideClickListener)\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Read the current browser location into a small bag.\n\t\t *\n\t\t * @return {{pathname: string, host: string}} the location bag.\n\t\t */\n\t\treadLocation() {\n\t\t\tif (typeof window === 'undefined' || !window.location) {\n\t\t\t\treturn { pathname: '/', host: '' }\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tpathname: window.location.pathname || '/',\n\t\t\t\thost: window.location.host || '',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stash a top-level button ref by index.\n\t\t *\n\t\t * @param {HTMLElement} el the button element.\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\tsetTopRef(el, idx) {\n\t\t\tif (el) {\n\t\t\t\tthis.topRefs[idx] = el\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stash a dropdown-style top-level `<li>` ref by index. This `<li>`\n\t\t * contains both the toggle button and its dropdown/flyout panel, so a\n\t\t * single containment check against it covers the whole open structure.\n\t\t *\n\t\t * @param {HTMLElement} el the `<li>` element.\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\tsetBarItemRef(el, idx) {\n\t\t\tif (el) {\n\t\t\t\tthis.barItemRefs[idx] = el\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an item has children.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true when it has a non-empty children array.\n\t\t */\n\t\thasChildren(item) {\n\t\t\treturn item && Array.isArray(item.children) && item.children.length > 0\n\t\t},\n\n\t\t/**\n\t\t * The active-state class for a dotted key.\n\t\t *\n\t\t * @param {string} key the dotted item key.\n\t\t * @return {string} the class name (or `''`).\n\t\t */\n\t\ttopItemClass(key) {\n\t\t\tconst state = this.activePath[key]\n\t\t\tif (state === 'active' || key === this.activeLeafKey) {\n\t\t\t\treturn 'cn-menu-widget__item--active'\n\t\t\t}\n\t\t\tif (state === 'in-path') {\n\t\t\t\treturn 'cn-menu-widget__item--in-path'\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * Top-level dropdown click — navigate a leaf or toggle the dropdown.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonDropdownTopClick(idx, item) {\n\t\t\tif (!this.hasChildren(item)) {\n\t\t\t\tthis.onNavigate(item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.dropOpenIndex === idx) {\n\t\t\t\tthis.dropOpenIndex = null\n\t\t\t\tthis.flyoutOpenKey = null\n\t\t\t} else {\n\t\t\t\tthis.dropOpenIndex = idx\n\t\t\t\tthis.flyoutOpenKey = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open the dropdown at an index when it has children.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\topenDropdown(idx) {\n\t\t\tif (this.hasChildren(this.items[idx])) {\n\t\t\t\tthis.dropOpenIndex = idx\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Top-level megamenu click — navigate a leaf or toggle the panel.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonMegaTopClick(idx, item) {\n\t\t\tif (!this.hasChildren(item)) {\n\t\t\t\tthis.onNavigate(item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.megaOpenIndex = this.megaOpenIndex === idx ? null : idx\n\t\t},\n\n\t\t/**\n\t\t * Keyboard handler for megamenu top items.\n\t\t *\n\t\t * @param {KeyboardEvent} event the key event.\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonMegaTopKey(event, idx, item) {\n\t\t\t// Enter on a link clicks it natively.\n\t\t\tif (event.key === 'Enter' && this.isTopLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.onMegaTopClick(idx, item)\n\t\t\t} else if (event.key === 'Tab') {\n\t\t\t\tthis.megaOpenIndex = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Close every open dropdown/flyout/panel.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcloseAll() {\n\t\t\tthis.dropOpenIndex = null\n\t\t\tthis.flyoutOpenKey = null\n\t\t\tthis.megaOpenIndex = null\n\t\t},\n\n\t\t/**\n\t\t * Document-level click handler — closes any open dropdown/flyout/panel\n\t\t * when the click lands outside its own toggle button + panel, INCLUDING\n\t\t * empty space elsewhere in the widget (e.g. bar padding, gaps between\n\t\t * items) — not just outside the whole widget. The toggle button's own\n\t\t * `@click` handler manages opening/closing for its own clicks, so a\n\t\t * click still inside the open button/panel is left alone — otherwise\n\t\t * every open-toggle click would immediately re-close itself via this\n\t\t * same listener.\n\t\t *\n\t\t * @param {MouseEvent} event the document click event.\n\t\t * @return {void}\n\t\t */\n\t\tonOutsideClick(event) {\n\t\t\tconst target = event.target\n\t\t\tif (this.effectiveStyle === 'megamenu') {\n\t\t\t\tif (this.megaOpenIndex === null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst button = this.topRefs[this.megaOpenIndex]\n\t\t\t\tconst panel = this.$refs.megaPanel\n\t\t\t\tconst insideButton = Boolean(button && button.contains(target))\n\t\t\t\tconst insidePanel = Boolean(panel && panel.contains(target))\n\t\t\t\tif (!insideButton && !insidePanel) {\n\t\t\t\t\tthis.closeAll()\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Dropdown style — the button and its dropdown/flyout panel share\n\t\t\t// the same top-level `<li>`, so one containment check covers both.\n\t\t\tif (this.dropOpenIndex === null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst barItem = this.barItemRefs[this.dropOpenIndex]\n\t\t\tif (barItem && !barItem.contains(target)) {\n\t\t\t\tthis.closeAll()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an item renders as a link (it has a URL).\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true when the item carries a non-empty URL.\n\t\t */\n\t\tisLink(item) {\n\t\t\treturn Boolean(item) && typeof item.url === 'string' && item.url !== ''\n\t\t},\n\n\t\t/**\n\t\t * Whether a top-level item renders as a link. One with children\n\t\t * stays a button, because its click toggles the panel.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true for a top-level leaf with a URL.\n\t\t */\n\t\tisTopLink(item) {\n\t\t\treturn this.isLink(item) && !this.hasChildren(item)\n\t\t},\n\n\t\t/**\n\t\t * Anchor attributes for an item's URL: external URLs open in a new tab.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {object} the `href` (plus `target` / `rel` for external URLs).\n\t\t */\n\t\tlinkAttrs(item) {\n\t\t\treturn menuLinkAttrs(item.url, this.$router)\n\t\t},\n\n\t\t/**\n\t\t * Attributes for a panel item: link attributes, or `type=\"button\"`.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {object} the element attributes.\n\t\t */\n\t\titemAttrs(item) {\n\t\t\treturn this.isLink(item) ? this.linkAttrs(item) : { type: 'button' }\n\t\t},\n\n\t\t/**\n\t\t * Click on an item link: close the menus, then let the browser open\n\t\t * an external URL or a modified click, and route a plain click on an\n\t\t * internal one. A no-op for an item without a URL.\n\t\t *\n\t\t * @param {MouseEvent} event the click event.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonLinkClick(event, item) {\n\t\t\tif (!this.isLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.closeAll()\n\t\t\tif (isRouterPath(item.url)) {\n\t\t\t\tfollowLinkClick(event, item.url, this.$router)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Enter on a dropdown top item: a link follows natively (Enter\n\t\t * clicks it); a button toggles or navigates.\n\t\t *\n\t\t * @param {KeyboardEvent} event the key event.\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonDropdownTopEnter(event, idx, item) {\n\t\t\tif (this.isTopLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.onDropdownTopClick(idx, item)\n\t\t},\n\n\t\t/**\n\t\t * Navigation request from a tree node: a link click goes through the\n\t\t * link handler, a request without an event navigates directly.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @param {MouseEvent} [event] the click event, when the node's link was clicked.\n\t\t * @return {void}\n\t\t */\n\t\tonTreeNavigate(item, event) {\n\t\t\tif (event) {\n\t\t\t\tthis.onLinkClick(event, item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onNavigate(item)\n\t\t},\n\n\t\t/**\n\t\t * Navigate to an item's URL (router push for internal, new tab for\n\t\t * external), closing any open menus first. Used where no link click\n\t\t * is involved (keyboard activation of a menu item).\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonNavigate(item) {\n\t\t\tif (!item || typeof item.url !== 'string' || item.url === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.closeAll()\n\t\t\tconst url = item.url\n\t\t\tconst { href } = menuLinkAttrs(url, this.$router)\n\t\t\tif (this.isExternal(url)) {\n\t\t\t\tif (typeof window !== 'undefined' && typeof window.open === 'function') {\n\t\t\t\t\twindow.open(href, '_blank', 'noopener,noreferrer')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (isRouterPath(url) && this.$router && typeof this.$router.push === 'function') {\n\t\t\t\ttry {\n\t\t\t\t\tthis.$router.push(url)\n\t\t\t\t\treturn\n\t\t\t\t} catch {\n\t\t\t\t\t// Fall through to location assignment.\n\t\t\t\t}\n\t\t\t}\n\t\t\t// href is safeHref-checked, so a javascript: URL never reaches location.\n\t\t\tif (href !== '#' && typeof window !== 'undefined' && window.location) {\n\t\t\t\twindow.location.href = href\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether a URL is external (http/https).\n\t\t *\n\t\t * @param {string} url the URL.\n\t\t * @return {boolean} true for absolute http(s) URLs.\n\t\t */\n\t\tisExternal(url) {\n\t\t\treturn typeof url === 'string' && /^https?:\\/\\//i.test(url)\n\t\t},\n\n\t\t/**\n\t\t * Empty-state click — request edit when in edit mode.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonEmptyStateClick() {\n\t\t\tif (this.isInEditMode) {\n\t\t\t\tthis.$emit('edit-request')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Test helper — whether a URL is active for the current location.\n\t\t *\n\t\t * @param {string} itemUrl the URL to test.\n\t\t * @return {boolean} true when active.\n\t\t */\n\t\t__isActive(itemUrl) {\n\t\t\treturn isActiveItem(itemUrl, this.currentLocation)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-menu-widget {\n\twidth: 100%;\n\theight: 100%;\n\tpadding: 4px;\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n}\n\n.cn-menu-widget__empty {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-style: italic;\n\tborder: 1px dashed var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-menu-widget__empty--clickable {\n\tcursor: pointer;\n}\n\n.cn-menu-widget__empty-icon {\n\tfont-size: 18px;\n}\n\n.cn-menu-widget__bar {\n\tdisplay: flex;\n\tflex-direction: row;\n\tgap: 4px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-menu-widget__bar--vertical {\n\tflex-direction: column;\n}\n\n.cn-menu-widget__bar-item {\n\tposition: relative;\n\tlist-style: none;\n}\n\n.cn-menu-widget__bar-button,\n.cn-menu-widget__dropdown-item,\n.cn-menu-widget__mega-group-title,\n.cn-menu-widget__mega-leaf {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 6px 10px;\n\tbackground: transparent;\n\tborder: none;\n\tborder-radius: var(--border-radius);\n\tcolor: inherit;\n\tfont-size: 14px;\n\tcursor: pointer;\n\ttext-align: left;\n\ttext-decoration: none;\n\tbox-sizing: border-box;\n\twidth: 100%;\n}\n\n.cn-menu-widget__mega-group-title {\n\tfont-weight: 600;\n}\n\n.cn-menu-widget__bar-button:hover,\n.cn-menu-widget__dropdown-item:hover,\n.cn-menu-widget__mega-group-title:hover,\n.cn-menu-widget__mega-leaf:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-menu-widget__bar-button:focus-visible,\n.cn-menu-widget__dropdown-item:focus-visible {\n\toutline: 2px solid var(--color-primary);\n\toutline-offset: 2px;\n}\n\n.cn-menu-widget__icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 20px;\n\theight: 20px;\n}\n\n.cn-menu-widget__icon--hidden {\n\tvisibility: hidden;\n}\n\n.cn-menu-widget__caret {\n\tmargin-left: auto;\n\tfont-size: 10px;\n}\n\n.cn-menu-widget__dropdown {\n\tposition: absolute;\n\ttop: 100%;\n\tleft: 0;\n\tz-index: 50;\n\tmargin: 4px 0 0;\n\tpadding: 4px;\n\tmin-width: 180px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n\tlist-style: none;\n}\n\n.cn-menu-widget__dropdown-item-wrap {\n\tposition: relative;\n}\n\n.cn-menu-widget__flyout {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 100%;\n\tmargin-left: 2px;\n\tpadding: 4px;\n\tmin-width: 160px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n\tlist-style: none;\n}\n\n.cn-menu-widget__mega-panel {\n\tmargin-top: 4px;\n\tpadding: 12px;\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n\tgap: 12px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-menu-widget__mega-leaves {\n\tmargin: 4px 0 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-menu-widget__tree {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n/* Active-item highlight variants. */\n.cn-menu-widget--highlight-underline .cn-menu-widget__item--active {\n\tborder-bottom: 3px solid var(--color-primary);\n}\n\n.cn-menu-widget--highlight-underline .cn-menu-widget__item--in-path {\n\tborder-bottom: 2px solid var(--color-primary);\n\topacity: 0.85;\n}\n\n.cn-menu-widget--highlight-background .cn-menu-widget__item--active,\n.cn-menu-widget--highlight-background .cn-menu-widget__item--in-path {\n\tbackground: var(--color-primary-element-light, rgba(0, 112, 192, 0.1));\n\tcolor: var(--color-primary);\n}\n\n.cn-menu-widget--highlight-left-bar .cn-menu-widget__item--active {\n\tborder-left: 5px solid var(--color-primary);\n\tpadding-left: 6px;\n}\n\n.cn-menu-widget--highlight-left-bar .cn-menu-widget__item--in-path {\n\tborder-left: 4px solid var(--color-primary);\n\tpadding-left: 7px;\n\topacity: 0.85;\n}\n</style>\n"],"names":["CnMenuTreeNode","CnMenuItemIcon"],"mappings":"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"}
1
+ {"version":3,"file":"CnMenuWidget.vue2.js","sources":["../../../../src/components/CnMenuWidget/CnMenuWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-menu-widget\"\n\t\t:class=\"rootClasses\"\n\t\t@keydown.esc.stop=\"closeAll\">\n\t\t<!-- Empty state -->\n\t\t<div\n\t\t\tv-if=\"isEmpty\"\n\t\t\tclass=\"cn-menu-widget__empty\"\n\t\t\t:class=\"{ 'cn-menu-widget__empty--clickable': isInEditMode }\"\n\t\t\t:role=\"isInEditMode ? 'button' : null\"\n\t\t\t:tabindex=\"isInEditMode ? 0 : -1\"\n\t\t\t@click=\"onEmptyStateClick\"\n\t\t\t@keyup.enter=\"onEmptyStateClick\"\n\t\t\t@keyup.space=\"onEmptyStateClick\">\n\t\t\t<span class=\"cn-menu-widget__empty-icon\" aria-hidden=\"true\">⚙</span>\n\t\t\t<span>{{ t('nextcloud-vue', 'No menu items yet — click the gear icon to add some.') }}</span>\n\t\t</div>\n\n\t\t<!-- Tree style -->\n\t\t<ul\n\t\t\tv-else-if=\"effectiveStyle === 'tree'\"\n\t\t\tclass=\"cn-menu-widget__tree\"\n\t\t\trole=\"tree\">\n\t\t\t<CnMenuTreeNode\n\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t:key=\"`tree-${idx}`\"\n\t\t\t\t:item=\"item\"\n\t\t\t\t:depth=\"1\"\n\t\t\t\t:showIcons=\"showIcons\"\n\t\t\t\t:expandedByDefault=\"expandedByDefault\"\n\t\t\t\t:activePath=\"activePath\"\n\t\t\t\t:activeLeafKey=\"activeLeafKey\"\n\t\t\t\t:currentKey=\"`${idx}`\"\n\t\t\t\t:activeHighlight=\"activeItemHighlight\"\n\t\t\t\t@navigate=\"onTreeNavigate\" />\n\t\t</ul>\n\n\t\t<!-- Megamenu style -->\n\t\t<div\n\t\t\tv-else-if=\"effectiveStyle === 'megamenu'\"\n\t\t\tclass=\"cn-menu-widget__megamenu\">\n\t\t\t<ul\n\t\t\t\tclass=\"cn-menu-widget__bar\"\n\t\t\t\trole=\"menubar\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t\t:key=\"`mega-top-${idx}`\"\n\t\t\t\t\trole=\"none\"\n\t\t\t\t\tclass=\"cn-menu-widget__bar-item\">\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"isTopLink(item) ? 'a' : 'button'\"\n\t\t\t\t\t\t:ref=\"(el) => setTopRef(el, idx)\"\n\t\t\t\t\t\tv-bind=\"isTopLink(item) ? linkAttrs(item) : { type: 'button' }\"\n\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__bar-button\"\n\t\t\t\t\t\t:class=\"topItemClass(`${idx}`)\"\n\t\t\t\t\t\t:aria-haspopup=\"hasChildren(item) ? 'menu' : null\"\n\t\t\t\t\t\t:aria-expanded=\"megaOpenIndex === idx ? 'true' : 'false'\"\n\t\t\t\t\t\t@click=\"isTopLink(item) ? onLinkClick($event, item) : onMegaTopClick(idx, item)\"\n\t\t\t\t\t\t@keydown=\"onMegaTopKey($event, idx, item)\">\n\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !item.icon }\">\n\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"item.icon\" :icon=\"item.icon\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ item.label }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t\t<div\n\t\t\t\tv-if=\"megaOpenIndex !== null && hasChildren(items[megaOpenIndex])\"\n\t\t\t\tref=\"megaPanel\"\n\t\t\t\tclass=\"cn-menu-widget__mega-panel\"\n\t\t\t\trole=\"menu\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(child, childIdx) in items[megaOpenIndex].children\"\n\t\t\t\t\t:key=\"`mega-group-${childIdx}`\"\n\t\t\t\t\tclass=\"cn-menu-widget__mega-group\">\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"isLink(child) ? 'a' : 'button'\"\n\t\t\t\t\t\tv-bind=\"itemAttrs(child)\"\n\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__mega-group-title\"\n\t\t\t\t\t\t:class=\"topItemClass(`${megaOpenIndex}.${childIdx}`)\"\n\t\t\t\t\t\t@click=\"onLinkClick($event, child)\">\n\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !child.icon }\">\n\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"child.icon\" :icon=\"child.icon\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ child.label }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t\t<ul\n\t\t\t\t\t\tv-if=\"hasChildren(child)\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__mega-leaves\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"(leaf, leafIdx) in child.children\"\n\t\t\t\t\t\t\t:key=\"`mega-leaf-${leafIdx}`\"\n\t\t\t\t\t\t\trole=\"none\">\n\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t:is=\"isLink(leaf) ? 'a' : 'button'\"\n\t\t\t\t\t\t\t\tv-bind=\"itemAttrs(leaf)\"\n\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\tclass=\"cn-menu-widget__mega-leaf\"\n\t\t\t\t\t\t\t\t:class=\"topItemClass(`${megaOpenIndex}.${childIdx}.${leafIdx}`)\"\n\t\t\t\t\t\t\t\t@click=\"onLinkClick($event, leaf)\">\n\t\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !leaf.icon }\">\n\t\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"leaf.icon\" :icon=\"leaf.icon\" />\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ leaf.label }}</span>\n\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Dropdown style (default) -->\n\t\t<ul\n\t\t\tv-else\n\t\t\tclass=\"cn-menu-widget__bar\"\n\t\t\t:class=\"{ 'cn-menu-widget__bar--vertical': orientation === 'vertical' }\"\n\t\t\trole=\"menubar\">\n\t\t\t<li\n\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t:key=\"`drop-top-${idx}`\"\n\t\t\t\t:ref=\"(el) => setBarItemRef(el, idx)\"\n\t\t\t\trole=\"none\"\n\t\t\t\tclass=\"cn-menu-widget__bar-item\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"isTopLink(item) ? 'a' : 'button'\"\n\t\t\t\t\t:ref=\"(el) => setTopRef(el, idx)\"\n\t\t\t\t\tv-bind=\"isTopLink(item) ? linkAttrs(item) : { type: 'button' }\"\n\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\tclass=\"cn-menu-widget__bar-button\"\n\t\t\t\t\t:class=\"topItemClass(`${idx}`)\"\n\t\t\t\t\t:aria-haspopup=\"hasChildren(item) ? 'menu' : null\"\n\t\t\t\t\t:aria-expanded=\"dropOpenIndex === idx ? 'true' : 'false'\"\n\t\t\t\t\t@click=\"isTopLink(item) ? onLinkClick($event, item) : onDropdownTopClick(idx, item)\"\n\t\t\t\t\t@keydown.tab=\"closeAll\"\n\t\t\t\t\t@keydown.enter=\"onDropdownTopEnter($event, idx, item)\"\n\t\t\t\t\t@keydown.space.prevent=\"onDropdownTopClick(idx, item)\"\n\t\t\t\t\t@keydown.down.prevent=\"openDropdown(idx)\">\n\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !item.icon }\">\n\t\t\t\t\t\t<CnMenuItemIcon v-if=\"item.icon\" :icon=\"item.icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ item.label }}</span>\n\t\t\t\t\t<span v-if=\"hasChildren(item)\" class=\"cn-menu-widget__caret\" aria-hidden=\"true\">▾</span>\n\t\t\t\t</component>\n\t\t\t\t<ul\n\t\t\t\t\tv-if=\"dropOpenIndex === idx && hasChildren(item)\"\n\t\t\t\t\tclass=\"cn-menu-widget__dropdown\"\n\t\t\t\t\trole=\"menu\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"(child, childIdx) in item.children\"\n\t\t\t\t\t\t:key=\"`drop-child-${childIdx}`\"\n\t\t\t\t\t\trole=\"none\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item-wrap\"\n\t\t\t\t\t\t@mouseenter=\"hasChildren(child) && (flyoutOpenKey = `${idx}.${childIdx}`)\">\n\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t:is=\"isLink(child) ? 'a' : 'button'\"\n\t\t\t\t\t\t\tv-bind=\"itemAttrs(child)\"\n\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item\"\n\t\t\t\t\t\t\t:class=\"topItemClass(`${idx}.${childIdx}`)\"\n\t\t\t\t\t\t\t@click=\"onLinkClick($event, child)\">\n\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !child.icon }\">\n\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"child.icon\" :icon=\"child.icon\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ child.label }}</span>\n\t\t\t\t\t\t\t<span v-if=\"hasChildren(child)\" class=\"cn-menu-widget__caret cn-menu-widget__caret--right\" aria-hidden=\"true\">▸</span>\n\t\t\t\t\t\t</component>\n\t\t\t\t\t\t<ul\n\t\t\t\t\t\t\tv-if=\"flyoutOpenKey === `${idx}.${childIdx}` && hasChildren(child)\"\n\t\t\t\t\t\t\tclass=\"cn-menu-widget__flyout\"\n\t\t\t\t\t\t\trole=\"menu\">\n\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\tv-for=\"(leaf, leafIdx) in child.children\"\n\t\t\t\t\t\t\t\t:key=\"`drop-leaf-${leafIdx}`\"\n\t\t\t\t\t\t\t\trole=\"none\">\n\t\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t\t:is=\"isLink(leaf) ? 'a' : 'button'\"\n\t\t\t\t\t\t\t\t\tv-bind=\"itemAttrs(leaf)\"\n\t\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item\"\n\t\t\t\t\t\t\t\t\t:class=\"topItemClass(`${idx}.${childIdx}.${leafIdx}`)\"\n\t\t\t\t\t\t\t\t\t@click=\"onLinkClick($event, leaf)\">\n\t\t\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !leaf.icon }\">\n\t\t\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"leaf.icon\" :icon=\"leaf.icon\" />\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ leaf.label }}</span>\n\t\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport CnMenuItemIcon from './CnMenuItemIcon.vue'\nimport CnMenuTreeNode from './CnMenuTreeNode.vue'\nimport { followLinkClick } from '../../utils/linkNavigation.js'\nimport { computeActivePath, isActiveItem, isRouterPath, menuLinkAttrs } from './menuActive.js'\n\nconst VALID_STYLES = ['dropdown', 'megamenu', 'tree']\nconst VALID_ORIENTATIONS = ['horizontal', 'vertical']\nconst VALID_HIGHLIGHTS = ['background', 'underline', 'left-bar', 'none']\n\n/**\n * CnMenuWidget — renders a hierarchical navigation widget with three visual\n * styles (dropdown, megamenu, tree) and active-item highlighting based on\n * `window.location.pathname`.\n *\n * Active-item detection runs on mount and whenever `items` or the URL change;\n * ancestors of the active leaf are flagged \"in-path\" so the configured\n * highlight (`underline`, `background`, `left-bar`, `none`) renders\n * consistently. Keyboard navigation follows the WAI-ARIA Menu/Menubar pattern.\n *\n * Open dropdown/megamenu panels close on: re-clicking the toggle, Escape,\n * Tab, navigating a leaf, and clicking ANYWHERE else — including empty space\n * elsewhere in the widget itself, not just outside it entirely (a\n * document-level listener that checks containment against the open toggle\n * button + its own panel specifically, since the panels are plain in-DOM\n * markup, not a teleported popover with its own outside-click handling).\n *\n * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n */\nexport default {\n\tname: 'CnMenuWidget',\n\n\tcomponents: {\n\t\tCnMenuTreeNode,\n\t\tCnMenuItemIcon,\n\t},\n\n\tinject: {\n\t\t/** Host translate function provided by CnAppRoot, for the item labels. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content `{items, style, orientation, showIcons,\n\t\t * expandedByDefault, activeItemHighlight}`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Whether the current user is an admin (pairs with `canEdit`). */\n\t\tisAdmin: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether the surrounding shell is in edit mode. */\n\t\tcanEdit: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: [\n\t\t/** Fired when the empty-state gear is clicked in edit mode. */\n\t\t'edit-request',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tdropOpenIndex: null,\n\t\t\tflyoutOpenKey: null,\n\t\t\tmegaOpenIndex: null,\n\t\t\tcurrentLocation: this.readLocation(),\n\t\t\ttopRefs: [],\n\t\t\tbarItemRefs: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The configured menu items.\n\t\t *\n\t\t * @return {object[]} the items array.\n\t\t */\n\t\titems() {\n\t\t\tconst arr = this.content?.items\n\t\t\tif (!Array.isArray(arr)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\t// Labels are manifest strings: they go through the app's translate,\n\t\t\t// children included, so \"Case types\" reads \"Zaaktypen\" (DqCatalogus).\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key\n\t\t\tconst translated = (list) => list.map((item) => {\n\t\t\t\tif (!item || typeof item !== 'object') {\n\t\t\t\t\treturn item\n\t\t\t\t}\n\t\t\t\tconst out = { ...item }\n\t\t\t\tif (typeof item.label === 'string' && item.label !== '') {\n\t\t\t\t\tout.label = translate(item.label)\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(item.children)) {\n\t\t\t\t\tout.children = translated(item.children)\n\t\t\t\t}\n\t\t\t\treturn out\n\t\t\t})\n\t\t\treturn translated(arr)\n\t\t},\n\n\t\t/**\n\t\t * Whether the menu has no items.\n\t\t *\n\t\t * @return {boolean} true when empty.\n\t\t */\n\t\tisEmpty() {\n\t\t\treturn this.items.length === 0\n\t\t},\n\n\t\t/**\n\t\t * The validated visual style (default `dropdown`).\n\t\t *\n\t\t * @return {string} one of dropdown/megamenu/tree.\n\t\t */\n\t\teffectiveStyle() {\n\t\t\tconst s = this.content?.style\n\t\t\treturn VALID_STYLES.includes(s) ? s : 'dropdown'\n\t\t},\n\n\t\t/**\n\t\t * The validated orientation (tree is always vertical).\n\t\t *\n\t\t * @return {string} `horizontal` or `vertical`.\n\t\t */\n\t\torientation() {\n\t\t\tconst o = this.content?.orientation\n\t\t\tif (this.effectiveStyle === 'tree') {\n\t\t\t\treturn 'vertical'\n\t\t\t}\n\t\t\treturn VALID_ORIENTATIONS.includes(o) ? o : 'horizontal'\n\t\t},\n\n\t\t/**\n\t\t * Whether item icons render (default true).\n\t\t *\n\t\t * @return {boolean} the showIcons flag.\n\t\t */\n\t\tshowIcons() {\n\t\t\treturn this.content?.showIcons !== false\n\t\t},\n\n\t\t/**\n\t\t * Whether tree nodes start expanded.\n\t\t *\n\t\t * @return {boolean} the expandedByDefault flag.\n\t\t */\n\t\texpandedByDefault() {\n\t\t\treturn this.content?.expandedByDefault === true\n\t\t},\n\n\t\t/**\n\t\t * The validated active-item highlight style (default `underline`).\n\t\t *\n\t\t * @return {string} one of background/underline/left-bar/none.\n\t\t */\n\t\tactiveItemHighlight() {\n\t\t\tconst h = this.content?.activeItemHighlight\n\t\t\treturn VALID_HIGHLIGHTS.includes(h) ? h : 'underline'\n\t\t},\n\n\t\t/**\n\t\t * Whether the widget is in edit mode (empty-state gear clickable).\n\t\t *\n\t\t * @return {boolean} true when admin + canEdit.\n\t\t */\n\t\tisInEditMode() {\n\t\t\treturn this.isAdmin === true && this.canEdit === true\n\t\t},\n\n\t\t/**\n\t\t * The root style/highlight/orientation CSS classes.\n\t\t *\n\t\t * @return {object} the class map.\n\t\t */\n\t\trootClasses() {\n\t\t\treturn {\n\t\t\t\t[`cn-menu-widget--style-${this.effectiveStyle}`]: true,\n\t\t\t\t[`cn-menu-widget--highlight-${this.activeItemHighlight}`]: true,\n\t\t\t\t[`cn-menu-widget--orientation-${this.orientation}`]: true,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The dotted-key path map for the current location.\n\t\t *\n\t\t * @return {{path: object, leafKey: string|null}} the active-path map.\n\t\t */\n\t\tactivePathMap() {\n\t\t\treturn computeActivePath({\n\t\t\t\titems: this.items,\n\t\t\t\tcurrentLocation: this.currentLocation,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The dotted-key → state map.\n\t\t *\n\t\t * @return {Record<string, string>} the path map.\n\t\t */\n\t\tactivePath() {\n\t\t\treturn this.activePathMap.path\n\t\t},\n\n\t\t/**\n\t\t * The key of the deepest active leaf.\n\t\t *\n\t\t * @return {string|null} the leaf key.\n\t\t */\n\t\tactiveLeafKey() {\n\t\t\treturn this.activePathMap.leafKey\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.boundLocationListener = () => {\n\t\t\tthis.currentLocation = this.readLocation()\n\t\t}\n\t\tif (typeof window !== 'undefined') {\n\t\t\twindow.addEventListener('popstate', this.boundLocationListener)\n\t\t\twindow.addEventListener('hashchange', this.boundLocationListener)\n\t\t}\n\t\tthis.boundOutsideClickListener = this.onOutsideClick\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.addEventListener('click', this.boundOutsideClickListener)\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tif (typeof window !== 'undefined' && this.boundLocationListener) {\n\t\t\twindow.removeEventListener('popstate', this.boundLocationListener)\n\t\t\twindow.removeEventListener('hashchange', this.boundLocationListener)\n\t\t}\n\t\tif (typeof document !== 'undefined' && this.boundOutsideClickListener) {\n\t\t\tdocument.removeEventListener('click', this.boundOutsideClickListener)\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Read the current browser location into a small bag.\n\t\t *\n\t\t * @return {{pathname: string, host: string}} the location bag.\n\t\t */\n\t\treadLocation() {\n\t\t\tif (typeof window === 'undefined' || !window.location) {\n\t\t\t\treturn { pathname: '/', host: '' }\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tpathname: window.location.pathname || '/',\n\t\t\t\thost: window.location.host || '',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stash a top-level button ref by index.\n\t\t *\n\t\t * @param {HTMLElement} el the button element.\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\tsetTopRef(el, idx) {\n\t\t\tif (el) {\n\t\t\t\tthis.topRefs[idx] = el\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stash a dropdown-style top-level `<li>` ref by index. This `<li>`\n\t\t * contains both the toggle button and its dropdown/flyout panel, so a\n\t\t * single containment check against it covers the whole open structure.\n\t\t *\n\t\t * @param {HTMLElement} el the `<li>` element.\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\tsetBarItemRef(el, idx) {\n\t\t\tif (el) {\n\t\t\t\tthis.barItemRefs[idx] = el\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an item has children.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true when it has a non-empty children array.\n\t\t */\n\t\thasChildren(item) {\n\t\t\treturn item && Array.isArray(item.children) && item.children.length > 0\n\t\t},\n\n\t\t/**\n\t\t * The active-state class for a dotted key.\n\t\t *\n\t\t * @param {string} key the dotted item key.\n\t\t * @return {string} the class name (or `''`).\n\t\t */\n\t\ttopItemClass(key) {\n\t\t\tconst state = this.activePath[key]\n\t\t\tif (state === 'active' || key === this.activeLeafKey) {\n\t\t\t\treturn 'cn-menu-widget__item--active'\n\t\t\t}\n\t\t\tif (state === 'in-path') {\n\t\t\t\treturn 'cn-menu-widget__item--in-path'\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * Top-level dropdown click — navigate a leaf or toggle the dropdown.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonDropdownTopClick(idx, item) {\n\t\t\tif (!this.hasChildren(item)) {\n\t\t\t\tthis.onNavigate(item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.dropOpenIndex === idx) {\n\t\t\t\tthis.dropOpenIndex = null\n\t\t\t\tthis.flyoutOpenKey = null\n\t\t\t} else {\n\t\t\t\tthis.dropOpenIndex = idx\n\t\t\t\tthis.flyoutOpenKey = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open the dropdown at an index when it has children.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\topenDropdown(idx) {\n\t\t\tif (this.hasChildren(this.items[idx])) {\n\t\t\t\tthis.dropOpenIndex = idx\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Top-level megamenu click — navigate a leaf or toggle the panel.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonMegaTopClick(idx, item) {\n\t\t\tif (!this.hasChildren(item)) {\n\t\t\t\tthis.onNavigate(item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.megaOpenIndex = this.megaOpenIndex === idx ? null : idx\n\t\t},\n\n\t\t/**\n\t\t * Keyboard handler for megamenu top items.\n\t\t *\n\t\t * @param {KeyboardEvent} event the key event.\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonMegaTopKey(event, idx, item) {\n\t\t\t// Enter on a link clicks it natively.\n\t\t\tif (event.key === 'Enter' && this.isTopLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.onMegaTopClick(idx, item)\n\t\t\t} else if (event.key === 'Tab') {\n\t\t\t\tthis.megaOpenIndex = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Close every open dropdown/flyout/panel.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcloseAll() {\n\t\t\tthis.dropOpenIndex = null\n\t\t\tthis.flyoutOpenKey = null\n\t\t\tthis.megaOpenIndex = null\n\t\t},\n\n\t\t/**\n\t\t * Document-level click handler — closes any open dropdown/flyout/panel\n\t\t * when the click lands outside its own toggle button + panel, INCLUDING\n\t\t * empty space elsewhere in the widget (e.g. bar padding, gaps between\n\t\t * items) — not just outside the whole widget. The toggle button's own\n\t\t * `@click` handler manages opening/closing for its own clicks, so a\n\t\t * click still inside the open button/panel is left alone — otherwise\n\t\t * every open-toggle click would immediately re-close itself via this\n\t\t * same listener.\n\t\t *\n\t\t * @param {MouseEvent} event the document click event.\n\t\t * @return {void}\n\t\t */\n\t\tonOutsideClick(event) {\n\t\t\tconst target = event.target\n\t\t\tif (this.effectiveStyle === 'megamenu') {\n\t\t\t\tif (this.megaOpenIndex === null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst button = this.topRefs[this.megaOpenIndex]\n\t\t\t\tconst panel = this.$refs.megaPanel\n\t\t\t\tconst insideButton = Boolean(button && button.contains(target))\n\t\t\t\tconst insidePanel = Boolean(panel && panel.contains(target))\n\t\t\t\tif (!insideButton && !insidePanel) {\n\t\t\t\t\tthis.closeAll()\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Dropdown style — the button and its dropdown/flyout panel share\n\t\t\t// the same top-level `<li>`, so one containment check covers both.\n\t\t\tif (this.dropOpenIndex === null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst barItem = this.barItemRefs[this.dropOpenIndex]\n\t\t\tif (barItem && !barItem.contains(target)) {\n\t\t\t\tthis.closeAll()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an item renders as a link (it has a URL).\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true when the item carries a non-empty URL.\n\t\t */\n\t\tisLink(item) {\n\t\t\treturn Boolean(item) && typeof item.url === 'string' && item.url !== ''\n\t\t},\n\n\t\t/**\n\t\t * Whether a top-level item renders as a link. One with children\n\t\t * stays a button, because its click toggles the panel.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true for a top-level leaf with a URL.\n\t\t */\n\t\tisTopLink(item) {\n\t\t\treturn this.isLink(item) && !this.hasChildren(item)\n\t\t},\n\n\t\t/**\n\t\t * Anchor attributes for an item's URL: external URLs open in a new tab.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {object} the `href` (plus `target` / `rel` for external URLs).\n\t\t */\n\t\tlinkAttrs(item) {\n\t\t\treturn menuLinkAttrs(item.url, this.$router)\n\t\t},\n\n\t\t/**\n\t\t * Attributes for a panel item: link attributes, or `type=\"button\"`.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {object} the element attributes.\n\t\t */\n\t\titemAttrs(item) {\n\t\t\treturn this.isLink(item) ? this.linkAttrs(item) : { type: 'button' }\n\t\t},\n\n\t\t/**\n\t\t * Click on an item link: close the menus, then let the browser open\n\t\t * an external URL or a modified click, and route a plain click on an\n\t\t * internal one. A no-op for an item without a URL.\n\t\t *\n\t\t * @param {MouseEvent} event the click event.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonLinkClick(event, item) {\n\t\t\tif (!this.isLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.closeAll()\n\t\t\tif (isRouterPath(item.url)) {\n\t\t\t\tfollowLinkClick(event, item.url, this.$router)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Enter on a dropdown top item: a link follows natively (Enter\n\t\t * clicks it); a button toggles or navigates.\n\t\t *\n\t\t * @param {KeyboardEvent} event the key event.\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonDropdownTopEnter(event, idx, item) {\n\t\t\tif (this.isTopLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.onDropdownTopClick(idx, item)\n\t\t},\n\n\t\t/**\n\t\t * Navigation request from a tree node: a link click goes through the\n\t\t * link handler, a request without an event navigates directly.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @param {MouseEvent} [event] the click event, when the node's link was clicked.\n\t\t * @return {void}\n\t\t */\n\t\tonTreeNavigate(item, event) {\n\t\t\tif (event) {\n\t\t\t\tthis.onLinkClick(event, item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onNavigate(item)\n\t\t},\n\n\t\t/**\n\t\t * Navigate to an item's URL (router push for internal, new tab for\n\t\t * external), closing any open menus first. Used where no link click\n\t\t * is involved (keyboard activation of a menu item).\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonNavigate(item) {\n\t\t\tif (!item || typeof item.url !== 'string' || item.url === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.closeAll()\n\t\t\tconst url = item.url\n\t\t\tconst { href } = menuLinkAttrs(url, this.$router)\n\t\t\tif (this.isExternal(url)) {\n\t\t\t\tif (typeof window !== 'undefined' && typeof window.open === 'function') {\n\t\t\t\t\twindow.open(href, '_blank', 'noopener,noreferrer')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (isRouterPath(url) && this.$router && typeof this.$router.push === 'function') {\n\t\t\t\ttry {\n\t\t\t\t\tthis.$router.push(url)\n\t\t\t\t\treturn\n\t\t\t\t} catch {\n\t\t\t\t\t// Fall through to location assignment.\n\t\t\t\t}\n\t\t\t}\n\t\t\t// href is safeHref-checked, so a javascript: URL never reaches location.\n\t\t\tif (href !== '#' && typeof window !== 'undefined' && window.location) {\n\t\t\t\twindow.location.href = href\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether a URL is external (http/https).\n\t\t *\n\t\t * @param {string} url the URL.\n\t\t * @return {boolean} true for absolute http(s) URLs.\n\t\t */\n\t\tisExternal(url) {\n\t\t\treturn typeof url === 'string' && /^https?:\\/\\//i.test(url)\n\t\t},\n\n\t\t/**\n\t\t * Empty-state click — request edit when in edit mode.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonEmptyStateClick() {\n\t\t\tif (this.isInEditMode) {\n\t\t\t\tthis.$emit('edit-request')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Test helper — whether a URL is active for the current location.\n\t\t *\n\t\t * @param {string} itemUrl the URL to test.\n\t\t * @return {boolean} true when active.\n\t\t */\n\t\t__isActive(itemUrl) {\n\t\t\treturn isActiveItem(itemUrl, this.currentLocation)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-menu-widget {\n\twidth: 100%;\n\theight: 100%;\n\tpadding: 4px;\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n}\n\n.cn-menu-widget__empty {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-style: italic;\n\tborder: 1px dashed var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-menu-widget__empty--clickable {\n\tcursor: pointer;\n}\n\n.cn-menu-widget__empty-icon {\n\tfont-size: 18px;\n}\n\n.cn-menu-widget__bar {\n\tdisplay: flex;\n\tflex-direction: row;\n\tgap: 4px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-menu-widget__bar--vertical {\n\tflex-direction: column;\n}\n\n.cn-menu-widget__bar-item {\n\tposition: relative;\n\tlist-style: none;\n}\n\n.cn-menu-widget__bar-button,\n.cn-menu-widget__dropdown-item,\n.cn-menu-widget__mega-group-title,\n.cn-menu-widget__mega-leaf {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 6px 10px;\n\tbackground: transparent;\n\tborder: none;\n\tborder-radius: var(--border-radius);\n\tcolor: inherit;\n\tfont-size: 14px;\n\tcursor: pointer;\n\ttext-align: left;\n\ttext-decoration: none;\n\tbox-sizing: border-box;\n\twidth: 100%;\n}\n\n.cn-menu-widget__mega-group-title {\n\tfont-weight: 600;\n}\n\n.cn-menu-widget__bar-button:hover,\n.cn-menu-widget__dropdown-item:hover,\n.cn-menu-widget__mega-group-title:hover,\n.cn-menu-widget__mega-leaf:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-menu-widget__bar-button:focus-visible,\n.cn-menu-widget__dropdown-item:focus-visible {\n\toutline: 2px solid var(--color-primary);\n\toutline-offset: 2px;\n}\n\n.cn-menu-widget__icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 20px;\n\theight: 20px;\n}\n\n.cn-menu-widget__icon--hidden {\n\tvisibility: hidden;\n}\n\n.cn-menu-widget__caret {\n\tmargin-left: auto;\n\tfont-size: 10px;\n}\n\n.cn-menu-widget__dropdown {\n\tposition: absolute;\n\ttop: 100%;\n\tleft: 0;\n\tz-index: 50;\n\tmargin: 4px 0 0;\n\tpadding: 4px;\n\tmin-width: 180px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n\tlist-style: none;\n}\n\n.cn-menu-widget__dropdown-item-wrap {\n\tposition: relative;\n}\n\n.cn-menu-widget__flyout {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 100%;\n\tmargin-left: 2px;\n\tpadding: 4px;\n\tmin-width: 160px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n\tlist-style: none;\n}\n\n.cn-menu-widget__mega-panel {\n\tmargin-top: 4px;\n\tpadding: 12px;\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n\tgap: 12px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-menu-widget__mega-leaves {\n\tmargin: 4px 0 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-menu-widget__tree {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n/* Active-item highlight variants. */\n.cn-menu-widget--highlight-underline .cn-menu-widget__item--active {\n\tborder-bottom: 3px solid var(--color-primary);\n}\n\n.cn-menu-widget--highlight-underline .cn-menu-widget__item--in-path {\n\tborder-bottom: 2px solid var(--color-primary);\n\topacity: 0.85;\n}\n\n.cn-menu-widget--highlight-background .cn-menu-widget__item--active,\n.cn-menu-widget--highlight-background .cn-menu-widget__item--in-path {\n\tbackground: var(--color-primary-element-light, rgba(0, 112, 192, 0.1));\n\tcolor: var(--color-primary);\n}\n\n.cn-menu-widget--highlight-left-bar .cn-menu-widget__item--active {\n\tborder-left: 5px solid var(--color-primary);\n\tpadding-left: 6px;\n}\n\n.cn-menu-widget--highlight-left-bar .cn-menu-widget__item--in-path {\n\tborder-left: 4px solid var(--color-primary);\n\tpadding-left: 7px;\n\topacity: 0.85;\n}\n</style>\n"],"names":["CnMenuTreeNode","CnMenuItemIcon"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnMenuWidget.vue3.js","sources":["../../../../src/components/CnMenuWidget/CnMenuWidget.vue?vue&type=template&id=2a4e786e&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-menu-widget\"\n\t\t:class=\"rootClasses\"\n\t\t@keydown.esc.stop=\"closeAll\">\n\t\t<!-- Empty state -->\n\t\t<div\n\t\t\tv-if=\"isEmpty\"\n\t\t\tclass=\"cn-menu-widget__empty\"\n\t\t\t:class=\"{ 'cn-menu-widget__empty--clickable': isInEditMode }\"\n\t\t\t:role=\"isInEditMode ? 'button' : null\"\n\t\t\t:tabindex=\"isInEditMode ? 0 : -1\"\n\t\t\t@click=\"onEmptyStateClick\"\n\t\t\t@keyup.enter=\"onEmptyStateClick\"\n\t\t\t@keyup.space=\"onEmptyStateClick\">\n\t\t\t<span class=\"cn-menu-widget__empty-icon\" aria-hidden=\"true\">⚙</span>\n\t\t\t<span>{{ t('nextcloud-vue', 'No menu items yet — click the gear icon to add some.') }}</span>\n\t\t</div>\n\n\t\t<!-- Tree style -->\n\t\t<ul\n\t\t\tv-else-if=\"effectiveStyle === 'tree'\"\n\t\t\tclass=\"cn-menu-widget__tree\"\n\t\t\trole=\"tree\">\n\t\t\t<CnMenuTreeNode\n\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t:key=\"`tree-${idx}`\"\n\t\t\t\t:item=\"item\"\n\t\t\t\t:depth=\"1\"\n\t\t\t\t:showIcons=\"showIcons\"\n\t\t\t\t:expandedByDefault=\"expandedByDefault\"\n\t\t\t\t:activePath=\"activePath\"\n\t\t\t\t:activeLeafKey=\"activeLeafKey\"\n\t\t\t\t:currentKey=\"`${idx}`\"\n\t\t\t\t:activeHighlight=\"activeItemHighlight\"\n\t\t\t\t@navigate=\"onTreeNavigate\" />\n\t\t</ul>\n\n\t\t<!-- Megamenu style -->\n\t\t<div\n\t\t\tv-else-if=\"effectiveStyle === 'megamenu'\"\n\t\t\tclass=\"cn-menu-widget__megamenu\">\n\t\t\t<ul\n\t\t\t\tclass=\"cn-menu-widget__bar\"\n\t\t\t\trole=\"menubar\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t\t:key=\"`mega-top-${idx}`\"\n\t\t\t\t\trole=\"none\"\n\t\t\t\t\tclass=\"cn-menu-widget__bar-item\">\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"isTopLink(item) ? 'a' : 'button'\"\n\t\t\t\t\t\t:ref=\"(el) => setTopRef(el, idx)\"\n\t\t\t\t\t\tv-bind=\"isTopLink(item) ? linkAttrs(item) : { type: 'button' }\"\n\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__bar-button\"\n\t\t\t\t\t\t:class=\"topItemClass(`${idx}`)\"\n\t\t\t\t\t\t:aria-haspopup=\"hasChildren(item) ? 'menu' : null\"\n\t\t\t\t\t\t:aria-expanded=\"megaOpenIndex === idx ? 'true' : 'false'\"\n\t\t\t\t\t\t@click=\"isTopLink(item) ? onLinkClick($event, item) : onMegaTopClick(idx, item)\"\n\t\t\t\t\t\t@keydown=\"onMegaTopKey($event, idx, item)\">\n\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !item.icon }\">\n\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"item.icon\" :icon=\"item.icon\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ item.label }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t\t<div\n\t\t\t\tv-if=\"megaOpenIndex !== null && hasChildren(items[megaOpenIndex])\"\n\t\t\t\tref=\"megaPanel\"\n\t\t\t\tclass=\"cn-menu-widget__mega-panel\"\n\t\t\t\trole=\"menu\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(child, childIdx) in items[megaOpenIndex].children\"\n\t\t\t\t\t:key=\"`mega-group-${childIdx}`\"\n\t\t\t\t\tclass=\"cn-menu-widget__mega-group\">\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"isLink(child) ? 'a' : 'button'\"\n\t\t\t\t\t\tv-bind=\"itemAttrs(child)\"\n\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__mega-group-title\"\n\t\t\t\t\t\t:class=\"topItemClass(`${megaOpenIndex}.${childIdx}`)\"\n\t\t\t\t\t\t@click=\"onLinkClick($event, child)\">\n\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !child.icon }\">\n\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"child.icon\" :icon=\"child.icon\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ child.label }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t\t<ul\n\t\t\t\t\t\tv-if=\"hasChildren(child)\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__mega-leaves\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"(leaf, leafIdx) in child.children\"\n\t\t\t\t\t\t\t:key=\"`mega-leaf-${leafIdx}`\"\n\t\t\t\t\t\t\trole=\"none\">\n\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t:is=\"isLink(leaf) ? 'a' : 'button'\"\n\t\t\t\t\t\t\t\tv-bind=\"itemAttrs(leaf)\"\n\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\tclass=\"cn-menu-widget__mega-leaf\"\n\t\t\t\t\t\t\t\t:class=\"topItemClass(`${megaOpenIndex}.${childIdx}.${leafIdx}`)\"\n\t\t\t\t\t\t\t\t@click=\"onLinkClick($event, leaf)\">\n\t\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !leaf.icon }\">\n\t\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"leaf.icon\" :icon=\"leaf.icon\" />\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ leaf.label }}</span>\n\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Dropdown style (default) -->\n\t\t<ul\n\t\t\tv-else\n\t\t\tclass=\"cn-menu-widget__bar\"\n\t\t\t:class=\"{ 'cn-menu-widget__bar--vertical': orientation === 'vertical' }\"\n\t\t\trole=\"menubar\">\n\t\t\t<li\n\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t:key=\"`drop-top-${idx}`\"\n\t\t\t\t:ref=\"(el) => setBarItemRef(el, idx)\"\n\t\t\t\trole=\"none\"\n\t\t\t\tclass=\"cn-menu-widget__bar-item\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"isTopLink(item) ? 'a' : 'button'\"\n\t\t\t\t\t:ref=\"(el) => setTopRef(el, idx)\"\n\t\t\t\t\tv-bind=\"isTopLink(item) ? linkAttrs(item) : { type: 'button' }\"\n\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\tclass=\"cn-menu-widget__bar-button\"\n\t\t\t\t\t:class=\"topItemClass(`${idx}`)\"\n\t\t\t\t\t:aria-haspopup=\"hasChildren(item) ? 'menu' : null\"\n\t\t\t\t\t:aria-expanded=\"dropOpenIndex === idx ? 'true' : 'false'\"\n\t\t\t\t\t@click=\"isTopLink(item) ? onLinkClick($event, item) : onDropdownTopClick(idx, item)\"\n\t\t\t\t\t@keydown.tab=\"closeAll\"\n\t\t\t\t\t@keydown.enter=\"onDropdownTopEnter($event, idx, item)\"\n\t\t\t\t\t@keydown.space.prevent=\"onDropdownTopClick(idx, item)\"\n\t\t\t\t\t@keydown.down.prevent=\"openDropdown(idx)\">\n\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !item.icon }\">\n\t\t\t\t\t\t<CnMenuItemIcon v-if=\"item.icon\" :icon=\"item.icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ item.label }}</span>\n\t\t\t\t\t<span v-if=\"hasChildren(item)\" class=\"cn-menu-widget__caret\" aria-hidden=\"true\">▾</span>\n\t\t\t\t</component>\n\t\t\t\t<ul\n\t\t\t\t\tv-if=\"dropOpenIndex === idx && hasChildren(item)\"\n\t\t\t\t\tclass=\"cn-menu-widget__dropdown\"\n\t\t\t\t\trole=\"menu\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"(child, childIdx) in item.children\"\n\t\t\t\t\t\t:key=\"`drop-child-${childIdx}`\"\n\t\t\t\t\t\trole=\"none\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item-wrap\"\n\t\t\t\t\t\t@mouseenter=\"hasChildren(child) && (flyoutOpenKey = `${idx}.${childIdx}`)\">\n\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t:is=\"isLink(child) ? 'a' : 'button'\"\n\t\t\t\t\t\t\tv-bind=\"itemAttrs(child)\"\n\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item\"\n\t\t\t\t\t\t\t:class=\"topItemClass(`${idx}.${childIdx}`)\"\n\t\t\t\t\t\t\t@click=\"onLinkClick($event, child)\">\n\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !child.icon }\">\n\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"child.icon\" :icon=\"child.icon\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ child.label }}</span>\n\t\t\t\t\t\t\t<span v-if=\"hasChildren(child)\" class=\"cn-menu-widget__caret cn-menu-widget__caret--right\" aria-hidden=\"true\">▸</span>\n\t\t\t\t\t\t</component>\n\t\t\t\t\t\t<ul\n\t\t\t\t\t\t\tv-if=\"flyoutOpenKey === `${idx}.${childIdx}` && hasChildren(child)\"\n\t\t\t\t\t\t\tclass=\"cn-menu-widget__flyout\"\n\t\t\t\t\t\t\trole=\"menu\">\n\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\tv-for=\"(leaf, leafIdx) in child.children\"\n\t\t\t\t\t\t\t\t:key=\"`drop-leaf-${leafIdx}`\"\n\t\t\t\t\t\t\t\trole=\"none\">\n\t\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t\t:is=\"isLink(leaf) ? 'a' : 'button'\"\n\t\t\t\t\t\t\t\t\tv-bind=\"itemAttrs(leaf)\"\n\t\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item\"\n\t\t\t\t\t\t\t\t\t:class=\"topItemClass(`${idx}.${childIdx}.${leafIdx}`)\"\n\t\t\t\t\t\t\t\t\t@click=\"onLinkClick($event, leaf)\">\n\t\t\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !leaf.icon }\">\n\t\t\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"leaf.icon\" :icon=\"leaf.icon\" />\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ leaf.label }}</span>\n\t\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport CnMenuItemIcon from './CnMenuItemIcon.vue'\nimport CnMenuTreeNode from './CnMenuTreeNode.vue'\nimport { followLinkClick } from '../../utils/linkNavigation.js'\nimport { computeActivePath, isActiveItem, isRouterPath, menuLinkAttrs } from './menuActive.js'\n\nconst VALID_STYLES = ['dropdown', 'megamenu', 'tree']\nconst VALID_ORIENTATIONS = ['horizontal', 'vertical']\nconst VALID_HIGHLIGHTS = ['background', 'underline', 'left-bar', 'none']\n\n/**\n * CnMenuWidget — renders a hierarchical navigation widget with three visual\n * styles (dropdown, megamenu, tree) and active-item highlighting based on\n * `window.location.pathname`.\n *\n * Active-item detection runs on mount and whenever `items` or the URL change;\n * ancestors of the active leaf are flagged \"in-path\" so the configured\n * highlight (`underline`, `background`, `left-bar`, `none`) renders\n * consistently. Keyboard navigation follows the WAI-ARIA Menu/Menubar pattern.\n *\n * Open dropdown/megamenu panels close on: re-clicking the toggle, Escape,\n * Tab, navigating a leaf, and clicking ANYWHERE else — including empty space\n * elsewhere in the widget itself, not just outside it entirely (a\n * document-level listener that checks containment against the open toggle\n * button + its own panel specifically, since the panels are plain in-DOM\n * markup, not a teleported popover with its own outside-click handling).\n *\n * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n */\nexport default {\n\tname: 'CnMenuWidget',\n\n\tcomponents: {\n\t\tCnMenuTreeNode,\n\t\tCnMenuItemIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content `{items, style, orientation, showIcons,\n\t\t * expandedByDefault, activeItemHighlight}`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Whether the current user is an admin (pairs with `canEdit`). */\n\t\tisAdmin: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether the surrounding shell is in edit mode. */\n\t\tcanEdit: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: [\n\t\t/** Fired when the empty-state gear is clicked in edit mode. */\n\t\t'edit-request',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tdropOpenIndex: null,\n\t\t\tflyoutOpenKey: null,\n\t\t\tmegaOpenIndex: null,\n\t\t\tcurrentLocation: this.readLocation(),\n\t\t\ttopRefs: [],\n\t\t\tbarItemRefs: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The configured menu items.\n\t\t *\n\t\t * @return {object[]} the items array.\n\t\t */\n\t\titems() {\n\t\t\tconst arr = this.content?.items\n\t\t\treturn Array.isArray(arr) ? arr : []\n\t\t},\n\n\t\t/**\n\t\t * Whether the menu has no items.\n\t\t *\n\t\t * @return {boolean} true when empty.\n\t\t */\n\t\tisEmpty() {\n\t\t\treturn this.items.length === 0\n\t\t},\n\n\t\t/**\n\t\t * The validated visual style (default `dropdown`).\n\t\t *\n\t\t * @return {string} one of dropdown/megamenu/tree.\n\t\t */\n\t\teffectiveStyle() {\n\t\t\tconst s = this.content?.style\n\t\t\treturn VALID_STYLES.includes(s) ? s : 'dropdown'\n\t\t},\n\n\t\t/**\n\t\t * The validated orientation (tree is always vertical).\n\t\t *\n\t\t * @return {string} `horizontal` or `vertical`.\n\t\t */\n\t\torientation() {\n\t\t\tconst o = this.content?.orientation\n\t\t\tif (this.effectiveStyle === 'tree') {\n\t\t\t\treturn 'vertical'\n\t\t\t}\n\t\t\treturn VALID_ORIENTATIONS.includes(o) ? o : 'horizontal'\n\t\t},\n\n\t\t/**\n\t\t * Whether item icons render (default true).\n\t\t *\n\t\t * @return {boolean} the showIcons flag.\n\t\t */\n\t\tshowIcons() {\n\t\t\treturn this.content?.showIcons !== false\n\t\t},\n\n\t\t/**\n\t\t * Whether tree nodes start expanded.\n\t\t *\n\t\t * @return {boolean} the expandedByDefault flag.\n\t\t */\n\t\texpandedByDefault() {\n\t\t\treturn this.content?.expandedByDefault === true\n\t\t},\n\n\t\t/**\n\t\t * The validated active-item highlight style (default `underline`).\n\t\t *\n\t\t * @return {string} one of background/underline/left-bar/none.\n\t\t */\n\t\tactiveItemHighlight() {\n\t\t\tconst h = this.content?.activeItemHighlight\n\t\t\treturn VALID_HIGHLIGHTS.includes(h) ? h : 'underline'\n\t\t},\n\n\t\t/**\n\t\t * Whether the widget is in edit mode (empty-state gear clickable).\n\t\t *\n\t\t * @return {boolean} true when admin + canEdit.\n\t\t */\n\t\tisInEditMode() {\n\t\t\treturn this.isAdmin === true && this.canEdit === true\n\t\t},\n\n\t\t/**\n\t\t * The root style/highlight/orientation CSS classes.\n\t\t *\n\t\t * @return {object} the class map.\n\t\t */\n\t\trootClasses() {\n\t\t\treturn {\n\t\t\t\t[`cn-menu-widget--style-${this.effectiveStyle}`]: true,\n\t\t\t\t[`cn-menu-widget--highlight-${this.activeItemHighlight}`]: true,\n\t\t\t\t[`cn-menu-widget--orientation-${this.orientation}`]: true,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The dotted-key path map for the current location.\n\t\t *\n\t\t * @return {{path: object, leafKey: string|null}} the active-path map.\n\t\t */\n\t\tactivePathMap() {\n\t\t\treturn computeActivePath({\n\t\t\t\titems: this.items,\n\t\t\t\tcurrentLocation: this.currentLocation,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The dotted-key → state map.\n\t\t *\n\t\t * @return {Record<string, string>} the path map.\n\t\t */\n\t\tactivePath() {\n\t\t\treturn this.activePathMap.path\n\t\t},\n\n\t\t/**\n\t\t * The key of the deepest active leaf.\n\t\t *\n\t\t * @return {string|null} the leaf key.\n\t\t */\n\t\tactiveLeafKey() {\n\t\t\treturn this.activePathMap.leafKey\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.boundLocationListener = () => {\n\t\t\tthis.currentLocation = this.readLocation()\n\t\t}\n\t\tif (typeof window !== 'undefined') {\n\t\t\twindow.addEventListener('popstate', this.boundLocationListener)\n\t\t\twindow.addEventListener('hashchange', this.boundLocationListener)\n\t\t}\n\t\tthis.boundOutsideClickListener = this.onOutsideClick\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.addEventListener('click', this.boundOutsideClickListener)\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tif (typeof window !== 'undefined' && this.boundLocationListener) {\n\t\t\twindow.removeEventListener('popstate', this.boundLocationListener)\n\t\t\twindow.removeEventListener('hashchange', this.boundLocationListener)\n\t\t}\n\t\tif (typeof document !== 'undefined' && this.boundOutsideClickListener) {\n\t\t\tdocument.removeEventListener('click', this.boundOutsideClickListener)\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Read the current browser location into a small bag.\n\t\t *\n\t\t * @return {{pathname: string, host: string}} the location bag.\n\t\t */\n\t\treadLocation() {\n\t\t\tif (typeof window === 'undefined' || !window.location) {\n\t\t\t\treturn { pathname: '/', host: '' }\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tpathname: window.location.pathname || '/',\n\t\t\t\thost: window.location.host || '',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stash a top-level button ref by index.\n\t\t *\n\t\t * @param {HTMLElement} el the button element.\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\tsetTopRef(el, idx) {\n\t\t\tif (el) {\n\t\t\t\tthis.topRefs[idx] = el\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stash a dropdown-style top-level `<li>` ref by index. This `<li>`\n\t\t * contains both the toggle button and its dropdown/flyout panel, so a\n\t\t * single containment check against it covers the whole open structure.\n\t\t *\n\t\t * @param {HTMLElement} el the `<li>` element.\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\tsetBarItemRef(el, idx) {\n\t\t\tif (el) {\n\t\t\t\tthis.barItemRefs[idx] = el\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an item has children.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true when it has a non-empty children array.\n\t\t */\n\t\thasChildren(item) {\n\t\t\treturn item && Array.isArray(item.children) && item.children.length > 0\n\t\t},\n\n\t\t/**\n\t\t * The active-state class for a dotted key.\n\t\t *\n\t\t * @param {string} key the dotted item key.\n\t\t * @return {string} the class name (or `''`).\n\t\t */\n\t\ttopItemClass(key) {\n\t\t\tconst state = this.activePath[key]\n\t\t\tif (state === 'active' || key === this.activeLeafKey) {\n\t\t\t\treturn 'cn-menu-widget__item--active'\n\t\t\t}\n\t\t\tif (state === 'in-path') {\n\t\t\t\treturn 'cn-menu-widget__item--in-path'\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * Top-level dropdown click — navigate a leaf or toggle the dropdown.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonDropdownTopClick(idx, item) {\n\t\t\tif (!this.hasChildren(item)) {\n\t\t\t\tthis.onNavigate(item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.dropOpenIndex === idx) {\n\t\t\t\tthis.dropOpenIndex = null\n\t\t\t\tthis.flyoutOpenKey = null\n\t\t\t} else {\n\t\t\t\tthis.dropOpenIndex = idx\n\t\t\t\tthis.flyoutOpenKey = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open the dropdown at an index when it has children.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\topenDropdown(idx) {\n\t\t\tif (this.hasChildren(this.items[idx])) {\n\t\t\t\tthis.dropOpenIndex = idx\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Top-level megamenu click — navigate a leaf or toggle the panel.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonMegaTopClick(idx, item) {\n\t\t\tif (!this.hasChildren(item)) {\n\t\t\t\tthis.onNavigate(item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.megaOpenIndex = this.megaOpenIndex === idx ? null : idx\n\t\t},\n\n\t\t/**\n\t\t * Keyboard handler for megamenu top items.\n\t\t *\n\t\t * @param {KeyboardEvent} event the key event.\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonMegaTopKey(event, idx, item) {\n\t\t\t// Enter on a link clicks it natively.\n\t\t\tif (event.key === 'Enter' && this.isTopLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.onMegaTopClick(idx, item)\n\t\t\t} else if (event.key === 'Tab') {\n\t\t\t\tthis.megaOpenIndex = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Close every open dropdown/flyout/panel.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcloseAll() {\n\t\t\tthis.dropOpenIndex = null\n\t\t\tthis.flyoutOpenKey = null\n\t\t\tthis.megaOpenIndex = null\n\t\t},\n\n\t\t/**\n\t\t * Document-level click handler — closes any open dropdown/flyout/panel\n\t\t * when the click lands outside its own toggle button + panel, INCLUDING\n\t\t * empty space elsewhere in the widget (e.g. bar padding, gaps between\n\t\t * items) — not just outside the whole widget. The toggle button's own\n\t\t * `@click` handler manages opening/closing for its own clicks, so a\n\t\t * click still inside the open button/panel is left alone — otherwise\n\t\t * every open-toggle click would immediately re-close itself via this\n\t\t * same listener.\n\t\t *\n\t\t * @param {MouseEvent} event the document click event.\n\t\t * @return {void}\n\t\t */\n\t\tonOutsideClick(event) {\n\t\t\tconst target = event.target\n\t\t\tif (this.effectiveStyle === 'megamenu') {\n\t\t\t\tif (this.megaOpenIndex === null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst button = this.topRefs[this.megaOpenIndex]\n\t\t\t\tconst panel = this.$refs.megaPanel\n\t\t\t\tconst insideButton = Boolean(button && button.contains(target))\n\t\t\t\tconst insidePanel = Boolean(panel && panel.contains(target))\n\t\t\t\tif (!insideButton && !insidePanel) {\n\t\t\t\t\tthis.closeAll()\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Dropdown style — the button and its dropdown/flyout panel share\n\t\t\t// the same top-level `<li>`, so one containment check covers both.\n\t\t\tif (this.dropOpenIndex === null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst barItem = this.barItemRefs[this.dropOpenIndex]\n\t\t\tif (barItem && !barItem.contains(target)) {\n\t\t\t\tthis.closeAll()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an item renders as a link (it has a URL).\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true when the item carries a non-empty URL.\n\t\t */\n\t\tisLink(item) {\n\t\t\treturn Boolean(item) && typeof item.url === 'string' && item.url !== ''\n\t\t},\n\n\t\t/**\n\t\t * Whether a top-level item renders as a link. One with children\n\t\t * stays a button, because its click toggles the panel.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true for a top-level leaf with a URL.\n\t\t */\n\t\tisTopLink(item) {\n\t\t\treturn this.isLink(item) && !this.hasChildren(item)\n\t\t},\n\n\t\t/**\n\t\t * Anchor attributes for an item's URL: external URLs open in a new tab.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {object} the `href` (plus `target` / `rel` for external URLs).\n\t\t */\n\t\tlinkAttrs(item) {\n\t\t\treturn menuLinkAttrs(item.url, this.$router)\n\t\t},\n\n\t\t/**\n\t\t * Attributes for a panel item: link attributes, or `type=\"button\"`.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {object} the element attributes.\n\t\t */\n\t\titemAttrs(item) {\n\t\t\treturn this.isLink(item) ? this.linkAttrs(item) : { type: 'button' }\n\t\t},\n\n\t\t/**\n\t\t * Click on an item link: close the menus, then let the browser open\n\t\t * an external URL or a modified click, and route a plain click on an\n\t\t * internal one. A no-op for an item without a URL.\n\t\t *\n\t\t * @param {MouseEvent} event the click event.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonLinkClick(event, item) {\n\t\t\tif (!this.isLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.closeAll()\n\t\t\tif (isRouterPath(item.url)) {\n\t\t\t\tfollowLinkClick(event, item.url, this.$router)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Enter on a dropdown top item: a link follows natively (Enter\n\t\t * clicks it); a button toggles or navigates.\n\t\t *\n\t\t * @param {KeyboardEvent} event the key event.\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonDropdownTopEnter(event, idx, item) {\n\t\t\tif (this.isTopLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.onDropdownTopClick(idx, item)\n\t\t},\n\n\t\t/**\n\t\t * Navigation request from a tree node: a link click goes through the\n\t\t * link handler, a request without an event navigates directly.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @param {MouseEvent} [event] the click event, when the node's link was clicked.\n\t\t * @return {void}\n\t\t */\n\t\tonTreeNavigate(item, event) {\n\t\t\tif (event) {\n\t\t\t\tthis.onLinkClick(event, item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onNavigate(item)\n\t\t},\n\n\t\t/**\n\t\t * Navigate to an item's URL (router push for internal, new tab for\n\t\t * external), closing any open menus first. Used where no link click\n\t\t * is involved (keyboard activation of a menu item).\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonNavigate(item) {\n\t\t\tif (!item || typeof item.url !== 'string' || item.url === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.closeAll()\n\t\t\tconst url = item.url\n\t\t\tconst { href } = menuLinkAttrs(url, this.$router)\n\t\t\tif (this.isExternal(url)) {\n\t\t\t\tif (typeof window !== 'undefined' && typeof window.open === 'function') {\n\t\t\t\t\twindow.open(href, '_blank', 'noopener,noreferrer')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (isRouterPath(url) && this.$router && typeof this.$router.push === 'function') {\n\t\t\t\ttry {\n\t\t\t\t\tthis.$router.push(url)\n\t\t\t\t\treturn\n\t\t\t\t} catch {\n\t\t\t\t\t// Fall through to location assignment.\n\t\t\t\t}\n\t\t\t}\n\t\t\t// href is safeHref-checked, so a javascript: URL never reaches location.\n\t\t\tif (href !== '#' && typeof window !== 'undefined' && window.location) {\n\t\t\t\twindow.location.href = href\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether a URL is external (http/https).\n\t\t *\n\t\t * @param {string} url the URL.\n\t\t * @return {boolean} true for absolute http(s) URLs.\n\t\t */\n\t\tisExternal(url) {\n\t\t\treturn typeof url === 'string' && /^https?:\\/\\//i.test(url)\n\t\t},\n\n\t\t/**\n\t\t * Empty-state click — request edit when in edit mode.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonEmptyStateClick() {\n\t\t\tif (this.isInEditMode) {\n\t\t\t\tthis.$emit('edit-request')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Test helper — whether a URL is active for the current location.\n\t\t *\n\t\t * @param {string} itemUrl the URL to test.\n\t\t * @return {boolean} true when active.\n\t\t */\n\t\t__isActive(itemUrl) {\n\t\t\treturn isActiveItem(itemUrl, this.currentLocation)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-menu-widget {\n\twidth: 100%;\n\theight: 100%;\n\tpadding: 4px;\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n}\n\n.cn-menu-widget__empty {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-style: italic;\n\tborder: 1px dashed var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-menu-widget__empty--clickable {\n\tcursor: pointer;\n}\n\n.cn-menu-widget__empty-icon {\n\tfont-size: 18px;\n}\n\n.cn-menu-widget__bar {\n\tdisplay: flex;\n\tflex-direction: row;\n\tgap: 4px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-menu-widget__bar--vertical {\n\tflex-direction: column;\n}\n\n.cn-menu-widget__bar-item {\n\tposition: relative;\n\tlist-style: none;\n}\n\n.cn-menu-widget__bar-button,\n.cn-menu-widget__dropdown-item,\n.cn-menu-widget__mega-group-title,\n.cn-menu-widget__mega-leaf {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 6px 10px;\n\tbackground: transparent;\n\tborder: none;\n\tborder-radius: var(--border-radius);\n\tcolor: inherit;\n\tfont-size: 14px;\n\tcursor: pointer;\n\ttext-align: left;\n\ttext-decoration: none;\n\tbox-sizing: border-box;\n\twidth: 100%;\n}\n\n.cn-menu-widget__mega-group-title {\n\tfont-weight: 600;\n}\n\n.cn-menu-widget__bar-button:hover,\n.cn-menu-widget__dropdown-item:hover,\n.cn-menu-widget__mega-group-title:hover,\n.cn-menu-widget__mega-leaf:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-menu-widget__bar-button:focus-visible,\n.cn-menu-widget__dropdown-item:focus-visible {\n\toutline: 2px solid var(--color-primary);\n\toutline-offset: 2px;\n}\n\n.cn-menu-widget__icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 20px;\n\theight: 20px;\n}\n\n.cn-menu-widget__icon--hidden {\n\tvisibility: hidden;\n}\n\n.cn-menu-widget__caret {\n\tmargin-left: auto;\n\tfont-size: 10px;\n}\n\n.cn-menu-widget__dropdown {\n\tposition: absolute;\n\ttop: 100%;\n\tleft: 0;\n\tz-index: 50;\n\tmargin: 4px 0 0;\n\tpadding: 4px;\n\tmin-width: 180px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n\tlist-style: none;\n}\n\n.cn-menu-widget__dropdown-item-wrap {\n\tposition: relative;\n}\n\n.cn-menu-widget__flyout {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 100%;\n\tmargin-left: 2px;\n\tpadding: 4px;\n\tmin-width: 160px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n\tlist-style: none;\n}\n\n.cn-menu-widget__mega-panel {\n\tmargin-top: 4px;\n\tpadding: 12px;\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n\tgap: 12px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-menu-widget__mega-leaves {\n\tmargin: 4px 0 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-menu-widget__tree {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n/* Active-item highlight variants. */\n.cn-menu-widget--highlight-underline .cn-menu-widget__item--active {\n\tborder-bottom: 3px solid var(--color-primary);\n}\n\n.cn-menu-widget--highlight-underline .cn-menu-widget__item--in-path {\n\tborder-bottom: 2px solid var(--color-primary);\n\topacity: 0.85;\n}\n\n.cn-menu-widget--highlight-background .cn-menu-widget__item--active,\n.cn-menu-widget--highlight-background .cn-menu-widget__item--in-path {\n\tbackground: var(--color-primary-element-light, rgba(0, 112, 192, 0.1));\n\tcolor: var(--color-primary);\n}\n\n.cn-menu-widget--highlight-left-bar .cn-menu-widget__item--active {\n\tborder-left: 5px solid var(--color-primary);\n\tpadding-left: 6px;\n}\n\n.cn-menu-widget--highlight-left-bar .cn-menu-widget__item--in-path {\n\tborder-left: 4px solid var(--color-primary);\n\tpadding-left: 7px;\n\topacity: 0.85;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_openBlock","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_mergeProps","_toDisplayString","_withKeys","_withModifiers"],"mappings":"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"}
1
+ {"version":3,"file":"CnMenuWidget.vue3.js","sources":["../../../../src/components/CnMenuWidget/CnMenuWidget.vue?vue&type=template&id=2edac2f0&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-menu-widget\"\n\t\t:class=\"rootClasses\"\n\t\t@keydown.esc.stop=\"closeAll\">\n\t\t<!-- Empty state -->\n\t\t<div\n\t\t\tv-if=\"isEmpty\"\n\t\t\tclass=\"cn-menu-widget__empty\"\n\t\t\t:class=\"{ 'cn-menu-widget__empty--clickable': isInEditMode }\"\n\t\t\t:role=\"isInEditMode ? 'button' : null\"\n\t\t\t:tabindex=\"isInEditMode ? 0 : -1\"\n\t\t\t@click=\"onEmptyStateClick\"\n\t\t\t@keyup.enter=\"onEmptyStateClick\"\n\t\t\t@keyup.space=\"onEmptyStateClick\">\n\t\t\t<span class=\"cn-menu-widget__empty-icon\" aria-hidden=\"true\">⚙</span>\n\t\t\t<span>{{ t('nextcloud-vue', 'No menu items yet — click the gear icon to add some.') }}</span>\n\t\t</div>\n\n\t\t<!-- Tree style -->\n\t\t<ul\n\t\t\tv-else-if=\"effectiveStyle === 'tree'\"\n\t\t\tclass=\"cn-menu-widget__tree\"\n\t\t\trole=\"tree\">\n\t\t\t<CnMenuTreeNode\n\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t:key=\"`tree-${idx}`\"\n\t\t\t\t:item=\"item\"\n\t\t\t\t:depth=\"1\"\n\t\t\t\t:showIcons=\"showIcons\"\n\t\t\t\t:expandedByDefault=\"expandedByDefault\"\n\t\t\t\t:activePath=\"activePath\"\n\t\t\t\t:activeLeafKey=\"activeLeafKey\"\n\t\t\t\t:currentKey=\"`${idx}`\"\n\t\t\t\t:activeHighlight=\"activeItemHighlight\"\n\t\t\t\t@navigate=\"onTreeNavigate\" />\n\t\t</ul>\n\n\t\t<!-- Megamenu style -->\n\t\t<div\n\t\t\tv-else-if=\"effectiveStyle === 'megamenu'\"\n\t\t\tclass=\"cn-menu-widget__megamenu\">\n\t\t\t<ul\n\t\t\t\tclass=\"cn-menu-widget__bar\"\n\t\t\t\trole=\"menubar\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t\t:key=\"`mega-top-${idx}`\"\n\t\t\t\t\trole=\"none\"\n\t\t\t\t\tclass=\"cn-menu-widget__bar-item\">\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"isTopLink(item) ? 'a' : 'button'\"\n\t\t\t\t\t\t:ref=\"(el) => setTopRef(el, idx)\"\n\t\t\t\t\t\tv-bind=\"isTopLink(item) ? linkAttrs(item) : { type: 'button' }\"\n\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__bar-button\"\n\t\t\t\t\t\t:class=\"topItemClass(`${idx}`)\"\n\t\t\t\t\t\t:aria-haspopup=\"hasChildren(item) ? 'menu' : null\"\n\t\t\t\t\t\t:aria-expanded=\"megaOpenIndex === idx ? 'true' : 'false'\"\n\t\t\t\t\t\t@click=\"isTopLink(item) ? onLinkClick($event, item) : onMegaTopClick(idx, item)\"\n\t\t\t\t\t\t@keydown=\"onMegaTopKey($event, idx, item)\">\n\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !item.icon }\">\n\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"item.icon\" :icon=\"item.icon\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ item.label }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t\t<div\n\t\t\t\tv-if=\"megaOpenIndex !== null && hasChildren(items[megaOpenIndex])\"\n\t\t\t\tref=\"megaPanel\"\n\t\t\t\tclass=\"cn-menu-widget__mega-panel\"\n\t\t\t\trole=\"menu\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(child, childIdx) in items[megaOpenIndex].children\"\n\t\t\t\t\t:key=\"`mega-group-${childIdx}`\"\n\t\t\t\t\tclass=\"cn-menu-widget__mega-group\">\n\t\t\t\t\t<component\n\t\t\t\t\t\t:is=\"isLink(child) ? 'a' : 'button'\"\n\t\t\t\t\t\tv-bind=\"itemAttrs(child)\"\n\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__mega-group-title\"\n\t\t\t\t\t\t:class=\"topItemClass(`${megaOpenIndex}.${childIdx}`)\"\n\t\t\t\t\t\t@click=\"onLinkClick($event, child)\">\n\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !child.icon }\">\n\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"child.icon\" :icon=\"child.icon\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ child.label }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t\t<ul\n\t\t\t\t\t\tv-if=\"hasChildren(child)\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__mega-leaves\">\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tv-for=\"(leaf, leafIdx) in child.children\"\n\t\t\t\t\t\t\t:key=\"`mega-leaf-${leafIdx}`\"\n\t\t\t\t\t\t\trole=\"none\">\n\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t:is=\"isLink(leaf) ? 'a' : 'button'\"\n\t\t\t\t\t\t\t\tv-bind=\"itemAttrs(leaf)\"\n\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\tclass=\"cn-menu-widget__mega-leaf\"\n\t\t\t\t\t\t\t\t:class=\"topItemClass(`${megaOpenIndex}.${childIdx}.${leafIdx}`)\"\n\t\t\t\t\t\t\t\t@click=\"onLinkClick($event, leaf)\">\n\t\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !leaf.icon }\">\n\t\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"leaf.icon\" :icon=\"leaf.icon\" />\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ leaf.label }}</span>\n\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Dropdown style (default) -->\n\t\t<ul\n\t\t\tv-else\n\t\t\tclass=\"cn-menu-widget__bar\"\n\t\t\t:class=\"{ 'cn-menu-widget__bar--vertical': orientation === 'vertical' }\"\n\t\t\trole=\"menubar\">\n\t\t\t<li\n\t\t\t\tv-for=\"(item, idx) in items\"\n\t\t\t\t:key=\"`drop-top-${idx}`\"\n\t\t\t\t:ref=\"(el) => setBarItemRef(el, idx)\"\n\t\t\t\trole=\"none\"\n\t\t\t\tclass=\"cn-menu-widget__bar-item\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"isTopLink(item) ? 'a' : 'button'\"\n\t\t\t\t\t:ref=\"(el) => setTopRef(el, idx)\"\n\t\t\t\t\tv-bind=\"isTopLink(item) ? linkAttrs(item) : { type: 'button' }\"\n\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\tclass=\"cn-menu-widget__bar-button\"\n\t\t\t\t\t:class=\"topItemClass(`${idx}`)\"\n\t\t\t\t\t:aria-haspopup=\"hasChildren(item) ? 'menu' : null\"\n\t\t\t\t\t:aria-expanded=\"dropOpenIndex === idx ? 'true' : 'false'\"\n\t\t\t\t\t@click=\"isTopLink(item) ? onLinkClick($event, item) : onDropdownTopClick(idx, item)\"\n\t\t\t\t\t@keydown.tab=\"closeAll\"\n\t\t\t\t\t@keydown.enter=\"onDropdownTopEnter($event, idx, item)\"\n\t\t\t\t\t@keydown.space.prevent=\"onDropdownTopClick(idx, item)\"\n\t\t\t\t\t@keydown.down.prevent=\"openDropdown(idx)\">\n\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !item.icon }\">\n\t\t\t\t\t\t<CnMenuItemIcon v-if=\"item.icon\" :icon=\"item.icon\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ item.label }}</span>\n\t\t\t\t\t<span v-if=\"hasChildren(item)\" class=\"cn-menu-widget__caret\" aria-hidden=\"true\">▾</span>\n\t\t\t\t</component>\n\t\t\t\t<ul\n\t\t\t\t\tv-if=\"dropOpenIndex === idx && hasChildren(item)\"\n\t\t\t\t\tclass=\"cn-menu-widget__dropdown\"\n\t\t\t\t\trole=\"menu\">\n\t\t\t\t\t<li\n\t\t\t\t\t\tv-for=\"(child, childIdx) in item.children\"\n\t\t\t\t\t\t:key=\"`drop-child-${childIdx}`\"\n\t\t\t\t\t\trole=\"none\"\n\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item-wrap\"\n\t\t\t\t\t\t@mouseenter=\"hasChildren(child) && (flyoutOpenKey = `${idx}.${childIdx}`)\">\n\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t:is=\"isLink(child) ? 'a' : 'button'\"\n\t\t\t\t\t\t\tv-bind=\"itemAttrs(child)\"\n\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item\"\n\t\t\t\t\t\t\t:class=\"topItemClass(`${idx}.${childIdx}`)\"\n\t\t\t\t\t\t\t@click=\"onLinkClick($event, child)\">\n\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !child.icon }\">\n\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"child.icon\" :icon=\"child.icon\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ child.label }}</span>\n\t\t\t\t\t\t\t<span v-if=\"hasChildren(child)\" class=\"cn-menu-widget__caret cn-menu-widget__caret--right\" aria-hidden=\"true\">▸</span>\n\t\t\t\t\t\t</component>\n\t\t\t\t\t\t<ul\n\t\t\t\t\t\t\tv-if=\"flyoutOpenKey === `${idx}.${childIdx}` && hasChildren(child)\"\n\t\t\t\t\t\t\tclass=\"cn-menu-widget__flyout\"\n\t\t\t\t\t\t\trole=\"menu\">\n\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\tv-for=\"(leaf, leafIdx) in child.children\"\n\t\t\t\t\t\t\t\t:key=\"`drop-leaf-${leafIdx}`\"\n\t\t\t\t\t\t\t\trole=\"none\">\n\t\t\t\t\t\t\t\t<component\n\t\t\t\t\t\t\t\t\t:is=\"isLink(leaf) ? 'a' : 'button'\"\n\t\t\t\t\t\t\t\t\tv-bind=\"itemAttrs(leaf)\"\n\t\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\t\tclass=\"cn-menu-widget__dropdown-item\"\n\t\t\t\t\t\t\t\t\t:class=\"topItemClass(`${idx}.${childIdx}.${leafIdx}`)\"\n\t\t\t\t\t\t\t\t\t@click=\"onLinkClick($event, leaf)\">\n\t\t\t\t\t\t\t\t\t<span v-if=\"showIcons\" class=\"cn-menu-widget__icon\" :class=\"{ 'cn-menu-widget__icon--hidden': !leaf.icon }\">\n\t\t\t\t\t\t\t\t\t\t<CnMenuItemIcon v-if=\"leaf.icon\" :icon=\"leaf.icon\" />\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t<span class=\"cn-menu-widget__label\">{{ leaf.label }}</span>\n\t\t\t\t\t\t\t\t</component>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport CnMenuItemIcon from './CnMenuItemIcon.vue'\nimport CnMenuTreeNode from './CnMenuTreeNode.vue'\nimport { followLinkClick } from '../../utils/linkNavigation.js'\nimport { computeActivePath, isActiveItem, isRouterPath, menuLinkAttrs } from './menuActive.js'\n\nconst VALID_STYLES = ['dropdown', 'megamenu', 'tree']\nconst VALID_ORIENTATIONS = ['horizontal', 'vertical']\nconst VALID_HIGHLIGHTS = ['background', 'underline', 'left-bar', 'none']\n\n/**\n * CnMenuWidget — renders a hierarchical navigation widget with three visual\n * styles (dropdown, megamenu, tree) and active-item highlighting based on\n * `window.location.pathname`.\n *\n * Active-item detection runs on mount and whenever `items` or the URL change;\n * ancestors of the active leaf are flagged \"in-path\" so the configured\n * highlight (`underline`, `background`, `left-bar`, `none`) renders\n * consistently. Keyboard navigation follows the WAI-ARIA Menu/Menubar pattern.\n *\n * Open dropdown/megamenu panels close on: re-clicking the toggle, Escape,\n * Tab, navigating a leaf, and clicking ANYWHERE else — including empty space\n * elsewhere in the widget itself, not just outside it entirely (a\n * document-level listener that checks containment against the open toggle\n * button + its own panel specifically, since the panels are plain in-DOM\n * markup, not a teleported popover with its own outside-click handling).\n *\n * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n */\nexport default {\n\tname: 'CnMenuWidget',\n\n\tcomponents: {\n\t\tCnMenuTreeNode,\n\t\tCnMenuItemIcon,\n\t},\n\n\tinject: {\n\t\t/** Host translate function provided by CnAppRoot, for the item labels. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content `{items, style, orientation, showIcons,\n\t\t * expandedByDefault, activeItemHighlight}`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Whether the current user is an admin (pairs with `canEdit`). */\n\t\tisAdmin: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether the surrounding shell is in edit mode. */\n\t\tcanEdit: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: [\n\t\t/** Fired when the empty-state gear is clicked in edit mode. */\n\t\t'edit-request',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tdropOpenIndex: null,\n\t\t\tflyoutOpenKey: null,\n\t\t\tmegaOpenIndex: null,\n\t\t\tcurrentLocation: this.readLocation(),\n\t\t\ttopRefs: [],\n\t\t\tbarItemRefs: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The configured menu items.\n\t\t *\n\t\t * @return {object[]} the items array.\n\t\t */\n\t\titems() {\n\t\t\tconst arr = this.content?.items\n\t\t\tif (!Array.isArray(arr)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\t// Labels are manifest strings: they go through the app's translate,\n\t\t\t// children included, so \"Case types\" reads \"Zaaktypen\" (DqCatalogus).\n\t\t\tconst translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key\n\t\t\tconst translated = (list) => list.map((item) => {\n\t\t\t\tif (!item || typeof item !== 'object') {\n\t\t\t\t\treturn item\n\t\t\t\t}\n\t\t\t\tconst out = { ...item }\n\t\t\t\tif (typeof item.label === 'string' && item.label !== '') {\n\t\t\t\t\tout.label = translate(item.label)\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(item.children)) {\n\t\t\t\t\tout.children = translated(item.children)\n\t\t\t\t}\n\t\t\t\treturn out\n\t\t\t})\n\t\t\treturn translated(arr)\n\t\t},\n\n\t\t/**\n\t\t * Whether the menu has no items.\n\t\t *\n\t\t * @return {boolean} true when empty.\n\t\t */\n\t\tisEmpty() {\n\t\t\treturn this.items.length === 0\n\t\t},\n\n\t\t/**\n\t\t * The validated visual style (default `dropdown`).\n\t\t *\n\t\t * @return {string} one of dropdown/megamenu/tree.\n\t\t */\n\t\teffectiveStyle() {\n\t\t\tconst s = this.content?.style\n\t\t\treturn VALID_STYLES.includes(s) ? s : 'dropdown'\n\t\t},\n\n\t\t/**\n\t\t * The validated orientation (tree is always vertical).\n\t\t *\n\t\t * @return {string} `horizontal` or `vertical`.\n\t\t */\n\t\torientation() {\n\t\t\tconst o = this.content?.orientation\n\t\t\tif (this.effectiveStyle === 'tree') {\n\t\t\t\treturn 'vertical'\n\t\t\t}\n\t\t\treturn VALID_ORIENTATIONS.includes(o) ? o : 'horizontal'\n\t\t},\n\n\t\t/**\n\t\t * Whether item icons render (default true).\n\t\t *\n\t\t * @return {boolean} the showIcons flag.\n\t\t */\n\t\tshowIcons() {\n\t\t\treturn this.content?.showIcons !== false\n\t\t},\n\n\t\t/**\n\t\t * Whether tree nodes start expanded.\n\t\t *\n\t\t * @return {boolean} the expandedByDefault flag.\n\t\t */\n\t\texpandedByDefault() {\n\t\t\treturn this.content?.expandedByDefault === true\n\t\t},\n\n\t\t/**\n\t\t * The validated active-item highlight style (default `underline`).\n\t\t *\n\t\t * @return {string} one of background/underline/left-bar/none.\n\t\t */\n\t\tactiveItemHighlight() {\n\t\t\tconst h = this.content?.activeItemHighlight\n\t\t\treturn VALID_HIGHLIGHTS.includes(h) ? h : 'underline'\n\t\t},\n\n\t\t/**\n\t\t * Whether the widget is in edit mode (empty-state gear clickable).\n\t\t *\n\t\t * @return {boolean} true when admin + canEdit.\n\t\t */\n\t\tisInEditMode() {\n\t\t\treturn this.isAdmin === true && this.canEdit === true\n\t\t},\n\n\t\t/**\n\t\t * The root style/highlight/orientation CSS classes.\n\t\t *\n\t\t * @return {object} the class map.\n\t\t */\n\t\trootClasses() {\n\t\t\treturn {\n\t\t\t\t[`cn-menu-widget--style-${this.effectiveStyle}`]: true,\n\t\t\t\t[`cn-menu-widget--highlight-${this.activeItemHighlight}`]: true,\n\t\t\t\t[`cn-menu-widget--orientation-${this.orientation}`]: true,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The dotted-key path map for the current location.\n\t\t *\n\t\t * @return {{path: object, leafKey: string|null}} the active-path map.\n\t\t */\n\t\tactivePathMap() {\n\t\t\treturn computeActivePath({\n\t\t\t\titems: this.items,\n\t\t\t\tcurrentLocation: this.currentLocation,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The dotted-key → state map.\n\t\t *\n\t\t * @return {Record<string, string>} the path map.\n\t\t */\n\t\tactivePath() {\n\t\t\treturn this.activePathMap.path\n\t\t},\n\n\t\t/**\n\t\t * The key of the deepest active leaf.\n\t\t *\n\t\t * @return {string|null} the leaf key.\n\t\t */\n\t\tactiveLeafKey() {\n\t\t\treturn this.activePathMap.leafKey\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.boundLocationListener = () => {\n\t\t\tthis.currentLocation = this.readLocation()\n\t\t}\n\t\tif (typeof window !== 'undefined') {\n\t\t\twindow.addEventListener('popstate', this.boundLocationListener)\n\t\t\twindow.addEventListener('hashchange', this.boundLocationListener)\n\t\t}\n\t\tthis.boundOutsideClickListener = this.onOutsideClick\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.addEventListener('click', this.boundOutsideClickListener)\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tif (typeof window !== 'undefined' && this.boundLocationListener) {\n\t\t\twindow.removeEventListener('popstate', this.boundLocationListener)\n\t\t\twindow.removeEventListener('hashchange', this.boundLocationListener)\n\t\t}\n\t\tif (typeof document !== 'undefined' && this.boundOutsideClickListener) {\n\t\t\tdocument.removeEventListener('click', this.boundOutsideClickListener)\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Read the current browser location into a small bag.\n\t\t *\n\t\t * @return {{pathname: string, host: string}} the location bag.\n\t\t */\n\t\treadLocation() {\n\t\t\tif (typeof window === 'undefined' || !window.location) {\n\t\t\t\treturn { pathname: '/', host: '' }\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tpathname: window.location.pathname || '/',\n\t\t\t\thost: window.location.host || '',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stash a top-level button ref by index.\n\t\t *\n\t\t * @param {HTMLElement} el the button element.\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\tsetTopRef(el, idx) {\n\t\t\tif (el) {\n\t\t\t\tthis.topRefs[idx] = el\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Stash a dropdown-style top-level `<li>` ref by index. This `<li>`\n\t\t * contains both the toggle button and its dropdown/flyout panel, so a\n\t\t * single containment check against it covers the whole open structure.\n\t\t *\n\t\t * @param {HTMLElement} el the `<li>` element.\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\tsetBarItemRef(el, idx) {\n\t\t\tif (el) {\n\t\t\t\tthis.barItemRefs[idx] = el\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an item has children.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true when it has a non-empty children array.\n\t\t */\n\t\thasChildren(item) {\n\t\t\treturn item && Array.isArray(item.children) && item.children.length > 0\n\t\t},\n\n\t\t/**\n\t\t * The active-state class for a dotted key.\n\t\t *\n\t\t * @param {string} key the dotted item key.\n\t\t * @return {string} the class name (or `''`).\n\t\t */\n\t\ttopItemClass(key) {\n\t\t\tconst state = this.activePath[key]\n\t\t\tif (state === 'active' || key === this.activeLeafKey) {\n\t\t\t\treturn 'cn-menu-widget__item--active'\n\t\t\t}\n\t\t\tif (state === 'in-path') {\n\t\t\t\treturn 'cn-menu-widget__item--in-path'\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/**\n\t\t * Top-level dropdown click — navigate a leaf or toggle the dropdown.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonDropdownTopClick(idx, item) {\n\t\t\tif (!this.hasChildren(item)) {\n\t\t\t\tthis.onNavigate(item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.dropOpenIndex === idx) {\n\t\t\t\tthis.dropOpenIndex = null\n\t\t\t\tthis.flyoutOpenKey = null\n\t\t\t} else {\n\t\t\t\tthis.dropOpenIndex = idx\n\t\t\t\tthis.flyoutOpenKey = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open the dropdown at an index when it has children.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @return {void}\n\t\t */\n\t\topenDropdown(idx) {\n\t\t\tif (this.hasChildren(this.items[idx])) {\n\t\t\t\tthis.dropOpenIndex = idx\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Top-level megamenu click — navigate a leaf or toggle the panel.\n\t\t *\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonMegaTopClick(idx, item) {\n\t\t\tif (!this.hasChildren(item)) {\n\t\t\t\tthis.onNavigate(item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.megaOpenIndex = this.megaOpenIndex === idx ? null : idx\n\t\t},\n\n\t\t/**\n\t\t * Keyboard handler for megamenu top items.\n\t\t *\n\t\t * @param {KeyboardEvent} event the key event.\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonMegaTopKey(event, idx, item) {\n\t\t\t// Enter on a link clicks it natively.\n\t\t\tif (event.key === 'Enter' && this.isTopLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.onMegaTopClick(idx, item)\n\t\t\t} else if (event.key === 'Tab') {\n\t\t\t\tthis.megaOpenIndex = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Close every open dropdown/flyout/panel.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcloseAll() {\n\t\t\tthis.dropOpenIndex = null\n\t\t\tthis.flyoutOpenKey = null\n\t\t\tthis.megaOpenIndex = null\n\t\t},\n\n\t\t/**\n\t\t * Document-level click handler — closes any open dropdown/flyout/panel\n\t\t * when the click lands outside its own toggle button + panel, INCLUDING\n\t\t * empty space elsewhere in the widget (e.g. bar padding, gaps between\n\t\t * items) — not just outside the whole widget. The toggle button's own\n\t\t * `@click` handler manages opening/closing for its own clicks, so a\n\t\t * click still inside the open button/panel is left alone — otherwise\n\t\t * every open-toggle click would immediately re-close itself via this\n\t\t * same listener.\n\t\t *\n\t\t * @param {MouseEvent} event the document click event.\n\t\t * @return {void}\n\t\t */\n\t\tonOutsideClick(event) {\n\t\t\tconst target = event.target\n\t\t\tif (this.effectiveStyle === 'megamenu') {\n\t\t\t\tif (this.megaOpenIndex === null) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst button = this.topRefs[this.megaOpenIndex]\n\t\t\t\tconst panel = this.$refs.megaPanel\n\t\t\t\tconst insideButton = Boolean(button && button.contains(target))\n\t\t\t\tconst insidePanel = Boolean(panel && panel.contains(target))\n\t\t\t\tif (!insideButton && !insidePanel) {\n\t\t\t\t\tthis.closeAll()\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Dropdown style — the button and its dropdown/flyout panel share\n\t\t\t// the same top-level `<li>`, so one containment check covers both.\n\t\t\tif (this.dropOpenIndex === null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst barItem = this.barItemRefs[this.dropOpenIndex]\n\t\t\tif (barItem && !barItem.contains(target)) {\n\t\t\t\tthis.closeAll()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an item renders as a link (it has a URL).\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true when the item carries a non-empty URL.\n\t\t */\n\t\tisLink(item) {\n\t\t\treturn Boolean(item) && typeof item.url === 'string' && item.url !== ''\n\t\t},\n\n\t\t/**\n\t\t * Whether a top-level item renders as a link. One with children\n\t\t * stays a button, because its click toggles the panel.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {boolean} true for a top-level leaf with a URL.\n\t\t */\n\t\tisTopLink(item) {\n\t\t\treturn this.isLink(item) && !this.hasChildren(item)\n\t\t},\n\n\t\t/**\n\t\t * Anchor attributes for an item's URL: external URLs open in a new tab.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {object} the `href` (plus `target` / `rel` for external URLs).\n\t\t */\n\t\tlinkAttrs(item) {\n\t\t\treturn menuLinkAttrs(item.url, this.$router)\n\t\t},\n\n\t\t/**\n\t\t * Attributes for a panel item: link attributes, or `type=\"button\"`.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {object} the element attributes.\n\t\t */\n\t\titemAttrs(item) {\n\t\t\treturn this.isLink(item) ? this.linkAttrs(item) : { type: 'button' }\n\t\t},\n\n\t\t/**\n\t\t * Click on an item link: close the menus, then let the browser open\n\t\t * an external URL or a modified click, and route a plain click on an\n\t\t * internal one. A no-op for an item without a URL.\n\t\t *\n\t\t * @param {MouseEvent} event the click event.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonLinkClick(event, item) {\n\t\t\tif (!this.isLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.closeAll()\n\t\t\tif (isRouterPath(item.url)) {\n\t\t\t\tfollowLinkClick(event, item.url, this.$router)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Enter on a dropdown top item: a link follows natively (Enter\n\t\t * clicks it); a button toggles or navigates.\n\t\t *\n\t\t * @param {KeyboardEvent} event the key event.\n\t\t * @param {number} idx the item index.\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonDropdownTopEnter(event, idx, item) {\n\t\t\tif (this.isTopLink(item)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.onDropdownTopClick(idx, item)\n\t\t},\n\n\t\t/**\n\t\t * Navigation request from a tree node: a link click goes through the\n\t\t * link handler, a request without an event navigates directly.\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @param {MouseEvent} [event] the click event, when the node's link was clicked.\n\t\t * @return {void}\n\t\t */\n\t\tonTreeNavigate(item, event) {\n\t\t\tif (event) {\n\t\t\t\tthis.onLinkClick(event, item)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onNavigate(item)\n\t\t},\n\n\t\t/**\n\t\t * Navigate to an item's URL (router push for internal, new tab for\n\t\t * external), closing any open menus first. Used where no link click\n\t\t * is involved (keyboard activation of a menu item).\n\t\t *\n\t\t * @param {object} item the menu item.\n\t\t * @return {void}\n\t\t */\n\t\tonNavigate(item) {\n\t\t\tif (!item || typeof item.url !== 'string' || item.url === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.closeAll()\n\t\t\tconst url = item.url\n\t\t\tconst { href } = menuLinkAttrs(url, this.$router)\n\t\t\tif (this.isExternal(url)) {\n\t\t\t\tif (typeof window !== 'undefined' && typeof window.open === 'function') {\n\t\t\t\t\twindow.open(href, '_blank', 'noopener,noreferrer')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (isRouterPath(url) && this.$router && typeof this.$router.push === 'function') {\n\t\t\t\ttry {\n\t\t\t\t\tthis.$router.push(url)\n\t\t\t\t\treturn\n\t\t\t\t} catch {\n\t\t\t\t\t// Fall through to location assignment.\n\t\t\t\t}\n\t\t\t}\n\t\t\t// href is safeHref-checked, so a javascript: URL never reaches location.\n\t\t\tif (href !== '#' && typeof window !== 'undefined' && window.location) {\n\t\t\t\twindow.location.href = href\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether a URL is external (http/https).\n\t\t *\n\t\t * @param {string} url the URL.\n\t\t * @return {boolean} true for absolute http(s) URLs.\n\t\t */\n\t\tisExternal(url) {\n\t\t\treturn typeof url === 'string' && /^https?:\\/\\//i.test(url)\n\t\t},\n\n\t\t/**\n\t\t * Empty-state click — request edit when in edit mode.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonEmptyStateClick() {\n\t\t\tif (this.isInEditMode) {\n\t\t\t\tthis.$emit('edit-request')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Test helper — whether a URL is active for the current location.\n\t\t *\n\t\t * @param {string} itemUrl the URL to test.\n\t\t * @return {boolean} true when active.\n\t\t */\n\t\t__isActive(itemUrl) {\n\t\t\treturn isActiveItem(itemUrl, this.currentLocation)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-menu-widget {\n\twidth: 100%;\n\theight: 100%;\n\tpadding: 4px;\n\tcolor: var(--color-main-text);\n\tfont-size: 14px;\n}\n\n.cn-menu-widget__empty {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 16px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-style: italic;\n\tborder: 1px dashed var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-menu-widget__empty--clickable {\n\tcursor: pointer;\n}\n\n.cn-menu-widget__empty-icon {\n\tfont-size: 18px;\n}\n\n.cn-menu-widget__bar {\n\tdisplay: flex;\n\tflex-direction: row;\n\tgap: 4px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-menu-widget__bar--vertical {\n\tflex-direction: column;\n}\n\n.cn-menu-widget__bar-item {\n\tposition: relative;\n\tlist-style: none;\n}\n\n.cn-menu-widget__bar-button,\n.cn-menu-widget__dropdown-item,\n.cn-menu-widget__mega-group-title,\n.cn-menu-widget__mega-leaf {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 6px 10px;\n\tbackground: transparent;\n\tborder: none;\n\tborder-radius: var(--border-radius);\n\tcolor: inherit;\n\tfont-size: 14px;\n\tcursor: pointer;\n\ttext-align: left;\n\ttext-decoration: none;\n\tbox-sizing: border-box;\n\twidth: 100%;\n}\n\n.cn-menu-widget__mega-group-title {\n\tfont-weight: 600;\n}\n\n.cn-menu-widget__bar-button:hover,\n.cn-menu-widget__dropdown-item:hover,\n.cn-menu-widget__mega-group-title:hover,\n.cn-menu-widget__mega-leaf:hover {\n\tbackground: var(--color-background-hover);\n}\n\n.cn-menu-widget__bar-button:focus-visible,\n.cn-menu-widget__dropdown-item:focus-visible {\n\toutline: 2px solid var(--color-primary);\n\toutline-offset: 2px;\n}\n\n.cn-menu-widget__icon {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 20px;\n\theight: 20px;\n}\n\n.cn-menu-widget__icon--hidden {\n\tvisibility: hidden;\n}\n\n.cn-menu-widget__caret {\n\tmargin-left: auto;\n\tfont-size: 10px;\n}\n\n.cn-menu-widget__dropdown {\n\tposition: absolute;\n\ttop: 100%;\n\tleft: 0;\n\tz-index: 50;\n\tmargin: 4px 0 0;\n\tpadding: 4px;\n\tmin-width: 180px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n\tlist-style: none;\n}\n\n.cn-menu-widget__dropdown-item-wrap {\n\tposition: relative;\n}\n\n.cn-menu-widget__flyout {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 100%;\n\tmargin-left: 2px;\n\tpadding: 4px;\n\tmin-width: 160px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n\tlist-style: none;\n}\n\n.cn-menu-widget__mega-panel {\n\tmargin-top: 4px;\n\tpadding: 12px;\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n\tgap: 12px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-menu-widget__mega-leaves {\n\tmargin: 4px 0 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-menu-widget__tree {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n/* Active-item highlight variants. */\n.cn-menu-widget--highlight-underline .cn-menu-widget__item--active {\n\tborder-bottom: 3px solid var(--color-primary);\n}\n\n.cn-menu-widget--highlight-underline .cn-menu-widget__item--in-path {\n\tborder-bottom: 2px solid var(--color-primary);\n\topacity: 0.85;\n}\n\n.cn-menu-widget--highlight-background .cn-menu-widget__item--active,\n.cn-menu-widget--highlight-background .cn-menu-widget__item--in-path {\n\tbackground: var(--color-primary-element-light, rgba(0, 112, 192, 0.1));\n\tcolor: var(--color-primary);\n}\n\n.cn-menu-widget--highlight-left-bar .cn-menu-widget__item--active {\n\tborder-left: 5px solid var(--color-primary);\n\tpadding-left: 6px;\n}\n\n.cn-menu-widget--highlight-left-bar .cn-menu-widget__item--in-path {\n\tborder-left: 4px solid var(--color-primary);\n\tpadding-left: 7px;\n\topacity: 0.85;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_openBlock","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_mergeProps","_toDisplayString","_withKeys","_withModifiers"],"mappings":"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"}
@@ -2,7 +2,7 @@ import script from './CnQuickFilterBar.vue2.js';
2
2
  import { render } from './CnQuickFilterBar.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-ed2d2a38";
5
+ script.__scopeId = "data-v-2d5e5fa6";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };