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
079cd362
Commit
079cd362
authored
Sep 22, 2021
by
ioedeveloper
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
finalize child components
parent
882251f6
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
251 additions
and
308 deletions
+251
-308
file-panel.js
apps/remix-ide/src/app/panels/file-panel.js
+5
-2
workspace.ts
libs/remix-ui/workspace/src/lib/actions/workspace.ts
+4
-0
file-explorer-context-menu.tsx
...rkspace/src/lib/components/file-explorer-context-menu.tsx
+1
-1
file-explorer.tsx
libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
+42
-198
file-label.tsx
libs/remix-ui/workspace/src/lib/components/file-label.tsx
+59
-0
file-render.tsx
libs/remix-ui/workspace/src/lib/components/file-render.tsx
+116
-0
workspace.ts
libs/remix-ui/workspace/src/lib/reducers/workspace.ts
+8
-7
remix-ui-workspace.css
libs/remix-ui/workspace/src/lib/remix-ui-workspace.css
+0
-62
remix-ui-workspace.tsx
libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
+11
-31
index.ts
libs/remix-ui/workspace/src/lib/types/index.ts
+5
-7
No files found.
apps/remix-ide/src/app/panels/file-panel.js
View file @
079cd362
...
...
@@ -138,8 +138,11 @@ module.exports = class Filepanel extends ViewPlugin {
this
.
emit
(
'displayNewFileInput'
,
dir
)
}
async
uploadFile
(
event
)
{
return
await
this
.
request
.
uploadFile
(
event
)
async
uploadFile
(
target
)
{
const
provider
=
this
.
fileManager
.
currentFileProvider
()
const
dir
=
provider
.
workspace
||
'/'
return
this
.
emit
(
'uploadFileEvent'
,
dir
,
target
)
}
async
processCreateWorkspace
(
name
)
{
...
...
libs/remix-ui/workspace/src/lib/actions/workspace.ts
View file @
079cd362
...
...
@@ -484,6 +484,10 @@ const listenOnEvents = (provider) => {
plugin
.
on
(
'filePanel'
,
'displayNewFileInput'
,
(
path
)
=>
{
addInputField
(
'file'
,
path
)(
dispatch
)
})
plugin
.
on
(
'filePanel'
,
'uploadFileEvent'
,
(
dir
:
string
,
target
)
=>
{
uploadFile
(
target
,
dir
)(
dispatch
)
})
}
export
const
initWorkspace
=
(
filePanelPlugin
)
=>
async
(
reducerDispatch
:
React
.
Dispatch
<
any
>
)
=>
{
...
...
libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx
View file @
079cd362
import
React
,
{
useRef
,
useEffect
}
from
'react'
// eslint-disable-line
import
{
action
,
FileExplorerContextMenuProps
}
from
'../types'
import
'./css/file-explorer-context-menu.css'
import
'.
.
/css/file-explorer-context-menu.css'
import
{
customAction
}
from
'@remixproject/plugin-api/lib/file-system/file-panel'
declare
global
{
...
...
libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
View file @
079cd362
...
...
@@ -3,16 +3,18 @@ import React, { useEffect, useState, useRef, useContext } from 'react' // eslint
import
{
TreeView
,
TreeViewItem
}
from
'@remix-ui/tree-view'
// eslint-disable-line
import
{
FileExplorerMenu
}
from
'./file-explorer-menu'
// eslint-disable-line
import
{
FileExplorerContextMenu
}
from
'./file-explorer-context-menu'
// eslint-disable-line
import
{
FileExplorerProps
,
File
,
MenuItems
,
FileExplorerState
}
from
'../types'
import
{
FileSystemContext
}
from
'
@remix-ui/workspace
'
import
{
FileExplorerProps
,
MenuItems
,
FileExplorerState
}
from
'../types'
import
{
FileSystemContext
}
from
'
../contexts
'
import
{
customAction
}
from
'@remixproject/plugin-api/lib/file-system/file-panel'
import
{
contextMenuActions
}
from
'../utils'
import
'./css/file-explorer.css'
import
{
checkSpecialChars
,
extractParentFromKey
,
getPathIcon
,
joinPath
}
from
'@remix-ui/helper'
import
'../css/file-explorer.css'
import
{
checkSpecialChars
,
extractNameFromKey
,
extractParentFromKey
,
joinPath
}
from
'@remix-ui/helper'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import
{
FileRender
}
from
'./file-render'
export
const
FileExplorer
=
(
props
:
FileExplorerProps
)
=>
{
const
{
name
,
focusRoot
,
contextMenuItems
,
externalUploads
,
removedContextMenuItems
,
resetFocu
s
,
files
}
=
props
const
{
name
,
contextMenuItems
,
externalUploads
,
removedContextMenuItem
s
,
files
}
=
props
const
[
state
,
setState
]
=
useState
<
FileExplorerState
>
({
ctrlKey
:
false
,
newFileName
:
''
,
...
...
@@ -36,37 +38,13 @@ export const FileExplorer = (props: FileExplorerProps) => {
copyElement
:
[]
})
const
[
canPaste
,
setCanPaste
]
=
useState
(
false
)
const
editRef
=
useRef
(
null
)
const
global
=
useContext
(
FileSystemContext
)
useEffect
(()
=>
{
if
(
global
.
fs
.
mode
===
'browser'
)
{
setState
(
prevState
=>
{
return
{
...
prevState
,
expandPath
:
[...
new
Set
([...
prevState
.
expandPath
,
...
global
.
fs
.
browser
.
expandPath
])]
}
return
{
...
prevState
,
expandPath
:
[...
new
Set
([...
prevState
.
expandPath
,
...
props
.
expandPath
])]
}
})
}
else
if
(
global
.
fs
.
mode
===
'localhost'
)
{
setState
(
prevState
=>
{
return
{
...
prevState
,
expandPath
:
[...
new
Set
([...
prevState
.
expandPath
,
...
global
.
fs
.
localhost
.
expandPath
])]
}
})
}
},
[
global
.
fs
.
browser
.
expandPath
,
global
.
fs
.
localhost
.
expandPath
])
useEffect
(()
=>
{
if
(
state
.
focusEdit
.
element
)
{
setTimeout
(()
=>
{
if
(
editRef
&&
editRef
.
current
)
{
editRef
.
current
.
focus
()
}
},
0
)
}
},
[
state
.
focusEdit
.
element
])
useEffect
(()
=>
{
if
(
focusRoot
)
{
global
.
dispatchSetFocusElement
([{
key
:
''
,
type
:
'folder'
}])
resetFocus
(
false
)
}
},
[
focusRoot
])
},
[
props
.
expandPath
])
useEffect
(()
=>
{
if
(
contextMenuItems
)
{
...
...
@@ -81,12 +59,12 @@ export const FileExplorer = (props: FileExplorerProps) => {
},
[
contextMenuItems
])
useEffect
(()
=>
{
if
(
global
.
f
s
.
focusEdit
)
{
if
(
prop
s
.
focusEdit
)
{
setState
(
prevState
=>
{
return
{
...
prevState
,
focusEdit
:
{
element
:
global
.
f
s
.
focusEdit
,
type
:
'file'
,
isNew
:
true
,
lastEdit
:
null
}
}
return
{
...
prevState
,
focusEdit
:
{
element
:
prop
s
.
focusEdit
,
type
:
'file'
,
isNew
:
true
,
lastEdit
:
null
}
}
})
}
},
[
global
.
f
s
.
focusEdit
])
},
[
prop
s
.
focusEdit
])
useEffect
(()
=>
{
if
(
externalUploads
)
{
...
...
@@ -161,22 +139,16 @@ export const FileExplorer = (props: FileExplorerProps) => {
})
}
const
extractNameFromKey
=
(
key
:
string
):
string
=>
{
const
keyPath
=
key
.
split
(
'/'
)
return
keyPath
[
keyPath
.
length
-
1
]
}
const
hasReservedKeyword
=
(
content
:
string
):
boolean
=>
{
if
(
state
.
reservedKeywords
.
findIndex
(
value
=>
content
.
startsWith
(
value
))
!==
-
1
)
return
true
else
return
false
}
const
getFocusedFolder
=
()
=>
{
if
(
global
.
f
s
.
focusElement
[
0
])
{
if
(
global
.
fs
.
focusElement
[
0
].
type
===
'folder'
&&
global
.
fs
.
focusElement
[
0
].
key
)
return
global
.
f
s
.
focusElement
[
0
].
key
else
if
(
global
.
fs
.
focusElement
[
0
].
type
===
'gist'
&&
global
.
fs
.
focusElement
[
0
].
key
)
return
global
.
f
s
.
focusElement
[
0
].
key
else
if
(
global
.
fs
.
focusElement
[
0
].
type
===
'file'
&&
global
.
fs
.
focusElement
[
0
].
key
)
return
extractParentFromKey
(
global
.
fs
.
focusElement
[
0
].
key
)
?
extractParentFromKey
(
global
.
f
s
.
focusElement
[
0
].
key
)
:
name
if
(
prop
s
.
focusElement
[
0
])
{
if
(
props
.
focusElement
[
0
].
type
===
'folder'
&&
props
.
focusElement
[
0
].
key
)
return
prop
s
.
focusElement
[
0
].
key
else
if
(
props
.
focusElement
[
0
].
type
===
'gist'
&&
props
.
focusElement
[
0
].
key
)
return
prop
s
.
focusElement
[
0
].
key
else
if
(
props
.
focusElement
[
0
].
type
===
'file'
&&
props
.
focusElement
[
0
].
key
)
return
extractParentFromKey
(
props
.
focusElement
[
0
].
key
)
?
extractParentFromKey
(
prop
s
.
focusElement
[
0
].
key
)
:
name
else
return
name
}
}
...
...
@@ -279,12 +251,12 @@ export const FileExplorer = (props: FileExplorerProps) => {
if
(
!
state
.
ctrlKey
)
{
global
.
dispatchHandleClickFile
(
path
,
type
)
}
else
{
if
(
global
.
f
s
.
focusElement
.
findIndex
(
item
=>
item
.
key
===
path
)
!==
-
1
)
{
const
focusElement
=
global
.
f
s
.
focusElement
.
filter
(
item
=>
item
.
key
!==
path
)
if
(
prop
s
.
focusElement
.
findIndex
(
item
=>
item
.
key
===
path
)
!==
-
1
)
{
const
focusElement
=
prop
s
.
focusElement
.
filter
(
item
=>
item
.
key
!==
path
)
global
.
dispatchSetFocusElement
(
focusElement
)
}
else
{
const
nonRootFocus
=
global
.
f
s
.
focusElement
.
filter
((
el
)
=>
{
return
!
(
el
.
key
===
''
&&
el
.
type
===
'folder'
)
})
const
nonRootFocus
=
prop
s
.
focusElement
.
filter
((
el
)
=>
{
return
!
(
el
.
key
===
''
&&
el
.
type
===
'folder'
)
})
nonRootFocus
.
push
({
key
:
path
,
type
})
global
.
dispatchSetFocusElement
(
nonRootFocus
)
...
...
@@ -294,12 +266,12 @@ export const FileExplorer = (props: FileExplorerProps) => {
const
handleClickFolder
=
async
(
path
:
string
,
type
:
'folder'
|
'file'
|
'gist'
)
=>
{
if
(
state
.
ctrlKey
)
{
if
(
global
.
f
s
.
focusElement
.
findIndex
(
item
=>
item
.
key
===
path
)
!==
-
1
)
{
const
focusElement
=
global
.
f
s
.
focusElement
.
filter
(
item
=>
item
.
key
!==
path
)
if
(
prop
s
.
focusElement
.
findIndex
(
item
=>
item
.
key
===
path
)
!==
-
1
)
{
const
focusElement
=
prop
s
.
focusElement
.
filter
(
item
=>
item
.
key
!==
path
)
global
.
dispatchSetFocusElement
(
focusElement
)
}
else
{
const
nonRootFocus
=
global
.
f
s
.
focusElement
.
filter
((
el
)
=>
{
return
!
(
el
.
key
===
''
&&
el
.
type
===
'folder'
)
})
const
nonRootFocus
=
prop
s
.
focusElement
.
filter
((
el
)
=>
{
return
!
(
el
.
key
===
''
&&
el
.
type
===
'folder'
)
})
nonRootFocus
.
push
({
key
:
path
,
type
})
global
.
dispatchSetFocusElement
(
nonRootFocus
)
...
...
@@ -321,14 +293,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
}
}
const
handleContextMenuFile
=
(
pageX
:
number
,
pageY
:
number
,
path
:
string
,
content
:
string
,
type
:
string
)
=>
{
if
(
!
content
)
return
setState
(
prevState
=>
{
return
{
...
prevState
,
focusContext
:
{
element
:
path
,
x
:
pageX
,
y
:
pageY
,
type
},
focusEdit
:
{
...
prevState
.
focusEdit
,
lastEdit
:
content
},
showContextMenu
:
prevState
.
focusEdit
.
element
!==
path
}
})
}
const
handleContextMenuFolder
=
(
pageX
:
number
,
pageY
:
number
,
path
:
string
,
content
:
string
,
type
:
string
)
=>
{
const
handleContextMenu
=
(
pageX
:
number
,
pageY
:
number
,
path
:
string
,
content
:
string
,
type
:
string
)
=>
{
if
(
!
content
)
return
setState
(
prevState
=>
{
return
{
...
prevState
,
focusContext
:
{
element
:
path
,
x
:
pageX
,
y
:
pageY
,
type
},
focusEdit
:
{
...
prevState
.
focusEdit
,
lastEdit
:
content
},
showContextMenu
:
prevState
.
focusEdit
.
element
!==
path
}
...
...
@@ -359,14 +324,14 @@ export const FileExplorer = (props: FileExplorerProps) => {
return
{
...
prevState
,
focusEdit
:
{
element
:
null
,
isNew
:
false
,
type
:
''
,
lastEdit
:
''
}
}
})
}
else
{
editRef
.
current
.
textContent
=
state
.
focusEdit
.
lastEdit
//
editRef.current.textContent = state.focusEdit.lastEdit
setState
(
prevState
=>
{
return
{
...
prevState
,
focusEdit
:
{
element
:
null
,
isNew
:
false
,
type
:
''
,
lastEdit
:
''
}
}
})
}
}
else
{
if
(
state
.
focusEdit
.
lastEdit
===
content
)
{
editRef
.
current
.
textContent
=
content
//
editRef.current.textContent = content
return
setState
(
prevState
=>
{
return
{
...
prevState
,
focusEdit
:
{
element
:
null
,
isNew
:
false
,
type
:
''
,
lastEdit
:
''
}
}
})
...
...
@@ -384,14 +349,14 @@ export const FileExplorer = (props: FileExplorerProps) => {
}
}
else
{
if
(
hasReservedKeyword
(
content
))
{
editRef
.
current
.
textContent
=
state
.
focusEdit
.
lastEdit
//
editRef.current.textContent = state.focusEdit.lastEdit
global
.
modal
(
'Reserved Keyword'
,
`File name contains remix reserved keywords. '
${
content
}
'`
,
'Close'
,
()
=>
{})
}
else
{
const
oldPath
:
string
=
state
.
focusEdit
.
element
const
oldName
=
extractNameFromKey
(
oldPath
)
const
newPath
=
oldPath
.
replace
(
oldName
,
content
)
editRef
.
current
.
textContent
=
extractNameFromKey
(
oldPath
)
//
editRef.current.textContent = extractNameFromKey(oldPath)
renamePath
(
oldPath
,
newPath
)
}
}
...
...
@@ -425,25 +390,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
editModeOn
(
parentFolder
+
'/blank'
,
'folder'
,
true
)
}
const
handleEditInput
=
(
event
)
=>
{
if
(
event
.
which
===
13
)
{
event
.
preventDefault
()
editModeOff
(
editRef
.
current
.
innerText
)
}
}
const
handleMouseOver
=
(
path
:
string
)
=>
{
setState
(
prevState
=>
{
return
{
...
prevState
,
mouseOverElement
:
path
}
})
}
const
handleMouseOut
=
()
=>
{
setState
(
prevState
=>
{
return
{
...
prevState
,
mouseOverElement
:
null
}
})
}
const
handleCopyClick
=
(
path
:
string
,
type
:
'folder'
|
'gist'
|
'file'
)
=>
{
setState
(
prevState
=>
{
return
{
...
prevState
,
copyElement
:
[{
key
:
path
,
type
}]
}
...
...
@@ -470,113 +416,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
)
}
const
label
=
(
file
:
File
)
=>
{
const
isEditable
=
(
state
.
focusEdit
.
element
===
file
.
path
)
||
(
global
.
fs
.
focusEdit
===
file
.
path
)
return
(
<
div
className=
'remixui_items d-inline-block w-100'
ref=
{
isEditable
?
editRef
:
null
}
suppressContentEditableWarning=
{
true
}
contentEditable=
{
isEditable
}
onKeyDown=
{
handleEditInput
}
onBlur=
{
(
e
)
=>
{
e
.
stopPropagation
()
editModeOff
(
editRef
.
current
.
innerText
)
}
}
>
<
span
title=
{
file
.
path
}
className=
{
'remixui_label '
+
(
file
.
isDirectory
?
'folder'
:
'remixui_leaf'
)
}
data
-
path=
{
file
.
path
}
>
{
file
.
name
}
</
span
>
</
div
>
)
}
const
renderFiles
=
(
file
:
File
,
index
:
number
)
=>
{
if
(
!
file
||
!
file
.
path
||
typeof
file
===
'string'
||
typeof
file
===
'number'
||
typeof
file
===
'boolean'
)
return
const
labelClass
=
state
.
focusEdit
.
element
===
file
.
path
?
'bg-light'
:
global
.
fs
.
focusElement
.
findIndex
(
item
=>
item
.
key
===
file
.
path
)
!==
-
1
?
'bg-secondary'
:
state
.
mouseOverElement
===
file
.
path
?
'bg-light border'
:
(
state
.
focusContext
.
element
===
file
.
path
)
&&
(
state
.
focusEdit
.
element
!==
file
.
path
)
?
'bg-light border'
:
''
const
icon
=
getPathIcon
(
file
.
path
)
const
spreadProps
=
{
onClick
:
(
e
)
=>
e
.
stopPropagation
()
}
if
(
file
.
isDirectory
)
{
return
(
<
TreeViewItem
id=
{
`treeViewItem${file.path}`
}
iconX=
'pr-3 fa fa-folder'
iconY=
'pr-3 fa fa-folder-open'
key=
{
`${file.path + index}`
}
label=
{
label
(
file
)
}
onClick=
{
(
e
)
=>
{
e
.
stopPropagation
()
if
(
state
.
focusEdit
.
element
!==
file
.
path
)
handleClickFolder
(
file
.
path
,
file
.
type
)
}
}
onContextMenu=
{
(
e
)
=>
{
e
.
preventDefault
()
e
.
stopPropagation
()
handleContextMenuFolder
(
e
.
pageX
,
e
.
pageY
,
file
.
path
,
e
.
target
.
textContent
,
file
.
type
)
}
}
labelClass=
{
labelClass
}
controlBehaviour=
{
state
.
ctrlKey
}
expand=
{
state
.
expandPath
.
includes
(
file
.
path
)
}
onMouseOver=
{
(
e
)
=>
{
e
.
stopPropagation
()
handleMouseOver
(
file
.
path
)
}
}
onMouseOut=
{
(
e
)
=>
{
e
.
stopPropagation
()
if
(
state
.
mouseOverElement
===
file
.
path
)
handleMouseOut
()
}
}
>
{
file
.
child
?
<
TreeView
id=
{
`treeView${file.path}`
}
key=
{
`treeView${file.path}`
}
{
...
spreadProps
}
>
{
Object
.
keys
(
file
.
child
).
map
((
key
,
index
)
=>
{
return
renderFiles
(
file
.
child
[
key
],
index
)
})
}
</
TreeView
>
:
<
TreeView
id=
{
`treeView${file.path}`
}
key=
{
`treeView${file.path}`
}
{
...
spreadProps
}
/>
}
</
TreeViewItem
>
)
}
else
{
return
(
<
TreeViewItem
id=
{
`treeViewItem${file.path}`
}
key=
{
`treeView${file.path}`
}
label=
{
label
(
file
)
}
onClick=
{
(
e
)
=>
{
e
.
stopPropagation
()
if
(
state
.
focusEdit
.
element
!==
file
.
path
)
handleClickFile
(
file
.
path
,
file
.
type
)
}
}
onContextMenu=
{
(
e
)
=>
{
e
.
preventDefault
()
e
.
stopPropagation
()
handleContextMenuFile
(
e
.
pageX
,
e
.
pageY
,
file
.
path
,
e
.
target
.
textContent
,
file
.
type
)
}
}
icon=
{
icon
}
labelClass=
{
labelClass
}
onMouseOver=
{
(
e
)
=>
{
e
.
stopPropagation
()
handleMouseOver
(
file
.
path
)
}
}
onMouseOut=
{
(
e
)
=>
{
e
.
stopPropagation
()
if
(
state
.
mouseOverElement
===
file
.
path
)
handleMouseOut
()
}
}
/>
)
}
}
return
(
<
div
>
<
TreeView
id=
'treeView'
>
...
...
@@ -597,7 +436,6 @@ export const FileExplorer = (props: FileExplorerProps) => {
setState
(
prevState
=>
{
return
{
...
prevState
,
expandPath
}
})
resetFocus
(
true
)
}
}
>
<
FileExplorerMenu
title=
{
''
}
...
...
@@ -613,9 +451,19 @@ export const FileExplorer = (props: FileExplorerProps) => {
<
div
className=
'pb-2'
>
<
TreeView
id=
'treeViewMenu'
>
{
files
[
props
.
name
]
&&
Object
.
keys
(
files
[
props
.
name
]).
map
((
key
,
index
)
=>
{
return
renderFiles
(
files
[
props
.
name
][
key
],
index
)
})
files
[
props
.
name
]
&&
Object
.
keys
(
files
[
props
.
name
]).
map
((
key
,
index
)
=>
<
FileRender
file=
{
files
[
props
.
name
][
key
]
}
index=
{
index
}
focusContext=
{
state
.
focusContext
}
focusEdit=
{
state
.
focusEdit
}
focusElement=
{
props
.
focusElement
}
ctrlKey=
{
state
.
ctrlKey
}
expandPath=
{
state
.
expandPath
}
editModeOff=
{
editModeOff
}
handleClickFile=
{
handleClickFile
}
handleClickFolder=
{
handleClickFolder
}
handleContextMenu=
{
handleContextMenu
}
/>)
}
</
TreeView
>
</
div
>
...
...
@@ -623,7 +471,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
</
TreeView
>
{
state
.
showContextMenu
&&
<
FileExplorerContextMenu
actions=
{
global
.
f
s
.
focusElement
.
length
>
1
?
state
.
actions
.
filter
(
item
=>
item
.
multiselect
)
:
state
.
actions
.
filter
(
item
=>
!
item
.
multiselect
)
}
actions=
{
prop
s
.
focusElement
.
length
>
1
?
state
.
actions
.
filter
(
item
=>
item
.
multiselect
)
:
state
.
actions
.
filter
(
item
=>
!
item
.
multiselect
)
}
hideContextMenu=
{
hideContextMenu
}
createNewFile=
{
handleNewFileInput
}
createNewFolder=
{
handleNewFolderInput
}
...
...
@@ -637,11 +485,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
pageY=
{
state
.
focusContext
.
y
}
path=
{
state
.
focusContext
.
element
}
type=
{
state
.
focusContext
.
type
}
focus=
{
global
.
fs
.
focusElement
}
onMouseOver=
{
(
e
)
=>
{
e
.
stopPropagation
()
handleMouseOver
(
state
.
focusContext
.
element
)
}
}
focus=
{
props
.
focusElement
}
pushChangesToGist=
{
pushChangesToGist
}
publishFolderToGist=
{
publishFolderToGist
}
publishFileToGist=
{
publishFileToGist
}
...
...
libs/remix-ui/workspace/src/lib/components/file-label.tsx
0 → 100644
View file @
079cd362
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import
React
,
{
useEffect
,
useRef
}
from
'react'
import
{
FileType
}
from
'../types'
export
interface
FileLabelProps
{
file
:
FileType
,
focusEdit
:
{
element
:
string
type
:
string
isNew
:
boolean
lastEdit
:
string
}
editModeOff
:
(
content
:
string
)
=>
void
}
export
const
FileLabel
=
(
props
:
FileLabelProps
)
=>
{
const
{
file
,
focusEdit
,
editModeOff
}
=
props
const
isEditable
=
focusEdit
.
element
===
file
.
path
const
labelRef
=
useRef
(
null
)
useEffect
(()
=>
{
if
(
isEditable
)
{
setTimeout
(()
=>
{
labelRef
.
current
.
focus
()
},
0
)
}
},
[
isEditable
])
const
handleEditInput
=
(
event
:
React
.
KeyboardEvent
<
HTMLDivElement
>
)
=>
{
if
(
event
.
which
===
13
)
{
event
.
preventDefault
()
editModeOff
(
labelRef
.
current
.
innerText
)
}
}
const
handleEditBlur
=
(
event
:
React
.
SyntheticEvent
)
=>
{
event
.
stopPropagation
()
editModeOff
(
labelRef
.
current
.
innerText
)
}
return
(
<
div
className=
'remixui_items d-inline-block w-100'
ref=
{
labelRef
}
suppressContentEditableWarning=
{
true
}
contentEditable=
{
isEditable
}
onKeyDown=
{
handleEditInput
}
onBlur=
{
handleEditBlur
}
>
<
span
title=
{
file
.
path
}
className=
{
'remixui_label '
+
(
file
.
isDirectory
?
'folder'
:
'remixui_leaf'
)
}
data
-
path=
{
file
.
path
}
>
{
file
.
name
}
</
span
>
</
div
>
)
}
libs/remix-ui/workspace/src/lib/components/file-render.tsx
0 → 100644
View file @
079cd362
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import
React
,
{
SyntheticEvent
,
useState
}
from
'react'
import
{
FileType
}
from
'../types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import
{
TreeView
,
TreeViewItem
}
from
'@remix-ui/tree-view'
import
{
getPathIcon
}
from
'@remix-ui/helper'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import
{
FileLabel
}
from
'./file-label'
export
interface
RenderFileProps
{
file
:
FileType
,
index
:
number
,
focusEdit
:
{
element
:
string
,
type
:
string
,
isNew
:
boolean
,
lastEdit
:
string
},
focusElement
:
{
key
:
string
,
type
:
'file'
|
'folder'
|
'gist'
}[],
focusContext
:
{
element
:
string
,
x
:
number
,
y
:
number
,
type
:
string
},
ctrlKey
:
boolean
,
expandPath
:
string
[],
editModeOff
:
(
content
:
string
)
=>
void
,
handleClickFolder
:
(
path
:
string
,
type
:
string
)
=>
void
,
handleClickFile
:
(
path
:
string
,
type
:
string
)
=>
void
,
handleContextMenu
:
(
pageX
:
number
,
pageY
:
number
,
path
:
string
,
content
:
string
,
type
:
string
)
=>
void
}
export
const
FileRender
=
(
props
:
RenderFileProps
)
=>
{
const
{
file
}
=
props
if
(
!
file
||
!
file
.
path
||
typeof
file
===
'string'
||
typeof
file
===
'number'
||
typeof
file
===
'boolean'
)
return
const
[
hover
,
setHover
]
=
useState
<
boolean
>
(
false
)
const
labelClass
=
props
.
focusEdit
.
element
===
file
.
path
?
'bg-light'
:
props
.
focusElement
.
findIndex
(
item
=>
item
.
key
===
file
.
path
)
!==
-
1
?
'bg-secondary'
:
hover
?
'bg-light border'
:
(
props
.
focusContext
.
element
===
file
.
path
)
&&
(
props
.
focusEdit
.
element
!==
file
.
path
)
?
'bg-light border'
:
''
const
icon
=
getPathIcon
(
file
.
path
)
const
spreadProps
=
{
onClick
:
(
e
)
=>
e
.
stopPropagation
()
}
const
handleFolderClick
=
(
event
:
SyntheticEvent
)
=>
{
event
.
stopPropagation
()
if
(
props
.
focusEdit
.
element
!==
file
.
path
)
props
.
handleClickFolder
(
file
.
path
,
file
.
type
)
}
const
handleFileClick
=
(
event
:
SyntheticEvent
)
=>
{
event
.
stopPropagation
()
if
(
props
.
focusEdit
.
element
!==
file
.
path
)
props
.
handleClickFile
(
file
.
path
,
file
.
type
)
}
const
handleContextMenu
=
(
event
:
PointerEvent
)
=>
{
event
.
preventDefault
()
event
.
stopPropagation
()
props
.
handleContextMenu
(
event
.
pageX
,
event
.
pageY
,
file
.
path
,
(
event
.
target
as
HTMLElement
).
textContent
,
file
.
type
)
}
const
handleMouseOut
=
(
event
:
SyntheticEvent
)
=>
{
event
.
stopPropagation
()
setHover
(
false
)
}
const
handleMouseOver
=
(
event
:
SyntheticEvent
)
=>
{
event
.
stopPropagation
()
setHover
(
true
)
}
if
(
file
.
isDirectory
)
{
return
(
<
TreeViewItem
id=
{
`treeViewItem${file.path}`
}
iconX=
'pr-3 fa fa-folder'
iconY=
'pr-3 fa fa-folder-open'
key=
{
`${file.path + props.index}`
}
label=
{
<
FileLabel
file=
{
file
}
focusEdit=
{
props
.
focusEdit
}
editModeOff=
{
props
.
editModeOff
}
/>
}
onClick=
{
handleFolderClick
}
onContextMenu=
{
handleContextMenu
}
labelClass=
{
labelClass
}
controlBehaviour=
{
props
.
ctrlKey
}
expand=
{
props
.
expandPath
.
includes
(
file
.
path
)
}
onMouseOver=
{
handleMouseOver
}
onMouseOut=
{
handleMouseOut
}
>
{
file
.
child
?
<
TreeView
id=
{
`treeView${file.path}`
}
key=
{
`treeView${file.path}`
}
{
...
spreadProps
}
>
{
Object
.
keys
(
file
.
child
).
map
((
key
,
index
)
=>
<
FileRender
file=
{
file
.
child
[
key
]
}
index=
{
index
}
focusContext=
{
props
.
focusContext
}
focusEdit=
{
props
.
focusEdit
}
focusElement=
{
props
.
focusElement
}
ctrlKey=
{
props
.
ctrlKey
}
editModeOff=
{
props
.
editModeOff
}
handleClickFile=
{
props
.
handleClickFile
}
handleClickFolder=
{
props
.
handleClickFolder
}
handleContextMenu=
{
props
.
handleContextMenu
}
expandPath=
{
props
.
expandPath
}
/>)
}
</
TreeView
>
:
<
TreeView
id=
{
`treeView${file.path}`
}
key=
{
`treeView${file.path}`
}
{
...
spreadProps
}
/>
}
</
TreeViewItem
>
)
}
else
{
return
(
<
TreeViewItem
id=
{
`treeViewItem${file.path}`
}
key=
{
`treeView${file.path}`
}
label=
{
<
FileLabel
file=
{
file
}
focusEdit=
{
props
.
focusEdit
}
editModeOff=
{
props
.
editModeOff
}
/>
}
onClick=
{
handleFileClick
}
onContextMenu=
{
handleContextMenu
}
icon=
{
icon
}
labelClass=
{
labelClass
}
onMouseOver=
{
handleMouseOver
}
onMouseOut=
{
handleMouseOut
}
/>
)
}
}
libs/remix-ui/workspace/src/lib/reducers/workspace.ts
View file @
079cd362
import
{
extractNameFromKey
}
from
'@remix-ui/helper'
import
{
FileType
}
from
'../types'
import
*
as
_
from
'lodash'
interface
Action
{
type
:
string
...
...
@@ -8,7 +9,7 @@ export interface BrowserState {
browser
:
{
currentWorkspace
:
string
,
workspaces
:
string
[],
files
:
{
[
x
:
string
]:
Record
<
string
,
File
>
},
files
:
{
[
x
:
string
]:
Record
<
string
,
File
Type
>
},
expandPath
:
string
[]
isRequesting
:
boolean
,
isSuccessful
:
boolean
,
...
...
@@ -16,7 +17,7 @@ export interface BrowserState {
},
localhost
:
{
sharedFolder
:
string
,
files
:
{
[
x
:
string
]:
Record
<
string
,
File
>
},
files
:
{
[
x
:
string
]:
Record
<
string
,
File
Type
>
},
expandPath
:
string
[],
isRequesting
:
boolean
,
isSuccessful
:
boolean
,
...
...
@@ -469,7 +470,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
}
const
fileAdded
=
(
state
:
BrowserState
,
path
:
string
):
{
[
x
:
string
]:
Record
<
string
,
File
>
}
=>
{
const
fileAdded
=
(
state
:
BrowserState
,
path
:
string
):
{
[
x
:
string
]:
Record
<
string
,
File
Type
>
}
=>
{
let
files
=
state
.
mode
===
'browser'
?
state
.
browser
.
files
:
state
.
localhost
.
files
const
_path
=
splitPath
(
state
,
path
)
...
...
@@ -482,7 +483,7 @@ const fileAdded = (state: BrowserState, path: string): { [x: string]: Record<str
return
files
}
const
fileRemoved
=
(
state
:
BrowserState
,
path
:
string
):
{
[
x
:
string
]:
Record
<
string
,
File
>
}
=>
{
const
fileRemoved
=
(
state
:
BrowserState
,
path
:
string
):
{
[
x
:
string
]:
Record
<
string
,
File
Type
>
}
=>
{
const
files
=
state
.
mode
===
'browser'
?
state
.
browser
.
files
:
state
.
localhost
.
files
const
_path
=
splitPath
(
state
,
path
)
...
...
@@ -491,7 +492,7 @@ const fileRemoved = (state: BrowserState, path: string): { [x: string]: Record<s
}
// IDEA: Modify function to remove blank input field without fetching content
const
fetchDirectoryContent
=
(
state
:
BrowserState
,
payload
:
{
fileTree
,
path
:
string
,
type
?:
'file'
|
'folder'
},
deletePath
?:
string
):
{
[
x
:
string
]:
Record
<
string
,
File
>
}
=>
{
const
fetchDirectoryContent
=
(
state
:
BrowserState
,
payload
:
{
fileTree
,
path
:
string
,
type
?:
'file'
|
'folder'
},
deletePath
?:
string
):
{
[
x
:
string
]:
Record
<
string
,
File
Type
>
}
=>
{
if
(
!
payload
.
fileTree
)
return
state
.
mode
===
'browser'
?
state
.
browser
.
files
:
state
[
state
.
mode
].
files
if
(
state
.
mode
===
'browser'
)
{
if
(
payload
.
path
===
state
.
browser
.
currentWorkspace
)
{
...
...
@@ -538,7 +539,7 @@ const fetchDirectoryContent = (state: BrowserState, payload: { fileTree, path: s
}
}
const
fetchWorkspaceDirectoryContent
=
(
state
:
BrowserState
,
payload
:
{
fileTree
,
path
:
string
}):
{
[
x
:
string
]:
Record
<
string
,
File
>
}
=>
{
const
fetchWorkspaceDirectoryContent
=
(
state
:
BrowserState
,
payload
:
{
fileTree
,
path
:
string
}):
{
[
x
:
string
]:
Record
<
string
,
File
Type
>
}
=>
{
if
(
state
.
mode
===
'browser'
)
{
const
files
=
normalize
(
payload
.
fileTree
,
payload
.
path
)
...
...
@@ -548,7 +549,7 @@ const fetchWorkspaceDirectoryContent = (state: BrowserState, payload: { fileTree
}
}
const
normalize
=
(
filesList
,
directory
?:
string
,
newInputType
?:
'folder'
|
'file'
):
Record
<
string
,
File
>
=>
{
const
normalize
=
(
filesList
,
directory
?:
string
,
newInputType
?:
'folder'
|
'file'
):
Record
<
string
,
File
Type
>
=>
{
const
folders
=
{}
const
files
=
{}
...
...
libs/remix-ui/workspace/src/lib/remix-ui-workspace.css
deleted
100644 → 0
View file @
882251f6
.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/remix-ui-workspace.tsx
View file @
079cd362
import
React
,
{
useState
,
useEffect
,
useRef
,
useContext
}
from
'react'
// eslint-disable-line
import
{
FileExplorer
}
from
'./components/file-explorer'
// eslint-disable-line
import
'./remix-ui-workspace.css'
import
'./
css/
remix-ui-workspace.css'
import
{
WorkspaceProps
,
WorkspaceState
}
from
'./types'
import
{
FileSystemContext
}
from
'./contexts'
...
...
@@ -9,12 +9,9 @@ const canUpload = window.File || window.FileReader || window.FileList || window.
export
function
Workspace
(
props
:
WorkspaceProps
)
{
const
LOCALHOST
=
' - connect to localhost - '
const
NO_WORKSPACE
=
' - none - '
const
[
state
,
setState
]
=
useState
<
WorkspaceState
>
({
reset
:
false
,
const
[
state
]
=
useState
<
WorkspaceState
>
({
hideRemixdExplorer
:
true
,
displayNewFile
:
false
,
externalUploads
:
null
,
uploadFileEvent
:
null
,
loadingLocalhost
:
false
})
const
[
currentWorkspace
,
setCurrentWorkspace
]
=
useState
<
string
>
(
NO_WORKSPACE
)
...
...
@@ -50,19 +47,6 @@ export function Workspace (props: WorkspaceProps) {
return
createWorkspace
()
}
// props.plugin.request.createNewFile = async () => {
// if (!state.workspaces.length) await createNewWorkspace('default_workspace')
// props.plugin.resetNewFile()
// }
// props.plugin.request.uploadFile = async (target: EventTarget & HTMLInputElement) => {
// if (!state.workspaces.length) await createNewWorkspace('default_workspace')
// setState(prevState => {
// return { ...prevState, uploadFileEvent: target }
// })
// }
props
.
plugin
.
request
.
getCurrentWorkspace
=
()
=>
{
return
{
name
:
currentWorkspace
,
isLocalhost
:
currentWorkspace
===
LOCALHOST
,
absolutePath
:
`
${
props
.
plugin
.
workspace
.
workspacesPath
}
/
${
currentWorkspace
}
`
}
}
...
...
@@ -120,10 +104,8 @@ export function Workspace (props: WorkspaceProps) {
}
/** ** ****/
const
resetFocus
=
(
reset
)
=>
{
setState
(
prevState
=>
{
return
{
...
prevState
,
reset
}
})
const
resetFocus
=
()
=>
{
global
.
dispatchSetFocusElement
([{
key
:
''
,
type
:
'folder'
}])
}
const
switchWorkspace
=
async
(
name
:
string
)
=>
{
...
...
@@ -153,7 +135,7 @@ export function Workspace (props: WorkspaceProps) {
return
(
<
div
className=
'remixui_container'
>
<
div
className=
'remixui_fileexplorer'
onClick=
{
()
=>
resetFocus
(
true
)
}
>
<
div
className=
'remixui_fileexplorer'
onClick=
{
resetFocus
}
>
<
div
>
<
header
>
<
div
className=
"mb-2"
>
...
...
@@ -214,14 +196,12 @@ export function Workspace (props: WorkspaceProps) {
<
FileExplorer
name=
{
currentWorkspace
}
menuItems=
{
[
'createNewFile'
,
'createNewFolder'
,
'publishToGist'
,
canUpload
?
'uploadFile'
:
''
]
}
plugin=
{
props
.
plugin
}
focusRoot=
{
state
.
reset
}
contextMenuItems=
{
props
.
plugin
.
registeredMenuItems
}
removedContextMenuItems=
{
props
.
plugin
.
removedMenuItems
}
displayInput=
{
state
.
displayNewFile
}
externalUploads=
{
state
.
uploadFileEvent
}
resetFocus=
{
resetFocus
}
files=
{
global
.
fs
.
browser
.
files
}
expandPath=
{
global
.
fs
.
browser
.
expandPath
}
focusEdit=
{
global
.
fs
.
focusEdit
}
focusElement=
{
global
.
fs
.
focusElement
}
/>
}
</
div
>
...
...
@@ -232,12 +212,12 @@ export function Workspace (props: WorkspaceProps) {
<
FileExplorer
name=
'localhost'
menuItems=
{
[
'createNewFile'
,
'createNewFolder'
]
}
plugin=
{
props
.
plugin
}
focusRoot=
{
state
.
reset
}
contextMenuItems=
{
props
.
plugin
.
registeredMenuItems
}
removedContextMenuItems=
{
props
.
plugin
.
removedMenuItems
}
resetFocus=
{
resetFocus
}
files=
{
global
.
fs
.
localhost
.
files
}
expandPath=
{
global
.
fs
.
localhost
.
expandPath
}
focusEdit=
{
global
.
fs
.
focusEdit
}
focusElement=
{
global
.
fs
.
focusElement
}
/>
}
</
div
>
...
...
libs/remix-ui/workspace/src/lib/types/index.ts
View file @
079cd362
...
...
@@ -28,11 +28,8 @@ export interface WorkspaceProps {
}
}
export
interface
WorkspaceState
{
reset
:
boolean
hideRemixdExplorer
:
boolean
displayNewFile
:
boolean
externalUploads
:
EventTarget
&
HTMLInputElement
uploadFileEvent
:
EventTarget
&
HTMLInputElement
loadingLocalhost
:
boolean
}
...
...
@@ -46,7 +43,7 @@ export interface Modal {
cancelFn
:
()
=>
void
}
export
interface
File
{
export
interface
File
Type
{
path
:
string
,
name
:
string
,
isDirectory
:
boolean
,
...
...
@@ -58,13 +55,14 @@ export interface File {
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
>
}
files
:
{
[
x
:
string
]:
Record
<
string
,
FileType
>
},
expandPath
:
string
[],
focusEdit
:
string
,
focusElement
:
{
key
:
string
,
type
:
'file'
|
'folder'
|
'gist'
}[]
}
export
interface
FileExplorerMenuProps
{
...
...
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