@soul-refiner/design-system 0.1.25 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/package.json +7 -6
  2. package/src/components/actions/dropdown/dropdown.tsx +14 -11
  3. package/src/components/atoms/typography/typography.tsx +1 -1
  4. package/src/components/editor/constants.ts +181 -0
  5. package/src/components/editor/editor.tsx +95 -0
  6. package/src/components/editor/index.ts +2 -0
  7. package/src/components/editor/navbar/alignment_dropdown.tsx +142 -0
  8. package/src/components/editor/navbar/button_modal.tsx +146 -0
  9. package/src/components/editor/navbar/index.ts +2 -0
  10. package/src/components/editor/navbar/navbar.tsx +350 -0
  11. package/src/components/editor/navbar/style_dropdown.tsx +107 -0
  12. package/src/components/editor/plate.ts +37 -0
  13. package/src/components/editor/plugins/blockquote.tsx +25 -0
  14. package/src/components/editor/plugins/buttons.tsx +140 -0
  15. package/src/components/editor/plugins/exitBreak.ts +39 -0
  16. package/src/components/editor/plugins/image.tsx +80 -0
  17. package/src/components/editor/plugins/index.ts +165 -0
  18. package/src/components/editor/plugins/link.tsx +144 -0
  19. package/src/components/editor/plugins/list.tsx +34 -0
  20. package/src/components/editor/plugins/resetBlockType.ts +29 -0
  21. package/src/components/editor/plugins/selectOnBackspace.ts +7 -0
  22. package/src/components/editor/plugins/softBreak.ts +19 -0
  23. package/src/components/editor/plugins/text.tsx +142 -0
  24. package/src/components/index.ts +1 -0
  25. package/src/components/input/radio/radio.tsx +2 -2
  26. package/src/components/surfaces/modal/modal.tsx +6 -2
  27. package/src/icons/text/align_justified.tsx +1 -1
  28. package/tsconfig.json +1 -1
@@ -0,0 +1,350 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import {
4
+ getPluginType,
5
+ insertImage,
6
+ insertNodes,
7
+ toggleNodeType,
8
+ useLinkToolbarButton,
9
+ useLinkToolbarButtonState,
10
+ useListToolbarButton,
11
+ useListToolbarButtonState,
12
+ useMarkToolbarButton,
13
+ useMarkToolbarButtonState,
14
+ usePlateEditorRef,
15
+ usePlateEditorState,
16
+ } from '@udecode/plate';
17
+
18
+ import { Tooltip } from 'components';
19
+ import {
20
+ Bold,
21
+ FileCode,
22
+ Headphones,
23
+ Image,
24
+ Italic,
25
+ Link,
26
+ ListOrdered,
27
+ ListUnordered,
28
+ Quote,
29
+ Redo,
30
+ Strikethrough,
31
+ Underline,
32
+ Undo,
33
+ Video,
34
+ } from 'icons';
35
+ import {
36
+ ELEMENT_BLOCKQUOTE,
37
+ ELEMENT_BUTTON,
38
+ ELEMENT_OL,
39
+ ELEMENT_UL,
40
+ MARK_BOLD,
41
+ MARK_CODE,
42
+ MARK_ITALIC,
43
+ MARK_STRIKETHROUGH,
44
+ MARK_UNDERLINE,
45
+ } from '../constants';
46
+ import { AlignmentDropdown } from './alignment_dropdown';
47
+ import { ButtonModal } from './button_modal';
48
+ import { StyleDropdown } from './style_dropdown';
49
+
50
+ export interface EditorBarProps {
51
+ handleUploadImage: (dataURL: File) => Promise<string>;
52
+ }
53
+
54
+ export const EditorBar = ({ handleUploadImage }: EditorBarProps) => {
55
+ const editorRef = usePlateEditorRef('main');
56
+ const stateEditor = usePlateEditorState();
57
+ const audioRef = React.useRef<HTMLInputElement>(null);
58
+ const imageRef = React.useRef<HTMLInputElement>(null);
59
+ const videoRef = React.useRef<HTMLInputElement>(null);
60
+
61
+ const boldState = useMarkToolbarButtonState({ nodeType: MARK_BOLD });
62
+ const {
63
+ props: { pressed: currBold, onClick: handleBold },
64
+ } = useMarkToolbarButton(boldState);
65
+
66
+ const italicState = useMarkToolbarButtonState({ nodeType: MARK_ITALIC });
67
+ const {
68
+ props: { pressed: currItalic, onClick: handleItalic },
69
+ } = useMarkToolbarButton(italicState);
70
+
71
+ const strikethroughState = useMarkToolbarButtonState({
72
+ nodeType: MARK_STRIKETHROUGH,
73
+ });
74
+ const {
75
+ props: { pressed: currStrikethrough, onClick: handleStrikethrough },
76
+ } = useMarkToolbarButton(strikethroughState);
77
+
78
+ const underlineState = useMarkToolbarButtonState({
79
+ nodeType: MARK_UNDERLINE,
80
+ });
81
+ const {
82
+ props: { pressed: currUnderline, onClick: handleUnderline },
83
+ } = useMarkToolbarButton(underlineState);
84
+
85
+ const inlineCodeState = useMarkToolbarButtonState({ nodeType: MARK_CODE });
86
+ const {
87
+ props: { pressed: currInlineCode, onClick: handleInlineCode },
88
+ } = useMarkToolbarButton(inlineCodeState);
89
+
90
+ const linkState = useLinkToolbarButtonState();
91
+ const {
92
+ props: { pressed: currLink, onClick: handleLink },
93
+ } = useLinkToolbarButton(linkState);
94
+
95
+ const orderedListState = useListToolbarButtonState({ nodeType: ELEMENT_OL });
96
+ const {
97
+ props: { pressed: currOrderedList, onClick: handleOrderedList },
98
+ } = useListToolbarButton(orderedListState);
99
+
100
+ const unorderedListState = useListToolbarButtonState({
101
+ nodeType: ELEMENT_UL,
102
+ });
103
+ const {
104
+ props: { pressed: currUnorderedList, onClick: handleUnorderedList },
105
+ } = useListToolbarButton(unorderedListState);
106
+
107
+ const handleAudioChange = async (event: React.FormEvent) => {
108
+ const file = (event.target as HTMLInputElement)?.files?.[0];
109
+ if (!file) return;
110
+ /**
111
+ * TODO: Insert audio component
112
+ */
113
+ };
114
+
115
+ const handleBlockquote = () => {
116
+ toggleNodeType(stateEditor, { activeType: ELEMENT_BLOCKQUOTE });
117
+ };
118
+
119
+ const handleButton = (type: string) => {
120
+ insertNodes(editorRef, [
121
+ {
122
+ type: getPluginType(editorRef, ELEMENT_BUTTON),
123
+ buttonType: type,
124
+ children: [
125
+ {
126
+ text: 'Subscribe',
127
+ },
128
+ ],
129
+ },
130
+ ]);
131
+ };
132
+
133
+ const handleImageChange = async (event: React.FormEvent) => {
134
+ const file = (event.target as HTMLInputElement)?.files?.[0];
135
+ if (!file) return;
136
+ const imageurl = await handleUploadImage(file);
137
+ if (imageurl) {
138
+ insertImage(editorRef, imageurl);
139
+ }
140
+ };
141
+
142
+ const handleRedo = () => editorRef.redo();
143
+
144
+ const handleUndo = () => editorRef.undo();
145
+
146
+ const handleVideoChange = async (event: React.FormEvent) => {
147
+ const file = (event.target as HTMLInputElement)?.files?.[0];
148
+ if (!file) return;
149
+ /**
150
+ * TODO: Insert video component
151
+ */
152
+ };
153
+
154
+ return (
155
+ <Wrapper>
156
+ <Menu aria-label="editor-navigation">
157
+ <MenuSection>
158
+ <Tooltip content="Undo" place="top">
159
+ <MenuButton $active={false} onClick={handleUndo}>
160
+ <Undo size={24} color="grey-5" />
161
+ </MenuButton>
162
+ </Tooltip>
163
+ <Tooltip content="Redo" place="top">
164
+ <MenuButton $active={false} onClick={handleRedo}>
165
+ <Redo size={24} color="grey-5" />
166
+ </MenuButton>
167
+ </Tooltip>
168
+ </MenuSection>
169
+ <Divider />
170
+ <MenuSection>
171
+ <StyleDropdown editorRef={editorRef} />
172
+ </MenuSection>
173
+ <Divider />
174
+ <MenuSection>
175
+ <Tooltip content="Bold" place="top">
176
+ <MenuButton $active={currBold} onClick={handleBold}>
177
+ <Bold size={24} color="grey-5" />
178
+ </MenuButton>
179
+ </Tooltip>
180
+ <Tooltip content="Italic" place="top">
181
+ <MenuButton $active={currItalic} onClick={handleItalic}>
182
+ <Italic size={24} color="grey-5" />
183
+ </MenuButton>
184
+ </Tooltip>
185
+ <Tooltip content="Underline" place="top">
186
+ <MenuButton $active={currUnderline} onClick={handleUnderline}>
187
+ <Underline size={24} color="grey-5" />
188
+ </MenuButton>
189
+ </Tooltip>
190
+ <Tooltip content="Strikethrough" place="top">
191
+ <MenuButton
192
+ $active={currStrikethrough}
193
+ onClick={handleStrikethrough}
194
+ >
195
+ <Strikethrough size={24} color="grey-5" />
196
+ </MenuButton>
197
+ </Tooltip>
198
+ <Tooltip content="Inline code" place="top">
199
+ <MenuButton $active={currInlineCode} onClick={handleInlineCode}>
200
+ <FileCode size={24} color="grey-5" />
201
+ </MenuButton>
202
+ </Tooltip>
203
+ </MenuSection>
204
+ <Divider />
205
+ <MenuSection>
206
+ <MenuButton $active={false} as="div">
207
+ <AlignmentDropdown editorRef={editorRef} />
208
+ </MenuButton>
209
+ </MenuSection>
210
+ <Divider />
211
+ <MenuSection>
212
+ <Tooltip content="Link" place="top">
213
+ <MenuButton $active={currLink} onClick={handleLink}>
214
+ <Link size={24} color="grey-5" />
215
+ </MenuButton>
216
+ </Tooltip>
217
+ <Tooltip content="Image" place="top">
218
+ <MenuButton $active={false} as="label" onChange={handleImageChange}>
219
+ <div onClick={() => imageRef.current?.click()}>
220
+ <input
221
+ accept="image/*"
222
+ ref={imageRef}
223
+ style={{ display: 'none' }}
224
+ type="file"
225
+ />
226
+ <Image size={24} color="grey-5" />
227
+ </div>
228
+ </MenuButton>
229
+ </Tooltip>
230
+ <Tooltip content="Video" place="top">
231
+ <MenuButton $active={false} as="label" onChange={handleVideoChange}>
232
+ <div onClick={() => videoRef.current?.click()}>
233
+ <input
234
+ accept="video/*"
235
+ ref={videoRef}
236
+ style={{ display: 'none' }}
237
+ type="file"
238
+ />
239
+ <Video size={24} color="grey-5" />
240
+ </div>
241
+ </MenuButton>
242
+ </Tooltip>
243
+ <Tooltip content="Audio" place="top">
244
+ <MenuButton $active={false} as="label" onChange={handleAudioChange}>
245
+ <div onClick={() => audioRef.current?.click()}>
246
+ <input
247
+ accept="audio/*"
248
+ ref={audioRef}
249
+ style={{ display: 'none' }}
250
+ type="file"
251
+ />
252
+ <Headphones size={24} color="grey-5" />
253
+ </div>
254
+ </MenuButton>
255
+ </Tooltip>
256
+ <Tooltip content="Blockquote" place="top">
257
+ <MenuButton $active={false} onClick={handleBlockquote}>
258
+ <Quote size={24} color="grey-5" />
259
+ </MenuButton>
260
+ </Tooltip>
261
+ <Tooltip content="Add a button" place="top">
262
+ <ButtonModal onButtonInsert={handleButton} />
263
+ </Tooltip>
264
+ </MenuSection>
265
+ <Divider />
266
+ <MenuSection>
267
+ <Tooltip content="Unordered List" place="top">
268
+ <MenuButton
269
+ $active={currUnorderedList}
270
+ onClick={handleUnorderedList}
271
+ >
272
+ <ListUnordered size={24} color="grey-5" />
273
+ </MenuButton>
274
+ </Tooltip>
275
+ <Tooltip content="Ordered List" place="top">
276
+ <MenuButton $active={currOrderedList} onClick={handleOrderedList}>
277
+ <ListOrdered size={24} color="grey-5" />
278
+ </MenuButton>
279
+ </Tooltip>
280
+ </MenuSection>
281
+ </Menu>
282
+ </Wrapper>
283
+ );
284
+ };
285
+
286
+ const Wrapper = styled.div`
287
+ position: sticky;
288
+ z-index: 2;
289
+ left: 0;
290
+ right: 0;
291
+ top: 0;
292
+ `;
293
+
294
+ const Menu = styled.nav`
295
+ display: flex;
296
+ gap: 12px;
297
+ justify-content: center;
298
+ max-width: 1200px;
299
+ width: 100%;
300
+ margin: 0 auto;
301
+ padding: 14px 24px;
302
+ background-color: var(--color-white);
303
+ `;
304
+
305
+ const MenuSection = styled.div`
306
+ display: flex;
307
+ gap: 16px;
308
+ align-items: center;
309
+ width: fit-content;
310
+ flex-shrink: 0;
311
+ `;
312
+
313
+ export const MenuButton = styled.button<{
314
+ $active?: boolean;
315
+ }>`
316
+ padding: 2px;
317
+ border: none;
318
+ outline: none;
319
+ cursor: pointer;
320
+ box-sizing: border-box;
321
+ height: 28px;
322
+ width: 28px;
323
+ border-radius: 2px;
324
+
325
+ &:hover:not(:disabled) {
326
+ padding: 1px;
327
+ border: 1px solid var(--color-grey-8);
328
+ }
329
+
330
+ &:active:not(:disabled) {
331
+ background-color: var(--color-grey-9);
332
+ }
333
+
334
+ &:disabled {
335
+ background-color: var(--color-white) !important;
336
+ cursor: initial;
337
+ svg {
338
+ fill: var(--color-grey-7) !important;
339
+ }
340
+ }
341
+
342
+ background-color: ${({ $active }) =>
343
+ $active ? 'var(--color-grey-9)' : 'transparent'};
344
+ `;
345
+
346
+ const Divider = styled.div`
347
+ width: 1px;
348
+ height: 28px;
349
+ background-color: var(--color-grey-9);
350
+ `;
@@ -0,0 +1,107 @@
1
+ import React from 'react';
2
+ import { Button, Dropdown } from 'components';
3
+ import {
4
+ findNode,
5
+ isBlock,
6
+ isCollapsed,
7
+ PlateEditor,
8
+ toggleNodeType,
9
+ TElement,
10
+ } from '@udecode/plate';
11
+ import {
12
+ ELEMENT_H1,
13
+ ELEMENT_H2,
14
+ ELEMENT_H3,
15
+ ELEMENT_H4,
16
+ ELEMENT_H5,
17
+ ELEMENT_H6,
18
+ ELEMENT_PARAGRAPH,
19
+ } from '../constants';
20
+
21
+ const items = [
22
+ {
23
+ value: ELEMENT_PARAGRAPH,
24
+ label: 'Paragraph',
25
+ description: 'Paragraph',
26
+ },
27
+ {
28
+ value: ELEMENT_H1,
29
+ label: 'Heading 1',
30
+ description: 'Heading 1',
31
+ },
32
+ {
33
+ value: ELEMENT_H2,
34
+ label: 'Heading 2',
35
+ description: 'Heading 2',
36
+ },
37
+ {
38
+ value: ELEMENT_H3,
39
+ label: 'Heading 3',
40
+ description: 'Heading 3',
41
+ },
42
+ {
43
+ value: ELEMENT_H4,
44
+ label: 'Heading 4',
45
+ description: 'Heading 4',
46
+ },
47
+ {
48
+ value: ELEMENT_H5,
49
+ label: 'Heading 5',
50
+ description: 'Heading 5',
51
+ },
52
+ {
53
+ value: ELEMENT_H6,
54
+ label: 'Heading 6',
55
+ description: 'Heading 6',
56
+ },
57
+ ];
58
+
59
+ // eslint-disable-next-line
60
+ const defaultItem = items.find((item) => item.value === ELEMENT_PARAGRAPH)!;
61
+
62
+ export interface StyleDropdown {
63
+ editorRef: PlateEditor;
64
+ }
65
+
66
+ export const StyleDropdown = ({ editorRef }: StyleDropdown) => {
67
+ let value: string = ELEMENT_PARAGRAPH;
68
+
69
+ const handleChange = (event: React.FormEvent) => {
70
+ const value = (event.target as any).getAttribute('data-value');
71
+ if (!value) return;
72
+ toggleNodeType(editorRef, { activeType: value });
73
+ };
74
+
75
+ if (isCollapsed(editorRef?.selection)) {
76
+ const entry = findNode<TElement>(editorRef, {
77
+ match: (n) => isBlock(editorRef, n),
78
+ });
79
+ if (entry) {
80
+ value =
81
+ items.find((item) => item.value === entry[0].type)?.value ??
82
+ ELEMENT_PARAGRAPH;
83
+ }
84
+ }
85
+
86
+ const selectedItem =
87
+ items.find((item) => item.value === value) ?? defaultItem;
88
+
89
+ const { label: selectedItemLabel } = selectedItem;
90
+
91
+ return (
92
+ <Dropdown
93
+ maintainState={false}
94
+ onChange={handleChange}
95
+ options={items}
96
+ trigger={
97
+ <Button
98
+ type="special"
99
+ size="small"
100
+ sx="padding: 1px 4px; & > * { color: var(--color-grey-5); }"
101
+ >
102
+ {selectedItemLabel}
103
+ </Button>
104
+ }
105
+ />
106
+ );
107
+ };
@@ -0,0 +1,37 @@
1
+ import { withProps } from '@udecode/plate';
2
+ import { BlockquoteElement } from './plugins/blockquote';
3
+ import { Button } from './plugins/buttons';
4
+ import { ImageElement } from './plugins/image';
5
+ import { LinkElement } from './plugins/link';
6
+ import { ListElement } from './plugins/list';
7
+ import {
8
+ ParagraphElement,
9
+ HOneElement,
10
+ HTwoElement,
11
+ HThreeElement,
12
+ HFourElement,
13
+ HFiveElement,
14
+ HSixElement,
15
+ } from './plugins/text';
16
+
17
+ import * as Elements from './constants';
18
+
19
+ export const plateUI = {
20
+ [Elements.ELEMENT_PARAGRAPH]: ParagraphElement,
21
+ [Elements.ELEMENT_H1]: HOneElement,
22
+ [Elements.ELEMENT_H2]: HTwoElement,
23
+ [Elements.ELEMENT_H3]: HThreeElement,
24
+ [Elements.ELEMENT_H4]: HFourElement,
25
+ [Elements.ELEMENT_H5]: HFiveElement,
26
+ [Elements.ELEMENT_H6]: HSixElement,
27
+ [Elements.ELEMENT_BLOCKQUOTE]: BlockquoteElement,
28
+ [Elements.ELEMENT_LINK]: LinkElement,
29
+ [Elements.ELEMENT_OL]: withProps(ListElement, {
30
+ variant: Elements.ELEMENT_OL,
31
+ }),
32
+ [Elements.ELEMENT_UL]: withProps(ListElement, {
33
+ variant: Elements.ELEMENT_UL,
34
+ }),
35
+ [Elements.ELEMENT_IMAGE]: ImageElement,
36
+ [Elements.ELEMENT_BUTTON]: Button,
37
+ };
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import {
3
+ PlateElement,
4
+ PlateElementProps,
5
+ PlateRenderElementProps,
6
+ } from '@udecode/plate';
7
+ import { P } from 'components';
8
+
9
+ export const BlockquoteElement = React.forwardRef<
10
+ React.ElementRef<typeof PlateElement>,
11
+ PlateElementProps
12
+ >(({ children, ...props }: PlateRenderElementProps, ref) => {
13
+ return (
14
+ <PlateElement asChild ref={ref} {...props}>
15
+ <>
16
+ <P
17
+ as="blockquote"
18
+ sx="padding-left: 16px; border-left: 1px solid var(--color-grey-8);"
19
+ >
20
+ {children}
21
+ </P>
22
+ </>
23
+ </PlateElement>
24
+ );
25
+ });
@@ -0,0 +1,140 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import {
4
+ createPluginFactory,
5
+ getBlockAbove,
6
+ getPluginType,
7
+ isEditorReadOnly,
8
+ setNodes,
9
+ someNode,
10
+ PlateElement,
11
+ PlateElementProps,
12
+ PlateRenderElementProps,
13
+ } from '@udecode/plate';
14
+ import { Button as UIButton } from 'components';
15
+ import { COLORS, ELEMENT_BUTTON, ELEMENT_PARAGRAPH } from '../constants';
16
+
17
+ export const Button = React.forwardRef<
18
+ React.ElementRef<typeof PlateElement>,
19
+ PlateElementProps
20
+ >(({ children, editor, element, ...props }: PlateRenderElementProps, ref) => {
21
+ const { buttonType, color } = element;
22
+
23
+ const pressed =
24
+ !!editor?.selection &&
25
+ someNode(editor, {
26
+ match: { type: getPluginType(editor, ELEMENT_BUTTON) },
27
+ });
28
+
29
+ const currBlock = getBlockAbove(editor, {
30
+ at: editor.selection?.focus,
31
+ match: {
32
+ type: getPluginType(editor, ELEMENT_BUTTON),
33
+ buttonType: buttonType,
34
+ },
35
+ });
36
+
37
+ const editable = !isEditorReadOnly(editor);
38
+
39
+ const handleColorChange = (color: string) => {
40
+ if (currBlock) {
41
+ setNodes(
42
+ editor,
43
+ {
44
+ color,
45
+ },
46
+ {
47
+ at: currBlock ? currBlock[1] : undefined,
48
+ }
49
+ );
50
+ }
51
+ };
52
+
53
+ const ColorMenu = () =>
54
+ !pressed || !editable ? null : (
55
+ <ColorMenuComponent contentEditable={false}>
56
+ {COLORS.map((color) => (
57
+ <ColorItem
58
+ $color={color}
59
+ contentEditable={false}
60
+ key={color}
61
+ onClick={(event: React.MouseEvent) => {
62
+ event.stopPropagation();
63
+ handleColorChange(color);
64
+ }}
65
+ />
66
+ ))}
67
+ </ColorMenuComponent>
68
+ );
69
+
70
+ return (
71
+ <PlateElement
72
+ asChild
73
+ editor={editor}
74
+ element={element}
75
+ ref={ref}
76
+ {...props}
77
+ >
78
+ <>
79
+ <ColorMenu />
80
+ <UIButton
81
+ size="small"
82
+ sx={`
83
+ margin-left: auto !important;
84
+ margin-right: auto !important;
85
+ background-color: ${
86
+ color ? color : 'var(--color-blue-5)'
87
+ } !important;
88
+ border-color: ${color ? color : 'var(--color-blue-5)'} !important;
89
+ &:hover {
90
+ background-color: ${
91
+ color ? color : 'var(--color-blue-5)'
92
+ } !important;
93
+ filter: brightness(85%);
94
+ }
95
+ `}
96
+ >
97
+ {children}
98
+ </UIButton>
99
+ </>
100
+ </PlateElement>
101
+ );
102
+ });
103
+
104
+ const ColorMenuComponent = styled.div`
105
+ display: flex;
106
+ justify-content: center;
107
+ align-items: center;
108
+ gap: 16px;
109
+ z-index: 5;
110
+ position: absolute;
111
+ transform: translateY(24px);
112
+ left: calc(50% - 148px);
113
+ border-radius: 16px;
114
+ padding: 8px 16px;
115
+ box-shadow: var(--elevation-1);
116
+ background-color: var(--color-white);
117
+ `;
118
+
119
+ const ColorItem = styled.div<{ $color: string }>`
120
+ width: 24px;
121
+ height: 24px;
122
+ border-radius: 50%;
123
+ background-color: ${({ $color }) => $color};
124
+ cursor: pointer;
125
+ `;
126
+
127
+ export const createButtonsPlugin = createPluginFactory({
128
+ key: ELEMENT_BUTTON,
129
+ type: ELEMENT_BUTTON,
130
+ isElement: true,
131
+ component: Button,
132
+ then: () => ({
133
+ inject: {
134
+ props: {
135
+ classNames: undefined,
136
+ validTypes: [ELEMENT_PARAGRAPH],
137
+ },
138
+ },
139
+ }),
140
+ });
@@ -0,0 +1,39 @@
1
+ import { KEYS_HEADING } from '@udecode/plate';
2
+ import {
3
+ ELEMENT_BLOCKQUOTE,
4
+ ELEMENT_BUTTON,
5
+ ELEMENT_MEDIA,
6
+ ELEMENT_MEDIA_EMBED,
7
+ ELEMENT_OL,
8
+ ELEMENT_UL,
9
+ } from '../constants';
10
+
11
+ export const exitBreakPlugin = {
12
+ options: {
13
+ rules: [
14
+ {
15
+ hotkey: 'mod+enter',
16
+ },
17
+ {
18
+ hotkey: 'mod+shift+enter',
19
+ before: true,
20
+ },
21
+ {
22
+ hotkey: 'enter',
23
+ query: {
24
+ start: true,
25
+ end: true,
26
+ allow: [
27
+ ELEMENT_BLOCKQUOTE,
28
+ ELEMENT_BUTTON,
29
+ ELEMENT_MEDIA,
30
+ ELEMENT_MEDIA_EMBED,
31
+ ELEMENT_OL,
32
+ ELEMENT_UL,
33
+ ...KEYS_HEADING,
34
+ ],
35
+ },
36
+ },
37
+ ],
38
+ },
39
+ };