@sinups/editor v0.0.24
LayersTextEditor
LayersTextEditor is a text editor for web applications written in JavaScript, with a focus on reliability, accessibility, and performance.
Installation
To install the package, run one of the following commands:
Use npm:
npm install @layers-app/editor
Use yarn:
yarn add @layers-app/editor
Initializing the text editor
import { Editor } from '@layers-app/editor';
By default, LayersTextEditor works with an object and can return either an object or HTML.
Example with an object:
const text = 'Hello world';
const json = {
root: {
children: [
{
children: [
{
detail: 0,
format: 0,
mode: 'normal',
style: '',
text: text,
type: 'text',
version: 1,
},
],
direction: 'ltr',
format: '',
indent: 0,
type: 'paragraph',
version: 1,
},
],
direction: 'ltr',
format: '',
indent: 0,
type: 'root',
version: 1,
},
};
const onChange = (
data, // json
) => <Editor initialContent={json} onChange={onChange} />;
You can also pass an HTML string to the editor.
Example with HTML:
const html = `
<h2 dir="ltr" style="text-align: left;">
<span style="background-color: rgb(248, 231, 28); font-family: "Trebuchet MS"; white-space: pre-wrap;">Hello</span>
</h2>
<h2 dir="ltr">
<br>
</h2>
<p dir="ltr">
<br>
</p>
<p dir="ltr">
<span style="font-size: 21px; white-space: pre-wrap;">world</span>
</p>
`
const onChange = (data) => // json
<Editor initialContent={html} onChange={onChange} />
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:
const html = `
<h2 dir="ltr" style="text-align: left;">
<span style="background-color: rgb(248, 231, 28); font-family: "Trebuchet MS"; white-space: pre-wrap;">Hello</span>
</h2>
<h2 dir="ltr">
<br>
</h2>
<p dir="ltr">
<br>
</p>
<p dir="ltr">
<span style="font-size: 21px; white-space: pre-wrap;">world</span>
</p>
`
const onChange = (data: string, text?: string) => {
// data - html from editor
// text - text from editor
}
<Editor initialContent={html} outputFormat="html" onChange={onChange} />
Use StylesProvider to add styling to your HTML content.
<StylesProvider>
<div
dangerouslySetInnerHTML={{ __html: '<p>Your html here</p>' }}
/>
</StylesProvider>
Image upload
To start working with image uploads, use the fetchUploadMedia 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.
const fetchUploadMedia = async (
file: File,
success: (url: string, id: string) => void,
error?: (error?: Error) => void
) => {
const formData = new FormData();
formData.append('File', file);
formData.append('FileAccessModifier', '0');
try {
const response = await fetch('/api/v1/Files/Upload', {
method: 'POST',
body: formData,
credentials: 'include'
});
if (!response.ok) {
throw new Error('File upload failed');
}
const data = await response.json();
const { Id, Url } = data;
success(Url, Id);
} catch (err) {
if (error) {
if (err instanceof Error) {
error(err);
} else {
error(new Error('An unknown error occurred'));
}
}
}
};
<Editor
...props
fetchUploadMedia={fetchUploadMedia}
/>
Image Deletion
To have greater control over image deletion, pass an optional function fetchDeleteMedia 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.
const fetchDeleteMedia = async (
id: string,
success: () => void,
error?: (error?: Error) => void
) => {
const body = { Ids: [id] };
try {
const response = await fetch('/api/v1/Documents/Delete', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(body),
credentials: 'include'
});
await response.json();
success();
} catch (err) {
if (error) {
if (err instanceof Error) {
error(err);
} else {
error(new Error('An unknown error occurred'));
}
}
}
};
<Editor
...props
fetchUploadMedia={fetchUploadMedia}
fetchDeleteMedia={fetchUploadMedia}
/>
Additional options for working with image uploads.
import { Editor, Dropzone } from "@sinups/editor-dsd";
const Content = () => (
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
{/*
The components Dropzone.Accept, Dropzone.Reject, and Dropzone.Idle are visible only when the user performs specific actions:
Dropzone.Accept is visible only when the user drags a file that can be accepted into the drop zone.
Dropzone.Reject is visible only when the user drags a file that cannot be accepted into the drop zone.
Dropzone.Idle is visible when the user is not dragging any file into the drop zone.
*/}
<Dropzone.Accept>
<IconUpload
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-blue-6)' }}
stroke={1.5}
/>
</Dropzone.Accept>
<Dropzone.Reject>
<IconX
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-red-6)' }}
stroke={1.5}
/>
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-dimmed)' }}
stroke={1.5}
/>
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Drag images here or click to select files
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
Attach as many files as you want, each file must not exceed{' '} {maxImageSize} МБ.
</Text>
</div>
</Group>
);
<Editor
...props
fetchUploadMedia={fetchUploadMedia}
contentModalUploadImage={Content}
maxImageSize={5}
maxImageSizeError={() => {}}
/>
File upload
For uploading a file or audio, you might need the third parameter "data".
const fetchUploadMedia = async (
file: File,
success: (url: string, id: string, data?: {
contentType: string;
fileSize: string;
originalFileName: string;
}) => void,
error?: (error?: Error) => void
) => {
const formData = new FormData();
formData.append('File', file);
formData.append('FileAccessModifier', '0');
try {
const response = await fetch('/api/v1/Files/Upload', {
method: 'POST',
body: formData,
credentials: 'include'
});
if (!response.ok) {
throw new Error('File upload failed');
}
const data = await response.json();
const { Id, Url } = data;
success(Url, Id, data);
} catch (err) {
if (error) {
if (err instanceof Error) {
error(err);
} else {
error(new Error('An unknown error occurred'));
}
}
}
};
<Editor
{...props}
ws={{
url: 'https://wss.dudoc.io/', // WebSocket URL
id: '322323', // Unique document ID
user: userProfile, // Current user
getActiveUsers: (users) => {
// Returns active users editing the document
setActiveUsers(users);
},
}}
/>
import { CLEAR_EDITOR_COMMAND } from './EditorLexical';
<>
<button
onClick={() => {
if (editorRef.current) {
editorRef.current.update(() => {
editorRef.current?.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined);
});
}
}}
>
Reset
</button>
<Editor
...props
editorRef={editorRef}
/>
<>
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.
debounce?: number - Defines how often the onChange function is called, in milliseconds.
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.
outputFormat?: 'html' | 'json' - The outputFormat property defines whether we want to output an HTML string or a JSON object. The default is JSON.
initialContent: string | object - The initial content for the editor.
maxHeight?: number - Sets the height of the editor. The default is 100%.
mode?: 'simple' | 'default' | 'full' | 'editor' - The editor mode. Depending on the chosen mode, functionality may be restricted or extended. The default is default.
fetchUploadMedia?: (file: File, success: (url: string, id: string, error?: (error?: Error) => void) => void) - Function to upload an image to your service.
fetchDeleteMedia?: (id: string, success: () => void, error?: (error?: Error) => void) - Helper function to delete an image.
maxImageSize?: number - The maximum image size in megabytes.
contentModalUploadImage?: React.FunctionComponent - A React component to replace content in DropZone.
maxImageSizeError?: () => void - A function that is called if the image exceeds the maxImageSize.
disable?: boolean - Toggles the editor into read-only mode.
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.
editorRef?: { current: EditorType | null } - Reference to the editor.