Files
AFFiNE-Mirror/blocksuite/affine/blocks/block-root/src/widgets/element-toolbar/change-brush-button.ts
T
2025-03-08 12:00:34 +08:00

193 lines
5.4 KiB
TypeScript

import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
import type {
EdgelessColorPickerButton,
PickColorEvent,
} from '@blocksuite/affine-components/color-picker';
import {
packColor,
packColorsWithColorScheme,
} from '@blocksuite/affine-components/color-picker';
import type {
BrushElementModel,
BrushProps,
ColorScheme,
} from '@blocksuite/affine-model';
import {
DefaultTheme,
LineWidth,
resolveColor,
} from '@blocksuite/affine-model';
import { FeatureFlagService } from '@blocksuite/affine-shared/services';
import type { ColorEvent } from '@blocksuite/affine-shared/utils';
import { WithDisposable } from '@blocksuite/global/lit';
import { html, LitElement, nothing } from 'lit';
import { property, query } from 'lit/decorators.js';
import { when } from 'lit/directives/when.js';
import countBy from 'lodash-es/countBy';
import maxBy from 'lodash-es/maxBy';
import type { LineWidthEvent } from '../../edgeless/components/panel/line-width-panel.js';
import type { EdgelessRootBlockComponent } from '../../edgeless/edgeless-root-block.js';
function getMostCommonColor(
elements: BrushElementModel[],
colorScheme: ColorScheme
): string {
const colors = countBy(elements, (ele: BrushElementModel) =>
resolveColor(ele.color, colorScheme)
);
const max = maxBy(Object.entries(colors), ([_k, count]) => count);
return max
? (max[0] as string)
: resolveColor(DefaultTheme.black, colorScheme);
}
function getMostCommonSize(elements: BrushElementModel[]): LineWidth {
const sizes = countBy(elements, ele => ele.lineWidth);
const max = maxBy(Object.entries(sizes), ([_k, count]) => count);
return max ? (Number(max[0]) as LineWidth) : LineWidth.Four;
}
function notEqual<K extends keyof BrushProps>(key: K, value: BrushProps[K]) {
return (element: BrushElementModel) => element[key] !== value;
}
export class EdgelessChangeBrushButton extends WithDisposable(LitElement) {
private readonly _setBrushColor = ({ detail }: ColorEvent) => {
const color = detail.value;
this._setBrushProp('color', color);
};
private readonly _setLineWidth = ({ detail: lineWidth }: LineWidthEvent) => {
this._setBrushProp('lineWidth', lineWidth);
};
pickColor = (e: PickColorEvent) => {
const field = 'color';
if (e.type === 'pick') {
const color = e.detail.value;
this.elements.forEach(ele => {
const props = packColor(field, color);
this.crud.updateElement(ele.id, props);
});
return;
}
this.elements.forEach(ele =>
ele[e.type === 'start' ? 'stash' : 'pop'](field)
);
};
get doc() {
return this.edgeless.doc;
}
get service() {
return this.edgeless.service;
}
get surface() {
return this.edgeless.surface;
}
get crud() {
return this.edgeless.std.get(EdgelessCRUDIdentifier);
}
private _setBrushProp<K extends keyof BrushProps>(
key: K,
value: BrushProps[K]
) {
this.doc.captureSync();
this.elements
.filter(notEqual(key, value))
.forEach(element =>
this.crud.updateElement(element.id, { [key]: value })
);
}
override render() {
const colorScheme = this.edgeless.surface.renderer.getColorScheme();
const elements = this.elements;
const selectedColor = getMostCommonColor(elements, colorScheme);
const selectedSize = getMostCommonSize(elements);
return html`
<edgeless-line-width-panel
.selectedSize=${selectedSize}
@select=${this._setLineWidth}
>
</edgeless-line-width-panel>
<editor-toolbar-separator></editor-toolbar-separator>
${when(
this.edgeless.doc
.get(FeatureFlagService)
.getFlag('enable_color_picker'),
() => {
const { type, colors } = packColorsWithColorScheme(
colorScheme,
selectedColor,
elements[0].color
);
return html`
<edgeless-color-picker-button
class="color"
.label="${'Color'}"
.pick=${this.pickColor}
.color=${selectedColor}
.colors=${colors}
.colorType=${type}
.theme=${colorScheme}
>
</edgeless-color-picker-button>
`;
},
() => html`
<editor-menu-button
.contentPadding=${'8px'}
.button=${html`
<editor-icon-button aria-label="Color" .tooltip=${'Color'}>
<edgeless-color-button
.color=${selectedColor}
></edgeless-color-button>
</editor-icon-button>
`}
>
<edgeless-color-panel
.value=${selectedColor}
.theme=${colorScheme}
@select=${this._setBrushColor}
>
</edgeless-color-panel>
</editor-menu-button>
`
)}
`;
}
@query('edgeless-color-picker-button.color')
accessor colorButton!: EdgelessColorPickerButton;
@property({ attribute: false })
accessor edgeless!: EdgelessRootBlockComponent;
@property({ attribute: false })
accessor elements: BrushElementModel[] = [];
}
export function renderChangeBrushButton(
edgeless: EdgelessRootBlockComponent,
elements?: BrushElementModel[]
) {
if (!elements?.length) return nothing;
return html`
<edgeless-change-brush-button .elements=${elements} .edgeless=${edgeless}>
</edgeless-change-brush-button>
`;
}