Skip to content

Commit 5fc5ed3

Browse files
committed
fix: 🐛 Responsive mini map
1 parent 1bf419e commit 5fc5ed3

2 files changed

Lines changed: 65 additions & 41 deletions

File tree

src/components/mini-map/mini-map.tsx

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
/* eslint-disable react/require-default-props */
2-
import React, { useMemo, useRef, useState } from "react";
2+
import React, {
3+
useCallback,
4+
useEffect,
5+
useMemo,
6+
useRef,
7+
useState,
8+
} from "react";
39

410
import {
511
useTransformContext,
612
useTransformEffect,
713
useTransformInit,
814
} from "hooks";
915
import { useResize } from "./use-resize.hook";
16+
import { ReactZoomPanPinchRef } from "models";
1017

1118
export type MiniMapProps = {
19+
children: React.ReactNode;
1220
width?: number;
1321
height?: number;
14-
children: React.ReactNode;
1522
} & React.DetailedHTMLProps<
1623
React.HTMLAttributes<HTMLDivElement>,
1724
HTMLDivElement
@@ -34,33 +41,37 @@ export const MiniMap: React.FC<MiniMapProps> = ({
3441
children,
3542
...rest
3643
}) => {
37-
const [size, setSize] = useState({ width: 0, height: 0 });
44+
const [initialized, setInitialized] = useState(false);
3845
const instance = useTransformContext();
46+
const miniMapInstance = useRef<ReactZoomPanPinchRef>(null);
3947

48+
const mainRef = useRef<HTMLDivElement | null>(null);
4049
const wrapperRef = useRef<HTMLDivElement | null>(null);
4150
const previewRef = useRef<HTMLDivElement | null>(null);
4251

43-
const getContentSize = () => {
52+
const getContentSize = useCallback(() => {
4453
if (instance.contentComponent) {
54+
const rect = instance.contentComponent.getBoundingClientRect();
55+
4556
return {
46-
width: instance.contentComponent.offsetWidth,
47-
height: instance.contentComponent.offsetHeight,
57+
width: rect.width / instance.transformState.scale,
58+
height: rect.height / instance.transformState.scale,
4859
};
4960
}
5061
return {
5162
width: 0,
5263
height: 0,
5364
};
54-
};
65+
}, [instance.contentComponent, instance.transformState.scale]);
5566

56-
const computeMiniMapScale = () => {
67+
const computeMiniMapScale = useCallback(() => {
5768
const contentSize = getContentSize();
5869
const scaleX = width / contentSize.width;
5970
const scaleY = height / contentSize.height;
6071
const scale = scaleY > scaleX ? scaleX : scaleY;
6172

6273
return scale;
63-
};
74+
}, [getContentSize, height, width]);
6475

6576
const computeMiniMapSize = () => {
6677
const contentSize = getContentSize();
@@ -72,55 +83,53 @@ export const MiniMap: React.FC<MiniMapProps> = ({
7283
return { width: contentSize.width * scaleY, height };
7384
};
7485

75-
const computeWrapperStyle = () => {
76-
return {
77-
width: instance.contentComponent?.offsetWidth || 0,
78-
height: instance.contentComponent?.offsetHeight || 0,
79-
};
80-
};
81-
8286
const computeMiniMapStyle = () => {
8387
const scale = computeMiniMapScale();
84-
return {
88+
const style = {
8589
transform: `scale(${scale || 1})`,
8690
transformOrigin: "0% 0%",
8791
position: "absolute",
8892
boxSizing: "border-box",
8993
zIndex: 1,
9094
overflow: "hidden",
9195
} as const;
96+
97+
Object.keys(style).forEach((key) => {
98+
if (wrapperRef.current) {
99+
wrapperRef.current.style[key] = style[key];
100+
}
101+
});
92102
};
93103

94104
const transformMiniMap = () => {
95-
const style = computeWrapperStyle();
105+
computeMiniMapStyle();
106+
const miniSize = computeMiniMapSize();
107+
const wrapSize = getContentSize();
96108
if (wrapperRef.current) {
97-
wrapperRef.current.style.width = `${style.width}px`;
98-
wrapperRef.current.style.width = `${style.width}px`;
99-
wrapperRef.current.style.height = `${style.height}px`;
109+
wrapperRef.current.style.width = `${wrapSize.width}px`;
110+
wrapperRef.current.style.height = `${wrapSize.height}px`;
111+
}
112+
if (mainRef.current) {
113+
mainRef.current.style.width = `${miniSize.width}px`;
114+
mainRef.current.style.height = `${miniSize.height}px`;
100115
}
101116
if (previewRef.current) {
117+
const size = getContentSize();
102118
const scale = computeMiniMapScale();
103119
const previewScale = scale * (1 / instance.transformState.scale);
104120
const transform = instance.handleTransformStyles(
105121
-instance.transformState.positionX * previewScale,
106122
-instance.transformState.positionY * previewScale,
107123
1,
108124
);
125+
109126
previewRef.current.style.transform = transform;
110-
previewRef.current.style.width = `${style.width * previewScale}px`;
111-
previewRef.current.style.height = `${style.height * previewScale}px`;
127+
previewRef.current.style.width = `${size.width * previewScale}px`;
128+
previewRef.current.style.height = `${size.height * previewScale}px`;
112129
}
113130
};
114131

115132
const initialize = () => {
116-
const style = computeMiniMapStyle();
117-
const initSize = computeMiniMapSize();
118-
setSize(initSize);
119-
Object.keys(style).forEach((key) => {
120-
if (wrapperRef.current) {
121-
wrapperRef.current.style[key] = style[key];
122-
}
123-
});
124133
transformMiniMap();
125134
};
126135

@@ -130,24 +139,37 @@ export const MiniMap: React.FC<MiniMapProps> = ({
130139

131140
useTransformInit(() => {
132141
initialize();
142+
setInitialized(true);
133143
});
134144

135-
useResize(instance.contentComponent, () => {
136-
initialize();
137-
});
145+
useResize(instance.contentComponent, initialize, [initialized]);
146+
147+
useEffect(() => {
148+
return instance.onChange((zpp) => {
149+
const scale = computeMiniMapScale();
150+
if (miniMapInstance.current) {
151+
miniMapInstance.current.instance.transformState.scale =
152+
zpp.instance.transformState.scale;
153+
miniMapInstance.current.instance.transformState.positionX =
154+
zpp.instance.transformState.positionX * scale;
155+
miniMapInstance.current.instance.transformState.positionY =
156+
zpp.instance.transformState.positionY * scale;
157+
}
158+
});
159+
}, [computeMiniMapScale, instance, miniMapInstance]);
138160

139161
const wrapperStyle = useMemo(() => {
140162
return {
141-
...size,
142163
position: "relative",
143164
zIndex: 2,
144165
overflow: "hidden",
145166
} as const;
146-
}, [size]);
167+
}, []);
147168

148169
return (
149170
<div
150171
{...rest}
172+
ref={mainRef}
151173
style={wrapperStyle}
152174
className={`rzpp-mini-map ${rest.className || ""}`}
153175
>

src/components/mini-map/use-resize.hook.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useRef } from "react";
1+
import { useLayoutEffect, useRef } from "react";
22

33
export type NonNullableKeys<T> = {
44
[P in keyof T]-?: NonNullable<T[P]>;
@@ -27,14 +27,15 @@ type ResizeHandler<T extends HTMLElement> = (
2727
export const useResize = <T extends HTMLElement>(
2828
ref: Nullable<T>,
2929
onResize: ResizeHandler<T>,
30+
dependencies: any[],
3031
) => {
3132
const resizeObserverRef = useRef<ResizeObserver>();
3233

3334
const rectRef = useRef(initialElementRect);
3435

3536
const didUnmount = useRef(false);
3637

37-
useEffect(() => {
38+
useLayoutEffect(() => {
3839
didUnmount.current = false;
3940
if (ref) {
4041
resizeObserverRef.current = new ResizeObserver(
@@ -44,8 +45,8 @@ export const useResize = <T extends HTMLElement>(
4445
!Array.isArray(entries) ||
4546
!entries.length ||
4647
didUnmount.current ||
47-
newSize.width === rectRef.current.width ||
48-
newSize.height === rectRef.current.height
48+
(newSize.width === rectRef.current.width &&
49+
newSize.height === rectRef.current.height)
4950
)
5051
return;
5152

@@ -63,5 +64,6 @@ export const useResize = <T extends HTMLElement>(
6364
resizeObserverRef.current?.unobserve(ref);
6465
}
6566
};
66-
}, [onResize, ref]);
67+
// eslint-disable-next-line react-hooks/exhaustive-deps
68+
}, [onResize, ref, ...dependencies]);
6769
};

0 commit comments

Comments
 (0)