Skip to content

Commit

Permalink
feat: new emui use monaco as json editor (#1733)
Browse files Browse the repository at this point in the history
## Description:
This PR switches to using the monaco editor for json input. I've changed
the layout functionality to effectively match the previous
`CodeEditor.js` - which seems to work for me, though I can't explain why
calling `layout` twice (or so frequently) is necessary.

This PR also fixes the use of the Inter font

Additionally this PR fixes several pieces of feedback from Tise:
* breadcrumb styling
* Uppercase text in tags
* Tab hover
* `x selected` hover interactivity
* Update progress widget and colour used
* Implement the copy toast
* Fix table header icons and hover appearance.

### Demo


https://github.com/kurtosis-tech/kurtosis/assets/4419574/f01efc78-2022-4edf-a18d-01587f053390

## Is this change user facing?
YES
  • Loading branch information
Dartoxian committed Nov 9, 2023
1 parent a559ae2 commit 298a0a2
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 50 deletions.
16 changes: 11 additions & 5 deletions enclave-manager/web/src/components/CodeEditor.tsx
@@ -1,7 +1,7 @@
import { Box } from "@chakra-ui/react";
import { Editor, OnChange, OnMount } from "@monaco-editor/react";
import { editor } from "monaco-editor";
import { useState } from "react";
import { useEffect, useState } from "react";
import { isDefined } from "../utils";

type CodeEditorProps = {
Expand All @@ -14,8 +14,12 @@ export const CodeEditor = ({ text, onTextChange, showLineNumbers }: CodeEditorPr
const isReadOnly = !isDefined(onTextChange);
const [editor, setEditor] = useState<editor.IStandaloneCodeEditor>();

const handleContentSizeChange = (e: editor.IContentSizeChangedEvent) => {
editor?.layout({ width: 500, height: e.contentHeight });
const resizeEditorBasedOnContent = () => {
if (isDefined(editor)) {
const contentHeight = Math.min(750, editor.getContentHeight() || 10);
editor.layout({ width: 500, height: contentHeight });
editor.layout();
}
};

const handleMount: OnMount = (editor, monaco) => {
Expand All @@ -27,17 +31,19 @@ export const CodeEditor = ({ text, onTextChange, showLineNumbers }: CodeEditorPr
colors: {},
});
monaco.editor.setTheme("kurtosis-theme");
editor.onDidContentSizeChange(handleContentSizeChange);
};

useEffect(() => resizeEditorBasedOnContent(), [editor]);

const handleChange: OnChange = (value, ev) => {
if (isDefined(value) && onTextChange) {
onTextChange(value);
resizeEditorBasedOnContent();
}
};

return (
<Box width={"100%"} minHeight={`${editor?.getContentHeight() || 10}px`}>
<Box width={"100%"} minHeight={`${editor?.getLayoutInfo().height || 10}px`}>
<Editor
onMount={handleMount}
value={text}
Expand Down
5 changes: 3 additions & 2 deletions enclave-manager/web/src/components/CopyButton.tsx
@@ -1,6 +1,7 @@
import { Button, ButtonProps, IconButton, IconButtonProps, useToast } from "@chakra-ui/react";
import { FiCopy } from "react-icons/fi";
import { isDefined } from "../utils";
import { SuccessToast } from "./Toasts";

type CopyButtonProps<IsIconButton extends boolean> = (IsIconButton extends true ? IconButtonProps : ButtonProps) & {
valueToCopy?: (() => string) | string | null;
Expand All @@ -23,8 +24,8 @@ export const CopyButton = <IsIconButton extends boolean>({
const v = typeof valueToCopy === "string" ? valueToCopy : valueToCopy();
navigator.clipboard.writeText(v);
toast({
title: `Copied ${contentName} to the clipboard`,
status: `success`,
position: "bottom",
render: () => <SuccessToast message={`Copied ${contentName} to the clipboard`} />,
});
}
};
Expand Down
25 changes: 13 additions & 12 deletions enclave-manager/web/src/components/DataTable.tsx
@@ -1,5 +1,4 @@
import { TriangleDownIcon, TriangleUpIcon } from "@chakra-ui/icons";
import { Button, chakra, Table, Tbody, Td, Th, Thead, Tr } from "@chakra-ui/react";
import { Button, Icon, Table, Tbody, Td, Th, Thead, Tr } from "@chakra-ui/react";
import {
ColumnDef,
flexRender,
Expand All @@ -13,6 +12,7 @@ import {
import { type RowSelectionState } from "@tanstack/table-core/src/features/RowSelection";
import { type OnChangeFn } from "@tanstack/table-core/src/types";
import { useState } from "react";
import { BiDownArrowAlt, BiUpArrowAlt } from "react-icons/bi";
import { assertDefined, isDefined } from "../utils";

declare module "@tanstack/table-core" {
Expand Down Expand Up @@ -80,20 +80,21 @@ export function DataTable<Data extends object>({
textAlign={!!meta?.centerAligned ? "center" : undefined}
>
{header.column.getCanSort() && (
<Button variant={"sortableHeader"} size={"xs"}>
<Button
variant={"sortableHeader"}
size={"xs"}
rightIcon={
header.column.getIsSorted() === "desc" ? (
<Icon as={BiDownArrowAlt} color={"gray.400"} />
) : header.column.getIsSorted() === "asc" ? (
<Icon as={BiUpArrowAlt} color={"gray.400"} />
) : undefined
}
>
{flexRender(header.column.columnDef.header, header.getContext())}
</Button>
)}
{!header.column.getCanSort() && flexRender(header.column.columnDef.header, header.getContext())}
{header.column.getIsSorted() && (
<chakra.span pl="4">
{header.column.getIsSorted() === "desc" ? (
<TriangleDownIcon aria-label="sorted descending" color={"gray.400"} />
) : (
<TriangleUpIcon aria-label="sorted ascending" color={"gray.400"} />
)}
</chakra.span>
)}
</Th>
);
})}
Expand Down
10 changes: 8 additions & 2 deletions enclave-manager/web/src/components/KurtosisBreadcrumbs.tsx
@@ -1,5 +1,5 @@
import { ChevronRightIcon } from "@chakra-ui/icons";
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, Flex } from "@chakra-ui/react";
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, Button, Flex } from "@chakra-ui/react";
import { useEffect, useState } from "react";
import { Link, Params, UIMatch, useMatches } from "react-router-dom";
import { isDefined } from "../utils";
Expand Down Expand Up @@ -46,7 +46,13 @@ export const KurtosisBreadcrumbs = () => {
{matchCrumbs.map(({ name, destination }, i, arr) => (
<BreadcrumbItem key={i} isCurrentPage={i === arr.length - 1}>
<BreadcrumbLink as={i === arr.length - 1 ? undefined : Link} to={destination}>
{name}
{i === arr.length - 1 ? (
name
) : (
<Button variant={"breadcrumb"} size={"sm"}>
{name}
</Button>
)}
</BreadcrumbLink>
</BreadcrumbItem>
))}
Expand Down
14 changes: 12 additions & 2 deletions enclave-manager/web/src/components/KurtosisThemeProvider.tsx
Expand Up @@ -97,7 +97,8 @@ const theme = extendTheme({
},
variants: {
outline: (props: StyleFunctionProps) => ({
_hover: { borderColor: `${props.colorScheme}.400` },
_hover: { borderColor: `${props.colorScheme}.400`, bg: `gray.700` },
_active: { bg: `gray.800` },
color: `${props.colorScheme}.400`,
borderColor: "gray.300",
}),
Expand All @@ -114,7 +115,8 @@ const theme = extendTheme({
const outline = theme.components.Button.variants!.outline(props);
return {
...outline,
_hover: { ...outline._hover, bg: "gray.700" },
_hover: { ...outline._hover, bg: "gray.700", borderColor: "gray.300", cursor: "unset" },
_active: { ...outline._active, bg: "gray.700", borderColor: "gray.300", cursor: "unset" },
bg: "gray.700",
color: `${props.colorScheme}.100`,
borderColor: "gray.300",
Expand All @@ -137,6 +139,14 @@ const theme = extendTheme({
textTransform: "uppercase",
};
},
breadcrumb: (props: StyleFunctionProps) => {
const ghost = theme.components.Button.variants!.ghost(props);
return {
...ghost,
color: "gray.100",
fontWeight: "normal",
};
},
nav: {
_active: {
bg: "gray.600",
Expand Down
17 changes: 17 additions & 0 deletions enclave-manager/web/src/components/Toasts.tsx
@@ -0,0 +1,17 @@
import { CheckCircleIcon } from "@chakra-ui/icons";
import { Flex, forwardRef, Icon, Text } from "@chakra-ui/react";

type ToastProps = {
message: string;
};

export const SuccessToast = forwardRef<ToastProps, "div">(({ message }: ToastProps, ref) => {
return (
<Flex ref={ref} bg={"rgba(0, 194, 35, 0.24)"} p={"6px 16px"} borderRadius={"6px"} gap={"12px"}>
<Icon height={"24px"} width={"24px"} as={CheckCircleIcon} color={"kurtosisGreen.400"} />
<Text fontWeight={"bold"} fontSize={"lg"}>
{message}
</Text>
</Flex>
);
});
@@ -1,25 +1,28 @@
import { Textarea } from "@chakra-ui/react";
import { Controller } from "react-hook-form";
import { stringifyError } from "../../../../utils";
import { CodeEditor } from "../../../CodeEditor";
import { useEnclaveConfigurationFormContext } from "../EnclaveConfigurationForm";
import { KurtosisArgumentTypeInputProps } from "./KurtosisArgumentTypeInput";

export const JSONArgumentInput = (props: Omit<KurtosisArgumentTypeInputProps, "type">) => {
const { register } = useEnclaveConfigurationFormContext();
const { control } = useEnclaveConfigurationFormContext();

return (
<Textarea
{...register(props.name, {
disabled: props.disabled,
<Controller
render={({ field }) => <CodeEditor text={field.value} onTextChange={field.onChange} />}
name={props.name}
defaultValue={"{}"}
rules={{
required: props.isRequired,
value: "{}",
validate: (value: string) => {
try {
JSON.parse(value);
} catch (err: any) {
return `This is not valid JSON. ${stringifyError(err)}`;
}
},
})}
}}
disabled={props.disabled}
/>
);
};
15 changes: 1 addition & 14 deletions enclave-manager/web/src/components/theme/Fonts.tsx
@@ -1,21 +1,8 @@
import { Global } from "@emotion/react";

/*
* Source: https://fonts.googleapis.com/css2?family=Inter:wght@500&display=swap
* */
const Fonts = () => (
<Global
styles={`
@font-face {
font-family: 'Inter';
font-stretch: 75% 125%;
font-style: normal;
font-weight: 150 900;
font-display: swap;
src: url(https://fonts.gstatic.com/s/inter/v13/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7SUc.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
`}
styles={`@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');`}
/>
);

Expand Down
5 changes: 4 additions & 1 deletion enclave-manager/web/src/components/theme/tabsTheme.ts
Expand Up @@ -15,8 +15,11 @@ export const tabsTheme = defineMultiStyleConfig({
fontStyle: "normal",
fontWeight: "medium",
fontSize: "lg",
color: "gray.200",
color: "gray.100",
lineHeight: "28px",
_hover: {
bg: `gray.700`,
},
_selected: {
fontWeight: "semibold",
color: `${props.colorScheme}.400`,
Expand Down
4 changes: 3 additions & 1 deletion enclave-manager/web/src/components/theme/tagsTheme.ts
Expand Up @@ -5,6 +5,9 @@ const { defineMultiStyleConfig } = createMultiStyleConfigHelpers(tagAnatomy.keys

// export the component theme
export const tagTheme = defineMultiStyleConfig({
baseStyle: {
container: { textTransform: "uppercase" },
},
variants: {
asText: (props: StyleFunctionProps) => ({
container: {
Expand All @@ -24,7 +27,6 @@ export const tagTheme = defineMultiStyleConfig({
fontSize: "xs",
lineHeight: "16px",
borderRadius: "2px",
textTransform: "uppercase",
fontWeight: "bold",
minHeight: "unset",
},
Expand Down
@@ -1,6 +1,7 @@
import { CircularProgress } from "@chakra-ui/react";
import { CircularProgress, Icon } from "@chakra-ui/react";
import { StarlarkRunResponseLine } from "enclave-manager-sdk/build/api_container_service_pb";
import { useEffect, useState } from "react";
import { FiCheck, FiX } from "react-icons/fi";
import { Location, useLocation, useNavigate, useRevalidator } from "react-router-dom";
import { LogLineProps } from "../../../../components/enclaves/logs/LogLine";
import { LogViewer } from "../../../../components/enclaves/logs/LogViewer";
Expand Down Expand Up @@ -139,7 +140,7 @@ const ProgressSummary = ({ progress }: ProgressSummaryProps) => {
<CircularProgress
size={"18px"}
value={(100 * progress.step + 1) / (progress.totalSteps + 1)}
color={"green"}
color={"kurtosisGreen.400"}
/>
<span>
{progress.step} / {progress.totalSteps}
Expand All @@ -148,15 +149,15 @@ const ProgressSummary = ({ progress }: ProgressSummaryProps) => {
)}
{progress.stage === "done" && (
<>
<CircularProgress size={"18px"} value={100} color={"green"} />
<Icon as={FiCheck} size={"18px"} color={"kurtosisGreen.400"} />
<span>
{progress.totalSteps} / {progress.totalSteps}
</span>
</>
)}
{progress.stage === "failed" && (
<>
<CircularProgress size={"18px"} value={100} color={"red"} />
<Icon as={FiX} size={"18px"} color={"red.400"} />
<span>Failed</span>
</>
)}
Expand Down

0 comments on commit 298a0a2

Please sign in to comment.