Skip to content

Commit 986178a

Browse files
committed
fix(Tab): slide related issues
1 parent 788a0d0 commit 986178a

File tree

6 files changed

+275
-77
lines changed

6 files changed

+275
-77
lines changed

docs/tab/demo/excess-mode.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function onClick(key) {
4343

4444
ReactDOM.render(<div className="fusion-demo" style={{ maxWidth: '520px' }}>
4545
<div className="demo-item-title">Dropdown mode</div>
46-
<Tab excessMode="dropdown">
46+
<Tab excessMode="dropdown" popupProps={{style: {maxHeight: 400, overflowY: 'scroll'}}}>
4747
{
4848
tabs.map(item => <Tab.Item key={item.key} title={item.tab} onClick={onClick}>{item.tab} content, content, content</Tab.Item>)
4949
}

docs/tab/demo/extra.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
- order: 11
44

5-
通过 `extra` 属性添加附加内容,请确保只在有限选项卡的情况下才使用附加内容。
5+
通过 `extra` 属性添加附加内容,请确保只在有限选项卡的情况下才使用附加内容, 该功能在选项卡溢出时会和溢出导航的按钮冲突
66

77
:::lang=en-us
88
# Extra
99

1010
- order: 11
1111

12-
Pass your custom contents to `extra`.
12+
Pass your custom contents to `extra`, please consider using this when the tab-items are limited, since it is not designed to be used in combination with excess-mode.
1313

1414
:::
1515

docs/tab/index.en-us.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Disable animation with `animation={false}`
3535
| navClassName | Custom className of nav | String | - |
3636
| contentStyle | Custom style of content | Object | - |
3737
| contentClassName | Custom className of content | String | - |
38-
| extra | Extra content of tab | ReactNode | - |
38+
| extra | Extra content of tab, ensure the item won't excess when using this | ReactNode | - |
3939
| onClick | Callback when click tab | Function | () => {} |
4040
| onChange | Callback when active tab changes<br><br>**signature**:<br>Function(key: String/Number)) => void<br>**parameter**:<br>_key_: {String/Number)} theActiveKey | Function | () => {} |
4141
| onClose | Callback when close the tab<br><br>**signature**:<br>Function(key: String/Number)) => void<br>**parameter**:<br>_key_: {String/Number)} theClosedKey | Function | () => {} |

src/tab/rtl.scss

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
@import "../core/index-noreset.scss";
22
@import "scss/variable";
33

4-
#{$tab-prefix}[dir="rtl"] {
4+
#{$tab-prefix}[dir='rtl'] {
55
&.#{$css-prefix}medium {
66
& #{$tab-prefix}-nav-container-scrolling {
77
padding-left: $tab-nav-scroll-padding-right-m;
@@ -56,4 +56,14 @@
5656
right: auto;
5757
}
5858
}
59+
#{$tab-prefix}-text[dir='rtl'] > #{$tab-prefix}-bar {
60+
#{$tab-prefix}-tab {
61+
&:not(:last-child):after {
62+
content: ' ';
63+
position: absolute;
64+
left: 0;
65+
right: auto;
66+
}
67+
}
68+
}
5969

src/tab/tabs/nav.jsx

+97-53
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Icon from '../../icon';
66
import Overlay from '../../overlay';
77
import Menu from '../../menu';
88
import Animate from '../../animate';
9-
import { events, KEYCODE } from '../../util';
9+
import { events, KEYCODE, dom } from '../../util';
1010
import {
1111
triggerEvents,
1212
getOffsetLT,
@@ -42,8 +42,7 @@ class Nav extends React.Component {
4242
constructor(props, context) {
4343
super(props, context);
4444
this.state = {
45-
next: false,
46-
prev: false,
45+
showBtn: false,
4746
dropdownTabs: [],
4847
};
4948
this.offset = 0;
@@ -54,22 +53,17 @@ class Nav extends React.Component {
5453

5554
ctx.setSlideBtn();
5655
ctx.getDropdownItems(this.props);
57-
// 此处通过延时处理,屏蔽动画带来的定位不准确问题(由于要支持ie9,因此无法使用transitionend)
58-
clearTimeout(ctx.scrollTimer);
59-
ctx.scrollTimer = setTimeout(() => {
60-
ctx.scrollToActiveTab();
61-
}, 400);
62-
6356
events.on(window, 'resize', this.onWindowResized);
6457
}
6558

6659
componentDidUpdate() {
6760
const ctx = this;
68-
69-
clearTimeout(ctx.slideTimer);
70-
ctx.slideTimer = setTimeout(() => {
71-
ctx.setSlideBtn();
72-
}, 200);
61+
// 此处通过延时处理,屏蔽动画带来的定位不准确问题(由于要支持ie9,因此无法使用transitionend)
62+
clearTimeout(ctx.scrollTimer);
63+
ctx.scrollTimer = setTimeout(() => {
64+
ctx.scrollToActiveTab();
65+
}, 410); // transition-duration is set to be .4s, wait for the transition finishes before re-calc
66+
ctx.setSlideBtn();
7367
if (
7468
this.activeTab &&
7569
findDOMNode(this).contains(document.activeElement)
@@ -89,7 +83,7 @@ class Nav extends React.Component {
8983
* @param {bool} setActive need to check the active status or not
9084
*/
9185
setOffset(target, checkSlideBtn = true, setActive = true) {
92-
const { tabPosition } = this.props;
86+
const { tabPosition, rtl } = this.props;
9387
const navWH = getOffsetWH(this.nav, tabPosition);
9488
const wrapperWH = getOffsetWH(this.wrapper);
9589

@@ -107,15 +101,14 @@ class Nav extends React.Component {
107101
const activeTabOffset =
108102
getOffsetLT(this.activeTab) + relativeOffset;
109103
const wrapperOffset = getOffsetLT(this.wrapper);
110-
111-
if (
112-
// active tab partially in visible zone
113-
wrapperOffset + wrapperWH < activeTabOffset + activeTabWH &&
114-
activeTabOffset < wrapperOffset + wrapperWH
115-
) {
116-
target -= // Move more to make active tab totally in visible zone
117-
activeTabOffset + activeTabWH - (wrapperOffset + wrapperWH);
118-
}
104+
target = this._adjustTarget(
105+
wrapperOffset,
106+
wrapperWH,
107+
activeTabWH,
108+
activeTabOffset,
109+
rtl,
110+
target
111+
);
119112
}
120113

121114
if (this.offset !== target) {
@@ -171,6 +164,65 @@ class Nav extends React.Component {
171164
}
172165
}
173166

167+
_adjustTarget(
168+
wrapperOffset,
169+
wrapperWH,
170+
activeTabWH,
171+
activeTabOffset,
172+
rtl,
173+
target
174+
) {
175+
if (
176+
// active tab covers wrapper right edge
177+
wrapperOffset + wrapperWH < activeTabOffset + activeTabWH &&
178+
activeTabOffset < wrapperOffset + wrapperWH
179+
) {
180+
if (rtl) {
181+
target += // Move more to make active tab totally in visible zone
182+
activeTabOffset + activeTabWH - (wrapperOffset + wrapperWH);
183+
} else {
184+
target -=
185+
activeTabOffset +
186+
activeTabWH -
187+
(wrapperOffset + wrapperWH) +
188+
1;
189+
}
190+
191+
return target;
192+
}
193+
if (
194+
// active tab covers wrapper left edge
195+
wrapperOffset < activeTabOffset + activeTabWH &&
196+
activeTabOffset < wrapperOffset
197+
) {
198+
if (rtl) {
199+
target -= wrapperOffset - activeTabOffset + 1;
200+
} else {
201+
target += wrapperOffset - activeTabOffset;
202+
}
203+
return target;
204+
}
205+
return target;
206+
}
207+
208+
_setBtnStyle(prev, next) {
209+
if (this.prevBtn && this.nextBtn) {
210+
const cls = 'disabled';
211+
this.prevBtn.disabled = !prev;
212+
this.nextBtn.disabled = !next;
213+
if (prev) {
214+
dom.removeClass(this.prevBtn, cls);
215+
} else {
216+
dom.addClass(this.prevBtn, cls);
217+
}
218+
if (next) {
219+
dom.removeClass(this.nextBtn, cls);
220+
} else {
221+
dom.addClass(this.nextBtn, cls);
222+
}
223+
}
224+
}
225+
174226
setSlideBtn() {
175227
const { tabPosition } = this.props;
176228

@@ -185,7 +237,6 @@ class Nav extends React.Component {
185237
if (minOffset >= 0 || navWH <= navbarWH) {
186238
next = false;
187239
prev = false;
188-
this.setOffset(0, false); // no need to check slide again since this call is invoked from inside setSlideBtn
189240
} else if (this.offset < 0 && this.offset <= minOffset) {
190241
prev = true;
191242
next = false;
@@ -196,12 +247,12 @@ class Nav extends React.Component {
196247
prev = true;
197248
next = true;
198249
}
199-
200-
if (next !== this.state.next || prev !== this.state.prev) {
250+
if ((prev || next) !== this.state.showBtn) {
201251
this.setState({
202-
next,
203-
prev,
252+
showBtn: prev || next,
204253
});
254+
} else {
255+
this._setBtnStyle(prev, next);
205256
}
206257
}
207258

@@ -343,15 +394,7 @@ class Nav extends React.Component {
343394
) {
344395
return;
345396
}
346-
// if (activeTabOffset < wrapperOffset) {
347-
// target += wrapperOffset - activeTabOffset;
348-
// this.setOffset(target);
349-
// }
350-
if (wrapperOffset + wrapperWH < activeTabOffset + activeTabWH) {
351-
target -=
352-
activeTabOffset + activeTabWH - (wrapperOffset + wrapperWH);
353-
this.setOffset(target, true, false);
354-
}
397+
this.setOffset(target, true, true);
355398
};
356399

357400
onPrevClick = () => {
@@ -480,6 +523,14 @@ class Nav extends React.Component {
480523
this.activeTab = ref;
481524
};
482525

526+
prevBtnHandler = ref => {
527+
this.prevBtn = findDOMNode(ref);
528+
};
529+
530+
nextBtnHandler = ref => {
531+
this.nextBtn = findDOMNode(ref);
532+
};
533+
483534
render() {
484535
const {
485536
prefix,
@@ -498,39 +549,32 @@ class Nav extends React.Component {
498549
let prevButton;
499550
let restButton;
500551

501-
const showNextPrev = state.prev || state.next;
552+
const showNextPrev = state.showBtn;
502553

503554
if (
504555
excessMode === 'dropdown' &&
505-
state.next &&
556+
showNextPrev &&
506557
state.dropdownTabs.length
507558
) {
508559
restButton = this.renderDropdownTabs(state.dropdownTabs);
509560
prevButton = null;
510561
nextButton = null;
511562
} else if (showNextPrev) {
512-
const prevBtnCls = classnames({
513-
[`${prefix}tabs-btn-prev`]: 1,
514-
disabled: !state.prev,
515-
});
516-
const nextBtnCls = classnames({
517-
[`${prefix}tabs-btn-next`]: 1,
518-
disabled: !state.next,
519-
});
520-
521563
prevButton = (
522564
<button
523-
onClick={state.prev ? this.onPrevClick : noop}
524-
className={prevBtnCls}
565+
onClick={this.onPrevClick}
566+
className={`${prefix}tabs-btn-prev`}
567+
ref={this.prevBtnHandler}
525568
>
526569
<Icon rtl={rtl} type="arrow-left" />
527570
</button>
528571
);
529572

530573
nextButton = (
531574
<button
532-
onClick={state.next ? this.onNextClick : noop}
533-
className={nextBtnCls}
575+
onClick={this.onNextClick}
576+
className={`${prefix}tabs-btn-next`}
577+
ref={this.nextBtnHandler}
534578
>
535579
<Icon rtl={rtl} type="arrow-right" />
536580
</button>

0 commit comments

Comments
 (0)