import { DefaultTheme, isTransparent, type Palette, type ShapeName, ShapeStyle, ShapeType, } from '@blocksuite/affine-model'; import { EditPropsStore, FeatureFlagService, ThemeProvider, } from '@blocksuite/affine-shared/services'; import type { ColorEvent } from '@blocksuite/affine-shared/utils'; import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit'; import { StyleGeneralIcon, StyleScribbleIcon } from '@blocksuite/icons/lit'; import type { BlockComponent } from '@blocksuite/std'; import { GfxControllerIdentifier, type ToolOptionWithType, } from '@blocksuite/std/gfx'; import { computed, effect, type Signal, signal } from '@preact/signals-core'; import { css, html, LitElement } from 'lit'; import { property } from 'lit/decorators.js'; import { when } from 'lit/directives/when.js'; import { ShapeTool } from '../shape-tool'; import { ShapeComponentConfig } from '../toolbar'; export class EdgelessShapeMenu extends SignalWatcher( WithDisposable(LitElement) ) { static override styles = css` :host { display: flex; z-index: -1; } .menu-content { display: flex; align-items: center; } .shape-type-container, .shape-style-container { display: flex; align-items: center; justify-content: center; gap: 14px; } .shape-type-container svg, .shape-style-container svg { fill: var(--affine-icon-color); stroke: none; } menu-divider { height: 24px; margin: 0 9px; } `; private readonly _shapeName$: Signal = signal(ShapeType.Rect); @property({ attribute: false }) accessor edgeless!: BlockComponent; private readonly _props$ = computed(() => { const shapeName: ShapeName = this._shapeName$.value; const { shapeStyle, fillColor, strokeColor, radius } = this.edgeless.std.get(EditPropsStore).lastProps$.value[ `shape:${shapeName}` ]; return { shapeStyle, shapeName, fillColor, strokeColor, radius, }; }); private readonly _setFillColor = ({ key, value }: Palette) => { const filled = !isTransparent(value); const fillColor = value; const strokeColor = filled ? DefaultTheme.StrokeColorShortPalettes.find( palette => palette.key === key )?.value : DefaultTheme.StrokeColorShortMap.Grey; const { shapeName } = this._props$.value; this.edgeless.std .get(EditPropsStore) .recordLastProps(`shape:${shapeName}`, { filled, fillColor, strokeColor, }); this.onChange(shapeName); }; private readonly _setShapeStyle = (shapeStyle: ShapeStyle) => { const { shapeName } = this._props$.value; this.edgeless.std .get(EditPropsStore) .recordLastProps(`shape:${shapeName}`, { shapeStyle, }); this.onChange(shapeName); }; private readonly _theme$ = computed(() => { return this.edgeless.std.get(ThemeProvider).theme$.value; }); override connectedCallback(): void { super.connectedCallback(); const gfx = this.edgeless.std.get(GfxControllerIdentifier); this._disposables.add( effect(() => { const value = gfx.tool.currentToolOption$.value; if (value && value.toolType === ShapeTool) { const shapeName = (value as ToolOptionWithType).options ?.shapeName; if (shapeName) { this._shapeName$.value = shapeName; } } }) ); } override render() { const { fillColor, shapeStyle, shapeName } = this._props$.value; return html` `; } @property({ attribute: false }) accessor onChange!: (name: ShapeName) => void; }