是否有更好的方法来实现 Google Sheet 的类似单元格功能?

问题描述

我正在 React 中构建一个系统,该系统具有包含单元格的表格数据。这些单元格可以通过 contentEditable div 进行编辑。它在功能上类似于谷歌表格。我正在研究单击单元格允许用户覆盖单元格的当前值并双击允许他们编辑值的功能

所涉及的功能基本上是这样的:

  • 当单击单元格时覆盖当前值。 (没有光标可见?)
  • 双击单元格时允许用户编辑当前值。 (光标可见,可以用箭头键左右移动字符)
  • 双击单元格重新格式化值时(删除尾随零表示美分:8.50 变为 8.5)
  • 双击时在输入的末尾开始插入符号位置。
  • 用户点击单元格时,将当前值重新格式化为其适当的格式(例如价格单元格)

我的单元格组件如下所示:

(注意* useDoubleClick() 是我写的一个自定义钩子,它工作得很好,会相应地调用单击/双击操作)

export default function Cell({ name,value,updateItem }) {
  const [value,setValue] = useState(props.value),// This stays uncontrolled to prevent the caret jumps with content editable.
        [isInputMode,setIsInputMode] = useState(false),cellRef = useRef(null);

  // Handle single click. Basically does nothing right Now.
  const singleClickAction = () => {
    if(isInputMode)
      return;
  }    

  // Handle double click.
  const doubleClickAction = () => {
    // If already input mode,do nothing.
    if(isInputMode) {
      return;
    }

    setIsInputMode(true);
    setCaretPosition(); // Crashing page sometimes [see error below]
    reformatValue();
  }  

  // It's Now input mode,set the caret position to the length of the cell's innerText.
  const setCaretPosition = () => {
    var range = document.createrange(),select = window.getSelection();

    range.setStart(cellRef.current.childNodes[0],cellRef.current.innerText.length);

    range.collapse(true);

    selectObject.removeAllRanges();
    selectObject.addRange(range);
  }

  // Reformat innerText value to remove trailing zero's from cents.
  const reformatValue = () => {
    var updatedValue = parseFloat(value);

    setValue(updatedValue);
  }

  const onClick = useDoubleClick(singleClickAction,doubleClickAction);

  /*
   * Handle input change. Pass innerText value to global update function.

   * Because we are using contentEditable and render "" if !isInputMode 
   * we have override functionality.
   */
  const onInput = (e) => {
    props.updateItem(props.name,e.target.innerText);
  }

  // When cell is blurred,reset isInputMode 
  const onBlur = () => {
    setIsInputMode(false);
    cellRef.current.innerText = ""; // Without this single click will not override again after blur.
  }

  return (
    <div 
      data-placeholder={value} // to view current value while isInputMode is false
      class="cell-input"
      contentEditable="true"
      onClick={onClick}
      onInput={onInput}
      onBlur={onBlur}
      ref={cellRef}
    >
      {isInputMode ? value : ""}
    </div>
  )      
}

这里有一些 css 以便用户可以在 isInputMode 为 false 时看到当前值:

.cell-input {
  :empty:before {
    content: attr(data-placeholder);
  }
  :empty:focus:before {
    content: attr(data-placeholder);
  }
}

现在是我遇到的问题。

  • 当我调用 setCaretPosition 函数时,没有 childNodes,因为我正在呈现空值 ("") 并且有时会因错误而使页面崩溃 - TypeError: Argument 1 ('node') to Range.setStart must be一个 Node 实例。
  • 我在包含价格的单元格内有一个 $,我在 css 中设置了 ::before 和 content: '$',但现在我不能因为数据占位符片段。
  • 双击单元格时,光标根本不可见。如果您单击箭头在字符之间移动,它就会变得可见。

这个解决方案让我非常接近我想要的输出,所以我感觉很好,但我认为可能有更好的方法解决它,或者在我的解决方案中进行一些调整,这将是一个普遍的改进。很想听听一些想法。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)