@matfsw/docusaurus-plantuml-plugin 1.0.0 → 1.0.2
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/CHANGELOG.md +34 -1
- package/README.md +20 -5
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/theme/PlantUmlDiagram/index.js +2 -1
- package/dist/theme/PlantUmlDiagram/styles.module.css +20 -8
- package/dist/theme/PlantUmlDiagram/useZoomPan.d.ts +3 -3
- package/dist/theme/PlantUmlDiagram/useZoomPan.js +65 -56
- package/dist/theme/PlantUmlDiagram/zoomMath.d.ts +1 -1
- package/dist/theme/PlantUmlDiagram/zoomMath.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.2]
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- The zoom buttons and keyboard shortcuts now zoom about the viewport's top-left corner rather
|
|
15
|
+
than its centre. A diagram that fits its viewport is left-aligned, so the empty space is to
|
|
16
|
+
its right and below; zooming about the centre scaled that empty space and pushed the diagram
|
|
17
|
+
off the top and left edges, most visibly on a maximized diagram. Wheel zoom continues to
|
|
18
|
+
track the pointer.
|
|
19
|
+
|
|
20
|
+
## [1.0.1]
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Maximizing a diagram no longer uses the Fullscreen API. It took the whole browser window
|
|
25
|
+
fullscreen in Firefox instead of presenting the diagram, and the `::backdrop` sits outside
|
|
26
|
+
the fullscreened element, so the page showed through behind a diagram with a transparent
|
|
27
|
+
background. The diagram now expands into an opaque in-page overlay that fills the browser
|
|
28
|
+
viewport, closed with the same button or <kbd>Escape</kbd>.
|
|
29
|
+
- The maximize control is now available in every browser. It previously required
|
|
30
|
+
`Element.requestFullscreen()` and was hidden where that is missing, notably iOS Safari.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- `data-plantuml-maximized` on the figure while a diagram fills the viewport.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- The fourth toolbar button is labelled `Maximize diagram` (was `Toggle fullscreen`) and
|
|
39
|
+
exposes `aria-pressed`. End-to-end tests selecting it by its accessible name need updating.
|
|
40
|
+
|
|
10
41
|
## [1.0.0]
|
|
11
42
|
|
|
12
43
|
First stable release. The public surface — plugin options, fence metadata flags, the `data-*`
|
|
@@ -65,6 +96,8 @@ Initial release.
|
|
|
65
96
|
opt-out.
|
|
66
97
|
- Plugin option validation that rejects unknown keys and out-of-range values at build time.
|
|
67
98
|
|
|
68
|
-
[Unreleased]: https://github.com/matf/docusaurus-plantuml-plugin/compare/v1.0.
|
|
99
|
+
[Unreleased]: https://github.com/matf/docusaurus-plantuml-plugin/compare/v1.0.2...HEAD
|
|
100
|
+
[1.0.2]: https://github.com/matf/docusaurus-plantuml-plugin/compare/v1.0.1...v1.0.2
|
|
101
|
+
[1.0.1]: https://github.com/matf/docusaurus-plantuml-plugin/compare/v1.0.0...v1.0.1
|
|
69
102
|
[1.0.0]: https://github.com/matf/docusaurus-plantuml-plugin/compare/v0.1.0...v1.0.0
|
|
70
103
|
[0.1.0]: https://github.com/matf/docusaurus-plantuml-plugin/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -257,7 +257,7 @@ pannable by default, with a small control toolbar in the top-right corner.
|
|
|
257
257
|
| Drag | Pans, once zoomed in |
|
|
258
258
|
| One finger on a touchscreen | Scrolls the page |
|
|
259
259
|
| Two-finger pinch on a touchscreen | The browser's own page zoom |
|
|
260
|
-
| Toolbar buttons | Zoom out, zoom in, reset,
|
|
260
|
+
| Toolbar buttons | Zoom out, zoom in, reset, maximize |
|
|
261
261
|
|
|
262
262
|
`Cmd` + wheel is deliberately **not** intercepted: on macOS that is the browser's own page
|
|
263
263
|
zoom, and taking it over would fight the platform.
|
|
@@ -277,9 +277,28 @@ The diagram viewport is focusable. Once focused:
|
|
|
277
277
|
Keys with Ctrl, Cmd or Alt held are left to the browser, so shortcuts such as Ctrl + 0 keep
|
|
278
278
|
working, and `Tab` always moves on — the diagram is never a keyboard trap.
|
|
279
279
|
|
|
280
|
+
The toolbar buttons and the keyboard zoom about the **top-left corner** of the viewport, while
|
|
281
|
+
the wheel zooms about the **pointer**. That difference is deliberate: a diagram rarely fills its
|
|
282
|
+
viewport, and one that fits is left-aligned, so the empty space sits to its right and below.
|
|
283
|
+
Zooming about the centre would scale that empty space and push the diagram off the top and left
|
|
284
|
+
edges — most visibly when maximized. Anchoring at the top-left grows the diagram into the empty
|
|
285
|
+
space instead and keeps it visible as long as possible.
|
|
286
|
+
|
|
280
287
|
Scale is limited to 0.25×–8×, and the view resets to 100% whenever the diagram source or the
|
|
281
288
|
site colour mode changes.
|
|
282
289
|
|
|
290
|
+
### Maximizing
|
|
291
|
+
|
|
292
|
+
The fourth toolbar button expands the diagram to fill the browser viewport, fitted to the
|
|
293
|
+
available space, over an opaque background. <kbd>Escape</kbd> or the same button restores it,
|
|
294
|
+
along with whatever zoom level you had before.
|
|
295
|
+
|
|
296
|
+
This is an in-page overlay rather than the Fullscreen API. `requestFullscreen()` takes the
|
|
297
|
+
entire browser window fullscreen in Firefox instead of presenting the element, and its
|
|
298
|
+
`::backdrop` sits outside the element so the page shows through behind the diagram. An overlay
|
|
299
|
+
has neither problem, behaves identically in every browser, and needs no capability detection —
|
|
300
|
+
so the control also works on iOS Safari, which has no element fullscreen at all.
|
|
301
|
+
|
|
283
302
|
### Turning it off
|
|
284
303
|
|
|
285
304
|
Globally:
|
|
@@ -528,8 +547,6 @@ The runtime requires:
|
|
|
528
547
|
- Pointer Events — required for drag-to-pan when `zoom` is enabled
|
|
529
548
|
- `ResizeObserver` — optional; without it zoom still works, but the view is not re-clamped when
|
|
530
549
|
the column is resized
|
|
531
|
-
- `Element.requestFullscreen()` — optional; the fullscreen button is simply not rendered where
|
|
532
|
-
it is unavailable, which includes iOS Safari
|
|
533
550
|
|
|
534
551
|
That is modern evergreen Chrome, Edge, Firefox and Safari. Internet Explorer is not
|
|
535
552
|
supported and will not be.
|
|
@@ -774,8 +791,6 @@ package.
|
|
|
774
791
|
the browser reports it as Ctrl + wheel.
|
|
775
792
|
- **Zoom state is not persisted.** Navigating away and back, or switching colour mode, returns
|
|
776
793
|
the diagram to 100%.
|
|
777
|
-
- **No fullscreen on iOS Safari.** It does not implement `Element.requestFullscreen()`, so the
|
|
778
|
-
button is not shown there.
|
|
779
794
|
|
|
780
795
|
## Troubleshooting
|
|
781
796
|
|
package/dist/constants.d.ts
CHANGED
|
@@ -26,4 +26,6 @@ export declare const DATA_ATTR: {
|
|
|
26
26
|
* the imperative zoom writes can never race React's attribute diffing.
|
|
27
27
|
*/
|
|
28
28
|
readonly zoom: "data-plantuml-zoom";
|
|
29
|
+
/** `"true"` on the figure while the diagram fills the browser viewport. */
|
|
30
|
+
readonly maximized: "data-plantuml-maximized";
|
|
29
31
|
};
|
package/dist/constants.js
CHANGED
|
@@ -138,7 +138,8 @@ export default function PlantUmlDiagram({ source, title, language, zoom: zoomPro
|
|
|
138
138
|
return (_jsxs("figure", { ref: containerRef, className: styles.figure, "aria-busy": busy || undefined, [DATA_ATTR.diagram]: language ?? 'plantuml',
|
|
139
139
|
[DATA_ATTR.status]: state.status,
|
|
140
140
|
[DATA_ATTR.theme]: dark ? 'dark' : 'light',
|
|
141
|
-
...(interactive ? { [DATA_ATTR.interactive]: 'true' } : {}),
|
|
141
|
+
...(interactive ? { [DATA_ATTR.interactive]: 'true' } : {}),
|
|
142
|
+
...(zoom.maximized ? { [DATA_ATTR.maximized]: 'true' } : {}), children: [state.status === 'ready' && state.svg !== null && !interactive && canvas, state.status === 'ready' && state.svg !== null && interactive && (_jsxs("div", { ref: zoom.stageRef, className: zoom.maximized ? `${styles.stage} ${styles.maximized}` : styles.stage, children: [_jsx("div", { ref: zoom.viewportRef, className: styles.viewport, tabIndex: 0, "aria-describedby": hintId, "aria-keyshortcuts": "Plus Minus 0 ArrowUp ArrowDown ArrowLeft ArrowRight", onKeyDown: zoom.onKeyDown, [DATA_ATTR.zoom]: '1', children: _jsx("div", { ref: zoom.layerRef, className: styles.layer, children: canvas }) }), _jsxs("div", { className: styles.toolbar, role: "group", "aria-label": `${label} zoom controls`, children: [_jsx("button", { type: "button", className: styles.toolbarButton, "aria-label": "Zoom out", onClick: zoom.zoomOut, children: _jsx("span", { "aria-hidden": "true", children: "\u2212" }) }), _jsx("button", { type: "button", className: styles.toolbarButton, "aria-label": "Zoom in", onClick: zoom.zoomIn, children: _jsx("span", { "aria-hidden": "true", children: "+" }) }), _jsx("button", { type: "button", className: styles.toolbarButton, "aria-label": "Reset zoom", onClick: zoom.reset, children: _jsx("span", { "aria-hidden": "true", children: "\u27F2" }) }), _jsx("button", { type: "button", className: styles.toolbarButton, "aria-label": "Maximize diagram", "aria-pressed": zoom.maximized, onClick: zoom.toggleMaximize, children: _jsx("span", { "aria-hidden": "true", children: zoom.maximized ? '✕' : '⛶' }) }), _jsx("span", { ref: zoom.readoutRef, className: styles.readout, "aria-hidden": "true", children: "100%" })] })] })), state.status === 'ready' && state.svg !== null && interactive && (_jsx("p", { id: hintId, className: styles.visuallyHidden, children: "Zoomable diagram. Use the zoom controls, or press plus and minus to zoom, the arrow keys to pan, and zero to reset." })), state.status !== 'ready' && state.status !== 'error' && (
|
|
142
143
|
// `aria-busy` on the figure already conveys progress; announcing every phase change
|
|
143
144
|
// would make a page with several diagrams unusable with a screen reader.
|
|
144
145
|
_jsxs("div", { className: styles.placeholder, children: [_jsx("span", { className: styles.spinner, "aria-hidden": "true" }), _jsxs("span", { children: [state.status === 'loading' ? 'Loading PlantUML runtime…' : null, state.status === 'rendering' ? `Rendering ${label}…` : null, state.status === 'idle' ? `${label} (waiting to render)` : null] })] })), state.status === 'error' && (_jsxs("div", { className: styles.error, role: "alert", children: [_jsxs("p", { className: styles.errorHeading, children: [_jsx("span", { className: styles.errorIcon, "aria-hidden": "true", children: "\u26A0" }), _jsx("span", { children: "Error: PlantUML diagram could not be rendered" })] }), _jsx("pre", { className: styles.errorMessage, children: state.error }), config?.options.showSourceOnError !== false && (_jsxs("details", { className: styles.errorDetails, children: [_jsx("summary", { children: "Show diagram source" }), _jsx("pre", { className: styles.source, children: source })] }))] })), title !== undefined && _jsx("figcaption", { className: styles.caption, children: title }), _jsx("noscript", { dangerouslySetInnerHTML: {
|
|
@@ -108,24 +108,36 @@
|
|
|
108
108
|
text-align: right;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
/*
|
|
112
|
+
* Maximized is an in-page overlay, not the Fullscreen API.
|
|
113
|
+
*
|
|
114
|
+
* `requestFullscreen()` took the whole browser window fullscreen in Firefox, and its
|
|
115
|
+
* `::backdrop` sits outside the element so the page showed through behind the diagram. A
|
|
116
|
+
* fixed overlay is fully opaque, behaves the same everywhere, and needs no capability check.
|
|
117
|
+
*/
|
|
118
|
+
.maximized {
|
|
119
|
+
position: fixed;
|
|
120
|
+
inset: 0;
|
|
121
|
+
z-index: var(--ifm-z-index-overlay, 400);
|
|
112
122
|
display: flex;
|
|
113
123
|
align-items: center;
|
|
114
124
|
justify-content: center;
|
|
115
|
-
|
|
125
|
+
/*
|
|
126
|
+
* `--ifm-background-surface-color`, not `--ifm-background-color`: Infima defines the latter
|
|
127
|
+
* as `#0000` in light mode, so the overlay would be transparent and the page would show
|
|
128
|
+
* through — which is the defect this overlay exists to fix. The surface colour is opaque in
|
|
129
|
+
* both colour modes (white / #242526), and a `var()` fallback would not have saved us here,
|
|
130
|
+
* because the variable is defined, just transparent.
|
|
131
|
+
*/
|
|
132
|
+
background: var(--ifm-background-surface-color, #ffffff);
|
|
116
133
|
}
|
|
117
134
|
|
|
118
|
-
.
|
|
135
|
+
.maximized .viewport {
|
|
119
136
|
width: 100vw;
|
|
120
137
|
height: 100dvh;
|
|
121
138
|
max-height: none;
|
|
122
139
|
}
|
|
123
140
|
|
|
124
|
-
/* The default backdrop is black, which makes a transparent light-mode diagram look broken. */
|
|
125
|
-
.stage::backdrop {
|
|
126
|
-
background: var(--ifm-background-color, #ffffff);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
141
|
.visuallyHidden {
|
|
130
142
|
position: absolute;
|
|
131
143
|
width: 1px;
|
|
@@ -8,10 +8,10 @@ export interface ZoomPanHandle {
|
|
|
8
8
|
zoomIn: () => void;
|
|
9
9
|
zoomOut: () => void;
|
|
10
10
|
reset: () => void;
|
|
11
|
-
|
|
11
|
+
toggleMaximize: () => void;
|
|
12
12
|
onKeyDown: (event: ReactKeyboardEvent<HTMLDivElement>) => void;
|
|
13
|
-
/** Whether
|
|
14
|
-
|
|
13
|
+
/** Whether the diagram currently fills the browser viewport. */
|
|
14
|
+
maximized: boolean;
|
|
15
15
|
}
|
|
16
16
|
export interface UseZoomPanParams {
|
|
17
17
|
enabled: boolean;
|
|
@@ -1,38 +1,26 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
2
2
|
import { clampTransform, fitScale, formatPercent, formatZoom, IDENTITY, MAX_SCALE, MIN_SCALE, normalizeWheelDelta, panBy, PAN_STEP, SCALE_STEP, toCssTransform, wheelZoomFactor, zoomAbout, } from './zoomMath.js';
|
|
3
3
|
/**
|
|
4
|
-
* Zoom, pan and
|
|
4
|
+
* Zoom, pan and maximize for one diagram.
|
|
5
5
|
*
|
|
6
|
-
* The
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* The transform lives in a ref and is written straight to the DOM, so panning and zooming
|
|
7
|
+
* never re-render the component. The single piece of React state is `maximized`, which changes
|
|
8
|
+
* only on an explicit click or Escape — it drives markup, so it belongs in React, and at one
|
|
9
|
+
* transition per user action it costs nothing.
|
|
9
10
|
*/
|
|
10
11
|
/** Distance the pointer must travel before a press becomes a drag rather than a click. */
|
|
11
12
|
const DRAG_THRESHOLD_PX = 3;
|
|
12
13
|
/** Fraction of the viewport a Shift+arrow press pans. */
|
|
13
14
|
const PAGE_PAN_RATIO = 0.9;
|
|
14
|
-
function supportsFullscreen() {
|
|
15
|
-
if (typeof document === 'undefined')
|
|
16
|
-
return false;
|
|
17
|
-
const doc = document;
|
|
18
|
-
return Boolean(doc.fullscreenEnabled ||
|
|
19
|
-
typeof document.documentElement.webkitRequestFullscreen ===
|
|
20
|
-
'function');
|
|
21
|
-
}
|
|
22
|
-
function currentFullscreenElement() {
|
|
23
|
-
if (typeof document === 'undefined')
|
|
24
|
-
return null;
|
|
25
|
-
const doc = document;
|
|
26
|
-
return doc.fullscreenElement ?? doc.webkitFullscreenElement ?? null;
|
|
27
|
-
}
|
|
28
15
|
export function useZoomPan({ enabled, resetKey }) {
|
|
29
16
|
const stageRef = useRef(null);
|
|
30
17
|
const viewportRef = useRef(null);
|
|
31
18
|
const layerRef = useRef(null);
|
|
32
19
|
const readoutRef = useRef(null);
|
|
20
|
+
const [maximized, setMaximized] = useState(false);
|
|
33
21
|
const transformRef = useRef(IDENTITY);
|
|
34
|
-
/** The view to restore when
|
|
35
|
-
const
|
|
22
|
+
/** The view to restore when the diagram is un-maximized. */
|
|
23
|
+
const beforeMaximizeRef = useRef(null);
|
|
36
24
|
const frameRef = useRef(null);
|
|
37
25
|
/**
|
|
38
26
|
* Layout sizes, never `getBoundingClientRect()`: the layer's rect includes the transform we
|
|
@@ -66,26 +54,24 @@ export function useZoomPan({ enabled, resetKey }) {
|
|
|
66
54
|
const reset = useCallback(() => {
|
|
67
55
|
apply(IDENTITY);
|
|
68
56
|
}, [apply]);
|
|
69
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Zooms about the viewport's top-left corner.
|
|
59
|
+
*
|
|
60
|
+
* Not the centre: a diagram rarely fills its viewport, and a diagram that fits is
|
|
61
|
+
* left-aligned, so the empty space sits to its right and below. Zooming about the centre
|
|
62
|
+
* scales that empty space too and pushes the diagram off the top and left edges — most
|
|
63
|
+
* visibly when maximized. Anchoring at the top-left leaves a left-aligned diagram exactly
|
|
64
|
+
* where it is and grows it into the empty space, keeping it visible as long as possible.
|
|
65
|
+
*
|
|
66
|
+
* Wheel zoom still tracks the pointer, which is what direct manipulation should do.
|
|
67
|
+
*/
|
|
70
68
|
const zoomByStep = useCallback((factor) => {
|
|
71
|
-
|
|
72
|
-
const focalX = (viewport?.clientWidth ?? 0) / 2;
|
|
73
|
-
const focalY = (viewport?.clientHeight ?? 0) / 2;
|
|
74
|
-
apply(zoomAbout(transformRef.current, factor, focalX, focalY));
|
|
69
|
+
apply(zoomAbout(transformRef.current, factor, 0, 0));
|
|
75
70
|
}, [apply]);
|
|
76
71
|
const zoomIn = useCallback(() => zoomByStep(SCALE_STEP), [zoomByStep]);
|
|
77
72
|
const zoomOut = useCallback(() => zoomByStep(1 / SCALE_STEP), [zoomByStep]);
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
if (!stage)
|
|
81
|
-
return;
|
|
82
|
-
const doc = document;
|
|
83
|
-
if (currentFullscreenElement()) {
|
|
84
|
-
void (doc.exitFullscreen?.() ?? doc.webkitExitFullscreen?.());
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
beforeFullscreenRef.current = transformRef.current;
|
|
88
|
-
void (stage.requestFullscreen?.() ?? stage.webkitRequestFullscreen?.());
|
|
73
|
+
const toggleMaximize = useCallback(() => {
|
|
74
|
+
setMaximized((previous) => !previous);
|
|
89
75
|
}, []);
|
|
90
76
|
const onKeyDown = useCallback((event) => {
|
|
91
77
|
if (!enabled)
|
|
@@ -229,19 +215,6 @@ export function useZoomPan({ enabled, resetKey }) {
|
|
|
229
215
|
event.preventDefault();
|
|
230
216
|
event.stopPropagation();
|
|
231
217
|
};
|
|
232
|
-
const onFullscreenChange = () => {
|
|
233
|
-
const stage = stageRef.current;
|
|
234
|
-
const isFullscreen = stage !== null && currentFullscreenElement() === stage;
|
|
235
|
-
if (isFullscreen) {
|
|
236
|
-
// Fill the screen: the viewport is much larger now, so refit rather than keep a
|
|
237
|
-
// transform that was computed for a column-width box.
|
|
238
|
-
apply({ ...IDENTITY, k: fitScale(measure()) });
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
apply(beforeFullscreenRef.current ?? IDENTITY);
|
|
242
|
-
beforeFullscreenRef.current = null;
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
218
|
viewport.addEventListener('wheel', onWheel, { passive: false });
|
|
246
219
|
viewport.addEventListener('pointerdown', onPointerDown);
|
|
247
220
|
viewport.addEventListener('pointermove', onPointerMove);
|
|
@@ -249,8 +222,6 @@ export function useZoomPan({ enabled, resetKey }) {
|
|
|
249
222
|
viewport.addEventListener('pointercancel', endDrag);
|
|
250
223
|
viewport.addEventListener('lostpointercapture', endDrag);
|
|
251
224
|
viewport.addEventListener('click', onClickCapture, true);
|
|
252
|
-
document.addEventListener('fullscreenchange', onFullscreenChange);
|
|
253
|
-
document.addEventListener('webkitfullscreenchange', onFullscreenChange);
|
|
254
225
|
// Re-clamp when the column width changes (window resize, sidebar collapse), so a panned
|
|
255
226
|
// diagram cannot end up stranded outside a narrower viewport.
|
|
256
227
|
const observer = typeof ResizeObserver === 'undefined'
|
|
@@ -265,8 +236,6 @@ export function useZoomPan({ enabled, resetKey }) {
|
|
|
265
236
|
viewport.removeEventListener('pointercancel', endDrag);
|
|
266
237
|
viewport.removeEventListener('lostpointercapture', endDrag);
|
|
267
238
|
viewport.removeEventListener('click', onClickCapture, true);
|
|
268
|
-
document.removeEventListener('fullscreenchange', onFullscreenChange);
|
|
269
|
-
document.removeEventListener('webkitfullscreenchange', onFullscreenChange);
|
|
270
239
|
observer?.disconnect();
|
|
271
240
|
if (frameRef.current !== null) {
|
|
272
241
|
cancelAnimationFrame(frameRef.current);
|
|
@@ -295,6 +264,46 @@ export function useZoomPan({ enabled, resetKey }) {
|
|
|
295
264
|
return;
|
|
296
265
|
write(IDENTITY);
|
|
297
266
|
}, [enabled, resetKey, write]);
|
|
267
|
+
/**
|
|
268
|
+
* Maximizing is done in the page, not with the Fullscreen API.
|
|
269
|
+
*
|
|
270
|
+
* `requestFullscreen()` gave two problems it cannot solve: Firefox takes the whole browser
|
|
271
|
+
* window fullscreen rather than presenting the element, and the `::backdrop` is outside the
|
|
272
|
+
* element so the page behind it shows through. A fixed-position overlay we own has neither
|
|
273
|
+
* issue, works identically in every browser, and needs no capability detection — which also
|
|
274
|
+
* restores the control on iOS Safari, where element fullscreen does not exist.
|
|
275
|
+
*/
|
|
276
|
+
useEffect(() => {
|
|
277
|
+
if (!enabled || !maximized)
|
|
278
|
+
return undefined;
|
|
279
|
+
beforeMaximizeRef.current = transformRef.current;
|
|
280
|
+
// The overlay covers the viewport; scrolling the page underneath it would be confusing.
|
|
281
|
+
const body = document.body;
|
|
282
|
+
const previousOverflow = body.style.overflow;
|
|
283
|
+
body.style.overflow = 'hidden';
|
|
284
|
+
// The layout is already committed by the time an effect runs, so the viewport now
|
|
285
|
+
// reports its maximized size and the diagram can be fitted to it.
|
|
286
|
+
apply({ ...IDENTITY, k: fitScale(measure()) });
|
|
287
|
+
viewportRef.current?.focus();
|
|
288
|
+
const onKeyDownDocument = (event) => {
|
|
289
|
+
if (event.key !== 'Escape')
|
|
290
|
+
return;
|
|
291
|
+
event.preventDefault();
|
|
292
|
+
setMaximized(false);
|
|
293
|
+
};
|
|
294
|
+
document.addEventListener('keydown', onKeyDownDocument);
|
|
295
|
+
return () => {
|
|
296
|
+
document.removeEventListener('keydown', onKeyDownDocument);
|
|
297
|
+
body.style.overflow = previousOverflow;
|
|
298
|
+
apply(beforeMaximizeRef.current ?? IDENTITY);
|
|
299
|
+
beforeMaximizeRef.current = null;
|
|
300
|
+
};
|
|
301
|
+
}, [apply, enabled, maximized, measure]);
|
|
302
|
+
// A diagram that is re-rendered or unmounted must not leave the page scroll-locked.
|
|
303
|
+
useEffect(() => {
|
|
304
|
+
if (!enabled)
|
|
305
|
+
setMaximized(false);
|
|
306
|
+
}, [enabled]);
|
|
298
307
|
return {
|
|
299
308
|
stageRef,
|
|
300
309
|
viewportRef,
|
|
@@ -303,9 +312,9 @@ export function useZoomPan({ enabled, resetKey }) {
|
|
|
303
312
|
zoomIn,
|
|
304
313
|
zoomOut,
|
|
305
314
|
reset,
|
|
306
|
-
|
|
315
|
+
toggleMaximize,
|
|
307
316
|
onKeyDown,
|
|
308
|
-
|
|
317
|
+
maximized,
|
|
309
318
|
};
|
|
310
319
|
}
|
|
311
320
|
export { MAX_SCALE, MIN_SCALE };
|
|
@@ -62,7 +62,7 @@ export declare function wheelZoomFactor(normalizedDelta: number): number;
|
|
|
62
62
|
/**
|
|
63
63
|
* Largest scale at which the whole diagram still fits inside the given viewport.
|
|
64
64
|
*
|
|
65
|
-
* Used when
|
|
65
|
+
* Used when maximizing. Small diagrams are magnified rather than left stranded in
|
|
66
66
|
* the middle of a large screen — the output is vector, so it stays crisp — but never beyond
|
|
67
67
|
* `max`, and never below {@link MIN_SCALE}.
|
|
68
68
|
*/
|
|
@@ -82,7 +82,7 @@ export function wheelZoomFactor(normalizedDelta) {
|
|
|
82
82
|
/**
|
|
83
83
|
* Largest scale at which the whole diagram still fits inside the given viewport.
|
|
84
84
|
*
|
|
85
|
-
* Used when
|
|
85
|
+
* Used when maximizing. Small diagrams are magnified rather than left stranded in
|
|
86
86
|
* the middle of a large screen — the output is vector, so it stays crisp — but never beyond
|
|
87
87
|
* `max`, and never below {@link MIN_SCALE}.
|
|
88
88
|
*/
|
package/package.json
CHANGED