/* eslint-disable @typescript-eslint/ban-ts-comment */ import { getUnifiedContentLine, SplitSide } from "@git-diff-view/core"; import { diffFontSizeName, removeAllSelection, diffAsideWidthName, getDiffIdFromElement } from "@git-diff-view/utils"; import { Fragment, memo, useEffect, useMemo, useRef } from "react"; // @ts-ignore import { useSyncExternalStore } from "use-sync-external-store/shim/index.js"; import { useTextWidth } from "../hooks/useTextWidth"; import { DiffUnifiedContentLine } from "./DiffUnifiedContentLine"; import { DiffUnifiedExtendLine } from "./DiffUnifiedExtendLine"; import { DiffUnifiedHunkLine } from "./DiffUnifiedHunkLine"; import { DiffUnifiedWidgetLine } from "./DiffUnifiedWidgetLine"; import { useDiffViewContext } from "./DiffViewContext"; import { DiffWidgetContext } from "./DiffWidgetContext"; import { createDiffWidgetStore } from "./tools"; import type { DiffFile } from "@git-diff-view/core"; import type { MouseEventHandler } from "react"; export const DiffUnifiedView = memo(({ diffFile }: { diffFile: DiffFile }) => { const { useDiffContext } = useDiffViewContext(); const ref = useRef(null); const tempRef = useRef(undefined); const useDiffContextRef = useRef(useDiffContext); useDiffContextRef.current = useDiffContext; // performance optimization const useWidget = useMemo(() => createDiffWidgetStore(useDiffContextRef), []); const contextValue = useMemo(() => ({ useWidget }), [useWidget]); const { fontSize, enableWrap, enableHighlight, enableAddWidget, onCreateUseWidgetHook } = useDiffContext.useShallowStableSelector((s) => ({ fontSize: s.fontSize, enableWrap: s.enableWrap, enableHighlight: s.enableHighlight, enableAddWidget: s.enableAddWidget, onCreateUseWidgetHook: s.onCreateUseWidgetHook, })); useSyncExternalStore(diffFile.subscribe, diffFile.getUpdateCount, diffFile.getUpdateCount); useEffect(() => { const { setWidget } = useWidget.getReadonlyState(); setWidget({}); }, [diffFile, useWidget]); useEffect(() => { onCreateUseWidgetHook?.(useWidget); }, [useWidget, onCreateUseWidgetHook]); const unifiedLineLength = Math.max(diffFile.unifiedLineLength, diffFile.fileLineLength); const _width = useTextWidth({ text: unifiedLineLength.toString(), font: useMemo(() => ({ fontSize: fontSize + "px", fontFamily: "Menlo, Consolas, monospace" }), [fontSize]), }); const width = Math.max(40, _width + 10); const lines = getUnifiedContentLine(diffFile); const setStyle = (side?: SplitSide) => { if (!ref.current) return; if (!side) { ref.current.textContent = ""; } else { const id = `diff-root${diffFile.getId()}`; ref.current.textContent = `#${id} [data-state="extend"] {user-select: none} \n#${id} [data-state="hunk"] {user-select: none} \n#${id} [data-state="widget"] {user-select: none}`; } }; const onMouseDown: MouseEventHandler = (e) => { let ele: Element | null = e.target as Element; // need remove all the selection if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") { removeAllSelection(); return; } const id = getDiffIdFromElement(ele as HTMLElement); if (id && id !== `diff-root${diffFile.getId()}`) { return; } while (ele && ele instanceof HTMLElement) { const state = ele.getAttribute("data-state"); if (state) { if (state === "extend" || state === "hunk" || state === "widget") { if (tempRef.current !== undefined) { tempRef.current = undefined; setStyle(undefined); removeAllSelection(); } return; } else { if (tempRef.current !== SplitSide.new) { tempRef.current = SplitSide.new; setStyle(SplitSide.new); removeAllSelection(); } return; } } ele = ele.parentElement; } }; return (