@mohamedatia/fly-design-system 2.6.4 → 2.7.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.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, signal, computed, Injectable, inject, ErrorHandler, PLATFORM_ID, Pipe, DOCUMENT, ElementRef, input, output, HostListener, ViewChild, ChangeDetectionStrategy, Component, EventEmitter, DestroyRef, Output, Input, forwardRef, Injector, viewChild, effect, afterNextRender, ViewEncapsulation, model, Directive } from '@angular/core';
2
+ import { InjectionToken, signal, computed, Injectable, inject, ErrorHandler, PLATFORM_ID, ChangeDetectionStrategy, Component, Pipe, DOCUMENT, ElementRef, input, output, HostListener, ViewChild, EventEmitter, DestroyRef, Output, Input, forwardRef, Injector, viewChild, effect, afterNextRender, ViewEncapsulation, model, Directive } from '@angular/core';
3
3
  import * as i1$1 from '@angular/common';
4
- import { isPlatformBrowser, CommonModule, DOCUMENT as DOCUMENT$1 } from '@angular/common';
4
+ import { isPlatformBrowser, NgComponentOutlet, CommonModule, DOCUMENT as DOCUMENT$1 } from '@angular/common';
5
5
  import { Router, NavigationEnd } from '@angular/router';
6
6
  import { of, ReplaySubject, Subject } from 'rxjs';
7
7
  import * as i1 from '@angular/forms';
@@ -375,6 +375,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
375
375
  type: Injectable
376
376
  }] });
377
377
 
378
+ /**
379
+ * Optional injection token a remote provides at its root to declare which routes
380
+ * `<fly-remote-router-outlet>` should resolve. Provided WITHIN the remote (not
381
+ * the shell), e.g.:
382
+ *
383
+ * ```ts
384
+ * @Component({
385
+ * providers: [
386
+ * FlyRemoteRouter,
387
+ * { provide: FLY_REMOTE_ROUTES, useValue: CIRCLES_ROUTES },
388
+ * ],
389
+ * })
390
+ * export class CirclesComponent { }
391
+ * ```
392
+ *
393
+ * If no routes are provided, `FlyRemoteRouter.matchedRoute()` is always `null`
394
+ * and the outlet renders nothing — useful for remotes that don't need internal
395
+ * routing.
396
+ */
397
+ const FLY_REMOTE_ROUTES = new InjectionToken('FLY_REMOTE_ROUTES');
398
+ /**
399
+ * Match a route's path pattern against URL segments. Returns the captured params
400
+ * map on a successful match, or `null` if the pattern can't match. Empty path
401
+ * matches only an empty segments array.
402
+ *
403
+ * Exported for unit testing — most consumers should rely on
404
+ * `FlyRemoteRouter.matchedRoute()`.
405
+ */
406
+ function matchFlyRoutePattern(pattern, segments) {
407
+ const patternSegments = pattern.split('/').filter(Boolean);
408
+ if (patternSegments.length !== segments.length)
409
+ return null;
410
+ const params = {};
411
+ for (let i = 0; i < patternSegments.length; i++) {
412
+ const p = patternSegments[i];
413
+ const s = segments[i];
414
+ if (p.startsWith(':')) {
415
+ params[p.slice(1)] = decodeURIComponent(s);
416
+ }
417
+ else if (p !== s) {
418
+ return null;
419
+ }
420
+ }
421
+ return params;
422
+ }
423
+
378
424
  /**
379
425
  * FlyOS standard navigation surface for Business / Supporting App remotes.
380
426
  *
@@ -437,6 +483,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
437
483
  class FlyRemoteRouter {
438
484
  windowData = inject(WINDOW_DATA, { optional: true });
439
485
  router = inject(Router, { optional: true });
486
+ /**
487
+ * Optional route table — if the remote provides `FLY_REMOTE_ROUTES`,
488
+ * `matchedRoute` / `params` resolve against it. Unset means the consumer is
489
+ * driving its own switch/template — `matchedRoute` stays `null`.
490
+ */
491
+ routes = inject(FLY_REMOTE_ROUTES, { optional: true }) ?? [];
440
492
  /**
441
493
  * True when this remote is rendered inside the FlyOS desktop shell.
442
494
  *
@@ -469,6 +521,35 @@ class FlyRemoteRouter {
469
521
  const u = this._url();
470
522
  return u.split('?')[0].split('#')[0].split('/').filter(Boolean);
471
523
  }, ...(ngDevMode ? [{ debugName: "segments" }] : /* istanbul ignore next */ []));
524
+ /**
525
+ * First route from `FLY_REMOTE_ROUTES` whose pattern matches `segments()`,
526
+ * paired with its captured params. `null` when no routes are registered or
527
+ * none matches. Used by `<fly-remote-router-outlet>` to pick what to render.
528
+ *
529
+ * Match order: routes are tried in declaration order. Put more specific
530
+ * patterns (e.g. `'signals/:id'`) before catch-alls.
531
+ */
532
+ matchedRoute = computed(() => {
533
+ const segs = this.segments();
534
+ for (const route of this.routes) {
535
+ const params = matchFlyRoutePattern(route.path, segs);
536
+ if (params != null)
537
+ return { route, params };
538
+ }
539
+ return null;
540
+ }, ...(ngDevMode ? [{ debugName: "matchedRoute" }] : /* istanbul ignore next */ []));
541
+ /**
542
+ * Captured route params from the active route, e.g. `{ id: 'abc' }` for a
543
+ * `'signals/:id'` match on `/signals/abc`. Empty object if no route matched
544
+ * or the matched route has no captures.
545
+ *
546
+ * Components can read this directly:
547
+ * ```ts
548
+ * private readonly flyRouter = inject(FlyRemoteRouter);
549
+ * readonly signalId = computed(() => this.flyRouter.params()['id'] ?? '');
550
+ * ```
551
+ */
552
+ params = computed(() => this.matchedRoute()?.params ?? {}, ...(ngDevMode ? [{ debugName: "params" }] : /* istanbul ignore next */ []));
472
553
  constructor() {
473
554
  if (!this.isEmbedded && this.router) {
474
555
  // Seed with the current standalone URL so consumers can read `segments()`
@@ -535,6 +616,71 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
535
616
  args: [{ providedIn: 'root' }]
536
617
  }], ctorParameters: () => [] });
537
618
 
619
+ /**
620
+ * Outlet for FlyOS-embedded routing. Renders the component associated with the
621
+ * first matching route in the consumer's `FLY_REMOTE_ROUTES` table, reacting to
622
+ * `FlyRemoteRouter.navigate(...)` calls.
623
+ *
624
+ * Use this **only in embedded mode**. Standalone remotes should keep their
625
+ * `<router-outlet>` — `FlyRemoteRouter.navigate` delegates to Angular's Router
626
+ * there, and this outlet would just shadow it.
627
+ *
628
+ * Recommended pattern in a remote's root component:
629
+ * ```html
630
+ * @if (router.isEmbedded) {
631
+ * <fly-remote-router-outlet />
632
+ * } @else {
633
+ * <router-outlet />
634
+ * }
635
+ * ```
636
+ *
637
+ * Why both? Standalone keeps full Angular routing — query params, guards,
638
+ * resolvers, lazy loading, RouterLink. Embedded gets a stripped-down
639
+ * synchronous outlet that matches against the same route table the remote
640
+ * declared via `FLY_REMOTE_ROUTES`. The same `router.navigate(['/foo', id])`
641
+ * call works in both modes; only the rendering surface differs.
642
+ *
643
+ * Limitations vs. `<router-outlet>`:
644
+ * - Synchronous components only (no `loadComponent` / `loadChildren`).
645
+ * - No guards / resolvers / data resolution.
646
+ * - No query-param / hash handling — only path segments.
647
+ * - No `RouterLink` directive — use `(click)="router.navigate(...)"`.
648
+ *
649
+ * Components rendered by this outlet read route params via `FlyRemoteRouter.params`:
650
+ * ```ts
651
+ * private readonly router = inject(FlyRemoteRouter);
652
+ * readonly id = computed(() => this.router.params()['id'] ?? '');
653
+ * ```
654
+ */
655
+ class FlyRemoteRouterOutletComponent {
656
+ router = inject(FlyRemoteRouter);
657
+ /**
658
+ * Read directly from FlyRemoteRouter so the outlet re-renders whenever the
659
+ * URL changes (signal-based, OnPush-friendly).
660
+ */
661
+ matched = this.router.matchedRoute;
662
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyRemoteRouterOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
663
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: FlyRemoteRouterOutletComponent, isStandalone: true, selector: "fly-remote-router-outlet", ngImport: i0, template: `
664
+ @if (matched(); as m) {
665
+ <ng-container *ngComponentOutlet="m.route.component" />
666
+ }
667
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
668
+ }
669
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyRemoteRouterOutletComponent, decorators: [{
670
+ type: Component,
671
+ args: [{
672
+ selector: 'fly-remote-router-outlet',
673
+ standalone: true,
674
+ imports: [NgComponentOutlet],
675
+ changeDetection: ChangeDetectionStrategy.OnPush,
676
+ template: `
677
+ @if (matched(); as m) {
678
+ <ng-container *ngComponentOutlet="m.route.component" />
679
+ }
680
+ `,
681
+ }]
682
+ }] });
683
+
538
684
  /**
539
685
  * Translates a key using the shared I18nService.
540
686
  *
@@ -3882,5 +4028,5 @@ const AUDIENCE_ERROR_CODES = {
3882
4028
  * Generated bundle index. Do not edit.
3883
4029
  */
3884
4030
 
3885
- export { AGENT_DRAG_MIME, AGENT_PAYLOAD_VERSION, AUDIENCE_ERROR_CODES, AUDIENCE_LIMITS, AUDIENCE_PRESETS, AUDIENCE_TERM_KINDS, AgentCommandRegistry, AgentDropRegistry, AgentPayloadOversizeError, AudienceBuilderComponent, AuthService, ContextMenuComponent, DEFAULT_AGENT_PAYLOAD_LIMITS, DEFAULT_FLY_THEME_MODE, DialogResult, FLY_LOCALE_CATALOG, FLY_THEME_MODE_IDS, FlyAgentDraggableDirective, FlyBlockUiComponent, FlyFileUploadComponent, FlyImageUploadComponent, FlyRemoteRouter, FlyThemeService, I18nService, LAUNCH_CONTEXT, MessageBoxButtons, MessageBoxComponent, MessageBoxIcon, MessageBoxService, MockAuthService, RTL_LOCALE_SET, SHARE_ORG_CHART_SYSTEM_KEY_APPS, SHARE_ORG_CHART_SYSTEM_KEY_DEFAULT, SHARE_PANEL_DEFAULT_FILE_LEVELS, SharePanelComponent, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, findLocaleByDialect, findLocaleByPrefix, isRtlLocale, isRtlLocaleEntry, normalizeFlyTheme, trimAgentPayload, trimAgentString, utf8ByteLength, validateAgentPayload };
4031
+ export { AGENT_DRAG_MIME, AGENT_PAYLOAD_VERSION, AUDIENCE_ERROR_CODES, AUDIENCE_LIMITS, AUDIENCE_PRESETS, AUDIENCE_TERM_KINDS, AgentCommandRegistry, AgentDropRegistry, AgentPayloadOversizeError, AudienceBuilderComponent, AuthService, ContextMenuComponent, DEFAULT_AGENT_PAYLOAD_LIMITS, DEFAULT_FLY_THEME_MODE, DialogResult, FLY_LOCALE_CATALOG, FLY_REMOTE_ROUTES, FLY_THEME_MODE_IDS, FlyAgentDraggableDirective, FlyBlockUiComponent, FlyFileUploadComponent, FlyImageUploadComponent, FlyRemoteRouter, FlyRemoteRouterOutletComponent, FlyThemeService, I18nService, LAUNCH_CONTEXT, MessageBoxButtons, MessageBoxComponent, MessageBoxIcon, MessageBoxService, MockAuthService, RTL_LOCALE_SET, SHARE_ORG_CHART_SYSTEM_KEY_APPS, SHARE_ORG_CHART_SYSTEM_KEY_DEFAULT, SHARE_PANEL_DEFAULT_FILE_LEVELS, SharePanelComponent, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, findLocaleByDialect, findLocaleByPrefix, isRtlLocale, isRtlLocaleEntry, matchFlyRoutePattern, normalizeFlyTheme, trimAgentPayload, trimAgentString, utf8ByteLength, validateAgentPayload };
3886
4032
  //# sourceMappingURL=mohamedatia-fly-design-system.mjs.map