diff --git a/internal_packages/category-picker/lib/category-picker.cjsx b/internal_packages/category-picker/lib/category-picker.cjsx index d15807e42..d7ea31f98 100644 --- a/internal_packages/category-picker/lib/category-picker.cjsx +++ b/internal_packages/category-picker/lib/category-picker.cjsx @@ -62,6 +62,7 @@ class CategoryPicker extends React.Component return ( ); diff --git a/internal_packages/composer-templates/lib/template-picker.jsx b/internal_packages/composer-templates/lib/template-picker.jsx index 3617ac2ce..599ac5ca9 100644 --- a/internal_packages/composer-templates/lib/template-picker.jsx +++ b/internal_packages/composer-templates/lib/template-picker.jsx @@ -102,6 +102,7 @@ class TemplatePicker extends React.Component { render() { return ( -
- - +
- isForwardedMessage: => - return false if not @_proxy - draft = @_proxy.draft() - Utils.isForwardedMessage(draft) - # This lets us click outside of the `contenteditable`'s `contentBody` # and simulate what happens when you click beneath the text *in* the # contentEditable. @@ -486,112 +350,18 @@ class ComposerView extends React.Component @refs[Fields.Body].focusAbsoluteEnd() @_mouseDownTarget = null - # When a user focuses the composer, it's possible that no input is - # initially focused. If this happens, we focus the contenteditable. If - # we didn't focus the contenteditable, the user may start typing and - # erroneously trigger keyboard shortcuts. - _onFocusIn: (event) => - return unless @_proxy - return if DOMUtils.closest(event.target, DOMUtils.inputTypes()) - @setState(focusedField: @_initiallyFocusedField(@_proxy.draft())) - _onMouseMoveComposeBody: (event) => if @_mouseComposeBody is "down" then @_mouseComposeBody = "move" - _onDraftChanged: => - return if @_ignoreNextTrigger - return unless @_proxy - draft = @_proxy.draft() + _initiallyFocusedField: -> + {pristine, to, subject} = @props.draft - if not @_initialHistorySave - @_saveToHistory() - @_initialHistorySave = true - - state = - to: draft.to - cc: draft.cc - bcc: draft.bcc - from: draft.from - body: draft.body - files: draft.files - uploads: draft.uploads - subject: draft.subject - accounts: @_getAccountsForSend() - - if !@state.draftReady - _.extend state, - draftReady: true - focusedField: @_initiallyFocusedField(draft) - enabledFields: @_initiallyEnabledFields(draft) - showQuotedText: @isForwardedMessage() - - state = @_verifyEnabledFields(draft, state) - - @setState(state) - - _initiallyFocusedField: (draft) -> - return Fields.To if draft.to.length is 0 - return Fields.Subject if (draft.subject ? "").trim().length is 0 - - shouldFocusBody = NylasEnv.isComposerWindow() or draft.pristine or + return Fields.To if to.length is 0 + return Fields.Subject if (subject ? "").trim().length is 0 + return Fields.Body if NylasEnv.isComposerWindow() or pristine or (FocusedContentStore.didFocusUsingClick('thread') is true) - return Fields.Body if shouldFocusBody return null - _verifyEnabledFields: (draft, state) -> - enabledFields = @state.enabledFields.concat(state.enabledFields) - updated = false - if draft.cc.length > 0 - updated = true - enabledFields.push(Fields.Cc) - - if draft.bcc.length > 0 - updated = true - enabledFields.push(Fields.Bcc) - - if updated - state.enabledFields = _.uniq(enabledFields) - - return state - - _initiallyEnabledFields: (draft) -> - enabledFields = [Fields.To] - enabledFields.push Fields.Cc if not _.isEmpty(draft.cc) - enabledFields.push Fields.Bcc if not _.isEmpty(draft.bcc) - enabledFields.push Fields.From if @_shouldShowFromField(draft) - enabledFields.push Fields.Subject if @_shouldEnableSubject() - enabledFields.push Fields.Body - return enabledFields - - _getAccountsForSend: => - if @_proxy.draft()?.threadId - [AccountStore.accountForId(@_proxy.draft().accountId)] - else - AccountStore.accounts() - - # When the account store changes, the From field may or may not still - # be in scope. We need to make sure to update our enabled fields. - _onAccountStoreChanged: => - return unless @_proxy - accounts = @_getAccountsForSend() - enabledFields = if @_shouldShowFromField(@_proxy?.draft()) - @state.enabledFields.concat [Fields.From] - else - _.without(@state.enabledFields, Fields.From) - @setState {enabledFields, accounts} - - _shouldShowFromField: (draft) => - return true if draft - return false - - _shouldEnableSubject: => - return false unless @_proxy - draft = @_proxy.draft() - if _.isEmpty(draft.subject ? "") then return true - else if @isForwardedMessage() then return true - else if draft.replyToMessageId then return false - else return true - _shouldAcceptDrop: (event) => # Ensure that you can't pick up a file and drop it on the same draft nonNativeFilePath = @_nonNativeFilePathForDrop(event) @@ -620,76 +390,37 @@ class ComposerView extends React.Component _onDrop: (e) => # Accept drops of real files from other applications for file in e.dataTransfer.files - Actions.addAttachment({filePath: file.path, messageClientId: @props.draftClientId}) + Actions.addAttachment({filePath: file.path, messageClientId: @props.draft.clientId}) # Accept drops from attachment components / images within the app if (uri = @_nonNativeFilePathForDrop(e)) - Actions.addAttachment({filePath: uri, messageClientId: @props.draftClientId}) + Actions.addAttachment({filePath: uri, messageClientId: @props.draft.clientId}) _onFilePaste: (path) => - Actions.addAttachment({filePath: path, messageClientId: @props.draftClientId}) - - _onChangeParticipants: (changes={}) => - @_addToProxy(changes) - Actions.draftParticipantsChanged(@props.draftClientId, changes) - - _onChangeSubject: (event) => - @_addToProxy(subject: event.target.value) + Actions.addAttachment({filePath: path, messageClientId: @props.draft.clientId}) _onBodyChanged: (event) => - return unless @_proxy - - newBody = @_showQuotedText(event.target.value) - - # The body changes extremely frequently (on every key stroke). To keep - # performance up, we don't want to trigger every single key stroke - # since that will cause an entire composer re-render. We, however, - # never want to lose any data, so we still add data to the proxy on - # every keystroke. - # - # We want to use debounce instead of throttle because we don't want ot - # trigger janky re-renders mid quick-type. Let's just do it at the end - # when you're done typing and about to move onto something else. - @_addToProxy({body: newBody}, {fromBodyChange: true}) - @_throttledTrigger ?= _.debounce => - @_ignoreNextTrigger = false - @_proxy.trigger() - , 100 - - @_throttledTrigger() + @_addToProxy({body: @_showQuotedText(event.target.value)}) return _addToProxy: (changes={}, source={}) => - return unless @_proxy and @_proxy.draft() - selections = @_getSelections() + @props.session.changes.add(changes) - oldDraft = @_proxy.draft() - return if _.all changes, (change, key) -> _.isEqual(change, oldDraft[key]) - - # Other extensions might want to hear about changes immediately. We - # only need to prevent this view from re-rendering until we're done - # throttling body changes. - if source.fromBodyChange then @_ignoreNextTrigger = true - - @_proxy.changes.add(changes) - - @_saveToHistory(selections) unless source.fromUndoManager + if not source.fromUndoManager + @_saveToHistory(selections) _isValidDraft: (options = {}) => - return false unless @_proxy - # We need to check the `DraftStore` because the `DraftStore` is # immediately and synchronously updated as soon as this function # fires. Since `setState` is asynchronous, if we used that as our only # check, then we might get a false reading. - return false if DraftStore.isSendingDraft(@props.draftClientId) + return false if DraftStore.isSendingDraft(@props.draft.clientId) - draft = @_proxy.draft() {remote} = require('electron') dialog = remote.require('dialog') - allRecipients = [].concat(draft.to, draft.cc, draft.bcc) + allRecipients = [].concat(@props.draft.to, @props.draft.cc, @props.draft.bcc) for contact in allRecipients if not ContactStore.isValidContact(contact) dealbreaker = "#{contact.email} is not a valid email address - please remove or edit it before sending." @@ -705,16 +436,16 @@ class ComposerView extends React.Component }) return false - bodyIsEmpty = draft.body is @_proxy.draftPristineBody() - forwarded = Utils.isForwardedMessage(draft) - hasAttachment = (draft.files ? []).length > 0 or (draft.uploads ? []).length > 0 + bodyIsEmpty = @props.draft.body is @props.session.draftPristineBody() + forwarded = Utils.isForwardedMessage(@props.draft) + hasAttachment = (@props.draft.files ? []).length > 0 or (@props.draft.uploads ? []).length > 0 warnings = [] - if draft.subject.length is 0 + if @props.draft.subject.length is 0 warnings.push('without a subject line') - if @_mentionsAttachment(draft.body) and not hasAttachment + if @_mentionsAttachment(@props.draft.body) and not hasAttachment warnings.push('without an attachment') if bodyIsEmpty and not forwarded and not hasAttachment @@ -723,7 +454,7 @@ class ComposerView extends React.Component # Check third party warnings added via Composer extensions for extension in ExtensionRegistry.Composer.extensions() continue unless extension.warningsForSending - warnings = warnings.concat(extension.warningsForSending({draft})) + warnings = warnings.concat(extension.warningsForSending({draft: @props.draft})) if warnings.length > 0 and not options.force response = dialog.showMessageBox(remote.getCurrentWindow(), { @@ -748,10 +479,10 @@ class ComposerView extends React.Component return body.indexOf("attach") >= 0 _destroyDraft: => - Actions.destroyDraft(@props.draftClientId) + Actions.destroyDraft(@props.draft.clientId) _selectAttachment: => - Actions.selectAttachment({messageClientId: @props.draftClientId}) + Actions.selectAttachment({messageClientId: @props.draft.clientId}) undo: (event) => event.preventDefault() @@ -760,7 +491,7 @@ class ComposerView extends React.Component return unless historyItem.state? @_recoveredSelection = historyItem.currentSelection - @_addToProxy historyItem.state, fromUndoManager: true + @_addToProxy(historyItem.state, fromUndoManager: true) @_recoveredSelection = null redo: (event) => @@ -770,7 +501,7 @@ class ComposerView extends React.Component return unless historyItem.state? @_recoveredSelection = historyItem.currentSelection - @_addToProxy historyItem.state, fromUndoManager: true + @_addToProxy(historyItem.state, fromUndoManager: true) @_recoveredSelection = null _getSelections: => @@ -778,20 +509,17 @@ class ComposerView extends React.Component previousSelection: @refs[Fields.Body]?.getPreviousSelection?() _saveToHistory: (selections) => - return unless @_proxy selections ?= @_getSelections() - newDraft = @_proxy.draft() - historyItem = previousSelection: selections.previousSelection currentSelection: selections.currentSelection state: - body: _.clone newDraft.body - subject: _.clone newDraft.subject - to: _.clone newDraft.to - cc: _.clone newDraft.cc - bcc: _.clone newDraft.bcc + body: _.clone @props.draft.body + subject: _.clone @props.draft.subject + to: _.clone @props.draft.to + cc: _.clone @props.draft.cc + bcc: _.clone @props.draft.bcc lastState = @undoManager.current() if lastState? @@ -799,8 +527,4 @@ class ComposerView extends React.Component @undoManager.saveToHistory(historyItem) - _deleteDraftIfEmpty: => - return unless @_proxy - if @_proxy.draft().pristine then Actions.destroyDraft(@props.draftClientId) - module.exports = ComposerView diff --git a/internal_packages/composer/lib/expanded-participants.cjsx b/internal_packages/composer/lib/expanded-participants.cjsx deleted file mode 100644 index 02fc326d0..000000000 --- a/internal_packages/composer/lib/expanded-participants.cjsx +++ /dev/null @@ -1,143 +0,0 @@ -_ = require 'underscore' -React = require 'react' -AccountContactField = require './account-contact-field' -ParticipantsTextField = require './participants-text-field' -{Actions} = require 'nylas-exports' -{RetinaImg} = require 'nylas-component-kit' - -Fields = require './fields' - -class ExpandedParticipants extends React.Component - @displayName: "ExpandedParticipants" - - @propTypes: - # Arrays of Contact objects. - to: React.PropTypes.array - cc: React.PropTypes.array - bcc: React.PropTypes.array - from: React.PropTypes.array - - # We need to know if the draft is ready so we can enable and disable - # ParticipantTextFields. - # - # It's possible for a ParticipantTextField, before the draft is - # ready, to start the request to `add`, `remove`, or `edit`. This - # happens when there are multiple drafts rendering, each requesting - # focus. A blur event gets fired before the draft is loaded, causing - # logic to run that sets an empty field. These requests are - # asynchronous. They may resolve after the draft is in fact ready. - # This is bad because the desire to `remove` participants may have - # been made with an empty, non-loaded draft, but executed on the new - # draft that was loaded in the time it took the async request to - # return. - draftReady: React.PropTypes.bool - - # The account to which the current draft belongs - accounts: React.PropTypes.array - - # The field that should be focused - focusedField: React.PropTypes.string - - # An enum array of visible fields. Can be any constant in the `Fields` - # dict. We are passed these as props instead of holding it as state - # since this component is frequently unmounted and re-mounted every - # time it is displayed - enabledFields: React.PropTypes.array - - # Callback for when a user changes which fields should be visible - onAdjustEnabledFields: React.PropTypes.func - - # Callback for the participants change - onChangeParticipants: React.PropTypes.func - - # Callback for the field focus changes - onChangeFocusedField: React.PropTypes.func - - @defaultProps: - to: [] - cc: [] - bcc: [] - from: [] - accounts: [] - draftReady: false - enabledFields: [] - - constructor: (@props={}) -> - - componentDidMount: => - @_applyFocusedField() - - componentDidUpdate: -> - @_applyFocusedField() - - render: -> -
- {@_renderFields()} -
- - _applyFocusedField: -> - if @props.focusedField - return unless @refs[@props.focusedField] - if @refs[@props.focusedField].focus - @refs[@props.focusedField].focus() - else - React.findDOMNode(@refs[@props.focusedField]).focus() - - _renderFields: => - # Note: We need to physically add and remove these elements, not just hide them. - # If they're hidden, shift-tab between fields breaks. - fields = [] - fields.push( - @props.onChangeFocusedField(Fields.To) } - participants={to: @props['to'], cc: @props['cc'], bcc: @props['bcc']} /> - ) - - if Fields.Cc in @props.enabledFields - fields.push( - @props.onAdjustEnabledFields(hide: [Fields.Cc]) } - onFocus={ => @props.onChangeFocusedField(Fields.Cc) } - className="composer-participant-field cc-field" - participants={to: @props['to'], cc: @props['cc'], bcc: @props['bcc']} /> - ) - - if Fields.Bcc in @props.enabledFields - fields.push( - @props.onAdjustEnabledFields(hide: [Fields.Bcc]) } - onFocus={ => @props.onChangeFocusedField(Fields.Bcc) } - className="composer-participant-field bcc-field" - participants={to: @props['to'], cc: @props['cc'], bcc: @props['bcc']} /> - ) - - if Fields.From in @props.enabledFields - fields.push( - @props.onChangeParticipants({from})} - onFocus={ => @props.onChangeFocusedField(Fields.From) } - accounts={@props.accounts} - value={@props.from?[0]} /> - ) - - fields - -module.exports = ExpandedParticipants diff --git a/internal_packages/composer/lib/fields.cjsx b/internal_packages/composer/lib/fields.cjsx index 0721e1721..58f927a33 100644 --- a/internal_packages/composer/lib/fields.cjsx +++ b/internal_packages/composer/lib/fields.cjsx @@ -5,7 +5,7 @@ Fields = From: "fromField" Subject: "textFieldSubject" Body: "contentBody" -Fields.ParticipantFields = [Fields.To, Fields.Cc, Fields.Bcc, Fields.From] +Fields.ParticipantFields = [Fields.To, Fields.Cc, Fields.Bcc] Fields.Order = "textFieldTo": 1 diff --git a/internal_packages/composer/lib/main.cjsx b/internal_packages/composer/lib/main.cjsx index f50e3a71c..57297ccbd 100644 --- a/internal_packages/composer/lib/main.cjsx +++ b/internal_packages/composer/lib/main.cjsx @@ -1,6 +1,7 @@ _ = require 'underscore' React = require 'react' +{DraftSessionContainer} = require('nylas-component-kit'); {AccountStore, DatabaseStore, Message, @@ -29,7 +30,9 @@ class ComposerWithWindowProps extends React.Component render: ->
- + + +
_showInitialErrorDialog: (msg) -> diff --git a/internal_packages/composer/lib/participants-text-field.cjsx b/internal_packages/composer/lib/participants-text-field.cjsx index a34b6683f..c51e067db 100644 --- a/internal_packages/composer/lib/participants-text-field.cjsx +++ b/internal_packages/composer/lib/participants-text-field.cjsx @@ -8,9 +8,6 @@ class ParticipantsTextField extends React.Component @displayName: 'ParticipantsTextField' @propTypes: - # The tab index of the ParticipantsTextField - tabIndex: React.PropTypes.string, - # The name of the field, used for both display purposes and also # to modify the `participants` provided. field: React.PropTypes.string, @@ -31,24 +28,8 @@ class ParticipantsTextField extends React.Component onFocus: React.PropTypes.func - # We need to know if the draft is ready so we can enable and disable - # ParticipantTextFields. - # - # It's possible for a ParticipantTextField, before the draft is - # ready, to start the request to `add`, `remove`, or `edit`. This - # happens when there are multiple drafts rendering, each requesting - # focus. A blur event gets fired before the draft is loaded, causing - # logic to run that sets an empty field. These requests are - # asynchronous. They may resolve after the draft is in fact ready. - # This is bad because the desire to `remove` participants may have - # been made with an empty, non-loaded draft, but executed on the new - # draft that was loaded in the time it took the async request to - # return. - draftReady: React.PropTypes.bool - @defaultProps: visible: true - draftReady: false shouldComponentUpdate: (nextProps, nextState) => not Utils.isEqualReact(nextProps, @props) or @@ -72,7 +53,6 @@ class ParticipantsTextField extends React.Component onEmptied={@props.onEmptied} onFocus={@props.onFocus} onTokenAction={@_showContextMenu} - tabIndex={@props.tabIndex} menuClassSet={classSet} menuPrompt={@props.field} /> @@ -116,7 +96,6 @@ class ParticipantsTextField extends React.Component return [new Contact(email: string, name: null)] _remove: (values) => - return unless @props.draftReady field = @props.field updates = {} updates[field] = _.reject @props.participants[field], (p) -> @@ -126,7 +105,6 @@ class ParticipantsTextField extends React.Component @props.change(updates) _edit: (token, replacementString) => - return unless @props.draftReady field = @props.field tokenIndex = @props.participants[field].indexOf(token) @_tokensForString(replacementString).then (replacements) => @@ -142,7 +120,6 @@ class ParticipantsTextField extends React.Component # The `tokensPromise` may be formed with an empty draft, but resolved # after a draft was prepared. This would cause the bad data to be # propagated. - return unless @props.draftReady # If the input is a string, parse out email addresses and build # an array of contact objects. For each email address wrapped in diff --git a/internal_packages/composer/lib/send-action-button.cjsx b/internal_packages/composer/lib/send-action-button.cjsx index d88734a4c..6b70bf75d 100644 --- a/internal_packages/composer/lib/send-action-button.cjsx +++ b/internal_packages/composer/lib/send-action-button.cjsx @@ -82,6 +82,7 @@ class SendActionButton extends React.Component _renderSingleDefaultButton: => diff --git a/internal_packages/send-later/lib/send-later-button.jsx b/internal_packages/send-later/lib/send-later-button.jsx index 001663a9d..05c280f89 100644 --- a/internal_packages/send-later/lib/send-later-button.jsx +++ b/internal_packages/send-later/lib/send-later-button.jsx @@ -75,7 +75,7 @@ class SendLaterButton extends Component { if (scheduledDate === 'saving') { return ( - ); diff --git a/src/components/tokenizing-text-field.cjsx b/src/components/tokenizing-text-field.cjsx index 8374f57e7..66e3ec234 100644 --- a/src/components/tokenizing-text-field.cjsx +++ b/src/components/tokenizing-text-field.cjsx @@ -95,7 +95,7 @@ class Token extends React.Component draggable="true" onDoubleClick={@_onDoubleClick} onClick={@_onSelect}> - {@props.children} @@ -244,12 +244,6 @@ class TokenizingTextField extends React.Component # Called when the input is focused onFocus: React.PropTypes.func - # The tabIndex of the input item - tabIndex: React.PropTypes.oneOfType([ - React.PropTypes.number - React.PropTypes.string - ]) - # A Prompt used in the head of the menu menuPrompt: React.PropTypes.string @@ -304,7 +298,7 @@ class TokenizingTextField extends React.Component onFocus: @_onInputFocused onChange: @_onInputChanged disabled: @props.disabled - tabIndex: @props.tabIndex + tabIndex: 0 value: @state.inputValue # If we can't accept additional tokens, override the events that would @@ -386,14 +380,21 @@ class TokenizingTextField extends React.Component else if event.key in ["Escape"] @_refreshCompletions("", clear: true) - else if event.key in ["Tab", "Enter"] or event.keyCode is 188 # comma - event.preventDefault() - return if (@state.inputValue ? "").trim().length is 0 - event.stopPropagation() - if @state.completions.length > 0 - @_addToken(@refs.completions.getSelectedItem() || @state.completions[0]) - else - @_addInputValue() + else if event.key in ["Tab", "Enter"] + @_onInputTrySubmit() + + else if event.keyCode is 188 # comma + event.preventDefault() # never allow commas in the field + @_onInputTrySubmit() + + _onInputTrySubmit: => + return if (@state.inputValue ? "").trim().length is 0 + event.preventDefault() + event.stopPropagation() + if @state.completions.length > 0 + @_addToken(@refs.completions.getSelectedItem() || @state.completions[0]) + else + @_addInputValue() _onInputChanged: (event) => val = event.target.value.trimLeft() diff --git a/src/global/nylas-component-kit.coffee b/src/global/nylas-component-kit.coffee index 2da023d72..3321e6032 100644 --- a/src/global/nylas-component-kit.coffee +++ b/src/global/nylas-component-kit.coffee @@ -46,6 +46,7 @@ class NylasComponentKit @load "TimeoutTransitionGroup", 'timeout-transition-group' @load "MetadataComposerToggleButton", 'metadata-composer-toggle-button' @load "ConfigPropContainer", "config-prop-container" + @load "DraftSessionContainer", 'draft-session-container' @load "DisclosureTriangle", "disclosure-triangle" @load "EditableList", "editable-list" @load "OutlineViewItem", "outline-view-item" diff --git a/src/sheet-toolbar.cjsx b/src/sheet-toolbar.cjsx index 2c77abfad..769e95dbb 100644 --- a/src/sheet-toolbar.cjsx +++ b/src/sheet-toolbar.cjsx @@ -88,9 +88,9 @@ class ToolbarWindowControls extends React.Component render: =>
- - - + + +
_onAlt: (event) => diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index d0a300e12..effe5f209 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -86,10 +86,6 @@ class WindowEventHandler ReactRemote = require './react-remote/react-remote-parent' ReactRemote.toggleContainerVisible() - @subscribeToCommand $(document), 'core:focus-next', @focusNext - - @subscribeToCommand $(document), 'core:focus-previous', @focusPrevious - document.addEventListener 'keydown', @onKeydown # "Pinch to zoom" on the Mac gets translated by the system into a @@ -244,58 +240,6 @@ class WindowEventHandler })) menu.popup(remote.getCurrentWindow()) - eachTabIndexedElement: (callback) -> - for element in $('[tabindex]') - element = $(element) - continue if element.isDisabled() - - tabIndex = parseInt(element.attr('tabindex')) - continue unless tabIndex >= 0 - - callback(element, tabIndex) - - focusNext: => - focusedTabIndex = parseInt($(':focus').attr('tabindex')) or -Infinity - - nextElement = null - nextTabIndex = Infinity - lowestElement = null - lowestTabIndex = Infinity - @eachTabIndexedElement (element, tabIndex) -> - if tabIndex < lowestTabIndex - lowestTabIndex = tabIndex - lowestElement = element - - if focusedTabIndex < tabIndex < nextTabIndex - nextTabIndex = tabIndex - nextElement = element - - if nextElement? - nextElement.focus() - else if lowestElement? - lowestElement.focus() - - focusPrevious: => - focusedTabIndex = parseInt($(':focus').attr('tabindex')) or Infinity - - previousElement = null - previousTabIndex = -Infinity - highestElement = null - highestTabIndex = -Infinity - @eachTabIndexedElement (element, tabIndex) -> - if tabIndex > highestTabIndex - highestTabIndex = tabIndex - highestElement = element - - if focusedTabIndex > tabIndex > previousTabIndex - previousTabIndex = tabIndex - previousElement = element - - if previousElement? - previousElement.focus() - else if highestElement? - highestElement.focus() - showDevModeMessages: -> return unless NylasEnv.isMainWindow() diff --git a/static/index.html b/static/index.html index 8415700f6..1b6641597 100644 --- a/static/index.html +++ b/static/index.html @@ -39,7 +39,7 @@ } - + diff --git a/static/workspace.less b/static/workspace.less index f3b6332a5..367555d94 100644 --- a/static/workspace.less +++ b/static/workspace.less @@ -12,6 +12,10 @@ body { -webkit-font-smoothing: antialiased; } +*:focus { + border:2px solid red !important; +} + nylas-workspace { display: block; height: 100%;