mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-24 04:27:27 +08:00
feat(editor): gfx link extension (#12046)
Closes: BS-3368 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new link tool extension, enabling enhanced link-related functionality within the edgeless workspace. - Added a new view extension for link tools, improving integration and usability in edgeless mode. - **Chores** - Added a new package for link tool functionality with appropriate dependencies and exports. - Registered new custom elements for edgeless toolbars and link tools to support modular UI components. - Updated project configurations and workspace dependencies to include the new link tool module. - **Refactor** - Removed unused quick tool exports and toolbar component registrations to streamline the edgeless extension codebase. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@blocksuite/affine-gfx-link",
|
||||
"description": "Gfx link for BlockSuite.",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"keywords": [],
|
||||
"author": "toeverything",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@blocksuite/affine-block-bookmark": "workspace:*",
|
||||
"@blocksuite/affine-block-embed": "workspace:*",
|
||||
"@blocksuite/affine-block-surface": "workspace:*",
|
||||
"@blocksuite/affine-components": "workspace:*",
|
||||
"@blocksuite/affine-ext-loader": "workspace:*",
|
||||
"@blocksuite/affine-gfx-pointer": "workspace:*",
|
||||
"@blocksuite/affine-model": "workspace:*",
|
||||
"@blocksuite/affine-rich-text": "workspace:*",
|
||||
"@blocksuite/affine-shared": "workspace:*",
|
||||
"@blocksuite/affine-widget-edgeless-toolbar": "workspace:*",
|
||||
"@blocksuite/global": "workspace:*",
|
||||
"@blocksuite/icons": "^2.2.12",
|
||||
"@blocksuite/std": "workspace:*",
|
||||
"@blocksuite/store": "workspace:*",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@preact/signals-core": "^1.8.0",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"lit": "^3.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"minimatch": "^10.0.1",
|
||||
"rxjs": "^7.8.1",
|
||||
"yjs": "^13.6.21",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./effects": "./src/effects.ts",
|
||||
"./view": "./src/view.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist",
|
||||
"!src/__tests__",
|
||||
"!dist/__tests__"
|
||||
],
|
||||
"version": "0.21.0"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { EdgelessLinkToolButton } from './toolbar/link-tool-button';
|
||||
|
||||
export function effects() {
|
||||
customElements.define('edgeless-link-tool-button', EdgelessLinkToolButton);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export {};
|
||||
@@ -0,0 +1,13 @@
|
||||
import { QuickToolExtension } from '@blocksuite/affine-widget-edgeless-toolbar';
|
||||
import { html } from 'lit';
|
||||
|
||||
import { buildLinkDenseMenu } from './toolbar/link-dense-menu';
|
||||
|
||||
export const linkQuickTool = QuickToolExtension('link', ({ block, gfx }) => {
|
||||
return {
|
||||
content: html`<edgeless-link-tool-button
|
||||
.edgeless=${block}
|
||||
></edgeless-link-tool-button>`,
|
||||
menu: buildLinkDenseMenu(block, gfx),
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark';
|
||||
import { menu } from '@blocksuite/affine-components/context-menu';
|
||||
import { LinkIcon } from '@blocksuite/affine-components/icons';
|
||||
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
|
||||
import type { DenseMenuBuilder } from '@blocksuite/affine-widget-edgeless-toolbar';
|
||||
|
||||
export const buildLinkDenseMenu: DenseMenuBuilder = edgeless =>
|
||||
menu.action({
|
||||
name: 'Link',
|
||||
prefix: LinkIcon,
|
||||
select: () => {
|
||||
const [_, { insertedLinkType }] = edgeless.std.command.exec(
|
||||
insertLinkByQuickSearchCommand
|
||||
);
|
||||
|
||||
insertedLinkType
|
||||
?.then(type => {
|
||||
const flavour = type?.flavour;
|
||||
if (!flavour) return;
|
||||
|
||||
edgeless.std
|
||||
.getOptional(TelemetryProvider)
|
||||
?.track('CanvasElementAdded', {
|
||||
control: 'toolbar:general',
|
||||
page: 'whiteboard editor',
|
||||
module: 'toolbar',
|
||||
type: flavour.split(':')[1],
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
import { insertLinkByQuickSearchCommand } from '@blocksuite/affine-block-bookmark';
|
||||
import { insertEmbedCard } from '@blocksuite/affine-block-embed';
|
||||
import { toggleEmbedCardCreateModal } from '@blocksuite/affine-components/embed-card-modal';
|
||||
import { LinkIcon } from '@blocksuite/affine-components/icons';
|
||||
import type * as PointerEffect from '@blocksuite/affine-gfx-pointer';
|
||||
import { TelemetryProvider } from '@blocksuite/affine-shared/services';
|
||||
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
|
||||
declare type _GLOBAL_ = typeof PointerEffect;
|
||||
|
||||
export class EdgelessLinkToolButton extends QuickToolMixin(LitElement) {
|
||||
static override styles = css`
|
||||
.link-icon,
|
||||
.link-icon > svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
override type = 'default' as const;
|
||||
|
||||
private _onClick() {
|
||||
const [success, { insertedLinkType }] = this.edgeless.std.command.exec(
|
||||
insertLinkByQuickSearchCommand
|
||||
);
|
||||
|
||||
if (!success) {
|
||||
// fallback to create a bookmark block with input modal
|
||||
toggleEmbedCardCreateModal(
|
||||
this.edgeless.host,
|
||||
'Links',
|
||||
'The added link will be displayed as a card view.',
|
||||
{
|
||||
mode: 'edgeless',
|
||||
onSave: url => {
|
||||
insertEmbedCard(this.edgeless.std, {
|
||||
flavour: 'affine:bookmark',
|
||||
targetStyle: 'vertical',
|
||||
props: { url },
|
||||
});
|
||||
},
|
||||
}
|
||||
).catch(console.error);
|
||||
return;
|
||||
}
|
||||
|
||||
insertedLinkType
|
||||
?.then(type => {
|
||||
const flavour = type?.flavour;
|
||||
if (!flavour) return;
|
||||
|
||||
this.edgeless.std
|
||||
.getOptional(TelemetryProvider)
|
||||
?.track('CanvasElementAdded', {
|
||||
control: 'toolbar:general',
|
||||
page: 'whiteboard editor',
|
||||
module: 'toolbar',
|
||||
segment: 'toolbar',
|
||||
type: flavour.split(':')[1],
|
||||
});
|
||||
|
||||
this.edgeless.std
|
||||
.getOptional(TelemetryProvider)
|
||||
?.track('LinkedDocCreated', {
|
||||
control: 'links',
|
||||
page: 'whiteboard editor',
|
||||
module: 'edgeless toolbar',
|
||||
segment: 'whiteboard',
|
||||
type: flavour.split(':')[1],
|
||||
other: 'existing doc',
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`<edgeless-tool-icon-button
|
||||
.iconContainerPadding="${6}"
|
||||
.tooltip="${html`<affine-tooltip-content-with-shortcut
|
||||
data-tip="${'Link'}"
|
||||
data-shortcut="${'@'}"
|
||||
></affine-tooltip-content-with-shortcut>`}"
|
||||
.tooltipOffset=${17}
|
||||
class="edgeless-link-tool-button"
|
||||
@click=${this._onClick}
|
||||
>
|
||||
<span class="link-icon">${LinkIcon}</span>
|
||||
</edgeless-tool-icon-button>`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
type ViewExtensionContext,
|
||||
ViewExtensionProvider,
|
||||
} from '@blocksuite/affine-ext-loader';
|
||||
|
||||
import { effects } from './effects';
|
||||
import { linkQuickTool } from './link-tool';
|
||||
|
||||
export class LinkViewExtension extends ViewExtensionProvider {
|
||||
override name = 'affine-link-gfx';
|
||||
|
||||
override effect() {
|
||||
super.effect();
|
||||
effects();
|
||||
}
|
||||
|
||||
override setup(context: ViewExtensionContext) {
|
||||
super.setup(context);
|
||||
if (this.isEdgeless(context.scope)) {
|
||||
context.register(linkQuickTool);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src"],
|
||||
"references": [
|
||||
{ "path": "../../blocks/bookmark" },
|
||||
{ "path": "../../blocks/embed" },
|
||||
{ "path": "../../blocks/surface" },
|
||||
{ "path": "../../components" },
|
||||
{ "path": "../../ext-loader" },
|
||||
{ "path": "../pointer" },
|
||||
{ "path": "../../model" },
|
||||
{ "path": "../../rich-text" },
|
||||
{ "path": "../../shared" },
|
||||
{ "path": "../../widgets/edgeless-toolbar" },
|
||||
{ "path": "../../../framework/global" },
|
||||
{ "path": "../../../framework/std" },
|
||||
{ "path": "../../../framework/store" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user