diff --git a/.babelrc b/.babelrc index 65b44c5d..b3ae6f35 100644 --- a/.babelrc +++ b/.babelrc @@ -5,7 +5,8 @@ "stage-0" ], "plugins": [ - "transform-runtime" + "transform-runtime", + "transform-decorators-legacy" ], "env": { "development": { diff --git a/app/components/DragAndDrop/actions.js b/app/components/DragAndDrop/actions.js new file mode 100644 index 00000000..e9829a09 --- /dev/null +++ b/app/components/DragAndDrop/actions.js @@ -0,0 +1,32 @@ +/* @flow weak */ +export const DND_DRAG_START = 'DND_DRAG_START' +export function dragStart ({sourceType, sourceId}) { + return { + type: DND_DRAG_START, + payload: {sourceType, sourceId} + } +} + +export const DND_DRAG_OVER = 'DND_DRAG_OVER' +export function dragOverTarget (payload) { + return { + type: DND_DRAG_OVER, + payload: payload + } +} + +export const DND_UPDATE_DRAG_OVER_META = 'DND_UPDATE_DRAG_OVER_META' +export function updateDragOverMeta (payload) { + return { + type: DND_UPDATE_DRAG_OVER_META, + payload: payload + } +} + +export const DND_DRAG_END = 'DND_DRAG_END' +export function dragEnd (payload) { + return { + type: DND_DRAG_END, + payload: payload + } +} diff --git a/app/components/DragAndDrop/index.jsx b/app/components/DragAndDrop/index.jsx new file mode 100644 index 00000000..7afdd135 --- /dev/null +++ b/app/components/DragAndDrop/index.jsx @@ -0,0 +1,170 @@ +/* @flow weak */ +import React, { Component } from 'react' +import { connect } from 'react-redux' +import _ from 'lodash' +import { dragOverTarget, updateDragOverMeta, dragEnd } from './actions' +import * as PaneActions from '../Pane/actions' + +@connect(state => state.DragAndDrop) +class DragAndDrop extends Component { + + constructor (props) { + super(props) + } + + render () { + const {isDragging, meta, target} = this.props + if (!isDragging) return null + + if (meta && meta.paneLayoutOverlay) { + const {top, left, width, height} = meta.paneLayoutOverlay + var overlayStyle = { + position: 'fixed', + top: top, + left: left, + width: width, + height: height, + opacity: 0.2 + } + } + + return meta && meta.paneLayoutOverlay + ?
+ : null + } + + componentDidMount () { + window.ondragover = this.onDragOver + window.ondrop = this.onDrop + window.ondragend = this.onDragEnd + } + + onDragOver = (e) => { + e.preventDefault() + const {source, droppables, dispatch, meta} = this.props + const prevTarget = this.props.target + + const [oX, oY] = [e.pageX, e.pageY] + const target = droppables.reduce((result, droppable) => { + const {top, left, right, bottom} = droppable.rect + if (left <= oX && oX <= right && top <= oY && oY <= bottom) { + if (!result) return droppable + return result.DOMNode.contains(droppable.DOMNode) ? droppable : result + } else { + return result + } + }, null) + + if (!target) return + if (!prevTarget || target.id !== prevTarget.id) { + dispatch(dragOverTarget({id: target.id, type: target.type})) + } + + switch (source.type) { + case 'TAB': + return this.dragTabOverPane(e, target) + + default: + } + } + + onDrop = (e) => { + e.preventDefault() + const {source, target, meta, dispatch} = this.props + if (!source || !target) return + switch (`${source.type}_to_${target.type}`) { + case 'TAB_to_PANE': + if (meta.paneSplitDirection === 'center') { + + } else { + dispatch(PaneActions.splitTo(target.id, meta.paneSplitDirection)) + } + } + dispatch(dragEnd()) + } + + onDragEnd = (e) => { + e.preventDefault() + const {dispatch} = this.props + dispatch(dragEnd()) + } + + dragTabOverPane (e, target) { + if (target.type !== 'PANE') return + const {meta, dispatch} = this.props + const [oX, oY] = [e.pageX, e.pageY] + + const {top, left, right, bottom, height, width} = target.rect + const leftRule = left + width/3 + const rightRule = right - width/3 + const topRule = top + height/3 + const bottomRule = bottom - height/3 + + let overlayPos + if (oX < leftRule) { + overlayPos = 'left' + } else if (oX > rightRule) { + overlayPos = 'right' + } else if (oY < topRule) { + overlayPos = 'top' + } else if (oY > bottomRule) { + overlayPos = 'bottom' + } else { + overlayPos = 'center' + } + + // nothing changed, stop here + if (meta && meta.paneSplitDirection === overlayPos) return + + const heightTabBar = target.DOMNode.querySelector('.tab-bar').offsetHeight + let overlay + switch (overlayPos) { + case 'left': + overlay = { + top: top + heightTabBar, + left: left, + width: width/2, + height: height - heightTabBar + } + break + case 'right': + overlay = { + top: top + heightTabBar, + left: left + width/2, + width: width/2, + height: height - heightTabBar + } + break + case 'top': + overlay = { + top: top + heightTabBar, + left: left, + width: width, + height: (height - heightTabBar)/2 + } + break + case 'bottom': + overlay = { + top: top + (height + heightTabBar)/2, + left: left, + width: width, + height: (height - heightTabBar)/2 + } + break + default: + overlay = { + top: top + heightTabBar, + left: left, + width: width, + height: height - heightTabBar + } + } + + dispatch(updateDragOverMeta({ + paneSplitDirection: overlayPos, + paneLayoutOverlay: overlay + })) + } +} + +export default DragAndDrop diff --git a/app/components/DragAndDrop/reducer.js b/app/components/DragAndDrop/reducer.js new file mode 100644 index 00000000..e05aec7e --- /dev/null +++ b/app/components/DragAndDrop/reducer.js @@ -0,0 +1,53 @@ +/* @flow weak */ +import _ from 'lodash' +import { + DND_DRAG_START, + DND_DRAG_OVER, + DND_UPDATE_DRAG_OVER_META, + DND_DRAG_END +} from './actions' + +function getDroppables () { + var droppables = _.map(document.querySelectorAll('[data-droppable]'), (DOMNode) => { + return { + id: DOMNode.id, + DOMNode: DOMNode, + type: DOMNode.getAttribute('data-droppable'), + rect: DOMNode.getBoundingClientRect() + } + }) + return droppables +} + +export default function DragAndDropReducer (state={isDragging: false}, action) { + switch (action.type) { + case DND_DRAG_START: + var {sourceType, sourceId} = action.payload + return { + isDragging: true, + source: { + type: sourceType, + id: sourceId + }, + droppables: getDroppables() + } + + case DND_DRAG_OVER: + return { + ...state, + target: action.payload + } + + case DND_UPDATE_DRAG_OVER_META: + return { + ...state, + meta: action.payload + } + + case DND_DRAG_END: + return {isDragging: false} + + default: + return state + } +} diff --git a/app/components/Modal/index.jsx b/app/components/Modal/index.jsx index 669121fd..1afe5c6d 100644 --- a/app/components/Modal/index.jsx +++ b/app/components/Modal/index.jsx @@ -21,11 +21,7 @@ var ModalContainer = (props) => { ) : null } - -ModalContainer = connect( - state => state.ModalState -, null -)(ModalContainer); +ModalContainer = connect(state => state.ModalState, null)(ModalContainer) class Modal extends Component { diff --git a/app/components/Pane/PaneAxis.jsx b/app/components/Pane/PaneAxis.jsx index 3537d6db..5d197e8d 100644 --- a/app/components/Pane/PaneAxis.jsx +++ b/app/components/Pane/PaneAxis.jsx @@ -7,34 +7,74 @@ import * as PaneActions from './actions' import TabViewContainer from '../Tab' import AceEditor from '../AceEditor' -const Pane = (props) => { - const {id, views, size, flexDirection, parentFlexDirection, resizingListeners} = props - var content - if (views.length > 1) { - content =