Skip to content

Commit 3ebf14f

Browse files
authored
feat(toolbars): restructured toolbar configuration and presets (#509)
1 parent 15767d7 commit 3ebf14f

File tree

16 files changed

+1893
-98
lines changed

16 files changed

+1893
-98
lines changed

demo/components/Playground.tsx

Lines changed: 47 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
type UseMarkdownEditorProps,
1919
WysiwygPlaceholderOptions,
2020
logger,
21-
markupToolbarConfigs,
2221
useMarkdownEditor,
2322
wysiwygToolbarConfigs,
2423
} from '../../src';
@@ -29,8 +28,8 @@ import {Math} from '../../src/extensions/additional/Math';
2928
import {Mermaid} from '../../src/extensions/additional/Mermaid';
3029
import {YfmHtmlBlock} from '../../src/extensions/additional/YfmHtmlBlock';
3130
import {getSanitizeYfmHtmlBlock} from '../../src/extensions/additional/YfmHtmlBlock/utils';
32-
import {cloneDeep} from '../../src/lodash';
3331
import type {CodeEditor} from '../../src/markup';
32+
import {ToolbarsPreset} from '../../src/modules/toolbars/types';
3433
import {VERSION} from '../../src/version';
3534
import {getPlugins} from '../defaults/md-plugins';
3635
import useYfmHtmlBlockStyles from '../hooks/useYfmHtmlBlockStyles';
@@ -52,19 +51,6 @@ const fileUploadHandler: FileUploadHandler = async (file) => {
5251
return {url: URL.createObjectURL(file)};
5352
};
5453

55-
const mToolbarConfig = [
56-
...markupToolbarConfigs.mToolbarConfig,
57-
[markupToolbarConfigs.mMermaidButton, markupToolbarConfigs.mYfmHtmlBlockButton],
58-
];
59-
mToolbarConfig[2].push(markupToolbarConfigs.mMathListItem);
60-
61-
const wToolbarConfig = cloneDeep(wysiwygToolbarConfigs.wToolbarConfig);
62-
wToolbarConfig[2].push(wysiwygToolbarConfigs.wMathListItem);
63-
wToolbarConfig.push([
64-
wysiwygToolbarConfigs.wMermaidItemData,
65-
wysiwygToolbarConfigs.wYfmHtmlBlockItemData,
66-
]);
67-
6854
const wCommandMenuConfig = wysiwygToolbarConfigs.wCommandMenuConfig.concat(
6955
wysiwygToolbarConfigs.wMathInlineItemData,
7056
wysiwygToolbarConfigs.wMathBlockItemData,
@@ -92,6 +78,7 @@ export type PlaygroundProps = {
9278
escapeConfig?: EscapeConfig;
9379
wysiwygCommandMenuConfig?: wysiwygToolbarConfigs.WToolbarItemData[];
9480
markupToolbarConfig?: ToolbarGroupData<CodeEditor>[];
81+
toolbarsPreset?: ToolbarsPreset;
9582
onChangeEditorType?: (mode: MarkdownEditorMode) => void;
9683
onChangeSplitModeEnabled?: (splitModeEnabled: boolean) => void;
9784
directiveSyntax?: DirectiveSyntaxValue;
@@ -137,6 +124,7 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
137124
height,
138125
extraExtensions,
139126
extensionOptions,
127+
toolbarsPreset,
140128
wysiwygToolbarConfig,
141129
wysiwygCommandMenuConfig,
142130
markupConfigExtensions,
@@ -175,6 +163,47 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
175163

176164
const mdEditor = useMarkdownEditor(
177165
{
166+
preset: 'full',
167+
wysiwygConfig: {
168+
escapeConfig,
169+
placeholderOptions: placeholderOptions,
170+
extensions: (builder) => {
171+
builder
172+
.use(Math, {
173+
loadRuntimeScript: () => {
174+
import(
175+
/* webpackChunkName: "latex-runtime" */ '@diplodoc/latex-extension/runtime'
176+
);
177+
import(
178+
// @ts-expect-error // no types for styles
179+
/* webpackChunkName: "latex-styles" */ '@diplodoc/latex-extension/runtime/styles'
180+
);
181+
},
182+
})
183+
.use(Mermaid, {
184+
loadRuntimeScript: () => {
185+
import(
186+
/* webpackChunkName: "mermaid-runtime" */ '@diplodoc/mermaid-extension/runtime'
187+
);
188+
},
189+
})
190+
.use(FoldingHeading)
191+
.use(YfmHtmlBlock, {
192+
useConfig: useYfmHtmlBlockStyles,
193+
sanitize: getSanitizeYfmHtmlBlock({options: defaultOptions}),
194+
head: `
195+
<base target="_blank" />
196+
<style>
197+
html, body {
198+
margin: 0;
199+
padding: 0;
200+
}
201+
</style
202+
`,
203+
});
204+
if (extraExtensions) builder.use(extraExtensions);
205+
},
206+
},
178207
allowHTML,
179208
linkify,
180209
linkifyTlds,
@@ -184,13 +213,9 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
184213
initialSplitModeEnabled: initialSplitModeEnabled,
185214
initialToolbarVisible: true,
186215
splitMode: splitModeOrientation,
187-
escapeConfig: escapeConfig,
188216
needToSetDimensionsForUploadedImages,
189217
renderPreview: renderPreviewDefined ? renderPreview : undefined,
190218
fileUploadHandler,
191-
wysiwygConfig: {
192-
placeholderOptions: placeholderOptions,
193-
},
194219
experimental: {
195220
...experimental,
196221
directiveSyntax,
@@ -209,42 +234,6 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
209234
extensions: markupConfigExtensions,
210235
parseInsertedUrlAsImage,
211236
},
212-
extraExtensions: (builder) => {
213-
builder
214-
.use(Math, {
215-
loadRuntimeScript: () => {
216-
import(
217-
/* webpackChunkName: "latex-runtime" */ '@diplodoc/latex-extension/runtime'
218-
);
219-
import(
220-
// @ts-expect-error // no types for styles
221-
/* webpackChunkName: "latex-styles" */ '@diplodoc/latex-extension/runtime/styles'
222-
);
223-
},
224-
})
225-
.use(Mermaid, {
226-
loadRuntimeScript: () => {
227-
import(
228-
/* webpackChunkName: "mermaid-runtime" */ '@diplodoc/mermaid-extension/runtime'
229-
);
230-
},
231-
})
232-
.use(FoldingHeading)
233-
.use(YfmHtmlBlock, {
234-
useConfig: useYfmHtmlBlockStyles,
235-
sanitize: getSanitizeYfmHtmlBlock({options: defaultOptions}),
236-
head: `
237-
<base target="_blank" />
238-
<style>
239-
html, body {
240-
margin: 0;
241-
padding: 0;
242-
}
243-
</style
244-
`,
245-
});
246-
if (extraExtensions) builder.use(extraExtensions);
247-
},
248237
},
249238
[
250239
allowHTML,
@@ -394,8 +383,9 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
394383
toaster={toaster}
395384
className={b('editor-view')}
396385
stickyToolbar={Boolean(stickyToolbar)}
397-
wysiwygToolbarConfig={wysiwygToolbarConfig ?? wToolbarConfig}
398-
markupToolbarConfig={markupToolbarConfig ?? mToolbarConfig}
386+
toolbarsPreset={toolbarsPreset}
387+
wysiwygToolbarConfig={wysiwygToolbarConfig}
388+
markupToolbarConfig={markupToolbarConfig}
399389
settingsVisible={settingsVisible}
400390
editor={mdEditor}
401391
enableSubmitInPreview={enableSubmitInPreview}

demo/stories/presets/Preset.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
logger,
1212
useMarkdownEditor,
1313
} from '../../../src';
14+
import {ToolbarsPreset} from '../../../src/modules/toolbars/types';
1415
import type {FileUploadHandler} from '../../../src/utils/upload';
1516
import {VERSION} from '../../../src/version';
1617
// ---
@@ -41,6 +42,7 @@ export type PresetDemoProps = {
4142
splitModeOrientation?: 'horizontal' | 'vertical' | false;
4243
stickyToolbar?: boolean;
4344
height?: CSSProperties['height'];
45+
toolbarsPreset?: ToolbarsPreset;
4446
};
4547

4648
logger.setLogger({
@@ -60,6 +62,7 @@ export const Preset = React.memo<PresetDemoProps>((props) => {
6062
splitModeOrientation,
6163
stickyToolbar,
6264
height,
65+
toolbarsPreset,
6366
} = props;
6467
const [editorMode, setEditorMode] = React.useState<MarkdownEditorMode>('wysiwyg');
6568
const [mdRaw, setMdRaw] = React.useState<MarkupString>('');
@@ -130,6 +133,7 @@ export const Preset = React.memo<PresetDemoProps>((props) => {
130133
<div className={b('editor')} style={{height: height ?? 'initial'}}>
131134
<MarkdownEditorView
132135
autofocus
136+
toolbarsPreset={toolbarsPreset}
133137
toaster={toaster}
134138
className={b('editor-view')}
135139
stickyToolbar={Boolean(stickyToolbar)}

demo/stories/presets/Presets.stories.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
import {StoryObj} from '@storybook/react';
22

3+
import {ActionName as Action} from '../../../src/bundle/config/action-names';
4+
import {ToolbarName as Toolbar} from '../../../src/modules/toolbars/constants';
5+
import {
6+
boldItemView,
7+
boldItemWysiwyg,
8+
colorifyItemMarkup,
9+
colorifyItemView,
10+
colorifyItemWysiwyg,
11+
italicItemMarkup,
12+
italicItemView,
13+
redoItemMarkup,
14+
redoItemView,
15+
redoItemWysiwyg,
16+
undoItemMarkup,
17+
undoItemView,
18+
undoItemWysiwyg,
19+
} from '../../../src/modules/toolbars/items';
20+
321
import {Preset as component} from './Preset';
422

523
export const Zero: StoryObj<typeof component> = {
@@ -22,6 +40,50 @@ export const Full: StoryObj<typeof component> = {
2240
args: {preset: 'full'},
2341
};
2442

43+
export const Custom: StoryObj<typeof component> = {
44+
args: {
45+
toolbarsPreset: {
46+
items: {
47+
[Action.undo]: {
48+
view: undoItemView,
49+
wysiwyg: undoItemWysiwyg,
50+
markup: undoItemMarkup,
51+
},
52+
[Action.redo]: {
53+
view: redoItemView,
54+
wysiwyg: redoItemWysiwyg,
55+
markup: redoItemMarkup,
56+
},
57+
[Action.bold]: {
58+
view: boldItemView,
59+
wysiwyg: boldItemWysiwyg,
60+
},
61+
[Action.italic]: {
62+
view: italicItemView,
63+
markup: italicItemMarkup,
64+
},
65+
[Action.colorify]: {
66+
view: colorifyItemView,
67+
wysiwyg: colorifyItemWysiwyg,
68+
markup: colorifyItemMarkup,
69+
},
70+
},
71+
orders: {
72+
[Toolbar.wysiwygMain]: [
73+
[Action.colorify],
74+
[Action.bold],
75+
[Action.undo, Action.redo],
76+
],
77+
[Toolbar.markupMain]: [
78+
[Action.colorify],
79+
[Action.italic],
80+
[Action.undo, Action.redo],
81+
],
82+
},
83+
},
84+
},
85+
};
86+
2587
export default {
2688
component,
2789
title: 'Extensions / Presets',

src/bundle/MarkdownEditorView.tsx

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {useEnsuredForwardedRef, useKey, useUpdate} from 'react-use';
77
import {ClassNameProps, cn} from '../classname';
88
import {i18n} from '../i18n/bundle';
99
import {logger} from '../logger';
10+
import type {ToolbarsPreset} from '../modules/toolbars/types';
1011
import {ToasterContext, useBooleanState, useSticky} from '../react-utils';
1112
import {isMac} from '../utils';
1213

@@ -15,19 +16,11 @@ import {HorizontalDrag} from './HorizontalDrag';
1516
import {MarkupEditorView} from './MarkupEditorView';
1617
import {SplitModeView} from './SplitModeView';
1718
import {WysiwygEditorView} from './WysiwygEditorView';
18-
import {
19-
MToolbarData,
20-
MToolbarItemData,
21-
WToolbarData,
22-
WToolbarItemData,
23-
mHiddenDataByPreset,
24-
mToolbarConfigByPreset,
25-
wHiddenDataByPreset,
26-
wToolbarConfigByPreset,
27-
} from './config';
19+
import {MToolbarData, MToolbarItemData, WToolbarData, WToolbarItemData} from './config';
2820
import {useMarkdownEditorContext} from './context';
2921
import {EditorSettings, EditorSettingsProps} from './settings';
3022
import {stickyCn} from './sticky';
23+
import {getToolbarsConfigs} from './toolbar/utils';
3124
import type {MarkdownEditorMode} from './types';
3225

3326
import '../styles/styles.scss';
@@ -39,9 +32,22 @@ const b = cnEditorComponent;
3932
export type MarkdownEditorViewProps = ClassNameProps & {
4033
editor?: Editor;
4134
autofocus?: boolean;
35+
toolbarsPreset?: ToolbarsPreset;
36+
/**
37+
* @deprecated use `toolbarsPreset` instead
38+
*/
4239
markupToolbarConfig?: MToolbarData;
40+
/**
41+
* @deprecated use `toolbarsPreset` instead
42+
*/
4343
wysiwygToolbarConfig?: WToolbarData;
44+
/**
45+
* @deprecated use `toolbarsPreset` instead
46+
*/
4447
markupHiddenActionsConfig?: MToolbarItemData[];
48+
/**
49+
* @deprecated use `toolbarsPreset` instead
50+
*/
4551
wysiwygHiddenActionsConfig?: WToolbarItemData[];
4652
/** @default true */
4753
settingsVisible?: boolean;
@@ -73,16 +79,44 @@ export const MarkdownEditorView = React.forwardRef<HTMLDivElement, MarkdownEdito
7379
autofocus,
7480
className,
7581
settingsVisible = true,
76-
markupToolbarConfig = mToolbarConfigByPreset[editor.preset],
77-
wysiwygToolbarConfig = wToolbarConfigByPreset[editor.preset],
78-
markupHiddenActionsConfig = mHiddenDataByPreset[editor.preset],
79-
wysiwygHiddenActionsConfig = wHiddenDataByPreset[editor.preset],
82+
toolbarsPreset,
8083
toaster,
8184
stickyToolbar,
85+
wysiwygToolbarConfig: initialWysiwygToolbarConfig,
86+
markupToolbarConfig: initialMarkupToolbarConfig,
87+
wysiwygHiddenActionsConfig: initialWysiwygHiddenActionsConfig,
88+
markupHiddenActionsConfig: initialMarkupHiddenActionsConfig,
8289
enableSubmitInPreview = true,
8390
hidePreviewAfterSubmit = false,
8491
} = props;
8592

93+
const {
94+
wysiwygToolbarConfig,
95+
markupToolbarConfig,
96+
wysiwygHiddenActionsConfig,
97+
markupHiddenActionsConfig,
98+
} = useMemo(
99+
() =>
100+
getToolbarsConfigs({
101+
toolbarsPreset,
102+
props: {
103+
wysiwygToolbarConfig: initialWysiwygToolbarConfig,
104+
markupToolbarConfig: initialMarkupToolbarConfig,
105+
wysiwygHiddenActionsConfig: initialWysiwygHiddenActionsConfig,
106+
markupHiddenActionsConfig: initialMarkupHiddenActionsConfig,
107+
},
108+
preset: editor.preset,
109+
}),
110+
[
111+
toolbarsPreset,
112+
initialWysiwygToolbarConfig,
113+
initialMarkupToolbarConfig,
114+
initialWysiwygHiddenActionsConfig,
115+
initialMarkupHiddenActionsConfig,
116+
editor.preset,
117+
],
118+
);
119+
86120
const rerender = useUpdate();
87121
React.useLayoutEffect(() => {
88122
editor.on('rerender', rerender);

0 commit comments

Comments
 (0)