@mohamedatia/fly-design-system 2.9.0 → 2.10.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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, signal, computed, Injectable, inject, ErrorHandler, PLATFORM_ID, DestroyRef, ChangeDetectionStrategy, Component, Pipe, DOCUMENT, ElementRef, input, output, HostListener, ViewChild, EventEmitter, Output, Input, forwardRef, Injector, viewChild, effect, afterNextRender, ViewEncapsulation, model, Directive } from '@angular/core';
2
+ import { InjectionToken, signal, computed, Injectable, inject, ErrorHandler, PLATFORM_ID, DestroyRef, ChangeDetectionStrategy, Component, Pipe, DOCUMENT, ElementRef, input, output, HostListener, ViewChild, EventEmitter, Output, Input, forwardRef, Injector, viewChild, effect, afterNextRender, ViewEncapsulation, model, Directive, Renderer2 } from '@angular/core';
3
3
  import * as i1$1 from '@angular/common';
4
4
  import { isPlatformBrowser, NgComponentOutlet, CommonModule, DOCUMENT as DOCUMENT$1 } from '@angular/common';
5
5
  import { Router, NavigationEnd } from '@angular/router';
@@ -3922,6 +3922,113 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
3922
3922
  args: ['dragstart', ['$event']]
3923
3923
  }] } });
3924
3924
 
3925
+ /**
3926
+ * Renders an authenticated image into a host `<img>` element by fetching the
3927
+ * resource as a blob through Angular's `HttpClient` and binding the resulting
3928
+ * object URL to the host's `src` attribute.
3929
+ *
3930
+ * Why this exists: every Business / Supporting app stores files in the Files
3931
+ * Manager service, which authenticates with a Bearer JWT. A raw `<img src>`
3932
+ * request bypasses the Angular HTTP interceptor — the browser issues the GET
3933
+ * without the Authorization header, so the response is 401 and the image is
3934
+ * broken. The standard workaround is roughly fifteen lines of `effect` +
3935
+ * `URL.createObjectURL` + `URL.revokeObjectURL` per call site (see prior
3936
+ * inline copies in `FlyImageUploadComponent` and the Circles trend-detail
3937
+ * cover). This directive centralizes that pattern so:
3938
+ *
3939
+ * * Memory hygiene is automatic — the previous object URL is revoked
3940
+ * whenever the input changes and on destroy. Easy to forget inline.
3941
+ * * Error handling is uniform — a fetch failure clears the host `src`
3942
+ * so the browser shows its broken-image affordance once, not the prior
3943
+ * image stuck on screen.
3944
+ * * A future protocol change (per-request signed URLs, CDN tokens, etc.)
3945
+ * lands in one place rather than across every app's image bindings.
3946
+ *
3947
+ * Usage:
3948
+ *
3949
+ * ```html
3950
+ * <img [flySecureSrc]="trend.coverImageId" alt="" class="cover-img" />
3951
+ * ```
3952
+ *
3953
+ * Pass a full URL when the resource lives somewhere other than the default
3954
+ * Files Manager download endpoint:
3955
+ *
3956
+ * ```html
3957
+ * <img [flySecureSrc]="'/api/avatars/' + user.id" alt="" />
3958
+ * ```
3959
+ *
3960
+ * A bare file id (no `/` prefix) is resolved against
3961
+ * `/api/files/{id}/download`, which matches the Files Manager convention
3962
+ * used everywhere in the platform. Anything that starts with `/` is treated
3963
+ * as a literal URL and passed through unchanged — apps with bespoke download
3964
+ * routes (signed Excel exports, attachment thumbnails, …) keep working
3965
+ * without a wrapper.
3966
+ */
3967
+ class FlySecureSrcDirective {
3968
+ /**
3969
+ * File id or absolute path. Passing `null` / `undefined` / `''` clears the
3970
+ * `src` attribute and revokes the previous blob. Useful when binding to a
3971
+ * signal whose value can become empty (e.g. after a file is detached).
3972
+ */
3973
+ flySecureSrc = input(...(ngDevMode ? [undefined, { debugName: "flySecureSrc" }] : /* istanbul ignore next */ []));
3974
+ http = inject(HttpClient);
3975
+ host = inject((ElementRef));
3976
+ renderer = inject(Renderer2);
3977
+ /**
3978
+ * The object URL currently bound to the host's `src` attribute. Tracked so
3979
+ * we can revoke it before binding a new one (avoids leaking blob memory
3980
+ * across rapid input changes — e.g. carousel scrubbing) and on destroy.
3981
+ */
3982
+ currentUrl = null;
3983
+ constructor() {
3984
+ effect((onCleanup) => {
3985
+ const raw = this.flySecureSrc();
3986
+ this.releaseCurrent();
3987
+ this.renderer.removeAttribute(this.host.nativeElement, 'src');
3988
+ if (!raw)
3989
+ return;
3990
+ const url = raw.startsWith('/') ? raw : `/api/files/${raw}/download`;
3991
+ const sub = this.http
3992
+ .get(url, { responseType: 'blob' })
3993
+ .subscribe({
3994
+ next: (blob) => {
3995
+ const objectUrl = URL.createObjectURL(blob);
3996
+ this.currentUrl = objectUrl;
3997
+ this.renderer.setAttribute(this.host.nativeElement, 'src', objectUrl);
3998
+ },
3999
+ // A 401 / 403 / 404 here means the image is unavailable for this user.
4000
+ // Don't loop or retry — clear the src and let the host page render its
4001
+ // own placeholder. Logging is intentionally silent: it's expected on
4002
+ // permission boundaries (e.g. shared-evidence on a trend you can read
4003
+ // but whose file you can't), and stack-traces would be noise.
4004
+ error: () => {
4005
+ this.releaseCurrent();
4006
+ this.renderer.removeAttribute(this.host.nativeElement, 'src');
4007
+ },
4008
+ });
4009
+ onCleanup(() => sub.unsubscribe());
4010
+ });
4011
+ }
4012
+ ngOnDestroy() {
4013
+ this.releaseCurrent();
4014
+ }
4015
+ releaseCurrent() {
4016
+ if (this.currentUrl) {
4017
+ URL.revokeObjectURL(this.currentUrl);
4018
+ this.currentUrl = null;
4019
+ }
4020
+ }
4021
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlySecureSrcDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
4022
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: FlySecureSrcDirective, isStandalone: true, selector: "img[flySecureSrc]", inputs: { flySecureSrc: { classPropertyName: "flySecureSrc", publicName: "flySecureSrc", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
4023
+ }
4024
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlySecureSrcDirective, decorators: [{
4025
+ type: Directive,
4026
+ args: [{
4027
+ selector: 'img[flySecureSrc]',
4028
+ standalone: true,
4029
+ }]
4030
+ }], ctorParameters: () => [], propDecorators: { flySecureSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "flySecureSrc", required: false }] }] } });
4031
+
3925
4032
  /**
3926
4033
  * Verbatim TypeScript port of the C# `LanguageDTO[] _AllLanguages` array supplied by
3927
4034
  * product. **31 entries**, in the canonical order. Three RTL flagged: `he-IL`, `ur-IN`,
@@ -4560,5 +4667,5 @@ const AUDIENCE_ERROR_CODES = {
4560
4667
  * Generated bundle index. Do not edit.
4561
4668
  */
4562
4669
 
4563
- export { AGENT_DRAG_MIME, AGENT_PAYLOAD_VERSION, APP_LOOKUP, 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, FLYOS_LAUNCH_EVENT, FLY_LOCALE_CATALOG, FLY_REMOTE_BASE_PATH, FLY_REMOTE_ROUTES, FLY_THEME_MODE_IDS, FlyAgentDraggableDirective, FlyBlockUiComponent, FlyFileUploadComponent, FlyImageUploadComponent, FlyRemoteRouter, FlyRemoteRouterOutletComponent, FlyThemeService, FlyosPendingLaunchesGlobalKey, 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, SourceAppResolver, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, findLocaleByDialect, findLocaleByPrefix, isRtlLocale, isRtlLocaleEntry, loadRemoteStyles, matchFlyRoutePattern, normalizeFlyTheme, trimAgentPayload, trimAgentString, unloadRemoteStyles, utf8ByteLength, validateAgentPayload };
4670
+ export { AGENT_DRAG_MIME, AGENT_PAYLOAD_VERSION, APP_LOOKUP, 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, FLYOS_LAUNCH_EVENT, FLY_LOCALE_CATALOG, FLY_REMOTE_BASE_PATH, FLY_REMOTE_ROUTES, FLY_THEME_MODE_IDS, FlyAgentDraggableDirective, FlyBlockUiComponent, FlyFileUploadComponent, FlyImageUploadComponent, FlyRemoteRouter, FlyRemoteRouterOutletComponent, FlySecureSrcDirective, FlyThemeService, FlyosPendingLaunchesGlobalKey, 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, SourceAppResolver, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, findLocaleByDialect, findLocaleByPrefix, isRtlLocale, isRtlLocaleEntry, loadRemoteStyles, matchFlyRoutePattern, normalizeFlyTheme, trimAgentPayload, trimAgentString, unloadRemoteStyles, utf8ByteLength, validateAgentPayload };
4564
4671
  //# sourceMappingURL=mohamedatia-fly-design-system.mjs.map