diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js
index 91f33673dd5..ca5e94417e8 100644
--- a/apps/remix-ide/src/app/panels/file-panel.js
+++ b/apps/remix-ide/src/app/panels/file-panel.js
@@ -66,6 +66,7 @@ module.exports = class Filepanel extends ViewPlugin {
}
}
this.reset = false
+ this.registeredMenuItems = []
this.el = yo`
@@ -102,6 +103,20 @@ module.exports = class Filepanel extends ViewPlugin {
return this.el
}
+ /**
+ *
+ * @param item { id: string, name: string, type?: string[], path?: string[], extension?: string[], pattern?: string[] }
+ * @param callback (...args) => void
+ */
+ registerContextMenuItem (item) {
+ if (!item) throw new Error('Invalid register context menu argument')
+ if (!item.name || !item.id) throw new Error('Item name and id is mandatory')
+ if (!item.type && !item.path && !item.extension && !item.pattern) throw new Error('Invalid file matching criteria provided')
+
+ this.registeredMenuItems = [...this.registeredMenuItems, item]
+ this.renderComponent()
+ }
+
renderComponent () {
ReactDOM.render(
@@ -116,6 +131,7 @@ module.exports = class Filepanel extends ViewPlugin {
menuItems={['createNewFile', 'createNewFolder', 'publishToGist', canUpload ? 'uploadFile' : '']}
plugin={this}
focusRoot={this.reset}
+ contextMenuItems={this.registeredMenuItems}
/>
@@ -127,6 +143,7 @@ module.exports = class Filepanel extends ViewPlugin {
menuItems={['createNewFile', 'createNewFolder']}
plugin={this}
focusRoot={this.reset}
+ contextMenuItems={this.registeredMenuItems}
/>
}
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
index aebd8eb0534..3a842d91b3b 100644
--- 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
@@ -4,7 +4,7 @@ import { FileExplorerContextMenuProps } from './types'
import './css/file-explorer-context-menu.css'
export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => {
- const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, publishToGist, runScript, pageX, pageY, path, type, ...otherProps } = props
+ const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, publishToGist, runScript, emit, pageX, pageY, path, type, ...otherProps } = props
const contextMenuRef = useRef(null)
useEffect(() => {
@@ -24,10 +24,10 @@ export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) =>
const menu = () => {
return actions.filter(item => {
- if (item.type.findIndex(name => name === type) !== -1) return true
- else if (item.path.findIndex(key => key === path) !== -1) return true
- else if (item.extension.findIndex(ext => path.endsWith(ext)) !== -1) return true
- else if (item.pattern.filter(value => path.match(new RegExp(value))).length > 0) return true
+ if (item.type && Array.isArray(item.type) && (item.type.findIndex(name => name === type) !== -1)) return true
+ else if (item.path && Array.isArray(item.path) && (item.path.findIndex(key => key === path) !== -1)) return true
+ else if (item.extension && Array.isArray(item.extension) && (item.extension.findIndex(ext => path.endsWith(ext)) !== -1)) return true
+ else if (item.pattern && Array.isArray(item.pattern) && (item.pattern.filter(value => path.match(new RegExp(value))).length > 0)) return true
else return false
}).map((item, index) => {
return
runScript(path)
break
default:
+ emit && emit(item.id, path)
break
}
hideContextMenu()
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 16ab0285996..e7e2f6e31ff 100644
--- a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx
+++ b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx
@@ -16,7 +16,7 @@ import './css/file-explorer.css'
const queryParams = new QueryParams()
export const FileExplorer = (props: FileExplorerProps) => {
- const { filesProvider, name, registry, plugin, focusRoot } = props
+ const { filesProvider, name, registry, plugin, focusRoot, contextMenuItems } = props
const [state, setState] = useState({
focusElement: [{
key: name,
@@ -76,36 +76,42 @@ export const FileExplorer = (props: FileExplorerProps) => {
const accessToken = config.get('settings/gist-access-token')
const files = await fetchDirectoryContent(name)
const actions = [{
+ id: 'newFile',
name: 'New File',
type: ['folder'],
path: [],
extension: [],
pattern: []
}, {
+ id: 'newFolder',
name: 'New Folder',
type: ['folder'],
path: [],
extension: [],
pattern: []
}, {
+ id: 'rename',
name: 'Rename',
type: ['file', 'folder'],
path: [],
extension: [],
pattern: []
}, {
+ id: 'delete',
name: 'Delete',
type: ['file', 'folder'],
path: [],
extension: [],
pattern: []
}, {
+ id: 'pushChangesToGist',
name: 'Push changes to gist',
type: [],
path: [],
extension: [],
pattern: ['^browser/gists/([0-9]|[a-z])*$']
}, {
+ id: 'run',
name: 'Run',
type: [],
path: [],
@@ -165,6 +171,17 @@ export const FileExplorer = (props: FileExplorerProps) => {
}
}, [focusRoot])
+ useEffect(() => {
+ if (contextMenuItems) {
+ setState(prevState => {
+ // filter duplicate items
+ const items = contextMenuItems.filter(({ name }) => prevState.actions.findIndex(action => action.name === name) === -1)
+
+ return { ...prevState, actions: [...prevState.actions, ...items] }
+ })
+ }
+ }, [contextMenuItems])
+
const resolveDirectory = async (folderPath, dir: File[], isChild = false): Promise => {
if (!isChild && (state.focusEdit.element === 'browser/blank') && state.focusEdit.isNew && (dir.findIndex(({ path }) => path === 'browser/blank') === -1)) {
dir = state.focusEdit.type === 'file' ? [...dir, {
@@ -603,6 +620,10 @@ export const FileExplorer = (props: FileExplorerProps) => {
})
}
+ const emitContextMenuEvent = (id: string, path: string) => {
+ plugin.emit(id, path)
+ }
+
const handleHideModal = () => {
setState(prevState => {
return { ...prevState, modalOptions: { ...state.modalOptions, hide: true } }
@@ -839,6 +860,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
deletePath={deletePath}
renamePath={editModeOn}
publishToGist={publishToGist}
+ emit={emitContextMenuEvent}
pageX={state.focusContext.x}
pageY={state.focusContext.y}
path={file.path}
@@ -875,6 +897,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
deletePath={deletePath}
renamePath={editModeOn}
runScript={runScript}
+ emit={emitContextMenuEvent}
pageX={state.focusContext.x}
pageY={state.focusContext.y}
path={file.path}
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 e2225dfc241..caa203ab7b1 100644
--- a/libs/remix-ui/file-explorer/src/lib/types/index.ts
+++ b/libs/remix-ui/file-explorer/src/lib/types/index.ts
@@ -5,7 +5,8 @@ export interface FileExplorerProps {
filesProvider: any,
menuItems?: string[],
plugin: any,
- focusRoot: boolean
+ focusRoot: boolean,
+ contextMenuItems: { name: string, type: string[], path: string[], extension: string[], pattern: string[] }[]
}
export interface File {
@@ -26,15 +27,15 @@ export interface FileExplorerMenuProps {
}
export interface FileExplorerContextMenuProps {
- actions: { name: string, type: string[], path: string[], extension: string[], pattern: string[] }[],
+ actions: { name: string, type: string[], path: string[], extension: string[], pattern: string[], id: string }[],
createNewFile: (folder?: string) => void,
createNewFolder: (parentFolder?: string) => void,
deletePath: (path: string) => void,
renamePath: (path: string, type: string) => void,
hideContextMenu: () => void,
- extractParentFromKey?: (key: string) => string,
publishToGist?: () => void,
runScript?: (path: string) => void,
+ emit?: (id: string, path: string) => void,
pageX: number,
pageY: number,
path: string,