Skip to content

Commit

Permalink
refactor: extract toggle button in list block (#8795)
Browse files Browse the repository at this point in the history
  • Loading branch information
Flrande authored Nov 25, 2024
1 parent acc6b56 commit ed802f3
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 141 deletions.
112 changes: 55 additions & 57 deletions packages/affine/block-list/src/list-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ import type { BaseSelection, BlockComponent } from '@blocksuite/block-std';
import type { InlineRangeProvider } from '@blocksuite/inline';

import { CaptionedBlockComponent } from '@blocksuite/affine-components/caption';
import {
playCheckAnimation,
toggleDown,
toggleRight,
} from '@blocksuite/affine-components/icons';
import { playCheckAnimation } from '@blocksuite/affine-components/icons';
import {
DefaultInlineManagerExtension,
type RichText,
} from '@blocksuite/affine-components/rich-text';
import '@blocksuite/affine-shared/commands';
import { TOGGLE_BUTTON_PARENT_CLASS } from '@blocksuite/affine-components/toggle-button';
import {
BLOCK_CHILDREN_CONTAINER_PADDING_LEFT,
NOTE_SELECTOR,
Expand All @@ -23,6 +20,8 @@ import { getInlineRangeProvider } from '@blocksuite/block-std';
import { effect } from '@preact/signals-core';
import { html, nothing, type TemplateResult } from 'lit';
import { query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';

import type { ListBlockService } from './list-service.js';

Expand All @@ -42,7 +41,15 @@ export class ListBlockComponent extends CaptionedBlockComponent<
e.stopPropagation();

if (this.model.type === 'toggle') {
this._toggleChildren();
if (this.doc.readonly) {
this._readonlyCollapsed = !this._readonlyCollapsed;
} else {
this.doc.captureSync();
this.doc.updateBlock(this.model, {
collapsed: !this.model.collapsed,
});
}

return;
} else if (this.model.type === 'todo') {
if (this.doc.readonly) return;
Expand Down Expand Up @@ -97,47 +104,17 @@ export class ListBlockComponent extends CaptionedBlockComponent<
});
}

private _toggleChildren() {
if (this.doc.readonly) {
this._isCollapsedWhenReadOnly = !this._isCollapsedWhenReadOnly;
return;
}
const newCollapsedState = !this.model.collapsed;
this._isCollapsedWhenReadOnly = newCollapsedState;
this.doc.captureSync();
this.doc.updateBlock(this.model, {
collapsed: newCollapsedState,
} as Partial<ListBlockModel>);
}

private _toggleTemplate(isCollapsed: boolean) {
const noChildren = this.model.children.length === 0;
if (noChildren) return nothing;

const toggleDownTemplate = html`<div
contenteditable="false"
class="toggle-icon"
@click=${this._toggleChildren}
>
${toggleDown}
</div>`;

const toggleRightTemplate = html`<div
contenteditable="false"
class="toggle-icon toggle-icon__collapsed"
@click=${this._toggleChildren}
>
${toggleRight}
</div>`;

return isCollapsed ? toggleRightTemplate : toggleDownTemplate;
}

override connectedCallback() {
super.connectedCallback();

this._inlineRangeProvider = getInlineRangeProvider(this);
this._isCollapsedWhenReadOnly = this.model.collapsed;

this.disposables.add(
effect(() => {
const collapsed = this.model.collapsed$.value;
this._readonlyCollapsed = collapsed;
})
);

this.disposables.add(
effect(() => {
Expand All @@ -164,28 +141,49 @@ export class ListBlockComponent extends CaptionedBlockComponent<
override renderBlock(): TemplateResult<1> {
const { model, _onClickIcon } = this;
const collapsed = this.doc.readonly
? this._isCollapsedWhenReadOnly
: !!model.collapsed;
const listIcon = getListIcon(model, !collapsed, _onClickIcon);
? this._readonlyCollapsed
: model.collapsed;

const checked =
this.model.type === 'todo' && this.model.checked
? 'affine-list--checked'
: '';
const listIcon = getListIcon(model, !collapsed, _onClickIcon);

const children = html`<div
class="affine-block-children-container ${collapsed
? 'affine-list__collapsed'
: ''}"
style="padding-left: ${BLOCK_CHILDREN_CONTAINER_PADDING_LEFT}px;"
class="affine-block-children-container"
style=${styleMap({
paddingLeft: `${BLOCK_CHILDREN_CONTAINER_PADDING_LEFT}px`,
display: collapsed ? 'none' : undefined,
})}
>
${this.renderChildren(this.model)}
</div>`;

return html`
<div class=${'affine-list-block-container'}>
<div class=${`affine-list-rich-text-wrapper ${checked}`}>
${this._toggleTemplate(collapsed)} ${listIcon}
<div
class=${classMap({
'affine-list-rich-text-wrapper': true,
'affine-list--checked':
this.model.type === 'todo' && this.model.checked,
[TOGGLE_BUTTON_PARENT_CLASS]: true,
})}
>
${this.model.children.length > 0
? html`
<blocksuite-toggle-button
.collapsed=${collapsed}
.updateCollapsed=${(value: boolean) => {
if (this.doc.readonly) {
this._readonlyCollapsed = value;
} else {
this.doc.captureSync();
this.doc.updateBlock(this.model, {
collapsed: value,
});
}
}}
></blocksuite-toggle-button>
`
: nothing}
${listIcon}
<rich-text
.yText=${this.model.text.yText}
.inlineEventSource=${this.topContenteditableElement ?? nothing}
Expand All @@ -209,7 +207,7 @@ export class ListBlockComponent extends CaptionedBlockComponent<
}

@state()
private accessor _isCollapsedWhenReadOnly = false;
private accessor _readonlyCollapsed = false;

@query('rich-text')
private accessor _richTextElement: RichText | null = null;
Expand Down
8 changes: 0 additions & 8 deletions packages/affine/block-list/src/list-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,9 @@ import { matchFlavours } from '@blocksuite/affine-shared/utils';
import { BlockService } from '@blocksuite/block-std';

import { correctNumberedListsOrderToPrev } from './commands/utils.js';
import { listPrefix, toggleStyles } from './styles.js';
import { getListIcon } from './utils/get-list-icon.js';

export class ListBlockService extends BlockService {
static override readonly flavour = ListBlockSchema.model.flavour;

readonly styles = {
icon: getListIcon,
prefix: listPrefix,
toggle: toggleStyles,
};
}

export const ListDragHandleOption = DragHandleConfigExtension({
Expand Down
37 changes: 0 additions & 37 deletions packages/affine/block-list/src/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,42 +34,6 @@ export const listPrefix = css`
}
`;

export const toggleStyles = css`
.toggle-icon {
display: flex;
align-items: center;
height: 16px;
margin: 4px 0;
position: absolute;
left: 0;
transform: translateX(-100%);
border-radius: 4px;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.toggle-icon:hover {
background: var(--affine-hover-color);
}
.affine-list-rich-text-wrapper:hover .toggle-icon {
opacity: 1;
}
.toggle-icon__collapsed {
opacity: 1;
}
.with-drag-handle .affine-list-rich-text-wrapper .toggle-icon {
opacity: 1;
}
.with-drag-handle .affine-block-children-container .toggle-icon {
opacity: 0;
}
.affine-list__collapsed {
display: none;
}
`;

export const listBlockStyles = css`
affine-list {
display: block;
Expand Down Expand Up @@ -97,5 +61,4 @@ export const listBlockStyles = css`
}
${listPrefix}
${toggleStyles}
`;
7 changes: 6 additions & 1 deletion packages/affine/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"./context-menu": "./src/context-menu/index.ts",
"./date-picker": "./src/date-picker/index.ts",
"./drag-indicator": "./src/drag-indicator/index.ts",
"./virtual-keyboard": "./src/virtual-keyboard/index.ts"
"./virtual-keyboard": "./src/virtual-keyboard/index.ts",
"./toggle-button": "./src/toggle-button/index.ts"
},
"publishConfig": {
"access": "public",
Expand Down Expand Up @@ -110,6 +111,10 @@
"./virtual-keyboard": {
"types": "./dist/virtual-keyboard/index.d.ts",
"import": "./dist/virtual-keyboard/index.js"
},
"./toggle-button": {
"types": "./dist/toggle-button/index.d.ts",
"import": "./dist/toggle-button/index.js"
}
}
},
Expand Down
7 changes: 7 additions & 0 deletions packages/affine/components/src/toggle-button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { TOGGLE_BUTTON_PARENT_CLASS, ToggleButton } from './toggle-button.js';

export function effects() {
customElements.define('blocksuite-toggle-button', ToggleButton);
}

export { TOGGLE_BUTTON_PARENT_CLASS, ToggleButton };
82 changes: 82 additions & 0 deletions packages/affine/components/src/toggle-button/toggle-button.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { ShadowlessElement } from '@blocksuite/block-std';
import { WithDisposable } from '@blocksuite/global/utils';
import { css, unsafeCSS } from 'lit';
import { property } from 'lit/decorators.js';
import { html } from 'lit-html';

import { toggleDown, toggleRight } from '../icons/list.js';

export const TOGGLE_BUTTON_PARENT_CLASS = 'blocksuite-toggle-button-parent';

export class ToggleButton extends WithDisposable(ShadowlessElement) {
static override styles = css`
.toggle-icon {
display: flex;
align-items: center;
height: 16px;
margin: 4px 0;
position: absolute;
left: 0;
transform: translateX(-100%);
border-radius: 4px;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.toggle-icon:hover {
background: var(--affine-hover-color);
}
.toggle-icon[data-collapsed='true'] {
opacity: 1;
}
.${unsafeCSS(TOGGLE_BUTTON_PARENT_CLASS)}:hover .toggle-icon {
opacity: 1;
}
.with-drag-handle .toggle-icon {
opacity: 1;
}
.with-drag-handle .affine-block-children-container .toggle-icon {
opacity: 0;
}
`;

override render() {
const toggleDownTemplate = html`
<div
contenteditable="false"
class="toggle-icon"
@click=${() => this.updateCollapsed(!this.collapsed)}
>
${toggleDown}
</div>
`;

const toggleRightTemplate = html`
<div
contenteditable="false"
class="toggle-icon"
data-collapsed=${this.collapsed}
@click=${() => this.updateCollapsed(!this.collapsed)}
>
${toggleRight}
</div>
`;

return this.collapsed ? toggleRightTemplate : toggleDownTemplate;
}

@property({ attribute: false })
accessor collapsed!: boolean;

@property({ attribute: false })
accessor updateCollapsed!: (collapsed: boolean) => void;
}

declare global {
interface HTMLElementTagNameMap {
'blocksuite-toggle-button': ToggleButton;
}
}
2 changes: 2 additions & 0 deletions packages/blocks/src/effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { effects as componentDatePickerEffects } from '@blocksuite/affine-compon
import { effects as componentDragIndicatorEffects } from '@blocksuite/affine-components/drag-indicator';
import { effects as componentPortalEffects } from '@blocksuite/affine-components/portal';
import { effects as componentRichTextEffects } from '@blocksuite/affine-components/rich-text';
import { effects as componentToggleButtonEffects } from '@blocksuite/affine-components/toggle-button';
import { effects as componentToolbarEffects } from '@blocksuite/affine-components/toolbar';
import { effects as widgetScrollAnchoringEffects } from '@blocksuite/affine-widget-scroll-anchoring/effects';
import { effects as stdEffects } from '@blocksuite/block-std/effects';
Expand Down Expand Up @@ -334,6 +335,7 @@ export function effects() {
componentRichTextEffects();
componentToolbarEffects();
componentDragIndicatorEffects();
componentToggleButtonEffects();

widgetScrollAnchoringEffects();
widgetMobileToolbarEffects();
Expand Down
Loading

0 comments on commit ed802f3

Please sign in to comment.