@mohamedatia/fly-design-system 2.7.2 → 2.7.3
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 +91 -19
- package/fesm2022/mohamedatia-fly-design-system.mjs.map +1 -1
- package/package.json +1 -1
- package/scss/_business-app-buttons.scss +4 -0
- package/scss/_theme-auto.scss +2 -0
- package/scss/_theme-dark.scss +2 -0
- package/scss/_theme-light.scss +2 -0
- package/scss/_theme-spatial.scss +2 -0
- package/scss/_tokens.scss +2 -0
- package/types/mohamedatia-fly-design-system.d.ts +45 -6
- package/types/mohamedatia-fly-design-system.d.ts.map +1 -1
- package/scss/apps.scss +0 -886
|
@@ -749,9 +749,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
749
749
|
* 3. Parses the HTML with DOMParser to extract the first
|
|
750
750
|
* `<link rel="stylesheet">` — the hashed production stylesheet
|
|
751
751
|
* (e.g. `styles-ABC123.css`). This is Strategy (b): index.html discovery.
|
|
752
|
-
* 4. Injects `<
|
|
753
|
-
*
|
|
754
|
-
*
|
|
752
|
+
* 4. Injects an inline `<style data-fly-app="<appId>">` block containing
|
|
753
|
+
* `@layer remote { @import url("..."); }` into `document.head`.
|
|
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.
|
|
755
757
|
*
|
|
756
758
|
* Why strategy (b)?
|
|
757
759
|
* -----------------
|
|
@@ -761,6 +763,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
761
763
|
* (c) `stylesUrl` in manifest/remoteEntry.json — cleanest long-term; requires
|
|
762
764
|
* protocol change to every remote's CI pipeline (out of scope for Wave A1).
|
|
763
765
|
*
|
|
766
|
+
* CSS Cascade Layer — Option A (`@import` inside inline `<style>`)
|
|
767
|
+
* ----------------------------------------------------------------
|
|
768
|
+
* Remote stylesheets are injected inside `@layer remote { @import url("..."); }`
|
|
769
|
+
* rather than as bare `<link rel="stylesheet">`. This keeps remote styles in a
|
|
770
|
+
* named cascade layer so the shell can reliably override them via `@layer overrides`.
|
|
771
|
+
*
|
|
772
|
+
* Option A was chosen over:
|
|
773
|
+
* - Option B (fetch+inline): relative `url(...)` paths inside the remote CSS
|
|
774
|
+
* would resolve against the shell origin instead of the remote origin,
|
|
775
|
+
* breaking fonts and images. Dealbreaker without a full URL-rewrite pass.
|
|
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.
|
|
779
|
+
*
|
|
780
|
+
* Trade-off of Option A: `@import` inside `@layer` delays style application until
|
|
781
|
+
* the remote stylesheet is fetched (same as a plain `<link>`, effectively). Modern
|
|
782
|
+
* engines schedule it as a discoverable resource from the inline `<style>` element.
|
|
783
|
+
* The performance characteristic is equivalent to the old `<link>` approach.
|
|
784
|
+
*
|
|
785
|
+
* Layer order declaration
|
|
786
|
+
* -----------------------
|
|
787
|
+
* A one-time `<style data-fly-layers>` element is prepended to `<head>` the first
|
|
788
|
+
* time any remote style is loaded. It establishes the canonical layer order:
|
|
789
|
+
* reset → designsystem → shell → remote → overrides
|
|
790
|
+
* This guarantees that even if individual `@layer` blocks are injected in any
|
|
791
|
+
* order at runtime, the cascade priority is always deterministic.
|
|
792
|
+
*
|
|
764
793
|
* Unload decision
|
|
765
794
|
* ---------------
|
|
766
795
|
* `unloadRemoteStyles` is exported for symmetry but should NOT be called on
|
|
@@ -774,7 +803,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
774
803
|
* - CORS: the remote's dev/prod server must serve `index.html` with a
|
|
775
804
|
* permissive `Access-Control-Allow-Origin` header (or be same-origin via
|
|
776
805
|
* the YARP gateway). The fetch uses `credentials: 'omit'` to avoid
|
|
777
|
-
* credential-carrying preflights.
|
|
806
|
+
* credential-carrying preflights. The CSS file itself must also be CORS-
|
|
807
|
+
* accessible since `@import` inside a `<style>` is subject to CORS checks.
|
|
778
808
|
* - Race on rapid mount/unmount: if `loadRemoteStyles` is called a second time
|
|
779
809
|
* for the same appId while the first fetch is still in-flight, the second
|
|
780
810
|
* call joins the same in-flight Promise (idempotency guard at fetch level).
|
|
@@ -787,6 +817,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
787
817
|
const _resolvedHref = new Map();
|
|
788
818
|
/** In-flight fetch promises keyed by appId — prevents duplicate fetches. */
|
|
789
819
|
const _inFlight = new Map();
|
|
820
|
+
/**
|
|
821
|
+
* Whether the canonical layer-order declaration has already been injected.
|
|
822
|
+
* Module-level so it survives across multiple `loadRemoteStyles` calls within
|
|
823
|
+
* the same shell session but is reset on a full page reload.
|
|
824
|
+
*/
|
|
825
|
+
let _layersDeclared = false;
|
|
826
|
+
/**
|
|
827
|
+
* Injects a single `<style data-fly-layers>` element at the top of `<head>`
|
|
828
|
+
* that establishes the canonical cascade-layer priority order for the shell:
|
|
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.
|
|
835
|
+
*/
|
|
836
|
+
function _ensureLayerOrder() {
|
|
837
|
+
if (_layersDeclared)
|
|
838
|
+
return;
|
|
839
|
+
_layersDeclared = true;
|
|
840
|
+
// Guard against duplicate elements in the DOM (e.g. hot-module-reload edge cases).
|
|
841
|
+
if (document.head.querySelector('style[data-fly-layers]'))
|
|
842
|
+
return;
|
|
843
|
+
const style = document.createElement('style');
|
|
844
|
+
style.setAttribute('data-fly-layers', '');
|
|
845
|
+
style.textContent = '@layer reset, designsystem, shell, remote, overrides;';
|
|
846
|
+
// Prepend so this always precedes any other layer-bearing <style> blocks.
|
|
847
|
+
document.head.insertBefore(style, document.head.firstChild);
|
|
848
|
+
}
|
|
790
849
|
/**
|
|
791
850
|
* Discovers the hashed stylesheet href from the remote's `index.html`.
|
|
792
851
|
* Returns `null` if no stylesheet `<link>` is found or the fetch fails.
|
|
@@ -830,13 +889,24 @@ async function _discoverStylesheetHref(remoteBaseUrl) {
|
|
|
830
889
|
* or `http://localhost:7202`. Must NOT include `/remoteEntry.json`.
|
|
831
890
|
*
|
|
832
891
|
* The call is idempotent:
|
|
833
|
-
* - If a `<
|
|
834
|
-
*
|
|
892
|
+
* - If a `<style data-fly-app="appId">` whose content references the same href
|
|
893
|
+
* already exists → no-op.
|
|
894
|
+
* - If the href differs (hot upgrade) → existing element is replaced.
|
|
835
895
|
* - If no stylesheet is found in `index.html` → no-op (logged as a warning).
|
|
896
|
+
*
|
|
897
|
+
* Injection shape:
|
|
898
|
+
* <style data-fly-app="<appId>" data-fly-href="<href>">
|
|
899
|
+
* @layer remote { @import url("<href>"); }
|
|
900
|
+
* </style>
|
|
901
|
+
*
|
|
902
|
+
* The `data-fly-href` attribute stores the discovered href separately from the
|
|
903
|
+
* style content so idempotency checks can compare the URL without parsing CSS.
|
|
836
904
|
*/
|
|
837
905
|
async function loadRemoteStyles(appId, remoteBaseUrl) {
|
|
838
906
|
if (typeof document === 'undefined')
|
|
839
907
|
return; // SSR guard
|
|
908
|
+
// Ensure the canonical layer order is declared before any remote layer is injected.
|
|
909
|
+
_ensureLayerOrder();
|
|
840
910
|
// Kick off or join an in-flight fetch for this appId.
|
|
841
911
|
let fetchPromise = _inFlight.get(appId);
|
|
842
912
|
if (!fetchPromise) {
|
|
@@ -856,19 +926,21 @@ async function loadRemoteStyles(appId, remoteBaseUrl) {
|
|
|
856
926
|
console.warn(`[FlyOS] loadRemoteStyles: no stylesheet found in ${remoteBaseUrl}/index.html for appId="${appId}"`);
|
|
857
927
|
return;
|
|
858
928
|
}
|
|
859
|
-
const
|
|
929
|
+
const selector = `style[data-fly-app="${CSS.escape(appId)}"]`;
|
|
930
|
+
const existing = document.head.querySelector(selector);
|
|
860
931
|
if (existing) {
|
|
861
|
-
if (existing.getAttribute('href') === href)
|
|
932
|
+
if (existing.getAttribute('data-fly-href') === href)
|
|
862
933
|
return; // identical — no-op
|
|
863
|
-
// Hot upgrade: replace
|
|
864
|
-
existing.
|
|
865
|
-
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
934
|
+
// Hot upgrade: replace the entire element so the @import URL updates atomically.
|
|
935
|
+
existing.remove();
|
|
936
|
+
}
|
|
937
|
+
// Option A: inline <style> with @layer remote { @import url("..."); }.
|
|
938
|
+
// See module JSDoc for the rationale over Options B and C.
|
|
939
|
+
const style = document.createElement('style');
|
|
940
|
+
style.setAttribute('data-fly-app', appId);
|
|
941
|
+
style.setAttribute('data-fly-href', href);
|
|
942
|
+
style.textContent = `@layer remote { @import url("${href}"); }`;
|
|
943
|
+
document.head.appendChild(style);
|
|
872
944
|
}
|
|
873
945
|
/**
|
|
874
946
|
* Removes the injected stylesheet for `appId` from `document.head`.
|
|
@@ -880,8 +952,8 @@ async function loadRemoteStyles(appId, remoteBaseUrl) {
|
|
|
880
952
|
function unloadRemoteStyles(appId) {
|
|
881
953
|
if (typeof document === 'undefined')
|
|
882
954
|
return; // SSR guard
|
|
883
|
-
const
|
|
884
|
-
|
|
955
|
+
const style = document.head.querySelector(`style[data-fly-app="${CSS.escape(appId)}"]`);
|
|
956
|
+
style?.parentNode?.removeChild(style);
|
|
885
957
|
_resolvedHref.delete(appId);
|
|
886
958
|
}
|
|
887
959
|
|