-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Description: This PR includes a number or emui enhancements: * Artifacts view * fix to dict/list input sizes * Update logging ui to match designs * Update general app layout to match designs (headers) Additionally the feedback left on #1830 is implemented. ### Demo of the artifacts view https://github.com/kurtosis-tech/kurtosis/assets/4419574/0849e46e-c410-4a07-a65f-bcc74297c53b ## Is this change user facing? YES
- Loading branch information
Showing
49 changed files
with
1,752 additions
and
803 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,112 @@ | ||
import { Flex } from "@chakra-ui/react"; | ||
import React, { PropsWithChildren } from "react"; | ||
import { PropsWithChildren, useRef } from "react"; | ||
import { Navbar } from "../emui/Navbar"; | ||
import { KurtosisBreadcrumbs } from "./KurtosisBreadcrumbs"; | ||
import { MAIN_APP_MAX_WIDTH } from "./theme/constants"; | ||
import { | ||
MAIN_APP_BOTTOM_PADDING, | ||
MAIN_APP_LEFT_PADDING, | ||
MAIN_APP_MAX_WIDTH, | ||
MAIN_APP_RIGHT_PADDING, | ||
MAIN_APP_TOP_PADDING, | ||
} from "./theme/constants"; | ||
|
||
type AppLayoutProps = PropsWithChildren<{ | ||
Nav: React.ReactElement; | ||
}>; | ||
|
||
export const AppLayout = ({ Nav, children }: AppLayoutProps) => { | ||
export const AppLayout = ({ children }: PropsWithChildren) => { | ||
return ( | ||
<> | ||
{Nav} | ||
<Navbar /> | ||
<Flex | ||
as="main" | ||
w={"100%"} | ||
minH={"calc(100vh - 40px)"} | ||
minH={"100vh"} | ||
justifyContent={"flex-start"} | ||
p={"20px 40px 20px 112px"} | ||
flexDirection={"column"} | ||
className={"app-container"} | ||
> | ||
<Flex maxWidth={MAIN_APP_MAX_WIDTH} w={"100%"}> | ||
<Flex direction={"column"} gap={"36px"} width={"100%"}> | ||
<KurtosisBreadcrumbs /> | ||
{children} | ||
</Flex> | ||
</> | ||
); | ||
}; | ||
|
||
type AppPageLayoutProps = PropsWithChildren<{ | ||
preventPageScroll?: boolean; | ||
}>; | ||
|
||
export const AppPageLayout = ({ preventPageScroll, children }: AppPageLayoutProps) => { | ||
const headerRef = useRef<HTMLDivElement>(null); | ||
const numberOfChildren = Array.isArray(children) ? children.length : 1; | ||
|
||
if (numberOfChildren === 1) { | ||
return ( | ||
<Flex | ||
flexDirection={"column"} | ||
w={"100%"} | ||
h={"100%"} | ||
maxHeight={preventPageScroll ? `100vh` : undefined} | ||
flex={"1"} | ||
> | ||
<Flex | ||
flexDirection={"column"} | ||
flex={"1"} | ||
w={"100%"} | ||
h={"100%"} | ||
maxWidth={MAIN_APP_MAX_WIDTH} | ||
pl={MAIN_APP_LEFT_PADDING} | ||
pr={MAIN_APP_RIGHT_PADDING} | ||
> | ||
<KurtosisBreadcrumbs /> | ||
<Flex | ||
w={"100%"} | ||
h={"100%"} | ||
pt={MAIN_APP_TOP_PADDING} | ||
pb={MAIN_APP_BOTTOM_PADDING} | ||
flexDirection={"column"} | ||
flex={"1"} | ||
> | ||
{children} | ||
</Flex> | ||
</Flex> | ||
</Flex> | ||
</> | ||
); | ||
} | ||
|
||
// TS cannot infer that children is an array if numberOfChildren === 2 | ||
if (numberOfChildren === 2 && Array.isArray(children)) { | ||
return ( | ||
<Flex direction="column" width={"100%"} h={"100%"} flex={"1"}> | ||
<Flex ref={headerRef} width={"100%"} bg={"gray.850"}> | ||
<Flex | ||
flexDirection={"column"} | ||
width={"100%"} | ||
pl={MAIN_APP_LEFT_PADDING} | ||
pr={MAIN_APP_RIGHT_PADDING} | ||
maxW={MAIN_APP_MAX_WIDTH} | ||
> | ||
<KurtosisBreadcrumbs /> | ||
{children[0]} | ||
</Flex> | ||
</Flex> | ||
<Flex | ||
maxWidth={MAIN_APP_MAX_WIDTH} | ||
pl={MAIN_APP_LEFT_PADDING} | ||
pr={MAIN_APP_RIGHT_PADDING} | ||
pt={MAIN_APP_TOP_PADDING} | ||
pb={MAIN_APP_BOTTOM_PADDING} | ||
w={"100%"} | ||
h={"100%"} | ||
flex={"1"} | ||
flexDirection={"column"} | ||
maxHeight={preventPageScroll ? `calc(100vh - ${headerRef.current?.offsetHeight || 0}px)` : undefined} | ||
> | ||
{children[1]} | ||
</Flex> | ||
</Flex> | ||
); | ||
} | ||
|
||
throw new Error( | ||
`AppPageLayout expects to receive exactly one or two children. ` + | ||
`If there are two children, the first child is the header section and the next child is the body. ` + | ||
`Otherwise the only child is the body.`, | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,77 +1,132 @@ | ||
import { Box } from "@chakra-ui/react"; | ||
import { Editor, OnChange, OnMount } from "@monaco-editor/react"; | ||
import { editor } from "monaco-editor"; | ||
import { useState } from "react"; | ||
import { isDefined } from "../utils"; | ||
import { editor as monacoEditor } from "monaco-editor"; | ||
import { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react"; | ||
import { assertDefined, isDefined } from "../utils"; | ||
|
||
type CodeEditorProps = { | ||
text: string; | ||
fileName?: string; | ||
onTextChange?: (newText: string) => void; | ||
showLineNumbers?: boolean; | ||
}; | ||
|
||
export const CodeEditor = ({ text, onTextChange, showLineNumbers }: CodeEditorProps) => { | ||
const isReadOnly = !isDefined(onTextChange); | ||
const [editor, setEditor] = useState<editor.IStandaloneCodeEditor>(); | ||
export type CodeEditorImperativeAttributes = { | ||
formatCode: () => Promise<void>; | ||
}; | ||
|
||
export const CodeEditor = forwardRef<CodeEditorImperativeAttributes, CodeEditorProps>( | ||
({ text, fileName, onTextChange, showLineNumbers }, ref) => { | ||
const isReadOnly = !isDefined(onTextChange); | ||
const [editor, setEditor] = useState<monacoEditor.IStandaloneCodeEditor>(); | ||
|
||
const resizeEditorBasedOnContent = useCallback(() => { | ||
if (isDefined(editor)) { | ||
// An initial layout call is needed, else getContentHeight is garbage | ||
editor.layout(); | ||
const contentHeight = editor.getContentHeight(); | ||
editor.layout({ width: editor.getContentWidth(), height: contentHeight }); | ||
// Unclear why layout must be called twice, but seems to be necessary | ||
editor.layout(); | ||
} | ||
}, [editor]); | ||
|
||
const resizeEditorBasedOnContent = () => { | ||
if (isDefined(editor)) { | ||
// An initial layout call is needed, else getContentHeight is garbage | ||
editor.layout(); | ||
const contentHeight = editor.getContentHeight(); | ||
editor.layout({ width: 500, height: contentHeight }); | ||
// Unclear why layout must be called twice, but seems to be necessary | ||
editor.layout(); | ||
} | ||
}; | ||
const handleMount: OnMount = (editor, monaco) => { | ||
setEditor(editor); | ||
const colors: monacoEditor.IColors = {}; | ||
if (isReadOnly) { | ||
colors["editor.background"] = "#111111"; | ||
} | ||
monaco.editor.defineTheme("kurtosis-theme", { | ||
base: "vs-dark", | ||
inherit: true, | ||
rules: [], | ||
colors, | ||
}); | ||
monaco.editor.setTheme("kurtosis-theme"); | ||
}; | ||
|
||
const handleMount: OnMount = (editor, monaco) => { | ||
setEditor(editor); | ||
monaco.editor.defineTheme("kurtosis-theme", { | ||
base: "vs-dark", | ||
inherit: true, | ||
rules: [], | ||
colors: {}, | ||
}); | ||
monaco.editor.setTheme("kurtosis-theme"); | ||
}; | ||
const handleChange: OnChange = (value, ev) => { | ||
if (isDefined(value) && onTextChange) { | ||
onTextChange(value); | ||
resizeEditorBasedOnContent(); | ||
} | ||
}; | ||
|
||
const handleChange: OnChange = (value, ev) => { | ||
if (isDefined(value) && onTextChange) { | ||
onTextChange(value); | ||
useImperativeHandle( | ||
ref, | ||
() => ({ | ||
formatCode: async () => { | ||
console.log("formatting"); | ||
if (!isDefined(editor)) { | ||
// do nothing | ||
console.log("no editor"); | ||
return; | ||
} | ||
return new Promise((resolve) => { | ||
const listenerDisposer = editor.onDidChangeConfiguration((event) => { | ||
console.log("listener called", event); | ||
if (event.hasChanged(89 /* ID of the readonly option */)) { | ||
console.log("running format"); | ||
const formatAction = editor.getAction("editor.action.formatDocument"); | ||
assertDefined(formatAction, `Format action is not defined`); | ||
formatAction.run().then(() => { | ||
listenerDisposer.dispose(); | ||
editor.updateOptions({ | ||
readOnly: isReadOnly, | ||
}); | ||
resizeEditorBasedOnContent(); | ||
resolve(); | ||
}); | ||
} | ||
}); | ||
console.log("disablin read only"); | ||
editor.updateOptions({ | ||
readOnly: false, | ||
}); | ||
}); | ||
}, | ||
}), | ||
[isReadOnly, editor, resizeEditorBasedOnContent], | ||
); | ||
|
||
useEffect(() => { | ||
// Triggered as the text can change without internal editing. (ie if the | ||
// controlled prop changes) | ||
resizeEditorBasedOnContent(); | ||
} | ||
}; | ||
}, [text, resizeEditorBasedOnContent]); | ||
|
||
// Triggering this on every render seems to keep the editor correctly sized | ||
// it is unclear why this is the case. | ||
resizeEditorBasedOnContent(); | ||
// Triggering this on every render seems to keep the editor correctly sized | ||
// it is unclear why this is the case. | ||
resizeEditorBasedOnContent(); | ||
|
||
return ( | ||
<Box width={"100%"}> | ||
<Editor | ||
onMount={handleMount} | ||
value={text} | ||
onChange={handleChange} | ||
options={{ | ||
automaticLayout: false, // if this is `true` a ResizeObserver is installed. This causes issues with us managing the container size outside. | ||
readOnly: isReadOnly, | ||
lineNumbers: showLineNumbers || (!isDefined(showLineNumbers) && !isReadOnly) ? "on" : "off", | ||
minimap: { enabled: false }, | ||
wordWrap: "on", | ||
wrappingStrategy: "advanced", | ||
scrollBeyondLastLine: false, | ||
renderLineHighlight: isReadOnly ? "none" : "line", | ||
selectionHighlight: !isReadOnly, | ||
occurrencesHighlight: !isReadOnly, | ||
overviewRulerLanes: isReadOnly ? 0 : 3, | ||
scrollbar: { | ||
alwaysConsumeMouseWheel: false, | ||
}, | ||
}} | ||
defaultLanguage={"json"} | ||
theme={"vs-dark"} | ||
/> | ||
</Box> | ||
); | ||
}; | ||
return ( | ||
<Box width={"100%"}> | ||
<Editor | ||
onMount={handleMount} | ||
value={text} | ||
path={fileName} | ||
onChange={handleChange} | ||
options={{ | ||
automaticLayout: false, // if this is `true` a ResizeObserver is installed. This causes issues with us managing the container size outside. | ||
readOnly: isReadOnly, | ||
lineNumbers: showLineNumbers || (!isDefined(showLineNumbers) && !isReadOnly) ? "on" : "off", | ||
minimap: { enabled: false }, | ||
wordWrap: "on", | ||
wrappingStrategy: "advanced", | ||
scrollBeyondLastLine: false, | ||
renderLineHighlight: isReadOnly ? "none" : "line", | ||
selectionHighlight: !isReadOnly, | ||
occurrencesHighlight: !isReadOnly, | ||
overviewRulerLanes: isReadOnly ? 0 : 3, | ||
scrollbar: { | ||
alwaysConsumeMouseWheel: false, | ||
}, | ||
}} | ||
defaultLanguage={!isDefined(fileName) ? "json" : undefined} | ||
theme={"vs-dark"} | ||
/> | ||
</Box> | ||
); | ||
}, | ||
); |
Oops, something went wrong.