From ad0bcc33d5535e06572c7146a24207093b0e0164 Mon Sep 17 00:00:00 2001 From: Javi Velasco Date: Tue, 22 Nov 2016 09:45:38 +0100 Subject: [PATCH] Inject and use FontIcon and minor rendering changes for Tabs --- components/tabs/Tabs.js | 151 +++++++++++++++++-------------------- components/tabs/index.js | 3 +- components/tabs/theme.scss | 19 +++-- spec/components/tabs.js | 2 +- 4 files changed, 85 insertions(+), 90 deletions(-) diff --git a/components/tabs/Tabs.js b/components/tabs/Tabs.js index ad0d3200..b9f6a7f6 100644 --- a/components/tabs/Tabs.js +++ b/components/tabs/Tabs.js @@ -2,10 +2,11 @@ import React, { Component, PropTypes } from 'react'; import classnames from 'classnames'; import { themr } from 'react-css-themr'; import { TABS } from '../identifiers.js'; +import InjectFontIcon from '../font_icon/FontIcon.js'; import InjectTab from './Tab.js'; import InjectTabContent from './TabContent.js'; -const factory = (Tab, TabContent) => { +const factory = (Tab, TabContent, FontIcon) => { class Tabs extends Component { static propTypes = { children: PropTypes.node, @@ -38,13 +39,12 @@ const factory = (Tab, TabContent) => { }; componentDidMount () { - !this.props.disableAnimatedBottomBorder && this.updatePointer(this.props.index); window.addEventListener('resize', this.handleResize); this.handleResize(); } componentWillReceiveProps (nextProps) { - !this.props.disableAnimatedBottomBorder && this.updatePointer(nextProps.index); + this.updatePointer(nextProps.index); } componentWillUnmount () { @@ -60,16 +60,52 @@ const factory = (Tab, TabContent) => { }; handleResize = () => { - if (this.resizeTimeout) { - clearTimeout(this.resizeTimeout); - } - this.resizeTimeout = setTimeout(this.handleResizeEnd, 50); + if (this.resizeTimeout) clearTimeout(this.resizeTimeout); + this.resizeTimeout = setTimeout(() => { + this.updatePointer(this.props.index); + this.updateArrows(); + }, 100); }; - handleResizeEnd = () => { - this.updatePointer(this.props.index); - this.updateArrows(); - }; + updatePointer = idx => { + if (this.navigationNode && this.navigationNode.children[idx]) { + const nav = this.navigationNode.getBoundingClientRect(); + const label = this.navigationNode.children[idx].getBoundingClientRect(); + const scrollLeft = this.navigationNode.scrollLeft; + this.setState({ + pointer: { + top: `${nav.height}px`, + left: `${label.left - nav.left + scrollLeft}px`, + width: `${label.width}px` + } + }); + } + } + + updateArrows = () => { + const nav = this.navigationNode; + this.setState({ + arrows: { + left: nav.scrollLeft > 0, + right: nav.scrollWidth > nav.clientWidth + && (nav.scrollLeft + nav.clientWidth) < nav.scrollWidth + } + }); + } + + scrollNavigation = (factor) => { + const oldScrollLeft = this.navigationNode.scrollLeft; + this.navigationNode.scrollLeft += factor * this.navigationNode.clientWidth; + if (this.navigationNode.scrollLeft !== oldScrollLeft) { + this.updateArrows(); + } + } + + scrollRight = () => + this.scrollNavigation(-1); + + scrollLeft = () => + this.scrollNavigation(+1); parseChildren () { const headers = []; @@ -89,51 +125,6 @@ const factory = (Tab, TabContent) => { return {headers, contents}; } - updatePointer (idx) { - clearTimeout(this.pointerTimeout); - this.pointerTimeout = setTimeout(() => { - const nav = this.refs.navigation.getBoundingClientRect(); - const label = this.refs.navigation.children[idx].getBoundingClientRect(); - const scrollLeft = this.refs.navigation.scrollLeft; - this.setState({ - pointer: { - top: `${nav.height}px`, - left: `${label.left - nav.left + scrollLeft}px`, - width: `${label.width}px` - } - }); - }, 20); - } - - updateArrows () { - clearTimeout(this.arrowsTimeout); - this.arrowsTimeout = setTimeout(() => { - const nav = this.refs.navigation; - this.setState({ - arrows: { - left: nav.scrollLeft > 0, - right: nav.scrollWidth > nav.clientWidth && (nav.scrollLeft + nav.clientWidth) < nav.scrollWidth - } - }); - }, 20); - } - - scrollNavigationLeft = () => { - const oldScrollLeft = this.refs.navigation.scrollLeft; - this.refs.navigation.scrollLeft -= this.refs.navigation.clientWidth; - if (this.refs.navigation.scrollLeft !== oldScrollLeft) { - this.updateArrows(); - } - } - - scrollNavigationRight = () => { - const oldScrollLeft = this.refs.navigation.scrollLeft; - this.refs.navigation.scrollLeft += this.refs.navigation.clientWidth; - if (this.refs.navigation.scrollLeft !== oldScrollLeft) { - this.updateArrows(); - } - } - renderHeaders (headers) { return headers.map((item, idx) => { return React.cloneElement(item, { @@ -160,37 +151,37 @@ const factory = (Tab, TabContent) => { }); }); - if (this.props.hideMode === 'display') { - return contentElements; - } - - return contentElements.filter((item, idx) => (idx === this.props.index)); + return this.props.hideMode === 'display' + ? contentElements + : contentElements.filter((item, idx) => (idx === this.props.index)); } render () { - const { className, theme, fixed, inverse } = this.props; + const { className, disableAnimatedBottomBorder, theme, fixed, inverse } = this.props; + const { left: hasLeftArrow, right: hasRightArrow } = this.state.arrows; const { headers, contents } = this.parseChildren(); - const classes = classnames( - theme.tabs, - className, - { - [theme.fixed]: fixed, - [theme.inverse]: inverse - } - ); + const classNamePointer = classnames(theme.pointer, { + [theme.disableAnimation]: disableAnimatedBottomBorder + }); + + const classNames = classnames(theme.tabs, { + [theme.fixed]: fixed, + [theme.inverse]: inverse + }, className); + return ( -
+
-
- keyboard_arrow_left -
-
{this.renderContents(contents)}
@@ -201,7 +192,7 @@ const factory = (Tab, TabContent) => { return Tabs; }; -const Tabs = factory(InjectTab, InjectTabContent); +const Tabs = factory(InjectTab, InjectTabContent, InjectFontIcon); export default themr(TABS)(Tabs); export { factory as tabsFactory }; export { Tabs }; diff --git a/components/tabs/index.js b/components/tabs/index.js index 85c995a6..72802054 100644 --- a/components/tabs/index.js +++ b/components/tabs/index.js @@ -3,12 +3,13 @@ import { TABS } from '../identifiers.js'; import { tabsFactory } from './Tabs.js'; import { TabContent } from './TabContent.js'; import { Tab } from './Tab.js'; +import FontIcon from '../font_icon/FontIcon.js'; import theme from './theme.scss'; const applyTheme = (Component) => themr(TABS, theme)(Component); const ThemedTabContent = applyTheme(TabContent); const ThemedTab = applyTheme(Tab); -const ThemedTabs = applyTheme(tabsFactory(ThemedTab, ThemedTabContent)); +const ThemedTabs = applyTheme(tabsFactory(ThemedTab, ThemedTabContent, FontIcon)); export { ThemedTab as Tab }; export { ThemedTabs as Tabs }; diff --git a/components/tabs/theme.scss b/components/tabs/theme.scss index 4c59913e..85ae18be 100644 --- a/components/tabs/theme.scss +++ b/components/tabs/theme.scss @@ -27,10 +27,6 @@ .arrow { padding: 0 $tab-label-h-padding; color: $tab-text-color; - - &.invisible { - visibility: hidden; - } } .arrowContainer { @@ -38,10 +34,6 @@ align-items: center; cursor: pointer; box-shadow: inset 0 -1px $tab-navigation-border-color; - - &.invisible { - display: none; - } } .label { @@ -55,15 +47,19 @@ transition-timing-function: $animation-curve-default; transition-duration: $animation-duration; transition-property: box-shadow, color; + &.active { color: $tab-text-color; } + &.disabled { opacity: $tab-label-disabled-opacity; } + &:not(.disabled) { cursor: pointer; } + &.hidden { display: none; } @@ -96,15 +92,21 @@ transition-timing-function: $animation-curve-default; transition-duration: $animation-duration; transition-property: left, width; + + &.disableAnimation { + transition: none; + } } .tab { display: flex; flex-direction: column; padding: $tab-label-v-padding $tab-label-h-padding; + &:not(.active) { display: none; } + &.active { display: flex; } @@ -124,6 +126,7 @@ .label { color: $tab-inverse-text-inactive-color; + &.active { color: $tab-inverse-text-color; } diff --git a/spec/components/tabs.js b/spec/components/tabs.js index a956388a..0ce6018d 100644 --- a/spec/components/tabs.js +++ b/spec/components/tabs.js @@ -29,7 +29,7 @@ class TabsTest extends React.Component {
Tabs

This tabs can be disabled or hidden

- + Primary content Secondary content Disabled content