@atlaskit/link-picker 1.11.2 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/services/use-plugins/index.js +7 -1
  3. package/dist/cjs/ui/link-picker/index.js +11 -7
  4. package/dist/cjs/ui/scrolling-tabs/index.js +192 -0
  5. package/dist/cjs/ui/scrolling-tabs/scrolling-tabs.js +123 -0
  6. package/dist/cjs/ui/scrolling-tabs/styles.js +64 -0
  7. package/dist/cjs/version.json +1 -1
  8. package/dist/es2019/services/use-plugins/index.js +7 -1
  9. package/dist/es2019/ui/link-picker/index.js +8 -5
  10. package/dist/es2019/ui/scrolling-tabs/index.js +127 -0
  11. package/dist/es2019/ui/scrolling-tabs/scrolling-tabs.js +90 -0
  12. package/dist/es2019/ui/scrolling-tabs/styles.js +49 -0
  13. package/dist/es2019/version.json +1 -1
  14. package/dist/esm/services/use-plugins/index.js +7 -1
  15. package/dist/esm/ui/link-picker/index.js +10 -7
  16. package/dist/esm/ui/scrolling-tabs/index.js +165 -0
  17. package/dist/esm/ui/scrolling-tabs/scrolling-tabs.js +96 -0
  18. package/dist/esm/ui/scrolling-tabs/styles.js +49 -0
  19. package/dist/esm/version.json +1 -1
  20. package/dist/types/ui/link-picker/index.d.ts +1 -0
  21. package/dist/types/ui/scrolling-tabs/index.d.ts +7 -0
  22. package/dist/types/ui/scrolling-tabs/scrolling-tabs.d.ts +11 -0
  23. package/dist/types/ui/scrolling-tabs/styles.d.ts +6 -0
  24. package/dist/types/ui/types.d.ts +2 -0
  25. package/dist/types-ts4.0/ui/link-picker/index.d.ts +1 -0
  26. package/dist/types-ts4.0/ui/scrolling-tabs/index.d.ts +7 -0
  27. package/dist/types-ts4.0/ui/scrolling-tabs/scrolling-tabs.d.ts +11 -0
  28. package/dist/types-ts4.0/ui/scrolling-tabs/styles.d.ts +6 -0
  29. package/dist/types-ts4.0/ui/types.d.ts +2 -0
  30. package/package.json +6 -6
  31. package/report.api.md +3 -0
  32. package/tmp/api-report-tmp.d.ts +3 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @atlaskit/link-picker
2
2
 
3
+ ## 1.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`91bd9e3193c`](https://bitbucket.org/atlassian/atlassian-frontend/commits/91bd9e3193c) - add experimental support for UNSAFE_onActivation call for plugins
8
+
9
+ ## 1.12.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`2ecb5e8a6d9`](https://bitbucket.org/atlassian/atlassian-frontend/commits/2ecb5e8a6d9) - [ux] introduce horizontal scrolling tabs
14
+
3
15
  ## 1.11.2
4
16
 
5
17
  ### Patch Changes
@@ -43,7 +43,13 @@ function usePlugins(state, activeTab, plugins) {
43
43
  pluginState = _usePluginReducer2[0],
44
44
  dispatch = _usePluginReducer2[1];
45
45
 
46
- var activePlugin = plugins === null || plugins === void 0 ? void 0 : plugins[activeTab];
46
+ var activePlugin = plugins === null || plugins === void 0 ? void 0 : plugins[activeTab]; // This useEffect block must be called before any other to ensure onActivation is fired at before resolve
47
+
48
+ (0, _react.useEffect)(function () {
49
+ if (activePlugin && activePlugin.UNSAFE_onActivation) {
50
+ activePlugin.UNSAFE_onActivation();
51
+ }
52
+ }, [activePlugin]);
47
53
  (0, _react.useEffect)(function () {
48
54
  if (!activePlugin) {
49
55
  return;
@@ -55,6 +55,8 @@ var _browser = require("./browser");
55
55
 
56
56
  var _announcer = _interopRequireDefault(require("./announcer"));
57
57
 
58
+ var _scrollingTabs = _interopRequireDefault(require("../scrolling-tabs"));
59
+
58
60
  var _linkSearchList = _interopRequireWildcard(require("./link-search-list"));
59
61
 
60
62
  var _linkSearchError = _interopRequireWildcard(require("./link-search-error"));
@@ -139,7 +141,8 @@ function LinkPicker(_ref) {
139
141
  onContentResize = _ref.onContentResize,
140
142
  plugins = _ref.plugins,
141
143
  initUrl = _ref.url,
142
- initDisplayText = _ref.displayText;
144
+ initDisplayText = _ref.displayText,
145
+ featureFlags = _ref.featureFlags;
143
146
 
144
147
  var _useAnalyticsEvents = (0, _analyticsNext.useAnalyticsEvents)(),
145
148
  createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
@@ -350,6 +353,12 @@ function LinkPicker(_ref) {
350
353
  size: "medium",
351
354
  label: ''
352
355
  }));
356
+ var tabList = (0, _react2.jsx)(_tabs.TabList, null, tabs.map(function (tab) {
357
+ return (0, _react2.jsx)(_tabs.Tab, {
358
+ key: tab.tabTitle,
359
+ testId: testIds.tabItem
360
+ }, tab.tabTitle);
361
+ }));
353
362
  return (0, _react2.jsx)("form", {
354
363
  "data-testid": testIds.linkPicker,
355
364
  css: _styled.rootContainerStyles,
@@ -400,12 +409,7 @@ function LinkPicker(_ref) {
400
409
  testId: testIds.tabList,
401
410
  selected: activeTab,
402
411
  onChange: handleTabChange
403
- }, (0, _react2.jsx)(_tabs.TabList, null, tabs.map(function (tab) {
404
- return (0, _react2.jsx)(_tabs.Tab, {
405
- key: tab.tabTitle,
406
- testId: testIds.tabItem
407
- }, tab.tabTitle);
408
- }))), (0, _react2.jsx)(_trackTabViewed.default, {
412
+ }, featureFlags !== null && featureFlags !== void 0 && featureFlags.scrollingTabs ? (0, _react2.jsx)(_scrollingTabs.default, null, tabList) : tabList), (0, _react2.jsx)(_trackTabViewed.default, {
409
413
  activeTab: activeTab
410
414
  })), (0, _react2.jsx)("div", (0, _extends2.default)({
411
415
  css: _styled.flexColumnStyles
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ var _typeof = require("@babel/runtime/helpers/typeof");
6
+
7
+ Object.defineProperty(exports, "__esModule", {
8
+ value: true
9
+ });
10
+ exports.default = exports.ScrollingTabList = void 0;
11
+
12
+ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
13
+
14
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
15
+
16
+ var _react = _interopRequireWildcard(require("react"));
17
+
18
+ var _react2 = require("@emotion/react");
19
+
20
+ var _button = _interopRequireDefault(require("@atlaskit/button"));
21
+
22
+ var _chevronLeft = _interopRequireDefault(require("@atlaskit/icon/glyph/chevron-left"));
23
+
24
+ var _chevronRight = _interopRequireDefault(require("@atlaskit/icon/glyph/chevron-right"));
25
+
26
+ var _scrollingTabs = require("./scrolling-tabs");
27
+
28
+ var _styles = require("./styles");
29
+
30
+ 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); }
31
+
32
+ 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; }
33
+
34
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
35
+
36
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
37
+
38
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
39
+
40
+ function isTouchDevice() {
41
+ return 'ontouchstart' in window || // eslint-disable-next-line compat/compat
42
+ navigator.maxTouchPoints > 0;
43
+ }
44
+
45
+ var initialConditionalButtonsState = {
46
+ back: false,
47
+ forward: false
48
+ };
49
+
50
+ var ScrollingTabList = function ScrollingTabList(props) {
51
+ var ref = (0, _react.useRef)(null);
52
+
53
+ var _useState = (0, _react.useState)(initialConditionalButtonsState),
54
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
55
+ conditionalButtons = _useState2[0],
56
+ setConditionalButtons = _useState2[1];
57
+
58
+ var ghost = (0, _react.useMemo)(function () {
59
+ return (0, _scrollingTabs.createGhost)();
60
+ }, []);
61
+ var onTabClick = (0, _react.useCallback)(function (e) {
62
+ var target = e.currentTarget;
63
+
64
+ if (target) {
65
+ target.scrollIntoView({
66
+ behavior: 'smooth'
67
+ });
68
+ }
69
+ }, []);
70
+
71
+ _react.default.useLayoutEffect(function () {
72
+ var container = ref.current;
73
+ var scrollingContainer;
74
+ var tabs;
75
+
76
+ var handleConditionalButtonsChange = function handleConditionalButtonsChange() {
77
+ var buttons = (0, _scrollingTabs.calculateConditionalButtons)(scrollingContainer, isTouchDevice());
78
+ setConditionalButtons(buttons);
79
+ };
80
+
81
+ var observerCallback = function observerCallback(mutationList) {
82
+ var tablist = (0, _scrollingTabs.getTabList)(ref);
83
+
84
+ var _iterator = _createForOfIteratorHelper(mutationList),
85
+ _step;
86
+
87
+ try {
88
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
89
+ var mutation = _step.value;
90
+
91
+ if (mutation.type === 'childList') {
92
+ var addedNodes = Array.from(mutation.addedNodes);
93
+ var found = addedNodes.find(function (node) {
94
+ return node.getAttribute('role') === 'tab';
95
+ });
96
+
97
+ if (found && tablist) {
98
+ ghost.remove();
99
+
100
+ var _tabs = Array.from(tablist.children);
101
+
102
+ _tabs.forEach(function (tab) {
103
+ tab.removeEventListener('click', onTabClick);
104
+ tab.addEventListener('click', onTabClick);
105
+ });
106
+
107
+ tablist.appendChild(ghost);
108
+ handleConditionalButtonsChange();
109
+ }
110
+ }
111
+ }
112
+ } catch (err) {
113
+ _iterator.e(err);
114
+ } finally {
115
+ _iterator.f();
116
+ }
117
+ };
118
+
119
+ var observer = new MutationObserver(observerCallback);
120
+
121
+ if (container) {
122
+ var tablist = (0, _scrollingTabs.getTabList)(ref);
123
+ scrollingContainer = tablist === null || tablist === void 0 ? void 0 : tablist.parentElement;
124
+ observer.observe(container, {
125
+ attributes: false,
126
+ childList: true,
127
+ subtree: true
128
+ });
129
+
130
+ if (scrollingContainer instanceof HTMLElement && tablist) {
131
+ tablist.appendChild(ghost);
132
+ tabs = Array.from(tablist.children);
133
+ tabs.forEach(function (tab) {
134
+ return tab.addEventListener('click', onTabClick);
135
+ });
136
+ handleConditionalButtonsChange();
137
+ scrollingContainer.addEventListener('scroll', handleConditionalButtonsChange);
138
+ }
139
+
140
+ return function () {
141
+ if (scrollingContainer) {
142
+ scrollingContainer.removeEventListener('scroll', handleConditionalButtonsChange);
143
+ }
144
+
145
+ if (tabs.length) {
146
+ tabs.forEach(function (tab) {
147
+ return tab.removeEventListener('click', onTabClick);
148
+ });
149
+ }
150
+ };
151
+ }
152
+ }, [onTabClick, ghost, ref]);
153
+
154
+ var buttonProps = {
155
+ appearance: 'subtle',
156
+ spacing: 'none'
157
+ };
158
+ return (0, _react2.jsx)("div", {
159
+ css: _styles.containerStyles,
160
+ ref: ref,
161
+ "data-testid": "scrolling-tabs"
162
+ }, conditionalButtons.back && (0, _react2.jsx)("div", {
163
+ className: "back",
164
+ css: _styles.backButtonStyles
165
+ }, (0, _react2.jsx)(_button.default, (0, _extends2.default)({
166
+ "data-test-id": "back"
167
+ }, buttonProps, {
168
+ iconBefore: (0, _react2.jsx)(_chevronLeft.default, {
169
+ label: "back"
170
+ }),
171
+ onClick: function onClick() {
172
+ return (0, _scrollingTabs.scrollBack)(ref);
173
+ }
174
+ }))), (0, _react2.jsx)("div", {
175
+ css: _styles.scrollingContainerStyles
176
+ }, props.children), conditionalButtons.forward && (0, _react2.jsx)("div", {
177
+ css: _styles.nextButtonStyles
178
+ }, (0, _react2.jsx)(_button.default, (0, _extends2.default)({
179
+ "data-test-id": "forward"
180
+ }, buttonProps, {
181
+ iconBefore: (0, _react2.jsx)(_chevronRight.default, {
182
+ label: "forward"
183
+ }),
184
+ onClick: function onClick() {
185
+ return (0, _scrollingTabs.scrollForward)(ref);
186
+ }
187
+ }))));
188
+ };
189
+
190
+ exports.ScrollingTabList = ScrollingTabList;
191
+ var _default = ScrollingTabList;
192
+ exports.default = _default;
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.scrollForward = exports.scrollBack = exports.getTabList = exports.getContainer = exports.createGhost = exports.calculateConditionalButtons = void 0;
9
+
10
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
+
12
+ var BUTTON_SIZE = 24;
13
+ var GUTTER = 48;
14
+ var GHOST_WIDTH = 342 - GUTTER - 40;
15
+
16
+ var scrollBack = function scrollBack(ref) {
17
+ moveTabsScroll(ref, findPrevious);
18
+ };
19
+
20
+ exports.scrollBack = scrollBack;
21
+
22
+ var scrollForward = function scrollForward(ref) {
23
+ moveTabsScroll(ref, findNext);
24
+ };
25
+
26
+ exports.scrollForward = scrollForward;
27
+
28
+ var calculateConditionalButtons = function calculateConditionalButtons(container, hideButtons) {
29
+ var initialState = {
30
+ back: false,
31
+ forward: false
32
+ };
33
+
34
+ if (!container || hideButtons) {
35
+ return initialState;
36
+ }
37
+
38
+ var maxScrollValue = container.scrollWidth - container.clientWidth;
39
+ return {
40
+ back: container.scrollLeft > 0,
41
+ forward: container.scrollLeft < maxScrollValue - GHOST_WIDTH
42
+ };
43
+ };
44
+
45
+ exports.calculateConditionalButtons = calculateConditionalButtons;
46
+
47
+ var createGhost = function createGhost() {
48
+ var ghost = document.createElement('div');
49
+ ghost.setAttribute('id', 'scrolling-tabs-ghost');
50
+ ghost.style.minWidth = "".concat(GHOST_WIDTH, "px");
51
+ ghost.style.pointerEvents = 'none';
52
+ return ghost;
53
+ };
54
+
55
+ exports.createGhost = createGhost;
56
+
57
+ var moveTabsScroll = function moveTabsScroll(ref, findFn) {
58
+ var container = getContainer(ref);
59
+
60
+ if (container) {
61
+ var tablist = container.children[0];
62
+
63
+ var _tabs = (0, _toConsumableArray2.default)(tablist.children);
64
+
65
+ if (_tabs.length > 0) {
66
+ var target = findFn(container, _tabs);
67
+
68
+ if (target) {
69
+ var left = container.scrollLeft + target.getBoundingClientRect().left - container.getBoundingClientRect().left - BUTTON_SIZE;
70
+ scrollTo(container, left);
71
+ }
72
+ }
73
+ }
74
+ };
75
+
76
+ var scrollTo = function scrollTo(container, left) {
77
+ container.scrollTo({
78
+ left: left
79
+ });
80
+ };
81
+
82
+ var getTabList = function getTabList(ref) {
83
+ var _ref$current;
84
+
85
+ return (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.querySelector('[role="tablist"]');
86
+ };
87
+
88
+ exports.getTabList = getTabList;
89
+
90
+ var getContainer = function getContainer(ref) {
91
+ var _getTabList;
92
+
93
+ return (_getTabList = getTabList(ref)) === null || _getTabList === void 0 ? void 0 : _getTabList.parentElement;
94
+ };
95
+
96
+ exports.getContainer = getContainer;
97
+
98
+ var findPrevious = function findPrevious(container, tabs) {
99
+ var previousTabs = tabs.filter(function (tab) {
100
+ var _ref = [container.getBoundingClientRect(), tab.getBoundingClientRect()],
101
+ containerRect = _ref[0],
102
+ tabRect = _ref[1];
103
+ var containerArea = containerRect.left + container.scrollLeft - GUTTER,
104
+ tabArea = container.scrollLeft + tabRect.left + tabRect.width;
105
+ return containerArea > tabArea;
106
+ });
107
+
108
+ if (previousTabs.length < 3) {
109
+ return previousTabs[0];
110
+ }
111
+
112
+ return previousTabs[previousTabs.length - 1];
113
+ };
114
+
115
+ var findNext = function findNext(container, tabs) {
116
+ var containerRect = container.getBoundingClientRect();
117
+ return tabs.find(function (tab) {
118
+ var tabRect = tab.getBoundingClientRect();
119
+ var containerArea = container.clientWidth + containerRect.left - GUTTER,
120
+ tabArea = tabRect.left + tabRect.width;
121
+ return containerArea < tabArea;
122
+ });
123
+ };
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.scrollingContainerStyles = exports.nextButtonStyles = exports.containerStyles = exports.buttonContainerStyles = exports.backButtonStyles = void 0;
7
+
8
+ var _react = require("@emotion/react");
9
+
10
+ var _tokens = require("@atlaskit/tokens");
11
+
12
+ var _colors = require("@atlaskit/theme/colors");
13
+
14
+ var scrollingContainerStyles = (0, _react.css)({
15
+ overflowX: 'auto',
16
+ scrollBehavior: 'smooth',
17
+ scrollPadding: '0 24px',
18
+ scrollbarWidth: 'none',
19
+ '&::-webkit-scrollbar': {
20
+ display: 'none'
21
+ },
22
+ '&::before': {
23
+ content: '""',
24
+ borderRadius: 2,
25
+ bottom: 0,
26
+ margin: 0,
27
+ position: 'absolute',
28
+ width: 'inherit',
29
+ left: 8,
30
+ right: 8,
31
+ height: 2,
32
+ backgroundColor: (0, _tokens.token)('color.border', _colors.N30)
33
+ }
34
+ });
35
+ exports.scrollingContainerStyles = scrollingContainerStyles;
36
+ var containerStyles = (0, _react.css)({
37
+ position: 'relative',
38
+ '[role="tablist"]': {
39
+ '&::before': {
40
+ display: 'none'
41
+ }
42
+ },
43
+ // Overrides Atlaskit tabs styles to stop overflowing with ellipsis
44
+ '[role="tab"]': {
45
+ overflow: 'unset !important',
46
+ textOverflow: 'unset !important'
47
+ }
48
+ });
49
+ exports.containerStyles = containerStyles;
50
+ var buttonContainerStyles = (0, _react.css)({
51
+ position: 'absolute',
52
+ top: 4,
53
+ zIndex: 999,
54
+ backgroundColor: (0, _tokens.token)('elevation.surface', _colors.N0)
55
+ });
56
+ exports.buttonContainerStyles = buttonContainerStyles;
57
+ var backButtonStyles = (0, _react.css)([buttonContainerStyles, {
58
+ left: 0
59
+ }]);
60
+ exports.backButtonStyles = backButtonStyles;
61
+ var nextButtonStyles = (0, _react.css)([buttonContainerStyles, {
62
+ right: 0
63
+ }]);
64
+ exports.nextButtonStyles = nextButtonStyles;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/link-picker",
3
- "version": "1.11.2",
3
+ "version": "1.13.0",
4
4
  "sideEffects": false
5
5
  }
@@ -12,7 +12,13 @@ export function usePlugins(state, activeTab, plugins) {
12
12
  } = useAnalyticsEvents();
13
13
  const [retries, setRetries] = useState(0);
14
14
  const [pluginState, dispatch] = usePluginReducer();
15
- const activePlugin = plugins === null || plugins === void 0 ? void 0 : plugins[activeTab];
15
+ const activePlugin = plugins === null || plugins === void 0 ? void 0 : plugins[activeTab]; // This useEffect block must be called before any other to ensure onActivation is fired at before resolve
16
+
17
+ useEffect(() => {
18
+ if (activePlugin && activePlugin.UNSAFE_onActivation) {
19
+ activePlugin.UNSAFE_onActivation();
20
+ }
21
+ }, [activePlugin]);
16
22
  useEffect(() => {
17
23
  if (!activePlugin) {
18
24
  return;
@@ -20,6 +20,7 @@ import TextInput, { testIds as textFieldTestIds } from './text-input';
20
20
  import { rootContainerStyles, searchIconStyles, tabsWrapperStyles, flexColumnStyles, formFooterMargin } from './styled';
21
21
  import { browser } from './browser';
22
22
  import Announcer from './announcer';
23
+ import ScrollingTabList from '../scrolling-tabs';
23
24
  import LinkSearchList, { testIds as listTestIds } from './link-search-list';
24
25
  import LinkSearchError, { testIds as searchErrorTestIds } from './link-search-error';
25
26
  import FormFooter, { testIds as formFooterTestIds } from './form-footer';
@@ -86,7 +87,8 @@ function LinkPicker({
86
87
  onContentResize,
87
88
  plugins,
88
89
  url: initUrl,
89
- displayText: initDisplayText
90
+ displayText: initDisplayText,
91
+ featureFlags
90
92
  }) {
91
93
  var _errorFallback;
92
94
 
@@ -296,6 +298,10 @@ function LinkPicker({
296
298
  size: "medium",
297
299
  label: ''
298
300
  }));
301
+ const tabList = jsx(TabList, null, tabs.map(tab => jsx(Tab, {
302
+ key: tab.tabTitle,
303
+ testId: testIds.tabItem
304
+ }, tab.tabTitle)));
299
305
  return jsx("form", {
300
306
  "data-testid": testIds.linkPicker,
301
307
  css: rootContainerStyles,
@@ -346,10 +352,7 @@ function LinkPicker({
346
352
  testId: testIds.tabList,
347
353
  selected: activeTab,
348
354
  onChange: handleTabChange
349
- }, jsx(TabList, null, tabs.map(tab => jsx(Tab, {
350
- key: tab.tabTitle,
351
- testId: testIds.tabItem
352
- }, tab.tabTitle)))), jsx(TrackTabViewed, {
355
+ }, featureFlags !== null && featureFlags !== void 0 && featureFlags.scrollingTabs ? jsx(ScrollingTabList, null, tabList) : tabList), jsx(TrackTabViewed, {
353
356
  activeTab: activeTab
354
357
  })), jsx("div", _extends({
355
358
  css: flexColumnStyles
@@ -0,0 +1,127 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+
3
+ /** @jsx jsx */
4
+ import React, { useRef, useMemo, useState, useCallback } from 'react';
5
+ import { jsx } from '@emotion/react';
6
+ import Button from '@atlaskit/button';
7
+ import ChevronLeftIcon from '@atlaskit/icon/glyph/chevron-left';
8
+ import ChevronRightIcon from '@atlaskit/icon/glyph/chevron-right';
9
+ import { scrollBack, scrollForward, calculateConditionalButtons, getTabList, createGhost } from './scrolling-tabs';
10
+ import { containerStyles, scrollingContainerStyles, nextButtonStyles, backButtonStyles } from './styles';
11
+
12
+ function isTouchDevice() {
13
+ return 'ontouchstart' in window || // eslint-disable-next-line compat/compat
14
+ navigator.maxTouchPoints > 0;
15
+ }
16
+
17
+ const initialConditionalButtonsState = {
18
+ back: false,
19
+ forward: false
20
+ };
21
+ export const ScrollingTabList = props => {
22
+ const ref = useRef(null);
23
+ const [conditionalButtons, setConditionalButtons] = useState(initialConditionalButtonsState);
24
+ const ghost = useMemo(() => createGhost(), []);
25
+ const onTabClick = useCallback(e => {
26
+ const target = e.currentTarget;
27
+
28
+ if (target) {
29
+ target.scrollIntoView({
30
+ behavior: 'smooth'
31
+ });
32
+ }
33
+ }, []);
34
+ React.useLayoutEffect(() => {
35
+ const container = ref.current;
36
+ let scrollingContainer;
37
+ let tabs;
38
+
39
+ const handleConditionalButtonsChange = () => {
40
+ const buttons = calculateConditionalButtons(scrollingContainer, isTouchDevice());
41
+ setConditionalButtons(buttons);
42
+ };
43
+
44
+ const observerCallback = mutationList => {
45
+ const tablist = getTabList(ref);
46
+
47
+ for (const mutation of mutationList) {
48
+ if (mutation.type === 'childList') {
49
+ const addedNodes = Array.from(mutation.addedNodes);
50
+ const found = addedNodes.find(node => node.getAttribute('role') === 'tab');
51
+
52
+ if (found && tablist) {
53
+ ghost.remove();
54
+ const tabs = Array.from(tablist.children);
55
+ tabs.forEach(tab => {
56
+ tab.removeEventListener('click', onTabClick);
57
+ tab.addEventListener('click', onTabClick);
58
+ });
59
+ tablist.appendChild(ghost);
60
+ handleConditionalButtonsChange();
61
+ }
62
+ }
63
+ }
64
+ };
65
+
66
+ const observer = new MutationObserver(observerCallback);
67
+
68
+ if (container) {
69
+ const tablist = getTabList(ref);
70
+ scrollingContainer = tablist === null || tablist === void 0 ? void 0 : tablist.parentElement;
71
+ observer.observe(container, {
72
+ attributes: false,
73
+ childList: true,
74
+ subtree: true
75
+ });
76
+
77
+ if (scrollingContainer instanceof HTMLElement && tablist) {
78
+ tablist.appendChild(ghost);
79
+ tabs = Array.from(tablist.children);
80
+ tabs.forEach(tab => tab.addEventListener('click', onTabClick));
81
+ handleConditionalButtonsChange();
82
+ scrollingContainer.addEventListener('scroll', handleConditionalButtonsChange);
83
+ }
84
+
85
+ return () => {
86
+ if (scrollingContainer) {
87
+ scrollingContainer.removeEventListener('scroll', handleConditionalButtonsChange);
88
+ }
89
+
90
+ if (tabs.length) {
91
+ tabs.forEach(tab => tab.removeEventListener('click', onTabClick));
92
+ }
93
+ };
94
+ }
95
+ }, [onTabClick, ghost, ref]);
96
+ const buttonProps = {
97
+ appearance: 'subtle',
98
+ spacing: 'none'
99
+ };
100
+ return jsx("div", {
101
+ css: containerStyles,
102
+ ref: ref,
103
+ "data-testid": "scrolling-tabs"
104
+ }, conditionalButtons.back && jsx("div", {
105
+ className: "back",
106
+ css: backButtonStyles
107
+ }, jsx(Button, _extends({
108
+ "data-test-id": "back"
109
+ }, buttonProps, {
110
+ iconBefore: jsx(ChevronLeftIcon, {
111
+ label: "back"
112
+ }),
113
+ onClick: () => scrollBack(ref)
114
+ }))), jsx("div", {
115
+ css: scrollingContainerStyles
116
+ }, props.children), conditionalButtons.forward && jsx("div", {
117
+ css: nextButtonStyles
118
+ }, jsx(Button, _extends({
119
+ "data-test-id": "forward"
120
+ }, buttonProps, {
121
+ iconBefore: jsx(ChevronRightIcon, {
122
+ label: "forward"
123
+ }),
124
+ onClick: () => scrollForward(ref)
125
+ }))));
126
+ };
127
+ export default ScrollingTabList;