@mohamedatia/fly-design-system 2.7.3 → 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 +207 -105
- 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 +65 -47
- 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,33 +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.
|
|
770
|
+
*
|
|
771
|
+
* Fallback (browsers without `link[layer]` support):
|
|
772
|
+
* `<style>@import url("…") layer(remote);</style>`
|
|
771
773
|
*
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
* - Option C (`<link layer="remote">`): the `layer` attribute on `<link>` is
|
|
777
|
-
* not yet shipped in Firefox (as of 2025-05). Chromium 99+ and Safari 16.4+
|
|
778
|
-
* support it, but Firefox is still behind, making it non-portable today.
|
|
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.
|
|
779
778
|
*
|
|
780
|
-
*
|
|
781
|
-
*
|
|
782
|
-
* engines schedule it as a discoverable resource from the inline `<style>` element.
|
|
783
|
-
* The performance characteristic is equivalent to the old `<link>` approach.
|
|
779
|
+
* Both code paths attach `data-fly-app` and `data-fly-href` attributes to the
|
|
780
|
+
* injected element so idempotency checks work uniformly.
|
|
784
781
|
*
|
|
785
782
|
* Layer order declaration
|
|
786
783
|
* -----------------------
|
|
787
|
-
* A one-time `<style data-fly-layers>` element is prepended to `<head>`
|
|
788
|
-
*
|
|
784
|
+
* A one-time `<style data-fly-layers>` element is prepended to `<head>` at module
|
|
785
|
+
* init. It establishes the canonical layer order:
|
|
789
786
|
* reset → designsystem → shell → remote → overrides
|
|
790
787
|
* This guarantees that even if individual `@layer` blocks are injected in any
|
|
791
788
|
* order at runtime, the cascade priority is always deterministic.
|
|
792
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
|
+
*
|
|
793
803
|
* Unload decision
|
|
794
804
|
* ---------------
|
|
795
805
|
* `unloadRemoteStyles` is exported for symmetry but should NOT be called on
|
|
@@ -800,63 +810,69 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
800
810
|
*
|
|
801
811
|
* Caveats
|
|
802
812
|
* -------
|
|
803
|
-
* - CORS: the remote's dev/prod server must serve `index.html` with a
|
|
804
|
-
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
* - Race on rapid mount/unmount: if `loadRemoteStyles` is called a second time
|
|
809
|
-
*
|
|
810
|
-
*
|
|
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.
|
|
811
821
|
* - Angular hashing: Angular's production build hashes the stylesheet filename.
|
|
812
|
-
* DOMParser picks the first `<link rel="stylesheet">` in `<head>`, which is
|
|
813
|
-
*
|
|
814
|
-
*
|
|
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.
|
|
815
825
|
*/
|
|
816
|
-
/** Per-appId cache of the resolved stylesheet href (or `null` if not found). */
|
|
817
|
-
const _resolvedHref = new Map();
|
|
818
|
-
/** In-flight fetch promises keyed by appId — prevents duplicate fetches. */
|
|
819
|
-
const _inFlight = new Map();
|
|
820
826
|
/**
|
|
821
|
-
*
|
|
822
|
-
*
|
|
823
|
-
*
|
|
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.
|
|
824
830
|
*/
|
|
825
|
-
|
|
831
|
+
const _linkLayerSupported = typeof HTMLLinkElement !== 'undefined' && 'layer' in HTMLLinkElement.prototype;
|
|
826
832
|
/**
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
* reset → designsystem → shell → remote → overrides
|
|
831
|
-
*
|
|
832
|
-
* Any subsequent `@layer X { … }` block lands in the already-established slot
|
|
833
|
-
* regardless of injection order, so late-arriving remote stylesheets never
|
|
834
|
-
* "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.
|
|
835
835
|
*/
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
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
|
|
841
847
|
if (document.head.querySelector('style[data-fly-layers]'))
|
|
842
848
|
return;
|
|
843
849
|
const style = document.createElement('style');
|
|
844
850
|
style.setAttribute('data-fly-layers', '');
|
|
845
851
|
style.textContent = '@layer reset, designsystem, shell, remote, overrides;';
|
|
852
|
+
if (_cspNonce)
|
|
853
|
+
style.nonce = _cspNonce;
|
|
846
854
|
// Prepend so this always precedes any other layer-bearing <style> blocks.
|
|
847
855
|
document.head.insertBefore(style, document.head.firstChild);
|
|
848
|
-
}
|
|
856
|
+
})();
|
|
857
|
+
// ---------------------------------------------------------------------------
|
|
858
|
+
// Internal helpers
|
|
859
|
+
// ---------------------------------------------------------------------------
|
|
849
860
|
/**
|
|
850
861
|
* Discovers the hashed stylesheet href from the remote's `index.html`.
|
|
851
|
-
*
|
|
862
|
+
* Applies a 5-second AbortController timeout. Clears `_inFlight` on abort so
|
|
863
|
+
* a retry is possible. Returns `null` on any failure.
|
|
852
864
|
*/
|
|
853
|
-
async function _discoverStylesheetHref(remoteBaseUrl) {
|
|
865
|
+
async function _discoverStylesheetHref(appId, remoteBaseUrl) {
|
|
854
866
|
const indexUrl = remoteBaseUrl.replace(/\/$/, '') + '/index.html';
|
|
867
|
+
const controller = new AbortController();
|
|
868
|
+
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
855
869
|
try {
|
|
856
870
|
const res = await fetch(indexUrl, {
|
|
857
871
|
credentials: 'omit',
|
|
858
872
|
cache: 'default',
|
|
873
|
+
signal: controller.signal,
|
|
859
874
|
});
|
|
875
|
+
clearTimeout(timeoutId);
|
|
860
876
|
if (!res.ok)
|
|
861
877
|
return null;
|
|
862
878
|
const html = await res.text();
|
|
@@ -865,85 +881,163 @@ async function _discoverStylesheetHref(remoteBaseUrl) {
|
|
|
865
881
|
const link = doc.head.querySelector('link[rel="stylesheet"]');
|
|
866
882
|
if (!link?.href)
|
|
867
883
|
return null;
|
|
868
|
-
// `link.href` from DOMParser
|
|
869
|
-
// which defaults to `about:blank`. We get the raw `href` attribute instead.
|
|
884
|
+
// `link.href` from DOMParser resolves against `about:blank`. Use raw attribute.
|
|
870
885
|
const rawHref = link.getAttribute('href') ?? '';
|
|
871
886
|
if (!rawHref)
|
|
872
887
|
return null;
|
|
873
|
-
//
|
|
874
|
-
//
|
|
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;
|
|
875
900
|
if (rawHref.startsWith('http://') || rawHref.startsWith('https://') || rawHref.startsWith('//')) {
|
|
876
|
-
|
|
901
|
+
resolvedHref = rawHref;
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
resolvedHref = remoteBaseUrl.replace(/\/$/, '') + '/' + rawHref.replace(/^\//, '');
|
|
905
|
+
}
|
|
906
|
+
return _validateHref(resolvedHref, remoteBaseUrl);
|
|
907
|
+
}
|
|
908
|
+
catch {
|
|
909
|
+
clearTimeout(timeoutId);
|
|
910
|
+
// On abort or network error, clear in-flight so a retry is possible.
|
|
911
|
+
_inFlight.delete(appId);
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
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;
|
|
877
947
|
}
|
|
878
|
-
return remoteBaseUrl.replace(/\/$/, '') + '/' + rawHref.replace(/^\//, '');
|
|
879
948
|
}
|
|
880
949
|
catch {
|
|
950
|
+
console.warn(`[FlyOS] loadRemoteStyles: could not parse href origin for "${href}"`);
|
|
881
951
|
return null;
|
|
882
952
|
}
|
|
953
|
+
return href;
|
|
883
954
|
}
|
|
955
|
+
// ---------------------------------------------------------------------------
|
|
956
|
+
// Public API
|
|
957
|
+
// ---------------------------------------------------------------------------
|
|
884
958
|
/**
|
|
885
959
|
* Injects the remote's stylesheet into `document.head`.
|
|
886
960
|
*
|
|
887
|
-
* @param appId
|
|
961
|
+
* @param appId - Stable identifier for the remote app (matches `DesktopApp.id`).
|
|
888
962
|
* @param remoteBaseUrl - Base URL of the remote, e.g. `https://circles.example.com`
|
|
889
963
|
* or `http://localhost:7202`. Must NOT include `/remoteEntry.json`.
|
|
890
964
|
*
|
|
891
965
|
* The call is idempotent:
|
|
892
|
-
* - If
|
|
893
|
-
* 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.
|
|
894
968
|
* - If the href differs (hot upgrade) → existing element is replaced.
|
|
895
969
|
* - If no stylesheet is found in `index.html` → no-op (logged as a warning).
|
|
896
970
|
*
|
|
897
|
-
* 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):
|
|
898
976
|
* <style data-fly-app="<appId>" data-fly-href="<href>">
|
|
899
|
-
* @
|
|
977
|
+
* @import url("<href>") layer(remote);
|
|
900
978
|
* </style>
|
|
901
979
|
*
|
|
902
980
|
* The `data-fly-href` attribute stores the discovered href separately from the
|
|
903
|
-
*
|
|
981
|
+
* element content so idempotency checks can compare the URL without parsing CSS.
|
|
904
982
|
*/
|
|
905
983
|
async function loadRemoteStyles(appId, remoteBaseUrl) {
|
|
906
984
|
if (typeof document === 'undefined')
|
|
907
985
|
return; // SSR guard
|
|
908
|
-
// Ensure the canonical layer order is declared before any remote layer is injected.
|
|
909
|
-
_ensureLayerOrder();
|
|
910
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.
|
|
911
990
|
let fetchPromise = _inFlight.get(appId);
|
|
912
991
|
if (!fetchPromise) {
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
fetchPromise = Promise.resolve(_resolvedHref.get(appId) ?? null);
|
|
916
|
-
}
|
|
917
|
-
else {
|
|
918
|
-
fetchPromise = _discoverStylesheetHref(remoteBaseUrl);
|
|
919
|
-
_inFlight.set(appId, fetchPromise);
|
|
920
|
-
}
|
|
992
|
+
fetchPromise = _discoverStylesheetHref(appId, remoteBaseUrl);
|
|
993
|
+
_inFlight.set(appId, fetchPromise);
|
|
921
994
|
}
|
|
922
995
|
const href = await fetchPromise;
|
|
923
996
|
_inFlight.delete(appId);
|
|
924
|
-
_resolvedHref.set(appId, href);
|
|
925
997
|
if (!href) {
|
|
926
998
|
console.warn(`[FlyOS] loadRemoteStyles: no stylesheet found in ${remoteBaseUrl}/index.html for appId="${appId}"`);
|
|
927
999
|
return;
|
|
928
1000
|
}
|
|
929
|
-
|
|
930
|
-
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}"]`);
|
|
931
1004
|
if (existing) {
|
|
932
1005
|
if (existing.getAttribute('data-fly-href') === href)
|
|
933
1006
|
return; // identical — no-op
|
|
934
|
-
// Hot upgrade:
|
|
1007
|
+
// Hot upgrade: remove old element (could be <link> or <style>) atomically.
|
|
935
1008
|
existing.remove();
|
|
936
1009
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
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
|
+
}
|
|
944
1037
|
}
|
|
945
1038
|
/**
|
|
946
|
-
* 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.
|
|
947
1041
|
*
|
|
948
1042
|
* NOTE: Prefer NOT calling this on normal window close — keeping styles loaded
|
|
949
1043
|
* prevents FOUC flicker when the user reopens the same app. Call only if you
|
|
@@ -952,9 +1046,17 @@ async function loadRemoteStyles(appId, remoteBaseUrl) {
|
|
|
952
1046
|
function unloadRemoteStyles(appId) {
|
|
953
1047
|
if (typeof document === 'undefined')
|
|
954
1048
|
return; // SSR guard
|
|
955
|
-
const
|
|
956
|
-
|
|
957
|
-
|
|
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);
|
|
958
1060
|
}
|
|
959
1061
|
|
|
960
1062
|
/**
|
|
@@ -2767,7 +2869,7 @@ class FlyImageUploadComponent {
|
|
|
2767
2869
|
</div>
|
|
2768
2870
|
}
|
|
2769
2871
|
</div>
|
|
2770
|
-
`, 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 });
|
|
2771
2873
|
}
|
|
2772
2874
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyImageUploadComponent, decorators: [{
|
|
2773
2875
|
type: Component,
|
|
@@ -2821,7 +2923,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
2821
2923
|
</div>
|
|
2822
2924
|
}
|
|
2823
2925
|
</div>
|
|
2824
|
-
`, 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"] }]
|
|
2825
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 }] }] } });
|
|
2826
2928
|
|
|
2827
2929
|
const FILE_ICONS = {
|