mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-22 08:16:09 +08:00
polish(account-dropdown): Use CSS (vs styles) to hide/show switcher, animate with CSS
This commit is contained in:
parent
3fb2754c95
commit
761a533c9a
|
@ -21,59 +21,48 @@ class AccountSwitcher extends React.Component
|
|||
|
||||
render: =>
|
||||
return false unless @state.account
|
||||
<div id="account-switcher" tabIndex={-1} onBlur={@_onBlur} ref="button">
|
||||
{@_renderAccount(@state.account, true)}
|
||||
|
||||
classnames = ""
|
||||
classnames += "open" if @state.showing
|
||||
|
||||
<div id="account-switcher"
|
||||
tabIndex={-1}
|
||||
onBlur={@_onBlur}
|
||||
ref="button"
|
||||
className={classnames}>
|
||||
{@_renderPrimaryItem()}
|
||||
{@_renderDropdown()}
|
||||
</div>
|
||||
|
||||
_renderAccount: (account, isPrimaryItem) =>
|
||||
classes = classNames
|
||||
"account": true
|
||||
"item": true
|
||||
"dropdown-item-padding": not isPrimaryItem
|
||||
"active": account is @state.account
|
||||
"bg-color-hover": not isPrimaryItem
|
||||
"primary-item": isPrimaryItem
|
||||
"account-option": not isPrimaryItem
|
||||
|
||||
email = account.emailAddress.trim().toLowerCase()
|
||||
|
||||
if isPrimaryItem
|
||||
dropdownClasses = classNames
|
||||
"account-switcher-dropdown": true,
|
||||
"account-switcher-dropdown-hidden": @state.showing
|
||||
|
||||
dropdownArrow = <div style={float: 'right', marginTop: -2}>
|
||||
<RetinaImg className={dropdownClasses} name="account-switcher-dropdown.png"
|
||||
mode={RetinaImg.Mode.ContentPreserve} />
|
||||
</div>
|
||||
|
||||
onClick = @_toggleDropdown
|
||||
|
||||
else
|
||||
onClick = =>
|
||||
@_onSwitchAccount account
|
||||
|
||||
<div className={classes}
|
||||
onClick={onClick}
|
||||
key={email}>
|
||||
<div style={float: 'left'}>
|
||||
<div className="gravatar" style={backgroundImage: @_gravatarUrl(email)}></div>
|
||||
<RetinaImg name={"ic-settings-account-#{account.provider}@2x.png"}
|
||||
style={width: 28, height: 28, marginTop: -10}
|
||||
fallback="ic-settings-account-imap.png"
|
||||
_renderPrimaryItem: =>
|
||||
<div className="item primary-item" onClick={@_toggleDropdown}>
|
||||
{@_renderGravatarForAccount(@state.account)}
|
||||
<div style={float: 'right', marginTop: -2}>
|
||||
<RetinaImg className="toggle"
|
||||
name="account-switcher-dropdown.png"
|
||||
mode={RetinaImg.Mode.ContentPreserve} />
|
||||
</div>
|
||||
{dropdownArrow}
|
||||
<div className="name" style={lineHeight: "110%"}>
|
||||
{email}
|
||||
</div>
|
||||
<div style={clear: "both"}>
|
||||
{@state.account.emailAddress.trim().toLowerCase()}
|
||||
</div>
|
||||
<div style={clear: "both"}></div>
|
||||
</div>
|
||||
|
||||
_renderAccount: (account) =>
|
||||
email = account.emailAddress.trim().toLowerCase()
|
||||
classes = classNames
|
||||
"active": account is @state.account
|
||||
"item": true
|
||||
"secondary-item": true
|
||||
|
||||
<div className={classes} onClick={ => @_onSwitchAccount(account)} key={email}>
|
||||
{@_renderGravatarForAccount(account)}
|
||||
<div className="name" style={lineHeight: "110%"}>{email}</div>
|
||||
<div style={clear: "both"}></div>
|
||||
</div>
|
||||
|
||||
_renderNewAccountOption: =>
|
||||
<div className="account item dropdown-item-padding bg-color-hover new-account-option"
|
||||
<div className="item secondary-item new-account-option"
|
||||
onClick={@_onAddAccount}
|
||||
tabIndex={999}>
|
||||
<div style={float: 'left'}>
|
||||
|
@ -85,31 +74,33 @@ class AccountSwitcher extends React.Component
|
|||
<div className="name" style={lineHeight: "110%", textTransform: 'none'}>
|
||||
Add account…
|
||||
</div>
|
||||
<div style={clear: "both"}>
|
||||
</div>
|
||||
<div style={clear: "both"}></div>
|
||||
</div>
|
||||
|
||||
_renderDropdown: =>
|
||||
display = if @state.showing then "block" else "none"
|
||||
# display = "block"
|
||||
<div className="dropdown">
|
||||
<div className="inner">
|
||||
{@state.accounts.map(@_renderAccount)}
|
||||
{@_renderNewAccountOption()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
accounts = @state.accounts.map (a) =>
|
||||
@_renderAccount(a)
|
||||
_renderGravatarForAccount: (account) =>
|
||||
email = account.emailAddress.trim().toLowerCase()
|
||||
hash = crypto.createHash('md5').update(email, 'utf8').digest('hex')
|
||||
url = "url(http://www.gravatar.com/avatar/#{hash}?d=blank&s=56)"
|
||||
|
||||
<div style={display: display}
|
||||
ref="account-switcher-dropdown"
|
||||
className="dropdown dropdown-positioning dropdown-colors">
|
||||
{accounts}
|
||||
{@_renderNewAccountOption()}
|
||||
<div style={float: 'left'}>
|
||||
<div className="gravatar" style={backgroundImage:url}></div>
|
||||
<RetinaImg name={"ic-settings-account-#{account.provider}@2x.png"}
|
||||
style={width: 28, height: 28, marginTop: -10}
|
||||
fallback="ic-settings-account-imap.png"
|
||||
mode={RetinaImg.Mode.ContentPreserve} />
|
||||
</div>
|
||||
|
||||
_toggleDropdown: =>
|
||||
@setState showing: !@state.showing
|
||||
|
||||
_gravatarUrl: (email) =>
|
||||
hash = crypto.createHash('md5').update(email, 'utf8').digest('hex')
|
||||
"url(http://www.gravatar.com/avatar/#{hash}?d=blank&s=56)"
|
||||
|
||||
_onStoreChange: =>
|
||||
@setState @_getStateFromStores()
|
||||
|
||||
|
@ -132,5 +123,4 @@ class AccountSwitcher extends React.Component
|
|||
accounts: AccountStore.items()
|
||||
account: AccountStore.current()
|
||||
|
||||
|
||||
module.exports = AccountSwitcher
|
||||
|
|
|
@ -22,39 +22,30 @@ describe "AccountSidebar", ->
|
|||
)
|
||||
|
||||
it "doesn't render the dropdown if nothing clicked", ->
|
||||
dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
|
||||
dropdownNode = React.findDOMNode dropdown, "account-switcher-dropdown"
|
||||
openDropdown = TestUtils.scryRenderedDOMComponentsWithClass sidebar, 'open'
|
||||
expect(openDropdown.length).toBe 0
|
||||
|
||||
expect(dropdownNode.style.display).toBe "none"
|
||||
|
||||
it "renders the dropdown if clicking the arrow btn", ->
|
||||
it "shows the dropdown on click", ->
|
||||
toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
|
||||
TestUtils.Simulate.click toggler
|
||||
dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
|
||||
dropdownNode = React.findDOMNode dropdown, "account-switcher-dropdown"
|
||||
openDropdown = TestUtils.scryRenderedDOMComponentsWithClass sidebar, 'open'
|
||||
expect(openDropdown.length).toBe 1
|
||||
|
||||
expect(dropdownNode.style.display).toBe "block"
|
||||
|
||||
it "removes the dropdown when clicking elsewhere", ->
|
||||
it "hides the dropdown on blur", ->
|
||||
toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
|
||||
TestUtils.Simulate.click toggler
|
||||
toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
|
||||
TestUtils.Simulate.blur toggler
|
||||
dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
|
||||
dropdownNode = React.findDOMNode dropdown, "account-switcher-dropdown"
|
||||
openDropdown = TestUtils.scryRenderedDOMComponentsWithClass sidebar, 'open'
|
||||
expect(openDropdown.length).toBe 0
|
||||
|
||||
expect(dropdownNode.style.display).toBe "none"
|
||||
|
||||
it "shows all the accounts in the dropdown", ->
|
||||
it "shows other accounts and the 'Add Account' button", ->
|
||||
toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
|
||||
TestUtils.Simulate.click toggler
|
||||
|
||||
dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
|
||||
accounts = TestUtils.scryRenderedDOMComponentsWithClass dropdown, "account-option"
|
||||
items = TestUtils.scryRenderedDOMComponentsWithClass dropdown, "secondary-item"
|
||||
newAccountButton = TestUtils.scryRenderedDOMComponentsWithClass dropdown, "new-account-option"
|
||||
|
||||
expect(accounts.length).toBe 2
|
||||
|
||||
it "shows the 'Add Account' button too", ->
|
||||
toggler = TestUtils.findRenderedDOMComponentWithClass sidebar, 'primary-item'
|
||||
TestUtils.Simulate.click toggler
|
||||
dropdown = TestUtils.findRenderedDOMComponentWithClass sidebar, "dropdown"
|
||||
accounts = TestUtils.scryRenderedDOMComponentsWithClass dropdown, "new-account-option"
|
||||
|
||||
expect(accounts.length).toBe 1
|
||||
expect(items.length).toBe 3
|
||||
expect(newAccountButton.length).toBe 1
|
||||
|
|
|
@ -86,7 +86,47 @@
|
|||
padding-bottom: @padding-base-vertical;
|
||||
}
|
||||
|
||||
.account {
|
||||
.name {
|
||||
text-transform: lowercase;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
transform: rotateX(0deg);
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform:scale(1, 0.2);
|
||||
transform-origin: top;
|
||||
transition: transform 125ms cubic-bezier(0.18, 0.89, 0.32, 1.12), opacity 100ms linear;
|
||||
.inner {
|
||||
opacity: 0;
|
||||
transition: opacity 25ms linear;
|
||||
transition-delay: 0;
|
||||
}
|
||||
margin-top: -7px;
|
||||
background: lighten(@source-list-bg, 5%);
|
||||
border: 1px solid @border-color-divider;
|
||||
border-radius: @border-radius-base;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.21);
|
||||
|
||||
position: absolute;
|
||||
top: 54px;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
|
||||
.account .gravatar {
|
||||
top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
margin-bottom: 0;
|
||||
display:block;
|
||||
|
@ -100,41 +140,12 @@
|
|||
top: -2px;
|
||||
}
|
||||
}
|
||||
.name {
|
||||
text-transform: lowercase;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.account-switcher-dropdown {
|
||||
margin-top: -7px;
|
||||
transform: scale(1, -1);
|
||||
|
||||
&.account-switcher-dropdown-hidden {
|
||||
transform: scale(1, 1);
|
||||
.secondary-item {
|
||||
&:hover {
|
||||
background: @list-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-positioning {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
width: 100%;
|
||||
z-index: 999;
|
||||
|
||||
.account .gravatar {
|
||||
top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-colors {
|
||||
background: lighten(@source-list-bg, 5%);
|
||||
border: 1px solid @border-color-divider;
|
||||
border-radius: @border-radius-base;
|
||||
box-shadow: @standard-shadow;
|
||||
}
|
||||
|
||||
.dropdown-item-padding {
|
||||
padding: 6px 5px 0 14px;
|
||||
|
||||
&:first-child {
|
||||
|
@ -148,11 +159,22 @@
|
|||
border-bottom-right-radius: @list-border-radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bg-color-hover {
|
||||
|
||||
&:hover {
|
||||
background: @list-hover-bg;
|
||||
#account-switcher.open {
|
||||
.dropdown {
|
||||
opacity: 1;
|
||||
pointer-events: initial;
|
||||
transform:scale(1, 1);
|
||||
transform-origin: top;
|
||||
.inner {
|
||||
opacity: 1;
|
||||
transition: opacity 50ms linear;
|
||||
transition-delay: 50ms;
|
||||
}
|
||||
}
|
||||
.toggle {
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ describe 'TokenizingTextField', ->
|
|||
ReactTestUtils.Simulate.dragStart(token, dragStartEvent)
|
||||
|
||||
expect(dragStartEventData).toEqual({
|
||||
'nylas-token-item': '{"client_id":"123","server_id":"2","name":"Nylas Burger Basket","email":"burgers@nylas.com","id":"2","__constructorName":"Contact"}'
|
||||
'nylas-token-item': '{"client_id":"123","server_id":"2","name":"Nylas Burger Basket","email":"burgers@nylas.com","thirdPartyData":{},"id":"2","__constructorName":"Contact"}'
|
||||
'text/plain': 'Nylas Burger Basket <burgers@nylas.com>'
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in a new issue