Skip to content

Commit f126556

Browse files
committed
feat: add mindmap and connector settings (#8198)
### What changed? - Add `connector` label settings. - Add `mindmap` style settings. - Add skeleton loading placeholder. <div class='graphite__hidden'> <div>🎥 Video uploaded on Graphite:</div> <a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/31159d74-ef62-4c7f-b1d9-cde73047cf29.mov"> <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/31159d74-ef62-4c7f-b1d9-cde73047cf29.mov"> </a> </div> <video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/31159d74-ef62-4c7f-b1d9-cde73047cf29.mov">录屏2024-09-11 16.30.17.mov</video>
1 parent 85aa73b commit f126556

File tree

8 files changed

+420
-75
lines changed

8 files changed

+420
-75
lines changed

packages/frontend/core/src/components/affine/setting-modal/general-setting/editor/edgeless/connector.tsx

Lines changed: 253 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,23 @@ import { EditorSettingService } from '@affine/core/modules/editor-settting';
1010
import { useI18n } from '@affine/i18n';
1111
import {
1212
ConnectorMode,
13+
FontFamily,
14+
FontFamilyMap,
15+
FontStyle,
16+
FontWeightMap,
1317
LineColor,
1418
LineColorMap,
1519
PointStyle,
1620
StrokeStyle,
21+
TextAlign,
1722
} from '@blocksuite/blocks';
1823
import type { Doc } from '@blocksuite/store';
1924
import { useFramework, useLiveData } from '@toeverything/infra';
2025
import { useCallback, useMemo } from 'react';
2126

2227
import { DropdownMenu } from '../menu';
2328
import { menuTrigger, settingWrapper } from '../style.css';
24-
import { useColor } from '../utils';
29+
import { sortedFontWeightEntries, useColor } from '../utils';
2530
import { Point } from './point';
2631
import { EdgelessSnapshot } from './snapshot';
2732
import { getSurfaceBlock } from './utils';
@@ -31,6 +36,15 @@ enum ConnecterStyle {
3136
Scribbled = 'scribbled',
3237
}
3338

39+
enum ConnectorTextFontSize {
40+
'16px' = '16',
41+
'20px' = '20',
42+
'24px' = '24',
43+
'32px' = '32',
44+
'40px' = '40',
45+
'64px' = '64',
46+
}
47+
3448
export const ConnectorSettings = () => {
3549
const t = useI18n();
3650
const framework = useFramework();
@@ -191,6 +205,150 @@ export const ConnectorSettings = () => {
191205
});
192206
}, [editorSetting, settings]);
193207

208+
const alignItems = useMemo<RadioItem[]>(
209+
() => [
210+
{
211+
value: TextAlign.Left,
212+
label:
213+
t[
214+
'com.affine.settings.editorSettings.edgeless.text.alignment.left'
215+
](),
216+
},
217+
{
218+
value: TextAlign.Center,
219+
label:
220+
t[
221+
'com.affine.settings.editorSettings.edgeless.text.alignment.center'
222+
](),
223+
},
224+
{
225+
value: TextAlign.Right,
226+
label:
227+
t[
228+
'com.affine.settings.editorSettings.edgeless.text.alignment.right'
229+
](),
230+
},
231+
],
232+
[t]
233+
);
234+
235+
const textAlignment = settings.connector.labelStyle.textAlign;
236+
const setTextAlignment = useCallback(
237+
(value: TextAlign) => {
238+
editorSetting.set('connector', {
239+
labelStyle: {
240+
textAlign: value,
241+
},
242+
});
243+
},
244+
[editorSetting]
245+
);
246+
247+
const fontFamilyItems = useMemo(() => {
248+
const { fontFamily } = settings.connector.labelStyle;
249+
return Object.entries(FontFamily).map(([name, value]) => {
250+
const handler = () => {
251+
editorSetting.set('connector', {
252+
labelStyle: {
253+
fontFamily: value,
254+
},
255+
});
256+
};
257+
const isSelected = fontFamily === value;
258+
return (
259+
<MenuItem key={name} onSelect={handler} selected={isSelected}>
260+
{name}
261+
</MenuItem>
262+
);
263+
});
264+
}, [editorSetting, settings]);
265+
266+
const fontStyleItems = useMemo(() => {
267+
const { fontStyle } = settings.connector.labelStyle;
268+
return Object.entries(FontStyle).map(([name, value]) => {
269+
const handler = () => {
270+
editorSetting.set('connector', {
271+
labelStyle: {
272+
fontStyle: value,
273+
},
274+
});
275+
};
276+
const isSelected = fontStyle === value;
277+
return (
278+
<MenuItem key={name} onSelect={handler} selected={isSelected}>
279+
{name}
280+
</MenuItem>
281+
);
282+
});
283+
}, [editorSetting, settings]);
284+
285+
const fontWeightItems = useMemo(() => {
286+
const { fontWeight } = settings.connector.labelStyle;
287+
return sortedFontWeightEntries.map(([name, value]) => {
288+
const handler = () => {
289+
editorSetting.set('connector', {
290+
labelStyle: {
291+
fontWeight: value,
292+
},
293+
});
294+
};
295+
const isSelected = fontWeight === value;
296+
return (
297+
<MenuItem key={name} onSelect={handler} selected={isSelected}>
298+
{name}
299+
</MenuItem>
300+
);
301+
});
302+
}, [editorSetting, settings]);
303+
304+
const fontSizeItems = useMemo(() => {
305+
const { fontSize } = settings.connector.labelStyle;
306+
return Object.entries(ConnectorTextFontSize).map(([name, value]) => {
307+
const handler = () => {
308+
editorSetting.set('connector', {
309+
labelStyle: {
310+
fontSize: Number(value),
311+
},
312+
});
313+
};
314+
const isSelected = fontSize === Number(value);
315+
return (
316+
<MenuItem key={name} onSelect={handler} selected={isSelected}>
317+
{name}
318+
</MenuItem>
319+
);
320+
});
321+
}, [editorSetting, settings]);
322+
323+
const textColorItems = useMemo(() => {
324+
const { color } = settings.connector.labelStyle;
325+
return Object.entries(LineColor).map(([name, value]) => {
326+
const handler = () => {
327+
editorSetting.set('connector', {
328+
labelStyle: {
329+
color: value,
330+
},
331+
});
332+
};
333+
const isSelected = color === value;
334+
return (
335+
<MenuItem
336+
key={name}
337+
onSelect={handler}
338+
selected={isSelected}
339+
prefix={<Point color={value} />}
340+
>
341+
{name}
342+
</MenuItem>
343+
);
344+
});
345+
}, [editorSetting, settings]);
346+
347+
const textColor = useMemo(() => {
348+
const { color } = settings.connector.labelStyle;
349+
return getColorFromMap(color, LineColorMap);
350+
}, [getColorFromMap, settings]);
351+
194352
const getElements = useCallback((doc: Doc) => {
195353
const surface = getSurfaceBlock(doc);
196354
return surface?.getElementsByType('connector') || [];
@@ -309,6 +467,100 @@ export const ConnectorSettings = () => {
309467
}
310468
/>
311469
</SettingRow>
470+
<SettingRow
471+
name={t[
472+
'com.affine.settings.editorSettings.edgeless.shape.text-color'
473+
]()}
474+
desc={''}
475+
>
476+
{textColor ? (
477+
<DropdownMenu
478+
items={textColorItems}
479+
trigger={
480+
<MenuTrigger
481+
className={menuTrigger}
482+
prefix={<Point color={textColor.value} />}
483+
>
484+
{textColor.key}
485+
</MenuTrigger>
486+
}
487+
/>
488+
) : null}
489+
</SettingRow>
490+
<SettingRow
491+
name={t[
492+
'com.affine.settings.editorSettings.edgeless.text.font-family'
493+
]()}
494+
desc={''}
495+
>
496+
<DropdownMenu
497+
items={fontFamilyItems}
498+
trigger={
499+
<MenuTrigger className={menuTrigger}>
500+
{FontFamilyMap[settings.connector.labelStyle.fontFamily]}
501+
</MenuTrigger>
502+
}
503+
/>
504+
</SettingRow>
505+
<SettingRow
506+
name={t[
507+
'com.affine.settings.editorSettings.edgeless.shape.font-size'
508+
]()}
509+
desc={''}
510+
>
511+
<DropdownMenu
512+
items={fontSizeItems}
513+
trigger={
514+
<MenuTrigger className={menuTrigger}>
515+
{settings.connector.labelStyle.fontSize + 'px'}
516+
</MenuTrigger>
517+
}
518+
/>
519+
</SettingRow>
520+
<SettingRow
521+
name={t[
522+
'com.affine.settings.editorSettings.edgeless.text.font-style'
523+
]()}
524+
desc={''}
525+
>
526+
<DropdownMenu
527+
items={fontStyleItems}
528+
trigger={
529+
<MenuTrigger className={menuTrigger}>
530+
{settings.connector.labelStyle.fontStyle}
531+
</MenuTrigger>
532+
}
533+
/>
534+
</SettingRow>
535+
<SettingRow
536+
name={t[
537+
'com.affine.settings.editorSettings.edgeless.text.font-weight'
538+
]()}
539+
desc={''}
540+
>
541+
<DropdownMenu
542+
items={fontWeightItems}
543+
trigger={
544+
<MenuTrigger className={menuTrigger}>
545+
{FontWeightMap[settings.connector.labelStyle.fontWeight]}
546+
</MenuTrigger>
547+
}
548+
/>
549+
</SettingRow>
550+
<SettingRow
551+
name={t[
552+
'com.affine.settings.editorSettings.edgeless.shape.text-alignment'
553+
]()}
554+
desc={''}
555+
>
556+
<RadioGroup
557+
items={alignItems}
558+
value={textAlignment}
559+
width={250}
560+
className={settingWrapper}
561+
onChange={setTextAlignment}
562+
/>
563+
</SettingRow>
312564
</>
313565
);
314566
};

packages/frontend/core/src/components/affine/setting-modal/general-setting/editor/edgeless/docs/connector.json

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"type": "page",
33
"meta": {
4-
"id": "v4nxnq2Al2",
4+
"id": "gJEFfmo4QJ",
55
"title": "",
6-
"createDate": 1725459565290,
6+
"createDate": 1726038448921,
77
"tags": []
88
},
99
"blocks": {
1010
"type": "block",
11-
"id": "eDFE5HokPO",
11+
"id": "orzKfiHevj",
1212
"flavour": "affine:page",
1313
"version": 2,
1414
"props": {
@@ -20,37 +20,58 @@
2020
"children": [
2121
{
2222
"type": "block",
23-
"id": "ZksVPLRI0K",
23+
"id": "-48EmppaxI",
2424
"flavour": "affine:surface",
2525
"version": 5,
2626
"props": {
2727
"elements": {
28-
"hdqh5vFnzj": {
28+
"m_PwyyI76y": {
2929
"index": "a0",
30-
"seed": 263456687,
30+
"seed": 44330892,
3131
"frontEndpointStyle": "None",
32+
"labelOffset": {
33+
"distance": 0.5,
34+
"anchor": "center"
35+
},
36+
"labelStyle": {
37+
"color": "--affine-palette-line-black",
38+
"fontSize": 16,
39+
"fontFamily": "blocksuite:surface:Inter",
40+
"fontWeight": "400",
41+
"fontStyle": "normal",
42+
"textAlign": "center"
43+
},
44+
"labelXYWH": [235.6484375, 65.23828125, 200, 20],
3245
"mode": 2,
3346
"rearEndpointStyle": "Arrow",
3447
"rough": false,
3548
"roughness": 1.4,
3649
"source": {
37-
"position": [196.0625, 145.84765625]
50+
"position": [120.8515625, 146.44921875]
3851
},
3952
"stroke": "--affine-palette-line-grey",
4053
"strokeStyle": "solid",
4154
"strokeWidth": 2,
4255
"target": {
43-
"position": [418.5859375, 52.13671875]
56+
"position": [387.4453125, 4.02734375]
57+
},
58+
"text": {
59+
"affine:surface:text": true,
60+
"delta": [
61+
{
62+
"insert": "label"
63+
}
64+
]
4465
},
4566
"type": "connector",
46-
"id": "hdqh5vFnzj"
67+
"id": "m_PwyyI76y"
4768
}
4869
}
4970
},
5071
"children": [
5172
{
5273
"type": "block",
53-
"id": "TNgzGwq6Ct",
74+
"id": "-6UhNH7qhy",
5475
"flavour": "affine:frame",
5576
"version": 1,
5677
"props": {
@@ -63,10 +84,10 @@
6384
]
6485
},
6586
"background": "--affine-palette-transparent",
66-
"xywh": "[-13.38671875,-4.5625,739.3828125,192.51171875]",
67-
"index": "a0",
87+
"xywh": "[-12.04296875,-49.66796875,542.9765625,248.3984375]",
88+
"index": "Zz",
6889
"childElementIds": {
69-
"hdqh5vFnzj": true
90+
"m_PwyyI76y": true
7091
}
7192
},
7293
"children": []

0 commit comments

Comments
 (0)