@atlaskit/editor-plugin-table 1.6.2 → 1.6.3

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 (34) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/plugins/table/index.js +2 -1
  3. package/dist/cjs/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +3 -1
  4. package/dist/cjs/plugins/table/types.js +1 -0
  5. package/dist/cjs/plugins/table/ui/FloatingContextualButton/FixedButton.js +133 -0
  6. package/dist/cjs/plugins/table/ui/FloatingContextualButton/index.js +73 -128
  7. package/dist/cjs/version.json +1 -1
  8. package/dist/es2019/plugins/table/index.js +2 -1
  9. package/dist/es2019/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +3 -1
  10. package/dist/es2019/plugins/table/types.js +1 -0
  11. package/dist/es2019/plugins/table/ui/FloatingContextualButton/FixedButton.js +120 -0
  12. package/dist/es2019/plugins/table/ui/FloatingContextualButton/index.js +76 -108
  13. package/dist/es2019/version.json +1 -1
  14. package/dist/esm/plugins/table/index.js +2 -1
  15. package/dist/esm/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +3 -1
  16. package/dist/esm/plugins/table/types.js +1 -0
  17. package/dist/esm/plugins/table/ui/FloatingContextualButton/FixedButton.js +118 -0
  18. package/dist/esm/plugins/table/ui/FloatingContextualButton/index.js +73 -129
  19. package/dist/esm/version.json +1 -1
  20. package/dist/types/plugins/table/types.d.ts +1 -0
  21. package/dist/types/plugins/table/ui/FloatingContextualButton/FixedButton.d.ts +23 -0
  22. package/dist/types/plugins/table/ui/FloatingContextualButton/index.d.ts +1 -9
  23. package/dist/types-ts4.5/plugins/table/types.d.ts +1 -0
  24. package/dist/types-ts4.5/plugins/table/ui/FloatingContextualButton/FixedButton.d.ts +23 -0
  25. package/dist/types-ts4.5/plugins/table/ui/FloatingContextualButton/index.d.ts +1 -9
  26. package/package.json +4 -3
  27. package/src/__tests__/playwright/__fixtures__/base-adfs.ts +1486 -0
  28. package/src/__tests__/playwright/extensions.spec.ts +67 -0
  29. package/src/__tests__/unit/ui/FixedButton.tsx +214 -0
  30. package/src/plugins/table/index.tsx +1 -0
  31. package/src/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.ts +2 -1
  32. package/src/plugins/table/types.ts +1 -0
  33. package/src/plugins/table/ui/FloatingContextualButton/FixedButton.tsx +175 -0
  34. package/src/plugins/table/ui/FloatingContextualButton/index.tsx +41 -95
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/editor-plugin-table
2
2
 
3
+ ## 1.6.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`1202f6f0a82`](https://bitbucket.org/atlassian/atlassian-frontend/commits/1202f6f0a82) - ED-16692: add logic to position FloatingContextualButton correctly when sticky and scrolling
8
+
3
9
  ## 1.6.2
4
10
 
5
11
  ### Patch Changes
@@ -261,7 +261,8 @@ var tablesPlugin = function tablesPlugin(options, api) {
261
261
  dispatchAnalyticsEvent: dispatchAnalyticsEvent,
262
262
  isContextualMenuOpen: isContextualMenuOpen,
263
263
  layout: layout,
264
- stickyHeader: stickyHeader
264
+ stickyHeader: stickyHeader,
265
+ tableWrapper: tableWrapperTarget
265
266
  }), allowControls && /*#__PURE__*/_react.default.createElement(_FloatingInsertButton.default, {
266
267
  tableNode: tableNode,
267
268
  tableRef: tableRef,
@@ -232,7 +232,8 @@ var TableRowNodeView = /*#__PURE__*/function () {
232
232
  if (_this.stickyRowHeight && _this.stickyRowHeight > window.innerHeight / 2) {
233
233
  return;
234
234
  }
235
- var table = tree.table;
235
+ var table = tree.table,
236
+ wrapper = tree.wrapper;
236
237
 
237
238
  // ED-16035 Make sure sticky header is only applied to first row
238
239
  var tbody = _this.dom.parentElement;
@@ -253,6 +254,7 @@ var TableRowNodeView = /*#__PURE__*/function () {
253
254
  }
254
255
  _this.dom.style.top = "".concat(domTop, "px");
255
256
  (0, _dom.updateStickyMargins)(table);
257
+ _this.dom.scrollLeft = wrapper.scrollLeft;
256
258
  _this.emitOn(domTop, _this.colControlsOffset);
257
259
  });
258
260
  (0, _defineProperty2.default)(this, "makeRowHeaderNotSticky", function (table) {
@@ -98,6 +98,7 @@ var TableCssClassName = _objectSpread(_objectSpread({}, _styles.TableSharedCssCl
98
98
  CONTEXTUAL_SUBMENU: "".concat(_adfSchema.tablePrefixSelector, "-contextual-submenu"),
99
99
  CONTEXTUAL_MENU_BUTTON_WRAP: "".concat(_adfSchema.tablePrefixSelector, "-contextual-menu-button-wrap"),
100
100
  CONTEXTUAL_MENU_BUTTON: "".concat(_adfSchema.tablePrefixSelector, "-contextual-menu-button"),
101
+ CONTEXTUAL_MENU_BUTTON_FIXED: "".concat(_adfSchema.tablePrefixSelector, "-contextual-menu-button-fixed"),
101
102
  CONTEXTUAL_MENU_ICON: "".concat(_adfSchema.tablePrefixSelector, "-contextual-submenu-icon"),
102
103
  // come from prosemirror-table
103
104
  SELECTED_CELL: 'selectedCell',
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.default = exports.calcObserverTargetMargin = exports.calcLeftPos = exports.FixedButton = exports.BUTTON_WIDTH = void 0;
9
+ var _react = _interopRequireWildcard(require("react"));
10
+ var _reactDom = require("react-dom");
11
+ var _rafSchd = _interopRequireDefault(require("raf-schd"));
12
+ var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
13
+ var _commonStyles = require("../common-styles");
14
+ var _types = require("../../types");
15
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
16
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
17
+ var BUTTON_WIDTH = 20;
18
+ exports.BUTTON_WIDTH = BUTTON_WIDTH;
19
+ var calcLeftPos = function calcLeftPos(_ref) {
20
+ var buttonWidth = _ref.buttonWidth,
21
+ cellRectLeft = _ref.cellRectLeft,
22
+ cellRefWidth = _ref.cellRefWidth,
23
+ offset = _ref.offset;
24
+ return cellRectLeft + cellRefWidth - buttonWidth - offset;
25
+ };
26
+ exports.calcLeftPos = calcLeftPos;
27
+ var calcObserverTargetMargin = function calcObserverTargetMargin(tableWrapper, fixedButtonRefCurrent) {
28
+ var tableWrapperRect = tableWrapper.getBoundingClientRect();
29
+ var fixedButtonRect = fixedButtonRefCurrent.getBoundingClientRect();
30
+ var scrollLeft = tableWrapper.scrollLeft;
31
+ return fixedButtonRect.left - tableWrapperRect.left + scrollLeft;
32
+ };
33
+ exports.calcObserverTargetMargin = calcObserverTargetMargin;
34
+ var FixedButton = function FixedButton(_ref2) {
35
+ var children = _ref2.children,
36
+ isContextualMenuOpen = _ref2.isContextualMenuOpen,
37
+ mountTo = _ref2.mountTo,
38
+ offset = _ref2.offset,
39
+ stickyHeader = _ref2.stickyHeader,
40
+ tableWrapper = _ref2.tableWrapper,
41
+ targetCellPosition = _ref2.targetCellPosition,
42
+ targetCellRef = _ref2.targetCellRef;
43
+ var fixedButtonRef = (0, _react.useRef)(null);
44
+ var observerTargetRef = (0, _react.useRef)(null);
45
+
46
+ // Using refs here rather than state to prevent heaps of renders on scroll
47
+ var scrollDataRef = (0, _react.useRef)(0);
48
+ var leftPosDataRef = (0, _react.useRef)(0);
49
+ (0, _react.useEffect)(function () {
50
+ var observerTargetRefCurrent = observerTargetRef.current;
51
+ var fixedButtonRefCurrent = fixedButtonRef.current;
52
+ if (fixedButtonRefCurrent && observerTargetRefCurrent) {
53
+ scrollDataRef.current = tableWrapper.scrollLeft;
54
+ leftPosDataRef.current = 0;
55
+ // Hide the button initially in case there's a flash of the button being
56
+ // outside the table before the Intersection Observer fires
57
+ fixedButtonRefCurrent.style.visibility = 'hidden';
58
+ var margin = calcObserverTargetMargin(tableWrapper, fixedButtonRefCurrent);
59
+
60
+ // Much more simple and predictable to add this margin to the observer target
61
+ // rather than using it to calculate the rootMargin values
62
+ observerTargetRefCurrent.style.marginLeft = "".concat(margin, "px");
63
+ var observer = new IntersectionObserver(function (entries) {
64
+ entries.forEach(function (entry) {
65
+ if (entry.isIntersecting) {
66
+ fixedButtonRefCurrent.style.visibility = 'visible';
67
+ } else {
68
+ fixedButtonRefCurrent.style.visibility = 'hidden';
69
+ }
70
+ });
71
+ }, {
72
+ root: tableWrapper,
73
+ rootMargin: "0px ".concat(_commonStyles.insertColumnButtonOffset, "px 0px 0px"),
74
+ threshold: 1
75
+ });
76
+ var handleScroll = (0, _rafSchd.default)(function (event) {
77
+ if (fixedButtonRef.current) {
78
+ var delta = event.target.scrollLeft - scrollDataRef.current;
79
+ var style = "translateX(".concat(leftPosDataRef.current - delta, "px)");
80
+ fixedButtonRef.current.style.transform = style;
81
+ scrollDataRef.current = event.target.scrollLeft;
82
+ leftPosDataRef.current = leftPosDataRef.current - delta;
83
+ }
84
+ });
85
+ observer.observe(observerTargetRefCurrent);
86
+ tableWrapper.addEventListener('scroll', handleScroll);
87
+ return function () {
88
+ tableWrapper.removeEventListener('scroll', handleScroll);
89
+ fixedButtonRefCurrent.style.transform = '';
90
+ observer.unobserve(observerTargetRefCurrent);
91
+ };
92
+ }
93
+ }, [fixedButtonRef, observerTargetRef, tableWrapper, targetCellPosition, targetCellRef, isContextualMenuOpen]);
94
+ var targetCellRect = targetCellRef.getBoundingClientRect();
95
+
96
+ // Using a portal here to ensure wrapperRef has the tableWrapper as an
97
+ // ancestor. This is required to make the Intersection Observer work.
98
+ return /*#__PURE__*/(0, _reactDom.createPortal)(
99
+ /*#__PURE__*/
100
+ // Using observerTargetRef here for our Intersection Observer. There is issues
101
+ // getting the observer to work just using the fixedButtonRef, possible due
102
+ // to using position fixed on this Element, or possibly due to its position
103
+ // being changed on scroll.
104
+ _react.default.createElement("div", {
105
+ ref: observerTargetRef,
106
+ style: {
107
+ position: 'absolute',
108
+ top: '0px',
109
+ left: '0px',
110
+ width: "".concat(BUTTON_WIDTH, "px"),
111
+ height: "".concat(BUTTON_WIDTH, "px")
112
+ }
113
+ }, /*#__PURE__*/_react.default.createElement("div", {
114
+ ref: fixedButtonRef,
115
+ style: {
116
+ position: 'fixed',
117
+ top: stickyHeader.top + stickyHeader.padding + offset * 2,
118
+ zIndex: _editorSharedStyles.akEditorFloatingPanelZIndex,
119
+ left: calcLeftPos({
120
+ buttonWidth: BUTTON_WIDTH,
121
+ cellRectLeft: targetCellRect.left,
122
+ cellRefWidth: targetCellRef.clientWidth,
123
+ offset: offset
124
+ }),
125
+ width: "".concat(BUTTON_WIDTH, "px"),
126
+ height: "".concat(BUTTON_WIDTH, "px")
127
+ },
128
+ className: _types.TableCssClassName.CONTEXTUAL_MENU_BUTTON_FIXED
129
+ }, children)), mountTo);
130
+ };
131
+ exports.FixedButton = FixedButton;
132
+ var _default = FixedButton;
133
+ exports.default = _default;
@@ -4,15 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.FloatingContextualButtonInner = void 0;
8
7
  exports.default = _default;
9
- var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
10
- var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
11
- var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
12
- var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
13
- var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
14
- var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
15
- var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
16
8
  var _react = _interopRequireDefault(require("react"));
17
9
  var _react2 = require("@emotion/react");
18
10
  var _prosemirrorUtils = require("prosemirror-utils");
@@ -21,135 +13,88 @@ var _ui = require("@atlaskit/editor-common/ui");
21
13
  var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
22
14
  var _chevronDown = _interopRequireDefault(require("@atlaskit/icon/glyph/chevron-down"));
23
15
  var _uiMenu = require("@atlaskit/editor-common/ui-menu");
24
- var _utils = require("@atlaskit/editor-common/utils");
25
16
  var _commands = require("../../commands");
26
17
  var _types = require("../../types");
27
18
  var _messages = _interopRequireDefault(require("../../ui/messages"));
19
+ var _FixedButton = _interopRequireDefault(require("./FixedButton"));
28
20
  var _analytics = require("@atlaskit/editor-common/analytics");
29
21
  var _styles = require("./styles");
30
22
  var _errorBoundary = require("@atlaskit/editor-common/error-boundary");
31
- function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
32
- function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** @jsx jsx */
33
- var FloatingContextualButtonInner = /*#__PURE__*/function (_React$Component) {
34
- (0, _inherits2.default)(FloatingContextualButtonInner, _React$Component);
35
- var _super = _createSuper(FloatingContextualButtonInner);
36
- function FloatingContextualButtonInner() {
37
- var _this;
38
- (0, _classCallCheck2.default)(this, FloatingContextualButtonInner);
39
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
40
- args[_key] = arguments[_key];
23
+ /** @jsx jsx */
24
+
25
+ var BUTTON_OFFSET = 3;
26
+ var FloatingContextualButtonInner = /*#__PURE__*/_react.default.memo(function (props) {
27
+ var editorView = props.editorView,
28
+ isContextualMenuOpen = props.isContextualMenuOpen,
29
+ mountPoint = props.mountPoint,
30
+ scrollableElement = props.scrollableElement,
31
+ stickyHeader = props.stickyHeader,
32
+ tableWrapper = props.tableWrapper,
33
+ targetCellPosition = props.targetCellPosition,
34
+ formatMessage = props.intl.formatMessage; // : Props & WrappedComponentProps
35
+
36
+ var handleClick = function handleClick() {
37
+ var state = editorView.state,
38
+ dispatch = editorView.dispatch;
39
+ // Clicking outside the dropdown handles toggling the menu closed
40
+ // (otherwise these two toggles combat each other).
41
+ // In the event a user clicks the chevron button again
42
+ // That will count as clicking outside the dropdown and
43
+ // will be toggled appropriately
44
+ if (!isContextualMenuOpen) {
45
+ (0, _commands.toggleContextualMenu)()(state, dispatch);
41
46
  }
42
- _this = _super.call.apply(_super, [this].concat(args));
43
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "handleClick", function () {
44
- var _this$props$editorVie = _this.props.editorView,
45
- state = _this$props$editorVie.state,
46
- dispatch = _this$props$editorVie.dispatch;
47
- // Clicking outside the dropdown handles toggling the menu closed
48
- // (otherwise these two toggles combat each other).
49
- // In the event a user clicks the chevron button again
50
- // That will count as clicking outside the dropdown and
51
- // will be toggled appropriately
52
- if (!_this.props.isContextualMenuOpen) {
53
- (0, _commands.toggleContextualMenu)()(state, dispatch);
54
- }
55
- });
56
- return _this;
47
+ };
48
+ var domAtPos = editorView.domAtPos.bind(editorView);
49
+ var targetCellRef;
50
+ targetCellRef = (0, _prosemirrorUtils.findDomRefAtPos)(targetCellPosition, domAtPos);
51
+ if (!targetCellRef || !(targetCellRef instanceof HTMLElement)) {
52
+ return null;
57
53
  }
58
- (0, _createClass2.default)(FloatingContextualButtonInner, [{
59
- key: "render",
60
- value: function render() {
61
- var _this$props = this.props,
62
- mountPoint = _this$props.mountPoint,
63
- scrollableElement = _this$props.scrollableElement,
64
- editorView = _this$props.editorView,
65
- targetCellPosition = _this$props.targetCellPosition,
66
- isContextualMenuOpen = _this$props.isContextualMenuOpen,
67
- formatMessage = _this$props.intl.formatMessage,
68
- dispatchAnalyticsEvent = _this$props.dispatchAnalyticsEvent; // : Props & WrappedComponentProps
69
- var domAtPos = editorView.domAtPos.bind(editorView);
70
- var targetCellRef;
71
- try {
72
- targetCellRef = (0, _prosemirrorUtils.findDomRefAtPos)(targetCellPosition, domAtPos);
73
- } catch (error) {
74
- // eslint-disable-next-line no-console
75
- console.warn(error);
76
- if (dispatchAnalyticsEvent) {
77
- var payload = {
78
- action: _analytics.ACTION.ERRORED,
79
- actionSubject: _analytics.ACTION_SUBJECT.CONTENT_COMPONENT,
80
- eventType: _analytics.EVENT_TYPE.OPERATIONAL,
81
- attributes: {
82
- component: _analytics.CONTENT_COMPONENT.FLOATING_CONTEXTUAL_BUTTON,
83
- selection: editorView.state.selection.toJSON(),
84
- position: targetCellPosition,
85
- docSize: editorView.state.doc.nodeSize,
86
- error: error instanceof Error ? error.message : String(error)
87
- },
88
- nonPrivacySafeAttributes: {
89
- errorStack: error instanceof Error ? error.stack : undefined
90
- }
91
- };
92
- dispatchAnalyticsEvent(payload);
93
- }
94
- }
95
- if (!targetCellRef || !(targetCellRef instanceof HTMLElement)) {
96
- return null;
97
- }
98
- var tableWrapper = (0, _utils.closestElement)(targetCellRef, ".".concat(_types.TableCssClassName.TABLE_NODE_WRAPPER));
99
- var labelCellOptions = formatMessage(_messages.default.cellOptions);
100
- var button = (0, _react2.jsx)("div", {
101
- css: function css(theme) {
102
- return [(0, _styles.tableFloatingCellButtonStyles)({
103
- theme: theme
104
- }), isContextualMenuOpen && (0, _styles.tableFloatingCellButtonSelectedStyles)({
105
- theme: theme
106
- })];
107
- }
108
- }, (0, _react2.jsx)(_uiMenu.ToolbarButton, {
109
- className: _types.TableCssClassName.CONTEXTUAL_MENU_BUTTON,
110
- selected: isContextualMenuOpen,
111
- title: labelCellOptions,
112
- onClick: this.handleClick,
113
- iconBefore: (0, _react2.jsx)(_chevronDown.default, {
114
- label: ""
115
- }),
116
- "aria-label": labelCellOptions
117
- }));
118
- var parentSticky = targetCellRef.parentElement && targetCellRef.parentElement.className.indexOf('sticky') > -1;
119
- if (this.props.stickyHeader && parentSticky) {
120
- var pos = targetCellRef.getBoundingClientRect();
121
- return (0, _react2.jsx)("div", {
122
- style: {
123
- position: 'fixed',
124
- top: this.props.stickyHeader.top + this.props.stickyHeader.padding + 3 + 3,
125
- zIndex: _editorSharedStyles.akEditorFloatingOverlapPanelZIndex,
126
- left: pos.left + targetCellRef.clientWidth - 20 - 3
127
- }
128
- }, button);
129
- }
130
- return (0, _react2.jsx)(_ui.Popup, {
131
- alignX: "right",
132
- alignY: "start",
133
- target: targetCellRef,
134
- mountTo: tableWrapper || mountPoint,
135
- boundariesElement: targetCellRef,
136
- scrollableElement: scrollableElement,
137
- offset: [3, -3],
138
- forcePlacement: true,
139
- allowOutOfBounds: true,
140
- zIndex: _editorSharedStyles.akEditorSmallZIndex
141
- }, button);
142
- }
143
- }, {
144
- key: "shouldComponentUpdate",
145
- value: function shouldComponentUpdate(nextProps) {
146
- return this.props.tableNode !== nextProps.tableNode || this.props.targetCellPosition !== nextProps.targetCellPosition || this.props.layout !== nextProps.layout || this.props.isContextualMenuOpen !== nextProps.isContextualMenuOpen || this.props.isNumberColumnEnabled !== nextProps.isNumberColumnEnabled || this.props.stickyHeader !== nextProps.stickyHeader;
54
+ var labelCellOptions = formatMessage(_messages.default.cellOptions);
55
+ var button = (0, _react2.jsx)("div", {
56
+ css: function css(theme) {
57
+ return [(0, _styles.tableFloatingCellButtonStyles)({
58
+ theme: theme
59
+ }), isContextualMenuOpen && (0, _styles.tableFloatingCellButtonSelectedStyles)({
60
+ theme: theme
61
+ })];
147
62
  }
148
- }]);
149
- return FloatingContextualButtonInner;
150
- }(_react.default.Component);
151
- exports.FloatingContextualButtonInner = FloatingContextualButtonInner;
152
- (0, _defineProperty2.default)(FloatingContextualButtonInner, "displayName", 'FloatingContextualButton');
63
+ }, (0, _react2.jsx)(_uiMenu.ToolbarButton, {
64
+ className: _types.TableCssClassName.CONTEXTUAL_MENU_BUTTON,
65
+ selected: isContextualMenuOpen,
66
+ title: labelCellOptions,
67
+ onClick: handleClick,
68
+ iconBefore: (0, _react2.jsx)(_chevronDown.default, {
69
+ label: ""
70
+ }),
71
+ "aria-label": labelCellOptions
72
+ }));
73
+ var parentSticky = targetCellRef.parentElement && targetCellRef.parentElement.className.indexOf('sticky') > -1;
74
+ if (stickyHeader && parentSticky && tableWrapper) {
75
+ return (0, _react2.jsx)(_FixedButton.default, {
76
+ offset: BUTTON_OFFSET,
77
+ stickyHeader: stickyHeader,
78
+ tableWrapper: tableWrapper,
79
+ targetCellPosition: targetCellPosition,
80
+ targetCellRef: targetCellRef,
81
+ mountTo: tableWrapper,
82
+ isContextualMenuOpen: isContextualMenuOpen
83
+ }, button);
84
+ }
85
+ return (0, _react2.jsx)(_ui.Popup, {
86
+ alignX: "right",
87
+ alignY: "start",
88
+ target: targetCellRef,
89
+ mountTo: tableWrapper || mountPoint,
90
+ boundariesElement: targetCellRef,
91
+ scrollableElement: scrollableElement,
92
+ offset: [BUTTON_OFFSET, -BUTTON_OFFSET],
93
+ forcePlacement: true,
94
+ allowOutOfBounds: true,
95
+ zIndex: _editorSharedStyles.akEditorSmallZIndex
96
+ }, button);
97
+ });
153
98
  var FloatingContextualButton = (0, _reactIntlNext.injectIntl)(FloatingContextualButtonInner);
154
99
  function _default(props) {
155
100
  return (0, _react2.jsx)(_errorBoundary.ErrorBoundary, {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-table",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "sideEffects": false
5
5
  }
@@ -251,7 +251,8 @@ const tablesPlugin = (options, api) => {
251
251
  dispatchAnalyticsEvent: dispatchAnalyticsEvent,
252
252
  isContextualMenuOpen: isContextualMenuOpen,
253
253
  layout: layout,
254
- stickyHeader: stickyHeader
254
+ stickyHeader: stickyHeader,
255
+ tableWrapper: tableWrapperTarget
255
256
  }), allowControls && /*#__PURE__*/React.createElement(FloatingInsertButton, {
256
257
  tableNode: tableNode,
257
258
  tableRef: tableRef,
@@ -217,7 +217,8 @@ export class TableRowNodeView {
217
217
  return;
218
218
  }
219
219
  const {
220
- table
220
+ table,
221
+ wrapper
221
222
  } = tree;
222
223
 
223
224
  // ED-16035 Make sure sticky header is only applied to first row
@@ -239,6 +240,7 @@ export class TableRowNodeView {
239
240
  }
240
241
  this.dom.style.top = `${domTop}px`;
241
242
  updateTableMargin(table);
243
+ this.dom.scrollLeft = wrapper.scrollLeft;
242
244
  this.emitOn(domTop, this.colControlsOffset);
243
245
  });
244
246
  _defineProperty(this, "makeRowHeaderNotSticky", (table, isEditorDestroyed = false) => {
@@ -88,6 +88,7 @@ export const TableCssClassName = {
88
88
  CONTEXTUAL_SUBMENU: `${tablePrefixSelector}-contextual-submenu`,
89
89
  CONTEXTUAL_MENU_BUTTON_WRAP: `${tablePrefixSelector}-contextual-menu-button-wrap`,
90
90
  CONTEXTUAL_MENU_BUTTON: `${tablePrefixSelector}-contextual-menu-button`,
91
+ CONTEXTUAL_MENU_BUTTON_FIXED: `${tablePrefixSelector}-contextual-menu-button-fixed`,
91
92
  CONTEXTUAL_MENU_ICON: `${tablePrefixSelector}-contextual-submenu-icon`,
92
93
  // come from prosemirror-table
93
94
  SELECTED_CELL: 'selectedCell',
@@ -0,0 +1,120 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import rafSchedule from 'raf-schd';
4
+ import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
5
+ import { insertColumnButtonOffset } from '../common-styles';
6
+ import { TableCssClassName as ClassName } from '../../types';
7
+ export const BUTTON_WIDTH = 20;
8
+ export const calcLeftPos = ({
9
+ buttonWidth,
10
+ cellRectLeft,
11
+ cellRefWidth,
12
+ offset
13
+ }) => {
14
+ return cellRectLeft + cellRefWidth - buttonWidth - offset;
15
+ };
16
+ export const calcObserverTargetMargin = (tableWrapper, fixedButtonRefCurrent) => {
17
+ const tableWrapperRect = tableWrapper.getBoundingClientRect();
18
+ const fixedButtonRect = fixedButtonRefCurrent.getBoundingClientRect();
19
+ const scrollLeft = tableWrapper.scrollLeft;
20
+ return fixedButtonRect.left - tableWrapperRect.left + scrollLeft;
21
+ };
22
+ export const FixedButton = ({
23
+ children,
24
+ isContextualMenuOpen,
25
+ mountTo,
26
+ offset,
27
+ stickyHeader,
28
+ tableWrapper,
29
+ targetCellPosition,
30
+ targetCellRef
31
+ }) => {
32
+ const fixedButtonRef = useRef(null);
33
+ const observerTargetRef = useRef(null);
34
+
35
+ // Using refs here rather than state to prevent heaps of renders on scroll
36
+ const scrollDataRef = useRef(0);
37
+ const leftPosDataRef = useRef(0);
38
+ useEffect(() => {
39
+ const observerTargetRefCurrent = observerTargetRef.current;
40
+ const fixedButtonRefCurrent = fixedButtonRef.current;
41
+ if (fixedButtonRefCurrent && observerTargetRefCurrent) {
42
+ scrollDataRef.current = tableWrapper.scrollLeft;
43
+ leftPosDataRef.current = 0;
44
+ // Hide the button initially in case there's a flash of the button being
45
+ // outside the table before the Intersection Observer fires
46
+ fixedButtonRefCurrent.style.visibility = 'hidden';
47
+ const margin = calcObserverTargetMargin(tableWrapper, fixedButtonRefCurrent);
48
+
49
+ // Much more simple and predictable to add this margin to the observer target
50
+ // rather than using it to calculate the rootMargin values
51
+ observerTargetRefCurrent.style.marginLeft = `${margin}px`;
52
+ const observer = new IntersectionObserver(entries => {
53
+ entries.forEach(entry => {
54
+ if (entry.isIntersecting) {
55
+ fixedButtonRefCurrent.style.visibility = 'visible';
56
+ } else {
57
+ fixedButtonRefCurrent.style.visibility = 'hidden';
58
+ }
59
+ });
60
+ }, {
61
+ root: tableWrapper,
62
+ rootMargin: `0px ${insertColumnButtonOffset}px 0px 0px`,
63
+ threshold: 1
64
+ });
65
+ const handleScroll = rafSchedule(event => {
66
+ if (fixedButtonRef.current) {
67
+ const delta = event.target.scrollLeft - scrollDataRef.current;
68
+ const style = `translateX(${leftPosDataRef.current - delta}px)`;
69
+ fixedButtonRef.current.style.transform = style;
70
+ scrollDataRef.current = event.target.scrollLeft;
71
+ leftPosDataRef.current = leftPosDataRef.current - delta;
72
+ }
73
+ });
74
+ observer.observe(observerTargetRefCurrent);
75
+ tableWrapper.addEventListener('scroll', handleScroll);
76
+ return () => {
77
+ tableWrapper.removeEventListener('scroll', handleScroll);
78
+ fixedButtonRefCurrent.style.transform = '';
79
+ observer.unobserve(observerTargetRefCurrent);
80
+ };
81
+ }
82
+ }, [fixedButtonRef, observerTargetRef, tableWrapper, targetCellPosition, targetCellRef, isContextualMenuOpen]);
83
+ const targetCellRect = targetCellRef.getBoundingClientRect();
84
+
85
+ // Using a portal here to ensure wrapperRef has the tableWrapper as an
86
+ // ancestor. This is required to make the Intersection Observer work.
87
+ return /*#__PURE__*/createPortal(
88
+ /*#__PURE__*/
89
+ // Using observerTargetRef here for our Intersection Observer. There is issues
90
+ // getting the observer to work just using the fixedButtonRef, possible due
91
+ // to using position fixed on this Element, or possibly due to its position
92
+ // being changed on scroll.
93
+ React.createElement("div", {
94
+ ref: observerTargetRef,
95
+ style: {
96
+ position: 'absolute',
97
+ top: '0px',
98
+ left: '0px',
99
+ width: `${BUTTON_WIDTH}px`,
100
+ height: `${BUTTON_WIDTH}px`
101
+ }
102
+ }, /*#__PURE__*/React.createElement("div", {
103
+ ref: fixedButtonRef,
104
+ style: {
105
+ position: 'fixed',
106
+ top: stickyHeader.top + stickyHeader.padding + offset * 2,
107
+ zIndex: akEditorFloatingPanelZIndex,
108
+ left: calcLeftPos({
109
+ buttonWidth: BUTTON_WIDTH,
110
+ cellRectLeft: targetCellRect.left,
111
+ cellRefWidth: targetCellRef.clientWidth,
112
+ offset
113
+ }),
114
+ width: `${BUTTON_WIDTH}px`,
115
+ height: `${BUTTON_WIDTH}px`
116
+ },
117
+ className: ClassName.CONTEXTUAL_MENU_BUTTON_FIXED
118
+ }, children)), mountTo);
119
+ };
120
+ export default FixedButton;