@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.
- package/README.md +9 -9
- package/dist/components/__internal__/selectionControl/selectionControl.styles.cjs +22 -0
- package/dist/components/__internal__/selectionControl/selectionControl.styles.cjs.map +1 -0
- package/dist/components/__internal__/selectionControl/selectionControl.styles.d.ts +17 -0
- package/dist/components/__internal__/selectionControl/selectionControl.styles.d.ts.map +1 -0
- package/dist/components/__internal__/selectionControl/selectionControl.styles.js +20 -0
- package/dist/components/__internal__/selectionControl/selectionControl.styles.js.map +1 -0
- package/dist/components/__internal__/selectionControl/selectionControl.utils.d.ts +23 -0
- package/dist/components/__internal__/selectionControl/selectionControl.utils.d.ts.map +1 -0
- package/dist/components/atoms/Checkbox/index.cjs +3 -16
- package/dist/components/atoms/Checkbox/index.cjs.map +1 -1
- package/dist/components/atoms/Checkbox/index.d.ts.map +1 -1
- package/dist/components/atoms/Checkbox/index.js +3 -16
- package/dist/components/atoms/Checkbox/index.js.map +1 -1
- package/dist/components/atoms/Toggle/Toggle.styles.cjs +0 -14
- package/dist/components/atoms/Toggle/Toggle.styles.cjs.map +1 -1
- package/dist/components/atoms/Toggle/Toggle.styles.d.ts +0 -14
- package/dist/components/atoms/Toggle/Toggle.styles.d.ts.map +1 -1
- package/dist/components/atoms/Toggle/Toggle.styles.js +0 -14
- package/dist/components/atoms/Toggle/Toggle.styles.js.map +1 -1
- package/dist/components/atoms/Toggle/index.cjs +3 -2
- package/dist/components/atoms/Toggle/index.cjs.map +1 -1
- package/dist/components/atoms/Toggle/index.d.ts.map +1 -1
- package/dist/components/atoms/Toggle/index.js +3 -2
- package/dist/components/atoms/Toggle/index.js.map +1 -1
- package/dist/components/templates/WidgetLayout/index.cjs +36 -0
- package/dist/components/templates/WidgetLayout/index.cjs.map +1 -0
- package/dist/components/templates/WidgetLayout/index.d.ts +54 -0
- package/dist/components/templates/WidgetLayout/index.d.ts.map +1 -0
- package/dist/components/templates/WidgetLayout/index.js +34 -0
- package/dist/components/templates/WidgetLayout/index.js.map +1 -0
- package/dist/native/components/__internal__/selectionControl/SelectionControlText.d.ts +1 -1
- package/dist/native/components/__internal__/selectionControl/SelectionControlText.d.ts.map +1 -1
- package/dist/native/components/__internal__/selectionControl/SelectionControlText.js +2 -1
- package/dist/native/components/__internal__/selectionControl/SelectionControlText.js.map +1 -1
- package/dist/native/components/__internal__/selectionControl/selectionControl.styles.d.ts +17 -0
- package/dist/native/components/__internal__/selectionControl/selectionControl.styles.d.ts.map +1 -0
- package/dist/native/components/__internal__/selectionControl/selectionControl.styles.js +20 -0
- package/dist/native/components/__internal__/selectionControl/selectionControl.styles.js.map +1 -0
- package/dist/native/components/__internal__/selectionControl/selectionControl.utils.d.ts +23 -0
- package/dist/native/components/__internal__/selectionControl/selectionControl.utils.d.ts.map +1 -0
- package/dist/native/components/__internal__/selectionControl/selectionControl.utils.js +20 -0
- package/dist/native/components/__internal__/selectionControl/selectionControl.utils.js.map +1 -0
- package/dist/native/components/atoms/Checkbox/index.d.ts +31 -9
- package/dist/native/components/atoms/Checkbox/index.d.ts.map +1 -0
- package/dist/native/components/atoms/Checkbox/index.js +39 -0
- package/dist/native/components/atoms/Checkbox/index.js.map +1 -0
- package/dist/native/components/atoms/Toggle/Toggle.styles.d.ts +0 -14
- package/dist/native/components/atoms/Toggle/Toggle.styles.d.ts.map +1 -1
- package/dist/native/components/atoms/Toggle/Toggle.styles.js +0 -14
- package/dist/native/components/atoms/Toggle/Toggle.styles.js.map +1 -1
- package/dist/native/components/atoms/Toggle/index.d.ts.map +1 -1
- package/dist/native/components/atoms/Toggle/index.js +3 -1
- package/dist/native/components/atoms/Toggle/index.js.map +1 -1
- package/dist/native/components/templates/WidgetLayout/index.d.ts +12 -0
- package/dist/providers/PortalContainer/index.d.ts +1 -1
- package/dist/providers/ThemeProvider/index.cjs +36 -14
- package/dist/providers/ThemeProvider/index.cjs.map +1 -1
- package/dist/providers/ThemeProvider/index.d.ts +19 -5
- package/dist/providers/ThemeProvider/index.d.ts.map +1 -1
- package/dist/providers/ThemeProvider/index.js +36 -14
- package/dist/providers/ThemeProvider/index.js.map +1 -1
- package/dist/providers/UIProvider/hooks/usePortalAnchor.cjs +137 -0
- package/dist/providers/UIProvider/hooks/usePortalAnchor.cjs.map +1 -0
- package/dist/providers/UIProvider/hooks/usePortalAnchor.d.ts +60 -0
- package/dist/providers/UIProvider/hooks/usePortalAnchor.d.ts.map +1 -0
- package/dist/providers/UIProvider/hooks/usePortalAnchor.js +131 -0
- package/dist/providers/UIProvider/hooks/usePortalAnchor.js.map +1 -0
- package/dist/providers/UIProvider/index.cjs +7 -64
- package/dist/providers/UIProvider/index.cjs.map +1 -1
- package/dist/providers/UIProvider/index.d.ts +16 -11
- package/dist/providers/UIProvider/index.d.ts.map +1 -1
- package/dist/providers/UIProvider/index.js +7 -64
- package/dist/providers/UIProvider/index.js.map +1 -1
- package/dist/utilities/cssScopeUtils/index.cjs +22 -11
- package/dist/utilities/cssScopeUtils/index.cjs.map +1 -1
- package/dist/utilities/cssScopeUtils/index.d.ts +20 -9
- package/dist/utilities/cssScopeUtils/index.d.ts.map +1 -1
- package/dist/utilities/cssScopeUtils/index.js +22 -11
- package/dist/utilities/cssScopeUtils/index.js.map +1 -1
- package/dist/utilities/renderUtils/index.cjs +36 -7
- package/dist/utilities/renderUtils/index.cjs.map +1 -1
- package/dist/utilities/renderUtils/index.d.ts +23 -2
- package/dist/utilities/renderUtils/index.d.ts.map +1 -1
- package/dist/utilities/renderUtils/index.js +35 -8
- package/dist/utilities/renderUtils/index.js.map +1 -1
- 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;
|
|
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
|
|
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
|
|
76
|
-
*
|
|
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","
|
|
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 {
|
|
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.
|
|
26
|
-
*
|
|
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
|
-
*
|
|
29
|
-
*
|
|
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
|
|
39
|
-
*
|
|
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,
|
|
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
|
|
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
|
|
74
|
-
*
|
|
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":";;;;;;;;
|
|
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;;"}
|