this.editorContainer = c}
+ id={name}
+ style={divStyle}
+ />
+ )
}
}
-CodeMirrorEditor = connect(state => ({
+
+@connect(state => ({
setting: state.SettingState.views.tabs.EDITOR,
themeSetting: state.SettingState.views.tabs.THEME,
-})
-)(CodeMirrorEditor);
+}))
+class TablessCodeMirrorEditor extends Component {
+ constructor (props) {
+ super(props)
+ this.state = {}
+ }
+
+ componentDidMount() {
+ const { themeSetting, width, height } = this.props
+ const theme = themeSetting.items[1].value
+
+ this.editor = initializeEditor(this.editorContainer, theme)
+ this.editor.focus()
+ this.editor.on('change', this.onChange)
+ }
+
+ onChange = (e) => {
+ this.props.dispatch(TabActions.createTabInGroup(this.props.tabGroupId, {
+ flags: { modified: true },
+ content: this.editor.getValue()
+ }))
+ }
+
+ componentWillReceiveProps ({ themeSetting }) {
+ const nextTheme = themeSetting.items[1].value
+ const theme = this.props.themeSetting.items[1].value
+ if (theme !== nextTheme) this.editor.setOption('theme', nextTheme)
+ }
+
+ render () {
+ return (
+
this.editorContainer = c } style={{ height: '100%', width: '100%' }} />
+ )
+ }
+}
-export default CodeMirrorEditor;
+export default CodeMirrorEditor
+export { TablessCodeMirrorEditor }
diff --git a/app/components/DragAndDrop/index.jsx b/app/components/DragAndDrop.jsx
similarity index 78%
rename from app/components/DragAndDrop/index.jsx
rename to app/components/DragAndDrop.jsx
index 2642d351..661fef31 100644
--- a/app/components/DragAndDrop/index.jsx
+++ b/app/components/DragAndDrop.jsx
@@ -1,13 +1,18 @@
-/* @flow weak */
import React, { Component } from 'react'
-import { connect } from 'react-redux'
-import _ from 'lodash'
-import { updateDragOverTarget, updateDragOverMeta, dragEnd, dragStart } from './actions'
-import * as PaneActions from '../Pane/actions'
-import * as TabActions from '../Tab/actions'
-import * as FileTreeActions from '../FileTree/actions'
-
-@connect(state => state.DragAndDrop)
+import { dispatch } from '../store'
+import { observer } from 'mobx-react'
+import { dnd } from 'utils'
+import * as PaneActions from './Pane/actions'
+import * as TabActions from 'commons/Tab/actions'
+import * as FileTreeActions from './FileTree/actions'
+
+// Corner case: file dragging doesn't trigger 'dragend' natively
+// so need to patch for this behavior
+function isFileDragEnd (e) {
+ return (e.screenX === 0 && e.screenY === 0 && e.dataTransfer.files.length)
+}
+
+@observer
class DragAndDrop extends Component {
constructor (props) {
@@ -15,7 +20,7 @@ class DragAndDrop extends Component {
}
render () {
- const {isDragging, meta, target} = this.props
+ const { isDragging, meta, target } = dnd
if (!isDragging) return null
if (meta && meta.paneLayoutOverlay) {
@@ -41,22 +46,20 @@ class DragAndDrop extends Component {
window.ondragend = this.onDragEnd
window.ondragleave = this.onDragLeave
}
+
onDragLeave = (e) => {
e.preventDefault()
- const {source = {}, dispatch} = this.props
- if (source.type && source.type === 'EXTERNAL_FILE') {
- setTimeout(() => dispatch(dragEnd()), 1000)
- }
+ if (isFileDragEnd(e)) dnd.dragEnd()
}
+
onDragOver = (e) => {
e.preventDefault()
- const {source, droppables = [], dispatch, meta} = this.props
- const prevTarget = this.props.target
+ const { source, droppables = [], meta } = dnd
if (!source) {
- dispatch(dragStart({
- sourceType: 'EXTERNAL_FILE',
- sourceId: Date.now(),
- }))
+ dnd.dragStart({
+ type: 'EXTERNAL_FILE',
+ id: Date.now(),
+ })
}
const [oX, oY] = [e.pageX, e.pageY]
const target = droppables.reduce((result, droppable) => {
@@ -69,9 +72,9 @@ class DragAndDrop extends Component {
}
}, null)
if (!target) return
- // if (!prevTarget || target.id !== prevTarget.id) {
- dispatch(updateDragOverTarget({id: target.id, type: target.type }))
- // }
+
+ dnd.dragOver({ id: target.id, type: target.type })
+
switch (`${source.type}_on_${target.type}`) {
case 'TAB_on_PANE':
return this.dragTabOverPane(e, target)
@@ -87,7 +90,7 @@ class DragAndDrop extends Component {
onDrop = (e) => {
e.preventDefault()
- const {source, target, meta, dispatch} = this.props
+ const { source, target, meta } = dnd
if (!source || !target) return
switch (`${source.type}_on_${target.type}`) {
case 'TAB_on_PANE':
@@ -110,22 +113,20 @@ class DragAndDrop extends Component {
default:
}
- dispatch(dragEnd())
+ dnd.dragEnd()
}
onDragEnd = (e) => {
e.preventDefault()
- const {dispatch} = this.props
- dispatch(dragEnd())
+ dnd.dragEnd()
}
dragTabOverTabBar (e, target) {
- const {dispatch} = this.props
if (target.type !== 'TABLABEL' && target.type !== 'TABBAR') return
if (target.type === 'TABLABEL') {
- dispatch(updateDragOverMeta({tabLabelTargetId: target.id}))
+ dnd.updateDragOverMeta({tabLabelTargetId: target.id})
} else {
- dispatch(updateDragOverMeta({tabBarTargetId: target.id}))
+ dnd.updateDragOverMeta({tabBarTargetId: target.id})
}
}
@@ -138,7 +139,7 @@ class DragAndDrop extends Component {
}
dragTabOverPane (e, target) {
if (target.type !== 'PANE') return
- const {meta, dispatch} = this.props
+ const { meta } = dnd
const [oX, oY] = [e.pageX, e.pageY]
const {top, left, right, bottom, height, width} = target.rect
@@ -207,10 +208,10 @@ class DragAndDrop extends Component {
}
}
- dispatch(updateDragOverMeta({
+ dnd.updateDragOverMeta({
paneSplitDirection: overlayPos,
paneLayoutOverlay: overlay
- }))
+ })
}
}
diff --git a/app/components/DragAndDrop/actions.js b/app/components/DragAndDrop/actions.js
deleted file mode 100644
index 94f92116..00000000
--- a/app/components/DragAndDrop/actions.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/* @flow weak */
-import { createAction } from 'redux-actions'
-
-export const DND_DRAG_START = 'DND_DRAG_START'
-export const dragStart = createAction(DND_DRAG_START,
- ({sourceType, sourceId}) => ({sourceType, sourceId})
-)
-
-export const DND_DRAG_OVER = 'DND_DRAG_OVER'
-export const updateDragOverTarget = createAction(DND_DRAG_OVER, target => target)
-
-export const DND_UPDATE_DRAG_OVER_META = 'DND_UPDATE_DRAG_OVER_META'
-export const updateDragOverMeta = createAction(DND_UPDATE_DRAG_OVER_META, meta => meta)
-
-export const DND_DRAG_END = 'DND_DRAG_END'
-export const dragEnd = createAction(DND_DRAG_END)
diff --git a/app/components/DragAndDrop/reducer.js b/app/components/DragAndDrop/reducer.js
deleted file mode 100644
index 364a0142..00000000
--- a/app/components/DragAndDrop/reducer.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/* @flow weak */
-import _ from 'lodash'
-import { handleActions } from 'redux-actions'
-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 handleActions({
- [DND_DRAG_START]: (state, action) => {
- const {sourceType, sourceId} = action.payload
- return {
- isDragging: true,
- source: {
- type: sourceType,
- id: sourceId
- },
- droppables: getDroppables()
- }
- },
-
- [DND_DRAG_OVER]: (state, action) => {
- return {
- ...state,
- target: action.payload
- }
- },
-
- [DND_UPDATE_DRAG_OVER_META]: (state, action) => {
- return {
- ...state,
- meta: action.payload
- }
- },
-
- [DND_DRAG_END]: (state, action) => {
- return {isDragging: false}
- }
-}, {isDragging: false})
-
-
-/*
-@StateShape:
-{
- isDragging:
- source: {
- id:
- type:
- }
- target: {
- id:
- type:
- }
-}
-*/
diff --git a/app/components/Editor/TabContainer.jsx b/app/components/Editor/TabContainer.jsx
new file mode 100644
index 00000000..e842f38b
--- /dev/null
+++ b/app/components/Editor/TabContainer.jsx
@@ -0,0 +1,66 @@
+import _ from 'lodash';
+import React, { Component, PropTypes } from 'react'
+import cx from 'classnames'
+import { observer, inject } from 'mobx-react'
+import { TabBar, TabContent, TabContentItem } from 'commons/Tab'
+import * as TabActions from 'commons/Tab/actions';
+import EditorWrapper from '../EditorWrapper'
+import { TablessCodeMirrorEditor } from '../CodeMirrorEditor'
+
+const contextMenuItems = [
+ {
+ name: 'Close',
+ icon: '',
+ command: 'tab:close'
+ }, {
+ name: 'Close Others',
+ icon: '',
+ command: 'tab:close_other'
+ }, {
+ name: 'Close All',
+ icon: '',
+ command: 'tab:close_all'
+ },
+ { name: '-' },
+ {
+ name: 'Vertical Split',
+ icon: '',
+ command: 'tab:split_v'
+ }, {
+ name: 'Horizontal Split',
+ icon: '',
+ command: 'tab:split_h'
+ }
+]
+
+@observer
+class TabContainer extends Component {
+ static propTypes = {
+ containingPaneId: PropTypes.string,
+ tabGroup: PropTypes.object,
+ createGroup: PropTypes.func,
+ };
+
+ render () {
+ const tabGroup = this.props.tabGroup
+ if (!tabGroup) return null
+ return (
+
+
+
+ {tabGroup.tabs.length ? tabGroup.tabs.map(tab =>
+
+
+
+ )
+ :
+
+
+ }
+
+
+ )
+ }
+}
+
+export default TabContainer
diff --git a/app/components/Editor/index.js b/app/components/Editor/index.js
new file mode 100644
index 00000000..0073d5a7
--- /dev/null
+++ b/app/components/Editor/index.js
@@ -0,0 +1,3 @@
+import TabContainer from './TabContainer'
+
+export default TabContainer
diff --git a/app/components/Editor/reducer.js b/app/components/Editor/reducer.js
new file mode 100644
index 00000000..8c6a0a90
--- /dev/null
+++ b/app/components/Editor/reducer.js
@@ -0,0 +1,124 @@
+import { extendObservable, createTransformer, action } from 'mobx'
+import { handleActions } from 'utils/actions'
+import EditorTabState, { Tab, TabGroup } from './state'
+import store from 'app/mobxStore'
+import {
+ TAB_CREATE,
+ TAB_CREATE_IN_GROUP,
+ TAB_REMOVE,
+ TAB_ACTIVATE,
+ TAB_CREATE_GROUP,
+ TAB_REMOVE_GROUP,
+ TAB_UPDATE,
+ TAB_UPDATE_FLAGS,
+ TAB_UPDATE_BY_PATH,
+ TAB_MOVE_TO_GROUP,
+ TAB_MOVE_TO_PANE,
+ TAB_INSERT_AT,
+ TAB_REMOVE_OTHER,
+ TAB_REMOVE_ALL,
+} from 'commons/Tab/actions'
+
+const renew = createTransformer(state => {
+ return {
+ ...state,
+ tabGroups: state.tabGroups.toJS(),
+ tabs: state.tabs.toJS(),
+ activeTab: state.activeTab,
+ activeTabGroup: state.activeTabGroup,
+ }
+})
+
+const TabActionHandler = handleActions({
+ [TAB_CREATE]: (state, payload) => {
+ const tabConfig = payload
+ const tab = new Tab(tabConfig)
+ const activeTabGroup = state.activeTabGroup
+ activeTabGroup.addTab(tab)
+ },
+
+ [TAB_CREATE_IN_GROUP]: (state, payload) => {
+ const { groupId, tab: tabConfig } = payload
+ const tab = new Tab(tabConfig)
+ state.tabGroups.get(groupId).addTab(tab)
+ },
+
+ [TAB_REMOVE]: (state, payload) => {
+ const tab = state.tabs.get(payload)
+ tab.destroy()
+ },
+
+ [TAB_ACTIVATE]: (state, payload) => {
+ const tab = state.tabs.get(payload)
+ tab.activate()
+ },
+
+ [TAB_REMOVE_OTHER]: (state, payload) => {
+ const tab = state.tabs.get(payload)
+ tab.activate()
+ tab.tabGroup.tabs.forEach(eachTab => {
+ if (eachTab !== tab) eachTab.destroy()
+ })
+ },
+
+ [TAB_REMOVE_ALL]: (state, payload) => {
+ const tab = state.tabs.get(payload)
+ tab.tabGroup.tabs.forEach(tab => tab.destroy())
+ },
+
+ [TAB_CREATE_GROUP]: (state, payload) => {
+ const { groupId } = payload
+ new TabGroup({ id: groupId })
+ },
+
+ [TAB_REMOVE_GROUP]: (state, payload) => {
+ const tab = state.tabs.get(payload)
+ },
+
+ [TAB_UPDATE]: (state, payload) => {
+ const tabId = payload.id
+ const tab = state.tabs.get(tabId)
+ if (tab) extendObservable(tab, payload)
+ },
+
+ [TAB_UPDATE_FLAGS]: (state, { tabId, flags }) => {
+ const tab = state.tabs.get(tabId)
+ tab.flags = flags
+ },
+
+ [TAB_MOVE_TO_GROUP]: (state, { tabId, groupId }) => {
+ const tab = state.tabs.get(tabId)
+ const tabGroup = state.tabGroups.get(groupId)
+ if (!tab || !tabGroup) return
+ tabGroup.addTab(tab)
+ },
+
+ [TAB_INSERT_AT]: (state, { tabId, beforeTabId }) => {
+ const tab = state.tabs.get(tabId)
+ const anchorTab = state.tabs.get(beforeTabId)
+ const prev = anchorTab.prev
+ const insertIndex = (prev) ? (anchorTab.index + prev.index) / 2 : -1
+ tab.tabGroup.addTab(tab, insertIndex)
+ }
+}, EditorTabState)
+
+const TabReducer = action((s, action) => {
+ return renew(EditorTabState)
+})
+
+export default TabReducer
+
+const crossActionHandlers = handleActions({
+ [TAB_MOVE_TO_PANE]: (allState, { tabId, paneId }) => {
+ const pane = allState.PaneState.panes.get(paneId)
+ const tab = allState.EditorTabState.tabs.get(tabId)
+ tab.tabGroup.removeTab(tab)
+ pane.tabGroup.addTab(tab)
+ return allState
+ }
+}, store)
+
+
+export const TabCrossReducer = (state, action) => {
+ return state
+}
diff --git a/app/components/Editor/state.js b/app/components/Editor/state.js
new file mode 100644
index 00000000..0ec52e68
--- /dev/null
+++ b/app/components/Editor/state.js
@@ -0,0 +1,28 @@
+import { extendObservable, observable, computed } from 'mobx'
+import { TabStateScope } from 'commons/Tab'
+const { Tab: BaseTab, TabGroup: BaseTabGroup, entities: state } = TabStateScope()
+import PaneState from 'components/Pane/state'
+
+class Tab extends BaseTab {
+ constructor (config={}) {
+ super(config)
+ extendObservable(this, config)
+ }
+
+ @observable path = ''
+ @observable content = {}
+}
+
+class TabGroup extends BaseTabGroup {
+ constructor (config={}) {
+ super(config)
+ extendObservable(this, config)
+ }
+
+ @computed get pane () {
+ return PaneState.panes.values().find(pane => pane.contentId === this.id)
+ }
+}
+
+export default state
+export { Tab, TabGroup }
diff --git a/app/components/ImageEditor/index.jsx b/app/components/EditorWrapper/Editors/ImageEditor.jsx
similarity index 97%
rename from app/components/ImageEditor/index.jsx
rename to app/components/EditorWrapper/Editors/ImageEditor.jsx
index 249558b4..d4a3f984 100644
--- a/app/components/ImageEditor/index.jsx
+++ b/app/components/EditorWrapper/Editors/ImageEditor.jsx
@@ -1,5 +1,5 @@
import React, { Component, PropTypes } from 'react'
-import config from '../../config'
+import config from 'config'
import { request } from 'utils'
const previewPic = 'https://dn-coding-net-production-static.qbox.me/static/5d487aa5c207cf1ca5a36524acb953f1.gif'
diff --git a/app/components/UnknownEditor/index.jsx b/app/components/EditorWrapper/Editors/UnknownEditor.jsx
similarity index 97%
rename from app/components/UnknownEditor/index.jsx
rename to app/components/EditorWrapper/Editors/UnknownEditor.jsx
index 6895dee0..7597194d 100644
--- a/app/components/UnknownEditor/index.jsx
+++ b/app/components/EditorWrapper/Editors/UnknownEditor.jsx
@@ -1,6 +1,6 @@
import React, { Component, PropTypes } from 'react'
import filesize from 'filesize'
-import config from '../../config'
+import config from 'config'
class UnknownEditor extends Component {
constructor (props) {
diff --git a/app/components/EditorWrapper/Editors/WelcomeEditor.js b/app/components/EditorWrapper/Editors/WelcomeEditor.js
new file mode 100644
index 00000000..043621d5
--- /dev/null
+++ b/app/components/EditorWrapper/Editors/WelcomeEditor.js
@@ -0,0 +1,5 @@
+import React, { Component, PropTypes } from 'react'
+
+export default function () {
+ return Welcome
+}
diff --git a/app/components/EditorWrapper/index.jsx b/app/components/EditorWrapper/index.jsx
index f8eced84..a118a3c9 100644
--- a/app/components/EditorWrapper/index.jsx
+++ b/app/components/EditorWrapper/index.jsx
@@ -1,9 +1,10 @@
import React, { PropTypes } from 'react'
-import MarkdownEditor from '../MarkdownEditor'
-import ImageEditor from '../ImageEditor'
import CodeMirrorEditor from '../CodeMirrorEditor'
-import UnknownEditor from '../UnknownEditor'
-import * as Tab from '../Tab'
+import MarkdownEditor from '../MarkdownEditor'
+import ImageEditor from './Editors/ImageEditor'
+import UnknownEditor from './Editors/UnknownEditor'
+import WelcomeEditor from './Editors/WelcomeEditor'
+import { getTabType } from 'utils'
const editors = {
CodeMirrorEditor,
@@ -42,9 +43,9 @@ const EditorWrapper = ({ tab }, { i18n }) => {
const { path = '' } = tab
let type = 'default'
if (tab.contentType) {
- if (Tab.types.getTabType(tab) === 'IMAGE') {
+ if (getTabType(tab) === 'IMAGE') {
type = 'imageEditor'
- } else if (Tab.types.getTabType(tab) === 'UNKNOWN') {
+ } else if (getTabType(tab) === 'UNKNOWN') {
type = 'unknownEditor'
}
}
diff --git a/app/components/FileTree/actions.js b/app/components/FileTree/actions.js
index 4cab0508..e2263b25 100644
--- a/app/components/FileTree/actions.js
+++ b/app/components/FileTree/actions.js
@@ -1,7 +1,8 @@
import _ from 'lodash'
import { createAction } from 'redux-actions'
import api from '../../backendAPI'
-import * as Tab from '../Tab'
+import * as TabActions from 'commons/Tab/actions'
+import { getTabType } from 'utils'
import { updateUploadProgress } from '../StatusBar/actions'
export const FILETREE_SELECT_NODE = 'FILETREE_SELECT_NODE'
@@ -23,13 +24,13 @@ export function openNode (node, shouldBeFolded = null, deep = false) {
dispatch(toggleNodeFold(node, shouldBeFolded, deep))
}
} else {
- const tabType = Tab.types.getTabType(node)
+ const tabType = getTabType(node)
if (
- Tab.types.getTabType(node) === 'TEXT'
+ getTabType(node) === 'TEXT'
) {
api.readFile(node.path)
.then(data => {
- dispatch(Tab.actions.createTab({
+ dispatch(TabActions.createTab({
id: _.uniqueId('tab_'),
type: 'editor',
title: node.name,
@@ -44,7 +45,7 @@ export function openNode (node, shouldBeFolded = null, deep = false) {
}))
})
} else {
- dispatch(Tab.actions.createTab({
+ dispatch(TabActions.createTab({
id: _.uniqueId('tab_'),
type: 'editor',
title: node.name,
diff --git a/app/components/FileTree/subscribeToFileChange.js b/app/components/FileTree/subscribeToFileChange.js
index 5ebddb86..cdb734b1 100644
--- a/app/components/FileTree/subscribeToFileChange.js
+++ b/app/components/FileTree/subscribeToFileChange.js
@@ -2,8 +2,9 @@
import config from '../../config'
import api from '../../backendAPI'
import store, { dispatch } from '../../store'
+import mobxStore from '../../mobxStore'
import * as FileTreeActions from './actions'
-import { actions as TabActions, selectors as TabSelectors } from '../Tab'
+import * as TabActions from 'commons/Tab/actions'
export default function subscribeToFileChange () {
return api.websocketConnectedPromise.then(client =>
@@ -16,7 +17,7 @@ export default function subscribeToFileChange () {
break
case 'modify':
dispatch(FileTreeActions.loadNodeData([node]))
- var tabsToUpdate = TabSelectors.getTabsByPath(store.getState().TabState, node.path)
+ var tabsToUpdate = mobxStore.EditorTabState.tabs.values().filter(tab => tab.path === node.path)
if (tabsToUpdate.length) {
api.readFile(node.path).then(({ content }) => {
dispatch(TabActions.updateTabByPath({
diff --git a/app/components/Modal/FilePalette/component.jsx b/app/components/Modal/FilePalette/component.jsx
index 708d8cce..a8a856d4 100644
--- a/app/components/Modal/FilePalette/component.jsx
+++ b/app/components/Modal/FilePalette/component.jsx
@@ -3,7 +3,7 @@ import _ from 'lodash'
import React, { Component } from 'react'
import api from '../../../backendAPI'
import store, { dispatch as $d } from '../../../store'
-import * as Tab from '../../Tab'
+import * as TabActions from 'commons/Tab/actions'
import cx from 'classnames'
import { dispatchCommand } from '../../../commands/lib/keymapper'
@@ -70,7 +70,7 @@ class FilePalette extends Component {
api.readFile(node.path)
.then(data => {
- $d(Tab.actions.createTab({
+ $d(TabActions.createTab({
id: _.uniqueId('tab_'),
type: 'editor',
title: node.name || filename,
diff --git a/app/components/Pane/Pane.jsx b/app/components/Pane/Pane.jsx
index bb44cd0d..aebc8ac7 100644
--- a/app/components/Pane/Pane.jsx
+++ b/app/components/Pane/Pane.jsx
@@ -1,18 +1,18 @@
/* @flow weak */
import React, { PropTypes } from 'react'
-import { bindActionCreators } from 'redux'
-import { connect } from 'react-redux'
+import { observer } from 'mobx-react'
import cx from 'classnames'
import { confirmResize } from './actions'
-import TabContainer from '../Tab'
+import TabContainer from '../Editor'
import EditorWrapper from '../EditorWrapper'
import PaneAxis from './PaneAxis'
import ResizeBar from '../ResizeBar'
-const _Pane = (props) => {
- const { pane, parentFlexDirection, confirmResize } = props
+const Pane = observer(props => {
+ const { pane, parentFlexDirection } = props
const style = { flexGrow: pane.size, display: pane.display }
+
return (
{
> {pane.views.length // priortize `pane.views` over `pane.content`
?
:
-
+
}
{
parentFlexDirection={parentFlexDirection} />
)
-}
+})
-_Pane.propTypes = {
+Pane.propTypes = {
pane: PropTypes.object,
parentFlexDirection: PropTypes.string,
}
-const Pane = connect(
- (state, { paneId }) => ({ pane: state.PaneState.panes[paneId] }),
- dispatch => bindActionCreators({ confirmResize }, dispatch)
-)(_Pane)
-
export default Pane
diff --git a/app/components/Pane/PaneAxis.jsx b/app/components/Pane/PaneAxis.jsx
index 57da8f83..f8a135c8 100644
--- a/app/components/Pane/PaneAxis.jsx
+++ b/app/components/Pane/PaneAxis.jsx
@@ -1,8 +1,10 @@
/* @flow weak */
import React, { Component, PropTypes } from 'react'
+import { observer } from 'mobx-react'
import cx from 'classnames'
import Pane from './Pane'
+@observer
class PaneAxis extends Component {
static propTypes = {
id: PropTypes.string,
@@ -20,20 +22,20 @@ class PaneAxis extends Component {
onResizing (listener) { if (typeof listener === 'function') { this.resizingListeners.push(listener) } }
render () {
- const { pane } = this.props
+ const selfPane = this.props.pane
let Subviews
- if (pane.views.length) {
- Subviews = pane.views.map(paneId =>
-
+ if (selfPane.views.length) {
+ Subviews = selfPane.views.map(pane =>
+
)
} else {
- Subviews =
+ Subviews =
}
return (
{Subviews}
)
diff --git a/app/components/Pane/PanesContainer.jsx b/app/components/Pane/PanesContainer.jsx
index e3829edf..73b4b504 100644
--- a/app/components/Pane/PanesContainer.jsx
+++ b/app/components/Pane/PanesContainer.jsx
@@ -1,16 +1,15 @@
/* @flow weak */
import React, { Component } from 'react'
-import { connect } from 'react-redux'
+import { inject } from 'mobx-react'
import PaneAxis from './PaneAxis'
-import store from '../../store.js'
-var PrimaryPaneAxis = connect(state => {
- let rootPane = state.PaneState.panes[state.PaneState.rootPaneId]
+var PrimaryPaneAxis = inject(state => {
+ let rootPane = state.PaneState.rootPane
return { pane: rootPane }
})(PaneAxis)
-var PanesContainer = (props) => {
- return
+var PanesContainer = () => {
+ return
}
export default PanesContainer
diff --git a/app/components/Pane/actions.js b/app/components/Pane/actions.js
index 73665a01..d74a5103 100644
--- a/app/components/Pane/actions.js
+++ b/app/components/Pane/actions.js
@@ -4,8 +4,8 @@ import {
getPrevSibling,
} from './selectors'
-import { createAction } from 'redux-actions'
-import { promiseActionMixin } from '../../utils'
+import { createAction } from 'utils/actions'
+import { promiseActionMixin } from 'utils'
export const PANE_INITIALIZE = 'PANE_INITIALIZE'
export const PANE_UNSET_COVER = 'PANE_UNSET_COVER'
@@ -27,9 +27,7 @@ export const split = createAction(PANE_SPLIT_WITH_KEY,
)
export const PANE_SPLIT = 'PANE_SPLIT'
-export const splitTo = promiseActionMixin(
- createAction(PANE_SPLIT, (paneId, splitDirection) => ({paneId, splitDirection}))
-)
+export const splitTo = createAction.promise(PANE_SPLIT, (paneId, splitDirection) => ({paneId, splitDirection}))
export const PANE_UPDATE = 'PANE_UPDATE'
export const updatePane = createAction(PANE_UPDATE)
diff --git a/app/components/Pane/reducer.js b/app/components/Pane/reducer.js
index c5dc612b..5653abd7 100644
--- a/app/components/Pane/reducer.js
+++ b/app/components/Pane/reducer.js
@@ -1,7 +1,7 @@
-/* @flow weak */
import _ from 'lodash'
-import { update } from '../../utils'
-import { handleActions } from 'redux-actions'
+import { createTransformer } from 'mobx'
+import { handleActions } from 'utils/actions'
+import entities, { Pane } from './state'
import {
PANE_UPDATE,
PANE_SPLIT,
@@ -9,113 +9,16 @@ import {
PANE_CLOSE,
PANE_CONFIRM_RESIZE,
} from './actions'
-import {
- getPaneById,
- getParent,
- getNextSibling,
- getPrevSibling,
- getPanesWithPosMap,
-} from './selectors'
-const debounced = _.debounce(func => func(), 50)
-
-/**
- * The state shape:
- *
- * PaneState = {
- rootPaneId: PropTypes.string,
- panes: {
- [pane_id]: {
- id: PropTypes.string,
- flexDirection: PropTypes.string,
- size: PropTypes.number,
- parentId: PropTypes.string,
- views: PropTypes.arrayOf(PropTypes.string),
- content: PropTypes.shape({
- type: PropTypes.string,
- id: PropTypes.string,
- })
- }
- }
- }
-*/
-
-const Pane = (paneConfig) => {
- const defaults = {
- id: _.uniqueId('pane_view_'),
- flexDirection: 'row',
- size: 100,
- views: [],
- parentId: '',
- content: undefined,
- }
-
- return { ...defaults, ...paneConfig }
-}
-
-const rootPane = Pane({
- id: 'pane_view_1',
- flexDirection: 'row',
- size: 100,
- views: [],
- content: {
- type: 'tabGroup',
- id: ''
- }
-})
-const defaultState = {
- panes: {
- [rootPane.id]: rootPane
- },
- rootPaneId: rootPane.id
-}
-
-const _addSiblingAfterPane = (state, pane, siblingPane) => _addSibling(state, pane, siblingPane, 1)
-const _addSiblingBeforePane = (state, pane, siblingPane) => _addSibling(state, pane, siblingPane, -1)
-const _addSibling = (state, pane, siblingPane, indexOffset) => {
- let parent = getParent(state, pane)
- let atIndex = parent.views.indexOf(pane.id) + indexOffset
- parent.views.splice(atIndex, 0, siblingPane.id)
-
- return update(state, {
- panes: {
- [parent.id]: {$set: {...parent}},
- [siblingPane.id]: {$set: siblingPane}
- }
- })
-}
-
-function _hoistSingleChild (state, parent) {
- if (parent.views.length != 1) return state
- const singleChild = state.panes[parent.views[0]]
- if (singleChild.views.length > 0) {
- parent = update(parent, {
- views: {$set: singleChild.views},
- flexDirection: {$set: singleChild.flexDirection}
- })
- } else {
- parent = update(parent, {
- views: {$set: []},
- content: {$set: singleChild.content}
- })
- }
- let nextState = update(state, {panes: {[parent.id]: {$set: parent}}})
- nextState = update(nextState, {panes: {$delete: singleChild.id}})
- return _hoistSingleChild(nextState, parent)
-}
-export default handleActions({
- [PANE_UPDATE]: (state, action) => {
- const { id: paneId, tabGroupId } = action.payload
- let pane = getPaneById(state, paneId)
-
- return update(state, {
- panes: {[pane.id]: {content: {id: {$set: tabGroupId}}}}
- })
+const actionHandlers = handleActions({
+ [PANE_UPDATE]: (state, { id: paneId, tabGroupId }) => {
+ const pane = state.panes.get(paneId)
+ pane.contentId = tabGroupId
},
- [PANE_SPLIT]: (state, action) => {
- const { paneId, splitDirection } = action.payload
- let pane = getPaneById(state, paneId)
+ [PANE_SPLIT]: (state, { paneId, splitDirection }, action) => {
+ console.log(PANE_SPLIT + ' start');
+ const pane = state.panes.get(paneId)
/* ----- */
let flexDirection, newPane
if (splitDirection === 'center') {
@@ -133,105 +36,69 @@ export default handleActions({
flexDirection = 'column'
break
default:
- throw 'Pane.splitToDirection method requires param "splitDirection"'
+ throw Error('Pane.splitToDirection method requires param "splitDirection"')
}
- let parent = getParent(state, pane)
+ const parent = pane.parent
// If flexDirection is same as parent's,
// then we can simply push the newly splitted view into parent's "views" array
+ // debugger
if (parent && parent.flexDirection === flexDirection) {
- newPane = Pane({ parentId: parent.id, content: {type: 'tabGroup'} })
- action.meta.resolve(newPane.id)
+ newPane = new Pane({ parentId: parent.id })
if (splitDirection === 'right' || splitDirection === 'bottom') {
- return _addSiblingAfterPane(state, pane, newPane)
+ // return _addSiblingAfterPane(state, pane, newPane)
+ newPane.index = pane.index + 0.5
} else {
- return _addSiblingBeforePane(state, pane, newPane)
+ newPane.index = pane.index - 0.5
}
-
+ action.meta.resolve(newPane.id)
// If flexDirection is NOT the same as parent's,
// that means we should spawn a child pane
} else {
- pane = {...pane, flexDirection}
- let spawnedChild = Pane({parentId: pane.id, content: {...pane.content}})
- delete pane.content
- newPane = Pane({ parentId: pane.id, content: {type: 'tabGroup'} })
+ pane.flexDirection = flexDirection
+ const spawnedChild = new Pane({ parentId: pane.id, contentId: pane.contentId })
+ pane.contentId = null
+ newPane = new Pane({ parentId: pane.id })
if (splitDirection === 'right' || splitDirection === 'bottom') {
- pane.views = [spawnedChild.id, newPane.id]
+ spawnedChild.index = 0
+ newPane.index = 1
} else {
- pane.views = [newPane.id, spawnedChild.id]
+ spawnedChild.index = 1
+ newPane.index = 0
+ console.log(newPane.index, spawnedChild.index);
}
action.meta.resolve(newPane.id)
-
- return update(state, {
- panes: {
- [newPane.id]: {$set: newPane},
- [pane.id]: {$set: pane},
- [spawnedChild.id]: {$set: spawnedChild},
- }
- })
}
},
- [PANE_CLOSE]: (state, action) => {
- const { paneId, targetTabGroupId, sourceTabGroupId } = action.payload
- let parent = getParent(state, paneId)
- let nextState = state
+ [PANE_CLOSE]: (state, { paneId }) => {
+ let pane = state.panes.get(paneId)
+ let parent = pane.parent
// the `mergeTabGroups` part of the action is handled inside `Tab/reducer.js`
- parent = update(parent, {views: {$without: paneId}})
- nextState = update(nextState, {panes: {[parent.id]: {$set: parent}}})
- nextState = _hoistSingleChild(nextState, parent)
- parent = nextState.panes[parent.id]
- nextState = update(nextState, {panes: {[parent.id]: {$set: parent}}})
- nextState = update(nextState, {panes: {$delete: paneId}})
- return nextState
+ // if parent is about to have only one child left
+ // we short-circut parent.content to the-pane-to-delete.content
+ if (parent.views.length === 2) parent.contentId = pane.contentId
+ entities.panes.delete(pane.id)
},
- [PANE_CONFIRM_RESIZE]: (state, { payload: { leftView, rightView } }) => {
- return update(state, {
- panes: {
- [leftView.id]: { size: { $set: leftView.size } },
- [rightView.id]: { size: { $set: rightView.size } },
- }
- })
- }
-}, defaultState)
-
-
-export const PaneCrossReducer = handleActions({
- [PANE_SPLIT_WITH_KEY]: (allStates, action) => {
- return allStates
- // const { PaneState, TabState } = allStates
- // const { splitCount, flexDirection } = action.payload
- // let rootPane = PaneState.panes[PaneState.rootPaneId]
-
- // if (
- // (splitCount === rootPane.views.length && flexDirection === rootPane.flexDirection) ||
- // (splitCount === 1 && rootPane.views.length === 0 && rootPane.content)
- // ) {
- // return allStates
- // }
-
- // // if rootPane has children
- // if (rootPane.views.length) {
-
- // if (splitCount > rootPane.views.length) {
- // // this is the easier case where we simply increase panes
- // rootPane.views
+ [PANE_CONFIRM_RESIZE]: (state, { leftView, rightView }) => {
+ state.panes[leftView.id].size = leftView.size
+ state.panes[rightView.id].size = rightView.size
+ },
+}, entities)
- // } else {
- // // this is the harder case where we need to merge tabGroups
- // }
- // }
+const transform = createTransformer(entities => {
+ return {
+ panes: entities.panes.toJS(),
+ rootPaneId: entities.rootPaneId,
}
})
+export default function (state, action) {
+ return transform(entities)
+}
-
-
-
-
-
-
+export const PaneCrossReducer = f => f
diff --git a/app/components/Pane/state.js b/app/components/Pane/state.js
new file mode 100644
index 00000000..2e2e56ab
--- /dev/null
+++ b/app/components/Pane/state.js
@@ -0,0 +1,81 @@
+import uniqueId from 'lodash/uniqueId'
+import { extendObservable, observable, computed, autorun } from 'mobx'
+import EditorTabState, { TabGroup } from 'components/Editor/state'
+
+const state = observable({
+ panes: observable.map({}),
+ activePaneId: null,
+ rootPaneId: null,
+ get rootPane () {
+ const rootPane = this.panes.get(this.rootPaneId)
+ return rootPane || this.panes.values()[0]
+ },
+ get activePane () {
+ const activePane = this.panes.get(this.activePaneId)
+ return activePane || this.rootPane
+ },
+})
+
+class BasePane {
+ constructor (paneConfig) {
+ const defaults = {
+ id: uniqueId('pane_view_'),
+ flexDirection: 'row',
+ size: 100,
+ parentId: '',
+ index: 0,
+ }
+
+ paneConfig = Object.assign({}, defaults, paneConfig)
+ extendObservable(this, paneConfig)
+ state.panes.set(this.id, this)
+ }
+
+ @computed
+ get parent () {
+ return state.panes.get(this.parentId)
+ }
+
+ @computed
+ get views () {
+ return state.panes.values()
+ .filter(pane => pane.parentId === this.id)
+ .sort((a, b) => a.index - b.index)
+ }
+}
+
+class Pane extends BasePane {
+ constructor (paneConfig) {
+ super(paneConfig)
+ this.contentType = 'tabGroup'
+ const tabGroup = this.tabGroup || new TabGroup()
+ this.contentId = tabGroup.id
+ }
+
+ @observable contentId = ''
+
+ @computed
+ get tabGroup () {
+ return EditorTabState.tabGroups.get(this.contentId)
+ }
+}
+
+const rootPane = new Pane({
+ id: 'pane_view_1',
+ flexDirection: 'row',
+ size: 100,
+})
+
+state.panes.set(rootPane.id, rootPane)
+state.rootPaneId = rootPane.id
+
+autorun(() => {
+ state.panes.forEach(parentPane =>
+ parentPane.views.forEach((pane, index) => {
+ if (pane.index !== index) pane.index = index
+ })
+ )
+})
+
+export default state
+export { Pane }
diff --git a/app/components/Panel/PanelContent.jsx b/app/components/Panel/PanelContent.jsx
index 4ff201fd..f7f0baa8 100644
--- a/app/components/Panel/PanelContent.jsx
+++ b/app/components/Panel/PanelContent.jsx
@@ -6,8 +6,7 @@ import StatusBar from '../StatusBar'
import PanesContainer from '../Pane'
import FileTree from '../FileTree'
import ExtensionPanelContent from './ExtensionPanelContent'
-import Terminal from '../Terminal'
-import TabContainer from '../Tab'
+import TerminalContainer from '../Terminal'
import SideBar, { SideBar2 } from './SideBar'
import { SidePanelContainer, SidePanelView } from './SidePanel'
import GitHistoryView from '../Git/GitHistoryView'
@@ -47,11 +46,10 @@ const PanelContent = ({ panel }) => {
)
case 'PANEL_BOTTOM':
- // return
return (
-
+
diff --git a/app/components/Panel/PanelsContainer.js b/app/components/Panel/PanelsContainer.js
index 2a2e92ca..56f64651 100644
--- a/app/components/Panel/PanelsContainer.js
+++ b/app/components/Panel/PanelsContainer.js
@@ -2,13 +2,7 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
-import store from '../../store.js'
import PanelAxis from './PanelAxis'
-import * as PanelActions from './actions'
-import PanesContainer from '../Pane'
-import TabContainer from '../Tab'
-import Terminal from '../Terminal'
-import FileTree from '../FileTree'
const PrimaryPanelAxis = connect(state =>
({panel: state.PanelState.panels[state.PanelState.rootPanelId]})
diff --git a/app/components/Setting/reducer.js b/app/components/Setting/reducer.js
index 3490c731..08bdc412 100644
--- a/app/components/Setting/reducer.js
+++ b/app/components/Setting/reducer.js
@@ -1,7 +1,7 @@
/* @flow weak */
import { handleActions } from 'redux-actions'
import { OrderedMap } from 'immutable'
-import { changeTheme, changeCodeTheme } from '../../utils/themeManager'
+import { changeTheme } from '../../utils/themeManager'
import {
SETTING_ACTIVATE_TAB,
SETTING_UPDATE_FIELD,
@@ -123,7 +123,6 @@ export default handleActions({
[SETTING_UPDATE_FIELD]: (state, action) => {
const { domain, fieldName, value } = action.payload
if (fieldName === 'UI Theme') { changeTheme(value); }
- if (fieldName === 'Syntax Theme') { changeCodeTheme(value); }
return {
...state,
views: { ...state.views,
diff --git a/app/components/Tab/TabBar.jsx b/app/components/Tab/TabBar.jsx
deleted file mode 100644
index 86f7a9e4..00000000
--- a/app/components/Tab/TabBar.jsx
+++ /dev/null
@@ -1,203 +0,0 @@
-import React, { Component, PropTypes } from 'react';
-import { connect } from 'react-redux';
-import cx from 'classnames';
-import { dragStart } from '../DragAndDrop/actions';
-import Menu from '../Menu'
-import * as TabActions from './actions';
-import * as PaneActions from '../Pane/actions';
-import ContextMenu from '../ContextMenu'
-
-const dividItem = { name: '-' }
-const items = [
- {
- name: 'Close',
- icon: '',
- command: 'tab:close'
- }, {
- name: 'Close Others',
- icon: '',
- command: 'tab:close_other'
- }, {
- name: 'Close All',
- icon: '',
- command: 'tab:close_all'
- }
-]
-const itemsSplit = [
- dividItem,
- {
- name: 'Vertical Split',
- icon: '',
- command: 'tab:split_v'
- }, {
- name: 'Horizontal Split',
- icon: '',
- command: 'tab:split_h'
- }
-]
-
-class _TabBar extends Component {
- constructor (props) {
- super(props)
- this.state = {
- showDropdownMenu: false
- }
- }
-
- static propTypes = {
- tabGroupId: PropTypes.string,
- tabIds: PropTypes.array,
- isDraggedOver: PropTypes.bool,
- addTab: PropTypes.func,
- closePane: PropTypes.func,
- isRootPane: PropTypes.bool
- }
-
- makeDropdownMenuItems = () => {
- let baseItems = this.props.isRootPane ? []
- : [{
- name: 'Close Pane',
- command: this.props.closePane,
- }]
- const tabs = this.props.tabs
- const tabLabelsItem = tabs && tabs.map(tab => ({
- name: tab.title || 'untitled',
- command: e => this.props.activateTab(tab.id)
- }))
-
- if (tabLabelsItem.length) {
- return baseItems.concat({name: '-'}, tabLabelsItem)
- } else {
- return baseItems
- }
- }
-
- renderDropdownMenu () {
- const dropdownMenuItems = this.makeDropdownMenuItems()
- if (this.state.showDropdownMenu && dropdownMenuItems.length) {
- return