mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 21:27:20 +00:00
refactor(editor): edgeless toolbar more actions (#10882)
This commit is contained in:
@@ -30,8 +30,8 @@ import { EditIcon, PageIcon, UngroupIcon } from '@blocksuite/icons/lit';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { html } from 'lit';
|
||||
|
||||
import { EdgelessRootBlockComponent } from '../..';
|
||||
import { mountFrameTitleEditor } from '../../utils/text';
|
||||
import { getEdgelessWith } from './utils';
|
||||
|
||||
const builtinSurfaceToolbarConfig = {
|
||||
actions: [
|
||||
@@ -87,15 +87,8 @@ const builtinSurfaceToolbarConfig = {
|
||||
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;
|
||||
}
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
mountFrameTitleEditor(model, edgeless);
|
||||
},
|
||||
@@ -108,15 +101,8 @@ const builtinSurfaceToolbarConfig = {
|
||||
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;
|
||||
}
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
ctx.store.captureSync();
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||
import { Bound } from '@blocksuite/global/gfx';
|
||||
import { EditIcon, PageIcon, UngroupIcon } from '@blocksuite/icons/lit';
|
||||
|
||||
import { EdgelessRootBlockComponent } from '../..';
|
||||
import { mountGroupTitleEditor } from '../../utils/text';
|
||||
import { getEdgelessWith } from './utils';
|
||||
|
||||
export const builtinGroupToolbarConfig = {
|
||||
actions: [
|
||||
@@ -69,15 +69,8 @@ export const builtinGroupToolbarConfig = {
|
||||
const model = ctx.getCurrentModelByType(GroupElementModel);
|
||||
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;
|
||||
}
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
mountGroupTitleEditor(model, edgeless);
|
||||
},
|
||||
@@ -90,15 +83,8 @@ export const builtinGroupToolbarConfig = {
|
||||
const models = ctx.getSurfaceModelsByType(GroupElementModel);
|
||||
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;
|
||||
}
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
for (const model of models) {
|
||||
edgeless.service.ungroup(model);
|
||||
|
||||
@@ -25,8 +25,9 @@ import {
|
||||
} from '@blocksuite/icons/lit';
|
||||
import { html } from 'lit';
|
||||
|
||||
import { EdgelessRootBlockComponent } from '../..';
|
||||
import { renderAlignmentMenu } from './alignment';
|
||||
import { moreActions } from './more';
|
||||
import { getEdgelessWith } from './utils';
|
||||
|
||||
export const builtinMiscToolbarConfig = {
|
||||
actions: [
|
||||
@@ -86,15 +87,8 @@ export const builtinMiscToolbarConfig = {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (models.length < 2) 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;
|
||||
}
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
const frame = edgeless.service.frame.createFrameOnSelected();
|
||||
if (!frame) return;
|
||||
@@ -135,15 +129,8 @@ export const builtinMiscToolbarConfig = {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (models.length < 2) 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;
|
||||
}
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
// TODO(@fundon): should be a command
|
||||
edgeless.service.createGroupFromSelected();
|
||||
@@ -155,7 +142,6 @@ export const builtinMiscToolbarConfig = {
|
||||
when(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (models.length < 2) return false;
|
||||
if (models.some(model => model.isLocked())) return false;
|
||||
if (models.some(model => model.group instanceof MindmapElementModel))
|
||||
return false;
|
||||
if (
|
||||
@@ -227,15 +213,8 @@ export const builtinMiscToolbarConfig = {
|
||||
const models = ctx.getSurfaceModels();
|
||||
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;
|
||||
}
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
// get most top selected elements(*) from tree, like in a tree below
|
||||
// G0
|
||||
@@ -318,6 +297,12 @@ export const builtinMiscToolbarConfig = {
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
// More actions
|
||||
...moreActions.map(action => ({
|
||||
...action,
|
||||
placement: ActionPlacement.More,
|
||||
})),
|
||||
],
|
||||
when(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
@@ -336,15 +321,8 @@ export const builtinLockedToolbarConfig = {
|
||||
const models = ctx.getSurfaceModels();
|
||||
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;
|
||||
}
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
const elements = new Set(
|
||||
models.map(model =>
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
import { AttachmentBlockComponent } from '@blocksuite/affine-block-attachment';
|
||||
import { BookmarkBlockComponent } from '@blocksuite/affine-block-bookmark';
|
||||
import {
|
||||
isExternalEmbedBlockComponent,
|
||||
notifyDocCreated,
|
||||
promptDocTitle,
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import { EdgelessFrameManagerIdentifier } from '@blocksuite/affine-block-frame';
|
||||
import { ImageBlockComponent } from '@blocksuite/affine-block-image';
|
||||
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
AttachmentBlockModel,
|
||||
BookmarkBlockModel,
|
||||
EmbedLinkedDocBlockSchema,
|
||||
EmbedLinkedDocModel,
|
||||
EmbedSyncedDocBlockSchema,
|
||||
EmbedSyncedDocModel,
|
||||
FrameBlockModel,
|
||||
ImageBlockModel,
|
||||
isExternalEmbedModel,
|
||||
NoteBlockModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import type {
|
||||
ToolbarActions,
|
||||
ToolbarContext,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { type ReorderingType } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockComponent } from '@blocksuite/block-std';
|
||||
import { GfxBlockElementModel, type GfxModel } from '@blocksuite/block-std/gfx';
|
||||
import { Bound, getCommonBoundWithRotation } from '@blocksuite/global/gfx';
|
||||
import {
|
||||
ArrowDownBigBottomIcon,
|
||||
ArrowDownBigIcon,
|
||||
ArrowUpBigIcon,
|
||||
ArrowUpBigTopIcon,
|
||||
CopyIcon,
|
||||
DeleteIcon,
|
||||
DuplicateIcon,
|
||||
FrameIcon,
|
||||
GroupIcon,
|
||||
LinkedPageIcon,
|
||||
ResetIcon,
|
||||
} from '@blocksuite/icons/lit';
|
||||
|
||||
import {
|
||||
createLinkedDocFromEdgelessElements,
|
||||
createLinkedDocFromNote,
|
||||
} from '../../../widgets/element-toolbar/more-menu/render-linked-doc';
|
||||
import { duplicate } from '../../utils/clipboard-utils';
|
||||
import { getSortedCloneElements } from '../../utils/clone-utils';
|
||||
import { moveConnectors } from '../../utils/connector';
|
||||
import { deleteElements } from '../../utils/crud';
|
||||
import { getEdgelessWith } from './utils';
|
||||
|
||||
export const moreActions = [
|
||||
// Selection Group: frame & group
|
||||
{
|
||||
id: 'Z.a.selection',
|
||||
actions: [
|
||||
{
|
||||
id: 'a.create-frame',
|
||||
label: 'Frame section',
|
||||
icon: FrameIcon(),
|
||||
run(ctx) {
|
||||
const frame = ctx.std
|
||||
.get(EdgelessFrameManagerIdentifier)
|
||||
.createFrameOnSelected();
|
||||
if (!frame) return;
|
||||
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
edgeless.surface.fitToViewport(Bound.deserialize(frame.xywh));
|
||||
|
||||
ctx.track('CanvasElementAdded', {
|
||||
control: 'context-menu',
|
||||
type: 'frame',
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'b.create-group',
|
||||
label: 'Group section',
|
||||
icon: GroupIcon(),
|
||||
when(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (models.length === 0) return false;
|
||||
return !models.some(model => ctx.matchModel(model, FrameBlockModel));
|
||||
},
|
||||
run(ctx) {
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
edgeless.service.createGroupFromSelected();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Reordering Group
|
||||
{
|
||||
id: 'Z.b.reordering',
|
||||
actions: [
|
||||
{
|
||||
id: 'a.bring-to-front',
|
||||
label: 'Bring to Front',
|
||||
icon: ArrowUpBigTopIcon(),
|
||||
run(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
reorderElements(ctx, models, 'front');
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'b.bring-forward',
|
||||
label: 'Bring Forward',
|
||||
icon: ArrowUpBigIcon(),
|
||||
run(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
reorderElements(ctx, models, 'forward');
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'c.send-backward',
|
||||
label: 'Send Backward',
|
||||
icon: ArrowDownBigIcon(),
|
||||
run(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
reorderElements(ctx, models, 'backward');
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'c.send-to-back',
|
||||
label: 'Send to Back',
|
||||
icon: ArrowDownBigBottomIcon(),
|
||||
run(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
reorderElements(ctx, models, 'back');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Clipboard Group
|
||||
// Uses the same `ID` for both page and edgeless modes.
|
||||
{
|
||||
id: 'a.clipboard',
|
||||
actions: [
|
||||
{
|
||||
id: 'copy',
|
||||
label: 'Copy',
|
||||
icon: CopyIcon(),
|
||||
run(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (!models.length) return;
|
||||
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
edgeless.clipboardController.copy();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'duplicate',
|
||||
label: 'Duplicate',
|
||||
icon: DuplicateIcon(),
|
||||
run(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (!models.length) return;
|
||||
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
duplicate(edgeless, models).catch(console.error);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'reload',
|
||||
label: 'Reload',
|
||||
icon: ResetIcon(),
|
||||
when(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (models.length === 0) return false;
|
||||
return models.every(isRefreshableModel);
|
||||
},
|
||||
run(ctx) {
|
||||
const blocks = ctx
|
||||
.getSurfaceModels()
|
||||
.map(model => ctx.view.getBlock(model.id))
|
||||
.filter(isRefreshableBlock);
|
||||
|
||||
if (!blocks.length) return;
|
||||
|
||||
for (const block of blocks) {
|
||||
block.refreshData();
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Conversions Group
|
||||
{
|
||||
id: 'd.conversions',
|
||||
actions: [
|
||||
{
|
||||
id: 'a.turn-into-linked-doc',
|
||||
label: 'Turn into linked doc',
|
||||
icon: LinkedPageIcon(),
|
||||
when(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (models.length !== 1) return false;
|
||||
return ctx.matchModel(models[0], NoteBlockModel);
|
||||
},
|
||||
run(ctx) {
|
||||
const model = ctx.getCurrentModelByType(NoteBlockModel);
|
||||
if (!model) return;
|
||||
|
||||
const create = async () => {
|
||||
const title = await promptDocTitle(ctx.std);
|
||||
if (title === null) return;
|
||||
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
const surfaceId = edgeless.surfaceBlockModel.id;
|
||||
if (!surfaceId) return;
|
||||
|
||||
const linkedDoc = createLinkedDocFromNote(ctx.store, model, title);
|
||||
|
||||
// Inserts linked doc card
|
||||
const cardId = ctx.std.get(EdgelessCRUDIdentifier).addBlock(
|
||||
EmbedSyncedDocBlockSchema.model.flavour,
|
||||
{
|
||||
xywh: model.xywh,
|
||||
style: 'syncedDoc',
|
||||
pageId: linkedDoc.id,
|
||||
index: model.index,
|
||||
},
|
||||
surfaceId
|
||||
);
|
||||
|
||||
ctx.track('CanvasElementAdded', {
|
||||
control: 'context-menu',
|
||||
type: 'embed-synced-doc',
|
||||
});
|
||||
ctx.track('DocCreated', {
|
||||
control: 'turn into linked doc',
|
||||
type: 'embed-linked-doc',
|
||||
});
|
||||
ctx.track('LinkedDocCreated', {
|
||||
control: 'turn into linked doc',
|
||||
type: 'embed-linked-doc',
|
||||
other: 'new doc',
|
||||
});
|
||||
|
||||
moveConnectors(model.id, cardId, edgeless.service);
|
||||
|
||||
// Deletes selected note
|
||||
ctx.store.transact(() => {
|
||||
ctx.store.deleteBlock(model);
|
||||
});
|
||||
ctx.gfx.selection.set({
|
||||
elements: [cardId],
|
||||
editing: false,
|
||||
});
|
||||
};
|
||||
|
||||
create().catch(console.error);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'b.create-linked-doc',
|
||||
label: 'Create linked doc',
|
||||
icon: LinkedPageIcon(),
|
||||
when(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (models.length === 0) return false;
|
||||
if (models.length === 1) {
|
||||
return ![
|
||||
NoteBlockModel,
|
||||
EmbedLinkedDocModel,
|
||||
EmbedSyncedDocModel,
|
||||
].some(k => ctx.matchModel(models[0], k));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
run(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (!models.length) return;
|
||||
|
||||
const create = async () => {
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
const surfaceId = edgeless.surfaceBlockModel.id;
|
||||
if (!surfaceId) return;
|
||||
|
||||
const title = await promptDocTitle(ctx.std);
|
||||
if (title === null) return;
|
||||
|
||||
const clonedModels = getSortedCloneElements(models);
|
||||
const linkedDoc = createLinkedDocFromEdgelessElements(
|
||||
ctx.host,
|
||||
clonedModels,
|
||||
title
|
||||
);
|
||||
|
||||
ctx.store.transact(() => {
|
||||
deleteElements(edgeless, clonedModels);
|
||||
});
|
||||
|
||||
// Inserts linked doc card
|
||||
const width = 364;
|
||||
const height = 390;
|
||||
const bound = getCommonBoundWithRotation(clonedModels);
|
||||
const cardId = ctx.std.get(EdgelessCRUDIdentifier).addBlock(
|
||||
EmbedLinkedDocBlockSchema.model.flavour,
|
||||
{
|
||||
xywh: `[${bound.center[0] - width / 2}, ${bound.center[1] - height / 2}, ${width}, ${height}]`,
|
||||
style: 'vertical',
|
||||
pageId: linkedDoc.id,
|
||||
},
|
||||
surfaceId
|
||||
);
|
||||
|
||||
ctx.gfx.selection.set({
|
||||
elements: [cardId],
|
||||
editing: false,
|
||||
});
|
||||
|
||||
ctx.track('CanvasElementAdded', {
|
||||
control: 'context-menu',
|
||||
type: 'embed-linked-doc',
|
||||
});
|
||||
ctx.track('DocCreated', {
|
||||
control: 'create linked doc',
|
||||
type: 'embed-linked-doc',
|
||||
});
|
||||
ctx.track('LinkedDocCreated', {
|
||||
control: 'create linked doc',
|
||||
type: 'embed-linked-doc',
|
||||
other: 'new doc',
|
||||
});
|
||||
|
||||
notifyDocCreated(ctx.std, ctx.store);
|
||||
};
|
||||
|
||||
create().catch(console.error);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Deleting Group
|
||||
{
|
||||
id: 'e.delete',
|
||||
label: 'Delete',
|
||||
icon: DeleteIcon(),
|
||||
variant: 'destructive',
|
||||
run(ctx) {
|
||||
const models = ctx.getSurfaceModels();
|
||||
if (!models.length) return;
|
||||
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
ctx.store.captureSync();
|
||||
|
||||
deleteElements(edgeless, models);
|
||||
|
||||
// Clears
|
||||
ctx.select('surface');
|
||||
ctx.reset();
|
||||
},
|
||||
},
|
||||
] as const satisfies ToolbarActions;
|
||||
|
||||
function reorderElements(
|
||||
ctx: ToolbarContext,
|
||||
models: GfxModel[],
|
||||
type: ReorderingType
|
||||
) {
|
||||
if (!models.length) return;
|
||||
|
||||
for (const model of models) {
|
||||
const index = ctx.gfx.layer.getReorderedIndex(model, type);
|
||||
|
||||
// block should be updated in transaction
|
||||
if (model instanceof GfxBlockElementModel) {
|
||||
ctx.store.transact(() => {
|
||||
model.index = index;
|
||||
});
|
||||
} else {
|
||||
model.index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isRefreshableModel(model: GfxModel) {
|
||||
return (
|
||||
model instanceof AttachmentBlockModel ||
|
||||
model instanceof BookmarkBlockModel ||
|
||||
model instanceof ImageBlockModel ||
|
||||
isExternalEmbedModel(model)
|
||||
);
|
||||
}
|
||||
|
||||
function isRefreshableBlock(block: BlockComponent | null) {
|
||||
return (
|
||||
!!block &&
|
||||
(block instanceof AttachmentBlockComponent ||
|
||||
block instanceof BookmarkBlockComponent ||
|
||||
block instanceof ImageBlockComponent ||
|
||||
isExternalEmbedBlockComponent(block))
|
||||
);
|
||||
}
|
||||
@@ -35,7 +35,7 @@ import { AddTextIcon, ShapeIcon } from '@blocksuite/icons/lit';
|
||||
import { html } from 'lit';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
|
||||
import { EdgelessRootBlockComponent, type ShapeToolOption } from '../..';
|
||||
import type { ShapeToolOption } from '../..';
|
||||
import { ShapeComponentConfig } from '../../components/toolbar/shape/shape-menu-config';
|
||||
import { mountShapeTextEditor } from '../../utils/text';
|
||||
import { LINE_STYLE_LIST } from './consts';
|
||||
@@ -44,7 +44,7 @@ import {
|
||||
createMindmapStyleActionMenu,
|
||||
} from './mindmap';
|
||||
import { createTextActions } from './text-common';
|
||||
import { renderMenu } from './utils';
|
||||
import { getEdgelessWith, renderMenu } from './utils';
|
||||
|
||||
export const builtinShapeToolbarConfig = {
|
||||
actions: [
|
||||
@@ -318,15 +318,8 @@ export const builtinShapeToolbarConfig = {
|
||||
const model = ctx.getCurrentModelByType(ShapeElementModel);
|
||||
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;
|
||||
}
|
||||
const edgeless = getEdgelessWith(ctx);
|
||||
if (!edgeless) return;
|
||||
|
||||
mountShapeTextEditor(model, edgeless);
|
||||
},
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import type { ToolbarContext } from '@blocksuite/affine-shared/services';
|
||||
import { ArrowDownSmallIcon } from '@blocksuite/icons/lit';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import { EdgelessRootBlockComponent } from '../..';
|
||||
import type { Menu, MenuItem } from './types';
|
||||
|
||||
export function renderCurrentMenuItemWith<T, F extends keyof MenuItem<T>>(
|
||||
@@ -60,3 +62,17 @@ export function renderMenuItems<T>(
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(@fundon): it should be simple
|
||||
export function getEdgelessWith(ctx: ToolbarContext) {
|
||||
const rootModel = ctx.store.root;
|
||||
if (!rootModel) return;
|
||||
|
||||
const edgeless = ctx.view.getBlock(rootModel.id);
|
||||
if (!ctx.matchBlock(edgeless, EdgelessRootBlockComponent)) {
|
||||
console.error('edgeless view is not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
return edgeless;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
offset,
|
||||
shift,
|
||||
} from '@floating-ui/dom';
|
||||
import { html, render, type TemplateResult } from 'lit';
|
||||
import { html, render } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { join } from 'lit/directives/join.js';
|
||||
import { keyed } from 'lit/directives/keyed.js';
|
||||
@@ -223,32 +223,31 @@ export function renderToolbar(
|
||||
context,
|
||||
renderMenuActionItem
|
||||
);
|
||||
// if (moreMenuItems.length) {
|
||||
// TODO(@fundon): edgeless case needs to be considered
|
||||
const key = `${context.getCurrentModel()?.id}`;
|
||||
if (moreMenuItems.length) {
|
||||
const key = `${context.getCurrentModel()?.id}`;
|
||||
|
||||
primaryActionGroup.push({
|
||||
id: 'more',
|
||||
content: html`${keyed(
|
||||
`${flavour}:${key}`,
|
||||
html`
|
||||
<editor-menu-button
|
||||
class="more-menu"
|
||||
.contentPadding="${'8px'}"
|
||||
.button=${html`
|
||||
<editor-icon-button aria-label="More" .tooltip="${'More'}">
|
||||
${MoreVerticalIcon()}
|
||||
</editor-icon-button>
|
||||
`}
|
||||
>
|
||||
<div data-size="large" data-orientation="vertical">
|
||||
${join(moreMenuItems, renderToolbarSeparator('horizontal'))}
|
||||
</div>
|
||||
</editor-menu-button>
|
||||
`
|
||||
)}`,
|
||||
});
|
||||
// }
|
||||
primaryActionGroup.push({
|
||||
id: 'more',
|
||||
content: html`${keyed(
|
||||
`${flavour}:${key}`,
|
||||
html`
|
||||
<editor-menu-button
|
||||
class="more-menu"
|
||||
.contentPadding="${'8px'}"
|
||||
.button=${html`
|
||||
<editor-icon-button aria-label="More" .tooltip="${'More'}">
|
||||
${MoreVerticalIcon()}
|
||||
</editor-icon-button>
|
||||
`}
|
||||
>
|
||||
<div data-size="large" data-orientation="vertical">
|
||||
${join(moreMenuItems, renderToolbarSeparator('horizontal'))}
|
||||
</div>
|
||||
</editor-menu-button>
|
||||
`
|
||||
)}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render(
|
||||
@@ -264,20 +263,18 @@ function renderActions(
|
||||
) {
|
||||
return actions
|
||||
.map(action => {
|
||||
let content: TemplateResult | null = null;
|
||||
if ('content' in action) {
|
||||
if (typeof action.content === 'function') {
|
||||
content = action.content(context);
|
||||
return action.content(context);
|
||||
} else {
|
||||
content = action.content ?? null;
|
||||
return action.content ?? null;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
if ('actions' in action && action.actions.length) {
|
||||
const combined = combine(action.actions, context);
|
||||
|
||||
if (!combined.length) return content;
|
||||
if (!combined.length) return null;
|
||||
|
||||
const ordered = orderBy(combined, ['id', 'score'], ['asc', 'asc']);
|
||||
|
||||
@@ -301,7 +298,7 @@ function renderActions(
|
||||
return render(action, context);
|
||||
}
|
||||
|
||||
return content;
|
||||
return null;
|
||||
})
|
||||
.filter(action => action !== null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user