@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/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
+ })