fix: wrapper md operators
This commit is contained in:
parent
84a2b6bec2
commit
47cda8d0c8
@ -4,7 +4,7 @@ import { useProject } from '../../context/Project/provider'
|
|||||||
import type { DiffOnMount } from '@monaco-editor/react/'
|
import type { DiffOnMount } from '@monaco-editor/react/'
|
||||||
import TextEditorButtons from './TextEditorButtons'
|
import TextEditorButtons from './TextEditorButtons'
|
||||||
import createDiffEditorInteractions from '../../useCases/createDiffEditorInteractions'
|
import createDiffEditorInteractions from '../../useCases/createDiffEditorInteractions'
|
||||||
import ReactMarkdown from 'react-markdown'
|
import TextPreview from './TextPreview'
|
||||||
|
|
||||||
let editorInteractions: ReturnType<typeof createDiffEditorInteractions>
|
let editorInteractions: ReturnType<typeof createDiffEditorInteractions>
|
||||||
|
|
||||||
@ -34,59 +34,42 @@ const TextEditor = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const requestProcessedArea = async () => {
|
const requestProcessedArea = async () => {
|
||||||
let response
|
|
||||||
try {
|
try {
|
||||||
response = await getProcessedAreasByDocumentId(selectedDocumentId)
|
const response = await getProcessedAreasByDocumentId(selectedDocumentId)
|
||||||
if (!response || response.length === 0) return
|
if (!response || response.length === 0) return
|
||||||
const fullTextOfAreas = response.map(area => area.fullText + '\n').join('\n')
|
const fullTextOfAreas = response.map(area => area.fullText + '\n').join('\n')
|
||||||
setEditorValue(fullTextOfAreas)
|
setEditorValue(fullTextOfAreas)
|
||||||
} catch (err) { console.error(err) }
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
setEditorValue('# No Areas on Document were textualized')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
requestProcessedArea()
|
requestProcessedArea()
|
||||||
}, [selectedDocumentId, getProcessedAreasByDocumentId])
|
}, [selectedDocumentId, getProcessedAreasByDocumentId])
|
||||||
|
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
setEditorHeight(window.innerHeight - 200)
|
setEditorHeight(window.innerHeight - 200)
|
||||||
})
|
})
|
||||||
|
|
||||||
return <div className='relative m-0 p-0'>
|
return <div className='relative m-0 p-0'>
|
||||||
{ isEditorReady
|
{isEditorReady
|
||||||
? <TextEditorButtons
|
? <TextEditorButtons
|
||||||
togglePreview={() => setIsPreviewOpen(!isPreviewOpen)}
|
isPreviewOpen={isPreviewOpen}
|
||||||
editorInteractions={editorInteractions}
|
togglePreview={() => setIsPreviewOpen(!isPreviewOpen)}
|
||||||
|
editorInteractions={editorInteractions}
|
||||||
/>
|
/>
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
<DiffEditor
|
<DiffEditor
|
||||||
original={editorValue}
|
original={editorValue}
|
||||||
modified={editorValue}
|
modified={editorValue}
|
||||||
language='markdown'
|
language='markdown'
|
||||||
height={`${editorHeight}px`}
|
height={`${editorHeight}px`}
|
||||||
onMount={handleEditorDidMount}
|
onMount={handleEditorDidMount}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isPreviewOpen
|
{ isPreviewOpen ? <TextPreview markdown={modifiedEditorValue} height={editorHeight} /> : '' }
|
||||||
? <div
|
</div>
|
||||||
className='absolute w-1/2 top-[30px] bg-white overflow-y-scroll p-0 m-0'
|
|
||||||
style={{'height': `${editorHeight}px`}}>
|
|
||||||
<ReactMarkdown
|
|
||||||
components={{
|
|
||||||
h1: ({node, ...props}) => <h1 {...props} className='font-extrabold text-2xl' />,
|
|
||||||
h2: ({node, ...props}) => <h2 {...props} className='font-extrabold text-xl' />,
|
|
||||||
h3: ({node, ...props}) => <h3 {...props} className='font-extrabold text-lg' />,
|
|
||||||
h4: ({node, ...props}) => <h4 {...props} className='font-extrabold text-base' />,
|
|
||||||
h5: ({node, ...props}) => <h5 {...props} className='font-bold text-base' />,
|
|
||||||
h6: ({node, ...props}) => <h6 {...props} className='font-semibold text-base' />,
|
|
||||||
ul: ({node, ...props}) => <ul {...props} className='list-disc list-inside ml-2' />,
|
|
||||||
ol: ({node, ...props}) => <ol {...props} className='list-decimal list-inside ml-2' />,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{modifiedEditorValue}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TextEditor
|
export default TextEditor
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ListBulletIcon } from '@heroicons/react/20/solid'
|
import { ListBulletIcon, MinusIcon } from '@heroicons/react/20/solid'
|
||||||
import { EyeIcon } from '@heroicons/react/24/outline'
|
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline'
|
||||||
import createDiffEditorInteractions, { MarkdownOperator } from '../../useCases/createDiffEditorInteractions'
|
import createDiffEditorInteractions, { MarkdownOperator } from '../../useCases/createDiffEditorInteractions'
|
||||||
|
|
||||||
function classNames(...classes: any[]) {
|
function classNames(...classes: any[]) {
|
||||||
@ -8,6 +8,7 @@ function classNames(...classes: any[]) {
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
editorInteractions: ReturnType<typeof createDiffEditorInteractions>
|
editorInteractions: ReturnType<typeof createDiffEditorInteractions>
|
||||||
|
isPreviewOpen: boolean
|
||||||
togglePreview: () => void
|
togglePreview: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,18 +16,22 @@ const TextEditorButtons = (props: Props) => {
|
|||||||
const { editorInteractions, togglePreview } = props
|
const { editorInteractions, togglePreview } = props
|
||||||
return <span className="isolate inline-flex rounded-md shadow-sm">
|
return <span className="isolate inline-flex rounded-md shadow-sm">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={togglePreview}
|
onClick={togglePreview}
|
||||||
className="relative -ml-px inline-flex items-center rounded-r-md border border-gray-300 bg-white px-2 py-0 text-sm font-medium text-gray-500 hover:bg-gray-50 focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
|
className={classNames(
|
||||||
>
|
'relative -ml-px inline-flex items-center rounded-l-md border',
|
||||||
<span className="sr-only">Next</span>
|
'border-gray-300 bg-white px-2 py-0 text-sm font-medium text-gray-500',
|
||||||
<EyeIcon className="h-5 w-5" aria-hidden="true" />
|
'hover:bg-gray-50 focus:z-10 focus:border-indigo-500 focus:outline-none',
|
||||||
</button>
|
'focus:ring-1 focus:ring-indigo-500'
|
||||||
|
)}>
|
||||||
|
<span className="sr-only">Next</span>
|
||||||
|
{props.isPreviewOpen ? <EyeSlashIcon className="h-5 w-5" aria-hidden="true" /> : <EyeIcon className="h-5 w-5" aria-hidden="true" />}
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.H1)}
|
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.H1)}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'text-lg relative inline-flex items-center rounded-l-md border',
|
'text-lg relative inline-flex items-center border',
|
||||||
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
|
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
|
||||||
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
|
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
|
||||||
'focus:ring-indigo-500 font-extrabold',
|
'focus:ring-indigo-500 font-extrabold',
|
||||||
@ -75,7 +80,7 @@ const TextEditorButtons = (props: Props) => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.ITALLICS)}
|
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.ITALLICS)}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'text-sm relative inline-flex items-center rounded-r-md border',
|
'text-sm relative inline-flex items-center border',
|
||||||
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
|
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
|
||||||
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
|
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
|
||||||
'focus:ring-indigo-500 italic font-serif',
|
'focus:ring-indigo-500 italic font-serif',
|
||||||
@ -88,7 +93,7 @@ const TextEditorButtons = (props: Props) => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.BOLD)}
|
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.BOLD)}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'text-sm relative inline-flex items-center rounded-r-md border',
|
'text-sm relative inline-flex items-center border',
|
||||||
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
|
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
|
||||||
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
|
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
|
||||||
'focus:ring-indigo-500 font-extrabold',
|
'focus:ring-indigo-500 font-extrabold',
|
||||||
@ -101,7 +106,7 @@ const TextEditorButtons = (props: Props) => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.BULLET)}
|
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.BULLET)}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'text-sm relative inline-flex items-center rounded-r-md border',
|
'text-sm relative inline-flex items-center border',
|
||||||
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
|
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
|
||||||
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
|
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
|
||||||
'focus:ring-indigo-500 italic',
|
'focus:ring-indigo-500 italic',
|
||||||
@ -109,6 +114,19 @@ const TextEditorButtons = (props: Props) => {
|
|||||||
<span className="sr-only">Bullet</span>
|
<span className="sr-only">Bullet</span>
|
||||||
<ListBulletIcon className="h-5 w-5" aria-hidden="true" />
|
<ListBulletIcon className="h-5 w-5" aria-hidden="true" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.DIVIDER)}
|
||||||
|
className={classNames(
|
||||||
|
'text-sm relative inline-flex items-center rounded-r-md border',
|
||||||
|
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
|
||||||
|
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
|
||||||
|
'focus:ring-indigo-500 italic',
|
||||||
|
)}>
|
||||||
|
<span className="sr-only">Divider</span>
|
||||||
|
<MinusIcon className="h-5 w-5" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
28
frontend/components/workspace/TextPreview.tsx
Normal file
28
frontend/components/workspace/TextPreview.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { ReactMarkdown } from 'react-markdown/lib/react-markdown'
|
||||||
|
|
||||||
|
type Props = { markdown: string, height: number }
|
||||||
|
|
||||||
|
const TextPreview = (props: Props) => (
|
||||||
|
<div
|
||||||
|
className='absolute w-1/2 top-[30px] bg-white overflow-y-scroll p-0 m-0'
|
||||||
|
style={{ 'height': `${props.height}px` }}>
|
||||||
|
<ReactMarkdown
|
||||||
|
components={{
|
||||||
|
h1: ({ node, ...props }) => <h1 {...props} className='font-extrabold text-2xl' />,
|
||||||
|
h2: ({ node, ...props }) => <h2 {...props} className='font-extrabold text-xl' />,
|
||||||
|
h3: ({ node, ...props }) => <h3 {...props} className='font-extrabold text-lg' />,
|
||||||
|
h4: ({ node, ...props }) => <h4 {...props} className='font-extrabold text-base' />,
|
||||||
|
h5: ({ node, ...props }) => <h5 {...props} className='font-bold text-base' />,
|
||||||
|
h6: ({ node, ...props }) => <h6 {...props} className='font-semibold text-base' />,
|
||||||
|
ul: ({ node, ...props }) => <ul {...props} className='list-disc list-inside ml-2' />,
|
||||||
|
ol: ({ node, ...props }) => <ol {...props} className='list-decimal list-inside ml-2' />,
|
||||||
|
em: ({ node, ...props }) => <em {...props} className='italic font-light' />,
|
||||||
|
p: ({ node, ...props }) => <p {...props} className='text-base mb-2' />,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.markdown}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default TextPreview
|
||||||
@ -5,9 +5,10 @@ export enum MarkdownOperator {
|
|||||||
H2 = '## ',
|
H2 = '## ',
|
||||||
H3 = '### ',
|
H3 = '### ',
|
||||||
H4 = '#### ',
|
H4 = '#### ',
|
||||||
ITALLICS = '__',
|
ITALLICS = '_',
|
||||||
BOLD = '**',
|
BOLD = '**',
|
||||||
BULLET = '* ',
|
BULLET = '* ',
|
||||||
|
DIVIDER = '\n\n---\n\n'
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapperOperators = [
|
const wrapperOperators = [
|
||||||
@ -15,7 +16,7 @@ const wrapperOperators = [
|
|||||||
MarkdownOperator.BOLD
|
MarkdownOperator.BOLD
|
||||||
]
|
]
|
||||||
|
|
||||||
const createDiffEditorInteractions = (editor: monaco.editor.IStandaloneDiffEditor)=> {
|
const createDiffEditorInteractions = (editor: monaco.editor.IStandaloneDiffEditor) => {
|
||||||
const modifiedEditor = editor.getModifiedEditor()
|
const modifiedEditor = editor.getModifiedEditor()
|
||||||
const originalEditor = editor.getOriginalEditor()
|
const originalEditor = editor.getOriginalEditor()
|
||||||
|
|
||||||
@ -25,41 +26,47 @@ const createDiffEditorInteractions = (editor: monaco.editor.IStandaloneDiffEdito
|
|||||||
insertMarkdownOperator: (operator: MarkdownOperator) => {
|
insertMarkdownOperator: (operator: MarkdownOperator) => {
|
||||||
const selection = modifiedEditor.getSelection()
|
const selection = modifiedEditor.getSelection()
|
||||||
if (!selection) return
|
if (!selection) return
|
||||||
|
|
||||||
const { startColumn, startLineNumber, endColumn, endLineNumber } = selection
|
const { startColumn, startLineNumber, endColumn, endLineNumber } = selection
|
||||||
|
|
||||||
const doesSelectionHaveRange = (endLineNumber > startLineNumber) || (endColumn > startColumn)
|
const doesSelectionHaveRange = (endLineNumber > startLineNumber) || (endColumn > startColumn)
|
||||||
|
|
||||||
const lineOfCursor = startLineNumber
|
|
||||||
const lengthOfLine = (modifiedEditor.getModel()?.getLineLength(lineOfCursor) || 1) + 1
|
|
||||||
|
|
||||||
let range: monaco.IRange = { startColumn, startLineNumber, endColumn, endLineNumber, }
|
let range: monaco.IRange = { startColumn, startLineNumber, endColumn, endLineNumber, }
|
||||||
let newText = modifiedEditor.getModel()?.getValueInRange(range) || ''
|
let newText = modifiedEditor.getModel()?.getValueInRange(range) || ''
|
||||||
|
|
||||||
if (wrapperOperators.includes(operator)) {
|
const lineOfCursor = startLineNumber
|
||||||
if (!doesSelectionHaveRange) {
|
const lengthOfLine = (modifiedEditor.getModel()?.getLineLength(lineOfCursor) || 1) + 1
|
||||||
range = {
|
|
||||||
startLineNumber: lineOfCursor,
|
const wordAtStartPosition = modifiedEditor.getModel()?.getWordAtPosition({
|
||||||
endLineNumber: lineOfCursor,
|
column: startColumn, lineNumber: startLineNumber
|
||||||
startColumn: 0,
|
})
|
||||||
endColumn: lengthOfLine
|
|
||||||
}
|
if (operator == MarkdownOperator.DIVIDER) {
|
||||||
|
console.log('lineOfCursor:', lineOfCursor)
|
||||||
|
console.log('lengthOfLine:', lengthOfLine)
|
||||||
|
range = {
|
||||||
|
startLineNumber,
|
||||||
|
startColumn: lengthOfLine,
|
||||||
|
endLineNumber,
|
||||||
|
endColumn: lengthOfLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newText = `${operator}`
|
||||||
|
} else if (wrapperOperators.includes(operator)) {
|
||||||
|
if (!doesSelectionHaveRange && wordAtStartPosition) range = {
|
||||||
|
startLineNumber,
|
||||||
|
startColumn: wordAtStartPosition.startColumn,
|
||||||
|
endLineNumber,
|
||||||
|
endColumn: wordAtStartPosition.endColumn
|
||||||
|
}
|
||||||
newText = `${operator}${modifiedEditor.getModel()?.getValueInRange(range)}${operator}`
|
newText = `${operator}${modifiedEditor.getModel()?.getValueInRange(range)}${operator}`
|
||||||
} else {
|
} else {
|
||||||
const wordAtStartPosition = modifiedEditor.getModel()?.getWordAtPosition({
|
range = {
|
||||||
column:startColumn, lineNumber: startLineNumber
|
startLineNumber,
|
||||||
})
|
startColumn: 0,
|
||||||
|
endLineNumber,
|
||||||
if (!doesSelectionHaveRange && wordAtStartPosition) {
|
endColumn: 0
|
||||||
range = {
|
}
|
||||||
startLineNumber,
|
|
||||||
startColumn: wordAtStartPosition.startColumn,
|
|
||||||
endLineNumber,
|
|
||||||
endColumn: wordAtStartPosition.endColumn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newText = `${operator}${modifiedEditor.getModel()?.getValueInRange(range)}`
|
newText = `${operator}${modifiedEditor.getModel()?.getValueInRange(range)}`
|
||||||
}
|
}
|
||||||
@ -68,6 +75,8 @@ const createDiffEditorInteractions = (editor: monaco.editor.IStandaloneDiffEdito
|
|||||||
range,
|
range,
|
||||||
text: newText
|
text: newText
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
modifiedEditor.pushUndoStop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user