@atlaskit/editor-plugin-layout 10.3.3 → 10.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/layoutPlugin.js +4 -0
  3. package/dist/cjs/pm-plugins/actions.js +102 -7
  4. package/dist/cjs/pm-plugins/column-resize-divider.js +7 -9
  5. package/dist/cjs/pm-plugins/consts.js +4 -1
  6. package/dist/cjs/pm-plugins/utils/redistribute-proportionally.js +111 -0
  7. package/dist/cjs/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +69 -0
  8. package/dist/cjs/ui/LayoutColumnMenu/VerticalAlignDropdownItem.js +7 -9
  9. package/dist/cjs/ui/LayoutColumnMenu/VerticalAlignNestedMenu.js +11 -10
  10. package/dist/cjs/ui/LayoutColumnMenu/components.js +21 -0
  11. package/dist/cjs/ui/LayoutColumnMenu/index.js +39 -11
  12. package/dist/cjs/ui/LayoutColumnMenu/keys.js +10 -2
  13. package/dist/cjs/ui/LayoutColumnMenu/layoutColumnSelection.js +21 -0
  14. package/dist/cjs/ui/LayoutColumnMenu/useCurrentLayoutColumn.js +20 -0
  15. package/dist/es2019/layoutPlugin.js +5 -1
  16. package/dist/es2019/pm-plugins/actions.js +98 -1
  17. package/dist/es2019/pm-plugins/column-resize-divider.js +8 -9
  18. package/dist/es2019/pm-plugins/consts.js +3 -0
  19. package/dist/es2019/pm-plugins/utils/redistribute-proportionally.js +89 -0
  20. package/dist/es2019/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +64 -0
  21. package/dist/es2019/ui/LayoutColumnMenu/VerticalAlignDropdownItem.js +6 -11
  22. package/dist/es2019/ui/LayoutColumnMenu/VerticalAlignNestedMenu.js +7 -10
  23. package/dist/es2019/ui/LayoutColumnMenu/components.js +22 -1
  24. package/dist/es2019/ui/LayoutColumnMenu/index.js +34 -13
  25. package/dist/es2019/ui/LayoutColumnMenu/keys.js +12 -2
  26. package/dist/es2019/ui/LayoutColumnMenu/layoutColumnSelection.js +9 -0
  27. package/dist/es2019/ui/LayoutColumnMenu/useCurrentLayoutColumn.js +10 -0
  28. package/dist/esm/layoutPlugin.js +5 -1
  29. package/dist/esm/pm-plugins/actions.js +101 -7
  30. package/dist/esm/pm-plugins/column-resize-divider.js +8 -9
  31. package/dist/esm/pm-plugins/consts.js +3 -0
  32. package/dist/esm/pm-plugins/utils/redistribute-proportionally.js +104 -0
  33. package/dist/esm/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +61 -0
  34. package/dist/esm/ui/LayoutColumnMenu/VerticalAlignDropdownItem.js +8 -10
  35. package/dist/esm/ui/LayoutColumnMenu/VerticalAlignNestedMenu.js +9 -9
  36. package/dist/esm/ui/LayoutColumnMenu/components.js +22 -1
  37. package/dist/esm/ui/LayoutColumnMenu/index.js +40 -13
  38. package/dist/esm/ui/LayoutColumnMenu/keys.js +9 -1
  39. package/dist/esm/ui/LayoutColumnMenu/layoutColumnSelection.js +15 -0
  40. package/dist/esm/ui/LayoutColumnMenu/useCurrentLayoutColumn.js +14 -0
  41. package/dist/types/layoutPluginType.d.ts +3 -2
  42. package/dist/types/pm-plugins/actions.d.ts +6 -0
  43. package/dist/types/pm-plugins/consts.d.ts +3 -0
  44. package/dist/types/pm-plugins/utils/redistribute-proportionally.d.ts +1 -0
  45. package/dist/types/ui/LayoutColumnMenu/InsertColumnDropdownItem.d.ts +10 -0
  46. package/dist/types/ui/LayoutColumnMenu/keys.d.ts +2 -0
  47. package/dist/types/ui/LayoutColumnMenu/layoutColumnSelection.d.ts +7 -0
  48. package/dist/types/ui/LayoutColumnMenu/useCurrentLayoutColumn.d.ts +5 -0
  49. package/dist/types-ts4.5/layoutPluginType.d.ts +3 -2
  50. package/dist/types-ts4.5/pm-plugins/actions.d.ts +6 -0
  51. package/dist/types-ts4.5/pm-plugins/consts.d.ts +3 -0
  52. package/dist/types-ts4.5/pm-plugins/utils/redistribute-proportionally.d.ts +1 -0
  53. package/dist/types-ts4.5/ui/LayoutColumnMenu/InsertColumnDropdownItem.d.ts +10 -0
  54. package/dist/types-ts4.5/ui/LayoutColumnMenu/keys.d.ts +2 -0
  55. package/dist/types-ts4.5/ui/LayoutColumnMenu/layoutColumnSelection.d.ts +7 -0
  56. package/dist/types-ts4.5/ui/LayoutColumnMenu/useCurrentLayoutColumn.d.ts +5 -0
  57. package/package.json +5 -5
  58. package/dist/cjs/ui/LayoutColumnMenu/useCurrentLayoutColumnValign.js +0 -22
  59. package/dist/es2019/ui/LayoutColumnMenu/useCurrentLayoutColumnValign.js +0 -14
  60. package/dist/esm/ui/LayoutColumnMenu/useCurrentLayoutColumnValign.js +0 -16
  61. package/dist/types/ui/LayoutColumnMenu/useCurrentLayoutColumnValign.d.ts +0 -11
  62. package/dist/types-ts4.5/ui/LayoutColumnMenu/useCurrentLayoutColumnValign.d.ts +0 -11
@@ -0,0 +1,10 @@
1
+ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
2
+ import { getLayoutColumnAtSelection, getLayoutSectionAtSelection } from './layoutColumnSelection';
3
+ export const useCurrentLayoutColumn = api => useSharedPluginStateWithSelector(api, ['selection'], states => {
4
+ var _states$selectionStat;
5
+ return getLayoutColumnAtSelection((_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection);
6
+ });
7
+ export const useCurrentLayoutSection = api => useSharedPluginStateWithSelector(api, ['selection'], states => {
8
+ var _states$selectionStat2;
9
+ return getLayoutSectionAtSelection((_states$selectionStat2 = states.selectionState) === null || _states$selectionStat2 === void 0 ? void 0 : _states$selectionStat2.selection);
10
+ });
@@ -10,7 +10,7 @@ import { findParentNode } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { fg } from '@atlaskit/platform-feature-flags';
11
11
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
12
12
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
13
- import { createDefaultLayoutSection, createMultiColumnLayoutSection, insertLayoutColumnsWithAnalytics, setLayoutColumnValign, toggleLayoutColumnMenu } from './pm-plugins/actions';
13
+ import { createDefaultLayoutSection, createMultiColumnLayoutSection, insertLayoutColumn as _insertLayoutColumn, insertLayoutColumnsWithAnalytics, setLayoutColumnValign, toggleLayoutColumnMenu } from './pm-plugins/actions';
14
14
  import { default as createLayoutPlugin } from './pm-plugins/main';
15
15
  import { pluginKey } from './pm-plugins/plugin-key';
16
16
  import { default as createLayoutResizingPlugin } from './pm-plugins/resizing';
@@ -302,6 +302,10 @@ export var layoutPlugin = function layoutPlugin(_ref) {
302
302
  return pluginKey.getState(editorState);
303
303
  },
304
304
  commands: {
305
+ insertLayoutColumn: function insertLayoutColumn(side) {
306
+ var _api$analytics4;
307
+ return _insertLayoutColumn(side, api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions);
308
+ },
305
309
  setLayoutColumnValign: setLayoutColumnValign,
306
310
  toggleLayoutColumnMenu: toggleLayoutColumnMenu
307
311
  }
@@ -10,8 +10,9 @@ import { safeInsert } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { fg } from '@atlaskit/platform-feature-flags';
11
11
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
12
12
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
13
- import { EVEN_DISTRIBUTED_COL_WIDTHS } from './consts';
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 { redistributeProportionally } from './utils/redistribute-proportionally';
15
16
  export var ONE_COL_LAYOUTS = ['single'];
16
17
  export var TWO_COL_LAYOUTS = ['two_equal', 'two_left_sidebar', 'two_right_sidebar'];
17
18
  export var THREE_COL_LAYOUTS = ['three_equal', 'three_with_sidebars'];
@@ -540,9 +541,102 @@ var formatLayoutName = function formatLayoutName(layout) {
540
541
  return LAYOUT_TYPE.THREE_WITH_SIDEBARS;
541
542
  }
542
543
  };
544
+ /**
545
+ * Returns the active maximum layout column count for the current advanced layouts experiment state.
546
+ */
547
+ export function getEffectiveMaxLayoutColumns() {
548
+ return editorExperiment('advanced_layouts', true) ? MAX_LAYOUT_COLUMNS : MAX_STANDARD_LAYOUT_COLUMNS;
549
+ }
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
+ var insertLayoutColumnAt = function insertLayoutColumnAt(side, editorAnalyticsAPI) {
576
+ return function (_ref5) {
577
+ var tr = _ref5.tr;
578
+ if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
579
+ return null;
580
+ }
581
+ var selectedColumn = getSelectedLayoutColumnInSection(tr);
582
+ if (!selectedColumn) {
583
+ return null;
584
+ }
585
+ var layoutSectionNode = selectedColumn.layoutSectionNode,
586
+ layoutSectionPos = selectedColumn.layoutSectionPos,
587
+ selectedColumnIndex = selectedColumn.selectedColumnIndex;
588
+ if (layoutSectionNode.childCount >= getEffectiveMaxLayoutColumns()) {
589
+ return null;
590
+ }
591
+ var insertIndex = side === 'left' ? selectedColumnIndex : selectedColumnIndex + 1;
592
+ var existingWidths = mapChildren(layoutSectionNode, function (column) {
593
+ return column.attrs.width;
594
+ });
595
+ var redistributedWidths = redistributeProportionally(existingWidths, insertIndex, getEffectiveMaxLayoutColumns(), MIN_LAYOUT_COLUMN_WIDTH_PERCENT);
596
+ if (redistributedWidths === existingWidths) {
597
+ return null;
598
+ }
599
+ var layoutColumn = tr.doc.type.schema.nodes.layoutColumn;
600
+ var newColumn = layoutColumn.createAndFill({
601
+ width: redistributedWidths[insertIndex]
602
+ });
603
+ if (!newColumn) {
604
+ return null;
605
+ }
606
+ var updatedColumns = [];
607
+ layoutSectionNode.forEach(function (column, _offset, index) {
608
+ if (index === insertIndex) {
609
+ updatedColumns.push(newColumn);
610
+ }
611
+ updatedColumns.push(column);
612
+ });
613
+ if (insertIndex === layoutSectionNode.childCount) {
614
+ updatedColumns.push(newColumn);
615
+ }
616
+ var updatedLayoutSectionNode = layoutSectionNode.copy(Fragment.fromArray(updatedColumns));
617
+ tr.replaceWith(layoutSectionPos + 1, layoutSectionPos + layoutSectionNode.nodeSize - 1, columnWidth(updatedLayoutSectionNode, tr.doc.type.schema, redistributedWidths));
618
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent({
619
+ action: ACTION.INSERTED,
620
+ actionSubject: ACTION_SUBJECT.DOCUMENT,
621
+ actionSubjectId: ACTION_SUBJECT_ID.LAYOUT_COLUMN,
622
+ attributes: {
623
+ columnCount: redistributedWidths.length,
624
+ inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
625
+ selectedIndex: selectedColumnIndex,
626
+ side: side
627
+ },
628
+ eventType: EVENT_TYPE.TRACK
629
+ })(tr);
630
+ tr.setMeta('scrollIntoView', false);
631
+ return tr;
632
+ };
633
+ };
634
+ export var insertLayoutColumn = function insertLayoutColumn(side, editorAnalyticsAPI) {
635
+ return insertLayoutColumnAt(side, editorAnalyticsAPI);
636
+ };
543
637
  export var setLayoutColumnValign = function setLayoutColumnValign(valign) {
544
- return function (_ref4) {
545
- var tr = _ref4.tr;
638
+ return function (_ref6) {
639
+ var tr = _ref6.tr;
546
640
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
547
641
  return null;
548
642
  }
@@ -564,10 +658,10 @@ export var setLayoutColumnValign = function setLayoutColumnValign(valign) {
564
658
  return tr;
565
659
  };
566
660
  };
567
- export var toggleLayoutColumnMenu = function toggleLayoutColumnMenu(_ref5) {
568
- var isOpen = _ref5.isOpen;
569
- return function (_ref6) {
570
- var tr = _ref6.tr;
661
+ export var toggleLayoutColumnMenu = function toggleLayoutColumnMenu(_ref7) {
662
+ var isOpen = _ref7.isOpen;
663
+ return function (_ref8) {
664
+ var tr = _ref8.tr;
571
665
  tr.setMeta('toggleLayoutColumnMenu', {
572
666
  isOpen: isOpen
573
667
  });
@@ -4,14 +4,13 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
4
4
  import { bind } from 'bind-event-listener';
5
5
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
6
6
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
7
+ import { MIN_LAYOUT_COLUMN_WIDTH_PERCENT } from './consts';
8
+
7
9
  // Class names for the column resize divider widget — must stay in sync with layout.ts in editor-core
8
10
  var layoutColumnDividerClassName = 'layout-column-divider';
9
11
  var layoutColumnDividerRailClassName = 'layout-column-divider-rail';
10
12
  var layoutColumnDividerThumbClassName = 'layout-column-divider-thumb';
11
13
 
12
- // Minimum column width percentage to prevent columns from collapsing
13
- var MIN_COLUMN_WIDTH_PERCENT = 5;
14
-
15
14
  // Module-level drag state so it survives widget DOM recreation during transactions.
16
15
  var dragState = null;
17
16
 
@@ -72,12 +71,12 @@ var calcDragWidths = function calcDragWidths(clientX) {
72
71
  var deltaPercent = deltaX / columnsWidth * 100;
73
72
  var leftWidth = dragState.startLeftWidth + deltaPercent;
74
73
  var rightWidth = dragState.startRightWidth - deltaPercent;
75
- if (leftWidth < MIN_COLUMN_WIDTH_PERCENT) {
76
- leftWidth = MIN_COLUMN_WIDTH_PERCENT;
77
- rightWidth = combinedWidth - MIN_COLUMN_WIDTH_PERCENT;
78
- } else if (rightWidth < MIN_COLUMN_WIDTH_PERCENT) {
79
- rightWidth = MIN_COLUMN_WIDTH_PERCENT;
80
- leftWidth = combinedWidth - MIN_COLUMN_WIDTH_PERCENT;
74
+ if (leftWidth < MIN_LAYOUT_COLUMN_WIDTH_PERCENT) {
75
+ leftWidth = MIN_LAYOUT_COLUMN_WIDTH_PERCENT;
76
+ rightWidth = combinedWidth - MIN_LAYOUT_COLUMN_WIDTH_PERCENT;
77
+ } else if (rightWidth < MIN_LAYOUT_COLUMN_WIDTH_PERCENT) {
78
+ rightWidth = MIN_LAYOUT_COLUMN_WIDTH_PERCENT;
79
+ leftWidth = combinedWidth - MIN_LAYOUT_COLUMN_WIDTH_PERCENT;
81
80
  }
82
81
  return {
83
82
  leftWidth: leftWidth,
@@ -1,3 +1,6 @@
1
+ export var MAX_STANDARD_LAYOUT_COLUMNS = 3;
2
+ export var MAX_LAYOUT_COLUMNS = 5;
3
+ export var MIN_LAYOUT_COLUMN_WIDTH_PERCENT = 5;
1
4
  export var EVEN_DISTRIBUTED_COL_WIDTHS = {
2
5
  1: 100,
3
6
  2: 50,
@@ -0,0 +1,104 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ var roundLayoutColumnWidth = function roundLayoutColumnWidth(width) {
3
+ return Number(width.toFixed(2));
4
+ };
5
+ var sumWidths = function sumWidths(widths) {
6
+ return widths.reduce(function (sum, width) {
7
+ return sum + width;
8
+ }, 0);
9
+ };
10
+ var isValidWidth = function isValidWidth(width) {
11
+ return Number.isFinite(width) && width > 0;
12
+ };
13
+ var normaliseWidthsTotal = function normaliseWidthsTotal(widths, totalWidth, minWidth) {
14
+ var roundedWidths = widths.map(roundLayoutColumnWidth);
15
+ var remainder = roundLayoutColumnWidth(totalWidth - sumWidths(roundedWidths));
16
+ if (remainder === 0 || roundedWidths.length === 0) {
17
+ return roundedWidths;
18
+ }
19
+ var adjustmentIndex = 0;
20
+ roundedWidths.forEach(function (width, index) {
21
+ if (width > roundedWidths[adjustmentIndex]) {
22
+ adjustmentIndex = index;
23
+ }
24
+ });
25
+ var adjustedWidth = roundLayoutColumnWidth(roundedWidths[adjustmentIndex] + remainder);
26
+ if (adjustedWidth < minWidth) {
27
+ return roundedWidths;
28
+ }
29
+ return roundedWidths.map(function (width, index) {
30
+ return index === adjustmentIndex ? adjustedWidth : width;
31
+ });
32
+ };
33
+ var redistributeWithMinimumWidth = function redistributeWithMinimumWidth(_ref) {
34
+ var minWidth = _ref.minWidth,
35
+ totalWidth = _ref.totalWidth,
36
+ weights = _ref.weights;
37
+ if (weights.length * minWidth > totalWidth) {
38
+ return;
39
+ }
40
+ var widths = Array(weights.length).fill(0);
41
+ var clampedIndexes = new Set();
42
+ var remainingWidth = totalWidth;
43
+ var remainingWeight = sumWidths(weights);
44
+ var _loop = function _loop() {
45
+ var remainingWidthForPass = remainingWidth;
46
+ var remainingWeightForPass = remainingWeight;
47
+ var indexesToClamp = [];
48
+ weights.forEach(function (weight, index) {
49
+ if (clampedIndexes.has(index)) {
50
+ return;
51
+ }
52
+ var proportionalWidth = remainingWeightForPass > 0 ? weight / remainingWeightForPass * remainingWidthForPass : 0;
53
+ if (proportionalWidth < minWidth) {
54
+ indexesToClamp.push(index);
55
+ }
56
+ });
57
+ if (indexesToClamp.length === 0) {
58
+ return 1; // break
59
+ }
60
+ indexesToClamp.forEach(function (index) {
61
+ widths[index] = minWidth;
62
+ clampedIndexes.add(index);
63
+ remainingWidth -= minWidth;
64
+ remainingWeight -= weights[index];
65
+ });
66
+ };
67
+ while (clampedIndexes.size < weights.length) {
68
+ if (_loop()) break;
69
+ }
70
+ weights.forEach(function (weight, index) {
71
+ if (!clampedIndexes.has(index)) {
72
+ widths[index] = remainingWeight > 0 ? weight / remainingWeight * remainingWidth : minWidth;
73
+ }
74
+ });
75
+ return widths;
76
+ };
77
+ export var redistributeProportionally = function redistributeProportionally(currentWidths, insertIndex, maxColumns, minWidth) {
78
+ if (currentWidths.length === 0 || !Number.isInteger(maxColumns) || maxColumns <= 0 || currentWidths.length >= maxColumns || insertIndex < 0 || insertIndex > currentWidths.length || !isValidWidth(minWidth) || currentWidths.some(function (width) {
79
+ return !isValidWidth(width);
80
+ })) {
81
+ return currentWidths;
82
+ }
83
+ var currentTotalWidth = sumWidths(currentWidths);
84
+ if (!isValidWidth(currentTotalWidth)) {
85
+ return currentWidths;
86
+ }
87
+ var targetTotalWidth = Math.round(currentTotalWidth) === 100 ? 100 : currentTotalWidth;
88
+ var newColumnWidth = Math.max(minWidth, roundLayoutColumnWidth(targetTotalWidth / (currentWidths.length + 1)));
89
+ var existingColumnsTotalWidth = targetTotalWidth - newColumnWidth;
90
+ if (existingColumnsTotalWidth < currentWidths.length * minWidth) {
91
+ return currentWidths;
92
+ }
93
+ var redistributedExistingWidths = redistributeWithMinimumWidth({
94
+ weights: currentWidths,
95
+ totalWidth: existingColumnsTotalWidth,
96
+ minWidth: minWidth
97
+ });
98
+ if (!redistributedExistingWidths) {
99
+ return currentWidths;
100
+ }
101
+ var nextWidths = _toConsumableArray(redistributedExistingWidths);
102
+ nextWidths.splice(insertIndex, 0, newColumnWidth);
103
+ return normaliseWidthsTotal(nextWidths, targetTotalWidth, minWidth);
104
+ };
@@ -0,0 +1,61 @@
1
+ import React, { useCallback } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import { layoutMessages } from '@atlaskit/editor-common/messages';
4
+ import { TableColumnAddLeftIcon, TableColumnAddRightIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
5
+ import { getEffectiveMaxLayoutColumns } from '../../pm-plugins/actions';
6
+ import { getLayoutSectionColumnCount } from './layoutColumnSelection';
7
+ import { useCurrentLayoutColumn, useCurrentLayoutSection } from './useCurrentLayoutColumn';
8
+ var INSERT_COLUMN_OPTIONS = {
9
+ left: {
10
+ Icon: TableColumnAddLeftIcon,
11
+ label: layoutMessages.insertColumnLeft,
12
+ side: 'left'
13
+ },
14
+ right: {
15
+ Icon: TableColumnAddRightIcon,
16
+ label: layoutMessages.insertColumnRight,
17
+ side: 'right'
18
+ }
19
+ };
20
+ export var InsertColumnDropdownItem = function InsertColumnDropdownItem(_ref) {
21
+ var api = _ref.api,
22
+ side = _ref.side;
23
+ var _useIntl = useIntl(),
24
+ formatMessage = _useIntl.formatMessage;
25
+ var _INSERT_COLUMN_OPTION = INSERT_COLUMN_OPTIONS[side],
26
+ Icon = _INSERT_COLUMN_OPTION.Icon,
27
+ label = _INSERT_COLUMN_OPTION.label;
28
+ var currentColumn = useCurrentLayoutColumn(api);
29
+ var currentLayoutSection = useCurrentLayoutSection(api);
30
+ var columnCount = getLayoutSectionColumnCount(currentLayoutSection);
31
+ var maxColumnCount = getEffectiveMaxLayoutColumns();
32
+ var canInsertColumn = currentColumn !== undefined && columnCount < maxColumnCount;
33
+ var onClick = useCallback(function () {
34
+ var _api$layout, _api$core;
35
+ var insertCommand = api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.insertLayoutColumn(side);
36
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (props) {
37
+ var _api$layout2;
38
+ var tr = insertCommand === null || insertCommand === void 0 ? void 0 : insertCommand(props);
39
+ if (!tr) {
40
+ return tr !== null && tr !== void 0 ? tr : null;
41
+ }
42
+ api === null || api === void 0 || (_api$layout2 = api.layout) === null || _api$layout2 === void 0 || _api$layout2.commands.toggleLayoutColumnMenu({
43
+ isOpen: false
44
+ })({
45
+ tr: tr
46
+ });
47
+ return tr;
48
+ });
49
+ }, [api, side]);
50
+ if (!canInsertColumn) {
51
+ return null;
52
+ }
53
+ return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
54
+ elemBefore: /*#__PURE__*/React.createElement(Icon, {
55
+ color: "currentColor",
56
+ label: "",
57
+ size: "small"
58
+ }),
59
+ onClick: onClick
60
+ }, formatMessage(label));
61
+ };
@@ -1,7 +1,8 @@
1
- import React, { useCallback } from 'react';
1
+ import React, { useCallback, useMemo } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
3
  import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
4
- import { useCurrentLayoutColumnValign } from './useCurrentLayoutColumnValign';
4
+ import { getLayoutColumnValign } from './layoutColumnSelection';
5
+ import { useCurrentLayoutColumn } from './useCurrentLayoutColumn';
5
6
  import { VERTICAL_ALIGN_ICONS } from './verticalAlignIcons';
6
7
  export var VerticalAlignDropdownItem = function VerticalAlignDropdownItem(_ref) {
7
8
  var api = _ref.api,
@@ -9,24 +10,21 @@ export var VerticalAlignDropdownItem = function VerticalAlignDropdownItem(_ref)
9
10
  value = _ref.value;
10
11
  var _useIntl = useIntl(),
11
12
  formatMessage = _useIntl.formatMessage;
12
- var _useCurrentLayoutColu = useCurrentLayoutColumnValign(api),
13
- currentValign = _useCurrentLayoutColu.currentValign,
14
- selectedColumn = _useCurrentLayoutColu.selectedColumn;
13
+ var currentColumn = useCurrentLayoutColumn(api);
14
+ var currentValign = useMemo(function () {
15
+ return getLayoutColumnValign(currentColumn);
16
+ }, [currentColumn]);
15
17
  var Icon = VERTICAL_ALIGN_ICONS[value];
16
18
  var onClick = useCallback(function () {
17
19
  var _api$core, _api$layout;
18
20
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.setLayoutColumnValign(value));
19
21
  }, [api, value]);
20
- if (!selectedColumn) {
21
- return null;
22
- }
23
22
  return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
24
23
  elemBefore: /*#__PURE__*/React.createElement(Icon, {
25
24
  label: "",
26
25
  size: "small"
27
26
  }),
28
27
  isSelected: currentValign === value,
29
- onClick: onClick,
30
- testId: "layout-column-align-".concat(value)
28
+ onClick: onClick
31
29
  }, formatMessage(label));
32
30
  };
@@ -1,19 +1,21 @@
1
- import React from 'react';
1
+ 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 { useCurrentLayoutColumnValign } from './useCurrentLayoutColumnValign';
5
+ import { getLayoutColumnValign } from './layoutColumnSelection';
6
+ import { useCurrentLayoutColumn } from './useCurrentLayoutColumn';
6
7
  import { VERTICAL_ALIGN_ICONS } from './verticalAlignIcons';
7
8
  export var VerticalAlignNestedMenu = function VerticalAlignNestedMenu(_ref) {
8
9
  var api = _ref.api,
9
10
  children = _ref.children;
10
11
  var _useIntl = useIntl(),
11
12
  formatMessage = _useIntl.formatMessage;
12
- var _useCurrentLayoutColu = useCurrentLayoutColumnValign(api),
13
- currentValign = _useCurrentLayoutColu.currentValign,
14
- selectedColumn = _useCurrentLayoutColu.selectedColumn;
13
+ var currentColumn = useCurrentLayoutColumn(api);
14
+ var currentValign = useMemo(function () {
15
+ return getLayoutColumnValign(currentColumn);
16
+ }, [currentColumn]);
15
17
  var TriggerIcon = currentValign ? VERTICAL_ALIGN_ICONS[currentValign] : LayoutIcon;
16
- if (!selectedColumn) {
18
+ if (!currentColumn) {
17
19
  return null;
18
20
  }
19
21
  return /*#__PURE__*/React.createElement(ToolbarNestedDropdownMenu, {
@@ -25,8 +27,6 @@ export var VerticalAlignNestedMenu = function VerticalAlignNestedMenu(_ref) {
25
27
  label: "",
26
28
  size: "small"
27
29
  }),
28
- text: formatMessage(layoutMessages.alignColumn),
29
- testId: "layout-column-align-menu",
30
- dropdownTestId: "layout-column-align-dropdown"
30
+ text: formatMessage(layoutMessages.alignColumn)
31
31
  }, children);
32
32
  };
@@ -6,7 +6,8 @@ import { layoutMessages } from '@atlaskit/editor-common/messages';
6
6
  import { ToolbarDropdownItemSection } from '@atlaskit/editor-toolbar';
7
7
  import { ToolbarMenuContainer } from '@atlaskit/editor-toolbar/toolbar-menu-container';
8
8
  import { createDistributeColumnsDropdownItem } from './DistributeColumnsDropdownItem';
9
- import { DISTRIBUTE_COLUMNS_MENU_ITEM, LAYOUT_COLUMN_MENU, LAYOUT_COLUMN_MENU_RANK, LAYOUT_COLUMN_MENU_SECTION, LAYOUT_COLUMN_MENU_SECTION_RANK, LAYOUT_COLUMN_VERTICAL_ALIGN_MENU_SECTION, VERTICAL_ALIGN_BOTTOM_MENU_ITEM, VERTICAL_ALIGN_MENU, VERTICAL_ALIGN_MENU_RANK, VERTICAL_ALIGN_MENU_SECTION_RANK, VERTICAL_ALIGN_MIDDLE_MENU_ITEM, VERTICAL_ALIGN_TOP_MENU_ITEM } from './keys';
9
+ import { InsertColumnDropdownItem } from './InsertColumnDropdownItem';
10
+ import { DISTRIBUTE_COLUMNS_MENU_ITEM, INSERT_COLUMN_LEFT_MENU_ITEM, INSERT_COLUMN_RIGHT_MENU_ITEM, LAYOUT_COLUMN_MENU, LAYOUT_COLUMN_MENU_RANK, LAYOUT_COLUMN_MENU_SECTION, LAYOUT_COLUMN_MENU_SECTION_RANK, LAYOUT_COLUMN_VERTICAL_ALIGN_MENU_SECTION, VERTICAL_ALIGN_BOTTOM_MENU_ITEM, VERTICAL_ALIGN_MENU, VERTICAL_ALIGN_MENU_RANK, VERTICAL_ALIGN_MENU_SECTION_RANK, VERTICAL_ALIGN_MIDDLE_MENU_ITEM, VERTICAL_ALIGN_TOP_MENU_ITEM } from './keys';
10
11
  import { VerticalAlignDropdownItem } from './VerticalAlignDropdownItem';
11
12
  import { VerticalAlignNestedMenu } from './VerticalAlignNestedMenu';
12
13
  export var LAYOUT_COLUMN_MENU_FALLBACKS = {
@@ -23,6 +24,26 @@ export var getLayoutColumnMenuComponents = function getLayoutColumnMenuComponent
23
24
  parents: [_objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU), {}, {
24
25
  rank: LAYOUT_COLUMN_MENU_RANK[LAYOUT_COLUMN_MENU_SECTION.key]
25
26
  })]
27
+ }), _objectSpread(_objectSpread({}, INSERT_COLUMN_LEFT_MENU_ITEM), {}, {
28
+ component: function component() {
29
+ return /*#__PURE__*/React.createElement(InsertColumnDropdownItem, {
30
+ api: api,
31
+ side: "left"
32
+ });
33
+ },
34
+ parents: [_objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU_SECTION), {}, {
35
+ rank: LAYOUT_COLUMN_MENU_SECTION_RANK[INSERT_COLUMN_LEFT_MENU_ITEM.key]
36
+ })]
37
+ }), _objectSpread(_objectSpread({}, INSERT_COLUMN_RIGHT_MENU_ITEM), {}, {
38
+ component: function component() {
39
+ return /*#__PURE__*/React.createElement(InsertColumnDropdownItem, {
40
+ api: api,
41
+ side: "right"
42
+ });
43
+ },
44
+ parents: [_objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU_SECTION), {}, {
45
+ rank: LAYOUT_COLUMN_MENU_SECTION_RANK[INSERT_COLUMN_RIGHT_MENU_ITEM.key]
46
+ })]
26
47
  }), _objectSpread(_objectSpread({}, DISTRIBUTE_COLUMNS_MENU_ITEM), {}, {
27
48
  component: createDistributeColumnsDropdownItem(api),
28
49
  parents: [_objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU_SECTION), {}, {
@@ -1,25 +1,34 @@
1
- import React, { useCallback, useContext, useEffect, useRef } from 'react';
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import React, { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
2
3
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
4
+ import { DRAG_HANDLE_SELECTOR } from '@atlaskit/editor-common/styles';
3
5
  import { EditorToolbarProvider } from '@atlaskit/editor-common/toolbar';
4
6
  import { Popup } from '@atlaskit/editor-common/ui';
5
7
  import { OutsideClickTargetRefContext, withReactEditorViewOuterListeners } from '@atlaskit/editor-common/ui-react';
6
- import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
7
- import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
8
+ import { akEditorFloatingOverlapPanelZIndex } from '@atlaskit/editor-shared-styles';
8
9
  import { ToolbarDropdownMenuProvider } from '@atlaskit/editor-toolbar';
9
10
  import { SurfaceRenderer } from '@atlaskit/editor-ui-control-model';
10
11
  import { LAYOUT_COLUMN_MENU_FALLBACKS } from './components';
11
12
  import { LAYOUT_COLUMN_MENU } from './keys';
13
+ import { getLayoutColumnAtSelection } from './layoutColumnSelection';
12
14
  var PopupWithListeners = withReactEditorViewOuterListeners(Popup);
13
- var LAYOUT_COLUMN_MENU_POPUP_OFFSET = [0, 10];
14
- var getLayoutColumnMenuTarget = function getLayoutColumnMenuTarget(editorView, selectionAnchorPos) {
15
- if (selectionAnchorPos === undefined) {
15
+ var FALLBACK_MENU_HEIGHT = 300;
16
+ var LAYOUT_COLUMN_MENU_POPUP_OFFSET = [0, 4];
17
+
18
+ /**
19
+ * Returns the drag handle button for the selected layout column.
20
+ */
21
+ var getLayoutColumnMenuTarget = function getLayoutColumnMenuTarget(editorView, selection) {
22
+ var _columnDomRef$parentE;
23
+ if (!getLayoutColumnAtSelection(selection) || (selection === null || selection === void 0 ? void 0 : selection.from) === undefined) {
16
24
  return null;
17
25
  }
18
- var selectionNode = editorView.state.doc.nodeAt(selectionAnchorPos);
19
- if ((selectionNode === null || selectionNode === void 0 ? void 0 : selectionNode.type.name) !== 'layoutColumn') {
26
+ var columnDomRef = editorView.nodeDOM(selection.from);
27
+ if (!(columnDomRef instanceof HTMLElement)) {
20
28
  return null;
21
29
  }
22
- return findDomRefAtPos(selectionAnchorPos, editorView.domAtPos.bind(editorView));
30
+ var dragHandleContainer = (_columnDomRef$parentE = columnDomRef.parentElement) === null || _columnDomRef$parentE === void 0 ? void 0 : _columnDomRef$parentE.querySelector(':scope > [data-blocks-drag-handle-container]');
31
+ return dragHandleContainer === null || dragHandleContainer === void 0 ? void 0 : dragHandleContainer.querySelector(DRAG_HANDLE_SELECTOR);
23
32
  };
24
33
  export var LayoutColumnMenu = /*#__PURE__*/React.memo(function LayoutColumnMenu(_ref) {
25
34
  var _api$uiControlRegistr, _api$uiControlRegistr2;
@@ -39,6 +48,18 @@ export var LayoutColumnMenu = /*#__PURE__*/React.memo(function LayoutColumnMenu(
39
48
  selection = _useSharedPluginState.selection;
40
49
  var setOutsideClickTargetRef = useContext(OutsideClickTargetRefContext);
41
50
  var menuRef = useRef(null);
51
+ var popupRef = useRef(undefined);
52
+ var _React$useState = React.useState(FALLBACK_MENU_HEIGHT),
53
+ _React$useState2 = _slicedToArray(_React$useState, 2),
54
+ menuHeight = _React$useState2[0],
55
+ setMenuHeight = _React$useState2[1];
56
+ useLayoutEffect(function () {
57
+ var _popupRef$current;
58
+ if (!isLayoutColumnMenuOpen) {
59
+ return;
60
+ }
61
+ setMenuHeight(((_popupRef$current = popupRef.current) === null || _popupRef$current === void 0 ? void 0 : _popupRef$current.clientHeight) || FALLBACK_MENU_HEIGHT);
62
+ }, [isLayoutColumnMenuOpen]);
42
63
  var closeLayoutColumnMenu = useCallback(function () {
43
64
  var _api$core, _api$layout;
44
65
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.toggleLayoutColumnMenu({
@@ -63,9 +84,14 @@ export var LayoutColumnMenu = /*#__PURE__*/React.memo(function LayoutColumnMenu(
63
84
  var handleMenuRef = useCallback(function (el) {
64
85
  setOutsideClickTargetRef === null || setOutsideClickTargetRef === void 0 || setOutsideClickTargetRef(el);
65
86
  menuRef.current = el;
87
+ if (el) {
88
+ popupRef.current = el;
89
+ }
66
90
  }, [setOutsideClickTargetRef]);
67
91
  var components = (_api$uiControlRegistr = api === null || api === 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 : [];
68
- var target = isLayoutColumnMenuOpen ? getLayoutColumnMenuTarget(editorView, selection === null || selection === void 0 ? void 0 : selection.from) : null;
92
+ var target = useMemo(function () {
93
+ return isLayoutColumnMenuOpen ? getLayoutColumnMenuTarget(editorView, selection) : null;
94
+ }, [editorView, isLayoutColumnMenuOpen, selection]);
69
95
  var hasValidTarget = target instanceof HTMLElement;
70
96
  useEffect(function () {
71
97
  if (isLayoutColumnMenuOpen && (!hasValidTarget || components.length === 0)) {
@@ -80,10 +106,11 @@ export var LayoutColumnMenu = /*#__PURE__*/React.memo(function LayoutColumnMenu(
80
106
  mountTo: mountTo,
81
107
  boundariesElement: boundariesElement,
82
108
  scrollableElement: scrollableElement,
83
- zIndex: akEditorFloatingPanelZIndex,
109
+ zIndex: akEditorFloatingOverlapPanelZIndex,
84
110
  alignX: "center",
85
- alignY: "top",
86
- forcePlacement: true,
111
+ fitHeight: menuHeight,
112
+ preventOverflow: true,
113
+ stick: true,
87
114
  offset: LAYOUT_COLUMN_MENU_POPUP_OFFSET,
88
115
  handleClickOutside: handleClickOutside,
89
116
  handleEscapeKeydown: closeLayoutColumnMenu
@@ -27,6 +27,14 @@ export var VERTICAL_ALIGN_MENU = {
27
27
 
28
28
  // --- Items ---
29
29
 
30
+ export var INSERT_COLUMN_LEFT_MENU_ITEM = {
31
+ type: 'menu-item',
32
+ key: 'layout-column-menu-insert-left-item'
33
+ };
34
+ export var INSERT_COLUMN_RIGHT_MENU_ITEM = {
35
+ type: 'menu-item',
36
+ key: 'layout-column-menu-insert-right-item'
37
+ };
30
38
  export var DISTRIBUTE_COLUMNS_MENU_ITEM = {
31
39
  type: 'menu-item',
32
40
  key: 'layout-column-menu-distribute-columns-item'
@@ -46,6 +54,6 @@ export var VERTICAL_ALIGN_BOTTOM_MENU_ITEM = {
46
54
 
47
55
  // --- Item ranks within sections ---
48
56
 
49
- export var LAYOUT_COLUMN_MENU_SECTION_RANK = _defineProperty(_defineProperty({}, DISTRIBUTE_COLUMNS_MENU_ITEM.key, 100), VERTICAL_ALIGN_MENU.key, 200);
57
+ export var LAYOUT_COLUMN_MENU_SECTION_RANK = _defineProperty(_defineProperty(_defineProperty(_defineProperty({}, VERTICAL_ALIGN_MENU.key, 100), INSERT_COLUMN_LEFT_MENU_ITEM.key, 200), INSERT_COLUMN_RIGHT_MENU_ITEM.key, 300), DISTRIBUTE_COLUMNS_MENU_ITEM.key, 400);
50
58
  export var VERTICAL_ALIGN_MENU_RANK = _defineProperty({}, LAYOUT_COLUMN_VERTICAL_ALIGN_MENU_SECTION.key, 100);
51
59
  export var VERTICAL_ALIGN_MENU_SECTION_RANK = _defineProperty(_defineProperty(_defineProperty({}, VERTICAL_ALIGN_TOP_MENU_ITEM.key, 100), VERTICAL_ALIGN_MIDDLE_MENU_ITEM.key, 200), VERTICAL_ALIGN_BOTTOM_MENU_ITEM.key, 300);
@@ -0,0 +1,15 @@
1
+ import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
2
+ export var getLayoutColumnAtSelection = function getLayoutColumnAtSelection(selection) {
3
+ return selection instanceof NodeSelection && selection.node.type.name === 'layoutColumn' ? selection.node : undefined;
4
+ };
5
+ export var getLayoutSectionAtSelection = function getLayoutSectionAtSelection(selection) {
6
+ var selectedColumn = getLayoutColumnAtSelection(selection);
7
+ var parent = selectedColumn ? selection === null || selection === void 0 ? void 0 : selection.$from.parent : undefined;
8
+ return (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'layoutSection' ? parent : undefined;
9
+ };
10
+ export var getLayoutSectionColumnCount = function getLayoutSectionColumnCount(layoutSection) {
11
+ return (layoutSection === null || layoutSection === void 0 ? void 0 : layoutSection.type.name) === 'layoutSection' ? layoutSection.childCount : 0;
12
+ };
13
+ export var getLayoutColumnValign = function getLayoutColumnValign(layoutColumn) {
14
+ return layoutColumn === null || layoutColumn === void 0 ? void 0 : layoutColumn.attrs.valign;
15
+ };
@@ -0,0 +1,14 @@
1
+ import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
2
+ import { getLayoutColumnAtSelection, getLayoutSectionAtSelection } from './layoutColumnSelection';
3
+ export var useCurrentLayoutColumn = function useCurrentLayoutColumn(api) {
4
+ return useSharedPluginStateWithSelector(api, ['selection'], function (states) {
5
+ var _states$selectionStat;
6
+ return getLayoutColumnAtSelection((_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection);
7
+ });
8
+ };
9
+ export var useCurrentLayoutSection = function useCurrentLayoutSection(api) {
10
+ return useSharedPluginStateWithSelector(api, ['selection'], function (states) {
11
+ var _states$selectionStat2;
12
+ return getLayoutSectionAtSelection((_states$selectionStat2 = states.selectionState) === null || _states$selectionStat2 === void 0 ? void 0 : _states$selectionStat2.selection);
13
+ });
14
+ };