substance
Version:
Substance is a JavaScript library for web-based content editing. It provides building blocks for realizing custom text editors and web-based publishing system. It is developed to power our online editing platform [Substance](http://substance.io).
147 lines (130 loc) • 3.54 kB
JavaScript
import { Component, $$, domHelpers } from '../dom'
import { uuid } from '../util'
import Popover from './Popover'
export default class ModalCanvas extends Component {
getInitialState () {
return {
currentModal: null,
stack: []
}
}
getActionHandlers () {
return {
cancel: this._cancel,
confirm: this._confirm,
requestPopover: this._requestPopover,
releasePopover: this._releasePopover,
repositionPopover: this._repositionPopover,
closePopover: this._closePopover
}
}
getChildContext () {
return {
modalCanvas: this
}
}
render () {
const { currentModal, stack } = this.state
const { isMobile } = this.props
const className = 'sc-modal-canvas'
const el = $$('div', { class: className })
if (isMobile) el.addClass('sm-mobile')
if (currentModal) {
el.append(
$$('div', { class: 'se-current-modal' }).append(
currentModal.render().ref(currentModal.id)
).ref('container'),
...stack.map(stackedModal => {
return $$('div', { class: 'se-stacked-modal' }).append(
stackedModal.render().ref(stackedModal.id)
)
}),
$$(Popover, {
getContainer: () => {
return this.refs.container.getElement()
}
}).ref('popover')
)
el.on('mousedown', this._onMousedownCapture, this, { capture: true })
el.on('mouseup', this._onMouseup)
} else {
el.addClass('sm-hidden')
}
// do not let the global context menu handler handle this
el.on('contextmenu', domHelpers.stopAndPrevent)
return el
}
openModal (renderModal) {
const { currentModal, stack } = this.state
// if (this._resolve) throw new Error('Previous modal has not been closed.')
return new Promise((resolve, reject) => {
const newState = {
currentModal: {
id: uuid(),
render: renderModal,
resolve
},
stack: currentModal ? stack.concat(currentModal) : []
}
this.setState(newState)
})
}
close () {
this._cancel()
}
_cancel () {
const { currentModal } = this.state
currentModal.resolve(null)
this._close()
}
_confirm () {
const { currentModal } = this.state
currentModal.resolve(this.refs[currentModal.id])
this._close()
}
_close () {
// console.log('Closing modal')
// HACK: making sure that any popover requested in this modal is closed
this.refs.popover.close()
this._pop()
}
_onMousedownCapture (event) {
this._handleMouseup = false
if (event.target === this.getNativeElement()) {
this._handleMouseup = true
}
}
_onMouseup (event) {
if (this._handleMouseup) {
if (event.target === this.getNativeElement()) {
this.close()
}
}
}
_pop () {
const stack = this.state.stack.slice()
if (stack.length > 0) {
const currentModal = stack.pop()
const newState = {
currentModal,
stack
}
this.setState(newState)
} else {
// Note: this 'closes' the modal by emptying the canvas
this.setState(this.getInitialState())
}
}
_requestPopover (...args) {
return this.refs.popover.acquire(...args)
}
_releasePopover (...args) {
return this.refs.popover.release(...args)
}
_repositionPopover (...args) {
return this.refs.popover.reposition(...args)
}
_closePopover (...args) {
return this.refs.popover.close(...args)
}
}