feat(core): add color picker ui to editor settings (#8053)

close AF-1306 AF-1280

![CleanShot 2024-09-03 at 00 23 10@2x](https://github.com/user-attachments/assets/46928c85-45ec-43b1-bbde-24beb7c5c580)
This commit is contained in:
JimmFly 2024-09-03 03:14:49 +00:00
parent 02f0d7aa08
commit bea3d42f40
No known key found for this signature in database
GPG Key ID: 126E0320FEB0D05C
9 changed files with 330 additions and 169 deletions

View File

@ -1,5 +1,4 @@
import {
Menu,
MenuItem,
MenuTrigger,
RadioGroup,
@ -18,7 +17,9 @@ import {
import { useFramework, useLiveData } from '@toeverything/infra';
import { useCallback, useMemo } from 'react';
import { DropdownMenu } from '../menu';
import { menuTrigger, settingWrapper } from '../style.css';
import { Point } from './point';
import { EdgelessSnapshot } from './snapshot';
enum ConnecterStyle {
@ -130,6 +131,11 @@ export const ConnectorSettings = () => {
[editorSetting]
);
const currentColor = useMemo(() => {
const color = settings.connector.stroke;
return Object.entries(LineColor).find(([, value]) => value === color);
}, [settings]);
const colorItems = useMemo(() => {
const { stroke } = settings.connector;
return Object.entries(LineColor).map(([name, value]) => {
@ -138,7 +144,12 @@ export const ConnectorSettings = () => {
};
const isSelected = stroke === value;
return (
<MenuItem key={name} onSelect={handler} selected={isSelected}>
<MenuItem
key={name}
onSelect={handler}
selected={isSelected}
prefix={<Point color={value} />}
>
{name}
</MenuItem>
);
@ -188,11 +199,19 @@ export const ConnectorSettings = () => {
]()}
desc={''}
>
<Menu items={colorItems}>
<MenuTrigger className={menuTrigger}>
{String(settings.connector.stroke)}
</MenuTrigger>
</Menu>
{currentColor ? (
<DropdownMenu
items={colorItems}
trigger={
<MenuTrigger
className={menuTrigger}
prefix={<Point color={currentColor[1]} />}
>
{currentColor[0]}
</MenuTrigger>
}
/>
) : null}
</SettingRow>
<SettingRow
name={t['com.affine.settings.editorSettings.edgeless.style']()}
@ -255,11 +274,14 @@ export const ConnectorSettings = () => {
]()}
desc={''}
>
<Menu items={startEndPointItems}>
<MenuTrigger className={menuTrigger}>
{String(settings.connector.frontEndpointStyle)}
</MenuTrigger>
</Menu>
<DropdownMenu
items={startEndPointItems}
trigger={
<MenuTrigger className={menuTrigger}>
{String(settings.connector.frontEndpointStyle)}
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t[
@ -267,11 +289,14 @@ export const ConnectorSettings = () => {
]()}
desc={''}
>
<Menu items={endEndPointItems}>
<MenuTrigger className={menuTrigger}>
{String(settings.connector.rearEndpointStyle)}
</MenuTrigger>
</Menu>
<DropdownMenu
items={endEndPointItems}
trigger={
<MenuTrigger className={menuTrigger}>
{String(settings.connector.rearEndpointStyle)}
</MenuTrigger>
}
/>
</SettingRow>
</>
);

View File

@ -1,5 +1,4 @@
import {
Menu,
MenuItem,
MenuTrigger,
RadioGroup,
@ -9,6 +8,7 @@ import { SettingRow } from '@affine/component/setting-components';
import { useI18n } from '@affine/i18n';
import { useMemo, useState } from 'react';
import { DropdownMenu } from '../menu';
import { menuTrigger, settingWrapper } from '../style.css';
import { EdgelessSnapshot } from './snapshot';
@ -54,11 +54,14 @@ export const MindMapSettings = () => {
name={t['com.affine.settings.editorSettings.edgeless.style']()}
desc={''}
>
<Menu items={<MenuItem>Style 1</MenuItem>}>
<MenuTrigger className={menuTrigger} disabled>
Style 1
</MenuTrigger>
</Menu>
<DropdownMenu
items={<MenuItem>Style 1</MenuItem>}
trigger={
<MenuTrigger className={menuTrigger} disabled>
Style 1
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t[

View File

@ -1,5 +1,4 @@
import {
Menu,
MenuItem,
MenuTrigger,
RadioGroup,
@ -17,7 +16,9 @@ import {
import { useFramework, useLiveData } from '@toeverything/infra';
import { useCallback, useMemo } from 'react';
import { DropdownMenu } from '../menu';
import { menuTrigger, settingWrapper } from '../style.css';
import { Point } from './point';
import { EdgelessSnapshot } from './snapshot';
const CORNER_SIZE = [
@ -91,7 +92,12 @@ export const NoteSettings = () => {
};
const isSelected = background === value;
return (
<MenuItem key={name} onSelect={handler} selected={isSelected}>
<MenuItem
key={name}
onSelect={handler}
selected={isSelected}
prefix={<Point color={value} />}
>
{name}
</MenuItem>
);
@ -140,6 +146,13 @@ export const NoteSettings = () => {
});
}, [editorSetting, settings]);
const currentColor = useMemo(() => {
const { background } = settings['affine:note'];
return Object.entries(NoteBackgroundColor).find(
([, value]) => value === background
);
}, [settings]);
return (
<>
<EdgelessSnapshot
@ -153,31 +166,45 @@ export const NoteSettings = () => {
]()}
desc={''}
>
<Menu items={backgroundItems}>
<MenuTrigger className={menuTrigger}>
{String(settings['affine:note'].background)}
</MenuTrigger>
</Menu>
{currentColor ? (
<DropdownMenu
items={backgroundItems}
trigger={
<MenuTrigger
className={menuTrigger}
prefix={<Point color={currentColor[1]} />}
>
{currentColor[0]}
</MenuTrigger>
}
/>
) : null}
</SettingRow>
<SettingRow
name={t['com.affine.settings.editorSettings.edgeless.note.corners']()}
desc={''}
>
<Menu items={cornerItems}>
<MenuTrigger className={menuTrigger}>
{String(settings['affine:note'].edgeless.style.borderRadius)}
</MenuTrigger>
</Menu>
<DropdownMenu
items={cornerItems}
trigger={
<MenuTrigger className={menuTrigger}>
{String(settings['affine:note'].edgeless.style.borderRadius)}
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t['com.affine.settings.editorSettings.edgeless.note.shadow']()}
desc={''}
>
<Menu items={shadowItems}>
<MenuTrigger className={menuTrigger}>
{String(settings['affine:note'].edgeless.style.shadowType)}
</MenuTrigger>
</Menu>
<DropdownMenu
items={shadowItems}
trigger={
<MenuTrigger className={menuTrigger}>
{String(settings['affine:note'].edgeless.style.shadowType)}
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t['com.affine.settings.editorSettings.edgeless.note.border']()}

View File

@ -1,4 +1,4 @@
import { Menu, MenuItem, MenuTrigger, Slider } from '@affine/component';
import { MenuItem, MenuTrigger, Slider } from '@affine/component';
import { SettingRow } from '@affine/component/setting-components';
import { EditorSettingService } from '@affine/core/modules/editor-settting';
import { useI18n } from '@affine/i18n';
@ -6,6 +6,7 @@ import { LineColor } from '@blocksuite/blocks';
import { useFramework, useLiveData } from '@toeverything/infra';
import { useCallback, useMemo } from 'react';
import { DropdownMenu } from '../menu';
import { menuTrigger } from '../style.css';
import { EdgelessSnapshot } from './snapshot';
@ -39,7 +40,6 @@ export const PenSettings = () => {
},
[editorSetting]
);
return (
<>
<EdgelessSnapshot
@ -51,11 +51,14 @@ export const PenSettings = () => {
name={t['com.affine.settings.editorSettings.edgeless.pen.color']()}
desc={''}
>
<Menu items={colorItems}>
<MenuTrigger className={menuTrigger}>
{String(settings.brush.color)}
</MenuTrigger>
</Menu>
<DropdownMenu
items={colorItems}
trigger={
<MenuTrigger className={menuTrigger}>
{String(settings.brush.color)}
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t['com.affine.settings.editorSettings.edgeless.pen.thickness']()}

View File

@ -0,0 +1,21 @@
import { cssVarV2 } from '@toeverything/theme/v2';
export const Point = ({
color,
size = 8,
}: {
color: string;
size?: number;
}) => {
return (
<div
style={{
width: size,
height: size,
borderRadius: '50%',
backgroundColor: `var(${color})`,
border: `1px solid ${cssVarV2('layer/insideBorder/border')}`,
}}
></div>
);
};

View File

@ -1,5 +1,4 @@
import {
Menu,
MenuItem,
MenuTrigger,
RadioGroup,
@ -10,6 +9,7 @@ import { SettingRow } from '@affine/component/setting-components';
import { useI18n } from '@affine/i18n';
import { useMemo, useState } from 'react';
import { DropdownMenu } from '../menu';
import { menuTrigger, settingWrapper, shapeIndicator } from '../style.css';
import { EdgelessSnapshot } from './snapshot';
@ -164,11 +164,14 @@ export const ShapeSettings = () => {
]()}
desc={''}
>
<Menu items={<MenuItem>Yellow</MenuItem>}>
<MenuTrigger className={menuTrigger} disabled>
Yellow
</MenuTrigger>
</Menu>
<DropdownMenu
items={<MenuItem>Yellow</MenuItem>}
trigger={
<MenuTrigger className={menuTrigger} disabled>
Yellow
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t[
@ -176,11 +179,14 @@ export const ShapeSettings = () => {
]()}
desc={''}
>
<Menu items={<MenuItem>Yellow</MenuItem>}>
<MenuTrigger className={menuTrigger} disabled>
Yellow
</MenuTrigger>
</Menu>
<DropdownMenu
items={<MenuItem>Yellow</MenuItem>}
trigger={
<MenuTrigger className={menuTrigger} disabled>
Yellow
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t[
@ -217,21 +223,28 @@ export const ShapeSettings = () => {
]()}
desc={''}
>
<Menu items={<MenuItem>Yellow</MenuItem>}>
<MenuTrigger className={menuTrigger} disabled>
Yellow
</MenuTrigger>
</Menu>
<DropdownMenu
items={<MenuItem>Yellow</MenuItem>}
trigger={
<MenuTrigger className={menuTrigger} disabled>
Yellow
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t['com.affine.settings.editorSettings.edgeless.shape.font']()}
desc={''}
>
<Menu items={<MenuItem>Inter</MenuItem>}>
<MenuTrigger className={menuTrigger} disabled>
Inter
</MenuTrigger>
</Menu>
{' '}
<DropdownMenu
items={<MenuItem>Inter</MenuItem>}
trigger={
<MenuTrigger className={menuTrigger} disabled>
Inter
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t[
@ -239,11 +252,14 @@ export const ShapeSettings = () => {
]()}
desc={''}
>
<Menu items={<MenuItem>15px</MenuItem>}>
<MenuTrigger className={menuTrigger} disabled>
15px
</MenuTrigger>
</Menu>
<DropdownMenu
items={<MenuItem>15px</MenuItem>}
trigger={
<MenuTrigger className={menuTrigger} disabled>
15px
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t[
@ -251,11 +267,14 @@ export const ShapeSettings = () => {
]()}
desc={''}
>
<Menu items={<MenuItem>Regular</MenuItem>}>
<MenuTrigger className={menuTrigger} disabled>
Regular
</MenuTrigger>
</Menu>
<DropdownMenu
items={<MenuItem>Regular</MenuItem>}
trigger={
<MenuTrigger className={menuTrigger} disabled>
Regular
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t[

View File

@ -1,5 +1,4 @@
import {
Menu,
MenuItem,
MenuTrigger,
RadioGroup,
@ -19,7 +18,9 @@ import {
import { useFramework, useLiveData } from '@toeverything/infra';
import { useCallback, useMemo } from 'react';
import { DropdownMenu } from '../menu';
import { menuTrigger, settingWrapper } from '../style.css';
import { Point } from './point';
import { EdgelessSnapshot } from './snapshot';
export const TextSettings = () => {
@ -73,7 +74,12 @@ export const TextSettings = () => {
};
const isSelected = color === value;
return (
<MenuItem key={name} onSelect={handler} selected={isSelected}>
<MenuItem
key={name}
onSelect={handler}
selected={isSelected}
prefix={<Point color={value} />}
>
{name}
</MenuItem>
);
@ -125,6 +131,10 @@ export const TextSettings = () => {
});
}, [editorSetting, settings]);
const currentColor = useMemo(() => {
const { color } = settings['affine:edgeless-text'];
return Object.entries(LineColor).find(([, value]) => value === color);
}, [settings]);
return (
<>
<EdgelessSnapshot
@ -136,11 +146,19 @@ export const TextSettings = () => {
name={t['com.affine.settings.editorSettings.edgeless.text.color']()}
desc={''}
>
<Menu items={colorItems}>
<MenuTrigger className={menuTrigger}>
{String(settings['affine:edgeless-text'].color)}
</MenuTrigger>
</Menu>
{currentColor ? (
<DropdownMenu
items={colorItems}
trigger={
<MenuTrigger
className={menuTrigger}
prefix={<Point color={currentColor[1]} />}
>
{currentColor[0]}
</MenuTrigger>
}
/>
) : null}
</SettingRow>
<SettingRow
name={t[
@ -148,11 +166,14 @@ export const TextSettings = () => {
]()}
desc={''}
>
<Menu items={fontFamilyItems}>
<MenuTrigger className={menuTrigger}>
{FontFamilyMap[settings['affine:edgeless-text'].fontFamily]}
</MenuTrigger>
</Menu>
<DropdownMenu
items={fontFamilyItems}
trigger={
<MenuTrigger className={menuTrigger}>
{FontFamilyMap[settings['affine:edgeless-text'].fontFamily]}
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t[
@ -160,11 +181,14 @@ export const TextSettings = () => {
]()}
desc={''}
>
<Menu items={fontStyleItems}>
<MenuTrigger className={menuTrigger}>
{String(settings['affine:edgeless-text'].fontStyle)}
</MenuTrigger>
</Menu>
<DropdownMenu
items={fontStyleItems}
trigger={
<MenuTrigger className={menuTrigger}>
{String(settings['affine:edgeless-text'].fontStyle)}
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t[
@ -172,11 +196,14 @@ export const TextSettings = () => {
]()}
desc={''}
>
<Menu items={fontWeightItems}>
<MenuTrigger className={menuTrigger}>
{settings['affine:edgeless-text'].fontWeight}
</MenuTrigger>
</Menu>
<DropdownMenu
items={fontWeightItems}
trigger={
<MenuTrigger className={menuTrigger}>
{settings['affine:edgeless-text'].fontWeight}
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t['com.affine.settings.editorSettings.edgeless.text.alignment']()}

View File

@ -38,6 +38,7 @@ import {
} from 'react';
import { Virtuoso } from 'react-virtuoso';
import { DropdownMenu } from './menu';
import * as styles from './style.css';
const FontFamilySettings = () => {
@ -94,13 +95,18 @@ const FontFamilySettings = () => {
);
return (
<RadioGroup
items={radioItems}
value={settings.fontFamily}
width={250}
className={styles.settingWrapper}
onChange={handleFontFamilyChange}
/>
<SettingRow
name={t['com.affine.appearanceSettings.font.title']()}
desc={t['com.affine.appearanceSettings.font.description']()}
>
<RadioGroup
items={radioItems}
value={settings.fontFamily}
width={250}
className={styles.settingWrapper}
onChange={handleFontFamilyChange}
/>
</SettingRow>
);
};
@ -303,13 +309,71 @@ const NewDocDefaultModeSettings = () => {
[editorSettingService.editorSetting]
);
return (
<RadioGroup
items={radioItems}
value={settings.newDocDefaultMode}
width={250}
className={styles.settingWrapper}
onChange={updateNewDocDefaultMode}
/>
<SettingRow
name={t[
'com.affine.settings.editorSettings.general.default-new-doc.title'
]()}
desc={t[
'com.affine.settings.editorSettings.general.default-new-doc.description'
]()}
>
<RadioGroup
items={radioItems}
value={settings.newDocDefaultMode}
width={250}
className={styles.settingWrapper}
onChange={updateNewDocDefaultMode}
/>
</SettingRow>
);
};
export const DeFaultCodeBlockSettings = () => {
const t = useI18n();
return (
<>
<SettingRow
name={t[
'com.affine.settings.editorSettings.general.default-code-block.language.title'
]()}
desc={t[
'com.affine.settings.editorSettings.general.default-code-block.language.description'
]()}
>
<DropdownMenu
items={<MenuItem>Plain Text</MenuItem>}
trigger={
<MenuTrigger className={styles.menuTrigger} disabled>
Plain Text
</MenuTrigger>
}
/>
</SettingRow>
<SettingRow
name={t[
'com.affine.settings.editorSettings.general.default-code-block.wrap.title'
]()}
desc={t[
'com.affine.settings.editorSettings.general.default-code-block.wrap.description'
]()}
>
<Switch />
</SettingRow>
</>
);
};
export const SpellCheckSettings = () => {
const t = useI18n();
return (
<SettingRow
name={t['com.affine.settings.editorSettings.general.spell-check.title']()}
desc={t[
'com.affine.settings.editorSettings.general.spell-check.description'
]()}
>
<Switch />
</SettingRow>
);
};
@ -323,62 +387,11 @@ export const General = () => {
>
<Switch />
</SettingRow>
<SettingRow
name={t['com.affine.appearanceSettings.font.title']()}
desc={t['com.affine.appearanceSettings.font.description']()}
>
<FontFamilySettings />
</SettingRow>
<FontFamilySettings />
<CustomFontFamilySettings />
<SettingRow
name={t[
'com.affine.settings.editorSettings.general.default-new-doc.title'
]()}
desc={t[
'com.affine.settings.editorSettings.general.default-new-doc.description'
]()}
>
<NewDocDefaultModeSettings />
</SettingRow>
<SettingRow
name={t[
'com.affine.settings.editorSettings.general.default-code-block.language.title'
]()}
desc={t[
'com.affine.settings.editorSettings.general.default-code-block.language.description'
]()}
>
<Menu
contentOptions={{
className: styles.menu,
}}
items={<MenuItem>Plain Text</MenuItem>}
>
<MenuTrigger className={styles.menuTrigger} disabled>
Plain Text
</MenuTrigger>
</Menu>
</SettingRow>
<SettingRow
name={t[
'com.affine.settings.editorSettings.general.default-code-block.wrap.title'
]()}
desc={t[
'com.affine.settings.editorSettings.general.default-code-block.wrap.description'
]()}
>
<Switch />
</SettingRow>
<SettingRow
name={t[
'com.affine.settings.editorSettings.general.spell-check.title'
]()}
desc={t[
'com.affine.settings.editorSettings.general.spell-check.description'
]()}
>
<Switch />
</SettingRow>
<NewDocDefaultModeSettings />
<DeFaultCodeBlockSettings />
<SpellCheckSettings />
</SettingWrapper>
);
};

View File

@ -0,0 +1,23 @@
import { Menu } from '@affine/component';
import { type ReactNode } from 'react';
export const DropdownMenu = ({
items,
trigger,
}: {
items: ReactNode;
trigger: ReactNode;
}) => {
return (
<Menu
items={items}
contentOptions={{
style: {
width: '250px',
},
}}
>
{trigger}
</Menu>
);
};