diff --git a/browser/main/global.styl b/browser/main/global.styl index ef70fcb3..9da7a596 100644 --- a/browser/main/global.styl +++ b/browser/main/global.styl @@ -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 diff --git a/browser/main/lib/dataApi/index.js b/browser/main/lib/dataApi/index.js index bad6f527..768dfe32 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 00000000..3ee9229e --- /dev/null +++ b/browser/main/lib/dataApi/reorderFolder.js @@ -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 diff --git a/browser/main/lib/dataApi/updateFolder.js b/browser/main/lib/dataApi/updateFolder.js index e128a2d3..6c6e587b 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) } diff --git a/browser/main/modals/PreferencesModal/FolderItem.js b/browser/main/modals/PreferencesModal/FolderItem.js new file mode 100644 index 00000000..5bd9d9f1 --- /dev/null +++ b/browser/main/modals/PreferencesModal/FolderItem.js @@ -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 ( +
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 + }) +} + +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 CSSModules(SortableElement(SortableFolderItemComponent), styles) diff --git a/browser/main/modals/PreferencesModal/FolderItem.styl b/browser/main/modals/PreferencesModal/FolderItem.styl new file mode 100644 index 00000000..e3b09582 --- /dev/null +++ b/browser/main/modals/PreferencesModal/FolderItem.styl @@ -0,0 +1,103 @@ +.folderItem + height 35px + box-sizing border-box + 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 + 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 00000000..0172dc10 --- /dev/null +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -0,0 +1,84 @@ +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' + +class FolderList extends React.Component { + render () { + let { storage, hostBoundingBox } = this.props + + let folderList = storage.folders.map((folder, index) => { + 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.number, + color: PropTypes.string, + name: PropTypes.string + }) +} + +class SortableFolderListComponent extends React.Component { + constructor (props) { + super(props) + this.onSortEnd = ({oldIndex, newIndex}) => { + let { storage } = this.props + dataApi + .reorderFolder(storage.key, oldIndex, newIndex) + .then((data) => { + store.dispatch({ + type: 'REORDER_FOLDER', + storage: data.storage + }) + this.setState() + }) + } + } + + render () { + const StyledFolderList = CSSModules(FolderList, this.props.styles) + const SortableFolderList = SortableContainer(StyledFolderList) + + return ( + + ) + } +} + +export default CSSModules(SortableFolderListComponent, styles) diff --git a/browser/main/modals/PreferencesModal/FolderList.styl b/browser/main/modals/PreferencesModal/FolderList.styl new file mode 100644 index 00000000..e69de29b diff --git a/browser/main/modals/PreferencesModal/StorageItem.js b/browser/main/modals/PreferencesModal/StorageItem.js index d3586f35..478e4c44 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 538edfb8..13759007 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() diff --git a/browser/main/store.js b/browser/main/store.js index 68c46876..ac41b1d0 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 2370049b..33e42fde 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", @@ -77,6 +78,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", "striptags": "^2.2.1", diff --git a/tests/dataApi/reorderFolder-test.js b/tests/dataApi/reorderFolder-test.js new file mode 100644 index 00000000..bb7f6b0c --- /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('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 + + 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) +}) diff --git a/yarn.lock b/yarn.lock index 157e2977..ad4becaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1057,6 +1057,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" @@ -3900,6 +3907,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" @@ -3949,7 +3962,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" @@ -5337,6 +5350,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" @@ -5497,6 +5519,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"