improve isActive for marks

This commit is contained in:
Philipp Kühn
2020-11-30 15:40:16 +01:00
parent 4fbdb0ff0c
commit c0911c315c
5 changed files with 59 additions and 70 deletions

View File

@@ -1,8 +1,6 @@
import { EditorState } from 'prosemirror-state' import { EditorState } from 'prosemirror-state'
import { Node, Mark } from 'prosemirror-model'
import nodeIsActive from './nodeIsActive' import nodeIsActive from './nodeIsActive'
import markIsActive from './markIsActive' import markIsActive from './markIsActive'
import objectIncludes from '../utilities/objectIncludes'
import getSchemaTypeNameByName from './getSchemaTypeNameByName' import getSchemaTypeNameByName from './getSchemaTypeNameByName'
export default function isActive(state: EditorState, name: string | null, attributes: { [key: string ]: any } = {}): boolean { export default function isActive(state: EditorState, name: string | null, attributes: { [key: string ]: any } = {}): boolean {
@@ -14,32 +12,9 @@ export default function isActive(state: EditorState, name: string | null, attrib
} if (schemaType === 'mark') { } if (schemaType === 'mark') {
return markIsActive(state, state.schema.marks[name], attributes) return markIsActive(state, state.schema.marks[name], attributes)
} }
}
if (!name) {
const { from, to, empty } = state.selection
let nodes: Node[] = []
let marks: Mark[] = []
if (empty) {
marks = state.selection.$head.marks()
}
state.doc.nodesBetween(from, to, node => {
nodes = [...nodes, node]
if (!empty) {
marks = [...marks, ...node.marks]
}
})
const anyNodeWithAttributes = nodes.find(node => objectIncludes(node.attrs, attributes))
const anyMarkWithAttributes = marks.find(mark => objectIncludes(mark.attrs, attributes))
if (anyNodeWithAttributes || anyMarkWithAttributes) {
return true
}
}
return false return false
}
return nodeIsActive(state, null, attributes) || markIsActive(state, null, attributes)
} }

View File

@@ -1,15 +0,0 @@
import { EditorState } from 'prosemirror-state'
import { MarkType } from 'prosemirror-model'
import getMarkAttributes from './getMarkAttributes'
import isEmptyObject from '../utilities/isEmptyObject'
import objectIncludes from '../utilities/objectIncludes'
export default function markHasAttributes(state: EditorState, type: MarkType, attributes: {}) {
if (isEmptyObject(attributes)) {
return true
}
const originalAttributes = getMarkAttributes(state, type)
return objectIncludes(originalAttributes, attributes)
}

View File

@@ -1,20 +1,33 @@
import { EditorState } from 'prosemirror-state' import { EditorState } from 'prosemirror-state'
import { MarkType } from 'prosemirror-model' import { Mark, MarkType } from 'prosemirror-model'
import markHasAttributes from './markHasAttributes' import objectIncludes from '../utilities/objectIncludes'
import getMarkType from '../helpers/getMarkType'
export default function markIsActive(state: EditorState, type: MarkType, attributes = {}) { export default function markIsActive(state: EditorState, typeOrName: MarkType | string | null, attributes = {}) {
const { const { from, to, empty } = state.selection
from, const type = typeOrName
$from, ? getMarkType(typeOrName, state.schema)
to, : null
empty,
} = state.selection
const hasMark = empty let marks: Mark[] = []
? !!(type.isInSet(state.storedMarks || $from.marks()))
: state.doc.rangeHasMark(from, to, type)
const hasAttributes = markHasAttributes(state, type, attributes) if (empty) {
marks = state.selection.$head.marks()
} else {
state.doc.nodesBetween(from, to, node => {
marks = [...marks, ...node.marks]
})
}
return hasMark && hasAttributes const markWithAttributes = marks
.filter(mark => {
if (!type) {
return true
}
return type.name === mark.type.name
})
.find(mark => objectIncludes(mark.attrs, attributes))
return !!markWithAttributes
} }

View File

@@ -1,18 +1,29 @@
import { findParentNode, findSelectedNodeOfType } from 'prosemirror-utils'
import { EditorState } from 'prosemirror-state' import { EditorState } from 'prosemirror-state'
import { Node, NodeType } from 'prosemirror-model' import { Node, NodeType } from 'prosemirror-model'
import objectIncludes from '../utilities/objectIncludes'
import getNodeType from '../helpers/getNodeType'
export default function nodeIsActive(state: EditorState, type: NodeType, attributes = {}) { export default function nodeIsActive(state: EditorState, typeOrName: NodeType | string | null, attributes = {}) {
const predicate = (node: Node) => node.type === type const { from, to } = state.selection
const node = findSelectedNodeOfType(type)(state.selection) const type = typeOrName
|| findParentNode(predicate)(state.selection) ? getNodeType(typeOrName, state.schema)
: null
if (!Object.keys(attributes).length || !node) { let nodes: Node[] = []
return !!node
state.doc.nodesBetween(from, to, node => {
nodes = [...nodes, node]
})
const nodeWithAttributes = nodes
.filter(node => {
if (!type) {
return true
} }
return node.node.hasMarkup(type, { return type.name === node.type.name
...node.node.attrs,
...attributes,
}) })
.find(node => objectIncludes(node.attrs, attributes))
return !!nodeWithAttributes
} }

View File

@@ -4,8 +4,13 @@
* @param object2 Object * @param object2 Object
*/ */
export default function objectIncludes(object1: { [key: string ]: any }, object2: { [key: string ]: any }): boolean { export default function objectIncludes(object1: { [key: string ]: any }, object2: { [key: string ]: any }): boolean {
return !!Object const keys = Object.keys(object2)
.keys(object2)
if (!keys.length) {
return true
}
return !!keys
.filter(key => object2[key] === object1[key]) .filter(key => object2[key] === object1[key])
.length .length
} }