EZQMS-342: Add text editor configurable active highlighted node (#4019)

Signed-off-by: Anna No <anna.no@xored.com>
This commit is contained in:
Anna No 2023-11-21 11:03:53 +07:00 committed by GitHub
parent d36dcdc7da
commit 0e11cc704d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -3,7 +3,7 @@ import { type Node as ProseMirrorNode, type MarkType } from '@tiptap/pm/model'
import { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state' import { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state'
import { AddMarkStep, RemoveMarkStep } from '@tiptap/pm/transform' import { AddMarkStep, RemoveMarkStep } from '@tiptap/pm/transform'
import { Decoration, DecorationSet } from '@tiptap/pm/view' import { Decoration, DecorationSet } from '@tiptap/pm/view'
import { NodeUuidExtension, type NodeUuidOptions, type NodeUuidStorage, findNodeUuidMark } from './nodeUuid' import { NodeUuidExtension, type NodeUuidOptions, findNodeUuidMark } from './nodeUuid'
import { type TextEditorCommand } from '../../types' import { type TextEditorCommand } from '../../types'
export enum NodeHighlightType { export enum NodeHighlightType {
@ -17,7 +17,7 @@ export function highlightUpdateCommand (): TextEditorCommand {
} }
export interface NodeHighlightExtensionOptions extends NodeUuidOptions { export interface NodeHighlightExtensionOptions extends NodeUuidOptions {
getNodeHighlightType: (uuid: string) => NodeHighlightType | undefined | null getNodeHighlight: (uuid: string) => { type: NodeHighlightType, isActive?: boolean } | undefined | null
isHighlightModeOn: () => boolean isHighlightModeOn: () => boolean
isAutoSelect?: () => boolean isAutoSelect?: () => boolean
} }
@ -32,21 +32,23 @@ const generateAttributes = (uuid: string, options: NodeHighlightExtensionOptions
return undefined return undefined
} }
const type = options.getNodeHighlightType(uuid) const highlight = options.getNodeHighlight(uuid)
if (type === null || type === undefined) { if (highlight === null || highlight === undefined) {
return undefined return undefined
} }
const classAttrs: { class?: string } = {} const classAttrs: { class?: string } = {}
if (type === NodeHighlightType.WARNING) { if (highlight.type === NodeHighlightType.WARNING) {
classAttrs.class = 'text-editor-highlighted-node-warning' classAttrs.class = 'text-editor-highlighted-node-warning'
} else if (type === NodeHighlightType.ADD) { } else if (highlight.type === NodeHighlightType.ADD) {
classAttrs.class = 'text-editor-highlighted-node-add' classAttrs.class = 'text-editor-highlighted-node-add'
} else if (type === NodeHighlightType.DELETE) { } else if (highlight.type === NodeHighlightType.DELETE) {
classAttrs.class = 'text-editor-highlighted-node-delete' classAttrs.class = 'text-editor-highlighted-node-delete'
} }
return classAttrs return highlight.isActive === true
? mergeAttributes(classAttrs, { class: 'text-editor-highlighted-node-selected' })
: classAttrs
} }
const NodeHighlight = 'node-highlight' const NodeHighlight = 'node-highlight'
@ -69,17 +71,12 @@ declare module '@tiptap/core' {
/** /**
* Extension allows to highlight nodes based on uuid * Extension allows to highlight nodes based on uuid
*/ */
export const NodeHighlightExtension: Extension<NodeHighlightExtensionOptions, NodeUuidStorage> = export const NodeHighlightExtension: Extension<NodeHighlightExtensionOptions> =
Extension.create<NodeHighlightExtensionOptions>({ Extension.create<NodeHighlightExtensionOptions>({
name: NodeHighlight, name: NodeHighlight,
addStorage (): NodeUuidStorage {
return { activeNodeUuid: null }
},
addProseMirrorPlugins () { addProseMirrorPlugins () {
const options = this.options const options = this.options
const storage: NodeUuidStorage = this.storage
const plugins = [ const plugins = [
...(this.parent?.() ?? []), ...(this.parent?.() ?? []),
@ -114,14 +111,14 @@ export const NodeHighlightExtension: Extension<NodeHighlightExtensionOptions, No
init (_config, state): DecorationSet { init (_config, state): DecorationSet {
const { doc, schema } = state const { doc, schema } = state
const markType = schema.marks[NodeUuidExtension.name] const markType = schema.marks[NodeUuidExtension.name]
return createDecorations(doc, markType, storage, options) return createDecorations(doc, markType, options)
}, },
apply (tr, decorations, oldState, newState) { apply (tr, decorations, oldState, newState) {
const markType = newState.schema.marks[NodeUuidExtension.name] const markType = newState.schema.marks[NodeUuidExtension.name]
if (tr.getMeta(NodeHighlightMeta) !== undefined) { if (tr.getMeta(NodeHighlightMeta) !== undefined) {
return createDecorations(tr.doc, markType, storage, options) return createDecorations(tr.doc, markType, options)
} }
if (!tr.docChanged) { if (!tr.docChanged) {
@ -136,7 +133,7 @@ export const NodeHighlightExtension: Extension<NodeHighlightExtensionOptions, No
(step instanceof RemoveMarkStep && step.mark.type === markType) (step instanceof RemoveMarkStep && step.mark.type === markType)
) )
) { ) {
return createDecorations(tr.doc, markType, storage, options) return createDecorations(tr.doc, markType, options)
} }
// update all decorations when changed content has mark changes // update all decorations when changed content has mark changes
@ -157,7 +154,7 @@ export const NodeHighlightExtension: Extension<NodeHighlightExtensionOptions, No
}) })
if (hasMarkChanges) { if (hasMarkChanges) {
return createDecorations(tr.doc, markType, storage, options) return createDecorations(tr.doc, markType, options)
} }
return decorations.map(tr.mapping, tr.doc) return decorations.map(tr.mapping, tr.doc)
@ -189,17 +186,12 @@ export const NodeHighlightExtension: Extension<NodeHighlightExtensionOptions, No
addExtensions () { addExtensions () {
const options: NodeHighlightExtensionOptions = this.options const options: NodeHighlightExtensionOptions = this.options
const storage: NodeUuidStorage = this.storage
return [ return [
NodeUuidExtension.extend({ NodeUuidExtension.extend({
addOptions (): NodeUuidOptions { addOptions (): NodeUuidOptions {
return { return {
...this.parent?.(), ...this.parent?.(),
...options, ...options
onNodeSelected: (uuid: string) => {
storage.activeNodeUuid = uuid
options.onNodeSelected?.(uuid)
}
} }
} }
}) })
@ -210,7 +202,6 @@ export const NodeHighlightExtension: Extension<NodeHighlightExtensionOptions, No
const createDecorations = ( const createDecorations = (
doc: ProseMirrorNode, doc: ProseMirrorNode,
markType: MarkType, markType: MarkType,
storage: NodeUuidStorage,
options: NodeHighlightExtensionOptions options: NodeHighlightExtensionOptions
): DecorationSet => { ): DecorationSet => {
const decorations: Decoration[] = [] const decorations: Decoration[] = []
@ -231,16 +222,7 @@ const createDecorations = (
return return
} }
decorations.push( decorations.push(Decoration.inline(range.from, range.to, attributes))
Decoration.inline(
range.from,
range.to,
mergeAttributes(
attributes,
nodeUuid === storage.activeNodeUuid ? { class: 'text-editor-highlighted-node-selected' } : {}
)
)
)
} }
}) })