Commit 3550cda6 authored by ioedeveloper's avatar ioedeveloper

Move file-explorer to workspace directory

parent 0b6579f4
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
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
This diff is collapsed.
.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
.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;
}
.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
......@@ -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(() => {
......
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'
}[]
}
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: ''
}]
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