From 7fd1d71041a2d4c8ed9e5c5d9e97baba84110e12 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Mon, 7 Dec 2020 12:10:57 +0100 Subject: [PATCH] Focus context menu --- .../lib/css/file-explorer-context-menu.css | 28 ++++++ .../src/lib/file-explorer-context-menu.tsx | 55 ++++++++++++ .../file-explorer/src/lib/file-explorer.tsx | 87 ++++++++----------- .../file-explorer/src/lib/types/index.ts | 10 ++- 4 files changed, 129 insertions(+), 51 deletions(-) create mode 100644 libs/remix-ui/file-explorer/src/lib/css/file-explorer-context-menu.css create mode 100644 libs/remix-ui/file-explorer/src/lib/file-explorer-context-menu.tsx diff --git a/libs/remix-ui/file-explorer/src/lib/css/file-explorer-context-menu.css b/libs/remix-ui/file-explorer/src/lib/css/file-explorer-context-menu.css new file mode 100644 index 0000000000..f88c8825a8 --- /dev/null +++ b/libs/remix-ui/file-explorer/src/lib/css/file-explorer-context-menu.css @@ -0,0 +1,28 @@ +.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 diff --git a/libs/remix-ui/file-explorer/src/lib/file-explorer-context-menu.tsx b/libs/remix-ui/file-explorer/src/lib/file-explorer-context-menu.tsx new file mode 100644 index 0000000000..6ea088ab44 --- /dev/null +++ b/libs/remix-ui/file-explorer/src/lib/file-explorer-context-menu.tsx @@ -0,0 +1,55 @@ +import React, { useRef, useEffect } from 'react' // eslint-disable-line +import { FileExplorerContextMenuProps } from './types' + +import './css/file-explorer-context-menu.css' + +export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => { + const { actions, createNewFile, hideContextMenu, pageX, pageY, ...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 = 'absolute' + menuItemsContainer.style.bottom = '10px' + menuItemsContainer.style.top = null + } + }, [pageX, pageY]) + + const menu = () => { + return actions.map((item, index) => { + return
  • { + if (item.name === 'Create File') { + createNewFile() + } + hideContextMenu() + }}>{item.name}
  • + }) + } + + return ( + + ) +} + +export default FileExplorerContextMenu diff --git a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx index ce956c59ea..8a2e629e37 100644 --- a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx +++ b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState, useRef } from 'react' // eslint-disable-lin import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' // eslint-disable-line import { FileExplorerMenu } from './file-explorer-menu' // eslint-disable-line +import { FileExplorerContextMenu } from './file-explorer-context-menu' import { FileExplorerProps, File } from './types' import * as helper from '../../../../../apps/remix-ide/src/lib/helper' @@ -10,9 +11,11 @@ import './css/file-explorer.css' export const FileExplorer = (props: FileExplorerProps) => { const { files, name, registry, plugin } = props const containerRef = useRef(null) - const contextMenuRef = useRef({}) const [state, setState] = useState({ - focusElement: [], + focusElement: [{ + key: name, + type: 'folder' + }], focusPath: null, files: [], fileManager: null, @@ -116,13 +119,15 @@ export const FileExplorer = (props: FileExplorerProps) => { } const extractParentFromKey = (key: string):string => { + if (!key) return const keyPath = key.split('/') + keyPath.pop() - return keyPath.pop() + return keyPath.join('/') } const createNewFile = (parentFolder?: string) => { - if (!parentFolder) parentFolder = extractParentFromKey(state.focusElement[0]) + if (!parentFolder) parentFolder = state.focusElement[0] ? state.focusElement[0].type === 'folder' ? state.focusElement[0].key : extractParentFromKey(state.focusElement[0].key) : name // const self = this // modalDialogCustom.prompt('Create new file', 'File Name (e.g Untitled.sol)', 'Untitled.sol', (input) => { // if (!input) input = 'New file' @@ -154,14 +159,14 @@ export const FileExplorer = (props: FileExplorerProps) => { name: extractNameFromKey(newFileName), isDirectory: false }], - focusElement: [newFileName] + focusElement: [{ key: newFileName, type: 'file' }] } }) } else { const updatedFiles = await resolveDirectory(parentFolder, state.files) setState(prevState => { - return { ...prevState, files: updatedFiles, focusElement: [newFileName] } + return { ...prevState, files: [...updatedFiles], focusElement: [{ key: newFileName, type: 'file' }] } }) } if (newFileName.includes('_test.sol')) { @@ -250,33 +255,6 @@ export const FileExplorer = (props: FileExplorerProps) => { ) } - const contextMenu = (actions: { name: string, type: string[] }[], index: number) => { - const menu = actions.map((item) => { - return
  • { - if (item.name === 'Create File') { - createNewFile() - } - setState(prevState => { - return { ...prevState, focusContext: { element: null, x: null, y: null } } - }) - }}>{item.name}
  • - }) - - return ( - - ) - } - const onDragEnd = result => { } @@ -284,39 +262,40 @@ export const FileExplorer = (props: FileExplorerProps) => { const handleClickFile = (path) => { state.fileManager.open(path) setState(prevState => { - return { ...prevState, focusElement: [path] } + return { ...prevState, focusElement: [{ key: path, type: 'file' }] } }) } const handleClickFolder = async (path) => { if (state.ctrlKey) { - if (state.focusElement.findIndex(item => item === path) !== -1) { + if (state.focusElement.findIndex(item => item.key === path) !== -1) { setState(prevState => { - return { ...prevState, focusElement: [...prevState.focusElement.filter(item => item !== path)] } + return { ...prevState, focusElement: [...prevState.focusElement.filter(item => item.key !== path)] } }) } else { setState(prevState => { - return { ...prevState, focusElement: [...prevState.focusElement, path] } + return { ...prevState, focusElement: [...prevState.focusElement, { key: path, type: 'folder' }] } }) } } else { const files = await resolveDirectory(path, state.files) setState(prevState => { - return { ...prevState, focusElement: [path], files } + return { ...prevState, focusElement: [{ key: path, type: 'folder' }], files } }) } } - const handleContextMenuFile = (pageX, pageY, label) => { - const menuItemsContainer = contextMenuRef.current - const boundary = menuItemsContainer.getBoundingClientRect() + const handleContextMenuFile = (pageX: number, pageY: number, path: string) => { + setState(prevState => { + return { ...prevState, focusContext: { element: path, x: pageX, y: pageY } } + }) + } - if (boundary.bottom > (window.innerHeight || document.documentElement.clientHeight)) { - menuItemsContainer.style.position = 'absolute' - menuItemsContainer.style.bottom = '10px' - menuItemsContainer.style.top = null - } + const hideContextMenu = () => { + setState(prevState => { + return { ...prevState, focusContext: { element: null, x: 0, y: 0 } } + }) } const renderFiles = (file: File, index: number) => { @@ -336,7 +315,7 @@ export const FileExplorer = (props: FileExplorerProps) => { e.stopPropagation() handleClickFolder(file.path) }} - labelClass={ state.focusElement.findIndex(item => item === file.path) !== -1 ? 'bg-secondary' : '' } + labelClass={ state.focusElement.findIndex(item => item.key === file.path) !== -1 ? 'bg-secondary' : '' } controlBehaviour={ state.ctrlKey } onContextMenu={(e) => { e.preventDefault() @@ -373,12 +352,20 @@ export const FileExplorer = (props: FileExplorerProps) => { }} onContextMenu={(e) => { e.preventDefault() - handleContextMenuFile() + handleContextMenuFile(e.pageX, e.pageY, file.path) }} icon='fa fa-file' - labelClass={ state.focusElement.findIndex(item => item === file.path) !== -1 ? 'bg-secondary' : '' } + labelClass={ state.focusElement.findIndex(item => item.key === file.path) !== -1 ? 'bg-secondary' : '' } /> - { contextMenu(state.actions.filter(item => item.type.findIndex(name => name === 'file') !== -1), index) } + { (state.focusContext.element === file.path) && + item.type.findIndex(name => name === 'file') !== -1) } + hideContextMenu={hideContextMenu} + createNewFile={createNewFile} + pageX={state.focusContext.x} + pageY={state.focusContext.y} + /> + } )} diff --git a/libs/remix-ui/file-explorer/src/lib/types/index.ts b/libs/remix-ui/file-explorer/src/lib/types/index.ts index 581d0acbf5..ac28a26570 100644 --- a/libs/remix-ui/file-explorer/src/lib/types/index.ts +++ b/libs/remix-ui/file-explorer/src/lib/types/index.ts @@ -19,7 +19,15 @@ export interface FileExplorerMenuProps { menuItems: string[], fileManager: any, addFile: (parent: string, fileName: string) => void, - createNewFile: () => void, + createNewFile: (parentFolder?: string) => void, files: any, accessToken: string } + +export interface FileExplorerContextMenuProps { + actions: { name: string, type: string[] }[], + createNewFile: (parentFolder?: string) => void + hideContextMenu: () => void, + pageX: number, + pageY: number +}