Skip to content

Commit

Permalink
perf: 完善model模型
Browse files Browse the repository at this point in the history
  • Loading branch information
TheBeard30 committed Mar 16, 2024
1 parent 16effa4 commit 5cf256d
Show file tree
Hide file tree
Showing 11 changed files with 621 additions and 4 deletions.
4 changes: 3 additions & 1 deletion src/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { Engine } from '@/app/core/models';
import { createDesigner } from '@/app/core/externals';

export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes)]
providers: [provideRouter(routes), { provide: Engine, useFactory: createDesigner }]
};
6 changes: 6 additions & 0 deletions src/app/core/events/mutation/DropNodeEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { AbstractMutationNodeEvent } from './AbstractMutationNodeEvent';
import { ICustomEvent } from '../../../shared/event';

export class DropNodeEvent extends AbstractMutationNodeEvent implements ICustomEvent {
type = 'drop:node';
}
6 changes: 6 additions & 0 deletions src/app/core/events/mutation/SelectNodeEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { AbstractMutationNodeEvent } from './AbstractMutationNodeEvent';
import { ICustomEvent } from '../../../shared/event';

export class SelectNodeEvent extends AbstractMutationNodeEvent implements ICustomEvent {
type = 'select:node';
}
1 change: 1 addition & 0 deletions src/app/core/events/mutation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from './UpdateChildrenEvent';
export * from './RemoveNodeEvent';
export * from './CloneNodeEvent';
export * from './FromNodeEvent';
export * from './DropNodeEvent';
6 changes: 5 additions & 1 deletion src/app/core/externals.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IBehavior, IBehaviorHost, IResource, IResourceCreator } from './types';
import { Engine } from '@/app/core/models';
import { DEFAULT_DRIVERS, DEFAULT_EFFECTS } from '@/app/core/presets';

export const createResource = (...sources: IResourceCreator[]): IResource[] => {
return sources.reduce((buf, source) => {
Expand All @@ -26,5 +27,8 @@ export const isBehaviorHost = (val: any): val is IBehaviorHost => val?.Behavior
export const isBehaviorList = (val: any): val is IBehavior[] => Array.isArray(val) && val.every(isBehavior);

export const createDesigner = () => {
// return new Engine();
return new Engine({
effects: [...DEFAULT_EFFECTS],
drivers: [...DEFAULT_DRIVERS]
});
};
213 changes: 211 additions & 2 deletions src/app/core/models/move-helper.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { Operation } from './operation';
import { TreeNode } from './tree-node';
import { Viewport } from './viewport';
import { IPoint, Rect } from '../../shared/coordinate';
import {
calcDistanceOfPointToRect,
calcDistancePointToEdge,
IPoint,
isNearAfter,
isPointInRect,
Rect
} from '../../shared/coordinate';
import { CursorDragType } from '@/app/core/models/cursor';
import { DragNodeEvent } from '@/app/core/events';
import { DragNodeEvent, DropNodeEvent } from '@/app/core/events';

export enum ClosestPosition {
Before = 'BEFORE',
Expand Down Expand Up @@ -94,6 +101,153 @@ export class MoveHelper {
return this.viewportClosestDirection;
}

getClosestLayout(viewport: Viewport) {
return viewport.getValidNodeLayout(this.closestNode);
}

calcClosestPosition(point: IPoint, viewport: Viewport): ClosestPosition {
const closestNode = this.closestNode;
if (!closestNode || !viewport.isPointInViewport(point)) return ClosestPosition.Forbid;
const closestRect = viewport.getValidNodeRect(closestNode);
const isInline = this.getClosestLayout(viewport) === 'horizontal';
if (!closestRect) {
return null;
}
const isAfter = isNearAfter(point, closestRect, viewport.moveInsertionType === 'block' ? false : isInline);
const getValidParent = (node: TreeNode) => {
if (!node) return;
if (node.parent?.allowSibling(this.dragNodes)) return node.parent;
return getValidParent(node.parent);
};
if (isPointInRect(point, closestRect, viewport.moveSensitive)) {
if (!closestNode.allowAppend(this.dragNodes)) {
if (!closestNode.allowSibling(this.dragNodes)) {
const parentClosestNode = getValidParent(closestNode);
if (parentClosestNode) {
this.closestNode = parentClosestNode;
}
if (isInline) {
if (parentClosestNode) {
if (isAfter) {
return ClosestPosition.After;
}
return ClosestPosition.Before;
}
if (isAfter) {
return ClosestPosition.ForbidAfter;
}
return ClosestPosition.ForbidBefore;
} else {
if (parentClosestNode) {
if (isAfter) {
return ClosestPosition.Under;
}
return ClosestPosition.Upper;
}
if (isAfter) {
return ClosestPosition.ForbidUnder;
}
return ClosestPosition.ForbidUpper;
}
} else {
if (isInline) {
return isAfter ? ClosestPosition.After : ClosestPosition.Before;
} else {
return isAfter ? ClosestPosition.Under : ClosestPosition.Upper;
}
}
}
if (closestNode.contains(...this.dragNodes)) {
if (isAfter) {
return ClosestPosition.InnerAfter;
}
return ClosestPosition.InnerBefore;
} else {
return ClosestPosition.Inner;
}
} else if (closestNode === closestNode.root) {
return isAfter ? ClosestPosition.InnerAfter : ClosestPosition.InnerBefore;
} else {
if (!closestNode.allowSibling(this.dragNodes)) {
const parentClosestNode = getValidParent(closestNode);
if (parentClosestNode) {
this.closestNode = parentClosestNode;
}
if (isInline) {
if (parentClosestNode) {
if (isAfter) {
return ClosestPosition.After;
}
return ClosestPosition.Before;
}
return isAfter ? ClosestPosition.ForbidAfter : ClosestPosition.ForbidBefore;
} else {
if (parentClosestNode) {
if (isAfter) {
return ClosestPosition.Under;
}
return ClosestPosition.Upper;
}
return isAfter ? ClosestPosition.ForbidUnder : ClosestPosition.ForbidUpper;
}
}
if (isInline) {
return isAfter ? ClosestPosition.After : ClosestPosition.Before;
} else {
return isAfter ? ClosestPosition.Under : ClosestPosition.Upper;
}
}
}

calcClosestNode(point: IPoint, viewport: Viewport): TreeNode {
if (this.touchNode) {
const touchNodeRect = viewport.getValidNodeRect(this.touchNode);
if (!touchNodeRect) return null;
if (this.touchNode?.children?.length) {
const touchDistance = calcDistancePointToEdge(point, touchNodeRect);
let minDistance = touchDistance;
let minDistanceNode = this.touchNode;
this.touchNode.eachChildren(node => {
const rect = viewport.getElementRectById(node.id);
if (!rect) return;
const distance = isPointInRect(point, rect, viewport.moveSensitive)
? 0
: calcDistanceOfPointToRect(point, rect);
if (distance <= minDistance) {
minDistance = distance;
minDistanceNode = node;
}
});
return minDistanceNode;
} else {
return this.touchNode;
}
}
return this.operation.tree;
}

calcClosestRect(viewport: Viewport, closestDirection: ClosestPosition): Rect {
const closestNode = this.closestNode;
if (!closestNode || !closestDirection) return null;
const closestRect = viewport.getValidNodeRect(closestNode);
if (closestDirection === ClosestPosition.InnerAfter || closestDirection === ClosestPosition.InnerBefore) {
return viewport.getChildrenRect(closestNode);
} else {
return closestRect;
}
}

calcClosestOffsetRect(viewport: Viewport, closestDirection: ClosestPosition): Rect {
const closestNode = this.closestNode;
if (!closestNode || !closestDirection) return null;
const closestRect = viewport.getValidNodeOffsetRect(closestNode);
if (closestDirection === ClosestPosition.InnerAfter || closestDirection === ClosestPosition.InnerBefore) {
return viewport.getChildrenOffsetRect(closestNode);
} else {
return closestRect;
}
}

dragStart(props: IMoveHelperDragStartProps) {
const nodes = TreeNode.filterDraggable(props?.dragNodes);
if (nodes.length) {
Expand All @@ -110,6 +264,61 @@ export class MoveHelper {
}
}

dragMove(props: IMoveHelperDragMoveProps) {
const { point, touchNode } = props;
if (!this.dragging) return;
if (this.outline.isPointInViewport(point, false)) {
this.activeViewport = this.outline;
this.touchNode = touchNode;
this.closestNode = this.calcClosestNode(point, this.outline);
} else if (this.viewport.isPointInViewport(point, false)) {
this.activeViewport = this.viewport;
this.touchNode = touchNode;
this.closestNode = this.calcClosestNode(point, this.viewport);
}
if (!this.activeViewport) return;

if (this.activeViewport === this.outline) {
this.outlineClosestDirection = this.calcClosestPosition(point, this.outline);
this.viewportClosestDirection = this.outlineClosestDirection;
} else {
this.viewportClosestDirection = this.calcClosestPosition(point, this.viewport);
this.outlineClosestDirection = this.viewportClosestDirection;
}
if (this.outline.mounted) {
this.outlineClosestRect = this.calcClosestRect(this.outline, this.outlineClosestDirection);
this.outlineClosestOffsetRect = this.calcClosestOffsetRect(this.outline, this.outlineClosestDirection);
}
if (this.viewport.mounted) {
this.viewportClosestRect = this.calcClosestRect(this.viewport, this.viewportClosestDirection);
this.viewportClosestOffsetRect = this.calcClosestOffsetRect(this.viewport, this.viewportClosestDirection);
}
}

dragDrop(props: IMoveHelperDragDropProps) {
this.trigger(
new DropNodeEvent({
target: this.operation.tree,
source: props?.dropNode
})
);
}

dragEnd() {
this.dragging = false;
this.dragNodes = [];
this.touchNode = null;
this.closestNode = null;
this.activeViewport = null;
this.outlineClosestDirection = null;
this.outlineClosestOffsetRect = null;
this.outlineClosestRect = null;
this.viewportClosestDirection = null;
this.viewportClosestOffsetRect = null;
this.viewportClosestRect = null;
this.viewport.clearCache();
}

trigger(event: any) {
if (this.operation) {
return this.operation.dispatch(event);
Expand Down
30 changes: 30 additions & 0 deletions src/app/core/models/selection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { Operation } from './operation';
import { TreeNode } from '@/app/core/models/tree-node';
import { isArr, isStr } from '@/app/shared/types';
import { SelectNodeEvent } from '@/app/core/events/mutation/SelectNodeEvent';

export interface ISelection {
selected?: string[];
Expand All @@ -9,7 +12,34 @@ export class Selection {
selected: string[] = [];
indexes: Record<string, boolean> = {};

trigger(type = SelectNodeEvent) {
return this.operation.dispatch(
new type({
target: this.operation.tree,
source: this.selectedNodes
})
);
}

get selectedNodes() {
return this.selected.map(id => this.operation.tree.findById(id));
}

mapIds(ids: any) {
return isArr(ids) ? ids.map((node: any) => (isStr(node) ? node : node?.id)) : [];
}

batchSelect(ids: string[] | TreeNode[]) {
this.selected = this.mapIds(ids);
this.indexes = this.selected.reduce((buf, id) => {
buf[id] = true;
return buf;
}, {});
this.trigger(SelectNodeEvent);
}

batchSafeSelect(ids: string[] | TreeNode[]) {
if (!ids?.length) return;
this.batchSelect(ids);
}
}
Loading

0 comments on commit 5cf256d

Please sign in to comment.