Skip to content

Commit 2312621

Browse files
committed
perf(YfmNote): reduce number of yfm-note tooltip re-renders
1 parent 4d8de5d commit 2312621

3 files changed

Lines changed: 118 additions & 89 deletions

File tree

packages/editor/src/extensions/yfm/YfmNote/plugins/YfmNoteTooltipPlugin/index.scss renamed to packages/editor/src/extensions/yfm/YfmNote/plugins/YfmNoteTooltipPlugin/YfmNoteToolbar.scss

File renamed without changes.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import {useCallback, useMemo} from 'react';
2+
3+
import {CircleInfo, TrashBin} from '@gravity-ui/icons';
4+
import {useLatest} from 'react-use';
5+
6+
import type {Node} from '#pm/model';
7+
import type {Transaction} from '#pm/state';
8+
import type {EditorView} from '#pm/view';
9+
import {i18n} from 'src/i18n/yfm-note';
10+
import {typedMemo} from 'src/react-utils/memo';
11+
import {Toolbar, type ToolbarData, ToolbarDataType} from 'src/toolbar';
12+
import {removeNode} from 'src/utils/remove-node';
13+
14+
import './YfmNoteToolbar.scss';
15+
16+
const ToolbarMemoized = typedMemo(Toolbar);
17+
18+
enum YfmNoteType {
19+
tip = 'tip',
20+
info = 'info',
21+
warning = 'warning',
22+
alert = 'alert',
23+
}
24+
25+
const isEnable = () => true;
26+
const isActive = () => false;
27+
28+
const changeType: (
29+
type: YfmNoteType,
30+
) => (params: {
31+
node: Node;
32+
pos: number;
33+
dispatch: EditorView['dispatch'];
34+
tr: Transaction;
35+
}) => void =
36+
(type) =>
37+
({node, pos, dispatch, tr}) => {
38+
dispatch(
39+
tr.setNodeMarkup(pos, null, {
40+
...node.attrs,
41+
class: `yfm-note yfm-accent-${type}`,
42+
'note-type': type,
43+
}),
44+
);
45+
};
46+
47+
export type YfmNoteToolbarProps = {
48+
node: Node;
49+
pos: number;
50+
editorView: EditorView;
51+
};
52+
53+
export function YfmNoteToolbar({pos, node, editorView}: YfmNoteToolbarProps) {
54+
const posRef = useLatest(pos);
55+
const nodeRef = useLatest(node);
56+
57+
const onFocus = useCallback(() => {
58+
editorView.focus();
59+
}, [editorView]);
60+
61+
const toolbarData = useMemo<ToolbarData<EditorView>>(() => {
62+
return [
63+
[YfmNoteType.info, YfmNoteType.tip, YfmNoteType.warning, YfmNoteType.alert].map(
64+
(type) => ({
65+
id: `note-type-${type}`,
66+
icon: {data: CircleInfo},
67+
title: i18n(type),
68+
type: ToolbarDataType.SingleButton,
69+
isActive,
70+
isEnable,
71+
exec: (view) =>
72+
changeType(type)({
73+
pos: posRef.current,
74+
node: nodeRef.current,
75+
tr: view.state.tr,
76+
dispatch: view.dispatch,
77+
}),
78+
}),
79+
),
80+
[
81+
{
82+
id: 'note-remove',
83+
icon: {data: TrashBin},
84+
title: i18n('remove'),
85+
type: ToolbarDataType.SingleButton,
86+
isActive,
87+
isEnable,
88+
exec: (view) =>
89+
removeNode({
90+
pos: posRef.current,
91+
node: nodeRef.current,
92+
tr: view.state.tr,
93+
dispatch: view.dispatch,
94+
}),
95+
},
96+
],
97+
];
98+
}, [nodeRef, posRef]);
99+
100+
return (
101+
<ToolbarMemoized
102+
editor={editorView}
103+
focus={onFocus}
104+
// the yfm class allows to access css variables
105+
// https://github.com/diplodoc-platform/transform/blob/master/src/scss/_common.scss#L17
106+
className="yfm g-md-yfm-note-toolbar"
107+
qa="g-md-toolbar-yfm-note"
108+
data={toolbarData}
109+
/>
110+
);
111+
}

packages/editor/src/extensions/yfm/YfmNote/plugins/YfmNoteTooltipPlugin/index.tsx

Lines changed: 7 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,21 @@
1-
import {CircleInfo, TrashBin} from '@gravity-ui/icons';
2-
import type {Node, Schema} from 'prosemirror-model';
3-
import {Plugin, type Transaction} from 'prosemirror-state';
4-
import type {EditorView} from 'prosemirror-view';
1+
import {Plugin} from 'prosemirror-state';
52

6-
import type {ExtensionDeps} from '../../../../../core';
7-
import {i18n} from '../../../../../i18n/yfm-note';
8-
import {BaseTooltipPluginView} from '../../../../../plugins/BaseTooltip';
9-
import {Toolbar, ToolbarDataType} from '../../../../../toolbar';
10-
import {removeNode} from '../../../../../utils/remove-node';
11-
import {noteType} from '../../../index';
3+
import type {ExtensionDeps} from '#core';
4+
import {BaseTooltipPluginView} from 'src/plugins/BaseTooltip';
125

13-
import './index.scss';
6+
import {noteType} from '../../utils';
147

15-
enum YfmNoteType {
16-
tip = 'tip',
17-
info = 'info',
18-
warning = 'warning',
19-
alert = 'alert',
20-
}
8+
import {YfmNoteToolbar} from './YfmNoteToolbar';
219

22-
const isEnable = () => true;
23-
const isActive = () => false;
24-
25-
const changeType: (
26-
type: YfmNoteType,
27-
) => (params: {
28-
schema: Schema;
29-
node: Node;
30-
pos: number;
31-
dispatch: EditorView['dispatch'];
32-
tr: Transaction;
33-
}) => void =
34-
(type) =>
35-
({node, pos, dispatch, tr}) => {
36-
dispatch(
37-
tr.setNodeMarkup(pos, null, {
38-
...node.attrs,
39-
class: `yfm-note yfm-accent-${type}`,
40-
'note-type': type,
41-
}),
42-
);
43-
};
44-
45-
export const yfmNoteTooltipPlugin = ({actions, schema}: ExtensionDeps) =>
10+
export const yfmNoteTooltipPlugin = (_deps: ExtensionDeps) =>
4611
new Plugin({
4712
view(view) {
4813
return new BaseTooltipPluginView(view, {
4914
idPrefix: 'yfm-note-tooltip',
5015
nodeType: noteType(view.state.schema),
5116
popupPlacement: ['bottom', 'top'],
5217
content: (view, {node, pos}) => (
53-
<Toolbar
54-
editor={actions}
55-
focus={() => view.focus()}
56-
// the yfm class allows to access css variables
57-
// https://github.com/diplodoc-platform/transform/blob/master/src/scss/_common.scss#L17
58-
className="yfm g-md-yfm-note-toolbar"
59-
qa="g-md-toolbar-yfm-note"
60-
data={[
61-
[
62-
YfmNoteType.info,
63-
YfmNoteType.tip,
64-
YfmNoteType.warning,
65-
YfmNoteType.alert,
66-
].map((type) => ({
67-
id: `note-type-${type}`,
68-
icon: {data: CircleInfo},
69-
title: i18n(type),
70-
type: ToolbarDataType.SingleButton,
71-
isActive,
72-
isEnable,
73-
exec: () =>
74-
changeType(type)({
75-
schema,
76-
pos: pos,
77-
node: node,
78-
tr: view.state.tr,
79-
dispatch: view.dispatch.bind(view),
80-
}),
81-
})),
82-
[
83-
{
84-
id: 'note-remove',
85-
icon: {data: TrashBin},
86-
title: i18n('remove'),
87-
type: ToolbarDataType.SingleButton,
88-
isActive,
89-
isEnable,
90-
exec: () =>
91-
removeNode({
92-
pos: pos,
93-
node: node,
94-
tr: view.state.tr,
95-
dispatch: view.dispatch.bind(view),
96-
}),
97-
},
98-
],
99-
]}
100-
/>
18+
<YfmNoteToolbar node={node} pos={pos} editorView={view} />
10119
),
10220
});
10321
},

0 commit comments

Comments
 (0)