70 lines
1.7 KiB
TypeScript
70 lines
1.7 KiB
TypeScript
import { EditorState } from 'prosemirror-state'
|
|
import { Mark, MarkType } from 'prosemirror-model'
|
|
import objectIncludes from '../utilities/objectIncludes'
|
|
import getMarkType from './getMarkType'
|
|
import { AnyObject } from '../types'
|
|
|
|
export type MarkRange = {
|
|
mark: Mark,
|
|
from: number,
|
|
to: number,
|
|
}
|
|
|
|
export default function isMarkActive(
|
|
state: EditorState,
|
|
typeOrName: MarkType | string | null,
|
|
attributes: AnyObject = {},
|
|
): boolean {
|
|
const { from, to, empty } = state.selection
|
|
const type = typeOrName
|
|
? getMarkType(typeOrName, state.schema)
|
|
: null
|
|
|
|
if (empty) {
|
|
return !!(state.storedMarks || state.selection.$from.marks())
|
|
.filter(mark => {
|
|
if (!type) {
|
|
return true
|
|
}
|
|
|
|
return type.name === mark.type.name
|
|
})
|
|
.find(mark => objectIncludes(mark.attrs, attributes))
|
|
}
|
|
|
|
let selectionRange = 0
|
|
let markRanges: MarkRange[] = []
|
|
|
|
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
if (node.isInline) {
|
|
const relativeFrom = Math.max(from, pos)
|
|
const relativeTo = Math.min(to, pos + node.nodeSize)
|
|
const range = relativeTo - relativeFrom
|
|
|
|
selectionRange += range
|
|
|
|
markRanges = [...markRanges, ...node.marks.map(mark => ({
|
|
mark,
|
|
from: relativeFrom,
|
|
to: relativeTo,
|
|
}))]
|
|
}
|
|
})
|
|
|
|
const range = markRanges
|
|
.filter(markRange => {
|
|
if (!type) {
|
|
return true
|
|
}
|
|
|
|
return type.name === markRange.mark.type.name
|
|
})
|
|
.filter(markRange => objectIncludes(markRange.mark.attrs, attributes))
|
|
.reduce((sum, markRange) => {
|
|
const size = markRange.to - markRange.from
|
|
return sum + size
|
|
}, 0)
|
|
|
|
return selectionRange === range
|
|
}
|