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; outline-offset: 2px;
background-color: rgba(154, 161, 173, 0.06) !important; 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) doc.head.appendChild(style)
} }
@ -147,97 +159,76 @@ function bindIframeEvents(doc: Document) {
el.addEventListener('mouseleave', () => { el.addEventListener('mouseleave', () => {
el.classList.remove('hover-highlight') 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) => { doc.addEventListener('mousedown', (e: MouseEvent) => {
if (mode.value !== 'select') return if (mode.value !== 'select') return
// block mouseDownTarget = e.target as HTMLElement
const target = e.target as HTMLElement mouseMoved = false
if (target.hasAttribute('data-block-index')) return isDragging.value = false
showSelectionRect.value = false
isDragging.value = true
showSelectionRect.value = true
dragStart.value = { x: e.clientX, y: e.clientY } dragStart.value = { x: e.clientX, y: e.clientY }
selectionRect.value = { left: e.clientX, top: e.clientY, width: 0, height: 0 } 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) => { doc.addEventListener('mousemove', (e: MouseEvent) => {
if (!isDragging.value || mode.value !== 'select') return 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 = { selectionRect.value = {
left: Math.min(dragStart.value.x, e.clientX), left: Math.min(dragStart.value.x, e.clientX),
top: Math.min(dragStart.value.y, e.clientY), top: Math.min(dragStart.value.y, e.clientY),
width: Math.abs(e.clientX - dragStart.value.x), width: Math.abs(e.clientX - dragStart.value.x),
height: Math.abs(e.clientY - dragStart.value.y), height: Math.abs(e.clientY - dragStart.value.y),
} }
}
}) })
doc.addEventListener('mouseup', (e: MouseEvent) => { doc.addEventListener('mouseup', (e: MouseEvent) => {
if (!isDragging.value) return if (mode.value !== 'select') return
if (mouseMoved && isDragging.value) {
// blocks
isDragging.value = false isDragging.value = false
showSelectionRect.value = false showSelectionRect.value = false
// const iframeRect = iframeRef.value?.getBoundingClientRect()
const dx = Math.abs(e.clientX - dragStart.value.x) if (!iframeRect) {
const dy = Math.abs(e.clientY - dragStart.value.y) dragStart.value = { x: 0, y: 0 }
if (dx < 5 && dy < 5) {
//
selectionRect.value = { left: 0, top: 0, width: 0, height: 0 }
return return
} }
const newSelected = new Set(selectedIndices.value)
// 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 selRect = { const selRect = {
left: selectionRect.value.left, left: selectionRect.value.left,
top: selectionRect.value.top, top: selectionRect.value.top,
right: selectionRect.value.left + selectionRect.value.width, right: selectionRect.value.left + selectionRect.value.width,
bottom: selectionRect.value.top + selectionRect.value.height, bottom: selectionRect.value.top + selectionRect.value.height,
} }
const bRect = { blocks.forEach(el => {
left: blockRect.left, const br = el.getBoundingClientRect()
top: blockRect.top,
right: blockRect.right,
bottom: blockRect.bottom,
}
if ( if (
selRect.left < bRect.right && selRect.left < br.right &&
selRect.right > bRect.left && selRect.right > br.left &&
selRect.top < bRect.bottom && selRect.top < br.bottom &&
selRect.bottom > bRect.top selRect.bottom > br.top
) { ) {
const index = parseInt(el.getAttribute('data-block-index') || '') const index = parseInt(el.getAttribute('data-block-index') || '')
if (!isNaN(index)) { if (!isNaN(index)) {
@ -246,27 +237,59 @@ function bindIframeEvents(doc: Document) {
} }
} }
}) })
selectedIndices.value = newSelected selectedIndices.value = newSelected
selectionRect.value = { left: 0, top: 0, width: 0, height: 0 } selectionRect.value = { left: 0, top: 0, width: 0, height: 0 }
if (newSelected.size > 0) { if (newSelected.size > 0) {
showContextMenu.value = true showContextMenu.value = true
contextMenuPos.value = { x: e.clientX, y: e.clientY + 10 } contextMenuPos.value = { x: e.clientX, y: e.clientY + 10 }
} else {
showContextMenu.value = false
} }
emitBlockSelect() emitBlockSelect()
}) } else {
//
isDragging.value = false
showSelectionRect.value = false
selectionRect.value = { left: 0, top: 0, width: 0, height: 0 }
//
doc.addEventListener('click', (e: MouseEvent) => {
if (mode.value !== 'select') return
const target = e.target as HTMLElement const target = e.target as HTMLElement
if (!target.hasAttribute('data-block-index') && !isDragging.value) { 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) clearAllSelection(doc)
selectedIndices.value = new Set() selectedIndices.value = new Set()
showContextMenu.value = false showContextMenu.value = false
emitBlockSelect() emitBlockSelect()
} }
}
dragStart.value = { x: 0, y: 0 }
mouseMoved = false
}) })
} }