@clayui/tabs 3.78.3 → 3.79.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/lib/Content.d.ts CHANGED
@@ -4,18 +4,27 @@
4
4
  */
5
5
  import React from 'react';
6
6
  export interface IProps extends React.HTMLAttributes<HTMLDivElement> {
7
+ /**
8
+ * @ignore
9
+ */
10
+ active?: React.Key;
7
11
  /**
8
12
  * Receives a number that indicates the `tabkey` to be rendered.
13
+ * @deprecated since v3.78.2 - No longer needed in new composition.
9
14
  */
10
- activeIndex: number;
15
+ activeIndex?: number;
11
16
  /**
12
17
  * Children elements received from ClayTabs.Content component.
13
18
  */
14
- children: Array<React.ReactElement>;
19
+ children: React.ReactNode;
15
20
  /**
16
21
  * Flag to indicate if `fade` classname that applies an fading animation should be applied.
17
22
  */
18
23
  fade?: boolean;
24
+ /**
25
+ * @ignore
26
+ */
27
+ tabsId?: string;
19
28
  }
20
- declare const Content: ({ activeIndex, children, className, fade, ...otherProps }: IProps) => JSX.Element;
29
+ declare const Content: ({ active, activeIndex, children, className, fade, tabsId, ...otherProps }: IProps) => JSX.Element;
21
30
  export default Content;
package/lib/Content.js CHANGED
@@ -9,7 +9,7 @@ var _classnames = _interopRequireDefault(require("classnames"));
9
9
 
10
10
  var _react = _interopRequireDefault(require("react"));
11
11
 
12
- var _excluded = ["activeIndex", "children", "className", "fade"];
12
+ var _excluded = ["active", "activeIndex", "children", "className", "fade", "tabsId"];
13
13
 
14
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
15
 
@@ -26,20 +26,28 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
26
26
  function _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; }
27
27
 
28
28
  var Content = function Content(_ref) {
29
- var _ref$activeIndex = _ref.activeIndex,
29
+ var active = _ref.active,
30
+ _ref$activeIndex = _ref.activeIndex,
30
31
  activeIndex = _ref$activeIndex === void 0 ? 0 : _ref$activeIndex,
31
32
  children = _ref.children,
32
33
  className = _ref.className,
33
34
  _ref$fade = _ref.fade,
34
35
  fade = _ref$fade === void 0 ? false : _ref$fade,
36
+ tabsId = _ref.tabsId,
35
37
  otherProps = _objectWithoutProperties(_ref, _excluded);
36
38
 
37
39
  return /*#__PURE__*/_react.default.createElement("div", _extends({
38
40
  className: (0, _classnames.default)('tab-content', className)
39
41
  }, otherProps), _react.default.Children.map(children, function (child, index) {
40
- return child && /*#__PURE__*/_react.default.cloneElement(child, _objectSpread(_objectSpread({}, child.props), {}, {
41
- active: activeIndex === index,
42
+ if (! /*#__PURE__*/_react.default.isValidElement(child)) {
43
+ return child;
44
+ }
45
+
46
+ return /*#__PURE__*/_react.default.cloneElement(child, _objectSpread(_objectSpread({}, child.props), {}, {
47
+ active: typeof active === 'number' ? active === index : activeIndex === index,
48
+ 'aria-labelledby': tabsId ? "".concat(tabsId, "-tab-").concat(index) : child.props['aria-labelledby'],
42
49
  fade: fade,
50
+ id: tabsId ? "".concat(tabsId, "-tabpanel-").concat(index) : child.props.id,
43
51
  key: index
44
52
  }));
45
53
  }));
package/lib/Item.d.ts CHANGED
File without changes
package/lib/List.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: © 2022 Liferay, Inc. <https://liferay.com>
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ */
5
+ import { InternalDispatch } from '@clayui/shared';
6
+ import React from 'react';
7
+ export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
8
+ /**
9
+ * @ignore
10
+ */
11
+ activation?: 'manual' | 'automatic';
12
+ /**
13
+ * @ignore
14
+ */
15
+ active?: React.Key;
16
+ /**
17
+ * The tabs content.
18
+ */
19
+ children: React.ReactNode;
20
+ /**
21
+ * The custom class.
22
+ */
23
+ className?: string;
24
+ /**
25
+ * @ignore
26
+ */
27
+ displayType?: null | 'basic' | 'underline';
28
+ /**
29
+ * @ignore
30
+ */
31
+ justified?: boolean;
32
+ /**
33
+ * @ignore
34
+ */
35
+ modern?: boolean;
36
+ /**
37
+ * @ignore
38
+ */
39
+ onActiveChange?: InternalDispatch<number>;
40
+ /**
41
+ * @ignore
42
+ */
43
+ tabsId?: string;
44
+ }
45
+ export declare function List({ activation, active, children, className, displayType, justified, modern, onActiveChange, tabsId, ...otherProps }: IProps): JSX.Element;
46
+ export declare namespace List {
47
+ var displayName: string;
48
+ }
package/lib/List.js ADDED
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+
3
+ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.List = List;
9
+
10
+ var _shared = require("@clayui/shared");
11
+
12
+ var _classnames = _interopRequireDefault(require("classnames"));
13
+
14
+ var _react = _interopRequireWildcard(require("react"));
15
+
16
+ var _excluded = ["activation", "active", "children", "className", "displayType", "justified", "modern", "onActiveChange", "tabsId"];
17
+
18
+ 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); }
19
+
20
+ 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; }
21
+
22
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
+
24
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : 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); }
25
+
26
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
27
+
28
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
29
+
30
+ function _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; }
31
+
32
+ function _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; }
33
+
34
+ function _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; }
35
+
36
+ function List(_ref) {
37
+ var activation = _ref.activation,
38
+ active = _ref.active,
39
+ children = _ref.children,
40
+ className = _ref.className,
41
+ displayType = _ref.displayType,
42
+ justified = _ref.justified,
43
+ modern = _ref.modern,
44
+ onActiveChange = _ref.onActiveChange,
45
+ tabsId = _ref.tabsId,
46
+ otherProps = _objectWithoutProperties(_ref, _excluded);
47
+
48
+ var tabsRef = (0, _react.useRef)(null);
49
+
50
+ var _useNavigation = (0, _shared.useNavigation)({
51
+ activation: activation,
52
+ containeRef: tabsRef,
53
+ orientation: 'horizontal'
54
+ }),
55
+ onKeyDown = _useNavigation.onKeyDown;
56
+
57
+ return /*#__PURE__*/_react.default.createElement("ul", _extends({}, otherProps, {
58
+ className: (0, _classnames.default)('nav', {
59
+ 'nav-justified': justified
60
+ }, !displayType ? {
61
+ 'nav-tabs': !modern,
62
+ 'nav-underline': modern
63
+ } : {
64
+ 'nav-tabs': displayType === 'basic',
65
+ 'nav-underline': displayType === 'underline'
66
+ }, className),
67
+ onKeyDown: onKeyDown,
68
+ ref: tabsRef,
69
+ role: "tablist"
70
+ }), _react.default.Children.map(children, function (child, index) {
71
+ var _child$props$innerPro;
72
+
73
+ if (! /*#__PURE__*/_react.default.isValidElement(child)) {
74
+ return child;
75
+ }
76
+
77
+ return /*#__PURE__*/_react.default.cloneElement(child, {
78
+ active: child.props.active !== undefined ? child.props.active : active === index,
79
+ innerProps: _objectSpread({
80
+ 'aria-controls': tabsId && "".concat(tabsId, "-tabpanel-").concat(index),
81
+ id: tabsId && "".concat(tabsId, "-tab-").concat(index)
82
+ }, (_child$props$innerPro = child.props.innerProps) !== null && _child$props$innerPro !== void 0 ? _child$props$innerPro : {}),
83
+ onClick: function onClick(event) {
84
+ var onClick = child.props.onClick;
85
+
86
+ if (onClick) {
87
+ onClick(event);
88
+ } else {
89
+ onActiveChange(index);
90
+ }
91
+ }
92
+ });
93
+ }));
94
+ }
95
+
96
+ List.displayName = 'ClayTabsList';
package/lib/TabPane.d.ts CHANGED
@@ -13,5 +13,5 @@ export interface ITabPaneProps extends React.HTMLAttributes<HTMLDivElement> {
13
13
  */
14
14
  fade?: boolean;
15
15
  }
16
- declare const TabPane: ({ active, children, className, fade, ...otherProps }: ITabPaneProps) => JSX.Element;
16
+ declare const TabPane: ({ active, children, className, fade, tabIndex, ...otherProps }: ITabPaneProps) => JSX.Element;
17
17
  export default TabPane;
package/lib/TabPane.js CHANGED
@@ -9,7 +9,7 @@ var _classnames = _interopRequireDefault(require("classnames"));
9
9
 
10
10
  var _react = _interopRequireDefault(require("react"));
11
11
 
12
- var _excluded = ["active", "children", "className", "fade"];
12
+ var _excluded = ["active", "children", "className", "fade", "tabIndex"];
13
13
 
14
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
15
 
@@ -44,6 +44,8 @@ var TabPane = function TabPane(_ref) {
44
44
  children = _ref.children,
45
45
  className = _ref.className,
46
46
  fade = _ref.fade,
47
+ _ref$tabIndex = _ref.tabIndex,
48
+ tabIndex = _ref$tabIndex === void 0 ? 0 : _ref$tabIndex,
47
49
  otherProps = _objectWithoutProperties(_ref, _excluded);
48
50
 
49
51
  var _React$useState = _react.default.useState(active),
@@ -86,7 +88,7 @@ var TabPane = function TabPane(_ref) {
86
88
  show: internalShow
87
89
  }, className),
88
90
  role: "tabpanel",
89
- tabIndex: 0
91
+ tabIndex: tabIndex
90
92
  }), children);
91
93
  };
92
94
 
package/lib/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import { InternalDispatch } from '@clayui/shared';
6
6
  import React from 'react';
7
7
  import Content from './Content';
8
8
  import Item from './Item';
9
+ import { List } from './List';
9
10
  import TabPane from './TabPane';
10
11
  export declare type DisplayType = null | 'basic' | 'underline';
11
12
  export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
@@ -29,6 +30,11 @@ export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
29
30
  * Determines how tab is displayed.
30
31
  */
31
32
  displayType?: DisplayType;
33
+ /**
34
+ * Flag to indicate if `fade` classname that applies an fading animation
35
+ * should be applied.
36
+ */
37
+ fade?: boolean;
32
38
  /**
33
39
  * Justify the nav items according the tab content.
34
40
  */
@@ -44,14 +50,18 @@ export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
44
50
  }
45
51
  declare function ClayTabs(props: IProps): JSX.Element & {
46
52
  Content: typeof Content;
53
+ Item: typeof Item;
54
+ List: typeof List;
55
+ Panels: typeof Content;
47
56
  TabPane: typeof TabPane;
48
57
  TabPanel: typeof TabPane;
49
- Item: typeof Item;
50
58
  };
51
59
  declare namespace ClayTabs {
52
- var Content: ({ activeIndex, children, className, fade, ...otherProps }: import("./Content").IProps) => JSX.Element;
53
- var TabPane: ({ active, children, className, fade, ...otherProps }: import("./TabPane").ITabPaneProps) => JSX.Element;
54
- var TabPanel: ({ active, children, className, fade, ...otherProps }: import("./TabPane").ITabPaneProps) => JSX.Element;
60
+ var Content: ({ active, activeIndex, children, className, fade, tabsId, ...otherProps }: import("./Content").IProps) => JSX.Element;
61
+ var Panels: ({ active, activeIndex, children, className, fade, tabsId, ...otherProps }: import("./Content").IProps) => JSX.Element;
55
62
  var Item: React.ForwardRefExoticComponent<import("./Item").IProps & React.RefAttributes<any>>;
63
+ var List: typeof import("./List").List;
64
+ var TabPane: ({ active, children, className, fade, tabIndex, ...otherProps }: import("./TabPane").ITabPaneProps) => JSX.Element;
65
+ var TabPanel: ({ active, children, className, fade, tabIndex, ...otherProps }: import("./TabPane").ITabPaneProps) => JSX.Element;
56
66
  }
57
67
  export default ClayTabs;
package/lib/index.js CHANGED
@@ -1,7 +1,5 @@
1
1
  "use strict";
2
2
 
3
- function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
4
-
5
3
  Object.defineProperty(exports, "__esModule", {
6
4
  value: true
7
5
  });
@@ -9,21 +7,17 @@ exports.default = void 0;
9
7
 
10
8
  var _shared = require("@clayui/shared");
11
9
 
12
- var _classnames = _interopRequireDefault(require("classnames"));
13
-
14
- var _react = _interopRequireWildcard(require("react"));
10
+ var _react = _interopRequireDefault(require("react"));
15
11
 
16
12
  var _Content = _interopRequireDefault(require("./Content"));
17
13
 
18
14
  var _Item = _interopRequireDefault(require("./Item"));
19
15
 
20
- var _TabPane = _interopRequireDefault(require("./TabPane"));
16
+ var _List = require("./List");
21
17
 
22
- var _excluded = ["activation", "active", "children", "className", "defaultActive", "displayType", "justified", "modern", "onActiveChange"];
23
-
24
- 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); }
18
+ var _TabPane = _interopRequireDefault(require("./TabPane"));
25
19
 
26
- 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; }
20
+ var _excluded = ["activation", "active", "children", "className", "defaultActive", "displayType", "fade", "justified", "modern", "onActiveChange"];
27
21
 
28
22
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
29
23
 
@@ -46,6 +40,8 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
46
40
  function _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
41
 
48
42
  function ClayTabs(_ref) {
43
+ var _left$type;
44
+
49
45
  var _ref$activation = _ref.activation,
50
46
  activation = _ref$activation === void 0 ? 'manual' : _ref$activation,
51
47
  externalActive = _ref.active,
@@ -54,14 +50,14 @@ function ClayTabs(_ref) {
54
50
  _ref$defaultActive = _ref.defaultActive,
55
51
  defaultActive = _ref$defaultActive === void 0 ? 0 : _ref$defaultActive,
56
52
  displayType = _ref.displayType,
53
+ _ref$fade = _ref.fade,
54
+ fade = _ref$fade === void 0 ? false : _ref$fade,
57
55
  justified = _ref.justified,
58
56
  _ref$modern = _ref.modern,
59
57
  modern = _ref$modern === void 0 ? true : _ref$modern,
60
58
  onActiveChange = _ref.onActiveChange,
61
59
  otherProps = _objectWithoutProperties(_ref, _excluded);
62
60
 
63
- var tabsRef = (0, _react.useRef)(null);
64
-
65
61
  var _useInternalState = (0, _shared.useInternalState)({
66
62
  defaultName: 'defaultActive',
67
63
  defaultValue: defaultActive,
@@ -74,58 +70,49 @@ function ClayTabs(_ref) {
74
70
  active = _useInternalState2[0],
75
71
  setActive = _useInternalState2[1];
76
72
 
77
- return /*#__PURE__*/_react.default.createElement("ul", _extends({}, otherProps, {
78
- className: (0, _classnames.default)('nav', {
79
- 'nav-justified': justified
80
- }, !displayType ? {
81
- 'nav-tabs': !modern,
82
- 'nav-underline': modern
83
- } : {
84
- 'nav-tabs': displayType === 'basic',
85
- 'nav-underline': displayType === 'underline'
86
- }, className),
87
- onKeyDown: function onKeyDown(event) {
88
- if (!tabsRef.current) {
89
- return;
90
- }
91
-
92
- if (event.key === _shared.Keys.Left || event.key === _shared.Keys.Right) {
93
- var tabs = Array.from(tabsRef.current.querySelectorAll(_shared.FOCUSABLE_ELEMENTS.join(',')));
94
- var activeElement = document.activeElement;
95
- var position = tabs.indexOf(activeElement);
96
- var tab = tabs[event.key === _shared.Keys.Left ? position - 1 : position + 1];
97
-
98
- if (tab) {
99
- tab.focus();
100
-
101
- if (activation === 'automatic') {
102
- var newActive = Array.from(tabsRef.current.querySelectorAll('a, button')).indexOf(tab);
103
- setActive(newActive);
104
- }
105
- }
106
- }
107
- },
108
- ref: tabsRef,
109
- role: "tablist"
110
- }), _react.default.Children.map(children, function (child, index) {
111
- return /*#__PURE__*/_react.default.cloneElement(child, {
112
- active: child.props.active !== undefined ? child.props.active : active === index,
113
- onClick: function onClick(event) {
114
- var onClick = child.props.onClick;
115
-
116
- if (onClick) {
117
- onClick(event);
118
- } else {
119
- setActive(index);
120
- }
121
- }
122
- });
123
- }));
73
+ var _React$Children$toArr = _react.default.Children.toArray(children),
74
+ _React$Children$toArr2 = _slicedToArray(_React$Children$toArr, 2),
75
+ left = _React$Children$toArr2[0],
76
+ right = _React$Children$toArr2[1];
77
+
78
+ var tabsId = (0, _shared.useId)(); // @ts-ignore
79
+
80
+ if ((left === null || left === void 0 ? void 0 : (_left$type = left.type) === null || _left$type === void 0 ? void 0 : _left$type.displayName) === 'ClayTabsList') {
81
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.cloneElement(left, {
82
+ activation: activation,
83
+ active: active,
84
+ displayType: displayType,
85
+ justified: justified,
86
+ modern: modern,
87
+ onActiveChange: setActive,
88
+ tabsId: tabsId
89
+ }), /*#__PURE__*/_react.default.isValidElement(right) && /*#__PURE__*/_react.default.cloneElement(right, {
90
+ active: active,
91
+ fade: fade,
92
+ tabsId: tabsId
93
+ }));
94
+ }
95
+
96
+ return /*#__PURE__*/_react.default.createElement(_List.List, _extends({}, otherProps, {
97
+ activation: activation,
98
+ active: active,
99
+ className: className,
100
+ displayType: displayType,
101
+ justified: justified,
102
+ modern: modern,
103
+ onActiveChange: setActive
104
+ }), children);
124
105
  }
106
+ /**
107
+ * @deprecated since v3.78.2 - Use new composition with Tabs.List and Tabs.Panels.
108
+ */
109
+
125
110
 
126
111
  ClayTabs.Content = _Content.default;
112
+ ClayTabs.Panels = _Content.default;
113
+ ClayTabs.Item = _Item.default;
114
+ ClayTabs.List = _List.List;
127
115
  ClayTabs.TabPane = _TabPane.default;
128
116
  ClayTabs.TabPanel = _TabPane.default;
129
- ClayTabs.Item = _Item.default;
130
117
  var _default = ClayTabs;
131
118
  exports.default = _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clayui/tabs",
3
- "version": "3.78.3",
3
+ "version": "3.79.0",
4
4
  "description": "ClayTabs component",
5
5
  "license": "BSD-3-Clause",
6
6
  "repository": "https://github.com/liferay/clay",
@@ -26,7 +26,7 @@
26
26
  "react"
27
27
  ],
28
28
  "dependencies": {
29
- "@clayui/shared": "^3.78.3",
29
+ "@clayui/shared": "^3.79.0",
30
30
  "classnames": "^2.2.6"
31
31
  },
32
32
  "peerDependencies": {
@@ -37,5 +37,5 @@
37
37
  "browserslist": [
38
38
  "extends browserslist-config-clay"
39
39
  ],
40
- "gitHead": "6066b46f9b7e7455603e9f02e9ebc4f792a619e1"
40
+ "gitHead": "4bf7ee4967efc6c2c46aee28b69569f764a1926c"
41
41
  }
package/src/Content.tsx CHANGED
@@ -7,41 +7,62 @@ import classNames from 'classnames';
7
7
  import React from 'react';
8
8
 
9
9
  export interface IProps extends React.HTMLAttributes<HTMLDivElement> {
10
+ /**
11
+ * @ignore
12
+ */
13
+ active?: React.Key;
14
+
10
15
  /**
11
16
  * Receives a number that indicates the `tabkey` to be rendered.
17
+ * @deprecated since v3.78.2 - No longer needed in new composition.
12
18
  */
13
- activeIndex: number;
19
+ activeIndex?: number;
14
20
 
15
21
  /**
16
22
  * Children elements received from ClayTabs.Content component.
17
23
  */
18
- children: Array<React.ReactElement>;
24
+ children: React.ReactNode;
19
25
 
20
26
  /**
21
27
  * Flag to indicate if `fade` classname that applies an fading animation should be applied.
22
28
  */
23
29
  fade?: boolean;
30
+
31
+ /**
32
+ * @ignore
33
+ */
34
+ tabsId?: string;
24
35
  }
25
36
 
26
37
  const Content = ({
38
+ active,
27
39
  activeIndex = 0,
28
40
  children,
29
41
  className,
30
42
  fade = false,
43
+ tabsId,
31
44
  ...otherProps
32
45
  }: IProps) => {
33
46
  return (
34
47
  <div className={classNames('tab-content', className)} {...otherProps}>
35
48
  {React.Children.map(children, (child, index) => {
36
- return (
37
- child &&
38
- React.cloneElement(child, {
39
- ...child.props,
40
- active: activeIndex === index,
41
- fade,
42
- key: index,
43
- })
44
- );
49
+ if (!React.isValidElement(child)) {
50
+ return child;
51
+ }
52
+
53
+ return React.cloneElement(child, {
54
+ ...child.props,
55
+ active:
56
+ typeof active === 'number'
57
+ ? active === index
58
+ : activeIndex === index,
59
+ 'aria-labelledby': tabsId
60
+ ? `${tabsId}-tab-${index}`
61
+ : child.props['aria-labelledby'],
62
+ fade,
63
+ id: tabsId ? `${tabsId}-tabpanel-${index}` : child.props.id,
64
+ key: index,
65
+ });
45
66
  })}
46
67
  </div>
47
68
  );
package/src/List.tsx ADDED
@@ -0,0 +1,132 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: © 2022 Liferay, Inc. <https://liferay.com>
3
+ * SPDX-License-Identifier: BSD-3-Clause
4
+ */
5
+
6
+ import {InternalDispatch, useNavigation} from '@clayui/shared';
7
+ import classNames from 'classnames';
8
+ import React, {useRef} from 'react';
9
+
10
+ export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
11
+ /**
12
+ * @ignore
13
+ */
14
+ activation?: 'manual' | 'automatic';
15
+
16
+ /**
17
+ * @ignore
18
+ */
19
+ active?: React.Key;
20
+
21
+ /**
22
+ * The tabs content.
23
+ */
24
+ children: React.ReactNode;
25
+
26
+ /**
27
+ * The custom class.
28
+ */
29
+ className?: string;
30
+
31
+ /**
32
+ * @ignore
33
+ */
34
+ displayType?: null | 'basic' | 'underline';
35
+
36
+ /**
37
+ * @ignore
38
+ */
39
+ justified?: boolean;
40
+
41
+ /**
42
+ * @ignore
43
+ */
44
+ modern?: boolean;
45
+
46
+ /**
47
+ * @ignore
48
+ */
49
+ onActiveChange?: InternalDispatch<number>;
50
+
51
+ /**
52
+ * @ignore
53
+ */
54
+ tabsId?: string;
55
+ }
56
+
57
+ export function List({
58
+ activation,
59
+ active,
60
+ children,
61
+ className,
62
+ displayType,
63
+ justified,
64
+ modern,
65
+ onActiveChange,
66
+ tabsId,
67
+ ...otherProps
68
+ }: IProps) {
69
+ const tabsRef = useRef<HTMLUListElement>(null);
70
+
71
+ const {onKeyDown} = useNavigation({
72
+ activation,
73
+ containeRef: tabsRef,
74
+ orientation: 'horizontal',
75
+ });
76
+
77
+ return (
78
+ <ul
79
+ {...otherProps}
80
+ className={classNames(
81
+ 'nav',
82
+ {'nav-justified': justified},
83
+ !displayType
84
+ ? {
85
+ 'nav-tabs': !modern,
86
+ 'nav-underline': modern,
87
+ }
88
+ : {
89
+ 'nav-tabs': displayType === 'basic',
90
+ 'nav-underline': displayType === 'underline',
91
+ },
92
+
93
+ className
94
+ )}
95
+ onKeyDown={onKeyDown}
96
+ ref={tabsRef}
97
+ role="tablist"
98
+ >
99
+ {React.Children.map(children, (child, index) => {
100
+ if (!React.isValidElement(child)) {
101
+ return child;
102
+ }
103
+
104
+ return React.cloneElement(child as React.ReactElement, {
105
+ active:
106
+ (child as React.ReactElement).props.active !== undefined
107
+ ? (child as React.ReactElement).props.active
108
+ : active === index,
109
+ innerProps: {
110
+ 'aria-controls':
111
+ tabsId && `${tabsId}-tabpanel-${index}`,
112
+ id: tabsId && `${tabsId}-tab-${index}`,
113
+ ...(child.props.innerProps ?? {}),
114
+ },
115
+ onClick: (
116
+ event: React.MouseEvent<HTMLButtonElement, MouseEvent>
117
+ ) => {
118
+ const {onClick} = (child as React.ReactElement).props;
119
+
120
+ if (onClick) {
121
+ onClick(event);
122
+ } else {
123
+ onActiveChange!(index);
124
+ }
125
+ },
126
+ });
127
+ })}
128
+ </ul>
129
+ );
130
+ }
131
+
132
+ List.displayName = 'ClayTabsList';
package/src/TabPane.tsx CHANGED
@@ -28,6 +28,7 @@ const TabPane = ({
28
28
  children,
29
29
  className,
30
30
  fade,
31
+ tabIndex = 0,
31
32
  ...otherProps
32
33
  }: ITabPaneProps) => {
33
34
  const [internalActive, setInternalActive] = React.useState(active);
@@ -68,7 +69,7 @@ const TabPane = ({
68
69
  className
69
70
  )}
70
71
  role="tabpanel"
71
- tabIndex={0}
72
+ tabIndex={tabIndex}
72
73
  >
73
74
  {children}
74
75
  </div>
@@ -148,4 +148,48 @@ describe('ClayTabs', () => {
148
148
  fireEvent.click(tabItems[1]);
149
149
  expect(onClick).toBeCalled();
150
150
  });
151
+
152
+ it('renders elements not valid tabs should continue to work', () => {
153
+ const {getAllByRole} = render(
154
+ <>
155
+ <ClayTabs>
156
+ {false && <ClayTabs.Item active>One</ClayTabs.Item>}
157
+ <ClayTabs.Item>Two</ClayTabs.Item>
158
+ </ClayTabs>
159
+ <ClayTabs.Content activeIndex={1}>
160
+ {false && <ClayTabs.TabPane>Content One</ClayTabs.TabPane>}
161
+ <ClayTabs.TabPane>Content Two</ClayTabs.TabPane>
162
+ </ClayTabs.Content>
163
+ </>
164
+ );
165
+
166
+ const tabItems = getAllByRole('tab');
167
+ const tabPanels = getAllByRole('tabpanel');
168
+
169
+ expect(tabItems[0].innerHTML).toBe('Two');
170
+ expect(tabItems.length).toBe(1);
171
+
172
+ expect(tabPanels[0].innerHTML).toBe('Content Two');
173
+ expect(tabPanels.length).toBe(1);
174
+ });
175
+
176
+ it('renders the new default composition', () => {
177
+ const {getAllByRole} = render(
178
+ <ClayTabs>
179
+ <ClayTabs.List>
180
+ <ClayTabs.Item>Tab 1</ClayTabs.Item>
181
+ <ClayTabs.Item>Tab 2</ClayTabs.Item>
182
+ <ClayTabs.Item>Tab 3</ClayTabs.Item>
183
+ </ClayTabs.List>
184
+ <ClayTabs.Panels>
185
+ <ClayTabs.TabPanel>Tab Content 1</ClayTabs.TabPanel>
186
+ <ClayTabs.TabPanel>Tab Content 2</ClayTabs.TabPanel>
187
+ <ClayTabs.TabPanel>Tab Content 3</ClayTabs.TabPanel>
188
+ </ClayTabs.Panels>
189
+ </ClayTabs>
190
+ );
191
+
192
+ expect(getAllByRole('tab').length).toBe(3);
193
+ expect(getAllByRole('tabpanel').length).toBe(3);
194
+ });
151
195
  });
package/src/index.tsx CHANGED
@@ -3,17 +3,12 @@
3
3
  * SPDX-License-Identifier: BSD-3-Clause
4
4
  */
5
5
 
6
- import {
7
- FOCUSABLE_ELEMENTS,
8
- InternalDispatch,
9
- Keys,
10
- useInternalState,
11
- } from '@clayui/shared';
12
- import classNames from 'classnames';
13
- import React, {useRef} from 'react';
6
+ import {InternalDispatch, useId, useInternalState} from '@clayui/shared';
7
+ import React from 'react';
14
8
 
15
9
  import Content from './Content';
16
10
  import Item from './Item';
11
+ import {List} from './List';
17
12
  import TabPane from './TabPane';
18
13
 
19
14
  export type DisplayType = null | 'basic' | 'underline';
@@ -43,6 +38,12 @@ export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
43
38
  */
44
39
  displayType?: DisplayType;
45
40
 
41
+ /**
42
+ * Flag to indicate if `fade` classname that applies an fading animation
43
+ * should be applied.
44
+ */
45
+ fade?: boolean;
46
+
46
47
  /**
47
48
  * Justify the nav items according the tab content.
48
49
  */
@@ -61,9 +62,11 @@ export interface IProps extends React.HTMLAttributes<HTMLUListElement> {
61
62
 
62
63
  function ClayTabs(props: IProps): JSX.Element & {
63
64
  Content: typeof Content;
65
+ Item: typeof Item;
66
+ List: typeof List;
67
+ Panels: typeof Content;
64
68
  TabPane: typeof TabPane;
65
69
  TabPanel: typeof TabPane;
66
- Item: typeof Item;
67
70
  };
68
71
 
69
72
  function ClayTabs({
@@ -73,13 +76,12 @@ function ClayTabs({
73
76
  className,
74
77
  defaultActive = 0,
75
78
  displayType,
79
+ fade = false,
76
80
  justified,
77
81
  modern = true,
78
82
  onActiveChange,
79
83
  ...otherProps
80
84
  }: IProps) {
81
- const tabsRef = useRef<HTMLUListElement>(null);
82
-
83
85
  const [active, setActive] = useInternalState({
84
86
  defaultName: 'defaultActive',
85
87
  defaultValue: defaultActive,
@@ -89,88 +91,59 @@ function ClayTabs({
89
91
  value: externalActive,
90
92
  });
91
93
 
94
+ const [left, right] = React.Children.toArray(children);
95
+
96
+ const tabsId = useId();
97
+
98
+ // @ts-ignore
99
+ if (left?.type?.displayName === 'ClayTabsList') {
100
+ return (
101
+ <>
102
+ {React.cloneElement(left as React.ReactElement, {
103
+ activation,
104
+ active,
105
+ displayType,
106
+ justified,
107
+ modern,
108
+ onActiveChange: setActive,
109
+ tabsId,
110
+ })}
111
+
112
+ {React.isValidElement(right) &&
113
+ React.cloneElement(right as React.ReactElement, {
114
+ active,
115
+ fade,
116
+ tabsId,
117
+ })}
118
+ </>
119
+ );
120
+ }
121
+
92
122
  return (
93
- <ul
123
+ <List
94
124
  {...otherProps}
95
- className={classNames(
96
- 'nav',
97
- {'nav-justified': justified},
98
- !displayType
99
- ? {
100
- 'nav-tabs': !modern,
101
- 'nav-underline': modern,
102
- }
103
- : {
104
- 'nav-tabs': displayType === 'basic',
105
- 'nav-underline': displayType === 'underline',
106
- },
107
-
108
- className
109
- )}
110
- onKeyDown={(event) => {
111
- if (!tabsRef.current) {
112
- return;
113
- }
114
-
115
- if (event.key === Keys.Left || event.key === Keys.Right) {
116
- const tabs = Array.from<HTMLElement>(
117
- tabsRef.current.querySelectorAll(
118
- FOCUSABLE_ELEMENTS.join(',')
119
- )
120
- );
121
- const activeElement = document.activeElement as HTMLElement;
122
-
123
- const position = tabs.indexOf(activeElement);
124
-
125
- const tab =
126
- tabs[
127
- event.key === Keys.Left
128
- ? position - 1
129
- : position + 1
130
- ];
131
-
132
- if (tab) {
133
- tab.focus();
134
-
135
- if (activation === 'automatic') {
136
- const newActive = Array.from(
137
- tabsRef.current.querySelectorAll('a, button')
138
- ).indexOf(tab);
139
-
140
- setActive(newActive);
141
- }
142
- }
143
- }
144
- }}
145
- ref={tabsRef}
146
- role="tablist"
125
+ activation={activation}
126
+ active={active}
127
+ className={className}
128
+ displayType={displayType}
129
+ justified={justified}
130
+ modern={modern}
131
+ onActiveChange={setActive}
147
132
  >
148
- {React.Children.map(children, (child, index) =>
149
- React.cloneElement(child as React.ReactElement, {
150
- active:
151
- (child as React.ReactElement).props.active !== undefined
152
- ? (child as React.ReactElement).props.active
153
- : active === index,
154
- onClick: (
155
- event: React.MouseEvent<HTMLButtonElement, MouseEvent>
156
- ) => {
157
- const {onClick} = (child as React.ReactElement).props;
158
-
159
- if (onClick) {
160
- onClick(event);
161
- } else {
162
- setActive(index);
163
- }
164
- },
165
- })
166
- )}
167
- </ul>
133
+ {children}
134
+ </List>
168
135
  );
169
136
  }
170
137
 
138
+ /**
139
+ * @deprecated since v3.78.2 - Use new composition with Tabs.List and Tabs.Panels.
140
+ */
171
141
  ClayTabs.Content = Content;
142
+
143
+ ClayTabs.Panels = Content;
144
+ ClayTabs.Item = Item;
145
+ ClayTabs.List = List;
172
146
  ClayTabs.TabPane = TabPane;
173
147
  ClayTabs.TabPanel = TabPane;
174
- ClayTabs.Item = Item;
175
148
 
176
149
  export default ClayTabs;