@mohamedatia/fly-design-system 2.6.3 → 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,
|
|
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
|
*
|
|
@@ -450,23 +502,9 @@ class FlyRemoteRouter {
|
|
|
450
502
|
* when the InjectionToken splits between host and remote bundles
|
|
451
503
|
* (a known Native Federation edge case).
|
|
452
504
|
*/
|
|
453
|
-
isEmbedded =
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const fromGlobal = g.__FLYOS_SHELL__ === true;
|
|
457
|
-
const result = fromWindowData || fromGlobal;
|
|
458
|
-
// DIAGNOSTIC — remove once confirmed working.
|
|
459
|
-
// eslint-disable-next-line no-console
|
|
460
|
-
console.log('[FlyRemoteRouter] init', {
|
|
461
|
-
version: '2.6.3-debug',
|
|
462
|
-
fromWindowData,
|
|
463
|
-
fromGlobal,
|
|
464
|
-
result,
|
|
465
|
-
windowData: this.windowData,
|
|
466
|
-
globalKeys: Object.keys(g).filter(k => k.startsWith('__FLYOS')),
|
|
467
|
-
});
|
|
468
|
-
return result;
|
|
469
|
-
})();
|
|
505
|
+
isEmbedded = this.windowData != null
|
|
506
|
+
|| (typeof globalThis !== 'undefined'
|
|
507
|
+
&& globalThis.__FLYOS_SHELL__ === true);
|
|
470
508
|
_url = signal('/', ...(ngDevMode ? [{ debugName: "_url" }] : /* istanbul ignore next */ []));
|
|
471
509
|
/**
|
|
472
510
|
* Current logical URL of the remote — `/signals/abc` etc.
|
|
@@ -483,6 +521,35 @@ class FlyRemoteRouter {
|
|
|
483
521
|
const u = this._url();
|
|
484
522
|
return u.split('?')[0].split('#')[0].split('/').filter(Boolean);
|
|
485
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 */ []));
|
|
486
553
|
constructor() {
|
|
487
554
|
if (!this.isEmbedded && this.router) {
|
|
488
555
|
// Seed with the current standalone URL so consumers can read `segments()`
|
|
@@ -549,6 +616,71 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
549
616
|
args: [{ providedIn: 'root' }]
|
|
550
617
|
}], ctorParameters: () => [] });
|
|
551
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
|
+
|
|
552
684
|
/**
|
|
553
685
|
* Translates a key using the shared I18nService.
|
|
554
686
|
*
|
|
@@ -3896,5 +4028,5 @@ const AUDIENCE_ERROR_CODES = {
|
|
|
3896
4028
|
* Generated bundle index. Do not edit.
|
|
3897
4029
|
*/
|
|
3898
4030
|
|
|
3899
|
-
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 };
|
|
3900
4032
|
//# sourceMappingURL=mohamedatia-fly-design-system.mjs.map
|