diff --git a/blocksuite/affine/blocks/code/src/code-toolbar/components/code-toolbar.ts b/blocksuite/affine/blocks/code/src/code-toolbar/components/code-toolbar.ts index 4ee4c0869..bfd92ce08 100644 --- a/blocksuite/affine/blocks/code/src/code-toolbar/components/code-toolbar.ts +++ b/blocksuite/affine/blocks/code/src/code-toolbar/components/code-toolbar.ts @@ -11,7 +11,6 @@ import { MoreVerticalIcon } from '@blocksuite/icons/lit'; import { flip, offset } from '@floating-ui/dom'; import { css, html, LitElement } from 'lit'; import { property, query, state } from 'lit/decorators.js'; -import { styleMap } from 'lit/directives/style-map.js'; import type { CodeBlockToolbarContext } from '../context.js'; @@ -82,18 +81,10 @@ export class AffineCodeToolbar extends WithDisposable(LitElement) { createLitPortal({ template: html` - -
- ${renderGroups(this.moreGroups, this.context)} -
-
+ `, // should be greater than block-selection z-index as selection and popover wil share the same stacking context(editor-host) portalStyles: { diff --git a/blocksuite/affine/blocks/code/src/code-toolbar/components/more-menu.ts b/blocksuite/affine/blocks/code/src/code-toolbar/components/more-menu.ts new file mode 100644 index 000000000..a5587ae02 --- /dev/null +++ b/blocksuite/affine/blocks/code/src/code-toolbar/components/more-menu.ts @@ -0,0 +1,52 @@ +import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar'; +import { renderGroups } from '@blocksuite/affine-components/toolbar'; +import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit'; +import { ShadowlessElement } from '@blocksuite/std'; +import { html } from 'lit'; +import { property } from 'lit/decorators.js'; +import { styleMap } from 'lit/directives/style-map.js'; + +import type { CodeBlockToolbarContext } from '../context.js'; + +export class AffineCodeMoreMenu extends SignalWatcher( + WithDisposable(ShadowlessElement) +) { + override firstUpdated() { + this.disposables.add( + this.context.blockComponent.model.propsUpdated.subscribe(({ key }) => { + if (key === 'wrap' || key === 'lineNumber') { + this.requestUpdate(); + } + }) + ); + } + + override render() { + return html` + +
+ ${renderGroups(this.moreGroups, this.context)} +
+
+ `; + } + + @property({ attribute: false }) + accessor context!: CodeBlockToolbarContext; + + @property({ attribute: false }) + accessor moreGroups!: MenuItemGroup[]; +} + +declare global { + interface HTMLElementTagNameMap { + 'affine-code-more-menu': AffineCodeMoreMenu; + } +} diff --git a/blocksuite/affine/blocks/code/src/code-toolbar/config.ts b/blocksuite/affine/blocks/code/src/code-toolbar/config.ts index b6e4cfa26..e6bcdd1a2 100644 --- a/blocksuite/affine/blocks/code/src/code-toolbar/config.ts +++ b/blocksuite/affine/blocks/code/src/code-toolbar/config.ts @@ -174,7 +174,8 @@ export const toggleGroup: MenuItemGroup = { return html` { - blockComponent.setWrap(!wrapped); + const currentWrap = blockComponent.model.props.wrap; + blockComponent.setWrap(!currentWrap); }} aria-label=${label} > @@ -204,8 +205,10 @@ export const toggleGroup: MenuItemGroup = { return html` { + const currentLineNumber = + blockComponent.model.props.lineNumber ?? true; blockComponent.store.updateBlock(blockComponent.model, { - lineNumber: !lineNumber, + lineNumber: !currentLineNumber, }); }} aria-label=${label} diff --git a/blocksuite/affine/blocks/code/src/effects.ts b/blocksuite/affine/blocks/code/src/effects.ts index 403131c34..31af358c7 100644 --- a/blocksuite/affine/blocks/code/src/effects.ts +++ b/blocksuite/affine/blocks/code/src/effects.ts @@ -5,12 +5,14 @@ import { } from './code-toolbar'; import { AffineCodeToolbar } from './code-toolbar/components/code-toolbar'; import { LanguageListButton } from './code-toolbar/components/lang-button'; +import { AffineCodeMoreMenu } from './code-toolbar/components/more-menu'; import { PreviewButton } from './code-toolbar/components/preview-button'; import { AffineCodeUnit } from './highlight/affine-code-unit'; export function effects() { customElements.define('language-list-button', LanguageListButton); customElements.define('affine-code-toolbar', AffineCodeToolbar); + customElements.define('affine-code-more-menu', AffineCodeMoreMenu); customElements.define(AFFINE_CODE_TOOLBAR_WIDGET, AffineCodeToolbarWidget); customElements.define('affine-code-unit', AffineCodeUnit); customElements.define('affine-code', CodeBlockComponent); @@ -21,6 +23,7 @@ declare global { interface HTMLElementTagNameMap { 'language-list-button': LanguageListButton; 'affine-code-toolbar': AffineCodeToolbar; + 'affine-code-more-menu': AffineCodeMoreMenu; 'preview-button': PreviewButton; [AFFINE_CODE_TOOLBAR_WIDGET]: AffineCodeToolbarWidget; } diff --git a/blocksuite/affine/components/src/toolbar/menu-button.ts b/blocksuite/affine/components/src/toolbar/menu-button.ts index 314ea2250..2ccdbc3b3 100644 --- a/blocksuite/affine/components/src/toolbar/menu-button.ts +++ b/blocksuite/affine/components/src/toolbar/menu-button.ts @@ -187,6 +187,7 @@ export class EditorMenuAction extends LitElement { color: var(--affine-text-primary-color); font-weight: 400; min-height: 30px; // 22 + 8 + user-select: none; } :host(:hover), diff --git a/tests/blocksuite/e2e/code/crud.spec.ts b/tests/blocksuite/e2e/code/crud.spec.ts index a73ee2e82..e0b24a25f 100644 --- a/tests/blocksuite/e2e/code/crud.spec.ts +++ b/tests/blocksuite/e2e/code/crud.spec.ts @@ -286,6 +286,32 @@ test('toggle code block wrap can work', async ({ page }, testInfo) => { ); }); +test('toggle code block wrap can work in the same more menu', async ({ + page, +}) => { + await enterPlaygroundRoom(page); + await initEmptyCodeBlockState(page); + + const codeBlockController = getCodeBlock(page); + const codeBlockContainer = page.locator( + 'affine-code .affine-code-block-container' + ); + + await codeBlockController.codeBlock.hover(); + const moreMenu = await codeBlockController.openMore(); + + await moreMenu.wrapButton.click(); + + await expect(moreMenu.menu).toBeVisible(); + await expect(codeBlockContainer).toHaveClass(/wrap/); + await expect(moreMenu.cancelWrapButton).toBeVisible(); + + await moreMenu.cancelWrapButton.click(); + + await expect(moreMenu.menu).toBeVisible(); + await expect(codeBlockContainer).not.toHaveClass(/wrap/); +}); + test('add caption works', async ({ page }, testInfo) => { await enterPlaygroundRoom(page); await initEmptyCodeBlockState(page); @@ -355,6 +381,34 @@ test('toggle code block line number can work', async ({ page }) => { await expect(lineNumber).toBeVisible(); }); +test('toggle code block line number can work in the same more menu', async ({ + page, +}) => { + await enterPlaygroundRoom(page); + await initEmptyCodeBlockState(page); + await focusRichText(page); + + const lineNumber = page.locator('affine-code .line-number'); + + await expect(lineNumber).toBeVisible(); + + const codeBlockController = getCodeBlock(page); + + await codeBlockController.codeBlock.hover(); + const moreMenu = await codeBlockController.openMore(); + + await moreMenu.cancelLineNumberButton.click(); + + await expect(moreMenu.menu).toBeVisible(); + await expect(lineNumber).toBeHidden(); + await expect(moreMenu.lineNumberButton).toBeVisible(); + + await moreMenu.lineNumberButton.click(); + + await expect(moreMenu.menu).toBeVisible(); + await expect(lineNumber).toBeVisible(); +}); + test('code block toolbar widget can appear and disappear during mousemove', async ({ page, }) => {