feat(editor): inner toolbar layout for block (#11243)

Close [BS-2808](https://linear.app/affine-design/issue/BS-2808/组件内-toolbar-重构)
This commit is contained in:
L-Sun
2025-03-28 03:47:37 +00:00
parent 7193393a06
commit e2c752d56f
8 changed files with 150 additions and 62 deletions

View File

@@ -17,6 +17,7 @@ import {
ToolbarFlag as Flag,
ToolbarRegistryIdentifier,
} from '@blocksuite/affine-shared/services';
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
import { matchModels } from '@blocksuite/affine-shared/utils';
import {
type BlockComponent,
@@ -36,7 +37,7 @@ import {
getCommonBoundWithRotation,
} from '@blocksuite/global/gfx';
import { nextTick } from '@blocksuite/global/utils';
import type { Placement, ReferenceElement, SideObject } from '@floating-ui/dom';
import type { ReferenceElement, SideObject } from '@floating-ui/dom';
import { batch, effect, signal } from '@preact/signals-core';
import { css, unsafeCSS } from 'lit';
import groupBy from 'lodash-es/groupBy';
@@ -76,15 +77,32 @@ export class AffineToolbarWidget extends WidgetComponent {
}
}
editor-toolbar[data-app-theme='dark'] {
${unsafeCSS(darkToolbarStyles.join('\n'))}
}
editor-toolbar[data-app-theme='light'] {
${unsafeCSS(lightToolbarStyles.join('\n'))}
}
`;
editor-toolbar[data-placement='inner'] {
background-color: unset;
box-shadow: unset;
height: fit-content;
padding-top: 4px;
border-radius: 0;
border: unset;
justify-content: flex-end;
box-sizing: border-box;
gap: 4px;
placement$ = signal<Placement>('top');
editor-icon-button,
editor-menu-button {
background: ${unsafeCSSVarV2('button/iconButtonSolid')};
color: ${unsafeCSSVarV2('text/primary')};
box-shadow: ${unsafeCSSVar('buttonShadow')};
border-radius: 4px;
}
editor-menu-button > div {
gap: 4px;
}
}
${unsafeCSS(darkToolbarStyles('editor-toolbar'))}
${unsafeCSS(lightToolbarStyles('editor-toolbar'))}
`;
sideOptions$ = signal<Partial<SideObject> | null>(null);
@@ -213,7 +231,7 @@ export class AffineToolbarWidget extends WidgetComponent {
this.sideOptions$.value = sideOptions;
ctx.flavour$.value = flavour;
this.placement$.value = hasLocked ? 'top' : 'top-start';
ctx.placement$.value = hasLocked ? 'top' : 'top-start';
ctx.flags.refresh(Flag.Surface);
});
}
@@ -228,7 +246,6 @@ export class AffineToolbarWidget extends WidgetComponent {
super.connectedCallback();
const {
placement$,
sideOptions$,
referenceElement$,
disposables,
@@ -237,7 +254,7 @@ export class AffineToolbarWidget extends WidgetComponent {
host,
std,
} = this;
const { flags, flavour$, message$ } = toolbarRegistry;
const { flags, flavour$, message$, placement$ } = toolbarRegistry;
const context = new ToolbarContext(std);
// TODO(@fundon): fix toolbar position shaking when the wheel scrolls
@@ -266,7 +283,7 @@ export class AffineToolbarWidget extends WidgetComponent {
sideOptions$.value = null;
flavour$.value = 'affine:note';
placement$.value = 'top';
placement$.value = toolbarRegistry.getModulePlacement('affine:note');
flags.refresh(Flag.Text);
});
})
@@ -317,7 +334,7 @@ export class AffineToolbarWidget extends WidgetComponent {
sideOptions$.value = null;
flavour$.value = 'affine:note';
placement$.value = 'top';
placement$.value = toolbarRegistry.getModulePlacement('affine:note');
flags.refresh(Flag.Native);
});
});
@@ -338,7 +355,7 @@ export class AffineToolbarWidget extends WidgetComponent {
if (block) {
const modelFlavour = block.model.flavour;
const existed =
toolbarRegistry.modules.has(modelFlavour) ??
toolbarRegistry.modules.has(modelFlavour) ||
toolbarRegistry.modules.has(`custom:${modelFlavour}`);
if (existed) {
flavour = modelFlavour;
@@ -366,7 +383,10 @@ export class AffineToolbarWidget extends WidgetComponent {
sideOptions$.value = null;
flavour$.value = flavour;
placement$.value = flavour === 'affine:note' ? 'top' : 'top-start';
placement$.value = toolbarRegistry.getModulePlacement(
flavour,
flavour === 'affine:note' ? 'top' : 'top-start'
);
flags.refresh(Flag.Block);
});
})
@@ -494,7 +514,7 @@ export class AffineToolbarWidget extends WidgetComponent {
})
);
// Handles elemets when updating
// Handles elements when updating
disposables.add(
context.gfx.surface$.subscribe(surface => {
if (!surface) return;
@@ -580,7 +600,7 @@ export class AffineToolbarWidget extends WidgetComponent {
sideOptions$.value = null;
flavour$.value = flavour;
placement$.value = 'top';
placement$.value = toolbarRegistry.getModulePlacement(flavour);
flags.refresh(Flag.Hovering);
});
})
@@ -593,6 +613,13 @@ export class AffineToolbarWidget extends WidgetComponent {
})
);
// Update layout when placement changing to `inner`
disposables.add(
effect(() => {
toolbar.dataset.placement = placement$.value;
})
);
disposables.add(
effect(() => {
const value = flags.value$.value;