mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-24 09:52:49 +08:00
feat(editor): add callout block (#10563)
- Add `CalloutBlockModel ` - Implement `CalloutBlockComponent ` - Integrate with slash menu (/)
This commit is contained in:
45
blocksuite/affine/block-callout/package.json
Normal file
45
blocksuite/affine/block-callout/package.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@blocksuite/affine-block-callout",
|
||||
"description": "Callout 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-components": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/block-std": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/inline": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@floating-ui/dom": "^1.6.10",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"@types/mdast": "^4.0.4",
|
||||
"emoji-mart": "^5.6.0",
|
||||
"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__"
|
||||
],
|
||||
"version": "0.20.0"
|
||||
}
|
||||
121
blocksuite/affine/block-callout/src/callout-block.ts
Normal file
121
blocksuite/affine/block-callout/src/callout-block.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
|
||||
import { createLitPortal } from '@blocksuite/affine-components/portal';
|
||||
import { DefaultInlineManagerExtension } from '@blocksuite/affine-components/rich-text';
|
||||
import { type CalloutBlockModel } from '@blocksuite/affine-model';
|
||||
import { NOTE_SELECTOR } from '@blocksuite/affine-shared/consts';
|
||||
import {
|
||||
DocModeProvider,
|
||||
ThemeProvider,
|
||||
} from '@blocksuite/affine-shared/services';
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import type { BlockComponent } from '@blocksuite/block-std';
|
||||
import { flip, offset } from '@floating-ui/dom';
|
||||
import { css, html } from 'lit';
|
||||
import { query } from 'lit/decorators.js';
|
||||
|
||||
export class CalloutBlockComponent extends CaptionedBlockComponent<CalloutBlockModel> {
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.affine-callout-block-container {
|
||||
display: flex;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
background-color: ${unsafeCSSVarV2('block/callout/background/grey')};
|
||||
}
|
||||
|
||||
.affine-callout-emoji-container {
|
||||
margin-right: 12px;
|
||||
margin-top: 10px;
|
||||
user-select: none;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.affine-callout-emoji:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.affine-callout-children {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
private _emojiMenuAbortController: AbortController | null = null;
|
||||
private readonly _toggleEmojiMenu = () => {
|
||||
if (this._emojiMenuAbortController) {
|
||||
this._emojiMenuAbortController.abort();
|
||||
}
|
||||
this._emojiMenuAbortController = new AbortController();
|
||||
|
||||
const theme = this.std.get(ThemeProvider).theme$.value;
|
||||
|
||||
createLitPortal({
|
||||
template: html`<affine-emoji-menu
|
||||
.theme=${theme}
|
||||
.onEmojiSelect=${(data: any) => {
|
||||
this.model.emoji = data.native;
|
||||
console.log(data);
|
||||
}}
|
||||
></affine-emoji-menu>`,
|
||||
portalStyles: {
|
||||
zIndex: 'var(--affine-z-index-popover)',
|
||||
},
|
||||
container: this.host,
|
||||
computePosition: {
|
||||
referenceElement: this._emojiButton,
|
||||
placement: 'bottom-start',
|
||||
middleware: [flip(), offset(4)],
|
||||
autoUpdate: { animationFrame: true },
|
||||
},
|
||||
abortController: this._emojiMenuAbortController,
|
||||
closeOnClickAway: true,
|
||||
});
|
||||
};
|
||||
|
||||
get attributeRenderer() {
|
||||
return this.inlineManager.getRenderer();
|
||||
}
|
||||
|
||||
get attributesSchema() {
|
||||
return this.inlineManager.getSchema();
|
||||
}
|
||||
|
||||
get embedChecker() {
|
||||
return this.inlineManager.embedChecker;
|
||||
}
|
||||
|
||||
get inlineManager() {
|
||||
return this.std.get(DefaultInlineManagerExtension.identifier);
|
||||
}
|
||||
|
||||
@query('.affine-callout-emoji')
|
||||
private accessor _emojiButton!: HTMLElement;
|
||||
|
||||
override get topContenteditableElement() {
|
||||
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
||||
return this.closest<BlockComponent>(NOTE_SELECTOR);
|
||||
}
|
||||
return this.rootComponent;
|
||||
}
|
||||
|
||||
override renderBlock() {
|
||||
return html`
|
||||
<div class="affine-callout-block-container">
|
||||
<div
|
||||
@click=${this._toggleEmojiMenu}
|
||||
contenteditable="false"
|
||||
class="affine-callout-emoji-container"
|
||||
>
|
||||
<span class="affine-callout-emoji">${this.model.emoji}</span>
|
||||
</div>
|
||||
<div class="affine-callout-children">
|
||||
${this.renderChildren(this.model)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
8
blocksuite/affine/block-callout/src/callout-spec.ts
Normal file
8
blocksuite/affine/block-callout/src/callout-spec.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
export const CalloutBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:callout'),
|
||||
BlockViewExtension('affine:callout', literal`affine-callout`),
|
||||
];
|
||||
14
blocksuite/affine/block-callout/src/effects.ts
Normal file
14
blocksuite/affine/block-callout/src/effects.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { CalloutBlockComponent } from './callout-block';
|
||||
import { EmojiMenu } from './emoji-menu';
|
||||
|
||||
export function effects() {
|
||||
customElements.define('affine-callout', CalloutBlockComponent);
|
||||
customElements.define('affine-emoji-menu', EmojiMenu);
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'affine-callout': CalloutBlockComponent;
|
||||
'affine-emoji-menu': EmojiMenu;
|
||||
}
|
||||
}
|
||||
34
blocksuite/affine/block-callout/src/emoji-menu.ts
Normal file
34
blocksuite/affine/block-callout/src/emoji-menu.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import data from '@emoji-mart/data';
|
||||
import { Picker } from 'emoji-mart';
|
||||
import { html, LitElement, type PropertyValues } from 'lit';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
|
||||
export class EmojiMenu extends WithDisposable(LitElement) {
|
||||
override firstUpdated(props: PropertyValues) {
|
||||
const result = super.firstUpdated(props);
|
||||
|
||||
const picker = new Picker({
|
||||
data,
|
||||
onEmojiSelect: this.onEmojiSelect,
|
||||
autoFocus: true,
|
||||
theme: this.theme,
|
||||
});
|
||||
this.emojiMenu.append(picker as unknown as Node);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onEmojiSelect: (data: any) => void = () => {};
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor theme: 'light' | 'dark' = 'light';
|
||||
|
||||
@query('.affine-emoji-menu')
|
||||
accessor emojiMenu!: HTMLElement;
|
||||
|
||||
override render() {
|
||||
return html`<div class="affine-emoji-menu"></div>`;
|
||||
}
|
||||
}
|
||||
3
blocksuite/affine/block-callout/src/index.ts
Normal file
3
blocksuite/affine/block-callout/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './callout-block.js';
|
||||
export * from './callout-spec.js';
|
||||
export * from './effects.js';
|
||||
18
blocksuite/affine/block-callout/tsconfig.json
Normal file
18
blocksuite/affine/block-callout/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../components" },
|
||||
{ "path": "../model" },
|
||||
{ "path": "../shared" },
|
||||
{ "path": "../../framework/block-std" },
|
||||
{ "path": "../../framework/global" },
|
||||
{ "path": "../../framework/inline" },
|
||||
{ "path": "../../framework/store" }
|
||||
]
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
textKeymap,
|
||||
} from '@blocksuite/affine-components/rich-text';
|
||||
import {
|
||||
CalloutBlockModel,
|
||||
ParagraphBlockModel,
|
||||
ParagraphBlockSchema,
|
||||
} from '@blocksuite/affine-model';
|
||||
@@ -40,7 +41,12 @@ export const ParagraphKeymapExtension = KeymapExtension(
|
||||
|
||||
const { store } = std;
|
||||
const model = store.getBlock(text.from.blockId)?.model;
|
||||
if (!model || !matchModels(model, [ParagraphBlockModel])) return;
|
||||
if (
|
||||
!model ||
|
||||
!matchModels(model, [ParagraphBlockModel]) ||
|
||||
matchModels(model.parent, [CalloutBlockModel])
|
||||
)
|
||||
return;
|
||||
|
||||
const event = ctx.get('defaultState').event;
|
||||
event.preventDefault();
|
||||
@@ -71,7 +77,12 @@ export const ParagraphKeymapExtension = KeymapExtension(
|
||||
const text = std.selection.find(TextSelection);
|
||||
if (!text) return;
|
||||
const model = store.getBlock(text.from.blockId)?.model;
|
||||
if (!model || !matchModels(model, [ParagraphBlockModel])) return;
|
||||
if (
|
||||
!model ||
|
||||
!matchModels(model, [ParagraphBlockModel]) ||
|
||||
matchModels(model.parent, [CalloutBlockModel])
|
||||
)
|
||||
return;
|
||||
const inlineEditor = getInlineEditorByModel(
|
||||
std.host,
|
||||
text.from.blockId
|
||||
@@ -98,16 +109,21 @@ export const ParagraphKeymapExtension = KeymapExtension(
|
||||
const text = std.selection.find(TextSelection);
|
||||
if (!text) return;
|
||||
const model = store.getBlock(text.from.blockId)?.model;
|
||||
if (!model || !matchModels(model, [ParagraphBlockModel])) return;
|
||||
if (
|
||||
!model ||
|
||||
!matchModels(model, [ParagraphBlockModel]) ||
|
||||
matchModels(model.parent, [CalloutBlockModel])
|
||||
)
|
||||
return;
|
||||
const inlineEditor = getInlineEditorByModel(
|
||||
std.host,
|
||||
text.from.blockId
|
||||
);
|
||||
const range = inlineEditor?.getInlineRange();
|
||||
if (!range || !inlineEditor) return;
|
||||
const inlineRange = inlineEditor?.getInlineRange();
|
||||
if (!inlineRange || !inlineEditor) return;
|
||||
|
||||
const raw = ctx.get('keyboardState').raw;
|
||||
const isEnd = model.text.length === range.index;
|
||||
const isEnd = model.text.length === inlineRange.index;
|
||||
|
||||
if (model.type === 'quote') {
|
||||
const textStr = model.text.toString();
|
||||
@@ -129,7 +145,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
|
||||
if (isEnd && endWithTwoBlankLines) {
|
||||
raw.preventDefault();
|
||||
store.captureSync();
|
||||
model.text.delete(range.index - 1, 1);
|
||||
model.text.delete(inlineRange.index - 1, 1);
|
||||
std.command.chain().pipe(addParagraphCommand).run();
|
||||
return true;
|
||||
}
|
||||
@@ -149,7 +165,7 @@ export const ParagraphKeymapExtension = KeymapExtension(
|
||||
if (index === -1) return true;
|
||||
const collapsedSiblings = calculateCollapsedSiblings(model);
|
||||
|
||||
const rightText = model.text.split(range.index);
|
||||
const rightText = model.text.split(inlineRange.index);
|
||||
const newId = store.addBlock(
|
||||
model.flavour,
|
||||
{ type: model.type, text: rightText },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
AttachmentBlockModel,
|
||||
BookmarkBlockModel,
|
||||
CalloutBlockModel,
|
||||
CodeBlockModel,
|
||||
DatabaseBlockModel,
|
||||
DividerBlockModel,
|
||||
@@ -25,7 +26,12 @@ export function forwardDelete(std: BlockStdScope) {
|
||||
if (!text) return;
|
||||
const isCollapsed = text.isCollapsed();
|
||||
const model = store.getBlock(text.from.blockId)?.model;
|
||||
if (!model || !matchModels(model, [ParagraphBlockModel])) return;
|
||||
if (
|
||||
!model ||
|
||||
!matchModels(model, [ParagraphBlockModel]) ||
|
||||
matchModels(model.parent, [CalloutBlockModel])
|
||||
)
|
||||
return;
|
||||
const isEnd = isCollapsed && text.from.index === model.text.length;
|
||||
if (!isEnd) return;
|
||||
const parent = store.getParent(model);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from '@blocksuite/affine-block-embed';
|
||||
import { insertImagesCommand } from '@blocksuite/affine-block-image';
|
||||
import { insertLatexBlockCommand } from '@blocksuite/affine-block-latex';
|
||||
import { focusBlockEnd } from '@blocksuite/affine-block-note';
|
||||
import { getSurfaceBlock } from '@blocksuite/affine-block-surface';
|
||||
import { insertSurfaceRefBlockCommand } from '@blocksuite/affine-block-surface-ref';
|
||||
import { insertTableBlockCommand } from '@blocksuite/affine-block-table';
|
||||
@@ -61,6 +62,7 @@ import { assertType } from '@blocksuite/global/utils';
|
||||
import {
|
||||
DualLinkIcon,
|
||||
ExportToPdfIcon,
|
||||
FontIcon,
|
||||
FrameIcon,
|
||||
GroupingIcon,
|
||||
ImageIcon,
|
||||
@@ -171,6 +173,37 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
|
||||
!insideEdgelessText(model),
|
||||
})),
|
||||
|
||||
{
|
||||
name: 'Callout',
|
||||
description: 'Let your words stand out.',
|
||||
icon: FontIcon(),
|
||||
alias: ['callout'],
|
||||
showWhen: ({ model }) => {
|
||||
return model.doc.get(FeatureFlagService).getFlag('enable_callout');
|
||||
},
|
||||
action: ({ model, rootComponent }) => {
|
||||
const { doc } = model;
|
||||
const parent = doc.getParent(model);
|
||||
if (!parent) return;
|
||||
|
||||
const index = parent.children.indexOf(model);
|
||||
if (index === -1) return;
|
||||
const calloutId = doc.addBlock('affine:callout', {}, parent, index + 1);
|
||||
if (!calloutId) return;
|
||||
const paragraphId = doc.addBlock('affine:paragraph', {}, calloutId);
|
||||
if (!paragraphId) return;
|
||||
rootComponent.updateComplete
|
||||
.then(() => {
|
||||
const paragraph = rootComponent.std.view.getBlock(paragraphId);
|
||||
if (!paragraph) return;
|
||||
rootComponent.std.command.exec(focusBlockEnd, {
|
||||
focusBlock: paragraph,
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'Inline equation',
|
||||
description: 'Create a equation block.',
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { CodeBlockModel, ParagraphBlockModel } from '@blocksuite/affine-model';
|
||||
import {
|
||||
CalloutBlockModel,
|
||||
CodeBlockModel,
|
||||
ParagraphBlockModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
isHorizontalRuleMarkdown,
|
||||
isMarkdownPrefix,
|
||||
@@ -37,7 +41,13 @@ export function markdownInput(
|
||||
const isHeading = isParagraph && model.type.startsWith('h');
|
||||
const isParagraphQuoteBlock = isParagraph && model.type === 'quote';
|
||||
const isCodeBlock = matchModels(model, [CodeBlockModel]);
|
||||
if (isHeading || isParagraphQuoteBlock || isCodeBlock) return;
|
||||
if (
|
||||
isHeading ||
|
||||
isParagraphQuoteBlock ||
|
||||
isCodeBlock ||
|
||||
matchModels(model.parent, [CalloutBlockModel])
|
||||
)
|
||||
return;
|
||||
|
||||
const lineInfo = inline.getLine(range.index);
|
||||
if (!lineInfo) return;
|
||||
|
||||
39
blocksuite/affine/model/src/blocks/callout/callout-model.ts
Normal file
39
blocksuite/affine/model/src/blocks/callout/callout-model.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
BlockModel,
|
||||
BlockSchemaExtension,
|
||||
defineBlockSchema,
|
||||
type Text,
|
||||
} from '@blocksuite/store';
|
||||
|
||||
export const CalloutBlockSchema = defineBlockSchema({
|
||||
flavour: 'affine:callout',
|
||||
props: internal => ({
|
||||
emoji: '😀',
|
||||
text: internal.Text(),
|
||||
}),
|
||||
metadata: {
|
||||
version: 1,
|
||||
role: 'hub',
|
||||
parent: [
|
||||
'affine:note',
|
||||
'affine:database',
|
||||
'affine:paragraph',
|
||||
'affine:list',
|
||||
'affine:edgeless-text',
|
||||
],
|
||||
children: ['affine:paragraph'],
|
||||
},
|
||||
toModel: () => new CalloutBlockModel(),
|
||||
});
|
||||
|
||||
export type CalloutProps = {
|
||||
emoji: string;
|
||||
text: Text;
|
||||
};
|
||||
|
||||
export class CalloutBlockModel extends BlockModel<CalloutProps> {
|
||||
override text!: Text;
|
||||
}
|
||||
|
||||
export const CalloutBlockSchemaExtension =
|
||||
BlockSchemaExtension(CalloutBlockSchema);
|
||||
1
blocksuite/affine/model/src/blocks/callout/index.ts
Normal file
1
blocksuite/affine/model/src/blocks/callout/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './callout-model.js';
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './attachment/index.js';
|
||||
export * from './bookmark/index.js';
|
||||
export * from './callout/index.js';
|
||||
export * from './code/index.js';
|
||||
export * from './database/index.js';
|
||||
export * from './divider/index.js';
|
||||
|
||||
@@ -88,6 +88,7 @@ export const NoteBlockSchema = defineBlockSchema({
|
||||
'affine:surface-ref',
|
||||
'affine:embed-*',
|
||||
'affine:latex',
|
||||
'affine:callout',
|
||||
TableModelFlavour,
|
||||
],
|
||||
},
|
||||
|
||||
@@ -37,6 +37,7 @@ export const ParagraphBlockSchema = defineBlockSchema({
|
||||
'affine:paragraph',
|
||||
'affine:list',
|
||||
'affine:edgeless-text',
|
||||
'affine:callout',
|
||||
],
|
||||
},
|
||||
toModel: () => new ParagraphBlockModel(),
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface BlockSuiteFlags {
|
||||
enable_mobile_keyboard_toolbar: boolean;
|
||||
enable_mobile_linked_doc_menu: boolean;
|
||||
enable_block_meta: boolean;
|
||||
enable_callout: boolean;
|
||||
}
|
||||
|
||||
export class FeatureFlagService extends StoreExtension {
|
||||
@@ -38,6 +39,7 @@ export class FeatureFlagService extends StoreExtension {
|
||||
enable_mobile_keyboard_toolbar: false,
|
||||
enable_mobile_linked_doc_menu: false,
|
||||
enable_block_meta: false,
|
||||
enable_callout: false,
|
||||
});
|
||||
|
||||
setFlag(key: keyof BlockSuiteFlags, value: boolean) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-callout": "workspace:*",
|
||||
"@blocksuite/affine-block-list": "workspace:*",
|
||||
"@blocksuite/affine-block-note": "workspace:*",
|
||||
"@blocksuite/affine-block-paragraph": "workspace:*",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { type CalloutBlockComponent } from '@blocksuite/affine-block-callout';
|
||||
import {
|
||||
AFFINE_EDGELESS_NOTE,
|
||||
type EdgelessNoteBlockComponent,
|
||||
@@ -6,7 +7,7 @@ import { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph';
|
||||
import {
|
||||
DatabaseBlockModel,
|
||||
ListBlockModel,
|
||||
type ParagraphBlockModel,
|
||||
ParagraphBlockModel,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
@@ -209,6 +210,14 @@ export const getClosestBlockByPoint = (
|
||||
return null;
|
||||
}
|
||||
|
||||
if (matchModels(closestBlock.model, [ParagraphBlockModel])) {
|
||||
const callout =
|
||||
closestBlock.closest<CalloutBlockComponent>('affine-callout');
|
||||
if (callout) {
|
||||
return callout;
|
||||
}
|
||||
}
|
||||
|
||||
return closestBlock;
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../block-callout" },
|
||||
{ "path": "../block-list" },
|
||||
{ "path": "../block-note" },
|
||||
{ "path": "../block-paragraph" },
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-attachment": "workspace:*",
|
||||
"@blocksuite/affine-block-bookmark": "workspace:*",
|
||||
"@blocksuite/affine-block-callout": "workspace:*",
|
||||
"@blocksuite/affine-block-code": "workspace:*",
|
||||
"@blocksuite/affine-block-data-view": "workspace:*",
|
||||
"@blocksuite/affine-block-database": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { effects as blockAttachmentEffects } from '@blocksuite/affine-block-attachment/effects';
|
||||
import { effects as blockBookmarkEffects } from '@blocksuite/affine-block-bookmark/effects';
|
||||
import { effects as blockCalloutEffects } from '@blocksuite/affine-block-callout/effects';
|
||||
import { effects as blockCodeEffects } from '@blocksuite/affine-block-code/effects';
|
||||
import { effects as blockDataViewEffects } from '@blocksuite/affine-block-data-view/effects';
|
||||
import { effects as blockDatabaseEffects } from '@blocksuite/affine-block-database/effects';
|
||||
@@ -72,6 +73,7 @@ export function effects() {
|
||||
blockCodeEffects();
|
||||
blockTableEffects();
|
||||
blockRootEffects();
|
||||
blockCalloutEffects();
|
||||
|
||||
componentCaptionEffects();
|
||||
componentContextMenuEffects();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AttachmentBlockSpec } from '@blocksuite/affine-block-attachment';
|
||||
import { BookmarkBlockSpec } from '@blocksuite/affine-block-bookmark';
|
||||
import { CalloutBlockSpec } from '@blocksuite/affine-block-callout';
|
||||
import { CodeBlockSpec } from '@blocksuite/affine-block-code';
|
||||
import { DataViewBlockSpec } from '@blocksuite/affine-block-data-view';
|
||||
import { DatabaseBlockSpec } from '@blocksuite/affine-block-database';
|
||||
@@ -55,6 +56,7 @@ export const CommonBlockSpecs: ExtensionType[] = [
|
||||
ParagraphBlockSpec,
|
||||
DefaultOpenDocExtension,
|
||||
FontLoaderService,
|
||||
CalloutBlockSpec,
|
||||
].flat();
|
||||
|
||||
export const PageFirstPartyBlockSpecs: ExtensionType[] = [
|
||||
|
||||
@@ -6,6 +6,7 @@ import { TableSelectionExtension } from '@blocksuite/affine-block-table';
|
||||
import {
|
||||
AttachmentBlockSchemaExtension,
|
||||
BookmarkBlockSchemaExtension,
|
||||
CalloutBlockSchemaExtension,
|
||||
CodeBlockSchemaExtension,
|
||||
DatabaseBlockSchemaExtension,
|
||||
DividerBlockSchemaExtension,
|
||||
@@ -78,6 +79,7 @@ export const StoreExtensions: ExtensionType[] = [
|
||||
EdgelessTextBlockSchemaExtension,
|
||||
LatexBlockSchemaExtension,
|
||||
TableBlockSchemaExtension,
|
||||
CalloutBlockSchemaExtension,
|
||||
|
||||
BlockSelectionExtension,
|
||||
TextSelectionExtension,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { SurfaceBlockSchema } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
AttachmentBlockSchema,
|
||||
BookmarkBlockSchema,
|
||||
CalloutBlockSchema,
|
||||
CodeBlockSchema,
|
||||
DatabaseBlockSchema,
|
||||
DividerBlockSchema,
|
||||
@@ -54,4 +55,5 @@ export const AffineSchemas: z.infer<typeof BlockSchema>[] = [
|
||||
EdgelessTextBlockSchema,
|
||||
LatexBlockSchema,
|
||||
TableBlockSchema,
|
||||
CalloutBlockSchema,
|
||||
];
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"references": [
|
||||
{ "path": "../affine/block-attachment" },
|
||||
{ "path": "../affine/block-bookmark" },
|
||||
{ "path": "../affine/block-callout" },
|
||||
{ "path": "../affine/block-code" },
|
||||
{ "path": "../affine/block-data-view" },
|
||||
{ "path": "../affine/block-database" },
|
||||
|
||||
Reference in New Issue
Block a user