@tiptap/extension-node-range 2.22.0
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/dist/helpers/NodeRangeBookmark.d.ts +11 -0
- package/dist/helpers/NodeRangeBookmark.d.ts.map +1 -0
- package/dist/helpers/NodeRangeSelection.d.ts +24 -0
- package/dist/helpers/NodeRangeSelection.d.ts.map +1 -0
- package/dist/helpers/getNodeRangeDecorations.d.ts +4 -0
- package/dist/helpers/getNodeRangeDecorations.d.ts.map +1 -0
- package/dist/helpers/getSelectionRanges.d.ts +4 -0
- package/dist/helpers/getSelectionRanges.d.ts.map +1 -0
- package/dist/helpers/isNodeRangeSelection.d.ts +3 -0
- package/dist/helpers/isNodeRangeSelection.d.ts.map +1 -0
- package/dist/index.cjs +319 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +310 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +320 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/node-range.d.ts +7 -0
- package/dist/node-range.d.ts.map +1 -0
- package/package.json +48 -0
- package/src/helpers/NodeRangeBookmark.ts +28 -0
- package/src/helpers/NodeRangeSelection.ts +131 -0
- package/src/helpers/getNodeRangeDecorations.ts +28 -0
- package/src/helpers/getSelectionRanges.ts +38 -0
- package/src/helpers/isNodeRangeSelection.ts +5 -0
- package/src/index.ts +9 -0
- package/src/node-range.ts +207 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { NodeRange } from './node-range.js'
|
|
2
|
+
|
|
3
|
+
export * from './helpers/getNodeRangeDecorations.js'
|
|
4
|
+
export * from './helpers/getSelectionRanges.js'
|
|
5
|
+
export * from './helpers/isNodeRangeSelection.js'
|
|
6
|
+
export * from './helpers/NodeRangeSelection.js'
|
|
7
|
+
export * from './node-range.js'
|
|
8
|
+
|
|
9
|
+
export default NodeRange
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { Extension } from '@tiptap/core'
|
|
2
|
+
import { Plugin, PluginKey, SelectionRange } from '@tiptap/pm/state'
|
|
3
|
+
|
|
4
|
+
import { getNodeRangeDecorations } from './helpers/getNodeRangeDecorations.js'
|
|
5
|
+
import { getSelectionRanges } from './helpers/getSelectionRanges.js'
|
|
6
|
+
import { isNodeRangeSelection } from './helpers/isNodeRangeSelection.js'
|
|
7
|
+
import { NodeRangeSelection } from './helpers/NodeRangeSelection.js'
|
|
8
|
+
|
|
9
|
+
export interface NodeRangeOptions {
|
|
10
|
+
depth: number | undefined,
|
|
11
|
+
key: 'Shift' | 'Control' | 'Alt' | 'Meta' | 'Mod' | null | undefined,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const NodeRange = Extension.create<NodeRangeOptions>({
|
|
15
|
+
name: 'nodeRange',
|
|
16
|
+
|
|
17
|
+
addOptions() {
|
|
18
|
+
return {
|
|
19
|
+
depth: undefined,
|
|
20
|
+
key: 'Mod',
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
addKeyboardShortcuts() {
|
|
25
|
+
return {
|
|
26
|
+
// extend NodeRangeSelection upwards
|
|
27
|
+
'Shift-ArrowUp': ({ editor }) => {
|
|
28
|
+
const { depth } = this.options
|
|
29
|
+
const { view, state } = editor
|
|
30
|
+
const { doc, selection, tr } = state
|
|
31
|
+
const { anchor, head } = selection
|
|
32
|
+
|
|
33
|
+
if (!isNodeRangeSelection(selection)) {
|
|
34
|
+
const nodeRangeSelection = NodeRangeSelection.create(doc, anchor, head, depth, -1)
|
|
35
|
+
|
|
36
|
+
tr.setSelection(nodeRangeSelection)
|
|
37
|
+
view.dispatch(tr)
|
|
38
|
+
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const nodeRangeSelection = selection.extendBackwards()
|
|
43
|
+
|
|
44
|
+
tr.setSelection(nodeRangeSelection)
|
|
45
|
+
view.dispatch(tr)
|
|
46
|
+
|
|
47
|
+
return true
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// extend NodeRangeSelection downwards
|
|
51
|
+
'Shift-ArrowDown': ({ editor }) => {
|
|
52
|
+
const { depth } = this.options
|
|
53
|
+
const { view, state } = editor
|
|
54
|
+
const { doc, selection, tr } = state
|
|
55
|
+
const { anchor, head } = selection
|
|
56
|
+
|
|
57
|
+
if (!isNodeRangeSelection(selection)) {
|
|
58
|
+
const nodeRangeSelection = NodeRangeSelection.create(doc, anchor, head, depth)
|
|
59
|
+
|
|
60
|
+
tr.setSelection(nodeRangeSelection)
|
|
61
|
+
view.dispatch(tr)
|
|
62
|
+
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const nodeRangeSelection = selection.extendForwards()
|
|
67
|
+
|
|
68
|
+
tr.setSelection(nodeRangeSelection)
|
|
69
|
+
view.dispatch(tr)
|
|
70
|
+
|
|
71
|
+
return true
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// add `NodeRangeSelection` to all nodes
|
|
75
|
+
'Mod-a': ({ editor }) => {
|
|
76
|
+
const { depth } = this.options
|
|
77
|
+
const { view, state } = editor
|
|
78
|
+
const { doc, tr } = state
|
|
79
|
+
const nodeRangeSelection = NodeRangeSelection.create(doc, 0, doc.content.size, depth)
|
|
80
|
+
|
|
81
|
+
tr.setSelection(nodeRangeSelection)
|
|
82
|
+
view.dispatch(tr)
|
|
83
|
+
|
|
84
|
+
return true
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
onSelectionUpdate() {
|
|
90
|
+
const { selection } = this.editor.state
|
|
91
|
+
|
|
92
|
+
if (isNodeRangeSelection(selection)) {
|
|
93
|
+
this.editor.view.dom.classList.add('ProseMirror-noderangeselection')
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
addProseMirrorPlugins() {
|
|
98
|
+
let hideTextSelection = false
|
|
99
|
+
let activeMouseSelection = false
|
|
100
|
+
|
|
101
|
+
return [
|
|
102
|
+
new Plugin({
|
|
103
|
+
key: new PluginKey('nodeRange'),
|
|
104
|
+
|
|
105
|
+
props: {
|
|
106
|
+
attributes: () => {
|
|
107
|
+
if (hideTextSelection) {
|
|
108
|
+
return {
|
|
109
|
+
class: 'ProseMirror-noderangeselection',
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { class: '' }
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
handleDOMEvents: {
|
|
117
|
+
mousedown: (view, event) => {
|
|
118
|
+
const { key } = this.options
|
|
119
|
+
const isMac = /Mac/.test(navigator.platform)
|
|
120
|
+
const isShift = !!event.shiftKey
|
|
121
|
+
const isControl = !!event.ctrlKey
|
|
122
|
+
const isAlt = !!event.altKey
|
|
123
|
+
const isMeta = !!event.metaKey
|
|
124
|
+
const isMod = isMac
|
|
125
|
+
? isMeta
|
|
126
|
+
: isControl
|
|
127
|
+
|
|
128
|
+
if (
|
|
129
|
+
key === null
|
|
130
|
+
|| key === undefined
|
|
131
|
+
|| (key === 'Shift' && isShift)
|
|
132
|
+
|| (key === 'Control' && isControl)
|
|
133
|
+
|| (key === 'Alt' && isAlt)
|
|
134
|
+
|| (key === 'Meta' && isMeta)
|
|
135
|
+
|| (key === 'Mod' && isMod)
|
|
136
|
+
) {
|
|
137
|
+
activeMouseSelection = true
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!activeMouseSelection) {
|
|
141
|
+
return false
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
document.addEventListener('mouseup', () => {
|
|
145
|
+
activeMouseSelection = false
|
|
146
|
+
|
|
147
|
+
const { state } = view
|
|
148
|
+
const { doc, selection, tr } = state
|
|
149
|
+
const { $anchor, $head } = selection
|
|
150
|
+
|
|
151
|
+
if ($anchor.sameParent($head)) {
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const nodeRangeSelection = NodeRangeSelection.create(doc, $anchor.pos, $head.pos, this.options.depth)
|
|
156
|
+
|
|
157
|
+
tr.setSelection(nodeRangeSelection)
|
|
158
|
+
view.dispatch(tr)
|
|
159
|
+
}, { once: true })
|
|
160
|
+
|
|
161
|
+
return false
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// when selecting some text we want to render some decorations
|
|
166
|
+
// to preview a `NodeRangeSelection`
|
|
167
|
+
decorations: state => {
|
|
168
|
+
const { selection } = state
|
|
169
|
+
const isNodeRange = isNodeRangeSelection(selection)
|
|
170
|
+
|
|
171
|
+
hideTextSelection = false
|
|
172
|
+
|
|
173
|
+
if (!activeMouseSelection) {
|
|
174
|
+
if (!isNodeRange) {
|
|
175
|
+
return null
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
hideTextSelection = true
|
|
179
|
+
|
|
180
|
+
return getNodeRangeDecorations(selection.ranges as SelectionRange[])
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const { $from, $to } = selection
|
|
184
|
+
|
|
185
|
+
// selection is probably in the same node like a paragraph
|
|
186
|
+
// so we don’t render decorations and show
|
|
187
|
+
// a simple text selection instead
|
|
188
|
+
if (!isNodeRange && $from.sameParent($to)) {
|
|
189
|
+
return null
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// try to calculate some node ranges
|
|
193
|
+
const nodeRanges = getSelectionRanges($from, $to, this.options.depth)
|
|
194
|
+
|
|
195
|
+
if (!nodeRanges.length) {
|
|
196
|
+
return null
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
hideTextSelection = true
|
|
200
|
+
|
|
201
|
+
return getNodeRangeDecorations(nodeRanges)
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
}),
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
})
|