@atlaskit/editor-plugin-layout 11.0.3 → 11.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cjs/layoutPlugin.js +22 -6
  3. package/dist/cjs/nodeviews/index.js +49 -1
  4. package/dist/cjs/pm-plugins/actions.js +30 -10
  5. package/dist/cjs/pm-plugins/keymap.js +31 -0
  6. package/dist/cjs/pm-plugins/main.js +90 -21
  7. package/dist/cjs/pm-plugins/resizing.js +29 -4
  8. package/dist/cjs/pm-plugins/utils/layout-column-selection.js +36 -12
  9. package/dist/cjs/ui/LayoutColumnMenu/DeleteColumnDropdownItem.js +25 -5
  10. package/dist/cjs/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +7 -1
  11. package/dist/cjs/ui/LayoutColumnMenu/index.js +51 -4
  12. package/dist/cjs/ui/LayoutSSRReactContextsProvider.js +34 -0
  13. package/dist/cjs/ui/global-styles.js +11 -1
  14. package/dist/es2019/layoutPlugin.js +21 -7
  15. package/dist/es2019/nodeviews/index.js +49 -1
  16. package/dist/es2019/pm-plugins/actions.js +26 -11
  17. package/dist/es2019/pm-plugins/keymap.js +26 -0
  18. package/dist/es2019/pm-plugins/main.js +97 -25
  19. package/dist/es2019/pm-plugins/resizing.js +37 -7
  20. package/dist/es2019/pm-plugins/utils/layout-column-selection.js +33 -8
  21. package/dist/es2019/ui/LayoutColumnMenu/DeleteColumnDropdownItem.js +26 -6
  22. package/dist/es2019/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +8 -2
  23. package/dist/es2019/ui/LayoutColumnMenu/index.js +52 -5
  24. package/dist/es2019/ui/LayoutSSRReactContextsProvider.js +28 -0
  25. package/dist/es2019/ui/global-styles.js +9 -1
  26. package/dist/esm/layoutPlugin.js +23 -7
  27. package/dist/esm/nodeviews/index.js +49 -1
  28. package/dist/esm/pm-plugins/actions.js +30 -10
  29. package/dist/esm/pm-plugins/keymap.js +25 -0
  30. package/dist/esm/pm-plugins/main.js +90 -21
  31. package/dist/esm/pm-plugins/resizing.js +29 -4
  32. package/dist/esm/pm-plugins/utils/layout-column-selection.js +35 -11
  33. package/dist/esm/ui/LayoutColumnMenu/DeleteColumnDropdownItem.js +26 -6
  34. package/dist/esm/ui/LayoutColumnMenu/InsertColumnDropdownItem.js +8 -2
  35. package/dist/esm/ui/LayoutColumnMenu/index.js +52 -5
  36. package/dist/esm/ui/LayoutSSRReactContextsProvider.js +27 -0
  37. package/dist/esm/ui/global-styles.js +11 -1
  38. package/dist/types/layoutPluginType.d.ts +5 -7
  39. package/dist/types/nodeviews/index.d.ts +5 -0
  40. package/dist/types/pm-plugins/actions.d.ts +8 -4
  41. package/dist/types/pm-plugins/keymap.d.ts +10 -0
  42. package/dist/types/pm-plugins/resizing.d.ts +2 -1
  43. package/dist/types/pm-plugins/types.d.ts +2 -0
  44. package/dist/types/pm-plugins/utils/layout-column-selection.d.ts +1 -0
  45. package/dist/types/ui/LayoutSSRReactContextsProvider.d.ts +19 -0
  46. package/dist/types-ts4.5/layoutPluginType.d.ts +5 -7
  47. package/dist/types-ts4.5/nodeviews/index.d.ts +5 -0
  48. package/dist/types-ts4.5/pm-plugins/actions.d.ts +8 -4
  49. package/dist/types-ts4.5/pm-plugins/keymap.d.ts +10 -0
  50. package/dist/types-ts4.5/pm-plugins/resizing.d.ts +2 -1
  51. package/dist/types-ts4.5/pm-plugins/types.d.ts +2 -0
  52. package/dist/types-ts4.5/pm-plugins/utils/layout-column-selection.d.ts +1 -0
  53. package/dist/types-ts4.5/ui/LayoutSSRReactContextsProvider.d.ts +19 -0
  54. package/package.json +5 -5
@@ -9,8 +9,9 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
9
9
  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; }
10
10
  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
11
  import React, { useCallback } from 'react';
12
+ import { isSSR, isSSRStreaming } from '@atlaskit/editor-common/core-utils';
12
13
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
13
- import ReactNodeView from '@atlaskit/editor-common/react-node-view';
14
+ import ReactNodeView, { NodeViewContentHole } from '@atlaskit/editor-common/react-node-view';
14
15
  import { BreakoutResizer, ignoreResizerMutations } from '@atlaskit/editor-common/resizer';
15
16
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
16
17
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
@@ -18,6 +19,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
18
19
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
19
20
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
20
21
  import { selectIntoLayout } from '../pm-plugins/utils';
22
+ import { LayoutSSRReactContextsProvider } from '../ui/LayoutSSRReactContextsProvider';
21
23
  var layoutDynamicFullWidthGuidelineOffset = 16;
22
24
  var isEmptyParagraph = function isEmptyParagraph(node) {
23
25
  return !!node && node.type.name === 'paragraph' && !node.childCount;
@@ -131,6 +133,7 @@ export var LayoutSectionView = /*#__PURE__*/function (_ReactNodeView) {
131
133
  * @param props.eventDispatcher
132
134
  * @param props.pluginInjectionApi
133
135
  * @param props.options
136
+ * @param props.intl
134
137
  * @example
135
138
  */
136
139
  function LayoutSectionView(props) {
@@ -139,6 +142,7 @@ export var LayoutSectionView = /*#__PURE__*/function (_ReactNodeView) {
139
142
  _this = _callSuper(this, LayoutSectionView, [props.node, props.view, props.getPos, props.portalProviderAPI, props.eventDispatcher, props]);
140
143
  _this.isEmpty = isEmptyLayout(_this.node);
141
144
  _this.options = props.options;
145
+ _this.intl = props.intl;
142
146
  return _this;
143
147
  }
144
148
 
@@ -151,6 +155,13 @@ export var LayoutSectionView = /*#__PURE__*/function (_ReactNodeView) {
151
155
  return _createClass(LayoutSectionView, [{
152
156
  key: "getContentDOM",
153
157
  value: function getContentDOM() {
158
+ // Build the layout DOM via the schema's toDOM spec. This is the same
159
+ // path used in both CSR and SSR — the only SSR-specific concern is
160
+ // re-attaching `contentDOM` (= the `[data-layout-section]` element)
161
+ // after the portal's renderToStaticMarkup + innerHTML write detaches
162
+ // it. We handle that by stamping `data-ssr-content-dom-ref` on the
163
+ // outer container so `ReactNodeView.init()` can find a re-attach
164
+ // target inside `domRef` after the portal write.
154
165
  var _ref2 = DOMSerializer.renderSpec(document, toDOM(this.node)),
155
166
  container = _ref2.dom,
156
167
  contentDOM = _ref2.contentDOM;
@@ -163,6 +174,20 @@ export var LayoutSectionView = /*#__PURE__*/function (_ReactNodeView) {
163
174
  if (fg('platform_editor_adf_with_localid')) {
164
175
  this.layoutDOM.setAttribute('data-local-id', this.node.attrs.localId);
165
176
  }
177
+
178
+ // SSR streaming re-attach note:
179
+ // In SSR, `init()` appends `container` into `domRef`; the portal's
180
+ // renderToStaticMarkup + innerHTML write then wipes `domRef`,
181
+ // detaching the entire subtree (with PM-serialized children inside
182
+ // `[data-layout-section]`). React's `render()` emits a
183
+ // `<NodeViewContentHole/>` placeholder inside `domRef`; the SSR
184
+ // re-attach logic in `init()` finds it via `[data-ssr-content-dom-ref]`
185
+ // and calls `_handleRef`, which appends `contentDOMWrapper` (the
186
+ // detached `container`) back inside the placeholder. The end result
187
+ // is `domRef > NodeViewContentHole > layout-section-container >
188
+ // [data-layout-section] > [data-layout-column] children` — the
189
+ // layout DOM contract is preserved.
190
+
166
191
  return {
167
192
  dom: container,
168
193
  contentDOM: contentDOM
@@ -197,6 +222,29 @@ export var LayoutSectionView = /*#__PURE__*/function (_ReactNodeView) {
197
222
  if (this.layoutDOM) {
198
223
  this.layoutDOM.setAttribute('data-empty-layout', Boolean(this.isEmpty).toString());
199
224
  }
225
+
226
+ // SSR streaming path: render only a `<NodeViewContentHole/>` placeholder
227
+ // so ReactNodeView.init()'s SSR re-attach logic can find the marker
228
+ // (`data-ssr-content-dom-ref`) and re-append the detached
229
+ // contentDOMWrapper — which is the FULL layout structure
230
+ // (`layout-section-container > [data-layout-section] > children`) built
231
+ // in `getContentDOM` via DOMSerializer.renderSpec. This avoids
232
+ // duplicating layout structure between getContentDOM and render(), which
233
+ // previously caused an extra wrapping div between `[data-layout-section]`
234
+ // and the `[data-layout-column]` children and broke the flex layout.
235
+ //
236
+ // The BreakoutResizer is intentionally omitted in SSR — it relies on
237
+ // browser-only APIs and contributes no useful static markup. The
238
+ // LayoutSSRReactContextsProvider wraps the placeholder to inject the
239
+ // editor's IntlShape, defending against any descendants that call
240
+ // `useIntl()` during renderToStaticMarkup.
241
+ if (isSSR() && isSSRStreaming()) {
242
+ return /*#__PURE__*/React.createElement(LayoutSSRReactContextsProvider, {
243
+ intl: this.intl
244
+ }, /*#__PURE__*/React.createElement(NodeViewContentHole, {
245
+ ref: forwardRef
246
+ }));
247
+ }
200
248
  if (expValEquals('platform_editor_breakout_resizing', 'isEnabled', true)) {
201
249
  return null;
202
250
  }
@@ -14,7 +14,7 @@ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
14
14
  import { 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';
16
16
  import { calculateDistribution, isDistributedUniformly, redistributeAfterDeletion, redistributeProportionally } from './utils/layout-column-distribution';
17
- import { getAllLayoutColumnsFromSelection, getSelectedLayoutColumnsFromSelection } from './utils/layout-column-selection';
17
+ import { getAllLayoutColumnsFromSelection, getLayoutColumnsFromContentSelection, getSelectedLayoutColumnsFromSelection } from './utils/layout-column-selection';
18
18
  export var ONE_COL_LAYOUTS = ['single'];
19
19
  export var TWO_COL_LAYOUTS = ['two_equal', 'two_left_sidebar', 'two_right_sidebar'];
20
20
  export var THREE_COL_LAYOUTS = ['three_equal', 'three_with_sidebars'];
@@ -571,12 +571,13 @@ export function getEffectiveMaxLayoutColumns() {
571
571
  return editorExperiment('advanced_layouts', true) ? MAX_LAYOUT_COLUMNS : MAX_STANDARD_LAYOUT_COLUMNS;
572
572
  }
573
573
  var insertLayoutColumnAt = function insertLayoutColumnAt(side, editorAnalyticsAPI) {
574
+ var inputMethod = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : INPUT_METHOD.LAYOUT_COLUMN_MENU;
574
575
  return function (_ref4) {
575
576
  var tr = _ref4.tr;
576
577
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
577
578
  return null;
578
579
  }
579
- var selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
580
+ var selectedLayoutColumnsResult = getLayoutColumnsFromContentSelection(tr.selection);
580
581
  if (!selectedLayoutColumnsResult || selectedLayoutColumnsResult.selectedLayoutColumns.length === 0) {
581
582
  return null;
582
583
  }
@@ -636,7 +637,7 @@ var insertLayoutColumnAt = function insertLayoutColumnAt(side, editorAnalyticsAP
636
637
  attributes: {
637
638
  columnCount: redistributedWidths.length,
638
639
  endIndex: endIndex,
639
- inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
640
+ inputMethod: inputMethod,
640
641
  selectedCount: selectedColumnCount,
641
642
  side: side,
642
643
  startIndex: startIndex
@@ -648,9 +649,10 @@ var insertLayoutColumnAt = function insertLayoutColumnAt(side, editorAnalyticsAP
648
649
  };
649
650
  };
650
651
  export var insertLayoutColumn = function insertLayoutColumn(side, editorAnalyticsAPI, api) {
652
+ var inputMethod = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : INPUT_METHOD.LAYOUT_COLUMN_MENU;
651
653
  return function (_ref5) {
652
654
  var tr = _ref5.tr;
653
- var result = insertLayoutColumnAt(side, editorAnalyticsAPI)({
655
+ var result = insertLayoutColumnAt(side, editorAnalyticsAPI, inputMethod)({
654
656
  tr: tr
655
657
  });
656
658
  if (result) {
@@ -791,25 +793,43 @@ export var distributeLayoutColumns = function distributeLayoutColumns(editorAnal
791
793
  };
792
794
  export var toggleLayoutColumnMenu = function toggleLayoutColumnMenu(_ref11) {
793
795
  var anchorPos = _ref11.anchorPos,
794
- isOpen = _ref11.isOpen;
796
+ isOpen = _ref11.isOpen,
797
+ openedViaKeyboard = _ref11.openedViaKeyboard;
795
798
  return function (_ref12) {
796
799
  var tr = _ref12.tr;
797
800
  tr.setMeta('toggleLayoutColumnMenu', {
798
801
  anchorPos: anchorPos,
799
- isOpen: isOpen
802
+ isOpen: isOpen,
803
+ openedViaKeyboard: openedViaKeyboard
800
804
  });
801
805
  tr.setMeta('scrollIntoView', false);
802
806
  return tr;
803
807
  };
804
808
  };
805
- export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI, api) {
809
+ export var setLayoutColumnDangerPreview = function setLayoutColumnDangerPreview(show) {
806
810
  return function (_ref13) {
807
- var _api$blockControls4;
811
+ var _selectedLayoutColumn;
808
812
  var tr = _ref13.tr;
813
+ var selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
814
+ var positions = show ? (_selectedLayoutColumn = selectedLayoutColumnsResult === null || selectedLayoutColumnsResult === void 0 ? void 0 : selectedLayoutColumnsResult.selectedLayoutColumns.map(function (_ref14) {
815
+ var pos = _ref14.pos;
816
+ return pos;
817
+ })) !== null && _selectedLayoutColumn !== void 0 ? _selectedLayoutColumn : [] : null;
818
+ tr.setMeta('layoutColumnDangerPreview', positions);
819
+ tr.setMeta('addToHistory', false);
820
+ tr.setMeta('scrollIntoView', false);
821
+ return tr;
822
+ };
823
+ };
824
+ export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI, api) {
825
+ var inputMethod = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : INPUT_METHOD.LAYOUT_COLUMN_MENU;
826
+ return function (_ref15) {
827
+ var _api$blockControls4;
828
+ var tr = _ref15.tr;
809
829
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
810
830
  return null;
811
831
  }
812
- var selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
832
+ var selectedLayoutColumnsResult = getLayoutColumnsFromContentSelection(tr.selection);
813
833
  if (!selectedLayoutColumnsResult || selectedLayoutColumnsResult.selectedLayoutColumns.length === 0) {
814
834
  return null;
815
835
  }
@@ -826,7 +846,7 @@ export var deleteLayoutColumn = function deleteLayoutColumn(editorAnalyticsAPI,
826
846
  attributes: {
827
847
  columnCount: columnCount,
828
848
  endIndex: endIndex,
829
- inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
849
+ inputMethod: inputMethod,
830
850
  selectedCount: selectedLayoutColumns.length,
831
851
  startIndex: startIndex
832
852
  },
@@ -0,0 +1,25 @@
1
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
+ import { addColumnAfter, addColumnAfterVO, addColumnBefore, addColumnBeforeVO, bindKeymapWithEditorCommand, deleteColumn, keymap } from '@atlaskit/editor-common/keymaps';
3
+ import { deleteLayoutColumn, insertLayoutColumn } from './actions';
4
+ var bindLayoutColumnShortcut = function bindLayoutColumnShortcut(shortcut, command, list) {
5
+ if (!shortcut) {
6
+ return;
7
+ }
8
+ bindKeymapWithEditorCommand(shortcut, command, list);
9
+ };
10
+
11
+ /**
12
+ * Creates shortcut handlers for layout column actions.
13
+ */
14
+ function keymapPlugin(_ref) {
15
+ var _api$analytics, _api$analytics2, _api$analytics3, _api$analytics4, _api$analytics5;
16
+ var api = _ref.api;
17
+ var list = {};
18
+ bindLayoutColumnShortcut(addColumnBefore.common, insertLayoutColumn('left', api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions, api, INPUT_METHOD.KEYBOARD), list);
19
+ bindLayoutColumnShortcut(addColumnBeforeVO.common, insertLayoutColumn('left', api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, api, INPUT_METHOD.KEYBOARD), list);
20
+ bindLayoutColumnShortcut(addColumnAfter.common, insertLayoutColumn('right', api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions, api, INPUT_METHOD.KEYBOARD), list);
21
+ bindLayoutColumnShortcut(addColumnAfterVO.common, insertLayoutColumn('right', api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions, api, INPUT_METHOD.KEYBOARD), list);
22
+ bindLayoutColumnShortcut(deleteColumn.common, deleteLayoutColumn(api === null || api === void 0 || (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions, api, INPUT_METHOD.KEYBOARD), list);
23
+ return keymap(list);
24
+ }
25
+ export default keymapPlugin;
@@ -52,6 +52,18 @@ var getNodeDecoration = function getNodeDecoration(pos, node) {
52
52
  class: 'selected'
53
53
  })];
54
54
  };
55
+ var getDangerPreviewDecorations = function getDangerPreviewDecorations(state, positions) {
56
+ var _positions$flatMap;
57
+ return (_positions$flatMap = positions === null || positions === void 0 ? void 0 : positions.flatMap(function (pos) {
58
+ var node = state.doc.nodeAt(pos);
59
+ if (!node) {
60
+ return [];
61
+ }
62
+ return [Decoration.node(pos, pos + node.nodeSize, {
63
+ class: 'layout-column-danger-preview'
64
+ })];
65
+ })) !== null && _positions$flatMap !== void 0 ? _positions$flatMap : [];
66
+ };
55
67
  var getInitialPluginState = function getInitialPluginState(options, state) {
56
68
  var maybeLayoutSection = getMaybeLayoutSection(state);
57
69
  var allowBreakout = options.allowBreakout || false;
@@ -67,9 +79,52 @@ var getInitialPluginState = function getInitialPluginState(options, state) {
67
79
  allowSingleColumnLayout: allowSingleColumnLayout,
68
80
  isResizing: false,
69
81
  isLayoutColumnMenuOpen: false,
70
- layoutColumnMenuAnchorPos: undefined
82
+ layoutColumnMenuOpenedViaKeyboard: false,
83
+ layoutColumnMenuAnchorPos: undefined,
84
+ dangerPreviewLayoutColumnPositions: undefined
71
85
  };
72
86
  };
87
+ var reduceLayoutColumnMenuState = function reduceLayoutColumnMenuState(pluginState, action) {
88
+ switch (action.type) {
89
+ case 'toggleLayoutColumnMenu':
90
+ {
91
+ var _action$meta = action.meta,
92
+ anchorPos = _action$meta.anchorPos,
93
+ isOpen = _action$meta.isOpen,
94
+ openedViaKeyboard = _action$meta.openedViaKeyboard;
95
+ var nextIsOpen = isOpen !== null && isOpen !== void 0 ? isOpen : !pluginState.isLayoutColumnMenuOpen;
96
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
97
+ isLayoutColumnMenuOpen: nextIsOpen,
98
+ layoutColumnMenuOpenedViaKeyboard: nextIsOpen ? openedViaKeyboard !== null && openedViaKeyboard !== void 0 ? openedViaKeyboard : false : false,
99
+ layoutColumnMenuAnchorPos: nextIsOpen ? anchorPos : undefined,
100
+ dangerPreviewLayoutColumnPositions: nextIsOpen ? pluginState.dangerPreviewLayoutColumnPositions : undefined
101
+ });
102
+ }
103
+ case 'setDangerPreview':
104
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
105
+ dangerPreviewLayoutColumnPositions: action.positions
106
+ });
107
+ case 'clearDangerPreview':
108
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
109
+ dangerPreviewLayoutColumnPositions: undefined
110
+ });
111
+ case 'setResizeState':
112
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
113
+ isResizing: action.isResizing
114
+ });
115
+ case 'syncSelectionState':
116
+ {
117
+ var maybeLayoutSection = getMaybeLayoutSection(action.state);
118
+ return _objectSpread(_objectSpread({}, pluginState), {}, {
119
+ pos: maybeLayoutSection ? maybeLayoutSection.pos : null,
120
+ selectedLayout: getSelectedLayout(maybeLayoutSection && maybeLayoutSection.node,
121
+ // Ignored via go/ees005
122
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
123
+ pluginState.selectedLayout)
124
+ });
125
+ }
126
+ }
127
+ };
73
128
 
74
129
  // To prevent a single-column layout,
75
130
  // if a user attempts to delete a layout column and
@@ -116,28 +171,39 @@ export default (function (options, editorAnalyticsAPI) {
116
171
  return getInitialPluginState(options, state);
117
172
  },
118
173
  apply: function apply(tr, pluginState, oldState, newState) {
119
- var _columnMenuMeta$isOpe, _tr$getMeta, _pluginKey$getState;
174
+ var _tr$getMeta, _pluginKey$getState;
175
+ var nextPluginState = pluginState;
120
176
  var columnMenuMeta = tr.getMeta('toggleLayoutColumnMenu');
121
- var nextPluginState = columnMenuMeta ? _objectSpread(_objectSpread({}, pluginState), {}, {
122
- isLayoutColumnMenuOpen: (_columnMenuMeta$isOpe = columnMenuMeta.isOpen) !== null && _columnMenuMeta$isOpe !== void 0 ? _columnMenuMeta$isOpe : !pluginState.isLayoutColumnMenuOpen,
123
- layoutColumnMenuAnchorPos: columnMenuMeta.isOpen === false ? undefined : columnMenuMeta.anchorPos
124
- }) : pluginState;
177
+ var dangerPreviewMeta = tr.getMeta('layoutColumnDangerPreview');
178
+ if (columnMenuMeta) {
179
+ nextPluginState = reduceLayoutColumnMenuState(nextPluginState, {
180
+ meta: columnMenuMeta,
181
+ type: 'toggleLayoutColumnMenu'
182
+ });
183
+ }
184
+ if (tr.getMeta('layoutColumnDangerPreview') !== undefined) {
185
+ nextPluginState = reduceLayoutColumnMenuState(nextPluginState, {
186
+ positions: dangerPreviewMeta !== null && dangerPreviewMeta !== void 0 ? dangerPreviewMeta : undefined,
187
+ type: 'setDangerPreview'
188
+ });
189
+ }
190
+ if (tr.docChanged) {
191
+ nextPluginState = reduceLayoutColumnMenuState(nextPluginState, {
192
+ type: 'clearDangerPreview'
193
+ });
194
+ }
125
195
  var isResizing = editorExperiment('single_column_layouts', true) ? (_tr$getMeta = tr.getMeta('is-resizer-resizing')) !== null && _tr$getMeta !== void 0 ? _tr$getMeta : (_pluginKey$getState = pluginKey.getState(oldState)) === null || _pluginKey$getState === void 0 ? void 0 : _pluginKey$getState.isResizing : false;
196
+ nextPluginState = reduceLayoutColumnMenuState(nextPluginState, {
197
+ isResizing: isResizing,
198
+ type: 'setResizeState'
199
+ });
126
200
  if (tr.docChanged || tr.selectionSet) {
127
- var maybeLayoutSection = getMaybeLayoutSection(newState);
128
- var newPluginState = _objectSpread(_objectSpread({}, nextPluginState), {}, {
129
- pos: maybeLayoutSection ? maybeLayoutSection.pos : null,
130
- isResizing: isResizing,
131
- selectedLayout: getSelectedLayout(maybeLayoutSection && maybeLayoutSection.node,
132
- // Ignored via go/ees005
133
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
134
- pluginState.selectedLayout)
201
+ return reduceLayoutColumnMenuState(nextPluginState, {
202
+ state: newState,
203
+ type: 'syncSelectionState'
135
204
  });
136
- return newPluginState;
137
205
  }
138
- return _objectSpread(_objectSpread({}, nextPluginState), {}, {
139
- isResizing: isResizing
140
- });
206
+ return nextPluginState;
141
207
  }
142
208
  },
143
209
  props: {
@@ -147,14 +213,17 @@ export default (function (options, editorAnalyticsAPI) {
147
213
  if (editorExperiment('advanced_layouts', true) && editorExperiment('platform_editor_layout_column_resize_handle', true) && isLayoutResizingPluginAvailable) {
148
214
  var dividerDecorations = getColumnDividerDecorations(state, editorViewRef, editorAnalyticsAPI);
149
215
  var selectedDecorations = layoutState.pos !== null ? getNodeDecoration(layoutState.pos, state.doc.nodeAt(layoutState.pos)) : [];
150
- var allDecorations = [].concat(_toConsumableArray(selectedDecorations), _toConsumableArray(dividerDecorations));
216
+ var _dangerPreviewDecorations = getDangerPreviewDecorations(state, layoutState.dangerPreviewLayoutColumnPositions);
217
+ var allDecorations = [].concat(_toConsumableArray(selectedDecorations), _toConsumableArray(dividerDecorations), _toConsumableArray(_dangerPreviewDecorations));
151
218
  if (allDecorations.length > 0) {
152
219
  return DecorationSet.create(state.doc, allDecorations);
153
220
  }
154
221
  return undefined;
155
222
  }
156
- if (layoutState.pos !== null) {
157
- return DecorationSet.create(state.doc, getNodeDecoration(layoutState.pos, state.doc.nodeAt(layoutState.pos)));
223
+ var dangerPreviewDecorations = getDangerPreviewDecorations(state, layoutState.dangerPreviewLayoutColumnPositions);
224
+ if (layoutState.pos !== null || dangerPreviewDecorations.length > 0) {
225
+ var _selectedDecorations = layoutState.pos !== null ? getNodeDecoration(layoutState.pos, state.doc.nodeAt(layoutState.pos)) : [];
226
+ return DecorationSet.create(state.doc, [].concat(_toConsumableArray(_selectedDecorations), _toConsumableArray(dangerPreviewDecorations)));
158
227
  }
159
228
  return undefined;
160
229
  },
@@ -1,8 +1,10 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
3
  import _createClass from "@babel/runtime/helpers/createClass";
4
+ import _typeof from "@babel/runtime/helpers/typeof";
4
5
  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
6
  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; }
7
+ import { isSSR, isSSRStreaming } from '@atlaskit/editor-common/core-utils';
6
8
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
7
9
  import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
8
10
  import { PluginKey } from '@atlaskit/editor-prosemirror/state';
@@ -19,6 +21,28 @@ export var pluginKey = new PluginKey('layoutResizingPlugin');
19
21
  * (e.g. setting flex-basis to give real-time visual feedback without dispatching
20
22
  * PM transactions) are not "corrected" back by ProseMirror's DOM reconciliation.
21
23
  */
24
+ var isLayoutElementLike = function isLayoutElementLike(element) {
25
+ if (isSSR() && isSSRStreaming()) {
26
+ // In SSR environments, `HTMLElement` is undefined globally so a plain
27
+ // `instanceof HTMLElement` check is always `false`. That makes the
28
+ // `DOMSerializer.renderSpec(...)` result get rejected by the guard below and
29
+ // the NodeView falls back to a bare `<div>`, losing every schema-defined
30
+ // attribute (`data-layout-column`, `style="flex-basis:..."`,
31
+ // `data-column-width`, plus the inner `<div data-layout-content="true">`
32
+ // wrapper) and breaking the layout's flex sizing in SSR output.
33
+ //
34
+ // To unblock SSR streaming without changing CSR semantics, we gate the check:
35
+ // - In SSR (and only when `platform_editor_editor_ssr_streaming` is enabled),
36
+ // use a duck-typed check that mirrors `safe-plugin`'s `isHTMLElement`.
37
+ // - Everywhere else, keep the original `instanceof HTMLElement` check exactly
38
+ // as it was so we don't accidentally widen acceptance in CSR.
39
+ if (element === null || element === undefined) {
40
+ return false;
41
+ }
42
+ return _typeof(element) === 'object' && 'innerHTML' in element && 'style' in element && 'classList' in element;
43
+ }
44
+ return element instanceof HTMLElement;
45
+ };
22
46
  var LayoutColumnView = /*#__PURE__*/function () {
23
47
  function LayoutColumnView(node, view, getPos) {
24
48
  _classCallCheck(this, LayoutColumnView);
@@ -36,7 +60,7 @@ var LayoutColumnView = /*#__PURE__*/function () {
36
60
  var _DOMSerializer$render = DOMSerializer.renderSpec(document, nodeType.spec.toDOM(node)),
37
61
  dom = _DOMSerializer$render.dom,
38
62
  contentDOM = _DOMSerializer$render.contentDOM;
39
- if (!(dom instanceof HTMLElement) || !(contentDOM instanceof HTMLElement)) {
63
+ if (!isLayoutElementLike(dom) || !isLayoutElementLike(contentDOM)) {
40
64
  var _fallbackDiv = document.createElement('div');
41
65
  this.dom = _fallbackDiv;
42
66
  this.contentDOM = _fallbackDiv;
@@ -64,7 +88,7 @@ var LayoutColumnView = /*#__PURE__*/function () {
64
88
  }
65
89
  }]);
66
90
  }();
67
- export default (function (options, pluginInjectionApi, portalProviderAPI, eventDispatcher) {
91
+ export default (function (options, pluginInjectionApi, portalProviderAPI, eventDispatcher, intl) {
68
92
  return new SafePlugin({
69
93
  key: pluginKey,
70
94
  props: {
@@ -77,10 +101,11 @@ export default (function (options, pluginInjectionApi, portalProviderAPI, eventD
77
101
  portalProviderAPI: portalProviderAPI,
78
102
  eventDispatcher: eventDispatcher,
79
103
  pluginInjectionApi: pluginInjectionApi,
80
- options: options
104
+ options: options,
105
+ intl: intl
81
106
  }).init();
82
107
  }
83
- }, editorExperiment('platform_editor_layout_column_resize_handle', true) ? {
108
+ }, editorExperiment('platform_editor_layout_column_resize_handle', true) || isSSR() && isSSRStreaming() ? {
84
109
  layoutColumn: function layoutColumn(node, view, getPos) {
85
110
  return new LayoutColumnView(node, view, getPos);
86
111
  }
@@ -22,7 +22,7 @@ var findLayoutColumnsFromLayoutSection = function findLayoutColumnsFromLayoutSec
22
22
  };
23
23
  });
24
24
  };
25
- export var getSelectedLayoutColumnsFromSelection = function getSelectedLayoutColumnsFromSelection(selection) {
25
+ var getSelectedLayoutColumns = function getSelectedLayoutColumns(selection, isColumnSelected) {
26
26
  var layoutSection = findLayoutSectionFromSelection(selection);
27
27
  if (!layoutSection) {
28
28
  return undefined;
@@ -35,17 +35,15 @@ export var getSelectedLayoutColumnsFromSelection = function getSelectedLayoutCol
35
35
  }
36
36
  var startIndex = -1;
37
37
  var endIndex = -1;
38
- var selectedLayoutColumns = allLayoutColumns.filter(function (_ref2, index) {
39
- var node = _ref2.node,
40
- pos = _ref2.pos;
41
- var isSelected = selection.from <= pos && selection.to >= pos + node.nodeSize;
42
- if (isSelected) {
38
+ var selectedLayoutColumns = allLayoutColumns.filter(function (column, index) {
39
+ if (isColumnSelected(column, index)) {
43
40
  if (startIndex === -1) {
44
41
  startIndex = index;
45
42
  }
46
43
  endIndex = index;
44
+ return true;
47
45
  }
48
- return isSelected;
46
+ return false;
49
47
  });
50
48
  return {
51
49
  layoutSectionNode: layoutSectionNode,
@@ -55,6 +53,32 @@ export var getSelectedLayoutColumnsFromSelection = function getSelectedLayoutCol
55
53
  endIndex: endIndex
56
54
  };
57
55
  };
56
+ export var getSelectedLayoutColumnsFromSelection = function getSelectedLayoutColumnsFromSelection(selection) {
57
+ return getSelectedLayoutColumns(selection, function (_ref2) {
58
+ var node = _ref2.node,
59
+ pos = _ref2.pos;
60
+ // NodeSelection on a layout column is clearly selected.
61
+ if (selection instanceof NodeSelection && selection.node === node) {
62
+ return true;
63
+ }
64
+
65
+ // For TextSelection, only count columns that are fully contained within the selection
66
+ // (not partial text selections inside a column).
67
+ var nodeEndPos = pos + node.nodeSize;
68
+ return !selection.empty && selection.from <= pos && selection.to >= nodeEndPos;
69
+ });
70
+ };
71
+ export var getLayoutColumnsFromContentSelection = function getLayoutColumnsFromContentSelection(selection) {
72
+ return getSelectedLayoutColumns(selection, function (_ref3) {
73
+ var node = _ref3.node,
74
+ pos = _ref3.pos;
75
+ if (selection instanceof NodeSelection && selection.node === node) {
76
+ return true;
77
+ }
78
+ var nodeEndPos = pos + node.nodeSize;
79
+ return selection.empty ? selection.from > pos && selection.from < nodeEndPos : selection.from < nodeEndPos && selection.to > pos;
80
+ });
81
+ };
58
82
  export var getAllLayoutColumnsFromSelection = function getAllLayoutColumnsFromSelection(selection) {
59
83
  var layoutSection = findLayoutSectionFromSelection(selection);
60
84
  if (!layoutSection) {
@@ -73,8 +97,8 @@ export var getAllLayoutColumnsFromSelection = function getAllLayoutColumnsFromSe
73
97
  };
74
98
  };
75
99
  export var getLayoutColumnValign = function getLayoutColumnValign(layoutColumn) {
76
- var _ref3;
77
- return layoutColumn ? (_ref3 = layoutColumn.attrs.valign) !== null && _ref3 !== void 0 ? _ref3 : 'top' : undefined;
100
+ var _ref4;
101
+ return layoutColumn ? (_ref4 = layoutColumn.attrs.valign) !== null && _ref4 !== void 0 ? _ref4 : 'top' : undefined;
78
102
  };
79
103
  export var getLayoutColumnMenuAnchorPos = function getLayoutColumnMenuAnchorPos(selection, anchorPosFromHandle) {
80
104
  var _clickedSelectedColum, _selectedLayoutColumn;
@@ -82,8 +106,8 @@ export var getLayoutColumnMenuAnchorPos = function getLayoutColumnMenuAnchorPos(
82
106
  if (!selectedLayoutColumns) {
83
107
  return undefined;
84
108
  }
85
- var clickedSelectedColumn = selectedLayoutColumns.selectedLayoutColumns.find(function (_ref4) {
86
- var pos = _ref4.pos;
109
+ var clickedSelectedColumn = selectedLayoutColumns.selectedLayoutColumns.find(function (_ref5) {
110
+ var pos = _ref5.pos;
87
111
  return pos === anchorPosFromHandle;
88
112
  });
89
113
  return (_clickedSelectedColum = clickedSelectedColumn === null || clickedSelectedColumn === void 0 ? void 0 : clickedSelectedColumn.pos) !== null && _clickedSelectedColum !== void 0 ? _clickedSelectedColum : (_selectedLayoutColumn = selectedLayoutColumns.selectedLayoutColumns[0]) === null || _selectedLayoutColumn === void 0 ? void 0 : _selectedLayoutColumn.pos;
@@ -1,23 +1,35 @@
1
1
  import React, { useCallback } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
+ import { deleteColumn, getAriaKeyshortcuts, tooltip } from '@atlaskit/editor-common/keymaps';
3
4
  import { layoutMessages } from '@atlaskit/editor-common/messages';
4
- import { DeleteIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
5
+ import { DeleteIcon, ToolbarDropdownItem, ToolbarKeyboardShortcutHint } from '@atlaskit/editor-toolbar';
5
6
  import { useSelectedLayoutColumns } from './useSelectedLayoutColumns';
6
7
  var DeleteColumnDropdownItem = function DeleteColumnDropdownItem(_ref) {
8
+ var _tooltip;
7
9
  var api = _ref.api;
8
10
  var _useIntl = useIntl(),
9
11
  formatMessage = _useIntl.formatMessage;
10
12
  var selectedLayoutColumns = useSelectedLayoutColumns(api);
13
+ var setDangerPreview = useCallback(function (show) {
14
+ var _api$core, _api$layout;
15
+ api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.setLayoutColumnDangerPreview(show));
16
+ }, [api]);
17
+ var showDangerPreview = useCallback(function () {
18
+ setDangerPreview(true);
19
+ }, [setDangerPreview]);
20
+ var hideDangerPreview = useCallback(function () {
21
+ setDangerPreview(false);
22
+ }, [setDangerPreview]);
11
23
  var onClick = useCallback(function () {
12
- var _api$layout, _api$core;
13
- var deleteCommand = api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.deleteLayoutColumn;
14
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (props) {
15
- var _api$layout2;
24
+ var _api$layout2, _api$core2;
25
+ var deleteCommand = api === null || api === void 0 || (_api$layout2 = api.layout) === null || _api$layout2 === void 0 ? void 0 : _api$layout2.commands.deleteLayoutColumn();
26
+ api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (props) {
27
+ var _api$layout3;
16
28
  var tr = deleteCommand === null || deleteCommand === void 0 ? void 0 : deleteCommand(props);
17
29
  if (!tr) {
18
30
  return tr !== null && tr !== void 0 ? tr : null;
19
31
  }
20
- api === null || api === void 0 || (_api$layout2 = api.layout) === null || _api$layout2 === void 0 || _api$layout2.commands.toggleLayoutColumnMenu({
32
+ api === null || api === void 0 || (_api$layout3 = api.layout) === null || _api$layout3 === void 0 || _api$layout3.commands.toggleLayoutColumnMenu({
21
33
  isOpen: false
22
34
  })({
23
35
  tr: tr
@@ -30,11 +42,19 @@ var DeleteColumnDropdownItem = function DeleteColumnDropdownItem(_ref) {
30
42
  }
31
43
  var selectedColumnCount = selectedLayoutColumns.selectedLayoutColumns.length;
32
44
  return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
45
+ ariaKeyshortcuts: getAriaKeyshortcuts(deleteColumn),
33
46
  onClick: onClick,
47
+ onFocus: showDangerPreview,
48
+ onMouseEnter: showDangerPreview,
49
+ onBlur: hideDangerPreview,
50
+ onMouseLeave: hideDangerPreview,
34
51
  elemBefore: /*#__PURE__*/React.createElement(DeleteIcon, {
35
52
  color: "currentColor",
36
53
  label: "",
37
54
  size: "small"
55
+ }),
56
+ elemAfter: /*#__PURE__*/React.createElement(ToolbarKeyboardShortcutHint, {
57
+ shortcut: (_tooltip = tooltip(deleteColumn)) !== null && _tooltip !== void 0 ? _tooltip : ''
38
58
  })
39
59
  }, formatMessage(layoutMessages.deleteColumn, {
40
60
  count: selectedColumnCount
@@ -1,7 +1,8 @@
1
1
  import React, { useCallback } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
+ import { addColumnAfter, addColumnBefore, getAriaKeyshortcuts, tooltip } from '@atlaskit/editor-common/keymaps';
3
4
  import { layoutMessages } from '@atlaskit/editor-common/messages';
4
- import { TableColumnAddLeftIcon, TableColumnAddRightIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
5
+ import { TableColumnAddLeftIcon, TableColumnAddRightIcon, ToolbarDropdownItem, ToolbarKeyboardShortcutHint } from '@atlaskit/editor-toolbar';
5
6
  import { getEffectiveMaxLayoutColumns } from '../../pm-plugins/actions';
6
7
  import { useSelectedLayoutColumns } from './useSelectedLayoutColumns';
7
8
  var INSERT_COLUMN_OPTIONS = {
@@ -17,7 +18,7 @@ var INSERT_COLUMN_OPTIONS = {
17
18
  }
18
19
  };
19
20
  export var InsertColumnDropdownItem = function InsertColumnDropdownItem(_ref) {
20
- var _selectedLayoutColumn, _selectedLayoutColumn2;
21
+ var _selectedLayoutColumn, _selectedLayoutColumn2, _tooltip;
21
22
  var api = _ref.api,
22
23
  side = _ref.side;
23
24
  var _useIntl = useIntl(),
@@ -25,6 +26,7 @@ export var InsertColumnDropdownItem = function InsertColumnDropdownItem(_ref) {
25
26
  var _INSERT_COLUMN_OPTION = INSERT_COLUMN_OPTIONS[side],
26
27
  Icon = _INSERT_COLUMN_OPTION.Icon,
27
28
  label = _INSERT_COLUMN_OPTION.label;
29
+ var shortcut = side === 'left' ? addColumnBefore : addColumnAfter;
28
30
  var selectedLayoutColumns = useSelectedLayoutColumns(api);
29
31
  var columnCount = (_selectedLayoutColumn = selectedLayoutColumns === null || selectedLayoutColumns === void 0 || (_selectedLayoutColumn2 = selectedLayoutColumns.layoutSectionNode) === null || _selectedLayoutColumn2 === void 0 ? void 0 : _selectedLayoutColumn2.childCount) !== null && _selectedLayoutColumn !== void 0 ? _selectedLayoutColumn : 0;
30
32
  var maxColumnCount = getEffectiveMaxLayoutColumns();
@@ -50,11 +52,15 @@ export var InsertColumnDropdownItem = function InsertColumnDropdownItem(_ref) {
50
52
  return null;
51
53
  }
52
54
  return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
55
+ ariaKeyshortcuts: getAriaKeyshortcuts(shortcut),
53
56
  elemBefore: /*#__PURE__*/React.createElement(Icon, {
54
57
  color: "currentColor",
55
58
  label: "",
56
59
  size: "small"
57
60
  }),
61
+ elemAfter: /*#__PURE__*/React.createElement(ToolbarKeyboardShortcutHint, {
62
+ shortcut: (_tooltip = tooltip(shortcut)) !== null && _tooltip !== void 0 ? _tooltip : ''
63
+ }),
58
64
  onClick: onClick
59
65
  }, formatMessage(label));
60
66
  };