Skip to content

Commit

Permalink
feat: add middleware of layout selector
Browse files Browse the repository at this point in the history
  • Loading branch information
chenshenhai committed Mar 30, 2024
1 parent 9f4ba90 commit c0de303
Show file tree
Hide file tree
Showing 20 changed files with 626 additions and 27 deletions.
1 change: 1 addition & 0 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const eventChange = 'change';
2 changes: 2 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Data, PointSize, CoreOptions, BoardMiddleware, ViewSizeInfo, CoreE
import { Board } from '@idraw/board';
import { createBoardContent, validateElements } from '@idraw/util';
import { Cursor } from './lib/cursor';
export { eventChange } from './config';

// export { MiddlewareSelector } from './middleware/selector';
export { MiddlewareSelector, middlewareEventSelect, middlewareEventSelectClear } from './middleware/selector';
Expand All @@ -11,6 +12,7 @@ export { MiddlewareRuler, middlewareEventRuler } from './middleware/ruler';
export { MiddlewareTextEditor, middlewareEventTextEdit, middlewareEventTextChange } from './middleware/text-editor';
export { MiddlewareDragger } from './middleware/dragger';
export { MiddlewareInfo } from './middleware/info';
export { MiddlewareLayoutSelector } from './middleware/layout-selector';

export class Core<E extends CoreEventMap = CoreEventMap> {
#board: Board<E>;
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/middleware/layout-selector/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const key = 'LAYOUT_SELECT';
// export const keyHoverElement = Symbol(`${key}_hoverElementSize`);
export const keyLayoutActionType = Symbol(`${key}_layoutActionType`); // 'hover' | 'resize' | null = null;
export const keyLayoutControlType = Symbol(`${key}_layoutControlType`); // ControlType | null;
export const keyLayoutController = Symbol(`${key}_layoutController`); // ElementSizeController | null = null;

export const selectColor = '#1973ba';
export const disableColor = '#5b5959b5';
261 changes: 261 additions & 0 deletions packages/core/src/middleware/layout-selector/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
import type { BoardMiddleware, ElementSize, Point } from '@idraw/types';
import { calcLayoutSizeController, isViewPointInVertexes, getViewScaleInfoFromSnapshot } from '@idraw/util';
import type { LayoutSelectorSharedStorage, ControlType } from './types';
import { keyLayoutActionType, keyLayoutController, keyLayoutControlType } from './config';
import { keyActionType as keyElementActionType, middlewareEventSelectClear } from '../selector';
import { drawLayoutController } from './util';
import { eventChange } from '../../config';

export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStorage> = (opts) => {
const { sharer, boardContent, calculator, viewer, eventHub } = opts;
const { helperContext } = boardContent;

let prevPoint: Point | null = null;

const clear = () => {
prevPoint = null;
sharer.setSharedStorage(keyLayoutActionType, null);
sharer.setSharedStorage(keyLayoutControlType, null);
sharer.setSharedStorage(keyLayoutController, null);
};

const isInElementAction = () => {
const elementType = sharer.getSharedStorage(keyElementActionType);
if (elementType) {
return true;
}
return false;
};

const isDisbaledControl = (controlType: ControlType) => {
const data = sharer.getActiveStorage('data');
if (data?.layout?.operations) {
const operations = data.layout.operations;
if (controlType === 'left' && operations.disableLeft === true) {
return true;
}
if (controlType === 'top' && operations.disableTop === true) {
return true;
}
if (controlType === 'right' && operations.disableRight === true) {
return true;
}
if (controlType === 'bottom' && operations.disableBottom === true) {
return true;
}
if (controlType === 'top-left' && operations.disableTopLeft === true) {
return true;
}
if (controlType === 'top-right' && operations.disableTopRight === true) {
return true;
}
if (controlType === 'bottom-left' && operations.disableBottomLeft === true) {
return true;
}
if (controlType === 'bottom-right' && operations.disableBottomRight === true) {
return true;
}
}
return false;
};

const getLayoutSize = () => {
const data = sharer.getActiveStorage('data');
if (data?.layout) {
const { x, y, w, h } = data.layout;
return { x, y, w, h };
}
return null;
};

const resetController = () => {
const viewScaleInfo = sharer.getActiveViewScaleInfo();
const size: ElementSize | null = getLayoutSize();
if (size) {
const controller = calcLayoutSizeController(size, { viewScaleInfo, controllerSize: 10 });
sharer.setSharedStorage(keyLayoutController, controller);
} else {
sharer.setSharedStorage(keyLayoutController, null);
}
};

const resetControlType = (e?: { point: Point }) => {
const data = sharer.getActiveStorage('data');
const controller = sharer.getSharedStorage(keyLayoutController);
if (controller && data?.layout && e?.point) {
// sharer.setSharedStorage(keyLayoutControlType, null);
let layoutControlType: ControlType | null = null;
if (controller) {
const { topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left } = controller;
const list = [topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left];
for (let i = 0; i < list.length; i++) {
const item = list[i];
if (isViewPointInVertexes(e.point, item.vertexes)) {
layoutControlType = `${item.type}` as ControlType;
break;
}
}
if (layoutControlType) {
sharer.setSharedStorage(keyLayoutControlType, layoutControlType);
eventHub.trigger(middlewareEventSelectClear, {});
return layoutControlType;
}
}
}
return null;
};

return {
name: '@middleware/layout-selector',
use: () => {
clear();
resetController();
},
hover: (e) => {
if (isInElementAction()) {
return;
}
const prevLayoutActionType = sharer.getSharedStorage(keyLayoutActionType);

const data = sharer.getActiveStorage('data');
if (data?.layout && prevLayoutActionType !== 'resize') {
resetController();
const layoutControlType = resetControlType(e);
if (layoutControlType) {
sharer.setSharedStorage(keyLayoutActionType, 'hover');
if (!isDisbaledControl(layoutControlType)) {
eventHub.trigger('cursor', {
type: `resize-${layoutControlType}`,
groupQueue: [],
element: getLayoutSize()
});
}

viewer.drawFrame();
} else {
sharer.setSharedStorage(keyLayoutActionType, null);
}
}
if (['hover', 'resize'].includes(sharer.getSharedStorage(keyLayoutActionType) as string)) {
return false;
}
if (prevLayoutActionType === 'hover' && !sharer.getSharedStorage(keyLayoutActionType)) {
viewer.drawFrame();
}
},
pointStart: (e) => {
if (isInElementAction()) {
return;
}
resetController();
const layoutControlType = resetControlType(e);
prevPoint = e.point;
if (layoutControlType) {
if (isDisbaledControl(layoutControlType)) {
return;
}
sharer.setSharedStorage(keyLayoutActionType, 'resize');
return false;
}
const layoutActionType = sharer.getSharedStorage(keyLayoutActionType);
if (['hover', 'resize'].includes(layoutActionType as string)) {
return false;
}
},
pointMove: (e) => {
if (isInElementAction()) {
return;
}
const layoutActionType = sharer.getSharedStorage(keyLayoutActionType);
const layoutControlType = sharer.getSharedStorage(keyLayoutControlType);
const data = sharer.getActiveStorage('data');
if (layoutControlType && isDisbaledControl(layoutControlType)) {
return;
}

if (layoutActionType === 'resize' && layoutControlType && data?.layout) {
if (prevPoint) {
const scale = sharer.getActiveStorage('scale');
const moveX = (e.point.x - prevPoint.x) / scale;
const moveY = (e.point.y - prevPoint.y) / scale;
const { x, y, w, h } = data.layout;

if (layoutControlType === 'top') {
data.layout.y = calculator.toGridNum(y + moveY);
data.layout.h = calculator.toGridNum(h - moveY);
} else if (layoutControlType === 'right') {
data.layout.w = calculator.toGridNum(w + moveX);
} else if (layoutControlType === 'bottom') {
data.layout.h = calculator.toGridNum(h + moveY);
} else if (layoutControlType === 'left') {
data.layout.x = calculator.toGridNum(x + moveX);
data.layout.w = calculator.toGridNum(w - moveX);
} else if (layoutControlType === 'top-left') {
data.layout.x = calculator.toGridNum(x + moveX);
data.layout.y = calculator.toGridNum(y + moveY);
data.layout.w = calculator.toGridNum(w - moveX);
data.layout.h = calculator.toGridNum(h - moveY);
} else if (layoutControlType === 'top-right') {
data.layout.y = calculator.toGridNum(y + moveY);
data.layout.w = calculator.toGridNum(w + moveX);
data.layout.h = calculator.toGridNum(h - moveY);
} else if (layoutControlType === 'bottom-right') {
data.layout.w = calculator.toGridNum(w + moveX);
data.layout.h = calculator.toGridNum(h + moveY);
} else if (layoutControlType === 'bottom-left') {
data.layout.x = calculator.toGridNum(x + moveX);
data.layout.w = calculator.toGridNum(w - moveX);
data.layout.h = calculator.toGridNum(h + moveY);
}
}
prevPoint = e.point;
resetController();
viewer.drawFrame();

return false;
}

if (['hover', 'resize'].includes(layoutActionType as string)) {
return false;
}
},
pointEnd: () => {
const layoutActionType = sharer.getSharedStorage(keyLayoutActionType);
const layoutControlType = sharer.getSharedStorage(keyLayoutControlType);
const data = sharer.getActiveStorage('data');
if (data && layoutActionType === 'resize' && layoutControlType && !isDisbaledControl(layoutControlType)) {
eventHub.trigger(eventChange, {
type: 'changeLayout',
data
});
}

clear();
},
beforeDrawFrame: ({ snapshot }) => {
const { sharedStore, activeStore } = snapshot;
const layoutActionType = sharedStore[keyLayoutActionType];
const layoutControlType = sharedStore[keyLayoutControlType];

if (activeStore.data?.layout && layoutActionType && layoutControlType) {
if (['hover', 'resize'].includes(layoutActionType)) {
const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot);
const { x, y, w, h } = activeStore.data.layout;
const size = { x, y, w, h };
const controller = calcLayoutSizeController(size, { viewScaleInfo, controllerSize: 10 });

drawLayoutController(helperContext, { controller, operations: activeStore.data.layout.operations || {} });
}
}
},
scrollX: () => {
clear();
},
scrollY: () => {
clear();
},
wheelScale: () => {
clear();
}
};
};
15 changes: 15 additions & 0 deletions packages/core/src/middleware/layout-selector/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { LayoutSizeController } from '@idraw/types';
import { keyLayoutActionType, keyLayoutControlType, keyLayoutController } from './config';
import { keyActionType as keyElementActionType } from '../selector';
import type { ActionType as ElementActionType } from '../selector';

export type ActionType = 'hover' | 'resize' | null;

export type ControlType = 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';

export type LayoutSelectorSharedStorage = {
[keyLayoutActionType]: ActionType | null;
[keyLayoutControlType]: ControlType | null;
[keyLayoutController]: LayoutSizeController | null;
[keyElementActionType]: ElementActionType | null;
};
Loading

0 comments on commit c0de303

Please sign in to comment.