# BlockEditor
Éditeur WYSIWYG par blocs, style Notion. Construit en interne (pas de
ProseMirror/Lexical/Tiptap). Un `contentEditable` par bloc, pas un seul
contentEditable global — c'est plus robuste et plus simple à étendre.
## Utilisation
```jsx
import { BlockEditor } from '@zen/core/shared/components';
const [blocks, setBlocks] = useState([]);
```
`value` est un **tableau de blocs JSON**. C'est la source de vérité.
`MarkdownEditor` reste disponible en parallèle pour les usages markdown.
## Format des blocs (Phase 1)
Chaque bloc a un `id` (UUID) et un `type`. Selon le type :
| type | champs | description |
|----------------|---------------|----------------------------|
| `paragraph` | `content` | texte brut |
| `heading_1..6` | `content` | titre niveau 1 à 6 |
| `bullet_item` | `content` | élément de liste à puces |
| `numbered_item`| `content` | élément de liste numérotée |
| `quote` | `content` | citation |
| `code` | `content` | bloc de code (monospace) |
| `divider` | — | séparateur horizontal |
Phase 2 ajoutera : `checklist`, `image`, et le format de `content` passera de
`string` à `InlineNode[]` pour supporter le formatting inline (gras, italique,
couleur, lien). Phase 3 : `table`.
## Props
```jsx
void
label, error, placeholder, disabled, className
enabledBlocks={[...]} // optionnel : restreindre les types disponibles
/>
```
## Interactions clavier
- `/` → ouvre le menu de commandes (filtrable au clavier, ↑ ↓ Entrée pour valider, Échap pour fermer)
- `# ` → titre 1, `## ` → 2, …, `###### ` → 6
- `- ` → liste à puces
- `1. ` → liste numérotée
- `> ` → citation
- ` ``` ` → bloc de code
- `---` → séparateur
- `Backspace` au début d'un bloc typé → repasse en paragraphe ; au début d'un paragraphe, fusionne avec le bloc précédent (uniquement si la sélection est repliée — sinon le navigateur supprime le texte sélectionné, ex. après `Ctrl+A`)
- `Entrée` sur un item de liste vide → sort de la liste
- `Ctrl/Cmd + Z` / `Ctrl/Cmd + Shift + Z` → undo / redo
## Drag and drop
Chaque bloc affiche au survol :
- une poignée `+` pour insérer un bloc en dessous (ouvre le slash menu)
- une poignée `⋮⋮` pour glisser-déposer (réordonner)
Le drag-and-drop utilise l'API HTML5 native, pas de dépendance externe.
## Étendre — enregistrer un bloc custom
```js
import { registerBlock, newBlockId } from '@zen/core/shared/components/BlockEditor';
registerBlock({
type: 'kpi',
label: 'KPI',
icon: '📊',
keywords: ['kpi', 'metric', 'stat'],
isText: false,
create: () => ({ id: newBlockId(), type: 'kpi', value: 0 }),
Component: ({ block, onChange, disabled }) => (
onChange({ value: Number(e.target.value) })}
/>
),
});
```
Le nouveau type apparaît automatiquement dans le slash menu de tout
`` rendu après l'enregistrement. Pour les blocs **texte**, on
fournit à la place `isText: true`, `textTag`, `textClassName`, et optionnellement
`renderPrefix({ block, index, numberedIndex })` pour un préfixe (puce, numéro).
## Architecture interne
```
BlockEditor.client.js orchestrateur : value/onChange, undo, slash menu, drag-drop
Block.client.js wrapper d'un bloc : handles, contentEditable, paste sanitize
SlashMenu.client.js menu flottant filtrable
blockRegistry.js map type → définition, API publique d'extension
blockTypes/ un fichier par type built-in
utils/ids.js UUID pour les blocs
utils/caret.js gestion du caret dans un contentEditable
```
## Limitations connues (Phase 1)
- Inline formatting (gras, italique, couleur, lien) **pas encore** : tout est texte brut. Phase 2.
- Pas d'imbrication de listes.
- Paste : seul le texte brut est conservé (sanitize HTML).
- Tables : Phase 3.