Commit 079cd362 authored by ioedeveloper's avatar ioedeveloper

finalize child components

parent 882251f6
...@@ -138,8 +138,11 @@ module.exports = class Filepanel extends ViewPlugin { ...@@ -138,8 +138,11 @@ module.exports = class Filepanel extends ViewPlugin {
this.emit('displayNewFileInput', dir) this.emit('displayNewFileInput', dir)
} }
async uploadFile (event) { async uploadFile (target) {
return await this.request.uploadFile(event) const provider = this.fileManager.currentFileProvider()
const dir = provider.workspace || '/'
return this.emit('uploadFileEvent', dir, target)
} }
async processCreateWorkspace (name) { async processCreateWorkspace (name) {
......
...@@ -484,6 +484,10 @@ const listenOnEvents = (provider) => { ...@@ -484,6 +484,10 @@ const listenOnEvents = (provider) => {
plugin.on('filePanel', 'displayNewFileInput', (path) => { plugin.on('filePanel', 'displayNewFileInput', (path) => {
addInputField('file', path)(dispatch) addInputField('file', path)(dispatch)
}) })
plugin.on('filePanel', 'uploadFileEvent', (dir: string, target) => {
uploadFile(target, dir)(dispatch)
})
} }
export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch<any>) => { export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch<any>) => {
......
import React, { useRef, useEffect } from 'react' // eslint-disable-line import React, { useRef, useEffect } from 'react' // eslint-disable-line
import { action, FileExplorerContextMenuProps } from '../types' 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' import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel'
declare global { declare global {
......
// 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>
)
}
// 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}
/>
)
}
}
import { extractNameFromKey } from '@remix-ui/helper' import { extractNameFromKey } from '@remix-ui/helper'
import { FileType } from '../types'
import * as _ from 'lodash' import * as _ from 'lodash'
interface Action { interface Action {
type: string type: string
...@@ -8,7 +9,7 @@ export interface BrowserState { ...@@ -8,7 +9,7 @@ export interface BrowserState {
browser: { browser: {
currentWorkspace: string, currentWorkspace: string,
workspaces: string[], workspaces: string[],
files: { [x: string]: Record<string, File> }, files: { [x: string]: Record<string, FileType> },
expandPath: string[] expandPath: string[]
isRequesting: boolean, isRequesting: boolean,
isSuccessful: boolean, isSuccessful: boolean,
...@@ -16,7 +17,7 @@ export interface BrowserState { ...@@ -16,7 +17,7 @@ export interface BrowserState {
}, },
localhost: { localhost: {
sharedFolder: string, sharedFolder: string,
files: { [x: string]: Record<string, File> }, files: { [x: string]: Record<string, FileType> },
expandPath: string[], expandPath: string[],
isRequesting: boolean, isRequesting: boolean,
isSuccessful: boolean, isSuccessful: boolean,
...@@ -469,7 +470,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => { ...@@ -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, FileType> } => {
let files = state.mode === 'browser' ? state.browser.files : state.localhost.files let files = state.mode === 'browser' ? state.browser.files : state.localhost.files
const _path = splitPath(state, path) const _path = splitPath(state, path)
...@@ -482,7 +483,7 @@ const fileAdded = (state: BrowserState, path: string): { [x: string]: Record<str ...@@ -482,7 +483,7 @@ const fileAdded = (state: BrowserState, path: string): { [x: string]: Record<str
return files return files
} }
const fileRemoved = (state: BrowserState, path: string): { [x: string]: Record<string, File> } => { const fileRemoved = (state: BrowserState, path: string): { [x: string]: Record<string, FileType> } => {
const files = state.mode === 'browser' ? state.browser.files : state.localhost.files const files = state.mode === 'browser' ? state.browser.files : state.localhost.files
const _path = splitPath(state, path) const _path = splitPath(state, path)
...@@ -491,7 +492,7 @@ const fileRemoved = (state: BrowserState, path: string): { [x: string]: Record<s ...@@ -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 // 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, FileType> } => {
if (!payload.fileTree) return state.mode === 'browser' ? state.browser.files : state[state.mode].files if (!payload.fileTree) return state.mode === 'browser' ? state.browser.files : state[state.mode].files
if (state.mode === 'browser') { if (state.mode === 'browser') {
if (payload.path === state.browser.currentWorkspace) { if (payload.path === state.browser.currentWorkspace) {
...@@ -538,7 +539,7 @@ const fetchDirectoryContent = (state: BrowserState, payload: { fileTree, path: s ...@@ -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, FileType> } => {
if (state.mode === 'browser') { if (state.mode === 'browser') {
const files = normalize(payload.fileTree, payload.path) const files = normalize(payload.fileTree, payload.path)
...@@ -548,7 +549,7 @@ const fetchWorkspaceDirectoryContent = (state: BrowserState, payload: { fileTree ...@@ -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, FileType> => {
const folders = {} const folders = {}
const files = {} const files = {}
......
.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
import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line import React, { useState, useEffect, useRef, useContext } from 'react' // eslint-disable-line
import { FileExplorer } from './components/file-explorer' // 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 { WorkspaceProps, WorkspaceState } from './types'
import { FileSystemContext } from './contexts' import { FileSystemContext } from './contexts'
...@@ -9,12 +9,9 @@ const canUpload = window.File || window.FileReader || window.FileList || window. ...@@ -9,12 +9,9 @@ const canUpload = window.File || window.FileReader || window.FileList || window.
export function Workspace (props: WorkspaceProps) { export function Workspace (props: WorkspaceProps) {
const LOCALHOST = ' - connect to localhost - ' const LOCALHOST = ' - connect to localhost - '
const NO_WORKSPACE = ' - none - ' const NO_WORKSPACE = ' - none - '
const [state, setState] = useState<WorkspaceState>({ const [state] = useState<WorkspaceState>({
reset: false,
hideRemixdExplorer: true, hideRemixdExplorer: true,
displayNewFile: false, displayNewFile: false,
externalUploads: null,
uploadFileEvent: null,
loadingLocalhost: false loadingLocalhost: false
}) })
const [currentWorkspace, setCurrentWorkspace] = useState<string>(NO_WORKSPACE) const [currentWorkspace, setCurrentWorkspace] = useState<string>(NO_WORKSPACE)
...@@ -50,19 +47,6 @@ export function Workspace (props: WorkspaceProps) { ...@@ -50,19 +47,6 @@ export function Workspace (props: WorkspaceProps) {
return createWorkspace() 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 = () => { props.plugin.request.getCurrentWorkspace = () => {
return { name: currentWorkspace, isLocalhost: currentWorkspace === LOCALHOST, absolutePath: `${props.plugin.workspace.workspacesPath}/${currentWorkspace}` } return { name: currentWorkspace, isLocalhost: currentWorkspace === LOCALHOST, absolutePath: `${props.plugin.workspace.workspacesPath}/${currentWorkspace}` }
} }
...@@ -120,10 +104,8 @@ export function Workspace (props: WorkspaceProps) { ...@@ -120,10 +104,8 @@ export function Workspace (props: WorkspaceProps) {
} }
/** ** ****/ /** ** ****/
const resetFocus = (reset) => { const resetFocus = () => {
setState(prevState => { global.dispatchSetFocusElement([{ key: '', type: 'folder' }])
return { ...prevState, reset }
})
} }
const switchWorkspace = async (name: string) => { const switchWorkspace = async (name: string) => {
...@@ -153,7 +135,7 @@ export function Workspace (props: WorkspaceProps) { ...@@ -153,7 +135,7 @@ export function Workspace (props: WorkspaceProps) {
return ( return (
<div className='remixui_container'> <div className='remixui_container'>
<div className='remixui_fileexplorer' onClick={() => resetFocus(true)}> <div className='remixui_fileexplorer' onClick={resetFocus}>
<div> <div>
<header> <header>
<div className="mb-2"> <div className="mb-2">
...@@ -214,14 +196,12 @@ export function Workspace (props: WorkspaceProps) { ...@@ -214,14 +196,12 @@ export function Workspace (props: WorkspaceProps) {
<FileExplorer <FileExplorer
name={currentWorkspace} name={currentWorkspace}
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']} menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
plugin={props.plugin}
focusRoot={state.reset}
contextMenuItems={props.plugin.registeredMenuItems} contextMenuItems={props.plugin.registeredMenuItems}
removedContextMenuItems={props.plugin.removedMenuItems} removedContextMenuItems={props.plugin.removedMenuItems}
displayInput={state.displayNewFile}
externalUploads={state.uploadFileEvent}
resetFocus={resetFocus}
files={global.fs.browser.files} files={global.fs.browser.files}
expandPath={global.fs.browser.expandPath}
focusEdit={global.fs.focusEdit}
focusElement={global.fs.focusElement}
/> />
} }
</div> </div>
...@@ -232,12 +212,12 @@ export function Workspace (props: WorkspaceProps) { ...@@ -232,12 +212,12 @@ export function Workspace (props: WorkspaceProps) {
<FileExplorer <FileExplorer
name='localhost' name='localhost'
menuItems={['createNewFile', 'createNewFolder']} menuItems={['createNewFile', 'createNewFolder']}
plugin={props.plugin}
focusRoot={state.reset}
contextMenuItems={props.plugin.registeredMenuItems} contextMenuItems={props.plugin.registeredMenuItems}
removedContextMenuItems={props.plugin.removedMenuItems} removedContextMenuItems={props.plugin.removedMenuItems}
resetFocus={resetFocus}
files={global.fs.localhost.files} files={global.fs.localhost.files}
expandPath={global.fs.localhost.expandPath}
focusEdit={global.fs.focusEdit}
focusElement={global.fs.focusElement}
/> />
} }
</div> </div>
......
...@@ -28,11 +28,8 @@ export interface WorkspaceProps { ...@@ -28,11 +28,8 @@ export interface WorkspaceProps {
} }
} }
export interface WorkspaceState { export interface WorkspaceState {
reset: boolean
hideRemixdExplorer: boolean hideRemixdExplorer: boolean
displayNewFile: boolean displayNewFile: boolean
externalUploads: EventTarget & HTMLInputElement
uploadFileEvent: EventTarget & HTMLInputElement
loadingLocalhost: boolean loadingLocalhost: boolean
} }
...@@ -46,7 +43,7 @@ export interface Modal { ...@@ -46,7 +43,7 @@ export interface Modal {
cancelFn: () => void cancelFn: () => void
} }
export interface File { export interface FileType {
path: string, path: string,
name: string, name: string,
isDirectory: boolean, isDirectory: boolean,
...@@ -58,13 +55,14 @@ export interface File { ...@@ -58,13 +55,14 @@ export interface File {
export interface FileExplorerProps { export interface FileExplorerProps {
name: string, name: string,
menuItems?: string[], menuItems?: string[],
focusRoot: boolean,
contextMenuItems: MenuItems, contextMenuItems: MenuItems,
removedContextMenuItems: MenuItems, removedContextMenuItems: MenuItems,
displayInput?: boolean, displayInput?: boolean,
externalUploads?: EventTarget & HTMLInputElement, externalUploads?: EventTarget & HTMLInputElement,
resetFocus?: (value: boolean) => void, files: { [x: string]: Record<string, FileType> },
files: { [x: string]: Record<string, File> } expandPath: string[],
focusEdit: string,
focusElement: { key: string, type: 'file' | 'folder' | 'gist' }[]
} }
export interface FileExplorerMenuProps { export interface FileExplorerMenuProps {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment