From bdc5998bb11c82b73f78da524e9075f4d3dfeb44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Mon, 30 Nov 2020 20:53:02 +0100 Subject: [PATCH 1/3] invert markIsActive --- packages/core/src/helpers/markIsActive.ts | 55 ++++++++++++++++++----- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/packages/core/src/helpers/markIsActive.ts b/packages/core/src/helpers/markIsActive.ts index 65338fc1..9e9317ad 100644 --- a/packages/core/src/helpers/markIsActive.ts +++ b/packages/core/src/helpers/markIsActive.ts @@ -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 } From 3afe5af21e17f7aee94f2a65330212597e38a761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Mon, 30 Nov 2020 21:10:13 +0100 Subject: [PATCH 2/3] invert nodeIsActive --- packages/core/src/helpers/nodeIsActive.ts | 51 +++++++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/packages/core/src/helpers/nodeIsActive.ts b/packages/core/src/helpers/nodeIsActive.ts index d68bade0..711cceac 100644 --- a/packages/core/src/helpers/nodeIsActive.ts +++ b/packages/core/src/helpers/nodeIsActive.ts @@ -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 } From 6f8632f64369c3634540b19f3d781a3df91b81b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Mon, 30 Nov 2020 21:31:57 +0100 Subject: [PATCH 3/3] fix toggleMark --- packages/core/src/commands/toggleMark.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/core/src/commands/toggleMark.ts b/packages/core/src/commands/toggleMark.ts index b3714d83..39de0dcf 100644 --- a/packages/core/src/commands/toggleMark.ts +++ b/packages/core/src/commands/toggleMark.ts @@ -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) }