mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
fix use dimensions hook (#905)
This commit is contained in:
parent
2f5351e8ca
commit
cacde1b0ba
@ -11,7 +11,7 @@ import {
|
||||
Input,
|
||||
} from "@/app/block/blockutil";
|
||||
import { Button } from "@/app/element/button";
|
||||
import { useWidth } from "@/app/hook/useWidth";
|
||||
import { useDimensionsWithCallbackRef } from "@/app/hook/useDimensions";
|
||||
import { TypeAheadModal } from "@/app/modals/typeaheadmodal";
|
||||
import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||
import {
|
||||
@ -294,8 +294,8 @@ const ConnStatusOverlay = React.memo(
|
||||
const connName = blockData.meta?.connection;
|
||||
const connStatus = jotai.useAtomValue(getConnStatusAtom(connName));
|
||||
const isLayoutMode = jotai.useAtomValue(atoms.controlShiftDelayAtom);
|
||||
const overlayRef = React.useRef<HTMLDivElement>(null);
|
||||
const width = useWidth(overlayRef);
|
||||
const [overlayRefCallback, _, domRect] = useDimensionsWithCallbackRef(30);
|
||||
const width = domRect?.width;
|
||||
const [showError, setShowError] = React.useState(false);
|
||||
const blockNum = jotai.useAtomValue(nodeModel.blockNum);
|
||||
|
||||
@ -334,7 +334,7 @@ const ConnStatusOverlay = React.memo(
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="connstatus-overlay" ref={overlayRef}>
|
||||
<div className="connstatus-overlay" ref={overlayRefCallback}>
|
||||
<div className="connstatus-content">
|
||||
<div className={clsx("connstatus-status-icon-wrapper", { "has-error": showError })}>
|
||||
{showIcon && <i className="fa-solid fa-triangle-exclamation"></i>}
|
||||
|
@ -1,12 +1,11 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { useHeight } from "@/app/hook/useHeight";
|
||||
import { useWidth } from "@/app/hook/useWidth";
|
||||
import clsx from "clsx";
|
||||
import React, { memo, useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
import { useDimensionsWithExistingRef } from "@/app/hook/useDimensions";
|
||||
import "./menu.less";
|
||||
|
||||
type MenuItem = {
|
||||
@ -143,9 +142,9 @@ const Menu = memo(
|
||||
const [position, setPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 });
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const subMenuRefs = useRef<{ [key: string]: React.RefObject<HTMLDivElement> }>({});
|
||||
|
||||
const width = useWidth(scopeRef);
|
||||
const height = useHeight(scopeRef);
|
||||
const domRect = useDimensionsWithExistingRef(scopeRef, 30);
|
||||
const width = domRect?.width ?? 0;
|
||||
const height = domRect?.height ?? 0;
|
||||
|
||||
items.forEach((_, idx) => {
|
||||
const key = `${idx}`;
|
||||
|
@ -1,67 +1,99 @@
|
||||
import useResizeObserver from "@react-hook/resize-observer";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import * as React from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { debounce } from "throttle-debounce";
|
||||
|
||||
/**
|
||||
* Get the current dimensions for the specified element, and whether it is currently changing size. Update when the element resizes.
|
||||
* @param ref The reference to the element to observe.
|
||||
* @param delay The debounce delay to use for updating the dimensions.
|
||||
* @returns The dimensions of the element, and direction in which the dimensions are changing.
|
||||
*/
|
||||
const useDimensions = (ref: React.RefObject<HTMLElement>, delay = 0) => {
|
||||
const [dimensions, setDimensions] = useState<{
|
||||
height: number | null;
|
||||
width: number | null;
|
||||
widthDirection?: string;
|
||||
heightDirection?: string;
|
||||
}>({
|
||||
height: null,
|
||||
width: null,
|
||||
});
|
||||
|
||||
const previousDimensions = useRef<{ height: number | null; width: number | null }>({
|
||||
height: null,
|
||||
width: null,
|
||||
});
|
||||
|
||||
const updateDimensions = useCallback((entry: ResizeObserverEntry) => {
|
||||
const parentHeight = entry.contentRect.height;
|
||||
const parentWidth = entry.contentRect.width;
|
||||
|
||||
let widthDirection = "";
|
||||
let heightDirection = "";
|
||||
|
||||
if (previousDimensions.current.width !== null && previousDimensions.current.height !== null) {
|
||||
if (parentWidth > previousDimensions.current.width) {
|
||||
widthDirection = "expanding";
|
||||
} else if (parentWidth < previousDimensions.current.width) {
|
||||
widthDirection = "shrinking";
|
||||
} else {
|
||||
widthDirection = "unchanged";
|
||||
}
|
||||
|
||||
if (parentHeight > previousDimensions.current.height) {
|
||||
heightDirection = "expanding";
|
||||
} else if (parentHeight < previousDimensions.current.height) {
|
||||
heightDirection = "shrinking";
|
||||
} else {
|
||||
heightDirection = "unchanged";
|
||||
}
|
||||
}
|
||||
|
||||
previousDimensions.current = { height: parentHeight, width: parentWidth };
|
||||
|
||||
setDimensions({ height: parentHeight, width: parentWidth, widthDirection, heightDirection });
|
||||
// returns a callback ref, a ref object (that is set from the callback), and the width
|
||||
// pass debounceMs of null to not debounce
|
||||
export function useDimensionsWithCallbackRef<T extends HTMLElement>(
|
||||
debounceMs: number = null
|
||||
): [(node: T) => void, React.RefObject<T>, DOMRectReadOnly] {
|
||||
const [domRect, setDomRect] = useState<DOMRectReadOnly>(null);
|
||||
const [htmlElem, setHtmlElem] = useState<T>(null);
|
||||
const rszObjRef = React.useRef<ResizeObserver>(null);
|
||||
const oldHtmlElem = React.useRef<T>(null);
|
||||
const ref = React.useRef<T>(null);
|
||||
const refCallback = useCallback((node: T) => {
|
||||
setHtmlElem(node);
|
||||
ref.current = node;
|
||||
}, []);
|
||||
|
||||
const fUpdateDimensions = useCallback(delay > 0 ? debounce(delay, updateDimensions) : updateDimensions, [
|
||||
updateDimensions,
|
||||
delay,
|
||||
const setDomRectDebounced = React.useCallback(debounceMs == null ? setDomRect : debounce(debounceMs, setDomRect), [
|
||||
debounceMs,
|
||||
setDomRect,
|
||||
]);
|
||||
React.useEffect(() => {
|
||||
if (!rszObjRef.current) {
|
||||
rszObjRef.current = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (domRect == null) {
|
||||
setDomRect(entry.contentRect);
|
||||
} else {
|
||||
setDomRectDebounced(entry.contentRect);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (htmlElem) {
|
||||
rszObjRef.current.observe(htmlElem);
|
||||
oldHtmlElem.current = htmlElem;
|
||||
}
|
||||
return () => {
|
||||
if (oldHtmlElem.current) {
|
||||
rszObjRef.current?.unobserve(oldHtmlElem.current);
|
||||
oldHtmlElem.current = null;
|
||||
}
|
||||
};
|
||||
}, [htmlElem]);
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
rszObjRef.current?.disconnect();
|
||||
};
|
||||
}, []);
|
||||
return [refCallback, ref, domRect];
|
||||
}
|
||||
|
||||
useResizeObserver(ref, fUpdateDimensions);
|
||||
|
||||
return dimensions;
|
||||
};
|
||||
|
||||
export { useDimensions };
|
||||
// will not react to ref changes
|
||||
// pass debounceMs of null to not debounce
|
||||
export function useDimensionsWithExistingRef<T extends HTMLElement>(
|
||||
ref: React.RefObject<T>,
|
||||
debounceMs: number = null
|
||||
): DOMRectReadOnly {
|
||||
const [domRect, setDomRect] = useState<DOMRectReadOnly>(null);
|
||||
const rszObjRef = React.useRef<ResizeObserver>(null);
|
||||
const oldHtmlElem = React.useRef<T>(null);
|
||||
const setDomRectDebounced = React.useCallback(debounceMs == null ? setDomRect : debounce(debounceMs, setDomRect), [
|
||||
debounceMs,
|
||||
setDomRect,
|
||||
]);
|
||||
React.useEffect(() => {
|
||||
if (!rszObjRef.current) {
|
||||
rszObjRef.current = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (domRect == null) {
|
||||
setDomRect(entry.contentRect);
|
||||
} else {
|
||||
setDomRectDebounced(entry.contentRect);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (ref.current) {
|
||||
rszObjRef.current.observe(ref.current);
|
||||
oldHtmlElem.current = ref.current;
|
||||
}
|
||||
return () => {
|
||||
if (oldHtmlElem.current) {
|
||||
rszObjRef.current?.unobserve(oldHtmlElem.current);
|
||||
oldHtmlElem.current = null;
|
||||
}
|
||||
};
|
||||
}, [ref.current]);
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
rszObjRef.current?.disconnect();
|
||||
};
|
||||
}, []);
|
||||
if (ref.current != null) {
|
||||
return ref.current.getBoundingClientRect();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import useResizeObserver from "@react-hook/resize-observer";
|
||||
import { useCallback, useState } from "react";
|
||||
import { debounce } from "throttle-debounce";
|
||||
|
||||
/**
|
||||
* Get the height of the specified element and update it when the element resizes.
|
||||
* @param ref The reference to the element to observe.
|
||||
* @param delay The debounce delay to use for updating the height.
|
||||
* @returns The current height of the element, or null if the element is not yet mounted.
|
||||
*/
|
||||
const useHeight = (ref: React.RefObject<HTMLElement>, delay = 0) => {
|
||||
const [height, setHeight] = useState<number | null>(null);
|
||||
|
||||
const updateHeight = useCallback((entry: ResizeObserverEntry) => {
|
||||
setHeight(entry.contentRect.height);
|
||||
}, []);
|
||||
|
||||
const fUpdateHeight = useCallback(delay > 0 ? debounce(delay, updateHeight) : updateHeight, [updateHeight, delay]);
|
||||
|
||||
useResizeObserver(ref, fUpdateHeight);
|
||||
|
||||
return height;
|
||||
};
|
||||
|
||||
export { useHeight };
|
@ -1,28 +0,0 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import useResizeObserver from "@react-hook/resize-observer";
|
||||
import { useCallback, useState } from "react";
|
||||
import { debounce } from "throttle-debounce";
|
||||
|
||||
/**
|
||||
* Get the width of the specified element and update it when the element resizes.
|
||||
* @param ref The reference to the element to observe.
|
||||
* @param delay The debounce delay to use for updating the width.
|
||||
* @returns The current width of the element, or null if the element is not yet mounted.
|
||||
*/
|
||||
const useWidth = (ref: React.RefObject<HTMLElement>, delay = 0) => {
|
||||
const [width, setWidth] = useState<number | null>(null);
|
||||
|
||||
const updateWidth = useCallback((entry: ResizeObserverEntry) => {
|
||||
setWidth(entry.contentRect.width);
|
||||
}, []);
|
||||
|
||||
const fUpdateWidth = useCallback(delay > 0 ? debounce(delay, updateWidth) : updateWidth, [updateWidth, delay]);
|
||||
|
||||
useResizeObserver(ref, fUpdateWidth);
|
||||
|
||||
return width;
|
||||
};
|
||||
|
||||
export { useWidth };
|
@ -3,7 +3,7 @@
|
||||
|
||||
import { Input } from "@/app/element/input";
|
||||
import { InputDecoration } from "@/app/element/inputdecoration";
|
||||
import { useDimensions } from "@/app/hook/useDimensions";
|
||||
import { useDimensionsWithExistingRef } from "@/app/hook/useDimensions";
|
||||
import { makeIconClass } from "@/util/util";
|
||||
import clsx from "clsx";
|
||||
import React, { forwardRef, useLayoutEffect, useRef } from "react";
|
||||
@ -99,7 +99,9 @@ const TypeAheadModal = ({
|
||||
autoFocus,
|
||||
selectIndex,
|
||||
}: TypeAheadModalProps) => {
|
||||
const { width, height } = useDimensions(blockRef);
|
||||
const domRect = useDimensionsWithExistingRef(blockRef, 30);
|
||||
const width = domRect?.width ?? 0;
|
||||
const height = domRect?.height ?? 0;
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLDivElement>(null);
|
||||
const realInputRef = useRef<HTMLInputElement>(null);
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { useHeight } from "@/app/hook/useHeight";
|
||||
import { useWidth } from "@/app/hook/useWidth";
|
||||
import { getConnStatusAtom, globalStore, WOS } from "@/store/global";
|
||||
import * as util from "@/util/util";
|
||||
import * as Plot from "@observablehq/plot";
|
||||
@ -11,6 +9,7 @@ import * as htl from "htl";
|
||||
import * as jotai from "jotai";
|
||||
import * as React from "react";
|
||||
|
||||
import { useDimensionsWithExistingRef } from "@/app/hook/useDimensions";
|
||||
import { waveEventSubscribe } from "@/app/store/wps";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { WindowRpcClient } from "@/app/store/wshrpcutil";
|
||||
@ -231,8 +230,9 @@ function CpuPlotView({ model, blockId }: CpuPlotViewProps) {
|
||||
const CpuPlotViewInner = React.memo(({ model }: CpuPlotViewProps) => {
|
||||
const containerRef = React.useRef<HTMLInputElement>();
|
||||
const plotData = jotai.useAtomValue(model.dataAtom);
|
||||
const parentHeight = useHeight(containerRef);
|
||||
const parentWidth = useWidth(containerRef);
|
||||
const domRect = useDimensionsWithExistingRef(containerRef, 30);
|
||||
const parentHeight = domRect?.height ?? 0;
|
||||
const parentWidth = domRect?.width ?? 0;
|
||||
const yvals = jotai.useAtomValue(model.metrics);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { useHeight } from "@/app/hook/useHeight";
|
||||
import { useTableNav } from "@table-nav/react";
|
||||
import {
|
||||
createColumnHelper,
|
||||
@ -14,6 +13,7 @@ import { clsx } from "clsx";
|
||||
import Papa from "papaparse";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import { useDimensionsWithExistingRef } from "@/app/hook/useDimensions";
|
||||
import "./csvview.less";
|
||||
|
||||
const MAX_DATA_SIZE = 10 * 1024 * 1024; // 10MB in bytes
|
||||
@ -54,7 +54,8 @@ const CSVView = ({ parentRef, filename, content }: CSVViewProps) => {
|
||||
const [tableLoaded, setTableLoaded] = useState(false);
|
||||
|
||||
const { listeners } = useTableNav();
|
||||
const parentHeight = useHeight(parentRef);
|
||||
const domRect = useDimensionsWithExistingRef(parentRef, 30);
|
||||
const parentHeight = domRect?.height ?? 0;
|
||||
|
||||
const cacheKey = `${filename}`;
|
||||
csvCacheRef.current.set(cacheKey, content);
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { useHeight } from "@/app/hook/useHeight";
|
||||
import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||
import { atoms, createBlock, getApi } from "@/app/store/global";
|
||||
import type { PreviewModel } from "@/app/view/preview/preview";
|
||||
@ -27,6 +26,7 @@ import { quote as shellQuote } from "shell-quote";
|
||||
|
||||
import { OverlayScrollbars } from "overlayscrollbars";
|
||||
|
||||
import { useDimensionsWithExistingRef } from "@/app/hook/useDimensions";
|
||||
import "./directorypreview.less";
|
||||
|
||||
interface DirectoryTableProps {
|
||||
@ -352,8 +352,8 @@ function TableBody({
|
||||
const warningBoxRef = useRef<HTMLDivElement>(null);
|
||||
const osInstanceRef = useRef<OverlayScrollbars>(null);
|
||||
const rowRefs = useRef<HTMLDivElement[]>([]);
|
||||
|
||||
const parentHeight = useHeight(parentRef);
|
||||
const domRect = useDimensionsWithExistingRef(parentRef, 30);
|
||||
const parentHeight = domRect?.height ?? 0;
|
||||
const conn = jotai.useAtomValue(model.connection);
|
||||
|
||||
useEffect(() => {
|
||||
@ -363,7 +363,6 @@ function TableBody({
|
||||
const warningBoxHeight = warningBoxRef.current?.offsetHeight ?? 0;
|
||||
const maxHeightLessHeader = parentHeight - warningBoxHeight;
|
||||
const tbodyHeight = Math.min(maxHeightLessHeader, fullTBodyHeight);
|
||||
|
||||
setBodyHeight(tbodyHeight);
|
||||
}
|
||||
}, [data, parentHeight]);
|
||||
|
@ -55,6 +55,7 @@
|
||||
background-color: rgb(from var(--highlight-bg-color) r g b / 0.1);
|
||||
margin-right: auto;
|
||||
padding: 10px;
|
||||
max-width: 85%;
|
||||
|
||||
.markdown {
|
||||
width: 100%;
|
||||
@ -71,6 +72,7 @@
|
||||
&.chat-msg-user {
|
||||
margin-left: auto;
|
||||
padding: 10px;
|
||||
max-width: 85%;
|
||||
background-color: rgb(from var(--accent-color) r g b / 0.15);
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
import { Button } from "@/app/element/button";
|
||||
import { Markdown } from "@/app/element/markdown";
|
||||
import { TypingIndicator } from "@/app/element/typingindicator";
|
||||
import { useDimensions } from "@/app/hook/useDimensions";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { WindowRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { atoms, fetchWaveFile, globalStore, WOS } from "@/store/global";
|
||||
@ -260,10 +259,7 @@ const ChatItem = ({ chatItem }: ChatItemProps) => {
|
||||
<i className="fa-sharp fa-solid fa-sparkles"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="chat-msg chat-msg-assistant"
|
||||
style={{ maxWidth: "calc(var(--aichat-msg-width) * 1px)" }}
|
||||
>
|
||||
<div className="chat-msg chat-msg-assistant">
|
||||
<Markdown text={text} />
|
||||
</div>
|
||||
</>
|
||||
@ -278,7 +274,7 @@ const ChatItem = ({ chatItem }: ChatItemProps) => {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="chat-msg chat-msg-user" style={{ maxWidth: "calc(var(--aichat-msg-width) * 1px)" }}>
|
||||
<div className="chat-msg chat-msg-user">
|
||||
<Markdown className="msg-text" text={text} />
|
||||
</div>
|
||||
</>
|
||||
@ -439,10 +435,8 @@ const WaveAi = ({ model }: { model: WaveAiModel; blockId: string }) => {
|
||||
const [selectedBlockIdx, setSelectedBlockIdx] = useState<number | null>(null);
|
||||
|
||||
const termFontSize: number = 14;
|
||||
const windowDims = useDimensions(chatWindowRef);
|
||||
const msgWidths = {};
|
||||
const locked = useAtomValue(model.locked);
|
||||
msgWidths["--aichat-msg-width"] = windowDims.width * 0.85;
|
||||
|
||||
// a weird workaround to initialize ansynchronously
|
||||
useEffect(() => {
|
||||
|
Loading…
Reference in New Issue
Block a user