Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions browser/main/global.styl
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,10 @@ body[data-theme="dark"]
background #B1D7FE
::selection
background #B1D7FE

.sortableItemHelper
z-index modalZIndex + 5

body[data-theme="dark"]
.sortableItemHelper
color: $ui-dark-text-color
1 change: 1 addition & 0 deletions browser/main/lib/dataApi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const dataApi = {
createFolder: require('./createFolder'),
updateFolder: require('./updateFolder'),
deleteFolder: require('./deleteFolder'),
reorderFolder: require('./reorderFolder'),
createNote: require('./createNote'),
updateNote: require('./updateNote'),
deleteNote: require('./deleteNote'),
Expand Down
43 changes: 43 additions & 0 deletions browser/main/lib/dataApi/reorderFolder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const _ = require('lodash')
_.move = require('lodash-move').default
const path = require('path')
const resolveStorageData = require('./resolveStorageData')
const CSON = require('@rokt33r/season')
const { findStorage } = require('browser/lib/findStorage')

/**
* @param {String} storageKey
* @param {number} oldIndex
* @param {number} newIndex
*
* @return {Object}
* ```
* {
* storage: Object
* }
* ```
*/
function reorderFolder (storageKey, oldIndex, newIndex) {
let rawStorages
let targetStorage
try {
if (!_.isNumber(oldIndex)) throw new Error('oldIndex must be a number.')
if (!_.isNumber(newIndex)) throw new Error('newIndex must be a number.')

targetStorage = findStorage(storageKey)
} catch (e) {
return Promise.reject(e)
}

return resolveStorageData(targetStorage)
.then(function reorderFolder (storage) {
storage.folders = _.move(storage.folders, oldIndex, newIndex)
CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version']))

return {
storage
}
})
}

module.exports = reorderFolder
7 changes: 2 additions & 5 deletions browser/main/lib/dataApi/updateFolder.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const _ = require('lodash')
const path = require('path')
const resolveStorageData = require('./resolveStorageData')
const CSON = require('@rokt33r/season')
const { findStorage } = require('browser/lib/findStorage')

/**
* @param {String} storageKey
Expand Down Expand Up @@ -29,11 +30,7 @@ function updateFolder (storageKey, folderKey, input) {
if (!_.isString(input.name)) throw new Error('Name must be a string.')
if (!_.isString(input.color)) throw new Error('Color must be a string.')

rawStorages = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(rawStorages)) throw new Error('Target storage doesn\'t exist.')

targetStorage = _.find(rawStorages, {key: storageKey})
if (targetStorage == null) throw new Error('Target storage doesn\'t exist.')
targetStorage = findStorage(storageKey)
} catch (e) {
return Promise.reject(e)
}
Expand Down
303 changes: 303 additions & 0 deletions browser/main/modals/PreferencesModal/FolderItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
import React, { PropTypes } from 'react'
import CSSModules from 'browser/lib/CSSModules'
import ReactDOM from 'react-dom'
import styles from './FolderItem.styl'
import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import { SketchPicker } from 'react-color'
import { SortableElement, SortableHandle } from 'react-sortable-hoc'

class FolderItem extends React.Component {
constructor (props) {
super(props)

this.state = {
status: 'IDLE',
folder: {
showColumnPicker: false,
colorPickerPos: { left: 0, top: 0 },
color: props.color,
name: props.name
}
}
}

handleEditChange (e) {
let { folder } = this.state

folder.name = this.refs.nameInput.value
this.setState({
folder
})
}

handleConfirmButtonClick (e) {
this.confirm()
}

confirm () {
let { storage, folder } = this.props
dataApi
.updateFolder(storage.key, folder.key, {
color: this.state.folder.color,
name: this.state.folder.name
})
.then((data) => {
store.dispatch({
type: 'UPDATE_FOLDER',
storage: data.storage
})
this.setState({
status: 'IDLE'
})
})
}

handleColorButtonClick (e) {
const folder = Object.assign({}, this.state.folder, { showColumnPicker: true, colorPickerPos: { left: 0, top: 0 } })
this.setState({ folder }, function () {
// After the color picker has been painted, re-calculate its position
// by comparing its dimensions to the host dimensions.
const { hostBoundingBox } = this.props
const colorPickerNode = ReactDOM.findDOMNode(this.refs.colorPicker)
const colorPickerBox = colorPickerNode.getBoundingClientRect()
const offsetTop = hostBoundingBox.bottom - colorPickerBox.bottom
const folder = Object.assign({}, this.state.folder, {
colorPickerPos: {
left: 25,
top: offsetTop < 0 ? offsetTop - 5 : 0 // subtract 5px for aestetics
}
})
this.setState({ folder })
})
}

handleColorChange (color) {
const folder = Object.assign({}, this.state.folder, { color: color.hex })
this.setState({ folder })
}

handleColorPickerClose (event) {
const folder = Object.assign({}, this.state.folder, { showColumnPicker: false })
this.setState({ folder })
}

handleCancelButtonClick (e) {
this.setState({
status: 'IDLE'
})
}

handleFolderItemBlur (e) {
let el = e.relatedTarget
while (el != null) {
if (el === this.refs.root) {
return false
}
el = el.parentNode
}
this.confirm()
}

renderEdit (e) {
const popover = { position: 'absolute', zIndex: 2 }
const cover = {
position: 'fixed',
top: 0,
right: 0,
bottom: 0,
left: 0
}
const pickerStyle = Object.assign({}, {
position: 'absolute'
}, this.state.folder.colorPickerPos)
return (
<div styleName='folderItem'
onBlur={(e) => this.handleFolderItemBlur(e)}
tabIndex='-1'
ref='root'
>
<div styleName='folderItem-left'>
<button styleName='folderItem-left-colorButton' style={{color: this.state.folder.color}}
onClick={(e) => !this.state.folder.showColumnPicker && this.handleColorButtonClick(e)}
>
{this.state.folder.showColumnPicker
? <div style={popover}>
<div style={cover}
onClick={() => this.handleColorPickerClose()}
/>
<div style={pickerStyle}>
<SketchPicker
ref='colorPicker'
color={this.state.folder.color}
onChange={(color) => this.handleColorChange(color)}
onChangeComplete={(color) => this.handleColorChange(color)}
/>
</div>
</div>
: null
}
<i className='fa fa-square' />
</button>
<input styleName='folderItem-left-nameInput'
value={this.state.folder.name}
ref='nameInput'
onChange={(e) => this.handleEditChange(e)}
/>
</div>
<div styleName='folderItem-right'>
<button styleName='folderItem-right-confirmButton'
onClick={(e) => this.handleConfirmButtonClick(e)}
>
Confirm
</button>
<button styleName='folderItem-right-button'
onClick={(e) => this.handleCancelButtonClick(e)}
>
Cancel
</button>
</div>
</div>
)
}

handleDeleteConfirmButtonClick (e) {
let { storage, folder } = this.props
dataApi
.deleteFolder(storage.key, folder.key)
.then((data) => {
store.dispatch({
type: 'DELETE_FOLDER',
storage: data.storage,
folderKey: data.folderKey
})
})
}

renderDelete () {
return (
<div styleName='folderItem'>
<div styleName='folderItem-left'>
Are you sure to <span styleName='folderItem-left-danger'>delete</span> this folder?
</div>
<div styleName='folderItem-right'>
<button styleName='folderItem-right-dangerButton'
onClick={(e) => this.handleDeleteConfirmButtonClick(e)}
>
Confirm
</button>
<button styleName='folderItem-right-button'
onClick={(e) => this.handleCancelButtonClick(e)}
>
Cancel
</button>
</div>
</div>
)
}

handleEditButtonClick (e) {
let { folder: propsFolder } = this.props
let { folder: stateFolder } = this.state
const folder = Object.assign({}, stateFolder, propsFolder)
this.setState({
status: 'EDIT',
folder
}, () => {
this.refs.nameInput.select()
})
}

handleDeleteButtonClick (e) {
this.setState({
status: 'DELETE'
})
}

renderIdle () {
let { folder } = this.props
return (
<div styleName='folderItem'
onDoubleClick={(e) => this.handleEditButtonClick(e)}
>
<div styleName='folderItem-left'
style={{borderColor: folder.color}}
>
<span styleName='folderItem-left-name'>{folder.name}</span>
<span styleName='folderItem-left-key'>({folder.key})</span>
</div>
<div styleName='folderItem-right'>
<button styleName='folderItem-right-button'
onClick={(e) => this.handleEditButtonClick(e)}
>
Edit
</button>
<button styleName='folderItem-right-button'
onClick={(e) => this.handleDeleteButtonClick(e)}
>
Delete
</button>
</div>
</div>
)
}

render () {
switch (this.state.status) {
case 'DELETE':
return this.renderDelete()
case 'EDIT':
return this.renderEdit()
case 'IDLE':
default:
return this.renderIdle()
}
}
}

FolderItem.propTypes = {
hostBoundingBox: PropTypes.shape({
bottom: PropTypes.number,
height: PropTypes.number,
left: PropTypes.number,
right: PropTypes.number,
top: PropTypes.number,
width: PropTypes.number
}),
storage: PropTypes.shape({
key: PropTypes.string
}),
folder: PropTypes.shape({
key: PropTypes.string,
color: PropTypes.string,
name: PropTypes.string
})
}

class Handle extends React.Component {
render () {
return (
<div styleName='folderItem-drag-handle'>
<i className='fa fa-reorder' />
</div>
)
}
}

class SortableFolderItemComponent extends React.Component {
render () {
const StyledHandle = CSSModules(Handle, this.props.styles)
const DragHandle = SortableHandle(StyledHandle)

const StyledFolderItem = CSSModules(FolderItem, this.props.styles)

return (
<div>
<DragHandle />
<StyledFolderItem {...this.props} />
</div>
)
}
}

export default CSSModules(SortableElement(SortableFolderItemComponent), styles)
Loading