Tune & Inline menu

Les composants d'interaction — Tune, Inline_menu, dom.js, utils.js

Tune — contrôleur du bloc

Tune est un composant autonome injecté dans chaque bloc. Il génère deux éléments DOM : les boutons + et (rdac-tools), et le dropdown de contrôle (rdac-tune-dd).

Tune ne fait rien par lui-même. Il expose des callbacks que Redac._mount_tune() définit après la création du bloc. Cette séparation permet à Tune d'être complètement découplé de Redac.

Callbacks
CallbackDéclenché parCe que Redac fait
on_addClic +_add_block(block.id) — crée un bloc après
on_upClic ↑ MonterDéplace le DOM + blocks.up() + _refresh_states()
on_downClic ↓ DescendreDéplace le DOM + blocks.down() + _refresh_states()
on_deleteClic ✕ SupprimerRetire du DOM + blocks.rm() + _refresh_states()
on_apply_attrsClic AppliquerApplique data-custom-id et classes sur le wrapper
on_openOuverture du dropdownRetourne { id, classes } pour pré-remplir les inputs
Méthodes
MéthodeDescription
render()Construit les deux éléments DOM. Retourne { tools, dropdown }
update_state(is_first, is_last, is_only)Désactive les boutons ↑/↓/✕ selon la position du bloc dans la liste
close()Ferme le dropdown
get is_open()État actuel du dropdown
Pourquoi deux éléments séparés ? render() retourne { tools, dropdown } et non un seul wrapper. Cela permet à Paragraph d'insérer les deux au bon endroit dans sa structure flex (tools en flex item gauche, dropdown en position:absolute). Un seul wrapper rendrait le layout plus complexe.

Inline_menu — formatage de sélection

Barre d'outils flottante qui apparaît quand l'utilisateur sélectionne du texte dans une zone .rdac-content. Se positionne au-dessus de la sélection.

Fonctionnement
  1. Le wrapper est inséré une seule fois dans document.body au constructor.
  2. Au mouseup, Redac vérifie si la sélection est dans un .rdac-content.
  3. Si oui, inline_menu.render(selection, selected_txt) positionne le menu et construit les boutons.
  4. Au clic en dehors ou à selectstart, close() masque le menu.
Positionnement
const rect = selection.getRangeAt(0).getBoundingClientRect()
left      = scrollX + rect.left + rect.width / 2  // centré sur la sélection
top       = scrollY + rect.top                     // bord haut de la sélection
transform = translate(-50%, calc(-100% - 6px))     // au-dessus
Contrat des inline tools

Chaque outil (gras, italique, lien…) doit implémenter ce contrat :

MéthodeDescription
get name()Identifiant unique : 'bold', 'italic', 'link'
render(selection, selected_txt) Retourne le bouton DOM. Appelé à chaque ouverture du menu. L'outil stocke la sélection ici pour la relire dans action(). Le bouton doit avoir dataset.redacItName = this.name.
action(event) Applique le formatage sur la sélection stockée dans render(). Délégation : un seul listener sur le wrapper lit btn.dataset.redacItName pour trouver le bon outil.
Limitation connue : la sélection est stockée dans render() (mouseup) et relue dans action() (click). Certains navigateurs effacent la sélection entre les deux événements. Si bold/italic ne s'applique pas, c'est cette raison. Solution future : sauvegarder le Range et le restaurer avant d'appliquer.

dom.js — helpers DOM

Deux méthodes statiques utilisées partout dans le code.

MéthodeExempleRetourne
Dom.makel(tag, classes) Dom.makel('div', ['rdac-block', 'mt-2']) <div class="rdac-block mt-2">
Dom.make_icon(class_names) Dom.make_icon(['bi-type-bold']) <i class="bi bi-type-bold">
make_icon prend un tableau et ajoute automatiquement bi comme première classe. Ne pas passer de strings séparées — c'est une erreur qui a déjà causé un bug ('bi','bi-trash3' au lieu de ['bi-trash3']).

utils.js — utilitaires

MéthodeDescription
Utils.rdm_id(length = 10) Génère un identifiant aléatoire de la longueur spécifiée. Caractères : a-z, 0-9, -. Utilisé par Block pour créer l'id de chaque bloc. Exemple : "k7x2mq4pnb"