Merge pull request #337 from idrawjs/dev-v0.4

refactor: refactor events
This commit is contained in:
Deepsea 2024-07-20 12:37:41 +08:00 committed by GitHub
commit f8bb1ef804
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 350 additions and 209 deletions

View file

@ -129,6 +129,7 @@ export class Board<T extends BoardExtendEventMap = BoardExtendEventMap> {
this.#watcher.on('scrollY', this.#handleScrollY.bind(this));
this.#watcher.on('resize', this.#handleResize.bind(this));
this.#watcher.on('doubleClick', this.#handleDoubleClick.bind(this));
this.#watcher.on('contextMenu', this.#handleContextMenu.bind(this));
this.#renderer.on('load', () => {
this.#eventHub.trigger('loadResource');
@ -185,6 +186,16 @@ export class Board<T extends BoardExtendEventMap = BoardExtendEventMap> {
}
}
#handleContextMenu(e: BoardWatcherEventMap['contextMenu']) {
for (let i = 0; i < this.#activeMiddlewareObjs.length; i++) {
const obj = this.#activeMiddlewareObjs[i];
const result = obj?.contextMenu?.(e);
if (result === false) {
return;
}
}
}
#handleWheel(e: BoardWatcherEventMap['wheel']) {
for (let i = 0; i < this.#activeMiddlewareObjs.length; i++) {
const obj = this.#activeMiddlewareObjs[i];

View file

@ -75,6 +75,9 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
};
#onContextMenu = (e: MouseEvent) => {
if (e.button !== 2) {
return;
}
if (!this.#isInTarget(e)) {
return;
}
@ -83,7 +86,7 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
if (!this.#isVaildPoint(point)) {
return;
}
// TODO
this.trigger('contextMenu', { point });
};
#onClick = (e: MouseEvent) => {
@ -146,6 +149,12 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
};
#onPointStart = (e: MouseEvent) => {
// mouse-left-click: button = 0
// mouse-right-click: button = 2
// mouse-scroll button = 1
if (e.button !== 0) {
return;
}
if (!this.#isInTarget(e)) {
return;
}

View file

@ -1 +1,49 @@
export const eventChange = 'change';
export const EVENT_KEY_CHANGE = 'change';
export const EVENT_KEY_CURSOR = 'cursor';
export const EVENT_KEY_RULER = 'ruler';
export const EVENT_KEY_SCALE = 'scale';
export const EVENT_KEY_SELECT = 'select';
export const EVENT_KEY_CLEAR_SELECT = 'clearSelect';
export const EVENT_KEY_TEXT_EDIT = 'textEdit';
export const EVENT_KEY_TEXT_CHANGE = 'textChange';
export const EVENT_KEY_CONTEXT_MENU = 'contextMenu';
export const EVENT_KEY_SELECT_IN_GROUP = 'selectInGroup';
export const EVENT_KEY_SNAP_TO_GRID = 'snapToGrid';
export type CoreEventKeys = {
CURSOR: typeof EVENT_KEY_CURSOR;
CHANGE: typeof EVENT_KEY_CHANGE;
RULER: typeof EVENT_KEY_RULER;
SCALE: typeof EVENT_KEY_SCALE;
SELECT: typeof EVENT_KEY_SELECT;
CLEAR_SELECT: typeof EVENT_KEY_CLEAR_SELECT;
TEXT_EDIT: typeof EVENT_KEY_TEXT_EDIT;
TEXT_CHANGE: typeof EVENT_KEY_TEXT_CHANGE;
CONTEXT_MENU: typeof EVENT_KEY_CONTEXT_MENU;
SELECT_IN_GROUP: typeof EVENT_KEY_SELECT_IN_GROUP;
SNAP_TO_GRID: typeof EVENT_KEY_SELECT_IN_GROUP;
};
const innerEventKeys: CoreEventKeys = {
CURSOR: EVENT_KEY_CURSOR,
CHANGE: EVENT_KEY_CHANGE,
RULER: EVENT_KEY_RULER,
SCALE: EVENT_KEY_SCALE,
SELECT: EVENT_KEY_SELECT,
CLEAR_SELECT: EVENT_KEY_CLEAR_SELECT,
TEXT_EDIT: EVENT_KEY_TEXT_EDIT,
TEXT_CHANGE: EVENT_KEY_TEXT_CHANGE,
CONTEXT_MENU: EVENT_KEY_CONTEXT_MENU,
SELECT_IN_GROUP: EVENT_KEY_SELECT_IN_GROUP,
SNAP_TO_GRID: EVENT_KEY_SELECT_IN_GROUP
};
const coreEventKeys = {} as CoreEventKeys;
Object.keys(innerEventKeys).forEach((keyName: string) => {
Object.defineProperty(coreEventKeys, keyName, {
value: innerEventKeys[keyName as keyof CoreEventKeys],
writable: false
});
});
export { coreEventKeys };

View file

@ -2,23 +2,19 @@ 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 { coreEventKeys } from './config';
export type { CoreEventKeys } from './config';
// export { MiddlewareSelector } from './middleware/selector';
export {
MiddlewareSelector,
middlewareEventSelect,
middlewareEventSelectClear,
middlewareEventSelectInGroup,
middlewareEventSnapToGrid
} from './middleware/selector';
export { MiddlewareSelector } from './middleware/selector';
export { MiddlewareScroller } from './middleware/scroller';
export { MiddlewareScaler, middlewareEventScale } from './middleware/scaler';
export { MiddlewareRuler, middlewareEventRuler } from './middleware/ruler';
export { MiddlewareTextEditor, middlewareEventTextEdit, middlewareEventTextChange } from './middleware/text-editor';
export { MiddlewareScaler } from './middleware/scaler';
export { MiddlewareRuler } from './middleware/ruler';
export { MiddlewareTextEditor } from './middleware/text-editor';
export { MiddlewareDragger } from './middleware/dragger';
export { MiddlewareInfo } from './middleware/info';
export { MiddlewareLayoutSelector } from './middleware/layout-selector';
export { MiddlewarePointer } from './middleware/pointer';
export class Core<E extends CoreEventMap = CoreEventMap> {
#board: Board<E>;

View file

@ -1,6 +1,7 @@
import type { UtilEventEmitter, CoreEventMap } from '@idraw/types';
import { limitAngle, loadImage, parseAngleToRadian } from '@idraw/util';
import { CURSOR, CURSOR_RESIZE, CURSOR_DRAG_DEFAULT, CURSOR_DRAG_ACTIVE, CURSOR_RESIZE_ROTATE } from './cursor-image';
import { coreEventKeys } from '../config';
export class Cursor {
#eventHub: UtilEventEmitter<CoreEventMap>;
@ -29,7 +30,7 @@ export class Cursor {
#init() {
const eventHub = this.#eventHub;
this.#resetCursor('default');
eventHub.on('cursor', (e) => {
eventHub.on(coreEventKeys.CURSOR, (e) => {
if (e.type === 'over-element' || !e.type) {
this.#resetCursor('auto');
} else if (e.type === 'resize-rotate') {
@ -78,7 +79,7 @@ export class Cursor {
}
}
#setCursorResize(e: CoreEventMap['cursor']) {
#setCursorResize(e: CoreEventMap[typeof coreEventKeys.CURSOR]) {
let totalAngle = 0;
if (e.type === 'resize-top') {
totalAngle += 0;

View file

@ -1,4 +1,5 @@
import type { BoardMiddleware, CoreEventMap, Point } from '@idraw/types';
import { coreEventKeys } from '../../config';
const key = 'DRAG';
const keyPrevPoint = Symbol(`${key}_prevPoint`);
@ -17,7 +18,7 @@ export const MiddlewareDragger: BoardMiddleware<DraggerSharedStorage, CoreEventM
if (isDragging === true) {
return;
}
eventHub.trigger('cursor', {
eventHub.trigger(coreEventKeys.CURSOR, {
type: 'drag-default'
});
},
@ -26,7 +27,7 @@ export const MiddlewareDragger: BoardMiddleware<DraggerSharedStorage, CoreEventM
const { point } = e;
sharer.setSharedStorage(keyPrevPoint, point);
isDragging = true;
eventHub.trigger('cursor', {
eventHub.trigger(coreEventKeys.CURSOR, {
type: 'drag-active'
});
},
@ -46,7 +47,7 @@ export const MiddlewareDragger: BoardMiddleware<DraggerSharedStorage, CoreEventM
pointEnd() {
isDragging = false;
sharer.setSharedStorage(keyPrevPoint, null);
eventHub.trigger('cursor', {
eventHub.trigger(coreEventKeys.CURSOR, {
type: 'drag-default'
});
}

View file

@ -1,6 +1,6 @@
import type { BoardMiddleware, ViewRectInfo, Element, MiddlewareInfoConfig } from '@idraw/types';
import type { BoardMiddleware, ViewRectInfo, Element, MiddlewareInfoConfig, CoreEventMap } from '@idraw/types';
import { formatNumber, getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot, createUUID, limitAngle, rotatePoint, parseAngleToRadian } from '@idraw/util';
import { keySelectedElementList, keyHoverElement, keyActionType, keyGroupQueue } from '../selector';
import { keySelectedElementList, keyActionType, keyGroupQueue } from '../selector';
import { drawSizeInfoText, drawPositionInfoText, drawAngleInfoText } from './draw-info';
import type { DeepInfoSharedStorage } from './types';
import { defaltStyle } from './config';
@ -8,7 +8,7 @@ import { defaltStyle } from './config';
const infoFontSize = 10;
const infoLineHeight = 16;
export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage, any, MiddlewareInfoConfig> = (opts, config) => {
export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage, CoreEventMap, MiddlewareInfoConfig> = (opts, config) => {
const { boardContent, calculator } = opts;
const { overlayContext } = boardContent;
const innerConfig = {
@ -28,11 +28,10 @@ export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage, any, Middlew
const { sharedStore } = snapshot;
const selectedElementList = sharedStore[keySelectedElementList];
const hoverElement = sharedStore[keyHoverElement];
const actionType = sharedStore[keyActionType];
const groupQueue = sharedStore[keyGroupQueue] || [];
if (selectedElementList.length === 1 && !hoverElement?.operations?.locked) {
if (selectedElementList.length === 1) {
const elem = selectedElementList[0];
if (elem && ['select', 'drag', 'resize'].includes(actionType as string)) {
const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot);

View file

@ -1,14 +1,14 @@
import type { BoardMiddleware, ElementSize, Point, MiddlewareLayoutSelectorConfig } from '@idraw/types';
import type { BoardMiddleware, ElementSize, Point, MiddlewareLayoutSelectorConfig, CoreEventMap } from '@idraw/types';
import { calcLayoutSizeController, isViewPointInVertexes, getViewScaleInfoFromSnapshot, isViewPointInElementSize, calcViewElementSize } from '@idraw/util';
import type { LayoutSelectorSharedStorage, ControlType } from './types';
import { keyLayoutActionType, keyLayoutController, keyLayoutControlType, keyLayoutIsHover, keyLayoutIsSelected, controllerSize, defaultStyle } from './config';
import { keyActionType as keyElementActionType, keyHoverElement, middlewareEventSelectClear } from '../selector';
import { keyActionType as keyElementActionType, keyHoverElement } from '../selector';
import { drawLayoutController, drawLayoutHover } from './util';
import { eventChange } from '../../config';
import { coreEventKeys } from '../../config';
export { keyLayoutIsSelected };
export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStorage, any, MiddlewareLayoutSelectorConfig> = (opts, config) => {
export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStorage, CoreEventMap, MiddlewareLayoutSelectorConfig> = (opts, config) => {
const { sharer, boardContent, calculator, viewer, eventHub } = opts;
const { overlayContext } = boardContent;
const innerConfig = {
@ -110,7 +110,7 @@ export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStora
}
if (layoutControlType) {
sharer.setSharedStorage(keyLayoutControlType, layoutControlType);
eventHub.trigger(middlewareEventSelectClear, {});
eventHub.trigger(coreEventKeys.CLEAR_SELECT);
return layoutControlType;
}
}
@ -122,7 +122,7 @@ export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStora
if (isBusy === true) {
return;
}
eventHub.trigger('cursor', {
eventHub.trigger(coreEventKeys.CURSOR, {
type: controlType ? `resize-${controlType}` : controlType,
groupQueue: [],
element: getLayoutSize()
@ -309,7 +309,7 @@ export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStora
const layoutControlType = sharer.getSharedStorage(keyLayoutControlType);
const data = sharer.getActiveStorage('data');
if (data && layoutActionType === 'resize' && layoutControlType) {
eventHub.trigger(eventChange, {
eventHub.trigger(coreEventKeys.CHANGE, {
type: 'changeLayout',
data
});

View file

@ -0,0 +1,60 @@
import type { BoardMiddleware, CoreEventMap } from '@idraw/types';
import type { DeepPointerSharedStorage } from './types';
import { keySelectedElementList } from '../selector';
import { coreEventKeys } from '../../config';
export const MiddlewarePointer: BoardMiddleware<DeepPointerSharedStorage, CoreEventMap> = (opts) => {
const { boardContent, eventHub, sharer } = opts;
const canvas = boardContent.boardContext.canvas;
const container = opts.container || document.body;
const id = `idraw-middleware-pointer-${Math.random().toString(26).substring(2)}`;
const getCanvasRect = () => {
const clientRect = canvas.getBoundingClientRect() as DOMRect;
const { left, top, width, height } = clientRect;
return { left, top, width, height };
};
const contextMenuPointer = document.createElement('div');
contextMenuPointer.setAttribute('id', id);
contextMenuPointer.style.position = 'fixed';
contextMenuPointer.style.top = '0';
contextMenuPointer.style.bottom = 'unset';
contextMenuPointer.style.left = '0';
contextMenuPointer.style.right = 'unset';
// // TODO
// contextMenuPointer.style.width = '10px';
// contextMenuPointer.style.height = '10px';
// contextMenuPointer.style.background = 'red';
container.appendChild(contextMenuPointer);
return {
name: '@middleware/pointer',
use() {
// TODO
},
disuse() {
// TODO
},
pointStart(e) {
// TODO
},
pointEnd() {
// TODO
},
contextMenu(e) {
const { point } = e;
const { left, top } = getCanvasRect();
contextMenuPointer.style.left = `${left + point.x}px`;
contextMenuPointer.style.top = `${top + point.y}px`;
const selectedElements = sharer.getSharedStorage(keySelectedElementList);
eventHub.trigger(coreEventKeys.CONTEXT_MENU, {
pointerContainer: contextMenuPointer,
selectedElements: selectedElements || []
});
}
};
};

View file

@ -0,0 +1,4 @@
import { keySelectedElementList } from '../selector';
import type { DeepSelectorSharedStorage } from '../selector';
export type DeepPointerSharedStorage = Pick<DeepSelectorSharedStorage, typeof keySelectedElementList>;

View file

@ -3,8 +3,7 @@ import { getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot } from '@idra
import { drawRulerBackground, drawXRuler, drawYRuler, calcXRulerScaleList, calcYRulerScaleList, drawGrid, drawScrollerSelectedArea } from './util';
import type { DeepRulerSharedStorage } from './types';
import { defaultStyle } from './config';
export const middlewareEventRuler = '@middleware/show-ruler';
import { coreEventKeys } from '../../config';
export const MiddlewareRuler: BoardMiddleware<DeepRulerSharedStorage, CoreEventMap, MiddlewareRulerConfig> = (opts, config) => {
const { boardContent, viewer, eventHub, calculator } = opts;
@ -43,10 +42,10 @@ export const MiddlewareRuler: BoardMiddleware<DeepRulerSharedStorage, CoreEventM
return {
name: '@middleware/ruler',
use() {
eventHub.on(middlewareEventRuler, rulerCallback);
eventHub.on(coreEventKeys.RULER, rulerCallback);
},
disuse() {
eventHub.off(middlewareEventRuler, rulerCallback);
eventHub.off(coreEventKeys.RULER, rulerCallback);
},
beforeDrawFrame: ({ snapshot }) => {
if (show === true) {

View file

@ -1,7 +1,6 @@
import type { BoardMiddleware, CoreEventMap } from '@idraw/types';
import { formatNumber } from '@idraw/util';
export const middlewareEventScale = '@middleware/scale';
import { coreEventKeys } from '../../config';
export const MiddlewareScaler: BoardMiddleware<Record<string, any>, CoreEventMap> = (opts) => {
const { viewer, sharer, eventHub } = opts;
@ -27,7 +26,7 @@ export const MiddlewareScaler: BoardMiddleware<Record<string, any>, CoreEventMap
viewer.scroll({ moveX, moveY });
viewer.drawFrame();
const scaleNum = formatNumber(scale);
eventHub.trigger(middlewareEventScale, { scale: scaleNum });
eventHub.trigger(coreEventKeys.SCALE, { scale: scaleNum });
}
};
};

View file

@ -3,6 +3,7 @@ import { drawScroller, isPointInScrollThumb } from './util';
// import type { ScrollbarThumbType } from './util';
import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType, keyHoverXThumbRect, keyHoverYThumbRect, defaultStyle } from './config';
import type { DeepScrollerSharedStorage } from './types';
import { coreEventKeys } from '../../config';
export const MiddlewareScroller: BoardMiddleware<DeepScrollerSharedStorage, any, MiddlewareScrollerConfig> = (opts, config) => {
const { viewer, boardContent, sharer, eventHub } = opts;
@ -98,7 +99,7 @@ export const MiddlewareScroller: BoardMiddleware<DeepScrollerSharedStorage, any,
sharer.setSharedStorage(keyHoverXThumbRect, false);
sharer.setSharedStorage(keyHoverYThumbRect, true);
}
eventHub.trigger('cursor', { type: 'default' });
eventHub.trigger(coreEventKeys.CURSOR, { type: 'default' });
return false;
}

View file

@ -42,11 +42,3 @@ export const defaultStyle: MiddlewareSelectorStyle = {
lockedColor,
referenceColor
};
export const middlewareEventSelect: string = '@middleware/select';
export const middlewareEventSelectClear: string = '@middleware/select-clear';
export const middlewareEventSelectInGroup: string = '@middleware/select-in-group';
export const middlewareEventSnapToGrid: string = '@middleware/snap-to-grid';

View file

@ -17,7 +17,6 @@ import type {
Data,
ViewRectVertexes,
CoreEventMap,
ElementPosition,
ViewScaleInfo,
ViewSizeInfo,
ElementSizeController,
@ -56,10 +55,6 @@ import {
calcMoveInGroup
} from './util';
import {
middlewareEventSelect,
middlewareEventSelectClear,
middlewareEventSelectInGroup,
middlewareEventSnapToGrid,
keyActionType,
keyResizeType,
keyAreaStart,
@ -85,15 +80,12 @@ import {
// keyDebugStartVertical
} from './config';
import { calcReferenceInfo } from './reference';
import { middlewareEventTextEdit } from '../text-editor';
import { eventChange } from '../../config';
import { coreEventKeys } from '../../config';
import { keyLayoutIsSelected } from '../layout-selector';
export { keySelectedElementList, keyHoverElement, keyActionType, keyResizeType, keyGroupQueue };
export type { DeepSelectorSharedStorage, ActionType };
export { middlewareEventSelect, middlewareEventSelectClear, middlewareEventSelectInGroup, middlewareEventSnapToGrid };
export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, CoreEventMap, MiddlewareSelectorConfig> = (opts, config) => {
const innerConfig = {
...defaultStyle,
@ -162,7 +154,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
}
if (opts?.triggerEvent === true) {
eventHub.trigger(middlewareEventSelect, { uuids: list.map((elem) => elem.uuid) });
eventHub.trigger(coreEventKeys.SELECT, { uuids: list.map((elem) => elem.uuid), positions: [] });
}
};
@ -199,7 +191,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
clear();
const selectCallback = ({ uuids, positions }: { uuids: string[]; positions: ElementPosition[] }) => {
const selectCallback = ({ uuids = [], positions }: CoreEventMap[typeof coreEventKeys.SELECT]) => {
let elements: Element[] = [];
const actionType = sharer.getSharedStorage(keyActionType);
const data = sharer.getActiveStorage('data');
@ -244,17 +236,17 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
return {
name: '@middleware/selector',
use() {
eventHub.on(middlewareEventSelect, selectCallback);
eventHub.on(middlewareEventSelectClear, selectClearCallback);
eventHub.on(middlewareEventSelectInGroup, selectInGroupCallback);
eventHub.on(middlewareEventSnapToGrid, setSnapToSnapCallback);
eventHub.on(coreEventKeys.SELECT, selectCallback);
eventHub.on(coreEventKeys.CLEAR_SELECT, selectClearCallback);
eventHub.on(coreEventKeys.SELECT_IN_GROUP, selectInGroupCallback);
eventHub.on(coreEventKeys.SNAP_TO_GRID, setSnapToSnapCallback);
},
disuse() {
eventHub.off(middlewareEventSelect, selectCallback);
eventHub.off(middlewareEventSelectClear, selectClearCallback);
eventHub.off(middlewareEventSelectInGroup, selectInGroupCallback);
eventHub.off(middlewareEventSnapToGrid, setSnapToSnapCallback);
eventHub.off(coreEventKeys.SELECT, selectCallback);
eventHub.off(coreEventKeys.CLEAR_SELECT, selectClearCallback);
eventHub.off(coreEventKeys.SELECT_IN_GROUP, selectInGroupCallback);
eventHub.off(coreEventKeys.SNAP_TO_GRID, setSnapToSnapCallback);
},
hover: (e: PointWatcherEvent) => {
@ -269,7 +261,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
}
const cursor: string | null = target.type;
if (inBusyMode === null) {
eventHub.trigger('cursor', {
eventHub.trigger(coreEventKeys.CURSOR, {
type: cursor,
groupQueue: target.groupQueue,
element: target.elements[0]
@ -388,17 +380,20 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
})
) {
const target = getPointTarget(e.point, pointTargetBaseOptions());
if (target?.elements?.length === 1 && target.elements[0]?.operations?.locked === true) {
return;
} else {
updateHoverElement(null);
}
const isLockedElement = target?.elements?.length === 1 && target.elements[0]?.operations?.locked === true;
// if (target?.elements?.length === 1 && target.elements[0]?.operations?.locked === true) {
// return;
// } else {
updateHoverElement(null);
// }
if (target?.elements?.length === 1) {
moveOriginalStartElementSize = getElementSize(target?.elements[0]);
}
if (target.type === 'over-element' && target?.elements?.length === 1) {
if (isLockedElement === true) {
clear();
} else if (target.type === 'over-element' && target?.elements?.length === 1) {
updateSelectedElementList([target.elements[0]], { triggerEvent: true });
sharer.setSharedStorage(keyActionType, 'drag');
} else if (target.type?.startsWith('resize-')) {
@ -426,30 +421,29 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
areaSize: listAreaSize,
groupQueue: []
});
const isLockedElement = target?.elements?.length === 1 && target.elements[0]?.operations?.locked === true;
if (!isLockedElement) {
updateHoverElement(null);
}
// if (!isLockedElement) {
updateHoverElement(null);
// }
if (target?.elements?.length === 1) {
moveOriginalStartElementSize = getElementSize(target?.elements[0]);
}
if (!isLockedElement) {
if (target.type === 'list-area') {
sharer.setSharedStorage(keyActionType, 'drag-list');
} else if (target.type === 'over-element' && target?.elements?.length === 1) {
updateSelectedElementList([target.elements[0]], { triggerEvent: true });
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);
updateSelectedElementList([], { triggerEvent: true });
}
if (isLockedElement === true) {
clear();
sharer.setSharedStorage(keyActionType, 'area');
sharer.setSharedStorage(keyAreaStart, e.point);
updateSelectedElementList([], { triggerEvent: true });
} else if (target.type === 'list-area') {
sharer.setSharedStorage(keyActionType, 'drag-list');
} else if (target.type === 'over-element' && target?.elements?.length === 1) {
updateSelectedElementList([target.elements[0]], { triggerEvent: true });
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');
@ -702,11 +696,11 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
}
if (data && (['drag', 'drag-list', 'drag-list-end', 'resize'] as ActionType[]).includes(actionType)) {
let type = 'dragElement';
let type: any = 'dragElement';
if (type === 'resize') {
type = 'resizeElement';
}
eventHub.trigger(eventChange, { data, type, selectedElements, hoverElement });
eventHub.trigger(coreEventKeys.CHANGE, { data, type, selectedElements, hoverElement });
}
viewer.drawFrame();
};
@ -743,8 +737,8 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
return;
}
} else if (target.elements.length === 1 && target.elements[0]?.type === 'text' && !target.elements[0]?.operations?.invisible) {
eventHub.trigger(middlewareEventTextEdit, {
element: target.elements[0],
eventHub.trigger(coreEventKeys.TEXT_EDIT, {
element: target.elements[0] as Element<'text'>,
groupQueue: sharer.getSharedStorage(keyGroupQueue) || [],
position: getElementPositionFromList(target.elements[0]?.uuid, sharer.getActiveStorage('data')?.elements || []),
viewScaleInfo: sharer.getActiveViewScaleInfo()
@ -753,6 +747,53 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
sharer.setSharedStorage(keyActionType, null);
},
contextMenu: (e: PointWatcherEvent) => {
const groupQueue = sharer.getSharedStorage(keyGroupQueue);
if (groupQueue?.length > 0) {
if (
isPointInViewActiveGroup(e.point, {
ctx: overlayContext,
viewScaleInfo: sharer.getActiveViewScaleInfo(),
viewSizeInfo: sharer.getActiveViewSizeInfo(),
groupQueue
})
) {
const target = getPointTarget(e.point, pointTargetBaseOptions());
if (target?.elements?.length === 1 && target.elements[0]?.operations?.locked !== true) {
clear();
updateSelectedElementList([target.elements[0]], { triggerEvent: true });
viewer.drawFrame();
} else if (!target?.elements?.length) {
clear();
}
}
return;
}
// not in group
const listAreaSize = calcSelectedElementsArea(getActiveElements(), {
viewScaleInfo: sharer.getActiveViewScaleInfo(),
viewSizeInfo: sharer.getActiveViewSizeInfo(),
calculator
});
const target = getPointTarget(e.point, {
...pointTargetBaseOptions(),
areaSize: listAreaSize,
groupQueue: []
});
if (target?.elements?.length === 1 && target.elements[0]?.operations?.locked !== true) {
clear();
updateSelectedElementList([target.elements[0]], { triggerEvent: true });
viewer.drawFrame();
return;
} else if (!target?.elements?.length) {
clear();
}
},
beforeDrawFrame({ snapshot }) {
const { activeStore, sharedStore } = snapshot;
const { scale, offsetLeft, offsetTop, offsetRight, offsetBottom, width, height, contextHeight, contextWidth, devicePixelRatio } = activeStore;
@ -784,13 +825,13 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
})
: null;
const isLocked: boolean = !!hoverElement?.operations?.locked;
const isHoverLocked: boolean = !!hoverElement?.operations?.locked;
if (groupQueue?.length > 0) {
// in group
drawGroupQueueVertexesWrappers(overlayContext, groupQueueVertexesList, drawBaseOpts);
if (hoverElement && actionType !== 'drag') {
if (isLocked) {
if (isHoverLocked) {
drawLockedVertexesWrapper(overlayContext, hoverElementVertexes, {
...drawBaseOpts,
controller: calcElementSizeController(hoverElement, {
@ -804,7 +845,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
drawHoverVertexesWrapper(overlayContext, hoverElementVertexes, drawBaseOpts);
}
}
if (!isLocked && elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) {
if (elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) {
drawSelectedElementControllersVertexes(overlayContext, selectedElementController, {
...drawBaseOpts,
element: elem,
@ -837,7 +878,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
} else {
// in root
if (hoverElement && actionType !== 'drag') {
if (isLocked) {
if (isHoverLocked) {
drawLockedVertexesWrapper(overlayContext, hoverElementVertexes, {
...drawBaseOpts,
controller: calcElementSizeController(hoverElement, {
@ -851,7 +892,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
drawHoverVertexesWrapper(overlayContext, hoverElementVertexes, drawBaseOpts);
}
}
if (!isLocked && elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) {
if (elem && (['select', 'drag', 'resize'] as ActionType[]).includes(actionType)) {
drawSelectedElementControllersVertexes(overlayContext, selectedElementController, {
...drawBaseOpts,
element: elem,

View file

@ -1,7 +1,6 @@
import type { BoardMiddleware, CoreEventMap, Element, ElementSize, ViewScaleInfo, ElementPosition } from '@idraw/types';
import { limitAngle, getDefaultElementDetailConfig, enhanceFontFamliy } from '@idraw/util';
export const middlewareEventTextEdit = '@middleware/text-edit';
export const middlewareEventTextChange = '@middleware/text-change';
import { coreEventKeys } from '../../config';
type TextEditEvent = {
element: Element<'text'>;
@ -20,7 +19,7 @@ type TextChangeEvent = {
position: ElementPosition;
};
type ExtendEventMap = Record<typeof middlewareEventTextEdit, TextEditEvent> & Record<typeof middlewareEventTextChange, TextChangeEvent>;
type ExtendEventMap = Record<typeof coreEventKeys.TEXT_EDIT, TextEditEvent> & Record<typeof coreEventKeys.TEXT_CHANGE, TextChangeEvent>;
const defaultElementDetail = getDefaultElementDetailConfig();
@ -35,7 +34,8 @@ export const MiddlewareTextEditor: BoardMiddleware<ExtendEventMap, CoreEventMap
const mask = document.createElement('div');
let activeElem: Element<'text'> | null = null;
let activePosition: ElementPosition = [];
const id = `idraw-middleware-text-editor-${Math.random().toString(26).substring(2)}`;
mask.setAttribute('id', id);
canvasWrapper.appendChild(textarea);
canvasWrapper.style.position = 'absolute';
@ -211,7 +211,7 @@ export const MiddlewareTextEditor: BoardMiddleware<ExtendEventMap, CoreEventMap
if (activeElem && activePosition) {
// activeElem.detail.text = (e.target as any).value || '';
activeElem.detail.text = textarea.innerText || '';
eventHub.trigger(middlewareEventTextChange, {
eventHub.trigger(coreEventKeys.TEXT_CHANGE, {
element: {
uuid: activeElem.uuid,
detail: {
@ -225,7 +225,7 @@ export const MiddlewareTextEditor: BoardMiddleware<ExtendEventMap, CoreEventMap
});
textarea.addEventListener('blur', () => {
if (activeElem && activePosition) {
eventHub.trigger(middlewareEventTextChange, {
eventHub.trigger(coreEventKeys.TEXT_CHANGE, {
element: {
uuid: activeElem.uuid,
detail: {
@ -263,10 +263,10 @@ export const MiddlewareTextEditor: BoardMiddleware<ExtendEventMap, CoreEventMap
return {
name: '@middleware/text-editor',
use() {
eventHub.on(middlewareEventTextEdit, textEditCallback);
eventHub.on(coreEventKeys.TEXT_EDIT, textEditCallback);
},
disuse() {
eventHub.off(middlewareEventTextEdit, textEditCallback);
eventHub.off(coreEventKeys.TEXT_EDIT, textEditCallback);
}
};
};

View file

@ -1,61 +1,9 @@
import type { CoreEventMap, Data } from '@idraw/types';
import { eventChange } from '@idraw/core';
import type { CoreEventMap } from '@idraw/types';
import { coreEventKeys } from '@idraw/core';
import type { CoreEventKeys } from '@idraw/core';
import {
middlewareEventRuler,
middlewareEventScale,
middlewareEventSelect,
middlewareEventSelectClear,
middlewareEventTextEdit,
middlewareEventTextChange
} from '@idraw/core';
export type IDrawEventKeys = CoreEventKeys; // TODO
const idrawEventChange = eventChange;
export type IDrawEvent = CoreEventMap;
export type IDrawEvent = CoreEventMap & {
[idrawEventChange]: {
data: Data;
type:
| 'updateElement'
| 'deleteElement'
| 'moveElement'
| 'addElement'
| 'dragElement'
| 'resizeElement'
| 'setData'
| 'undo'
| 'redo'
| 'changeLayout' // TODO
| 'other';
};
};
export interface IDrawEventKeys {
change: typeof idrawEventChange;
ruler: typeof middlewareEventRuler;
scale: typeof middlewareEventScale;
select: typeof middlewareEventSelect;
clearSelect: typeof middlewareEventSelectClear;
textEdit: typeof middlewareEventTextEdit;
textChange: typeof middlewareEventTextChange;
}
const innerEventKeys: IDrawEventKeys = {
change: idrawEventChange,
ruler: middlewareEventRuler,
scale: middlewareEventScale,
select: middlewareEventSelect,
clearSelect: middlewareEventSelectClear,
textEdit: middlewareEventTextEdit,
textChange: middlewareEventTextChange
};
const eventKeys = {} as IDrawEventKeys;
Object.keys(innerEventKeys).forEach((keyName: string) => {
Object.defineProperty(eventKeys, keyName, {
value: innerEventKeys[keyName as keyof IDrawEventKeys],
writable: false
});
});
export { eventKeys };
export const eventKeys = coreEventKeys; // TODO

View file

@ -1,4 +1,4 @@
import { Core, middlewareEventSelectInGroup, middlewareEventSnapToGrid } from '@idraw/core';
import { Core, coreEventKeys } from '@idraw/core';
import type {
PointSize,
IDrawOptions,
@ -14,7 +14,6 @@ import type {
ElementPosition,
IDrawStorage
} from '@idraw/types';
import type { IDrawEvent } from './event';
import {
createElement,
insertElementToListByPosition,
@ -31,7 +30,7 @@ import {
import { defaultSettings, getDefaultStorage, defaultMode, parseStyles } from './config';
import { exportImageFileBlobURL } from './file';
import type { ExportImageFileBaseOptions, ExportImageFileResult } from './file';
import { eventKeys } from './event';
import type { IDrawEvent } from './event';
import { changeMode, runMiddlewares } from './mode';
export class iDraw {
@ -70,11 +69,11 @@ export class iDraw {
runMiddlewares(this.#core, store);
this.#core.refresh();
} else if (feat === 'selectInGroup') {
this.#core.trigger(middlewareEventSelectInGroup, {
this.#core.trigger(coreEventKeys.SELECT_IN_GROUP, {
enable: !!status
});
} else if (feat === 'snapToGrid') {
this.#core.trigger(middlewareEventSnapToGrid, {
this.#core.trigger(coreEventKeys.SNAP_TO_GRID, {
enable: !!status
});
}
@ -110,7 +109,7 @@ export class iDraw {
setData(data: Data) {
const core = this.#core;
core.setData(data);
core.trigger(eventKeys.change, { data, type: 'setData' });
core.trigger(coreEventKeys.CHANGE, { data, type: 'setData' });
}
getData(opts?: { compact?: boolean }): Data | null {
@ -162,7 +161,7 @@ export class iDraw {
}
trigger<T extends keyof IDrawEvent>(name: T, e?: IDrawEvent[T]) {
this.#core.trigger(name, e);
this.#core.trigger(name, e as IDrawEvent[T]);
}
selectElement(uuid: string) {
@ -170,7 +169,7 @@ export class iDraw {
}
selectElements(uuids: string[]) {
this.trigger(eventKeys.select, { uuids });
this.trigger(coreEventKeys.SELECT, { uuids });
}
selectElementByPosition(position: ElementPosition) {
@ -178,11 +177,11 @@ export class iDraw {
}
selectElementsByPositions(positions: ElementPosition[]) {
this.trigger(eventKeys.select, { positions });
this.trigger(coreEventKeys.SELECT, { positions });
}
cancelElements() {
this.trigger(eventKeys.clearSelect, { uuids: [] });
this.trigger(coreEventKeys.CLEAR_SELECT, { uuids: [] });
}
createElement<T extends ElementType>(
@ -211,7 +210,7 @@ export class iDraw {
updateElementInList(element.uuid, element, data.elements);
core.setData(data);
core.refresh();
core.trigger(eventKeys.change, { data, type: 'updateElement' });
core.trigger(coreEventKeys.CHANGE, { data, type: 'updateElement' });
}
addElement(
@ -230,7 +229,7 @@ export class iDraw {
}
core.setData(data);
core.refresh();
core.trigger(eventKeys.change, { data, type: 'addElement' });
core.trigger(coreEventKeys.CHANGE, { data, type: 'addElement' });
return data;
}
@ -240,7 +239,7 @@ export class iDraw {
deleteElementInList(uuid, data.elements);
core.setData(data);
core.refresh();
core.trigger(eventKeys.change, { data, type: 'deleteElement' });
core.trigger(coreEventKeys.CHANGE, { data, type: 'deleteElement' });
}
moveElement(uuid: string, to: ElementPosition) {
@ -251,7 +250,7 @@ export class iDraw {
data.elements = list;
core.setData(data);
core.refresh();
core.trigger(eventKeys.change, { data, type: 'moveElement' });
core.trigger(coreEventKeys.CHANGE, { data, type: 'moveElement' });
}
async getImageBlobURL(opts?: ExportImageFileBaseOptions): Promise<ExportImageFileResult> {

View file

@ -1,16 +1,5 @@
export type * from '@idraw/types';
export {
Core,
MiddlewareSelector,
middlewareEventSelect,
middlewareEventSelectClear,
MiddlewareScroller,
MiddlewareScaler,
middlewareEventScale,
MiddlewareRuler,
middlewareEventRuler,
MiddlewareTextEditor
} from '@idraw/core';
export { Core, MiddlewareSelector, MiddlewareScroller, MiddlewareScaler, MiddlewareRuler, MiddlewareTextEditor, coreEventKeys } from '@idraw/core';
export { Sharer, Calculator } from '@idraw/board';
export { Renderer } from '@idraw/renderer';
export {

View file

@ -9,15 +9,16 @@ import {
MiddlewareRuler,
MiddlewareTextEditor,
MiddlewareDragger,
MiddlewareInfo
MiddlewareInfo,
MiddlewarePointer
} from '@idraw/core';
import { IDrawEvent } from './event';
import { InnerEvent } from './event';
function isValidMode(mode: string | IDrawMode) {
return ['select', 'drag', 'readOnly'].includes(mode);
}
export function runMiddlewares(core: Core<IDrawEvent>, store: Store<IDrawStorage>) {
export function runMiddlewares(core: Core<InnerEvent>, store: Store<IDrawStorage>) {
const { enableRuler, enableScale, enableScroll, enableSelect, enableTextEdit, enableDrag, enableInfo } = store.getSnapshot();
const styles = store.get('middlewareStyles');
if (enableScroll === true) {
@ -63,9 +64,11 @@ export function runMiddlewares(core: Core<IDrawEvent>, store: Store<IDrawStorage
} else if (enableInfo === false) {
core.disuse(MiddlewareInfo);
}
core.use(MiddlewarePointer);
}
export function changeMode(mode: IDrawMode, core: Core<IDrawEvent>, store: Store<IDrawStorage>) {
export function changeMode(mode: IDrawMode, core: Core<InnerEvent>, store: Store<IDrawStorage>) {
let enableScale: boolean = false;
let enableScroll: boolean = false;
let enableSelect: boolean = false;

View file

@ -49,6 +49,7 @@ export interface BoardWatcherEventMap<S extends Record<any | symbol, any> = any>
pointEnd: BoardWatcherPointEvent;
pointLeave: BoardWatcherPointEvent;
doubleClick: BoardWatcherPointEvent;
contextMenu: BoardWatcherPointEvent;
wheel: BoardWatherWheelEvent;
wheelScale: BoardWatherWheelScaleEvent;
scrollX: BoardWatherScrollXEvent;
@ -70,12 +71,10 @@ export interface BoardMiddlewareObject<S extends Record<any | symbol, any> = any
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;
contextMenu?: (e: BoardWatcherEventMap<S>['contextMenu']) => void | boolean;
wheel?: (e: BoardWatcherEventMap<S>['wheel']) => void | boolean;
wheelScale?: (e: BoardWatcherEventMap<S>['wheelScale']) => 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;

View file

@ -1,4 +1,5 @@
import type { Element, ElementType } from './element';
import type { Element, ElementSize, ElementType, ElementPosition } from './element';
import type { ViewScaleInfo } from './view';
import type { Data } from './data';
import type { ViewContext2D } from './context2d';
import type { BoardBaseEventMap } from './board';
@ -24,18 +25,29 @@ export type CursorType =
| 'default';
export interface CoreEventCursor {
type: CursorType | string | null;
type?: CursorType | string | null;
groupQueue?: Element<'group'>[];
element?: Element<ElementType>;
element?: Element<ElementType> | ElementSize | null;
}
export interface CoreEventSelect {
uuids: string[];
positions?: Array<Array<number>>;
}
// export interface CoreEventSelect {
// uuids: string[];
// positions?: Array<Array<number>>;
// }
export interface CoreEventChange {
type: string;
data: Data;
type:
| 'updateElement'
| 'deleteElement'
| 'moveElement'
| 'addElement'
| 'dragElement'
| 'resizeElement'
| 'setData'
| 'undo'
| 'redo'
| 'changeLayout' // TODO
| 'other';
selectedElements?: Element[] | null;
hoverElement?: Element | null;
}
@ -43,8 +55,38 @@ export interface CoreEventScale {
scale: number;
}
type CoreEventTextEdit = {
element: Element<'text'>;
position: ElementPosition;
groupQueue: Element<'group'>[];
viewScaleInfo: ViewScaleInfo;
};
type CoreEventTextChange = {
element: {
uuid: string;
detail: {
text: string;
};
};
position: ElementPosition;
};
type CoreEventContextMenu = {
pointerContainer: HTMLDivElement;
selectedElements: Element[];
};
export type CoreEventMap = BoardBaseEventMap & {
cursor: CoreEventCursor;
change: CoreEventChange;
[key: string]: any;
ruler: { show: boolean; showGrid: boolean };
scale: { scale: number };
select: { uuids?: string[]; positions?: ElementPosition[] };
clearSelect: { uuids?: string[] } | void;
textEdit: CoreEventTextEdit;
textChange: CoreEventTextChange;
contextMenu: CoreEventContextMenu;
selectInGroup: { enable: boolean };
snapToGrid: { enable: boolean };
};

View file

@ -1,7 +1,7 @@
export interface UtilEventEmitter<T extends Record<string, any>> {
on<K extends keyof T>(eventKey: K, callback: (e: T[K]) => void): void;
off<K extends keyof T>(eventKey: K, callback: (e: T[K]) => void): void;
trigger<K extends keyof T>(eventKey: K, e: T[K]): void;
trigger<K extends keyof T>(eventKey: K, e?: T[K]): void;
has<K extends keyof T>(name: K | string): boolean;
}