mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-15 00:56:26 +08:00
refactor(editor): edgeless frame toolbar config extension (#10769)
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import { FrameBlockSchema } from '@blocksuite/affine-model';
|
||||
import { BlockViewExtension } from '@blocksuite/block-std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { EdgelessFrameManager, FrameOverlay } from './frame-manager';
|
||||
|
||||
const flavour = FrameBlockSchema.model.flavour;
|
||||
|
||||
export const FrameBlockSpec: ExtensionType[] = [
|
||||
BlockViewExtension('affine:frame', literal`affine-frame`),
|
||||
BlockViewExtension(flavour, literal`affine-frame`),
|
||||
FrameOverlay,
|
||||
EdgelessFrameManager,
|
||||
];
|
||||
|
||||
@@ -1,11 +1,198 @@
|
||||
import type { ToolbarModuleConfig } from '@blocksuite/affine-shared/services';
|
||||
import { EdgelessFrameManagerIdentifier } from '@blocksuite/affine-block-frame';
|
||||
import { EdgelessCRUDExtension } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
packColor,
|
||||
type PickColorEvent,
|
||||
} from '@blocksuite/affine-components/color-picker';
|
||||
import { toast } from '@blocksuite/affine-components/toast';
|
||||
import {
|
||||
DEFAULT_NOTE_HEIGHT,
|
||||
DefaultTheme,
|
||||
FrameBlockModel,
|
||||
NoteBlockModel,
|
||||
NoteBlockSchema,
|
||||
NoteDisplayMode,
|
||||
resolveColor,
|
||||
SurfaceRefBlockSchema,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
FeatureFlagService,
|
||||
type ToolbarModuleConfig,
|
||||
ToolbarModuleExtension,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
getMostCommonResolvedValue,
|
||||
matchModels,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { BlockFlavourIdentifier } from '@blocksuite/block-std';
|
||||
import { Bound } from '@blocksuite/global/gfx';
|
||||
import { EditIcon, PageIcon, UngroupIcon } from '@blocksuite/icons/lit';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { html } from 'lit';
|
||||
|
||||
export const builtinFrameToolbarConfig = {
|
||||
import { EdgelessRootBlockComponent } from '../..';
|
||||
import { mountFrameTitleEditor } from '../../utils/text';
|
||||
|
||||
const builtinSurfaceToolbarConfig = {
|
||||
actions: [
|
||||
{
|
||||
id: 'a.test',
|
||||
label: 'Frame',
|
||||
run() {},
|
||||
id: 'a.insert-into-page',
|
||||
label: 'Insert into Page',
|
||||
tooltip: 'Insert into Page',
|
||||
icon: PageIcon(),
|
||||
when: ctx => ctx.getSurfaceModelsByType(FrameBlockModel).length === 1,
|
||||
run(ctx) {
|
||||
const model = ctx.getCurrentModelByType(FrameBlockModel);
|
||||
if (!model) return;
|
||||
|
||||
const rootModel = ctx.store.root;
|
||||
if (!rootModel) return;
|
||||
|
||||
const { id: frameId, xywh } = model;
|
||||
let lastNoteId = rootModel.children
|
||||
.filter(
|
||||
note =>
|
||||
matchModels(note, [NoteBlockModel]) &&
|
||||
note.props.displayMode !== NoteDisplayMode.EdgelessOnly
|
||||
)
|
||||
.pop()?.id;
|
||||
|
||||
if (!lastNoteId) {
|
||||
const bounds = Bound.deserialize(xywh);
|
||||
bounds.y += bounds.h;
|
||||
bounds.h = DEFAULT_NOTE_HEIGHT;
|
||||
|
||||
lastNoteId = ctx.store.addBlock(
|
||||
NoteBlockSchema.model.flavour,
|
||||
{ xywh: bounds.serialize() },
|
||||
rootModel.id
|
||||
);
|
||||
}
|
||||
|
||||
ctx.store.addBlock(
|
||||
SurfaceRefBlockSchema.model.flavour,
|
||||
{ reference: frameId, refFlavour: NoteBlockSchema.model.flavour },
|
||||
lastNoteId
|
||||
);
|
||||
|
||||
toast(ctx.host, 'Frame has been inserted into doc');
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'b.rename',
|
||||
tooltip: 'Rename',
|
||||
icon: EditIcon(),
|
||||
when: ctx => ctx.getSurfaceModelsByType(FrameBlockModel).length === 1,
|
||||
run(ctx) {
|
||||
const model = ctx.getCurrentModelByType(FrameBlockModel);
|
||||
if (!model) return;
|
||||
|
||||
const rootModel = ctx.store.root;
|
||||
if (!rootModel) return;
|
||||
|
||||
// TODO(@fundon): it should be simple
|
||||
const edgeless = ctx.view.getBlock(rootModel.id);
|
||||
if (!ctx.matchBlock(edgeless, EdgelessRootBlockComponent)) {
|
||||
console.error('edgeless view is not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
mountFrameTitleEditor(model, edgeless);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'b.ungroup',
|
||||
tooltip: 'Ungroup',
|
||||
icon: UngroupIcon(),
|
||||
run(ctx) {
|
||||
const models = ctx.getSurfaceModelsByType(FrameBlockModel);
|
||||
if (!models.length) return;
|
||||
|
||||
const rootModel = ctx.store.root;
|
||||
if (!rootModel) return;
|
||||
|
||||
// TODO(@fundon): it should be simple
|
||||
const edgeless = ctx.view.getBlock(rootModel.id);
|
||||
if (!ctx.matchBlock(edgeless, EdgelessRootBlockComponent)) {
|
||||
console.error('edgeless view is not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.store.captureSync();
|
||||
|
||||
const frameManager = ctx.std.get(EdgelessFrameManagerIdentifier);
|
||||
|
||||
for (const model of models) {
|
||||
frameManager.removeAllChildrenFromFrame(model);
|
||||
}
|
||||
|
||||
for (const model of models) {
|
||||
edgeless.service.removeElement(model);
|
||||
}
|
||||
|
||||
edgeless.service.selection.clear();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'c.color-picker',
|
||||
content(ctx) {
|
||||
const models = ctx.getSurfaceModelsByType(FrameBlockModel);
|
||||
if (!models.length) return null;
|
||||
|
||||
const enableCustomColor = ctx.std
|
||||
.get(FeatureFlagService)
|
||||
.getFlag('enable_color_picker');
|
||||
const theme = ctx.themeProvider.edgelessTheme;
|
||||
|
||||
const firstModel = models[0];
|
||||
const background =
|
||||
getMostCommonResolvedValue(
|
||||
models.map(model => model.props),
|
||||
'background',
|
||||
background => resolveColor(background, theme)
|
||||
) ?? DefaultTheme.transparent;
|
||||
|
||||
const onPick = (e: PickColorEvent) => {
|
||||
const field = 'background';
|
||||
|
||||
if (e.type === 'pick') {
|
||||
const color = e.detail.value;
|
||||
for (const model of models) {
|
||||
const props = packColor(field, color);
|
||||
ctx.std.get(EdgelessCRUDExtension).updateElement(model.id, props);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (const model of models) {
|
||||
model[e.type === 'start' ? 'stash' : 'pop'](field);
|
||||
}
|
||||
};
|
||||
|
||||
return html`
|
||||
<edgeless-color-picker-button
|
||||
class="background"
|
||||
.label="${'Background'}"
|
||||
.pick=${onPick}
|
||||
.color=${background}
|
||||
.theme=${theme}
|
||||
.originalColor=${firstModel.props.background}
|
||||
.enableCustomColor=${enableCustomColor}
|
||||
>
|
||||
</edgeless-color-picker-button>
|
||||
`;
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
when: ctx => ctx.getSurfaceModelsByType(FrameBlockModel).length > 0,
|
||||
} as const satisfies ToolbarModuleConfig;
|
||||
|
||||
export const createFrameToolbarConfig = (flavour: string): ExtensionType => {
|
||||
const name = flavour.split(':').pop();
|
||||
|
||||
return ToolbarModuleExtension({
|
||||
id: BlockFlavourIdentifier(`affine:surface:${name}`),
|
||||
config: builtinSurfaceToolbarConfig,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { FrameBlockSchema } from '@blocksuite/affine-model';
|
||||
import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
|
||||
import { BlockFlavourIdentifier } from '@blocksuite/block-std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
import { builtinBrushToolbarConfig } from './brush';
|
||||
import { builtinConnectorToolbarConfig } from './connector';
|
||||
import { builtinFrameToolbarConfig } from './frame';
|
||||
import { createFrameToolbarConfig } from './frame';
|
||||
import { builtinGroupToolbarConfig } from './group';
|
||||
import { builtinMindmapToolbarConfig } from './mindmap';
|
||||
import { builtinMiscToolbarConfig } from './misc';
|
||||
@@ -12,6 +13,8 @@ import { builtinShapeToolbarConfig } from './shape';
|
||||
import { builtinTextToolbarConfig } from './text';
|
||||
|
||||
export const EdgelessElementToolbarExtension: ExtensionType[] = [
|
||||
createFrameToolbarConfig(FrameBlockSchema.model.flavour),
|
||||
|
||||
ToolbarModuleExtension({
|
||||
id: BlockFlavourIdentifier('affine:surface:brush'),
|
||||
config: builtinBrushToolbarConfig,
|
||||
@@ -22,11 +25,6 @@ export const EdgelessElementToolbarExtension: ExtensionType[] = [
|
||||
config: builtinConnectorToolbarConfig,
|
||||
}),
|
||||
|
||||
ToolbarModuleExtension({
|
||||
id: BlockFlavourIdentifier('affine:surface:frame'),
|
||||
config: builtinFrameToolbarConfig,
|
||||
}),
|
||||
|
||||
ToolbarModuleExtension({
|
||||
id: BlockFlavourIdentifier('affine:surface:group'),
|
||||
config: builtinGroupToolbarConfig,
|
||||
|
||||
Reference in New Issue
Block a user