@mohamedatia/fly-design-system 2.7.4 → 2.8.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.
- package/fesm2022/mohamedatia-fly-design-system.mjs +205 -106
- package/fesm2022/mohamedatia-fly-design-system.mjs.map +1 -1
- package/package.json +1 -1
- package/scss/_fly-theme.scss +1 -2
- package/scss/_theme-auto.scss +6 -12
- package/scss/_theme-dark.scss +0 -2
- package/scss/_theme-light.scss +0 -2
- package/scss/_tokens.scss +0 -2
- package/types/mohamedatia-fly-design-system.d.ts +61 -44
- package/types/mohamedatia-fly-design-system.d.ts.map +1 -1
- package/scss/_theme-spatial-vars.scss +0 -56
- package/scss/_theme-spatial.scss +0 -25
|
@@ -296,7 +296,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
296
296
|
}] });
|
|
297
297
|
|
|
298
298
|
/** Single source of truth for persisted / API theme strings. */
|
|
299
|
-
const FLY_THEME_MODE_IDS = ['light', '
|
|
299
|
+
const FLY_THEME_MODE_IDS = ['light', 'dark'];
|
|
300
300
|
/** Factory default: `dark` (opaque dark / `html.dark-theme`); shell i18n `settings.theme.dark`. Keep in sync with `UserSettings` defaults. */
|
|
301
301
|
const DEFAULT_FLY_THEME_MODE = 'dark';
|
|
302
302
|
/** Coerce unknown values (localStorage, API) to a valid theme mode. */
|
|
@@ -307,7 +307,7 @@ function normalizeFlyTheme(value) {
|
|
|
307
307
|
return DEFAULT_FLY_THEME_MODE;
|
|
308
308
|
}
|
|
309
309
|
/**
|
|
310
|
-
* Applies `html.light-theme` / `html.
|
|
310
|
+
* Applies `html.light-theme` / `html.dark-theme` for DS SCSS.
|
|
311
311
|
* Shell and standalone Business Apps use the same service (federation singleton when shared).
|
|
312
312
|
*/
|
|
313
313
|
class FlyThemeService {
|
|
@@ -319,11 +319,8 @@ class FlyThemeService {
|
|
|
319
319
|
return;
|
|
320
320
|
}
|
|
321
321
|
const html = document.documentElement;
|
|
322
|
-
html.classList.remove('light-theme', '
|
|
323
|
-
if (mode === '
|
|
324
|
-
html.classList.add('spatial-theme');
|
|
325
|
-
}
|
|
326
|
-
else if (mode === 'dark') {
|
|
322
|
+
html.classList.remove('light-theme', 'dark-theme');
|
|
323
|
+
if (mode === 'dark') {
|
|
327
324
|
html.classList.add('dark-theme');
|
|
328
325
|
}
|
|
329
326
|
else {
|
|
@@ -745,15 +742,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
745
742
|
* On first mount of each remote, the shell calls `loadRemoteStyles(appId,
|
|
746
743
|
* remoteBaseUrl)`. This function:
|
|
747
744
|
* 1. Derives the remote's `index.html` URL from `remoteBaseUrl`.
|
|
748
|
-
* 2. Fetches it (one network round-trip per remote
|
|
745
|
+
* 2. Fetches it (one network round-trip per remote; browser-cached thereafter).
|
|
749
746
|
* 3. Parses the HTML with DOMParser to extract the first
|
|
750
747
|
* `<link rel="stylesheet">` — the hashed production stylesheet
|
|
751
748
|
* (e.g. `styles-ABC123.css`). This is Strategy (b): index.html discovery.
|
|
752
|
-
* 4. Injects
|
|
753
|
-
*
|
|
754
|
-
* Idempotent: if a `<style data-fly-app="appId">` with the same href
|
|
755
|
-
* is already present, the call is a no-op. If the href differs (hot
|
|
756
|
-
* upgrade), the existing element is replaced.
|
|
749
|
+
* 4. Injects the stylesheet into `document.head` using one of two approaches
|
|
750
|
+
* (see Injection approach below).
|
|
757
751
|
*
|
|
758
752
|
* Why strategy (b)?
|
|
759
753
|
* -----------------
|
|
@@ -763,34 +757,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
763
757
|
* (c) `stylesUrl` in manifest/remoteEntry.json — cleanest long-term; requires
|
|
764
758
|
* protocol change to every remote's CI pipeline (out of scope for Wave A1).
|
|
765
759
|
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
768
|
-
*
|
|
769
|
-
*
|
|
770
|
-
*
|
|
760
|
+
* Injection approach
|
|
761
|
+
* ------------------
|
|
762
|
+
* Preferred (Firefox 134+, Chrome 99+, Safari 17.2+):
|
|
763
|
+
* `<link rel="stylesheet" href="…" crossorigin="anonymous" layer="remote">`
|
|
764
|
+
*
|
|
765
|
+
* The `layer` attribute on `<link>` is feature-detected once at module init via:
|
|
766
|
+
* `'layer' in HTMLLinkElement.prototype`
|
|
767
|
+
* If the attribute is supported, the shell injects a non-render-blocking `<link>`
|
|
768
|
+
* element. The browser gets proper preload semantics and CORS headers flow through
|
|
769
|
+
* the normal stylesheet pipeline.
|
|
771
770
|
*
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
-
* rules, causing remote stylesheets to never load. The correct syntax is the
|
|
775
|
-
* `layer()` modifier on a top-level `@import`.
|
|
771
|
+
* Fallback (browsers without `link[layer]` support):
|
|
772
|
+
* `<style>@import url("…") layer(remote);</style>`
|
|
776
773
|
*
|
|
777
|
-
*
|
|
774
|
+
* CSS Cascade Level 5: `@import url("…") layer(remote)` is the ONLY valid way to
|
|
775
|
+
* place an @import inside a named cascade layer when using the `<style>` approach.
|
|
776
|
+
* The block-form `@layer remote { @import url("…"); }` is INVALID CSS — the spec
|
|
777
|
+
* forbids `@import` inside any block at-rule. Browsers silently drop such rules.
|
|
778
778
|
*
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
* resolve against the shell origin instead of the remote origin, breaking
|
|
782
|
-
* fonts and images. Dealbreaker without a full URL-rewrite pass.
|
|
783
|
-
* - `<link layer="remote">`: the `layer` attribute on `<link>` is not yet
|
|
784
|
-
* shipped in Firefox (as of 2025-05). Non-portable.
|
|
779
|
+
* Both code paths attach `data-fly-app` and `data-fly-href` attributes to the
|
|
780
|
+
* injected element so idempotency checks work uniformly.
|
|
785
781
|
*
|
|
786
782
|
* Layer order declaration
|
|
787
783
|
* -----------------------
|
|
788
|
-
* A one-time `<style data-fly-layers>` element is prepended to `<head>`
|
|
789
|
-
*
|
|
784
|
+
* A one-time `<style data-fly-layers>` element is prepended to `<head>` at module
|
|
785
|
+
* init. It establishes the canonical layer order:
|
|
790
786
|
* reset → designsystem → shell → remote → overrides
|
|
791
787
|
* This guarantees that even if individual `@layer` blocks are injected in any
|
|
792
788
|
* order at runtime, the cascade priority is always deterministic.
|
|
793
789
|
*
|
|
790
|
+
* CSP nonce propagation
|
|
791
|
+
* ---------------------
|
|
792
|
+
* If a `<meta name="csp-nonce">` element is present, its content is read once at
|
|
793
|
+
* module init and applied to every injected `<style>` or `<link>` element. If no
|
|
794
|
+
* nonce meta is present, nothing is set (graceful degradation).
|
|
795
|
+
*
|
|
796
|
+
* URL validation
|
|
797
|
+
* --------------
|
|
798
|
+
* Discovered hrefs must be `http://`-, `https://`-, or `//`-prefixed. `javascript:`,
|
|
799
|
+
* `data:`, and `blob:` are rejected outright. The href's origin must also match the
|
|
800
|
+
* origin of `remoteBaseUrl` (same-origin as the remote), preventing a compromised
|
|
801
|
+
* `index.html` from pointing at an attacker domain.
|
|
802
|
+
*
|
|
794
803
|
* Unload decision
|
|
795
804
|
* ---------------
|
|
796
805
|
* `unloadRemoteStyles` is exported for symmetry but should NOT be called on
|
|
@@ -801,63 +810,69 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
801
810
|
*
|
|
802
811
|
* Caveats
|
|
803
812
|
* -------
|
|
804
|
-
* - CORS: the remote's dev/prod server must serve `index.html` with a
|
|
805
|
-
*
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
* - Race on rapid mount/unmount: if `loadRemoteStyles` is called a second time
|
|
810
|
-
*
|
|
811
|
-
*
|
|
813
|
+
* - CORS: the remote's dev/prod server must serve `index.html` with a permissive
|
|
814
|
+
* `Access-Control-Allow-Origin` header (or be same-origin via the YARP gateway).
|
|
815
|
+
* The fetch uses `credentials: 'omit'` to avoid credential-carrying preflights.
|
|
816
|
+
* - Fetch timeout: the index.html fetch is capped at 5 seconds. On timeout the
|
|
817
|
+
* in-flight entry is cleared so a retry is possible on the next call.
|
|
818
|
+
* - Race on rapid mount/unmount: if `loadRemoteStyles` is called a second time for
|
|
819
|
+
* the same appId while the first fetch is still in-flight, the second call joins
|
|
820
|
+
* the same in-flight Promise.
|
|
812
821
|
* - Angular hashing: Angular's production build hashes the stylesheet filename.
|
|
813
|
-
* DOMParser picks the first `<link rel="stylesheet">` in `<head>`, which is
|
|
814
|
-
*
|
|
815
|
-
*
|
|
822
|
+
* DOMParser picks the first `<link rel="stylesheet">` in `<head>`, which is the
|
|
823
|
+
* single global stylesheet Angular emits. If a remote emits multiple stylesheets,
|
|
824
|
+
* only the first is loaded — acceptable for Wave A1.
|
|
816
825
|
*/
|
|
817
|
-
/** Per-appId cache of the resolved stylesheet href (or `null` if not found). */
|
|
818
|
-
const _resolvedHref = new Map();
|
|
819
|
-
/** In-flight fetch promises keyed by appId — prevents duplicate fetches. */
|
|
820
|
-
const _inFlight = new Map();
|
|
821
826
|
/**
|
|
822
|
-
*
|
|
823
|
-
*
|
|
824
|
-
*
|
|
827
|
+
* Feature detection: does the browser support the `layer` attribute on
|
|
828
|
+
* `<link>` elements? Probed once at module init.
|
|
829
|
+
* Firefox 134+, Chrome 99+, Safari 17.2+ all ship this.
|
|
825
830
|
*/
|
|
826
|
-
|
|
831
|
+
const _linkLayerSupported = typeof HTMLLinkElement !== 'undefined' && 'layer' in HTMLLinkElement.prototype;
|
|
827
832
|
/**
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
* reset → designsystem → shell → remote → overrides
|
|
832
|
-
*
|
|
833
|
-
* Any subsequent `@layer X { … }` block lands in the already-established slot
|
|
834
|
-
* regardless of injection order, so late-arriving remote stylesheets never
|
|
835
|
-
* "win" over shell or override rules. Idempotent — runs at most once per page.
|
|
833
|
+
* CSP nonce, read once from `<meta name="csp-nonce">` at module init.
|
|
834
|
+
* Applied to every injected `<style>` / `<link>` element. Null when absent.
|
|
836
835
|
*/
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
836
|
+
const _cspNonce = typeof document !== 'undefined'
|
|
837
|
+
? (document.querySelector('meta[name="csp-nonce"]')?.content ?? null)
|
|
838
|
+
: null;
|
|
839
|
+
/** In-flight fetch promises keyed by appId — prevents duplicate fetches. */
|
|
840
|
+
const _inFlight = new Map();
|
|
841
|
+
// ---------------------------------------------------------------------------
|
|
842
|
+
// Layer order — injected immediately at module init (synchronous, runs once).
|
|
843
|
+
// ---------------------------------------------------------------------------
|
|
844
|
+
(function _ensureLayerOrder() {
|
|
845
|
+
if (typeof document === 'undefined')
|
|
846
|
+
return; // SSR guard
|
|
842
847
|
if (document.head.querySelector('style[data-fly-layers]'))
|
|
843
848
|
return;
|
|
844
849
|
const style = document.createElement('style');
|
|
845
850
|
style.setAttribute('data-fly-layers', '');
|
|
846
851
|
style.textContent = '@layer reset, designsystem, shell, remote, overrides;';
|
|
852
|
+
if (_cspNonce)
|
|
853
|
+
style.nonce = _cspNonce;
|
|
847
854
|
// Prepend so this always precedes any other layer-bearing <style> blocks.
|
|
848
855
|
document.head.insertBefore(style, document.head.firstChild);
|
|
849
|
-
}
|
|
856
|
+
})();
|
|
857
|
+
// ---------------------------------------------------------------------------
|
|
858
|
+
// Internal helpers
|
|
859
|
+
// ---------------------------------------------------------------------------
|
|
850
860
|
/**
|
|
851
861
|
* Discovers the hashed stylesheet href from the remote's `index.html`.
|
|
852
|
-
*
|
|
862
|
+
* Applies a 5-second AbortController timeout. Clears `_inFlight` on abort so
|
|
863
|
+
* a retry is possible. Returns `null` on any failure.
|
|
853
864
|
*/
|
|
854
|
-
async function _discoverStylesheetHref(remoteBaseUrl) {
|
|
865
|
+
async function _discoverStylesheetHref(appId, remoteBaseUrl) {
|
|
855
866
|
const indexUrl = remoteBaseUrl.replace(/\/$/, '') + '/index.html';
|
|
867
|
+
const controller = new AbortController();
|
|
868
|
+
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
856
869
|
try {
|
|
857
870
|
const res = await fetch(indexUrl, {
|
|
858
871
|
credentials: 'omit',
|
|
859
872
|
cache: 'default',
|
|
873
|
+
signal: controller.signal,
|
|
860
874
|
});
|
|
875
|
+
clearTimeout(timeoutId);
|
|
861
876
|
if (!res.ok)
|
|
862
877
|
return null;
|
|
863
878
|
const html = await res.text();
|
|
@@ -866,87 +881,163 @@ async function _discoverStylesheetHref(remoteBaseUrl) {
|
|
|
866
881
|
const link = doc.head.querySelector('link[rel="stylesheet"]');
|
|
867
882
|
if (!link?.href)
|
|
868
883
|
return null;
|
|
869
|
-
// `link.href` from DOMParser
|
|
870
|
-
// which defaults to `about:blank`. We get the raw `href` attribute instead.
|
|
884
|
+
// `link.href` from DOMParser resolves against `about:blank`. Use raw attribute.
|
|
871
885
|
const rawHref = link.getAttribute('href') ?? '';
|
|
872
886
|
if (!rawHref)
|
|
873
887
|
return null;
|
|
874
|
-
//
|
|
875
|
-
//
|
|
888
|
+
// Early-reject dangerous schemes before any URL resolution.
|
|
889
|
+
// Without this guard, `javascript:alert(1)` would be rewritten to
|
|
890
|
+
// `remoteBaseUrl/javascript:alert(1)` and silently pass origin validation.
|
|
891
|
+
const rawLower = rawHref.toLowerCase();
|
|
892
|
+
if (rawLower.startsWith('javascript:') ||
|
|
893
|
+
rawLower.startsWith('data:') ||
|
|
894
|
+
rawLower.startsWith('blob:')) {
|
|
895
|
+
console.warn(`[FlyOS] loadRemoteStyles: rejected unsafe stylesheet href "${rawHref}"`);
|
|
896
|
+
return null;
|
|
897
|
+
}
|
|
898
|
+
// Resolve to absolute URL for origin validation.
|
|
899
|
+
let resolvedHref;
|
|
876
900
|
if (rawHref.startsWith('http://') || rawHref.startsWith('https://') || rawHref.startsWith('//')) {
|
|
877
|
-
|
|
901
|
+
resolvedHref = rawHref;
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
resolvedHref = remoteBaseUrl.replace(/\/$/, '') + '/' + rawHref.replace(/^\//, '');
|
|
878
905
|
}
|
|
879
|
-
return
|
|
906
|
+
return _validateHref(resolvedHref, remoteBaseUrl);
|
|
880
907
|
}
|
|
881
908
|
catch {
|
|
909
|
+
clearTimeout(timeoutId);
|
|
910
|
+
// On abort or network error, clear in-flight so a retry is possible.
|
|
911
|
+
_inFlight.delete(appId);
|
|
882
912
|
return null;
|
|
883
913
|
}
|
|
884
914
|
}
|
|
915
|
+
/**
|
|
916
|
+
* Validates that a discovered href is safe to inject:
|
|
917
|
+
* - Must be `http://`, `https://`, or `//`-prefixed (no `javascript:`, `data:`, `blob:`).
|
|
918
|
+
* - Must share the same origin as `remoteBaseUrl`.
|
|
919
|
+
*
|
|
920
|
+
* Returns the href on success; logs a warning and returns `null` on failure.
|
|
921
|
+
*/
|
|
922
|
+
function _validateHref(href, remoteBaseUrl) {
|
|
923
|
+
// Block dangerous schemes.
|
|
924
|
+
const lower = href.toLowerCase();
|
|
925
|
+
if (lower.startsWith('javascript:') ||
|
|
926
|
+
lower.startsWith('data:') ||
|
|
927
|
+
lower.startsWith('blob:')) {
|
|
928
|
+
console.warn(`[FlyOS] loadRemoteStyles: rejected unsafe stylesheet href "${href}"`);
|
|
929
|
+
return null;
|
|
930
|
+
}
|
|
931
|
+
// Must be absolute (http/https/protocol-relative).
|
|
932
|
+
if (!lower.startsWith('http://') &&
|
|
933
|
+
!lower.startsWith('https://') &&
|
|
934
|
+
!lower.startsWith('//')) {
|
|
935
|
+
console.warn(`[FlyOS] loadRemoteStyles: rejected non-absolute stylesheet href "${href}"`);
|
|
936
|
+
return null;
|
|
937
|
+
}
|
|
938
|
+
// Origin must match remoteBaseUrl — prevents a compromised index.html from
|
|
939
|
+
// redirecting styles at an attacker-controlled domain.
|
|
940
|
+
try {
|
|
941
|
+
const hrefOrigin = new URL(href.startsWith('//') ? 'https:' + href : href).origin;
|
|
942
|
+
const remoteOrigin = new URL(remoteBaseUrl).origin;
|
|
943
|
+
if (hrefOrigin !== remoteOrigin) {
|
|
944
|
+
console.warn(`[FlyOS] loadRemoteStyles: rejected cross-origin stylesheet href "${href}" ` +
|
|
945
|
+
`(expected origin "${remoteOrigin}", got "${hrefOrigin}")`);
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
catch {
|
|
950
|
+
console.warn(`[FlyOS] loadRemoteStyles: could not parse href origin for "${href}"`);
|
|
951
|
+
return null;
|
|
952
|
+
}
|
|
953
|
+
return href;
|
|
954
|
+
}
|
|
955
|
+
// ---------------------------------------------------------------------------
|
|
956
|
+
// Public API
|
|
957
|
+
// ---------------------------------------------------------------------------
|
|
885
958
|
/**
|
|
886
959
|
* Injects the remote's stylesheet into `document.head`.
|
|
887
960
|
*
|
|
888
|
-
* @param appId
|
|
961
|
+
* @param appId - Stable identifier for the remote app (matches `DesktopApp.id`).
|
|
889
962
|
* @param remoteBaseUrl - Base URL of the remote, e.g. `https://circles.example.com`
|
|
890
963
|
* or `http://localhost:7202`. Must NOT include `/remoteEntry.json`.
|
|
891
964
|
*
|
|
892
965
|
* The call is idempotent:
|
|
893
|
-
* - If
|
|
894
|
-
* already exists → no-op.
|
|
966
|
+
* - If an element with `data-fly-app="appId"` whose `data-fly-href` matches the
|
|
967
|
+
* discovered href already exists → no-op.
|
|
895
968
|
* - If the href differs (hot upgrade) → existing element is replaced.
|
|
896
969
|
* - If no stylesheet is found in `index.html` → no-op (logged as a warning).
|
|
897
970
|
*
|
|
898
|
-
* Injection shape:
|
|
971
|
+
* Injection shape (preferred — `link[layer]` supported):
|
|
972
|
+
* <link rel="stylesheet" data-fly-app="<appId>" data-fly-href="<href>"
|
|
973
|
+
* href="<href>" crossorigin="anonymous" layer="remote">
|
|
974
|
+
*
|
|
975
|
+
* Injection shape (fallback — `link[layer]` unsupported):
|
|
899
976
|
* <style data-fly-app="<appId>" data-fly-href="<href>">
|
|
900
977
|
* @import url("<href>") layer(remote);
|
|
901
978
|
* </style>
|
|
902
979
|
*
|
|
903
980
|
* The `data-fly-href` attribute stores the discovered href separately from the
|
|
904
|
-
*
|
|
981
|
+
* element content so idempotency checks can compare the URL without parsing CSS.
|
|
905
982
|
*/
|
|
906
983
|
async function loadRemoteStyles(appId, remoteBaseUrl) {
|
|
907
984
|
if (typeof document === 'undefined')
|
|
908
985
|
return; // SSR guard
|
|
909
|
-
// Ensure the canonical layer order is declared before any remote layer is injected.
|
|
910
|
-
_ensureLayerOrder();
|
|
911
986
|
// Kick off or join an in-flight fetch for this appId.
|
|
987
|
+
// Note: _resolvedHref cache is intentionally absent — always re-discover so
|
|
988
|
+
// the browser's HTTP cache handles cost (index.html is tiny) and hot-deploys
|
|
989
|
+
// are picked up without a page reload.
|
|
912
990
|
let fetchPromise = _inFlight.get(appId);
|
|
913
991
|
if (!fetchPromise) {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
fetchPromise = Promise.resolve(_resolvedHref.get(appId) ?? null);
|
|
917
|
-
}
|
|
918
|
-
else {
|
|
919
|
-
fetchPromise = _discoverStylesheetHref(remoteBaseUrl);
|
|
920
|
-
_inFlight.set(appId, fetchPromise);
|
|
921
|
-
}
|
|
992
|
+
fetchPromise = _discoverStylesheetHref(appId, remoteBaseUrl);
|
|
993
|
+
_inFlight.set(appId, fetchPromise);
|
|
922
994
|
}
|
|
923
995
|
const href = await fetchPromise;
|
|
924
996
|
_inFlight.delete(appId);
|
|
925
|
-
_resolvedHref.set(appId, href);
|
|
926
997
|
if (!href) {
|
|
927
998
|
console.warn(`[FlyOS] loadRemoteStyles: no stylesheet found in ${remoteBaseUrl}/index.html for appId="${appId}"`);
|
|
928
999
|
return;
|
|
929
1000
|
}
|
|
930
|
-
|
|
931
|
-
const
|
|
1001
|
+
// Check both <link> and <style> selectors — handle upgrades from the old fallback path.
|
|
1002
|
+
const escapedId = CSS.escape(appId);
|
|
1003
|
+
const existing = document.head.querySelector(`link[data-fly-app="${escapedId}"], style[data-fly-app="${escapedId}"]`);
|
|
932
1004
|
if (existing) {
|
|
933
1005
|
if (existing.getAttribute('data-fly-href') === href)
|
|
934
1006
|
return; // identical — no-op
|
|
935
|
-
// Hot upgrade:
|
|
1007
|
+
// Hot upgrade: remove old element (could be <link> or <style>) atomically.
|
|
936
1008
|
existing.remove();
|
|
937
1009
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1010
|
+
if (_linkLayerSupported) {
|
|
1011
|
+
// Preferred path: non-render-blocking <link> with native layer= attribute.
|
|
1012
|
+
// Firefox 134+, Chrome 99+, Safari 17.2+ all support this.
|
|
1013
|
+
const link = document.createElement('link');
|
|
1014
|
+
link.rel = 'stylesheet';
|
|
1015
|
+
link.setAttribute('data-fly-app', appId);
|
|
1016
|
+
link.setAttribute('data-fly-href', href);
|
|
1017
|
+
link.href = href;
|
|
1018
|
+
link.crossOrigin = 'anonymous';
|
|
1019
|
+
link['layer'] = 'remote';
|
|
1020
|
+
if (_cspNonce)
|
|
1021
|
+
link.nonce = _cspNonce;
|
|
1022
|
+
document.head.appendChild(link);
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
// Fallback path: <style> with @import layer(remote) for older browsers.
|
|
1026
|
+
// CSS Cascade Level 5: `@import url("…") layer(remote)` is the only valid
|
|
1027
|
+
// way to place an @import inside a named cascade layer. Block-form
|
|
1028
|
+
// `@layer remote { @import … }` is invalid and silently dropped by browsers.
|
|
1029
|
+
const style = document.createElement('style');
|
|
1030
|
+
style.setAttribute('data-fly-app', appId);
|
|
1031
|
+
style.setAttribute('data-fly-href', href);
|
|
1032
|
+
style.textContent = `@import url("${href}") layer(remote);`;
|
|
1033
|
+
if (_cspNonce)
|
|
1034
|
+
style.nonce = _cspNonce;
|
|
1035
|
+
document.head.appendChild(style);
|
|
1036
|
+
}
|
|
947
1037
|
}
|
|
948
1038
|
/**
|
|
949
|
-
* Removes the injected stylesheet
|
|
1039
|
+
* Removes the injected stylesheet element (either `<link>` or `<style>`) for
|
|
1040
|
+
* `appId` from `document.head` and clears all internal state for this appId.
|
|
950
1041
|
*
|
|
951
1042
|
* NOTE: Prefer NOT calling this on normal window close — keeping styles loaded
|
|
952
1043
|
* prevents FOUC flicker when the user reopens the same app. Call only if you
|
|
@@ -955,9 +1046,17 @@ async function loadRemoteStyles(appId, remoteBaseUrl) {
|
|
|
955
1046
|
function unloadRemoteStyles(appId) {
|
|
956
1047
|
if (typeof document === 'undefined')
|
|
957
1048
|
return; // SSR guard
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
1049
|
+
const escapedId = CSS.escape(appId);
|
|
1050
|
+
// Remove <link> element (preferred path) if present.
|
|
1051
|
+
document.head
|
|
1052
|
+
.querySelector(`link[data-fly-app="${escapedId}"]`)
|
|
1053
|
+
?.remove();
|
|
1054
|
+
// Remove <style> element (fallback path) if present.
|
|
1055
|
+
document.head
|
|
1056
|
+
.querySelector(`style[data-fly-app="${escapedId}"]`)
|
|
1057
|
+
?.remove();
|
|
1058
|
+
// Clear in-flight so a future call can retry.
|
|
1059
|
+
_inFlight.delete(appId);
|
|
961
1060
|
}
|
|
962
1061
|
|
|
963
1062
|
/**
|
|
@@ -2770,7 +2869,7 @@ class FlyImageUploadComponent {
|
|
|
2770
2869
|
</div>
|
|
2771
2870
|
}
|
|
2772
2871
|
</div>
|
|
2773
|
-
`, isInline: true, styles: [".cropper-container{-webkit-touch-callout:none;direction:ltr;font-size:0;line-height:0;position:relative;touch-action:none;-webkit-user-select:none;user-select:none}.cropper-container img{backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{inset:0;position:absolute}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:#3399ffbf;overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:\" \";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}.cropper-invisible{opacity:0}.cropper-bg{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC)}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}\n", ".fly-image-upload{display:flex;flex-direction:column;gap:6px}.fly-image-upload__hidden{display:none}.fly-image-upload__dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:24px;border:2px dashed var(--separator-primary, #e5e7eb);border-radius:12px;background:var(--fill-quaternary, #f9fafb);cursor:pointer;transition:border-color .15s,background .15s;min-height:120px}.fly-image-upload__dropzone:hover{border-color:var(--accent-primary, #0071e3);background:var(--fill-tertiary, #f3f4f6)}.fly-image-upload__icon{font-size:28px;color:var(--label-tertiary, #9ca3af)}.fly-image-upload__label{font-size:13px;color:var(--label-secondary, #6b7280)}.fly-image-upload__hint{font-size:11px;color:var(--label-tertiary, #9ca3af)}.fly-image-upload__preview{position:relative;border-radius:12px;overflow:hidden}.fly-image-upload__img{display:block;width:100%;max-height:280px;object-fit:cover;border-radius:12px}.fly-image-upload__overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:12px;background:#00000059;opacity:0;transition:opacity .15s}.fly-image-upload__preview:hover .fly-image-upload__overlay{opacity:1}.fly-image-upload__action-btn{width:36px;height:36px;border-radius:50%;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;background:#ffffffe6;color:var(--label-primary, #1f2937);font-size:14px;transition:background .15s}.fly-image-upload__action-btn:hover{background:#fff}.fly-image-upload__action-btn--danger:hover{background:#fee2e2;color:#dc2626}.fly-image-upload__error{font-size:12px;color:var(--system-red, #ef4444)}.fly-image-upload__crop-backdrop{position:fixed;inset:0;z-index:10000;display:flex;align-items:center;justify-content:center;background:#0009;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.fly-image-upload__crop-modal{background:var(--bg-primary, #fff);color:var(--label-primary, #1f2937);border-radius:16px;overflow:hidden;max-width:700px;width:90vw;box-shadow:0 20px 60px #0006;border:1px solid var(--separator-primary, rgba(0,0,0,.1))}.fly-image-upload__crop-header{padding:16px 20px;border-bottom:1px solid var(--separator-primary, #e5e7eb);background:var(--fill-quaternary, transparent)}.fly-image-upload__crop-header h3{margin:0;font-size:16px;font-weight:600;color:var(--label-primary, #1f2937)}.fly-image-upload__crop-body{max-height:60vh;overflow:hidden;background:var(--fill-tertiary, #f3f4f6)}.fly-image-upload__crop-body img{display:block;max-width:100%}.fly-image-upload__crop-footer{display:flex;justify-content:flex-end;gap:10px;padding:14px 20px;border-top:1px solid var(--separator-primary, #e5e7eb);background:var(--fill-quaternary, transparent)}html.
|
|
2872
|
+
`, isInline: true, styles: [".cropper-container{-webkit-touch-callout:none;direction:ltr;font-size:0;line-height:0;position:relative;touch-action:none;-webkit-user-select:none;user-select:none}.cropper-container img{backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{inset:0;position:absolute}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:#3399ffbf;overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:\" \";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}.cropper-invisible{opacity:0}.cropper-bg{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC)}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}\n", ".fly-image-upload{display:flex;flex-direction:column;gap:6px}.fly-image-upload__hidden{display:none}.fly-image-upload__dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:24px;border:2px dashed var(--separator-primary, #e5e7eb);border-radius:12px;background:var(--fill-quaternary, #f9fafb);cursor:pointer;transition:border-color .15s,background .15s;min-height:120px}.fly-image-upload__dropzone:hover{border-color:var(--accent-primary, #0071e3);background:var(--fill-tertiary, #f3f4f6)}.fly-image-upload__icon{font-size:28px;color:var(--label-tertiary, #9ca3af)}.fly-image-upload__label{font-size:13px;color:var(--label-secondary, #6b7280)}.fly-image-upload__hint{font-size:11px;color:var(--label-tertiary, #9ca3af)}.fly-image-upload__preview{position:relative;border-radius:12px;overflow:hidden}.fly-image-upload__img{display:block;width:100%;max-height:280px;object-fit:cover;border-radius:12px}.fly-image-upload__overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:12px;background:#00000059;opacity:0;transition:opacity .15s}.fly-image-upload__preview:hover .fly-image-upload__overlay{opacity:1}.fly-image-upload__action-btn{width:36px;height:36px;border-radius:50%;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;background:#ffffffe6;color:var(--label-primary, #1f2937);font-size:14px;transition:background .15s}.fly-image-upload__action-btn:hover{background:#fff}.fly-image-upload__action-btn--danger:hover{background:#fee2e2;color:#dc2626}.fly-image-upload__error{font-size:12px;color:var(--system-red, #ef4444)}.fly-image-upload__crop-backdrop{position:fixed;inset:0;z-index:10000;display:flex;align-items:center;justify-content:center;background:#0009;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.fly-image-upload__crop-modal{background:var(--bg-primary, #fff);color:var(--label-primary, #1f2937);border-radius:16px;overflow:hidden;max-width:700px;width:90vw;box-shadow:0 20px 60px #0006;border:1px solid var(--separator-primary, rgba(0,0,0,.1))}.fly-image-upload__crop-header{padding:16px 20px;border-bottom:1px solid var(--separator-primary, #e5e7eb);background:var(--fill-quaternary, transparent)}.fly-image-upload__crop-header h3{margin:0;font-size:16px;font-weight:600;color:var(--label-primary, #1f2937)}.fly-image-upload__crop-body{max-height:60vh;overflow:hidden;background:var(--fill-tertiary, #f3f4f6)}.fly-image-upload__crop-body img{display:block;max-width:100%}.fly-image-upload__crop-footer{display:flex;justify-content:flex-end;gap:10px;padding:14px 20px;border-top:1px solid var(--separator-primary, #e5e7eb);background:var(--fill-quaternary, transparent)}html.dark-theme .fly-image-upload__crop-modal{background:var(--bg-primary, #1c1c1e);border:1px solid var(--separator-primary, rgba(255,255,255,.1))}html.dark-theme .fly-image-upload__crop-body{background:var(--fill-tertiary, #2c2c2e)}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
2774
2873
|
}
|
|
2775
2874
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyImageUploadComponent, decorators: [{
|
|
2776
2875
|
type: Component,
|
|
@@ -2824,7 +2923,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
2824
2923
|
</div>
|
|
2825
2924
|
}
|
|
2826
2925
|
</div>
|
|
2827
|
-
`, styles: [".cropper-container{-webkit-touch-callout:none;direction:ltr;font-size:0;line-height:0;position:relative;touch-action:none;-webkit-user-select:none;user-select:none}.cropper-container img{backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{inset:0;position:absolute}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:#3399ffbf;overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:\" \";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}.cropper-invisible{opacity:0}.cropper-bg{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC)}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}\n", ".fly-image-upload{display:flex;flex-direction:column;gap:6px}.fly-image-upload__hidden{display:none}.fly-image-upload__dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:24px;border:2px dashed var(--separator-primary, #e5e7eb);border-radius:12px;background:var(--fill-quaternary, #f9fafb);cursor:pointer;transition:border-color .15s,background .15s;min-height:120px}.fly-image-upload__dropzone:hover{border-color:var(--accent-primary, #0071e3);background:var(--fill-tertiary, #f3f4f6)}.fly-image-upload__icon{font-size:28px;color:var(--label-tertiary, #9ca3af)}.fly-image-upload__label{font-size:13px;color:var(--label-secondary, #6b7280)}.fly-image-upload__hint{font-size:11px;color:var(--label-tertiary, #9ca3af)}.fly-image-upload__preview{position:relative;border-radius:12px;overflow:hidden}.fly-image-upload__img{display:block;width:100%;max-height:280px;object-fit:cover;border-radius:12px}.fly-image-upload__overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:12px;background:#00000059;opacity:0;transition:opacity .15s}.fly-image-upload__preview:hover .fly-image-upload__overlay{opacity:1}.fly-image-upload__action-btn{width:36px;height:36px;border-radius:50%;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;background:#ffffffe6;color:var(--label-primary, #1f2937);font-size:14px;transition:background .15s}.fly-image-upload__action-btn:hover{background:#fff}.fly-image-upload__action-btn--danger:hover{background:#fee2e2;color:#dc2626}.fly-image-upload__error{font-size:12px;color:var(--system-red, #ef4444)}.fly-image-upload__crop-backdrop{position:fixed;inset:0;z-index:10000;display:flex;align-items:center;justify-content:center;background:#0009;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.fly-image-upload__crop-modal{background:var(--bg-primary, #fff);color:var(--label-primary, #1f2937);border-radius:16px;overflow:hidden;max-width:700px;width:90vw;box-shadow:0 20px 60px #0006;border:1px solid var(--separator-primary, rgba(0,0,0,.1))}.fly-image-upload__crop-header{padding:16px 20px;border-bottom:1px solid var(--separator-primary, #e5e7eb);background:var(--fill-quaternary, transparent)}.fly-image-upload__crop-header h3{margin:0;font-size:16px;font-weight:600;color:var(--label-primary, #1f2937)}.fly-image-upload__crop-body{max-height:60vh;overflow:hidden;background:var(--fill-tertiary, #f3f4f6)}.fly-image-upload__crop-body img{display:block;max-width:100%}.fly-image-upload__crop-footer{display:flex;justify-content:flex-end;gap:10px;padding:14px 20px;border-top:1px solid var(--separator-primary, #e5e7eb);background:var(--fill-quaternary, transparent)}html.
|
|
2926
|
+
`, styles: [".cropper-container{-webkit-touch-callout:none;direction:ltr;font-size:0;line-height:0;position:relative;touch-action:none;-webkit-user-select:none;user-select:none}.cropper-container img{backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{inset:0;position:absolute}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:#3399ffbf;overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:\" \";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}.cropper-invisible{opacity:0}.cropper-bg{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC)}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}\n", ".fly-image-upload{display:flex;flex-direction:column;gap:6px}.fly-image-upload__hidden{display:none}.fly-image-upload__dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:24px;border:2px dashed var(--separator-primary, #e5e7eb);border-radius:12px;background:var(--fill-quaternary, #f9fafb);cursor:pointer;transition:border-color .15s,background .15s;min-height:120px}.fly-image-upload__dropzone:hover{border-color:var(--accent-primary, #0071e3);background:var(--fill-tertiary, #f3f4f6)}.fly-image-upload__icon{font-size:28px;color:var(--label-tertiary, #9ca3af)}.fly-image-upload__label{font-size:13px;color:var(--label-secondary, #6b7280)}.fly-image-upload__hint{font-size:11px;color:var(--label-tertiary, #9ca3af)}.fly-image-upload__preview{position:relative;border-radius:12px;overflow:hidden}.fly-image-upload__img{display:block;width:100%;max-height:280px;object-fit:cover;border-radius:12px}.fly-image-upload__overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:12px;background:#00000059;opacity:0;transition:opacity .15s}.fly-image-upload__preview:hover .fly-image-upload__overlay{opacity:1}.fly-image-upload__action-btn{width:36px;height:36px;border-radius:50%;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;background:#ffffffe6;color:var(--label-primary, #1f2937);font-size:14px;transition:background .15s}.fly-image-upload__action-btn:hover{background:#fff}.fly-image-upload__action-btn--danger:hover{background:#fee2e2;color:#dc2626}.fly-image-upload__error{font-size:12px;color:var(--system-red, #ef4444)}.fly-image-upload__crop-backdrop{position:fixed;inset:0;z-index:10000;display:flex;align-items:center;justify-content:center;background:#0009;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.fly-image-upload__crop-modal{background:var(--bg-primary, #fff);color:var(--label-primary, #1f2937);border-radius:16px;overflow:hidden;max-width:700px;width:90vw;box-shadow:0 20px 60px #0006;border:1px solid var(--separator-primary, rgba(0,0,0,.1))}.fly-image-upload__crop-header{padding:16px 20px;border-bottom:1px solid var(--separator-primary, #e5e7eb);background:var(--fill-quaternary, transparent)}.fly-image-upload__crop-header h3{margin:0;font-size:16px;font-weight:600;color:var(--label-primary, #1f2937)}.fly-image-upload__crop-body{max-height:60vh;overflow:hidden;background:var(--fill-tertiary, #f3f4f6)}.fly-image-upload__crop-body img{display:block;max-width:100%}.fly-image-upload__crop-footer{display:flex;justify-content:flex-end;gap:10px;padding:14px 20px;border-top:1px solid var(--separator-primary, #e5e7eb);background:var(--fill-quaternary, transparent)}html.dark-theme .fly-image-upload__crop-modal{background:var(--bg-primary, #1c1c1e);border:1px solid var(--separator-primary, rgba(255,255,255,.1))}html.dark-theme .fly-image-upload__crop-body{background:var(--fill-tertiary, #2c2c2e)}\n"] }]
|
|
2828
2927
|
}], ctorParameters: () => [], propDecorators: { aspectRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "aspectRatio", required: false }] }], maxSizeBytes: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSizeBytes", required: false }] }], currentImageId: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentImageId", required: false }] }], sourceApp: [{ type: i0.Input, args: [{ isSignal: true, alias: "sourceApp", required: false }] }], sourceEntityType: [{ type: i0.Input, args: [{ isSignal: true, alias: "sourceEntityType", required: false }] }], sourceEntityId: [{ type: i0.Input, args: [{ isSignal: true, alias: "sourceEntityId", required: false }] }], uploaded: [{ type: i0.Output, args: ["uploaded"] }], removed: [{ type: i0.Output, args: ["removed"] }], fileInput: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }], cropImage: [{ type: i0.ViewChild, args: ['cropImage', { isSignal: true }] }] } });
|
|
2829
2928
|
|
|
2830
2929
|
const FILE_ICONS = {
|