feat(editor): add collapse/expand functionality to code block component (#14884)

This PR fixes #14040 

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

* **New Features**
* Code blocks can be collapsed and expanded via a toolbar toggle
(visible when the document is editable).
* Collapsed code blocks show a limited preview (~8 lines) with a bottom
fade overlay and reduced padding.
* Toolbar button updates icon and tooltip to reflect collapsed/expanded
state.
* Collapse state is preserved on the block so its current
collapsed/expanded setting is retained.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ahsan Khaleeq
2026-05-04 02:07:42 +05:00
committed by GitHub
parent 5d234ad6a8
commit 9e412f58ec
6 changed files with 106 additions and 1 deletions
@@ -9,6 +9,7 @@ import { WithDisposable } from '@blocksuite/global/lit';
import { noop } from '@blocksuite/global/utils';
import { MoreVerticalIcon } from '@blocksuite/icons/lit';
import { flip, offset } from '@floating-ui/dom';
import { effect } from '@preact/signals-core';
import { css, html, LitElement } from 'lit';
import { property, query, state } from 'lit/decorators.js';
@@ -108,6 +109,17 @@ export class AffineCodeToolbar extends WithDisposable(LitElement) {
this.closeCurrentMenu();
}
override connectedCallback() {
super.connectedCallback();
// Mirror the collapsed$ signal from the block component into local @state
// so this LitElement re-renders when it changes.
this.disposables.add(
effect(() => {
this._collapsed = this.context.blockComponent.collapsed$.value;
})
);
}
override render() {
return html`
<editor-toolbar class="code-toolbar-container" data-without-bg>
@@ -136,6 +148,9 @@ export class AffineCodeToolbar extends WithDisposable(LitElement) {
@state()
private accessor _moreMenuOpen = false;
@state()
private accessor _collapsed = false;
@property({ attribute: false })
accessor context!: CodeBlockToolbarContext;
@@ -1,9 +1,11 @@
import {
CancelWrapIcon,
CaptionIcon,
CollapseCodeIcon,
CopyIcon,
DeleteIcon,
DuplicateIcon,
ExpandCodeIcon,
WrapIcon,
} from '@blocksuite/affine-components/icons';
import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar';
@@ -85,6 +87,38 @@ export const PRIMARY_GROUPS: MenuItemGroup<CodeBlockToolbarContext>[] = [
};
},
},
{
type: 'collapse',
when: ({ doc }) => !doc.readonly,
generate: ({ blockComponent }) => {
return {
action: () => {
blockComponent.setCollapsed(!blockComponent.collapsed$.value);
},
render: item => {
const collapsed = blockComponent.collapsed$.value;
const icon = collapsed ? ExpandCodeIcon : CollapseCodeIcon;
const label = collapsed ? 'Expand code' : 'Collapse code';
return html`
<editor-icon-button
class="code-toolbar-button collapse"
aria-label=${label}
.tooltip=${label}
.tooltipOffset=${4}
.iconSize=${'16px'}
.iconContainerPadding=${4}
@click=${(e: MouseEvent) => {
e.stopPropagation();
item.action();
}}
>
${icon}
</editor-icon-button>
`;
},
};
},
},
{
type: 'caption',
label: 'Caption',