fix: wrapper md operators

This commit is contained in:
Joshua Shoemaker 2023-02-09 10:44:47 -06:00
parent 84a2b6bec2
commit 47cda8d0c8
4 changed files with 118 additions and 80 deletions

View File

@ -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

View File

@ -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>
} }

View 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

View File

@ -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()
} }
} }
} }