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
3550cda6
Commit
3550cda6
authored
Sep 20, 2021
by
ioedeveloper
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move file-explorer to workspace directory
parent
0b6579f4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
527 additions
and
3 deletions
+527
-3
file-explorer-context-menu.tsx
...rkspace/src/lib/components/file-explorer-context-menu.tsx
+135
-0
file-explorer-menu.tsx
...ix-ui/workspace/src/lib/components/file-explorer-menu.tsx
+98
-0
file-explorer.tsx
libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
+0
-0
file-explorer-context-menu.css
...x-ui/workspace/src/lib/css/file-explorer-context-menu.css
+29
-0
file-explorer.css
libs/remix-ui/workspace/src/lib/css/file-explorer.css
+56
-0
remix-ui-workspace.css
libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css
+62
-0
FileSystemProvider.tsx
...mix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
+2
-2
index.ts
libs/remix-ui/workspace/src/lib/types/index.ts
+84
-1
index.ts
libs/remix-ui/workspace/src/lib/utils/index.ts
+61
-0
No files found.
libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx
0 → 100644
View file @
3550cda6
import
React
,
{
useRef
,
useEffect
}
from
'react'
// eslint-disable-line
import
{
action
,
FileExplorerContextMenuProps
}
from
'../types'
import
'./css/file-explorer-context-menu.css'
import
{
customAction
}
from
'@remixproject/plugin-api/lib/file-system/file-panel'
declare
global
{
interface
Window
{
_paq
:
any
}
}
const
_paq
=
window
.
_paq
=
window
.
_paq
||
[]
//eslint-disable-line
export
const
FileExplorerContextMenu
=
(
props
:
FileExplorerContextMenuProps
)
=>
{
const
{
actions
,
createNewFile
,
createNewFolder
,
deletePath
,
renamePath
,
hideContextMenu
,
pushChangesToGist
,
publishFileToGist
,
publishFolderToGist
,
copy
,
paste
,
runScript
,
emit
,
pageX
,
pageY
,
path
,
type
,
focus
,
...
otherProps
}
=
props
const
contextMenuRef
=
useRef
(
null
)
useEffect
(()
=>
{
contextMenuRef
.
current
.
focus
()
},
[])
useEffect
(()
=>
{
const
menuItemsContainer
=
contextMenuRef
.
current
const
boundary
=
menuItemsContainer
.
getBoundingClientRect
()
if
(
boundary
.
bottom
>
(
window
.
innerHeight
||
document
.
documentElement
.
clientHeight
))
{
menuItemsContainer
.
style
.
position
=
'fixed'
menuItemsContainer
.
style
.
bottom
=
'10px'
menuItemsContainer
.
style
.
top
=
null
}
},
[
pageX
,
pageY
])
const
filterItem
=
(
item
:
action
)
=>
{
/**
* if there are multiple elements focused we need to take this and all conditions must be met
* for example : 'downloadAsZip' with type ['file','folder'] will work on files and folders when multiple are selected
**/
const
nonRootFocus
=
focus
.
filter
((
el
)
=>
{
return
!
(
el
.
key
===
''
&&
el
.
type
===
'folder'
)
})
if
(
nonRootFocus
.
length
>
1
)
{
for
(
const
element
of
nonRootFocus
)
{
if
(
!
itemMatchesCondition
(
item
,
element
.
type
,
element
.
key
))
return
false
}
return
true
}
else
{
return
itemMatchesCondition
(
item
,
type
,
path
)
}
}
const
itemMatchesCondition
=
(
item
:
action
,
itemType
:
string
,
itemPath
:
string
)
=>
{
if
(
item
.
type
&&
Array
.
isArray
(
item
.
type
)
&&
(
item
.
type
.
findIndex
(
name
=>
name
===
itemType
)
!==
-
1
))
return
true
else
if
(
item
.
path
&&
Array
.
isArray
(
item
.
path
)
&&
(
item
.
path
.
findIndex
(
key
=>
key
===
itemPath
)
!==
-
1
))
return
true
else
if
(
item
.
extension
&&
Array
.
isArray
(
item
.
extension
)
&&
(
item
.
extension
.
findIndex
(
ext
=>
itemPath
.
endsWith
(
ext
))
!==
-
1
))
return
true
else
if
(
item
.
pattern
&&
Array
.
isArray
(
item
.
pattern
)
&&
(
item
.
pattern
.
filter
(
value
=>
itemPath
.
match
(
new
RegExp
(
value
))).
length
>
0
))
return
true
else
return
false
}
const
getPath
=
()
=>
{
if
(
focus
.
length
>
1
)
{
return
focus
.
map
((
element
)
=>
element
.
key
)
}
else
{
return
path
}
}
const
menu
=
()
=>
{
return
actions
.
filter
(
item
=>
filterItem
(
item
)).
map
((
item
,
index
)
=>
{
return
<
li
id=
{
`menuitem${item.name.toLowerCase()}`
}
key=
{
index
}
className=
'remixui_liitem'
onClick=
{
(
e
)
=>
{
e
.
stopPropagation
()
switch
(
item
.
name
)
{
case
'New File'
:
createNewFile
(
path
)
break
case
'New Folder'
:
createNewFolder
(
path
)
break
case
'Rename'
:
renamePath
(
path
,
type
)
break
case
'Delete'
:
deletePath
(
getPath
())
break
case
'Push changes to gist'
:
_paq
.
push
([
'trackEvent'
,
'fileExplorer'
,
'pushToChangesoGist'
])
pushChangesToGist
(
path
,
type
)
break
case
'Publish folder to gist'
:
_paq
.
push
([
'trackEvent'
,
'fileExplorer'
,
'publishFolderToGist'
])
publishFolderToGist
(
path
,
type
)
break
case
'Publish file to gist'
:
_paq
.
push
([
'trackEvent'
,
'fileExplorer'
,
'publishFileToGist'
])
publishFileToGist
(
path
,
type
)
break
case
'Run'
:
_paq
.
push
([
'trackEvent'
,
'fileExplorer'
,
'runScript'
])
runScript
(
path
)
break
case
'Copy'
:
copy
(
path
,
type
)
break
case
'Paste'
:
paste
(
path
,
type
)
break
case
'Delete All'
:
deletePath
(
getPath
())
break
default
:
_paq
.
push
([
'trackEvent'
,
'fileExplorer'
,
'customAction'
,
`${item.id}/${item.name}`
])
emit
&&
emit
({
...
item
,
path
:
[
path
]
}
as
customAction
)
break
}
hideContextMenu
()
}
}
>
{
item
.
label
||
item
.
name
}
</
li
>
})
}
return
(
<
div
id=
"menuItemsContainer"
className=
"p-1 remixui_contextContainer bg-light shadow border"
style=
{
{
left
:
pageX
,
top
:
pageY
}
}
ref=
{
contextMenuRef
}
onBlur=
{
hideContextMenu
}
tabIndex=
{
500
}
{
...
otherProps
}
>
<
ul
id=
'remixui_menuitems'
>
{
menu
()
}
</
ul
>
</
div
>
)
}
export
default
FileExplorerContextMenu
libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx
0 → 100644
View file @
3550cda6
import
React
,
{
useState
,
useEffect
}
from
'react'
//eslint-disable-line
import
{
FileExplorerMenuProps
}
from
'../types'
export
const
FileExplorerMenu
=
(
props
:
FileExplorerMenuProps
)
=>
{
const
[
state
,
setState
]
=
useState
({
menuItems
:
[
{
action
:
'createNewFile'
,
title
:
'Create New File'
,
icon
:
'far fa-file'
},
{
action
:
'createNewFolder'
,
title
:
'Create New Folder'
,
icon
:
'far fa-folder'
},
{
action
:
'publishToGist'
,
title
:
'Publish all the current workspace files (only root) to a github gist'
,
icon
:
'fab fa-github'
},
{
action
:
'uploadFile'
,
title
:
'Load a local file into current workspace'
,
icon
:
'fa fa-upload'
},
{
action
:
'updateGist'
,
title
:
'Update the current [gist] explorer'
,
icon
:
'fab fa-github'
}
].
filter
(
item
=>
props
.
menuItems
&&
props
.
menuItems
.
find
((
name
)
=>
{
return
name
===
item
.
action
})),
actions
:
{}
})
useEffect
(()
=>
{
const
actions
=
{
updateGist
:
()
=>
{}
}
setState
(
prevState
=>
{
return
{
...
prevState
,
actions
}
})
},
[])
return
(
<>
<
span
className=
'remixui_label'
title=
{
props
.
title
}
data
-
path=
{
props
.
title
}
style=
{
{
fontWeight
:
'bold'
}
}
>
{
props
.
title
}
</
span
>
<
span
className=
"pl-2"
>
{
state
.
menuItems
.
map
(({
action
,
title
,
icon
},
index
)
=>
{
if
(
action
===
'uploadFile'
)
{
return
(
<
label
id=
{
action
}
data
-
id=
{
'fileExplorerUploadFile'
+
action
}
className=
{
icon
+
' mb-0 remixui_newFile'
}
title=
{
title
}
key=
{
index
}
>
<
input
id=
"fileUpload"
data
-
id=
"fileExplorerFileUpload"
type=
"file"
onChange=
{
(
e
)
=>
{
e
.
stopPropagation
()
props
.
uploadFile
(
e
.
target
)
e
.
target
.
value
=
null
}
}
multiple
/>
</
label
>
)
}
else
{
return
(
<
span
id=
{
action
}
data
-
id=
{
'fileExplorerNewFile'
+
action
}
onClick=
{
(
e
)
=>
{
e
.
stopPropagation
()
if
(
action
===
'createNewFile'
)
{
props
.
createNewFile
()
}
else
if
(
action
===
'createNewFolder'
)
{
props
.
createNewFolder
()
}
else
if
(
action
===
'publishToGist'
)
{
props
.
publishToGist
()
}
else
{
state
.
actions
[
action
]()
}
}
}
className=
{
'newFile '
+
icon
+
' remixui_newFile'
}
title=
{
title
}
key=
{
index
}
>
</
span
>
)
}
})
}
</
span
>
</>
)
}
export
default
FileExplorerMenu
libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
0 → 100644
View file @
3550cda6
This diff is collapsed.
Click to expand it.
libs/remix-ui/workspace/src/lib/css/file-explorer-context-menu.css
0 → 100644
View file @
3550cda6
.remixui_contextContainer
{
display
:
block
;
position
:
fixed
;
border-radius
:
2px
;
z-index
:
1000
;
box-shadow
:
0
0
4px
var
(
--dark
);
}
.remixui_contextContainer
:focus
{
outline
:
none
;
}
.remixui_liitem
{
padding
:
2px
;
padding-left
:
6px
;
cursor
:
pointer
;
color
:
var
(
--text-dark
);
background-color
:
var
(
--light
);
}
.remixui_liitem
:hover
{
background-color
:
var
(
--secondary
);
}
#remixui_menuitems
{
list-style
:
none
;
margin
:
0px
;
}
\ No newline at end of file
libs/remix-ui/workspace/src/lib/css/file-explorer.css
0 → 100644
View file @
3550cda6
.remixui_label
{
margin-top
:
4px
;
}
.remixui_leaf
{
overflow
:
hidden
;
text-overflow
:
ellipsis
;
width
:
90%
;
margin-bottom
:
0px
;
}
.remixui_fileexplorer
{
box-sizing
:
border-box
;
user-select
:
none
;
}
input
[
type
=
"file"
]
{
display
:
none
;
}
.remixui_folder
,
.remixui_file
{
font-size
:
14px
;
cursor
:
pointer
;
}
.remixui_file
{
padding
:
4px
;
}
.remixui_newFile
{
padding-right
:
10px
;
}
.remixui_newFile
i
{
cursor
:
pointer
;
}
.remixui_newFile
:hover
{
transform
:
scale
(
1.3
);
}
.remixui_menu
{
margin-left
:
20px
;
}
.remixui_items
{
display
:
inline
}
.remixui_remove
{
margin-left
:
auto
;
padding-left
:
5px
;
padding-right
:
5px
;
}
.remixui_activeMode
{
display
:
flex
;
width
:
100%
;
margin-right
:
10px
;
padding-right
:
19px
;
}
.remixui_activeMode
>
div
{
min-width
:
10px
;
}
ul
{
padding
:
0
;
}
libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css
0 → 100644
View file @
3550cda6
.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-right
:
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/providers/FileSystemProvider.tsx
View file @
3550cda6
...
...
@@ -104,11 +104,11 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
}
const
dispatchEmitContextMenuEvent
=
async
(
cmd
:
customAction
)
=>
{
await
emitContextMenuEvent
(
cmd
)
await
emitContextMenuEvent
(
cmd
)
()
}
const
dispatchHandleClickFile
=
async
(
path
:
string
,
type
:
'file'
|
'folder'
|
'gist'
)
=>
{
await
handleClickFile
(
path
,
type
)
await
handleClickFile
(
path
,
type
)
(
fsDispatch
)
}
useEffect
(()
=>
{
...
...
libs/remix-ui/workspace/src/lib/types/index.ts
View file @
3550cda6
import
{
MenuItems
}
from
'@remix-ui/file-explorer
'
import
{
customAction
}
from
'@remixproject/plugin-api/lib/file-system/file-panel
'
export
interface
WorkspaceProps
{
plugin
:
{
setWorkspace
:
({
name
:
string
,
isLocalhost
:
boolean
},
setEvent
:
boolean
)
=>
void
,
...
...
@@ -53,3 +53,86 @@ export interface File {
type
:
'folder'
|
'file'
|
'gist'
,
child
?:
File
[]
}
/* eslint-disable-next-line */
export
interface
FileExplorerProps
{
name
:
string
,
menuItems
?:
string
[],
focusRoot
:
boolean
,
contextMenuItems
:
MenuItems
,
removedContextMenuItems
:
MenuItems
,
displayInput
?:
boolean
,
externalUploads
?:
EventTarget
&
HTMLInputElement
,
resetFocus
?:
(
value
:
boolean
)
=>
void
,
files
:
{
[
x
:
string
]:
Record
<
string
,
File
>
}
}
export
interface
FileExplorerMenuProps
{
title
:
string
,
menuItems
:
string
[],
createNewFile
:
(
folder
?:
string
)
=>
void
,
createNewFolder
:
(
parentFolder
?:
string
)
=>
void
,
publishToGist
:
(
path
?:
string
)
=>
void
,
uploadFile
:
(
target
:
EventTarget
&
HTMLInputElement
)
=>
void
}
export
type
action
=
{
name
:
string
,
type
?:
Array
<
'folder'
|
'gist'
|
'file'
>
,
path
?:
string
[],
extension
?:
string
[],
pattern
?:
string
[],
id
:
string
,
multiselect
:
boolean
,
label
:
string
}
export
type
MenuItems
=
action
[]
export
interface
FileExplorerContextMenuProps
{
actions
:
action
[],
createNewFile
:
(
folder
?:
string
)
=>
void
,
createNewFolder
:
(
parentFolder
?:
string
)
=>
void
,
deletePath
:
(
path
:
string
|
string
[])
=>
void
,
renamePath
:
(
path
:
string
,
type
:
string
)
=>
void
,
hideContextMenu
:
()
=>
void
,
publishToGist
?:
(
path
?:
string
,
type
?:
string
)
=>
void
,
pushChangesToGist
?:
(
path
?:
string
,
type
?:
string
)
=>
void
,
publishFolderToGist
?:
(
path
?:
string
,
type
?:
string
)
=>
void
,
publishFileToGist
?:
(
path
?:
string
,
type
?:
string
)
=>
void
,
runScript
?:
(
path
:
string
)
=>
void
,
emit
?:
(
cmd
:
customAction
)
=>
void
,
pageX
:
number
,
pageY
:
number
,
path
:
string
,
type
:
string
,
focus
:
{
key
:
string
,
type
:
string
}[],
onMouseOver
?:
(...
args
)
=>
void
,
copy
?:
(
path
:
string
,
type
:
string
)
=>
void
,
paste
?:
(
destination
:
string
,
type
:
string
)
=>
void
}
export
interface
FileExplorerState
{
ctrlKey
:
boolean
newFileName
:
string
actions
:
{
id
:
string
name
:
string
type
?:
Array
<
'folder'
|
'gist'
|
'file'
>
path
?:
string
[]
extension
?:
string
[]
pattern
?:
string
[]
multiselect
:
boolean
label
:
string
}[]
focusContext
:
{
element
:
string
x
:
number
y
:
number
type
:
string
}
focusEdit
:
{
element
:
string
type
:
string
isNew
:
boolean
lastEdit
:
string
}
expandPath
:
string
[]
mouseOverElement
:
string
showContextMenu
:
boolean
reservedKeywords
:
string
[]
copyElement
:
{
key
:
string
type
:
'folder'
|
'gist'
|
'file'
}[]
}
libs/remix-ui/workspace/src/lib/utils/index.ts
0 → 100644
View file @
3550cda6
export
const
contextMenuActions
:
MenuItems
=
[{
id
:
'newFile'
,
name
:
'New File'
,
type
:
[
'folder'
,
'gist'
],
multiselect
:
false
,
label
:
''
},
{
id
:
'newFolder'
,
name
:
'New Folder'
,
type
:
[
'folder'
,
'gist'
],
multiselect
:
false
,
label
:
''
},
{
id
:
'rename'
,
name
:
'Rename'
,
type
:
[
'file'
,
'folder'
],
multiselect
:
false
,
label
:
''
},
{
id
:
'delete'
,
name
:
'Delete'
,
type
:
[
'file'
,
'folder'
,
'gist'
],
multiselect
:
false
,
label
:
''
},
{
id
:
'run'
,
name
:
'Run'
,
extension
:
[
'.js'
],
multiselect
:
false
,
label
:
''
},
{
id
:
'pushChangesToGist'
,
name
:
'Push changes to gist'
,
type
:
[
'gist'
],
multiselect
:
false
,
label
:
''
},
{
id
:
'publishFolderToGist'
,
name
:
'Publish folder to gist'
,
type
:
[
'folder'
],
multiselect
:
false
,
label
:
''
},
{
id
:
'publishFileToGist'
,
name
:
'Publish file to gist'
,
type
:
[
'file'
],
multiselect
:
false
,
label
:
''
},
{
id
:
'copy'
,
name
:
'Copy'
,
type
:
[
'folder'
,
'file'
],
multiselect
:
false
,
label
:
''
},
{
id
:
'deleteAll'
,
name
:
'Delete All'
,
type
:
[
'folder'
,
'file'
],
multiselect
:
true
,
label
:
''
}]
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