@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
@@ -12,8 +12,8 @@ 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';
16
- import { redistributeAfterDeletion, redistributeProportionally } from './utils/redistribute-proportionally';
15
+ import { calculateDistribution, isDistributedUniformly, redistributeAfterDeletion, redistributeProportionally } from './utils/layout-column-distribution';
16
+ import { getAllLayoutColumnsFromSelection, getSelectedLayoutColumnsFromSelection } from './utils/layout-column-selection';
17
17
  export var ONE_COL_LAYOUTS = ['single'];
18
18
  export var TWO_COL_LAYOUTS = ['two_equal', 'two_left_sidebar', 'two_right_sidebar'];
19
19
  export var THREE_COL_LAYOUTS = ['three_equal', 'three_with_sidebars'];
@@ -554,18 +554,17 @@ var insertLayoutColumnAt = function insertLayoutColumnAt(side, editorAnalyticsAP
554
554
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
555
555
  return null;
556
556
  }
557
- var selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
558
- if (!selectedLayoutColumns) {
557
+ var selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
558
+ if (!selectedLayoutColumnsResult || selectedLayoutColumnsResult.selectedLayoutColumns.length === 0) {
559
559
  return null;
560
560
  }
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];
561
+ var layoutSectionNode = selectedLayoutColumnsResult.layoutSectionNode,
562
+ layoutSectionPos = selectedLayoutColumnsResult.layoutSectionPos,
563
+ startIndex = selectedLayoutColumnsResult.startIndex,
564
+ endIndex = selectedLayoutColumnsResult.endIndex,
565
+ selectedLayoutColumns = selectedLayoutColumnsResult.selectedLayoutColumns;
567
566
  var selectedColumnIndex = side === 'left' ? startIndex : endIndex;
568
- var selectedColumnCount = selectedColumns.length;
567
+ var selectedColumnCount = selectedLayoutColumns.length;
569
568
  if (layoutSectionNode.childCount >= getEffectiveMaxLayoutColumns()) {
570
569
  return null;
571
570
  }
@@ -623,22 +622,20 @@ export var setLayoutColumnValign = function setLayoutColumnValign(valign, editor
623
622
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
624
623
  return null;
625
624
  }
626
- var selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
627
- if (!selectedLayoutColumns) {
625
+ var selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
626
+ if (!selectedLayoutColumnsResult) {
628
627
  return null;
629
628
  }
630
- var selectedColumnIndices = selectedLayoutColumns.selectedColumnIndices,
631
- selectedColumns = selectedLayoutColumns.selectedColumns;
632
- var columnsToUpdate = selectedColumns.filter(function (_ref6) {
629
+ var startIndex = selectedLayoutColumnsResult.startIndex,
630
+ endIndex = selectedLayoutColumnsResult.endIndex,
631
+ selectedLayoutColumns = selectedLayoutColumnsResult.selectedLayoutColumns;
632
+ var columnsToUpdate = selectedLayoutColumns.filter(function (_ref6) {
633
633
  var node = _ref6.node;
634
634
  return node.attrs.valign !== valign;
635
635
  });
636
636
  if (columnsToUpdate.length === 0) {
637
637
  return null;
638
638
  }
639
- var startIndex = selectedColumnIndices[0];
640
- var endIndex = selectedColumnIndices[selectedColumnIndices.length - 1];
641
- var selectedColumnCount = selectedColumns.length;
642
639
  var updatedColumnCount = columnsToUpdate.length;
643
640
  columnsToUpdate.forEach(function (_ref7) {
644
641
  var node = _ref7.node,
@@ -654,7 +651,7 @@ export var setLayoutColumnValign = function setLayoutColumnValign(valign, editor
654
651
  attributes: {
655
652
  endIndex: endIndex,
656
653
  inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
657
- selectedCount: selectedColumnCount,
654
+ selectedCount: selectedLayoutColumns.length,
658
655
  startIndex: startIndex,
659
656
  updatedCount: updatedColumnCount,
660
657
  valign: valign
@@ -665,73 +662,80 @@ export var setLayoutColumnValign = function setLayoutColumnValign(valign, editor
665
662
  return tr;
666
663
  };
667
664
  };
668
- export var distributeLayoutColumns = function distributeLayoutColumns(editorAnalyticsAPI) {
669
- var inputMethod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : INPUT_METHOD.LAYOUT_COLUMN_MENU;
670
- return function (_ref8) {
671
- var tr = _ref8.tr;
665
+ export var distributeLayoutColumns = function distributeLayoutColumns() {
666
+ var _ref8 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
667
+ editorAnalyticsAPI = _ref8.editorAnalyticsAPI,
668
+ _ref8$inputMethod = _ref8.inputMethod,
669
+ inputMethod = _ref8$inputMethod === void 0 ? INPUT_METHOD.LAYOUT_COLUMN_MENU : _ref8$inputMethod,
670
+ _ref8$target = _ref8.target,
671
+ target = _ref8$target === void 0 ? 'selectedColumns' : _ref8$target;
672
+ return function (_ref9) {
673
+ var tr = _ref9.tr;
672
674
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
673
675
  return null;
674
676
  }
675
- var selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
676
- if (!selectedLayoutColumns || selectedLayoutColumns.selectedColumns.length < 2) {
677
+ var selectedLayoutColumnsResult = target === 'allColumns' ? getAllLayoutColumnsFromSelection(tr.selection) : getSelectedLayoutColumnsFromSelection(tr.selection);
678
+ if (!selectedLayoutColumnsResult || selectedLayoutColumnsResult.selectedLayoutColumns.length < 2) {
677
679
  return null;
678
680
  }
679
- var layoutSectionNode = selectedLayoutColumns.layoutSectionNode,
680
- layoutSectionPos = selectedLayoutColumns.layoutSectionPos,
681
- selectedColumnIndices = selectedLayoutColumns.selectedColumnIndices,
682
- selectedColumns = selectedLayoutColumns.selectedColumns;
683
- var endIndex = selectedColumnIndices[selectedColumnIndices.length - 1];
684
- var selectedColumnCount = selectedColumns.length;
685
- var totalColumnCount = layoutSectionNode.childCount;
686
-
687
- // Compute equal width for selected columns
681
+ var layoutSectionNode = selectedLayoutColumnsResult.layoutSectionNode,
682
+ startIndex = selectedLayoutColumnsResult.startIndex,
683
+ endIndex = selectedLayoutColumnsResult.endIndex,
684
+ selectedLayoutColumns = selectedLayoutColumnsResult.selectedLayoutColumns;
688
685
  var existingWidths = mapChildren(layoutSectionNode, function (column) {
689
686
  return column.attrs.width;
690
687
  });
691
- var selectedTotal = selectedColumnIndices.reduce(function (sum, idx) {
692
- return sum + existingWidths[idx];
693
- }, 0);
694
- var equalWidth = selectedTotal / selectedColumnCount;
695
-
696
- // Early return if selected columns are already uniformly distributed — avoids spurious undo entry.
697
- if (selectedColumnIndices.every(function (idx) {
698
- return existingWidths[idx] === Number(equalWidth.toFixed(2));
699
- })) {
688
+ var selectedWidths = selectedLayoutColumns.map(function (_ref0) {
689
+ var node = _ref0.node;
690
+ return node.attrs.width;
691
+ });
692
+ var distribution = calculateDistribution(selectedWidths);
693
+ if (!distribution) {
694
+ return null;
695
+ }
696
+ var selectedTotal = distribution.selectedTotal,
697
+ equalWidth = distribution.equalWidth;
698
+ if (isDistributedUniformly(selectedWidths, distribution)) {
700
699
  return null;
701
700
  }
702
701
 
703
702
  // Build new widths array: selected columns get equal share, unselected unchanged.
704
- // Assign truncated (2dp) equal widths to all selected cols except the last, which absorbs
703
+ // Assign rounded (2dp) equal widths to all selected cols except the last, which absorbs
705
704
  // the rounding remainder so the sum of selected widths equals selectedTotal exactly.
706
- var selectedIndexSet = new Set(selectedColumnIndices);
707
705
  var assignedToSelected = 0;
708
706
  var selectedAssignedCount = 0;
709
707
  var newWidths = existingWidths.map(function (w, idx) {
710
- if (!selectedIndexSet.has(idx)) {
708
+ if (idx < startIndex || idx > endIndex) {
711
709
  return w;
712
710
  }
713
711
  selectedAssignedCount += 1;
714
- if (selectedAssignedCount < selectedColumnCount) {
715
- var truncated = Number(equalWidth.toFixed(2));
716
- assignedToSelected += truncated;
717
- return truncated;
712
+ if (selectedAssignedCount < selectedLayoutColumns.length) {
713
+ assignedToSelected += equalWidth;
714
+ return equalWidth;
718
715
  }
719
716
  // Last selected column: absorb the remainder to avoid drift
720
717
  return Number((selectedTotal - assignedToSelected).toFixed(2));
721
718
  });
722
719
 
723
- // Apply widths via replaceWith (mirroring forceColumnWidths approach)
724
- tr.replaceWith(layoutSectionPos + 1, layoutSectionPos + layoutSectionNode.nodeSize - 1, columnWidth(layoutSectionNode, tr.doc.type.schema, newWidths));
720
+ // Apply widths via setNodeMarkup per selected column — keeps nodes in place (preserves identity, marks, decorations)
721
+ selectedLayoutColumns.forEach(function (_ref1, i) {
722
+ var node = _ref1.node,
723
+ pos = _ref1.pos;
724
+ var colIdx = startIndex + i;
725
+ tr.setNodeMarkup(pos, node.type, _objectSpread(_objectSpread({}, node.attrs), {}, {
726
+ width: newWidths[colIdx]
727
+ }));
728
+ });
725
729
  editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent({
726
730
  action: ACTION.UPDATED,
727
731
  actionSubject: ACTION_SUBJECT.DOCUMENT,
728
732
  actionSubjectId: ACTION_SUBJECT_ID.LAYOUT_COLUMN,
729
733
  attributes: {
730
- columnCount: totalColumnCount,
734
+ columnCount: layoutSectionNode.childCount,
731
735
  endIndex: endIndex,
732
736
  inputMethod: inputMethod,
733
- selectedCount: selectedColumnCount,
734
- startIndex: selectedColumnIndices[0]
737
+ selectedCount: selectedLayoutColumns.length,
738
+ startIndex: startIndex
735
739
  },
736
740
  eventType: EVENT_TYPE.TRACK
737
741
  })(tr);
@@ -739,11 +743,19 @@ export var distributeLayoutColumns = function distributeLayoutColumns(editorAnal
739
743
  return tr;
740
744
  };
741
745
  };
742
- export var toggleLayoutColumnMenu = function toggleLayoutColumnMenu(_ref9) {
743
- var anchorPos = _ref9.anchorPos,
744
- isOpen = _ref9.isOpen;
745
- return function (_ref0) {
746
- var tr = _ref0.tr;
746
+ export var createDistributeLayoutColumnsCommand = function createDistributeLayoutColumnsCommand(editorAnalyticsAPI) {
747
+ return function () {
748
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
749
+ return distributeLayoutColumns(_objectSpread(_objectSpread({}, options), {}, {
750
+ editorAnalyticsAPI: editorAnalyticsAPI
751
+ }));
752
+ };
753
+ };
754
+ export var toggleLayoutColumnMenu = function toggleLayoutColumnMenu(_ref10) {
755
+ var anchorPos = _ref10.anchorPos,
756
+ isOpen = _ref10.isOpen;
757
+ return function (_ref11) {
758
+ var tr = _ref11.tr;
747
759
  tr.setMeta('toggleLayoutColumnMenu', {
748
760
  anchorPos: anchorPos,
749
761
  isOpen: isOpen
@@ -753,23 +765,20 @@ export var toggleLayoutColumnMenu = function toggleLayoutColumnMenu(_ref9) {
753
765
  };
754
766
  };
755
767
  export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI) {
756
- return function (_ref1) {
757
- var tr = _ref1.tr;
768
+ return function (_ref12) {
769
+ var tr = _ref12.tr;
758
770
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
759
771
  return null;
760
772
  }
761
- var selectedLayoutColumns = getSelectedLayoutColumns(tr.selection);
762
- if (!selectedLayoutColumns) {
773
+ var selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
774
+ if (!selectedLayoutColumnsResult || selectedLayoutColumnsResult.selectedLayoutColumns.length === 0) {
763
775
  return null;
764
776
  }
765
- var layoutSectionNode = selectedLayoutColumns.layoutSectionNode,
766
- layoutSectionPos = selectedLayoutColumns.layoutSectionPos,
767
- selectedColumnIndices = selectedLayoutColumns.selectedColumnIndices,
768
- selectedColumns = selectedLayoutColumns.selectedColumns;
769
- var startIndex = selectedColumnIndices[0];
770
- var endIndex = selectedColumnIndices[selectedColumnIndices.length - 1];
771
- var selectedColumnCount = selectedColumns.length;
772
- var selectedColumnIndexSet = new Set(selectedColumnIndices);
777
+ var layoutSectionNode = selectedLayoutColumnsResult.layoutSectionNode,
778
+ layoutSectionPos = selectedLayoutColumnsResult.layoutSectionPos,
779
+ selectedLayoutColumns = selectedLayoutColumnsResult.selectedLayoutColumns,
780
+ startIndex = selectedLayoutColumnsResult.startIndex,
781
+ endIndex = selectedLayoutColumnsResult.endIndex;
773
782
  var emitDeleteColumnAnalytics = function emitDeleteColumnAnalytics(columnCount) {
774
783
  editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent({
775
784
  action: ACTION.DELETED,
@@ -779,7 +788,7 @@ export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI)
779
788
  columnCount: columnCount,
780
789
  endIndex: endIndex,
781
790
  inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
782
- selectedCount: selectedColumnCount,
791
+ selectedCount: selectedLayoutColumns.length,
783
792
  startIndex: startIndex
784
793
  },
785
794
  eventType: EVENT_TYPE.TRACK
@@ -787,7 +796,7 @@ export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI)
787
796
  };
788
797
 
789
798
  // If all columns are selected, remove the entire layoutSection
790
- if (selectedColumnCount === layoutSectionNode.childCount) {
799
+ if (selectedLayoutColumns.length === layoutSectionNode.childCount) {
791
800
  tr.delete(layoutSectionPos, layoutSectionPos + layoutSectionNode.nodeSize);
792
801
  emitDeleteColumnAnalytics(0);
793
802
  tr.setMeta('scrollIntoView', false);
@@ -797,7 +806,7 @@ export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI)
797
806
  // Build new column list without the selected columns
798
807
  var remainingColumns = [];
799
808
  layoutSectionNode.forEach(function (column, _offset, index) {
800
- if (!selectedColumnIndexSet.has(index)) {
809
+ if (index < startIndex || index > endIndex) {
801
810
  remainingColumns.push(column);
802
811
  }
803
812
  });
@@ -806,12 +815,12 @@ export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI)
806
815
  var existingWidths = mapChildren(layoutSectionNode, function (column) {
807
816
  return column.attrs.width;
808
817
  });
809
- var redistributed = selectedColumnIndices.slice()
818
+ var redistributed = selectedLayoutColumns.map(function (_, i) {
819
+ return startIndex + i;
820
+ })
810
821
  // Delete highest indices first so lower original indices still point at the same columns
811
822
  // as each redistribution step shrinks the widths array.
812
- .sort(function (a, b) {
813
- return b - a;
814
- }).reduce(function (widths, selectedIndex) {
823
+ .reverse().reduce(function (widths, selectedIndex) {
815
824
  return redistributeAfterDeletion(widths, selectedIndex, MIN_LAYOUT_COLUMN_WIDTH_PERCENT);
816
825
  }, existingWidths);
817
826
  var updatedLayoutSectionNode = layoutSectionNode.copy(Fragment.fromArray(remainingColumns));
@@ -7,9 +7,6 @@ var sumWidths = function sumWidths(widths) {
7
7
  return sum + width;
8
8
  }, 0);
9
9
  };
10
- var isValidWidth = function isValidWidth(width) {
11
- return Number.isFinite(width) && width > 0;
12
- };
13
10
  var normaliseWidthsTotal = function normaliseWidthsTotal(widths, totalWidth, minWidth) {
14
11
  var roundedWidths = widths.map(roundLayoutColumnWidth);
15
12
  var remainder = roundLayoutColumnWidth(totalWidth - sumWidths(roundedWidths));
@@ -30,6 +27,9 @@ var normaliseWidthsTotal = function normaliseWidthsTotal(widths, totalWidth, min
30
27
  return index === adjustmentIndex ? adjustedWidth : width;
31
28
  });
32
29
  };
30
+ var isValidWidth = function isValidWidth(width) {
31
+ return Number.isFinite(width) && width > 0;
32
+ };
33
33
  var redistributeWithMinimumWidth = function redistributeWithMinimumWidth(_ref) {
34
34
  var minWidth = _ref.minWidth,
35
35
  totalWidth = _ref.totalWidth,
@@ -74,6 +74,40 @@ var redistributeWithMinimumWidth = function redistributeWithMinimumWidth(_ref) {
74
74
  });
75
75
  return widths;
76
76
  };
77
+
78
+ /**
79
+ * Returns true when the given selected columns already reflect the distribution that
80
+ * `distributeLayoutColumns` would produce — i.e. the first N-1 cols each hold
81
+ * `equalWidth` (rounded to 2 dp) and the last col absorbs the rounding remainder.
82
+ *
83
+ * This mirrors the action's "last col absorbs remainder" logic so that the UI can
84
+ * disable the option when it would be a no-op, avoiding spurious undo entries.
85
+ */
86
+
87
+ export var calculateDistribution = function calculateDistribution(selectedWidths) {
88
+ var count = selectedWidths.length;
89
+ if (count < 2) {
90
+ return undefined;
91
+ }
92
+ var selectedTotal = sumWidths(selectedWidths);
93
+ var equalWidth = roundLayoutColumnWidth(selectedTotal / count);
94
+ return {
95
+ selectedTotal: selectedTotal,
96
+ equalWidth: equalWidth
97
+ };
98
+ };
99
+ export function isDistributedUniformly(selectedWidths) {
100
+ var distribution = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : calculateDistribution(selectedWidths);
101
+ if (!distribution || selectedWidths.length < 2) {
102
+ return false;
103
+ }
104
+ var selectedTotal = distribution.selectedTotal,
105
+ equalWidth = distribution.equalWidth;
106
+ var lastColWidth = roundLayoutColumnWidth(selectedTotal - equalWidth * (selectedWidths.length - 1));
107
+ return selectedWidths.slice(0, -1).every(function (width) {
108
+ return width === equalWidth;
109
+ }) && selectedWidths[selectedWidths.length - 1] === lastColWidth;
110
+ }
77
111
  export var redistributeAfterDeletion = function redistributeAfterDeletion(currentWidths, removeIndex, minWidth) {
78
112
  if (currentWidths.length === 0 || removeIndex < 0 || removeIndex >= currentWidths.length || !isValidWidth(minWidth)) {
79
113
  return currentWidths;
@@ -1,123 +1,81 @@
1
- import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
2
- import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
3
- var isLayoutColumn = function isLayoutColumn(node) {
4
- return (node === null || node === void 0 ? void 0 : node.type.name) === 'layoutColumn';
1
+ import { findChildrenByType, findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
2
+ var findLayoutSectionFromSelection = function findLayoutSectionFromSelection(selection) {
3
+ return findParentNodeOfType(selection.$from.doc.type.schema.nodes.layoutSection)(selection);
5
4
  };
6
- var isLayoutSection = function isLayoutSection(node) {
7
- return (node === null || node === void 0 ? void 0 : node.type.name) === 'layoutSection';
8
- };
9
- var getLayoutColumnIndexAtPos = function getLayoutColumnIndexAtPos($pos) {
10
- for (var depth = $pos.depth; depth > 0; depth--) {
11
- if (isLayoutColumn($pos.node(depth)) && isLayoutSection($pos.node(depth - 1))) {
12
- return $pos.index(depth - 1);
13
- }
14
- }
15
- return undefined;
16
- };
17
- var getLayoutSectionDepth = function getLayoutSectionDepth(selection) {
18
- var $from = selection.$from,
19
- $to = selection.$to;
20
- var sharedDepth = $from.sharedDepth($to.pos);
21
- for (var depth = sharedDepth; depth > 0; depth--) {
22
- if (isLayoutSection($from.node(depth))) {
23
- return depth;
24
- }
25
- }
26
- return undefined;
27
- };
28
- export var getSelectedLayoutColumns = function getSelectedLayoutColumns(selection) {
29
- if (!selection) {
30
- return undefined;
31
- }
32
- if (selection instanceof NodeSelection && isLayoutColumn(selection.node)) {
33
- var _$from = selection.$from;
34
- var _layoutSectionNode = _$from.parent;
35
- if (!isLayoutSection(_layoutSectionNode)) {
36
- return undefined;
37
- }
38
- var selectedColumnIndex = _$from.index(_$from.depth);
5
+ var findLayoutColumnsFromLayoutSection = function findLayoutColumnsFromLayoutSection(layoutSectionNode) {
6
+ var layoutSectionPos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
7
+ return findChildrenByType(layoutSectionNode, layoutSectionNode.type.schema.nodes.layoutColumn).map(function (_ref) {
8
+ var node = _ref.node,
9
+ pos = _ref.pos;
39
10
  return {
40
- layoutSectionNode: _layoutSectionNode,
41
- layoutSectionPos: _$from.before(_$from.depth),
42
- selectedColumnIndices: [selectedColumnIndex],
43
- selectedColumns: [{
44
- index: selectedColumnIndex,
45
- node: selection.node,
46
- pos: selection.from
47
- }]
11
+ node: node,
12
+ pos: pos + layoutSectionPos + 1
48
13
  };
49
- }
50
- if (selection.empty) {
51
- return undefined;
52
- }
53
- if (!editorExperiment('platform_editor_block_menu', true)) {
14
+ });
15
+ };
16
+ export var getSelectedLayoutColumnsFromSelection = function getSelectedLayoutColumnsFromSelection(selection) {
17
+ var layoutSection = findLayoutSectionFromSelection(selection);
18
+ if (!layoutSection) {
54
19
  return undefined;
55
20
  }
56
- var layoutSectionDepth = getLayoutSectionDepth(selection);
57
- if (layoutSectionDepth === undefined) {
21
+ var layoutSectionNode = layoutSection.node,
22
+ layoutSectionPos = layoutSection.pos;
23
+ var allLayoutColumns = findLayoutColumnsFromLayoutSection(layoutSectionNode, layoutSectionPos);
24
+ if (!allLayoutColumns.length) {
58
25
  return undefined;
59
26
  }
60
- var $from = selection.$from,
61
- $to = selection.$to;
62
- var layoutSectionNode = $from.node(layoutSectionDepth);
63
- var layoutSectionPos = $from.before(layoutSectionDepth);
64
- var selectedColumns = [];
65
- var invalidSelection = false;
66
- layoutSectionNode.forEach(function (column, offset, index) {
67
- var columnStart = layoutSectionPos + 1 + offset;
68
- var columnEnd = columnStart + column.nodeSize;
69
- var intersectsColumn = selection.from < columnEnd && selection.to > columnStart;
70
- if (!intersectsColumn) {
71
- return;
27
+ var startIndex = -1;
28
+ var endIndex = -1;
29
+ var selectedLayoutColumns = allLayoutColumns.filter(function (_ref2, index) {
30
+ var node = _ref2.node,
31
+ pos = _ref2.pos;
32
+ var isSelected = selection.from <= pos && selection.to >= pos + node.nodeSize;
33
+ if (isSelected) {
34
+ if (startIndex === -1) {
35
+ startIndex = index;
36
+ }
37
+ endIndex = index;
72
38
  }
73
- if (!isLayoutColumn(column)) {
74
- invalidSelection = true;
75
- return;
76
- }
77
- selectedColumns.push({
78
- index: index,
79
- node: column,
80
- pos: columnStart
81
- });
39
+ return isSelected;
82
40
  });
83
-
84
- // TextSelection inside a single column is normal text editing, not a selected column set.
85
- if (invalidSelection || selectedColumns.length < 2) {
41
+ return {
42
+ layoutSectionNode: layoutSectionNode,
43
+ layoutSectionPos: layoutSectionPos,
44
+ selectedLayoutColumns: selectedLayoutColumns,
45
+ startIndex: startIndex,
46
+ endIndex: endIndex
47
+ };
48
+ };
49
+ export var getAllLayoutColumnsFromSelection = function getAllLayoutColumnsFromSelection(selection) {
50
+ var layoutSection = findLayoutSectionFromSelection(selection);
51
+ if (!layoutSection) {
86
52
  return undefined;
87
53
  }
88
- var firstColumn = selectedColumns[0];
89
- var lastColumn = selectedColumns[selectedColumns.length - 1];
90
- var startColumnIndex = getLayoutColumnIndexAtPos($from);
91
- var endColumnIndex = getLayoutColumnIndexAtPos($to);
92
- if (startColumnIndex !== undefined && startColumnIndex !== firstColumn.index || endColumnIndex !== undefined && endColumnIndex !== lastColumn.index) {
54
+ var layoutColumns = findLayoutColumnsFromLayoutSection(layoutSection.node, layoutSection.pos);
55
+ if (!(layoutColumns !== null && layoutColumns !== void 0 && layoutColumns.length)) {
93
56
  return undefined;
94
57
  }
95
58
  return {
96
- layoutSectionNode: layoutSectionNode,
97
- layoutSectionPos: layoutSectionPos,
98
- selectedColumnIndices: selectedColumns.map(function (_ref) {
99
- var index = _ref.index;
100
- return index;
101
- }),
102
- selectedColumns: selectedColumns
59
+ layoutSectionNode: layoutSection.node,
60
+ layoutSectionPos: layoutSection.pos,
61
+ selectedLayoutColumns: layoutColumns,
62
+ startIndex: 0,
63
+ endIndex: layoutColumns.length - 1
103
64
  };
104
65
  };
105
- export var getLayoutSectionColumnCount = function getLayoutSectionColumnCount(layoutSection) {
106
- return (layoutSection === null || layoutSection === void 0 ? void 0 : layoutSection.type.name) === 'layoutSection' ? layoutSection.childCount : 0;
107
- };
108
66
  export var getLayoutColumnValign = function getLayoutColumnValign(layoutColumn) {
109
- var _ref2;
110
- return layoutColumn ? (_ref2 = layoutColumn.attrs.valign) !== null && _ref2 !== void 0 ? _ref2 : 'top' : undefined;
67
+ var _ref3;
68
+ return layoutColumn ? (_ref3 = layoutColumn.attrs.valign) !== null && _ref3 !== void 0 ? _ref3 : 'top' : undefined;
111
69
  };
112
70
  export var getLayoutColumnMenuAnchorPos = function getLayoutColumnMenuAnchorPos(selection, anchorPosFromHandle) {
113
71
  var _clickedSelectedColum, _selectedLayoutColumn;
114
- var selectedLayoutColumns = getSelectedLayoutColumns(selection);
72
+ var selectedLayoutColumns = getSelectedLayoutColumnsFromSelection(selection);
115
73
  if (!selectedLayoutColumns) {
116
74
  return undefined;
117
75
  }
118
- var clickedSelectedColumn = selectedLayoutColumns.selectedColumns.find(function (_ref3) {
119
- var pos = _ref3.pos;
76
+ var clickedSelectedColumn = selectedLayoutColumns.selectedLayoutColumns.find(function (_ref4) {
77
+ var pos = _ref4.pos;
120
78
  return pos === anchorPosFromHandle;
121
79
  });
122
- return (_clickedSelectedColum = clickedSelectedColumn === null || clickedSelectedColumn === void 0 ? void 0 : clickedSelectedColumn.pos) !== null && _clickedSelectedColum !== void 0 ? _clickedSelectedColum : (_selectedLayoutColumn = selectedLayoutColumns.selectedColumns[0]) === null || _selectedLayoutColumn === void 0 ? void 0 : _selectedLayoutColumn.pos;
80
+ return (_clickedSelectedColum = clickedSelectedColumn === null || clickedSelectedColumn === void 0 ? void 0 : clickedSelectedColumn.pos) !== null && _clickedSelectedColum !== void 0 ? _clickedSelectedColum : (_selectedLayoutColumn = selectedLayoutColumns.selectedLayoutColumns[0]) === null || _selectedLayoutColumn === void 0 ? void 0 : _selectedLayoutColumn.pos;
123
81
  };
@@ -28,7 +28,7 @@ var DeleteColumnDropdownItem = function DeleteColumnDropdownItem(_ref) {
28
28
  if (selectedLayoutColumns === undefined) {
29
29
  return null;
30
30
  }
31
- var selectedColumnCount = selectedLayoutColumns.selectedColumns.length;
31
+ var selectedColumnCount = selectedLayoutColumns.selectedLayoutColumns.length;
32
32
  return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
33
33
  onClick: onClick
34
34
  }, formatMessage(layoutMessages.deleteColumn, {
@@ -1,18 +1,24 @@
1
1
  import React, { useCallback, useMemo } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
3
4
  import { layoutMessages } from '@atlaskit/editor-common/messages';
4
5
  import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
6
+ import { isDistributedUniformly } from '../../pm-plugins/utils/layout-column-distribution';
5
7
  import { useSelectedLayoutColumns } from './useSelectedLayoutColumns';
6
8
  export var DistributeColumnsDropdownItem = function DistributeColumnsDropdownItem(_ref) {
7
9
  var api = _ref.api;
8
10
  var _useIntl = useIntl(),
9
11
  formatMessage = _useIntl.formatMessage;
10
- var selectedLayoutColumns = useSelectedLayoutColumns(api);
12
+ var selectedLayoutColumnsResult = useSelectedLayoutColumns(api);
13
+ var _ref2 = selectedLayoutColumnsResult !== null && selectedLayoutColumnsResult !== void 0 ? selectedLayoutColumnsResult : {},
14
+ selectedLayoutColumns = _ref2.selectedLayoutColumns;
11
15
  var handleClick = useCallback(function () {
12
16
  var _api$core;
13
17
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (props) {
14
18
  var _api$layout, _api$layout2;
15
- var tr = api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.distributeLayoutColumns(props);
19
+ var tr = api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.distributeLayoutColumns({
20
+ inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU
21
+ })(props);
16
22
  if (!tr) {
17
23
  return null;
18
24
  }
@@ -24,31 +30,20 @@ export var DistributeColumnsDropdownItem = function DistributeColumnsDropdownIte
24
30
  return closedTr !== null && closedTr !== void 0 ? closedTr : tr;
25
31
  });
26
32
  }, [api]);
27
-
28
- // Hide when selected columns are already evenly distributed — no-op action.
29
- // Must be before any early return to satisfy rules-of-hooks.
30
33
  var isAlreadyUniform = useMemo(function () {
31
- if (!selectedLayoutColumns || selectedLayoutColumns.selectedColumns.length < 2) {
34
+ if (!selectedLayoutColumns || selectedLayoutColumns.length < 2) {
32
35
  return false;
33
36
  }
34
- var selectedWidths = selectedLayoutColumns.selectedColumns.map(function (col) {
37
+ var selectedWidths = selectedLayoutColumns.map(function (col) {
35
38
  return col.node.attrs.width;
36
39
  });
37
- var selectedTotal = selectedWidths.reduce(function (sum, w) {
38
- return sum + w;
39
- }, 0);
40
- var equalWidth = Number((selectedTotal / selectedWidths.length).toFixed(2));
41
- return selectedWidths.every(function (w) {
42
- return w === equalWidth;
43
- });
40
+ return isDistributedUniformly(selectedWidths);
44
41
  }, [selectedLayoutColumns]);
45
- if (selectedLayoutColumns === undefined || selectedLayoutColumns.selectedColumns.length < 2) {
46
- return null;
47
- }
48
- if (isAlreadyUniform) {
42
+ if (selectedLayoutColumns === undefined || selectedLayoutColumns.length < 2) {
49
43
  return null;
50
44
  }
51
45
  return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
52
- onClick: handleClick
46
+ onClick: handleClick,
47
+ isDisabled: isAlreadyUniform
53
48
  }, formatMessage(layoutMessages.distributeColumns));
54
49
  };
@@ -3,7 +3,6 @@ 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 '../../pm-plugins/utils/layout-column-selection';
7
6
  import { useSelectedLayoutColumns } from './useSelectedLayoutColumns';
8
7
  var INSERT_COLUMN_OPTIONS = {
9
8
  left: {
@@ -18,6 +17,7 @@ var INSERT_COLUMN_OPTIONS = {
18
17
  }
19
18
  };
20
19
  export var InsertColumnDropdownItem = function InsertColumnDropdownItem(_ref) {
20
+ var _selectedLayoutColumn, _selectedLayoutColumn2;
21
21
  var api = _ref.api,
22
22
  side = _ref.side;
23
23
  var _useIntl = useIntl(),
@@ -26,7 +26,7 @@ export var InsertColumnDropdownItem = function InsertColumnDropdownItem(_ref) {
26
26
  Icon = _INSERT_COLUMN_OPTION.Icon,
27
27
  label = _INSERT_COLUMN_OPTION.label;
28
28
  var selectedLayoutColumns = useSelectedLayoutColumns(api);
29
- var columnCount = getLayoutSectionColumnCount(selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.layoutSectionNode);
29
+ 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;
30
30
  var maxColumnCount = getEffectiveMaxLayoutColumns();
31
31
  var canInsertColumn = selectedLayoutColumns !== undefined && columnCount < maxColumnCount;
32
32
  var onClick = useCallback(function () {
@@ -12,7 +12,7 @@ export var VerticalAlignDropdownItem = function VerticalAlignDropdownItem(_ref)
12
12
  var _useIntl = useIntl(),
13
13
  formatMessage = _useIntl.formatMessage;
14
14
  var selectedLayoutColumns = useSelectedLayoutColumns(api);
15
- var isSelected = (_selectedLayoutColumn = selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.selectedColumns.every(function (_ref2) {
15
+ var isSelected = (_selectedLayoutColumn = selectedLayoutColumns === null || selectedLayoutColumns === void 0 ? void 0 : selectedLayoutColumns.selectedLayoutColumns.every(function (_ref2) {
16
16
  var node = _ref2.node;
17
17
  return getLayoutColumnValign(node) === value;
18
18
  })) !== null && _selectedLayoutColumn !== void 0 ? _selectedLayoutColumn : false;