mirror of
https://github.com/harness/drone.git
synced 2025-05-17 01:20:13 +08:00
Implememnt diff view in editing file
This commit is contained in:
parent
615ac704d6
commit
821d79d53c
@ -23,12 +23,7 @@ import { useEventListener } from 'hooks/useEventListener'
|
|||||||
import { UserPreference, useUserPreference } from 'hooks/useUserPreference'
|
import { UserPreference, useUserPreference } from 'hooks/useUserPreference'
|
||||||
import { PipeSeparator } from 'components/PipeSeparator/PipeSeparator'
|
import { PipeSeparator } from 'components/PipeSeparator/PipeSeparator'
|
||||||
import type { DiffFileEntry } from 'utils/types'
|
import type { DiffFileEntry } from 'utils/types'
|
||||||
import {
|
import { DIFF2HTML_CONFIG, PullRequestCodeCommentPayload, ViewStyle } from 'components/DiffViewer/DiffViewerUtils'
|
||||||
DIFF2HTML_CONFIG,
|
|
||||||
PR_CODE_COMMENT_PAYLOAD_VERSION,
|
|
||||||
PullRequestCodeCommentPayload,
|
|
||||||
ViewStyle
|
|
||||||
} from 'components/DiffViewer/DiffViewerUtils'
|
|
||||||
import { NoResultCard } from 'components/NoResultCard/NoResultCard'
|
import { NoResultCard } from 'components/NoResultCard/NoResultCard'
|
||||||
import type { TypesPullReq, TypesPullReqActivity } from 'services/code'
|
import type { TypesPullReq, TypesPullReqActivity } from 'services/code'
|
||||||
import { useShowRequestError } from 'hooks/useShowRequestError'
|
import { useShowRequestError } from 'hooks/useShowRequestError'
|
||||||
@ -118,14 +113,10 @@ export const Changes: React.FC<ChangesProps> = ({
|
|||||||
const fileId = changedFileId([diff.oldName, diff.newName])
|
const fileId = changedFileId([diff.oldName, diff.newName])
|
||||||
const containerId = `container-${fileId}`
|
const containerId = `container-${fileId}`
|
||||||
const contentId = `content-${fileId}`
|
const contentId = `content-${fileId}`
|
||||||
const fileTitle = diff.isDeleted
|
const filePath = diff.isDeleted ? diff.oldName : diff.newName
|
||||||
? diff.oldName
|
|
||||||
: diff.isRename
|
|
||||||
? `${diff.oldName} -> ${diff.newName}`
|
|
||||||
: diff.newName
|
|
||||||
const fileActivities: TypesPullReqActivity[] | undefined = activities?.filter(activity => {
|
const fileActivities: TypesPullReqActivity[] | undefined = activities?.filter(activity => {
|
||||||
const payload = activity.payload as PullRequestCodeCommentPayload
|
const payload = activity.payload as PullRequestCodeCommentPayload
|
||||||
return payload?.file_id === fileId && payload?.version === PR_CODE_COMMENT_PAYLOAD_VERSION
|
return payload?.file_id === fileId
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -133,7 +124,7 @@ export const Changes: React.FC<ChangesProps> = ({
|
|||||||
containerId,
|
containerId,
|
||||||
contentId,
|
contentId,
|
||||||
fileId,
|
fileId,
|
||||||
fileTitle,
|
filePath,
|
||||||
fileActivities: fileActivities || [],
|
fileActivities: fileActivities || [],
|
||||||
activities: activities || []
|
activities: activities || []
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import type { DiffFileEntry } from 'utils/types'
|
|||||||
import { useConfirmAct } from 'hooks/useConfirmAction'
|
import { useConfirmAct } from 'hooks/useConfirmAction'
|
||||||
import { PipeSeparator } from 'components/PipeSeparator/PipeSeparator'
|
import { PipeSeparator } from 'components/PipeSeparator/PipeSeparator'
|
||||||
import { useAppContext } from 'AppContext'
|
import { useAppContext } from 'AppContext'
|
||||||
import type { TypesPullReq, TypesPullReqActivity } from 'services/code'
|
import type { OpenapiCommentCreatePullReqRequest, TypesPullReq, TypesPullReqActivity } from 'services/code'
|
||||||
import { getErrorMessage } from 'utils/Utils'
|
import { getErrorMessage } from 'utils/Utils'
|
||||||
import { CopyButton } from 'components/CopyButton/CopyButton'
|
import { CopyButton } from 'components/CopyButton/CopyButton'
|
||||||
import { AppWrapper } from 'App'
|
import { AppWrapper } from 'App'
|
||||||
@ -36,9 +36,6 @@ import {
|
|||||||
DiffCommentItem,
|
DiffCommentItem,
|
||||||
DIFF_VIEWER_HEADER_HEIGHT,
|
DIFF_VIEWER_HEADER_HEIGHT,
|
||||||
getCommentLineInfo,
|
getCommentLineInfo,
|
||||||
getDiffHTMLSnapshotFromRow,
|
|
||||||
getRawTextInRange,
|
|
||||||
PR_CODE_COMMENT_PAYLOAD_VERSION,
|
|
||||||
PullRequestCodeCommentPayload,
|
PullRequestCodeCommentPayload,
|
||||||
renderCommentOppositePlaceHolder,
|
renderCommentOppositePlaceHolder,
|
||||||
ViewStyle
|
ViewStyle
|
||||||
@ -308,22 +305,33 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
|||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case CommentAction.NEW: {
|
case CommentAction.NEW: {
|
||||||
// lineNumberRange can be used to allow multiple-line selection when commenting in the future
|
const commentPayload: OpenapiCommentCreatePullReqRequest = {
|
||||||
const lineNumberRange = [comment.lineNumber]
|
line_start: comment.lineNumber,
|
||||||
const payload: PullRequestCodeCommentPayload = {
|
line_end: comment.lineNumber,
|
||||||
type: CommentType.CODE_COMMENT,
|
line_start_new: !comment.left,
|
||||||
version: PR_CODE_COMMENT_PAYLOAD_VERSION,
|
line_end_new: !comment.left,
|
||||||
file_id: diff.fileId,
|
path: diff.filePath,
|
||||||
file_title: diff.fileTitle,
|
source_commit_sha: pullRequestMetadata?.merge_sha || '',
|
||||||
language: diff.language || '',
|
target_commit_sha: pullRequestMetadata?.merge_target_sha || '',
|
||||||
is_on_left: comment.left,
|
text: value
|
||||||
at_line_number: comment.lineNumber,
|
|
||||||
line_number_range: lineNumberRange,
|
|
||||||
range_text_content: getRawTextInRange(diff, lineNumberRange),
|
|
||||||
diff_html_snapshot: getDiffHTMLSnapshotFromRow(rowElement)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveComment({ type: CommentType.CODE_COMMENT, text: value, payload })
|
// lineNumberRange can be used to allow multiple-line selection when commenting in the future
|
||||||
|
// const lineNumberRange = [comment.lineNumber]
|
||||||
|
// const payload: PullRequestCodeCommentPayload = {
|
||||||
|
// type: CommentType.CODE_COMMENT,
|
||||||
|
// version: PR_CODE_COMMENT_PAYLOAD_VERSION,
|
||||||
|
// file_id: diff.fileId,
|
||||||
|
// file_title: diff.filePath,
|
||||||
|
// language: diff.language || '',
|
||||||
|
// is_on_left: comment.left,
|
||||||
|
// at_line_number: comment.lineNumber,
|
||||||
|
// line_number_range: lineNumberRange,
|
||||||
|
// range_text_content: getRawTextInRange(diff, lineNumberRange),
|
||||||
|
// diff_html_snapshot: getDiffHTMLSnapshotFromRow(rowElement)
|
||||||
|
// }
|
||||||
|
|
||||||
|
await saveComment({ type: CommentType.CODE_COMMENT, ...commentPayload })
|
||||||
.then((newComment: TypesPullReqActivity) => {
|
.then((newComment: TypesPullReqActivity) => {
|
||||||
updatedItem = activityToCommentItem(newComment)
|
updatedItem = activityToCommentItem(newComment)
|
||||||
})
|
})
|
||||||
@ -472,12 +480,12 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({
|
|||||||
to={routes.toCODERepository({
|
to={routes.toCODERepository({
|
||||||
repoPath: repoMetadata.path as string,
|
repoPath: repoMetadata.path as string,
|
||||||
gitRef: pullRequestMetadata?.source_branch,
|
gitRef: pullRequestMetadata?.source_branch,
|
||||||
resourcePath: diff.fileTitle
|
resourcePath: diff.isRename ? diff.newName : diff.filePath
|
||||||
})}>
|
})}>
|
||||||
{diff.fileTitle}
|
{diff.isRename ? `${diff.oldName} -> ${diff.newName}` : diff.filePath}
|
||||||
</Link>
|
</Link>
|
||||||
</Text>
|
</Text>
|
||||||
<CopyButton content={diff.fileTitle} icon={CodeIcon.Copy} size={ButtonSize.SMALL} />
|
<CopyButton content={diff.filePath} icon={CodeIcon.Copy} size={ButtonSize.SMALL} />
|
||||||
<FlexExpander />
|
<FlexExpander />
|
||||||
|
|
||||||
<Render when={!readOnly}>
|
<Render when={!readOnly}>
|
||||||
|
@ -21,8 +21,6 @@ export enum CommentType {
|
|||||||
STATE_CHANGE = 'state-change'
|
STATE_CHANGE = 'state-change'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PR_CODE_COMMENT_PAYLOAD_VERSION = '0.1'
|
|
||||||
|
|
||||||
export interface PullRequestCodeCommentPayload {
|
export interface PullRequestCodeCommentPayload {
|
||||||
type: CommentType
|
type: CommentType
|
||||||
version: string // used to avoid rendering old payload structure
|
version: string // used to avoid rendering old payload structure
|
||||||
@ -200,78 +198,6 @@ export const activityToCommentItem = (activity: TypesPullReqActivity): CommentIt
|
|||||||
payload: activity
|
payload: activity
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* Take a small HTML snapshot of a diff in order to render code comment.
|
|
||||||
* @param atRow Row element where the comment is placed.
|
|
||||||
* @param maxNumberOfLines Maximum number of lines to take.
|
|
||||||
* @returns HTML content of the diff.
|
|
||||||
*/
|
|
||||||
export function getDiffHTMLSnapshotFromRow(atRow: HTMLTableRowElement, maxNumberOfLines = 5) {
|
|
||||||
let linesCapturedCount = 0
|
|
||||||
const diffSnapshot = [atRow.outerHTML.trim()]
|
|
||||||
|
|
||||||
let prev = atRow.previousElementSibling
|
|
||||||
|
|
||||||
while (prev && linesCapturedCount < maxNumberOfLines) {
|
|
||||||
if (!prev.hasAttribute('data-annotated-line') && !prev.hasAttribute('data-place-holder-for-line')) {
|
|
||||||
// Don't count empty lines
|
|
||||||
const textContent = prev.textContent?.replace(/\s/g, '')
|
|
||||||
if (textContent?.length && textContent !== '+') {
|
|
||||||
linesCapturedCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
if (textContent !== '+') {
|
|
||||||
diffSnapshot.unshift((prev.outerHTML || '').trim())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prev = prev.previousElementSibling
|
|
||||||
}
|
|
||||||
|
|
||||||
return diffSnapshot.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
// export function getDiffHTMLSnapshotFromDiff(diff: DiffFileEntry, lineNumberRange: number[], isOnLeft: boolean) {
|
|
||||||
// const lines = diff?.blocks.reduce((group, item) => {
|
|
||||||
// group = group.concat(item.lines)
|
|
||||||
// return group
|
|
||||||
// }, [] as DiffLine[])
|
|
||||||
|
|
||||||
// const lastIndex = lines.findIndex(line =>
|
|
||||||
// lineNumberRange.includes((isOnLeft ? line.oldNumber : line.newNumber) as number)
|
|
||||||
// )
|
|
||||||
// const startIndex = Math.max(0, lastIndex - 5)
|
|
||||||
|
|
||||||
// const copiedLines = lines.slice(startIndex, lastIndex)
|
|
||||||
// const copiedDiff: DiffFileEntry = {
|
|
||||||
// ...diff,
|
|
||||||
// blocks: [
|
|
||||||
// {
|
|
||||||
// header: '',
|
|
||||||
// lines: copiedLines,
|
|
||||||
// newStartLine: copiedLines[0].newNumber as number,
|
|
||||||
// oldStartLine: copiedLines[0].oldNumber as number
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // console.log({ isOnLeft, startIndex, lastIndex, copiedLines, lines, lineNumberRange })
|
|
||||||
|
|
||||||
// const div = document.createElement('div')
|
|
||||||
// new Diff2HtmlUI(div, [copiedDiff], Object.assign({}, DIFF2HTML_CONFIG, { outputFormat: 'line-by-line' })).draw()
|
|
||||||
|
|
||||||
// return div.querySelector('table')?.outerHTML || ''
|
|
||||||
// }
|
|
||||||
|
|
||||||
export function getRawTextInRange(diff: DiffFileEntry, lineNumberRange: number[]) {
|
|
||||||
return (
|
|
||||||
// TODO: This is wrong, blocks can have multiple items, not one
|
|
||||||
(diff?.blocks[0]?.lines || [])
|
|
||||||
.filter(line => lineNumberRange.includes(line.newNumber as number))
|
|
||||||
.map(line => line.content)
|
|
||||||
.join('\n') || ''
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function activitiesToDiffCommentItems(diff: DiffFileEntry): DiffCommentItem<TypesPullReqActivity>[] {
|
export function activitiesToDiffCommentItems(diff: DiffFileEntry): DiffCommentItem<TypesPullReqActivity>[] {
|
||||||
return (
|
return (
|
||||||
diff.fileActivities?.map(activity => {
|
diff.fileActivities?.map(activity => {
|
||||||
|
@ -63,7 +63,8 @@ export const OptionsMenuButton = ({
|
|||||||
item as IMenuItemProps & React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
item as IMenuItemProps & React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
||||||
'isDanger',
|
'isDanger',
|
||||||
'hasIcon',
|
'hasIcon',
|
||||||
'iconName'
|
'iconName',
|
||||||
|
'iconSize'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React, { useEffect } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Container } from '@harness/uicore'
|
|
||||||
import type monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'
|
import type monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'
|
||||||
import MonacoEditor from 'react-monaco-editor'
|
import MonacoEditor, { MonacoDiffEditor } from 'react-monaco-editor'
|
||||||
import { noop } from 'lodash-es'
|
import { noop } from 'lodash-es'
|
||||||
import { SourceCodeEditorProps, PLAIN_TEXT } from 'utils/Utils'
|
import { SourceCodeEditorProps, PLAIN_TEXT } from 'utils/Utils'
|
||||||
|
import { useEventListener } from 'hooks/useEventListener'
|
||||||
|
|
||||||
export const MonacoEditorOptions = {
|
export const MonacoEditorOptions = {
|
||||||
ignoreTrimWhitespace: true,
|
ignoreTrimWhitespace: true,
|
||||||
@ -58,12 +58,12 @@ export default function MonacoSourceCodeEditor({
|
|||||||
language = PLAIN_TEXT,
|
language = PLAIN_TEXT,
|
||||||
lineNumbers = true,
|
lineNumbers = true,
|
||||||
readOnly = false,
|
readOnly = false,
|
||||||
className,
|
|
||||||
height,
|
height,
|
||||||
autoHeight,
|
autoHeight,
|
||||||
wordWrap = true,
|
wordWrap = true,
|
||||||
onChange = noop
|
onChange = noop
|
||||||
}: SourceCodeEditorProps) {
|
}: SourceCodeEditorProps) {
|
||||||
|
const [editor, setEditor] = useState<monacoEditor.editor.IStandaloneCodeEditor>()
|
||||||
const scrollbar = autoHeight ? 'hidden' : 'auto'
|
const scrollbar = autoHeight ? 'hidden' : 'auto'
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -72,31 +72,80 @@ export default function MonacoSourceCodeEditor({
|
|||||||
monaco.languages.typescript?.typescriptDefaults?.setCompilerOptions?.(compilerOptions)
|
monaco.languages.typescript?.typescriptDefaults?.setCompilerOptions?.(compilerOptions)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEventListener('resize', () => {
|
||||||
|
editor?.layout({ width: 0, height: 0 })
|
||||||
|
window.requestAnimationFrame(() => editor?.layout())
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className={className}>
|
<MonacoEditor
|
||||||
<MonacoEditor
|
language={language}
|
||||||
language={language}
|
theme="vs-light"
|
||||||
theme="vs-light"
|
value={source}
|
||||||
value={source}
|
height={height}
|
||||||
height={height}
|
options={{
|
||||||
options={{
|
...MonacoEditorOptions,
|
||||||
...MonacoEditorOptions,
|
readOnly,
|
||||||
readOnly,
|
wordWrap: toOnOff(wordWrap),
|
||||||
wordWrap: toOnOff(wordWrap),
|
lineNumbers: toOnOff(lineNumbers),
|
||||||
lineNumbers: toOnOff(lineNumbers),
|
scrollbar: {
|
||||||
scrollbar: {
|
vertical: scrollbar,
|
||||||
vertical: scrollbar,
|
horizontal: scrollbar,
|
||||||
horizontal: scrollbar,
|
alwaysConsumeMouseWheel: false
|
||||||
alwaysConsumeMouseWheel: false
|
}
|
||||||
}
|
}}
|
||||||
}}
|
editorDidMount={_editor => {
|
||||||
editorDidMount={_editor => {
|
if (autoHeight) {
|
||||||
if (autoHeight) {
|
autoAdjustEditorHeight(_editor)
|
||||||
autoAdjustEditorHeight(_editor)
|
}
|
||||||
}
|
setEditor(_editor)
|
||||||
}}
|
}}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
</Container>
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DiffEditorProps extends Omit<SourceCodeEditorProps, 'autoHeight'> {
|
||||||
|
original: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiffEditor({
|
||||||
|
source,
|
||||||
|
original,
|
||||||
|
language = PLAIN_TEXT,
|
||||||
|
lineNumbers = true,
|
||||||
|
readOnly = false,
|
||||||
|
height,
|
||||||
|
wordWrap = true,
|
||||||
|
onChange = noop
|
||||||
|
}: DiffEditorProps) {
|
||||||
|
const [editor, setEditor] = useState<monacoEditor.editor.IStandaloneDiffEditor>()
|
||||||
|
|
||||||
|
useEventListener('resize', () => {
|
||||||
|
editor?.layout({ width: 0, height: 0 })
|
||||||
|
window.requestAnimationFrame(() => editor?.layout())
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MonacoDiffEditor
|
||||||
|
language={language}
|
||||||
|
theme="vs-light"
|
||||||
|
original={original}
|
||||||
|
value={source}
|
||||||
|
height={height}
|
||||||
|
options={{
|
||||||
|
...MonacoEditorOptions,
|
||||||
|
readOnly,
|
||||||
|
wordWrap: toOnOff(wordWrap),
|
||||||
|
lineNumbers: toOnOff(lineNumbers),
|
||||||
|
scrollbar: {
|
||||||
|
vertical: 'auto',
|
||||||
|
horizontal: 'auto',
|
||||||
|
alwaysConsumeMouseWheel: false
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
editorDidMount={setEditor}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ export interface StringsMap {
|
|||||||
branches: string
|
branches: string
|
||||||
browse: string
|
browse: string
|
||||||
cancel: string
|
cancel: string
|
||||||
|
changes: string
|
||||||
checkRuns: string
|
checkRuns: string
|
||||||
checkSuites: string
|
checkSuites: string
|
||||||
checks: string
|
checks: string
|
||||||
@ -62,6 +63,7 @@ export interface StringsMap {
|
|||||||
confirmDeleteWebhook: string
|
confirmDeleteWebhook: string
|
||||||
confirmation: string
|
confirmation: string
|
||||||
content: string
|
content: string
|
||||||
|
contents: string
|
||||||
conversation: string
|
conversation: string
|
||||||
copy: string
|
copy: string
|
||||||
copyCommitSHA: string
|
copyCommitSHA: string
|
||||||
|
@ -192,7 +192,7 @@ pr:
|
|||||||
failedToDeleteComment: Failed to delete comment. Please try again.
|
failedToDeleteComment: Failed to delete comment. Please try again.
|
||||||
prMerged: This Pull Request was merged
|
prMerged: This Pull Request was merged
|
||||||
reviewSubmitted: Review submitted.
|
reviewSubmitted: Review submitted.
|
||||||
prReviewSubmit: '{user} {state|approved:approved, rejected:rejected,changereq:requested changes to, reviewed} the pull request. {time}'
|
prReviewSubmit: '{user} {state|approved:approved, rejected:rejected,changereq:requested to, reviewed} the pull request. {time}'
|
||||||
prMergedInfo: '{user} merged branch {source} into {target} {time}.'
|
prMergedInfo: '{user} merged branch {source} into {target} {time}.'
|
||||||
prBranchPushInfo: '{user} pushed a new commit {commit}.'
|
prBranchPushInfo: '{user} pushed a new commit {commit}.'
|
||||||
prStateChanged: '{user} changed pull request state from {old} to {new}.'
|
prStateChanged: '{user} changed pull request state from {old} to {new}.'
|
||||||
@ -375,3 +375,5 @@ manageCredText: You can also manage your git credential {URL}
|
|||||||
blame: Blame
|
blame: Blame
|
||||||
viewRaw: View Raw
|
viewRaw: View Raw
|
||||||
download: Download
|
download: Download
|
||||||
|
changes: Changes
|
||||||
|
contents: Contents
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
.container {
|
.container {
|
||||||
|
--header-height: 96px;
|
||||||
|
--heading-height: 58px;
|
||||||
|
--tabs-height: 36px;
|
||||||
|
|
||||||
background-color: var(--white) !important;
|
background-color: var(--white) !important;
|
||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
// box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.08), 0px 0.5px 2px rgba(96, 97, 112, 0.16);
|
// box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.08), 0px 0.5px 2px rgba(96, 97, 112, 0.16);
|
||||||
// border-radius: 4px;
|
// border-radius: 4px;
|
||||||
height: calc(100vh - 96px);
|
height: calc(100vh - var(--header-height));
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.heading {
|
.heading {
|
||||||
@ -11,7 +15,7 @@
|
|||||||
// border-top-right-radius: 4px;
|
// border-top-right-radius: 4px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 var(--spacing-xlarge) !important;
|
padding: 0 var(--spacing-xlarge) !important;
|
||||||
height: 58px;
|
height: var(--heading-height);
|
||||||
background-color: var(--grey-100);
|
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);
|
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);
|
border-bottom: 1px solid var(--grey-200);
|
||||||
@ -40,13 +44,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.tabs {
|
||||||
|
height: var(--tabs-height);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--grey-50);
|
||||||
|
--tab-height: 18px;
|
||||||
|
|
||||||
|
.selectedView {
|
||||||
|
height: var(--tab-height);
|
||||||
|
|
||||||
|
> div {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
height: var(--tab-height) !important;
|
||||||
|
width: auto;
|
||||||
|
padding: 0 var(--spacing-large);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
&[class*='selected'] {
|
||||||
|
background-color: var(--primary-9);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not([class*='selected']) {
|
||||||
|
color: var(--primary-9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.editorContainer {
|
||||||
padding-left: var(--spacing-medium) !important;
|
padding-left: var(--spacing-medium) !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.editorContainer {
|
height: calc(100vh - var(--header-height) - var(--heading-height) - var(--tabs-height));
|
||||||
height: calc(100vh - 96px - 58px);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@ declare const styles: {
|
|||||||
readonly path: string
|
readonly path: string
|
||||||
readonly inputContainer: string
|
readonly inputContainer: string
|
||||||
readonly refLink: string
|
readonly refLink: string
|
||||||
readonly content: string
|
readonly tabs: string
|
||||||
|
readonly selectedView: string
|
||||||
readonly editorContainer: string
|
readonly editorContainer: string
|
||||||
}
|
}
|
||||||
export default styles
|
export default styles
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
import React, { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import React, { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Button, ButtonVariation, Color, Container, FlexExpander, Icon, Layout, Text, TextInput } from '@harness/uicore'
|
import {
|
||||||
|
Button,
|
||||||
|
ButtonVariation,
|
||||||
|
Color,
|
||||||
|
Container,
|
||||||
|
FlexExpander,
|
||||||
|
Icon,
|
||||||
|
Layout,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
VisualYamlSelectedView,
|
||||||
|
VisualYamlToggle
|
||||||
|
} from '@harness/uicore'
|
||||||
import { Link, useHistory } from 'react-router-dom'
|
import { Link, useHistory } from 'react-router-dom'
|
||||||
import ReactJoin from 'react-join'
|
import ReactJoin from 'react-join'
|
||||||
import cx from 'classnames'
|
import cx from 'classnames'
|
||||||
@ -11,6 +23,7 @@ import { useStrings } from 'framework/strings'
|
|||||||
import { filenameToLanguage, FILE_SEPERATOR } from 'utils/Utils'
|
import { filenameToLanguage, FILE_SEPERATOR } from 'utils/Utils'
|
||||||
import { useGetResourceContent } from 'hooks/useGetResourceContent'
|
import { useGetResourceContent } from 'hooks/useGetResourceContent'
|
||||||
import { CommitModalButton } from 'components/CommitModalButton/CommitModalButton'
|
import { CommitModalButton } from 'components/CommitModalButton/CommitModalButton'
|
||||||
|
import { DiffEditor } from 'components/SourceCodeEditor/MonacoSourceCodeEditor'
|
||||||
import css from './FileEditor.module.scss'
|
import css from './FileEditor.module.scss'
|
||||||
|
|
||||||
interface EditorProps extends Pick<GitInfoProps, 'repoMetadata' | 'gitRef' | 'resourcePath'> {
|
interface EditorProps extends Pick<GitInfoProps, 'repoMetadata' | 'gitRef' | 'resourcePath'> {
|
||||||
@ -83,6 +96,7 @@ function Editor({ resourceContent, repoMetadata, gitRef, resourcePath, isReposit
|
|||||||
// Make API call to verify if fileResourcePath is an existing folder
|
// Make API call to verify if fileResourcePath is an existing folder
|
||||||
verifyFolder().then(() => setStartVerifyFolder(true))
|
verifyFolder().then(() => setStartVerifyFolder(true))
|
||||||
}, [fileName, parentPath, language, content, verifyFolder])
|
}, [fileName, parentPath, language, content, verifyFolder])
|
||||||
|
const [selectedView, setSelectedView] = useState(VisualYamlSelectedView.VISUAL)
|
||||||
|
|
||||||
// Calculate file name input field width based on number of characters inside
|
// Calculate file name input field width based on number of characters inside
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -111,16 +125,14 @@ function Editor({ resourceContent, repoMetadata, gitRef, resourcePath, isReposit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [isNew, name])
|
}, [isNew, name])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className={css.container}>
|
<Container className={css.container}>
|
||||||
<Layout.Horizontal className={css.heading}>
|
<Layout.Horizontal className={css.heading}>
|
||||||
<Container>
|
<Container>
|
||||||
<Layout.Horizontal spacing="small" className={css.path}>
|
<Layout.Horizontal spacing="small" className={css.path}>
|
||||||
<Link to={routes.toCODERepository({ repoPath: repoMetadata.path as string, gitRef })}>
|
<Link to={routes.toCODERepository({ repoPath: repoMetadata.path as string, gitRef })}>
|
||||||
<Icon name="main-folder" padding={{ right: 'xsmall' }} />
|
<Icon name="code-folder" padding={{ right: 'xsmall' }} />
|
||||||
{/* <Text color={Color.GREY_900} inline>
|
|
||||||
{repoMetadata.uid}
|
|
||||||
</Text> */}
|
|
||||||
</Link>
|
</Link>
|
||||||
<PathSeparator />
|
<PathSeparator />
|
||||||
{parentPath && (
|
{parentPath && (
|
||||||
@ -148,7 +160,7 @@ function Editor({ resourceContent, repoMetadata, gitRef, resourcePath, isReposit
|
|||||||
<TextInput
|
<TextInput
|
||||||
autoFocus={isNew}
|
autoFocus={isNew}
|
||||||
value={fileName}
|
value={fileName}
|
||||||
inputRef={ref => (inputRef.current = ref)}
|
inputRef={_ref => (inputRef.current = _ref)}
|
||||||
wrapperClassName={css.inputContainer}
|
wrapperClassName={css.inputContainer}
|
||||||
placeholder={getString('nameYourFile')}
|
placeholder={getString('nameYourFile')}
|
||||||
onInput={(event: ChangeEvent<HTMLInputElement>) => {
|
onInput={(event: ChangeEvent<HTMLInputElement>) => {
|
||||||
@ -231,15 +243,22 @@ function Editor({ resourceContent, repoMetadata, gitRef, resourcePath, isReposit
|
|||||||
</Container>
|
</Container>
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
|
|
||||||
<Container className={cx(css.content, language)}>
|
<Container className={css.tabs}>
|
||||||
<SourceCodeEditor
|
<VisualYamlToggle
|
||||||
className={css.editorContainer}
|
onChange={setSelectedView}
|
||||||
height="100%"
|
selectedView={selectedView}
|
||||||
language={language}
|
labels={{ visual: getString('contents'), yaml: getString('changes') }}
|
||||||
source={originalContent}
|
className={css.selectedView}
|
||||||
onChange={setContent}
|
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
|
<Container className={cx(css.editorContainer, language)}>
|
||||||
|
{selectedView === VisualYamlSelectedView.VISUAL ? (
|
||||||
|
<SourceCodeEditor language={language} source={content} onChange={setContent} />
|
||||||
|
) : (
|
||||||
|
<DiffEditor language={language} original={originalContent} source={content} onChange={setContent} />
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,6 @@ export interface SourceCodeEditorProps {
|
|||||||
lineNumbers?: boolean
|
lineNumbers?: boolean
|
||||||
readOnly?: boolean
|
readOnly?: boolean
|
||||||
highlightLines?: string // i.e: {1,3-4}, TODO: not yet supported
|
highlightLines?: string // i.e: {1,3-4}, TODO: not yet supported
|
||||||
className?: string
|
|
||||||
height?: number | string
|
height?: number | string
|
||||||
autoHeight?: boolean
|
autoHeight?: boolean
|
||||||
wordWrap?: boolean
|
wordWrap?: boolean
|
||||||
|
@ -3,7 +3,7 @@ import type { TypesPullReqActivity } from 'services/code'
|
|||||||
|
|
||||||
export interface DiffFileEntry extends DiffFile {
|
export interface DiffFileEntry extends DiffFile {
|
||||||
fileId: string
|
fileId: string
|
||||||
fileTitle: string
|
filePath: string
|
||||||
containerId: string
|
containerId: string
|
||||||
contentId: string
|
contentId: string
|
||||||
fileActivities?: TypesPullReqActivity[]
|
fileActivities?: TypesPullReqActivity[]
|
||||||
|
Loading…
Reference in New Issue
Block a user