61 lines
2.2 KiB
TypeScript
61 lines
2.2 KiB
TypeScript
import { Extension } from '@tiptap/core'
|
|
import { Plugin, PluginKey, NodeSelection } from '@tiptap/pm/state'
|
|
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
|
|
|
/**
|
|
* Extension ProseMirror/TipTap qui ajoute la classe CSS 'block-selected'
|
|
* aux blocs de premier niveau inclus ou traversés par la sélection active.
|
|
*/
|
|
export const BlockSelectionExtension = Extension.create({
|
|
name: 'blockSelection',
|
|
|
|
addProseMirrorPlugins() {
|
|
return [
|
|
new Plugin({
|
|
key: new PluginKey('blockSelection'),
|
|
props: {
|
|
decorations(state) {
|
|
const { from, to, empty } = state.selection
|
|
if (empty) return DecorationSet.empty
|
|
|
|
const decorations: Decoration[] = []
|
|
|
|
// Parcourir uniquement les nœuds de premier niveau (enfants directs de doc)
|
|
state.doc.forEach((node, offset) => {
|
|
const start = offset
|
|
const end = offset + node.nodeSize
|
|
|
|
// Vérifier si la plage de sélection [from, to] intersecte le bloc [start, end]
|
|
const intersects = Math.max(start, from) < Math.min(end, to)
|
|
if (intersects) {
|
|
// Il faut s'assurer que c'est un nœud de bloc pour appliquer Decoration.node
|
|
if (node.isBlock) {
|
|
try {
|
|
decorations.push(
|
|
Decoration.node(start, end, {
|
|
class: 'block-selected',
|
|
})
|
|
)
|
|
} catch (e) {
|
|
// Fallback en cas d'erreur sur des structures de nœuds particulières
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
const isNodeSel = state.selection instanceof NodeSelection
|
|
|
|
// Si la sélection s'étend sur plusieurs blocs ou si c'est une NodeSelection explicite,
|
|
// on renvoie l'ensemble des décorations pour coloration visuelle.
|
|
if (decorations.length > 1 || (decorations.length === 1 && isNodeSel)) {
|
|
return DecorationSet.create(state.doc, decorations)
|
|
}
|
|
|
|
return DecorationSet.empty
|
|
},
|
|
},
|
|
}),
|
|
]
|
|
},
|
|
})
|