Просмотр исходного кода

Merge pull request #14495 from overleaf/mj-table-gen-textarea

[visual] Use textarea instead of input in table generator

GitOrigin-RevId: 5d394f9978dc25bebf63f1cbd4f1c39466469843
Mathias Jakobsen 3 лет назад
Родитель
Сommit
a3ee0dcd39

+ 33 - 0
services/web/frontend/js/features/source-editor/components/table-generator/cell-input.tsx

@@ -0,0 +1,33 @@
+import { forwardRef, useImperativeHandle, useLayoutEffect, useRef } from 'react'
+
+interface CellInputProps
+  extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
+  value: string
+}
+
+export type CellInputRef = {
+  focus: () => void
+}
+
+export const CellInput = forwardRef<CellInputRef, CellInputProps>(
+  function CellInput({ value, ...props }: CellInputProps, ref) {
+    const inputRef = useRef<HTMLTextAreaElement>(null)
+    useImperativeHandle(ref, () => {
+      return {
+        focus() {
+          inputRef.current?.focus()
+          inputRef.current?.setSelectionRange(value.length, value.length)
+        },
+      }
+    })
+
+    useLayoutEffect(() => {
+      if (inputRef?.current) {
+        inputRef.current.style.height = '1px'
+        inputRef.current.style.height = `${inputRef.current.scrollHeight}px`
+      }
+    }, [value])
+
+    return <textarea {...props} value={value} ref={inputRef} />
+  }
+)

+ 21 - 13
services/web/frontend/js/features/source-editor/components/table-generator/cell.tsx

@@ -10,6 +10,7 @@ import { loadMathJax } from '../../../mathjax/load-mathjax'
 import { typesetNodeIntoElement } from '../../extensions/visual/utils/typeset-content'
 import { parser } from '../../lezer-latex/latex.mjs'
 import { useTableContext } from './contexts/table-context'
+import { CellInput, CellInputRef } from './cell-input'
 
 export const Cell: FC<{
   cellData: CellData
@@ -38,7 +39,7 @@ export const Cell: FC<{
     startEditing,
     commitCellData,
   } = useEditingContext()
-  const inputRef = useRef<HTMLInputElement>(null)
+  const inputRef = useRef<CellInputRef>(null)
 
   const editing =
     editingCellData?.rowIndex === rowIndex &&
@@ -48,6 +49,9 @@ export const Cell: FC<{
 
   const onMouseDown: MouseEventHandler = useCallback(
     event => {
+      if (editing) {
+        return
+      }
       if (event.button !== 0) {
         return
       }
@@ -65,7 +69,7 @@ export const Cell: FC<{
         )
       })
     },
-    [setDragging, columnIndex, rowIndex, setSelection, table]
+    [setDragging, columnIndex, rowIndex, setSelection, table, editing]
   )
 
   const onMouseUp = useCallback(() => {
@@ -117,19 +121,19 @@ export const Cell: FC<{
   )
 
   useEffect(() => {
-    if (editing) {
-      inputRef.current?.focus()
+    if (editing && inputRef.current) {
+      inputRef.current.focus()
     }
-  }, [editing])
+  }, [editing, cellData.content.length])
 
-  const filterInput = (input: string) => {
+  const filterInput = useCallback((input: string) => {
     // TODO: Are there situations where we don't want to filter the input?
     return input
       .replaceAll(/(^&|[^\\]&)/g, match =>
         match.length === 1 ? '\\&' : `${match[0]}\\&`
       )
       .replaceAll('\\\\', '')
-  }
+  }, [])
 
   const isFocused =
     selection?.to.row === rowIndex &&
@@ -161,18 +165,22 @@ export const Cell: FC<{
     }
   }, [cellData.content, editing])
 
+  const onInput = useCallback(
+    e => {
+      update(filterInput(e.target.value))
+    },
+    [update, filterInput]
+  )
+
   let body = <div ref={renderDiv} />
   if (editing) {
     body = (
-      <input
+      <CellInput
         className="table-generator-cell-input"
-        ref={inputRef}
         value={editingCellData.content}
         onBlur={commitCellData}
-        style={{ width: `inherit` }}
-        onChange={e => {
-          update(filterInput(e.target.value))
-        }}
+        onInput={onInput}
+        ref={inputRef}
       />
     )
   }

+ 1 - 1
services/web/frontend/js/features/source-editor/components/table-generator/table.tsx

@@ -79,7 +79,7 @@ export const Table: FC = () => {
 
   const onKeyDown: KeyboardEventHandler = useCallback(
     event => {
-      if (event.code === 'Enter') {
+      if (event.code === 'Enter' && !event.shiftKey) {
         event.preventDefault()
         event.stopPropagation()
         if (!selection) {

+ 5 - 1
services/web/frontend/js/features/source-editor/extensions/visual/table-generator.ts

@@ -275,10 +275,14 @@ export const tableGeneratorTheme = EditorView.baseTheme({
 
   '.table-generator-cell-input': {
     'max-width': 'calc(200px - 0.5em)',
-    width: '100%',
     'background-color': 'transparent',
+    width: '100%',
+    height: '1.5em',
     border: '1px solid var(--table-generator-toolbar-shadow-color)',
     padding: '0',
+    resize: 'none',
+    'box-sizing': 'border-box',
+    overflow: 'hidden',
   },
 
   '.table-generator-border-options-coming-soon': {