@atlaskit/editor-plugin-floating-toolbar 3.3.3 → 3.3.5

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,19 @@
1
1
  # @atlaskit/editor-plugin-floating-toolbar
2
2
 
3
+ ## 3.3.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#134885](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/134885)
8
+ [`0d61709802162`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0d61709802162) -
9
+ [ux] [ED-27312] Implement new scroll left/right buttons for scrollable floating toolbars
10
+
11
+ ## 3.3.4
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies
16
+
3
17
  ## 3.3.3
4
18
 
5
19
  ### Patch Changes
@@ -10,6 +10,7 @@ exports.floatingToolbarPlugin = void 0;
10
10
  exports.floatingToolbarPluginFactory = floatingToolbarPluginFactory;
11
11
  exports.pluginKey = exports.getRelevantConfig = void 0;
12
12
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
13
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
13
14
  var _react = _interopRequireDefault(require("react"));
14
15
  var _camelCase = _interopRequireDefault(require("lodash/camelCase"));
15
16
  var _analytics = require("@atlaskit/editor-common/analytics");
@@ -20,6 +21,7 @@ var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
20
21
  var _ui = require("@atlaskit/editor-common/ui");
21
22
  var _state = require("@atlaskit/editor-prosemirror/state");
22
23
  var _utils = require("@atlaskit/editor-prosemirror/utils");
24
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
23
25
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
24
26
  var _commands = require("./pm-plugins/commands");
25
27
  var _forceFocus = _interopRequireWildcard(require("./pm-plugins/force-focus"));
@@ -30,6 +32,7 @@ var _utils2 = require("./pm-plugins/utils");
30
32
  var _ConfirmationModal = require("./ui/ConfirmationModal");
31
33
  var _ExpandButton = require("./ui/ExpandButton");
32
34
  var _ToolbarLoader = require("./ui/ToolbarLoader");
35
+ var _utils3 = require("./ui/utils");
33
36
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
34
37
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
35
38
  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; }
@@ -275,6 +278,32 @@ function ContentComponent(_ref5) {
275
278
  return toolbarItemViewModeProp in item && !!item[toolbarItemViewModeProp];
276
279
  });
277
280
  }
281
+ if ((0, _experiments.editorExperiment)('platform_editor_controls', 'variant1') && (0, _platformFeatureFlags.fg)('platform_editor_controls_patch_1')) {
282
+ var _items2;
283
+ // Consolidate floating toolbar items
284
+ var toolbarItemsArray = Array.isArray(items) ? items : (_items2 = items) === null || _items2 === void 0 ? void 0 : _items2(node);
285
+ var overflowDropdownItems = toolbarItemsArray.filter(function (item) {
286
+ return item.type === 'overflow-dropdown';
287
+ });
288
+ if (overflowDropdownItems.length > 1) {
289
+ var consolidatedOverflowDropdown = (0, _utils3.consolidateOverflowDropdownItems)(overflowDropdownItems);
290
+ var otherItems = toolbarItemsArray.filter(function (item) {
291
+ return item.type !== 'overflow-dropdown';
292
+ });
293
+ if (otherItems.length > 0) {
294
+ // remove the last separators
295
+ while (((_otherItems$at = otherItems.at(-1)) === null || _otherItems$at === void 0 ? void 0 : _otherItems$at.type) === 'separator') {
296
+ var _otherItems$at;
297
+ otherItems.pop();
298
+ }
299
+ }
300
+ items = [].concat((0, _toConsumableArray2.default)(otherItems), [{
301
+ type: 'separator',
302
+ fullHeight: true,
303
+ supportsViewMode: true
304
+ }, consolidatedOverflowDropdown]);
305
+ }
306
+ }
278
307
  var customPositionCalculation;
279
308
  var toolbarItems = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c = pluginInjectionApi.copyButton) === null || _pluginInjectionApi$c === void 0 ? void 0 : _pluginInjectionApi$c.actions.processCopyButtonItems(editorView.state)(Array.isArray(items) ? items : items(node), pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$d = pluginInjectionApi.decorations) === null || _pluginInjectionApi$d === void 0 ? void 0 : _pluginInjectionApi$d.actions.hoverDecoration);
280
309
  var viewModeToolbarEntry = (0, _experiments.unstable_editorExperimentParam)('live_pages_graceful_edit', 'toolbar-entry', {
@@ -0,0 +1,148 @@
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.ScrollButton = void 0;
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _react = _interopRequireWildcard(require("react"));
11
+ var _bindEventListener = require("bind-event-listener");
12
+ var _rafSchd = _interopRequireDefault(require("raf-schd"));
13
+ var _new = require("@atlaskit/button/new");
14
+ var _floatingToolbar = require("@atlaskit/editor-common/floating-toolbar");
15
+ var _chevronLeftChevronLeftLarge = _interopRequireDefault(require("@atlaskit/icon/utility/migration/chevron-left--chevron-left-large"));
16
+ var _chevronRightChevronRightLarge = _interopRequireDefault(require("@atlaskit/icon/utility/migration/chevron-right--chevron-right-large"));
17
+ var _primitives = require("@atlaskit/primitives");
18
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
19
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
20
+ var rightSideStyles = (0, _primitives.xcss)({
21
+ borderLeft: "solid ".concat("var(--ds-border, #091E4224)", " 1px"),
22
+ right: 'space.0',
23
+ borderTopRightRadius: '3px',
24
+ borderBottomRightRadius: '3px'
25
+ });
26
+ var leftSideStyles = (0, _primitives.xcss)({
27
+ borderRight: "solid ".concat("var(--ds-border, #091E4224)", " 1px"),
28
+ left: 'space.0',
29
+ borderTopLeftRadius: '3px',
30
+ borderBottomLeftRadius: '3px'
31
+ });
32
+ var buttonCommonStyles = (0, _primitives.xcss)({
33
+ backgroundColor: 'elevation.surface.overlay',
34
+ zIndex: '1',
35
+ position: 'absolute'
36
+ });
37
+ var ScrollButton = exports.ScrollButton = function ScrollButton(_ref) {
38
+ var intl = _ref.intl,
39
+ scrollContainerRef = _ref.scrollContainerRef,
40
+ node = _ref.node,
41
+ disabled = _ref.disabled,
42
+ side = _ref.side;
43
+ var _useState = (0, _react.useState)(false),
44
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
45
+ needScroll = _useState2[0],
46
+ setNeedScroll = _useState2[1];
47
+ var _useState3 = (0, _react.useState)(true),
48
+ _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
49
+ canScrollToSide = _useState4[0],
50
+ setCanScrollToSide = _useState4[1];
51
+ var setCanScrollDebounced = (0, _rafSchd.default)(function () {
52
+ // Refs are null before mounting and after unmount
53
+ if (!scrollContainerRef.current) {
54
+ return;
55
+ }
56
+ var _scrollContainerRef$c = scrollContainerRef.current,
57
+ scrollLeft = _scrollContainerRef$c.scrollLeft,
58
+ scrollWidth = _scrollContainerRef$c.scrollWidth,
59
+ offsetWidth = _scrollContainerRef$c.offsetWidth;
60
+ setCanScrollToSide(
61
+ // -1 to account for pixel rounding error
62
+ side === 'left' ? scrollLeft > 0 : scrollLeft < scrollWidth - offsetWidth - 1);
63
+ });
64
+ var onScroll = function onScroll() {
65
+ setCanScrollDebounced();
66
+ };
67
+ var onClick = function onClick() {
68
+ var _scrollContainerRef$c2, _scrollContainerRef$c3, _scrollContainerRef$c4;
69
+ var _ref2 = ((_scrollContainerRef$c2 = scrollContainerRef.current) === null || _scrollContainerRef$c2 === void 0 ? void 0 : _scrollContainerRef$c2.getBoundingClientRect()) || {},
70
+ _ref2$width = _ref2.width,
71
+ scrollContainerWidth = _ref2$width === void 0 ? 0 : _ref2$width;
72
+ var scrollLeft = ((_scrollContainerRef$c3 = scrollContainerRef.current) === null || _scrollContainerRef$c3 === void 0 ? void 0 : _scrollContainerRef$c3.scrollLeft) || 0;
73
+ var scrollTo = side === 'left' ? scrollLeft - scrollContainerWidth : scrollLeft + scrollContainerWidth;
74
+ (_scrollContainerRef$c4 = scrollContainerRef.current) === null || _scrollContainerRef$c4 === void 0 || _scrollContainerRef$c4.scrollTo({
75
+ top: 0,
76
+ left: scrollTo,
77
+ behavior: 'smooth'
78
+ });
79
+ };
80
+ var resizeObserver = new ResizeObserver(function (t) {
81
+ var _scrollContainerRef$c5, _scrollContainerRef$c6;
82
+ var widthNeededToShowAllItems = ((_scrollContainerRef$c5 = scrollContainerRef.current) === null || _scrollContainerRef$c5 === void 0 ? void 0 : _scrollContainerRef$c5.scrollWidth) || 0;
83
+ var parentNode = (_scrollContainerRef$c6 = scrollContainerRef.current) === null || _scrollContainerRef$c6 === void 0 ? void 0 : _scrollContainerRef$c6.parentNode;
84
+ var availableSpace = -1;
85
+ if (parentNode instanceof HTMLElement) {
86
+ availableSpace = parentNode.offsetWidth;
87
+ }
88
+ if (availableSpace === -1) {
89
+ return;
90
+ }
91
+ if (availableSpace >= widthNeededToShowAllItems) {
92
+ setNeedScroll(false);
93
+ } else {
94
+ setNeedScroll(true);
95
+ onScroll();
96
+ }
97
+ });
98
+ (0, _react.useEffect)(function () {
99
+ onScroll();
100
+ var scrollContainerRefCurrent = scrollContainerRef.current;
101
+ var unbind;
102
+ if (scrollContainerRefCurrent) {
103
+ // Adding/removing scroll button depending on scroll position
104
+ unbind = (0, _bindEventListener.bind)(scrollContainerRefCurrent, {
105
+ type: 'scroll',
106
+ listener: onScroll
107
+ });
108
+
109
+ // watch for toolbar resize and show/hide scroll buttons if needed
110
+ resizeObserver.observe(scrollContainerRefCurrent);
111
+ }
112
+ return function () {
113
+ if (scrollContainerRefCurrent) {
114
+ var _unbind;
115
+ (_unbind = unbind) === null || _unbind === void 0 || _unbind();
116
+ resizeObserver.unobserve(scrollContainerRefCurrent);
117
+ }
118
+ setCanScrollDebounced.cancel();
119
+ };
120
+ // eslint-disable-next-line react-hooks/exhaustive-deps
121
+ }, []);
122
+ (0, _react.useEffect)(function () {
123
+ var scrollContainerRefCurrent = scrollContainerRef.current;
124
+ if (scrollContainerRefCurrent) {
125
+ var _scrollContainerRefCu;
126
+ // reset scroll position when switching from one node with toolbar to another
127
+ // scroll to made optional as it may not be rendered in testing env
128
+ (_scrollContainerRefCu = scrollContainerRefCurrent.scrollTo) === null || _scrollContainerRefCu === void 0 || _scrollContainerRefCu.call(scrollContainerRefCurrent, {
129
+ left: 0
130
+ });
131
+ }
132
+ }, [node.type, scrollContainerRef]);
133
+ var Icon = side === 'left' ? _chevronLeftChevronLeftLarge.default : _chevronRightChevronRightLarge.default;
134
+ return needScroll && (side === 'left' && canScrollToSide || side === 'right' && canScrollToSide) && /*#__PURE__*/_react.default.createElement(_primitives.Box, {
135
+ padding: "space.050",
136
+ xcss: [side === 'left' ? leftSideStyles : rightSideStyles, buttonCommonStyles]
137
+ }, /*#__PURE__*/_react.default.createElement(_new.IconButton, {
138
+ appearance: "subtle",
139
+ label: intl.formatMessage(side === 'left' ? _floatingToolbar.messages.floatingToolbarScrollLeft : _floatingToolbar.messages.floatingToolbarScrollRight),
140
+ onClick: onClick,
141
+ isDisabled: disabled,
142
+ icon: Icon,
143
+ isTooltipDisabled: false,
144
+ tooltip: {
145
+ position: 'top'
146
+ }
147
+ }));
148
+ };
@@ -36,6 +36,7 @@ var toolbarScrollButtons = (0, _react2.css)({
36
36
  });
37
37
  var LeftIcon = _chevronLeftChevronLeftLarge.default;
38
38
  var RightIcon = _chevronRightChevronRightLarge.default;
39
+ // Remove this component (replaced by ScrollButton) as part of platform_editor_controls clean up
39
40
  var ScrollButtons = exports.ScrollButtons = function ScrollButtons(_ref) {
40
41
  var intl = _ref.intl,
41
42
  scrollContainerRef = _ref.scrollContainerRef,
@@ -33,6 +33,7 @@ var _Dropdown = _interopRequireDefault(require("./Dropdown"));
33
33
  var _EmojiPickerButton = require("./EmojiPickerButton");
34
34
  var _ExtensionsPlaceholder = require("./ExtensionsPlaceholder");
35
35
  var _Input = require("./Input");
36
+ var _ScrollButton = require("./ScrollButton");
36
37
  var _ScrollButtons = require("./ScrollButtons");
37
38
  var _Select = _interopRequireDefault(require("./Select"));
38
39
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
@@ -206,11 +207,7 @@ var ToolbarItems = /*#__PURE__*/_react.default.memo(function (_ref) {
206
207
  setDisableParentScroll: scrollable ? setDisableScroll : undefined,
207
208
  dropdownListId: (item === null || item === void 0 ? void 0 : item.id) && "".concat(item.id, "-dropdownList"),
208
209
  alignDropdownWithToolbar: items.length === 1,
209
- onToggle: item.onToggle,
210
- footer: item.footer,
211
- onMount: item.onMount,
212
- onClick: item.onClick,
213
- pulse: item.pulse
210
+ onClick: item.onClick
214
211
  });
215
212
  case 'dropdown':
216
213
  var DropdownIcon = item.icon;
@@ -650,6 +647,8 @@ var Toolbar = /*#__PURE__*/function (_Component) {
650
647
  intl = _this$props2.intl,
651
648
  scrollable = _this$props2.scrollable,
652
649
  mediaAssistiveMessage = _this$props2.mediaAssistiveMessage;
650
+ var isEditorControlsEnabled = (0, _experiments.editorExperiment)('platform_editor_controls', 'variant1');
651
+ var isEditorControlsPatch2Enabled = isEditorControlsEnabled && (0, _platformFeatureFlags.fg)('platform_editor_controls_patch_2');
653
652
  if (!items || !items.length) {
654
653
  return null;
655
654
  }
@@ -681,10 +680,16 @@ var Toolbar = /*#__PURE__*/function (_Component) {
681
680
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
682
681
  ,
683
682
  className: className,
684
- onMouseDown: (0, _experiments.editorExperiment)('platform_editor_controls', 'variant1') ? this.captureMouseEvent : undefined
683
+ onMouseDown: isEditorControlsEnabled ? this.captureMouseEvent : undefined
685
684
  }, (0, _react2.jsx)(_ui.Announcer, {
686
685
  text: mediaAssistiveMessage ? "".concat(mediaAssistiveMessage, ", ").concat(intl.formatMessage(_floatingToolbar.messages.floatingToolbarAnnouncer)) : intl.formatMessage(_floatingToolbar.messages.floatingToolbarAnnouncer),
687
686
  delay: 250
687
+ }), scrollable && isEditorControlsPatch2Enabled && (0, _react2.jsx)(_ScrollButton.ScrollButton, {
688
+ intl: intl,
689
+ scrollContainerRef: this.scrollContainerRef,
690
+ node: node,
691
+ disabled: this.state.scrollDisabled,
692
+ side: "left"
688
693
  }), (0, _react2.jsx)("div", {
689
694
  "data-testid": "floating-toolbar-items",
690
695
  ref: this.scrollContainerRef
@@ -704,12 +709,18 @@ var Toolbar = /*#__PURE__*/function (_Component) {
704
709
  setDisableScroll: this.setDisableScroll.bind(this),
705
710
  mountRef: this.mountRef,
706
711
  mounted: this.state.mounted
707
- }))), scrollable && (0, _react2.jsx)(_ScrollButtons.ScrollButtons, {
712
+ }))), scrollable && (isEditorControlsPatch2Enabled ? (0, _react2.jsx)(_ScrollButton.ScrollButton, {
713
+ intl: intl,
714
+ scrollContainerRef: this.scrollContainerRef,
715
+ node: node,
716
+ disabled: this.state.scrollDisabled,
717
+ side: "right"
718
+ }) : (0, _react2.jsx)(_ScrollButtons.ScrollButtons, {
708
719
  intl: intl,
709
720
  scrollContainerRef: this.scrollContainerRef,
710
721
  node: node,
711
722
  disabled: this.state.scrollDisabled
712
- })), (0, _react2.jsx)("div", {
723
+ }))), (0, _react2.jsx)("div", {
713
724
  ref: this.mountRef
714
725
  })));
715
726
  }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.consolidateOverflowDropdownItems = void 0;
8
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
+ // if there are more than 1 item with type `overflow-dropdown`, we should only show one and combine the options
10
+ var consolidateOverflowDropdownItems = exports.consolidateOverflowDropdownItems = function consolidateOverflowDropdownItems(overflowDropdowns) {
11
+ if (overflowDropdowns.length <= 1) {
12
+ return overflowDropdowns[0];
13
+ }
14
+ var combinedItems = overflowDropdowns.reduce(function (acc, item) {
15
+ if (item.options) {
16
+ acc.push.apply(acc, (0, _toConsumableArray2.default)(item.options));
17
+ }
18
+ return acc;
19
+ }, []);
20
+
21
+ // To filter out items that have a rank property, sort them by rank in descending order (highest rank first)
22
+ var rankedItems = combinedItems.filter(function (item) {
23
+ return 'rank' in item && typeof item.rank === 'number';
24
+ }).sort(function (a, b) {
25
+ return (b.rank || 0) - (a.rank || 0);
26
+ });
27
+ var unrankedItems = combinedItems.filter(function (item) {
28
+ return !('rank' in item && typeof item.rank === 'number');
29
+ });
30
+ var sortedItems = [].concat((0, _toConsumableArray2.default)(rankedItems), (0, _toConsumableArray2.default)(unrankedItems));
31
+ var largestDropdownWidth = overflowDropdowns.reduce(function (acc, item) {
32
+ if (item.dropdownWidth && item.dropdownWidth > acc) {
33
+ return item.dropdownWidth;
34
+ }
35
+ return acc;
36
+ }, 0);
37
+ return {
38
+ type: 'overflow-dropdown',
39
+ dropdownWidth: largestDropdownWidth,
40
+ options: sortedItems
41
+ };
42
+ };
@@ -8,6 +8,7 @@ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
8
8
  import { Popup } from '@atlaskit/editor-common/ui';
9
9
  import { AllSelection, PluginKey } from '@atlaskit/editor-prosemirror/state';
10
10
  import { findDomRefAtPos, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
11
+ import { fg } from '@atlaskit/platform-feature-flags';
11
12
  import { editorExperiment, unstable_editorExperimentParam } from '@atlaskit/tmp-editor-statsig/experiments';
12
13
  import { copyNode } from './pm-plugins/commands';
13
14
  import forceFocusPlugin, { forceFocusSelector } from './pm-plugins/force-focus';
@@ -18,6 +19,7 @@ import { findNode } from './pm-plugins/utils';
18
19
  import { ConfirmationModal } from './ui/ConfirmationModal';
19
20
  import { ExpandButton } from './ui/ExpandButton';
20
21
  import { ToolbarLoader } from './ui/ToolbarLoader';
22
+ import { consolidateOverflowDropdownItems } from './ui/utils';
21
23
 
22
24
  // TODO: AFP-2532 - Fix automatic suppressions below
23
25
  export const getRelevantConfig = (selection, configs) => {
@@ -259,6 +261,28 @@ export function ContentComponent({
259
261
  const toolbarItemViewModeProp = 'supportsViewMode';
260
262
  items = iterableItems.filter(item => toolbarItemViewModeProp in item && !!item[toolbarItemViewModeProp]);
261
263
  }
264
+ if (editorExperiment('platform_editor_controls', 'variant1') && fg('platform_editor_controls_patch_1')) {
265
+ var _items2;
266
+ // Consolidate floating toolbar items
267
+ const toolbarItemsArray = Array.isArray(items) ? items : (_items2 = items) === null || _items2 === void 0 ? void 0 : _items2(node);
268
+ const overflowDropdownItems = toolbarItemsArray.filter(item => item.type === 'overflow-dropdown');
269
+ if (overflowDropdownItems.length > 1) {
270
+ const consolidatedOverflowDropdown = consolidateOverflowDropdownItems(overflowDropdownItems);
271
+ const otherItems = toolbarItemsArray.filter(item => item.type !== 'overflow-dropdown');
272
+ if (otherItems.length > 0) {
273
+ // remove the last separators
274
+ while (((_otherItems$at = otherItems.at(-1)) === null || _otherItems$at === void 0 ? void 0 : _otherItems$at.type) === 'separator') {
275
+ var _otherItems$at;
276
+ otherItems.pop();
277
+ }
278
+ }
279
+ items = [...otherItems, {
280
+ type: 'separator',
281
+ fullHeight: true,
282
+ supportsViewMode: true
283
+ }, consolidatedOverflowDropdown];
284
+ }
285
+ }
262
286
  let customPositionCalculation;
263
287
  const toolbarItems = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c = pluginInjectionApi.copyButton) === null || _pluginInjectionApi$c === void 0 ? void 0 : _pluginInjectionApi$c.actions.processCopyButtonItems(editorView.state)(Array.isArray(items) ? items : items(node), pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$d = pluginInjectionApi.decorations) === null || _pluginInjectionApi$d === void 0 ? void 0 : _pluginInjectionApi$d.actions.hoverDecoration);
264
288
  const viewModeToolbarEntry = unstable_editorExperimentParam('live_pages_graceful_edit', 'toolbar-entry', {
@@ -0,0 +1,133 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { bind } from 'bind-event-listener';
3
+ import rafSchedule from 'raf-schd';
4
+ import { IconButton } from '@atlaskit/button/new';
5
+ import { messages } from '@atlaskit/editor-common/floating-toolbar';
6
+ import ChevronLeftLargeIcon from '@atlaskit/icon/utility/migration/chevron-left--chevron-left-large';
7
+ import ChevronRightLargeIcon from '@atlaskit/icon/utility/migration/chevron-right--chevron-right-large';
8
+ import { Box, xcss } from '@atlaskit/primitives';
9
+ const rightSideStyles = xcss({
10
+ borderLeft: `solid ${"var(--ds-border, #091E4224)"} 1px`,
11
+ right: 'space.0',
12
+ borderTopRightRadius: '3px',
13
+ borderBottomRightRadius: '3px'
14
+ });
15
+ const leftSideStyles = xcss({
16
+ borderRight: `solid ${"var(--ds-border, #091E4224)"} 1px`,
17
+ left: 'space.0',
18
+ borderTopLeftRadius: '3px',
19
+ borderBottomLeftRadius: '3px'
20
+ });
21
+ const buttonCommonStyles = xcss({
22
+ backgroundColor: 'elevation.surface.overlay',
23
+ zIndex: '1',
24
+ position: 'absolute'
25
+ });
26
+ export const ScrollButton = ({
27
+ intl,
28
+ scrollContainerRef,
29
+ node,
30
+ disabled,
31
+ side
32
+ }) => {
33
+ const [needScroll, setNeedScroll] = useState(false);
34
+ const [canScrollToSide, setCanScrollToSide] = useState(true);
35
+ const setCanScrollDebounced = rafSchedule(() => {
36
+ // Refs are null before mounting and after unmount
37
+ if (!scrollContainerRef.current) {
38
+ return;
39
+ }
40
+ const {
41
+ scrollLeft,
42
+ scrollWidth,
43
+ offsetWidth
44
+ } = scrollContainerRef.current;
45
+ setCanScrollToSide(
46
+ // -1 to account for pixel rounding error
47
+ side === 'left' ? scrollLeft > 0 : scrollLeft < scrollWidth - offsetWidth - 1);
48
+ });
49
+ const onScroll = () => {
50
+ setCanScrollDebounced();
51
+ };
52
+ const onClick = () => {
53
+ var _scrollContainerRef$c, _scrollContainerRef$c2, _scrollContainerRef$c3;
54
+ const {
55
+ width: scrollContainerWidth = 0
56
+ } = ((_scrollContainerRef$c = scrollContainerRef.current) === null || _scrollContainerRef$c === void 0 ? void 0 : _scrollContainerRef$c.getBoundingClientRect()) || {};
57
+ const scrollLeft = ((_scrollContainerRef$c2 = scrollContainerRef.current) === null || _scrollContainerRef$c2 === void 0 ? void 0 : _scrollContainerRef$c2.scrollLeft) || 0;
58
+ const scrollTo = side === 'left' ? scrollLeft - scrollContainerWidth : scrollLeft + scrollContainerWidth;
59
+ (_scrollContainerRef$c3 = scrollContainerRef.current) === null || _scrollContainerRef$c3 === void 0 ? void 0 : _scrollContainerRef$c3.scrollTo({
60
+ top: 0,
61
+ left: scrollTo,
62
+ behavior: 'smooth'
63
+ });
64
+ };
65
+ const resizeObserver = new ResizeObserver(t => {
66
+ var _scrollContainerRef$c4, _scrollContainerRef$c5;
67
+ const widthNeededToShowAllItems = ((_scrollContainerRef$c4 = scrollContainerRef.current) === null || _scrollContainerRef$c4 === void 0 ? void 0 : _scrollContainerRef$c4.scrollWidth) || 0;
68
+ const parentNode = (_scrollContainerRef$c5 = scrollContainerRef.current) === null || _scrollContainerRef$c5 === void 0 ? void 0 : _scrollContainerRef$c5.parentNode;
69
+ let availableSpace = -1;
70
+ if (parentNode instanceof HTMLElement) {
71
+ availableSpace = parentNode.offsetWidth;
72
+ }
73
+ if (availableSpace === -1) {
74
+ return;
75
+ }
76
+ if (availableSpace >= widthNeededToShowAllItems) {
77
+ setNeedScroll(false);
78
+ } else {
79
+ setNeedScroll(true);
80
+ onScroll();
81
+ }
82
+ });
83
+ useEffect(() => {
84
+ onScroll();
85
+ const scrollContainerRefCurrent = scrollContainerRef.current;
86
+ let unbind;
87
+ if (scrollContainerRefCurrent) {
88
+ // Adding/removing scroll button depending on scroll position
89
+ unbind = bind(scrollContainerRefCurrent, {
90
+ type: 'scroll',
91
+ listener: onScroll
92
+ });
93
+
94
+ // watch for toolbar resize and show/hide scroll buttons if needed
95
+ resizeObserver.observe(scrollContainerRefCurrent);
96
+ }
97
+ return () => {
98
+ if (scrollContainerRefCurrent) {
99
+ var _unbind;
100
+ (_unbind = unbind) === null || _unbind === void 0 ? void 0 : _unbind();
101
+ resizeObserver.unobserve(scrollContainerRefCurrent);
102
+ }
103
+ setCanScrollDebounced.cancel();
104
+ };
105
+ // eslint-disable-next-line react-hooks/exhaustive-deps
106
+ }, []);
107
+ useEffect(() => {
108
+ const scrollContainerRefCurrent = scrollContainerRef.current;
109
+ if (scrollContainerRefCurrent) {
110
+ var _scrollContainerRefCu;
111
+ // reset scroll position when switching from one node with toolbar to another
112
+ // scroll to made optional as it may not be rendered in testing env
113
+ (_scrollContainerRefCu = scrollContainerRefCurrent.scrollTo) === null || _scrollContainerRefCu === void 0 ? void 0 : _scrollContainerRefCu.call(scrollContainerRefCurrent, {
114
+ left: 0
115
+ });
116
+ }
117
+ }, [node.type, scrollContainerRef]);
118
+ const Icon = side === 'left' ? ChevronLeftLargeIcon : ChevronRightLargeIcon;
119
+ return needScroll && (side === 'left' && canScrollToSide || side === 'right' && canScrollToSide) && /*#__PURE__*/React.createElement(Box, {
120
+ padding: "space.050",
121
+ xcss: [side === 'left' ? leftSideStyles : rightSideStyles, buttonCommonStyles]
122
+ }, /*#__PURE__*/React.createElement(IconButton, {
123
+ appearance: "subtle",
124
+ label: intl.formatMessage(side === 'left' ? messages.floatingToolbarScrollLeft : messages.floatingToolbarScrollRight),
125
+ onClick: onClick,
126
+ isDisabled: disabled,
127
+ icon: Icon,
128
+ isTooltipDisabled: false,
129
+ tooltip: {
130
+ position: 'top'
131
+ }
132
+ }));
133
+ };
@@ -24,6 +24,7 @@ const toolbarScrollButtons = css({
24
24
  });
25
25
  const LeftIcon = ChevronLeftLargeIcon;
26
26
  const RightIcon = ChevronRightLargeIcon;
27
+ // Remove this component (replaced by ScrollButton) as part of platform_editor_controls clean up
27
28
  export const ScrollButtons = ({
28
29
  intl,
29
30
  scrollContainerRef,
@@ -26,6 +26,7 @@ import Dropdown from './Dropdown';
26
26
  import { EmojiPickerButton } from './EmojiPickerButton';
27
27
  import { ExtensionsPlaceholder } from './ExtensionsPlaceholder';
28
28
  import { Input } from './Input';
29
+ import { ScrollButton } from './ScrollButton';
29
30
  import { ScrollButtons } from './ScrollButtons';
30
31
  import Select from './Select';
31
32
  export function groupItems(items) {
@@ -183,11 +184,7 @@ const ToolbarItems = /*#__PURE__*/React.memo(({
183
184
  setDisableParentScroll: scrollable ? setDisableScroll : undefined,
184
185
  dropdownListId: (item === null || item === void 0 ? void 0 : item.id) && `${item.id}-dropdownList`,
185
186
  alignDropdownWithToolbar: items.length === 1,
186
- onToggle: item.onToggle,
187
- footer: item.footer,
188
- onMount: item.onMount,
189
- onClick: item.onClick,
190
- pulse: item.pulse
187
+ onClick: item.onClick
191
188
  });
192
189
  case 'dropdown':
193
190
  const DropdownIcon = item.icon;
@@ -604,6 +601,8 @@ class Toolbar extends Component {
604
601
  scrollable,
605
602
  mediaAssistiveMessage
606
603
  } = this.props;
604
+ const isEditorControlsEnabled = editorExperiment('platform_editor_controls', 'variant1');
605
+ const isEditorControlsPatch2Enabled = isEditorControlsEnabled && fg('platform_editor_controls_patch_2');
607
606
  if (!items || !items.length) {
608
607
  return null;
609
608
  }
@@ -631,10 +630,16 @@ class Toolbar extends Component {
631
630
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
632
631
  ,
633
632
  className: className,
634
- onMouseDown: editorExperiment('platform_editor_controls', 'variant1') ? this.captureMouseEvent : undefined
633
+ onMouseDown: isEditorControlsEnabled ? this.captureMouseEvent : undefined
635
634
  }, jsx(Announcer, {
636
635
  text: mediaAssistiveMessage ? `${mediaAssistiveMessage}, ${intl.formatMessage(messages.floatingToolbarAnnouncer)}` : intl.formatMessage(messages.floatingToolbarAnnouncer),
637
636
  delay: 250
637
+ }), scrollable && isEditorControlsPatch2Enabled && jsx(ScrollButton, {
638
+ intl: intl,
639
+ scrollContainerRef: this.scrollContainerRef,
640
+ node: node,
641
+ disabled: this.state.scrollDisabled,
642
+ side: "left"
638
643
  }), jsx("div", {
639
644
  "data-testid": "floating-toolbar-items",
640
645
  ref: this.scrollContainerRef
@@ -654,12 +659,18 @@ class Toolbar extends Component {
654
659
  setDisableScroll: this.setDisableScroll.bind(this),
655
660
  mountRef: this.mountRef,
656
661
  mounted: this.state.mounted
657
- }))), scrollable && jsx(ScrollButtons, {
662
+ }))), scrollable && (isEditorControlsPatch2Enabled ? jsx(ScrollButton, {
663
+ intl: intl,
664
+ scrollContainerRef: this.scrollContainerRef,
665
+ node: node,
666
+ disabled: this.state.scrollDisabled,
667
+ side: "right"
668
+ }) : jsx(ScrollButtons, {
658
669
  intl: intl,
659
670
  scrollContainerRef: this.scrollContainerRef,
660
671
  node: node,
661
672
  disabled: this.state.scrollDisabled
662
- })), jsx("div", {
673
+ }))), jsx("div", {
663
674
  ref: this.mountRef
664
675
  })));
665
676
  }
@@ -0,0 +1,28 @@
1
+ // if there are more than 1 item with type `overflow-dropdown`, we should only show one and combine the options
2
+ export const consolidateOverflowDropdownItems = overflowDropdowns => {
3
+ if (overflowDropdowns.length <= 1) {
4
+ return overflowDropdowns[0];
5
+ }
6
+ const combinedItems = overflowDropdowns.reduce((acc, item) => {
7
+ if (item.options) {
8
+ acc.push(...item.options);
9
+ }
10
+ return acc;
11
+ }, []);
12
+
13
+ // To filter out items that have a rank property, sort them by rank in descending order (highest rank first)
14
+ const rankedItems = combinedItems.filter(item => 'rank' in item && typeof item.rank === 'number').sort((a, b) => (b.rank || 0) - (a.rank || 0));
15
+ const unrankedItems = combinedItems.filter(item => !('rank' in item && typeof item.rank === 'number'));
16
+ const sortedItems = [...rankedItems, ...unrankedItems];
17
+ const largestDropdownWidth = overflowDropdowns.reduce((acc, item) => {
18
+ if (item.dropdownWidth && item.dropdownWidth > acc) {
19
+ return item.dropdownWidth;
20
+ }
21
+ return acc;
22
+ }, 0);
23
+ return {
24
+ type: 'overflow-dropdown',
25
+ dropdownWidth: largestDropdownWidth,
26
+ options: sortedItems
27
+ };
28
+ };
@@ -1,4 +1,5 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
3
  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; }
3
4
  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
5
  import React from 'react';
@@ -11,6 +12,7 @@ import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
11
12
  import { Popup } from '@atlaskit/editor-common/ui';
12
13
  import { AllSelection, PluginKey } from '@atlaskit/editor-prosemirror/state';
13
14
  import { findDomRefAtPos, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
15
+ import { fg } from '@atlaskit/platform-feature-flags';
14
16
  import { editorExperiment, unstable_editorExperimentParam } from '@atlaskit/tmp-editor-statsig/experiments';
15
17
  import { copyNode as _copyNode } from './pm-plugins/commands';
16
18
  import forceFocusPlugin, { forceFocusSelector } from './pm-plugins/force-focus';
@@ -21,6 +23,7 @@ import { findNode } from './pm-plugins/utils';
21
23
  import { ConfirmationModal } from './ui/ConfirmationModal';
22
24
  import { ExpandButton } from './ui/ExpandButton';
23
25
  import { ToolbarLoader } from './ui/ToolbarLoader';
26
+ import { consolidateOverflowDropdownItems } from './ui/utils';
24
27
 
25
28
  // TODO: AFP-2532 - Fix automatic suppressions below
26
29
  export var getRelevantConfig = function getRelevantConfig(selection, configs) {
@@ -263,6 +266,32 @@ export function ContentComponent(_ref5) {
263
266
  return toolbarItemViewModeProp in item && !!item[toolbarItemViewModeProp];
264
267
  });
265
268
  }
269
+ if (editorExperiment('platform_editor_controls', 'variant1') && fg('platform_editor_controls_patch_1')) {
270
+ var _items2;
271
+ // Consolidate floating toolbar items
272
+ var toolbarItemsArray = Array.isArray(items) ? items : (_items2 = items) === null || _items2 === void 0 ? void 0 : _items2(node);
273
+ var overflowDropdownItems = toolbarItemsArray.filter(function (item) {
274
+ return item.type === 'overflow-dropdown';
275
+ });
276
+ if (overflowDropdownItems.length > 1) {
277
+ var consolidatedOverflowDropdown = consolidateOverflowDropdownItems(overflowDropdownItems);
278
+ var otherItems = toolbarItemsArray.filter(function (item) {
279
+ return item.type !== 'overflow-dropdown';
280
+ });
281
+ if (otherItems.length > 0) {
282
+ // remove the last separators
283
+ while (((_otherItems$at = otherItems.at(-1)) === null || _otherItems$at === void 0 ? void 0 : _otherItems$at.type) === 'separator') {
284
+ var _otherItems$at;
285
+ otherItems.pop();
286
+ }
287
+ }
288
+ items = [].concat(_toConsumableArray(otherItems), [{
289
+ type: 'separator',
290
+ fullHeight: true,
291
+ supportsViewMode: true
292
+ }, consolidatedOverflowDropdown]);
293
+ }
294
+ }
266
295
  var customPositionCalculation;
267
296
  var toolbarItems = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c = pluginInjectionApi.copyButton) === null || _pluginInjectionApi$c === void 0 ? void 0 : _pluginInjectionApi$c.actions.processCopyButtonItems(editorView.state)(Array.isArray(items) ? items : items(node), pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$d = pluginInjectionApi.decorations) === null || _pluginInjectionApi$d === void 0 ? void 0 : _pluginInjectionApi$d.actions.hoverDecoration);
268
297
  var viewModeToolbarEntry = unstable_editorExperimentParam('live_pages_graceful_edit', 'toolbar-entry', {
@@ -0,0 +1,138 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import React, { useEffect, useState } from 'react';
3
+ import { bind } from 'bind-event-listener';
4
+ import rafSchedule from 'raf-schd';
5
+ import { IconButton } from '@atlaskit/button/new';
6
+ import { messages } from '@atlaskit/editor-common/floating-toolbar';
7
+ import ChevronLeftLargeIcon from '@atlaskit/icon/utility/migration/chevron-left--chevron-left-large';
8
+ import ChevronRightLargeIcon from '@atlaskit/icon/utility/migration/chevron-right--chevron-right-large';
9
+ import { Box, xcss } from '@atlaskit/primitives';
10
+ var rightSideStyles = xcss({
11
+ borderLeft: "solid ".concat("var(--ds-border, #091E4224)", " 1px"),
12
+ right: 'space.0',
13
+ borderTopRightRadius: '3px',
14
+ borderBottomRightRadius: '3px'
15
+ });
16
+ var leftSideStyles = xcss({
17
+ borderRight: "solid ".concat("var(--ds-border, #091E4224)", " 1px"),
18
+ left: 'space.0',
19
+ borderTopLeftRadius: '3px',
20
+ borderBottomLeftRadius: '3px'
21
+ });
22
+ var buttonCommonStyles = xcss({
23
+ backgroundColor: 'elevation.surface.overlay',
24
+ zIndex: '1',
25
+ position: 'absolute'
26
+ });
27
+ export var ScrollButton = function ScrollButton(_ref) {
28
+ var intl = _ref.intl,
29
+ scrollContainerRef = _ref.scrollContainerRef,
30
+ node = _ref.node,
31
+ disabled = _ref.disabled,
32
+ side = _ref.side;
33
+ var _useState = useState(false),
34
+ _useState2 = _slicedToArray(_useState, 2),
35
+ needScroll = _useState2[0],
36
+ setNeedScroll = _useState2[1];
37
+ var _useState3 = useState(true),
38
+ _useState4 = _slicedToArray(_useState3, 2),
39
+ canScrollToSide = _useState4[0],
40
+ setCanScrollToSide = _useState4[1];
41
+ var setCanScrollDebounced = rafSchedule(function () {
42
+ // Refs are null before mounting and after unmount
43
+ if (!scrollContainerRef.current) {
44
+ return;
45
+ }
46
+ var _scrollContainerRef$c = scrollContainerRef.current,
47
+ scrollLeft = _scrollContainerRef$c.scrollLeft,
48
+ scrollWidth = _scrollContainerRef$c.scrollWidth,
49
+ offsetWidth = _scrollContainerRef$c.offsetWidth;
50
+ setCanScrollToSide(
51
+ // -1 to account for pixel rounding error
52
+ side === 'left' ? scrollLeft > 0 : scrollLeft < scrollWidth - offsetWidth - 1);
53
+ });
54
+ var onScroll = function onScroll() {
55
+ setCanScrollDebounced();
56
+ };
57
+ var onClick = function onClick() {
58
+ var _scrollContainerRef$c2, _scrollContainerRef$c3, _scrollContainerRef$c4;
59
+ var _ref2 = ((_scrollContainerRef$c2 = scrollContainerRef.current) === null || _scrollContainerRef$c2 === void 0 ? void 0 : _scrollContainerRef$c2.getBoundingClientRect()) || {},
60
+ _ref2$width = _ref2.width,
61
+ scrollContainerWidth = _ref2$width === void 0 ? 0 : _ref2$width;
62
+ var scrollLeft = ((_scrollContainerRef$c3 = scrollContainerRef.current) === null || _scrollContainerRef$c3 === void 0 ? void 0 : _scrollContainerRef$c3.scrollLeft) || 0;
63
+ var scrollTo = side === 'left' ? scrollLeft - scrollContainerWidth : scrollLeft + scrollContainerWidth;
64
+ (_scrollContainerRef$c4 = scrollContainerRef.current) === null || _scrollContainerRef$c4 === void 0 || _scrollContainerRef$c4.scrollTo({
65
+ top: 0,
66
+ left: scrollTo,
67
+ behavior: 'smooth'
68
+ });
69
+ };
70
+ var resizeObserver = new ResizeObserver(function (t) {
71
+ var _scrollContainerRef$c5, _scrollContainerRef$c6;
72
+ var widthNeededToShowAllItems = ((_scrollContainerRef$c5 = scrollContainerRef.current) === null || _scrollContainerRef$c5 === void 0 ? void 0 : _scrollContainerRef$c5.scrollWidth) || 0;
73
+ var parentNode = (_scrollContainerRef$c6 = scrollContainerRef.current) === null || _scrollContainerRef$c6 === void 0 ? void 0 : _scrollContainerRef$c6.parentNode;
74
+ var availableSpace = -1;
75
+ if (parentNode instanceof HTMLElement) {
76
+ availableSpace = parentNode.offsetWidth;
77
+ }
78
+ if (availableSpace === -1) {
79
+ return;
80
+ }
81
+ if (availableSpace >= widthNeededToShowAllItems) {
82
+ setNeedScroll(false);
83
+ } else {
84
+ setNeedScroll(true);
85
+ onScroll();
86
+ }
87
+ });
88
+ useEffect(function () {
89
+ onScroll();
90
+ var scrollContainerRefCurrent = scrollContainerRef.current;
91
+ var unbind;
92
+ if (scrollContainerRefCurrent) {
93
+ // Adding/removing scroll button depending on scroll position
94
+ unbind = bind(scrollContainerRefCurrent, {
95
+ type: 'scroll',
96
+ listener: onScroll
97
+ });
98
+
99
+ // watch for toolbar resize and show/hide scroll buttons if needed
100
+ resizeObserver.observe(scrollContainerRefCurrent);
101
+ }
102
+ return function () {
103
+ if (scrollContainerRefCurrent) {
104
+ var _unbind;
105
+ (_unbind = unbind) === null || _unbind === void 0 || _unbind();
106
+ resizeObserver.unobserve(scrollContainerRefCurrent);
107
+ }
108
+ setCanScrollDebounced.cancel();
109
+ };
110
+ // eslint-disable-next-line react-hooks/exhaustive-deps
111
+ }, []);
112
+ useEffect(function () {
113
+ var scrollContainerRefCurrent = scrollContainerRef.current;
114
+ if (scrollContainerRefCurrent) {
115
+ var _scrollContainerRefCu;
116
+ // reset scroll position when switching from one node with toolbar to another
117
+ // scroll to made optional as it may not be rendered in testing env
118
+ (_scrollContainerRefCu = scrollContainerRefCurrent.scrollTo) === null || _scrollContainerRefCu === void 0 || _scrollContainerRefCu.call(scrollContainerRefCurrent, {
119
+ left: 0
120
+ });
121
+ }
122
+ }, [node.type, scrollContainerRef]);
123
+ var Icon = side === 'left' ? ChevronLeftLargeIcon : ChevronRightLargeIcon;
124
+ return needScroll && (side === 'left' && canScrollToSide || side === 'right' && canScrollToSide) && /*#__PURE__*/React.createElement(Box, {
125
+ padding: "space.050",
126
+ xcss: [side === 'left' ? leftSideStyles : rightSideStyles, buttonCommonStyles]
127
+ }, /*#__PURE__*/React.createElement(IconButton, {
128
+ appearance: "subtle",
129
+ label: intl.formatMessage(side === 'left' ? messages.floatingToolbarScrollLeft : messages.floatingToolbarScrollRight),
130
+ onClick: onClick,
131
+ isDisabled: disabled,
132
+ icon: Icon,
133
+ isTooltipDisabled: false,
134
+ tooltip: {
135
+ position: 'top'
136
+ }
137
+ }));
138
+ };
@@ -25,6 +25,7 @@ var toolbarScrollButtons = css({
25
25
  });
26
26
  var LeftIcon = ChevronLeftLargeIcon;
27
27
  var RightIcon = ChevronRightLargeIcon;
28
+ // Remove this component (replaced by ScrollButton) as part of platform_editor_controls clean up
28
29
  export var ScrollButtons = function ScrollButtons(_ref) {
29
30
  var intl = _ref.intl,
30
31
  scrollContainerRef = _ref.scrollContainerRef,
@@ -33,6 +33,7 @@ import Dropdown from './Dropdown';
33
33
  import { EmojiPickerButton } from './EmojiPickerButton';
34
34
  import { ExtensionsPlaceholder } from './ExtensionsPlaceholder';
35
35
  import { Input } from './Input';
36
+ import { ScrollButton } from './ScrollButton';
36
37
  import { ScrollButtons } from './ScrollButtons';
37
38
  import Select from './Select';
38
39
  export function groupItems(items) {
@@ -199,11 +200,7 @@ var ToolbarItems = /*#__PURE__*/React.memo(function (_ref) {
199
200
  setDisableParentScroll: scrollable ? setDisableScroll : undefined,
200
201
  dropdownListId: (item === null || item === void 0 ? void 0 : item.id) && "".concat(item.id, "-dropdownList"),
201
202
  alignDropdownWithToolbar: items.length === 1,
202
- onToggle: item.onToggle,
203
- footer: item.footer,
204
- onMount: item.onMount,
205
- onClick: item.onClick,
206
- pulse: item.pulse
203
+ onClick: item.onClick
207
204
  });
208
205
  case 'dropdown':
209
206
  var DropdownIcon = item.icon;
@@ -643,6 +640,8 @@ var Toolbar = /*#__PURE__*/function (_Component) {
643
640
  intl = _this$props2.intl,
644
641
  scrollable = _this$props2.scrollable,
645
642
  mediaAssistiveMessage = _this$props2.mediaAssistiveMessage;
643
+ var isEditorControlsEnabled = editorExperiment('platform_editor_controls', 'variant1');
644
+ var isEditorControlsPatch2Enabled = isEditorControlsEnabled && fg('platform_editor_controls_patch_2');
646
645
  if (!items || !items.length) {
647
646
  return null;
648
647
  }
@@ -674,10 +673,16 @@ var Toolbar = /*#__PURE__*/function (_Component) {
674
673
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
675
674
  ,
676
675
  className: className,
677
- onMouseDown: editorExperiment('platform_editor_controls', 'variant1') ? this.captureMouseEvent : undefined
676
+ onMouseDown: isEditorControlsEnabled ? this.captureMouseEvent : undefined
678
677
  }, jsx(Announcer, {
679
678
  text: mediaAssistiveMessage ? "".concat(mediaAssistiveMessage, ", ").concat(intl.formatMessage(messages.floatingToolbarAnnouncer)) : intl.formatMessage(messages.floatingToolbarAnnouncer),
680
679
  delay: 250
680
+ }), scrollable && isEditorControlsPatch2Enabled && jsx(ScrollButton, {
681
+ intl: intl,
682
+ scrollContainerRef: this.scrollContainerRef,
683
+ node: node,
684
+ disabled: this.state.scrollDisabled,
685
+ side: "left"
681
686
  }), jsx("div", {
682
687
  "data-testid": "floating-toolbar-items",
683
688
  ref: this.scrollContainerRef
@@ -697,12 +702,18 @@ var Toolbar = /*#__PURE__*/function (_Component) {
697
702
  setDisableScroll: this.setDisableScroll.bind(this),
698
703
  mountRef: this.mountRef,
699
704
  mounted: this.state.mounted
700
- }))), scrollable && jsx(ScrollButtons, {
705
+ }))), scrollable && (isEditorControlsPatch2Enabled ? jsx(ScrollButton, {
706
+ intl: intl,
707
+ scrollContainerRef: this.scrollContainerRef,
708
+ node: node,
709
+ disabled: this.state.scrollDisabled,
710
+ side: "right"
711
+ }) : jsx(ScrollButtons, {
701
712
  intl: intl,
702
713
  scrollContainerRef: this.scrollContainerRef,
703
714
  node: node,
704
715
  disabled: this.state.scrollDisabled
705
- })), jsx("div", {
716
+ }))), jsx("div", {
706
717
  ref: this.mountRef
707
718
  })));
708
719
  }
@@ -0,0 +1,35 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ // if there are more than 1 item with type `overflow-dropdown`, we should only show one and combine the options
3
+ export var consolidateOverflowDropdownItems = function consolidateOverflowDropdownItems(overflowDropdowns) {
4
+ if (overflowDropdowns.length <= 1) {
5
+ return overflowDropdowns[0];
6
+ }
7
+ var combinedItems = overflowDropdowns.reduce(function (acc, item) {
8
+ if (item.options) {
9
+ acc.push.apply(acc, _toConsumableArray(item.options));
10
+ }
11
+ return acc;
12
+ }, []);
13
+
14
+ // To filter out items that have a rank property, sort them by rank in descending order (highest rank first)
15
+ var rankedItems = combinedItems.filter(function (item) {
16
+ return 'rank' in item && typeof item.rank === 'number';
17
+ }).sort(function (a, b) {
18
+ return (b.rank || 0) - (a.rank || 0);
19
+ });
20
+ var unrankedItems = combinedItems.filter(function (item) {
21
+ return !('rank' in item && typeof item.rank === 'number');
22
+ });
23
+ var sortedItems = [].concat(_toConsumableArray(rankedItems), _toConsumableArray(unrankedItems));
24
+ var largestDropdownWidth = overflowDropdowns.reduce(function (acc, item) {
25
+ if (item.dropdownWidth && item.dropdownWidth > acc) {
26
+ return item.dropdownWidth;
27
+ }
28
+ return acc;
29
+ }, 0);
30
+ return {
31
+ type: 'overflow-dropdown',
32
+ dropdownWidth: largestDropdownWidth,
33
+ options: sortedItems
34
+ };
35
+ };
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import type { IntlShape } from 'react-intl-next';
3
+ import type { Node } from '@atlaskit/editor-prosemirror/model';
4
+ type ScrollButtonProps = {
5
+ intl: IntlShape;
6
+ scrollContainerRef: React.RefObject<HTMLDivElement>;
7
+ node: Node;
8
+ disabled: boolean;
9
+ side: 'left' | 'right';
10
+ };
11
+ export declare const ScrollButton: ({ intl, scrollContainerRef, node, disabled, side, }: ScrollButtonProps) => false | React.JSX.Element;
12
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Command, FloatingToolbarOverflowDropdown } from '@atlaskit/editor-common/types';
2
+ export declare const consolidateOverflowDropdownItems: (overflowDropdowns: FloatingToolbarOverflowDropdown<Command>[]) => FloatingToolbarOverflowDropdown<Command>;
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import type { IntlShape } from 'react-intl-next';
3
+ import type { Node } from '@atlaskit/editor-prosemirror/model';
4
+ type ScrollButtonProps = {
5
+ intl: IntlShape;
6
+ scrollContainerRef: React.RefObject<HTMLDivElement>;
7
+ node: Node;
8
+ disabled: boolean;
9
+ side: 'left' | 'right';
10
+ };
11
+ export declare const ScrollButton: ({ intl, scrollContainerRef, node, disabled, side, }: ScrollButtonProps) => false | React.JSX.Element;
12
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Command, FloatingToolbarOverflowDropdown } from '@atlaskit/editor-common/types';
2
+ export declare const consolidateOverflowDropdownItems: (overflowDropdowns: FloatingToolbarOverflowDropdown<Command>[]) => FloatingToolbarOverflowDropdown<Command>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-floating-toolbar",
3
- "version": "3.3.3",
3
+ "version": "3.3.5",
4
4
  "description": "Floating toolbar plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -26,33 +26,34 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@atlaskit/adf-utils": "^19.19.0",
29
- "@atlaskit/button": "^22.0.0",
29
+ "@atlaskit/button": "^23.0.0",
30
30
  "@atlaskit/checkbox": "^17.0.0",
31
- "@atlaskit/editor-common": "^102.13.0",
31
+ "@atlaskit/editor-common": "^102.16.0",
32
32
  "@atlaskit/editor-palette": "^2.1.0",
33
- "@atlaskit/editor-plugin-block-controls": "^3.5.0",
33
+ "@atlaskit/editor-plugin-block-controls": "^3.8.0",
34
34
  "@atlaskit/editor-plugin-context-panel": "^4.0.0",
35
35
  "@atlaskit/editor-plugin-copy-button": "^2.0.0",
36
36
  "@atlaskit/editor-plugin-decorations": "^2.0.0",
37
37
  "@atlaskit/editor-plugin-editor-disabled": "^2.0.0",
38
38
  "@atlaskit/editor-plugin-editor-viewmode": "^3.0.0",
39
- "@atlaskit/editor-plugin-emoji": "^3.2.0",
40
- "@atlaskit/editor-plugin-extension": "^5.0.0",
39
+ "@atlaskit/editor-plugin-emoji": "^3.3.0",
40
+ "@atlaskit/editor-plugin-extension": "^5.1.0",
41
41
  "@atlaskit/editor-plugin-table": "^10.5.0",
42
42
  "@atlaskit/editor-prosemirror": "7.0.0",
43
43
  "@atlaskit/emoji": "^69.0.0",
44
- "@atlaskit/icon": "^25.2.0",
45
- "@atlaskit/menu": "^3.1.0",
46
- "@atlaskit/modal-dialog": "^14.0.0",
44
+ "@atlaskit/icon": "^25.4.0",
45
+ "@atlaskit/menu": "^3.2.0",
46
+ "@atlaskit/modal-dialog": "^14.1.0",
47
47
  "@atlaskit/platform-feature-flags": "^1.1.0",
48
48
  "@atlaskit/primitives": "^14.2.0",
49
49
  "@atlaskit/select": "^20.0.0",
50
50
  "@atlaskit/theme": "^18.0.0",
51
- "@atlaskit/tmp-editor-statsig": "^4.4.0",
51
+ "@atlaskit/tmp-editor-statsig": "^4.6.0",
52
52
  "@atlaskit/tokens": "^4.5.0",
53
53
  "@atlaskit/tooltip": "^20.0.0",
54
54
  "@babel/runtime": "^7.0.0",
55
55
  "@emotion/react": "^11.7.1",
56
+ "bind-event-listener": "^3.0.0",
56
57
  "lodash": "^4.17.21",
57
58
  "raf-schd": "^4.0.3",
58
59
  "react-intl-next": "npm:react-intl@^5.18.1",
@@ -127,6 +128,12 @@
127
128
  },
128
129
  "platform_editor_fix_floating_toolbar_scrollbar": {
129
130
  "type": "boolean"
131
+ },
132
+ "platform_editor_controls_patch_1": {
133
+ "type": "boolean"
134
+ },
135
+ "platform_editor_controls_patch_2": {
136
+ "type": "boolean"
130
137
  }
131
138
  }
132
139
  }