@atlaskit/editor-plugin-floating-toolbar 3.3.4 → 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,13 @@
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
+
3
11
  ## 3.3.4
4
12
 
5
13
  ### Patch Changes
@@ -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); }
@@ -646,6 +647,8 @@ var Toolbar = /*#__PURE__*/function (_Component) {
646
647
  intl = _this$props2.intl,
647
648
  scrollable = _this$props2.scrollable,
648
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');
649
652
  if (!items || !items.length) {
650
653
  return null;
651
654
  }
@@ -677,10 +680,16 @@ var Toolbar = /*#__PURE__*/function (_Component) {
677
680
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
678
681
  ,
679
682
  className: className,
680
- onMouseDown: (0, _experiments.editorExperiment)('platform_editor_controls', 'variant1') ? this.captureMouseEvent : undefined
683
+ onMouseDown: isEditorControlsEnabled ? this.captureMouseEvent : undefined
681
684
  }, (0, _react2.jsx)(_ui.Announcer, {
682
685
  text: mediaAssistiveMessage ? "".concat(mediaAssistiveMessage, ", ").concat(intl.formatMessage(_floatingToolbar.messages.floatingToolbarAnnouncer)) : intl.formatMessage(_floatingToolbar.messages.floatingToolbarAnnouncer),
683
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"
684
693
  }), (0, _react2.jsx)("div", {
685
694
  "data-testid": "floating-toolbar-items",
686
695
  ref: this.scrollContainerRef
@@ -700,12 +709,18 @@ var Toolbar = /*#__PURE__*/function (_Component) {
700
709
  setDisableScroll: this.setDisableScroll.bind(this),
701
710
  mountRef: this.mountRef,
702
711
  mounted: this.state.mounted
703
- }))), 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, {
704
719
  intl: intl,
705
720
  scrollContainerRef: this.scrollContainerRef,
706
721
  node: node,
707
722
  disabled: this.state.scrollDisabled
708
- })), (0, _react2.jsx)("div", {
723
+ }))), (0, _react2.jsx)("div", {
709
724
  ref: this.mountRef
710
725
  })));
711
726
  }
@@ -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) {
@@ -600,6 +601,8 @@ class Toolbar extends Component {
600
601
  scrollable,
601
602
  mediaAssistiveMessage
602
603
  } = this.props;
604
+ const isEditorControlsEnabled = editorExperiment('platform_editor_controls', 'variant1');
605
+ const isEditorControlsPatch2Enabled = isEditorControlsEnabled && fg('platform_editor_controls_patch_2');
603
606
  if (!items || !items.length) {
604
607
  return null;
605
608
  }
@@ -627,10 +630,16 @@ class Toolbar extends Component {
627
630
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
628
631
  ,
629
632
  className: className,
630
- onMouseDown: editorExperiment('platform_editor_controls', 'variant1') ? this.captureMouseEvent : undefined
633
+ onMouseDown: isEditorControlsEnabled ? this.captureMouseEvent : undefined
631
634
  }, jsx(Announcer, {
632
635
  text: mediaAssistiveMessage ? `${mediaAssistiveMessage}, ${intl.formatMessage(messages.floatingToolbarAnnouncer)}` : intl.formatMessage(messages.floatingToolbarAnnouncer),
633
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"
634
643
  }), jsx("div", {
635
644
  "data-testid": "floating-toolbar-items",
636
645
  ref: this.scrollContainerRef
@@ -650,12 +659,18 @@ class Toolbar extends Component {
650
659
  setDisableScroll: this.setDisableScroll.bind(this),
651
660
  mountRef: this.mountRef,
652
661
  mounted: this.state.mounted
653
- }))), 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, {
654
669
  intl: intl,
655
670
  scrollContainerRef: this.scrollContainerRef,
656
671
  node: node,
657
672
  disabled: this.state.scrollDisabled
658
- })), jsx("div", {
673
+ }))), jsx("div", {
659
674
  ref: this.mountRef
660
675
  })));
661
676
  }
@@ -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) {
@@ -639,6 +640,8 @@ var Toolbar = /*#__PURE__*/function (_Component) {
639
640
  intl = _this$props2.intl,
640
641
  scrollable = _this$props2.scrollable,
641
642
  mediaAssistiveMessage = _this$props2.mediaAssistiveMessage;
643
+ var isEditorControlsEnabled = editorExperiment('platform_editor_controls', 'variant1');
644
+ var isEditorControlsPatch2Enabled = isEditorControlsEnabled && fg('platform_editor_controls_patch_2');
642
645
  if (!items || !items.length) {
643
646
  return null;
644
647
  }
@@ -670,10 +673,16 @@ var Toolbar = /*#__PURE__*/function (_Component) {
670
673
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
671
674
  ,
672
675
  className: className,
673
- onMouseDown: editorExperiment('platform_editor_controls', 'variant1') ? this.captureMouseEvent : undefined
676
+ onMouseDown: isEditorControlsEnabled ? this.captureMouseEvent : undefined
674
677
  }, jsx(Announcer, {
675
678
  text: mediaAssistiveMessage ? "".concat(mediaAssistiveMessage, ", ").concat(intl.formatMessage(messages.floatingToolbarAnnouncer)) : intl.formatMessage(messages.floatingToolbarAnnouncer),
676
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"
677
686
  }), jsx("div", {
678
687
  "data-testid": "floating-toolbar-items",
679
688
  ref: this.scrollContainerRef
@@ -693,12 +702,18 @@ var Toolbar = /*#__PURE__*/function (_Component) {
693
702
  setDisableScroll: this.setDisableScroll.bind(this),
694
703
  mountRef: this.mountRef,
695
704
  mounted: this.state.mounted
696
- }))), 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, {
697
712
  intl: intl,
698
713
  scrollContainerRef: this.scrollContainerRef,
699
714
  node: node,
700
715
  disabled: this.state.scrollDisabled
701
- })), jsx("div", {
716
+ }))), jsx("div", {
702
717
  ref: this.mountRef
703
718
  })));
704
719
  }
@@ -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,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 {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-floating-toolbar",
3
- "version": "3.3.4",
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",
@@ -53,6 +53,7 @@
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",
@@ -130,6 +131,9 @@
130
131
  },
131
132
  "platform_editor_controls_patch_1": {
132
133
  "type": "boolean"
134
+ },
135
+ "platform_editor_controls_patch_2": {
136
+ "type": "boolean"
133
137
  }
134
138
  }
135
139
  }