Skip to content

Tab label enhance #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions app/api/fileAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,12 @@ export function readFile (path) {
})
}

export function createFile (path, content, base64) {
export function createFile (path) {
return request({
method: 'POST',
url: `/workspaces/${config.spaceKey}/files`,
form: {
path: path,
content: content || '',
base64: base64 || false,
override: true,
createParent: true
path: path
}
})
}
Expand Down
48 changes: 38 additions & 10 deletions app/commands/commandBindings/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import store, { getState, dispatch } from '../../store'
import { path as pathUtil } from '../../utils'
import api from '../../api'
import * as _Modal from '../../components/Modal/actions'
import * as _Tab from '../../components/Tab/actions'
import { notify } from '../../components/Notification/actions'

const Modal = bindActionCreators(_Modal, dispatch)
const Tab = bindActionCreators(_Tab, dispatch)

const nodeToNearestDirPath = (node) => {
if (!node) node = {isDir:true, path:'/'} // fake a root node if !node
Expand All @@ -25,20 +27,26 @@ const nodeToParentDirPath = (node) => {
return pathSplitted.join('/')+'/'
}

function createFileWithContent (content) {
return function createFileAtPath (path) {
return api.createFile(path, content)
.then(() => {if (content) api.writeFile(path, content)})
.then(Modal.dismissModal)
.then(() => path)
// if error, try again.
.catch(err=>
Modal.updateModal({statusMessage:err.msg}).then(createFileAtPath)
)
}
}

export default {
'file:new_file': (c) => {
var node = c.context
var path = nodeToNearestDirPath(node)
var defaultValue = pathUtil.join(path, 'untitled')

const createFile = (pathValue) => {
api.createFile(pathValue)
.then( () => Modal.dismissModal() )
// if error, try again.
.catch(err=>
Modal.updateModal({statusMessage:err.msg}).then(createFile)
)
}
const createFile = createFileWithContent(null)

Modal.showModal('Prompt', {
message: 'Enter the path for the new file.',
Expand All @@ -49,9 +57,29 @@ export default {


'file:save': (c) => {
var activeTab = getState().TabState.activeGroup.activeTab;
var activeTab = getState().TabState.getActiveGroup().activeTab;
var content = activeTab.editor.getValue();
api.writeFile(activeTab.path, content);

if (!activeTab.path) {
const createFile = createFileWithContent(content)
Modal.showModal('Prompt', {
message: 'Enter the path for the new file.',
defaultValue: '/untitled',
selectionRange: [1, '/untitled'.length]
})
.then(createFile)
.then(path => Tab.updateTab({
id: activeTab.id,
path: path,
title: path.replace(/^.*\/([^\/]+$)/, '$1')
}))
.then(() => Tab.updateTabFlags(activeTab.id, 'modified', false))

} else {
api.writeFile(activeTab.path, content)
.then(() => Tab.updateTabFlags(activeTab.id, 'modified', false))
}

},


Expand Down
15 changes: 12 additions & 3 deletions app/components/AceEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { connect } from 'react-redux';
import _ from 'lodash';
import ReactAce from './ReactAce';
import * as EditorActions from './actions';
import * as TabActions from '../Tab/actions';
import getMode from './getAceMode';

import 'brace/mode/java';
Expand Down Expand Up @@ -43,13 +44,13 @@ class AceEditor extends Component {
width: '100%',
};

constructor(props) {
constructor (props) {
super(props);
this.state = {};
this.state.name = this.props.name || _.uniqueId('ide_editor_');
}

componentDidMount() {
componentDidMount () {
const {theme, mode, tab, dispatch} = this.props;
var editor = this.editor = ace.edit(this.state.name);
editor.setTheme(`ace/theme/${theme}`);
Expand All @@ -64,10 +65,11 @@ class AceEditor extends Component {
}
editor.focus();
tab.editor = editor;
editor.session.on('change', this.onChange)
setTimeout( () => editor.resize(), 0);
}

render() {
render () {
const { width, height } = this.props;
const name = this.state.name;
const divStyle = { width, height };
Expand All @@ -79,6 +81,13 @@ class AceEditor extends Component {
></div>
);
}

onChange = (e) => {
const {tab, dispatch} = this.props;
if (!tab.flags.modified) {
dispatch(TabActions.updateTabFlags(tab.id, 'modified', true))
}
}
}

AceEditor = connect(null, null)(AceEditor);
Expand Down
27 changes: 27 additions & 0 deletions app/components/Tab/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,30 @@ export function removeGroup (groupId) {
groupId: groupId
}
}

export const TAB_UPDATE = 'TAB_UPDATE'
export function updateTab(tabConfig) {
return {
type: TAB_UPDATE,
payload: tabConfig
}
}

export const TAB_UPDATE_FLAGS = 'TAB_UPDATE_FLAGS'
export function updateTabFlags (tabId, flag, value=true) {
if (!arguments.length) return
var payload = { tabId }

if (typeof flag === 'string') {
payload.flags = {[flag]: arguments[2]}
} else if (typeof flag === 'object') {
payload.flags = flag
} else {
return
}

return {
type: TAB_UPDATE_FLAGS,
payload
}
}
13 changes: 9 additions & 4 deletions app/components/Tab/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,16 @@ const Tab = ({tab, removeTab, activateTab}) => {

return (
<li className={cx('tab', {
active: tab.isActive
active: tab.isActive,
modified: tab.flags.modified
})}
onClick={e => activateTab(tab.id)} >
<div className='title'>{tab.title}</div>
<div className='close' onClick={e => { e.stopPropagation(); removeTab(tab.id) }}>×</div>
onClick={e => activateTab(tab.id)}
>
<div className='title'>{tab.title}</div>
<div className='control'>
<i className='close' onClick={e => { e.stopPropagation(); removeTab(tab.id) }}>×</i>
<i className='dot'></i>
</div>
</li>
)
}
Expand Down
27 changes: 24 additions & 3 deletions app/components/Tab/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,31 @@ import {
TAB_ACTIVATE,
TAB_CREATE_GROUP,
TAB_REMOVE_GROUP,
TAB_DISSOLVE_GROUP
TAB_DISSOLVE_GROUP,
TAB_UPDATE,
TAB_UPDATE_FLAGS
} from './actions'

class Tab {
constructor (tab = {}, tabGroup) {
const {id, isActive, status, icon, title, path, content} = tab
const {id, isActive, flags, icon, title, path, content} = tab
this.id = id || _.uniqueId('tab_')
this.status = status || 'modified'
this.flags = flags || {}
this.icon = icon || 'fa fa-folder-o'
this.title = title || 'untitled'
this.isActive = isActive || true
this.path = path
this.content = '' || content
this.group = tabGroup
}

update (tab) {
_.extend(this, tab)
}

updateFlags (flags) {
_.extend(this.flags, flags)
}
}

class TabGroup {
Expand Down Expand Up @@ -179,6 +189,17 @@ export default function TabReducer (state = _state, action) {
_.remove(tabGroups, {id: action.groupId})
return normalizeState(state)

case TAB_UPDATE:
var tabConfig = action.payload
var tab = getTabById(tabConfig.id)
tab.update(tabConfig)
return normalizeState(state)

case TAB_UPDATE_FLAGS:
var tab = getTabById(action.payload.tabId)
tab.updateFlags(action.payload.flags)
return normalizeState(state)

default:
return state
}
Expand Down
9 changes: 5 additions & 4 deletions app/components/Terminal/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import _ from 'lodash';
import { connect } from 'react-redux';

import terms from './terminal-client';
import * as TabActions from '../Tab/actions';

class Term extends Component {
static contextTypes = {
Expand Down Expand Up @@ -69,10 +70,10 @@ class Term extends Component {
}


connect(state => {

}, dispatch => {

Term = connect(null, dispatch => {
return {
handleTabTitle: (id, title) => dispatch(TabActions.updateTab({title, id}))
}
})(Term)

export default Term;
60 changes: 55 additions & 5 deletions app/styles/components/TabView.styl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
$tab-height = 30px;
$tab-control-width = 16px;

.tab-component {
position: relative;
Expand Down Expand Up @@ -31,6 +32,7 @@ $tab-height = 30px;
}

.tab-bar {
noSelect();
flex: 0 0 auto;
display: flex;
align-items: center;
Expand Down Expand Up @@ -69,18 +71,66 @@ $tab-height = 30px;
flex-grow: 1;
display: flex;
align-items: center;
.status{
padding: 3px;
}
cursor: default;

.title {
text-overflow: ellipsis;
padding: 0 4px;
}
.close {
padding: 0 4px;

.control {
min-width: $tab-control-width;
width: $tab-control-width;
height: $tab-control-width;
cursor: pointer;
margin-left: auto;
position: relative;
}
}


.tab {
// case default:
i {
absolute(0);
line-height: $tab-control-width;
text-align: center;
font-style: normal;
}

.dot {
height: $tab-control-width*0.5;
width: $tab-control-width*0.5;
margin-top: $tab-control-width*0.25;
margin-left: $tab-control-width*0.25;
border-radius: 100%;
background-color: #5b91d7;
display: none;
}

.close {
margin-top: -1px;
display: none;
}

// case hover-on-tab / tab-is-active:
&.active, &:hover {
.close {display: block;}
}

// case modified flag:
&.modified {
.dot {display: block;}
}

// case hover-on-tab-control
.control:hover {
.dot {display: none;}
}

}


.tab-content-item {
display: none;
&.active {
Expand Down
8 changes: 0 additions & 8 deletions app/styles/main.styl
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@ ul, ol {
padding: 0;
}

.no-select {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}

.hidden {display: none;}

html, body, #root {
Expand Down
2 changes: 1 addition & 1 deletion app/styles/themes/dark/styles/tabs.styl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

.tab {
font-size: $font-size;
color: $text-color;
color: $text-color-subtle;
height: $tab-height;
line-height: $tab-height;
border-right: 1px solid $tab-border-color;
Expand Down
2 changes: 1 addition & 1 deletion app/styles/themes/light/styles/tabs.styl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

.tab {
font-size: $font-size;
color: $text-color;
color: $text-color-subtle;
height: $tab-height;
line-height: $tab-height;
border-right: 1px solid $tab-border-color;
Expand Down
Loading