import type { ReactIsland } from "@ethicdevs/react-monolith";
import React, { useCallback } from "react";
import styled, { css } from "styled-components";
import type {
RepositoryFile,
RepositoryLog,
WithThemeSchemeProp,
} from "../types";
import { AppRoute } from "../routes.defs";
import { Grid, TextEllipsis } from "../components";
import { NamedColors } from "../utils/style";
import { buildRouteLink } from "../utils/shared";
export interface RepositoryTreeViewProps {
currentPath: string;
currentRef: string;
lastCommit: RepositoryLog;
orgSlug: string;
repoFiles: RepositoryFile[];
repoSlug: string;
}
const RepositoryTreeView: ReactIsland<
RepositoryTreeViewProps & WithThemeSchemeProp
> = ({
themeScheme,
currentPath,
currentRef,
orgSlug,
repoFiles,
repoSlug,
}) => {
const buildRepoFileLink = useCallback(
(file: RepositoryFile) => {
const fileName = `${file.name}${file.type === "tree" ? "/" : ""}`;
return {
text: fileName,
href:
currentPath === "/"
? buildRouteLink(AppRoute.REPOSITORY_BROWSER_WITH_PATH, {
orgSlug,
repoSlug,
currentRef: encodeURIComponent(currentRef),
"*": fileName,
})
: buildRouteLink(AppRoute.REPOSITORY_BROWSER_WITH_PATH, {
orgSlug,
repoSlug,
currentRef: encodeURIComponent(currentRef),
"*": `${
currentPath.endsWith("/") || currentPath === ""
? currentPath
: `${currentPath}/`
}${fileName}`,
}),
};
},
[orgSlug, repoSlug, currentPath]
);
const currPathParts = currentPath.split("/");
const shouldShowPrevPath = currentPath !== "/";
let prevPath: string | null = currPathParts
.slice(0, currPathParts.length - 2)
.join("/");
prevPath = prevPath.trim() === "" ? null : prevPath;
prevPath = prevPath == null ? "/" : prevPath;
const prevPathLink =
prevPath === "/"
? buildRouteLink(AppRoute.REPOSITORY_DETAILS, { orgSlug, repoSlug })
: buildRouteLink(AppRoute.REPOSITORY_BROWSER_WITH_PATH, {
orgSlug,
repoSlug,
currentRef: encodeURIComponent(currentRef),
"*": prevPath.endsWith("/") ? prevPath : `${prevPath}/`,
});
return (
<StyledRepositoryTreeViewContainer>
<Grid.Col fluid>
<Grid.Row
gap={8}
alignItems={"center"}
justifyContent={"flex-start"}
style={{
marginTop: 8,
width: "100%",
padding: "0 8px 8px 8px",
borderBottom: `1px solid ${NamedColors.BORDER_DEFAULT[themeScheme]}`,
}}
>
<Grid.Row fluid nowrap alignItems={"center"}>
{currPathParts.map(
(pathPart, idx) =>
pathPart.trim() !== "" &&
pathPart !== "/" && (
<a
key={[idx, pathPart].join(":")}
style={{ marginLeft: idx >= 1 ? 8 : 0 }}
title={`Go to "${currPathParts
.slice(0, idx + 1)
.join("/")}/" folder`}
href={buildRouteLink(
AppRoute.REPOSITORY_BROWSER_WITH_PATH,
{
orgSlug,
repoSlug,
currentRef: encodeURIComponent(currentRef),
"*": `${currPathParts.slice(0, idx + 1).join("/")}/`,
}
)}
>
<TextEllipsis>{pathPart}/</TextEllipsis>
</a>
)
)}
</Grid.Row>
<Grid.Row nowrap alignItems={"center"}>
<a
style={{ minWidth: "max-content" }}
href={buildRouteLink(AppRoute.REPOSITORY_COMMITS_LOG, {
orgSlug,
repoSlug,
currentRef: encodeURIComponent(currentRef),
})}
>
Commits History
</a>
</Grid.Row>
</Grid.Row>
<Grid.Col fluid nowrap>
<ul
style={{
listStyle: "none",
margin: "0 0 8px 0",
padding: 0,
width: "100%",
}}
>
{shouldShowPrevPath && (
<StyledTreeViewListItem
key={"go-previous"}
themeScheme={themeScheme}
>
<StyledTreeViewListItemAnchor
href={prevPathLink}
title={"Go to previous folder (..)"}
fullWidth
>
..
</StyledTreeViewListItemAnchor>
</StyledTreeViewListItem>
)}
{repoFiles.map((file) => {
const fileLink = buildRepoFileLink(file);
return (
<StyledTreeViewListItem
key={[file.id, file.name].join(":")}
themeScheme={themeScheme}
>
<StyledTreeViewListItemAnchor
style={{ flex: "0 0 30%" }}
href={fileLink.href}
title={fileLink.text}
>
<span>
<TextEllipsis>{fileLink.text}</TextEllipsis>
</span>
</StyledTreeViewListItemAnchor>
{file.lastCommit != null && (
<StyledTreeViewListItemAnchor
style={{ flex: 1, marginLeft: 16 }}
href={buildRouteLink(AppRoute.REPOSITORY_SHOW_OBJECT, {
orgSlug,
repoSlug,
objectId: file.lastCommit.commit,
})}
title={file.lastCommit.subject}
>
<span
style={{ flex: 1, opacity: 0.77, fontWeight: "normal" }}
>
<TextEllipsis>{file.lastCommit.subject}</TextEllipsis>
</span>
<span style={{ marginLeft: 16 }}>
{file.lastCommit.abbreviated_commit}
</span>
</StyledTreeViewListItemAnchor>
)}
</StyledTreeViewListItem>
);
})}
</ul>
</Grid.Col>
</Grid.Col>
</StyledRepositoryTreeViewContainer>
);
};
const StyledTreeViewListItem = styled.li<WithThemeSchemeProp>`
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
align-items: center;
height: 32px;
width: 100%;
padding: 0 8px;
${({ themeScheme }) => css`
border-bottom: 1px solid ${NamedColors.BORDER_DEFAULT[themeScheme]};
&:hover {
background-color: ${NamedColors.CARD_OVERLAY[themeScheme]};
}
`}
`;
const StyledTreeViewListItemAnchor = styled.a<{ fullWidth?: boolean }>`
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
align-items: center;
${({ fullWidth = false }) =>
fullWidth === true &&
css`
width: 100%;
`}
`;
const StyledRepositoryTreeViewContainer = styled.div`
width: 100%;
`;
RepositoryTreeView.displayName = "RepositoryTreeView";
export default RepositoryTreeView;