앱 위젯에서 구분선 리사이즈 안되는 문제 수정
This commit is contained in:
@@ -37,9 +37,27 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
nativeShell = false,
|
||||
}) => {
|
||||
const [pickSlotId, setPickSlotId] = useState<string | null>(null);
|
||||
const bodyWrapRef = useRef<HTMLDivElement>(null);
|
||||
const bodyRef = useRef<HTMLDivElement>(null);
|
||||
const [bodySize, setBodySize] = useState({ width: 0, height: 0 });
|
||||
|
||||
const resolveBodySize = useCallback(() => {
|
||||
const wrap = bodyWrapRef.current;
|
||||
const body = bodyRef.current;
|
||||
const width = wrap?.clientWidth ?? body?.clientWidth ?? bodySize.width;
|
||||
const height = wrap?.clientHeight ?? body?.clientHeight ?? bodySize.height;
|
||||
if (width > 0 && height > 0) {
|
||||
return { width, height };
|
||||
}
|
||||
if (nativeShell) {
|
||||
return {
|
||||
width: width > 0 ? width : instance.width,
|
||||
height: height > 0 ? height : instance.height,
|
||||
};
|
||||
}
|
||||
return { width, height };
|
||||
}, [bodySize.height, bodySize.width, instance.height, instance.width, nativeShell]);
|
||||
|
||||
const rowFr = instance.rowFr?.length === instance.rows
|
||||
? instance.rowFr
|
||||
: defaultGridFr(instance.rows);
|
||||
@@ -92,15 +110,17 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
onUpdateGridFr(next, colFrRef.current);
|
||||
}, [onUpdateGridFr]);
|
||||
|
||||
const notifyLayoutSync = useCallback(() => {
|
||||
window.dispatchEvent(new CustomEvent('gc-widget-layout-sync'));
|
||||
}, []);
|
||||
|
||||
const { onColSplitPointerDown, onRowSplitPointerDown } = useFloatingWidgetGridSplit({
|
||||
onColFrChange: handleColFrChange,
|
||||
onRowFrChange: handleRowFrChange,
|
||||
getColFr: () => colFrRef.current,
|
||||
getRowFr: () => rowFrRef.current,
|
||||
getBodySize: () => ({
|
||||
width: bodyRef.current?.clientWidth ?? bodySize.width,
|
||||
height: bodyRef.current?.clientHeight ?? bodySize.height,
|
||||
}),
|
||||
getBodySize: resolveBodySize,
|
||||
onSplitEnd: notifyLayoutSync,
|
||||
});
|
||||
|
||||
const updateSlot = useCallback((slotId: string, updater: (s: WidgetSlot) => WidgetSlot) => {
|
||||
@@ -119,24 +139,30 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
|
||||
/* 창·그리드 크기 변경 시 내부 차트·위젯 레이아웃 재동기화 */
|
||||
useEffect(() => {
|
||||
const el = bodyRef.current;
|
||||
const el = bodyWrapRef.current ?? bodyRef.current;
|
||||
if (!el) return undefined;
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
const scheduleLayoutSync = () => {
|
||||
setBodySize({ width: el.clientWidth, height: el.clientHeight });
|
||||
const wrap = bodyWrapRef.current;
|
||||
const body = bodyRef.current;
|
||||
setBodySize({
|
||||
width: wrap?.clientWidth ?? body?.clientWidth ?? 0,
|
||||
height: wrap?.clientHeight ?? body?.clientHeight ?? 0,
|
||||
});
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
window.dispatchEvent(new CustomEvent('gc-widget-layout-sync'));
|
||||
}, 80);
|
||||
timer = setTimeout(notifyLayoutSync, 80);
|
||||
};
|
||||
scheduleLayoutSync();
|
||||
const ro = new ResizeObserver(() => scheduleLayoutSync());
|
||||
ro.observe(el);
|
||||
if (bodyRef.current && bodyRef.current !== el) {
|
||||
ro.observe(bodyRef.current);
|
||||
}
|
||||
return () => {
|
||||
if (timer) clearTimeout(timer);
|
||||
ro.disconnect();
|
||||
};
|
||||
}, [instance.rows, instance.cols]);
|
||||
}, [instance.rows, instance.cols, notifyLayoutSync]);
|
||||
|
||||
const colSplitPositions = useMemo(
|
||||
() => gridSplitPositions(colFr, bodySize.width, 1),
|
||||
@@ -193,7 +219,7 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="fw-window-body-wrap">
|
||||
<div ref={bodyWrapRef} className="fw-window-body-wrap">
|
||||
<div
|
||||
ref={bodyRef}
|
||||
className="fw-window-body"
|
||||
@@ -221,6 +247,7 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
key={`col-${index}`}
|
||||
className="fw-grid-splitter fw-grid-splitter--v"
|
||||
style={{ left }}
|
||||
data-no-chart-pan="true"
|
||||
role="separator"
|
||||
aria-orientation="vertical"
|
||||
aria-label={`열 ${index + 1}·${index + 2} 경계 조절`}
|
||||
@@ -232,6 +259,7 @@ const FloatingWidgetWindow: React.FC<Props> = ({
|
||||
key={`row-${index}`}
|
||||
className="fw-grid-splitter fw-grid-splitter--h"
|
||||
style={{ top }}
|
||||
data-no-chart-pan="true"
|
||||
role="separator"
|
||||
aria-orientation="horizontal"
|
||||
aria-label={`행 ${index + 1}·${index + 2} 경계 조절`}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
FLOATING_WIDGET_CELL_MIN_W,
|
||||
FLOATING_WIDGET_GRID_GAP,
|
||||
} from '../types/floatingWidget';
|
||||
import { bindWindowDrag, isPrimaryPointerButton } from '../utils/pointerDrag';
|
||||
import { isPrimaryPointerButton } from '../utils/pointerDrag';
|
||||
|
||||
type SplitAxis = 'col' | 'row';
|
||||
|
||||
@@ -15,6 +15,7 @@ interface Options {
|
||||
getColFr: () => number[];
|
||||
getRowFr: () => number[];
|
||||
getBodySize: () => { width: number; height: number };
|
||||
onSplitEnd?: () => void;
|
||||
}
|
||||
|
||||
export function useFloatingWidgetGridSplit({
|
||||
@@ -23,18 +24,12 @@ export function useFloatingWidgetGridSplit({
|
||||
getColFr,
|
||||
getRowFr,
|
||||
getBodySize,
|
||||
onSplitEnd,
|
||||
}: Options) {
|
||||
const unbindDragRef = useRef<(() => void) | null>(null);
|
||||
|
||||
const endDrag = useCallback(() => {
|
||||
unbindDragRef.current?.();
|
||||
unbindDragRef.current = null;
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
}, []);
|
||||
const activeDragRef = useRef<(() => void) | null>(null);
|
||||
|
||||
useEffect(() => () => {
|
||||
unbindDragRef.current?.();
|
||||
activeDragRef.current?.();
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
}, []);
|
||||
@@ -60,43 +55,67 @@ export function useFloatingWidgetGridSplit({
|
||||
const startY = e.clientY;
|
||||
const startColFr = [...getColFr()];
|
||||
const startRowFr = [...getRowFr()];
|
||||
const startSize = getBodySize();
|
||||
|
||||
document.body.style.cursor = axis === 'col' ? 'col-resize' : 'row-resize';
|
||||
document.body.style.userSelect = 'none';
|
||||
|
||||
unbindDragRef.current?.();
|
||||
unbindDragRef.current = bindWindowDrag(
|
||||
(xy) => {
|
||||
if (axis === 'col') {
|
||||
const delta = xy.x - startX;
|
||||
onColFrChange(adjustAdjacentGridFr(
|
||||
startColFr,
|
||||
index,
|
||||
delta,
|
||||
startSize.width,
|
||||
FLOATING_WIDGET_GRID_GAP,
|
||||
FLOATING_WIDGET_CELL_MIN_W,
|
||||
));
|
||||
return;
|
||||
}
|
||||
const delta = xy.y - startY;
|
||||
onRowFrChange(adjustAdjacentGridFr(
|
||||
startRowFr,
|
||||
activeDragRef.current?.();
|
||||
|
||||
const onMove = (ev: PointerEvent) => {
|
||||
const size = getBodySize();
|
||||
if (axis === 'col') {
|
||||
if (size.width <= 0) return;
|
||||
const delta = ev.clientX - startX;
|
||||
onColFrChange(adjustAdjacentGridFr(
|
||||
startColFr,
|
||||
index,
|
||||
delta,
|
||||
startSize.height,
|
||||
size.width,
|
||||
FLOATING_WIDGET_GRID_GAP,
|
||||
FLOATING_WIDGET_CELL_MIN_H,
|
||||
FLOATING_WIDGET_CELL_MIN_W,
|
||||
));
|
||||
},
|
||||
() => {
|
||||
handle.classList.remove('fw-grid-splitter--active');
|
||||
endDrag();
|
||||
},
|
||||
{ preventTouchScroll: true },
|
||||
);
|
||||
}, [endDrag, getBodySize, getColFr, getRowFr, onColFrChange, onRowFrChange]);
|
||||
return;
|
||||
}
|
||||
if (size.height <= 0) return;
|
||||
const delta = ev.clientY - startY;
|
||||
onRowFrChange(adjustAdjacentGridFr(
|
||||
startRowFr,
|
||||
index,
|
||||
delta,
|
||||
size.height,
|
||||
FLOATING_WIDGET_GRID_GAP,
|
||||
FLOATING_WIDGET_CELL_MIN_H,
|
||||
));
|
||||
};
|
||||
|
||||
const onUp = (ev: PointerEvent) => {
|
||||
handle.classList.remove('fw-grid-splitter--active');
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
try {
|
||||
if (handle.hasPointerCapture(ev.pointerId)) {
|
||||
handle.releasePointerCapture(ev.pointerId);
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
document.removeEventListener('pointermove', onMove, true);
|
||||
document.removeEventListener('pointerup', onUp, true);
|
||||
document.removeEventListener('pointercancel', onUp, true);
|
||||
activeDragRef.current = null;
|
||||
onSplitEnd?.();
|
||||
};
|
||||
|
||||
document.addEventListener('pointermove', onMove, { capture: true });
|
||||
document.addEventListener('pointerup', onUp, { capture: true });
|
||||
document.addEventListener('pointercancel', onUp, { capture: true });
|
||||
|
||||
activeDragRef.current = () => {
|
||||
document.removeEventListener('pointermove', onMove, true);
|
||||
document.removeEventListener('pointerup', onUp, true);
|
||||
document.removeEventListener('pointercancel', onUp, true);
|
||||
};
|
||||
}, [getBodySize, getColFr, getRowFr, onColFrChange, onRowFrChange, onSplitEnd]);
|
||||
|
||||
const onColSplitPointerDown = useCallback(
|
||||
(index: number) => (e: React.PointerEvent) => startSplit(e, 'col', index),
|
||||
|
||||
@@ -648,8 +648,22 @@ html.fw-native-widget #root {
|
||||
.fw-window--native .fw-window-body-wrap {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fw-window--native .fw-window-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.fw-window--native .fw-grid-split-layer {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.fw-window--native .fw-grid-splitter {
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user