From 47ffd20edd2c7948b02210de64fdb4eceadd9e9b Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Tue, 9 Sep 2025 22:26:57 +0200 Subject: [PATCH] feat(sideNav): make plugins work with new nav --- .../magic-nav-config.service.ts | 20 +++++++++++++++++++ .../magic-side-nav.component.html | 14 +++++++++++++ .../magic-side-nav.component.ts | 4 ++++ .../magic-side-nav/magic-side-nav.model.ts | 12 ++++++++++- .../nav-item/nav-item.component.html | 2 ++ .../nav-item/nav-item.component.ts | 3 +++ 6 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/app/core-ui/magic-side-nav/magic-nav-config.service.ts b/src/app/core-ui/magic-side-nav/magic-nav-config.service.ts index c164e333b..7ef361df7 100644 --- a/src/app/core-ui/magic-side-nav/magic-nav-config.service.ts +++ b/src/app/core-ui/magic-side-nav/magic-nav-config.service.ts @@ -23,6 +23,7 @@ import { import { toggleHideFromMenu } from '../../features/project/store/project.actions'; import { NavConfig, NavItem, NavWorkContextItem } from './magic-side-nav.model'; import { TODAY_TAG } from '../../features/tag/tag.const'; +import { PluginBridgeService } from '../../plugins/plugin-bridge.service'; @Injectable({ providedIn: 'root', @@ -34,6 +35,7 @@ export class MagicNavConfigService { private readonly _shepherdService = inject(ShepherdService); private readonly _matDialog = inject(MatDialog); private readonly _store = inject(Store); + private readonly _pluginBridge = inject(PluginBridgeService); // Simple state signals private readonly _isProjectsExpanded = signal( @@ -66,6 +68,7 @@ export class MagicNavConfigService { this._workContextService.activeWorkContextId$, { initialValue: null }, ); + private readonly _pluginMenuEntries = this._pluginBridge.menuEntries; // Main navigation configuration readonly navConfig = computed(() => ({ @@ -100,6 +103,9 @@ export class MagicNavConfigService { route: '/boards', }, + // Plugin entries + ...this._buildPluginItems(), + // Separator { type: 'separator', id: 'sep-2' }, @@ -228,6 +234,7 @@ export class MagicNavConfigService { label: T.MH.SETTINGS, icon: 'settings', route: '/config', + tourClass: 'tour-settingsMenuBtn', }, ], fullModeByDefault: true, @@ -337,6 +344,19 @@ export class MagicNavConfigService { })); } + private _buildPluginItems(): NavItem[] { + const pluginEntries = this._pluginMenuEntries(); + + return pluginEntries.map((entry) => ({ + type: 'plugin', + id: `plugin-${entry.pluginId}-${entry.label}`, + label: entry.label, + icon: entry.icon || 'extension', + pluginId: entry.pluginId, + action: entry.onClick, + })); + } + // Public computed signals for expansion state (for component to check) readonly isProjectsExpanded = computed(() => this._isProjectsExpanded()); readonly isTagsExpanded = computed(() => this._isTagsExpanded()); diff --git a/src/app/core-ui/magic-side-nav/magic-side-nav.component.html b/src/app/core-ui/magic-side-nav/magic-side-nav.component.html index d85b2957e..5a41627ec 100644 --- a/src/app/core-ui/magic-side-nav/magic-side-nav.component.html +++ b/src/app/core-ui/magic-side-nav/magic-side-nav.component.html @@ -123,6 +123,7 @@ [svgIcon]="item.svgIcon" [label]="item.label" [showLabels]="showText()" + [tourClass]="item.tourClass" (clicked)="onItemClick(item)" > } @@ -134,6 +135,7 @@ [svgIcon]="item.svgIcon" [label]="item.label" [showLabels]="showText()" + [tourClass]="item.tourClass" (clicked)="onItemClick(item)" > } @@ -144,6 +146,18 @@ [svgIcon]="item.svgIcon" [label]="item.label" [showLabels]="showText()" + [tourClass]="item.tourClass" + (clicked)="onItemClick(item)" + > + } + @case ('plugin') { + } diff --git a/src/app/core-ui/magic-side-nav/magic-side-nav.component.ts b/src/app/core-ui/magic-side-nav/magic-side-nav.component.ts index 3db822549..b5ebab450 100644 --- a/src/app/core-ui/magic-side-nav/magic-side-nav.component.ts +++ b/src/app/core-ui/magic-side-nav/magic-side-nav.component.ts @@ -216,6 +216,10 @@ export class MagicSideNavComponent implements OnInit, OnDestroy { item.action?.(); } + if (item.type === 'plugin') { + item.action?.(); + } + // Handle via service for actions/hrefs this._sideNavConfigService.onNavItemClick(item); diff --git a/src/app/core-ui/magic-side-nav/magic-side-nav.model.ts b/src/app/core-ui/magic-side-nav/magic-side-nav.model.ts index 4b9f5c654..b2f3d9d3d 100644 --- a/src/app/core-ui/magic-side-nav/magic-side-nav.model.ts +++ b/src/app/core-ui/magic-side-nav/magic-side-nav.model.ts @@ -10,13 +10,15 @@ export type NavItem = | NavHrefItem | NavActionItem | NavGroupItem - | NavMenuItem; + | NavMenuItem + | NavPluginItem; export interface NavBaseItem { id: string; label?: string; icon?: string; svgIcon?: string; + tourClass?: string; } export interface NavSeparatorItem extends NavBaseItem { @@ -80,6 +82,14 @@ export interface NavMenuItem extends NavBaseItem { children: NavItem[]; } +export interface NavPluginItem extends NavBaseItem { + type: 'plugin'; + label: string; + icon: string; + pluginId: string; + action: () => void; +} + export interface NavAdditionalButton { id: string; icon: string; diff --git a/src/app/core-ui/magic-side-nav/nav-item/nav-item.component.html b/src/app/core-ui/magic-side-nav/nav-item/nav-item.component.html index b5be04eff..a590b8034 100644 --- a/src/app/core-ui/magic-side-nav/nav-item/nav-item.component.html +++ b/src/app/core-ui/magic-side-nav/nav-item/nav-item.component.html @@ -59,6 +59,7 @@ mat-menu-item class="nav-link" [class.expanded]="expanded()" + [class]="tourClass()" (click)="clicked.emit()" > @if (svgIcon()) { @@ -131,6 +132,7 @@ [attr.aria-expanded]="container() === 'group' ? expanded() : null" [attr.aria-controls]="container() === 'group' ? ariaControls() : null" [matMenuTriggerFor]="menuTriggerFor()" + [class]="tourClass()" (click)="clicked.emit()" > @if (svgIcon()) { diff --git a/src/app/core-ui/magic-side-nav/nav-item/nav-item.component.ts b/src/app/core-ui/magic-side-nav/nav-item/nav-item.component.ts index 30c7f32e0..f613f421e 100644 --- a/src/app/core-ui/magic-side-nav/nav-item/nav-item.component.ts +++ b/src/app/core-ui/magic-side-nav/nav-item/nav-item.component.ts @@ -87,6 +87,9 @@ export class NavItemComponent { // Optional: menu trigger for dropdown menuTriggerFor = input(null); + // Tour class for Shepherd.js guide + tourClass = input(null); + // Events clicked = output();