import type { HTMLAttributes } from 'react'; import React, { useState, useMemo, useRef } from 'react'; import { uniq } from 'lodash'; import classNames from 'classnames'; import { HoverSplitter } from '@teambit/base-ui.surfaces.split-pane.hover-splitter'; import { SplitPane, Pane, Layout } from '@teambit/base-ui.surfaces.split-pane.split-pane'; import type { FileIconSlot } from '@teambit/code'; import { useComponentCompare } from '@teambit/component.ui.component-compare.context'; import { useCompareQueryParam, useUpdatedUrlFromQuery, } from '@teambit/component.ui.component-compare.hooks.use-component-compare-url'; import { useCode } from '@teambit/code.ui.queries.get-component-code'; import { ThemeSwitcher } from '@teambit/design.themes.theme-toggler'; import { DarkTheme } from '@teambit/design.themes.dark-theme'; import { useLocation } from '@teambit/base-react.navigation.link'; import { useQuery } from '@teambit/ui-foundation.ui.react-router.use-query'; import { CodeCompareTree } from './code-compare-tree'; import type { CodeCompareViewProps } from './code-compare-view'; import { CodeCompareView } from './code-compare-view'; import { Widget } from './code-compare.widgets'; import styles from './code-compare.module.scss'; const DEFAULT_FILE = 'index.ts'; export type CodeCompareProps = { fileIconSlot?: FileIconSlot; CodeView?: React.ComponentType; } & HTMLAttributes; export function CodeCompare({ fileIconSlot, className, CodeView = CodeCompareView }: CodeCompareProps) { const componentCompareContext = useComponentCompare(); const query = useQuery(); const location = useLocation() || { pathname: '/' }; // todo - look into this loading flag where it needs to be used // eslint-disable-next-line @typescript-eslint/no-unused-vars const { base, compare, state: compareState, hooks: compareHooks, hidden, loading } = componentCompareContext || {}; const state = compareState?.code; const hook = compareHooks?.code; const [isSidebarOpen, setSidebarOpenness] = useState(false); const baseHost = 'teambit.scope/scope'; const compareHost = compare?.hasLocalChanges ? 'teambit.workspace/workspace' : 'teambit.scope/scope'; const { fileTree: baseFileTree = [], mainFile } = useCode(hidden ? undefined : base?.model.id, baseHost); const { fileTree: compareFileTree = [] } = useCode(hidden ? undefined : compare?.model.id, compareHost); const fileCompareDataByName = componentCompareContext?.fileCompareDataByName; const anyFileHasDiffStatus = useRef(false); const fileTree = useMemo(() => { const allFiles = uniq(baseFileTree.concat(compareFileTree)); anyFileHasDiffStatus.current = false; // sort by diff status return !fileCompareDataByName ? allFiles : allFiles.sort((a, b) => { const aCompareResult = fileCompareDataByName.get(a); const bCompareResult = fileCompareDataByName.get(b); const noStatus = (status?: string) => !status || status === 'UNCHANGED'; const aStatus = aCompareResult?.status; const bStatus = bCompareResult?.status; if (!noStatus(aStatus) && !noStatus(bStatus)) return 0; if (!noStatus(aStatus)) return -1; if (!noStatus(bStatus)) return 1; if (!anyFileHasDiffStatus.current) anyFileHasDiffStatus.current = true; if (aStatus?.toLowerCase() === 'new') return -1; if (bStatus?.toLowerCase() === 'new') return 1; return 0; }); }, [fileCompareDataByName?.size, baseFileTree.length, compareFileTree.length, hidden]); const selectedFileFromParams = useCompareQueryParam('file'); const selectedFile = React.useMemo( () => state?.id || selectedFileFromParams || (anyFileHasDiffStatus.current ? fileTree[0] : mainFile || DEFAULT_FILE), [state?.id, selectedFileFromParams, fileTree, mainFile] ); const controlledHref = useUpdatedUrlFromQuery({}); const useHref = (node: any) => { const hrefFromHook = hook?.useUpdatedUrlFromQuery?.( { file: node.id }, () => query, () => location ) ?? null; const defaultHref = useUpdatedUrlFromQuery( { file: node.id }, () => query, () => location ); return hrefFromHook || state?.controlled ? controlledHref : defaultHref; }; const sidebarIconUrl = isSidebarOpen ? 'https://static.bit.dev/design-system-assets/Icons/sidebar-close.svg' : 'https://static.bit.dev/design-system-assets/Icons/sidebar-open.svg'; const sidebarOpenness = isSidebarOpen ? Layout.row : Layout.right; return ( setSidebarOpenness((value) => !value)} onKeyDown={(event) => { if (event.key === 'Enter' || event.key === 'Space') { setSidebarOpenness((value) => !value); } }} role="button" tabIndex={0} > ); }