@atlaskit/editor-plugin-expand 1.3.1 → 1.3.2
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/CHANGELOG.md +6 -0
- package/dist/cjs/singlePlayerExpand/commands.js +134 -0
- package/dist/cjs/singlePlayerExpand/node-views/index.js +381 -0
- package/dist/cjs/singlePlayerExpand/plugin.js +72 -9
- package/dist/cjs/singlePlayerExpand/pm-plugins/keymap.js +121 -0
- package/dist/cjs/singlePlayerExpand/pm-plugins/main.js +68 -0
- package/dist/cjs/singlePlayerExpand/toolbar.js +59 -0
- package/dist/cjs/singlePlayerExpand/ui/ExpandButton.js +87 -0
- package/dist/cjs/singlePlayerExpand/ui/NodeView.js +59 -0
- package/dist/cjs/singlePlayerExpand/utils.js +35 -0
- package/dist/es2019/index.js +0 -2
- package/dist/es2019/legacyExpand/pm-plugins/keymap.js +1 -2
- package/dist/es2019/singlePlayerExpand/commands.js +118 -0
- package/dist/es2019/singlePlayerExpand/node-views/index.js +370 -0
- package/dist/es2019/singlePlayerExpand/plugin.js +69 -10
- package/dist/es2019/singlePlayerExpand/pm-plugins/keymap.js +137 -0
- package/dist/es2019/singlePlayerExpand/pm-plugins/main.js +53 -0
- package/dist/es2019/singlePlayerExpand/toolbar.js +51 -0
- package/dist/es2019/singlePlayerExpand/ui/ExpandButton.js +77 -0
- package/dist/es2019/singlePlayerExpand/ui/NodeView.js +52 -0
- package/dist/es2019/singlePlayerExpand/utils.js +5 -0
- package/dist/esm/index.js +0 -2
- package/dist/esm/legacyExpand/pm-plugins/keymap.js +1 -2
- package/dist/esm/singlePlayerExpand/commands.js +128 -0
- package/dist/esm/singlePlayerExpand/node-views/index.js +373 -0
- package/dist/esm/singlePlayerExpand/plugin.js +71 -10
- package/dist/esm/singlePlayerExpand/pm-plugins/keymap.js +115 -0
- package/dist/esm/singlePlayerExpand/pm-plugins/main.js +60 -0
- package/dist/esm/singlePlayerExpand/toolbar.js +52 -0
- package/dist/esm/singlePlayerExpand/ui/ExpandButton.js +77 -0
- package/dist/esm/singlePlayerExpand/ui/NodeView.js +53 -0
- package/dist/esm/singlePlayerExpand/utils.js +5 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/legacyExpand/nodeviews/index.d.ts +1 -1
- package/dist/types/legacyExpand/plugin.d.ts +1 -1
- package/dist/types/legacyExpand/pm-plugins/keymap.d.ts +1 -1
- package/dist/types/legacyExpand/pm-plugins/main.d.ts +2 -2
- package/dist/types/legacyExpand/pm-plugins/plugin-factory.d.ts +1 -1
- package/dist/types/legacyExpand/reducer.d.ts +1 -1
- package/dist/types/legacyExpand/toolbar.d.ts +1 -1
- package/dist/types/plugin.d.ts +1 -1
- package/dist/types/singlePlayerExpand/commands.d.ts +15 -0
- package/dist/types/singlePlayerExpand/node-views/index.d.ts +51 -0
- package/dist/types/singlePlayerExpand/plugin.d.ts +1 -1
- package/dist/types/singlePlayerExpand/pm-plugins/keymap.d.ts +6 -0
- package/dist/types/singlePlayerExpand/pm-plugins/main.d.ts +9 -0
- package/dist/types/singlePlayerExpand/toolbar.d.ts +3 -0
- package/dist/types/singlePlayerExpand/ui/ExpandButton.d.ts +13 -0
- package/dist/types/singlePlayerExpand/ui/NodeView.d.ts +5 -0
- package/dist/types/singlePlayerExpand/utils.d.ts +3 -0
- package/dist/{types-ts4.5/legacyExpand → types}/types.d.ts +1 -1
- package/dist/types-ts4.5/index.d.ts +1 -1
- package/dist/types-ts4.5/legacyExpand/nodeviews/index.d.ts +1 -1
- package/dist/types-ts4.5/legacyExpand/plugin.d.ts +1 -1
- package/dist/types-ts4.5/legacyExpand/pm-plugins/keymap.d.ts +1 -1
- package/dist/types-ts4.5/legacyExpand/pm-plugins/main.d.ts +2 -2
- package/dist/types-ts4.5/legacyExpand/pm-plugins/plugin-factory.d.ts +1 -1
- package/dist/types-ts4.5/legacyExpand/reducer.d.ts +1 -1
- package/dist/types-ts4.5/legacyExpand/toolbar.d.ts +1 -1
- package/dist/types-ts4.5/plugin.d.ts +1 -1
- package/dist/types-ts4.5/singlePlayerExpand/commands.d.ts +15 -0
- package/dist/types-ts4.5/singlePlayerExpand/node-views/index.d.ts +51 -0
- package/dist/types-ts4.5/singlePlayerExpand/plugin.d.ts +1 -1
- package/dist/types-ts4.5/singlePlayerExpand/pm-plugins/keymap.d.ts +6 -0
- package/dist/types-ts4.5/singlePlayerExpand/pm-plugins/main.d.ts +9 -0
- package/dist/types-ts4.5/singlePlayerExpand/toolbar.d.ts +3 -0
- package/dist/types-ts4.5/singlePlayerExpand/ui/ExpandButton.d.ts +13 -0
- package/dist/types-ts4.5/singlePlayerExpand/ui/NodeView.d.ts +5 -0
- package/dist/types-ts4.5/singlePlayerExpand/utils.d.ts +3 -0
- package/dist/{types/legacyExpand → types-ts4.5}/types.d.ts +1 -1
- package/package.json +1 -1
- /package/dist/cjs/{legacyExpand/types.js → types.js} +0 -0
- /package/dist/es2019/{legacyExpand/types.js → types.js} +0 -0
- /package/dist/esm/{legacyExpand/types.js → types.js} +0 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import { keyName } from 'w3c-keyname';
|
|
4
|
+
import { GapCursorSelection, RelativeSelectionPos, Side } from '@atlaskit/editor-common/selection';
|
|
5
|
+
import { expandClassNames } from '@atlaskit/editor-common/styles';
|
|
6
|
+
import { closestElement, isEmptyNode } from '@atlaskit/editor-common/utils';
|
|
7
|
+
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
8
|
+
import { NodeSelection, Selection } from '@atlaskit/editor-prosemirror/state';
|
|
9
|
+
import { deleteExpand, setSelectionInsideExpand, updateExpandTitle } from '../commands';
|
|
10
|
+
import { renderIcon, toDOM } from '../ui/NodeView';
|
|
11
|
+
export class ExpandNodeView {
|
|
12
|
+
constructor(node, view, getPos, getIntl, isMobile, selectNearNode, api, allowInteractiveExpand = true, __livePage = false) {
|
|
13
|
+
_defineProperty(this, "allowInteractiveExpand", true);
|
|
14
|
+
_defineProperty(this, "isMobile", false);
|
|
15
|
+
_defineProperty(this, "focusTitle", () => {
|
|
16
|
+
if (this.input) {
|
|
17
|
+
const {
|
|
18
|
+
state,
|
|
19
|
+
dispatch
|
|
20
|
+
} = this.view;
|
|
21
|
+
if (this.selectNearNode) {
|
|
22
|
+
const tr = this.selectNearNode({
|
|
23
|
+
selectionRelativeToNode: RelativeSelectionPos.Start
|
|
24
|
+
})(state);
|
|
25
|
+
if (dispatch) {
|
|
26
|
+
dispatch(tr);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const pos = this.getPos();
|
|
30
|
+
if (typeof pos === 'number') {
|
|
31
|
+
setSelectionInsideExpand(pos)(state, dispatch, this.view);
|
|
32
|
+
}
|
|
33
|
+
this.input.focus();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
_defineProperty(this, "handleIconKeyDown", event => {
|
|
37
|
+
switch (keyName(event)) {
|
|
38
|
+
case 'Tab':
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
this.focusTitle();
|
|
41
|
+
break;
|
|
42
|
+
case 'Enter':
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
this.handleClick(event);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
_defineProperty(this, "handleClick", event => {
|
|
49
|
+
const pos = this.getPos();
|
|
50
|
+
if (typeof pos !== 'number') {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const target = event.target;
|
|
54
|
+
if (closestElement(target, `.${expandClassNames.icon}`)) {
|
|
55
|
+
if (!this.allowInteractiveExpand) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
event.stopPropagation();
|
|
59
|
+
|
|
60
|
+
// We blur the editorView, to prevent any keyboard showing on mobile
|
|
61
|
+
// When we're interacting with the expand toggle
|
|
62
|
+
if (this.view.dom instanceof HTMLElement) {
|
|
63
|
+
this.view.dom.blur();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// TODO https://product-fabric.atlassian.net/browse/ED-22841
|
|
67
|
+
// call toggleExpandExpanded
|
|
68
|
+
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (target === this.input) {
|
|
72
|
+
event.stopPropagation();
|
|
73
|
+
this.focusTitle();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
_defineProperty(this, "handleInput", event => {
|
|
78
|
+
const pos = this.getPos();
|
|
79
|
+
if (typeof pos !== 'number') {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const target = event.target;
|
|
83
|
+
if (target === this.input) {
|
|
84
|
+
event.stopPropagation();
|
|
85
|
+
const {
|
|
86
|
+
state,
|
|
87
|
+
dispatch
|
|
88
|
+
} = this.view;
|
|
89
|
+
updateExpandTitle({
|
|
90
|
+
title: target.value,
|
|
91
|
+
pos,
|
|
92
|
+
nodeType: this.node.type
|
|
93
|
+
})(state, dispatch);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
_defineProperty(this, "handleFocus", event => {
|
|
97
|
+
event.stopImmediatePropagation();
|
|
98
|
+
});
|
|
99
|
+
_defineProperty(this, "handleTitleKeydown", event => {
|
|
100
|
+
switch (keyName(event)) {
|
|
101
|
+
case 'Enter':
|
|
102
|
+
// TO-DO
|
|
103
|
+
break;
|
|
104
|
+
case 'Tab':
|
|
105
|
+
case 'ArrowDown':
|
|
106
|
+
this.moveToOutsideOfTitle(event);
|
|
107
|
+
break;
|
|
108
|
+
case 'ArrowRight':
|
|
109
|
+
this.handleArrowRightFromTitle(event);
|
|
110
|
+
break;
|
|
111
|
+
case 'ArrowLeft':
|
|
112
|
+
this.handleArrowLeftFromTitle(event);
|
|
113
|
+
break;
|
|
114
|
+
case 'ArrowUp':
|
|
115
|
+
this.setLeftGapCursor(event);
|
|
116
|
+
break;
|
|
117
|
+
case 'Backspace':
|
|
118
|
+
this.deleteEmptyExpand();
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
_defineProperty(this, "deleteEmptyExpand", () => {
|
|
123
|
+
const {
|
|
124
|
+
state
|
|
125
|
+
} = this.view;
|
|
126
|
+
const expandNode = this.node;
|
|
127
|
+
if (expandNode && isEmptyNode(state.schema)(expandNode)) {
|
|
128
|
+
var _this$api, _this$api$analytics;
|
|
129
|
+
deleteExpand((_this$api = this.api) === null || _this$api === void 0 ? void 0 : (_this$api$analytics = _this$api.analytics) === null || _this$api$analytics === void 0 ? void 0 : _this$api$analytics.actions)(state, this.view.dispatch);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
_defineProperty(this, "moveToOutsideOfTitle", event => {
|
|
133
|
+
event.preventDefault();
|
|
134
|
+
const {
|
|
135
|
+
state,
|
|
136
|
+
dispatch
|
|
137
|
+
} = this.view;
|
|
138
|
+
const expandPos = this.getPos();
|
|
139
|
+
if (typeof expandPos !== 'number') {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
let pos = expandPos;
|
|
143
|
+
if (this.isCollapsed()) {
|
|
144
|
+
pos = expandPos + this.node.nodeSize;
|
|
145
|
+
}
|
|
146
|
+
const resolvedPos = state.doc.resolve(pos);
|
|
147
|
+
if (!resolvedPos) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (this.isCollapsed() && resolvedPos.nodeAfter && ['expand', 'nestedExpand'].indexOf(resolvedPos.nodeAfter.type.name) > -1) {
|
|
151
|
+
return this.setRightGapCursor(event);
|
|
152
|
+
}
|
|
153
|
+
const sel = Selection.findFrom(resolvedPos, 1, true);
|
|
154
|
+
if (sel) {
|
|
155
|
+
// If the input has focus, ProseMirror doesn't
|
|
156
|
+
// Give PM focus back before changing our selection
|
|
157
|
+
this.view.focus();
|
|
158
|
+
dispatch(state.tr.setSelection(sel));
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
// TODO: https://product-fabric.atlassian.net/browse/ED-22841
|
|
162
|
+
_defineProperty(this, "isCollapsed", () => {
|
|
163
|
+
return false;
|
|
164
|
+
});
|
|
165
|
+
_defineProperty(this, "setRightGapCursor", event => {
|
|
166
|
+
if (!this.input) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const pos = this.getPos();
|
|
170
|
+
if (typeof pos !== 'number') {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const {
|
|
174
|
+
value,
|
|
175
|
+
selectionStart,
|
|
176
|
+
selectionEnd
|
|
177
|
+
} = this.input;
|
|
178
|
+
if (selectionStart === selectionEnd && selectionStart === value.length) {
|
|
179
|
+
const {
|
|
180
|
+
state,
|
|
181
|
+
dispatch
|
|
182
|
+
} = this.view;
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
this.view.focus();
|
|
185
|
+
dispatch(state.tr.setSelection(new GapCursorSelection(state.doc.resolve(this.node.nodeSize + pos), Side.RIGHT)));
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
_defineProperty(this, "setLeftGapCursor", event => {
|
|
189
|
+
if (!this.input) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const pos = this.getPos();
|
|
193
|
+
if (typeof pos !== 'number') {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const {
|
|
197
|
+
selectionStart,
|
|
198
|
+
selectionEnd
|
|
199
|
+
} = this.input;
|
|
200
|
+
if (selectionStart === selectionEnd && selectionStart === 0) {
|
|
201
|
+
event.preventDefault();
|
|
202
|
+
const {
|
|
203
|
+
state,
|
|
204
|
+
dispatch
|
|
205
|
+
} = this.view;
|
|
206
|
+
this.view.focus();
|
|
207
|
+
dispatch(state.tr.setSelection(new GapCursorSelection(state.doc.resolve(pos), Side.LEFT)));
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
_defineProperty(this, "handleArrowRightFromTitle", event => {
|
|
211
|
+
if (!this.input || !this.selectNearNode) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const pos = this.getPos();
|
|
215
|
+
if (typeof pos !== 'number') {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const {
|
|
219
|
+
value,
|
|
220
|
+
selectionStart,
|
|
221
|
+
selectionEnd
|
|
222
|
+
} = this.input;
|
|
223
|
+
if (selectionStart === selectionEnd && selectionStart === value.length) {
|
|
224
|
+
event.preventDefault();
|
|
225
|
+
const {
|
|
226
|
+
state,
|
|
227
|
+
dispatch
|
|
228
|
+
} = this.view;
|
|
229
|
+
this.view.focus();
|
|
230
|
+
const tr = this.selectNearNode({
|
|
231
|
+
selectionRelativeToNode: RelativeSelectionPos.End,
|
|
232
|
+
selection: NodeSelection.create(state.doc, pos)
|
|
233
|
+
})(state);
|
|
234
|
+
if (dispatch) {
|
|
235
|
+
dispatch(tr);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
_defineProperty(this, "handleArrowLeftFromTitle", event => {
|
|
240
|
+
if (!this.input || !this.selectNearNode) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const pos = this.getPos();
|
|
244
|
+
if (typeof pos !== 'number') {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const {
|
|
248
|
+
selectionStart,
|
|
249
|
+
selectionEnd
|
|
250
|
+
} = this.input;
|
|
251
|
+
if (selectionStart === selectionEnd && selectionStart === 0) {
|
|
252
|
+
var _this$api2, _this$api2$selection;
|
|
253
|
+
event.preventDefault();
|
|
254
|
+
const {
|
|
255
|
+
state,
|
|
256
|
+
dispatch
|
|
257
|
+
} = this.view;
|
|
258
|
+
this.view.focus();
|
|
259
|
+
const selectionSharedState = ((_this$api2 = this.api) === null || _this$api2 === void 0 ? void 0 : (_this$api2$selection = _this$api2.selection) === null || _this$api2$selection === void 0 ? void 0 : _this$api2$selection.sharedState.currentState()) || {};
|
|
260
|
+
// selectionRelativeToNode is undefined when user clicked to select node, then hit left to get focus in title
|
|
261
|
+
// This is a special case where we want to bypass node selection and jump straight to gap cursor
|
|
262
|
+
if ((selectionSharedState === null || selectionSharedState === void 0 ? void 0 : selectionSharedState.selectionRelativeToNode) === undefined) {
|
|
263
|
+
const tr = this.selectNearNode({
|
|
264
|
+
selectionRelativeToNode: undefined,
|
|
265
|
+
selection: new GapCursorSelection(state.doc.resolve(pos), Side.LEFT)
|
|
266
|
+
})(state);
|
|
267
|
+
if (dispatch) {
|
|
268
|
+
dispatch(tr);
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
const tr = this.selectNearNode({
|
|
272
|
+
selectionRelativeToNode: RelativeSelectionPos.Start,
|
|
273
|
+
selection: NodeSelection.create(state.doc, pos)
|
|
274
|
+
})(state);
|
|
275
|
+
if (dispatch) {
|
|
276
|
+
dispatch(tr);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
this.selectNearNode = selectNearNode;
|
|
282
|
+
this.__livePage = __livePage;
|
|
283
|
+
this.intl = getIntl();
|
|
284
|
+
const {
|
|
285
|
+
dom,
|
|
286
|
+
contentDOM
|
|
287
|
+
} = DOMSerializer.renderSpec(document, toDOM(node, this.__livePage, this.intl));
|
|
288
|
+
this.allowInteractiveExpand = allowInteractiveExpand;
|
|
289
|
+
this.getPos = getPos;
|
|
290
|
+
this.view = view;
|
|
291
|
+
this.node = node;
|
|
292
|
+
this.dom = dom;
|
|
293
|
+
this.contentDOM = contentDOM;
|
|
294
|
+
this.isMobile = isMobile;
|
|
295
|
+
this.api = api;
|
|
296
|
+
this.icon = this.dom.querySelector(`.${expandClassNames.icon}`);
|
|
297
|
+
this.input = this.dom.querySelector(`.${expandClassNames.titleInput}`);
|
|
298
|
+
this.titleContainer = this.dom.querySelector(`.${expandClassNames.titleContainer}`);
|
|
299
|
+
renderIcon(this.icon, this.allowInteractiveExpand, this.intl);
|
|
300
|
+
if (!this.input || !this.titleContainer || !this.icon) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Add event listeners
|
|
305
|
+
/* eslint-disable @repo/internal/dom-events/no-unsafe-event-listeners*/
|
|
306
|
+
this.dom.addEventListener('click', this.handleClick);
|
|
307
|
+
this.dom.addEventListener('input', this.handleInput);
|
|
308
|
+
this.input.addEventListener('keydown', this.handleTitleKeydown);
|
|
309
|
+
// If the user interacts in our title bar (either toggle or input)
|
|
310
|
+
// Prevent ProseMirror from getting a focus event (causes weird selection issues).
|
|
311
|
+
this.titleContainer.addEventListener('focus', this.handleFocus);
|
|
312
|
+
this.icon.addEventListener('keydown', this.handleIconKeyDown);
|
|
313
|
+
}
|
|
314
|
+
stopEvent(event) {
|
|
315
|
+
const target = event.target;
|
|
316
|
+
return target === this.input || target === this.icon || !!closestElement(target, `.${expandClassNames.icon}`);
|
|
317
|
+
}
|
|
318
|
+
ignoreMutation(mutationRecord) {
|
|
319
|
+
// ME-1931: Mobile relies on composition which creates dom mutations. If we ignore them, prosemirror
|
|
320
|
+
// does not recognise the changes and reverts them.
|
|
321
|
+
if (this.isMobile && (mutationRecord.type === 'characterData' || mutationRecord.type === 'childList')) {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
if (mutationRecord.type === 'selection') {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
update(node, _decorations) {
|
|
330
|
+
if (this.node.type === node.type) {
|
|
331
|
+
// TODO: https://product-fabric.atlassian.net/browse/ED-22841
|
|
332
|
+
// Handle toggling of the expand on update here
|
|
333
|
+
|
|
334
|
+
// During a collab session the title doesn't sync with other users
|
|
335
|
+
// since we're intentionally being less aggressive about re-rendering.
|
|
336
|
+
// We also apply a rAF to avoid abrupt continuous replacement of the title.
|
|
337
|
+
window.requestAnimationFrame(() => {
|
|
338
|
+
if (this.input && this.node.attrs.title !== this.input.value) {
|
|
339
|
+
this.input.value = this.node.attrs.title;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
this.node = node;
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
destroy() {
|
|
348
|
+
if (!this.dom || !this.input || !this.titleContainer || !this.icon) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
this.dom.removeEventListener('click', this.handleClick);
|
|
352
|
+
this.dom.removeEventListener('input', this.handleInput);
|
|
353
|
+
this.input.removeEventListener('keydown', this.handleTitleKeydown);
|
|
354
|
+
this.titleContainer.removeEventListener('focus', this.handleFocus);
|
|
355
|
+
this.icon.removeEventListener('keydown', this.handleIconKeyDown);
|
|
356
|
+
ReactDOM.unmountComponentAtNode(this.icon);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
export default function ({
|
|
360
|
+
getIntl,
|
|
361
|
+
isMobile,
|
|
362
|
+
api,
|
|
363
|
+
allowInteractiveExpand = true,
|
|
364
|
+
__livePage
|
|
365
|
+
}) {
|
|
366
|
+
return (node, view, getPos) => {
|
|
367
|
+
var _api$selection, _api$selection$action;
|
|
368
|
+
return new ExpandNodeView(node, view, getPos, getIntl, isMobile, api === null || api === void 0 ? void 0 : (_api$selection = api.selection) === null || _api$selection === void 0 ? void 0 : (_api$selection$action = _api$selection.actions) === null || _api$selection$action === void 0 ? void 0 : _api$selection$action.selectNearNode, api, allowInteractiveExpand, __livePage);
|
|
369
|
+
};
|
|
370
|
+
}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import { expand, extendedNestedExpand, nestedExpand } from '@atlaskit/adf-schema';
|
|
3
|
+
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
4
|
+
import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
5
|
+
import { IconExpand } from '@atlaskit/editor-common/quick-insert';
|
|
6
|
+
import { createWrapSelectionTransaction } from '@atlaskit/editor-common/utils';
|
|
2
7
|
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export const expandPlugin = (
|
|
8
|
+
import { createExpandNode, insertExpand } from './commands';
|
|
9
|
+
import { expandKeymap } from './pm-plugins/keymap';
|
|
10
|
+
import { createPlugin } from './pm-plugins/main';
|
|
11
|
+
import { getToolbarConfig } from './toolbar';
|
|
12
|
+
export const expandPlugin = ({
|
|
13
|
+
config: options = {},
|
|
14
|
+
api
|
|
15
|
+
}) => {
|
|
16
|
+
var _api$analytics;
|
|
8
17
|
return {
|
|
9
18
|
name: 'expand',
|
|
10
19
|
nodes() {
|
|
@@ -18,13 +27,63 @@ export const expandPlugin = () => {
|
|
|
18
27
|
}];
|
|
19
28
|
},
|
|
20
29
|
actions: {
|
|
21
|
-
insertExpand: ()
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
30
|
+
insertExpand: insertExpand(api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions)
|
|
24
31
|
},
|
|
25
32
|
pmPlugins() {
|
|
26
|
-
return [
|
|
33
|
+
return [{
|
|
34
|
+
name: 'expand',
|
|
35
|
+
plugin: ({
|
|
36
|
+
dispatch,
|
|
37
|
+
getIntl
|
|
38
|
+
}) => {
|
|
39
|
+
var _options$allowInterac;
|
|
40
|
+
return createPlugin(dispatch, getIntl, options.appearance, options.useLongPressSelection, api, (_options$allowInterac = options.allowInteractiveExpand) !== null && _options$allowInterac !== void 0 ? _options$allowInterac : true, options.__livePage);
|
|
41
|
+
}
|
|
42
|
+
}, {
|
|
43
|
+
name: 'expandKeymap',
|
|
44
|
+
plugin: () => expandKeymap(api, {
|
|
45
|
+
__livePage: options.__livePage
|
|
46
|
+
})
|
|
47
|
+
}];
|
|
27
48
|
},
|
|
28
|
-
pluginsOptions: {
|
|
49
|
+
pluginsOptions: {
|
|
50
|
+
floatingToolbar: getToolbarConfig(api),
|
|
51
|
+
quickInsert: ({
|
|
52
|
+
formatMessage
|
|
53
|
+
}) => {
|
|
54
|
+
if (options && options.allowInsertion !== true) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
return [{
|
|
58
|
+
id: 'expand',
|
|
59
|
+
title: formatMessage(messages.expand),
|
|
60
|
+
description: formatMessage(messages.expandDescription),
|
|
61
|
+
keywords: ['accordion', 'collapse'],
|
|
62
|
+
priority: 600,
|
|
63
|
+
icon: () => /*#__PURE__*/React.createElement(IconExpand, null),
|
|
64
|
+
action(insert, state) {
|
|
65
|
+
var _api$analytics2;
|
|
66
|
+
const node = createExpandNode(state);
|
|
67
|
+
if (!node) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const tr = state.selection.empty ? insert(node) : createWrapSelectionTransaction({
|
|
71
|
+
state,
|
|
72
|
+
type: node.type
|
|
73
|
+
});
|
|
74
|
+
api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions.attachAnalyticsEvent({
|
|
75
|
+
action: ACTION.INSERTED,
|
|
76
|
+
actionSubject: ACTION_SUBJECT.DOCUMENT,
|
|
77
|
+
actionSubjectId: node.type === state.schema.nodes.nestedExpand ? ACTION_SUBJECT_ID.NESTED_EXPAND : ACTION_SUBJECT_ID.EXPAND,
|
|
78
|
+
attributes: {
|
|
79
|
+
inputMethod: INPUT_METHOD.QUICK_INSERT
|
|
80
|
+
},
|
|
81
|
+
eventType: EVENT_TYPE.TRACK
|
|
82
|
+
})(tr);
|
|
83
|
+
return tr;
|
|
84
|
+
}
|
|
85
|
+
}];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
29
88
|
};
|
|
30
89
|
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { backspace, bindKeymapWithCommand, moveDown, moveUp } from '@atlaskit/editor-common/keymaps';
|
|
2
|
+
import { GapCursorSelection, Side } from '@atlaskit/editor-common/selection';
|
|
3
|
+
import { findExpand } from '@atlaskit/editor-common/transforms';
|
|
4
|
+
import { isEmptyNode, isPositionNearTableRow } from '@atlaskit/editor-common/utils';
|
|
5
|
+
import { keymap } from '@atlaskit/editor-prosemirror/keymap';
|
|
6
|
+
import { Selection } from '@atlaskit/editor-prosemirror/state';
|
|
7
|
+
import { deleteExpand, focusTitle } from '../commands';
|
|
8
|
+
const isExpandNode = node => {
|
|
9
|
+
return (node === null || node === void 0 ? void 0 : node.type.name) === 'expand' || (node === null || node === void 0 ? void 0 : node.type.name) === 'nestedExpand';
|
|
10
|
+
};
|
|
11
|
+
export function expandKeymap(api, options) {
|
|
12
|
+
const list = {};
|
|
13
|
+
bindKeymapWithCommand(moveUp.common, (state, dispatch, editorView) => {
|
|
14
|
+
if (!editorView) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const {
|
|
18
|
+
selection,
|
|
19
|
+
schema
|
|
20
|
+
} = state;
|
|
21
|
+
const {
|
|
22
|
+
nodeBefore
|
|
23
|
+
} = selection.$from;
|
|
24
|
+
if (selection instanceof GapCursorSelection && selection.side === Side.RIGHT && nodeBefore && (nodeBefore.type === schema.nodes.expand || nodeBefore.type === schema.nodes.nestedExpand)
|
|
25
|
+
// TO-DO: add back in expanded logic
|
|
26
|
+
) {
|
|
27
|
+
const {
|
|
28
|
+
$from
|
|
29
|
+
} = selection;
|
|
30
|
+
return focusTitle(Math.max($from.pos - 1, 0))(state, dispatch, editorView);
|
|
31
|
+
}
|
|
32
|
+
const {
|
|
33
|
+
$from
|
|
34
|
+
} = state.selection;
|
|
35
|
+
if (editorView.endOfTextblock('up')) {
|
|
36
|
+
const expand = findExpand(state);
|
|
37
|
+
|
|
38
|
+
// Moving UP in a table should move the cursor to the row above
|
|
39
|
+
// however when an expand is in a table cell to the left of the
|
|
40
|
+
// current table cell, arrow UP moves the cursor to the left
|
|
41
|
+
// see ED-15425
|
|
42
|
+
if (isPositionNearTableRow($from, schema, 'before') && !expand) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const prevCursorPos = Math.max($from.pos - $from.parentOffset - 1, 0);
|
|
46
|
+
// move cursor from expand's content to its title
|
|
47
|
+
if (expand && expand.start === prevCursorPos) {
|
|
48
|
+
return focusTitle(expand.start)(state, dispatch, editorView);
|
|
49
|
+
}
|
|
50
|
+
const sel = Selection.findFrom(state.doc.resolve(prevCursorPos), -1);
|
|
51
|
+
const expandBefore = findExpand(state, sel);
|
|
52
|
+
if (sel && expandBefore) {
|
|
53
|
+
// moving cursor from outside of an expand to the title when it is collapsed
|
|
54
|
+
|
|
55
|
+
// TO-DO: Bring back expanded logic
|
|
56
|
+
return focusTitle(expandBefore.start)(state, dispatch, editorView);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}, list);
|
|
61
|
+
bindKeymapWithCommand(moveDown.common, (state, dispatch, editorView) => {
|
|
62
|
+
if (!editorView) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const {
|
|
66
|
+
expand,
|
|
67
|
+
nestedExpand
|
|
68
|
+
} = state.schema.nodes;
|
|
69
|
+
const {
|
|
70
|
+
selection
|
|
71
|
+
} = state;
|
|
72
|
+
const {
|
|
73
|
+
nodeAfter
|
|
74
|
+
} = selection.$from;
|
|
75
|
+
if (selection instanceof GapCursorSelection && selection.side === Side.LEFT && nodeAfter && (nodeAfter.type === expand || nodeAfter.type === nestedExpand)
|
|
76
|
+
// TO-DO: Bring back expanded logic
|
|
77
|
+
) {
|
|
78
|
+
const {
|
|
79
|
+
$from
|
|
80
|
+
} = selection;
|
|
81
|
+
return focusTitle($from.pos + 1)(state, dispatch, editorView);
|
|
82
|
+
}
|
|
83
|
+
if (editorView.endOfTextblock('down')) {
|
|
84
|
+
const {
|
|
85
|
+
$from
|
|
86
|
+
} = state.selection;
|
|
87
|
+
if ($from.depth === 0) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const $after = state.doc.resolve($from.after());
|
|
91
|
+
if ($after.nodeAfter && ($after.nodeAfter.type === expand || $after.nodeAfter.type === nestedExpand)) {
|
|
92
|
+
return focusTitle($after.pos + 1)(state, dispatch, editorView);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}, list);
|
|
97
|
+
bindKeymapWithCommand(backspace.common, (state, dispatch, editorView) => {
|
|
98
|
+
const {
|
|
99
|
+
selection
|
|
100
|
+
} = state;
|
|
101
|
+
const {
|
|
102
|
+
$from
|
|
103
|
+
} = selection;
|
|
104
|
+
if (!editorView || !selection.empty) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
const {
|
|
108
|
+
expand,
|
|
109
|
+
nestedExpand
|
|
110
|
+
} = state.schema.nodes;
|
|
111
|
+
const expandNode = findExpand(state);
|
|
112
|
+
if (!expandNode) {
|
|
113
|
+
// @see ED-7977
|
|
114
|
+
const sel = Selection.findFrom(state.doc.resolve(Math.max(selection.$from.pos - 1, 0)), -1);
|
|
115
|
+
const expandBefore = findExpand(state, sel);
|
|
116
|
+
if (expandBefore && (expandBefore.node.type === expand || expandBefore.node.type === nestedExpand)
|
|
117
|
+
// TO-DO: Bring back expanded logic
|
|
118
|
+
) {
|
|
119
|
+
return focusTitle(expandBefore.start)(state, dispatch, editorView);
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const parentNode = state.doc.nodeAt($from.before(Math.max($from.depth - 1, 1)));
|
|
124
|
+
// ED-10012 catch cases where the expand has another node nested within it and
|
|
125
|
+
// the backspace should be applied only to the inner node instead of the expand
|
|
126
|
+
if (parentNode && !isExpandNode(parentNode)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const textSel = Selection.findFrom(state.doc.resolve(expandNode.pos), 1, true);
|
|
130
|
+
if (textSel && selection.$from.pos === textSel.$from.pos && isEmptyNode(state.schema)(expandNode.node) && dispatch) {
|
|
131
|
+
var _api$analytics;
|
|
132
|
+
return deleteExpand(api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions)(state, dispatch);
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}, list);
|
|
136
|
+
return keymap(list);
|
|
137
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
|
+
import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
|
|
3
|
+
import { expandClassNames } from '@atlaskit/editor-common/styles';
|
|
4
|
+
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
5
|
+
import ExpandNodeView from '../node-views';
|
|
6
|
+
export const pluginKey = new PluginKey('expandPlugin');
|
|
7
|
+
export function containsClass(element, className) {
|
|
8
|
+
var _element$classList;
|
|
9
|
+
return Boolean(element === null || element === void 0 ? void 0 : (_element$classList = element.classList) === null || _element$classList === void 0 ? void 0 : _element$classList.contains(className));
|
|
10
|
+
}
|
|
11
|
+
export const createPlugin = (dispatch, getIntl, appearance = 'full-page', useLongPressSelection = false, api, allowInteractiveExpand = true, __livePage = false) => {
|
|
12
|
+
const isMobile = appearance === 'mobile';
|
|
13
|
+
return new SafePlugin({
|
|
14
|
+
key: pluginKey,
|
|
15
|
+
props: {
|
|
16
|
+
nodeViews: {
|
|
17
|
+
expand: ExpandNodeView({
|
|
18
|
+
getIntl,
|
|
19
|
+
isMobile,
|
|
20
|
+
api,
|
|
21
|
+
allowInteractiveExpand,
|
|
22
|
+
__livePage
|
|
23
|
+
}),
|
|
24
|
+
nestedExpand: ExpandNodeView({
|
|
25
|
+
getIntl,
|
|
26
|
+
isMobile,
|
|
27
|
+
api,
|
|
28
|
+
allowInteractiveExpand,
|
|
29
|
+
__livePage
|
|
30
|
+
})
|
|
31
|
+
},
|
|
32
|
+
handleKeyDown(_view, event) {
|
|
33
|
+
return containsClass(event.target, expandClassNames.titleContainer);
|
|
34
|
+
},
|
|
35
|
+
handleKeyPress(_view, event) {
|
|
36
|
+
return containsClass(event.target, expandClassNames.titleContainer);
|
|
37
|
+
},
|
|
38
|
+
handleScrollToSelection() {
|
|
39
|
+
return containsClass(document.activeElement, expandClassNames.titleInput);
|
|
40
|
+
},
|
|
41
|
+
handleClickOn: createSelectionClickHandler(['expand', 'nestedExpand'], target => target.classList.contains(expandClassNames.prefix), {
|
|
42
|
+
useLongPressSelection
|
|
43
|
+
})
|
|
44
|
+
},
|
|
45
|
+
// @see ED-8027 to follow up on this work-around
|
|
46
|
+
filterTransaction(tr) {
|
|
47
|
+
if (containsClass(document.activeElement, expandClassNames.titleInput) && tr.selectionSet && (!tr.steps.length || tr.isGeneric)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
};
|