TableBubbleMenu — Menú flotante de tabla en el Cuaderno
Descripción
TableBubbleMenu es un componente React que muestra un menú flotante contextual (bubble menu) encima de cualquier tabla cuando el cursor del editor está posicionado dentro de ella. Sigue el patrón “esquina superior” popularizado por Notion, Confluence y Coda.
El componente se monta directamente en NoteEditor.tsx y delega el posicionamiento al BubbleMenu de @tiptap/react/menus, usando @floating-ui/dom internamente.
Ubicación en el código
| Artefacto | Ruta |
|---|---|
| Componente | src/components/notebook/TableBubbleMenu.tsx |
| Estilos | src/styles/tools.css (clases .cuaderno-table-menu*) |
| Punto de montaje | src/components/notebook/NoteEditor.tsx |
Props
| Prop | Tipo | Descripción |
|---|---|---|
editor | Editor | null | Instancia del editor TipTap. Si es null el componente no renderiza nada. |
Comportamiento
- Trigger:
shouldShowevalúaeditor.isActive('table'). Solo aparece cuando el cursor está dentro de un nodotablede TipTap/ProseMirror. - Posicionamiento:
placement: 'top-start'con offset de 8 px (flotante encima de la tabla, alineado a la izquierda). pluginKey:"tableBubbleMenu"— evita conflictos con otros BubbleMenus presentes en el editor (ej. formateo de texto).
Botones expuestos
| Label | Comando TipTap | Acción |
|---|---|---|
+ fila ↑ | addRowBefore() | Inserta fila encima de la celda actual |
+ fila ↓ | addRowAfter() | Inserta fila debajo |
+ col ← | addColumnBefore() | Inserta columna a la izquierda |
+ col → | addColumnAfter() | Inserta columna a la derecha |
− fila | deleteRow() | Elimina fila actual |
− col | deleteColumn() | Elimina columna actual |
⇈ header | toggleHeaderRow() | Alterna la fila de cabecera <th> |
⊟ | mergeOrSplit() | Fusiona celdas seleccionadas o divide celda fusionada |
✕ Eliminar tabla | deleteTable() | Elimina la tabla entera (con confirm() de seguridad) |
Patrón onMouseDown + preventDefault
Todos los botones usan onMouseDown={(e) => e.preventDefault()} para evitar que el editor pierda el foco antes de ejecutar el comando. Sin esto, el cursor se mueve y isActive('table') devuelve false en el momento del onClick.
Estilos CSS relevantes
/* Contenedor */
.cuaderno-table-menu { display: flex; gap: 2px; padding: 4px; background: var(--bg-raised); border-radius: 8px; }
/* Botones */
.cuaderno-table-menu button { padding: 4px 8px; border-radius: 5px; color: var(--fg-muted); }
/* Separadores visuales */
.cuaderno-table-menu-sep { width: 1px; height: 18px; background: var(--border); }
/* Botón destructivo */
.cuaderno-table-menu-danger { color: var(--danger) !important; }
Dependencias clave
@tiptap/extension-bubble-menuv3.23.1 (bump desde 3.22.5 en este commit — la dependencia pasa deoptionala directa en el lockfile).@floating-ui/dom(transitiva, gestionada por TipTap).- Extensiones TipTap de tablas:
@tiptap/extension-table,@tiptap/extension-table-row,@tiptap/extension-table-cell,@tiptap/extension-table-header.
Notas de mantenimiento
- Si se añaden más BubbleMenus al editor (ej. para imágenes o código), cada uno debe tener un
pluginKeyúnico. - La clase
.cuaderno-table-menu-dangerusa!importantpara superar la especificidad del selector de botones base — revisar si la arquitectura CSS del cuaderno crece. - El
confirm()nativo de bloqueo síncrono puede reemplazarse por un modal personalizado si se requiere un UX más refinado.
Véase también
- [[feature—cuaderno—editor-rich-text]]
- [[workspace—que-es-workspace]]
- [[crearack—conceptos—interfaz-general]]