152 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import { Marked } from "marked"
 | 
						|
import { cursors, Cursor } from "./cursor"
 | 
						|
 | 
						|
// 示例 Markdown 文本
 | 
						|
const markdown = `
 | 
						|
# 2333
 | 
						|
- [ ] 1111
 | 
						|
- [ ] 1111
 | 
						|
- [ ] 1111
 | 
						|
- [x] 2222
 | 
						|
    - [ ] 233323
 | 
						|
    - [ ] 233323
 | 
						|
    - [ ] 233323
 | 
						|
 | 
						|
这是一段测试文本
 | 
						|
`
 | 
						|
 | 
						|
const marked = new Marked()
 | 
						|
const tokens = marked.lexer(markdown)
 | 
						|
 | 
						|
// 创建 textarea 接受输入
 | 
						|
const textarea = document.createElement("textarea")
 | 
						|
Object.assign(textarea.style, {
 | 
						|
    position: "fixed",
 | 
						|
    bottom: "10px",
 | 
						|
    left: "10px",
 | 
						|
    width: "300px",
 | 
						|
    height: "100px",
 | 
						|
    zIndex: "1000",
 | 
						|
    placeholder: "在这里输入文本或使用方向键调整位置",
 | 
						|
})
 | 
						|
document.body.appendChild(textarea)
 | 
						|
 | 
						|
// 处理输入事件
 | 
						|
textarea.oninput = () => {
 | 
						|
    const inputText = textarea.value // 获取用户输入的文本
 | 
						|
    if (!inputText) return
 | 
						|
 | 
						|
    cursors.forEach(cursor => {
 | 
						|
        if (!cursor.targetNode || cursor.insertIndex === null) return
 | 
						|
 | 
						|
        // 更新文本节点内容
 | 
						|
        cursor.targetNode.textContent =
 | 
						|
            cursor.targetNode.textContent.slice(0, cursor.insertIndex) +
 | 
						|
            inputText +
 | 
						|
            cursor.targetNode.textContent.slice(cursor.insertIndex)
 | 
						|
 | 
						|
        // 更新插入位置
 | 
						|
        cursor.insertIndex += inputText.length
 | 
						|
    })
 | 
						|
 | 
						|
    // 清空输入框
 | 
						|
    textarea.value = ""
 | 
						|
    updateCursors()
 | 
						|
}
 | 
						|
 | 
						|
// 处理方向键移动插入点
 | 
						|
textarea.onkeydown = (event) => {
 | 
						|
    cursors.filter(cursor => cursor.targetNode && cursor.insertIndex !== null).forEach(cursor => {
 | 
						|
        cursor.move(event.key)
 | 
						|
        updateCursors()
 | 
						|
    })
 | 
						|
}
 | 
						|
 | 
						|
// 更新所有光标的位置
 | 
						|
const updateCursors = () => {
 | 
						|
    cursors.forEach(cursor => {
 | 
						|
        if (!cursor.targetNode || cursor.insertIndex === null) {
 | 
						|
            cursor.hide()
 | 
						|
            return
 | 
						|
        }
 | 
						|
 | 
						|
        // 获取插入点位置
 | 
						|
        const rect = cursor.getBoundingClientRect()
 | 
						|
 | 
						|
        // 更新光标位置
 | 
						|
        cursor.updatePosition(rect)
 | 
						|
    })
 | 
						|
}
 | 
						|
 | 
						|
// 渲染 Markdown 并监听点击事件
 | 
						|
const element = document.createElement("div")
 | 
						|
element.innerHTML = marked.parser(tokens)
 | 
						|
document.body.appendChild(element)
 | 
						|
 | 
						|
// 点击事件:记录插入位置
 | 
						|
element.onclick = (event) => {
 | 
						|
    if (event.target.tagName !== "LI") return
 | 
						|
 | 
						|
    const { clientX: x, clientY: y } = event
 | 
						|
 | 
						|
    // 查找点击的文本节点
 | 
						|
    const textNodes = Array.from(event.target.childNodes).filter(
 | 
						|
        (node) => node.nodeType === Node.TEXT_NODE
 | 
						|
    )
 | 
						|
 | 
						|
    const targetNode = textNodes.find((node) => {
 | 
						|
        const range = document.createRange()
 | 
						|
        range.selectNodeContents(node)
 | 
						|
        const rect = range.getBoundingClientRect()
 | 
						|
        return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
 | 
						|
    })
 | 
						|
 | 
						|
    if (!targetNode) return
 | 
						|
 | 
						|
    // 计算插入位置索引
 | 
						|
    const positions = [...targetNode.textContent].map((_, i) => {
 | 
						|
        const range = document.createRange()
 | 
						|
        range.setStart(targetNode, i)
 | 
						|
        range.setEnd(targetNode, i + 1)
 | 
						|
        return { index: i, rect: range.getBoundingClientRect() }
 | 
						|
    })
 | 
						|
 | 
						|
    const closest = positions.reduce(
 | 
						|
        (closest, pos) => {
 | 
						|
            const dist = Math.abs(x - pos.rect.left)
 | 
						|
            return dist < closest.distance ? { ...pos, distance: dist } : closest
 | 
						|
        },
 | 
						|
        { index: -1, distance: Infinity }
 | 
						|
    )
 | 
						|
 | 
						|
    const rect = closest.rect
 | 
						|
    const insertBefore = x < rect.left + rect.width / 2
 | 
						|
    const insertIndex = closest.index + (insertBefore ? 0 : 1)
 | 
						|
 | 
						|
    // 设置光标的目标节点和插入位置
 | 
						|
    cursors.forEach(cursor => cursor.setTarget(targetNode, insertIndex))
 | 
						|
 | 
						|
    // 如果是点击多个光标的情况,增加新光标
 | 
						|
    if (event.ctrlKey || !cursors.length) {
 | 
						|
        const newCursor = new Cursor(targetNode, insertIndex)
 | 
						|
        cursors.push(newCursor)
 | 
						|
        console.log("新增光标")
 | 
						|
    }
 | 
						|
 | 
						|
    // 更新光标位置
 | 
						|
    updateCursors()
 | 
						|
 | 
						|
    // 聚焦输入框
 | 
						|
    textarea.value = ""
 | 
						|
    textarea.focus()
 | 
						|
}
 | 
						|
 | 
						|
// 按删除键移除光标
 | 
						|
document.addEventListener('keydown', (event) => {
 | 
						|
    if (event.key === 'Delete' && cursors.length > 0) {
 | 
						|
        const cursorToRemove = cursors.pop() // 移除最后一个光标
 | 
						|
        cursorToRemove.remove() // 移除对应的光标元素
 | 
						|
        console.log("移除光标")
 | 
						|
    }
 | 
						|
})
 |