mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-15 00:56:26 +08:00
feat: support snap when resizing element (#12563)
Fixes [BS-2753](https://linear.app/affine-design/issue/BS-2753/) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added snapping support when resizing elements, improving alignment and precision during resize operations. - Introduced new resize event handlers allowing extensions to customize resize behavior with start, move, and end callbacks. - **Bug Fixes** - Improved handling of snapping state to prevent errors during drag and resize actions. - **Tests** - Updated resizing tests to ensure consistent snapping behavior by removing default elements that could interfere with test results. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -14,6 +14,7 @@ import { GfxPrimitiveElementModel } from '../model/surface/element-model.js';
|
||||
import type { GfxElementModelView } from '../view/view.js';
|
||||
import { createInteractionContext, type SupportedEvents } from './event.js';
|
||||
import {
|
||||
type ActionContextMap,
|
||||
type InteractivityActionAPI,
|
||||
type InteractivityEventAPI,
|
||||
InteractivityExtensionIdentifier,
|
||||
@@ -882,7 +883,11 @@ export class InteractivityManager extends GfxExtension {
|
||||
handleElementResize(
|
||||
options: Omit<
|
||||
OptionResize,
|
||||
'lockRatio' | 'onResizeStart' | 'onResizeEnd' | 'onResizeUpdate'
|
||||
| 'lockRatio'
|
||||
| 'onResizeStart'
|
||||
| 'onResizeEnd'
|
||||
| 'onResizeUpdate'
|
||||
| 'onResizeMove'
|
||||
> & {
|
||||
onResizeStart?: () => void;
|
||||
onResizeEnd?: () => void;
|
||||
@@ -910,6 +915,23 @@ export class InteractivityManager extends GfxExtension {
|
||||
const elements = Array.from(viewConfigMap.values()).map(
|
||||
config => config.view.model
|
||||
) as GfxModel[];
|
||||
const extensionHandlers = this.interactExtensions.values().reduce(
|
||||
(handlers, ext) => {
|
||||
const extHandlers = (ext.action as InteractivityActionAPI).emit(
|
||||
'elementResize',
|
||||
{
|
||||
elements,
|
||||
}
|
||||
);
|
||||
|
||||
if (extHandlers) {
|
||||
handlers.push(extHandlers);
|
||||
}
|
||||
|
||||
return handlers;
|
||||
},
|
||||
[] as ActionContextMap['elementResize']['returnType'][]
|
||||
);
|
||||
let lockRatio = false;
|
||||
|
||||
viewConfigMap.forEach(config => {
|
||||
@@ -925,11 +947,46 @@ export class InteractivityManager extends GfxExtension {
|
||||
...options,
|
||||
lockRatio,
|
||||
elements,
|
||||
onResizeMove: ({ dx, dy, handleSign, lockRatio }) => {
|
||||
const suggested: {
|
||||
dx: number;
|
||||
dy: number;
|
||||
priority?: number;
|
||||
}[] = [];
|
||||
const suggest = (distance: { dx: number; dy: number }) => {
|
||||
suggested.push(distance);
|
||||
};
|
||||
|
||||
extensionHandlers.forEach(ext => {
|
||||
ext.onResizeMove?.({
|
||||
dx,
|
||||
dy,
|
||||
elements,
|
||||
handleSign,
|
||||
handle,
|
||||
lockRatio,
|
||||
suggest,
|
||||
});
|
||||
});
|
||||
|
||||
suggested.sort((a, b) => {
|
||||
return (a.priority ?? 0) - (b.priority ?? 0);
|
||||
});
|
||||
|
||||
return last(suggested) ?? { dx, dy };
|
||||
},
|
||||
onResizeStart: ({ data }) => {
|
||||
this.activeInteraction$.value = {
|
||||
type: 'resize',
|
||||
elements,
|
||||
};
|
||||
extensionHandlers.forEach(ext => {
|
||||
ext.onResizeStart?.({
|
||||
elements,
|
||||
handle,
|
||||
});
|
||||
});
|
||||
|
||||
options.onResizeStart?.();
|
||||
data.forEach(({ model }) => {
|
||||
if (!viewConfigMap.has(model.id)) {
|
||||
@@ -990,6 +1047,13 @@ export class InteractivityManager extends GfxExtension {
|
||||
},
|
||||
onResizeEnd: ({ data }) => {
|
||||
this.activeInteraction$.value = null;
|
||||
|
||||
extensionHandlers.forEach(ext => {
|
||||
ext.onResizeEnd?.({
|
||||
elements,
|
||||
handle,
|
||||
});
|
||||
});
|
||||
options.onResizeEnd?.();
|
||||
this.std.store.transact(() => {
|
||||
data.forEach(({ model }) => {
|
||||
|
||||
Reference in New Issue
Block a user