From 77089a1178b451ef733900f6652a231ced7e3c2b Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Wed, 6 Sep 2017 20:02:47 +0200 Subject: [PATCH 01/13] extracted folder list and item to separate components --- .../modals/PreferencesModal/FolderItem.js | 276 ++++++++++++++++++ .../modals/PreferencesModal/FolderItem.styl | 94 ++++++ .../modals/PreferencesModal/FolderList.js | 52 ++++ .../modals/PreferencesModal/FolderList.styl | 0 .../modals/PreferencesModal/StorageItem.js | 276 +----------------- .../modals/PreferencesModal/StorageItem.styl | 94 ------ 6 files changed, 427 insertions(+), 365 deletions(-) create mode 100644 browser/main/modals/PreferencesModal/FolderItem.js create mode 100644 browser/main/modals/PreferencesModal/FolderItem.styl create mode 100644 browser/main/modals/PreferencesModal/FolderList.js create mode 100644 browser/main/modals/PreferencesModal/FolderList.styl diff --git a/browser/main/modals/PreferencesModal/FolderItem.js b/browser/main/modals/PreferencesModal/FolderItem.js new file mode 100644 index 000000000..354074062 --- /dev/null +++ b/browser/main/modals/PreferencesModal/FolderItem.js @@ -0,0 +1,276 @@ +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' + +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 ( +
this.handleFolderItemBlur(e)} + tabIndex='-1' + ref='root' + > +
+ + this.handleEditChange(e)} + /> +
+
+ + +
+
+ ) + } + + 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 ( +
+
+ Are you sure to delete this folder? +
+
+ + +
+
+ ) + } + + 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 ( +
this.handleEditButtonClick(e)} + > +
+ {folder.name} + ({folder.key}) +
+
+ + +
+
+ ) + } + + 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 + }) +} + +export default CSSModules(FolderItem, styles) diff --git a/browser/main/modals/PreferencesModal/FolderItem.styl b/browser/main/modals/PreferencesModal/FolderItem.styl new file mode 100644 index 000000000..010e5ebbc --- /dev/null +++ b/browser/main/modals/PreferencesModal/FolderItem.styl @@ -0,0 +1,94 @@ +.folderItem + height 35px + box-sizing border-box + padding 2.5px 15px + &:hover + background-color darken(white, 3%) +.folderItem-left + height 30px + border-left solid 2px transparent + padding 0 10px + line-height 30px + float left +.folderItem-left-danger + color $danger-color + font-weight bold + +.folderItem-left-key + color $ui-inactive-text-color + font-size 10px + margin 0 5px + border none + +.folderItem-left-colorButton + colorDefaultButton() + height 25px + width 25px + line-height 23px + padding 0 + box-sizing border-box + vertical-align middle + border $ui-border + border-radius 2px + margin-right 5px + margin-left -15px + +.folderItem-left-nameInput + height 25px + box-sizing border-box + vertical-align middle + border $ui-border + border-radius 2px + padding 0 5px + outline none + +.folderItem-right + float right + +.folderItem-right-button + vertical-align middle + height 25px + margin-top 2.5px + colorDefaultButton() + border-radius 2px + border $ui-border + margin-right 5px + padding 0 5px + &:last-child + margin-right 0 + +.folderItem-right-confirmButton + @extend .folderItem-right-button + border none + colorPrimaryButton() + +.folderItem-right-dangerButton + @extend .folderItem-right-button + border none + colorDangerButton() + +body[data-theme="dark"] + .folderItem + &:hover + background-color lighten($ui-dark-button--hover-backgroundColor, 5%) + + .folderItem-left-danger + color $danger-color + font-weight bold + + .folderItem-left-key + color $ui-dark-inactive-text-color + + .folderItem-left-colorButton + colorDarkDefaultButton() + border-color $ui-dark-borderColor + + .folderItem-right-button + colorDarkDefaultButton() + border-color $ui-dark-borderColor + + .folderItem-right-confirmButton + colorDarkPrimaryButton() + + .folderItem-right-dangerButton + colorDarkDangerButton() diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js new file mode 100644 index 000000000..5d6010b98 --- /dev/null +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -0,0 +1,52 @@ +import React, { PropTypes } from 'react' +import CSSModules from 'browser/lib/CSSModules' +import styles from './FolderList.styl' +import FolderItem from './FolderItem' + +class FolderList extends React.Component { + constructor (props) { + super(props) + } + + render () { + let { storage, hostBoundingBox } = this.props + + let folderList = storage.folders.map((folder) => { + return + }) + + return ( +
+ {folderList.length > 0 + ? folderList + :
No Folders
+ } +
+ ) + } +} + +FolderList.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 + }) +} + +export default CSSModules(FolderList, styles) diff --git a/browser/main/modals/PreferencesModal/FolderList.styl b/browser/main/modals/PreferencesModal/FolderList.styl new file mode 100644 index 000000000..e69de29bb diff --git a/browser/main/modals/PreferencesModal/StorageItem.js b/browser/main/modals/PreferencesModal/StorageItem.js index d3586f355..478e4c443 100644 --- a/browser/main/modals/PreferencesModal/StorageItem.js +++ b/browser/main/modals/PreferencesModal/StorageItem.js @@ -1,265 +1,13 @@ import React, { PropTypes } from 'react' -import ReactDOM from 'react-dom' import CSSModules from 'browser/lib/CSSModules' import styles from './StorageItem.styl' import consts from 'browser/lib/consts' import dataApi from 'browser/main/lib/dataApi' import store from 'browser/main/store' +import FolderList from './FolderList' const { shell, remote } = require('electron') const { dialog } = remote -import { SketchPicker } from 'react-color' - -class UnstyledFolderItem 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 ( -
this.handleFolderItemBlur(e)} - tabIndex='-1' - ref='root' - > -
- - this.handleEditChange(e)} - /> -
-
- - -
-
- ) - } - - 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 ( -
-
- Are you sure to delete this folder? -
-
- - -
-
- ) - } - - 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 ( -
this.handleEditButtonClick(e)} - > -
- {folder.name} - ({folder.key}) -
-
- - -
-
- - ) - } - - render () { - switch (this.state.status) { - case 'DELETE': - return this.renderDelete() - case 'EDIT': - return this.renderEdit() - case 'IDLE': - default: - return this.renderIdle() - } - } -} - -const FolderItem = CSSModules(UnstyledFolderItem, styles) class StorageItem extends React.Component { constructor (props) { @@ -349,13 +97,7 @@ class StorageItem extends React.Component { render () { let { storage, hostBoundingBox } = this.props - let folderList = storage.folders.map((folder) => { - return - }) + return (
@@ -404,12 +146,9 @@ class StorageItem extends React.Component {
-
- {folderList.length > 0 - ? folderList - :
No Folders
- } -
+ ) } @@ -426,11 +165,6 @@ StorageItem.propTypes = { }), storage: PropTypes.shape({ key: PropTypes.string - }), - folder: PropTypes.shape({ - key: PropTypes.string, - color: PropTypes.string, - name: PropTypes.string }) } diff --git a/browser/main/modals/PreferencesModal/StorageItem.styl b/browser/main/modals/PreferencesModal/StorageItem.styl index 538edfb8f..13759007c 100644 --- a/browser/main/modals/PreferencesModal/StorageItem.styl +++ b/browser/main/modals/PreferencesModal/StorageItem.styl @@ -63,75 +63,6 @@ z-index 10 white-space nowrap -.folderList-item - height 35px - box-sizing border-box - padding 2.5px 15px - &:hover - background-color darken(white, 3%) -.folderList-item-left - height 30px - border-left solid 2px transparent - padding 0 10px - line-height 30px - float left -.folderList-item-left-danger - color $danger-color - font-weight bold - -.folderList-item-left-key - color $ui-inactive-text-color - font-size 10px - margin 0 5px - border none - -.folderList-item-left-colorButton - colorDefaultButton() - height 25px - width 25px - line-height 23px - padding 0 - box-sizing border-box - vertical-align middle - border $ui-border - border-radius 2px - margin-right 5px - margin-left -15px - -.folderList-item-left-nameInput - height 25px - box-sizing border-box - vertical-align middle - border $ui-border - border-radius 2px - padding 0 5px - outline none - -.folderList-item-right - float right - -.folderList-item-right-button - vertical-align middle - height 25px - margin-top 2.5px - colorDefaultButton() - border-radius 2px - border $ui-border - margin-right 5px - padding 0 5px - &:last-child - margin-right 0 - -.folderList-item-right-confirmButton - @extend .folderList-item-right-button - border none - colorPrimaryButton() - -.folderList-item-right-dangerButton - @extend .folderList-item-right-button - border none - colorDangerButton() - body[data-theme="dark"] .header border-color $ui-dark-borderColor @@ -153,28 +84,3 @@ body[data-theme="dark"] top 25px z-index 10 white-space nowrap - - .folderList-item - &:hover - background-color lighten($ui-dark-button--hover-backgroundColor, 5%) - - .folderList-item-left-danger - color $danger-color - font-weight bold - - .folderList-item-left-key - color $ui-dark-inactive-text-color - - .folderList-item-left-colorButton - colorDarkDefaultButton() - border-color $ui-dark-borderColor - - .folderList-item-right-button - colorDarkDefaultButton() - border-color $ui-dark-borderColor - - .folderList-item-right-confirmButton - colorDarkPrimaryButton() - - .folderList-item-right-dangerButton - colorDarkDangerButton() From a58c191dedd78d76dded3b9ac9edb32b4974bd48 Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Wed, 6 Sep 2017 22:56:32 +0200 Subject: [PATCH 02/13] used react-sortable-hoc on folderList and folderItem --- .../modals/PreferencesModal/FolderItem.js | 5 +++- .../modals/PreferencesModal/FolderList.js | 26 ++++++++++++++++--- package.json | 1 + yarn.lock | 22 +++++++++++++++- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/browser/main/modals/PreferencesModal/FolderItem.js b/browser/main/modals/PreferencesModal/FolderItem.js index 354074062..aa4f8ecd7 100644 --- a/browser/main/modals/PreferencesModal/FolderItem.js +++ b/browser/main/modals/PreferencesModal/FolderItem.js @@ -5,6 +5,7 @@ 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 } from 'react-sortable-hoc' class FolderItem extends React.Component { constructor (props) { @@ -273,4 +274,6 @@ FolderItem.propTypes = { }) } -export default CSSModules(FolderItem, styles) +const StyledFolderItem = CSSModules(FolderItem, styles) + +export default SortableElement(StyledFolderItem) diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js index 5d6010b98..2ad72f169 100644 --- a/browser/main/modals/PreferencesModal/FolderList.js +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -2,6 +2,7 @@ import React, { PropTypes } from 'react' import CSSModules from 'browser/lib/CSSModules' import styles from './FolderList.styl' import FolderItem from './FolderItem' +import { SortableContainer } from 'react-sortable-hoc' class FolderList extends React.Component { constructor (props) { @@ -11,10 +12,11 @@ class FolderList extends React.Component { render () { let { storage, hostBoundingBox } = this.props - let folderList = storage.folders.map((folder) => { + let folderList = storage.folders.map((folder, index) => { return }) @@ -43,10 +45,28 @@ FolderList.propTypes = { key: PropTypes.string }), folder: PropTypes.shape({ - key: PropTypes.string, + key: PropTypes.number, color: PropTypes.string, name: PropTypes.string }) } -export default CSSModules(FolderList, styles) +const StyledFolderList = CSSModules(FolderList, styles) +const SortableFolderList = SortableContainer(StyledFolderList) + +class SortableFolderListComponent extends React.Component { + constructor (props) { + super(props) + this.onSortEnd = ({oldIndex, newIndex}) => { + console.log("end") + } + } + + render() { + return ( + + ) + } +} + +export default SortableFolderListComponent diff --git a/package.json b/package.json index e5e1afc7b..03652a9ba 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "react-codemirror": "^0.3.0", "react-dom": "^15.0.2", "react-redux": "^4.4.5", + "react-sortable-hoc": "^0.6.7", "redux": "^3.5.2", "sander": "^0.5.1", "superagent": "^1.2.0", diff --git a/yarn.lock b/yarn.lock index d45101935..b3e2dc36a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1059,6 +1059,13 @@ babel-register@^6.11.6, babel-register@^6.24.1: mkdirp "^0.5.1" source-map-support "^0.4.2" +babel-runtime@^6.11.6: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.3.19: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" @@ -3951,7 +3958,7 @@ lodash@^3.5.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" -lodash@^4.0.0, lodash@^4.0.1, lodash@^4.11.1, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1: +lodash@^4.0.0, lodash@^4.0.1, lodash@^4.11.1, lodash@^4.12.0, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -5339,6 +5346,15 @@ react-router@^2.4.0: loose-envify "^1.2.0" warning "^3.0.0" +react-sortable-hoc@^0.6.7: + version "0.6.7" + resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-0.6.7.tgz#e30d247bc36dd5a605430c331ac9cb50a5fa72a6" + dependencies: + babel-runtime "^6.11.6" + invariant "^2.2.1" + lodash "^4.12.0" + prop-types "^15.5.7" + react-transform-catch-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/react-transform-catch-errors/-/react-transform-catch-errors-1.0.2.tgz#1b4d4a76e97271896fc16fe3086c793ec88a9eeb" @@ -5499,6 +5515,10 @@ regenerator-runtime@^0.10.0: version "0.10.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" +regenerator-runtime@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" + regenerator-transform@0.9.11: version "0.9.11" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283" From 0fea85e2f27e33a76b7e36ed0cfc2d9045662443 Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Thu, 7 Sep 2017 23:52:27 +0200 Subject: [PATCH 03/13] added drag handle to the folder item --- .../modals/PreferencesModal/FolderItem.js | 30 +++++++++++++++++-- .../modals/PreferencesModal/FolderItem.styl | 9 ++++++ .../modals/PreferencesModal/FolderList.js | 2 +- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/browser/main/modals/PreferencesModal/FolderItem.js b/browser/main/modals/PreferencesModal/FolderItem.js index aa4f8ecd7..87f4828b5 100644 --- a/browser/main/modals/PreferencesModal/FolderItem.js +++ b/browser/main/modals/PreferencesModal/FolderItem.js @@ -5,7 +5,7 @@ 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 } from 'react-sortable-hoc' +import { SortableElement, SortableHandle } from 'react-sortable-hoc' class FolderItem extends React.Component { constructor (props) { @@ -274,6 +274,30 @@ FolderItem.propTypes = { }) } -const StyledFolderItem = CSSModules(FolderItem, styles) +class Handle extends React.Component { + render() { + return ( +
+ +
+ ) + } +} + +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 ( +
+ + +
+ ) + } +} -export default SortableElement(StyledFolderItem) +export default CSSModules(SortableElement(SortableFolderItemComponent), styles) diff --git a/browser/main/modals/PreferencesModal/FolderItem.styl b/browser/main/modals/PreferencesModal/FolderItem.styl index 010e5ebbc..e3b095828 100644 --- a/browser/main/modals/PreferencesModal/FolderItem.styl +++ b/browser/main/modals/PreferencesModal/FolderItem.styl @@ -4,6 +4,15 @@ padding 2.5px 15px &:hover background-color darken(white, 3%) + +.folderItem-drag-handle + height 35px + border none + padding 0 10px + line-height 35px + float left + cursor row-resize + .folderItem-left height 30px border-left solid 2px transparent diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js index 2ad72f169..fa4beb66f 100644 --- a/browser/main/modals/PreferencesModal/FolderList.js +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -64,7 +64,7 @@ class SortableFolderListComponent extends React.Component { render() { return ( - + ) } } From 9141b1a6414b7cbe0868c4ea607e54d6e0eaf5e9 Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Fri, 8 Sep 2017 00:09:13 +0200 Subject: [PATCH 04/13] saving new order in the storage only for now --- browser/main/modals/PreferencesModal/FolderList.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js index fa4beb66f..7daa72430 100644 --- a/browser/main/modals/PreferencesModal/FolderList.js +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react' import CSSModules from 'browser/lib/CSSModules' import styles from './FolderList.styl' import FolderItem from './FolderItem' -import { SortableContainer } from 'react-sortable-hoc' +import { SortableContainer, arrayMove } from 'react-sortable-hoc' class FolderList extends React.Component { constructor (props) { @@ -58,7 +58,9 @@ class SortableFolderListComponent extends React.Component { constructor (props) { super(props) this.onSortEnd = ({oldIndex, newIndex}) => { - console.log("end") + let { storage } = this.props + storage.folders = arrayMove(storage.folders, oldIndex, newIndex) + this.setState() } } From c34dd462b66a86d5a0735a0786655576c9dff54f Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Fri, 8 Sep 2017 00:41:20 +0200 Subject: [PATCH 05/13] made sortable item helper visible by setting proper zindex --- browser/main/global.styl | 3 +++ browser/main/modals/PreferencesModal/FolderList.js | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/browser/main/global.styl b/browser/main/global.styl index 552e0d6cb..b84d2480b 100644 --- a/browser/main/global.styl +++ b/browser/main/global.styl @@ -102,3 +102,6 @@ body[data-theme="dark"] background #B1D7FE ::selection background #B1D7FE + +.sortableItemHelper + z-index 1005 diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js index 7daa72430..2feac7f29 100644 --- a/browser/main/modals/PreferencesModal/FolderList.js +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -51,8 +51,6 @@ FolderList.propTypes = { }) } -const StyledFolderList = CSSModules(FolderList, styles) -const SortableFolderList = SortableContainer(StyledFolderList) class SortableFolderListComponent extends React.Component { constructor (props) { @@ -65,10 +63,14 @@ class SortableFolderListComponent extends React.Component { } render() { + + const StyledFolderList = CSSModules(FolderList, this.props.styles) + const SortableFolderList = SortableContainer(StyledFolderList) + return ( - + ) } } -export default SortableFolderListComponent +export default CSSModules(SortableFolderListComponent, styles) From 5c2d7e2d2a58439bb04fb2d3efc2c7a46d4493c4 Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Fri, 8 Sep 2017 00:59:38 +0200 Subject: [PATCH 06/13] fixed typo in the useDragHandle attribute for the folder list --- browser/main/modals/PreferencesModal/FolderList.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js index 2feac7f29..d603d1460 100644 --- a/browser/main/modals/PreferencesModal/FolderList.js +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -68,7 +68,12 @@ class SortableFolderListComponent extends React.Component { const SortableFolderList = SortableContainer(StyledFolderList) return ( - + ) } } From 3804a746df521c7ff3414c63a0d5d8cdc3f623a7 Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Fri, 8 Sep 2017 12:07:59 +0200 Subject: [PATCH 07/13] connected sortable folder list to data api --- browser/main/lib/dataApi/index.js | 1 + browser/main/lib/dataApi/reorderFolder.js | 46 +++++++++++++++++++ .../modals/PreferencesModal/FolderList.js | 13 +++++- browser/main/store.js | 7 +++ package.json | 1 + yarn.lock | 6 +++ 6 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 browser/main/lib/dataApi/reorderFolder.js diff --git a/browser/main/lib/dataApi/index.js b/browser/main/lib/dataApi/index.js index bad6f5274..768dfe32f 100644 --- a/browser/main/lib/dataApi/index.js +++ b/browser/main/lib/dataApi/index.js @@ -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'), diff --git a/browser/main/lib/dataApi/reorderFolder.js b/browser/main/lib/dataApi/reorderFolder.js new file mode 100644 index 000000000..3f54358b2 --- /dev/null +++ b/browser/main/lib/dataApi/reorderFolder.js @@ -0,0 +1,46 @@ +const _ = require('lodash') +_.move = require('lodash-move').default +const path = require('path') +const resolveStorageData = require('./resolveStorageData') +const CSON = require('@rokt33r/season') + +/** + * @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.') + + 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.') + } 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 diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js index d603d1460..5dd317025 100644 --- a/browser/main/modals/PreferencesModal/FolderList.js +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -1,6 +1,8 @@ import React, { PropTypes } from 'react' import CSSModules from 'browser/lib/CSSModules' +import dataApi from 'browser/main/lib/dataApi' import styles from './FolderList.styl' +import store from 'browser/main/store' import FolderItem from './FolderItem' import { SortableContainer, arrayMove } from 'react-sortable-hoc' @@ -57,8 +59,15 @@ class SortableFolderListComponent extends React.Component { super(props) this.onSortEnd = ({oldIndex, newIndex}) => { let { storage } = this.props - storage.folders = arrayMove(storage.folders, oldIndex, newIndex) - this.setState() + dataApi + .reorderFolder(storage.key, oldIndex, newIndex) + .then((data) => { + store.dispatch({ + type: 'REORDER_FOLDER', + storage: data.storage + }) + this.setState() + }) } } diff --git a/browser/main/store.js b/browser/main/store.js index 68c468768..ac41b1d0d 100644 --- a/browser/main/store.js +++ b/browser/main/store.js @@ -346,6 +346,13 @@ function data (state = defaultDataMap(), action) { state.storageMap.set(action.storage.key, action.storage) } return state + case 'REORDER_FOLDER': + { + state = Object.assign({}, state) + state.storageMap = new Map(state.storageMap) + state.storageMap.set(action.storage.key, action.storage) + } + return state case 'DELETE_FOLDER': { state = Object.assign({}, state) diff --git a/package.json b/package.json index 03652a9ba..e2948050c 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "js-sequence-diagrams": "^1000000.0.6", "katex": "^0.7.1", "lodash": "^4.11.1", + "lodash-move": "^1.1.1", "markdown-it": "^6.0.1", "markdown-it-checkbox": "^1.1.0", "markdown-it-emoji": "^1.1.1", diff --git a/yarn.lock b/yarn.lock index b3e2dc36a..19f219fdb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3909,6 +3909,12 @@ lodash-es@^4.2.1: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7" +lodash-move@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/lodash-move/-/lodash-move-1.1.1.tgz#59f76e0f1ac57e6d8683f531bec07c5b6ea4e348" + dependencies: + lodash "^4.6.1" + lodash._reinterpolate@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" From 94e6f89d0722997299f83c587036b52067ab9c43 Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Fri, 8 Sep 2017 18:46:17 +0200 Subject: [PATCH 08/13] fixed errors from lint --- browser/main/modals/PreferencesModal/FolderItem.js | 6 +++--- browser/main/modals/PreferencesModal/FolderList.js | 10 ++-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/browser/main/modals/PreferencesModal/FolderItem.js b/browser/main/modals/PreferencesModal/FolderItem.js index 87f4828b5..5bd9d9f1d 100644 --- a/browser/main/modals/PreferencesModal/FolderItem.js +++ b/browser/main/modals/PreferencesModal/FolderItem.js @@ -275,17 +275,17 @@ FolderItem.propTypes = { } class Handle extends React.Component { - render() { + render () { return (
- +
) } } class SortableFolderItemComponent extends React.Component { - render() { + render () { const StyledHandle = CSSModules(Handle, this.props.styles) const DragHandle = SortableHandle(StyledHandle) diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js index 5dd317025..0172dc103 100644 --- a/browser/main/modals/PreferencesModal/FolderList.js +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -7,10 +7,6 @@ import FolderItem from './FolderItem' import { SortableContainer, arrayMove } from 'react-sortable-hoc' class FolderList extends React.Component { - constructor (props) { - super(props) - } - render () { let { storage, hostBoundingBox } = this.props @@ -18,7 +14,7 @@ class FolderList extends React.Component { return }) @@ -53,7 +49,6 @@ FolderList.propTypes = { }) } - class SortableFolderListComponent extends React.Component { constructor (props) { super(props) @@ -71,8 +66,7 @@ class SortableFolderListComponent extends React.Component { } } - render() { - + render () { const StyledFolderList = CSSModules(FolderList, this.props.styles) const SortableFolderList = SortableContainer(StyledFolderList) From 5c2c99282d4dcf09ede04daad24d2ded7c003684 Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Fri, 8 Sep 2017 18:58:35 +0200 Subject: [PATCH 09/13] added test for reorderFolder --- tests/dataApi/reorderFolder-test.js | 48 +++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tests/dataApi/reorderFolder-test.js diff --git a/tests/dataApi/reorderFolder-test.js b/tests/dataApi/reorderFolder-test.js new file mode 100644 index 000000000..2069683fd --- /dev/null +++ b/tests/dataApi/reorderFolder-test.js @@ -0,0 +1,48 @@ +const test = require('ava') +const reorderFolder = require('browser/main/lib/dataApi/reorderFolder') + +global.document = require('jsdom').jsdom('') +global.window = document.defaultView +global.navigator = window.navigator + +const Storage = require('dom-storage') +const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const path = require('path') +const _ = require('lodash') +const TestDummy = require('../fixtures/TestDummy') +const sander = require('sander') +const os = require('os') +const CSON = require('@rokt33r/season') + +const storagePath = path.join(os.tmpdir(), 'test/reorder-folder') + +test.beforeEach((t) => { + t.context.storage = TestDummy.dummyStorage(storagePath) + localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) +}) + +test.serial('Update a folder', (t) => { + const storageKey = t.context.storage.cache.key + const firstFolderKey = t.context.storage.json.folders[0].key + const secondFolderKey = t.context.storage.json.folders[1].key + + return Promise.resolve() + .then(function doTest () { + return reorderFolder(storageKey, 0, 1) + }) + .then(function assert (data) { + t.true(_.nth(data.storage.folders, 0).key === secondFolderKey) + t.true(_.nth(data.storage.folders, 1).key === firstFolderKey) + + let jsonData = CSON.readFileSync(path.join(data.storage.path, 'boostnote.json')) + console.log(path.join(data.storage.path, 'boostnote.json')) + + t.true(_.nth(jsonData.folders, 0).key === secondFolderKey) + t.true(_.nth(jsonData.folders, 1).key === firstFolderKey) + }) +}) + +test.after(function after () { + localStorage.clear() + sander.rimrafSync(storagePath) +}) From 6b7132f134c1397f7716a7d7718969d51d918c17 Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Sat, 9 Sep 2017 21:50:06 +0200 Subject: [PATCH 10/13] fixed style of dragged folder item for dark theme --- browser/main/global.styl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/browser/main/global.styl b/browser/main/global.styl index b84d2480b..521b79905 100644 --- a/browser/main/global.styl +++ b/browser/main/global.styl @@ -104,4 +104,8 @@ body[data-theme="dark"] background #B1D7FE .sortableItemHelper - z-index 1005 + z-index modalZIndex + 5 + +body[data-theme="dark"] + .sortableItemHelper + color: $ui-dark-text-color From 59985dee721f4ea7c6aacf94b6101fa003c48ae7 Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Sat, 9 Sep 2017 21:53:10 +0200 Subject: [PATCH 11/13] fixed test title --- tests/dataApi/reorderFolder-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/dataApi/reorderFolder-test.js b/tests/dataApi/reorderFolder-test.js index 2069683fd..bb7f6b0c7 100644 --- a/tests/dataApi/reorderFolder-test.js +++ b/tests/dataApi/reorderFolder-test.js @@ -21,7 +21,7 @@ test.beforeEach((t) => { localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Update a folder', (t) => { +test.serial('Reorder a folder', (t) => { const storageKey = t.context.storage.cache.key const firstFolderKey = t.context.storage.json.folders[0].key const secondFolderKey = t.context.storage.json.folders[1].key From a15dfffa44d123eee440f4960231804bc9eef2af Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Mon, 11 Sep 2017 21:09:51 +0200 Subject: [PATCH 12/13] used findStorage function in the reorderFolder --- browser/main/lib/dataApi/reorderFolder.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/browser/main/lib/dataApi/reorderFolder.js b/browser/main/lib/dataApi/reorderFolder.js index 3f54358b2..3ee9229e8 100644 --- a/browser/main/lib/dataApi/reorderFolder.js +++ b/browser/main/lib/dataApi/reorderFolder.js @@ -3,6 +3,7 @@ _.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 @@ -23,11 +24,7 @@ function reorderFolder (storageKey, oldIndex, newIndex) { if (!_.isNumber(oldIndex)) throw new Error('oldIndex must be a number.') if (!_.isNumber(newIndex)) throw new Error('newIndex must be a number.') - 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) } From 43c49f54d29a2789381f811dcd680e47cf874878 Mon Sep 17 00:00:00 2001 From: Matus Benko Date: Mon, 11 Sep 2017 21:10:44 +0200 Subject: [PATCH 13/13] used findStorage function in the updateFolder --- browser/main/lib/dataApi/updateFolder.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/browser/main/lib/dataApi/updateFolder.js b/browser/main/lib/dataApi/updateFolder.js index e128a2d32..6c6e587b0 100644 --- a/browser/main/lib/dataApi/updateFolder.js +++ b/browser/main/lib/dataApi/updateFolder.js @@ -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 @@ -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) }