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