@flipdish/ui-library 0.37.0 → 0.39.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.
Files changed (87) hide show
  1. package/README.md +9 -9
  2. package/dist/components/__internal__/selectionControl/selectionControl.styles.cjs +22 -0
  3. package/dist/components/__internal__/selectionControl/selectionControl.styles.cjs.map +1 -0
  4. package/dist/components/__internal__/selectionControl/selectionControl.styles.d.ts +17 -0
  5. package/dist/components/__internal__/selectionControl/selectionControl.styles.d.ts.map +1 -0
  6. package/dist/components/__internal__/selectionControl/selectionControl.styles.js +20 -0
  7. package/dist/components/__internal__/selectionControl/selectionControl.styles.js.map +1 -0
  8. package/dist/components/__internal__/selectionControl/selectionControl.utils.d.ts +23 -0
  9. package/dist/components/__internal__/selectionControl/selectionControl.utils.d.ts.map +1 -0
  10. package/dist/components/atoms/Checkbox/index.cjs +3 -16
  11. package/dist/components/atoms/Checkbox/index.cjs.map +1 -1
  12. package/dist/components/atoms/Checkbox/index.d.ts.map +1 -1
  13. package/dist/components/atoms/Checkbox/index.js +3 -16
  14. package/dist/components/atoms/Checkbox/index.js.map +1 -1
  15. package/dist/components/atoms/Toggle/Toggle.styles.cjs +0 -14
  16. package/dist/components/atoms/Toggle/Toggle.styles.cjs.map +1 -1
  17. package/dist/components/atoms/Toggle/Toggle.styles.d.ts +0 -14
  18. package/dist/components/atoms/Toggle/Toggle.styles.d.ts.map +1 -1
  19. package/dist/components/atoms/Toggle/Toggle.styles.js +0 -14
  20. package/dist/components/atoms/Toggle/Toggle.styles.js.map +1 -1
  21. package/dist/components/atoms/Toggle/index.cjs +3 -2
  22. package/dist/components/atoms/Toggle/index.cjs.map +1 -1
  23. package/dist/components/atoms/Toggle/index.d.ts.map +1 -1
  24. package/dist/components/atoms/Toggle/index.js +3 -2
  25. package/dist/components/atoms/Toggle/index.js.map +1 -1
  26. package/dist/components/templates/WidgetLayout/index.cjs +36 -0
  27. package/dist/components/templates/WidgetLayout/index.cjs.map +1 -0
  28. package/dist/components/templates/WidgetLayout/index.d.ts +54 -0
  29. package/dist/components/templates/WidgetLayout/index.d.ts.map +1 -0
  30. package/dist/components/templates/WidgetLayout/index.js +34 -0
  31. package/dist/components/templates/WidgetLayout/index.js.map +1 -0
  32. package/dist/native/components/__internal__/selectionControl/SelectionControlText.d.ts +1 -1
  33. package/dist/native/components/__internal__/selectionControl/SelectionControlText.d.ts.map +1 -1
  34. package/dist/native/components/__internal__/selectionControl/SelectionControlText.js +2 -1
  35. package/dist/native/components/__internal__/selectionControl/SelectionControlText.js.map +1 -1
  36. package/dist/native/components/__internal__/selectionControl/selectionControl.styles.d.ts +17 -0
  37. package/dist/native/components/__internal__/selectionControl/selectionControl.styles.d.ts.map +1 -0
  38. package/dist/native/components/__internal__/selectionControl/selectionControl.styles.js +20 -0
  39. package/dist/native/components/__internal__/selectionControl/selectionControl.styles.js.map +1 -0
  40. package/dist/native/components/__internal__/selectionControl/selectionControl.utils.d.ts +23 -0
  41. package/dist/native/components/__internal__/selectionControl/selectionControl.utils.d.ts.map +1 -0
  42. package/dist/native/components/__internal__/selectionControl/selectionControl.utils.js +20 -0
  43. package/dist/native/components/__internal__/selectionControl/selectionControl.utils.js.map +1 -0
  44. package/dist/native/components/atoms/Checkbox/index.d.ts +31 -9
  45. package/dist/native/components/atoms/Checkbox/index.d.ts.map +1 -0
  46. package/dist/native/components/atoms/Checkbox/index.js +39 -0
  47. package/dist/native/components/atoms/Checkbox/index.js.map +1 -0
  48. package/dist/native/components/atoms/Toggle/Toggle.styles.d.ts +0 -14
  49. package/dist/native/components/atoms/Toggle/Toggle.styles.d.ts.map +1 -1
  50. package/dist/native/components/atoms/Toggle/Toggle.styles.js +0 -14
  51. package/dist/native/components/atoms/Toggle/Toggle.styles.js.map +1 -1
  52. package/dist/native/components/atoms/Toggle/index.d.ts.map +1 -1
  53. package/dist/native/components/atoms/Toggle/index.js +3 -1
  54. package/dist/native/components/atoms/Toggle/index.js.map +1 -1
  55. package/dist/native/components/templates/WidgetLayout/index.d.ts +12 -0
  56. package/dist/providers/PortalContainer/index.d.ts +1 -1
  57. package/dist/providers/ThemeProvider/index.cjs +36 -14
  58. package/dist/providers/ThemeProvider/index.cjs.map +1 -1
  59. package/dist/providers/ThemeProvider/index.d.ts +19 -5
  60. package/dist/providers/ThemeProvider/index.d.ts.map +1 -1
  61. package/dist/providers/ThemeProvider/index.js +36 -14
  62. package/dist/providers/ThemeProvider/index.js.map +1 -1
  63. package/dist/providers/UIProvider/hooks/usePortalAnchor.cjs +137 -0
  64. package/dist/providers/UIProvider/hooks/usePortalAnchor.cjs.map +1 -0
  65. package/dist/providers/UIProvider/hooks/usePortalAnchor.d.ts +60 -0
  66. package/dist/providers/UIProvider/hooks/usePortalAnchor.d.ts.map +1 -0
  67. package/dist/providers/UIProvider/hooks/usePortalAnchor.js +131 -0
  68. package/dist/providers/UIProvider/hooks/usePortalAnchor.js.map +1 -0
  69. package/dist/providers/UIProvider/index.cjs +7 -64
  70. package/dist/providers/UIProvider/index.cjs.map +1 -1
  71. package/dist/providers/UIProvider/index.d.ts +16 -11
  72. package/dist/providers/UIProvider/index.d.ts.map +1 -1
  73. package/dist/providers/UIProvider/index.js +7 -64
  74. package/dist/providers/UIProvider/index.js.map +1 -1
  75. package/dist/utilities/cssScopeUtils/index.cjs +22 -11
  76. package/dist/utilities/cssScopeUtils/index.cjs.map +1 -1
  77. package/dist/utilities/cssScopeUtils/index.d.ts +20 -9
  78. package/dist/utilities/cssScopeUtils/index.d.ts.map +1 -1
  79. package/dist/utilities/cssScopeUtils/index.js +22 -11
  80. package/dist/utilities/cssScopeUtils/index.js.map +1 -1
  81. package/dist/utilities/renderUtils/index.cjs +36 -7
  82. package/dist/utilities/renderUtils/index.cjs.map +1 -1
  83. package/dist/utilities/renderUtils/index.d.ts +23 -2
  84. package/dist/utilities/renderUtils/index.d.ts.map +1 -1
  85. package/dist/utilities/renderUtils/index.js +35 -8
  86. package/dist/utilities/renderUtils/index.js.map +1 -1
  87. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/providers/ThemeProvider/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":";;;AAcA,MAAM,YAAY,GAAG,aAAa,CAA+B,SAAS,CAAC;AAE3E;;;;;AAKG;AACI,MAAM,QAAQ,GAAG,MAAuB;AAC7C,IAAA,MAAM,OAAO,GAAG,UAAU,CAAC,YAAY,CAAC;AAExC,IAAA,IAAI,OAAO,KAAK,SAAS,EAAE;AACzB,QAAA,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC;IACjE;AAEA,IAAA,OAAO,OAAO;AAChB;AAEA,MAAM,uBAAuB,GAAG,MAAuB;IACrD,IAAI,OAAO,MAAM,KAAK,WAAW;AAAE,QAAA,OAAO,OAAO;AACjD,IAAA,OAAO,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,GAAG,MAAM,GAAG,OAAO;AACrF,CAAC;AAqBD;;;;;;AAMG;AACI,MAAM,aAAa,GAAG,CAAC,EAAE,QAAQ,EAAE,YAAY,GAAG,QAAQ,EAAE,UAAU,GAAG,UAAU,EAAE,iBAAiB,EAAsB,KAAI;IACrI,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAQ,MAAK;AAC7C,QAAA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;YACjC,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,UAAU,CAAiB;YACnE,OAAO,UAAU,IAAI,YAAY;QACnC;AACA,QAAA,OAAO,YAAY;AACrB,IAAA,CAAC,CAAC;IAEF,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAAmB,uBAAuB,CAAC;IACnG,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAqB,IAAI,CAAC;AAEpE,IAAA,MAAM,WAAW,GAAU,iBAAiB,IAAI,KAAK;AACrD,IAAA,MAAM,YAAY,GAAqB,WAAW,KAAK,QAAQ,GAAG,gBAAgB,GAAG,WAAW;IAEhG,SAAS,CAAC,MAAK;QACb,QAAQ,CAAC,eAAe,CAAC,YAAY,CAAC,YAAY,EAAE,YAAY,CAAC;;QAGjE,IAAI,CAAC,iBAAiB,EAAE;AACtB,YAAA,IAAI,WAAW,KAAK,QAAQ,EAAE;AAC5B,gBAAA,YAAY,CAAC,UAAU,CAAC,UAAU,CAAC;YACrC;iBAAO;AACL,gBAAA,YAAY,CAAC,OAAO,CAAC,UAAU,EAAE,WAAW,CAAC;YAC/C;QACF;QAEA,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC;AACpE,QAAA,MAAM,YAAY,GAAG,MAAM,mBAAmB,CAAC,UAAU,CAAC,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AACrF,QAAA,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC;QACnD,OAAO,MAAM,UAAU,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC;IACrE,CAAC,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,UAAU,CAAC,CAAC;AAE9D,IAAA,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AAEvI,IAAA,QACEA,GAAA,CAAC,YAAY,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,YAAY,EAAA,QAAA,EACxCA,GAAA,CAAA,KAAA,EAAA,EAAK,GAAG,EAAE,YAAY,EAAA,YAAA,EAAc,YAAY,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAA,QAAA,EAC7E,QAAQ,EAAA,CACL,EAAA,CACgB;AAE5B;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/providers/ThemeProvider/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+
5
+ /** Fallback for the `body` anchor's stacking level when the `--z-index-overlay-root` token is not in scope. */
6
+ const OVERLAY_ROOT_Z_INDEX_FALLBACK = 1300;
7
+ /**
8
+ * Reads the design system's overlay stacking tier. Library overlays are `z-50`, which loses to host chrome
9
+ * such as Portal's sidebar at 200, so the `body` anchor lifts them as a whole to this tier. The token lives
10
+ * on the themed root because a scoped CSS build rewrites `:root` to the scope, so `body` never carries it.
11
+ *
12
+ * @param themeRoot The element the app's tokens cascade from.
13
+ * @returns The `--z-index-overlay-root` token as a number, or the 1300 fallback.
14
+ */
15
+ const readOverlayZIndex = (themeRoot) => {
16
+ const value = themeRoot ? Number.parseInt(getComputedStyle(themeRoot).getPropertyValue('--z-index-overlay-root'), 10) : Number.NaN;
17
+ return Number.isFinite(value) ? value : OVERLAY_ROOT_Z_INDEX_FALLBACK;
18
+ };
19
+ const portalToBody = () => document.body;
20
+ /**
21
+ * Decides where overlays portal. An embedded app (one with a `scopeId`) defaults to `document.body` so
22
+ * its overlays escape the host's `isolation` and `z-index` layers; `null` opts back into the themed root.
23
+ *
24
+ * @param portalContainer The consumer's `portalContainer` prop, as passed.
25
+ * @param scopeId The consumer's `scopeId` prop, as passed.
26
+ * @returns A resolver for the container, or `null` for the themed root.
27
+ */
28
+ const resolvePortalContainer = (portalContainer, scopeId) => portalContainer === undefined && scopeId ? portalToBody : (portalContainer ?? null);
29
+ /**
30
+ * Builds the anchor overlays portal into. On `body` it is a zero-height positioned block: no layout
31
+ * footprint (beyond a `gap` on a flex or grid body), but a stacking context the host's `z-index` layers cannot beat. Anywhere else it is
32
+ * `display: contents`, so it cannot disturb a flex or grid host. It carries `data-theme` and, with a
33
+ * scope, `data-mfe-scope`, which is what lets the app's scoped CSS reach an overlay rendered outside its tree.
34
+ *
35
+ * @param container The element the anchor is appended to.
36
+ * @param options The scope to stamp, if any, the resolved theme mode, and the stacking tier for a `body` anchor.
37
+ * @returns The appended anchor element.
38
+ */
39
+ const createPortalAnchor = (container, { scopeId, resolvedMode, zIndex = OVERLAY_ROOT_Z_INDEX_FALLBACK }) => {
40
+ const anchor = document.createElement('div');
41
+ if (container === document.body) {
42
+ anchor.style.position = 'relative';
43
+ anchor.style.height = '0';
44
+ anchor.style.zIndex = String(zIndex);
45
+ // A flex or grid body must not stretch the block or give it a share of the space; a `gap` still applies.
46
+ anchor.style.flex = '0 0 auto';
47
+ anchor.style.alignSelf = 'flex-start';
48
+ anchor.style.margin = '0';
49
+ }
50
+ else {
51
+ anchor.style.display = 'contents';
52
+ }
53
+ anchor.setAttribute('data-theme', resolvedMode);
54
+ if (scopeId)
55
+ anchor.setAttribute('data-mfe-scope', scopeId);
56
+ container.appendChild(anchor);
57
+ return anchor;
58
+ };
59
+ /**
60
+ * Keeps one themed, optionally scoped anchor inside the resolved portal container and hands React Aria
61
+ * a `getContainer` that only ever returns an attached anchor or the themed root. Compares resolved DOM
62
+ * nodes rather than callback identity, so an inline `() => getElementById(...)` does not rebuild the
63
+ * anchor on every render, and returns `null` for the commit in which the container is still changing so
64
+ * no overlay lands on `document.body` unstyled and then relocates.
65
+ *
66
+ * @param options The container choice, scope, themed root and theme mode.
67
+ * @returns The `getContainer` callback for `PortalContainer`.
68
+ */
69
+ const usePortalAnchor = ({ portalContainer, scopeId, themeRoot, resolvedMode }) => {
70
+ const resolveContainer = React.useMemo(() => resolvePortalContainer(portalContainer, scopeId), [portalContainer, scopeId]);
71
+ const [portalAnchor, setPortalAnchor] = React.useState(null);
72
+ const portalAnchorNode = React.useRef(null);
73
+ const lastExplicitContainer = React.useRef(null);
74
+ // useLayoutEffect + useState so the anchor exists before paint and its creation re-renders
75
+ // PortalContainer with a real container; a ref-only write left the first commit portalling to body.
76
+ React.useLayoutEffect(() => {
77
+ const explicit = resolveContainer?.() ?? null;
78
+ if (explicit === lastExplicitContainer.current) {
79
+ return;
80
+ }
81
+ lastExplicitContainer.current = explicit;
82
+ portalAnchorNode.current?.remove();
83
+ portalAnchorNode.current = null;
84
+ if (!explicit) {
85
+ setPortalAnchor(null);
86
+ return;
87
+ }
88
+ const anchor = createPortalAnchor(explicit, { scopeId, resolvedMode, zIndex: readOverlayZIndex(themeRoot) });
89
+ portalAnchorNode.current = anchor;
90
+ setPortalAnchor(anchor);
91
+ });
92
+ React.useLayoutEffect(() => () => {
93
+ portalAnchorNode.current?.remove();
94
+ portalAnchorNode.current = null;
95
+ // Reset alongside the node so StrictMode's dev-only mount → cleanup → remount does not see a stale
96
+ // "unchanged" match, skip recreating the anchor, and keep state pointing at the removed node.
97
+ lastExplicitContainer.current = null;
98
+ }, []);
99
+ React.useLayoutEffect(() => {
100
+ portalAnchorNode.current?.setAttribute('data-theme', resolvedMode);
101
+ }, [resolvedMode]);
102
+ React.useLayoutEffect(() => {
103
+ const anchor = portalAnchorNode.current;
104
+ if (!anchor)
105
+ return;
106
+ if (scopeId)
107
+ anchor.setAttribute('data-mfe-scope', scopeId);
108
+ else
109
+ anchor.removeAttribute('data-mfe-scope');
110
+ }, [scopeId]);
111
+ // `themeRoot` is state filled by a ref callback after the first commit, so the anchor is created before the
112
+ // token can be read; re-read it once the root exists (and whenever it changes) so a scoped override applies.
113
+ React.useLayoutEffect(() => {
114
+ if (!portalAnchor || portalAnchor.parentElement !== document.body)
115
+ return;
116
+ portalAnchor.style.zIndex = String(readOverlayZIndex(themeRoot));
117
+ }, [themeRoot, portalAnchor]);
118
+ // Re-resolving here (rather than trusting state alone) covers a host swap: this render's anchor still
119
+ // belongs to the old host until the layout effect rebuilds it, so it must not be handed out.
120
+ return React.useCallback(() => {
121
+ const explicit = resolveContainer?.() ?? null;
122
+ if (explicit) {
123
+ return explicit === lastExplicitContainer.current ? portalAnchor : null;
124
+ }
125
+ if (resolveContainer) {
126
+ return null;
127
+ }
128
+ return themeRoot;
129
+ }, [portalAnchor, resolveContainer, themeRoot]);
130
+ };
131
+
132
+ exports.OVERLAY_ROOT_Z_INDEX_FALLBACK = OVERLAY_ROOT_Z_INDEX_FALLBACK;
133
+ exports.createPortalAnchor = createPortalAnchor;
134
+ exports.readOverlayZIndex = readOverlayZIndex;
135
+ exports.resolvePortalContainer = resolvePortalContainer;
136
+ exports.usePortalAnchor = usePortalAnchor;
137
+ //# sourceMappingURL=usePortalAnchor.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePortalAnchor.cjs","sources":["../../../../src/providers/UIProvider/hooks/usePortalAnchor.ts"],"sourcesContent":[null],"names":["useMemo","useState","useRef","useLayoutEffect","useCallback"],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /** The `portalContainer` prop as consumers pass it: a resolver, `null` for the themed root, or absent. */
2
+ export type PortalContainerProp = (() => HTMLElement | null) | null | undefined;
3
+ /** Fallback for the `body` anchor's stacking level when the `--z-index-overlay-root` token is not in scope. */
4
+ export declare const OVERLAY_ROOT_Z_INDEX_FALLBACK = 1300;
5
+ /**
6
+ * Reads the design system's overlay stacking tier. Library overlays are `z-50`, which loses to host chrome
7
+ * such as Portal's sidebar at 200, so the `body` anchor lifts them as a whole to this tier. The token lives
8
+ * on the themed root because a scoped CSS build rewrites `:root` to the scope, so `body` never carries it.
9
+ *
10
+ * @param themeRoot The element the app's tokens cascade from.
11
+ * @returns The `--z-index-overlay-root` token as a number, or the 1300 fallback.
12
+ */
13
+ export declare const readOverlayZIndex: (themeRoot: HTMLElement | null) => number;
14
+ /**
15
+ * Decides where overlays portal. An embedded app (one with a `scopeId`) defaults to `document.body` so
16
+ * its overlays escape the host's `isolation` and `z-index` layers; `null` opts back into the themed root.
17
+ *
18
+ * @param portalContainer The consumer's `portalContainer` prop, as passed.
19
+ * @param scopeId The consumer's `scopeId` prop, as passed.
20
+ * @returns A resolver for the container, or `null` for the themed root.
21
+ */
22
+ export declare const resolvePortalContainer: (portalContainer: PortalContainerProp, scopeId: string | undefined) => (() => HTMLElement | null) | null;
23
+ /**
24
+ * Builds the anchor overlays portal into. On `body` it is a zero-height positioned block: no layout
25
+ * footprint (beyond a `gap` on a flex or grid body), but a stacking context the host's `z-index` layers cannot beat. Anywhere else it is
26
+ * `display: contents`, so it cannot disturb a flex or grid host. It carries `data-theme` and, with a
27
+ * scope, `data-mfe-scope`, which is what lets the app's scoped CSS reach an overlay rendered outside its tree.
28
+ *
29
+ * @param container The element the anchor is appended to.
30
+ * @param options The scope to stamp, if any, the resolved theme mode, and the stacking tier for a `body` anchor.
31
+ * @returns The appended anchor element.
32
+ */
33
+ export declare const createPortalAnchor: (container: HTMLElement, { scopeId, resolvedMode, zIndex }: {
34
+ scopeId?: string;
35
+ resolvedMode: "light" | "dark";
36
+ zIndex?: number;
37
+ }) => HTMLElement;
38
+ interface UsePortalAnchorOptions {
39
+ /** The consumer's `portalContainer` prop, as passed. */
40
+ portalContainer: PortalContainerProp;
41
+ /** The consumer's `scopeId` prop, as passed. */
42
+ scopeId: string | undefined;
43
+ /** `ThemeProvider`'s themed root, the fallback container. */
44
+ themeRoot: HTMLElement | null;
45
+ /** The resolved theme, mirrored onto the anchor so overlays inherit it. */
46
+ resolvedMode: 'light' | 'dark';
47
+ }
48
+ /**
49
+ * Keeps one themed, optionally scoped anchor inside the resolved portal container and hands React Aria
50
+ * a `getContainer` that only ever returns an attached anchor or the themed root. Compares resolved DOM
51
+ * nodes rather than callback identity, so an inline `() => getElementById(...)` does not rebuild the
52
+ * anchor on every render, and returns `null` for the commit in which the container is still changing so
53
+ * no overlay lands on `document.body` unstyled and then relocates.
54
+ *
55
+ * @param options The container choice, scope, themed root and theme mode.
56
+ * @returns The `getContainer` callback for `PortalContainer`.
57
+ */
58
+ export declare const usePortalAnchor: ({ portalContainer, scopeId, themeRoot, resolvedMode }: UsePortalAnchorOptions) => (() => HTMLElement | null);
59
+ export {};
60
+ //# sourceMappingURL=usePortalAnchor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePortalAnchor.d.ts","sourceRoot":"","sources":["../../../../src/providers/UIProvider/hooks/usePortalAnchor.ts"],"names":[],"mappings":"AAIA,0GAA0G;AAC1G,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAEhF,+GAA+G;AAC/G,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,WAAW,WAAW,GAAG,IAAI,KAAG,MAGjE,CAAC;AAIF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GAAI,iBAAiB,mBAAmB,EAAE,SAAS,MAAM,GAAG,SAAS,KAAG,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,IACnD,CAAC;AAEtF;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,GAC7B,WAAW,WAAW,EACtB,mCAAmE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,KACvI,WAiBF,CAAC;AAEF,UAAU,sBAAsB;IAC9B,wDAAwD;IACxD,eAAe,EAAE,mBAAmB,CAAC;IACrC,gDAAgD;IAChD,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,6DAA6D;IAC7D,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IAC9B,2EAA2E;IAC3E,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAAI,uDAAuD,sBAAsB,KAAG,CAAC,MAAM,WAAW,GAAG,IAAI,CAqExI,CAAC"}
@@ -0,0 +1,131 @@
1
+ import { useMemo, useState, useRef, useLayoutEffect, useCallback } from 'react';
2
+
3
+ /** Fallback for the `body` anchor's stacking level when the `--z-index-overlay-root` token is not in scope. */
4
+ const OVERLAY_ROOT_Z_INDEX_FALLBACK = 1300;
5
+ /**
6
+ * Reads the design system's overlay stacking tier. Library overlays are `z-50`, which loses to host chrome
7
+ * such as Portal's sidebar at 200, so the `body` anchor lifts them as a whole to this tier. The token lives
8
+ * on the themed root because a scoped CSS build rewrites `:root` to the scope, so `body` never carries it.
9
+ *
10
+ * @param themeRoot The element the app's tokens cascade from.
11
+ * @returns The `--z-index-overlay-root` token as a number, or the 1300 fallback.
12
+ */
13
+ const readOverlayZIndex = (themeRoot) => {
14
+ const value = themeRoot ? Number.parseInt(getComputedStyle(themeRoot).getPropertyValue('--z-index-overlay-root'), 10) : Number.NaN;
15
+ return Number.isFinite(value) ? value : OVERLAY_ROOT_Z_INDEX_FALLBACK;
16
+ };
17
+ const portalToBody = () => document.body;
18
+ /**
19
+ * Decides where overlays portal. An embedded app (one with a `scopeId`) defaults to `document.body` so
20
+ * its overlays escape the host's `isolation` and `z-index` layers; `null` opts back into the themed root.
21
+ *
22
+ * @param portalContainer The consumer's `portalContainer` prop, as passed.
23
+ * @param scopeId The consumer's `scopeId` prop, as passed.
24
+ * @returns A resolver for the container, or `null` for the themed root.
25
+ */
26
+ const resolvePortalContainer = (portalContainer, scopeId) => portalContainer === undefined && scopeId ? portalToBody : (portalContainer ?? null);
27
+ /**
28
+ * Builds the anchor overlays portal into. On `body` it is a zero-height positioned block: no layout
29
+ * footprint (beyond a `gap` on a flex or grid body), but a stacking context the host's `z-index` layers cannot beat. Anywhere else it is
30
+ * `display: contents`, so it cannot disturb a flex or grid host. It carries `data-theme` and, with a
31
+ * scope, `data-mfe-scope`, which is what lets the app's scoped CSS reach an overlay rendered outside its tree.
32
+ *
33
+ * @param container The element the anchor is appended to.
34
+ * @param options The scope to stamp, if any, the resolved theme mode, and the stacking tier for a `body` anchor.
35
+ * @returns The appended anchor element.
36
+ */
37
+ const createPortalAnchor = (container, { scopeId, resolvedMode, zIndex = OVERLAY_ROOT_Z_INDEX_FALLBACK }) => {
38
+ const anchor = document.createElement('div');
39
+ if (container === document.body) {
40
+ anchor.style.position = 'relative';
41
+ anchor.style.height = '0';
42
+ anchor.style.zIndex = String(zIndex);
43
+ // A flex or grid body must not stretch the block or give it a share of the space; a `gap` still applies.
44
+ anchor.style.flex = '0 0 auto';
45
+ anchor.style.alignSelf = 'flex-start';
46
+ anchor.style.margin = '0';
47
+ }
48
+ else {
49
+ anchor.style.display = 'contents';
50
+ }
51
+ anchor.setAttribute('data-theme', resolvedMode);
52
+ if (scopeId)
53
+ anchor.setAttribute('data-mfe-scope', scopeId);
54
+ container.appendChild(anchor);
55
+ return anchor;
56
+ };
57
+ /**
58
+ * Keeps one themed, optionally scoped anchor inside the resolved portal container and hands React Aria
59
+ * a `getContainer` that only ever returns an attached anchor or the themed root. Compares resolved DOM
60
+ * nodes rather than callback identity, so an inline `() => getElementById(...)` does not rebuild the
61
+ * anchor on every render, and returns `null` for the commit in which the container is still changing so
62
+ * no overlay lands on `document.body` unstyled and then relocates.
63
+ *
64
+ * @param options The container choice, scope, themed root and theme mode.
65
+ * @returns The `getContainer` callback for `PortalContainer`.
66
+ */
67
+ const usePortalAnchor = ({ portalContainer, scopeId, themeRoot, resolvedMode }) => {
68
+ const resolveContainer = useMemo(() => resolvePortalContainer(portalContainer, scopeId), [portalContainer, scopeId]);
69
+ const [portalAnchor, setPortalAnchor] = useState(null);
70
+ const portalAnchorNode = useRef(null);
71
+ const lastExplicitContainer = useRef(null);
72
+ // useLayoutEffect + useState so the anchor exists before paint and its creation re-renders
73
+ // PortalContainer with a real container; a ref-only write left the first commit portalling to body.
74
+ useLayoutEffect(() => {
75
+ const explicit = resolveContainer?.() ?? null;
76
+ if (explicit === lastExplicitContainer.current) {
77
+ return;
78
+ }
79
+ lastExplicitContainer.current = explicit;
80
+ portalAnchorNode.current?.remove();
81
+ portalAnchorNode.current = null;
82
+ if (!explicit) {
83
+ setPortalAnchor(null);
84
+ return;
85
+ }
86
+ const anchor = createPortalAnchor(explicit, { scopeId, resolvedMode, zIndex: readOverlayZIndex(themeRoot) });
87
+ portalAnchorNode.current = anchor;
88
+ setPortalAnchor(anchor);
89
+ });
90
+ useLayoutEffect(() => () => {
91
+ portalAnchorNode.current?.remove();
92
+ portalAnchorNode.current = null;
93
+ // Reset alongside the node so StrictMode's dev-only mount → cleanup → remount does not see a stale
94
+ // "unchanged" match, skip recreating the anchor, and keep state pointing at the removed node.
95
+ lastExplicitContainer.current = null;
96
+ }, []);
97
+ useLayoutEffect(() => {
98
+ portalAnchorNode.current?.setAttribute('data-theme', resolvedMode);
99
+ }, [resolvedMode]);
100
+ useLayoutEffect(() => {
101
+ const anchor = portalAnchorNode.current;
102
+ if (!anchor)
103
+ return;
104
+ if (scopeId)
105
+ anchor.setAttribute('data-mfe-scope', scopeId);
106
+ else
107
+ anchor.removeAttribute('data-mfe-scope');
108
+ }, [scopeId]);
109
+ // `themeRoot` is state filled by a ref callback after the first commit, so the anchor is created before the
110
+ // token can be read; re-read it once the root exists (and whenever it changes) so a scoped override applies.
111
+ useLayoutEffect(() => {
112
+ if (!portalAnchor || portalAnchor.parentElement !== document.body)
113
+ return;
114
+ portalAnchor.style.zIndex = String(readOverlayZIndex(themeRoot));
115
+ }, [themeRoot, portalAnchor]);
116
+ // Re-resolving here (rather than trusting state alone) covers a host swap: this render's anchor still
117
+ // belongs to the old host until the layout effect rebuilds it, so it must not be handed out.
118
+ return useCallback(() => {
119
+ const explicit = resolveContainer?.() ?? null;
120
+ if (explicit) {
121
+ return explicit === lastExplicitContainer.current ? portalAnchor : null;
122
+ }
123
+ if (resolveContainer) {
124
+ return null;
125
+ }
126
+ return themeRoot;
127
+ }, [portalAnchor, resolveContainer, themeRoot]);
128
+ };
129
+
130
+ export { OVERLAY_ROOT_Z_INDEX_FALLBACK, createPortalAnchor, readOverlayZIndex, resolvePortalContainer, usePortalAnchor };
131
+ //# sourceMappingURL=usePortalAnchor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePortalAnchor.js","sources":["../../../../src/providers/UIProvider/hooks/usePortalAnchor.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -5,84 +5,27 @@ var ErrorBoundary = require('@flipdish/ui-library/providers/ErrorBoundary');
5
5
  var PortalContainer = require('@flipdish/ui-library/providers/PortalContainer');
6
6
  var RouteProvider = require('@flipdish/ui-library/providers/RouteProvider');
7
7
  var ThemeProvider = require('@flipdish/ui-library/providers/ThemeProvider');
8
- var React = require('react');
9
8
  var reactRouter = require('react-router');
9
+ var usePortalAnchor = require('./hooks/usePortalAnchor.cjs');
10
10
 
11
- const UIProviderInner = ({ children, baseUrl, errorBoundary, portalContainer }) => {
11
+ const UIProviderInner = ({ children, baseUrl, errorBoundary, scopeId, portalContainer, }) => {
12
12
  const { themeRoot, resolvedMode } = ThemeProvider.useTheme();
13
- const [portalAnchor, setPortalAnchor] = React.useState(null);
14
- const portalAnchorNode = React.useRef(null);
15
- const lastExplicitContainer = React.useRef(null);
16
- // Create a themed, layout-invisible anchor inside the portalContainer so
17
- // React Aria overlays inherit scoped CSS and the active data-theme.
18
- // Compares resolved DOM nodes (not callback identity) to avoid tearing
19
- // down the anchor when an inline callback like `() => getElementById(...)` is passed.
20
- // useLayoutEffect + useState so the anchor exists before paint and its
21
- // creation re-renders PortalContainer with a real container — a ref-only
22
- // write left the first commit portalling to document.body.
23
- React.useLayoutEffect(() => {
24
- const explicit = portalContainer?.() ?? null;
25
- if (explicit === lastExplicitContainer.current) {
26
- return;
27
- }
28
- lastExplicitContainer.current = explicit;
29
- portalAnchorNode.current?.remove();
30
- portalAnchorNode.current = null;
31
- if (!explicit) {
32
- setPortalAnchor(null);
33
- return;
34
- }
35
- const anchor = document.createElement('div');
36
- anchor.style.display = 'contents';
37
- anchor.setAttribute('data-theme', resolvedMode);
38
- explicit.appendChild(anchor);
39
- portalAnchorNode.current = anchor;
40
- setPortalAnchor(anchor);
41
- });
42
- React.useLayoutEffect(() => () => {
43
- portalAnchorNode.current?.remove();
44
- portalAnchorNode.current = null;
45
- // Reset alongside portalAnchorNode so StrictMode's dev-only mount → cleanup →
46
- // remount doesn't see a stale "unchanged" match on remount: without this, the
47
- // remount's effect compares against the pre-cleanup container, skips recreating
48
- // the anchor, and leaves `portalAnchor` state pointing at the node just removed
49
- // above — permanently detached from the document.
50
- lastExplicitContainer.current = null;
51
- }, []);
52
- React.useLayoutEffect(() => {
53
- portalAnchorNode.current?.setAttribute('data-theme', resolvedMode);
54
- }, [resolvedMode]);
55
- // While the explicit container's anchor (or ThemeProvider's themed root) is
56
- // unresolved, return null so overlays render nothing for that commit instead
57
- // of landing on document.body and then relocating. Re-resolving `portalContainer`
58
- // here (rather than trusting `portalAnchor` alone) also covers a host swap: if the
59
- // host changes from A to B, this render's anchor still belongs to host A until the
60
- // layout effect rebuilds it, so we must not hand out the stale host-A anchor.
61
- const getContainer = React.useCallback(() => {
62
- const explicit = portalContainer?.() ?? null;
63
- if (explicit) {
64
- return explicit === lastExplicitContainer.current ? portalAnchor : null;
65
- }
66
- if (portalContainer) {
67
- return null;
68
- }
69
- return themeRoot;
70
- }, [portalAnchor, portalContainer, themeRoot]);
13
+ const getContainer = usePortalAnchor.usePortalAnchor({ portalContainer, scopeId, themeRoot, resolvedMode });
71
14
  return (jsxRuntime.jsx(reactRouter.BrowserRouter, { basename: baseUrl, children: jsxRuntime.jsx(RouteProvider.RouteProvider, { children: jsxRuntime.jsx(ErrorBoundary.ErrorBoundary, { ...errorBoundary, children: jsxRuntime.jsx(PortalContainer.PortalContainer, { getContainer: getContainer, children: children }) }) }) }));
72
15
  };
73
16
  /**
74
17
  * Single entry-point provider that composes `ThemeProvider`, `RouteProvider`, `ErrorBoundary`
75
- * and overlay portalling for a consuming app. Wire it once at the root; pass `portalContainer`
76
- * only when overlays must land in a specific container.
18
+ * and overlay portalling for a consuming app. Wire it once at the root and pass `scopeId` when the
19
+ * app ships CSS scoped to `[data-mfe-scope]`; overlays then portal into a scoped anchor on `body`.
77
20
  *
78
21
  * @summary root provider composing theme, routing, errors and portals
79
22
  */
80
- const UIProvider = ({ children, themeModeOverride, ...rest }) => {
23
+ const UIProvider = ({ children, themeModeOverride, scopeId, ...rest }) => {
81
24
  // TODO(i18n): add an internal library translation context so ui-library components can
82
25
  // source their own copy (e.g. ErrorBoundary's "Something went wrong") without the
83
26
  // consuming app needing to pass text props. This is purely internal — consuming apps
84
27
  // own their own translation layer and wrap their content inside UIProvider as usual.
85
- return (jsxRuntime.jsx(ThemeProvider.ThemeProvider, { themeModeOverride: themeModeOverride, children: jsxRuntime.jsx(UIProviderInner, { ...rest, children: children }) }));
28
+ return (jsxRuntime.jsx(ThemeProvider.ThemeProvider, { themeModeOverride: themeModeOverride, scopeId: scopeId, children: jsxRuntime.jsx(UIProviderInner, { scopeId: scopeId, ...rest, children: children }) }));
86
29
  };
87
30
 
88
31
  exports.UIProvider = UIProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../../src/providers/UIProvider/index.tsx"],"sourcesContent":[null],"names":["useTheme","useState","useRef","useLayoutEffect","useCallback","_jsx","BrowserRouter","RouteProvider","ErrorBoundary","PortalContainer","ThemeProvider"],"mappings":";;;;;;;;;;AA2CA,MAAM,eAAe,GAAG,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAiE,KAAI;IAC/I,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAGA,sBAAQ,EAAE;IAC9C,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAGC,cAAQ,CAAqB,IAAI,CAAC;AAC1E,IAAA,MAAM,gBAAgB,GAAGC,YAAM,CAAqB,IAAI,CAAC;AACzD,IAAA,MAAM,qBAAqB,GAAGA,YAAM,CAAqB,IAAI,CAAC;;;;;;;;IAS9DC,qBAAe,CAAC,MAAK;AACnB,QAAA,MAAM,QAAQ,GAAG,eAAe,IAAI,IAAI,IAAI;AAC5C,QAAA,IAAI,QAAQ,KAAK,qBAAqB,CAAC,OAAO,EAAE;YAC9C;QACF;AAEA,QAAA,qBAAqB,CAAC,OAAO,GAAG,QAAQ;AACxC,QAAA,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE;AAClC,QAAA,gBAAgB,CAAC,OAAO,GAAG,IAAI;QAE/B,IAAI,CAAC,QAAQ,EAAE;YACb,eAAe,CAAC,IAAI,CAAC;YACrB;QACF;QAEA,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC;AAC5C,QAAA,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,UAAU;AACjC,QAAA,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,YAAY,CAAC;AAC/C,QAAA,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC;AAC5B,QAAA,gBAAgB,CAAC,OAAO,GAAG,MAAM;QACjC,eAAe,CAAC,MAAM,CAAC;AACzB,IAAA,CAAC,CAAC;AAEF,IAAAA,qBAAe,CACb,MAAM,MAAK;AACT,QAAA,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE;AAClC,QAAA,gBAAgB,CAAC,OAAO,GAAG,IAAI;;;;;;AAM/B,QAAA,qBAAqB,CAAC,OAAO,GAAG,IAAI;IACtC,CAAC,EACD,EAAE,CACH;IAEDA,qBAAe,CAAC,MAAK;QACnB,gBAAgB,CAAC,OAAO,EAAE,YAAY,CAAC,YAAY,EAAE,YAAY,CAAC;AACpE,IAAA,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC;;;;;;;AAQlB,IAAA,MAAM,YAAY,GAAGC,iBAAW,CAAC,MAAK;AACpC,QAAA,MAAM,QAAQ,GAAG,eAAe,IAAI,IAAI,IAAI;QAC5C,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,QAAQ,KAAK,qBAAqB,CAAC,OAAO,GAAG,YAAY,GAAG,IAAI;QACzE;QACA,IAAI,eAAe,EAAE;AACnB,YAAA,OAAO,IAAI;QACb;AACA,QAAA,OAAO,SAAS;IAClB,CAAC,EAAE,CAAC,YAAY,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;AAE9C,IAAA,QACEC,cAAA,CAACC,yBAAa,EAAA,EAAC,QAAQ,EAAE,OAAO,EAAA,QAAA,EAC9BD,cAAA,CAACE,2BAAa,EAAA,EAAA,QAAA,EACZF,cAAA,CAACG,2BAAa,EAAA,EAAA,GAAK,aAAa,EAAA,QAAA,EAC9BH,cAAA,CAACI,+BAAe,EAAA,EAAC,YAAY,EAAE,YAAY,EAAA,QAAA,EAAG,QAAQ,EAAA,CAAmB,EAAA,CAC3D,EAAA,CACF,EAAA,CACF;AAEpB,CAAC;AAED;;;;;;AAMG;AACI,MAAM,UAAU,GAAG,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,GAAG,IAAI,EAAsC,KAAI;;;;;AAKzG,IAAA,QACEJ,cAAA,CAACK,2BAAa,EAAA,EAAC,iBAAiB,EAAE,iBAAiB,EAAA,QAAA,EACjDL,cAAA,CAAC,eAAe,OAAK,IAAI,EAAA,QAAA,EAAG,QAAQ,EAAA,CAAmB,EAAA,CACzC;AAEpB;;"}
1
+ {"version":3,"file":"index.cjs","sources":["../../../src/providers/UIProvider/index.tsx"],"sourcesContent":[null],"names":["useTheme","usePortalAnchor","_jsx","BrowserRouter","RouteProvider","ErrorBoundary","PortalContainer","ThemeProvider"],"mappings":";;;;;;;;;;AAiDA,MAAM,eAAe,GAAG,CAAC,EACvB,QAAQ,EACR,OAAO,EACP,aAAa,EACb,OAAO,EACP,eAAe,GAC+C,KAAI;IAClE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAGA,sBAAQ,EAAE;AAC9C,IAAA,MAAM,YAAY,GAAGC,+BAAe,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AAE3F,IAAA,QACEC,cAAA,CAACC,yBAAa,EAAA,EAAC,QAAQ,EAAE,OAAO,EAAA,QAAA,EAC9BD,cAAA,CAACE,2BAAa,EAAA,EAAA,QAAA,EACZF,cAAA,CAACG,2BAAa,EAAA,EAAA,GAAK,aAAa,EAAA,QAAA,EAC9BH,cAAA,CAACI,+BAAe,EAAA,EAAC,YAAY,EAAE,YAAY,EAAA,QAAA,EAAG,QAAQ,EAAA,CAAmB,EAAA,CAC3D,EAAA,CACF,EAAA,CACF;AAEpB,CAAC;AAED;;;;;;AAMG;AACI,MAAM,UAAU,GAAG,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,GAAG,IAAI,EAAsC,KAAI;;;;;IAKlH,QACEJ,cAAA,CAACK,2BAAa,EAAA,EAAC,iBAAiB,EAAE,iBAAiB,EAAE,OAAO,EAAE,OAAO,EAAA,QAAA,EACnEL,eAAC,eAAe,EAAA,EAAC,OAAO,EAAE,OAAO,EAAA,GAAM,IAAI,EAAA,QAAA,EACxC,QAAQ,EAAA,CACO,EAAA,CACJ;AAEpB;;"}
@@ -1,5 +1,5 @@
1
1
  import { type ErrorBoundaryProps } from '@flipdish/ui-library/providers/ErrorBoundary';
2
- import { type PropsWithChildren } from 'react';
2
+ import type { PropsWithChildren } from 'react';
3
3
  export interface UIProviderProps {
4
4
  /**
5
5
  * Controlled theme override. When set, the tree renders in this mode and theme
@@ -20,25 +20,30 @@ export interface UIProviderProps {
20
20
  * errorBoundary={{ identifier: 'app', onError: datadogReporter }}
21
21
  */
22
22
  errorBoundary: Omit<ErrorBoundaryProps, 'children'>;
23
+ /**
24
+ * Stamps `data-mfe-scope={scopeId}` on the themed root so the app's scoped CSS build
25
+ * (`createScopedPrefixTransform('[data-mfe-scope="sale-service"]')`) matches this tree wherever the
26
+ * host mounts it. Overlays then portal into a scoped, themed anchor on `document.body`, above the
27
+ * host's stacking contexts, and `ThemeProvider` stops mirroring `data-theme` onto the host `<html>`.
28
+ */
29
+ scopeId?: string;
23
30
  /**
24
31
  * Callback that returns the DOM element where React Aria overlays (Modal, Popover,
25
- * Select, Tooltip, Toast) should portal into. Passed to `PortalContainer`
26
- * internally — see its docs for full details.
32
+ * Select, Tooltip, Toast) should portal into. A layout-invisible anchor carrying `data-theme`
33
+ * and, with `scopeId`, `data-mfe-scope` is created inside it so overlays keep their styles.
27
34
  *
28
- * Use this in micro-frontends or embedded apps whose CSS is scoped to a mount
29
- * subtree. Omit to portal into ThemeProvider's themed root element.
30
- *
31
- * @example
32
- * portalContainer={() => document.getElementById('flipdish-micro-frontend')}
35
+ * Defaults to `document.body` when `scopeId` is set, where the anchor is a stacking context at the
36
+ * `--z-index-overlay-root` token (1300) so overlays paint above the host's chrome and isolated content
37
+ * areas, and to the themed root otherwise. Pass `null` to portal into the themed root even with `scopeId`.
33
38
  */
34
39
  portalContainer?: (() => HTMLElement | null) | null;
35
40
  }
36
41
  /**
37
42
  * Single entry-point provider that composes `ThemeProvider`, `RouteProvider`, `ErrorBoundary`
38
- * and overlay portalling for a consuming app. Wire it once at the root; pass `portalContainer`
39
- * only when overlays must land in a specific container.
43
+ * and overlay portalling for a consuming app. Wire it once at the root and pass `scopeId` when the
44
+ * app ships CSS scoped to `[data-mfe-scope]`; overlays then portal into a scoped anchor on `body`.
40
45
  *
41
46
  * @summary root provider composing theme, routing, errors and portals
42
47
  */
43
- export declare const UIProvider: ({ children, themeModeOverride, ...rest }: PropsWithChildren<UIProviderProps>) => import("react").JSX.Element;
48
+ export declare const UIProvider: ({ children, themeModeOverride, scopeId, ...rest }: PropsWithChildren<UIProviderProps>) => import("react").JSX.Element;
44
49
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/UIProvider/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AAItG,OAAO,EAAE,KAAK,iBAAiB,EAAkD,MAAM,OAAO,CAAC;AAG/F,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,aAAa,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;IACpD;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;CACrD;AAoFD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,0CAA0C,iBAAiB,CAAC,eAAe,CAAC,gCAUtG,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/UIProvider/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AAItG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAI/C,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACrC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,aAAa,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;IACpD;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;CACrD;AAuBD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,mDAAmD,iBAAiB,CAAC,eAAe,CAAC,gCAY/G,CAAC"}
@@ -3,84 +3,27 @@ import { ErrorBoundary } from '@flipdish/ui-library/providers/ErrorBoundary';
3
3
  import { PortalContainer } from '@flipdish/ui-library/providers/PortalContainer';
4
4
  import { RouteProvider } from '@flipdish/ui-library/providers/RouteProvider';
5
5
  import { ThemeProvider, useTheme } from '@flipdish/ui-library/providers/ThemeProvider';
6
- import { useState, useRef, useLayoutEffect, useCallback } from 'react';
7
6
  import { BrowserRouter } from 'react-router';
7
+ import { usePortalAnchor } from './hooks/usePortalAnchor.js';
8
8
 
9
- const UIProviderInner = ({ children, baseUrl, errorBoundary, portalContainer }) => {
9
+ const UIProviderInner = ({ children, baseUrl, errorBoundary, scopeId, portalContainer, }) => {
10
10
  const { themeRoot, resolvedMode } = useTheme();
11
- const [portalAnchor, setPortalAnchor] = useState(null);
12
- const portalAnchorNode = useRef(null);
13
- const lastExplicitContainer = useRef(null);
14
- // Create a themed, layout-invisible anchor inside the portalContainer so
15
- // React Aria overlays inherit scoped CSS and the active data-theme.
16
- // Compares resolved DOM nodes (not callback identity) to avoid tearing
17
- // down the anchor when an inline callback like `() => getElementById(...)` is passed.
18
- // useLayoutEffect + useState so the anchor exists before paint and its
19
- // creation re-renders PortalContainer with a real container — a ref-only
20
- // write left the first commit portalling to document.body.
21
- useLayoutEffect(() => {
22
- const explicit = portalContainer?.() ?? null;
23
- if (explicit === lastExplicitContainer.current) {
24
- return;
25
- }
26
- lastExplicitContainer.current = explicit;
27
- portalAnchorNode.current?.remove();
28
- portalAnchorNode.current = null;
29
- if (!explicit) {
30
- setPortalAnchor(null);
31
- return;
32
- }
33
- const anchor = document.createElement('div');
34
- anchor.style.display = 'contents';
35
- anchor.setAttribute('data-theme', resolvedMode);
36
- explicit.appendChild(anchor);
37
- portalAnchorNode.current = anchor;
38
- setPortalAnchor(anchor);
39
- });
40
- useLayoutEffect(() => () => {
41
- portalAnchorNode.current?.remove();
42
- portalAnchorNode.current = null;
43
- // Reset alongside portalAnchorNode so StrictMode's dev-only mount → cleanup →
44
- // remount doesn't see a stale "unchanged" match on remount: without this, the
45
- // remount's effect compares against the pre-cleanup container, skips recreating
46
- // the anchor, and leaves `portalAnchor` state pointing at the node just removed
47
- // above — permanently detached from the document.
48
- lastExplicitContainer.current = null;
49
- }, []);
50
- useLayoutEffect(() => {
51
- portalAnchorNode.current?.setAttribute('data-theme', resolvedMode);
52
- }, [resolvedMode]);
53
- // While the explicit container's anchor (or ThemeProvider's themed root) is
54
- // unresolved, return null so overlays render nothing for that commit instead
55
- // of landing on document.body and then relocating. Re-resolving `portalContainer`
56
- // here (rather than trusting `portalAnchor` alone) also covers a host swap: if the
57
- // host changes from A to B, this render's anchor still belongs to host A until the
58
- // layout effect rebuilds it, so we must not hand out the stale host-A anchor.
59
- const getContainer = useCallback(() => {
60
- const explicit = portalContainer?.() ?? null;
61
- if (explicit) {
62
- return explicit === lastExplicitContainer.current ? portalAnchor : null;
63
- }
64
- if (portalContainer) {
65
- return null;
66
- }
67
- return themeRoot;
68
- }, [portalAnchor, portalContainer, themeRoot]);
11
+ const getContainer = usePortalAnchor({ portalContainer, scopeId, themeRoot, resolvedMode });
69
12
  return (jsx(BrowserRouter, { basename: baseUrl, children: jsx(RouteProvider, { children: jsx(ErrorBoundary, { ...errorBoundary, children: jsx(PortalContainer, { getContainer: getContainer, children: children }) }) }) }));
70
13
  };
71
14
  /**
72
15
  * Single entry-point provider that composes `ThemeProvider`, `RouteProvider`, `ErrorBoundary`
73
- * and overlay portalling for a consuming app. Wire it once at the root; pass `portalContainer`
74
- * only when overlays must land in a specific container.
16
+ * and overlay portalling for a consuming app. Wire it once at the root and pass `scopeId` when the
17
+ * app ships CSS scoped to `[data-mfe-scope]`; overlays then portal into a scoped anchor on `body`.
75
18
  *
76
19
  * @summary root provider composing theme, routing, errors and portals
77
20
  */
78
- const UIProvider = ({ children, themeModeOverride, ...rest }) => {
21
+ const UIProvider = ({ children, themeModeOverride, scopeId, ...rest }) => {
79
22
  // TODO(i18n): add an internal library translation context so ui-library components can
80
23
  // source their own copy (e.g. ErrorBoundary's "Something went wrong") without the
81
24
  // consuming app needing to pass text props. This is purely internal — consuming apps
82
25
  // own their own translation layer and wrap their content inside UIProvider as usual.
83
- return (jsx(ThemeProvider, { themeModeOverride: themeModeOverride, children: jsx(UIProviderInner, { ...rest, children: children }) }));
26
+ return (jsx(ThemeProvider, { themeModeOverride: themeModeOverride, scopeId: scopeId, children: jsx(UIProviderInner, { scopeId: scopeId, ...rest, children: children }) }));
84
27
  };
85
28
 
86
29
  export { UIProvider };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/providers/UIProvider/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/providers/UIProvider/index.tsx"],"sourcesContent":[null],"names":["_jsx"],"mappings":";;;;;;;;AAiDA,MAAM,eAAe,GAAG,CAAC,EACvB,QAAQ,EACR,OAAO,EACP,aAAa,EACb,OAAO,EACP,eAAe,GAC+C,KAAI;IAClE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,QAAQ,EAAE;AAC9C,IAAA,MAAM,YAAY,GAAG,eAAe,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AAE3F,IAAA,QACEA,GAAA,CAAC,aAAa,EAAA,EAAC,QAAQ,EAAE,OAAO,EAAA,QAAA,EAC9BA,GAAA,CAAC,aAAa,EAAA,EAAA,QAAA,EACZA,GAAA,CAAC,aAAa,EAAA,EAAA,GAAK,aAAa,EAAA,QAAA,EAC9BA,GAAA,CAAC,eAAe,EAAA,EAAC,YAAY,EAAE,YAAY,EAAA,QAAA,EAAG,QAAQ,EAAA,CAAmB,EAAA,CAC3D,EAAA,CACF,EAAA,CACF;AAEpB,CAAC;AAED;;;;;;AAMG;AACI,MAAM,UAAU,GAAG,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,GAAG,IAAI,EAAsC,KAAI;;;;;IAKlH,QACEA,GAAA,CAAC,aAAa,EAAA,EAAC,iBAAiB,EAAE,iBAAiB,EAAE,OAAO,EAAE,OAAO,EAAA,QAAA,EACnEA,IAAC,eAAe,EAAA,EAAC,OAAO,EAAE,OAAO,EAAA,GAAM,IAAI,EAAA,QAAA,EACxC,QAAQ,EAAA,CACO,EAAA,CACJ;AAEpB;;"}