1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 17:56:25 +00:00

rewite whole code

add dataApi
renew PreferencesModal
This commit is contained in:
Dick Choi
2016-07-14 13:58:14 +09:00
parent 9ff70c4aef
commit 44f270f408
50 changed files with 2572 additions and 2496 deletions

View File

@@ -0,0 +1,416 @@
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ConfigTab.styl'
import fetchConfig from 'browser/lib/fetchConfig'
import hljsTheme from 'browser/lib/hljsThemes'
import ConfigManager from 'browser/main/lib/ConfigManager'
import store from 'browser/main/store'
const electron = require('electron')
const ipc = electron.ipcRenderer
const remote = electron.remote
const ace = window.ace
const OSX = global.process.platform === 'darwin'
class ConfigTab extends React.Component {
constructor (props) {
super(props)
this.state = {
isHotkeyHintOpen: false,
config: props.config
}
}
componentDidMount () {
this.handleSettingDone = () => {
this.setState({keymapAlert: {
type: 'success',
message: 'Successfully done!'
}})
}
this.handleSettingError = (err) => {
this.setState({keymapAlert: {
type: 'error',
message: err.message != null ? err.message : 'Error occurs!'
}})
}
ipc.addListener('APP_SETTING_DONE', this.handleSettingDone)
ipc.addListener('APP_SETTING_ERROR', this.handleSettingError)
}
componentWillUnmount () {
ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone)
ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError)
}
submitHotKey () {
ipc.send('hotkeyUpdated', this.state.keymap)
}
submitConfig () {
ipc.send('configUpdated', this.state.config)
}
handleSaveButtonClick (e) {
this.submitHotKey()
}
handleConfigSaveButtonClick (e) {
this.submitConfig()
}
handleKeyDown (e) {
if (e.keyCode === 13) {
this.submitHotKey()
}
}
handleConfigKeyDown (e) {
if (e.keyCode === 13) {
this.submitConfig()
}
}
handleLineNumberingClick (e) {
let config = this.state.config
config['preview-line-number'] = e.target.checked
this.setState({
config
})
}
handleDisableDirectWriteClick (e) {
let config = this.state.config
config['disable-direct-write'] = e.target.checked
this.setState({
config
})
}
handleHintToggleButtonClick (e) {
this.setState({
isHotkeyHintOpen: !this.state.isHotkeyHintOpen
})
}
handleHotkeyChange (e) {
let { config } = this.state
config.hotkey = {
toggleFinder: this.refs.toggleFinder.value,
toggleMain: this.refs.toggleMain.value
}
this.setState({
config
})
}
handleUIChange (e) {
let { config } = this.state
config.ui = {
theme: this.refs.uiTheme.value,
disableDirectWrite: this.refs.uiD2w != null
? this.refs.uiD2w.checked
: false
}
config.editor = {
theme: this.refs.editorTheme.value,
fontSize: this.refs.editorFontSize.value,
fontFamily: this.refs.editorFontFamily.value,
indentType: this.refs.editorIndentType.value,
indentSize: this.refs.editorIndentSize.value,
switchPreview: this.refs.editorSwitchPreview.value
}
config.preview = {
fontSize: this.refs.previewFontSize.value,
fontFamily: this.refs.previewFontFamily.value,
codeBlockTheme: this.refs.previewCodeBlockTheme.value,
lineNumber: this.refs.previewLineNumber.checked
}
this.setState({
config
})
}
handleSaveUIClick (e) {
let newConfig = {
ui: this.state.config.ui,
editor: this.state.config.editor,
preview: this.state.config.preview
}
ConfigManager.set(newConfig)
store.dispatch({
type: 'SET_UI',
config: newConfig
})
}
render () {
let keymapAlert = this.state.keymapAlert
let keymapAlertElement = keymapAlert != null
? <p className={`alert ${keymapAlert.type}`}>
{keymapAlert.message}
</p>
: null
let aceThemeList = ace.require('ace/ext/themelist')
let hljsThemeList = hljsTheme()
let { config } = this.state
return (
<div styleName='root'>
<div styleName='group'>
<div styleName='group-header'>Hotkey</div>
<div styleName='group-section'>
<div styleName='group-section-label'>Toggle Main</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleHotkeyChange(e)}
ref='toggleMain'
value={config.hotkey.toggleMain}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>Toggle Finder(popup)</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleHotkeyChange(e)}
ref='toggleFinder'
value={config.hotkey.toggleFinder}
type='text'
/>
</div>
</div>
<div styleName='group-control'>
<button styleName='group-control-leftButton'
onClick={(e) => this.handleHintToggleButtonClick(e)}
>
{this.state.isHotkeyHintOpen
? 'Hide Hint'
: 'Show Hint'
}
</button>
<button styleName='group-control-rightButton'
onClick={(e) => this.handleSaveButtonClick(e)}>Save Hotkey
</button>
{keymapAlertElement}
</div>
{this.state.isHotkeyHintOpen &&
<div styleName='group-hint'>
<p>Available Keys</p>
<ul>
<li><code>0</code> to <code>9</code></li>
<li><code>A</code> to <code>Z</code></li>
<li><code>F1</code> to <code>F24</code></li>
<li>Punctuations like <code>~</code>, <code>!</code>, <code>@</code>, <code>#</code>, <code>$</code>, etc.</li>
<li><code>Plus</code></li>
<li><code>Space</code></li>
<li><code>Backspace</code></li>
<li><code>Delete</code></li>
<li><code>Insert</code></li>
<li><code>Return</code> (or <code>Enter</code> as alias)</li>
<li><code>Up</code>, <code>Down</code>, <code>Left</code> and <code>Right</code></li>
<li><code>Home</code> and <code>End</code></li>
<li><code>PageUp</code> and <code>PageDown</code></li>
<li><code>Escape</code> (or <code>Esc</code> for short)</li>
<li><code>VolumeUp</code>, <code>VolumeDown</code> and <code>VolumeMute</code></li>
<li><code>MediaNextTrack</code>, <code>MediaPreviousTrack</code>, <code>MediaStop</code> and <code>MediaPlayPause</code></li>
</ul>
</div>
}
</div>
<div styleName='group'>
<div styleName='group-header'>UI</div>
<div styleName='group-section'>
<div styleName='group-section-label'>Theme</div>
<div styleName='group-section-control'>
<select value={config.ui.theme}
onChange={(e) => this.handleUIChange(e)}
ref='uiTheme'
disabled
>
<option value='default'>Light</option>
<option value='dark'>Dark</option>
</select>
</div>
</div>
{
global.process.platform === 'win32'
? <div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.ui.disableDirectWrite}
refs='uiD2w'
disabled={OSX}
type='checkbox'
/>
Disable Direct Write(It will be applied after restarting)
</label>
</div>
: null
}
<div styleName='group-header2'>Editor</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Editor Theme
</div>
<div styleName='group-section-control'>
<select value={config.editor.theme}
ref='editorTheme'
onChange={(e) => this.handleUIChange(e)}
>
{
aceThemeList.themes.map((theme) => {
return (<option value={theme.name} key={theme.name}>{theme.caption}</option>)
})
}
</select>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Editor Font Size
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
ref='editorFontSize'
value={config.editor.fontSize}
onChange={(e) => this.handleUIChange(e)}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Editor Font Family
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
ref='editorFontFamily'
value={config.editor.fontFamily}
onChange={(e) => this.handleUIChange(e)}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Editor Indent Style
</div>
<div styleName='group-section-control'>
<select value={config.editor.indentSize}
ref='editorIndentSize'
onChange={(e) => this.handleUIChange(e)}
>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='4'>4</option>
<option value='8'>8</option>
</select>&nbsp;
<select value={config.editor.indentType}
ref='editorIndentType'
onChange={(e) => this.handleUIChange(e)}
>
<option value='space'>Spaces</option>
<option value='tab'>Tabs</option>
</select>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Switching Preview
</div>
<div styleName='group-section-control'>
<select value={config.editor.switchPreview}
ref='editorSwitchPreview'
onChange={(e) => this.handleUIChange(e)}
>
<option value='BLUR'>When Editor Blurred</option>
<option value='RIGHTCLICK'>When Right Clicking</option>
</select>
</div>
</div>
<div styleName='group-header2'>Preview</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Preview Font Size
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
value={config.preview.fontSize}
ref='previewFontSize'
onChange={(e) => this.handleUIChange(e)}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Preview Font Family
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
ref='previewFontFamily'
value={config.preview.fontFamily}
onChange={(e) => this.handleUIChange(e)}
type='text'
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>Code block Theme</div>
<div styleName='group-section-control'>
<select value={config.preview.codeBlockTheme}
ref='previewCodeBlockTheme'
onChange={(e) => this.handleUIChange(e)}
>
{
hljsThemeList.map((theme) => {
return (<option value={theme.name} key={theme.name}>{theme.caption}</option>)
})
}
</select>
</div>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.preview.lineNumber}
ref='previewLineNumber'
type='checkbox'
/>&nbsp;
Code block line numbering
</label>
</div>
<div className='group-control'>
<button styleName='group-control-rightButton'
onClick={(e) => this.handleSaveUIClick(e)}
>
Save UI Config
</button>
</div>
</div>
</div>
)
}
}
ConfigTab.propTypes = {
user: PropTypes.shape({
name: PropTypes.string
}),
dispatch: PropTypes.func
}
export default CSSModules(ConfigTab, styles)

View File

@@ -0,0 +1,80 @@
.root
padding 15px
color $ui-text-color
.group
margin-bottom 45px
.group-header
font-size 24px
color $ui-text-color
padding 5px
border-bottom $default-border
margin-bottom 15px
.group-header2
font-size 18px
color $ui-text-color
padding 5px
margin-bottom 15px
.group-section
margin-bottom 15px
display flex
line-height 30px
.group-section-label
width 150px
text-align right
margin-right 10px
.group-section-control
flex 1
.group-section-control-input
height 30px
vertical-align middle
width 150px
font-size 12px
border solid 1px $border-color
border-radius 2px
padding 0 5px
.group-checkBoxSection
margin-bottom 15px
display flex
line-height 30px
padding-left 15px
.group-control
border-top $default-border
padding-top 10px
box-sizing border-box
height 40px
text-align right
.group-control-leftButton
float left
colorDefaultButton()
border $default-border
border-radius 2px
height 30px
padding 0 15px
margin-right 5px
.group-control-rightButton
float right
colorPrimaryButton()
border none
border-radius 2px
height 30px
padding 0 15px
margin-right 5px
.group-hint
border $ui-border
padding 10px 15px
margin 15px 0
border-radius 5px
background-color $ui-backgroundColor
color $ui-inactive-text-color
ul
list-style inherit
padding-left 1em
line-height 1.2
p
line-height 1.2

View File

@@ -0,0 +1,39 @@
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoTab.styl'
const appVersion = global.process.version
class InfoTab extends React.Component {
constructor (props) {
super(props)
this.state = {
}
}
render () {
return (
<div styleName='root'>
<div styleName='top'>
<img styleName='icon' src='../resources/app.png' width='150' height='150'/>
<div styleName='appId'>Boostnote {appVersion}</div>
<div styleName='madeBy'>Made by MAISIN&CO.</div>
</div>
<ul>
<li>
- License : GPLv3
</li>
<li>
- Issue Tracker : <a href='https://github.com/BoostIO/Boostnote/issues'>https://github.com/BoostIO/Boostnote/issues</a>
</li>
</ul>
</div>
)
}
}
InfoTab.propTypes = {
}
export default CSSModules(InfoTab, styles)

View File

@@ -0,0 +1,13 @@
.root
padding 15px
white-space pre
line-height 1.4
color $ui-text-color
.top
text-align center
margin-bottom 25px
.appId
font-size 18px
.madeBy
font-size 12px
$ui-inactive-text-color

View File

@@ -0,0 +1,37 @@
.root
modal()
max-width 540px
min-height 400px
height 80%
overflow hidden
position relative
.nav
absolute top left right
height 50px
background-color $ui-backgroundColor
border-bottom solid 1px $ui-borderColor
.nav-button
width 80px
height 50px
border none
background-color transparent
color #939395
font-size 14px
&:hover
color #515151
.nav-button--active
@extend .nav-button
color #6AA5E9
&:hover
color #6AA5E9
.nav-button-icon
display block
.content
absolute left right bottom
top 50px
overflow-y auto

View File

@@ -0,0 +1,299 @@
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './StorageItem.styl'
import consts from 'browser/lib/consts'
import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
const electron = require('electron')
const { shell, remote } = electron
const { Menu, MenuItem } = remote
class UnstyledFolderItem extends React.Component {
constructor (props) {
super(props)
this.state = {
status: 'IDLE',
folder: {
color: props.color,
name: props.name
}
}
}
handleEditChange (e) {
let { folder } = this.state
folder.name = this.refs.nameInput.value
this.setState({
folder
})
}
handleConfirmButtonClick (e) {
let { storage, folder } = this.props
dataApi
.updateFolder(storage.key, folder.key, {
color: this.state.folder.color,
name: this.state.folder.name
})
.then((storage) => {
store.dispatch({
type: 'UPDATE_STORAGE',
storage: storage
})
this.setState({
status: 'IDLE'
})
})
}
handleColorButtonClick (e) {
var menu = new Menu()
consts.FOLDER_COLORS.forEach((color, index) => {
menu.append(new MenuItem({
label: consts.FOLDER_COLOR_NAMES[index],
click: (e) => {
let { folder } = this.state
folder.color = color
this.setState({
folder
})
}
}))
})
menu.popup(remote.getCurrentWindow())
}
handleCancelButtonClick (e) {
this.setState({
status: 'IDLE'
})
}
renderEdit (e) {
return (
<div styleName='folderList-item'>
<div styleName='folderList-item-left'>
<button styleName='folderList-item-left-colorButton' style={{color: this.state.folder.color}}
onClick={(e) => this.handleColorButtonClick(e)}
>
<i className='fa fa-square'/>
</button>
<input styleName='folderList-item-left-nameInput'
value={this.state.folder.name}
ref='nameInput'
onChange={(e) => this.handleEditChange(e)}
/>
</div>
<div styleName='folderList-item-right'>
<button styleName='folderList-item-right-confirmButton'
onClick={(e) => this.handleConfirmButtonClick(e)}
>
Confirm
</button>
<button styleName='folderList-item-right-button'
onClick={(e) => this.handleCancelButtonClick(e)}
>
Cancel
</button>
</div>
</div>
)
}
handleDeleteConfirmButtonClick (e) {
let { storage, folder } = this.props
dataApi
.removeFolder(storage.key, folder.key)
.then((storage) => {
store.dispatch({
type: 'REMOVE_FOLDER',
key: folder.key,
storage: storage
})
})
}
renderDelete () {
return (
<div styleName='folderList-item'>
<div styleName='folderList-item-left'>
Are you sure to <span styleName='folderList-item-left-danger'>delete</span> this folder?
</div>
<div styleName='folderList-item-right'>
<button styleName='folderList-item-right-dangerButton'
onClick={(e) => this.handleDeleteConfirmButtonClick(e)}
>
Confirm
</button>
<button styleName='folderList-item-right-button'
onClick={(e) => this.handleCancelButtonClick(e)}
>
Cancel
</button>
</div>
</div>
)
}
handleEditButtonClick (e) {
let { folder } = this.props
this.setState({
status: 'EDIT',
folder: {
color: folder.color,
name: folder.name
}
}, () => {
this.refs.nameInput.select()
})
}
handleDeleteButtonClick (e) {
this.setState({
status: 'DELETE'
})
}
renderIdle () {
let { folder } = this.props
return (
<div styleName='folderList-item'
onDoubleClick={(e) => this.handleEditButtonClick(e)}
>
<div styleName='folderList-item-left'
style={{borderColor: folder.color}}
>
<span styleName='folderList-item-left-name'>{folder.name}</span>
<span styleName='folderList-item-left-key'>({folder.key})</span>
</div>
<div styleName='folderList-item-right'>
<button styleName='folderList-item-right-button'
onClick={(e) => this.handleEditButtonClick(e)}
>
Edit
</button>
<button styleName='folderList-item-right-button'
onClick={(e) => this.handleDeleteButtonClick(e)}
>
Delete
</button>
</div>
</div>
)
}
render () {
switch (this.state.status) {
case 'DELETE':
return this.renderDelete()
case 'EDIT':
return this.renderEdit()
case 'IDLE':
default:
return this.renderIdle()
}
}
}
const FolderItem = CSSModules(UnstyledFolderItem, styles)
class StorageItem extends React.Component {
handleNewFolderButtonClick (e) {
let { storage } = this.props
let input = {
name: 'Untitled',
color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7)]
}
dataApi.createFolder(storage.key, input)
.then((storage) => {
store.dispatch({
type: 'ADD_FOLDER',
storage: storage
})
})
.catch((err) => {
console.error(err)
})
}
handleExternalButtonClick () {
let { storage } = this.props
shell.showItemInFolder(storage.path)
}
handleUnlinkButtonClick (e) {
let { storage } = this.props
dataApi.removeStorage(storage.key)
.then(() => {
store.dispatch({
type: 'REMOVE_STORAGE',
key: storage.key
})
})
.catch((err) => {
console.error(err)
})
}
render () {
let { storage } = this.props
let folderList = storage.folders.map((folder) => {
return <FolderItem key={folder.key}
folder={folder}
storage={storage}
/>
})
return (
<div styleName='root' key={storage.key}>
<div styleName='header'>
<i className='fa fa-folder-open'/>&nbsp;
{storage.name}&nbsp;
<span styleName='header-path'>({storage.path})</span>
<div styleName='header-control'>
<button styleName='header-control-button'
onClick={(e) => this.handleNewFolderButtonClick(e)}
>
<i className='fa fa-plus'/>
</button>
<button styleName='header-control-button'
onClick={(e) => this.handleExternalButtonClick(e)}
>
<i className='fa fa-external-link'/>
</button>
<button styleName='header-control-button'
onClick={(e) => this.handleUnlinkButtonClick(e)}
>
<i className='fa fa-unlink'/>
</button>
</div>
</div>
<div styleName='folderList'>
{folderList.length > 0
? folderList
: <div styleName='folderList-empty'>No Folders</div>
}
</div>
</div>
)
}
}
StorageItem.propTypes = {
storage: PropTypes.shape({
key: PropTypes.string
}),
folder: PropTypes.shape({
key: PropTypes.string,
color: PropTypes.string,
name: PropTypes.string
})
}
export default CSSModules(StorageItem, styles)

View File

@@ -0,0 +1,97 @@
.root
position relative
margin-bottom 15px
.header
height 35px
line-height 30px
padding 0 10px 5px
box-sizing border-box
border-bottom $default-border
margin-bottom 5px
.header-path
color $ui-inactive-text-color
font-size 10px
margin 0 5px
.header-control
float right
.header-control-button
width 30px
height 25px
colorDefaultButton()
border-radius 2px
border $ui-border
margin-right 5px
&:last-child
margin-right 0
.folderList-item
height 35px
box-sizing border-box
padding 2.5px 15px
&:hover
background-color darken(white, 3%)
.folderList-item-left
height 30px
border-left solid 6px transparent
padding 0 10px
line-height 30px
float left
.folderList-item-left-danger
color $danger-color
font-weight bold
.folderList-item-left-key
color $ui-inactive-text-color
font-size 10px
margin 0 5px
border none
.folderList-item-left-colorButton
colorDefaultButton()
height 25px
width 25px
line-height 23px
padding 0
box-sizing border-box
vertical-align middle
border $ui-border
border-radius 2px
margin-right 5px
margin-left -15px
.folderList-item-left-nameInput
height 25px
box-sizing border-box
vertical-align middle
border $ui-border
border-radius 2px
padding 0 5px
.folderList-item-right
float right
.folderList-item-right-button
vertical-align middle
height 25px
margin-top 2.5px
colorDefaultButton()
border-radius 2px
border $ui-border
margin-right 5px
padding 0 5px
&:last-child
margin-right 0
.folderList-item-right-confirmButton
@extend .folderList-item-right-button
border none
colorPrimaryButton()
.folderList-item-right-dangerButton
@extend .folderList-item-right-button
border none
colorDangerButton()

View File

@@ -0,0 +1,223 @@
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './StoragesTab.styl'
import dataApi from 'browser/main/lib/dataApi'
import StorageItem from './StorageItem'
const electron = require('electron')
const remote = electron.remote
function browseFolder () {
let dialog = remote.dialog
let defaultPath = remote.app.getPath('home')
return new Promise((resolve, reject) => {
dialog.showOpenDialog({
title: 'Select Directory',
defaultPath,
properties: ['openDirectory', 'createDirectory']
}, function (targetPaths) {
if (targetPaths == null) return resolve('')
resolve(targetPaths[0])
})
})
}
class StoragesTab extends React.Component {
constructor (props) {
super(props)
this.state = {
page: 'LIST',
newStorage: {
name: 'Unnamed',
type: 'FILESYSTEM',
path: ''
}
}
}
handleAddStorageButton (e) {
this.setState({
page: 'ADD_STORAGE',
newStorage: {
name: 'Unnamed',
type: 'FILESYSTEM',
path: ''
}
}, () => {
this.refs.addStorageName.select()
})
}
renderList () {
let { storages } = this.props
let storageList = storages.map((storage) => {
return <StorageItem
key={storage.key}
storage={storage}
/>
})
return (
<div styleName='list'>
{storageList.length > 0
? storageList
: <div styleName='list-empty'>No storage found.</div>
}
<div styleName='list-control'>
<button styleName='list-control-addStorageButton'
onClick={(e) => this.handleAddStorageButton(e)}
>
<i className='fa fa-plus'/> Add Storage
</button>
</div>
</div>
)
}
handleAddStorageBrowseButtonClick (e) {
browseFolder()
.then((targetPath) => {
if (targetPath.length > 0) {
let { newStorage } = this.state
newStorage.path = targetPath
this.setState({
newStorage
})
}
})
.catch((err) => {
console.error('BrowseFAILED')
console.error(err)
})
}
handleAddStorageChange (e) {
let { newStorage } = this.state
newStorage.name = this.refs.addStorageName.value
newStorage.path = this.refs.addStoragePath.value
this.setState({
newStorage
})
}
handleAddStorageCreateButton (e) {
dataApi
.addStorage({
name: this.state.newStorage.name,
path: this.state.newStorage.path
})
.then((data) => {
let { dispatch } = this.props
dispatch({
type: 'ADD_STORAGE',
storage: data.storage,
notes: data.notes
})
this.setState({
page: 'LIST'
})
})
}
handleAddStorageCancelButton (e) {
this.setState({
page: 'LIST'
})
}
renderAddStorage () {
return (
<div styleName='addStorage'>
<div styleName='addStorage-header'>Add Storage</div>
<div styleName='addStorage-body'>
<div styleName='addStorage-body-section'>
<div styleName='addStorage-body-section-label'>
Name
</div>
<div styleName='addStorage-body-section-name'>
<input styleName='addStorage-body-section-name-input'
ref='addStorageName'
value={this.state.newStorage.name}
onChange={(e) => this.handleAddStorageChange(e)}
/>
</div>
</div>
<div styleName='addStorage-body-section'>
<div styleName='addStorage-body-section-label'>Type</div>
<div styleName='addStorage-body-section-type'>
<select styleName='addStorage-body-section-type-select'
value={this.state.newStorage.type}
readOnly
>
<option value='FILESYSTEM'>File System</option>
</select>
<div styleName='addStorage-body-section-type-description'>
3rd party cloud integration(such as Google Drive and Dropbox) will be available soon.
</div>
</div>
</div>
<div styleName='addStorage-body-section'>
<div styleName='addStorage-body-section-label'>Location
</div>
<div styleName='addStorage-body-section-path'>
<input styleName='addStorage-body-section-path-input'
ref='addStoragePath'
placeholder='Select Folder'
value={this.state.newStorage.path}
onChange={(e) => this.handleAddStorageChange(e)}
/>
<button styleName='addStorage-body-section-path-button'
onClick={(e) => this.handleAddStorageBrowseButtonClick(e)}
>
...
</button>
</div>
</div>
<div styleName='addStorage-body-control'>
<button styleName='addStorage-body-control-createButton'
onClick={(e) => this.handleAddStorageCreateButton(e)}
>Create</button>
<button styleName='addStorage-body-control-cancelButton'
onClick={(e) => this.handleAddStorageCancelButton(e)}
>Cancel</button>
</div>
</div>
</div>
)
}
renderContent () {
switch (this.state.page) {
case 'ADD_STORAGE':
case 'ADD_FOLDER':
return this.renderAddStorage()
case 'LIST':
default:
return this.renderList()
}
}
render () {
return (
<div styleName='root'>
{this.renderContent()}
</div>
)
}
}
StoragesTab.propTypes = {
dispatch: PropTypes.func
}
export default CSSModules(StoragesTab, styles)

View File

@@ -0,0 +1,115 @@
.root
padding 15px
color $ui-text-color
.list
margin-bottom 15px
font-size 14px
.folderList
padding 0 15px
.folderList-item
height 30px
line-height 30px
border-bottom $ui-border
.folderList-empty
height 30px
line-height 30px
font-size 12px
color $ui-inactive-text-color
.list-empty
height 30px
color $ui-inactive-text-color
.list-control
height 30px
.list-control-addStorageButton
height 30px
padding 0 15px
border $ui-border
colorDefaultButton()
border-radius 2px
.addStorage
margin-bottom 15px
.addStorage-header
font-size 24px
color $ui-text-color
padding 5px
border-bottom $default-border
margin-bottom 15px
.addStorage-body-section
margin-bottom 15px
display flex
line-height 30px
.addStorage-body-section-label
width 150px
text-align right
margin-right 10px
.addStorage-body-section-name
flex 1
.addStorage-body-section-name-input
height 30px
vertical-align middle
width 150px
font-size 12px
border solid 1px $border-color
border-radius 2px
padding 0 5px
.addStorage-body-section-type
flex 1
.addStorage-body-section-type-select
height 30px
.addStorage-body-section-type-description
margin 5px
font-size 12px
color $ui-inactive-text-color
line-height 16px
.addStorage-body-section-path
flex 1
.addStorage-body-section-path-input
height 30px
vertical-align middle
width 150px
font-size 12px
border-style solid
border-width 1px 0 1px 1px
border-color $border-color
border-top-left-radius 2px
border-bottom-left-radius 2px
padding 0 5px
.addStorage-body-section-path-button
height 30px
border none
border-top-right-radius 2px
border-bottom-right-radius 2px
colorPrimaryButton()
vertical-align middle
.addStorage-body-control
border-top $default-border
padding-top 10px
box-sizing border-box
height 40px
text-align right
.addStorage-body-control-createButton
colorPrimaryButton()
border none
border-radius 2px
height 30px
padding 0 15px
margin-right 5px
.addStorage-body-control-cancelButton
colorDefaultButton()
border $default-border
border-radius 2px
height 30px
padding 0 15px

View File

@@ -0,0 +1,98 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import ConfigTab from './ConfigTab'
import InfoTab from './InfoTab'
import StoragesTab from './StoragesTab'
import CSSModules from 'browser/lib/CSSModules'
import styles from './PreferencesModal.styl'
class Preferences extends React.Component {
constructor (props) {
super(props)
this.state = {
currentTab: 'STORAGES'
}
}
switchTeam (teamId) {
this.setState({currentTeamId: teamId})
}
handleNavButtonClick (tab) {
return (e) => {
this.setState({currentTab: tab})
}
}
renderContent () {
let { dispatch, config, storages } = this.props
switch (this.state.currentTab) {
case 'INFO':
return <InfoTab/>
case 'CONFIG':
return (
<ConfigTab
dispatch={dispatch}
config={config}
/>
)
case 'STORAGES':
default:
return (
<StoragesTab
dispatch={dispatch}
storages={storages}
/>
)
}
}
render () {
let content = this.renderContent()
let tabs = [
{target: 'STORAGES', label: 'Storages', icon: 'database'},
{target: 'CONFIG', label: 'Config', icon: 'cogs'},
{target: 'INFO', label: 'Info', icon: 'info-circle'}
]
let navButtons = tabs.map((tab) => {
let isActive = this.state.currentTab === tab.target
return (
<button styleName={isActive
? 'nav-button--active'
: 'nav-button'
}
key={tab.target}
onClick={(e) => this.handleNavButtonClick(tab.target)(e)}
>
<i styleName='nav-button-icon'
className={'fa fa-' + tab.icon}
/>
<span styleName='nav-button-label'>
{tab.label}
</span>
</button>
)
})
return (
<div styleName='root'>
<div styleName='nav'>
{navButtons}
</div>
<div styleName='content'>
{content}
</div>
</div>
)
}
}
Preferences.propTypes = {
dispatch: PropTypes.func
}
export default connect((x) => x)(CSSModules(Preferences, styles))