@ardimedia/angular-portal-azure 0.3.18 → 0.3.25

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ardimedia/angular-portal-azure",
3
- "version": "0.3.18",
3
+ "version": "0.3.25",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.1.0",
6
6
  "@angular/core": "^21.1.0"
@@ -47,6 +47,8 @@ declare function statusBarInfo(text: string): StatusBarState;
47
47
  declare function statusBarError(text: string): StatusBarState;
48
48
  declare function statusBarSuccess(text: string): StatusBarState;
49
49
 
50
+ /** @internal */
51
+ declare function nextBladeUid(): number;
50
52
  /**
51
53
  * Base blade definition used by BladeService to manage the blade stack.
52
54
  * Ported from Blade class in v0.2.346 — flattened from the UserControlBase → Blade hierarchy.
@@ -55,6 +57,8 @@ declare function statusBarSuccess(text: string): StatusBarState;
55
57
  * In v0.3.0 commands are a BladeCommand[] array and blade state is managed by the service.
56
58
  */
57
59
  interface BladeDefinition {
60
+ /** Auto-incrementing unique ID for @for track identity */
61
+ readonly uid: number;
58
62
  /** Unique path identifying this blade (lowercased) */
59
63
  path: string;
60
64
  /** Blade header title */
@@ -69,10 +73,12 @@ interface BladeDefinition {
69
73
  commands: BladeCommand[];
70
74
  /** Status bar state */
71
75
  statusBar: StatusBarState;
76
+ /** URL-persisted parameters for this blade (e.g., { id: '1' }) */
77
+ params: Record<string, string>;
72
78
  }
73
79
  /** Creates a blade definition with sensible defaults.
74
- * statusBar uses a getter/setter backed by a signal for zoneless change detection. */
75
- declare function createBlade(path: string, title: string, width?: number): BladeDefinition;
80
+ * statusBar and title use getter/setter pairs backed by signals for zoneless change detection. */
81
+ declare function createBlade(path: string, title: string, width?: number, params?: Record<string, string>, uid?: number): BladeDefinition;
76
82
  /**
77
83
  * Event args for blade navigation.
78
84
  * Ported from IAddBladeEventArgs in v0.2.346.
@@ -196,9 +202,9 @@ interface BladeDataDefinition<T> extends BladeDefinition {
196
202
  lifecycle: BladeDataLifecycle<T>;
197
203
  }
198
204
  /** Creates a data blade definition with sensible defaults.
199
- * statusBar, item, items use getter/setter pairs backed by signals for zoneless change detection.
205
+ * statusBar, title, item, items use getter/setter pairs backed by signals for zoneless change detection.
200
206
  * Note: cannot use ...createBlade() spread here — spread copies getter values, not getter/setter pairs. */
201
- declare function createDataBlade<T>(path: string, title: string, width?: number): BladeDataDefinition<T>;
207
+ declare function createDataBlade<T>(path: string, title: string, width?: number, params?: Record<string, string>, uid?: number): BladeDataDefinition<T>;
202
208
  /**
203
209
  * Execute a load-item operation with lifecycle hooks and status bar updates.
204
210
  * Replaces the BladeData.loadItem() template method from v0.2.346.
@@ -282,6 +288,8 @@ interface TileDefinition {
282
288
  size: TileSize;
283
289
  /** Optional CSS class for tile icon/content */
284
290
  cssClass?: string;
291
+ /** Whether the tile is disabled (grayed out, not clickable) */
292
+ disabled?: boolean;
285
293
  }
286
294
  declare function createTile(title: string, bladePath: string, size?: TileSize): TileDefinition;
287
295
 
@@ -302,6 +310,15 @@ declare function getUserDisplayName(account: UserAccount): string;
302
310
  * Notification panel definition.
303
311
  * Ported from AreaNotification in v0.2.346.
304
312
  */
313
+ /** Lifecycle hooks for the notification panel */
314
+ interface NotificationLifecycle {
315
+ /** Called before hide; return false to prevent hiding */
316
+ onHide?: () => boolean;
317
+ /** Called before showing */
318
+ onShow?: () => void;
319
+ /** Called after the panel is shown */
320
+ onShowed?: () => void;
321
+ }
305
322
  interface NotificationDefinition {
306
323
  /** Path/identifier for the content shown in the notification area */
307
324
  path: string;
@@ -311,15 +328,8 @@ interface NotificationDefinition {
311
328
  isVisible: boolean;
312
329
  /** Background color (default '#32383f') */
313
330
  backgroundColor: string;
314
- }
315
- /** Lifecycle hooks for the notification panel */
316
- interface NotificationLifecycle {
317
- /** Called before hide; return false to prevent hiding */
318
- onHide?: () => boolean;
319
- /** Called before showing */
320
- onShow?: () => void;
321
- /** Called after the panel is shown */
322
- onShowed?: () => void;
331
+ /** Optional lifecycle hooks invoked during show/hide transitions */
332
+ lifecycle?: NotificationLifecycle;
323
333
  }
324
334
  declare function createNotificationPanel(): NotificationDefinition;
325
335
 
@@ -474,8 +484,8 @@ declare class PortalService {
474
484
  /** Clear the blade parameter back to defaults */
475
485
  clearParameter(): void;
476
486
  /** Show the notification panel */
477
- showNotification(path: string, width?: number): void;
478
- /** Hide the notification panel */
487
+ showNotification(path: string, width?: number, lifecycle?: NotificationLifecycle): void;
488
+ /** Hide the notification panel. Aborted if onHide() returns false. */
479
489
  hideNotification(): void;
480
490
  /** Toggle avatar menu open/close. Closes settings if opening. */
481
491
  toggleAvatarMenu(): void;
@@ -493,9 +503,13 @@ declare class PortalService {
493
503
  *
494
504
  * Manages the blade stack: adding, removing, cascade-closing,
495
505
  * and panorama visibility toggling.
506
+ *
507
+ * When a BladeRegistry is available, metadata (title, width) from the
508
+ * registry is used as defaults — explicit arguments take precedence.
496
509
  */
497
510
  declare class BladeService {
498
511
  private readonly portal;
512
+ private readonly registry;
499
513
  /**
500
514
  * Set the first blade (e.g., when opening a top-level item from a tile).
501
515
  * Clears all existing blades, hides panorama, and adds the new blade.
@@ -509,7 +523,7 @@ declare class BladeService {
509
523
  *
510
524
  * Ported from AreaBlades.addBlade() in v0.2.346.
511
525
  */
512
- addBlade(path: string, senderPath?: string, title?: string, width?: number): BladeDefinition | undefined;
526
+ addBlade(path: string, senderPath?: string, title?: string, width?: number, params?: Record<string, string>): BladeDefinition | undefined;
513
527
  /**
514
528
  * Open a blade from a navigation event (e.g., tile click, nav item click).
515
529
  * Wraps addBlade with AddBladeEventArgs for compatibility.
@@ -557,10 +571,23 @@ declare class BladeService {
557
571
  * Check if a blade with the given path is currently open.
558
572
  */
559
573
  isBladeOpen(path: string): boolean;
574
+ /**
575
+ * Get URL-persisted parameters for a blade by path.
576
+ * Returns empty object if blade not found or has no params.
577
+ */
578
+ getBladeParams(path: string): Record<string, string>;
560
579
  static ɵfac: i0.ɵɵFactoryDeclaration<BladeService, never>;
561
580
  static ɵprov: i0.ɵɵInjectableDeclaration<BladeService>;
562
581
  }
563
582
 
583
+ /** Entry in the blade registry: component + optional metadata for URL restoration */
584
+ interface BladeRegistryEntry {
585
+ component: Type<unknown>;
586
+ title?: string;
587
+ width?: number;
588
+ /** Parameter names this blade expects from the URL (e.g., ['id']) */
589
+ params?: string[];
590
+ }
564
591
  /**
565
592
  * Registry for mapping blade paths to Angular components.
566
593
  *
@@ -572,7 +599,7 @@ declare class BladeService {
572
599
  * ```typescript
573
600
  * const registry = inject(BladeRegistry);
574
601
  * registry.register('customers', CustomerNavComponent);
575
- * registry.register('customers/list', CustomerListComponent);
602
+ * registry.register('customers/list', CustomerListComponent, { title: 'All Customers', width: 585 });
576
603
  * ```
577
604
  *
578
605
  * Or register multiple at once:
@@ -585,18 +612,86 @@ declare class BladeService {
585
612
  */
586
613
  declare class BladeRegistry {
587
614
  private readonly registry;
588
- /** Register a component for a blade path */
589
- register(path: string, component: Type<unknown>): void;
615
+ /** Register a component for a blade path with optional metadata (title, width, params) */
616
+ register(path: string, component: Type<unknown>, metadata?: {
617
+ title?: string;
618
+ width?: number;
619
+ params?: string[];
620
+ }): void;
590
621
  /** Register multiple blade path → component mappings */
591
622
  registerAll(mappings: Record<string, Type<unknown>>): void;
592
623
  /** Get the component registered for a path, if any */
593
624
  get(path: string): Type<unknown> | undefined;
625
+ /** Get the full registry entry (component + metadata) for a path */
626
+ getEntry(path: string): BladeRegistryEntry | undefined;
594
627
  /** Check if a component is registered for a path */
595
628
  has(path: string): boolean;
596
629
  static ɵfac: i0.ɵɵFactoryDeclaration<BladeRegistry, never>;
597
630
  static ɵprov: i0.ɵɵInjectableDeclaration<BladeRegistry>;
598
631
  }
599
632
 
633
+ /**
634
+ * Optional service that syncs the blade stack with the browser URL.
635
+ *
636
+ * When enabled, blade paths are stored as URL path segments with matrix params:
637
+ * `/crm/customers/list/detail;id=1`
638
+ *
639
+ * This makes blade states bookmarkable, shareable, and back-button navigable.
640
+ *
641
+ * Opt-in: add `provideBladeRouter()` to your app providers alongside `provideRouter()`.
642
+ * Without it, blade navigation remains purely in-memory (no URL changes).
643
+ */
644
+ declare class BladeRouterService {
645
+ private readonly router;
646
+ private readonly portal;
647
+ private readonly registry;
648
+ private readonly destroyRef;
649
+ private _syncingFromUrl;
650
+ private _initialRestoreDone;
651
+ constructor();
652
+ /**
653
+ * Encode the blade stack into URL path segments.
654
+ * Each blade contributes its "short name" (suffix after parent prefix).
655
+ * Blades with params get Angular matrix params appended.
656
+ *
657
+ * Example: [customers, customers/list, customers/detail{id:1}]
658
+ * → "customers/list/detail;id=1"
659
+ */
660
+ private encodeBladesToPath;
661
+ /**
662
+ * Decode URL path segments into blade definitions.
663
+ * Resolves short segment names to full blade paths using the registry.
664
+ *
665
+ * Example: "customers/list/detail;id=1" with route prefix "crm"
666
+ * → [customers, customers/list, customers/detail] with detail.params={id:'1'}
667
+ */
668
+ private decodeBladesFromPath;
669
+ /**
670
+ * Parse a URL segment into its name and matrix parameters.
671
+ * "detail;id=1;mode=edit" → { name: "detail", params: { id: "1", mode: "edit" } }
672
+ */
673
+ private parseSegment;
674
+ /**
675
+ * Resolve a short segment name to a full blade path using the registry.
676
+ * Tries parent/segment first, then walks up ancestors.
677
+ */
678
+ private resolveSegment;
679
+ /** Restore blade stack from a path-based URL */
680
+ private restoreFromPath;
681
+ /** Check if params match between two blade arrays */
682
+ private paramsMatch;
683
+ /** Get the route prefix from the current URL (e.g., 'crm' from '/crm/...') */
684
+ private getRoutePrefix;
685
+ /** Normalize URL for comparison (strip trailing slashes) */
686
+ private normalizeUrl;
687
+ /** Extract legacy `blades` query parameter from URL */
688
+ private extractLegacyBladesParam;
689
+ /** Handle legacy ?blades= URL by redirecting to new path format */
690
+ private handleLegacyUrl;
691
+ static ɵfac: i0.ɵɵFactoryDeclaration<BladeRouterService, never>;
692
+ static ɵprov: i0.ɵɵInjectableDeclaration<BladeRouterService>;
693
+ }
694
+
600
695
  /**
601
696
  * Provide the angular-portal-azure library configuration.
602
697
  *
@@ -617,6 +712,27 @@ declare class BladeRegistry {
617
712
  */
618
713
  declare function providePortalAzure(config: PortalConfig): EnvironmentProviders;
619
714
 
715
+ /**
716
+ * Enables opt-in URL synchronization for the blade stack.
717
+ *
718
+ * Add alongside `provideRouter()` and `providePortalAzure()`:
719
+ * ```typescript
720
+ * export const appConfig: ApplicationConfig = {
721
+ * providers: [
722
+ * provideRouter(routes),
723
+ * providePortalAzure({ title: 'My Portal', ... }),
724
+ * provideBladeRouter(),
725
+ * ],
726
+ * };
727
+ * ```
728
+ *
729
+ * When enabled, blade paths sync to the URL as a query parameter:
730
+ * `?blades=customers,customers/list,customers/1`
731
+ *
732
+ * Without this provider, blade navigation remains purely in-memory.
733
+ */
734
+ declare function provideBladeRouter(): EnvironmentProviders;
735
+
620
736
  /**
621
737
  * Individual dashboard tile.
622
738
  * Ported from the tile section in home.html (v0.2.346).
@@ -641,18 +757,27 @@ declare class TileComponent {
641
757
  * Displays tiles on the startboard. When a tile is clicked, it opens
642
758
  * the first blade via BladeService.setFirstBlade().
643
759
  *
760
+ * When `autoNavigate` is true (default), clicking a tile automatically
761
+ * opens the first blade. Set it to false to handle tile clicks manually
762
+ * via the `(tileClick)` output.
763
+ *
644
764
  * Usage:
645
765
  * ```html
646
766
  * <apa-panorama />
767
+ * <apa-panorama [autoNavigate]="false" (tileClick)="onTileClick($event)" />
647
768
  * ```
648
769
  */
649
770
  declare class PanoramaComponent {
650
771
  protected readonly portal: PortalService;
651
772
  private readonly bladeService;
773
+ /** When false, tile clicks only emit tileClick without opening a blade. */
774
+ readonly autoNavigate: i0.InputSignal<boolean>;
775
+ /** Emitted when a tile is clicked (always, regardless of autoNavigate). */
776
+ readonly tileClick: i0.OutputEmitterRef<PositionedTile>;
652
777
  protected panorama: i0.WritableSignal<_ardimedia_angular_portal_azure.PanoramaDefinition>;
653
778
  onTileClick(tile: PositionedTile): void;
654
779
  static ɵfac: i0.ɵɵFactoryDeclaration<PanoramaComponent, never>;
655
- static ɵcmp: i0.ɵɵComponentDeclaration<PanoramaComponent, "apa-panorama", never, {}, {}, never, never, true, never>;
780
+ static ɵcmp: i0.ɵɵComponentDeclaration<PanoramaComponent, "apa-panorama", never, { "autoNavigate": { "alias": "autoNavigate"; "required": false; "isSignal": true; }; }, { "tileClick": "tileClick"; }, never, never, true, never>;
656
781
  }
657
782
 
658
783
  /**
@@ -812,6 +937,8 @@ declare class BladeGridComponent {
812
937
  readonly senderPath: i0.InputSignal<string>;
813
938
  readonly displayField: i0.InputSignal<string>;
814
939
  readonly bladePathField: i0.InputSignal<string>;
940
+ readonly idField: i0.InputSignal<string>;
941
+ readonly iconClass: i0.InputSignal<string>;
815
942
  readonly searchable: i0.InputSignal<boolean>;
816
943
  readonly itemClick: i0.OutputEmitterRef<any>;
817
944
  searchText: string;
@@ -822,7 +949,7 @@ declare class BladeGridComponent {
822
949
  onSearchInput(event: Event): void;
823
950
  onRowClick(item: any): void;
824
951
  static ɵfac: i0.ɵɵFactoryDeclaration<BladeGridComponent, never>;
825
- static ɵcmp: i0.ɵɵComponentDeclaration<BladeGridComponent, "apa-blade-grid", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "senderPath": { "alias": "senderPath"; "required": false; "isSignal": true; }; "displayField": { "alias": "displayField"; "required": false; "isSignal": true; }; "bladePathField": { "alias": "bladePathField"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; }, { "itemClick": "itemClick"; }, never, never, true, never>;
952
+ static ɵcmp: i0.ɵɵComponentDeclaration<BladeGridComponent, "apa-blade-grid", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "senderPath": { "alias": "senderPath"; "required": false; "isSignal": true; }; "displayField": { "alias": "displayField"; "required": false; "isSignal": true; }; "bladePathField": { "alias": "bladePathField"; "required": false; "isSignal": true; }; "idField": { "alias": "idField"; "required": false; "isSignal": true; }; "iconClass": { "alias": "iconClass"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; }, { "itemClick": "itemClick"; }, never, never, true, never>;
826
953
  }
827
954
 
828
955
  /**
@@ -876,6 +1003,8 @@ declare function createDetailCommands(handlers: {
876
1003
  */
877
1004
  declare class NotificationPanelComponent {
878
1005
  protected readonly portal: PortalService;
1006
+ private wasVisible;
1007
+ constructor();
879
1008
  onClose(): void;
880
1009
  static ɵfac: i0.ɵɵFactoryDeclaration<NotificationPanelComponent, never>;
881
1010
  static ɵcmp: i0.ɵɵComponentDeclaration<NotificationPanelComponent, "apa-notification-panel", never, {}, {}, never, ["*"], true, never>;
@@ -928,5 +1057,5 @@ declare class SidebarComponent {
928
1057
  static ɵcmp: i0.ɵɵComponentDeclaration<SidebarComponent, "apa-sidebar", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "collapsedWidth": { "alias": "collapsedWidth"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
929
1058
  }
930
1059
 
931
- export { AvatarMenuComponent, BladeComponent, BladeDetailComponent, BladeGridComponent, BladeHostComponent, BladeNavComponent, BladeRegistry, BladeService, CommandBarComponent, DEFAULT_LABELS, LABELS_DE_CH, LABELS_DE_DE, LABELS_EN, LABELS_ES, LABELS_FR, LABELS_IT, LANGUAGE_PRESETS, NotificationPanelComponent, PanoramaComponent, PortalLayoutComponent, PortalService, SidebarComponent, TILE_DIMENSIONS, TileComponent, TileSize, clearStatusBar, createAvatarMenu, createBlade, createCommand, createDataBlade, createDetailCommands, createNavItem, createNotificationPanel, createPanorama, createTile, executeDeleteItem, executeLoadItem, executeLoadItems, executeSaveItem, filterItems, getUserDisplayName, layoutTiles, providePortalAzure, registerLanguagePreset, statusBarError, statusBarInfo, statusBarSuccess };
932
- export type { AddBladeEventArgs, ApiException, AvatarMenuDefinition, AvatarMenuItem, BladeCommand, BladeDataDefinition, BladeDataLifecycle, BladeDefinition, BladeNavItem, BladeParameter, LanguagePreset, NotificationDefinition, NotificationLifecycle, PanoramaDefinition, PortalConfig, PortalLabels, PositionedTile, StandardCommandKey, StatusBarState, StatusBarStyle, TileDefinition, UserAccount };
1060
+ export { AvatarMenuComponent, BladeComponent, BladeDetailComponent, BladeGridComponent, BladeHostComponent, BladeNavComponent, BladeRegistry, BladeRouterService, BladeService, CommandBarComponent, DEFAULT_LABELS, LABELS_DE_CH, LABELS_DE_DE, LABELS_EN, LABELS_ES, LABELS_FR, LABELS_IT, LANGUAGE_PRESETS, NotificationPanelComponent, PanoramaComponent, PortalLayoutComponent, PortalService, SidebarComponent, TILE_DIMENSIONS, TileComponent, TileSize, clearStatusBar, createAvatarMenu, createBlade, createCommand, createDataBlade, createDetailCommands, createNavItem, createNotificationPanel, createPanorama, createTile, executeDeleteItem, executeLoadItem, executeLoadItems, executeSaveItem, filterItems, getUserDisplayName, layoutTiles, nextBladeUid, provideBladeRouter, providePortalAzure, registerLanguagePreset, statusBarError, statusBarInfo, statusBarSuccess };
1061
+ export type { AddBladeEventArgs, ApiException, AvatarMenuDefinition, AvatarMenuItem, BladeCommand, BladeDataDefinition, BladeDataLifecycle, BladeDefinition, BladeNavItem, BladeParameter, BladeRegistryEntry, LanguagePreset, NotificationDefinition, NotificationLifecycle, PanoramaDefinition, PortalConfig, PortalLabels, PositionedTile, StandardCommandKey, StatusBarState, StatusBarStyle, TileDefinition, UserAccount };