360 lines
10 KiB
TypeScript

import React, { Component, Fragment } from 'react';
import DragResizeEngine from './drag-resize-engine';
import { observer, computed, globalContext } from '@alilc/lowcode-editor-core';
import classNames from 'classnames';
import { SimulatorContext } from '../context';
import { BuiltinSimulatorHost } from '../host';
import { OffsetObserver, Designer } from '../../designer';
import { Node } from '../../document';
import { normalizeTriggers } from '../../utils/misc';
@observer
export default class BoxResizing extends Component<{ host: BuiltinSimulatorHost }> {
static contextType = SimulatorContext;
get host(): BuiltinSimulatorHost {
return this.props.host;
}
get dragging(): boolean {
return this.host.designer.dragon.dragging;
}
@computed get selecting() {
const doc = this.host.currentDocument;
if (!doc || doc.suspensed) {
return null;
}
const { selection } = doc;
return this.dragging ? selection.getTopNodes() : selection.getNodes();
}
componentDidUpdate() {
// this.hoveringCapture.setBoundary(this.outline);
// this.willBind();
}
render() {
const { selecting } = this;
if (!selecting || selecting.length < 1) {
// DIRTY FIX, recore has a bug!
return <Fragment />;
}
// const componentMeta = selecting[0].componentMeta;
// const metadata = componentMeta.getMetadata();
return (
<Fragment>
{selecting.map((node) => (
<BoxResizingForNode key={node.id} node={node} host={this.props.host} />
))}
</Fragment>
);
}
}
@observer
export class BoxResizingForNode extends Component<{ host: BuiltinSimulatorHost; node: Node }> {
static contextType = SimulatorContext;
get host(): BuiltinSimulatorHost {
return this.props.host;
}
get dragging(): boolean {
return this.host.designer.dragon.dragging;
}
@computed get instances() {
return this.host.getComponentInstances(this.props.node);
}
render() {
const { instances } = this;
const { node } = this.props;
const { designer } = this.host;
if (!instances || instances.length < 1 || this.dragging) {
return null;
}
return (
<Fragment key={node.id}>
{instances.map((instance: any) => {
const observed = designer.createOffsetObserver({
node,
instance,
});
if (!observed) {
return null;
}
return (
<BoxResizingInstance key={observed.id} dragging={this.dragging} designer={designer} observed={observed} />
);
})}
</Fragment>
);
}
}
@observer
export class BoxResizingInstance extends Component<{
observed: OffsetObserver;
highlight?: boolean;
dragging?: boolean;
designer?: Designer;
}> {
// private outline: any;
private willUnbind: () => any;
// outline of eight direction
private outlineN: any;
private outlineE: any;
private outlineS: any;
private outlineW: any;
private outlineNE: any;
private outlineNW: any;
private outlineSE: any;
private outlineSW: any;
private dragEngine: DragResizeEngine;
constructor(props: any) {
super(props);
this.dragEngine = new DragResizeEngine(props.designer);
}
componentWillUnmount() {
if (this.willUnbind) {
this.willUnbind();
}
this.props.observed.purge();
}
componentDidMount() {
// this.hoveringCapture.setBoundary(this.outline);
this.willBind();
const resize = (e: MouseEvent, direction: string, node: any, moveX: number, moveY: number) => {
const { advanced } = node.componentMeta;
if (
advanced.callbacks &&
typeof advanced.callbacks.onResize === 'function'
) {
(e as any).trigger = direction;
(e as any).deltaX = moveX;
(e as any).deltaY = moveY;
const cbNode = node?.isNode ? node.internalToShellNode() : node;
advanced.callbacks.onResize(e, cbNode);
}
};
const resizeStart = (e: MouseEvent, direction: string, node: any) => {
const { advanced } = node.componentMeta;
if (
advanced.callbacks &&
typeof advanced.callbacks.onResizeStart === 'function'
) {
(e as any).trigger = direction;
const cbNode = node?.isNode ? node.internalToShellNode() : node;
advanced.callbacks.onResizeStart(e, cbNode);
}
};
const resizeEnd = (e: MouseEvent, direction: string, node: any) => {
const { advanced } = node.componentMeta;
if (
advanced.callbacks &&
typeof advanced.callbacks.onResizeEnd === 'function'
) {
(e as any).trigger = direction;
const cbNode = node?.isNode ? node.internalToShellNode() : node;
advanced.callbacks.onResizeEnd(e, cbNode);
}
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.eventBus.emit('designer.border.resize', {
selected,
layout: node?.parent?.getPropValue('layout') || '',
});
};
this.dragEngine.onResize(resize);
this.dragEngine.onResizeStart(resizeStart);
this.dragEngine.onResizeEnd(resizeEnd);
}
willBind() {
if (this.willUnbind) {
this.willUnbind();
}
if (
!this.outlineN &&
!this.outlineE &&
!this.outlineS &&
!this.outlineW &&
!this.outlineNE &&
!this.outlineNW &&
!this.outlineSE &&
!this.outlineSW
) {
return;
}
const unBind: any[] = [];
const { node } = this.props.observed;
unBind.push(
...[
this.dragEngine.from(this.outlineN, 'n', () => node),
this.dragEngine.from(this.outlineE, 'e', () => node),
this.dragEngine.from(this.outlineS, 's', () => node),
this.dragEngine.from(this.outlineW, 'w', () => node),
this.dragEngine.from(this.outlineNE, 'ne', () => node),
this.dragEngine.from(this.outlineNW, 'nw', () => node),
this.dragEngine.from(this.outlineSE, 'se', () => node),
this.dragEngine.from(this.outlineSW, 'sw', () => node),
],
);
this.willUnbind = () => {
if (unBind && unBind.length > 0) {
unBind.forEach((item) => {
item();
});
}
this.willUnbind = () => {};
};
}
render() {
const { observed } = this.props;
let triggerVisible: any = [];
let offsetWidth = 0;
let offsetHeight = 0;
let offsetTop = 0;
let offsetLeft = 0;
if (observed.hasOffset) {
offsetWidth = observed.offsetWidth;
offsetHeight = observed.offsetHeight;
offsetTop = observed.offsetTop;
offsetLeft = observed.offsetLeft;
const { node } = observed;
const metadata = node.componentMeta.getMetadata();
if (metadata.configure?.advanced?.getResizingHandlers) {
triggerVisible = metadata.configure.advanced.getResizingHandlers(node.internalToShellNode());
}
}
triggerVisible = normalizeTriggers(triggerVisible);
const baseSideClass = 'lc-borders lc-resize-side';
const baseCornerClass = 'lc-borders lc-resize-corner';
return (
<div>
<div
ref={(ref) => {
this.outlineN = ref;
}}
className={classNames(baseSideClass, 'n')}
style={{
height: 20,
transform: `translate(${offsetLeft}px, ${offsetTop - 10}px)`,
width: offsetWidth,
display: triggerVisible.includes('N') ? 'flex' : 'none',
}}
/>
<div
ref={(ref) => {
this.outlineNE = ref;
}}
className={classNames(baseCornerClass, 'ne')}
style={{
transform: `translate(${offsetLeft + offsetWidth - 5}px, ${offsetTop - 3}px)`,
cursor: 'nesw-resize',
display: triggerVisible.includes('NE') ? 'flex' : 'none',
}}
/>
<div
className={classNames(baseSideClass, 'e')}
ref={(ref) => {
this.outlineE = ref;
}}
style={{
height: offsetHeight,
transform: `translate(${offsetLeft + offsetWidth - 10}px, ${offsetTop}px)`,
width: 20,
display: triggerVisible.includes('E') ? 'flex' : 'none',
}}
/>
<div
ref={(ref) => {
this.outlineSE = ref;
}}
className={classNames(baseCornerClass, 'se')}
style={{
transform: `translate(${offsetLeft + offsetWidth - 5}px, ${
offsetTop + offsetHeight - 5
}px)`,
cursor: 'nwse-resize',
display: triggerVisible.includes('SE') ? 'flex' : 'none',
}}
/>
<div
ref={(ref) => {
this.outlineS = ref;
}}
className={classNames(baseSideClass, 's')}
style={{
height: 20,
transform: `translate(${offsetLeft}px, ${offsetTop + offsetHeight - 10}px)`,
width: offsetWidth,
display: triggerVisible.includes('S') ? 'flex' : 'none',
}}
/>
<div
ref={(ref) => {
this.outlineSW = ref;
}}
className={classNames(baseCornerClass, 'sw')}
style={{
transform: `translate(${offsetLeft - 3}px, ${offsetTop + offsetHeight - 5}px)`,
cursor: 'nesw-resize',
display: triggerVisible.includes('SW') ? 'flex' : 'none',
}}
/>
<div
ref={(ref) => {
this.outlineW = ref;
}}
className={classNames(baseSideClass, 'w')}
style={{
height: offsetHeight,
transform: `translate(${offsetLeft - 10}px, ${offsetTop}px)`,
width: 20,
display: triggerVisible.includes('W') ? 'flex' : 'none',
}}
/>
<div
ref={(ref) => {
this.outlineNW = ref;
}}
className={classNames(baseCornerClass, 'nw')}
style={{
transform: `translate(${offsetLeft - 3}px, ${offsetTop - 3}px)`,
cursor: 'nwse-resize',
display: triggerVisible.includes('NW') ? 'flex' : 'none',
}}
/>
</div>
);
}
}