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

refactor file structure

This commit is contained in:
Rokt33r
2016-05-12 20:45:21 +09:00
parent c851f8f006
commit 18b6d8289f
22 changed files with 43 additions and 470 deletions

View File

@@ -0,0 +1,53 @@
import React, { PropTypes } from 'react'
import ReactDOM from 'react-dom'
import store from '../store'
const electron = require('electron')
const ipc = electron.ipcRenderer
export default class DeleteArticleModal extends React.Component {
constructor (props) {
super(props)
this.confirmHandler = (e) => this.handleYesButtonClick()
}
componentDidMount () {
ReactDOM.findDOMNode(this.refs.no).focus()
ipc.on('modal-confirm', this.confirmHandler)
}
componentWillUnmount () {
ipc.removeListener('modal-confirm', this.confirmHandler)
}
handleNoButtonClick (e) {
this.props.close()
}
handleYesButtonClick (e) {
// store.dispatch(destroyArticle(this.props.articleKey))
this.props.close()
}
render () {
return (
<div className='DeleteArticleModal modal'>
<div className='title'><i className='fa fa-fw fa-trash'/> Delete an article.</div>
<div className='message'>Do you really want to delete?</div>
<div className='control'>
<button ref='no' onClick={(e) => this.handleNoButtonClick(e)}><i className='fa fa-fw fa-close'/> No</button>
<button ref='yes' onClick={(e) => this.handleYesButtonClick(e)} className='danger'><i className='fa fa-fw fa-check'/> Yes</button>
</div>
</div>
)
}
}
DeleteArticleModal.propTypes = {
action: PropTypes.object,
articleKey: PropTypes.string,
close: PropTypes.func
}

View File

@@ -0,0 +1,193 @@
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './NewRepositoryModal.styl'
import Repository from 'browser/lib/Repository'
import store from 'browser/main/store'
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 NewRepositoryModal extends React.Component {
constructor (props) {
super(props)
this.state = {
name: '',
path: '',
isPathSectionFocused: false,
error: null,
isBrowsingPath: false
}
}
componentDidMount () {
this.refs.nameInput.focus()
}
handleCloseButtonClick (e) {
this.props.close()
}
handlePathFocus (e) {
this.setState({
isPathSectionFocused: true
})
}
handlePathBlur (e) {
if (e.relatedTarget !== this.refs.pathInput && e.relatedTarget !== this.refs.browseButton) {
this.setState({
isPathSectionFocused: false
})
}
}
handleBrowseButtonClick (e) {
this.setState({
isBrowsingPath: true
}, () => {
browseFolder()
.then((targetPath) => {
this.setState({
path: targetPath,
isBrowsingPath: false
})
})
.catch((err) => {
console.error('BrowseFAILED')
console.error(err)
this.setState({
isBrowsingPath: false
})
})
})
}
handleConfirmButtonClick (e) {
let targetPath = this.state.path
let name = this.state.name
let repository = new Repository({
name: name,
path: targetPath
})
repository
.mount()
.then(() => repository.load())
.then((data) => {
store.dispatch({
type: 'ADD_REPOSITORY',
repository: data
})
this.props.close()
})
.catch((err) => {
console.error(err)
this.setState({
error: err.message
})
})
}
handleChange (e) {
let name = this.refs.nameInput.value
let path = this.refs.pathInput.value
this.setState({
name,
path
})
}
render () {
return (
<div className='NewRepositoryModal'
styleName='root'
>
<div styleName='header'>
<div styleName='header-title'>New Repository</div>
<button styleName='header-closeButton'
onClick={(e) => this.handleCloseButtonClick(e)}
>
<i className='fa fa-times'/>
</button>
</div>
<div styleName='body'>
<div styleName='body-section'>
<div styleName='body-section-label'>Repository Name</div>
<input styleName='body-section-input'
ref='nameInput'
value={this.state.name}
onChange={(e) => this.handleChange(e)}
/>
</div>
<div styleName='body-section'>
<div styleName='body-section-label'>Repository Path</div>
<div styleName={!this.state.isPathSectionFocused ? 'body-section-path' : 'body-section-path--focus'}>
<input styleName='body-section-path-input'
ref='pathInput'
value={this.state.path}
style={styles.body_section_path_input}
onFocus={(e) => this.handlePathFocus(e)}
onBlur={(e) => this.handlePathBlur(e)}
disabled={this.state.isBrowsingPath}
onChange={(e) => this.handleChange(e)}
/>
<button styleName='body-section-path-button'
onClick={(e) => this.handleBrowseButtonClick(e)}
onFocus={(e) => this.handlePathFocus(e)}
onBlur={(e) => this.handlePathBlur(e)}
disabled={this.state.isBrowsingPath}
>
...
</button>
</div>
</div>
{
this.state.error != null && (
<div styleName='body-error'>
{this.state.error}
</div>
)
}
</div>
<div styleName='footer'>
<button styleName='footer-cancelButton'
onClick={(e) => this.handleCloseButtonClick(e)}
>
<i className='fa fa-times'/> Cancel
</button>
<button styleName='footer-confirmButton'
onClick={(e) => this.handleConfirmButtonClick(e)}
>
<i className='fa fa-check'/> Confirm
</button>
</div>
</div>
)
}
}
NewRepositoryModal.propTypes = {
close: PropTypes.func
}
export default CSSModules(NewRepositoryModal, styles)

View File

@@ -0,0 +1,133 @@
$modal-width = 550px
$modal-header-color = #F2F2F2
$body-button-background-color = #2BAC8F
.root
modal()
width $modal-width
height 310px
.header
height 50px
background-color $modal-header-color
.header-title
font-size 24px
line-height 50px
padding-left 15px
.header-closeButton
position absolute
top 8.5px
right 8.5px
width 33px
height 33px
font-size 20px
background-color transparent
border none
color #AAA
&:hover
color #4D4D4D
.body
absolute left right
top 50px
bottom 50px
padding 35px 0
.body-section
height 33px
margin-bottom 15px
position relative
.body-section-label
absolute top bottom left
width 175px
text-align right
line-height 33px
padding-right 15px
.body-section-input
absolute top bottom
left 175px
width 315px
padding 0 10px
border $default-border
border-radius 5px
outline none
&:focus
border $active-border
.body-section-path
absolute top bottom
left 175px
width 315px
padding 0 10px
border $default-border
border-radius 5px
outline none
overflow hidden
.body-section-path--focus
@extend .body-section-path
border $active-border
.body-section-path-input
absolute top left bottom
width 265px
border none
outline none
padding 0 10px
.body-section-path-button
absolute top right bottom
width 50px
border none
border-left $default-border
outline none
color white
background-color $body-button-background-color
transition 0.15s
&:hover
background-color lighten($body-button-background-color, 7%)
&:disabled
background-color lighten(gray, 15%)
.body-error
height 33px
margin 35px auto 0
width 320px
border-radius 5px
text-align center
line-height 33px
color $danger-color
background-color $danger-lighten-color
.footer
absolute left right bottom
height 50px
.footer-cancelButton
position absolute
height 33px
right 85.5px
width 72px
top 8.5px
border-radius 5px
border $default-border
background-color darken(white, 0.03)
&:hover
background-color white
.footer-confirmButton
position absolute
height 33px
right 8.5px
width 72px
top 8.5px
color white
border-radius 5px
border none
background-color #2BAC8F
&:hover
background-color lighten($body-button-background-color, 7%)

View File

@@ -0,0 +1,244 @@
import React, { PropTypes } from 'react'
import linkState from 'browser/lib/linkState'
import fetchConfig from 'browser/lib/fetchConfig'
import hljsTheme from 'browser/lib/hljsThemes'
const electron = require('electron')
const ipc = electron.ipcRenderer
const remote = electron.remote
const ace = window.ace
const OSX = global.process.platform === 'darwin'
export default class AppSettingTab extends React.Component {
constructor (props) {
super(props)
let keymap = Object.assign({}, remote.getGlobal('keymap'))
let config = Object.assign({}, fetchConfig())
let userName = props.user != null ? props.user.name : null
this.state = {
user: {
name: userName,
alert: null
},
userAlert: null,
keymap: keymap,
keymapAlert: null,
config: config,
configAlert: null
}
}
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
})
}
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()
return (
<div className='AppSettingTab content'>
<div className='section'>
<div className='sectionTitle'>Editor</div>
<div className='sectionInput'>
<label>Editor Font Size</label>
<input valueLink={this.linkState('config.editor-font-size')} onKeyDown={(e) => this.handleConfigKeyDown(e)} type='text'/>
</div>
<div className='sectionInput'>
<label>Editor Font Family</label>
<input valueLink={this.linkState('config.editor-font-family')} onKeyDown={(e) => this.handleConfigKeyDown(e)} type='text'/>
</div>
<div className='sectionMultiSelect'>
<label>Editor Indent Style</label>
<div className='sectionMultiSelect-input'>
type
<select valueLink={this.linkState('config.editor-indent-type')}>
<option value='space'>Space</option>
<option value='tab'>Tab</option>
</select>
size
<select valueLink={this.linkState('config.editor-indent-size')}>
<option value='2'>2</option>
<option value='4'>4</option>
<option value='8'>8</option>
</select>
</div>
</div>
<div className='sectionTitle'>Preview</div>
<div className='sectionInput'>
<label>Preview Font Size</label>
<input valueLink={this.linkState('config.preview-font-size')} onKeyDown={(e) => this.handleConfigKeyDown(e)} type='text'/>
</div>
<div className='sectionInput'>
<label>Preview Font Family</label>
<input valueLink={this.linkState('config.preview-font-family')} onKeyDown={(e) => this.handleConfigKeyDown(e)} type='text'/>
</div>
<div className='sectionSelect'>
<label>Switching Preview</label>
<select valueLink={this.linkState('config.switch-preview')}>
<option value='blur'>When Editor Blurred</option>
<option value='rightclick'>When Right Clicking</option>
</select>
</div>
<div className='sectionCheck'>
<label><input onChange={e => this.handleLineNumberingClick(e)} checked={this.state.config['preview-line-number']} type='checkbox'/>Code block line numbering</label>
</div>
{
global.process.platform === 'win32'
? (
<div className='sectionCheck'>
<label><input onChange={e => this.handleDisableDirectWriteClick(e)} checked={this.state.config['disable-direct-write']} disabled={OSX} type='checkbox'/>Disable Direct Write<span className='sectionCheck-warn'>It will be applied after restarting</span></label>
</div>
)
: null
}
<div className='sectionTitle'>Theme</div>
<div className='sectionSelect'>
<label>UI Theme</label>
<select valueLink={this.linkState('config.theme-ui')}>
<option value='light'>Light</option>
<option value='dark'>Dark</option>
</select>
</div>
<div className='sectionSelect'>
<label>Code block Theme</label>
<select valueLink={this.linkState('config.theme-code')}>
{
hljsThemeList.map((theme) => {
return (<option value={theme.name} key={theme.name}>{theme.caption}</option>)
})
}
</select>
</div>
<div className='sectionSelect'>
<label>Editor Theme</label>
<select valueLink={this.linkState('config.theme-syntax')}>
{
aceThemeList.themes.map((theme) => {
return (<option value={theme.name} key={theme.name}>{theme.caption}</option>)
})
}
</select>
</div>
<div className='sectionConfirm'>
<button onClick={(e) => this.handleConfigSaveButtonClick(e)}>Save</button>
</div>
</div>
<div className='section'>
<div className='sectionTitle'>Hotkey</div>
<div className='sectionInput'>
<label>Toggle Main</label>
<input onKeyDown={(e) => this.handleKeyDown(e)} valueLink={this.linkState('keymap.toggleMain')} type='text'/>
</div>
<div className='sectionInput'>
<label>Toggle Finder(popup)</label>
<input onKeyDown={(e) => this.handleKeyDown(e)} valueLink={this.linkState('keymap.toggleFinder')} type='text'/>
</div>
<div className='sectionConfirm'>
<button onClick={(e) => this.handleSaveButtonClick(e)}>Save</button>
{keymapAlertElement}
</div>
<div className='description'>
<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>
)
}
}
AppSettingTab.prototype.linkState = linkState
AppSettingTab.propTypes = {
user: PropTypes.shape({
name: PropTypes.string
}),
dispatch: PropTypes.func
}

View File

@@ -0,0 +1,24 @@
import React from 'react'
import ReactDOM from 'react-dom'
import linkState from 'browser/lib/linkState'
import ExternalLink from 'browser/components/ExternalLink'
export default class ContactTab extends React.Component {
componentDidMount () {
let titleInput = ReactDOM.findDOMNode(this.refs.title)
if (titleInput != null) titleInput.focus()
}
render () {
return (
<div className='ContactTab content'>
<div className='title'>Contact</div>
<p>
- Issues: <ExternalLink href='https://github.com/BoostIO/Boostnote/issues'>https://github.com/BoostIO/Boostnote/issues</ExternalLink>
</p>
</div>
)
}
}
ContactTab.prototype.linkState = linkState

View File

@@ -0,0 +1,97 @@
import React, { PropTypes } from 'react'
import { connect, Provider } from 'react-redux'
import linkState from 'browser/lib/linkState'
import store from 'browser/main/store'
import AppSettingTab from './AppSettingTab'
import ContactTab from './ContactTab'
import { closeModal } from 'browser/lib/modal'
const APP = 'APP'
const CONTACT = 'CONTACT'
class Preferences extends React.Component {
constructor (props) {
super(props)
this.state = {
currentTab: APP
}
}
switchTeam (teamId) {
this.setState({currentTeamId: teamId})
}
handleNavButtonClick (tab) {
return (e) => {
this.setState({currentTab: tab})
}
}
render () {
let content = this.renderContent()
let tabs = [
{target: APP, label: 'Preferences'},
{target: CONTACT, label: 'Contact'}
]
let navButtons = tabs.map((tab) => (
<button key={tab.target} onClick={(e) => this.handleNavButtonClick(tab.target)(e)} className={this.state.currentTab === tab.target ? 'active' : ''}>{tab.label}</button>
))
return (
<div className='Preferences modal'>
<div className='header'>
<div className='title'>Setting</div>
<button onClick={(e) => closeModal()} className='closeBtn'>Done</button>
</div>
<div className='nav'>
{navButtons}
</div>
{content}
</div>
)
}
renderContent () {
let { user, dispatch } = this.props
switch (this.state.currentTab) {
case CONTACT:
return (
<ContactTab/>
)
case APP:
default:
return (
<AppSettingTab
user={user}
dispatch={dispatch}
/>
)
}
}
}
Preferences.propTypes = {
user: PropTypes.shape({
name: PropTypes.string
}),
dispatch: PropTypes.func
}
Preferences.prototype.linkState = linkState
let RootComponent = connect((x) => x)(Preferences)
export default class PreferencesModal extends React.Component {
render () {
return (
<Provider store={store}>
<RootComponent/>
</Provider>
)
}
}