|
|
|
@ -1,7 +1,9 @@ |
|
|
|
|
import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line
|
|
|
|
|
import { Dropdown } from 'react-bootstrap' |
|
|
|
|
import { CustomMenu, CustomToggle } from './components/custom-dropdown' |
|
|
|
|
import { FileExplorer } from './components/file-explorer' // eslint-disable-line
|
|
|
|
|
import './css/remix-ui-workspace.css' |
|
|
|
|
import { FileSystemContext } from './contexts' |
|
|
|
|
import './css/remix-ui-workspace.css' |
|
|
|
|
|
|
|
|
|
const canUpload = window.File || window.FileReader || window.FileList || window.Blob |
|
|
|
|
|
|
|
|
@ -9,10 +11,12 @@ export function Workspace () { |
|
|
|
|
const LOCALHOST = ' - connect to localhost - ' |
|
|
|
|
const NO_WORKSPACE = ' - none - ' |
|
|
|
|
const [currentWorkspace, setCurrentWorkspace] = useState<string>(NO_WORKSPACE) |
|
|
|
|
const [selectedWorkspace, setSelectedWorkspace] = useState<{ name: string, isGitRepo: boolean}>(null) |
|
|
|
|
const global = useContext(FileSystemContext) |
|
|
|
|
const workspaceRenameInput = useRef() |
|
|
|
|
const workspaceCreateInput = useRef() |
|
|
|
|
const workspaceCreateTemplateInput = useRef() |
|
|
|
|
const cloneUrlRef = useRef<HTMLInputElement>() |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
resetFocus() |
|
|
|
@ -30,15 +34,21 @@ export function Workspace () { |
|
|
|
|
}, [global.fs.browser.currentWorkspace, global.fs.localhost.sharedFolder, global.fs.mode]) |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
if (global.fs.browser.currentWorkspace && !global.fs.browser.workspaces.includes(global.fs.browser.currentWorkspace)) { |
|
|
|
|
if (global.fs.browser.currentWorkspace && !global.fs.browser.workspaces.find(({ name }) => name === global.fs.browser.currentWorkspace)) { |
|
|
|
|
if (global.fs.browser.workspaces.length > 0) { |
|
|
|
|
switchWorkspace(global.fs.browser.workspaces[global.fs.browser.workspaces.length - 1]) |
|
|
|
|
switchWorkspace(global.fs.browser.workspaces[global.fs.browser.workspaces.length - 1].name) |
|
|
|
|
} else { |
|
|
|
|
switchWorkspace(NO_WORKSPACE) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}, [global.fs.browser.workspaces]) |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
const workspace = global.fs.browser.workspaces.find(workspace => workspace.name === currentWorkspace) |
|
|
|
|
|
|
|
|
|
setSelectedWorkspace(workspace) |
|
|
|
|
}, [currentWorkspace]) |
|
|
|
|
|
|
|
|
|
const renameCurrentWorkspace = () => { |
|
|
|
|
global.modal('Rename Current Workspace', renameModalMessage(), 'OK', onFinishRenameWorkspace, '') |
|
|
|
|
} |
|
|
|
@ -51,6 +61,10 @@ export function Workspace () { |
|
|
|
|
global.modal('Delete Current Workspace', 'Are you sure to delete the current workspace?', 'OK', onFinishDeleteWorkspace, '') |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const cloneGitRepository = () => { |
|
|
|
|
global.modal('Clone Git Repository', cloneModalMessage(), 'OK', handleTypingUrl, '') |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const downloadWorkspaces = async () => { |
|
|
|
|
try { |
|
|
|
|
await global.dispatchHandleDownloadFiles() |
|
|
|
@ -124,6 +138,16 @@ export function Workspace () { |
|
|
|
|
workspaceCreateInput.current.value = `${workspaceCreateTemplateInput.current.value || 'remixDefault'}_${Date.now()}` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const handleTypingUrl = () => { |
|
|
|
|
const url = cloneUrlRef.current.value |
|
|
|
|
|
|
|
|
|
if (url) { |
|
|
|
|
global.dispatchCloneRepository(url) |
|
|
|
|
} else { |
|
|
|
|
global.modal('Clone Git Repository', 'Please provide a valid git repository url.', 'OK', () => {}, '') |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const createModalMessage = () => { |
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
@ -149,6 +173,14 @@ export function Workspace () { |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const cloneModalMessage = () => { |
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
<input type="text" data-id="modalDialogCustomPromptTextClone" placeholder='Enter git repository url' ref={cloneUrlRef} className="form-control" /> |
|
|
|
|
</> |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<div className='remixui_container'> |
|
|
|
|
<div className='remixui_fileexplorer' data-id="remixUIWorkspaceExplorer" onClick={resetFocus}> |
|
|
|
@ -158,111 +190,142 @@ export function Workspace () { |
|
|
|
|
<label className="form-check-label" htmlFor="workspacesSelect"> |
|
|
|
|
Workspaces |
|
|
|
|
</label> |
|
|
|
|
<span className="remixui_menu"> |
|
|
|
|
<span |
|
|
|
|
hidden={currentWorkspace === LOCALHOST} |
|
|
|
|
id='workspaceCreate' |
|
|
|
|
data-id='workspaceCreate' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
createWorkspace() |
|
|
|
|
}} |
|
|
|
|
className='far fa-plus-square remixui_menuicon' |
|
|
|
|
title='Create'> |
|
|
|
|
</span> |
|
|
|
|
<span |
|
|
|
|
hidden={currentWorkspace === LOCALHOST || currentWorkspace === NO_WORKSPACE} |
|
|
|
|
id='workspaceRename' |
|
|
|
|
data-id='workspaceRename' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
renameCurrentWorkspace() |
|
|
|
|
}} |
|
|
|
|
className='far fa-edit remixui_menuicon' |
|
|
|
|
title='Rename'> |
|
|
|
|
</span> |
|
|
|
|
<span |
|
|
|
|
hidden={currentWorkspace === LOCALHOST || currentWorkspace === NO_WORKSPACE} |
|
|
|
|
id='workspaceDelete' |
|
|
|
|
data-id='workspaceDelete' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
deleteCurrentWorkspace() |
|
|
|
|
}} |
|
|
|
|
className='fas fa-trash remixui_menuicon' |
|
|
|
|
title='Delete'> |
|
|
|
|
</span> |
|
|
|
|
<span |
|
|
|
|
hidden={currentWorkspace === LOCALHOST || currentWorkspace === NO_WORKSPACE} |
|
|
|
|
id='workspacesDownload' |
|
|
|
|
data-id='workspacesDownload' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
downloadWorkspaces() |
|
|
|
|
}} |
|
|
|
|
className='far fa-download remixui_menuicon' |
|
|
|
|
title='Download Workspaces'> |
|
|
|
|
<span className="remixui_menu"> |
|
|
|
|
<span |
|
|
|
|
hidden={currentWorkspace === LOCALHOST} |
|
|
|
|
id='workspaceCreate' |
|
|
|
|
data-id='workspaceCreate' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
createWorkspace() |
|
|
|
|
}} |
|
|
|
|
className='far fa-plus-square remixui_menuicon' |
|
|
|
|
title='Create'> |
|
|
|
|
</span> |
|
|
|
|
<span |
|
|
|
|
hidden={currentWorkspace === LOCALHOST || currentWorkspace === NO_WORKSPACE} |
|
|
|
|
id='workspaceRename' |
|
|
|
|
data-id='workspaceRename' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
renameCurrentWorkspace() |
|
|
|
|
}} |
|
|
|
|
className='far fa-edit remixui_menuicon' |
|
|
|
|
title='Rename'> |
|
|
|
|
</span> |
|
|
|
|
<span |
|
|
|
|
hidden={currentWorkspace === LOCALHOST || currentWorkspace === NO_WORKSPACE} |
|
|
|
|
id='workspaceDelete' |
|
|
|
|
data-id='workspaceDelete' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
deleteCurrentWorkspace() |
|
|
|
|
}} |
|
|
|
|
className='fas fa-trash remixui_menuicon' |
|
|
|
|
title='Delete'> |
|
|
|
|
</span> |
|
|
|
|
<span |
|
|
|
|
hidden={currentWorkspace === LOCALHOST || currentWorkspace === NO_WORKSPACE} |
|
|
|
|
id='workspacesDownload' |
|
|
|
|
data-id='workspacesDownload' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
downloadWorkspaces() |
|
|
|
|
}} |
|
|
|
|
className='far fa-download remixui_menuicon' |
|
|
|
|
title='Download Workspaces'> |
|
|
|
|
</span> |
|
|
|
|
<span |
|
|
|
|
hidden={currentWorkspace === LOCALHOST} |
|
|
|
|
id='workspacesRestore' |
|
|
|
|
data-id='workspacesRestore' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
restoreBackup() |
|
|
|
|
}} |
|
|
|
|
className='far fa-upload remixui_menuicon' |
|
|
|
|
title='Restore Workspaces Backup'> |
|
|
|
|
</span> |
|
|
|
|
<span |
|
|
|
|
id='cloneGitRepository' |
|
|
|
|
data-id='cloneGitRepository' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
cloneGitRepository() |
|
|
|
|
}} |
|
|
|
|
className='far fa-clone remixui_menuicon' |
|
|
|
|
title='Clone Git Repository'> |
|
|
|
|
</span> |
|
|
|
|
</span> |
|
|
|
|
<span |
|
|
|
|
hidden={currentWorkspace === LOCALHOST} |
|
|
|
|
id='workspacesRestore' |
|
|
|
|
data-id='workspacesRestore' |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.stopPropagation() |
|
|
|
|
restoreBackup() |
|
|
|
|
}} |
|
|
|
|
className='far fa-upload remixui_menuicon' |
|
|
|
|
title='Restore Workspaces Backup'> |
|
|
|
|
</span> |
|
|
|
|
</span> |
|
|
|
|
<select id="workspacesSelect" value={currentWorkspace} data-id="workspacesSelect" onChange={(e) => switchWorkspace(e.target.value)} className="form-control custom-select"> |
|
|
|
|
{ |
|
|
|
|
global.fs.browser.workspaces |
|
|
|
|
.map((folder, index) => { |
|
|
|
|
return <option key={index} value={folder}>{folder}</option> |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
<option value={LOCALHOST}>{currentWorkspace === LOCALHOST ? 'localhost' : LOCALHOST}</option> |
|
|
|
|
{ global.fs.browser.workspaces.length <= 0 && <option value={NO_WORKSPACE}>{NO_WORKSPACE}</option> } |
|
|
|
|
</select> |
|
|
|
|
<Dropdown id="workspacesSelect" data-id="workspacesSelect"> |
|
|
|
|
<Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components" className="btn btn-light btn-block w-100 d-inline-block border border-dark form-control" icon={selectedWorkspace && selectedWorkspace.isGitRepo ? 'far fa-code-branch' : null}> |
|
|
|
|
{ selectedWorkspace ? selectedWorkspace.name : currentWorkspace === LOCALHOST ? 'localhost' : NO_WORKSPACE } |
|
|
|
|
</Dropdown.Toggle> |
|
|
|
|
|
|
|
|
|
<Dropdown.Menu as={CustomMenu} className='w-100 custom-dropdown-items' data-id="custom-dropdown-items" > |
|
|
|
|
{ |
|
|
|
|
global.fs.browser.workspaces.map(({ name, isGitRepo }, index) => ( |
|
|
|
|
<Dropdown.Item |
|
|
|
|
key={index} |
|
|
|
|
onClick={() => { |
|
|
|
|
switchWorkspace(name) |
|
|
|
|
}} |
|
|
|
|
data-id={`dropdown-item-${name}`} |
|
|
|
|
> |
|
|
|
|
{ isGitRepo ?
|
|
|
|
|
<div className='d-flex justify-content-between'> |
|
|
|
|
<span>{ currentWorkspace === name ? <span>✓ { name } </span> : <span className="pl-3">{ name }</span> }</span> |
|
|
|
|
<i className='fas fa-code-branch pt-1'></i> |
|
|
|
|
</div> :
|
|
|
|
|
<span>{ currentWorkspace === name ? <span>✓ { name } </span> : <span className="pl-3">{ name }</span> }</span> |
|
|
|
|
} |
|
|
|
|
</Dropdown.Item> |
|
|
|
|
)) |
|
|
|
|
} |
|
|
|
|
<Dropdown.Item onClick={() => { switchWorkspace(LOCALHOST) }}>{currentWorkspace === LOCALHOST ? <span>✓ localhost </span> : <span className="pl-3"> { LOCALHOST } </span>}</Dropdown.Item> |
|
|
|
|
{ ((global.fs.browser.workspaces.length <= 0) || currentWorkspace === NO_WORKSPACE) && <Dropdown.Item onClick={() => { switchWorkspace(NO_WORKSPACE) }}>{ <span className="pl-3">NO_WORKSPACE</span> }</Dropdown.Item> } |
|
|
|
|
</Dropdown.Menu> |
|
|
|
|
</Dropdown> |
|
|
|
|
</div> |
|
|
|
|
</header> |
|
|
|
|
</div> |
|
|
|
|
<div className='h-100 remixui_fileExplorerTree'> |
|
|
|
|
<div className='h-100'> |
|
|
|
|
<div className='pl-2 remixui_treeview' data-id='filePanelFileExplorerTree'> |
|
|
|
|
{ (global.fs.mode === 'browser') && (currentWorkspace !== NO_WORKSPACE) && |
|
|
|
|
<FileExplorer |
|
|
|
|
name={currentWorkspace} |
|
|
|
|
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']} |
|
|
|
|
contextMenuItems={global.fs.browser.contextMenu.registeredMenuItems} |
|
|
|
|
removedContextMenuItems={global.fs.browser.contextMenu.removedMenuItems} |
|
|
|
|
files={global.fs.browser.files} |
|
|
|
|
expandPath={global.fs.browser.expandPath} |
|
|
|
|
focusEdit={global.fs.focusEdit} |
|
|
|
|
focusElement={global.fs.focusElement} |
|
|
|
|
dispatchCreateNewFile={global.dispatchCreateNewFile} |
|
|
|
|
modal={global.modal} |
|
|
|
|
dispatchCreateNewFolder={global.dispatchCreateNewFolder} |
|
|
|
|
readonly={global.fs.readonly} |
|
|
|
|
toast={global.toast} |
|
|
|
|
dispatchDeletePath={global.dispatchDeletePath} |
|
|
|
|
dispatchRenamePath={global.dispatchRenamePath} |
|
|
|
|
dispatchUploadFile={global.dispatchUploadFile} |
|
|
|
|
dispatchCopyFile={global.dispatchCopyFile} |
|
|
|
|
dispatchCopyFolder={global.dispatchCopyFolder} |
|
|
|
|
dispatchPublishToGist={global.dispatchPublishToGist} |
|
|
|
|
dispatchRunScript={global.dispatchRunScript} |
|
|
|
|
dispatchEmitContextMenuEvent={global.dispatchEmitContextMenuEvent} |
|
|
|
|
dispatchHandleClickFile={global.dispatchHandleClickFile} |
|
|
|
|
dispatchSetFocusElement={global.dispatchSetFocusElement} |
|
|
|
|
dispatchFetchDirectory={global.dispatchFetchDirectory} |
|
|
|
|
dispatchRemoveInputField={global.dispatchRemoveInputField} |
|
|
|
|
dispatchAddInputField={global.dispatchAddInputField} |
|
|
|
|
dispatchHandleExpandPath={global.dispatchHandleExpandPath} |
|
|
|
|
/> |
|
|
|
|
} |
|
|
|
|
</div> |
|
|
|
|
{ global.fs.browser.isRequestingWorkspace || global.fs.browser.isRequestingCloning ? <div className="text-center py-5"><i className="fas fa-spinner fa-pulse fa-2x"></i></div> |
|
|
|
|
: <div className='pl-2 remixui_treeview' data-id='filePanelFileExplorerTree'> |
|
|
|
|
{ (global.fs.mode === 'browser') && (currentWorkspace !== NO_WORKSPACE) && |
|
|
|
|
<FileExplorer |
|
|
|
|
name={currentWorkspace} |
|
|
|
|
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']} |
|
|
|
|
contextMenuItems={global.fs.browser.contextMenu.registeredMenuItems} |
|
|
|
|
removedContextMenuItems={global.fs.browser.contextMenu.removedMenuItems} |
|
|
|
|
files={global.fs.browser.files} |
|
|
|
|
expandPath={global.fs.browser.expandPath} |
|
|
|
|
focusEdit={global.fs.focusEdit} |
|
|
|
|
focusElement={global.fs.focusElement} |
|
|
|
|
dispatchCreateNewFile={global.dispatchCreateNewFile} |
|
|
|
|
modal={global.modal} |
|
|
|
|
dispatchCreateNewFolder={global.dispatchCreateNewFolder} |
|
|
|
|
readonly={global.fs.readonly} |
|
|
|
|
toast={global.toast} |
|
|
|
|
dispatchDeletePath={global.dispatchDeletePath} |
|
|
|
|
dispatchRenamePath={global.dispatchRenamePath} |
|
|
|
|
dispatchUploadFile={global.dispatchUploadFile} |
|
|
|
|
dispatchCopyFile={global.dispatchCopyFile} |
|
|
|
|
dispatchCopyFolder={global.dispatchCopyFolder} |
|
|
|
|
dispatchPublishToGist={global.dispatchPublishToGist} |
|
|
|
|
dispatchRunScript={global.dispatchRunScript} |
|
|
|
|
dispatchEmitContextMenuEvent={global.dispatchEmitContextMenuEvent} |
|
|
|
|
dispatchHandleClickFile={global.dispatchHandleClickFile} |
|
|
|
|
dispatchSetFocusElement={global.dispatchSetFocusElement} |
|
|
|
|
dispatchFetchDirectory={global.dispatchFetchDirectory} |
|
|
|
|
dispatchRemoveInputField={global.dispatchRemoveInputField} |
|
|
|
|
dispatchAddInputField={global.dispatchAddInputField} |
|
|
|
|
dispatchHandleExpandPath={global.dispatchHandleExpandPath} |
|
|
|
|
/> |
|
|
|
|
} |
|
|
|
|
</div> |
|
|
|
|
} |
|
|
|
|
{ |
|
|
|
|
global.fs.localhost.isRequestingLocalhost ? <div className="text-center py-5"><i className="fas fa-spinner fa-pulse fa-2x"></i></div> |
|
|
|
|
: <div className='pl-2 filesystemexplorer remixui_treeview'> |
|
|
|
|