@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.
Files changed (41) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/demos/setup/helper.d.ts +2 -0
  3. package/dist/demos/setup/react.d.ts +3 -0
  4. package/dist/demos/setup/vue.d.ts +3 -0
  5. package/dist/demos/vite.config.d.ts +2 -0
  6. package/dist/packages/extension-drag-handle/src/drag-handle.d.ts +13 -0
  7. package/dist/packages/extension-drag-handle/src/helpers/cloneElement.d.ts +1 -0
  8. package/dist/packages/extension-drag-handle/src/helpers/dragHandler.d.ts +2 -0
  9. package/dist/packages/extension-drag-handle/src/helpers/getComputedStyle.d.ts +1 -0
  10. package/dist/packages/extension-drag-handle/src/helpers/getInnerCoords.d.ts +5 -0
  11. package/dist/packages/extension-drag-handle/src/helpers/minMax.d.ts +1 -0
  12. package/dist/packages/extension-drag-handle/src/helpers/removeNode.d.ts +1 -0
  13. package/dist/packages/extension-drag-handle/src/index.d.ts +3 -0
  14. package/dist/packages/extension-node-range/src/helpers/NodeRangeBookmark.d.ts +10 -0
  15. package/dist/packages/extension-node-range/src/helpers/NodeRangeSelection.d.ts +23 -0
  16. package/dist/packages/extension-node-range/src/helpers/getNodeRangeDecorations.d.ts +3 -0
  17. package/dist/packages/extension-node-range/src/helpers/getSelectionRanges.d.ts +3 -0
  18. package/dist/packages/extension-node-range/src/helpers/isNodeRangeSelection.d.ts +2 -0
  19. package/dist/packages/extension-node-range/src/index.d.ts +7 -0
  20. package/dist/packages/extension-node-range/src/node-range.d.ts +6 -0
  21. package/dist/packages/extension-unique-id/src/helpers/arrayDifference.d.ts +9 -0
  22. package/dist/packages/extension-unique-id/src/helpers/combineTransactionSteps.d.ts +7 -0
  23. package/dist/packages/extension-unique-id/src/helpers/findDuplicates.d.ts +4 -0
  24. package/dist/packages/extension-unique-id/src/helpers/getChangedRanges.d.ts +12 -0
  25. package/dist/packages/extension-unique-id/src/helpers/removeDuplicates.d.ts +8 -0
  26. package/dist/packages/extension-unique-id/src/index.d.ts +3 -0
  27. package/dist/packages/extension-unique-id/src/unique-id.d.ts +9 -0
  28. package/dist/tiptap-extension-unique-id.cjs.js +291 -0
  29. package/dist/tiptap-extension-unique-id.cjs.js.map +1 -0
  30. package/dist/tiptap-extension-unique-id.esm.js +286 -0
  31. package/dist/tiptap-extension-unique-id.esm.js.map +1 -0
  32. package/dist/tiptap-extension-unique-id.umd.js +291 -0
  33. package/dist/tiptap-extension-unique-id.umd.js.map +1 -0
  34. package/package.json +34 -0
  35. package/src/helpers/arrayDifference.ts +35 -0
  36. package/src/helpers/combineTransactionSteps.ts +18 -0
  37. package/src/helpers/findDuplicates.ts +11 -0
  38. package/src/helpers/getChangedRanges.ts +78 -0
  39. package/src/helpers/removeDuplicates.ts +15 -0
  40. package/src/index.ts +5 -0
  41. package/src/unique-id.ts +245 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tiptap-extension-unique-id.cjs.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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"}
@@ -0,0 +1,286 @@
1
+ import { Extension, findChildren, findChildrenInRange } from '@tiptap/core';
2
+ import { Plugin, PluginKey } from 'prosemirror-state';
3
+ import { Slice, Fragment } from 'prosemirror-model';
4
+ import { v4 } from 'uuid';
5
+ import { Transform } from 'prosemirror-transform';
6
+
7
+ /**
8
+ * Returns a new `Transform` based on all steps of the passed transactions.
9
+ */
10
+ function combineTransactionSteps(oldDoc, transactions) {
11
+ const transform = new Transform(oldDoc);
12
+ transactions.forEach(transaction => {
13
+ transaction.steps.forEach(step => {
14
+ transform.step(step);
15
+ });
16
+ });
17
+ return transform;
18
+ }
19
+
20
+ /**
21
+ * Removes duplicated values within an array.
22
+ * Supports numbers, strings and objects.
23
+ */
24
+ function removeDuplicates(array, by = JSON.stringify) {
25
+ const seen = {};
26
+ return array.filter(item => {
27
+ const key = by(item);
28
+ return Object.prototype.hasOwnProperty.call(seen, key)
29
+ ? false
30
+ : (seen[key] = true);
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Removes duplicated ranges and ranges that are
36
+ * fully captured by other ranges.
37
+ */
38
+ function simplifyChangedRanges(changes) {
39
+ const uniqueChanges = removeDuplicates(changes);
40
+ return uniqueChanges.length === 1
41
+ ? uniqueChanges
42
+ : uniqueChanges.filter((change, index) => {
43
+ const rest = uniqueChanges.filter((_, i) => i !== index);
44
+ return !rest.some(otherChange => {
45
+ return change.oldStart >= otherChange.oldStart
46
+ && change.oldEnd <= otherChange.oldEnd
47
+ && change.newStart >= otherChange.newStart
48
+ && change.newEnd <= otherChange.newEnd;
49
+ });
50
+ });
51
+ }
52
+ /**
53
+ * Returns a list of changed ranges
54
+ * based on the first and last state of all steps.
55
+ */
56
+ function getChangedRanges(transform) {
57
+ const { mapping, steps } = transform;
58
+ const changes = [];
59
+ mapping.maps.forEach((stepMap, index) => {
60
+ // This accounts for step changes where no range was actually altered
61
+ // e.g. when setting a mark, node attribute, etc.
62
+ // @ts-ignore
63
+ if (!stepMap.ranges.length) {
64
+ const step = steps[index];
65
+ if (step.from === undefined || step.to === undefined) {
66
+ return;
67
+ }
68
+ changes.push({
69
+ oldStart: step.from,
70
+ oldEnd: step.to,
71
+ newStart: step.from,
72
+ newEnd: step.to,
73
+ });
74
+ }
75
+ else {
76
+ stepMap.forEach((from, to) => {
77
+ const newStart = mapping.slice(index).map(from, -1);
78
+ const newEnd = mapping.slice(index).map(to);
79
+ const oldStart = mapping.invert().map(newStart, -1);
80
+ const oldEnd = mapping.invert().map(newEnd);
81
+ changes.push({
82
+ oldStart,
83
+ oldEnd,
84
+ newStart,
85
+ newEnd,
86
+ });
87
+ });
88
+ }
89
+ });
90
+ return simplifyChangedRanges(changes);
91
+ }
92
+
93
+ /**
94
+ * Returns a list of duplicated items within an array.
95
+ */
96
+ function findDuplicates(items) {
97
+ const filtered = items.filter((el, index) => items.indexOf(el) !== index);
98
+ const duplicates = removeDuplicates(filtered);
99
+ return duplicates;
100
+ }
101
+
102
+ const UniqueId = Extension.create({
103
+ name: 'uniqueId',
104
+ // we’ll set a very high priority to make sure this runs first
105
+ // and is compatible with `appendTransaction` hooks of other extensions
106
+ priority: 10000,
107
+ defaultOptions: {
108
+ attributeName: 'id',
109
+ types: [],
110
+ generateId: () => v4(),
111
+ filterTransaction: null,
112
+ },
113
+ addGlobalAttributes() {
114
+ return [
115
+ {
116
+ types: this.options.types,
117
+ attributes: {
118
+ [this.options.attributeName]: {
119
+ default: null,
120
+ parseHTML: element => element.getAttribute(`data-${this.options.attributeName}`),
121
+ renderHTML: attributes => {
122
+ if (!attributes[this.options.attributeName]) {
123
+ return {};
124
+ }
125
+ return {
126
+ [`data-${this.options.attributeName}`]: attributes[this.options.attributeName],
127
+ };
128
+ },
129
+ },
130
+ },
131
+ },
132
+ ];
133
+ },
134
+ // check initial content for missing ids
135
+ onCreate() {
136
+ const { view, state } = this.editor;
137
+ const { tr, doc } = state;
138
+ const { types, attributeName, generateId } = this.options;
139
+ const nodesWithoutId = findChildren(doc, node => {
140
+ return types.includes(node.type.name)
141
+ && node.attrs[attributeName] === null;
142
+ });
143
+ nodesWithoutId.forEach(({ node, pos }) => {
144
+ tr.setNodeMarkup(pos, undefined, {
145
+ ...node.attrs,
146
+ [attributeName]: generateId(),
147
+ });
148
+ });
149
+ view.dispatch(tr);
150
+ },
151
+ addProseMirrorPlugins() {
152
+ let dragSourceElement = null;
153
+ let transformPasted = false;
154
+ return [
155
+ new Plugin({
156
+ key: new PluginKey('uniqueId'),
157
+ appendTransaction: (transactions, oldState, newState) => {
158
+ const docChanges = transactions.some(transaction => transaction.docChanged)
159
+ && !oldState.doc.eq(newState.doc);
160
+ const filterTransactions = this.options.filterTransaction
161
+ && transactions.some(tr => !this.options.filterTransaction?.(tr));
162
+ if (!docChanges || filterTransactions) {
163
+ return;
164
+ }
165
+ const { tr } = newState;
166
+ const { types, attributeName, generateId } = this.options;
167
+ const transform = combineTransactionSteps(oldState.doc, transactions);
168
+ const { mapping } = transform;
169
+ // get changed ranges based on the old state
170
+ const changes = getChangedRanges(transform);
171
+ changes.forEach(change => {
172
+ const newRange = {
173
+ from: change.newStart,
174
+ to: change.newEnd,
175
+ };
176
+ const newNodes = findChildrenInRange(newState.doc, newRange, node => {
177
+ return types.includes(node.type.name);
178
+ });
179
+ const newIds = newNodes
180
+ .map(({ node }) => node.attrs[attributeName])
181
+ .filter(id => id !== null);
182
+ const duplicatedNewIds = findDuplicates(newIds);
183
+ newNodes.forEach(({ node, pos }) => {
184
+ // instead of checking `node.attrs[attributeName]` directly
185
+ // we look at the current state of the node within `tr.doc`.
186
+ // this helps to prevent adding new ids to the same node
187
+ // if the node changed multiple times within one transaction
188
+ const id = tr.doc.nodeAt(pos)?.attrs[attributeName];
189
+ if (id === null) {
190
+ tr.setNodeMarkup(pos, undefined, {
191
+ ...node.attrs,
192
+ [attributeName]: generateId(),
193
+ });
194
+ return;
195
+ }
196
+ // check if the node doesn’t exist in the old state
197
+ const { deleted } = mapping.invert().mapResult(pos);
198
+ const newNode = deleted && duplicatedNewIds.includes(id);
199
+ if (newNode) {
200
+ tr.setNodeMarkup(pos, undefined, {
201
+ ...node.attrs,
202
+ [attributeName]: generateId(),
203
+ });
204
+ }
205
+ });
206
+ });
207
+ if (!tr.steps.length) {
208
+ return;
209
+ }
210
+ return tr;
211
+ },
212
+ // we register a global drag handler to track the current drag source element
213
+ view(view) {
214
+ const handleDragstart = (event) => {
215
+ dragSourceElement = view.dom.parentElement?.contains(event.target)
216
+ ? view.dom.parentElement
217
+ : null;
218
+ };
219
+ window.addEventListener('dragstart', handleDragstart);
220
+ return {
221
+ destroy() {
222
+ window.removeEventListener('dragstart', handleDragstart);
223
+ },
224
+ };
225
+ },
226
+ props: {
227
+ // `handleDOMEvents` is called before `transformPasted`
228
+ // so we can do some checks before
229
+ handleDOMEvents: {
230
+ // only create new ids for dropped content while holding `alt`
231
+ // or content is dragged from another editor
232
+ drop: (view, event) => {
233
+ if (dragSourceElement !== view.dom.parentElement
234
+ || event.dataTransfer?.effectAllowed === 'copy') {
235
+ dragSourceElement = null;
236
+ transformPasted = true;
237
+ }
238
+ return false;
239
+ },
240
+ // always create new ids on pasted content
241
+ paste: () => {
242
+ transformPasted = true;
243
+ return false;
244
+ },
245
+ },
246
+ // we’ll remove ids for every pasted node
247
+ // so we can create a new one within `appendTransaction`
248
+ transformPasted: slice => {
249
+ if (!transformPasted) {
250
+ return slice;
251
+ }
252
+ const { types, attributeName } = this.options;
253
+ const removeId = (fragment) => {
254
+ const list = [];
255
+ fragment.forEach(node => {
256
+ // don’t touch text nodes
257
+ if (node.isText) {
258
+ list.push(node);
259
+ return;
260
+ }
261
+ // check for any other child nodes
262
+ if (!types.includes(node.type.name)) {
263
+ list.push(node.copy(removeId(node.content)));
264
+ return;
265
+ }
266
+ // remove id
267
+ const nodeWithoutId = node.type.create({
268
+ ...node.attrs,
269
+ [attributeName]: null,
270
+ }, removeId(node.content), node.marks);
271
+ list.push(nodeWithoutId);
272
+ });
273
+ return Fragment.from(list);
274
+ };
275
+ // reset check
276
+ transformPasted = false;
277
+ return new Slice(removeId(slice.content), slice.openStart, slice.openEnd);
278
+ },
279
+ },
280
+ }),
281
+ ];
282
+ },
283
+ });
284
+
285
+ export { UniqueId, UniqueId as default };
286
+ //# sourceMappingURL=tiptap-extension-unique-id.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tiptap-extension-unique-id.esm.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":["uuidv4"],"mappings":"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"}
@@ -0,0 +1,291 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tiptap/core'), require('prosemirror-state'), require('prosemirror-model'), require('uuid'), require('prosemirror-transform')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@tiptap/core', 'prosemirror-state', 'prosemirror-model', 'uuid', 'prosemirror-transform'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global['@tiptap/extension-unique-id'] = {}, global.core, global.prosemirrorState, global.prosemirrorModel, global.uuid, global.prosemirrorTransform));
5
+ }(this, (function (exports, core, prosemirrorState, prosemirrorModel, uuid, prosemirrorTransform) { 'use strict';
6
+
7
+ /**
8
+ * Returns a new `Transform` based on all steps of the passed transactions.
9
+ */
10
+ function combineTransactionSteps(oldDoc, transactions) {
11
+ const transform = new prosemirrorTransform.Transform(oldDoc);
12
+ transactions.forEach(transaction => {
13
+ transaction.steps.forEach(step => {
14
+ transform.step(step);
15
+ });
16
+ });
17
+ return transform;
18
+ }
19
+
20
+ /**
21
+ * Removes duplicated values within an array.
22
+ * Supports numbers, strings and objects.
23
+ */
24
+ function removeDuplicates(array, by = JSON.stringify) {
25
+ const seen = {};
26
+ return array.filter(item => {
27
+ const key = by(item);
28
+ return Object.prototype.hasOwnProperty.call(seen, key)
29
+ ? false
30
+ : (seen[key] = true);
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Removes duplicated ranges and ranges that are
36
+ * fully captured by other ranges.
37
+ */
38
+ function simplifyChangedRanges(changes) {
39
+ const uniqueChanges = removeDuplicates(changes);
40
+ return uniqueChanges.length === 1
41
+ ? uniqueChanges
42
+ : uniqueChanges.filter((change, index) => {
43
+ const rest = uniqueChanges.filter((_, i) => i !== index);
44
+ return !rest.some(otherChange => {
45
+ return change.oldStart >= otherChange.oldStart
46
+ && change.oldEnd <= otherChange.oldEnd
47
+ && change.newStart >= otherChange.newStart
48
+ && change.newEnd <= otherChange.newEnd;
49
+ });
50
+ });
51
+ }
52
+ /**
53
+ * Returns a list of changed ranges
54
+ * based on the first and last state of all steps.
55
+ */
56
+ function getChangedRanges(transform) {
57
+ const { mapping, steps } = transform;
58
+ const changes = [];
59
+ mapping.maps.forEach((stepMap, index) => {
60
+ // This accounts for step changes where no range was actually altered
61
+ // e.g. when setting a mark, node attribute, etc.
62
+ // @ts-ignore
63
+ if (!stepMap.ranges.length) {
64
+ const step = steps[index];
65
+ if (step.from === undefined || step.to === undefined) {
66
+ return;
67
+ }
68
+ changes.push({
69
+ oldStart: step.from,
70
+ oldEnd: step.to,
71
+ newStart: step.from,
72
+ newEnd: step.to,
73
+ });
74
+ }
75
+ else {
76
+ stepMap.forEach((from, to) => {
77
+ const newStart = mapping.slice(index).map(from, -1);
78
+ const newEnd = mapping.slice(index).map(to);
79
+ const oldStart = mapping.invert().map(newStart, -1);
80
+ const oldEnd = mapping.invert().map(newEnd);
81
+ changes.push({
82
+ oldStart,
83
+ oldEnd,
84
+ newStart,
85
+ newEnd,
86
+ });
87
+ });
88
+ }
89
+ });
90
+ return simplifyChangedRanges(changes);
91
+ }
92
+
93
+ /**
94
+ * Returns a list of duplicated items within an array.
95
+ */
96
+ function findDuplicates(items) {
97
+ const filtered = items.filter((el, index) => items.indexOf(el) !== index);
98
+ const duplicates = removeDuplicates(filtered);
99
+ return duplicates;
100
+ }
101
+
102
+ const UniqueId = core.Extension.create({
103
+ name: 'uniqueId',
104
+ // we’ll set a very high priority to make sure this runs first
105
+ // and is compatible with `appendTransaction` hooks of other extensions
106
+ priority: 10000,
107
+ defaultOptions: {
108
+ attributeName: 'id',
109
+ types: [],
110
+ generateId: () => uuid.v4(),
111
+ filterTransaction: null,
112
+ },
113
+ addGlobalAttributes() {
114
+ return [
115
+ {
116
+ types: this.options.types,
117
+ attributes: {
118
+ [this.options.attributeName]: {
119
+ default: null,
120
+ parseHTML: element => element.getAttribute(`data-${this.options.attributeName}`),
121
+ renderHTML: attributes => {
122
+ if (!attributes[this.options.attributeName]) {
123
+ return {};
124
+ }
125
+ return {
126
+ [`data-${this.options.attributeName}`]: attributes[this.options.attributeName],
127
+ };
128
+ },
129
+ },
130
+ },
131
+ },
132
+ ];
133
+ },
134
+ // check initial content for missing ids
135
+ onCreate() {
136
+ const { view, state } = this.editor;
137
+ const { tr, doc } = state;
138
+ const { types, attributeName, generateId } = this.options;
139
+ const nodesWithoutId = core.findChildren(doc, node => {
140
+ return types.includes(node.type.name)
141
+ && node.attrs[attributeName] === null;
142
+ });
143
+ nodesWithoutId.forEach(({ node, pos }) => {
144
+ tr.setNodeMarkup(pos, undefined, {
145
+ ...node.attrs,
146
+ [attributeName]: generateId(),
147
+ });
148
+ });
149
+ view.dispatch(tr);
150
+ },
151
+ addProseMirrorPlugins() {
152
+ let dragSourceElement = null;
153
+ let transformPasted = false;
154
+ return [
155
+ new prosemirrorState.Plugin({
156
+ key: new prosemirrorState.PluginKey('uniqueId'),
157
+ appendTransaction: (transactions, oldState, newState) => {
158
+ const docChanges = transactions.some(transaction => transaction.docChanged)
159
+ && !oldState.doc.eq(newState.doc);
160
+ const filterTransactions = this.options.filterTransaction
161
+ && transactions.some(tr => !this.options.filterTransaction?.(tr));
162
+ if (!docChanges || filterTransactions) {
163
+ return;
164
+ }
165
+ const { tr } = newState;
166
+ const { types, attributeName, generateId } = this.options;
167
+ const transform = combineTransactionSteps(oldState.doc, transactions);
168
+ const { mapping } = transform;
169
+ // get changed ranges based on the old state
170
+ const changes = getChangedRanges(transform);
171
+ changes.forEach(change => {
172
+ const newRange = {
173
+ from: change.newStart,
174
+ to: change.newEnd,
175
+ };
176
+ const newNodes = core.findChildrenInRange(newState.doc, newRange, node => {
177
+ return types.includes(node.type.name);
178
+ });
179
+ const newIds = newNodes
180
+ .map(({ node }) => node.attrs[attributeName])
181
+ .filter(id => id !== null);
182
+ const duplicatedNewIds = findDuplicates(newIds);
183
+ newNodes.forEach(({ node, pos }) => {
184
+ // instead of checking `node.attrs[attributeName]` directly
185
+ // we look at the current state of the node within `tr.doc`.
186
+ // this helps to prevent adding new ids to the same node
187
+ // if the node changed multiple times within one transaction
188
+ const id = tr.doc.nodeAt(pos)?.attrs[attributeName];
189
+ if (id === null) {
190
+ tr.setNodeMarkup(pos, undefined, {
191
+ ...node.attrs,
192
+ [attributeName]: generateId(),
193
+ });
194
+ return;
195
+ }
196
+ // check if the node doesn’t exist in the old state
197
+ const { deleted } = mapping.invert().mapResult(pos);
198
+ const newNode = deleted && duplicatedNewIds.includes(id);
199
+ if (newNode) {
200
+ tr.setNodeMarkup(pos, undefined, {
201
+ ...node.attrs,
202
+ [attributeName]: generateId(),
203
+ });
204
+ }
205
+ });
206
+ });
207
+ if (!tr.steps.length) {
208
+ return;
209
+ }
210
+ return tr;
211
+ },
212
+ // we register a global drag handler to track the current drag source element
213
+ view(view) {
214
+ const handleDragstart = (event) => {
215
+ dragSourceElement = view.dom.parentElement?.contains(event.target)
216
+ ? view.dom.parentElement
217
+ : null;
218
+ };
219
+ window.addEventListener('dragstart', handleDragstart);
220
+ return {
221
+ destroy() {
222
+ window.removeEventListener('dragstart', handleDragstart);
223
+ },
224
+ };
225
+ },
226
+ props: {
227
+ // `handleDOMEvents` is called before `transformPasted`
228
+ // so we can do some checks before
229
+ handleDOMEvents: {
230
+ // only create new ids for dropped content while holding `alt`
231
+ // or content is dragged from another editor
232
+ drop: (view, event) => {
233
+ if (dragSourceElement !== view.dom.parentElement
234
+ || event.dataTransfer?.effectAllowed === 'copy') {
235
+ dragSourceElement = null;
236
+ transformPasted = true;
237
+ }
238
+ return false;
239
+ },
240
+ // always create new ids on pasted content
241
+ paste: () => {
242
+ transformPasted = true;
243
+ return false;
244
+ },
245
+ },
246
+ // we’ll remove ids for every pasted node
247
+ // so we can create a new one within `appendTransaction`
248
+ transformPasted: slice => {
249
+ if (!transformPasted) {
250
+ return slice;
251
+ }
252
+ const { types, attributeName } = this.options;
253
+ const removeId = (fragment) => {
254
+ const list = [];
255
+ fragment.forEach(node => {
256
+ // don’t touch text nodes
257
+ if (node.isText) {
258
+ list.push(node);
259
+ return;
260
+ }
261
+ // check for any other child nodes
262
+ if (!types.includes(node.type.name)) {
263
+ list.push(node.copy(removeId(node.content)));
264
+ return;
265
+ }
266
+ // remove id
267
+ const nodeWithoutId = node.type.create({
268
+ ...node.attrs,
269
+ [attributeName]: null,
270
+ }, removeId(node.content), node.marks);
271
+ list.push(nodeWithoutId);
272
+ });
273
+ return prosemirrorModel.Fragment.from(list);
274
+ };
275
+ // reset check
276
+ transformPasted = false;
277
+ return new prosemirrorModel.Slice(removeId(slice.content), slice.openStart, slice.openEnd);
278
+ },
279
+ },
280
+ }),
281
+ ];
282
+ },
283
+ });
284
+
285
+ exports.UniqueId = UniqueId;
286
+ exports['default'] = UniqueId;
287
+
288
+ Object.defineProperty(exports, '__esModule', { value: true });
289
+
290
+ })));
291
+ //# sourceMappingURL=tiptap-extension-unique-id.umd.js.map