import { assertExists } from '@blockeditor/global/utils'; import type { EditorHost } from '@blockeditor/lit'; import { computePosition, flip, offset, shift } from '@floating-ui/dom'; import { html } from 'lit'; import { ref, type RefOrCallback } from 'lit/directives/ref.js'; import { whenHover } from '../../../../../_common/components/hover/index.js'; import { ArrowDownIcon, HighLightDuotoneIcon, TextBackgroundDuotoneIcon, TextForegroundDuotoneIcon, } from '../../../../../_common/icons/index.js'; import type { AffineTextAttributes } from '../../../../../_common/inline/presets/affine-inline-specs.js'; import type { AffineFormatBarWidget } from '../../format-bar.js'; import { backgroundConfig, foregroundConfig } from './consts.js'; enum HighlightType { Foreground, Background, } let lastUsedColor: string | null = null; let lastUsedHighlightType: HighlightType = HighlightType.Background; const updateHighlight = ( host: EditorHost, color: string | null, highlightType: HighlightType ) => { lastUsedColor = color; lastUsedHighlightType = highlightType; const payload: { styles: AffineTextAttributes; } = { styles: { color: highlightType === HighlightType.Foreground ? color : null, background: highlightType === HighlightType.Background ? color : null, }, }; host.std.command .pipe() .withHost() .try(chain => [ chain.getTextSelection().formatText(payload), chain.getBlockSelections().formatBlock(payload), chain.formatNative(payload), ]) .run(); }; const HighlightPanel = ( formatBar: AffineFormatBarWidget, containerRef?: RefOrCallback ) => { return html`
Color
${foregroundConfig.map( ({ name, color }) => html` ${TextForegroundDuotoneIcon} ` )}
Background
${backgroundConfig.map( ({ name, color }) => html` ${TextBackgroundDuotoneIcon} ` )}
`; }; export const HighlightButton = (formatBar: AffineFormatBarWidget) => { const root = formatBar.blockElement.host; const { setFloating, setReference } = whenHover(isHover => { if (!isHover) { const panel = formatBar.shadowRoot?.querySelector('.highlight-panel'); if (!panel) return; panel.style.display = 'none'; return; } const button = formatBar.shadowRoot?.querySelector('.highlight-button'); const panel = formatBar.shadowRoot?.querySelector('.highlight-panel'); assertExists(button); assertExists(panel); panel.style.display = 'block'; computePosition(button, panel, { placement: 'bottom', middleware: [ flip(), offset(10), shift({ padding: 6, }), ], }) .then(({ x, y }) => { panel.style.left = `${x}px`; panel.style.top = `${y}px`; }) .catch(console.error); }); const highlightPanel = HighlightPanel(formatBar, setFloating); return html`
${HighLightDuotoneIcon} ${ArrowDownIcon} ${highlightPanel}
`; };