@soul-refiner/design-system 0.1.26 → 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.
- package/package.json +7 -6
- package/src/components/atoms/typography/typography.tsx +1 -1
- package/src/components/editor/constants.ts +31 -138
- package/src/components/editor/editor.tsx +41 -2
- package/src/components/editor/navbar/alignment_dropdown.tsx +104 -56
- package/src/components/editor/navbar/button_modal.tsx +146 -0
- package/src/components/editor/navbar/navbar.tsx +167 -153
- package/src/components/editor/navbar/style_dropdown.tsx +107 -0
- package/src/components/editor/plate.ts +32 -12
- package/src/components/editor/plugins/blockquote.tsx +25 -0
- package/src/components/editor/plugins/buttons.tsx +56 -43
- package/src/components/editor/plugins/exitBreak.ts +0 -2
- package/src/components/editor/plugins/image.tsx +80 -0
- package/src/components/editor/plugins/index.ts +30 -16
- package/src/components/editor/plugins/link.tsx +144 -0
- package/src/components/editor/plugins/list.tsx +34 -0
- package/src/components/editor/plugins/text.tsx +76 -71
- package/src/components/input/radio/radio.tsx +2 -2
- package/src/components/surfaces/modal/modal.tsx +6 -2
- package/src/icons/text/align_justified.tsx +1 -1
- package/tsconfig.json +1 -1
|
@@ -1,24 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import {
|
|
4
|
-
Alignment,
|
|
5
|
-
AlignToolbarButton,
|
|
6
|
-
focusEditor,
|
|
7
4
|
getPluginType,
|
|
8
|
-
// getPreventDefaultHandler,
|
|
9
|
-
ImageToolbarButton,
|
|
10
5
|
insertImage,
|
|
11
6
|
insertNodes,
|
|
12
|
-
isCollapsed,
|
|
13
|
-
LinkToolbarButton,
|
|
14
|
-
MarkToolbarButton,
|
|
15
|
-
PlateEditor,
|
|
16
|
-
setMarks,
|
|
17
|
-
setAlign,
|
|
18
|
-
someNode,
|
|
19
|
-
toggleIndentList,
|
|
20
7
|
toggleNodeType,
|
|
21
|
-
|
|
8
|
+
useLinkToolbarButton,
|
|
9
|
+
useLinkToolbarButtonState,
|
|
10
|
+
useListToolbarButton,
|
|
11
|
+
useListToolbarButtonState,
|
|
12
|
+
useMarkToolbarButton,
|
|
13
|
+
useMarkToolbarButtonState,
|
|
22
14
|
usePlateEditorRef,
|
|
23
15
|
usePlateEditorState,
|
|
24
16
|
} from '@udecode/plate';
|
|
@@ -26,8 +18,9 @@ import {
|
|
|
26
18
|
import { Tooltip } from 'components';
|
|
27
19
|
import {
|
|
28
20
|
Bold,
|
|
29
|
-
Dollar,
|
|
30
21
|
FileCode,
|
|
22
|
+
Headphones,
|
|
23
|
+
Image,
|
|
31
24
|
Italic,
|
|
32
25
|
Link,
|
|
33
26
|
ListOrdered,
|
|
@@ -37,38 +30,80 @@ import {
|
|
|
37
30
|
Strikethrough,
|
|
38
31
|
Underline,
|
|
39
32
|
Undo,
|
|
33
|
+
Video,
|
|
40
34
|
} from 'icons';
|
|
41
35
|
import {
|
|
42
|
-
BUTTON_SUBSCRIBE,
|
|
43
36
|
ELEMENT_BLOCKQUOTE,
|
|
44
37
|
ELEMENT_BUTTON,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
ELEMENT_PARAGRAPH,
|
|
48
|
-
ELEMENT_PAYWALL,
|
|
49
|
-
ELEMENT_QA,
|
|
38
|
+
ELEMENT_OL,
|
|
39
|
+
ELEMENT_UL,
|
|
50
40
|
MARK_BOLD,
|
|
51
41
|
MARK_CODE,
|
|
52
42
|
MARK_ITALIC,
|
|
53
|
-
MARK_HIGHLIGHT,
|
|
54
43
|
MARK_STRIKETHROUGH,
|
|
55
|
-
MARK_SUBSCRIPT,
|
|
56
|
-
MARK_SUPERSCRIPT,
|
|
57
44
|
MARK_UNDERLINE,
|
|
58
45
|
} from '../constants';
|
|
59
46
|
import { AlignmentDropdown } from './alignment_dropdown';
|
|
47
|
+
import { ButtonModal } from './button_modal';
|
|
48
|
+
import { StyleDropdown } from './style_dropdown';
|
|
60
49
|
|
|
61
50
|
export interface EditorBarProps {
|
|
62
|
-
|
|
51
|
+
handleUploadImage: (dataURL: File) => Promise<string>;
|
|
63
52
|
}
|
|
64
53
|
|
|
65
|
-
export const EditorBar = ({
|
|
54
|
+
export const EditorBar = ({ handleUploadImage }: EditorBarProps) => {
|
|
66
55
|
const editorRef = usePlateEditorRef('main');
|
|
67
56
|
const stateEditor = usePlateEditorState();
|
|
68
57
|
const audioRef = React.useRef<HTMLInputElement>(null);
|
|
69
58
|
const imageRef = React.useRef<HTMLInputElement>(null);
|
|
70
59
|
const videoRef = React.useRef<HTMLInputElement>(null);
|
|
71
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
|
+
|
|
72
107
|
const handleAudioChange = async (event: React.FormEvent) => {
|
|
73
108
|
const file = (event.target as HTMLInputElement)?.files?.[0];
|
|
74
109
|
if (!file) return;
|
|
@@ -77,110 +112,37 @@ export const EditorBar = ({ communityName }: EditorBarProps) => {
|
|
|
77
112
|
*/
|
|
78
113
|
};
|
|
79
114
|
|
|
80
|
-
const handleBlockquote = (
|
|
115
|
+
const handleBlockquote = () => {
|
|
81
116
|
toggleNodeType(stateEditor, { activeType: ELEMENT_BLOCKQUOTE });
|
|
82
117
|
};
|
|
83
118
|
|
|
84
|
-
const
|
|
85
|
-
const file = (event.target as HTMLInputElement)?.files?.[0];
|
|
86
|
-
if (!file) return;
|
|
87
|
-
// const imageurl = await handleUploadImage(file);
|
|
88
|
-
// if (imageurl) {
|
|
89
|
-
// insertImage(editorRef, imageurl);
|
|
90
|
-
// }
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const handleFont = (value: string) => {
|
|
94
|
-
const type = getPluginType(editorRef, 'fontSize');
|
|
95
|
-
focusEditor(editorRef);
|
|
96
|
-
setMarks(stateEditor, { [type]: `${value}px` });
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const handleOrderedList = (event: any) => {
|
|
100
|
-
event.preventDefault();
|
|
101
|
-
toggleIndentList(editorRef, {
|
|
102
|
-
listStyleType: 'decimals',
|
|
103
|
-
});
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const handlePaywall = () => {
|
|
119
|
+
const handleButton = (type: string) => {
|
|
107
120
|
insertNodes(editorRef, [
|
|
108
121
|
{
|
|
109
|
-
type:
|
|
122
|
+
type: getPluginType(editorRef, ELEMENT_BUTTON),
|
|
123
|
+
buttonType: type,
|
|
110
124
|
children: [
|
|
111
125
|
{
|
|
112
|
-
|
|
113
|
-
children: [
|
|
114
|
-
{
|
|
115
|
-
text: 'Keep reading with a paid subscription',
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
type: ELEMENT_PARAGRAPH,
|
|
121
|
-
children: [
|
|
122
|
-
{
|
|
123
|
-
text: 'Subscribe to ',
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
text: communityName,
|
|
127
|
-
italic: true,
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
text: ' to keep reading this content.',
|
|
131
|
-
},
|
|
132
|
-
],
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
type: ELEMENT_BUTTON,
|
|
136
|
-
buttonType: BUTTON_SUBSCRIBE,
|
|
137
|
-
children: [
|
|
138
|
-
{
|
|
139
|
-
text: 'Get Access',
|
|
140
|
-
},
|
|
141
|
-
],
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
type: ELEMENT_DEFAULT,
|
|
147
|
-
children: [
|
|
148
|
-
{
|
|
149
|
-
text: '',
|
|
126
|
+
text: 'Subscribe',
|
|
150
127
|
},
|
|
151
128
|
],
|
|
152
129
|
},
|
|
153
130
|
]);
|
|
154
|
-
focusEditor(editorRef);
|
|
155
131
|
};
|
|
156
132
|
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
{
|
|
165
|
-
text: 'Ask your question here...',
|
|
166
|
-
},
|
|
167
|
-
],
|
|
168
|
-
},
|
|
169
|
-
],
|
|
170
|
-
});
|
|
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
|
+
}
|
|
171
140
|
};
|
|
172
141
|
|
|
173
142
|
const handleRedo = () => editorRef.redo();
|
|
174
143
|
|
|
175
144
|
const handleUndo = () => editorRef.undo();
|
|
176
145
|
|
|
177
|
-
const handleUnorderedList = (event: any) => {
|
|
178
|
-
event.preventDefault();
|
|
179
|
-
toggleIndentList(editorRef, {
|
|
180
|
-
listStyleType: 'disc',
|
|
181
|
-
});
|
|
182
|
-
};
|
|
183
|
-
|
|
184
146
|
const handleVideoChange = async (event: React.FormEvent) => {
|
|
185
147
|
const file = (event.target as HTMLInputElement)?.files?.[0];
|
|
186
148
|
if (!file) return;
|
|
@@ -205,57 +167,38 @@ export const EditorBar = ({ communityName }: EditorBarProps) => {
|
|
|
205
167
|
</Tooltip>
|
|
206
168
|
</MenuSection>
|
|
207
169
|
<Divider />
|
|
170
|
+
<MenuSection>
|
|
171
|
+
<StyleDropdown editorRef={editorRef} />
|
|
172
|
+
</MenuSection>
|
|
173
|
+
<Divider />
|
|
208
174
|
<MenuSection>
|
|
209
175
|
<Tooltip content="Bold" place="top">
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
icon={
|
|
214
|
-
<MenuButton $active={true}>
|
|
215
|
-
<Bold size={24} color="grey-5" />
|
|
216
|
-
</MenuButton>
|
|
217
|
-
}
|
|
218
|
-
/>
|
|
176
|
+
<MenuButton $active={currBold} onClick={handleBold}>
|
|
177
|
+
<Bold size={24} color="grey-5" />
|
|
178
|
+
</MenuButton>
|
|
219
179
|
</Tooltip>
|
|
220
180
|
<Tooltip content="Italic" place="top">
|
|
221
|
-
<
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
icon={
|
|
225
|
-
<MenuButton $active={false}>
|
|
226
|
-
<Italic size={24} color="grey-5" />
|
|
227
|
-
</MenuButton>
|
|
228
|
-
}
|
|
229
|
-
/>
|
|
181
|
+
<MenuButton $active={currItalic} onClick={handleItalic}>
|
|
182
|
+
<Italic size={24} color="grey-5" />
|
|
183
|
+
</MenuButton>
|
|
230
184
|
</Tooltip>
|
|
231
185
|
<Tooltip content="Underline" place="top">
|
|
232
|
-
<
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
icon={<Underline size={24} color="grey-5" />}
|
|
236
|
-
/>
|
|
186
|
+
<MenuButton $active={currUnderline} onClick={handleUnderline}>
|
|
187
|
+
<Underline size={24} color="grey-5" />
|
|
188
|
+
</MenuButton>
|
|
237
189
|
</Tooltip>
|
|
238
190
|
<Tooltip content="Strikethrough" place="top">
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
</MenuButton>
|
|
246
|
-
}
|
|
247
|
-
/>
|
|
191
|
+
<MenuButton
|
|
192
|
+
$active={currStrikethrough}
|
|
193
|
+
onClick={handleStrikethrough}
|
|
194
|
+
>
|
|
195
|
+
<Strikethrough size={24} color="grey-5" />
|
|
196
|
+
</MenuButton>
|
|
248
197
|
</Tooltip>
|
|
249
198
|
<Tooltip content="Inline code" place="top">
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
icon={
|
|
254
|
-
<MenuButton $active={false}>
|
|
255
|
-
<FileCode size={24} color="grey-5" />
|
|
256
|
-
</MenuButton>
|
|
257
|
-
}
|
|
258
|
-
/>
|
|
199
|
+
<MenuButton $active={currInlineCode} onClick={handleInlineCode}>
|
|
200
|
+
<FileCode size={24} color="grey-5" />
|
|
201
|
+
</MenuButton>
|
|
259
202
|
</Tooltip>
|
|
260
203
|
</MenuSection>
|
|
261
204
|
<Divider />
|
|
@@ -264,6 +207,77 @@ export const EditorBar = ({ communityName }: EditorBarProps) => {
|
|
|
264
207
|
<AlignmentDropdown editorRef={editorRef} />
|
|
265
208
|
</MenuButton>
|
|
266
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>
|
|
267
281
|
</Menu>
|
|
268
282
|
</Wrapper>
|
|
269
283
|
);
|
|
@@ -326,7 +340,7 @@ export const MenuButton = styled.button<{
|
|
|
326
340
|
}
|
|
327
341
|
|
|
328
342
|
background-color: ${({ $active }) =>
|
|
329
|
-
$active ? '
|
|
343
|
+
$active ? 'var(--color-grey-9)' : 'transparent'};
|
|
330
344
|
`;
|
|
331
345
|
|
|
332
346
|
const Divider = styled.div`
|
|
@@ -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
|
+
};
|
|
@@ -1,17 +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';
|
|
1
7
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
ParagraphElement,
|
|
9
|
+
HOneElement,
|
|
10
|
+
HTwoElement,
|
|
11
|
+
HThreeElement,
|
|
12
|
+
HFourElement,
|
|
13
|
+
HFiveElement,
|
|
14
|
+
HSixElement,
|
|
15
|
+
} from './plugins/text';
|
|
16
|
+
|
|
9
17
|
import * as Elements from './constants';
|
|
10
18
|
|
|
11
|
-
export const plateUI =
|
|
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,
|
|
12
27
|
[Elements.ELEMENT_BLOCKQUOTE]: BlockquoteElement,
|
|
13
|
-
[Elements.
|
|
14
|
-
[Elements.
|
|
15
|
-
|
|
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
|
+
}),
|
|
16
35
|
[Elements.ELEMENT_IMAGE]: ImageElement,
|
|
17
|
-
|
|
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
|
+
});
|