Merge remote-tracking branch 'upstream/master' into spacesailor24/web3js-v4-upgrade

pull/3951/head
Oleksii Kosynskyi 1 year ago
commit 8d0c157e4a
No known key found for this signature in database
GPG Key ID: B4A8D3CCE22EA65E
  1. 2
      libs/remix-ui/tree-view/src/lib/remix-ui-tree-view.tsx
  2. 55
      libs/remix-ui/tree-view/src/lib/tree-view-item/tree-view-item.tsx
  3. 122
      libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx
  4. 293
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  5. 185
      libs/remix-ui/workspace/src/lib/components/file-render.tsx

@ -7,7 +7,7 @@ export const TreeView = (props: TreeViewProps) => {
const { children, id, ...otherProps } = props
return (
<ul data-id={`treeViewUl${id}`} className="ul_tv ml-0 pl-2" { ...otherProps }>
<ul data-id={`treeViewUl${id}`} className="ul_tv ml-0 pl-1" { ...otherProps }>
{ children }
</ul>
)

@ -1,10 +1,23 @@
import React, { useState, useEffect } from 'react' // eslint-disable-line
import { TreeViewItemProps } from '../../types'
import React, {useState, useEffect} from 'react' // eslint-disable-line
import {TreeViewItemProps} from '../../types'
import './tree-view-item.css'
export const TreeViewItem = (props: TreeViewItemProps) => {
const { id, children, label, labelClass, expand, iconX = 'fas fa-caret-right', iconY = 'fas fa-caret-down', icon, controlBehaviour = false, innerRef, showIcon = true, ...otherProps } = props
const {
id,
children,
label,
labelClass,
expand,
iconX = 'fas fa-caret-right',
iconY = '',
icon,
controlBehaviour = false,
innerRef,
showIcon = true,
...otherProps
} = props
const [isExpanded, setIsExpanded] = useState(false)
useEffect(() => {
@ -12,18 +25,32 @@ export const TreeViewItem = (props: TreeViewItemProps) => {
}, [expand])
return (
<li ref={innerRef} key={`treeViewLi${id}`} data-id={`treeViewLi${id}`} className='li_tv' {...otherProps}>
<div key={`treeViewDiv${id}`} data-id={`treeViewDiv${id}`} className={`d-flex flex-row align-items-center ${labelClass}`} onClick={() => !controlBehaviour && setIsExpanded(!isExpanded)}>
{ children && showIcon ? <div
className={isExpanded ? `px-1 ${iconY} caret caret_tv` : `px-1 ${iconX} caret caret_tv`}
style={{ visibility: children ? 'visible' : 'hidden' }}
></div> : icon ? <div className={`pr-3 pl-1 ${icon} caret caret_tv`}></div> : null
}
<span className='w-100 pl-1'>
{ label }
</span>
<li
ref={innerRef}
key={`treeViewLi${id}`}
data-id={`treeViewLi${id}`}
className="li_tv"
{...otherProps}
>
<div
key={`treeViewDiv${id}`}
data-id={`treeViewDiv${id}`}
className={`d-flex flex-row align-items-center ${labelClass}`}
onClick={() => !controlBehaviour && setIsExpanded(!isExpanded)}
>
{children && showIcon ? (
<div
className={
isExpanded ? `px-0 ${iconY}` : `px-1 ${iconX} caret caret_tv`
}
style={{visibility: children ? 'visible' : 'hidden'}}
></div>
) : icon ? (
<div className={`pr-3 pl-1 ${icon} caret caret_tv`}></div>
) : null}
<span className="w-100 pb-1 pl-1">{label}</span>
</div>
{ isExpanded ? children : null }
{isExpanded ? children : null}
</li>
)
}

@ -1,9 +1,9 @@
import { CustomTooltip } from '@remix-ui/helper'
import React, { useState, useEffect, } from 'react' //eslint-disable-line
import { FormattedMessage } from 'react-intl'
import { Placement } from 'react-bootstrap/esm/Overlay'
import { FileExplorerMenuProps } from '../types'
const _paq = window._paq = window._paq || []
import {CustomTooltip} from '@remix-ui/helper'
import React, {useState, useEffect} from 'react' //eslint-disable-line
import {FormattedMessage} from 'react-intl'
import {Placement} from 'react-bootstrap/esm/Overlay'
import {FileExplorerMenuProps} from '../types'
const _paq = (window._paq = window._paq || [])
export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
const [state, setState] = useState({
@ -44,18 +44,24 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
icon: 'fab fa-github',
placement: 'bottom-start'
}
].filter(item => props.menuItems && props.menuItems.find((name) => { return name === item.action })),
].filter(
(item) =>
props.menuItems &&
props.menuItems.find((name) => {
return name === item.action
})
),
actions: {}
})
const enableDirUpload = { directory: "", webkitdirectory: "" }
const enableDirUpload = {directory: '', webkitdirectory: ''}
useEffect(() => {
const actions = {
updateGist: () => {}
}
setState(prevState => {
return { ...prevState, actions }
setState((prevState) => {
return {...prevState, actions}
})
}, [])
@ -67,32 +73,53 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
tooltipClasses="text-nowrap"
tooltipText={props.title}
>
<span className='remixui_label' data-path={props.title} style={{ fontWeight: 'bold' }}>{ props.title }</span>
<span
className="remixui_label"
data-path={props.title}
style={{fontWeight: 'bold'}}
>
{props.title}
</span>
</CustomTooltip>
<span className="pl-2">{
state.menuItems.map(({ action, title, icon, placement }, index) => {
<span className="pl-0 pb-1">
{state.menuItems.map(({action, title, icon, placement}, index) => {
if (action === 'uploadFile') {
return (
<CustomTooltip
placement={placement as Placement}
tooltipId="uploadFileTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id={`filePanel.${action}`} defaultMessage={title} />}
tooltipText={
<FormattedMessage
id={`filePanel.${action}`}
defaultMessage={title}
/>
}
key={`index-${action}-${placement}-${icon}`}
>
<label
id={action}
data-id={'fileExplorerUploadFile' + action }
data-id={'fileExplorerUploadFile' + action}
className={icon + ' mb-0 px-1 remixui_newFile'}
key={`index-${action}-${placement}-${icon}`}
>
<input id="fileUpload" data-id="fileExplorerFileUpload" type="file" onChange={(e) => {
e.stopPropagation()
_paq.push(['trackEvent', 'fileExplorer', 'fileAction', action])
props.uploadFile(e.target)
e.target.value = null
}}
multiple />
<input
id="fileUpload"
data-id="fileExplorerFileUpload"
type="file"
onChange={(e) => {
e.stopPropagation()
_paq.push([
'trackEvent',
'fileExplorer',
'fileAction',
action
])
props.uploadFile(e.target)
e.target.value = null
}}
multiple
/>
</label>
</CustomTooltip>
)
@ -102,22 +129,38 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
placement={placement as Placement}
tooltipId="uploadFolderTooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id={`filePanel.${action}`} defaultMessage={title} />}
tooltipText={
<FormattedMessage
id={`filePanel.${action}`}
defaultMessage={title}
/>
}
key={`index-${action}-${placement}-${icon}`}
>
<label
id={action}
data-id={'fileExplorerUploadFolder' + action }
data-id={'fileExplorerUploadFolder' + action}
className={icon + ' mb-0 px-1 remixui_newFile'}
key={`index-${action}-${placement}-${icon}`}
>
<input id="folderUpload" data-id="fileExplorerFolderUpload" type="file" onChange={(e) => {
e.stopPropagation()
_paq.push(['trackEvent', 'fileExplorer', 'fileAction', action])
props.uploadFolder(e.target)
e.target.value = null
}}
{...enableDirUpload} multiple />
<input
id="folderUpload"
data-id="fileExplorerFolderUpload"
type="file"
onChange={(e) => {
e.stopPropagation()
_paq.push([
'trackEvent',
'fileExplorer',
'fileAction',
action
])
props.uploadFolder(e.target)
e.target.value = null
}}
{...enableDirUpload}
multiple
/>
</label>
</CustomTooltip>
)
@ -127,7 +170,12 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
placement={placement as Placement}
tooltipId={`${action}-${title}-${icon}-${index}`}
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id={`filePanel.${action}`} defaultMessage={title} />}
tooltipText={
<FormattedMessage
id={`filePanel.${action}`}
defaultMessage={title}
/>
}
key={`${action}-${title}-${index}`}
>
<span
@ -135,7 +183,12 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
data-id={'fileExplorerNewFile' + action}
onClick={(e) => {
e.stopPropagation()
_paq.push(['trackEvent', 'fileExplorer', 'fileAction', action])
_paq.push([
'trackEvent',
'fileExplorer',
'fileAction',
action
])
if (action === 'createNewFile') {
props.createNewFile()
} else if (action === 'createNewFolder') {
@ -148,8 +201,7 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
}}
className={'newFile ' + icon + ' px-1 remixui_newFile'}
key={`${action}-${title}-${index}`}
>
</span>
></span>
</CustomTooltip>
)
}

@ -1,22 +1,41 @@
import React, { useEffect, useState, useRef, SyntheticEvent } from 'react' // eslint-disable-line
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, WorkSpaceState } from '../types'
import React, {useEffect, useState, useRef, SyntheticEvent} from 'react' // eslint-disable-line
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, WorkSpaceState} from '../types'
import '../css/file-explorer.css'
import { checkSpecialChars, extractNameFromKey, extractParentFromKey, joinPath } from '@remix-ui/helper'
import {
checkSpecialChars,
extractNameFromKey,
extractParentFromKey,
joinPath
} from '@remix-ui/helper'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { FileRender } from './file-render'
import { Drag } from "@remix-ui/drag-n-drop"
import { ROOT_PATH } from '../utils/constants'
import {FileRender} from './file-render'
import {Drag} from '@remix-ui/drag-n-drop'
import {ROOT_PATH} from '../utils/constants'
export const FileExplorer = (props: FileExplorerProps) => {
const { name, contextMenuItems, removedContextMenuItems, files, workspaceState, toGist, addMenuItems,
removeMenuItems, handleContextMenu, handleNewFileInput, handleNewFolderInput, uploadFile, uploadFolder, fileState } = props
const [state, setState] = useState<WorkSpaceState>( workspaceState)
const {
name,
contextMenuItems,
removedContextMenuItems,
files,
workspaceState,
toGist,
addMenuItems,
removeMenuItems,
handleContextMenu,
handleNewFileInput,
handleNewFolderInput,
uploadFile,
uploadFolder,
fileState
} = props
const [state, setState] = useState<WorkSpaceState>(workspaceState)
const treeRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (contextMenuItems) {
addMenuItems(contextMenuItems)
@ -31,8 +50,16 @@ export const FileExplorer = (props: FileExplorerProps) => {
useEffect(() => {
if (props.focusEdit) {
setState(prevState => {
return { ...prevState, focusEdit: { element: props.focusEdit, type: 'file', isNew: true, lastEdit: null } }
setState((prevState) => {
return {
...prevState,
focusEdit: {
element: props.focusEdit,
type: 'file',
isNew: true,
lastEdit: null
}
}
})
}
}, [props.focusEdit])
@ -45,21 +72,21 @@ export const FileExplorer = (props: FileExplorerProps) => {
if (treeRef.current) {
const keyPressHandler = (e: KeyboardEvent) => {
if (e.shiftKey) {
setState(prevState => {
return { ...prevState, ctrlKey: true }
setState((prevState) => {
return {...prevState, ctrlKey: true}
})
}
}
const keyUpHandler = (e: KeyboardEvent) => {
if (!e.shiftKey) {
setState(prevState => {
return { ...prevState, ctrlKey: false }
setState((prevState) => {
return {...prevState, ctrlKey: false}
})
}
}
const targetDocument = treeRef.current
targetDocument.addEventListener('keydown', keyPressHandler)
targetDocument.addEventListener('keyup', keyUpHandler)
return () => {
@ -70,7 +97,11 @@ export const FileExplorer = (props: FileExplorerProps) => {
}, [treeRef.current])
const hasReservedKeyword = (content: string): boolean => {
if (state.reservedKeywords.findIndex(value => content.startsWith(value)) !== -1) return true
if (
state.reservedKeywords.findIndex((value) => content.startsWith(value)) !==
-1
)
return true
else return false
}
@ -78,7 +109,12 @@ export const FileExplorer = (props: FileExplorerProps) => {
try {
props.dispatchCreateNewFile(newFilePath, ROOT_PATH)
} catch (error) {
return props.modal('File Creation Failed', typeof error === 'string' ? error : error.message, 'Close', async () => {})
return props.modal(
'File Creation Failed',
typeof error === 'string' ? error : error.message,
'Close',
async () => {}
)
}
}
@ -86,7 +122,12 @@ export const FileExplorer = (props: FileExplorerProps) => {
try {
props.dispatchCreateNewFolder(newFolderPath, ROOT_PATH)
} catch (e) {
return props.modal('Folder Creation Failed', typeof e === 'string' ? e : e.message, 'Close', async () => {})
return props.modal(
'Folder Creation Failed',
typeof e === 'string' ? e : e.message,
'Close',
async () => {}
)
}
}
@ -94,42 +135,66 @@ export const FileExplorer = (props: FileExplorerProps) => {
try {
props.dispatchRenamePath(oldPath, newPath)
} catch (error) {
props.modal('Rename File Failed', 'Unexpected error while renaming: ' + typeof error === 'string' ? error : error.message, 'Close', async () => {})
props.modal(
'Rename File Failed',
'Unexpected error while renaming: ' + typeof error === 'string'
? error
: error.message,
'Close',
async () => {}
)
}
}
const publishToGist = (path?: string, type?: string) => {
props.modal('Create a public gist', `Are you sure you want to anonymously publish all your files in the ${name} workspace as a public gist on github.com?`, 'OK', () => toGist(path, type), 'Cancel', () => {})
props.modal(
'Create a public gist',
`Are you sure you want to anonymously publish all your files in the ${name} workspace as a public gist on github.com?`,
'OK',
() => toGist(path, type),
'Cancel',
() => {}
)
}
const handleClickFile = (path: string, type: 'folder' | 'file' | 'gist') => {
if (!state.ctrlKey) {
props.dispatchHandleClickFile(path, type)
} else {
if (props.focusElement.findIndex(item => item.key === path) !== -1) {
const focusElement = props.focusElement.filter(item => item.key !== path)
if (props.focusElement.findIndex((item) => item.key === path) !== -1) {
const focusElement = props.focusElement.filter(
(item) => item.key !== path
)
props.dispatchSetFocusElement(focusElement)
} else {
const nonRootFocus = props.focusElement.filter((el) => { return !(el.key === '' && el.type === 'folder') })
const nonRootFocus = props.focusElement.filter((el) => {
return !(el.key === '' && el.type === 'folder')
})
nonRootFocus.push({ key: path, type })
nonRootFocus.push({key: path, type})
props.dispatchSetFocusElement(nonRootFocus)
}
}
}
const handleClickFolder = async (path: string, type: 'folder' | 'file' | 'gist') => {
const handleClickFolder = async (
path: string,
type: 'folder' | 'file' | 'gist'
) => {
if (state.ctrlKey) {
if (props.focusElement.findIndex(item => item.key === path) !== -1) {
const focusElement = props.focusElement.filter(item => item.key !== path)
if (props.focusElement.findIndex((item) => item.key === path) !== -1) {
const focusElement = props.focusElement.filter(
(item) => item.key !== path
)
props.dispatchSetFocusElement(focusElement)
} else {
const nonRootFocus = props.focusElement.filter((el) => { return !(el.key === '' && el.type === 'folder') })
const nonRootFocus = props.focusElement.filter((el) => {
return !(el.key === '' && el.type === 'folder')
})
nonRootFocus.push({ key: path, type })
nonRootFocus.push({key: path, type})
props.dispatchSetFocusElement(nonRootFocus)
}
} else {
@ -139,10 +204,16 @@ export const FileExplorer = (props: FileExplorerProps) => {
expandPath = [...new Set([...props.expandPath, path])]
props.dispatchFetchDirectory(path)
} else {
expandPath = [...new Set(props.expandPath.filter(key => key && (typeof key === 'string') && !key.startsWith(path)))]
expandPath = [
...new Set(
props.expandPath.filter(
(key) => key && typeof key === 'string' && !key.startsWith(path)
)
)
]
}
props.dispatchSetFocusElement([{ key: path, type }])
props.dispatchSetFocusElement([{key: path, type}])
props.dispatchHandleExpandPath(expandPath)
}
}
@ -151,37 +222,63 @@ export const FileExplorer = (props: FileExplorerProps) => {
if (typeof content === 'string') content = content.trim()
const parentFolder = extractParentFromKey(state.focusEdit.element)
if (!content || (content.trim() === '')) {
if (!content || content.trim() === '') {
if (state.focusEdit.isNew) {
props.dispatchRemoveInputField(parentFolder)
setState(prevState => {
return { ...prevState, focusEdit: { element: null, isNew: false, type: '', lastEdit: '' } }
setState((prevState) => {
return {
...prevState,
focusEdit: {element: null, isNew: false, type: '', lastEdit: ''}
}
})
} else {
setState(prevState => {
return { ...prevState, focusEdit: { element: null, isNew: false, type: '', lastEdit: '' } }
setState((prevState) => {
return {
...prevState,
focusEdit: {element: null, isNew: false, type: '', lastEdit: ''}
}
})
}
} else {
if (state.focusEdit.lastEdit === content) {
return setState(prevState => {
return { ...prevState, focusEdit: { element: null, isNew: false, type: '', lastEdit: '' } }
return setState((prevState) => {
return {
...prevState,
focusEdit: {element: null, isNew: false, type: '', lastEdit: ''}
}
})
}
if (checkSpecialChars(content)) {
props.modal('Validation Error', 'Special characters are not allowed', 'OK', () => {})
props.modal(
'Validation Error',
'Special characters are not allowed',
'OK',
() => {}
)
} else {
if (state.focusEdit.isNew) {
if (hasReservedKeyword(content)) {
props.dispatchRemoveInputField(parentFolder)
props.modal('Reserved Keyword', `File name contains Remix reserved keywords. '${content}'`, 'Close', () => {})
props.modal(
'Reserved Keyword',
`File name contains Remix reserved keywords. '${content}'`,
'Close',
() => {}
)
} else {
state.focusEdit.type === 'file' ? createNewFile(joinPath(parentFolder, content)) : createNewFolder(joinPath(parentFolder, content))
state.focusEdit.type === 'file'
? createNewFile(joinPath(parentFolder, content))
: createNewFolder(joinPath(parentFolder, content))
props.dispatchRemoveInputField(parentFolder)
}
} else {
if (hasReservedKeyword(content)) {
props.modal('Reserved Keyword', `File name contains Remix reserved keywords. '${content}'`, 'Close', () => {})
props.modal(
'Reserved Keyword',
`File name contains Remix reserved keywords. '${content}'`,
'Close',
() => {}
)
} else {
if (state.focusEdit.element) {
const oldPath: string = state.focusEdit.element
@ -192,24 +289,39 @@ export const FileExplorer = (props: FileExplorerProps) => {
}
}
}
setState(prevState => {
return { ...prevState, focusEdit: { element: null, isNew: false, type: '', lastEdit: '' } }
setState((prevState) => {
return {
...prevState,
focusEdit: {element: null, isNew: false, type: '', lastEdit: ''}
}
})
}
}
}
const handleFileExplorerMenuClick = (e: SyntheticEvent) => {
e.stopPropagation()
if (e && (e.target as any).getAttribute('data-id') === 'fileExplorerUploadFileuploadFile') return // we don't want to let propagate the input of type file
if (e && (e.target as any).getAttribute('data-id') === 'fileExplorerFileUpload') return // we don't want to let propagate the input of type file
if (
e &&
(e.target as any).getAttribute('data-id') ===
'fileExplorerUploadFileuploadFile'
)
return // we don't want to let propagate the input of type file
if (
e &&
(e.target as any).getAttribute('data-id') === 'fileExplorerFileUpload'
)
return // we don't want to let propagate the input of type file
let expandPath = []
if (!props.expandPath.includes(ROOT_PATH)) {
expandPath = [ROOT_PATH, ...new Set([...props.expandPath])]
} else {
expandPath = [...new Set(props.expandPath.filter(key => key && (typeof key === 'string')))]
expandPath = [
...new Set(
props.expandPath.filter((key) => key && typeof key === 'string')
)
]
}
props.dispatchHandleExpandPath(expandPath)
}
@ -218,23 +330,34 @@ export const FileExplorer = (props: FileExplorerProps) => {
try {
props.dispatchMoveFile(src, dest)
} catch (error) {
props.modal('Moving File Failed', 'Unexpected error while moving file: ' + src, 'Close', async () => {})
}
props.modal(
'Moving File Failed',
'Unexpected error while moving file: ' + src,
'Close',
async () => {}
)
}
}
const handleFolderMove = (dest: string, src: string) => {
try {
props.dispatchMoveFolder(src, dest)
} catch (error) {
props.modal('Moving Folder Failed', 'Unexpected error while moving folder: ' + src, 'Close', async () => {})
}
props.modal(
'Moving Folder Failed',
'Unexpected error while moving folder: ' + src,
'Close',
async () => {}
)
}
}
return (
<Drag onFileMoved={handleFileMove} onFolderMoved={handleFolderMove}>
<div ref={treeRef} tabIndex={0} style={{ outline: "none" }}>
<TreeView id='treeView'>
<TreeViewItem id="treeViewItem"
<div ref={treeRef} tabIndex={0} style={{outline: 'none'}}>
<TreeView id="treeView">
<TreeViewItem
id="treeViewItem"
controlBehaviour={true}
label={
<div onClick={handleFileExplorerMenuClick}>
@ -249,28 +372,30 @@ export const FileExplorer = (props: FileExplorerProps) => {
/>
</div>
}
expand={true}>
<div className='pb-4 mb-4'>
<TreeView id='treeViewMenu'>
{
files[ROOT_PATH] && Object.keys(files[ROOT_PATH]).map((key, index) => <FileRender
file={files[ROOT_PATH][key]}
fileDecorations={fileState}
index={index}
focusContext={state.focusContext}
focusEdit={state.focusEdit}
focusElement={props.focusElement}
ctrlKey={state.ctrlKey}
expandPath={props.expandPath}
editModeOff={editModeOff}
handleClickFile={handleClickFile}
handleClickFolder={handleClickFolder}
handleContextMenu={handleContextMenu}
key={index}
showIconsMenu={props.showIconsMenu}
hideIconsMenu={props.hideIconsMenu}
/>)
}
expand={true}
>
<div className="pb-4 mb-4">
<TreeView id="treeViewMenu">
{files[ROOT_PATH] &&
Object.keys(files[ROOT_PATH]).map((key, index) => (
<FileRender
file={files[ROOT_PATH][key]}
fileDecorations={fileState}
index={index}
focusContext={state.focusContext}
focusEdit={state.focusEdit}
focusElement={props.focusElement}
ctrlKey={state.ctrlKey}
expandPath={props.expandPath}
editModeOff={editModeOff}
handleClickFile={handleClickFile}
handleClickFolder={handleClickFolder}
handleContextMenu={handleContextMenu}
key={index}
showIconsMenu={props.showIconsMenu}
hideIconsMenu={props.hideIconsMenu}
/>
))}
</TreeView>
</div>
</TreeViewItem>

@ -1,28 +1,34 @@
// eslint-disable-next-line no-use-before-define
import React, { SyntheticEvent, useEffect, useState } from 'react'
import { FileType } from '../types'
import React, {SyntheticEvent, useEffect, 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'
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'
import { fileDecoration, FileDecorationIcons } from '@remix-ui/file-decorators'
import { Draggable } from "@remix-ui/drag-n-drop"
import {FileLabel} from './file-label'
import {fileDecoration, FileDecorationIcons} from '@remix-ui/file-decorators'
import {Draggable} from '@remix-ui/drag-n-drop'
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[],
hideIconsMenu?: React.Dispatch<React.SetStateAction<boolean>>,
showIconsMenu?: boolean,
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
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[]
hideIconsMenu?: React.Dispatch<React.SetStateAction<boolean>>
showIconsMenu?: boolean
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
fileDecorations: fileDecoration[]
}
@ -38,11 +44,17 @@ export const FileRender = (props: RenderFileProps) => {
}
}, [props.file])
const labelClass = props.focusEdit.element === file.path
? 'bg-light' : props.focusElement.findIndex(item => item.key === file.path) !== -1
? 'bg-secondary' : hover
? 'bg-light border-no-shift' : (props.focusContext.element === file.path) && (props.focusEdit.element !== file.path)
? 'bg-light border-no-shift' : ''
const labelClass =
props.focusEdit.element === file.path
? 'bg-light'
: props.focusElement.findIndex((item) => item.key === file.path) !== -1
? 'bg-secondary'
: hover
? 'bg-light border-no-shift'
: props.focusContext.element === file.path &&
props.focusEdit.element !== file.path
? 'bg-light border-no-shift'
: ''
const spreadProps = {
onClick: (e) => e.stopPropagation()
@ -50,20 +62,28 @@ export const FileRender = (props: RenderFileProps) => {
const handleFolderClick = (event: SyntheticEvent) => {
event.stopPropagation()
if (props.focusEdit.element !== file.path) props.handleClickFolder(file.path, file.type)
if (props.focusEdit.element !== file.path)
props.handleClickFolder(file.path, file.type)
if (props.showIconsMenu === true) props.hideIconsMenu(!props.showIconsMenu)
}
const handleFileClick = (event: SyntheticEvent) => {
event.stopPropagation()
if (props.focusEdit.element !== file.path) props.handleClickFile(file.path, file.type)
if (props.focusEdit.element !== file.path)
props.handleClickFile(file.path, file.type)
if (props.showIconsMenu === true) props.hideIconsMenu(!props.showIconsMenu)
}
const handleContextMenu = (event: PointerEvent) => {
event.preventDefault()
event.stopPropagation()
props.handleContextMenu(event.pageX, event.pageY, file.path, (event.target as HTMLElement).textContent, file.type)
props.handleContextMenu(
event.pageX,
event.pageY,
file.path,
(event.target as HTMLElement).textContent,
file.type
)
}
const handleMouseOut = (event: SyntheticEvent) => {
@ -80,17 +100,36 @@ export const FileRender = (props: RenderFileProps) => {
return (
<TreeViewItem
id={`treeViewItem${file.path}`}
iconX='pr-3 fa fa-folder'
iconY='pr-3 fa fa-folder-open'
iconX="pr-3 fa fa-folder"
iconY={
props.expandPath.includes(file.path)
? 'pr-0 fa fa-folder-open'
: 'pr-3 fa fa-folder'
}
key={`${file.path + props.index}`}
label={<>
<Draggable isDraggable={props.focusEdit.element !== null} file={file} expandedPath={props.expandPath} handleClickFolder={props.handleClickFolder}>
<div className="d-flex flex-row">
<FileLabel fileDecorations={props.fileDecorations} file={file} focusEdit={props.focusEdit} editModeOff={props.editModeOff} />
<FileDecorationIcons file={file} fileDecorations={props.fileDecorations} />
</div>
</Draggable>
</>}
label={
<>
<Draggable
isDraggable={props.focusEdit.element !== null}
file={file}
expandedPath={props.expandPath}
handleClickFolder={props.handleClickFolder}
>
<div className="d-flex flex-row">
<FileLabel
fileDecorations={props.fileDecorations}
file={file}
focusEdit={props.focusEdit}
editModeOff={props.editModeOff}
/>
<FileDecorationIcons
file={file}
fileDecorations={props.fileDecorations}
/>
</div>
</Draggable>
</>
}
onClick={handleFolderClick}
onContextMenu={handleContextMenu}
labelClass={labelClass}
@ -99,26 +138,37 @@ export const FileRender = (props: RenderFileProps) => {
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]}
fileDecorations={props.fileDecorations}
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}
key={index}
/>)
}
</TreeView> : <TreeView id={`treeView${file.path}`} key={`treeView${file.path}`} {...spreadProps} />
}
{file.child ? (
<TreeView
id={`treeView${file.path}`}
key={`treeView${file.path}`}
{...spreadProps}
>
{Object.keys(file.child).map((key, index) => (
<FileRender
file={file.child[key]}
fileDecorations={props.fileDecorations}
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}
key={index}
/>
))}
</TreeView>
) : (
<TreeView
id={`treeView${file.path}`}
key={`treeView${file.path}`}
{...spreadProps}
/>
)}
</TreeViewItem>
)
} else {
@ -128,10 +178,23 @@ export const FileRender = (props: RenderFileProps) => {
key={`treeView${file.path}`}
label={
<>
<Draggable isDraggable={props.focusEdit.element !== null} file={file} expandedPath={props.expandPath} handleClickFolder={props.handleClickFolder}>
<Draggable
isDraggable={props.focusEdit.element !== null}
file={file}
expandedPath={props.expandPath}
handleClickFolder={props.handleClickFolder}
>
<div className="d-flex flex-row">
<FileLabel fileDecorations={props.fileDecorations} file={file} focusEdit={props.focusEdit} editModeOff={props.editModeOff} />
<FileDecorationIcons file={file} fileDecorations={props.fileDecorations} />
<FileLabel
fileDecorations={props.fileDecorations}
file={file}
focusEdit={props.focusEdit}
editModeOff={props.editModeOff}
/>
<FileDecorationIcons
file={file}
fileDecorations={props.fileDecorations}
/>
</div>
</Draggable>
</>

Loading…
Cancel
Save