feat(map): add reference layers and stabilize workspace controls
This commit is contained in:
@@ -35,6 +35,7 @@ export interface WorkspaceWindowProps extends Omit<HTMLAttributes<HTMLDivElement
|
||||
minWidth?: number;
|
||||
minHeight?: number;
|
||||
resizable?: boolean;
|
||||
autoHeight?: boolean;
|
||||
active?: boolean;
|
||||
zIndex?: number;
|
||||
closeLabel?: string;
|
||||
@@ -121,6 +122,7 @@ export function WorkspaceWindow({
|
||||
minWidth = 260,
|
||||
minHeight = 180,
|
||||
resizable = true,
|
||||
autoHeight = false,
|
||||
active = false,
|
||||
zIndex,
|
||||
closeLabel = "Закрыть окно",
|
||||
@@ -139,6 +141,10 @@ export function WorkspaceWindow({
|
||||
const interactionRef = useRef<WorkspaceInteraction | null>(null);
|
||||
const rectRef = useRef(rect);
|
||||
const onRectChangeRef = useRef(onRectChange);
|
||||
const headRef = useRef<HTMLElement>(null);
|
||||
const bodyRef = useRef<HTMLDivElement>(null);
|
||||
const bodyContentRef = useRef<HTMLDivElement>(null);
|
||||
const footerRef = useRef<HTMLElement>(null);
|
||||
const [interactionKind, setInteractionKind] = useState<WorkspaceInteraction["kind"] | null>(null);
|
||||
|
||||
rectRef.current = rect;
|
||||
@@ -175,6 +181,52 @@ export function WorkspaceWindow({
|
||||
};
|
||||
}, [boundsRef, maximized, minHeight, minWidth, rect.height, rect.width, rect.x, rect.y]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!autoHeight || maximized) return;
|
||||
const bounds = boundsRef.current;
|
||||
const head = headRef.current;
|
||||
const body = bodyRef.current;
|
||||
const content = bodyContentRef.current;
|
||||
if (!bounds || !head || !body || !content) return;
|
||||
let frame = 0;
|
||||
|
||||
const fitContent = () => {
|
||||
frame = 0;
|
||||
const nextBounds = readBounds();
|
||||
if (!nextBounds) return;
|
||||
const normalized = normalizeRect(rectRef.current, nextBounds, minWidth, minHeight);
|
||||
const bodyStyle = window.getComputedStyle(body);
|
||||
const bodyPadding = Number.parseFloat(bodyStyle.paddingTop || "0")
|
||||
+ Number.parseFloat(bodyStyle.paddingBottom || "0");
|
||||
const desiredHeight = Math.ceil(
|
||||
head.offsetHeight
|
||||
+ content.scrollHeight
|
||||
+ bodyPadding
|
||||
+ (footerRef.current?.offsetHeight ?? 0),
|
||||
);
|
||||
const availableHeight = Math.max(0, nextBounds.height - normalized.y);
|
||||
emitRect({
|
||||
...normalized,
|
||||
height: clampDimension(desiredHeight, minHeight, availableHeight),
|
||||
});
|
||||
};
|
||||
|
||||
const scheduleFit = () => {
|
||||
if (frame) cancelAnimationFrame(frame);
|
||||
frame = requestAnimationFrame(fitContent);
|
||||
};
|
||||
|
||||
scheduleFit();
|
||||
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(scheduleFit);
|
||||
observer?.observe(content);
|
||||
observer?.observe(head);
|
||||
if (footerRef.current) observer?.observe(footerRef.current);
|
||||
return () => {
|
||||
if (frame) cancelAnimationFrame(frame);
|
||||
observer?.disconnect();
|
||||
};
|
||||
}, [autoHeight, boundsRef, maximized, minHeight, minWidth, rect.width]);
|
||||
|
||||
useEffect(() => {
|
||||
const handlePointerMove = (event: globalThis.PointerEvent) => {
|
||||
const interaction = interactionRef.current;
|
||||
@@ -273,6 +325,7 @@ export function WorkspaceWindow({
|
||||
data-active={active ? "true" : undefined}
|
||||
data-maximized={maximized ? "true" : undefined}
|
||||
data-resizable={resizable ? "true" : undefined}
|
||||
data-auto-height={autoHeight ? "true" : undefined}
|
||||
data-interaction={interactionKind ?? undefined}
|
||||
role="dialog"
|
||||
aria-modal="false"
|
||||
@@ -289,6 +342,7 @@ export function WorkspaceWindow({
|
||||
}}
|
||||
>
|
||||
<header
|
||||
ref={headRef}
|
||||
className="nodedc-workspace-window__head"
|
||||
tabIndex={maximized ? -1 : 0}
|
||||
role="group"
|
||||
@@ -326,8 +380,10 @@ export function WorkspaceWindow({
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div className="nodedc-workspace-window__body">{children}</div>
|
||||
{footer ? <footer className="nodedc-workspace-window__footer">{footer}</footer> : null}
|
||||
<div ref={bodyRef} className="nodedc-workspace-window__body">
|
||||
{autoHeight ? <div ref={bodyContentRef} className="nodedc-workspace-window__body-content">{children}</div> : children}
|
||||
</div>
|
||||
{footer ? <footer ref={footerRef} className="nodedc-workspace-window__footer">{footer}</footer> : null}
|
||||
{resizable && !maximized ? (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user