mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-30 20:07:48 +08:00
feat(composer): add bullets, numbered lists, and tab controls
Summary: Adding bullets, numbered lists, and tabbing indenting and outdenting Test Plan: manual Reviewers: bengotow Reviewed By: bengotow Differential Revision: https://phab.nylas.com/D1917
This commit is contained in:
parent
06a1eb42b2
commit
e018d6a8ec
2 changed files with 63 additions and 1 deletions
|
@ -103,6 +103,7 @@ class ContenteditableComponent extends React.Component
|
|||
onClick={@_onClick}
|
||||
onPaste={@_onPaste}
|
||||
onInput={@_onInput}
|
||||
onKeyDown={@_onKeyDown}
|
||||
dangerouslySetInnerHTML={@_dangerouslySetInnerHTML()}></div>
|
||||
<a className={@_quotedTextClasses()} onClick={@_onToggleQuotedText}></a>
|
||||
</div>
|
||||
|
@ -125,9 +126,18 @@ class ContenteditableComponent extends React.Component
|
|||
# Note: Related to composer-view#_onClickComposeBody
|
||||
event.stopPropagation()
|
||||
|
||||
_onKeyDown: (event) =>
|
||||
if event.key is "Tab"
|
||||
@_onTabDown(event)
|
||||
return
|
||||
|
||||
_onInput: =>
|
||||
return if @_ignoreInputChanges
|
||||
@_ignoreInputChanges = true
|
||||
@_dragging = false
|
||||
|
||||
@_runCoreFilters()
|
||||
|
||||
@_runExtensionFilters()
|
||||
|
||||
@_prepareForReactContenteditable()
|
||||
|
@ -136,6 +146,53 @@ class ContenteditableComponent extends React.Component
|
|||
|
||||
html = @_unapplyHTMLDisplayFilters(@_editableNode().innerHTML)
|
||||
@props.onChange(target: {value: html})
|
||||
@_ignoreInputChanges = false
|
||||
return
|
||||
|
||||
_runCoreFilters: ->
|
||||
@_createLists()
|
||||
|
||||
# Determines if the user wants to add an ordered or unordered list.
|
||||
_createLists: ->
|
||||
# The `execCommand` will update the DOM and move the cursor. Since
|
||||
# this is happening in the middle of an `_onInput` callback, we want
|
||||
# the whole operation to look "atomic". As such we'll do any necessary
|
||||
# DOM cleanup and fire the `exec` command with the listeners off, then
|
||||
# re-enable at the end.
|
||||
updateDOM = (command) =>
|
||||
@_teardownSelectionListeners()
|
||||
@_ignoreInputChanges = true
|
||||
document.execCommand(command)
|
||||
selection = document.getSelection()
|
||||
selection.anchorNode.parentElement.innerHTML = ""
|
||||
@_ignoreInputChanges = false
|
||||
@_setupSelectionListeners()
|
||||
|
||||
text = @_textContentAtCursor()
|
||||
if (/^\d\.\s$/).test text
|
||||
updateDOM("insertOrderedList")
|
||||
else if (/^-\s$/).test text
|
||||
updateDOM("insertUnorderedList")
|
||||
|
||||
_onTabDown: (event) ->
|
||||
event.preventDefault()
|
||||
selection = document.getSelection()
|
||||
if selection?.isCollapsed
|
||||
# https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
|
||||
# Some nodes anchorNodes might not have a `closest` method.
|
||||
if selection.anchorNode?.closest?("li")
|
||||
if event.shiftKey
|
||||
document.execCommand("outdent")
|
||||
else
|
||||
document.execCommand("indent")
|
||||
return
|
||||
document.execCommand("insertText", false, "\t")
|
||||
|
||||
_textContentAtCursor: ->
|
||||
selection = document.getSelection()
|
||||
if selection.isCollapsed
|
||||
return selection.anchorNode.textContent
|
||||
else return null
|
||||
|
||||
_runExtensionFilters: ->
|
||||
for extension in DraftStore.extensions()
|
||||
|
|
|
@ -178,7 +178,7 @@
|
|||
'?': 'native!'
|
||||
'/': 'native!'
|
||||
|
||||
'body input, body textarea, body *[contenteditable]':
|
||||
'body input, body textarea':
|
||||
'tab': 'core:focus-next'
|
||||
'shift-tab': 'core:focus-previous'
|
||||
|
||||
|
@ -186,6 +186,11 @@
|
|||
'tab': 'native!'
|
||||
'shift-tab': 'native!'
|
||||
|
||||
# So our contenteditable control can do its own thing
|
||||
'body *[contenteditable]':
|
||||
'tab': 'native!'
|
||||
'shift-tab': 'native!'
|
||||
|
||||
# For menus
|
||||
'body .menu, body .menu, body .menu input':
|
||||
# and by "native!" I actually mean for it to just let React deal with
|
||||
|
|
Loading…
Reference in a new issue