mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-01 09:30:01 +08:00
refactor(editor): move shape toolbar config and components to its package (#11082)
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
import type { ShapeToolOption } from '@blocksuite/affine-gfx-shape';
|
||||
import { render, type TemplateResult } from 'lit';
|
||||
|
||||
type TransformState = {
|
||||
/** horizental offset base on center */
|
||||
x?: number | string;
|
||||
/** vertical offset base on center */
|
||||
y?: number | string;
|
||||
/** scale */
|
||||
s?: number;
|
||||
/** z-index */
|
||||
z?: number;
|
||||
};
|
||||
|
||||
export type DraggableShape = {
|
||||
name: ShapeToolOption['shapeName'];
|
||||
svg: TemplateResult;
|
||||
style: {
|
||||
default?: TransformState;
|
||||
hover?: TransformState;
|
||||
/**
|
||||
* The next shape when previous shape is dragged outside toolbar
|
||||
*/
|
||||
next?: TransformState;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to build the CSS variables object for the shape
|
||||
* @returns
|
||||
*/
|
||||
export const buildVariablesObject = (style: DraggableShape['style']) => {
|
||||
const states: Array<keyof DraggableShape['style']> = [
|
||||
'default',
|
||||
'hover',
|
||||
'next',
|
||||
];
|
||||
const variables: Array<keyof TransformState> = ['x', 'y', 's', 'z'];
|
||||
|
||||
const resolveValue = (
|
||||
variable: keyof TransformState,
|
||||
value: string | number
|
||||
) => {
|
||||
if (['x', 'y'].includes(variable)) {
|
||||
return typeof value === 'number' ? `${value}px` : value;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
return states.reduce((acc, state) => {
|
||||
return {
|
||||
...acc,
|
||||
...variables.reduce((acc, variable) => {
|
||||
const defaultValue = style.default?.[variable];
|
||||
const value = style[state]?.[variable] ?? defaultValue;
|
||||
if (value === undefined) return acc;
|
||||
return {
|
||||
...acc,
|
||||
[`--${state}-${variable}`]: resolveValue(variable, value),
|
||||
};
|
||||
}, {}),
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
|
||||
// drag helper
|
||||
export type ShapeDragEvent = {
|
||||
inputType: 'mouse' | 'touch';
|
||||
x: number;
|
||||
y: number;
|
||||
el: HTMLElement;
|
||||
originalEvent: MouseEvent | TouchEvent;
|
||||
};
|
||||
|
||||
export const touchResolver = (event: TouchEvent) =>
|
||||
({
|
||||
inputType: 'touch',
|
||||
x: event.touches[0].clientX,
|
||||
y: event.touches[0].clientY,
|
||||
el: event.currentTarget as HTMLElement,
|
||||
originalEvent: event,
|
||||
}) satisfies ShapeDragEvent;
|
||||
|
||||
export const mouseResolver = (event: MouseEvent) =>
|
||||
({
|
||||
inputType: 'mouse',
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
el: event.currentTarget as HTMLElement,
|
||||
originalEvent: event,
|
||||
}) satisfies ShapeDragEvent;
|
||||
|
||||
// overlay helper
|
||||
export const defaultDraggingInfo = {
|
||||
startPos: { x: 0, y: 0 },
|
||||
toolbarRect: {} as DOMRect,
|
||||
edgelessRect: {} as DOMRect,
|
||||
shapeRectOriginal: {} as DOMRect,
|
||||
shapeEl: null as unknown as HTMLElement,
|
||||
parentToMount: null as unknown as HTMLElement,
|
||||
moved: false,
|
||||
shape: null as unknown as DraggableShape,
|
||||
style: {} as CSSStyleDeclaration,
|
||||
};
|
||||
export type DraggingInfo = typeof defaultDraggingInfo;
|
||||
|
||||
export const createShapeDraggingOverlay = (info: DraggingInfo) => {
|
||||
const { edgelessRect, parentToMount } = info;
|
||||
const overlay = document.createElement('div');
|
||||
Object.assign(overlay.style, {
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0',
|
||||
width: edgelessRect.width + 'px',
|
||||
// always clip
|
||||
// height: toolbarRect.bottom - edgelessRect.top + 'px',
|
||||
height: edgelessRect.height + 'px',
|
||||
overflow: 'hidden',
|
||||
zIndex: '9999',
|
||||
|
||||
// for debug purpose
|
||||
// background: 'rgba(255, 0, 0, 0.1)',
|
||||
});
|
||||
|
||||
const shape = document.createElement('div');
|
||||
const shapeScaleWrapper = document.createElement('div');
|
||||
Object.assign(shapeScaleWrapper.style, {
|
||||
transform: 'scale(var(--s, 1))',
|
||||
transition: 'transform 0.1s',
|
||||
transformOrigin: 'var(--o, center)',
|
||||
});
|
||||
render(info.shape.svg, shapeScaleWrapper);
|
||||
Object.assign(shape.style, {
|
||||
position: 'absolute',
|
||||
color: info.style.color,
|
||||
stroke: info.style.stroke,
|
||||
filter: `var(--shape-filter, ${info.style.filter})`,
|
||||
transform: 'translate(var(--x, 0), var(--y, 0))',
|
||||
left: 'var(--left, 0)',
|
||||
top: 'var(--top, 0)',
|
||||
cursor: 'grabbing',
|
||||
transition: 'inherit',
|
||||
});
|
||||
|
||||
shape.append(shapeScaleWrapper);
|
||||
overlay.append(shape);
|
||||
parentToMount.append(overlay);
|
||||
|
||||
return overlay;
|
||||
};
|
||||
Reference in New Issue
Block a user