Skip to content

Commit c24f2e0

Browse files
authored
chore(demo): added new story; rewritten some other stories (#536)
For `demo` only: 1. Added PlaygroundLayout component 2. Added `useMarkdownEditorValue()` hook 3. Rewritten `CustomCSSVariables`, `EditorInEditor`, `EscapeConfig`, `Ghost`, `GPT` stories 4. Added new story – `PreserveEmptyRows`
1 parent a3f8b50 commit c24f2e0

File tree

14 files changed

+400
-198
lines changed

14 files changed

+400
-198
lines changed

demo/components/Playground.tsx

Lines changed: 91 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {toaster} from '@gravity-ui/uikit/toaster-singleton-react-18';
66

77
import {
88
type DirectiveSyntaxValue,
9-
type EscapeConfig,
109
type FileUploadHandler,
1110
type MarkdownEditorMode,
1211
MarkdownEditorView,
@@ -30,21 +29,15 @@ import {YfmHtmlBlock} from '../../src/extensions/additional/YfmHtmlBlock';
3029
import {getSanitizeYfmHtmlBlock} from '../../src/extensions/additional/YfmHtmlBlock/utils';
3130
import type {CodeEditor} from '../../src/markup';
3231
import {ToolbarsPreset} from '../../src/modules/toolbars/types';
33-
import {VERSION} from '../../src/version';
3432
import {getPlugins} from '../defaults/md-plugins';
3533
import useYfmHtmlBlockStyles from '../hooks/useYfmHtmlBlockStyles';
36-
import {block} from '../utils/cn';
3734
import {randomDelay} from '../utils/delay';
3835
import {parseInsertedUrlAsImage} from '../utils/imageUrl';
3936
import {debouncedUpdateLocation as updateLocation} from '../utils/location';
4037

41-
import {WysiwygSelection} from './PMSelection';
42-
import {WysiwygDevTools} from './ProseMirrorDevTools';
38+
import {PlaygroundLayout, b} from './PlaygroundLayout';
4339
import {SplitModePreview} from './SplitModePreview';
4440

45-
import './Playground.scss';
46-
47-
const b = block('playground');
4841
const fileUploadHandler: FileUploadHandler = async (file) => {
4942
console.info('[Playground] Uploading file: ' + file.name);
5043
await randomDelay(1000, 3000);
@@ -76,7 +69,6 @@ export type PlaygroundProps = {
7669
renderPreviewDefined?: boolean;
7770
height?: CSSProperties['height'];
7871
markupConfigExtensions?: Extension[];
79-
escapeConfig?: EscapeConfig;
8072
wysiwygCommandMenuConfig?: wysiwygToolbarConfigs.WToolbarItemData[];
8173
markupToolbarConfig?: ToolbarGroupData<CodeEditor>[];
8274
toolbarsPreset?: ToolbarsPreset;
@@ -132,7 +124,6 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
132124
markupConfigExtensions,
133125
markupToolbarConfig,
134126
placeholderOptions,
135-
escapeConfig,
136127
enableSubmitInPreview,
137128
hidePreviewAfterSubmit,
138129
needToSetDimensionsForUploadedImages,
@@ -167,7 +158,6 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
167158
{
168159
preset: 'full',
169160
wysiwygConfig: {
170-
escapeConfig,
171161
placeholderOptions: placeholderOptions,
172162
extensions: (builder) => {
173163
builder
@@ -304,107 +294,96 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
304294
}, [mdEditor]);
305295

306296
return (
307-
<div className={b()}>
308-
<div className={b('header')}>
309-
Markdown Editor Playground
310-
<span className={b('version')}>{VERSION}</span>
311-
</div>
312-
<div className={b('actions')}>
313-
<DropdownMenu
314-
size="s"
315-
switcher={
316-
<Button size="s" view="flat">
317-
isEmpty: {String(mdEditor.isEmpty())}
318-
</Button>
319-
}
320-
>
321-
<DropdownMenu.Item
322-
text="Clear"
323-
action={() => {
324-
mdEditor.clear();
325-
mdEditor.focus();
326-
}}
327-
/>
328-
<DropdownMenu.Item
329-
text="Append"
330-
action={() => {
331-
mdEditor.append('> append');
332-
mdEditor.focus();
333-
}}
334-
/>
335-
<DropdownMenu.Item
336-
text="Prepend"
337-
action={() => {
338-
mdEditor.prepend('> prepend');
339-
mdEditor.focus();
340-
}}
341-
/>
342-
<DropdownMenu.Item
343-
text="Replace"
344-
action={() => {
345-
mdEditor.replace('> replace');
346-
mdEditor.focus();
347-
}}
348-
/>
349-
<DropdownMenu.Item
350-
text="Move cursor to start"
351-
action={() => {
352-
mdEditor.moveCursor('start');
353-
mdEditor.focus();
354-
}}
355-
/>
356-
<DropdownMenu.Item
357-
text="Move cursor to end"
358-
action={() => {
359-
mdEditor.moveCursor('end');
360-
mdEditor.focus();
361-
}}
362-
/>
363-
<DropdownMenu.Item
364-
text="Move to line"
365-
action={() => {
366-
mdEditor.moveCursor({line: 115});
367-
mdEditor.focus();
368-
}}
369-
/>
370-
</DropdownMenu>
371-
{mdEditor.currentMode === 'markup' && (
372-
<MoveToLine
373-
onClick={(line) => {
374-
if (typeof line !== 'number' || Number.isNaN(line)) return;
375-
mdEditor.moveCursor({line});
376-
mdEditor.focus();
377-
}}
378-
/>
379-
)}
380-
</div>
381-
<hr />
382-
<React.StrictMode>
383-
<div className={b('editor')} style={{height: height ?? 'initial'}}>
384-
<MarkdownEditorView
385-
autofocus
386-
toaster={toaster}
387-
className={b('editor-view')}
388-
stickyToolbar={Boolean(stickyToolbar)}
389-
toolbarsPreset={toolbarsPreset}
390-
wysiwygToolbarConfig={wysiwygToolbarConfig}
391-
markupToolbarConfig={markupToolbarConfig}
392-
settingsVisible={settingsVisible}
393-
editor={mdEditor}
394-
enableSubmitInPreview={enableSubmitInPreview}
395-
hidePreviewAfterSubmit={hidePreviewAfterSubmit}
396-
/>
397-
<WysiwygDevTools editor={mdEditor} />
398-
<WysiwygSelection editor={mdEditor} className={b('pm-selection')} />
399-
</div>
400-
</React.StrictMode>
401-
402-
<hr />
403-
404-
<div className={b('preview')}>
405-
{editorMode === 'wysiwyg' && <pre className={b('markup')}>{mdRaw}</pre>}
406-
</div>
407-
</div>
297+
<PlaygroundLayout
298+
editor={mdEditor}
299+
viewHeight={height}
300+
view={({className}) => (
301+
<MarkdownEditorView
302+
autofocus
303+
toaster={toaster}
304+
className={className}
305+
stickyToolbar={Boolean(stickyToolbar)}
306+
toolbarsPreset={toolbarsPreset}
307+
wysiwygToolbarConfig={wysiwygToolbarConfig}
308+
markupToolbarConfig={markupToolbarConfig}
309+
settingsVisible={settingsVisible}
310+
editor={mdEditor}
311+
enableSubmitInPreview={enableSubmitInPreview}
312+
hidePreviewAfterSubmit={hidePreviewAfterSubmit}
313+
/>
314+
)}
315+
actions={() => (
316+
<>
317+
<DropdownMenu
318+
size="s"
319+
switcher={
320+
<Button size="s" view="flat">
321+
isEmpty: {String(mdEditor.isEmpty())}
322+
</Button>
323+
}
324+
>
325+
<DropdownMenu.Item
326+
text="Clear"
327+
action={() => {
328+
mdEditor.clear();
329+
mdEditor.focus();
330+
}}
331+
/>
332+
<DropdownMenu.Item
333+
text="Append"
334+
action={() => {
335+
mdEditor.append('> append');
336+
mdEditor.focus();
337+
}}
338+
/>
339+
<DropdownMenu.Item
340+
text="Prepend"
341+
action={() => {
342+
mdEditor.prepend('> prepend');
343+
mdEditor.focus();
344+
}}
345+
/>
346+
<DropdownMenu.Item
347+
text="Replace"
348+
action={() => {
349+
mdEditor.replace('> replace');
350+
mdEditor.focus();
351+
}}
352+
/>
353+
<DropdownMenu.Item
354+
text="Move cursor to start"
355+
action={() => {
356+
mdEditor.moveCursor('start');
357+
mdEditor.focus();
358+
}}
359+
/>
360+
<DropdownMenu.Item
361+
text="Move cursor to end"
362+
action={() => {
363+
mdEditor.moveCursor('end');
364+
mdEditor.focus();
365+
}}
366+
/>
367+
<DropdownMenu.Item
368+
text="Move to line"
369+
action={() => {
370+
mdEditor.moveCursor({line: 115});
371+
mdEditor.focus();
372+
}}
373+
/>
374+
</DropdownMenu>
375+
{mdEditor.currentMode === 'markup' && (
376+
<MoveToLine
377+
onClick={(line) => {
378+
if (typeof line !== 'number' || Number.isNaN(line)) return;
379+
mdEditor.moveCursor({line});
380+
mdEditor.focus();
381+
}}
382+
/>
383+
)}
384+
</>
385+
)}
386+
/>
408387
);
409388
});
410389

demo/components/PlaygroundLayout.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React, {useEffect} from 'react';
2+
3+
import {useUpdate} from 'react-use';
4+
5+
import type {MarkdownEditorInstance} from '../../src';
6+
import {VERSION} from '../../src/version';
7+
import {useMarkdownEditorValue} from '../hooks/useMarkdownEditorValue';
8+
import {block} from '../utils/cn';
9+
10+
import {WysiwygSelection} from './PMSelection';
11+
import {WysiwygDevTools} from './ProseMirrorDevTools';
12+
13+
import './Playground.scss';
14+
15+
export const b = block('playground');
16+
17+
export type RenderFn = (props: {className?: string}) => React.ReactNode;
18+
19+
export type PlaygroundLayoutProps = {
20+
title?: string;
21+
editor: MarkdownEditorInstance;
22+
view: RenderFn;
23+
viewHeight?: React.CSSProperties['height'];
24+
actions?: RenderFn;
25+
style?: React.CSSProperties;
26+
};
27+
28+
export const PlaygroundLayout: React.FC<PlaygroundLayoutProps> = function PlaygroundLayout(props) {
29+
const {editor} = props;
30+
31+
const forceRender = useUpdate();
32+
const mdMarkup = useMarkdownEditorValue(editor);
33+
34+
useEffect(() => {
35+
editor.on('change-editor-mode', forceRender);
36+
return () => {
37+
editor.off('change-editor-mode', forceRender);
38+
};
39+
}, [editor, forceRender]);
40+
41+
return (
42+
<div className={b()} style={props.style}>
43+
<div className={b('header')}>
44+
{props.title ?? 'Markdown Editor Playground'}
45+
<span className={b('version')}>{VERSION}</span>
46+
</div>
47+
48+
<div className={b('actions')}>{props.actions?.({})}</div>
49+
50+
<hr />
51+
52+
<React.StrictMode>
53+
<div className={b('editor')} style={{height: props.viewHeight ?? 'initial'}}>
54+
{props.view({className: b('editor-view')})}
55+
56+
<WysiwygDevTools editor={editor} />
57+
<WysiwygSelection editor={editor} className={b('pm-selection')} />
58+
</div>
59+
</React.StrictMode>
60+
61+
<hr />
62+
63+
<div className={b('preview')}>
64+
{editor.currentMode === 'wysiwyg' && <pre className={b('markup')}>{mdMarkup}</pre>}
65+
</div>
66+
</div>
67+
);
68+
};

demo/components/PlaygroundMini.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export type PlaygroundMiniProps = Pick<
2222
| 'initial'
2323
| 'onChangeEditorType'
2424
| 'onChangeSplitModeEnabled'
25-
| 'escapeConfig'
2625
| 'directiveSyntax'
2726
> & {withDefaultInitialContent?: boolean};
2827

demo/defaults/excluded-controls.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@ export const excludedControls = [
33
'withDefaultInitialContent',
44
'onChangeEditorType',
55
'onChangeSplitModeEnabled',
6-
'escapeConfig',
76
];

demo/hooks/useMarkdownEditorValue.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {useEffect, useState} from 'react';
2+
3+
import {type MarkdownEditorInstance, type MarkupString, useDebounce} from '../../src';
4+
5+
export function useMarkdownEditorValue(editor: MarkdownEditorInstance, delay = 500): MarkupString {
6+
const [value, setValue] = useState(() => editor.getValue());
7+
8+
const fn = useDebounce(() => setValue(editor.getValue()), delay);
9+
10+
useEffect(() => {
11+
editor.on('change', fn);
12+
return () => {
13+
editor.off('change', fn);
14+
};
15+
}, [editor, fn]);
16+
17+
return value;
18+
}

demo/stories/css-variables/CSSVariables.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
11
import React from 'react';
22

3-
import {PlaygroundMini} from '../../components/PlaygroundMini';
3+
import {toaster} from '@gravity-ui/uikit/toaster-singleton-react-18';
4+
5+
import {MarkdownEditorView, useMarkdownEditor} from '../../../src';
6+
import {PlaygroundLayout} from '../../components/PlaygroundLayout';
7+
import {markup} from '../../defaults/content';
48

59
export const CustomCSSVariablesDemo = React.memo((styles) => {
10+
const editor = useMarkdownEditor({initial: {markup}});
11+
612
return (
7-
<div style={styles}>
8-
<PlaygroundMini
9-
initialEditor="wysiwyg"
10-
settingsVisible
11-
withDefaultInitialContent
12-
stickyToolbar
13-
/>
14-
</div>
13+
<PlaygroundLayout
14+
editor={editor}
15+
style={styles}
16+
view={({className}) => (
17+
<MarkdownEditorView
18+
autofocus
19+
stickyToolbar
20+
settingsVisible
21+
editor={editor}
22+
toaster={toaster}
23+
className={className}
24+
/>
25+
)}
26+
/>
1527
);
1628
});
1729

0 commit comments

Comments
 (0)