UNPKG

21.1 kBJavaScriptView Raw
1"use strict";
2
3function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
4
5Object.defineProperty(exports, "__esModule", {
6 value: true
7});
8exports.Tabs = Tabs;
9
10var React = _interopRequireWildcard(require("react"));
11
12var _reactUid = require("react-uid");
13
14var _index = require("../styles/index.js");
15
16var _overrides = require("../helpers/overrides.js");
17
18var _focusVisible = require("../utils/focusVisible.js");
19
20var _constants = require("./constants.js");
21
22var _styledComponents = require("./styled-components.js");
23
24var _utils = require("./utils.js");
25
26function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
27
28function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (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; }
29
30function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
31
32function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
33
34function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
35
36function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
37
38function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
39
40function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
41
42function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
43
44function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
45
46function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
47
48function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
49
50function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
51
52function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
53
54function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
55
56function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
57
58var KEYBOARD_ACTION = {
59 next: 'next',
60 previous: 'previous'
61};
62
63var getLayoutParams = function getLayoutParams(el, orientation) {
64 if (!el) {
65 return {
66 length: 0,
67 distance: 0
68 };
69 } // Note, we are using clientHeight/Width here, which excludes borders.
70 // This means borders won't be taken into account if someone adds borders
71 // through overrides. In that case you would use getBoundingClientRect
72 // which includes borders, but because it returns a fractional value the
73 // highlight is slightly misaligned every so often.
74
75
76 if ((0, _utils.isVertical)(orientation)) {
77 return {
78 length: el.clientHeight,
79 distance: el.offsetTop
80 };
81 } else {
82 return {
83 length: el.clientWidth,
84 distance: el.offsetLeft
85 };
86 }
87};
88
89function Tabs(_ref) {
90 var _ref$activeKey = _ref.activeKey,
91 activeKey = _ref$activeKey === void 0 ? '0' : _ref$activeKey,
92 _ref$disabled = _ref.disabled,
93 disabled = _ref$disabled === void 0 ? false : _ref$disabled,
94 children = _ref.children,
95 _ref$fill = _ref.fill,
96 fill = _ref$fill === void 0 ? _constants.FILL.intrinsic : _ref$fill,
97 _ref$activateOnFocus = _ref.activateOnFocus,
98 activateOnFocus = _ref$activateOnFocus === void 0 ? true : _ref$activateOnFocus,
99 onChange = _ref.onChange,
100 _ref$orientation = _ref.orientation,
101 orientation = _ref$orientation === void 0 ? _constants.ORIENTATION.horizontal : _ref$orientation,
102 _ref$overrides = _ref.overrides,
103 overrides = _ref$overrides === void 0 ? {} : _ref$overrides,
104 _ref$renderAll = _ref.renderAll,
105 renderAll = _ref$renderAll === void 0 ? false : _ref$renderAll;
106 // Create unique id prefix for this tabs component
107 var uid = (0, _reactUid.useUID)(); // Unpack overrides
108
109 var RootOverrides = overrides.Root,
110 TabListOverrides = overrides.TabList,
111 TabHighlightOverrides = overrides.TabHighlight,
112 TabBorderOverrides = overrides.TabBorder;
113
114 var _getOverrides = (0, _overrides.getOverrides)(RootOverrides, _styledComponents.StyledRoot),
115 _getOverrides2 = _slicedToArray(_getOverrides, 2),
116 Root = _getOverrides2[0],
117 RootProps = _getOverrides2[1];
118
119 var _getOverrides3 = (0, _overrides.getOverrides)(TabListOverrides, _styledComponents.StyledTabList),
120 _getOverrides4 = _slicedToArray(_getOverrides3, 2),
121 TabList = _getOverrides4[0],
122 TabListProps = _getOverrides4[1];
123
124 var _getOverrides5 = (0, _overrides.getOverrides)(TabHighlightOverrides, _styledComponents.StyledTabHighlight),
125 _getOverrides6 = _slicedToArray(_getOverrides5, 2),
126 TabHighlight = _getOverrides6[0],
127 TabHighlightProps = _getOverrides6[1];
128
129 var _getOverrides7 = (0, _overrides.getOverrides)(TabBorderOverrides, _styledComponents.StyledTabBorder),
130 _getOverrides8 = _slicedToArray(_getOverrides7, 2),
131 TabBorder = _getOverrides8[0],
132 TabBorderProps = _getOverrides8[1]; // Count key updates
133 // We disable a few things until after first mount:
134 // - the highlight animation, avoiding an initial slide-in
135 // - smooth scrolling active tab into view
136
137
138 var _React$useState = React.useState(0),
139 _React$useState2 = _slicedToArray(_React$useState, 2),
140 keyUpdated = _React$useState2[0],
141 setKeyUpdated = _React$useState2[1];
142
143 React.useEffect(function () {
144 setKeyUpdated(keyUpdated + 1);
145 }, [activeKey]); // Positioning the highlight.
146
147 var activeTabRef = React.useRef();
148
149 var _React$useState3 = React.useState({
150 length: 0,
151 distance: 0
152 }),
153 _React$useState4 = _slicedToArray(_React$useState3, 2),
154 highlightLayout = _React$useState4[0],
155 setHighlightLayout = _React$useState4[1]; // Create a shared, memoized callback for tabs to call on resize.
156
157
158 var updateHighlight = React.useCallback(function () {
159 if (activeTabRef.current) {
160 setHighlightLayout(getLayoutParams(activeTabRef.current, orientation));
161 }
162 }, [activeTabRef.current, orientation]); // Update highlight on key and orientation changes.
163
164 React.useEffect(updateHighlight, [activeTabRef.current, orientation]); // Scroll active tab into view when the parent has scrollbar on mount and
165 // on key change (smooth scroll). Note, if the active key changes while
166 // the tab is not in view, the page will scroll it into view.
167 // TODO: replace with custom scrolling logic.
168
169 React.useEffect(function () {
170 // Flow needs this condition pulled out.
171 if (activeTabRef.current) {
172 if ((0, _utils.isHorizontal)(orientation) ? activeTabRef.current.parentNode.scrollWidth > activeTabRef.current.parentNode.clientWidth : activeTabRef.current.parentNode.scrollHeight > activeTabRef.current.parentNode.clientHeight) {
173 if (keyUpdated > 1) {
174 activeTabRef.current.scrollIntoView({
175 behavior: 'smooth',
176 block: 'nearest',
177 inline: 'nearest'
178 });
179 } else {
180 activeTabRef.current.scrollIntoView({
181 block: 'center',
182 inline: 'center'
183 });
184 }
185 }
186 }
187 }, [activeTabRef.current]); // Collect shared styling props
188
189 var sharedStylingProps = {
190 $orientation: orientation,
191 $fill: fill
192 }; // Helper for parsing directional keys
193 // TODO(WPT-6473): move to universal keycode aliases
194
195 var _useStyletron = (0, _index.useStyletron)(),
196 _useStyletron2 = _slicedToArray(_useStyletron, 2),
197 theme = _useStyletron2[1];
198
199 var parseKeyDown = React.useCallback(function (event) {
200 if ((0, _utils.isHorizontal)(orientation)) {
201 if ((0, _utils.isRTL)(theme.direction)) {
202 switch (event.keyCode) {
203 case 39:
204 return KEYBOARD_ACTION.previous;
205
206 case 37:
207 return KEYBOARD_ACTION.next;
208
209 default:
210 return null;
211 }
212 } else {
213 switch (event.keyCode) {
214 case 37:
215 return KEYBOARD_ACTION.previous;
216
217 case 39:
218 return KEYBOARD_ACTION.next;
219
220 default:
221 return null;
222 }
223 }
224 } else {
225 switch (event.keyCode) {
226 case 38:
227 return KEYBOARD_ACTION.previous;
228
229 case 40:
230 return KEYBOARD_ACTION.next;
231
232 default:
233 return null;
234 }
235 }
236 }, [orientation, theme.direction]);
237 return React.createElement(Root, _extends({}, sharedStylingProps, RootProps), React.createElement(TabList, _extends({
238 "data-baseweb": "tab-list",
239 role: "tablist",
240 "aria-orientation": orientation
241 }, sharedStylingProps, TabListProps), React.Children.map(children, function (child, index) {
242 if (!child) return;
243 return React.createElement(InternalTab, _extends({
244 childKey: child.key,
245 childIndex: index,
246 activeKey: activeKey,
247 orientation: orientation,
248 activeTabRef: activeTabRef,
249 updateHighlight: updateHighlight,
250 parseKeyDown: parseKeyDown,
251 activateOnFocus: activateOnFocus,
252 uid: uid,
253 disabled: disabled,
254 sharedStylingProps: sharedStylingProps,
255 onChange: onChange
256 }, child.props));
257 }), React.createElement(TabHighlight, _extends({
258 "data-baseweb": "tab-highlight",
259 $length: highlightLayout.length,
260 $distance: highlightLayout.distance // This avoids the tab sliding in from the side on mount
261 ,
262 $animate: keyUpdated > 1,
263 "aria-hidden": "true",
264 role: "presentation"
265 }, sharedStylingProps, TabHighlightProps))), React.createElement(TabBorder, _extends({
266 "data-baseweb": "tab-border",
267 "aria-hidden": "true",
268 role: "presentation"
269 }, sharedStylingProps, TabBorderProps)), React.Children.map(children, function (child, index) {
270 if (!child) return;
271 return React.createElement(InternalTabPanel, _extends({
272 childKey: child.key,
273 childIndex: index,
274 activeKey: activeKey,
275 uid: uid,
276 sharedStylingProps: sharedStylingProps,
277 renderAll: renderAll
278 }, child.props));
279 }));
280}
281
282function InternalTab(_ref2) {
283 var childKey = _ref2.childKey,
284 childIndex = _ref2.childIndex,
285 activeKey = _ref2.activeKey,
286 orientation = _ref2.orientation,
287 activeTabRef = _ref2.activeTabRef,
288 updateHighlight = _ref2.updateHighlight,
289 parseKeyDown = _ref2.parseKeyDown,
290 activateOnFocus = _ref2.activateOnFocus,
291 uid = _ref2.uid,
292 disabled = _ref2.disabled,
293 sharedStylingProps = _ref2.sharedStylingProps,
294 onChange = _ref2.onChange,
295 props = _objectWithoutProperties(_ref2, ["childKey", "childIndex", "activeKey", "orientation", "activeTabRef", "updateHighlight", "parseKeyDown", "activateOnFocus", "uid", "disabled", "sharedStylingProps", "onChange"]);
296
297 var key = childKey || String(childIndex);
298 var isActive = key == activeKey;
299
300 var Artwork = props.artwork,
301 _props$overrides = props.overrides,
302 overrides = _props$overrides === void 0 ? {} : _props$overrides,
303 tabRef = props.tabRef,
304 _onClick = props.onClick,
305 title = props.title,
306 restProps = _objectWithoutProperties(props, ["artwork", "overrides", "tabRef", "onClick", "title"]); // A way to share our internal activeTabRef via the "tabRef" prop.
307
308
309 var ref = React.useRef();
310 React.useImperativeHandle(tabRef, function () {
311 return isActive ? activeTabRef.current : ref.current;
312 }); // Track tab dimensions in a ref after each render
313 // This is used to compare params when the resize observer fires
314
315 var tabLayoutParams = React.useRef({
316 length: 0,
317 distance: 0
318 });
319 React.useEffect(function () {
320 tabLayoutParams.current = getLayoutParams(isActive ? activeTabRef.current : ref.current, orientation);
321 }); // We need to potentially update the active tab highlight when the width or
322 // placement changes for a tab so we listen for resize updates in each tab.
323
324 React.useEffect(function () {
325 if (window.ResizeObserver) {
326 var observer = new window.ResizeObserver(function (entries) {
327 if (entries[0] && entries[0].target) {
328 var tabLayoutParamsAfterResize = getLayoutParams(entries[0].target, orientation);
329
330 if (tabLayoutParamsAfterResize.length !== tabLayoutParams.current.length || tabLayoutParamsAfterResize.distance !== tabLayoutParams.current.distance) {
331 updateHighlight();
332 }
333 }
334 });
335 observer.observe(isActive ? activeTabRef.current : ref.current);
336 return function () {
337 observer.disconnect();
338 };
339 }
340 }, [activeKey, orientation]); // Collect overrides
341
342 var TabOverrides = overrides.Tab,
343 ArtworkContainerOverrides = overrides.ArtworkContainer;
344
345 var _getOverrides9 = (0, _overrides.getOverrides)(TabOverrides, _styledComponents.StyledTab),
346 _getOverrides10 = _slicedToArray(_getOverrides9, 2),
347 Tab = _getOverrides10[0],
348 TabProps = _getOverrides10[1];
349
350 var _getOverrides11 = (0, _overrides.getOverrides)(ArtworkContainerOverrides, _styledComponents.StyledArtworkContainer),
351 _getOverrides12 = _slicedToArray(_getOverrides11, 2),
352 ArtworkContainer = _getOverrides12[0],
353 ArtworkContainerProps = _getOverrides12[1]; // Keyboard focus styling
354
355
356 var _React$useState5 = React.useState(false),
357 _React$useState6 = _slicedToArray(_React$useState5, 2),
358 focusVisible = _React$useState6[0],
359 setFocusVisible = _React$useState6[1];
360
361 var handleFocus = React.useCallback(function (event) {
362 if ((0, _focusVisible.isFocusVisible)(event)) {
363 setFocusVisible(true);
364 }
365 }, []);
366 var handleBlur = React.useCallback(function (event) {
367 if (focusVisible !== false) {
368 setFocusVisible(false);
369 }
370 }, [focusVisible]); // Keyboard focus management
371
372 var handleKeyDown = React.useCallback(function (event) {
373 // WAI-ARIA 1.1
374 // https://www.w3.org/TR/wai-aria-practices-1.1/#tabpanel
375 // We use directional keys to iterate focus through Tabs.
376 // Find all tabs eligible for focus
377 var availableTabs = _toConsumableArray(event.target.parentNode.childNodes).filter(function (node) {
378 return !node.disabled && node.getAttribute('role') === 'tab';
379 }); // Exit early if there are no other tabs available
380
381
382 if (availableTabs.length === 1) return; // Find tab to focus, looping to start/end of list if necessary
383
384 var currentTabIndex = availableTabs.indexOf(event.target);
385 var action = parseKeyDown(event);
386
387 if (action) {
388 var nextTab;
389
390 if (action === KEYBOARD_ACTION.previous) {
391 if (availableTabs[currentTabIndex - 1]) {
392 nextTab = availableTabs[currentTabIndex - 1];
393 } else {
394 nextTab = availableTabs[availableTabs.length - 1];
395 }
396 } else if (action === KEYBOARD_ACTION.next) {
397 if (availableTabs[currentTabIndex + 1]) {
398 nextTab = availableTabs[currentTabIndex + 1];
399 } else {
400 nextTab = availableTabs[0];
401 }
402 }
403
404 if (nextTab) {
405 // Focus the tab
406 nextTab.focus(); // Optionally activate the tab
407
408 if (activateOnFocus) {
409 nextTab.click();
410 }
411 } // Prevent default page scroll when in vertical orientation
412
413
414 if ((0, _utils.isVertical)(orientation)) {
415 event.preventDefault();
416 }
417 }
418 });
419 return React.createElement(Tab, _extends({
420 "data-baseweb": "tab",
421 key: key,
422 id: (0, _utils.getTabId)(uid, key),
423 role: "tab",
424 onKeyDown: handleKeyDown,
425 "aria-selected": isActive,
426 "aria-controls": (0, _utils.getTabPanelId)(uid, key),
427 tabIndex: isActive ? '0' : '-1',
428 ref: isActive ? activeTabRef : ref,
429 disabled: !isActive && disabled,
430 type: "button" // so it doesn't trigger a submit when used inside forms
431 ,
432 $focusVisible: focusVisible,
433 $isActive: isActive
434 }, sharedStylingProps, restProps, TabProps, {
435 onClick: function onClick(event) {
436 if (typeof onChange === 'function') onChange({
437 activeKey: key
438 });
439 if (typeof _onClick === 'function') _onClick(event);
440 },
441 onFocus: (0, _focusVisible.forkFocus)(_objectSpread({}, restProps, {}, TabProps), handleFocus),
442 onBlur: (0, _focusVisible.forkBlur)(_objectSpread({}, restProps, {}, TabProps), handleBlur)
443 }), Artwork ? React.createElement(ArtworkContainer, _extends({
444 "data-baseweb": "artwork-container"
445 }, sharedStylingProps, ArtworkContainerProps), React.createElement(Artwork, {
446 size: 20,
447 color: "contentPrimary"
448 })) : null, title ? title : key);
449}
450
451function InternalTabPanel(_ref3) {
452 var childKey = _ref3.childKey,
453 childIndex = _ref3.childIndex,
454 activeKey = _ref3.activeKey,
455 uid = _ref3.uid,
456 sharedStylingProps = _ref3.sharedStylingProps,
457 renderAll = _ref3.renderAll,
458 props = _objectWithoutProperties(_ref3, ["childKey", "childIndex", "activeKey", "uid", "sharedStylingProps", "renderAll"]);
459
460 var key = childKey || String(childIndex);
461 var isActive = key == activeKey;
462 var _props$overrides2 = props.overrides,
463 overrides = _props$overrides2 === void 0 ? {} : _props$overrides2,
464 children = props.children;
465 var TabPanelOverrides = overrides.TabPanel;
466
467 var _getOverrides13 = (0, _overrides.getOverrides)(TabPanelOverrides, _styledComponents.StyledTabPanel),
468 _getOverrides14 = _slicedToArray(_getOverrides13, 2),
469 TabPanel = _getOverrides14[0],
470 TabPanelProps = _getOverrides14[1];
471
472 return React.createElement(TabPanel, _extends({
473 "data-baseweb": "tab-panel",
474 key: key,
475 role: "tabpanel",
476 id: (0, _utils.getTabPanelId)(uid, key),
477 "aria-labelledby": (0, _utils.getTabId)(uid, key),
478 "aria-expanded": isActive,
479 hidden: !isActive
480 }, sharedStylingProps, TabPanelProps), isActive || renderAll ? children : null);
481}
\No newline at end of file