Skip to content

Commit

Permalink
Merge 69912c4 into b00312c
Browse files Browse the repository at this point in the history
  • Loading branch information
zombieJ committed Nov 28, 2018
2 parents b00312c + 69912c4 commit f816df7
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 38 deletions.
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -91,8 +91,10 @@
"dependencies": {
"babel-runtime": "6.x",
"classnames": "2.x",
"create-react-context": "^0.2.3",
"lodash": "^4.17.5",
"prop-types": "15.x",
"raf": "^3.4.1",
"rc-hammerjs": "~0.6.0",
"rc-util": "^4.0.4",
"warning": "^3.0.0"
Expand Down
46 changes: 46 additions & 0 deletions src/Sentinel.jsx
@@ -0,0 +1,46 @@
import React from 'react';
import PropTypes from 'prop-types';
import KeyCode from 'rc-util/lib/KeyCode';
import createReactContext from 'create-react-context';

const SentinelContext = createReactContext(null);
export const SentinelProvider = SentinelContext.Provider;
export const SentinelConsumer = SentinelContext.Consumer;

const sentinelStyle = { width: 0, height: 0, overflow: 'hidden' };
export default class Sentinel extends React.Component {
static propTypes = {
setRef: PropTypes.func,
prevElement: PropTypes.object,
nextElement: PropTypes.object,
};

onKeyDown = ({ target, which, shiftKey }) => {
const { nextElement, prevElement } = this.props;
if (which !== KeyCode.TAB || document.activeElement !== target) return;

// Tab next
if (!shiftKey && nextElement) {
nextElement.focus();
}

// Tab prev
if (shiftKey && prevElement) {
prevElement.focus();
}
};

render() {
const { setRef } = this.props;

return (
<div
tabIndex={0}
ref={setRef}
style={sentinelStyle}
onKeyDown={this.onKeyDown}
role="presentation"
/>
);
}
}
49 changes: 39 additions & 10 deletions src/TabPane.js
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { getDataAttr } from './utils';
import Sentinel, { SentinelConsumer } from './Sentinel';

export default class TabPane extends React.Component {
render() {
Expand All @@ -18,17 +19,45 @@ export default class TabPane extends React.Component {
[className]: className,
});
const isRender = destroyInactiveTabPane ? active : this._isActived;
const shouldRender = isRender || forceRender;

return (
<div
style={style}
role="tabpanel"
aria-hidden={active ? 'false' : 'true'}
className={cls}
id={id}
{...getDataAttr(restProps)}
>
{isRender || forceRender ? children : placeholder}
</div>
<SentinelConsumer>
{({ sentinelStart, sentinelEnd, setPanelSentinelStart, setPanelSentinelEnd }) => {
// Create sentinel
let panelSentinelStart;
let panelSentinelEnd;
if (active && shouldRender) {
panelSentinelStart = (
<Sentinel
setRef={setPanelSentinelStart}
prevElement={sentinelStart}
/>
);
panelSentinelEnd = (
<Sentinel
setRef={setPanelSentinelEnd}
nextElement={sentinelEnd}
/>
);
}

return (
<div
style={style}
role="tabpanel"
aria-hidden={active ? 'false' : 'true'}
className={cls}
id={id}
{...getDataAttr(restProps)}
>
{panelSentinelStart}
{shouldRender ? children : placeholder}
{panelSentinelEnd}
</div>
);
}}
</SentinelConsumer>
);
}
}
Expand Down
128 changes: 100 additions & 28 deletions src/Tabs.js
Expand Up @@ -3,7 +3,9 @@ import PropTypes from 'prop-types';
import KeyCode from './KeyCode';
import TabPane from './TabPane';
import classnames from 'classnames';
import raf from 'raf';
import { getDataAttr } from './utils';
import Sentinel, { SentinelProvider } from './Sentinel';

function noop() {
}
Expand Down Expand Up @@ -54,6 +56,10 @@ export default class Tabs extends React.Component {
}
}

componentWillUnmount() {
raf.cancel(this.sentinelId);
}

onTabClick = (activeKey, e) => {
if (this.tabBar.props.onTabClick) {
this.tabBar.props.onTabClick(activeKey, e);
Expand All @@ -74,6 +80,35 @@ export default class Tabs extends React.Component {
}
}

onScroll = ({ target, currentTarget }) => {
if (target === currentTarget && target.scrollLeft > 0) {
target.scrollLeft = 0;
}
};

// Sentinel for tab index
setSentinelStart = (node) => {
this.sentinelStart = node;
};

setSentinelEnd = (node) => {
this.sentinelEnd = node;
};

setPanelSentinelStart = (node) => {
if (node !== this.panelSentinelStart) {
this.updateSentinelContext();
}
this.panelSentinelStart = node;
};

setPanelSentinelEnd = (node) => {
if (node !== this.panelSentinelEnd) {
this.updateSentinelContext();
}
this.panelSentinelEnd = node;
};

setActiveKey = (activeKey) => {
if (this.state.activeKey !== activeKey) {
if (!('activeKey' in this.props)) {
Expand Down Expand Up @@ -111,6 +146,13 @@ export default class Tabs extends React.Component {
return ret;
}

updateSentinelContext() {
raf.cancel(this.sentinelId);
this.sentinelId = raf(() => {
this.forceUpdate();
});
}

render() {
const props = this.props;
const {
Expand All @@ -129,38 +171,68 @@ export default class Tabs extends React.Component {
});

this.tabBar = renderTabBar();
const contents = [
React.cloneElement(this.tabBar, {
prefixCls,
navWrapper,
key: 'tabBar',
onKeyDown: this.onNavKeyDown,
tabBarPosition,
onTabClick: this.onTabClick,
panels: props.children,
activeKey: this.state.activeKey,
}),
React.cloneElement(renderTabContent(), {
prefixCls,
tabBarPosition,
activeKey: this.state.activeKey,
destroyInactiveTabPane,
children: props.children,
onChange: this.setActiveKey,
key: 'tabContent',
}),
];

const tabBar = React.cloneElement(this.tabBar, {
prefixCls,
navWrapper,
key: 'tabBar',
onKeyDown: this.onNavKeyDown,
tabBarPosition,
onTabClick: this.onTabClick,
panels: props.children,
activeKey: this.state.activeKey,
});

const tabContent = React.cloneElement(renderTabContent(), {
prefixCls,
tabBarPosition,
activeKey: this.state.activeKey,
destroyInactiveTabPane,
children: props.children,
onChange: this.setActiveKey,
key: 'tabContent',
});

const sentinelStart = (
<Sentinel
key="sentinelStart"
setRef={this.setSentinelStart}
nextElement={this.panelSentinelStart}
/>
);
const sentinelEnd = (
<Sentinel
key="sentinelEnd"
setRef={this.setSentinelEnd}
prevElement={this.panelSentinelEnd}
/>
);

const contents = [];
if (tabBarPosition === 'bottom') {
contents.reverse();
contents.push(sentinelStart, tabContent, sentinelEnd, tabBar);
} else {
contents.push(tabBar, sentinelStart, tabContent, sentinelEnd);
}

return (
<div
className={cls}
style={props.style}
{...getDataAttr(restProps)}
<SentinelProvider
value={{
sentinelStart: this.sentinelStart,
sentinelEnd: this.sentinelEnd,
setPanelSentinelStart: this.setPanelSentinelStart,
setPanelSentinelEnd: this.setPanelSentinelEnd,
}}
>
{contents}
</div>
<div
className={cls}
style={props.style}
{...getDataAttr(restProps)}
onScroll={this.onScroll}
>
{contents}
</div>
</SentinelProvider>
);
}
}
Expand Down
28 changes: 28 additions & 0 deletions tests/__snapshots__/index.spec.js.snap
Expand Up @@ -76,6 +76,13 @@ exports[`rc-tabs should render Tabs with correct DOM structure 1`] = `
</div>
</div>
</div>
</div>
<div
role="presentation"
style="width:0;height:0;overflow:hidden"
tabindex="0"
>
</div>
<div
class="rc-tabs-content rc-tabs-content-animated"
Expand All @@ -93,7 +100,21 @@ exports[`rc-tabs should render Tabs with correct DOM structure 1`] = `
class="rc-tabs-tabpane rc-tabs-tabpane-active"
role="tabpanel"
>
<div
role="presentation"
style="width:0;height:0;overflow:hidden"
tabindex="0"
>
</div>
second
<div
role="presentation"
style="width:0;height:0;overflow:hidden"
tabindex="0"
>
</div>
</div>
<div
aria-hidden="true"
Expand All @@ -103,5 +124,12 @@ exports[`rc-tabs should render Tabs with correct DOM structure 1`] = `
</div>
</div>
<div
role="presentation"
style="width:0;height:0;overflow:hidden"
tabindex="0"
>
</div>
</div>
`;
28 changes: 28 additions & 0 deletions tests/__snapshots__/swipe.spec.js.snap
Expand Up @@ -132,6 +132,13 @@ exports[`rc-swipeable-tabs should render Slider with correct DOM structure 1`] =
</div>
</div>
</div>
</div>
<div
role="presentation"
style="width:0;height:0;overflow:hidden"
tabindex="0"
>
</div>
<div
class="rc-tabs-content rc-tabs-content-animated"
Expand Down Expand Up @@ -198,11 +205,25 @@ exports[`rc-swipeable-tabs should render Slider with correct DOM structure 1`] =
class="rc-tabs-tabpane rc-tabs-tabpane-active"
role="tabpanel"
>
<div
role="presentation"
style="width:0;height:0;overflow:hidden"
tabindex="0"
>
</div>
<div
style="display:flex;align-items:center;justify-content:center;height:5rem;background-color:#fff"
>
选项8内容
</div>
<div
role="presentation"
style="width:0;height:0;overflow:hidden"
tabindex="0"
>
</div>
</div>
<div
aria-hidden="true"
Expand All @@ -219,6 +240,13 @@ exports[`rc-swipeable-tabs should render Slider with correct DOM structure 1`] =
</div>
</div>
<div
role="presentation"
style="width:0;height:0;overflow:hidden"
tabindex="0"
>
</div>
</div>
</div>
`;

0 comments on commit f816df7

Please sign in to comment.