Skip to content

Commit 2b45683

Browse files
committed
chore: better code
1 parent 06fd01e commit 2b45683

File tree

10 files changed

+53
-42
lines changed

10 files changed

+53
-42
lines changed

.eslintrc.cjs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ module.exports = {
6868
{ blankLine: 'always', prev: 'block', next: '*' },
6969

7070
],
71-
'@typescript-eslint/no-explicit-any': 'off',
72-
'import/no-named-as-default': 'off',
73-
'import/no-named-as-default-member': 'off',
7471
},
7572
ignorePatterns: ['src/**/*.test.ts', '**/__tests__/**/*.json', 'package.json', '__mocks__/*.ts'],
7673
};

src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export function SelectionToolbar() {
4646
<div
4747
ref={ref}
4848
className={
49-
'selection-toolbar pointer-events-none transform transition-opacity duration-200 absolute z-[100] flex min-h-[32px] w-fit flex-grow items-center rounded-lg bg-[var(--surface-primary)] px-2 py-[var(--spacing-spacing-xs)] opacity-0 shadow-toolbar'
49+
'selection-toolbar pointer-events-none transform transition-opacity duration-200 absolute z-[100] flex min-h-[32px] w-fit flex-grow items-center rounded-[var(--border-radius-border-radius-l)] bg-[var(--surface-primary)] px-2 py-[var(--spacing-spacing-xs)] opacity-0 shadow-toolbar'
5050
}
5151
onMouseDown={(e) => {
5252
// prevent toolbar from taking focus away from editor

src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import Button from '@mui/material/Button';
22
import { PopoverProps } from '@mui/material/Popover';
3-
import { useCallback, useEffect, useRef, useState } from 'react';
3+
import { cloneElement, useCallback, useEffect, useRef, useState } from 'react';
44
import { useTranslation } from 'react-i18next';
55
import { Element } from 'slate';
66
import { useSlateStatic } from 'slate-react';
7-
import React from 'react';
87

98
import { YjsEditor } from '@/application/slate-yjs';
109
import { CustomEditor } from '@/application/slate-yjs/command';
@@ -36,20 +35,29 @@ const popoverProps: Partial<PopoverProps> = {
3635
},
3736
};
3837

38+
// Define allowed translation keys for align options
39+
const alignLabelKeys = [
40+
'toolbar.alignLeft',
41+
'toolbar.alignCenter',
42+
'toolbar.alignRight',
43+
] as const;
44+
45+
type AlignLabelKey = typeof alignLabelKeys[number];
46+
3947
const alignOptions = [
4048
{
4149
icon: <AlignLeftSvg className="h-5 w-5" />,
42-
labelKey: 'toolbar.alignLeft',
50+
labelKey: 'toolbar.alignLeft' as AlignLabelKey,
4351
type: AlignType.Left,
4452
},
4553
{
4654
icon: <AlignCenterSvg className="h-5 w-5" />,
47-
labelKey: 'toolbar.alignCenter',
55+
labelKey: 'toolbar.alignCenter' as AlignLabelKey,
4856
type: AlignType.Center,
4957
},
5058
{
5159
icon: <AlignRightSvg className="h-5 w-5" />,
52-
labelKey: 'toolbar.alignRight',
60+
labelKey: 'toolbar.alignRight' as AlignLabelKey,
5361
type: AlignType.Right,
5462
},
5563
];
@@ -76,6 +84,7 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?:
7684
const getAlign = useCallback(() => {
7785
try {
7886
const node = getNode();
87+
7988
return (node.data as BlockData).align;
8089
} catch (e) {
8190
return;
@@ -93,7 +102,8 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?:
93102
const activeIcon = useCallback(() => {
94103
const align = getAlign();
95104
const option = alignOptions.find(opt => opt.type === align) || alignOptions[0];
96-
return React.cloneElement(option.icon, {
105+
106+
return cloneElement(option.icon, {
97107
className: `h-5 w-5 ${align ? 'text-fill-default' : ''}`
98108
});
99109
}, [getAlign]);
@@ -103,6 +113,7 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?:
103113
return () => {
104114
try {
105115
const node = getNode();
116+
106117
CustomEditor.setBlockData(editor, node.blockId as string, { align });
107118
handleClose();
108119
rePosition();
@@ -186,7 +197,7 @@ export function Align({ blockId, enabled = true }: { blockId?: string; enabled?:
186197
})
187198
}}
188199
>
189-
{t(option.labelKey as any)}
200+
{t(option.labelKey)}
190201
</Button>
191202
))}
192203
</div>

src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import { ReactComponent as BulletedListSvg } from '@/assets/icons/bulleted_list.
1010

1111
import ActionButton from './ActionButton';
1212

13-
14-
1513
export function BulletedList() {
1614
const { t } = useTranslation();
1715
const editor = useSlateStatic() as YjsEditor;

src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import { renderColor } from '@/utils/color';
1414

1515
import ActionButton from './ActionButton';
1616

17-
18-
1917
function Color() {
2018
const { t } = useTranslation();
2119
const { visible: toolbarVisible } = useSelectionToolbarContext();

src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,38 @@ const popoverProps: Partial<PopoverProps> = {
3939
},
4040
};
4141

42+
// Define allowed translation keys for heading options
43+
const headingLabelKeys = [
44+
'editor.text',
45+
'document.slashMenu.name.heading1',
46+
'document.slashMenu.name.heading2',
47+
'document.slashMenu.name.heading3',
48+
] as const;
49+
50+
type HeadingLabelKey = typeof headingLabelKeys[number];
51+
4252
const headingOptions = [
4353
{
4454
icon: <ParagraphSvg className="h-5 w-5" />,
45-
labelKey: 'editor.text',
55+
labelKey: 'editor.text' as HeadingLabelKey,
4656
isActive: (isParagraph: () => boolean, _isActivated: (level: number) => boolean) => isParagraph(),
4757
onClick: (toParagraph: () => void, _toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toParagraph(); setOpen(false); },
4858
},
4959
{
5060
icon: <Heading1 className="h-5 w-5" />,
51-
labelKey: 'document.slashMenu.name.heading1',
61+
labelKey: 'document.slashMenu.name.heading1' as HeadingLabelKey,
5262
isActive: (_isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(1),
5363
onClick: (_toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(1)(); setOpen(false); },
5464
},
5565
{
5666
icon: <Heading2 className="h-5 w-5" />,
57-
labelKey: 'document.slashMenu.name.heading2',
67+
labelKey: 'document.slashMenu.name.heading2' as HeadingLabelKey,
5868
isActive: (_isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(2),
5969
onClick: (_toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(2)(); setOpen(false); },
6070
},
6171
{
6272
icon: <Heading3 className="h-5 w-5" />,
63-
labelKey: 'document.slashMenu.name.heading3',
73+
labelKey: 'document.slashMenu.name.heading3' as HeadingLabelKey,
6474
isActive: (_isParagraph: () => boolean, isActivated: (level: number) => boolean) => isActivated(3),
6575
onClick: (_toParagraph: () => void, toHeading: (level: number) => () => void, setOpen: (v: boolean) => void) => () => { toHeading(3)(); setOpen(false); },
6676
},
@@ -211,7 +221,7 @@ export function Heading() {
211221
})
212222
}}
213223
>
214-
{String(t(opt.labelKey as any))}
224+
{t(opt.labelKey)}
215225
{opt.isActive(isParagraph, isActivated) && (
216226
<span className="ml-auto flex items-center">
217227
<TickIcon className="h-5 w-5 text-[var(--icon-secondary)]" />

src/components/editor/components/toolbar/selection-toolbar/actions/MoreOptions.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,14 @@ import { ReactComponent as StrikeThroughSvg } from '@/assets/icons/strikethrough
1212
import Popover from '@/components/_shared/popover/Popover';
1313

1414
import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation';
15-
1615
import ActionButton from './ActionButton';
1716
import { useRef, useState } from 'react';
1817

19-
20-
2118
const options = [
2219
{
2320
icon: <StrikeThroughSvg className="h-5 w-5" />,
24-
labelKey: 'editor.strikethrough',
25-
onClick: (editor: any, setOpen: (v: boolean) => void) => {
21+
labelKey: 'editor.strikethrough' as const,
22+
onClick: (editor: Editor, setOpen: (v: boolean) => void) => {
2623
CustomEditor.toggleMark(editor, {
2724
key: EditorMarkFormat.StrikeThrough,
2825
value: true,
@@ -32,8 +29,8 @@ const options = [
3229
},
3330
{
3431
icon: <FormulaSvg className="h-5 w-5" />,
35-
labelKey: 'document.plugins.createInlineMathEquation',
36-
onClick: (editor: any, setOpen: (v: boolean) => void) => {
32+
labelKey: 'document.plugins.createInlineMathEquation' as const,
33+
onClick: (editor: Editor, setOpen: (v: boolean) => void) => {
3734
const selection = editor.selection;
3835

3936
if (!selection) return;
@@ -61,12 +58,12 @@ const options = [
6158
} else {
6259
const [entry] = editor.nodes({
6360
at: selection,
64-
match: (n: any) => !Editor.isEditor(n) && Text.isText(n) && (n as any).formula !== undefined,
61+
match: (n) => !Editor.isEditor(n) && Text.isText(n) && n.formula !== undefined,
6562
});
6663

6764
if (!entry) return;
6865
const [node, path] = entry;
69-
const formula = (node).formula;
66+
const { formula } = node as Text;
7067

7168
if (!formula) return;
7269
editor.select(path);
@@ -162,7 +159,7 @@ export default function MoreOptions() {
162159
})
163160
}}
164161
>
165-
{t(opt.labelKey as any)}
162+
{t(opt.labelKey)}
166163
</Button>
167164
))}
168165
</div>

src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import { ReactComponent as NumberedListSvg } from '@/assets/icons/numbered_list.
1010

1111
import ActionButton from './ActionButton';
1212

13-
14-
1513
export function NumberedList() {
1614
const { t } = useTranslation();
1715
const editor = useSlateStatic() as YjsEditor;

src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import { ReactComponent as QuoteSvg } from '@/assets/icons/quote.svg';
1010

1111
import ActionButton from './ActionButton';
1212

13-
14-
1513
export function Quote() {
1614
const { t } = useTranslation();
1715
const editor = useSlateStatic() as YjsEditor;

src/components/editor/components/toolbar/selection-toolbar/actions/TurnInto.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useSlateStatic } from 'slate-react';
66
import { YjsEditor } from '@/application/slate-yjs';
77
import { CustomEditor } from '@/application/slate-yjs/command';
88
import { getBlockEntry } from '@/application/slate-yjs/utils/editor';
9-
import { type HeadingBlockData, BlockType } from '@/application/types';
9+
import { type HeadingBlockData, BlockType, BlockData } from '@/application/types';
1010
import { ReactComponent as BulletedListSvg } from '@/assets/icons/bulleted_list.svg';
1111
import { ReactComponent as Heading1 } from '@/assets/icons/h1.svg';
1212
import { ReactComponent as Heading2 } from '@/assets/icons/h2.svg';
@@ -31,7 +31,7 @@ type BlockOption = {
3131
icon: FC<SVGProps<SVGSVGElement>>;
3232
label: string;
3333
blockType: BlockType;
34-
data?: any;
34+
data?: BlockData | HeadingBlockData;
3535
group: 'text' | 'list' | 'toggle' | 'other';
3636
};
3737

@@ -121,6 +121,10 @@ const blockOptions: BlockOption[] = [
121121
},
122122
];
123123

124+
function isHeadingBlockData(data: BlockOption['data']): data is HeadingBlockData {
125+
return !!data && typeof (data as HeadingBlockData).level === 'number';
126+
}
127+
124128
function TurnInfo() {
125129
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
126130
const [selectedType, setSelectedType] = useState<BlockOption['type'] | null>(null);
@@ -191,7 +195,7 @@ function TurnInfo() {
191195
if (!node) return;
192196

193197
if (node.type === option.blockType &&
194-
(!option.data || (node.type === BlockType.HeadingBlock && (node.data as HeadingBlockData).level === option.data.level))) {
198+
(!option.data || (node.type === BlockType.HeadingBlock && isHeadingBlockData(option.data) && (node.data as HeadingBlockData).level === option.data.level))) {
195199
CustomEditor.turnToBlock(editor, node.blockId as string, BlockType.Paragraph, {});
196200
} else {
197201
CustomEditor.turnToBlock(editor, node.blockId as string, option.blockType, option.data || {});
@@ -240,14 +244,14 @@ function TurnInfo() {
240244
if (!currentType) return false;
241245
if (option.type === 'paragraph' && currentType === 'paragraph') return true;
242246
if (option.type.startsWith('heading') && currentType === 'heading') {
243-
// heading1/2/3 匹配 level
244-
return option.data && option.data.level === currentLevel;
247+
// heading1/2/3 --- level
248+
return isHeadingBlockData(option.data) && option.data.level === currentLevel;
245249
}
246250

247251
if (option.type === 'bulleted' && currentType === 'bulleted') return true;
248252
if (option.type === 'numbered' && currentType === 'numbered') return true;
249253
if (option.type === 'toggle' && currentType === 'toggle') return true;
250-
if (option.type.startsWith('toggleHeading') && currentType === 'toggle' && option.data && option.data.level === currentLevel) return true;
254+
if (option.type.startsWith('toggleHeading') && currentType === 'toggle' && isHeadingBlockData(option.data) && option.data.level === currentLevel) return true;
251255
if (option.type === 'quote' && currentType === 'quote') return true;
252256
return false;
253257
}
@@ -310,7 +314,7 @@ function TurnInfo() {
310314
{suggestionOptions.map((option, index) => (
311315
<MenuItem
312316
key={option.type}
313-
ref={el => getButtonProps(index).ref?.(el as any)}
317+
ref={el => getButtonProps(index).ref?.(el as unknown as HTMLButtonElement)}
314318
selected={selectedIndex === index}
315319
className="text--text-primary"
316320
sx={{
@@ -360,7 +364,7 @@ function TurnInfo() {
360364
{turnIntoOptions.map((option, index) => (
361365
<MenuItem
362366
key={option.type}
363-
ref={el => getButtonProps(index + suggestionOptions.length).ref?.(el as any)}
367+
ref={el => getButtonProps(index + suggestionOptions.length).ref?.(el as unknown as HTMLButtonElement)}
364368
selected={selectedIndex === index + suggestionOptions.length}
365369
className="text--text-primary"
366370
sx={{

0 commit comments

Comments
 (0)