refactor(editor): extract note block (#9310)
This commit is contained in:
50 files changed
+467
-394
No files matched your search
@@ -5,14 +5,18 @@ import {
|
||||
NoteDisplayMode,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts';
|
||||
import { NotificationProvider } from '@blocksuite/affine-shared/services';
|
||||
import { matchFlavours, SpecProvider } from '@blocksuite/affine-shared/utils';
|
||||
import { BlockStdScope } from '@blocksuite/block-std';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import {
|
||||
type BlockModel,
|
||||
type BlockSnapshot,
|
||||
BlockViewType,
|
||||
type Doc,
|
||||
type DraftModel,
|
||||
type Query,
|
||||
Slice,
|
||||
} from '@blocksuite/store';
|
||||
import { render, type TemplateResult } from 'lit';
|
||||
|
||||
@@ -295,3 +299,122 @@ export function getDocContentWithMaxLength(doc: Doc, maxlength = 500) {
|
||||
|
||||
return texts.join('\n');
|
||||
}
|
||||
|
||||
export function getTitleFromSelectedModels(selectedModels: DraftModel[]) {
|
||||
const firstBlock = selectedModels[0];
|
||||
if (
|
||||
matchFlavours(firstBlock, ['affine:paragraph']) &&
|
||||
firstBlock.type.startsWith('h')
|
||||
) {
|
||||
return firstBlock.text.toString();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function promptDocTitle(std: BlockStdScope, autofill?: string) {
|
||||
const notification = std.getOptional(NotificationProvider);
|
||||
if (!notification) return Promise.resolve(undefined);
|
||||
|
||||
return notification.prompt({
|
||||
title: 'Create linked doc',
|
||||
message: 'Enter a title for the new doc.',
|
||||
placeholder: 'Untitled',
|
||||
autofill,
|
||||
confirmText: 'Confirm',
|
||||
cancelText: 'Cancel',
|
||||
});
|
||||
}
|
||||
|
||||
export function notifyDocCreated(std: BlockStdScope, doc: Doc) {
|
||||
const notification = std.getOptional(NotificationProvider);
|
||||
if (!notification) return;
|
||||
|
||||
const abortController = new AbortController();
|
||||
const clear = () => {
|
||||
doc.history.off('stack-item-added', addHandler);
|
||||
doc.history.off('stack-item-popped', popHandler);
|
||||
disposable.dispose();
|
||||
};
|
||||
const closeNotify = () => {
|
||||
abortController.abort();
|
||||
clear();
|
||||
};
|
||||
|
||||
// edit or undo or switch doc, close notify toast
|
||||
const addHandler = doc.history.on('stack-item-added', closeNotify);
|
||||
const popHandler = doc.history.on('stack-item-popped', closeNotify);
|
||||
const disposable = std.host.slots.unmounted.on(closeNotify);
|
||||
|
||||
notification.notify({
|
||||
title: 'Linked doc created',
|
||||
message: 'You can click undo to recovery block content',
|
||||
accent: 'info',
|
||||
duration: 10 * 1000,
|
||||
action: {
|
||||
label: 'Undo',
|
||||
onClick: () => {
|
||||
doc.undo();
|
||||
clear();
|
||||
},
|
||||
},
|
||||
abort: abortController.signal,
|
||||
onClose: clear,
|
||||
});
|
||||
}
|
||||
|
||||
export async function convertSelectedBlocksToLinkedDoc(
|
||||
std: BlockStdScope,
|
||||
doc: Doc,
|
||||
selectedModels: DraftModel[] | Promise<DraftModel[]>,
|
||||
docTitle?: string
|
||||
) {
|
||||
const models = await selectedModels;
|
||||
const slice = std.clipboard.sliceToSnapshot(Slice.fromModels(doc, models));
|
||||
if (!slice) {
|
||||
return;
|
||||
}
|
||||
const firstBlock = models[0];
|
||||
if (!firstBlock) {
|
||||
return;
|
||||
}
|
||||
// if title undefined, use the first heading block content as doc title
|
||||
const title = docTitle || getTitleFromSelectedModels(models);
|
||||
const linkedDoc = createLinkedDocFromSlice(std, doc, slice.content, title);
|
||||
// insert linked doc card
|
||||
doc.addSiblingBlocks(
|
||||
doc.getBlock(firstBlock.id)!.model,
|
||||
[
|
||||
{
|
||||
flavour: 'affine:embed-linked-doc',
|
||||
pageId: linkedDoc.id,
|
||||
},
|
||||
],
|
||||
'before'
|
||||
);
|
||||
// delete selected elements
|
||||
models.forEach(model => doc.deleteBlock(model));
|
||||
return linkedDoc;
|
||||
}
|
||||
|
||||
export function createLinkedDocFromSlice(
|
||||
std: BlockStdScope,
|
||||
doc: Doc,
|
||||
snapshots: BlockSnapshot[],
|
||||
docTitle?: string
|
||||
) {
|
||||
// const modelsWithChildren = (list:BlockModel[]):BlockModel[]=>list.flatMap(model=>[model,...modelsWithChildren(model.children)])
|
||||
const linkedDoc = doc.collection.createDoc({});
|
||||
linkedDoc.load(() => {
|
||||
const rootId = linkedDoc.addBlock('affine:page', {
|
||||
title: new doc.Text(docTitle),
|
||||
});
|
||||
linkedDoc.addBlock('affine:surface', {}, rootId);
|
||||
const noteId = linkedDoc.addBlock('affine:note', {}, rootId);
|
||||
snapshots.forEach(snapshot => {
|
||||
std.clipboard
|
||||
.pasteBlockSnapshot(snapshot, linkedDoc, noteId)
|
||||
.catch(console.error);
|
||||
});
|
||||
});
|
||||
return linkedDoc;
|
||||
}
|
||||
@@ -28,7 +28,7 @@ export {
|
||||
LinkPreviewer,
|
||||
type LinkPreviewResponseData,
|
||||
} from './common/link-previewer.js';
|
||||
export { getDocContentWithMaxLength } from './common/render-linked-doc';
|
||||
export * from './common/render-linked-doc';
|
||||
export { toEdgelessEmbedBlock } from './common/to-edgeless-embed-block';
|
||||
export * from './common/utils';
|
||||
export * from './embed-figma-block';
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@blocksuite/affine-block-note",
|
||||
"description": "Note block for BlockSuite.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test:unit": "nx vite:test --run --passWithNoTests",
|
||||
"test:unit:coverage": "nx vite:test --run --coverage",
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"keywords": [],
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-embed": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.1.75",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.1",
|
||||
"lit": "^3.2.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./effects": "./src/effects.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
]
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,52 @@
|
||||
import type { BlockComponent } from '@blocksuite/block-std';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
|
||||
import type { updateBlockType } from './commands/block-type';
|
||||
import type { dedentBlock } from './commands/dedent-block';
|
||||
import type { dedentBlockToRoot } from './commands/dedent-block-to-root';
|
||||
import type { dedentBlocks } from './commands/dedent-blocks';
|
||||
import type { dedentBlocksToRoot } from './commands/dedent-blocks-to-root';
|
||||
import type { focusBlockEnd } from './commands/focus-block-end';
|
||||
import type { focusBlockStart } from './commands/focus-block-start';
|
||||
import type { indentBlock } from './commands/indent-block';
|
||||
import type { indentBlocks } from './commands/indent-blocks';
|
||||
import type { selectBlock } from './commands/select-block';
|
||||
import type { selectBlocksBetween } from './commands/select-blocks-between';
|
||||
import { NoteBlockComponent } from './note-block';
|
||||
import {
|
||||
EdgelessNoteBlockComponent,
|
||||
EdgelessNoteMask,
|
||||
} from './note-edgeless-block';
|
||||
import type { NoteBlockService } from './note-service';
|
||||
|
||||
export function effects() {
|
||||
customElements.define('affine-note', NoteBlockComponent);
|
||||
customElements.define('edgeless-note-mask', EdgelessNoteMask);
|
||||
customElements.define('affine-edgeless-note', EdgelessNoteBlockComponent);
|
||||
}
|
||||
|
||||
declare global {
|
||||
namespace BlockSuite {
|
||||
interface Commands {
|
||||
selectBlock: typeof selectBlock;
|
||||
selectBlocksBetween: typeof selectBlocksBetween;
|
||||
focusBlockStart: typeof focusBlockStart;
|
||||
focusBlockEnd: typeof focusBlockEnd;
|
||||
indentBlocks: typeof indentBlocks;
|
||||
dedentBlock: typeof dedentBlock;
|
||||
dedentBlocksToRoot: typeof dedentBlocksToRoot;
|
||||
dedentBlocks: typeof dedentBlocks;
|
||||
indentBlock: typeof indentBlock;
|
||||
updateBlockType: typeof updateBlockType;
|
||||
dedentBlockToRoot: typeof dedentBlockToRoot;
|
||||
}
|
||||
interface CommandContext {
|
||||
focusBlock?: BlockComponent | null;
|
||||
anchorBlock?: BlockComponent | null;
|
||||
updatedBlocks?: BlockModel[];
|
||||
}
|
||||
interface BlockServices {
|
||||
'affine:note': NoteBlockService;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export * from './adapters';
|
||||
export * from './commands';
|
||||
export * from './note-block';
|
||||
export * from './note-edgeless-block';
|
||||
export * from './note-service';
|
||||
export * from './note-spec';
|
||||
File renamed without changes.
File renamed without changes.
+11
-7
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import { MoreIndicatorIcon } from '@blocksuite/affine-components/icons';
|
||||
import type { NoteBlockModel } from '@blocksuite/affine-model';
|
||||
import {
|
||||
@@ -14,7 +13,11 @@ import {
|
||||
matchFlavours,
|
||||
stopPropagation,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockComponent, EditorHost } from '@blocksuite/block-std';
|
||||
import type {
|
||||
BlockComponent,
|
||||
BlockService,
|
||||
EditorHost,
|
||||
} from '@blocksuite/block-std';
|
||||
import { ShadowlessElement, toGfxBlockComponent } from '@blocksuite/block-std';
|
||||
import {
|
||||
almostEqual,
|
||||
@@ -23,13 +26,12 @@ import {
|
||||
Point,
|
||||
WithDisposable,
|
||||
} from '@blocksuite/global/utils';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
import type { BlockModel, Slot } from '@blocksuite/store';
|
||||
import { css, html, nothing } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type { EdgelessRootService } from '../root-block/index.js';
|
||||
import { NoteBlockComponent } from './note-block.js';
|
||||
|
||||
export class EdgelessNoteMask extends WithDisposable(ShadowlessElement) {
|
||||
@@ -151,7 +153,9 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
}
|
||||
|
||||
get rootService() {
|
||||
return this.std.getService('affine:page') as EdgelessRootService;
|
||||
return this.std.getService('affine:page') as BlockService & {
|
||||
slots: Record<string, Slot>;
|
||||
};
|
||||
}
|
||||
|
||||
private _collapsedContent() {
|
||||
@@ -312,7 +316,7 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
const selection = this.rootService.selection;
|
||||
const selection = this.gfx.selection;
|
||||
|
||||
this._editing = selection.has(this.model.id) && selection.editing;
|
||||
this._disposables.add(
|
||||
@@ -328,7 +332,7 @@ export class EdgelessNoteBlockComponent extends toGfxBlockComponent(
|
||||
|
||||
override firstUpdated() {
|
||||
const { _disposables } = this;
|
||||
const selection = this.rootService.selection;
|
||||
const selection = this.gfx.selection;
|
||||
|
||||
_disposables.add(
|
||||
this.rootService.slots.elementResizeStart.on(() => {
|
||||
+25
-9
@@ -1,3 +1,4 @@
|
||||
import { textConversionConfigs } from '@blocksuite/affine-components/rich-text';
|
||||
import { NoteBlockSchema } from '@blocksuite/affine-model';
|
||||
import { matchFlavours } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
@@ -5,14 +6,14 @@ import {
|
||||
type BlockComponent,
|
||||
type BlockSelection,
|
||||
BlockService,
|
||||
type BlockStdScope,
|
||||
type UIEventHandler,
|
||||
type UIEventStateContext,
|
||||
} from '@blocksuite/block-std';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
|
||||
import { moveBlockConfigs } from '../_common/configs/move-block.js';
|
||||
import { quickActionConfig } from '../_common/configs/quick-action/config.js';
|
||||
import { textConversionConfigs } from '../_common/configs/text-conversion.js';
|
||||
import { onModelElementUpdated } from '../root-block/utils/callback.js';
|
||||
import { moveBlockConfigs } from './move-block';
|
||||
import { quickActionConfig } from './quick-action';
|
||||
|
||||
export class NoteBlockService extends BlockService {
|
||||
static override readonly flavour = NoteBlockSchema.model.flavour;
|
||||
@@ -51,10 +52,10 @@ export class NoteBlockService extends BlockService {
|
||||
return {
|
||||
...acc,
|
||||
[config.hotkey!]: ctx => {
|
||||
if (!config.showWhen(this.std.host)) return;
|
||||
if (!config.showWhen(this.std)) return;
|
||||
|
||||
ctx.get('defaultState').event.preventDefault();
|
||||
config.action(this.std.host);
|
||||
config.action(this.std);
|
||||
},
|
||||
};
|
||||
},
|
||||
@@ -83,8 +84,7 @@ export class NoteBlockService extends BlockService {
|
||||
})
|
||||
.inline((ctx, next) => {
|
||||
const newModels = ctx.updatedBlocks;
|
||||
const host = ctx.std.host;
|
||||
if (!host || !newModels) {
|
||||
if (!newModels) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export class NoteBlockService extends BlockService {
|
||||
}
|
||||
|
||||
const [codeModel] = newModels;
|
||||
onModelElementUpdated(host, codeModel, codeElement => {
|
||||
onModelElementUpdated(ctx.std, codeModel, codeElement => {
|
||||
this._std.selection.setGroup('note', [
|
||||
this._std.selection.create('text', {
|
||||
from: {
|
||||
@@ -584,3 +584,19 @@ export class NoteBlockService extends BlockService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function onModelElementUpdated(
|
||||
std: BlockStdScope,
|
||||
model: BlockModel,
|
||||
callback: (block: BlockComponent) => void
|
||||
) {
|
||||
const page = model.doc;
|
||||
if (!page.root) return;
|
||||
|
||||
const rootComponent = std.view.getBlock(page.root.id);
|
||||
if (!rootComponent) return;
|
||||
await rootComponent.updateComplete;
|
||||
|
||||
const element = std.view.getBlock(model.id);
|
||||
if (element) callback(element);
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
convertSelectedBlocksToLinkedDoc,
|
||||
getTitleFromSelectedModels,
|
||||
notifyDocCreated,
|
||||
promptDocTitle,
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import type { BlockStdScope } from '@blocksuite/block-std';
|
||||
|
||||
export interface QuickActionConfig {
|
||||
id: string;
|
||||
hotkey?: string;
|
||||
showWhen: (std: BlockStdScope) => boolean;
|
||||
action: (std: BlockStdScope) => void;
|
||||
}
|
||||
|
||||
export const quickActionConfig: QuickActionConfig[] = [
|
||||
{
|
||||
id: 'convert-to-linked-doc',
|
||||
hotkey: `Mod-Shift-l`,
|
||||
showWhen: std => {
|
||||
const [_, ctx] = std.command
|
||||
.chain()
|
||||
.getSelectedModels({
|
||||
types: ['block'],
|
||||
})
|
||||
.run();
|
||||
const { selectedModels } = ctx;
|
||||
return !!selectedModels && selectedModels.length > 0;
|
||||
},
|
||||
action: std => {
|
||||
const [_, ctx] = std.command
|
||||
.chain()
|
||||
.getSelectedModels({
|
||||
types: ['block'],
|
||||
mode: 'highest',
|
||||
})
|
||||
.draftSelectedModels()
|
||||
.run();
|
||||
const { selectedModels, draftedModels } = ctx;
|
||||
if (!selectedModels) return;
|
||||
|
||||
if (!selectedModels.length || !draftedModels) return;
|
||||
|
||||
std.selection.clear();
|
||||
|
||||
const doc = std.doc;
|
||||
const autofill = getTitleFromSelectedModels(selectedModels);
|
||||
promptDocTitle(std, autofill)
|
||||
.then(title => {
|
||||
if (title === null) return;
|
||||
convertSelectedBlocksToLinkedDoc(
|
||||
std,
|
||||
doc,
|
||||
draftedModels,
|
||||
title
|
||||
).catch(console.error);
|
||||
notifyDocCreated(std, doc);
|
||||
})
|
||||
.catch(console.error);
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src/",
|
||||
"outDir": "./dist/",
|
||||
"noEmit": false
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../framework/global"
|
||||
},
|
||||
{
|
||||
"path": "../../framework/store"
|
||||
},
|
||||
{
|
||||
"path": "../../framework/block-std"
|
||||
},
|
||||
{
|
||||
"path": "../../framework/inline"
|
||||
},
|
||||
{
|
||||
"path": "../model"
|
||||
},
|
||||
{
|
||||
"path": "../components"
|
||||
},
|
||||
{
|
||||
"path": "../shared"
|
||||
},
|
||||
{
|
||||
"path": "../block-embed"
|
||||
}
|
||||
]
|
||||
}
|
||||
+3
-2
@@ -1,3 +1,5 @@
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
import {
|
||||
BulletedListIcon,
|
||||
CheckBoxIcon,
|
||||
@@ -12,8 +14,7 @@ import {
|
||||
NumberedListIcon,
|
||||
QuoteIcon,
|
||||
TextIcon,
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import type { TemplateResult } from 'lit';
|
||||
} from '../icons';
|
||||
|
||||
/**
|
||||
* Text primitive entries used in slash menu and format bar,
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './all-extensions.js';
|
||||
export * from './all-extensions';
|
||||
export { type TextConversionConfig, textConversionConfigs } from './conversion';
|
||||
export {
|
||||
asyncGetRichText,
|
||||
asyncSetInlineRange,
|
||||
@@ -7,9 +8,9 @@ export {
|
||||
getRichTextByModel,
|
||||
onModelTextUpdated,
|
||||
selectTextModel,
|
||||
} from './dom.js';
|
||||
export * from './effects.js';
|
||||
export * from './extension/index.js';
|
||||
} from './dom';
|
||||
export * from './effects';
|
||||
export * from './extension';
|
||||
export {
|
||||
clearMarksOnDiscontinuousInput,
|
||||
FORMAT_BLOCK_SUPPORT_FLAVOURS,
|
||||
@@ -20,9 +21,9 @@ export {
|
||||
textCommands,
|
||||
type TextFormatConfig,
|
||||
textFormatConfigs,
|
||||
} from './format/index.js';
|
||||
export * from './inline/index.js';
|
||||
export { textKeymap } from './keymap/index.js';
|
||||
export { insertLinkedNode } from './linked-node.js';
|
||||
export { markdownInput } from './markdown/index.js';
|
||||
export { RichText } from './rich-text.js';
|
||||
} from './format';
|
||||
export * from './inline';
|
||||
export { textKeymap } from './keymap';
|
||||
export { insertLinkedNode } from './linked-node';
|
||||
export { markdownInput } from './markdown';
|
||||
export { RichText } from './rich-text';
|
||||
@@ -19,6 +19,7 @@
|
||||
"@blocksuite/affine-block-embed": "workspace:*",
|
||||
"@blocksuite/affine-block-image": "workspace:*",
|
||||
"@blocksuite/affine-block-list": "workspace:*",
|
||||
"@blocksuite/affine-block-note": "workspace:*",
|
||||
"@blocksuite/affine-block-paragraph": "workspace:*",
|
||||
"@blocksuite/affine-block-surface": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
import {
|
||||
CopyIcon,
|
||||
DatabaseTableViewIcon20,
|
||||
LinkedDocIcon,
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import { toast } from '@blocksuite/affine-components/toast';
|
||||
import { matchFlavours } from '@blocksuite/affine-shared/utils';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { tableViewMeta } from '@blocksuite/data-view/view-presets';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
import { convertToDatabase } from '../../../database-block/data-source.js';
|
||||
import { DATABASE_CONVERT_WHITE_LIST } from '../../../database-block/utils/block-utils.js';
|
||||
import {
|
||||
convertSelectedBlocksToLinkedDoc,
|
||||
getTitleFromSelectedModels,
|
||||
notifyDocCreated,
|
||||
promptDocTitle,
|
||||
} from '../../utils/render-linked-doc.js';
|
||||
|
||||
export interface QuickActionConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
disabledToolTip?: string;
|
||||
icon: TemplateResult<1>;
|
||||
hotkey?: string;
|
||||
showWhen: (host: EditorHost) => boolean;
|
||||
enabledWhen: (host: EditorHost) => boolean;
|
||||
action: (host: EditorHost) => void;
|
||||
}
|
||||
|
||||
export const quickActionConfig: QuickActionConfig[] = [
|
||||
{
|
||||
id: 'copy',
|
||||
name: 'Copy',
|
||||
disabledToolTip: undefined,
|
||||
icon: CopyIcon,
|
||||
hotkey: undefined,
|
||||
showWhen: () => true,
|
||||
enabledWhen: () => true,
|
||||
action: host => {
|
||||
host.std.command
|
||||
.chain()
|
||||
.getSelectedModels()
|
||||
.with({
|
||||
onCopy: () => {
|
||||
toast(host, 'Copied to clipboard');
|
||||
},
|
||||
})
|
||||
.draftSelectedModels()
|
||||
.copySelectedModels()
|
||||
.run();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'convert-to-database',
|
||||
name: 'Group as Table',
|
||||
disabledToolTip:
|
||||
'Contains Block types that cannot be converted to Database',
|
||||
icon: DatabaseTableViewIcon20,
|
||||
showWhen: host => {
|
||||
const [_, ctx] = host.std.command
|
||||
.chain()
|
||||
.getSelectedModels({
|
||||
types: ['block', 'text'],
|
||||
})
|
||||
.run();
|
||||
const { selectedModels } = ctx;
|
||||
if (!selectedModels || selectedModels.length === 0) return false;
|
||||
|
||||
const firstBlock = selectedModels[0];
|
||||
assertExists(firstBlock);
|
||||
if (matchFlavours(firstBlock, ['affine:database'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
enabledWhen: host => {
|
||||
const [_, ctx] = host.std.command
|
||||
.chain()
|
||||
.getSelectedModels({
|
||||
types: ['block', 'text'],
|
||||
})
|
||||
.run();
|
||||
const { selectedModels } = ctx;
|
||||
if (!selectedModels || selectedModels.length === 0) return false;
|
||||
|
||||
return selectedModels.every(block =>
|
||||
DATABASE_CONVERT_WHITE_LIST.includes(block.flavour)
|
||||
);
|
||||
},
|
||||
action: host => {
|
||||
convertToDatabase(host, tableViewMeta.type);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'convert-to-linked-doc',
|
||||
name: 'Create Linked Doc',
|
||||
icon: LinkedDocIcon,
|
||||
hotkey: `Mod-Shift-l`,
|
||||
showWhen: host => {
|
||||
const [_, ctx] = host.std.command
|
||||
.chain()
|
||||
.getSelectedModels({
|
||||
types: ['block'],
|
||||
})
|
||||
.run();
|
||||
const { selectedModels } = ctx;
|
||||
return !!selectedModels && selectedModels.length > 0;
|
||||
},
|
||||
enabledWhen: host => {
|
||||
const [_, ctx] = host.std.command
|
||||
.chain()
|
||||
.getSelectedModels({
|
||||
types: ['block'],
|
||||
})
|
||||
.run();
|
||||
const { selectedModels } = ctx;
|
||||
return !!selectedModels && selectedModels.length > 0;
|
||||
},
|
||||
action: host => {
|
||||
const [_, ctx] = host.std.command
|
||||
.chain()
|
||||
.getSelectedModels({
|
||||
types: ['block'],
|
||||
mode: 'highest',
|
||||
})
|
||||
.draftSelectedModels()
|
||||
.run();
|
||||
const { selectedModels, draftedModels } = ctx;
|
||||
assertExists(selectedModels);
|
||||
if (!selectedModels.length || !draftedModels) return;
|
||||
|
||||
host.selection.clear();
|
||||
|
||||
const doc = host.doc;
|
||||
const autofill = getTitleFromSelectedModels(selectedModels);
|
||||
promptDocTitle(host, autofill)
|
||||
.then(title => {
|
||||
if (title === null) return;
|
||||
convertSelectedBlocksToLinkedDoc(
|
||||
host.std,
|
||||
doc,
|
||||
draftedModels,
|
||||
title
|
||||
).catch(console.error);
|
||||
notifyDocCreated(host, doc);
|
||||
})
|
||||
.catch(console.error);
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,19 +1,9 @@
|
||||
import type { FrameBlockModel, NoteBlockModel } from '@blocksuite/affine-model';
|
||||
import { NoteDisplayMode } from '@blocksuite/affine-model';
|
||||
import {
|
||||
DocModeProvider,
|
||||
NotificationProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { getBlockProps, matchFlavours } from '@blocksuite/affine-shared/utils';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { getBlockProps } from '@blocksuite/affine-shared/utils';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import {
|
||||
type BlockModel,
|
||||
type BlockSnapshot,
|
||||
type Doc,
|
||||
type DraftModel,
|
||||
Slice,
|
||||
} from '@blocksuite/store';
|
||||
import { type BlockModel, type Doc } from '@blocksuite/store';
|
||||
|
||||
import { GfxBlockModel } from '../../root-block/edgeless/block-model.js';
|
||||
import {
|
||||
@@ -27,68 +17,6 @@ import {
|
||||
} from '../../root-block/edgeless/utils/query.js';
|
||||
import { getSurfaceBlock } from '../../surface-ref-block/utils.js';
|
||||
|
||||
export function promptDocTitle(host: EditorHost, autofill?: string) {
|
||||
const notification = host.std.getOptional(NotificationProvider);
|
||||
if (!notification) return Promise.resolve(undefined);
|
||||
|
||||
return notification.prompt({
|
||||
title: 'Create linked doc',
|
||||
message: 'Enter a title for the new doc.',
|
||||
placeholder: 'Untitled',
|
||||
autofill,
|
||||
confirmText: 'Confirm',
|
||||
cancelText: 'Cancel',
|
||||
});
|
||||
}
|
||||
|
||||
export function getTitleFromSelectedModels(selectedModels: DraftModel[]) {
|
||||
const firstBlock = selectedModels[0];
|
||||
if (
|
||||
matchFlavours(firstBlock, ['affine:paragraph']) &&
|
||||
firstBlock.type.startsWith('h')
|
||||
) {
|
||||
return firstBlock.text.toString();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function notifyDocCreated(host: EditorHost, doc: Doc) {
|
||||
const notification = host.std.getOptional(NotificationProvider);
|
||||
if (!notification) return;
|
||||
|
||||
const abortController = new AbortController();
|
||||
const clear = () => {
|
||||
doc.history.off('stack-item-added', addHandler);
|
||||
doc.history.off('stack-item-popped', popHandler);
|
||||
disposable.dispose();
|
||||
};
|
||||
const closeNotify = () => {
|
||||
abortController.abort();
|
||||
clear();
|
||||
};
|
||||
|
||||
// edit or undo or switch doc, close notify toast
|
||||
const addHandler = doc.history.on('stack-item-added', closeNotify);
|
||||
const popHandler = doc.history.on('stack-item-popped', closeNotify);
|
||||
const disposable = host.slots.unmounted.on(closeNotify);
|
||||
|
||||
notification.notify({
|
||||
title: 'Linked doc created',
|
||||
message: 'You can click undo to recovery block content',
|
||||
accent: 'info',
|
||||
duration: 10 * 1000,
|
||||
action: {
|
||||
label: 'Undo',
|
||||
onClick: () => {
|
||||
doc.undo();
|
||||
clear();
|
||||
},
|
||||
},
|
||||
abort: abortController.signal,
|
||||
onClose: clear,
|
||||
});
|
||||
}
|
||||
|
||||
export function addBlocksToDoc(
|
||||
targetDoc: Doc,
|
||||
model: BlockModel,
|
||||
@@ -110,61 +38,6 @@ export function addBlocksToDoc(
|
||||
}
|
||||
}
|
||||
|
||||
export async function convertSelectedBlocksToLinkedDoc(
|
||||
std: BlockSuite.Std,
|
||||
doc: Doc,
|
||||
selectedModels: DraftModel[] | Promise<DraftModel[]>,
|
||||
docTitle?: string
|
||||
) {
|
||||
const models = await selectedModels;
|
||||
const slice = std.clipboard.sliceToSnapshot(Slice.fromModels(doc, models));
|
||||
if (!slice) {
|
||||
return;
|
||||
}
|
||||
const firstBlock = models[0];
|
||||
assertExists(firstBlock);
|
||||
// if title undefined, use the first heading block content as doc title
|
||||
const title = docTitle || getTitleFromSelectedModels(models);
|
||||
const linkedDoc = createLinkedDocFromSlice(std, doc, slice.content, title);
|
||||
// insert linked doc card
|
||||
doc.addSiblingBlocks(
|
||||
doc.getBlock(firstBlock.id)!.model,
|
||||
[
|
||||
{
|
||||
flavour: 'affine:embed-linked-doc',
|
||||
pageId: linkedDoc.id,
|
||||
},
|
||||
],
|
||||
'before'
|
||||
);
|
||||
// delete selected elements
|
||||
models.forEach(model => doc.deleteBlock(model));
|
||||
return linkedDoc;
|
||||
}
|
||||
|
||||
export function createLinkedDocFromSlice(
|
||||
std: BlockSuite.Std,
|
||||
doc: Doc,
|
||||
snapshots: BlockSnapshot[],
|
||||
docTitle?: string
|
||||
) {
|
||||
// const modelsWithChildren = (list:BlockModel[]):BlockModel[]=>list.flatMap(model=>[model,...modelsWithChildren(model.children)])
|
||||
const linkedDoc = doc.collection.createDoc({});
|
||||
linkedDoc.load(() => {
|
||||
const rootId = linkedDoc.addBlock('affine:page', {
|
||||
title: new doc.Text(docTitle),
|
||||
});
|
||||
linkedDoc.addBlock('affine:surface', {}, rootId);
|
||||
const noteId = linkedDoc.addBlock('affine:note', {}, rootId);
|
||||
snapshots.forEach(snapshot => {
|
||||
std.clipboard
|
||||
.pasteBlockSnapshot(snapshot, linkedDoc, noteId)
|
||||
.catch(console.error);
|
||||
});
|
||||
});
|
||||
return linkedDoc;
|
||||
}
|
||||
|
||||
export function createLinkedDocFromNote(
|
||||
doc: Doc,
|
||||
note: NoteBlockModel,
|
||||
|
||||
@@ -3,6 +3,10 @@ import { BookmarkBlockSpec } from '@blocksuite/affine-block-bookmark';
|
||||
import { EmbedExtensions } from '@blocksuite/affine-block-embed';
|
||||
import { ImageBlockSpec } from '@blocksuite/affine-block-image';
|
||||
import { ListBlockSpec } from '@blocksuite/affine-block-list';
|
||||
import {
|
||||
EdgelessNoteBlockSpec,
|
||||
NoteBlockSpec,
|
||||
} from '@blocksuite/affine-block-note';
|
||||
import { ParagraphBlockSpec } from '@blocksuite/affine-block-paragraph';
|
||||
import { RichTextExtensions } from '@blocksuite/affine-components/rich-text';
|
||||
import { EditPropsStore } from '@blocksuite/affine-shared/services';
|
||||
@@ -13,10 +17,6 @@ import { CodeBlockSpec } from '../code-block/code-block-spec.js';
|
||||
import { DataViewBlockSpec } from '../data-view-block/data-view-spec.js';
|
||||
import { DatabaseBlockSpec } from '../database-block/database-spec.js';
|
||||
import { DividerBlockSpec } from '../divider-block/divider-spec.js';
|
||||
import {
|
||||
EdgelessNoteBlockSpec,
|
||||
NoteBlockSpec,
|
||||
} from '../note-block/note-spec.js';
|
||||
|
||||
export const CommonFirstPartyBlockSpecs: ExtensionType[] = [
|
||||
RichTextExtensions,
|
||||
|
||||
@@ -11,16 +11,16 @@ import {
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import { ImageBlockSpec } from '@blocksuite/affine-block-image';
|
||||
import { ListBlockSpec } from '@blocksuite/affine-block-list';
|
||||
import {
|
||||
EdgelessNoteBlockSpec,
|
||||
NoteBlockSpec,
|
||||
} from '@blocksuite/affine-block-note';
|
||||
import { ParagraphBlockSpec } from '@blocksuite/affine-block-paragraph';
|
||||
|
||||
import { CodeBlockSpec } from '../../code-block/code-block-spec.js';
|
||||
import { DataViewBlockSpec } from '../../data-view-block/data-view-spec.js';
|
||||
import { DatabaseBlockSpec } from '../../database-block/database-spec.js';
|
||||
import { DividerBlockSpec } from '../../divider-block/divider-spec.js';
|
||||
import {
|
||||
EdgelessNoteBlockSpec,
|
||||
NoteBlockSpec,
|
||||
} from '../../note-block/note-spec.js';
|
||||
|
||||
export {
|
||||
AttachmentBlockSpec,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { NoteBlockComponent } from '@blocksuite/affine-block-note';
|
||||
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
|
||||
import {
|
||||
menu,
|
||||
@@ -40,7 +41,6 @@ import { html } from 'lit/static-html.js';
|
||||
|
||||
import { BlockRenderer } from '../database-block/detail-panel/block-renderer.js';
|
||||
import { NoteRenderer } from '../database-block/detail-panel/note-renderer.js';
|
||||
import type { NoteBlockComponent } from '../note-block/index.js';
|
||||
import {
|
||||
EdgelessRootBlockComponent,
|
||||
type RootService,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { NoteBlockComponent } from '@blocksuite/affine-block-note';
|
||||
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
|
||||
import {
|
||||
menu,
|
||||
@@ -44,7 +45,6 @@ import { autoUpdate } from '@floating-ui/dom';
|
||||
import { computed, signal } from '@preact/signals-core';
|
||||
import { css, html, nothing, unsafeCSS } from 'lit';
|
||||
|
||||
import type { NoteBlockComponent } from '../note-block/index.js';
|
||||
import { EdgelessRootBlockComponent } from '../root-block/index.js';
|
||||
import { getDropResult } from '../root-block/widgets/drag-handle/utils.js';
|
||||
import { popSideDetail } from './components/layout.js';
|
||||
|
||||
@@ -3,6 +3,7 @@ import { effects as blockBookmarkEffects } from '@blocksuite/affine-block-bookma
|
||||
import { effects as blockEmbedEffects } from '@blocksuite/affine-block-embed/effects';
|
||||
import { effects as blockImageEffects } from '@blocksuite/affine-block-image/effects';
|
||||
import { effects as blockListEffects } from '@blocksuite/affine-block-list/effects';
|
||||
import { effects as blockNoteEffects } from '@blocksuite/affine-block-note/effects';
|
||||
import { effects as blockParagraphEffects } from '@blocksuite/affine-block-paragraph/effects';
|
||||
import { effects as blockSurfaceEffects } from '@blocksuite/affine-block-surface/effects';
|
||||
import { effects as componentAiItemEffects } from '@blocksuite/affine-components/ai-item';
|
||||
@@ -66,23 +67,6 @@ import { EdgelessTextBlockComponent } from './edgeless-text-block/index.js';
|
||||
import { FrameBlockComponent } from './frame-block/index.js';
|
||||
import { effects as blockLatexEffects } from './latex-block/effects.js';
|
||||
import { LatexBlockComponent } from './latex-block/index.js';
|
||||
import type { updateBlockType } from './note-block/commands/block-type.js';
|
||||
import type { dedentBlock } from './note-block/commands/dedent-block.js';
|
||||
import type { dedentBlockToRoot } from './note-block/commands/dedent-block-to-root.js';
|
||||
import type { dedentBlocks } from './note-block/commands/dedent-blocks.js';
|
||||
import type { dedentBlocksToRoot } from './note-block/commands/dedent-blocks-to-root.js';
|
||||
import type { focusBlockEnd } from './note-block/commands/focus-block-end.js';
|
||||
import type { focusBlockStart } from './note-block/commands/focus-block-start.js';
|
||||
import type { indentBlock } from './note-block/commands/indent-block.js';
|
||||
import type { indentBlocks } from './note-block/commands/indent-blocks.js';
|
||||
import type { selectBlock } from './note-block/commands/select-block.js';
|
||||
import type { selectBlocksBetween } from './note-block/commands/select-blocks-between.js';
|
||||
import {
|
||||
EdgelessNoteBlockComponent,
|
||||
EdgelessNoteMask,
|
||||
NoteBlockComponent,
|
||||
type NoteBlockService,
|
||||
} from './note-block/index.js';
|
||||
import { EdgelessAutoCompletePanel } from './root-block/edgeless/components/auto-complete/auto-complete-panel.js';
|
||||
import { EdgelessAutoComplete } from './root-block/edgeless/components/auto-complete/edgeless-auto-complete.js';
|
||||
import { EdgelessToolIconButton } from './root-block/edgeless/components/buttons/tool-icon-button.js';
|
||||
@@ -264,6 +248,7 @@ export function effects() {
|
||||
stdEffects();
|
||||
inlineEffects();
|
||||
|
||||
blockNoteEffects();
|
||||
blockAttachmentEffects();
|
||||
blockBookmarkEffects();
|
||||
blockListEffects();
|
||||
@@ -314,8 +299,6 @@ export function effects() {
|
||||
customElements.define('database-datasource-block-renderer', BlockRenderer);
|
||||
customElements.define('affine-latex', LatexBlockComponent);
|
||||
customElements.define('affine-page-root', PageRootBlockComponent);
|
||||
customElements.define('edgeless-note-mask', EdgelessNoteMask);
|
||||
customElements.define('affine-edgeless-note', EdgelessNoteBlockComponent);
|
||||
customElements.define('affine-preview-root', PreviewRootBlockComponent);
|
||||
customElements.define('affine-code', CodeBlockComponent);
|
||||
customElements.define('mini-mindmap-preview', MiniMindmapPreview);
|
||||
@@ -445,7 +428,6 @@ export function effects() {
|
||||
customElements.define('edgeless-present-button', EdgelessPresentButton);
|
||||
customElements.define('edgeless-color-picker', EdgelessColorPicker);
|
||||
customElements.define('overlay-scrollbar', OverlayScrollbar);
|
||||
customElements.define('affine-note', NoteBlockComponent);
|
||||
customElements.define('affine-template-loading', AffineTemplateLoading);
|
||||
customElements.define(
|
||||
'edgeless-color-picker-button',
|
||||
@@ -549,18 +531,7 @@ export function effects() {
|
||||
declare global {
|
||||
namespace BlockSuite {
|
||||
interface Commands {
|
||||
selectBlock: typeof selectBlock;
|
||||
selectBlocksBetween: typeof selectBlocksBetween;
|
||||
focusBlockStart: typeof focusBlockStart;
|
||||
focusBlockEnd: typeof focusBlockEnd;
|
||||
indentBlocks: typeof indentBlocks;
|
||||
dedentBlock: typeof dedentBlock;
|
||||
dedentBlocksToRoot: typeof dedentBlocksToRoot;
|
||||
dedentBlocks: typeof dedentBlocks;
|
||||
indentBlock: typeof indentBlock;
|
||||
updateBlockType: typeof updateBlockType;
|
||||
insertEdgelessText: typeof insertEdgelessTextCommand;
|
||||
dedentBlockToRoot: typeof dedentBlockToRoot;
|
||||
}
|
||||
interface CommandContext {
|
||||
focusBlock?: BlockComponent | null;
|
||||
@@ -573,7 +544,6 @@ declare global {
|
||||
'affine:page': RootBlockConfig;
|
||||
}
|
||||
interface BlockServices {
|
||||
'affine:note': NoteBlockService;
|
||||
'affine:page': RootService;
|
||||
'affine:database': DatabaseBlockService;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ export * from './divider-block/index.js';
|
||||
export * from './edgeless-text-block/index.js';
|
||||
export * from './frame-block/index.js';
|
||||
export * from './latex-block/index.js';
|
||||
export * from './note-block/index.js';
|
||||
export { EdgelessTemplatePanel } from './root-block/edgeless/components/toolbar/template/template-panel.js';
|
||||
export type {
|
||||
Template,
|
||||
@@ -52,6 +51,7 @@ export * from '@blocksuite/affine-block-bookmark';
|
||||
export * from '@blocksuite/affine-block-embed';
|
||||
export * from '@blocksuite/affine-block-image';
|
||||
export * from '@blocksuite/affine-block-list';
|
||||
export * from '@blocksuite/affine-block-note';
|
||||
export * from '@blocksuite/affine-block-paragraph';
|
||||
export * from '@blocksuite/affine-block-surface';
|
||||
export {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export * from './commands/index.js';
|
||||
export * from './note-block.js';
|
||||
export * from './note-edgeless-block.js';
|
||||
export * from './note-service.js';
|
||||
@@ -1,14 +1,13 @@
|
||||
import { matchFlavours } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockComponent, BlockSelection } from '@blocksuite/block-std';
|
||||
import { IS_MAC, IS_WINDOWS } from '@blocksuite/global/env';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
|
||||
import {
|
||||
convertSelectedBlocksToLinkedDoc,
|
||||
getTitleFromSelectedModels,
|
||||
notifyDocCreated,
|
||||
promptDocTitle,
|
||||
} from '../../_common/utils/render-linked-doc.js';
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import { matchFlavours } from '@blocksuite/affine-shared/utils';
|
||||
import type { BlockComponent, BlockSelection } from '@blocksuite/block-std';
|
||||
import { IS_MAC, IS_WINDOWS } from '@blocksuite/global/env';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
|
||||
export class PageKeyboardManager {
|
||||
private readonly _handleDelete = () => {
|
||||
@@ -117,7 +116,7 @@ export class PageKeyboardManager {
|
||||
|
||||
const doc = rootComponent.host.doc;
|
||||
const autofill = getTitleFromSelectedModels(selectedModels);
|
||||
promptDocTitle(rootComponent.host, autofill)
|
||||
promptDocTitle(rootComponent.std, autofill)
|
||||
.then(title => {
|
||||
if (title === null) return;
|
||||
convertSelectedBlocksToLinkedDoc(
|
||||
@@ -126,7 +125,7 @@ export class PageKeyboardManager {
|
||||
draftedModels,
|
||||
title
|
||||
).catch(console.error);
|
||||
notifyDocCreated(rootComponent.host, doc);
|
||||
notifyDocCreated(rootComponent.std, doc);
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,3 +1,4 @@
|
||||
import type { NoteBlockComponent } from '@blocksuite/affine-block-note';
|
||||
import { captureEventTarget } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
BLOCK_ID_ATTR,
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
import { Point, throttle } from '@blocksuite/global/utils';
|
||||
import { computed } from '@preact/signals-core';
|
||||
|
||||
import type { NoteBlockComponent } from '../../../../note-block/index.js';
|
||||
import type { EdgelessRootBlockComponent } from '../../../edgeless/index.js';
|
||||
import {
|
||||
DRAG_HANDLE_CONTAINER_WIDTH,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { AttachmentBlockComponent } from '@blocksuite/affine-block-attachment';
|
||||
import type { BookmarkBlockComponent } from '@blocksuite/affine-block-bookmark';
|
||||
import type {
|
||||
EmbedFigmaBlockComponent,
|
||||
EmbedGithubBlockComponent,
|
||||
EmbedLoomBlockComponent,
|
||||
EmbedYoutubeBlockComponent,
|
||||
import {
|
||||
type EmbedFigmaBlockComponent,
|
||||
type EmbedGithubBlockComponent,
|
||||
type EmbedLoomBlockComponent,
|
||||
type EmbedYoutubeBlockComponent,
|
||||
notifyDocCreated,
|
||||
promptDocTitle,
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import type { ImageBlockComponent } from '@blocksuite/affine-block-image';
|
||||
import { isPeekable, peek } from '@blocksuite/affine-components/peek';
|
||||
@@ -30,8 +32,6 @@ import {
|
||||
import {
|
||||
createLinkedDocFromEdgelessElements,
|
||||
createLinkedDocFromNote,
|
||||
notifyDocCreated,
|
||||
promptDocTitle,
|
||||
} from '../../../../_common/utils/render-linked-doc.js';
|
||||
import { duplicate } from '../../../edgeless/utils/clipboard-utils.js';
|
||||
import { getSortedCloneElements } from '../../../edgeless/utils/clone-utils.js';
|
||||
@@ -245,11 +245,11 @@ export const conversionsGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
|
||||
label: 'Turn into linked doc',
|
||||
type: 'turn-into-linked-doc',
|
||||
action: async ctx => {
|
||||
const { doc, service, surface, host, std } = ctx;
|
||||
const { doc, service, surface, std } = ctx;
|
||||
const element = ctx.getNoteBlock();
|
||||
if (!element) return;
|
||||
|
||||
const title = await promptDocTitle(host);
|
||||
const title = await promptDocTitle(std);
|
||||
if (title === null) return;
|
||||
|
||||
const linkedDoc = createLinkedDocFromNote(doc, element, title);
|
||||
@@ -309,7 +309,7 @@ export const conversionsGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
|
||||
host,
|
||||
std,
|
||||
}) => {
|
||||
const title = await promptDocTitle(host);
|
||||
const title = await promptDocTitle(std);
|
||||
if (title === null) return;
|
||||
|
||||
const elements = getSortedCloneElements(selection.selectedElements);
|
||||
@@ -360,7 +360,7 @@ export const conversionsGroup: MenuItemGroup<ElementToolbarMoreMenuContext> = {
|
||||
other: 'new doc',
|
||||
});
|
||||
|
||||
notifyDocCreated(host, doc);
|
||||
notifyDocCreated(std, doc);
|
||||
},
|
||||
when: ctx => !(ctx.getLinkedDocBlock() || ctx.getNoteBlock()),
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { whenHover } from '@blocksuite/affine-components/hover';
|
||||
import { ArrowDownIcon } from '@blocksuite/affine-components/icons';
|
||||
import { textConversionConfigs } from '@blocksuite/affine-components/rich-text';
|
||||
import type { ParagraphBlockModel } from '@blocksuite/affine-model';
|
||||
import type { EditorHost } from '@blocksuite/block-std';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
@@ -8,7 +9,6 @@ import { html } from 'lit';
|
||||
import { ref, type RefOrCallback } from 'lit/directives/ref.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import { textConversionConfigs } from '../../../../_common/configs/text-conversion.js';
|
||||
import type { ParagraphActionConfigItem } from '../config.js';
|
||||
import type { AffineFormatBarWidget } from '../format-bar.js';
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import {
|
||||
convertSelectedBlocksToLinkedDoc,
|
||||
getTitleFromSelectedModels,
|
||||
notifyDocCreated,
|
||||
promptDocTitle,
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import {
|
||||
BoldIcon,
|
||||
BulletedListIcon,
|
||||
@@ -37,12 +43,6 @@ import { assertExists } from '@blocksuite/global/utils';
|
||||
import { Slice } from '@blocksuite/store';
|
||||
import { html, type TemplateResult } from 'lit';
|
||||
|
||||
import {
|
||||
convertSelectedBlocksToLinkedDoc,
|
||||
getTitleFromSelectedModels,
|
||||
notifyDocCreated,
|
||||
promptDocTitle,
|
||||
} from '../../../_common/utils/render-linked-doc.js';
|
||||
import { convertToDatabase } from '../../../database-block/data-source.js';
|
||||
import { DATABASE_CONVERT_WHITE_LIST } from '../../../database-block/utils/block-utils.js';
|
||||
import { FormatBarContext } from './context.js';
|
||||
@@ -201,7 +201,7 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) {
|
||||
})
|
||||
.draftSelectedModels()
|
||||
.run();
|
||||
const { draftedModels, selectedModels } = ctx;
|
||||
const { draftedModels, selectedModels, std } = ctx;
|
||||
if (!selectedModels?.length || !draftedModels) return;
|
||||
|
||||
const host = formatBar.host;
|
||||
@@ -209,16 +209,16 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) {
|
||||
|
||||
const doc = host.doc;
|
||||
const autofill = getTitleFromSelectedModels(selectedModels);
|
||||
promptDocTitle(host, autofill)
|
||||
promptDocTitle(std, autofill)
|
||||
.then(async title => {
|
||||
if (title === null) return;
|
||||
await convertSelectedBlocksToLinkedDoc(
|
||||
host.std,
|
||||
std,
|
||||
doc,
|
||||
draftedModels,
|
||||
title
|
||||
);
|
||||
notifyDocCreated(host, doc);
|
||||
notifyDocCreated(std, doc);
|
||||
host.std.getOptional(TelemetryProvider)?.track('DocCreated', {
|
||||
control: 'create linked doc',
|
||||
page: 'doc editor',
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
getInlineEditorByModel,
|
||||
insertContent,
|
||||
REFERENCE_NODE,
|
||||
textConversionConfigs,
|
||||
textFormatConfigs,
|
||||
} from '@blocksuite/affine-components/rich-text';
|
||||
import { toast } from '@blocksuite/affine-components/toast';
|
||||
@@ -49,7 +50,6 @@ import { Slice, Text } from '@blocksuite/store';
|
||||
import type { TemplateResult } from 'lit';
|
||||
|
||||
import { toggleEmbedCardCreateModal } from '../../../_common/components/embed-card/modal/embed-card-create-modal.js';
|
||||
import { textConversionConfigs } from '../../../_common/configs/text-conversion.js';
|
||||
import type { DataViewBlockComponent } from '../../../data-view-block/index.js';
|
||||
import { getSurfaceBlock } from '../../../surface-ref-block/utils.js';
|
||||
import type { RootBlockComponent } from '../../types.js';
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { TextFormatConfig } from '@blocksuite/affine-components/rich-text';
|
||||
import type {
|
||||
TextConversionConfig,
|
||||
TextFormatConfig,
|
||||
} from '@blocksuite/affine-components/rich-text';
|
||||
import { isInsideBlockByFlavour } from '@blocksuite/affine-shared/utils';
|
||||
import { assertType } from '@blocksuite/global/utils';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
|
||||
import type { TextConversionConfig } from '../../../_common/configs/text-conversion.js';
|
||||
import type {
|
||||
SlashMenuActionItem,
|
||||
SlashMenuContext,
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
{
|
||||
"path": "../affine/block-embed"
|
||||
},
|
||||
{
|
||||
"path": "../affine/block-note"
|
||||
},
|
||||
{
|
||||
"path": "../affine/block-bookmark"
|
||||
},
|
||||
|
||||
@@ -81,6 +81,20 @@ export const PackageList = [
|
||||
'blocksuite/framework/store',
|
||||
],
|
||||
},
|
||||
{
|
||||
location: 'blocksuite/affine/block-note',
|
||||
name: '@blocksuite/affine-block-note',
|
||||
workspaceDependencies: [
|
||||
'blocksuite/affine/block-embed',
|
||||
'blocksuite/affine/components',
|
||||
'blocksuite/affine/model',
|
||||
'blocksuite/affine/shared',
|
||||
'blocksuite/framework/block-std',
|
||||
'blocksuite/framework/global',
|
||||
'blocksuite/framework/inline',
|
||||
'blocksuite/framework/store',
|
||||
],
|
||||
},
|
||||
{
|
||||
location: 'blocksuite/affine/block-paragraph',
|
||||
name: '@blocksuite/affine-block-paragraph',
|
||||
@@ -170,6 +184,7 @@ export const PackageList = [
|
||||
'blocksuite/affine/block-embed',
|
||||
'blocksuite/affine/block-image',
|
||||
'blocksuite/affine/block-list',
|
||||
'blocksuite/affine/block-note',
|
||||
'blocksuite/affine/block-paragraph',
|
||||
'blocksuite/affine/block-surface',
|
||||
'blocksuite/affine/components',
|
||||
@@ -506,6 +521,7 @@ export type PackageName =
|
||||
| '@blocksuite/affine-block-embed'
|
||||
| '@blocksuite/affine-block-image'
|
||||
| '@blocksuite/affine-block-list'
|
||||
| '@blocksuite/affine-block-note'
|
||||
| '@blocksuite/affine-block-paragraph'
|
||||
| '@blocksuite/affine-block-surface'
|
||||
| '@blocksuite/affine-components'
|
||||
|
||||
@@ -3340,6 +3340,29 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@blocksuite/affine-block-note@workspace:*, @blocksuite/affine-block-note@workspace:blocksuite/affine/block-note":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@blocksuite/affine-block-note@workspace:blocksuite/affine/block-note"
|
||||
dependencies:
|
||||
"@blocksuite/affine-block-embed": "workspace:*"
|
||||
"@blocksuite/affine-components": "workspace:*"
|
||||
"@blocksuite/affine-model": "workspace:*"
|
||||
"@blocksuite/affine-shared": "workspace:*"
|
||||
"@blocksuite/block-std": "workspace:*"
|
||||
"@blocksuite/global": "workspace:*"
|
||||
"@blocksuite/icons": "npm:^2.1.75"
|
||||
"@blocksuite/inline": "workspace:*"
|
||||
"@blocksuite/store": "workspace:*"
|
||||
"@floating-ui/dom": "npm:^1.6.10"
|
||||
"@lit/context": "npm:^1.1.2"
|
||||
"@preact/signals-core": "npm:^1.8.0"
|
||||
"@toeverything/theme": "npm:^1.1.1"
|
||||
lit: "npm:^3.2.0"
|
||||
minimatch: "npm:^10.0.1"
|
||||
zod: "npm:^3.23.8"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@blocksuite/affine-block-paragraph@workspace:*, @blocksuite/affine-block-paragraph@workspace:blocksuite/affine/block-paragraph":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@blocksuite/affine-block-paragraph@workspace:blocksuite/affine/block-paragraph"
|
||||
@@ -3511,6 +3534,7 @@ __metadata:
|
||||
"@blocksuite/affine-block-embed": "workspace:*"
|
||||
"@blocksuite/affine-block-image": "workspace:*"
|
||||
"@blocksuite/affine-block-list": "workspace:*"
|
||||
"@blocksuite/affine-block-note": "workspace:*"
|
||||
"@blocksuite/affine-block-paragraph": "workspace:*"
|
||||
"@blocksuite/affine-block-surface": "workspace:*"
|
||||
"@blocksuite/affine-components": "workspace:*"
|
||||
|
||||
Reference in new issue
Block a user