Інтеграція редактора Lexical в CMS сайту

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Інтеграція редактора Lexical в CMS сайту
Середня
від 1 робочого дня до 3 робочих днів
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Інтеграція редактора Lexical у CMS

Lexical — редактор розроблений Meta з акцентом на розширюваність та продуктивність. Написаний на TypeScript, розроблений з урахуванням досвіду створення редактора Facebook. Headless як Tiptap — весь UI будується розробником.

Встановлення

npm install lexical @lexical/react @lexical/rich-text @lexical/list @lexical/link
npm install @lexical/markdown @lexical/code @lexical/utils @lexical/selection

Базова конфігурація

import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
import { ListPlugin } from '@lexical/react/LexicalListPlugin';
import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
import { ListNode, ListItemNode } from '@lexical/list';
import { LinkNode, AutoLinkNode } from '@lexical/link';
import { CodeNode, CodeHighlightNode } from '@lexical/code';

const editorConfig = {
    namespace: 'cms-editor',
    theme: {
        paragraph: 'mb-3',
        heading: { h1: 'text-3xl font-bold', h2: 'text-2xl font-bold', h3: 'text-xl font-bold' },
        list: { ol: 'list-decimal ml-6', ul: 'list-disc ml-6' },
        text: { bold: 'font-bold', italic: 'italic', underline: 'underline' }
    },
    nodes: [HeadingNode, QuoteNode, ListNode, ListItemNode, LinkNode, AutoLinkNode, CodeNode, CodeHighlightNode],
    onError: console.error
};

function LexicalEditor({ initialState, onChange }) {
    return (
        <LexicalComposer initialConfig={{ ...editorConfig, editorState: initialState }}>
            <div className="relative border rounded-lg">
                <ToolbarPlugin />
                <div className="relative">
                    <RichTextPlugin
                        contentEditable={<ContentEditable className="outline-none p-4 min-h-96" />}
                        placeholder={<div className="absolute top-4 left-4 text-gray-400">Почніть друкувати...</div>}
                        ErrorBoundary={({ children }) => children}
                    />
                    <HistoryPlugin />
                    <AutoFocusPlugin />
                    <ListPlugin />
                    <LinkPlugin />
                    <OnChangePlugin onChange={(editorState) => {
                        editorState.read(() => {
                            const json = editorState.toJSON();
                            onChange(JSON.stringify(json));
                        });
                    }} />
                </div>
            </div>
        </LexicalComposer>
    );
}

Користувацька панель інструментів

import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { FORMAT_TEXT_COMMAND } from 'lexical';
import { INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND } from '@lexical/list';

function ToolbarPlugin() {
    const [editor] = useLexicalComposerContext();
    const [isBold, setIsBold] = useState(false);

    useEffect(() => {
        return editor.registerUpdateListener(({ editorState }) => {
            editorState.read(() => {
                const selection = $getSelection();
                if ($isRangeSelection(selection)) {
                    setIsBold(selection.hasFormat('bold'));
                }
            });
        });
    }, [editor]);

    return (
        <div className="flex gap-1 p-2 border-b">
            <button
                onClick={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold')}
                className={isBold ? 'bg-gray-200 rounded px-2' : 'px-2'}
            >
                B
            </button>
            <button onClick={() => editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined)}>
                Список
            </button>
        </div>
    );
}

Користувацькі вузли

import { DecoratorNode, SerializedLexicalNode } from 'lexical';

class ImageNode extends DecoratorNode<React.ReactElement> {
    static getType() { return 'image'; }
    static clone(node: ImageNode) { return new ImageNode(node.__src, node.__alt, node.__key); }

    constructor(private __src: string, private __alt: string, key?: string) {
        super(key);
    }

    createDOM() {
        return document.createElement('div');
    }

    decorate() {
        return <img src={this.__src} alt={this.__alt} className="max-w-full rounded" />;
    }

    exportJSON(): SerializedLexicalNode {
        return { type: 'image', src: this.__src, alt: this.__alt, version: 1 };
    }
}

Збереження та завантаження стану

// Збереження
const jsonState = JSON.stringify(editor.getEditorState().toJSON());

// Завантаження
editor.update(() => {
    const state = editor.parseEditorState(savedJsonState);
    editor.setEditorState(state);
});

Термін інтеграції: 2–4 дні для повнофункціонального редактора з панеллю інструментів, користувацькими вузлами та завантаженням медіа.