mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-31 00:59:57 +08:00
feat(editor): update edgeless color palette (#9243)
Closes: [BS-1475](https://linear.app/affine-design/issue/BS-1475/颜色主题更新) [BS-1803](https://linear.app/affine-design/issue/BS-1803/fill-color色板影响的yuan素) [BS-1804](https://linear.app/affine-design/issue/BS-1804/border-color色板影响的yuan素) [BS-1815](https://linear.app/affine-design/issue/BS-1815/连线文字配色略瞎) ### What's Changed * refactor `EdgelessLineWidthPanel` component, the previous width is fixed and cannot be used in the new design * refactor `EdgelessColorPanel` and `EdgelessColorButton` components, make them simple and reusable * delete redundant `EdgelessOneRowColorPanel` component * unity and update color palette, if the previously set color is not in the latest color palette, the custom color button will be selected
This commit is contained in:
@@ -3,6 +3,7 @@ import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { html, LitElement } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { choose } from 'lit/directives/choose.js';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import type { ColorEvent } from '../panel/color-panel.js';
|
||||
@@ -12,7 +13,7 @@ import type {
|
||||
PickColorEvent,
|
||||
PickColorType,
|
||||
} from './types.js';
|
||||
import { keepColor, preprocessColor } from './utils.js';
|
||||
import { keepColor, preprocessColor, rgbaToHex8 } from './utils.js';
|
||||
|
||||
type Type = 'normal' | 'custom';
|
||||
|
||||
@@ -38,10 +39,28 @@ export class EdgelessColorPickerButton extends WithDisposable(LitElement) {
|
||||
get customButtonStyle() {
|
||||
let b = 'transparent';
|
||||
let c = 'transparent';
|
||||
if (!this.isCSSVariable) {
|
||||
|
||||
if (!this.isCustomColor) {
|
||||
return { '--b': b, '--c': c };
|
||||
}
|
||||
|
||||
if (this.isCSSVariable) {
|
||||
if (!this.color.endsWith('transparent')) {
|
||||
b = 'var(--affine-background-overlay-panel-color)';
|
||||
c = keepColor(
|
||||
rgbaToHex8(
|
||||
preprocessColor(window.getComputedStyle(this))({
|
||||
type: 'normal',
|
||||
value: this.color,
|
||||
}).rgba
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
b = 'var(--affine-background-overlay-panel-color)';
|
||||
c = keepColor(this.color);
|
||||
}
|
||||
|
||||
return { '--b': b, '--c': c };
|
||||
}
|
||||
|
||||
@@ -49,6 +68,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`;
|
||||
}
|
||||
@@ -101,7 +124,9 @@ export class EdgelessColorPickerButton extends WithDisposable(LitElement) {
|
||||
<slot name="separator"></slot>
|
||||
<edgeless-color-panel
|
||||
role="listbox"
|
||||
class=${ifDefined(this.colorPanelClass)}
|
||||
.value=${this.color}
|
||||
.palettes=${this.palettes}
|
||||
.options=${this.palettes}
|
||||
.hollowCircle=${this.hollowCircle}
|
||||
.openColorPicker=${this.switchToCustomTab}
|
||||
@@ -111,7 +136,7 @@ export class EdgelessColorPickerButton extends WithDisposable(LitElement) {
|
||||
<edgeless-color-custom-button
|
||||
slot="custom"
|
||||
style=${styleMap(this.customButtonStyle)}
|
||||
.active=${!this.isCSSVariable}
|
||||
?active=${this.isCustomColor}
|
||||
@click=${this.switchToCustomTab}
|
||||
></edgeless-color-custom-button>
|
||||
</edgeless-color-panel>
|
||||
@@ -142,6 +167,9 @@ export class EdgelessColorPickerButton extends WithDisposable(LitElement) {
|
||||
@property()
|
||||
accessor color!: string;
|
||||
|
||||
@property()
|
||||
accessor colorPanelClass: string | undefined = undefined;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor colors: { type: ModeType; value: string }[] = [];
|
||||
|
||||
|
||||
+25
-12
@@ -1,18 +1,35 @@
|
||||
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 +60,11 @@ export class EdgelessColorCustomButton extends LitElement {
|
||||
`;
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="color-container" ?active=${this.active}>
|
||||
<div class="color-unit color-custom"></div>
|
||||
</div>
|
||||
`;
|
||||
return html`<div class="color-unit color-custom"></div>`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor active!: boolean;
|
||||
@property({ attribute: true, type: Boolean })
|
||||
accessor active: boolean = false;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -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 { Black, ColorScheme, PALETTES, White } from '@blocksuite/affine-model';
|
||||
import { 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;
|
||||
@@ -28,70 +22,64 @@ export class ColorEvent extends Event {
|
||||
}
|
||||
|
||||
export const GET_DEFAULT_LINE_COLOR = (theme: ColorScheme) => {
|
||||
return theme === ColorScheme.Dark ? LineColor.White : LineColor.Black;
|
||||
return theme === ColorScheme.Dark ? White : Black;
|
||||
};
|
||||
|
||||
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`<div style=${styleMap(maskStyle)}></div>`
|
||||
function TransparentIcon(hollowCircle = false) {
|
||||
const CircleIcon: TemplateResult | typeof nothing = hollowCircle
|
||||
? svg`<circle cx="10" cy="10" r="8" fill="white" />`
|
||||
: nothing;
|
||||
|
||||
return html`
|
||||
<div style=${styleMap(containerStyle)}>${TransparentIcon} ${mask}</div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M-1.17405 5.17857C-1.2241 5.5285 -1.25 5.88623 -1.25 6.25V8.39286H1.96429V11.6071H-1.25V13.75C-1.25 14.1138 -1.2241 14.4715 -1.17405 14.8214H1.96429V18.0357H0.0943102C0.602244 18.7639 1.23609 19.3978 1.96429 19.9057V18.0357L5.17857 18.0357V21.174C5.5285 21.2241 5.88623 21.25 6.25 21.25H8.39286V18.0357H11.6071V21.25H13.75C14.1138 21.25 14.4715 21.2241 14.8214 21.174V18.0357H18.0357L18.0357 19.9057C18.7639 19.3978 19.3978 18.7639 19.9057 18.0357L18.0357 18.0357V14.8214H21.174C21.2241 14.4715 21.25 14.1138 21.25 13.75V11.6071H18.0357V8.39286H21.25V6.25C21.25 5.88623 21.2241 5.5285 21.174 5.17857H18.0357V1.96429H19.9057C19.3978 1.23609 18.7639 0.602244 18.0357 0.09431L18.0357 1.96429H14.8214V-1.17405C14.4715 -1.2241 14.1138 -1.25 13.75 -1.25H11.6071V1.96429H8.39286V-1.25H6.25C5.88623 -1.25 5.5285 -1.2241 5.17857 -1.17405V1.96429H1.96429V0.0943099C1.23609 0.602244 0.602244 1.23609 0.0943099 1.96429H1.96429V5.17857H-1.17405ZM5.17857 5.17857V1.96429H8.39286V5.17857H5.17857ZM5.17857 8.39286H1.96429V5.17857H5.17857V8.39286ZM8.39286 8.39286V5.17857H11.6071V8.39286H8.39286ZM8.39286 11.6071V8.39286H5.17857V11.6071H1.96429V14.8214H5.17857V18.0357H8.39286V14.8214H11.6071V18.0357H14.8214V14.8214H18.0357V11.6071H14.8214V8.39286H18.0357V5.17857H14.8214V1.96429H11.6071V5.17857H14.8214V8.39286H11.6071V11.6071H8.39286ZM8.39286 11.6071V14.8214H5.17857V11.6071H8.39286ZM11.6071 11.6071H14.8214V14.8214H11.6071V11.6071Z"
|
||||
fill="#D9D9D9"
|
||||
/>
|
||||
${CircleIcon}
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
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`
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="${color}"
|
||||
>
|
||||
<circle cx="10" cy="10" r="10" />
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
function HollowCircleIcon(color: string) {
|
||||
return html`
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="${color}"
|
||||
>
|
||||
<path
|
||||
d="M12.3125 8C12.3125 10.3817 10.3817 12.3125 8 12.3125C5.61827 12.3125 3.6875 10.3817 3.6875 8C3.6875 5.61827 5.61827 3.6875 8 3.6875C10.3817 3.6875 12.3125 5.61827 12.3125 8ZM8 15.5C12.1421 15.5 15.5 12.1421 15.5 8C15.5 3.85786 12.1421 0.5 8 0.5C3.85786 0.5 0.5 3.85786 0.5 8C0.5 12.1421 3.85786 15.5 8 15.5Z"
|
||||
stroke-width="${strokeWidth}"
|
||||
style=${styleMap(style)}
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10 17C13.866 17 17 13.866 17 10C17 6.13401 13.866 3 10 3C6.13401 3 3 6.13401 3 10C3 13.866 6.13401 17 10 17ZM10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20Z"
|
||||
/>
|
||||
</svg>
|
||||
`;
|
||||
@@ -99,76 +87,82 @@ 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`
|
||||
<div
|
||||
class="color-unit"
|
||||
style=${styleMap(style)}
|
||||
aria-label=${color.toLowerCase()}
|
||||
data-letter=${letter ? 'A' : ''}
|
||||
>
|
||||
${additionIcon}
|
||||
</div>
|
||||
`;
|
||||
return CircleIcon(color);
|
||||
}
|
||||
|
||||
export class EdgelessColorButton extends LitElement {
|
||||
static override styles = css`
|
||||
:host {
|
||||
position: relative;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.color-unit {
|
||||
position: relative;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
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 .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(.black) .color-unit:after {
|
||||
border-color: ${unsafeCSSVarV2('layer/insideBorder/border')};
|
||||
}
|
||||
|
||||
:host(.large) {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
:host(.large) .color-unit {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.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,85 +172,54 @@ export class EdgelessColorButton extends LitElement {
|
||||
}
|
||||
|
||||
override render() {
|
||||
const { color, hollowCircle, letter } = this;
|
||||
const additionIcon = AdditionIcon(color, !!hollowCircle);
|
||||
const style: Record<string, string> = {};
|
||||
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`<div
|
||||
class="color-unit"
|
||||
aria-label=${color.toLowerCase()}
|
||||
aria-label=${color}
|
||||
data-letter=${letter ? 'A' : nothing}
|
||||
style=${styleMap(style)}
|
||||
>
|
||||
${additionIcon}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@property({ attribute: true, type: Boolean })
|
||||
accessor active: boolean = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor color!: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor hollowCircle: boolean | undefined = undefined;
|
||||
accessor hollowCircle: boolean = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor letter: boolean | undefined = undefined;
|
||||
}
|
||||
|
||||
export const colorContainerStyles = css`
|
||||
.color-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.color-unit::before {
|
||||
content: attr(data-letter);
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.color-container[active]:after {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 0.5px solid var(--affine-primary-color);
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
content: attr(data-letter);
|
||||
}
|
||||
`;
|
||||
|
||||
export class EdgelessColorPanel extends LitElement {
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
width: 184px;
|
||||
gap: 8px;
|
||||
display: grid;
|
||||
grid-gap: 4px;
|
||||
grid-template-columns: repeat(9, 1fr);
|
||||
}
|
||||
|
||||
${colorContainerStyles}
|
||||
`;
|
||||
/* note */
|
||||
:host(.small) {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
grid-gap: 8px;
|
||||
}
|
||||
|
||||
get palettes() {
|
||||
return this.hasTransparent
|
||||
? ['--affine-palette-transparent', ...this.options]
|
||||
: this.options;
|
||||
}
|
||||
/* edgeless toolbar */
|
||||
:host(.one-way) {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
padding: 0 2px;
|
||||
gap: 14px;
|
||||
box-sizing: border-box;
|
||||
background: var(--affine-background-overlay-panel-color);
|
||||
}
|
||||
`;
|
||||
|
||||
onSelect(value: string) {
|
||||
this.dispatchEvent(
|
||||
@@ -274,24 +237,20 @@ export class EdgelessColorPanel extends LitElement {
|
||||
${repeat(
|
||||
this.palettes,
|
||||
color => color,
|
||||
color => {
|
||||
const unit = ColorUnit(color, {
|
||||
hollowCircle: this.hollowCircle,
|
||||
letter: this.showLetterMark,
|
||||
});
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="color-container"
|
||||
?active=${color === this.value}
|
||||
@click=${() => this.onSelect(color)}
|
||||
>
|
||||
${unit}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
color =>
|
||||
html`<edgeless-color-button
|
||||
class=${classMap({
|
||||
large: true,
|
||||
black: color.startsWith('--') && color.endsWith('black'),
|
||||
})}
|
||||
.color=${color}
|
||||
.letter=${this.showLetterMark}
|
||||
.hollowCircle=${this.hollowCircle}
|
||||
?active=${color === this.value}
|
||||
@click=${() => this.onSelect(color)}
|
||||
>
|
||||
</edgeless-color-button>`
|
||||
)}
|
||||
</div>
|
||||
<slot name="custom"></slot>
|
||||
`;
|
||||
}
|
||||
@@ -306,7 +265,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 +293,11 @@ export class EdgelessTextColorIcon extends LitElement {
|
||||
override render() {
|
||||
return html`
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
|
||||
@@ -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';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
type DragConfig = {
|
||||
stepWidth: number;
|
||||
boundLeft: number;
|
||||
containerWidth: number;
|
||||
bottomLineWidth: number;
|
||||
};
|
||||
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,165 +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 readonly _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 readonly _getDragHandlePosition = (e: PointerEvent) => {
|
||||
return clamp(e.offsetX, 0, this.config.width);
|
||||
};
|
||||
|
||||
private readonly _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);
|
||||
|
||||
const dispose = on(this, 'pointermove', this._onPointerMove);
|
||||
this._disposables.add(once(this, 'pointerup', dispose));
|
||||
this._disposables.add(once(this, 'pointerout', dispose));
|
||||
};
|
||||
|
||||
private readonly _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();
|
||||
};
|
||||
|
||||
private readonly _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;
|
||||
};
|
||||
const x = this._getDragHandlePosition(e);
|
||||
|
||||
private readonly _onPointerUp = (e: PointerEvent) => {
|
||||
e.preventDefault();
|
||||
if (!this._dragConfig) return;
|
||||
const dragHandlerPosition = this._getDragHandlePosition(
|
||||
e,
|
||||
this._dragConfig
|
||||
);
|
||||
this._updateLineWidthPanelByDragHandlePosition(dragHandlerPosition);
|
||||
this._dragConfig = null;
|
||||
};
|
||||
|
||||
private readonly _updateIconsColor = () => {
|
||||
if (!this._dragHandle.offsetParent) {
|
||||
requestConnectedFrame(() => this._updateIconsColor(), this);
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -227,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`<style>
|
||||
.line-width-panel {
|
||||
opacity: ${this.disable ? '0.5' : '1'};
|
||||
}
|
||||
</style>
|
||||
<div
|
||||
class="line-width-panel"
|
||||
@mousedown="${(e: Event) => e.preventDefault()}"
|
||||
>
|
||||
<div class="line-width-button">
|
||||
<div class="line-width-icon"></div>
|
||||
</div>
|
||||
<div class="line-width-button">
|
||||
<div class="line-width-icon"></div>
|
||||
</div>
|
||||
<div class="line-width-button">
|
||||
<div class="line-width-icon"></div>
|
||||
</div>
|
||||
<div class="line-width-button">
|
||||
<div class="line-width-icon"></div>
|
||||
</div>
|
||||
<div class="line-width-button">
|
||||
<div class="line-width-icon"></div>
|
||||
</div>
|
||||
<div class="line-width-button">
|
||||
<div class="line-width-icon"></div>
|
||||
</div>
|
||||
<div class="drag-handle"></div>
|
||||
<div class="bottom-line"></div>
|
||||
<div class="line-width-overlay"></div>
|
||||
${this.hasTooltip
|
||||
? html`<affine-tooltip .offset=${8}>Thickness</affine-tooltip>`
|
||||
: nothing}
|
||||
</div>`;
|
||||
return html`<div class="line-width-panel">
|
||||
${repeat(
|
||||
this.lineWidths,
|
||||
w => w,
|
||||
(w, n) =>
|
||||
html`<div
|
||||
class="line-width-button"
|
||||
aria-label=${w}
|
||||
data-index=${n}
|
||||
?data-selected=${w <= this.selectedSize}
|
||||
>
|
||||
<div class="line-width-icon"></div>
|
||||
</div>`
|
||||
)}
|
||||
<div class="drag-handle"></div>
|
||||
<div class="bottom-line"></div>
|
||||
<div class="line-width-overlay"></div>
|
||||
${this.hasTooltip
|
||||
? html`<affine-tooltip .offset=${8}>Thickness</affine-tooltip>`
|
||||
: nothing}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
override willUpdate(changedProperties: PropertyValues<this>) {
|
||||
@@ -339,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<HTMLElement>;
|
||||
|
||||
@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 = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor hasTooltip = true;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor lineWidths: LineWidth[] = LINE_WIDTHS;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor selectedSize: LineWidth = LineWidth.Two;
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { css } from 'lit';
|
||||
|
||||
import { colorContainerStyles, EdgelessColorPanel } from './color-panel.js';
|
||||
|
||||
export class EdgelessOneRowColorPanel extends EdgelessColorPanel {
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
padding: 0 2px;
|
||||
gap: 14px;
|
||||
box-sizing: border-box;
|
||||
background: var(--affine-background-overlay-panel-color);
|
||||
}
|
||||
|
||||
${colorContainerStyles}
|
||||
|
||||
.color-container {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.color-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
right: calc(100% + 7px);
|
||||
height: 100%;
|
||||
// FIXME: not working
|
||||
scroll-snap-align: start;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'edgeless-one-row-color-panel': EdgelessOneRowColorPanel;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SHAPE_STROKE_COLORS, StrokeStyle } from '@blocksuite/affine-model';
|
||||
import { PALETTES, type StrokeStyle } from '@blocksuite/affine-model';
|
||||
import { WithDisposable } from '@blocksuite/global/utils';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
@@ -30,7 +30,6 @@ export class StrokeStylePanel extends WithDisposable(LitElement) {
|
||||
selectedLineSize: this.strokeWidth,
|
||||
selectedLineStyle: this.strokeStyle,
|
||||
onClick: e => this.setStrokeStyle(e),
|
||||
lineStyles: [StrokeStyle.Solid, StrokeStyle.Dash],
|
||||
})}
|
||||
</div>
|
||||
<editor-toolbar-separator
|
||||
@@ -39,7 +38,7 @@ export class StrokeStylePanel extends WithDisposable(LitElement) {
|
||||
<edgeless-color-panel
|
||||
role="listbox"
|
||||
aria-label="Border colors"
|
||||
.options=${SHAPE_STROKE_COLORS}
|
||||
.palettes=${PALETTES}
|
||||
.value=${this.strokeColor}
|
||||
.hollowCircle=${this.hollowCircle}
|
||||
@select=${(e: ColorEvent) => this.setStrokeColor(e)}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { STROKE_COLORS } from '@blocksuite/affine-model';
|
||||
import {
|
||||
EditPropsStore,
|
||||
ThemeProvider,
|
||||
@@ -63,13 +64,15 @@ export class EdgelessBrushMenu extends EdgelessToolbarToolMixin(
|
||||
>
|
||||
</edgeless-line-width-panel>
|
||||
<menu-divider .vertical=${true}></menu-divider>
|
||||
<edgeless-one-row-color-panel
|
||||
<edgeless-color-panel
|
||||
class="one-way"
|
||||
.value=${color}
|
||||
.palettes=${STROKE_COLORS}
|
||||
.hasTransparent=${!this.edgeless.doc.awarenessStore.getFlag(
|
||||
'enable_color_picker'
|
||||
)}
|
||||
@select=${(e: ColorEvent) => this.onChange({ color: e.detail })}
|
||||
></edgeless-one-row-color-panel>
|
||||
></edgeless-color-panel>
|
||||
</div>
|
||||
</edgeless-slide-menu>
|
||||
`;
|
||||
|
||||
+5
-2
@@ -6,6 +6,7 @@ import {
|
||||
import {
|
||||
ConnectorMode,
|
||||
DEFAULT_CONNECTOR_COLOR,
|
||||
STROKE_COLORS,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
EditPropsStore,
|
||||
@@ -127,13 +128,15 @@ export class EdgelessConnectorMenu extends EdgelessToolbarToolMixin(
|
||||
>
|
||||
</edgeless-line-width-panel>
|
||||
<div class="submenu-divider"></div>
|
||||
<edgeless-one-row-color-panel
|
||||
<edgeless-color-panel
|
||||
class="one-way"
|
||||
.value=${color}
|
||||
.palettes=${STROKE_COLORS}
|
||||
.hasTransparent=${!this.edgeless.doc.awarenessStore.getFlag(
|
||||
'enable_color_picker'
|
||||
)}
|
||||
@select=${(e: ColorEvent) => this.onChange({ stroke: e.detail })}
|
||||
></edgeless-one-row-color-panel>
|
||||
></edgeless-color-panel>
|
||||
</div>
|
||||
</edgeless-slide-menu>
|
||||
`;
|
||||
|
||||
-3
@@ -68,6 +68,3 @@ export const ShapeComponentConfigMap = ShapeComponentConfig.reduce(
|
||||
},
|
||||
{} as Record<Config['name'], Config>
|
||||
);
|
||||
|
||||
export const SHAPE_COLOR_PREFIX = '--affine-palette-shape-';
|
||||
export const LINE_COLOR_PREFIX = '--affine-palette-line-';
|
||||
|
||||
@@ -4,12 +4,13 @@ import {
|
||||
} from '@blocksuite/affine-components/icons';
|
||||
import {
|
||||
DEFAULT_SHAPE_FILL_COLOR,
|
||||
LineColor,
|
||||
LIGHT_PALETTES,
|
||||
MEDIUM_PALETTES,
|
||||
SHAPE_FILL_COLORS,
|
||||
type ShapeFillColor,
|
||||
type ShapeName,
|
||||
ShapeStyle,
|
||||
ShapeType,
|
||||
StrokeColor,
|
||||
} from '@blocksuite/affine-model';
|
||||
import {
|
||||
EditPropsStore,
|
||||
@@ -23,11 +24,7 @@ import { property } from 'lit/decorators.js';
|
||||
|
||||
import type { EdgelessRootBlockComponent } from '../../../edgeless-root-block.js';
|
||||
import { type ColorEvent, isTransparent } from '../../panel/color-panel.js';
|
||||
import {
|
||||
LINE_COLOR_PREFIX,
|
||||
SHAPE_COLOR_PREFIX,
|
||||
ShapeComponentConfig,
|
||||
} from './shape-menu-config.js';
|
||||
import { ShapeComponentConfig } from './shape-menu-config.js';
|
||||
|
||||
export class EdgelessShapeMenu extends SignalWatcher(
|
||||
WithDisposable(LitElement)
|
||||
@@ -79,15 +76,19 @@ export class EdgelessShapeMenu extends SignalWatcher(
|
||||
};
|
||||
});
|
||||
|
||||
private readonly _setFillColor = (fillColor: ShapeFillColor) => {
|
||||
private readonly _setFillColor = (fillColor: string) => {
|
||||
const filled = !isTransparent(fillColor);
|
||||
let strokeColor = fillColor.replace(
|
||||
SHAPE_COLOR_PREFIX,
|
||||
LINE_COLOR_PREFIX
|
||||
) as LineColor;
|
||||
let strokeColor = fillColor; // white or black
|
||||
|
||||
if (filled) {
|
||||
const index = LIGHT_PALETTES.findIndex(color => color === fillColor);
|
||||
if (index !== -1) {
|
||||
strokeColor = MEDIUM_PALETTES[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (strokeColor.endsWith('transparent')) {
|
||||
strokeColor = LineColor.Grey;
|
||||
strokeColor = StrokeColor.Grey;
|
||||
}
|
||||
|
||||
const { shapeName } = this._props$.value;
|
||||
@@ -176,15 +177,15 @@ export class EdgelessShapeMenu extends SignalWatcher(
|
||||
)}
|
||||
</div>
|
||||
<menu-divider .vertical=${true}></menu-divider>
|
||||
<edgeless-one-row-color-panel
|
||||
<edgeless-color-panel
|
||||
class="one-way"
|
||||
.value=${color}
|
||||
.options=${SHAPE_FILL_COLORS}
|
||||
.palettes=${SHAPE_FILL_COLORS}
|
||||
.hasTransparent=${!this.edgeless.doc.awarenessStore.getFlag(
|
||||
'enable_color_picker'
|
||||
)}
|
||||
@select=${(e: ColorEvent) =>
|
||||
this._setFillColor(e.detail as ShapeFillColor)}
|
||||
></edgeless-one-row-color-panel>
|
||||
@select=${(e: ColorEvent) => this._setFillColor(e.detail)}
|
||||
></edgeless-color-panel>
|
||||
</div>
|
||||
</edgeless-slide-menu>
|
||||
`;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { STROKE_COLORS } from '@blocksuite/affine-model';
|
||||
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
|
||||
import { css, html, LitElement, nothing } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
@@ -22,10 +23,12 @@ export class EdgelessTextMenu extends EdgelessToolbarToolMixin(LitElement) {
|
||||
return html`
|
||||
<edgeless-slide-menu>
|
||||
<div class="menu-content">
|
||||
<edgeless-one-row-color-panel
|
||||
<edgeless-color-panel
|
||||
class="one-way"
|
||||
.value=${this.color}
|
||||
.palettes=${STROKE_COLORS}
|
||||
@select=${(e: ColorEvent) => this.onChange({ color: e.detail })}
|
||||
></edgeless-one-row-color-panel>
|
||||
></edgeless-color-panel>
|
||||
</div>
|
||||
</edgeless-slide-menu>
|
||||
`;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
Black,
|
||||
DEFAULT_ROUGHNESS,
|
||||
LineColor,
|
||||
LineWidth,
|
||||
ShapeFillColor,
|
||||
StrokeStyle,
|
||||
White,
|
||||
} from '@blocksuite/affine-model';
|
||||
|
||||
export const NOTE_OVERLAY_OFFSET_X = 6;
|
||||
@@ -48,9 +48,9 @@ export const SurfaceColor = '#6046FE';
|
||||
export const NoteColor = '#1E96EB';
|
||||
export const BlendColor = '#7D91FF';
|
||||
|
||||
export const SHAPE_TEXT_COLOR_PURE_WHITE = LineColor.White;
|
||||
export const SHAPE_TEXT_COLOR_PURE_BLACK = LineColor.Black;
|
||||
export const SHAPE_FILL_COLOR_BLACK = ShapeFillColor.Black;
|
||||
export const SHAPE_TEXT_COLOR_PURE_WHITE = White;
|
||||
export const SHAPE_TEXT_COLOR_PURE_BLACK = Black;
|
||||
export const SHAPE_FILL_COLOR_BLACK = Black;
|
||||
|
||||
export const AI_CHAT_BLOCK_MIN_WIDTH = 260;
|
||||
export const AI_CHAT_BLOCK_MIN_HEIGHT = 160;
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
BrushProps,
|
||||
ColorScheme,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { LINE_COLORS, LineWidth } from '@blocksuite/affine-model';
|
||||
import { LineWidth, PALETTES } from '@blocksuite/affine-model';
|
||||
import { countBy, maxBy, WithDisposable } from '@blocksuite/global/utils';
|
||||
import { html, LitElement, nothing } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
@@ -138,7 +138,7 @@ export class EdgelessChangeBrushButton extends WithDisposable(LitElement) {
|
||||
.color=${selectedColor}
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.palettes=${LINE_COLORS}
|
||||
.palettes=${PALETTES}
|
||||
>
|
||||
</edgeless-color-picker-button>
|
||||
`;
|
||||
|
||||
@@ -28,8 +28,8 @@ import {
|
||||
ConnectorMode,
|
||||
DEFAULT_FRONT_END_POINT_STYLE,
|
||||
DEFAULT_REAR_END_POINT_STYLE,
|
||||
LINE_COLORS,
|
||||
LineWidth,
|
||||
PALETTES,
|
||||
PointStyle,
|
||||
StrokeStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
@@ -373,7 +373,7 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) {
|
||||
.color=${selectedColor}
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.palettes=${LINE_COLORS}
|
||||
.palettes=${PALETTES}
|
||||
.hollowCircle=${true}
|
||||
>
|
||||
<div
|
||||
@@ -390,7 +390,6 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) {
|
||||
selectedLineSize: selectedLineSize,
|
||||
selectedLineStyle: selectedLineStyle,
|
||||
onClick: (e: LineStyleEvent) => this._setConnectorStroke(e),
|
||||
lineStyles: [StrokeStyle.Solid, StrokeStyle.Dash],
|
||||
})}
|
||||
</div>
|
||||
<editor-toolbar-separator
|
||||
|
||||
@@ -9,9 +9,9 @@ import { renderToolbarSeparator } from '@blocksuite/affine-components/toolbar';
|
||||
import {
|
||||
type ColorScheme,
|
||||
DEFAULT_NOTE_HEIGHT,
|
||||
FRAME_BACKGROUND_COLORS,
|
||||
type FrameBlockModel,
|
||||
NoteDisplayMode,
|
||||
PALETTES,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { matchFlavours } from '@blocksuite/affine-shared/utils';
|
||||
import { GfxExtensionIdentifier } from '@blocksuite/block-std/gfx';
|
||||
@@ -130,8 +130,7 @@ export class EdgelessChangeFrameButton extends WithDisposable(LitElement) {
|
||||
const len = frames.length;
|
||||
const onlyOne = len === 1;
|
||||
const colorScheme = this.edgeless.surface.renderer.getColorScheme();
|
||||
const background =
|
||||
getMostCommonColor(frames, colorScheme) ?? '--affine-palette-transparent';
|
||||
const background = getMostCommonColor(frames, colorScheme) ?? 'transparent';
|
||||
|
||||
return join(
|
||||
[
|
||||
@@ -204,7 +203,7 @@ export class EdgelessChangeFrameButton extends WithDisposable(LitElement) {
|
||||
.color=${background}
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.palettes=${FRAME_BACKGROUND_COLORS}
|
||||
.palettes=${PALETTES}
|
||||
>
|
||||
</edgeless-color-picker-button>
|
||||
`;
|
||||
@@ -225,7 +224,7 @@ export class EdgelessChangeFrameButton extends WithDisposable(LitElement) {
|
||||
>
|
||||
<edgeless-color-panel
|
||||
.value=${background}
|
||||
.options=${FRAME_BACKGROUND_COLORS}
|
||||
.palettes=${PALETTES}
|
||||
@select=${(e: ColorEvent) => this._setFrameBackground(e.detail)}
|
||||
>
|
||||
</edgeless-color-panel>
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import {
|
||||
type ColorScheme,
|
||||
DEFAULT_NOTE_BACKGROUND_COLOR,
|
||||
NOTE_BACKGROUND_COLORS,
|
||||
NOTE_BACKGROUND_PALETTES,
|
||||
type NoteBlockModel,
|
||||
NoteDisplayMode,
|
||||
type StrokeStyle,
|
||||
@@ -319,7 +319,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
|
||||
.color=${background}
|
||||
.colorType=${type}
|
||||
.colors=${colors}
|
||||
.palettes=${NOTE_BACKGROUND_COLORS}
|
||||
.palettes=${NOTE_BACKGROUND_PALETTES}
|
||||
>
|
||||
</edgeless-color-picker-button>
|
||||
`;
|
||||
@@ -340,7 +340,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) {
|
||||
>
|
||||
<edgeless-color-panel
|
||||
.value=${background}
|
||||
.options=${NOTE_BACKGROUND_COLORS}
|
||||
.options=${NOTE_BACKGROUND_PALETTES}
|
||||
@select=${(e: ColorEvent) => this._setBackground(e.detail)}
|
||||
>
|
||||
</edgeless-color-panel>
|
||||
|
||||
@@ -21,8 +21,7 @@ import {
|
||||
getShapeType,
|
||||
LineWidth,
|
||||
MindmapElementModel,
|
||||
SHAPE_FILL_COLORS,
|
||||
SHAPE_STROKE_COLORS,
|
||||
PALETTES,
|
||||
ShapeStyle,
|
||||
StrokeStyle,
|
||||
} from '@blocksuite/affine-model';
|
||||
@@ -96,7 +95,7 @@ function getMostCommonFillColor(
|
||||
? (ele.fillColor[colorScheme] ?? ele.fillColor.normal ?? null)
|
||||
: ele.fillColor;
|
||||
}
|
||||
return '--affine-palette-transparent';
|
||||
return 'transparent';
|
||||
});
|
||||
const max = maxBy(Object.entries(colors), ([_k, count]) => count);
|
||||
return max ? (max[0] as string) : null;
|
||||
@@ -345,7 +344,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
|
||||
.color=${selectedFillColor}
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.palettes=${SHAPE_FILL_COLORS}
|
||||
.palettes=${PALETTES}
|
||||
>
|
||||
</edgeless-color-picker-button>
|
||||
`;
|
||||
@@ -368,7 +367,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)}
|
||||
>
|
||||
</edgeless-color-panel>
|
||||
@@ -393,7 +392,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) {
|
||||
.color=${selectedStrokeColor}
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.palettes=${SHAPE_STROKE_COLORS}
|
||||
.palettes=${PALETTES}
|
||||
.hollowCircle=${true}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
FontFamily,
|
||||
FontStyle,
|
||||
FontWeight,
|
||||
LINE_COLORS,
|
||||
PALETTES,
|
||||
ShapeElementModel,
|
||||
TextAlign,
|
||||
TextElementModel,
|
||||
@@ -391,7 +391,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
|
||||
.color=${selectedColor}
|
||||
.colors=${colors}
|
||||
.colorType=${type}
|
||||
.palettes=${LINE_COLORS}
|
||||
.palettes=${PALETTES}
|
||||
>
|
||||
</edgeless-color-picker-button>
|
||||
`;
|
||||
@@ -412,6 +412,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) {
|
||||
>
|
||||
<edgeless-color-panel
|
||||
.value=${selectedColor}
|
||||
.palettes=${PALETTES}
|
||||
@select=${this._setTextColor}
|
||||
></edgeless-color-panel>
|
||||
</editor-menu-button>
|
||||
|
||||
Reference in New Issue
Block a user