mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 01:36:22 +00:00
resizible list
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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
25
browser/main/Main.styl
Normal 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
|
||||
17
browser/main/NoteDetail/NoteDetail.styl
Normal file
17
browser/main/NoteDetail/NoteDetail.styl
Normal 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
|
||||
|
||||
45
browser/main/NoteDetail/index.js
Normal file
45
browser/main/NoteDetail/index.js
Normal 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)
|
||||
4
browser/main/NoteList/NoteList.styl
Normal file
4
browser/main/NoteList/NoteList.styl
Normal file
@@ -0,0 +1,4 @@
|
||||
.root
|
||||
absolute top left bottom
|
||||
border-top $ui-border
|
||||
border-bottom $ui-border
|
||||
150
browser/main/NoteList/index.js
Normal file
150
browser/main/NoteList/index.js
Normal 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)
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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%)
|
||||
|
||||
Reference in New Issue
Block a user