refactor(editor): edgeless toolbar lock and unlock actions (#10878)

This commit is contained in:
fundon
2025-03-20 02:08:16 +00:00
parent bd9b78f7d2
commit 07a64eb004
5 changed files with 277 additions and 79 deletions

View File

@@ -128,6 +128,82 @@ export class AffineToolbarWidget extends WidgetComponent {
: null;
}
updateWithSurface(
ctx: ToolbarContext,
activated: boolean,
elementIds: string[]
) {
const gfx = ctx.gfx;
const surface = gfx.surface;
let flavour = 'affine:surface';
let elements: GfxModel[] = [];
let hasLocked = false;
let sideOptions = null;
let paired: [string, GfxModel[]][] = [];
if (activated && surface) {
elements = elementIds
.map(id => gfx.getElementById(id))
.filter(model => model !== null) as GfxModel[];
hasLocked = elements.some(e => e.isLocked());
const grouped = groupBy(
elements.map(model => {
let flavour = surface.flavour;
if (model instanceof GfxBlockElementModel) {
flavour += `:${model.flavour.split(':').pop()}`;
} else if (model instanceof GfxPrimitiveElementModel) {
flavour += `:${model.type}`;
}
return { model, flavour };
}),
e => e.flavour
);
paired = toPairs(grouped).map(([flavour, items]) => [
flavour,
items.map(({ model }) => model),
]);
if (hasLocked) {
flavour = 'affine:surface:locked';
} else {
if (paired.length === 1) {
flavour = paired[0][0];
if (flavour === 'affine:surface:shape' && paired[0][1].length === 1) {
sideOptions = sideMap.get(flavour) ?? null;
}
}
}
if (!sideOptions) {
const flavours = new Set(paired.map(([f]) => f));
if (flavours.has('affine:surface:frame')) {
sideOptions = sideMap.get('affine:surface:frame') ?? null;
} else if (flavours.has('affine:surface:group')) {
sideOptions = sideMap.get('affine:surface:group') ?? null;
}
}
}
batch(() => {
ctx.flags.toggle(Flag.Surface, activated);
ctx.elementsMap$.value = new Map(paired);
if (!activated || !flavour) return;
this.setReferenceElementWithElements(gfx, elements);
this.sideOptions$.value = sideOptions;
ctx.flavour$.value = flavour;
this.placement$.value = hasLocked ? 'top' : 'top-start';
ctx.flags.refresh(Flag.Surface);
});
}
toolbar = new EditorToolbar();
get toolbarRegistry() {
@@ -147,7 +223,7 @@ export class AffineToolbarWidget extends WidgetComponent {
host,
std,
} = this;
const { flags, flavour$, message$, elementsMap$ } = toolbarRegistry;
const { flags, flavour$, message$ } = toolbarRegistry;
const context = new ToolbarContext(std);
// TODO(@fundon): fix toolbar position shaking when the wheel scrolls
@@ -293,76 +369,9 @@ export class AffineToolbarWidget extends WidgetComponent {
.map(s => (s.editing || s.inoperable ? [] : s.elements))
.flat();
const count = elementIds.length;
const gfx = context.gfx;
const surface = gfx.surface;
const activated =
context.activated && Boolean(surface) && Boolean(count);
let flavour = 'affine:surface';
let elements: GfxModel[] = [];
let hasLocked = false;
let sideOptions = null;
let paired: [string, GfxModel[]][] = [];
const activated = context.activated && Boolean(count);
if (activated && surface) {
elements = elementIds
.map(id => gfx.getElementById(id))
.filter(model => model !== null) as GfxModel[];
hasLocked = elements.some(e => e.isLocked());
const grouped = groupBy(
elements.map(model => {
let flavour = surface.flavour;
if (model instanceof GfxBlockElementModel) {
flavour += `:${model.flavour.split(':').pop()}`;
} else if (model instanceof GfxPrimitiveElementModel) {
flavour += `:${model.type}`;
}
return { model, flavour };
}),
e => e.flavour
);
paired = toPairs(grouped).map(([flavour, items]) => [
flavour,
items.map(({ model }) => model),
]);
if (paired.length === 1) {
flavour = paired[0][0];
if (
flavour === 'affine:surface:shape' &&
paired[0][1].length === 1
) {
sideOptions = sideMap.get(flavour) ?? null;
}
}
if (!sideOptions) {
const flavours = new Set(paired.map(([f]) => f));
if (flavours.has('affine:surface:frame')) {
sideOptions = sideMap.get('affine:surface:frame') ?? null;
} else if (flavours.has('affine:surface:group')) {
sideOptions = sideMap.get('affine:surface:group') ?? null;
}
}
}
batch(() => {
flags.toggle(Flag.Surface, activated);
elementsMap$.value = new Map(paired);
if (!activated || !flavour) return;
this.setReferenceElementWithElements(gfx, elements);
sideOptions$.value = sideOptions;
flavour$.value = flavour;
placement$.value = hasLocked ? 'top' : 'top-start';
flags.refresh(Flag.Surface);
});
this.updateWithSurface(context, activated, elementIds);
})
);
@@ -452,7 +461,12 @@ export class AffineToolbarWidget extends WidgetComponent {
}
if (flags.isSurface()) {
flags.refresh(Flag.Surface);
const elementIds = context.gfx.selection.selectedIds;
this.updateWithSurface(
context,
context.activated && Boolean(elementIds.length),
elementIds
);
return;
}
})
@@ -460,14 +474,18 @@ export class AffineToolbarWidget extends WidgetComponent {
// Handles elemets when updating
disposables.add(
effect(() => {
const surface = context.gfx.surface$.value;
context.gfx.surface$.subscribe(surface => {
if (!surface) return;
const subscription = surface.elementUpdated.subscribe(() => {
if (!flags.isSurface()) return;
flags.refresh(Flag.Surface);
const elementIds = context.gfx.selection.selectedIds;
this.updateWithSurface(
context,
context.activated && Boolean(elementIds.length),
elementIds
);
});
return () => {