From edf81014c235ca212d500b29716f49878fd18e08 Mon Sep 17 00:00:00 2001 From: fundon Date: Wed, 10 Jul 2024 17:19:28 +0000 Subject: [PATCH] chore(blocks): refine format bar (#7520) Closes: [BS-741](https://linear.app/affine-design/issue/BS-741/format-bar) Screenshot 2024-07-09 at 10 44 53 Screenshot 2024-07-09 at 10 45 10 Screenshot 2024-07-09 at 15 44 56 --- .../_common/components/toolbar/icon-button.ts | 4 +- .../_common/components/toolbar/menu-button.ts | 6 +- .../src/_common/components/toolbar/toolbar.ts | 7 +- .../blocks/src/_common/components/utils.ts | 6 +- .../format-bar/components/config-renderer.ts | 31 +++-- .../components/highlight/highlight-button.ts | 131 +++++++++--------- .../format-bar/components/paragraph-button.ts | 41 +++--- .../root-block/widgets/format-bar/config.ts | 6 +- .../widgets/format-bar/format-bar.ts | 21 ++- .../root-block/widgets/format-bar/styles.ts | 63 +++------ tests/format-bar.spec.ts | 8 +- 11 files changed, 156 insertions(+), 168 deletions(-) diff --git a/packages/blocks/src/_common/components/toolbar/icon-button.ts b/packages/blocks/src/_common/components/toolbar/icon-button.ts index 14fb1ab56b04..64a2ff799e14 100644 --- a/packages/blocks/src/_common/components/toolbar/icon-button.ts +++ b/packages/blocks/src/_common/components/toolbar/icon-button.ts @@ -32,11 +32,11 @@ export class EditorIconButton extends LitElement { user-select: none; } - .icon-container.active-mode-color[active] { + :host([active]) .icon-container.active-mode-color { color: var(--affine-primary-color); } - .icon-container.active-mode-background[active] { + :host([active]) .icon-container.active-mode-background { background: var(--affine-hover-color); } diff --git a/packages/blocks/src/_common/components/toolbar/menu-button.ts b/packages/blocks/src/_common/components/toolbar/menu-button.ts index 34abe3aaac3e..1d85147da335 100644 --- a/packages/blocks/src/_common/components/toolbar/menu-button.ts +++ b/packages/blocks/src/_common/components/toolbar/menu-button.ts @@ -112,7 +112,6 @@ export class EditorMenuContent extends LitElement { :host([data-show]) { ${PANEL_BASE} justify-content: center; - gap: 8px; padding: var(--content-padding, 0 6px); } @@ -124,6 +123,10 @@ export class EditorMenuContent extends LitElement { min-height: 36px; } + ::slotted([slot]) { + min-width: 146px; + } + ::slotted([slot][data-size='small']) { min-width: 164px; } @@ -162,6 +165,7 @@ export class EditorMenuAction extends LitElement { gap: 8px; color: var(--affine-text-primary-color); font-weight: 400; + min-height: 30px; // 22 + 8 } :host(:hover), diff --git a/packages/blocks/src/_common/components/toolbar/toolbar.ts b/packages/blocks/src/_common/components/toolbar/toolbar.ts index be6c891ab6e3..3050ba0a4b94 100644 --- a/packages/blocks/src/_common/components/toolbar/toolbar.ts +++ b/packages/blocks/src/_common/components/toolbar/toolbar.ts @@ -33,7 +33,12 @@ export class EditorToolbar extends WithDisposable(LitElement) { override connectedCallback() { super.connectedCallback(); - this._disposables.addFromEvent(this, 'pointerdown', stopPropagation); + + this._disposables.addFromEvent(this, 'pointerdown', (e: PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); + }); + this._disposables.addFromEvent(this, 'wheel', stopPropagation); } override render() { diff --git a/packages/blocks/src/_common/components/utils.ts b/packages/blocks/src/_common/components/utils.ts index 8b47c2f43708..1e484554d696 100644 --- a/packages/blocks/src/_common/components/utils.ts +++ b/packages/blocks/src/_common/components/utils.ts @@ -253,11 +253,7 @@ export const scrollbarStyle = (container: string) => { ); // sanitize container name - if ( - container.length > 50 || - container.includes('{') || - container.includes('}') - ) + if (container.includes('{') || container.includes('}')) throw new Error('Invalid container name!'); return css` diff --git a/packages/blocks/src/root-block/widgets/format-bar/components/config-renderer.ts b/packages/blocks/src/root-block/widgets/format-bar/components/config-renderer.ts index 908aec196ee5..4a91819210e8 100644 --- a/packages/blocks/src/root-block/widgets/format-bar/components/config-renderer.ts +++ b/packages/blocks/src/root-block/widgets/format-bar/components/config-renderer.ts @@ -1,5 +1,9 @@ +import '../../../../_common/components/toolbar/icon-button.js'; +import '../../../../_common/components/toolbar/separator.js'; + import { html, type TemplateResult } from 'lit'; +import { renderToolbarSeparator } from '../../../../_common/components/toolbar/separator.js'; import { isFormatSupported } from '../../../../note-block/commands/utils.js'; import type { AffineFormatBarWidget } from '../format-bar.js'; import { HighlightButton } from './highlight/highlight-button.js'; @@ -26,7 +30,7 @@ export function ConfigRenderer(formatBar: AffineFormatBarWidget) { let template: TemplateResult | null = null; switch (item.type) { case 'divider': - template = html`
`; + template = renderToolbarSeparator(); break; case 'highlighter-dropdown': { template = HighlightButton(formatBar); @@ -36,18 +40,19 @@ export function ConfigRenderer(formatBar: AffineFormatBarWidget) { template = ParagraphButton(formatBar); break; case 'inline-action': { - template = html` { - item.action(formatBar.std.command.chain(), formatBar); - formatBar.requestUpdate(); - }} - > - ${typeof item.icon === 'function' ? item.icon() : item.icon} - ${item.name} - `; + template = html` + { + item.action(formatBar.std.command.chain(), formatBar); + formatBar.requestUpdate(); + }} + > + ${typeof item.icon === 'function' ? item.icon() : item.icon} + + `; break; } case 'custom': { diff --git a/packages/blocks/src/root-block/widgets/format-bar/components/highlight/highlight-button.ts b/packages/blocks/src/root-block/widgets/format-bar/components/highlight/highlight-button.ts index 974806b5907d..50cdf865472b 100644 --- a/packages/blocks/src/root-block/widgets/format-bar/components/highlight/highlight-button.ts +++ b/packages/blocks/src/root-block/widgets/format-bar/components/highlight/highlight-button.ts @@ -1,3 +1,6 @@ +import '../../../../../_common/components/toolbar/icon-button.js'; +import '../../../../../_common/components/toolbar/menu-button.js'; + import type { EditorHost } from '@blocksuite/block-std'; import { assertExists } from '@blocksuite/global/utils'; import { computePosition, flip, offset, shift } from '@floating-ui/dom'; @@ -53,51 +56,56 @@ const HighlightPanel = ( formatBar: AffineFormatBarWidget, containerRef?: RefOrCallback ) => { - return html`
- -
Color
- ${foregroundConfig.map( - ({ name, color }) => - html` - - ${TextForegroundDuotoneIcon} - - ` - )} + return html` + +
+ +
Color
+ ${foregroundConfig.map( + ({ name, color }) => html` + + + ${TextForegroundDuotoneIcon} + + ${name} + + ` + )} - -
Background
- ${backgroundConfig.map( - ({ name, color }) => - html` - - ${TextBackgroundDuotoneIcon} - - ` - )} -
`; + +
Background
+ ${backgroundConfig.map( + ({ name, color }) => html` + + + ${TextBackgroundDuotoneIcon} + + ${name} + + ` + )} +
+ + `; }; export const HighlightButton = (formatBar: AffineFormatBarWidget) => { @@ -117,12 +125,12 @@ export const HighlightButton = (formatBar: AffineFormatBarWidget) => { formatBar.shadowRoot?.querySelector('.highlight-panel'); assertExists(button); assertExists(panel); - panel.style.display = 'block'; + panel.style.display = 'flex'; computePosition(button, panel, { placement: 'bottom', middleware: [ flip(), - offset(10), + offset(6), shift({ padding: 6, }), @@ -137,21 +145,20 @@ export const HighlightButton = (formatBar: AffineFormatBarWidget) => { const highlightPanel = HighlightPanel(formatBar, setFloating); - return html`
- - ${HighLightDuotoneIcon} + - ${ArrowDownIcon} - ${highlightPanel} -
`; + + ${HighLightDuotoneIcon} + + ${ArrowDownIcon} + + ${highlightPanel} + + `; }; diff --git a/packages/blocks/src/root-block/widgets/format-bar/components/paragraph-button.ts b/packages/blocks/src/root-block/widgets/format-bar/components/paragraph-button.ts index 2d2f03187430..77cd95c16047 100644 --- a/packages/blocks/src/root-block/widgets/format-bar/components/paragraph-button.ts +++ b/packages/blocks/src/root-block/widgets/format-bar/components/paragraph-button.ts @@ -1,3 +1,6 @@ +import '../../../../_common/components/toolbar/icon-button.js'; +import '../../../../_common/components/toolbar/menu-button.js'; + import type { EditorHost } from '@blocksuite/block-std'; import { assertExists } from '@blocksuite/global/utils'; import { computePosition, flip, offset, shift } from '@floating-ui/dom'; @@ -33,22 +36,22 @@ const ParagraphPanel = ({ const renderedConfig = repeat( config, - item => - html` html` + ${typeof item.icon === 'function' ? item.icon() : item.icon} - ` + ${item.name} + + ` ); - return html`
- ${renderedConfig} -
`; + return html` + +
${renderedConfig}
+
+ `; }; export const ParagraphButton = (formatBar: AffineFormatBarWidget) => { @@ -93,12 +96,12 @@ export const ParagraphButton = (formatBar: AffineFormatBarWidget) => { assertExists(button); assertExists(panel); assertExists(formatQuickBarElement, 'format quick bar should exist'); - panel.style.display = 'block'; + panel.style.display = 'flex'; computePosition(formatQuickBarElement, panel, { placement: 'top-start', middleware: [ flip(), - offset(4), + offset(6), shift({ padding: 6, }), @@ -117,10 +120,12 @@ export const ParagraphButton = (formatBar: AffineFormatBarWidget) => { ref: setFloating, }); - return html`
- - ${paragraphIcon} ${ArrowDownIcon} - ${paragraphPanel} -
`; + return html` +
+ + ${paragraphIcon} ${ArrowDownIcon} + + ${paragraphPanel} +
+ `; }; diff --git a/packages/blocks/src/root-block/widgets/format-bar/config.ts b/packages/blocks/src/root-block/widgets/format-bar/config.ts index b4ce1df18220..22c01aa1ff9d 100644 --- a/packages/blocks/src/root-block/widgets/format-bar/config.ts +++ b/packages/blocks/src/root-block/widgets/format-bar/config.ts @@ -12,7 +12,6 @@ import { CodeIcon, CopyIcon, DatabaseTableViewIcon20, - FontLinkedDocIcon, Heading1Icon, Heading2Icon, Heading3Icon, @@ -20,6 +19,7 @@ import { Heading5Icon, Heading6Icon, ItalicIcon, + LinkedDocIcon, LinkIcon, NumberedListIcon, QuoteIcon, @@ -143,6 +143,7 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) { }, showWhen: () => true, }) + .addDivider() .addInlineAction({ id: 'convert-to-database', name: 'Group as Database', @@ -169,10 +170,11 @@ export function toolbarDefaultConfig(toolbar: AffineFormatBarWidget) { ); }, }) + .addDivider() .addInlineAction({ id: 'convert-to-linked-doc', name: 'Create Linked Doc', - icon: FontLinkedDocIcon, + icon: LinkedDocIcon, isActive: () => false, action: (chain, formatBar) => { const [_, ctx] = chain diff --git a/packages/blocks/src/root-block/widgets/format-bar/format-bar.ts b/packages/blocks/src/root-block/widgets/format-bar/format-bar.ts index 8250889d4c9c..9774716d6a94 100644 --- a/packages/blocks/src/root-block/widgets/format-bar/format-bar.ts +++ b/packages/blocks/src/root-block/widgets/format-bar/format-bar.ts @@ -1,4 +1,5 @@ import '../../../_common/components/button.js'; +import '../../../_common/components/toolbar/toolbar.js'; import type { BaseSelection, @@ -24,7 +25,6 @@ import { type RichText, } from '../../../_common/components/index.js'; import type { AffineTextAttributes } from '../../../_common/inline/presets/affine-inline-specs.js'; -import { stopPropagation } from '../../../_common/utils/event.js'; import { matchFlavours } from '../../../_common/utils/model.js'; import { isFormatSupported } from '../../../note-block/commands/utils.js'; import { isRootElement } from '../../../root-block/utils/guard.js'; @@ -41,6 +41,8 @@ export const AFFINE_FORMAT_BAR_WIDGET = 'affine-format-bar-widget'; @customElement(AFFINE_FORMAT_BAR_WIDGET) export class AffineFormatBarWidget extends WidgetElement { + static override styles = formatBarStyle; + private get _selectionManager() { return this.host.selection; } @@ -59,8 +61,6 @@ export class AffineFormatBarWidget extends WidgetElement { return sl.getRangeAt(0); } - static override styles = formatBarStyle; - @state() private accessor _dragging = false; @@ -568,16 +568,11 @@ export class AffineFormatBarWidget extends WidgetElement { const items = ConfigRenderer(this); - return html`
- ${items} -
`; + return html` + + ${items} + + `; } } diff --git a/packages/blocks/src/root-block/widgets/format-bar/styles.ts b/packages/blocks/src/root-block/widgets/format-bar/styles.ts index e3b94aa7bae1..27222b37b446 100644 --- a/packages/blocks/src/root-block/widgets/format-bar/styles.ts +++ b/packages/blocks/src/root-block/widgets/format-bar/styles.ts @@ -11,22 +11,6 @@ const paragraphButtonStyle = css` transform: rotate(180deg); } - .paragraph-panel { - display: none; - - font-size: var(--affine-font-sm); - box-sizing: border-box; - position: absolute; - min-width: 173px; - padding: 8px 4px; - overflow-y: auto; - - background: var(--affine-background-overlay-panel-color); - box-shadow: var(--affine-shadow-2); - border-radius: 8px; - z-index: var(--affine-z-index-popover); - } - .highlight-icon > svg:nth-child(2) { transition-duration: 0.3s; } @@ -35,53 +19,38 @@ const paragraphButtonStyle = css` } .highlight-panel { - display: none; - - font-size: var(--affine-font-sm); - box-sizing: border-box; - position: absolute; - min-width: 178px; - padding: 8px 4px 8px 8px; max-height: 380px; - overflow-y: auto; - - background: var(--affine-background-overlay-panel-color); - box-shadow: var(--affine-shadow-2); - border-radius: 8px; - z-index: var(--affine-z-index-popover); } - ${scrollbarStyle('.highlight-panel')} - .highligh-panel-heading { + display: flex; color: var(--affine-text-secondary-color); padding: 4px; } + + editor-menu-content { + display: none; + position: absolute; + padding: 0; + z-index: var(--affine-z-index-popover); + --packed-height: 6px; + } + + editor-menu-content > div[data-orientation='vertical'] { + padding: 8px; + overflow-y: auto; + } + + ${scrollbarStyle('editor-menu-content > div[data-orientation="vertical"]')} `; export const formatBarStyle = css` .affine-format-bar-widget { - box-sizing: border-box; position: absolute; display: none; - align-items: center; - padding: 4px 8px; - gap: 8px; - height: 40px; - width: max-content; - - border-radius: 8px; - background: var(--affine-background-overlay-panel-color); - box-shadow: var(--affine-shadow-2); z-index: var(--affine-z-index-popover); user-select: none; } - .divider { - width: 1px; - height: 24px; - background-color: var(--affine-border-color); - } - ${paragraphButtonStyle} `; diff --git a/tests/format-bar.spec.ts b/tests/format-bar.spec.ts index 2d378355c9ff..b0344db6b747 100644 --- a/tests/format-bar.spec.ts +++ b/tests/format-bar.spec.ts @@ -93,7 +93,7 @@ test('should format quick bar show when clicking drag handle', async ({ if (!box) { throw new Error("formatBar doesn't exist"); } - assertAlmostEqual(box.x, 222.5, 5); + assertAlmostEqual(box.x, 251, 5); assertAlmostEqual(box.y - dragHandleRect.y, -55.5, 5); }); @@ -996,7 +996,7 @@ test('should format quick bar work in single block selection', async ({ const selectionRect = await blockSelections.boundingBox(); assertExists(formatRect); assertExists(selectionRect); - assertAlmostEqual(formatRect.x - selectionRect.x, 118.5, 10); + assertAlmostEqual(formatRect.x - selectionRect.x, 147.5, 10); assertAlmostEqual(formatRect.y - selectionRect.y, 33, 10); const boldBtn = formatBar.getByTestId('bold'); @@ -1089,7 +1089,7 @@ test('should format quick bar work in multiple block selection', async ({ } const rect = await blockSelections.first().boundingBox(); assertExists(rect); - assertAlmostEqual(box.x - rect.x, 118.5, 10); + assertAlmostEqual(box.x - rect.x, 147.5, 10); assertAlmostEqual(box.y - rect.y, 99, 10); await formatBarController.boldBtn.click(); @@ -1292,7 +1292,7 @@ test('should format quick bar show after convert to code block', async ({ { x: 0, y: 0 } ); await expect(formatBarController.formatBar).toBeVisible(); - await formatBarController.assertBoundingBox(222.5, 343); + await formatBarController.assertBoundingBox(251, 343); await formatBarController.openParagraphMenu(); await formatBarController.codeBlockBtn.click();