feat(editor): slash menu and toolbar widget extensions (#12002)

Closes: BS-3226
Closes: BS-3227
Closes: BS-3228
Closes: BS-3229

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced new view extensions for remote selection, scroll anchoring, slash menu, and toolbar widgets, enhancing customization and integration capabilities.

- **Refactor**
  - Updated export paths and module structure for widget-related packages to improve modularity and accessibility.
  - Reorganized internal dependencies and removed redundant widget imports from core block packages.
  - Refactored edgeless-related widgets and components to use a standardized scope interface for improved context and state management.
  - Centralized graphics controller access and slot management through standardized context objects.

- **Chores**
  - Added and updated dependencies to streamline extension loading and workspace management.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Saul-Mirone
2025-04-26 11:27:13 +00:00
parent de01692414
commit 59d4942d9b
37 changed files with 200 additions and 99 deletions

View File

@@ -3,6 +3,10 @@ import { PresentationToolbar } from './edgeless-toolbar/presentation-toolbar';
import { FrameBlockComponent } from './frame-block';
import { EdgelessFrameOrderButton } from './present/frame-order-button';
import { EdgelessFrameOrderMenu } from './present/frame-order-menu';
import {
EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET,
EdgelessNavigatorBlackBackgroundWidget,
} from './present/navigator-bg-widget';
import { EdgelessNavigatorSettingButton } from './present/navigator-setting-button';
import { EdgelessPresentButton } from './present/present-button';
@@ -21,6 +25,11 @@ export function effects() {
);
customElements.define('edgeless-present-button', EdgelessPresentButton);
customElements.define('presentation-toolbar', PresentationToolbar);
// Navigation components
customElements.define(
EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET,
EdgelessNavigatorBlackBackgroundWidget
);
}
declare global {
@@ -33,5 +42,6 @@ declare global {
'edgeless-navigator-setting-button': EdgelessNavigatorSettingButton;
'edgeless-present-button': EdgelessPresentButton;
'presentation-toolbar': PresentationToolbar;
'edgeless-navigator-black-background': EdgelessNavigatorBlackBackgroundWidget;
}
}

View File

@@ -0,0 +1,124 @@
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
import type { FrameBlockModel, RootBlockModel } from '@blocksuite/affine-model';
import { EditPropsStore } from '@blocksuite/affine-shared/services';
import { Bound } from '@blocksuite/global/gfx';
import { WidgetComponent, WidgetViewExtension } from '@blocksuite/std';
import { GfxControllerIdentifier } from '@blocksuite/std/gfx';
import { effect } from '@preact/signals-core';
import { css, html, nothing } from 'lit';
import { state } from 'lit/decorators.js';
import { literal, unsafeStatic } from 'lit/static-html.js';
export const EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET =
'edgeless-navigator-black-background';
export class EdgelessNavigatorBlackBackgroundWidget extends WidgetComponent<RootBlockModel> {
static override styles = css`
.edgeless-navigator-black-background {
background-color: black;
position: absolute;
z-index: 1;
background-color: transparent;
box-shadow: 0 0 0 5000px black;
pointer-events: none;
}
`;
private _blackBackground = false;
get gfx() {
return this.std.get(GfxControllerIdentifier);
}
private get _slots() {
return this.std.get(EdgelessLegacySlotIdentifier);
}
private _tryLoadBlackBackground() {
const value = this.std
.get(EditPropsStore)
.getStorage('presentBlackBackground');
this._blackBackground = value ?? true;
}
override firstUpdated() {
const { _disposables, gfx } = this;
_disposables.add(
this._slots.navigatorFrameChanged.subscribe(frame => {
this.frame = frame;
})
);
_disposables.add(
this._slots.navigatorSettingUpdated.subscribe(({ blackBackground }) => {
if (blackBackground !== undefined) {
this.std
.get(EditPropsStore)
.setStorage('presentBlackBackground', blackBackground);
this._blackBackground = blackBackground;
this.show =
blackBackground &&
this.gfx.tool.currentToolOption$.peek().type === 'frameNavigator';
}
})
);
_disposables.add(
effect(() => {
const tool = gfx.tool.currentToolName$.value;
if (tool !== 'frameNavigator') {
this.show = false;
} else {
this.show = this._blackBackground;
}
})
);
_disposables.add(
this._slots.fullScreenToggled.subscribe(
() =>
setTimeout(() => {
this.requestUpdate();
}, 500) // wait for full screen animation
)
);
this._tryLoadBlackBackground();
}
override render() {
const { frame, show, gfx } = this;
if (!show || !frame) return nothing;
const bound = Bound.deserialize(frame.xywh);
const zoom = gfx.viewport.zoom;
const width = bound.w * zoom;
const height = bound.h * zoom;
const [x, y] = gfx.viewport.toViewCoord(bound.x, bound.y);
return html` <style>
.edgeless-navigator-black-background {
width: ${width}px;
height: ${height}px;
top: ${y}px;
left: ${x}px;
}
</style>
<div class="edgeless-navigator-black-background"></div>`;
}
@state()
private accessor frame: FrameBlockModel | undefined = undefined;
@state()
private accessor show = false;
}
export const edgelessNavigatorBgWidget = WidgetViewExtension(
'affine:page',
EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET,
literal`${unsafeStatic(EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET)}`
);

View File

@@ -9,6 +9,7 @@ import { FrameHighlightManager } from './frame-highlight-manager';
import { FrameBlockSpec } from './frame-spec';
import { FrameTool } from './frame-tool';
import { frameToolbarExtension } from './frame-toolbar';
import { edgelessNavigatorBgWidget } from './present/navigator-bg-widget';
import { PresentTool } from './preset-tool';
export class FrameViewExtension extends ViewExtensionProvider {
@@ -28,6 +29,7 @@ export class FrameViewExtension extends ViewExtensionProvider {
context.register(PresentTool);
context.register(frameQuickTool);
context.register(frameToolbarExtension);
context.register(edgelessNavigatorBgWidget);
}
}
}