Update clone URL + minor code refactoring (#60)

* FileEditor improvements

* Update clone URL + minor code refactoring
jobatzil/rename
Tan Nhu 2022-11-08 14:34:43 -08:00 committed by GitHub
parent 8fe80ab811
commit 073154eebb
12 changed files with 266 additions and 185 deletions

View File

@ -1,17 +1,17 @@
const path = require('path');
const path = require('path')
const webpack = require('webpack')
const {
container: { ModuleFederationPlugin },
DefinePlugin
} = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
} = require('webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')
const GenerateStringTypesPlugin = require('../scripts/webpack/GenerateStringTypesPlugin').GenerateStringTypesPlugin
const { RetryChunkLoadPlugin } = require('webpack-retry-chunk-load-plugin')
const moduleFederationConfig = require('./moduleFederation.config');
const CONTEXT = process.cwd();
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
const moduleFederationConfig = require('./moduleFederation.config')
const CONTEXT = process.cwd()
const DEV = process.env.NODE_ENV === 'development'
const ON_PREM = `${process.env.ON_PREM}` === 'true'
@ -150,8 +150,7 @@ module.exports = {
},
resolve: {
extensions: ['.mjs', '.js', '.ts', '.tsx', '.json', '.ttf', '.scss'],
plugins: [
new TsconfigPathsPlugin()]
plugins: [new TsconfigPathsPlugin()]
},
plugins: [
new ModuleFederationPlugin(moduleFederationConfig),
@ -164,5 +163,71 @@ module.exports = {
new RetryChunkLoadPlugin({
maxRetries: 2
}),
new MonacoWebpackPlugin({
// available options are documented at https://github.com/Microsoft/monaco-editor-webpack-plugin#options
languages: [
'abap',
'apex',
'azcli',
'bat',
'cameligo',
'clojure',
'coffee',
'cpp',
'csharp',
'csp',
'css',
'dockerfile',
'fsharp',
'go',
'graphql',
'handlebars',
'html',
'ini',
'java',
'javascript',
'json',
'kotlin',
'less',
'lua',
'markdown',
'mips',
'msdax',
'mysql',
'objective-c',
'pascal',
'pascaligo',
'perl',
'pgsql',
'php',
'postiats',
'powerquery',
'powershell',
'pug',
'python',
'r',
'razor',
'redis',
'redshift',
'restructuredtext',
'ruby',
'rust',
'sb',
'scheme',
'scss',
'shell',
'solidity',
'sophia',
'sql',
'st',
'swift',
'tcl',
'twig',
'typescript',
'vb',
'xml',
'yaml'
]
})
]
};
}

View File

@ -12,7 +12,6 @@ const {
WatchIgnorePlugin,
container: { ModuleFederationPlugin }
} = require('webpack')
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
const commonConfig = require('./webpack.common')
const baseUrl = process.env.BASE_URL ?? 'https://qa.harness.io/gateway'
@ -61,72 +60,6 @@ const devConfig = {
new DefinePlugin({
'process.env': '{}', // required for @blueprintjs/core
__DEV__: DEV
}),
new MonacoWebpackPlugin({
// available options are documented at https://github.com/Microsoft/monaco-editor-webpack-plugin#options
languages: [
'abap',
'apex',
'azcli',
'bat',
'cameligo',
'clojure',
'coffee',
'cpp',
'csharp',
'csp',
'css',
'dockerfile',
'fsharp',
'go',
'graphql',
'handlebars',
'html',
'ini',
'java',
'javascript',
'json',
'kotlin',
'less',
'lua',
'markdown',
'mips',
'msdax',
'mysql',
'objective-c',
'pascal',
'pascaligo',
'perl',
'pgsql',
'php',
'postiats',
'powerquery',
'powershell',
'pug',
'python',
'r',
'razor',
'redis',
'redshift',
'restructuredtext',
'ruby',
'rust',
'sb',
'scheme',
'scss',
'shell',
'solidity',
'sophia',
'sql',
'st',
'swift',
'tcl',
'twig',
'typescript',
'vb',
'xml',
'yaml'
]
})
// new ForkTsCheckerWebpackPlugin()
// new WatchIgnorePlugin({

View File

@ -0,0 +1,102 @@
import React, { useEffect } from 'react'
import { Container } from '@harness/uicore'
import type monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'
import MonacoEditor from 'react-monaco-editor'
import { noop } from 'lodash-es'
import type { SourceCodeEditorProps } from 'utils/Utils'
export const MonacoEditorOptions = {
ignoreTrimWhitespace: true,
minimap: { enabled: false },
codeLens: false,
scrollBeyondLastLine: false,
smartSelect: false,
tabSize: 2,
insertSpaces: true,
overviewRulerBorder: false,
automaticLayout: true
}
const diagnosticsOptions = {
noSemanticValidation: true,
noSyntaxValidation: true
}
const compilerOptions = {
jsx: 'react',
noLib: true,
allowNonTsExtensions: true
}
function autoAdjustEditorHeight(editor: monacoEditor.editor.IStandaloneCodeEditor) {
// Adjust editor height based on its content
// https://github.com/microsoft/monaco-editor/issues/794#issuecomment-427092969
const LINE_HEIGHT = 18
const CONTAINER_GUTTER = 10
const editorNode = editor.getDomNode() as HTMLElement
const codeContainer = editorNode.getElementsByClassName('view-lines')[0]
let prevLineCount = 0
const adjustHeight = (): void => {
const _height =
codeContainer.childElementCount > prevLineCount
? (codeContainer as HTMLElement).offsetHeight // unfold
: codeContainer.childElementCount * LINE_HEIGHT + CONTAINER_GUTTER // fold
prevLineCount = codeContainer.childElementCount
editorNode.style.height = Math.max(_height, 100) + 'px'
editor.layout()
}
setTimeout(adjustHeight, 0)
editor.onDidChangeModelDecorations(() => setTimeout(adjustHeight, 0))
}
const toOnOff = (flag: boolean) => (flag ? 'on' : 'off')
export default function MonacoSourceCodeEditor({
source,
language = 'plaintext',
lineNumbers = true,
readOnly = false,
className,
height,
autoHeight,
wordWrap = true,
onChange = noop
}: SourceCodeEditorProps) {
const scrollbar = autoHeight ? 'hidden' : 'auto'
useEffect(() => {
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions?.(diagnosticsOptions)
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions?.(diagnosticsOptions)
monaco.languages.typescript.typescriptDefaults.setCompilerOptions(compilerOptions)
}, [])
return (
<Container className={className}>
<MonacoEditor
language={language}
theme="vs-light"
value={source}
height={height}
options={{
...MonacoEditorOptions,
readOnly,
wordWrap: toOnOff(wordWrap),
lineNumbers: toOnOff(lineNumbers),
scrollbar: {
vertical: scrollbar,
horizontal: scrollbar,
alwaysConsumeMouseWheel: false
}
}}
editorDidMount={editor => {
if (autoHeight) {
autoAdjustEditorHeight(editor)
}
}}
onChange={onChange}
/>
</Container>
)
}

View File

@ -1,79 +1,17 @@
import React, { useRef } from 'react'
import { Container } from '@harness/uicore'
import MonacoEditor from 'react-monaco-editor'
import { MonacoEditorOptions } from 'utils/Utils'
import React, { lazy, Suspense } from 'react'
import { Text } from '@harness/uicore'
import type { SourceCodeEditorProps } from 'utils/Utils'
import { useStrings } from 'framework/strings'
export interface SourceCodeEditorProps {
source: string
language?: string
lineNumbers?: boolean
readOnly?: boolean
highlightLines?: string // i.e: {1,3-4}, TODO: not yet supported
className?: string
height?: number | string
autoHeight?: boolean
}
function Editor(props: SourceCodeEditorProps) {
const { getString } = useStrings()
const MonacoSourceCodeEditor = lazy(() => import('./MonacoSourceCodeEditor'))
function MonacoSourceCodeEditor({
source,
language = 'plaintext',
lineNumbers = true,
readOnly = false,
className,
height,
autoHeight
}: SourceCodeEditorProps) {
const inputContainerRef = useRef<HTMLDivElement>(null)
const scrollbar = autoHeight ? 'hidden' : 'auto'
// return <Container ref={inputContainerRef} className={className}> </Container>
return (
<Container ref={inputContainerRef} className={className}>
<MonacoEditor
language={language}
theme="vs-light"
value={source}
height={height}
options={{
...MonacoEditorOptions,
...(autoHeight ? {} : { scrollBeyondLastLine: false }),
automaticLayout: true,
readOnly,
wordWrap: 'on',
lineNumbers: lineNumbers ? 'on' : 'off',
scrollbar: {
vertical: scrollbar,
horizontal: scrollbar,
alwaysConsumeMouseWheel: false
}
}}
editorDidMount={editor => {
if (autoHeight) {
// Aadjust editor height based on content
// https://github.com/microsoft/monaco-editor/issues/794#issuecomment-427092969
const LINE_HEIGHT = 18
const CONTAINER_GUTTER = 10
const editorNode = editor.getDomNode() as HTMLElement
const codeContainer = editorNode.getElementsByClassName('view-lines')[0]
let prevLineCount = 0
const adjustHeight = (): void => {
const _height =
codeContainer.childElementCount > prevLineCount
? (codeContainer as HTMLElement).offsetHeight // unfold
: codeContainer.childElementCount * LINE_HEIGHT + CONTAINER_GUTTER // fold
prevLineCount = codeContainer.childElementCount
editorNode.style.height = Math.max(_height, 100) + 'px'
editor.layout()
}
setTimeout(adjustHeight, 0)
editor.onDidChangeModelDecorations(() => setTimeout(adjustHeight, 0))
}
}}
/>
</Container>
<Suspense fallback={<Text>{getString('loading')}</Text>}>
<MonacoSourceCodeEditor {...props} />
</Suspense>
)
}
export const SourceCodeEditor = React.memo(MonacoSourceCodeEditor)
export const SourceCodeEditor = React.memo(Editor)

View File

@ -36,9 +36,12 @@ export interface StringsMap {
failedToCreateRepo: string
files: string
history: string
identifier: string
in: string
inactiveBranches: string
loading: string
name: string
nameYourFile: string
newFile: string
newFolder: string
newRepo: string

View File

@ -32,6 +32,7 @@ create: Create
clone: Clone
copy: Copy
defaultBranch: Default
in: in
ok: OK
loading: Loading...
addGitIgnore: Add a .gitignore
@ -51,6 +52,7 @@ createBranch: + Create Branch
searchBranches: Search branches
updated: Updated
cloneHTTPS: Clone with HTTPS
nameYourFile: Name your file...
repos:
name: Repo Name
data: Repo Data

View File

@ -129,9 +129,7 @@ export function ContentHeader({ repoMetadata, gitRef, resourcePath = '' }: Conte
variation={ButtonVariation.SECONDARY}
icon="main-clone"
iconProps={{ size: 10 }}
tooltip={
<CloneButtonTooltip httpsURL={'https://localhost:8181/kmpySmUISimoRrJL6NL73w/default/scm1/policy-mgmt'} />
}
tooltip={<CloneButtonTooltip httpsURL={`http://localhost:3000/${repoMetadata.path}.git`} />}
tooltipProps={{
interactionKind: 'click',
minimal: true,

View File

@ -7,13 +7,28 @@
overflow: hidden;
.heading {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
// border-top-left-radius: 4px;
// border-top-right-radius: 4px;
align-items: center;
padding: 0 var(--spacing-xlarge) !important;
height: 52px;
background-color: var(--grey-100);
box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.08), 0px 0.5px 2px rgba(96, 97, 112, 0.16);
border-bottom: 1px solid var(--grey-200);
.path {
align-items: center;
.inputContainer {
margin-left: var(--spacing-small) !important;
margin-bottom: 0;
input {
padding: var(--spacing-xsmall) var(--spacing-small);
height: 28px;
}
}
}
}
.content {
@ -21,8 +36,7 @@
overflow: hidden;
.editorContainer {
height: calc(100vh - 95px - 52px);
background-color: red;
height: calc(100vh - 96px - 52px);
overflow: hidden;
}
}

View File

@ -3,6 +3,8 @@
declare const styles: {
readonly container: string
readonly heading: string
readonly path: string
readonly inputContainer: string
readonly content: string
readonly editorContainer: string
}

View File

@ -8,10 +8,12 @@ import {
FlexExpander,
Icon,
Layout,
Text
Text,
TextInput
} from '@harness/uicore'
import { Link } from 'react-router-dom'
import ReactJoin from 'react-join'
import cx from 'classnames'
import { SourceCodeEditor } from 'components/SourceCodeEditor/SourceCodeEditor'
import type { OpenapiGetContentOutput, RepoFileContent, TypesRepository } from 'services/scm'
import { useAppContext } from 'AppContext'
@ -22,7 +24,7 @@ import css from './FileEditor.module.scss'
interface FileEditorProps {
repoMetadata: TypesRepository
gitRef?: string
gitRef: string
resourcePath?: string
contentInfo: OpenapiGetContentOutput
}
@ -30,12 +32,13 @@ interface FileEditorProps {
export function FileEditor({ contentInfo, repoMetadata, gitRef, resourcePath = '' }: FileEditorProps) {
const { getString } = useStrings()
const { routes } = useAppContext()
const language = filenameToLanguage(contentInfo?.name)
return (
<Container className={css.container}>
<Layout.Horizontal className={css.heading}>
<Container>
<Layout.Horizontal spacing="small">
<Layout.Horizontal spacing="small" className={css.path}>
<Link to={routes.toSCMRepository({ repoPath: repoMetadata.path as string, gitRef })}>
<Icon name="main-folder" />
</Link>
@ -44,7 +47,7 @@ export function FileEditor({ contentInfo, repoMetadata, gitRef, resourcePath = '
{resourcePath.split('/').map((_path, index, paths) => {
const pathAtIndex = paths.slice(0, index + 1).join('/')
return (
return index < paths.length - 1 ? (
<Link
key={_path + index}
to={routes.toSCMRepository({
@ -54,9 +57,25 @@ export function FileEditor({ contentInfo, repoMetadata, gitRef, resourcePath = '
})}>
<Text color={Color.GREY_900}>{_path}</Text>
</Link>
) : (
<TextInput
key={_path + index}
autoFocus
value={_path || ''}
wrapperClassName={css.inputContainer}
placeholder={getString('nameYourFile')}
/>
)
})}
</ReactJoin>
<Text color={Color.GREY_900}>{getString('in')}</Text>
<Link
to={routes.toSCMRepository({
repoPath: repoMetadata.path as string,
gitRef
})}>
{gitRef}
</Link>
</Layout.Horizontal>
</Container>
<FlexExpander />
@ -69,16 +88,14 @@ export function FileEditor({ contentInfo, repoMetadata, gitRef, resourcePath = '
/>
</Layout.Horizontal>
{(contentInfo?.content as RepoFileContent)?.data && (
<Container className={css.content}>
<SourceCodeEditor
className={css.editorContainer}
height="100%"
language={filenameToLanguage(contentInfo?.name)}
source={window.atob((contentInfo?.content as RepoFileContent)?.data || '')}
/>
</Container>
)}
<Container className={cx(css.content, language)}>
<SourceCodeEditor
className={css.editorContainer}
height="100%"
language={language}
source={window.atob((contentInfo?.content as RepoFileContent)?.data || '')}
/>
</Container>
</Container>
)
}

View File

@ -1,7 +1,6 @@
import React from 'react'
import { Container } from '@harness/uicore'
import type { TypesRepository } from 'services/scm'
import { isFile } from 'utils/GitUtils'
import { useGetResourceContent } from 'hooks/useGetResourceContent'
import { FileEditor } from '../FileEditor/FileEditor'
import css from './RepositoryFileEditContent.module.scss'
@ -19,10 +18,10 @@ export function RepositoryFileEditContent({ repoMetadata, gitRef, resourcePath }
return (
<Container className={css.resourceContent}>
{data && isFile(data) && (
{data && (
<FileEditor
repoMetadata={repoMetadata}
gitRef={gitRef || repoMetadata.defaultBranch}
gitRef={gitRef || (repoMetadata.defaultBranch as string)}
resourcePath={resourcePath}
contentInfo={data}
/>

View File

@ -26,15 +26,17 @@ export function showToaster(message: string, props?: Partial<IToastProps>): IToa
export const getErrorMessage = (error: Unknown): string =>
get(error, 'data.error', get(error, 'data.message', error?.message))
export const MonacoEditorOptions = {
ignoreTrimWhitespace: true,
minimap: { enabled: false },
codeLens: false,
scrollBeyondLastLine: false,
smartSelect: false,
tabSize: 2,
insertSpaces: true,
overviewRulerBorder: false
export interface SourceCodeEditorProps {
source: string
language?: string
lineNumbers?: boolean
readOnly?: boolean
highlightLines?: string // i.e: {1,3-4}, TODO: not yet supported
className?: string
height?: number | string
autoHeight?: boolean
wordWrap?: boolean
onChange?: (value: string) => void
}
// Monaco editor has a bug where when its value is set, the value
@ -157,10 +159,16 @@ const MONACO_SUPPORTED_LANGUAGES = [
'yaml'
]
const EXTENSION_TO_LANG: Record<string, string> = {
tsx: 'typescript',
jsx: 'typescript'
}
export const filenameToLanguage = (name?: string): string | undefined => {
const map = langMap.languages(name?.split('.').pop() || '')
const extension = name?.split('.').pop() || ''
const map = langMap.languages(extension)
if (map?.length) {
return MONACO_SUPPORTED_LANGUAGES.find(lang => map.includes(lang))
return MONACO_SUPPORTED_LANGUAGES.find(lang => map.includes(lang)) || EXTENSION_TO_LANG[extension]
}
}