|
|
@ -1,18 +1,38 @@ |
|
|
import { keys, bindAll, each, isUndefined, debounce } from 'underscore'; |
|
|
import { keys, bindAll, each, isUndefined, debounce } from 'underscore'; |
|
|
import { CanvasEvents } from '../../canvas/types'; |
|
|
import { CanvasEvents } from '../../canvas/types'; |
|
|
import Dragger, { DraggerOptions } from '../../utils/Dragger'; |
|
|
import Dragger, { DraggerOptions } from '../../utils/Dragger'; |
|
|
import type { CommandObject } from './CommandAbstract'; |
|
|
|
|
|
import type Editor from '../../editor'; |
|
|
import type Editor from '../../editor'; |
|
|
import type Component from '../../dom_components/model/Component'; |
|
|
import type Component from '../../dom_components/model/Component'; |
|
|
import type EditorModel from '../../editor/model/Editor'; |
|
|
import type EditorModel from '../../editor/model/Editor'; |
|
|
import { getComponentModel, getComponentView } from '../../utils/mixins'; |
|
|
import { getComponentModel, getComponentView } from '../../utils/mixins'; |
|
|
import type ComponentView from '../../dom_components/view/ComponentView'; |
|
|
import type ComponentView from '../../dom_components/view/ComponentView'; |
|
|
import type CommandAbstract from './CommandAbstract'; |
|
|
import type { CommandPublicFnFromHandler } from '../registryHelpers'; |
|
|
|
|
|
import CommandAbstract from './CommandAbstract'; |
|
|
|
|
|
|
|
|
const evName = 'dmode'; |
|
|
const evName = 'dmode'; |
|
|
|
|
|
|
|
|
export default { |
|
|
export interface ComponentDragCommandRegistryRun { |
|
|
run(editor, _sender, opts = {} as ComponentDragOpts) { |
|
|
'core:component-drag': CommandPublicFnFromHandler<CommandComponentDrag['run']>; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export default class CommandComponentDrag extends CommandAbstract<ComponentDragOpts> { |
|
|
|
|
|
editor!: Editor; |
|
|
|
|
|
em!: EditorModel; |
|
|
|
|
|
opts!: ComponentDragOpts; |
|
|
|
|
|
target!: Component; |
|
|
|
|
|
guides?: ComponentDragGuide[]; |
|
|
|
|
|
guidesContainer?: HTMLElement; |
|
|
|
|
|
guidesEl?: HTMLElement; |
|
|
|
|
|
guidesStatic?: ComponentDragGuide[]; |
|
|
|
|
|
guidesTarget?: ComponentDragGuide[]; |
|
|
|
|
|
isTran?: boolean; |
|
|
|
|
|
elGuideInfoX?: HTMLElement; |
|
|
|
|
|
elGuideInfoY?: HTMLElement; |
|
|
|
|
|
elGuideInfoContentX?: HTMLElement; |
|
|
|
|
|
elGuideInfoContentY?: HTMLElement; |
|
|
|
|
|
dragger?: Dragger; |
|
|
|
|
|
|
|
|
|
|
|
run(editor: Editor, _sender: any, opts = {} as ComponentDragOpts) { |
|
|
bindAll( |
|
|
bindAll( |
|
|
this, |
|
|
this, |
|
|
'setPosition', |
|
|
'setPosition', |
|
|
@ -62,24 +82,24 @@ export default { |
|
|
this.em.trigger(`${evName}:start`, this.getEventOpts()); |
|
|
this.em.trigger(`${evName}:start`, this.getEventOpts()); |
|
|
|
|
|
|
|
|
return drg; |
|
|
return drg; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getEventOpts() { |
|
|
getEventOpts(): ComponentDragEventProps { |
|
|
const guidesActive = this.guidesTarget?.filter((item) => item.active) ?? []; |
|
|
const guidesActive = this.guidesTarget?.filter((item) => item.active) ?? []; |
|
|
return { |
|
|
return { |
|
|
mode: this.opts.mode, |
|
|
mode: this.opts.mode, |
|
|
component: this.target, |
|
|
component: this.target, |
|
|
target: this.target, |
|
|
target: this.target, |
|
|
guidesTarget: this.guidesTarget, |
|
|
guidesTarget: this.guidesTarget ?? [], |
|
|
guidesStatic: this.guidesStatic, |
|
|
guidesStatic: this.guidesStatic ?? [], |
|
|
guidesMatched: this.getGuidesMatched(guidesActive), |
|
|
guidesMatched: this.getGuidesMatched(guidesActive), |
|
|
command: this, |
|
|
command: this, |
|
|
}; |
|
|
}; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
stop() { |
|
|
stop() { |
|
|
this.toggleDrag(); |
|
|
this.toggleDrag(); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
setupGuides() { |
|
|
setupGuides() { |
|
|
(this.guides ?? []).forEach((item) => { |
|
|
(this.guides ?? []).forEach((item) => { |
|
|
@ -87,7 +107,7 @@ export default { |
|
|
guide?.parentNode?.removeChild(guide); |
|
|
guide?.parentNode?.removeChild(guide); |
|
|
}); |
|
|
}); |
|
|
this.guides = []; |
|
|
this.guides = []; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getGuidesContainer() { |
|
|
getGuidesContainer() { |
|
|
let { guidesEl } = this; |
|
|
let { guidesEl } = this; |
|
|
@ -125,7 +145,7 @@ export default { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return guidesEl; |
|
|
return guidesEl; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getGuidesStatic() { |
|
|
getGuidesStatic() { |
|
|
let result: ComponentDragGuide[] = []; |
|
|
let result: ComponentDragGuide[] = []; |
|
|
@ -138,19 +158,19 @@ export default { |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
return result.concat(this.getElementGuides(parentNode)); |
|
|
return result.concat(this.getElementGuides(parentNode)); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getGuidesTarget() { |
|
|
getGuidesTarget() { |
|
|
return this.getElementGuides(this.target.getEl()!); |
|
|
return this.getElementGuides(this.target.getEl()!); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
updateGuides(guides) { |
|
|
updateGuides(guides?: ComponentDragGuide[]) { |
|
|
let lastEl: HTMLElement; |
|
|
let lastEl: HTMLElement | undefined; |
|
|
let lastPos: ComponentOrigRect; |
|
|
let lastPos: ComponentOrigRect | undefined; |
|
|
const guidesToUpdate = guides ?? this.guides ?? []; |
|
|
const guidesToUpdate = guides ?? this.guides ?? []; |
|
|
guidesToUpdate.forEach((item) => { |
|
|
guidesToUpdate.forEach((item) => { |
|
|
const { origin } = item; |
|
|
const { origin } = item; |
|
|
const pos = lastEl === origin ? lastPos : this.getElementPos(origin); |
|
|
const pos = lastEl === origin ? lastPos! : this.getElementPos(origin); |
|
|
lastEl = origin; |
|
|
lastEl = origin; |
|
|
lastPos = pos; |
|
|
lastPos = pos; |
|
|
each(this.getGuidePosUpdate(item, pos), (val, key) => { |
|
|
each(this.getGuidePosUpdate(item, pos), (val, key) => { |
|
|
@ -158,9 +178,9 @@ export default { |
|
|
}); |
|
|
}); |
|
|
item.originRect = pos; |
|
|
item.originRect = pos; |
|
|
}); |
|
|
}); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getGuidePosUpdate(item, rect) { |
|
|
getGuidePosUpdate(item: ComponentDragGuide, rect: ComponentOrigRect) { |
|
|
const result: { x?: number; y?: number } = {}; |
|
|
const result: { x?: number; y?: number } = {}; |
|
|
const { top, height, left, width } = rect; |
|
|
const { top, height, left, width } = rect; |
|
|
|
|
|
|
|
|
@ -186,9 +206,9 @@ export default { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return result; |
|
|
return result; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
renderGuide(item) { |
|
|
renderGuide(item: { active?: boolean; guide?: HTMLElement; x?: number; y?: number }) { |
|
|
if (this.opts.skipGuidesRender) return; |
|
|
if (this.opts.skipGuidesRender) return; |
|
|
const el = item.guide ?? document.createElement('div'); |
|
|
const el = item.guide ?? document.createElement('div'); |
|
|
const un = 'px'; |
|
|
const un = 'px'; |
|
|
@ -216,13 +236,13 @@ export default { |
|
|
|
|
|
|
|
|
!item.guide && this.guidesContainer?.appendChild(el); |
|
|
!item.guide && this.guidesContainer?.appendChild(el); |
|
|
return el; |
|
|
return el; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getElementPos(el) { |
|
|
getElementPos(el: HTMLElement) { |
|
|
return this.editor.Canvas.getElementPos(el, { noScroll: 1 }); |
|
|
return this.editor.Canvas.getElementPos(el, { noScroll: 1 }); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getElementGuides(el) { |
|
|
getElementGuides(el: HTMLElement) { |
|
|
const { opts } = this; |
|
|
const { opts } = this; |
|
|
const origin = el; |
|
|
const origin = el; |
|
|
const originRect = this.getElementPos(el); |
|
|
const originRect = this.getElementPos(el); |
|
|
@ -231,12 +251,12 @@ export default { |
|
|
|
|
|
|
|
|
const { top, height, left, width } = originRect; |
|
|
const { top, height, left, width } = originRect; |
|
|
const guidePoints: { type: string; x?: number; y?: number }[] = [ |
|
|
const guidePoints: { type: string; x?: number; y?: number }[] = [ |
|
|
{ type: 't', y: top }, // Top
|
|
|
{ type: 't', y: top }, |
|
|
{ type: 'b', y: top + height }, // Bottom
|
|
|
{ type: 'b', y: top + height }, |
|
|
{ type: 'l', x: left }, // Left
|
|
|
{ type: 'l', x: left }, |
|
|
{ type: 'r', x: left + width }, // Right
|
|
|
{ type: 'r', x: left + width }, |
|
|
{ type: 'x', x: left + width / 2 }, // Mid x
|
|
|
{ type: 'x', x: left + width / 2 }, |
|
|
{ type: 'y', y: top + height / 2 }, // Mid y
|
|
|
{ type: 'y', y: top + height / 2 }, |
|
|
]; |
|
|
]; |
|
|
|
|
|
|
|
|
const guides = guidePoints.map((guidePoint) => { |
|
|
const guides = guidePoints.map((guidePoint) => { |
|
|
@ -252,14 +272,14 @@ export default { |
|
|
guideEl: guide, |
|
|
guideEl: guide, |
|
|
guide, |
|
|
guide, |
|
|
}; |
|
|
}; |
|
|
}) as ComponentDragGuide[]; |
|
|
}) as unknown as ComponentDragGuide[]; |
|
|
|
|
|
|
|
|
guides.forEach((guidePoint) => this.guides?.push(guidePoint)); |
|
|
guides.forEach((guidePoint) => this.guides?.push(guidePoint)); |
|
|
|
|
|
|
|
|
return guides; |
|
|
return guides; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getTranslate(transform, axis = 'x') { |
|
|
getTranslate(transform: string, axis = 'x') { |
|
|
let result = 0; |
|
|
let result = 0; |
|
|
(transform || '').split(' ').forEach((item) => { |
|
|
(transform || '').split(' ').forEach((item) => { |
|
|
const itemStr = item.trim(); |
|
|
const itemStr = item.trim(); |
|
|
@ -267,9 +287,9 @@ export default { |
|
|
if (itemStr.indexOf(fn) === 0) result = parseFloat(itemStr.replace(fn, '')); |
|
|
if (itemStr.indexOf(fn) === 0) result = parseFloat(itemStr.replace(fn, '')); |
|
|
}); |
|
|
}); |
|
|
return result; |
|
|
return result; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
setTranslate(transform, axis, value) { |
|
|
setTranslate(transform: string, axis: string, value: string) { |
|
|
const fn = `translate${axis.toUpperCase()}(`; |
|
|
const fn = `translate${axis.toUpperCase()}(`; |
|
|
const val = `${fn}${value})`; |
|
|
const val = `${fn}${value})`; |
|
|
let result = (transform || '') |
|
|
let result = (transform || '') |
|
|
@ -283,7 +303,7 @@ export default { |
|
|
if (result.indexOf(fn) < 0) result += ` ${val}`; |
|
|
if (result.indexOf(fn) < 0) result += ` ${val}`; |
|
|
|
|
|
|
|
|
return result; |
|
|
return result; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getPosition() { |
|
|
getPosition() { |
|
|
const { target, isTran } = this; |
|
|
const { target, isTran } = this; |
|
|
@ -305,12 +325,12 @@ export default { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return { x, y }; |
|
|
return { x, y }; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
setPosition({ x, y, end, position, width, height }) { |
|
|
setPosition({ x, y, end, position, width, height }: any) { |
|
|
const { target, isTran, em, opts } = this; |
|
|
const { target, isTran, em, opts } = this; |
|
|
const unit = 'px'; |
|
|
const unit = 'px'; |
|
|
const __p = !end; // Indicate if partial change
|
|
|
const __p = !end; |
|
|
const left = `${parseInt(`${x}`, 10)}${unit}`; |
|
|
const left = `${parseInt(`${x}`, 10)}${unit}`; |
|
|
const top = `${parseInt(`${y}`, 10)}${unit}`; |
|
|
const top = `${parseInt(`${y}`, 10)}${unit}`; |
|
|
let styleUp = {}; |
|
|
let styleUp = {}; |
|
|
@ -337,7 +357,7 @@ export default { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
em.Styles.__emitCmpStyleUpdate(styleUp, { components: em.getSelected() }); |
|
|
em.Styles.__emitCmpStyleUpdate(styleUp, { components: em.getSelected() }); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
_getDragData() { |
|
|
_getDragData() { |
|
|
const { target } = this; |
|
|
const { target } = this; |
|
|
@ -346,9 +366,9 @@ export default { |
|
|
parent: target.parent(), |
|
|
parent: target.parent(), |
|
|
index: target.index(), |
|
|
index: target.index(), |
|
|
}; |
|
|
}; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
onStart(event) { |
|
|
onStart(event: Event) { |
|
|
const { target, editor, isTran, opts } = this; |
|
|
const { target, editor, isTran, opts } = this; |
|
|
const { Canvas } = editor; |
|
|
const { Canvas } = editor; |
|
|
const style = target.getStyle(); |
|
|
const style = target.getStyle(); |
|
|
@ -362,17 +382,15 @@ export default { |
|
|
let parent = target.parent(); |
|
|
let parent = target.parent(); |
|
|
let parentRel = null; |
|
|
let parentRel = null; |
|
|
|
|
|
|
|
|
// Check for the relative parent
|
|
|
|
|
|
do { |
|
|
do { |
|
|
const pStyle = parent?.getStyle(); |
|
|
const pStyle = parent?.getStyle(); |
|
|
const position = pStyle?.position as string | undefined; |
|
|
const parentPosition = pStyle?.position as string | undefined; |
|
|
if (position) { |
|
|
if (parentPosition) { |
|
|
parentRel = relPos.indexOf(position) >= 0 ? parent : null; |
|
|
parentRel = relPos.indexOf(parentPosition) >= 0 ? parent : null; |
|
|
} |
|
|
} |
|
|
parent = parent?.parent(); |
|
|
parent = parent?.parent(); |
|
|
} while (parent && !parentRel); |
|
|
} while (parent && !parentRel); |
|
|
|
|
|
|
|
|
// Center the target to the pointer position (used in Droppable for Blocks)
|
|
|
|
|
|
if (opts.center) { |
|
|
if (opts.center) { |
|
|
const { x, y } = Canvas.getMouseRelativeCanvas(event as MouseEvent); |
|
|
const { x, y } = Canvas.getMouseRelativeCanvas(event as MouseEvent); |
|
|
left = x; |
|
|
left = x; |
|
|
@ -392,11 +410,10 @@ export default { |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Recalculate guides to avoid issues with the new position durin the first drag
|
|
|
|
|
|
this.guidesStatic = this.getGuidesStatic(); |
|
|
this.guidesStatic = this.getGuidesStatic(); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
onDrag(event) { |
|
|
onDrag(event: Event) { |
|
|
const { guidesTarget, opts } = this; |
|
|
const { guidesTarget, opts } = this; |
|
|
|
|
|
|
|
|
this.updateGuides(guidesTarget); |
|
|
this.updateGuides(guidesTarget); |
|
|
@ -406,25 +423,25 @@ export default { |
|
|
|
|
|
|
|
|
this.opts.event = event; |
|
|
this.opts.event = event; |
|
|
this.em.trigger(`${evName}:move`, this.getEventOpts()); |
|
|
this.em.trigger(`${evName}:move`, this.getEventOpts()); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
onEnd(ev, _dragger, opt) { |
|
|
onEnd(ev: Event, _dragger: any, opt: any) { |
|
|
const { editor, opts, id } = this; |
|
|
const { editor, opts, id } = this; |
|
|
opts.onEnd?.(ev, opt, { event: ev, ...opt, ...this._getDragData() }); |
|
|
opts.onEnd?.(ev, opt, { event: ev, ...opt, ...this._getDragData() }); |
|
|
editor.stopCommand(`${id}`); |
|
|
editor.stopCommand(`${id}`); |
|
|
this.hideGuidesInfo(); |
|
|
this.hideGuidesInfo(); |
|
|
|
|
|
|
|
|
this.em.trigger(`${evName}:end`, this.getEventOpts()); |
|
|
this.em.trigger(`${evName}:end`, this.getEventOpts()); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
hideGuidesInfo() { |
|
|
hideGuidesInfo() { |
|
|
['X', 'Y'].forEach((item) => { |
|
|
['X', 'Y'].forEach((item) => { |
|
|
const guide = this[`elGuideInfo${item}` as ElGuideInfoKey]; |
|
|
const guide = this[`elGuideInfo${item}` as ElGuideInfoKey]; |
|
|
if (guide) guide.style.display = 'none'; |
|
|
if (guide) guide.style.display = 'none'; |
|
|
}); |
|
|
}); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
renderGuideInfo(guides = []) { |
|
|
renderGuideInfo(guides: ComponentDragGuide[] = []) { |
|
|
this.hideGuidesInfo(); |
|
|
this.hideGuidesInfo(); |
|
|
|
|
|
|
|
|
const guidesMatched = this.getGuidesMatched(guides); |
|
|
const guidesMatched = this.getGuidesMatched(guides); |
|
|
@ -439,9 +456,9 @@ export default { |
|
|
...guideMatched, |
|
|
...guideMatched, |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
renderSingleGuideInfo(guideMatched) { |
|
|
renderSingleGuideInfo(guideMatched: ComponentDragGuideMatched) { |
|
|
const { posFirst, posSecond, size, sizeRaw, guide, elGuideInfo, elGuideInfoCnt } = guideMatched; |
|
|
const { posFirst, posSecond, size, sizeRaw, guide, elGuideInfo, elGuideInfoCnt } = guideMatched; |
|
|
|
|
|
|
|
|
const axis = isUndefined(guide.x) ? 'y' : 'x'; |
|
|
const axis = isUndefined(guide.x) ? 'y' : 'x'; |
|
|
@ -455,9 +472,9 @@ export default { |
|
|
guideInfoStyle[isY ? 'width' : 'height'] = `${size}px`; |
|
|
guideInfoStyle[isY ? 'width' : 'height'] = `${size}px`; |
|
|
|
|
|
|
|
|
elGuideInfoCnt.innerHTML = `${Math.round(sizeRaw)}px`; |
|
|
elGuideInfoCnt.innerHTML = `${Math.round(sizeRaw)}px`; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
getGuidesMatched(guides = []) { |
|
|
getGuidesMatched(guides: ComponentDragGuide[] = []) { |
|
|
const { guidesStatic = [] } = this; |
|
|
const { guidesStatic = [] } = this; |
|
|
return guides |
|
|
return guides |
|
|
.map((guide) => { |
|
|
.map((guide) => { |
|
|
@ -466,26 +483,22 @@ export default { |
|
|
const axis = isUndefined(x) ? 'y' : 'x'; |
|
|
const axis = isUndefined(x) ? 'y' : 'x'; |
|
|
const isY = axis === 'y'; |
|
|
const isY = axis === 'y'; |
|
|
|
|
|
|
|
|
// Calculate the edges of the element
|
|
|
|
|
|
const origEdge1 = rectOrigin[isY ? 'left' : 'top']; |
|
|
const origEdge1 = rectOrigin[isY ? 'left' : 'top']; |
|
|
const origEdge1Raw = rectOrigin.rect[isY ? 'left' : 'top']; |
|
|
const origEdge1Raw = rectOrigin.rect[isY ? 'left' : 'top']; |
|
|
const origEdge2 = isY ? origEdge1 + rectOrigin.width : origEdge1 + rectOrigin.height; |
|
|
const origEdge2 = isY ? origEdge1 + rectOrigin.width : origEdge1 + rectOrigin.height; |
|
|
const origEdge2Raw = isY ? origEdge1Raw + rectOrigin.rect.width : origEdge1Raw + rectOrigin.rect.height; |
|
|
const origEdge2Raw = isY ? origEdge1Raw + rectOrigin.rect.width : origEdge1Raw + rectOrigin.rect.height; |
|
|
|
|
|
|
|
|
// Find the nearest element
|
|
|
|
|
|
const guidesMatched = guidesStatic |
|
|
const guidesMatched = guidesStatic |
|
|
.filter((guideStatic) => { |
|
|
.filter((guideStatic) => { |
|
|
// Define complementary guide types
|
|
|
|
|
|
const complementaryTypes: Record<string, string[]> = { |
|
|
const complementaryTypes: Record<string, string[]> = { |
|
|
l: ['r', 'x'], // Left can match with Right or Middle (horizontal)
|
|
|
l: ['r', 'x'], |
|
|
r: ['l', 'x'], // Right can match with Left or Middle (horizontal)
|
|
|
r: ['l', 'x'], |
|
|
x: ['l', 'r'], // Middle (horizontal) can match with Left or Right
|
|
|
x: ['l', 'r'], |
|
|
t: ['b', 'y'], // Top can match with Bottom or Middle (vertical)
|
|
|
t: ['b', 'y'], |
|
|
b: ['t', 'y'], // Bottom can match with Top or Middle (vertical)
|
|
|
b: ['t', 'y'], |
|
|
y: ['t', 'b'], // Middle (vertical) can match with Top or Bottom
|
|
|
y: ['t', 'b'], |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// Check if the guide type matches or is complementary
|
|
|
|
|
|
return guideStatic.type === guide.type || complementaryTypes[guide.type]?.includes(guideStatic.type); |
|
|
return guideStatic.type === guide.type || complementaryTypes[guide.type]?.includes(guideStatic.type); |
|
|
}) |
|
|
}) |
|
|
.map((guideStatic) => { |
|
|
.map((guideStatic) => { |
|
|
@ -500,7 +513,6 @@ export default { |
|
|
.filter((item) => item.gap > 0) |
|
|
.filter((item) => item.gap > 0) |
|
|
.sort((a, b) => a.gap - b.gap) |
|
|
.sort((a, b) => a.gap - b.gap) |
|
|
.map((item) => item.guide) |
|
|
.map((item) => item.guide) |
|
|
// Filter the guides that don't match the position of the dragged element
|
|
|
|
|
|
.filter((item) => { |
|
|
.filter((item) => { |
|
|
switch (guide.type) { |
|
|
switch (guide.type) { |
|
|
case 'l': |
|
|
case 'l': |
|
|
@ -516,7 +528,6 @@ export default { |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// TODO: consider supporting multiple guides
|
|
|
|
|
|
const firstGuideMatched = guidesMatched[0]; |
|
|
const firstGuideMatched = guidesMatched[0]; |
|
|
|
|
|
|
|
|
if (firstGuideMatched) { |
|
|
if (firstGuideMatched) { |
|
|
@ -550,9 +561,9 @@ export default { |
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
.filter(Boolean) as ComponentDragGuideMatched[]; |
|
|
.filter(Boolean) as ComponentDragGuideMatched[]; |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
toggleDrag(enable) { |
|
|
toggleDrag(enable?: boolean) { |
|
|
const { ppfx, editor } = this; |
|
|
const { ppfx, editor } = this; |
|
|
const methodCls = enable ? 'add' : 'remove'; |
|
|
const methodCls = enable ? 'add' : 'remove'; |
|
|
const classes = [`${ppfx}is__grabbing`]; |
|
|
const classes = [`${ppfx}is__grabbing`]; |
|
|
@ -560,56 +571,7 @@ export default { |
|
|
const body = Canvas.getBody(); |
|
|
const body = Canvas.getBody(); |
|
|
classes.forEach((cls) => body.classList[methodCls](cls)); |
|
|
classes.forEach((cls) => body.classList[methodCls](cls)); |
|
|
Canvas[enable ? 'startAutoscroll' : 'stopAutoscroll'](); |
|
|
Canvas[enable ? 'startAutoscroll' : 'stopAutoscroll'](); |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
// These properties values are set in the run method, they need to be initialized here to avoid TS errors
|
|
|
|
|
|
editor: undefined as unknown as Editor, |
|
|
|
|
|
em: undefined as unknown as EditorModel, |
|
|
|
|
|
opts: undefined as unknown as ComponentDragOpts, |
|
|
|
|
|
target: undefined as unknown as Component, |
|
|
|
|
|
} as CommandObject<ComponentDragOpts, ComponentDragProps>; |
|
|
|
|
|
|
|
|
|
|
|
interface ComponentDragProps { |
|
|
|
|
|
editor: Editor; |
|
|
|
|
|
em?: EditorModel; |
|
|
|
|
|
guides?: ComponentDragGuide[]; |
|
|
|
|
|
guidesContainer?: HTMLElement; |
|
|
|
|
|
guidesEl?: HTMLElement; |
|
|
|
|
|
guidesStatic?: ComponentDragGuide[]; |
|
|
|
|
|
guidesTarget?: ComponentDragGuide[]; |
|
|
|
|
|
isTran?: boolean; |
|
|
|
|
|
opts: ComponentDragOpts; |
|
|
|
|
|
target: Component; |
|
|
|
|
|
elGuideInfoX?: HTMLElement; |
|
|
|
|
|
elGuideInfoY?: HTMLElement; |
|
|
|
|
|
elGuideInfoContentX?: HTMLElement; |
|
|
|
|
|
elGuideInfoContentY?: HTMLElement; |
|
|
|
|
|
dragger?: Dragger; |
|
|
|
|
|
|
|
|
|
|
|
getEventOpts: () => ComponentDragEventProps; |
|
|
|
|
|
stop: () => void; |
|
|
|
|
|
setupGuides: () => void; |
|
|
|
|
|
getGuidesContainer: () => HTMLElement; |
|
|
|
|
|
getGuidesStatic: () => ComponentDragGuide[]; |
|
|
|
|
|
getGuidesTarget: () => ComponentDragGuide[]; |
|
|
|
|
|
updateGuides: (guides?: ComponentDragGuide[]) => void; |
|
|
|
|
|
getGuidePosUpdate: (item: ComponentDragGuide, rect: ComponentOrigRect) => { x?: number; y?: number }; |
|
|
|
|
|
renderGuide: (item: { active?: boolean; guide?: HTMLElement; x?: number; y?: number }) => HTMLElement; |
|
|
|
|
|
getElementPos: (el: HTMLElement) => ComponentOrigRect; |
|
|
|
|
|
getElementGuides: (el: HTMLElement) => ComponentDragGuide[]; |
|
|
|
|
|
getTranslate: (transform: string, axis?: string) => number; |
|
|
|
|
|
setTranslate: (transform: string, axis: string, value: string) => string; |
|
|
|
|
|
getPosition: DraggerOptions['getPosition']; |
|
|
|
|
|
setPosition: (data: any) => void; // TODO: fix any
|
|
|
|
|
|
_getDragData: () => { target: Component; parent?: Component; index?: number }; |
|
|
|
|
|
onStart: DraggerOptions['onStart']; |
|
|
|
|
|
onDrag: DraggerOptions['onDrag']; |
|
|
|
|
|
onEnd: DraggerOptions['onEnd']; |
|
|
|
|
|
hideGuidesInfo: () => void; |
|
|
|
|
|
renderGuideInfo: (guides?: ComponentDragGuide[]) => void; |
|
|
|
|
|
renderSingleGuideInfo: (guideMatched: ComponentDragGuideMatched) => void; |
|
|
|
|
|
getGuidesMatched: (guides?: ComponentDragGuide[]) => ComponentDragGuideMatched[]; |
|
|
|
|
|
toggleDrag: (enable?: boolean) => void; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
interface ComponentDragOpts { |
|
|
interface ComponentDragOpts { |
|
|
@ -622,8 +584,8 @@ interface ComponentDragOpts { |
|
|
mode?: 'absolute' | 'translate'; |
|
|
mode?: 'absolute' | 'translate'; |
|
|
skipGuidesRender?: boolean; |
|
|
skipGuidesRender?: boolean; |
|
|
addStyle?: (data: { component?: Component; styles?: Record<string, unknown>; partial?: boolean }) => void; |
|
|
addStyle?: (data: { component?: Component; styles?: Record<string, unknown>; partial?: boolean }) => void; |
|
|
onStart?: (data: any) => Editor; |
|
|
onStart?: (data: any) => any; |
|
|
onDrag?: (data: any) => Editor; |
|
|
onDrag?: (data: any) => any; |
|
|
onEnd?: (ev: Event, opt: any, data: any) => void; |
|
|
onEnd?: (ev: Event, opt: any, data: any) => void; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -631,141 +593,97 @@ interface ComponentDragOpts { |
|
|
* Represents the properties of the drag events. |
|
|
* Represents the properties of the drag events. |
|
|
*/ |
|
|
*/ |
|
|
export interface ComponentDragEventProps { |
|
|
export interface ComponentDragEventProps { |
|
|
/** |
|
|
|
|
|
* The mode of the drag (absolute or translate). |
|
|
|
|
|
*/ |
|
|
|
|
|
mode: ComponentDragOpts['mode']; |
|
|
mode: ComponentDragOpts['mode']; |
|
|
/** |
|
|
|
|
|
* The component being dragged. |
|
|
|
|
|
* @deprecated Use `component` instead. |
|
|
|
|
|
*/ |
|
|
|
|
|
target: Component; |
|
|
target: Component; |
|
|
/** |
|
|
|
|
|
* The component being dragged. |
|
|
|
|
|
*/ |
|
|
|
|
|
component: Component; |
|
|
component: Component; |
|
|
/** |
|
|
|
|
|
* The guides of the component being dragged. |
|
|
|
|
|
* @deprecated Use `guidesMatched` instead. |
|
|
|
|
|
*/ |
|
|
|
|
|
guidesTarget: ComponentDragGuide[]; |
|
|
guidesTarget: ComponentDragGuide[]; |
|
|
/** |
|
|
|
|
|
* All the guides except the ones of the component being dragged. |
|
|
|
|
|
* @deprecated Use `guidesMatched` instead. |
|
|
|
|
|
*/ |
|
|
|
|
|
guidesStatic: ComponentDragGuide[]; |
|
|
guidesStatic: ComponentDragGuide[]; |
|
|
/** |
|
|
|
|
|
* The guides that are being matched. |
|
|
|
|
|
*/ |
|
|
|
|
|
guidesMatched: ComponentDragGuideMatched[]; |
|
|
guidesMatched: ComponentDragGuideMatched[]; |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* The options used for the drag event. |
|
|
|
|
|
*/ |
|
|
|
|
|
command: ComponentDragProps & CommandAbstract<ComponentDragOpts>; |
|
|
command: ComponentDragProps & CommandAbstract<ComponentDragOpts>; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Represents a guide used during component dragging. |
|
|
|
|
|
*/ |
|
|
|
|
|
interface ComponentDragGuide { |
|
|
interface ComponentDragGuide { |
|
|
/** |
|
|
|
|
|
* The type of the guide (e.g., 't', 'b', 'l', 'r', 'x', 'y'). |
|
|
|
|
|
*/ |
|
|
|
|
|
type: string; |
|
|
type: string; |
|
|
/** |
|
|
|
|
|
* The vertical position of the guide. |
|
|
|
|
|
*/ |
|
|
|
|
|
y: number; |
|
|
y: number; |
|
|
/** |
|
|
|
|
|
* The horizontal position of the guide. |
|
|
|
|
|
*/ |
|
|
|
|
|
x: number; |
|
|
x: number; |
|
|
/** |
|
|
|
|
|
* The component associated with the guide. |
|
|
|
|
|
*/ |
|
|
|
|
|
component: Component; |
|
|
component: Component; |
|
|
/** |
|
|
|
|
|
* The view of the component associated with the guide. |
|
|
|
|
|
*/ |
|
|
|
|
|
componentView: ComponentView; |
|
|
componentView: ComponentView; |
|
|
/** |
|
|
|
|
|
* The HTML element associated with the guide. |
|
|
|
|
|
* @deprecated Use `componentEl` instead. |
|
|
|
|
|
*/ |
|
|
|
|
|
origin: HTMLElement; |
|
|
origin: HTMLElement; |
|
|
/** |
|
|
|
|
|
* The HTML element associated with the guide. |
|
|
|
|
|
*/ |
|
|
|
|
|
componentEl: HTMLElement; |
|
|
componentEl: HTMLElement; |
|
|
/** |
|
|
|
|
|
* The rectangle (position and dimensions) of the guide's element. |
|
|
|
|
|
* @deprecated Use `componentElRect` instead. |
|
|
|
|
|
*/ |
|
|
|
|
|
originRect: ComponentOrigRect; |
|
|
originRect: ComponentOrigRect; |
|
|
/** |
|
|
|
|
|
* The rectangle (position and dimensions) of the guide's element. |
|
|
|
|
|
*/ |
|
|
|
|
|
componentElRect: ComponentOrigRect; |
|
|
componentElRect: ComponentOrigRect; |
|
|
/** |
|
|
|
|
|
* The HTML element representing the guide. |
|
|
|
|
|
* @deprecated Use `guideEl` instead. |
|
|
|
|
|
*/ |
|
|
|
|
|
guide?: HTMLElement; |
|
|
guide?: HTMLElement; |
|
|
/** |
|
|
|
|
|
* The HTML element representing the guide. |
|
|
|
|
|
*/ |
|
|
|
|
|
guideEl?: HTMLElement; |
|
|
guideEl?: HTMLElement; |
|
|
/** |
|
|
|
|
|
* Indicates whether the guide is active. |
|
|
|
|
|
* @todo The `active` property is not set in the code, but the value is changing. |
|
|
|
|
|
*/ |
|
|
|
|
|
active?: boolean; |
|
|
active?: boolean; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Represents a matched guide during component dragging. |
|
|
|
|
|
*/ |
|
|
|
|
|
interface ComponentDragGuideMatched { |
|
|
interface ComponentDragGuideMatched { |
|
|
/** |
|
|
|
|
|
* The static guides used for matching. |
|
|
|
|
|
*/ |
|
|
|
|
|
guidesStatic: ComponentDragGuide[]; |
|
|
guidesStatic: ComponentDragGuide[]; |
|
|
/** |
|
|
|
|
|
* The origin component guide. |
|
|
|
|
|
*/ |
|
|
|
|
|
guide: ComponentDragGuide; |
|
|
guide: ComponentDragGuide; |
|
|
/** |
|
|
|
|
|
* The matched component guide. |
|
|
|
|
|
*/ |
|
|
|
|
|
matched: ComponentDragGuide; |
|
|
matched: ComponentDragGuide; |
|
|
/** |
|
|
|
|
|
* The primary position of the guide (either x or y depending on the axis). |
|
|
|
|
|
*/ |
|
|
|
|
|
posFirst: number; |
|
|
posFirst: number; |
|
|
/** |
|
|
|
|
|
* The secondary position of the guide (the opposite axis of posFirst). |
|
|
|
|
|
*/ |
|
|
|
|
|
posSecond: number; |
|
|
posSecond: number; |
|
|
/** |
|
|
|
|
|
* The distance between the two matched guides in pixels. |
|
|
|
|
|
*/ |
|
|
|
|
|
size: number; |
|
|
size: number; |
|
|
/** |
|
|
|
|
|
* The raw distance between the two matched guides in pixels. |
|
|
|
|
|
*/ |
|
|
|
|
|
sizeRaw: number; |
|
|
sizeRaw: number; |
|
|
/** |
|
|
|
|
|
* The HTML element representing the guide info (line between the guides). |
|
|
|
|
|
*/ |
|
|
|
|
|
elGuideInfo: HTMLElement; |
|
|
elGuideInfo: HTMLElement; |
|
|
/** |
|
|
|
|
|
* The container element for the guide info (text content of the line). |
|
|
|
|
|
*/ |
|
|
|
|
|
elGuideInfoCnt: HTMLElement; |
|
|
elGuideInfoCnt: HTMLElement; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
type ComponentRect = { left: number; width: number; top: number; height: number }; |
|
|
interface ComponentOrigRect { |
|
|
type ComponentOrigRect = ComponentRect & { rect: ComponentRect }; |
|
|
top: number; |
|
|
|
|
|
left: number; |
|
|
|
|
|
width: number; |
|
|
|
|
|
height: number; |
|
|
|
|
|
rect: { |
|
|
|
|
|
top: number; |
|
|
|
|
|
left: number; |
|
|
|
|
|
width: number; |
|
|
|
|
|
height: number; |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
type ElGuideInfoKey = 'elGuideInfoX' | 'elGuideInfoY'; |
|
|
type ElGuideInfoKey = 'elGuideInfoX' | 'elGuideInfoY'; |
|
|
type ElGuideInfoContentKey = 'elGuideInfoContentX' | 'elGuideInfoContentY'; |
|
|
type ElGuideInfoContentKey = 'elGuideInfoContentX' | 'elGuideInfoContentY'; |
|
|
|
|
|
|
|
|
|
|
|
interface ComponentDragProps { |
|
|
|
|
|
editor: Editor; |
|
|
|
|
|
em?: EditorModel; |
|
|
|
|
|
guides?: ComponentDragGuide[]; |
|
|
|
|
|
guidesContainer?: HTMLElement; |
|
|
|
|
|
guidesEl?: HTMLElement; |
|
|
|
|
|
guidesStatic?: ComponentDragGuide[]; |
|
|
|
|
|
guidesTarget?: ComponentDragGuide[]; |
|
|
|
|
|
isTran?: boolean; |
|
|
|
|
|
opts: ComponentDragOpts; |
|
|
|
|
|
target: Component; |
|
|
|
|
|
elGuideInfoX?: HTMLElement; |
|
|
|
|
|
elGuideInfoY?: HTMLElement; |
|
|
|
|
|
elGuideInfoContentX?: HTMLElement; |
|
|
|
|
|
elGuideInfoContentY?: HTMLElement; |
|
|
|
|
|
dragger?: Dragger; |
|
|
|
|
|
|
|
|
|
|
|
getEventOpts: () => ComponentDragEventProps; |
|
|
|
|
|
stop: () => void; |
|
|
|
|
|
setupGuides: () => void; |
|
|
|
|
|
getGuidesContainer: () => HTMLElement; |
|
|
|
|
|
getGuidesStatic: () => ComponentDragGuide[]; |
|
|
|
|
|
getGuidesTarget: () => ComponentDragGuide[]; |
|
|
|
|
|
updateGuides: (guides?: ComponentDragGuide[]) => void; |
|
|
|
|
|
getGuidePosUpdate: (item: ComponentDragGuide, rect: ComponentOrigRect) => { x?: number; y?: number }; |
|
|
|
|
|
renderGuide: (item: { active?: boolean; guide?: HTMLElement; x?: number; y?: number }) => HTMLElement | undefined; |
|
|
|
|
|
getElementPos: (el: HTMLElement) => ComponentOrigRect; |
|
|
|
|
|
getElementGuides: (el: HTMLElement) => ComponentDragGuide[]; |
|
|
|
|
|
getTranslate: (transform: string, axis?: string) => number; |
|
|
|
|
|
setTranslate: (transform: string, axis: string, value: string) => string; |
|
|
|
|
|
getPosition: DraggerOptions['getPosition']; |
|
|
|
|
|
setPosition: (data: any) => void; |
|
|
|
|
|
_getDragData: () => { target: Component; parent?: Component; index?: number }; |
|
|
|
|
|
onStart: DraggerOptions['onStart']; |
|
|
|
|
|
onDrag: DraggerOptions['onDrag']; |
|
|
|
|
|
onEnd: DraggerOptions['onEnd']; |
|
|
|
|
|
hideGuidesInfo: () => void; |
|
|
|
|
|
renderGuideInfo: (guides?: ComponentDragGuide[]) => void; |
|
|
|
|
|
renderSingleGuideInfo: (guideMatched: ComponentDragGuideMatched) => void; |
|
|
|
|
|
getGuidesMatched: (guides?: ComponentDragGuide[]) => ComponentDragGuideMatched[]; |
|
|
|
|
|
toggleDrag: (enable?: boolean) => void; |
|
|
|
|
|
} |
|
|
|