73 lines
2.2 KiB
TypeScript
73 lines
2.2 KiB
TypeScript
import type { Editor } from '@tiptap/core'
|
|
import { detectTextDirection, inferLangFromUrl } from '@/lib/clip/rtl-content'
|
|
|
|
const RTL_BLOCK_TYPES = new Set([
|
|
'clipArticle',
|
|
'bulletList',
|
|
'orderedList',
|
|
'listItem',
|
|
'heading',
|
|
'paragraph',
|
|
'blockquote',
|
|
])
|
|
|
|
function nodeShouldBeRtl(
|
|
node: { type: { name: string }; textContent: string; attrs: { dir?: string | null } },
|
|
urlIsRtl: boolean,
|
|
): boolean {
|
|
if (node.attrs.dir === 'rtl') return false
|
|
if (urlIsRtl) return true
|
|
const text = node.textContent || ''
|
|
if (!text.trim()) return false
|
|
return detectTextDirection(text) === 'rtl'
|
|
}
|
|
|
|
/**
|
|
* Applique dir="rtl" explicitement sur les nœuds TipTap (titres, listes, paragraphes).
|
|
* Basé sur la doc TipTap setTextDirection — évite textDirection:'auto' (bug listes #7338).
|
|
* @see https://tiptap.dev/docs/editor/api/commands/nodes-and-marks/set-text-direction
|
|
* @see https://github.com/ueberdosis/tiptap/issues/7338
|
|
*/
|
|
export function applyClipRtlDirection(
|
|
editor: Editor,
|
|
options?: { sourceUrl?: string | null },
|
|
): boolean {
|
|
if (!editor || editor.isDestroyed) return false
|
|
|
|
const urlIsRtl = Boolean(options?.sourceUrl && inferLangFromUrl(options.sourceUrl))
|
|
const ranges: Array<{ from: number; to: number }> = []
|
|
|
|
editor.state.doc.descendants((node, pos) => {
|
|
if (node.type.name === 'clipArticle') {
|
|
ranges.push({ from: pos, to: pos + node.nodeSize })
|
|
}
|
|
})
|
|
|
|
if (ranges.length === 0) {
|
|
editor.state.doc.descendants((node, pos) => {
|
|
if (node.isText || !RTL_BLOCK_TYPES.has(node.type.name)) return
|
|
if (!nodeShouldBeRtl(node, urlIsRtl)) return
|
|
ranges.push({ from: pos, to: pos + node.nodeSize })
|
|
})
|
|
}
|
|
|
|
if (ranges.length === 0) return false
|
|
|
|
return editor
|
|
.chain()
|
|
.command(({ tr, state, dispatch }) => {
|
|
let changed = false
|
|
for (const { from, to } of ranges) {
|
|
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
if (node.isText || node.attrs.dir === 'rtl') return
|
|
if (!RTL_BLOCK_TYPES.has(node.type.name)) return
|
|
tr.setNodeMarkup(pos, undefined, { ...node.attrs, dir: 'rtl' })
|
|
changed = true
|
|
})
|
|
}
|
|
if (dispatch && changed) dispatch(tr)
|
|
return changed
|
|
})
|
|
.run()
|
|
}
|