From 699d67a9f4d6b72f2d05bfee72ddee054414906b Mon Sep 17 00:00:00 2001 From: Ben Gotow Date: Mon, 2 Apr 2018 20:49:23 -0700 Subject: [PATCH] Group buttons in the thread toolbar for nicer layout --- .../category-picker/lib/label-picker.jsx | 83 ------------- .../category-picker/lib/main.jsx | 13 +- .../category-picker/lib/move-picker.jsx | 82 ------------- .../lib/toolbar-category-picker.jsx | 114 ++++++++++++++++++ .../thread-list/lib/main.es6 | 33 +---- .../lib/thread-toolbar-buttons.jsx | 40 +++--- .../decorators/create-button-group.jsx | 13 ++ app/src/global/mailspring-component-kit.es6 | 1 + .../images/message-list/thread-popin@1x.png | Bin 18297 -> 51238 bytes .../images/message-list/thread-popin@2x.png | Bin 14808 -> 51599 bytes .../images/message-list/thread-popout@1x.png | Bin 17831 -> 50594 bytes .../images/message-list/thread-popout@2x.png | Bin 455 -> 48824 bytes app/static/workspace.less | 26 ++++ 13 files changed, 181 insertions(+), 224 deletions(-) delete mode 100644 app/internal_packages/category-picker/lib/label-picker.jsx delete mode 100644 app/internal_packages/category-picker/lib/move-picker.jsx create mode 100644 app/internal_packages/category-picker/lib/toolbar-category-picker.jsx create mode 100644 app/src/components/decorators/create-button-group.jsx diff --git a/app/internal_packages/category-picker/lib/label-picker.jsx b/app/internal_packages/category-picker/lib/label-picker.jsx deleted file mode 100644 index 4af13691f..000000000 --- a/app/internal_packages/category-picker/lib/label-picker.jsx +++ /dev/null @@ -1,83 +0,0 @@ -const { Actions, AccountStore, React, PropTypes, WorkspaceStore } = require('mailspring-exports'); -const { RetinaImg, KeyCommandsRegion } = require('mailspring-component-kit'); - -const LabelPickerPopover = require('./label-picker-popover').default; - -// This changes the category on one or more threads. -class LabelPicker extends React.Component { - static displayName = 'LabelPicker'; - static containerRequired = false; - static propTypes = { items: PropTypes.array }; - static contextTypes = { sheetDepth: PropTypes.number }; - - constructor(props) { - super(props); - this._account = AccountStore.accountForItems(this.props.items); - } - - // If the threads we're picking categories for change, (like when they - // get their categories updated), we expect our parents to pass us new - // props. We don't listen to the DatabaseStore ourselves. - componentWillReceiveProps(nextProps) { - return (this._account = AccountStore.accountForItems(nextProps.items)); - } - - _keymapHandlers() { - return { 'core:change-labels': this._onOpenCategoryPopover }; - } - - _onOpenCategoryPopover = () => { - if (!(this.props.items.length > 0)) { - return; - } - if (this.context.sheetDepth !== WorkspaceStore.sheetStack().length - 1) { - return; - } - const buttonRect = this._buttonEl.getBoundingClientRect(); - Actions.openPopover(, { - originRect: buttonRect, - direction: 'down', - }); - }; - - render() { - if (!this._account) { - return ; - } - if (!this._account.usesLabels()) { - return ; - } - const btnClasses = 'btn btn-toolbar btn-category-picker'; - - return ( - - - - ); - } -} - -module.exports = LabelPicker; diff --git a/app/internal_packages/category-picker/lib/main.jsx b/app/internal_packages/category-picker/lib/main.jsx index 200c24d24..99f360293 100644 --- a/app/internal_packages/category-picker/lib/main.jsx +++ b/app/internal_packages/category-picker/lib/main.jsx @@ -1,17 +1,12 @@ -const MovePicker = require('./move-picker'); -const LabelPicker = require('./label-picker'); - +const ToolbarCategoryPicker = require('./toolbar-category-picker'); const { ComponentRegistry } = require('mailspring-exports'); module.exports = { - activate(state = {}) { - this.state = state; - ComponentRegistry.register(MovePicker, { role: 'ThreadActionsToolbarButton' }); - ComponentRegistry.register(LabelPicker, { role: 'ThreadActionsToolbarButton' }); + activate() { + ComponentRegistry.register(ToolbarCategoryPicker, { role: 'ThreadActionsToolbarButton' }); }, deactivate() { - ComponentRegistry.unregister(MovePicker); - ComponentRegistry.unregister(LabelPicker); + ComponentRegistry.unregister(ToolbarCategoryPicker); }, }; diff --git a/app/internal_packages/category-picker/lib/move-picker.jsx b/app/internal_packages/category-picker/lib/move-picker.jsx deleted file mode 100644 index c895a454a..000000000 --- a/app/internal_packages/category-picker/lib/move-picker.jsx +++ /dev/null @@ -1,82 +0,0 @@ -const { Actions, React, PropTypes, AccountStore, WorkspaceStore } = require('mailspring-exports'); -const { RetinaImg, KeyCommandsRegion } = require('mailspring-component-kit'); - -const MovePickerPopover = require('./move-picker-popover').default; - -// This sets the folder / label on one or more threads. -class MovePicker extends React.Component { - static displayName = 'MovePicker'; - static containerRequired = false; - - static propTypes = { items: PropTypes.array }; - static contextTypes = { sheetDepth: PropTypes.number }; - - constructor(props) { - super(props); - - this._account = AccountStore.accountForItems(this.props.items); - } - - // If the threads we're picking categories for change, (like when they - // get their categories updated), we expect our parents to pass us new - // props. We don't listen to the DatabaseStore ourselves. - componentWillReceiveProps(nextProps) { - this._account = AccountStore.accountForItems(nextProps.items); - } - - _keymapHandlers() { - return { 'core:change-folders': this._onOpenCategoryPopover }; - } - - _onOpenCategoryPopover = () => { - if (!(this.props.items.length > 0)) { - return; - } - if (this.context.sheetDepth !== WorkspaceStore.sheetStack().length - 1) { - return; - } - const buttonRect = this._buttonEl.getBoundingClientRect(); - Actions.openPopover(, { - originRect: buttonRect, - direction: 'down', - }); - }; - - render() { - if (!this._account) { - return ; - } - const btnClasses = 'btn btn-toolbar btn-category-picker'; - - return ( - - - - ); - } -} - -module.exports = MovePicker; diff --git a/app/internal_packages/category-picker/lib/toolbar-category-picker.jsx b/app/internal_packages/category-picker/lib/toolbar-category-picker.jsx new file mode 100644 index 000000000..436820e1c --- /dev/null +++ b/app/internal_packages/category-picker/lib/toolbar-category-picker.jsx @@ -0,0 +1,114 @@ +const { Actions, React, PropTypes, AccountStore, WorkspaceStore } = require('mailspring-exports'); +const { RetinaImg, KeyCommandsRegion } = require('mailspring-component-kit'); + +const MovePickerPopover = require('./move-picker-popover').default; +const LabelPickerPopover = require('./label-picker-popover').default; + +// This sets the folder / label on one or more threads. +class MovePicker extends React.Component { + static displayName = 'MovePicker'; + static containerRequired = false; + + static propTypes = { items: PropTypes.array }; + static contextTypes = { sheetDepth: PropTypes.number }; + + constructor(props) { + super(props); + + this._account = AccountStore.accountForItems(this.props.items); + } + + // If the threads we're picking categories for change, (like when they + // get their categories updated), we expect our parents to pass us new + // props. We don't listen to the DatabaseStore ourselves. + componentWillReceiveProps(nextProps) { + this._account = AccountStore.accountForItems(nextProps.items); + } + + _onOpenLabelsPopover = () => { + if (!(this.props.items.length > 0)) { + return; + } + if (this.context.sheetDepth !== WorkspaceStore.sheetStack().length - 1) { + return; + } + Actions.openPopover(, { + originRect: this._labelEl.getBoundingClientRect(), + direction: 'down', + }); + }; + + _onOpenMovePopover = () => { + if (!(this.props.items.length > 0)) { + return; + } + if (this.context.sheetDepth !== WorkspaceStore.sheetStack().length - 1) { + return; + } + Actions.openPopover(, { + originRect: this._moveEl.getBoundingClientRect(), + direction: 'down', + }); + }; + + render() { + if (!this._account) { + return ; + } + + const handlers = { + 'core:change-folders': this._onOpenMovePopover, + }; + const submenu = [ + { + label: 'Move to Folder...', + command: 'core:change-folders', + position: 'endof=thread-actions', + }, + ]; + + if (this._account.usesLabels()) { + Object.assign(handlers, { + 'core:change-labels': this._onOpenLabelsPopover, + }); + + submenu.push({ + label: 'Apply Labels...', + command: 'core:change-labels', + position: 'endof=thread-actions', + }); + } + + return ( +
+ + + {this._account.usesLabels() && ( + + )} + +
+ ); + } +} + +module.exports = MovePicker; diff --git a/app/internal_packages/thread-list/lib/main.es6 b/app/internal_packages/thread-list/lib/main.es6 index cce7488e7..7bfae39b0 100644 --- a/app/internal_packages/thread-list/lib/main.es6 +++ b/app/internal_packages/thread-list/lib/main.es6 @@ -6,15 +6,7 @@ import ThreadListEmptyFolderBar from './thread-list-empty-folder-bar'; import MessageListToolbar from './message-list-toolbar'; import SelectedItemsStack from './selected-items-stack'; -import { - UpButton, - DownButton, - TrashButton, - ArchiveButton, - MarkAsSpamButton, - ToggleUnreadButton, - ToggleStarredButton, -} from './thread-toolbar-buttons'; +import { UpButton, DownButton, MoveButtons, FlagButtons } from './thread-toolbar-buttons'; export function activate() { ComponentRegistry.register(ThreadListEmptyFolderBar, { @@ -50,23 +42,11 @@ export function activate() { modes: ['list'], }); - ComponentRegistry.register(ArchiveButton, { + ComponentRegistry.register(MoveButtons, { role: 'ThreadActionsToolbarButton', }); - ComponentRegistry.register(TrashButton, { - role: 'ThreadActionsToolbarButton', - }); - - ComponentRegistry.register(MarkAsSpamButton, { - role: 'ThreadActionsToolbarButton', - }); - - ComponentRegistry.register(ToggleStarredButton, { - role: 'ThreadActionsToolbarButton', - }); - - ComponentRegistry.register(ToggleUnreadButton, { + ComponentRegistry.register(FlagButtons, { role: 'ThreadActionsToolbarButton', }); } @@ -76,11 +56,8 @@ export function deactivate() { ComponentRegistry.unregister(SelectedItemsStack); ComponentRegistry.unregister(ThreadListToolbar); ComponentRegistry.unregister(MessageListToolbar); - ComponentRegistry.unregister(ArchiveButton); - ComponentRegistry.unregister(TrashButton); - ComponentRegistry.unregister(MarkAsSpamButton); - ComponentRegistry.unregister(ToggleUnreadButton); - ComponentRegistry.unregister(ToggleStarredButton); + ComponentRegistry.unregister(MoveButtons); + ComponentRegistry.unregister(FlagButtons); ComponentRegistry.unregister(UpButton); ComponentRegistry.unregister(DownButton); } diff --git a/app/internal_packages/thread-list/lib/thread-toolbar-buttons.jsx b/app/internal_packages/thread-list/lib/thread-toolbar-buttons.jsx index 2db292636..fb3e09451 100644 --- a/app/internal_packages/thread-list/lib/thread-toolbar-buttons.jsx +++ b/app/internal_packages/thread-list/lib/thread-toolbar-buttons.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; -import { RetinaImg } from 'mailspring-component-kit'; +import { RetinaImg, CreateButtonGroup } from 'mailspring-component-kit'; import { Actions, TaskFactory, @@ -33,17 +33,11 @@ export class ArchiveButton extends React.Component { render() { const allowed = FocusedPerspectiveStore.current().canArchiveThreads(this.props.items); if (!allowed) { - return ; + return false; } return ( - ); @@ -72,13 +66,12 @@ export class TrashButton extends React.Component { render() { const allowed = FocusedPerspectiveStore.current().canMoveThreadsTo(this.props.items, 'trash'); if (!allowed) { - return ; + return false; } return ( ); @@ -222,7 +207,6 @@ export class ToggleUnreadButton extends React.Component { return (