mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-12-29 11:52:34 +08:00
fix(mail-merge): Switch to using new overlaid components api
Summary: - Fixes several selection and focus issues along the way - Can now preview what tokens will look like when not editing - Adds decorator to listen to mail merge session changes and removes a bunch of duplicated code - Gets rid of all imperative code (and specs) that had to imperatively reach into the dom to update the tokens Test Plan: - Unit tests Reviewers: bengotow, evan Reviewed By: evan Differential Revision: https://phab.nylas.com/D2989
This commit is contained in:
parent
6ba667daa2
commit
3398640e86
6 changed files with 44 additions and 15 deletions
|
@ -460,7 +460,6 @@ body.platform-win32 {
|
|||
z-index: 10;
|
||||
}
|
||||
img.n1-overlaid-component-anchor-container {
|
||||
display: block;
|
||||
border: 0;
|
||||
}
|
||||
.toggle-serialized {
|
||||
|
|
|
@ -42,6 +42,8 @@ render: function() {
|
|||
class Contenteditable extends React.Component
|
||||
@displayName: "Contenteditable"
|
||||
|
||||
@IgnoreMutationClassName: 'ignore-mutations'
|
||||
|
||||
@propTypes:
|
||||
# The current html state, as a string, of the contenteditable.
|
||||
value: React.PropTypes.string
|
||||
|
@ -298,16 +300,27 @@ class Contenteditable extends React.Component
|
|||
########################### Event Handlers ###########################
|
||||
######################################################################
|
||||
|
||||
# Every time the contents of the contenteditable DOM node change, the
|
||||
# `_onDOMMutated` event gets fired.
|
||||
# Every time the contents of the contenteditable DOM node change due to a user
|
||||
# action, the `_onDOMMutated` event gets fired.
|
||||
#
|
||||
# If we are in the middle of an `atomic` change transaction, we ignore
|
||||
# those changes.
|
||||
#
|
||||
# If any target element of the mutations contains `IgnoreMutationClassName` in
|
||||
# its className, we will also ignore the mutations. This is order to support
|
||||
# extensions that might imperatively want to mutate the contenteditable
|
||||
# wihtout it being due to a direct user action, i.e. without wanting to
|
||||
# trigger this callback. For example, `OverlaidComponents` will mutate the
|
||||
# dimensions of its anchor elements without the user explicitly doing so.
|
||||
#
|
||||
# At all other times we take the change, apply various filters to the
|
||||
# new content, then notify our parent that the content has been updated.
|
||||
#
|
||||
_onDOMMutated: (mutations) =>
|
||||
return unless mutations and mutations.length > 0
|
||||
for mutation in mutations
|
||||
if mutation.target?.className?.includes(Contenteditable.IgnoreMutationClassName)
|
||||
return
|
||||
|
||||
@_mutationObserver.disconnect()
|
||||
@setInnerState dragging: false if @innerState.dragging
|
||||
|
|
|
@ -2,7 +2,7 @@ _ = require 'underscore'
|
|||
{DOMUtils} = require 'nylas-exports'
|
||||
React = require 'react'
|
||||
ExtendedSelection = require './extended-selection'
|
||||
OverlaidComponents = require('../overlaid-components/overlaid-components').default
|
||||
OverlaidComponents = null
|
||||
|
||||
# An extended interface of execCommand
|
||||
#
|
||||
|
@ -73,8 +73,15 @@ class EditorAPI
|
|||
normalize: -> @rootNode.normalize(); @
|
||||
|
||||
insertCustomComponent: (componentKey, props = {}) ->
|
||||
anchorTag = OverlaidComponents.buildAnchorTag(componentKey, props)
|
||||
OverlaidComponents ?= require('../overlaid-components/overlaid-components').default
|
||||
{anchorId, anchorTag} = OverlaidComponents.buildAnchorTag(componentKey, props)
|
||||
@insertHTML(anchorTag)
|
||||
return anchorId
|
||||
|
||||
removeCustomComponentByAnchorId: (anchorId) ->
|
||||
return unless anchorId
|
||||
node = @rootNode.querySelector("img[data-overlay-id=\"#{anchorId}\"]")
|
||||
node?.parentNode.removeChild(node)
|
||||
|
||||
########################################################################
|
||||
####################### execCommand Delegation #########################
|
||||
|
|
|
@ -3,6 +3,7 @@ import React from 'react'
|
|||
import ReactDOM from 'react-dom'
|
||||
import Utils from '../../flux/models/utils'
|
||||
import CustomContenteditableComponents from './custom-contenteditable-components'
|
||||
import {IgnoreMutationClassName} from '../contenteditable/contenteditable'
|
||||
import {ANCHOR_CLASS, IMG_SRC} from './anchor-constants'
|
||||
|
||||
const MUTATION_CONFIG = {
|
||||
|
@ -19,6 +20,7 @@ export default class OverlaidComponents extends React.Component {
|
|||
|
||||
static propTypes = {
|
||||
children: React.PropTypes.node,
|
||||
className: React.PropTypes.string,
|
||||
exposedProps: React.PropTypes.object,
|
||||
}
|
||||
|
||||
|
@ -34,11 +36,17 @@ export default class OverlaidComponents extends React.Component {
|
|||
}
|
||||
|
||||
static buildAnchorTag(componentKey, props = {}, existingId = null, style = "") {
|
||||
const id = existingId || Utils.generateTempId()
|
||||
let className = ANCHOR_CLASS
|
||||
if (props.className) { className = `${className} ${props.className}` }
|
||||
const overlayId = existingId || Utils.generateTempId()
|
||||
let className = `${IgnoreMutationClassName} ${ANCHOR_CLASS}`
|
||||
if (props.className) {
|
||||
className = `${className} ${props.className}`
|
||||
}
|
||||
const propsStr = OverlaidComponents.propsToDOMAttr(props);
|
||||
return `<img class="${className}" src="${IMG_SRC}" data-overlay-id="${id}" data-component-props="${propsStr}" data-component-key="${componentKey}" style="${style}">`
|
||||
return {
|
||||
anchorId: overlayId,
|
||||
anchorTag:
|
||||
`<img class="${className}" src="${IMG_SRC}" data-overlay-id="${overlayId}" data-component-props="${propsStr}" data-component-key="${componentKey}" style="${style}">`,
|
||||
}
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -168,7 +176,7 @@ export default class OverlaidComponents extends React.Component {
|
|||
const data = this._anchorData[id];
|
||||
if (!data) { throw new Error("No mounted rect for #{id}") }
|
||||
|
||||
const style = {left: data.left, top: data.top, position: "relative"}
|
||||
const style = {left: data.left, top: data.top, position: 'absolute'}
|
||||
const componentData = CustomContenteditableComponents.get(data.componentKey);
|
||||
|
||||
if (!componentData) {
|
||||
|
@ -189,13 +197,14 @@ export default class OverlaidComponents extends React.Component {
|
|||
const el = React.createElement(component, props)
|
||||
|
||||
const wrap = (
|
||||
<div
|
||||
<span
|
||||
key={id}
|
||||
className={OverlaidComponents.WRAP_CLASS}
|
||||
style={style}
|
||||
data-overlay-id={id}
|
||||
>
|
||||
{el}
|
||||
</div>
|
||||
</span>
|
||||
)
|
||||
|
||||
els.push(wrap)
|
||||
|
@ -210,8 +219,9 @@ export default class OverlaidComponents extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {className} = this.props
|
||||
return (
|
||||
<div className="overlaid-components-wrap" style={{position: "relative"}}>
|
||||
<div className={`overlaid-components-wrap ${className}`} style={{position: "relative"}}>
|
||||
<div className="anchor-container" ref="anchorContainer">
|
||||
{this.props.children}
|
||||
</div>
|
||||
|
|
|
@ -75,7 +75,7 @@ export default class OverlaidComposerExtension extends ComposerExtension {
|
|||
const matcher = self.overlayMatches(self._serializedExtractRe(), outDraft.body);
|
||||
|
||||
for (const match of matcher) {
|
||||
const anchorTag = OverlaidComponents.buildAnchorTag(match.dataComponentKey, match.dataComponentProps, match.dataOverlayId, match.dataStyle);
|
||||
const {anchorTag} = OverlaidComponents.buildAnchorTag(match.dataComponentKey, match.dataComponentProps, match.dataOverlayId, match.dataStyle);
|
||||
|
||||
outBody = outBody.replace(OverlaidComposerExtension._serializedReplacerRe(match.dataOverlayId), anchorTag)
|
||||
}
|
||||
|
|
2
src/pro
2
src/pro
|
@ -1 +1 @@
|
|||
Subproject commit 8fcfe13577d014ad4583b5f23b639047f1cb5789
|
||||
Subproject commit b0d3088fd8d9f853d82872ddd5aae14cc02447cf
|
Loading…
Reference in a new issue