react-display-scale-engine 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -47
- package/dist/{chunk-OQRISU2D.js → chunk-47O3TJUJ.js} +69 -26
- package/dist/chunk-47O3TJUJ.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mui.d.ts +44 -8
- package/dist/mui.js +1 -1
- package/docs/display-scale-engine.md +193 -163
- package/package.json +5 -3
- package/dist/chunk-OQRISU2D.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
# react-display-scale-engine
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Keep a React app visually consistent on Windows displays that use elevated OS display scaling, while preserving normal browser behavior at standard scale. It is especially useful for MUI overlays such as menus, popovers, poppers, and selects.
|
|
4
4
|
|
|
5
|
-
The
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
import { detectDisplayScale } from "react-display-scale-engine/core";
|
|
9
|
-
import { DisplayScaleProvider } from "react-display-scale-engine/react";
|
|
10
|
-
import { DisplayScaleMenu } from "react-display-scale-engine/mui";
|
|
11
|
-
```
|
|
5
|
+
The engine only applies compensation when it detects the supported display-scale condition. At normal scale, it leaves the app layout and native MUI positioning unchanged.
|
|
12
6
|
|
|
13
7
|
## Install
|
|
14
8
|
|
|
@@ -16,11 +10,20 @@ import { DisplayScaleMenu } from "react-display-scale-engine/mui";
|
|
|
16
10
|
npm install react-display-scale-engine
|
|
17
11
|
```
|
|
18
12
|
|
|
19
|
-
|
|
13
|
+
## Compatibility
|
|
20
14
|
|
|
21
|
-
|
|
15
|
+
| Dependency | Supported version | Notes |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| Node.js | 18 or newer | Required by the package. |
|
|
18
|
+
| React / React DOM | 18 or newer | React 19 is supported and tested. |
|
|
19
|
+
| Material UI | 7.x | Required only when using `…/mui`. MUI 8 is not supported yet. |
|
|
20
|
+
| Emotion | 11 or newer | Required only when using `…/mui`. |
|
|
21
|
+
| `@popperjs/core` | 2 or newer | Required only when using `…/mui`. |
|
|
22
|
+
| TypeScript | Optional | Type declarations are included; the package is verified with TypeScript 5.7. |
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
## Start here
|
|
25
|
+
|
|
26
|
+
Call the optional warm-up before rendering, then add one provider around the app.
|
|
24
27
|
|
|
25
28
|
```tsx
|
|
26
29
|
import { primeDisplayScaleCompensation } from "react-display-scale-engine/core";
|
|
@@ -29,56 +32,54 @@ import { DisplayScaleProvider } from "react-display-scale-engine/react";
|
|
|
29
32
|
primeDisplayScaleCompensation();
|
|
30
33
|
|
|
31
34
|
export function AppRoot() {
|
|
32
|
-
return
|
|
35
|
+
return (
|
|
36
|
+
<DisplayScaleProvider>
|
|
37
|
+
<App />
|
|
38
|
+
</DisplayScaleProvider>
|
|
39
|
+
);
|
|
33
40
|
}
|
|
34
41
|
```
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
## MUI overlays: recommended hook API
|
|
37
44
|
|
|
38
|
-
|
|
39
|
-
import { DisplayScaleMenu } from "react-display-scale-engine/mui";
|
|
45
|
+
Keep using MUI's native components. The hook supplies the compensated props; spread them onto the matching MUI component.
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
See [the engine guide](docs/display-scale-engine.md) for policy, limitations, and migration guidance.
|
|
47
|
+
```tsx
|
|
48
|
+
import { Menu, MenuItem } from "@mui/material";
|
|
49
|
+
import { useMuiDisplayScaleMenu } from "react-display-scale-engine/mui";
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
function ActionsMenu({ anchorEl, open, close }) {
|
|
52
|
+
const { menuProps } = useMuiDisplayScaleMenu({ anchorEl, open });
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
return (
|
|
55
|
+
<Menu {...menuProps} onClose={close}>
|
|
56
|
+
<MenuItem onClick={close}>Action</MenuItem>
|
|
57
|
+
</Menu>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
55
60
|
```
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
Use the corresponding hook for each MUI component:
|
|
58
63
|
|
|
59
|
-
|
|
|
64
|
+
| MUI component | Engine hook |
|
|
60
65
|
| --- | --- |
|
|
61
|
-
| `
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
66
|
-
## Publishing
|
|
66
|
+
| `Menu` | `useMuiDisplayScaleMenu` |
|
|
67
|
+
| `Popover` | `useMuiDisplayScalePopover` |
|
|
68
|
+
| `Popper` | `useMuiDisplayScalePopper` |
|
|
69
|
+
| `Select` | `useMuiDisplayScaleSelect` |
|
|
67
70
|
|
|
68
|
-
|
|
71
|
+
`useMuiDisplayScaleSelect` also handles its internal MUI menu, including the first time the options list opens.
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
npm run release:check
|
|
72
|
-
npm run release:dry-run
|
|
73
|
-
npm run release:publish -- --otp 123456
|
|
74
|
-
```
|
|
73
|
+
Wrapper components (`DisplayScaleMenu`, `DisplayScalePopover`, `DisplayScalePopper`, and `DisplayScaleSelect`) remain available, but hooks are the preferred API for both new and existing MUI applications.
|
|
75
74
|
|
|
76
|
-
|
|
75
|
+
## Import paths
|
|
77
76
|
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
```ts
|
|
78
|
+
import { detectDisplayScale } from "react-display-scale-engine/core";
|
|
79
|
+
import { DisplayScaleProvider } from "react-display-scale-engine/react";
|
|
80
|
+
import { useMuiDisplayScalePopover } from "react-display-scale-engine/mui";
|
|
82
81
|
```
|
|
83
82
|
|
|
84
|
-
|
|
83
|
+
The package root (`react-display-scale-engine`) also exports all public APIs for compatibility. Prefer the explicit paths above for new code.
|
|
84
|
+
|
|
85
|
+
For engine behavior, limitations, and migration examples, see [the guide](docs/display-scale-engine.md).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useDisplayScaleCompensation, useDisplayScaleAnchor } from './chunk-F5AFPMQC.js';
|
|
2
2
|
import { Menu, Popover, Popper, Select } from '@mui/material';
|
|
3
3
|
import { useTheme } from '@mui/material/styles';
|
|
4
|
-
import { useCallback, useLayoutEffect, useRef, useMemo,
|
|
4
|
+
import { useCallback, useLayoutEffect, useRef, useMemo, useState, useId } from 'react';
|
|
5
5
|
import { jsx } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
// src/createMuiDisplayScaleComponents.ts
|
|
@@ -88,6 +88,7 @@ function resolveOrigins(anchorOrigin, transformOrigin, anchorWidth, anchorHeight
|
|
|
88
88
|
function useDisplayScalePopoverPosition({
|
|
89
89
|
open,
|
|
90
90
|
overlayRootRef,
|
|
91
|
+
overlayRoot: mountedOverlayRoot,
|
|
91
92
|
anchorElement,
|
|
92
93
|
anchorReference,
|
|
93
94
|
anchorPosition,
|
|
@@ -98,7 +99,7 @@ function useDisplayScalePopoverPosition({
|
|
|
98
99
|
const { compensation, layoutExtents, rootRef } = useDisplayScaleCompensation();
|
|
99
100
|
const correctPosition = useCallback(() => {
|
|
100
101
|
if (!open || !compensation.active || !layoutExtents) return;
|
|
101
|
-
const overlayRoot = overlayRootRef.current;
|
|
102
|
+
const overlayRoot = mountedOverlayRoot ?? overlayRootRef.current;
|
|
102
103
|
const paper = overlayRoot?.querySelector(".MuiPopover-paper");
|
|
103
104
|
if (!paper) return;
|
|
104
105
|
const zoom = compensation.zoom > 0 ? compensation.zoom : 1;
|
|
@@ -140,6 +141,7 @@ function useDisplayScalePopoverPosition({
|
|
|
140
141
|
compensation.zoom,
|
|
141
142
|
layoutExtents,
|
|
142
143
|
marginThreshold,
|
|
144
|
+
mountedOverlayRoot,
|
|
143
145
|
open,
|
|
144
146
|
overlayRootRef,
|
|
145
147
|
rootRef,
|
|
@@ -160,7 +162,10 @@ function useDisplayScalePopoverPosition({
|
|
|
160
162
|
};
|
|
161
163
|
}, [correctPosition, open]);
|
|
162
164
|
}
|
|
163
|
-
function
|
|
165
|
+
function useMuiDisplayScaleMenu({
|
|
166
|
+
anchorEl,
|
|
167
|
+
...props
|
|
168
|
+
}) {
|
|
164
169
|
const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);
|
|
165
170
|
const { compensation, rootRef } = useDisplayScaleCompensation();
|
|
166
171
|
const theme = useTheme();
|
|
@@ -194,9 +199,8 @@ function DisplayScaleMenu({ anchorEl, ...props }) {
|
|
|
194
199
|
transformOrigin,
|
|
195
200
|
marginThreshold: props.marginThreshold
|
|
196
201
|
});
|
|
197
|
-
return
|
|
198
|
-
|
|
199
|
-
{
|
|
202
|
+
return {
|
|
203
|
+
menuProps: {
|
|
200
204
|
...props,
|
|
201
205
|
ref: overlayRootRef,
|
|
202
206
|
anchorEl: displayScaleAnchor,
|
|
@@ -205,9 +209,13 @@ function DisplayScaleMenu({ anchorEl, ...props }) {
|
|
|
205
209
|
container: rootRef.current ?? document.body,
|
|
206
210
|
transformOrigin
|
|
207
211
|
}
|
|
208
|
-
|
|
212
|
+
};
|
|
209
213
|
}
|
|
210
|
-
function
|
|
214
|
+
function DisplayScaleMenu(props) {
|
|
215
|
+
const { menuProps } = useMuiDisplayScaleMenu(props);
|
|
216
|
+
return /* @__PURE__ */ jsx(Menu, { ...menuProps });
|
|
217
|
+
}
|
|
218
|
+
function useMuiDisplayScalePopover({
|
|
211
219
|
anchorEl,
|
|
212
220
|
...props
|
|
213
221
|
}) {
|
|
@@ -224,15 +232,18 @@ function DisplayScalePopover({
|
|
|
224
232
|
transformOrigin: props.transformOrigin,
|
|
225
233
|
marginThreshold: props.marginThreshold
|
|
226
234
|
});
|
|
227
|
-
return
|
|
228
|
-
|
|
229
|
-
{
|
|
235
|
+
return {
|
|
236
|
+
popoverProps: {
|
|
230
237
|
...props,
|
|
231
238
|
ref: overlayRootRef,
|
|
232
239
|
anchorEl: displayScaleAnchor,
|
|
233
240
|
container: rootRef.current ?? document.body
|
|
234
241
|
}
|
|
235
|
-
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function DisplayScalePopover(props) {
|
|
245
|
+
const { popoverProps } = useMuiDisplayScalePopover(props);
|
|
246
|
+
return /* @__PURE__ */ jsx(Popover, { ...popoverProps });
|
|
236
247
|
}
|
|
237
248
|
var defaultDisplayScalePopperModifiers = [
|
|
238
249
|
{
|
|
@@ -255,25 +266,31 @@ function scalePopperOffsets(modifiers, zoom) {
|
|
|
255
266
|
return { ...modifier, options: { ...options, offset } };
|
|
256
267
|
});
|
|
257
268
|
}
|
|
258
|
-
function
|
|
269
|
+
function useMuiDisplayScalePopper({
|
|
270
|
+
anchorEl,
|
|
271
|
+
...props
|
|
272
|
+
}) {
|
|
259
273
|
const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);
|
|
260
274
|
const { compensation, rootRef } = useDisplayScaleCompensation();
|
|
261
275
|
const modifiers = useMemo(
|
|
262
276
|
() => scalePopperOffsets(props.modifiers ?? defaultDisplayScalePopperModifiers, compensation.zoom),
|
|
263
277
|
[compensation.zoom, props.modifiers]
|
|
264
278
|
);
|
|
265
|
-
return
|
|
266
|
-
|
|
267
|
-
{
|
|
279
|
+
return {
|
|
280
|
+
popperProps: {
|
|
268
281
|
...props,
|
|
269
282
|
anchorEl: displayScaleAnchor,
|
|
270
283
|
container: rootRef.current ?? document.body,
|
|
271
284
|
modifiers,
|
|
272
285
|
style: { ...props.style, zoom: 1 }
|
|
273
286
|
}
|
|
274
|
-
|
|
287
|
+
};
|
|
275
288
|
}
|
|
276
|
-
function
|
|
289
|
+
function DisplayScalePopper(props) {
|
|
290
|
+
const { popperProps } = useMuiDisplayScalePopper(props);
|
|
291
|
+
return /* @__PURE__ */ jsx(Popper, { ...popperProps });
|
|
292
|
+
}
|
|
293
|
+
function useMuiDisplayScaleSelect(props) {
|
|
277
294
|
const {
|
|
278
295
|
MenuProps,
|
|
279
296
|
SelectDisplayProps,
|
|
@@ -285,10 +302,15 @@ function DisplayScaleSelect(props) {
|
|
|
285
302
|
} = props;
|
|
286
303
|
const { rootRef } = useDisplayScaleCompensation();
|
|
287
304
|
const menuRootRef = useRef(null);
|
|
305
|
+
const [menuRootElement, setMenuRootElement] = useState(null);
|
|
288
306
|
const selectId = useId();
|
|
289
307
|
const [anchorElement, setAnchorElement] = useState(null);
|
|
290
308
|
const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));
|
|
291
309
|
const open = openProp ?? uncontrolledOpen;
|
|
310
|
+
const handleMenuRootRef = useCallback((element) => {
|
|
311
|
+
menuRootRef.current = element;
|
|
312
|
+
setMenuRootElement(element);
|
|
313
|
+
}, []);
|
|
292
314
|
useLayoutEffect(() => {
|
|
293
315
|
if (!open) {
|
|
294
316
|
setAnchorElement(null);
|
|
@@ -315,6 +337,7 @@ function DisplayScaleSelect(props) {
|
|
|
315
337
|
useDisplayScalePopoverPosition({
|
|
316
338
|
open,
|
|
317
339
|
overlayRootRef: menuRootRef,
|
|
340
|
+
overlayRoot: menuRootElement,
|
|
318
341
|
anchorElement,
|
|
319
342
|
anchorOrigin: { vertical: "bottom", horizontal: "center" },
|
|
320
343
|
transformOrigin: { vertical: "top", horizontal: "center" }
|
|
@@ -325,7 +348,10 @@ function DisplayScaleSelect(props) {
|
|
|
325
348
|
return {
|
|
326
349
|
...MenuProps,
|
|
327
350
|
container: rootRef.current ?? document.body,
|
|
328
|
-
|
|
351
|
+
// Select mounts its internal Menu only after the first open event.
|
|
352
|
+
// Store the mounted root in state so the position hook runs again once
|
|
353
|
+
// MUI has measured and rendered its paper, rather than only on reopen.
|
|
354
|
+
ref: handleMenuRootRef,
|
|
329
355
|
slotProps: {
|
|
330
356
|
...MenuProps?.slotProps,
|
|
331
357
|
paper: {
|
|
@@ -334,10 +360,9 @@ function DisplayScaleSelect(props) {
|
|
|
334
360
|
}
|
|
335
361
|
}
|
|
336
362
|
};
|
|
337
|
-
}, [MenuProps, rootRef]);
|
|
338
|
-
return
|
|
339
|
-
|
|
340
|
-
{
|
|
363
|
+
}, [MenuProps, handleMenuRootRef, rootRef]);
|
|
364
|
+
return {
|
|
365
|
+
selectProps: {
|
|
341
366
|
...selectProps,
|
|
342
367
|
open,
|
|
343
368
|
onOpen: handleOpen,
|
|
@@ -348,6 +373,24 @@ function DisplayScaleSelect(props) {
|
|
|
348
373
|
"data-ep-display-scale-select": selectId
|
|
349
374
|
}
|
|
350
375
|
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function DisplayScaleSelect(props) {
|
|
379
|
+
const { selectProps } = useMuiDisplayScaleSelect(props);
|
|
380
|
+
return /* @__PURE__ */ jsx(Select, { ...selectProps });
|
|
381
|
+
}
|
|
382
|
+
function useMuiDisplayScaleComponents() {
|
|
383
|
+
const { compensation, overlayStrategy, rootRef } = useDisplayScaleCompensation();
|
|
384
|
+
const getPortalContainer = useCallback(() => {
|
|
385
|
+
if (overlayStrategy === "document-body") return document.body;
|
|
386
|
+
return rootRef.current ?? document.body;
|
|
387
|
+
}, [overlayStrategy, rootRef]);
|
|
388
|
+
return useMemo(
|
|
389
|
+
() => createMuiDisplayScaleComponents({
|
|
390
|
+
getPortalContainer,
|
|
391
|
+
overlayZoom: overlayStrategy === "document-body" ? compensation.zoom : 1
|
|
392
|
+
}),
|
|
393
|
+
[compensation.zoom, getPortalContainer, overlayStrategy]
|
|
351
394
|
);
|
|
352
395
|
}
|
|
353
396
|
function useDisplayScaleTabsIndicator(tabsRef, value) {
|
|
@@ -408,6 +451,6 @@ var signinSplitColumnSx = {
|
|
|
408
451
|
boxSizing: "border-box"
|
|
409
452
|
};
|
|
410
453
|
|
|
411
|
-
export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator };
|
|
412
|
-
//# sourceMappingURL=chunk-
|
|
413
|
-
//# sourceMappingURL=chunk-
|
|
454
|
+
export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator, useMuiDisplayScaleComponents, useMuiDisplayScaleMenu, useMuiDisplayScalePopover, useMuiDisplayScalePopper, useMuiDisplayScaleSelect };
|
|
455
|
+
//# sourceMappingURL=chunk-47O3TJUJ.js.map
|
|
456
|
+
//# sourceMappingURL=chunk-47O3TJUJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/createMuiDisplayScaleComponents.ts","../src/useDisplayScalePopoverPosition.ts","../src/DisplayScaleMuiOverlays.tsx","../src/DisplayScaleSelect.tsx","../src/useMuiDisplayScaleComponents.ts","../src/useDisplayScaleTabsIndicator.ts","../src/displayScaleLayoutTokens.ts"],"names":["useRef","useCallback","useLayoutEffect","useMemo","jsx"],"mappings":"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","file":"chunk-47O3TJUJ.js","sourcesContent":["import type { Components, Theme } from \"@mui/material/styles\";\n\ntype PortalContainerGetter = () => HTMLElement;\n\ntype MuiDisplayScaleComponentsOptions = {\n getPortalContainer: PortalContainerGetter;\n /**\n * Overlay content is portalled outside the zoomed app root, so it needs the\n * same density correction independently.\n */\n overlayZoom?: number;\n};\n\n/**\n * MUI adapter for a compensated app root.\n *\n * Centralizing these defaults gives every MUI overlay the portal strategy and\n * density correction chosen by the display-scale engine.\n */\nexport function createMuiDisplayScaleComponents({\n getPortalContainer,\n overlayZoom = 1,\n}: MuiDisplayScaleComponentsOptions): Components<Theme> {\n const zoom = overlayZoom > 0 ? overlayZoom : 1;\n const resolveContainerZoom = (container: unknown): number => {\n if (typeof document === \"undefined\") return zoom;\n const resolvedContainer =\n typeof container === \"function\" ? (container as PortalContainerGetter)() : container;\n // MUI does not always expose the resolved portal node through ownerState.\n // Missing means the component will use its default (document.body).\n return resolvedContainer == null || resolvedContainer === document.body ? zoom : 1;\n };\n\n return {\n MuiModal: {\n defaultProps: {\n container: getPortalContainer,\n },\n },\n MuiDialog: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiPopover: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiPopper: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n root: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiDrawer: {\n defaultProps: {\n // Drawer inherits Modal props. Supplying the portal host here (rather\n // than only through MuiModal) also covers Drawer internals directly.\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => {\n // Permanent/persistent drawers live inside the compensated root and\n // must not be zoomed a second time. Temporary drawers are portalled\n // to document.body, so their paper needs the overlay density zoom.\n if (ownerState.variant !== \"temporary\") return {};\n\n const isVertical = ownerState.anchor === \"left\" || ownerState.anchor === \"right\";\n return isVertical\n ? {\n zoom,\n height: \"var(--ep-display-scale-overlay-viewport-height, 100dvh)\",\n maxHeight: \"var(--ep-display-scale-overlay-viewport-height, 100dvh)\",\n }\n : {\n zoom,\n width: \"var(--ep-display-scale-overlay-viewport-width, 100dvw)\",\n maxWidth: \"var(--ep-display-scale-overlay-viewport-width, 100dvw)\",\n };\n },\n },\n },\n };\n}\n","import type { PopoverOrigin, PopoverProps } from \"@mui/material\";\nimport { type RefObject, useCallback, useLayoutEffect } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\n\ntype DisplayScalePopoverPositionOptions = {\n open: boolean;\n overlayRootRef: RefObject<HTMLDivElement | null>;\n /**\n * The mounted overlay root. A ref assignment alone does not trigger a\n * render, so callers with lazily-mounted overlays (notably MUI Select)\n * provide this state value to schedule the first correction after mount.\n */\n overlayRoot?: HTMLDivElement | null;\n anchorElement: HTMLElement | null;\n anchorReference?: PopoverProps[\"anchorReference\"];\n anchorPosition?: PopoverProps[\"anchorPosition\"];\n anchorOrigin?: PopoverProps[\"anchorOrigin\"];\n transformOrigin?: PopoverProps[\"transformOrigin\"];\n marginThreshold?: PopoverProps[\"marginThreshold\"];\n};\n\nfunction resolveOffset(\n origin: number | \"top\" | \"center\" | \"bottom\" | \"left\" | \"right\" | undefined,\n size: number,\n): number {\n if (typeof origin === \"number\") return origin;\n if (origin === \"center\") return size / 2;\n if (origin === \"bottom\" || origin === \"right\") return size;\n return 0;\n}\n\nfunction resolveOrigins(\n anchorOrigin: PopoverOrigin | undefined,\n transformOrigin: PopoverOrigin | undefined,\n anchorWidth: number,\n anchorHeight: number,\n paperWidth: number,\n paperHeight: number,\n): { anchorX: number; anchorY: number; transformX: number; transformY: number } {\n const resolvedAnchorOrigin = anchorOrigin ?? { vertical: \"top\", horizontal: \"left\" };\n const resolvedTransformOrigin = transformOrigin ?? { vertical: \"top\", horizontal: \"left\" };\n\n return {\n anchorX: resolveOffset(resolvedAnchorOrigin.horizontal, anchorWidth),\n anchorY: resolveOffset(resolvedAnchorOrigin.vertical, anchorHeight),\n transformX: resolveOffset(resolvedTransformOrigin.horizontal, paperWidth),\n transformY: resolveOffset(resolvedTransformOrigin.vertical, paperHeight),\n };\n}\n\n/**\n * Replaces only MUI Popover's physical-viewport collision pass. MUI still\n * owns focus, transition and anchor semantics; this writes the final logical\n * top/left inside the compensated portal root.\n */\nexport function useDisplayScalePopoverPosition({\n open,\n overlayRootRef,\n overlayRoot: mountedOverlayRoot,\n anchorElement,\n anchorReference,\n anchorPosition,\n anchorOrigin,\n transformOrigin,\n marginThreshold = 16,\n}: DisplayScalePopoverPositionOptions): void {\n const { compensation, layoutExtents, rootRef } = useDisplayScaleCompensation();\n\n const correctPosition = useCallback(() => {\n if (!open || !compensation.active || !layoutExtents) return;\n\n const overlayRoot = mountedOverlayRoot ?? overlayRootRef.current;\n const paper = overlayRoot?.querySelector<HTMLElement>(\".MuiPopover-paper\");\n if (!paper) return;\n\n const zoom = compensation.zoom > 0 ? compensation.zoom : 1;\n const rootRect = rootRef.current?.getBoundingClientRect();\n const rootLeft = rootRect?.left ?? 0;\n const rootTop = rootRect?.top ?? 0;\n const anchorRect = anchorElement?.getBoundingClientRect();\n const anchorX =\n anchorReference === \"anchorPosition\" && anchorPosition\n ? (anchorPosition.left - rootLeft) / zoom\n : anchorRect\n ? (anchorRect.left - rootLeft) / zoom\n : null;\n const anchorY =\n anchorReference === \"anchorPosition\" && anchorPosition\n ? (anchorPosition.top - rootTop) / zoom\n : anchorRect\n ? (anchorRect.top - rootTop) / zoom\n : null;\n if (anchorX == null || anchorY == null) return;\n\n const paperWidth = paper.offsetWidth;\n const paperHeight = paper.offsetHeight;\n const {\n anchorX: originX,\n anchorY: originY,\n transformX,\n transformY,\n } = resolveOrigins(\n anchorOrigin,\n transformOrigin,\n anchorRect ? anchorRect.width / zoom : 0,\n anchorRect ? anchorRect.height / zoom : 0,\n paperWidth,\n paperHeight,\n );\n const margin = typeof marginThreshold === \"number\" ? marginThreshold : 16;\n const maxLeft = Math.max(margin, layoutExtents.layoutWidthPx - margin - paperWidth);\n const maxTop = Math.max(margin, layoutExtents.layoutHeightPx - margin - paperHeight);\n const left = Math.min(maxLeft, Math.max(margin, anchorX + originX - transformX));\n const top = Math.min(maxTop, Math.max(margin, anchorY + originY - transformY));\n\n paper.style.left = `${Math.round(left)}px`;\n paper.style.top = `${Math.round(top)}px`;\n }, [\n anchorElement,\n anchorOrigin,\n anchorPosition,\n anchorReference,\n compensation.active,\n compensation.zoom,\n layoutExtents,\n marginThreshold,\n mountedOverlayRoot,\n open,\n overlayRootRef,\n rootRef,\n transformOrigin,\n ]);\n\n useLayoutEffect(() => {\n if (!open) return;\n\n const firstFrame = window.requestAnimationFrame(() => {\n correctPosition();\n window.requestAnimationFrame(correctPosition);\n });\n window.addEventListener(\"resize\", correctPosition);\n window.addEventListener(\"scroll\", correctPosition, true);\n\n return () => {\n window.cancelAnimationFrame(firstFrame);\n window.removeEventListener(\"resize\", correctPosition);\n window.removeEventListener(\"scroll\", correctPosition, true);\n };\n }, [correctPosition, open]);\n}\n","import type { MenuProps, PopoverProps, PopperProps } from \"@mui/material\";\nimport { Menu, Popover, Popper } from \"@mui/material\";\nimport { useTheme } from \"@mui/material/styles\";\nimport type {\n OffsetsFunction,\n Options as PopperOffsetOptions,\n} from \"@popperjs/core/lib/modifiers/offset\";\nimport { type ReactElement, useMemo, useRef } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { useDisplayScaleAnchor } from \"./useDisplayScaleAnchor\";\nimport { useDisplayScalePopoverPosition } from \"./useDisplayScalePopoverPosition\";\n\ntype NativeAnchorProps = {\n anchorEl?: HTMLElement | null;\n};\n\nexport type DisplayScaleMenuProps = Omit<MenuProps, \"anchorEl\"> & NativeAnchorProps;\n\nexport type MuiDisplayScaleMenuAdapter = {\n /** Spread these props onto MUI's native `<Menu />`. */\n menuProps: MenuProps;\n};\n\n/** Non-wrapper display-scale adapter for MUI's native `<Menu />`. */\nexport function useMuiDisplayScaleMenu({\n anchorEl,\n ...props\n}: DisplayScaleMenuProps): MuiDisplayScaleMenuAdapter {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { compensation, rootRef } = useDisplayScaleCompensation();\n const theme = useTheme();\n const overlayRootRef = useRef<HTMLDivElement>(null);\n const horizontalOrigin = theme.direction === \"rtl\" ? \"right\" : \"left\";\n const anchorOrigin = props.anchorOrigin ?? {\n vertical: \"bottom\",\n horizontal: horizontalOrigin,\n };\n const transformOrigin = props.transformOrigin ?? {\n vertical: \"top\",\n horizontal: horizontalOrigin,\n };\n const anchorPosition = useMemo(() => {\n if (props.anchorReference !== \"anchorPosition\" || !props.anchorPosition) {\n return props.anchorPosition;\n }\n const zoom = compensation.zoom > 0 ? compensation.zoom : 1;\n return {\n top: props.anchorPosition.top / zoom,\n left: props.anchorPosition.left / zoom,\n };\n }, [compensation.zoom, props.anchorPosition, props.anchorReference]);\n useDisplayScalePopoverPosition({\n open: props.open,\n overlayRootRef,\n anchorElement: anchorEl ?? null,\n anchorReference: props.anchorReference,\n anchorPosition: props.anchorPosition,\n anchorOrigin,\n transformOrigin,\n marginThreshold: props.marginThreshold,\n });\n\n return {\n menuProps: {\n ...props,\n ref: overlayRootRef,\n anchorEl: displayScaleAnchor,\n anchorPosition,\n anchorOrigin,\n container: rootRef.current ?? document.body,\n transformOrigin,\n },\n };\n}\n\n/** MUI Menu wrapper — prefer `useMuiDisplayScaleMenu` when retaining `<Menu />`. */\nexport function DisplayScaleMenu(props: DisplayScaleMenuProps): ReactElement {\n const { menuProps } = useMuiDisplayScaleMenu(props);\n return <Menu {...menuProps} />;\n}\n\nexport type DisplayScalePopoverProps = Omit<PopoverProps, \"anchorEl\"> & NativeAnchorProps;\n\nexport type MuiDisplayScalePopoverAdapter = {\n /** Spread these props onto MUI's native `<Popover />`. */\n popoverProps: PopoverProps;\n};\n\n/** Non-wrapper display-scale adapter for MUI's native `<Popover />`. */\nexport function useMuiDisplayScalePopover({\n anchorEl,\n ...props\n}: DisplayScalePopoverProps): MuiDisplayScalePopoverAdapter {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { rootRef } = useDisplayScaleCompensation();\n const overlayRootRef = useRef<HTMLDivElement>(null);\n useDisplayScalePopoverPosition({\n open: props.open,\n overlayRootRef,\n anchorElement: anchorEl ?? null,\n anchorReference: props.anchorReference,\n anchorPosition: props.anchorPosition,\n anchorOrigin: props.anchorOrigin,\n transformOrigin: props.transformOrigin,\n marginThreshold: props.marginThreshold,\n });\n return {\n popoverProps: {\n ...props,\n ref: overlayRootRef,\n anchorEl: displayScaleAnchor,\n container: rootRef.current ?? document.body,\n },\n };\n}\n\n/** MUI Popover wrapper — prefer `useMuiDisplayScalePopover` when retaining `<Popover />`. */\nexport function DisplayScalePopover(props: DisplayScalePopoverProps): ReactElement {\n const { popoverProps } = useMuiDisplayScalePopover(props);\n return <Popover {...popoverProps} />;\n}\n\nexport type DisplayScalePopperProps = Omit<PopperProps, \"anchorEl\"> & NativeAnchorProps;\n\nexport type MuiDisplayScalePopperAdapter = {\n /** Spread these props onto MUI's native `<Popper />`. */\n popperProps: PopperProps;\n};\n\nconst defaultDisplayScalePopperModifiers: PopperProps[\"modifiers\"] = [\n {\n name: \"offset\",\n options: { offset: [0, 8] },\n },\n];\n\nfunction scalePopperOffsets(\n modifiers: PopperProps[\"modifiers\"],\n zoom: number,\n): PopperProps[\"modifiers\"] {\n if (!modifiers || zoom === 1) return modifiers;\n\n return modifiers.map((modifier) => {\n if (modifier.name !== \"offset\") return modifier;\n\n const options = modifier.options as Partial<PopperOffsetOptions> | undefined;\n const rawOffset = options?.offset;\n if (!rawOffset) return modifier;\n\n const scaleValue = (value: number | null | undefined) => (value == null ? value : value / zoom);\n const offset: PopperOffsetOptions[\"offset\"] =\n typeof rawOffset === \"function\"\n ? (args: Parameters<OffsetsFunction>[0]) => {\n const [skidding, distance] = rawOffset(args);\n return [scaleValue(skidding), scaleValue(distance)];\n }\n : [scaleValue(rawOffset[0]), scaleValue(rawOffset[1])];\n\n return { ...modifier, options: { ...options, offset } };\n });\n}\n\n/** Non-wrapper display-scale adapter for MUI's native `<Popper />`. */\nexport function useMuiDisplayScalePopper({\n anchorEl,\n ...props\n}: DisplayScalePopperProps): MuiDisplayScalePopperAdapter {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { compensation, rootRef } = useDisplayScaleCompensation();\n const modifiers = useMemo(\n () =>\n scalePopperOffsets(props.modifiers ?? defaultDisplayScalePopperModifiers, compensation.zoom),\n [compensation.zoom, props.modifiers],\n );\n return {\n popperProps: {\n ...props,\n anchorEl: displayScaleAnchor,\n container: rootRef.current ?? document.body,\n modifiers,\n style: { ...props.style, zoom: 1 },\n },\n };\n}\n\n/** MUI Popper wrapper — prefer `useMuiDisplayScalePopper` when retaining `<Popper />`. */\nexport function DisplayScalePopper(props: DisplayScalePopperProps): ReactElement {\n const { popperProps } = useMuiDisplayScalePopper(props);\n return <Popper {...popperProps} />;\n}\n","import type { MenuProps, SelectProps } from \"@mui/material\";\nimport { Select } from \"@mui/material\";\nimport type { ReactElement, SyntheticEvent } from \"react\";\nimport { useCallback, useId, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { useDisplayScalePopoverPosition } from \"./useDisplayScalePopoverPosition\";\n\nexport type MuiDisplayScaleSelectAdapter<Value = unknown> = {\n /** Spread these props onto MUI's native `<Select />`. */\n selectProps: SelectProps<Value>;\n};\n\n/**\n * MUI Select adapter for the display-scale engine. Select creates its Menu\n * internally, so it cannot use DisplayScaleMenu directly.\n */\nexport function useMuiDisplayScaleSelect<Value = unknown>(\n props: SelectProps<Value>,\n): MuiDisplayScaleSelectAdapter<Value> {\n const {\n MenuProps,\n SelectDisplayProps,\n defaultOpen,\n onClose,\n onOpen,\n open: openProp,\n ...selectProps\n } = props;\n const { rootRef } = useDisplayScaleCompensation();\n const menuRootRef = useRef<HTMLDivElement>(null);\n const [menuRootElement, setMenuRootElement] = useState<HTMLDivElement | null>(null);\n const selectId = useId();\n const [anchorElement, setAnchorElement] = useState<HTMLElement | null>(null);\n const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));\n const open = openProp ?? uncontrolledOpen;\n\n const handleMenuRootRef = useCallback((element: HTMLDivElement | null) => {\n menuRootRef.current = element;\n setMenuRootElement(element);\n }, []);\n\n useLayoutEffect(() => {\n if (!open) {\n setAnchorElement(null);\n return;\n }\n setAnchorElement(\n document.querySelector<HTMLElement>(`[data-ep-display-scale-select=\"${selectId}\"]`),\n );\n }, [open, selectId]);\n\n const handleOpen = useCallback(\n (event: SyntheticEvent) => {\n if (openProp === undefined) setUncontrolledOpen(true);\n onOpen?.(event);\n },\n [onOpen, openProp],\n );\n const handleClose = useCallback(\n (event: SyntheticEvent) => {\n if (openProp === undefined) setUncontrolledOpen(false);\n onClose?.(event);\n },\n [onClose, openProp],\n );\n\n useDisplayScalePopoverPosition({\n open,\n overlayRootRef: menuRootRef,\n overlayRoot: menuRootElement,\n anchorElement,\n anchorOrigin: { vertical: \"bottom\", horizontal: \"center\" },\n transformOrigin: { vertical: \"top\", horizontal: \"center\" },\n });\n\n const menuProps = useMemo((): Partial<MenuProps> => {\n const paperSlot = MenuProps?.slotProps?.paper;\n const resolvedPaperSlot = typeof paperSlot === \"function\" ? undefined : paperSlot;\n\n return {\n ...MenuProps,\n container: rootRef.current ?? document.body,\n // Select mounts its internal Menu only after the first open event.\n // Store the mounted root in state so the position hook runs again once\n // MUI has measured and rendered its paper, rather than only on reopen.\n ref: handleMenuRootRef,\n slotProps: {\n ...MenuProps?.slotProps,\n paper: {\n ...resolvedPaperSlot,\n style: { ...resolvedPaperSlot?.style, zoom: 1 },\n },\n },\n };\n }, [MenuProps, handleMenuRootRef, rootRef]);\n\n return {\n selectProps: {\n ...selectProps,\n open,\n onOpen: handleOpen,\n onClose: handleClose,\n MenuProps: menuProps,\n SelectDisplayProps: {\n ...SelectDisplayProps,\n \"data-ep-display-scale-select\": selectId,\n } as SelectProps<Value>[\"SelectDisplayProps\"],\n },\n };\n}\n\n/** MUI Select wrapper — prefer `useMuiDisplayScaleSelect` when retaining `<Select />`. */\nexport function DisplayScaleSelect<Value = unknown>(props: SelectProps<Value>): ReactElement {\n const { selectProps } = useMuiDisplayScaleSelect(props);\n return <Select<Value> {...selectProps} />;\n}\n","import type { Components, Theme } from \"@mui/material/styles\";\nimport { useCallback, useMemo } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { createMuiDisplayScaleComponents } from \"./createMuiDisplayScaleComponents\";\n\n/**\n * React adapter for global MUI overlay defaults. Use inside an existing\n * `DisplayScaleProvider`, then pass the result to `createTheme`.\n */\nexport function useMuiDisplayScaleComponents(): Components<Theme> {\n const { compensation, overlayStrategy, rootRef } = useDisplayScaleCompensation();\n\n const getPortalContainer = useCallback((): HTMLElement => {\n if (overlayStrategy === \"document-body\") return document.body;\n return rootRef.current ?? document.body;\n }, [overlayStrategy, rootRef]);\n\n return useMemo(\n () =>\n createMuiDisplayScaleComponents({\n getPortalContainer,\n overlayZoom: overlayStrategy === \"document-body\" ? compensation.zoom : 1,\n }),\n [compensation.zoom, getPortalContainer, overlayStrategy],\n );\n}\n","import { type RefObject, useLayoutEffect } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\n\n/**\n * Keeps MUI Tabs' indicator in the compensated root's logical coordinate\n * space. MUI normally uses getBoundingClientRect(), whose values are already\n * zoomed and therefore too small for a logical layout container.\n */\nexport function useDisplayScaleTabsIndicator(\n tabsRef: RefObject<HTMLDivElement | null>,\n value: unknown,\n): void {\n const { compensation } = useDisplayScaleCompensation();\n\n useLayoutEffect(() => {\n if (!compensation.active) return;\n // Re-run after the selected Tabs value changes.\n void value;\n\n const updateIndicator = (): void => {\n const tabs = tabsRef.current;\n const indicator = tabs?.querySelector<HTMLElement>(\".MuiTabs-indicator\");\n const selectedTab = tabs?.querySelector<HTMLElement>(\".MuiTab-root.Mui-selected\");\n if (!tabs || !indicator || !selectedTab) return;\n\n const isRtl = getComputedStyle(tabs).direction === \"rtl\";\n const left = selectedTab.offsetLeft;\n const width = selectedTab.offsetWidth;\n\n indicator.style.width = `${width}px`;\n if (isRtl) {\n indicator.style.left = \"auto\";\n indicator.style.right = `${tabs.offsetWidth - left - width}px`;\n } else {\n indicator.style.right = \"auto\";\n indicator.style.left = `${left}px`;\n }\n };\n\n const frame = window.requestAnimationFrame(updateIndicator);\n const observer = new ResizeObserver(updateIndicator);\n if (tabsRef.current) observer.observe(tabsRef.current);\n window.addEventListener(\"resize\", updateIndicator);\n\n return () => {\n window.cancelAnimationFrame(frame);\n observer.disconnect();\n window.removeEventListener(\"resize\", updateIndicator);\n };\n }, [compensation.active, tabsRef, value]);\n}\n","import type { SxProps, Theme } from \"@mui/material/styles\";\n\n/** Fill the compensated app tree — prefer over raw `100vh` inside `#root`. */\nexport const displayScalePageFillSx: SxProps<Theme> = {\n flex: 1,\n minHeight: 0,\n minWidth: 0,\n width: \"100%\",\n height: \"100%\",\n};\n\n/** Full-viewport auth split layout — fills the compensated app root (not `position: fixed`). */\nexport const signinViewportShellSx: SxProps<Theme> = {\n flex: 1,\n minHeight: 0,\n minWidth: 0,\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n boxSizing: \"border-box\",\n};\n\n/** 50/50 desktop columns — avoids flex min-content overflow at OS display scale. */\nexport const signinSplitColumnSx: SxProps<Theme> = {\n flex: { xs: \"0 0 auto\", md: \"1 1 0\" },\n width: { xs: \"100%\", md: \"50%\" },\n maxWidth: { xs: \"100%\", md: \"50%\" },\n minWidth: 0,\n boxSizing: \"border-box\",\n};\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { DEFAULT_DISPLAY_SCALE_THRESHOLDS, clearDisplayScaleBaseline, detectDisplayScale, evaluateDisplayScale, readDisplayScaleBaseline, recalibrateDisplayScale, resetDisplayScaleEngine, shouldApplyOsScaleCompensation, snapToWindowsOsScale, subscribeDisplayScale, writeDisplayScaleBaseline } from './core.js';
|
|
2
2
|
export { C as ComputeDisplayScaleCompensationOptions, D as DEFAULT_DISPLAY_SCALE_COMPENSATION_CONFIG, a as DetectDisplayScaleOptions, b as DisplayScaleBaseline, c as DisplayScaleCalibration, d as DisplayScaleCompensation, e as DisplayScaleCompensationConfig, f as DisplayScaleConfidence, g as DisplayScaleDetectionMethod, h as DisplayScaleEvaluation, i as DisplayScaleIssue, j as DisplayScaleLayoutExtents, k as DisplayScaleLogicalViewport, l as DisplayScaleOverlayBoundsOptions, m as DisplayScaleScreen, n as DisplayScaleSnapshot, o as DisplayScaleThresholds, p as DisplayScaleViewport, S as SubscribeDisplayScaleOptions, q as applyDisplayScaleCompensationStyles, r as applyDisplayScaleCompensationToDocument, s as clearDisplayScaleCompensationFromDocument, t as clearDisplayScaleCompensationStyles, u as computeDisplayScaleCompensation, v as getDisplayScaleLogicalViewport, w as getDisplayScaleOverlayMaxHeight, x as measureDisplayScaleLayoutExtents, y as primeDisplayScaleCompensation, z as readDisplayScaleViewportSize, A as supportsCssZoom, B as syncDisplayScaleCompensationMetadata, E as toDisplayScaleLogicalPixels } from './displayScaleLogicalViewport-Ikf-DtkQ.js';
|
|
3
3
|
export { DisplayScaleCompensationProvider, DisplayScaleOverlay, DisplayScaleOverlayStrategy, DisplayScaleProvider, DisplayScaleProviderProps, DisplayScaleVirtualAnchor, useDisplayScale, useDisplayScaleAnchor, useDisplayScaleCompensation, useDisplayScaleOverlay, useDisplayScalePortalContainer, useLogicalMaxWidth, useLogicalMinWidth } from './react.js';
|
|
4
|
-
export { DisplayScaleMenu, DisplayScaleMenuProps, DisplayScalePopover, DisplayScalePopoverProps, DisplayScalePopper, DisplayScalePopperProps, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator } from './mui.js';
|
|
4
|
+
export { DisplayScaleMenu, DisplayScaleMenuProps, DisplayScalePopover, DisplayScalePopoverProps, DisplayScalePopper, DisplayScalePopperProps, DisplayScaleSelect, MuiDisplayScaleMenuAdapter, MuiDisplayScalePopoverAdapter, MuiDisplayScalePopperAdapter, MuiDisplayScaleSelectAdapter, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator, useMuiDisplayScaleComponents, useMuiDisplayScaleMenu, useMuiDisplayScalePopover, useMuiDisplayScalePopper, useMuiDisplayScaleSelect } from './mui.js';
|
|
5
5
|
import 'react';
|
|
6
6
|
import '@mui/material/styles';
|
|
7
7
|
import '@mui/material';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import './chunk-SLNYXBHU.js';
|
|
2
2
|
export { useDisplayScaleOverlay, useLogicalMaxWidth, useLogicalMinWidth } from './chunk-64EBQUAW.js';
|
|
3
3
|
export { getDisplayScaleLogicalViewport, getDisplayScaleOverlayMaxHeight, toDisplayScaleLogicalPixels } from './chunk-U3ZWHUJG.js';
|
|
4
|
-
export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator } from './chunk-
|
|
4
|
+
export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator, useMuiDisplayScaleComponents, useMuiDisplayScaleMenu, useMuiDisplayScalePopover, useMuiDisplayScalePopper, useMuiDisplayScaleSelect } from './chunk-47O3TJUJ.js';
|
|
5
5
|
export { DisplayScaleCompensationProvider, DisplayScaleProvider, useDisplayScale, useDisplayScaleAnchor, useDisplayScaleCompensation, useDisplayScalePortalContainer } from './chunk-F5AFPMQC.js';
|
|
6
6
|
export { DEFAULT_DISPLAY_SCALE_COMPENSATION_CONFIG, DEFAULT_DISPLAY_SCALE_THRESHOLDS, applyDisplayScaleCompensationStyles, applyDisplayScaleCompensationToDocument, clearDisplayScaleBaseline, clearDisplayScaleCompensationFromDocument, clearDisplayScaleCompensationStyles, computeDisplayScaleCompensation, detectDisplayScale, evaluateDisplayScale, measureDisplayScaleLayoutExtents, primeDisplayScaleCompensation, readDisplayScaleBaseline, readDisplayScaleViewportSize, recalibrateDisplayScale, resetDisplayScaleEngine, shouldApplyOsScaleCompensation, snapToWindowsOsScale, subscribeDisplayScale, supportsCssZoom, syncDisplayScaleCompensationMetadata, writeDisplayScaleBaseline } from './chunk-VWJNHWMD.js';
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
package/dist/mui.d.ts
CHANGED
|
@@ -23,24 +23,60 @@ type NativeAnchorProps = {
|
|
|
23
23
|
anchorEl?: HTMLElement | null;
|
|
24
24
|
};
|
|
25
25
|
type DisplayScaleMenuProps = Omit<MenuProps, "anchorEl"> & NativeAnchorProps;
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
type MuiDisplayScaleMenuAdapter = {
|
|
27
|
+
/** Spread these props onto MUI's native `<Menu />`. */
|
|
28
|
+
menuProps: MenuProps;
|
|
29
|
+
};
|
|
30
|
+
/** Non-wrapper display-scale adapter for MUI's native `<Menu />`. */
|
|
31
|
+
declare function useMuiDisplayScaleMenu({ anchorEl, ...props }: DisplayScaleMenuProps): MuiDisplayScaleMenuAdapter;
|
|
32
|
+
/** MUI Menu wrapper — prefer `useMuiDisplayScaleMenu` when retaining `<Menu />`. */
|
|
33
|
+
declare function DisplayScaleMenu(props: DisplayScaleMenuProps): ReactElement;
|
|
28
34
|
type DisplayScalePopoverProps = Omit<PopoverProps, "anchorEl"> & NativeAnchorProps;
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
type MuiDisplayScalePopoverAdapter = {
|
|
36
|
+
/** Spread these props onto MUI's native `<Popover />`. */
|
|
37
|
+
popoverProps: PopoverProps;
|
|
38
|
+
};
|
|
39
|
+
/** Non-wrapper display-scale adapter for MUI's native `<Popover />`. */
|
|
40
|
+
declare function useMuiDisplayScalePopover({ anchorEl, ...props }: DisplayScalePopoverProps): MuiDisplayScalePopoverAdapter;
|
|
41
|
+
/** MUI Popover wrapper — prefer `useMuiDisplayScalePopover` when retaining `<Popover />`. */
|
|
42
|
+
declare function DisplayScalePopover(props: DisplayScalePopoverProps): ReactElement;
|
|
31
43
|
type DisplayScalePopperProps = Omit<PopperProps, "anchorEl"> & NativeAnchorProps;
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
type MuiDisplayScalePopperAdapter = {
|
|
45
|
+
/** Spread these props onto MUI's native `<Popper />`. */
|
|
46
|
+
popperProps: PopperProps;
|
|
47
|
+
};
|
|
48
|
+
/** Non-wrapper display-scale adapter for MUI's native `<Popper />`. */
|
|
49
|
+
declare function useMuiDisplayScalePopper({ anchorEl, ...props }: DisplayScalePopperProps): MuiDisplayScalePopperAdapter;
|
|
50
|
+
/** MUI Popper wrapper — prefer `useMuiDisplayScalePopper` when retaining `<Popper />`. */
|
|
51
|
+
declare function DisplayScalePopper(props: DisplayScalePopperProps): ReactElement;
|
|
34
52
|
|
|
53
|
+
type MuiDisplayScaleSelectAdapter<Value = unknown> = {
|
|
54
|
+
/** Spread these props onto MUI's native `<Select />`. */
|
|
55
|
+
selectProps: SelectProps<Value>;
|
|
56
|
+
};
|
|
35
57
|
/**
|
|
36
58
|
* MUI Select adapter for the display-scale engine. Select creates its Menu
|
|
37
59
|
* internally, so it cannot use DisplayScaleMenu directly.
|
|
38
60
|
*/
|
|
61
|
+
declare function useMuiDisplayScaleSelect<Value = unknown>(props: SelectProps<Value>): MuiDisplayScaleSelectAdapter<Value>;
|
|
62
|
+
/** MUI Select wrapper — prefer `useMuiDisplayScaleSelect` when retaining `<Select />`. */
|
|
39
63
|
declare function DisplayScaleSelect<Value = unknown>(props: SelectProps<Value>): ReactElement;
|
|
40
64
|
|
|
65
|
+
/**
|
|
66
|
+
* React adapter for global MUI overlay defaults. Use inside an existing
|
|
67
|
+
* `DisplayScaleProvider`, then pass the result to `createTheme`.
|
|
68
|
+
*/
|
|
69
|
+
declare function useMuiDisplayScaleComponents(): Components<Theme>;
|
|
70
|
+
|
|
41
71
|
type DisplayScalePopoverPositionOptions = {
|
|
42
72
|
open: boolean;
|
|
43
73
|
overlayRootRef: RefObject<HTMLDivElement | null>;
|
|
74
|
+
/**
|
|
75
|
+
* The mounted overlay root. A ref assignment alone does not trigger a
|
|
76
|
+
* render, so callers with lazily-mounted overlays (notably MUI Select)
|
|
77
|
+
* provide this state value to schedule the first correction after mount.
|
|
78
|
+
*/
|
|
79
|
+
overlayRoot?: HTMLDivElement | null;
|
|
44
80
|
anchorElement: HTMLElement | null;
|
|
45
81
|
anchorReference?: PopoverProps["anchorReference"];
|
|
46
82
|
anchorPosition?: PopoverProps["anchorPosition"];
|
|
@@ -53,7 +89,7 @@ type DisplayScalePopoverPositionOptions = {
|
|
|
53
89
|
* owns focus, transition and anchor semantics; this writes the final logical
|
|
54
90
|
* top/left inside the compensated portal root.
|
|
55
91
|
*/
|
|
56
|
-
declare function useDisplayScalePopoverPosition({ open, overlayRootRef, anchorElement, anchorReference, anchorPosition, anchorOrigin, transformOrigin, marginThreshold, }: DisplayScalePopoverPositionOptions): void;
|
|
92
|
+
declare function useDisplayScalePopoverPosition({ open, overlayRootRef, overlayRoot: mountedOverlayRoot, anchorElement, anchorReference, anchorPosition, anchorOrigin, transformOrigin, marginThreshold, }: DisplayScalePopoverPositionOptions): void;
|
|
57
93
|
|
|
58
94
|
/**
|
|
59
95
|
* Keeps MUI Tabs' indicator in the compensated root's logical coordinate
|
|
@@ -69,4 +105,4 @@ declare const signinViewportShellSx: SxProps<Theme>;
|
|
|
69
105
|
/** 50/50 desktop columns — avoids flex min-content overflow at OS display scale. */
|
|
70
106
|
declare const signinSplitColumnSx: SxProps<Theme>;
|
|
71
107
|
|
|
72
|
-
export { DisplayScaleMenu, type DisplayScaleMenuProps, DisplayScalePopover, type DisplayScalePopoverProps, DisplayScalePopper, type DisplayScalePopperProps, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator };
|
|
108
|
+
export { DisplayScaleMenu, type DisplayScaleMenuProps, DisplayScalePopover, type DisplayScalePopoverProps, DisplayScalePopper, type DisplayScalePopperProps, DisplayScaleSelect, type MuiDisplayScaleMenuAdapter, type MuiDisplayScalePopoverAdapter, type MuiDisplayScalePopperAdapter, type MuiDisplayScaleSelectAdapter, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator, useMuiDisplayScaleComponents, useMuiDisplayScaleMenu, useMuiDisplayScalePopover, useMuiDisplayScalePopper, useMuiDisplayScaleSelect };
|
package/dist/mui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator } from './chunk-
|
|
1
|
+
export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator, useMuiDisplayScaleComponents, useMuiDisplayScaleMenu, useMuiDisplayScalePopover, useMuiDisplayScalePopper, useMuiDisplayScaleSelect } from './chunk-47O3TJUJ.js';
|
|
2
2
|
import './chunk-F5AFPMQC.js';
|
|
3
3
|
import './chunk-VWJNHWMD.js';
|
|
4
4
|
//# sourceMappingURL=mui.js.map
|
|
@@ -1,208 +1,238 @@
|
|
|
1
|
-
# Display
|
|
1
|
+
# Display Scale Engine guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`react-display-scale-engine` is an optional visual-density policy for React applications.
|
|
4
|
+
It targets Windows desktop users whose operating-system display scale is above 100%—for example
|
|
5
|
+
125% or 150%—when a product requires roughly the same dashboard density as Windows 100%.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
When Windows display scale is 125%, 150%, or another supported tier, the app can render toward a
|
|
7
|
-
100% visual-density target while retaining an expanded logical layout area.
|
|
7
|
+
It is not a generic replacement for responsive design and it does not force browser zoom.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
enable this behavior unless matching 100% visual density is an explicit requirement.
|
|
9
|
+
## What it solves
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
At Windows 150%, a browser usually reports a higher `devicePixelRatio`. A conventional web app
|
|
12
|
+
then renders typography, spacing, and MUI components visibly larger. That is normally correct and
|
|
13
|
+
accessible browser behavior, but dense desktop dashboards sometimes require a consistent visual
|
|
14
|
+
density across managed workstations.
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
call `recalibrateDisplayScale()` after the user resets page zoom to 100%.
|
|
19
|
-
- Native shells such as Electron can provide a more authoritative OS scale factor. A pure web app
|
|
20
|
-
cannot.
|
|
16
|
+
Applying CSS `zoom` to an app root corrects that density, but creates a second coordinate system.
|
|
17
|
+
Anchored overlays such as MUI `Menu`, `Popover`, `Popper`, `Select`, `Dialog`, and temporary
|
|
18
|
+
`Drawer` can drift, clip, or calculate a wrong viewport height. This engine manages the root
|
|
19
|
+
compensation and the overlay geometry together.
|
|
21
20
|
|
|
22
|
-
##
|
|
21
|
+
## When it has an effect
|
|
22
|
+
|
|
23
|
+
| Environment | Engine result |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| Windows desktop at 100% display scale | Inactive: no compensation wrapper and no visual change. |
|
|
26
|
+
| Windows desktop at 125%, 150%, and supported higher tiers | Active: the app root is compensated toward the configured target density. |
|
|
27
|
+
| macOS / Linux | Inactive by default. |
|
|
28
|
+
| Touch/coarse-pointer or compact viewport | Inactive by default. |
|
|
29
|
+
| Browser zoom differs from the recorded baseline | Best-effort detection; use calibration for a strict policy. |
|
|
30
|
+
|
|
31
|
+
When inactive, `DisplayScaleProvider` supplies context to hooks only. It does not insert a layout
|
|
32
|
+
container or apply visual scaling, so a normal display scale preserves the host app's layout.
|
|
33
|
+
|
|
34
|
+
## Engine model
|
|
23
35
|
|
|
24
36
|
```text
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
Browser metrics + session baseline
|
|
38
|
+
│
|
|
39
|
+
▼
|
|
40
|
+
/core detection
|
|
41
|
+
policy + compensation
|
|
42
|
+
│
|
|
43
|
+
▼
|
|
44
|
+
/react DisplayScaleProvider
|
|
45
|
+
active: compensated root + logical viewport
|
|
46
|
+
inactive: context only
|
|
47
|
+
│
|
|
48
|
+
▼
|
|
49
|
+
/mui integration
|
|
50
|
+
corrected anchor + portal + final overlay placement
|
|
35
51
|
```
|
|
36
52
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
an
|
|
53
|
+
A web browser cannot perfectly separate OS display scale from browser page zoom. The engine uses
|
|
54
|
+
`devicePixelRatio`, `visualViewport`, and a session baseline to form a best-effort estimate. For a
|
|
55
|
+
strict product policy, provide an explicit action after users reset browser zoom to 100%.
|
|
40
56
|
|
|
41
|
-
##
|
|
57
|
+
## Best-practice quick start
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
<DisplayScaleProvider
|
|
51
|
-
config={{
|
|
52
|
-
windowsOnly: true,
|
|
53
|
-
targetOsScale: 1,
|
|
54
|
-
minOsScaleToCompensate: 1.05,
|
|
55
|
-
requireExplicitCalibration: true,
|
|
56
|
-
}}
|
|
57
|
-
>
|
|
58
|
-
<App />
|
|
59
|
-
</DisplayScaleProvider>;
|
|
59
|
+
Use the subpath imports below. The root package export exists only for compatibility.
|
|
60
|
+
|
|
61
|
+
### 1. Install
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install react-display-scale-engine
|
|
60
65
|
```
|
|
61
66
|
|
|
62
|
-
###
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
confidence.
|
|
66
|
-
- `recalibrateDisplayScale()`: replaces the current session baseline when browser zoom is known
|
|
67
|
-
to be 100% and marks it as explicit calibration.
|
|
68
|
-
- `computeDisplayScaleCompensation()`: resolves whether compensation is active and the target
|
|
69
|
-
root zoom.
|
|
70
|
-
- `getDisplayScaleLogicalViewport()`: returns browser viewport and compensated-root viewport.
|
|
71
|
-
- `toDisplayScaleLogicalPixels()`: converts visible CSS-pixel geometry to root coordinates.
|
|
72
|
-
|
|
73
|
-
### React utilities
|
|
74
|
-
|
|
75
|
-
- `DisplayScaleProvider`: package-friendly name for the existing root provider.
|
|
76
|
-
- `useDisplayScale()`: subscribes to a scale snapshot.
|
|
77
|
-
- `useDisplayScaleCompensation()`: exposes the root and resolved compensation.
|
|
78
|
-
- `useDisplayScaleOverlay()`: obtains the portal container, logical viewport and overlay bounds
|
|
79
|
-
helpers, including `toOverlayContentPixels()` for a custom density-zoomed dimension.
|
|
80
|
-
- `useDisplayScaleAnchor()`: converts a native anchor to a MUI-compatible virtual anchor in the
|
|
81
|
-
compensated portal coordinate space.
|
|
82
|
-
- `useLogicalMinWidth()` / `useLogicalMaxWidth()`: evaluate app-shell breakpoints against the
|
|
83
|
-
compensated root width.
|
|
84
|
-
- `createMuiDisplayScaleComponents()`: optional MUI adapter that centralizes portal defaults for
|
|
85
|
-
Dialog, Menu/Popover and Popper.
|
|
86
|
-
|
|
87
|
-
## Overlay integration
|
|
88
|
-
|
|
89
|
-
MUI overlays must share the same coordinate system as their anchor. The theme adapter keeps legacy
|
|
90
|
-
MUI portals on `document.body` with density correction. For anchored `Menu`, `Popover` and
|
|
91
|
-
`Popper`, use the provided wrappers: they move only that overlay into the compensated root and
|
|
92
|
-
adapt the anchor rectangle relative to the engine root centrally. This avoids the `top`/`left`
|
|
93
|
-
drift caused by applying CSS zoom to an absolutely positioned body portal. The adapter also handles temporary MUI Drawers: the
|
|
94
|
-
portal host, density zoom, and inverse viewport dimension are set globally. Permanent and
|
|
95
|
-
persistent Drawers remain inside the compensated root and are deliberately untouched.
|
|
96
|
-
|
|
97
|
-
The compensated root is intentionally a visible portal host. The app content sits in a clipped
|
|
98
|
-
child layer, so a portalled overlay is bounded by the real viewport rather than an invisible
|
|
99
|
-
`overflow: hidden` root rectangle.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Use `overlayStrategy="compensated-root"` only for an app that has verified every portal and
|
|
103
|
-
anchor in logical root coordinates. `document-body` is preferred for CSS zoom roots.
|
|
104
|
-
|
|
105
|
-
Do not divide a Popper or Menu height by `compensation.zoom` in feature code. Use the overlay
|
|
106
|
-
adapter instead; it uses visible pixels for placement and converts only an overlay content
|
|
107
|
-
dimension when its density zoom requires it:
|
|
67
|
+
### 2. Prime once before React renders
|
|
68
|
+
|
|
69
|
+
In the application entry file, before `createRoot`:
|
|
108
70
|
|
|
109
71
|
```tsx
|
|
110
|
-
|
|
72
|
+
import { primeDisplayScaleCompensation } from "react-display-scale-engine/core";
|
|
111
73
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
74
|
+
primeDisplayScaleCompensation();
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 3. Add exactly one provider at the application root
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { DisplayScaleProvider } from "react-display-scale-engine/react";
|
|
81
|
+
|
|
82
|
+
export function App() {
|
|
83
|
+
return (
|
|
84
|
+
<DisplayScaleProvider>
|
|
85
|
+
<AppContent />
|
|
86
|
+
</DisplayScaleProvider>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
116
89
|
```
|
|
117
90
|
|
|
118
|
-
|
|
91
|
+
Do not add a provider around individual features, dialogs, or pages.
|
|
92
|
+
|
|
93
|
+
### 4. Add the MUI theme adapter once
|
|
94
|
+
|
|
95
|
+
Call this hook inside `DisplayScaleProvider`, then merge its result into the existing MUI theme.
|
|
119
96
|
|
|
120
97
|
```tsx
|
|
121
|
-
|
|
122
|
-
|
|
98
|
+
import { CssBaseline, ThemeProvider, createTheme } from "@mui/material";
|
|
99
|
+
import { useMemo } from "react";
|
|
100
|
+
import { useMuiDisplayScaleComponents } from "react-display-scale-engine/mui";
|
|
101
|
+
|
|
102
|
+
function AppThemeProvider({ children }: { children: React.ReactNode }) {
|
|
103
|
+
const components = useMuiDisplayScaleComponents();
|
|
104
|
+
const theme = useMemo(() => createTheme({ components }), [components]);
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<ThemeProvider theme={theme}>
|
|
108
|
+
<CssBaseline />
|
|
109
|
+
{children}
|
|
110
|
+
</ThemeProvider>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
For an existing theme, use `createTheme(baseTheme, { components })`; do not create a second theme
|
|
116
|
+
provider only for this engine.
|
|
123
117
|
|
|
124
|
-
|
|
118
|
+
### 5. Use hook adapters for anchored MUI components
|
|
119
|
+
|
|
120
|
+
This is the recommended API for new code. Your app retains native MUI components and supplies
|
|
121
|
+
engine-managed props through the matching hook.
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
import { Menu, MenuItem } from "@mui/material";
|
|
125
|
+
import { useState } from "react";
|
|
126
|
+
import { useMuiDisplayScaleMenu } from "react-display-scale-engine/mui";
|
|
127
|
+
|
|
128
|
+
function ActionsMenu() {
|
|
129
|
+
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
|
|
130
|
+
const open = Boolean(anchorEl);
|
|
131
|
+
const { menuProps } = useMuiDisplayScaleMenu({ anchorEl, open });
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<Menu {...menuProps} onClose={() => setAnchorEl(null)}>
|
|
135
|
+
<MenuItem onClick={() => setAnchorEl(null)}>Action</MenuItem>
|
|
136
|
+
</Menu>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
125
139
|
```
|
|
126
140
|
|
|
127
|
-
|
|
141
|
+
| Native MUI component | Engine hook |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `Menu` | `useMuiDisplayScaleMenu` |
|
|
144
|
+
| `Popover` | `useMuiDisplayScalePopover` |
|
|
145
|
+
| `Popper` | `useMuiDisplayScalePopper` |
|
|
146
|
+
| `Select` | `useMuiDisplayScaleSelect` |
|
|
147
|
+
|
|
148
|
+
The hook owns display-scale-specific props only: corrected anchor geometry, portal container,
|
|
149
|
+
placement correction, zoom-aware modifiers, and internal overlay refs. Keep business state,
|
|
150
|
+
children, item rendering, `onClose`, and `onChange` in the application.
|
|
151
|
+
|
|
152
|
+
## Wrapper API
|
|
153
|
+
|
|
154
|
+
For a fast migration, wrappers remain available:
|
|
128
155
|
|
|
129
156
|
```tsx
|
|
130
|
-
import { DisplayScaleMenu
|
|
157
|
+
import { DisplayScaleMenu } from "react-display-scale-engine/mui";
|
|
131
158
|
|
|
132
|
-
<DisplayScaleMenu anchorEl={anchorEl} open={
|
|
159
|
+
<DisplayScaleMenu anchorEl={anchorEl} open={open} onClose={handleClose}>
|
|
160
|
+
<MenuItem>Action</MenuItem>
|
|
161
|
+
</DisplayScaleMenu>;
|
|
133
162
|
```
|
|
134
163
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
164
|
+
`DisplayScaleMenu`, `DisplayScalePopover`, `DisplayScalePopper`, and `DisplayScaleSelect` use the
|
|
165
|
+
same hook adapters internally. Hook adapters and wrappers therefore have equivalent placement and
|
|
166
|
+
scaling behavior. Prefer hooks for new code; use wrappers when migrating a feature quickly.
|
|
167
|
+
|
|
168
|
+
## Custom overlay content
|
|
169
|
+
|
|
170
|
+
For a custom panel or scrollable list, use the logical overlay helper instead of dividing values
|
|
171
|
+
by `zoom` manually:
|
|
138
172
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
173
|
+
```tsx
|
|
174
|
+
import { useDisplayScaleOverlay } from "react-display-scale-engine/react";
|
|
175
|
+
|
|
176
|
+
const { getMaxHeightBelowAnchor } = useDisplayScaleOverlay();
|
|
177
|
+
const maxHeight = getMaxHeightBelowAnchor(anchorElement, {
|
|
178
|
+
offset: 8,
|
|
179
|
+
viewportInset: 16,
|
|
180
|
+
});
|
|
181
|
+
```
|
|
143
182
|
|
|
144
|
-
|
|
183
|
+
Apply `maxHeight` to the scrollable surface, usually with `overflowY: "auto"` and `minHeight: 0`.
|
|
145
184
|
|
|
146
|
-
|
|
147
|
-
2. Let `useDisplayScaleOverlay()` convert geometry only when the active portal strategy needs it.
|
|
148
|
-
3. Keep the anchor, portal host and overlay in the same strategy.
|
|
149
|
-
4. Give scrollable content `minHeight: 0` and put `overflowY: "auto"` on the list surface.
|
|
150
|
-
5. Prefer Popper modifiers (`offset`, `flip`, `preventOverflow`) over hard-coded
|
|
151
|
-
`position: fixed`, `top` or `right`.
|
|
185
|
+
## Responsive behavior
|
|
152
186
|
|
|
153
|
-
|
|
187
|
+
Density and responsive layout are different concerns. Browser media queries still use the raw CSS
|
|
188
|
+
viewport. When an application-shell breakpoint must stay stable between Windows 100% and 150%, use
|
|
189
|
+
the logical breakpoint hooks:
|
|
154
190
|
|
|
155
|
-
|
|
191
|
+
```tsx
|
|
192
|
+
import { useLogicalMinWidth } from "react-display-scale-engine/react";
|
|
156
193
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
- **Overlay geometry** controls portals and viewport bounds.
|
|
194
|
+
const isDesktop = useLogicalMinWidth(900);
|
|
195
|
+
```
|
|
160
196
|
|
|
161
|
-
|
|
162
|
-
same breakpoint behavior at 100% and 150% should use container queries within the compensated root
|
|
163
|
-
or `useLogicalMinWidth()` / `useLogicalMaxWidth()` for JavaScript app-shell decisions. Do not
|
|
164
|
-
assume root `zoom` changes `window.matchMedia()`.
|
|
197
|
+
Do not use the engine to make a mobile layout behave as desktop.
|
|
165
198
|
|
|
166
|
-
##
|
|
199
|
+
## Calibration and diagnostics
|
|
167
200
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
2. Add `DisplayScaleProvider` around the new app shell.
|
|
171
|
-
3. Configure the app's portal system with `createMuiDisplayScaleComponents()`.
|
|
172
|
-
4. Migrate overlays first, then fixed-position pages, then breakpoint-sensitive layouts.
|
|
173
|
-
5. For strict density lock, set `requireExplicitCalibration: true` and call
|
|
174
|
-
`recalibrateDisplayScale()` after the app confirms that browser zoom is 100%.
|
|
201
|
+
The default automatic baseline is suitable for most applications. For strict calibration, call this
|
|
202
|
+
only when browser zoom is known to be 100%:
|
|
175
203
|
|
|
176
|
-
|
|
204
|
+
```tsx
|
|
205
|
+
import { recalibrateDisplayScale } from "react-display-scale-engine/core";
|
|
177
206
|
|
|
178
|
-
|
|
179
|
-
@envidi/display-scale-core
|
|
180
|
-
@envidi/display-scale-react
|
|
181
|
-
@envidi/display-scale-mui
|
|
207
|
+
recalibrateDisplayScale();
|
|
182
208
|
```
|
|
183
209
|
|
|
184
|
-
|
|
210
|
+
`useDisplayScale()` exposes the current estimate, confidence, browser zoom, and OS scale for a
|
|
211
|
+
diagnostics or support screen. Avoid changing feature layout directly from raw scale values.
|
|
185
212
|
|
|
186
|
-
##
|
|
213
|
+
## Rules that prevent overlay bugs
|
|
187
214
|
|
|
188
|
-
|
|
215
|
+
1. Use one provider for one application root.
|
|
216
|
+
2. Import by responsibility: `/core`, `/react`, and `/mui`.
|
|
217
|
+
3. Use an engine hook or wrapper for every anchored MUI overlay; do not manually divide coordinates.
|
|
218
|
+
4. Keep anchor, portal, and overlay under the same engine strategy.
|
|
219
|
+
5. Avoid hard-coded `position: fixed`, `top`, or `left` as an overlay repair.
|
|
220
|
+
6. Test Windows 100%, 125%, and 150% with browser zoom at 100%.
|
|
189
221
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
- The engine currently uses best-effort browser-zoom detection; explicit calibration UX remains a
|
|
208
|
-
follow-up before enabling strict density lock across other apps.
|
|
222
|
+
## Compatibility and release checks
|
|
223
|
+
|
|
224
|
+
- `/core` is framework-independent.
|
|
225
|
+
- `/react` requires React 18 or later.
|
|
226
|
+
- `/mui` is tested with MUI 7 (`>=7 <8`) and Emotion peers.
|
|
227
|
+
- The root export is a compatibility entry; prefer subpaths for new application code.
|
|
228
|
+
|
|
229
|
+
Before publishing, run:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
npm run check
|
|
233
|
+
npm run release:dry-run
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`npm run check` type-checks, builds distributable artifacts, and runs a DOM consumer test. It
|
|
237
|
+
renders root and subpath APIs, wrappers, and native MUI components receiving hook adapter props.
|
|
238
|
+
This catches React Context duplication and normal-scale wrapper regressions before publishing.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-display-scale-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "React and Material UI utilities for consistent Windows display-scale rendering.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsup",
|
|
42
42
|
"typecheck": "tsc --noEmit",
|
|
43
|
-
"check": "npm run typecheck && npm run build",
|
|
43
|
+
"check": "npm run typecheck && npm run build && npm run test:consumer",
|
|
44
|
+
"test:consumer": "node ./scripts/verify-consumer-runtime.mjs",
|
|
44
45
|
"pack:check": "npm pack --dry-run",
|
|
45
46
|
"release:check": "node ./bin/rds-engine.mjs check",
|
|
46
47
|
"release:dry-run": "node ./bin/rds-engine.mjs publish --dry-run",
|
|
@@ -58,7 +59,7 @@
|
|
|
58
59
|
"peerDependencies": {
|
|
59
60
|
"@emotion/react": ">=11.0.0",
|
|
60
61
|
"@emotion/styled": ">=11.0.0",
|
|
61
|
-
"@mui/material": ">=
|
|
62
|
+
"@mui/material": ">=7.0.0 <8.0.0",
|
|
62
63
|
"@popperjs/core": ">=2.0.0",
|
|
63
64
|
"react": ">=18.0.0",
|
|
64
65
|
"react-dom": ">=18.0.0"
|
|
@@ -71,6 +72,7 @@
|
|
|
71
72
|
"@types/node": "^22.10.0",
|
|
72
73
|
"@types/react": "^19.0.0",
|
|
73
74
|
"@types/react-dom": "^19.0.0",
|
|
75
|
+
"jsdom": "^26.1.0",
|
|
74
76
|
"react": "^19.0.0",
|
|
75
77
|
"react-dom": "^19.0.0",
|
|
76
78
|
"tsup": "^8.5.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/createMuiDisplayScaleComponents.ts","../src/useDisplayScalePopoverPosition.ts","../src/DisplayScaleMuiOverlays.tsx","../src/DisplayScaleSelect.tsx","../src/useDisplayScaleTabsIndicator.ts","../src/displayScaleLayoutTokens.ts"],"names":["useRef","useLayoutEffect","useCallback","useMemo","jsx"],"mappings":"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","file":"chunk-OQRISU2D.js","sourcesContent":["import type { Components, Theme } from \"@mui/material/styles\";\n\ntype PortalContainerGetter = () => HTMLElement;\n\ntype MuiDisplayScaleComponentsOptions = {\n getPortalContainer: PortalContainerGetter;\n /**\n * Overlay content is portalled outside the zoomed app root, so it needs the\n * same density correction independently.\n */\n overlayZoom?: number;\n};\n\n/**\n * MUI adapter for a compensated app root.\n *\n * Centralizing these defaults gives every MUI overlay the portal strategy and\n * density correction chosen by the display-scale engine.\n */\nexport function createMuiDisplayScaleComponents({\n getPortalContainer,\n overlayZoom = 1,\n}: MuiDisplayScaleComponentsOptions): Components<Theme> {\n const zoom = overlayZoom > 0 ? overlayZoom : 1;\n const resolveContainerZoom = (container: unknown): number => {\n if (typeof document === \"undefined\") return zoom;\n const resolvedContainer =\n typeof container === \"function\" ? (container as PortalContainerGetter)() : container;\n // MUI does not always expose the resolved portal node through ownerState.\n // Missing means the component will use its default (document.body).\n return resolvedContainer == null || resolvedContainer === document.body ? zoom : 1;\n };\n\n return {\n MuiModal: {\n defaultProps: {\n container: getPortalContainer,\n },\n },\n MuiDialog: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiPopover: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiPopper: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n root: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiDrawer: {\n defaultProps: {\n // Drawer inherits Modal props. Supplying the portal host here (rather\n // than only through MuiModal) also covers Drawer internals directly.\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => {\n // Permanent/persistent drawers live inside the compensated root and\n // must not be zoomed a second time. Temporary drawers are portalled\n // to document.body, so their paper needs the overlay density zoom.\n if (ownerState.variant !== \"temporary\") return {};\n\n const isVertical = ownerState.anchor === \"left\" || ownerState.anchor === \"right\";\n return isVertical\n ? {\n zoom,\n height: \"var(--ep-display-scale-overlay-viewport-height, 100dvh)\",\n maxHeight: \"var(--ep-display-scale-overlay-viewport-height, 100dvh)\",\n }\n : {\n zoom,\n width: \"var(--ep-display-scale-overlay-viewport-width, 100dvw)\",\n maxWidth: \"var(--ep-display-scale-overlay-viewport-width, 100dvw)\",\n };\n },\n },\n },\n };\n}\n","import type { PopoverOrigin, PopoverProps } from \"@mui/material\";\nimport { type RefObject, useCallback, useLayoutEffect } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\n\ntype DisplayScalePopoverPositionOptions = {\n open: boolean;\n overlayRootRef: RefObject<HTMLDivElement | null>;\n anchorElement: HTMLElement | null;\n anchorReference?: PopoverProps[\"anchorReference\"];\n anchorPosition?: PopoverProps[\"anchorPosition\"];\n anchorOrigin?: PopoverProps[\"anchorOrigin\"];\n transformOrigin?: PopoverProps[\"transformOrigin\"];\n marginThreshold?: PopoverProps[\"marginThreshold\"];\n};\n\nfunction resolveOffset(\n origin: number | \"top\" | \"center\" | \"bottom\" | \"left\" | \"right\" | undefined,\n size: number,\n): number {\n if (typeof origin === \"number\") return origin;\n if (origin === \"center\") return size / 2;\n if (origin === \"bottom\" || origin === \"right\") return size;\n return 0;\n}\n\nfunction resolveOrigins(\n anchorOrigin: PopoverOrigin | undefined,\n transformOrigin: PopoverOrigin | undefined,\n anchorWidth: number,\n anchorHeight: number,\n paperWidth: number,\n paperHeight: number,\n): { anchorX: number; anchorY: number; transformX: number; transformY: number } {\n const resolvedAnchorOrigin = anchorOrigin ?? { vertical: \"top\", horizontal: \"left\" };\n const resolvedTransformOrigin = transformOrigin ?? { vertical: \"top\", horizontal: \"left\" };\n\n return {\n anchorX: resolveOffset(resolvedAnchorOrigin.horizontal, anchorWidth),\n anchorY: resolveOffset(resolvedAnchorOrigin.vertical, anchorHeight),\n transformX: resolveOffset(resolvedTransformOrigin.horizontal, paperWidth),\n transformY: resolveOffset(resolvedTransformOrigin.vertical, paperHeight),\n };\n}\n\n/**\n * Replaces only MUI Popover's physical-viewport collision pass. MUI still\n * owns focus, transition and anchor semantics; this writes the final logical\n * top/left inside the compensated portal root.\n */\nexport function useDisplayScalePopoverPosition({\n open,\n overlayRootRef,\n anchorElement,\n anchorReference,\n anchorPosition,\n anchorOrigin,\n transformOrigin,\n marginThreshold = 16,\n}: DisplayScalePopoverPositionOptions): void {\n const { compensation, layoutExtents, rootRef } = useDisplayScaleCompensation();\n\n const correctPosition = useCallback(() => {\n if (!open || !compensation.active || !layoutExtents) return;\n\n const overlayRoot = overlayRootRef.current;\n const paper = overlayRoot?.querySelector<HTMLElement>(\".MuiPopover-paper\");\n if (!paper) return;\n\n const zoom = compensation.zoom > 0 ? compensation.zoom : 1;\n const rootRect = rootRef.current?.getBoundingClientRect();\n const rootLeft = rootRect?.left ?? 0;\n const rootTop = rootRect?.top ?? 0;\n const anchorRect = anchorElement?.getBoundingClientRect();\n const anchorX =\n anchorReference === \"anchorPosition\" && anchorPosition\n ? (anchorPosition.left - rootLeft) / zoom\n : anchorRect\n ? (anchorRect.left - rootLeft) / zoom\n : null;\n const anchorY =\n anchorReference === \"anchorPosition\" && anchorPosition\n ? (anchorPosition.top - rootTop) / zoom\n : anchorRect\n ? (anchorRect.top - rootTop) / zoom\n : null;\n if (anchorX == null || anchorY == null) return;\n\n const paperWidth = paper.offsetWidth;\n const paperHeight = paper.offsetHeight;\n const {\n anchorX: originX,\n anchorY: originY,\n transformX,\n transformY,\n } = resolveOrigins(\n anchorOrigin,\n transformOrigin,\n anchorRect ? anchorRect.width / zoom : 0,\n anchorRect ? anchorRect.height / zoom : 0,\n paperWidth,\n paperHeight,\n );\n const margin = typeof marginThreshold === \"number\" ? marginThreshold : 16;\n const maxLeft = Math.max(margin, layoutExtents.layoutWidthPx - margin - paperWidth);\n const maxTop = Math.max(margin, layoutExtents.layoutHeightPx - margin - paperHeight);\n const left = Math.min(maxLeft, Math.max(margin, anchorX + originX - transformX));\n const top = Math.min(maxTop, Math.max(margin, anchorY + originY - transformY));\n\n paper.style.left = `${Math.round(left)}px`;\n paper.style.top = `${Math.round(top)}px`;\n }, [\n anchorElement,\n anchorOrigin,\n anchorPosition,\n anchorReference,\n compensation.active,\n compensation.zoom,\n layoutExtents,\n marginThreshold,\n open,\n overlayRootRef,\n rootRef,\n transformOrigin,\n ]);\n\n useLayoutEffect(() => {\n if (!open) return;\n\n const firstFrame = window.requestAnimationFrame(() => {\n correctPosition();\n window.requestAnimationFrame(correctPosition);\n });\n window.addEventListener(\"resize\", correctPosition);\n window.addEventListener(\"scroll\", correctPosition, true);\n\n return () => {\n window.cancelAnimationFrame(firstFrame);\n window.removeEventListener(\"resize\", correctPosition);\n window.removeEventListener(\"scroll\", correctPosition, true);\n };\n }, [correctPosition, open]);\n}\n","import type { MenuProps, PopoverProps, PopperProps } from \"@mui/material\";\nimport { Menu, Popover, Popper } from \"@mui/material\";\nimport { useTheme } from \"@mui/material/styles\";\nimport type {\n OffsetsFunction,\n Options as PopperOffsetOptions,\n} from \"@popperjs/core/lib/modifiers/offset\";\nimport { type ReactElement, useMemo, useRef } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { useDisplayScaleAnchor } from \"./useDisplayScaleAnchor\";\nimport { useDisplayScalePopoverPosition } from \"./useDisplayScalePopoverPosition\";\n\ntype NativeAnchorProps = {\n anchorEl?: HTMLElement | null;\n};\n\nexport type DisplayScaleMenuProps = Omit<MenuProps, \"anchorEl\"> & NativeAnchorProps;\n\n/** MUI Menu with an anchor adapted to the active portal strategy. */\nexport function DisplayScaleMenu({ anchorEl, ...props }: DisplayScaleMenuProps): ReactElement {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { compensation, rootRef } = useDisplayScaleCompensation();\n const theme = useTheme();\n const overlayRootRef = useRef<HTMLDivElement>(null);\n const horizontalOrigin = theme.direction === \"rtl\" ? \"right\" : \"left\";\n const anchorOrigin = props.anchorOrigin ?? {\n vertical: \"bottom\",\n horizontal: horizontalOrigin,\n };\n const transformOrigin = props.transformOrigin ?? {\n vertical: \"top\",\n horizontal: horizontalOrigin,\n };\n const anchorPosition = useMemo(() => {\n if (props.anchorReference !== \"anchorPosition\" || !props.anchorPosition) {\n return props.anchorPosition;\n }\n const zoom = compensation.zoom > 0 ? compensation.zoom : 1;\n return {\n top: props.anchorPosition.top / zoom,\n left: props.anchorPosition.left / zoom,\n };\n }, [compensation.zoom, props.anchorPosition, props.anchorReference]);\n useDisplayScalePopoverPosition({\n open: props.open,\n overlayRootRef,\n anchorElement: anchorEl ?? null,\n anchorReference: props.anchorReference,\n anchorPosition: props.anchorPosition,\n anchorOrigin,\n transformOrigin,\n marginThreshold: props.marginThreshold,\n });\n\n return (\n <Menu\n {...props}\n ref={overlayRootRef}\n anchorEl={displayScaleAnchor}\n anchorPosition={anchorPosition}\n anchorOrigin={anchorOrigin}\n container={rootRef.current ?? document.body}\n transformOrigin={transformOrigin}\n />\n );\n}\n\nexport type DisplayScalePopoverProps = Omit<PopoverProps, \"anchorEl\"> & NativeAnchorProps;\n\n/** MUI Popover with an anchor adapted to the active portal strategy. */\nexport function DisplayScalePopover({\n anchorEl,\n ...props\n}: DisplayScalePopoverProps): ReactElement {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { rootRef } = useDisplayScaleCompensation();\n const overlayRootRef = useRef<HTMLDivElement>(null);\n useDisplayScalePopoverPosition({\n open: props.open,\n overlayRootRef,\n anchorElement: anchorEl ?? null,\n anchorReference: props.anchorReference,\n anchorPosition: props.anchorPosition,\n anchorOrigin: props.anchorOrigin,\n transformOrigin: props.transformOrigin,\n marginThreshold: props.marginThreshold,\n });\n return (\n <Popover\n {...props}\n ref={overlayRootRef}\n anchorEl={displayScaleAnchor}\n container={rootRef.current ?? document.body}\n />\n );\n}\n\nexport type DisplayScalePopperProps = Omit<PopperProps, \"anchorEl\"> & NativeAnchorProps;\n\nconst defaultDisplayScalePopperModifiers: PopperProps[\"modifiers\"] = [\n {\n name: \"offset\",\n options: { offset: [0, 8] },\n },\n];\n\nfunction scalePopperOffsets(\n modifiers: PopperProps[\"modifiers\"],\n zoom: number,\n): PopperProps[\"modifiers\"] {\n if (!modifiers || zoom === 1) return modifiers;\n\n return modifiers.map((modifier) => {\n if (modifier.name !== \"offset\") return modifier;\n\n const options = modifier.options as Partial<PopperOffsetOptions> | undefined;\n const rawOffset = options?.offset;\n if (!rawOffset) return modifier;\n\n const scaleValue = (value: number | null | undefined) => (value == null ? value : value / zoom);\n const offset: PopperOffsetOptions[\"offset\"] =\n typeof rawOffset === \"function\"\n ? (args: Parameters<OffsetsFunction>[0]) => {\n const [skidding, distance] = rawOffset(args);\n return [scaleValue(skidding), scaleValue(distance)];\n }\n : [scaleValue(rawOffset[0]), scaleValue(rawOffset[1])];\n\n return { ...modifier, options: { ...options, offset } };\n });\n}\n\n/** MUI Popper with an anchor adapted to the active portal strategy. */\nexport function DisplayScalePopper({ anchorEl, ...props }: DisplayScalePopperProps): ReactElement {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { compensation, rootRef } = useDisplayScaleCompensation();\n const modifiers = useMemo(\n () =>\n scalePopperOffsets(props.modifiers ?? defaultDisplayScalePopperModifiers, compensation.zoom),\n [compensation.zoom, props.modifiers],\n );\n return (\n <Popper\n {...props}\n anchorEl={displayScaleAnchor}\n container={rootRef.current ?? document.body}\n modifiers={modifiers}\n style={{ ...props.style, zoom: 1 }}\n />\n );\n}\n","import type { MenuProps, SelectProps } from \"@mui/material\";\nimport { Select } from \"@mui/material\";\nimport type { ReactElement, SyntheticEvent } from \"react\";\nimport { useCallback, useId, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { useDisplayScalePopoverPosition } from \"./useDisplayScalePopoverPosition\";\n\n/**\n * MUI Select adapter for the display-scale engine. Select creates its Menu\n * internally, so it cannot use DisplayScaleMenu directly.\n */\nexport function DisplayScaleSelect<Value = unknown>(props: SelectProps<Value>): ReactElement {\n const {\n MenuProps,\n SelectDisplayProps,\n defaultOpen,\n onClose,\n onOpen,\n open: openProp,\n ...selectProps\n } = props;\n const { rootRef } = useDisplayScaleCompensation();\n const menuRootRef = useRef<HTMLDivElement>(null);\n const selectId = useId();\n const [anchorElement, setAnchorElement] = useState<HTMLElement | null>(null);\n const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));\n const open = openProp ?? uncontrolledOpen;\n\n useLayoutEffect(() => {\n if (!open) {\n setAnchorElement(null);\n return;\n }\n setAnchorElement(\n document.querySelector<HTMLElement>(`[data-ep-display-scale-select=\"${selectId}\"]`),\n );\n }, [open, selectId]);\n\n const handleOpen = useCallback(\n (event: SyntheticEvent) => {\n if (openProp === undefined) setUncontrolledOpen(true);\n onOpen?.(event);\n },\n [onOpen, openProp],\n );\n const handleClose = useCallback(\n (event: SyntheticEvent) => {\n if (openProp === undefined) setUncontrolledOpen(false);\n onClose?.(event);\n },\n [onClose, openProp],\n );\n\n useDisplayScalePopoverPosition({\n open,\n overlayRootRef: menuRootRef,\n anchorElement,\n anchorOrigin: { vertical: \"bottom\", horizontal: \"center\" },\n transformOrigin: { vertical: \"top\", horizontal: \"center\" },\n });\n\n const menuProps = useMemo((): Partial<MenuProps> => {\n const paperSlot = MenuProps?.slotProps?.paper;\n const resolvedPaperSlot = typeof paperSlot === \"function\" ? undefined : paperSlot;\n\n return {\n ...MenuProps,\n container: rootRef.current ?? document.body,\n ref: menuRootRef,\n slotProps: {\n ...MenuProps?.slotProps,\n paper: {\n ...resolvedPaperSlot,\n style: { ...resolvedPaperSlot?.style, zoom: 1 },\n },\n },\n };\n }, [MenuProps, rootRef]);\n\n return (\n <Select<Value>\n {...selectProps}\n open={open}\n onOpen={handleOpen}\n onClose={handleClose}\n MenuProps={menuProps}\n SelectDisplayProps={\n {\n ...SelectDisplayProps,\n \"data-ep-display-scale-select\": selectId,\n } as SelectProps<Value>[\"SelectDisplayProps\"]\n }\n />\n );\n}\n","import { type RefObject, useLayoutEffect } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\n\n/**\n * Keeps MUI Tabs' indicator in the compensated root's logical coordinate\n * space. MUI normally uses getBoundingClientRect(), whose values are already\n * zoomed and therefore too small for a logical layout container.\n */\nexport function useDisplayScaleTabsIndicator(\n tabsRef: RefObject<HTMLDivElement | null>,\n value: unknown,\n): void {\n const { compensation } = useDisplayScaleCompensation();\n\n useLayoutEffect(() => {\n if (!compensation.active) return;\n // Re-run after the selected Tabs value changes.\n void value;\n\n const updateIndicator = (): void => {\n const tabs = tabsRef.current;\n const indicator = tabs?.querySelector<HTMLElement>(\".MuiTabs-indicator\");\n const selectedTab = tabs?.querySelector<HTMLElement>(\".MuiTab-root.Mui-selected\");\n if (!tabs || !indicator || !selectedTab) return;\n\n const isRtl = getComputedStyle(tabs).direction === \"rtl\";\n const left = selectedTab.offsetLeft;\n const width = selectedTab.offsetWidth;\n\n indicator.style.width = `${width}px`;\n if (isRtl) {\n indicator.style.left = \"auto\";\n indicator.style.right = `${tabs.offsetWidth - left - width}px`;\n } else {\n indicator.style.right = \"auto\";\n indicator.style.left = `${left}px`;\n }\n };\n\n const frame = window.requestAnimationFrame(updateIndicator);\n const observer = new ResizeObserver(updateIndicator);\n if (tabsRef.current) observer.observe(tabsRef.current);\n window.addEventListener(\"resize\", updateIndicator);\n\n return () => {\n window.cancelAnimationFrame(frame);\n observer.disconnect();\n window.removeEventListener(\"resize\", updateIndicator);\n };\n }, [compensation.active, tabsRef, value]);\n}\n","import type { SxProps, Theme } from \"@mui/material/styles\";\n\n/** Fill the compensated app tree — prefer over raw `100vh` inside `#root`. */\nexport const displayScalePageFillSx: SxProps<Theme> = {\n flex: 1,\n minHeight: 0,\n minWidth: 0,\n width: \"100%\",\n height: \"100%\",\n};\n\n/** Full-viewport auth split layout — fills the compensated app root (not `position: fixed`). */\nexport const signinViewportShellSx: SxProps<Theme> = {\n flex: 1,\n minHeight: 0,\n minWidth: 0,\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n boxSizing: \"border-box\",\n};\n\n/** 50/50 desktop columns — avoids flex min-content overflow at OS display scale. */\nexport const signinSplitColumnSx: SxProps<Theme> = {\n flex: { xs: \"0 0 auto\", md: \"1 1 0\" },\n width: { xs: \"100%\", md: \"50%\" },\n maxWidth: { xs: \"100%\", md: \"50%\" },\n minWidth: 0,\n boxSizing: \"border-box\",\n};\n"]}
|