@atlaskit/editor-plugin-layout 10.8.0 → 10.9.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 (48) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/cjs/layoutPlugin.js +2 -5
  3. package/dist/cjs/pm-plugins/actions.js +91 -82
  4. package/dist/cjs/pm-plugins/utils/{redistribute-proportionally.js → layout-column-distribution.js} +39 -3
  5. package/dist/cjs/pm-plugins/utils/layout-column-selection.js +56 -98
  6. package/dist/cjs/ui/LayoutColumnMenu/DeleteColumnDropdownItem.js +1 -1
  7. package/dist/cjs/ui/LayoutColumnMenu/DistributeColumnsDropdownItem.js +14 -19
  8. package/dist/cjs/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +2 -2
  9. package/dist/cjs/ui/LayoutColumnMenu/VerticalAlignDropdownItem.js +1 -1
  10. package/dist/cjs/ui/LayoutColumnMenu/VerticalAlignNestedMenu.js +1 -1
  11. package/dist/cjs/ui/LayoutColumnMenu/index.js +1 -1
  12. package/dist/cjs/ui/LayoutColumnMenu/useSelectedLayoutColumns.js +4 -3
  13. package/dist/cjs/ui/toolbar.js +70 -11
  14. package/dist/es2019/layoutPlugin.js +3 -6
  15. package/dist/es2019/pm-plugins/actions.js +74 -63
  16. package/dist/es2019/pm-plugins/utils/{redistribute-proportionally.js → layout-column-distribution.js} +34 -1
  17. package/dist/es2019/pm-plugins/utils/layout-column-selection.js +54 -94
  18. package/dist/es2019/ui/LayoutColumnMenu/DeleteColumnDropdownItem.js +1 -1
  19. package/dist/es2019/ui/LayoutColumnMenu/DistributeColumnsDropdownItem.js +15 -15
  20. package/dist/es2019/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +2 -2
  21. package/dist/es2019/ui/LayoutColumnMenu/VerticalAlignDropdownItem.js +1 -1
  22. package/dist/es2019/ui/LayoutColumnMenu/VerticalAlignNestedMenu.js +1 -1
  23. package/dist/es2019/ui/LayoutColumnMenu/index.js +1 -1
  24. package/dist/es2019/ui/LayoutColumnMenu/useSelectedLayoutColumns.js +6 -4
  25. package/dist/es2019/ui/toolbar.js +68 -11
  26. package/dist/esm/layoutPlugin.js +3 -6
  27. package/dist/esm/pm-plugins/actions.js +89 -80
  28. package/dist/esm/pm-plugins/utils/{redistribute-proportionally.js → layout-column-distribution.js} +37 -3
  29. package/dist/esm/pm-plugins/utils/layout-column-selection.js +55 -97
  30. package/dist/esm/ui/LayoutColumnMenu/DeleteColumnDropdownItem.js +1 -1
  31. package/dist/esm/ui/LayoutColumnMenu/DistributeColumnsDropdownItem.js +14 -19
  32. package/dist/esm/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +2 -2
  33. package/dist/esm/ui/LayoutColumnMenu/VerticalAlignDropdownItem.js +1 -1
  34. package/dist/esm/ui/LayoutColumnMenu/VerticalAlignNestedMenu.js +1 -1
  35. package/dist/esm/ui/LayoutColumnMenu/index.js +1 -1
  36. package/dist/esm/ui/LayoutColumnMenu/useSelectedLayoutColumns.js +5 -4
  37. package/dist/esm/ui/toolbar.js +71 -12
  38. package/dist/types/layoutPluginType.d.ts +2 -2
  39. package/dist/types/pm-plugins/actions.d.ts +12 -2
  40. package/dist/types/pm-plugins/utils/layout-column-distribution.d.ts +16 -0
  41. package/dist/types/pm-plugins/utils/layout-column-selection.d.ts +8 -7
  42. package/dist/types-ts4.5/layoutPluginType.d.ts +2 -2
  43. package/dist/types-ts4.5/pm-plugins/actions.d.ts +12 -2
  44. package/dist/types-ts4.5/pm-plugins/utils/layout-column-distribution.d.ts +16 -0
  45. package/dist/types-ts4.5/pm-plugins/utils/layout-column-selection.d.ts +8 -7
  46. package/package.json +5 -5
  47. package/dist/types/pm-plugins/utils/redistribute-proportionally.d.ts +0 -2
  48. package/dist/types-ts4.5/pm-plugins/utils/redistribute-proportionally.d.ts +0 -2
@@ -7,20 +7,26 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.DistributeColumnsDropdownItem = void 0;
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
  var _reactIntl = require("react-intl");
10
+ var _analytics = require("@atlaskit/editor-common/analytics");
10
11
  var _messages = require("@atlaskit/editor-common/messages");
11
12
  var _editorToolbar = require("@atlaskit/editor-toolbar");
13
+ var _layoutColumnDistribution = require("../../pm-plugins/utils/layout-column-distribution");
12
14
  var _useSelectedLayoutColumns = require("./useSelectedLayoutColumns");
13
15
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
14
16
  var DistributeColumnsDropdownItem = exports.DistributeColumnsDropdownItem = function DistributeColumnsDropdownItem(_ref) {
15
17
  var api = _ref.api;
16
18
  var _useIntl = (0, _reactIntl.useIntl)(),
17
19
  formatMessage = _useIntl.formatMessage;
18
- var selectedLayoutColumns = (0, _useSelectedLayoutColumns.useSelectedLayoutColumns)(api);
20
+ var selectedLayoutColumnsResult = (0, _useSelectedLayoutColumns.useSelectedLayoutColumns)(api);
21
+ var _ref2 = selectedLayoutColumnsResult !== null && selectedLayoutColumnsResult !== void 0 ? selectedLayoutColumnsResult : {},
22
+ selectedLayoutColumns = _ref2.selectedLayoutColumns;
19
23
  var handleClick = (0, _react.useCallback)(function () {
20
24
  var _api$core;
21
25
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (props) {
22
26
  var _api$layout, _api$layout2;
23
- var tr = api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.distributeLayoutColumns(props);
27
+ var tr = api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.distributeLayoutColumns({
28
+ inputMethod: _analytics.INPUT_METHOD.LAYOUT_COLUMN_MENU
29
+ })(props);
24
30
  if (!tr) {
25
31
  return null;
26
32
  }
@@ -32,31 +38,20 @@ var DistributeColumnsDropdownItem = exports.DistributeColumnsDropdownItem = func
32
38
  return closedTr !== null && closedTr !== void 0 ? closedTr : tr;
33
39
  });
34
40
  }, [api]);
35
-
36
- // Hide when selected columns are already evenly distributed — no-op action.
37
- // Must be before any early return to satisfy rules-of-hooks.
38
41
  var isAlreadyUniform = (0, _react.useMemo)(function () {
39
- if (!selectedLayoutColumns || selectedLayoutColumns.selectedColumns.length < 2) {
42
+ if (!selectedLayoutColumns || selectedLayoutColumns.length < 2) {
40
43
  return false;
41
44
  }
42
- var selectedWidths = selectedLayoutColumns.selectedColumns.map(function (col) {
45
+ var selectedWidths = selectedLayoutColumns.map(function (col) {
43
46
  return col.node.attrs.width;
44
47
  });
45
- var selectedTotal = selectedWidths.reduce(function (sum, w) {
46
- return sum + w;
47
- }, 0);
48
- var equalWidth = Number((selectedTotal / selectedWidths.length).toFixed(2));
49
- return selectedWidths.every(function (w) {
50
- return w === equalWidth;
51
- });
48
+ return (0, _layoutColumnDistribution.isDistributedUniformly)(selectedWidths);
52
49
  }, [selectedLayoutColumns]);
53
- if (selectedLayoutColumns === undefined || selectedLayoutColumns.selectedColumns.length < 2) {
54
- return null;
55
- }
56
- if (isAlreadyUniform) {
50
+ if (selectedLayoutColumns === undefined || selectedLayoutColumns.length < 2) {
57
51
  return null;
58
52
  }
59
53
  return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownItem, {
60
- onClick: handleClick
54
+ onClick: handleClick,
55
+ isDisabled: isAlreadyUniform
61
56
  }, formatMessage(_messages.layoutMessages.distributeColumns));
62
57
  };
@@ -10,7 +10,6 @@ var _reactIntl = require("react-intl");
10
10
  var _messages = require("@atlaskit/editor-common/messages");
11
11
  var _editorToolbar = require("@atlaskit/editor-toolbar");
12
12
  var _actions = require("../../pm-plugins/actions");
13
- var _layoutColumnSelection = require("../../pm-plugins/utils/layout-column-selection");
14
13
  var _useSelectedLayoutColumns = require("./useSelectedLayoutColumns");
15
14
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
16
15
  var INSERT_COLUMN_OPTIONS = {
@@ -26,6 +25,7 @@ var INSERT_COLUMN_OPTIONS = {
26
25
  }
27
26
  };
28
27
  var InsertColumnDropdownItem = exports.InsertColumnDropdownItem = function InsertColumnDropdownItem(_ref) {
28
+ var _selectedLayoutColumn, _selectedLayoutColumn2;
29
29
  var api = _ref.api,
30
30
  side = _ref.side;
31
31
  var _useIntl = (0, _reactIntl.useIntl)(),
@@ -34,7 +34,7 @@ var InsertColumnDropdownItem = exports.InsertColumnDropdownItem = function Inser
34
34
  Icon = _INSERT_COLUMN_OPTION.Icon,
35
35
  label = _INSERT_COLUMN_OPTION.label;
36
36
  var selectedLayoutColumns = (0, _useSelectedLayoutColumns.useSelectedLayoutColumns)(api);
37
- var columnCount = (0, _layoutColumnSelection.getLayoutSectionColumnCount)(selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.layoutSectionNode);
37
+ var columnCount = (_selectedLayoutColumn = selectedLayoutColumns === null || selectedLayoutColumns === void 0 || (_selectedLayoutColumn2 = selectedLayoutColumns.layoutSectionNode) === null || _selectedLayoutColumn2 === void 0 ? void 0 : _selectedLayoutColumn2.childCount) !== null && _selectedLayoutColumn !== void 0 ? _selectedLayoutColumn : 0;
38
38
  var maxColumnCount = (0, _actions.getEffectiveMaxLayoutColumns)();
39
39
  var canInsertColumn = selectedLayoutColumns !== undefined && columnCount < maxColumnCount;
40
40
  var onClick = (0, _react.useCallback)(function () {
@@ -20,7 +20,7 @@ var VerticalAlignDropdownItem = exports.VerticalAlignDropdownItem = function Ver
20
20
  var _useIntl = (0, _reactIntl.useIntl)(),
21
21
  formatMessage = _useIntl.formatMessage;
22
22
  var selectedLayoutColumns = (0, _useSelectedLayoutColumns.useSelectedLayoutColumns)(api);
23
- var isSelected = (_selectedLayoutColumn = selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.selectedColumns.every(function (_ref2) {
23
+ var isSelected = (_selectedLayoutColumn = selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.selectedLayoutColumns.every(function (_ref2) {
24
24
  var node = _ref2.node;
25
25
  return (0, _layoutColumnSelection.getLayoutColumnValign)(node) === value;
26
26
  })) !== null && _selectedLayoutColumn !== void 0 ? _selectedLayoutColumn : false;
@@ -20,7 +20,7 @@ var VerticalAlignNestedMenu = exports.VerticalAlignNestedMenu = function Vertica
20
20
  formatMessage = _useIntl.formatMessage;
21
21
  var selectedLayoutColumns = (0, _useSelectedLayoutColumns.useSelectedLayoutColumns)(api);
22
22
  var currentValign = (0, _react.useMemo)(function () {
23
- var selectedColumns = selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.selectedColumns;
23
+ var selectedColumns = selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.selectedLayoutColumns;
24
24
  var firstColumn = selectedColumns === null || selectedColumns === void 0 ? void 0 : selectedColumns[0];
25
25
  var firstValign = (0, _layoutColumnSelection.getLayoutColumnValign)(firstColumn === null || firstColumn === void 0 ? void 0 : firstColumn.node);
26
26
  if (!firstValign || !(selectedColumns !== null && selectedColumns !== void 0 && selectedColumns.every(function (_ref2) {
@@ -26,7 +26,7 @@ var TOOLBAR_MENU_SELECTOR = '[data-toolbar-component="menu"]';
26
26
  */
27
27
  var getLayoutColumnMenuTarget = function getLayoutColumnMenuTarget(editorView, selection, anchorPosFromHandle) {
28
28
  var _columnDomRef$parentE;
29
- var anchorPos = (0, _layoutColumnSelection.getLayoutColumnMenuAnchorPos)(selection, anchorPosFromHandle);
29
+ var anchorPos = selection && (0, _layoutColumnSelection.getLayoutColumnMenuAnchorPos)(selection, anchorPosFromHandle);
30
30
  if (anchorPos === undefined) {
31
31
  return null;
32
32
  }
@@ -7,8 +7,9 @@ exports.useSelectedLayoutColumns = void 0;
7
7
  var _hooks = require("@atlaskit/editor-common/hooks");
8
8
  var _layoutColumnSelection = require("../../pm-plugins/utils/layout-column-selection");
9
9
  var useSelectedLayoutColumns = exports.useSelectedLayoutColumns = function useSelectedLayoutColumns(api) {
10
- return (0, _hooks.useSharedPluginStateWithSelector)(api, ['selection'], function (states) {
11
- var _states$selectionStat;
12
- return (0, _layoutColumnSelection.getSelectedLayoutColumns)((_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection);
10
+ return (0, _hooks.useSharedPluginStateWithSelector)(api, ['selection'], function (_ref) {
11
+ var selectionState = _ref.selectionState;
12
+ var selectedLayoutColumns = (selectionState === null || selectionState === void 0 ? void 0 : selectionState.selection) && (0, _layoutColumnSelection.getSelectedLayoutColumnsFromSelection)(selectionState.selection);
13
+ return selectedLayoutColumns !== null && selectedLayoutColumns !== void 0 && selectedLayoutColumns.selectedLayoutColumns.length ? selectedLayoutColumns : undefined;
13
14
  });
14
15
  };
@@ -22,8 +22,11 @@ var _layoutThreeColumnsSidebars = _interopRequireDefault(require("@atlaskit/icon
22
22
  var _layoutTwoColumns = _interopRequireDefault(require("@atlaskit/icon/core/layout-two-columns"));
23
23
  var _layoutTwoColumnsSidebarLeft = _interopRequireDefault(require("@atlaskit/icon/core/layout-two-columns-sidebar-left"));
24
24
  var _layoutTwoColumnsSidebarRight = _interopRequireDefault(require("@atlaskit/icon/core/layout-two-columns-sidebar-right"));
25
+ var _tableColumnsDistribute = _interopRequireDefault(require("@atlaskit/icon/core/table-columns-distribute"));
26
+ var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
25
27
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
26
28
  var _actions = require("../pm-plugins/actions");
29
+ var _layoutColumnDistribution = require("../pm-plugins/utils/layout-column-distribution");
27
30
  var _LayoutColumnsIcon = require("./icons/LayoutColumnsIcon");
28
31
  var _LayoutThreeWithLeftSidebars = require("./icons/LayoutThreeWithLeftSidebars");
29
32
  var _LayoutThreeWithRightSidebars = require("./icons/LayoutThreeWithRightSidebars");
@@ -65,7 +68,7 @@ var SIDEBAR_LAYOUT_TYPES = [{
65
68
  }];
66
69
 
67
70
  // These are used for advanced layout options
68
- var LAYOUT_WITH_TWO_COL_DISTRIBUTION = [{
71
+ var LAYOUT_WITH_TWO_COL_DISTRIBUTION_OLD = [{
69
72
  id: 'editor.layout.twoEquals',
70
73
  type: 'two_equal',
71
74
  title: _messages.layoutMessages.twoColumns,
@@ -81,7 +84,7 @@ var LAYOUT_WITH_TWO_COL_DISTRIBUTION = [{
81
84
  title: _messages.layoutMessages.leftSidebar,
82
85
  icon: _layoutTwoColumnsSidebarLeft.default
83
86
  }];
84
- var LAYOUT_WITH_THREE_COL_DISTRIBUTION = [{
87
+ var LAYOUT_WITH_THREE_COL_DISTRIBUTION_OLD = [{
85
88
  id: 'editor.layout.threeEquals',
86
89
  type: 'three_equal',
87
90
  title: _messages.layoutMessages.threeColumns,
@@ -104,6 +107,35 @@ var LAYOUT_WITH_THREE_COL_DISTRIBUTION = [{
104
107
  icon: _LayoutThreeWithLeftSidebars.LayoutThreeWithLeftSidebarsIcon,
105
108
  iconFallback: _LayoutThreeWithLeftSidebars.LayoutThreeWithLeftSidebarsIcon
106
109
  }];
110
+ var LAYOUT_WITH_TWO_COL_DISTRIBUTION = [{
111
+ id: 'editor.layout.twoRightSidebar',
112
+ type: 'two_right_sidebar',
113
+ title: _messages.layoutMessages.rightSidebar,
114
+ icon: _layoutTwoColumnsSidebarRight.default
115
+ }, {
116
+ id: 'editor.layout.twoLeftSidebar',
117
+ type: 'two_left_sidebar',
118
+ title: _messages.layoutMessages.leftSidebar,
119
+ icon: _layoutTwoColumnsSidebarLeft.default
120
+ }];
121
+ var LAYOUT_WITH_THREE_COL_DISTRIBUTION = [{
122
+ id: 'editor.layout.threeWithSidebars',
123
+ type: 'three_with_sidebars',
124
+ title: _messages.layoutMessages.threeColumnsWithSidebars,
125
+ icon: _layoutThreeColumnsSidebars.default
126
+ }, {
127
+ id: 'editor.layout.threeRightSidebars',
128
+ type: 'three_right_sidebars',
129
+ title: _messages.layoutMessages.threeColumnsWithRightSidebars,
130
+ icon: _LayoutThreeWithRightSidebars.LayoutThreeWithRightSidebarsIcon,
131
+ iconFallback: _LayoutThreeWithRightSidebars.LayoutThreeWithRightSidebarsIcon
132
+ }, {
133
+ id: 'editor.layout.threeLeftSidebars',
134
+ type: 'three_left_sidebars',
135
+ title: _messages.layoutMessages.threeColumnsWithLeftSidebars,
136
+ icon: _LayoutThreeWithLeftSidebars.LayoutThreeWithLeftSidebarsIcon,
137
+ iconFallback: _LayoutThreeWithLeftSidebars.LayoutThreeWithLeftSidebarsIcon
138
+ }];
107
139
  var buildLayoutButton = function buildLayoutButton(intl, item, currentLayout, editorAnalyticsAPI) {
108
140
  return {
109
141
  id: item.id,
@@ -118,7 +150,9 @@ var buildLayoutButton = function buildLayoutButton(intl, item, currentLayout, ed
118
150
  };
119
151
  };
120
152
  var layoutToolbarTitle = exports.layoutToolbarTitle = 'Layout floating controls';
121
- var iconPlaceholder = _layoutTwoColumns.default; // TODO: ED-25466 - Replace with proper icon
153
+ var iconPlaceholder = /*#__PURE__*/_react.default.createElement(_layoutTwoColumns.default, {
154
+ label: ""
155
+ }); // TODO: ED-25466 - Replace with proper icon
122
156
 
123
157
  var getLayoutColumnsIcons = function getLayoutColumnsIcons(colCount) {
124
158
  if (!(0, _experiments.editorExperiment)('single_column_layouts', true) && !(0, _experiments.editorExperiment)('platform_editor_controls', 'variant1')) {
@@ -145,11 +179,15 @@ var getLayoutColumnsIcons = function getLayoutColumnsIcons(colCount) {
145
179
  return undefined;
146
180
  }
147
181
  };
182
+ var getLayoutColumnWidths = function getLayoutColumnWidths(node) {
183
+ return node.children.map(function (child) {
184
+ return child.attrs.width;
185
+ });
186
+ };
148
187
  var getAdvancedLayoutItems = function getAdvancedLayoutItems(_ref) {
149
188
  var addSidebarLayouts = _ref.addSidebarLayouts,
150
189
  intl = _ref.intl,
151
190
  editorAnalyticsAPI = _ref.editorAnalyticsAPI,
152
- state = _ref.state,
153
191
  node = _ref.node,
154
192
  nodeType = _ref.nodeType,
155
193
  separator = _ref.separator,
@@ -157,7 +195,8 @@ var getAdvancedLayoutItems = function getAdvancedLayoutItems(_ref) {
157
195
  currentLayout = _ref.currentLayout,
158
196
  allowAdvancedSingleColumnLayout = _ref.allowAdvancedSingleColumnLayout;
159
197
  var numberOfColumns = node.content.childCount || 2;
160
- var distributionOptions = numberOfColumns === 2 ? LAYOUT_WITH_TWO_COL_DISTRIBUTION : numberOfColumns === 3 ? LAYOUT_WITH_THREE_COL_DISTRIBUTION : [];
198
+ var isLayoutColumnMenuEnabled = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true);
199
+ var distributionOptions = isLayoutColumnMenuEnabled ? numberOfColumns === 2 ? LAYOUT_WITH_TWO_COL_DISTRIBUTION : numberOfColumns === 3 ? LAYOUT_WITH_THREE_COL_DISTRIBUTION : [] : numberOfColumns === 2 ? LAYOUT_WITH_TWO_COL_DISTRIBUTION_OLD : numberOfColumns === 3 ? LAYOUT_WITH_THREE_COL_DISTRIBUTION_OLD : [];
161
200
  var columnOptions = [{
162
201
  title: intl.formatMessage(_messages.layoutMessages.columnOption, {
163
202
  count: 2
@@ -191,7 +230,7 @@ var getAdvancedLayoutItems = function getAdvancedLayoutItems(_ref) {
191
230
  onClick: (0, _actions.setPresetLayout)(editorAnalyticsAPI)('five_equal'),
192
231
  selected: numberOfColumns === 5
193
232
  }];
194
- var singleColumnOption = allowAdvancedSingleColumnLayout ? {
233
+ var dropdownOptions = [].concat((0, _toConsumableArray2.default)(allowAdvancedSingleColumnLayout ? [{
195
234
  title: intl.formatMessage(_messages.layoutMessages.columnOption, {
196
235
  count: 1
197
236
  }),
@@ -199,19 +238,40 @@ var getAdvancedLayoutItems = function getAdvancedLayoutItems(_ref) {
199
238
  icon: getLayoutColumnsIcons(1) || iconPlaceholder,
200
239
  onClick: (0, _actions.setPresetLayout)(editorAnalyticsAPI)('single'),
201
240
  selected: numberOfColumns === 1
202
- } : [];
241
+ }] : []), columnOptions);
242
+ var distributeColumnsButton = isLayoutColumnMenuEnabled && numberOfColumns > 1 ? {
243
+ disabled: (0, _layoutColumnDistribution.isDistributedUniformly)(getLayoutColumnWidths(node)),
244
+ icon: _tableColumnsDistribute.default,
245
+ onClick: function onClick(editorState, dispatch) {
246
+ var tr = (0, _actions.distributeLayoutColumns)({
247
+ editorAnalyticsAPI: editorAnalyticsAPI,
248
+ inputMethod: _analytics.INPUT_METHOD.FLOATING_TB,
249
+ target: 'allColumns'
250
+ })({
251
+ tr: editorState.tr
252
+ });
253
+ if (!tr) {
254
+ return false;
255
+ }
256
+ dispatch === null || dispatch === void 0 || dispatch(tr);
257
+ return true;
258
+ },
259
+ testId: 'layout-distribute-columns',
260
+ title: intl.formatMessage(_messages.layoutMessages.distributeColumns),
261
+ type: 'button'
262
+ } : undefined;
203
263
  return [{
204
264
  type: 'dropdown',
205
265
  title: intl.formatMessage(_messages.layoutMessages.columnOption, {
206
266
  count: numberOfColumns
207
267
  }),
208
268
  //`${numberOfColumns}-columns`,
209
- options: [singleColumnOption, columnOptions].flat(),
269
+ options: dropdownOptions,
210
270
  showSelected: true,
211
271
  testId: 'column-options-button'
212
- }].concat((0, _toConsumableArray2.default)(distributionOptions.length > 0 ? [separator] : []), (0, _toConsumableArray2.default)(addSidebarLayouts ? distributionOptions.map(function (i) {
272
+ }].concat((0, _toConsumableArray2.default)(distributionOptions.length > 0 || distributeColumnsButton ? [separator] : []), (0, _toConsumableArray2.default)(addSidebarLayouts ? distributionOptions.map(function (i) {
213
273
  return buildLayoutButton(intl, i, currentLayout, editorAnalyticsAPI);
214
- }) : []));
274
+ }) : []), (0, _toConsumableArray2.default)(distributeColumnsButton ? [distributeColumnsButton] : []));
215
275
  };
216
276
  var fullHeightSeparator = {
217
277
  type: 'separator',
@@ -300,7 +360,6 @@ var buildToolbar = exports.buildToolbar = function buildToolbar(state, intl, pos
300
360
  addSidebarLayouts: addSidebarLayouts,
301
361
  intl: intl,
302
362
  editorAnalyticsAPI: editorAnalyticsAPI,
303
- state: state,
304
363
  nodeType: nodeType,
305
364
  node: node,
306
365
  separator: separator,
@@ -11,7 +11,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
11
11
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
12
12
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
13
13
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
14
- import { createDefaultLayoutSection, createMultiColumnLayoutSection, deleteLayoutColumn, distributeLayoutColumns, insertLayoutColumn, insertLayoutColumnsWithAnalytics, setLayoutColumnValign, toggleLayoutColumnMenu } from './pm-plugins/actions';
14
+ import { createDefaultLayoutSection, createDistributeLayoutColumnsCommand, createMultiColumnLayoutSection, deleteLayoutColumn, insertLayoutColumn, insertLayoutColumnsWithAnalytics, setLayoutColumnValign, toggleLayoutColumnMenu } from './pm-plugins/actions';
15
15
  import { default as createLayoutPlugin } from './pm-plugins/main';
16
16
  import { pluginKey } from './pm-plugins/plugin-key';
17
17
  import { default as createLayoutResizingPlugin } from './pm-plugins/resizing';
@@ -52,7 +52,7 @@ export const layoutPlugin = ({
52
52
  config: options = {},
53
53
  api
54
54
  }) => {
55
- var _api$analytics;
55
+ var _api$analytics, _api$analytics5;
56
56
  const allowAdvancedSingleColumnLayout = editorExperiment('advanced_layouts', true) && editorExperiment('single_column_layouts', true, {
57
57
  exposure: true
58
58
  });
@@ -353,10 +353,7 @@ export const layoutPlugin = ({
353
353
  var _api$analytics4;
354
354
  return deleteLayoutColumn(api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions)(props);
355
355
  },
356
- distributeLayoutColumns: props => {
357
- var _api$analytics5;
358
- return distributeLayoutColumns(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions)(props);
359
- },
356
+ distributeLayoutColumns: createDistributeLayoutColumnsCommand(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions),
360
357
  insertLayoutColumn: side => {
361
358
  var _api$analytics6;
362
359
  return insertLayoutColumn(side, api === null || api === void 0 ? void 0 : (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions);
@@ -9,8 +9,8 @@ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equ
9
9
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
10
10
  import { EVEN_DISTRIBUTED_COL_WIDTHS, MAX_LAYOUT_COLUMNS, MAX_STANDARD_LAYOUT_COLUMNS, MIN_LAYOUT_COLUMN_WIDTH_PERCENT } from './consts';
11
11
  import { pluginKey } from './plugin-key';
12
- import { getSelectedLayoutColumns } from './utils/layout-column-selection';
13
- import { redistributeAfterDeletion, redistributeProportionally } from './utils/redistribute-proportionally';
12
+ import { calculateDistribution, isDistributedUniformly, redistributeAfterDeletion, redistributeProportionally } from './utils/layout-column-distribution';
13
+ import { getAllLayoutColumnsFromSelection, getSelectedLayoutColumnsFromSelection } from './utils/layout-column-selection';
14
14
  export const ONE_COL_LAYOUTS = ['single'];
15
15
  export const TWO_COL_LAYOUTS = ['two_equal', 'two_left_sidebar', 'two_right_sidebar'];
16
16
  export const THREE_COL_LAYOUTS = ['three_equal', 'three_with_sidebars'];
@@ -540,20 +540,19 @@ const insertLayoutColumnAt = (side, editorAnalyticsAPI) => ({
540
540
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
541
541
  return null;
542
542
  }
543
- const selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
544
- if (!selectedLayoutColumns) {
543
+ const selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
544
+ if (!selectedLayoutColumnsResult || selectedLayoutColumnsResult.selectedLayoutColumns.length === 0) {
545
545
  return null;
546
546
  }
547
547
  const {
548
548
  layoutSectionNode,
549
549
  layoutSectionPos,
550
- selectedColumnIndices,
551
- selectedColumns
552
- } = selectedLayoutColumns;
553
- const startIndex = selectedColumnIndices[0];
554
- const endIndex = selectedColumnIndices[selectedColumnIndices.length - 1];
550
+ startIndex,
551
+ endIndex,
552
+ selectedLayoutColumns
553
+ } = selectedLayoutColumnsResult;
555
554
  const selectedColumnIndex = side === 'left' ? startIndex : endIndex;
556
- const selectedColumnCount = selectedColumns.length;
555
+ const selectedColumnCount = selectedLayoutColumns.length;
557
556
  if (layoutSectionNode.childCount >= getEffectiveMaxLayoutColumns()) {
558
557
  return null;
559
558
  }
@@ -608,23 +607,21 @@ export const setLayoutColumnValign = (valign, editorAnalyticsAPI) => ({
608
607
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
609
608
  return null;
610
609
  }
611
- const selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
612
- if (!selectedLayoutColumns) {
610
+ const selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
611
+ if (!selectedLayoutColumnsResult) {
613
612
  return null;
614
613
  }
615
614
  const {
616
- selectedColumnIndices,
617
- selectedColumns
618
- } = selectedLayoutColumns;
619
- const columnsToUpdate = selectedColumns.filter(({
615
+ startIndex,
616
+ endIndex,
617
+ selectedLayoutColumns
618
+ } = selectedLayoutColumnsResult;
619
+ const columnsToUpdate = selectedLayoutColumns.filter(({
620
620
  node
621
621
  }) => node.attrs.valign !== valign);
622
622
  if (columnsToUpdate.length === 0) {
623
623
  return null;
624
624
  }
625
- const startIndex = selectedColumnIndices[0];
626
- const endIndex = selectedColumnIndices[selectedColumnIndices.length - 1];
627
- const selectedColumnCount = selectedColumns.length;
628
625
  const updatedColumnCount = columnsToUpdate.length;
629
626
  columnsToUpdate.forEach(({
630
627
  node,
@@ -642,7 +639,7 @@ export const setLayoutColumnValign = (valign, editorAnalyticsAPI) => ({
642
639
  attributes: {
643
640
  endIndex,
644
641
  inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
645
- selectedCount: selectedColumnCount,
642
+ selectedCount: selectedLayoutColumns.length,
646
643
  startIndex,
647
644
  updatedCount: updatedColumnCount,
648
645
  valign
@@ -652,74 +649,91 @@ export const setLayoutColumnValign = (valign, editorAnalyticsAPI) => ({
652
649
  tr.setMeta('scrollIntoView', false);
653
650
  return tr;
654
651
  };
655
- export const distributeLayoutColumns = (editorAnalyticsAPI, inputMethod = INPUT_METHOD.LAYOUT_COLUMN_MENU) => ({
652
+ export const distributeLayoutColumns = ({
653
+ editorAnalyticsAPI,
654
+ inputMethod = INPUT_METHOD.LAYOUT_COLUMN_MENU,
655
+ target = 'selectedColumns'
656
+ } = {}) => ({
656
657
  tr
657
658
  }) => {
658
659
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
659
660
  return null;
660
661
  }
661
- const selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
662
- if (!selectedLayoutColumns || selectedLayoutColumns.selectedColumns.length < 2) {
662
+ const selectedLayoutColumnsResult = target === 'allColumns' ? getAllLayoutColumnsFromSelection(tr.selection) : getSelectedLayoutColumnsFromSelection(tr.selection);
663
+ if (!selectedLayoutColumnsResult || selectedLayoutColumnsResult.selectedLayoutColumns.length < 2) {
663
664
  return null;
664
665
  }
665
666
  const {
666
667
  layoutSectionNode,
667
- layoutSectionPos,
668
- selectedColumnIndices,
669
- selectedColumns
670
- } = selectedLayoutColumns;
671
- const endIndex = selectedColumnIndices[selectedColumnIndices.length - 1];
672
- const selectedColumnCount = selectedColumns.length;
673
- const totalColumnCount = layoutSectionNode.childCount;
674
-
675
- // Compute equal width for selected columns
668
+ startIndex,
669
+ endIndex,
670
+ selectedLayoutColumns
671
+ } = selectedLayoutColumnsResult;
676
672
  const existingWidths = mapChildren(layoutSectionNode, column => column.attrs.width);
677
- const selectedTotal = selectedColumnIndices.reduce((sum, idx) => sum + existingWidths[idx], 0);
678
- const equalWidth = selectedTotal / selectedColumnCount;
679
-
680
- // Early return if selected columns are already uniformly distributed — avoids spurious undo entry.
681
- if (selectedColumnIndices.every(idx => existingWidths[idx] === Number(equalWidth.toFixed(2)))) {
673
+ const selectedWidths = selectedLayoutColumns.map(({
674
+ node
675
+ }) => node.attrs.width);
676
+ const distribution = calculateDistribution(selectedWidths);
677
+ if (!distribution) {
678
+ return null;
679
+ }
680
+ const {
681
+ selectedTotal,
682
+ equalWidth
683
+ } = distribution;
684
+ if (isDistributedUniformly(selectedWidths, distribution)) {
682
685
  return null;
683
686
  }
684
687
 
685
688
  // Build new widths array: selected columns get equal share, unselected unchanged.
686
- // Assign truncated (2dp) equal widths to all selected cols except the last, which absorbs
689
+ // Assign rounded (2dp) equal widths to all selected cols except the last, which absorbs
687
690
  // the rounding remainder so the sum of selected widths equals selectedTotal exactly.
688
- const selectedIndexSet = new Set(selectedColumnIndices);
689
691
  let assignedToSelected = 0;
690
692
  let selectedAssignedCount = 0;
691
693
  const newWidths = existingWidths.map((w, idx) => {
692
- if (!selectedIndexSet.has(idx)) {
694
+ if (idx < startIndex || idx > endIndex) {
693
695
  return w;
694
696
  }
695
697
  selectedAssignedCount += 1;
696
- if (selectedAssignedCount < selectedColumnCount) {
697
- const truncated = Number(equalWidth.toFixed(2));
698
- assignedToSelected += truncated;
699
- return truncated;
698
+ if (selectedAssignedCount < selectedLayoutColumns.length) {
699
+ assignedToSelected += equalWidth;
700
+ return equalWidth;
700
701
  }
701
702
  // Last selected column: absorb the remainder to avoid drift
702
703
  return Number((selectedTotal - assignedToSelected).toFixed(2));
703
704
  });
704
705
 
705
- // Apply widths via replaceWith (mirroring forceColumnWidths approach)
706
- tr.replaceWith(layoutSectionPos + 1, layoutSectionPos + layoutSectionNode.nodeSize - 1, columnWidth(layoutSectionNode, tr.doc.type.schema, newWidths));
706
+ // Apply widths via setNodeMarkup per selected column — keeps nodes in place (preserves identity, marks, decorations)
707
+ selectedLayoutColumns.forEach(({
708
+ node,
709
+ pos
710
+ }, i) => {
711
+ const colIdx = startIndex + i;
712
+ tr.setNodeMarkup(pos, node.type, {
713
+ ...node.attrs,
714
+ width: newWidths[colIdx]
715
+ });
716
+ });
707
717
  editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent({
708
718
  action: ACTION.UPDATED,
709
719
  actionSubject: ACTION_SUBJECT.DOCUMENT,
710
720
  actionSubjectId: ACTION_SUBJECT_ID.LAYOUT_COLUMN,
711
721
  attributes: {
712
- columnCount: totalColumnCount,
722
+ columnCount: layoutSectionNode.childCount,
713
723
  endIndex,
714
724
  inputMethod,
715
- selectedCount: selectedColumnCount,
716
- startIndex: selectedColumnIndices[0]
725
+ selectedCount: selectedLayoutColumns.length,
726
+ startIndex
717
727
  },
718
728
  eventType: EVENT_TYPE.TRACK
719
729
  })(tr);
720
730
  tr.setMeta('scrollIntoView', false);
721
731
  return tr;
722
732
  };
733
+ export const createDistributeLayoutColumnsCommand = editorAnalyticsAPI => (options = {}) => distributeLayoutColumns({
734
+ ...options,
735
+ editorAnalyticsAPI
736
+ });
723
737
  export const toggleLayoutColumnMenu = ({
724
738
  anchorPos,
725
739
  isOpen
@@ -739,20 +753,17 @@ export const deleteLayoutColumn = editorAnalyticsAPI => ({
739
753
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
740
754
  return null;
741
755
  }
742
- const selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
743
- if (!selectedLayoutColumns) {
756
+ const selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
757
+ if (!selectedLayoutColumnsResult || selectedLayoutColumnsResult.selectedLayoutColumns.length === 0) {
744
758
  return null;
745
759
  }
746
760
  const {
747
761
  layoutSectionNode,
748
762
  layoutSectionPos,
749
- selectedColumnIndices,
750
- selectedColumns
751
- } = selectedLayoutColumns;
752
- const startIndex = selectedColumnIndices[0];
753
- const endIndex = selectedColumnIndices[selectedColumnIndices.length - 1];
754
- const selectedColumnCount = selectedColumns.length;
755
- const selectedColumnIndexSet = new Set(selectedColumnIndices);
763
+ selectedLayoutColumns,
764
+ startIndex,
765
+ endIndex
766
+ } = selectedLayoutColumnsResult;
756
767
  const emitDeleteColumnAnalytics = columnCount => {
757
768
  editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent({
758
769
  action: ACTION.DELETED,
@@ -762,7 +773,7 @@ export const deleteLayoutColumn = editorAnalyticsAPI => ({
762
773
  columnCount,
763
774
  endIndex,
764
775
  inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
765
- selectedCount: selectedColumnCount,
776
+ selectedCount: selectedLayoutColumns.length,
766
777
  startIndex
767
778
  },
768
779
  eventType: EVENT_TYPE.TRACK
@@ -770,7 +781,7 @@ export const deleteLayoutColumn = editorAnalyticsAPI => ({
770
781
  };
771
782
 
772
783
  // If all columns are selected, remove the entire layoutSection
773
- if (selectedColumnCount === layoutSectionNode.childCount) {
784
+ if (selectedLayoutColumns.length === layoutSectionNode.childCount) {
774
785
  tr.delete(layoutSectionPos, layoutSectionPos + layoutSectionNode.nodeSize);
775
786
  emitDeleteColumnAnalytics(0);
776
787
  tr.setMeta('scrollIntoView', false);
@@ -780,17 +791,17 @@ export const deleteLayoutColumn = editorAnalyticsAPI => ({
780
791
  // Build new column list without the selected columns
781
792
  const remainingColumns = [];
782
793
  layoutSectionNode.forEach((column, _offset, index) => {
783
- if (!selectedColumnIndexSet.has(index)) {
794
+ if (index < startIndex || index > endIndex) {
784
795
  remainingColumns.push(column);
785
796
  }
786
797
  });
787
798
 
788
799
  // Redistribute widths proportionally among remaining columns using shared utility
789
800
  const existingWidths = mapChildren(layoutSectionNode, column => column.attrs.width);
790
- const redistributed = selectedColumnIndices.slice()
801
+ const redistributed = selectedLayoutColumns.map((_, i) => startIndex + i)
791
802
  // Delete highest indices first so lower original indices still point at the same columns
792
803
  // as each redistribution step shrinks the widths array.
793
- .sort((a, b) => b - a).reduce((widths, selectedIndex) => redistributeAfterDeletion(widths, selectedIndex, MIN_LAYOUT_COLUMN_WIDTH_PERCENT), existingWidths);
804
+ .reverse().reduce((widths, selectedIndex) => redistributeAfterDeletion(widths, selectedIndex, MIN_LAYOUT_COLUMN_WIDTH_PERCENT), existingWidths);
794
805
  const updatedLayoutSectionNode = layoutSectionNode.copy(Fragment.fromArray(remainingColumns));
795
806
  tr.replaceWith(layoutSectionPos + 1, layoutSectionPos + layoutSectionNode.nodeSize - 1, columnWidth(updatedLayoutSectionNode, tr.doc.type.schema, redistributed));
796
807
  emitDeleteColumnAnalytics(redistributed.length);
@@ -1,6 +1,5 @@
1
1
  const roundLayoutColumnWidth = width => Number(width.toFixed(2));
2
2
  const sumWidths = widths => widths.reduce((sum, width) => sum + width, 0);
3
- const isValidWidth = width => Number.isFinite(width) && width > 0;
4
3
  const normaliseWidthsTotal = (widths, totalWidth, minWidth) => {
5
4
  const roundedWidths = widths.map(roundLayoutColumnWidth);
6
5
  const remainder = roundLayoutColumnWidth(totalWidth - sumWidths(roundedWidths));
@@ -19,6 +18,7 @@ const normaliseWidthsTotal = (widths, totalWidth, minWidth) => {
19
18
  }
20
19
  return roundedWidths.map((width, index) => index === adjustmentIndex ? adjustedWidth : width);
21
20
  };
21
+ const isValidWidth = width => Number.isFinite(width) && width > 0;
22
22
  const redistributeWithMinimumWidth = ({
23
23
  minWidth,
24
24
  totalWidth,
@@ -61,6 +61,39 @@ const redistributeWithMinimumWidth = ({
61
61
  });
62
62
  return widths;
63
63
  };
64
+
65
+ /**
66
+ * Returns true when the given selected columns already reflect the distribution that
67
+ * `distributeLayoutColumns` would produce — i.e. the first N-1 cols each hold
68
+ * `equalWidth` (rounded to 2 dp) and the last col absorbs the rounding remainder.
69
+ *
70
+ * This mirrors the action's "last col absorbs remainder" logic so that the UI can
71
+ * disable the option when it would be a no-op, avoiding spurious undo entries.
72
+ */
73
+
74
+ export const calculateDistribution = selectedWidths => {
75
+ const count = selectedWidths.length;
76
+ if (count < 2) {
77
+ return undefined;
78
+ }
79
+ const selectedTotal = sumWidths(selectedWidths);
80
+ const equalWidth = roundLayoutColumnWidth(selectedTotal / count);
81
+ return {
82
+ selectedTotal,
83
+ equalWidth
84
+ };
85
+ };
86
+ export function isDistributedUniformly(selectedWidths, distribution = calculateDistribution(selectedWidths)) {
87
+ if (!distribution || selectedWidths.length < 2) {
88
+ return false;
89
+ }
90
+ const {
91
+ selectedTotal,
92
+ equalWidth
93
+ } = distribution;
94
+ const lastColWidth = roundLayoutColumnWidth(selectedTotal - equalWidth * (selectedWidths.length - 1));
95
+ return selectedWidths.slice(0, -1).every(width => width === equalWidth) && selectedWidths[selectedWidths.length - 1] === lastColWidth;
96
+ }
64
97
  export const redistributeAfterDeletion = (currentWidths, removeIndex, minWidth) => {
65
98
  if (currentWidths.length === 0 || removeIndex < 0 || removeIndex >= currentWidths.length || !isValidWidth(minWidth)) {
66
99
  return currentWidths;