From 568324e84cf33d4aa696ceed37f9f1d2f1e2ee1c Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Thu, 3 Dec 2020 02:54:37 +0100 Subject: [PATCH] Publish to gist --- .../file-explorer/src/lib/file-explorer.tsx | 465 ++++++++++-------- .../src/lib/tree-view-item/tree-view-item.tsx | 4 +- libs/remix-ui/tree-view/src/types/index.ts | 1 + package-lock.json | 102 +++- package.json | 3 +- 5 files changed, 365 insertions(+), 210 deletions(-) diff --git a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx index a1453ae99c..6b1882fd7e 100644 --- a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx +++ b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx @@ -1,13 +1,43 @@ import React, { useEffect, useState, useRef } from 'react' // eslint-disable-line import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line -import Draggable from 'react-draggable' // eslint-disable-line +import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' // eslint-disable-line +import * as async from 'async' +import * as Gists from 'gists' import * as helper from '../../../../../apps/remix-ide/src/lib/helper' +import QueryParams from '../../../../../apps/remix-ide/src/lib/query-params' import { FileExplorerProps, File } from './types' import './css/file-explorer.css' +const queryParams = new QueryParams() + +function packageFiles (filesProvider, directory, callback) { + const ret = {} + filesProvider.resolveDirectory(directory, (error, files) => { + if (error) callback(error) + else { + async.eachSeries(Object.keys(files), (path, cb) => { + if (filesProvider.isDirectory(path)) { + cb() + } else { + filesProvider.get(path, (error, content) => { + if (error) return cb(error) + if (/^\s+$/.test(content) || !content.length) { + content = '// this line is added to create a gist. Empty file is not allowed.' + } + ret[path] = { content } + cb() + }) + } + }, (error) => { + callback(error, ret) + }) + } + }) +} + export const FileExplorer = (props: FileExplorerProps) => { - const { files, name, registry } = props + const { files, name, registry, plugin } = props const uploadFile = (target) => { // TODO The file explorer is merely a view on the current state of // the files module. Please ask the user here if they want to overwrite @@ -47,34 +77,6 @@ export const FileExplorer = (props: FileExplorerProps) => { }) }) } - const publishToGist = () => { - // modalDialogCustom.confirm( - // 'Create a public gist', - // 'Are you sure you want to publish all your files in browser directory anonymously as a public gist on github.com? Note: this will not include directories.', - // () => { this.toGist() } - // ) - } - const createNewFile = (parentFolder = 'browser') => { - // const self = this - // modalDialogCustom.prompt('Create new file', 'File Name (e.g Untitled.sol)', 'Untitled.sol', (input) => { - // if (!input) input = 'New file' - // helper.createNonClashingName(parentFolder + '/' + input, self.files, async (error, newName) => { - // if (error) return tooltip('Failed to create file ' + newName + ' ' + error) - // const fileManager = self._deps.fileManager - // const createFile = await fileManager.writeFile(newName, '') - - // if (!createFile) { - // tooltip('Failed to create file ' + newName) - // } else { - // await fileManager.open(newName) - // if (newName.includes('_test.sol')) { - // self.events.trigger('newTestFileCreated', [newName]) - // } - // } - // }) - // }, null, true) - } - const containerRef = useRef(null) const [state, setState] = useState({ focusElement: [], @@ -102,23 +104,28 @@ export const FileExplorer = (props: FileExplorerProps) => { } ].filter(item => props.menuItems && props.menuItems.find((name) => { return name === item.action })), files: [], - actions: { - updateGist: () => {}, - uploadFile, - publishToGist, - createNewFile - }, + actions: {}, fileManager: null, - ctrlKey: false + tokenAccess: null, + ctrlKey: false, + newFileName: '' }) useEffect(() => { (async () => { + console.log('registry: ', registry) const fileManager = registry.get('filemanager').api + const config = registry.get('config').api + const tokenAccess = config.get('settings/gist-access-token').api const files = await fetchDirectoryContent(name) + const actions = { + updateGist: () => {}, + uploadFile, + publishToGist + } setState(prevState => { - return { ...prevState, fileManager, files } + return { ...prevState, fileManager, tokenAccess, files, actions } }) })() }, []) @@ -150,22 +157,6 @@ export const FileExplorer = (props: FileExplorerProps) => { }) } - const label = (data) => { - return ( -
- - { data.path.split('/').pop() } - -
- ) - } - const normalize = (path, filesList): File[] => { const folders = [] const files = [] @@ -198,100 +189,147 @@ export const FileExplorer = (props: FileExplorerProps) => { return keyPath[keyPath.length - 1] } - const toGist = (id) => { - // const proccedResult = function (error, data) { - // if (error) { - // modalDialogCustom.alert('Failed to manage gist: ' + error) - // console.log('Failed to manage gist: ' + error) - // } else { - // if (data.html_url) { - // modalDialogCustom.confirm('Gist is ready', `The gist is at ${data.html_url}. Would you like to open it in a new window?`, () => { - // window.open(data.html_url, '_blank') - // }) - // } else { - // modalDialogCustom.alert(data.message + ' ' + data.documentation_url + ' ' + JSON.stringify(data.errors, null, '\t')) - // } - // } - // } + const createNewFile = (parentFolder = 'browser') => { + // const self = this + // modalDialogCustom.prompt('Create new file', 'File Name (e.g Untitled.sol)', 'Untitled.sol', (input) => { + // if (!input) input = 'New file' + // get filename from state (state.newFileName) + const fileManager = state.fileManager + const newFileName = parentFolder + '/' + 'unnamed' + Math.floor(Math.random() * 101) + + helper.createNonClashingName(newFileName, files, async (error, newName) => { + // if (error) return tooltip('Failed to create file ' + newName + ' ' + error) + if (error) return + const createFile = await fileManager.writeFile(newName, '') + + if (!createFile) { + // tooltip('Failed to create file ' + newName) + } else { + if (parentFolder === name) { + // const updatedFiles = await resolveDirectory(parentFolder, state.files) - // /** - // * This function is to get the original content of given gist - // * @params id is the gist id to fetch - // */ - // async function getOriginalFiles (id) { - // if (!id) { - // return [] - // } - - // const url = `https://api.github.com/gists/${id}` - // const res = await fetch(url) - // const data = await res.json() - // return data.files || [] - // } - - // // If 'id' is not defined, it is not a gist update but a creation so we have to take the files from the browser explorer. - // const folder = id ? 'browser/gists/' + id : 'browser/' - // this.packageFiles(this.files, folder, (error, packaged) => { - // if (error) { - // console.log(error) - // modalDialogCustom.alert('Failed to create gist: ' + error.message) - // } else { - // // check for token - // var tokenAccess = this._deps.config.get('settings/gist-access-token') - // if (!tokenAccess) { - // modalDialogCustom.alert( - // 'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.' - // ) - // } else { - // const description = 'Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=' + - // queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&runs=' + queryParams.get().runs + '&gist=' - // const gists = new Gists({ token: tokenAccess }) - - // if (id) { - // const originalFileList = getOriginalFiles(id) - // // Telling the GIST API to remove files - // const updatedFileList = Object.keys(packaged) - // const allItems = Object.keys(originalFileList) - // .filter(fileName => updatedFileList.indexOf(fileName) === -1) - // .reduce((acc, deleteFileName) => ({ - // ...acc, - // [deleteFileName]: null - // }), originalFileList) - // // adding new files - // updatedFileList.forEach((file) => { - // const _items = file.split('/') - // const _fileName = _items[_items.length - 1] - // allItems[_fileName] = packaged[file] - // }) - - // tooltip('Saving gist (' + id + ') ...') - // gists.edit({ - // description: description, - // public: true, - // files: allItems, - // id: id - // }, (error, result) => { - // proccedResult(error, result) - // if (!error) { - // for (const key in allItems) { - // if (allItems[key] === null) delete allItems[key] - // } - // } - // }) - // } else { - // // id is not existing, need to create a new gist - // tooltip('Creating a new gist ...') - // gists.create({ - // description: description, - // public: true, - // files: packaged - // }, (error, result) => { - // proccedResult(error, result) - // }) - // } - // } - // } - // }) + setState(prevState => { + return { + ...prevState, + files: [...prevState.files, { + path: newFileName, + name: extractNameFromKey(newFileName), + isDirectory: false + }] + } + }) + } + await fileManager.open(newName) + if (newName.includes('_test.sol')) { + plugin.events.trigger('newTestFileCreated', [newName]) + } + } + }) + // }, null, true) + } + + const publishToGist = () => { + // modalDialogCustom.confirm( + // 'Create a public gist', + // 'Are you sure you want to publish all your files in browser directory anonymously as a public gist on github.com? Note: this will not include directories.', + // () => { this.toGist() } + toGist() + // ) + } + + const toGist = (id?: string) => { + const proccedResult = function (error, data) { + if (error) { + // modalDialogCustom.alert('Failed to manage gist: ' + error) + console.log('Failed to manage gist: ' + error) + } else { + if (data.html_url) { + // modalDialogCustom.confirm('Gist is ready', `The gist is at ${data.html_url}. Would you like to open it in a new window?`, () => { + // window.open(data.html_url, '_blank') + // }) + } else { + // modalDialogCustom.alert(data.message + ' ' + data.documentation_url + ' ' + JSON.stringify(data.errors, null, '\t')) + } + } + } + + /** + * This function is to get the original content of given gist + * @params id is the gist id to fetch + */ + async function getOriginalFiles (id) { + if (!id) { + return [] + } + + const url = `https://api.github.com/gists/${id}` + const res = await fetch(url) + const data = await res.json() + return data.files || [] + } + + // If 'id' is not defined, it is not a gist update but a creation so we have to take the files from the browser explorer. + const folder = id ? 'browser/gists/' + id : 'browser/' + packageFiles(files, folder, (error, packaged) => { + if (error) { + console.log(error) + // modalDialogCustom.alert('Failed to create gist: ' + error.message) + } else { + // check for token + if (!state.tokenAccess) { + // modalDialogCustom.alert( + // 'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.' + // ) + } else { + const description = 'Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=' + + queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&runs=' + queryParams.get().runs + '&gist=' + const gists = new Gists({ token: state.tokenAccess }) + + if (id) { + const originalFileList = getOriginalFiles(id) + // Telling the GIST API to remove files + const updatedFileList = Object.keys(packaged) + const allItems = Object.keys(originalFileList) + .filter(fileName => updatedFileList.indexOf(fileName) === -1) + .reduce((acc, deleteFileName) => ({ + ...acc, + [deleteFileName]: null + }), originalFileList) + // adding new files + updatedFileList.forEach((file) => { + const _items = file.split('/') + const _fileName = _items[_items.length - 1] + allItems[_fileName] = packaged[file] + }) + + // tooltip('Saving gist (' + id + ') ...') + gists.edit({ + description: description, + public: true, + files: allItems, + id: id + }, (error, result) => { + proccedResult(error, result) + if (!error) { + for (const key in allItems) { + if (allItems[key] === null) delete allItems[key] + } + } + }) + } else { + // id is not existing, need to create a new gist + // tooltip('Creating a new gist ...') + gists.create({ + description: description, + public: true, + files: packaged + }, (error, result) => { + proccedResult(error, result) + }) + } + } + } + }) } // self._components = {} @@ -359,6 +397,26 @@ export const FileExplorer = (props: FileExplorerProps) => { // } // } + const label = (data) => { + return ( +
+ + { data.path.split('/').pop() } + +
+ ) + } + + const onDragEnd = result => { + + } + const handleClickFile = (path) => { state.fileManager.open(path) setState(prevState => { @@ -414,7 +472,7 @@ export const FileExplorer = (props: FileExplorerProps) => { data-id={'fileExplorerNewFile' + action} onClick={(e) => { e.stopPropagation() - state.actions[action]() + action === 'createNewFile' ? createNewFile() : state.actions[action]() }} className={'newFile ' + icon + ' remixui_newFile'} title={title} @@ -436,45 +494,55 @@ export const FileExplorer = (props: FileExplorerProps) => { const renderFiles = (file, index) => { if (file.isDirectory) { return ( - - { - e.stopPropagation() - handleClickFolder(file.path) - }} - labelClass={ state.focusElement.findIndex(item => item === file.path) !== -1 ? 'bg-secondary' : '' } - controlBehaviour={ state.ctrlKey } - > - { - file.child ? { - file.child.map((file, index) => { - return renderFiles(file, index) - }) + + {(provided) => ( + { + e.stopPropagation() + handleClickFolder(file.path) + }} + labelClass={ state.focusElement.findIndex(item => item === file.path) !== -1 ? 'bg-secondary' : '' } + controlBehaviour={ state.ctrlKey } + > + { + file.child ? { + file.child.map((file, index) => { + return renderFiles(file, index) + }) + } + : } - : - } - - + { provided.placeholder } + + )} + ) } else { return ( - - { - e.stopPropagation() - handleClickFile(file.path) - }} - icon='fa fa-file' - labelClass={ state.focusElement.findIndex(item => item === file.path) !== -1 ? 'bg-secondary' : '' } - /> + + {(provided) => ( + { + e.stopPropagation() + handleClickFile(file.path) + }} + icon='fa fa-file' + labelClass={ state.focusElement.findIndex(item => item === file.path) !== -1 ? 'bg-secondary' : '' } + /> + )} ) } @@ -486,14 +554,12 @@ export const FileExplorer = (props: FileExplorerProps) => { tabIndex={-1} onKeyDown={(e) => { if (e.shiftKey) { - console.log('TRUE') setState(prevState => { return { ...prevState, ctrlKey: true } }) } }} onKeyUp={() => { - console.log('FALSE') setState(prevState => { return { ...prevState, ctrlKey: false } }) @@ -501,17 +567,24 @@ export const FileExplorer = (props: FileExplorerProps) => { > - false}> -
- - { - state.files.map((file, index) => { - return renderFiles(file, index) - }) - } - -
-
+ + + {(provided) => ( +
+ + { + state.files.map((file, index) => { + return renderFiles(file, index) + }) + } + + { provided.placeholder } +
+ )} +
+
diff --git a/libs/remix-ui/tree-view/src/lib/tree-view-item/tree-view-item.tsx b/libs/remix-ui/tree-view/src/lib/tree-view-item/tree-view-item.tsx index b3aa60cd9f..c570de5f07 100644 --- a/libs/remix-ui/tree-view/src/lib/tree-view-item/tree-view-item.tsx +++ b/libs/remix-ui/tree-view/src/lib/tree-view-item/tree-view-item.tsx @@ -4,7 +4,7 @@ import { TreeViewItemProps } from '../../types' import './tree-view-item.css' export const TreeViewItem = (props: TreeViewItemProps) => { - const { id, children, label, labelClass, expand, iconX = 'fas fa-caret-right', iconY = 'fas fa-caret-down', icon, controlBehaviour = false, ...otherProps } = props + const { id, children, label, labelClass, expand, iconX = 'fas fa-caret-right', iconY = 'fas fa-caret-down', icon, controlBehaviour = false, innerRef, ...otherProps } = props const [isExpanded, setIsExpanded] = useState(false) useEffect(() => { @@ -12,7 +12,7 @@ export const TreeViewItem = (props: TreeViewItemProps) => { }, [expand]) return ( -
  • +
  • !controlBehaviour && setIsExpanded(!isExpanded)}> { children ?
    : icon ?
    : null } diff --git a/libs/remix-ui/tree-view/src/types/index.ts b/libs/remix-ui/tree-view/src/types/index.ts index e3d54e71b3..cf8f850b52 100644 --- a/libs/remix-ui/tree-view/src/types/index.ts +++ b/libs/remix-ui/tree-view/src/types/index.ts @@ -15,4 +15,5 @@ export interface TreeViewItemProps { icon?: string, labelClass?: string, controlBehaviour?: boolean + innerRef?: any } diff --git a/package-lock.json b/package-lock.json index bb2393535c..e2dbf90fc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7597,6 +7597,15 @@ "csstype": "^2.2.0" } }, + "@types/react-beautiful-dnd": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz", + "integrity": "sha512-by80tJ8aTTDXT256Gl+RfLRtFjYbUWOnZuEigJgNsJrSEGxvFe5eY6k3g4VIvf0M/6+xoLgfYWoWonlOo6Wqdg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-dom": { "version": "16.9.4", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.4.tgz", @@ -13509,6 +13518,14 @@ "insert-css": "^0.2.0" } }, + "css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "requires": { + "tiny-invariant": "^1.0.6" + } + }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -20226,6 +20243,14 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -25592,6 +25617,11 @@ } } }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, "memory-fs": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", @@ -35427,6 +35457,11 @@ "integrity": "sha1-W4h48ROlgheEjGSCAmxz4bpXcn8=", "dev": true }, + "raf-schd": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz", + "integrity": "sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ==" + }, "ramda": { "version": "0.26.1", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", @@ -35534,6 +35569,20 @@ "prop-types": "^15.6.2" } }, + "react-beautiful-dnd": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz", + "integrity": "sha512-87It8sN0ineoC3nBW0SbQuTFXM6bUqM62uJGY4BtTf0yzPl8/3+bHMWkgIe0Z6m8e+gJgjWxefGRVfpE3VcdEg==", + "requires": { + "@babel/runtime": "^7.8.4", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.1.1", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + } + }, "react-bootstrap": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.3.0.tgz", @@ -35586,15 +35635,6 @@ "scheduler": "^0.19.1" } }, - "react-draggable": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz", - "integrity": "sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==", - "requires": { - "classnames": "^2.2.5", - "prop-types": "^15.6.0" - } - }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -35620,6 +35660,28 @@ "warning": "^4.0.3" } }, + "react-redux": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz", + "integrity": "sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA==", + "requires": { + "@babel/runtime": "^7.12.1", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, "react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", @@ -35904,6 +35966,15 @@ "esprima": "~4.0.0" } }, + "redux": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", + "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", + "requires": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + } + }, "regenerate": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", @@ -39146,8 +39217,7 @@ "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, "symbol-tree": { "version": "3.2.4", @@ -39735,6 +39805,11 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, + "tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -40752,6 +40827,11 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, + "use-memo-one": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.1.tgz", + "integrity": "sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ==" + }, "utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", diff --git a/package.json b/package.json index 50a14088df..e232e3750a 100644 --- a/package.json +++ b/package.json @@ -153,9 +153,9 @@ "merge": "^1.2.0", "npm-install-version": "^6.0.2", "react": "16.13.1", + "react-beautiful-dnd": "^13.0.0", "react-bootstrap": "^1.3.0", "react-dom": "16.13.1", - "react-draggable": "^4.4.3", "signale": "^1.4.0", "time-stamp": "^2.2.0", "winston": "^3.3.3", @@ -191,6 +191,7 @@ "@types/nightwatch": "^1.1.6", "@types/node": "~8.9.4", "@types/react": "16.9.17", + "@types/react-beautiful-dnd": "^13.0.0", "@types/react-dom": "16.9.4", "@types/react-router-dom": "5.1.3", "@types/ws": "^7.2.4",