@linzjs/windows 7.2.0 → 8.0.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/panel/Panel.tsx +62 -42
- package/dist/panel/PanelInstanceContextProvider.tsx +6 -4
- package/dist/panel/PanelsContext.tsx +1 -1
- package/dist/panel/PanelsContextProvider.tsx +7 -2
- package/dist/panel/PopinWIndow.tsx +8 -2
- package/dist/panel/PopoutWindow.tsx +4 -4
- package/dist/panel/openExternalWindow.ts +2 -2
- package/dist/panel/types/PanelProps.ts +3 -9
- package/dist/panel/types/PanelSize.ts +6 -1
- package/package.json +1 -1
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 {
|
|
6
|
+
import { isEqual, pick } from 'lodash-es';
|
|
7
7
|
import {
|
|
8
8
|
forwardRef,
|
|
9
9
|
PropsWithChildren,
|
|
@@ -21,21 +21,25 @@ import { PanelInstanceContext } from './PanelInstanceContext';
|
|
|
21
21
|
import { PanelsContext } from './PanelsContext';
|
|
22
22
|
import { PopinWindow } from './PopinWIndow';
|
|
23
23
|
import { PopoutWindow } from './PopoutWindow';
|
|
24
|
-
import { PanelPosition } from './types';
|
|
24
|
+
import { PanelPosition } from './types/PanelPosition';
|
|
25
25
|
import { PanelProps } from './types/PanelProps';
|
|
26
|
+
import { PanelSize } from './types/PanelSize';
|
|
26
27
|
import { useRestoreStateFrom } from './usePanelStateHandler';
|
|
27
28
|
|
|
28
29
|
const defaultInitialSize = { width: 320, height: 200 };
|
|
29
30
|
|
|
30
31
|
export const Panel = (props: PanelProps): ReactElement => {
|
|
31
32
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
32
|
-
const { title, size = defaultInitialSize, className,
|
|
33
|
+
const { title, size = defaultInitialSize, className, dynamicBounds, children } = props;
|
|
33
34
|
|
|
34
35
|
const { dockElements, nextStackPosition } = useContext(PanelsContext);
|
|
35
36
|
const { panelPoppedOut, uniqueId, setTitle, dockId, docked } = useContext(PanelInstanceContext);
|
|
36
37
|
|
|
37
38
|
const savedState = useRestoreStateFrom({ uniqueId, panelPoppedOut: false });
|
|
38
39
|
|
|
40
|
+
const lastPanelHeightRef = useRef(panelRef.current?.clientHeight);
|
|
41
|
+
lastPanelHeightRef.current = panelRef.current?.clientHeight ?? lastPanelHeightRef.current ?? 400;
|
|
42
|
+
|
|
39
43
|
useEffect(() => {
|
|
40
44
|
setTitle(title);
|
|
41
45
|
}, [setTitle, title]);
|
|
@@ -51,63 +55,52 @@ export const Panel = (props: PanelProps): ReactElement => {
|
|
|
51
55
|
}, [title]);
|
|
52
56
|
|
|
53
57
|
const [panelPosition, setPanelPosition] = useState((): PanelPosition => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
if (dynamicBounds) {
|
|
59
|
+
const result = dynamicBounds();
|
|
60
|
+
if (result) {
|
|
61
|
+
return pick(result, ['x', 'y']);
|
|
62
|
+
}
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
if (b) {
|
|
63
|
-
return { x: b[pos.edgeX ?? 'left'] + (pos.offsetX ?? 0), y: b[pos.edgeY ?? 'bottom'] + (pos.offsetY ?? 0) };
|
|
64
|
-
}
|
|
65
|
+
const pos = props.position;
|
|
66
|
+
if (savedState?.panelPosition) {
|
|
67
|
+
return savedState.panelPosition;
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
if (pos === 'center') {
|
|
71
|
+
const height = size.height === 'auto' ? 400 : size.height;
|
|
68
72
|
return {
|
|
69
73
|
x: Math.max((window.innerWidth - size.width) / 2, 0),
|
|
70
|
-
y: Math.max((window.innerHeight -
|
|
74
|
+
y: Math.max((window.innerHeight - height) / 2, 0),
|
|
71
75
|
};
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
if (pos != null && pos !== 'tile') {
|
|
75
|
-
return pos
|
|
79
|
+
return pos;
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
return nextStackPosition();
|
|
79
83
|
});
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
() => {
|
|
83
|
-
const pos = props.position;
|
|
84
|
-
if (pos && typeof pos === 'object' && 'selector' in pos) {
|
|
85
|
-
const b = document.querySelector(pos.selector)?.getBoundingClientRect();
|
|
86
|
-
if (b) {
|
|
87
|
-
const newPos = {
|
|
88
|
-
x: b[pos.edgeX ?? 'left'] + (pos.offsetX ?? 0),
|
|
89
|
-
y: b[pos.edgeY ?? 'bottom'] + (pos.offsetY ?? 0),
|
|
90
|
-
};
|
|
91
|
-
if (newPos.x !== panelPosition.x || newPos.y !== panelPosition.y) {
|
|
92
|
-
setPanelPosition(newPos);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
props.position && typeof props.position === 'object' && 'selector' in props.position ? 250 : null,
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
const cropSizeToWindow = useCallback((size: NumberSize) => {
|
|
85
|
+
const cropSizeToWindow = useCallback((size: PanelSize): PanelSize => {
|
|
101
86
|
return typeof window !== 'undefined' && window.innerWidth && window.innerHeight
|
|
102
87
|
? {
|
|
103
88
|
width: Math.min(window.document.body?.clientWidth ?? window.innerWidth, size.width),
|
|
104
|
-
height:
|
|
89
|
+
height:
|
|
90
|
+
size.height === 'auto'
|
|
91
|
+
? 'auto'
|
|
92
|
+
: Math.min(window.document.body?.clientHeight ?? window.innerHeight, size.height),
|
|
105
93
|
}
|
|
106
94
|
: size;
|
|
107
95
|
}, []);
|
|
108
96
|
|
|
109
|
-
const [panelSize, _setPanelSize] = useState<
|
|
110
|
-
|
|
97
|
+
const [panelSize, _setPanelSize] = useState<PanelSize>(() => {
|
|
98
|
+
const result = dynamicBounds?.();
|
|
99
|
+
if (result) {
|
|
100
|
+
return pick(result, ['width', 'height']);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (savedState?.panelSize) {
|
|
111
104
|
return cropSizeToWindow(savedState.panelSize);
|
|
112
105
|
}
|
|
113
106
|
|
|
@@ -115,14 +108,14 @@ export const Panel = (props: PanelProps): ReactElement => {
|
|
|
115
108
|
});
|
|
116
109
|
|
|
117
110
|
const setPanelSize = useCallback(
|
|
118
|
-
(size:
|
|
111
|
+
(size: PanelSize) => {
|
|
119
112
|
_setPanelSize(cropSizeToWindow(size));
|
|
120
113
|
},
|
|
121
114
|
[cropSizeToWindow],
|
|
122
115
|
);
|
|
123
116
|
|
|
124
117
|
const setInitialPanelSize = useCallback(
|
|
125
|
-
(size:
|
|
118
|
+
(size: PanelSize) => {
|
|
126
119
|
// If panel was already sized from state, then don't resize
|
|
127
120
|
if (savedState?.panelSize) {
|
|
128
121
|
return;
|
|
@@ -133,8 +126,32 @@ export const Panel = (props: PanelProps): ReactElement => {
|
|
|
133
126
|
[savedState?.panelSize, setPanelSize],
|
|
134
127
|
);
|
|
135
128
|
|
|
136
|
-
const
|
|
129
|
+
const dynamicResize = useCallback(() => {
|
|
130
|
+
const bounds = dynamicBounds?.();
|
|
131
|
+
if (!bounds) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const newPosition = pick(bounds, ['x', 'y']);
|
|
135
|
+
if (!isEqual(newPosition, panelPosition)) {
|
|
136
|
+
setPanelPosition(newPosition);
|
|
137
|
+
}
|
|
138
|
+
const newSize = pick(bounds, ['width', 'height']);
|
|
139
|
+
if (!isEqual(newSize, panelSize)) {
|
|
140
|
+
setPanelSize(newSize);
|
|
141
|
+
}
|
|
142
|
+
}, [dynamicBounds, panelPosition, panelSize, setPanelSize]);
|
|
137
143
|
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
window.addEventListener('resize', dynamicResize);
|
|
146
|
+
|
|
147
|
+
return () => {
|
|
148
|
+
window.removeEventListener('resize', dynamicResize);
|
|
149
|
+
};
|
|
150
|
+
}, [dynamicResize]);
|
|
151
|
+
|
|
152
|
+
useInterval(dynamicResize, dynamicBounds ? 250 : null);
|
|
153
|
+
|
|
154
|
+
const dockElement = docked && dockId && dockElements[dockId];
|
|
138
155
|
return (
|
|
139
156
|
<>
|
|
140
157
|
{dockElement && dockElement.isConnected ? (
|
|
@@ -143,9 +160,12 @@ export const Panel = (props: PanelProps): ReactElement => {
|
|
|
143
160
|
<PopoutWindow
|
|
144
161
|
name={uniqueId}
|
|
145
162
|
title={title}
|
|
146
|
-
size={size}
|
|
163
|
+
size={{ width: size.width, height: lastPanelHeightRef.current }}
|
|
147
164
|
popInPanelPosition={panelPosition}
|
|
148
|
-
popInPanelSize={
|
|
165
|
+
popInPanelSize={{
|
|
166
|
+
width: panelSize.width,
|
|
167
|
+
height: panelSize.height === 'auto' ? lastPanelHeightRef.current : panelSize.height,
|
|
168
|
+
}}
|
|
149
169
|
>
|
|
150
170
|
<div
|
|
151
171
|
ref={panelRef}
|
|
@@ -37,25 +37,27 @@ export const PanelInstanceContextProvider = ({
|
|
|
37
37
|
|
|
38
38
|
const undock = useCallback(() => setDockId(undefined), []);
|
|
39
39
|
|
|
40
|
+
const uniqueId = panelInstance.uniqueId;
|
|
41
|
+
|
|
40
42
|
return (
|
|
41
43
|
<PanelInstanceContext.Provider
|
|
42
44
|
value={{
|
|
43
45
|
title,
|
|
44
46
|
setTitle,
|
|
45
|
-
uniqueId
|
|
47
|
+
uniqueId,
|
|
46
48
|
bounds,
|
|
47
|
-
panelName:
|
|
49
|
+
panelName: uniqueId,
|
|
48
50
|
panelClose: () => {
|
|
49
51
|
panelInstance.window?.close();
|
|
50
52
|
panelInstance.window = null;
|
|
51
|
-
closePanel(
|
|
53
|
+
closePanel(uniqueId);
|
|
52
54
|
},
|
|
53
55
|
panelTogglePopout: togglePopOut,
|
|
54
56
|
panelPoppedOut: poppedOut,
|
|
55
57
|
setPanelWindow: (w: Window) => {
|
|
56
58
|
panelInstance.window = w;
|
|
57
59
|
},
|
|
58
|
-
bringPanelToFront: () => bringPanelToFront(
|
|
60
|
+
bringPanelToFront: () => bringPanelToFront(uniqueId),
|
|
59
61
|
zIndex: panelInstance.zIndex,
|
|
60
62
|
docked: !poppedOut && !!dockId && !!dockElements[dockId],
|
|
61
63
|
dockId,
|
|
@@ -24,7 +24,7 @@ export interface PanelsContextType {
|
|
|
24
24
|
// returns unique id of panel that was created/or arleady found, otherwise null on error.
|
|
25
25
|
openPanel: (args: OpenPanelOptions) => string | null;
|
|
26
26
|
closePanel: (uniqueId: string) => void;
|
|
27
|
-
bringPanelToFront: (
|
|
27
|
+
bringPanelToFront: (panelUniqueId: string) => void;
|
|
28
28
|
nextStackPosition: () => PanelPosition;
|
|
29
29
|
dockElements: Record<string, HTMLDivElement>;
|
|
30
30
|
setDockElement: (id: string, element: HTMLDivElement) => void;
|
|
@@ -50,7 +50,12 @@ export const PanelsContextProvider = ({
|
|
|
50
50
|
);
|
|
51
51
|
|
|
52
52
|
const bringPanelToFront = useCallback(
|
|
53
|
-
(
|
|
53
|
+
(panelUniqueId: string) => {
|
|
54
|
+
const panelInstance = panelInstances.find((panelInstance) => panelInstance.uniqueId === panelUniqueId);
|
|
55
|
+
if (!panelInstance) {
|
|
56
|
+
console.warn(`bringPanelToFront cannot find panel with uniqueId: ${panelUniqueId}`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
54
59
|
if (panelInstance.window) {
|
|
55
60
|
panelInstance.window.focus();
|
|
56
61
|
} else {
|
|
@@ -74,7 +79,7 @@ export const PanelsContextProvider = ({
|
|
|
74
79
|
if (existingPanelInstance.window) {
|
|
75
80
|
existingPanelInstance.window?.focus();
|
|
76
81
|
} else {
|
|
77
|
-
bringPanelToFront(
|
|
82
|
+
bringPanelToFront(uniqueId);
|
|
78
83
|
}
|
|
79
84
|
return existingPanelInstance.uniqueId;
|
|
80
85
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
|
+
import { pick } from 'lodash-es';
|
|
2
3
|
import { Dispatch, ReactElement, useCallback, useContext, useEffect, useRef } from 'react';
|
|
3
4
|
import { Rnd } from 'react-rnd';
|
|
4
5
|
|
|
@@ -37,9 +38,14 @@ export function PopinWindow({
|
|
|
37
38
|
|
|
38
39
|
const centerWindow = useCallback(() => {
|
|
39
40
|
const b = panelRef.current?.getSelfElement()?.getBoundingClientRect();
|
|
41
|
+
let height = b?.height ?? size.height;
|
|
42
|
+
// You can't auto height a centered panel
|
|
43
|
+
if (height === 'auto') {
|
|
44
|
+
height = 400;
|
|
45
|
+
}
|
|
40
46
|
setPanelPosition({
|
|
41
47
|
x: Math.max((window.innerWidth - (b?.width ?? size.width)) / 2, 0),
|
|
42
|
-
y: Math.max((window.innerHeight -
|
|
48
|
+
y: Math.max((window.innerHeight - height) / 2, 0),
|
|
43
49
|
});
|
|
44
50
|
}, [setPanelPosition, size.height, size.width]);
|
|
45
51
|
|
|
@@ -106,7 +112,7 @@ export function PopinWindow({
|
|
|
106
112
|
minWidth={minWidth}
|
|
107
113
|
minHeight={minHeight}
|
|
108
114
|
position={panelPosition}
|
|
109
|
-
size={panelSize}
|
|
115
|
+
size={panelSize.height === 'auto' ? (pick(panelSize, 'width') as PanelSize) : panelSize}
|
|
110
116
|
style={{ zIndex }}
|
|
111
117
|
disableDragging={!resizeable}
|
|
112
118
|
enableResizing={resizeable}
|
|
@@ -6,15 +6,15 @@ import ReactDOM from 'react-dom';
|
|
|
6
6
|
import { makePopoutId } from './generateId';
|
|
7
7
|
import { openExternalWindow, OpenExternalWindowResponse } from './openExternalWindow';
|
|
8
8
|
import { PanelInstanceContext } from './PanelInstanceContext';
|
|
9
|
-
import { PanelPosition,
|
|
9
|
+
import { PanelPosition, PanelSizeExact } from './types';
|
|
10
10
|
import { useRestoreStateFrom, useSaveStateIn } from './usePanelStateHandler';
|
|
11
11
|
|
|
12
12
|
interface _PopoutWindowProps {
|
|
13
13
|
name: string;
|
|
14
14
|
title?: string; // The title of the popout window
|
|
15
|
-
size:
|
|
15
|
+
size: PanelSizeExact;
|
|
16
16
|
popInPanelPosition: PanelPosition;
|
|
17
|
-
popInPanelSize:
|
|
17
|
+
popInPanelSize: PanelSizeExact;
|
|
18
18
|
|
|
19
19
|
//says if we want to watch for any dynamic style changes
|
|
20
20
|
//only needed when using UI libraries like MUI
|
|
@@ -62,7 +62,7 @@ export const PopoutWindow = ({
|
|
|
62
62
|
const oew = openExternalWindowFn ?? openExternalWindow;
|
|
63
63
|
const { externalWindow, onCloseExternalWindow } = oew({
|
|
64
64
|
title,
|
|
65
|
-
size: panelSize,
|
|
65
|
+
size: panelSize.height === 'auto' ? { ...panelSize, height: 400 } : (panelSize as PanelSizeExact),
|
|
66
66
|
setContainerElement,
|
|
67
67
|
setHeadElement,
|
|
68
68
|
observeStyleChanges,
|
|
@@ -2,7 +2,7 @@ import { Dispatch, SetStateAction } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { makePopoutId, popoutWindowDivId } from './generateId';
|
|
4
4
|
import { copyStyleSheets, observeStyleSheetChanges } from './handleStyleSheetsChanges';
|
|
5
|
-
import { PanelPosition } from './types';
|
|
5
|
+
import { PanelPosition, PanelSizeExact } from './types';
|
|
6
6
|
|
|
7
7
|
export type OpenExternalWindowResponse = {
|
|
8
8
|
externalWindow: Window | null;
|
|
@@ -26,7 +26,7 @@ export const openExternalWindow = ({
|
|
|
26
26
|
position,
|
|
27
27
|
}: {
|
|
28
28
|
title: string;
|
|
29
|
-
size:
|
|
29
|
+
size: PanelSizeExact;
|
|
30
30
|
setContainerElement: Dispatch<SetStateAction<HTMLElement | undefined>>;
|
|
31
31
|
setHeadElement: Dispatch<SetStateAction<HTMLElement | undefined>>;
|
|
32
32
|
observeStyleChanges?: boolean;
|
|
@@ -3,13 +3,7 @@ import { PropsWithChildren } from 'react';
|
|
|
3
3
|
import { PanelPosition } from './PanelPosition';
|
|
4
4
|
import { PanelSize } from './PanelSize';
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
selector: string;
|
|
8
|
-
edgeX?: 'left' | 'right';
|
|
9
|
-
edgeY?: 'top' | 'bottom';
|
|
10
|
-
offsetX?: number;
|
|
11
|
-
offsetY?: number;
|
|
12
|
-
}
|
|
6
|
+
export type PanelBounds = PanelPosition & PanelSize;
|
|
13
7
|
|
|
14
8
|
interface _PanelProps {
|
|
15
9
|
className?: string;
|
|
@@ -18,11 +12,11 @@ interface _PanelProps {
|
|
|
18
12
|
minHeight?: number | string;
|
|
19
13
|
minWidth?: number | string;
|
|
20
14
|
modal?: boolean;
|
|
21
|
-
|
|
15
|
+
dynamicBounds?: () => PanelBounds | null;
|
|
16
|
+
position?: PanelPosition | 'tile' | 'center';
|
|
22
17
|
resizeable?: boolean;
|
|
23
18
|
size?: PanelSize;
|
|
24
19
|
title: string;
|
|
25
|
-
ignoreSavedState?: boolean;
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
export type PanelProps = PropsWithChildren<_PanelProps>;
|