@layers-app/editor 0.0.1

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/README.md ADDED
@@ -0,0 +1,340 @@
1
+ # Editor
2
+
3
+
4
+ Editor is a JavaScript text editor for web applications with a focus on reliability, accessibility, and performance. Editor aims to provide an optimal developer experience so that you can easily prototype and implement features with confidence. Combined with a highly extensible architecture, Editor allows developers to create unique text editors that can scale in both size and functionality.
5
+
6
+
7
+ ## Quick Start
8
+
9
+
10
+ ```js
11
+ import { Editor } from '@layers-app/editor';
12
+
13
+ Initialize the text editor.
14
+
15
+ By default, Editor works with an object and can return either an object or HTML.
16
+
17
+ Example with an object:
18
+
19
+ const text = 'Hello world';
20
+
21
+ const json = {
22
+ root: {
23
+ children: [
24
+ {
25
+ children: [
26
+ {
27
+ detail: 0,
28
+ format: 0,
29
+ mode: 'normal',
30
+ style: '',
31
+ text: text,
32
+ type: 'text',
33
+ version: 1
34
+ }
35
+ ],
36
+ direction: 'ltr',
37
+ format: '',
38
+ indent: 0,
39
+ type: 'paragraph',
40
+ version: 1
41
+ }
42
+ ],
43
+ direction: 'ltr',
44
+ format: '',
45
+ indent: 0,
46
+ type: 'root',
47
+ version: 1
48
+ }
49
+ };
50
+
51
+ const onChange = (
52
+ data // json
53
+ ) => <Editor initialContent={json} onChange={onChange} />;
54
+
55
+ You can also pass an HTML string to the editor.
56
+
57
+ Example with HTML:
58
+
59
+ const html = `
60
+ <h2 dir="ltr" style="text-align: left;">
61
+ <span style="background-color: rgb(248, 231, 28); font-family: &quot;Trebuchet MS&quot;; white-space: pre-wrap;">Hello</span>
62
+ </h2>
63
+ <h2 dir="ltr">
64
+ <br>
65
+ </h2>
66
+ <p dir="ltr">
67
+ <br>
68
+ </p>
69
+ <p dir="ltr">
70
+ <span style="font-size: 21px; white-space: pre-wrap;">world</span>
71
+ </p>
72
+ `
73
+
74
+ const onChange = (data) => // json
75
+
76
+ <Editor initialContent={html} onChange={onChange} />
77
+
78
+ The outputFormat property controls how the onChange function outputs data. outputFormat can be either "html" or "json". An example with outputFormat:
79
+
80
+ const html = `
81
+ <h2 dir="ltr" style="text-align: left;">
82
+ <span style="background-color: rgb(248, 231, 28); font-family: &quot;Trebuchet MS&quot;; white-space: pre-wrap;">Hello</span>
83
+ </h2>
84
+ <h2 dir="ltr">
85
+ <br>
86
+ </h2>
87
+ <p dir="ltr">
88
+ <br>
89
+ </p>
90
+ <p dir="ltr">
91
+ <span style="font-size: 21px; white-space: pre-wrap;">world</span>
92
+ </p>
93
+ `
94
+
95
+ const onChange = (data) => // html
96
+
97
+ <Editor initialContent={html} outputFormat="html" onChange={onChange} />
98
+
99
+ DocSpaceStylesProvider
100
+
101
+ Use DocSpaceStylesProvider to add styling to your HTML content:
102
+
103
+ <DocSpaceStylesProvider>
104
+ <div
105
+ dangerouslySetInnerHTML={{ __html: '<p>Your html here</p>' }}
106
+ />
107
+ </DocSpaceStylesProvider>
108
+
109
+ Image Upload
110
+
111
+ To work with image uploads, use the fetchUploadImage function, which takes three parameters: file, success, and error. After your images have been successfully uploaded to your service, you need to call the success function and pass two required arguments: the image URL and ID.
112
+
113
+ const fetchUploadImage = async (
114
+ file: File,
115
+ success: (url: string, id: string) => void,
116
+ error?: (error?: Error) => void
117
+ ) => {
118
+ const formData = new FormData();
119
+ formData.append('File', file);
120
+ formData.append('FileAccessModifier', '0');
121
+
122
+ try {
123
+ const response = await fetch('/api/v1/Files/Upload', {
124
+ method: 'POST',
125
+ body: formData,
126
+ credentials: 'include'
127
+ });
128
+
129
+ if (!response.ok) {
130
+ throw new Error('File upload failed');
131
+ }
132
+
133
+ const data = await response.json();
134
+ const { Id, Url } = data;
135
+
136
+ success(Url, Id);
137
+ } catch (err) {
138
+ if (error) {
139
+ if (err instanceof Error) {
140
+ error(err);
141
+ } else {
142
+ error(new Error('An unknown error occurred'));
143
+ }
144
+ }
145
+ }
146
+ };
147
+
148
+ <Editor
149
+ {...props}
150
+ fetchUploadImage={fetchUploadImage}
151
+ />
152
+
153
+ Image Deletion
154
+
155
+ For greater control over image deletion, pass an optional fetchDeleteImage function to the editor. It takes three parameters: id, success, and error. After the image is successfully removed from your service, call the success function.
156
+
157
+ const fetchDeleteImage = async (
158
+ id: string,
159
+ success: () => void,
160
+ error?: (error?: Error) => void
161
+ ) => {
162
+ const body = { Ids: [id] };
163
+
164
+ try {
165
+ const response = await fetch('/api/v1/Documents/Delete', {
166
+ method: 'POST',
167
+ headers: {
168
+ 'Content-Type': 'application/json'
169
+ },
170
+ body: JSON.stringify(body),
171
+ credentials: 'include'
172
+ });
173
+
174
+ await response.json();
175
+ success();
176
+ } catch (err) {
177
+ if (error) {
178
+ if (err instanceof Error) {
179
+ error(err);
180
+ } else {
181
+ error(new Error('An unknown error occurred'));
182
+ }
183
+ }
184
+ }
185
+ };
186
+
187
+ <Editor
188
+ {...props}
189
+ fetchUploadImage={fetchUploadImage}
190
+ fetchDeleteImage={fetchDeleteImage}
191
+ />
192
+
193
+ Additional Options for Image Upload
194
+
195
+ import { Editor, Dropzone } from "@layers-app/editor";
196
+
197
+ const Content = () => (
198
+ <Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
199
+ {/*
200
+ Dropzone.Accept, Dropzone.Reject, and Dropzone.Idle components are only visible
201
+ when the user performs a specific action:
202
+
203
+ Dropzone.Accept is displayed only when a file that can be accepted is dragged over the dropzone.
204
+ Dropzone.Reject is displayed only when a file that cannot be accepted is dragged over the dropzone.
205
+ Dropzone.Idle is visible when the user is not dragging anything over the dropzone.
206
+ */}
207
+ <Dropzone.Accept>
208
+ <IconUpload
209
+ style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-blue-6)' }}
210
+ stroke={1.5}
211
+ />
212
+ </Dropzone.Accept>
213
+ <Dropzone.Reject>
214
+ <IconX
215
+ style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-red-6)' }}
216
+ stroke={1.5}
217
+ />
218
+ </Dropzone.Reject>
219
+ <Dropzone.Idle>
220
+ <IconPhoto
221
+ style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-dimmed)' }}
222
+ stroke={1.5}
223
+ />
224
+ </Dropzone.Idle>
225
+
226
+ <div>
227
+ <Text size="xl" inline>
228
+ Drag images here or click to select files
229
+ </Text>
230
+ <Text size="sm" c="dimmed" inline mt={7}>
231
+ Attach as many files as you like. Each file must not exceed {' '}
232
+ {maxImageSize} MB.
233
+ </Text>
234
+ </div>
235
+ </Group>
236
+ );
237
+
238
+ <Editor
239
+ {...props}
240
+ fetchUploadImage={fetchUploadImage}
241
+ contentModalUploadImage={Content}
242
+ maxImageSize={5}
243
+ maxImageSizeError={() => {}}
244
+ />
245
+
246
+ Collaboration
247
+
248
+ <Editor
249
+ {...props}
250
+ ws={{
251
+ url: 'https://wss.dudoc.io/', // Websocket URL
252
+ id: '322323', // Unique document ID
253
+ user: userProfile, // Current user
254
+ getActiveUsers: (users) => {
255
+ // Returns the users actively editing the document
256
+ setActiveUsers(users);
257
+ }
258
+ }}
259
+ />
260
+
261
+ Reset Editor Content
262
+
263
+ import { CLEAR_EDITOR_COMMAND } from './EditorLexical';
264
+
265
+ <>
266
+ <button
267
+ onClick={() => {
268
+ if (editorRef.current) {
269
+ editorRef.current.update(() => {
270
+ editorRef.current?.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined);
271
+ });
272
+ }
273
+ }}
274
+ >
275
+ Reset
276
+ </button>
277
+ <Editor
278
+ {...props}
279
+ editorRef={editorRef}
280
+ />
281
+ </>
282
+
283
+ Properties
284
+
285
+ onChange: (value: string | object) => void
286
+ // Fires whenever the editor changes, returning an HTML string or an object, depending on outputFormat.
287
+
288
+ debounce?: number
289
+ // Controls how often the onChange function is called, in milliseconds.
290
+
291
+ onBlur: (value: string | object) => void
292
+ // Fires when the editor loses focus, returning an HTML string or an object, depending on outputFormat.
293
+
294
+ outputFormat?: 'html' | 'json'
295
+ // Determines if onChange returns HTML or JSON. Default is JSON.
296
+
297
+ initialContent: string | object
298
+ // The initial data for the editor.
299
+
300
+ maxHeight?: number
301
+ // Sets the height of the editor. Default is 100%.
302
+
303
+ mode?: 'simple' | 'default' | 'full' | 'editor'
304
+ // The editor mode, which can restrict or add functionality. Default is 'default'.
305
+
306
+ fetchUploadImage?: (
307
+ file: File,
308
+ success: (url: string, id: string),
309
+ error?: (error?: Error) => void
310
+ ) => void
311
+ // Function to upload images to your service.
312
+
313
+ fetchDeleteImage?: (
314
+ id: string,
315
+ success: () => void,
316
+ error?: (error?: Error) => void
317
+ ) => void
318
+ // Helper function to delete images.
319
+
320
+ maxImageSize?: number
321
+ // Maximum image size in MB.
322
+
323
+ contentModalUploadImage?: React.FunctionComponent
324
+ // React component to replace the DropZone content.
325
+
326
+ maxImageSizeError?: () => void
327
+ // Function called when the image exceeds maxImageSize.
328
+
329
+ disable?: boolean
330
+ // Toggles read-only mode.
331
+
332
+ ws?: {
333
+ url: string, // Websocket URL
334
+ id: string, // Unique document ID
335
+ user: { color: string; name: string }, // Current user
336
+ getActiveUsers: (users) => void // Returns the users actively editing the document
337
+ }
338
+
339
+ editorRef?: { current: EditorType | null }
340
+ // Reference to the editor.