feat: allow wildcards when checking attributes in isActive, fix #1752
This commit is contained in:
@@ -23,7 +23,7 @@ export default function isMarkActive(
|
|||||||
|
|
||||||
return type.name === mark.type.name
|
return type.name === mark.type.name
|
||||||
})
|
})
|
||||||
.find(mark => objectIncludes(mark.attrs, attributes))
|
.find(mark => objectIncludes(mark.attrs, attributes, { strict: false }))
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectionRange = 0
|
let selectionRange = 0
|
||||||
@@ -58,7 +58,7 @@ export default function isMarkActive(
|
|||||||
|
|
||||||
return type.name === markRange.mark.type.name
|
return type.name === markRange.mark.type.name
|
||||||
})
|
})
|
||||||
.filter(markRange => objectIncludes(markRange.mark.attrs, attributes))
|
.filter(markRange => objectIncludes(markRange.mark.attrs, attributes, { strict: false }))
|
||||||
.reduce((sum, markRange) => {
|
.reduce((sum, markRange) => {
|
||||||
const size = markRange.to - markRange.from
|
const size = markRange.to - markRange.from
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default function isNodeActive(
|
|||||||
|
|
||||||
return type.name === nodeRange.node.type.name
|
return type.name === nodeRange.node.type.name
|
||||||
})
|
})
|
||||||
.find(nodeRange => objectIncludes(nodeRange.node.attrs, attributes))
|
.find(nodeRange => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }))
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectionRange = to - from
|
const selectionRange = to - from
|
||||||
@@ -51,7 +51,7 @@ export default function isNodeActive(
|
|||||||
|
|
||||||
return type.name === nodeRange.node.type.name
|
return type.name === nodeRange.node.type.name
|
||||||
})
|
})
|
||||||
.filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes))
|
.filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }))
|
||||||
.reduce((sum, nodeRange) => {
|
.reduce((sum, nodeRange) => {
|
||||||
const size = nodeRange.to - nodeRange.from
|
const size = nodeRange.to - nodeRange.from
|
||||||
return sum + size
|
return sum + size
|
||||||
|
|||||||
3
packages/core/src/utilities/isRegExp.ts
Normal file
3
packages/core/src/utilities/isRegExp.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function isRegExp(value: any): boolean {
|
||||||
|
return Object.prototype.toString.call(value) === '[object RegExp]'
|
||||||
|
}
|
||||||
@@ -1,16 +1,30 @@
|
|||||||
|
import isRegExp from './isRegExp'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if object1 includes object2
|
* Check if object1 includes object2
|
||||||
* @param object1 Object
|
* @param object1 Object
|
||||||
* @param object2 Object
|
* @param object2 Object
|
||||||
*/
|
*/
|
||||||
export default function objectIncludes(object1: Record<string, any>, object2: Record<string, any>): boolean {
|
export default function objectIncludes(
|
||||||
|
object1: Record<string, any>,
|
||||||
|
object2: Record<string, any>,
|
||||||
|
options: { strict: boolean } = { strict: true },
|
||||||
|
): boolean {
|
||||||
const keys = Object.keys(object2)
|
const keys = Object.keys(object2)
|
||||||
|
|
||||||
if (!keys.length) {
|
if (!keys.length) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return !!keys
|
return keys.every(key => {
|
||||||
.filter(key => object2[key] === object1[key])
|
if (options.strict) {
|
||||||
.length
|
return object2[key] === object1[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRegExp(object2[key])) {
|
||||||
|
return object2[key].test(object1[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
return object2[key] === object1[key]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
124
tests/cypress/integration/core/isActive.spec.ts
Normal file
124
tests/cypress/integration/core/isActive.spec.ts
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import { Editor } from '@tiptap/core'
|
||||||
|
import Document from '@tiptap/extension-document'
|
||||||
|
import Paragraph from '@tiptap/extension-paragraph'
|
||||||
|
import Text from '@tiptap/extension-text'
|
||||||
|
import TextStyle from '@tiptap/extension-text-style'
|
||||||
|
import FontFamily from '@tiptap/extension-font-family'
|
||||||
|
import Color from '@tiptap/extension-color'
|
||||||
|
|
||||||
|
describe('isActive', () => {
|
||||||
|
it('should check the current node', () => {
|
||||||
|
const editor = new Editor({
|
||||||
|
extensions: [
|
||||||
|
Document,
|
||||||
|
Paragraph,
|
||||||
|
Text,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(editor.isActive('paragraph')).to.eq(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should check non-existent nodes', () => {
|
||||||
|
const editor = new Editor({
|
||||||
|
extensions: [
|
||||||
|
Document,
|
||||||
|
Paragraph,
|
||||||
|
Text,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(editor.isActive('doesNotExist')).to.eq(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should check the current mark for correct values', () => {
|
||||||
|
const editor = new Editor({
|
||||||
|
extensions: [
|
||||||
|
Document,
|
||||||
|
Paragraph,
|
||||||
|
Text,
|
||||||
|
TextStyle,
|
||||||
|
FontFamily,
|
||||||
|
Color,
|
||||||
|
],
|
||||||
|
content: `
|
||||||
|
<p><span style="font-family: Inter">text</span></p>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(editor.isActive('textStyle', { fontFamily: 'Inter' })).to.eq(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should check the current mark for false values', () => {
|
||||||
|
const editor = new Editor({
|
||||||
|
extensions: [
|
||||||
|
Document,
|
||||||
|
Paragraph,
|
||||||
|
Text,
|
||||||
|
TextStyle,
|
||||||
|
FontFamily,
|
||||||
|
Color,
|
||||||
|
],
|
||||||
|
content: `
|
||||||
|
<p><span style="font-family: Inter; color: red">text</span></p>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(editor.isActive('textStyle', { fontFamily: 'Comic Sans' })).to.eq(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should check the current mark for any values', () => {
|
||||||
|
const editor = new Editor({
|
||||||
|
extensions: [
|
||||||
|
Document,
|
||||||
|
Paragraph,
|
||||||
|
Text,
|
||||||
|
TextStyle,
|
||||||
|
FontFamily,
|
||||||
|
],
|
||||||
|
content: `
|
||||||
|
<p><span style="font-family: Inter; color: red">text</span></p>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(editor.isActive('textStyle', { fontFamily: /.*/ })).to.eq(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should check the current mark for correct values (multiple)', () => {
|
||||||
|
const editor = new Editor({
|
||||||
|
extensions: [
|
||||||
|
Document,
|
||||||
|
Paragraph,
|
||||||
|
Text,
|
||||||
|
TextStyle,
|
||||||
|
FontFamily,
|
||||||
|
Color,
|
||||||
|
],
|
||||||
|
content: `
|
||||||
|
<p><span style="font-family: Inter; color: red">text</span></p>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(editor.isActive('textStyle', { fontFamily: 'Inter', color: 'red' })).to.eq(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should check the current mark for false values (multiple)', () => {
|
||||||
|
const editor = new Editor({
|
||||||
|
extensions: [
|
||||||
|
Document,
|
||||||
|
Paragraph,
|
||||||
|
Text,
|
||||||
|
TextStyle,
|
||||||
|
FontFamily,
|
||||||
|
Color,
|
||||||
|
],
|
||||||
|
content: `
|
||||||
|
<p><span style="font-family: Inter; color: red">text</span></p>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(editor.isActive('textStyle', { fontFamily: 'Inter', color: 'green' })).to.eq(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user