@layers-app/editor 0.0.1 → 0.0.2
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 +237 -212
- package/dist/{DSD.BSMoqlgJ.js → DSD.BJ38UQLe.js} +6993 -6987
- package/dist/{DSD.BTb8iwne.js → DSD.BL_py6LH.js} +1 -1
- package/dist/{DSD.Dps-yU0B.js → DSD.C11ukNU9.js} +12 -12
- package/dist/{DSD.wx0g6wlB.js → DSD.CdYWFL0O.js} +12 -12
- package/dist/{DSD.rW4l7_6K.js → DSD.CjmkQe7N.js} +72 -72
- package/dist/index.d.ts +4 -4
- package/dist/index.js +6 -6
- package/dist/index.umd.cjs +72 -72
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,21 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# LayersTextEditor
|
|
2
2
|
|
|
3
|
+
LayersTextEditor is a text editor for web applications written in JavaScript, with a focus on reliability, accessibility, and performance.
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
<details>
|
|
6
|
+
<summary>
|
|
7
|
+
🚀 Quick Start
|
|
8
|
+
</summary>
|
|
5
9
|
|
|
10
|
+
## Installation
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
To install the package, run one of the following commands:
|
|
8
13
|
|
|
14
|
+
### Use npm:
|
|
9
15
|
|
|
10
|
-
```
|
|
11
|
-
|
|
16
|
+
```bash
|
|
17
|
+
npm install @layers-app/editor
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Use yarn:
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
```bash
|
|
23
|
+
yarn add @layers-app/editor
|
|
24
|
+
```
|
|
14
25
|
|
|
15
|
-
|
|
26
|
+
### Initializing the text editor
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
import { Editor } from '@layers-app/editor';
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
By default, LayersTextEditor works with an object and can return either an object or HTML.
|
|
16
33
|
|
|
17
34
|
Example with an object:
|
|
18
35
|
|
|
36
|
+
```js
|
|
19
37
|
const text = 'Hello world';
|
|
20
38
|
|
|
21
39
|
const json = {
|
|
@@ -51,11 +69,13 @@ const json = {
|
|
|
51
69
|
const onChange = (
|
|
52
70
|
data // json
|
|
53
71
|
) => <Editor initialContent={json} onChange={onChange} />;
|
|
72
|
+
```
|
|
54
73
|
|
|
55
74
|
You can also pass an HTML string to the editor.
|
|
56
75
|
|
|
57
76
|
Example with HTML:
|
|
58
77
|
|
|
78
|
+
```
|
|
59
79
|
const html = `
|
|
60
80
|
<h2 dir="ltr" style="text-align: left;">
|
|
61
81
|
<span style="background-color: rgb(248, 231, 28); font-family: "Trebuchet MS"; white-space: pre-wrap;">Hello</span>
|
|
@@ -74,9 +94,11 @@ const html = `
|
|
|
74
94
|
const onChange = (data) => // json
|
|
75
95
|
|
|
76
96
|
<Editor initialContent={html} onChange={onChange} />
|
|
97
|
+
```
|
|
77
98
|
|
|
78
|
-
The
|
|
99
|
+
The output of the data in the `onChange` function is controlled by the **outputFormat** property. **outputFormat** can be either "html" or "json". Example with **outputFormat**:
|
|
79
100
|
|
|
101
|
+
```
|
|
80
102
|
const html = `
|
|
81
103
|
<h2 dir="ltr" style="text-align: left;">
|
|
82
104
|
<span style="background-color: rgb(248, 231, 28); font-family: "Trebuchet MS"; white-space: pre-wrap;">Hello</span>
|
|
@@ -95,246 +117,249 @@ const html = `
|
|
|
95
117
|
const onChange = (data) => // html
|
|
96
118
|
|
|
97
119
|
<Editor initialContent={html} outputFormat="html" onChange={onChange} />
|
|
120
|
+
```
|
|
98
121
|
|
|
99
|
-
|
|
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>
|
|
122
|
+
</details>
|
|
108
123
|
|
|
109
|
-
|
|
124
|
+
<details>
|
|
125
|
+
<summary>
|
|
126
|
+
🎨 StylesProvider
|
|
127
|
+
</summary>
|
|
110
128
|
|
|
111
|
-
|
|
129
|
+
Use **StylesProvider** to add styling to your HTML content.
|
|
112
130
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
```
|
|
132
|
+
<StylesProvider>
|
|
133
|
+
<div
|
|
134
|
+
dangerouslySetInnerHTML={{ __html: '<p>Your html here</p>' }}
|
|
135
|
+
/>
|
|
136
|
+
</StylesProvider>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
</details>
|
|
140
|
+
|
|
141
|
+
<details>
|
|
142
|
+
<summary>
|
|
143
|
+
🖼️ Image upload
|
|
144
|
+
</summary>
|
|
145
|
+
|
|
146
|
+
## Image upload
|
|
147
|
+
|
|
148
|
+
To start working with image uploads, use the **fetchUploadImage** function, which takes three parameters: **file**, **success**, and **error**. After successfully uploading the image to your service, you should call the **success** function and pass two required arguments: the **URL** of the image and its **ID**.
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
const fetchUploadImage = async (
|
|
152
|
+
file: File,
|
|
153
|
+
success: (url: string, id: string) => void,
|
|
154
|
+
error?: (error?: Error) => void
|
|
155
|
+
) => {
|
|
156
|
+
const formData = new FormData();
|
|
157
|
+
formData.append('File', file);
|
|
158
|
+
formData.append('FileAccessModifier', '0');
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const response = await fetch('/api/v1/Files/Upload', {
|
|
162
|
+
method: 'POST',
|
|
163
|
+
body: formData,
|
|
164
|
+
credentials: 'include'
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
if (!response.ok) {
|
|
168
|
+
throw new Error('File upload failed');
|
|
169
|
+
}
|
|
135
170
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
171
|
+
const data = await response.json();
|
|
172
|
+
const { Id, Url } = data;
|
|
173
|
+
|
|
174
|
+
success(Url, Id);
|
|
175
|
+
} catch (err) {
|
|
176
|
+
if (error) {
|
|
177
|
+
if (err instanceof Error) {
|
|
178
|
+
error(err);
|
|
179
|
+
} else {
|
|
180
|
+
error(new Error('An unknown error occurred'));
|
|
181
|
+
}
|
|
143
182
|
}
|
|
144
183
|
}
|
|
145
|
-
}
|
|
146
|
-
};
|
|
184
|
+
};
|
|
147
185
|
|
|
148
186
|
<Editor
|
|
149
|
-
|
|
187
|
+
...props
|
|
150
188
|
fetchUploadImage={fetchUploadImage}
|
|
151
189
|
/>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
) =>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Image Deletion
|
|
193
|
+
|
|
194
|
+
To have greater control over image deletion, pass an optional function **fetchDeleteImage** to the editor, which accepts three parameters: **id**, **success**, and **error**. After successfully deleting the image from your service, the **success** function should be called.
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
const fetchDeleteImage = async (
|
|
198
|
+
id: string,
|
|
199
|
+
success: () => void,
|
|
200
|
+
error?: (error?: Error) => void
|
|
201
|
+
) => {
|
|
202
|
+
const body = { Ids: [id] };
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const response = await fetch('/api/v1/Documents/Delete', {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
headers: {
|
|
208
|
+
'Content-Type': 'application/json'
|
|
209
|
+
},
|
|
210
|
+
body: JSON.stringify(body),
|
|
211
|
+
credentials: 'include'
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
await response.json();
|
|
215
|
+
success();
|
|
216
|
+
} catch (err) {
|
|
217
|
+
if (error) {
|
|
218
|
+
if (err instanceof Error) {
|
|
219
|
+
error(err);
|
|
220
|
+
} else {
|
|
221
|
+
error(new Error('An unknown error occurred'));
|
|
222
|
+
}
|
|
182
223
|
}
|
|
183
224
|
}
|
|
184
|
-
}
|
|
185
|
-
};
|
|
225
|
+
};
|
|
186
226
|
|
|
187
227
|
<Editor
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
228
|
+
...props
|
|
229
|
+
fetchUploadImage={fetchUploadImage}
|
|
230
|
+
fetchDeleteImage={fetchUploadImage}
|
|
191
231
|
/>
|
|
232
|
+
```
|
|
192
233
|
|
|
193
|
-
Additional
|
|
234
|
+
## Additional options for working with image uploads.
|
|
194
235
|
|
|
195
|
-
|
|
236
|
+
```
|
|
237
|
+
import { Editor, Dropzone } from "@sinups/editor-dsd";
|
|
196
238
|
|
|
197
239
|
const Content = () => (
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
Dropzone.Accept, Dropzone.Reject, and Dropzone.Idle
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
</Group>
|
|
236
|
-
);
|
|
240
|
+
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
|
241
|
+
{/*
|
|
242
|
+
The components Dropzone.Accept, Dropzone.Reject, and Dropzone.Idle are visible only when the user performs specific actions:
|
|
243
|
+
|
|
244
|
+
Dropzone.Accept is visible only when the user drags a file that can be accepted into the drop zone.
|
|
245
|
+
Dropzone.Reject is visible only when the user drags a file that cannot be accepted into the drop zone.
|
|
246
|
+
Dropzone.Idle is visible when the user is not dragging any file into the drop zone.
|
|
247
|
+
*/}
|
|
248
|
+
<Dropzone.Accept>
|
|
249
|
+
<IconUpload
|
|
250
|
+
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-blue-6)' }}
|
|
251
|
+
stroke={1.5}
|
|
252
|
+
/>
|
|
253
|
+
</Dropzone.Accept>
|
|
254
|
+
<Dropzone.Reject>
|
|
255
|
+
<IconX
|
|
256
|
+
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-red-6)' }}
|
|
257
|
+
stroke={1.5}
|
|
258
|
+
/>
|
|
259
|
+
</Dropzone.Reject>
|
|
260
|
+
<Dropzone.Idle>
|
|
261
|
+
<IconPhoto
|
|
262
|
+
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-dimmed)' }}
|
|
263
|
+
stroke={1.5}
|
|
264
|
+
/>
|
|
265
|
+
</Dropzone.Idle>
|
|
266
|
+
|
|
267
|
+
<div>
|
|
268
|
+
<Text size="xl" inline>
|
|
269
|
+
Drag images here or click to select files
|
|
270
|
+
</Text>
|
|
271
|
+
<Text size="sm" c="dimmed" inline mt={7}>
|
|
272
|
+
Attach as many files as you want, each file must not exceed{' '} {maxImageSize} МБ.
|
|
273
|
+
</Text>
|
|
274
|
+
</div>
|
|
275
|
+
</Group>
|
|
276
|
+
);
|
|
237
277
|
|
|
238
278
|
<Editor
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
279
|
+
...props
|
|
280
|
+
fetchUploadImage={fetchUploadImage}
|
|
281
|
+
contentModalUploadImage={Content}
|
|
282
|
+
maxImageSize={5}
|
|
283
|
+
maxImageSizeError={() => {}}
|
|
244
284
|
/>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
</details>
|
|
245
288
|
|
|
246
|
-
|
|
289
|
+
<details>
|
|
290
|
+
<summary>
|
|
291
|
+
👥 Collaboration
|
|
292
|
+
</summary>
|
|
247
293
|
|
|
294
|
+
```jsx
|
|
248
295
|
<Editor
|
|
249
296
|
{...props}
|
|
250
297
|
ws={{
|
|
251
|
-
url: 'https://wss.dudoc.io/', //
|
|
298
|
+
url: 'https://wss.dudoc.io/', // WebSocket URL
|
|
252
299
|
id: '322323', // Unique document ID
|
|
253
300
|
user: userProfile, // Current user
|
|
254
301
|
getActiveUsers: (users) => {
|
|
255
|
-
// Returns
|
|
302
|
+
// Returns active users editing the document
|
|
256
303
|
setActiveUsers(users);
|
|
257
304
|
}
|
|
258
305
|
}}
|
|
259
306
|
/>
|
|
307
|
+
```
|
|
260
308
|
|
|
261
|
-
|
|
309
|
+
</details>
|
|
262
310
|
|
|
263
|
-
|
|
311
|
+
<details>
|
|
312
|
+
<summary>
|
|
313
|
+
📝 Additional options
|
|
314
|
+
</summary>
|
|
315
|
+
|
|
316
|
+
## Reset editor content
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
import { CLEAR_EDITOR_COMMAND } from './EditorLexical';
|
|
264
321
|
|
|
265
322
|
<>
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
outputFormat?: 'html' | 'json'
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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.
|
|
323
|
+
<button
|
|
324
|
+
onClick={() => {
|
|
325
|
+
if (editorRef.current) {
|
|
326
|
+
editorRef.current.update(() => {
|
|
327
|
+
editorRef.current?.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}}
|
|
331
|
+
>
|
|
332
|
+
Reset
|
|
333
|
+
</button>
|
|
334
|
+
<Editor
|
|
335
|
+
...props
|
|
336
|
+
editorRef={editorRef}
|
|
337
|
+
/>
|
|
338
|
+
<>
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
</details>
|
|
342
|
+
<details>
|
|
343
|
+
<summary>
|
|
344
|
+
⚙️ Properties
|
|
345
|
+
</summary>
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
onChange: (value: string | object) => undefined - A function that triggers every time the editor content changes and returns an HTML string or an object depending on the outputFormat property.
|
|
349
|
+
debounce?: number - Defines how often the onChange function is called, in milliseconds.
|
|
350
|
+
onBlur: (value: string | object) => undefined - A function that triggers when the editor loses focus and returns an HTML string or an object depending on the outputFormat property.
|
|
351
|
+
outputFormat?: 'html' | 'json' - The outputFormat property defines whether we want to output an HTML string or a JSON object. The default is JSON.
|
|
352
|
+
initialContent: string | object - The initial content for the editor.
|
|
353
|
+
maxHeight?: number - Sets the height of the editor. The default is 100%.
|
|
354
|
+
mode?: 'simple' | 'default' | 'full' | 'editor' - The editor mode. Depending on the chosen mode, functionality may be restricted or extended. The default is default.
|
|
355
|
+
fetchUploadImage?: (file: File, success: (url: string, id: string, error?: (error?: Error) => void) => void) - Function to upload an image to your service.
|
|
356
|
+
fetchDeleteImage?: (id: string, success: () => void, error?: (error?: Error) => void) - Helper function to delete an image.
|
|
357
|
+
maxImageSize?: number - The maximum image size in megabytes.
|
|
358
|
+
contentModalUploadImage?: React.FunctionComponent - A React component to replace content in DropZone.
|
|
359
|
+
maxImageSizeError?: () => void - A function that is called if the image exceeds the maxImageSize.
|
|
360
|
+
disable?: boolean - Toggles the editor into read-only mode.
|
|
361
|
+
ws?: { url: string, id: string, user: { color: string, name: string }, getActiveUsers: (users) => void } - WebSocket settings: URL, document ID, current user details, and function to return active users editing the document.
|
|
362
|
+
editorRef?: { current: EditorType | null } - Reference to the editor.
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
</details>
|