Logo Draft.js

Draft.js

Un framework robuste et flexible pour la création d'éditeurs de texte riches dans vos applications React.

Pour les non-initiés

Qu'est-ce que Draft.js ?

Imaginez que vous souhaitiez créer un éditeur de texte en ligne, comme celui de Medium, Google Docs ou WordPress. Vous auriez besoin de gérer le formatage (gras, italique, listes), les liens, les images et bien d'autres fonctionnalités. C'est un défi technique considérable.

Draft.js est un framework créé par Facebook qui permet aux développeurs de construire des éditeurs de texte riches dans les applications web. C'est comme une boîte à outils spécialisée qui fournit une base solide pour créer des expériences d'édition de texte sophistiquées et personnalisables.

Pourquoi Draft.js est-il important ?

Puissance et flexibilité

Il permet de construire des expériences d'édition allant des simples commentaires aux outils de création de contenu complets.

Personnalisation

Contrairement aux éditeurs WYSIWYG prêts à l'emploi, Draft.js peut être adapté à n'importe quel besoin ou style de design.

En résumé, Draft.js permet aux entreprises de créer des expériences d'édition de texte sur mesure pour leurs applications web, qu'il s'agisse d'un simple outil de commentaires, d'un CMS complet, ou de toute autre interface où les utilisateurs doivent saisir et formater du texte.

Pour les développeurs

Fonctionnement technique

Draft.js est un framework d'édition de texte riche pour Icône ReactReact qui utilise un modèle d'état immuable pour gérer le contenu de l'éditeur. Il est construit autour du concept d'EditorState, qui encapsule tout l'état de l'éditeur, y compris le contenu, les sélections et l'historique des opérations.

Les concepts fondamentaux

Éditeur de base

Créer un éditeur Draft.js de base est simple. Voici comment mettre en place un éditeur simple avec Icône ReactReact.

BasicEditor.jsx
import React, { useState } from 'react'; import { Editor, EditorState } from 'draft-js'; import 'draft-js/dist/Draft.css'; function MyEditor() { const [editorState, setEditorState] = useState( EditorState.createEmpty() ); const onChange = (newEditorState) => { setEditorState(newEditorState); }; return ( <div className="editor-container"> <Editor editorState={editorState} onChange={onChange} placeholder="Commencez à écrire..." /> </div> ); } export default MyEditor;

Rich Text Editor

Draft.js brille vraiment lorsqu'il s'agit de créer des éditeurs de texte riche avec des fonctionnalités de formatage. Voici un exemple plus complet qui inclut des contrôles pour le formatage du texte et des types de blocs.

RichTextEditor.jsx
import React, { useState } from 'react'; import { Editor, EditorState, RichUtils, convertToRaw, convertFromRaw } from 'draft-js'; import 'draft-js/dist/Draft.css'; function RichTextEditor() { const [editorState, setEditorState] = useState( EditorState.createEmpty() ); const onChange = (newEditorState) => { setEditorState(newEditorState); }; const handleKeyCommand = (command, editorState) => { const newState = RichUtils.handleKeyCommand(editorState, command); if (newState) { onChange(newState); return 'handled'; } return 'not-handled'; }; const toggleBlockType = (blockType) => { onChange(RichUtils.toggleBlockType(editorState, blockType)); }; const toggleInlineStyle = (inlineStyle) => { onChange(RichUtils.toggleInlineStyle(editorState, inlineStyle)); }; // Boutons pour le formatage const StyleButton = ({ onToggle, style, active, label }) => { return ( <button className={`style-button ${active ? 'active' : ''}`} onMouseDown={(e) => { e.preventDefault(); onToggle(style); }} > {label} </button> ); }; // Styles de texte disponibles const INLINE_STYLES = [ { label: 'Gras', style: 'BOLD' }, { label: 'Italique', style: 'ITALIC' }, { label: 'Souligné', style: 'UNDERLINE' }, { label: 'Monospace', style: 'CODE' } ]; // Types de blocs disponibles const BLOCK_TYPES = [ { label: 'H1', style: 'header-one' }, { label: 'H2', style: 'header-two' }, { label: 'H3', style: 'header-three' }, { label: 'Citation', style: 'blockquote' }, { label: 'Liste à puces', style: 'unordered-list-item' }, { label: 'Liste numérotée', style: 'ordered-list-item' }, { label: 'Code Block', style: 'code-block' } ]; // Créer les boutons pour les styles inline const InlineStyleControls = () => { const currentStyle = editorState.getCurrentInlineStyle(); return ( <div className="style-controls"> {INLINE_STYLES.map((type) => ( <StyleButton key={type.style} active={currentStyle.has(type.style)} label={type.label} onToggle={toggleInlineStyle} style={type.style} /> ))} </div> ); }; // Créer les boutons pour les types de blocs const BlockStyleControls = () => { const selection = editorState.getSelection(); const blockType = editorState .getCurrentContent() .getBlockForKey(selection.getStartKey()) .getType(); return ( <div className="block-style-controls"> {BLOCK_TYPES.map((type) => ( <StyleButton key={type.style} active={type.style === blockType} label={type.label} onToggle={toggleBlockType} style={type.style} /> ))} </div> ); }; // Sauvegarde du contenu sous forme JSON const saveContent = () => { const contentState = editorState.getCurrentContent(); const rawContent = convertToRaw(contentState); const savedData = JSON.stringify(rawContent); localStorage.setItem('draftEditorContent', savedData); console.log('Contenu sauvegardé:', savedData); }; // Chargement du contenu const loadContent = () => { const savedData = localStorage.getItem('draftEditorContent'); if (savedData) { const rawContent = JSON.parse(savedData); const contentState = convertFromRaw(rawContent); const newEditorState = EditorState.createWithContent(contentState); onChange(newEditorState); } }; return ( <div className="rich-editor-container"> <div className="toolbar"> <InlineStyleControls /> <BlockStyleControls /> <button onClick={saveContent}>Sauvegarder</button> <button onClick={loadContent}>Charger</button> </div> <div className="editor"> <Editor editorState={editorState} onChange={onChange} handleKeyCommand={handleKeyCommand} placeholder="Commencez à écrire du texte riche..." /> </div> </div> ); } export default RichTextEditor;

Entités personnalisées

Draft.js permet de définir des entités personnalisées comme des liens, des mentions, ou d'autres éléments interactifs dans le texte. Voici un exemple d'implémentation d'entités de lien.

EntityEditor.jsx
import React, { useState } from 'react'; import { Editor, EditorState, RichUtils, CompositeDecorator, Entity, Modifier, ContentState } from 'draft-js'; import 'draft-js/dist/Draft.css'; // Définition du composant pour afficher les liens const Link = (props) => { const { url } = props.contentState.getEntity(props.entityKey).getData(); return ( <a href={url} className="editor-link" title={url}> {props.children} </a> ); }; // Fonction pour trouver les entités de lien const findLinkEntities = (contentBlock, callback, contentState) => { contentBlock.findEntityRanges( (character) => { const entityKey = character.getEntity(); return ( entityKey !== null && contentState.getEntity(entityKey).getType() === 'LINK' ); }, callback ); }; function EntityEditor() { // Création d'un décorateur pour les liens const decorator = new CompositeDecorator([ { strategy: findLinkEntities, component: Link, }, ]); const [editorState, setEditorState] = useState( EditorState.createEmpty(decorator) ); const [urlValue, setUrlValue] = useState(''); const [showURLInput, setShowURLInput] = useState(false); const onChange = (newEditorState) => { setEditorState(newEditorState); }; // Fonction pour ajouter un lien const promptForLink = (e) => { e.preventDefault(); const selection = editorState.getSelection(); if (!selection.isCollapsed()) { setShowURLInput(true); } }; const confirmLink = (e) => { e.preventDefault(); // Création de l'entité lien const contentState = editorState.getCurrentContent(); const contentStateWithEntity = contentState.createEntity( 'LINK', 'MUTABLE', { url: urlValue } ); const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); // Application de l'entité au texte sélectionné let nextEditorState = EditorState.set(editorState, { currentContent: contentStateWithEntity }); nextEditorState = RichUtils.toggleLink( nextEditorState, nextEditorState.getSelection(), entityKey ); // Mise à jour de l'état setEditorState(nextEditorState); setShowURLInput(false); setUrlValue(''); }; const onURLChange = (e) => { setUrlValue(e.target.value); }; return ( <div className="entity-editor-container"> <div className="controls"> <button onMouseDown={promptForLink}>Ajouter un lien</button> </div> {showURLInput && ( <div className="url-input"> <input onChange={onURLChange} value={urlValue} placeholder="http://example.com/" /> <button onMouseDown={confirmLink}>Confirmer</button> </div> )} <div className="editor"> <Editor editorState={editorState} onChange={onChange} placeholder="Sélectionnez du texte et ajoutez un lien..." /> </div> </div> ); } export default EntityEditor;

Concepts clés de Draft.js

  • EditorState - Objet immuable qui représente l'état complet de l'éditeur
  • ContentState - Représentation du contenu de l'éditeur (texte, blocs, entités)
  • SelectionState - Position du curseur et sélection de texte
  • ContentBlock - Unité de base du contenu (paragraphe, titre, liste, etc.)
  • Decorator - Système pour personnaliser l'affichage de portions spécifiques du texte
  • Entity - Métadonnées associées à des portions de texte (liens, mentions, etc.)
  • Modifier - Utilitaire pour appliquer des modifications au contenu

Forces et limitations

Forces

  • Architecture immuable robuste
  • Hautement personnalisable
  • Gestion avancée des sélections
  • Prise en charge des raccourcis clavier
  • Développé et utilisé par Facebook

Limitations

  • Courbe d'apprentissage abrupte
  • Documentation parfois incomplète
  • Complexité pour des fonctionnalités avancées
  • Mise en page limitée (pas de tables natives)
  • Pas de support natif pour le markdown

Megadraft : Une extension de Draft.js

Megadraft est une extension de Draft.js qui propose une expérience plus complète avec des plugins prêts à l'emploi. Créé par Globo.com, il simplifie l'utilisation de Draft.js en fournissant :

  • Une barre d'outils prête à l'emploi
  • Un système de plugins extensible
  • Des plugins intégrés pour les images, vidéos, et plus
  • Une meilleure expérience pour la gestion des blocs
  • Un style par défaut moderne et personnalisable

Megadraft est particulièrement utile lorsque vous avez besoin de créer rapidement un éditeur riche sans passer par toute la complexité de la configuration manuelle de Draft.js.

Applications concrètes

Cas d'usage

Plateformes de blogging

Idéal pour créer des éditeurs WYSIWYG personnalisés pour les CMS et les plateformes de blogging comme Medium ou WordPress.

Systèmes de commentaires

Permet aux utilisateurs de laisser des commentaires formatés avec du texte riche, des liens et des mentions d'utilisateurs.

Outils de prise de notes

Excellent pour créer des applications de prise de notes comme Notion ou Evernote, avec structure flexible et formatage avancé.

Outils collaboratifs

Peut être intégré dans des systèmes d'édition collaborative en temps réel en utilisant des bibliothèques comme Yjs ou ShareDB.

Sites qui utilisent Draft.js

De nombreuses entreprises et plateformes de premier plan font confiance à Draft.js pour leurs éditeurs de texte :

Facebook
Twitter
Medium
Atlassian
Slab
Notion
Globo.com
Dropbox Paper