feat: init deep selector middleware
55
.eslintrc.js
|
|
@ -1,28 +1,33 @@
|
|||
module.exports = {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": ["plugin:@typescript-eslint/recommended"],
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"semi": "error",
|
||||
"indent": ["error", 2, {
|
||||
"SwitchCase": 1,
|
||||
"VariableDeclarator": 1,
|
||||
"outerIIFEBody": 1,
|
||||
"MemberExpression": 1,
|
||||
"FunctionDeclaration": { "parameters": 1, "body": 1 },
|
||||
"FunctionExpression": { "parameters": 1, "body": 1 },
|
||||
"CallExpression": { "arguments": 1 },
|
||||
"ArrayExpression": 1,
|
||||
"ObjectExpression": 1,
|
||||
"ImportDeclaration": 1,
|
||||
"flatTernaryExpressions": false,
|
||||
"ignoreComments": false
|
||||
}],
|
||||
"@typescript-eslint/rule-name": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: ['plugin:@typescript-eslint/recommended'],
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
},
|
||||
rules: {
|
||||
semi: 'error',
|
||||
indent: [
|
||||
'error',
|
||||
2,
|
||||
{
|
||||
SwitchCase: 1,
|
||||
VariableDeclarator: 1,
|
||||
outerIIFEBody: 1,
|
||||
MemberExpression: 1,
|
||||
FunctionDeclaration: { parameters: 1, body: 1 },
|
||||
FunctionExpression: { parameters: 1, body: 1 },
|
||||
CallExpression: { arguments: 1 },
|
||||
ArrayExpression: 1,
|
||||
ObjectExpression: 1,
|
||||
ImportDeclaration: 1,
|
||||
flatTernaryExpressions: false,
|
||||
ignoreComments: false
|
||||
}
|
||||
],
|
||||
'no-console': 1,
|
||||
'@typescript-eslint/rule-name': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off'
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ export class Board {
|
|||
this._handleHover(e);
|
||||
}, frameTime)
|
||||
);
|
||||
|
||||
this._watcher.on(
|
||||
'wheelX',
|
||||
throttle((e) => {
|
||||
|
|
@ -87,6 +88,7 @@ export class Board {
|
|||
this._watcher.on('scrollX', this._handleScrollX.bind(this));
|
||||
this._watcher.on('scrollY', this._handleScrollY.bind(this));
|
||||
this._watcher.on('resize', this._handleResize.bind(this));
|
||||
this._watcher.on('doubleClick', this._handleDoubleClick.bind(this));
|
||||
}
|
||||
|
||||
private _handlePointStart(e: BoardWatcherEventMap['pointStart']) {
|
||||
|
|
@ -129,6 +131,16 @@ export class Board {
|
|||
}
|
||||
}
|
||||
|
||||
private _handleDoubleClick(e: BoardWatcherEventMap['doubleClick']) {
|
||||
for (let i = 0; i < this._activeMiddlewareObjs.length; i++) {
|
||||
const obj = this._activeMiddlewareObjs[i];
|
||||
const result = obj?.doubleClick?.(e);
|
||||
if (result === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _handleWheelX(e: BoardWatcherEventMap['wheelX']) {
|
||||
for (let i = 0; i < this._activeMiddlewareObjs.length; i++) {
|
||||
const obj = this._activeMiddlewareObjs[i];
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export class Calculator implements ViewCalculator {
|
|||
viewScale(num: number, prevScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ViewScaleInfo {
|
||||
const scale = num;
|
||||
|
||||
const { width, height, contextX, contextY, contextWidth, contextHeight } = viewSizeInfo;
|
||||
const { width, height, contextWidth, contextHeight } = viewSizeInfo;
|
||||
let offsetLeft = 0;
|
||||
let offsetRight = 0;
|
||||
let offsetTop = 0;
|
||||
|
|
@ -47,7 +47,7 @@ export class Calculator implements ViewCalculator {
|
|||
viewScroll(opts: { moveX?: number; moveY?: number }, scaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo): ViewScaleInfo {
|
||||
const scale = scaleInfo.scale;
|
||||
const { moveX, moveY } = opts;
|
||||
const { width, height, contextWidth, contextHeight, contextX, contextY } = viewSizeInfo;
|
||||
const { width, height, contextWidth, contextHeight } = viewSizeInfo;
|
||||
let offsetLeft = scaleInfo.offsetLeft;
|
||||
let offsetRight = scaleInfo.offsetRight;
|
||||
let offsetTop = scaleInfo.offsetTop;
|
||||
|
|
@ -103,7 +103,8 @@ export class Calculator implements ViewCalculator {
|
|||
const { x, y, w, h, angle } = size;
|
||||
const { contextX = 0, contextY = 0 } = viewSizeInfo;
|
||||
const { scale, offsetTop, offsetLeft } = scaleInfo;
|
||||
return {
|
||||
|
||||
const newSize = {
|
||||
x: x * scale + offsetLeft - contextX,
|
||||
y: y * scale + offsetTop - contextY,
|
||||
w: w * scale,
|
||||
|
|
@ -111,6 +112,8 @@ export class Calculator implements ViewCalculator {
|
|||
angle
|
||||
};
|
||||
|
||||
return newSize;
|
||||
|
||||
// const { x, y, w, h, angle } = size;
|
||||
// const { scale, offsetTop, offsetLeft } = scaleInfo;
|
||||
// return {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
|
|||
private _store: Store<BoardWatcherStore>;
|
||||
constructor(opts: BoardWatcherOptions) {
|
||||
super();
|
||||
const store = new Store<BoardWatcherStore>({ defaultStorage: { hasPointDown: true } });
|
||||
const store = new Store<BoardWatcherStore>({ defaultStorage: { hasPointDown: true, prevClickPoint: null } });
|
||||
this._store = store;
|
||||
this._opts = opts;
|
||||
this._init();
|
||||
|
|
@ -97,6 +97,25 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
|
|||
},
|
||||
{ passive: false }
|
||||
);
|
||||
container.addEventListener('click', (e: MouseEvent) => {
|
||||
if (!this._isInTarget(e)) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
this._store.set('hasPointDown', true);
|
||||
const point = this._getPoint(e);
|
||||
if (!this._isVaildPoint(point)) {
|
||||
return;
|
||||
}
|
||||
const maxLimitTime = 500;
|
||||
const t = Date.now();
|
||||
const preClickPoint = this._store.get('prevClickPoint');
|
||||
if (preClickPoint && t - preClickPoint.t <= maxLimitTime && Math.abs(preClickPoint.x - point.x) <= 5 && Math.abs(preClickPoint.y - point.y) <= 5) {
|
||||
this.trigger('doubleClick', { point });
|
||||
} else {
|
||||
this._store.set('prevClickPoint', point);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _isInTarget(e: MouseEvent | WheelEvent) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import type { Data, CoreOptions, BoardMiddleware, ViewSizeInfo } from '@idraw/ty
|
|||
import { Board } from '@idraw/board';
|
||||
import { createBoardContexts, validateElements, calcElementsContextSize } from '@idraw/util';
|
||||
|
||||
export { MiddlewareSelector } from './middleware/selector';
|
||||
// export { MiddlewareSelector } from './middleware/selector';
|
||||
export { MiddlewareSelector } from './middleware/deep-selector';
|
||||
export { MiddlewareScroller } from './middleware/scroller';
|
||||
export { MiddlewareRuler } from './middleware/rule';
|
||||
|
||||
|
|
@ -13,6 +14,7 @@ export class Core {
|
|||
private _canvas: HTMLCanvasElement;
|
||||
constructor(mount: HTMLDivElement, opts: CoreOptions) {
|
||||
const { devicePixelRatio = 1, width, height } = opts;
|
||||
|
||||
this._opts = opts;
|
||||
this._mount = mount;
|
||||
const canvas = document.createElement('canvas');
|
||||
|
|
@ -45,13 +47,12 @@ export class Core {
|
|||
this._board.setData(data);
|
||||
const sharer = this._board.getSharer();
|
||||
const currentViewSize = sharer.getActiveViewSizeInfo();
|
||||
const currentScaleInfo = sharer.getActiveScaleInfo();
|
||||
|
||||
// const currentScaleInfo = sharer.getActiveScaleInfo();
|
||||
const newViewContextSize = calcElementsContextSize(data.elements, {
|
||||
viewWidth: currentViewSize.width,
|
||||
viewHeight: currentViewSize.height
|
||||
viewHeight: currentViewSize.height,
|
||||
extend: true
|
||||
});
|
||||
|
||||
this.resize({
|
||||
...currentViewSize,
|
||||
...newViewContextSize
|
||||
|
|
|
|||
14
packages/core/src/middleware/deep-selector/config.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
export const key = 'SELECT';
|
||||
export const keyHoverElementSize = Symbol(`${key}_hoverElementSize`);
|
||||
export const keyActionType = Symbol(`${key}_actionType`); // 'select' | 'drag-list' | 'drag-list-end' | 'drag' | 'hover' | 'resize' | 'area' | null = null;
|
||||
export const keyResizeType = Symbol(`${key}_resizeType`); // ResizeType | null;
|
||||
export const keyAreaStart = Symbol(`${key}_areaStart`); // Point
|
||||
export const keyAreaEnd = Symbol(`${key}_areaEnd`); // Point
|
||||
export const keyInGroupQueue = Symbol(`${key}_targetQueue`); // Element<'group'>[]
|
||||
|
||||
// export const keyHoverElementSize = `${key}_hoverElementSize`;
|
||||
// export const keyActionType = `${key}_actionType`; // 'select' | 'drag-list' | 'drag-list-end' | 'drag' | 'hover' | 'resize' | 'area' | null = null;
|
||||
// export const keyResizeType = `${key}_resizeType`; // ResizeType | null;
|
||||
// export const keyAreaStart = `${key}_areaStart`; // Point
|
||||
// export const keyAreaEnd = `${key}_areaEnd`; // Point
|
||||
// export const keyInGroupQueue = `${key}_targetQueue`; // Element<'group'>[]
|
||||
88
packages/core/src/middleware/deep-selector/controller.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import type { ElementSize } from '@idraw/types';
|
||||
import type { ElementSizeController } from './types';
|
||||
const wrapperColor = '#1973ba';
|
||||
|
||||
export function calcElementControllerStyle(elemSize: ElementSize): ElementSizeController {
|
||||
const bw = 0; // TODO
|
||||
|
||||
const ctrlSize = 8;
|
||||
const ctrlBgColor = '#FFFFFF';
|
||||
const ctrlBorderWidth = 2;
|
||||
const ctrlBorderColor = wrapperColor;
|
||||
const { x, y, w, h } = elemSize;
|
||||
const sizeControllers: ElementSizeController = {
|
||||
// topLeft: {
|
||||
// x: x - bw - ctrlSize / 2,
|
||||
// y: y - bw - ctrlSize / 2,
|
||||
// w: ctrlSize,
|
||||
// h: ctrlSize,
|
||||
// borderWidth: ctrlBorderWidth,
|
||||
// borderColor: ctrlBorderColor,
|
||||
// bgColor: ctrlBgColor
|
||||
// },
|
||||
top: {
|
||||
x: x - bw + w / 2 - ctrlSize / 2,
|
||||
y: y - bw - ctrlSize / 2,
|
||||
w: ctrlSize,
|
||||
h: ctrlSize,
|
||||
borderWidth: ctrlBorderWidth,
|
||||
borderColor: ctrlBorderColor,
|
||||
bgColor: ctrlBgColor
|
||||
},
|
||||
// topRight: {
|
||||
// x: x + w - bw - ctrlSize / 2,
|
||||
// y: y - bw - ctrlSize / 2,
|
||||
// w: ctrlSize,
|
||||
// h: ctrlSize,
|
||||
// borderWidth: ctrlBorderWidth,
|
||||
// borderColor: ctrlBorderColor,
|
||||
// bgColor: ctrlBgColor
|
||||
// },
|
||||
right: {
|
||||
x: x + w - bw - ctrlSize / 2,
|
||||
y: y + h / 2 - bw - ctrlSize / 2,
|
||||
w: ctrlSize,
|
||||
h: ctrlSize,
|
||||
borderWidth: ctrlBorderWidth,
|
||||
borderColor: ctrlBorderColor,
|
||||
bgColor: ctrlBgColor
|
||||
},
|
||||
// bottomRight: {
|
||||
// x: x + w - bw - ctrlSize / 2,
|
||||
// y: y + h - bw - ctrlSize / 2,
|
||||
// w: ctrlSize,
|
||||
// h: ctrlSize,
|
||||
// borderWidth: ctrlBorderWidth,
|
||||
// borderColor: ctrlBorderColor,
|
||||
// bgColor: ctrlBgColor
|
||||
// },
|
||||
bottom: {
|
||||
x: x + w / 2 - bw - ctrlSize / 2,
|
||||
y: y + h - bw - ctrlSize / 2,
|
||||
w: ctrlSize,
|
||||
h: ctrlSize,
|
||||
borderWidth: ctrlBorderWidth,
|
||||
borderColor: ctrlBorderColor,
|
||||
bgColor: ctrlBgColor
|
||||
},
|
||||
// bottomLeft: {
|
||||
// x: x - bw - ctrlSize / 2,
|
||||
// y: y + h - bw - ctrlSize / 2,
|
||||
// w: ctrlSize,
|
||||
// h: ctrlSize,
|
||||
// borderWidth: ctrlBorderWidth,
|
||||
// borderColor: ctrlBorderColor,
|
||||
// bgColor: ctrlBgColor
|
||||
// },
|
||||
left: {
|
||||
x: x - bw - ctrlSize / 2,
|
||||
y: y + h / 2 - bw - ctrlSize / 2,
|
||||
w: ctrlSize,
|
||||
h: ctrlSize,
|
||||
borderWidth: ctrlBorderWidth,
|
||||
borderColor: ctrlBorderColor,
|
||||
bgColor: ctrlBgColor
|
||||
}
|
||||
};
|
||||
return sizeControllers;
|
||||
}
|
||||
189
packages/core/src/middleware/deep-selector/draw-wrapper.ts
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
import type { Element, ElementSize, ElementType, PointSize, RendererDrawElementOptions, ViewContext2D } from '@idraw/types';
|
||||
import { rotateElement, rotateElementVertexes } from '@idraw/util';
|
||||
// import { calcElementControllerStyle } from './controller';
|
||||
import type { AreaSize, ControllerStyle, ElementSizeController } from './types';
|
||||
|
||||
const wrapperColor = '#1973ba';
|
||||
|
||||
export function drawPointWrapper(ctx: ViewContext2D, elem: ElementSize) {
|
||||
const bw = 0;
|
||||
const { x, y, w, h } = elem;
|
||||
const { angle = 0 } = elem;
|
||||
|
||||
rotateElement(ctx, { x, y, w, h, angle }, () => {
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = wrapperColor;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - bw, y - bw);
|
||||
ctx.lineTo(x + w + bw, y - bw);
|
||||
ctx.lineTo(x + w + bw, y + h + bw);
|
||||
ctx.lineTo(x - bw, y + h + bw);
|
||||
ctx.lineTo(x - bw, y - bw);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
});
|
||||
}
|
||||
|
||||
export function drawHoverWrapper(ctx: ViewContext2D, elem: ElementSize) {
|
||||
const bw = 0;
|
||||
const { x, y, w, h } = elem;
|
||||
const { angle = 0 } = elem;
|
||||
rotateElement(ctx, { x, y, w, h, angle }, () => {
|
||||
// ctx.setLineDash([4, 4]);
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = wrapperColor;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - bw, y - bw);
|
||||
ctx.lineTo(x + w + bw, y - bw);
|
||||
ctx.lineTo(x + w + bw, y + h + bw);
|
||||
ctx.lineTo(x - bw, y + h + bw);
|
||||
ctx.lineTo(x - bw, y - bw);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
});
|
||||
}
|
||||
|
||||
function drawController(ctx: ViewContext2D, style: ControllerStyle) {
|
||||
const { x, y, w, h, borderColor, borderWidth, bgColor } = style;
|
||||
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = borderWidth;
|
||||
ctx.strokeStyle = borderColor;
|
||||
ctx.fillStyle = bgColor;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + w, y);
|
||||
ctx.lineTo(x + w, y + h);
|
||||
ctx.lineTo(x, y + h);
|
||||
ctx.lineTo(x, y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
export function drawElementControllers(
|
||||
ctx: ViewContext2D,
|
||||
elem: ElementSize,
|
||||
opts: Omit<RendererDrawElementOptions, 'loader' | 'parentElementSize'> & { sizeControllers: ElementSizeController }
|
||||
) {
|
||||
const bw = 0;
|
||||
const { x, y, w, h } = elem;
|
||||
const { angle = 0 } = elem;
|
||||
const { sizeControllers } = opts;
|
||||
|
||||
rotateElement(ctx, { x, y, w, h, angle }, () => {
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = wrapperColor;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - bw, y - bw);
|
||||
ctx.lineTo(x + w + bw, y - bw);
|
||||
ctx.lineTo(x + w + bw, y + h + bw);
|
||||
ctx.lineTo(x - bw, y + h + bw);
|
||||
ctx.lineTo(x - bw, y - bw);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
|
||||
Object.keys(sizeControllers).forEach((name: string) => {
|
||||
const ctrl = sizeControllers[name];
|
||||
drawController(ctx, { ...ctrl, ...{} });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function drawElementListShadows(ctx: ViewContext2D, elements: Element<ElementType>[], opts?: Omit<RendererDrawElementOptions, 'loader'>) {
|
||||
elements.forEach((elem) => {
|
||||
let { x, y, w, h } = elem;
|
||||
const { angle = 0 } = elem;
|
||||
if (opts?.calculator) {
|
||||
const { calculator } = opts;
|
||||
const size = calculator.elementSize({ x, y, w, h }, opts.scaleInfo, opts.viewSize);
|
||||
x = size.x;
|
||||
y = size.y;
|
||||
w = size.w;
|
||||
h = size.h;
|
||||
}
|
||||
const vertexes = rotateElementVertexes({ x, y, w, h, angle });
|
||||
if (vertexes.length >= 2) {
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = '#aaaaaa';
|
||||
ctx.fillStyle = '#0000001A';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(vertexes[0].x, vertexes[0].y);
|
||||
for (let i = 0; i < vertexes.length; i++) {
|
||||
const p = vertexes[i];
|
||||
ctx.lineTo(p.x, p.y);
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function drawArea(ctx: ViewContext2D, opts: { start: PointSize; end: PointSize }) {
|
||||
const { start, end } = opts;
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = '#1976d2';
|
||||
ctx.fillStyle = '#1976d24f';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(start.x, start.y);
|
||||
ctx.lineTo(end.x, start.y);
|
||||
ctx.lineTo(end.x, end.y);
|
||||
ctx.lineTo(start.x, end.y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
export function drawListArea(ctx: ViewContext2D, opts: { areaSize: AreaSize }) {
|
||||
const { areaSize } = opts;
|
||||
const { x, y, w, h } = areaSize;
|
||||
ctx.setLineDash([]);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = '#1976d2';
|
||||
ctx.fillStyle = '#1976d21c';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + w, y);
|
||||
ctx.lineTo(x + w, y + h);
|
||||
ctx.lineTo(x, y + h);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
export function drawGroupsWrapper(ctx: ViewContext2D, elemList: ElementSize[]) {
|
||||
let totalX = 0;
|
||||
let totalY = 0;
|
||||
let totalAngle = 0;
|
||||
|
||||
for (let i = 0; i < elemList.length; i++) {
|
||||
const elem = elemList[i];
|
||||
const bw = 0;
|
||||
const { x, y, w, h, angle = 0 } = elem;
|
||||
totalX += x;
|
||||
totalY += y;
|
||||
totalAngle += angle;
|
||||
|
||||
rotateElement(ctx, { x: totalX, y: totalY, w, h, angle: totalAngle }, () => {
|
||||
ctx.setLineDash([4, 4]);
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = wrapperColor;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - bw, y - bw);
|
||||
ctx.lineTo(x + w + bw, y - bw);
|
||||
ctx.lineTo(x + w + bw, y + h + bw);
|
||||
ctx.lineTo(x - bw, y + h + bw);
|
||||
ctx.lineTo(x - bw, y - bw);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
});
|
||||
}
|
||||
}
|
||||
379
packages/core/src/middleware/deep-selector/index.ts
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
import { getSelectedElementIndexes, getSelectedElements, calcElementsViewInfo } from '@idraw/util';
|
||||
import type { Point, PointWatcherEvent, BoardMiddleware, Element, ElementSize, ActionType, ResizeType, DeepSelectorSharedStorage } from './types';
|
||||
import { drawPointWrapper, drawHoverWrapper, drawElementControllers, drawArea, drawListArea, drawGroupsWrapper } from './draw-wrapper';
|
||||
import { calcElementControllerStyle } from './controller';
|
||||
import { getPointTarget, resizeElement, getSelectedListArea, calcSelectedElementsArea, isElementInGroup } from './util';
|
||||
import { key, keyHoverElementSize, keyActionType, keyResizeType, keyAreaStart, keyAreaEnd, keyInGroupQueue } from './config';
|
||||
|
||||
export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage> = (opts) => {
|
||||
const { viewer, sharer, viewContent, calculator } = opts;
|
||||
const { helperContext } = viewContent;
|
||||
let prevPoint: Point | null = null;
|
||||
|
||||
sharer.setSharedStorage(keyActionType, null);
|
||||
|
||||
const getIndexes = () => {
|
||||
const data = sharer.getActiveStorage('data');
|
||||
if (data) {
|
||||
const uuids = sharer.getActiveStorage('selectedUUIDs');
|
||||
const idxes: Array<number | string> = getSelectedElementIndexes(data, uuids);
|
||||
return idxes;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const getActiveElements = () => {
|
||||
const data = sharer.getActiveStorage('data');
|
||||
if (data) {
|
||||
const uuids = sharer.getActiveStorage('selectedUUIDs');
|
||||
const elems = getSelectedElements(data, uuids);
|
||||
return elems;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const pushGroupQueue = (elem: Element<'group'>) => {
|
||||
let groupQueue = sharer.getSharedStorage(keyInGroupQueue);
|
||||
if (!Array.isArray(groupQueue)) {
|
||||
groupQueue = [];
|
||||
}
|
||||
if (groupQueue.length > 0) {
|
||||
if (isElementInGroup(elem, groupQueue[groupQueue.length - 1])) {
|
||||
groupQueue.push(elem);
|
||||
} else {
|
||||
groupQueue = [];
|
||||
}
|
||||
} else if (groupQueue.length === 0) {
|
||||
groupQueue.push(elem);
|
||||
}
|
||||
sharer.setSharedStorage(keyInGroupQueue, groupQueue);
|
||||
return groupQueue.length > 0;
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
sharer.setSharedStorage(keyActionType, null);
|
||||
sharer.setSharedStorage(keyHoverElementSize, null);
|
||||
sharer.setSharedStorage(keyResizeType, null);
|
||||
sharer.setSharedStorage(keyAreaStart, null);
|
||||
sharer.setSharedStorage(keyAreaEnd, null);
|
||||
sharer.setSharedStorage(keyInGroupQueue, null);
|
||||
};
|
||||
|
||||
clear();
|
||||
|
||||
return {
|
||||
mode: key,
|
||||
hover: (e: PointWatcherEvent) => {
|
||||
const data = sharer.getActiveStorage('data');
|
||||
const resizeType = sharer.getSharedStorage(keyResizeType);
|
||||
const actionType = sharer.getSharedStorage(keyActionType);
|
||||
if (resizeType || (['area', 'drag', 'drag-list'] as ActionType[]).includes(actionType)) {
|
||||
sharer.setSharedStorage(keyHoverElementSize, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (actionType === 'drag') {
|
||||
sharer.setSharedStorage(keyHoverElementSize, null);
|
||||
} else if (data) {
|
||||
const selectedElements = getActiveElements();
|
||||
const scaleInfo = sharer.getActiveScaleInfo();
|
||||
const viewSize = sharer.getActiveViewSizeInfo();
|
||||
const target = getPointTarget(e.point, {
|
||||
ctx: helperContext,
|
||||
data,
|
||||
selectedIndexes: getIndexes(),
|
||||
selectedUUIDs: sharer.getActiveStorage('selectedUUIDs') || [],
|
||||
selectedElements: selectedElements,
|
||||
scaleInfo,
|
||||
viewSize,
|
||||
calculator,
|
||||
areaSize: calcSelectedElementsArea(selectedElements, {
|
||||
scaleInfo,
|
||||
viewSize,
|
||||
calculator
|
||||
}),
|
||||
groupQueue: [] // TODO
|
||||
});
|
||||
if (target.type === 'over-element' && target?.elements?.length === 1) {
|
||||
const { x, y, w, h, angle } = target.elements[0];
|
||||
sharer.setSharedStorage(keyHoverElementSize, { x, y, w, h, angle });
|
||||
viewer.drawFrame();
|
||||
return;
|
||||
}
|
||||
if (sharer.getSharedStorage(keyHoverElementSize)) {
|
||||
sharer.setSharedStorage(keyHoverElementSize, null);
|
||||
viewer.drawFrame();
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
pointStart: (e: PointWatcherEvent) => {
|
||||
// reset all shared storage
|
||||
// clear();
|
||||
sharer.setSharedStorage(keyHoverElementSize, null);
|
||||
const data = sharer.getActiveStorage('data');
|
||||
const listAreaSize = calcSelectedElementsArea(getActiveElements(), {
|
||||
scaleInfo: sharer.getActiveScaleInfo(),
|
||||
viewSize: sharer.getActiveViewSizeInfo(),
|
||||
calculator
|
||||
});
|
||||
const target = getPointTarget(e.point, {
|
||||
ctx: helperContext,
|
||||
data,
|
||||
selectedIndexes: getIndexes(),
|
||||
selectedUUIDs: sharer.getActiveStorage('selectedUUIDs') || [],
|
||||
selectedElements: getActiveElements(),
|
||||
scaleInfo: sharer.getActiveScaleInfo(),
|
||||
viewSize: sharer.getActiveViewSizeInfo(),
|
||||
calculator,
|
||||
areaSize: listAreaSize,
|
||||
groupQueue: [] // TODO
|
||||
});
|
||||
|
||||
if (target.type === 'list-area') {
|
||||
sharer.setSharedStorage(keyActionType, 'drag-list');
|
||||
} else if (target.type === 'over-element' && target?.uuids?.length === 1 && target?.elements?.length === 1) {
|
||||
sharer.setActiveStorage('selectedUUIDs', target?.uuids[0] ? [target?.uuids[0]] : []);
|
||||
sharer.setSharedStorage(keyActionType, 'drag');
|
||||
} else if (target.type?.startsWith('resize-')) {
|
||||
sharer.setSharedStorage(keyResizeType, target.type as ResizeType);
|
||||
sharer.setSharedStorage(keyActionType, 'resize');
|
||||
} else {
|
||||
clear();
|
||||
sharer.setSharedStorage(keyActionType, 'area');
|
||||
sharer.setSharedStorage(keyAreaStart, e.point);
|
||||
sharer.setActiveStorage('selectedUUIDs', []);
|
||||
}
|
||||
if (target.type) {
|
||||
prevPoint = e.point;
|
||||
} else {
|
||||
prevPoint = null;
|
||||
}
|
||||
viewer.drawFrame();
|
||||
},
|
||||
|
||||
pointMove: (e: PointWatcherEvent) => {
|
||||
const data = sharer.getActiveStorage('data');
|
||||
const indexes = getIndexes();
|
||||
const elems = getActiveElements();
|
||||
const scale = sharer.getActiveStorage('scale') || 1;
|
||||
const start = prevPoint;
|
||||
const end = e.point;
|
||||
const resizeType = sharer.getSharedStorage(keyResizeType);
|
||||
const actionType = sharer.getSharedStorage(keyActionType);
|
||||
|
||||
if (actionType === 'drag') {
|
||||
if (data && elems?.length === 1 && indexes?.length === 1 && typeof indexes[0] === 'number' && indexes[0] >= 0 && start && end) {
|
||||
data.elements[indexes[0]].x += (end.x - start.x) / scale;
|
||||
data.elements[indexes[0]].y += (end.y - start.y) / scale;
|
||||
sharer.setActiveStorage('data', data);
|
||||
prevPoint = e.point;
|
||||
} else {
|
||||
prevPoint = null;
|
||||
}
|
||||
viewer.drawFrame();
|
||||
} else if (actionType === 'drag-list') {
|
||||
if (data && start && end && indexes?.length > 1) {
|
||||
const moveX = (end.x - start.x) / scale;
|
||||
const moveY = (end.y - start.y) / scale;
|
||||
indexes.forEach((idx: number | string) => {
|
||||
if (typeof idx === 'number' && data.elements[idx]) {
|
||||
data.elements[idx].x += moveX;
|
||||
data.elements[idx].y += moveY;
|
||||
}
|
||||
});
|
||||
|
||||
sharer.setActiveStorage('data', data);
|
||||
prevPoint = e.point;
|
||||
} else {
|
||||
prevPoint = null;
|
||||
}
|
||||
viewer.drawFrame();
|
||||
} else if (actionType === 'resize') {
|
||||
if (
|
||||
data &&
|
||||
elems?.length === 1 &&
|
||||
indexes?.length === 1 &&
|
||||
typeof indexes[0] === 'number' &&
|
||||
indexes[0] >= 0 &&
|
||||
start &&
|
||||
resizeType?.startsWith('resize-')
|
||||
) {
|
||||
const resizedElemSize = resizeElement(elems[0], { scale, start, end, resizeType });
|
||||
data.elements[indexes[0]].x = resizedElemSize.x;
|
||||
data.elements[indexes[0]].y = resizedElemSize.y;
|
||||
data.elements[indexes[0]].w = resizedElemSize.w;
|
||||
data.elements[indexes[0]].h = resizedElemSize.h;
|
||||
prevPoint = e.point;
|
||||
viewer.drawFrame();
|
||||
}
|
||||
} else if (actionType === 'area') {
|
||||
sharer.setSharedStorage(keyAreaEnd, e.point);
|
||||
viewer.drawFrame();
|
||||
}
|
||||
},
|
||||
|
||||
pointEnd(e: PointWatcherEvent) {
|
||||
const data = sharer.getActiveStorage('data');
|
||||
const resizeType = sharer.getSharedStorage(keyResizeType);
|
||||
const actionType = sharer.getSharedStorage(keyActionType);
|
||||
const scaleInfo = sharer.getActiveScaleInfo();
|
||||
const viewSize = sharer.getActiveViewSizeInfo();
|
||||
const { offsetLeft, offsetTop } = scaleInfo;
|
||||
let needDrawFrame = false;
|
||||
|
||||
if (actionType === 'resize' && resizeType) {
|
||||
sharer.setSharedStorage(keyResizeType, null);
|
||||
} else if (actionType === 'area') {
|
||||
sharer.setSharedStorage(keyActionType, null);
|
||||
if (data) {
|
||||
const start = sharer.getSharedStorage(keyAreaStart);
|
||||
const end = sharer.getSharedStorage(keyAreaEnd);
|
||||
if (start && end) {
|
||||
const { uuids } = getSelectedListArea(data, {
|
||||
start,
|
||||
end,
|
||||
calculator,
|
||||
scaleInfo: sharer.getActiveScaleInfo(),
|
||||
viewSize: sharer.getActiveViewSizeInfo()
|
||||
});
|
||||
|
||||
if (uuids.length > 0) {
|
||||
sharer.setActiveStorage('selectedUUIDs', uuids);
|
||||
sharer.setSharedStorage(keyActionType, 'drag-list');
|
||||
needDrawFrame = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (actionType === 'drag-list') {
|
||||
sharer.setSharedStorage(keyActionType, 'drag-list-end');
|
||||
needDrawFrame = true;
|
||||
} else if (data) {
|
||||
const result = calculator.getPointElement(e.point, data, sharer.getActiveScaleInfo(), sharer.getActiveViewSizeInfo());
|
||||
if (result.element) {
|
||||
sharer.setSharedStorage(keyActionType, 'select');
|
||||
needDrawFrame = true;
|
||||
} else {
|
||||
sharer.setSharedStorage(keyActionType, null);
|
||||
}
|
||||
}
|
||||
if (sharer.getSharedStorage(keyActionType) === null) {
|
||||
clear();
|
||||
needDrawFrame = true;
|
||||
}
|
||||
|
||||
const finalDrawFrame = () => {
|
||||
if (!needDrawFrame) {
|
||||
return;
|
||||
}
|
||||
if (data && Array.isArray(data?.elements) && (['drag', 'drag-list'] as ActionType[]).includes(actionType)) {
|
||||
const viewInfo = calcElementsViewInfo(data.elements, viewSize, { extend: true });
|
||||
sharer.setActiveStorage('contextX', viewInfo.contextSize.contextX);
|
||||
sharer.setActiveStorage('contextY', viewInfo.contextSize.contextY);
|
||||
sharer.setActiveStorage('contextHeight', viewInfo.contextSize.contextHeight);
|
||||
sharer.setActiveStorage('contextWidth', viewInfo.contextSize.contextWidth);
|
||||
viewer.scrollX(offsetLeft + viewInfo.changeContextLeft);
|
||||
viewer.scrollY(offsetTop + viewInfo.changeContextTop);
|
||||
}
|
||||
viewer.drawFrame();
|
||||
};
|
||||
|
||||
finalDrawFrame();
|
||||
},
|
||||
|
||||
pointLeave() {
|
||||
clear();
|
||||
viewer.drawFrame();
|
||||
},
|
||||
|
||||
doubleClick(e: PointWatcherEvent) {
|
||||
// console.log('doubleClick =====', e);
|
||||
const groupQueue = sharer.getSharedStorage(keyInGroupQueue);
|
||||
const data = sharer.getActiveStorage('data');
|
||||
const target = getPointTarget(e.point, {
|
||||
ctx: helperContext,
|
||||
data,
|
||||
selectedIndexes: getIndexes(),
|
||||
selectedUUIDs: sharer.getActiveStorage('selectedUUIDs') || [],
|
||||
selectedElements: getActiveElements(),
|
||||
scaleInfo: sharer.getActiveScaleInfo(),
|
||||
viewSize: sharer.getActiveViewSizeInfo(),
|
||||
calculator,
|
||||
areaSize: null,
|
||||
groupQueue: [] // TODO
|
||||
});
|
||||
if (target.elements.length === 1 && target.elements[0]?.type === 'group') {
|
||||
const pushResult = pushGroupQueue(target.elements[0] as Element<'group'>);
|
||||
if (pushResult === true) {
|
||||
viewer.drawFrame();
|
||||
return;
|
||||
}
|
||||
}
|
||||
sharer.setSharedStorage(keyActionType, null);
|
||||
console.log('doubleClick target ======= ', target);
|
||||
},
|
||||
|
||||
beforeDrawFrame({ snapshot }) {
|
||||
const { activeStore, sharedStore } = snapshot;
|
||||
const {
|
||||
data,
|
||||
selectedUUIDs,
|
||||
scale,
|
||||
offsetLeft,
|
||||
offsetTop,
|
||||
offsetRight,
|
||||
offsetBottom,
|
||||
width,
|
||||
height,
|
||||
contextX,
|
||||
contextY,
|
||||
contextHeight,
|
||||
contextWidth,
|
||||
devicePixelRatio
|
||||
} = activeStore;
|
||||
const scaleInfo = { scale, offsetLeft, offsetTop, offsetRight, offsetBottom };
|
||||
const viewSize = { width, height, contextX, contextY, contextHeight, contextWidth, devicePixelRatio };
|
||||
// const elem = data?.elements?.[selectedIndexes?.[0] as number];
|
||||
const selectedElements = getSelectedElements(data, selectedUUIDs);
|
||||
const elem = selectedElements[0];
|
||||
const hoverElement: ElementSize = sharedStore[keyHoverElementSize] as ElementSize;
|
||||
const actionType: ActionType = sharedStore[keyActionType] as ActionType;
|
||||
const areaStart: Point | null = sharedStore[keyAreaStart];
|
||||
const areaEnd: Point | null = sharedStore[keyAreaEnd];
|
||||
const groupQueue: Element<'group'>[] | null = sharedStore[keyInGroupQueue];
|
||||
|
||||
if (groupQueue && groupQueue?.length > 0) {
|
||||
// in group
|
||||
drawGroupsWrapper(helperContext, groupQueue);
|
||||
} else {
|
||||
// in root
|
||||
const drawOpts = { calculator, scaleInfo, viewSize };
|
||||
if (hoverElement && actionType !== 'drag') {
|
||||
const hoverElemSize = calculator.elementSize(hoverElement, scaleInfo, viewSize);
|
||||
drawHoverWrapper(helperContext, hoverElemSize);
|
||||
}
|
||||
|
||||
if (elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) {
|
||||
const selectedElemSize = calculator.elementSize(elem, scaleInfo, viewSize);
|
||||
const sizeControllers = calcElementControllerStyle(selectedElemSize);
|
||||
drawPointWrapper(helperContext, selectedElemSize);
|
||||
drawElementControllers(helperContext, selectedElemSize, { ...drawOpts, sizeControllers });
|
||||
} else if (actionType === 'area' && areaStart && areaEnd) {
|
||||
drawArea(helperContext, { start: areaStart, end: areaEnd });
|
||||
} else if ((['drag-list', 'drag-list-end'] as ActionType[]).includes(actionType)) {
|
||||
const listAreaSize = calcSelectedElementsArea(getActiveElements(), {
|
||||
scaleInfo: sharer.getActiveScaleInfo(),
|
||||
viewSize: sharer.getActiveViewSizeInfo(),
|
||||
calculator
|
||||
});
|
||||
if (listAreaSize) {
|
||||
drawListArea(helperContext, { areaSize: listAreaSize });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
63
packages/core/src/middleware/deep-selector/types.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import { keyHoverElementSize, keyActionType, keyResizeType, keyAreaStart, keyAreaEnd, keyInGroupQueue } from './config';
|
||||
|
||||
import {
|
||||
Data,
|
||||
ElementSize,
|
||||
ElementType,
|
||||
Element,
|
||||
ViewContext2D,
|
||||
Point,
|
||||
PointSize,
|
||||
ViewScaleInfo,
|
||||
ViewSizeInfo,
|
||||
ViewCalculator,
|
||||
PointWatcherEvent,
|
||||
BoardMiddleware
|
||||
} from '@idraw/types';
|
||||
|
||||
export {
|
||||
Data,
|
||||
ElementType,
|
||||
Element,
|
||||
ElementSize,
|
||||
ViewContext2D,
|
||||
Point,
|
||||
PointSize,
|
||||
ViewScaleInfo,
|
||||
ViewSizeInfo,
|
||||
ViewCalculator,
|
||||
PointWatcherEvent,
|
||||
BoardMiddleware
|
||||
};
|
||||
|
||||
export type ControllerStyle = ElementSize & {
|
||||
borderWidth: number;
|
||||
borderColor: string;
|
||||
bgColor: string;
|
||||
};
|
||||
|
||||
export type ElementSizeController = Record<string, ControllerStyle>;
|
||||
|
||||
export type ResizeType = 'resize-left' | 'resize-right' | 'resize-top' | 'resize-bottom';
|
||||
|
||||
export type PointTargetType = null | 'list-area' | 'over-element' | ResizeType;
|
||||
|
||||
export interface PointTarget {
|
||||
type: PointTargetType;
|
||||
elements: Element<ElementType>[];
|
||||
indexes: Array<number | string>;
|
||||
uuids: string[];
|
||||
}
|
||||
|
||||
export type AreaSize = ElementSize;
|
||||
|
||||
export type ActionType = 'select' | 'drag-list' | 'drag-list-end' | 'drag' | 'hover' | 'resize' | 'area' | null;
|
||||
|
||||
export type DeepSelectorSharedStorage = {
|
||||
[keyHoverElementSize]: ElementSize | null;
|
||||
[keyActionType]: ActionType;
|
||||
[keyResizeType]: ResizeType | null;
|
||||
[keyAreaStart]: Point | null;
|
||||
[keyAreaEnd]: Point | null;
|
||||
[keyInGroupQueue]: Element<'group'>[] | null;
|
||||
};
|
||||
488
packages/core/src/middleware/deep-selector/util.ts
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
import type {
|
||||
Data,
|
||||
Element,
|
||||
ViewContext2D,
|
||||
Point,
|
||||
PointSize,
|
||||
PointTarget,
|
||||
PointTargetType,
|
||||
ViewScaleInfo,
|
||||
ViewCalculator,
|
||||
ElementType,
|
||||
ElementSize,
|
||||
ResizeType,
|
||||
AreaSize,
|
||||
ViewSizeInfo
|
||||
} from './types';
|
||||
import { rotateElement, calcElementCenter, rotateElementVertexes } from '@idraw/util';
|
||||
import { calcElementControllerStyle } from './controller';
|
||||
|
||||
function parseRadian(angle: number) {
|
||||
return (angle * Math.PI) / 180;
|
||||
}
|
||||
|
||||
function calcMoveDist(moveX: number, moveY: number) {
|
||||
return Math.sqrt(moveX * moveX + moveY * moveY);
|
||||
}
|
||||
|
||||
function changeMoveDistDirect(moveDist: number, moveDirect: number) {
|
||||
return moveDirect > 0 ? Math.abs(moveDist) : 0 - Math.abs(moveDist);
|
||||
}
|
||||
|
||||
export function getPointTarget(
|
||||
p: PointSize,
|
||||
opts: {
|
||||
ctx: ViewContext2D;
|
||||
data?: Data | null;
|
||||
selectedIndexes?: Array<number | string>;
|
||||
selectedUUIDs: Array<string>;
|
||||
selectedElements?: Element<ElementType>[];
|
||||
areaSize?: AreaSize | null;
|
||||
scaleInfo: ViewScaleInfo;
|
||||
viewSize: ViewSizeInfo;
|
||||
calculator: ViewCalculator;
|
||||
groupQueue: Element<'group'>[];
|
||||
}
|
||||
): PointTarget {
|
||||
const target: PointTarget = {
|
||||
type: null,
|
||||
elements: [],
|
||||
indexes: [],
|
||||
uuids: []
|
||||
};
|
||||
const { ctx, data, calculator, selectedElements, selectedIndexes, selectedUUIDs, scaleInfo, viewSize, areaSize } = opts;
|
||||
|
||||
// list area
|
||||
if (areaSize && Array.isArray(selectedElements) && selectedElements?.length > 1 && Array.isArray(selectedIndexes) && selectedIndexes?.length > 1) {
|
||||
const { x, y, w, h } = areaSize;
|
||||
if (p.x >= x && p.x <= x + w && p.y >= y && p.y <= y + h) {
|
||||
target.type = 'list-area';
|
||||
target.elements = selectedElements;
|
||||
target.indexes = selectedIndexes;
|
||||
target.uuids = selectedUUIDs;
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
// resize
|
||||
if (selectedElements?.length === 1) {
|
||||
const elemSize = calculator.elementSize(selectedElements[0], scaleInfo, viewSize);
|
||||
const ctrls = calcElementControllerStyle(elemSize);
|
||||
rotateElement(ctx, elemSize, () => {
|
||||
const ctrlKeys = Object.keys(ctrls);
|
||||
for (let i = 0; i < ctrlKeys.length; i++) {
|
||||
const key = ctrlKeys[i];
|
||||
const ctrl = ctrls[key];
|
||||
const { x, y, w, h } = ctrl;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + w, y);
|
||||
ctx.lineTo(x + w, y + h);
|
||||
ctx.lineTo(x, y + h);
|
||||
ctx.closePath();
|
||||
if (ctx.isPointInPath(p.x, p.y)) {
|
||||
target.type = `resize-${key}` as PointTargetType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (target.type !== null) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
// over-element
|
||||
if (data) {
|
||||
const { index, element } = calculator.getPointElement(p as Point, data, scaleInfo, viewSize);
|
||||
if (index >= 0 && element) {
|
||||
target.indexes = [index];
|
||||
target.elements = [element];
|
||||
target.uuids = [element.uuid];
|
||||
target.type = 'over-element';
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
export function resizeElement(
|
||||
elem: Element<ElementType>,
|
||||
opts: {
|
||||
start: Point;
|
||||
end: Point;
|
||||
resizeType: ResizeType;
|
||||
scale: number;
|
||||
}
|
||||
): ElementSize {
|
||||
let { x, y, w, h, angle = 0 } = elem;
|
||||
if (angle < 0) {
|
||||
angle = Math.max(0, 360 + angle);
|
||||
}
|
||||
|
||||
angle = angle > 0 ? angle : Math.max(0, angle + 360);
|
||||
const { start, end, resizeType, scale } = opts;
|
||||
|
||||
let moveX = (end.x - start.x) / scale;
|
||||
let moveY = (end.y - start.y) / scale;
|
||||
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
const maxDist = Math.max(Math.abs(moveX), Math.abs(moveY));
|
||||
moveX = (moveX >= 0 ? 1 : -1) * maxDist;
|
||||
moveY = (((moveY >= 0 ? 1 : -1) * maxDist) / elem.w) * elem.h;
|
||||
}
|
||||
|
||||
switch (resizeType) {
|
||||
case 'resize-top': {
|
||||
if (elem.angle === 0) {
|
||||
if (h - moveY > 0) {
|
||||
y += moveY;
|
||||
h -= moveY;
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
x += ((moveY / elem.h) * elem.w) / 2;
|
||||
w -= (moveY / elem.h) * elem.w;
|
||||
}
|
||||
}
|
||||
} else if (elem.angle !== undefined && (elem.angle > 0 || elem.angle < 0)) {
|
||||
const angle = elem.angle > 0 ? elem.angle : Math.max(0, elem.angle + 360);
|
||||
let moveDist = calcMoveDist(moveX, moveY);
|
||||
let centerX = x + elem.w / 2;
|
||||
let centerY = y + elem.h / 2;
|
||||
if (angle < 90) {
|
||||
moveDist = 0 - changeMoveDistDirect(moveDist, moveY);
|
||||
const radian = parseRadian(angle);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX + centerMoveDist * Math.sin(radian);
|
||||
centerY = centerY - centerMoveDist * Math.cos(radian);
|
||||
} else if (angle < 180) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveX);
|
||||
const radian = parseRadian(angle - 90);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX + centerMoveDist * Math.cos(radian);
|
||||
centerY = centerY + centerMoveDist * Math.sin(radian);
|
||||
} else if (angle < 270) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveY);
|
||||
const radian = parseRadian(angle - 180);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX - centerMoveDist * Math.sin(radian);
|
||||
centerY = centerY + centerMoveDist * Math.cos(radian);
|
||||
} else if (angle < 360) {
|
||||
moveDist = 0 - changeMoveDistDirect(moveDist, moveX);
|
||||
const radian = parseRadian(angle - 270);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX - centerMoveDist * Math.cos(radian);
|
||||
centerY = centerY - centerMoveDist * Math.sin(radian);
|
||||
}
|
||||
if (h + moveDist > 0) {
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
w = w + (moveDist / elem.h) * elem.w;
|
||||
}
|
||||
h = h + moveDist;
|
||||
x = centerX - w / 2;
|
||||
y = centerY - h / 2;
|
||||
}
|
||||
} else {
|
||||
if (h - moveY > 0) {
|
||||
y += moveY;
|
||||
h -= moveY;
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
x -= moveX / 2;
|
||||
w += moveX;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'resize-bottom': {
|
||||
if (elem.angle === 0) {
|
||||
if (elem.h + moveY > 0) {
|
||||
h += moveY;
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
x -= ((moveY / elem.h) * elem.w) / 2;
|
||||
w += (moveY / elem.h) * elem.w;
|
||||
}
|
||||
}
|
||||
} else if (elem.angle !== undefined && (elem.angle > 0 || elem.angle < 0)) {
|
||||
const angle = elem.angle > 0 ? elem.angle : Math.max(0, elem.angle + 360);
|
||||
let moveDist = calcMoveDist(moveX, moveY);
|
||||
let centerX = x + elem.w / 2;
|
||||
let centerY = y + elem.h / 2;
|
||||
if (angle < 90) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveY);
|
||||
const radian = parseRadian(angle);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX - centerMoveDist * Math.sin(radian);
|
||||
centerY = centerY + centerMoveDist * Math.cos(radian);
|
||||
} else if (angle < 180) {
|
||||
moveDist = 0 - changeMoveDistDirect(moveDist, moveX);
|
||||
const radian = parseRadian(angle - 90);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX - centerMoveDist * Math.cos(radian);
|
||||
centerY = centerY - centerMoveDist * Math.sin(radian);
|
||||
} else if (angle < 270) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveX);
|
||||
const radian = parseRadian(angle - 180);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX + centerMoveDist * Math.sin(radian);
|
||||
centerY = centerY - centerMoveDist * Math.cos(radian);
|
||||
} else if (angle < 360) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveX);
|
||||
const radian = parseRadian(angle - 270);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX + centerMoveDist * Math.cos(radian);
|
||||
centerY = centerY + centerMoveDist * Math.sin(radian);
|
||||
}
|
||||
if (h + moveDist > 0) {
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
w = w + (moveDist / elem.h) * elem.w;
|
||||
}
|
||||
h = h + moveDist;
|
||||
x = centerX - w / 2;
|
||||
y = centerY - h / 2;
|
||||
}
|
||||
} else {
|
||||
if (elem.h + moveY > 0) {
|
||||
h += moveY;
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
x -= ((moveY / elem.h) * elem.w) / 2;
|
||||
w += (moveY / elem.h) * elem.w;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'resize-left': {
|
||||
if (angle === 0 || !angle) {
|
||||
if (elem.w - moveX > 0) {
|
||||
x += moveX;
|
||||
w -= moveX;
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
h -= (moveX / elem.w) * elem.h;
|
||||
y += ((moveX / elem.w) * elem.h) / 2;
|
||||
}
|
||||
}
|
||||
} else if (elem.angle !== undefined && (elem.angle > 0 || elem.angle < 0)) {
|
||||
const angle = elem.angle > 0 ? elem.angle : Math.max(0, elem.angle + 360);
|
||||
let moveDist = calcMoveDist(moveX, moveY);
|
||||
let centerX = x + elem.w / 2;
|
||||
let centerY = y + elem.h / 2;
|
||||
if (angle < 90) {
|
||||
moveDist = 0 - changeMoveDistDirect(moveDist, moveX);
|
||||
const radian = parseRadian(angle);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX - centerMoveDist * Math.cos(radian);
|
||||
centerY = centerY - centerMoveDist * Math.sin(radian);
|
||||
} else if (angle < 180) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveX);
|
||||
const radian = parseRadian(angle - 90);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX + centerMoveDist * Math.sin(radian);
|
||||
centerY = centerY - centerMoveDist * Math.cos(radian);
|
||||
} else if (angle < 270) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveY);
|
||||
const radian = parseRadian(angle - 180);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX + centerMoveDist * Math.cos(radian);
|
||||
centerY = centerY + centerMoveDist * Math.sin(radian);
|
||||
} else if (angle < 360) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveY);
|
||||
const radian = parseRadian(angle - 270);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX - centerMoveDist * Math.sin(radian);
|
||||
centerY = centerY + centerMoveDist * Math.cos(radian);
|
||||
}
|
||||
if (w + moveDist > 0) {
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
h = h + (moveDist / elem.w) * elem.h;
|
||||
}
|
||||
w = w + moveDist;
|
||||
x = centerX - w / 2;
|
||||
y = centerY - h / 2;
|
||||
}
|
||||
} else {
|
||||
if (elem.w - moveX > 0) {
|
||||
x += moveX;
|
||||
w -= moveX;
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
h -= (moveX / elem.w) * elem.h;
|
||||
y += ((moveX / elem.w) * elem.h) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'resize-right': {
|
||||
if (angle === 0 || !angle) {
|
||||
if (elem.w + moveX > 0) {
|
||||
w += moveX;
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
y -= (moveX * elem.h) / elem.w / 2;
|
||||
h += (moveX * elem.h) / elem.w;
|
||||
}
|
||||
}
|
||||
} else if (elem.angle !== undefined && (elem.angle > 0 || elem.angle < 0)) {
|
||||
const angle = elem.angle > 0 ? elem.angle : Math.max(0, elem.angle + 360);
|
||||
let moveDist = calcMoveDist(moveX, moveY);
|
||||
let centerX = x + elem.w / 2;
|
||||
let centerY = y + elem.h / 2;
|
||||
if (angle < 90) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveY);
|
||||
const radian = parseRadian(angle);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX + centerMoveDist * Math.cos(radian);
|
||||
centerY = centerY + centerMoveDist * Math.sin(radian);
|
||||
} else if (angle < 180) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveY);
|
||||
const radian = parseRadian(angle - 90);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX - centerMoveDist * Math.sin(radian);
|
||||
centerY = centerY + centerMoveDist * Math.cos(radian);
|
||||
} else if (angle < 270) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveY);
|
||||
const radian = parseRadian(angle - 180);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX + centerMoveDist * Math.cos(radian);
|
||||
centerY = centerY + centerMoveDist * Math.sin(radian);
|
||||
moveDist = 0 - moveDist;
|
||||
} else if (angle < 360) {
|
||||
moveDist = changeMoveDistDirect(moveDist, moveX);
|
||||
const radian = parseRadian(angle - 270);
|
||||
const centerMoveDist = moveDist / 2;
|
||||
centerX = centerX + centerMoveDist * Math.sin(radian);
|
||||
centerY = centerY - centerMoveDist * Math.cos(radian);
|
||||
}
|
||||
if (w + moveDist > 0) {
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
h = h + (moveDist / elem.w) * elem.h;
|
||||
}
|
||||
w = w + moveDist;
|
||||
x = centerX - w / 2;
|
||||
y = centerY - h / 2;
|
||||
}
|
||||
} else {
|
||||
if (elem.w + moveX > 0) {
|
||||
w += moveX;
|
||||
if (elem.operation?.limitRatio === true) {
|
||||
h += (moveX * elem.h) / elem.w;
|
||||
y -= (moveX * elem.h) / elem.w / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return { x, y, w, h, angle: elem.angle };
|
||||
}
|
||||
|
||||
export function getSelectedListArea(
|
||||
data: Data,
|
||||
opts: {
|
||||
start: Point;
|
||||
end: Point;
|
||||
scaleInfo: ViewScaleInfo;
|
||||
viewSize: ViewSizeInfo;
|
||||
calculator: ViewCalculator;
|
||||
}
|
||||
): { indexes: number[]; uuids: string[] } {
|
||||
const indexes: number[] = [];
|
||||
const uuids: string[] = [];
|
||||
const { calculator, scaleInfo, viewSize, start, end } = opts;
|
||||
|
||||
if (!(Array.isArray(data.elements) && start && end)) {
|
||||
return { indexes, uuids };
|
||||
}
|
||||
const startX = Math.min(start.x, end.x);
|
||||
const endX = Math.max(start.x, end.x);
|
||||
const startY = Math.min(start.y, end.y);
|
||||
const endY = Math.max(start.y, end.y);
|
||||
|
||||
data.elements.forEach((elem, idx) => {
|
||||
const elemSize = calculator.elementSize(elem, scaleInfo, viewSize);
|
||||
|
||||
const center = calcElementCenter(elemSize);
|
||||
if (center.x >= startX && center.x <= endX && center.y >= startY && center.y <= endY) {
|
||||
indexes.push(idx);
|
||||
uuids.push(elem.uuid);
|
||||
if (elemSize.angle && (elemSize.angle > 0 || elemSize.angle < 0)) {
|
||||
const ves = rotateElementVertexes(elemSize);
|
||||
if (ves.length === 4) {
|
||||
const xList = [ves[0].x, ves[1].x, ves[2].x, ves[3].x];
|
||||
const yList = [ves[0].y, ves[1].y, ves[2].y, ves[3].y];
|
||||
elemSize.x = Math.min(...xList);
|
||||
elemSize.y = Math.min(...yList);
|
||||
elemSize.w = Math.abs(Math.max(...xList) - Math.min(...xList));
|
||||
elemSize.h = Math.abs(Math.max(...yList) - Math.min(...yList));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return { indexes, uuids };
|
||||
}
|
||||
|
||||
export function calcSelectedElementsArea(
|
||||
elements: Element<ElementType>[],
|
||||
opts: {
|
||||
scaleInfo: ViewScaleInfo;
|
||||
viewSize: ViewSizeInfo;
|
||||
calculator: ViewCalculator;
|
||||
}
|
||||
): AreaSize | null {
|
||||
if (!Array.isArray(elements)) {
|
||||
return null;
|
||||
}
|
||||
const area: AreaSize = { x: 0, y: 0, w: 0, h: 0 };
|
||||
const { calculator, scaleInfo, viewSize } = opts;
|
||||
let prevElemSize: ElementSize | null = null;
|
||||
|
||||
elements.forEach((elem) => {
|
||||
const elemSize = calculator.elementSize(elem, scaleInfo, viewSize);
|
||||
|
||||
if (elemSize.angle && (elemSize.angle > 0 || elemSize.angle < 0)) {
|
||||
const ves = rotateElementVertexes(elemSize);
|
||||
|
||||
if (ves.length === 4) {
|
||||
const xList = [ves[0].x, ves[1].x, ves[2].x, ves[3].x];
|
||||
const yList = [ves[0].y, ves[1].y, ves[2].y, ves[3].y];
|
||||
elemSize.x = Math.min(...xList);
|
||||
elemSize.y = Math.min(...yList);
|
||||
elemSize.w = Math.abs(Math.max(...xList) - Math.min(...xList));
|
||||
elemSize.h = Math.abs(Math.max(...yList) - Math.min(...yList));
|
||||
}
|
||||
}
|
||||
if (prevElemSize) {
|
||||
const areaStartX = Math.min(elemSize.x, area.x);
|
||||
const areaStartY = Math.min(elemSize.y, area.y);
|
||||
|
||||
const areaEndX = Math.max(elemSize.x + elemSize.w, area.x + area.w);
|
||||
const areaEndY = Math.max(elemSize.y + elemSize.h, area.y + area.h);
|
||||
|
||||
area.x = areaStartX;
|
||||
area.y = areaStartY;
|
||||
area.w = Math.abs(areaEndX - areaStartX);
|
||||
area.h = Math.abs(areaEndY - areaStartY);
|
||||
} else {
|
||||
area.x = elemSize.x;
|
||||
area.y = elemSize.y;
|
||||
area.w = elemSize.w;
|
||||
area.h = elemSize.h;
|
||||
}
|
||||
prevElemSize = elemSize;
|
||||
});
|
||||
return area;
|
||||
}
|
||||
|
||||
export function isElementInGroup(elem: Element<ElementType>, group: Element<'group'>): boolean {
|
||||
if (group?.type === 'group' && Array.isArray(group?.desc?.children)) {
|
||||
for (let i = 0; i < group.desc.children.length; i++) {
|
||||
const child = group.desc.children[i];
|
||||
if (elem.uuid === child.uuid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { createUUID } from '@idraw/util';
|
||||
import type { ElementSize } from '@idraw/types';
|
||||
import type { DesignComponent, DesignComponentItem } from '../../../src';
|
||||
|
||||
function createButtonItem(variantName: string) {
|
||||
|
|
@ -73,20 +74,21 @@ function createButtonItem(variantName: string) {
|
|||
return componentItem;
|
||||
}
|
||||
|
||||
export function createButton(name: string) {
|
||||
export function createButton(name: string, size?: Partial<ElementSize>) {
|
||||
const button: DesignComponent = {
|
||||
uuid: createUUID(),
|
||||
type: 'component',
|
||||
name: `Button ${name}`,
|
||||
x: 50,
|
||||
y: 50,
|
||||
w: 800,
|
||||
h: 400,
|
||||
w: 360,
|
||||
h: 200,
|
||||
desc: {
|
||||
bgColor: '#aaaaaa54',
|
||||
default: createButtonItem('default'),
|
||||
variants: [createButtonItem('primary'), createButtonItem('secondary')]
|
||||
}
|
||||
},
|
||||
...(size || {})
|
||||
};
|
||||
return button;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { createUUID } from '@idraw/util';
|
||||
import type { ElementSize } from '@idraw/types';
|
||||
import type { DesignComponent, DesignComponentItem } from '../../../src';
|
||||
|
||||
function createCheckboxItem(variantName: string) {
|
||||
|
|
@ -73,20 +74,21 @@ function createCheckboxItem(variantName: string) {
|
|||
return componentItem;
|
||||
}
|
||||
|
||||
export function createCheckbox(name: string) {
|
||||
export function createCheckbox(name: string, size?: Partial<ElementSize>) {
|
||||
const checkbox: DesignComponent = {
|
||||
uuid: createUUID(),
|
||||
type: 'component',
|
||||
name: `Checkbox ${name}`,
|
||||
x: 50,
|
||||
y: 50,
|
||||
w: 800,
|
||||
h: 400,
|
||||
w: 360,
|
||||
h: 200,
|
||||
desc: {
|
||||
bgColor: '#aaaaaa54',
|
||||
default: createCheckboxItem('default'),
|
||||
variants: [createCheckboxItem('primary'), createCheckboxItem('secondary')]
|
||||
}
|
||||
},
|
||||
...(size || {})
|
||||
};
|
||||
return checkbox;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { createButton } from './components/button';
|
|||
import { createCheckbox } from './components/checkbox';
|
||||
|
||||
const data: DesignData = {
|
||||
components: [createButton('001'), createButton('002'), createCheckbox('001'), createCheckbox('002')],
|
||||
components: [createButton('001'), createButton('002', { x: 450 }), createCheckbox('001', { x: 50, y: 300 }), createCheckbox('002', { x: 450, y: 300 })],
|
||||
modules: [],
|
||||
pages: []
|
||||
};
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export const Sketch = (props: DashboardProps) => {
|
|||
return;
|
||||
}
|
||||
const core = refCore.current;
|
||||
const contextSize = calcElementsContextSize(state.viewDrawData.elements, { viewWidth: width, viewHeight: height });
|
||||
const contextSize = calcElementsContextSize(state.viewDrawData.elements, { viewWidth: width, viewHeight: height, extend: true });
|
||||
core.resize({
|
||||
width,
|
||||
height,
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
# @idraw/studio
|
||||
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 124 KiB |
|
|
@ -1,27 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<style></style>
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0; padding: 0;
|
||||
background: #f0f0f088;
|
||||
}
|
||||
canvas {
|
||||
/* border-right: 1px solid #aaaaaa40;
|
||||
border-bottom: 1px solid #aaaaaa40; */
|
||||
background-image:
|
||||
linear-gradient(#aaaaaa40 1px, transparent 0),
|
||||
linear-gradient(90deg, #aaaaaa40 1px, transparent 0),
|
||||
linear-gradient(#aaa 1px, transparent 0),
|
||||
linear-gradient(90deg, #aaa 1px, transparent 0);
|
||||
background-size: 10px 10px, 10px 10px, 50px 50px, 50px 50px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="lab"></div>
|
||||
</body>
|
||||
<script type="module" src="./main.tsx"></script>
|
||||
</html>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { Lab } from '../src/index';
|
||||
|
||||
const dom = document.querySelector('#lab') as HTMLDivElement;
|
||||
const root = createRoot(dom);
|
||||
|
||||
root.render(<Lab />);
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"name": "@idraw/lab",
|
||||
"version": "0.4.0-alpha.0",
|
||||
"dependencies": {
|
||||
"@idraw/core": "^0.4.0-alpha.0",
|
||||
"@idraw/util": "^0.4.0-alpha.0",
|
||||
"antd": "^5.5.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@idraw/types": "^0.4.0-alpha.0",
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.1"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,521 +0,0 @@
|
|||
import type { Data } from '@idraw/types';
|
||||
import { deepClone } from '@idraw/util';
|
||||
|
||||
const data: Data = {
|
||||
elements: [
|
||||
{
|
||||
uuid: 'xxx-0003',
|
||||
type: 'image',
|
||||
x: 100,
|
||||
y: 100,
|
||||
w: 100,
|
||||
h: 100,
|
||||
angle: 30,
|
||||
desc: {
|
||||
src: './images/lena.png'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxxx-0001',
|
||||
x: -50,
|
||||
y: -40,
|
||||
w: 100,
|
||||
h: 100,
|
||||
type: 'circle',
|
||||
desc: {
|
||||
bgColor: '#f44336'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxx-0002',
|
||||
type: 'rect',
|
||||
x: 50,
|
||||
y: 50,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#2196f3'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxx-0004',
|
||||
type: 'image',
|
||||
x: 250,
|
||||
y: 250,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
src: './images/github.png?t=003'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxxx-0005',
|
||||
x: 0,
|
||||
y: 300,
|
||||
w: 100,
|
||||
h: 100,
|
||||
type: 'circle',
|
||||
desc: {
|
||||
bgColor: '#009688'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxxx-0006',
|
||||
x: 300,
|
||||
y: 300,
|
||||
w: 100,
|
||||
h: 100,
|
||||
type: 'circle',
|
||||
desc: {
|
||||
bgColor: '#673ab7'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxxx-0007',
|
||||
x: 300,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
type: 'circle',
|
||||
desc: {
|
||||
bgColor: '#ffc107'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxxx-0008',
|
||||
x: 150,
|
||||
y: 150,
|
||||
w: 100,
|
||||
h: 100,
|
||||
type: 'circle',
|
||||
desc: {
|
||||
bgColor: '#4caf50'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxxx-0009',
|
||||
x: 0,
|
||||
y: 150,
|
||||
w: 100,
|
||||
h: 100,
|
||||
type: 'circle',
|
||||
desc: {
|
||||
bgColor: '#ff9800'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxxx-0010',
|
||||
x: 150,
|
||||
y: 50,
|
||||
w: 100,
|
||||
h: 100,
|
||||
type: 'circle',
|
||||
desc: {
|
||||
bgColor: '#cddc39'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'text-0010',
|
||||
name: 'text-002',
|
||||
x: 300,
|
||||
y: 100,
|
||||
w: 100,
|
||||
h: 60,
|
||||
type: 'text',
|
||||
desc: {
|
||||
fontSize: 16,
|
||||
text: [0, 1, 2, 3, 4].map((i) => `Hello Text ${i}`).join('\r\n'),
|
||||
// text: [0, 1, 2, 3, 4].map(i => `Hello Text ${i}`).join(''),
|
||||
fontWeight: 'bold',
|
||||
color: '#666666',
|
||||
borderRadius: 30,
|
||||
borderWidth: 2,
|
||||
borderColor: '#ff5722'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxx-0011',
|
||||
type: 'svg',
|
||||
x: 400,
|
||||
y: 100,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
svg: `<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M336 421m-48 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0Z" ></path><path d="M688 421m-48 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0Z" ></path><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64z m263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2-44.3-18.7-84.1-45.6-118.3-79.8-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8c18.7-44.3 45.6-84.1 79.8-118.3 34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2 44.3 18.7 84.1 45.6 118.3 79.8 34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8c-18.7 44.3-45.6 84.1-79.8 118.2z"></path><path d="M664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-0.3-4.2-3.9-7.4-8.1-7.4H360c-4.6 0-8.2 3.8-8 8.4 4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6c0.2-4.6-3.4-8.4-8-8.4z" ></path></svg>`
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxx-0012',
|
||||
x: 400,
|
||||
y: 200,
|
||||
w: 150,
|
||||
h: 100,
|
||||
type: 'html',
|
||||
angle: 0,
|
||||
desc: {
|
||||
html: `
|
||||
<style>
|
||||
.btn-box {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.btn {
|
||||
line-height: 1.5715;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
background-image: none;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: 0 2px #00000004;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
height: 32px;
|
||||
padding: 4px 15px;
|
||||
font-size: 14px;
|
||||
border-radius: 2px;
|
||||
color: #000000d9;
|
||||
background: #fff;
|
||||
border-color: #d9d9d9;
|
||||
}
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background: #1890ff;
|
||||
border-color: #1890ff;
|
||||
text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);
|
||||
box-shadow: 0 2px #0000000b;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<div class="btn-box">
|
||||
<button class="btn">
|
||||
<span> Hello Button</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-box">
|
||||
<button class="btn btn-primary">
|
||||
<span>Button Primary</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-001',
|
||||
x: 400,
|
||||
y: 400,
|
||||
w: 100,
|
||||
h: 100,
|
||||
type: 'group',
|
||||
desc: {
|
||||
bgColor: '#1f1f1f',
|
||||
children: [
|
||||
{
|
||||
uuid: 'group-001-0014',
|
||||
type: 'circle',
|
||||
x: -40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#f44336'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-001-0015',
|
||||
type: 'circle',
|
||||
x: -20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ff9800'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-001-0016',
|
||||
type: 'circle',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ffc106'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-001-0017',
|
||||
type: 'circle',
|
||||
x: 20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#cddc39'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-001-0018',
|
||||
type: 'circle',
|
||||
x: 40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#4caf50'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-003',
|
||||
x: 550,
|
||||
y: 50,
|
||||
w: 173.20508075688775,
|
||||
// w: 100,
|
||||
h: 100,
|
||||
angle: 30,
|
||||
type: 'group',
|
||||
desc: {
|
||||
children: [
|
||||
{
|
||||
uuid: 'group-003-014',
|
||||
type: 'circle',
|
||||
x: -40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#f44336'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-003-0015',
|
||||
type: 'circle',
|
||||
x: -20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ff9800'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-003-0016',
|
||||
type: 'circle',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ffc106'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-003-0017',
|
||||
type: 'circle',
|
||||
x: 20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#cddc39'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-003-0018',
|
||||
type: 'circle',
|
||||
x: 40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#4caf50'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'xxxx-0017',
|
||||
type: 'image',
|
||||
x: 100,
|
||||
y: 300,
|
||||
w: 100,
|
||||
h: 100,
|
||||
angle: 30,
|
||||
desc: {
|
||||
src: './images/lena.png?v=0017'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-004',
|
||||
x: 550,
|
||||
y: 250,
|
||||
w: 375,
|
||||
h: 400,
|
||||
type: 'group',
|
||||
desc: {
|
||||
bgColor: '#FFFFFF',
|
||||
children: [
|
||||
{
|
||||
uuid: 'groud-004-001',
|
||||
type: 'image',
|
||||
x: 200,
|
||||
y: 200,
|
||||
w: 100,
|
||||
h: 100,
|
||||
angle: 30,
|
||||
desc: {
|
||||
src: './images/lena.png'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'groud-004-002',
|
||||
type: 'group',
|
||||
x: 50,
|
||||
y: 50,
|
||||
w: 200,
|
||||
h: 200,
|
||||
angle: 30,
|
||||
desc: {
|
||||
bgColor: '#f0f0f0',
|
||||
children: [
|
||||
{
|
||||
uuid: 'group-004-002-014',
|
||||
type: 'circle',
|
||||
x: -40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#f44336'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-004-001-0015',
|
||||
type: 'circle',
|
||||
x: -20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ff9800'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-004-002-0016',
|
||||
type: 'circle',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ffc106'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-004-002-0017',
|
||||
type: 'circle',
|
||||
x: 20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#cddc39'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-004-002-0018',
|
||||
type: 'circle',
|
||||
x: 40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#4caf50'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'groud-004-002-xxxx',
|
||||
type: 'group',
|
||||
x: 50,
|
||||
y: 100,
|
||||
w: 200,
|
||||
h: 100,
|
||||
angle: 30,
|
||||
desc: {
|
||||
bgColor: '#666666',
|
||||
children: [
|
||||
{
|
||||
uuid: 'group-004-002-xxx-014',
|
||||
type: 'circle',
|
||||
x: -40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#f44336'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-004-002-xxx-0015',
|
||||
type: 'circle',
|
||||
x: -20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ff9800'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-004-002-xxx-0016',
|
||||
type: 'circle',
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#ffc106'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-004-002-xxx-0017',
|
||||
type: 'circle',
|
||||
x: 20,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#cddc39'
|
||||
}
|
||||
},
|
||||
{
|
||||
uuid: 'group-004-002-xxx-0018',
|
||||
type: 'circle',
|
||||
x: 40,
|
||||
y: 0,
|
||||
w: 100,
|
||||
h: 100,
|
||||
desc: {
|
||||
bgColor: '#4caf50'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export function getData() {
|
||||
return deepClone(data);
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import React, { useEffect, useRef } from 'react';
|
||||
import { Core, MiddlewareScroller, MiddlewareSelector } from '@idraw/core';
|
||||
import { calcElementsContextSize } from '@idraw/util';
|
||||
import { getData } from './data';
|
||||
|
||||
export const Lab = () => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
if (ref?.current) {
|
||||
const data = getData();
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
const devicePixelRatio = window.devicePixelRatio;
|
||||
const options = {
|
||||
width,
|
||||
height,
|
||||
devicePixelRatio
|
||||
};
|
||||
const core = new Core(ref.current, options);
|
||||
|
||||
core.use(MiddlewareScroller);
|
||||
core.use(MiddlewareSelector);
|
||||
core.setData(data);
|
||||
// core.scrollX(0);
|
||||
// core.scrollY(0);
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
const devicePixelRatio = window.devicePixelRatio;
|
||||
const contextSize = calcElementsContextSize(data.elements, { viewWidth: width, viewHeight: height });
|
||||
core.resize({
|
||||
width,
|
||||
height,
|
||||
devicePixelRatio,
|
||||
...contextSize
|
||||
});
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<div style={{ position: 'fixed', left: 0, right: 0, width: '100%', height: '100%' }}>
|
||||
<div ref={ref}></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import React, { useEffect, useRef } from 'react';
|
||||
import { Core, MiddlewareScroller, MiddlewareSelector } from '@idraw/core';
|
||||
import { calcElementsContextSize } from '@idraw/util';
|
||||
import { getData } from './data';
|
||||
|
||||
export const Lab = () => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
if (ref?.current) {
|
||||
const data = getData();
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
const devicePixelRatio = window.devicePixelRatio;
|
||||
const options = {
|
||||
width,
|
||||
height,
|
||||
devicePixelRatio
|
||||
};
|
||||
const core = new Core(ref.current, options);
|
||||
|
||||
core.use(MiddlewareScroller);
|
||||
core.use(MiddlewareSelector);
|
||||
core.setData(data);
|
||||
// core.scrollX(0);
|
||||
// core.scrollY(0);
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
const devicePixelRatio = window.devicePixelRatio;
|
||||
const contextSize = calcElementsContextSize(data.elements, { viewWidth: width, viewHeight: height });
|
||||
core.resize({
|
||||
width,
|
||||
height,
|
||||
devicePixelRatio,
|
||||
...contextSize
|
||||
});
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<div style={{ position: 'fixed', left: 0, right: 0, width: '100%', height: '100%' }}>
|
||||
<div ref={ref}></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -18,8 +18,8 @@ export interface BoardWatherWheelYEvent {
|
|||
point: Point;
|
||||
}
|
||||
|
||||
export interface BoardWatherDrawFrameEvent {
|
||||
snapshot: BoardViewerFrameSnapshot;
|
||||
export interface BoardWatherDrawFrameEvent<S extends Record<any | symbol, any>> {
|
||||
snapshot: BoardViewerFrameSnapshot<S>;
|
||||
}
|
||||
|
||||
export type BoardWatherScaleEvent = ViewScaleInfo;
|
||||
|
|
@ -30,7 +30,7 @@ export type BoardWatherScrollYEvent = ViewScaleInfo;
|
|||
|
||||
export type BoardWatherResizeEvent = ViewSizeInfo;
|
||||
|
||||
export interface BoardWatcherEventMap {
|
||||
export interface BoardWatcherEventMap<S extends Record<any | symbol, any>> {
|
||||
hover: BoardWatcherPointEvent;
|
||||
pointStart: BoardWatcherPointEvent;
|
||||
pointMove: BoardWatcherPointEvent;
|
||||
|
|
@ -43,52 +43,52 @@ export interface BoardWatcherEventMap {
|
|||
scrollX: BoardWatherScrollXEvent;
|
||||
scrollY: BoardWatherScrollYEvent;
|
||||
resize: BoardWatherResizeEvent;
|
||||
beforeDrawFrame: BoardWatherDrawFrameEvent;
|
||||
afterDrawFrame: BoardWatherDrawFrameEvent;
|
||||
beforeDrawFrame: BoardWatherDrawFrameEvent<S>;
|
||||
afterDrawFrame: BoardWatherDrawFrameEvent<S>;
|
||||
}
|
||||
|
||||
export type BoardMode = 'SELECT' | 'SCROLL' | 'RULE' | 'CONNECT' | 'PENCIL' | 'PEN' | string;
|
||||
|
||||
export interface BoardMiddlewareObject {
|
||||
export interface BoardMiddlewareObject<S extends Record<any | symbol, any>> {
|
||||
mode: BoardMode;
|
||||
isDefault?: boolean;
|
||||
created?: () => void;
|
||||
// action
|
||||
hover?: (e: BoardWatcherEventMap['hover']) => void | boolean;
|
||||
pointStart?: (e: BoardWatcherEventMap['pointStart']) => void | boolean;
|
||||
pointMove?: (e: BoardWatcherEventMap['pointMove']) => void | boolean;
|
||||
pointEnd?: (e: BoardWatcherEventMap['pointEnd']) => void | boolean;
|
||||
pointLeave?: (e: BoardWatcherEventMap['pointLeave']) => void | boolean;
|
||||
doubleClick?: (e: BoardWatcherEventMap['doubleClick']) => void | boolean;
|
||||
wheelX?: (e: BoardWatcherEventMap['wheelX']) => void | boolean;
|
||||
wheelY?: (e: BoardWatcherEventMap['wheelY']) => void | boolean;
|
||||
hover?: (e: BoardWatcherEventMap<S>['hover']) => void | boolean;
|
||||
pointStart?: (e: BoardWatcherEventMap<S>['pointStart']) => void | boolean;
|
||||
pointMove?: (e: BoardWatcherEventMap<S>['pointMove']) => void | boolean;
|
||||
pointEnd?: (e: BoardWatcherEventMap<S>['pointEnd']) => void | boolean;
|
||||
pointLeave?: (e: BoardWatcherEventMap<S>['pointLeave']) => void | boolean;
|
||||
doubleClick?: (e: BoardWatcherEventMap<S>['doubleClick']) => void | boolean;
|
||||
wheelX?: (e: BoardWatcherEventMap<S>['wheelX']) => void | boolean;
|
||||
wheelY?: (e: BoardWatcherEventMap<S>['wheelY']) => void | boolean;
|
||||
|
||||
scale?: (e: BoardWatcherEventMap['scale']) => void | boolean;
|
||||
scrollX?: (e: BoardWatcherEventMap['scrollX']) => void | boolean;
|
||||
scrollY?: (e: BoardWatcherEventMap['scrollY']) => void | boolean;
|
||||
resize?: (e: BoardWatcherEventMap['resize']) => void | boolean;
|
||||
scale?: (e: BoardWatcherEventMap<S>['scale']) => void | boolean;
|
||||
scrollX?: (e: BoardWatcherEventMap<S>['scrollX']) => void | boolean;
|
||||
scrollY?: (e: BoardWatcherEventMap<S>['scrollY']) => void | boolean;
|
||||
resize?: (e: BoardWatcherEventMap<S>['resize']) => void | boolean;
|
||||
|
||||
// draw
|
||||
beforeDrawFrame?(e: BoardWatcherEventMap['beforeDrawFrame']): void | boolean;
|
||||
afterDrawFrame?(e: BoardWatcherEventMap['afterDrawFrame']): void | boolean;
|
||||
beforeDrawFrame?(e: BoardWatcherEventMap<S>['beforeDrawFrame']): void | boolean;
|
||||
afterDrawFrame?(e: BoardWatcherEventMap<S>['afterDrawFrame']): void | boolean;
|
||||
}
|
||||
|
||||
export interface BoardMiddlewareOptions {
|
||||
export interface BoardMiddlewareOptions<S extends Record<any | symbol, any>> {
|
||||
viewContent: ViewContent;
|
||||
sharer: StoreSharer;
|
||||
sharer: StoreSharer<S>;
|
||||
viewer: BoardViewer;
|
||||
calculator: ViewCalculator;
|
||||
}
|
||||
|
||||
export type BoardMiddleware = (opts: BoardMiddlewareOptions) => BoardMiddlewareObject;
|
||||
export type BoardMiddleware<S extends Record<any | symbol, any> = any> = (opts: BoardMiddlewareOptions<S>) => BoardMiddlewareObject<S>;
|
||||
|
||||
export interface BoardOptions {
|
||||
viewContent: ViewContent;
|
||||
}
|
||||
|
||||
export interface BoardViewerFrameSnapshot {
|
||||
export interface BoardViewerFrameSnapshot<S extends Record<any | symbol, any>> {
|
||||
activeStore: ActiveStore;
|
||||
sharedStore: Record<string, any>;
|
||||
sharedStore: S;
|
||||
}
|
||||
|
||||
export interface BoardViewerEventMap {
|
||||
|
|
@ -97,12 +97,12 @@ export interface BoardViewerEventMap {
|
|||
}
|
||||
|
||||
export interface BoardViewerOptions {
|
||||
sharer: StoreSharer;
|
||||
sharer: StoreSharer<Record<any | symbol, any>>;
|
||||
renderer: BoardRenderer;
|
||||
calculator: ViewCalculator;
|
||||
viewContent: ViewContent;
|
||||
beforeDrawFrame: (e: { snapshot: BoardViewerFrameSnapshot }) => void;
|
||||
afterDrawFrame: (e: { snapshot: BoardViewerFrameSnapshot }) => void;
|
||||
beforeDrawFrame: (e: { snapshot: BoardViewerFrameSnapshot<Record<any | symbol, any>> }) => void;
|
||||
afterDrawFrame: (e: { snapshot: BoardViewerFrameSnapshot<Record<any | symbol, any>> }) => void;
|
||||
}
|
||||
|
||||
export interface BoardViewer extends UtilEventEmitter<BoardViewerEventMap> {
|
||||
|
|
@ -121,9 +121,10 @@ export interface BoardRenderer extends UtilEventEmitter<RendererEventMap> {
|
|||
|
||||
export interface BoardWatcherOptions {
|
||||
viewContent: ViewContent;
|
||||
sharer: StoreSharer;
|
||||
sharer: StoreSharer<Record<any | symbol, any>>;
|
||||
}
|
||||
|
||||
export interface BoardWatcherStore {
|
||||
hasPointDown: boolean;
|
||||
prevClickPoint: Point | null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ export interface CoreOptions {
|
|||
width: number;
|
||||
height: number;
|
||||
devicePixelRatio?: number;
|
||||
padding?: number;
|
||||
// contextWidth?: number;
|
||||
// contextHeight?: number;
|
||||
// onlyRender?: boolean;
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ export type ActiveStore = ViewSizeInfo &
|
|||
selectedUUIDs: string[];
|
||||
};
|
||||
|
||||
export interface StoreSharer {
|
||||
export interface StoreSharer<S extends Record<any, any>> {
|
||||
getActiveStorage<T extends keyof ActiveStore>(key: T): ActiveStore[T];
|
||||
setActiveStorage<T extends keyof ActiveStore>(key: T, storage: ActiveStore[T]): void;
|
||||
getActiveStoreSnapshot(): ActiveStore;
|
||||
getSharedStorage(key: string): any;
|
||||
setSharedStorage(key: string, storage: any): void;
|
||||
getSharedStorage<K extends keyof S>(key: K): S[K];
|
||||
setSharedStorage<K extends keyof S>(key: K, storage: S[K]): void;
|
||||
getSharedStoreSnapshot(): Record<string, any>;
|
||||
|
||||
getActiveScaleInfo(): ViewScaleInfo;
|
||||
setActiveScaleInfo(scaleInfo: ViewScaleInfo): void;
|
||||
setActiveViewSizeInfo(size: ViewSizeInfo): void;
|
||||
|
|
|
|||
|
|
@ -10,11 +10,15 @@ export function deepClone(target: any): any {
|
|||
});
|
||||
return arr;
|
||||
} else if (type === 'Object') {
|
||||
const obj: { [key: string]: any } = {};
|
||||
const obj: { [key: string | symbol]: any } = {};
|
||||
const keys = Object.keys(t);
|
||||
keys.forEach((key) => {
|
||||
obj[key] = _clone(t[key]);
|
||||
});
|
||||
const symbolKeys = Object.getOwnPropertySymbols(t);
|
||||
symbolKeys.forEach((key) => {
|
||||
obj[key] = _clone(t[key]);
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,10 @@ export function validateElements(elements: Array<Element<ElementType>>): boolean
|
|||
|
||||
type AreaSize = ElementSize;
|
||||
|
||||
export function calcElementsContextSize(elements: Array<Element<ElementType>>, opts?: { viewWidth: number; viewHeight: number }): ViewContextSize {
|
||||
export function calcElementsContextSize(
|
||||
elements: Array<Element<ElementType>>,
|
||||
opts?: { viewWidth: number; viewHeight: number; extend?: boolean }
|
||||
): ViewContextSize {
|
||||
const area: AreaSize = { x: 0, y: 0, w: 0, h: 0 };
|
||||
let prevElemSize: ElementSize | null = null;
|
||||
elements.forEach((elem: Element<ElementType>) => {
|
||||
|
|
@ -167,6 +170,11 @@ export function calcElementsContextSize(elements: Array<Element<ElementType>>, o
|
|||
prevElemSize = elemSize;
|
||||
});
|
||||
|
||||
if (opts?.extend) {
|
||||
area.x = Math.min(area.x, 0);
|
||||
area.y = Math.min(area.y, 0);
|
||||
}
|
||||
|
||||
const ctxSize: ViewContextSize = {
|
||||
contextX: area.x,
|
||||
contextY: area.y,
|
||||
|
|
@ -198,11 +206,11 @@ export function calcElementsViewInfo(
|
|||
changeContextTop: number;
|
||||
changeContextBottom: number;
|
||||
} {
|
||||
const contextSize = calcElementsContextSize(elements, { viewWidth: prevViewSize.width, viewHeight: prevViewSize.height });
|
||||
const contextSize = calcElementsContextSize(elements, { viewWidth: prevViewSize.width, viewHeight: prevViewSize.height, extend: options?.extend });
|
||||
|
||||
if (options?.extend === true) {
|
||||
contextSize.contextX = Math.min(contextSize.contextX, prevViewSize.contextX);
|
||||
contextSize.contextY = Math.min(contextSize.contextY, prevViewSize.contextY);
|
||||
contextSize.contextX = Math.min(0, contextSize.contextX, prevViewSize.contextX);
|
||||
contextSize.contextY = Math.min(0, contextSize.contextY, prevViewSize.contextY);
|
||||
contextSize.contextWidth = Math.max(contextSize.contextWidth, prevViewSize.contextWidth);
|
||||
contextSize.contextHeight = Math.max(contextSize.contextHeight, prevViewSize.contextHeight);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { deepClone } from './data';
|
||||
|
||||
export class Store<T extends Record<string, any>> {
|
||||
export class Store<T extends Record<string | symbol, any>> {
|
||||
private _temp: T;
|
||||
private _backUpDefaultStorage: T;
|
||||
|
||||
|
|
|
|||