@tiptap/core 2.2.0-rc.8 → 2.2.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.
@@ -3,10 +3,14 @@ import { Editor } from './Editor.js';
3
3
  import { Content, Range } from './types.js';
4
4
  export declare class NodePos {
5
5
  private resolvedPos;
6
+ private isBlock;
6
7
  private editor;
7
- constructor(pos: ResolvedPos, editor: Editor);
8
+ private get name();
9
+ constructor(pos: ResolvedPos, editor: Editor, isBlock?: boolean, node?: Node | null);
10
+ private currentNode;
8
11
  get node(): Node;
9
12
  get element(): HTMLElement;
13
+ actualDepth: number | null;
10
14
  get depth(): number;
11
15
  get pos(): number;
12
16
  get content(): Fragment;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/core",
3
3
  "description": "headless rich text editor",
4
- "version": "2.2.0-rc.8",
4
+ "version": "2.2.1",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -32,7 +32,7 @@
32
32
  "dist"
33
33
  ],
34
34
  "devDependencies": {
35
- "@tiptap/pm": "^2.2.0-rc.8"
35
+ "@tiptap/pm": "^2.2.1"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@tiptap/pm": "^2.0.0"
package/src/NodePos.ts CHANGED
@@ -8,23 +8,35 @@ import { Content, Range } from './types.js'
8
8
  export class NodePos {
9
9
  private resolvedPos: ResolvedPos
10
10
 
11
+ private isBlock: boolean
12
+
11
13
  private editor: Editor
12
14
 
13
- constructor(pos: ResolvedPos, editor: Editor) {
15
+ private get name(): string {
16
+ return this.node.type.name
17
+ }
18
+
19
+ constructor(pos: ResolvedPos, editor: Editor, isBlock = false, node: Node | null = null) {
20
+ this.isBlock = isBlock
14
21
  this.resolvedPos = pos
15
22
  this.editor = editor
23
+ this.currentNode = node
16
24
  }
17
25
 
26
+ private currentNode: Node | null = null
27
+
18
28
  get node(): Node {
19
- return this.resolvedPos.node()
29
+ return this.currentNode || this.resolvedPos.node()
20
30
  }
21
31
 
22
32
  get element(): HTMLElement {
23
33
  return this.editor.view.domAtPos(this.pos).node as HTMLElement
24
34
  }
25
35
 
36
+ public actualDepth: number | null = null
37
+
26
38
  get depth(): number {
27
- return this.resolvedPos.depth
39
+ return this.actualDepth ?? this.resolvedPos.depth
28
40
  }
29
41
 
30
42
  get pos(): number {
@@ -36,7 +48,20 @@ export class NodePos {
36
48
  }
37
49
 
38
50
  set content(content: Content) {
39
- this.editor.commands.insertContentAt({ from: this.from, to: this.to }, content)
51
+ let from = this.from
52
+ let to = this.to
53
+
54
+ if (this.isBlock) {
55
+ if (this.content.size === 0) {
56
+ console.error(`You can’t set content on a block node. Tried to set content on ${this.name} at ${this.pos}`)
57
+ return
58
+ }
59
+
60
+ from = this.from + 1
61
+ to = this.to - 1
62
+ }
63
+
64
+ this.editor.commands.insertContentAt({ from, to }, content)
40
65
  }
41
66
 
42
67
  get attributes() : { [key: string]: any } {
@@ -52,6 +77,10 @@ export class NodePos {
52
77
  }
53
78
 
54
79
  get from(): number {
80
+ if (this.isBlock) {
81
+ return this.pos
82
+ }
83
+
55
84
  return this.resolvedPos.start(this.resolvedPos.depth)
56
85
  }
57
86
 
@@ -63,6 +92,10 @@ export class NodePos {
63
92
  }
64
93
 
65
94
  get to(): number {
95
+ if (this.isBlock) {
96
+ return this.pos + this.size
97
+ }
98
+
66
99
  return this.resolvedPos.end(this.resolvedPos.depth) + (this.node.isText ? 0 : 1)
67
100
  }
68
101
 
@@ -78,7 +111,7 @@ export class NodePos {
78
111
  }
79
112
 
80
113
  get before(): NodePos | null {
81
- let $pos = this.resolvedPos.doc.resolve(this.from - 2)
114
+ let $pos = this.resolvedPos.doc.resolve(this.from - (this.isBlock ? 1 : 2))
82
115
 
83
116
  if ($pos.depth !== this.depth) {
84
117
  $pos = this.resolvedPos.doc.resolve(this.from - 3)
@@ -88,7 +121,7 @@ export class NodePos {
88
121
  }
89
122
 
90
123
  get after(): NodePos | null {
91
- let $pos = this.resolvedPos.doc.resolve(this.to + 2)
124
+ let $pos = this.resolvedPos.doc.resolve(this.to + (this.isBlock ? 2 : 1))
92
125
 
93
126
  if ($pos.depth !== this.depth) {
94
127
  $pos = this.resolvedPos.doc.resolve(this.to + 3)
@@ -101,14 +134,22 @@ export class NodePos {
101
134
  const children: NodePos[] = []
102
135
 
103
136
  this.node.content.forEach((node, offset) => {
104
- const targetPos = this.pos + offset + 1
137
+ const isBlock = node.isBlock && !node.isTextblock
138
+
139
+ const targetPos = this.pos + offset + (isBlock ? 0 : 1)
105
140
  const $pos = this.resolvedPos.doc.resolve(targetPos)
106
141
 
107
- if ($pos.depth === this.depth) {
142
+ if (!isBlock && $pos.depth <= this.depth) {
108
143
  return
109
144
  }
110
145
 
111
- children.push(new NodePos($pos, this.editor))
146
+ const childNodePos = new NodePos($pos, this.editor, isBlock, isBlock ? node : null)
147
+
148
+ if (isBlock) {
149
+ childNodePos.actualDepth = this.depth + 1
150
+ }
151
+
152
+ children.push(new NodePos($pos, this.editor, isBlock, isBlock ? node : null))
112
153
  })
113
154
 
114
155
  return children
@@ -160,14 +201,14 @@ export class NodePos {
160
201
  let nodes: NodePos[] = []
161
202
 
162
203
  // iterate through children recursively finding all nodes which match the selector with the node name
163
- if (!this.children || this.children.length === 0) {
204
+ if (this.isBlock || !this.children || this.children.length === 0) {
164
205
  return nodes
165
206
  }
166
207
 
167
- this.children.forEach(node => {
168
- if (node.node.type.name === selector) {
208
+ this.children.forEach(childPos => {
209
+ if (childPos.node.type.name === selector) {
169
210
  if (Object.keys(attributes).length > 0) {
170
- const nodeAttributes = node.node.attrs
211
+ const nodeAttributes = childPos.node.attrs
171
212
  const attrKeys = Object.keys(attributes)
172
213
 
173
214
  for (let index = 0; index < attrKeys.length; index += 1) {
@@ -179,14 +220,14 @@ export class NodePos {
179
220
  }
180
221
  }
181
222
 
182
- nodes.push(node)
223
+ nodes.push(childPos)
183
224
 
184
225
  if (firstItemOnly) {
185
226
  return
186
227
  }
187
228
  }
188
229
 
189
- nodes = nodes.concat(node.querySelectorAll(selector))
230
+ nodes = nodes.concat(childPos.querySelectorAll(selector))
190
231
  })
191
232
 
192
233
  return nodes
@@ -26,7 +26,7 @@ export function getMarksBetween(from: number, to: number, doc: ProseMirrorNode):
26
26
  })
27
27
  } else {
28
28
  doc.nodesBetween(from, to, (node, pos) => {
29
- if (!node || node.nodeSize === undefined) {
29
+ if (!node || node?.nodeSize === undefined) {
30
30
  return
31
31
  }
32
32
 
@@ -1,6 +1,24 @@
1
+ const removeWhitespaces = (node: HTMLElement) => {
2
+ const children = node.childNodes
3
+
4
+ for (let i = children.length - 1; i >= 0; i -= 1) {
5
+ const child = children[i]
6
+
7
+ if (child.nodeType === 3 && child.nodeValue && /^(\n\s\s|\n)$/.test(child.nodeValue)) {
8
+ node.removeChild(child)
9
+ } else if (child.nodeType === 1) {
10
+ removeWhitespaces(child as HTMLElement)
11
+ }
12
+ }
13
+
14
+ return node
15
+ }
16
+
1
17
  export function elementFromString(value: string): HTMLElement {
2
18
  // add a wrapper to preserve leading and trailing whitespace
3
19
  const wrappedValue = `<body>${value}</body>`
4
20
 
5
- return new window.DOMParser().parseFromString(wrappedValue, 'text/html').body
21
+ const html = new window.DOMParser().parseFromString(wrappedValue, 'text/html').body
22
+
23
+ return removeWhitespaces(html)
6
24
  }