Skip to content

Commit fca2055

Browse files
authored
Merge pull request #752 from ethereum/context-menu
Context Menu For Plugins
2 parents 2af196b + 0c7f92e commit fca2055

File tree

4 files changed

+51
-9
lines changed

4 files changed

+51
-9
lines changed

apps/remix-ide/src/app/panels/file-panel.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ module.exports = class Filepanel extends ViewPlugin {
6666
}
6767
}
6868
this.reset = false
69+
this.registeredMenuItems = []
6970
this.el = yo`
7071
<div id="fileExplorerView">
7172
</div>
@@ -102,6 +103,20 @@ module.exports = class Filepanel extends ViewPlugin {
102103
return this.el
103104
}
104105

106+
/**
107+
*
108+
* @param item { id: string, name: string, type?: string[], path?: string[], extension?: string[], pattern?: string[] }
109+
* @param callback (...args) => void
110+
*/
111+
registerContextMenuItem (item) {
112+
if (!item) throw new Error('Invalid register context menu argument')
113+
if (!item.name || !item.id) throw new Error('Item name and id is mandatory')
114+
if (!item.type && !item.path && !item.extension && !item.pattern) throw new Error('Invalid file matching criteria provided')
115+
116+
this.registeredMenuItems = [...this.registeredMenuItems, item]
117+
this.renderComponent()
118+
}
119+
105120
renderComponent () {
106121
ReactDOM.render(
107122
<div className='remixui_container'>
@@ -116,6 +131,7 @@ module.exports = class Filepanel extends ViewPlugin {
116131
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
117132
plugin={this}
118133
focusRoot={this.reset}
134+
contextMenuItems={this.registeredMenuItems}
119135
/>
120136
</div>
121137
<div className='pl-2 filesystemexplorer remixui_treeview'>
@@ -127,6 +143,7 @@ module.exports = class Filepanel extends ViewPlugin {
127143
menuItems={['createNewFile', 'createNewFolder']}
128144
plugin={this}
129145
focusRoot={this.reset}
146+
contextMenuItems={this.registeredMenuItems}
130147
/>
131148
}
132149
</div>

libs/remix-ui/file-explorer/src/lib/file-explorer-context-menu.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FileExplorerContextMenuProps } from './types'
44
import './css/file-explorer-context-menu.css'
55

66
export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => {
7-
const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, publishToGist, runScript, pageX, pageY, path, type, ...otherProps } = props
7+
const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, publishToGist, runScript, emit, pageX, pageY, path, type, ...otherProps } = props
88
const contextMenuRef = useRef(null)
99

1010
useEffect(() => {
@@ -24,10 +24,10 @@ export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) =>
2424

2525
const menu = () => {
2626
return actions.filter(item => {
27-
if (item.type.findIndex(name => name === type) !== -1) return true
28-
else if (item.path.findIndex(key => key === path) !== -1) return true
29-
else if (item.extension.findIndex(ext => path.endsWith(ext)) !== -1) return true
30-
else if (item.pattern.filter(value => path.match(new RegExp(value))).length > 0) return true
27+
if (item.type && Array.isArray(item.type) && (item.type.findIndex(name => name === type) !== -1)) return true
28+
else if (item.path && Array.isArray(item.path) && (item.path.findIndex(key => key === path) !== -1)) return true
29+
else if (item.extension && Array.isArray(item.extension) && (item.extension.findIndex(ext => path.endsWith(ext)) !== -1)) return true
30+
else if (item.pattern && Array.isArray(item.pattern) && (item.pattern.filter(value => path.match(new RegExp(value))).length > 0)) return true
3131
else return false
3232
}).map((item, index) => {
3333
return <li
@@ -56,6 +56,7 @@ export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) =>
5656
runScript(path)
5757
break
5858
default:
59+
emit && emit(item.id, path)
5960
break
6061
}
6162
hideContextMenu()

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import './css/file-explorer.css'
1616
const queryParams = new QueryParams()
1717

1818
export const FileExplorer = (props: FileExplorerProps) => {
19-
const { filesProvider, name, registry, plugin, focusRoot } = props
19+
const { filesProvider, name, registry, plugin, focusRoot, contextMenuItems } = props
2020
const [state, setState] = useState({
2121
focusElement: [{
2222
key: name,
@@ -76,36 +76,42 @@ export const FileExplorer = (props: FileExplorerProps) => {
7676
const accessToken = config.get('settings/gist-access-token')
7777
const files = await fetchDirectoryContent(name)
7878
const actions = [{
79+
id: 'newFile',
7980
name: 'New File',
8081
type: ['folder'],
8182
path: [],
8283
extension: [],
8384
pattern: []
8485
}, {
86+
id: 'newFolder',
8587
name: 'New Folder',
8688
type: ['folder'],
8789
path: [],
8890
extension: [],
8991
pattern: []
9092
}, {
93+
id: 'rename',
9194
name: 'Rename',
9295
type: ['file', 'folder'],
9396
path: [],
9497
extension: [],
9598
pattern: []
9699
}, {
100+
id: 'delete',
97101
name: 'Delete',
98102
type: ['file', 'folder'],
99103
path: [],
100104
extension: [],
101105
pattern: []
102106
}, {
107+
id: 'pushChangesToGist',
103108
name: 'Push changes to gist',
104109
type: [],
105110
path: [],
106111
extension: [],
107112
pattern: ['^browser/gists/([0-9]|[a-z])*$']
108113
}, {
114+
id: 'run',
109115
name: 'Run',
110116
type: [],
111117
path: [],
@@ -165,6 +171,17 @@ export const FileExplorer = (props: FileExplorerProps) => {
165171
}
166172
}, [focusRoot])
167173

174+
useEffect(() => {
175+
if (contextMenuItems) {
176+
setState(prevState => {
177+
// filter duplicate items
178+
const items = contextMenuItems.filter(({ name }) => prevState.actions.findIndex(action => action.name === name) === -1)
179+
180+
return { ...prevState, actions: [...prevState.actions, ...items] }
181+
})
182+
}
183+
}, [contextMenuItems])
184+
168185
const resolveDirectory = async (folderPath, dir: File[], isChild = false): Promise<File[]> => {
169186
if (!isChild && (state.focusEdit.element === 'browser/blank') && state.focusEdit.isNew && (dir.findIndex(({ path }) => path === 'browser/blank') === -1)) {
170187
dir = state.focusEdit.type === 'file' ? [...dir, {
@@ -603,6 +620,10 @@ export const FileExplorer = (props: FileExplorerProps) => {
603620
})
604621
}
605622

623+
const emitContextMenuEvent = (id: string, path: string) => {
624+
plugin.emit(id, path)
625+
}
626+
606627
const handleHideModal = () => {
607628
setState(prevState => {
608629
return { ...prevState, modalOptions: { ...state.modalOptions, hide: true } }
@@ -839,6 +860,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
839860
deletePath={deletePath}
840861
renamePath={editModeOn}
841862
publishToGist={publishToGist}
863+
emit={emitContextMenuEvent}
842864
pageX={state.focusContext.x}
843865
pageY={state.focusContext.y}
844866
path={file.path}
@@ -875,6 +897,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
875897
deletePath={deletePath}
876898
renamePath={editModeOn}
877899
runScript={runScript}
900+
emit={emitContextMenuEvent}
878901
pageX={state.focusContext.x}
879902
pageY={state.focusContext.y}
880903
path={file.path}

libs/remix-ui/file-explorer/src/lib/types/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ export interface FileExplorerProps {
55
filesProvider: any,
66
menuItems?: string[],
77
plugin: any,
8-
focusRoot: boolean
8+
focusRoot: boolean,
9+
contextMenuItems: { name: string, type: string[], path: string[], extension: string[], pattern: string[] }[]
910
}
1011

1112
export interface File {
@@ -26,15 +27,15 @@ export interface FileExplorerMenuProps {
2627
}
2728

2829
export interface FileExplorerContextMenuProps {
29-
actions: { name: string, type: string[], path: string[], extension: string[], pattern: string[] }[],
30+
actions: { name: string, type: string[], path: string[], extension: string[], pattern: string[], id: string }[],
3031
createNewFile: (folder?: string) => void,
3132
createNewFolder: (parentFolder?: string) => void,
3233
deletePath: (path: string) => void,
3334
renamePath: (path: string, type: string) => void,
3435
hideContextMenu: () => void,
35-
extractParentFromKey?: (key: string) => string,
3636
publishToGist?: () => void,
3737
runScript?: (path: string) => void,
38+
emit?: (id: string, path: string) => void,
3839
pageX: number,
3940
pageY: number,
4041
path: string,

0 commit comments

Comments
 (0)