@atlaskit/editor-plugin-layout 10.4.0 → 10.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/cjs/layoutPlugin.js +96 -25
  3. package/dist/cjs/pm-plugins/actions.js +87 -64
  4. package/dist/cjs/pm-plugins/main.js +4 -2
  5. package/dist/cjs/pm-plugins/utils/layout-column-selection.js +128 -0
  6. package/dist/cjs/ui/LayoutColumnMenu/DeleteColumnDropdownItem.js +7 -4
  7. package/dist/cjs/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +5 -6
  8. package/dist/cjs/ui/LayoutColumnMenu/VerticalAlignDropdownItem.js +9 -7
  9. package/dist/cjs/ui/LayoutColumnMenu/VerticalAlignNestedMenu.js +15 -6
  10. package/dist/cjs/ui/LayoutColumnMenu/index.js +17 -7
  11. package/dist/cjs/ui/LayoutColumnMenu/useSelectedLayoutColumns.js +14 -0
  12. package/dist/es2019/layoutPlugin.js +80 -18
  13. package/dist/es2019/pm-plugins/actions.js +73 -58
  14. package/dist/es2019/pm-plugins/main.js +4 -2
  15. package/dist/es2019/pm-plugins/utils/layout-column-selection.js +118 -0
  16. package/dist/es2019/ui/LayoutColumnMenu/DeleteColumnDropdownItem.js +7 -4
  17. package/dist/es2019/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +5 -6
  18. package/dist/es2019/ui/LayoutColumnMenu/VerticalAlignDropdownItem.js +9 -6
  19. package/dist/es2019/ui/LayoutColumnMenu/VerticalAlignNestedMenu.js +15 -5
  20. package/dist/es2019/ui/LayoutColumnMenu/index.js +16 -6
  21. package/dist/es2019/ui/LayoutColumnMenu/useSelectedLayoutColumns.js +6 -0
  22. package/dist/esm/layoutPlugin.js +97 -26
  23. package/dist/esm/pm-plugins/actions.js +87 -64
  24. package/dist/esm/pm-plugins/main.js +4 -2
  25. package/dist/esm/pm-plugins/utils/layout-column-selection.js +122 -0
  26. package/dist/esm/ui/LayoutColumnMenu/DeleteColumnDropdownItem.js +7 -4
  27. package/dist/esm/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +5 -6
  28. package/dist/esm/ui/LayoutColumnMenu/VerticalAlignDropdownItem.js +10 -8
  29. package/dist/esm/ui/LayoutColumnMenu/VerticalAlignNestedMenu.js +15 -6
  30. package/dist/esm/ui/LayoutColumnMenu/index.js +17 -7
  31. package/dist/esm/ui/LayoutColumnMenu/useSelectedLayoutColumns.js +8 -0
  32. package/dist/types/layoutPluginType.d.ts +1 -1
  33. package/dist/types/pm-plugins/actions.d.ts +3 -2
  34. package/dist/types/pm-plugins/types.d.ts +1 -0
  35. package/dist/types/pm-plugins/utils/layout-column-selection.d.ts +18 -0
  36. package/dist/types/ui/LayoutColumnMenu/useSelectedLayoutColumns.d.ts +4 -0
  37. package/dist/types-ts4.5/layoutPluginType.d.ts +1 -1
  38. package/dist/types-ts4.5/pm-plugins/actions.d.ts +3 -2
  39. package/dist/types-ts4.5/pm-plugins/types.d.ts +1 -0
  40. package/dist/types-ts4.5/pm-plugins/utils/layout-column-selection.d.ts +18 -0
  41. package/dist/types-ts4.5/ui/LayoutColumnMenu/useSelectedLayoutColumns.d.ts +4 -0
  42. package/package.json +5 -4
  43. package/dist/cjs/ui/LayoutColumnMenu/layoutColumnSelection.js +0 -21
  44. package/dist/cjs/ui/LayoutColumnMenu/useCurrentLayoutColumn.js +0 -20
  45. package/dist/es2019/ui/LayoutColumnMenu/layoutColumnSelection.js +0 -9
  46. package/dist/es2019/ui/LayoutColumnMenu/useCurrentLayoutColumn.js +0 -10
  47. package/dist/esm/ui/LayoutColumnMenu/layoutColumnSelection.js +0 -15
  48. package/dist/esm/ui/LayoutColumnMenu/useCurrentLayoutColumn.js +0 -14
  49. package/dist/types/ui/LayoutColumnMenu/layoutColumnSelection.d.ts +0 -7
  50. package/dist/types/ui/LayoutColumnMenu/useCurrentLayoutColumn.d.ts +0 -5
  51. package/dist/types-ts4.5/ui/LayoutColumnMenu/layoutColumnSelection.d.ts +0 -7
  52. package/dist/types-ts4.5/ui/LayoutColumnMenu/useCurrentLayoutColumn.d.ts +0 -5
@@ -2,14 +2,14 @@ import React, { useCallback } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
3
  import { layoutMessages } from '@atlaskit/editor-common/messages';
4
4
  import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
5
- import { useCurrentLayoutColumn } from './useCurrentLayoutColumn';
5
+ import { useSelectedLayoutColumns } from './useSelectedLayoutColumns';
6
6
  const DeleteColumnDropdownItem = ({
7
7
  api
8
8
  }) => {
9
9
  const {
10
10
  formatMessage
11
11
  } = useIntl();
12
- const currentColumn = useCurrentLayoutColumn(api);
12
+ const selectedLayoutColumns = useSelectedLayoutColumns(api);
13
13
  const onClick = useCallback(() => {
14
14
  var _api$layout, _api$core;
15
15
  const deleteCommand = api === null || api === void 0 ? void 0 : (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.deleteLayoutColumn;
@@ -27,11 +27,14 @@ const DeleteColumnDropdownItem = ({
27
27
  return tr;
28
28
  });
29
29
  }, [api]);
30
- if (currentColumn === undefined) {
30
+ if (selectedLayoutColumns === undefined) {
31
31
  return null;
32
32
  }
33
+ const selectedColumnCount = selectedLayoutColumns.selectedColumns.length;
33
34
  return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
34
35
  onClick: onClick
35
- }, formatMessage(layoutMessages.deleteColumn));
36
+ }, formatMessage(layoutMessages.deleteColumn, {
37
+ count: selectedColumnCount
38
+ }));
36
39
  };
37
40
  export { DeleteColumnDropdownItem };
@@ -3,8 +3,8 @@ import { useIntl } from 'react-intl';
3
3
  import { layoutMessages } from '@atlaskit/editor-common/messages';
4
4
  import { TableColumnAddLeftIcon, TableColumnAddRightIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
5
5
  import { getEffectiveMaxLayoutColumns } from '../../pm-plugins/actions';
6
- import { getLayoutSectionColumnCount } from './layoutColumnSelection';
7
- import { useCurrentLayoutColumn, useCurrentLayoutSection } from './useCurrentLayoutColumn';
6
+ import { getLayoutSectionColumnCount } from '../../pm-plugins/utils/layout-column-selection';
7
+ import { useSelectedLayoutColumns } from './useSelectedLayoutColumns';
8
8
  const INSERT_COLUMN_OPTIONS = {
9
9
  left: {
10
10
  Icon: TableColumnAddLeftIcon,
@@ -28,11 +28,10 @@ export const InsertColumnDropdownItem = ({
28
28
  Icon,
29
29
  label
30
30
  } = INSERT_COLUMN_OPTIONS[side];
31
- const currentColumn = useCurrentLayoutColumn(api);
32
- const currentLayoutSection = useCurrentLayoutSection(api);
33
- const columnCount = getLayoutSectionColumnCount(currentLayoutSection);
31
+ const selectedLayoutColumns = useSelectedLayoutColumns(api);
32
+ const columnCount = getLayoutSectionColumnCount(selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.layoutSectionNode);
34
33
  const maxColumnCount = getEffectiveMaxLayoutColumns();
35
- const canInsertColumn = currentColumn !== undefined && columnCount < maxColumnCount;
34
+ const canInsertColumn = selectedLayoutColumns !== undefined && columnCount < maxColumnCount;
36
35
  const onClick = useCallback(() => {
37
36
  var _api$layout, _api$core;
38
37
  const insertCommand = api === null || api === void 0 ? void 0 : (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.insertLayoutColumn(side);
@@ -1,19 +1,22 @@
1
- import React, { useCallback, useMemo } from 'react';
1
+ import React, { useCallback } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
3
  import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
4
- import { getLayoutColumnValign } from './layoutColumnSelection';
5
- import { useCurrentLayoutColumn } from './useCurrentLayoutColumn';
4
+ import { getLayoutColumnValign } from '../../pm-plugins/utils/layout-column-selection';
5
+ import { useSelectedLayoutColumns } from './useSelectedLayoutColumns';
6
6
  import { VERTICAL_ALIGN_ICONS } from './verticalAlignIcons';
7
7
  export const VerticalAlignDropdownItem = ({
8
8
  api,
9
9
  label,
10
10
  value
11
11
  }) => {
12
+ var _selectedLayoutColumn;
12
13
  const {
13
14
  formatMessage
14
15
  } = useIntl();
15
- const currentColumn = useCurrentLayoutColumn(api);
16
- const currentValign = useMemo(() => getLayoutColumnValign(currentColumn), [currentColumn]);
16
+ const selectedLayoutColumns = useSelectedLayoutColumns(api);
17
+ const isSelected = (_selectedLayoutColumn = selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.selectedColumns.every(({
18
+ node
19
+ }) => getLayoutColumnValign(node) === value)) !== null && _selectedLayoutColumn !== void 0 ? _selectedLayoutColumn : false;
17
20
  const Icon = VERTICAL_ALIGN_ICONS[value];
18
21
  const onClick = useCallback(() => {
19
22
  var _api$core, _api$layout;
@@ -24,7 +27,7 @@ export const VerticalAlignDropdownItem = ({
24
27
  label: "",
25
28
  size: "small"
26
29
  }),
27
- isSelected: currentValign === value,
30
+ isSelected: isSelected,
28
31
  onClick: onClick
29
32
  }, formatMessage(label));
30
33
  };
@@ -2,8 +2,8 @@ import React, { useMemo } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
3
  import { layoutMessages } from '@atlaskit/editor-common/messages';
4
4
  import { LayoutIcon, NestedDropdownRightIcon, ToolbarNestedDropdownMenu } from '@atlaskit/editor-toolbar';
5
- import { getLayoutColumnValign } from './layoutColumnSelection';
6
- import { useCurrentLayoutColumn } from './useCurrentLayoutColumn';
5
+ import { getLayoutColumnValign } from '../../pm-plugins/utils/layout-column-selection';
6
+ import { useSelectedLayoutColumns } from './useSelectedLayoutColumns';
7
7
  import { VERTICAL_ALIGN_ICONS } from './verticalAlignIcons';
8
8
  export const VerticalAlignNestedMenu = ({
9
9
  api,
@@ -12,10 +12,20 @@ export const VerticalAlignNestedMenu = ({
12
12
  const {
13
13
  formatMessage
14
14
  } = useIntl();
15
- const currentColumn = useCurrentLayoutColumn(api);
16
- const currentValign = useMemo(() => getLayoutColumnValign(currentColumn), [currentColumn]);
15
+ const selectedLayoutColumns = useSelectedLayoutColumns(api);
16
+ const currentValign = useMemo(() => {
17
+ const selectedColumns = selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.selectedColumns;
18
+ const firstColumn = selectedColumns === null || selectedColumns === void 0 ? void 0 : selectedColumns[0];
19
+ const firstValign = getLayoutColumnValign(firstColumn === null || firstColumn === void 0 ? void 0 : firstColumn.node);
20
+ if (!firstValign || !(selectedColumns !== null && selectedColumns !== void 0 && selectedColumns.every(({
21
+ node
22
+ }) => getLayoutColumnValign(node) === firstValign))) {
23
+ return undefined;
24
+ }
25
+ return firstValign;
26
+ }, [selectedLayoutColumns]);
17
27
  const TriggerIcon = currentValign ? VERTICAL_ALIGN_ICONS[currentValign] : LayoutIcon;
18
- if (!currentColumn) {
28
+ if (!selectedLayoutColumns) {
19
29
  return null;
20
30
  }
21
31
  return /*#__PURE__*/React.createElement(ToolbarNestedDropdownMenu, {
@@ -7,9 +7,9 @@ import { OutsideClickTargetRefContext, withReactEditorViewOuterListeners } from
7
7
  import { akEditorFloatingOverlapPanelZIndex } from '@atlaskit/editor-shared-styles';
8
8
  import { ToolbarDropdownMenuProvider } from '@atlaskit/editor-toolbar';
9
9
  import { SurfaceRenderer } from '@atlaskit/editor-ui-control-model';
10
+ import { getLayoutColumnMenuAnchorPos } from '../../pm-plugins/utils/layout-column-selection';
10
11
  import { LAYOUT_COLUMN_MENU_FALLBACKS } from './components';
11
12
  import { LAYOUT_COLUMN_MENU } from './keys';
12
- import { getLayoutColumnAtSelection } from './layoutColumnSelection';
13
13
  const PopupWithListeners = withReactEditorViewOuterListeners(Popup);
14
14
  const FALLBACK_MENU_HEIGHT = 300;
15
15
  const LAYOUT_COLUMN_MENU_POPUP_OFFSET = [0, 4];
@@ -17,12 +17,13 @@ const LAYOUT_COLUMN_MENU_POPUP_OFFSET = [0, 4];
17
17
  /**
18
18
  * Returns the drag handle button for the selected layout column.
19
19
  */
20
- const getLayoutColumnMenuTarget = (editorView, selection) => {
20
+ const getLayoutColumnMenuTarget = (editorView, selection, anchorPosFromHandle) => {
21
21
  var _columnDomRef$parentE;
22
- if (!getLayoutColumnAtSelection(selection) || (selection === null || selection === void 0 ? void 0 : selection.from) === undefined) {
22
+ const anchorPos = getLayoutColumnMenuAnchorPos(selection, anchorPosFromHandle);
23
+ if (anchorPos === undefined) {
23
24
  return null;
24
25
  }
25
- const columnDomRef = editorView.nodeDOM(selection.from);
26
+ const columnDomRef = editorView.nodeDOM(anchorPos);
26
27
  if (!(columnDomRef instanceof HTMLElement)) {
27
28
  return null;
28
29
  }
@@ -39,11 +40,13 @@ export const LayoutColumnMenu = /*#__PURE__*/React.memo(function LayoutColumnMen
39
40
  var _api$uiControlRegistr, _api$uiControlRegistr2;
40
41
  const {
41
42
  isLayoutColumnMenuOpen,
43
+ layoutColumnMenuAnchorPos,
42
44
  selection
43
45
  } = useSharedPluginStateWithSelector(api, ['layout', 'selection'], states => {
44
- var _states$layoutState$i, _states$layoutState, _states$selectionStat;
46
+ var _states$layoutState$i, _states$layoutState, _states$layoutState2, _states$selectionStat;
45
47
  return {
46
48
  isLayoutColumnMenuOpen: (_states$layoutState$i = (_states$layoutState = states.layoutState) === null || _states$layoutState === void 0 ? void 0 : _states$layoutState.isLayoutColumnMenuOpen) !== null && _states$layoutState$i !== void 0 ? _states$layoutState$i : false,
49
+ layoutColumnMenuAnchorPos: (_states$layoutState2 = states.layoutState) === null || _states$layoutState2 === void 0 ? void 0 : _states$layoutState2.layoutColumnMenuAnchorPos,
47
50
  selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection
48
51
  };
49
52
  });
@@ -72,6 +75,13 @@ export const LayoutColumnMenu = /*#__PURE__*/React.memo(function LayoutColumnMen
72
75
  if (event.target instanceof Element && event.target.closest('[data-toolbar-nested-dropdown-menu]')) {
73
76
  return;
74
77
  }
78
+
79
+ // Clicking a drag handle should let the drag handle's own click handler
80
+ // update selection/menu state. Treating it as a generic outside click
81
+ // races that transaction and can immediately close the layout column menu.
82
+ if (event.target instanceof Element && event.target.closest(DRAG_HANDLE_SELECTOR)) {
83
+ return;
84
+ }
75
85
  closeLayoutColumnMenu();
76
86
  }, [closeLayoutColumnMenu]);
77
87
  const handleSetIsOpen = useCallback(isOpen => {
@@ -87,7 +97,7 @@ export const LayoutColumnMenu = /*#__PURE__*/React.memo(function LayoutColumnMen
87
97
  }
88
98
  }, [setOutsideClickTargetRef]);
89
99
  const components = (_api$uiControlRegistr = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(LAYOUT_COLUMN_MENU.key)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
90
- const target = useMemo(() => isLayoutColumnMenuOpen ? getLayoutColumnMenuTarget(editorView, selection) : null, [editorView, isLayoutColumnMenuOpen, selection]);
100
+ const target = useMemo(() => isLayoutColumnMenuOpen ? getLayoutColumnMenuTarget(editorView, selection, layoutColumnMenuAnchorPos) : null, [editorView, isLayoutColumnMenuOpen, layoutColumnMenuAnchorPos, selection]);
91
101
  const hasValidTarget = target instanceof HTMLElement;
92
102
  useEffect(() => {
93
103
  if (isLayoutColumnMenuOpen && (!hasValidTarget || components.length === 0)) {
@@ -0,0 +1,6 @@
1
+ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
2
+ import { getSelectedLayoutColumns } from '../../pm-plugins/utils/layout-column-selection';
3
+ export const useSelectedLayoutColumns = api => useSharedPluginStateWithSelector(api, ['selection'], states => {
4
+ var _states$selectionStat;
5
+ return getSelectedLayoutColumns((_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection);
6
+ });
@@ -8,9 +8,10 @@ import { IconFiveColumnLayout, IconFourColumnLayout, IconLayout, IconOneColumnLa
8
8
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
9
9
  import { findParentNode } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { fg } from '@atlaskit/platform-feature-flags';
11
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
11
12
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
12
13
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
13
- import { createDefaultLayoutSection, createMultiColumnLayoutSection, deleteLayoutColumn as _deleteLayoutColumn, insertLayoutColumn as _insertLayoutColumn, insertLayoutColumnsWithAnalytics, setLayoutColumnValign, toggleLayoutColumnMenu } from './pm-plugins/actions';
14
+ import { createDefaultLayoutSection, createMultiColumnLayoutSection, deleteLayoutColumn as _deleteLayoutColumn, insertLayoutColumn as _insertLayoutColumn, insertLayoutColumnsWithAnalytics, setLayoutColumnValign as _setLayoutColumnValign, toggleLayoutColumnMenu } from './pm-plugins/actions';
14
15
  import { default as createLayoutPlugin } from './pm-plugins/main';
15
16
  import { pluginKey } from './pm-plugins/plugin-key';
16
17
  import { default as createLayoutResizingPlugin } from './pm-plugins/resizing';
@@ -148,27 +149,94 @@ export var layoutPlugin = function layoutPlugin(_ref) {
148
149
  })(tr);
149
150
  return tr;
150
151
  };
151
- var advancedSingleColumnOption = allowAdvancedSingleColumnLayout ? [{
152
- id: 'onecolumnlayout',
153
- title: formatMessage(layoutMessages.singleColumnAdvancedLayout),
154
- description: formatMessage(messages.singleColumnsDescriptionAdvancedLayout),
155
- keywords: ['layout', 'column', 'section', 'single column'],
156
- priority: 1100,
157
- icon: function icon() {
158
- return /*#__PURE__*/React.createElement(IconOneColumnLayout, null);
159
- },
160
- action: function action(insert, state) {
161
- var tr = insert(createMultiColumnLayoutSection(state, 1));
162
- if (fg('platform_editor_column_count_analytics')) {
163
- withInsertLayoutAnalytics(tr, 1);
164
- } else {
165
- withInsertLayoutAnalytics(tr);
152
+ if (editorExperiment('advanced_layouts', true)) {
153
+ var advancedSingleColumnOption = allowAdvancedSingleColumnLayout ? [{
154
+ id: 'onecolumnlayout',
155
+ title: formatMessage(layoutMessages.singleColumnAdvancedLayout),
156
+ description: formatMessage(messages.singleColumnsDescriptionAdvancedLayout),
157
+ keywords: ['layout', 'column', 'section', 'single column'],
158
+ priority: 1100,
159
+ icon: function icon() {
160
+ return /*#__PURE__*/React.createElement(IconOneColumnLayout, null);
161
+ },
162
+ action: function action(insert, state) {
163
+ var tr = insert(createMultiColumnLayoutSection(state, 1));
164
+ if (fg('platform_editor_column_count_analytics')) {
165
+ withInsertLayoutAnalytics(tr, 1);
166
+ } else {
167
+ withInsertLayoutAnalytics(tr);
168
+ }
169
+ selectIntoLayoutSection(tr);
170
+ return tr;
166
171
  }
167
- selectIntoLayoutSection(tr);
168
- return tr;
172
+ }] : [];
173
+ if (expValEquals('platform_editor_layout_typeahead_reorder', 'isEnabled', true)) {
174
+ var createAdvancedColumnLayoutOption = function createAdvancedColumnLayoutOption(_ref4) {
175
+ var columnCount = _ref4.columnCount,
176
+ descriptionColumnCount = _ref4.descriptionColumnCount,
177
+ Icon = _ref4.icon,
178
+ id = _ref4.id,
179
+ keyword = _ref4.keyword,
180
+ title = _ref4.title;
181
+ return {
182
+ id: id,
183
+ title: title,
184
+ description: formatMessage(messages.columnsDescriptionAdvancedLayout, {
185
+ numberOfColumns: descriptionColumnCount
186
+ }),
187
+ keywords: ['layout', 'column', 'section', keyword],
188
+ priority: 1100,
189
+ icon: Icon,
190
+ action: function action(insert, state) {
191
+ var tr = insert(createMultiColumnLayoutSection(state, columnCount));
192
+ if (fg('platform_editor_column_count_analytics')) {
193
+ withInsertLayoutAnalytics(tr, columnCount);
194
+ } else {
195
+ withInsertLayoutAnalytics(tr);
196
+ }
197
+ selectIntoLayoutSection(tr);
198
+ return tr;
199
+ }
200
+ };
201
+ };
202
+ return [createAdvancedColumnLayoutOption({
203
+ columnCount: 2,
204
+ descriptionColumnCount: 'two',
205
+ icon: function icon() {
206
+ return /*#__PURE__*/React.createElement(IconTwoColumnLayout, null);
207
+ },
208
+ id: 'twocolumnslayout',
209
+ keyword: 'two column',
210
+ title: formatMessage(layoutMessages.twoColumnsAdvancedLayout)
211
+ })].concat(advancedSingleColumnOption, [createAdvancedColumnLayoutOption({
212
+ columnCount: 3,
213
+ descriptionColumnCount: 'three',
214
+ icon: function icon() {
215
+ return /*#__PURE__*/React.createElement(IconThreeColumnLayout, null);
216
+ },
217
+ id: 'threecolumnslayout',
218
+ keyword: 'three column',
219
+ title: formatMessage(layoutMessages.threeColumnsAdvancedLayout)
220
+ }), createAdvancedColumnLayoutOption({
221
+ columnCount: 4,
222
+ descriptionColumnCount: 'four',
223
+ icon: function icon() {
224
+ return /*#__PURE__*/React.createElement(IconFourColumnLayout, null);
225
+ },
226
+ id: 'fourcolumnslayout',
227
+ keyword: 'four column',
228
+ title: formatMessage(layoutMessages.fourColumns)
229
+ }), createAdvancedColumnLayoutOption({
230
+ columnCount: 5,
231
+ descriptionColumnCount: 'five',
232
+ icon: function icon() {
233
+ return /*#__PURE__*/React.createElement(IconFiveColumnLayout, null);
234
+ },
235
+ id: 'fivecolumnslayout',
236
+ keyword: 'five column',
237
+ title: formatMessage(layoutMessages.fiveColumns)
238
+ })]);
169
239
  }
170
- }] : [];
171
- if (editorExperiment('advanced_layouts', true)) {
172
240
  return [].concat(advancedSingleColumnOption, [{
173
241
  id: 'twocolumnslayout',
174
242
  title: formatMessage(layoutMessages.twoColumnsAdvancedLayout),
@@ -282,11 +350,11 @@ export var layoutPlugin = function layoutPlugin(_ref) {
282
350
  }
283
351
  }
284
352
  },
285
- contentComponent: function contentComponent(_ref4) {
286
- var editorView = _ref4.editorView,
287
- popupsMountPoint = _ref4.popupsMountPoint,
288
- popupsBoundariesElement = _ref4.popupsBoundariesElement,
289
- popupsScrollableElement = _ref4.popupsScrollableElement;
353
+ contentComponent: function contentComponent(_ref5) {
354
+ var editorView = _ref5.editorView,
355
+ popupsMountPoint = _ref5.popupsMountPoint,
356
+ popupsBoundariesElement = _ref5.popupsBoundariesElement,
357
+ popupsScrollableElement = _ref5.popupsScrollableElement;
290
358
  return /*#__PURE__*/React.createElement(React.Fragment, null, editorExperiment('advanced_layouts', true) ? /*#__PURE__*/React.createElement(GlobalStylesWrapper, null) : null, expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true) && editorView ? /*#__PURE__*/React.createElement(LayoutColumnMenu, {
291
359
  api: api,
292
360
  editorView: editorView,
@@ -310,7 +378,10 @@ export var layoutPlugin = function layoutPlugin(_ref) {
310
378
  var _api$analytics5;
311
379
  return _insertLayoutColumn(side, api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions);
312
380
  },
313
- setLayoutColumnValign: setLayoutColumnValign,
381
+ setLayoutColumnValign: function setLayoutColumnValign(valign) {
382
+ var _api$analytics6;
383
+ return _setLayoutColumnValign(valign, api === null || api === void 0 || (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions);
384
+ },
314
385
  toggleLayoutColumnMenu: toggleLayoutColumnMenu
315
386
  }
316
387
  };
@@ -12,6 +12,7 @@ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equ
12
12
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
13
13
  import { EVEN_DISTRIBUTED_COL_WIDTHS, MAX_LAYOUT_COLUMNS, MAX_STANDARD_LAYOUT_COLUMNS, MIN_LAYOUT_COLUMN_WIDTH_PERCENT } from './consts';
14
14
  import { pluginKey } from './plugin-key';
15
+ import { getSelectedLayoutColumns } from './utils/layout-column-selection';
15
16
  import { redistributeAfterDeletion, redistributeProportionally } from './utils/redistribute-proportionally';
16
17
  export var ONE_COL_LAYOUTS = ['single'];
17
18
  export var TWO_COL_LAYOUTS = ['two_equal', 'two_left_sidebar', 'two_right_sidebar'];
@@ -547,44 +548,24 @@ var formatLayoutName = function formatLayoutName(layout) {
547
548
  export function getEffectiveMaxLayoutColumns() {
548
549
  return editorExperiment('advanced_layouts', true) ? MAX_LAYOUT_COLUMNS : MAX_STANDARD_LAYOUT_COLUMNS;
549
550
  }
550
- var getSelectedLayoutColumnInSection = function getSelectedLayoutColumnInSection(_ref4) {
551
- var doc = _ref4.doc,
552
- selection = _ref4.selection;
553
- var _doc$type$schema$node = doc.type.schema.nodes,
554
- layoutColumn = _doc$type$schema$node.layoutColumn,
555
- layoutSection = _doc$type$schema$node.layoutSection;
556
- if (!(selection instanceof NodeSelection) || selection.node.type !== layoutColumn) {
557
- return;
558
- }
559
- var $from = selection.$from;
560
- if ($from.parent.type !== layoutSection) {
561
- return;
562
- }
563
- var selectedColumnIndex = $from.index($from.depth);
564
- var selectedColumnNode = selection.node;
565
- var selectedColumnPos = selection.from;
566
- var layoutSectionPos = $from.before($from.depth);
567
- return {
568
- layoutSectionNode: $from.parent,
569
- layoutSectionPos: layoutSectionPos,
570
- selectedColumnIndex: selectedColumnIndex,
571
- selectedColumnNode: selectedColumnNode,
572
- selectedColumnPos: selectedColumnPos
573
- };
574
- };
575
551
  var insertLayoutColumnAt = function insertLayoutColumnAt(side, editorAnalyticsAPI) {
576
- return function (_ref5) {
577
- var tr = _ref5.tr;
552
+ return function (_ref4) {
553
+ var tr = _ref4.tr;
578
554
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
579
555
  return null;
580
556
  }
581
- var selectedColumn = getSelectedLayoutColumnInSection(tr);
582
- if (!selectedColumn) {
557
+ var selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
558
+ if (!selectedLayoutColumns) {
583
559
  return null;
584
560
  }
585
- var layoutSectionNode = selectedColumn.layoutSectionNode,
586
- layoutSectionPos = selectedColumn.layoutSectionPos,
587
- selectedColumnIndex = selectedColumn.selectedColumnIndex;
561
+ var layoutSectionNode = selectedLayoutColumns.layoutSectionNode,
562
+ layoutSectionPos = selectedLayoutColumns.layoutSectionPos,
563
+ selectedColumnIndices = selectedLayoutColumns.selectedColumnIndices,
564
+ selectedColumns = selectedLayoutColumns.selectedColumns;
565
+ var startIndex = selectedColumnIndices[0];
566
+ var endIndex = selectedColumnIndices[selectedColumnIndices.length - 1];
567
+ var selectedColumnIndex = side === 'left' ? startIndex : endIndex;
568
+ var selectedColumnCount = selectedColumns.length;
588
569
  if (layoutSectionNode.childCount >= getEffectiveMaxLayoutColumns()) {
589
570
  return null;
590
571
  }
@@ -621,9 +602,11 @@ var insertLayoutColumnAt = function insertLayoutColumnAt(side, editorAnalyticsAP
621
602
  actionSubjectId: ACTION_SUBJECT_ID.LAYOUT_COLUMN,
622
603
  attributes: {
623
604
  columnCount: redistributedWidths.length,
605
+ endIndex: endIndex,
624
606
  inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
625
- selectedIndex: selectedColumnIndex,
626
- side: side
607
+ selectedCount: selectedColumnCount,
608
+ side: side,
609
+ startIndex: startIndex
627
610
  },
628
611
  eventType: EVENT_TYPE.TRACK
629
612
  })(tr);
@@ -634,35 +617,61 @@ var insertLayoutColumnAt = function insertLayoutColumnAt(side, editorAnalyticsAP
634
617
  export var insertLayoutColumn = function insertLayoutColumn(side, editorAnalyticsAPI) {
635
618
  return insertLayoutColumnAt(side, editorAnalyticsAPI);
636
619
  };
637
- export var setLayoutColumnValign = function setLayoutColumnValign(valign) {
638
- return function (_ref6) {
639
- var tr = _ref6.tr;
620
+ export var setLayoutColumnValign = function setLayoutColumnValign(valign, editorAnalyticsAPI) {
621
+ return function (_ref5) {
622
+ var tr = _ref5.tr;
640
623
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
641
624
  return null;
642
625
  }
643
- var layoutColumn = tr.doc.type.schema.nodes.layoutColumn;
644
- var selectedColumn = tr.selection instanceof NodeSelection && tr.selection.node.type === layoutColumn ? {
645
- node: tr.selection.node,
646
- pos: tr.selection.from
647
- } : undefined;
648
- if (!selectedColumn) {
626
+ var selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
627
+ if (!selectedLayoutColumns) {
649
628
  return null;
650
629
  }
651
- if (selectedColumn.node.attrs.valign === valign) {
630
+ var selectedColumnIndices = selectedLayoutColumns.selectedColumnIndices,
631
+ selectedColumns = selectedLayoutColumns.selectedColumns;
632
+ var columnsToUpdate = selectedColumns.filter(function (_ref6) {
633
+ var node = _ref6.node;
634
+ return node.attrs.valign !== valign;
635
+ });
636
+ if (columnsToUpdate.length === 0) {
652
637
  return null;
653
638
  }
654
- tr.setNodeMarkup(selectedColumn.pos, selectedColumn.node.type, _objectSpread(_objectSpread({}, selectedColumn.node.attrs), {}, {
655
- valign: valign
656
- }));
639
+ var startIndex = selectedColumnIndices[0];
640
+ var endIndex = selectedColumnIndices[selectedColumnIndices.length - 1];
641
+ var selectedColumnCount = selectedColumns.length;
642
+ var updatedColumnCount = columnsToUpdate.length;
643
+ columnsToUpdate.forEach(function (_ref7) {
644
+ var node = _ref7.node,
645
+ pos = _ref7.pos;
646
+ tr.setNodeMarkup(pos, node.type, _objectSpread(_objectSpread({}, node.attrs), {}, {
647
+ valign: valign
648
+ }));
649
+ });
650
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent({
651
+ action: ACTION.UPDATED,
652
+ actionSubject: ACTION_SUBJECT.DOCUMENT,
653
+ actionSubjectId: ACTION_SUBJECT_ID.LAYOUT_COLUMN,
654
+ attributes: {
655
+ endIndex: endIndex,
656
+ inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
657
+ selectedCount: selectedColumnCount,
658
+ startIndex: startIndex,
659
+ updatedCount: updatedColumnCount,
660
+ valign: valign
661
+ },
662
+ eventType: EVENT_TYPE.TRACK
663
+ })(tr);
657
664
  tr.setMeta('scrollIntoView', false);
658
665
  return tr;
659
666
  };
660
667
  };
661
- export var toggleLayoutColumnMenu = function toggleLayoutColumnMenu(_ref7) {
662
- var isOpen = _ref7.isOpen;
663
- return function (_ref8) {
664
- var tr = _ref8.tr;
668
+ export var toggleLayoutColumnMenu = function toggleLayoutColumnMenu(_ref8) {
669
+ var anchorPos = _ref8.anchorPos,
670
+ isOpen = _ref8.isOpen;
671
+ return function (_ref9) {
672
+ var tr = _ref9.tr;
665
673
  tr.setMeta('toggleLayoutColumnMenu', {
674
+ anchorPos: anchorPos,
666
675
  isOpen: isOpen
667
676
  });
668
677
  tr.setMeta('scrollIntoView', false);
@@ -670,18 +679,23 @@ export var toggleLayoutColumnMenu = function toggleLayoutColumnMenu(_ref7) {
670
679
  };
671
680
  };
672
681
  export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI) {
673
- return function (_ref9) {
674
- var tr = _ref9.tr;
682
+ return function (_ref0) {
683
+ var tr = _ref0.tr;
675
684
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
676
685
  return null;
677
686
  }
678
- var selectedColumn = getSelectedLayoutColumnInSection(tr);
679
- if (!selectedColumn) {
687
+ var selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
688
+ if (!selectedLayoutColumns) {
680
689
  return null;
681
690
  }
682
- var layoutSectionNode = selectedColumn.layoutSectionNode,
683
- layoutSectionPos = selectedColumn.layoutSectionPos,
684
- selectedColumnIndex = selectedColumn.selectedColumnIndex;
691
+ var layoutSectionNode = selectedLayoutColumns.layoutSectionNode,
692
+ layoutSectionPos = selectedLayoutColumns.layoutSectionPos,
693
+ selectedColumnIndices = selectedLayoutColumns.selectedColumnIndices,
694
+ selectedColumns = selectedLayoutColumns.selectedColumns;
695
+ var startIndex = selectedColumnIndices[0];
696
+ var endIndex = selectedColumnIndices[selectedColumnIndices.length - 1];
697
+ var selectedColumnCount = selectedColumns.length;
698
+ var selectedColumnIndexSet = new Set(selectedColumnIndices);
685
699
  var emitDeleteColumnAnalytics = function emitDeleteColumnAnalytics(columnCount) {
686
700
  editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent({
687
701
  action: ACTION.DELETED,
@@ -689,25 +703,27 @@ export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI)
689
703
  actionSubjectId: ACTION_SUBJECT_ID.LAYOUT_COLUMN,
690
704
  attributes: {
691
705
  columnCount: columnCount,
706
+ endIndex: endIndex,
692
707
  inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
693
- selectedIndex: selectedColumnIndex
708
+ selectedCount: selectedColumnCount,
709
+ startIndex: startIndex
694
710
  },
695
711
  eventType: EVENT_TYPE.TRACK
696
712
  })(tr);
697
713
  };
698
714
 
699
- // If only one column remains, remove the entire layoutSection
700
- if (layoutSectionNode.childCount === 1) {
715
+ // If all columns are selected, remove the entire layoutSection
716
+ if (selectedColumnCount === layoutSectionNode.childCount) {
701
717
  tr.delete(layoutSectionPos, layoutSectionPos + layoutSectionNode.nodeSize);
702
718
  emitDeleteColumnAnalytics(0);
703
719
  tr.setMeta('scrollIntoView', false);
704
720
  return tr;
705
721
  }
706
722
 
707
- // Build new column list without the selected column
723
+ // Build new column list without the selected columns
708
724
  var remainingColumns = [];
709
725
  layoutSectionNode.forEach(function (column, _offset, index) {
710
- if (index !== selectedColumnIndex) {
726
+ if (!selectedColumnIndexSet.has(index)) {
711
727
  remainingColumns.push(column);
712
728
  }
713
729
  });
@@ -716,7 +732,14 @@ export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI)
716
732
  var existingWidths = mapChildren(layoutSectionNode, function (column) {
717
733
  return column.attrs.width;
718
734
  });
719
- var redistributed = redistributeAfterDeletion(existingWidths, selectedColumnIndex, MIN_LAYOUT_COLUMN_WIDTH_PERCENT);
735
+ var redistributed = selectedColumnIndices.slice()
736
+ // Delete highest indices first so lower original indices still point at the same columns
737
+ // as each redistribution step shrinks the widths array.
738
+ .sort(function (a, b) {
739
+ return b - a;
740
+ }).reduce(function (widths, selectedIndex) {
741
+ return redistributeAfterDeletion(widths, selectedIndex, MIN_LAYOUT_COLUMN_WIDTH_PERCENT);
742
+ }, existingWidths);
720
743
  var updatedLayoutSectionNode = layoutSectionNode.copy(Fragment.fromArray(remainingColumns));
721
744
  tr.replaceWith(layoutSectionPos + 1, layoutSectionPos + layoutSectionNode.nodeSize - 1, columnWidth(updatedLayoutSectionNode, tr.doc.type.schema, redistributed));
722
745
  emitDeleteColumnAnalytics(redistributed.length);
@@ -66,7 +66,8 @@ var getInitialPluginState = function getInitialPluginState(options, state) {
66
66
  selectedLayout: selectedLayout,
67
67
  allowSingleColumnLayout: allowSingleColumnLayout,
68
68
  isResizing: false,
69
- isLayoutColumnMenuOpen: false
69
+ isLayoutColumnMenuOpen: false,
70
+ layoutColumnMenuAnchorPos: undefined
70
71
  };
71
72
  };
72
73
 
@@ -118,7 +119,8 @@ export default (function (options) {
118
119
  var _columnMenuMeta$isOpe, _tr$getMeta, _pluginKey$getState;
119
120
  var columnMenuMeta = tr.getMeta('toggleLayoutColumnMenu');
120
121
  var nextPluginState = columnMenuMeta ? _objectSpread(_objectSpread({}, pluginState), {}, {
121
- isLayoutColumnMenuOpen: (_columnMenuMeta$isOpe = columnMenuMeta.isOpen) !== null && _columnMenuMeta$isOpe !== void 0 ? _columnMenuMeta$isOpe : !pluginState.isLayoutColumnMenuOpen
122
+ isLayoutColumnMenuOpen: (_columnMenuMeta$isOpe = columnMenuMeta.isOpen) !== null && _columnMenuMeta$isOpe !== void 0 ? _columnMenuMeta$isOpe : !pluginState.isLayoutColumnMenuOpen,
123
+ layoutColumnMenuAnchorPos: columnMenuMeta.isOpen === false ? undefined : columnMenuMeta.anchorPos
122
124
  }) : pluginState;
123
125
  var isResizing = editorExperiment('single_column_layouts', true) ? (_tr$getMeta = tr.getMeta('is-resizer-resizing')) !== null && _tr$getMeta !== void 0 ? _tr$getMeta : (_pluginKey$getState = pluginKey.getState(oldState)) === null || _pluginKey$getState === void 0 ? void 0 : _pluginKey$getState.isResizing : false;
124
126
  if (tr.docChanged || tr.selectionSet) {