@mohamedatia/fly-design-system 2.7.2 → 2.7.4
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 +94 -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 +46 -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,34 @@ 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 — `@import url("...") layer(remote)`
|
|
767
|
+
* --------------------------------------------------------
|
|
768
|
+
* Remote stylesheets are injected via `@import url("…") layer(remote)` (CSS
|
|
769
|
+
* Cascade Level 5). This keeps remote styles in a named cascade layer so the
|
|
770
|
+
* shell can reliably override them via `@layer overrides`.
|
|
771
|
+
*
|
|
772
|
+
* The block-form `@layer remote { @import url("…"); }` is INVALID CSS — the
|
|
773
|
+
* spec forbids `@import` inside any block at-rule. Browsers silently drop such
|
|
774
|
+
* rules, causing remote stylesheets to never load. The correct syntax is the
|
|
775
|
+
* `layer()` modifier on a top-level `@import`.
|
|
776
|
+
*
|
|
777
|
+
* Browser support: Chrome 99+, Firefox 97+, Safari 15.4+ — all current engines.
|
|
778
|
+
*
|
|
779
|
+
* Alternative approaches rejected:
|
|
780
|
+
* - Fetch+inline: relative `url(...)` paths inside the remote CSS would
|
|
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.
|
|
785
|
+
*
|
|
786
|
+
* Layer order declaration
|
|
787
|
+
* -----------------------
|
|
788
|
+
* A one-time `<style data-fly-layers>` element is prepended to `<head>` the first
|
|
789
|
+
* time any remote style is loaded. It establishes the canonical layer order:
|
|
790
|
+
* reset → designsystem → shell → remote → overrides
|
|
791
|
+
* This guarantees that even if individual `@layer` blocks are injected in any
|
|
792
|
+
* order at runtime, the cascade priority is always deterministic.
|
|
793
|
+
*
|
|
764
794
|
* Unload decision
|
|
765
795
|
* ---------------
|
|
766
796
|
* `unloadRemoteStyles` is exported for symmetry but should NOT be called on
|
|
@@ -774,7 +804,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
774
804
|
* - CORS: the remote's dev/prod server must serve `index.html` with a
|
|
775
805
|
* permissive `Access-Control-Allow-Origin` header (or be same-origin via
|
|
776
806
|
* the YARP gateway). The fetch uses `credentials: 'omit'` to avoid
|
|
777
|
-
* credential-carrying preflights.
|
|
807
|
+
* credential-carrying preflights. The CSS file itself must also be CORS-
|
|
808
|
+
* accessible since `@import` inside a `<style>` is subject to CORS checks.
|
|
778
809
|
* - Race on rapid mount/unmount: if `loadRemoteStyles` is called a second time
|
|
779
810
|
* for the same appId while the first fetch is still in-flight, the second
|
|
780
811
|
* call joins the same in-flight Promise (idempotency guard at fetch level).
|
|
@@ -787,6 +818,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
787
818
|
const _resolvedHref = new Map();
|
|
788
819
|
/** In-flight fetch promises keyed by appId — prevents duplicate fetches. */
|
|
789
820
|
const _inFlight = new Map();
|
|
821
|
+
/**
|
|
822
|
+
* Whether the canonical layer-order declaration has already been injected.
|
|
823
|
+
* Module-level so it survives across multiple `loadRemoteStyles` calls within
|
|
824
|
+
* the same shell session but is reset on a full page reload.
|
|
825
|
+
*/
|
|
826
|
+
let _layersDeclared = false;
|
|
827
|
+
/**
|
|
828
|
+
* Injects a single `<style data-fly-layers>` element at the top of `<head>`
|
|
829
|
+
* that establishes the canonical cascade-layer priority order for the shell:
|
|
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.
|
|
836
|
+
*/
|
|
837
|
+
function _ensureLayerOrder() {
|
|
838
|
+
if (_layersDeclared)
|
|
839
|
+
return;
|
|
840
|
+
_layersDeclared = true;
|
|
841
|
+
// Guard against duplicate elements in the DOM (e.g. hot-module-reload edge cases).
|
|
842
|
+
if (document.head.querySelector('style[data-fly-layers]'))
|
|
843
|
+
return;
|
|
844
|
+
const style = document.createElement('style');
|
|
845
|
+
style.setAttribute('data-fly-layers', '');
|
|
846
|
+
style.textContent = '@layer reset, designsystem, shell, remote, overrides;';
|
|
847
|
+
// Prepend so this always precedes any other layer-bearing <style> blocks.
|
|
848
|
+
document.head.insertBefore(style, document.head.firstChild);
|
|
849
|
+
}
|
|
790
850
|
/**
|
|
791
851
|
* Discovers the hashed stylesheet href from the remote's `index.html`.
|
|
792
852
|
* Returns `null` if no stylesheet `<link>` is found or the fetch fails.
|
|
@@ -830,13 +890,24 @@ async function _discoverStylesheetHref(remoteBaseUrl) {
|
|
|
830
890
|
* or `http://localhost:7202`. Must NOT include `/remoteEntry.json`.
|
|
831
891
|
*
|
|
832
892
|
* The call is idempotent:
|
|
833
|
-
* - If a `<
|
|
834
|
-
*
|
|
893
|
+
* - If a `<style data-fly-app="appId">` whose content references the same href
|
|
894
|
+
* already exists → no-op.
|
|
895
|
+
* - If the href differs (hot upgrade) → existing element is replaced.
|
|
835
896
|
* - If no stylesheet is found in `index.html` → no-op (logged as a warning).
|
|
897
|
+
*
|
|
898
|
+
* Injection shape:
|
|
899
|
+
* <style data-fly-app="<appId>" data-fly-href="<href>">
|
|
900
|
+
* @import url("<href>") layer(remote);
|
|
901
|
+
* </style>
|
|
902
|
+
*
|
|
903
|
+
* The `data-fly-href` attribute stores the discovered href separately from the
|
|
904
|
+
* style content so idempotency checks can compare the URL without parsing CSS.
|
|
836
905
|
*/
|
|
837
906
|
async function loadRemoteStyles(appId, remoteBaseUrl) {
|
|
838
907
|
if (typeof document === 'undefined')
|
|
839
908
|
return; // SSR guard
|
|
909
|
+
// Ensure the canonical layer order is declared before any remote layer is injected.
|
|
910
|
+
_ensureLayerOrder();
|
|
840
911
|
// Kick off or join an in-flight fetch for this appId.
|
|
841
912
|
let fetchPromise = _inFlight.get(appId);
|
|
842
913
|
if (!fetchPromise) {
|
|
@@ -856,19 +927,23 @@ async function loadRemoteStyles(appId, remoteBaseUrl) {
|
|
|
856
927
|
console.warn(`[FlyOS] loadRemoteStyles: no stylesheet found in ${remoteBaseUrl}/index.html for appId="${appId}"`);
|
|
857
928
|
return;
|
|
858
929
|
}
|
|
859
|
-
const
|
|
930
|
+
const selector = `style[data-fly-app="${CSS.escape(appId)}"]`;
|
|
931
|
+
const existing = document.head.querySelector(selector);
|
|
860
932
|
if (existing) {
|
|
861
|
-
if (existing.getAttribute('href') === href)
|
|
933
|
+
if (existing.getAttribute('data-fly-href') === href)
|
|
862
934
|
return; // identical — no-op
|
|
863
|
-
// Hot upgrade: replace
|
|
864
|
-
existing.
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
935
|
+
// Hot upgrade: replace the entire element so the @import URL updates atomically.
|
|
936
|
+
existing.remove();
|
|
937
|
+
}
|
|
938
|
+
// CSS Cascade Level 5: `@import url("…") layer(remote)` is the only valid
|
|
939
|
+
// way to place an @import inside a named cascade layer. Block-form
|
|
940
|
+
// `@layer remote { @import … }` is invalid and silently dropped by browsers.
|
|
941
|
+
// See module JSDoc for full rationale.
|
|
942
|
+
const style = document.createElement('style');
|
|
943
|
+
style.setAttribute('data-fly-app', appId);
|
|
944
|
+
style.setAttribute('data-fly-href', href);
|
|
945
|
+
style.textContent = `@import url("${href}") layer(remote);`;
|
|
946
|
+
document.head.appendChild(style);
|
|
872
947
|
}
|
|
873
948
|
/**
|
|
874
949
|
* Removes the injected stylesheet for `appId` from `document.head`.
|
|
@@ -880,8 +955,8 @@ async function loadRemoteStyles(appId, remoteBaseUrl) {
|
|
|
880
955
|
function unloadRemoteStyles(appId) {
|
|
881
956
|
if (typeof document === 'undefined')
|
|
882
957
|
return; // SSR guard
|
|
883
|
-
const
|
|
884
|
-
|
|
958
|
+
const style = document.head.querySelector(`style[data-fly-app="${CSS.escape(appId)}"]`);
|
|
959
|
+
style?.parentNode?.removeChild(style);
|
|
885
960
|
_resolvedHref.delete(appId);
|
|
886
961
|
}
|
|
887
962
|
|