@atlaskit/link-picker 1.11.2 → 1.12.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.
- package/CHANGELOG.md +6 -0
- package/dist/cjs/ui/link-picker/index.js +11 -7
- package/dist/cjs/ui/scrolling-tabs/index.js +192 -0
- package/dist/cjs/ui/scrolling-tabs/scrolling-tabs.js +123 -0
- package/dist/cjs/ui/scrolling-tabs/styles.js +64 -0
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/ui/link-picker/index.js +8 -5
- package/dist/es2019/ui/scrolling-tabs/index.js +127 -0
- package/dist/es2019/ui/scrolling-tabs/scrolling-tabs.js +90 -0
- package/dist/es2019/ui/scrolling-tabs/styles.js +49 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/ui/link-picker/index.js +10 -7
- package/dist/esm/ui/scrolling-tabs/index.js +165 -0
- package/dist/esm/ui/scrolling-tabs/scrolling-tabs.js +96 -0
- package/dist/esm/ui/scrolling-tabs/styles.js +49 -0
- package/dist/esm/version.json +1 -1
- package/dist/types/ui/link-picker/index.d.ts +1 -0
- package/dist/types/ui/scrolling-tabs/index.d.ts +7 -0
- package/dist/types/ui/scrolling-tabs/scrolling-tabs.d.ts +11 -0
- package/dist/types/ui/scrolling-tabs/styles.d.ts +6 -0
- package/dist/types-ts4.0/ui/link-picker/index.d.ts +1 -0
- package/dist/types-ts4.0/ui/scrolling-tabs/index.d.ts +7 -0
- package/dist/types-ts4.0/ui/scrolling-tabs/scrolling-tabs.d.ts +11 -0
- package/dist/types-ts4.0/ui/scrolling-tabs/styles.d.ts +6 -0
- package/package.json +6 -6
- package/report.api.md +2 -0
- package/tmp/api-report-tmp.d.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -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)(
|
|
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;
|
package/dist/cjs/version.json
CHANGED
|
@@ -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(
|
|
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;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const BUTTON_SIZE = 24;
|
|
2
|
+
const GUTTER = 48;
|
|
3
|
+
const GHOST_WIDTH = 342 - GUTTER - 40;
|
|
4
|
+
export const scrollBack = ref => {
|
|
5
|
+
moveTabsScroll(ref, findPrevious);
|
|
6
|
+
};
|
|
7
|
+
export const scrollForward = ref => {
|
|
8
|
+
moveTabsScroll(ref, findNext);
|
|
9
|
+
};
|
|
10
|
+
export const calculateConditionalButtons = (container, hideButtons) => {
|
|
11
|
+
const initialState = {
|
|
12
|
+
back: false,
|
|
13
|
+
forward: false
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
if (!container || hideButtons) {
|
|
17
|
+
return initialState;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const maxScrollValue = container.scrollWidth - container.clientWidth;
|
|
21
|
+
return {
|
|
22
|
+
back: container.scrollLeft > 0,
|
|
23
|
+
forward: container.scrollLeft < maxScrollValue - GHOST_WIDTH
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export const createGhost = () => {
|
|
27
|
+
const ghost = document.createElement('div');
|
|
28
|
+
ghost.setAttribute('id', 'scrolling-tabs-ghost');
|
|
29
|
+
ghost.style.minWidth = `${GHOST_WIDTH}px`;
|
|
30
|
+
ghost.style.pointerEvents = 'none';
|
|
31
|
+
return ghost;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const moveTabsScroll = (ref, findFn) => {
|
|
35
|
+
const container = getContainer(ref);
|
|
36
|
+
|
|
37
|
+
if (container) {
|
|
38
|
+
const tablist = container.children[0];
|
|
39
|
+
const tabs = [...tablist.children];
|
|
40
|
+
|
|
41
|
+
if (tabs.length > 0) {
|
|
42
|
+
const target = findFn(container, tabs);
|
|
43
|
+
|
|
44
|
+
if (target) {
|
|
45
|
+
const left = container.scrollLeft + target.getBoundingClientRect().left - container.getBoundingClientRect().left - BUTTON_SIZE;
|
|
46
|
+
scrollTo(container, left);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const scrollTo = (container, left) => {
|
|
53
|
+
container.scrollTo({
|
|
54
|
+
left
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const getTabList = ref => {
|
|
59
|
+
var _ref$current;
|
|
60
|
+
|
|
61
|
+
return (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.querySelector('[role="tablist"]');
|
|
62
|
+
};
|
|
63
|
+
export const getContainer = ref => {
|
|
64
|
+
var _getTabList;
|
|
65
|
+
|
|
66
|
+
return (_getTabList = getTabList(ref)) === null || _getTabList === void 0 ? void 0 : _getTabList.parentElement;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const findPrevious = (container, tabs) => {
|
|
70
|
+
const previousTabs = tabs.filter(tab => {
|
|
71
|
+
const [containerRect, tabRect] = [container.getBoundingClientRect(), tab.getBoundingClientRect()];
|
|
72
|
+
const [containerArea, tabArea] = [containerRect.left + container.scrollLeft - GUTTER, container.scrollLeft + tabRect.left + tabRect.width];
|
|
73
|
+
return containerArea > tabArea;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (previousTabs.length < 3) {
|
|
77
|
+
return previousTabs[0];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return previousTabs[previousTabs.length - 1];
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const findNext = (container, tabs) => {
|
|
84
|
+
const containerRect = container.getBoundingClientRect();
|
|
85
|
+
return tabs.find(tab => {
|
|
86
|
+
const tabRect = tab.getBoundingClientRect();
|
|
87
|
+
const [containerArea, tabArea] = [container.clientWidth + containerRect.left - GUTTER, tabRect.left + tabRect.width];
|
|
88
|
+
return containerArea < tabArea;
|
|
89
|
+
});
|
|
90
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { css } from '@emotion/react';
|
|
2
|
+
import { token } from '@atlaskit/tokens';
|
|
3
|
+
import { N0, N30 } from '@atlaskit/theme/colors';
|
|
4
|
+
export const scrollingContainerStyles = css({
|
|
5
|
+
overflowX: 'auto',
|
|
6
|
+
scrollBehavior: 'smooth',
|
|
7
|
+
scrollPadding: '0 24px',
|
|
8
|
+
scrollbarWidth: 'none',
|
|
9
|
+
'&::-webkit-scrollbar': {
|
|
10
|
+
display: 'none'
|
|
11
|
+
},
|
|
12
|
+
'&::before': {
|
|
13
|
+
content: '""',
|
|
14
|
+
borderRadius: 2,
|
|
15
|
+
bottom: 0,
|
|
16
|
+
margin: 0,
|
|
17
|
+
position: 'absolute',
|
|
18
|
+
width: 'inherit',
|
|
19
|
+
left: 8,
|
|
20
|
+
right: 8,
|
|
21
|
+
height: 2,
|
|
22
|
+
backgroundColor: token('color.border', N30)
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
export const containerStyles = css({
|
|
26
|
+
position: 'relative',
|
|
27
|
+
'[role="tablist"]': {
|
|
28
|
+
'&::before': {
|
|
29
|
+
display: 'none'
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
// Overrides Atlaskit tabs styles to stop overflowing with ellipsis
|
|
33
|
+
'[role="tab"]': {
|
|
34
|
+
overflow: 'unset !important',
|
|
35
|
+
textOverflow: 'unset !important'
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
export const buttonContainerStyles = css({
|
|
39
|
+
position: 'absolute',
|
|
40
|
+
top: 4,
|
|
41
|
+
zIndex: 999,
|
|
42
|
+
backgroundColor: token('elevation.surface', N0)
|
|
43
|
+
});
|
|
44
|
+
export const backButtonStyles = css([buttonContainerStyles, {
|
|
45
|
+
left: 0
|
|
46
|
+
}]);
|
|
47
|
+
export const nextButtonStyles = css([buttonContainerStyles, {
|
|
48
|
+
right: 0
|
|
49
|
+
}]);
|
package/dist/es2019/version.json
CHANGED
|
@@ -28,6 +28,7 @@ import TextInput, { testIds as textFieldTestIds } from './text-input';
|
|
|
28
28
|
import { rootContainerStyles, searchIconStyles, tabsWrapperStyles, flexColumnStyles, formFooterMargin } from './styled';
|
|
29
29
|
import { browser } from './browser';
|
|
30
30
|
import Announcer from './announcer';
|
|
31
|
+
import ScrollingTabList from '../scrolling-tabs';
|
|
31
32
|
import LinkSearchList, { testIds as listTestIds } from './link-search-list';
|
|
32
33
|
import LinkSearchError, { testIds as searchErrorTestIds } from './link-search-error';
|
|
33
34
|
import FormFooter, { testIds as formFooterTestIds } from './form-footer';
|
|
@@ -92,7 +93,8 @@ function LinkPicker(_ref) {
|
|
|
92
93
|
onContentResize = _ref.onContentResize,
|
|
93
94
|
plugins = _ref.plugins,
|
|
94
95
|
initUrl = _ref.url,
|
|
95
|
-
initDisplayText = _ref.displayText
|
|
96
|
+
initDisplayText = _ref.displayText,
|
|
97
|
+
featureFlags = _ref.featureFlags;
|
|
96
98
|
|
|
97
99
|
var _useAnalyticsEvents = useAnalyticsEvents(),
|
|
98
100
|
createAnalyticsEvent = _useAnalyticsEvents.createAnalyticsEvent;
|
|
@@ -303,6 +305,12 @@ function LinkPicker(_ref) {
|
|
|
303
305
|
size: "medium",
|
|
304
306
|
label: ''
|
|
305
307
|
}));
|
|
308
|
+
var tabList = jsx(TabList, null, tabs.map(function (tab) {
|
|
309
|
+
return jsx(Tab, {
|
|
310
|
+
key: tab.tabTitle,
|
|
311
|
+
testId: testIds.tabItem
|
|
312
|
+
}, tab.tabTitle);
|
|
313
|
+
}));
|
|
306
314
|
return jsx("form", {
|
|
307
315
|
"data-testid": testIds.linkPicker,
|
|
308
316
|
css: rootContainerStyles,
|
|
@@ -353,12 +361,7 @@ function LinkPicker(_ref) {
|
|
|
353
361
|
testId: testIds.tabList,
|
|
354
362
|
selected: activeTab,
|
|
355
363
|
onChange: handleTabChange
|
|
356
|
-
}, jsx(
|
|
357
|
-
return jsx(Tab, {
|
|
358
|
-
key: tab.tabTitle,
|
|
359
|
-
testId: testIds.tabItem
|
|
360
|
-
}, tab.tabTitle);
|
|
361
|
-
}))), jsx(TrackTabViewed, {
|
|
364
|
+
}, featureFlags !== null && featureFlags !== void 0 && featureFlags.scrollingTabs ? jsx(ScrollingTabList, null, tabList) : tabList), jsx(TrackTabViewed, {
|
|
362
365
|
activeTab: activeTab
|
|
363
366
|
})), jsx("div", _extends({
|
|
364
367
|
css: flexColumnStyles
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
3
|
+
|
|
4
|
+
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; } } }; }
|
|
5
|
+
|
|
6
|
+
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); }
|
|
7
|
+
|
|
8
|
+
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; }
|
|
9
|
+
|
|
10
|
+
/** @jsx jsx */
|
|
11
|
+
import React, { useRef, useMemo, useState, useCallback } from 'react';
|
|
12
|
+
import { jsx } from '@emotion/react';
|
|
13
|
+
import Button from '@atlaskit/button';
|
|
14
|
+
import ChevronLeftIcon from '@atlaskit/icon/glyph/chevron-left';
|
|
15
|
+
import ChevronRightIcon from '@atlaskit/icon/glyph/chevron-right';
|
|
16
|
+
import { scrollBack, scrollForward, calculateConditionalButtons, getTabList, createGhost } from './scrolling-tabs';
|
|
17
|
+
import { containerStyles, scrollingContainerStyles, nextButtonStyles, backButtonStyles } from './styles';
|
|
18
|
+
|
|
19
|
+
function isTouchDevice() {
|
|
20
|
+
return 'ontouchstart' in window || // eslint-disable-next-line compat/compat
|
|
21
|
+
navigator.maxTouchPoints > 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var initialConditionalButtonsState = {
|
|
25
|
+
back: false,
|
|
26
|
+
forward: false
|
|
27
|
+
};
|
|
28
|
+
export var ScrollingTabList = function ScrollingTabList(props) {
|
|
29
|
+
var ref = useRef(null);
|
|
30
|
+
|
|
31
|
+
var _useState = useState(initialConditionalButtonsState),
|
|
32
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
33
|
+
conditionalButtons = _useState2[0],
|
|
34
|
+
setConditionalButtons = _useState2[1];
|
|
35
|
+
|
|
36
|
+
var ghost = useMemo(function () {
|
|
37
|
+
return createGhost();
|
|
38
|
+
}, []);
|
|
39
|
+
var onTabClick = useCallback(function (e) {
|
|
40
|
+
var target = e.currentTarget;
|
|
41
|
+
|
|
42
|
+
if (target) {
|
|
43
|
+
target.scrollIntoView({
|
|
44
|
+
behavior: 'smooth'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}, []);
|
|
48
|
+
React.useLayoutEffect(function () {
|
|
49
|
+
var container = ref.current;
|
|
50
|
+
var scrollingContainer;
|
|
51
|
+
var tabs;
|
|
52
|
+
|
|
53
|
+
var handleConditionalButtonsChange = function handleConditionalButtonsChange() {
|
|
54
|
+
var buttons = calculateConditionalButtons(scrollingContainer, isTouchDevice());
|
|
55
|
+
setConditionalButtons(buttons);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
var observerCallback = function observerCallback(mutationList) {
|
|
59
|
+
var tablist = getTabList(ref);
|
|
60
|
+
|
|
61
|
+
var _iterator = _createForOfIteratorHelper(mutationList),
|
|
62
|
+
_step;
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
66
|
+
var mutation = _step.value;
|
|
67
|
+
|
|
68
|
+
if (mutation.type === 'childList') {
|
|
69
|
+
var addedNodes = Array.from(mutation.addedNodes);
|
|
70
|
+
var found = addedNodes.find(function (node) {
|
|
71
|
+
return node.getAttribute('role') === 'tab';
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (found && tablist) {
|
|
75
|
+
ghost.remove();
|
|
76
|
+
|
|
77
|
+
var _tabs = Array.from(tablist.children);
|
|
78
|
+
|
|
79
|
+
_tabs.forEach(function (tab) {
|
|
80
|
+
tab.removeEventListener('click', onTabClick);
|
|
81
|
+
tab.addEventListener('click', onTabClick);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
tablist.appendChild(ghost);
|
|
85
|
+
handleConditionalButtonsChange();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
_iterator.e(err);
|
|
91
|
+
} finally {
|
|
92
|
+
_iterator.f();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
var observer = new MutationObserver(observerCallback);
|
|
97
|
+
|
|
98
|
+
if (container) {
|
|
99
|
+
var tablist = getTabList(ref);
|
|
100
|
+
scrollingContainer = tablist === null || tablist === void 0 ? void 0 : tablist.parentElement;
|
|
101
|
+
observer.observe(container, {
|
|
102
|
+
attributes: false,
|
|
103
|
+
childList: true,
|
|
104
|
+
subtree: true
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (scrollingContainer instanceof HTMLElement && tablist) {
|
|
108
|
+
tablist.appendChild(ghost);
|
|
109
|
+
tabs = Array.from(tablist.children);
|
|
110
|
+
tabs.forEach(function (tab) {
|
|
111
|
+
return tab.addEventListener('click', onTabClick);
|
|
112
|
+
});
|
|
113
|
+
handleConditionalButtonsChange();
|
|
114
|
+
scrollingContainer.addEventListener('scroll', handleConditionalButtonsChange);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return function () {
|
|
118
|
+
if (scrollingContainer) {
|
|
119
|
+
scrollingContainer.removeEventListener('scroll', handleConditionalButtonsChange);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (tabs.length) {
|
|
123
|
+
tabs.forEach(function (tab) {
|
|
124
|
+
return tab.removeEventListener('click', onTabClick);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}, [onTabClick, ghost, ref]);
|
|
130
|
+
var buttonProps = {
|
|
131
|
+
appearance: 'subtle',
|
|
132
|
+
spacing: 'none'
|
|
133
|
+
};
|
|
134
|
+
return jsx("div", {
|
|
135
|
+
css: containerStyles,
|
|
136
|
+
ref: ref,
|
|
137
|
+
"data-testid": "scrolling-tabs"
|
|
138
|
+
}, conditionalButtons.back && jsx("div", {
|
|
139
|
+
className: "back",
|
|
140
|
+
css: backButtonStyles
|
|
141
|
+
}, jsx(Button, _extends({
|
|
142
|
+
"data-test-id": "back"
|
|
143
|
+
}, buttonProps, {
|
|
144
|
+
iconBefore: jsx(ChevronLeftIcon, {
|
|
145
|
+
label: "back"
|
|
146
|
+
}),
|
|
147
|
+
onClick: function onClick() {
|
|
148
|
+
return scrollBack(ref);
|
|
149
|
+
}
|
|
150
|
+
}))), jsx("div", {
|
|
151
|
+
css: scrollingContainerStyles
|
|
152
|
+
}, props.children), conditionalButtons.forward && jsx("div", {
|
|
153
|
+
css: nextButtonStyles
|
|
154
|
+
}, jsx(Button, _extends({
|
|
155
|
+
"data-test-id": "forward"
|
|
156
|
+
}, buttonProps, {
|
|
157
|
+
iconBefore: jsx(ChevronRightIcon, {
|
|
158
|
+
label: "forward"
|
|
159
|
+
}),
|
|
160
|
+
onClick: function onClick() {
|
|
161
|
+
return scrollForward(ref);
|
|
162
|
+
}
|
|
163
|
+
}))));
|
|
164
|
+
};
|
|
165
|
+
export default ScrollingTabList;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
+
var BUTTON_SIZE = 24;
|
|
3
|
+
var GUTTER = 48;
|
|
4
|
+
var GHOST_WIDTH = 342 - GUTTER - 40;
|
|
5
|
+
export var scrollBack = function scrollBack(ref) {
|
|
6
|
+
moveTabsScroll(ref, findPrevious);
|
|
7
|
+
};
|
|
8
|
+
export var scrollForward = function scrollForward(ref) {
|
|
9
|
+
moveTabsScroll(ref, findNext);
|
|
10
|
+
};
|
|
11
|
+
export var calculateConditionalButtons = function calculateConditionalButtons(container, hideButtons) {
|
|
12
|
+
var initialState = {
|
|
13
|
+
back: false,
|
|
14
|
+
forward: false
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
if (!container || hideButtons) {
|
|
18
|
+
return initialState;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
var maxScrollValue = container.scrollWidth - container.clientWidth;
|
|
22
|
+
return {
|
|
23
|
+
back: container.scrollLeft > 0,
|
|
24
|
+
forward: container.scrollLeft < maxScrollValue - GHOST_WIDTH
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export var createGhost = function createGhost() {
|
|
28
|
+
var ghost = document.createElement('div');
|
|
29
|
+
ghost.setAttribute('id', 'scrolling-tabs-ghost');
|
|
30
|
+
ghost.style.minWidth = "".concat(GHOST_WIDTH, "px");
|
|
31
|
+
ghost.style.pointerEvents = 'none';
|
|
32
|
+
return ghost;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
var moveTabsScroll = function moveTabsScroll(ref, findFn) {
|
|
36
|
+
var container = getContainer(ref);
|
|
37
|
+
|
|
38
|
+
if (container) {
|
|
39
|
+
var tablist = container.children[0];
|
|
40
|
+
|
|
41
|
+
var _tabs = _toConsumableArray(tablist.children);
|
|
42
|
+
|
|
43
|
+
if (_tabs.length > 0) {
|
|
44
|
+
var target = findFn(container, _tabs);
|
|
45
|
+
|
|
46
|
+
if (target) {
|
|
47
|
+
var left = container.scrollLeft + target.getBoundingClientRect().left - container.getBoundingClientRect().left - BUTTON_SIZE;
|
|
48
|
+
scrollTo(container, left);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
var scrollTo = function scrollTo(container, left) {
|
|
55
|
+
container.scrollTo({
|
|
56
|
+
left: left
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export var getTabList = function getTabList(ref) {
|
|
61
|
+
var _ref$current;
|
|
62
|
+
|
|
63
|
+
return (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.querySelector('[role="tablist"]');
|
|
64
|
+
};
|
|
65
|
+
export var getContainer = function getContainer(ref) {
|
|
66
|
+
var _getTabList;
|
|
67
|
+
|
|
68
|
+
return (_getTabList = getTabList(ref)) === null || _getTabList === void 0 ? void 0 : _getTabList.parentElement;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
var findPrevious = function findPrevious(container, tabs) {
|
|
72
|
+
var previousTabs = tabs.filter(function (tab) {
|
|
73
|
+
var _ref = [container.getBoundingClientRect(), tab.getBoundingClientRect()],
|
|
74
|
+
containerRect = _ref[0],
|
|
75
|
+
tabRect = _ref[1];
|
|
76
|
+
var containerArea = containerRect.left + container.scrollLeft - GUTTER,
|
|
77
|
+
tabArea = container.scrollLeft + tabRect.left + tabRect.width;
|
|
78
|
+
return containerArea > tabArea;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (previousTabs.length < 3) {
|
|
82
|
+
return previousTabs[0];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return previousTabs[previousTabs.length - 1];
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
var findNext = function findNext(container, tabs) {
|
|
89
|
+
var containerRect = container.getBoundingClientRect();
|
|
90
|
+
return tabs.find(function (tab) {
|
|
91
|
+
var tabRect = tab.getBoundingClientRect();
|
|
92
|
+
var containerArea = container.clientWidth + containerRect.left - GUTTER,
|
|
93
|
+
tabArea = tabRect.left + tabRect.width;
|
|
94
|
+
return containerArea < tabArea;
|
|
95
|
+
});
|
|
96
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { css } from '@emotion/react';
|
|
2
|
+
import { token } from '@atlaskit/tokens';
|
|
3
|
+
import { N0, N30 } from '@atlaskit/theme/colors';
|
|
4
|
+
export var scrollingContainerStyles = css({
|
|
5
|
+
overflowX: 'auto',
|
|
6
|
+
scrollBehavior: 'smooth',
|
|
7
|
+
scrollPadding: '0 24px',
|
|
8
|
+
scrollbarWidth: 'none',
|
|
9
|
+
'&::-webkit-scrollbar': {
|
|
10
|
+
display: 'none'
|
|
11
|
+
},
|
|
12
|
+
'&::before': {
|
|
13
|
+
content: '""',
|
|
14
|
+
borderRadius: 2,
|
|
15
|
+
bottom: 0,
|
|
16
|
+
margin: 0,
|
|
17
|
+
position: 'absolute',
|
|
18
|
+
width: 'inherit',
|
|
19
|
+
left: 8,
|
|
20
|
+
right: 8,
|
|
21
|
+
height: 2,
|
|
22
|
+
backgroundColor: token('color.border', N30)
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
export var containerStyles = css({
|
|
26
|
+
position: 'relative',
|
|
27
|
+
'[role="tablist"]': {
|
|
28
|
+
'&::before': {
|
|
29
|
+
display: 'none'
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
// Overrides Atlaskit tabs styles to stop overflowing with ellipsis
|
|
33
|
+
'[role="tab"]': {
|
|
34
|
+
overflow: 'unset !important',
|
|
35
|
+
textOverflow: 'unset !important'
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
export var buttonContainerStyles = css({
|
|
39
|
+
position: 'absolute',
|
|
40
|
+
top: 4,
|
|
41
|
+
zIndex: 999,
|
|
42
|
+
backgroundColor: token('elevation.surface', N0)
|
|
43
|
+
});
|
|
44
|
+
export var backButtonStyles = css([buttonContainerStyles, {
|
|
45
|
+
left: 0
|
|
46
|
+
}]);
|
|
47
|
+
export var nextButtonStyles = css([buttonContainerStyles, {
|
|
48
|
+
right: 0
|
|
49
|
+
}]);
|
package/dist/esm/version.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export interface ConditionalButtons {
|
|
3
|
+
back: boolean;
|
|
4
|
+
forward: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare const scrollBack: (ref: RefObject<HTMLDivElement>) => void;
|
|
7
|
+
export declare const scrollForward: (ref: RefObject<HTMLDivElement>) => void;
|
|
8
|
+
export declare const calculateConditionalButtons: (container: HTMLElement | null | undefined, hideButtons: boolean) => ConditionalButtons;
|
|
9
|
+
export declare const createGhost: () => HTMLDivElement;
|
|
10
|
+
export declare const getTabList: (ref: RefObject<HTMLDivElement>) => Element | null | undefined;
|
|
11
|
+
export declare const getContainer: (ref: RefObject<HTMLDivElement>) => HTMLElement | null | undefined;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SerializedStyles } from '@emotion/react';
|
|
2
|
+
export declare const scrollingContainerStyles: SerializedStyles;
|
|
3
|
+
export declare const containerStyles: SerializedStyles;
|
|
4
|
+
export declare const buttonContainerStyles: SerializedStyles;
|
|
5
|
+
export declare const backButtonStyles: SerializedStyles;
|
|
6
|
+
export declare const nextButtonStyles: SerializedStyles;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export interface ConditionalButtons {
|
|
3
|
+
back: boolean;
|
|
4
|
+
forward: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare const scrollBack: (ref: RefObject<HTMLDivElement>) => void;
|
|
7
|
+
export declare const scrollForward: (ref: RefObject<HTMLDivElement>) => void;
|
|
8
|
+
export declare const calculateConditionalButtons: (container: HTMLElement | null | undefined, hideButtons: boolean) => ConditionalButtons;
|
|
9
|
+
export declare const createGhost: () => HTMLDivElement;
|
|
10
|
+
export declare const getTabList: (ref: RefObject<HTMLDivElement>) => Element | null | undefined;
|
|
11
|
+
export declare const getContainer: (ref: RefObject<HTMLDivElement>) => HTMLElement | null | undefined;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SerializedStyles } from '@emotion/react';
|
|
2
|
+
export declare const scrollingContainerStyles: SerializedStyles;
|
|
3
|
+
export declare const containerStyles: SerializedStyles;
|
|
4
|
+
export declare const buttonContainerStyles: SerializedStyles;
|
|
5
|
+
export declare const backButtonStyles: SerializedStyles;
|
|
6
|
+
export declare const nextButtonStyles: SerializedStyles;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/link-picker",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Standalone link picker",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"publishConfig": {
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@atlaskit/analytics-next": "^8.3.1",
|
|
43
|
-
"@atlaskit/button": "^16.
|
|
43
|
+
"@atlaskit/button": "^16.4.0",
|
|
44
44
|
"@atlaskit/empty-state": "^7.4.0",
|
|
45
|
-
"@atlaskit/form": "^8.
|
|
45
|
+
"@atlaskit/form": "^8.7.0",
|
|
46
46
|
"@atlaskit/frontend-utilities": "^2.0.0",
|
|
47
47
|
"@atlaskit/icon": "^21.11.0",
|
|
48
|
-
"@atlaskit/spinner": "^15.
|
|
48
|
+
"@atlaskit/spinner": "^15.2.0",
|
|
49
49
|
"@atlaskit/tabs": "^13.3.0",
|
|
50
50
|
"@atlaskit/textfield": "^5.3.0",
|
|
51
51
|
"@atlaskit/theme": "^12.2.0",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
"@atlaskit/visual-regression": "*",
|
|
81
81
|
"@atlaskit/webdriver-runner": "*",
|
|
82
82
|
"@atlassian/atlassian-frontend-prettier-config-1.0.0": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.0",
|
|
83
|
-
"@atlassian/link-picker-atlassian-plugin": "^
|
|
84
|
-
"@atlassian/link-picker-plugins": "^
|
|
83
|
+
"@atlassian/link-picker-atlassian-plugin": "^22.0.0",
|
|
84
|
+
"@atlassian/link-picker-plugins": "^10.0.0",
|
|
85
85
|
"@atlassian/recent-work-client": "^1.6.0",
|
|
86
86
|
"@atlassian/search-provider": "^2.3.0",
|
|
87
87
|
"@testing-library/dom": "^8.17.1",
|
package/report.api.md
CHANGED
package/tmp/api-report-tmp.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface LinkPickerProps {
|
|
|
38
38
|
children: React.ReactElement;
|
|
39
39
|
}>;
|
|
40
40
|
displayText?: null | string;
|
|
41
|
+
// (undocumented)
|
|
42
|
+
featureFlags?: Record<string, unknown>;
|
|
41
43
|
onCancel: () => void;
|
|
42
44
|
onContentResize?: () => void;
|
|
43
45
|
onSubmit: (arg: OnSubmitParameter, analytic?: UIAnalyticsEvent | null) => void;
|