refactor(editor): move frame toolbar config and components to its package (#11084)

This commit is contained in:
Saul-Mirone
2025-03-21 16:47:40 +00:00
parent f5fb5c848e
commit c1d426d8e9
27 changed files with 82 additions and 53 deletions

View File

@@ -0,0 +1,9 @@
export const FrameConfig: {
name: string;
wh: [number, number];
}[] = [
{ name: '1:1', wh: [1200, 1200] },
{ name: '4:3', wh: [1600, 1200] },
{ name: '16:9', wh: [1600, 900] },
{ name: '2:1', wh: [1600, 800] },
];

View File

@@ -0,0 +1,33 @@
import { menu } from '@blocksuite/affine-components/context-menu';
import type { DenseMenuBuilder } from '@blocksuite/affine-widget-edgeless-toolbar';
import { FrameIcon } from '@blocksuite/icons/lit';
import { EdgelessFrameManagerIdentifier } from '../frame-manager.js';
import { FrameConfig } from './config.js';
export const buildFrameDenseMenu: DenseMenuBuilder = (edgeless, gfx) =>
menu.subMenu({
name: 'Frame',
prefix: FrameIcon({ width: '20px', height: '20px' }),
select: () => gfx.tool.setTool({ type: 'frame' }),
isSelected: gfx.tool.currentToolName$.peek() === 'frame',
options: {
items: [
menu.action({
name: 'Custom',
select: () => gfx.tool.setTool({ type: 'frame' }),
}),
...FrameConfig.map(config =>
menu.action({
name: `Slide ${config.name}`,
select: () => {
const frame = edgeless.std.get(EdgelessFrameManagerIdentifier);
// @ts-expect-error FIXME: resolve after gfx tool refactor
gfx.tool.setTool('default');
frame.createFrameOnViewportCenter(config.wh);
},
})
),
],
},
});

View File

@@ -0,0 +1,104 @@
import { EdgelessToolbarToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { css, html, LitElement } from 'lit';
import { repeat } from 'lit/directives/repeat.js';
import { EdgelessFrameManagerIdentifier } from '../frame-manager.js';
import { FrameConfig } from './config.js';
export class EdgelessFrameMenu extends EdgelessToolbarToolMixin(LitElement) {
static override styles = css`
:host {
position: absolute;
display: flex;
z-index: -1;
}
.menu-content {
display: flex;
align-items: center;
justify-content: center;
gap: 14px;
}
.frame-add-button {
width: 40px;
height: 24px;
border-radius: 4px;
border: 1px solid var(--affine-border-color);
color: var(--affine-text-primary-color);
line-height: 20px;
font-weight: 400;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
position: relative;
}
.frame-add-button::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
border-radius: 3px;
background: transparent;
transition: background-color 0.23s ease;
pointer-events: none;
}
.frame-add-button:hover::before {
background: var(--affine-hover-color);
}
.custom {
width: 60px;
background: var(--affine-hover-color);
}
.divider {
width: 1px;
height: 20px;
background: var(--affine-border-color);
transform: scaleX(0.5);
}
`;
override type: GfxToolsFullOptionValue['type'] = 'frame';
get frameManager() {
return this.edgeless.std.get(EdgelessFrameManagerIdentifier);
}
override render() {
const { gfx, frameManager } = this;
return html`
<edgeless-slide-menu .showNext=${false}>
<div class="menu-content">
<div class="frame-add-button custom">Custom</div>
<div class="divider"></div>
${repeat(
FrameConfig,
item => item.name,
(item, index) => html`
<div
@click=${() => {
// @ts-expect-error FIXME: resolve after gfx tool refactor
gfx.tool.setTool('default');
frameManager.createFrameOnViewportCenter(item.wh);
}}
class="frame-add-button ${index}"
data-name="${item.name}"
data-w="${item.wh[0]}"
data-h="${item.wh[1]}"
>
${item.name}
</div>
`
)}
</div>
</edgeless-slide-menu>
`;
}
}

View File

@@ -0,0 +1,48 @@
import { QuickToolMixin } from '@blocksuite/affine-widget-edgeless-toolbar';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { FrameIcon } from '@blocksuite/icons/lit';
import { css, html, LitElement } from 'lit';
export class EdgelessFrameToolButton extends QuickToolMixin(LitElement) {
static override styles = css`
:host {
display: flex;
}
`;
override type: GfxToolsFullOptionValue['type'] = 'frame';
private _toggleFrameMenu() {
if (this.tryDisposePopper()) return;
const menu = this.createPopper('edgeless-frame-menu', this);
menu.element.edgeless = this.edgeless;
}
override render() {
const type = this.edgelessTool?.type;
return html`
<edgeless-tool-icon-button
class="edgeless-frame-button"
.tooltip=${this.popper
? ''
: html`<affine-tooltip-content-with-shortcut
data-tip="${'Frame'}"
data-shortcut="${'F'}"
></affine-tooltip-content-with-shortcut>`}
.tooltipOffset=${17}
.iconSize=${'24px'}
.active=${type === 'frame'}
.iconContainerPadding=${6}
@click=${() => {
// don't update tool before toggling menu
this._toggleFrameMenu();
this.setEdgelessTool({ type: 'frame' });
}}
>
${FrameIcon()}
<toolbar-arrow-up-icon></toolbar-arrow-up-icon>
</edgeless-tool-icon-button>
`;
}
}

View File

@@ -0,0 +1,5 @@
export * from './config';
export * from './frame-dense-menu';
export * from './frame-menu';
export * from './frame-tool-button';
export * from './quick-tool';

View File

@@ -0,0 +1,15 @@
import { QuickToolExtension } from '@blocksuite/affine-widget-edgeless-toolbar';
import { html } from 'lit';
import { buildFrameDenseMenu } from './frame-dense-menu';
export const frameQuickTool = QuickToolExtension('frame', ({ block, gfx }) => {
return {
type: 'frame',
content: html`<edgeless-frame-tool-button
.edgeless=${block}
></edgeless-frame-tool-button>`,
menu: buildFrameDenseMenu(block, gfx),
enable: !block.doc.readonly,
};
});

View File

@@ -1,5 +1,16 @@
import { EdgelessFrameMenu, EdgelessFrameToolButton } from './edgeless-toolbar';
import { FrameBlockComponent } from './frame-block';
export function effects() {
customElements.define('affine-frame', FrameBlockComponent);
customElements.define('edgeless-frame-tool-button', EdgelessFrameToolButton);
customElements.define('edgeless-frame-menu', EdgelessFrameMenu);
}
declare global {
interface HTMLElementTagNameMap {
'affine-frame': FrameBlockComponent;
'edgeless-frame-tool-button': EdgelessFrameToolButton;
'edgeless-frame-menu': EdgelessFrameMenu;
}
}

View File

@@ -1,6 +1,7 @@
import type { FrameTool } from './frame-tool';
import type { PresentTool, PresentToolOption } from './preset-tool';
export * from './edgeless-toolbar';
export * from './frame-block';
export * from './frame-highlight-manager';
export * from './frame-manager';