@atlaskit/editor-plugin-block-menu 5.1.3 → 5.1.5

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 (60) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/editor-commands/transform-node-utils/flattenListStep.js +108 -0
  3. package/dist/cjs/editor-commands/transform-node-utils/flattenStep.js +21 -0
  4. package/dist/cjs/editor-commands/transform-node-utils/transform.js +33 -5
  5. package/dist/cjs/editor-commands/transform-node-utils/types.js +1 -1
  6. package/dist/cjs/editor-commands/transform-node-utils/unwrapExpandStep.js +44 -0
  7. package/dist/cjs/editor-commands/transform-node-utils/unwrapListStep.js +28 -0
  8. package/dist/cjs/editor-commands/transform-node-utils/unwrapStep.js +20 -0
  9. package/dist/cjs/editor-commands/transform-node-utils/utils.js +7 -1
  10. package/dist/cjs/editor-commands/transform-node-utils/wrapIntoLayoutStep.js +23 -0
  11. package/dist/cjs/editor-commands/transform-node-utils/wrapStep.js +16 -0
  12. package/dist/cjs/editor-commands/transformNode.js +12 -6
  13. package/dist/cjs/ui/block-menu.js +20 -7
  14. package/dist/es2019/editor-commands/transform-node-utils/flattenListStep.js +94 -0
  15. package/dist/es2019/editor-commands/transform-node-utils/flattenStep.js +15 -0
  16. package/dist/es2019/editor-commands/transform-node-utils/transform.js +34 -6
  17. package/dist/es2019/editor-commands/transform-node-utils/types.js +1 -1
  18. package/dist/es2019/editor-commands/transform-node-utils/unwrapExpandStep.js +38 -0
  19. package/dist/es2019/editor-commands/transform-node-utils/unwrapListStep.js +18 -0
  20. package/dist/es2019/editor-commands/transform-node-utils/unwrapStep.js +12 -0
  21. package/dist/es2019/editor-commands/transform-node-utils/utils.js +6 -0
  22. package/dist/es2019/editor-commands/transform-node-utils/wrapIntoLayoutStep.js +20 -0
  23. package/dist/es2019/editor-commands/transform-node-utils/wrapStep.js +12 -0
  24. package/dist/es2019/editor-commands/transformNode.js +12 -7
  25. package/dist/es2019/ui/block-menu.js +15 -7
  26. package/dist/esm/editor-commands/transform-node-utils/flattenListStep.js +102 -0
  27. package/dist/esm/editor-commands/transform-node-utils/flattenStep.js +15 -0
  28. package/dist/esm/editor-commands/transform-node-utils/transform.js +33 -5
  29. package/dist/esm/editor-commands/transform-node-utils/types.js +1 -1
  30. package/dist/esm/editor-commands/transform-node-utils/unwrapExpandStep.js +37 -0
  31. package/dist/esm/editor-commands/transform-node-utils/unwrapListStep.js +21 -0
  32. package/dist/esm/editor-commands/transform-node-utils/unwrapStep.js +13 -0
  33. package/dist/esm/editor-commands/transform-node-utils/utils.js +6 -0
  34. package/dist/esm/editor-commands/transform-node-utils/wrapIntoLayoutStep.js +17 -0
  35. package/dist/esm/editor-commands/transform-node-utils/wrapStep.js +10 -0
  36. package/dist/esm/editor-commands/transformNode.js +12 -6
  37. package/dist/esm/ui/block-menu.js +19 -7
  38. package/dist/types/editor-commands/transform-node-utils/flattenListStep.d.ts +49 -0
  39. package/dist/types/editor-commands/transform-node-utils/flattenStep.d.ts +2 -0
  40. package/dist/types/editor-commands/transform-node-utils/transform.d.ts +3 -2
  41. package/dist/types/editor-commands/transform-node-utils/types.d.ts +1 -1
  42. package/dist/types/editor-commands/transform-node-utils/unwrapExpandStep.d.ts +8 -0
  43. package/dist/types/editor-commands/transform-node-utils/unwrapListStep.d.ts +7 -0
  44. package/dist/types/editor-commands/transform-node-utils/unwrapStep.d.ts +2 -0
  45. package/dist/types/editor-commands/transform-node-utils/utils.d.ts +2 -0
  46. package/dist/types/editor-commands/transform-node-utils/wrapIntoLayoutStep.d.ts +2 -0
  47. package/dist/types/editor-commands/transform-node-utils/wrapStep.d.ts +2 -0
  48. package/dist/types/editor-commands/transforms/types.d.ts +1 -1
  49. package/dist/types-ts4.5/editor-commands/transform-node-utils/flattenListStep.d.ts +49 -0
  50. package/dist/types-ts4.5/editor-commands/transform-node-utils/flattenStep.d.ts +2 -0
  51. package/dist/types-ts4.5/editor-commands/transform-node-utils/transform.d.ts +3 -2
  52. package/dist/types-ts4.5/editor-commands/transform-node-utils/types.d.ts +1 -1
  53. package/dist/types-ts4.5/editor-commands/transform-node-utils/unwrapExpandStep.d.ts +8 -0
  54. package/dist/types-ts4.5/editor-commands/transform-node-utils/unwrapListStep.d.ts +7 -0
  55. package/dist/types-ts4.5/editor-commands/transform-node-utils/unwrapStep.d.ts +2 -0
  56. package/dist/types-ts4.5/editor-commands/transform-node-utils/utils.d.ts +2 -0
  57. package/dist/types-ts4.5/editor-commands/transform-node-utils/wrapIntoLayoutStep.d.ts +2 -0
  58. package/dist/types-ts4.5/editor-commands/transform-node-utils/wrapStep.d.ts +2 -0
  59. package/dist/types-ts4.5/editor-commands/transforms/types.d.ts +1 -1
  60. package/package.json +3 -3
@@ -1,5 +1,13 @@
1
+ import { getTargetNodeTypeNameInContext } from '../transform-node-utils/utils';
2
+ import { flattenListStep } from './flattenListStep';
3
+ import { flattenStep } from './flattenStep';
1
4
  import { stubStep } from './stubStep';
2
5
  import { NODE_CATEGORY_BY_TYPE, toNodeTypeValue } from './types';
6
+ import { unwrapExpandStep } from './unwrapExpandStep';
7
+ import { unwrapListStep } from './unwrapListStep';
8
+ import { unwrapStep } from './unwrapStep';
9
+ import { wrapIntoLayoutStep } from './wrapIntoLayoutStep';
10
+ import { wrapStep } from './wrapStep';
3
11
 
4
12
  // Exampled step for overrides:
5
13
  // - open Block menu on a paragraph, click 'Panel' in the Turn into'
@@ -19,15 +27,15 @@ const TRANSFORM_STEPS = {
19
27
  },
20
28
  container: {
21
29
  atomic: undefined,
22
- container: [stubStep],
30
+ container: [unwrapStep, wrapStep],
23
31
  list: undefined,
24
- text: undefined
32
+ text: [unwrapStep]
25
33
  },
26
34
  list: {
27
35
  atomic: undefined,
28
36
  container: [stubStep],
29
37
  list: [stubStep],
30
- text: [stubStep]
38
+ text: [flattenListStep, unwrapListStep]
31
39
  },
32
40
  text: {
33
41
  atomic: undefined,
@@ -42,6 +50,23 @@ const TRANSFORM_STEPS = {
42
50
  const TRANSFORM_STEPS_OVERRIDE = {
43
51
  paragraph: {
44
52
  panel: [wrapIntoPanelStep]
53
+ },
54
+ panel: {
55
+ layoutSection: [unwrapStep, wrapIntoLayoutStep],
56
+ codeBlock: [unwrapStep, flattenStep, wrapStep]
57
+ },
58
+ expand: {
59
+ panel: [unwrapExpandStep, wrapStep],
60
+ blockquote: [unwrapExpandStep, wrapStep],
61
+ layoutSection: [unwrapExpandStep, wrapIntoLayoutStep],
62
+ paragraph: [unwrapExpandStep],
63
+ codeBlock: [unwrapExpandStep, flattenStep, wrapStep]
64
+ },
65
+ nestedExpand: {
66
+ panel: [unwrapExpandStep, wrapStep],
67
+ blockquote: [unwrapExpandStep, wrapStep],
68
+ paragraph: [unwrapExpandStep],
69
+ codeBlock: [unwrapExpandStep, flattenStep, wrapStep]
45
70
  }
46
71
  };
47
72
  const getTransformStepsForNodeTypes = (selectedNodeTypeName, targetNodeTypeName) => {
@@ -55,11 +80,13 @@ const getTransformStepsForNodeTypes = (selectedNodeTypeName, targetNodeTypeName)
55
80
  export const getOutputNodes = ({
56
81
  sourceNode,
57
82
  targetNodeType,
58
- schema
83
+ schema,
84
+ isNested
59
85
  }) => {
60
86
  const nodesToReplace = [sourceNode];
61
87
  const selectedNodeTypeName = toNodeTypeValue(sourceNode.type.name);
62
- const targetNodeTypeName = toNodeTypeValue(targetNodeType.name);
88
+ let targetNodeTypeName = toNodeTypeValue(targetNodeType.name);
89
+ targetNodeTypeName = getTargetNodeTypeNameInContext(targetNodeTypeName, isNested);
63
90
  if (!selectedNodeTypeName || !targetNodeTypeName) {
64
91
  // We may decide to return an empty array or undefined here
65
92
  return;
@@ -74,6 +101,7 @@ export const getOutputNodes = ({
74
101
  return;
75
102
  }
76
103
  return steps.reduce((nodes, step) => {
77
- return step(nodes, context);
104
+ const result = step(nodes, context);
105
+ return result;
78
106
  }, nodesToReplace);
79
107
  };
@@ -9,7 +9,7 @@ export const NODE_CATEGORY_BY_TYPE = {
9
9
  expand: 'container',
10
10
  extension: 'atomic',
11
11
  heading: 'text',
12
- layout: 'container',
12
+ layoutSection: 'container',
13
13
  media: 'atomic',
14
14
  mediaGroup: 'atomic',
15
15
  mediaSingle: 'atomic',
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Unwraps an expand/nestedExpand node, converting its title attribute to a paragraph
3
+ * and prepending it to the children.
4
+ *
5
+ * Example: expand({ title: 'title' })(p('b')) → [p('title'), p('b')]
6
+ */
7
+ export const unwrapExpandStep = (nodes, context) => {
8
+ const {
9
+ schema
10
+ } = context;
11
+ const outputNodes = [];
12
+ nodes.forEach(node => {
13
+ const isExpand = node.type.name === 'expand' || node.type.name === 'nestedExpand';
14
+ if (isExpand) {
15
+ var _node$attrs;
16
+ const title = (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.title;
17
+
18
+ // Create a paragraph from the title if it exists
19
+ if (title) {
20
+ const titleParagraph = schema.nodes.paragraph.createAndFill({}, schema.text(title));
21
+ if (titleParagraph) {
22
+ outputNodes.push(titleParagraph);
23
+ }
24
+ }
25
+
26
+ // Add the children
27
+ outputNodes.push(...node.children);
28
+ } else {
29
+ // Fallback: behave like unwrapStep for non-expand nodes
30
+ if (node.children.length === 0) {
31
+ outputNodes.push(node);
32
+ } else {
33
+ outputNodes.push(...node.children);
34
+ }
35
+ }
36
+ });
37
+ return outputNodes;
38
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Given an array of nodes, returns an array with the flattened children of any list nodes.
3
+ * @param nodes
4
+ * @returns
5
+ */
6
+ export const unwrapListStep = (nodes, context) => {
7
+ const listTypes = [context.schema.nodes.bulletList, context.schema.nodes.orderedList, context.schema.nodes.taskList];
8
+ return nodes.flatMap(node => {
9
+ if (listTypes.some(type => node.type === type)) {
10
+ const listItems = [];
11
+ node.forEach(listItem => {
12
+ listItems.push(...listItem.children);
13
+ });
14
+ return listItems;
15
+ }
16
+ return node;
17
+ });
18
+ };
@@ -0,0 +1,12 @@
1
+ export const unwrapStep = nodes => {
2
+ const outputNodes = [];
3
+ nodes.forEach(node => {
4
+ // we may want to just skip the original instead of using it
5
+ if (node.children.length === 0) {
6
+ outputNodes.push(node);
7
+ } else {
8
+ outputNodes.push(...node.children);
9
+ }
10
+ });
11
+ return outputNodes;
12
+ };
@@ -43,4 +43,10 @@ export const getSelectedNode = selection => {
43
43
  }
44
44
  }
45
45
  return undefined;
46
+ };
47
+ export const getTargetNodeTypeNameInContext = (nodeTypeName, isNested) => {
48
+ if (nodeTypeName === 'expand' && isNested) {
49
+ return 'nestedExpand';
50
+ }
51
+ return nodeTypeName;
46
52
  };
@@ -0,0 +1,20 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ export const wrapIntoLayoutStep = (nodes, context) => {
3
+ const {
4
+ schema
5
+ } = context;
6
+ const {
7
+ layoutSection,
8
+ layoutColumn
9
+ } = schema.nodes || {};
10
+ const columnOne = layoutColumn.createAndFill({}, Fragment.fromArray(nodes));
11
+ const columnTwo = layoutColumn.createAndFill();
12
+ if (!columnOne || !columnTwo) {
13
+ return nodes;
14
+ }
15
+ const layout = layoutSection.createAndFill({}, Fragment.fromArray([columnOne, columnTwo]));
16
+ if (!layout) {
17
+ return nodes;
18
+ }
19
+ return [layout];
20
+ };
@@ -0,0 +1,12 @@
1
+ export const wrapStep = (nodes, context) => {
2
+ const {
3
+ schema,
4
+ targetNodeTypeName
5
+ } = context;
6
+ // edge case: nestedExpand
7
+ const outputNode = schema.nodes[targetNodeTypeName].createAndFill({}, nodes);
8
+ if (outputNode) {
9
+ return [outputNode];
10
+ }
11
+ return nodes;
12
+ };
@@ -1,4 +1,6 @@
1
+ import { expandToBlockRange } from '@atlaskit/editor-common/selection';
1
2
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
3
+ import { isNestedNode } from '../ui/utils/isNestedNode';
2
4
  import { getOutputNodes } from './transform-node-utils/transform';
3
5
  import { isListNode } from './transforms/utils';
4
6
  export const transformNode = api =>
@@ -13,25 +15,28 @@ export const transformNode = api =>
13
15
  return tr;
14
16
  }
15
17
  const {
16
- from,
17
- to,
18
- $from
19
- } = preservedSelection;
18
+ $from,
19
+ $to
20
+ } = expandToBlockRange(preservedSelection.$from, preservedSelection.$to);
21
+ const isNested = isNestedNode(preservedSelection, '');
20
22
  const selectedParent = $from.parent;
21
23
  let fragment = Fragment.empty;
22
24
  const isList = isListNode(selectedParent);
23
- const slice = tr.doc.slice(isList ? from - 1 : from, isList ? to + 1 : to);
25
+ const slice = tr.doc.slice(isList ? $from.pos - 1 : $from.pos, isList ? $to.pos + 1 : $to.pos);
24
26
  slice.content.forEach(node => {
25
27
  const outputNode = getOutputNodes({
26
28
  sourceNode: node,
27
29
  targetNodeType: targetType,
28
- schema: tr.doc.type.schema
30
+ schema: tr.doc.type.schema,
31
+ isNested
29
32
  });
30
33
  if (outputNode) {
31
34
  fragment = fragment.append(Fragment.fromArray(outputNode));
32
35
  }
33
36
  });
34
- tr.replaceWith(isList ? preservedSelection.from - 1 : preservedSelection.from, preservedSelection.to, fragment);
37
+
38
+ // TODO: ED-12345 - selection is broken post transaction, to fix.
39
+ tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, fragment);
35
40
  return tr;
36
41
  };
37
42
  };
@@ -25,6 +25,7 @@ const styles = {
25
25
  };
26
26
  const DEFAULT_MENU_WIDTH = 230;
27
27
  const DRAG_HANDLE_OFFSET_PADDING = 5;
28
+ const FALLBACK_MENU_HEIGHT = 300;
28
29
  const PopupWithListeners = withReactEditorViewOuterListeners(Popup);
29
30
  const useConditionalBlockMenuEffect = conditionalHooksFactory(() => fg('platform_editor_toolbar_aifc_user_intent_fix'), ({
30
31
  api,
@@ -153,6 +154,15 @@ const BlockMenu = ({
153
154
  const targetHandleRef = editorView === null || editorView === void 0 ? void 0 : (_editorView$dom = editorView.dom) === null || _editorView$dom === void 0 ? void 0 : _editorView$dom.querySelector(DRAG_HANDLE_SELECTOR);
154
155
  const prevIsMenuOpenRef = useRef(false);
155
156
  const popupRef = useRef(undefined);
157
+ const [menuHeight, setMenuHeight] = React.useState(0);
158
+ const targetHandleHeightOffset = -((targetHandleRef === null || targetHandleRef === void 0 ? void 0 : targetHandleRef.clientHeight) || 0);
159
+ React.useLayoutEffect(() => {
160
+ var _popupRef$current;
161
+ if (!isMenuOpen) {
162
+ return;
163
+ }
164
+ setMenuHeight(((_popupRef$current = popupRef.current) === null || _popupRef$current === void 0 ? void 0 : _popupRef$current.clientHeight) || FALLBACK_MENU_HEIGHT);
165
+ }, [isMenuOpen]);
156
166
  const hasFocus = (_ref = (editorView === null || editorView === void 0 ? void 0 : editorView.hasFocus()) || document.activeElement === targetHandleRef || popupRef.current && (popupRef.current.contains(document.activeElement) || popupRef.current === document.activeElement)) !== null && _ref !== void 0 ? _ref : false;
157
167
  const selectedByShortcutOrDragHandle = !!isSelectedViaDragHandle || !!openedViaKeyboard;
158
168
 
@@ -218,8 +228,7 @@ const BlockMenu = ({
218
228
  fallbackComponent: null
219
229
  }, /*#__PURE__*/React.createElement(PopupWithListeners, {
220
230
  alignX: 'right',
221
- alignY: 'start' // respected when forcePlacement is true
222
- ,
231
+ alignY: 'start',
223
232
  handleClickOutside: closeMenu,
224
233
  handleEscapeKeydown: closeMenu,
225
234
  handleBackspaceDeleteKeydown: handleBackspaceDeleteKeydown,
@@ -229,16 +238,15 @@ const BlockMenu = ({
229
238
  target: targetHandleRef,
230
239
  zIndex: akEditorFloatingOverlapPanelZIndex,
231
240
  fitWidth: DEFAULT_MENU_WIDTH,
232
- forcePlacement: true,
233
- preventOverflow: true // disables forced horizontal placement when forcePlacement is on, so fitWidth controls flipping
234
- ,
241
+ fitHeight: menuHeight,
242
+ preventOverflow: true,
235
243
  stick: true,
244
+ offset: [DRAG_HANDLE_WIDTH + DRAG_HANDLE_OFFSET_PADDING, targetHandleHeightOffset],
236
245
  focusTrap: openedViaKeyboard ?
237
246
  // Only enable focus trap when opened via keyboard to make sure the focus is on the first focusable menu item
238
247
  {
239
248
  initialFocus: undefined
240
- } : undefined,
241
- offset: [DRAG_HANDLE_WIDTH + DRAG_HANDLE_OFFSET_PADDING, 0]
249
+ } : undefined
242
250
  }, /*#__PURE__*/React.createElement(BlockMenuContent, {
243
251
  api: api,
244
252
  setRef: setRef
@@ -0,0 +1,102 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ var extractNestedLists = function extractNestedLists(node, listTypes, itemTypes) {
3
+ var items = [];
4
+ var _extract = function extract(currentNode) {
5
+ currentNode.forEach(function (child) {
6
+ // list item -> take content without nested lists, then recurse into nested lists
7
+ if (itemTypes.some(function (type) {
8
+ return child.type === type;
9
+ })) {
10
+ // Filter out nested list nodes from the list item's content
11
+ var contentWithoutNestedLists = [];
12
+ var nestedLists = [];
13
+ child.forEach(function (grandChild) {
14
+ if (listTypes.some(function (type) {
15
+ return grandChild.type === type;
16
+ })) {
17
+ // This is a nested list - collect it for later processing
18
+ nestedLists.push(grandChild);
19
+ } else {
20
+ // This is regular content (paragraph, etc.) - keep it
21
+ contentWithoutNestedLists.push(grandChild);
22
+ }
23
+ });
24
+
25
+ // Add the list item with only its non-list content
26
+ items.push(child.copy(Fragment.from(contentWithoutNestedLists)));
27
+
28
+ // Now process nested lists to maintain document order
29
+ nestedLists.forEach(function (nestedList) {
30
+ _extract(nestedList);
31
+ });
32
+ }
33
+ // lists -> keep operating
34
+ else if (listTypes.some(function (type) {
35
+ return child.type === type;
36
+ })) {
37
+ _extract(child);
38
+ }
39
+ });
40
+ };
41
+ _extract(node);
42
+ return items;
43
+ };
44
+
45
+ /**
46
+ * Given an array of nodes, returns an array with the flattened children of any list node
47
+ * to it's first ancestor list, maintaining document order.
48
+ *
49
+ * @example
50
+ * Input:
51
+ * - bulletList
52
+ * - listItem "A"
53
+ * - listItem "B"
54
+ * - bulletList
55
+ * - listItem "C"
56
+ * - listItem "D"
57
+ * - listItem "E"
58
+ *
59
+ * Output:
60
+ * - bulletList
61
+ * - listItem "A"
62
+ * - listItem "B"
63
+ * - listItem "C"
64
+ * - listItem "D"
65
+ * - listItem "E"
66
+ *
67
+ * @example
68
+ * Input (deeply nested):
69
+ * - bulletList
70
+ * - listItem "1"
71
+ * - bulletList
72
+ * - listItem "1.1"
73
+ * - bulletList
74
+ * - listItem "1.1.1"
75
+ * - listItem "1.2"
76
+ * - listItem "2"
77
+ *
78
+ * Output:
79
+ * - bulletList
80
+ * - listItem "1"
81
+ * - listItem "1.1"
82
+ * - listItem "1.1.1"
83
+ * - listItem "1.2"
84
+ * - listItem "2"
85
+ *
86
+ * @param nodes
87
+ * @param context
88
+ * @returns
89
+ *
90
+ * TODO: Lists with mixed types (e.g. bulletList with a taskItem) doesn't full flatten
91
+ */
92
+ export var flattenListStep = function flattenListStep(nodes, context) {
93
+ var listTypes = [context.schema.nodes.bulletList, context.schema.nodes.orderedList, context.schema.nodes.taskList];
94
+ return nodes.map(function (node) {
95
+ if (listTypes.some(function (type) {
96
+ return node.type === type;
97
+ })) {
98
+ return node.copy(Fragment.from(extractNestedLists(node, listTypes, [context.schema.nodes.listItem, context.schema.nodes.taskItem])));
99
+ }
100
+ return node;
101
+ });
102
+ };
@@ -0,0 +1,15 @@
1
+ export var flattenStep = function flattenStep(nodes, context) {
2
+ var schema = context.schema,
3
+ targetNodeTypeName = context.targetNodeTypeName;
4
+
5
+ // TODO: EDITOR-2920 - Implement flattening logic.
6
+ // This is a simplified preliminary approach. We might want to use prosemirror-markdown functions.
7
+ var codeBlockContent = nodes.map(function (node) {
8
+ return node.content.textBetween(0, node.content.size, '\n');
9
+ }).join('\n');
10
+ var outputNode = schema.nodes[targetNodeTypeName].createAndFill({}, schema.text(codeBlockContent));
11
+ if (!outputNode) {
12
+ return nodes;
13
+ }
14
+ return [outputNode];
15
+ };
@@ -1,5 +1,13 @@
1
+ import { getTargetNodeTypeNameInContext } from '../transform-node-utils/utils';
2
+ import { flattenListStep } from './flattenListStep';
3
+ import { flattenStep } from './flattenStep';
1
4
  import { stubStep } from './stubStep';
2
5
  import { NODE_CATEGORY_BY_TYPE, toNodeTypeValue } from './types';
6
+ import { unwrapExpandStep } from './unwrapExpandStep';
7
+ import { unwrapListStep } from './unwrapListStep';
8
+ import { unwrapStep } from './unwrapStep';
9
+ import { wrapIntoLayoutStep } from './wrapIntoLayoutStep';
10
+ import { wrapStep } from './wrapStep';
3
11
 
4
12
  // Exampled step for overrides:
5
13
  // - open Block menu on a paragraph, click 'Panel' in the Turn into'
@@ -19,15 +27,15 @@ var TRANSFORM_STEPS = {
19
27
  },
20
28
  container: {
21
29
  atomic: undefined,
22
- container: [stubStep],
30
+ container: [unwrapStep, wrapStep],
23
31
  list: undefined,
24
- text: undefined
32
+ text: [unwrapStep]
25
33
  },
26
34
  list: {
27
35
  atomic: undefined,
28
36
  container: [stubStep],
29
37
  list: [stubStep],
30
- text: [stubStep]
38
+ text: [flattenListStep, unwrapListStep]
31
39
  },
32
40
  text: {
33
41
  atomic: undefined,
@@ -42,6 +50,23 @@ var TRANSFORM_STEPS = {
42
50
  var TRANSFORM_STEPS_OVERRIDE = {
43
51
  paragraph: {
44
52
  panel: [wrapIntoPanelStep]
53
+ },
54
+ panel: {
55
+ layoutSection: [unwrapStep, wrapIntoLayoutStep],
56
+ codeBlock: [unwrapStep, flattenStep, wrapStep]
57
+ },
58
+ expand: {
59
+ panel: [unwrapExpandStep, wrapStep],
60
+ blockquote: [unwrapExpandStep, wrapStep],
61
+ layoutSection: [unwrapExpandStep, wrapIntoLayoutStep],
62
+ paragraph: [unwrapExpandStep],
63
+ codeBlock: [unwrapExpandStep, flattenStep, wrapStep]
64
+ },
65
+ nestedExpand: {
66
+ panel: [unwrapExpandStep, wrapStep],
67
+ blockquote: [unwrapExpandStep, wrapStep],
68
+ paragraph: [unwrapExpandStep],
69
+ codeBlock: [unwrapExpandStep, flattenStep, wrapStep]
45
70
  }
46
71
  };
47
72
  var getTransformStepsForNodeTypes = function getTransformStepsForNodeTypes(selectedNodeTypeName, targetNodeTypeName) {
@@ -55,10 +80,12 @@ var getTransformStepsForNodeTypes = function getTransformStepsForNodeTypes(selec
55
80
  export var getOutputNodes = function getOutputNodes(_ref) {
56
81
  var sourceNode = _ref.sourceNode,
57
82
  targetNodeType = _ref.targetNodeType,
58
- schema = _ref.schema;
83
+ schema = _ref.schema,
84
+ isNested = _ref.isNested;
59
85
  var nodesToReplace = [sourceNode];
60
86
  var selectedNodeTypeName = toNodeTypeValue(sourceNode.type.name);
61
87
  var targetNodeTypeName = toNodeTypeValue(targetNodeType.name);
88
+ targetNodeTypeName = getTargetNodeTypeNameInContext(targetNodeTypeName, isNested);
62
89
  if (!selectedNodeTypeName || !targetNodeTypeName) {
63
90
  // We may decide to return an empty array or undefined here
64
91
  return;
@@ -73,6 +100,7 @@ export var getOutputNodes = function getOutputNodes(_ref) {
73
100
  return;
74
101
  }
75
102
  return steps.reduce(function (nodes, step) {
76
- return step(nodes, context);
103
+ var result = step(nodes, context);
104
+ return result;
77
105
  }, nodesToReplace);
78
106
  };
@@ -9,7 +9,7 @@ export var NODE_CATEGORY_BY_TYPE = {
9
9
  expand: 'container',
10
10
  extension: 'atomic',
11
11
  heading: 'text',
12
- layout: 'container',
12
+ layoutSection: 'container',
13
13
  media: 'atomic',
14
14
  mediaGroup: 'atomic',
15
15
  mediaSingle: 'atomic',
@@ -0,0 +1,37 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ /**
3
+ * Unwraps an expand/nestedExpand node, converting its title attribute to a paragraph
4
+ * and prepending it to the children.
5
+ *
6
+ * Example: expand({ title: 'title' })(p('b')) → [p('title'), p('b')]
7
+ */
8
+ export var unwrapExpandStep = function unwrapExpandStep(nodes, context) {
9
+ var schema = context.schema;
10
+ var outputNodes = [];
11
+ nodes.forEach(function (node) {
12
+ var isExpand = node.type.name === 'expand' || node.type.name === 'nestedExpand';
13
+ if (isExpand) {
14
+ var _node$attrs;
15
+ var title = (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.title;
16
+
17
+ // Create a paragraph from the title if it exists
18
+ if (title) {
19
+ var titleParagraph = schema.nodes.paragraph.createAndFill({}, schema.text(title));
20
+ if (titleParagraph) {
21
+ outputNodes.push(titleParagraph);
22
+ }
23
+ }
24
+
25
+ // Add the children
26
+ outputNodes.push.apply(outputNodes, _toConsumableArray(node.children));
27
+ } else {
28
+ // Fallback: behave like unwrapStep for non-expand nodes
29
+ if (node.children.length === 0) {
30
+ outputNodes.push(node);
31
+ } else {
32
+ outputNodes.push.apply(outputNodes, _toConsumableArray(node.children));
33
+ }
34
+ }
35
+ });
36
+ return outputNodes;
37
+ };
@@ -0,0 +1,21 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ /**
3
+ * Given an array of nodes, returns an array with the flattened children of any list nodes.
4
+ * @param nodes
5
+ * @returns
6
+ */
7
+ export var unwrapListStep = function unwrapListStep(nodes, context) {
8
+ var listTypes = [context.schema.nodes.bulletList, context.schema.nodes.orderedList, context.schema.nodes.taskList];
9
+ return nodes.flatMap(function (node) {
10
+ if (listTypes.some(function (type) {
11
+ return node.type === type;
12
+ })) {
13
+ var listItems = [];
14
+ node.forEach(function (listItem) {
15
+ listItems.push.apply(listItems, _toConsumableArray(listItem.children));
16
+ });
17
+ return listItems;
18
+ }
19
+ return node;
20
+ });
21
+ };
@@ -0,0 +1,13 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ export var unwrapStep = function unwrapStep(nodes) {
3
+ var outputNodes = [];
4
+ nodes.forEach(function (node) {
5
+ // we may want to just skip the original instead of using it
6
+ if (node.children.length === 0) {
7
+ outputNodes.push(node);
8
+ } else {
9
+ outputNodes.push.apply(outputNodes, _toConsumableArray(node.children));
10
+ }
11
+ });
12
+ return outputNodes;
13
+ };
@@ -42,4 +42,10 @@ export var getSelectedNode = function getSelectedNode(selection) {
42
42
  }
43
43
  }
44
44
  return undefined;
45
+ };
46
+ export var getTargetNodeTypeNameInContext = function getTargetNodeTypeNameInContext(nodeTypeName, isNested) {
47
+ if (nodeTypeName === 'expand' && isNested) {
48
+ return 'nestedExpand';
49
+ }
50
+ return nodeTypeName;
45
51
  };
@@ -0,0 +1,17 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ export var wrapIntoLayoutStep = function wrapIntoLayoutStep(nodes, context) {
3
+ var schema = context.schema;
4
+ var _ref = schema.nodes || {},
5
+ layoutSection = _ref.layoutSection,
6
+ layoutColumn = _ref.layoutColumn;
7
+ var columnOne = layoutColumn.createAndFill({}, Fragment.fromArray(nodes));
8
+ var columnTwo = layoutColumn.createAndFill();
9
+ if (!columnOne || !columnTwo) {
10
+ return nodes;
11
+ }
12
+ var layout = layoutSection.createAndFill({}, Fragment.fromArray([columnOne, columnTwo]));
13
+ if (!layout) {
14
+ return nodes;
15
+ }
16
+ return [layout];
17
+ };
@@ -0,0 +1,10 @@
1
+ export var wrapStep = function wrapStep(nodes, context) {
2
+ var schema = context.schema,
3
+ targetNodeTypeName = context.targetNodeTypeName;
4
+ // edge case: nestedExpand
5
+ var outputNode = schema.nodes[targetNodeTypeName].createAndFill({}, nodes);
6
+ if (outputNode) {
7
+ return [outputNode];
8
+ }
9
+ return nodes;
10
+ };
@@ -1,4 +1,6 @@
1
+ import { expandToBlockRange } from '@atlaskit/editor-common/selection';
1
2
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
3
+ import { isNestedNode } from '../ui/utils/isNestedNode';
2
4
  import { getOutputNodes } from './transform-node-utils/transform';
3
5
  import { isListNode } from './transforms/utils';
4
6
  export var transformNode = function transformNode(api) {
@@ -12,24 +14,28 @@ export var transformNode = function transformNode(api) {
12
14
  if (!preservedSelection) {
13
15
  return tr;
14
16
  }
15
- var from = preservedSelection.from,
16
- to = preservedSelection.to,
17
- $from = preservedSelection.$from;
17
+ var _expandToBlockRange = expandToBlockRange(preservedSelection.$from, preservedSelection.$to),
18
+ $from = _expandToBlockRange.$from,
19
+ $to = _expandToBlockRange.$to;
20
+ var isNested = isNestedNode(preservedSelection, '');
18
21
  var selectedParent = $from.parent;
19
22
  var fragment = Fragment.empty;
20
23
  var isList = isListNode(selectedParent);
21
- var slice = tr.doc.slice(isList ? from - 1 : from, isList ? to + 1 : to);
24
+ var slice = tr.doc.slice(isList ? $from.pos - 1 : $from.pos, isList ? $to.pos + 1 : $to.pos);
22
25
  slice.content.forEach(function (node) {
23
26
  var outputNode = getOutputNodes({
24
27
  sourceNode: node,
25
28
  targetNodeType: targetType,
26
- schema: tr.doc.type.schema
29
+ schema: tr.doc.type.schema,
30
+ isNested: isNested
27
31
  });
28
32
  if (outputNode) {
29
33
  fragment = fragment.append(Fragment.fromArray(outputNode));
30
34
  }
31
35
  });
32
- tr.replaceWith(isList ? preservedSelection.from - 1 : preservedSelection.from, preservedSelection.to, fragment);
36
+
37
+ // TODO: ED-12345 - selection is broken post transaction, to fix.
38
+ tr.replaceWith(isList ? $from.pos - 1 : $from.pos, $to.pos, fragment);
33
39
  return tr;
34
40
  };
35
41
  }