From 20c3b071bd19bc35297f836c48a73e2cf3333bc9 Mon Sep 17 00:00:00 2001 From: Gene Date: Wed, 19 Mar 2025 16:39:36 +0800 Subject: [PATCH] feat: The outline tree supports hotkeys and works like canvas (#1226) --- packages/canvas/container/index.js | 5 ++- packages/canvas/container/src/keyboard.js | 28 ++++++++++-- packages/plugins/tree/src/DraggableTree.vue | 21 +++++++-- packages/plugins/tree/src/Main.vue | 48 ++++++++++++++++++--- 4 files changed, 88 insertions(+), 14 deletions(-) diff --git a/packages/canvas/container/index.js b/packages/canvas/container/index.js index 1cc59304..31c7dcd8 100644 --- a/packages/canvas/container/index.js +++ b/packages/canvas/container/index.js @@ -1,7 +1,10 @@ import CanvasContainer from './src/CanvasContainer.vue' +import { useMultiSelect } from './src/composables/useMultiSelect' +import { registerHotkeyEvent, removeHotkeyEvent } from './src/keyboard' import metaData from './meta' export default { ...metaData, - entry: CanvasContainer + entry: CanvasContainer, + api: { useMultiSelect, registerHotkeyEvent, removeHotkeyEvent } } diff --git a/packages/canvas/container/src/keyboard.js b/packages/canvas/container/src/keyboard.js index 26bb630a..4c83e366 100644 --- a/packages/canvas/container/src/keyboard.js +++ b/packages/canvas/container/src/keyboard.js @@ -146,7 +146,15 @@ const handleCopyEvent = (event) => { setClipboardSchema(event, dataToCopy) } +const eventFiltersMap = new WeakMap() + const handlerClipboardEvent = (event) => { + const eventFilter = eventFiltersMap.get(event.currentTarget) + // 如果过滤器返回 false,则阻止处理 + if (typeof eventFilter === 'function' && !eventFilter(event)) { + return + } + switch (event.type) { case 'copy': handleCopyEvent(event) @@ -163,12 +171,18 @@ const handlerClipboardEvent = (event) => { } const keyboardHandler = (event) => { + const eventFilter = eventFiltersMap.get(event.currentTarget) + // 如果过滤器返回 false,则阻止处理 + if (typeof eventFilter === 'function' && !eventFilter(event)) { + return + } + // 处理 Ctrl 或 Command 键 if (event.ctrlKey || event.metaKey) { handlerCtrl(event) + } else { + handlerArrow(event.keyCode) } - - handlerArrow(event.keyCode) } const removeHotkeyEvent = (dom) => { @@ -176,11 +190,19 @@ const removeHotkeyEvent = (dom) => { dom.removeEventListener('copy', handlerClipboardEvent) dom.removeEventListener('cut', handlerClipboardEvent) dom.removeEventListener('paste', handlerClipboardEvent) + + eventFiltersMap.delete(dom) } -const registerHotkeyEvent = (dom) => { +const registerHotkeyEvent = (dom, options) => { removeHotkeyEvent(dom) + const { eventFilter } = options || {} + + if (typeof eventFilter === 'function') { + eventFiltersMap.set(dom, eventFilter) + } + dom.addEventListener('keydown', keyboardHandler) dom.addEventListener('copy', handlerClipboardEvent) dom.addEventListener('cut', handlerClipboardEvent) diff --git a/packages/plugins/tree/src/DraggableTree.vue b/packages/plugins/tree/src/DraggableTree.vue index fc7252a2..3adfc1a4 100644 --- a/packages/plugins/tree/src/DraggableTree.vue +++ b/packages/plugins/tree/src/DraggableTree.vue @@ -9,14 +9,14 @@ 'tree-row', 'flex-center', { - active: active === row.id, + active: activesComputed.includes(row.id), dragging: draggingState.hovering?.id === row.id, 'border-all': draggingState.hovering?.id === row.id && draggingState.position === 'center', forbid: draggingState.forbidInsert } ]" :draggable="draggable ? 'true' : undefined" - @click="handleClickRow(row)" + @click="handleClickRow($event, row)" @mouseenter="handleMouseEnterRow(row)" @dragstart="handleDragStart($event, row)" @dragover="handleDragOver($event, row)" @@ -60,6 +60,10 @@ const props = defineProps({ active: { type: String }, + actives: { + type: Array, + default: () => [] + }, idKey: { type: String, default: 'id' @@ -82,6 +86,15 @@ const props = defineProps({ } }) +/** @type {import('vue').ComputedRef} */ +const activesComputed = computed(() => { + if (props.actives.length === 0 && props.active) { + return [props.active] + } + + return props.actives +}) + /** * @typedef {Object} Node * @property {string} id @@ -195,8 +208,8 @@ const rows = computed(() => flattenNodes(normalizedData.value)) const emit = defineEmits(['click', 'mouseenter', 'drop']) -const handleClickRow = (row) => { - emit('click', row) +const handleClickRow = (event, row) => { + emit('click', event, row) } const handleMouseEnterRow = (row) => { diff --git a/packages/plugins/tree/src/Main.vue b/packages/plugins/tree/src/Main.vue index 676bbc97..a2e74c7f 100644 --- a/packages/plugins/tree/src/Main.vue +++ b/packages/plugins/tree/src/Main.vue @@ -1,5 +1,5 @@