mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-07 04:20:11 +08:00
feat(editor): group configuration of note styles to panel from toolbar (#12230)
Close [BS-3401](https://linear.app/affine-design/issue/BS-3401/note-style-需要合并同类项) 
This commit is contained in:
@@ -1,175 +0,0 @@
|
||||
import { EditorChevronDown } from '@blocksuite/affine-components/toolbar';
|
||||
import { ColorScheme, NoteShadow } from '@blocksuite/affine-model';
|
||||
import { NoteShadowDuotoneIcon } from '@blocksuite/icons/lit';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { NoteNoShadowIcon, NoteShadowSampleIcon } from './icons';
|
||||
|
||||
const SHADOWS = [
|
||||
{
|
||||
type: NoteShadow.None,
|
||||
styles: {
|
||||
light: '',
|
||||
dark: '',
|
||||
},
|
||||
tooltip: 'No shadow',
|
||||
},
|
||||
{
|
||||
type: NoteShadow.Box,
|
||||
styles: {
|
||||
light:
|
||||
'0px 0.2px 4.8px 0px rgba(66, 65, 73, 0.2), 0px 0px 1.6px 0px rgba(66, 65, 73, 0.2)',
|
||||
dark: '0px 0.2px 6px 0px rgba(0, 0, 0, 0.44), 0px 0px 2px 0px rgba(0, 0, 0, 0.66)',
|
||||
},
|
||||
tooltip: 'Box shadow',
|
||||
},
|
||||
{
|
||||
type: NoteShadow.Sticker,
|
||||
styles: {
|
||||
light:
|
||||
'0px 9.6px 10.4px -4px rgba(66, 65, 73, 0.07), 0px 10.4px 7.2px -8px rgba(66, 65, 73, 0.22)',
|
||||
dark: '0px 9.6px 10.4px -4px rgba(0, 0, 0, 0.66), 0px 10.4px 7.2px -8px rgba(0, 0, 0, 0.44)',
|
||||
},
|
||||
tooltip: 'Sticker shadow',
|
||||
},
|
||||
{
|
||||
type: NoteShadow.Paper,
|
||||
styles: {
|
||||
light:
|
||||
'0px 0px 0px 4px rgba(255, 255, 255, 1), 0px 1.2px 2.4px 4.8px rgba(66, 65, 73, 0.16)',
|
||||
dark: '0px 1.2px 2.4px 4.8px rgba(0, 0, 0, 0.36), 0px 0px 0px 3.4px rgba(75, 75, 75, 1)',
|
||||
},
|
||||
tooltip: 'Paper shadow',
|
||||
},
|
||||
{
|
||||
type: NoteShadow.Float,
|
||||
styles: {
|
||||
light:
|
||||
'0px 5.2px 12px 0px rgba(66, 65, 73, 0.13), 0px 0px 0.4px 1px rgba(0, 0, 0, 0.06)',
|
||||
dark: '0px 5.2px 12px 0px rgba(0, 0, 0, 0.66), 0px 0px 0.4px 1px rgba(0, 0, 0, 0.44)',
|
||||
},
|
||||
tooltip: 'Floation shadow',
|
||||
},
|
||||
{
|
||||
type: NoteShadow.Film,
|
||||
styles: {
|
||||
light:
|
||||
'0px 0px 0px 1.4px rgba(0, 0, 0, 1), 2.4px 2.4px 0px 1px rgba(0, 0, 0, 1)',
|
||||
dark: '0px 0px 0px 1.4px rgba(178, 178, 178, 1), 2.4px 2.4px 0px 1px rgba(178, 178, 178, 1)',
|
||||
},
|
||||
tooltip: 'Film shadow',
|
||||
},
|
||||
];
|
||||
|
||||
export class EdgelessNoteShadowDropdownMenu extends LitElement {
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-icon svg rect:first-of-type {
|
||||
fill: var(--background);
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
background-color: var(--affine-hover-color);
|
||||
}
|
||||
|
||||
.item[data-selected] {
|
||||
border: 1px solid var(--affine-brand-color);
|
||||
}
|
||||
`;
|
||||
|
||||
select(value: NoteShadow) {
|
||||
this.dispatchEvent(new CustomEvent('select', { detail: value }));
|
||||
}
|
||||
|
||||
override render() {
|
||||
const { value, background, theme } = this;
|
||||
const isDark = theme === ColorScheme.Dark;
|
||||
|
||||
return html`
|
||||
<editor-menu-button
|
||||
.contentPadding="${'8px'}"
|
||||
.button=${html`
|
||||
<editor-icon-button
|
||||
aria-label="Shadow style"
|
||||
.tooltip="${'Shadow style'}"
|
||||
>
|
||||
${NoteShadowDuotoneIcon()} ${EditorChevronDown}
|
||||
</editor-icon-button>
|
||||
`}
|
||||
>
|
||||
<div
|
||||
data-orientation="horizontal"
|
||||
style=${styleMap({
|
||||
'--background': background.startsWith('--')
|
||||
? `var(${background})`
|
||||
: background,
|
||||
})}
|
||||
>
|
||||
${repeat(
|
||||
SHADOWS,
|
||||
shadow => shadow.type,
|
||||
({ type, tooltip, styles: { dark, light } }, index) =>
|
||||
html`<div
|
||||
class="item"
|
||||
?data-selected="${value === type}"
|
||||
@click=${() => this.select(type)}
|
||||
>
|
||||
<editor-icon-button
|
||||
class="item-icon"
|
||||
data-testid="${type.replace('--', '')}"
|
||||
.tooltip=${tooltip}
|
||||
.tipPosition="${'bottom'}"
|
||||
.iconContainerPadding=${0}
|
||||
.hover=${false}
|
||||
style=${styleMap({
|
||||
boxShadow: `${isDark ? dark : light}`,
|
||||
})}
|
||||
>
|
||||
${index === 0 ? NoteNoShadowIcon : NoteShadowSampleIcon}
|
||||
</editor-icon-button>
|
||||
</div>`
|
||||
)}
|
||||
</div>
|
||||
</editor-menu-button>
|
||||
`;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor background!: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor theme!: ColorScheme;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor value!: NoteShadow;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'edgeless-note-shadow-dropdown-menu': EdgelessNoteShadowDropdownMenu;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
import { ColorScheme, NoteShadow } from '@blocksuite/affine-model';
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import { css, html, LitElement, type PropertyValues } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
|
||||
import { NoteNoShadowIcon, NoteShadowSampleIcon } from './icons';
|
||||
|
||||
type Shadow = {
|
||||
type: NoteShadow;
|
||||
light: Parameters<typeof styleMap>[0];
|
||||
dark: Parameters<typeof styleMap>[0];
|
||||
style: Parameters<typeof styleMap>[0];
|
||||
tooltip: string;
|
||||
};
|
||||
|
||||
const SHADOWS: Shadow[] = [
|
||||
{
|
||||
type: NoteShadow.None,
|
||||
light: {},
|
||||
dark: {},
|
||||
style: {},
|
||||
tooltip: 'No shadow',
|
||||
},
|
||||
{
|
||||
type: NoteShadow.Box,
|
||||
light: {
|
||||
'--note-box-shadow-color-1': 'rgba(0, 0, 0, 0.14)',
|
||||
'--note-box-shadow-color-2': 'rgba(0, 0, 0, 0.14)',
|
||||
},
|
||||
dark: {
|
||||
'--note-box-shadow-color-1': 'rgba(0, 0, 0, 0.30)',
|
||||
'--note-box-shadow-color-2': 'rgba(0, 0, 0, 0.44)',
|
||||
},
|
||||
style: {
|
||||
boxShadow:
|
||||
'0px 0.109px 2.621px var(--note-box-shadow-color-1), 0px 0px 0.874px var(--note-box-shadow-color-2)',
|
||||
},
|
||||
tooltip: 'Box shadow',
|
||||
},
|
||||
{
|
||||
type: NoteShadow.Sticker,
|
||||
light: {
|
||||
'--note-sticker-shadow-color-1': 'rgba(0, 0, 0, 0.08)',
|
||||
'--note-sticker-shadow-color-2': 'rgba(0, 0, 0, 0.10)',
|
||||
},
|
||||
dark: {
|
||||
'--note-sticker-shadow-color-1': 'rgba(0, 0, 0, 0.22)',
|
||||
'--note-sticker-shadow-color-2': 'rgba(0, 0, 0, 0.52)',
|
||||
},
|
||||
style: {
|
||||
boxShadow:
|
||||
'0px 5.243px 5.68px var(--note-sticker-shadow-color-1), 0px 5.68px 3.932px var(--note-sticker-shadow-color-2)',
|
||||
},
|
||||
tooltip: 'Sticker shadow',
|
||||
},
|
||||
{
|
||||
type: NoteShadow.Paper,
|
||||
light: {
|
||||
'--note-paper-shadow-color-1': 'rgba(0, 0, 0, 0.14)',
|
||||
'--note-paper-shadow-color-2': '#FFF',
|
||||
},
|
||||
dark: {
|
||||
'--note-paper-shadow-color-1': 'rgba(0, 0, 0, 0.30)',
|
||||
'--note-paper-shadow-color-2': '#7A7A7A',
|
||||
},
|
||||
style: {
|
||||
border: '2px solid var(--note-paper-shadow-color-2)',
|
||||
boxShadow: '0px 0.655px 1.311px var(--note-paper-shadow-color-1)',
|
||||
},
|
||||
tooltip: 'Paper shadow',
|
||||
},
|
||||
{
|
||||
type: NoteShadow.Float,
|
||||
light: {
|
||||
'--note-float-shadow-color-1': 'rgba(0, 0, 0, 0.09)',
|
||||
'--note-float-shadow-color-2': 'rgba(0, 0, 0, 0.10)',
|
||||
},
|
||||
dark: {
|
||||
'--note-float-shadow-color-1': 'rgba(0, 0, 0, 0.30)',
|
||||
'--note-float-shadow-color-2': 'rgba(0, 0, 0, 0.44)',
|
||||
},
|
||||
style: {
|
||||
boxShadow:
|
||||
'0px 2.84px 6.554px var(--note-float-shadow-color-1), 0px 0px 0.218px var(--note-float-shadow-color-2)',
|
||||
},
|
||||
tooltip: 'Floating shadow',
|
||||
},
|
||||
{
|
||||
type: NoteShadow.Film,
|
||||
light: {
|
||||
'--note-film-shadow-color-1': '#000',
|
||||
'--note-film-shadow-color-2': '#000',
|
||||
},
|
||||
dark: {
|
||||
'--note-film-shadow-color-1': '#7A7A7A',
|
||||
'--note-film-shadow-color-2': '#7A7A7A',
|
||||
},
|
||||
style: {
|
||||
border: '1px solid var(--note-film-shadow-color-1)',
|
||||
boxShadow: '2px 2px 0px var(--note-film-shadow-color-2)',
|
||||
},
|
||||
tooltip: 'Film shadow',
|
||||
},
|
||||
];
|
||||
|
||||
export class EdgelessNoteShadowMenu extends LitElement {
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 4.369px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-icon svg {
|
||||
width: 30px;
|
||||
height: auto;
|
||||
border: 1px solid ${unsafeCSSVarV2('layer/insideBorder/blackBorder')};
|
||||
fill: ${unsafeCSSVarV2('layer/insideBorder/blackBorder')};
|
||||
}
|
||||
|
||||
.item-icon svg rect:first-of-type {
|
||||
fill: var(--background);
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
background-color: var(--affine-hover-color);
|
||||
}
|
||||
|
||||
.item[data-selected] {
|
||||
border: 1px solid var(--affine-brand-color);
|
||||
}
|
||||
`;
|
||||
|
||||
select(value: NoteShadow) {
|
||||
this.dispatchEvent(new CustomEvent('select', { detail: value }));
|
||||
}
|
||||
|
||||
override willUpdate(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has('background')) {
|
||||
this.style.setProperty('--background', this.background);
|
||||
}
|
||||
}
|
||||
|
||||
override render() {
|
||||
const { value, theme } = this;
|
||||
const isDark = theme === ColorScheme.Dark;
|
||||
|
||||
return repeat(
|
||||
SHADOWS,
|
||||
shadow => shadow.type,
|
||||
({ type, tooltip, style, light, dark }, index) =>
|
||||
html`<div
|
||||
class="item"
|
||||
?data-selected="${value === type}"
|
||||
@click=${() => this.select(type)}
|
||||
>
|
||||
<editor-icon-button
|
||||
class="item-icon"
|
||||
data-testid="${type.replace('--', '')}"
|
||||
.tooltip=${tooltip}
|
||||
.tipPosition="${'bottom'}"
|
||||
.iconContainerPadding=${0}
|
||||
.hover=${false}
|
||||
style=${styleMap({
|
||||
...(isDark ? dark : light),
|
||||
...style,
|
||||
})}
|
||||
>
|
||||
${index === 0 ? NoteNoShadowIcon : NoteShadowSampleIcon}
|
||||
</editor-icon-button>
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor background!: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor theme!: ColorScheme;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor value!: NoteShadow;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'edgeless-note-shadow-menu': EdgelessNoteShadowMenu;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface';
|
||||
import {
|
||||
packColorsWith,
|
||||
type PickColorEvent,
|
||||
preprocessColor,
|
||||
} from '@blocksuite/affine-components/color-picker';
|
||||
import type { LineDetailType } from '@blocksuite/affine-components/edgeless-line-styles-panel';
|
||||
import type { SliderSelectEvent } from '@blocksuite/affine-components/slider';
|
||||
import {
|
||||
DefaultTheme,
|
||||
NoteBlockModel,
|
||||
type NoteProps,
|
||||
type NoteShadow,
|
||||
resolveColor,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { ThemeProvider } from '@blocksuite/affine-shared/services';
|
||||
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||
import {
|
||||
type ColorEvent,
|
||||
getMostCommonResolvedValue,
|
||||
stopPropagation,
|
||||
} from '@blocksuite/affine-shared/utils';
|
||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
|
||||
import { ArrowLeftSmallIcon, PaletteIcon } from '@blocksuite/icons/lit';
|
||||
import { BlockStdScope, PropTypes, requiredProperties } from '@blocksuite/std';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { choose } from 'lit/directives/choose.js';
|
||||
|
||||
@requiredProperties({
|
||||
notes: PropTypes.arrayOf(model => model instanceof NoteBlockModel),
|
||||
std: PropTypes.instanceOf(BlockStdScope),
|
||||
})
|
||||
export class EdgelessNoteStylePanel extends SignalWatcher(
|
||||
WithDisposable(LitElement)
|
||||
) {
|
||||
@property({ attribute: false })
|
||||
accessor notes!: NoteBlockModel[];
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor std!: BlockStdScope;
|
||||
|
||||
@query('.edgeless-note-style-panel')
|
||||
private accessor _panel!: HTMLDivElement;
|
||||
|
||||
@state()
|
||||
accessor tabType: 'style' | 'customColor' = 'style';
|
||||
|
||||
static override styles = css`
|
||||
.edgeless-note-style-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.edgeless-note-style-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.edgeless-note-style-section-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
height: 22px;
|
||||
align-self: stretch;
|
||||
color: ${unsafeCSSVarV2('text/secondary')};
|
||||
font-feature-settings:
|
||||
'liga' off,
|
||||
'clig' off;
|
||||
|
||||
/* Client/sm */
|
||||
font-family: var(--affine-font-family);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px; /* 157.143% */
|
||||
}
|
||||
|
||||
edgeless-line-styles-panel {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.edgeless-note-corner-radius-panel {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
|
||||
affine-slider {
|
||||
width: 168px;
|
||||
}
|
||||
|
||||
input {
|
||||
border: 0.5px solid ${unsafeCSSVarV2('layer/insideBorder/border')};
|
||||
border-radius: 4px;
|
||||
text-indent: 4px;
|
||||
box-sizing: border-box;
|
||||
width: 88px;
|
||||
color: ${unsafeCSSVarV2('text/placeholder')};
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-color: ${unsafeCSSVarV2('input/border/active')};
|
||||
color: ${unsafeCSSVarV2('text/primary')};
|
||||
}
|
||||
}
|
||||
|
||||
.edgeless-note-style-custom-color-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.edgeless-note-custom-color-picker {
|
||||
padding-top: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
private _styleChanged = false;
|
||||
|
||||
private _beforeChange() {
|
||||
if (!this._styleChanged) {
|
||||
// record the history
|
||||
this.std.store.captureSync();
|
||||
this._styleChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
private get _theme() {
|
||||
return this.std.get(ThemeProvider).edgeless$.value;
|
||||
}
|
||||
|
||||
private get _background() {
|
||||
return (
|
||||
getMostCommonResolvedValue(
|
||||
this.notes.map(model => model.props),
|
||||
'background',
|
||||
background => resolveColor(background, this._theme)
|
||||
) ?? resolveColor(DefaultTheme.noteBackgrounColor, this._theme)
|
||||
);
|
||||
}
|
||||
|
||||
private get _originalBackground() {
|
||||
return this.notes[0].props.background;
|
||||
}
|
||||
|
||||
private get _shadow() {
|
||||
return this.notes[0].props.edgeless.style.shadowType;
|
||||
}
|
||||
|
||||
private get _borderSize() {
|
||||
return this.notes[0].props.edgeless.style.borderSize;
|
||||
}
|
||||
|
||||
private get _borderStyle() {
|
||||
return this.notes[0].props.edgeless.style.borderStyle;
|
||||
}
|
||||
|
||||
private get _borderRadius() {
|
||||
return this.notes[0].props.edgeless.style.borderRadius;
|
||||
}
|
||||
|
||||
private readonly _switchToCustomColorTab = () => {
|
||||
this.tabType = 'customColor';
|
||||
};
|
||||
|
||||
private readonly _switchToStyleTab = () => {
|
||||
this.tabType = 'style';
|
||||
};
|
||||
|
||||
private readonly _selectColor = (e: ColorEvent) => {
|
||||
this._beforeChange();
|
||||
const color = e.detail.value;
|
||||
const crud = this.std.get(EdgelessCRUDIdentifier);
|
||||
this.notes.forEach(note => {
|
||||
crud.updateElement(note.id, {
|
||||
background: color,
|
||||
} satisfies Partial<NoteProps>);
|
||||
});
|
||||
};
|
||||
|
||||
private readonly _pickColor = (e: PickColorEvent) => {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
private readonly _selectShadow = (e: CustomEvent<NoteShadow>) => {
|
||||
this._beforeChange();
|
||||
const shadowType = e.detail;
|
||||
const crud = this.std.get(EdgelessCRUDIdentifier);
|
||||
this.notes.forEach(note => {
|
||||
crud.updateElement(note.id, {
|
||||
edgeless: {
|
||||
...note.props.edgeless,
|
||||
style: {
|
||||
...note.props.edgeless.style,
|
||||
shadowType,
|
||||
},
|
||||
},
|
||||
} satisfies Partial<NoteProps>);
|
||||
});
|
||||
};
|
||||
|
||||
private readonly _selectBorder = (e: CustomEvent<LineDetailType>) => {
|
||||
this._beforeChange();
|
||||
|
||||
const { type, value } = e.detail;
|
||||
const crud = this.std.get(EdgelessCRUDIdentifier);
|
||||
if (type === 'size') {
|
||||
const borderSize = value;
|
||||
this.notes.forEach(note => {
|
||||
const edgeless = note.props.edgeless;
|
||||
crud.updateElement(note.id, {
|
||||
edgeless: {
|
||||
...edgeless,
|
||||
style: {
|
||||
...edgeless.style,
|
||||
borderSize,
|
||||
},
|
||||
},
|
||||
} satisfies Partial<NoteProps>);
|
||||
});
|
||||
} else {
|
||||
const borderStyle = value;
|
||||
this.notes.forEach(note => {
|
||||
const edgeless = note.props.edgeless;
|
||||
crud.updateElement(note.id, {
|
||||
edgeless: { ...edgeless, style: { ...edgeless.style, borderStyle } },
|
||||
} satisfies Partial<NoteProps>);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private readonly _selectBorderRadius = (
|
||||
e: SliderSelectEvent | InputEvent
|
||||
) => {
|
||||
this._beforeChange();
|
||||
|
||||
let borderRadius = this._borderRadius;
|
||||
if (e instanceof InputEvent) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const value = parseInt(target.value);
|
||||
if (isNaN(value)) {
|
||||
return;
|
||||
}
|
||||
borderRadius = value;
|
||||
} else {
|
||||
borderRadius = e.detail.value;
|
||||
}
|
||||
const crud = this.std.get(EdgelessCRUDIdentifier);
|
||||
this.notes.forEach(note => {
|
||||
crud.updateElement(note.id, {
|
||||
edgeless: {
|
||||
...note.props.edgeless,
|
||||
style: { ...note.props.edgeless.style, borderRadius },
|
||||
},
|
||||
} satisfies Partial<NoteProps>);
|
||||
});
|
||||
};
|
||||
|
||||
private _renderStylePanel() {
|
||||
return html` <div class="edgeless-note-style-panel">
|
||||
<div class="edgeless-note-style-section">
|
||||
<div class="edgeless-note-style-section-title">Fill color</div>
|
||||
<edgeless-color-panel
|
||||
role="listbox"
|
||||
.value=${this._background}
|
||||
.theme=${this._theme}
|
||||
.palettes=${DefaultTheme.NoteBackgroundColorPalettes}
|
||||
.hasTransparent=${false}
|
||||
.columns=${DefaultTheme.NoteBackgroundColorPalettes.length + 1}
|
||||
@select=${this._selectColor}
|
||||
>
|
||||
<edgeless-color-custom-button
|
||||
slot="custom"
|
||||
@click=${this._switchToCustomColorTab}
|
||||
></edgeless-color-custom-button>
|
||||
</edgeless-color-panel>
|
||||
</div>
|
||||
<div class="edgeless-note-style-section">
|
||||
<div class="edgeless-note-style-section-title">Shadow</div>
|
||||
<edgeless-note-shadow-menu
|
||||
.background=${this._background}
|
||||
.theme=${this._theme}
|
||||
.value=${this._shadow}
|
||||
@select=${this._selectShadow}
|
||||
></edgeless-note-shadow-menu>
|
||||
</div>
|
||||
<div
|
||||
class="edgeless-note-style-section"
|
||||
data-testid="affine-note-border-style-panel"
|
||||
>
|
||||
<div class="edgeless-note-style-section-title">Border</div>
|
||||
<edgeless-line-styles-panel
|
||||
.lineSize=${this._borderSize}
|
||||
.lineStyle=${this._borderStyle}
|
||||
@select=${this._selectBorder}
|
||||
></edgeless-line-styles-panel>
|
||||
</div>
|
||||
<div
|
||||
class="edgeless-note-style-section"
|
||||
data-testid="affine-note-corner-radius-panel"
|
||||
>
|
||||
<div class="edgeless-note-style-section-title">Corner Radius</div>
|
||||
<div class="edgeless-note-corner-radius-panel">
|
||||
<affine-slider
|
||||
.value=${this._borderRadius}
|
||||
.range=${{
|
||||
points: [0, 4, 8, 16, 24, 32],
|
||||
}}
|
||||
@select=${this._selectBorderRadius}
|
||||
></affine-slider>
|
||||
|
||||
<editor-toolbar-separator></editor-toolbar-separator>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
pattern="[0-9]*"
|
||||
min="0"
|
||||
max="32"
|
||||
.value=${this._borderRadius}
|
||||
@input=${this._selectBorderRadius}
|
||||
@click=${stopPropagation}
|
||||
@pointerdown=${stopPropagation}
|
||||
@cut=${stopPropagation}
|
||||
@copy=${stopPropagation}
|
||||
@paste=${stopPropagation}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _renderCustomColorPanel() {
|
||||
const packed = packColorsWith(
|
||||
this._theme,
|
||||
this._background,
|
||||
this._originalBackground
|
||||
);
|
||||
const type = packed.type === 'palette' ? 'normal' : packed.type;
|
||||
const modes = packed.colors.map(
|
||||
preprocessColor(window.getComputedStyle(this))
|
||||
);
|
||||
|
||||
return html`<div class="edgeless-note-style-custom-color-panel">
|
||||
<div class="edgeless-note-style-section-title">
|
||||
<editor-icon-button
|
||||
aria-label="Back"
|
||||
.iconSize=${'16px'}
|
||||
@click=${this._switchToStyleTab}
|
||||
>
|
||||
${ArrowLeftSmallIcon()}
|
||||
</editor-icon-button>
|
||||
Custom color
|
||||
</div>
|
||||
<edgeless-color-picker
|
||||
class="edgeless-note-custom-color-picker"
|
||||
.pick=${this._pickColor}
|
||||
.colors=${{ type, modes }}
|
||||
></edgeless-color-picker>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
override firstUpdated() {
|
||||
this.disposables.addFromEvent(this._panel, 'click', e => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html`
|
||||
<editor-menu-button
|
||||
.contentPadding=${'8px'}
|
||||
.button=${html`
|
||||
<editor-icon-button aria-label="Note Style" .tooltip=${'Note Style'}>
|
||||
${PaletteIcon()}
|
||||
</editor-icon-button>
|
||||
`}
|
||||
>
|
||||
${choose(this.tabType, [
|
||||
['style', () => this._renderStylePanel()],
|
||||
['customColor', () => this._renderCustomColorPanel()],
|
||||
])}
|
||||
</editor-menu-button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'edgeless-note-style-panel': EdgelessNoteStylePanel;
|
||||
}
|
||||
}
|
||||
@@ -5,23 +5,13 @@ export const NoteNoShadowIcon = html`
|
||||
width="60"
|
||||
height="72"
|
||||
viewBox="0 0 60 72"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="60" height="72" />
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="58.0769"
|
||||
height="71"
|
||||
stroke="black"
|
||||
stroke-opacity="0.1"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M21.9576 26.8962L38.6423 43.5809C42.5269 38.9268 42.2845 31.993 37.9149 27.6235C33.5454 23.254 26.6117 23.0115 21.9576 26.8962ZM37.1193 45.1038L20.4346 28.4192C16.55 33.0732 16.7924 40.007 21.162 44.3765C25.5315 48.746 32.4652 48.9885 37.1193 45.1038ZM19.639 26.1005C25.1063 20.6332 33.9706 20.6332 39.4379 26.1005C44.9053 31.5678 44.9053 40.4322 39.4379 45.8995C33.9706 51.3668 25.1063 51.3668 19.639 45.8995C14.1716 40.4322 14.1716 31.5678 19.639 26.1005Z"
|
||||
fill="black"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.1"
|
||||
/>
|
||||
</svg>
|
||||
@@ -41,7 +31,7 @@ export const NoteShadowSampleIcon = html`
|
||||
width="32.3077"
|
||||
height="4.61538"
|
||||
rx="2"
|
||||
fill="black"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.1"
|
||||
/>
|
||||
<rect
|
||||
@@ -50,7 +40,7 @@ export const NoteShadowSampleIcon = html`
|
||||
width="40.6154"
|
||||
height="2.76923"
|
||||
rx="1.38462"
|
||||
fill="black"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.1"
|
||||
/>
|
||||
<rect
|
||||
@@ -59,7 +49,7 @@ export const NoteShadowSampleIcon = html`
|
||||
width="40.6154"
|
||||
height="2.76923"
|
||||
rx="1.38462"
|
||||
fill="black"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.1"
|
||||
/>
|
||||
<rect
|
||||
@@ -68,7 +58,7 @@ export const NoteShadowSampleIcon = html`
|
||||
width="40.6154"
|
||||
height="2.76923"
|
||||
rx="1.38462"
|
||||
fill="black"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.1"
|
||||
/>
|
||||
<rect
|
||||
@@ -77,7 +67,7 @@ export const NoteShadowSampleIcon = html`
|
||||
width="13.8462"
|
||||
height="2.76923"
|
||||
rx="1.38462"
|
||||
fill="black"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.1"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Reference in New Issue
Block a user