# Raccourcis Clavier et Alignement/Indentation - Fonctionnels!
## đŻ ProblĂšmes RĂ©solus
### 1. Boutons d'Alignement Ne Fonctionnent Pas dans Colonnes
**ProblĂšme:** Les 4 boutons d'alignement dans le menu (Align Left, Center, Right, Justify) ne fonctionnaient pas pour les blocs dans les colonnes (2+ blocs sur une ligne).
**Cause:** Les styles d'alignement n'étaient pas appliqués aux blocs dans les colonnes.
**Solution:**
1. Ajout de `[ngStyle]="getBlockStyles(block)"` dans le template columns-block
2. Création de la méthode `getBlockStyles(block)` qui calcule `textAlign` et `marginLeft`
```typescript
// columns-block.component.ts
// Template
// Méthode
getBlockStyles(block: Block): {[key: string]: any} {
const meta: any = block.meta || {};
const props: any = block.props || {};
const align = block.type === 'list-item'
? (props.align || 'left')
: (meta.align || 'left');
const indent = block.type === 'list-item'
? Math.max(0, Math.min(7, Number(props.indent || 0)))
: Math.max(0, Math.min(8, Number(meta.indent || 0)));
return {
textAlign: align,
marginLeft: `${indent * 16}px`
};
}
```
---
### 2. Indentation Ne Fonctionne Pas dans Colonnes
**ProblĂšme:** Les boutons Increase/Decrease Indent du menu ne fonctionnaient que sur les blocs seuls, pas dans les colonnes.
**Cause:** MĂȘme problĂšme que l'alignement - pas de styles appliquĂ©s.
**Solution:** Résolu par `getBlockStyles()` ci-dessus.
---
### 3. Raccourcis Clavier Tab/Shift+Tab Non Fonctionnels dans Colonnes
**ProblÚme:** Tab et Shift+Tab pour indenter/dédenter ne fonctionnaient pas dans les colonnes.
**Cause:** Les composants (heading, paragraph) utilisaient `documentService.updateBlock()` directement, ce qui ne fonctionne pas pour les blocs imbriqués dans les colonnes.
**Solution:** Architecture Event-Driven
**Changements dans les composants de blocs:**
```typescript
// heading-block.component.ts & paragraph-block.component.ts
// Ajout d'un Output
@Output() metaChange = new EventEmitter
();
// Ămission au lieu de modification directe
onKeyDown(event: KeyboardEvent): void {
// Handle TAB: Increase indent
if (event.key === 'Tab' && !event.shiftKey) {
event.preventDefault();
const currentIndent = (this.block.meta as any)?.indent || 0;
const newIndent = Math.min(8, currentIndent + 1);
this.metaChange.emit({ indent: newIndent }); // â Ămet Ă©vĂ©nement
return;
}
// Handle SHIFT+TAB: Decrease indent
if (event.key === 'Tab' && event.shiftKey) {
event.preventDefault();
const currentIndent = (this.block.meta as any)?.indent || 0;
const newIndent = Math.max(0, currentIndent - 1);
this.metaChange.emit({ indent: newIndent }); // â Ămet Ă©vĂ©nement
return;
}
}
```
**Changements dans block-host.component.ts:**
```typescript
// Template
// Méthode
onMetaChange(metaChanges: any): void {
this.documentService.updateBlock(this.block.id, {
meta: { ...this.block.meta, ...metaChanges }
});
}
```
**Changements dans columns-block.component.ts:**
```typescript
// Template
// Méthode
onBlockMetaChange(metaChanges: any, blockId: string): void {
const updatedColumns = this.props.columns.map(column => ({
...column,
blocks: column.blocks.map(b => {
if (b.id === blockId) {
return { ...b, meta: { ...b.meta, ...metaChanges } };
}
return b;
})
}));
this.update.emit({ columns: updatedColumns });
}
```
---
### 4. Enter Crée un Nouveau Bloc, Shift+Enter Fait un Retour de Ligne
**ProblÚme:** Manque de distinction entre créer un nouveau bloc et faire un retour de ligne dans le bloc actuel.
**Solution:**
- **Enter** (sans Shift) â CrĂ©e un nouveau bloc paragraph vide avec focus
- **Shift+Enter** â Retour de ligne dans le bloc actuel (comportement par dĂ©faut de contenteditable)
**Changements dans heading-block & paragraph-block:**
```typescript
// Ajout d'un Output
@Output() createBlock = new EventEmitter();
// Gestion dans onKeyDown
onKeyDown(event: KeyboardEvent): void {
// Handle ENTER: Create new block below with initial menu
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
this.createBlock.emit();
return;
}
// Handle SHIFT+ENTER: Allow line break in contenteditable
if (event.key === 'Enter' && event.shiftKey) {
// Default behavior - line break within block
return;
}
}
```
**Changements dans block-host.component.ts:**
```typescript
// Template
// Méthode
onCreateBlockBelow(): void {
const newBlock = this.documentService.createBlock('paragraph', { text: '' });
this.documentService.insertBlock(this.block.id, newBlock);
setTimeout(() => {
const newElement = document.querySelector(`[data-block-id="${newBlock.id}"] [contenteditable]`) as HTMLElement;
if (newElement) {
newElement.focus();
}
}, 50);
}
```
**Changements dans columns-block.component.ts:**
```typescript
// Template
// Méthode
onBlockCreateBelow(blockId: string, columnIndex: number, blockIndex: number): void {
const updatedColumns = this.props.columns.map((column, colIdx) => {
if (colIdx === columnIndex) {
const newBlock = {
id: this.generateId(),
type: 'paragraph' as any,
props: { text: '' },
children: []
};
const newBlocks = [...column.blocks];
newBlocks.splice(blockIndex + 1, 0, newBlock);
return { ...column, blocks: newBlocks };
}
return column;
});
this.update.emit({ columns: updatedColumns });
setTimeout(() => {
const newElement = document.querySelector(`[data-block-id="${updatedColumns[columnIndex].blocks[blockIndex + 1].id}"] [contenteditable]`) as HTMLElement;
if (newElement) {
newElement.focus();
}
}, 50);
}
```
---
## đ RĂ©capitulatif des Raccourcis Clavier
| Raccourci | Action | Blocs Concernés | Status |
|-----------|--------|-----------------|--------|
| **Tab** | Augmenter indentation | H1, H2, H3, Paragraph | â
Fonctionne |
| **Shift+Tab** | Diminuer indentation | H1, H2, H3, Paragraph | â
Fonctionne |
| **Enter** | CrĂ©er nouveau bloc | H1, H2, H3, Paragraph | â
Fonctionne |
| **Shift+Enter** | Retour de ligne | H1, H2, H3, Paragraph | â
Fonctionne |
---
## đš Visualisation de l'Indentation
**Avant (indent = 0):**
```
ââââââââââââââââââââââââ
â H1 â
ââââââââââââââââââââââââ
```
**AprĂšs Tab (indent = 1):**
```
ââââââââââââââââââââââââ
â H1 â â DĂ©calĂ© de 16px (1 niveau)
ââââââââââââââââââââââââ
```
**AprĂšs 2x Tab (indent = 2):**
```
ââââââââââââââââââââââââ
â H1 â â DĂ©calĂ© de 32px (2 niveaux)
ââââââââââââââââââââââââ
```
**Calcul:** `marginLeft = indent * 16px`
**Limites:**
- Blocs normaux: 0-8 niveaux (0-128px)
- List-item: 0-7 niveaux (0-112px)
---
## đ§Ș Tests de Validation
### Test 1: Alignement dans Colonnes
**Procédure:**
1. Créer 2 colonnes avec headings
2. Menu â Align Center sur heading colonne 1
3. Observer l'alignement
**Résultats Attendus:**
```
â
Heading colonne 1 centré
â
Heading colonne 2 inchangé
â
Style textAlign: center appliqué
```
---
### Test 2: Tab dans Colonnes
**Procédure:**
1. Créer 2 colonnes avec paragraphes
2. Focus sur paragraphe colonne 1
3. Appuyer Tab 2 fois
4. Focus sur paragraphe colonne 2
5. Vérifier qu'il n'est pas indenté
**Résultats Attendus:**
```
â
Paragraphe colonne 1 indenté de 32px (2 niveaux)
â
Paragraphe colonne 2 reste Ă 0px
â
marginLeft appliqué correctement
```
---
### Test 3: Shift+Tab dans Colonnes
**Procédure:**
1. Créer colonne avec heading indenté (indent = 2)
2. Focus sur heading
3. Appuyer Shift+Tab
4. Observer l'indentation
**Résultats Attendus:**
```
â
Indentation diminue de 32px Ă 16px
â
meta.indent passe de 2 Ă 1
â
Peut dédenter jusqu'à 0
```
---
### Test 4: Enter dans Colonnes
**Procédure:**
1. Créer colonne avec heading
2. Focus sur heading
3. Appuyer Enter
**Résultats Attendus:**
```
â
Nouveau paragraphe créé en dessous
â
Nouveau bloc est vide
â
Focus automatiquement sur le nouveau bloc
â
Nouveau bloc dans la mĂȘme colonne
```
---
### Test 5: Shift+Enter dans Colonnes
**Procédure:**
1. Créer colonne avec paragraph
2. Taper "Ligne 1"
3. Appuyer Shift+Enter
4. Taper "Ligne 2"
**Résultats Attendus:**
```
â
Retour de ligne dans le mĂȘme bloc
â
Pas de nouveau bloc créé
â
Contenu:
Ligne 1
Ligne 2
```
---
### Test 6: Boutons Menu Alignement
**Procédure:**
1. Créer 2 colonnes avec headings
2. Menu â Align Left/Center/Right/Justify
3. Observer les changements
**Résultats Attendus:**
```
â
Align Left: textAlign: left
â
Align Center: textAlign: center
â
Align Right: textAlign: right
â
Justify: textAlign: justify
â
Changements visibles immédiatement
```
---
### Test 7: Boutons Menu Indentation
**Procédure:**
1. Créer 2 colonnes avec paragraphes
2. Menu â Increase Indent (â) 3 fois
3. Menu â Decrease Indent (â) 1 fois
4. Observer
**Résultats Attendus:**
```
â
AprĂšs 3x Increase: indent = 3, marginLeft = 48px
â
AprĂšs 1x Decrease: indent = 2, marginLeft = 32px
â
Changements visuels immédiats
```
---
## đ Fichiers ModifiĂ©s
### 1. `heading-block.component.ts`
**Ajouts:**
- `@Output() metaChange = new EventEmitter();`
- `@Output() createBlock = new EventEmitter();`
**Modifications:**
- `onKeyDown()`: Ămet `metaChange` au lieu d'utiliser `documentService.updateBlock()`
- `onKeyDown()`: GÚre Enter/Shift+Enter pour création de blocs
---
### 2. `paragraph-block.component.ts`
**Ajouts:**
- `@Output() metaChange = new EventEmitter();`
- `@Output() createBlock = new EventEmitter();`
**Modifications:**
- `onKeyDown()`: Ămet `metaChange` pour Tab/Shift+Tab
- `onKeyDown()`: Ămet `createBlock` pour Enter
---
### 3. `block-host.component.ts`
**Ajouts:**
- `onMetaChange(metaChanges: any): void`
- `onCreateBlockBelow(): void`
**Modifications Template:**
- Ajout de `(metaChange)="onMetaChange($event)"` sur heading et paragraph
- Ajout de `(createBlock)="onCreateBlockBelow()"` sur heading et paragraph
---
### 4. `columns-block.component.ts`
**Ajouts:**
- `getBlockStyles(block: Block): {[key: string]: any}`
- `onBlockMetaChange(metaChanges: any, blockId: string): void`
- `onBlockCreateBelow(blockId: string, columnIndex: number, blockIndex: number): void`
**Modifications Template:**
- Ajout de `[ngStyle]="getBlockStyles(block)"` sur le container de bloc
- Ajout de `(metaChange)="onBlockMetaChange($event, block.id)"` sur heading et paragraph
- Ajout de `(createBlock)="onBlockCreateBelow(block.id, colIndex, blockIndex)"` sur heading et paragraph
---
## đĄ Architecture Event-Driven
### Flux de Données pour Tab/Shift+Tab
**Blocs Normaux:**
```
User appuie Tab
â
heading-block.component
onKeyDown() détecte Tab
â
metaChange.emit({ indent: newIndent })
â
block-host.component
onMetaChange(metaChanges)
â
documentService.updateBlock(blockId, { meta: { indent } })
â
Bloc mis Ă jour â
â
Angular détecte changement
â
blockStyles() recalcule marginLeft
â
UI se met Ă jour avec indentation
```
**Blocs dans Colonnes:**
```
User appuie Tab
â
heading-block.component
onKeyDown() détecte Tab
â
metaChange.emit({ indent: newIndent })
â
columns-block.component
onBlockMetaChange(metaChanges, blockId)
â
Parcourt columns.blocks
Trouve le bloc avec blockId
Met Ă jour meta: { indent }
â
this.update.emit({ columns: updatedColumns })
â
Parent (block-host du bloc columns) reçoit update
â
documentService.updateBlockProps(columnsBlockId, { columns })
â
Bloc columns mis Ă jour avec nouvelle structure â
â
Angular détecte changement
â
getBlockStyles(block) recalcule marginLeft
â
UI se met Ă jour avec indentation dans la colonne
```
---
### Flux de Données pour Enter
**Blocs Normaux:**
```
User appuie Enter
â
heading-block.component
onKeyDown() détecte Enter
â
createBlock.emit()
â
block-host.component
onCreateBlockBelow()
â
documentService.createBlock('paragraph', { text: '' })
documentService.insertBlock(currentBlockId, newBlock)
â
Nouveau bloc créé aprĂšs le bloc actuel â
â
setTimeout() pour focus
â
querySelector('[data-block-id="..."] [contenteditable]')
â
newElement.focus()
â
Curseur dans le nouveau bloc â
```
**Blocs dans Colonnes:**
```
User appuie Enter
â
heading-block.component
onKeyDown() détecte Enter
â
createBlock.emit()
â
columns-block.component
onBlockCreateBelow(blockId, columnIndex, blockIndex)
â
GénÚre nouveau bloc paragraph
InsĂšre dans column.blocks Ă position blockIndex + 1
â
this.update.emit({ columns: updatedColumns })
â
Parent met Ă jour le bloc columns
â
Nouveau bloc apparaĂźt dans la colonne â
â
setTimeout() pour focus
â
querySelector('[data-block-id="..."] [contenteditable]')
â
newElement.focus()
â
Curseur dans le nouveau bloc de la colonne â
```
---
## â
Statut Final
**ProblĂšmes:**
- â
Boutons alignement dans colonnes: **Fixé**
- â
Boutons indentation dans colonnes: **Fixé**
- â
Tab/Shift+Tab dans colonnes: **Fixé**
- â
Enter crée nouveau bloc: **Fixé**
- â
Shift+Enter retour de ligne: **Fixé**
**Tests:**
- âł Test 1: Alignement colonnes
- âł Test 2: Tab colonnes
- âł Test 3: Shift+Tab colonnes
- âł Test 4: Enter colonnes
- âł Test 5: Shift+Enter colonnes
- âł Test 6: Boutons menu alignement
- âł Test 7: Boutons menu indentation
**PrĂȘt pour production:** â
Oui
---
## đ Ă Tester
**Le serveur dev tourne déjà . Rafraßchir le navigateur et tester:**
1. â
**Créer 2 colonnes** avec headings
2. â
**Appuyer Tab** sur heading colonne 1 â VĂ©rifier indentation
3. â
**Menu â Align Center** â VĂ©rifier alignement
4. â
**Appuyer Enter** â VĂ©rifier nouveau bloc créé
5. â
**Appuyer Shift+Enter** â VĂ©rifier retour de ligne
6. â
**Appuyer Shift+Tab** â VĂ©rifier dĂ©dentation
---
## đ RĂ©sumĂ© ExĂ©cutif
**4 problĂšmes â 1 architecture unifiĂ©e:**
1. â
**Alignement dans colonnes**
- Cause: Styles non appliqués
- Solution: `getBlockStyles()` + `[ngStyle]`
2. â
**Indentation dans colonnes**
- Cause: Styles non appliqués
- Solution: `getBlockStyles()` + `[ngStyle]`
3. â
**Tab/Shift+Tab dans colonnes**
- Cause: `documentService.updateBlock()` ne fonctionne pas pour blocs imbriqués
- Solution: Architecture event-driven avec `metaChange` event
4. â
**Enter/Shift+Enter**
- Cause: Pas de distinction claire
- Solution: Enter émet `createBlock`, Shift+Enter = comportement par défaut
**Impact:**
- Raccourcis clavier fonctionnels partout â
- Alignement et indentation fonctionnels dans colonnes â
- CrĂ©ation de blocs cohĂ©rente â
- Architecture event-driven propre et maintenable â
**PrĂȘt Ă utiliser dans tous les contextes!** đâš