@tiptap/extension-unique-id 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/demos/setup/helper.d.ts +2 -0
- package/dist/demos/setup/react.d.ts +3 -0
- package/dist/demos/setup/vue.d.ts +3 -0
- package/dist/demos/vite.config.d.ts +2 -0
- package/dist/packages/extension-drag-handle/src/drag-handle.d.ts +13 -0
- package/dist/packages/extension-drag-handle/src/helpers/cloneElement.d.ts +1 -0
- package/dist/packages/extension-drag-handle/src/helpers/dragHandler.d.ts +2 -0
- package/dist/packages/extension-drag-handle/src/helpers/getComputedStyle.d.ts +1 -0
- package/dist/packages/extension-drag-handle/src/helpers/getInnerCoords.d.ts +5 -0
- package/dist/packages/extension-drag-handle/src/helpers/minMax.d.ts +1 -0
- package/dist/packages/extension-drag-handle/src/helpers/removeNode.d.ts +1 -0
- package/dist/packages/extension-drag-handle/src/index.d.ts +3 -0
- package/dist/packages/extension-node-range/src/helpers/NodeRangeBookmark.d.ts +10 -0
- package/dist/packages/extension-node-range/src/helpers/NodeRangeSelection.d.ts +23 -0
- package/dist/packages/extension-node-range/src/helpers/getNodeRangeDecorations.d.ts +3 -0
- package/dist/packages/extension-node-range/src/helpers/getSelectionRanges.d.ts +3 -0
- package/dist/packages/extension-node-range/src/helpers/isNodeRangeSelection.d.ts +2 -0
- package/dist/packages/extension-node-range/src/index.d.ts +7 -0
- package/dist/packages/extension-node-range/src/node-range.d.ts +6 -0
- package/dist/packages/extension-unique-id/src/helpers/arrayDifference.d.ts +9 -0
- package/dist/packages/extension-unique-id/src/helpers/combineTransactionSteps.d.ts +7 -0
- package/dist/packages/extension-unique-id/src/helpers/findDuplicates.d.ts +4 -0
- package/dist/packages/extension-unique-id/src/helpers/getChangedRanges.d.ts +12 -0
- package/dist/packages/extension-unique-id/src/helpers/removeDuplicates.d.ts +8 -0
- package/dist/packages/extension-unique-id/src/index.d.ts +3 -0
- package/dist/packages/extension-unique-id/src/unique-id.d.ts +9 -0
- package/dist/tiptap-extension-unique-id.cjs.js +291 -0
- package/dist/tiptap-extension-unique-id.cjs.js.map +1 -0
- package/dist/tiptap-extension-unique-id.esm.js +286 -0
- package/dist/tiptap-extension-unique-id.esm.js.map +1 -0
- package/dist/tiptap-extension-unique-id.umd.js +291 -0
- package/dist/tiptap-extension-unique-id.umd.js.map +1 -0
- package/package.json +34 -0
- package/src/helpers/arrayDifference.ts +35 -0
- package/src/helpers/combineTransactionSteps.ts +18 -0
- package/src/helpers/findDuplicates.ts +11 -0
- package/src/helpers/getChangedRanges.ts +78 -0
- package/src/helpers/removeDuplicates.ts +15 -0
- package/src/index.ts +5 -0
- package/src/unique-id.ts +245 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tiptap-extension-unique-id.umd.js","sources":["../src/helpers/combineTransactionSteps.ts","../src/helpers/removeDuplicates.ts","../src/helpers/getChangedRanges.ts","../src/helpers/findDuplicates.ts","../src/unique-id.ts"],"sourcesContent":["import { Node as ProseMirrorNode } from 'prosemirror-model'\nimport { Transaction } from 'prosemirror-state'\nimport { Transform } from 'prosemirror-transform'\n\n/**\n * Returns a new `Transform` based on all steps of the passed transactions.\n */\nexport default function combineTransactionSteps(oldDoc: ProseMirrorNode, transactions: Transaction[]): Transform {\n const transform = new Transform(oldDoc)\n\n transactions.forEach(transaction => {\n transaction.steps.forEach(step => {\n transform.step(step)\n })\n })\n\n return transform\n}\n","/**\n * Removes duplicated values within an array.\n * Supports numbers, strings and objects.\n */\nexport default function removeDuplicates<T>(array: T[], by = JSON.stringify): T[] {\n const seen: Record<any, any> = {}\n\n return array.filter(item => {\n const key = by(item)\n\n return Object.prototype.hasOwnProperty.call(seen, key)\n ? false\n : (seen[key] = true)\n })\n}\n","import { Transform, Step } from 'prosemirror-transform'\nimport removeDuplicates from './removeDuplicates'\n\nexport type ChangedRange = {\n oldStart: number,\n oldEnd: number,\n newStart: number,\n newEnd: number,\n}\n\n/**\n * Removes duplicated ranges and ranges that are\n * fully captured by other ranges.\n */\nfunction simplifyChangedRanges(changes: ChangedRange[]): ChangedRange[] {\n const uniqueChanges = removeDuplicates(changes)\n\n return uniqueChanges.length === 1\n ? uniqueChanges\n : uniqueChanges.filter((change, index) => {\n const rest = uniqueChanges.filter((_, i) => i !== index)\n\n return !rest.some(otherChange => {\n return change.oldStart >= otherChange.oldStart\n && change.oldEnd <= otherChange.oldEnd\n && change.newStart >= otherChange.newStart\n && change.newEnd <= otherChange.newEnd\n })\n })\n}\n\n/**\n * Returns a list of changed ranges\n * based on the first and last state of all steps.\n */\nexport default function getChangedRanges(transform: Transform): ChangedRange[] {\n const { mapping, steps } = transform\n const changes: ChangedRange[] = []\n\n mapping.maps.forEach((stepMap, index) => {\n // This accounts for step changes where no range was actually altered\n // e.g. when setting a mark, node attribute, etc.\n // @ts-ignore\n if (!stepMap.ranges.length) {\n const step = steps[index] as Step & {\n from?: number,\n to?: number,\n }\n\n if (step.from === undefined || step.to === undefined) {\n return\n }\n\n changes.push({\n oldStart: step.from,\n oldEnd: step.to,\n newStart: step.from,\n newEnd: step.to,\n })\n } else {\n stepMap.forEach((from, to) => {\n const newStart = mapping.slice(index).map(from, -1)\n const newEnd = mapping.slice(index).map(to)\n const oldStart = mapping.invert().map(newStart, -1)\n const oldEnd = mapping.invert().map(newEnd)\n\n changes.push({\n oldStart,\n oldEnd,\n newStart,\n newEnd,\n })\n })\n }\n })\n\n return simplifyChangedRanges(changes)\n}\n","import removeDuplicates from './removeDuplicates'\n\n/**\n * Returns a list of duplicated items within an array.\n */\nexport default function findDuplicates(items: any[]): any[] {\n const filtered = items.filter((el, index) => items.indexOf(el) !== index)\n const duplicates = removeDuplicates(filtered)\n\n return duplicates\n}\n","import { Extension, findChildren, findChildrenInRange } from '@tiptap/core'\nimport { Plugin, PluginKey, Transaction } from 'prosemirror-state'\nimport { Slice, Fragment, Node as ProseMirrorNode } from 'prosemirror-model'\nimport { v4 as uuidv4 } from 'uuid'\nimport combineTransactionSteps from './helpers/combineTransactionSteps'\nimport getChangedRanges from './helpers/getChangedRanges'\nimport findDuplicates from './helpers/findDuplicates'\n\nexport interface UniqueIdOptions {\n attributeName: string,\n types: string[],\n generateId: () => any,\n filterTransaction: ((transaction: Transaction) => boolean) | null,\n}\n\nexport const UniqueId = Extension.create<UniqueIdOptions>({\n name: 'uniqueId',\n\n // we’ll set a very high priority to make sure this runs first\n // and is compatible with `appendTransaction` hooks of other extensions\n priority: 10000,\n\n defaultOptions: {\n attributeName: 'id',\n types: [],\n generateId: () => uuidv4(),\n filterTransaction: null,\n },\n\n addGlobalAttributes() {\n return [\n {\n types: this.options.types,\n attributes: {\n [this.options.attributeName]: {\n default: null,\n parseHTML: element => element.getAttribute(`data-${this.options.attributeName}`),\n renderHTML: attributes => {\n if (!attributes[this.options.attributeName]) {\n return {}\n }\n\n return {\n [`data-${this.options.attributeName}`]: attributes[this.options.attributeName],\n }\n },\n },\n },\n },\n ]\n },\n\n // check initial content for missing ids\n onCreate() {\n const { view, state } = this.editor\n const { tr, doc } = state\n const { types, attributeName, generateId } = this.options\n const nodesWithoutId = findChildren(doc, node => {\n return types.includes(node.type.name)\n && node.attrs[attributeName] === null\n })\n\n nodesWithoutId.forEach(({ node, pos }) => {\n tr.setNodeMarkup(pos, undefined, {\n ...node.attrs,\n [attributeName]: generateId(),\n })\n })\n\n view.dispatch(tr)\n },\n\n addProseMirrorPlugins() {\n let dragSourceElement: Element | null = null\n let transformPasted = false\n\n return [\n new Plugin({\n key: new PluginKey('uniqueId'),\n\n appendTransaction: (transactions, oldState, newState) => {\n const docChanges = transactions.some(transaction => transaction.docChanged)\n && !oldState.doc.eq(newState.doc)\n const filterTransactions = this.options.filterTransaction\n && transactions.some(tr => !this.options.filterTransaction?.(tr))\n\n if (!docChanges || filterTransactions) {\n return\n }\n\n const { tr } = newState\n const { types, attributeName, generateId } = this.options\n const transform = combineTransactionSteps(oldState.doc, transactions)\n const { mapping } = transform\n\n // get changed ranges based on the old state\n const changes = getChangedRanges(transform)\n\n changes.forEach(change => {\n const newRange = {\n from: change.newStart,\n to: change.newEnd,\n }\n\n const newNodes = findChildrenInRange(newState.doc, newRange, node => {\n return types.includes(node.type.name)\n })\n\n const newIds = newNodes\n .map(({ node }) => node.attrs[attributeName])\n .filter(id => id !== null)\n\n const duplicatedNewIds = findDuplicates(newIds)\n\n newNodes.forEach(({ node, pos }) => {\n // instead of checking `node.attrs[attributeName]` directly\n // we look at the current state of the node within `tr.doc`.\n // this helps to prevent adding new ids to the same node\n // if the node changed multiple times within one transaction\n const id = tr.doc.nodeAt(pos)?.attrs[attributeName]\n\n if (id === null) {\n tr.setNodeMarkup(pos, undefined, {\n ...node.attrs,\n [attributeName]: generateId(),\n })\n\n return\n }\n\n // check if the node doesn’t exist in the old state\n const { deleted } = mapping.invert().mapResult(pos)\n const newNode = deleted && duplicatedNewIds.includes(id)\n\n if (newNode) {\n tr.setNodeMarkup(pos, undefined, {\n ...node.attrs,\n [attributeName]: generateId(),\n })\n }\n })\n\n })\n\n if (!tr.steps.length) {\n return\n }\n\n return tr\n },\n\n // we register a global drag handler to track the current drag source element\n view(view) {\n const handleDragstart = (event: DragEvent) => {\n dragSourceElement = view.dom.parentElement?.contains(event.target as Element)\n ? view.dom.parentElement\n : null\n }\n\n window.addEventListener('dragstart', handleDragstart)\n\n return {\n destroy() {\n window.removeEventListener('dragstart', handleDragstart)\n },\n }\n },\n\n props: {\n // `handleDOMEvents` is called before `transformPasted`\n // so we can do some checks before\n handleDOMEvents: {\n // only create new ids for dropped content while holding `alt`\n // or content is dragged from another editor\n drop: (view, event) => {\n if (\n dragSourceElement !== view.dom.parentElement\n || event.dataTransfer?.effectAllowed === 'copy'\n ) {\n dragSourceElement = null\n transformPasted = true\n }\n\n return false\n },\n // always create new ids on pasted content\n paste: () => {\n transformPasted = true\n\n return false\n },\n },\n\n // we’ll remove ids for every pasted node\n // so we can create a new one within `appendTransaction`\n transformPasted: slice => {\n if (!transformPasted) {\n return slice\n }\n\n const { types, attributeName } = this.options\n const removeId = (fragment: Fragment): Fragment => {\n const list: ProseMirrorNode[] = []\n\n fragment.forEach(node => {\n // don’t touch text nodes\n if (node.isText) {\n list.push(node)\n\n return\n }\n\n // check for any other child nodes\n if (!types.includes(node.type.name)) {\n list.push(node.copy(removeId(node.content)))\n\n return\n }\n\n // remove id\n const nodeWithoutId = node.type.create(\n {\n ...node.attrs,\n [attributeName]: null,\n },\n removeId(node.content),\n node.marks,\n )\n list.push(nodeWithoutId)\n })\n\n return Fragment.from(list)\n }\n\n // reset check\n transformPasted = false\n\n return new Slice(removeId(slice.content), slice.openStart, slice.openEnd)\n },\n },\n }),\n ]\n },\n\n})\n"],"names":["Transform","Extension","uuidv4","findChildren","Plugin","PluginKey","findChildrenInRange","Fragment","Slice"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tiptap/extension-unique-id",
|
|
3
|
+
"description": "unique id extension for tiptap",
|
|
4
|
+
"version": "2.0.0-beta.1",
|
|
5
|
+
"homepage": "https://tiptap.dev",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"tiptap",
|
|
8
|
+
"tiptap extension"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"funding": {
|
|
12
|
+
"type": "github",
|
|
13
|
+
"url": "https://github.com/sponsors/ueberdosis"
|
|
14
|
+
},
|
|
15
|
+
"main": "dist/tiptap-extension-unique-id.cjs.js",
|
|
16
|
+
"umd": "dist/tiptap-extension-unique-id.umd.js",
|
|
17
|
+
"module": "dist/tiptap-extension-unique-id.esm.js",
|
|
18
|
+
"types": "dist/packages/extension-unique-id/src/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"src",
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@tiptap/core": "^2.0.0-beta.1"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@types/uuid": "^8.3.1",
|
|
28
|
+
"prosemirror-model": "^1.14.3",
|
|
29
|
+
"prosemirror-state": "^1.3.4",
|
|
30
|
+
"prosemirror-transform": "^1.3.2",
|
|
31
|
+
"uuid": "^8.3.2"
|
|
32
|
+
},
|
|
33
|
+
"gitHead": "23420cec8b845a4f13d75da37c451c43d0c4b66c"
|
|
34
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import removeDuplicates from './removeDuplicates'
|
|
2
|
+
|
|
3
|
+
export interface ArrayDifference {
|
|
4
|
+
added: any[],
|
|
5
|
+
removed: any[],
|
|
6
|
+
common: any[],
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Checks for added, removed and common items between two arrays.
|
|
11
|
+
*/
|
|
12
|
+
export default function arrayDifference(array1: any[], array2: any[]): ArrayDifference {
|
|
13
|
+
const uniqueCombinedArray = removeDuplicates([...array1, ...array2])
|
|
14
|
+
const data: ArrayDifference = {
|
|
15
|
+
added: [],
|
|
16
|
+
removed: [],
|
|
17
|
+
common: [],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
uniqueCombinedArray.forEach(item => {
|
|
21
|
+
if (!array1.includes(item) && array2.includes(item)) {
|
|
22
|
+
data.added.push(item)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (array1.includes(item) && !array2.includes(item)) {
|
|
26
|
+
data.removed.push(item)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (array1.includes(item) && array2.includes(item)) {
|
|
30
|
+
data.common.push(item)
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
return data
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Node as ProseMirrorNode } from 'prosemirror-model'
|
|
2
|
+
import { Transaction } from 'prosemirror-state'
|
|
3
|
+
import { Transform } from 'prosemirror-transform'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns a new `Transform` based on all steps of the passed transactions.
|
|
7
|
+
*/
|
|
8
|
+
export default function combineTransactionSteps(oldDoc: ProseMirrorNode, transactions: Transaction[]): Transform {
|
|
9
|
+
const transform = new Transform(oldDoc)
|
|
10
|
+
|
|
11
|
+
transactions.forEach(transaction => {
|
|
12
|
+
transaction.steps.forEach(step => {
|
|
13
|
+
transform.step(step)
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
return transform
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import removeDuplicates from './removeDuplicates'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns a list of duplicated items within an array.
|
|
5
|
+
*/
|
|
6
|
+
export default function findDuplicates(items: any[]): any[] {
|
|
7
|
+
const filtered = items.filter((el, index) => items.indexOf(el) !== index)
|
|
8
|
+
const duplicates = removeDuplicates(filtered)
|
|
9
|
+
|
|
10
|
+
return duplicates
|
|
11
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Transform, Step } from 'prosemirror-transform'
|
|
2
|
+
import removeDuplicates from './removeDuplicates'
|
|
3
|
+
|
|
4
|
+
export type ChangedRange = {
|
|
5
|
+
oldStart: number,
|
|
6
|
+
oldEnd: number,
|
|
7
|
+
newStart: number,
|
|
8
|
+
newEnd: number,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Removes duplicated ranges and ranges that are
|
|
13
|
+
* fully captured by other ranges.
|
|
14
|
+
*/
|
|
15
|
+
function simplifyChangedRanges(changes: ChangedRange[]): ChangedRange[] {
|
|
16
|
+
const uniqueChanges = removeDuplicates(changes)
|
|
17
|
+
|
|
18
|
+
return uniqueChanges.length === 1
|
|
19
|
+
? uniqueChanges
|
|
20
|
+
: uniqueChanges.filter((change, index) => {
|
|
21
|
+
const rest = uniqueChanges.filter((_, i) => i !== index)
|
|
22
|
+
|
|
23
|
+
return !rest.some(otherChange => {
|
|
24
|
+
return change.oldStart >= otherChange.oldStart
|
|
25
|
+
&& change.oldEnd <= otherChange.oldEnd
|
|
26
|
+
&& change.newStart >= otherChange.newStart
|
|
27
|
+
&& change.newEnd <= otherChange.newEnd
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns a list of changed ranges
|
|
34
|
+
* based on the first and last state of all steps.
|
|
35
|
+
*/
|
|
36
|
+
export default function getChangedRanges(transform: Transform): ChangedRange[] {
|
|
37
|
+
const { mapping, steps } = transform
|
|
38
|
+
const changes: ChangedRange[] = []
|
|
39
|
+
|
|
40
|
+
mapping.maps.forEach((stepMap, index) => {
|
|
41
|
+
// This accounts for step changes where no range was actually altered
|
|
42
|
+
// e.g. when setting a mark, node attribute, etc.
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
if (!stepMap.ranges.length) {
|
|
45
|
+
const step = steps[index] as Step & {
|
|
46
|
+
from?: number,
|
|
47
|
+
to?: number,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (step.from === undefined || step.to === undefined) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
changes.push({
|
|
55
|
+
oldStart: step.from,
|
|
56
|
+
oldEnd: step.to,
|
|
57
|
+
newStart: step.from,
|
|
58
|
+
newEnd: step.to,
|
|
59
|
+
})
|
|
60
|
+
} else {
|
|
61
|
+
stepMap.forEach((from, to) => {
|
|
62
|
+
const newStart = mapping.slice(index).map(from, -1)
|
|
63
|
+
const newEnd = mapping.slice(index).map(to)
|
|
64
|
+
const oldStart = mapping.invert().map(newStart, -1)
|
|
65
|
+
const oldEnd = mapping.invert().map(newEnd)
|
|
66
|
+
|
|
67
|
+
changes.push({
|
|
68
|
+
oldStart,
|
|
69
|
+
oldEnd,
|
|
70
|
+
newStart,
|
|
71
|
+
newEnd,
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
return simplifyChangedRanges(changes)
|
|
78
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes duplicated values within an array.
|
|
3
|
+
* Supports numbers, strings and objects.
|
|
4
|
+
*/
|
|
5
|
+
export default function removeDuplicates<T>(array: T[], by = JSON.stringify): T[] {
|
|
6
|
+
const seen: Record<any, any> = {}
|
|
7
|
+
|
|
8
|
+
return array.filter(item => {
|
|
9
|
+
const key = by(item)
|
|
10
|
+
|
|
11
|
+
return Object.prototype.hasOwnProperty.call(seen, key)
|
|
12
|
+
? false
|
|
13
|
+
: (seen[key] = true)
|
|
14
|
+
})
|
|
15
|
+
}
|
package/src/index.ts
ADDED
package/src/unique-id.ts
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { Extension, findChildren, findChildrenInRange } from '@tiptap/core'
|
|
2
|
+
import { Plugin, PluginKey, Transaction } from 'prosemirror-state'
|
|
3
|
+
import { Slice, Fragment, Node as ProseMirrorNode } from 'prosemirror-model'
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
5
|
+
import combineTransactionSteps from './helpers/combineTransactionSteps'
|
|
6
|
+
import getChangedRanges from './helpers/getChangedRanges'
|
|
7
|
+
import findDuplicates from './helpers/findDuplicates'
|
|
8
|
+
|
|
9
|
+
export interface UniqueIdOptions {
|
|
10
|
+
attributeName: string,
|
|
11
|
+
types: string[],
|
|
12
|
+
generateId: () => any,
|
|
13
|
+
filterTransaction: ((transaction: Transaction) => boolean) | null,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const UniqueId = Extension.create<UniqueIdOptions>({
|
|
17
|
+
name: 'uniqueId',
|
|
18
|
+
|
|
19
|
+
// we’ll set a very high priority to make sure this runs first
|
|
20
|
+
// and is compatible with `appendTransaction` hooks of other extensions
|
|
21
|
+
priority: 10000,
|
|
22
|
+
|
|
23
|
+
defaultOptions: {
|
|
24
|
+
attributeName: 'id',
|
|
25
|
+
types: [],
|
|
26
|
+
generateId: () => uuidv4(),
|
|
27
|
+
filterTransaction: null,
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
addGlobalAttributes() {
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
types: this.options.types,
|
|
34
|
+
attributes: {
|
|
35
|
+
[this.options.attributeName]: {
|
|
36
|
+
default: null,
|
|
37
|
+
parseHTML: element => element.getAttribute(`data-${this.options.attributeName}`),
|
|
38
|
+
renderHTML: attributes => {
|
|
39
|
+
if (!attributes[this.options.attributeName]) {
|
|
40
|
+
return {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
[`data-${this.options.attributeName}`]: attributes[this.options.attributeName],
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// check initial content for missing ids
|
|
54
|
+
onCreate() {
|
|
55
|
+
const { view, state } = this.editor
|
|
56
|
+
const { tr, doc } = state
|
|
57
|
+
const { types, attributeName, generateId } = this.options
|
|
58
|
+
const nodesWithoutId = findChildren(doc, node => {
|
|
59
|
+
return types.includes(node.type.name)
|
|
60
|
+
&& node.attrs[attributeName] === null
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
nodesWithoutId.forEach(({ node, pos }) => {
|
|
64
|
+
tr.setNodeMarkup(pos, undefined, {
|
|
65
|
+
...node.attrs,
|
|
66
|
+
[attributeName]: generateId(),
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
view.dispatch(tr)
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
addProseMirrorPlugins() {
|
|
74
|
+
let dragSourceElement: Element | null = null
|
|
75
|
+
let transformPasted = false
|
|
76
|
+
|
|
77
|
+
return [
|
|
78
|
+
new Plugin({
|
|
79
|
+
key: new PluginKey('uniqueId'),
|
|
80
|
+
|
|
81
|
+
appendTransaction: (transactions, oldState, newState) => {
|
|
82
|
+
const docChanges = transactions.some(transaction => transaction.docChanged)
|
|
83
|
+
&& !oldState.doc.eq(newState.doc)
|
|
84
|
+
const filterTransactions = this.options.filterTransaction
|
|
85
|
+
&& transactions.some(tr => !this.options.filterTransaction?.(tr))
|
|
86
|
+
|
|
87
|
+
if (!docChanges || filterTransactions) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const { tr } = newState
|
|
92
|
+
const { types, attributeName, generateId } = this.options
|
|
93
|
+
const transform = combineTransactionSteps(oldState.doc, transactions)
|
|
94
|
+
const { mapping } = transform
|
|
95
|
+
|
|
96
|
+
// get changed ranges based on the old state
|
|
97
|
+
const changes = getChangedRanges(transform)
|
|
98
|
+
|
|
99
|
+
changes.forEach(change => {
|
|
100
|
+
const newRange = {
|
|
101
|
+
from: change.newStart,
|
|
102
|
+
to: change.newEnd,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const newNodes = findChildrenInRange(newState.doc, newRange, node => {
|
|
106
|
+
return types.includes(node.type.name)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const newIds = newNodes
|
|
110
|
+
.map(({ node }) => node.attrs[attributeName])
|
|
111
|
+
.filter(id => id !== null)
|
|
112
|
+
|
|
113
|
+
const duplicatedNewIds = findDuplicates(newIds)
|
|
114
|
+
|
|
115
|
+
newNodes.forEach(({ node, pos }) => {
|
|
116
|
+
// instead of checking `node.attrs[attributeName]` directly
|
|
117
|
+
// we look at the current state of the node within `tr.doc`.
|
|
118
|
+
// this helps to prevent adding new ids to the same node
|
|
119
|
+
// if the node changed multiple times within one transaction
|
|
120
|
+
const id = tr.doc.nodeAt(pos)?.attrs[attributeName]
|
|
121
|
+
|
|
122
|
+
if (id === null) {
|
|
123
|
+
tr.setNodeMarkup(pos, undefined, {
|
|
124
|
+
...node.attrs,
|
|
125
|
+
[attributeName]: generateId(),
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// check if the node doesn’t exist in the old state
|
|
132
|
+
const { deleted } = mapping.invert().mapResult(pos)
|
|
133
|
+
const newNode = deleted && duplicatedNewIds.includes(id)
|
|
134
|
+
|
|
135
|
+
if (newNode) {
|
|
136
|
+
tr.setNodeMarkup(pos, undefined, {
|
|
137
|
+
...node.attrs,
|
|
138
|
+
[attributeName]: generateId(),
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
if (!tr.steps.length) {
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return tr
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
// we register a global drag handler to track the current drag source element
|
|
153
|
+
view(view) {
|
|
154
|
+
const handleDragstart = (event: DragEvent) => {
|
|
155
|
+
dragSourceElement = view.dom.parentElement?.contains(event.target as Element)
|
|
156
|
+
? view.dom.parentElement
|
|
157
|
+
: null
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
window.addEventListener('dragstart', handleDragstart)
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
destroy() {
|
|
164
|
+
window.removeEventListener('dragstart', handleDragstart)
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
props: {
|
|
170
|
+
// `handleDOMEvents` is called before `transformPasted`
|
|
171
|
+
// so we can do some checks before
|
|
172
|
+
handleDOMEvents: {
|
|
173
|
+
// only create new ids for dropped content while holding `alt`
|
|
174
|
+
// or content is dragged from another editor
|
|
175
|
+
drop: (view, event) => {
|
|
176
|
+
if (
|
|
177
|
+
dragSourceElement !== view.dom.parentElement
|
|
178
|
+
|| event.dataTransfer?.effectAllowed === 'copy'
|
|
179
|
+
) {
|
|
180
|
+
dragSourceElement = null
|
|
181
|
+
transformPasted = true
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return false
|
|
185
|
+
},
|
|
186
|
+
// always create new ids on pasted content
|
|
187
|
+
paste: () => {
|
|
188
|
+
transformPasted = true
|
|
189
|
+
|
|
190
|
+
return false
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
// we’ll remove ids for every pasted node
|
|
195
|
+
// so we can create a new one within `appendTransaction`
|
|
196
|
+
transformPasted: slice => {
|
|
197
|
+
if (!transformPasted) {
|
|
198
|
+
return slice
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const { types, attributeName } = this.options
|
|
202
|
+
const removeId = (fragment: Fragment): Fragment => {
|
|
203
|
+
const list: ProseMirrorNode[] = []
|
|
204
|
+
|
|
205
|
+
fragment.forEach(node => {
|
|
206
|
+
// don’t touch text nodes
|
|
207
|
+
if (node.isText) {
|
|
208
|
+
list.push(node)
|
|
209
|
+
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// check for any other child nodes
|
|
214
|
+
if (!types.includes(node.type.name)) {
|
|
215
|
+
list.push(node.copy(removeId(node.content)))
|
|
216
|
+
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// remove id
|
|
221
|
+
const nodeWithoutId = node.type.create(
|
|
222
|
+
{
|
|
223
|
+
...node.attrs,
|
|
224
|
+
[attributeName]: null,
|
|
225
|
+
},
|
|
226
|
+
removeId(node.content),
|
|
227
|
+
node.marks,
|
|
228
|
+
)
|
|
229
|
+
list.push(nodeWithoutId)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
return Fragment.from(list)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// reset check
|
|
236
|
+
transformPasted = false
|
|
237
|
+
|
|
238
|
+
return new Slice(removeId(slice.content), slice.openStart, slice.openEnd)
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
}),
|
|
242
|
+
]
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
})
|