fix(admin): resolve Tiptap editor bugs, dynamic RTL/LTR layout, sticky TOC, and equalize Bidi typography
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
import type { EditorState } from '@tiptap/pm/state'
|
||||
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||
import { LATIN_CLASS, RTL_CLASS, findScriptRuns } from '../../utils/bidiText'
|
||||
|
||||
/** نام مارکهای قدیمی که خودشان اندازه/جهت متن را تعیین میکنند */
|
||||
const LEGACY_MARKS = new Set(['englishText', 'smallSize'])
|
||||
|
||||
type BidiState = { dir: 'rtl' | 'ltr' }
|
||||
|
||||
/** کلید پلاگین — جهت متن با متای همین کلید بهروز میشود */
|
||||
export const bidiPluginKey = new PluginKey<BidiState>('bidiTypography')
|
||||
|
||||
/**
|
||||
* تزئین دوزبانه متن ویرایشگر — اجرای لاتین (و در متن انگلیسی، اجرای فارسی) با یک
|
||||
* span جهتدار نشانهگذاری میشود تا ویرایشگر هم دقیقاً همان اندازه «۲ پیکسل کوچکتر»
|
||||
* خروجی سایت را نشان دهد. تزئینها سند را تغییر نمیدهند و ذخیره نمیشوند.
|
||||
*/
|
||||
export const BidiTypography = Extension.create({
|
||||
name: 'bidiTypography',
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
new Plugin<BidiState>({
|
||||
key: bidiPluginKey,
|
||||
state: {
|
||||
init: () => ({ dir: 'rtl' }),
|
||||
apply: (tr, value) => {
|
||||
const dir = tr.getMeta(bidiPluginKey) as BidiState['dir'] | undefined
|
||||
return dir && dir !== value.dir ? { dir } : value
|
||||
},
|
||||
},
|
||||
props: {
|
||||
decorations: (state) =>
|
||||
buildDecorations(state, bidiPluginKey.getState(state)?.dir ?? 'rtl'),
|
||||
},
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
||||
function buildDecorations(state: EditorState, dir: 'rtl' | 'ltr'): DecorationSet {
|
||||
const className = dir === 'rtl' ? LATIN_CLASS : RTL_CLASS
|
||||
const runDir = dir === 'rtl' ? 'ltr' : 'rtl'
|
||||
const decorations: Decoration[] = []
|
||||
const { doc, schema } = state
|
||||
|
||||
doc.descendants((node, pos, parent) => {
|
||||
if (node.type.name === 'codeBlock' || parent?.type.name === 'codeBlock') return false
|
||||
if (!node.isText || !node.text) return
|
||||
if (node.marks.some((mark) => mark.type === schema.marks.code || LEGACY_MARKS.has(mark.type.name))) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const run of findScriptRuns(node.text, dir)) {
|
||||
decorations.push(
|
||||
Decoration.inline(pos + run.from, pos + run.to, { class: className, dir: runDir }),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return DecorationSet.create(doc, decorations)
|
||||
}
|
||||
Reference in New Issue
Block a user