import { useCommander } from './../plugin/command.plugin'; import { ReactVisualEditorBlock, ReactVisualEditorValue } from './ReactVisualEditor.utils'; import deepcopy from 'deepcopy'; import { useRef } from 'react'; import { useCallbackRef } from './../hook/useCallbackRef'; export function useVisualCommand({ focusData, value, updateBlocks, updateValue, dragstart, dragend, }: { focusData: { focus: ReactVisualEditorBlock[]; unFocus: ReactVisualEditorBlock[]; }; value: ReactVisualEditorValue; updateBlocks: (blocks: ReactVisualEditorBlock[]) => void; updateValue: (value: ReactVisualEditorValue) => void; dragstart: { on: (cb: () => void) => void; off: (cb: () => void) => void }; dragend: { on: (cb: () => void) => void; off: (cb: () => void) => void }; }) { const commander = useCommander(); /*删除命令*/ commander.useRegistry({ name: 'delete', keyboard: ['delete', 'ctrl+d', 'backspace'], execute() { const before = deepcopy(value.blocks); const after = deepcopy(focusData.unFocus); return { redo: () => { updateBlocks(deepcopy(after)); }, undo: () => { updateBlocks(deepcopy(before)); }, }; }, }); /** * 拖拽命令 */ (() => { const dragData = useRef({ before: null as null | ReactVisualEditorBlock[] }); const handler = { dragstart: useCallbackRef(() => (dragData.current.before = deepcopy(value.blocks))), dragend: useCallbackRef(() => commander.state.commands.drag()), }; /** * 拖拽命令,适用于三种情况: * - 从菜单拖拽组件到容器画布; * - 在容器中拖拽组件调整位置 * - 拖拽调整组件的宽度和高度; */ commander.useRegistry({ name: 'drag', init() { dragData.current = { before: null }; dragstart.on(handler.dragstart); dragend.on(handler.dragend); return () => { dragstart.off(handler.dragstart); dragend.off(handler.dragend); }; }, execute() { let before = deepcopy(dragData.current.before!); let after = deepcopy(value.blocks); return { redo: () => { updateBlocks(deepcopy(after)); }, undo: () => { updateBlocks(deepcopy(before)); }, }; }, }); })(); /*全选命令*/ commander.useRegistry({ name: 'selectAll', followQueue: false, keyboard: ['ctrl+a'], execute() { return { redo: () => { value.blocks.forEach((block) => (block.focus = true)); updateBlocks(value.blocks); }, }; }, }); /*置顶命令*/ commander.useRegistry({ name: 'placeTop', keyboard: 'ctrl+up', execute() { const before = deepcopy(value.blocks); const after = deepcopy( (() => { const { focus, unFocus } = focusData; const maxUnFocusIndex = unFocus.reduce( (prev, item) => Math.max(prev, item.zIndex), -Infinity, ); const minFocusIndex = focus.reduce((prev, item) => Math.min(prev, item.zIndex), Infinity); let dur = maxUnFocusIndex - minFocusIndex; if (dur >= 0) { dur++; focus.forEach((block) => (block.zIndex = block.zIndex + dur)); } return value.blocks; })(), ); return { redo: () => updateBlocks(after), undo: () => updateBlocks(before), }; }, }); /*置底命令*/ commander.useRegistry({ name: 'placeBottom', keyboard: 'ctrl+down', execute() { const before = deepcopy(value.blocks); const after = deepcopy( (() => { const { focus, unFocus } = focusData; const minUnFocusIndex = unFocus.reduce( (prev, item) => Math.min(prev, item.zIndex), Infinity, ); const maxFocusIndex = focus.reduce( (prev, item) => Math.max(prev, item.zIndex), -Infinity, ); const minFocusIndex = focus.reduce((prev, item) => Math.min(prev, item.zIndex), Infinity); let dur = maxFocusIndex - minUnFocusIndex; if (dur >= 0) { dur++; focus.forEach((block) => (block.zIndex = block.zIndex - dur)); if (minFocusIndex - dur < 0) { dur = dur - minFocusIndex; value.blocks.forEach((block) => (block.zIndex = block.zIndex + dur)); } } return value.blocks; })(), ); return { redo: () => updateBlocks(after), undo: () => updateBlocks(before), }; }, }); /*清空命令*/ commander.useRegistry({ name: 'clear', execute: () => { const before = deepcopy(value.blocks); const after = deepcopy([]); return { redo: () => updateBlocks(after), undo: () => updateBlocks(before), }; }, }); /** * 更新编辑数据的命令 */ commander.useRegistry({ name: 'updateValue', execute: (newVal: ReactVisualEditorValue) => { const before = deepcopy(value); const after = deepcopy(newVal); return { redo: () => updateValue(after), undo: () => updateValue(before), }; }, }); /** * 更新某个节点 */ commander.useRegistry({ name: 'updateBlock', execute: (newBlock: ReactVisualEditorBlock, oldBlock: ReactVisualEditorBlock) => { const before = deepcopy(value); value.blocks.splice(value.blocks.indexOf(oldBlock), 1, newBlock); const after = deepcopy(value); return { redo: () => updateValue(after), undo: () => updateValue(before), }; }, }); commander.useInit(); return { delete: () => commander.state.commands.delete(), undo: () => commander.state.commands.undo(), redo: () => commander.state.commands.redo(), placeTop: () => commander.state.commands.placeTop(), placeBottom: () => commander.state.commands.placeBottom(), clear: () => commander.state.commands.clear(), updateValue: (val: ReactVisualEditorValue) => commander.state.commands.updateValue(val), updateBlock: (newBlock: ReactVisualEditorBlock, oldBlock: ReactVisualEditorBlock) => commander.state.commands.updateBlock(newBlock, oldBlock), }; }