refactor(editor): unify directories naming (#11516)
**Directory Structure Changes** - Renamed multiple block-related directories by removing the "block-" prefix: - `block-attachment` → `attachment` - `block-bookmark` → `bookmark` - `block-callout` → `callout` - `block-code` → `code` - `block-data-view` → `data-view` - `block-database` → `database` - `block-divider` → `divider` - `block-edgeless-text` → `edgeless-text` - `block-embed` → `embed`
This commit is contained in:
46
blocksuite/affine/blocks/callout/package.json
Normal file
46
blocksuite/affine/blocks/callout/package.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "@blocksuite/affine-block-callout",
|
||||
"description": "Callout block for BlockSuite.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"keywords": [],
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-inline-preset": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-rich-text": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/affine-widget-slash-menu": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.10",
|
||||
"@blocksuite/std": "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",
|
||||
"rxjs": "^7.8.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./effects": "./src/effects.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.21.0"
|
||||
}
|
||||
121
blocksuite/affine/blocks/callout/src/callout-block.ts
Normal file
121
blocksuite/affine/blocks/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-inline-preset';
|
||||
import { type CalloutBlockModel } from '@blocksuite/affine-model';
|
||||
import { EDGELESS_TOP_CONTENTEDITABLE_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/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.props.emoji = data.native;
|
||||
}}
|
||||
></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>(
|
||||
EDGELESS_TOP_CONTENTEDITABLE_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.props.emoji$}</span>
|
||||
</div>
|
||||
<div class="affine-callout-children">
|
||||
${this.renderChildren(this.model)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
34
blocksuite/affine/blocks/callout/src/callout-keymap.ts
Normal file
34
blocksuite/affine/blocks/callout/src/callout-keymap.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { CalloutBlockModel } from '@blocksuite/affine-model';
|
||||
import { matchModels } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
BlockSelection,
|
||||
KeymapExtension,
|
||||
TextSelection,
|
||||
} from '@blocksuite/std';
|
||||
|
||||
export const CalloutKeymapExtension = KeymapExtension(std => {
|
||||
return {
|
||||
Backspace: ctx => {
|
||||
const text = std.selection.find(TextSelection);
|
||||
if (text && text.isCollapsed() && text.from.index === 0) {
|
||||
const event = ctx.get('defaultState').event;
|
||||
event.preventDefault();
|
||||
|
||||
const block = std.store.getBlock(text.from.blockId);
|
||||
if (!block) return false;
|
||||
const parent = std.store.getParent(block.model);
|
||||
if (!parent) return false;
|
||||
if (!matchModels(parent, [CalloutBlockModel])) return false;
|
||||
|
||||
std.selection.setGroup('note', [
|
||||
std.selection.create(BlockSelection, {
|
||||
blockId: parent.id,
|
||||
}),
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
});
|
||||
14
blocksuite/affine/blocks/callout/src/callout-spec.ts
Normal file
14
blocksuite/affine/blocks/callout/src/callout-spec.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
|
||||
import { BlockViewExtension, FlavourExtension } from '@blocksuite/std';
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
import { literal } from 'lit/static-html.js';
|
||||
|
||||
import { CalloutKeymapExtension } from './callout-keymap';
|
||||
import { calloutSlashMenuConfig } from './configs/slash-menu';
|
||||
|
||||
export const CalloutBlockSpec: ExtensionType[] = [
|
||||
FlavourExtension('affine:callout'),
|
||||
BlockViewExtension('affine:callout', literal`affine-callout`),
|
||||
CalloutKeymapExtension,
|
||||
SlashMenuConfigExtension('affine:callout', calloutSlashMenuConfig),
|
||||
];
|
||||
62
blocksuite/affine/blocks/callout/src/configs/slash-menu.ts
Normal file
62
blocksuite/affine/blocks/callout/src/configs/slash-menu.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { CalloutBlockModel } from '@blocksuite/affine-model';
|
||||
import { focusBlockEnd } from '@blocksuite/affine-shared/commands';
|
||||
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
|
||||
import {
|
||||
findAncestorModel,
|
||||
isInsideBlockByFlavour,
|
||||
matchModels,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { type SlashMenuConfig } from '@blocksuite/affine-widget-slash-menu';
|
||||
import { FontIcon } from '@blocksuite/icons/lit';
|
||||
|
||||
import { calloutTooltip } from './tooltips';
|
||||
|
||||
export const calloutSlashMenuConfig: SlashMenuConfig = {
|
||||
disableWhen: ({ model }) => {
|
||||
return (
|
||||
findAncestorModel(model, ancestor =>
|
||||
matchModels(ancestor, [CalloutBlockModel])
|
||||
) !== null
|
||||
);
|
||||
},
|
||||
items: [
|
||||
{
|
||||
name: 'Callout',
|
||||
description: 'Let your words stand out.',
|
||||
icon: FontIcon(),
|
||||
tooltip: {
|
||||
figure: calloutTooltip,
|
||||
caption: 'Callout',
|
||||
},
|
||||
searchAlias: ['callout'],
|
||||
group: '0_Basic@9',
|
||||
when: ({ std, model }) => {
|
||||
return (
|
||||
std.get(FeatureFlagService).getFlag('enable_callout') &&
|
||||
!isInsideBlockByFlavour(model.doc, model, 'affine:edgeless-text')
|
||||
);
|
||||
},
|
||||
action: ({ model, std }) => {
|
||||
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;
|
||||
std.host.updateComplete
|
||||
.then(() => {
|
||||
const paragraph = std.view.getBlock(paragraphId);
|
||||
if (!paragraph) return;
|
||||
std.command.exec(focusBlockEnd, {
|
||||
focusBlock: paragraph,
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
28
blocksuite/affine/blocks/callout/src/configs/tooltips.ts
Normal file
28
blocksuite/affine/blocks/callout/src/configs/tooltips.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { html } from 'lit';
|
||||
|
||||
// prettier-ignore
|
||||
export const calloutTooltip = html`<svg width="170" height="106" viewBox="0 0 170 106" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_3479_144688)">
|
||||
<rect width="170" height="106" fill="white"/>
|
||||
<text fill="#8E8D91" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="10" letter-spacing="0px"><tspan x="8" y="16.6364">Highlight important notes.</tspan></text>
|
||||
<path d="M8 32.1816C8 29.3199 10.3199 27 13.1816 27H489.892C492.754 27 495.073 29.3199 495.073 32.1816V90.8403C495.073 93.702 492.754 96.0219 489.892 96.0219H13.1816C10.3199 96.0219 8 93.702 8 90.8403V32.1816Z" fill="#EEFFFD"/>
|
||||
<g clip-path="url(#clip1_3479_144688)">
|
||||
<g clip-path="url(#clip2_3479_144688)">
|
||||
<text fill="black" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="15" font-weight="500" letter-spacing="0em"><tspan x="14.7494" y="50.1658">👋</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<text fill="#141414" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="10" letter-spacing="0px"><tspan x="44.9541" y="49.4196">A storyboard is a visual representation of a story, typically presented in the form of sequential </tspan><tspan x="44.9541" y="64.9645">panels. Each panel features illustrations or images accompanied by notes describing key </tspan><tspan x="44.9541" y="80.5094">scenes or shots, including details of action and dialogue. </tspan></text>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_3479_144688">
|
||||
<rect width="170" height="106" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_3479_144688">
|
||||
<rect width="24" height="30.477" fill="white" transform="translate(14.477 30.2388)"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip2_3479_144688">
|
||||
<rect x="14.477" y="36.7158" width="24" height="24" rx="1.94311" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
`
|
||||
14
blocksuite/affine/blocks/callout/src/effects.ts
Normal file
14
blocksuite/affine/blocks/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/blocks/callout/src/emoji-menu.ts
Normal file
34
blocksuite/affine/blocks/callout/src/emoji-menu.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { WithDisposable } from '@blocksuite/global/lit';
|
||||
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/blocks/callout/src/index.ts
Normal file
3
blocksuite/affine/blocks/callout/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './callout-block.js';
|
||||
export * from './callout-spec.js';
|
||||
export * from './effects.js';
|
||||
20
blocksuite/affine/blocks/callout/tsconfig.json
Normal file
20
blocksuite/affine/blocks/callout/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../../components" },
|
||||
{ "path": "../../inlines/preset" },
|
||||
{ "path": "../../model" },
|
||||
{ "path": "../../rich-text" },
|
||||
{ "path": "../../shared" },
|
||||
{ "path": "../../widgets/slash-menu" },
|
||||
{ "path": "../../../framework/global" },
|
||||
{ "path": "../../../framework/std" },
|
||||
{ "path": "../../../framework/store" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user