1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 01:36:22 +00:00

resizible list

This commit is contained in:
Rokt33r
2016-05-19 13:58:39 +09:00
parent dc6bd1aae8
commit 89a76d9ead
24 changed files with 370 additions and 1112 deletions

View File

@@ -1,339 +0,0 @@
import React, { PropTypes } from 'react'
import ReactDOM from 'react-dom'
import moment from 'moment'
import _ from 'lodash'
import TagSelect from 'browser/components/TagSelect'
import ModeSelect from 'browser/components/ModeSelect'
import ShareButton from './ShareButton'
import { openModal, isModalOpen } from 'browser/main/lib/modal'
import DeleteArticleModal from '../modals/DeleteArticleModal'
import ArticleEditor from './ArticleEditor'
const electron = require('electron')
const ipc = electron.ipcRenderer
import fetchConfig from 'browser/lib/fetchConfig'
let config = fetchConfig()
ipc.on('config-apply', function (e, newConfig) {
config = newConfig
})
const BRAND_COLOR = '#18AF90'
const OSX = global.process.platform === 'darwin'
const tagSelectTutorialElement = (
<svg width='500' height='500' className='tutorial'>
<text x='155' y='50' fill={BRAND_COLOR} fontSize='24'>Attach some tags here!</text>
<svg x='0' y='-15'>
<path fill='white' d='M15.5,22.2c77.8-0.7,155.6-1.3,233.5-2c22.2-0.2,44.4-0.4,66.6-0.6c1.9,0,1.9-3,0-3
c-77.8,0.7-155.6,1.3-233.5,2c-22.2,0.2-44.4,0.4-66.6,0.6C13.6,19.2,13.6,22.2,15.5,22.2L15.5,22.2z'/>
<path fill='white' d='M130.8,25c-5.4,6.8-10.3,14-14.6,21.5c-0.8,1.4,1.2,3.2,2.4,1.8c1-1.2,2-2.4,3.1-3.7c1.2-1.5-0.9-3.6-2.1-2.1
c-1,1.2-2,2.4-3.1,3.7c0.8,0.6,1.6,1.2,2.4,1.8c4.2-7.3,8.9-14.3,14.2-20.9C134.1,25.6,132,23.4,130.8,25L130.8,25z'/>
<path fill='white' d='M132.6,22.1c8.4,5.9,16.8,11.9,25.2,17.8c1.6,1.1,3.1-1.5,1.5-2.6c-8.4-5.9-16.8-11.9-25.2-17.8
C132.5,18.4,131,21,132.6,22.1L132.6,22.1z'/>
<path fill='white' d='M132.9,18.6c0.4,6.7-0.7,13.3-3.5,19.3c-1.5,3.1-3.9,6.4-3.1,10c0.7,3.1,3.4,4.4,6.2,5.5
c5.1,2.1,10.5,3.1,16.1,3.2c1.9,0,1.9-3,0-3c-4.7-0.1-9.2-0.8-13.6-2.4c-3-1.1-6.2-1.9-5.4-6.6c0.4-2,2-4.1,2.8-5.9
c2.9-6.3,4-13.1,3.6-20.1C135.8,16.7,132.8,16.7,132.9,18.6L132.9,18.6z'/>
</svg>
</svg>
)
const modeSelectTutorialElement = (
<svg width='500' height='500' className='tutorial'>
<text x='195' y='130' fill={BRAND_COLOR} fontSize='24'>Select code syntax!!</text>
<svg x='300' y='0'>
<path fill='white' d='M99.9,58.8c-14.5-0.5-29-2.2-43.1-5.6c-12.3-2.9-27.9-6.4-37.1-15.5C7.9,26,28.2,18.9,37,16.7
c13.8-3.5,28.3-4.7,42.4-5.8c29.6-2.2,59.3-1.7,89-1c3,0.1,7.5-0.6,10.2,0.6c3.1,1.4,3.1,5.3,3.3,8.1c0.3,5.2-0.2,10.7-2.4,15.4
c-4.4,9.6-18.4,14.7-27.5,18.1c-27.1,10.1-56.7,12.8-85.3,15.6c-1.9,0.2-1.9,3.2,0,3c29.3-2.9,59.8-5.6,87.5-16.2
c9.6-3.7,22.8-8.7,27.7-18.4c2.3-4.6,3.2-9.9,3.2-15c0-3.6,0-9.4-2.9-12c-1.9-1.7-4.7-1.8-7.1-2c-4.8-0.2-9.6-0.2-14.4-0.3
c-8.7-0.2-17.5-0.3-26.2-0.4C116.7,6.3,99,6.5,81.3,7.8c-15.8,1.1-32.1,2.3-47.4,6.6c-7.7,2.2-22.1,6.9-20.9,17.4
c0.6,5.4,5.6,9.4,9.9,12.1c6.7,4.3,14.4,6.9,22,9.2c17.8,5.4,36.4,8,54.9,8.6C101.8,61.8,101.8,58.8,99.9,58.8L99.9,58.8z'/>
<path fill='white' d='M11.1,67.8c9.2-6.1,18.6-11.9,28.2-17.2c-0.7-0.3-1.5-0.6-2.2-0.9c0.9,5.3,0.7,10.3-0.5,15.5
c-0.4,1.9,2.4,2.7,2.9,0.8c1.4-5.7,1.5-11.3,0.5-17.1c-0.2-1-1.4-1.3-2.2-0.9c-9.7,5.3-19.1,11.1-28.2,17.2
C8,66.3,9.5,68.9,11.1,67.8L11.1,67.8z'/>
<path fill='white' d='M31.5,52.8C23.4,68.9,0.2,83.2,7.9,104c0.7,1.8,3.6,1,2.9-0.8C3.6,83.7,26.4,69.7,34.1,54.3
C35,52.6,32.4,51.1,31.5,52.8L31.5,52.8z'/>
</svg>
</svg>
)
export default class ArticleDetail extends React.Component {
constructor (props) {
super(props)
this.deleteHandler = (e) => {
if (isModalOpen()) return true
this.handleDeleteButtonClick()
}
this.uncacheHandler = (e) => {
if (isModalOpen()) return true
this.handleUncache()
}
this.titleHandler = (e) => {
if (isModalOpen()) return true
if (this.refs.title) {
this.focusTitle()
}
}
this.editHandler = (e) => {
if (isModalOpen()) return true
if (this.refs.editor) this.refs.editor.switchEditMode()
}
this.previewHandler = (e) => {
if (isModalOpen()) return true
if (this.refs.editor) this.refs.editor.switchPreviewMode()
}
this.configApplyHandler = (e, config) => this.handleConfigApply(e, config)
this.state = {
article: Object.assign({content: ''}, props.activeArticle),
openShareDropdown: false,
fontFamily: config['editor-font-family']
}
}
componentDidMount () {
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
this.shareDropdownInterceptor = (e) => {
e.stopPropagation()
}
ipc.on('detail-delete', this.deleteHandler)
ipc.on('detail-uncache', this.uncacheHandler)
ipc.on('detail-title', this.titleHandler)
ipc.on('detail-edit', this.editHandler)
ipc.on('detail-preview', this.previewHandler)
ipc.on('config-apply', this.configApplyHandler)
}
componentWillUnmount () {
clearInterval(this.refreshTimer)
ipc.removeListener('detail-delete', this.deleteHandler)
ipc.removeListener('detail-uncache', this.uncacheHandler)
ipc.removeListener('detail-title', this.titleHandler)
ipc.removeListener('detail-edit', this.editHandler)
ipc.removeListener('detail-preview', this.previewHandler)
}
componentDidUpdate (prevProps, prevState) {
if (this.props.activeArticle == null || prevProps.activeArticle == null || this.props.activeArticle.key !== prevProps.activeArticle.key) {
if (this.refs.editor) this.refs.editor.resetCursorPosition()
}
if (prevProps.activeArticle == null && this.props.activeArticle) {
}
}
handleConfigApply (e, config) {
this.setState({
fontFamily: config['editor-font-family']
})
}
renderEmpty () {
return (
<div className='ArticleDetail empty'>
<div className='ArticleDetail-empty-box'>
<div className='ArticleDetail-empty-box-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br/>to create a new post</div>
</div>
</div>
)
}
handleOthersButtonClick (e) {
this.deleteHandler()
}
handleFolderKeyChange (e) {
let { dispatch, activeArticle, status, folders } = this.props
let article = Object.assign({}, activeArticle, {
FolderKey: e.target.value,
updatedAt: new Date()
})
// dispatch(updateArticle(article))
let targetFolderKey = e.target.value
if (status.targetFolders.length > 0) {
let targetFolder = _.findWhere(folders, {key: targetFolderKey})
// dispatch(switchFolder(targetFolder.name))
}
}
handleTitleChange (e) {
let { dispatch, activeArticle } = this.props
let article = Object.assign({}, activeArticle, {
title: e.target.value,
updatedAt: new Date()
})
// dispatch(updateArticle(article))
}
handleTagsChange (newTag, tags) {
let { dispatch, activeArticle } = this.props
let article = Object.assign({}, activeArticle, {
tags: tags,
updatedAt: new Date()
})
// dispatch(updateArticle(article))
}
handleModeChange (value) {
let { dispatch, activeArticle } = this.props
let article = Object.assign({}, activeArticle, {
mode: value,
updatedAt: new Date()
})
// dispatch(updateArticle(article))
this.switchEditMode()
}
handleContentChange (value) {
let { dispatch, activeArticle } = this.props
if (activeArticle.content !== value) {
let article = Object.assign({}, activeArticle, {
content: value,
updatedAt: new Date()
})
// dispatch(updateArticle(article))
}
}
handleDeleteButtonClick (e) {
if (this.props.activeArticle) {
openModal(DeleteArticleModal, {articleKey: this.props.activeArticle.key})
}
}
handleTitleKeyDown (e) {
if (e.keyCode === 9 && !e.shiftKey) {
e.preventDefault()
this.refs.mode.handleIdleSelectClick()
}
}
handleModeSelectKeyDown (e) {
if (e.keyCode === 9 && !e.shiftKey) {
e.preventDefault()
this.switchEditMode()
}
if (e.keyCode === 9 && e.shiftKey) {
e.preventDefault()
this.focusTitle()
}
if (e.keyCode === 27) {
this.focusTitle()
}
}
switchEditMode () {
this.refs.editor.switchEditMode()
}
focusTitle () {
if (this.refs.title) {
let titleEl = ReactDOM.findDOMNode(this.refs.title)
titleEl.focus()
titleEl.select()
}
}
render () {
let { folders, status, tags, activeArticle, modified, user } = this.props
if (activeArticle == null) return this.renderEmpty()
let folderOptions = folders.map((folder) => {
return (
<option key={folder.key} value={folder.key}>{folder.name}</option>
)
})
let isUnsaved = !!_.findWhere(modified, {key: activeArticle.key})
return (
<div tabIndex='4' className='ArticleDetail'>
<div className='ArticleDetail-info'>
<div className='ArticleDetail-info-row'>
<select
className='ArticleDetail-info-folder'
value={activeArticle.FolderKey}
onChange={(e) => this.handleFolderKeyChange(e)}
>
{folderOptions}
</select>
<span className='ArticleDetail-info-status'
children={
isUnsaved
? <span> <span className='unsaved-mark'></span> Unsaved</span>
: `Created : ${moment(activeArticle.createdAt).format('YYYY/MM/DD')} Updated : ${moment(activeArticle.updatedAt).format('YYYY/MM/DD')}`
}
/>
<div className='ArticleDetail-info-control'>
<ShareButton
article={activeArticle}
user={user}
/>
<button className='ArticleDetail-info-control-delete-button' onClick={(e) => this.handleOthersButtonClick(e)}>
<i className='fa fa-fw fa-trash'/>
<span className='tooltip'>Delete Post (^ + Del)</span>
</button>
</div>
</div>
<div className='ArticleDetail-info-row2'>
<TagSelect
tags={activeArticle.tags}
onChange={(tags, tag) => this.handleTagsChange(tags, tag)}
suggestTags={tags}
/>
{status.isTutorialOpen ? tagSelectTutorialElement : null}
</div>
</div>
<div className='ArticleDetail-panel'>
<div className='ArticleDetail-panel-header'>
<div className='ArticleDetail-panel-header-title'>
<input
onKeyDown={(e) => this.handleTitleKeyDown(e)}
placeholder='(Untitled)'
ref='title'
value={activeArticle.title}
onChange={(e) => this.handleTitleChange(e)}
style={{
fontFamily: this.state.fontFamily
}}
/>
</div>
<ModeSelect
ref='mode'
onChange={(e) => this.handleModeChange(e)}
onKeyDown={(e) => this.handleModeSelectKeyDown(e)}
value={activeArticle.mode}
className='ArticleDetail-panel-header-mode'
/>
{status.isTutorialOpen ? modeSelectTutorialElement : null}
</div>
<ArticleEditor
ref='editor'
article={activeArticle}
onChange={(content) => this.handleContentChange(content)}
/>
</div>
</div>
)
}
}
ArticleDetail.propTypes = {
dispatch: PropTypes.func,
repositories: PropTypes.array
}

View File

@@ -1,13 +1,27 @@
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './Main.styl'
import { connect } from 'react-redux'
import SideNav from './SideNav'
import TopBar from './TopBar'
import ArticleList from './ArticleList'
import ArticleDetail from './ArticleDetail'
import NoteList from './NoteList'
import NoteDetail from './NoteDetail'
import Repository from 'browser/lib/Repository'
import StatusBar from './StatusBar'
import _ from 'lodash'
import ConfigManager from 'browser/main/lib/ConfigManager'
class Main extends React.Component {
constructor (props) {
super(props)
let { config } = props
this.state = {
isSliderFocused: false,
listWidth: config.listWidth
}
}
componentDidMount () {
let { dispatch } = this.props
@@ -18,25 +32,74 @@ class Main extends React.Component {
})
}
handleSlideMouseDown (e) {
e.preventDefault()
this.setState({
isSliderFocused: true
})
}
handleMouseUp (e) {
if (this.state.isSliderFocused) {
this.setState({
isSliderFocused: false
}, () => {
let { dispatch } = this.props
let newListWidth = this.state.listWidth
// TODO: ConfigManager should dispatch itself.
ConfigManager.set({listWidth: newListWidth})
dispatch({
type: 'SET_LIST_WIDTH',
listWidth: newListWidth
})
})
}
}
handleMouseMove (e) {
if (this.state.isSliderFocused) {
let offset = this.refs.body.getBoundingClientRect().left
let newListWidth = e.pageX - offset
if (newListWidth < 10) {
newListWidth = 10
} else if (newListWidth > 600) {
newListWidth = 600
}
this.setState({
listWidth: newListWidth
})
}
}
render () {
let { config } = this.props
return (
<div
className='Main'
styleName='root'
onMouseMove={(e) => this.handleMouseMove(e)}
onMouseUp={(e) => this.handleMouseUp(e)}
>
<SideNav
{...this.props}
/>
<TopBar
{...this.props}
/>
<ArticleList
{...this.props}
/>
<ArticleDetail
{...this.props}
{..._.pick(this.props, ['dispatch', 'repositories', 'config', 'location'])}
/>
<TopBar/>
<div styleName={config.isSideNavFolded ? 'body--expanded' : 'body'}
ref='body'
>
<NoteList style={{width: this.state.listWidth}}/>
<div styleName={this.state.isSliderFocused ? 'slider--active' : 'slider'}
style={{left: this.state.listWidth}}
onMouseDown={(e) => this.handleSlideMouseDown(e)}
draggable='false'
/>
<NoteDetail
style={{left: this.state.listWidth + 5}}
/>
</div>
<StatusBar
{...this.props}
{..._.pick(this.props, ['config', 'location', 'dispatch'])}
/>
</div>
)
@@ -48,4 +111,4 @@ Main.propTypes = {
repositories: PropTypes.array
}
export default connect((x) => x)(Main)
export default connect((x) => x)(CSSModules(Main, styles))

25
browser/main/Main.styl Normal file
View File

@@ -0,0 +1,25 @@
.root
absolute top left bottom right
.body
absolute right
bottom $statusBar-height - 1
top $topBar-height - 1
left $sideNav-width
.body--expanded
@extend .body
left $sideNav--folded-width
.slider
absolute top bottom
width 5px
background-color $ui-backgroundColor
border-width 0 1px 0
border-style solid
border-color $ui-borderColor
cursor ew-resize
.slider--active
@extend .slider
background-color $ui-button--active-backgroundColor

View File

@@ -0,0 +1,17 @@
.root
absolute top bottom right
border-width 1px 0
border-style solid
border-color $ui-borderColor
.empty
height 320px
display flex
align-items center
.empty-message
width 100%
font-size 42px
line-height 72px
text-align center
color $ui-inactive-text-color

View File

@@ -0,0 +1,45 @@
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './NoteDetail.styl'
const electron = require('electron')
const OSX = global.process.platform === 'darwin'
class NoteDetail extends React.Component {
componentDidUpdate (prevProps, prevState) {
}
renderEmpty () {
return (
<div styleName='empty'>
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br/>to create a new post</div>
</div>
)
}
render () {
let isEmpty = true
let view = isEmpty
? this.renderEmpty()
: null
return (
<div className='NoteDetail'
style={this.props.style}
styleName='root'
tabIndex='0'
>
{view}
</div>
)
}
}
NoteDetail.propTypes = {
dispatch: PropTypes.func,
repositories: PropTypes.array,
style: PropTypes.shape({
left: PropTypes.number
})
}
export default CSSModules(NoteDetail, styles)

View File

@@ -0,0 +1,4 @@
.root
absolute top left bottom
border-top $ui-border
border-bottom $ui-border

View File

@@ -0,0 +1,150 @@
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './NoteList.styl'
import ReactDOM from 'react-dom'
import ModeIcon from 'browser/components/ModeIcon'
import moment from 'moment'
import _ from 'lodash'
const electron = require('electron')
const remote = electron.remote
const ipc = electron.ipcRenderer
class NoteList extends React.Component {
constructor (props) {
super(props)
// this.focusHandler = (e) => this.focus()
}
componentDidMount () {
// this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
// ipc.on('list-focus', this.focusHandler)
// this.focus()
}
componentWillUnmount () {
// clearInterval(this.refreshTimer)
// ipc.removeListener('list-focus', this.focusHandler)
}
componentDidUpdate () {
// return false
// var index = articles.indexOf(null)
// var el = ReactDOM.findDOMNode(this)
// var li = el.querySelectorAll('.NoteList>div')[index]
// if (li == null) {
// return
// }
// var overflowBelow = el.clientHeight + el.scrollTop < li.offsetTop + li.clientHeight
// if (overflowBelow) {
// el.scrollTop = li.offsetTop + li.clientHeight - el.clientHeight
// }
// var overflowAbove = el.scrollTop > li.offsetTop
// if (overflowAbove) {
// el.scrollTop = li.offsetTop
// }
}
focus () {
// ReactDOM.findDOMNode(this).focus()
}
// 移動ができなかったらfalseを返す:
selectPriorArticle () {
let { articles, activeArticle, dispatch } = this.props
let targetIndex = articles.indexOf(activeArticle) - 1
let targetArticle = articles[targetIndex]
return false
}
selectNextArticle () {
let { articles, activeArticle, dispatch } = this.props
let targetIndex = articles.indexOf(activeArticle) + 1
let targetArticle = articles[targetIndex]
if (targetArticle != null) {
dispatch(switchArticle(targetArticle.key))
return true
}
return false
}
handleArticleClick (article) {
let { dispatch } = this.props
return function (e) {
dispatch(switchArticle(article.key))
}
}
handleNoteListKeyDown (e) {
if (e.metaKey || e.ctrlKey) return true
if (e.keyCode === 65 && !e.shiftKey) {
e.preventDefault()
remote.getCurrentWebContents().send('top-new-post')
}
if (e.keyCode === 65 && e.shiftKey) {
e.preventDefault()
remote.getCurrentWebContents().send('nav-new-folder')
}
if (e.keyCode === 68) {
e.preventDefault()
remote.getCurrentWebContents().send('detail-delete')
}
if (e.keyCode === 84) {
e.preventDefault()
remote.getCurrentWebContents().send('detail-title')
}
if (e.keyCode === 69) {
e.preventDefault()
remote.getCurrentWebContents().send('detail-edit')
}
if (e.keyCode === 83) {
e.preventDefault()
remote.getCurrentWebContents().send('detail-save')
}
if (e.keyCode === 38) {
e.preventDefault()
this.selectPriorArticle()
}
if (e.keyCode === 40) {
e.preventDefault()
this.selectNextArticle()
}
}
render () {
let articleElements = []
return (
<div className='NoteList'
styleName='root'
tabIndex='0'
onKeyDown={(e) => this.handleNoteListKeyDown(e)}
style={this.props.style}
>
{articleElements}
</div>
)
}
}
NoteList.propTypes = {
dispatch: PropTypes.func,
repositories: PropTypes.array,
style: PropTypes.shape({
width: PropTypes.number
})
}
export default CSSModules(NoteList, styles)

View File

@@ -86,6 +86,7 @@
margin-left 0
overflow ellipsis
background-color $ui-tooltip-backgroundColor
z-index 10
color white
line-height 34px
border-top-right-radius 5px

View File

@@ -158,7 +158,7 @@ class RepositorySection extends React.Component {
onContextMenu={(e) => this.handleContextButtonClick(e)}
>
<div styleName='header-name'>
<i styleName='header-name-icon' className='fa fa-archive fa-fw'/>
<i className='fa fa-archive fa-fw'/>
<span styleName='header-name-label'>{repository.name}</span>
</div>

View File

@@ -10,10 +10,12 @@
text-align left
font-size 14px
color $ui-inactive-text-color
&:hover
background-color $ui-button--hover-backgroundColor
&:hover .header-control-button
opacity 1
&:active
background-color $ui-button--active-backgroundColor !important
background-color $ui-button--active-backgroundColor
color $ui-button--active-color
.header-control-button, .header-control-button--show
color white
@@ -22,6 +24,8 @@
@extend .header
background-color $ui-button--active-backgroundColor
color $ui-button--active-color
&:hover
background-color $ui-button--active-backgroundColor
.header-control-button,
.header-control-button--show
color white
@@ -124,6 +128,7 @@
margin-left 0
overflow ellipsis
background-color $ui-tooltip-backgroundColor
z-index 10
color white
line-height 34px
border-top-right-radius 5px
@@ -136,6 +141,7 @@
height 33px
top inherit
bottom inherit
z-index 11
left 43px
box-sizing border-box
overflow hidden
@@ -164,6 +170,7 @@
margin-left 0
overflow ellipsis
background-color $ui-tooltip-backgroundColor
z-index 10
color white
line-height 34px
border-top-right-radius 5px

View File

@@ -1,8 +1,9 @@
.root
absolute top left
bottom $statusBar-height
bottom $statusBar-height - 1
width $sideNav-width
border-right $ui-border
border-bottom $ui-border
background-color $ui-backgroundColor
user-select none
color $ui-text-color
@@ -88,6 +89,7 @@
margin-left 0
overflow hidden
background-color $ui-tooltip-backgroundColor
z-index 10
color white
line-height 34px
border-top-right-radius 5px
@@ -99,6 +101,7 @@
text-align center
&:hover .menu-button-label
width 100px
// TODO: extract tooltip style to a mixin
.menu-button-label
position fixed
display inline-block
@@ -110,6 +113,7 @@
margin-left 0
overflow ellipsis
background-color $ui-tooltip-backgroundColor
z-index 10
color white
line-height 34px
border-top-right-radius 5px

View File

@@ -74,8 +74,7 @@ class SideNav extends React.Component {
})
return (
<div
className='SideNav'
<div className='SideNav'
styleName={isFolded ? 'root-folded' : 'root'}
tabIndex='1'
>
@@ -83,7 +82,7 @@ class SideNav extends React.Component {
<button styleName='top-menu'
onClick={(e) => this.handleMenuButtonClick(e)}
>
<i styleName='top-menu-icon' className='fa fa-navicon fa-fw'/>
<i className='fa fa-navicon fa-fw'/>
<span styleName='top-menu-label'>Menu</span>
</button>
</div>
@@ -92,17 +91,13 @@ class SideNav extends React.Component {
<button styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
onClick={(e) => this.handleHomeButtonClick(e)}
>
<i styleName='menu-button-icon'
className='fa fa-home fa-fw'
/>
<i className='fa fa-home fa-fw'/>
<span styleName='menu-button-label'>Home</span>
</button>
<button styleName={isStarredActive ? 'menu-button--active' : 'menu-button'}
onClick={(e) => this.handleStarredButtonClick(e)}
>
<i styleName='menu-button-icon'
className='fa fa-star fa-fw'
/>
<i className='fa fa-star fa-fw'/>
<span styleName='menu-button-label'>Starred</span>
</button>
</div>
@@ -132,7 +127,13 @@ SideNav.contextTypes = {
SideNav.propTypes = {
dispatch: PropTypes.func,
repositories: PropTypes.array
repositories: PropTypes.array,
config: PropTypes.shape({
isSideNavFolded: PropTypes.bool
}),
location: PropTypes.shape({
pathname: PropTypes.string
})
}
export default CSSModules(SideNav, styles)

View File

@@ -1,7 +1,6 @@
.root
absolute bottom left right
height $statusBar-height
border-top $ui-border
height $statusBar-height - 1
background-color $ui-backgroundColor
.pathname

View File

@@ -2,14 +2,13 @@
absolute top right
background-color $ui-backgroundColor
left $sideNav-width
border-bottom $ui-border
height 60px
height $topBar-height -1
clearfix()
.left
float left
height 59px
line-height 60px
height $topBar-height - 1
line-height $topBar-height
.left-search
margin-top 12px
@@ -60,11 +59,12 @@
.left-control-newPostButton-tooltip
position fixed
line-height 1.4
background-color $ui-tooltip-backgroundColor
color $ui-tooltip-text-color
font-size 10px
margin-left -35px
margin-top 12px
margin-top 5px
padding 5px
z-index 1
border-radius 5px
@@ -73,7 +73,7 @@
.right
float right
height 60px
height $topBar-height -1
clearfix()
.right-helpButton

View File

@@ -1,4 +1,5 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TopBar.styl'
import activityRecord from 'browser/lib/activityRecord'
@@ -80,7 +81,7 @@ class TopBar extends React.Component {
onClick={(e) => this.handleNewPostButtonClick(e)}>
<i className='fa fa-plus'/>
<span styleName='left-control-newPostButton-tooltip'>
New Post ({OSX ? '⌘' : '^'} + n)
New Post {OSX ? '⌘' : '^'} + n
</span>
</button>
</div>
@@ -111,4 +112,4 @@ TopBar.propTypes = {
})
}
export default CSSModules(TopBar, styles)
export default connect((x) => x)(CSSModules(TopBar, styles))

View File

@@ -2,13 +2,16 @@ import _ from 'lodash'
const defaultConfig = {
zoom: 1,
isSideNavFolded: false
isSideNavFolded: false,
listWidth: 250
}
function _validate (config) {
function validate (config) {
console.log(config)
if (!_.isObject(config)) return false
if (!_.isNumber(config.zoom) || config.zoom < 0) return false
if (!_.isBoolean(config.isSideNavFolded)) return false
if (!_.isNumber(config.listWidth) || config.listWidth <= 0) return false
return true
}
@@ -22,7 +25,7 @@ function get () {
try {
config = JSON.parse(config)
if (!_validate(config)) throw new Error('INVALID CONFIG')
if (!validate(config)) throw new Error('INVALID CONFIG')
} catch (err) {
console.warn('Boostnote resets the malformed configuration.')
config = defaultConfig
@@ -35,11 +38,12 @@ function get () {
function set (updates) {
let currentConfig = get()
let newConfig = Object.assign({}, defaultConfig, currentConfig, updates)
if (!_validate(newConfig)) throw new Error('INVALID CONFIG')
if (!validate(newConfig)) throw new Error('INVALID CONFIG')
_save(newConfig)
}
export default {
get,
set
set,
validate
}

View File

@@ -30,7 +30,7 @@ import ConfigManager from 'browser/main/lib/ConfigManager'
const initialRepositories = []
function repositories (state = initialRepositories, action) {
console.log(action)
console.info('REDUX >> ', action)
switch (action.type) {
case 'INIT_ALL':
return action.data.slice()
@@ -121,6 +121,11 @@ function config (state = defaultConfig, action) {
case 'SET_ZOOM':
state.zoom = action.zoom
return Object.assign({}, state)
case 'SET_LIST_WIDTH':
state.listWidth = action.listWidth
return Object.assign({}, state)
case 'SET_CONFIG':
return Object.assign({}, state, action.config)
}
return state
}

View File

@@ -7,6 +7,7 @@ $danger-lighten-color = #FFE5E6
$statusBar-height = 24px
$sideNav-width = 200px
$sideNav--folded-width = 44px
$topBar-height = 60px
// UI default
$ui-text-color = #515151

View File

@@ -1,393 +0,0 @@
noTagsColor = #999
infoButton()
display inline-block
border-radius 16.5px
cursor pointer
height 33px
width 33px
line-height 33px
margin-right 5px
font-size 18px
color inactiveTextColor
background-color white
padding 0
border 1px solid white
&:focus
border-color focusBorderColor
&:hover
color inherit
.ArticleDetail
absolute right bottom
top 60px
left 450px
padding 10px
background-color #E6E6E6
border-top 1px solid borderColor
&.empty
.ArticleDetail-empty-box
line-height 72px
font-size 42px
height 320px
display flex
align-items center
.ArticleDetail-empty-box-message
text-align center
width 100%
color inactiveTextColor
.ArticleDetail-info
height 70px
width 100%
font-size 12px
user-select none
&>.tutorial
position fixed
z-index 35
.ArticleDetail-info-folder
display inline-block
max-width 100px
overflow ellipsis
height 10px
width 150px
height 27px
outline none
background-color darken(white, 5%)
border 1px solid transparent
&:hover
background-color white
&:focus
border-color focusBorderColor
&>.tutorial
position fixed
z-index 35
.ArticleDetail-info-status
padding 0 5px
.unsaved-mark
color brandColor
.ArticleDetail-info-control
float right
clearfix
.ShareButton
display block
float left
&>button, .ShareButton-open-button
infoButton()
.tooltip
tooltip()
margin-top 30px
&:hover
.tooltip
opacity 1
&>button
float left
&:nth-child(1) .tooltip
margin-left -65px
.ArticleDetail-info-control-delete-button
.tooltip
right 5px
.ArticleDetail-info-control-save
float left
width 80px
margin-right 5px
overflow hidden
transition width 0.15s ease-in-out
border-radius 16.5px
&.hide
width 0px
opacity 0.2
.ArticleDetail-info-control-save-button
infoButton()
background-color brandColor
color white
font-size 12px
width 100%
border 1px solid brandBorderColor
white-space nowrap
.fa
font-size 18px
&:hover
color white
background-color lighten(brandColor, 15%)
&:focus
color white
background-color lighten(brandColor, 15%)
.tooltip
tooltip()
margin-top 30px
margin-left -90px
&:hover .tooltip
opacity 1
.ShareButton-open-button .tooltip
margin-left -40px
.ShareButton-dropdown
position fixed
width 185px
z-index 35
background-color #F0F0F0
padding 4px 0
border-radius 5px
right 5px
top 95px
box-shadow 0px 0px 10px 1px alpha(#bbb, 0.8)
border 1px solid #bcbcbc
&.hide
display none
&>button
background-color transparent
height 21px
width 100%
border none
padding-left 20px
text-align left
font-size 13px
font-family '.HelveticaNeueDeskInterface-Regular', sans-serif
&:hover
background-color #4297FE
color white
.ShareButton-url
height 40px
width 100%
position relative
padding 0 5px
.ShareButton-url-input
height 21px
border none
width 143px
float left
border-top-left-radius 3px
border-bottom-left-radius 3px
border 1px solid borderColor
border-right none
.ShareButton-url-button
height 21px
border none
width 30px
float left
background-color #F0F0F0
border-top-right-radius 3px
border-bottom-right-radius 3px
border 1px solid borderColor
.ShareButton-url-button-tooltip
tooltip()
right 10px
margin-top 5px
&:hover
.ShareButton-url-button-tooltip
opacity 1
&:active
background-color #4297FE
color white
.ShareButton-url-alert
padding 10px
line-height 16px
.ArticleDetail-info-row2
.tutorial
position fixed
z-index 35
font-style italic
.TagSelect
margin-top 5px
.TagSelect-tags
white-space nowrap
overflow-x auto
position relative
noSelect()
z-index 30
background-color #E6E6E6
clearfix()
.TagSelect-tags-item
background-color transparent
color white
margin 0 2px
padding 0
height 17px
float left
button.TagSelect-tags-item-remove
display block
float left
background-color transparent
border none
font-size 8px
color white
width 15px
height 17px
text-align center
line-height 12px
padding 0
margin 0
border-top solid 1px darken(brandColor, 5%)
border-bottom solid 1px darken(brandColor, 5%)
border-left solid 1px darken(brandColor, 5%)
border-right solid 1px transparent
border-radius left 2px
background-color brandColor
&:hover
background-color lighten(brandColor, 10%)
border-color lighten(brandColor, 10%)
&:focus
background-color lighten(brandColor, 10%)
border-color focusBorderColor
.TagSelect-tags-item-label
background-color brandColor
float left
font-size 12px
border-top solid 1px darken(brandColor, 5%)
border-bottom solid 1px darken(brandColor, 5%)
border-right solid 1px darken(brandColor, 5%)
line-height 15px
padding 0 5px
border-radius right 2px
input.TagSelect-input
background-color transparent
border none
border-bottom 1px solid transparent
outline none
margin 0 2px
transition 0.15s
height 18px
&:focus
border-color focusBorderColor
.TagSelect-suggest
position fixed
width 150px
max-height 150px
background-color white
z-index 50
border 1px solid borderColor
border-radius 5px
overflow-y auto
&>button
width 100%
display block
padding 0 15px
height 33px
line-height 33px
background-color transparent
border none
text-align left
font-size 14px
&:hover
background-color darken(white, 10%)
.ArticleDetail-panel
position absolute
top 70px
left 10px
right 10px
bottom 10px
overflow-x hidden
overflow-y auto
background-color white
border-radius 5px
border solid 1px lighten(borderColor, 15%)
&>.ArticleDetail-panel-header
display block
height 60px
&>.tutorial
fixed right
z-index 35
font-style italic
.ArticleDetail-panel-header-mode
z-index 30
background-color white
absolute top bottom
right 10px
display block
height 33px
margin-top 14px
width 120px
margin-right 15px
border solid 1px borderColor
border-radius 5px
transition width 0.15s
user-select none
&.idle
cursor pointer
&:hover
background-color darken(white, 5%)
.ModeIcon
padding 0 5px
line-height 33px
&.edit
border-color focusBorderColor
input
width 120px
line-height 31px
padding 0 10px
border none
outline none
background-color transparent
font-size 14px
.ModeSelect-options
position fixed
width 120px
z-index 10
border 1px solid borderColor
border-radius 5px
background-color white
max-height 250px
overflow-y auto
margin-top 5px
.ModeSelect-options-item
height 33px
line-height 33px
cursor pointer
&.active, &:hover.active
background-color brandColor
color white
.ModeIcon
width 30px
text-align center
display inline-block
&:hover
background-color darken(white, 10%)
.ArticleDetail-panel-header-title
absolute left top
right 145px
padding 0 15px
background-color transparent
input
border none
line-height 60px
width 100%
font-size 24px
outline none
.ArticleEditor
absolute left right bottom
top 60px
.ArticleDetail-panel-content-tooltip
absolute bottom right
height 24px
background-color alpha(black, 0.5)
line-height 24px
color white
padding 0 15px
opacity 0
transition 0.1s
z-index 35
&:hover .ArticleDetail-panel-content-tooltip
opacity 1
.MarkdownPreview
absolute top left right bottom
marked()
box-sizing border-box
padding 5px 15px
border-top solid 1px borderColor
overflow-y auto
user-select all
&.empty
color lighten(inactiveTextColor, 10%)
user-select none
font-size 14px
&.lineNumbered
.lineNumber
display block
.CodeEditor
absolute top left right bottom
border-top solid 1px borderColor
min-height 300px
border-bottom-left-radius 5px
border-bottom-right-radius 5px

View File

@@ -1,105 +0,0 @@
articleItemHoverBgColor = darken(white, 5%)
articleItemColor = #777
.ArticleList
absolute bottom
top 60px
left 200px
width 250px
border-top 1px solid borderColor
border-right 1px solid borderColor
&:focus
border-color focusBorderColor
overflow-y auto
noSelect()
&>div
.ArticleList-item
border solid 2px transparent
position relative
min-height 110px
width 100%
cursor pointer
transition 0.1s
background-color white
padding 0 10px
font-size 12px
.ArticleList-item-top
clearfix()
padding-top 2px
line-height 18px
height 20px
color articleItemColor
font-size 11px
i
margin-right 4px
.folderName
overflow ellipsis
display inline-block
width 120px
.updatedAt
float right
line-height 18px
.unsaved-mark
color brandColor
.ArticleList-item-middle
font-size 16px
position relative
padding-top 6px
height 22px
.mode
position absolute
left 0
font-size 12px
line-height 16px
.title
position absolute
left 19px
right 0
overflow ellipsis
small
color #AAA
.ArticleList-item-middle2
padding-top 8px
pre
color lighten(inactiveTextColor, 10%)
white-space pre-wrap
overflow hidden
height 33px
line-height 14px
font-size 10px
code
font-family Monaco, Menlo, 'Ubuntu Mono', Consolas, source-code-pro, monospace
.ArticleList-item-bottom
padding-bottom 5px
.tags
color articleItemColor
line-height 18px
word-wrap break-word
clearfix()
i.fa-tags
display inline
float left
padding 2px 2px 0 0
height 14px
line-height 13px
a
background-color brandColor
float left
color white
border-radius 2px
padding 1px 5px
margin 2px
height 14px
line-height 13px
font-size 10px
opacity 0.8
&:hover
opacity 1
&:hover, &.hover
background-color articleItemHoverBgColor
&:active, &.active
background-color white
&:active, &.active
border-color brandBorderColor
.divider
border-bottom solid 1px borderColor

View File

@@ -1,213 +0,0 @@
articleNavBgColor = #353535
articleCount = #999
.ArticleNavigator
background-color articleNavBgColor
absolute top bottom left
width 200px
border-right 1px solid borderColor
color white
user-select none
.userInfo
height 60px
display block
box-sizing content-box
border-bottom 1px solid borderColor
.userProfileName
color brandColor
font-size 28px
padding 6px 37px 0 10px
white-space nowrap
text-overflow ellipsis
overflow hidden
.userName
color white
padding-left 20px
margin-top 3px
.tutorial
position fixed
z-index 35
top 0
left 0
pointer-event none
font-style italic
transition 0.1s
&.hide
opacity 0
.settingBtn
width 22px
height 22px
line-height 22px
border-radius 11px
position absolute
top 19px
right 14px
color white
padding 0
background-color transparent
border 1px solid white
z-index 31
.tooltip
tooltip()
margin-top -5px
margin-left 10px
&:hover
.tooltip
opacity 1
&:active
background-color brandColor
border-color brandColor
.ArticleNavigator-unsaved
position absolute
top 100px
width 100%
height 225px
transition opacity 0.2s ease-in-out
&.hide
opacity 0.2
.ArticleNavigator-unsaved-header
border-bottom 1px solid alpha(borderColor, 0.5)
padding-bottom 5px
clearfix()
position relative
padding-left 10px
font-size 18px
line-height 22px
.ArticleNavigator-unsaved-list
height 165px
padding 5px 0
overflow-y scroll
.ArticleNavigator-unsaved-list-item
height 33px
padding-left 15px
clearfix()
transition 0.1s
cursor pointer
overflow hidden
&:hover
background-color alpha(white, 0.05)
&.active, &:active
background-color alpha(lighten(brandColor, 25%), 70%)
.ArticleNavigator-unsaved-list-item-label
float left
width 151px
line-height 33px
overflow ellipsis
.ArticleNavigator-unsaved-list-item-label-untitled
color inactiveTextColor
.ArticleNavigator-unsaved-list-item-discard-button
float right
width 33px
line-height 30px
height 33px
border none
background-color transparent
color white
font-size 18px
opacity 0.5
&:hover
opacity 1
.ArticleNavigator-unsaved-list-empty
height 33px
padding-left 15px
color alpha(white, 0.4)
transition 0.1s
line-height 33px
&:hover
color alpha(white, 0.6)
.ArticleNavigator-unsaved-control
absolute bottom
height 33px
border-top 1px solid alpha(borderColor, 0.5)
width 100%
.ArticleNavigator-unsaved-control-save-all-button
border none
background-color transparent
font-size 14px
color brandColor
padding-left 15px
width 100%
height 33px
text-align left
&:hover
color lighten(brandColor, 15%)
background-color alpha(white, 0.05)
&:active
color white
&:disabled
color alpha(brandColor, 0.5)
&:hover
color alpha(lighten(brandColor, 25%), 0.5)
background-color transparent
.ArticleNavigator-folders
absolute bottom
top 365px
width 100%
transition top 0.15s ease-in-out
background-color articleNavBgColor
.tutorial
position fixed
z-index 35
font-style italic
&.expand
top 100px
.ArticleNavigator-folders-header
border-bottom 1px solid alpha(borderColor, 0.5)
padding-bottom 5px
clearfix()
position relative
z-index 30
.title
float left
padding-left 10px
font-size 18px
line-height 22px
.addBtn
float right
margin-right 15px
width 22px
height 22px
font-size 10px
padding 0
line-height 22px
border 1px solid white
border-radius 11px
background-color transparent
color white
padding 0
font-weight bold
.tooltip
tooltip()
margin-top -6px
margin-left 11px
&:hover
.tooltip
opacity 1
&:active
background-color brandColor
border-color brandColor
.folderList
absolute bottom
top 33px
overflow-y auto
.folderList button
height 33px
width 199px
border none
text-align left
font-size 14px
background-color transparent
color white
padding-left 15px
overflow ellipsis
&:hover
background-color alpha(white, 0.05)
&.active, &:active
background-color alpha(lighten(brandColor, 25%), 70%)
.articleCount
color white
.articleCount
color articleCount
font-size 12px

View File

@@ -2,9 +2,6 @@
@import '../mixins/*'
global-reset()
@import '../shared/*'
@import './ArticleNavigator'
@import './ArticleList'
@import './ArticleDetail'
@import './modal/*'
@import '../theme/*'
@@ -89,19 +86,3 @@ textarea.block-input
#content
fullsize()
.Main
.appUpdateButton
position fixed
z-index 2000
bottom 5px
right 53px
padding 10px 15px
border none
border-radius 5px
background-color brandColor
color white
opacity 0.7
&:hover
opacity 1
background-color lighten(brandColor, 10%)