@linzjs/windows 5.4.3 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LuiModalAsync/LuiModalAsync.scss +1 -1
- package/dist/panel/Panel.tsx +46 -18
- package/dist/panel/PanelsContext.tsx +1 -1
- package/dist/panel/PanelsContextProvider.tsx +9 -2
- package/dist/panel/PopinWIndow.tsx +13 -33
- package/dist/panel/PopoutWindow.tsx +22 -109
- package/dist/panel/openExternalWindow.ts +98 -0
- package/dist/panel/types/PanelStateOptions.ts +2 -2
- package/package.json +20 -20
package/dist/panel/Panel.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import './PanelBlue.scss';
|
|
|
3
3
|
import '@linzjs/lui/dist/scss/base.scss';
|
|
4
4
|
|
|
5
5
|
import clsx from 'clsx';
|
|
6
|
-
import { PropsWithChildren, ReactElement, useContext, useEffect,
|
|
6
|
+
import { PropsWithChildren, ReactElement, useContext, useEffect, useRef, useState } from 'react';
|
|
7
7
|
import { createPortal } from 'react-dom';
|
|
8
8
|
|
|
9
9
|
import { useConstFunction } from '../common';
|
|
@@ -12,12 +12,13 @@ import { PanelsContext } from './PanelsContext';
|
|
|
12
12
|
import { PopinWindow } from './PopinWIndow';
|
|
13
13
|
import { PopoutWindow } from './PopoutWindow';
|
|
14
14
|
import { PanelProps } from './types/PanelProps';
|
|
15
|
+
import { useRestoreStateFrom } from './usePanelStateHandler';
|
|
15
16
|
|
|
16
17
|
export const Panel = (props: PanelProps): ReactElement => {
|
|
17
18
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
18
19
|
const { title, size = { width: 320, height: 200 }, className, children, onClose } = props;
|
|
19
20
|
|
|
20
|
-
const { dockElements
|
|
21
|
+
const { dockElements } = useContext(PanelsContext);
|
|
21
22
|
const { panelPoppedOut, uniqueId, setTitle, dockId, docked } = useContext(PanelInstanceContext);
|
|
22
23
|
|
|
23
24
|
const onCloseConstFn = useConstFunction(onClose ?? (() => {}));
|
|
@@ -42,30 +43,51 @@ export const Panel = (props: PanelProps): ReactElement => {
|
|
|
42
43
|
}
|
|
43
44
|
}, [title]);
|
|
44
45
|
|
|
45
|
-
const
|
|
46
|
+
const { nextStackPosition } = useContext(PanelsContext);
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
* This needs to be here, otherwise, every time PopinWindow is rendered, nextStackPosition is recalculated.
|
|
49
|
-
*/
|
|
48
|
+
const savedState = useRestoreStateFrom({ uniqueId, panelPoppedOut: false });
|
|
50
49
|
|
|
51
|
-
const
|
|
52
|
-
()
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
50
|
+
const [panelPosition, setPanelPosition] = useState(() => {
|
|
51
|
+
if (savedState?.panelPosition) {
|
|
52
|
+
return savedState.panelPosition;
|
|
53
|
+
}
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
if (props.position === 'center') {
|
|
56
|
+
return {
|
|
57
|
+
x: Math.max((window.innerWidth - size.width) / 2, 0),
|
|
58
|
+
y: Math.max((window.innerHeight - size.height) / 2, 0),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (props.position != null && props.position !== 'tile') {
|
|
63
|
+
return props.position;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return nextStackPosition();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const [panelSize, setPanelSize] = useState(() => {
|
|
70
|
+
if (savedState?.panelSize) {
|
|
71
|
+
return savedState.panelSize;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return size ?? { width: 320, height: 200 };
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const dockElement = docked && dockId && dockElements[dockId];
|
|
62
78
|
|
|
63
79
|
return (
|
|
64
80
|
<>
|
|
65
81
|
{dockElement && dockElement.isConnected ? (
|
|
66
82
|
createPortal(children, dockElement)
|
|
67
83
|
) : panelPoppedOut ? (
|
|
68
|
-
<PopoutWindow
|
|
84
|
+
<PopoutWindow
|
|
85
|
+
name={uniqueId}
|
|
86
|
+
title={title}
|
|
87
|
+
size={size}
|
|
88
|
+
popInPanelPosition={panelPosition}
|
|
89
|
+
popInPanelSize={panelSize}
|
|
90
|
+
>
|
|
69
91
|
<div
|
|
70
92
|
ref={panelRef}
|
|
71
93
|
style={{
|
|
@@ -80,7 +102,13 @@ export const Panel = (props: PanelProps): ReactElement => {
|
|
|
80
102
|
</div>
|
|
81
103
|
</PopoutWindow>
|
|
82
104
|
) : (
|
|
83
|
-
<PopinWindow
|
|
105
|
+
<PopinWindow
|
|
106
|
+
{...props}
|
|
107
|
+
panelPosition={panelPosition}
|
|
108
|
+
setPanelPosition={setPanelPosition}
|
|
109
|
+
panelSize={panelSize}
|
|
110
|
+
setPanelSize={setPanelSize}
|
|
111
|
+
>
|
|
84
112
|
{children}
|
|
85
113
|
</PopinWindow>
|
|
86
114
|
)}
|
|
@@ -25,7 +25,7 @@ export interface PanelsContextType {
|
|
|
25
25
|
nextStackPosition: () => PanelPosition;
|
|
26
26
|
dockElements: Record<string, HTMLDivElement>;
|
|
27
27
|
setDockElement: (id: string, element: HTMLDivElement) => void;
|
|
28
|
-
panelStateOptions?: PanelStateOptions;
|
|
28
|
+
panelStateOptions?: PanelStateOptions | null;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const NoContext = () => {
|
|
@@ -11,21 +11,28 @@ import { PanelStateOptions } from './types/PanelStateOptions';
|
|
|
11
11
|
export interface PanelsContextProviderProps {
|
|
12
12
|
baseZIndex?: number;
|
|
13
13
|
bounds?: string | Element;
|
|
14
|
-
panelStateOptions?: PanelStateOptions;
|
|
14
|
+
panelStateOptions?: PanelStateOptions | null;
|
|
15
15
|
tilingStart?: PanelPosition;
|
|
16
16
|
tilingOffset?: PanelPosition;
|
|
17
17
|
tilingMax?: PanelPosition;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
const defaultPanelStateOptions: PanelStateOptions = {
|
|
21
|
+
saveStateIn: 'localStorage',
|
|
22
|
+
saveStateKey: 'userId',
|
|
23
|
+
};
|
|
24
|
+
|
|
20
25
|
export const PanelsContextProvider = ({
|
|
21
26
|
bounds,
|
|
22
27
|
baseZIndex = 500,
|
|
23
28
|
children,
|
|
24
|
-
panelStateOptions,
|
|
29
|
+
panelStateOptions: _panelStateOptions,
|
|
25
30
|
tilingStart = { x: 30, y: 30 },
|
|
26
31
|
tilingOffset = { x: 30, y: 50 },
|
|
27
32
|
tilingMax = { x: 200, y: 300 },
|
|
28
33
|
}: PropsWithChildren<PanelsContextProviderProps>): ReactElement => {
|
|
34
|
+
const panelStateOptions = _panelStateOptions === undefined ? defaultPanelStateOptions : _panelStateOptions;
|
|
35
|
+
|
|
29
36
|
const stackPositionRef = useRef(tilingStart);
|
|
30
37
|
|
|
31
38
|
// Can't use a map here as we need to retain render order for performance
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
|
-
import { useCallback, useContext, useEffect, useRef
|
|
2
|
+
import { Dispatch, useCallback, useContext, useEffect, useRef } from 'react';
|
|
3
3
|
import { Rnd } from 'react-rnd';
|
|
4
4
|
|
|
5
5
|
import { PanelContext } from './PanelContext';
|
|
6
6
|
import { PanelInstanceContext } from './PanelInstanceContext';
|
|
7
7
|
import { PanelPosition, PanelSize } from './types';
|
|
8
8
|
import { PanelProps } from './types/PanelProps';
|
|
9
|
-
import {
|
|
9
|
+
import { useSaveStateIn } from './usePanelStateHandler';
|
|
10
10
|
|
|
11
11
|
export function PopinWindow({
|
|
12
12
|
children,
|
|
@@ -16,50 +16,30 @@ export function PopinWindow({
|
|
|
16
16
|
minHeight,
|
|
17
17
|
minWidth,
|
|
18
18
|
modal,
|
|
19
|
-
nextStackPosition,
|
|
20
19
|
position = modal ? 'center' : 'tile',
|
|
21
20
|
resizeable = modal !== true,
|
|
22
21
|
size = { height: 200, width: 320 },
|
|
23
|
-
|
|
22
|
+
panelPosition,
|
|
23
|
+
setPanelPosition,
|
|
24
|
+
panelSize,
|
|
25
|
+
setPanelSize,
|
|
26
|
+
}: PanelProps & {
|
|
27
|
+
panelPosition: PanelPosition;
|
|
28
|
+
setPanelPosition: Dispatch<PanelPosition>;
|
|
29
|
+
panelSize: PanelSize;
|
|
30
|
+
setPanelSize: Dispatch<PanelSize>;
|
|
31
|
+
}): JSX.Element {
|
|
24
32
|
const panelRef = useRef<Rnd>(null);
|
|
25
33
|
|
|
26
34
|
const { bounds, zIndex, bringPanelToFront, uniqueId } = useContext(PanelInstanceContext);
|
|
27
35
|
|
|
28
|
-
const savedState = useRestoreStateFrom({ uniqueId, panelPoppedOut: false });
|
|
29
|
-
|
|
30
|
-
const [panelPosition, setPanelPosition] = useState(() => {
|
|
31
|
-
if (savedState?.panelPosition) {
|
|
32
|
-
return savedState.panelPosition;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
switch (position) {
|
|
36
|
-
case 'center':
|
|
37
|
-
return {
|
|
38
|
-
x: Math.max((window.innerWidth - size.width) / 2, 0),
|
|
39
|
-
y: Math.max((window.innerHeight - size.height) / 2, 0),
|
|
40
|
-
};
|
|
41
|
-
case 'tile':
|
|
42
|
-
return nextStackPosition();
|
|
43
|
-
default:
|
|
44
|
-
return position ?? nextStackPosition();
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const [panelSize, setPanelSize] = useState(() => {
|
|
49
|
-
if (savedState?.panelSize) {
|
|
50
|
-
return savedState.panelSize;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return size ?? { width: 320, height: 200 };
|
|
54
|
-
});
|
|
55
|
-
|
|
56
36
|
const centerWindow = useCallback(() => {
|
|
57
37
|
const b = panelRef.current?.getSelfElement()?.getBoundingClientRect();
|
|
58
38
|
setPanelPosition({
|
|
59
39
|
x: Math.max((window.innerWidth - (b?.width ?? size.width)) / 2, 0),
|
|
60
40
|
y: Math.max((window.innerHeight - (b?.height ?? size.height)) / 2, 0),
|
|
61
41
|
});
|
|
62
|
-
}, [size]);
|
|
42
|
+
}, [setPanelPosition, size.height, size.width]);
|
|
63
43
|
|
|
64
44
|
useEffect(() => {
|
|
65
45
|
if (position === 'center' && !resizeable) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import createCache from '@emotion/cache';
|
|
2
2
|
import { CacheProvider } from '@emotion/react';
|
|
3
|
-
import {
|
|
3
|
+
import { ReactElement, ReactNode, useContext, useEffect, useState } from 'react';
|
|
4
4
|
import ReactDOM from 'react-dom';
|
|
5
5
|
|
|
6
|
-
import { makePopoutId
|
|
7
|
-
import {
|
|
6
|
+
import { makePopoutId } from './generateId';
|
|
7
|
+
import { openExternalWindow, OpenExternalWindowResponse } from './openExternalWindow';
|
|
8
8
|
import { PanelInstanceContext } from './PanelInstanceContext';
|
|
9
9
|
import { PanelPosition, PanelSize } from './types';
|
|
10
10
|
import { useRestoreStateFrom, useSaveStateIn } from './usePanelStateHandler';
|
|
@@ -14,6 +14,8 @@ interface PopoutWindowProps {
|
|
|
14
14
|
title?: string; // The title of the popout window
|
|
15
15
|
children: ReactNode; // what to render inside the window
|
|
16
16
|
size: PanelSize;
|
|
17
|
+
popInPanelPosition: PanelPosition;
|
|
18
|
+
popInPanelSize: PanelSize;
|
|
17
19
|
|
|
18
20
|
//says if we want to watch for any dynamic style changes
|
|
19
21
|
//only needed when using UI libraries like MUI
|
|
@@ -24,104 +26,12 @@ interface PopoutWindowProps {
|
|
|
24
26
|
className?: string;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
type OpenExternalWindowResponse = {
|
|
28
|
-
externalWindow: Window | null;
|
|
29
|
-
onCloseExternalWindow: (() => void) | null;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Open and scaffold the new external window.
|
|
34
|
-
*
|
|
35
|
-
* The main motivation for extracting these procedures into a function is for testing ease. JSDOM does not mock out the
|
|
36
|
-
* entire window object, making the PopoutWindow component difficult to unit test without extensive mocking. Instead, we
|
|
37
|
-
* can avoid mocking most of the window methods entirely by conditionally calling an alternative to `openExternalWindow`
|
|
38
|
-
* during unit testing by providing a truthy `openExternalWindowFn` prop.
|
|
39
|
-
*/
|
|
40
|
-
const openExternalWindow = ({
|
|
41
|
-
title,
|
|
42
|
-
size,
|
|
43
|
-
setContainerElement,
|
|
44
|
-
setHeadElement,
|
|
45
|
-
observeStyleChanges = false,
|
|
46
|
-
className,
|
|
47
|
-
position,
|
|
48
|
-
}: {
|
|
49
|
-
title: string;
|
|
50
|
-
size: { width: number; height: number };
|
|
51
|
-
setContainerElement: Dispatch<SetStateAction<HTMLElement | undefined>>;
|
|
52
|
-
setHeadElement: Dispatch<SetStateAction<HTMLElement | undefined>>;
|
|
53
|
-
observeStyleChanges?: boolean;
|
|
54
|
-
className?: string;
|
|
55
|
-
position: PanelPosition;
|
|
56
|
-
}): OpenExternalWindowResponse => {
|
|
57
|
-
const response: OpenExternalWindowResponse = { externalWindow: null, onCloseExternalWindow: null };
|
|
58
|
-
|
|
59
|
-
// NOTE:
|
|
60
|
-
// 1/ Edge ignores the left and top settings
|
|
61
|
-
// 2/ Chrome has a bug that screws up the window size by a factor dependent on your windows scaling settings
|
|
62
|
-
// if you open the browser in a window with a scaling and drag it to a window with a different scaling
|
|
63
|
-
const features =
|
|
64
|
-
`scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,` +
|
|
65
|
-
`width=${size.width},height=${size.height},left=${position.x},top=${position.y}`;
|
|
66
|
-
response.externalWindow = window.open('', makePopoutId(title), features);
|
|
67
|
-
|
|
68
|
-
if (response.externalWindow) {
|
|
69
|
-
const { externalWindow } = response;
|
|
70
|
-
|
|
71
|
-
// Reset the default body style applied by the browser
|
|
72
|
-
externalWindow.document.body.style.margin = '0';
|
|
73
|
-
externalWindow.document.body.style.padding = '0';
|
|
74
|
-
|
|
75
|
-
// Remove any existing body HTML from this window
|
|
76
|
-
const existingBodyMountElement = externalWindow.document.body?.firstChild;
|
|
77
|
-
if (existingBodyMountElement) {
|
|
78
|
-
externalWindow.document.body.removeChild(existingBodyMountElement);
|
|
79
|
-
}
|
|
80
|
-
// Create a new HTML element to hang our rendering off
|
|
81
|
-
const newMountElement = externalWindow.document.createElement('div');
|
|
82
|
-
newMountElement.className = `PopoutWindowContainer ${className}`;
|
|
83
|
-
setContainerElement(newMountElement);
|
|
84
|
-
externalWindow.document.body.appendChild(newMountElement);
|
|
85
|
-
|
|
86
|
-
// Do the same for the head node (where all the styles are added)
|
|
87
|
-
const existingHeadMountElement = externalWindow.document.head?.firstChild;
|
|
88
|
-
if (existingHeadMountElement) {
|
|
89
|
-
externalWindow.document.head.removeChild(existingHeadMountElement);
|
|
90
|
-
}
|
|
91
|
-
// and an HTML element to hang our copied styles off
|
|
92
|
-
const newHeadMountElement = externalWindow.document.createElement('div');
|
|
93
|
-
externalWindow.document.head.appendChild(newHeadMountElement);
|
|
94
|
-
setHeadElement(newHeadMountElement);
|
|
95
|
-
|
|
96
|
-
// Set External window title
|
|
97
|
-
externalWindow.document.title = title;
|
|
98
|
-
|
|
99
|
-
// Set an id to the external window div to be queried and used by react-modal as parentSelector
|
|
100
|
-
// so the modal shows up in the popped out window instead the main window
|
|
101
|
-
newMountElement.id = popoutWindowDivId(title);
|
|
102
|
-
|
|
103
|
-
copyStyleSheets(newHeadMountElement);
|
|
104
|
-
|
|
105
|
-
let observer: MutationObserver | null = null;
|
|
106
|
-
if (observeStyleChanges) {
|
|
107
|
-
observer = observeStyleSheetChanges(newHeadMountElement);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
externalWindow.onbeforeunload = () => observer?.disconnect();
|
|
111
|
-
|
|
112
|
-
response.onCloseExternalWindow = () => {
|
|
113
|
-
// Make sure the window closes when the component unmounts
|
|
114
|
-
externalWindow.close();
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return response;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
29
|
export const PopoutWindow = ({
|
|
122
30
|
name,
|
|
123
31
|
title = name,
|
|
124
32
|
size,
|
|
33
|
+
popInPanelPosition,
|
|
34
|
+
popInPanelSize,
|
|
125
35
|
observeStyleChanges,
|
|
126
36
|
className,
|
|
127
37
|
openExternalWindowFn,
|
|
@@ -132,10 +42,16 @@ export const PopoutWindow = ({
|
|
|
132
42
|
const [containerElement, setContainerElement] = useState<HTMLElement>();
|
|
133
43
|
const [headElement, setHeadElement] = useState<HTMLElement>();
|
|
134
44
|
|
|
135
|
-
const savedState = useRestoreStateFrom({
|
|
45
|
+
const savedState = useRestoreStateFrom({ uniqueId: name, panelPoppedOut: true });
|
|
136
46
|
|
|
137
|
-
|
|
138
|
-
const
|
|
47
|
+
// If we use a popped in position as a popped out position we must adjust for which screen the window is on.
|
|
48
|
+
const poppedInPosition = popInPanelPosition;
|
|
49
|
+
const poppedInPositionAdjusted = {
|
|
50
|
+
x: window.screenX + poppedInPosition.x,
|
|
51
|
+
y: window.screenY + poppedInPosition.y,
|
|
52
|
+
};
|
|
53
|
+
const position = savedState?.panelPosition ?? poppedInPositionAdjusted;
|
|
54
|
+
const panelSize = savedState?.panelSize ?? popInPanelSize ?? size;
|
|
139
55
|
|
|
140
56
|
const saveStateIn = useSaveStateIn({ panelPoppedOut: true, uniqueId: name });
|
|
141
57
|
|
|
@@ -150,7 +66,7 @@ export const PopoutWindow = ({
|
|
|
150
66
|
setHeadElement,
|
|
151
67
|
observeStyleChanges,
|
|
152
68
|
className,
|
|
153
|
-
position
|
|
69
|
+
position,
|
|
154
70
|
});
|
|
155
71
|
|
|
156
72
|
if (!externalWindow) return;
|
|
@@ -161,23 +77,20 @@ export const PopoutWindow = ({
|
|
|
161
77
|
externalWindow.close();
|
|
162
78
|
};
|
|
163
79
|
|
|
164
|
-
const savePanelState = (
|
|
165
|
-
const extWindow = currentTarget as Window;
|
|
166
|
-
|
|
80
|
+
const savePanelState = () => {
|
|
167
81
|
saveStateIn({
|
|
168
82
|
panelPosition: {
|
|
169
|
-
x:
|
|
170
|
-
y:
|
|
83
|
+
x: externalWindow.screenX,
|
|
84
|
+
y: externalWindow.screenY,
|
|
171
85
|
},
|
|
172
86
|
panelSize: {
|
|
173
|
-
height:
|
|
174
|
-
width:
|
|
87
|
+
height: externalWindow.innerHeight,
|
|
88
|
+
width: externalWindow.innerWidth,
|
|
175
89
|
},
|
|
176
90
|
});
|
|
177
91
|
};
|
|
178
92
|
|
|
179
93
|
externalWindow.addEventListener('beforeunload', savePanelState);
|
|
180
|
-
externalWindow.addEventListener('resize', savePanelState);
|
|
181
94
|
|
|
182
95
|
// When the main window closes or reloads, close the popout
|
|
183
96
|
window.addEventListener('beforeunload', closeExternalWindow, { once: true });
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
|
|
3
|
+
import { makePopoutId, popoutWindowDivId } from './generateId';
|
|
4
|
+
import { copyStyleSheets, observeStyleSheetChanges } from './handleStyleSheetsChanges';
|
|
5
|
+
import { PanelPosition } from './types';
|
|
6
|
+
|
|
7
|
+
export type OpenExternalWindowResponse = {
|
|
8
|
+
externalWindow: Window | null;
|
|
9
|
+
onCloseExternalWindow: (() => void) | null;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Open and scaffold the new external window.
|
|
13
|
+
*
|
|
14
|
+
* The main motivation for extracting these procedures into a function is for testing ease. JSDOM does not mock out the
|
|
15
|
+
* entire window object, making the PopoutWindow component difficult to unit test without extensive mocking. Instead, we
|
|
16
|
+
* can avoid mocking most of the window methods entirely by conditionally calling an alternative to `openExternalWindow`
|
|
17
|
+
* during unit testing by providing a truthy `openExternalWindowFn` prop.
|
|
18
|
+
*/
|
|
19
|
+
export const openExternalWindow = ({
|
|
20
|
+
title,
|
|
21
|
+
size,
|
|
22
|
+
setContainerElement,
|
|
23
|
+
setHeadElement,
|
|
24
|
+
observeStyleChanges = false,
|
|
25
|
+
className,
|
|
26
|
+
position,
|
|
27
|
+
}: {
|
|
28
|
+
title: string;
|
|
29
|
+
size: { width: number; height: number };
|
|
30
|
+
setContainerElement: Dispatch<SetStateAction<HTMLElement | undefined>>;
|
|
31
|
+
setHeadElement: Dispatch<SetStateAction<HTMLElement | undefined>>;
|
|
32
|
+
observeStyleChanges?: boolean;
|
|
33
|
+
className?: string;
|
|
34
|
+
position: PanelPosition;
|
|
35
|
+
}): OpenExternalWindowResponse => {
|
|
36
|
+
const response: OpenExternalWindowResponse = { externalWindow: null, onCloseExternalWindow: null };
|
|
37
|
+
|
|
38
|
+
// NOTE:
|
|
39
|
+
// 1/ Edge ignores the left and top settings
|
|
40
|
+
// 2/ Chrome has a bug that screws up the window size by a factor dependent on your windows scaling settings
|
|
41
|
+
// if you open the browser in a window with a scaling and drag it to a window with a different scaling
|
|
42
|
+
const features =
|
|
43
|
+
`scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,` +
|
|
44
|
+
`width=${size.width},height=${size.height},left=${position.x},top=${position.y}`;
|
|
45
|
+
response.externalWindow = window.open('', makePopoutId(title), features);
|
|
46
|
+
|
|
47
|
+
if (response.externalWindow) {
|
|
48
|
+
const { externalWindow } = response;
|
|
49
|
+
|
|
50
|
+
// Reset the default body style applied by the browser
|
|
51
|
+
externalWindow.document.body.style.margin = '0';
|
|
52
|
+
externalWindow.document.body.style.padding = '0';
|
|
53
|
+
|
|
54
|
+
// Remove any existing body HTML from this window
|
|
55
|
+
const existingBodyMountElement = externalWindow.document.body?.firstChild;
|
|
56
|
+
if (existingBodyMountElement) {
|
|
57
|
+
externalWindow.document.body.removeChild(existingBodyMountElement);
|
|
58
|
+
}
|
|
59
|
+
// Create a new HTML element to hang our rendering off
|
|
60
|
+
const newMountElement = externalWindow.document.createElement('div');
|
|
61
|
+
newMountElement.className = `PopoutWindowContainer ${className}`;
|
|
62
|
+
setContainerElement(newMountElement);
|
|
63
|
+
externalWindow.document.body.appendChild(newMountElement);
|
|
64
|
+
|
|
65
|
+
// Do the same for the head node (where all the styles are added)
|
|
66
|
+
const existingHeadMountElement = externalWindow.document.head?.firstChild;
|
|
67
|
+
if (existingHeadMountElement) {
|
|
68
|
+
externalWindow.document.head.removeChild(existingHeadMountElement);
|
|
69
|
+
}
|
|
70
|
+
// and an HTML element to hang our copied styles off
|
|
71
|
+
const newHeadMountElement = externalWindow.document.createElement('div');
|
|
72
|
+
externalWindow.document.head.appendChild(newHeadMountElement);
|
|
73
|
+
setHeadElement(newHeadMountElement);
|
|
74
|
+
|
|
75
|
+
// Set External window title
|
|
76
|
+
externalWindow.document.title = title;
|
|
77
|
+
|
|
78
|
+
// Set an id to the external window div to be queried and used by react-modal as parentSelector
|
|
79
|
+
// so the modal shows up in the popped out window instead the main window
|
|
80
|
+
newMountElement.id = popoutWindowDivId(title);
|
|
81
|
+
|
|
82
|
+
copyStyleSheets(newHeadMountElement);
|
|
83
|
+
|
|
84
|
+
let observer: MutationObserver | null = null;
|
|
85
|
+
if (observeStyleChanges) {
|
|
86
|
+
observer = observeStyleSheetChanges(newHeadMountElement);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
externalWindow.onbeforeunload = () => observer?.disconnect();
|
|
90
|
+
|
|
91
|
+
response.onCloseExternalWindow = () => {
|
|
92
|
+
// Make sure the window closes when the component unmounts
|
|
93
|
+
externalWindow.close();
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return response;
|
|
98
|
+
};
|
|
@@ -3,9 +3,9 @@ import { PanelState } from './PanelState';
|
|
|
3
3
|
|
|
4
4
|
export type SaveStateInOption = 'external' | 'localStorage';
|
|
5
5
|
|
|
6
|
-
export
|
|
6
|
+
export interface PanelStateOptions {
|
|
7
7
|
onRestoreState?: () => PanelRestoredState;
|
|
8
8
|
onSaveState?: (panelState: PanelState) => void;
|
|
9
9
|
saveStateIn: SaveStateInOption;
|
|
10
10
|
saveStateKey: string;
|
|
11
|
-
}
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"popout"
|
|
14
14
|
],
|
|
15
15
|
"main": "./dist/index.ts",
|
|
16
|
-
"version": "5.
|
|
16
|
+
"version": "5.5.0",
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"@linzjs/lui": ">=21",
|
|
19
19
|
"lodash-es": ">=4",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@emotion/cache": "^11.14.0",
|
|
53
53
|
"@emotion/react": "^11.14.0",
|
|
54
|
-
"@emotion/styled": "11.14.
|
|
54
|
+
"@emotion/styled": "11.14.1",
|
|
55
55
|
"@types/uuid": "^10.0.0",
|
|
56
56
|
"lodash-es": ">=4",
|
|
57
57
|
"react-rnd": "^10.5.2",
|
|
@@ -59,17 +59,17 @@
|
|
|
59
59
|
"uuid": "^11.1.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@chromatic-com/storybook": "^4.
|
|
63
|
-
"@linzjs/lui": "^23.
|
|
64
|
-
"@linzjs/step-ag-grid": "^
|
|
62
|
+
"@chromatic-com/storybook": "^4.1.1",
|
|
63
|
+
"@linzjs/lui": "^23.11.1",
|
|
64
|
+
"@linzjs/step-ag-grid": "^28.5.1",
|
|
65
65
|
"@linzjs/style": "^5.4.0",
|
|
66
66
|
"@rollup/plugin-commonjs": "^28.0.6",
|
|
67
67
|
"@rollup/plugin-json": "^6.1.0",
|
|
68
68
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
69
|
-
"@storybook/addon-docs": "^9.
|
|
70
|
-
"@storybook/addon-links": "^9.
|
|
71
|
-
"@storybook/react-vite": "^9.
|
|
72
|
-
"@testing-library/dom": "^10.4.
|
|
69
|
+
"@storybook/addon-docs": "^9.1.4",
|
|
70
|
+
"@storybook/addon-links": "^9.1.4",
|
|
71
|
+
"@storybook/react-vite": "^9.1.4",
|
|
72
|
+
"@testing-library/dom": "^10.4.1",
|
|
73
73
|
"@testing-library/react": "^16.3.0",
|
|
74
74
|
"@testing-library/user-event": "^14.6.1",
|
|
75
75
|
"@types/lodash-es": "^4.17.12",
|
|
@@ -78,39 +78,39 @@
|
|
|
78
78
|
"@types/react-dom": "^18.3.7",
|
|
79
79
|
"@vitejs/plugin-react-swc": "^3.10.2",
|
|
80
80
|
"@vitest/ui": "^3.2.4",
|
|
81
|
-
"ag-grid-community": "^
|
|
82
|
-
"ag-grid-react": "^
|
|
81
|
+
"ag-grid-community": "^34.1.2",
|
|
82
|
+
"ag-grid-react": "^34.1.2",
|
|
83
83
|
"babel-preset-react-app": "^10.1.0",
|
|
84
84
|
"eslint-plugin-react": "^7.37.5",
|
|
85
|
-
"eslint-plugin-storybook": "9.
|
|
85
|
+
"eslint-plugin-storybook": "9.1.4",
|
|
86
86
|
"jsdom": "^26.1.0",
|
|
87
87
|
"mkdirp": "^3.0.1",
|
|
88
88
|
"npm-run-all": "^4.1.5",
|
|
89
89
|
"react": "^18.3.1",
|
|
90
90
|
"react-app-polyfill": "^3.0.0",
|
|
91
91
|
"react-dom": "^18.3.1",
|
|
92
|
-
"rollup": "^4.
|
|
92
|
+
"rollup": "^4.50.0",
|
|
93
93
|
"rollup-plugin-copy": "^3.5.0",
|
|
94
|
-
"sass": "^1.
|
|
94
|
+
"sass": "^1.92.0",
|
|
95
95
|
"sass-loader": "^16.0.5",
|
|
96
|
-
"semantic-release": "^24.2.
|
|
97
|
-
"storybook": "^9.
|
|
96
|
+
"semantic-release": "^24.2.7",
|
|
97
|
+
"storybook": "^9.1.4",
|
|
98
98
|
"style-loader": "^4.0.0",
|
|
99
|
-
"stylelint": "^16.
|
|
99
|
+
"stylelint": "^16.23.1",
|
|
100
100
|
"stylelint-config-recommended": "^15.0.0",
|
|
101
101
|
"stylelint-config-recommended-scss": "^14.1.0",
|
|
102
102
|
"stylelint-config-standard": "^38.0.0",
|
|
103
103
|
"stylelint-prettier": "5.0.3",
|
|
104
104
|
"stylelint-scss": "6.12.1",
|
|
105
|
-
"typescript": "^5.
|
|
105
|
+
"typescript": "^5.9.2",
|
|
106
106
|
"vite": "^6.3.5",
|
|
107
107
|
"vite-plugin-html": "^3.2.2",
|
|
108
108
|
"vite-tsconfig-paths": "^5.1.4",
|
|
109
109
|
"vitest": "^3.2.4"
|
|
110
110
|
},
|
|
111
111
|
"optionalDependencies": {
|
|
112
|
-
"@rollup/rollup-linux-x64-gnu": "^4.
|
|
113
|
-
"@swc/core-linux-x64-gnu": "^1.
|
|
112
|
+
"@rollup/rollup-linux-x64-gnu": "^4.50.0",
|
|
113
|
+
"@swc/core-linux-x64-gnu": "^1.13.5"
|
|
114
114
|
},
|
|
115
115
|
"browserslist": {
|
|
116
116
|
"production": [
|