diff --git a/packages/core/src/commands/registry.ts b/packages/core/src/commands/registry.ts index 47b69fbd0..1d9776ad6 100644 --- a/packages/core/src/commands/registry.ts +++ b/packages/core/src/commands/registry.ts @@ -17,6 +17,7 @@ import type { ExportTemplateCommandRegistryRun, ExportTemplateCommandRegistrySto import type { OpenAssetsCommandRegistryRun, OpenAssetsCommandRegistryStop } from './view/OpenAssets'; import type { OpenLayersCommandRegistryRun, OpenLayersCommandRegistryStop } from './view/OpenLayers'; import type { OpenBlocksCommandRegistryRun, OpenBlocksCommandRegistryStop } from './view/OpenBlocks'; +import type { MoveComponentCommandRegistryRun, MoveComponentCommandRegistryStop } from './view/MoveComponent'; import type { ShowOffsetCommandRegistryRun, ShowOffsetCommandRegistryStop } from './view/ShowOffset'; import type { SwitchVisibilityCommandRegistryRun, SwitchVisibilityCommandRegistryStop } from './view/SwitchVisibility'; import type { @@ -44,6 +45,7 @@ export interface CommandRegistryRun CanvasMoveCommandRegistryRun, CanvasClearCommandRegistryRun, ExportTemplateCommandRegistryRun, + MoveComponentCommandRegistryRun, OpenAssetsCommandRegistryRun, OpenBlocksCommandRegistryRun, OpenLayersCommandRegistryRun, @@ -58,6 +60,7 @@ export interface CommandRegistryStop ResizeCommandRegistryStop, CanvasMoveCommandRegistryStop, ExportTemplateCommandRegistryStop, + MoveComponentCommandRegistryStop, OpenAssetsCommandRegistryStop, OpenBlocksCommandRegistryStop, OpenLayersCommandRegistryStop, diff --git a/packages/core/src/commands/view/MoveComponent.ts b/packages/core/src/commands/view/MoveComponent.ts index 0b065ec88..475fe3762 100644 --- a/packages/core/src/commands/view/MoveComponent.ts +++ b/packages/core/src/commands/view/MoveComponent.ts @@ -1,38 +1,55 @@ -import { bindAll, extend } from 'underscore'; +import { bindAll } from 'underscore'; import { $ } from '../../common'; -import Component from '../../dom_components/model/Component'; +import type Component from '../../dom_components/model/Component'; import { off, on } from '../../utils/dom'; -import { CommandObject } from './CommandAbstract'; +import type { CommandPublicFnFromHandler } from '../registryHelpers'; +import CommandAbstract from './CommandAbstract'; import SelectComponent from './SelectComponent'; import SelectPosition from './SelectPosition'; -export default extend({}, SelectPosition, SelectComponent, { +export interface MoveComponentCommandRegistryRun { + 'core:component-move': CommandPublicFnFromHandler; + 'move-comp': CommandPublicFnFromHandler; +} + +export interface MoveComponentCommandRegistryStop { + 'core:component-move': CommandPublicFnFromHandler; + 'move-comp': CommandPublicFnFromHandler; +} + +export default class CommandMoveComponent extends CommandAbstract { + [key: string]: any; + init(o: any) { - SelectComponent.init.apply(this, arguments); + (SelectComponent.init as any).apply(this, arguments as any); bindAll(this, 'initSorter', 'rollback', 'onEndMove'); this.opt = o; - this.hoverClass = this.ppfx + 'highlighter-warning'; - this.badgeClass = this.ppfx + 'badge-warning'; - this.noSelClass = this.ppfx + 'no-select'; - }, + this.hoverClass = `${this.ppfx}highlighter-warning`; + this.badgeClass = `${this.ppfx}badge-warning`; + this.noSelClass = `${this.ppfx}no-select`; + } + + run(...args: any[]) { + return (SelectPosition.run as any).apply(this, args); + } - enable(...args: any) { + enable(...args: any[]) { SelectComponent.enable.apply(this, args); this.getBadgeEl().addClass(this.badgeClass); this.getHighlighterEl().addClass(this.hoverClass); - var wp = this.$wrapper; + const wp = this.$wrapper; wp.css('cursor', 'move'); wp.on('mousedown', this.initSorter); // Avoid strange moving behavior wp.addClass(this.noSelClass); - }, + } /** * Overwrite for doing nothing * @private */ - toggleClipboard() {}, + toggleClipboard() {} /** * Delegate sorting @@ -40,8 +57,8 @@ export default extend({}, SelectPosition, SelectComponent, { * @private * */ initSorter(e: any) { - var el = $(e.target).data('model'); - var drag = el.get('draggable'); + const el = $(e.target).data('model'); + const drag = el.get('draggable'); if (!drag) return; // Avoid badge showing on move @@ -52,7 +69,7 @@ export default extend({}, SelectPosition, SelectComponent, { this.stopSelectComponent(); this.$wrapper.off('mousedown', this.initSorter); on(this.getContentWindow(), 'keydown', this.rollback); - }, + } /** * Init sorter from model @@ -60,27 +77,18 @@ export default extend({}, SelectPosition, SelectComponent, { * @private */ initSorterFromModel(model: Component) { - var drag = model.get('draggable'); + const drag = model.get('draggable'); if (!drag) return; // Avoid badge showing on move this.cacheEl = null; - // @ts-ignore - var el = model.view.el; + const el = model.view?.el; + if (!el) return; this.startSelectPosition(el, this.frameEl.contentDocument); this.sorter.draggable = drag; this.sorter.eventHandlers.legacyOnEndMove = this.onEndMoveFromModel.bind(this); - - /* - this.sorter.setDragHelper(el); - var dragHelper = this.sorter.dragHelper; - dragHelper.className = this.ppfx + 'drag-helper'; - dragHelper.innerHTML = ''; - dragHelper.backgroundColor = 'white'; - */ - this.stopSelectComponent(); on(this.getContentWindow(), 'keydown', this.rollback); - }, + } /** * Init sorter from models @@ -101,11 +109,11 @@ export default extend({}, SelectPosition, SelectComponent, { this.sorter.eventHandlers.legacyOnEndMove = this.onEndMoveFromModel.bind(this); this.stopSelectComponent(); on(this.getContentWindow(), 'keydown', this.rollback); - }, + } onEndMoveFromModel() { off(this.getContentWindow(), 'keydown', this.rollback); - }, + } /** * Callback after sorting @@ -114,7 +122,7 @@ export default extend({}, SelectPosition, SelectComponent, { onEndMove() { this.enable(); off(this.getContentWindow(), 'keydown', this.rollback); - }, + } /** * Say what to do after the component was selected (selectComponent) @@ -122,7 +130,7 @@ export default extend({}, SelectPosition, SelectComponent, { * @param {Object} Selected element * @private * */ - onSelect(e: any, el: any) {}, + onSelect(e: any, el: any) {} /** * Used to bring the previous situation before start moving the component @@ -130,13 +138,13 @@ export default extend({}, SelectPosition, SelectComponent, { * @param {Boolean} Indicates if rollback in anycase * @private * */ - rollback(e: any, force: boolean) { - var key = e.which || e.keyCode; + rollback(e: any, force?: boolean) { + const key = e.which || e.keyCode; if (key == 27 || force) { this.sorter.cancelDrag(); } return; - }, + } /** * Returns badge element @@ -146,7 +154,7 @@ export default extend({}, SelectPosition, SelectComponent, { getBadgeEl() { if (!this.$badge) this.$badge = $(this.getBadge()); return this.$badge; - }, + } /** * Returns highlighter element @@ -156,14 +164,24 @@ export default extend({}, SelectPosition, SelectComponent, { getHighlighterEl() { if (!this.$hl) this.$hl = $(this.canvas.getHighlighter()); return this.$hl; - }, + } - stop(...args) { - // @ts-ignore - SelectComponent.stop.apply(this, args); + stop(...args: any[]) { + (SelectComponent.stop as any).apply(this, args); this.getBadgeEl().removeClass(this.badgeClass); this.getHighlighterEl().removeClass(this.hoverClass); - var wp = this.$wrapper; + const wp = this.$wrapper; wp.css('cursor', '').unbind().removeClass(this.noSelClass); - }, -} as CommandObject<{}, { [k: string]: any }>); + } +} + +[ + SelectPosition as Record, + SelectComponent as Record, +].forEach((source) => { + Object.keys(source).forEach((key) => { + if (!(key in CommandMoveComponent.prototype)) { + (CommandMoveComponent.prototype as Record)[key] = source[key]; + } + }); +}); diff --git a/packages/core/test/specs/commands/view/MoveComponent.ts b/packages/core/test/specs/commands/view/MoveComponent.ts new file mode 100644 index 000000000..3ed79c60f --- /dev/null +++ b/packages/core/test/specs/commands/view/MoveComponent.ts @@ -0,0 +1,42 @@ +import MoveComponent from '../../../../src/commands/view/MoveComponent'; + +describe('MoveComponent command', () => { + test('rollback should cancel drag on escape', () => { + const command = new MoveComponent({}); + const cancelDrag = jest.fn(); + command.sorter = { cancelDrag }; + + command.rollback({ which: 27 }, false); + + expect(cancelDrag).toHaveBeenCalledTimes(1); + }); + + test('stop should reset wrapper and remove helper classes', () => { + const command = new MoveComponent({}); + const wrapper: any = {}; + wrapper.css = jest.fn(() => wrapper); + wrapper.unbind = jest.fn(() => wrapper); + wrapper.removeClass = jest.fn(() => wrapper); + const removeBadgeClass = jest.fn(); + const removeHighlighterClass = jest.fn(); + command.$wrapper = wrapper; + command.$badge = { removeClass: removeBadgeClass }; + command.$hl = { removeClass: removeHighlighterClass }; + command.badgeClass = 'badge-warning'; + command.hoverClass = 'highlighter-warning'; + command.noSelClass = 'no-select'; + command.onHovered = jest.fn(); + command.stopSelectComponent = jest.fn(); + command.em = { setSelected: jest.fn() } as any; + command.toggleToolsEl = jest.fn(); + command.editor = { stopCommand: jest.fn() }; + + command.stop(); + + expect(removeBadgeClass).toHaveBeenCalledWith('badge-warning'); + expect(removeHighlighterClass).toHaveBeenCalledWith('highlighter-warning'); + expect(wrapper.css).toHaveBeenCalledWith('cursor', ''); + expect(wrapper.unbind).toHaveBeenCalledTimes(1); + expect(wrapper.removeClass).toHaveBeenCalledWith('no-select'); + }); +});