add support for only checking attributes in isActive

This commit is contained in:
Philipp Kühn
2020-11-30 00:04:30 +01:00
parent 9a18cce546
commit ec56158739
7 changed files with 76 additions and 25 deletions

View File

@@ -66,6 +66,8 @@ module.exports = {
'@typescript-eslint/no-use-before-define': ['error'], '@typescript-eslint/no-use-before-define': ['error'],
'no-dupe-class-members': 'off', 'no-dupe-class-members': 'off',
'@typescript-eslint/no-dupe-class-members': ['error'], '@typescript-eslint/no-dupe-class-members': ['error'],
'lines-between-class-members': 'off',
'@typescript-eslint/lines-between-class-members': ['error'],
'@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-interface': 'off', '@typescript-eslint/no-empty-interface': 'off',
'@typescript-eslint/explicit-module-boundary-type': 'off', '@typescript-eslint/explicit-module-boundary-type': 'off',

View File

@@ -19,16 +19,16 @@
<button @click="editor.chain().focus().setParagraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }"> <button @click="editor.chain().focus().setParagraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }">
paragraph paragraph
</button> </button>
<button @click="editor.chain().focus().setTextAlign('left').run()"> <button @click="editor.chain().focus().setTextAlign('left').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'left' }) }">
left left
</button> </button>
<button @click="editor.chain().focus().setTextAlign('center').run()"> <button @click="editor.chain().focus().setTextAlign('center').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'center' }) }">
center center
</button> </button>
<button @click="editor.chain().focus().setTextAlign('right').run()"> <button @click="editor.chain().focus().setTextAlign('right').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'right' }) }">
right right
</button> </button>
<button @click="editor.chain().focus().setTextAlign('justify').run()"> <button @click="editor.chain().focus().setTextAlign('justify').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'justify' }) }">
justify justify
</button> </button>
</div> </div>

View File

@@ -1,15 +1,15 @@
<template> <template>
<div v-if="editor"> <div v-if="editor">
<button @click="editor.chain().focus().setTextAlign('left').run()"> <button @click="editor.chain().focus().setTextAlign('left').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'left' }) }">
left left
</button> </button>
<button @click="editor.chain().focus().setTextAlign('center').run()"> <button @click="editor.chain().focus().setTextAlign('center').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'center' }) }">
center center
</button> </button>
<button @click="editor.chain().focus().setTextAlign('right').run()"> <button @click="editor.chain().focus().setTextAlign('right').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'right' }) }">
right right
</button> </button>
<button @click="editor.chain().focus().setTextAlign('justify').run()"> <button @click="editor.chain().focus().setTextAlign('justify').run()" :class="{ 'is-active': editor.isActive({ textAlign: 'justify' }) }">
justify justify
</button> </button>
<button @click="editor.chain().focus().unsetTextAlign().run()"> <button @click="editor.chain().focus().unsetTextAlign().run()">

View File

@@ -3,12 +3,10 @@ import { EditorView } from 'prosemirror-view'
import { Schema, DOMParser, Node } from 'prosemirror-model' import { Schema, DOMParser, Node } from 'prosemirror-model'
import magicMethods from './utils/magicMethods' import magicMethods from './utils/magicMethods'
import elementFromString from './utils/elementFromString' import elementFromString from './utils/elementFromString'
import nodeIsActive from './utils/nodeIsActive'
import markIsActive from './utils/markIsActive'
import getNodeAttributes from './utils/getNodeAttributes' import getNodeAttributes from './utils/getNodeAttributes'
import getMarkAttributes from './utils/getMarkAttributes' import getMarkAttributes from './utils/getMarkAttributes'
import isActive from './utils/isActive'
import removeElement from './utils/removeElement' import removeElement from './utils/removeElement'
import getSchemaTypeNameByName from './utils/getSchemaTypeNameByName'
import getHTMLFromFragment from './utils/getHTMLFromFragment' import getHTMLFromFragment from './utils/getHTMLFromFragment'
import createStyleTag from './utils/createStyleTag' import createStyleTag from './utils/createStyleTag'
import CommandManager from './CommandManager' import CommandManager from './CommandManager'
@@ -350,18 +348,20 @@ export class Editor extends EventEmitter {
* Returns if the currently selected node or mark is active. * Returns if the currently selected node or mark is active.
* *
* @param name Name of the node or mark * @param name Name of the node or mark
* @param attrs Attributes of the node or mark * @param attributes Attributes of the node or mark
*/ */
public isActive(name: string, attrs = {}) { public isActive(name: string, attributes?: {}): boolean;
const schemaType = getSchemaTypeNameByName(name, this.schema) public isActive(attributes: {}): boolean;
public isActive(nameOrAttributes: string, attributesOrUndefined?: {}): boolean {
const name = typeof nameOrAttributes === 'string'
? nameOrAttributes
: null
if (schemaType === 'node') { const attributes = typeof nameOrAttributes === 'string'
return nodeIsActive(this.state, this.schema.nodes[name], attrs) ? attributesOrUndefined
} if (schemaType === 'mark') { : nameOrAttributes
return markIsActive(this.state, this.schema.marks[name], attrs)
}
return false return isActive(this.state, name, attributes)
} }
/** /**

View File

@@ -0,0 +1,45 @@
import { EditorState } from 'prosemirror-state'
import { Node, Mark } from 'prosemirror-model'
import nodeIsActive from './nodeIsActive'
import markIsActive from './markIsActive'
import objectIncludes from './objectIncludes'
import getSchemaTypeNameByName from './getSchemaTypeNameByName'
export default function isActive(state: EditorState, name: string | null, attributes: { [key: string ]: any } = {}): boolean {
if (name) {
const schemaType = getSchemaTypeNameByName(name, state.schema)
if (schemaType === 'node') {
return nodeIsActive(state, state.schema.nodes[name], attributes)
} if (schemaType === 'mark') {
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
}

View File

@@ -3,16 +3,14 @@ import { MarkType } from 'prosemirror-model'
import getMarkAttributes from './getMarkAttributes' import getMarkAttributes from './getMarkAttributes'
import { AnyObject } from '../types' import { AnyObject } from '../types'
import isEmptyObject from './isEmptyObject' import isEmptyObject from './isEmptyObject'
import objectIncludes from './objectIncludes'
export default function markHasAttributes(state: EditorState, type: MarkType, attributes: AnyObject) { export default function markHasAttributes(state: EditorState, type: MarkType, attributes: AnyObject) {
if (isEmptyObject(attributes)) { if (isEmptyObject(attributes)) {
return true return true
} }
const originalAttrs = getMarkAttributes(state, type) const originalAttributes = getMarkAttributes(state, type)
return !!Object return objectIncludes(originalAttributes, attributes)
.keys(attributes)
.filter(key => attributes[key] === originalAttrs[key])
.length
} }

View File

@@ -0,0 +1,6 @@
export default function objectIncludes(object1: { [key: string ]: any }, object2: { [key: string ]: any }): boolean {
return !!Object
.keys(object2)
.filter(key => object2[key] === object1[key])
.length
}