move workspace to a react component

pull/924/head
yann300 4 years ago
parent 57c55f0f7f
commit dc57090e52
  1. 1
      apps/remix-ide/src/app.js
  2. 344
      apps/remix-ide/src/app/panels/file-panel.js
  3. 59
      apps/remix-ide/src/app/panels/styles/file-panel-styles.css
  4. 4
      libs/remix-ui/workspace/.babelrc
  5. 19
      libs/remix-ui/workspace/.eslintrc
  6. 7
      libs/remix-ui/workspace/README.md
  7. 1
      libs/remix-ui/workspace/src/index.ts
  8. 38
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.css
  9. 432
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  10. 16
      libs/remix-ui/workspace/tsconfig.json
  11. 13
      libs/remix-ui/workspace/tsconfig.lib.json
  12. 3
      nx.json
  13. 7
      tsconfig.json
  14. 22
      workspace.json

@ -486,6 +486,5 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
migrateToWorkspace(fileManager)
filePanel.initWorkspace()
if (params.embed) framingService.embed()
}

@ -3,10 +3,7 @@ import { ViewPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom'
import { FileExplorer } from '@remix-ui/file-explorer' // eslint-disable-line
import './styles/file-panel-styles.css'
var ethutil = require('ethereumjs-util')
var yo = require('yo-yo')
import { Workspace } from '@remix-ui/workspace' // eslint-disable-line
var EventManager = require('../../lib/events')
var { RemixdHandle } = require('../files/remixd-handle.js')
var { GitHandle } = require('../files/git-handle.js')
@ -14,9 +11,6 @@ var globalRegistry = require('../../global/registry')
var examples = require('../editor/examples')
var GistHandler = require('../../lib/gist-handler')
var QueryParams = require('../../lib/query-params')
const modalDialog = require('../ui/modal-dialog-custom')
var canUpload = window.File || window.FileReader || window.FileList || window.Blob
/*
Overview of APIs:
@ -51,328 +45,86 @@ const profile = {
module.exports = class Filepanel extends ViewPlugin {
constructor (appManager) {
super(profile)
this.event = new EventManager()
this._components = {}
this._components.registry = globalRegistry
this._deps = {
fileProviders: this._components.registry.get('fileproviders').api,
fileManager: this._components.registry.get('filemanager').api,
config: this._components.registry.get('config').api
fileManager: this._components.registry.get('filemanager').api
}
this.LOCALHOST = ' - connect to localhost - '
this.NO_WORKSPACE = ' - none - '
this.hideRemixdExplorer = true
this.remixdExplorer = {
hide: () => {
if (this.currentWorkspace === this.LOCALHOST) this.setWorkspace(this.NO_WORKSPACE)
this._deps.fileManager.setMode('browser')
this.hideRemixdExplorer = true
this.renderComponent()
},
show: () => {
this._deps.fileManager.setMode('localhost')
this.hideRemixdExplorer = false
this.renderComponent()
}
}
this.reset = false
this.registeredMenuItems = []
this.displayNewFile = false
this.uploadFileEvent = null
this.el = yo`
<div id="fileExplorerView">
</div>
`
this.el = document.createElement('div')
this.el.setAttribute('id', 'fileExplorerView')
this.remixdHandle = new RemixdHandle(this.remixdExplorer, this._deps.fileProviders.localhost, appManager)
this.gitHandle = new GitHandle()
this.event = new EventManager()
this._deps.fileProviders.localhost.event.register('connecting', (event) => {
})
this._deps.fileProviders.localhost.event.register('connected', (event) => {
this.remixdExplorer.show()
})
this._deps.fileProviders.localhost.event.register('errored', (event) => {
this.remixdExplorer.hide()
})
this._deps.fileProviders.localhost.event.register('closed', (event) => {
this.remixdExplorer.hide()
})
this.currentWorkspace = null
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
this._deps.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, fileTree) => {
if (error) return console.error(error)
this.setWorkspace(Object.keys(fileTree)[0].replace(workspacesPath + '/', ''))
})
this.renderComponent()
}
async initWorkspace () {
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
const queryParams = new QueryParams()
const params = queryParams.get()
// get the file from gist
const gistHandler = new GistHandler()
const loadedFromGist = gistHandler.loadFromGist(params, this._deps.fileManager)
if (loadedFromGist) return
if (params.code) {
try {
await this._deps.fileManager.createWorkspace('code-sample')
var hash = ethutil.bufferToHex(ethutil.keccak(params.code))
const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol'
const path = 'browser/' + workspacesPath + '/code-sample/' + fileName
await this._deps.fileManager.writeFile(path, atob(params.code))
this.setWorkspace('code-sample')
await this._deps.fileManager.openFile(path)
} catch (e) {
console.error(e)
}
return
}
// insert example contracts if there are no files to show
this._deps.fileProviders.browser.resolveDirectory('/', async (error, filesList) => {
if (error) console.error(error)
if (Object.keys(filesList).length === 0) {
for (const file in examples) {
await this._deps.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + examples[file].name, examples[file].content)
}
this.setWorkspace('default_workspace')
}
})
}
async refreshWorkspacesList () {
if (!document.getElementById('workspacesSelect')) return
const workspaces = await this.getWorkspaces()
workspaces.push(this.LOCALHOST)
workspaces.push(this.NO_WORKSPACE)
this.request = {}
ReactDOM.render(
(
workspaces
.map((folder) => {
return <option selected={this.currentWorkspace === folder} value={folder}>{folder}</option>
})), document.getElementById('workspacesSelect')
)
}
resetFocus (value) {
this.reset = value
this.renderComponent()
}
createNewFile () {
this.displayNewFile = true
this.renderComponent()
<Workspace
setWorkspace={this.setWorkspace.bind(this)}
renameWorkspace={this.renameWorkspace.bind(this)}
deleteWorkspace={this.deleteWorkspace.bind(this)}
createWorkspace={this.createWorkspace.bind(this)}
workspace={this._deps.fileProviders.workspace}
browser={this._deps.fileProviders.browser}
localhost={this._deps.fileProviders.localhost}
fileManager={this._deps.fileManager}
examples={examples}
queryParams={new QueryParams()}
gistHandler={new GistHandler()}
registry={this._components.registry}
plugin={this}
request={this.request}
/>
, this.el)
}
resetNewFile () {
this.displayNewFile = false
this.renderComponent()
render () {
return this.el
}
uploadFile (target) {
this.uploadFileEvent = target
this.renderComponent()
async getCurrentWorkspace () {
return await this.request.getWorkspaces()
}
resetUploadFile () {
this.uploadFileEvent = null
this.renderComponent()
async getWorkspaces () {
return await this.request.getWorkspaces()
}
render () {
return this.el
async createNewFile () {
return await this.request.createNewFile()
}
getWorkspaces () {
return new Promise((resolve, reject) => {
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
this._deps.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, items) => {
if (error) return reject(error)
resolve(Object.keys(items)
.filter((item) => items[item].isDirectory)
.map((folder) => folder.replace(workspacesPath + '/', '')))
})
})
async uploadFile () {
return await this.request.uploadFile()
}
getCurrentWorkspace () {
return this.currentWorkspace
async createWorkspace () {
return await this.request.createWorkspace()
}
async setWorkspace (name) {
/** these are called by the react component, action is already finished whent it's called */
async setWorkspace (workspace) {
this._deps.fileManager.removeTabsOf(this._deps.fileProviders.workspace)
this.currentWorkspace = name
if (name === this.LOCALHOST) {
this._deps.fileProviders.workspace.clearWorkspace()
if (workspace.isLocalhost) {
this.call('manager', 'activatePlugin', 'remixd')
} else if (name === this.NO_WORKSPACE) {
this._deps.fileProviders.workspace.clearWorkspace()
} else {
this._deps.fileProviders.workspace.setWorkspace(name)
}
if (name !== this.LOCALHOST && await this.call('manager', 'isActive', 'remixd')) {
} else if (await this.call('manager', 'isActive', 'remixd')) {
this.call('manager', 'deactivatePlugin', 'remixd')
}
this.renderComponent()
this.emit('setWorkspace', { name })
}
/**
*
* @param item { id: string, name: string, type?: string[], path?: string[], extension?: string[], pattern?: string[] }
* @param callback (...args) => void
*/
registerContextMenuItem (item) {
if (!item) throw new Error('Invalid register context menu argument')
if (!item.name || !item.id) throw new Error('Item name and id is mandatory')
if (!item.type && !item.path && !item.extension && !item.pattern) throw new Error('Invalid file matching criteria provided')
this.registeredMenuItems = [...this.registeredMenuItems, item]
this.renderComponent()
this.emit('setWorkspace', workspace)
}
renameWorkspace () {
modalDialog.prompt('Rename Workspace', 'Please choose a name for the workspace', this.currentWorkspace, async (value) => {
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
await this._deps.fileManager.rename('browser/' + workspacesPath + '/' + this.currentWorkspace, 'browser/' + workspacesPath + '/' + value)
this.setWorkspace(value)
this.emit('renameWorkspace', { name: value })
})
renameWorkspace (workspace) {
this.emit('renameWorkspace', workspace)
}
createWorkspace () {
return new Promise((resolve, reject) => {
const workspace = `workspace_${Date.now()}`
modalDialog.prompt('New Workspace', 'Please choose a name for the workspace', workspace, (value) => {
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
this._deps.fileProviders.browser.createDir(workspacesPath + '/' + value, async () => {
this.setWorkspace(value)
for (const file in examples) {
await this._deps.fileManager.writeFile(`${examples[file].name}`, examples[file].content)
}
resolve(value)
})
}, () => reject(new Error('workspace creation rejected by user')))
})
deleteWorkspace (workspace) {
this.emit('deleteWorkspace', workspace)
}
deleteCurrentWorkspace () {
if (!this.currentWorkspace) return
modalDialog.confirm('Delete Workspace', 'Please confirm workspace deletion', () => {
const workspacesPath = this._deps.fileProviders.workspace.workspacesPath
this._deps.fileProviders.browser.remove(workspacesPath + '/' + this.currentWorkspace)
const name = this.currentWorkspace
this.currentWorkspace = null
this.setWorkspace(this.NO_WORKSPACE)
this.renderComponent()
this.emit('deleteWorkspace', { name })
})
}
renderComponent () {
ReactDOM.render(
<div className='remixui_container'>
<div className='remixui_fileexplorer' onClick={() => this.resetFocus(true)}>
<div>
<header>
<div className="mb-2">
<label className="form-check-label" htmlFor="workspacesSelect">
Workspaces
</label>
<span className="remixui_menu">
<span
id='workspaceCreate'
data-id='workspaceCreate'
onClick={(e) => {
e.stopPropagation()
this.createWorkspace()
}}
className='far fa-plus-square remixui_menuicon'
title='Create a new Workspace'>
</span>
<span
hidden={this.currentWorkspace === this.LOCALHOST || this.currentWorkspace === this.NO_WORKSPACE}
id='workspaceRename'
data-id='workspaceRename'
onClick={(e) => {
e.stopPropagation()
this.renameWorkspace()
}}
className='far fa-edit remixui_menuicon'
title='Rename current Workspace'>
</span>
<span
hidden={this.currentWorkspace === this.LOCALHOST || this.currentWorkspace === this.NO_WORKSPACE}
id='workspaceDelete'
data-id='workspaceDelete'
onClick={(e) => {
e.stopPropagation()
this.deleteCurrentWorkspace()
}}
className='fas fa-trash'
title='Delete current Workspace'>
</span>
</span>
<select id="workspacesSelect" data-id="workspacesSelect" onChange={(e) => this.setWorkspace(e.target.value)} className="form-control custom-select">
</select>
</div>
</header>
</div>
<div className='remixui_fileExplorerTree'>
<div>
<div className='pl-2 remixui_treeview' data-id='filePanelFileExplorerTree'>
{ this.hideRemixdExplorer && this.currentWorkspace && this.currentWorkspace !== this.NO_WORKSPACE &&
<FileExplorer
name={this.currentWorkspace}
registry={this._components.registry}
filesProvider={this._deps.fileProviders.workspace}
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
plugin={this}
focusRoot={this.reset}
contextMenuItems={this.registeredMenuItems}
/>
}
</div>
<div className='pl-2 filesystemexplorer remixui_treeview'>
{ !this.hideRemixdExplorer &&
<FileExplorer
name='localhost'
registry={this._components.registry}
filesProvider={this._deps.fileProviders.localhost}
menuItems={['createNewFile', 'createNewFolder']}
plugin={this}
focusRoot={this.reset}
contextMenuItems={this.registeredMenuItems}
/>
}
</div>
<div className='pl-2 remixui_treeview'>
{ false && <FileExplorer
name='browser'
registry={this._components.registry}
filesProvider={this._deps.fileProviders.browser}
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
plugin={this}
focusRoot={this.reset}
contextMenuItems={this.registeredMenuItems}
displayInput={this.displayNewFile}
externalUploads={this.uploadFileEvent}
/>
}
</div>
</div>
</div>
</div>
</div>
, this.el)
setTimeout(() => {
this.refreshWorkspacesList()
}, 500)
createWorkspace (workspace) {
this.emit('createWorkspace', workspace)
}
/** end section */
}

@ -1,59 +0,0 @@
.remixui_container {
display : flex;
flex-direction : row;
width : 100%;
height : 100%;
box-sizing : border-box;
}
.remixui_fileexplorer {
display : flex;
flex-direction : column;
position : relative;
width : 100%;
padding-left : 6px;
padding-top : 6px;
}
.remixui_fileExplorerTree {
cursor : default;
}
.remixui_gist {
padding : 10px;
}
.remixui_gist i {
cursor : pointer;
}
.remixui_gist i:hover {
color : orange;
}
.remixui_connectToLocalhost {
padding : 10px;
}
.remixui_connectToLocalhost i {
cursor : pointer;
}
.remixui_connectToLocalhost i:hover {
color : var(--secondary)
}
.remixui_uploadFile {
padding : 10px;
}
.remixui_uploadFile label:hover {
color : var(--secondary)
}
.remixui_uploadFile label {
cursor : pointer;
}
.remixui_treeview {
overflow-y : auto;
}
.remixui_dialog {
display: flex;
flex-direction: column;
}
.remixui_dialogParagraph {
margin-bottom: 2em;
word-break: break-word;
}
.remixui_menuicon {
padding-right : 10px;
}

@ -0,0 +1,4 @@
{
"presets": ["@nrwl/react/babel"],
"plugins": []
}

@ -0,0 +1,19 @@
{
"env": {
"browser": true,
"es6": true
},
"extends": "../../../.eslintrc",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error"
}
}

@ -0,0 +1,7 @@
# remix-ui-workspace
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test remix-ui-workspace` to execute the unit tests via [Jest](https://jestjs.io).

@ -0,0 +1 @@
export * from './lib/remix-ui-workspace';

@ -1,14 +1,11 @@
var csjs = require('csjs-inject')
var css = csjs`
.container {
.remixui_container {
display : flex;
flex-direction : row;
width : 100%;
height : 100%;
box-sizing : border-box;
}
.fileexplorer {
.remixui_fileexplorer {
display : flex;
flex-direction : column;
position : relative;
@ -16,47 +13,48 @@ var css = csjs`
padding-left : 6px;
padding-top : 6px;
}
.fileExplorerTree {
.remixui_fileExplorerTree {
cursor : default;
}
.gist {
.remixui_gist {
padding : 10px;
}
.gist i {
.remixui_gist i {
cursor : pointer;
}
.gist i:hover {
.remixui_gist i:hover {
color : orange;
}
.connectToLocalhost {
.remixui_connectToLocalhost {
padding : 10px;
}
.connectToLocalhost i {
.remixui_connectToLocalhost i {
cursor : pointer;
}
.connectToLocalhost i:hover {
.remixui_connectToLocalhost i:hover {
color : var(--secondary)
}
.uploadFile {
.remixui_uploadFile {
padding : 10px;
}
.uploadFile label:hover {
.remixui_uploadFile label:hover {
color : var(--secondary)
}
.uploadFile label {
.remixui_uploadFile label {
cursor : pointer;
}
.treeview {
.remixui_treeview {
overflow-y : auto;
}
.dialog {
.remixui_dialog {
display: flex;
flex-direction: column;
}
.dialogParagraph {
.remixui_dialogParagraph {
margin-bottom: 2em;
word-break: break-word;
}
`
.remixui_menuicon {
padding-right : 10px;
}
module.exports = css

@ -0,0 +1,432 @@
import React, { useState, useEffect } from 'react';
import ethutil from 'ethereumjs-util'
import { FileExplorer } from '@remix-ui/file-explorer' // eslint-disable-line
import './remix-ui-workspace.css';
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
type CodeExamples = {
[key: string]: { name: string, content: string }
};
/* eslint-disable-next-line */
export interface WorkspaceProps {
setWorkspace: ({ name: string, isLocalhost: boolean }) => void,
renameWorkspace: ({ name: string }) => void,
createWorkspace: ({ name: string }) => void,
deleteWorkspace: ({ name: string }) => void,
workspace: any // workspace provider,
browser: any // browser provider
localhost: any // localhost provider
fileManager : any
examples: CodeExamples,
queryParams: any // URL parameters
gistHandler: any // handle load gist
registry: any // registry
plugin: any // plugin call and resetFocus
request: any // api request
}
var canUpload = window.File || window.FileReader || window.FileList || window.Blob
export const Workspace = (props: WorkspaceProps) => {
const LOCALHOST = ' - connect to localhost - '
const NO_WORKSPACE = ' - none - '
props.request.createWorkspace = () => {
createWorkspace()
}
props.request.getWorkspaces = () => {
return getWorkspaces()
}
props.request.createNewFile = () => {
setState(prevState => {
return { ...prevState, displayNewFile: true }
})
}
props.request.uploadFile = (target) => {
setState(prevState => {
return { ...prevState, uploadFileEvent: target }
})
}
props.request.getCurrentWorkspace = () => {
return state.currentWorkspace
}
useEffect(() => {
initWorkspace()
}, [])
const handleHideCreateModal = () => {
setState(prevState => {
state.createModal.hide = true
return { ...prevState, ...state.createModal }
})
}
const handleHideDeleteModal = () => {
setState(prevState => {
state.deleteModal.hide = true
return { ...prevState, ...state.deleteModal }
})
}
const handleHideRenameModal = () => {
setState(prevState => {
state.renameModal.hide = true
return { ...prevState, ...state.renameModal }
})
}
const createWorkspace = () => {
setState(prevState => {
state.createModal.hide = false
return { ...prevState, ...state.createModal }
})
}
const renameCurrentWorkspace = () => {
setState(prevState => {
state.renameModal.hide = false
return { ...prevState, ...state.renameModal }
})
}
const deleteCurrentWorkspace = () => {
setState(prevState => {
state.deleteModal.hide = false
return { ...prevState, ...state.deleteModal }
})
}
const [state, setState] = useState({
workspaces: [],
reset: false,
currentWorkspace: NO_WORKSPACE,
hideRemixdExplorer: true,
registeredMenuItems: [],
displayNewFile: false,
externalUploads: null,
uploadFileEvent: null,
renameModal: {
id: 'renameWorkspace',
hide: true,
title: 'Rename Workspace',
message: 'Please choose a name for the workspace',
ok: {
label: '',
fn: () => onFinishRenameWorkspace()
},
cancel: {
label: '',
fn: () => {}
},
handleHide: handleHideRenameModal
},
deleteModal: {
id: 'deleteWorkspace',
hide: true,
title: 'Remove Workspace',
message: 'Please confirm workspace deletion',
ok: {
label: '',
fn: () => onFinishDeleteWorkspace()
},
cancel: {
label: '',
fn: () => {}
},
handleHide: handleHideDeleteModal
},
createModal: {
id: 'createWorkspace',
hide: true,
title: 'Create Workspace',
message: 'Please choose a name for the workspace',
ok: {
label: '',
fn: () => onFinishCreateWorkspace()
},
cancel: {
label: '',
fn: () => {}
},
handleHide: handleHideCreateModal
}
})
let worspaceNewName // used for renaming and creation
const onFinishRenameWorkspace = async () => {
const workspacesPath = props.workspace.workspacesPath
await props.fileManager.rename('browser/' + workspacesPath + '/' + state.currentWorkspace, 'browser/' + workspacesPath + '/' + worspaceNewName)
setWorkspace(worspaceNewName)
worspaceNewName = ''
props.renameWorkspace({ name: state.currentWorkspace })
}
const onFinishCreateWorkspace = async () => {
const workspacesPath = props.workspace.workspacesPath
props.browser.createDir(workspacesPath + '/' + worspaceNewName, async () => {
setWorkspace(worspaceNewName)
for (const file in props.examples) {
await props.fileManager.writeFile(`${props.examples[file].name}`, props.examples[file].content)
}
worspaceNewName = ''
props.createWorkspace({ name: state.currentWorkspace })
})
}
const onFinishDeleteWorkspace = async () => {
const workspacesPath = props.workspace.workspacesPath
props.browser.remove(workspacesPath + '/' + state.currentWorkspace)
const name = state.currentWorkspace
setWorkspace(NO_WORKSPACE)
props.deleteWorkspace({ name })
}
const resetFocus = (reset) => {
/*setState(prevState => {
return { ...prevState, reset }
})*/
}
const setWorkspace = async (name) => {
if (name === LOCALHOST) {
props.workspace.clearWorkspace()
} else if (name === NO_WORKSPACE) {
props.workspace.clearWorkspace()
} else {
props.workspace.setWorkspace(name)
}
const workspaces = await getWorkspaces()
setState(prevState => {
return { ...prevState, workspaces, currentWorkspace: name }
})
props.setWorkspace({ name, isLocalhost: name === LOCALHOST })
}
const initWorkspace = async () => {
const workspacesPath = props.workspace.workspacesPath
const params = props.queryParams.get()
// get the file from gist
const loadedFromGist = props.gistHandler.loadFromGist(params, props.fileManager)
if (loadedFromGist) return
if (params.code) {
try {
await props.fileManager.createWorkspace('code-sample')
var hash = ethutil.bufferToHex(ethutil.keccak(params.code))
const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol'
const path = 'browser/' + workspacesPath + '/code-sample/' + fileName
await props.fileManager.writeFile(path, atob(params.code))
setWorkspace('code-sample')
await props.fileManager.openFile(path)
} catch (e) {
console.error(e)
}
return
}
// insert example contracts if there are no files to show
props.browser.resolveDirectory('/', async (error, filesList) => {
if (error) console.error(error)
if (Object.keys(filesList).length === 0) {
for (const file in props.examples) {
await props.fileManager.writeFile('browser/' + workspacesPath + '/default_workspace/' + props.examples[file].name, props.examples[file].content)
}
setWorkspace('default_workspace')
} else {
// we've already got some workspaces
const workspaces = await getWorkspaces()
if (workspaces.length) setWorkspace(workspaces[0])
else setWorkspace(NO_WORKSPACE)
}
})
}
const getWorkspaces = (): any => {
return new Promise((resolve, reject) => {
const workspacesPath = props.workspace.workspacesPath
props.browser.resolveDirectory('/' + workspacesPath, (error, items) => {
if (error) return reject(error)
resolve(Object.keys(items)
.filter((item) => items[item].isDirectory)
.map((folder) => folder.replace(workspacesPath + '/', '')))
})
})
}
const remixdExplorer = {
hide: () => {
if (state.currentWorkspace === LOCALHOST) setWorkspace(NO_WORKSPACE)
props.fileManager.setMode('browser')
setState(prevState => {
return { ...prevState, hideRemixdExplorer: true }
})
},
show: () => {
props.fileManager.setMode('localhost')
setState(prevState => {
return { ...prevState, hideRemixdExplorer: false }
})
}
}
props.localhost.event.register('connecting', (event) => {})
props.localhost.event.register('connected', (event) => {
remixdExplorer.show()
})
props.localhost.event.register('errored', (event) => {
remixdExplorer.hide()
})
props.localhost.event.register('closed', (event) => {
remixdExplorer.hide()
})
props.plugin.resetFocus = () => {
setState(prevState => {
return { ...prevState, reset: true }
})
}
return (
<div className='remixui_container'>
<ModalDialog
id={ state.renameModal.id }
title={ state.renameModal.title }
message={ state.renameModal.message }
hide={ state.renameModal.hide }
ok={ state.renameModal.ok }
cancel={ state.renameModal.cancel }
handleHide={ handleHideRenameModal }>
<input placeholder={ state.currentWorkspace } onChange={(e) => { worspaceNewName = e.target.value } }/>
</ModalDialog>
<ModalDialog
id={ state.createModal.id }
title={ state.createModal.title }
message={ state.createModal.message }
hide={ state.createModal.hide }
ok={ state.createModal.ok }
cancel={ state.createModal.cancel }
handleHide={ handleHideCreateModal }>
<input placeholder={ `workspace_${Date.now()}` } onChange={(e) => { worspaceNewName = e.target.value } }/>
</ModalDialog>
<ModalDialog
id={ state.deleteModal.id }
title={ state.deleteModal.title }
message={ state.deleteModal.message }
hide={ state.deleteModal.hide }
ok={ state.deleteModal.ok }
cancel={ state.deleteModal.cancel }
handleHide={ handleHideDeleteModal }>
</ModalDialog>
<div className='remixui_fileexplorer' onClick={() => resetFocus(true)}>
<div>
<header>
<div className="mb-2">
<label className="form-check-label" htmlFor="workspacesSelect">
Workspaces
</label>
<span className="remixui_menu">
<span
id='workspaceCreate'
data-id='workspaceCreate'
onClick={(e) => {
e.stopPropagation()
createWorkspace()
}}
className='far fa-plus-square remixui_menuicon'
title='Create a new Workspace'>
</span>
<span
hidden={state.currentWorkspace === LOCALHOST || state.currentWorkspace === NO_WORKSPACE}
id='workspaceRename'
data-id='workspaceRename'
onClick={(e) => {
e.stopPropagation()
renameCurrentWorkspace()
}}
className='far fa-edit remixui_menuicon'
title='Rename current Workspace'>
</span>
<span
hidden={state.currentWorkspace === LOCALHOST || state.currentWorkspace === NO_WORKSPACE}
id='workspaceDelete'
data-id='workspaceDelete'
onClick={(e) => {
e.stopPropagation()
deleteCurrentWorkspace()
}}
className='fas fa-trash'
title='Delete current Workspace'>
</span>
</span>
<select id="workspacesSelect" data-id="workspacesSelect" onChange={(e) => setWorkspace(e.target.value)} className="form-control custom-select">
{
state.workspaces
.map((folder) => {
return <option selected={state.currentWorkspace === folder} value={folder}>{folder}</option>
})
}
<option selected={state.currentWorkspace === LOCALHOST} value={LOCALHOST}>{LOCALHOST}</option>
<option selected={state.currentWorkspace === NO_WORKSPACE} value={NO_WORKSPACE}>{NO_WORKSPACE}</option>
</select>
</div>
</header>
</div>
<div className='remixui_fileExplorerTree'>
<div>
<div className='pl-2 remixui_treeview' data-id='filePanelFileExplorerTree'>
{ state.hideRemixdExplorer && state.currentWorkspace && state.currentWorkspace !== NO_WORKSPACE &&
<FileExplorer
name={state.currentWorkspace}
registry={props.registry}
filesProvider={props.workspace}
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
plugin={props.plugin}
focusRoot={state.reset}
contextMenuItems={state.registeredMenuItems}
displayInput={state.displayNewFile}
externalUploads={state.uploadFileEvent}
/>
}
</div>
<div className='pl-2 filesystemexplorer remixui_treeview'>
{ !state.hideRemixdExplorer &&
<FileExplorer
name='localhost'
registry={props.registry}
filesProvider={props.localhost}
menuItems={['createNewFile', 'createNewFolder']}
plugin={props.plugin}
focusRoot={state.reset}
contextMenuItems={state.registeredMenuItems}
/>
}
</div>
<div className='pl-2 remixui_treeview'>
{ false && <FileExplorer
name='browser'
registry={props.registry}
filesProvider={props.browser}
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
plugin={props.plugin}
focusRoot={state.reset}
contextMenuItems={state.registeredMenuItems}
displayInput={state.displayNewFile}
externalUploads={state.uploadFileEvent}
/>
}
</div>
</div>
</div>
</div>
</div>
);
};
export default Workspace;

@ -0,0 +1,16 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"jsx": "react",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}

@ -0,0 +1,13 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": ["**/*.spec.ts", "**/*.spec.tsx"],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

@ -92,6 +92,9 @@
},
"debugger": {
"tags": []
},
"remix-ui-workspace": {
"tags": []
}
}
}

@ -20,7 +20,9 @@
"@remix-project/remix-astwalker": ["dist/libs/remix-astwalker/index.js"],
"@remix-project/remix-debug": ["dist/libs/remix-debug/src/index.js"],
"@remix-project/remix-lib": ["dist/libs/remix-lib/src/index.js"],
"@remix-project/remix-simulator": ["dist/libs/remix-simulator/src/index.js"],
"@remix-project/remix-simulator": [
"dist/libs/remix-simulator/src/index.js"
],
"@remix-project/remix-solidity": ["dist/libs/remix-solidity/index.js"],
"@remix-project/remix-tests": ["dist/libs/remix-tests/src/index.js"],
"@remix-project/remix-url-resolver": [
@ -35,7 +37,8 @@
"@remix-project/remix-solidity-ts": ["libs/remix-solidity/src/index.ts"],
"@remix-ui/modal-dialog": ["libs/remix-ui/modal-dialog/src/index.ts"],
"@remix-ui/toaster": ["libs/remix-ui/toaster/src/index.ts"],
"@remix-ui/file-explorer": ["libs/remix-ui/file-explorer/src/index.ts"]
"@remix-ui/file-explorer": ["libs/remix-ui/file-explorer/src/index.ts"],
"@remix-ui/workspace": ["libs/remix-ui/workspace/src/index.ts"]
}
},
"exclude": ["node_modules", "tmp"]

@ -347,7 +347,11 @@
"linter": "eslint",
"config": "libs/remix-tests/.eslintrc",
"tsConfig": ["libs/remix-tests/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "libs/remix-tests/tests/**/*", "**/dist/**"]
"exclude": [
"**/node_modules/**",
"libs/remix-tests/tests/**/*",
"**/dist/**"
]
}
},
"test": {
@ -705,6 +709,22 @@
}
}
}
},
"remix-ui-workspace": {
"root": "libs/remix-ui/workspace",
"sourceRoot": "libs/remix-ui/workspace/src",
"projectType": "library",
"schematics": {},
"architect": {
"lint": {
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/workspace/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/workspace/**/*"]
}
}
}
}
},
"cli": {

Loading…
Cancel
Save