@atlaskit/editor-plugin-layout 13.2.2 → 13.2.3

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @atlaskit/editor-plugin-layout
2
2
 
3
+ ## 13.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`6827a39105ea0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6827a39105ea0) -
8
+ EDITOR-7736: Fix left gap cursor before first image/expand in vertically-aligned layout column
9
+ - Updated dependencies
10
+
3
11
  ## 13.2.2
4
12
 
5
13
  ### Patch Changes
@@ -16,7 +16,6 @@ var _state = require("@atlaskit/editor-prosemirror/state");
16
16
  var _utils = require("@atlaskit/editor-prosemirror/utils");
17
17
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
18
18
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
19
- var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
20
19
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
21
20
  var _actions = require("./pm-plugins/actions");
22
21
  var _keymap = _interopRequireDefault(require("./pm-plugins/keymap"));
@@ -82,7 +81,7 @@ var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
82
81
  }
83
82
  }]);
84
83
  }
85
- if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true)) {
84
+ if ((0, _expValEquals.expValEquals)('platform_editor_layout_column_menu', 'isEnabled', true)) {
86
85
  var _api$uiControlRegistr;
87
86
  api === null || api === void 0 || (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 || _api$uiControlRegistr.actions.register((0, _components.getLayoutColumnMenuComponents)({
88
87
  api: api
@@ -107,7 +106,7 @@ var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
107
106
  return (0, _main.default)(options, api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions);
108
107
  }
109
108
  }];
110
- if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true)) {
109
+ if ((0, _expValEquals.expValEquals)('platform_editor_layout_column_menu', 'isEnabled', true)) {
111
110
  plugins.push({
112
111
  name: 'layoutKeymap',
113
112
  plugin: function plugin() {
@@ -372,7 +371,7 @@ var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
372
371
  popupsMountPoint = _ref5.popupsMountPoint,
373
372
  popupsBoundariesElement = _ref5.popupsBoundariesElement,
374
373
  popupsScrollableElement = _ref5.popupsScrollableElement;
375
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, (0, _experiments.editorExperiment)('advanced_layouts', true) ? /*#__PURE__*/_react.default.createElement(_globalStyles.GlobalStylesWrapper, null) : null, (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true) && editorView ? /*#__PURE__*/_react.default.createElement(_LayoutColumnMenu.LayoutColumnMenu, {
374
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, (0, _experiments.editorExperiment)('advanced_layouts', true) ? /*#__PURE__*/_react.default.createElement(_globalStyles.GlobalStylesWrapper, null) : null, (0, _expValEquals.expValEquals)('platform_editor_layout_column_menu', 'isEnabled', true) && editorView ? /*#__PURE__*/_react.default.createElement(_LayoutColumnMenu.LayoutColumnMenu, {
376
375
  api: api,
377
376
  editorView: editorView,
378
377
  mountTo: popupsMountPoint,
@@ -17,7 +17,7 @@ var _state = require("@atlaskit/editor-prosemirror/state");
17
17
  var _transform = require("@atlaskit/editor-prosemirror/transform");
18
18
  var _utils2 = require("@atlaskit/editor-prosemirror/utils");
19
19
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
20
- var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
20
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
21
21
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
22
22
  var _consts = require("./consts");
23
23
  var _pluginKey = require("./plugin-key");
@@ -596,7 +596,7 @@ var insertLayoutColumnAt = function insertLayoutColumnAt(side, editorAnalyticsAP
596
596
  var inputMethod = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _analytics.INPUT_METHOD.LAYOUT_COLUMN_MENU;
597
597
  return function (_ref5) {
598
598
  var tr = _ref5.tr;
599
- if (!(0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true)) {
599
+ if (!(0, _expValEquals.expValEquals)('platform_editor_layout_column_menu', 'isEnabled', true)) {
600
600
  return null;
601
601
  }
602
602
  var selectedLayoutColumnsResult = (0, _layoutColumnSelection.getLayoutColumnsFromContentSelection)(tr.selection);
@@ -692,7 +692,7 @@ var setLayoutColumnValign = exports.setLayoutColumnValign = function setLayoutCo
692
692
  inputMethod = _ref8$inputMethod === void 0 ? _analytics.INPUT_METHOD.LAYOUT_COLUMN_MENU : _ref8$inputMethod;
693
693
  return function (_ref9) {
694
694
  var tr = _ref9.tr;
695
- if (!(0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true)) {
695
+ if (!(0, _expValEquals.expValEquals)('platform_editor_layout_column_menu', 'isEnabled', true)) {
696
696
  return null;
697
697
  }
698
698
  var selectedLayoutColumnsResult = (0, _layoutColumnSelection.getSelectedLayoutColumnsFromSelection)(tr.selection);
@@ -749,7 +749,7 @@ var distributeLayoutColumns = exports.distributeLayoutColumns = function distrib
749
749
  target = _ref10$target === void 0 ? 'selectedColumns' : _ref10$target;
750
750
  return function (_ref11) {
751
751
  var tr = _ref11.tr;
752
- if (!(0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true)) {
752
+ if (!(0, _expValEquals.expValEquals)('platform_editor_layout_column_menu', 'isEnabled', true)) {
753
753
  return null;
754
754
  }
755
755
  var selectedLayoutColumnsResult = target === 'allColumns' ? (0, _layoutColumnSelection.getAllLayoutColumnsFromSelection)(tr.selection) : (0, _layoutColumnSelection.getSelectedLayoutColumnsFromSelection)(tr.selection);
@@ -860,7 +860,7 @@ var deleteLayoutColumn = exports.deleteLayoutColumn = function deleteLayoutColum
860
860
  return function (_ref18) {
861
861
  var _api$blockControls4;
862
862
  var tr = _ref18.tr;
863
- if (!(0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true)) {
863
+ if (!(0, _expValEquals.expValEquals)('platform_editor_layout_column_menu', 'isEnabled', true)) {
864
864
  return null;
865
865
  }
866
866
  var selectedLayoutColumnsResult = (0, _layoutColumnSelection.getLayoutColumnsFromContentSelection)(tr.selection);
@@ -16,6 +16,7 @@ var _model = require("@atlaskit/editor-prosemirror/model");
16
16
  var _state = require("@atlaskit/editor-prosemirror/state");
17
17
  var _utils2 = require("@atlaskit/editor-prosemirror/utils");
18
18
  var _view2 = require("@atlaskit/editor-prosemirror/view");
19
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
19
20
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
20
21
  var _actions = require("./actions");
21
22
  var _columnResizeDivider = require("./column-resize-divider");
@@ -27,6 +28,33 @@ var _layoutColumnSelection = require("./utils/layout-column-selection");
27
28
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
28
29
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
29
30
  var DEFAULT_LAYOUT = exports.DEFAULT_LAYOUT = 'two_equal';
31
+
32
+ /**
33
+ * Shared blank-space gap cursor placement, used by both `handleClick` and `handleClickOn`
34
+ * (the latter catches clicks on atomic node views that stop propagation before `handleClick`).
35
+ * Returns `true` when it placed a selection and consumed the click, else `false`.
36
+ */
37
+ var applyBlankSpaceGapCursor = function applyBlankSpaceGapCursor(view, event) {
38
+ if (!(0, _expValEquals.expValEquals)('platform_editor_layout_column_menu', 'isEnabled', true) || !(0, _experiments.editorExperiment)('advanced_layouts', true)) {
39
+ return false;
40
+ }
41
+ var gapTarget = (0, _utils3.getGapCursorTargetForBlankSpaceClick)(view, event);
42
+ if (gapTarget === undefined) {
43
+ return false;
44
+ }
45
+ var $pos = view.state.doc.resolve(gapTarget.pos);
46
+ // A paragraph child takes a TextSelection (caret at the edge) rather than a gap cursor.
47
+ var isParagraphTarget = (0, _utils3.isParagraphBlankSpaceTarget)(view, gapTarget);
48
+ var nextSelection = isParagraphTarget ? _state.TextSelection.near($pos, gapTarget.side === 'left' ? 1 : -1) : new _selection.GapCursorSelection($pos, gapTarget.side === 'left' ? _selection.Side.LEFT : _selection.Side.RIGHT);
49
+ // Idempotency guard: `mousedown` already placed this selection, but the browser still
50
+ // fires `mouseup`, so `handleClick`/`handleClickOn` re-run for the same click. Consume it
51
+ // without re-dispatching (which would add a redundant undo entry).
52
+ if (view.state.selection.eq(nextSelection)) {
53
+ return true;
54
+ }
55
+ view.dispatch(view.state.tr.setSelection(nextSelection).scrollIntoView());
56
+ return true;
57
+ };
30
58
  var isWholeSelectionInsideLayoutColumn = function isWholeSelectionInsideLayoutColumn(state) {
31
59
  // Since findParentNodeOfType doesn't check if selection.to shares the parent, we do this check ourselves
32
60
  var fromParent = (0, _utils2.findParentNodeOfType)(state.schema.nodes.layoutColumn)(state.selection);
@@ -272,14 +300,52 @@ var _default = exports.default = function _default(options, editorAnalyticsAPI)
272
300
  Backspace: handleDeleteLayoutColumn,
273
301
  Delete: handleDeleteLayoutColumn
274
302
  }),
275
- handleClickOn: (0, _selection.createSelectionClickHandler)(['layoutColumn'], function (target) {
276
- return target.hasAttribute('data-layout-section') || target.hasAttribute('data-layout-column');
277
- }, {
278
- useLongPressSelection: options.useLongPressSelection || false,
279
- getNodeSelectionPos: function getNodeSelectionPos(state, nodePos) {
280
- return state.doc.resolve(nodePos).before();
303
+ handleDOMEvents: {
304
+ // Place the gap cursor on `mousedown` (not `mouseup`) so the caret never flashes
305
+ // inside a nested editable child first.
306
+ mousedown: function mousedown(view, event) {
307
+ var target = event.target;
308
+ if (target !== null && target !== void 0 && target.hasAttribute('data-layout-section')) {
309
+ return false;
310
+ }
311
+ if (applyBlankSpaceGapCursor(view, event)) {
312
+ event.preventDefault();
313
+ // `preventDefault()` blocks the editor focus that makes the gap cursor blink,
314
+ // so restore it here. The `handleClick`/`handleClickOn` paths don't need this.
315
+ if (!view.hasFocus()) {
316
+ view.focus();
317
+ }
318
+ return true;
319
+ }
320
+ return false;
321
+ }
322
+ },
323
+ handleClickOn: function () {
324
+ var selectionClickHandler = (0, _selection.createSelectionClickHandler)(['layoutColumn'], function (target) {
325
+ return target.hasAttribute('data-layout-section') || target.hasAttribute('data-layout-column');
326
+ }, {
327
+ useLongPressSelection: options.useLongPressSelection || false,
328
+ getNodeSelectionPos: function getNodeSelectionPos(state, nodePos) {
329
+ return state.doc.resolve(nodePos).before();
330
+ }
331
+ });
332
+ return function (view, pos, node, nodePos, event, direct) {
333
+ // Fallback for clicks on an atomic node view that the mousedown hook missed.
334
+ var target = event.target;
335
+ if (!(target !== null && target !== void 0 && target.hasAttribute('data-layout-section')) && applyBlankSpaceGapCursor(view, event)) {
336
+ return true;
337
+ }
338
+ return selectionClickHandler(view, pos, node, nodePos, event, direct);
339
+ };
340
+ }(),
341
+ handleClick: function handleClick(view, _pos, event) {
342
+ // Fallback for clicks the mousedown interceptor missed.
343
+ var target = event.target;
344
+ if (target !== null && target !== void 0 && target.hasAttribute('data-layout-section')) {
345
+ return false;
281
346
  }
282
- })
347
+ return applyBlankSpaceGapCursor(view, event);
348
+ }
283
349
  },
284
350
  appendTransaction: function appendTransaction(transactions, _oldState, newState) {
285
351
  var changes = [];
@@ -3,10 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.selectIntoLayout = exports.getMaybeLayoutSection = void 0;
6
+ exports.selectIntoLayout = exports.isParagraphBlankSpaceTarget = exports.getMaybeLayoutSection = exports.getGapCursorTargetForBlankSpaceClick = void 0;
7
7
  var _selection = require("@atlaskit/editor-common/selection");
8
8
  var _state = require("@atlaskit/editor-prosemirror/state");
9
9
  var _utils = require("@atlaskit/editor-prosemirror/utils");
10
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
10
11
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
11
12
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
12
13
 
@@ -56,4 +57,195 @@ var selectIntoLayout = exports.selectIntoLayout = function selectIntoLayout(view
56
57
  }
57
58
  return tr;
58
59
  }
60
+ };
61
+ /**
62
+ * For a blank-space click inside a layout column — above the first child (middle/bottom-aligned
63
+ * columns) or below the last child (any alignment) — return the ProseMirror position and side
64
+ * for a gap cursor. Returns `undefined` when the kill switch is ON, the click is outside a
65
+ * layoutColumn, or the Y coordinate is not in blank space.
66
+ *
67
+ * The `advanced_layouts` / `platform_editor_layout_column_menu` gates live in the caller
68
+ * (`applyBlankSpaceGapCursor`); only the kill switch is checked here.
69
+ */
70
+ var getGapCursorTargetForBlankSpaceClick = exports.getGapCursorTargetForBlankSpaceClick = function getGapCursorTargetForBlankSpaceClick(view, event) {
71
+ var _columnNode$attrs;
72
+ if ((0, _platformFeatureFlags.fg)('platform_editor_layout_column_menu_kill_switch_1')) {
73
+ return undefined;
74
+ }
75
+
76
+ // Resolve the column from the DOM target so it works even when posAtCoords returns null
77
+ // (nothing rendered at the clicked Y).
78
+ var target = event.target;
79
+ var columnEl = target === null || target === void 0 ? void 0 : target.closest('[data-layout-column]');
80
+ if (!columnEl) {
81
+ return undefined;
82
+ }
83
+ var columnStartPos;
84
+ try {
85
+ columnStartPos = view.posAtDOM(columnEl, 0);
86
+ } catch (_unused) {
87
+ return undefined;
88
+ }
89
+
90
+ // posAtDOM resolves at varying depths, so walk up to find the layoutColumn.
91
+ var $columnStart = view.state.doc.resolve(columnStartPos);
92
+ var depth = -1;
93
+ for (var d = $columnStart.depth; d >= 0; d--) {
94
+ if ($columnStart.node(d).type.name === 'layoutColumn') {
95
+ depth = d;
96
+ break;
97
+ }
98
+ }
99
+ if (depth < 0) {
100
+ return undefined;
101
+ }
102
+ var columnNode = $columnStart.node(depth);
103
+ if (columnNode.childCount === 0) {
104
+ return undefined;
105
+ }
106
+ var columnContentStart = $columnStart.start(depth);
107
+ var columnEndPos = $columnStart.end(depth);
108
+ var getChildDom = function getChildDom(nodePos) {
109
+ try {
110
+ var dom = view.nodeDOM(nodePos);
111
+ return dom instanceof Element ? dom : null;
112
+ } catch (_unused2) {
113
+ return null;
114
+ }
115
+ };
116
+ var valign = (_columnNode$attrs = columnNode.attrs) === null || _columnNode$attrs === void 0 ? void 0 : _columnNode$attrs.valign;
117
+ var isNonTopAligned = valign && valign !== 'top';
118
+
119
+ // Use the column rect (not child rects) for above/below detection: it stays stable as
120
+ // gap-cursor widgets shift child DOM positions between repeated clicks.
121
+ var columnRect = columnEl.getBoundingClientRect();
122
+
123
+ // Click ABOVE the first child (only for middle/bottom-aligned columns).
124
+ var firstChildPos = columnContentStart;
125
+ var firstChildDom = getChildDom(firstChildPos);
126
+ if (isNonTopAligned && firstChildDom) {
127
+ var rect = firstChildDom.getBoundingClientRect();
128
+ if (event.clientY < rect.top && event.clientY >= columnRect.top) {
129
+ return {
130
+ pos: firstChildPos,
131
+ side: 'left'
132
+ };
133
+ }
134
+ }
135
+
136
+ // Click BELOW the last child (for any column alignment).
137
+ var lastChild = columnNode.lastChild;
138
+ var lastChildEndPos = columnEndPos;
139
+ var lastChildStartPos = lastChild ? lastChildEndPos - lastChild.nodeSize : columnContentStart;
140
+ var lastChildDom = lastChild ? getChildDom(lastChildStartPos) : null;
141
+ if (lastChild && lastChildDom) {
142
+ var _rect = lastChildDom.getBoundingClientRect();
143
+ if (event.clientY > _rect.bottom && event.clientY <= columnRect.bottom) {
144
+ return {
145
+ pos: lastChildEndPos,
146
+ side: 'right'
147
+ };
148
+ }
149
+ }
150
+
151
+ // Fallback: click lands ON a single atomic child that fills the column (mediaSingle/expand),
152
+ // so the above/below checks never fired.
153
+ if (columnNode.childCount === 1) {
154
+ var onlyChild = columnNode.firstChild;
155
+ // Exclude `panel`: its wrapper makes `view.nodeDOM` non-null and intercepts clicks, so the
156
+ // guard below would wrongly fire for in-panel blank-space clicks (which have their own
157
+ // native gap cursor).
158
+ if (onlyChild && onlyChild.type.name !== 'paragraph' && onlyChild.type.name !== 'panel') {
159
+ // Bail when the click is on the child's own content. For media the wrapper is full-width
160
+ // so test against the <img> rect; resolve it only for a direct mediaSingle child (else
161
+ // getContentRect could grab an image nested in an expand and break its toggle).
162
+ var contentRect = onlyChild.type.name === 'mediaSingle' ? getContentRect(firstChildDom) : null;
163
+ if (contentRect) {
164
+ var insideImage = event.clientX >= contentRect.left && event.clientX <= contentRect.right && event.clientY >= contentRect.top && event.clientY <= contentRect.bottom;
165
+ if (insideImage) {
166
+ return undefined;
167
+ }
168
+ } else {
169
+ // Other atomics: bail when posAtCoords resolves strictly inside the node range.
170
+ var coordPos = null;
171
+ try {
172
+ coordPos = view.posAtCoords({
173
+ left: event.clientX,
174
+ top: event.clientY
175
+ });
176
+ } catch (_unused3) {
177
+ coordPos = null;
178
+ }
179
+ if (coordPos && coordPos.pos > firstChildPos && coordPos.pos < lastChildEndPos) {
180
+ return undefined;
181
+ }
182
+ }
183
+
184
+ // Fire when the child DOM is resolvable, or when it's null (media not yet loaded) but
185
+ // the click target is the column itself (no node view intercepted it).
186
+ var targetEl = event.target;
187
+ var targetIsColumn = targetEl === columnEl;
188
+ var shouldUseFallback = firstChildDom !== null || targetIsColumn;
189
+ if (shouldUseFallback) {
190
+ var side = getGapCursorSideForBlankSpaceClick(firstChildDom, columnRect, event.clientX, event.clientY);
191
+ return side === 'left' ? {
192
+ pos: firstChildPos,
193
+ side: 'left'
194
+ } : {
195
+ pos: lastChildEndPos,
196
+ side: 'right'
197
+ };
198
+ }
199
+ }
200
+ }
201
+ return undefined;
202
+ };
203
+
204
+ /**
205
+ * The tight `<img>` content rect (or `null`). The outer wrapper often fills the whole column
206
+ * width, so the `<img>` rect is needed to tell "beside the image" from "on the image".
207
+ */
208
+ var getContentRect = function getContentRect(firstChildDom) {
209
+ var img = firstChildDom === null || firstChildDom === void 0 ? void 0 : firstChildDom.querySelector('img');
210
+ return img ? img.getBoundingClientRect() : null;
211
+ };
212
+
213
+ /**
214
+ * Which side of an atomic child a blank-space click belongs to. Prefers the tight content (image)
215
+ * rect when available — using its midpoint so it's direction-agnostic (handles RTL right-aligned
216
+ * images) — otherwise falls back to the column's vertical midpoint.
217
+ */
218
+ var getGapCursorSideForBlankSpaceClick = function getGapCursorSideForBlankSpaceClick(firstChildDom, columnRect, clientX, clientY) {
219
+ var contentRect = getContentRect(firstChildDom);
220
+ if (contentRect) {
221
+ if (clientY < contentRect.top) {
222
+ return 'left';
223
+ }
224
+ if (clientY > contentRect.bottom) {
225
+ return 'right';
226
+ }
227
+ if (clientX < (contentRect.left + contentRect.right) / 2) {
228
+ return 'left';
229
+ }
230
+ return 'right';
231
+ }
232
+ var columnMidY = columnRect.top + columnRect.height / 2;
233
+ return clientY < columnMidY ? 'left' : 'right';
234
+ };
235
+
236
+ /**
237
+ * True when the blank-space click target child is a paragraph, so the caller uses a TextSelection
238
+ * instead of a gap cursor. LEFT inspects the first child, RIGHT the last child.
239
+ */
240
+ var isParagraphBlankSpaceTarget = exports.isParagraphBlankSpaceTarget = function isParagraphBlankSpaceTarget(view, gapTarget) {
241
+ var pos = gapTarget.pos,
242
+ side = gapTarget.side;
243
+ var doc = view.state.doc;
244
+ try {
245
+ var $pos = doc.resolve(pos);
246
+ var childNode = side === 'left' ? $pos.nodeAfter : $pos.nodeBefore;
247
+ return (childNode === null || childNode === void 0 ? void 0 : childNode.type.name) === 'paragraph';
248
+ } catch (_unused4) {
249
+ return false;
250
+ }
59
251
  };
@@ -9,7 +9,6 @@ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
9
9
  import { findParentNode } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { fg } from '@atlaskit/platform-feature-flags';
11
11
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
12
- import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
13
12
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
14
13
  import { createDefaultLayoutSection, createMultiColumnLayoutSection, deleteLayoutColumn, distributeLayoutColumns, insertLayoutColumn, insertLayoutColumnsWithAnalytics, setLayoutColumnDangerPreview, setLayoutColumnValign, toggleLayoutColumnMenu } from './pm-plugins/actions';
15
14
  import { default as createLayoutKeymapPlugin } from './pm-plugins/keymap';
@@ -74,7 +73,7 @@ export const layoutPlugin = ({
74
73
  }
75
74
  }]);
76
75
  }
77
- if (expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
76
+ if (expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
78
77
  var _api$uiControlRegistr;
79
78
  api === null || api === void 0 ? void 0 : (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 ? void 0 : _api$uiControlRegistr.actions.register(getLayoutColumnMenuComponents({
80
79
  api
@@ -99,7 +98,7 @@ export const layoutPlugin = ({
99
98
  return createLayoutPlugin(options, api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions);
100
99
  }
101
100
  }];
102
- if (expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
101
+ if (expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
103
102
  plugins.push({
104
103
  name: 'layoutKeymap',
105
104
  plugin: () => createLayoutKeymapPlugin({
@@ -344,7 +343,7 @@ export const layoutPlugin = ({
344
343
  popupsBoundariesElement,
345
344
  popupsScrollableElement
346
345
  }) {
347
- return /*#__PURE__*/React.createElement(React.Fragment, null, editorExperiment('advanced_layouts', true) ? /*#__PURE__*/React.createElement(GlobalStylesWrapper, null) : null, expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true) && editorView ? /*#__PURE__*/React.createElement(LayoutColumnMenu, {
346
+ return /*#__PURE__*/React.createElement(React.Fragment, null, editorExperiment('advanced_layouts', true) ? /*#__PURE__*/React.createElement(GlobalStylesWrapper, null) : null, expValEquals('platform_editor_layout_column_menu', 'isEnabled', true) && editorView ? /*#__PURE__*/React.createElement(LayoutColumnMenu, {
348
347
  api: api,
349
348
  editorView: editorView,
350
349
  mountTo: popupsMountPoint,
@@ -6,7 +6,7 @@ import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state
6
6
  import { Mapping, StepMap } from '@atlaskit/editor-prosemirror/transform';
7
7
  import { safeInsert } from '@atlaskit/editor-prosemirror/utils';
8
8
  import { fg } from '@atlaskit/platform-feature-flags';
9
- import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
9
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
10
10
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
11
11
  import { DEFAULT_LAYOUT_COLUMN_VALIGN, EVEN_DISTRIBUTED_COL_WIDTHS, MAX_LAYOUT_COLUMNS, MAX_STANDARD_LAYOUT_COLUMNS, MIN_LAYOUT_COLUMN_WIDTH_PERCENT } from './consts';
12
12
  import { pluginKey } from './plugin-key';
@@ -568,7 +568,7 @@ export function getEffectiveMaxLayoutColumns() {
568
568
  const insertLayoutColumnAt = (side, editorAnalyticsAPI, inputMethod = INPUT_METHOD.LAYOUT_COLUMN_MENU) => ({
569
569
  tr
570
570
  }) => {
571
- if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
571
+ if (!expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
572
572
  return null;
573
573
  }
574
574
  const selectedLayoutColumnsResult = getLayoutColumnsFromContentSelection(tr.selection);
@@ -664,7 +664,7 @@ export const setLayoutColumnValign = ({
664
664
  }, editorAnalyticsAPI, api) => ({
665
665
  tr
666
666
  }) => {
667
- if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
667
+ if (!expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
668
668
  return null;
669
669
  }
670
670
  const selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
@@ -720,7 +720,7 @@ export const distributeLayoutColumns = (editorAnalyticsAPI, api) => ({
720
720
  } = {}) => ({
721
721
  tr
722
722
  }) => {
723
- if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
723
+ if (!expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
724
724
  return null;
725
725
  }
726
726
  const selectedLayoutColumnsResult = target === 'allColumns' ? getAllLayoutColumnsFromSelection(tr.selection) : getSelectedLayoutColumnsFromSelection(tr.selection);
@@ -826,7 +826,7 @@ export const deleteLayoutColumn = ({
826
826
  tr
827
827
  }) => {
828
828
  var _api$blockControls4;
829
- if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
829
+ if (!expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
830
830
  return null;
831
831
  }
832
832
  const selectedLayoutColumnsResult = getLayoutColumnsFromContentSelection(tr.selection);
@@ -1,21 +1,49 @@
1
1
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
3
- import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
3
+ import { createSelectionClickHandler, GapCursorSelection, Side } from '@atlaskit/editor-common/selection';
4
4
  import { filterCommand as filter } from '@atlaskit/editor-common/utils';
5
5
  import { keydownHandler } from '@atlaskit/editor-prosemirror/keymap';
6
6
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
7
7
  import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
8
8
  import { findParentNodeClosestToPos, findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
9
9
  import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
10
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
10
11
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
11
12
  import { fixColumnSizes, fixColumnStructure, getSelectedLayout, LAYOUT_COLUMN_INSERT_META } from './actions';
12
13
  import { getColumnDividerDecorations } from './column-resize-divider';
13
14
  import { EVEN_DISTRIBUTED_COL_WIDTHS } from './consts';
14
15
  import { pluginKey } from './plugin-key';
15
16
  import { pluginKey as layoutResizingPluginKey } from './resizing';
16
- import { getMaybeLayoutSection } from './utils';
17
+ import { getGapCursorTargetForBlankSpaceClick, getMaybeLayoutSection, isParagraphBlankSpaceTarget } from './utils';
17
18
  import { getSelectedLayoutColumnsFromSelection } from './utils/layout-column-selection';
18
19
  export const DEFAULT_LAYOUT = 'two_equal';
20
+
21
+ /**
22
+ * Shared blank-space gap cursor placement, used by both `handleClick` and `handleClickOn`
23
+ * (the latter catches clicks on atomic node views that stop propagation before `handleClick`).
24
+ * Returns `true` when it placed a selection and consumed the click, else `false`.
25
+ */
26
+ const applyBlankSpaceGapCursor = (view, event) => {
27
+ if (!expValEquals('platform_editor_layout_column_menu', 'isEnabled', true) || !editorExperiment('advanced_layouts', true)) {
28
+ return false;
29
+ }
30
+ const gapTarget = getGapCursorTargetForBlankSpaceClick(view, event);
31
+ if (gapTarget === undefined) {
32
+ return false;
33
+ }
34
+ const $pos = view.state.doc.resolve(gapTarget.pos);
35
+ // A paragraph child takes a TextSelection (caret at the edge) rather than a gap cursor.
36
+ const isParagraphTarget = isParagraphBlankSpaceTarget(view, gapTarget);
37
+ const nextSelection = isParagraphTarget ? TextSelection.near($pos, gapTarget.side === 'left' ? 1 : -1) : new GapCursorSelection($pos, gapTarget.side === 'left' ? Side.LEFT : Side.RIGHT);
38
+ // Idempotency guard: `mousedown` already placed this selection, but the browser still
39
+ // fires `mouseup`, so `handleClick`/`handleClickOn` re-run for the same click. Consume it
40
+ // without re-dispatching (which would add a redundant undo entry).
41
+ if (view.state.selection.eq(nextSelection)) {
42
+ return true;
43
+ }
44
+ view.dispatch(view.state.tr.setSelection(nextSelection).scrollIntoView());
45
+ return true;
46
+ };
19
47
  const isWholeSelectionInsideLayoutColumn = state => {
20
48
  // Since findParentNodeOfType doesn't check if selection.to shares the parent, we do this check ourselves
21
49
  const fromParent = findParentNodeOfType(state.schema.nodes.layoutColumn)(state.selection);
@@ -272,10 +300,48 @@ export default ((options, editorAnalyticsAPI) => {
272
300
  Backspace: handleDeleteLayoutColumn,
273
301
  Delete: handleDeleteLayoutColumn
274
302
  }),
275
- handleClickOn: createSelectionClickHandler(['layoutColumn'], target => target.hasAttribute('data-layout-section') || target.hasAttribute('data-layout-column'), {
276
- useLongPressSelection: options.useLongPressSelection || false,
277
- getNodeSelectionPos: (state, nodePos) => state.doc.resolve(nodePos).before()
278
- })
303
+ handleDOMEvents: {
304
+ // Place the gap cursor on `mousedown` (not `mouseup`) so the caret never flashes
305
+ // inside a nested editable child first.
306
+ mousedown(view, event) {
307
+ const target = event.target;
308
+ if (target !== null && target !== void 0 && target.hasAttribute('data-layout-section')) {
309
+ return false;
310
+ }
311
+ if (applyBlankSpaceGapCursor(view, event)) {
312
+ event.preventDefault();
313
+ // `preventDefault()` blocks the editor focus that makes the gap cursor blink,
314
+ // so restore it here. The `handleClick`/`handleClickOn` paths don't need this.
315
+ if (!view.hasFocus()) {
316
+ view.focus();
317
+ }
318
+ return true;
319
+ }
320
+ return false;
321
+ }
322
+ },
323
+ handleClickOn: (() => {
324
+ const selectionClickHandler = createSelectionClickHandler(['layoutColumn'], target => target.hasAttribute('data-layout-section') || target.hasAttribute('data-layout-column'), {
325
+ useLongPressSelection: options.useLongPressSelection || false,
326
+ getNodeSelectionPos: (state, nodePos) => state.doc.resolve(nodePos).before()
327
+ });
328
+ return (view, pos, node, nodePos, event, direct) => {
329
+ // Fallback for clicks on an atomic node view that the mousedown hook missed.
330
+ const target = event.target;
331
+ if (!(target !== null && target !== void 0 && target.hasAttribute('data-layout-section')) && applyBlankSpaceGapCursor(view, event)) {
332
+ return true;
333
+ }
334
+ return selectionClickHandler(view, pos, node, nodePos, event, direct);
335
+ };
336
+ })(),
337
+ handleClick(view, _pos, event) {
338
+ // Fallback for clicks the mousedown interceptor missed.
339
+ const target = event.target;
340
+ if (target !== null && target !== void 0 && target.hasAttribute('data-layout-section')) {
341
+ return false;
342
+ }
343
+ return applyBlankSpaceGapCursor(view, event);
344
+ }
279
345
  },
280
346
  appendTransaction: (transactions, _oldState, newState) => {
281
347
  const changes = [];
@@ -2,6 +2,7 @@ import { GapCursorSelection } from '@atlaskit/editor-common/selection';
2
2
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
3
3
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
4
4
  import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
5
+ import { fg } from '@atlaskit/platform-feature-flags';
5
6
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
6
7
  export const getMaybeLayoutSection = state => {
7
8
  const {
@@ -53,4 +54,199 @@ export const selectIntoLayout = (view, posOfLayout, childIndex = 0) => {
53
54
  }
54
55
  return tr;
55
56
  }
57
+ };
58
+ /**
59
+ * For a blank-space click inside a layout column — above the first child (middle/bottom-aligned
60
+ * columns) or below the last child (any alignment) — return the ProseMirror position and side
61
+ * for a gap cursor. Returns `undefined` when the kill switch is ON, the click is outside a
62
+ * layoutColumn, or the Y coordinate is not in blank space.
63
+ *
64
+ * The `advanced_layouts` / `platform_editor_layout_column_menu` gates live in the caller
65
+ * (`applyBlankSpaceGapCursor`); only the kill switch is checked here.
66
+ */
67
+ export const getGapCursorTargetForBlankSpaceClick = (view, event) => {
68
+ var _columnNode$attrs;
69
+ if (fg('platform_editor_layout_column_menu_kill_switch_1')) {
70
+ return undefined;
71
+ }
72
+
73
+ // Resolve the column from the DOM target so it works even when posAtCoords returns null
74
+ // (nothing rendered at the clicked Y).
75
+ const target = event.target;
76
+ const columnEl = target === null || target === void 0 ? void 0 : target.closest('[data-layout-column]');
77
+ if (!columnEl) {
78
+ return undefined;
79
+ }
80
+ let columnStartPos;
81
+ try {
82
+ columnStartPos = view.posAtDOM(columnEl, 0);
83
+ } catch {
84
+ return undefined;
85
+ }
86
+
87
+ // posAtDOM resolves at varying depths, so walk up to find the layoutColumn.
88
+ const $columnStart = view.state.doc.resolve(columnStartPos);
89
+ let depth = -1;
90
+ for (let d = $columnStart.depth; d >= 0; d--) {
91
+ if ($columnStart.node(d).type.name === 'layoutColumn') {
92
+ depth = d;
93
+ break;
94
+ }
95
+ }
96
+ if (depth < 0) {
97
+ return undefined;
98
+ }
99
+ const columnNode = $columnStart.node(depth);
100
+ if (columnNode.childCount === 0) {
101
+ return undefined;
102
+ }
103
+ const columnContentStart = $columnStart.start(depth);
104
+ const columnEndPos = $columnStart.end(depth);
105
+ const getChildDom = nodePos => {
106
+ try {
107
+ const dom = view.nodeDOM(nodePos);
108
+ return dom instanceof Element ? dom : null;
109
+ } catch {
110
+ return null;
111
+ }
112
+ };
113
+ const valign = (_columnNode$attrs = columnNode.attrs) === null || _columnNode$attrs === void 0 ? void 0 : _columnNode$attrs.valign;
114
+ const isNonTopAligned = valign && valign !== 'top';
115
+
116
+ // Use the column rect (not child rects) for above/below detection: it stays stable as
117
+ // gap-cursor widgets shift child DOM positions between repeated clicks.
118
+ const columnRect = columnEl.getBoundingClientRect();
119
+
120
+ // Click ABOVE the first child (only for middle/bottom-aligned columns).
121
+ const firstChildPos = columnContentStart;
122
+ const firstChildDom = getChildDom(firstChildPos);
123
+ if (isNonTopAligned && firstChildDom) {
124
+ const rect = firstChildDom.getBoundingClientRect();
125
+ if (event.clientY < rect.top && event.clientY >= columnRect.top) {
126
+ return {
127
+ pos: firstChildPos,
128
+ side: 'left'
129
+ };
130
+ }
131
+ }
132
+
133
+ // Click BELOW the last child (for any column alignment).
134
+ const lastChild = columnNode.lastChild;
135
+ const lastChildEndPos = columnEndPos;
136
+ const lastChildStartPos = lastChild ? lastChildEndPos - lastChild.nodeSize : columnContentStart;
137
+ const lastChildDom = lastChild ? getChildDom(lastChildStartPos) : null;
138
+ if (lastChild && lastChildDom) {
139
+ const rect = lastChildDom.getBoundingClientRect();
140
+ if (event.clientY > rect.bottom && event.clientY <= columnRect.bottom) {
141
+ return {
142
+ pos: lastChildEndPos,
143
+ side: 'right'
144
+ };
145
+ }
146
+ }
147
+
148
+ // Fallback: click lands ON a single atomic child that fills the column (mediaSingle/expand),
149
+ // so the above/below checks never fired.
150
+ if (columnNode.childCount === 1) {
151
+ const onlyChild = columnNode.firstChild;
152
+ // Exclude `panel`: its wrapper makes `view.nodeDOM` non-null and intercepts clicks, so the
153
+ // guard below would wrongly fire for in-panel blank-space clicks (which have their own
154
+ // native gap cursor).
155
+ if (onlyChild && onlyChild.type.name !== 'paragraph' && onlyChild.type.name !== 'panel') {
156
+ // Bail when the click is on the child's own content. For media the wrapper is full-width
157
+ // so test against the <img> rect; resolve it only for a direct mediaSingle child (else
158
+ // getContentRect could grab an image nested in an expand and break its toggle).
159
+ const contentRect = onlyChild.type.name === 'mediaSingle' ? getContentRect(firstChildDom) : null;
160
+ if (contentRect) {
161
+ const insideImage = event.clientX >= contentRect.left && event.clientX <= contentRect.right && event.clientY >= contentRect.top && event.clientY <= contentRect.bottom;
162
+ if (insideImage) {
163
+ return undefined;
164
+ }
165
+ } else {
166
+ // Other atomics: bail when posAtCoords resolves strictly inside the node range.
167
+ let coordPos = null;
168
+ try {
169
+ coordPos = view.posAtCoords({
170
+ left: event.clientX,
171
+ top: event.clientY
172
+ });
173
+ } catch {
174
+ coordPos = null;
175
+ }
176
+ if (coordPos && coordPos.pos > firstChildPos && coordPos.pos < lastChildEndPos) {
177
+ return undefined;
178
+ }
179
+ }
180
+
181
+ // Fire when the child DOM is resolvable, or when it's null (media not yet loaded) but
182
+ // the click target is the column itself (no node view intercepted it).
183
+ const targetEl = event.target;
184
+ const targetIsColumn = targetEl === columnEl;
185
+ const shouldUseFallback = firstChildDom !== null || targetIsColumn;
186
+ if (shouldUseFallback) {
187
+ const side = getGapCursorSideForBlankSpaceClick(firstChildDom, columnRect, event.clientX, event.clientY);
188
+ return side === 'left' ? {
189
+ pos: firstChildPos,
190
+ side: 'left'
191
+ } : {
192
+ pos: lastChildEndPos,
193
+ side: 'right'
194
+ };
195
+ }
196
+ }
197
+ }
198
+ return undefined;
199
+ };
200
+
201
+ /**
202
+ * The tight `<img>` content rect (or `null`). The outer wrapper often fills the whole column
203
+ * width, so the `<img>` rect is needed to tell "beside the image" from "on the image".
204
+ */
205
+ const getContentRect = firstChildDom => {
206
+ const img = firstChildDom === null || firstChildDom === void 0 ? void 0 : firstChildDom.querySelector('img');
207
+ return img ? img.getBoundingClientRect() : null;
208
+ };
209
+
210
+ /**
211
+ * Which side of an atomic child a blank-space click belongs to. Prefers the tight content (image)
212
+ * rect when available — using its midpoint so it's direction-agnostic (handles RTL right-aligned
213
+ * images) — otherwise falls back to the column's vertical midpoint.
214
+ */
215
+ const getGapCursorSideForBlankSpaceClick = (firstChildDom, columnRect, clientX, clientY) => {
216
+ const contentRect = getContentRect(firstChildDom);
217
+ if (contentRect) {
218
+ if (clientY < contentRect.top) {
219
+ return 'left';
220
+ }
221
+ if (clientY > contentRect.bottom) {
222
+ return 'right';
223
+ }
224
+ if (clientX < (contentRect.left + contentRect.right) / 2) {
225
+ return 'left';
226
+ }
227
+ return 'right';
228
+ }
229
+ const columnMidY = columnRect.top + columnRect.height / 2;
230
+ return clientY < columnMidY ? 'left' : 'right';
231
+ };
232
+
233
+ /**
234
+ * True when the blank-space click target child is a paragraph, so the caller uses a TextSelection
235
+ * instead of a gap cursor. LEFT inspects the first child, RIGHT the last child.
236
+ */
237
+ export const isParagraphBlankSpaceTarget = (view, gapTarget) => {
238
+ const {
239
+ pos,
240
+ side
241
+ } = gapTarget;
242
+ const {
243
+ doc
244
+ } = view.state;
245
+ try {
246
+ const $pos = doc.resolve(pos);
247
+ const childNode = side === 'left' ? $pos.nodeAfter : $pos.nodeBefore;
248
+ return (childNode === null || childNode === void 0 ? void 0 : childNode.type.name) === 'paragraph';
249
+ } catch {
250
+ return false;
251
+ }
56
252
  };
@@ -9,7 +9,6 @@ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
9
9
  import { findParentNode } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { fg } from '@atlaskit/platform-feature-flags';
11
11
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
12
- import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
13
12
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
14
13
  import { createDefaultLayoutSection, createMultiColumnLayoutSection, deleteLayoutColumn as _deleteLayoutColumn, distributeLayoutColumns as _distributeLayoutColumns, insertLayoutColumn as _insertLayoutColumn, insertLayoutColumnsWithAnalytics, setLayoutColumnDangerPreview, setLayoutColumnValign as _setLayoutColumnValign, toggleLayoutColumnMenu } from './pm-plugins/actions';
15
14
  import { default as createLayoutKeymapPlugin } from './pm-plugins/keymap';
@@ -75,7 +74,7 @@ export var layoutPlugin = function layoutPlugin(_ref) {
75
74
  }
76
75
  }]);
77
76
  }
78
- if (expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
77
+ if (expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
79
78
  var _api$uiControlRegistr;
80
79
  api === null || api === void 0 || (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 || _api$uiControlRegistr.actions.register(getLayoutColumnMenuComponents({
81
80
  api: api
@@ -100,7 +99,7 @@ export var layoutPlugin = function layoutPlugin(_ref) {
100
99
  return createLayoutPlugin(options, api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions);
101
100
  }
102
101
  }];
103
- if (expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
102
+ if (expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
104
103
  plugins.push({
105
104
  name: 'layoutKeymap',
106
105
  plugin: function plugin() {
@@ -365,7 +364,7 @@ export var layoutPlugin = function layoutPlugin(_ref) {
365
364
  popupsMountPoint = _ref5.popupsMountPoint,
366
365
  popupsBoundariesElement = _ref5.popupsBoundariesElement,
367
366
  popupsScrollableElement = _ref5.popupsScrollableElement;
368
- return /*#__PURE__*/React.createElement(React.Fragment, null, editorExperiment('advanced_layouts', true) ? /*#__PURE__*/React.createElement(GlobalStylesWrapper, null) : null, expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true) && editorView ? /*#__PURE__*/React.createElement(LayoutColumnMenu, {
367
+ return /*#__PURE__*/React.createElement(React.Fragment, null, editorExperiment('advanced_layouts', true) ? /*#__PURE__*/React.createElement(GlobalStylesWrapper, null) : null, expValEquals('platform_editor_layout_column_menu', 'isEnabled', true) && editorView ? /*#__PURE__*/React.createElement(LayoutColumnMenu, {
369
368
  api: api,
370
369
  editorView: editorView,
371
370
  mountTo: popupsMountPoint,
@@ -9,7 +9,7 @@ import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state
9
9
  import { Mapping, StepMap } from '@atlaskit/editor-prosemirror/transform';
10
10
  import { safeInsert } from '@atlaskit/editor-prosemirror/utils';
11
11
  import { fg } from '@atlaskit/platform-feature-flags';
12
- import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
12
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
13
13
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
14
14
  import { DEFAULT_LAYOUT_COLUMN_VALIGN, EVEN_DISTRIBUTED_COL_WIDTHS, MAX_LAYOUT_COLUMNS, MAX_STANDARD_LAYOUT_COLUMNS, MIN_LAYOUT_COLUMN_WIDTH_PERCENT } from './consts';
15
15
  import { pluginKey } from './plugin-key';
@@ -586,7 +586,7 @@ var insertLayoutColumnAt = function insertLayoutColumnAt(side, editorAnalyticsAP
586
586
  var inputMethod = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : INPUT_METHOD.LAYOUT_COLUMN_MENU;
587
587
  return function (_ref5) {
588
588
  var tr = _ref5.tr;
589
- if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
589
+ if (!expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
590
590
  return null;
591
591
  }
592
592
  var selectedLayoutColumnsResult = getLayoutColumnsFromContentSelection(tr.selection);
@@ -682,7 +682,7 @@ export var setLayoutColumnValign = function setLayoutColumnValign(_ref8, editorA
682
682
  inputMethod = _ref8$inputMethod === void 0 ? INPUT_METHOD.LAYOUT_COLUMN_MENU : _ref8$inputMethod;
683
683
  return function (_ref9) {
684
684
  var tr = _ref9.tr;
685
- if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
685
+ if (!expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
686
686
  return null;
687
687
  }
688
688
  var selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
@@ -739,7 +739,7 @@ export var distributeLayoutColumns = function distributeLayoutColumns(editorAnal
739
739
  target = _ref10$target === void 0 ? 'selectedColumns' : _ref10$target;
740
740
  return function (_ref11) {
741
741
  var tr = _ref11.tr;
742
- if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
742
+ if (!expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
743
743
  return null;
744
744
  }
745
745
  var selectedLayoutColumnsResult = target === 'allColumns' ? getAllLayoutColumnsFromSelection(tr.selection) : getSelectedLayoutColumnsFromSelection(tr.selection);
@@ -850,7 +850,7 @@ export var deleteLayoutColumn = function deleteLayoutColumn() {
850
850
  return function (_ref18) {
851
851
  var _api$blockControls4;
852
852
  var tr = _ref18.tr;
853
- if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
853
+ if (!expValEquals('platform_editor_layout_column_menu', 'isEnabled', true)) {
854
854
  return null;
855
855
  }
856
856
  var selectedLayoutColumnsResult = getLayoutColumnsFromContentSelection(tr.selection);
@@ -4,22 +4,50 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
4
4
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
5
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
6
6
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
7
- import { createSelectionClickHandler } from '@atlaskit/editor-common/selection';
7
+ import { createSelectionClickHandler, GapCursorSelection, Side } from '@atlaskit/editor-common/selection';
8
8
  import { filterCommand as filter } from '@atlaskit/editor-common/utils';
9
9
  import { keydownHandler } from '@atlaskit/editor-prosemirror/keymap';
10
10
  import { Fragment } from '@atlaskit/editor-prosemirror/model';
11
11
  import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
12
12
  import { findParentNodeClosestToPos, findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
13
13
  import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
14
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
14
15
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
15
16
  import { fixColumnSizes, fixColumnStructure, getSelectedLayout, LAYOUT_COLUMN_INSERT_META } from './actions';
16
17
  import { getColumnDividerDecorations } from './column-resize-divider';
17
18
  import { EVEN_DISTRIBUTED_COL_WIDTHS } from './consts';
18
19
  import { pluginKey } from './plugin-key';
19
20
  import { pluginKey as layoutResizingPluginKey } from './resizing';
20
- import { getMaybeLayoutSection } from './utils';
21
+ import { getGapCursorTargetForBlankSpaceClick, getMaybeLayoutSection, isParagraphBlankSpaceTarget } from './utils';
21
22
  import { getSelectedLayoutColumnsFromSelection } from './utils/layout-column-selection';
22
23
  export var DEFAULT_LAYOUT = 'two_equal';
24
+
25
+ /**
26
+ * Shared blank-space gap cursor placement, used by both `handleClick` and `handleClickOn`
27
+ * (the latter catches clicks on atomic node views that stop propagation before `handleClick`).
28
+ * Returns `true` when it placed a selection and consumed the click, else `false`.
29
+ */
30
+ var applyBlankSpaceGapCursor = function applyBlankSpaceGapCursor(view, event) {
31
+ if (!expValEquals('platform_editor_layout_column_menu', 'isEnabled', true) || !editorExperiment('advanced_layouts', true)) {
32
+ return false;
33
+ }
34
+ var gapTarget = getGapCursorTargetForBlankSpaceClick(view, event);
35
+ if (gapTarget === undefined) {
36
+ return false;
37
+ }
38
+ var $pos = view.state.doc.resolve(gapTarget.pos);
39
+ // A paragraph child takes a TextSelection (caret at the edge) rather than a gap cursor.
40
+ var isParagraphTarget = isParagraphBlankSpaceTarget(view, gapTarget);
41
+ var nextSelection = isParagraphTarget ? TextSelection.near($pos, gapTarget.side === 'left' ? 1 : -1) : new GapCursorSelection($pos, gapTarget.side === 'left' ? Side.LEFT : Side.RIGHT);
42
+ // Idempotency guard: `mousedown` already placed this selection, but the browser still
43
+ // fires `mouseup`, so `handleClick`/`handleClickOn` re-run for the same click. Consume it
44
+ // without re-dispatching (which would add a redundant undo entry).
45
+ if (view.state.selection.eq(nextSelection)) {
46
+ return true;
47
+ }
48
+ view.dispatch(view.state.tr.setSelection(nextSelection).scrollIntoView());
49
+ return true;
50
+ };
23
51
  var isWholeSelectionInsideLayoutColumn = function isWholeSelectionInsideLayoutColumn(state) {
24
52
  // Since findParentNodeOfType doesn't check if selection.to shares the parent, we do this check ourselves
25
53
  var fromParent = findParentNodeOfType(state.schema.nodes.layoutColumn)(state.selection);
@@ -265,14 +293,52 @@ export default (function (options, editorAnalyticsAPI) {
265
293
  Backspace: handleDeleteLayoutColumn,
266
294
  Delete: handleDeleteLayoutColumn
267
295
  }),
268
- handleClickOn: createSelectionClickHandler(['layoutColumn'], function (target) {
269
- return target.hasAttribute('data-layout-section') || target.hasAttribute('data-layout-column');
270
- }, {
271
- useLongPressSelection: options.useLongPressSelection || false,
272
- getNodeSelectionPos: function getNodeSelectionPos(state, nodePos) {
273
- return state.doc.resolve(nodePos).before();
296
+ handleDOMEvents: {
297
+ // Place the gap cursor on `mousedown` (not `mouseup`) so the caret never flashes
298
+ // inside a nested editable child first.
299
+ mousedown: function mousedown(view, event) {
300
+ var target = event.target;
301
+ if (target !== null && target !== void 0 && target.hasAttribute('data-layout-section')) {
302
+ return false;
303
+ }
304
+ if (applyBlankSpaceGapCursor(view, event)) {
305
+ event.preventDefault();
306
+ // `preventDefault()` blocks the editor focus that makes the gap cursor blink,
307
+ // so restore it here. The `handleClick`/`handleClickOn` paths don't need this.
308
+ if (!view.hasFocus()) {
309
+ view.focus();
310
+ }
311
+ return true;
312
+ }
313
+ return false;
314
+ }
315
+ },
316
+ handleClickOn: function () {
317
+ var selectionClickHandler = createSelectionClickHandler(['layoutColumn'], function (target) {
318
+ return target.hasAttribute('data-layout-section') || target.hasAttribute('data-layout-column');
319
+ }, {
320
+ useLongPressSelection: options.useLongPressSelection || false,
321
+ getNodeSelectionPos: function getNodeSelectionPos(state, nodePos) {
322
+ return state.doc.resolve(nodePos).before();
323
+ }
324
+ });
325
+ return function (view, pos, node, nodePos, event, direct) {
326
+ // Fallback for clicks on an atomic node view that the mousedown hook missed.
327
+ var target = event.target;
328
+ if (!(target !== null && target !== void 0 && target.hasAttribute('data-layout-section')) && applyBlankSpaceGapCursor(view, event)) {
329
+ return true;
330
+ }
331
+ return selectionClickHandler(view, pos, node, nodePos, event, direct);
332
+ };
333
+ }(),
334
+ handleClick: function handleClick(view, _pos, event) {
335
+ // Fallback for clicks the mousedown interceptor missed.
336
+ var target = event.target;
337
+ if (target !== null && target !== void 0 && target.hasAttribute('data-layout-section')) {
338
+ return false;
274
339
  }
275
- })
340
+ return applyBlankSpaceGapCursor(view, event);
341
+ }
276
342
  },
277
343
  appendTransaction: function appendTransaction(transactions, _oldState, newState) {
278
344
  var changes = [];
@@ -2,6 +2,7 @@ import { GapCursorSelection } from '@atlaskit/editor-common/selection';
2
2
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
3
3
  import { TextSelection } from '@atlaskit/editor-prosemirror/state';
4
4
  import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
5
+ import { fg } from '@atlaskit/platform-feature-flags';
5
6
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
6
7
  export var getMaybeLayoutSection = function getMaybeLayoutSection(state) {
7
8
  var _state$schema$nodes = state.schema.nodes,
@@ -49,4 +50,195 @@ export var selectIntoLayout = function selectIntoLayout(view, posOfLayout) {
49
50
  }
50
51
  return tr;
51
52
  }
53
+ };
54
+ /**
55
+ * For a blank-space click inside a layout column — above the first child (middle/bottom-aligned
56
+ * columns) or below the last child (any alignment) — return the ProseMirror position and side
57
+ * for a gap cursor. Returns `undefined` when the kill switch is ON, the click is outside a
58
+ * layoutColumn, or the Y coordinate is not in blank space.
59
+ *
60
+ * The `advanced_layouts` / `platform_editor_layout_column_menu` gates live in the caller
61
+ * (`applyBlankSpaceGapCursor`); only the kill switch is checked here.
62
+ */
63
+ export var getGapCursorTargetForBlankSpaceClick = function getGapCursorTargetForBlankSpaceClick(view, event) {
64
+ var _columnNode$attrs;
65
+ if (fg('platform_editor_layout_column_menu_kill_switch_1')) {
66
+ return undefined;
67
+ }
68
+
69
+ // Resolve the column from the DOM target so it works even when posAtCoords returns null
70
+ // (nothing rendered at the clicked Y).
71
+ var target = event.target;
72
+ var columnEl = target === null || target === void 0 ? void 0 : target.closest('[data-layout-column]');
73
+ if (!columnEl) {
74
+ return undefined;
75
+ }
76
+ var columnStartPos;
77
+ try {
78
+ columnStartPos = view.posAtDOM(columnEl, 0);
79
+ } catch (_unused) {
80
+ return undefined;
81
+ }
82
+
83
+ // posAtDOM resolves at varying depths, so walk up to find the layoutColumn.
84
+ var $columnStart = view.state.doc.resolve(columnStartPos);
85
+ var depth = -1;
86
+ for (var d = $columnStart.depth; d >= 0; d--) {
87
+ if ($columnStart.node(d).type.name === 'layoutColumn') {
88
+ depth = d;
89
+ break;
90
+ }
91
+ }
92
+ if (depth < 0) {
93
+ return undefined;
94
+ }
95
+ var columnNode = $columnStart.node(depth);
96
+ if (columnNode.childCount === 0) {
97
+ return undefined;
98
+ }
99
+ var columnContentStart = $columnStart.start(depth);
100
+ var columnEndPos = $columnStart.end(depth);
101
+ var getChildDom = function getChildDom(nodePos) {
102
+ try {
103
+ var dom = view.nodeDOM(nodePos);
104
+ return dom instanceof Element ? dom : null;
105
+ } catch (_unused2) {
106
+ return null;
107
+ }
108
+ };
109
+ var valign = (_columnNode$attrs = columnNode.attrs) === null || _columnNode$attrs === void 0 ? void 0 : _columnNode$attrs.valign;
110
+ var isNonTopAligned = valign && valign !== 'top';
111
+
112
+ // Use the column rect (not child rects) for above/below detection: it stays stable as
113
+ // gap-cursor widgets shift child DOM positions between repeated clicks.
114
+ var columnRect = columnEl.getBoundingClientRect();
115
+
116
+ // Click ABOVE the first child (only for middle/bottom-aligned columns).
117
+ var firstChildPos = columnContentStart;
118
+ var firstChildDom = getChildDom(firstChildPos);
119
+ if (isNonTopAligned && firstChildDom) {
120
+ var rect = firstChildDom.getBoundingClientRect();
121
+ if (event.clientY < rect.top && event.clientY >= columnRect.top) {
122
+ return {
123
+ pos: firstChildPos,
124
+ side: 'left'
125
+ };
126
+ }
127
+ }
128
+
129
+ // Click BELOW the last child (for any column alignment).
130
+ var lastChild = columnNode.lastChild;
131
+ var lastChildEndPos = columnEndPos;
132
+ var lastChildStartPos = lastChild ? lastChildEndPos - lastChild.nodeSize : columnContentStart;
133
+ var lastChildDom = lastChild ? getChildDom(lastChildStartPos) : null;
134
+ if (lastChild && lastChildDom) {
135
+ var _rect = lastChildDom.getBoundingClientRect();
136
+ if (event.clientY > _rect.bottom && event.clientY <= columnRect.bottom) {
137
+ return {
138
+ pos: lastChildEndPos,
139
+ side: 'right'
140
+ };
141
+ }
142
+ }
143
+
144
+ // Fallback: click lands ON a single atomic child that fills the column (mediaSingle/expand),
145
+ // so the above/below checks never fired.
146
+ if (columnNode.childCount === 1) {
147
+ var onlyChild = columnNode.firstChild;
148
+ // Exclude `panel`: its wrapper makes `view.nodeDOM` non-null and intercepts clicks, so the
149
+ // guard below would wrongly fire for in-panel blank-space clicks (which have their own
150
+ // native gap cursor).
151
+ if (onlyChild && onlyChild.type.name !== 'paragraph' && onlyChild.type.name !== 'panel') {
152
+ // Bail when the click is on the child's own content. For media the wrapper is full-width
153
+ // so test against the <img> rect; resolve it only for a direct mediaSingle child (else
154
+ // getContentRect could grab an image nested in an expand and break its toggle).
155
+ var contentRect = onlyChild.type.name === 'mediaSingle' ? getContentRect(firstChildDom) : null;
156
+ if (contentRect) {
157
+ var insideImage = event.clientX >= contentRect.left && event.clientX <= contentRect.right && event.clientY >= contentRect.top && event.clientY <= contentRect.bottom;
158
+ if (insideImage) {
159
+ return undefined;
160
+ }
161
+ } else {
162
+ // Other atomics: bail when posAtCoords resolves strictly inside the node range.
163
+ var coordPos = null;
164
+ try {
165
+ coordPos = view.posAtCoords({
166
+ left: event.clientX,
167
+ top: event.clientY
168
+ });
169
+ } catch (_unused3) {
170
+ coordPos = null;
171
+ }
172
+ if (coordPos && coordPos.pos > firstChildPos && coordPos.pos < lastChildEndPos) {
173
+ return undefined;
174
+ }
175
+ }
176
+
177
+ // Fire when the child DOM is resolvable, or when it's null (media not yet loaded) but
178
+ // the click target is the column itself (no node view intercepted it).
179
+ var targetEl = event.target;
180
+ var targetIsColumn = targetEl === columnEl;
181
+ var shouldUseFallback = firstChildDom !== null || targetIsColumn;
182
+ if (shouldUseFallback) {
183
+ var side = getGapCursorSideForBlankSpaceClick(firstChildDom, columnRect, event.clientX, event.clientY);
184
+ return side === 'left' ? {
185
+ pos: firstChildPos,
186
+ side: 'left'
187
+ } : {
188
+ pos: lastChildEndPos,
189
+ side: 'right'
190
+ };
191
+ }
192
+ }
193
+ }
194
+ return undefined;
195
+ };
196
+
197
+ /**
198
+ * The tight `<img>` content rect (or `null`). The outer wrapper often fills the whole column
199
+ * width, so the `<img>` rect is needed to tell "beside the image" from "on the image".
200
+ */
201
+ var getContentRect = function getContentRect(firstChildDom) {
202
+ var img = firstChildDom === null || firstChildDom === void 0 ? void 0 : firstChildDom.querySelector('img');
203
+ return img ? img.getBoundingClientRect() : null;
204
+ };
205
+
206
+ /**
207
+ * Which side of an atomic child a blank-space click belongs to. Prefers the tight content (image)
208
+ * rect when available — using its midpoint so it's direction-agnostic (handles RTL right-aligned
209
+ * images) — otherwise falls back to the column's vertical midpoint.
210
+ */
211
+ var getGapCursorSideForBlankSpaceClick = function getGapCursorSideForBlankSpaceClick(firstChildDom, columnRect, clientX, clientY) {
212
+ var contentRect = getContentRect(firstChildDom);
213
+ if (contentRect) {
214
+ if (clientY < contentRect.top) {
215
+ return 'left';
216
+ }
217
+ if (clientY > contentRect.bottom) {
218
+ return 'right';
219
+ }
220
+ if (clientX < (contentRect.left + contentRect.right) / 2) {
221
+ return 'left';
222
+ }
223
+ return 'right';
224
+ }
225
+ var columnMidY = columnRect.top + columnRect.height / 2;
226
+ return clientY < columnMidY ? 'left' : 'right';
227
+ };
228
+
229
+ /**
230
+ * True when the blank-space click target child is a paragraph, so the caller uses a TextSelection
231
+ * instead of a gap cursor. LEFT inspects the first child, RIGHT the last child.
232
+ */
233
+ export var isParagraphBlankSpaceTarget = function isParagraphBlankSpaceTarget(view, gapTarget) {
234
+ var pos = gapTarget.pos,
235
+ side = gapTarget.side;
236
+ var doc = view.state.doc;
237
+ try {
238
+ var $pos = doc.resolve(pos);
239
+ var childNode = side === 'left' ? $pos.nodeAfter : $pos.nodeBefore;
240
+ return (childNode === null || childNode === void 0 ? void 0 : childNode.type.name) === 'paragraph';
241
+ } catch (_unused4) {
242
+ return false;
243
+ }
52
244
  };
@@ -11,3 +11,22 @@ export declare const getMaybeLayoutSection: (state: EditorState) => ContentNodeW
11
11
  * @returns Transaction or undefined
12
12
  */
13
13
  export declare const selectIntoLayout: (view: EditorView, posOfLayout: number, childIndex?: number) => Transaction | undefined;
14
+ export type GapCursorTarget = {
15
+ pos: number;
16
+ side: 'left' | 'right';
17
+ };
18
+ /**
19
+ * For a blank-space click inside a layout column — above the first child (middle/bottom-aligned
20
+ * columns) or below the last child (any alignment) — return the ProseMirror position and side
21
+ * for a gap cursor. Returns `undefined` when the kill switch is ON, the click is outside a
22
+ * layoutColumn, or the Y coordinate is not in blank space.
23
+ *
24
+ * The `advanced_layouts` / `platform_editor_layout_column_menu` gates live in the caller
25
+ * (`applyBlankSpaceGapCursor`); only the kill switch is checked here.
26
+ */
27
+ export declare const getGapCursorTargetForBlankSpaceClick: (view: EditorView, event: MouseEvent) => GapCursorTarget | undefined;
28
+ /**
29
+ * True when the blank-space click target child is a paragraph, so the caller uses a TextSelection
30
+ * instead of a gap cursor. LEFT inspects the first child, RIGHT the last child.
31
+ */
32
+ export declare const isParagraphBlankSpaceTarget: (view: EditorView, gapTarget: GapCursorTarget) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-layout",
3
- "version": "13.2.2",
3
+ "version": "13.2.3",
4
4
  "description": "Layout plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -49,7 +49,7 @@
49
49
  "bind-event-listener": "^3.0.0"
50
50
  },
51
51
  "peerDependencies": {
52
- "@atlaskit/editor-common": "^116.12.0",
52
+ "@atlaskit/editor-common": "^116.13.0",
53
53
  "react": "^18.2.0",
54
54
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
55
55
  },