mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: use emotion for styling (pt. 3) (#10026)
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { FC } from "react";
|
||||
import { type FC } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
import { CopyButton } from "../CopyButton/CopyButton";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
|
||||
export interface CodeExampleProps {
|
||||
code: string;
|
||||
@@ -14,46 +12,40 @@ export interface CodeExampleProps {
|
||||
/**
|
||||
* Component to show single-line code examples, with a copy button
|
||||
*/
|
||||
export const CodeExample: FC<CodeExampleProps> = ({
|
||||
code,
|
||||
password,
|
||||
className,
|
||||
}) => {
|
||||
const styles = useStyles({ password });
|
||||
export const CodeExample: FC<CodeExampleProps> = (props) => {
|
||||
const { code, password, className } = props;
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<div className={combineClasses([styles.root, className])}>
|
||||
<code className={styles.code}>{code}</code>
|
||||
<div
|
||||
css={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
background: "rgb(0 0 0 / 30%)",
|
||||
color: theme.palette.primary.contrastText,
|
||||
fontFamily: MONOSPACE_FONT_FAMILY,
|
||||
fontSize: 14,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: theme.spacing(1),
|
||||
lineHeight: "150%",
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
className={className}
|
||||
>
|
||||
<code
|
||||
css={{
|
||||
padding: theme.spacing(0, 1),
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
wordBreak: "break-all",
|
||||
"-webkit-text-security": password ? "disc" : undefined,
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
</code>
|
||||
<CopyButton text={code} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface styleProps {
|
||||
inline?: boolean;
|
||||
password?: boolean;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles<Theme, styleProps>((theme) => ({
|
||||
root: (props) => ({
|
||||
display: props.inline ? "inline-flex" : "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
background: "rgb(0 0 0 / 30%)",
|
||||
color: theme.palette.primary.contrastText,
|
||||
fontFamily: MONOSPACE_FONT_FAMILY,
|
||||
fontSize: 14,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: theme.spacing(1),
|
||||
lineHeight: "150%",
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
}),
|
||||
code: {
|
||||
padding: theme.spacing(0, 1),
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
wordBreak: "break-all",
|
||||
"-webkit-text-security": (props) => (props.password ? "disc" : undefined),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -5,10 +5,7 @@ import { colors } from "theme/colors";
|
||||
import * as TypesGen from "api/typesGenerated";
|
||||
import { navHeight } from "theme/constants";
|
||||
import { BorderedMenu } from "./BorderedMenu";
|
||||
import {
|
||||
CloseDropdown,
|
||||
OpenDropdown,
|
||||
} from "components/DropdownArrows/DropdownArrows";
|
||||
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
|
||||
import { UserAvatar } from "components/UserAvatar/UserAvatar";
|
||||
import { UserDropdownContent } from "./UserDropdownContent";
|
||||
import { BUTTON_SM_HEIGHT } from "theme/theme";
|
||||
@@ -69,11 +66,7 @@ export const UserDropdown: FC<PropsWithChildren<UserDropdownProps>> = ({
|
||||
avatarURL={user.avatar_url}
|
||||
/>
|
||||
</Badge>
|
||||
{anchorEl ? (
|
||||
<CloseDropdown color={colors.gray[6]} />
|
||||
) : (
|
||||
<OpenDropdown color={colors.gray[6]} />
|
||||
)}
|
||||
<DropdownArrow color={colors.gray[6]} close={Boolean(anchorEl)} />
|
||||
</div>
|
||||
</MenuItem>
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import KeyboardArrowDown from "@mui/icons-material/KeyboardArrowDown";
|
||||
import KeyboardArrowUp from "@mui/icons-material/KeyboardArrowUp";
|
||||
import { type FC } from "react";
|
||||
import { type Theme } from "@emotion/react";
|
||||
|
||||
interface ArrowProps {
|
||||
margin?: boolean;
|
||||
color?: string;
|
||||
close?: boolean;
|
||||
}
|
||||
|
||||
export const DropdownArrow: FC<ArrowProps> = (props) => {
|
||||
const { margin = true, color, close } = props;
|
||||
|
||||
const Arrow = close ? KeyboardArrowUp : KeyboardArrowDown;
|
||||
|
||||
return (
|
||||
<Arrow
|
||||
aria-label={close ? "close-dropdown" : "open-dropdown"}
|
||||
css={(theme: Theme) => ({
|
||||
color: color ?? theme.palette.primary.contrastText,
|
||||
marginLeft: margin ? theme.spacing(1) : 0,
|
||||
width: 16,
|
||||
height: 16,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import KeyboardArrowDown from "@mui/icons-material/KeyboardArrowDown";
|
||||
import KeyboardArrowUp from "@mui/icons-material/KeyboardArrowUp";
|
||||
import { FC } from "react";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
|
||||
const useStyles = makeStyles<Theme, ArrowProps>((theme: Theme) => ({
|
||||
arrowIcon: {
|
||||
color: ({ color }) => color ?? theme.palette.primary.contrastText,
|
||||
marginLeft: ({ margin }) => (margin ? theme.spacing(1) : 0),
|
||||
width: 16,
|
||||
height: 16,
|
||||
},
|
||||
arrowIconUp: {
|
||||
color: ({ color }) => color ?? theme.palette.primary.contrastText,
|
||||
},
|
||||
}));
|
||||
|
||||
interface ArrowProps {
|
||||
margin?: boolean;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export const OpenDropdown: FC<ArrowProps> = ({ margin = true, color }) => {
|
||||
const styles = useStyles({ margin, color });
|
||||
return (
|
||||
<KeyboardArrowDown
|
||||
aria-label="open-dropdown"
|
||||
className={styles.arrowIcon}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const CloseDropdown: FC<ArrowProps> = ({ margin = true, color }) => {
|
||||
const styles = useStyles({ margin, color });
|
||||
return (
|
||||
<KeyboardArrowUp
|
||||
aria-label="close-dropdown"
|
||||
className={`${styles.arrowIcon} ${styles.arrowIconUp}`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +1,8 @@
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import Link from "@mui/material/Link";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import {
|
||||
CloseDropdown,
|
||||
OpenDropdown,
|
||||
} from "components/DropdownArrows/DropdownArrows";
|
||||
import { PropsWithChildren, FC } from "react";
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
|
||||
import { type FC, type PropsWithChildren } from "react";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
|
||||
export interface ExpanderProps {
|
||||
@@ -28,7 +25,7 @@ export const Expander: FC<PropsWithChildren<ExpanderProps>> = ({
|
||||
<Link onClick={toggleExpanded} className={styles.expandLink}>
|
||||
<span className={styles.text}>
|
||||
Click here to learn more
|
||||
<OpenDropdown margin={false} />
|
||||
<DropdownArrow margin={false} />
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
@@ -42,7 +39,7 @@ export const Expander: FC<PropsWithChildren<ExpanderProps>> = ({
|
||||
>
|
||||
<span className={styles.text}>
|
||||
Click here to hide
|
||||
<CloseDropdown margin={false} />
|
||||
<DropdownArrow margin={false} close />
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Link from "@mui/material/Link";
|
||||
import Popover, { PopoverProps } from "@mui/material/Popover";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import HelpIcon from "@mui/icons-material/HelpOutline";
|
||||
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
|
||||
import {
|
||||
@@ -11,9 +10,10 @@ import {
|
||||
FC,
|
||||
PropsWithChildren,
|
||||
} from "react";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import Box, { BoxProps } from "@mui/material/Box";
|
||||
import { type CSSObject, css as className } from "@emotion/css";
|
||||
import { css, type Interpolation, type Theme, useTheme } from "@emotion/react";
|
||||
|
||||
type Icon = typeof HelpIcon;
|
||||
|
||||
@@ -46,12 +46,24 @@ const useHelpTooltip = () => {
|
||||
export const HelpPopover: FC<
|
||||
PopoverProps & { onOpen: () => void; onClose: () => void }
|
||||
> = ({ onOpen, onClose, children, ...props }) => {
|
||||
const styles = useStyles({ size: "small" });
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Popover
|
||||
className={styles.popover}
|
||||
classes={{ paper: styles.popoverPaper }}
|
||||
className={className`
|
||||
pointer-events: none;
|
||||
`}
|
||||
classes={{
|
||||
paper: className`
|
||||
${theme.typography.body2 as CSSObject}
|
||||
|
||||
margin-top: ${theme.spacing(0.5)};
|
||||
width: ${theme.spacing(38)};
|
||||
padding: ${theme.spacing(2.5)};
|
||||
color: ${theme.palette.text.secondary};
|
||||
pointer-events: auto;
|
||||
`,
|
||||
}}
|
||||
onClose={onClose}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
@@ -80,7 +92,7 @@ export const HelpTooltip: FC<PropsWithChildren<HelpTooltipProps>> = ({
|
||||
iconClassName,
|
||||
buttonClassName,
|
||||
}) => {
|
||||
const styles = useStyles({ size });
|
||||
const theme = useTheme();
|
||||
const anchorRef = useRef<HTMLButtonElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(Boolean(open));
|
||||
const id = isOpen ? "help-popover" : undefined;
|
||||
@@ -94,7 +106,24 @@ export const HelpTooltip: FC<PropsWithChildren<HelpTooltipProps>> = ({
|
||||
<button
|
||||
ref={anchorRef}
|
||||
aria-describedby={id}
|
||||
className={combineClasses([styles.button, buttonClassName])}
|
||||
css={css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: ${theme.spacing(getButtonSpacingFromSize(size))};
|
||||
height: ${theme.spacing(getButtonSpacingFromSize(size))};
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: ${theme.palette.text.primary};
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.75;
|
||||
}
|
||||
`}
|
||||
className={buttonClassName}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setIsOpen(true);
|
||||
@@ -107,7 +136,13 @@ export const HelpTooltip: FC<PropsWithChildren<HelpTooltipProps>> = ({
|
||||
}}
|
||||
aria-label="More info"
|
||||
>
|
||||
<Icon className={combineClasses([styles.icon, iconClassName])} />
|
||||
<Icon
|
||||
css={{
|
||||
width: theme.spacing(getIconSpacingFromSize(size)),
|
||||
height: theme.spacing(getIconSpacingFromSize(size)),
|
||||
}}
|
||||
className={iconClassName}
|
||||
/>
|
||||
</button>
|
||||
<HelpPopover
|
||||
id={id}
|
||||
@@ -127,32 +162,20 @@ export const HelpTooltip: FC<PropsWithChildren<HelpTooltipProps>> = ({
|
||||
export const HelpTooltipTitle: FC<PropsWithChildren<unknown>> = ({
|
||||
children,
|
||||
}) => {
|
||||
const styles = useStyles({});
|
||||
|
||||
return <h4 className={styles.title}>{children}</h4>;
|
||||
return <h4 css={styles.title}>{children}</h4>;
|
||||
};
|
||||
|
||||
export const HelpTooltipText = (props: BoxProps) => {
|
||||
const styles = useStyles({});
|
||||
|
||||
return (
|
||||
<Box
|
||||
component="p"
|
||||
{...props}
|
||||
className={combineClasses([styles.text, props.className])}
|
||||
/>
|
||||
);
|
||||
return <Box component="p" css={styles.text} {...props} />;
|
||||
};
|
||||
|
||||
export const HelpTooltipLink: FC<PropsWithChildren<{ href: string }>> = ({
|
||||
children,
|
||||
href,
|
||||
}) => {
|
||||
const styles = useStyles({});
|
||||
|
||||
return (
|
||||
<Link href={href} target="_blank" rel="noreferrer" className={styles.link}>
|
||||
<OpenInNewIcon className={styles.linkIcon} />
|
||||
<Link href={href} target="_blank" rel="noreferrer" css={styles.link}>
|
||||
<OpenInNewIcon css={styles.linkIcon} />
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
@@ -165,20 +188,19 @@ export const HelpTooltipAction: FC<
|
||||
ariaLabel?: string;
|
||||
}>
|
||||
> = ({ children, icon: Icon, onClick, ariaLabel }) => {
|
||||
const styles = useStyles({});
|
||||
const tooltip = useHelpTooltip();
|
||||
|
||||
return (
|
||||
<button
|
||||
aria-label={ariaLabel ?? ""}
|
||||
className={styles.action}
|
||||
css={styles.action}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onClick();
|
||||
tooltip.onClose();
|
||||
}}
|
||||
>
|
||||
<Icon className={styles.actionIcon} />
|
||||
<Icon css={styles.actionIcon} />
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
@@ -187,10 +209,8 @@ export const HelpTooltipAction: FC<
|
||||
export const HelpTooltipLinksGroup: FC<PropsWithChildren<unknown>> = ({
|
||||
children,
|
||||
}) => {
|
||||
const styles = useStyles({});
|
||||
|
||||
return (
|
||||
<Stack spacing={1} className={styles.linksGroup}>
|
||||
<Stack spacing={1} css={styles.linksGroup}>
|
||||
{children}
|
||||
</Stack>
|
||||
);
|
||||
@@ -216,80 +236,40 @@ const getIconSpacingFromSize = (size?: Size): number => {
|
||||
}
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
button: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: ({ size }: { size?: Size }) =>
|
||||
theme.spacing(getButtonSpacingFromSize(size)),
|
||||
height: ({ size }: { size?: Size }) =>
|
||||
theme.spacing(getButtonSpacingFromSize(size)),
|
||||
padding: 0,
|
||||
border: 0,
|
||||
background: "transparent",
|
||||
color: theme.palette.text.primary,
|
||||
opacity: 0.5,
|
||||
cursor: "pointer",
|
||||
|
||||
"&:hover": {
|
||||
opacity: 0.75,
|
||||
},
|
||||
},
|
||||
|
||||
icon: {
|
||||
width: ({ size }: { size?: Size }) =>
|
||||
theme.spacing(getIconSpacingFromSize(size)),
|
||||
height: ({ size }: { size?: Size }) =>
|
||||
theme.spacing(getIconSpacingFromSize(size)),
|
||||
},
|
||||
|
||||
popover: {
|
||||
pointerEvents: "none",
|
||||
},
|
||||
|
||||
popoverPaper: {
|
||||
marginTop: theme.spacing(0.5),
|
||||
width: theme.spacing(38),
|
||||
padding: theme.spacing(2.5),
|
||||
color: theme.palette.text.secondary,
|
||||
pointerEvents: "auto",
|
||||
...theme.typography.body2,
|
||||
},
|
||||
|
||||
title: {
|
||||
const styles = {
|
||||
title: (theme) => ({
|
||||
marginTop: 0,
|
||||
marginBottom: theme.spacing(1),
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: 14,
|
||||
lineHeight: "120%",
|
||||
fontWeight: 600,
|
||||
},
|
||||
}),
|
||||
|
||||
text: {
|
||||
text: (theme) => ({
|
||||
marginTop: theme.spacing(0.5),
|
||||
marginBottom: theme.spacing(0.5),
|
||||
...theme.typography.body2,
|
||||
},
|
||||
...(theme.typography.body2 as CSSObject),
|
||||
}),
|
||||
|
||||
link: {
|
||||
link: (theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
...theme.typography.body2,
|
||||
},
|
||||
...(theme.typography.body2 as CSSObject),
|
||||
}),
|
||||
|
||||
linkIcon: {
|
||||
linkIcon: (theme) => ({
|
||||
color: "inherit",
|
||||
width: 14,
|
||||
height: 14,
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
}),
|
||||
|
||||
linksGroup: {
|
||||
linksGroup: (theme) => ({
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
}),
|
||||
|
||||
action: {
|
||||
action: (theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
background: "none",
|
||||
@@ -298,12 +278,12 @@ const useStyles = makeStyles((theme) => ({
|
||||
padding: 0,
|
||||
cursor: "pointer",
|
||||
fontSize: 14,
|
||||
},
|
||||
}),
|
||||
|
||||
actionIcon: {
|
||||
actionIcon: (theme) => ({
|
||||
color: "inherit",
|
||||
width: 14,
|
||||
height: 14,
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
||||
@@ -2,7 +2,7 @@ import Button from "@mui/material/Button";
|
||||
import InputAdornment from "@mui/material/InputAdornment";
|
||||
import Popover from "@mui/material/Popover";
|
||||
import TextField, { TextFieldProps } from "@mui/material/TextField";
|
||||
import { OpenDropdown } from "components/DropdownArrows/DropdownArrows";
|
||||
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
|
||||
import { useRef, FC, useState } from "react";
|
||||
import Picker from "@emoji-mart/react";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
@@ -52,7 +52,7 @@ const IconField: FC<IconFieldProps> = ({ onPickEmoji, ...textFieldProps }) => {
|
||||
<Button
|
||||
fullWidth
|
||||
ref={emojiButtonRef}
|
||||
endIcon={<OpenDropdown />}
|
||||
endIcon={<DropdownArrow />}
|
||||
onClick={() => {
|
||||
setIsEmojiPickerOpen((v) => !v);
|
||||
}}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
HelpTooltipTitle,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import InfoIcon from "@mui/icons-material/InfoOutlined";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { css } from "@emotion/css";
|
||||
import { colors } from "theme/colors";
|
||||
|
||||
interface InfoTooltipProps {
|
||||
@@ -17,31 +17,22 @@ interface InfoTooltipProps {
|
||||
export const InfoTooltip: FC<InfoTooltipProps> = (props) => {
|
||||
const { title, message, type = "info" } = props;
|
||||
|
||||
const styles = useStyles({ type });
|
||||
|
||||
return (
|
||||
<HelpTooltip
|
||||
size="small"
|
||||
icon={InfoIcon}
|
||||
iconClassName={styles.icon}
|
||||
buttonClassName={styles.button}
|
||||
iconClassName={css`
|
||||
color: ${type === "info" ? colors.blue[5] : colors.yellow[5]};
|
||||
`}
|
||||
buttonClassName={css`
|
||||
opacity: 1;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<HelpTooltipTitle>{title}</HelpTooltipTitle>
|
||||
<HelpTooltipText>{message}</HelpTooltipText>
|
||||
</HelpTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles<unknown, Pick<InfoTooltipProps, "type">>(() => ({
|
||||
icon: ({ type }) => ({
|
||||
color: type === "info" ? colors.blue[5] : colors.yellow[5],
|
||||
}),
|
||||
|
||||
button: {
|
||||
opacity: 1,
|
||||
|
||||
"&:hover": {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import Link from "@mui/material/Link";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import Table from "@mui/material/Table";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
@@ -12,17 +11,24 @@ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import gfm from "remark-gfm";
|
||||
import { colors } from "theme/colors";
|
||||
import { darcula } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
import { type Interpolation, type Theme } from "@emotion/react";
|
||||
|
||||
export const Markdown: FC<{ children: string; className?: string }> = ({
|
||||
children,
|
||||
className,
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
interface MarkdownProps {
|
||||
/**
|
||||
* The Markdown text to parse and render
|
||||
*/
|
||||
children: string;
|
||||
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const Markdown: FC<MarkdownProps> = (props) => {
|
||||
const { children, className } = props;
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
className={combineClasses([styles.markdown, className])}
|
||||
css={markdownStyles}
|
||||
className={className}
|
||||
remarkPlugins={[gfm]}
|
||||
components={{
|
||||
a: ({ href, target, children }) => (
|
||||
@@ -58,7 +64,16 @@ export const Markdown: FC<{ children: string; className?: string }> = ({
|
||||
{String(children)}
|
||||
</SyntaxHighlighter>
|
||||
) : (
|
||||
<code className={styles.codeWithoutLanguage} {...props}>
|
||||
<code
|
||||
css={(theme) => ({
|
||||
padding: theme.spacing(0.125, 0.5),
|
||||
background: theme.palette.divider,
|
||||
borderRadius: 4,
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: 14,
|
||||
})}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
@@ -100,67 +115,57 @@ export const Markdown: FC<{ children: string; className?: string }> = ({
|
||||
|
||||
export const MemoizedMarkdown = memo(Markdown);
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
markdown: {
|
||||
fontSize: 16,
|
||||
lineHeight: "24px",
|
||||
const markdownStyles: Interpolation<Theme> = (theme: Theme) => ({
|
||||
fontSize: 16,
|
||||
lineHeight: "24px",
|
||||
|
||||
"& h1, & h2, & h3, & h4, & h5, & h6": {
|
||||
marginTop: theme.spacing(4),
|
||||
marginBottom: theme.spacing(2),
|
||||
lineHeight: "1.25",
|
||||
},
|
||||
|
||||
"& p": {
|
||||
marginTop: 0,
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
|
||||
"& p:only-child": {
|
||||
marginTop: 0,
|
||||
marginBottom: 0,
|
||||
},
|
||||
|
||||
"& ul, & ol": {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: theme.spacing(1),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
|
||||
"& li > ul, & li > ol": {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
|
||||
"& li > p": {
|
||||
marginBottom: 0,
|
||||
},
|
||||
|
||||
"& .prismjs": {
|
||||
background: theme.palette.background.paperLight,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: theme.spacing(2, 3),
|
||||
overflowX: "auto",
|
||||
|
||||
"& code": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
|
||||
"& .key, & .property, & .inserted, .keyword": {
|
||||
color: colors.turquoise[7],
|
||||
},
|
||||
|
||||
"& .deleted": {
|
||||
color: theme.palette.error.light,
|
||||
},
|
||||
},
|
||||
"& h1, & h2, & h3, & h4, & h5, & h6": {
|
||||
marginTop: theme.spacing(4),
|
||||
marginBottom: theme.spacing(2),
|
||||
lineHeight: "1.25",
|
||||
},
|
||||
|
||||
codeWithoutLanguage: {
|
||||
padding: theme.spacing(0.125, 0.5),
|
||||
background: theme.palette.divider,
|
||||
borderRadius: 4,
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: 14,
|
||||
"& p": {
|
||||
marginTop: 0,
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
"& p:only-child": {
|
||||
marginTop: 0,
|
||||
marginBottom: 0,
|
||||
},
|
||||
|
||||
"& ul, & ol": {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: theme.spacing(1),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
|
||||
"& li > ul, & li > ol": {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
|
||||
"& li > p": {
|
||||
marginBottom: 0,
|
||||
},
|
||||
|
||||
"& .prismjs": {
|
||||
background: theme.palette.background.paperLight,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: theme.spacing(2, 3),
|
||||
overflowX: "auto",
|
||||
|
||||
"& code": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
|
||||
"& .key, & .property, & .inserted, .keyword": {
|
||||
color: colors.turquoise[7],
|
||||
},
|
||||
|
||||
"& .deleted": {
|
||||
color: theme.palette.error.light,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -9,10 +9,7 @@ import {
|
||||
WorkspaceAgentLogSource,
|
||||
WorkspaceAgentMetadata,
|
||||
} from "api/typesGenerated";
|
||||
import {
|
||||
CloseDropdown,
|
||||
OpenDropdown,
|
||||
} from "components/DropdownArrows/DropdownArrows";
|
||||
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
|
||||
import { displayError } from "components/GlobalSnackbar/utils";
|
||||
import { VSCodeDesktopButton } from "components/Resources/VSCodeDesktopButton/VSCodeDesktopButton";
|
||||
import {
|
||||
@@ -443,7 +440,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
setShowLogs((v) => !v);
|
||||
}}
|
||||
>
|
||||
<CloseDropdown />
|
||||
<DropdownArrow close />
|
||||
Hide logs
|
||||
</button>
|
||||
) : (
|
||||
@@ -456,7 +453,7 @@ export const AgentRow: FC<AgentRowProps> = ({
|
||||
setShowLogs((v) => !v);
|
||||
}}
|
||||
>
|
||||
<OpenDropdown />
|
||||
<DropdownArrow />
|
||||
Show logs
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -1,17 +1,71 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { FC, useState } from "react";
|
||||
import { type FC, useState } from "react";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import { type CSSObject, type Interpolation, type Theme } from "@emotion/react";
|
||||
import { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated";
|
||||
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
|
||||
import { CopyableValue } from "components/CopyableValue/CopyableValue";
|
||||
import { Stack } from "../Stack/Stack";
|
||||
import { ResourceAvatar } from "./ResourceAvatar";
|
||||
import { SensitiveValue } from "./SensitiveValue";
|
||||
import {
|
||||
OpenDropdown,
|
||||
CloseDropdown,
|
||||
} from "components/DropdownArrows/DropdownArrows";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import { CopyableValue } from "components/CopyableValue/CopyableValue";
|
||||
import { type Theme } from "@mui/material/styles";
|
||||
|
||||
const styles = {
|
||||
resourceCard: (theme) => ({
|
||||
background: theme.palette.background.paper,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
|
||||
"&:not(:first-of-type)": {
|
||||
borderTop: 0,
|
||||
borderTopLeftRadius: 0,
|
||||
borderTopRightRadius: 0,
|
||||
},
|
||||
|
||||
"&:not(:last-child)": {
|
||||
borderBottomLeftRadius: 0,
|
||||
borderBottomRightRadius: 0,
|
||||
},
|
||||
}),
|
||||
|
||||
resourceCardProfile: {
|
||||
flexShrink: 0,
|
||||
width: "fit-content",
|
||||
},
|
||||
|
||||
resourceCardHeader: (theme) => ({
|
||||
padding: theme.spacing(3, 4),
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
|
||||
"&:last-child": {
|
||||
borderBottom: 0,
|
||||
},
|
||||
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "100%",
|
||||
overflow: "scroll",
|
||||
},
|
||||
}),
|
||||
|
||||
metadata: (theme) => ({
|
||||
...(theme.typography.body2 as CSSObject),
|
||||
lineHeight: "120%",
|
||||
}),
|
||||
|
||||
metadataLabel: (theme) => ({
|
||||
fontSize: 12,
|
||||
color: theme.palette.text.secondary,
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
}),
|
||||
|
||||
metadataValue: (theme) => ({
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
...(theme.typography.body1 as CSSObject),
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
||||
export interface ResourceCardProps {
|
||||
resource: WorkspaceResource;
|
||||
@@ -33,44 +87,52 @@ export const ResourceCard: FC<ResourceCardProps> = ({ resource, agentRow }) => {
|
||||
metadataLength += 1;
|
||||
visibleMetadata.pop();
|
||||
}
|
||||
const styles = useStyles({ metadataLength });
|
||||
const gridWidth = metadataLength === 1 ? 1 : 4;
|
||||
|
||||
return (
|
||||
<div key={resource.id} className={`${styles.resourceCard} resource-card`}>
|
||||
<div key={resource.id} css={styles.resourceCard} className="resource-card">
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="flex-start"
|
||||
className={styles.resourceCardHeader}
|
||||
css={styles.resourceCardHeader}
|
||||
spacing={10}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
className={styles.resourceCardProfile}
|
||||
css={styles.resourceCardProfile}
|
||||
>
|
||||
<div>
|
||||
<ResourceAvatar resource={resource} />
|
||||
</div>
|
||||
<div className={styles.metadata}>
|
||||
<div className={styles.metadataLabel}>{resource.type}</div>
|
||||
<div className={styles.metadataValue}>{resource.name}</div>
|
||||
<div css={styles.metadata}>
|
||||
<div css={styles.metadataLabel}>{resource.type}</div>
|
||||
<div css={styles.metadataValue}>{resource.name}</div>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
<div className={styles.metadataHeader}>
|
||||
<div
|
||||
css={(theme) => ({
|
||||
flexGrow: 2,
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${gridWidth}, minmax(0, 1fr))`,
|
||||
gap: theme.spacing(5),
|
||||
rowGap: theme.spacing(3),
|
||||
})}
|
||||
>
|
||||
{resource.daily_cost > 0 && (
|
||||
<div className={styles.metadata}>
|
||||
<div className={styles.metadataLabel}>
|
||||
<div css={styles.metadata}>
|
||||
<div css={styles.metadataLabel}>
|
||||
<b>cost</b>
|
||||
</div>
|
||||
<div className={styles.metadataValue}>{resource.daily_cost}</div>
|
||||
<div css={styles.metadataValue}>{resource.daily_cost}</div>
|
||||
</div>
|
||||
)}
|
||||
{visibleMetadata.map((meta) => {
|
||||
return (
|
||||
<div className={styles.metadata} key={meta.key}>
|
||||
<div className={styles.metadataLabel}>{meta.key}</div>
|
||||
<div className={styles.metadataValue}>
|
||||
<div css={styles.metadata} key={meta.key}>
|
||||
<div css={styles.metadataLabel}>{meta.key}</div>
|
||||
<div css={styles.metadataValue}>
|
||||
{meta.sensitive ? (
|
||||
<SensitiveValue value={meta.value} />
|
||||
) : (
|
||||
@@ -95,11 +157,7 @@ export const ResourceCard: FC<ResourceCardProps> = ({ resource, agentRow }) => {
|
||||
}}
|
||||
size="large"
|
||||
>
|
||||
{shouldDisplayAllMetadata ? (
|
||||
<CloseDropdown margin={false} />
|
||||
) : (
|
||||
<OpenDropdown margin={false} />
|
||||
)}
|
||||
<DropdownArrow margin={false} close={shouldDisplayAllMetadata} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
@@ -111,66 +169,3 @@ export const ResourceCard: FC<ResourceCardProps> = ({ resource, agentRow }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles<Theme, { metadataLength: number }>((theme) => ({
|
||||
resourceCard: {
|
||||
background: theme.palette.background.paper,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
|
||||
"&:not(:first-of-type)": {
|
||||
borderTop: 0,
|
||||
borderTopLeftRadius: 0,
|
||||
borderTopRightRadius: 0,
|
||||
},
|
||||
|
||||
"&:not(:last-child)": {
|
||||
borderBottomLeftRadius: 0,
|
||||
borderBottomRightRadius: 0,
|
||||
},
|
||||
},
|
||||
|
||||
resourceCardProfile: {
|
||||
flexShrink: 0,
|
||||
width: "fit-content",
|
||||
},
|
||||
|
||||
resourceCardHeader: {
|
||||
padding: theme.spacing(3, 4),
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
|
||||
"&:last-child": {
|
||||
borderBottom: 0,
|
||||
},
|
||||
},
|
||||
|
||||
metadataHeader: (props) => ({
|
||||
flexGrow: 2,
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${
|
||||
props.metadataLength === 1 ? 1 : 4
|
||||
}, minmax(0, 1fr))`,
|
||||
gap: theme.spacing(5),
|
||||
rowGap: theme.spacing(3),
|
||||
}),
|
||||
|
||||
metadata: {
|
||||
...theme.typography.body2,
|
||||
lineHeight: "120%",
|
||||
},
|
||||
|
||||
metadataLabel: {
|
||||
fontSize: 12,
|
||||
color: theme.palette.text.secondary,
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
|
||||
metadataValue: {
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
...theme.typography.body1,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import Button from "@mui/material/Button";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import {
|
||||
CloseDropdown,
|
||||
OpenDropdown,
|
||||
} from "components/DropdownArrows/DropdownArrows";
|
||||
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
|
||||
import { FC, useState } from "react";
|
||||
import { WorkspaceAgent, WorkspaceResource } from "api/typesGenerated";
|
||||
import { Stack } from "../Stack/Stack";
|
||||
@@ -49,15 +46,8 @@ export const Resources: FC<React.PropsWithChildren<ResourcesProps>> = ({
|
||||
size="small"
|
||||
onClick={() => setShouldDisplayHideResources((v) => !v)}
|
||||
>
|
||||
{shouldDisplayHideResources ? (
|
||||
<>
|
||||
Hide resources <CloseDropdown />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Show hidden resources <OpenDropdown />
|
||||
</>
|
||||
)}
|
||||
{shouldDisplayHideResources ? "Hide" : "Show hidden"} resources
|
||||
<DropdownArrow close={shouldDisplayHideResources} />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,27 +1,111 @@
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import Radio from "@mui/material/Radio";
|
||||
import RadioGroup from "@mui/material/RadioGroup";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import TextField, { TextFieldProps } from "@mui/material/TextField";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import Box from "@mui/material/Box";
|
||||
import { FC } from "react";
|
||||
import { TemplateVersionParameter } from "api/typesGenerated";
|
||||
import { colors } from "theme/colors";
|
||||
import { MemoizedMarkdown } from "components/Markdown/Markdown";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { colors } from "theme/colors";
|
||||
import { MultiTextField } from "./MultiTextField";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import { Interpolation, Theme } from "@emotion/react";
|
||||
|
||||
const isBoolean = (parameter: TemplateVersionParameter) => {
|
||||
return parameter.type === "bool";
|
||||
};
|
||||
|
||||
const styles = {
|
||||
label: (theme) => ({
|
||||
marginBottom: theme.spacing(0.5),
|
||||
}),
|
||||
labelCaption: (theme) => ({
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.secondary,
|
||||
|
||||
".small &": {
|
||||
fontSize: 13,
|
||||
lineHeight: "140%",
|
||||
},
|
||||
}),
|
||||
labelPrimary: (theme) => ({
|
||||
fontSize: 16,
|
||||
color: theme.palette.text.primary,
|
||||
fontWeight: 600,
|
||||
|
||||
"& p": {
|
||||
margin: 0,
|
||||
lineHeight: "24px", // Keep the same as ParameterInput
|
||||
},
|
||||
|
||||
".small &": {
|
||||
fontSize: 14,
|
||||
},
|
||||
}),
|
||||
labelImmutable: (theme) => ({
|
||||
marginTop: theme.spacing(0.5),
|
||||
marginBottom: theme.spacing(0.5),
|
||||
color: colors.yellow[7],
|
||||
}),
|
||||
textField: {
|
||||
".small & .MuiInputBase-root": {
|
||||
height: 36,
|
||||
fontSize: 14,
|
||||
borderRadius: 6,
|
||||
},
|
||||
},
|
||||
radioGroup: (theme) => ({
|
||||
".small & .MuiFormControlLabel-label": {
|
||||
fontSize: 14,
|
||||
},
|
||||
".small & .MuiRadio-root": {
|
||||
padding: theme.spacing(0.75, "9px"), // 8px + 1px border
|
||||
},
|
||||
".small & .MuiRadio-root svg": {
|
||||
width: 16,
|
||||
height: 16,
|
||||
},
|
||||
}),
|
||||
checkbox: (theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(1),
|
||||
}),
|
||||
labelIconWrapper: (theme) => ({
|
||||
width: theme.spacing(2.5),
|
||||
height: theme.spacing(2.5),
|
||||
display: "block",
|
||||
|
||||
".small &": {
|
||||
display: "none",
|
||||
},
|
||||
}),
|
||||
labelIcon: {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain",
|
||||
},
|
||||
radioOption: (theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(1.5),
|
||||
}),
|
||||
optionIcon: {
|
||||
maxHeight: 20,
|
||||
width: 20,
|
||||
|
||||
".small &": {
|
||||
maxHeight: 16,
|
||||
width: 16,
|
||||
},
|
||||
},
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
||||
export interface ParameterLabelProps {
|
||||
parameter: TemplateVersionParameter;
|
||||
}
|
||||
|
||||
const ParameterLabel: FC<ParameterLabelProps> = ({ parameter }) => {
|
||||
const styles = useStyles();
|
||||
const hasDescription = parameter.description && parameter.description !== "";
|
||||
const displayName = parameter.display_name
|
||||
? parameter.display_name
|
||||
@@ -31,9 +115,9 @@ const ParameterLabel: FC<ParameterLabelProps> = ({ parameter }) => {
|
||||
<label htmlFor={parameter.name}>
|
||||
<Stack direction="row" alignItems="center">
|
||||
{parameter.icon && (
|
||||
<span className={styles.labelIconWrapper}>
|
||||
<span css={styles.labelIconWrapper}>
|
||||
<img
|
||||
className={styles.labelIcon}
|
||||
css={styles.labelIcon}
|
||||
alt="Parameter icon"
|
||||
src={parameter.icon}
|
||||
/>
|
||||
@@ -42,13 +126,13 @@ const ParameterLabel: FC<ParameterLabelProps> = ({ parameter }) => {
|
||||
|
||||
{hasDescription ? (
|
||||
<Stack spacing={0}>
|
||||
<span className={styles.labelPrimary}>{displayName}</span>
|
||||
<MemoizedMarkdown className={styles.labelCaption}>
|
||||
<span css={styles.labelPrimary}>{displayName}</span>
|
||||
<MemoizedMarkdown css={styles.labelCaption}>
|
||||
{parameter.description}
|
||||
</MemoizedMarkdown>
|
||||
</Stack>
|
||||
) : (
|
||||
<span className={styles.labelPrimary}>{displayName}</span>
|
||||
<span css={styles.labelPrimary}>{displayName}</span>
|
||||
)}
|
||||
</Stack>
|
||||
</label>
|
||||
@@ -94,14 +178,12 @@ const RichParameterField: React.FC<RichParameterInputProps> = ({
|
||||
size,
|
||||
...props
|
||||
}) => {
|
||||
const styles = useStyles();
|
||||
|
||||
if (isBoolean(parameter)) {
|
||||
return (
|
||||
<RadioGroup
|
||||
id={parameter.name}
|
||||
data-testid="parameter-field-bool"
|
||||
className={styles.radioGroup}
|
||||
css={styles.radioGroup}
|
||||
value={value}
|
||||
onChange={(_, value) => onChange(value)}
|
||||
>
|
||||
@@ -126,7 +208,7 @@ const RichParameterField: React.FC<RichParameterInputProps> = ({
|
||||
<RadioGroup
|
||||
id={parameter.name}
|
||||
data-testid="parameter-field-options"
|
||||
className={styles.radioGroup}
|
||||
css={styles.radioGroup}
|
||||
value={value}
|
||||
onChange={(_, value) => onChange(value)}
|
||||
>
|
||||
@@ -137,10 +219,10 @@ const RichParameterField: React.FC<RichParameterInputProps> = ({
|
||||
value={option.value}
|
||||
control={<Radio size="small" />}
|
||||
label={
|
||||
<span className={styles.radioOption}>
|
||||
<span css={styles.radioOption}>
|
||||
{option.icon && (
|
||||
<img
|
||||
className={styles.optionIcon}
|
||||
css={styles.optionIcon}
|
||||
alt="Parameter icon"
|
||||
src={option.icon}
|
||||
style={{
|
||||
@@ -198,7 +280,7 @@ const RichParameterField: React.FC<RichParameterInputProps> = ({
|
||||
{...props}
|
||||
id={parameter.name}
|
||||
data-testid="parameter-field-text"
|
||||
className={styles.textField}
|
||||
css={styles.textField}
|
||||
type={parameter.type}
|
||||
disabled={disabled}
|
||||
required={parameter.required}
|
||||
@@ -210,89 +292,3 @@ const RichParameterField: React.FC<RichParameterInputProps> = ({
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles<Theme>((theme) => ({
|
||||
label: {
|
||||
marginBottom: theme.spacing(0.5),
|
||||
},
|
||||
labelCaption: {
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.secondary,
|
||||
|
||||
".small &": {
|
||||
fontSize: 13,
|
||||
lineHeight: "140%",
|
||||
},
|
||||
},
|
||||
labelPrimary: {
|
||||
fontSize: 16,
|
||||
color: theme.palette.text.primary,
|
||||
fontWeight: 600,
|
||||
|
||||
"& p": {
|
||||
margin: 0,
|
||||
lineHeight: "24px", // Keep the same as ParameterInput
|
||||
},
|
||||
|
||||
".small &": {
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
labelImmutable: {
|
||||
marginTop: theme.spacing(0.5),
|
||||
marginBottom: theme.spacing(0.5),
|
||||
color: colors.yellow[7],
|
||||
},
|
||||
textField: {
|
||||
".small & .MuiInputBase-root": {
|
||||
height: 36,
|
||||
fontSize: 14,
|
||||
borderRadius: 6,
|
||||
},
|
||||
},
|
||||
radioGroup: {
|
||||
".small & .MuiFormControlLabel-label": {
|
||||
fontSize: 14,
|
||||
},
|
||||
".small & .MuiRadio-root": {
|
||||
padding: theme.spacing(0.75, "9px"), // 8px + 1px border
|
||||
},
|
||||
".small & .MuiRadio-root svg": {
|
||||
width: 16,
|
||||
height: 16,
|
||||
},
|
||||
},
|
||||
checkbox: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(1),
|
||||
},
|
||||
labelIconWrapper: {
|
||||
width: theme.spacing(2.5),
|
||||
height: theme.spacing(2.5),
|
||||
display: "block",
|
||||
|
||||
".small &": {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
labelIcon: {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain",
|
||||
},
|
||||
radioOption: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(1.5),
|
||||
},
|
||||
optionIcon: {
|
||||
maxHeight: 20,
|
||||
width: 20,
|
||||
|
||||
".small &": {
|
||||
maxHeight: 16,
|
||||
width: 16,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -1,61 +1,39 @@
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { CSSProperties } from "@mui/styles/withStyles";
|
||||
import { FC } from "react";
|
||||
import { ReactNode } from "react-markdown/lib/react-markdown";
|
||||
import { combineClasses } from "utils/combineClasses";
|
||||
|
||||
type Direction = "column" | "row";
|
||||
import type { FC } from "react";
|
||||
import type { CSSObject } from "@emotion/react";
|
||||
|
||||
export type StackProps = {
|
||||
className?: string;
|
||||
direction?: Direction;
|
||||
direction?: "column" | "row";
|
||||
spacing?: number;
|
||||
alignItems?: CSSProperties["alignItems"];
|
||||
justifyContent?: CSSProperties["justifyContent"];
|
||||
maxWidth?: CSSProperties["maxWidth"];
|
||||
wrap?: CSSProperties["flexWrap"];
|
||||
alignItems?: CSSObject["alignItems"];
|
||||
justifyContent?: CSSObject["justifyContent"];
|
||||
wrap?: CSSObject["flexWrap"];
|
||||
} & React.HTMLProps<HTMLDivElement>;
|
||||
|
||||
type StyleProps = Omit<StackProps, "className">;
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
stack: {
|
||||
display: "flex",
|
||||
flexDirection: ({ direction }: StyleProps) => direction,
|
||||
gap: ({ spacing }: StyleProps) => spacing && theme.spacing(spacing),
|
||||
alignItems: ({ alignItems }: StyleProps) => alignItems,
|
||||
justifyContent: ({ justifyContent }: StyleProps) => justifyContent,
|
||||
flexWrap: ({ wrap }: StyleProps) => wrap,
|
||||
maxWidth: ({ maxWidth }: StyleProps) => maxWidth,
|
||||
|
||||
[theme.breakpoints.down("md")]: {
|
||||
width: "100%",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export const Stack: FC<StackProps & { children: ReactNode | ReactNode[] }> = ({
|
||||
children,
|
||||
className,
|
||||
direction = "column",
|
||||
spacing = 2,
|
||||
alignItems,
|
||||
justifyContent,
|
||||
maxWidth,
|
||||
wrap,
|
||||
...divProps
|
||||
}) => {
|
||||
const styles = useStyles({
|
||||
spacing,
|
||||
direction,
|
||||
export const Stack: FC<StackProps> = (props) => {
|
||||
const {
|
||||
children,
|
||||
direction = "column",
|
||||
spacing = 2,
|
||||
alignItems,
|
||||
justifyContent,
|
||||
wrap,
|
||||
maxWidth,
|
||||
});
|
||||
...divProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div {...divProps} className={combineClasses([styles.stack, className])}>
|
||||
<div
|
||||
{...divProps}
|
||||
css={(theme) => ({
|
||||
display: "flex",
|
||||
flexDirection: direction,
|
||||
gap: spacing && theme.spacing(spacing),
|
||||
alignItems: alignItems,
|
||||
justifyContent: justifyContent,
|
||||
flexWrap: wrap,
|
||||
maxWidth: "100%",
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,10 +2,7 @@ import Collapse from "@mui/material/Collapse";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import TableCell from "@mui/material/TableCell";
|
||||
import { AuditLog } from "api/typesGenerated";
|
||||
import {
|
||||
CloseDropdown,
|
||||
OpenDropdown,
|
||||
} from "components/DropdownArrows/DropdownArrows";
|
||||
import { DropdownArrow } from "components/DropdownArrow/DropdownArrow";
|
||||
import { Pill, type PillType } from "components/Pill/Pill";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { TimelineEntry } from "components/Timeline/TimelineEntry";
|
||||
@@ -154,7 +151,7 @@ export const AuditLogRow: React.FC<AuditLogRowProps> = ({
|
||||
</Stack>
|
||||
|
||||
{shouldDisplayDiff ? (
|
||||
<div> {isDiffOpen ? <CloseDropdown /> : <OpenDropdown />}</div>
|
||||
<div> {<DropdownArrow close={isDiffOpen} />}</div>
|
||||
) : (
|
||||
<div className={styles.columnWithoutDiff}></div>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import TextField from "@mui/material/TextField";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Link from "@mui/material/Link";
|
||||
import { FormikContextType, useFormik } from "formik";
|
||||
import { FC } from "react";
|
||||
import * as Yup from "yup";
|
||||
import { hasApiFieldErrors, isApiError } from "api/errors";
|
||||
import * as TypesGen from "api/typesGenerated";
|
||||
import {
|
||||
getFormHelpers,
|
||||
@@ -12,11 +15,6 @@ import { FormFooter } from "components/FormFooter/FormFooter";
|
||||
import { FullPageForm } from "components/FullPageForm/FullPageForm";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { ErrorAlert } from "components/Alert/ErrorAlert";
|
||||
import { hasApiFieldErrors, isApiError } from "api/errors";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import Link from "@mui/material/Link";
|
||||
|
||||
export const Language = {
|
||||
emailLabel: "Email",
|
||||
@@ -104,8 +102,6 @@ export const CreateUserForm: FC<
|
||||
error,
|
||||
);
|
||||
|
||||
const styles = useStyles();
|
||||
|
||||
const methods = [
|
||||
authMethods?.password.enabled && "password",
|
||||
authMethods?.oidc.enabled && "oidc",
|
||||
@@ -161,9 +157,21 @@ export const CreateUserForm: FC<
|
||||
const language = authMethodLanguage[value];
|
||||
return (
|
||||
<MenuItem key={value} id={"item-" + value} value={value}>
|
||||
<Stack spacing={0} maxWidth={400}>
|
||||
<Stack
|
||||
spacing={0}
|
||||
css={{
|
||||
maxWidth: 400,
|
||||
}}
|
||||
>
|
||||
{language.displayName}
|
||||
<span className={styles.labelDescription}>
|
||||
<span
|
||||
css={(theme) => ({
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.secondary,
|
||||
wordWrap: "normal",
|
||||
whiteSpace: "break-spaces",
|
||||
})}
|
||||
>
|
||||
{language.description}
|
||||
</span>
|
||||
</Stack>
|
||||
@@ -192,12 +200,3 @@ export const CreateUserForm: FC<
|
||||
</FullPageForm>
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles<Theme>((theme) => ({
|
||||
labelDescription: {
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.secondary,
|
||||
wordWrap: "normal",
|
||||
whiteSpace: "break-spaces",
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -32,31 +32,6 @@ export const NoIcon: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const SmallViewport: Story = {
|
||||
args: {
|
||||
template: MockTemplate,
|
||||
activeVersion: {
|
||||
...MockTemplateVersion,
|
||||
readme: `---
|
||||
name:Template test
|
||||
---
|
||||
## Instructions
|
||||
You can add instructions here
|
||||
|
||||
[Some link info](https://coder.com)
|
||||
\`\`\`
|
||||
# This is a really long sentence to test that the code block wraps into a new line properly.
|
||||
\`\`\`
|
||||
`,
|
||||
},
|
||||
resources: [MockWorkspaceResource, MockWorkspaceVolumeResource],
|
||||
},
|
||||
};
|
||||
|
||||
SmallViewport.parameters = {
|
||||
chromatic: { viewports: [600] },
|
||||
};
|
||||
|
||||
export const WithDeprecatedParameters: Story = {
|
||||
args: {
|
||||
template: MockTemplate,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {
|
||||
import { type FC, useEffect } from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import type {
|
||||
Template,
|
||||
TemplateVersion,
|
||||
WorkspaceResource,
|
||||
@@ -7,8 +9,6 @@ import { Loader } from "components/Loader/Loader";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import { TemplateResourcesTable } from "components/TemplateResourcesTable/TemplateResourcesTable";
|
||||
import { TemplateStats } from "./TemplateStats";
|
||||
import { FC, useEffect } from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { TemplateVersionWarnings } from "components/TemplateVersionWarnings/TemplateVersionWarnings";
|
||||
|
||||
export interface TemplateSummaryPageViewProps {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {
|
||||
MockFailedProvisionerJob,
|
||||
MockRunningProvisionerJob,
|
||||
MockTemplate,
|
||||
MockTemplateVersion,
|
||||
MockTemplateVersionFileTree,
|
||||
@@ -15,7 +17,7 @@ import { TemplateVersionEditor } from "./TemplateVersionEditor";
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
|
||||
const meta: Meta<typeof TemplateVersionEditor> = {
|
||||
title: "components/TemplateVersionEditor",
|
||||
title: "pages/TemplateVersionEditor",
|
||||
component: TemplateVersionEditor,
|
||||
args: {
|
||||
template: MockTemplate,
|
||||
@@ -34,12 +36,18 @@ export const Example: Story = {};
|
||||
|
||||
export const Logs = {
|
||||
args: {
|
||||
isBuildingNewVersion: true,
|
||||
buildLogs: MockWorkspaceBuildLogs,
|
||||
templateVersion: {
|
||||
...MockTemplateVersion,
|
||||
job: MockRunningProvisionerJob,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Resources: Story = {
|
||||
args: {
|
||||
isBuildingNewVersion: true,
|
||||
buildLogs: MockWorkspaceBuildLogs,
|
||||
resources: [
|
||||
MockWorkspaceResource,
|
||||
@@ -54,10 +62,11 @@ export const Resources: Story = {
|
||||
|
||||
export const ManyLogs = {
|
||||
args: {
|
||||
isBuildingNewVersion: true,
|
||||
templateVersion: {
|
||||
...MockTemplateVersion,
|
||||
job: {
|
||||
...MockTemplateVersion.job,
|
||||
...MockFailedProvisionerJob,
|
||||
error:
|
||||
"template import provision for start: terraform plan: exit status 1",
|
||||
},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import Button from "@mui/material/Button";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Link from "@mui/material/Link";
|
||||
import { makeStyles } from "@mui/styles";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import CreateIcon from "@mui/icons-material/AddOutlined";
|
||||
import BuildIcon from "@mui/icons-material/BuildOutlined";
|
||||
@@ -21,7 +20,7 @@ import { AvatarData } from "components/AvatarData/AvatarData";
|
||||
import { TemplateResourcesTable } from "components/TemplateResourcesTable/TemplateResourcesTable";
|
||||
import { WorkspaceBuildLogs } from "components/WorkspaceBuildLogs/WorkspaceBuildLogs";
|
||||
import { PublishVersionData } from "pages/TemplateVersionEditorPage/types";
|
||||
import { FC, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { type FC, useCallback, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
createFile,
|
||||
existsFile,
|
||||
@@ -46,17 +45,17 @@ import {
|
||||
getStatus,
|
||||
TemplateVersionStatusBadge,
|
||||
} from "./TemplateVersionStatusBadge";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import AlertTitle from "@mui/material/AlertTitle";
|
||||
import { DashboardFullPage } from "components/Dashboard/DashboardLayout";
|
||||
import { type Interpolation, type Theme, useTheme } from "@emotion/react";
|
||||
|
||||
export interface TemplateVersionEditorProps {
|
||||
template: Template;
|
||||
templateVersion: TemplateVersion;
|
||||
isBuildingNewVersion: boolean;
|
||||
defaultFileTree: FileTree;
|
||||
buildLogs?: ProvisionerJobLog[];
|
||||
resources?: WorkspaceResource[];
|
||||
deploymentBannerVisible?: boolean;
|
||||
disablePreview: boolean;
|
||||
disableUpdate: boolean;
|
||||
onPreview: (files: FileTree) => void;
|
||||
@@ -92,8 +91,8 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
disablePreview,
|
||||
disableUpdate,
|
||||
template,
|
||||
deploymentBannerVisible,
|
||||
templateVersion,
|
||||
isBuildingNewVersion,
|
||||
defaultFileTree,
|
||||
onPreview,
|
||||
onPublish,
|
||||
@@ -111,6 +110,7 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
onSubmitMissingVariableValues,
|
||||
onCancelSubmitMissingVariableValues,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
// If resources are provided, show them by default!
|
||||
// This is for Storybook!
|
||||
const [selectedTab, setSelectedTab] = useState(() => (resources ? 1 : 0));
|
||||
@@ -170,25 +170,22 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
}, [templateVersion]);
|
||||
|
||||
const hasIcon = template.icon && template.icon !== "";
|
||||
const templateVersionSucceeded = templateVersion.job.status === "succeeded";
|
||||
const showBuildLogs = Boolean(buildLogs);
|
||||
const editorValue = getFileContent(activePath ?? "", fileTree) as string;
|
||||
const firstTemplateVersionOnEditor = useRef(templateVersion);
|
||||
|
||||
useEffect(() => {
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
}, [showBuildLogs]);
|
||||
const styles = useStyles({
|
||||
templateVersionSucceeded,
|
||||
showBuildLogs,
|
||||
deploymentBannerVisible,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<DashboardFullPage className={styles.root}>
|
||||
<div className={styles.topbar} data-testid="topbar">
|
||||
<div className={styles.topbarSides}>
|
||||
<DashboardFullPage
|
||||
css={{
|
||||
background: theme.palette.background.default,
|
||||
}}
|
||||
>
|
||||
<div css={styles.topbar} data-testid="topbar">
|
||||
<div css={styles.topbarSides}>
|
||||
<Link
|
||||
component={RouterLink}
|
||||
underline="none"
|
||||
@@ -220,12 +217,9 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className={styles.topbarSides}>
|
||||
{/* Only start to show the build when a new template version is building */}
|
||||
{templateVersion.id !== firstTemplateVersionOnEditor.current.id && (
|
||||
<div className={styles.buildStatus}>
|
||||
<TemplateVersionStatusBadge version={templateVersion} />
|
||||
</div>
|
||||
<div css={styles.topbarSides}>
|
||||
{isBuildingNewVersion && (
|
||||
<TemplateVersionStatusBadge version={templateVersion} />
|
||||
)}
|
||||
|
||||
<Button
|
||||
@@ -248,11 +242,11 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.sidebarAndEditor}>
|
||||
<div className={styles.sidebar}>
|
||||
<div className={styles.sidebarTitle}>
|
||||
<div css={styles.sidebarAndEditor}>
|
||||
<div css={styles.sidebar}>
|
||||
<div css={styles.sidebarTitle}>
|
||||
Template files
|
||||
<div className={styles.sidebarActions}>
|
||||
<div css={styles.sidebarActions}>
|
||||
<Tooltip title="Create File" placement="top">
|
||||
<IconButton
|
||||
aria-label="Create File"
|
||||
@@ -331,8 +325,16 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.editorPane}>
|
||||
<div className={styles.editor} data-chromatic="ignore">
|
||||
<div
|
||||
css={{
|
||||
display: "grid",
|
||||
width: "100%",
|
||||
gridTemplateColumns: showBuildLogs ? "1fr 1fr" : "1fr 0fr",
|
||||
minHeight: "100%",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<div css={styles.editor} data-chromatic="ignore">
|
||||
{activePath ? (
|
||||
<MonacoEditor
|
||||
value={editorValue}
|
||||
@@ -352,12 +354,11 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.panelWrapper}>
|
||||
<div className={styles.tabs}>
|
||||
<div css={styles.panelWrapper}>
|
||||
<div css={styles.tabs}>
|
||||
<button
|
||||
className={`${styles.tab} ${
|
||||
selectedTab === 0 ? "active" : ""
|
||||
}`}
|
||||
css={styles.tab}
|
||||
className={selectedTab === 0 ? "active" : ""}
|
||||
onClick={() => {
|
||||
setSelectedTab(0);
|
||||
}}
|
||||
@@ -372,9 +373,8 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
|
||||
{!disableUpdate && (
|
||||
<button
|
||||
className={`${styles.tab} ${
|
||||
selectedTab === 1 ? "active" : ""
|
||||
}`}
|
||||
css={styles.tab}
|
||||
className={selectedTab === 1 ? "active" : ""}
|
||||
onClick={() => {
|
||||
setSelectedTab(1);
|
||||
}}
|
||||
@@ -386,9 +386,13 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`${styles.panel} ${styles.buildLogs} ${
|
||||
selectedTab === 0 ? "" : "hidden"
|
||||
}`}
|
||||
css={[
|
||||
styles.panel,
|
||||
{
|
||||
display: selectedTab !== 0 ? "none" : "flex",
|
||||
flexDirection: "column",
|
||||
},
|
||||
]}
|
||||
>
|
||||
{templateVersion.job.error && (
|
||||
<div>
|
||||
@@ -419,9 +423,13 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`${styles.panel} ${styles.resources} ${
|
||||
selectedTab === 1 ? "" : "hidden"
|
||||
}`}
|
||||
css={[
|
||||
styles.panel,
|
||||
{
|
||||
paddingBottom: theme.spacing(2),
|
||||
display: selectedTab !== 1 ? "none" : undefined,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{resources && (
|
||||
<TemplateResourcesTable
|
||||
@@ -432,12 +440,6 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{templateVersionSucceeded && (
|
||||
<>
|
||||
<div className={styles.panelDivider} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</DashboardFullPage>
|
||||
@@ -462,18 +464,8 @@ export const TemplateVersionEditor: FC<TemplateVersionEditorProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const useStyles = makeStyles<
|
||||
Theme,
|
||||
{
|
||||
templateVersionSucceeded: boolean;
|
||||
showBuildLogs: boolean;
|
||||
deploymentBannerVisible?: boolean;
|
||||
}
|
||||
>((theme) => ({
|
||||
root: {
|
||||
background: theme.palette.background.default,
|
||||
},
|
||||
topbar: {
|
||||
const styles = {
|
||||
topbar: (theme) => ({
|
||||
padding: theme.spacing(2),
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
display: "flex",
|
||||
@@ -481,29 +473,24 @@ const useStyles = makeStyles<
|
||||
justifyContent: "space-between",
|
||||
height: topbarHeight,
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
topbarSides: {
|
||||
}),
|
||||
topbarSides: (theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(2),
|
||||
},
|
||||
buildStatus: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
},
|
||||
}),
|
||||
sidebarAndEditor: {
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
flexBasis: 0,
|
||||
overflow: "hidden",
|
||||
},
|
||||
sidebar: {
|
||||
sidebar: (theme) => ({
|
||||
minWidth: 256,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
borderRight: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
sidebarTitle: {
|
||||
}),
|
||||
sidebarTitle: (theme) => ({
|
||||
fontSize: 10,
|
||||
textTransform: "uppercase",
|
||||
padding: theme.spacing(1, 2),
|
||||
@@ -512,45 +499,33 @@ const useStyles = makeStyles<
|
||||
letterSpacing: "0.5px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
sidebarActions: {
|
||||
}),
|
||||
sidebarActions: (theme) => ({
|
||||
marginLeft: "auto",
|
||||
"& svg": {
|
||||
fill: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
editorPane: {
|
||||
display: "grid",
|
||||
width: "100%",
|
||||
gridTemplateColumns: (props) =>
|
||||
props.showBuildLogs ? "1fr 1fr" : "1fr 0fr",
|
||||
minHeight: "100%",
|
||||
overflow: "hidden",
|
||||
},
|
||||
}),
|
||||
editor: {
|
||||
flex: 1,
|
||||
},
|
||||
panelWrapper: {
|
||||
panelWrapper: (theme) => ({
|
||||
flex: 1,
|
||||
borderLeft: `1px solid ${theme.palette.divider}`,
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
},
|
||||
}),
|
||||
panel: {
|
||||
overflowY: "auto",
|
||||
height: "100%",
|
||||
|
||||
"&.hidden": {
|
||||
display: "none",
|
||||
},
|
||||
|
||||
// Hack to access customize resource-card from here
|
||||
"& .resource-card": {
|
||||
border: 0,
|
||||
},
|
||||
},
|
||||
tabs: {
|
||||
tabs: (theme) => ({
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
display: "flex",
|
||||
boxShadow: "#000000 0 6px 6px -6px inset",
|
||||
@@ -561,8 +536,8 @@ const useStyles = makeStyles<
|
||||
textTransform: "none",
|
||||
letterSpacing: "unset",
|
||||
},
|
||||
},
|
||||
tab: {
|
||||
}),
|
||||
tab: (theme) => ({
|
||||
cursor: "pointer",
|
||||
padding: theme.spacing(1.5),
|
||||
fontSize: 10,
|
||||
@@ -601,8 +576,8 @@ const useStyles = makeStyles<
|
||||
"&:hover": {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
tabBar: {
|
||||
}),
|
||||
tabBar: (theme) => ({
|
||||
padding: "8px 16px",
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
@@ -615,12 +590,5 @@ const useStyles = makeStyles<
|
||||
"&.top": {
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
},
|
||||
buildLogs: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
},
|
||||
resources: {
|
||||
paddingBottom: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useMachine } from "@xstate/react";
|
||||
import { TemplateVersionEditor } from "./TemplateVersionEditor";
|
||||
import { useOrganizationId } from "hooks/useOrganizationId";
|
||||
import { usePermissions } from "hooks/usePermissions";
|
||||
import { FC } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
@@ -22,7 +21,6 @@ export const TemplateVersionEditorPage: FC = () => {
|
||||
const [editorState, sendEvent] = useMachine(templateVersionEditorMachine, {
|
||||
context: { orgId },
|
||||
});
|
||||
const permissions = usePermissions();
|
||||
const { isSuccess, data } = useTemplateVersionData(
|
||||
{
|
||||
orgId,
|
||||
@@ -45,8 +43,8 @@ export const TemplateVersionEditorPage: FC = () => {
|
||||
{isSuccess && (
|
||||
<TemplateVersionEditor
|
||||
template={data.template}
|
||||
deploymentBannerVisible={permissions.viewDeploymentStats}
|
||||
templateVersion={editorState.context.version || data.version}
|
||||
isBuildingNewVersion={Boolean(editorState.context.version)}
|
||||
defaultFileTree={data.fileTree}
|
||||
onPreview={(fileTree) => {
|
||||
sendEvent({
|
||||
|
||||
Reference in New Issue
Block a user