@atlaskit/editor-plugin-expand 1.3.5 → 1.4.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 (27) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/legacyExpand/nodeviews/index.js +20 -8
  3. package/dist/cjs/singlePlayerExpand/commands.js +47 -6
  4. package/dist/cjs/singlePlayerExpand/node-views/index.js +80 -32
  5. package/dist/cjs/singlePlayerExpand/pm-plugins/keymap.js +8 -4
  6. package/dist/cjs/singlePlayerExpand/ui/NodeView.js +11 -6
  7. package/dist/es2019/legacyExpand/nodeviews/index.js +20 -8
  8. package/dist/es2019/singlePlayerExpand/commands.js +44 -2
  9. package/dist/es2019/singlePlayerExpand/node-views/index.js +67 -20
  10. package/dist/es2019/singlePlayerExpand/pm-plugins/keymap.js +8 -4
  11. package/dist/es2019/singlePlayerExpand/ui/NodeView.js +40 -35
  12. package/dist/esm/legacyExpand/nodeviews/index.js +20 -8
  13. package/dist/esm/singlePlayerExpand/commands.js +47 -6
  14. package/dist/esm/singlePlayerExpand/node-views/index.js +82 -34
  15. package/dist/esm/singlePlayerExpand/pm-plugins/keymap.js +8 -4
  16. package/dist/esm/singlePlayerExpand/ui/NodeView.js +11 -7
  17. package/dist/types/legacyExpand/nodeviews/index.d.ts +2 -0
  18. package/dist/types/singlePlayerExpand/commands.d.ts +5 -0
  19. package/dist/types/singlePlayerExpand/node-views/index.d.ts +4 -0
  20. package/dist/types/singlePlayerExpand/ui/NodeView.d.ts +1 -1
  21. package/dist/types/types.d.ts +3 -1
  22. package/dist/types-ts4.5/legacyExpand/nodeviews/index.d.ts +2 -0
  23. package/dist/types-ts4.5/singlePlayerExpand/commands.d.ts +5 -0
  24. package/dist/types-ts4.5/singlePlayerExpand/node-views/index.d.ts +4 -0
  25. package/dist/types-ts4.5/singlePlayerExpand/ui/NodeView.d.ts +1 -1
  26. package/dist/types-ts4.5/types.d.ts +3 -1
  27. package/package.json +3 -2
@@ -51,6 +51,7 @@ export class ExpandNodeView {
51
51
  _defineProperty(this, "isMobile", false);
52
52
  _defineProperty(this, "focusTitle", () => {
53
53
  if (this.input) {
54
+ var _this$api, _this$api$selectionMa, _this$api$selectionMa2;
54
55
  const {
55
56
  state,
56
57
  dispatch
@@ -67,6 +68,7 @@ export class ExpandNodeView {
67
68
  if (typeof pos === 'number') {
68
69
  setSelectionInsideExpand(pos)(state, dispatch, this.view);
69
70
  }
71
+ this.decorationCleanup = (_this$api = this.api) === null || _this$api === void 0 ? void 0 : (_this$api$selectionMa = _this$api.selectionMarker) === null || _this$api$selectionMa === void 0 ? void 0 : (_this$api$selectionMa2 = _this$api$selectionMa.actions) === null || _this$api$selectionMa2 === void 0 ? void 0 : _this$api$selectionMa2.hideDecoration();
70
72
  this.input.focus();
71
73
  }
72
74
  });
@@ -93,7 +95,7 @@ export class ExpandNodeView {
93
95
  dispatch
94
96
  } = this.view;
95
97
  if (closestElement(target, `.${expandClassNames.icon}`)) {
96
- var _this$api, _this$api$analytics;
98
+ var _this$api2, _this$api2$analytics;
97
99
  if (!this.allowInteractiveExpand) {
98
100
  return;
99
101
  }
@@ -105,7 +107,7 @@ export class ExpandNodeView {
105
107
  this.view.dom.blur();
106
108
  }
107
109
  toggleExpandExpanded({
108
- editorAnalyticsAPI: (_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,
110
+ editorAnalyticsAPI: (_this$api2 = this.api) === null || _this$api2 === void 0 ? void 0 : (_this$api2$analytics = _this$api2.analytics) === null || _this$api2$analytics === void 0 ? void 0 : _this$api2$analytics.actions,
109
111
  pos,
110
112
  nodeType: this.node.type,
111
113
  __livePage: this.__livePage
@@ -141,6 +143,10 @@ export class ExpandNodeView {
141
143
  _defineProperty(this, "handleFocus", event => {
142
144
  event.stopImmediatePropagation();
143
145
  });
146
+ _defineProperty(this, "handleBlur", event => {
147
+ var _this$decorationClean;
148
+ (_this$decorationClean = this.decorationCleanup) === null || _this$decorationClean === void 0 ? void 0 : _this$decorationClean.call(this);
149
+ });
144
150
  _defineProperty(this, "handleTitleKeydown", event => {
145
151
  switch (keyName(event)) {
146
152
  case 'Enter':
@@ -184,8 +190,8 @@ export class ExpandNodeView {
184
190
  } = this.view;
185
191
  const expandNode = this.node;
186
192
  if (expandNode && isEmptyNode(state.schema)(expandNode)) {
187
- var _this$api2, _this$api2$analytics;
188
- deleteExpandAtPos((_this$api2 = this.api) === null || _this$api2 === void 0 ? void 0 : (_this$api2$analytics = _this$api2.analytics) === null || _this$api2$analytics === void 0 ? void 0 : _this$api2$analytics.actions)(pos, expandNode)(state, this.view.dispatch);
193
+ var _this$api3, _this$api3$analytics;
194
+ deleteExpandAtPos((_this$api3 = this.api) === null || _this$api3 === void 0 ? void 0 : (_this$api3$analytics = _this$api3.analytics) === null || _this$api3$analytics === void 0 ? void 0 : _this$api3$analytics.actions)(pos, expandNode)(state, this.view.dispatch);
189
195
  }
190
196
  });
191
197
  _defineProperty(this, "toggleExpand", () => {
@@ -194,13 +200,13 @@ export class ExpandNodeView {
194
200
  return;
195
201
  }
196
202
  if (this.allowInteractiveExpand) {
197
- var _this$api3, _this$api3$analytics;
203
+ var _this$api4, _this$api4$analytics;
198
204
  const {
199
205
  state,
200
206
  dispatch
201
207
  } = this.view;
202
208
  toggleExpandExpanded({
203
- editorAnalyticsAPI: (_this$api3 = this.api) === null || _this$api3 === void 0 ? void 0 : (_this$api3$analytics = _this$api3.analytics) === null || _this$api3$analytics === void 0 ? void 0 : _this$api3$analytics.actions,
209
+ editorAnalyticsAPI: (_this$api4 = this.api) === null || _this$api4 === void 0 ? void 0 : (_this$api4$analytics = _this$api4.analytics) === null || _this$api4$analytics === void 0 ? void 0 : _this$api4$analytics.actions,
204
210
  pos,
205
211
  nodeType: this.node.type,
206
212
  __livePage: this.__livePage
@@ -329,14 +335,14 @@ export class ExpandNodeView {
329
335
  selectionEnd
330
336
  } = this.input;
331
337
  if (selectionStart === selectionEnd && selectionStart === 0) {
332
- var _this$api4, _this$api4$selection;
338
+ var _this$api5, _this$api5$selection;
333
339
  event.preventDefault();
334
340
  const {
335
341
  state,
336
342
  dispatch
337
343
  } = this.view;
338
344
  this.view.focus();
339
- const selectionSharedState = ((_this$api4 = this.api) === null || _this$api4 === void 0 ? void 0 : (_this$api4$selection = _this$api4.selection) === null || _this$api4$selection === void 0 ? void 0 : _this$api4$selection.sharedState.currentState()) || {};
345
+ const selectionSharedState = ((_this$api5 = this.api) === null || _this$api5 === void 0 ? void 0 : (_this$api5$selection = _this$api5.selection) === null || _this$api5$selection === void 0 ? void 0 : _this$api5$selection.sharedState.currentState()) || {};
340
346
  // selectionRelativeToNode is undefined when user clicked to select node, then hit left to get focus in title
341
347
  // This is a special case where we want to bypass node selection and jump straight to gap cursor
342
348
  if ((selectionSharedState === null || selectionSharedState === void 0 ? void 0 : selectionSharedState.selectionRelativeToNode) === undefined) {
@@ -390,6 +396,8 @@ export class ExpandNodeView {
390
396
  if (this.input) {
391
397
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
392
398
  this.input.addEventListener('keydown', this.handleTitleKeydown);
399
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
400
+ this.input.addEventListener('blur', this.handleBlur);
393
401
  }
394
402
  if (this.titleContainer) {
395
403
  // If the user interacts in our title bar (either toggle or input)
@@ -459,6 +467,7 @@ export class ExpandNodeView {
459
467
  return false;
460
468
  }
461
469
  destroy() {
470
+ var _this$decorationClean2;
462
471
  if (this.dom) {
463
472
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
464
473
  this.dom.removeEventListener('click', this.handleClick);
@@ -468,6 +477,8 @@ export class ExpandNodeView {
468
477
  if (this.input) {
469
478
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
470
479
  this.input.removeEventListener('keydown', this.handleTitleKeydown);
480
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
481
+ this.input.removeEventListener('blur', this.handleBlur);
471
482
  }
472
483
  if (this.titleContainer) {
473
484
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
@@ -478,6 +489,7 @@ export class ExpandNodeView {
478
489
  this.icon.removeEventListener('keydown', this.handleIconKeyDown);
479
490
  ReactDOM.unmountComponentAtNode(this.icon);
480
491
  }
492
+ (_this$decorationClean2 = this.decorationCleanup) === null || _this$decorationClean2 === void 0 ? void 0 : _this$decorationClean2.call(this);
481
493
 
482
494
  // @ts-ignore - [unblock prosemirror bump] reset non optional prop to undefined to clear reference
483
495
  this.dom = undefined;
@@ -1,5 +1,7 @@
1
1
  import { SetAttrsStep } from '@atlaskit/adf-schema/steps';
2
- import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
+ import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD, MODE, PLATFORMS } from '@atlaskit/editor-common/analytics';
3
+ import { expandedState } from '@atlaskit/editor-common/expand';
4
+ import { GapCursorSelection, Side } from '@atlaskit/editor-common/selection';
3
5
  import { findExpand } from '@atlaskit/editor-common/transforms';
4
6
  import { createWrapSelectionTransaction } from '@atlaskit/editor-common/utils';
5
7
  import { Selection } from '@atlaskit/editor-prosemirror/state';
@@ -13,7 +15,9 @@ export const createExpandNode = state => {
13
15
  nestedExpand
14
16
  } = state.schema.nodes;
15
17
  const expandType = findTable(state.selection) ? nestedExpand : expand;
16
- return expandType.createAndFill({});
18
+ const expandNode = expandType.createAndFill({});
19
+ expandedState.set(expandNode, true);
20
+ return expandNode;
17
21
  };
18
22
  export const insertExpand = editorAnalyticsAPI => (state, dispatch) => {
19
23
  const expandNode = createExpandNode(state);
@@ -83,6 +87,44 @@ export const setSelectionInsideExpand = expandPos => (state, dispatch, editorVie
83
87
  }
84
88
  return false;
85
89
  };
90
+ export const toggleExpandExpanded = ({
91
+ editorAnalyticsAPI,
92
+ pos,
93
+ node
94
+ }) => (state, dispatch) => {
95
+ if (node && dispatch) {
96
+ var _expandedState$get;
97
+ const {
98
+ tr
99
+ } = state;
100
+ const expanded = (_expandedState$get = expandedState.get(node)) !== null && _expandedState$get !== void 0 ? _expandedState$get : false;
101
+ const isExpandedNext = !expanded;
102
+ expandedState.set(node, isExpandedNext);
103
+
104
+ // If we're going to collapse the expand and our cursor is currently inside
105
+ // Move to a right gap cursor, if the toolbar is interacted (or an API),
106
+ // it will insert below rather than inside (which will be invisible).
107
+ if (isExpandedNext === true) {
108
+ tr.setSelection(new GapCursorSelection(tr.doc.resolve(pos + node.nodeSize), Side.RIGHT));
109
+ }
110
+
111
+ // log when people open/close expands
112
+ // TODO: ED-8523 make platform/mode global attributes?
113
+ const payload = {
114
+ action: ACTION.TOGGLE_EXPAND,
115
+ actionSubject: node.type === state.schema.nodes.expand ? ACTION_SUBJECT.EXPAND : ACTION_SUBJECT.NESTED_EXPAND,
116
+ attributes: {
117
+ platform: PLATFORMS.WEB,
118
+ mode: MODE.EDITOR,
119
+ expanded: isExpandedNext
120
+ },
121
+ eventType: EVENT_TYPE.TRACK
122
+ };
123
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent(payload)(tr);
124
+ dispatch(tr);
125
+ }
126
+ return true;
127
+ };
86
128
  export const updateExpandTitle = ({
87
129
  title,
88
130
  nodeType,
@@ -1,19 +1,21 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import ReactDOM from 'react-dom';
3
3
  import { keyName } from 'w3c-keyname';
4
+ import { expandedState } from '@atlaskit/editor-common/expand';
4
5
  import { GapCursorSelection, RelativeSelectionPos, Side } from '@atlaskit/editor-common/selection';
5
6
  import { expandClassNames } from '@atlaskit/editor-common/styles';
6
7
  import { closestElement, isEmptyNode } from '@atlaskit/editor-common/utils';
7
8
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
8
9
  import { NodeSelection, Selection } from '@atlaskit/editor-prosemirror/state';
9
- import { deleteExpand, setSelectionInsideExpand, updateExpandTitle } from '../commands';
10
- import { renderIcon, toDOM } from '../ui/NodeView';
10
+ import { deleteExpand, setSelectionInsideExpand, toggleExpandExpanded, updateExpandTitle } from '../commands';
11
+ import { buildExpandClassName, renderIcon, toDOM } from '../ui/NodeView';
11
12
  export class ExpandNodeView {
12
13
  constructor(node, view, getPos, getIntl, isMobile, selectNearNode, api, allowInteractiveExpand = true, __livePage = false) {
13
14
  _defineProperty(this, "allowInteractiveExpand", true);
14
15
  _defineProperty(this, "isMobile", false);
15
16
  _defineProperty(this, "focusTitle", () => {
16
17
  if (this.input) {
18
+ var _this$api, _this$api$selectionMa, _this$api$selectionMa2;
17
19
  const {
18
20
  state,
19
21
  dispatch
@@ -30,6 +32,7 @@ export class ExpandNodeView {
30
32
  if (typeof pos === 'number') {
31
33
  setSelectionInsideExpand(pos)(state, dispatch, this.view);
32
34
  }
35
+ this.decorationCleanup = (_this$api = this.api) === null || _this$api === void 0 ? void 0 : (_this$api$selectionMa = _this$api.selectionMarker) === null || _this$api$selectionMa === void 0 ? void 0 : (_this$api$selectionMa2 = _this$api$selectionMa.actions) === null || _this$api$selectionMa2 === void 0 ? void 0 : _this$api$selectionMa2.hideDecoration();
33
36
  this.input.focus();
34
37
  }
35
38
  });
@@ -52,6 +55,7 @@ export class ExpandNodeView {
52
55
  }
53
56
  const target = event.target;
54
57
  if (closestElement(target, `.${expandClassNames.icon}`)) {
58
+ var _this$api2, _this$api2$analytics;
55
59
  if (!this.allowInteractiveExpand) {
56
60
  return;
57
61
  }
@@ -62,10 +66,12 @@ export class ExpandNodeView {
62
66
  if (this.view.dom instanceof HTMLElement) {
63
67
  this.view.dom.blur();
64
68
  }
65
-
66
- // TODO https://product-fabric.atlassian.net/browse/ED-22841
67
- // call toggleExpandExpanded
68
-
69
+ toggleExpandExpanded({
70
+ editorAnalyticsAPI: (_this$api2 = this.api) === null || _this$api2 === void 0 ? void 0 : (_this$api2$analytics = _this$api2.analytics) === null || _this$api2$analytics === void 0 ? void 0 : _this$api2$analytics.actions,
71
+ pos,
72
+ node: this.node
73
+ })(this.view.state, this.view.dispatch);
74
+ this.updateExpandClassName();
69
75
  return;
70
76
  }
71
77
  if (target === this.input) {
@@ -96,10 +102,14 @@ export class ExpandNodeView {
96
102
  _defineProperty(this, "handleFocus", event => {
97
103
  event.stopImmediatePropagation();
98
104
  });
105
+ _defineProperty(this, "handleBlur", event => {
106
+ var _this$decorationClean;
107
+ (_this$decorationClean = this.decorationCleanup) === null || _this$decorationClean === void 0 ? void 0 : _this$decorationClean.call(this);
108
+ });
99
109
  _defineProperty(this, "handleTitleKeydown", event => {
100
110
  switch (keyName(event)) {
101
111
  case 'Enter':
102
- // TO-DO
112
+ this.toggleExpand();
103
113
  break;
104
114
  case 'Tab':
105
115
  case 'ArrowDown':
@@ -135,8 +145,27 @@ export class ExpandNodeView {
135
145
  return;
136
146
  }
137
147
  if (expandNode && isEmptyNode(state.schema)(expandNode)) {
138
- var _this$api, _this$api$analytics;
139
- 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);
148
+ var _this$api3, _this$api3$analytics;
149
+ deleteExpand((_this$api3 = this.api) === null || _this$api3 === void 0 ? void 0 : (_this$api3$analytics = _this$api3.analytics) === null || _this$api3$analytics === void 0 ? void 0 : _this$api3$analytics.actions)(state, this.view.dispatch);
150
+ }
151
+ });
152
+ _defineProperty(this, "toggleExpand", () => {
153
+ const pos = this.getPos();
154
+ if (typeof pos !== 'number') {
155
+ return;
156
+ }
157
+ if (this.allowInteractiveExpand) {
158
+ var _this$api4, _this$api4$analytics;
159
+ const {
160
+ state,
161
+ dispatch
162
+ } = this.view;
163
+ toggleExpandExpanded({
164
+ editorAnalyticsAPI: (_this$api4 = this.api) === null || _this$api4 === void 0 ? void 0 : (_this$api4$analytics = _this$api4.analytics) === null || _this$api4$analytics === void 0 ? void 0 : _this$api4$analytics.actions,
165
+ pos,
166
+ node: this.node
167
+ })(state, dispatch);
168
+ this.updateExpandClassName();
140
169
  }
141
170
  });
142
171
  _defineProperty(this, "moveToOutsideOfTitle", event => {
@@ -168,9 +197,8 @@ export class ExpandNodeView {
168
197
  dispatch(state.tr.setSelection(sel));
169
198
  }
170
199
  });
171
- // TODO: https://product-fabric.atlassian.net/browse/ED-22841
172
200
  _defineProperty(this, "isCollapsed", () => {
173
- return false;
201
+ return !expandedState.get(this.node);
174
202
  });
175
203
  _defineProperty(this, "setRightGapCursor", event => {
176
204
  if (!this.input) {
@@ -259,14 +287,14 @@ export class ExpandNodeView {
259
287
  selectionEnd
260
288
  } = this.input;
261
289
  if (selectionStart === selectionEnd && selectionStart === 0) {
262
- var _this$api2, _this$api2$selection;
290
+ var _this$api5, _this$api5$selection;
263
291
  event.preventDefault();
264
292
  const {
265
293
  state,
266
294
  dispatch
267
295
  } = this.view;
268
296
  this.view.focus();
269
- 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()) || {};
297
+ const selectionSharedState = ((_this$api5 = this.api) === null || _this$api5 === void 0 ? void 0 : (_this$api5$selection = _this$api5.selection) === null || _this$api5$selection === void 0 ? void 0 : _this$api5$selection.sharedState.currentState()) || {};
270
298
  // selectionRelativeToNode is undefined when user clicked to select node, then hit left to get focus in title
271
299
  // This is a special case where we want to bypass node selection and jump straight to gap cursor
272
300
  if ((selectionSharedState === null || selectionSharedState === void 0 ? void 0 : selectionSharedState.selectionRelativeToNode) === undefined) {
@@ -291,14 +319,14 @@ export class ExpandNodeView {
291
319
  this.selectNearNode = selectNearNode;
292
320
  this.__livePage = __livePage;
293
321
  this.intl = getIntl();
294
- const {
295
- dom,
296
- contentDOM
297
- } = DOMSerializer.renderSpec(document, toDOM(node, this.__livePage, this.intl));
298
322
  this.allowInteractiveExpand = allowInteractiveExpand;
299
323
  this.getPos = getPos;
300
324
  this.view = view;
301
325
  this.node = node;
326
+ const {
327
+ dom,
328
+ contentDOM
329
+ } = DOMSerializer.renderSpec(document, toDOM(node, this.__livePage, this.intl));
302
330
  this.dom = dom;
303
331
  this.contentDOM = contentDOM;
304
332
  this.isMobile = isMobile;
@@ -307,6 +335,9 @@ export class ExpandNodeView {
307
335
  this.input = this.dom.querySelector(`.${expandClassNames.titleInput}`);
308
336
  this.titleContainer = this.dom.querySelector(`.${expandClassNames.titleContainer}`);
309
337
  renderIcon(this.icon, this.allowInteractiveExpand, this.intl);
338
+ if (!expandedState.has(this.node)) {
339
+ expandedState.set(this.node, false);
340
+ }
310
341
  if (!this.input || !this.titleContainer || !this.icon) {
311
342
  return;
312
343
  }
@@ -316,6 +347,7 @@ export class ExpandNodeView {
316
347
  this.dom.addEventListener('click', this.handleClick);
317
348
  this.dom.addEventListener('input', this.handleInput);
318
349
  this.input.addEventListener('keydown', this.handleTitleKeydown);
350
+ this.input.addEventListener('blur', this.handleBlur);
319
351
  // If the user interacts in our title bar (either toggle or input)
320
352
  // Prevent ProseMirror from getting a focus event (causes weird selection issues).
321
353
  this.titleContainer.addEventListener('focus', this.handleFocus);
@@ -338,9 +370,6 @@ export class ExpandNodeView {
338
370
  }
339
371
  update(node, _decorations) {
340
372
  if (this.node.type === node.type) {
341
- // TODO: https://product-fabric.atlassian.net/browse/ED-22841
342
- // Handle toggling of the expand on update here
343
-
344
373
  // During a collab session the title doesn't sync with other users
345
374
  // since we're intentionally being less aggressive about re-rendering.
346
375
  // We also apply a rAF to avoid abrupt continuous replacement of the title.
@@ -349,21 +378,39 @@ export class ExpandNodeView {
349
378
  this.input.value = this.node.attrs.title;
350
379
  }
351
380
  });
381
+ if (this.node !== node) {
382
+ let previousExpandedState = expandedState.get(this.node);
383
+ if (previousExpandedState === undefined) {
384
+ previousExpandedState = true;
385
+ }
386
+ expandedState.set(node, previousExpandedState);
387
+ expandedState.delete(this.node);
388
+ }
352
389
  this.node = node;
353
390
  return true;
354
391
  }
355
392
  return false;
356
393
  }
394
+ updateExpandClassName() {
395
+ const expanded = expandedState.get(this.node) ? expandedState.get(this.node) : false;
396
+ if (this.dom && expanded !== undefined) {
397
+ this.dom.className = buildExpandClassName(this.node.type.name, expanded);
398
+ }
399
+ }
357
400
  destroy() {
401
+ var _this$decorationClean2;
358
402
  if (!this.dom || !this.input || !this.titleContainer || !this.icon) {
359
403
  return;
360
404
  }
361
405
  this.dom.removeEventListener('click', this.handleClick);
362
406
  this.dom.removeEventListener('input', this.handleInput);
363
407
  this.input.removeEventListener('keydown', this.handleTitleKeydown);
408
+ this.input.removeEventListener('blur', this.handleBlur);
364
409
  this.titleContainer.removeEventListener('focus', this.handleFocus);
365
410
  this.icon.removeEventListener('keydown', this.handleIconKeyDown);
411
+ (_this$decorationClean2 = this.decorationCleanup) === null || _this$decorationClean2 === void 0 ? void 0 : _this$decorationClean2.call(this);
366
412
  ReactDOM.unmountComponentAtNode(this.icon);
413
+ expandedState.delete(this.node);
367
414
  }
368
415
  }
369
416
  export default function ({
@@ -22,7 +22,8 @@ export function expandKeymap(api, options) {
22
22
  nodeBefore
23
23
  } = selection.$from;
24
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
25
+ // https://product-fabric.atlassian.net/browse/ED-22991
26
+ // TODO: add back in expanded logic
26
27
  ) {
27
28
  const {
28
29
  $from
@@ -52,7 +53,8 @@ export function expandKeymap(api, options) {
52
53
  if (sel && expandBefore) {
53
54
  // moving cursor from outside of an expand to the title when it is collapsed
54
55
 
55
- // TO-DO: Bring back expanded logic
56
+ // https://product-fabric.atlassian.net/browse/ED-22991
57
+ // TODO: add back in expanded logic
56
58
  return focusTitle(expandBefore.start)(state, dispatch, editorView);
57
59
  }
58
60
  }
@@ -73,7 +75,8 @@ export function expandKeymap(api, options) {
73
75
  nodeAfter
74
76
  } = selection.$from;
75
77
  if (selection instanceof GapCursorSelection && selection.side === Side.LEFT && nodeAfter && (nodeAfter.type === expand || nodeAfter.type === nestedExpand)
76
- // TO-DO: Bring back expanded logic
78
+ // https://product-fabric.atlassian.net/browse/ED-22991
79
+ // TODO: add back in expanded logic
77
80
  ) {
78
81
  const {
79
82
  $from
@@ -114,7 +117,8 @@ export function expandKeymap(api, options) {
114
117
  const sel = Selection.findFrom(state.doc.resolve(Math.max(selection.$from.pos - 1, 0)), -1);
115
118
  const expandBefore = findExpand(state, sel);
116
119
  if (expandBefore && (expandBefore.node.type === expand || expandBefore.node.type === nestedExpand)
117
- // TO-DO: Bring back expanded logic
120
+ // https://product-fabric.atlassian.net/browse/ED-22991
121
+ // TODO: add back in expanded logic
118
122
  ) {
119
123
  return focusTitle(expandBefore.start)(state, dispatch, editorView);
120
124
  }
@@ -1,52 +1,57 @@
1
1
  import React from 'react';
2
2
  import ReactDOM from 'react-dom';
3
+ import { expandedState } from '@atlaskit/editor-common/expand';
3
4
  import { expandClassNames } from '@atlaskit/editor-common/styles';
4
5
  import { expandMessages } from '@atlaskit/editor-common/ui';
5
6
  import { ExpandButton } from '../ui/ExpandButton';
6
-
7
- // TODO: https://product-fabric.atlassian.net/browse/ED-22841
8
- export const buildExpandClassName = type => {
9
- return `${expandClassNames.prefix} ${expandClassNames.type(type)}
10
- ${expandClassNames.expanded}`;
7
+ export const buildExpandClassName = (type, expanded) => {
8
+ return `${expandClassNames.prefix} ${expandClassNames.type(type)} ${expanded ? expandClassNames.expanded : ''}`;
11
9
  };
12
- export const toDOM = (node, __livePage, intl) => ['div', {
10
+ export const toDOM = (node, __livePage, intl) => {
11
+ var _expandedState$get;
12
+ return ['div', {
13
+ // prettier-ignore
14
+ 'class': buildExpandClassName(node.type.name, (_expandedState$get = expandedState.get(node)) !== null && _expandedState$get !== void 0 ? _expandedState$get : false),
15
+ 'data-node-type': node.type.name,
16
+ 'data-title': node.attrs.title
17
+ }, ['div', {
18
+ // prettier-ignore
19
+ 'class': expandClassNames.titleContainer,
20
+ contenteditable: 'false',
21
+ // Element gains access to focus events.
22
+ // This is needed to prevent PM gaining access
23
+ // on interacting with our controls.
24
+ tabindex: '-1'
25
+ },
13
26
  // prettier-ignore
14
- 'class': buildExpandClassName(node.type.name),
15
- 'data-node-type': node.type.name,
16
- 'data-title': node.attrs.title
17
- }, ['div', {
27
+ ['div', {
28
+ 'class': expandClassNames.icon
29
+ }], ['div', {
30
+ // prettier-ignore
31
+ 'class': expandClassNames.inputContainer
32
+ }, ['input', {
33
+ // prettier-ignore
34
+ 'class': expandClassNames.titleInput,
35
+ value: node.attrs.title,
36
+ placeholder: intl && typeof intl.formatMessage === 'function' && intl.formatMessage(expandMessages.expandPlaceholderText) || expandMessages.expandPlaceholderText.defaultMessage,
37
+ type: 'text'
38
+ }]]],
18
39
  // prettier-ignore
19
- 'class': expandClassNames.titleContainer,
20
- contenteditable: 'false',
21
- // Element gains access to focus events.
22
- // This is needed to prevent PM gaining access
23
- // on interacting with our controls.
24
- tabindex: '-1'
25
- },
26
- // prettier-ignore
27
- ['div', {
28
- 'class': expandClassNames.icon
29
- }], ['div', {
30
- // prettier-ignore
31
- 'class': expandClassNames.inputContainer
32
- }, ['input', {
33
- // prettier-ignore
34
- 'class': expandClassNames.titleInput,
35
- value: node.attrs.title,
36
- placeholder: intl && intl.formatMessage(expandMessages.expandPlaceholderText) || expandMessages.expandPlaceholderText.defaultMessage,
37
- type: 'text'
38
- }]]],
39
- // prettier-ignore
40
- ['div', {
41
- 'class': expandClassNames.content
42
- }, 0]];
40
+ ['div', {
41
+ 'class': expandClassNames.content
42
+ }, 0]];
43
+ };
43
44
  export const renderIcon = (icon, allowInteractiveExpand, intl, node) => {
44
45
  if (!icon) {
45
46
  return;
46
47
  }
48
+ let expanded = node ? expandedState.get(node) : true;
49
+ if (expanded === undefined) {
50
+ expanded = false;
51
+ }
47
52
  ReactDOM.render( /*#__PURE__*/React.createElement(ExpandButton, {
48
53
  intl: intl,
49
54
  allowInteractiveExpand: allowInteractiveExpand,
50
- expanded: true // TO-DO https://product-fabric.atlassian.net/browse/ED-22841
55
+ expanded: expanded
51
56
  }), icon);
52
57
  };
@@ -59,6 +59,7 @@ export var ExpandNodeView = /*#__PURE__*/function () {
59
59
  _defineProperty(this, "isMobile", false);
60
60
  _defineProperty(this, "focusTitle", function () {
61
61
  if (_this.input) {
62
+ var _this$api;
62
63
  var _this$view = _this.view,
63
64
  state = _this$view.state,
64
65
  dispatch = _this$view.dispatch;
@@ -74,6 +75,7 @@ export var ExpandNodeView = /*#__PURE__*/function () {
74
75
  if (typeof pos === 'number') {
75
76
  setSelectionInsideExpand(pos)(state, dispatch, _this.view);
76
77
  }
78
+ _this.decorationCleanup = (_this$api = _this.api) === null || _this$api === void 0 || (_this$api = _this$api.selectionMarker) === null || _this$api === void 0 || (_this$api = _this$api.actions) === null || _this$api === void 0 ? void 0 : _this$api.hideDecoration();
77
79
  _this.input.focus();
78
80
  }
79
81
  });
@@ -99,7 +101,7 @@ export var ExpandNodeView = /*#__PURE__*/function () {
99
101
  state = _this$view2.state,
100
102
  dispatch = _this$view2.dispatch;
101
103
  if (closestElement(target, ".".concat(expandClassNames.icon))) {
102
- var _this$api;
104
+ var _this$api2;
103
105
  if (!_this.allowInteractiveExpand) {
104
106
  return;
105
107
  }
@@ -111,7 +113,7 @@ export var ExpandNodeView = /*#__PURE__*/function () {
111
113
  _this.view.dom.blur();
112
114
  }
113
115
  toggleExpandExpanded({
114
- editorAnalyticsAPI: (_this$api = _this.api) === null || _this$api === void 0 || (_this$api = _this$api.analytics) === null || _this$api === void 0 ? void 0 : _this$api.actions,
116
+ editorAnalyticsAPI: (_this$api2 = _this.api) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.analytics) === null || _this$api2 === void 0 ? void 0 : _this$api2.actions,
115
117
  pos: pos,
116
118
  nodeType: _this.node.type,
117
119
  __livePage: _this.__livePage
@@ -146,6 +148,10 @@ export var ExpandNodeView = /*#__PURE__*/function () {
146
148
  _defineProperty(this, "handleFocus", function (event) {
147
149
  event.stopImmediatePropagation();
148
150
  });
151
+ _defineProperty(this, "handleBlur", function (event) {
152
+ var _this$decorationClean;
153
+ (_this$decorationClean = _this.decorationCleanup) === null || _this$decorationClean === void 0 || _this$decorationClean.call(_this);
154
+ });
149
155
  _defineProperty(this, "handleTitleKeydown", function (event) {
150
156
  switch (keyName(event)) {
151
157
  case 'Enter':
@@ -186,8 +192,8 @@ export var ExpandNodeView = /*#__PURE__*/function () {
186
192
  var state = _this.view.state;
187
193
  var expandNode = _this.node;
188
194
  if (expandNode && isEmptyNode(state.schema)(expandNode)) {
189
- var _this$api2;
190
- deleteExpandAtPos((_this$api2 = _this.api) === null || _this$api2 === void 0 || (_this$api2 = _this$api2.analytics) === null || _this$api2 === void 0 ? void 0 : _this$api2.actions)(pos, expandNode)(state, _this.view.dispatch);
195
+ var _this$api3;
196
+ deleteExpandAtPos((_this$api3 = _this.api) === null || _this$api3 === void 0 || (_this$api3 = _this$api3.analytics) === null || _this$api3 === void 0 ? void 0 : _this$api3.actions)(pos, expandNode)(state, _this.view.dispatch);
191
197
  }
192
198
  });
193
199
  _defineProperty(this, "toggleExpand", function () {
@@ -196,12 +202,12 @@ export var ExpandNodeView = /*#__PURE__*/function () {
196
202
  return;
197
203
  }
198
204
  if (_this.allowInteractiveExpand) {
199
- var _this$api3;
205
+ var _this$api4;
200
206
  var _this$view4 = _this.view,
201
207
  state = _this$view4.state,
202
208
  dispatch = _this$view4.dispatch;
203
209
  toggleExpandExpanded({
204
- editorAnalyticsAPI: (_this$api3 = _this.api) === null || _this$api3 === void 0 || (_this$api3 = _this$api3.analytics) === null || _this$api3 === void 0 ? void 0 : _this$api3.actions,
210
+ editorAnalyticsAPI: (_this$api4 = _this.api) === null || _this$api4 === void 0 || (_this$api4 = _this$api4.analytics) === null || _this$api4 === void 0 ? void 0 : _this$api4.actions,
205
211
  pos: pos,
206
212
  nodeType: _this.node.type,
207
213
  __livePage: _this.__livePage
@@ -322,13 +328,13 @@ export var ExpandNodeView = /*#__PURE__*/function () {
322
328
  selectionStart = _this$input5.selectionStart,
323
329
  selectionEnd = _this$input5.selectionEnd;
324
330
  if (selectionStart === selectionEnd && selectionStart === 0) {
325
- var _this$api4;
331
+ var _this$api5;
326
332
  event.preventDefault();
327
333
  var _this$view9 = _this.view,
328
334
  state = _this$view9.state,
329
335
  dispatch = _this$view9.dispatch;
330
336
  _this.view.focus();
331
- var selectionSharedState = ((_this$api4 = _this.api) === null || _this$api4 === void 0 || (_this$api4 = _this$api4.selection) === null || _this$api4 === void 0 ? void 0 : _this$api4.sharedState.currentState()) || {};
337
+ var selectionSharedState = ((_this$api5 = _this.api) === null || _this$api5 === void 0 || (_this$api5 = _this$api5.selection) === null || _this$api5 === void 0 ? void 0 : _this$api5.sharedState.currentState()) || {};
332
338
  // selectionRelativeToNode is undefined when user clicked to select node, then hit left to get focus in title
333
339
  // This is a special case where we want to bypass node selection and jump straight to gap cursor
334
340
  if ((selectionSharedState === null || selectionSharedState === void 0 ? void 0 : selectionSharedState.selectionRelativeToNode) === undefined) {
@@ -383,6 +389,8 @@ export var ExpandNodeView = /*#__PURE__*/function () {
383
389
  if (this.input) {
384
390
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
385
391
  this.input.addEventListener('keydown', this.handleTitleKeydown);
392
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
393
+ this.input.addEventListener('blur', this.handleBlur);
386
394
  }
387
395
  if (this.titleContainer) {
388
396
  // If the user interacts in our title bar (either toggle or input)
@@ -462,6 +470,7 @@ export var ExpandNodeView = /*#__PURE__*/function () {
462
470
  }, {
463
471
  key: "destroy",
464
472
  value: function destroy() {
473
+ var _this$decorationClean2;
465
474
  if (this.dom) {
466
475
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
467
476
  this.dom.removeEventListener('click', this.handleClick);
@@ -471,6 +480,8 @@ export var ExpandNodeView = /*#__PURE__*/function () {
471
480
  if (this.input) {
472
481
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
473
482
  this.input.removeEventListener('keydown', this.handleTitleKeydown);
483
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
484
+ this.input.removeEventListener('blur', this.handleBlur);
474
485
  }
475
486
  if (this.titleContainer) {
476
487
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
@@ -481,6 +492,7 @@ export var ExpandNodeView = /*#__PURE__*/function () {
481
492
  this.icon.removeEventListener('keydown', this.handleIconKeyDown);
482
493
  ReactDOM.unmountComponentAtNode(this.icon);
483
494
  }
495
+ (_this$decorationClean2 = this.decorationCleanup) === null || _this$decorationClean2 === void 0 || _this$decorationClean2.call(this);
484
496
 
485
497
  // @ts-ignore - [unblock prosemirror bump] reset non optional prop to undefined to clear reference
486
498
  this.dom = undefined;