Skip to content

Commit 46459f2

Browse files
authored
Merge pull request #4446 from ethereum/fe-hoverIcons
FE hover icons
2 parents 5988c71 + 7ae20c7 commit 46459f2

File tree

9 files changed

+287
-137
lines changed

9 files changed

+287
-137
lines changed

apps/remix-ide-e2e/src/tests/file_explorer_dragdrop.test.ts

Lines changed: 89 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -3,101 +3,101 @@ import { NightwatchBrowser } from 'nightwatch'
33
import init from '../helpers/init'
44

55
const checkBrowserIsChrome = function (browser: NightwatchBrowser) {
6-
return browser.browserName.indexOf('chrome') > -1
6+
return browser.browserName.indexOf('chrome') > -1
77
}
88

99

1010
module.exports = {
11-
'@disabled': true,
12-
before: function (browser: NightwatchBrowser, done: VoidFunction) {
13-
init(browser, done)
14-
},
15-
'drag and drop file from root to contracts #group1 ': function (browser: NightwatchBrowser) {
16-
if (checkBrowserIsChrome(browser)) {
17-
browser
18-
.clickLaunchIcon('filePanel')
19-
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
20-
.findElement('*[data-id="treeViewLitreeViewItemcontracts"]', (el) => {
21-
console.log((el as any).value.getId())
22-
const id = (el as any).value.getId()
23-
browser
24-
.waitForElementVisible('li[data-id="treeViewLitreeViewItemREADME.txt"]')
25-
.dragAndDrop('li[data-id="treeViewLitreeViewItemREADME.txt"]', id)
26-
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
27-
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
28-
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/README.txt"]')
29-
})
30-
}
31-
},
11+
'@disabled': true,
12+
before: function (browser: NightwatchBrowser, done: VoidFunction) {
13+
init(browser, done)
14+
},
15+
'drag and drop file from root to contracts #group1 ': function (browser: NightwatchBrowser) {
16+
if (checkBrowserIsChrome(browser)) {
17+
browser
18+
.clickLaunchIcon('filePanel')
19+
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
20+
.findElement('*[data-id="treeViewLitreeViewItemcontracts"]', (el) => {
21+
console.log((el as any).value.getId())
22+
const id = (el as any).value.getId()
23+
browser
24+
.waitForElementVisible('li[data-id="treeViewLitreeViewItemREADME.txt"]')
25+
.dragAndDrop('li[data-id="treeViewLitreeViewItemREADME.txt"]', id)
26+
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
27+
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
28+
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/README.txt"]')
29+
})
30+
}
31+
},
3232

33-
'drag and drop file from contracts to root #group1': function (browser: NightwatchBrowser) {
34-
if (checkBrowserIsChrome(browser)) {
35-
browser.findElement('*[data-id="treeViewUltreeViewMenu"]', (el) => {
36-
console.log((el as any).value.getId())
37-
const id = (el as any).value.getId()
38-
browser
39-
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]')
40-
.dragAndDrop('li[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]', id)
41-
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
42-
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
43-
})
44-
browser.pause(1000)
45-
.waitForElementVisible('li[data-id="treeViewLitreeViewItem1_Storage.sol"]')
46-
}
47-
},
48-
'drag and drop scripts from root to contracts #group1': function (browser: NightwatchBrowser) {
49-
if (checkBrowserIsChrome(browser)) {
50-
browser
51-
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
52-
.findElement('*[data-id="treeViewLitreeViewItemcontracts"]', (el) => {
53-
console.log((el as any).value.getId())
54-
const id = (el as any).value.getId()
55-
browser
56-
.waitForElementVisible('li[data-id="treeViewLitreeViewItemscripts"]')
57-
.dragAndDrop('li[data-id="treeViewLitreeViewItemscripts"]', id)
58-
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
59-
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
60-
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/scripts"]')
61-
})
62-
}
63-
},
64-
'drag scripts from contracts to root #group1': function (browser: NightwatchBrowser) {
65-
if (checkBrowserIsChrome(browser)) {
66-
browser.findElement('*[data-id="treeViewUltreeViewMenu"]', (el) => {
67-
console.log((el as any).value.getId())
68-
const id = (el as any).value.getId()
69-
browser
70-
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/scripts"]')
71-
.dragAndDrop('li[data-id="treeViewLitreeViewItemcontracts/scripts"]', id)
72-
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
73-
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
74-
})
75-
browser.pause(1000)
76-
.waitForElementVisible('li[data-id="treeViewLitreeViewItemscripts"]')
77-
}
78-
},
79-
'drag into nested folder': function (browser: NightwatchBrowser) {
80-
if (checkBrowserIsChrome(browser)) {
81-
browser.waitForElementVisible('li[data-id="treeViewLitreeViewItemscripts"]')
82-
.rightClick('li[data-id="treeViewLitreeViewItemscripts"]')
83-
.waitForElementPresent('[data-id="contextMenuItemnewFolder')
84-
.click('[data-id="contextMenuItemnewFolder')
85-
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
86-
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'nested')
87-
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
88-
.findElement('*[data-id="treeViewLitreeViewItemscripts/nested"]', (el) => {
89-
console.log((el as any).value.getId())
90-
const id = (el as any).value.getId()
91-
browser
92-
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/README.txt"]')
93-
.dragAndDrop('li[data-id="treeViewLitreeViewItemcontracts/README.txt"]', id)
94-
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
95-
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
96-
.waitForElementVisible('li[data-id="treeViewLitreeViewItemscripts/nested/README.txt"]')
97-
})
98-
}
33+
'drag and drop file from contracts to root #group1': function (browser: NightwatchBrowser) {
34+
if (checkBrowserIsChrome(browser)) {
35+
browser.findElement('*[data-id="treeViewUltreeViewMenu"]', (el) => {
36+
console.log((el as any).value.getId())
37+
const id = (el as any).value.getId()
38+
browser
39+
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]')
40+
.dragAndDrop('li[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]', id)
41+
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
42+
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
43+
})
44+
browser.pause(1000)
45+
.waitForElementVisible('li[data-id="treeViewLitreeViewItem1_Storage.sol"]')
46+
}
47+
},
48+
'drag and drop scripts from root to contracts #group1': function (browser: NightwatchBrowser) {
49+
if (checkBrowserIsChrome(browser)) {
50+
browser
51+
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
52+
.findElement('*[data-id="treeViewLitreeViewItemcontracts"]', (el) => {
53+
console.log((el as any).value.getId())
54+
const id = (el as any).value.getId()
55+
browser
56+
.waitForElementVisible('div[data-id="treeViewDivDraggableItemscripts"]')
57+
.dragAndDrop('div[data-id="treeViewDivDraggableItemscripts"]', id)
58+
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
59+
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
60+
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/scripts"]')
61+
})
62+
}
63+
},
64+
'drag scripts from contracts to root #group1': function (browser: NightwatchBrowser) {
65+
if (checkBrowserIsChrome(browser)) {
66+
browser.findElement('*[data-id="treeViewUltreeViewMenu"]', (el) => {
67+
console.log((el as any).value.getId())
68+
const id = (el as any).value.getId()
69+
browser
70+
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/scripts"]')
71+
.dragAndDrop('li[data-id="treeViewLitreeViewItemcontracts/scripts"]', id)
72+
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
73+
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
74+
})
75+
browser.pause(1000)
76+
.waitForElementVisible('li[data-id="treeViewLitreeViewItemscripts"]')
9977
}
78+
},
79+
'drag into nested folder': function (browser: NightwatchBrowser) {
80+
if (checkBrowserIsChrome(browser)) {
81+
browser.waitForElementVisible('li[data-id="treeViewLitreeViewItemscripts"]')
82+
.rightClick('li[data-id="treeViewLitreeViewItemscripts"]')
83+
.waitForElementPresent('[data-id="contextMenuItemnewFolder')
84+
.click('[data-id="contextMenuItemnewFolder')
85+
.waitForElementVisible('*[data-id$="fileExplorerTreeItemInput"]')
86+
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', 'nested')
87+
.sendKeys('*[data-id$="fileExplorerTreeItemInput"]', browser.Keys.ENTER)
88+
.findElement('*[data-id="treeViewLitreeViewItemscripts/nested"]', (el) => {
89+
console.log((el as any).value.getId())
90+
const id = (el as any).value.getId()
91+
browser
92+
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/README.txt"]')
93+
.dragAndDrop('li[data-id="treeViewLitreeViewItemcontracts/README.txt"]', id)
94+
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
95+
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
96+
.waitForElementVisible('li[data-id="treeViewLitreeViewItemscripts/nested/README.txt"]')
97+
})
98+
}
99+
}
100100

101101

102102

103-
}
103+
}

apps/remix-ide/src/app/tabs/locales/en/filePanel.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,4 @@
136136
"filePanel.movingFolderFailedMsg": "Unexpected error while moving folder: {src}",
137137
"filePanel.workspaceActions": "Workspace actions",
138138
"filePanel.saveCodeSample": "This code-sample workspace will not be persisted. Click here to save it."
139-
}
139+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import React, { useState } from 'react'
2+
import { CustomTooltip } from '@remix-ui/helper'
3+
import { FormattedMessage } from 'react-intl'
4+
5+
export type FileHoverIconsProps = {
6+
file: any
7+
handleNewFolderOp?: any
8+
handleNewFileOp?: any
9+
renamePathOp?: (path: string, type: string, isNew?: boolean) => void
10+
deletePathOp?: (path: string | string[]) => void | Promise<void>
11+
}
12+
13+
export function FileHoverIcons(props: FileHoverIconsProps) {
14+
const [mouseOver, setMouseOver] = useState(false)
15+
return (
16+
<>
17+
{<div className="d-flex flex-row align-items-center">
18+
{
19+
props.file.isDirectory ? (
20+
<>
21+
<CustomTooltip
22+
placement="bottom"
23+
delay={{show: 1000, hide: 0}}
24+
tooltipText={<FormattedMessage id="filePanel.createNewFolder" />}
25+
tooltipId={`filePanel.createNewFolder.${props.file.path}`}
26+
tooltipClasses="text-nowrap"
27+
>
28+
<span
29+
className="far fa-folder fa-1x mr-2 remixui_icons"
30+
onClick={async (e) => {
31+
e.stopPropagation()
32+
await props.handleNewFolderOp(props.file.path)
33+
}}
34+
style={{ cursor: mouseOver ? 'pointer' : 'default' }}
35+
onMouseEnter={(e) => setMouseOver(true)}
36+
onMouseLeave={(e) => setMouseOver(false)}
37+
></span>
38+
</CustomTooltip>
39+
<CustomTooltip
40+
placement="bottom"
41+
delay={{show: 1000, hide: 0}}
42+
tooltipText={<FormattedMessage id="filePanel.createNewFile" />}
43+
tooltipId={`fileExplorer.createNewFile.${props.file.path}`}
44+
tooltipClasses="text-nowrap"
45+
>
46+
<span
47+
className="far fa-file fa-1x remixui_icons mr-2"
48+
onClick={async (e) => {
49+
e.stopPropagation()
50+
await props.handleNewFileOp(props.file.path)
51+
}}
52+
style={{ cursor: mouseOver ? 'pointer' : 'default' }}
53+
onMouseEnter={(e) => setMouseOver(true)}
54+
onMouseLeave={(e) => setMouseOver(false)}
55+
></span>
56+
</CustomTooltip>
57+
</>
58+
) : null
59+
}
60+
<CustomTooltip
61+
placement="bottom"
62+
delay={{show: 1000, hide: 0}}
63+
tooltipText={<FormattedMessage id="filePanel.rename" />}
64+
tooltipId={`filePanel.rename.${props.file.path}`}
65+
tooltipClasses="text-nowrap"
66+
>
67+
<span
68+
className="far fa-pen fa-1x remixui_icons mr-2"
69+
onClick={(e) => {
70+
e.stopPropagation()
71+
props.renamePathOp(props.file.path, props.file.type)
72+
}}
73+
style={{ cursor: mouseOver ? 'pointer' : 'default' }}
74+
onMouseEnter={(e) => setMouseOver(true)}
75+
onMouseLeave={(e) => setMouseOver(false)}
76+
></span>
77+
</CustomTooltip>
78+
<CustomTooltip
79+
placement="bottom"
80+
delay={{show: 1000, hide: 0}}
81+
tooltipText={<FormattedMessage id="filePanel.deleteItem" />}
82+
tooltipId={`filePanel.deleteItem.${props.file.path}`}
83+
tooltipClasses="text-nowrap"
84+
>
85+
<span
86+
className="far fa-trash fa-1x remixui_icons mr-2"
87+
onClick={async (e) => {
88+
e.stopPropagation()
89+
await props.deletePathOp(props.file.path)
90+
}}
91+
style={{ cursor: mouseOver ? 'pointer' : 'default' }}
92+
onMouseEnter={(e) => setMouseOver(true)}
93+
onMouseLeave={(e) => setMouseOver(false)}
94+
></span>
95+
</CustomTooltip>
96+
</div>
97+
}
98+
</>
99+
)
100+
}

libs/remix-ui/workspace/src/lib/components/file-explorer.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState, useRef, SyntheticEvent, useTransition } from 'react' // eslint-disable-line
1+
import React, { useEffect, useState, useRef, SyntheticEvent } from 'react' // eslint-disable-line
22
import { useIntl } from 'react-intl'
33
import { TreeView } from '@remix-ui/tree-view' // eslint-disable-line
44
import { FileExplorerMenu } from './file-explorer-menu' // eslint-disable-line
@@ -27,12 +27,13 @@ export const FileExplorer = (props: FileExplorerProps) => {
2727
handleContextMenu,
2828
handleNewFileInput,
2929
handleNewFolderInput,
30+
deletePath,
3031
uploadFile,
3132
uploadFolder,
3233
fileState
3334
} = props
3435
const [state, setState] = useState<WorkSpaceState>(workspaceState)
35-
const [isPending, startTransition] = useTransition();
36+
// const [isPending, startTransition] = useTransition();
3637
const treeRef = useRef<HTMLDivElement>(null)
3738

3839
useEffect(() => {
@@ -130,6 +131,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
130131

131132
const renamePath = async (oldPath: string, newPath: string) => {
132133
try {
134+
if (oldPath === newPath) return
133135
props.dispatchRenamePath(oldPath, newPath)
134136
} catch (error) {
135137
props.modal(
@@ -404,6 +406,10 @@ export const FileExplorer = (props: FileExplorerProps) => {
404406
moveFile={handleFileMove}
405407
moveFolder={handleFolderMove}
406408
handleClickFolder={handleClickFolder}
409+
createNewFile={props.createNewFile}
410+
createNewFolder={props.createNewFolder}
411+
deletePath={deletePath}
412+
editPath={props.editModeOn}
407413
/>
408414
</div>
409415
</div>

libs/remix-ui/workspace/src/lib/components/flat-tree-drop.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { SyntheticEvent, startTransition, useEffect, useRef, useState } from 'react'
1+
import React, { SyntheticEvent, useEffect, useRef, useState } from 'react'
22
import { FileType } from '../types'
33
import { getEventTarget } from '../utils/getEventTarget'
44
import { extractParentFromKey } from '@remix-ui/helper'
@@ -23,7 +23,6 @@ export const FlatTreeDrop = (props: FlatTreeDropProps) => {
2323
const onDragOver = async (e: SyntheticEvent) => {
2424
e.preventDefault()
2525
const target = await getEventTarget(e)
26-
2726
if (!target || !target.path) {
2827
clearTimeout(timer)
2928
setFolderToOpen(null)
@@ -36,7 +35,7 @@ export const FlatTreeDrop = (props: FlatTreeDropProps) => {
3635
setFolderToOpen(null)
3736
}
3837
if (dragDestination && dragDestination.isDirectory && !expandPath.includes(dragDestination.path) && folderToOpen !== dragDestination.path && props.handleClickFolder) {
39-
38+
4039
setFolderToOpen(dragDestination.path)
4140
timer && clearTimeout(timer)
4241
setTimer(
@@ -61,7 +60,6 @@ export const FlatTreeDrop = (props: FlatTreeDropProps) => {
6160
} else {
6261
dragDestination = getFlatTreeItem(target.path)
6362
}
64-
6563
if (dragDestination.isDirectory) {
6664
if (dragSource.isDirectory) {
6765
moveFolder(dragDestination.path, dragSource.path)
@@ -84,4 +82,4 @@ export const FlatTreeDrop = (props: FlatTreeDropProps) => {
8482
onDrop={onDrop} onDragOver={onDragOver}
8583
className="d-flex h-100"
8684
>{props.children}</div>)
87-
}
85+
}

0 commit comments

Comments
 (0)