diff --git a/app/renderer/components/styles/animations.js b/app/renderer/components/styles/animations.js index b46c06aa250..c6eec1954f6 100644 --- a/app/renderer/components/styles/animations.js +++ b/app/renderer/components/styles/animations.js @@ -20,6 +20,10 @@ const opacityIncreaseKeyframes = { } } +const opacityIncreaseElementKeyframes = { + opacity: [0, 1] +} + // TODO: this could be a function with param included // to which property should be changed const widthIncreaseKeyframes = (start, end) => ({ @@ -31,6 +35,10 @@ const widthIncreaseKeyframes = (start, end) => ({ } }) +const widthIncreaseElementKeyframes = (start, end) => ({ + width: [start, end] +}) + const loaderAnimation = { '0': { transform: 'translate(0,0)' @@ -46,6 +54,8 @@ const loaderAnimation = { module.exports = { spinKeyframes, opacityIncreaseKeyframes, + opacityIncreaseElementKeyframes, widthIncreaseKeyframes, + widthIncreaseElementKeyframes, loaderAnimation } diff --git a/app/renderer/components/tabs/content/audioTabIcon.js b/app/renderer/components/tabs/content/audioTabIcon.js index 06c0cd67704..6c186f8931f 100644 --- a/app/renderer/components/tabs/content/audioTabIcon.js +++ b/app/renderer/components/tabs/content/audioTabIcon.js @@ -3,6 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const React = require('react') +const ReactDOM = require('react-dom') const {StyleSheet, css} = require('aphrodite/no-important') // Components @@ -18,7 +19,7 @@ const tabState = require('../../../../common/state/tabState') const windowActions = require('../../../../../js/actions/windowActions') // Styles -const {widthIncreaseKeyframes} = require('../../styles/animations') +const {widthIncreaseElementKeyframes} = require('../../styles/animations') const globalStyles = require('../../styles/global') const {theme} = require('../../styles/theme') @@ -26,6 +27,7 @@ class AudioTabIcon extends React.Component { constructor (props) { super(props) this.toggleMute = this.toggleMute.bind(this) + this.setRef = this.setRef.bind(this) } get audioIcon () { @@ -56,6 +58,46 @@ class AudioTabIcon extends React.Component { return props } + componentDidMount (props) { + this.transitionIfRequired() + } + + componentDidUpdate (prevProps) { + this.transitionIfRequired(prevProps) + } + + transitionIfRequired (prevProps) { + const shouldTransitionIn = ( + // need to have the element created already + this.element && + // no icon is showing if pinned tab + !this.props.isPinned && + // should show the icon + // TODO: if we want to animate the unmounting of the component (when + // audio is stopped), then we should use https://github.com/reactjs/react-transition-group + // For now, we'll just not do anything since we can't - the element + // will have already been removed + this.props.showAudioIcon && + // state has changed + (!prevProps || this.props.showAudioIcon !== prevProps.showAudioIcon) + ) + if (shouldTransitionIn) { + // TODO: measure element width if this ever becomes dynamic since + // it's not great to specify complex size logic in two places + // or animate a child element using transform: translateX + // in order to achieve the slide-in + const transitionKeyframes = widthIncreaseElementKeyframes(0, globalStyles.spacing.iconSize) + this.element.animate(transitionKeyframes, { + duration: 100, + easing: 'linear' + }) + } + } + + setRef (ref) { + this.element = ReactDOM.findDOMNode(ref) + } + render () { if (this.props.isPinned || !this.props.showAudioIcon) { return null @@ -66,19 +108,14 @@ class AudioTabIcon extends React.Component { className={css(styles.audioTab__icon)} symbol={this.audioIcon} onClick={this.toggleMute} + ref={this.setRef} /> } } const styles = StyleSheet.create({ audioTab__icon: { - width: 0, - animationName: widthIncreaseKeyframes(0, globalStyles.spacing.iconSize), - animationDelay: '50ms', - animationTimingFunction: 'linear', - animationDuration: '100ms', - animationFillMode: 'forwards', - + width: globalStyles.spacing.iconSize, overflow: 'hidden', margin: '0 -2px 0 2px', zIndex: globalStyles.zindex.zindexTabsAudioTopBorder, diff --git a/app/renderer/components/tabs/content/closeTabIcon.js b/app/renderer/components/tabs/content/closeTabIcon.js index 905c7b6535d..41226610e17 100644 --- a/app/renderer/components/tabs/content/closeTabIcon.js +++ b/app/renderer/components/tabs/content/closeTabIcon.js @@ -3,6 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const React = require('react') +const ReactDOM = require('react-dom') const {StyleSheet, css} = require('aphrodite/no-important') // Components @@ -18,13 +19,14 @@ const frameStateUtil = require('../../../../../js/state/frameStateUtil') // Styles const {theme} = require('../../styles/theme') const {spacing, zindex} = require('../../styles/global') -const {opacityIncreaseKeyframes} = require('../../styles/animations') +const {opacityIncreaseElementKeyframes} = require('../../styles/animations') const closeTabSvg = require('../../../../extensions/brave/img/tabs/close_btn.svg') class CloseTabIcon extends React.Component { constructor (props) { super(props) this.onDragStart = this.onDragStart.bind(this) + this.setRef = this.setRef.bind(this) } onDragStart (event) { @@ -46,6 +48,41 @@ class CloseTabIcon extends React.Component { return props } + componentDidMount (props) { + this.transitionIfRequired() + } + + componentDidUpdate (prevProps) { + this.transitionIfRequired(prevProps) + } + + transitionIfRequired (prevProps) { + const shouldTransitionIn = ( + // need to have the element created already + this.element && + // no icon is showing if pinned tab + !this.props.isPinned && + // should show the icon + // TODO: if we want to animate the unmounting of the component (when + // tab is unhovered), then we should use https://github.com/reactjs/react-transition-group + // For now, we'll just not do anything since we can't - the element + // will have already been removed + this.props.showCloseIcon && + // state has changed + (!prevProps || this.props.showCloseIcon !== prevProps.showCloseIcon) + ) + if (shouldTransitionIn) { + this.element.animate(opacityIncreaseElementKeyframes, { + duration: 200, + easing: 'linear' + }) + } + } + + setRef (ref) { + this.element = ReactDOM.findDOMNode(ref) + } + render () { if (this.props.isPinned || !this.props.showCloseIcon) { return null @@ -62,6 +99,7 @@ class CloseTabIcon extends React.Component { onClick={this.props.onClick} onDragStart={this.onDragStart} draggable='true' + ref={this.setRef} /> } } @@ -70,13 +108,7 @@ module.exports = ReduxComponent.connect(CloseTabIcon) const styles = StyleSheet.create({ closeTab__icon: { - opacity: 0, willChange: 'opacity', - animationName: opacityIncreaseKeyframes, - animationTimingFunction: 'linear', - animationDuration: '200ms', - animationDelay: '25ms', - animationFillMode: 'forwards', boxSizing: 'border-box', zIndex: zindex.zindexTabsThumbnail, diff --git a/app/renderer/components/tabs/content/favIcon.js b/app/renderer/components/tabs/content/favIcon.js index 15d7a16e406..4d2443645fd 100644 --- a/app/renderer/components/tabs/content/favIcon.js +++ b/app/renderer/components/tabs/content/favIcon.js @@ -3,6 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const React = require('react') +const ReactDOM = require('react-dom') const {StyleSheet, css} = require('aphrodite/no-important') // Components @@ -19,9 +20,14 @@ const tabState = require('../../../../common/state/tabState') const defaultIconSvg = require('../../../../extensions/brave/img/tabs/default.svg') const loadingIconSvg = require('../../../../extensions/brave/img/tabs/loading.svg') const {filter, color, spacing} = require('../../styles/global') -const {spinKeyframes, opacityIncreaseKeyframes} = require('../../styles/animations') +const {spinKeyframes, opacityIncreaseElementKeyframes} = require('../../styles/animations') class Favicon extends React.Component { + constructor (props) { + super(props) + this.setRef = this.setRef.bind(this) + } + mergeProps (state, ownProps) { const currentWindow = state.get('currentWindow') const tabId = ownProps.tabId @@ -47,6 +53,35 @@ class Favicon extends React.Component { : this.props.favicon || 'defaultIcon' } + componentDidMount (props) { + this.transitionInIfRequired() + } + + componentDidUpdate (prevProps) { + this.transitionInIfRequired(prevProps) + } + + transitionInIfRequired (prevProps) { + const shouldTransitionIn = ( + // need to have the element created already + this.element && + // we do not transition the loading animation + !this.props.tabLoading && + // only if the icon changes (or is new) + (!prevProps || this.props.favicon !== prevProps.favicon) + ) + if (shouldTransitionIn) { + this.element.animate(opacityIncreaseElementKeyframes, { + duration: 200, + easing: 'linear' + }) + } + } + + setRef (ref) { + this.element = ReactDOM.findDOMNode(ref) + } + render () { if (!this.props.isPinned && !this.props.showIcon) { return null @@ -69,6 +104,7 @@ class Favicon extends React.Component { !this.props.isPinned && this.props.showIconAtReducedSize && styles.icon_reducedSize )} style={instanceStyles} + ref={this.setRef} symbol={ this.props.tabLoading ? ( @@ -95,14 +131,7 @@ module.exports = ReduxComponent.connect(Favicon) const styles = StyleSheet.create({ icon: { - opacity: 0, willChange: 'opacity', - animationName: opacityIncreaseKeyframes, - animationDelay: '50ms', - animationTimingFunction: 'linear', - animationDuration: '200ms', - animationFillMode: 'forwards', - position: 'relative', boxSizing: 'border-box', width: spacing.iconSize, diff --git a/app/renderer/components/tabs/content/newSessionIcon.js b/app/renderer/components/tabs/content/newSessionIcon.js index 75c6b381b12..7260d960fba 100644 --- a/app/renderer/components/tabs/content/newSessionIcon.js +++ b/app/renderer/components/tabs/content/newSessionIcon.js @@ -3,6 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const React = require('react') +const ReactDOM = require('react-dom') const {StyleSheet, css} = require('aphrodite/no-important') // Components @@ -17,10 +18,15 @@ const frameStateUtil = require('../../../../../js/state/frameStateUtil') // Styles const globalStyles = require('../../styles/global') -const {opacityIncreaseKeyframes} = require('../../styles/animations') +const {opacityIncreaseElementKeyframes} = require('../../styles/animations') const newSessionSvg = require('../../../../extensions/brave/img/tabs/new_session.svg') class NewSessionIcon extends React.Component { + constructor (props) { + super(props) + this.setRef = this.setRef.bind(this) + } + mergeProps (state, ownProps) { const currentWindow = state.get('currentWindow') const tabId = ownProps.tabId @@ -37,6 +43,41 @@ class NewSessionIcon extends React.Component { return props } + componentDidMount (props) { + this.transitionInIfRequired() + } + + componentDidUpdate (prevProps) { + this.transitionInIfRequired(prevProps) + } + + transitionInIfRequired (prevProps) { + const shouldTransitionIn = ( + // need to have the element created already + this.element && + // no icon is showing if pinned tab + !this.props.isPinned && + // make sure icon should show + this.props.showPartitionIcon && this.props.partitionNumber !== 0 && + // only if the icon showing is a new state + // check the previous state to see if it was not showing + (!prevProps || !prevProps.showPartitionIcon || prevProps.partitionNumber === 0) + ) + if (prevProps) { + console.log({shouldTransitionIn, prevProps, props: this.props, element: this.element}) + } + if (shouldTransitionIn) { + this.element.animate(opacityIncreaseElementKeyframes, { + duration: 200, + easing: 'linear' + }) + } + } + + setRef (ref) { + this.element = ReactDOM.findDOMNode(ref) + } + render () { if ( this.props.isPinned || @@ -60,6 +101,7 @@ class NewSessionIcon extends React.Component { symbolContent={this.props.partitionNumber} l10nArgs={{partitionNumber: this.props.partitionNumber}} l10nId='sessionInfoTab' + ref={this.setRef} /> } } @@ -68,14 +110,7 @@ module.exports = ReduxComponent.connect(NewSessionIcon) const styles = StyleSheet.create({ newSession__icon: { - opacity: 0, willChange: 'opacity', - animationName: opacityIncreaseKeyframes, - animationDelay: '100ms', - animationTimingFunction: 'linear', - animationDuration: '200ms', - animationFillMode: 'forwards', - zIndex: globalStyles.zindex.zindexTabsThumbnail, boxSizing: 'border-box', display: 'flex', diff --git a/app/renderer/components/tabs/content/privateIcon.js b/app/renderer/components/tabs/content/privateIcon.js index 541564adc3f..3af4b00d78b 100644 --- a/app/renderer/components/tabs/content/privateIcon.js +++ b/app/renderer/components/tabs/content/privateIcon.js @@ -3,6 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const React = require('react') +const ReactDOM = require('react-dom') const {StyleSheet, css} = require('aphrodite/no-important') // Components @@ -17,10 +18,15 @@ const tabState = require('../../../../common/state/tabState') // Styles const {theme} = require('../../styles/theme') const globalStyles = require('../../styles/global') -const {opacityIncreaseKeyframes} = require('../../styles/animations') +const {opacityIncreaseElementKeyframes} = require('../../styles/animations') const privateSvg = require('../../../../extensions/brave/img/tabs/private.svg') class PrivateIcon extends React.Component { + constructor (props) { + super(props) + this.setRef = this.setRef.bind(this) + } + mergeProps (state, ownProps) { const currentWindow = state.get('currentWindow') const tabId = ownProps.tabId @@ -35,6 +41,35 @@ class PrivateIcon extends React.Component { return props } + componentDidMount (props) { + this.transitionInIfRequired() + } + + componentDidUpdate (prevProps) { + this.transitionInIfRequired(prevProps) + } + + transitionInIfRequired (prevProps) { + const shouldTransitionIn = ( + // need to have the element created already + this.element && + // should show the icon + this.props.showPrivateIcon && + // state has changed + (!prevProps || !prevProps.showPrivateIcon) + ) + if (shouldTransitionIn) { + this.element.animate(opacityIncreaseElementKeyframes, { + duration: 200, + easing: 'linear' + }) + } + } + + setRef (ref) { + this.element = ReactDOM.findDOMNode(ref) + } + render () { if (this.props.isPinned || !this.props.showPrivateIcon) { return null @@ -51,6 +86,7 @@ class PrivateIcon extends React.Component { return } } @@ -59,14 +95,7 @@ module.exports = ReduxComponent.connect(PrivateIcon) const styles = StyleSheet.create({ private__icon: { - opacity: 0, willChange: 'opacity', - animationName: opacityIncreaseKeyframes, - animationDelay: '100ms', - animationTimingFunction: 'linear', - animationDuration: '200ms', - animationFillMode: 'forwards', - zIndex: globalStyles.zindex.zindexTabsThumbnail, boxSizing: 'border-box', WebkitMaskRepeat: 'no-repeat',