@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
@@ -7,25 +7,37 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.DeleteColumnDropdownItem = void 0;
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
  var _reactIntl = require("react-intl");
10
+ var _keymaps = require("@atlaskit/editor-common/keymaps");
10
11
  var _messages = require("@atlaskit/editor-common/messages");
11
12
  var _editorToolbar = require("@atlaskit/editor-toolbar");
12
13
  var _useSelectedLayoutColumns = require("./useSelectedLayoutColumns");
13
14
  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); }
14
15
  var DeleteColumnDropdownItem = exports.DeleteColumnDropdownItem = function DeleteColumnDropdownItem(_ref) {
16
+ var _tooltip;
15
17
  var api = _ref.api;
16
18
  var _useIntl = (0, _reactIntl.useIntl)(),
17
19
  formatMessage = _useIntl.formatMessage;
18
20
  var selectedLayoutColumns = (0, _useSelectedLayoutColumns.useSelectedLayoutColumns)(api);
21
+ var setDangerPreview = (0, _react.useCallback)(function (show) {
22
+ var _api$core, _api$layout;
23
+ 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));
24
+ }, [api]);
25
+ var showDangerPreview = (0, _react.useCallback)(function () {
26
+ setDangerPreview(true);
27
+ }, [setDangerPreview]);
28
+ var hideDangerPreview = (0, _react.useCallback)(function () {
29
+ setDangerPreview(false);
30
+ }, [setDangerPreview]);
19
31
  var onClick = (0, _react.useCallback)(function () {
20
- var _api$layout, _api$core;
21
- var deleteCommand = api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.deleteLayoutColumn;
22
- api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (props) {
23
- var _api$layout2;
32
+ var _api$layout2, _api$core2;
33
+ var deleteCommand = api === null || api === void 0 || (_api$layout2 = api.layout) === null || _api$layout2 === void 0 ? void 0 : _api$layout2.commands.deleteLayoutColumn();
34
+ api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 || _api$core2.actions.execute(function (props) {
35
+ var _api$layout3;
24
36
  var tr = deleteCommand === null || deleteCommand === void 0 ? void 0 : deleteCommand(props);
25
37
  if (!tr) {
26
38
  return tr !== null && tr !== void 0 ? tr : null;
27
39
  }
28
- api === null || api === void 0 || (_api$layout2 = api.layout) === null || _api$layout2 === void 0 || _api$layout2.commands.toggleLayoutColumnMenu({
40
+ api === null || api === void 0 || (_api$layout3 = api.layout) === null || _api$layout3 === void 0 || _api$layout3.commands.toggleLayoutColumnMenu({
29
41
  isOpen: false
30
42
  })({
31
43
  tr: tr
@@ -38,11 +50,19 @@ var DeleteColumnDropdownItem = exports.DeleteColumnDropdownItem = function Delet
38
50
  }
39
51
  var selectedColumnCount = selectedLayoutColumns.selectedLayoutColumns.length;
40
52
  return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownItem, {
53
+ ariaKeyshortcuts: (0, _keymaps.getAriaKeyshortcuts)(_keymaps.deleteColumn),
41
54
  onClick: onClick,
55
+ onFocus: showDangerPreview,
56
+ onMouseEnter: showDangerPreview,
57
+ onBlur: hideDangerPreview,
58
+ onMouseLeave: hideDangerPreview,
42
59
  elemBefore: /*#__PURE__*/_react.default.createElement(_editorToolbar.DeleteIcon, {
43
60
  color: "currentColor",
44
61
  label: "",
45
62
  size: "small"
63
+ }),
64
+ elemAfter: /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarKeyboardShortcutHint, {
65
+ shortcut: (_tooltip = (0, _keymaps.tooltip)(_keymaps.deleteColumn)) !== null && _tooltip !== void 0 ? _tooltip : ''
46
66
  })
47
67
  }, formatMessage(_messages.layoutMessages.deleteColumn, {
48
68
  count: selectedColumnCount
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.InsertColumnDropdownItem = void 0;
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
  var _reactIntl = require("react-intl");
10
+ var _keymaps = require("@atlaskit/editor-common/keymaps");
10
11
  var _messages = require("@atlaskit/editor-common/messages");
11
12
  var _editorToolbar = require("@atlaskit/editor-toolbar");
12
13
  var _actions = require("../../pm-plugins/actions");
@@ -25,7 +26,7 @@ var INSERT_COLUMN_OPTIONS = {
25
26
  }
26
27
  };
27
28
  var InsertColumnDropdownItem = exports.InsertColumnDropdownItem = function InsertColumnDropdownItem(_ref) {
28
- var _selectedLayoutColumn, _selectedLayoutColumn2;
29
+ var _selectedLayoutColumn, _selectedLayoutColumn2, _tooltip;
29
30
  var api = _ref.api,
30
31
  side = _ref.side;
31
32
  var _useIntl = (0, _reactIntl.useIntl)(),
@@ -33,6 +34,7 @@ var InsertColumnDropdownItem = exports.InsertColumnDropdownItem = function Inser
33
34
  var _INSERT_COLUMN_OPTION = INSERT_COLUMN_OPTIONS[side],
34
35
  Icon = _INSERT_COLUMN_OPTION.Icon,
35
36
  label = _INSERT_COLUMN_OPTION.label;
37
+ var shortcut = side === 'left' ? _keymaps.addColumnBefore : _keymaps.addColumnAfter;
36
38
  var selectedLayoutColumns = (0, _useSelectedLayoutColumns.useSelectedLayoutColumns)(api);
37
39
  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;
38
40
  var maxColumnCount = (0, _actions.getEffectiveMaxLayoutColumns)();
@@ -58,11 +60,15 @@ var InsertColumnDropdownItem = exports.InsertColumnDropdownItem = function Inser
58
60
  return null;
59
61
  }
60
62
  return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownItem, {
63
+ ariaKeyshortcuts: (0, _keymaps.getAriaKeyshortcuts)(shortcut),
61
64
  elemBefore: /*#__PURE__*/_react.default.createElement(Icon, {
62
65
  color: "currentColor",
63
66
  label: "",
64
67
  size: "small"
65
68
  }),
69
+ elemAfter: /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarKeyboardShortcutHint, {
70
+ shortcut: (_tooltip = (0, _keymaps.tooltip)(shortcut)) !== null && _tooltip !== void 0 ? _tooltip : ''
71
+ }),
66
72
  onClick: onClick
67
73
  }, formatMessage(label));
68
74
  };
@@ -6,9 +6,11 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.LayoutColumnMenu = void 0;
8
8
  var _react = _interopRequireWildcard(require("react"));
9
+ var _bindEventListener = require("bind-event-listener");
9
10
  var _hooks = require("@atlaskit/editor-common/hooks");
10
11
  var _styles = require("@atlaskit/editor-common/styles");
11
12
  var _ui = require("@atlaskit/editor-common/ui");
13
+ var _uiMenu = require("@atlaskit/editor-common/ui-menu");
12
14
  var _uiReact = require("@atlaskit/editor-common/ui-react");
13
15
  var _userIntent = require("@atlaskit/editor-common/user-intent");
14
16
  var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
@@ -21,6 +23,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
21
23
  var PopupWithListeners = (0, _uiReact.withReactEditorViewOuterListeners)(_ui.Popup);
22
24
  var LAYOUT_COLUMN_MENU_POPUP_OFFSET = [0, 4];
23
25
  var TOOLBAR_MENU_SELECTOR = '[data-toolbar-component="menu"]';
26
+ var NESTED_DROPDOWN_MENU_SELECTOR = '[data-toolbar-nested-dropdown-menu]';
24
27
 
25
28
  /**
26
29
  * Returns the drag handle button for the selected layout column.
@@ -38,6 +41,9 @@ var getLayoutColumnMenuTarget = function getLayoutColumnMenuTarget(editorView, s
38
41
  var dragHandleContainer = (_columnDomRef$parentE = columnDomRef.parentElement) === null || _columnDomRef$parentE === void 0 ? void 0 : _columnDomRef$parentE.querySelector(':scope > [data-blocks-drag-handle-container]');
39
42
  return dragHandleContainer === null || dragHandleContainer === void 0 ? void 0 : dragHandleContainer.querySelector(_styles.DRAG_HANDLE_SELECTOR);
40
43
  };
44
+ var focusTrap = {
45
+ initialFocus: undefined
46
+ };
41
47
  var LayoutColumnMenu = exports.LayoutColumnMenu = /*#__PURE__*/_react.default.memo(function LayoutColumnMenu(_ref) {
42
48
  var _api$uiControlRegistr, _api$uiControlRegistr2;
43
49
  var api = _ref.api,
@@ -46,15 +52,17 @@ var LayoutColumnMenu = exports.LayoutColumnMenu = /*#__PURE__*/_react.default.me
46
52
  boundariesElement = _ref.boundariesElement,
47
53
  scrollableElement = _ref.scrollableElement;
48
54
  var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['layout', 'selection'], function (states) {
49
- var _states$layoutState$i, _states$layoutState, _states$layoutState2, _states$selectionStat;
55
+ var _states$layoutState$i, _states$layoutState, _states$layoutState2, _states$layoutState$l, _states$layoutState3, _states$selectionStat;
50
56
  return {
51
57
  isLayoutColumnMenuOpen: (_states$layoutState$i = (_states$layoutState = states.layoutState) === null || _states$layoutState === void 0 ? void 0 : _states$layoutState.isLayoutColumnMenuOpen) !== null && _states$layoutState$i !== void 0 ? _states$layoutState$i : false,
52
58
  layoutColumnMenuAnchorPos: (_states$layoutState2 = states.layoutState) === null || _states$layoutState2 === void 0 ? void 0 : _states$layoutState2.layoutColumnMenuAnchorPos,
59
+ openedViaKeyboard: (_states$layoutState$l = (_states$layoutState3 = states.layoutState) === null || _states$layoutState3 === void 0 ? void 0 : _states$layoutState3.layoutColumnMenuOpenedViaKeyboard) !== null && _states$layoutState$l !== void 0 ? _states$layoutState$l : false,
53
60
  selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection
54
61
  };
55
62
  }),
56
63
  isLayoutColumnMenuOpen = _useSharedPluginState.isLayoutColumnMenuOpen,
57
64
  layoutColumnMenuAnchorPos = _useSharedPluginState.layoutColumnMenuAnchorPos,
65
+ openedViaKeyboard = _useSharedPluginState.openedViaKeyboard,
58
66
  selection = _useSharedPluginState.selection;
59
67
  var closeLayoutColumnMenu = (0, _react.useCallback)(function () {
60
68
  var _api$core, _api$layout;
@@ -63,7 +71,7 @@ var LayoutColumnMenu = exports.LayoutColumnMenu = /*#__PURE__*/_react.default.me
63
71
  }));
64
72
  }, [api]);
65
73
  var handleClickOutside = (0, _react.useCallback)(function (event) {
66
- if (event.target instanceof Element && (event.target.closest(TOOLBAR_MENU_SELECTOR) || event.target.closest('[data-toolbar-nested-dropdown-menu]'))) {
74
+ if (event.target instanceof Element && (event.target.closest(TOOLBAR_MENU_SELECTOR) || event.target.closest(NESTED_DROPDOWN_MENU_SELECTOR))) {
67
75
  return;
68
76
  }
69
77
 
@@ -80,6 +88,38 @@ var LayoutColumnMenu = exports.LayoutColumnMenu = /*#__PURE__*/_react.default.me
80
88
  closeLayoutColumnMenu();
81
89
  }
82
90
  }, [closeLayoutColumnMenu]);
91
+ var handleArrowKeyNavigationClose = (0, _react.useCallback)(function (event) {
92
+ event.preventDefault();
93
+ closeLayoutColumnMenu();
94
+ }, [closeLayoutColumnMenu]);
95
+ var shouldDisableArrowKeyNavigation = (0, _react.useCallback)(function (event) {
96
+ if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') {
97
+ return false;
98
+ }
99
+ var target = event.target;
100
+ if (!(target instanceof HTMLElement)) {
101
+ return false;
102
+ }
103
+ return target.closest(NESTED_DROPDOWN_MENU_SELECTOR) !== null;
104
+ }, []);
105
+ var menuWrapperRef = (0, _react.useRef)(null);
106
+ var handleMenuKeyDown = (0, _react.useCallback)(function (event) {
107
+ // Keep menu keyboard events scoped to the menu while preserving Escape and
108
+ // ArrowUp/ArrowDown handling from Popup and ArrowKeyNavigationProvider.
109
+ if (event.key !== 'Escape' && event.key !== 'ArrowUp' && event.key !== 'ArrowDown') {
110
+ event.stopPropagation();
111
+ }
112
+ }, []);
113
+ (0, _react.useEffect)(function () {
114
+ var menuWrapper = menuWrapperRef.current;
115
+ if (!isLayoutColumnMenuOpen || !menuWrapper) {
116
+ return;
117
+ }
118
+ return (0, _bindEventListener.bind)(menuWrapper, {
119
+ type: 'keydown',
120
+ listener: handleMenuKeyDown
121
+ });
122
+ }, [handleMenuKeyDown, isLayoutColumnMenuOpen]);
83
123
  var components = (_api$uiControlRegistr = api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(_keys.LAYOUT_COLUMN_MENU.key)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
84
124
  var target = (0, _react.useMemo)(function () {
85
125
  return isLayoutColumnMenuOpen ? getLayoutColumnMenuTarget(editorView, selection, layoutColumnMenuAnchorPos) : null;
@@ -104,16 +144,23 @@ var LayoutColumnMenu = exports.LayoutColumnMenu = /*#__PURE__*/_react.default.me
104
144
  stick: true,
105
145
  offset: LAYOUT_COLUMN_MENU_POPUP_OFFSET,
106
146
  handleClickOutside: handleClickOutside,
107
- handleEscapeKeydown: closeLayoutColumnMenu
147
+ handleEscapeKeydown: closeLayoutColumnMenu,
148
+ focusTrap: openedViaKeyboard ? focusTrap : undefined
149
+ }, /*#__PURE__*/_react.default.createElement("div", {
150
+ ref: menuWrapperRef
108
151
  }, /*#__PURE__*/_react.default.createElement(_userIntent.UserIntentPopupWrapper, {
109
152
  api: api,
110
153
  userIntent: "layoutColumnMenuPopupOpen"
111
154
  }, /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownMenuProvider, {
112
155
  isOpen: isLayoutColumnMenuOpen,
113
156
  setIsOpen: handleSetIsOpen
157
+ }, /*#__PURE__*/_react.default.createElement(_uiMenu.ArrowKeyNavigationProvider, {
158
+ type: _uiMenu.ArrowKeyNavigationType.MENU,
159
+ handleClose: handleArrowKeyNavigationClose,
160
+ disableArrowKeyNavigation: shouldDisableArrowKeyNavigation
114
161
  }, /*#__PURE__*/_react.default.createElement(_editorUiControlModel.SurfaceRenderer, {
115
162
  components: components,
116
163
  fallbacks: _components.LAYOUT_COLUMN_MENU_FALLBACKS,
117
164
  surface: _keys.LAYOUT_COLUMN_MENU
118
- }))));
165
+ }))))));
119
166
  });
@@ -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
+ }
@@ -19,6 +19,13 @@ var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
19
19
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles, @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports
20
20
 
21
21
  var PLACEHOLDER_SELECTOR = '.ProseMirror-focused .layoutSectionView-content-wrap.selected [data-layout-column] > [data-layout-content] > p:only-child:has(.ProseMirror-trailingBreak:only-child)';
22
+ var layoutColumnDangerPreviewStyle = (0, _react2.css)({
23
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors -- Ignored via go/DSP-18766
24
+ '.ProseMirror [data-layout-column].layout-column-danger-preview': {
25
+ backgroundColor: "var(--ds-background-danger, #FFECEB)",
26
+ boxShadow: "inset 0 0 0 2px ".concat("var(--ds-border-danger, #E2483D)")
27
+ }
28
+ });
22
29
  var getPlaceholderStyle = function getPlaceholderStyle(message) {
23
30
  if ((0, _experiments.editorExperiment)('platform_editor_controls', 'variant1')) {
24
31
  return (0, _react2.css)((0, _defineProperty2.default)({}, "".concat(PLACEHOLDER_SELECTOR), {
@@ -56,7 +63,10 @@ var GlobalStylesWrapper = exports.GlobalStylesWrapper = function GlobalStylesWra
56
63
  var placeholderText = (0, _experiments.editorExperiment)('platform_editor_controls', 'variant1') ? _messages.layoutMessages.controlslayoutPlaceholder : _messages.layoutMessages.layoutPlaceholder;
57
64
  return getPlaceholderStyle(formatMessage(placeholderText));
58
65
  }, [formatMessage]);
66
+ var globalStyles = (0, _react.useMemo)(function () {
67
+ return [placeholderStyle, layoutColumnDangerPreviewStyle];
68
+ }, [placeholderStyle]);
59
69
  return (0, _react2.jsx)(_react2.Global, {
60
- styles: placeholderStyle
70
+ styles: globalStyles
61
71
  });
62
72
  };
@@ -11,7 +11,8 @@ import { fg } from '@atlaskit/platform-feature-flags';
11
11
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
12
12
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
13
13
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
14
- import { createDefaultLayoutSection, createMultiColumnLayoutSection, deleteLayoutColumn, distributeLayoutColumns, insertLayoutColumn, insertLayoutColumnsWithAnalytics, setLayoutColumnValign, toggleLayoutColumnMenu } from './pm-plugins/actions';
14
+ import { createDefaultLayoutSection, createMultiColumnLayoutSection, deleteLayoutColumn, distributeLayoutColumns, insertLayoutColumn, insertLayoutColumnsWithAnalytics, setLayoutColumnDangerPreview, setLayoutColumnValign, toggleLayoutColumnMenu } from './pm-plugins/actions';
15
+ import { default as createLayoutKeymapPlugin } from './pm-plugins/keymap';
15
16
  import { default as createLayoutPlugin } from './pm-plugins/main';
16
17
  import { pluginKey } from './pm-plugins/plugin-key';
17
18
  import { default as createLayoutResizingPlugin } from './pm-plugins/resizing';
@@ -52,7 +53,7 @@ export const layoutPlugin = ({
52
53
  config: options = {},
53
54
  api
54
55
  }) => {
55
- var _api$analytics2, _api$analytics5;
56
+ var _api$analytics2;
56
57
  const allowAdvancedSingleColumnLayout = editorExperiment('advanced_layouts', true) && editorExperiment('single_column_layouts', true, {
57
58
  exposure: true
58
59
  });
@@ -101,13 +102,22 @@ export const layoutPlugin = ({
101
102
  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
103
  }
103
104
  }];
105
+ if (expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
106
+ plugins.push({
107
+ name: 'layoutKeymap',
108
+ plugin: () => createLayoutKeymapPlugin({
109
+ api
110
+ })
111
+ });
112
+ }
104
113
  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)) {
105
114
  plugins.push({
106
115
  name: 'layoutResizing',
107
116
  plugin: ({
108
117
  portalProviderAPI,
109
- eventDispatcher
110
- }) => createLayoutResizingPlugin(options, api, portalProviderAPI, eventDispatcher)
118
+ eventDispatcher,
119
+ getIntl
120
+ }) => createLayoutResizingPlugin(options, api, portalProviderAPI, eventDispatcher, getIntl())
111
121
  });
112
122
  }
113
123
  return plugins;
@@ -352,15 +362,19 @@ export const layoutPlugin = ({
352
362
  return pluginKey.getState(editorState);
353
363
  },
354
364
  commands: {
355
- deleteLayoutColumn: deleteLayoutColumn(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions, api),
365
+ deleteLayoutColumn: inputMethod => {
366
+ var _api$analytics5;
367
+ return deleteLayoutColumn(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions, api, inputMethod);
368
+ },
356
369
  distributeLayoutColumns: options => {
357
370
  var _api$analytics6;
358
371
  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);
359
372
  },
360
- insertLayoutColumn: side => {
373
+ insertLayoutColumn: (side, inputMethod) => {
361
374
  var _api$analytics7;
362
- 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);
375
+ 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, inputMethod);
363
376
  },
377
+ setLayoutColumnDangerPreview,
364
378
  setLayoutColumnValign: valign => {
365
379
  var _api$analytics8;
366
380
  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);
@@ -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
  }
@@ -11,7 +11,7 @@ import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
11
11
  import { EVEN_DISTRIBUTED_COL_WIDTHS, MAX_LAYOUT_COLUMNS, MAX_STANDARD_LAYOUT_COLUMNS, MIN_LAYOUT_COLUMN_WIDTH_PERCENT } from './consts';
12
12
  import { pluginKey } from './plugin-key';
13
13
  import { calculateDistribution, isDistributedUniformly, redistributeAfterDeletion, redistributeProportionally } from './utils/layout-column-distribution';
14
- import { getAllLayoutColumnsFromSelection, getSelectedLayoutColumnsFromSelection } from './utils/layout-column-selection';
14
+ import { getAllLayoutColumnsFromSelection, getLayoutColumnsFromContentSelection, getSelectedLayoutColumnsFromSelection } from './utils/layout-column-selection';
15
15
  export const ONE_COL_LAYOUTS = ['single'];
16
16
  export const TWO_COL_LAYOUTS = ['two_equal', 'two_left_sidebar', 'two_right_sidebar'];
17
17
  export const THREE_COL_LAYOUTS = ['three_equal', 'three_with_sidebars'];
@@ -556,13 +556,13 @@ const mapLayoutColumnPreservedSelection = (tr, api) => {
556
556
  export function getEffectiveMaxLayoutColumns() {
557
557
  return editorExperiment('advanced_layouts', true) ? MAX_LAYOUT_COLUMNS : MAX_STANDARD_LAYOUT_COLUMNS;
558
558
  }
559
- const insertLayoutColumnAt = (side, editorAnalyticsAPI) => ({
559
+ const insertLayoutColumnAt = (side, editorAnalyticsAPI, inputMethod = INPUT_METHOD.LAYOUT_COLUMN_MENU) => ({
560
560
  tr
561
561
  }) => {
562
562
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
563
563
  return null;
564
564
  }
565
- const selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
565
+ const selectedLayoutColumnsResult = getLayoutColumnsFromContentSelection(tr.selection);
566
566
  if (!selectedLayoutColumnsResult || selectedLayoutColumnsResult.selectedLayoutColumns.length === 0) {
567
567
  return null;
568
568
  }
@@ -624,7 +624,7 @@ const insertLayoutColumnAt = (side, editorAnalyticsAPI) => ({
624
624
  attributes: {
625
625
  columnCount: redistributedWidths.length,
626
626
  endIndex,
627
- inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
627
+ inputMethod,
628
628
  selectedCount: selectedColumnCount,
629
629
  side,
630
630
  startIndex
@@ -634,10 +634,10 @@ const insertLayoutColumnAt = (side, editorAnalyticsAPI) => ({
634
634
  tr.setMeta('scrollIntoView', false);
635
635
  return tr;
636
636
  };
637
- export const insertLayoutColumn = (side, editorAnalyticsAPI, api) => ({
637
+ export const insertLayoutColumn = (side, editorAnalyticsAPI, api, inputMethod = INPUT_METHOD.LAYOUT_COLUMN_MENU) => ({
638
638
  tr
639
639
  }) => {
640
- const result = insertLayoutColumnAt(side, editorAnalyticsAPI)({
640
+ const result = insertLayoutColumnAt(side, editorAnalyticsAPI, inputMethod)({
641
641
  tr
642
642
  });
643
643
  if (result) {
@@ -777,25 +777,40 @@ export const distributeLayoutColumns = (editorAnalyticsAPI, api) => ({
777
777
  };
778
778
  export const toggleLayoutColumnMenu = ({
779
779
  anchorPos,
780
- isOpen
780
+ isOpen,
781
+ openedViaKeyboard
781
782
  }) => ({
782
783
  tr
783
784
  }) => {
784
785
  tr.setMeta('toggleLayoutColumnMenu', {
785
786
  anchorPos,
786
- isOpen
787
+ isOpen,
788
+ openedViaKeyboard
787
789
  });
788
790
  tr.setMeta('scrollIntoView', false);
789
791
  return tr;
790
792
  };
791
- export const deleteLayoutColumn = (editorAnalyticsAPI, api) => ({
793
+ export const setLayoutColumnDangerPreview = show => ({
794
+ tr
795
+ }) => {
796
+ var _selectedLayoutColumn;
797
+ const selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
798
+ const positions = show ? (_selectedLayoutColumn = selectedLayoutColumnsResult === null || selectedLayoutColumnsResult === void 0 ? void 0 : selectedLayoutColumnsResult.selectedLayoutColumns.map(({
799
+ pos
800
+ }) => pos)) !== null && _selectedLayoutColumn !== void 0 ? _selectedLayoutColumn : [] : null;
801
+ tr.setMeta('layoutColumnDangerPreview', positions);
802
+ tr.setMeta('addToHistory', false);
803
+ tr.setMeta('scrollIntoView', false);
804
+ return tr;
805
+ };
806
+ export const deleteLayoutColumn = (editorAnalyticsAPI, api, inputMethod = INPUT_METHOD.LAYOUT_COLUMN_MENU) => ({
792
807
  tr
793
808
  }) => {
794
809
  var _api$blockControls4;
795
810
  if (!expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true)) {
796
811
  return null;
797
812
  }
798
- const selectedLayoutColumnsResult = getSelectedLayoutColumnsFromSelection(tr.selection);
813
+ const selectedLayoutColumnsResult = getLayoutColumnsFromContentSelection(tr.selection);
799
814
  if (!selectedLayoutColumnsResult || selectedLayoutColumnsResult.selectedLayoutColumns.length === 0) {
800
815
  return null;
801
816
  }
@@ -814,7 +829,7 @@ export const deleteLayoutColumn = (editorAnalyticsAPI, api) => ({
814
829
  attributes: {
815
830
  columnCount,
816
831
  endIndex,
817
- inputMethod: INPUT_METHOD.LAYOUT_COLUMN_MENU,
832
+ inputMethod,
818
833
  selectedCount: selectedLayoutColumns.length,
819
834
  startIndex
820
835
  },
@@ -0,0 +1,26 @@
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
+ const 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({
15
+ api
16
+ }) {
17
+ var _api$analytics, _api$analytics2, _api$analytics3, _api$analytics4, _api$analytics5;
18
+ const list = {};
19
+ bindLayoutColumnShortcut(addColumnBefore.common, insertLayoutColumn('left', api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions, api, INPUT_METHOD.KEYBOARD), list);
20
+ bindLayoutColumnShortcut(addColumnBeforeVO.common, insertLayoutColumn('left', api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, api, INPUT_METHOD.KEYBOARD), list);
21
+ bindLayoutColumnShortcut(addColumnAfter.common, insertLayoutColumn('right', api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions, api, INPUT_METHOD.KEYBOARD), list);
22
+ bindLayoutColumnShortcut(addColumnAfterVO.common, insertLayoutColumn('right', api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions, api, INPUT_METHOD.KEYBOARD), list);
23
+ bindLayoutColumnShortcut(deleteColumn.common, deleteLayoutColumn(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions, api, INPUT_METHOD.KEYBOARD), list);
24
+ return keymap(list);
25
+ }
26
+ export default keymapPlugin;