fix: 框选模式改为任意位置拖拽均可画矩形选择

- 移除 block 元素上的 click 事件(单点选中)
- mousedown 不再跳过 block 元素,任意位置均可开始拖拽
- mouseup 根据移动距离判断:>3px 为框选,<=3px 为单击选中
- 单击空白取消选中,Ctrl+单击追加选中
- 添加列表项和变量块 CSS 样式注入
This commit is contained in:
zwt13703 2026-07-06 00:41:09 +08:00
parent 80f5c45e10
commit e247f0db73
1 changed files with 118 additions and 95 deletions

View File

@ -126,6 +126,18 @@ function injectStyles(doc: Document) {
outline-offset: 2px;
background-color: rgba(154, 161, 173, 0.06) !important;
}
[data-block-index].block-variable {
outline: 2px solid rgba(26, 140, 74, 0.5) !important;
outline-offset: 2px;
background-color: rgba(26, 140, 74, 0.06) !important;
}
.block-list-item.list-unordered::before {
content: "\\2022";
display: inline-block;
width: 1em;
margin-left: -1em;
color: #333;
}
`
doc.head.appendChild(style)
}
@ -147,126 +159,137 @@ function bindIframeEvents(doc: Document) {
el.addEventListener('mouseleave', () => {
el.classList.remove('hover-highlight')
})
//
el.addEventListener('click', (e: Event) => {
if (mode.value !== 'select') return
const mouseEvent = e as MouseEvent
const index = parseInt(el.getAttribute('data-block-index') || '')
if (isNaN(index)) return
if (mouseEvent.ctrlKey || mouseEvent.metaKey) {
// Ctrl+/
if (selectedIndices.value.has(index)) {
selectedIndices.value.delete(index)
el.classList.remove('block-selected')
} else {
selectedIndices.value.add(index)
el.classList.add('block-selected')
}
selectedIndices.value = new Set(selectedIndices.value)
} else {
//
clearAllSelection(doc)
selectedIndices.value = new Set([index])
el.classList.add('block-selected')
}
emitBlockSelect()
})
})
//
// mousedown mouseup
let mouseDownTarget: HTMLElement | null = null
let mouseMoved = false
doc.addEventListener('mousedown', (e: MouseEvent) => {
if (mode.value !== 'select') return
// block
const target = e.target as HTMLElement
if (target.hasAttribute('data-block-index')) return
isDragging.value = true
showSelectionRect.value = true
mouseDownTarget = e.target as HTMLElement
mouseMoved = false
isDragging.value = false
showSelectionRect.value = false
dragStart.value = { x: e.clientX, y: e.clientY }
selectionRect.value = { left: e.clientX, top: e.clientY, width: 0, height: 0 }
// Ctrl
if (!(e.ctrlKey || e.metaKey)) {
clearAllSelection(doc)
selectedIndices.value = new Set()
}
})
doc.addEventListener('mousemove', (e: MouseEvent) => {
if (!isDragging.value || mode.value !== 'select') return
selectionRect.value = {
left: Math.min(dragStart.value.x, e.clientX),
top: Math.min(dragStart.value.y, e.clientY),
width: Math.abs(e.clientX - dragStart.value.x),
height: Math.abs(e.clientY - dragStart.value.y),
if (mode.value !== 'select') return
if (dragStart.value.x === 0 && dragStart.value.y === 0) return
const dx = Math.abs(e.clientX - dragStart.value.x)
const dy = Math.abs(e.clientY - dragStart.value.y)
if (dx > 3 || dy > 3) {
mouseMoved = true
//
if (!isDragging.value) {
isDragging.value = true
showSelectionRect.value = true
}
selectionRect.value = {
left: Math.min(dragStart.value.x, e.clientX),
top: Math.min(dragStart.value.y, e.clientY),
width: Math.abs(e.clientX - dragStart.value.x),
height: Math.abs(e.clientY - dragStart.value.y),
}
}
})
doc.addEventListener('mouseup', (e: MouseEvent) => {
if (!isDragging.value) return
isDragging.value = false
showSelectionRect.value = false
if (mode.value !== 'select') return
//
const dx = Math.abs(e.clientX - dragStart.value.x)
const dy = Math.abs(e.clientY - dragStart.value.y)
if (dx < 5 && dy < 5) {
//
selectionRect.value = { left: 0, top: 0, width: 0, height: 0 }
return
}
if (mouseMoved && isDragging.value) {
// blocks
isDragging.value = false
showSelectionRect.value = false
// blocks
const rect = iframeRef.value?.getBoundingClientRect()
if (!rect) return
clearAllSelection(doc)
const newSelected = new Set<number>()
blocks.forEach(el => {
const blockRect = el.getBoundingClientRect()
// block
const iframeRect = iframeRef.value?.getBoundingClientRect()
if (!iframeRect) {
dragStart.value = { x: 0, y: 0 }
return
}
const newSelected = new Set(selectedIndices.value)
const selRect = {
left: selectionRect.value.left,
top: selectionRect.value.top,
right: selectionRect.value.left + selectionRect.value.width,
bottom: selectionRect.value.top + selectionRect.value.height,
}
const bRect = {
left: blockRect.left,
top: blockRect.top,
right: blockRect.right,
bottom: blockRect.bottom,
}
if (
selRect.left < bRect.right &&
selRect.right > bRect.left &&
selRect.top < bRect.bottom &&
selRect.bottom > bRect.top
) {
const index = parseInt(el.getAttribute('data-block-index') || '')
if (!isNaN(index)) {
newSelected.add(index)
el.classList.add('block-selected')
blocks.forEach(el => {
const br = el.getBoundingClientRect()
if (
selRect.left < br.right &&
selRect.right > br.left &&
selRect.top < br.bottom &&
selRect.bottom > br.top
) {
const index = parseInt(el.getAttribute('data-block-index') || '')
if (!isNaN(index)) {
newSelected.add(index)
el.classList.add('block-selected')
}
}
})
selectedIndices.value = newSelected
selectionRect.value = { left: 0, top: 0, width: 0, height: 0 }
if (newSelected.size > 0) {
showContextMenu.value = true
contextMenuPos.value = { x: e.clientX, y: e.clientY + 10 }
} else {
showContextMenu.value = false
}
})
selectedIndices.value = newSelected
selectionRect.value = { left: 0, top: 0, width: 0, height: 0 }
if (newSelected.size > 0) {
showContextMenu.value = true
contextMenuPos.value = { x: e.clientX, y: e.clientY + 10 }
}
emitBlockSelect()
})
//
doc.addEventListener('click', (e: MouseEvent) => {
if (mode.value !== 'select') return
const target = e.target as HTMLElement
if (!target.hasAttribute('data-block-index') && !isDragging.value) {
clearAllSelection(doc)
selectedIndices.value = new Set()
showContextMenu.value = false
emitBlockSelect()
} else {
//
isDragging.value = false
showSelectionRect.value = false
selectionRect.value = { left: 0, top: 0, width: 0, height: 0 }
const target = e.target as HTMLElement
const blockEl = target.closest?.('[data-block-index]') as HTMLElement | null
if (blockEl) {
const index = parseInt(blockEl.getAttribute('data-block-index') || '')
if (!isNaN(index)) {
if (e.ctrlKey || e.metaKey) {
// Ctrl+/
if (selectedIndices.value.has(index)) {
selectedIndices.value.delete(index)
blockEl.classList.remove('block-selected')
} else {
selectedIndices.value.add(index)
blockEl.classList.add('block-selected')
}
selectedIndices.value = new Set(selectedIndices.value)
} else {
// mousedown
selectedIndices.value = new Set([index])
blockEl.classList.add('block-selected')
}
emitBlockSelect()
if (selectedIndices.value.size > 0) {
showContextMenu.value = true
contextMenuPos.value = { x: e.clientX, y: e.clientY + 10 }
}
}
} else {
//
clearAllSelection(doc)
selectedIndices.value = new Set()
showContextMenu.value = false
emitBlockSelect()
}
}
dragStart.value = { x: 0, y: 0 }
mouseMoved = false
})
}