diff --git a/frontend/app/hook/useDimensions.tsx b/frontend/app/hook/useDimensions.tsx index 7455b2622..ac9f9086f 100644 --- a/frontend/app/hook/useDimensions.tsx +++ b/frontend/app/hook/useDimensions.tsx @@ -1,6 +1,13 @@ -import debounce from "lodash.debounce"; -import { useCallback, useEffect, useRef, useState } from "react"; +import useResizeObserver from "@react-hook/resize-observer"; +import { useCallback, useRef, 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, delay = 0) => { const [dimensions, setDimensions] = useState<{ height: number | null; @@ -17,73 +24,42 @@ const useDimensions = (ref: React.RefObject, delay = 0) => { width: null, }); - const updateDimensions = useCallback(() => { - if (ref.current) { - const element = ref.current; - const style = window.getComputedStyle(element); - const paddingTop = parseFloat(style.paddingTop); - const paddingBottom = parseFloat(style.paddingBottom); - const paddingLeft = parseFloat(style.paddingLeft); - const paddingRight = parseFloat(style.paddingRight); - const marginTop = parseFloat(style.marginTop); - const marginBottom = parseFloat(style.marginBottom); - const marginLeft = parseFloat(style.marginLeft); - const marginRight = parseFloat(style.marginRight); + const updateDimensions = useCallback((entry: ResizeObserverEntry) => { + const parentHeight = entry.contentRect.height; + const parentWidth = entry.contentRect.width; - const parentHeight = element.clientHeight - paddingTop - paddingBottom - marginTop - marginBottom; - const parentWidth = element.clientWidth - paddingLeft - paddingRight - marginLeft - marginRight; + let widthDirection = ""; + let heightDirection = ""; - 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"; - } + 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"; } - previousDimensions.current = { height: parentHeight, width: parentWidth }; - - setDimensions({ height: parentHeight, width: parentWidth, widthDirection, heightDirection }); + if (parentHeight > previousDimensions.current.height) { + heightDirection = "expanding"; + } else if (parentHeight < previousDimensions.current.height) { + heightDirection = "shrinking"; + } else { + heightDirection = "unchanged"; + } } - }, [ref]); - const fUpdateDimensions = useCallback(delay > 0 ? debounce(updateDimensions, delay) : updateDimensions, [ + previousDimensions.current = { height: parentHeight, width: parentWidth }; + + setDimensions({ height: parentHeight, width: parentWidth, widthDirection, heightDirection }); + }, []); + + const fUpdateDimensions = useCallback(delay > 0 ? debounce(delay, updateDimensions) : updateDimensions, [ updateDimensions, delay, ]); - useEffect(() => { - const resizeObserver = new ResizeObserver(() => { - fUpdateDimensions(); - }); - - if (ref.current) { - resizeObserver.observe(ref.current); - fUpdateDimensions(); - } - - return () => { - if (ref.current) { - resizeObserver.unobserve(ref.current); - } - if (delay > 0) { - fUpdateDimensions.cancel(); - } - }; - }, [fUpdateDimensions]); + useResizeObserver(ref, fUpdateDimensions); return dimensions; }; diff --git a/frontend/app/hook/useHeight.tsx b/frontend/app/hook/useHeight.tsx index 74791d0fc..e6ce41c31 100644 --- a/frontend/app/hook/useHeight.tsx +++ b/frontend/app/hook/useHeight.tsx @@ -1,46 +1,26 @@ // Copyright 2024, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 -import debounce from "lodash.debounce"; -import { useCallback, useEffect, useState } from "react"; +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, delay = 0) => { const [height, setHeight] = useState(null); - const updateHeight = useCallback(() => { - if (ref.current) { - const element = ref.current; - const style = window.getComputedStyle(element); - const paddingTop = parseFloat(style.paddingTop); - const paddingBottom = parseFloat(style.paddingBottom); - const marginTop = parseFloat(style.marginTop); - const marginBottom = parseFloat(style.marginBottom); - const parentHeight = element.clientHeight - paddingTop - paddingBottom - marginTop - marginBottom; - setHeight(parentHeight); - } + const updateHeight = useCallback((entry: ResizeObserverEntry) => { + setHeight(entry.contentRect.height); }, []); - const fUpdateHeight = useCallback(delay > 0 ? debounce(updateHeight, delay) : updateHeight, [updateHeight, delay]); + const fUpdateHeight = useCallback(delay > 0 ? debounce(delay, updateHeight) : updateHeight, [updateHeight, delay]); - useEffect(() => { - const resizeObserver = new ResizeObserver(() => { - fUpdateHeight(); - }); - - if (ref.current) { - resizeObserver.observe(ref.current); - fUpdateHeight(); - } - - return () => { - if (ref.current) { - resizeObserver.unobserve(ref.current); - } - if (delay > 0) { - fUpdateHeight.cancel(); - } - }; - }, [fUpdateHeight]); + useResizeObserver(ref, fUpdateHeight); return height; }; diff --git a/frontend/app/hook/useWidth.tsx b/frontend/app/hook/useWidth.tsx index 3eb7eb74c..c7007d7f4 100644 --- a/frontend/app/hook/useWidth.tsx +++ b/frontend/app/hook/useWidth.tsx @@ -1,46 +1,26 @@ // Copyright 2024, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 -import debounce from "lodash.debounce"; -import { useCallback, useEffect, useState } from "react"; +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, delay = 0) => { const [width, setWidth] = useState(null); - const updateWidth = useCallback(() => { - if (ref.current) { - const element = ref.current; - const style = window.getComputedStyle(element); - const paddingLeft = parseFloat(style.paddingLeft); - const paddingRight = parseFloat(style.paddingRight); - const marginLeft = parseFloat(style.marginLeft); - const marginRight = parseFloat(style.marginRight); - const parentWidth = element.clientWidth - paddingLeft - paddingRight - marginLeft - marginRight; - setWidth(parentWidth); - } + const updateWidth = useCallback((entry: ResizeObserverEntry) => { + setWidth(entry.contentRect.width); }, []); - const fUpdateWidth = useCallback(delay > 0 ? debounce(updateWidth, delay) : updateWidth, [updateWidth, delay]); + const fUpdateWidth = useCallback(delay > 0 ? debounce(delay, updateWidth) : updateWidth, [updateWidth, delay]); - useEffect(() => { - const resizeObserver = new ResizeObserver(() => { - fUpdateWidth(); - }); - - if (ref.current) { - resizeObserver.observe(ref.current); - fUpdateWidth(); - } - - return () => { - if (ref.current) { - resizeObserver.unobserve(ref.current); - } - if (delay > 0) { - fUpdateWidth.cancel(); - } - }; - }, [fUpdateWidth]); + useResizeObserver(ref, fUpdateWidth); return width; }; diff --git a/frontend/app/view/codeeditor/codeeditor.tsx b/frontend/app/view/codeeditor/codeeditor.tsx index 966b12c25..b6a7e7380 100644 --- a/frontend/app/view/codeeditor/codeeditor.tsx +++ b/frontend/app/view/codeeditor/codeeditor.tsx @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { useHeight } from "@/app/hook/useHeight"; +import { useWidth } from "@/app/hook/useWidth"; import loader from "@monaco-editor/loader"; import { Editor, Monaco } from "@monaco-editor/react"; import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api"; import React, { useRef } from "react"; - import "./codeeditor.less"; // there is a global monaco variable (TODO get the correct TS type) @@ -18,21 +18,29 @@ export function loadMonaco() { .init() .then(() => { monaco.editor.defineTheme("wave-theme-dark", { - base: "hc-black", + base: "vs-dark", inherit: true, rules: [], colors: { - "editor.background": "#000000", + "editor.background": "#00000000", + "minimap.background": "#00000077", + focusBorder: "#00000000", }, }); monaco.editor.defineTheme("wave-theme-light", { - base: "hc-light", + base: "vs", inherit: true, rules: [], colors: { "editor.background": "#fefefe", + focusBorder: "#00000000", }, }); + + // Disable default validation errors for typescript and javascript + monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ + noSemanticValidation: true, + }); }) .catch((e) => { console.error("error loading monaco", e); @@ -50,6 +58,9 @@ function defaultEditorOptions(): MonacoTypes.editor.IEditorOptions { verticalScrollbarSize: 5, horizontalScrollbarSize: 5, }, + minimap: { + enabled: true, + }, }; return opts; } @@ -67,6 +78,7 @@ export function CodeEditor({ parentRef, text, language, filename, onChange, onMo const divRef = useRef(null); const unmountRef = useRef<() => void>(null); const parentHeight = useHeight(parentRef); + const parentWidth = useWidth(parentRef); const theme = "wave-theme-dark"; React.useEffect(() => { @@ -97,9 +109,10 @@ export function CodeEditor({ parentRef, text, language, filename, onChange, onMo