|
|
|
@ -100,6 +100,7 @@ import { ItemManagerModule } from '../abstract/Module'; |
|
|
|
import EditorModel from '../editor/model/Editor'; |
|
|
|
import { ComponentAdd, ComponentDefinition, ComponentDefinitionDefined } from './model/types'; |
|
|
|
import { AddOptions } from '../common'; |
|
|
|
import { isComponent } from '../utils/mixins'; |
|
|
|
|
|
|
|
export type ComponentEvent = |
|
|
|
| 'component:create' |
|
|
|
@ -139,6 +140,29 @@ export interface AddComponentTypeOptions { |
|
|
|
extendFnView?: string[]; |
|
|
|
} |
|
|
|
|
|
|
|
/** @private */ |
|
|
|
export enum CanMoveReason { |
|
|
|
/** |
|
|
|
* Invalid source. This is a default value and should be ignored in case the `result` is true |
|
|
|
*/ |
|
|
|
InvalidSource = 0, |
|
|
|
/** |
|
|
|
* Source doesn't accept target as destination. |
|
|
|
*/ |
|
|
|
SourceReject = 1, |
|
|
|
/** |
|
|
|
* Target doesn't accept source. |
|
|
|
*/ |
|
|
|
TargetReject = 2, |
|
|
|
} |
|
|
|
|
|
|
|
export interface CanMoveResult { |
|
|
|
result: boolean; |
|
|
|
reason: CanMoveReason; |
|
|
|
target: Component; |
|
|
|
source?: Component | null; |
|
|
|
} |
|
|
|
|
|
|
|
export default class ComponentManager extends ItemManagerModule<DomComponentsConfig, any> { |
|
|
|
componentTypes = [ |
|
|
|
{ |
|
|
|
@ -629,36 +653,44 @@ export default class ComponentManager extends ItemManagerModule<DomComponentsCon |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Check if the component can be moved inside another. |
|
|
|
* @param {[Component]} target The target Component is the one that is supposed to receive the source one. |
|
|
|
* @param {[Component]|String} source The source can be another Component or an HTML string. |
|
|
|
* @param {Number} [index] Index position. If not specified, the check will perform against appending the source to target. |
|
|
|
* Check if a component can be moved inside another one. |
|
|
|
* @param {[Component]} target The target component is the one that is supposed to receive the source one. |
|
|
|
* @param {[Component]|String} source The source can be another component, a component definition or an HTML string. |
|
|
|
* @param {Number} [index] Index position, if not specified, the check will be performed against appending the source to the target. |
|
|
|
* @returns {Object} Object containing the `result` (Boolean), `source`, `target` (as Components), and a `reason` (Number) with these meanings: |
|
|
|
* * `0` - Invalid source. This is a default value and should be ignored in case the `result` is true. |
|
|
|
* * `1` - Source doesn't accept target as destination. |
|
|
|
* * `2` - Target doesn't accept source. |
|
|
|
* @private |
|
|
|
* @example |
|
|
|
* const rootComponent = editor.getWrapper(); |
|
|
|
* const someComponent = editor.getSelected(); |
|
|
|
* |
|
|
|
* // Check with two components
|
|
|
|
* editor.Components.canMove(rootComponent, someComponent); |
|
|
|
* |
|
|
|
* // Check with component definition
|
|
|
|
* editor.Components.canMove(rootComponent, { tagName: 'a', draggable: false }); |
|
|
|
* |
|
|
|
* // Check with HTML string
|
|
|
|
* editor.Components.canMove(rootComponent, '<form>...</form>'); |
|
|
|
*/ |
|
|
|
canMove(target: Component, source?: Component, index?: number) { |
|
|
|
const at = index || index === 0 ? index : null; |
|
|
|
const result = { |
|
|
|
canMove(target: Component, source?: Component | ComponentDefinition | string, index?: number): CanMoveResult { |
|
|
|
const result: CanMoveResult = { |
|
|
|
result: false, |
|
|
|
reason: 0, |
|
|
|
reason: CanMoveReason.InvalidSource, |
|
|
|
target, |
|
|
|
source: null, |
|
|
|
}; |
|
|
|
|
|
|
|
if (!source || !target) return result; |
|
|
|
|
|
|
|
//@ts-ignore
|
|
|
|
let srcModel = source.toHTML ? source : null; |
|
|
|
let srcModel = isComponent(source) ? source : null; |
|
|
|
|
|
|
|
if (!srcModel) { |
|
|
|
const wrapper = this.getShallowWrapper(); |
|
|
|
srcModel = wrapper?.append(source)[0] || null; |
|
|
|
} |
|
|
|
|
|
|
|
//@ts-ignore
|
|
|
|
result.source = srcModel; |
|
|
|
|
|
|
|
if (!srcModel) return result; |
|
|
|
@ -667,20 +699,20 @@ export default class ComponentManager extends ItemManagerModule<DomComponentsCon |
|
|
|
let draggable = srcModel.get('draggable'); |
|
|
|
|
|
|
|
if (isFunction(draggable)) { |
|
|
|
draggable = !!draggable(srcModel, target, at); |
|
|
|
draggable = !!draggable(srcModel, target, index); |
|
|
|
} else { |
|
|
|
const el = target.getEl(); |
|
|
|
draggable = isArray(draggable) ? draggable.join(',') : draggable; |
|
|
|
draggable = isString(draggable) ? el?.matches(draggable) : draggable; |
|
|
|
} |
|
|
|
|
|
|
|
if (!draggable) return { ...result, reason: 1 }; |
|
|
|
if (!draggable) return { ...result, reason: CanMoveReason.SourceReject }; |
|
|
|
|
|
|
|
// Check if the target accepts the source
|
|
|
|
let droppable = target.get('droppable'); |
|
|
|
|
|
|
|
if (isFunction(droppable)) { |
|
|
|
droppable = !!droppable(srcModel, target, at); |
|
|
|
droppable = !!droppable(srcModel, target, index); |
|
|
|
} else { |
|
|
|
if (droppable === false && target.isInstanceOf('text') && srcModel.get('textable')) { |
|
|
|
droppable = true; |
|
|
|
@ -694,7 +726,7 @@ export default class ComponentManager extends ItemManagerModule<DomComponentsCon |
|
|
|
// Ensure the target is not inside the source
|
|
|
|
const isTargetInside = [target].concat(target.parents()).indexOf(srcModel) > -1; |
|
|
|
|
|
|
|
if (!droppable || isTargetInside) return { ...result, reason: 2 }; |
|
|
|
if (!droppable || isTargetInside) return { ...result, reason: CanMoveReason.TargetReject }; |
|
|
|
|
|
|
|
return { ...result, result: true }; |
|
|
|
} |
|
|
|
|