diff --git a/app/common/state/tabContentState/titleState.js b/app/common/state/tabContentState/titleState.js index 6bdc5b75f47..1c21e294c35 100644 --- a/app/common/state/tabContentState/titleState.js +++ b/app/common/state/tabContentState/titleState.js @@ -8,6 +8,7 @@ const partitionState = require('../tabContentState/partitionState') const privateState = require('../tabContentState/privateState') const frameStateUtil = require('../../../../js/state/frameStateUtil') + const tabUIState = require('../tabUIState') // Utils const {isEntryIntersected} = require('../../../../app/renderer/lib/observerUtil') @@ -26,11 +27,13 @@ const isActive = frameStateUtil.isFrameKeyActive(state, frameKey) const isPartition = partitionState.isPartitionTab(state, frameKey) const isPrivate = privateState.isPrivateTab(state, frameKey) - const secondaryIconVisible = !isNewTabPage && (isPartition || isPrivate || isActive) + const secondaryIconVisible = !isNewTabPage && + (isPartition || isPrivate || isActive) && + tabUIState.showTabEndIcon(state, frameKey) // If title is being intersected by ~half with other icons visible // such as closeTab (activeTab) or session icons, do not show it - if (isEntryIntersected(state, 'tabs', intersection.at45) && secondaryIconVisible) { + if (isEntryIntersected(state, 'tabs', intersection.at46) && secondaryIconVisible) { return false } diff --git a/app/common/state/tabUIState.js b/app/common/state/tabUIState.js index 072ff4f0d04..55159538bc9 100644 --- a/app/common/state/tabUIState.js +++ b/app/common/state/tabUIState.js @@ -6,8 +6,6 @@ const settings = require('../../../js/constants/settings') // State helpers -const partitionState = require('../../common/state/tabContentState/partitionState') -const privateState = require('../../common/state/tabContentState/privateState') const closeState = require('../../common/state/tabContentState/closeState') const frameStateUtil = require('../../../js/state/frameStateUtil') @@ -20,7 +18,6 @@ const {getSetting} = require('../../../js/settings') // Styles const {intersection} = require('../../renderer/components/styles/global') -const {theme} = require('../../renderer/components/styles/theme') module.exports.getThemeColor = (state, frameKey) => { const frame = frameStateUtil.getFrameByKey(state, frameKey) @@ -76,7 +73,7 @@ module.exports.showTabEndIcon = (state, frameKey) => { return ( !closeState.hasFixedCloseIcon(state, frameKey) && !closeState.hasRelativeCloseIcon(state, frameKey) && - !isEntryIntersected(state, 'tabs', intersection.at40) + !isEntryIntersected(state, 'tabs', intersection.at46) ) } @@ -99,41 +96,3 @@ module.exports.centralizeTabIcons = (state, frameKey, isPinned) => { return isPinned || isEntryIntersected(state, 'tabs', intersection.at40) } - -module.exports.getTabEndIconBackgroundColor = (state, frameKey) => { - const frame = frameStateUtil.getFrameByKey(state, frameKey) - - if (frame == null) { - return false - } - - const themeColor = module.exports.getThemeColor(state, frameKey) - const isPrivate = privateState.isPrivateTab(state, frameKey) - const isPartition = partitionState.isPartitionTab(state, frameKey) - const isHover = frameStateUtil.getTabHoverState(state, frameKey) - const isActive = frameStateUtil.isFrameKeyActive(state, frameKey) - const hasCloseIcon = closeState.showCloseTabIcon(state, frameKey) - const isIntersecting = isEntryIntersected(state, 'tabs', intersection.at40) - - let backgroundColor = theme.tab.background - - if (isActive && themeColor) { - backgroundColor = themeColor - } - if (isActive && !themeColor) { - backgroundColor = theme.tab.active.background - } - if (isIntersecting) { - backgroundColor = 'transparent' - } - if (!isActive && isPrivate) { - backgroundColor = theme.tab.private.background - } - if ((isActive || isHover) && isPrivate) { - backgroundColor = theme.tab.active.private.background - } - - return isPartition || isPrivate || hasCloseIcon - ? `linear-gradient(to left, ${backgroundColor} 10px, transparent 40px)` - : `linear-gradient(to left, ${backgroundColor} 0, transparent 12px)` -} diff --git a/app/extensions/brave/img/tabs/close_btn.svg b/app/extensions/brave/img/tabs/close_btn.svg deleted file mode 100644 index 926636ea455..00000000000 --- a/app/extensions/brave/img/tabs/close_btn.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/renderer/components/frame/frame.js b/app/renderer/components/frame/frame.js index 1ce7897ee17..230de5213a8 100644 --- a/app/renderer/components/frame/frame.js +++ b/app/renderer/components/frame/frame.js @@ -869,12 +869,14 @@ class Frame extends React.Component { const contextMenu = currentWindow.get('contextMenuDetail') const tab = tabId && tabId > -1 && tabState.getByTabId(state, tabId) + const previewFrameKey = currentWindow.get('previewFrameKey') + const props = {} // used in renderer props.transitionState = ownProps.transitionState props.partition = frameStateUtil.getPartition(frame) props.isFullScreen = frame.get('isFullScreen') - props.isPreview = frame.get('key') === currentWindow.get('previewFrameKey') + props.isPreview = frame.get('key') === previewFrameKey props.isActive = frameStateUtil.isFrameKeyActive(currentWindow, frame.get('key')) props.showFullScreenWarning = frame.get('showFullScreenWarning') props.location = location @@ -956,8 +958,7 @@ class Frame extends React.Component { }
{ this.webviewContainer = node }} className={cx({ - webviewContainer: true, - isPreview: this.props.isPreview + webviewContainer: true })} /> { diff --git a/app/renderer/components/main/main.js b/app/renderer/components/main/main.js index 8dd21193cb1..b050ece37a9 100644 --- a/app/renderer/components/main/main.js +++ b/app/renderer/components/main/main.js @@ -574,6 +574,7 @@ class Main extends React.Component { item.get('frameOrigin') ? activeOrigin === item.get('frameOrigin') : true).size > 0 props.showFindBar = activeFrame.get('findbarShown') && !activeFrame.get('isFullScreen') props.sortedFrames = frameStateUtil.getSortedFrameKeys(currentWindow) + props.hasFramePreview = currentWindow.get('previewFrameKey') != null props.showDownloadBar = currentWindow.getIn(['ui', 'downloadsToolbar', 'isVisible']) && state.get('downloads') && state.get('downloads').size > 0 props.title = activeFrame.get('title') @@ -736,7 +737,10 @@ class Main extends React.Component { }
- + { this.props.sortedFrames.map((frameKey) => } @@ -116,12 +118,12 @@ class AudioTabIcon extends React.Component { const styles = StyleSheet.create({ icon_audio: { overflow: 'hidden', - margin: '0 -2px 0 2px', + margin: '1px -2px 0 2px', // get centered with funky font awesome sizing color: theme.tab.icon.audio.color, - fontSize: '13px', - - // Override default properties - zIndex: globalStyles.zindex.zindexTabsAudioTopBorder + fontSize: '14px', + ':hover': { + color: theme.tab.icon.audio.hoverColor + } } }) diff --git a/app/renderer/components/tabs/content/closeTabIcon.js b/app/renderer/components/tabs/content/closeTabIcon.js index d6e90ee2d8c..11b1917648a 100644 --- a/app/renderer/components/tabs/content/closeTabIcon.js +++ b/app/renderer/components/tabs/content/closeTabIcon.js @@ -4,11 +4,10 @@ const React = require('react') const ReactDOM = require('react-dom') -const {StyleSheet} = require('aphrodite/no-important') +const {StyleSheet, css} = require('aphrodite/no-important') // Components const ReduxComponent = require('../../reduxComponent') -const TabIcon = require('./tabIcon') // State helpers const tabState = require('../../../../common/state/tabState') @@ -19,9 +18,6 @@ const frameStateUtil = require('../../../../../js/state/frameStateUtil') // Styles const globalStyles = require('../../styles/global') const {theme} = require('../../styles/theme') -const {opacityIncreaseElementKeyframes} = require('../../styles/animations') - -const closeTabSvg = require('../../../../extensions/brave/img/tabs/close_btn.svg') class CloseTabIcon extends React.Component { constructor (props) { @@ -49,88 +45,76 @@ 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) { + if (this.props.isPinned || !this.props.showCloseIcon) { // <-- comment out to always show, in order to view in inspector return null } - return + > + + + +
} } const styles = StyleSheet.create({ - icon_close: { - marginRight: globalStyles.spacing.defaultTabMargin, - backgroundImage: `url(${closeTabSvg})`, - - // Override default properties - backgroundSize: globalStyles.spacing.closeIconSize, + closeIcon: { + '--close-line-color': 'var(--tab-color)', + boxSizing: 'border-box', + alignSelf: 'center', + position: 'relative', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + borderRadius: theme.tab.closeButton.borderRadius, + background: theme.tab.closeButton.background, + marginRight: `calc(${globalStyles.spacing.defaultTabMargin} - 2px)`, width: globalStyles.spacing.closeIconSize, height: globalStyles.spacing.closeIconSize, - - // mask icon to gray to avoid calling another icon on hover - transition: 'filter 150ms linear', - filter: theme.tab.icon.close.filter, - + zIndex: globalStyles.zindex.zindexTabsThumbnail, ':hover': { - filter: 'none' + '--close-line-color': theme.tab.closeButton.hover.color, + background: theme.tab.closeButton.hover.background, + '--close-transit-duration': theme.tab.transitionDurationIn, + '--close-transit-timing': theme.tab.transitionEasingIn + }, + ':active': { + background: theme.tab.closeButton.active.background } }, - icon_close_centered: { + closeIcon__graphic: { + flex: 1 + }, + + closeIcon__line: { + stroke: 'var(--close-line-color)' + }, + + closeIcon_centered: { position: 'absolute', - left: 0, + left: `calc(50% - (${globalStyles.spacing.closeIconSize} / 2))`, right: 0, - top: 0, + top: `calc(50% - (${globalStyles.spacing.closeIconSize} / 2))`, bottom: 0, - margin: 'auto' + margin: 0 } }) diff --git a/app/renderer/components/tabs/content/favIcon.js b/app/renderer/components/tabs/content/favIcon.js index 9b9b00353e8..fc564aede3a 100644 --- a/app/renderer/components/tabs/content/favIcon.js +++ b/app/renderer/components/tabs/content/favIcon.js @@ -120,8 +120,7 @@ class Favicon extends React.Component { !this.props.favicon && css( styles.icon__symbol_default, - this.props.showIconAtReducedSize && styles.icon__symbol_default_reducedSize, - themeLight && styles.icon__symbol_default_colorLight + this.props.showIconAtReducedSize && styles.icon__symbol_default_reducedSize ) ) } /> @@ -130,7 +129,8 @@ class Favicon extends React.Component { const styles = StyleSheet.create({ icon_fav: { - backgroundImage: 'var(--faviconsrc)' + backgroundImage: 'var(--faviconsrc)', + overflow: 'visible' }, icon_favLight: { @@ -151,7 +151,6 @@ const styles = StyleSheet.create({ icon__symbol_loading: { position: 'absolute', left: 0, - willChange: 'transform', backgroundImage: `url(${loadingIconSvg})`, backgroundRepeat: 'no-repeat', backgroundPosition: 'top left', @@ -170,15 +169,11 @@ const styles = StyleSheet.create({ WebkitMaskPosition: 'center', WebkitMaskImage: `url(${defaultIconSvg})`, WebkitMaskSize: '14px', - backgroundColor: theme.tab.icon.symbol.default.backgroundColor + backgroundColor: 'var(--tab-default-icon-color)' }, icon__symbol_default_reducedSize: { WebkitMaskSize: '10px' - }, - - icon__symbol_default_colorLight: { - backgroundColor: theme.tab.icon.symbol.default.light.backgroundColor } }) diff --git a/app/renderer/components/tabs/content/newSessionIcon.js b/app/renderer/components/tabs/content/newSessionIcon.js index f759a1e3b4b..575e34db3f7 100644 --- a/app/renderer/components/tabs/content/newSessionIcon.js +++ b/app/renderer/components/tabs/content/newSessionIcon.js @@ -66,8 +66,8 @@ class NewSessionIcon extends React.Component { ) if (shouldTransitionIn) { this.element.animate(opacityIncreaseElementKeyframes, { - duration: 200, - easing: 'linear' + duration: 120, + easing: 'ease-out' }) } } @@ -85,19 +85,11 @@ class NewSessionIcon extends React.Component { return null } - const newSessionProps = StyleSheet.create({ - newSession__indicator: { - filter: this.props.isActive && this.props.textIsWhite - ? 'invert(100%)' - : 'none' - } - }) - return @@ -106,7 +98,12 @@ const styles = StyleSheet.create({ // Override default properties backgroundSize: 0, height: globalStyles.spacing.sessionIconSize, - width: globalStyles.spacing.sessionIconSize + width: globalStyles.spacing.sessionIconSize, + backgroundColor: theme.tab.icon.private.background.notActive + }, + + icon_private_active: { + backgroundColor: theme.tab.icon.private.background.active } }) diff --git a/app/renderer/components/tabs/content/tabIcon.js b/app/renderer/components/tabs/content/tabIcon.js index 2a2131ba979..586efdc05ab 100644 --- a/app/renderer/components/tabs/content/tabIcon.js +++ b/app/renderer/components/tabs/content/tabIcon.js @@ -32,6 +32,7 @@ class TabIcon extends ImmutableComponent { draggable={this.props.draggable} onClick={this.props.onClick} style={this.props.style} + title={this.props.title} {...altProps} > { @@ -67,6 +68,7 @@ const styles = StyleSheet.create({ alignSelf: 'center', alignItems: 'center', justifyContent: 'center', + flexShrink: 0, // must keep set width and height and not crop self // Default background properties backgroundSize: globalStyles.spacing.iconSize, @@ -74,7 +76,6 @@ const styles = StyleSheet.create({ backgroundRepeat: 'no-repeat', // Default animation properties - willChange: 'opacity', animationFillMode: 'forwards' }, diff --git a/app/renderer/components/tabs/content/tabTitle.js b/app/renderer/components/tabs/content/tabTitle.js index ecdbaf77b12..885c902277e 100644 --- a/app/renderer/components/tabs/content/tabTitle.js +++ b/app/renderer/components/tabs/content/tabTitle.js @@ -14,11 +14,6 @@ const frameStateUtil = require('../../../../../js/state/frameStateUtil') const tabUIState = require('../../../../common/state/tabUIState') const tabState = require('../../../../common/state/tabState') -// Utils -const platformUtil = require('../../../../common/lib/platformUtil') -const isWindows = platformUtil.isWindows() -const isDarwin = platformUtil.isDarwin() - // Styles const globalStyles = require('../../styles/global') @@ -29,14 +24,10 @@ class TabTitle extends React.Component { const frameKey = frameStateUtil.getFrameKeyByTabId(currentWindow, tabId) const props = {} - props.isWindows = isWindows - props.isDarwin = isDarwin props.isPinned = tabState.isTabPinned(state, tabId) props.showTabTitle = titleState.showTabTitle(currentWindow, frameKey) props.displayTitle = titleState.getDisplayTitle(currentWindow, frameKey) props.addExtraGutter = tabUIState.addExtraGutterToTitle(currentWindow, frameKey) - props.isTextWhite = tabUIState.checkIfTextColor(currentWindow, frameKey, 'white') - props.gradientColor = tabUIState.getTabEndIconBackgroundColor(currentWindow, frameKey) props.tabId = tabId return props @@ -46,22 +37,12 @@ class TabTitle extends React.Component { if (this.props.isPinned || !this.props.showTabTitle) { return null } - const perPageGradient = StyleSheet.create({ - tab__title_gradient: { - '::after': { - background: this.props.gradientColor - } - } - }) - - return
{this.props.displayTitle}
@@ -78,35 +59,58 @@ const styles = StyleSheet.create({ flex: 1, userSelect: 'none', fontSize: globalStyles.fontSize.tabTitle, - lineHeight: '1.6', + fontWeight: 400, minWidth: 0, // see https://stackoverflow.com/a/36247448/4902448 - marginLeft: '4px', + lineHeight: globalStyles.spacing.tabsToolbarHeight, + width: '-webkit-fill-available', + marginLeft: '6px', + // Fade any overflow text out, + // but use a technique which preserves: + // 1. Sub-pixel colored antialized text - e.g. background-clip: text does not use this. + // (with color - zoom in 20x on mac and you'll see) + // 2. Background and text color transition with no artifacts left over due to a + // pseudo element gradient fade which cannot transition (cannot transition linear gradient color) overflow: 'hidden', - - // this enable us to have gradient text - '::after': { - zIndex: globalStyles.zindex.zindexTabs, - content: '""', + position: 'relative', + color: 'transparent', + // the text, rendered as normal, but cut off early + '::before': { position: 'absolute', + display: 'block', + overflow: 'hidden', + top: 0, + left: 0, + right: 'calc(18% - 1px)', bottom: 0, + fontWeight: 'inherit', + content: 'attr(data-text)', + color: 'var(--tab-color)', + transition: `color var(--tab-transit-duration) var(--tab-transit-easing)` + }, + // the fade-out using background gradient clipped to text + // and only starting off where actual text is cut off + '::after': { + position: 'absolute', + display: 'block', + top: 0, + left: 0, right: 0, - width: '-webkit-fill-available', - height: '-webkit-fill-available', - // add a pixel margin so the box-shadow of the - // webview is not covered by the gradient - marginBottom: '1px' + bottom: 0, + fontWeight: 'inherit', + content: 'attr(data-text)', + // restrict background-size to a tiny portion of the text as background-clip: text means + // no sub-pixel antializing + background: `linear-gradient( + to right, + var(--tab-color) 0, + transparent 100% + ) right top / 18% 100% no-repeat`, + WebkitBackgroundClip: 'text !important', // !important is neccessary because aphrodite will put this at top of ruleset :-( + color: 'transparent', + transition: `background 0s var(--tab-transit-easing) var(--tab-transit-duration)` } }, - tab__title_isDarwin: { - fontWeight: '400' - }, - - tab__title_isWindows: { - fontWeight: '500', - fontSize: globalStyles.fontSize.tabTitle - }, - tab__title_extraGutter: { margin: '0 2px' } diff --git a/app/renderer/components/tabs/pinnedTabs.js b/app/renderer/components/tabs/pinnedTabs.js index c197afa6e1f..caf17fd74fa 100644 --- a/app/renderer/components/tabs/pinnedTabs.js +++ b/app/renderer/components/tabs/pinnedTabs.js @@ -104,7 +104,10 @@ class PinnedTabs extends React.Component { const styles = StyleSheet.create({ pinnedTabs: { height: '-webkit-fill-available', + display: 'flex', + alignItems: 'stretch', boxSizing: 'border-box', + position: 'relative', marginLeft: 0, marginTop: 0 } diff --git a/app/renderer/components/tabs/tab.js b/app/renderer/components/tabs/tab.js index fb292457484..c5406a825dc 100644 --- a/app/renderer/components/tabs/tab.js +++ b/app/renderer/components/tabs/tab.js @@ -4,6 +4,7 @@ const React = require('react') const {StyleSheet, css} = require('aphrodite/no-important') +const aphroditeInject = require('aphrodite/lib/inject') const Immutable = require('immutable') // Components @@ -28,6 +29,7 @@ const privateState = require('../../../common/state/tabContentState/privateState const audioState = require('../../../common/state/tabContentState/audioState') const tabUIState = require('../../../common/state/tabUIState') const tabState = require('../../../common/state/tabState') +const titleState = require('../../../common/state/tabContentState/titleState') // Constants const settings = require('../../../../js/constants/settings') @@ -38,13 +40,12 @@ const globalStyles = require('../styles/global') const {theme} = require('../styles/theme') // Utils -const {getTextColorForBackground} = require('../../../../js/lib/color') +const {backgroundRequiresLightText} = require('../../../../js/lib/color') const {isIntermediateAboutPage} = require('../../../../js/lib/appUrlUtil') const contextMenus = require('../../../../js/contextMenus') const dnd = require('../../../../js/dnd') const frameStateUtil = require('../../../../js/state/frameStateUtil') const {hasTabAsRelatedTarget} = require('../../lib/tabUtil') -const isWindows = require('../../../common/lib/platformUtil').isWindows() const {getCurrentWindowId} = require('../../currentWindow') const {setObserver} = require('../../lib/observerUtil') const UrlUtil = require('../../../../js/lib/urlutil') @@ -167,9 +168,14 @@ class Tab extends React.Component { // In case there's a tab preview happening, cancel the preview // when mouse is over a tab windowActions.setTabPageHoverState(this.props.tabPageIndex, false) + // cache offset position for hover radial grandient + if (this.tabNode) { + const tabBounds = this.tabNode.getBoundingClientRect() + this.tabOffsetLeft = tabBounds.left + } } - onMouseMove () { + onMouseMove (e) { // dispatch a message to the store so it can delay // and preview the tab based on mouse idle time clearTimeout(this.mouseTimeout) @@ -178,6 +184,17 @@ class Tab extends React.Component { windowActions.setTabHoverState(this.props.frameKey, true, true) }, getSetting(settings.TAB_PREVIEW_TIMING)) + // fancy radial gradient mouse tracker + if (this.elementRef) { + // only update position once per render frame + if (!this.nextFrameSetTabMouseX) { + var x = e.pageX - this.tabOffsetLeft + this.nextFrameSetTabMouseX = window.requestAnimationFrame(() => { + this.nextFrameSetTabMouseX = null + this.elementRef.style.setProperty('--tab-mouse-x', `${x}px`) + }) + } + } } onAuxClick (e) { @@ -216,6 +233,14 @@ class Tab extends React.Component { } componentDidMount () { + // Workaround the fact that aphrodite will not inject style rules until some time + // after css([rules]) is called. + // This causes CSS transitions to fire on the changes from the default values to the + // specified initial values, which definitely should not happen. + // Ensuring styles are written to DOM before this element is rendered + // means the element will not be rendered first with 0 rules. + // See https://codepen.io/petemill/pen/rdeqqv for a reproduction. + aphroditeInject.flushToStyleTag() // unobserve tabs that we don't need. This will // likely be made by onObserve method but added again as // just to double-check @@ -259,6 +284,7 @@ class Tab extends React.Component { const currentWindow = state.get('currentWindow') const frame = frameStateUtil.getFrameByKey(currentWindow, ownProps.frameKey) || Immutable.Map() const frameKey = ownProps.frameKey + const previewFrameKey = frameStateUtil.getPreviewFrameKey(currentWindow) const tabId = frame.get('tabId', tabState.TAB_ID_NONE) const isPinned = tabState.isTabPinned(state, tabId) const partOfFullPageSet = ownProps.partOfFullPageSet @@ -275,10 +301,12 @@ class Tab extends React.Component { props.frameKey = frameKey props.isPinnedTab = isPinned props.isPrivateTab = privateState.isPrivateTab(currentWindow, frameKey) - props.isActive = frameStateUtil.isFrameKeyActive(currentWindow, frameKey) + props.isActive = !!frameStateUtil.isFrameKeyActive(currentWindow, frameKey) + props.isPreview = frameKey === previewFrameKey /* || frameKey === 2 */ // <-- uncomment to force 1 preview tab for style inspection + props.anyTabIsPreview = previewFrameKey != null props.tabWidth = isPinned ? null : currentWindow.getIn(['ui', 'tabs', 'fixTabWidth']) props.themeColor = tabUIState.getThemeColor(currentWindow, frameKey) - props.title = frame.get('title') + props.title = titleState.getDisplayTitle(currentWindow, frameKey) props.tabPageIndex = frameStateUtil.getTabPageIndex(currentWindow) props.partOfFullPageSet = partOfFullPageSet props.showAudioTopBorder = audioState.showAudioTopBorder(currentWindow, frameKey, isPinned) @@ -306,6 +334,19 @@ class Tab extends React.Component { easing: 'ease-in-out' }) } + // no transition between: + // - active <-> inactive state + // - no theme color and first theme color + if (this.elementRef && prevProps && ( + prevProps.isActive !== this.props.isActive || + (!prevProps.themeColor && this.props.themeColor) + )) { + const className = css(styles.tabArea_instantTransition) + this.elementRef.classList.add(className) + window.requestAnimationFrame(() => { + this.elementRef.classList.remove(className) + }) + } } render () { @@ -313,8 +354,21 @@ class Tab extends React.Component { const isThemed = !this.props.isPrivateTab && this.props.isActive && this.props.themeColor const instanceStyles = { } if (isThemed) { - instanceStyles['--theme-color-fg'] = getTextColorForBackground(this.props.themeColor) + const lightText = backgroundRequiresLightText(this.props.themeColor) instanceStyles['--theme-color-bg'] = this.props.themeColor + // complementing foreground color + instanceStyles['--theme-color-fg'] = + lightText + ? theme.tab.active.colorLight + : theme.tab.active.colorDark + // complementing icon color + instanceStyles['--theme-color-default-icon'] = + lightText + ? theme.tab.defaultFaviconColorLight + : theme.tab.defaultFaviconColor + } + if (this.props.tabWidth) { + instanceStyles.flex = `0 0 ${this.props.tabWidth}px` } return
{ this.tabNode = node }} className={css( styles.tabArea__tab, - // tab icon only (on pinned tab / small tab) this.props.isPinnedTab && styles.tabArea__tab_pinned, this.props.centralizeTabIcons && styles.tabArea__tab_centered, - this.props.showAudioTopBorder && styles.tabArea__tab_audioTopBorder, - - // Windows specific style (color) - isWindows && styles.tabArea__tab_forWindows, - - // Set background-color and color to active tab and private tab - this.props.isActive && styles.tabArea__tab_active, - this.props.isPrivateTab && styles.tabArea__tab_private, - (this.props.isPrivateTab && this.props.isActive) && styles.tabArea__tab_private_active, - - // Apply themeColor if tab is active and not private - isThemed && styles.tabArea__tab_themed + this.props.showAudioTopBorder && styles.tabArea__tab_audioTopBorder )} - style={instanceStyles} data-test-id='tab' data-test-active-tab={this.props.isActive} data-test-pinned-tab={this.props.isPinnedTab} data-test-private-tab={this.props.isPrivateTab} data-frame-key={this.props.frameKey} draggable - title={this.props.title} + title={this.props.isPreview ? null : this.props.title} onDrag={this.onDrag} onDragStart={this.onDragStart} onDragEnd={this.onDragEnd} @@ -400,20 +451,61 @@ class Tab extends React.Component { const styles = StyleSheet.create({ tabArea: { boxSizing: 'border-box', - display: 'inline-block', position: 'relative', - verticalAlign: 'top', overflow: 'hidden', - height: '-webkit-fill-available', flex: '1 1 0', - + '--tab-margin-top': `-${theme.tab.borderWidth}px`, + // put the top border underneath tab-stip top border, and + // the left border underneath the previous tab's right border + margin: `var(--tab-margin-top) 0 0 -${theme.tab.borderWidth}px`, + border: `solid var(--tab-border-width, ${theme.tab.borderWidth}px) var(--tab-border-color)`, + // Border bottom is added to the tabArea__tab so that we do not get + // 45-degree angles when the bottom border is different color from the side borders. + // This could change when we can put the tab's background on this element, + // which can happen when tab dragging does not introduce a left/right 'space' when a tab + // is dragged over. + borderBottomWidth: `0 !important`, // aphrodite puts this above the border defined in the previous line, so use important :-( + zIndex: 100, + transformOrigin: 'bottom center', + minWidth: 0, + width: 0, // no-drag is applied to the button and tab area // ref: tabs__tabStrip__newTabButton on tabs.js WebkitAppRegion: 'no-drag', - // There's a special case that tabs should span the full width // if there are a full set of them. - maxWidth: '184px' + maxWidth: '184px', + // Use css variables for some transition options so that we can change them + // with other classes below, without having to re-define the whole property. + // Avoid aphrodite bug which will change css variables + // to --tab--webkit-transition-duration by calling it 'transit'. + '--tab-transit-duration': theme.tab.transitionDurationOut, + '--tab-transit-easing': theme.tab.transitionEasingOut, + // z-index should be delayed when it changes, so that preview tab stays on top until + // its scale transition has completed + '--tab-zindex-delay': theme.tab.transitionDurationOut, + transition: ['box-shadow', 'transform', 'border', 'margin', 'opacity'] + .map(prop => `${prop} var(--tab-transit-duration) var(--tab-transit-easing) 0s`) + .join(',') + + ', z-index var(--tab-zindex-duration, 0s) linear var(--tab-zindex-delay)', + '--tab-background': theme.tab.background, + '--tab-color': theme.tab.color, + '--tab-border-color': theme.tab.borderColor, + '--tab-background-hover': theme.tab.hover.background, + '--tab-default-icon-color': theme.tab.defaultFaviconColor, + ':hover': { + '--tab-background': `var(--tab-background-hover)`, + '--tab-color': `var(--tab-color-hover, ${theme.tab.color})`, + '--tab-default-icon-color': `var(--tab-default-icon-color-hover, ${theme.tab.defaultFaviconColor})`, + '--tab-border-color': `var(--tab-border-color-hover, ${theme.tab.borderColor})`, + '--tab-transit-duration': theme.tab.transitionDurationIn, + '--tab-transit-easing': theme.tab.transitionEasingIn, + '--tab-mouse-opacity': '1' + } + }, + + tabArea_instantTransition: { + '--tab-transit-duration': '0 !important' }, tabArea_dragging_left: { @@ -431,35 +523,117 @@ const styles = StyleSheet.create({ }, tabArea_isPinned: { - flex: 'initial' + flex: 'initial', + width: 'auto' }, tabArea_partOfFullPageSet: { maxWidth: 'initial' }, + tabArea_isActive: { + '--tab-color': theme.tab.active.colorDark, + '--tab-background': theme.tab.active.background, + '--tab-background-hover': theme.tab.hover.active.background, + '--tab-border-color-bottom': 'var(--tab-background)', + '--tab-mouse-opacity': '0 !important' + }, + + tabArea_isPreview: { + '--tab-background': theme.tab.preview.background, + '--tab-background-hover': theme.tab.preview.background, + '--tab-color': theme.tab.active.colorDark, + '--tab-color-hover': theme.tab.active.colorDark, + '--tab-border-color': theme.tab.preview.background, + '--tab-border-color-hover': theme.tab.preview.background, + zIndex: 110, + transform: `scale(${theme.tab.preview.scale})`, + boxShadow: theme.tab.preview.boxShadow, + // want the zindex to change immediately when previewing, but delay when un-previewing + '--tab-zindex-delay': '0s', + '--tab-zindex-duration': '0s', + '--tab-transit-duration': theme.tab.transitionDurationIn, + '--tab-transit-easing': theme.tab.transitionEasingIn + }, + + tabArea_siblingIsPreview: { + // when un-previewing, if there's still another tab previewed + // then we want to immediately have that tab on top of the last-previewed tab + // but have the last previewed tab wait to be underneath the next tab in the DOM + '--tab-zindex-delay': '0s', + '--tab-zindex-duration': '2s', + willChange: 'transform' + }, + + tabArea_isActive_siblingIsPreview: { + opacity: '.5' + }, + + tabArea_private: { + '--tab-background': theme.tab.private.background, + '--tab-background-hover': theme.tab.hover.private.background + }, + + tabArea_private_active: { + '--tab-background': theme.tab.active.private.background, + '--tab-color': theme.tab.active.private.color, + '--tab-background-hover': theme.tab.active.private.background, + '--tab-color-hover': theme.tab.active.private.color, + '--tab-default-icon-color': theme.tab.active.private.defaultFaviconColor + }, + + tabArea_themed: { + '--tab-color': `var(--theme-color-fg)`, + '--tab-background': `var(--theme-color-bg)`, + '--tab-background-hover': 'var(--theme-color-bg)', + '--tab-color-hover': 'var(--theme-color-fg)', + '--tab-default-icon-color': 'var(--theme-color-default-icon)' + }, + tabArea__tab: { - borderWidth: '0 1px 0 0', - borderStyle: 'solid', - borderColor: theme.tab.borderColor, boxSizing: 'border-box', - color: theme.tab.color, + background: `var(--tab-background, ${theme.tab.background})`, + // make sure the tab element which contains the background color + // has a new layer, so that the tab title text is rendered with subpixel antialiasing + // that knows about both the foreground and background colors display: 'flex', - transition: theme.tab.transition, - height: '-webkit-fill-available', - width: '-webkit-fill-available', + transition: ['background-color', 'color', 'border'] + .map(prop => `${prop} var(--tab-transit-duration) var(--tab-transit-easing) 0s`) + .join(','), + height: '100%', alignItems: 'center', justifyContent: 'space-between', position: 'relative', + color: `var(--tab-color, ${theme.tab.color})`, + borderBottom: `solid var(--tab-border-width, ${theme.tab.borderWidth}px) var(--tab-border-color-bottom, var(--tab-border-color))`, - ':hover': { - background: theme.tab.hover.background + // mouse-tracking radial gradient + '::before': { + content: '" "', + position: 'absolute', + left: 'var(--tab-mouse-x)', + top: 0, + bottom: 0, + width: 'calc(190px * var(--tab-mouse-opacity, 0))', + background: `radial-gradient( + circle farthest-corner, + var(--tab-background-hover), + transparent + )`, + filter: 'brightness(var(--tab-mouse-brightness, 106%))', + transform: 'translateX(-50%)', + transition: 'opacity var(--tab-transit-duration) ease, width 0s linear var(--tab-transit-duration)', + opacity: 'var(--tab-mouse-opacity, 0)' + }, + ':hover:before': { + // Show immediately, and fade-in opacity, + // but when leaving, wait for fade-out to finish before hiding. + transitionDelay: '0s' } }, tabArea__tab_audioTopBorder: { - '::before': { - zIndex: globalStyles.zindex.zindexTabsAudioTopBorder, + '::after': { content: `''`, display: 'block', position: 'absolute', @@ -467,7 +641,7 @@ const styles = StyleSheet.create({ left: 0, right: 0, height: '2px', - background: 'lightskyblue' + background: theme.tab.icon.audio.color } }, @@ -484,47 +658,6 @@ const styles = StyleSheet.create({ margin: 0 }, - // Windows specific style - tabArea__tab_forWindows: { - color: theme.tab.forWindows.color - }, - - tabArea__tab_active: { - background: theme.tab.active.background, - - ':hover': { - background: theme.tab.active.background - } - }, - - tabArea__tab_private: { - background: theme.tab.private.background, - - ':hover': { - color: theme.tab.active.private.color, - background: theme.tab.active.private.background - } - }, - - tabArea__tab_private_active: { - background: theme.tab.active.private.background, - color: theme.tab.active.private.color, - - ':hover': { - background: theme.tab.active.private.background - } - }, - - tabArea__tab_themed: { - color: `var(--theme-color-fg, inherit)`, - background: `var(--theme-color-bg, inherit)`, - - ':hover': { - color: `var(--theme-color-fg, inherit)`, - background: `var(--theme-color-bg, inherit)` - } - }, - // The sentinel is responsible to respond to tabs // intersection state. This is an empty hidden element // which `width` value shouldn't be changed unless the intersection @@ -538,18 +671,21 @@ const styles = StyleSheet.create({ }, tabArea__tab__identity: { - justifyContent: 'flex-start', - alignItems: 'center', - overflow: 'hidden', - display: 'flex', flex: '1', minWidth: '0', // @see https://bugzilla.mozilla.org/show_bug.cgi?id=1108514#c5 - margin: `0 ${globalStyles.spacing.defaultTabMargin}` + margin: `calc(var(--tab-border-width, 0) * -1px) 6px 0 ${globalStyles.spacing.defaultTabMargin}`, // bring the right margin closer as we do fade-out + // make sure title text is not cut off, but is also vertically centered + // by giving it full height of favicon + height: theme.tab.identityHeight, + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', + overflow: 'visible' }, tabArea__tab__identity_centered: { - justifyContent: 'center', flex: 'auto', + justifyContent: 'center', padding: 0, margin: 0 } diff --git a/app/renderer/components/tabs/tabs.js b/app/renderer/components/tabs/tabs.js index afee41bdf53..ef61aef08f0 100644 --- a/app/renderer/components/tabs/tabs.js +++ b/app/renderer/components/tabs/tabs.js @@ -149,6 +149,7 @@ class Tabs extends React.Component { const props = {} // used in renderer props.previewTabPageIndex = currentWindow.getIn(['ui', 'tabs', 'previewTabPageIndex']) + props.previewTabFrameKey = frameStateUtil.getPreviewFrameKey(currentWindow) props.currentTabs = currentTabs props.partOfFullPageSet = currentTabs.size === tabsPerTabPage props.onNextPage = currentTabs.size >= tabsPerTabPage && totalPages > pageIndex + 1 @@ -165,6 +166,7 @@ class Tabs extends React.Component { } render () { + const isTabPreviewing = this.props.previewTabFrameKey != null this.tabRefs = [] return
{ const div = document.createElement('div') div.style.color = color - return div.style.color.split('(')[1].split(')')[0].split(',') + const normalizedColor = div.style.color + if (typeof normalizedColor === 'string' && + normalizedColor.includes('(') && + normalizedColor.includes(')') && + normalizedColor.includes(',')) { + return div.style.color.split('(')[1].split(')')[0].split(',') + } + return null } -module.exports.getTextColorForBackground = (color) => { +module.exports.backgroundRequiresLightText = (color) => { // Calculate text color based on contrast with background: // https://24ways.org/2010/calculating-color-contrast/ const rgb = module.exports.parseColor(color) @@ -17,5 +24,32 @@ module.exports.getTextColorForBackground = (color) => { } const [r, g, b] = rgb const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000 - return yiq >= 128 ? 'black' : 'white' + return yiq < 128 +} + +module.exports.getTextColorForBackground = (color) => { + return module.exports.backgroundRequiresLightText(color) ? 'white' : 'black' +} + +module.exports.removeAlphaChannelForBackground = (color, bR, bG, bB) => { + const rgba = module.exports.parseColor(color) + if (!rgba) { + return null + } + // handle no alpha channel + if (rgba.length !== 4 || Number.isNaN(rgba[3])) { + return color + } + + // remove alpha channel, blending color with background + const [oR, oG, oB, oA] = rgba + + const newR = blendChannel(oR, bR, oA) + const newG = blendChannel(oG, bG, oA) + const newB = blendChannel(oB, bB, oA) + return `rgb(${newR}, ${newG}, ${newB})` +} + +function blendChannel (original, background, alpha) { + return Math.round((original * alpha) + ((1 - alpha) * background)) } diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js index baa9ab4ece9..3b15b436ddf 100644 --- a/js/stores/windowStore.js +++ b/js/stores/windowStore.js @@ -16,6 +16,7 @@ const messages = require('../constants/messages') const debounce = require('../lib/debounce') const getSetting = require('../settings').getSetting const UrlUtil = require('../lib/urlutil') +const color = require('../lib/color') const {l10nErrorText} = require('../../app/common/lib/httpUtil') const { makeImmutable } = require('../../app/common/state/immutableUtil') const {aboutUrls, getTargetAboutUrl, newFrameUrl} = require('../lib/appUrlUtil') @@ -397,10 +398,13 @@ const doAction = (action) => { { const frameKey = action.frameProps.get('key') if (action.themeColor !== undefined) { - windowState = windowState.setIn(frameStateUtil.frameStatePath(windowState, frameKey).concat(['themeColor']), action.themeColor) + // remove alpha channel + const solidColor = color.removeAlphaChannelForBackground(action.themeColor, 255, 255, 255) + windowState = windowState.setIn(frameStateUtil.frameStatePath(windowState, frameKey).concat(['themeColor']), solidColor) } if (action.computedThemeColor !== undefined) { - windowState = windowState.setIn(frameStateUtil.frameStatePath(windowState, frameKey).concat(['computedThemeColor']), action.computedThemeColor) + const solidColor = color.removeAlphaChannelForBackground(action.computedThemeColor, 255, 255, 255) + windowState = windowState.setIn(frameStateUtil.frameStatePath(windowState, frameKey).concat(['computedThemeColor']), solidColor) } break } diff --git a/less/window.less b/less/window.less index 12ec648d38a..6848c79f42a 100644 --- a/less/window.less +++ b/less/window.less @@ -40,7 +40,7 @@ html, } .top { - background: linear-gradient(to bottom, #eaeaea, #f2f2f4); + background: linear-gradient(to top, #e9e9ea 0%, #e9e9ea 27px, #f6f6f7 100%); &.allowDragging { -webkit-app-region: drag; } @@ -52,10 +52,30 @@ html, flex-grow: 1; box-shadow: 0 -1px 0 #bbb; width: 100%; - + z-index: 1; .tabContainer { flex-grow: 1; position: relative; + &:after { + position: absolute; + content: ' '; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(to top, rgb(28, 27, 27) 0%, rgba(255, 255, 255, .3) 60%); + display: block; + opacity: 0; + transition: opacity .4s ease-in; // TODO: get from theme.tab + pointer-events: none; + will-change: opacity; + z-index: 1100; + } + &.hasFramePreview::after { + transition-timing-function: ease-out; // TODO: get from theme.tab + transition-duration: .2s; // TODO: get from theme.tab + opacity: 1; + } } } @@ -91,17 +111,26 @@ html, // Note that this value can be as large as we want, it does not // need to match the timeout specified in the frame's element // (in renderer main.js). However, there is no reason to set it higher than that. - transition: visibility 0s linear 150ms; - // 1000 - z-index: @zindexWindow; + --visibility-delay: .15s; + --frame-zIndex-delay: 0s; + transition: visibility 0s linear var(--visibility-delay), z-index 0s linear var(--frame-zIndex-delay); + // active frame z-index + z-index: 1000; + + .hasFramePreview &.isActive { + // Move to below preview frame, but do so as part of 'preview' entry animation + // so that it is clear which frame is being previewed and which is active. + z-index: 993; + --frame-zIndex-delay: .2s; + } + &:not(.isActive) { - // 900 - z-index: @zindexWindowNotActive; + // non-active always lower than active + z-index: 990; } &.isPreview { background: #222; - // 1100 - z-index: @zindexWindowIsPreview; + z-index: 995; } // show frame when active or preview &.isActive, @@ -111,7 +140,7 @@ html, &.isExiting { visibility: visible; // show instantly - transition-delay: 0s; + --visibility-delay: 0s; } // hide frame whilst it's figuring out which location to be // note: that isEntering is timed-out at a value set in renderer main.js @@ -119,7 +148,8 @@ html, // has an actual location &.isEntering.isBlankLocation { visibility: hidden; - transition-delay: 0s; + // hide instantly (at first) + --visibility-delay: 0s; } webview { @@ -133,12 +163,6 @@ html, .webviewContainer { height: 100%; width: 100%; - &.isPreview { - will-change: opacity; - opacity: 0.5; - animation: tabFadeIn 0.75s ease-in-out; - animation-fill-mode: forwards; - } } } diff --git a/test/unit/app/common/state/tabContentStateTest/audioStateTest.js b/test/unit/app/common/state/tabContentStateTest/audioStateTest.js index 19aec879344..0ac91b32b9f 100644 --- a/test/unit/app/common/state/tabContentStateTest/audioStateTest.js +++ b/test/unit/app/common/state/tabContentStateTest/audioStateTest.js @@ -156,7 +156,7 @@ describe('audioState unit tests', function () { const state = defaultState .setIn(['frames', index, 'audioPlaybackActive'], false) .setIn(['frames', index, 'audioMuted'], false) - .setIn(['ui', 'tabs', 'intersectionRatio'], intersection.at45) + .setIn(['ui', 'tabs', 'intersectionRatio'], intersection.at46) const result = audioState.showAudioTopBorder(state, frameKey, false) assert.equal(result, false) }) diff --git a/test/unit/app/common/state/tabContentStateTest/closeStateTest.js b/test/unit/app/common/state/tabContentStateTest/closeStateTest.js index 32f44f565c4..bd9619a6fb7 100644 --- a/test/unit/app/common/state/tabContentStateTest/closeStateTest.js +++ b/test/unit/app/common/state/tabContentStateTest/closeStateTest.js @@ -150,7 +150,7 @@ describe('closeState unit tests', function () { it('returns false if tab is intersected and not active', function * () { const state = defaultState .set('activeFrameKey', 1337) - .setIn(['ui', 'tabs', 'intersectionRatio', intersection.at45]) + .setIn(['ui', 'tabs', 'intersectionRatio', intersection.at46]) const result = closeState.showCloseTabIcon(state, frameKey) assert.equal(result, false) }) diff --git a/test/unit/app/common/state/tabUIStateTest.js b/test/unit/app/common/state/tabUIStateTest.js index 58a11a189bb..25de62604f2 100644 --- a/test/unit/app/common/state/tabUIStateTest.js +++ b/test/unit/app/common/state/tabUIStateTest.js @@ -8,7 +8,6 @@ const assert = require('assert') const Immutable = require('immutable') const mockery = require('mockery') const fakeElectron = require('../../../lib/fakeElectron') -const {theme} = require('../../../../../app/renderer/components/styles/theme') const {intersection} = require('../../../../../app/renderer/components/styles/global') const frameKey = 1 @@ -307,95 +306,4 @@ describe('tabUIState unit tests', function () { assert.equal(result, true) }) }) - - describe('getTabEndIconBackgroundColor', function () { - before(function () { - // just a helper for results - this.defaultResult = (bgColor, color1Size, color2Size) => - `linear-gradient(to left, ${bgColor} ${color1Size}, transparent ${color2Size})` - }) - - describe('when tab is private', function () { - it('returns `tab.private.background` color if not active', function * () { - const state = defaultState - .set('activeFrameKey', 1337) - .mergeIn(['frames', index], { - themeColor: '#c0ff33', - isPrivate: true - }) - const result = tabUIState.getTabEndIconBackgroundColor(state, frameKey) - const expected = this.defaultResult(theme.tab.private.background, '10px', '40px') - assert.equal(result, expected) - }) - - it('returns `tab.active.private.background` if tab is active', function * () { - const state = defaultState - .mergeIn(['frames', index], { - themeColor: '#c0ff33', - isPrivate: true - }) - const result = tabUIState.getTabEndIconBackgroundColor(state, frameKey) - const expected = this.defaultResult(theme.tab.active.private.background, '10px', '40px') - assert.equal(result, expected) - }) - - it('retuns active private color if tab is being hovered', function * () { - const state = defaultState - .mergeIn(['frames', index], { - themeColor: '#c0ff33', - isPrivate: true - }) - .setIn(['ui', 'tabs', 'hoverTabIndex'], index) - const result = tabUIState.getTabEndIconBackgroundColor(state, frameKey) - const expected = this.defaultResult(theme.tab.active.private.background, '10px', '40px') - assert.equal(result, expected) - }) - }) - - describe('when tab is not private', function () { - it('returns the themeColor if tab is active', function * () { - const state = defaultState - .setIn(['frames', index, 'themeColor'], '#c0ff33') - const result = tabUIState.getTabEndIconBackgroundColor(state, frameKey) - const expected = this.defaultResult('#c0ff33', '0', '12px') - assert.equal(result, expected) - }) - it('returns `theme.tab.background` if tab is not active', function * () { - const state = defaultState - .set('activeFrameKey', 1337) - .setIn(['frames', index, 'themeColor'], '#c0ff33') - const result = tabUIState.getTabEndIconBackgroundColor(state, frameKey) - const expected = this.defaultResult(theme.tab.background, '0', '12px') - assert.equal(result, expected) - }) - }) - - describe('returns `linear gradient` size', function () { - it('at 10px/40px if tab is partitioned', function * () { - const state = defaultState - .mergeIn(['frames', index], { - partitionNumber: 1337, - themeColor: '#c0ff33' - }) - const result = tabUIState.getTabEndIconBackgroundColor(state, frameKey) - const expected = this.defaultResult('#c0ff33', '10px', '40px') - assert.equal(result, expected) - }) - it('at 10px/40px gradient size if tab has a visible close icon', function * () { - const state = defaultState - .setIn(['frames', index, 'themeColor'], '#c0ff33') - .setIn(['ui', 'tabs', 'hoverTabIndex'], index) - const result = tabUIState.getTabEndIconBackgroundColor(state, frameKey) - const expected = this.defaultResult('#c0ff33', '10px', '40px') - assert.equal(result, expected) - }) - it('at 0/12px gradient size if is neither private, partition or has close icon visible', function * () { - const state = defaultState - .setIn(['frames', index, 'themeColor'], '#c0ff33') - const result = tabUIState.getTabEndIconBackgroundColor(state, frameKey) - const expected = this.defaultResult('#c0ff33', '0', '12px') - assert.equal(result, expected) - }) - }) - }) }) diff --git a/test/unit/app/renderer/components/tabs/content/closeTabIconTest.js b/test/unit/app/renderer/components/tabs/content/closeTabIconTest.js index 76388f87a67..4c091b8b97c 100644 --- a/test/unit/app/renderer/components/tabs/content/closeTabIconTest.js +++ b/test/unit/app/renderer/components/tabs/content/closeTabIconTest.js @@ -64,7 +64,6 @@ describe('Tabs content - CloseTabIcon', function () { useCleanCache: true }) mockery.registerMock('electron', fakeElectron) - mockery.registerMock('../../../../extensions/brave/img/tabs/close_btn.svg') windowStore = require('../../../../../../../js/stores/windowStore') appStore = require('../../../../../../../js/stores/appStoreRenderer') CloseTabIcon = require('../../../../../../../app/renderer/components/tabs/content/closeTabIcon')