diff --git a/app/extensions/brave/img/tabs/loading.svg b/app/extensions/brave/img/tabs/loading.svg new file mode 100644 index 00000000000..eb6ba3740a0 --- /dev/null +++ b/app/extensions/brave/img/tabs/loading.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/renderer/components/styles/animations.js b/app/renderer/components/styles/animations.js new file mode 100644 index 00000000000..38c8bddb349 --- /dev/null +++ b/app/renderer/components/styles/animations.js @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const spinKeyframes = { + 'from': { + transform: 'rotate(0deg)' + }, + 'to': { + transform: 'rotate(360deg)' + } +} + +module.exports = { + spinKeyframes +} diff --git a/app/renderer/components/styles/global.js b/app/renderer/components/styles/global.js index ed3961ac1cd..def03e9fe89 100644 --- a/app/renderer/components/styles/global.js +++ b/app/renderer/components/styles/global.js @@ -78,6 +78,9 @@ const globalStyles = { urlBarOutline: '#bbb', alphaWhite: 'rgba(255,255,255,0.8)' }, + filter: { + makeWhite: 'brightness(0) invert(1)' + }, radius: { borderRadius: '4px', borderRadiusTabs: '4px', diff --git a/app/renderer/components/tabContent.js b/app/renderer/components/tabContent.js index 7196b4635a7..044c1216b40 100644 --- a/app/renderer/components/tabContent.js +++ b/app/renderer/components/tabContent.js @@ -7,10 +7,12 @@ const ImmutableComponent = require('../../../js/components/immutableComponent') const {StyleSheet, css} = require('aphrodite/no-important') const globalStyles = require('./styles/global') const {isWindows} = require('../../common/lib/platformUtil') -const {getTextColorForBackground} = require('../../../js/lib/color') const {tabs} = require('../../../js/constants/config') const {hasBreakpoint, hasRelativeCloseIcon, - hasFixedCloseIcon, hasVisibleSecondaryIcon} = require('../lib/tabUtil') + hasFixedCloseIcon, hasVisibleSecondaryIcon, getTabIconColor} = require('../lib/tabUtil') +const {spinKeyframes} = require('./styles/animations') + +const loadingIconSvg = require('../../extensions/brave/img/tabs/loading.svg') const newSessionSvg = require('../../extensions/brave/img/tabs/new_session.svg') const privateSvg = require('../../extensions/brave/img/tabs/private.svg') const closeTabSvg = require('../../extensions/brave/img/tabs/close_btn_normal.svg') @@ -57,12 +59,6 @@ class Favicon extends ImmutableComponent { return !this.props.isLoading && this.props.tab.get('icon') } - get loadingIcon () { - return this.props.isLoading - ? globalStyles.appIcons.loading - : null - } - get defaultIcon () { return (!this.props.isLoading && !this.favicon) ? globalStyles.appIcons.defaultIcon @@ -80,18 +76,25 @@ class Favicon extends ImmutableComponent { render () { const iconStyles = StyleSheet.create({ - favicon: {backgroundImage: `url(${this.favicon})`} + favicon: {backgroundImage: `url(${this.favicon})`}, + loadingIconColor: { + // Don't change icon color unless when it should be white + filter: getTabIconColor(this.props) === 'white' ? globalStyles.filter.makeWhite : 'none' + } }) return !this.shouldHideFavicon ? + symbol={ + (this.props.isLoading && css(styles.loadingIcon, iconStyles.loadingIconColor)) || + this.defaultIcon + } /> : null } } @@ -166,10 +169,7 @@ class NewSessionIcon extends ImmutableComponent { } get iconColor () { - const themeColor = this.props.tab.get('themeColor') || this.props.tab.get('computedThemeColor') - return this.props.paintTabs && themeColor - ? getTextColorForBackground(themeColor) - : globalStyles.color.black100 + return getTabIconColor(this.props) } render () { @@ -206,22 +206,11 @@ class TabTitle extends ImmutableComponent { hasFixedCloseIcon(this.props) } - get themeColor () { - const themeColor = this.props.tab.get('themeColor') || this.props.tab.get('computedThemeColor') - const activeNonPrivateTab = !this.props.tab.get('isPrivate') && this.props.isActive - const isPrivateTab = this.props.tab.get('isPrivate') && (this.props.isActive || this.props.tab.get('hoverState')) - const defaultColor = isPrivateTab ? globalStyles.color.white100 : globalStyles.color.black100 - - return activeNonPrivateTab && this.props.paintTabs && !!themeColor - ? getTextColorForBackground(themeColor) - : defaultColor - } - render () { const titleStyles = StyleSheet.create({ gradientText: { backgroundImage: `-webkit-linear-gradient(left, - ${this.themeColor} 90%, ${globalStyles.color.almostInvisible} 100%)` + ${getTabIconColor(this.props)} 90%, ${globalStyles.color.almostInvisible} 100%)` } }) @@ -286,6 +275,14 @@ const styles = StyleSheet.create({ backgroundPosition: 'center center' }, + loadingIcon: { + backgroundImage: `url(${loadingIconSvg})`, + animationName: spinKeyframes, + animationTimingFunction: 'linear', + animationDuration: '1200ms', + animationIterationCount: 'infinite' + }, + audioIcon: { color: globalStyles.color.highlightBlue, fontSize: '16px' diff --git a/app/renderer/lib/tabUtil.js b/app/renderer/lib/tabUtil.js index ae0984966d5..4b167548a77 100644 --- a/app/renderer/lib/tabUtil.js +++ b/app/renderer/lib/tabUtil.js @@ -6,6 +6,7 @@ const styles = require('../components/styles/global') const frameStateUtil = require('../../../js/state/frameStateUtil') const settings = require('../../../js/constants/settings') const getSetting = require('../../../js/settings').getSetting +const {getTextColorForBackground} = require('../../../js/lib/color') /** * Get tab's breakpoint name for current tab size. @@ -69,6 +70,22 @@ module.exports.hasFixedCloseIcon = (props) => { return props.isActive && module.exports.hasBreakpoint(props, ['small', 'extraSmall']) } +/** + * Gets the icon color based on tab's background + * @param {Object} props - Object that hosts the tab props + * @returns {String} Contrasting color to use based on tab's color + */ +module.exports.getTabIconColor = (props) => { + const themeColor = props.tab.get('themeColor') || props.tab.get('computedThemeColor') + const activeNonPrivateTab = !props.tab.get('isPrivate') && props.isActive + const isPrivateTab = props.tab.get('isPrivate') && (props.isActive || props.tab.get('hoverState')) + const defaultColor = isPrivateTab ? styles.color.white100 : styles.color.black100 + + return activeNonPrivateTab && props.paintTabs && !!themeColor + ? getTextColorForBackground(themeColor) + : defaultColor +} + /** * Updates the tab page index to the specified frameProps * @param frameProps Any frame belonging to the page diff --git a/js/components/tab.js b/js/components/tab.js index c4a05413097..e769ee31864 100644 --- a/js/components/tab.js +++ b/js/components/tab.js @@ -324,6 +324,7 @@ class Tab extends ImmutableComponent { )}> ) - assert.equal(wrapper.props().symbol, globalStyles.appIcons.loading) + assert.equal(wrapper.props()['data-test-id'], 'loading') }) it('should not show favicon for new tab page', function () { const wrapper = shallow(