@atlaskit/editor-plugin-layout 10.2.0 → 10.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @atlaskit/editor-plugin-layout
2
2
 
3
+ ## 10.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`4c22fe79f104e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/4c22fe79f104e) -
8
+ Guard against undefined plugin state when reading shared state in floating-toolbar handler
9
+ (layout) and useEffect deps array (block-controls drag handle); fixes intermittent crash during
10
+ editor reconfigure / partial-preset transitions.
11
+
12
+ ## 10.3.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [`89a8af72aa2c9`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/89a8af72aa2c9) -
17
+ Add reusable toolbar menu container and render the layout column menu in a popup
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies
22
+
3
23
  ## 10.2.0
4
24
 
5
25
  ### Minor Changes
@@ -121,20 +121,23 @@ var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
121
121
  },
122
122
  pluginsOptions: {
123
123
  floatingToolbar: function floatingToolbar(state, intl) {
124
- var _ref3 = _pluginKey.pluginKey.getState(state),
125
- pos = _ref3.pos,
126
- allowBreakout = _ref3.allowBreakout,
127
- addSidebarLayouts = _ref3.addSidebarLayouts,
128
- allowSingleColumnLayout = _ref3.allowSingleColumnLayout,
129
- isResizing = _ref3.isResizing;
124
+ var layoutState = _pluginKey.pluginKey.getState(state);
125
+ if (!layoutState) {
126
+ return undefined;
127
+ }
128
+ var pos = layoutState.pos,
129
+ allowBreakout = layoutState.allowBreakout,
130
+ addSidebarLayouts = layoutState.addSidebarLayouts,
131
+ allowSingleColumnLayout = layoutState.allowSingleColumnLayout,
132
+ isResizing = layoutState.isResizing;
130
133
  var shouldHideToolbar = isResizing && (0, _experiments.editorExperiment)('single_column_layouts', true);
131
134
  if (pos !== null && !shouldHideToolbar) {
132
135
  return (0, _toolbar.buildToolbar)(state, intl, pos, allowBreakout, addSidebarLayouts, allowSingleColumnLayout, allowAdvancedSingleColumnLayout, api);
133
136
  }
134
137
  return undefined;
135
138
  },
136
- quickInsert: function quickInsert(_ref4) {
137
- var formatMessage = _ref4.formatMessage;
139
+ quickInsert: function quickInsert(_ref3) {
140
+ var formatMessage = _ref3.formatMessage;
138
141
  var withInsertLayoutAnalytics = function withInsertLayoutAnalytics(tr, columnCount) {
139
142
  var _api$analytics2;
140
143
  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({
@@ -283,9 +286,17 @@ var layoutPlugin = exports.layoutPlugin = function layoutPlugin(_ref) {
283
286
  }
284
287
  }
285
288
  },
286
- contentComponent: function contentComponent() {
287
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, (0, _experiments.editorExperiment)('advanced_layouts', true) ? /*#__PURE__*/_react.default.createElement(_globalStyles.GlobalStylesWrapper, null) : null, (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true) ? /*#__PURE__*/_react.default.createElement(_LayoutColumnMenu.LayoutColumnMenu, {
288
- api: api
289
+ contentComponent: function contentComponent(_ref4) {
290
+ var editorView = _ref4.editorView,
291
+ popupsMountPoint = _ref4.popupsMountPoint,
292
+ popupsBoundariesElement = _ref4.popupsBoundariesElement,
293
+ popupsScrollableElement = _ref4.popupsScrollableElement;
294
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, (0, _experiments.editorExperiment)('advanced_layouts', true) ? /*#__PURE__*/_react.default.createElement(_globalStyles.GlobalStylesWrapper, null) : null, (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_layout_column_menu', 'isEnabled', true) && editorView ? /*#__PURE__*/_react.default.createElement(_LayoutColumnMenu.LayoutColumnMenu, {
295
+ api: api,
296
+ editorView: editorView,
297
+ mountTo: popupsMountPoint,
298
+ boundariesElement: popupsBoundariesElement,
299
+ scrollableElement: popupsScrollableElement
289
300
  }) : null);
290
301
  },
291
302
  getSharedState: function getSharedState(editorState) {
@@ -8,6 +8,7 @@ exports.getLayoutColumnMenuComponents = exports.LAYOUT_COLUMN_MENU_FALLBACKS = e
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _react = _interopRequireDefault(require("react"));
10
10
  var _editorToolbar = require("@atlaskit/editor-toolbar");
11
+ var _toolbarMenuContainer = require("@atlaskit/editor-toolbar/toolbar-menu-container");
11
12
  var _DistributeColumnsDropdownItem = require("./DistributeColumnsDropdownItem");
12
13
  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; }
13
14
  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; }
@@ -33,7 +34,9 @@ var LAYOUT_COLUMN_MENU_FALLBACKS = exports.LAYOUT_COLUMN_MENU_FALLBACKS = {
33
34
  };
34
35
  var getLayoutColumnMenuComponents = exports.getLayoutColumnMenuComponents = function getLayoutColumnMenuComponents(_ref2) {
35
36
  var api = _ref2.api;
36
- return [_objectSpread({}, LAYOUT_COLUMN_MENU), _objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU_SECTION), {}, {
37
+ return [_objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU), {}, {
38
+ component: _toolbarMenuContainer.ToolbarMenuContainer
39
+ }), _objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU_SECTION), {}, {
37
40
  parents: [_objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU), {}, {
38
41
  rank: LAYOUT_COLUMN_MENU_RANK[LAYOUT_COLUMN_MENU_SECTION.key]
39
42
  })]
@@ -1,34 +1,91 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.LayoutColumnMenu = void 0;
8
- var _react = _interopRequireDefault(require("react"));
8
+ var _react = _interopRequireWildcard(require("react"));
9
9
  var _hooks = require("@atlaskit/editor-common/hooks");
10
+ var _toolbar = require("@atlaskit/editor-common/toolbar");
11
+ var _ui = require("@atlaskit/editor-common/ui");
12
+ var _uiReact = require("@atlaskit/editor-common/ui-react");
13
+ var _utils = require("@atlaskit/editor-prosemirror/utils");
14
+ var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
15
+ var _editorToolbar = require("@atlaskit/editor-toolbar");
10
16
  var _editorUiControlModel = require("@atlaskit/editor-ui-control-model");
11
17
  var _components = require("./components");
12
- var LayoutColumnMenu = exports.LayoutColumnMenu = function LayoutColumnMenu(_ref) {
18
+ 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); }
19
+ var PopupWithListeners = (0, _uiReact.withReactEditorViewOuterListeners)(_ui.Popup);
20
+ var LAYOUT_COLUMN_MENU_POPUP_OFFSET = [0, 10];
21
+ var getLayoutColumnMenuTarget = function getLayoutColumnMenuTarget(editorView, selectionAnchorPos) {
22
+ if (selectionAnchorPos === undefined) {
23
+ return null;
24
+ }
25
+ var selectionNode = editorView.state.doc.nodeAt(selectionAnchorPos);
26
+ if ((selectionNode === null || selectionNode === void 0 ? void 0 : selectionNode.type.name) !== 'layoutColumn') {
27
+ return null;
28
+ }
29
+ return (0, _utils.findDomRefAtPos)(selectionAnchorPos, editorView.domAtPos.bind(editorView));
30
+ };
31
+ var LayoutColumnMenu = exports.LayoutColumnMenu = /*#__PURE__*/_react.default.memo(function LayoutColumnMenu(_ref) {
13
32
  var _api$uiControlRegistr, _api$uiControlRegistr2;
14
- var api = _ref.api;
15
- var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['layout'], function (states) {
16
- var _states$layoutState$i, _states$layoutState;
33
+ var api = _ref.api,
34
+ editorView = _ref.editorView,
35
+ mountTo = _ref.mountTo,
36
+ boundariesElement = _ref.boundariesElement,
37
+ scrollableElement = _ref.scrollableElement;
38
+ var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['layout', 'selection'], function (states) {
39
+ var _states$layoutState$i, _states$layoutState, _states$selectionStat;
17
40
  return {
18
- 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
41
+ 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,
42
+ selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection
19
43
  };
20
44
  }),
21
- isLayoutColumnMenuOpen = _useSharedPluginState.isLayoutColumnMenuOpen;
22
- if (!isLayoutColumnMenuOpen) {
23
- return null;
24
- }
45
+ isLayoutColumnMenuOpen = _useSharedPluginState.isLayoutColumnMenuOpen,
46
+ selection = _useSharedPluginState.selection;
47
+ var closeLayoutColumnMenu = (0, _react.useCallback)(function () {
48
+ var _api$core, _api$layout;
49
+ 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.toggleLayoutColumnMenu({
50
+ isOpen: false
51
+ }));
52
+ }, [api]);
53
+ var handleSetIsOpen = (0, _react.useCallback)(function (isOpen) {
54
+ if (!isOpen) {
55
+ closeLayoutColumnMenu();
56
+ }
57
+ }, [closeLayoutColumnMenu]);
25
58
  var components = (_api$uiControlRegistr = api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(_components.LAYOUT_COLUMN_MENU.key)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
26
- if (components.length === 0) {
59
+ var target = isLayoutColumnMenuOpen ? getLayoutColumnMenuTarget(editorView, selection === null || selection === void 0 ? void 0 : selection.from) : null;
60
+ var hasValidTarget = target instanceof HTMLElement;
61
+ (0, _react.useEffect)(function () {
62
+ if (isLayoutColumnMenuOpen && (!hasValidTarget || components.length === 0)) {
63
+ closeLayoutColumnMenu();
64
+ }
65
+ }, [closeLayoutColumnMenu, components.length, hasValidTarget, isLayoutColumnMenuOpen]);
66
+ if (!isLayoutColumnMenuOpen || components.length === 0 || !hasValidTarget) {
27
67
  return null;
28
68
  }
29
- return /*#__PURE__*/_react.default.createElement(_editorUiControlModel.SurfaceRenderer, {
69
+ return /*#__PURE__*/_react.default.createElement(PopupWithListeners, {
70
+ target: target,
71
+ mountTo: mountTo,
72
+ boundariesElement: boundariesElement,
73
+ scrollableElement: scrollableElement,
74
+ zIndex: _editorSharedStyles.akEditorFloatingPanelZIndex,
75
+ alignX: "center",
76
+ alignY: "top",
77
+ forcePlacement: true,
78
+ offset: LAYOUT_COLUMN_MENU_POPUP_OFFSET,
79
+ handleClickOutside: closeLayoutColumnMenu,
80
+ handleEscapeKeydown: closeLayoutColumnMenu
81
+ }, /*#__PURE__*/_react.default.createElement(_toolbar.EditorToolbarProvider, {
82
+ editorView: editorView
83
+ }, /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownMenuProvider, {
84
+ isOpen: isLayoutColumnMenuOpen,
85
+ setIsOpen: handleSetIsOpen
86
+ }, /*#__PURE__*/_react.default.createElement(_editorUiControlModel.SurfaceRenderer, {
30
87
  components: components,
31
88
  fallbacks: _components.LAYOUT_COLUMN_MENU_FALLBACKS,
32
89
  surface: _components.LAYOUT_COLUMN_MENU
33
- });
34
- };
90
+ }))));
91
+ });
@@ -110,13 +110,17 @@ export const layoutPlugin = ({
110
110
  },
111
111
  pluginsOptions: {
112
112
  floatingToolbar(state, intl) {
113
+ const layoutState = pluginKey.getState(state);
114
+ if (!layoutState) {
115
+ return undefined;
116
+ }
113
117
  const {
114
118
  pos,
115
119
  allowBreakout,
116
120
  addSidebarLayouts,
117
121
  allowSingleColumnLayout,
118
122
  isResizing
119
- } = pluginKey.getState(state);
123
+ } = layoutState;
120
124
  const shouldHideToolbar = isResizing && editorExperiment('single_column_layouts', true);
121
125
  if (pos !== null && !shouldHideToolbar) {
122
126
  return buildToolbar(state, intl, pos, allowBreakout, addSidebarLayouts, allowSingleColumnLayout, allowAdvancedSingleColumnLayout, api);
@@ -262,9 +266,18 @@ export const layoutPlugin = ({
262
266
  }
263
267
  }
264
268
  },
265
- contentComponent() {
266
- return /*#__PURE__*/React.createElement(React.Fragment, null, editorExperiment('advanced_layouts', true) ? /*#__PURE__*/React.createElement(GlobalStylesWrapper, null) : null, expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true) ? /*#__PURE__*/React.createElement(LayoutColumnMenu, {
267
- api: api
269
+ contentComponent({
270
+ editorView,
271
+ popupsMountPoint,
272
+ popupsBoundariesElement,
273
+ popupsScrollableElement
274
+ }) {
275
+ return /*#__PURE__*/React.createElement(React.Fragment, null, editorExperiment('advanced_layouts', true) ? /*#__PURE__*/React.createElement(GlobalStylesWrapper, null) : null, expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true) && editorView ? /*#__PURE__*/React.createElement(LayoutColumnMenu, {
276
+ api: api,
277
+ editorView: editorView,
278
+ mountTo: popupsMountPoint,
279
+ boundariesElement: popupsBoundariesElement,
280
+ scrollableElement: popupsScrollableElement
268
281
  }) : null);
269
282
  },
270
283
  getSharedState(editorState) {
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { ToolbarDropdownItemSection } from '@atlaskit/editor-toolbar';
3
+ import { ToolbarMenuContainer } from '@atlaskit/editor-toolbar/toolbar-menu-container';
3
4
  import { createDistributeColumnsDropdownItem } from './DistributeColumnsDropdownItem';
4
5
  export const LAYOUT_COLUMN_MENU = {
5
6
  key: 'layout-column-menu',
@@ -28,7 +29,8 @@ export const getLayoutColumnMenuComponents = ({
28
29
  api
29
30
  }) => {
30
31
  return [{
31
- ...LAYOUT_COLUMN_MENU
32
+ ...LAYOUT_COLUMN_MENU,
33
+ component: ToolbarMenuContainer
32
34
  }, {
33
35
  ...LAYOUT_COLUMN_MENU_SECTION,
34
36
  parents: [{
@@ -1,29 +1,85 @@
1
- import React from 'react';
1
+ import React, { useCallback, useEffect } from 'react';
2
2
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
+ import { EditorToolbarProvider } from '@atlaskit/editor-common/toolbar';
4
+ import { Popup } from '@atlaskit/editor-common/ui';
5
+ import { withReactEditorViewOuterListeners } from '@atlaskit/editor-common/ui-react';
6
+ import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
7
+ import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
8
+ import { ToolbarDropdownMenuProvider } from '@atlaskit/editor-toolbar';
3
9
  import { SurfaceRenderer } from '@atlaskit/editor-ui-control-model';
4
10
  import { LAYOUT_COLUMN_MENU, LAYOUT_COLUMN_MENU_FALLBACKS } from './components';
5
- export const LayoutColumnMenu = ({
6
- api
7
- }) => {
11
+ const PopupWithListeners = withReactEditorViewOuterListeners(Popup);
12
+ const LAYOUT_COLUMN_MENU_POPUP_OFFSET = [0, 10];
13
+ const getLayoutColumnMenuTarget = (editorView, selectionAnchorPos) => {
14
+ if (selectionAnchorPos === undefined) {
15
+ return null;
16
+ }
17
+ const selectionNode = editorView.state.doc.nodeAt(selectionAnchorPos);
18
+ if ((selectionNode === null || selectionNode === void 0 ? void 0 : selectionNode.type.name) !== 'layoutColumn') {
19
+ return null;
20
+ }
21
+ return findDomRefAtPos(selectionAnchorPos, editorView.domAtPos.bind(editorView));
22
+ };
23
+ export const LayoutColumnMenu = /*#__PURE__*/React.memo(function LayoutColumnMenu({
24
+ api,
25
+ editorView,
26
+ mountTo,
27
+ boundariesElement,
28
+ scrollableElement
29
+ }) {
8
30
  var _api$uiControlRegistr, _api$uiControlRegistr2;
9
31
  const {
10
- isLayoutColumnMenuOpen
11
- } = useSharedPluginStateWithSelector(api, ['layout'], states => {
12
- var _states$layoutState$i, _states$layoutState;
32
+ isLayoutColumnMenuOpen,
33
+ selection
34
+ } = useSharedPluginStateWithSelector(api, ['layout', 'selection'], states => {
35
+ var _states$layoutState$i, _states$layoutState, _states$selectionStat;
13
36
  return {
14
- 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
37
+ 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,
38
+ selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection
15
39
  };
16
40
  });
17
- if (!isLayoutColumnMenuOpen) {
18
- return null;
19
- }
41
+ const closeLayoutColumnMenu = useCallback(() => {
42
+ var _api$core, _api$layout;
43
+ api === null || api === void 0 ? void 0 : (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(api === null || api === void 0 ? void 0 : (_api$layout = api.layout) === null || _api$layout === void 0 ? void 0 : _api$layout.commands.toggleLayoutColumnMenu({
44
+ isOpen: false
45
+ }));
46
+ }, [api]);
47
+ const handleSetIsOpen = useCallback(isOpen => {
48
+ if (!isOpen) {
49
+ closeLayoutColumnMenu();
50
+ }
51
+ }, [closeLayoutColumnMenu]);
20
52
  const components = (_api$uiControlRegistr = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(LAYOUT_COLUMN_MENU.key)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
21
- if (components.length === 0) {
53
+ const target = isLayoutColumnMenuOpen ? getLayoutColumnMenuTarget(editorView, selection === null || selection === void 0 ? void 0 : selection.from) : null;
54
+ const hasValidTarget = target instanceof HTMLElement;
55
+ useEffect(() => {
56
+ if (isLayoutColumnMenuOpen && (!hasValidTarget || components.length === 0)) {
57
+ closeLayoutColumnMenu();
58
+ }
59
+ }, [closeLayoutColumnMenu, components.length, hasValidTarget, isLayoutColumnMenuOpen]);
60
+ if (!isLayoutColumnMenuOpen || components.length === 0 || !hasValidTarget) {
22
61
  return null;
23
62
  }
24
- return /*#__PURE__*/React.createElement(SurfaceRenderer, {
63
+ return /*#__PURE__*/React.createElement(PopupWithListeners, {
64
+ target: target,
65
+ mountTo: mountTo,
66
+ boundariesElement: boundariesElement,
67
+ scrollableElement: scrollableElement,
68
+ zIndex: akEditorFloatingPanelZIndex,
69
+ alignX: "center",
70
+ alignY: "top",
71
+ forcePlacement: true,
72
+ offset: LAYOUT_COLUMN_MENU_POPUP_OFFSET,
73
+ handleClickOutside: closeLayoutColumnMenu,
74
+ handleEscapeKeydown: closeLayoutColumnMenu
75
+ }, /*#__PURE__*/React.createElement(EditorToolbarProvider, {
76
+ editorView: editorView
77
+ }, /*#__PURE__*/React.createElement(ToolbarDropdownMenuProvider, {
78
+ isOpen: isLayoutColumnMenuOpen,
79
+ setIsOpen: handleSetIsOpen
80
+ }, /*#__PURE__*/React.createElement(SurfaceRenderer, {
25
81
  components: components,
26
82
  fallbacks: LAYOUT_COLUMN_MENU_FALLBACKS,
27
83
  surface: LAYOUT_COLUMN_MENU
28
- });
29
- };
84
+ }))));
85
+ });
@@ -114,20 +114,23 @@ export var layoutPlugin = function layoutPlugin(_ref) {
114
114
  },
115
115
  pluginsOptions: {
116
116
  floatingToolbar: function floatingToolbar(state, intl) {
117
- var _ref3 = pluginKey.getState(state),
118
- pos = _ref3.pos,
119
- allowBreakout = _ref3.allowBreakout,
120
- addSidebarLayouts = _ref3.addSidebarLayouts,
121
- allowSingleColumnLayout = _ref3.allowSingleColumnLayout,
122
- isResizing = _ref3.isResizing;
117
+ var layoutState = pluginKey.getState(state);
118
+ if (!layoutState) {
119
+ return undefined;
120
+ }
121
+ var pos = layoutState.pos,
122
+ allowBreakout = layoutState.allowBreakout,
123
+ addSidebarLayouts = layoutState.addSidebarLayouts,
124
+ allowSingleColumnLayout = layoutState.allowSingleColumnLayout,
125
+ isResizing = layoutState.isResizing;
123
126
  var shouldHideToolbar = isResizing && editorExperiment('single_column_layouts', true);
124
127
  if (pos !== null && !shouldHideToolbar) {
125
128
  return buildToolbar(state, intl, pos, allowBreakout, addSidebarLayouts, allowSingleColumnLayout, allowAdvancedSingleColumnLayout, api);
126
129
  }
127
130
  return undefined;
128
131
  },
129
- quickInsert: function quickInsert(_ref4) {
130
- var formatMessage = _ref4.formatMessage;
132
+ quickInsert: function quickInsert(_ref3) {
133
+ var formatMessage = _ref3.formatMessage;
131
134
  var withInsertLayoutAnalytics = function withInsertLayoutAnalytics(tr, columnCount) {
132
135
  var _api$analytics2;
133
136
  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({
@@ -276,9 +279,17 @@ export var layoutPlugin = function layoutPlugin(_ref) {
276
279
  }
277
280
  }
278
281
  },
279
- contentComponent: function contentComponent() {
280
- return /*#__PURE__*/React.createElement(React.Fragment, null, editorExperiment('advanced_layouts', true) ? /*#__PURE__*/React.createElement(GlobalStylesWrapper, null) : null, expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true) ? /*#__PURE__*/React.createElement(LayoutColumnMenu, {
281
- api: api
282
+ contentComponent: function contentComponent(_ref4) {
283
+ var editorView = _ref4.editorView,
284
+ popupsMountPoint = _ref4.popupsMountPoint,
285
+ popupsBoundariesElement = _ref4.popupsBoundariesElement,
286
+ popupsScrollableElement = _ref4.popupsScrollableElement;
287
+ return /*#__PURE__*/React.createElement(React.Fragment, null, editorExperiment('advanced_layouts', true) ? /*#__PURE__*/React.createElement(GlobalStylesWrapper, null) : null, expValEqualsNoExposure('platform_editor_layout_column_menu', 'isEnabled', true) && editorView ? /*#__PURE__*/React.createElement(LayoutColumnMenu, {
288
+ api: api,
289
+ editorView: editorView,
290
+ mountTo: popupsMountPoint,
291
+ boundariesElement: popupsBoundariesElement,
292
+ scrollableElement: popupsScrollableElement
282
293
  }) : null);
283
294
  },
284
295
  getSharedState: function getSharedState(editorState) {
@@ -3,6 +3,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
3
3
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
4
  import React from 'react';
5
5
  import { ToolbarDropdownItemSection } from '@atlaskit/editor-toolbar';
6
+ import { ToolbarMenuContainer } from '@atlaskit/editor-toolbar/toolbar-menu-container';
6
7
  import { createDistributeColumnsDropdownItem } from './DistributeColumnsDropdownItem';
7
8
  export var LAYOUT_COLUMN_MENU = {
8
9
  key: 'layout-column-menu',
@@ -26,7 +27,9 @@ export var LAYOUT_COLUMN_MENU_FALLBACKS = {
26
27
  };
27
28
  export var getLayoutColumnMenuComponents = function getLayoutColumnMenuComponents(_ref2) {
28
29
  var api = _ref2.api;
29
- return [_objectSpread({}, LAYOUT_COLUMN_MENU), _objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU_SECTION), {}, {
30
+ return [_objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU), {}, {
31
+ component: ToolbarMenuContainer
32
+ }), _objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU_SECTION), {}, {
30
33
  parents: [_objectSpread(_objectSpread({}, LAYOUT_COLUMN_MENU), {}, {
31
34
  rank: LAYOUT_COLUMN_MENU_RANK[LAYOUT_COLUMN_MENU_SECTION.key]
32
35
  })]
@@ -1,27 +1,83 @@
1
- import React from 'react';
1
+ import React, { useCallback, useEffect } from 'react';
2
2
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
+ import { EditorToolbarProvider } from '@atlaskit/editor-common/toolbar';
4
+ import { Popup } from '@atlaskit/editor-common/ui';
5
+ import { withReactEditorViewOuterListeners } from '@atlaskit/editor-common/ui-react';
6
+ import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
7
+ import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
8
+ import { ToolbarDropdownMenuProvider } from '@atlaskit/editor-toolbar';
3
9
  import { SurfaceRenderer } from '@atlaskit/editor-ui-control-model';
4
10
  import { LAYOUT_COLUMN_MENU, LAYOUT_COLUMN_MENU_FALLBACKS } from './components';
5
- export var LayoutColumnMenu = function LayoutColumnMenu(_ref) {
11
+ var PopupWithListeners = withReactEditorViewOuterListeners(Popup);
12
+ var LAYOUT_COLUMN_MENU_POPUP_OFFSET = [0, 10];
13
+ var getLayoutColumnMenuTarget = function getLayoutColumnMenuTarget(editorView, selectionAnchorPos) {
14
+ if (selectionAnchorPos === undefined) {
15
+ return null;
16
+ }
17
+ var selectionNode = editorView.state.doc.nodeAt(selectionAnchorPos);
18
+ if ((selectionNode === null || selectionNode === void 0 ? void 0 : selectionNode.type.name) !== 'layoutColumn') {
19
+ return null;
20
+ }
21
+ return findDomRefAtPos(selectionAnchorPos, editorView.domAtPos.bind(editorView));
22
+ };
23
+ export var LayoutColumnMenu = /*#__PURE__*/React.memo(function LayoutColumnMenu(_ref) {
6
24
  var _api$uiControlRegistr, _api$uiControlRegistr2;
7
- var api = _ref.api;
8
- var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['layout'], function (states) {
9
- var _states$layoutState$i, _states$layoutState;
25
+ var api = _ref.api,
26
+ editorView = _ref.editorView,
27
+ mountTo = _ref.mountTo,
28
+ boundariesElement = _ref.boundariesElement,
29
+ scrollableElement = _ref.scrollableElement;
30
+ var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['layout', 'selection'], function (states) {
31
+ var _states$layoutState$i, _states$layoutState, _states$selectionStat;
10
32
  return {
11
- 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
33
+ 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,
34
+ selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection
12
35
  };
13
36
  }),
14
- isLayoutColumnMenuOpen = _useSharedPluginState.isLayoutColumnMenuOpen;
15
- if (!isLayoutColumnMenuOpen) {
16
- return null;
17
- }
37
+ isLayoutColumnMenuOpen = _useSharedPluginState.isLayoutColumnMenuOpen,
38
+ selection = _useSharedPluginState.selection;
39
+ var closeLayoutColumnMenu = useCallback(function () {
40
+ var _api$core, _api$layout;
41
+ 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.toggleLayoutColumnMenu({
42
+ isOpen: false
43
+ }));
44
+ }, [api]);
45
+ var handleSetIsOpen = useCallback(function (isOpen) {
46
+ if (!isOpen) {
47
+ closeLayoutColumnMenu();
48
+ }
49
+ }, [closeLayoutColumnMenu]);
18
50
  var components = (_api$uiControlRegistr = api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(LAYOUT_COLUMN_MENU.key)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [];
19
- if (components.length === 0) {
51
+ var target = isLayoutColumnMenuOpen ? getLayoutColumnMenuTarget(editorView, selection === null || selection === void 0 ? void 0 : selection.from) : null;
52
+ var hasValidTarget = target instanceof HTMLElement;
53
+ useEffect(function () {
54
+ if (isLayoutColumnMenuOpen && (!hasValidTarget || components.length === 0)) {
55
+ closeLayoutColumnMenu();
56
+ }
57
+ }, [closeLayoutColumnMenu, components.length, hasValidTarget, isLayoutColumnMenuOpen]);
58
+ if (!isLayoutColumnMenuOpen || components.length === 0 || !hasValidTarget) {
20
59
  return null;
21
60
  }
22
- return /*#__PURE__*/React.createElement(SurfaceRenderer, {
61
+ return /*#__PURE__*/React.createElement(PopupWithListeners, {
62
+ target: target,
63
+ mountTo: mountTo,
64
+ boundariesElement: boundariesElement,
65
+ scrollableElement: scrollableElement,
66
+ zIndex: akEditorFloatingPanelZIndex,
67
+ alignX: "center",
68
+ alignY: "top",
69
+ forcePlacement: true,
70
+ offset: LAYOUT_COLUMN_MENU_POPUP_OFFSET,
71
+ handleClickOutside: closeLayoutColumnMenu,
72
+ handleEscapeKeydown: closeLayoutColumnMenu
73
+ }, /*#__PURE__*/React.createElement(EditorToolbarProvider, {
74
+ editorView: editorView
75
+ }, /*#__PURE__*/React.createElement(ToolbarDropdownMenuProvider, {
76
+ isOpen: isLayoutColumnMenuOpen,
77
+ setIsOpen: handleSetIsOpen
78
+ }, /*#__PURE__*/React.createElement(SurfaceRenderer, {
23
79
  components: components,
24
80
  fallbacks: LAYOUT_COLUMN_MENU_FALLBACKS,
25
81
  surface: LAYOUT_COLUMN_MENU
26
- });
27
- };
82
+ }))));
83
+ });
@@ -1,8 +1,13 @@
1
1
  import React from 'react';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
4
  import type { LayoutPlugin } from '../../layoutPluginType';
4
5
  type LayoutColumnMenuProps = {
5
6
  api: ExtractInjectionAPI<LayoutPlugin> | undefined;
7
+ boundariesElement?: HTMLElement;
8
+ editorView: EditorView;
9
+ mountTo?: HTMLElement;
10
+ scrollableElement?: HTMLElement;
6
11
  };
7
- export declare const LayoutColumnMenu: ({ api }: LayoutColumnMenuProps) => React.JSX.Element | null;
12
+ export declare const LayoutColumnMenu: React.NamedExoticComponent<LayoutColumnMenuProps>;
8
13
  export {};
@@ -1,8 +1,13 @@
1
1
  import React from 'react';
2
2
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
3
+ import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
4
  import type { LayoutPlugin } from '../../layoutPluginType';
4
5
  type LayoutColumnMenuProps = {
5
6
  api: ExtractInjectionAPI<LayoutPlugin> | undefined;
7
+ boundariesElement?: HTMLElement;
8
+ editorView: EditorView;
9
+ mountTo?: HTMLElement;
10
+ scrollableElement?: HTMLElement;
6
11
  };
7
- export declare const LayoutColumnMenu: ({ api }: LayoutColumnMenuProps) => React.JSX.Element | null;
12
+ export declare const LayoutColumnMenu: React.NamedExoticComponent<LayoutColumnMenuProps>;
8
13
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-layout",
3
- "version": "10.2.0",
3
+ "version": "10.3.1",
4
4
  "description": "Layout plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -37,18 +37,18 @@
37
37
  "@atlaskit/editor-plugin-editor-disabled": "^10.1.0",
38
38
  "@atlaskit/editor-plugin-guideline": "^10.1.0",
39
39
  "@atlaskit/editor-plugin-interaction": "^19.1.0",
40
- "@atlaskit/editor-plugin-selection": "^10.0.0",
41
- "@atlaskit/editor-plugin-toolbar": "^7.2.0",
42
- "@atlaskit/editor-plugin-ui-control-registry": "^4.0.0",
43
- "@atlaskit/editor-plugin-width": "^11.0.0",
40
+ "@atlaskit/editor-plugin-selection": "^10.1.0",
41
+ "@atlaskit/editor-plugin-toolbar": "^7.3.0",
42
+ "@atlaskit/editor-plugin-ui-control-registry": "^4.1.0",
43
+ "@atlaskit/editor-plugin-width": "^11.1.0",
44
44
  "@atlaskit/editor-prosemirror": "^7.3.0",
45
45
  "@atlaskit/editor-shared-styles": "^3.10.0",
46
- "@atlaskit/editor-toolbar": "^1.2.0",
46
+ "@atlaskit/editor-toolbar": "^1.3.0",
47
47
  "@atlaskit/editor-ui-control-model": "^1.1.0",
48
48
  "@atlaskit/icon": "^34.5.0",
49
49
  "@atlaskit/icon-lab": "^6.9.0",
50
50
  "@atlaskit/platform-feature-flags": "^1.1.0",
51
- "@atlaskit/tmp-editor-statsig": "^80.0.0",
51
+ "@atlaskit/tmp-editor-statsig": "^80.1.0",
52
52
  "@atlaskit/tokens": "^13.0.0",
53
53
  "@babel/runtime": "^7.0.0",
54
54
  "@emotion/react": "^11.7.1",