From b64a502add5ef4c12b23a313889cafd5b04632bc Mon Sep 17 00:00:00 2001 From: Chen <99816898+donteatfriedrice@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:30:17 +0800 Subject: [PATCH] feat(edgeless): add change note scale button (#6335) --- packages/blocks/src/_common/icons/edgeless.ts | 77 +++++-- .../change-embed-card-button.ts | 108 +++++++++- .../component-toolbar/change-note-button.ts | 84 +++++++- .../edgeless/components/panel/scale-panel.ts | 200 ++++++++++++++++++ 4 files changed, 436 insertions(+), 33 deletions(-) create mode 100644 packages/blocks/src/root-block/edgeless/components/panel/scale-panel.ts diff --git a/packages/blocks/src/_common/icons/edgeless.ts b/packages/blocks/src/_common/icons/edgeless.ts index fde1453979b8..87926c9e2a98 100644 --- a/packages/blocks/src/_common/icons/edgeless.ts +++ b/packages/blocks/src/_common/icons/edgeless.ts @@ -261,17 +261,39 @@ export const ExpandIcon = html` width="20" height="20" viewBox="0 0 20 20" - fill="none" + fill="currentColor" xmlns="http://www.w3.org/2000/svg" > - + + + + + + + `; + export const NavigatorSettingsIcon = html` - - + + + + + + + `; diff --git a/packages/blocks/src/root-block/edgeless/components/component-toolbar/change-embed-card-button.ts b/packages/blocks/src/root-block/edgeless/components/component-toolbar/change-embed-card-button.ts index 8e32903c055e..f5aa846fc964 100644 --- a/packages/blocks/src/root-block/edgeless/components/component-toolbar/change-embed-card-button.ts +++ b/packages/blocks/src/root-block/edgeless/components/component-toolbar/change-embed-card-button.ts @@ -6,7 +6,15 @@ import type { BlockStdScope } from '@blocksuite/block-std'; import { assertExists } from '@blocksuite/global/utils'; import type { EditorHost } from '@blocksuite/lit'; import { WithDisposable } from '@blocksuite/lit'; -import { css, html, LitElement, nothing, type TemplateResult } from 'lit'; +import { baseTheme } from '@toeverything/theme'; +import { + css, + html, + LitElement, + nothing, + type TemplateResult, + unsafeCSS, +} from 'lit'; import { customElement, property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; @@ -16,7 +24,10 @@ import { EMBED_CARD_HEIGHT, EMBED_CARD_WIDTH, } from '../../../../_common/consts.js'; -import { BookmarkIcon } from '../../../../_common/icons/edgeless.js'; +import { + BookmarkIcon, + SmallArrowDownIcon, +} from '../../../../_common/icons/edgeless.js'; import { CaptionIcon, CopyIcon, @@ -121,7 +132,7 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { font-feature-settings: 'clig' off, 'liga' off; - font-family: var(--affine-font-family); + font-family: ${unsafeCSS(baseTheme.fontSansFamily)}; font-size: 15px; font-style: normal; font-weight: 400; @@ -161,7 +172,7 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { 'clig' off, 'liga' off; word-break: break-all; - font-family: var(--affine-font-family); + font-family: ${unsafeCSS(baseTheme.fontSansFamily)}; font-size: 14px; font-style: normal; font-weight: 400; @@ -201,10 +212,30 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { height: 24px; } - card-style-panel { + .embed-synced-doc-scale-button { + display: flex; + border-radius: 4px; + align-items: center; + gap: 2px; + padding: 2px; + font-size: var(--affine-font-sm); + font-weight: 500; + color: var(--affine-text-secondary-color); + height: 26px; + } + + .embed-synced-doc-scale-label { + display: flex; + padding: 2px 0px 2px 4px; + align-items: center; + } + + card-style-panel, + edgeless-scale-panel { display: none; } - card-style-panel[data-show] { + card-style-panel[data-show], + edgeless-scale-panel[data-show] { display: flex; } `; @@ -235,6 +266,14 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { @query('card-style-panel') private _cardStylePanel!: HTMLDivElement; + @query('.embed-synced-doc-scale-button') + private _embedSyncedDocScaleButton!: HTMLDivElement; + @query('edgeless-scale-panel') + private _embedSyncedDocScalePanel!: HTMLDivElement; + private _embedSyncedDocScalePopper: ReturnType< + typeof createButtonPopper + > | null = null; + private get _doc() { return this.model.doc; } @@ -512,6 +551,21 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { this._doc.deleteBlock(this.model); } + private _getScale(model: EmbedSyncedDocModel) { + return model.scale ?? 1; + } + + private _setEmbedSyncedDocScale(scale: number) { + if (!isEmbedSyncedDocBlock(this.model)) return; + const bound = Bound.deserialize(this.model.xywh); + const oldScale = this.model.scale ?? 1; + const ratio = scale / oldScale; + bound.w *= ratio; + bound.h *= ratio; + const xywh = bound.serialize(); + this.model.doc.updateBlock(this.model, { scale, xywh }); + } + override updated(changedProperties: Map) { this._cardStylePopper?.dispose(); if (this._canShowCardStylePanel) { @@ -525,6 +579,17 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { this._disposables.add(this._cardStylePopper); } + if (isEmbedSyncedDocBlock(this.model)) { + this._embedSyncedDocScalePopper = createButtonPopper( + this._embedSyncedDocScaleButton, + this._embedSyncedDocScalePanel, + ({ display }) => { + this._showPopper = display === 'show'; + } + ); + this._disposables.add(this._embedSyncedDocScalePopper); + } + super.updated(changedProperties); } @@ -677,6 +742,37 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { ${CaptionIcon} + ${isEmbedSyncedDocBlock(model) + ? html` + + + this._embedSyncedDocScalePopper?.toggle()} + > + + ${Math.round(this._getScale(model) * 100) + '%'} + ${SmallArrowDownIcon} + + + { + this._setEmbedSyncedDocScale(scale); + }} + .onPopperCose=${() => this._embedSyncedDocScalePopper?.hide()} + > + ` + : nothing} + diff --git a/packages/blocks/src/root-block/edgeless/components/component-toolbar/change-note-button.ts b/packages/blocks/src/root-block/edgeless/components/component-toolbar/change-note-button.ts index 6bdc01022b22..a0e9c43585f8 100644 --- a/packages/blocks/src/root-block/edgeless/components/component-toolbar/change-note-button.ts +++ b/packages/blocks/src/root-block/edgeless/components/component-toolbar/change-note-button.ts @@ -4,6 +4,8 @@ import '../panel/color-panel.js'; import './component-toolbar-menu-divider.js'; import '../panel/note-shadow-panel.js'; import '../panel/note-display-mode-panel.js'; +import '../panel/scale-panel.js'; +import '../panel/size-panel.js'; import { assertExists } from '@blocksuite/global/utils'; import { WithDisposable } from '@blocksuite/lit'; @@ -86,7 +88,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { align-items: center; gap: 4px; padding: 2px; - font-size: 12px; + font-size: var(--affine-font-xs); font-weight: 500; line-height: 20px; } @@ -95,7 +97,8 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { white-space: nowrap; } - .display-mode-button { + .display-mode-button, + .note-scale-button { display: flex; border-radius: 4px; background-color: var(--affine-hover-color); @@ -104,13 +107,22 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { padding: 2px; } - .current-mode-label { + .note-scale-button { + font-size: var(--affine-font-sm); + font-weight: 500; + color: var(--affine-text-secondary-color); + height: 26px; + } + + .current-mode-label, + .current-scale-label { display: flex; padding: 2px 0px 2px 4px; align-items: center; } - edgeless-size-panel { + edgeless-size-panel, + edgeless-scale-panel { border-radius: 8px; } @@ -126,14 +138,16 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { note-display-mode-panel, edgeless-note-shadow-panel, - edgeless-size-panel { + edgeless-size-panel, + edgeless-scale-panel { display: none; } note-display-mode-panel[data-show], edgeless-note-shadow-panel[data-show], edgeless-color-panel[data-show], - edgeless-size-panel[data-show] { + edgeless-size-panel[data-show], + edgeless-scale-panel[data-show] { display: flex; } @@ -206,6 +220,12 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { private _displayModePopper: ReturnType | null = null; + @query('.note-scale-button') + private _noteScaleButton!: HTMLDivElement; + @query('edgeless-scale-panel') + private _noteScalePanel!: HTMLDivElement; + private _noteScalePopper: ReturnType | null = null; + private get doc() { return this.surface.doc; } @@ -302,6 +322,21 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { }); }; + private _setNoteScale = (scale: number) => { + this.notes.forEach(note => { + this.doc.updateBlock(note, () => { + const bound = Bound.deserialize(note.xywh); + const oldScale = note.edgeless.scale ?? 1; + const ratio = scale / oldScale; + bound.w *= ratio; + bound.h *= ratio; + const xywh = bound.serialize(); + note.xywh = xywh; + note.edgeless.scale = scale; + }); + }); + }; + private _setCollapse() { this.notes.forEach(note => { const { collapse, collapsedHeight } = note.edgeless; @@ -343,6 +378,10 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { } } + private _getScaleLabel(scale: number) { + return Math.round(scale * 100) + '%'; + } + override updated(_changedProperties: PropertyValues) { const { _disposables } = this; if (_changedProperties.has('_queryCache')) { @@ -391,6 +430,15 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { } ); _disposables.add(this._borderRadiusPopper); + + this._noteScalePopper = createButtonPopper( + this._noteScaleButton, + this._noteScalePanel, + ({ display }) => { + this._showPopper = display === 'show'; + } + ); + _disposables.add(this._noteScalePopper); } } @@ -417,6 +465,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { edgeless.style; const { collapse } = edgeless; + const scale = edgeless.scale ?? 1; const currentMode = this._getCurrentModeLabel(displayMode); return html` @@ -533,7 +582,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { { this._setCollapse(); @@ -542,6 +591,27 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { ${collapse ? ExpandIcon : ShrinkIcon} + this._noteScalePopper?.toggle()} + > + + ${this._getScaleLabel(scale)} + ${SmallArrowDownIcon} + + + + { + this._setNoteScale(scale); + }} + .onPopperCose=${() => this._noteScalePopper?.hide()} + > + ${length === 1 ? html` void; + + @property({ attribute: false }) + onPopperCose?: () => void; + + @property({ attribute: false }) + minScale: number = MIN_SCALE; + + @property({ attribute: false }) + maxScale: number = MAX_SCALE; + + private _onSelect(scale: EdgelessScalePanel['scale']) { + if (this.onSelect) this.onSelect(scale / 100); + } + + private _onPopperClose() { + if (this.onPopperCose) this.onPopperCose(); + } + + private _onKeydown = (e: KeyboardEvent) => { + e.stopPropagation(); + + if (e.key === 'Enter' && !e.isComposing) { + e.preventDefault(); + const input = e.target as HTMLInputElement; + // Handle edge case where user enters a non-number + if (isNaN(parseInt(input.value))) { + input.value = ''; + return; + } + + let size = parseInt(input.value); + // Handle edge case when user enters a number that is out of range + if (size < this.minScale) { + size = this.minScale; + } else if (size > this.maxScale) { + size = this.maxScale; + } + + this._onSelect(size); + input.value = ''; + this._onPopperClose(); + } + }; + + override render() { + return html` + + { + e.preventDefault(); + e.stopPropagation(); + }} + > + + + + ${repeat( + this.scales, + scale => scale, + scale => + html` { + this._onSelect(scale); + }} + > + ${scale + '%'} + ` + )} + + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'edgeless-scale-panel': EdgelessScalePanel; + } +}