diff --git a/packages/affine/components/src/icons/edgeless.ts b/packages/affine/components/src/icons/edgeless.ts index d6ecfa738afe8..69bac3ad8ce8a 100644 --- a/packages/affine/components/src/icons/edgeless.ts +++ b/packages/affine/components/src/icons/edgeless.ts @@ -429,21 +429,6 @@ export const ViewBarIcon = icons.ViewBarIcon({ height: '24', }); -export const TransparentIcon = html` - -`; - export const MoreHorizontalIcon = icons.MoreHorizontalIcon({ width: '24', height: '24', diff --git a/packages/affine/model/src/consts/line.ts b/packages/affine/model/src/consts/line.ts index 83ff57c90232f..26e5312342973 100644 --- a/packages/affine/model/src/consts/line.ts +++ b/packages/affine/model/src/consts/line.ts @@ -1,18 +1,29 @@ +/* eslint perfectionist/sort-enums: "off" */ + import { z } from 'zod'; import { createEnumMap } from '../utils/enum.js'; export enum LineWidth { - Eight = 8, + Two = 2, // Thin Four = 4, Six = 6, + Eight = 8, // Thick Ten = 10, Twelve = 12, - Two = 2, } +export const LINE_WIDTHS = [ + LineWidth.Two, + LineWidth.Four, + LineWidth.Six, + LineWidth.Eight, + LineWidth.Ten, + LineWidth.Twelve, +]; + export enum LineColor { Black = '--affine-palette-line-black', Blue = '--affine-palette-line-blue', diff --git a/packages/affine/shared/src/theme/color.ts b/packages/affine/shared/src/theme/color.ts new file mode 100644 index 0000000000000..4e0bb67a3b8b6 --- /dev/null +++ b/packages/affine/shared/src/theme/color.ts @@ -0,0 +1,40 @@ +/* eslint perfectionist/sort-enums: "off" */ + +import { themeToVar } from '@toeverything/theme/v2'; +import { z } from 'zod'; + +// 3 x 9 +export const PALETTES = [ + // Light + themeToVar('edgeless/palette/light/redLight'), + themeToVar('edgeless/palette/light/orangeLight'), + themeToVar('edgeless/palette/light/yellowLight'), + themeToVar('edgeless/palette/light/greenLight'), + themeToVar('edgeless/palette/light/blueLight'), + themeToVar('edgeless/palette/light/purpleLight'), + themeToVar('edgeless/palette/light/magentaLight'), + themeToVar('edgeless/palette/light/greyLight'), + 'transparent', + // Medium + themeToVar('edgeless/palette/medium/redMedium'), + themeToVar('edgeless/palette/medium/orangeMedium'), + themeToVar('edgeless/palette/medium/yellowMedium'), + themeToVar('edgeless/palette/medium/greenMedium'), + themeToVar('edgeless/palette/medium/blueMedium'), + themeToVar('edgeless/palette/medium/purpleMedium'), + themeToVar('edgeless/palette/medium/magentaMedium'), + themeToVar('edgeless/palette/medium/greyMedium'), + themeToVar('edgeless/palette/white'), + // Heavy + themeToVar('edgeless/palette/heavy/red'), + themeToVar('edgeless/palette/heavy/orange'), + themeToVar('edgeless/palette/heavy/yellow'), + themeToVar('edgeless/palette/heavy/green'), + themeToVar('edgeless/palette/heavy/blue'), + themeToVar('edgeless/palette/heavy/purple'), + themeToVar('edgeless/palette/heavy/magenta'), + themeToVar('edgeless/palette/black'), +] as const; + +export const PaletteEnum = z.enum(PALETTES); +export type PaletteEnum = z.infer; diff --git a/packages/affine/shared/src/theme/index.ts b/packages/affine/shared/src/theme/index.ts index 48ddb6d474e9f..01ea238bb39fb 100644 --- a/packages/affine/shared/src/theme/index.ts +++ b/packages/affine/shared/src/theme/index.ts @@ -1 +1,2 @@ +export * from './color.js'; export * from './css-variables.js'; diff --git a/packages/affine/shared/src/utils/zod-schema.ts b/packages/affine/shared/src/utils/zod-schema.ts index 95be5b7d0ec55..78726cb65bd76 100644 --- a/packages/affine/shared/src/utils/zod-schema.ts +++ b/packages/affine/shared/src/utils/zod-schema.ts @@ -36,6 +36,8 @@ import { } from '@blocksuite/affine-model'; import { z, ZodDefault, ZodObject, type ZodTypeAny, ZodUnion } from 'zod'; +import { PaletteEnum } from '../theme/color.js'; + const ConnectorEndpointSchema = z.nativeEnum(PointStyle); const StrokeStyleSchema = z.nativeEnum(StrokeStyle); const LineWidthSchema = z.nativeEnum(LineWidth); @@ -59,10 +61,18 @@ export const ColorSchema = z.union([ dark: z.string(), }), ]); -const LineColorSchema = z.union([LineColorsSchema, ColorSchema]); -const ShapeFillColorSchema = z.union([FillColorsSchema, ColorSchema]); -const ShapeStrokeColorSchema = z.union([StrokeColorsSchema, ColorSchema]); -const TextColorSchema = z.union([LineColorsSchema, ColorSchema]); +const LineColorSchema = z.union([LineColorsSchema, ColorSchema, PaletteEnum]); +const ShapeFillColorSchema = z.union([ + FillColorsSchema, + ColorSchema, + PaletteEnum, +]); +const ShapeStrokeColorSchema = z.union([ + StrokeColorsSchema, + ColorSchema, + PaletteEnum, +]); +const TextColorSchema = z.union([LineColorsSchema, ColorSchema, PaletteEnum]); const NoteBackgroundColorSchema = z.union([ NoteBackgroundColorsSchema, ColorSchema, @@ -70,6 +80,7 @@ const NoteBackgroundColorSchema = z.union([ const FrameBackgroundColorSchema = z.union([ FrameBackgroundColorsSchema, ColorSchema, + PaletteEnum, ]); export const ConnectorSchema = z diff --git a/packages/blocks/src/root-block/edgeless/components/color-picker/button.ts b/packages/blocks/src/root-block/edgeless/components/color-picker/button.ts index e185f77769034..2b09ef9dc10ad 100644 --- a/packages/blocks/src/root-block/edgeless/components/color-picker/button.ts +++ b/packages/blocks/src/root-block/edgeless/components/color-picker/button.ts @@ -51,6 +51,10 @@ export class EdgelessColorPickerButton extends WithDisposable(LitElement) { return this.color.startsWith('--'); } + get isCustomColor() { + return !this.palettes.includes(this.color); + } + get tabContentPadding() { return `${this.tabType === 'custom' ? 0 : 8}px`; } @@ -82,13 +86,15 @@ export class EdgelessColorPickerButton extends WithDisposable(LitElement) { ${this.isText ? html` ` : html` `} @@ -104,7 +110,7 @@ export class EdgelessColorPickerButton extends WithDisposable(LitElement) { diff --git a/packages/blocks/src/root-block/edgeless/components/color-picker/custom-button.ts b/packages/blocks/src/root-block/edgeless/components/color-picker/custom-button.ts index 9c76ed365f62b..41bd64373c597 100644 --- a/packages/blocks/src/root-block/edgeless/components/color-picker/custom-button.ts +++ b/packages/blocks/src/root-block/edgeless/components/color-picker/custom-button.ts @@ -1,18 +1,36 @@ import { css, html, LitElement } from 'lit'; import { property } from 'lit/decorators.js'; -import { colorContainerStyles } from '../panel/color-panel.js'; - export class EdgelessColorCustomButton extends LitElement { static override styles = css` - ${colorContainerStyles} + :host { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + cursor: pointer; + } + + :host([active]):after { + position: absolute; + display: block; + content: ''; + width: 27px; + height: 27px; + border: 1.5px solid var(--affine-primary-color); + border-radius: 50%; + box-sizing: border-box; + overflow: hidden; + pointer-events: none; + } .color-custom { display: flex; align-items: center; justify-content: center; - width: 16px; - height: 16px; + width: 21px; + height: 21px; border-radius: 50%; box-sizing: border-box; overflow: hidden; @@ -43,15 +61,11 @@ export class EdgelessColorCustomButton extends LitElement { `; override render() { - return html` - - - - `; + return html``; } - @property({ attribute: false }) - accessor active!: boolean; + @property({ attribute: true, type: Boolean }) + accessor active: boolean = false; } declare global { diff --git a/packages/blocks/src/root-block/edgeless/components/panel/color-panel.ts b/packages/blocks/src/root-block/edgeless/components/panel/color-panel.ts index 04f48055fda7a..e7b27d090c65b 100644 --- a/packages/blocks/src/root-block/edgeless/components/panel/color-panel.ts +++ b/packages/blocks/src/root-block/edgeless/components/panel/color-panel.ts @@ -1,15 +1,9 @@ -import { TransparentIcon } from '@blocksuite/affine-components/icons'; -import { - ColorScheme, - LINE_COLORS, - LineColor, - NoteBackgroundColor, - ShapeFillColor, -} from '@blocksuite/affine-model'; -import { css, html, LitElement, nothing } from 'lit'; +import { ColorScheme, LineColor } from '@blocksuite/affine-model'; +import { PALETTES, unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme'; +import { css, html, LitElement, nothing, svg, type TemplateResult } from 'lit'; import { property } from 'lit/decorators.js'; +import { classMap } from 'lit/directives/class-map.js'; import { repeat } from 'lit/directives/repeat.js'; -import { styleMap } from 'lit/directives/style-map.js'; export class ColorEvent extends Event { detail: string; @@ -35,63 +29,57 @@ export function isTransparent(color: string) { return color.toLowerCase().endsWith('transparent'); } -function isSameColorWithBackground(color: string) { - const colors: string[] = [ - LineColor.Black, - LineColor.White, - NoteBackgroundColor.Black, - NoteBackgroundColor.White, - ShapeFillColor.Black, - ShapeFillColor.White, - ]; - return colors.includes(color.toLowerCase()); -} - -function TransparentColor(hollowCircle = false) { - const containerStyle = { - position: 'relative', - width: '16px', - height: '16px', - stroke: 'none', - }; - const maskStyle = { - position: 'absolute', - width: '10px', - height: '10px', - left: '3px', - top: '3.5px', - borderRadius: '50%', - background: 'var(--affine-background-overlay-panel-color)', - }; - - const mask = hollowCircle - ? html`` +function TransparentIcon(hollowCircle = false) { + const white: TemplateResult | typeof nothing = hollowCircle + ? svg`` : nothing; return html` - ${TransparentIcon} ${mask} + + + ${white} + `; } -function BorderedHollowCircle(color: string) { - const valid = color.startsWith('--'); - const strokeWidth = valid && isSameColorWithBackground(color) ? 1 : 0; - const style = { - fill: valid ? `var(${color})` : color, - stroke: 'var(--affine-border-color)', - }; +function CircleIcon(color: string) { + return html` + + + + `; +} + +function HollowCircleIcon(color: string) { return html` `; @@ -99,76 +87,87 @@ function BorderedHollowCircle(color: string) { function AdditionIcon(color: string, hollowCircle: boolean) { if (isTransparent(color)) { - return TransparentColor(hollowCircle); + return TransparentIcon(hollowCircle); } + if (hollowCircle) { - return BorderedHollowCircle(color); + return HollowCircleIcon(color); } - return nothing; -} - -export function ColorUnit( - color: string, - { - hollowCircle, - letter, - }: { - hollowCircle?: boolean; - letter?: boolean; - } = {} -) { - const additionIcon = AdditionIcon(color, !!hollowCircle); - - const colorStyle = - !hollowCircle && !isTransparent(color) - ? { background: `var(${color})` } - : {}; - - const borderStyle = - isSameColorWithBackground(color) && !hollowCircle - ? { - border: '0.5px solid var(--affine-border-color)', - } - : {}; - - const style = { - width: '16px', - height: '16px', - borderRadius: '50%', - boxSizing: 'border-box', - overflow: 'hidden', - ...borderStyle, - ...colorStyle, - }; - return html` - - ${additionIcon} - - `; + return CircleIcon(color); } export class EdgelessColorButton extends LitElement { static override styles = css` :host { + position: relative; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + } + + .color-unit { + position: relative; display: flex; justify-content: center; align-items: center; + border-radius: 50%; + box-sizing: border-box; + } + .color-unit svg { + width: 100%; + height: 100%; + border-radius: 50%; + overflow: hidden; + } + + :host(.large) { + width: 24px; + height: 24px; + } + :host(.large) .color-unit { width: 20px; height: 20px; } + :host(.large) .color-unit:after { + position: absolute; + display: block; + content: ''; + width: 100%; + height: 100%; + border-radius: 50%; + box-sizing: border-box; + overflow: hidden; + pointer-events: none; + border-width: 0.5px; + border-style: solid; + border-color: ${unsafeCSSVarV2('layer/insideBorder/blackBorder')}; + } + :host(.large.black) .color-unit:after { + border-color: ${unsafeCSSVarV2('layer/insideBorder/border')}; + } - .color-unit { + :host(.small) { + width: 20px; + height: 20px; + } + :host(.small) .color-unit { width: 16px; height: 16px; + } + + :host([active]):after { + position: absolute; + display: block; + content: ''; + width: 27px; + height: 27px; + border: 1.5px solid var(--affine-primary-color); border-radius: 50%; box-sizing: border-box; overflow: hidden; + pointer-events: none; } `; @@ -178,30 +177,25 @@ export class EdgelessColorButton extends LitElement { } override render() { - const { color, hollowCircle, letter } = this; - const additionIcon = AdditionIcon(color, !!hollowCircle); - const style: Record = {}; - if (!hollowCircle) { - style.background = this.preprocessColor; - if (isSameColorWithBackground(color)) { - style.border = '0.5px solid var(--affine-border-color)'; - } - } + const { color, preprocessColor, hollowCircle, letter } = this; + const additionIcon = AdditionIcon(preprocessColor, !!hollowCircle); return html` ${additionIcon} `; } + @property({ attribute: true, type: Boolean }) + accessor active: boolean = false; + @property({ attribute: false }) accessor color!: string; - @property({ attribute: false }) - accessor hollowCircle: boolean | undefined = undefined; + @property({ attribute: 'hollow-circle', type: Boolean }) + accessor hollowCircle: boolean = false; @property({ attribute: false }) accessor letter: boolean | undefined = undefined; @@ -213,8 +207,8 @@ export const colorContainerStyles = css` display: flex; align-items: center; justify-content: center; - width: 24px; - height: 24px; + width: 27px; + height: 27px; border-radius: 50%; box-sizing: border-box; overflow: hidden; @@ -230,9 +224,9 @@ export const colorContainerStyles = css` .color-container[active]:after { position: absolute; - width: 20px; - height: 20px; - border: 0.5px solid var(--affine-primary-color); + width: 27px; + height: 27px; + border: 1.5px solid var(--affine-primary-color); border-radius: 50%; box-sizing: border-box; content: attr(data-letter); @@ -245,19 +239,11 @@ export class EdgelessColorPanel extends LitElement { display: flex; flex-direction: row; flex-wrap: wrap; - width: 184px; - gap: 8px; + width: 248px; + gap: 4px; } - - ${colorContainerStyles} `; - get palettes() { - return this.hasTransparent - ? ['--affine-palette-transparent', ...this.options] - : this.options; - } - onSelect(value: string) { this.dispatchEvent( new ColorEvent('select', { @@ -274,24 +260,20 @@ export class EdgelessColorPanel extends LitElement { ${repeat( this.palettes, color => color, - color => { - const unit = ColorUnit(color, { - hollowCircle: this.hollowCircle, - letter: this.showLetterMark, - }); - - return html` - this.onSelect(color)} - > - ${unit} - - `; - } + color => + html` this.onSelect(color)} + > + ` )} - `; } @@ -306,7 +288,7 @@ export class EdgelessColorPanel extends LitElement { accessor openColorPicker!: (e: MouseEvent) => void; @property({ type: Array }) - accessor options: readonly string[] = LINE_COLORS; + accessor palettes: readonly string[] = PALETTES; @property({ attribute: false }) accessor showLetterMark = false; @@ -334,11 +316,11 @@ export class EdgelessTextColorIcon extends LitElement { override render() { return html` { onClick?.({ type: 'size', @@ -69,7 +69,7 @@ export function LineStylesPanel({ item => item.value, ({ name, icon, value }) => { const active = selectedLineStyle === value; - const classes: Record = { + const classes = { 'line-style-button': true, [`mode-${value}`]: true, }; diff --git a/packages/blocks/src/root-block/edgeless/components/panel/line-width-panel.ts b/packages/blocks/src/root-block/edgeless/components/panel/line-width-panel.ts index b6b4a47737cc5..a4ff82a269741 100644 --- a/packages/blocks/src/root-block/edgeless/components/panel/line-width-panel.ts +++ b/packages/blocks/src/root-block/edgeless/components/panel/line-width-panel.ts @@ -1,15 +1,17 @@ -import { LineWidth } from '@blocksuite/affine-model'; -import { requestConnectedFrame } from '@blocksuite/affine-shared/utils'; +import { LINE_WIDTHS, LineWidth } from '@blocksuite/affine-model'; +import { clamp, on, once } from '@blocksuite/affine-shared/utils'; import { WithDisposable } from '@blocksuite/global/utils'; import { css, html, LitElement, nothing, type PropertyValues } from 'lit'; -import { property, query, queryAll } from 'lit/decorators.js'; - -type DragConfig = { - stepWidth: number; - boundLeft: number; - containerWidth: number; - bottomLineWidth: number; -}; +import { property, query } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; + +interface Config { + width: number; + itemSize: number; + itemIconSize: number; + dragHandleSize: number; + count: number; +} export class LineWidthEvent extends Event { detail: LineWidth; @@ -34,10 +36,28 @@ export class EdgelessLineWidthPanel extends WithDisposable(LitElement) { align-items: center; justify-content: center; align-self: stretch; + + --width: 140px; + --item-size: 16px; + --item-icon-size: 8px; + --drag-handle-size: 14px; + --cursor: 0; + --count: 6; + /* (16 - 14) / 2 + (cursor / (count - 1)) * (140 - 16) */ + --drag-handle-center-x: calc( + (var(--item-size) - var(--drag-handle-size)) / 2 + + (var(--cursor) / (var(--count) - 1)) * + (var(--width) - var(--item-size)) + ); + } + + :host([disabled]) { + opacity: 0.5; + pointer-events: none; } .line-width-panel { - width: 108px; + width: var(--width); height: 24px; display: flex; flex-direction: row; @@ -51,162 +71,70 @@ export class EdgelessLineWidthPanel extends WithDisposable(LitElement) { display: flex; align-items: center; justify-content: center; - width: 16px; - height: 16px; + width: var(--item-size); + height: var(--item-size); z-index: 2; } .line-width-icon { - width: 4px; - height: 4px; - border-radius: 50%; + width: var(--item-icon-size); + height: var(--item-icon-size); background-color: var(--affine-border-color); + border-radius: 50%; } - .line-width-button:nth-child(1) { - margin-right: 0; - } - - .line-width-button:nth-child(6) { - margin-left: 0; + .line-width-button[data-selected] .line-width-icon { + background-color: var(--affine-icon-color); } .drag-handle { position: absolute; - left: 0; - top: 50%; - width: 8px; - height: 8px; - transform: translateY(-50%) translateX(4px); + width: var(--drag-handle-size); + height: var(--drag-handle-size); border-radius: 50%; background-color: var(--affine-icon-color); z-index: 3; + transform: translateX(var(--drag-handle-center-x)); } .bottom-line, .line-width-overlay { - left: 8px; - top: 50%; - transform: translateY(-50%); - height: 1px; - background-color: var(--affine-border-color); position: absolute; + height: 1px; + left: calc(var(--item-size) / 2); } .bottom-line { - width: calc(100% - 16px); + width: calc(100% - var(--item-size)); background-color: var(--affine-border-color); } .line-width-overlay { - width: 0; background-color: var(--affine-icon-color); z-index: 1; + width: var(--drag-handle-center-x); } `; - private _dragConfig: DragConfig | null = null; - - private _getDragHandlePosition = (e: PointerEvent, config: DragConfig) => { - const x = e.clientX; - const { boundLeft, bottomLineWidth, stepWidth, containerWidth } = config; - - let steps: number; - if (x <= boundLeft) { - steps = 0; - } else if (x - boundLeft >= containerWidth) { - steps = 100; - } else { - steps = Math.floor((x - boundLeft) / stepWidth); - } - - // The drag handle should not be dragged to the left of the first icon or right of the last icon. - // Calculate the drag handle position based on the steps. - const bottomLineOffsetX = 4; - const bottomLineStepWidth = (bottomLineWidth - bottomLineOffsetX) / 100; - const dragHandlerPosition = steps * bottomLineStepWidth; - return dragHandlerPosition; + private _getDragHandlePosition = (e: PointerEvent) => { + return clamp(e.offsetX, 0, this.config.width); }; private _onPointerDown = (e: PointerEvent) => { e.preventDefault(); - if (this.disable) return; - const { left, width } = this._lineWidthPanel.getBoundingClientRect(); - const bottomLineWidth = this._bottomLine.getBoundingClientRect().width; - this._dragConfig = { - stepWidth: width / 100, - boundLeft: left, - containerWidth: width, - bottomLineWidth, - }; this._onPointerMove(e); - }; - private _onPointerMove = (e: PointerEvent) => { - e.preventDefault(); - if (!this._dragConfig) return; - const dragHandlerPosition = this._getDragHandlePosition( - e, - this._dragConfig - ); - this._dragHandle.style.left = `${dragHandlerPosition}%`; - this._lineWidthOverlay.style.width = `${dragHandlerPosition}%`; - this._updateIconsColor(); + const dispose = on(this, 'pointermove', this._onPointerMove); + this._disposables.add(once(this, 'pointerup', dispose)); + this._disposables.add(once(this, 'pointerout', dispose)); }; - private _onPointerOut = (e: PointerEvent) => { - // If the pointer is out of the line width panel - // Stop dragging and update the selected size by nearest size. - e.preventDefault(); - if (!this._dragConfig) return; - const dragHandlerPosition = this._getDragHandlePosition( - e, - this._dragConfig - ); - this._updateLineWidthPanelByDragHandlePosition(dragHandlerPosition); - this._dragConfig = null; - }; - - private _onPointerUp = (e: PointerEvent) => { + private _onPointerMove = (e: PointerEvent) => { e.preventDefault(); - if (!this._dragConfig) return; - const dragHandlerPosition = this._getDragHandlePosition( - e, - this._dragConfig - ); - this._updateLineWidthPanelByDragHandlePosition(dragHandlerPosition); - this._dragConfig = null; - }; - private _updateIconsColor = () => { - if (!this._dragHandle.offsetParent) { - requestConnectedFrame(() => this._updateIconsColor(), this); - return; - } + const x = this._getDragHandlePosition(e); - const dragHandleRect = this._dragHandle.getBoundingClientRect(); - const dragHandleCenterX = dragHandleRect.left + dragHandleRect.width / 2; - // All the icons located at the left of the drag handle should be filled with the icon color. - const leftIcons = []; - // All the icons located at the right of the drag handle should be filled with the border color. - const rightIcons = []; - - for (const icon of this._lineWidthIcons) { - const { left, width } = icon.getBoundingClientRect(); - const centerX = left + width / 2; - if (centerX < dragHandleCenterX) { - leftIcons.push(icon); - } else { - rightIcons.push(icon); - } - } - - leftIcons.forEach( - icon => (icon.style.backgroundColor = 'var(--affine-icon-color)') - ); - rightIcons.forEach( - icon => (icon.style.backgroundColor = 'var(--affine-border-color)') - ); + this._updateLineWidthPanelByDragHandlePosition(x); }; private _onSelect(lineWidth: LineWidth) { @@ -224,110 +152,73 @@ export class EdgelessLineWidthPanel extends WithDisposable(LitElement) { private _updateLineWidthPanel(selectedSize: LineWidth) { if (!this._lineWidthOverlay) return; - let width = 0; - let dragHandleOffsetX = 0; - switch (selectedSize) { - case LineWidth.Two: - width = 0; - break; - case LineWidth.Four: - width = 16; - dragHandleOffsetX = 1; - break; - case LineWidth.Six: - width = 32; - dragHandleOffsetX = 2; - break; - case LineWidth.Eight: - width = 48; - dragHandleOffsetX = 3; - break; - case LineWidth.Ten: - width = 64; - dragHandleOffsetX = 4; - break; - default: - width = 80; - dragHandleOffsetX = 4; - } + const index = this.lineWidths.findIndex(w => w === selectedSize); + if (index === -1) return; - dragHandleOffsetX += 4; - this._lineWidthOverlay.style.width = `${width}%`; - this._dragHandle.style.left = `${width}%`; - this._dragHandle.style.transform = `translateY(-50%) translateX(${dragHandleOffsetX}px)`; - this._updateIconsColor(); + this.style.setProperty('--cursor', `${index}`); } - private _updateLineWidthPanelByDragHandlePosition( - dragHandlerPosition: number - ) { + private _updateLineWidthPanelByDragHandlePosition(x: number) { // Calculate the selected size based on the drag handle position. // Need to select the nearest size. - let selectedSize = this.selectedSize; - if (dragHandlerPosition <= 12) { - selectedSize = LineWidth.Two; - } else if (dragHandlerPosition > 12 && dragHandlerPosition <= 26) { - selectedSize = LineWidth.Four; - } else if (dragHandlerPosition > 26 && dragHandlerPosition <= 40) { - selectedSize = LineWidth.Six; - } else if (dragHandlerPosition > 40 && dragHandlerPosition <= 54) { - selectedSize = LineWidth.Eight; - } else if (dragHandlerPosition > 54 && dragHandlerPosition <= 68) { - selectedSize = LineWidth.Ten; - } else { - selectedSize = LineWidth.Twelve; - } + + const { + config: { width, itemSize, count }, + lineWidths, + } = this; + const targetWidth = width - itemSize; + const halfItemSize = itemSize / 2; + const offsetX = halfItemSize + (width - itemSize * count) / (count - 1) / 2; + const selectedSize = lineWidths.findLast((_, n) => { + const cx = halfItemSize + (n / (count - 1)) * targetWidth; + return x >= cx - offsetX && x < cx + offsetX; + }); + if (!selectedSize) return; + this._updateLineWidthPanel(selectedSize); this._onSelect(selectedSize); } - override disconnectedCallback(): void { - this._disposables.dispose(); + override connectedCallback() { + super.connectedCallback(); + const { + style, + config: { width, itemSize, itemIconSize, dragHandleSize, count }, + } = this; + style.setProperty('--width', `${width}px`); + style.setProperty('--item-size', `${itemSize}px`); + style.setProperty('--item-icon-size', `${itemIconSize}px`); + style.setProperty('--drag-handle-size', `${dragHandleSize}px`); + style.setProperty('--count', `${count}`); } - override firstUpdated(): void { + override firstUpdated() { this._updateLineWidthPanel(this.selectedSize); this._disposables.addFromEvent(this, 'pointerdown', this._onPointerDown); - this._disposables.addFromEvent(this, 'pointermove', this._onPointerMove); - this._disposables.addFromEvent(this, 'pointerup', this._onPointerUp); - this._disposables.addFromEvent(this, 'pointerout', this._onPointerOut); } override render() { - return html` - e.preventDefault()}" - > - - - - - - - - - - - - - - - - - - - - - - ${this.hasTooltip - ? html`Thickness` - : nothing} - `; + return html` + ${repeat( + this.lineWidths, + w => w, + (w, n) => + html` + + ` + )} + + + + ${this.hasTooltip + ? html`Thickness` + : nothing} + `; } override willUpdate(changedProperties: PropertyValues) { @@ -336,27 +227,26 @@ export class EdgelessLineWidthPanel extends WithDisposable(LitElement) { } } - @query('.bottom-line') - private accessor _bottomLine!: HTMLElement; - - @query('.drag-handle') - private accessor _dragHandle!: HTMLElement; - - @queryAll('.line-width-icon') - private accessor _lineWidthIcons!: NodeListOf; - @query('.line-width-overlay') private accessor _lineWidthOverlay!: HTMLElement; - @query('.line-width-panel') - private accessor _lineWidthPanel!: HTMLElement; + accessor config: Config = { + width: 140, + itemSize: 16, + itemIconSize: 8, + dragHandleSize: 14, + count: LINE_WIDTHS.length, + }; - @property({ attribute: false }) - accessor disable = false; + @property({ attribute: false, type: Boolean }) + accessor disabled = true; @property({ attribute: false }) accessor hasTooltip = true; + @property({ attribute: false }) + accessor lineWidths: LineWidth[] = LINE_WIDTHS; + @property({ attribute: false }) accessor selectedSize: LineWidth = LineWidth.Two; } diff --git a/packages/blocks/src/root-block/edgeless/components/panel/stroke-style-panel.ts b/packages/blocks/src/root-block/edgeless/components/panel/stroke-style-panel.ts index 3dc0d7ef1a176..8b4888f42f2fd 100644 --- a/packages/blocks/src/root-block/edgeless/components/panel/stroke-style-panel.ts +++ b/packages/blocks/src/root-block/edgeless/components/panel/stroke-style-panel.ts @@ -1,4 +1,6 @@ -import { SHAPE_STROKE_COLORS, StrokeStyle } from '@blocksuite/affine-model'; +import type { StrokeStyle } from '@blocksuite/affine-model'; + +import { PALETTES } from '@blocksuite/affine-shared/theme'; import { WithDisposable } from '@blocksuite/global/utils'; import { css, html, LitElement } from 'lit'; import { property } from 'lit/decorators.js'; @@ -31,7 +33,6 @@ export class StrokeStylePanel extends WithDisposable(LitElement) { selectedLineSize: this.strokeWidth, selectedLineStyle: this.strokeStyle, onClick: e => this.setStrokeStyle(e), - lineStyles: [StrokeStyle.Solid, StrokeStyle.Dash], })} this.setStrokeColor(e)} diff --git a/packages/blocks/src/root-block/widgets/element-toolbar/change-brush-button.ts b/packages/blocks/src/root-block/widgets/element-toolbar/change-brush-button.ts index a843ef9140b0c..e526acb47915b 100644 --- a/packages/blocks/src/root-block/widgets/element-toolbar/change-brush-button.ts +++ b/packages/blocks/src/root-block/widgets/element-toolbar/change-brush-button.ts @@ -4,7 +4,8 @@ import type { ColorScheme, } from '@blocksuite/affine-model'; -import { LINE_COLORS, LineWidth } from '@blocksuite/affine-model'; +import { LineWidth } from '@blocksuite/affine-model'; +import { PALETTES } from '@blocksuite/affine-shared/theme'; import { countBy, maxBy, WithDisposable } from '@blocksuite/global/utils'; import { html, LitElement, nothing } from 'lit'; import { property, query, state } from 'lit/decorators.js'; @@ -138,7 +139,7 @@ export class EdgelessChangeBrushButton extends WithDisposable(LitElement) { .color=${selectedColor} .colors=${colors} .colorType=${type} - .palettes=${LINE_COLORS} + .palettes=${PALETTES} > `; diff --git a/packages/blocks/src/root-block/widgets/element-toolbar/change-connector-button.ts b/packages/blocks/src/root-block/widgets/element-toolbar/change-connector-button.ts index f7f5ef050beb1..9674195859d53 100644 --- a/packages/blocks/src/root-block/widgets/element-toolbar/change-connector-button.ts +++ b/packages/blocks/src/root-block/widgets/element-toolbar/change-connector-button.ts @@ -29,7 +29,8 @@ import { DEFAULT_REAR_END_POINT_STYLE, PointStyle, } from '@blocksuite/affine-model'; -import { LINE_COLORS, LineWidth, StrokeStyle } from '@blocksuite/affine-model'; +import { LineWidth, StrokeStyle } from '@blocksuite/affine-model'; +import { PALETTES } from '@blocksuite/affine-shared/theme'; import { countBy, maxBy, WithDisposable } from '@blocksuite/global/utils'; import { html, LitElement, nothing, type TemplateResult } from 'lit'; import { property, query } from 'lit/decorators.js'; @@ -367,7 +368,7 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) { .color=${selectedColor} .colors=${colors} .colorType=${type} - .palettes=${LINE_COLORS} + .palettes=${PALETTES} .hollowCircle=${true} > this._setConnectorStroke(e), - lineStyles: [StrokeStyle.Solid, StrokeStyle.Dash], })} `; @@ -192,7 +192,7 @@ export class EdgelessChangeFrameButton extends WithDisposable(LitElement) { > this._setFrameBackground(e.detail)} > diff --git a/packages/blocks/src/root-block/widgets/element-toolbar/change-shape-button.ts b/packages/blocks/src/root-block/widgets/element-toolbar/change-shape-button.ts index 6c1a809b4fda3..37d908a69a5b2 100644 --- a/packages/blocks/src/root-block/widgets/element-toolbar/change-shape-button.ts +++ b/packages/blocks/src/root-block/widgets/element-toolbar/change-shape-button.ts @@ -20,11 +20,10 @@ import { getShapeRadius, getShapeType, LineWidth, - SHAPE_FILL_COLORS, - SHAPE_STROKE_COLORS, ShapeStyle, StrokeStyle, } from '@blocksuite/affine-model'; +import { PALETTES } from '@blocksuite/affine-shared/theme'; import { countBy, maxBy, WithDisposable } from '@blocksuite/global/utils'; import { css, html, LitElement, nothing, type TemplateResult } from 'lit'; import { property, query } from 'lit/decorators.js'; @@ -341,7 +340,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) { .color=${selectedFillColor} .colors=${colors} .colorType=${type} - .palettes=${SHAPE_FILL_COLORS} + .palettes=${PALETTES} > `; @@ -364,7 +363,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) { role="listbox" aria-label="Fill colors" .value=${selectedFillColor} - .options=${SHAPE_FILL_COLORS} + .palettes=${PALETTES} @select=${(e: ColorEvent) => this._setShapeFillColor(e.detail)} > @@ -389,7 +388,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) { .color=${selectedStrokeColor} .colors=${colors} .colorType=${type} - .palettes=${SHAPE_STROKE_COLORS} + .palettes=${PALETTES} .hollowCircle=${true} > this._setShapeStyles(e), - lineStyles: [StrokeStyle.Solid, StrokeStyle.Dash], })} `} diff --git a/packages/blocks/src/root-block/widgets/element-toolbar/change-text-menu.ts b/packages/blocks/src/root-block/widgets/element-toolbar/change-text-menu.ts index f4e47feb99681..c388f551006dd 100644 --- a/packages/blocks/src/root-block/widgets/element-toolbar/change-text-menu.ts +++ b/packages/blocks/src/root-block/widgets/element-toolbar/change-text-menu.ts @@ -21,10 +21,10 @@ import { import { ConnectorElementModel, EdgelessTextBlockModel, - LINE_COLORS, ShapeElementModel, TextElementModel, } from '@blocksuite/affine-model'; +import { PALETTES } from '@blocksuite/affine-shared/theme'; import { Bound, countBy, @@ -388,7 +388,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) { .color=${selectedColor} .colors=${colors} .colorType=${type} - .palettes=${LINE_COLORS} + .palettes=${PALETTES} > `; diff --git a/packages/blocks/src/root-block/widgets/pie-menu/components/pie-node-content.ts b/packages/blocks/src/root-block/widgets/pie-menu/components/pie-node-content.ts index f4ea22e02009e..092119b10cec6 100644 --- a/packages/blocks/src/root-block/widgets/pie-menu/components/pie-node-content.ts +++ b/packages/blocks/src/root-block/widgets/pie-menu/components/pie-node-content.ts @@ -4,7 +4,6 @@ import { property, query } from 'lit/decorators.js'; import type { PieNode } from '../node.js'; -import { ColorUnit } from '../../../edgeless/components/panel/color-panel.js'; import { isSubmenuNode } from '../utils.js'; const styles = css` @@ -46,7 +45,11 @@ export class PieNodeContent extends LitElement { 'IPieSubMenuNode.role with color-picker should have children of type color' ); const { color, hollowCircle } = hoveredNode.model; - return ColorUnit(color, { hollowCircle }); + return html``; } const { label } = model; diff --git a/packages/blocks/src/root-block/widgets/pie-menu/pie-builder.ts b/packages/blocks/src/root-block/widgets/pie-menu/pie-builder.ts index ec211ab750591..6b5ee411d2d20 100644 --- a/packages/blocks/src/root-block/widgets/pie-menu/pie-builder.ts +++ b/packages/blocks/src/root-block/widgets/pie-menu/pie-builder.ts @@ -1,4 +1,5 @@ import { assertExists } from '@blocksuite/global/utils'; +import { html } from 'lit'; import type { ActionFunction, @@ -10,7 +11,6 @@ import type { PieSubmenuNodeModel, } from './base.js'; -import { ColorUnit } from '../../edgeless/components/panel/color-panel.js'; import { PieManager } from './pie-manager.js'; import { calcNodeAngles, calcNodeWedges, isNodeWithChildren } from './utils.js'; @@ -118,7 +118,11 @@ export class PieMenuBuilder { const icon = (ctx: PieMenuContext) => { const color = props.active(ctx); - return ColorUnit(color, { hollowCircle: hollow }); + return html``; }; const colorPickerNode: PieSubmenuNodeModel = { @@ -128,7 +132,12 @@ export class PieMenuBuilder { role: 'color-picker', openOnHover: props.openOnHover ?? true, children: props.colors.map(({ color }) => ({ - icon: () => ColorUnit(color, { hollowCircle: hollow }), + icon: () => + html``, type: 'color', hollowCircle: hollow, label: color,