@hi-ui/tabs 4.0.0-beta.18 → 4.0.0-beta.20

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/cjs/TabInk.js CHANGED
@@ -32,36 +32,43 @@ var TabInk = function TabInk(_ref) {
32
32
 
33
33
  var disabled = _ref.disabled,
34
34
  prefixCls = _ref.prefixCls,
35
- itemRef = _ref.itemRef,
36
- tabListRef = _ref.tabListRef,
35
+ activeItemElement = _ref.activeItemElement,
37
36
  direction = _ref.direction,
38
- translate = _ref.translate;
37
+ activeTabId = _ref.activeTabId,
38
+ getTabOffset = _ref.getTabOffset;
39
39
  var inkRef = React.useRef(null);
40
40
  React.useEffect(function () {
41
- if (inkRef.current && itemRef && tabListRef) {
42
- var itemRect = itemRef.getBoundingClientRect();
43
- var listRect = tabListRef.getBoundingClientRect();
44
- var _style = {};
41
+ if (!inkRef.current) return;
42
+ if (!activeItemElement) return;
43
+ var computedStyle = getComputedStyle(activeItemElement);
44
+ var itemRect = activeItemElement.getBoundingClientRect();
45
+ var offset = getTabOffset(activeTabId);
45
46
 
46
- if (direction === 'vertical') {
47
- _style = {
48
- top: itemRect.top - listRect.top - translate + 2 + 8 + "px",
49
- height: itemRect.height - 4 - 16 + "px",
50
- left: '',
51
- width: ''
52
- };
53
- } else {
54
- _style = {
55
- left: itemRect.left - listRect.left - translate + 20 + "px",
56
- width: itemRect.width - 40 + "px",
57
- top: '',
58
- height: ''
59
- };
60
- }
47
+ var _style;
61
48
 
62
- Object.assign(inkRef.current.style, _style);
49
+ if (direction === 'vertical') {
50
+ var paddingTop = parseFloat(computedStyle.getPropertyValue('padding-top'));
51
+ var paddingBottom = parseFloat(computedStyle.getPropertyValue('padding-bottom'));
52
+ _style = {
53
+ // 2px 保证尽量和文字顶部对齐,减少文本行高的影响
54
+ top: offset + paddingTop + 2 + 'px',
55
+ height: itemRect.height - paddingTop - paddingBottom - 4 + "px",
56
+ left: '',
57
+ width: ''
58
+ };
59
+ } else {
60
+ var paddingLeft = parseFloat(computedStyle.getPropertyValue('padding-left'));
61
+ var paddingRight = parseFloat(computedStyle.getPropertyValue('padding-right'));
62
+ _style = {
63
+ left: offset + paddingLeft + 'px',
64
+ width: itemRect.width - paddingRight - paddingLeft + "px",
65
+ top: '',
66
+ height: ''
67
+ };
63
68
  }
64
- }, [itemRef, tabListRef, direction]);
69
+
70
+ Object.assign(inkRef.current.style, _style);
71
+ }, [activeItemElement, direction, activeTabId, getTabOffset]);
65
72
  return /*#__PURE__*/React__default["default"].createElement("div", {
66
73
  className: classname.cx(prefixCls + "__ink", (_cx = {}, _cx[prefixCls + "__ink--disabled"] = disabled, _cx)),
67
74
  ref: inkRef
@@ -35,6 +35,12 @@ var typeAssertion = require('@hi-ui/type-assertion');
35
35
 
36
36
  var iconButton = require('@hi-ui/icon-button');
37
37
 
38
+ var index = require('./hooks/index.js');
39
+
40
+ var useLatest = require('@hi-ui/use-latest');
41
+
42
+ var index$1 = require('./utils/index.js');
43
+
38
44
  function _interopDefaultLegacy(e) {
39
45
  return e && _typeof(e) === 'object' && 'default' in e ? e : {
40
46
  'default': e
@@ -50,8 +56,6 @@ var _prefix = classname.getPrefixCls(_role);
50
56
  var TabList = /*#__PURE__*/React.forwardRef(function (_a, ref) {
51
57
  var _cx;
52
58
 
53
- var _b;
54
-
55
59
  var data = _a.data,
56
60
  className = _a.className,
57
61
  style = _a.style,
@@ -82,102 +86,163 @@ var TabList = /*#__PURE__*/React.forwardRef(function (_a, ref) {
82
86
  }
83
87
 
84
88
  return defaultActiveId;
85
- }, activeId, onChange),
89
+ }, activeId, onChange, Object.is),
86
90
  activeTabId = _useUncontrolledState[0],
87
91
  setActiveTabId = _useUncontrolledState[1];
88
92
 
89
93
  var _useState = React.useState(null),
90
- innerRef = _useState[0],
91
- setInnerRef = _useState[1];
94
+ innerElement = _useState[0],
95
+ setInnerElement = _useState[1];
92
96
 
93
97
  var _useState2 = React.useState(null),
94
- scrollRef = _useState2[0],
95
- setScrollRef = _useState2[1];
98
+ scrollElement = _useState2[0],
99
+ setScrollElement = _useState2[1];
100
+
101
+ var showHorizontal = direction === 'horizontal';
102
+ var getClientSize = React.useCallback(function (element) {
103
+ return showHorizontal ? element.clientWidth : element.clientHeight;
104
+ }, [showHorizontal]);
96
105
 
97
106
  var _useState3 = React.useState(0),
98
- translateX = _useState3[0],
99
- setTranslateX = _useState3[1];
107
+ translatePos = _useState3[0],
108
+ setTranslatePos = _useState3[1];
100
109
 
101
110
  var _useState4 = React.useState(0),
102
- translateY = _useState4[0],
103
- setTranslateY = _useState4[1];
111
+ translateBoundPos = _useState4[0],
112
+ setTranslateBoundPos = _useState4[1];
104
113
 
105
114
  var itemsRef = React.useRef({});
106
- var showScrollBtn = false;
107
115
 
108
- if (scrollRef && innerRef) {
109
- showScrollBtn = direction === 'horizontal' ? scrollRef.clientWidth > innerRef.clientWidth : scrollRef.clientHeight > innerRef.clientHeight;
110
- }
116
+ var _useState5 = React.useState(false),
117
+ showScrollBtn = _useState5[0],
118
+ setShowScrollBtn = _useState5[1];
111
119
 
112
- var onClickTab = React.useCallback(function (key, event) {
113
- if (onTabClick) {
114
- onTabClick(key, event);
115
- }
120
+ var resizeScroll = function resizeScroll(scrollSize, innerSize) {
121
+ var showScrollBtn = scrollSize > innerSize;
122
+ setShowScrollBtn(showScrollBtn);
116
123
 
117
- if (key !== activeTabId && setActiveTabId) {
118
- setActiveTabId(key);
124
+ if (showScrollBtn) {
125
+ setTranslateBoundPos(scrollSize - innerSize);
119
126
  }
120
- }, [activeTabId, onTabClick, setActiveTabId]);
121
- return /*#__PURE__*/React__default["default"].createElement("div", Object.assign({
122
- style: style,
123
- className: classname.cx(prefixCls + "__list", (_cx = {}, _cx[prefixCls + "__list--" + type] = type, _cx), className),
124
- ref: ref
125
- }, rest), showScrollBtn && direction === 'horizontal' && /*#__PURE__*/React__default["default"].createElement(iconButton.IconButton, {
126
- className: prefixCls + "__left-btn",
127
- effect: true,
128
- icon: /*#__PURE__*/React__default["default"].createElement(icons.LeftOutlined, null),
129
- onClick: function onClick() {
130
- if (scrollRef && innerRef) {
131
- var canScroll = -translateX - innerRef.clientWidth;
132
- var moveWidth = 0;
127
+ }; // 响应式滚动按钮展示
133
128
 
134
- if (canScroll >= 0) {
135
- moveWidth = innerRef.clientWidth;
136
- } else {
137
- moveWidth = -translateX;
138
- }
139
129
 
140
- setTranslateX(translateX + moveWidth);
130
+ index.useResizeObserver({
131
+ element: innerElement,
132
+ getSize: getClientSize,
133
+ onResize: function onResize(_, innerSize) {
134
+ if (scrollElement) {
135
+ var scrollSize = getClientSize(scrollElement);
136
+ resizeScroll(scrollSize, innerSize);
141
137
  }
142
138
  }
143
- }), showScrollBtn && direction === 'vertical' && /*#__PURE__*/React__default["default"].createElement(iconButton.IconButton, {
144
- className: prefixCls + "__up-btn",
145
- effect: true,
146
- icon: /*#__PURE__*/React__default["default"].createElement(icons.UpOutlined, null),
147
- onClick: function onClick() {
148
- if (scrollRef && innerRef) {
149
- var canScroll = -translateY - innerRef.clientHeight;
150
- var moveWidth = 0;
139
+ });
140
+ index.useResizeObserver({
141
+ element: scrollElement,
142
+ getSize: getClientSize,
143
+ onResize: function onResize(_, scrollSize) {
144
+ if (innerElement) {
145
+ var innerSize = getClientSize(innerElement);
146
+ resizeScroll(scrollSize, innerSize);
147
+ }
148
+ }
149
+ });
150
+ var getTabPos = React.useCallback(function (tabId) {
151
+ var target = 0; // 获取目标元素的位置
152
+
153
+ var targetElement = itemsRef.current[tabId];
154
+
155
+ if (targetElement) {
156
+ var rect = targetElement.getBoundingClientRect();
157
+ target = showHorizontal ? rect.left : rect.top;
158
+ }
159
+
160
+ return target;
161
+ }, [showHorizontal]);
162
+ var getTabOffset = React.useCallback(function (tabId) {
163
+ // 获取目标元素的位置
164
+ var targetPos = getTabPos(tabId); // 获取基准元素的位置(第一个)
165
+
166
+ var basePos = 0;
167
+
168
+ if (typeAssertion.isArrayNonEmpty(data)) {
169
+ var baseItem = data[0];
170
+ basePos = getTabPos(baseItem.tabId);
171
+ } // 返回目标元素相对基准元素的偏移量
151
172
 
152
- if (canScroll >= 0) {
153
- moveWidth = innerRef.clientHeight;
154
- } else {
155
- moveWidth = -translateY;
156
- }
157
173
 
158
- setTranslateY(translateY + moveWidth);
174
+ return targetPos ? targetPos - basePos : 0;
175
+ }, [data, getTabPos]);
176
+
177
+ var syncScrollPosition = function syncScrollPosition(tabId) {
178
+ if (!innerElement) return;
179
+ if (!scrollElement) return;
180
+ var scrollSize = getClientSize(scrollElement);
181
+ var innerSize = getClientSize(innerElement);
182
+ var offsetValue = getTabOffset(tabId); // 左边或上半部内容展示不全
183
+
184
+ var currentOffset = -translatePos;
185
+
186
+ if (offsetValue < currentOffset) {
187
+ setTranslatePos(-offsetValue);
188
+ } else {
189
+ // 右边或下半部内容展示不全
190
+ var nextTabId = index$1.getNextTabId(data, tabId);
191
+ var nextOffsetValue = nextTabId !== null ? getTabOffset(nextTabId) : scrollSize;
192
+
193
+ var _currentOffset = -translatePos + innerSize;
194
+
195
+ if (nextOffsetValue > _currentOffset) {
196
+ setTranslatePos(translatePos - (nextOffsetValue - _currentOffset));
159
197
  }
160
198
  }
161
- }), /*#__PURE__*/React__default["default"].createElement("div", {
199
+ };
200
+
201
+ var onClickTab = useLatest.useLatestCallback(function (tabId, event) {
202
+ onTabClick === null || onTabClick === void 0 ? void 0 : onTabClick(tabId, event);
203
+ setActiveTabId(tabId);
204
+ syncScrollPosition(tabId);
205
+ });
206
+ return /*#__PURE__*/React__default["default"].createElement("div", Object.assign({
207
+ style: style,
208
+ className: classname.cx(prefixCls + "__list", (_cx = {}, _cx[prefixCls + "__list--" + type] = type, _cx), className),
209
+ ref: ref
210
+ }, rest), showScrollBtn ? /*#__PURE__*/React__default["default"].createElement(iconButton.IconButton, {
211
+ className: showHorizontal ? prefixCls + "__left-btn" : prefixCls + "__up-btn",
212
+ effect: true,
213
+ disabled: translatePos === 0,
214
+ icon: showHorizontal ? /*#__PURE__*/React__default["default"].createElement(icons.LeftOutlined, null) : /*#__PURE__*/React__default["default"].createElement(icons.UpOutlined, null),
215
+ onClick: function onClick() {
216
+ if (!scrollElement) return;
217
+ if (!innerElement) return; // 向前面滚动
218
+
219
+ var clientSize = getClientSize(innerElement);
220
+ var canScroll = -translatePos - clientSize;
221
+ var moveWidth = canScroll >= 0 ? clientSize : -translatePos;
222
+ setTranslatePos(function (prev) {
223
+ return prev + moveWidth;
224
+ });
225
+ }
226
+ }) : null, /*#__PURE__*/React__default["default"].createElement("div", {
162
227
  className: classname.cx(prefixCls + "__list--inner"),
163
- ref: setInnerRef
228
+ ref: setInnerElement
164
229
  }, /*#__PURE__*/React__default["default"].createElement("div", {
165
230
  className: classname.cx(prefixCls + "__list--scroll"),
166
- ref: setScrollRef,
231
+ ref: setScrollElement,
167
232
  style: showScrollBtn ? {
168
- transform: direction === 'horizontal' ? "translateX(" + translateX + "px)" : "translateY(" + translateY + "px)"
169
- } : {}
170
- }, data.map(function (d, index) {
233
+ transform: direction === 'horizontal' ? "translateX(" + translatePos + "px)" : "translateY(" + translatePos + "px)"
234
+ } : undefined
235
+ }, data.map(function (item, index) {
171
236
  return /*#__PURE__*/React__default["default"].createElement(TabItem.TabItem, Object.assign({
172
237
  key: index
173
- }, d, {
238
+ }, item, {
174
239
  ref: function ref(node) {
175
- itemsRef.current["" + d.tabId] = node;
240
+ itemsRef.current["" + item.tabId] = node;
176
241
  },
177
242
  type: type,
178
- itemRef: itemsRef.current["" + d.tabId],
243
+ itemRef: itemsRef.current["" + item.tabId],
179
244
  index: index,
180
- active: activeTabId === d.tabId,
245
+ active: activeTabId === item.tabId,
181
246
  prefixCls: prefixCls,
182
247
  draggable: draggable,
183
248
  onTabClick: onClickTab,
@@ -189,46 +254,30 @@ var TabList = /*#__PURE__*/React.forwardRef(function (_a, ref) {
189
254
  onDragEnd: onDragEnd,
190
255
  direction: direction
191
256
  }));
192
- }), type === 'line' && /*#__PURE__*/React__default["default"].createElement(TabInk.TabInk, {
193
- translate: direction === 'horizontal' ? translateX : translateY,
257
+ }), type === 'line' ? /*#__PURE__*/React__default["default"].createElement(TabInk.TabInk, {
194
258
  prefixCls: prefixCls,
195
259
  direction: direction,
196
- tabListRef: innerRef,
197
- itemRef: (_b = itemsRef.current) === null || _b === void 0 ? void 0 : _b[activeTabId]
198
- }))), showScrollBtn && direction === 'horizontal' && /*#__PURE__*/React__default["default"].createElement(iconButton.IconButton, {
199
- effect: true,
200
- className: prefixCls + "__right-btn",
201
- icon: /*#__PURE__*/React__default["default"].createElement(icons.RightOutlined, null),
202
- onClick: function onClick() {
203
- if (scrollRef && innerRef) {
204
- var scrollWidth = scrollRef.clientWidth;
205
- var innerWidth = innerRef.clientWidth;
206
- var canScrollWidth = scrollWidth - innerWidth + translateX;
207
- var moveWidth = canScrollWidth < innerWidth ? canScrollWidth : innerWidth;
208
- setTranslateX(function (prev) {
209
- return prev - moveWidth;
210
- });
211
- }
212
- }
213
- }), showScrollBtn && direction === 'vertical' && /*#__PURE__*/React__default["default"].createElement(iconButton.IconButton, {
260
+ activeItemElement: itemsRef.current[activeTabId],
261
+ activeTabId: activeTabId,
262
+ getTabOffset: getTabOffset
263
+ }) : null)), showScrollBtn ? /*#__PURE__*/React__default["default"].createElement(iconButton.IconButton, {
214
264
  effect: true,
215
- className: prefixCls + "__down-btn",
216
- icon: /*#__PURE__*/React__default["default"].createElement(icons.DownOutlined, null),
265
+ className: showHorizontal ? prefixCls + "__right-btn" : prefixCls + "__down-btn",
266
+ disabled: translateBoundPos === -translatePos,
267
+ icon: showHorizontal ? /*#__PURE__*/React__default["default"].createElement(icons.RightOutlined, null) : /*#__PURE__*/React__default["default"].createElement(icons.DownOutlined, null),
217
268
  onClick: function onClick() {
218
- if (scrollRef && innerRef) {
219
- var canScroll = scrollRef.clientHeight - innerRef.clientHeight + translateY;
220
- var moveWidth = 0;
221
-
222
- if (canScroll >= innerRef.clientHeight) {
223
- moveWidth = innerRef.clientHeight;
224
- } else {
225
- moveWidth = canScroll;
226
- }
227
-
228
- setTranslateY(translateY - moveWidth);
229
- }
269
+ if (!scrollElement) return;
270
+ if (!innerElement) return; // 向后面滚动
271
+
272
+ var scrollSize = getClientSize(scrollElement);
273
+ var innerSize = getClientSize(innerElement);
274
+ var canScrollWidth = scrollSize - innerSize + translatePos;
275
+ var moveWidth = canScrollWidth < innerSize ? canScrollWidth : innerSize;
276
+ setTranslatePos(function (prev) {
277
+ return prev - moveWidth;
278
+ });
230
279
  }
231
- }), editable ? /*#__PURE__*/React__default["default"].createElement(iconButton.IconButton, {
280
+ }) : null, editable ? /*#__PURE__*/React__default["default"].createElement(iconButton.IconButton, {
232
281
  icon: /*#__PURE__*/React__default["default"].createElement(icons.PlusOutlined, null),
233
282
  className: prefixCls + "__add-btn",
234
283
  onClick: onAdd
@@ -19,6 +19,8 @@ var tslib = require('tslib');
19
19
 
20
20
  var React = require('react');
21
21
 
22
+ var reactUtils = require('@hi-ui/react-utils');
23
+
22
24
  function _interopDefaultLegacy(e) {
23
25
  return e && _typeof(e) === 'object' && 'default' in e ? e : {
24
26
  'default': e
@@ -27,6 +29,8 @@ function _interopDefaultLegacy(e) {
27
29
 
28
30
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
29
31
 
32
+ var omitProps = ['tabId', 'tabTitle', 'disabled', 'tabDesc', 'closeable'];
33
+
30
34
  var TabPane = function TabPane(_a) {
31
35
  var children = _a.children,
32
36
  className = _a.className,
@@ -34,10 +38,11 @@ var TabPane = function TabPane(_a) {
34
38
  active = _a.active,
35
39
  rest = tslib.__rest(_a, ["children", "className", "style", "active"]);
36
40
 
41
+ var htmlProps = reactUtils.filterProps(rest, omitProps);
37
42
  return /*#__PURE__*/React__default["default"].createElement("div", Object.assign({
38
43
  style: style,
39
44
  className: className
40
- }, rest), active ? children : null);
45
+ }, htmlProps), active ? children : null);
41
46
  };
42
47
 
43
48
  exports.TabPane = TabPane;
@@ -0,0 +1,63 @@
1
+ /** @LICENSE
2
+ * @hi-ui/tabs
3
+ * https://github.com/XiaoMi/hiui/tree/master/packages/ui/tabs#readme
4
+ *
5
+ * Copyright (c) HIUI <mi-hiui@xiaomi.com>.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */
10
+ 'use strict';
11
+
12
+ Object.defineProperty(exports, '__esModule', {
13
+ value: true
14
+ });
15
+
16
+ var React = require('react');
17
+
18
+ var useLatest = require('@hi-ui/use-latest');
19
+
20
+ var useUnmountEffect = require('@hi-ui/use-unmount-effect');
21
+ /**
22
+ * 响应式监听元素的 size 变化
23
+ */
24
+
25
+
26
+ var useResizeObserver = function useResizeObserver(_ref) {
27
+ var element = _ref.element,
28
+ onResize = _ref.onResize,
29
+ _ref$initialSize = _ref.initialSize,
30
+ initialSize = _ref$initialSize === void 0 ? 0 : _ref$initialSize,
31
+ getSize = _ref.getSize;
32
+ var getSizeLatestRef = useLatest.useLatestRef(getSize);
33
+ var onResizeLatest = useLatest.useLatestCallback(onResize);
34
+ var prevSizeRef = React.useRef(initialSize);
35
+ var unmountRef = useUnmountEffect.useUnmountEffect();
36
+ React.useEffect(function () {
37
+ if (element) {
38
+ var _resizeObserver = new ResizeObserver(function () {
39
+ if (unmountRef.current) return;
40
+ var getSize = getSizeLatestRef.current;
41
+
42
+ if (element) {
43
+ if (getSize) {
44
+ var nextSize = getSize(element);
45
+
46
+ if (prevSizeRef.current !== nextSize) {
47
+ prevSizeRef.current = nextSize;
48
+ onResizeLatest(element, nextSize);
49
+ }
50
+ } else {
51
+ onResizeLatest(element);
52
+ }
53
+ }
54
+ });
55
+
56
+ _resizeObserver.observe(element);
57
+ }
58
+
59
+ return function () {};
60
+ }, [element, getSizeLatestRef, onResizeLatest, unmountRef]);
61
+ };
62
+
63
+ exports.useResizeObserver = useResizeObserver;
@@ -0,0 +1,32 @@
1
+ /** @LICENSE
2
+ * @hi-ui/tabs
3
+ * https://github.com/XiaoMi/hiui/tree/master/packages/ui/tabs#readme
4
+ *
5
+ * Copyright (c) HIUI <mi-hiui@xiaomi.com>.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */
10
+ 'use strict';
11
+
12
+ Object.defineProperty(exports, '__esModule', {
13
+ value: true
14
+ });
15
+
16
+ var typeAssertion = require('@hi-ui/type-assertion');
17
+
18
+ var getNextTabId = function getNextTabId(data, tabId) {
19
+ if (typeAssertion.isArrayNonEmpty(data)) {
20
+ var itemIndex = data.findIndex(function (item) {
21
+ return item.tabId === tabId;
22
+ });
23
+
24
+ if (itemIndex !== -1 && data[itemIndex + 1]) {
25
+ return data[itemIndex + 1].tabId;
26
+ }
27
+ }
28
+
29
+ return null;
30
+ };
31
+
32
+ exports.getNextTabId = getNextTabId;
package/lib/esm/TabInk.js CHANGED
@@ -15,36 +15,43 @@ var TabInk = function TabInk(_ref) {
15
15
 
16
16
  var disabled = _ref.disabled,
17
17
  prefixCls = _ref.prefixCls,
18
- itemRef = _ref.itemRef,
19
- tabListRef = _ref.tabListRef,
18
+ activeItemElement = _ref.activeItemElement,
20
19
  direction = _ref.direction,
21
- translate = _ref.translate;
20
+ activeTabId = _ref.activeTabId,
21
+ getTabOffset = _ref.getTabOffset;
22
22
  var inkRef = useRef(null);
23
23
  useEffect(function () {
24
- if (inkRef.current && itemRef && tabListRef) {
25
- var itemRect = itemRef.getBoundingClientRect();
26
- var listRect = tabListRef.getBoundingClientRect();
27
- var _style = {};
24
+ if (!inkRef.current) return;
25
+ if (!activeItemElement) return;
26
+ var computedStyle = getComputedStyle(activeItemElement);
27
+ var itemRect = activeItemElement.getBoundingClientRect();
28
+ var offset = getTabOffset(activeTabId);
28
29
 
29
- if (direction === 'vertical') {
30
- _style = {
31
- top: itemRect.top - listRect.top - translate + 2 + 8 + "px",
32
- height: itemRect.height - 4 - 16 + "px",
33
- left: '',
34
- width: ''
35
- };
36
- } else {
37
- _style = {
38
- left: itemRect.left - listRect.left - translate + 20 + "px",
39
- width: itemRect.width - 40 + "px",
40
- top: '',
41
- height: ''
42
- };
43
- }
30
+ var _style;
44
31
 
45
- Object.assign(inkRef.current.style, _style);
32
+ if (direction === 'vertical') {
33
+ var paddingTop = parseFloat(computedStyle.getPropertyValue('padding-top'));
34
+ var paddingBottom = parseFloat(computedStyle.getPropertyValue('padding-bottom'));
35
+ _style = {
36
+ // 2px 保证尽量和文字顶部对齐,减少文本行高的影响
37
+ top: offset + paddingTop + 2 + 'px',
38
+ height: itemRect.height - paddingTop - paddingBottom - 4 + "px",
39
+ left: '',
40
+ width: ''
41
+ };
42
+ } else {
43
+ var paddingLeft = parseFloat(computedStyle.getPropertyValue('padding-left'));
44
+ var paddingRight = parseFloat(computedStyle.getPropertyValue('padding-right'));
45
+ _style = {
46
+ left: offset + paddingLeft + 'px',
47
+ width: itemRect.width - paddingRight - paddingLeft + "px",
48
+ top: '',
49
+ height: ''
50
+ };
46
51
  }
47
- }, [itemRef, tabListRef, direction]);
52
+
53
+ Object.assign(inkRef.current.style, _style);
54
+ }, [activeItemElement, direction, activeTabId, getTabOffset]);
48
55
  return /*#__PURE__*/React.createElement("div", {
49
56
  className: cx(prefixCls + "__ink", (_cx = {}, _cx[prefixCls + "__ink--disabled"] = disabled, _cx)),
50
57
  ref: inkRef
@@ -8,15 +8,18 @@
8
8
  * LICENSE file in the root directory of this source tree.
9
9
  */
10
10
  import { __rest } from 'tslib';
11
- import React, { forwardRef, useState, useRef, useCallback } from 'react';
11
+ import React, { forwardRef, useState, useCallback, useRef } from 'react';
12
12
  import { __DEV__ } from '@hi-ui/env';
13
13
  import { TabItem } from './TabItem.js';
14
14
  import { useUncontrolledState } from '@hi-ui/use-uncontrolled-state';
15
15
  import { getPrefixCls, cx } from '@hi-ui/classname';
16
16
  import { TabInk } from './TabInk.js';
17
17
  import { LeftOutlined, UpOutlined, RightOutlined, DownOutlined, PlusOutlined } from '@hi-ui/icons';
18
- import { isUndef } from '@hi-ui/type-assertion';
18
+ import { isUndef, isArrayNonEmpty } from '@hi-ui/type-assertion';
19
19
  import { IconButton } from '@hi-ui/icon-button';
20
+ import { useResizeObserver } from './hooks/index.js';
21
+ import { useLatestCallback } from '@hi-ui/use-latest';
22
+ import { getNextTabId } from './utils/index.js';
20
23
  var _role = 'tabs';
21
24
 
22
25
  var _prefix = getPrefixCls(_role);
@@ -24,8 +27,6 @@ var _prefix = getPrefixCls(_role);
24
27
  var TabList = /*#__PURE__*/forwardRef(function (_a, ref) {
25
28
  var _cx;
26
29
 
27
- var _b;
28
-
29
30
  var data = _a.data,
30
31
  className = _a.className,
31
32
  style = _a.style,
@@ -56,102 +57,163 @@ var TabList = /*#__PURE__*/forwardRef(function (_a, ref) {
56
57
  }
57
58
 
58
59
  return defaultActiveId;
59
- }, activeId, onChange),
60
+ }, activeId, onChange, Object.is),
60
61
  activeTabId = _useUncontrolledState[0],
61
62
  setActiveTabId = _useUncontrolledState[1];
62
63
 
63
64
  var _useState = useState(null),
64
- innerRef = _useState[0],
65
- setInnerRef = _useState[1];
65
+ innerElement = _useState[0],
66
+ setInnerElement = _useState[1];
66
67
 
67
68
  var _useState2 = useState(null),
68
- scrollRef = _useState2[0],
69
- setScrollRef = _useState2[1];
69
+ scrollElement = _useState2[0],
70
+ setScrollElement = _useState2[1];
71
+
72
+ var showHorizontal = direction === 'horizontal';
73
+ var getClientSize = useCallback(function (element) {
74
+ return showHorizontal ? element.clientWidth : element.clientHeight;
75
+ }, [showHorizontal]);
70
76
 
71
77
  var _useState3 = useState(0),
72
- translateX = _useState3[0],
73
- setTranslateX = _useState3[1];
78
+ translatePos = _useState3[0],
79
+ setTranslatePos = _useState3[1];
74
80
 
75
81
  var _useState4 = useState(0),
76
- translateY = _useState4[0],
77
- setTranslateY = _useState4[1];
82
+ translateBoundPos = _useState4[0],
83
+ setTranslateBoundPos = _useState4[1];
78
84
 
79
85
  var itemsRef = useRef({});
80
- var showScrollBtn = false;
81
86
 
82
- if (scrollRef && innerRef) {
83
- showScrollBtn = direction === 'horizontal' ? scrollRef.clientWidth > innerRef.clientWidth : scrollRef.clientHeight > innerRef.clientHeight;
84
- }
87
+ var _useState5 = useState(false),
88
+ showScrollBtn = _useState5[0],
89
+ setShowScrollBtn = _useState5[1];
90
+
91
+ var resizeScroll = function resizeScroll(scrollSize, innerSize) {
92
+ var showScrollBtn = scrollSize > innerSize;
93
+ setShowScrollBtn(showScrollBtn);
85
94
 
86
- var onClickTab = useCallback(function (key, event) {
87
- if (onTabClick) {
88
- onTabClick(key, event);
95
+ if (showScrollBtn) {
96
+ setTranslateBoundPos(scrollSize - innerSize);
89
97
  }
98
+ }; // 响应式滚动按钮展示
90
99
 
91
- if (key !== activeTabId && setActiveTabId) {
92
- setActiveTabId(key);
100
+
101
+ useResizeObserver({
102
+ element: innerElement,
103
+ getSize: getClientSize,
104
+ onResize: function onResize(_, innerSize) {
105
+ if (scrollElement) {
106
+ var scrollSize = getClientSize(scrollElement);
107
+ resizeScroll(scrollSize, innerSize);
108
+ }
93
109
  }
94
- }, [activeTabId, onTabClick, setActiveTabId]);
95
- return /*#__PURE__*/React.createElement("div", Object.assign({
96
- style: style,
97
- className: cx(prefixCls + "__list", (_cx = {}, _cx[prefixCls + "__list--" + type] = type, _cx), className),
98
- ref: ref
99
- }, rest), showScrollBtn && direction === 'horizontal' && /*#__PURE__*/React.createElement(IconButton, {
100
- className: prefixCls + "__left-btn",
101
- effect: true,
102
- icon: /*#__PURE__*/React.createElement(LeftOutlined, null),
103
- onClick: function onClick() {
104
- if (scrollRef && innerRef) {
105
- var canScroll = -translateX - innerRef.clientWidth;
106
- var moveWidth = 0;
110
+ });
111
+ useResizeObserver({
112
+ element: scrollElement,
113
+ getSize: getClientSize,
114
+ onResize: function onResize(_, scrollSize) {
115
+ if (innerElement) {
116
+ var innerSize = getClientSize(innerElement);
117
+ resizeScroll(scrollSize, innerSize);
118
+ }
119
+ }
120
+ });
121
+ var getTabPos = useCallback(function (tabId) {
122
+ var target = 0; // 获取目标元素的位置
123
+
124
+ var targetElement = itemsRef.current[tabId];
125
+
126
+ if (targetElement) {
127
+ var rect = targetElement.getBoundingClientRect();
128
+ target = showHorizontal ? rect.left : rect.top;
129
+ }
130
+
131
+ return target;
132
+ }, [showHorizontal]);
133
+ var getTabOffset = useCallback(function (tabId) {
134
+ // 获取目标元素的位置
135
+ var targetPos = getTabPos(tabId); // 获取基准元素的位置(第一个)
136
+
137
+ var basePos = 0;
138
+
139
+ if (isArrayNonEmpty(data)) {
140
+ var baseItem = data[0];
141
+ basePos = getTabPos(baseItem.tabId);
142
+ } // 返回目标元素相对基准元素的偏移量
107
143
 
108
- if (canScroll >= 0) {
109
- moveWidth = innerRef.clientWidth;
110
- } else {
111
- moveWidth = -translateX;
112
- }
113
144
 
114
- setTranslateX(translateX + moveWidth);
145
+ return targetPos ? targetPos - basePos : 0;
146
+ }, [data, getTabPos]);
147
+
148
+ var syncScrollPosition = function syncScrollPosition(tabId) {
149
+ if (!innerElement) return;
150
+ if (!scrollElement) return;
151
+ var scrollSize = getClientSize(scrollElement);
152
+ var innerSize = getClientSize(innerElement);
153
+ var offsetValue = getTabOffset(tabId); // 左边或上半部内容展示不全
154
+
155
+ var currentOffset = -translatePos;
156
+
157
+ if (offsetValue < currentOffset) {
158
+ setTranslatePos(-offsetValue);
159
+ } else {
160
+ // 右边或下半部内容展示不全
161
+ var nextTabId = getNextTabId(data, tabId);
162
+ var nextOffsetValue = nextTabId !== null ? getTabOffset(nextTabId) : scrollSize;
163
+
164
+ var _currentOffset = -translatePos + innerSize;
165
+
166
+ if (nextOffsetValue > _currentOffset) {
167
+ setTranslatePos(translatePos - (nextOffsetValue - _currentOffset));
115
168
  }
116
169
  }
117
- }), showScrollBtn && direction === 'vertical' && /*#__PURE__*/React.createElement(IconButton, {
118
- className: prefixCls + "__up-btn",
170
+ };
171
+
172
+ var onClickTab = useLatestCallback(function (tabId, event) {
173
+ onTabClick === null || onTabClick === void 0 ? void 0 : onTabClick(tabId, event);
174
+ setActiveTabId(tabId);
175
+ syncScrollPosition(tabId);
176
+ });
177
+ return /*#__PURE__*/React.createElement("div", Object.assign({
178
+ style: style,
179
+ className: cx(prefixCls + "__list", (_cx = {}, _cx[prefixCls + "__list--" + type] = type, _cx), className),
180
+ ref: ref
181
+ }, rest), showScrollBtn ? /*#__PURE__*/React.createElement(IconButton, {
182
+ className: showHorizontal ? prefixCls + "__left-btn" : prefixCls + "__up-btn",
119
183
  effect: true,
120
- icon: /*#__PURE__*/React.createElement(UpOutlined, null),
184
+ disabled: translatePos === 0,
185
+ icon: showHorizontal ? /*#__PURE__*/React.createElement(LeftOutlined, null) : /*#__PURE__*/React.createElement(UpOutlined, null),
121
186
  onClick: function onClick() {
122
- if (scrollRef && innerRef) {
123
- var canScroll = -translateY - innerRef.clientHeight;
124
- var moveWidth = 0;
125
-
126
- if (canScroll >= 0) {
127
- moveWidth = innerRef.clientHeight;
128
- } else {
129
- moveWidth = -translateY;
130
- }
187
+ if (!scrollElement) return;
188
+ if (!innerElement) return; // 向前面滚动
131
189
 
132
- setTranslateY(translateY + moveWidth);
133
- }
190
+ var clientSize = getClientSize(innerElement);
191
+ var canScroll = -translatePos - clientSize;
192
+ var moveWidth = canScroll >= 0 ? clientSize : -translatePos;
193
+ setTranslatePos(function (prev) {
194
+ return prev + moveWidth;
195
+ });
134
196
  }
135
- }), /*#__PURE__*/React.createElement("div", {
197
+ }) : null, /*#__PURE__*/React.createElement("div", {
136
198
  className: cx(prefixCls + "__list--inner"),
137
- ref: setInnerRef
199
+ ref: setInnerElement
138
200
  }, /*#__PURE__*/React.createElement("div", {
139
201
  className: cx(prefixCls + "__list--scroll"),
140
- ref: setScrollRef,
202
+ ref: setScrollElement,
141
203
  style: showScrollBtn ? {
142
- transform: direction === 'horizontal' ? "translateX(" + translateX + "px)" : "translateY(" + translateY + "px)"
143
- } : {}
144
- }, data.map(function (d, index) {
204
+ transform: direction === 'horizontal' ? "translateX(" + translatePos + "px)" : "translateY(" + translatePos + "px)"
205
+ } : undefined
206
+ }, data.map(function (item, index) {
145
207
  return /*#__PURE__*/React.createElement(TabItem, Object.assign({
146
208
  key: index
147
- }, d, {
209
+ }, item, {
148
210
  ref: function ref(node) {
149
- itemsRef.current["" + d.tabId] = node;
211
+ itemsRef.current["" + item.tabId] = node;
150
212
  },
151
213
  type: type,
152
- itemRef: itemsRef.current["" + d.tabId],
214
+ itemRef: itemsRef.current["" + item.tabId],
153
215
  index: index,
154
- active: activeTabId === d.tabId,
216
+ active: activeTabId === item.tabId,
155
217
  prefixCls: prefixCls,
156
218
  draggable: draggable,
157
219
  onTabClick: onClickTab,
@@ -163,46 +225,30 @@ var TabList = /*#__PURE__*/forwardRef(function (_a, ref) {
163
225
  onDragEnd: onDragEnd,
164
226
  direction: direction
165
227
  }));
166
- }), type === 'line' && /*#__PURE__*/React.createElement(TabInk, {
167
- translate: direction === 'horizontal' ? translateX : translateY,
228
+ }), type === 'line' ? /*#__PURE__*/React.createElement(TabInk, {
168
229
  prefixCls: prefixCls,
169
230
  direction: direction,
170
- tabListRef: innerRef,
171
- itemRef: (_b = itemsRef.current) === null || _b === void 0 ? void 0 : _b[activeTabId]
172
- }))), showScrollBtn && direction === 'horizontal' && /*#__PURE__*/React.createElement(IconButton, {
231
+ activeItemElement: itemsRef.current[activeTabId],
232
+ activeTabId: activeTabId,
233
+ getTabOffset: getTabOffset
234
+ }) : null)), showScrollBtn ? /*#__PURE__*/React.createElement(IconButton, {
173
235
  effect: true,
174
- className: prefixCls + "__right-btn",
175
- icon: /*#__PURE__*/React.createElement(RightOutlined, null),
236
+ className: showHorizontal ? prefixCls + "__right-btn" : prefixCls + "__down-btn",
237
+ disabled: translateBoundPos === -translatePos,
238
+ icon: showHorizontal ? /*#__PURE__*/React.createElement(RightOutlined, null) : /*#__PURE__*/React.createElement(DownOutlined, null),
176
239
  onClick: function onClick() {
177
- if (scrollRef && innerRef) {
178
- var scrollWidth = scrollRef.clientWidth;
179
- var innerWidth = innerRef.clientWidth;
180
- var canScrollWidth = scrollWidth - innerWidth + translateX;
181
- var moveWidth = canScrollWidth < innerWidth ? canScrollWidth : innerWidth;
182
- setTranslateX(function (prev) {
183
- return prev - moveWidth;
184
- });
185
- }
186
- }
187
- }), showScrollBtn && direction === 'vertical' && /*#__PURE__*/React.createElement(IconButton, {
188
- effect: true,
189
- className: prefixCls + "__down-btn",
190
- icon: /*#__PURE__*/React.createElement(DownOutlined, null),
191
- onClick: function onClick() {
192
- if (scrollRef && innerRef) {
193
- var canScroll = scrollRef.clientHeight - innerRef.clientHeight + translateY;
194
- var moveWidth = 0;
240
+ if (!scrollElement) return;
241
+ if (!innerElement) return; // 向后面滚动
195
242
 
196
- if (canScroll >= innerRef.clientHeight) {
197
- moveWidth = innerRef.clientHeight;
198
- } else {
199
- moveWidth = canScroll;
200
- }
201
-
202
- setTranslateY(translateY - moveWidth);
203
- }
243
+ var scrollSize = getClientSize(scrollElement);
244
+ var innerSize = getClientSize(innerElement);
245
+ var canScrollWidth = scrollSize - innerSize + translatePos;
246
+ var moveWidth = canScrollWidth < innerSize ? canScrollWidth : innerSize;
247
+ setTranslatePos(function (prev) {
248
+ return prev - moveWidth;
249
+ });
204
250
  }
205
- }), editable ? /*#__PURE__*/React.createElement(IconButton, {
251
+ }) : null, editable ? /*#__PURE__*/React.createElement(IconButton, {
206
252
  icon: /*#__PURE__*/React.createElement(PlusOutlined, null),
207
253
  className: prefixCls + "__add-btn",
208
254
  onClick: onAdd
@@ -9,6 +9,8 @@
9
9
  */
10
10
  import { __rest } from 'tslib';
11
11
  import React from 'react';
12
+ import { filterProps } from '@hi-ui/react-utils';
13
+ var omitProps = ['tabId', 'tabTitle', 'disabled', 'tabDesc', 'closeable'];
12
14
 
13
15
  var TabPane = function TabPane(_a) {
14
16
  var children = _a.children,
@@ -17,10 +19,11 @@ var TabPane = function TabPane(_a) {
17
19
  active = _a.active,
18
20
  rest = __rest(_a, ["children", "className", "style", "active"]);
19
21
 
22
+ var htmlProps = filterProps(rest, omitProps);
20
23
  return /*#__PURE__*/React.createElement("div", Object.assign({
21
24
  style: style,
22
25
  className: className
23
- }, rest), active ? children : null);
26
+ }, htmlProps), active ? children : null);
24
27
  };
25
28
 
26
29
  export { TabPane };
@@ -0,0 +1,54 @@
1
+ /** @LICENSE
2
+ * @hi-ui/tabs
3
+ * https://github.com/XiaoMi/hiui/tree/master/packages/ui/tabs#readme
4
+ *
5
+ * Copyright (c) HIUI <mi-hiui@xiaomi.com>.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */
10
+ import { useRef, useEffect } from 'react';
11
+ import { useLatestRef, useLatestCallback } from '@hi-ui/use-latest';
12
+ import { useUnmountEffect } from '@hi-ui/use-unmount-effect';
13
+ /**
14
+ * 响应式监听元素的 size 变化
15
+ */
16
+
17
+ var useResizeObserver = function useResizeObserver(_ref) {
18
+ var element = _ref.element,
19
+ onResize = _ref.onResize,
20
+ _ref$initialSize = _ref.initialSize,
21
+ initialSize = _ref$initialSize === void 0 ? 0 : _ref$initialSize,
22
+ getSize = _ref.getSize;
23
+ var getSizeLatestRef = useLatestRef(getSize);
24
+ var onResizeLatest = useLatestCallback(onResize);
25
+ var prevSizeRef = useRef(initialSize);
26
+ var unmountRef = useUnmountEffect();
27
+ useEffect(function () {
28
+ if (element) {
29
+ var _resizeObserver = new ResizeObserver(function () {
30
+ if (unmountRef.current) return;
31
+ var getSize = getSizeLatestRef.current;
32
+
33
+ if (element) {
34
+ if (getSize) {
35
+ var nextSize = getSize(element);
36
+
37
+ if (prevSizeRef.current !== nextSize) {
38
+ prevSizeRef.current = nextSize;
39
+ onResizeLatest(element, nextSize);
40
+ }
41
+ } else {
42
+ onResizeLatest(element);
43
+ }
44
+ }
45
+ });
46
+
47
+ _resizeObserver.observe(element);
48
+ }
49
+
50
+ return function () {};
51
+ }, [element, getSizeLatestRef, onResizeLatest, unmountRef]);
52
+ };
53
+
54
+ export { useResizeObserver };
@@ -0,0 +1,26 @@
1
+ /** @LICENSE
2
+ * @hi-ui/tabs
3
+ * https://github.com/XiaoMi/hiui/tree/master/packages/ui/tabs#readme
4
+ *
5
+ * Copyright (c) HIUI <mi-hiui@xiaomi.com>.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */
10
+ import { isArrayNonEmpty } from '@hi-ui/type-assertion';
11
+
12
+ var getNextTabId = function getNextTabId(data, tabId) {
13
+ if (isArrayNonEmpty(data)) {
14
+ var itemIndex = data.findIndex(function (item) {
15
+ return item.tabId === tabId;
16
+ });
17
+
18
+ if (itemIndex !== -1 && data[itemIndex + 1]) {
19
+ return data[itemIndex + 1].tabId;
20
+ }
21
+ }
22
+
23
+ return null;
24
+ };
25
+
26
+ export { getNextTabId };
@@ -1,11 +1,11 @@
1
1
  import React from 'react';
2
2
  export declare const TabInk: React.FC<TabInkProps>;
3
3
  interface TabInkProps {
4
- disabled?: boolean;
5
4
  prefixCls?: string;
6
- itemRef: HTMLElement;
7
- tabListRef: HTMLElement;
5
+ disabled?: boolean;
6
+ activeItemElement: HTMLElement | null;
8
7
  direction: 'vertical' | 'horizontal';
9
- translate: number;
8
+ activeTabId: React.ReactText;
9
+ getTabOffset: (tabId: React.ReactText) => number;
10
10
  }
11
11
  export {};
@@ -0,0 +1,11 @@
1
+ interface UseResizeObserverProps {
2
+ element: HTMLElement | null;
3
+ onResize: (element: HTMLElement, size?: any) => void;
4
+ initialSize?: any;
5
+ getSize?: (element: HTMLElement) => any;
6
+ }
7
+ /**
8
+ * 响应式监听元素的 size 变化
9
+ */
10
+ export declare const useResizeObserver: ({ element, onResize, initialSize, getSize, }: UseResizeObserverProps) => void;
11
+ export {};
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { TabPaneProps } from '../TabPane';
3
+ export declare const getNextTabId: (data: TabPaneProps[], tabId: React.ReactText) => React.ReactText | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hi-ui/tabs",
3
- "version": "4.0.0-beta.18",
3
+ "version": "4.0.0-beta.20",
4
4
  "description": "A sub-package for @hi-ui/hiui.",
5
5
  "keywords": [],
6
6
  "author": "HIUI <mi-hiui@xiaomi.com>",
@@ -47,10 +47,13 @@
47
47
  "@hi-ui/core": "^4.0.0-beta.9",
48
48
  "@hi-ui/core-css": "^4.0.0-beta.5",
49
49
  "@hi-ui/env": "^4.0.0-beta.1",
50
- "@hi-ui/icon-button": "^4.0.0-beta.10",
50
+ "@hi-ui/icon-button": "^4.0.0-beta.11",
51
51
  "@hi-ui/icons": "^4.0.0-beta.11",
52
+ "@hi-ui/react-utils": "^4.0.0-beta.6",
52
53
  "@hi-ui/type-assertion": "^4.0.0-beta.5",
53
- "@hi-ui/use-uncontrolled-state": "^4.0.0-beta.5"
54
+ "@hi-ui/use-latest": "^4.0.0-beta.5",
55
+ "@hi-ui/use-uncontrolled-state": "^4.0.0-beta.5",
56
+ "@hi-ui/use-unmount-effect": "^4.0.0-beta.6"
54
57
  },
55
58
  "peerDependencies": {
56
59
  "react": ">=16.8.6",
@@ -61,5 +64,5 @@
61
64
  "react": "^17.0.1",
62
65
  "react-dom": "^17.0.1"
63
66
  },
64
- "gitHead": "ef96654e009a72c3445d8df4a4182973631d00e9"
67
+ "gitHead": "a2d7267f383300bb5b96f8c156f54da4924b79dc"
65
68
  }