@atlaskit/editor-plugin-layout 11.0.2 → 11.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/layoutPlugin.js +18 -16
  3. package/dist/cjs/nodeviews/index.js +49 -1
  4. package/dist/cjs/pm-plugins/column-resize-divider.js +25 -7
  5. package/dist/cjs/pm-plugins/main.js +2 -2
  6. package/dist/cjs/pm-plugins/resizing.js +29 -4
  7. package/dist/cjs/ui/LayoutSSRReactContextsProvider.js +34 -0
  8. package/dist/es2019/layoutPlugin.js +20 -16
  9. package/dist/es2019/nodeviews/index.js +49 -1
  10. package/dist/es2019/pm-plugins/column-resize-divider.js +25 -7
  11. package/dist/es2019/pm-plugins/main.js +2 -2
  12. package/dist/es2019/pm-plugins/resizing.js +37 -7
  13. package/dist/es2019/ui/LayoutSSRReactContextsProvider.js +28 -0
  14. package/dist/esm/layoutPlugin.js +18 -16
  15. package/dist/esm/nodeviews/index.js +49 -1
  16. package/dist/esm/pm-plugins/column-resize-divider.js +25 -7
  17. package/dist/esm/pm-plugins/main.js +2 -2
  18. package/dist/esm/pm-plugins/resizing.js +29 -4
  19. package/dist/esm/ui/LayoutSSRReactContextsProvider.js +27 -0
  20. package/dist/types/nodeviews/index.d.ts +5 -0
  21. package/dist/types/pm-plugins/column-resize-divider.d.ts +2 -1
  22. package/dist/types/pm-plugins/main.d.ts +2 -1
  23. package/dist/types/pm-plugins/resizing.d.ts +2 -1
  24. package/dist/types/ui/LayoutSSRReactContextsProvider.d.ts +19 -0
  25. package/dist/types-ts4.5/nodeviews/index.d.ts +5 -0
  26. package/dist/types-ts4.5/pm-plugins/column-resize-divider.d.ts +2 -1
  27. package/dist/types-ts4.5/pm-plugins/main.d.ts +2 -1
  28. package/dist/types-ts4.5/pm-plugins/resizing.d.ts +2 -1
  29. package/dist/types-ts4.5/ui/LayoutSSRReactContextsProvider.d.ts +19 -0
  30. package/package.json +7 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @atlaskit/editor-plugin-layout
2
2
 
3
+ ## 11.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`1f5c61250d103`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1f5c61250d103) -
8
+ Updating layout plugin to support ssr streaming
9
+
10
+ ## 11.0.3
11
+
12
+ ### Patch Changes
13
+
14
+ - [`795424ce08dae`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/795424ce08dae) -
15
+ Add analytics for layout column resizing
16
+ - Updated dependencies
17
+
3
18
  ## 11.0.2
4
19
 
5
20
  ### Patch Changes
@@ -57,7 +57,7 @@ var selectIntoLayoutSection = exports.selectIntoLayoutSection = function selectI
57
57
  return tr;
58
58
  };
59
59
  var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
60
- var _api$analytics, _api$analytics4;
60
+ var _api$analytics2, _api$analytics5;
61
61
  var _ref$config = _ref.config,
62
62
  options = _ref$config === void 0 ? {} : _ref$config,
63
63
  api = _ref.api;
@@ -105,7 +105,8 @@ var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
105
105
  var plugins = [{
106
106
  name: 'layout',
107
107
  plugin: function plugin() {
108
- return (0, _main.default)(options);
108
+ var _api$analytics;
109
+ return (0, _main.default)(options, api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions);
109
110
  }
110
111
  }];
111
112
  if ((options.editorAppearance === 'full-page' || options.editorAppearance === 'full-width' || options.editorAppearance === 'max' && (0, _experiments.editorExperiment)('platform_editor_layout_column_resize_handle', true)) && api && (0, _experiments.editorExperiment)('advanced_layouts', true)) {
@@ -113,15 +114,16 @@ var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
113
114
  name: 'layoutResizing',
114
115
  plugin: function plugin(_ref2) {
115
116
  var portalProviderAPI = _ref2.portalProviderAPI,
116
- eventDispatcher = _ref2.eventDispatcher;
117
- return (0, _resizing.default)(options, api, portalProviderAPI, eventDispatcher);
117
+ eventDispatcher = _ref2.eventDispatcher,
118
+ getIntl = _ref2.getIntl;
119
+ return (0, _resizing.default)(options, api, portalProviderAPI, eventDispatcher, getIntl());
118
120
  }
119
121
  });
120
122
  }
121
123
  return plugins;
122
124
  },
123
125
  actions: {
124
- insertLayoutColumns: (0, _actions.insertLayoutColumnsWithAnalytics)(api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions)
126
+ insertLayoutColumns: (0, _actions.insertLayoutColumnsWithAnalytics)(api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions)
125
127
  },
126
128
  pluginsOptions: {
127
129
  floatingToolbar: function floatingToolbar(state, intl) {
@@ -143,8 +145,8 @@ var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
143
145
  quickInsert: function quickInsert(_ref3) {
144
146
  var formatMessage = _ref3.formatMessage;
145
147
  var withInsertLayoutAnalytics = function withInsertLayoutAnalytics(tr, columnCount) {
146
- var _api$analytics2;
147
- api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 || (_api$analytics2 = _api$analytics2.actions) === null || _api$analytics2 === void 0 || _api$analytics2.attachAnalyticsEvent({
148
+ var _api$analytics3;
149
+ api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 || (_api$analytics3 = _api$analytics3.actions) === null || _api$analytics3 === void 0 || _api$analytics3.attachAnalyticsEvent({
148
150
  action: _analytics.ACTION.INSERTED,
149
151
  actionSubject: _analytics.ACTION_SUBJECT.DOCUMENT,
150
152
  actionSubjectId: _analytics.ACTION_SUBJECT_ID.LAYOUT,
@@ -340,9 +342,9 @@ var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
340
342
  return /*#__PURE__*/_react.default.createElement(_quickInsert.IconLayout, null);
341
343
  },
342
344
  action: function action(insert, state) {
343
- var _api$analytics3;
345
+ var _api$analytics4;
344
346
  var tr = insert((0, _actions.createDefaultLayoutSection)(state));
345
- api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 || (_api$analytics3 = _api$analytics3.actions) === null || _api$analytics3 === void 0 || _api$analytics3.attachAnalyticsEvent({
347
+ api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 || (_api$analytics4 = _api$analytics4.actions) === null || _api$analytics4 === void 0 || _api$analytics4.attachAnalyticsEvent({
346
348
  action: _analytics.ACTION.INSERTED,
347
349
  actionSubject: _analytics.ACTION_SUBJECT.DOCUMENT,
348
350
  actionSubjectId: _analytics.ACTION_SUBJECT_ID.LAYOUT,
@@ -377,18 +379,18 @@ var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
377
379
  return _pluginKey.pluginKey.getState(editorState);
378
380
  },
379
381
  commands: {
380
- deleteLayoutColumn: (0, _actions.deleteLayoutColumn)(api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions, api),
382
+ deleteLayoutColumn: (0, _actions.deleteLayoutColumn)(api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions, api),
381
383
  distributeLayoutColumns: function distributeLayoutColumns(options) {
382
- var _api$analytics5;
383
- return (0, _actions.distributeLayoutColumns)(api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions, api)(options);
384
+ var _api$analytics6;
385
+ return (0, _actions.distributeLayoutColumns)(api === null || api === void 0 || (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions, api)(options);
384
386
  },
385
387
  insertLayoutColumn: function insertLayoutColumn(side) {
386
- var _api$analytics6;
387
- return (0, _actions.insertLayoutColumn)(side, api === null || api === void 0 || (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions, api);
388
+ var _api$analytics7;
389
+ return (0, _actions.insertLayoutColumn)(side, api === null || api === void 0 || (_api$analytics7 = api.analytics) === null || _api$analytics7 === void 0 ? void 0 : _api$analytics7.actions, api);
388
390
  },
389
391
  setLayoutColumnValign: function setLayoutColumnValign(valign) {
390
- var _api$analytics7;
391
- return (0, _actions.setLayoutColumnValign)(valign, api === null || api === void 0 || (_api$analytics7 = api.analytics) === null || _api$analytics7 === void 0 ? void 0 : _api$analytics7.actions, api);
392
+ var _api$analytics8;
393
+ return (0, _actions.setLayoutColumnValign)(valign, api === null || api === void 0 || (_api$analytics8 = api.analytics) === null || _api$analytics8 === void 0 ? void 0 : _api$analytics8.actions, api);
392
394
  },
393
395
  toggleLayoutColumnMenu: _actions.toggleLayoutColumnMenu
394
396
  }
@@ -13,8 +13,9 @@ var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/ge
13
13
  var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
14
14
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
15
15
  var _react = _interopRequireWildcard(require("react"));
16
+ var _coreUtils = require("@atlaskit/editor-common/core-utils");
16
17
  var _hooks = require("@atlaskit/editor-common/hooks");
17
- var _reactNodeView = _interopRequireDefault(require("@atlaskit/editor-common/react-node-view"));
18
+ var _reactNodeView = _interopRequireWildcard(require("@atlaskit/editor-common/react-node-view"));
18
19
  var _resizer = require("@atlaskit/editor-common/resizer");
19
20
  var _useSharedPluginStateSelector = require("@atlaskit/editor-common/use-shared-plugin-state-selector");
20
21
  var _model = require("@atlaskit/editor-prosemirror/model");
@@ -22,6 +23,7 @@ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
22
23
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
23
24
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
24
25
  var _utils = require("../pm-plugins/utils");
26
+ var _LayoutSSRReactContextsProvider = require("../ui/LayoutSSRReactContextsProvider");
25
27
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
26
28
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
27
29
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
@@ -140,6 +142,7 @@ var LayoutSectionView = exports.LayoutSectionView = /*#__PURE__*/function (_Reac
140
142
  * @param props.eventDispatcher
141
143
  * @param props.pluginInjectionApi
142
144
  * @param props.options
145
+ * @param props.intl
143
146
  * @example
144
147
  */
145
148
  function LayoutSectionView(props) {
@@ -148,6 +151,7 @@ var LayoutSectionView = exports.LayoutSectionView = /*#__PURE__*/function (_Reac
148
151
  _this = _callSuper(this, LayoutSectionView, [props.node, props.view, props.getPos, props.portalProviderAPI, props.eventDispatcher, props]);
149
152
  _this.isEmpty = isEmptyLayout(_this.node);
150
153
  _this.options = props.options;
154
+ _this.intl = props.intl;
151
155
  return _this;
152
156
  }
153
157
 
@@ -160,6 +164,13 @@ var LayoutSectionView = exports.LayoutSectionView = /*#__PURE__*/function (_Reac
160
164
  return (0, _createClass2.default)(LayoutSectionView, [{
161
165
  key: "getContentDOM",
162
166
  value: function getContentDOM() {
167
+ // Build the layout DOM via the schema's toDOM spec. This is the same
168
+ // path used in both CSR and SSR — the only SSR-specific concern is
169
+ // re-attaching `contentDOM` (= the `[data-layout-section]` element)
170
+ // after the portal's renderToStaticMarkup + innerHTML write detaches
171
+ // it. We handle that by stamping `data-ssr-content-dom-ref` on the
172
+ // outer container so `ReactNodeView.init()` can find a re-attach
173
+ // target inside `domRef` after the portal write.
163
174
  var _ref2 = _model.DOMSerializer.renderSpec(document, toDOM(this.node)),
164
175
  container = _ref2.dom,
165
176
  contentDOM = _ref2.contentDOM;
@@ -172,6 +183,20 @@ var LayoutSectionView = exports.LayoutSectionView = /*#__PURE__*/function (_Reac
172
183
  if ((0, _platformFeatureFlags.fg)('platform_editor_adf_with_localid')) {
173
184
  this.layoutDOM.setAttribute('data-local-id', this.node.attrs.localId);
174
185
  }
186
+
187
+ // SSR streaming re-attach note:
188
+ // In SSR, `init()` appends `container` into `domRef`; the portal's
189
+ // renderToStaticMarkup + innerHTML write then wipes `domRef`,
190
+ // detaching the entire subtree (with PM-serialized children inside
191
+ // `[data-layout-section]`). React's `render()` emits a
192
+ // `<NodeViewContentHole/>` placeholder inside `domRef`; the SSR
193
+ // re-attach logic in `init()` finds it via `[data-ssr-content-dom-ref]`
194
+ // and calls `_handleRef`, which appends `contentDOMWrapper` (the
195
+ // detached `container`) back inside the placeholder. The end result
196
+ // is `domRef > NodeViewContentHole > layout-section-container >
197
+ // [data-layout-section] > [data-layout-column] children` — the
198
+ // layout DOM contract is preserved.
199
+
175
200
  return {
176
201
  dom: container,
177
202
  contentDOM: contentDOM
@@ -206,6 +231,29 @@ var LayoutSectionView = exports.LayoutSectionView = /*#__PURE__*/function (_Reac
206
231
  if (this.layoutDOM) {
207
232
  this.layoutDOM.setAttribute('data-empty-layout', Boolean(this.isEmpty).toString());
208
233
  }
234
+
235
+ // SSR streaming path: render only a `<NodeViewContentHole/>` placeholder
236
+ // so ReactNodeView.init()'s SSR re-attach logic can find the marker
237
+ // (`data-ssr-content-dom-ref`) and re-append the detached
238
+ // contentDOMWrapper — which is the FULL layout structure
239
+ // (`layout-section-container > [data-layout-section] > children`) built
240
+ // in `getContentDOM` via DOMSerializer.renderSpec. This avoids
241
+ // duplicating layout structure between getContentDOM and render(), which
242
+ // previously caused an extra wrapping div between `[data-layout-section]`
243
+ // and the `[data-layout-column]` children and broke the flex layout.
244
+ //
245
+ // The BreakoutResizer is intentionally omitted in SSR — it relies on
246
+ // browser-only APIs and contributes no useful static markup. The
247
+ // LayoutSSRReactContextsProvider wraps the placeholder to inject the
248
+ // editor's IntlShape, defending against any descendants that call
249
+ // `useIntl()` during renderToStaticMarkup.
250
+ if ((0, _coreUtils.isSSR)() && (0, _coreUtils.isSSRStreaming)()) {
251
+ return /*#__PURE__*/_react.default.createElement(_LayoutSSRReactContextsProvider.LayoutSSRReactContextsProvider, {
252
+ intl: this.intl
253
+ }, /*#__PURE__*/_react.default.createElement(_reactNodeView.NodeViewContentHole, {
254
+ ref: forwardRef
255
+ }));
256
+ }
209
257
  if ((0, _expValEquals.expValEquals)('platform_editor_breakout_resizing', 'isEnabled', true)) {
210
258
  return null;
211
259
  }
@@ -7,8 +7,10 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.getColumnDividerDecorations = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _bindEventListener = require("bind-event-listener");
10
+ var _analytics = require("@atlaskit/editor-common/analytics");
10
11
  var _model = require("@atlaskit/editor-prosemirror/model");
11
12
  var _view = require("@atlaskit/editor-prosemirror/view");
13
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
12
14
  var _consts = require("./consts");
13
15
  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; }
14
16
  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; }
@@ -24,7 +26,7 @@ var dragState = null;
24
26
  * Dispatches a single undoable ProseMirror transaction to commit the final
25
27
  * column widths after a drag completes.
26
28
  */
27
- var dispatchColumnWidths = function dispatchColumnWidths(view, sectionPos, leftColIndex, leftWidth, rightWidth) {
29
+ var dispatchColumnWidths = function dispatchColumnWidths(view, sectionPos, leftColIndex, leftWidth, rightWidth, editorAnalyticsAPI) {
28
30
  var state = view.state;
29
31
  var sectionNode = state.doc.nodeAt(sectionPos);
30
32
  if (!sectionNode) {
@@ -51,6 +53,21 @@ var dispatchColumnWidths = function dispatchColumnWidths(view, sectionPos, leftC
51
53
  tr.replaceWith(sectionPos + 1, sectionPos + sectionNode.nodeSize - 1, _model.Fragment.from(newColumns));
52
54
  tr.setMeta('layoutColumnResize', true);
53
55
  tr.setMeta('scrollIntoView', false);
56
+ if ((0, _platformFeatureFlags.fg)('platform_editor_layout_resize_analytics')) {
57
+ editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent({
58
+ action: _analytics.ACTION.DRAGGED,
59
+ actionSubject: _analytics.ACTION_SUBJECT.DOCUMENT,
60
+ actionSubjectId: _analytics.ACTION_SUBJECT_ID.LAYOUT_COLUMN,
61
+ attributes: {
62
+ columnCount: sectionNode.childCount,
63
+ leftColumnIndex: leftColIndex,
64
+ leftColumnWidth: Number(leftWidth.toFixed(2)),
65
+ rightColumnWidth: Number(rightWidth.toFixed(2)),
66
+ inputMethod: _analytics.INPUT_METHOD.DRAG
67
+ },
68
+ eventType: _analytics.EVENT_TYPE.TRACK
69
+ })(tr);
70
+ }
54
71
  view.dispatch(tr);
55
72
  };
56
73
 
@@ -152,7 +169,8 @@ var onDragEnd = function onDragEnd(clientX) {
152
169
  rafId = _dragState2.rafId,
153
170
  startLeftWidth = _dragState2.startLeftWidth,
154
171
  startRightWidth = _dragState2.startRightWidth,
155
- unbindListeners = _dragState2.unbindListeners;
172
+ unbindListeners = _dragState2.unbindListeners,
173
+ editorAnalyticsAPI = _dragState2.editorAnalyticsAPI;
156
174
  unbindListeners();
157
175
 
158
176
  // Cancel any pending rAF so a stale frame doesn't write styles after teardown.
@@ -175,7 +193,7 @@ var onDragEnd = function onDragEnd(clientX) {
175
193
  leftColEl.style.flexBasis = '';
176
194
  rightColEl.style.flexBasis = '';
177
195
  if (widths && (widths.leftWidth !== startLeftWidth || widths.rightWidth !== startRightWidth)) {
178
- dispatchColumnWidths(view, sectionPos, leftColIndex, widths.leftWidth, widths.rightWidth);
196
+ dispatchColumnWidths(view, sectionPos, leftColIndex, widths.leftWidth, widths.rightWidth, editorAnalyticsAPI);
179
197
  }
180
198
  };
181
199
  var onDragMouseUp = function onDragMouseUp(e) {
@@ -202,8 +220,7 @@ var onDragCancel = function onDragCancel() {
202
220
  * column DOM elements for zero-overhead visual feedback (no PM transactions).
203
221
  * A single undoable PM transaction is dispatched on mouseup to commit the final widths.
204
222
  */
205
- var createColumnDividerWidget = function createColumnDividerWidget(view, sectionPos, columnIndex // index of the column to the RIGHT of this divider
206
- ) {
223
+ var createColumnDividerWidget = function createColumnDividerWidget(view, sectionPos, columnIndex, editorAnalyticsAPI) {
207
224
  var ownerDoc = view.dom.ownerDocument;
208
225
 
209
226
  // Outer container: wide transparent hit area for easy grabbing, zero flex footprint
@@ -298,6 +315,7 @@ var createColumnDividerWidget = function createColumnDividerWidget(view, section
298
315
  var totalGap = childCount > 1 ? computedGap * (childCount - 1) : 0;
299
316
  var columnsWidth = sectionRect.width - dividersWidth - totalGap;
300
317
  dragState = {
318
+ editorAnalyticsAPI: editorAnalyticsAPI,
301
319
  hasDragged: false,
302
320
  lastClientX: e.clientX,
303
321
  rafId: null,
@@ -329,7 +347,7 @@ var createColumnDividerWidget = function createColumnDividerWidget(view, section
329
347
  * Returns ProseMirror Decoration widgets for column dividers between layout columns.
330
348
  * Each divider supports drag-to-resize interaction for the adjacent columns.
331
349
  */
332
- var getColumnDividerDecorations = exports.getColumnDividerDecorations = function getColumnDividerDecorations(state, view) {
350
+ var getColumnDividerDecorations = exports.getColumnDividerDecorations = function getColumnDividerDecorations(state, view, editorAnalyticsAPI) {
333
351
  var decorations = [];
334
352
  if (!view) {
335
353
  return decorations;
@@ -345,7 +363,7 @@ var getColumnDividerDecorations = exports.getColumnDividerDecorations = function
345
363
  var colIndex = index;
346
364
  var widgetPos = pos + offset + 1; // position at the start of this column
347
365
  decorations.push(_view.Decoration.widget(widgetPos, function () {
348
- return createColumnDividerWidget(view, sectionPos, colIndex);
366
+ return createColumnDividerWidget(view, sectionPos, colIndex, editorAnalyticsAPI);
349
367
  }, {
350
368
  side: -1,
351
369
  // place before the position
@@ -102,7 +102,7 @@ var handleDeleteLayoutColumn = function handleDeleteLayoutColumn(state, dispatch
102
102
  }
103
103
  return false;
104
104
  };
105
- var _default = exports.default = function _default(options) {
105
+ var _default = exports.default = function _default(options, editorAnalyticsAPI) {
106
106
  // Store a reference to the EditorView so widget decorations can dispatch transactions
107
107
  var editorViewRef;
108
108
  return new _safePlugin.SafePlugin({
@@ -152,7 +152,7 @@ var _default = exports.default = function _default(options) {
152
152
  var layoutState = _pluginKey.pluginKey.getState(state);
153
153
  var isLayoutResizingPluginAvailable = _resizing.pluginKey.get(state) !== undefined;
154
154
  if ((0, _experiments.editorExperiment)('advanced_layouts', true) && (0, _experiments.editorExperiment)('platform_editor_layout_column_resize_handle', true) && isLayoutResizingPluginAvailable) {
155
- var dividerDecorations = (0, _columnResizeDivider.getColumnDividerDecorations)(state, editorViewRef);
155
+ var dividerDecorations = (0, _columnResizeDivider.getColumnDividerDecorations)(state, editorViewRef, editorAnalyticsAPI);
156
156
  var selectedDecorations = layoutState.pos !== null ? getNodeDecoration(layoutState.pos, state.doc.nodeAt(layoutState.pos)) : [];
157
157
  var allDecorations = [].concat((0, _toConsumableArray2.default)(selectedDecorations), (0, _toConsumableArray2.default)(dividerDecorations));
158
158
  if (allDecorations.length > 0) {
@@ -8,6 +8,8 @@ exports.pluginKey = exports.default = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
10
10
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
11
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
12
+ var _coreUtils = require("@atlaskit/editor-common/core-utils");
11
13
  var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
12
14
  var _model = require("@atlaskit/editor-prosemirror/model");
13
15
  var _state = require("@atlaskit/editor-prosemirror/state");
@@ -26,6 +28,28 @@ var pluginKey = exports.pluginKey = new _state.PluginKey('layoutResizingPlugin')
26
28
  * (e.g. setting flex-basis to give real-time visual feedback without dispatching
27
29
  * PM transactions) are not "corrected" back by ProseMirror's DOM reconciliation.
28
30
  */
31
+ var isLayoutElementLike = function isLayoutElementLike(element) {
32
+ if ((0, _coreUtils.isSSR)() && (0, _coreUtils.isSSRStreaming)()) {
33
+ // In SSR environments, `HTMLElement` is undefined globally so a plain
34
+ // `instanceof HTMLElement` check is always `false`. That makes the
35
+ // `DOMSerializer.renderSpec(...)` result get rejected by the guard below and
36
+ // the NodeView falls back to a bare `<div>`, losing every schema-defined
37
+ // attribute (`data-layout-column`, `style="flex-basis:..."`,
38
+ // `data-column-width`, plus the inner `<div data-layout-content="true">`
39
+ // wrapper) and breaking the layout's flex sizing in SSR output.
40
+ //
41
+ // To unblock SSR streaming without changing CSR semantics, we gate the check:
42
+ // - In SSR (and only when `platform_editor_editor_ssr_streaming` is enabled),
43
+ // use a duck-typed check that mirrors `safe-plugin`'s `isHTMLElement`.
44
+ // - Everywhere else, keep the original `instanceof HTMLElement` check exactly
45
+ // as it was so we don't accidentally widen acceptance in CSR.
46
+ if (element === null || element === undefined) {
47
+ return false;
48
+ }
49
+ return (0, _typeof2.default)(element) === 'object' && 'innerHTML' in element && 'style' in element && 'classList' in element;
50
+ }
51
+ return element instanceof HTMLElement;
52
+ };
29
53
  var LayoutColumnView = /*#__PURE__*/function () {
30
54
  function LayoutColumnView(node, view, getPos) {
31
55
  (0, _classCallCheck2.default)(this, LayoutColumnView);
@@ -43,7 +67,7 @@ var LayoutColumnView = /*#__PURE__*/function () {
43
67
  var _DOMSerializer$render = _model.DOMSerializer.renderSpec(document, nodeType.spec.toDOM(node)),
44
68
  dom = _DOMSerializer$render.dom,
45
69
  contentDOM = _DOMSerializer$render.contentDOM;
46
- if (!(dom instanceof HTMLElement) || !(contentDOM instanceof HTMLElement)) {
70
+ if (!isLayoutElementLike(dom) || !isLayoutElementLike(contentDOM)) {
47
71
  var _fallbackDiv = document.createElement('div');
48
72
  this.dom = _fallbackDiv;
49
73
  this.contentDOM = _fallbackDiv;
@@ -71,7 +95,7 @@ var LayoutColumnView = /*#__PURE__*/function () {
71
95
  }
72
96
  }]);
73
97
  }();
74
- var _default = exports.default = function _default(options, pluginInjectionApi, portalProviderAPI, eventDispatcher) {
98
+ var _default = exports.default = function _default(options, pluginInjectionApi, portalProviderAPI, eventDispatcher, intl) {
75
99
  return new _safePlugin.SafePlugin({
76
100
  key: pluginKey,
77
101
  props: {
@@ -84,10 +108,11 @@ var _default = exports.default = function _default(options, pluginInjectionApi,
84
108
  portalProviderAPI: portalProviderAPI,
85
109
  eventDispatcher: eventDispatcher,
86
110
  pluginInjectionApi: pluginInjectionApi,
87
- options: options
111
+ options: options,
112
+ intl: intl
88
113
  }).init();
89
114
  }
90
- }, (0, _experiments.editorExperiment)('platform_editor_layout_column_resize_handle', true) ? {
115
+ }, (0, _experiments.editorExperiment)('platform_editor_layout_column_resize_handle', true) || (0, _coreUtils.isSSR)() && (0, _coreUtils.isSSRStreaming)() ? {
91
116
  layoutColumn: function layoutColumn(node, view, getPos) {
92
117
  return new LayoutColumnView(node, view, getPos);
93
118
  }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.LayoutSSRReactContextsProvider = LayoutSSRReactContextsProvider;
8
+ var _react = _interopRequireDefault(require("react"));
9
+ var _reactIntl = require("react-intl");
10
+ var _coreUtils = require("@atlaskit/editor-common/core-utils");
11
+ /**
12
+ * Wraps the layout section nodeview children with the editor's actual
13
+ * IntlProvider during SSR streaming (renderToStaticMarkup). This ensures any
14
+ * descendants that call `useIntl()` (e.g. `BreakoutResizer`'s ARIA labels)
15
+ * have a valid intl context and do not throw during the static render pass.
16
+ *
17
+ * Outside of SSR streaming this is a no-op passthrough.
18
+ *
19
+ * Follows the same pattern as `MediaSSRReactContextsProvider` and
20
+ * `SyncBlockSSRReactContextsProvider`.
21
+ */
22
+ function LayoutSSRReactContextsProvider(_ref) {
23
+ var children = _ref.children,
24
+ intl = _ref.intl;
25
+ if (!(0, _coreUtils.isSSRStreaming)() || !(0, _coreUtils.isSSR)()) {
26
+ return children;
27
+ }
28
+ if (!intl) {
29
+ return children;
30
+ }
31
+ return /*#__PURE__*/_react.default.createElement(_reactIntl.RawIntlProvider, {
32
+ value: intl
33
+ }, children);
34
+ }
@@ -52,7 +52,7 @@ export const layoutPlugin = ({
52
52
  config: options = {},
53
53
  api
54
54
  }) => {
55
- var _api$analytics, _api$analytics4;
55
+ var _api$analytics2, _api$analytics5;
56
56
  const allowAdvancedSingleColumnLayout = editorExperiment('advanced_layouts', true) && editorExperiment('single_column_layouts', true, {
57
57
  exposure: true
58
58
  });
@@ -96,21 +96,25 @@ export const layoutPlugin = ({
96
96
  pmPlugins() {
97
97
  const plugins = [{
98
98
  name: 'layout',
99
- plugin: () => createLayoutPlugin(options)
99
+ plugin: () => {
100
+ var _api$analytics;
101
+ return createLayoutPlugin(options, api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions);
102
+ }
100
103
  }];
101
104
  if ((options.editorAppearance === 'full-page' || options.editorAppearance === 'full-width' || options.editorAppearance === 'max' && editorExperiment('platform_editor_layout_column_resize_handle', true)) && api && editorExperiment('advanced_layouts', true)) {
102
105
  plugins.push({
103
106
  name: 'layoutResizing',
104
107
  plugin: ({
105
108
  portalProviderAPI,
106
- eventDispatcher
107
- }) => createLayoutResizingPlugin(options, api, portalProviderAPI, eventDispatcher)
109
+ eventDispatcher,
110
+ getIntl
111
+ }) => createLayoutResizingPlugin(options, api, portalProviderAPI, eventDispatcher, getIntl())
108
112
  });
109
113
  }
110
114
  return plugins;
111
115
  },
112
116
  actions: {
113
- insertLayoutColumns: insertLayoutColumnsWithAnalytics(api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions)
117
+ insertLayoutColumns: insertLayoutColumnsWithAnalytics(api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions)
114
118
  },
115
119
  pluginsOptions: {
116
120
  floatingToolbar(state, intl) {
@@ -135,8 +139,8 @@ export const layoutPlugin = ({
135
139
  formatMessage
136
140
  }) => {
137
141
  const withInsertLayoutAnalytics = (tr, columnCount) => {
138
- var _api$analytics2, _api$analytics2$actio;
139
- api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : (_api$analytics2$actio = _api$analytics2.actions) === null || _api$analytics2$actio === void 0 ? void 0 : _api$analytics2$actio.attachAnalyticsEvent({
142
+ var _api$analytics3, _api$analytics3$actio;
143
+ api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : (_api$analytics3$actio = _api$analytics3.actions) === null || _api$analytics3$actio === void 0 ? void 0 : _api$analytics3$actio.attachAnalyticsEvent({
140
144
  action: ACTION.INSERTED,
141
145
  actionSubject: ACTION_SUBJECT.DOCUMENT,
142
146
  actionSubjectId: ACTION_SUBJECT_ID.LAYOUT,
@@ -311,9 +315,9 @@ export const layoutPlugin = ({
311
315
  priority: 1100,
312
316
  icon: () => /*#__PURE__*/React.createElement(IconLayout, null),
313
317
  action(insert, state) {
314
- var _api$analytics3, _api$analytics3$actio;
318
+ var _api$analytics4, _api$analytics4$actio;
315
319
  const tr = insert(createDefaultLayoutSection(state));
316
- api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : (_api$analytics3$actio = _api$analytics3.actions) === null || _api$analytics3$actio === void 0 ? void 0 : _api$analytics3$actio.attachAnalyticsEvent({
320
+ api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : (_api$analytics4$actio = _api$analytics4.actions) === null || _api$analytics4$actio === void 0 ? void 0 : _api$analytics4$actio.attachAnalyticsEvent({
317
321
  action: ACTION.INSERTED,
318
322
  actionSubject: ACTION_SUBJECT.DOCUMENT,
319
323
  actionSubjectId: ACTION_SUBJECT_ID.LAYOUT,
@@ -349,18 +353,18 @@ export const layoutPlugin = ({
349
353
  return pluginKey.getState(editorState);
350
354
  },
351
355
  commands: {
352
- deleteLayoutColumn: deleteLayoutColumn(api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions, api),
356
+ deleteLayoutColumn: deleteLayoutColumn(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions, api),
353
357
  distributeLayoutColumns: options => {
354
- var _api$analytics5;
355
- return distributeLayoutColumns(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions, api)(options);
358
+ var _api$analytics6;
359
+ return distributeLayoutColumns(api === null || api === void 0 ? void 0 : (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions, api)(options);
356
360
  },
357
361
  insertLayoutColumn: side => {
358
- var _api$analytics6;
359
- return insertLayoutColumn(side, api === null || api === void 0 ? void 0 : (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions, api);
362
+ var _api$analytics7;
363
+ return insertLayoutColumn(side, api === null || api === void 0 ? void 0 : (_api$analytics7 = api.analytics) === null || _api$analytics7 === void 0 ? void 0 : _api$analytics7.actions, api);
360
364
  },
361
365
  setLayoutColumnValign: valign => {
362
- var _api$analytics7;
363
- return setLayoutColumnValign(valign, api === null || api === void 0 ? void 0 : (_api$analytics7 = api.analytics) === null || _api$analytics7 === void 0 ? void 0 : _api$analytics7.actions, api);
366
+ var _api$analytics8;
367
+ return setLayoutColumnValign(valign, api === null || api === void 0 ? void 0 : (_api$analytics8 = api.analytics) === null || _api$analytics8 === void 0 ? void 0 : _api$analytics8.actions, api);
364
368
  },
365
369
  toggleLayoutColumnMenu
366
370
  }
@@ -1,6 +1,7 @@
1
1
  import React, { useCallback } from 'react';
2
+ import { isSSR, isSSRStreaming } from '@atlaskit/editor-common/core-utils';
2
3
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
- import ReactNodeView from '@atlaskit/editor-common/react-node-view';
4
+ import ReactNodeView, { NodeViewContentHole } from '@atlaskit/editor-common/react-node-view';
4
5
  import { BreakoutResizer, ignoreResizerMutations } from '@atlaskit/editor-common/resizer';
5
6
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
6
7
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
@@ -8,6 +9,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
8
9
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
9
10
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
10
11
  import { selectIntoLayout } from '../pm-plugins/utils';
12
+ import { LayoutSSRReactContextsProvider } from '../ui/LayoutSSRReactContextsProvider';
11
13
  const layoutDynamicFullWidthGuidelineOffset = 16;
12
14
  const isEmptyParagraph = node => {
13
15
  return !!node && node.type.name === 'paragraph' && !node.childCount;
@@ -122,12 +124,14 @@ export class LayoutSectionView extends ReactNodeView {
122
124
  * @param props.eventDispatcher
123
125
  * @param props.pluginInjectionApi
124
126
  * @param props.options
127
+ * @param props.intl
125
128
  * @example
126
129
  */
127
130
  constructor(props) {
128
131
  super(props.node, props.view, props.getPos, props.portalProviderAPI, props.eventDispatcher, props);
129
132
  this.isEmpty = isEmptyLayout(this.node);
130
133
  this.options = props.options;
134
+ this.intl = props.intl;
131
135
  }
132
136
 
133
137
  /**
@@ -136,6 +140,13 @@ export class LayoutSectionView extends ReactNodeView {
136
140
  * @returns
137
141
  */
138
142
  getContentDOM() {
143
+ // Build the layout DOM via the schema's toDOM spec. This is the same
144
+ // path used in both CSR and SSR — the only SSR-specific concern is
145
+ // re-attaching `contentDOM` (= the `[data-layout-section]` element)
146
+ // after the portal's renderToStaticMarkup + innerHTML write detaches
147
+ // it. We handle that by stamping `data-ssr-content-dom-ref` on the
148
+ // outer container so `ReactNodeView.init()` can find a re-attach
149
+ // target inside `domRef` after the portal write.
139
150
  const {
140
151
  dom: container,
141
152
  contentDOM
@@ -149,6 +160,20 @@ export class LayoutSectionView extends ReactNodeView {
149
160
  if (fg('platform_editor_adf_with_localid')) {
150
161
  this.layoutDOM.setAttribute('data-local-id', this.node.attrs.localId);
151
162
  }
163
+
164
+ // SSR streaming re-attach note:
165
+ // In SSR, `init()` appends `container` into `domRef`; the portal's
166
+ // renderToStaticMarkup + innerHTML write then wipes `domRef`,
167
+ // detaching the entire subtree (with PM-serialized children inside
168
+ // `[data-layout-section]`). React's `render()` emits a
169
+ // `<NodeViewContentHole/>` placeholder inside `domRef`; the SSR
170
+ // re-attach logic in `init()` finds it via `[data-ssr-content-dom-ref]`
171
+ // and calls `_handleRef`, which appends `contentDOMWrapper` (the
172
+ // detached `container`) back inside the placeholder. The end result
173
+ // is `domRef > NodeViewContentHole > layout-section-container >
174
+ // [data-layout-section] > [data-layout-column] children` — the
175
+ // layout DOM contract is preserved.
176
+
152
177
  return {
153
178
  dom: container,
154
179
  contentDOM
@@ -179,6 +204,29 @@ export class LayoutSectionView extends ReactNodeView {
179
204
  if (this.layoutDOM) {
180
205
  this.layoutDOM.setAttribute('data-empty-layout', Boolean(this.isEmpty).toString());
181
206
  }
207
+
208
+ // SSR streaming path: render only a `<NodeViewContentHole/>` placeholder
209
+ // so ReactNodeView.init()'s SSR re-attach logic can find the marker
210
+ // (`data-ssr-content-dom-ref`) and re-append the detached
211
+ // contentDOMWrapper — which is the FULL layout structure
212
+ // (`layout-section-container > [data-layout-section] > children`) built
213
+ // in `getContentDOM` via DOMSerializer.renderSpec. This avoids
214
+ // duplicating layout structure between getContentDOM and render(), which
215
+ // previously caused an extra wrapping div between `[data-layout-section]`
216
+ // and the `[data-layout-column]` children and broke the flex layout.
217
+ //
218
+ // The BreakoutResizer is intentionally omitted in SSR — it relies on
219
+ // browser-only APIs and contributes no useful static markup. The
220
+ // LayoutSSRReactContextsProvider wraps the placeholder to inject the
221
+ // editor's IntlShape, defending against any descendants that call
222
+ // `useIntl()` during renderToStaticMarkup.
223
+ if (isSSR() && isSSRStreaming()) {
224
+ return /*#__PURE__*/React.createElement(LayoutSSRReactContextsProvider, {
225
+ intl: this.intl
226
+ }, /*#__PURE__*/React.createElement(NodeViewContentHole, {
227
+ ref: forwardRef
228
+ }));
229
+ }
182
230
  if (expValEquals('platform_editor_breakout_resizing', 'isEnabled', true)) {
183
231
  return null;
184
232
  }