@atlaskit/editor-plugin-layout 8.0.22 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -321,6 +321,11 @@ function forceColumnWidths(state, tr, pos, presetLayout) {
321
321
  }
322
322
  return tr.replaceWith(pos + 1, pos + node.nodeSize - 1, columnWidth(node, state.schema, getWidthsForPreset(presetLayout)));
323
323
  }
324
+
325
+ /**
326
+ * Forces a layout section node to match the given preset layout by adjusting
327
+ * its column structure and widths, then restoring the original selection.
328
+ */
324
329
  export function forceSectionToPresetLayout(state, node, pos, presetLayout) {
325
330
  var forceColumnStructureFn = editorExperiment('advanced_layouts', true) ? forceColumnStructureNew : forceColumnStructure;
326
331
  var tr = forceColumnStructureFn(state, node, pos, presetLayout);
@@ -372,6 +377,13 @@ export var setPresetLayout = function setPresetLayout(editorAnalyticsAPI) {
372
377
  };
373
378
  function layoutNeedChanges(node) {
374
379
  if (editorExperiment('advanced_layouts', true)) {
380
+ if (editorExperiment('platform_editor_layout_column_resize_handle', true)) {
381
+ // Custom widths that sum to 100% are valid and should not be forced back to presets
382
+ if (isValidLayoutWidthDistributions(node)) {
383
+ return false;
384
+ }
385
+ return true;
386
+ }
375
387
  return !getPresetLayout(node) || !isValidLayoutWidthDistributions(node);
376
388
  }
377
389
  return !getPresetLayout(node);
@@ -452,6 +464,11 @@ export var fixColumnStructure = function fixColumnStructure(state) {
452
464
  var node = state.doc.nodeAt(pos);
453
465
  if (node) {
454
466
  if (node.childCount !== getWidthsForPreset(selectedLayout).length) {
467
+ // If the resize handle experiment is on and widths are valid, don't force preset
468
+ // (column count mismatch might be from a different preset being selected)
469
+ if (editorExperiment('advanced_layouts', true) && editorExperiment('platform_editor_layout_column_resize_handle', true) && isValidLayoutWidthDistributions(node)) {
470
+ return;
471
+ }
455
472
  return forceSectionToPresetLayout(state, node, pos, selectedLayout);
456
473
  }
457
474
  if (!isValidLayoutWidthDistributions(node) && editorExperiment('advanced_layouts', true)) {
@@ -0,0 +1,357 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ 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; }
3
+ 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; }
4
+ import { bind } from 'bind-event-listener';
5
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
6
+ import { Decoration } from '@atlaskit/editor-prosemirror/view';
7
+ // Class names for the column resize divider widget — must stay in sync with layout.ts in editor-core
8
+ var layoutColumnDividerClassName = 'layout-column-divider';
9
+ var layoutColumnDividerRailClassName = 'layout-column-divider-rail';
10
+ var layoutColumnDividerThumbClassName = 'layout-column-divider-thumb';
11
+
12
+ // Minimum column width percentage to prevent columns from collapsing
13
+ var MIN_COLUMN_WIDTH_PERCENT = 5;
14
+
15
+ // Module-level drag state so it survives widget DOM recreation during transactions.
16
+ var dragState = null;
17
+
18
+ /**
19
+ * Dispatches a single undoable ProseMirror transaction to commit the final
20
+ * column widths after a drag completes.
21
+ */
22
+ var dispatchColumnWidths = function dispatchColumnWidths(view, sectionPos, leftColIndex, leftWidth, rightWidth) {
23
+ var state = view.state;
24
+ var sectionNode = state.doc.nodeAt(sectionPos);
25
+ if (!sectionNode) {
26
+ return;
27
+ }
28
+ var layoutColumn = state.schema.nodes.layoutColumn;
29
+ var tr = state.tr;
30
+ var newColumns = [];
31
+ sectionNode.forEach(function (child, _offset, index) {
32
+ if (child.type === layoutColumn) {
33
+ var newWidth = child.attrs.width;
34
+ if (index === leftColIndex) {
35
+ newWidth = Number(leftWidth.toFixed(2));
36
+ } else if (index === leftColIndex + 1) {
37
+ newWidth = Number(rightWidth.toFixed(2));
38
+ }
39
+ newColumns.push(layoutColumn.create(_objectSpread(_objectSpread({}, child.attrs), {}, {
40
+ width: newWidth
41
+ }), child.content, child.marks));
42
+ } else {
43
+ newColumns.push(child);
44
+ }
45
+ });
46
+ tr.replaceWith(sectionPos + 1, sectionPos + sectionNode.nodeSize - 1, Fragment.from(newColumns));
47
+ tr.setMeta('layoutColumnResize', true);
48
+ tr.setMeta('scrollIntoView', false);
49
+ view.dispatch(tr);
50
+ };
51
+
52
+ /**
53
+ * Calculates new column widths from the current mouse X position during drag.
54
+ * Uses the columns-only width cached at mousedown — no layout reflow.
55
+ *
56
+ * The denominator is `columnsWidth` (the total flex container width minus
57
+ * divider widgets and flex gaps) so that a 1 px mouse movement corresponds
58
+ * to the exact same visual shift in the column boundary, eliminating the
59
+ * few-pixel drift that occurred when using the full section width.
60
+ */
61
+ var calcDragWidths = function calcDragWidths(clientX) {
62
+ if (!dragState) {
63
+ return null;
64
+ }
65
+ var _dragState = dragState,
66
+ columnsWidth = _dragState.columnsWidth;
67
+ if (columnsWidth === 0) {
68
+ return null;
69
+ }
70
+ var deltaX = clientX - dragState.startX;
71
+ var combinedWidth = dragState.startLeftWidth + dragState.startRightWidth;
72
+ var deltaPercent = deltaX / columnsWidth * 100;
73
+ var leftWidth = dragState.startLeftWidth + deltaPercent;
74
+ 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;
81
+ }
82
+ return {
83
+ leftWidth: leftWidth,
84
+ rightWidth: rightWidth
85
+ };
86
+ };
87
+ var onDragMouseMove = function onDragMouseMove(e) {
88
+ if (!dragState) {
89
+ return;
90
+ }
91
+
92
+ // If the mouse button was released outside the window (e.g. over browser chrome
93
+ // or an iframe), we won't receive a mouseup on ownerDoc. Detect this by checking
94
+ // whether any button is still held — if not, treat it as a drag end.
95
+ if (e.buttons === 0) {
96
+ onDragEnd(e.clientX);
97
+ return;
98
+ }
99
+
100
+ // Always capture the latest clientX so the rAF callback uses the most recent
101
+ // mouse position. Previously, intermediate positions were dropped when an rAF
102
+ // was already scheduled, causing the column boundary to lag behind the cursor.
103
+ dragState.lastClientX = e.clientX;
104
+
105
+ // If a paint frame is already scheduled it will pick up lastClientX — no need
106
+ // to schedule another one.
107
+ if (dragState.rafId !== null) {
108
+ return;
109
+ }
110
+ dragState.rafId = requestAnimationFrame(function () {
111
+ if (!dragState) {
112
+ return;
113
+ }
114
+ dragState.rafId = null;
115
+ var widths = calcDragWidths(dragState.lastClientX);
116
+ if (!widths) {
117
+ return;
118
+ }
119
+
120
+ // Write flex-basis directly onto the column elements' inline styles for immediate
121
+ // visual feedback. This beats PM's own inline flex-basis value without dispatching
122
+ // any PM transaction — keeping drag completely off the ProseMirror render path.
123
+ // The LayoutColumnView.ignoreMutation implementation ensures PM's MutationObserver
124
+ // does not revert these style changes mid-drag.
125
+ dragState.hasDragged = true;
126
+ dragState.leftColEl.style.flexBasis = "".concat(widths.leftWidth, "%");
127
+ dragState.rightColEl.style.flexBasis = "".concat(widths.rightWidth, "%");
128
+ });
129
+ };
130
+
131
+ /**
132
+ * Shared teardown for all drag-end paths (mouseup, missed mouseup detected via
133
+ * e.buttons===0 on mousemove, window blur, and visibilitychange). Commits the
134
+ * final column widths if a real drag occurred.
135
+ */
136
+ var onDragEnd = function onDragEnd(clientX) {
137
+ if (!dragState) {
138
+ return;
139
+ }
140
+ var _dragState2 = dragState,
141
+ view = _dragState2.view,
142
+ sectionPos = _dragState2.sectionPos,
143
+ leftColIndex = _dragState2.leftColIndex,
144
+ leftColEl = _dragState2.leftColEl,
145
+ rightColEl = _dragState2.rightColEl,
146
+ hasDragged = _dragState2.hasDragged,
147
+ rafId = _dragState2.rafId,
148
+ startLeftWidth = _dragState2.startLeftWidth,
149
+ startRightWidth = _dragState2.startRightWidth,
150
+ unbindListeners = _dragState2.unbindListeners;
151
+ unbindListeners();
152
+
153
+ // Cancel any pending rAF so a stale frame doesn't write styles after teardown.
154
+ if (rafId !== null) {
155
+ cancelAnimationFrame(rafId);
156
+ }
157
+ var ownerDoc = view.dom.ownerDocument;
158
+ ownerDoc.body.style.userSelect = '';
159
+ ownerDoc.body.style.cursor = '';
160
+ var widths = calcDragWidths(clientX);
161
+ dragState = null;
162
+ if (!hasDragged) {
163
+ // The user clicked without dragging — no flex-basis overrides were written,
164
+ // so there is nothing to clean up and no transaction to dispatch.
165
+ return;
166
+ }
167
+
168
+ // Clear the drag-time flex-basis overrides. The PM transaction below will
169
+ // write the committed widths back into the node attrs and re-render the DOM.
170
+ leftColEl.style.flexBasis = '';
171
+ rightColEl.style.flexBasis = '';
172
+ if (widths && (widths.leftWidth !== startLeftWidth || widths.rightWidth !== startRightWidth)) {
173
+ dispatchColumnWidths(view, sectionPos, leftColIndex, widths.leftWidth, widths.rightWidth);
174
+ }
175
+ };
176
+ var onDragMouseUp = function onDragMouseUp(e) {
177
+ onDragEnd(e.clientX);
178
+ };
179
+
180
+ /**
181
+ * Called when the window loses focus (blur) or the tab becomes hidden
182
+ * (visibilitychange). In either case the user can't be dragging anymore,
183
+ * so we commit the drag at the last known mouse position.
184
+ */
185
+ var onDragCancel = function onDragCancel() {
186
+ if (!dragState) {
187
+ return;
188
+ }
189
+ // Commit at the last captured mouse position rather than startX, so the
190
+ // columns stay where the user last saw them.
191
+ onDragEnd(dragState.lastClientX);
192
+ };
193
+
194
+ /**
195
+ * Creates a column divider widget DOM element with drag-to-resize interaction for
196
+ * the adjacent layout columns. During drag, flex-basis is mutated directly on the
197
+ * column DOM elements for zero-overhead visual feedback (no PM transactions).
198
+ * A single undoable PM transaction is dispatched on mouseup to commit the final widths.
199
+ */
200
+ var createColumnDividerWidget = function createColumnDividerWidget(view, sectionPos, columnIndex // index of the column to the RIGHT of this divider
201
+ ) {
202
+ var ownerDoc = view.dom.ownerDocument;
203
+
204
+ // Outer container: wide transparent hit area for easy grabbing, zero flex footprint
205
+ var divider = ownerDoc.createElement('div');
206
+ divider.classList.add(layoutColumnDividerClassName);
207
+ divider.contentEditable = 'false';
208
+
209
+ // Rail: styled via layoutColumnDividerStyles in layout.ts
210
+ var rail = ownerDoc.createElement('div');
211
+ rail.classList.add(layoutColumnDividerRailClassName);
212
+ divider.appendChild(rail);
213
+
214
+ // Thumb: styled via layoutColumnDividerStyles in layout.ts
215
+ var thumb = ownerDoc.createElement('div');
216
+ thumb.classList.add(layoutColumnDividerThumbClassName);
217
+ rail.appendChild(thumb);
218
+ var leftColIndex = columnIndex - 1;
219
+ bind(divider, {
220
+ type: 'mousedown',
221
+ listener: function listener(e) {
222
+ var _ownerDoc$defaultView;
223
+ e.preventDefault();
224
+ e.stopPropagation();
225
+ var sectionNode = view.state.doc.nodeAt(sectionPos);
226
+ if (!sectionNode) {
227
+ return;
228
+ }
229
+
230
+ // Get the initial widths of the two adjacent columns
231
+ var leftCol = null;
232
+ var rightCol = null;
233
+ sectionNode.forEach(function (child, _offset, index) {
234
+ if (index === leftColIndex) {
235
+ leftCol = child;
236
+ }
237
+ if (index === leftColIndex + 1) {
238
+ rightCol = child;
239
+ }
240
+ });
241
+ if (!leftCol || !rightCol) {
242
+ return;
243
+ }
244
+ var sectionElement = divider.closest('[data-layout-section]');
245
+ if (!(sectionElement instanceof HTMLElement)) {
246
+ return;
247
+ }
248
+
249
+ // Capture the two adjacent column DOM elements upfront so mousemove can
250
+ // mutate their flex-basis directly without any PM transaction overhead.
251
+ // Query by data-layout-column-index (stamped by LayoutColumnView) rather than
252
+ // relying on positional order of [data-layout-column] elements, which would
253
+ // break if the DOM structure or ordering ever changes.
254
+ var leftColEl = sectionElement.querySelector("[data-layout-column-index=\"".concat(leftColIndex, "\"]"));
255
+ var rightColEl = sectionElement.querySelector("[data-layout-column-index=\"".concat(leftColIndex + 1, "\"]"));
256
+ if (!leftColEl || !rightColEl) {
257
+ return;
258
+ }
259
+ var unbindMove = bind(ownerDoc, {
260
+ type: 'mousemove',
261
+ listener: onDragMouseMove
262
+ });
263
+ var unbindUp = bind(ownerDoc, {
264
+ type: 'mouseup',
265
+ listener: onDragMouseUp
266
+ });
267
+ // If the user releases the mouse outside the browser window (e.g. over the
268
+ // OS desktop) and then brings the cursor back, we won't get a mouseup on
269
+ // ownerDoc. Listening on window for blur and on the document for
270
+ // visibilitychange catches tab switches and window focus loss respectively.
271
+ var unbindBlur = bind((_ownerDoc$defaultView = ownerDoc.defaultView) !== null && _ownerDoc$defaultView !== void 0 ? _ownerDoc$defaultView : window, {
272
+ type: 'blur',
273
+ listener: onDragCancel
274
+ });
275
+ var unbindVisibility = bind(ownerDoc, {
276
+ type: 'visibilitychange',
277
+ listener: onDragCancel
278
+ });
279
+
280
+ // Compute the width available to columns only (excluding divider widgets and
281
+ // flex gaps). Using this as the denominator ensures that a 1 px mouse delta
282
+ // translates to the exact pixel shift on the column boundary.
283
+ var sectionRect = sectionElement.getBoundingClientRect();
284
+ var dividers = sectionElement.querySelectorAll(".".concat(layoutColumnDividerClassName));
285
+ var dividersWidth = 0;
286
+ dividers.forEach(function (d) {
287
+ dividersWidth += d.getBoundingClientRect().width;
288
+ });
289
+ // Account for CSS gap between flex children. The gap is applied between
290
+ // every pair of direct children (columns + divider widgets).
291
+ var computedGap = parseFloat(getComputedStyle(sectionElement).gap || '0');
292
+ var childCount = sectionElement.children.length;
293
+ var totalGap = childCount > 1 ? computedGap * (childCount - 1) : 0;
294
+ var columnsWidth = sectionRect.width - dividersWidth - totalGap;
295
+ dragState = {
296
+ hasDragged: false,
297
+ lastClientX: e.clientX,
298
+ rafId: null,
299
+ view: view,
300
+ sectionPos: sectionPos,
301
+ leftColIndex: leftColIndex,
302
+ leftColEl: leftColEl,
303
+ rightColEl: rightColEl,
304
+ startX: e.clientX,
305
+ startLeftWidth: leftCol.attrs.width,
306
+ startRightWidth: rightCol.attrs.width,
307
+ columnsWidth: columnsWidth,
308
+ sectionElement: sectionElement,
309
+ unbindListeners: function unbindListeners() {
310
+ unbindMove();
311
+ unbindUp();
312
+ unbindBlur();
313
+ unbindVisibility();
314
+ }
315
+ };
316
+ ownerDoc.body.style.userSelect = 'none';
317
+ ownerDoc.body.style.cursor = 'col-resize';
318
+ }
319
+ });
320
+ return divider;
321
+ };
322
+
323
+ /**
324
+ * Returns ProseMirror Decoration widgets for column dividers between layout columns.
325
+ * Each divider supports drag-to-resize interaction for the adjacent columns.
326
+ */
327
+ export var getColumnDividerDecorations = function getColumnDividerDecorations(state, view) {
328
+ var decorations = [];
329
+ if (!view) {
330
+ return decorations;
331
+ }
332
+ var layoutSection = state.schema.nodes.layoutSection;
333
+ state.doc.descendants(function (node, pos) {
334
+ if (node.type === layoutSection) {
335
+ // Walk through layout column children and add dividers between them
336
+ node.forEach(function (child, offset, index) {
337
+ // Add a divider widget BEFORE every column except the first
338
+ if (index > 0) {
339
+ var sectionPos = pos;
340
+ var colIndex = index;
341
+ var widgetPos = pos + offset + 1; // position at the start of this column
342
+ decorations.push(Decoration.widget(widgetPos, function () {
343
+ return createColumnDividerWidget(view, sectionPos, colIndex);
344
+ }, {
345
+ side: -1,
346
+ // place before the position
347
+ key: "layout-col-divider-".concat(pos, "-").concat(index),
348
+ ignoreSelection: true
349
+ }));
350
+ }
351
+ });
352
+ return false; // don't descend into children
353
+ }
354
+ return true; // continue descending
355
+ });
356
+ return decorations;
357
+ };
@@ -1,3 +1,4 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
1
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
3
  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; }
3
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; }
@@ -11,6 +12,7 @@ import { findParentNodeClosestToPos, findParentNodeOfType } from '@atlaskit/edit
11
12
  import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
12
13
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
13
14
  import { fixColumnSizes, fixColumnStructure, getSelectedLayout } from './actions';
15
+ import { getColumnDividerDecorations } from './column-resize-divider';
14
16
  import { EVEN_DISTRIBUTED_COL_WIDTHS } from './consts';
15
17
  import { pluginKey } from './plugin-key';
16
18
  import { getMaybeLayoutSection } from './utils';
@@ -91,8 +93,21 @@ var handleDeleteLayoutColumn = function handleDeleteLayoutColumn(state, dispatch
91
93
  return false;
92
94
  };
93
95
  export default (function (options) {
96
+ // Store a reference to the EditorView so widget decorations can dispatch transactions
97
+ var editorViewRef;
94
98
  return new SafePlugin({
95
99
  key: pluginKey,
100
+ view: function view(_view) {
101
+ editorViewRef = _view;
102
+ return {
103
+ update: function update(updatedView) {
104
+ editorViewRef = updatedView;
105
+ },
106
+ destroy: function destroy() {
107
+ editorViewRef = undefined;
108
+ }
109
+ };
110
+ },
96
111
  state: {
97
112
  init: function init(_, state) {
98
113
  return getInitialPluginState(options, state);
@@ -120,6 +135,15 @@ export default (function (options) {
120
135
  props: {
121
136
  decorations: function decorations(state) {
122
137
  var layoutState = pluginKey.getState(state);
138
+ if (editorExperiment('advanced_layouts', true) && editorExperiment('platform_editor_layout_column_resize_handle', true)) {
139
+ var dividerDecorations = getColumnDividerDecorations(state, editorViewRef);
140
+ var selectedDecorations = layoutState.pos !== null ? getNodeDecoration(layoutState.pos, state.doc.nodeAt(layoutState.pos)) : [];
141
+ var allDecorations = [].concat(_toConsumableArray(selectedDecorations), _toConsumableArray(dividerDecorations));
142
+ if (allDecorations.length > 0) {
143
+ return DecorationSet.create(state.doc, allDecorations);
144
+ }
145
+ return undefined;
146
+ }
123
147
  if (layoutState.pos !== null) {
124
148
  return DecorationSet.create(state.doc, getNodeDecoration(layoutState.pos, state.doc.nodeAt(layoutState.pos)));
125
149
  }
@@ -157,6 +181,11 @@ export default (function (options) {
157
181
  if (!prevTr.docChanged) {
158
182
  return;
159
183
  }
184
+
185
+ // Skip fixing column sizes for column resize drag transactions
186
+ if (editorExperiment('platform_editor_layout_column_resize_handle', true) && prevTr.getMeta('layoutColumnResize')) {
187
+ return;
188
+ }
160
189
  var change = fixColumnSizes(prevTr, newState);
161
190
  if (change) {
162
191
  changes.push(change);
@@ -1,12 +1,74 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
+ import _createClass from "@babel/runtime/helpers/createClass";
4
+ 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; }
5
+ 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; }
1
6
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
7
+ import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
2
8
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
9
+ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
3
10
  import { LayoutSectionView } from '../nodeviews';
4
11
  export var pluginKey = new PluginKey('layoutResizingPlugin');
12
+
13
+ /**
14
+ * Minimal node view for layoutColumn that delegates all DOM serialization to the
15
+ * NodeSpec's own toDOM, but overrides ignoreMutation to suppress style attribute
16
+ * mutations from ProseMirror's MutationObserver.
17
+ *
18
+ * This is necessary so that direct inline style mutations during column drag
19
+ * (e.g. setting flex-basis to give real-time visual feedback without dispatching
20
+ * PM transactions) are not "corrected" back by ProseMirror's DOM reconciliation.
21
+ */
22
+ var LayoutColumnView = /*#__PURE__*/function () {
23
+ function LayoutColumnView(node, view, getPos) {
24
+ _classCallCheck(this, LayoutColumnView);
25
+ // Use the NodeSpec's own toDOM to produce the correct DOM structure and attributes.
26
+ var nodeType = view.state.schema.nodes[node.type.name];
27
+
28
+ // Fallback: create a plain div so PM always has a valid DOM node to work with.
29
+ // This path should never be reached in practice — layoutColumn always has a toDOM.
30
+ if (!nodeType.spec.toDOM) {
31
+ var fallbackDiv = document.createElement('div');
32
+ this.dom = fallbackDiv;
33
+ this.contentDOM = fallbackDiv;
34
+ return;
35
+ }
36
+ var _DOMSerializer$render = DOMSerializer.renderSpec(document, nodeType.spec.toDOM(node)),
37
+ dom = _DOMSerializer$render.dom,
38
+ contentDOM = _DOMSerializer$render.contentDOM;
39
+ if (!(dom instanceof HTMLElement) || !(contentDOM instanceof HTMLElement)) {
40
+ var _fallbackDiv = document.createElement('div');
41
+ this.dom = _fallbackDiv;
42
+ this.contentDOM = _fallbackDiv;
43
+ return;
44
+ }
45
+ this.dom = dom;
46
+ this.contentDOM = contentDOM;
47
+
48
+ // Stamp the column's index within its parent section onto the DOM element so that
49
+ // column-resize-divider can query columns by index rather than relying on positional
50
+ // order of [data-layout-column] elements (which could break if the DOM structure changes).
51
+ var pos = getPos();
52
+ if (pos !== undefined) {
53
+ var $pos = view.state.doc.resolve(pos);
54
+ this.dom.setAttribute('data-layout-column-index', String($pos.index()));
55
+ }
56
+ }
57
+ return _createClass(LayoutColumnView, [{
58
+ key: "ignoreMutation",
59
+ value: function ignoreMutation(mutation) {
60
+ // Ignore style attribute mutations — these are direct DOM writes during column drag
61
+ // (setting flex-basis for real-time resize feedback). Without this, PM's
62
+ // MutationObserver would immediately revert our style changes.
63
+ return mutation.type === 'attributes' && mutation.attributeName === 'style';
64
+ }
65
+ }]);
66
+ }();
5
67
  export default (function (options, pluginInjectionApi, portalProviderAPI, eventDispatcher) {
6
68
  return new SafePlugin({
7
69
  key: pluginKey,
8
70
  props: {
9
- nodeViews: {
71
+ nodeViews: _objectSpread({
10
72
  layoutSection: function layoutSection(node, view, getPos) {
11
73
  return new LayoutSectionView({
12
74
  node: node,
@@ -18,7 +80,11 @@ export default (function (options, pluginInjectionApi, portalProviderAPI, eventD
18
80
  options: options
19
81
  }).init();
20
82
  }
21
- }
83
+ }, editorExperiment('platform_editor_layout_column_resize_handle', true) ? {
84
+ layoutColumn: function layoutColumn(node, view, getPos) {
85
+ return new LayoutColumnView(node, view, getPos);
86
+ }
87
+ } : {})
22
88
  }
23
89
  });
24
90
  });
@@ -51,8 +51,8 @@ export declare class LayoutSectionView extends ReactNodeView<LayoutSectionViewPr
51
51
  * @returns
52
52
  */
53
53
  getContentDOM(): {
54
- dom: HTMLElement;
55
54
  contentDOM: HTMLElement | undefined;
55
+ dom: HTMLElement;
56
56
  };
57
57
  /**
58
58
  * setDomAttrs
@@ -17,6 +17,10 @@ export declare const createMultiColumnLayoutSection: (state: EditorState, number
17
17
  export declare const createDefaultLayoutSection: (state: EditorState) => Node;
18
18
  export declare const insertLayoutColumns: Command;
19
19
  export declare const insertLayoutColumnsWithAnalytics: (editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => (inputMethod: TOOLBAR_MENU_TYPE) => Command;
20
+ /**
21
+ * Forces a layout section node to match the given preset layout by adjusting
22
+ * its column structure and widths, then restoring the original selection.
23
+ */
20
24
  export declare function forceSectionToPresetLayout(state: EditorState, node: Node, pos: number, presetLayout: PresetLayout): Transaction;
21
25
  export declare const setPresetLayout: (editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => (layout: PresetLayout) => Command;
22
26
  export declare const fixColumnSizes: (changedTr: Transaction, state: EditorState) => Change | undefined;
@@ -0,0 +1,8 @@
1
+ import type { EditorState } from '@atlaskit/editor-prosemirror/state';
2
+ import { Decoration } from '@atlaskit/editor-prosemirror/view';
3
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
4
+ /**
5
+ * Returns ProseMirror Decoration widgets for column dividers between layout columns.
6
+ * Each divider supports drag-to-resize interaction for the adjacent columns.
7
+ */
8
+ export declare const getColumnDividerDecorations: (state: EditorState, view?: EditorView) => Decoration[];
@@ -51,8 +51,8 @@ export declare class LayoutSectionView extends ReactNodeView<LayoutSectionViewPr
51
51
  * @returns
52
52
  */
53
53
  getContentDOM(): {
54
- dom: HTMLElement;
55
54
  contentDOM: HTMLElement | undefined;
55
+ dom: HTMLElement;
56
56
  };
57
57
  /**
58
58
  * setDomAttrs
@@ -17,6 +17,10 @@ export declare const createMultiColumnLayoutSection: (state: EditorState, number
17
17
  export declare const createDefaultLayoutSection: (state: EditorState) => Node;
18
18
  export declare const insertLayoutColumns: Command;
19
19
  export declare const insertLayoutColumnsWithAnalytics: (editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => (inputMethod: TOOLBAR_MENU_TYPE) => Command;
20
+ /**
21
+ * Forces a layout section node to match the given preset layout by adjusting
22
+ * its column structure and widths, then restoring the original selection.
23
+ */
20
24
  export declare function forceSectionToPresetLayout(state: EditorState, node: Node, pos: number, presetLayout: PresetLayout): Transaction;
21
25
  export declare const setPresetLayout: (editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => (layout: PresetLayout) => Command;
22
26
  export declare const fixColumnSizes: (changedTr: Transaction, state: EditorState) => Change | undefined;
@@ -0,0 +1,8 @@
1
+ import type { EditorState } from '@atlaskit/editor-prosemirror/state';
2
+ import { Decoration } from '@atlaskit/editor-prosemirror/view';
3
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
4
+ /**
5
+ * Returns ProseMirror Decoration widgets for column dividers between layout columns.
6
+ * Each divider supports drag-to-resize interaction for the adjacent columns.
7
+ */
8
+ export declare const getColumnDividerDecorations: (state: EditorState, view?: EditorView) => Decoration[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-layout",
3
- "version": "8.0.22",
3
+ "version": "8.1.0",
4
4
  "description": "Layout plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -44,15 +44,16 @@
44
44
  "@atlaskit/editor-shared-styles": "^3.10.0",
45
45
  "@atlaskit/editor-toolbar": "^0.20.0",
46
46
  "@atlaskit/icon": "^34.0.0",
47
- "@atlaskit/icon-lab": "^6.2.0",
47
+ "@atlaskit/icon-lab": "^6.4.0",
48
48
  "@atlaskit/platform-feature-flags": "^1.1.0",
49
- "@atlaskit/tmp-editor-statsig": "^54.0.0",
49
+ "@atlaskit/tmp-editor-statsig": "^54.4.0",
50
50
  "@atlaskit/tokens": "^11.4.0",
51
51
  "@babel/runtime": "^7.0.0",
52
- "@emotion/react": "^11.7.1"
52
+ "@emotion/react": "^11.7.1",
53
+ "bind-event-listener": "^3.0.0"
53
54
  },
54
55
  "peerDependencies": {
55
- "@atlaskit/editor-common": "^112.16.0",
56
+ "@atlaskit/editor-common": "^112.18.0",
56
57
  "react": "^18.2.0",
57
58
  "react-intl-next": "npm:react-intl@^5.18.1"
58
59
  },