Merge branch 'feature/invert-isactive' into main

This commit is contained in:
Philipp Kühn 2020-12-01 09:00:45 +01:00
commit 81c0d02f5d
3 changed files with 90 additions and 30 deletions

View File

@ -1,4 +1,3 @@
import { toggleMark as originalToggleMark } from 'prosemirror-commands'
import { MarkType } from 'prosemirror-model'
import { Command } from '../types'
import getMarkType from '../helpers/getMarkType'
@ -7,16 +6,13 @@ import markIsActive from '../helpers/markIsActive'
/**
* Toggle a mark on and off.
*/
export const toggleMark = (typeOrName: string | MarkType, attributes?: {}): Command => ({ state, dispatch, commands }) => {
export const toggleMark = (typeOrName: string | MarkType, attributes?: {}): Command => ({ state, commands }) => {
const type = getMarkType(typeOrName, state.schema)
const isActive = markIsActive(state, type, attributes)
const hasMarkWithDifferentAttributes = attributes
&& markIsActive(state, type)
&& !markIsActive(state, type, attributes)
if (attributes && hasMarkWithDifferentAttributes) {
return commands.setMark(type, attributes)
if (isActive) {
return commands.unsetMark(type)
}
return originalToggleMark(type, attributes)(state, dispatch)
return commands.setMark(type, attributes)
}

View File

@ -3,31 +3,62 @@ import { Mark, MarkType } from 'prosemirror-model'
import objectIncludes from '../utilities/objectIncludes'
import getMarkType from '../helpers/getMarkType'
type MarkRange = {
mark: Mark,
from: number,
to: number,
}
export default function markIsActive(state: EditorState, typeOrName: MarkType | string | null, attributes = {}) {
const { from, to, empty } = state.selection
const type = typeOrName
? getMarkType(typeOrName, state.schema)
: null
let marks: Mark[] = []
if (empty) {
marks = state.selection.$head.marks()
} else {
state.doc.nodesBetween(from, to, node => {
marks = [...marks, ...node.marks]
})
return !!state.selection.$head.marks()
.filter(mark => {
if (!type) {
return true
}
return type.name === mark.type.name
})
.find(mark => objectIncludes(mark.attrs, attributes))
}
const markWithAttributes = marks
.filter(mark => {
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 === mark.type.name
return type.name === markRange.mark.type.name
})
.find(mark => objectIncludes(mark.attrs, attributes))
.filter(markRange => objectIncludes(markRange.mark.attrs, attributes))
.reduce((sum, markRange) => {
const size = markRange.to - markRange.from
return sum + size
}, 0)
return !!markWithAttributes
return selectionRange <= range
}

View File

@ -3,27 +3,60 @@ import { Node, NodeType } from 'prosemirror-model'
import objectIncludes from '../utilities/objectIncludes'
import getNodeType from '../helpers/getNodeType'
type NodeRange = {
node: Node,
from: number,
to: number,
}
export default function nodeIsActive(state: EditorState, typeOrName: NodeType | string | null, attributes = {}) {
const { from, to } = state.selection
const { from, to, empty } = state.selection
const type = typeOrName
? getNodeType(typeOrName, state.schema)
: null
let nodes: Node[] = []
let nodeRanges: NodeRange[] = []
state.doc.nodesBetween(from, to, node => {
nodes = [...nodes, node]
state.doc.nodesBetween(from, to, (node, pos) => {
if (!node.isText) {
const relativeFrom = Math.max(from, pos)
const relativeTo = Math.min(to, pos + node.nodeSize)
nodeRanges = [...nodeRanges, {
node,
from: relativeFrom,
to: relativeTo,
}]
}
})
const nodeWithAttributes = nodes
.filter(node => {
if (empty) {
return !!nodeRanges
.filter(nodeRange => {
if (!type) {
return true
}
return type.name === nodeRange.node.type.name
})
.find(nodeRange => objectIncludes(nodeRange.node.attrs, attributes))
}
const range = nodeRanges
.filter(nodeRange => {
if (!type) {
return true
}
return type.name === node.type.name
return type.name === nodeRange.node.type.name
})
.find(node => objectIncludes(node.attrs, attributes))
.filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes))
.reduce((sum, nodeRange) => {
const size = nodeRange.to - nodeRange.from
return sum + size
}, 0)
return !!nodeWithAttributes
const selectionRange = to - from
return selectionRange <= range
}