diff --git a/docs/de/user/repo/assets/repository-code-changesetDetails.png b/docs/de/user/repo/assets/repository-code-changesetDetails.png index e593abb1ed..bf000b9a59 100644 Binary files a/docs/de/user/repo/assets/repository-code-changesetDetails.png and b/docs/de/user/repo/assets/repository-code-changesetDetails.png differ diff --git a/docs/en/user/repo/assets/repository-code-changesetDetails.png b/docs/en/user/repo/assets/repository-code-changesetDetails.png index f7a0aa45a7..c068b189d0 100644 Binary files a/docs/en/user/repo/assets/repository-code-changesetDetails.png and b/docs/en/user/repo/assets/repository-code-changesetDetails.png differ diff --git a/gradle/changelog/add-status-icon-in-file-tree.yaml b/gradle/changelog/add-status-icon-in-file-tree.yaml new file mode 100644 index 0000000000..dd98fc3598 --- /dev/null +++ b/gradle/changelog/add-status-icon-in-file-tree.yaml @@ -0,0 +1,2 @@ +- type: added + description: Colored status icons in file tree diff --git a/scm-ui/ui-components/src/repos/LayoutRadioButtons.tsx b/scm-ui/ui-components/src/repos/LayoutRadioButtons.tsx index de4e26e26b..295d9a969c 100644 --- a/scm-ui/ui-components/src/repos/LayoutRadioButtons.tsx +++ b/scm-ui/ui-components/src/repos/LayoutRadioButtons.tsx @@ -28,10 +28,10 @@ const LayoutRadioButtons = ({ setLayout, layout }: LayoutProps) => { const [t] = useTranslation("repos"); return ( - + setLayout(value as unknown as LayoutMode)} > diff --git a/scm-ui/ui-components/src/repos/LoadingDiff.tsx b/scm-ui/ui-components/src/repos/LoadingDiff.tsx index 9aa1e9c367..4b4ce20806 100644 --- a/scm-ui/ui-components/src/repos/LoadingDiff.tsx +++ b/scm-ui/ui-components/src/repos/LoadingDiff.tsx @@ -16,6 +16,7 @@ import React, { FC, useState } from "react"; import { useTranslation } from "react-i18next"; +import classNames from "classnames"; import { NotFoundError, useDiff } from "@scm-manager/ui-api"; import ErrorNotification from "../ErrorNotification"; import Loading from "../Loading"; @@ -26,8 +27,8 @@ import { DiffObjectProps } from "./DiffTypes"; import DiffStatistics from "./DiffStatistics"; import { DiffDropDown } from "../index"; import DiffFileTree from "./diff/DiffFileTree"; -import { DiffContent, Divider, FileTreeContent, StickyFileDiffContainer } from "./diff/styledElements"; -import { useHistory, useLocation } from "react-router-dom"; +import { DiffContent, DiffTreeTitle, FileTreeContent, StickyFileDiffContainer } from "./diff/styledElements"; +import { useLocation } from "react-router-dom"; import { getFileNameFromHash } from "./diffs"; import LayoutRadioButtons from "./LayoutRadioButtons"; import { useAriaId } from "@scm-manager/ui-core"; @@ -66,7 +67,6 @@ const LoadingDiff: FC = ({ url, limit, refetchOnWindowFocus, ...props }) const [prevHash, setPrevHash] = useState(""); const diffContentId = useAriaId(); const location = useLocation(); - const history = useHistory(); const { error, isLoading, data, fetchNextPage, isFetchingNextPage } = useDiff(url, { limit, @@ -79,10 +79,9 @@ const LoadingDiff: FC = ({ url, limit, refetchOnWindowFocus, ...props }) setCollapsed(!collapsed); }; - const setFilePath = (path: string) => { + const setFilePath = () => { setPrevHash(""); setLayout("Both"); - history.push(`#diff-${encodeURIComponent(path)}`); }; if (error) { @@ -97,7 +96,7 @@ const LoadingDiff: FC = ({ url, limit, refetchOnWindowFocus, ...props }) } else { return ( <> - +
= ({ url, limit, refetchOnWindowFocus, ...props }) />
-
+
- -

{t("changesets.diffTree.title")}

- + + {t("changesets.diffTree.title")} {data?.tree && ( )} diff --git a/scm-ui/ui-components/src/repos/diff/DiffFileTree.tsx b/scm-ui/ui-components/src/repos/diff/DiffFileTree.tsx index 729c8195a2..dc291a62c4 100644 --- a/scm-ui/ui-components/src/repos/diff/DiffFileTree.tsx +++ b/scm-ui/ui-components/src/repos/diff/DiffFileTree.tsx @@ -14,39 +14,35 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -import { FileTree } from "@scm-manager/ui-types"; import React, { FC } from "react"; -import { FileDiffContainer, FileDiffContent } from "./styledElements"; -import { Icon } from "@scm-manager/ui-core"; +import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import styled from "styled-components"; +import classNames from "classnames"; +import { FileTree } from "@scm-manager/ui-types"; +import { FileDiffContent, StackedSpan, StyledIcon } from "./styledElements"; type Props = { tree: FileTree; currentFile: string; setCurrentFile: (path: string) => void; gap?: number }; -const StyledIcon = styled(Icon)` - min-width: 1.5rem; -`; - const DiffFileTree: FC = ({ tree, currentFile, setCurrentFile, gap = 15 }) => { return ( - - - {Object.keys(tree.children).map((key) => ( - - ))} - - + + {Object.keys(tree.children).map((key) => ( + + ))} + ); }; export default DiffFileTree; +type ChangeType = "add" | "modify" | "delete" | "rename" | "copy"; + type NodeProps = { node: FileTree; parentPath: string; currentFile: string; setCurrentFile: (path: string) => void }; const addPath = (parentPath: string, path: string) => { @@ -62,10 +58,10 @@ const TreeNode: FC = ({ node, parentPath, currentFile, setCurrentFile return (
  • {Object.keys(node.children).length > 0 ? ( -
      -
    • +
        +
      • folder -
        {node.nodeName}
        +
        {node.nodeName}
      • {Object.keys(node.children).map((key) => ( = ({ node, parentPath, currentFile, setCurrentFile
      ) : ( = ({ node, parentPath, currentFile, setCurrentFile ); }; -type FileProps = { path: string; parentPath: string; currentFile: string; setCurrentFile: (path: string) => void }; +const getColor = (changeType: ChangeType) => { + switch (changeType) { + case "add": + return "success"; + case "modify": + case "rename": + case "copy": + return "info"; + case "delete": + return "danger"; + } +}; -export const TreeFileContent = styled.div` - cursor: pointer; -`; +const getIcon = (changeType: ChangeType) => { + switch (changeType) { + case "add": + case "copy": + return "plus"; + case "modify": + case "rename": + return "circle"; + case "delete": + return "minus"; + } +}; -const TreeFile: FC = ({ path, parentPath, currentFile, setCurrentFile }) => { +type FileProps = { + changeType: ChangeType; + path: string; + parentPath: string; + currentFile: string; + setCurrentFile: (path: string) => void; +}; + +const TreeFile: FC = ({ changeType, path, parentPath, currentFile, setCurrentFile }) => { const [t] = useTranslation("repos"); const completePath = addPath(parentPath, path); @@ -104,17 +129,34 @@ const TreeFile: FC = ({ path, parentPath, currentFile, setCurrentFile }; return ( - setCurrentFile(completePath)}> - {isCurrentFile() ? ( - + setCurrentFile(completePath)} + to={`#diff-${encodeURIComponent(completePath)}`} + > + + file - ) : ( - - file + + {getIcon(changeType)} - )} -
      {path}
      -
      + +
      {path}
      + ); }; diff --git a/scm-ui/ui-components/src/repos/diff/styledElements.tsx b/scm-ui/ui-components/src/repos/diff/styledElements.tsx index 27ed46e559..be04ae7c80 100644 --- a/scm-ui/ui-components/src/repos/diff/styledElements.tsx +++ b/scm-ui/ui-components/src/repos/diff/styledElements.tsx @@ -17,6 +17,8 @@ import styled from "styled-components"; // @ts-ignore react-diff-view does not provide types import { Hunk } from "react-diff-view"; +import { Icon } from "@scm-manager/ui-core"; +import { devices } from "../../devices"; export type Collapsible = { collapsed?: boolean; @@ -83,27 +85,37 @@ export const PanelHeading = styled.div<{ sticky?: boolean | number }>` }} `; +export const StickyFileDiffContainer = styled.div` + top: 5rem; + position: sticky; + height: 100%; + + @media screen and (max-width: ${devices.mobile.width}px) { + top: 0; + position: relative; + flex: 0 0 100%; + margin-bottom: 0.5rem; + } +`; + export const FileTreeContent = styled.div<{ isBorder: boolean }>` ${({ isBorder }) => isBorder && ` border: 1px solid var(--scm-border-color); - border-radius: 1rem; + border-radius: 0.25rem; + overflow: hidden; `} `; +export const DiffTreeTitle = styled.h3` + border-bottom: 1px solid var(--scm-border-color); + box-shadow: 0 24px 3px -24px var(--scm-border-color); +`; + export const DiffContent = styled.div` width: 100%; -`; - -export const StickyFileDiffContainer = styled.div` - top: 3rem; - position: sticky; - height: 100%; -`; - -export const FileDiffContainer = styled.div` - top: 5rem; + padding-top: 0; `; export const FileDiffContent = styled.ul<{ gap?: number }>` @@ -120,8 +132,18 @@ export const FileDiffContent = styled.ul<{ gap?: number }>` }}; `; -export const Divider = styled.div` - margin-bottom: 16px; - border-bottom: 1px solid var(--scm-border-color); - box-shadow: 0 24px 3px -24px var(--scm-border-color); +export const StackedSpan = styled.span` + width: 3em; + height: 3em; + font-size: 0.5em; +`; + +export const StyledIcon = styled(Icon)<{ isSmaller?: boolean }>` + ${({ isSmaller }) => + isSmaller && + ` + font-size: 0.5em; + margin-top: 0.05rem; + `} + min-width: 1.5rem; `;