Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
B
baas-ide
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
guxukai
baas-ide
Commits
f816a5a6
Unverified
Commit
f816a5a6
authored
Mar 02, 2021
by
yann300
Committed by
GitHub
Mar 02, 2021
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #924 from ethereum/open-save-workspace
Move workspace to a react component
parents
57c55f0f
ce43be0f
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
702 additions
and
432 deletions
+702
-432
removeFile.ts
apps/remix-ide-e2e/src/commands/removeFile.ts
+5
-5
fileExplorer.test.ts
apps/remix-ide-e2e/src/tests/fileExplorer.test.ts
+4
-8
gist.test.ts
apps/remix-ide-e2e/src/tests/gist.test.ts
+2
-2
remixd.test.ts
apps/remix-ide-e2e/src/tests/remixd.test.ts
+1
-1
solidityUnittests.test.ts
apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
+1
-1
workspace.test.ts
apps/remix-ide-e2e/src/tests/workspace.test.ts
+10
-8
index.d.ts
apps/remix-ide-e2e/src/types/index.d.ts
+1
-1
app.js
apps/remix-ide/src/app.js
+2
-2
fileManager.js
apps/remix-ide/src/app/files/fileManager.js
+10
-0
remixDProvider.js
apps/remix-ide/src/app/files/remixDProvider.js
+6
-0
remixd-handle.js
apps/remix-ide/src/app/files/remixd-handle.js
+2
-6
file-panel.js
apps/remix-ide/src/app/panels/file-panel.js
+102
-271
file-panel-styles.js
apps/remix-ide/src/app/panels/styles/file-panel-styles.js
+0
-62
remix-ui-modal-dialog.tsx
libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx
+8
-4
.babelrc
libs/remix-ui/workspace/.babelrc
+4
-0
.eslintrc
libs/remix-ui/workspace/.eslintrc
+19
-0
README.md
libs/remix-ui/workspace/README.md
+7
-0
index.ts
libs/remix-ui/workspace/src/index.ts
+1
-0
remix-ui-workspace.css
libs/remix-ui/workspace/src/lib/remix-ui-workspace.css
+60
-58
remix-ui-workspace.tsx
libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
+399
-0
tsconfig.json
libs/remix-ui/workspace/tsconfig.json
+16
-0
tsconfig.lib.json
libs/remix-ui/workspace/tsconfig.lib.json
+13
-0
nx.json
nx.json
+3
-0
tsconfig.json
tsconfig.json
+5
-2
workspace.json
workspace.json
+21
-1
No files found.
apps/remix-ide-e2e/src/commands/removeFile.ts
View file @
f816a5a6
...
...
@@ -3,9 +3,9 @@ import { NightwatchBrowser } from 'nightwatch'
const
EventEmitter
=
require
(
'events'
)
class
RemoveFile
extends
EventEmitter
{
command
(
this
:
NightwatchBrowser
,
path
:
string
):
NightwatchBrowser
{
command
(
this
:
NightwatchBrowser
,
path
:
string
,
workspace
:
string
):
NightwatchBrowser
{
this
.
api
.
perform
((
done
)
=>
{
removeFile
(
this
.
api
,
path
,
()
=>
{
removeFile
(
this
.
api
,
path
,
workspace
,
()
=>
{
done
()
this
.
emit
(
'complete'
)
})
...
...
@@ -14,7 +14,7 @@ class RemoveFile extends EventEmitter {
}
}
function
removeFile
(
browser
:
NightwatchBrowser
,
path
:
string
,
done
:
VoidFunction
)
{
function
removeFile
(
browser
:
NightwatchBrowser
,
path
:
string
,
workspace
:
string
,
done
:
VoidFunction
)
{
browser
.
execute
(
function
(
path
)
{
function
contextMenuClick
(
element
)
{
const
evt
=
element
.
ownerDocument
.
createEvent
(
'MouseEvents'
)
...
...
@@ -39,8 +39,8 @@ function removeFile (browser: NightwatchBrowser, path: string, done: VoidFunctio
.
pause
(
2000
)
.
perform
(()
=>
{
console
.
log
(
path
,
'to remove'
)
browser
.
waitForElementVisible
(
'.modal-ok'
)
.
click
(
'.modal-ok'
)
browser
.
waitForElementVisible
(
'
*[data-id="'
+
workspace
+
'ModalDialogContainer-react"]
.modal-ok'
)
.
click
(
'
*[data-id="'
+
workspace
+
'ModalDialogContainer-react"]
.modal-ok'
)
.
waitForElementNotPresent
(
'[data-path="'
+
path
+
'"]'
)
done
()
})
...
...
apps/remix-ide-e2e/src/tests/fileExplorer.test.ts
View file @
f816a5a6
...
...
@@ -40,11 +40,7 @@ module.exports = {
'Should delete file `5_Renamed_Contract.sol` from file explorer'
:
function
(
browser
:
NightwatchBrowser
)
{
browser
.
waitForElementVisible
(
'*[data-id="treeViewLitreeViewItem5_Renamed_Contract.sol"]'
)
.
rightClick
(
'[data-path="5_Renamed_Contract.sol"]'
)
.
click
(
'*[id="menuitemdelete"]'
)
.
waitForElementVisible
(
'*[data-id="default_workspaceModalDialogContainer-react"]'
)
.
pause
(
2000
)
.
click
(
'.modal-ok'
)
.
removeFile
(
'5_Renamed_Contract.sol'
,
'default_workspace'
)
.
waitForElementNotPresent
(
'*[data-id="treeViewLitreeViewItem5_Renamed_Contract.sol"'
)
},
...
...
@@ -75,7 +71,7 @@ module.exports = {
.
click
(
'*[id="menuitemdelete"]'
)
.
waitForElementVisible
(
'*[data-id="default_workspaceModalDialogContainer-react"]'
)
.
pause
(
2000
)
.
click
(
'.modal-ok'
)
.
click
(
'
*[data-id="default_workspaceModalDialogContainer-react"]
.modal-ok'
)
.
waitForElementNotPresent
(
'*[data-id="treeViewLitreeViewItemBrowser_E2E_Tests"]'
)
},
...
...
@@ -88,11 +84,11 @@ module.exports = {
.
click
(
'*[data-id="fileExplorerNewFilepublishToGist"]'
)
.
waitForElementVisible
(
'*[data-id="default_workspaceModalDialogContainer-react"]'
)
.
pause
(
2000
)
.
click
(
'.modal-ok'
)
.
click
(
'
*[data-id="default_workspaceModalDialogContainer-react"]
.modal-ok'
)
.
pause
(
2000
)
.
waitForElementVisible
(
'*[data-id="default_workspaceModalDialogContainer-react"]'
)
.
pause
(
2000
)
.
click
(
'.modal-ok'
)
.
click
(
'
*[data-id="default_workspaceModalDialogContainer-react"]
.modal-ok'
)
.
pause
(
2000
)
.
perform
((
done
)
=>
{
if
(
runtimeBrowser
===
'chrome'
)
{
...
...
apps/remix-ide-e2e/src/tests/gist.test.ts
View file @
f816a5a6
...
...
@@ -38,7 +38,7 @@ module.exports = {
.
click
(
'*[data-id="fileExplorerNewFilepublishToGist"]'
)
.
pause
(
2000
)
.
waitForElementVisible
(
'*[data-id="default_workspaceModalDialogContainer-react"]'
)
.
click
(
'.modal-ok'
)
.
click
(
'
*[data-id="default_workspaceModalDialogContainer-react"]
.modal-ok'
)
.
pause
(
10000
)
.
getText
(
'[data-id="default_workspaceModalDialogModalBody-react"]'
,
(
result
)
=>
{
console
.
log
(
result
)
...
...
@@ -99,7 +99,7 @@ module.exports = {
.
click
(
'*[data-id="fileExplorerNewFilepublishToGist"]'
)
.
waitForElementVisible
(
'*[data-id="default_workspaceModalDialogContainer-react"]'
)
.
pause
(
2000
)
.
click
(
'.modal-ok'
)
.
click
(
'
*[data-id="default_workspaceModalDialogContainer-react"]
.modal-ok'
)
.
pause
(
10000
)
.
getText
(
'[data-id="default_workspaceModalDialogModalBody-react"]'
,
(
result
)
=>
{
browser
.
assert
.
ok
(
result
.
value
===
'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.'
,
'Assert failed. Gist token error message not displayed.'
)
...
...
apps/remix-ide-e2e/src/tests/remixd.test.ts
View file @
f816a5a6
...
...
@@ -125,7 +125,7 @@ function runTests (browser: NightwatchBrowser) {
.
pause
(
1000
)
.
renamePath
(
'folder1/contract_'
+
browserName
+
'.sol'
,
'renamed_contract_'
+
browserName
+
'.sol'
,
'folder1/renamed_contract_'
+
browserName
+
'.sol'
)
.
pause
(
1000
)
.
removeFile
(
'folder1/contract_'
+
browserName
+
'_toremove.sol'
)
.
removeFile
(
'folder1/contract_'
+
browserName
+
'_toremove.sol'
,
'localhost'
)
.
perform
(
function
(
done
)
{
testImportFromRemixd
(
browser
,
()
=>
{
done
()
})
})
...
...
apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
View file @
f816a5a6
...
...
@@ -37,7 +37,7 @@ module.exports = {
.
clickLaunchIcon
(
'fileExplorers'
)
.
pause
(
10000
)
.
openFile
(
'tests/simple_storage_test.sol'
)
.
removeFile
(
'tests/simple_storage_test.sol'
)
.
removeFile
(
'tests/simple_storage_test.sol'
,
'default_workspace'
)
},
'Should run simple unit test `simple_storage_test.sol` '
:
function
(
browser
:
NightwatchBrowser
)
{
...
...
apps/remix-ide-e2e/src/tests/workspace.test.ts
View file @
f816a5a6
...
...
@@ -18,6 +18,8 @@ module.exports = {
'Editor should be focused on the 3_Ballot.sol'
:
function
(
browser
:
NightwatchBrowser
)
{
browser
.
pause
(
5000
)
.
refresh
()
.
pause
(
2000
)
.
getEditorValue
((
content
)
=>
{
browser
.
assert
.
ok
(
content
.
indexOf
(
'contract Ballot {'
)
!==
-
1
,
'content doesn
\'
t include Ballot contract'
)
})
...
...
@@ -32,18 +34,18 @@ module.exports = {
browser
.
clickLaunchIcon
(
'fileExplorers'
)
.
click
(
'*[data-id="workspaceCreate"]'
)
// create workspace_name
.
waitForElementVisible
(
'*[data-id="modalDialogCustomPromptText"]'
)
.
clearValue
(
'*[data-id="modalDialogCustomPromptText"]'
)
.
setValue
(
'*[data-id="modalDialogCustomPromptText"]'
,
'workspace_name'
)
.
modalFooterOKClick
(
)
.
waitForElementVisible
(
'*[data-id="modalDialogCustomPromptText
Create
"]'
)
// eslint-disable-next-line dot-notation
.
execute
(
function
()
{
document
.
querySelector
(
'*[data-id="modalDialogCustomPromptTextCreate"]'
)[
'value'
]
=
'workspace_name'
}
)
.
click
(
'*[data-id="workspacesModalDialogModalDialogModalFooter-react"] .modal-ok'
)
.
waitForElementVisible
(
'*[data-id="treeViewLitreeViewItemtests"]'
)
.
addFile
(
'test.sol'
,
{
content
:
'test'
})
.
waitForElementVisible
(
'*[data-id="treeViewLitreeViewItemtest.sol"]'
)
.
click
(
'*[data-id="workspaceCreate"]'
)
// create workspace_name_1
.
waitForElementVisible
(
'*[data-id="modalDialogCustomPromptText"]'
)
.
clearValue
(
'*[data-id="modalDialogCustomPromptText"]'
)
.
setValue
(
'*[data-id="modalDialogCustomPromptText"]'
,
'workspace_name_1'
)
.
modalFooterOKClick
(
)
.
waitForElementVisible
(
'*[data-id="modalDialogCustomPromptText
Create
"]'
)
// eslint-disable-next-line dot-notation
.
execute
(
function
()
{
document
.
querySelector
(
'*[data-id="modalDialogCustomPromptTextCreate"]'
)[
'value'
]
=
'workspace_name_1'
}
)
.
click
(
'*[data-id="workspacesModalDialogModalDialogModalFooter-react"] .modal-ok'
)
.
waitForElementVisible
(
'*[data-id="treeViewLitreeViewItemtests"]'
)
.
waitForElementNotPresent
(
'*[data-id="treeViewLitreeViewItemtest.sol"]'
)
.
click
(
'*[data-id="workspacesSelect"] option[value="workspace_name"]'
)
...
...
apps/remix-ide-e2e/src/types/index.d.ts
View file @
f816a5a6
...
...
@@ -41,7 +41,7 @@ declare module "nightwatch" {
getInstalledPlugins
(
cb
:
(
plugins
:
string
[])
=>
void
):
NightwatchBrowser
,
verifyCallReturnValue
(
address
:
string
,
checks
:
string
[]):
NightwatchBrowser
,
testEditorValue
(
testvalue
:
string
):
NightwatchBrowser
,
removeFile
(
path
:
string
):
NightwatchBrowser
,
removeFile
(
path
:
string
,
workspace
:
string
):
NightwatchBrowser
,
switchBrowserWindow
(
url
:
string
,
windowName
:
string
,
cb
:
(
browser
:
NightwatchBrowser
,
window
?:
NightwatchCallbackResult
<
Window
>
)
=>
void
):
NightwatchBrowser
,
setupMetamask
(
passphrase
:
string
,
password
:
string
):
NightwatchBrowser
,
signMessage
(
msg
:
string
,
callback
:
(
hash
:
{
value
:
string
},
signature
:
{
value
:
string
})
=>
void
):
NightwatchBrowser
,
...
...
apps/remix-ide/src/app.js
View file @
f816a5a6
...
...
@@ -444,7 +444,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
await
appManager
.
activatePlugin
([
'contentImport'
,
'theme'
,
'editor'
,
'fileManager'
,
'compilerMetadata'
,
'compilerArtefacts'
,
'network'
,
'web3Provider'
,
'offsetToLineColumnConverter'
])
await
appManager
.
activatePlugin
([
'mainPanel'
,
'menuicons'
])
await
appManager
.
activatePlugin
([
'sidePanel'
])
// activating host plugin separately
await
appManager
.
activatePlugin
([
'home'
,
'hiddenPanel'
,
'pluginManager'
,
'fileExplorers'
,
'settings'
,
'contextualListener'
,
'terminal'
,
'fetchAndCompile'
])
await
appManager
.
activatePlugin
([
'home'
])
await
appManager
.
activatePlugin
([
'hiddenPanel'
,
'pluginManager'
,
'fileExplorers'
,
'settings'
,
'contextualListener'
,
'terminal'
,
'fetchAndCompile'
])
const
queryParams
=
new
QueryParams
()
const
params
=
queryParams
.
get
()
...
...
@@ -486,6 +487,5 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
migrateToWorkspace
(
fileManager
)
filePanel
.
initWorkspace
()
if
(
params
.
embed
)
framingService
.
embed
()
}
apps/remix-ide/src/app/files/fileManager.js
View file @
f816a5a6
...
...
@@ -49,6 +49,10 @@ class FileManager extends Plugin {
this
.
init
()
}
getOpenedFiles
()
{
return
this
.
openedFiles
}
setMode
(
mode
)
{
this
.
mode
=
mode
}
...
...
@@ -589,6 +593,12 @@ class FileManager extends Plugin {
if
(
!
this
.
exists
(
workspaceRootPath
))
await
this
.
mkdir
(
workspaceRootPath
)
if
(
!
this
.
exists
(
workspacePath
))
await
this
.
mkdir
(
workspacePath
)
}
async
workspaceExists
(
name
)
{
const
workspaceProvider
=
this
.
_deps
.
filesProviders
.
workspace
const
workspacePath
=
'browser/'
+
workspaceProvider
.
workspacesPath
+
'/'
+
name
return
this
.
exists
(
workspacePath
)
}
}
module
.
exports
=
FileManager
apps/remix-ide/src/app/files/remixDProvider.js
View file @
f816a5a6
...
...
@@ -54,6 +54,11 @@ module.exports = class RemixDProvider {
close
(
cb
)
{
this
.
_isReady
=
false
cb
()
this
.
event
.
trigger
(
'disconnected'
)
}
preInit
()
{
this
.
_registerEvent
()
}
init
(
cb
)
{
...
...
@@ -63,6 +68,7 @@ module.exports = class RemixDProvider {
this
.
_isReady
=
true
this
.
_readOnlyMode
=
result
this
.
_registerEvent
()
this
.
event
.
trigger
(
'connected'
)
cb
&&
cb
()
}).
catch
((
error
)
=>
{
cb
&&
cb
(
error
)
...
...
apps/remix-ide/src/app/files/remixd-handle.js
View file @
f816a5a6
...
...
@@ -30,15 +30,13 @@ const profile = {
}
export
class
RemixdHandle
extends
WebsocketPlugin
{
constructor
(
fileSystemExplorer
,
locahostProvider
,
appManager
)
{
constructor
(
locahostProvider
,
appManager
)
{
super
(
profile
)
this
.
fileSystemExplorer
=
fileSystemExplorer
this
.
locahostProvider
=
locahostProvider
this
.
appManager
=
appManager
}
deactivate
()
{
this
.
fileSystemExplorer
.
hide
()
if
(
super
.
socket
)
super
.
deactivate
()
this
.
call
(
'manager'
,
'deactivatePlugin'
,
'git'
)
this
.
locahostProvider
.
close
((
error
)
=>
{
...
...
@@ -82,9 +80,7 @@ export class RemixdHandle extends WebsocketPlugin {
this
.
canceled
()
}
},
3000
)
this
.
locahostProvider
.
init
(()
=>
{
this
.
fileSystemExplorer
.
show
()
})
this
.
locahostProvider
.
init
(()
=>
{})
this
.
call
(
'manager'
,
'activatePlugin'
,
'git'
)
}
}
...
...
apps/remix-ide/src/app/panels/file-panel.js
View file @
f816a5a6
...
...
@@ -3,10 +3,8 @@ 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
import
*
as
ethutil
from
'ethereumjs-util'
var
EventManager
=
require
(
'../../lib/events'
)
var
{
RemixdHandle
}
=
require
(
'../files/remixd-handle.js'
)
var
{
GitHandle
}
=
require
(
'../files/git-handle.js'
)
...
...
@@ -14,9 +12,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,84 +46,107 @@ 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
}
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
()
}
fileManager
:
this
.
_components
.
registry
.
get
(
'filemanager'
).
api
}
this
.
reset
=
false
this
.
registeredMenuItems
=
[]
this
.
displayNewFile
=
false
this
.
uploadFileEvent
=
null
this
.
el
=
yo
`
<div id="fileExplorerView">
</div>
`
this
.
remixdHandle
=
new
RemixdHandle
(
this
.
remixdExplorer
,
this
.
_deps
.
fileProviders
.
localhost
,
appManager
)
this
.
el
=
document
.
createElement
(
'div'
)
this
.
el
.
setAttribute
(
'id'
,
'fileExplorerView'
)
this
.
remixdHandle
=
new
RemixdHandle
(
this
.
_deps
.
fileProviders
.
localhost
,
appManager
)
this
.
gitHandle
=
new
GitHandle
()
this
.
registeredMenuItems
=
[]
this
.
request
=
{}
this
.
workspaces
=
[]
this
.
initialWorkspace
=
null
}
this
.
event
=
new
EventManager
()
this
.
_deps
.
fileProviders
.
localhost
.
event
.
register
(
'connecting'
,
(
event
)
=>
{
})
render
()
{
this
.
initWorkspace
().
then
(()
=>
this
.
getWorkspaces
()).
catch
(
console
.
error
)
return
this
.
el
}
this
.
_deps
.
fileProviders
.
localhost
.
event
.
register
(
'connected'
,
(
event
)
=>
{
this
.
remixdExplorer
.
show
()
})
renderComponent
()
{
ReactDOM
.
render
(
<
Workspace
createWorkspace
=
{
this
.
createWorkspace
.
bind
(
this
)}
setWorkspace
=
{
this
.
setWorkspace
.
bind
(
this
)}
workspaceRenamed
=
{
this
.
workspaceRenamed
.
bind
(
this
)}
workspaceDeleted
=
{
this
.
workspaceDeleted
.
bind
(
this
)}
workspaceCreated
=
{
this
.
workspaceCreated
.
bind
(
this
)}
workspace
=
{
this
.
_deps
.
fileProviders
.
workspace
}
browser
=
{
this
.
_deps
.
fileProviders
.
browser
}
localhost
=
{
this
.
_deps
.
fileProviders
.
localhost
}
fileManager
=
{
this
.
_deps
.
fileManager
}
registry
=
{
this
.
_components
.
registry
}
plugin
=
{
this
}
request
=
{
this
.
request
}
workspaces
=
{
this
.
workspaces
}
registeredMenuItems
=
{
this
.
registeredMenuItems
}
initialWorkspace
=
{
this
.
initialWorkspace
}
/
>
,
this
.
el
)
}
this
.
_deps
.
fileProviders
.
localhost
.
event
.
register
(
'errored'
,
(
event
)
=>
{
this
.
remixdExplorer
.
hide
()
})
/**
* @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
.
_deps
.
fileProviders
.
localhost
.
event
.
register
(
'closed'
,
(
event
)
=>
{
this
.
remixdExplorer
.
hide
()
})
this
.
registeredMenuItems
=
[...
this
.
registeredMenuItems
,
item
]
this
.
renderComponent
()
}
this
.
currentWorkspace
=
null
async
getCurrentWorkspace
()
{
return
await
this
.
request
.
getCurrentWorkspace
()
}
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
+
'/'
,
''
))
async
getWorkspaces
()
{
const
result
=
new
Promise
((
resolve
,
reject
)
=>
{
const
workspacesPath
=
this
.
_deps
.
fileProviders
.
workspace
.
workspacesPath
this
.
_deps
.
fileProviders
.
browser
.
resolveDirectory
(
'/'
+
workspacesPath
,
(
error
,
items
)
=>
{
if
(
error
)
{
console
.
error
(
error
)
return
reject
(
error
)
}
resolve
(
Object
.
keys
(
items
)
.
filter
((
item
)
=>
items
[
item
].
isDirectory
)
.
map
((
folder
)
=>
folder
.
replace
(
workspacesPath
+
'/'
,
''
)))
})
})
this
.
workspaces
=
await
result
this
.
renderComponent
()
return
this
.
workspaces
}
async
initWorkspace
()
{
const
workspacesPath
=
this
.
_deps
.
fileProviders
.
workspace
.
workspacesPath
const
queryParams
=
new
QueryParams
()
const
gistHandler
=
new
GistHandler
()
const
workspacesPath
=
this
.
_deps
.
fileProviders
.
workspace
.
workspacesPath
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'
)
this
.
_deps
.
fileProviders
.
workspace
.
setWorkspace
(
'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
)
this
.
initialWorkspace
=
'code-sample'
await
this
.
_deps
.
fileManager
.
openFile
(
fileName
)
}
catch
(
e
)
{
console
.
error
(
e
)
}
...
...
@@ -138,241 +156,54 @@ module.exports = class Filepanel extends ViewPlugin {
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'
)
await
this
.
createWorkspace
(
'default_workspace'
)
}
this
.
getWorkspaces
()
})
}
async
refreshWorkspacesList
()
{
if
(
!
document
.
getElementById
(
'workspacesSelect'
))
return
const
workspaces
=
await
this
.
getWorkspaces
()
workspaces
.
push
(
this
.
LOCALHOST
)
workspaces
.
push
(
this
.
NO_WORKSPACE
)
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
()
async
createNewFile
()
{
return
await
this
.
request
.
createNewFile
()
}
createNewFile
()
{
this
.
displayNewFile
=
true
this
.
renderComponent
()
async
uploadFile
()
{
return
await
this
.
request
.
uploadFile
()
}
resetNewFile
()
{
this
.
displayNewFile
=
false
this
.
renderComponent
()
}
uploadFile
(
target
)
{
this
.
uploadFileEvent
=
target
this
.
renderComponent
()
}
resetUploadFile
()
{
this
.
uploadFileEvent
=
null
this
.
renderComponent
()
}
render
()
{
return
this
.
el
}
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
+
'/'
,
''
)))
})
})
}
getCurrentWorkspace
()
{
return
this
.
currentWorkspace
async
createWorkspace
(
workspaceName
)
{
if
(
await
this
.
_deps
.
fileManager
.
workspaceExists
(
workspaceName
))
throw
new
Error
(
'workspace already exists'
)
const
workspacesPath
=
this
.
_deps
.
fileProviders
.
workspace
.
workspacesPath
await
this
.
_deps
.
fileManager
.
createWorkspace
(
workspaceName
)
for
(
const
file
in
examples
)
{
try
{
await
this
.
_deps
.
fileManager
.
writeFile
(
'browser/'
+
workspacesPath
+
'/'
+
workspaceName
+
'/'
+
examples
[
file
].
name
,
examples
[
file
].
content
)
}
catch
(
error
)
{
console
.
error
(
error
)
}
}
}
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
})
this
.
emit
(
'setWorkspace'
,
workspace
)
}
/**
*
* @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
()
workspaceRenamed
(
workspace
)
{
this
.
emit
(
'renameWorkspace'
,
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
})
})
}
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'
)))
})
}
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
})
})
workspaceDeleted
(
workspace
)
{
this
.
emit
(
'deleteWorkspace'
,
workspace
)
}
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
)
workspaceCreated
(
workspace
)
{
this
.
emit
(
'createWorkspace'
,
workspace
)
}
/** end section */
}
apps/remix-ide/src/app/panels/styles/file-panel-styles.js
deleted
100644 → 0
View file @
57c55f0f
var
csjs
=
require
(
'csjs-inject'
)
var
css
=
csjs
`
.container {
display : flex;
flex-direction : row;
width : 100%;
height : 100%;
box-sizing : border-box;
}
.fileexplorer {
display : flex;
flex-direction : column;
position : relative;
width : 100%;
padding-left : 6px;
padding-top : 6px;
}
.fileExplorerTree {
cursor : default;
}
.gist {
padding : 10px;
}
.gist i {
cursor : pointer;
}
.gist i:hover {
color : orange;
}
.connectToLocalhost {
padding : 10px;
}
.connectToLocalhost i {
cursor : pointer;
}
.connectToLocalhost i:hover {
color : var(--secondary)
}
.uploadFile {
padding : 10px;
}
.uploadFile label:hover {
color : var(--secondary)
}
.uploadFile label {
cursor : pointer;
}
.treeview {
overflow-y : auto;
}
.dialog {
display: flex;
flex-direction: column;
}
.dialogParagraph {
margin-bottom: 2em;
word-break: break-word;
}
`
module
.
exports
=
css
libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx
View file @
f816a5a6
...
...
@@ -40,6 +40,13 @@ export const ModalDialog = (props: ModalDialogProps) => {
handleHide
()
}
const
handleBlur
=
(
e
)
=>
{
if
(
!
e
.
currentTarget
.
contains
(
e
.
relatedTarget
))
{
e
.
stopPropagation
()
handleHide
()
}
}
return
(
<
div
data
-
id=
{
`${props.id}ModalDialogContainer-react`
}
...
...
@@ -51,10 +58,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
>
<
div
className=
"modal-dialog"
role=
"document"
>
<
div
onBlur=
{
(
e
)
=>
{
e
.
stopPropagation
()
handleHide
()
}
}
onBlur=
{
handleBlur
}
ref=
{
modal
}
tabIndex=
{
-
1
}
className=
{
'modal-content remixModalContent '
+
(
props
.
modalClass
?
props
.
modalClass
:
''
)
}
...
...
libs/remix-ui/workspace/.babelrc
0 → 100644
View file @
f816a5a6
{
"presets": ["@nrwl/react/babel"],
"plugins": []
}
libs/remix-ui/workspace/.eslintrc
0 → 100644
View file @
f816a5a6
{
"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"
}
}
libs/remix-ui/workspace/README.md
0 → 100644
View file @
f816a5a6
# 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
)
.
libs/remix-ui/workspace/src/index.ts
0 → 100644
View file @
f816a5a6
export
*
from
'./lib/remix-ui-workspace'
;
apps/remix-ide/src/app/panels/styles/file-panel-styles
.css
→
libs/remix-ui/workspace/src/lib/remix-ui-workspace
.css
View file @
f816a5a6
.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
;
}
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
;
}
\ No newline at end of file
libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
0 → 100644
View file @
f816a5a6
import
React
,
{
useState
,
useEffect
,
useRef
}
from
'react'
;
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
/* eslint-disable-next-line */
export
interface
WorkspaceProps
{
setWorkspace
:
({
name
:
string
,
isLocalhost
:
boolean
})
=>
void
,
createWorkspace
:
(
name
:
string
)
=>
void
,
workspaceRenamed
:
({
name
:
string
})
=>
void
,
workspaceCreated
:
({
name
:
string
})
=>
void
,
workspaceDeleted
:
({
name
:
string
})
=>
void
,
workspace
:
any
// workspace provider,
browser
:
any
// browser provider
localhost
:
any
// localhost provider
fileManager
:
any
registry
:
any
// registry
plugin
:
any
// plugin call and resetFocus
request
:
any
// api request,
workspaces
:
any
,
registeredMenuItems
:
[]
// menu items
initialWorkspace
:
string
}
var
canUpload
=
window
.
File
||
window
.
FileReader
||
window
.
FileList
||
window
.
Blob
export
const
Workspace
=
(
props
:
WorkspaceProps
)
=>
{
const
LOCALHOST
=
' - connect to localhost - '
const
NO_WORKSPACE
=
' - none - '
/* extends the parent 'plugin' with some function needed by the file explorer */
props
.
plugin
.
resetFocus
=
()
=>
{
setState
(
prevState
=>
{
return
{
...
prevState
,
reset
:
true
}
})
}
props
.
plugin
.
resetNewFile
=
()
=>
{
setState
(
prevState
=>
{
return
{
...
prevState
,
displayNewFile
:
!
state
.
displayNewFile
}
})
}
/* implement an external API, consumed by the parent */
props
.
request
.
createWorkspace
=
()
=>
{
return
createWorkspace
()
}
props
.
request
.
createNewFile
=
()
=>
{
props
.
plugin
.
resetNewFile
()
}
props
.
request
.
uploadFile
=
(
target
)
=>
{
setState
(
prevState
=>
{
return
{
...
prevState
,
uploadFileEvent
:
target
}
})
}
props
.
request
.
getCurrentWorkspace
=
()
=>
{
return
state
.
currentWorkspace
}
useEffect
(()
=>
{
const
getWorkspaces
=
async
()
=>
{
if
(
props
.
workspaces
&&
Array
.
isArray
(
props
.
workspaces
))
{
if
(
props
.
initialWorkspace
)
{
props
.
workspace
.
setWorkspace
(
props
.
initialWorkspace
)
setState
(
prevState
=>
{
return
{
...
prevState
,
workspaces
:
props
.
workspaces
,
currentWorkspace
:
props
.
initialWorkspace
}
})
}
else
if
(
props
.
workspaces
.
length
>
0
&&
state
.
currentWorkspace
===
NO_WORKSPACE
)
{
props
.
workspace
.
setWorkspace
(
props
.
workspaces
[
0
])
setState
(
prevState
=>
{
return
{
...
prevState
,
workspaces
:
props
.
workspaces
,
currentWorkspace
:
props
.
workspaces
[
0
]
}
})
}
else
{
setState
(
prevState
=>
{
return
{
...
prevState
,
workspaces
:
props
.
workspaces
}
})
}
}
}
getWorkspaces
()
},
[
props
.
workspaces
])
useEffect
(()
=>
{
props
.
localhost
.
event
.
register
(
'connected'
,
(
event
)
=>
{
remixdExplorer
.
show
()
})
props
.
localhost
.
event
.
register
(
'disconnected'
,
(
event
)
=>
{
remixdExplorer
.
hide
()
})
},
[])
const
[
state
,
setState
]
=
useState
({
workspaces
:
[],
reset
:
false
,
currentWorkspace
:
NO_WORKSPACE
,
hideRemixdExplorer
:
true
,
displayNewFile
:
false
,
externalUploads
:
null
,
uploadFileEvent
:
null
,
modal
:
{
hide
:
true
,
title
:
''
,
message
:
null
,
ok
:
{
label
:
''
,
fn
:
()
=>
{}
},
cancel
:
{
label
:
''
,
fn
:
()
=>
{}
},
handleHide
:
null
}
})
/* workspace creation, renaming and deletion */
const
renameCurrentWorkspace
=
()
=>
{
modal
(
'Rename Workspace'
,
renameModalMessage
(),
{
label
:
'OK'
,
fn
:
onFinishRenameWorkspace
},
{
label
:
''
,
fn
:
()
=>
{}
})
}
const
createWorkspace
=
()
=>
{
modal
(
'Create Workspace'
,
createModalMessage
(),
{
label
:
'OK'
,
fn
:
onFinishCreateWorkspace
},
{
label
:
''
,
fn
:
()
=>
{}
})
}
const
deleteCurrentWorkspace
=
()
=>
{
modal
(
'Remove Workspace'
,
'Please choose a name for the workspace'
,
{
label
:
'OK'
,
fn
:
onFinishDeleteWorkspace
},
{
label
:
''
,
fn
:
()
=>
{}
})
}
const
modalMessage
=
(
title
:
string
,
body
:
string
)
=>
{
modal
(
title
,
body
,
{
label
:
'OK'
,
fn
:
()
=>
{}
},
{
label
:
null
,
fn
:
null
})
}
const
workspaceRenameInput
=
useRef
()
const
workspaceCreateInput
=
useRef
()
const
onFinishRenameWorkspace
=
async
()
=>
{
if
(
workspaceRenameInput
.
current
===
undefined
)
return
// @ts-ignore: Object is possibly 'null'.
const
workspaceName
=
workspaceRenameInput
.
current
.
value
const
workspacesPath
=
props
.
workspace
.
workspacesPath
await
props
.
fileManager
.
rename
(
'browser/'
+
workspacesPath
+
'/'
+
state
.
currentWorkspace
,
'browser/'
+
workspacesPath
+
'/'
+
workspaceName
)
setWorkspace
(
workspaceName
)
props
.
workspaceRenamed
({
name
:
state
.
currentWorkspace
})
}
const
onFinishCreateWorkspace
=
async
()
=>
{
if
(
workspaceCreateInput
.
current
===
undefined
)
return
// @ts-ignore: Object is possibly 'null'.
const
workspaceName
=
workspaceCreateInput
.
current
.
value
try
{
await
props
.
createWorkspace
(
workspaceName
)
}
catch
(
e
)
{
modalMessage
(
'Workspace Creation'
,
e
.
message
)
console
.
error
(
e
)
}
await
setWorkspace
(
workspaceName
)
}
const
onFinishDeleteWorkspace
=
async
()
=>
{
const
workspacesPath
=
props
.
workspace
.
workspacesPath
props
.
browser
.
remove
(
workspacesPath
+
'/'
+
state
.
currentWorkspace
)
const
name
=
state
.
currentWorkspace
setWorkspace
(
NO_WORKSPACE
)
props
.
workspaceDeleted
({
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
)
}
props
.
plugin
.
getWorkspaces
()
setState
(
prevState
=>
{
return
{
...
prevState
,
currentWorkspace
:
name
}
})
props
.
setWorkspace
({
name
,
isLocalhost
:
name
===
LOCALHOST
})
}
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
}
})
}
}
const
handleHideModal
=
()
=>
{
setState
(
prevState
=>
{
return
{
...
prevState
,
modal
:
{
...
state
.
modal
,
hide
:
true
,
message
:
null
}
}
})
}
const
modal
=
async
(
title
:
string
,
message
:
string
|
JSX
.
Element
,
ok
:
{
label
:
string
,
fn
:
()
=>
void
},
cancel
:
{
label
:
string
,
fn
:
()
=>
void
})
=>
{
await
setState
(
prevState
=>
{
return
{
...
prevState
,
modal
:
{
...
prevState
.
modal
,
hide
:
false
,
message
,
title
,
ok
,
cancel
,
handleHide
:
handleHideModal
}
}
})
}
const
createModalMessage
=
()
=>
{
return
(
<>
<
span
>
{
state
.
modal
.
message
}
</
span
>
<
input
type=
"text"
data
-
id=
"modalDialogCustomPromptTextCreate"
placeholder=
{
`workspace_${Date.now()}`
}
ref=
{
workspaceCreateInput
}
className=
"form-control"
/>
</>
)
}
const
renameModalMessage
=
()
=>
{
return
(
<>
<
span
>
{
state
.
modal
.
message
}
</
span
>
<
input
type=
"text"
data
-
id=
"modalDialogCustomPromptTextRename"
placeholder=
{
state
.
currentWorkspace
}
ref=
{
workspaceRenameInput
}
className=
"form-control"
/>
</>
)
}
// const handleWorkspaceSelect = (e) => {
// const value = e.target.value
// setWorkspace(value)
// }
return
(
<
div
className=
'remixui_container'
>
<
ModalDialog
id=
'workspacesModalDialog'
title=
{
state
.
modal
.
title
}
message=
{
state
.
modal
.
message
}
hide=
{
state
.
modal
.
hide
}
ok=
{
state
.
modal
.
ok
}
cancel=
{
state
.
modal
.
cancel
}
handleHide=
{
handleHideModal
}
>
{
(
typeof
state
.
modal
.
message
!==
'string'
)
&&
state
.
modal
.
message
}
</
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
>
{
state
.
workspaces
.
length
<=
0
&&
<
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
&&
state
.
currentWorkspace
!==
LOCALHOST
&&
<
FileExplorer
name=
{
state
.
currentWorkspace
}
registry=
{
props
.
registry
}
filesProvider=
{
props
.
workspace
}
menuItems=
{
[
'createNewFile'
,
'createNewFolder'
,
'publishToGist'
,
canUpload
?
'uploadFile'
:
''
]
}
plugin=
{
props
.
plugin
}
focusRoot=
{
state
.
reset
}
contextMenuItems=
{
props
.
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=
{
props
.
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=
{
props
.
registeredMenuItems
}
displayInput=
{
state
.
displayNewFile
}
externalUploads=
{
state
.
uploadFileEvent
}
/>
}
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
);
};
export
default
Workspace
;
libs/remix-ui/workspace/tsconfig.json
0 → 100644
View file @
f816a5a6
{
"extends"
:
"../../../tsconfig.json"
,
"compilerOptions"
:
{
"jsx"
:
"react"
,
"allowJs"
:
true
,
"esModuleInterop"
:
true
,
"allowSyntheticDefaultImports"
:
true
},
"files"
:
[],
"include"
:
[],
"references"
:
[
{
"path"
:
"./tsconfig.lib.json"
}
]
}
libs/remix-ui/workspace/tsconfig.lib.json
0 → 100644
View file @
f816a5a6
{
"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"
]
}
nx.json
View file @
f816a5a6
...
...
@@ -92,6 +92,9 @@
},
"debugger"
:
{
"tags"
:
[]
},
"remix-ui-workspace"
:
{
"tags"
:
[]
}
}
}
tsconfig.json
View file @
f816a5a6
...
...
@@ -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"
]
...
...
workspace.json
View file @
f816a5a6
...
...
@@ -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"
:
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment