contentEditable 实现可编辑区域

背景

最近看豆包的时候,发现他的代码写AI编程模块 如下

借助的是contentEditable 来实现的,操作比较友好,可以随意输入可编辑数据,有类似于 placeholder等属性

如果我们开发大模型,免不了东西借鉴一下,下面基于react 来封装 一个 使用contentEditable 来实现的可编辑区域组件

实现重点

  1. 开启 contentEditable 后 如何模拟 placeholder
  2. 如何判断何时展示 placeholder 何时展示真实内容
  3. 调整优化以及受控组件封装

代码如下

js 复制代码
import React, { useState, useRef, useEffect } from 'react'

interface ContentEditablePromptProps {
  placeholder?: string
  className?: string
  value?: string
  onChange?: (value: string) => void
}

const ContentEditablePrompt: React.FC<ContentEditablePromptProps> = ({
  placeholder = '请输入',
  className = '',
  value = '',
  onChange
}) => {
  const contentEditableRef = useRef<HTMLDivElement>(null)
  const [isEmpty, setIsEmpty] = useState(true)
  const [internalValue, setInternalValue] = useState(value)

  // 更新内容并处理空状态
  const updateContent = (newValue: string) => {
    if (!contentEditableRef.current) return

    // 更新内部状态
    setInternalValue(newValue)

    // 调用父组件的onChange回调
    if (onChange) {
      onChange(newValue)
    }

    // 更新空状态
    const isNowEmpty = newValue.trim() === ''
    setIsEmpty(isNowEmpty)

    // 处理空状态下的placeholder显示
    if (contentEditableRef.current) {
      if (isNowEmpty && contentEditableRef.current.innerHTML === '<br>') {
        contentEditableRef.current.innerHTML = ''
      }
    }
  }

  // 处理输入事件
  const handleInput = () => {
    if (!contentEditableRef.current) return
    updateContent(contentEditableRef.current.textContent || '')
  }

  // 处理失去焦点事件
  const handleBlur = () => {
    if (!contentEditableRef.current) return

    // 规范化内容
    const newValue = contentEditableRef.current.textContent || ''
    updateContent(newValue)

    // 确保DOM内容与内部状态一致
    if (contentEditableRef.current.textContent !== internalValue) {
      contentEditableRef.current.textContent = internalValue
    }
  }

  // 初始化内容和响应外部value变化
  useEffect(() => {
    if (!contentEditableRef.current) return

    // 只有当外部value与内部值不同时才更新
    if (value !== internalValue) {
      setInternalValue(value)
      contentEditableRef.current.textContent = value
      setIsEmpty(value.trim() === '')
    }
  }, [value])

  return (
    <div
      ref={contentEditableRef}
      className={`p-2 px-3 w-fit rounded-2xl flex items-center ${
        isEmpty
          ? 'after:content-[attr(data-placeholder)] after:tracking-[1px] bg-[#EEF6FF] font-bold text-[#007DFA] word-spacing-2'
          : 'bg-[#EEF6FF] font-bold text-[#007DFA] word-spacing-2'
      } ${className}`}
      contentEditable
      data-placeholder={placeholder}
      onInput={handleInput}
      onBlur={handleBlur}
      suppressContentEditableWarning
    />
  )
}

export default ContentEditablePrompt

使用如下:

js 复制代码
<EditableArea placeholder='预设提取项' value='' onChange={onChangeValue}></EditableArea>

结束

相关推荐
韩师傅15 分钟前
前端开发消亡史:AI也无法掩盖没有设计创造力的真相
前端·人工智能·后端
XiaoYu200229 分钟前
第12章 支付宝SDK
前端
双向331 小时前
RAG的下一站:检索增强生成如何重塑企业知识中枢?
前端
拖拉斯旋风1 小时前
从零开始:使用 Ollama 在本地部署开源大模型并集成到 React 应用
前端·javascript·ollama
asing1 小时前
🤯 为什么我的收银台在鸿蒙系统“第一次返回”死活拦不住?一次差点背锅的排查实录
前端·harmonyos
德育处主任1 小时前
『NAS』在群晖部署图片压缩工具-Squoosh
前端·javascript·docker
Hao_Harrision1 小时前
50天50个小项目 (React19 + Tailwindcss V4) ✨| ThreeDBackgroundBoxes(3D背景盒子组件)
前端·3d·typescript·react·tailwindcss·vite7
加个鸡腿儿1 小时前
经验分享2:SSR 项目中响应式组件的闪动陷阱与修复实践
前端·css·架构
心.c1 小时前
如何基于 RAG 技术,搭建一个专属的智能 Agent 平台
开发语言·前端·vue.js
智航GIS2 小时前
10.7 pyspider 库入门
开发语言·前端·python