mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-02-04 06:18:24 +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
a7b652a31d
commit
b5624687d3
2 changed files with 63 additions and 1 deletions
|
@ -103,6 +103,7 @@ class ContenteditableComponent extends React.Component
|
||||||
onClick={@_onClick}
|
onClick={@_onClick}
|
||||||
onPaste={@_onPaste}
|
onPaste={@_onPaste}
|
||||||
onInput={@_onInput}
|
onInput={@_onInput}
|
||||||
|
onKeyDown={@_onKeyDown}
|
||||||
dangerouslySetInnerHTML={@_dangerouslySetInnerHTML()}></div>
|
dangerouslySetInnerHTML={@_dangerouslySetInnerHTML()}></div>
|
||||||
<a className={@_quotedTextClasses()} onClick={@_onToggleQuotedText}></a>
|
<a className={@_quotedTextClasses()} onClick={@_onToggleQuotedText}></a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -125,9 +126,18 @@ class ContenteditableComponent extends React.Component
|
||||||
# Note: Related to composer-view#_onClickComposeBody
|
# Note: Related to composer-view#_onClickComposeBody
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
|
_onKeyDown: (event) =>
|
||||||
|
if event.key is "Tab"
|
||||||
|
@_onTabDown(event)
|
||||||
|
return
|
||||||
|
|
||||||
_onInput: =>
|
_onInput: =>
|
||||||
|
return if @_ignoreInputChanges
|
||||||
|
@_ignoreInputChanges = true
|
||||||
@_dragging = false
|
@_dragging = false
|
||||||
|
|
||||||
|
@_runCoreFilters()
|
||||||
|
|
||||||
@_runExtensionFilters()
|
@_runExtensionFilters()
|
||||||
|
|
||||||
@_prepareForReactContenteditable()
|
@_prepareForReactContenteditable()
|
||||||
|
@ -136,6 +146,53 @@ class ContenteditableComponent extends React.Component
|
||||||
|
|
||||||
html = @_unapplyHTMLDisplayFilters(@_editableNode().innerHTML)
|
html = @_unapplyHTMLDisplayFilters(@_editableNode().innerHTML)
|
||||||
@props.onChange(target: {value: html})
|
@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: ->
|
_runExtensionFilters: ->
|
||||||
for extension in DraftStore.extensions()
|
for extension in DraftStore.extensions()
|
||||||
|
|
|
@ -178,7 +178,7 @@
|
||||||
'?': 'native!'
|
'?': 'native!'
|
||||||
'/': 'native!'
|
'/': 'native!'
|
||||||
|
|
||||||
'body input, body textarea, body *[contenteditable]':
|
'body input, body textarea':
|
||||||
'tab': 'core:focus-next'
|
'tab': 'core:focus-next'
|
||||||
'shift-tab': 'core:focus-previous'
|
'shift-tab': 'core:focus-previous'
|
||||||
|
|
||||||
|
@ -186,6 +186,11 @@
|
||||||
'tab': 'native!'
|
'tab': 'native!'
|
||||||
'shift-tab': 'native!'
|
'shift-tab': 'native!'
|
||||||
|
|
||||||
|
# So our contenteditable control can do its own thing
|
||||||
|
'body *[contenteditable]':
|
||||||
|
'tab': 'native!'
|
||||||
|
'shift-tab': 'native!'
|
||||||
|
|
||||||
# For menus
|
# For menus
|
||||||
'body .menu, body .menu, body .menu input':
|
'body .menu, body .menu, body .menu input':
|
||||||
# and by "native!" I actually mean for it to just let React deal with
|
# and by "native!" I actually mean for it to just let React deal with
|
||||||
|
|
Loading…
Reference in a new issue