add file decorators

pull/5370/head
filip mertens 2 years ago
parent c26846ca06
commit a75247681c
  1. 10
      apps/remix-ide/src/app.js
  2. 6
      apps/remix-ide/src/app/panels/file-panel.js
  3. 4
      apps/remix-ide/src/app/panels/tab-proxy.js
  4. 26
      apps/remix-ide/src/app/plugins/code-parser.tsx
  5. 23
      apps/remix-ide/src/app/plugins/file-decorator.ts
  6. 4
      libs/remix-core-plugin/src/index.ts
  7. 0
      libs/remix-ui/file-decorators/.babelrc
  8. 0
      libs/remix-ui/file-decorators/.eslintrc
  9. 3
      libs/remix-ui/file-decorators/src/index.ts
  10. 48
      libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx
  11. 8
      libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx
  12. 11
      libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx
  13. 11
      libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx
  14. 17
      libs/remix-ui/file-decorators/src/lib/types/index.ts
  15. 0
      libs/remix-ui/file-decorators/tsconfig.json
  16. 0
      libs/remix-ui/file-decorators/tsconfig.lib.json
  17. 3
      libs/remix-ui/file-states/src/index.ts
  18. 48
      libs/remix-ui/file-states/src/lib/components/file-state-icon.tsx
  19. 11
      libs/remix-ui/file-states/src/lib/components/filestates/file-state-error-icon.tsx
  20. 11
      libs/remix-ui/file-states/src/lib/components/filestates/file-state-warning-icon.tsx
  21. 37
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  22. 13
      libs/remix-ui/workspace/src/lib/actions/events.ts
  23. 7
      libs/remix-ui/workspace/src/lib/actions/payload.ts
  24. 2
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  25. 15
      libs/remix-ui/workspace/src/lib/components/file-label.tsx
  26. 15
      libs/remix-ui/workspace/src/lib/components/file-render.tsx
  27. 7
      libs/remix-ui/workspace/src/lib/reducers/workspace.ts
  28. 4
      libs/remix-ui/workspace/src/lib/types/index.ts
  29. 2
      nx.json
  30. 2
      package.json
  31. 2
      tsconfig.base.json
  32. 10
      workspace.json

@ -13,7 +13,9 @@ import { LandingPage } from './app/ui/landing-page/landing-page'
import { MainPanel } from './app/components/main-panel' import { MainPanel } from './app/components/main-panel'
import { PermissionHandlerPlugin } from './app/plugins/permission-handler-plugin' import { PermissionHandlerPlugin } from './app/plugins/permission-handler-plugin'
import { AstWalker } from '@remix-project/remix-astwalker' import { AstWalker } from '@remix-project/remix-astwalker'
import { LinkLibraries, DeployLibraries, OpenZeppelinProxy, CodeParser, FileStates } from '@remix-project/core-plugin' import { LinkLibraries, DeployLibraries, OpenZeppelinProxy } from '@remix-project/core-plugin'
import { CodeParser } from './app/plugins/code-parser'
import { FileDecorator } from './app/plugins/file-decorator'
import { WalkthroughService } from './walkthroughService' import { WalkthroughService } from './walkthroughService'
@ -204,7 +206,7 @@ class AppComponent {
) )
const contextualListener = new EditorContextListener(new AstWalker()) const contextualListener = new EditorContextListener(new AstWalker())
const codeParser = new CodeParser(new AstWalker()) const codeParser = new CodeParser(new AstWalker())
const fileStates = new FileStates() const fileDecorator = new FileDecorator()
this.notification = new NotificationPlugin() this.notification = new NotificationPlugin()
@ -230,7 +232,7 @@ class AppComponent {
offsetToLineColumnConverter, offsetToLineColumnConverter,
contextualListener, contextualListener,
codeParser, codeParser,
fileStates, fileDecorator,
terminal, terminal,
web3Provider, web3Provider,
compileAndRun, compileAndRun,
@ -357,7 +359,7 @@ class AppComponent {
await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await this.appManager.activatePlugin(['home']) await this.appManager.activatePlugin(['home'])
await this.appManager.activatePlugin(['settings', 'config']) await this.appManager.activatePlugin(['settings', 'config'])
await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'codeParser', 'fileStates', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler']) await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'codeParser', 'fileDecorator', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler'])
await this.appManager.activatePlugin(['settings']) await this.appManager.activatePlugin(['settings'])
await this.appManager.activatePlugin(['walkthrough','storage', 'search','compileAndRun']) await this.appManager.activatePlugin(['walkthrough','storage', 'search','compileAndRun'])

@ -30,7 +30,7 @@ const { SlitherHandle } = require('../files/slither-handle.js')
const profile = { const profile = {
name: 'filePanel', name: 'filePanel',
displayName: 'File explorers', displayName: 'File explorers',
methods: ['createNewFile', 'uploadFile', 'getCurrentWorkspace', 'getWorkspaces', 'createWorkspace', 'setWorkspace', 'registerContextMenuItem', 'renameWorkspace', 'deleteWorkspace', 'setFileState'], methods: ['createNewFile', 'uploadFile', 'getCurrentWorkspace', 'getWorkspaces', 'createWorkspace', 'setWorkspace', 'registerContextMenuItem', 'renameWorkspace', 'deleteWorkspace'],
events: ['setWorkspace', 'workspaceRenamed', 'workspaceDeleted', 'workspaceCreated'], events: ['setWorkspace', 'workspaceRenamed', 'workspaceDeleted', 'workspaceCreated'],
icon: 'assets/img/fileManager.webp', icon: 'assets/img/fileManager.webp',
description: ' - ', description: ' - ',
@ -85,10 +85,6 @@ module.exports = class Filepanel extends ViewPlugin {
}) })
} }
async setFileState (fileState) {
console.log(fileState)
this.emit('setFileState', fileState)
}
getCurrentWorkspace () { getCurrentWorkspace () {
return this.currentWorkspaceMetadata return this.currentWorkspaceMetadata

@ -170,8 +170,8 @@ export class TabProxy extends Plugin {
this.removeTab(profile.name) this.removeTab(profile.name)
}) })
this.on('fileStates', 'fileStateChanged', async (items) => { this.on('fileDecorator', 'fileDecoratorsChanged', async (items) => {
this.tabsApi.setFileStates(items) this.tabsApi.setFileDecorations(items)
}) })
try { try {

@ -8,7 +8,7 @@ import { AstNode, CompilationError, CompilationResult, CompilationSource } from
import { helper } from '@remix-project/remix-solidity' import { helper } from '@remix-project/remix-solidity'
import React from 'react' import React from 'react'
import { fileState, fileStateType } from '@remix-ui/file-states' import { fileDecoration, fileDecorationType } from '@remix-ui/file-decorators'
// eslint-disable-next-line // eslint-disable-next-line
@ -121,18 +121,18 @@ export class CodeParser extends Plugin {
try { try {
let fileState:fileState = { let fileState:fileDecoration = {
path: this.currentFile, path: this.currentFile,
isDirectory: false, isDirectory: false,
fileStateType: fileStateType.Custom, fileStateType: fileDecorationType.Error,
fileStateLabelClass: 'text-success', fileStateLabelClass: 'text-success',
fileStateIconClass: '', fileStateIconClass: '',
fileStateIcon: <i className="text-success fas fa-smile"></i>, fileStateIcon: <i className="text-success fas fa-smile"></i>,
comment: '', text: '2',
owner: 'code-parser', owner: 'code-parser',
bubble: true bubble: true
} }
await this.call('fileStates', 'setFileState', fileState) await this.call('fileDecorator', 'setFileDecorators', fileState)
fileState = { fileState = {
...fileState, ...fileState,
fileStateLabelClass: 'text-danger', fileStateLabelClass: 'text-danger',
@ -144,41 +144,41 @@ export class CodeParser extends Plugin {
fileStateLabelClass: 'text-danger', fileStateLabelClass: 'text-danger',
fileStateIcon: <div className='btn btn-danger btn-sm'>call rob now!</div>, fileStateIcon: <div className='btn btn-danger btn-sm'>call rob now!</div>,
} }
await this.call('fileStates', 'setFileState', fileState) await this.call('fileDecorator', 'setFileDecorators', fileState)
const states:fileState[] = [ const states:fileDecoration[] = [
{ {
path: 'contracts/2_Owner.sol', path: 'contracts/2_Owner.sol',
isDirectory: false, isDirectory: false,
fileStateType: fileStateType.Custom, fileStateType: fileDecorationType.Custom,
fileStateLabelClass: 'text-success', fileStateLabelClass: 'text-success',
fileStateIconClass: '', fileStateIconClass: '',
fileStateIcon: <i className="text-success fas fa-smile"></i>, fileStateIcon: <i className="text-success fas fa-smile"></i>,
comment: '', text: '',
owner: 'code-parser', owner: 'code-parser',
bubble: true bubble: true
}, },
{ {
path: 'contracts/2_Owner.sol', path: 'contracts/2_Owner.sol',
isDirectory: false, isDirectory: false,
fileStateType: fileStateType.Custom, fileStateType: fileDecorationType.Custom,
fileStateLabelClass: 'text-danger', fileStateLabelClass: 'text-danger',
fileStateIconClass: '', fileStateIconClass: '',
fileStateIcon: <i className="text-danger fas fa-smile"></i>, fileStateIcon: <i className="text-danger fas fa-smile"></i>,
comment: '', text: '',
owner: 'code-parser', owner: 'code-parser',
bubble: true bubble: true
} }
] ]
await this.call('fileStates', 'setFileState', states) await this.call('fileDecorator', 'setFileDecorators', states)
} catch (e) { } catch (e) {
console.log('error calling filePanel', e) console.log('error calling filePanel', e)
} }
} else { } else {
await this.call('fileStates', 'setFileState', [{ await this.call('fileDecorator', 'setFileDecorators', [{
path: this.currentFile, path: this.currentFile,
isDirectory: false, isDirectory: false,
fileStateType: [], fileStateType: [],

@ -3,18 +3,18 @@
import { default as deepequal } from 'deep-equal' // eslint-disable-line import { default as deepequal } from 'deep-equal' // eslint-disable-line
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { fileState } from '@remix-ui/file-states' import { fileDecoration } from '@remix-ui/file-decorators'
const profile = { const profile = {
name: 'fileStates', name: 'fileDecorator',
desciption: 'Keeps state of the files', desciption: 'Keeps decorators of the files',
methods: ['setFileState'], methods: ['setFileDecorators'],
events: ['fileStateChanged'], events: ['fileDecoratorsChanged'],
version: '0.0.1' version: '0.0.1'
} }
export class FileStates extends Plugin { export class FileDecorator extends Plugin {
private _fileStates: fileState[] = [] private _fileStates: fileDecoration[] = []
constructor() { constructor() {
super(profile) super(profile)
} }
@ -22,9 +22,9 @@ export class FileStates extends Plugin {
* *
* @param fileStates Array of file states * @param fileStates Array of file states
*/ */
async setFileState(fileStates: fileState[] | fileState) { async setFileDecorators(fileStates: fileDecoration[] | fileDecoration) {
const workspace = await this.call('filePanel', 'getCurrentWorkspace') const workspace = await this.call('filePanel', 'getCurrentWorkspace')
function sortByPath( a, b ) { function sortByPath( a: fileDecoration, b: fileDecoration ) {
if ( a.path < b.path ){ if ( a.path < b.path ){
return -1; return -1;
} }
@ -40,7 +40,7 @@ export class FileStates extends Plugin {
state.workspace = workspace state.workspace = workspace
}) })
const filteredState = this._fileStates.filter((state) => { const filteredState = this._fileStates.filter((state) => {
const index = fileStatesPayload.findIndex((payloadFileState: fileState) => { const index = fileStatesPayload.findIndex((payloadFileState: fileDecoration) => {
return payloadFileState.owner == state.owner && payloadFileState.path == state.path return payloadFileState.owner == state.owner && payloadFileState.path == state.path
}) })
return index == -1 return index == -1
@ -49,9 +49,8 @@ export class FileStates extends Plugin {
if (!deepequal(newState, this._fileStates)) { if (!deepequal(newState, this._fileStates)) {
this._fileStates = newState this._fileStates = newState
console.log('fileStates', this._fileStates) console.log('fileStates', this._fileStates)
this.emit('fileStateChanged', this._fileStates) this.emit('fileDecoratorsChanged', this._fileStates)
} }
} }
} }

@ -7,6 +7,4 @@ export { EditorContextListener } from './lib/editor-context-listener'
export { GistHandler } from './lib/gist-handler' export { GistHandler } from './lib/gist-handler'
export * from './types/contract' export * from './types/contract'
export { LinkLibraries, DeployLibraries } from './lib/link-libraries' export { LinkLibraries, DeployLibraries } from './lib/link-libraries'
export { OpenZeppelinProxy } from './lib/openzeppelin-proxy' export { OpenZeppelinProxy } from './lib/openzeppelin-proxy'
export { CodeParser } from './lib/code-parser'
export { FileStates } from './lib/file-states'

@ -0,0 +1,3 @@
export { fileDecoration, fileDecorationType, FileType } from './lib/types/index'
export { FileDecorationIcons } from './lib/components/file-decoration-icon'

@ -0,0 +1,48 @@
// eslint-disable-next-line no-use-before-define
import React, { useEffect, useState } from 'react'
import { fileDecoration, fileDecorationType, FileType } from '../types'
import FileDecorationCustomIcon from './filedecorationicons/file-decoration-custom-icon'
import FileDecorationErrorIcon from './filedecorationicons/file-decoration-error-icon'
import FileDecorationWarningIcon from './filedecorationicons/file-decoration-warning-icon'
export type fileDecorationProps = {
file: FileType,
fileDecorations: fileDecoration[]
}
export const FileDecorationIcons = (props: fileDecorationProps) => {
const [states, setStates] = useState<fileDecoration[]>([])
useEffect(() => {
//console.log(props.file)
//console.log(props.fileState)
setStates(props.fileDecorations.filter((fileDecoration) => fileDecoration.path === props.file.path || `${fileDecoration.workspace.name}/${fileDecoration.path}` === props.file.path))
}, [props.fileDecorations])
const getTags = function () {
if (states && states.length) {
const elements: JSX.Element[] = []
for (const [index, state] of states.entries()) {
switch (state.fileStateType) {
case fileDecorationType.Error:
elements.push(<FileDecorationErrorIcon fileState={state} key={index} />)
break
case fileDecorationType.Warning:
elements.push(<FileDecorationWarningIcon fileState={state} key={index}/>)
break
case fileDecorationType.Custom:
elements.push(<FileDecorationCustomIcon fileState={state} key={index} />)
break
}
}
return elements
}
}
return <>
{getTags()}
</>
}
export default FileDecorationIcons

@ -1,13 +1,13 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React from 'react'
import { fileState } from '../../types' import { fileDecoration } from '../../types'
const FileStateCustomIcon = (props: { const FileDecorationCustomIcon = (props: {
fileState: fileState fileState: fileDecoration
}) => { }) => {
return <><span className={`${props.fileState.fileStateIconClass}pr-2`}> return <><span className={`${props.fileState.fileStateIconClass}pr-2`}>
{props.fileState.fileStateIcon} {props.fileState.fileStateIcon}
</span></> </span></>
} }
export default FileStateCustomIcon export default FileDecorationCustomIcon

@ -0,0 +1,11 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import { fileDecoration } from '../../types'
const FileDecorationErrorIcon = (props: {
fileState: fileDecoration
}) => {
return <><span className={`${props.fileState.fileStateIconClass} text-danger pr-2`}>{props.fileState.text}</span></>
}
export default FileDecorationErrorIcon

@ -0,0 +1,11 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import { fileDecoration } from '../../types'
const FileDecorationWarningIcon = (props: {
fileState: fileDecoration
}) => {
return <><span className={`${props.fileState.fileStateIconClass} text-warning pr-2`}>{props.fileState.text}</span></>
}
export default FileDecorationWarningIcon

@ -1,4 +1,4 @@
export enum fileStateType { export enum fileDecorationType {
Error = 'ERROR', Error = 'ERROR',
Warning = 'WARNING', Warning = 'WARNING',
Success = 'SUCCESS', Success = 'SUCCESS',
@ -15,23 +15,24 @@ export enum fileStateType {
Custom = 'CUSTOM', Custom = 'CUSTOM',
} }
export type fileState = { export type fileDecoration = {
path: string, path: string,
isDirectory: boolean, isDirectory: boolean,
fileStateType: fileStateType, fileStateType: fileDecorationType,
fileStateLabelClass: string, fileStateLabelClass: string,
fileStateIconClass: string, fileStateIconClass: string,
fileStateIcon: string | HTMLDivElement | JSX.Element, fileStateIcon: string | HTMLDivElement | JSX.Element,
bubble: boolean, bubble: boolean,
comment: string, text?: string,
owner: string, owner: string,
workspace?: string workspace?: any
tooltip?: string
} }
export interface FileType { export interface FileType {
path: string, path: string,
name: string, name?: string,
isDirectory: boolean, isDirectory?: boolean,
type: 'folder' | 'file' | 'gist', type?: 'folder' | 'file' | 'gist',
child?: File[] child?: File[]
} }

@ -1,3 +0,0 @@
export { fileState, fileStateType } from './lib/types/index'
export { FileStateIcons } from './lib/components/file-state-icon'

@ -1,48 +0,0 @@
// eslint-disable-next-line no-use-before-define
import React, { useEffect, useState } from 'react'
import { fileState, fileStateType, FileType } from '../types'
import FileStateCustomIcon from './filestates/file-state-custom-icon'
import FileStateErrorIcon from './filestates/file-state-error-icon'
import FileStateWarningIcon from './filestates/file-state-warning-icon'
export type fileStateProps = {
file: FileType,
fileState: fileState[]
}
export const FileStateIcons = (props: fileStateProps) => {
const [states, setStates] = useState<fileState[]>([])
useEffect(() => {
//console.log(props.file)
//console.log(props.fileState)
setStates(props.fileState.filter((st) => st.path === props.file.path))
}, [props.fileState])
const getTags = function () {
if (states && states.length) {
const elements: JSX.Element[] = []
for (const [index, state] of states.entries()) {
switch (state.fileStateType) {
case fileStateType.Error:
elements.push(<FileStateErrorIcon fileState={state} key={index} />)
break
case fileStateType.Warning:
elements.push(<FileStateWarningIcon fileState={state} key={index}/>)
break
case fileStateType.Custom:
elements.push(<FileStateCustomIcon fileState={state} key={index} />)
break
}
}
return elements
}
}
return <>
{getTags()}
</>
}
export default FileStateIcons

@ -1,11 +0,0 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import { fileState } from '../../types'
const FileStateErrorIcon = (props: {
fileState: fileState
}) => {
return <><span className={`${props.fileState.fileStateIconClass} text-danger pr-2`}>{props.fileState.comment}</span></>
}
export default FileStateErrorIcon

@ -1,11 +0,0 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import { fileState } from '../../types'
const FileStateWarningIcon = (props: {
fileState: fileState
}) => {
return <><span className={`${props.fileState.fileStateIconClass} text-warning pr-2`}>{props.fileState.comment}</span></>
}
export default FileStateWarningIcon

@ -1,5 +1,7 @@
import { fileState } from '@remix-ui/workspace'
import { fileDecoration, FileDecorationIcons } from '@remix-ui/file-decorators'
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs' import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'
import './remix-ui-tabs.css' import './remix-ui-tabs.css'
@ -23,7 +25,7 @@ export interface TabsUIApi {
interface ITabsState { interface ITabsState {
selectedIndex: number, selectedIndex: number,
fileStates: fileState[], fileDecorations: fileDecoration[],
} }
interface ITabsAction { interface ITabsAction {
@ -34,7 +36,7 @@ interface ITabsAction {
const initialTabsState: ITabsState = { const initialTabsState: ITabsState = {
selectedIndex: -1, selectedIndex: -1,
fileStates: [], fileDecorations: [],
} }
const tabsReducer = (state: ITabsState, action: ITabsAction) => { const tabsReducer = (state: ITabsState, action: ITabsAction) => {
@ -44,10 +46,10 @@ const tabsReducer = (state: ITabsState, action: ITabsAction) => {
...state, ...state,
selectedIndex: action.payload, selectedIndex: action.payload,
} }
case 'SET_FILE_STATES': case 'SET_FILE_DECORATIONS':
return { return {
...state, ...state,
fileStates: action.payload, fileDecorations: action.payload as fileDecoration[],
} }
default: default:
return state return state
@ -64,16 +66,26 @@ export const TabsUI = (props: TabsUIProps) => {
tabs.current = props.tabs // we do this to pass the tabs list to the onReady callbacks tabs.current = props.tabs // we do this to pass the tabs list to the onReady callbacks
useEffect(() => { useEffect(() => {
console.log('TabsUI useEffect')
if (props.tabs[tabsState.selectedIndex]) { if (props.tabs[tabsState.selectedIndex]) {
tabsRef.current[tabsState.selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' }) tabsRef.current[tabsState.selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' })
} }
}, [tabsState.selectedIndex]) }, [tabsState.selectedIndex])
const getFileState = (tab: any) => {
console.log('TAB', tab, tabsState.fileStates)
const getFileDecorationClasses = (tab: any) => {
console.log('TAB', tab, tabsState.fileDecorations)
const fileDecoration = tabsState.fileDecorations.find((fileDecoration: fileDecoration) => {
if(`${fileDecoration.workspace.name}/${fileDecoration.path}` === tab.name) return true
})
return fileDecoration && fileDecoration.fileStateLabelClass
}
const getFileDecorationIcons = (tab: any) => {
return <FileDecorationIcons file={{path: tab.name}} fileDecorations={tabsState.fileDecorations} />
} }
const renderTab = (tab, index) => { const renderTab = (tab, index) => {
console.log('rendertab') console.log('rendertab')
const classNameImg = 'my-1 mr-1 text-dark ' + tab.iconClass const classNameImg = 'my-1 mr-1 text-dark ' + tab.iconClass
@ -83,7 +95,8 @@ export const TabsUI = (props: TabsUIProps) => {
return ( return (
<div ref={el => { tabsRef.current[index] = el }} className={classNameTab} data-id={index === currentIndexRef.current ? 'tab-active' : ''} title={tab.tooltip}> <div ref={el => { tabsRef.current[index] = el }} className={classNameTab} data-id={index === currentIndexRef.current ? 'tab-active' : ''} title={tab.tooltip}>
{tab.icon ? (<img className="my-1 mr-1 iconImage" style={{ filter: invert }} src={tab.icon} />) : (<i className={classNameImg}></i>)} {tab.icon ? (<img className="my-1 mr-1 iconImage" style={{ filter: invert }} src={tab.icon} />) : (<i className={classNameImg}></i>)}
<span className={`title-tabs ${getFileState(tab)}`}>{tab.title}</span> <span className={`title-tabs ${getFileDecorationClasses(tab)}`}>{tab.title}</span>
{getFileDecorationIcons(tab)}
<span className="close-tabs" onClick={(event) => { props.onClose(index); event.stopPropagation() }}> <span className="close-tabs" onClick={(event) => { props.onClose(index); event.stopPropagation() }}>
<i className="text-dark fas fa-times"></i> <i className="text-dark fas fa-times"></i>
</span> </span>
@ -101,8 +114,8 @@ export const TabsUI = (props: TabsUIProps) => {
dispatch({ type: 'SELECT_INDEX', payload: index }) dispatch({ type: 'SELECT_INDEX', payload: index })
} }
const setFileStates = (fileStates: fileState[]) => { const setFileDecorations = (fileStates: fileDecoration[]) => {
dispatch({ type: 'SET_FILE_STATES', payload: fileStates }) dispatch({ type: 'SET_FILE_DECORATIONS', payload: fileStates })
} }
const transformScroll = (event) => { const transformScroll = (event) => {
@ -118,7 +131,7 @@ export const TabsUI = (props: TabsUIProps) => {
props.onReady({ props.onReady({
activateTab, activateTab,
active, active,
setFileStates setFileDecorations
}) })
return () => { tabsElement.current.removeEventListener('wheel', transformScroll) } return () => { tabsElement.current.removeEventListener('wheel', transformScroll) }

@ -1,7 +1,8 @@
import { fileDecoration } from '@remix-ui/file-decorators'
import { extractParentFromKey } from '@remix-ui/helper' import { extractParentFromKey } from '@remix-ui/helper'
import React from 'react' import React from 'react'
import { action, WorkspaceTemplate, fileState } from '../types' import { action, WorkspaceTemplate } from '../types'
import { displayNotification, displayPopUp, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, loadLocalhostError, loadLocalhostRequest, loadLocalhostSuccess, removeContextMenuItem, removeFocus, rootFolderChangedSuccess, setContextMenuItem, setMode, setReadOnlyMode, setFileStateSuccess } from './payload' import { displayNotification, displayPopUp, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, loadLocalhostError, loadLocalhostRequest, loadLocalhostSuccess, removeContextMenuItem, removeFocus, rootFolderChangedSuccess, setContextMenuItem, setMode, setReadOnlyMode, setFileDecorationSuccess } from './payload'
import { addInputField, createWorkspace, deleteWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from './workspace' import { addInputField, createWorkspace, deleteWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from './workspace'
const LOCALHOST = ' - connect to localhost - ' const LOCALHOST = ' - connect to localhost - '
@ -38,8 +39,8 @@ export const listenOnPluginEvents = (filePanelPlugin) => {
uploadFile(target, dir, cb) uploadFile(target, dir, cb)
}) })
plugin.on('fileStates', 'fileStateChanged', async (items: fileState[]) => { plugin.on('fileDecorator', 'fileDecoratorsChanged', async (items: fileDecoration[]) => {
setFileState(items) setFileDecorators(items)
}) })
plugin.on('remixd', 'rootFolderChanged', async (path: string) => { plugin.on('remixd', 'rootFolderChanged', async (path: string) => {
@ -207,7 +208,7 @@ const rootFolderChanged = async (path) => {
await dispatch(rootFolderChangedSuccess(path)) await dispatch(rootFolderChangedSuccess(path))
} }
const setFileState = async (items: fileState[], cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => { const setFileDecorators = async (items: fileDecoration[], cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => {
await dispatch(setFileStateSuccess(items)) await dispatch(setFileDecorationSuccess(items))
cb && cb(null, true) cb && cb(null, true)
} }

@ -1,4 +1,5 @@
import { action, fileState } from '../types' import { fileDecoration } from '@remix-ui/file-decorators'
import { action } from '../types'
export const setCurrentWorkspace = (workspace: string) => { export const setCurrentWorkspace = (workspace: string) => {
return { return {
@ -240,9 +241,9 @@ export const fsInitializationCompleted = () => {
} }
} }
export const setFileStateSuccess = (items: fileState[]) => { export const setFileDecorationSuccess = (items: fileDecoration[]) => {
return { return {
type: 'SET_FILE_STATE_SUCCESS', type: 'SET_FILE_DECORATION_SUCCESS',
payload: items payload: items
} }
} }

@ -432,7 +432,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
{ {
files[props.name] && Object.keys(files[props.name]).map((key, index) => <FileRender files[props.name] && Object.keys(files[props.name]).map((key, index) => <FileRender
file={files[props.name][key]} file={files[props.name][key]}
fileState={fileState} fileDecorations={fileState}
index={index} index={index}
focusContext={state.focusContext} focusContext={state.focusContext}
focusEdit={state.focusEdit} focusEdit={state.focusEdit}

@ -1,5 +1,5 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import { fileState } from '@remix-ui/file-states' import { fileDecoration } from '@remix-ui/file-decorators'
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import { FileType } from '../types' import { FileType } from '../types'
@ -11,12 +11,12 @@ export interface FileLabelProps {
isNew: boolean isNew: boolean
lastEdit: string lastEdit: string
} }
fileState: fileState[], fileDecorations: fileDecoration[],
editModeOff: (content: string) => void editModeOff: (content: string) => void
} }
export const FileLabel = (props: FileLabelProps) => { export const FileLabel = (props: FileLabelProps) => {
const { file, focusEdit, editModeOff, fileState } = props const { file, focusEdit, editModeOff, fileDecorations } = props
const [isEditable, setIsEditable] = useState<boolean>(false) const [isEditable, setIsEditable] = useState<boolean>(false)
const [fileStateClasses, setFileStateClasses] = useState<string>('') const [fileStateClasses, setFileStateClasses] = useState<string>('')
const labelRef = useRef(null) const labelRef = useRef(null)
@ -28,16 +28,17 @@ export const FileLabel = (props: FileLabelProps) => {
}, [file.path, focusEdit]) }, [file.path, focusEdit])
useEffect(() => { useEffect(() => {
console.log('fileState', fileState, file.name) console.log('fileState', fileDecorations, file.name)
const state = props.fileState.find((state: fileState) => { const state = props.fileDecorations.find((state: fileDecoration) => {
console.log('FOUND STATE', state)
if(state.path === props.file.path) return true if(state.path === props.file.path) return true
if(state.bubble && props.file.isDirectory && state.path.startsWith(props.file.path)) return true if(state.bubble && props.file.isDirectory && state.path.startsWith(props.file.path)) return true
}) })
if (state && state.fileStateLabelClass) { if (state && state.fileStateLabelClass) {
setFileStateClasses(state.fileStateLabelClass) setFileStateClasses(state.fileStateLabelClass)
} else{
setFileStateClasses('')
} }
}, [fileState]) }, [fileDecorations])
useEffect(() => { useEffect(() => {
if (labelRef.current) { if (labelRef.current) {

@ -6,8 +6,11 @@ import { TreeView, TreeViewItem } from '@remix-ui/tree-view'
import { getPathIcon } from '@remix-ui/helper' import { getPathIcon } from '@remix-ui/helper'
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import { FileLabel } from './file-label' import { FileLabel } from './file-label'
import { fileDecoration, FileDecorationIcons } from '@remix-ui/file-decorators'
import { fileState, FileStateIcons } from '@remix-ui/file-states'
export interface RenderFileProps { export interface RenderFileProps {
file: FileType, file: FileType,
@ -21,7 +24,7 @@ export interface RenderFileProps {
handleClickFolder: (path: string, type: string) => void, handleClickFolder: (path: string, type: string) => void,
handleClickFile: (path: string, type: string) => void, handleClickFile: (path: string, type: string) => void,
handleContextMenu: (pageX: number, pageY: number, path: string, content: string, type: string) => void handleContextMenu: (pageX: number, pageY: number, path: string, content: string, type: string) => void
fileState: fileState[] fileDecorations: fileDecoration[]
} }
export const FileRender = (props: RenderFileProps) => { export const FileRender = (props: RenderFileProps) => {
@ -79,7 +82,7 @@ export const FileRender = (props: RenderFileProps) => {
iconX='pr-3 fa fa-folder' iconX='pr-3 fa fa-folder'
iconY='pr-3 fa fa-folder-open' iconY='pr-3 fa fa-folder-open'
key={`${file.path + props.index}`} key={`${file.path + props.index}`}
label={<FileLabel fileState={props.fileState} file={file} focusEdit={props.focusEdit} editModeOff={props.editModeOff} />} label={<FileLabel fileDecorations={props.fileDecorations} file={file} focusEdit={props.focusEdit} editModeOff={props.editModeOff} />}
onClick={handleFolderClick} onClick={handleFolderClick}
onContextMenu={handleContextMenu} onContextMenu={handleContextMenu}
labelClass={labelClass} labelClass={labelClass}
@ -92,7 +95,7 @@ export const FileRender = (props: RenderFileProps) => {
file.child ? <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 Object.keys(file.child).map((key, index) => <FileRender
file={file.child[key]} file={file.child[key]}
fileState={props.fileState} fileDecorations={props.fileDecorations}
index={index} index={index}
focusContext={props.focusContext} focusContext={props.focusContext}
focusEdit={props.focusEdit} focusEdit={props.focusEdit}
@ -118,8 +121,8 @@ export const FileRender = (props: RenderFileProps) => {
label={ label={
<> <>
<div className="d-flex flex-row"> <div className="d-flex flex-row">
<FileLabel file={file} fileState={props.fileState} focusEdit={props.focusEdit} editModeOff={props.editModeOff} /> <FileLabel file={file} fileDecorations={props.fileDecorations} focusEdit={props.focusEdit} editModeOff={props.editModeOff} />
<FileStateIcons file={file} fileState={props.fileState}/> <FileDecorationIcons file={file} fileDecorations={props.fileDecorations}/>
</div> </div>
</> </>
} }

@ -1,6 +1,7 @@
import { extractNameFromKey } from '@remix-ui/helper' import { extractNameFromKey } from '@remix-ui/helper'
import { action, fileState, FileType } from '../types' import { action, FileType } from '../types'
import * as _ from 'lodash' import * as _ from 'lodash'
import { fileDecoration } from '@remix-ui/file-decorators'
interface Action { interface Action {
type: string type: string
payload: any payload: any
@ -21,7 +22,7 @@ export interface BrowserState {
removedMenuItems: action[], removedMenuItems: action[],
error: string error: string
}, },
fileState: fileState[] fileState: fileDecoration[]
}, },
localhost: { localhost: {
sharedFolder: string, sharedFolder: string,
@ -603,7 +604,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
} }
} }
case 'SET_FILE_STATE_SUCCESS': { case 'SET_FILE_DECORATION_SUCCESS': {
return { return {
...state, ...state,
browser: { browser: {

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel' import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel'
import { fileState } from '@remix-ui/file-states'; import { fileDecoration } from '@remix-ui/file-decorators';
export type action = { name: string, type?: Array<'folder' | 'gist' | 'file'>, path?: string[], extension?: string[], pattern?: string[], id: string, multiselect: boolean, label: string, sticky?: boolean } export type action = { name: string, type?: Array<'folder' | 'gist' | 'file'>, path?: string[], extension?: string[], pattern?: string[], id: string, multiselect: boolean, label: string, sticky?: boolean }
export interface JSONStandardInput { export interface JSONStandardInput {
@ -74,7 +74,7 @@ export interface FileExplorerProps {
contextMenuItems: MenuItems, contextMenuItems: MenuItems,
removedContextMenuItems: MenuItems, removedContextMenuItems: MenuItems,
files: { [x: string]: Record<string, FileType> }, files: { [x: string]: Record<string, FileType> },
fileState: fileState[], fileState: fileDecoration[],
expandPath: string[], expandPath: string[],
focusEdit: string, focusEdit: string,
focusElement: { key: string, type: 'file' | 'folder' | 'gist' }[], focusElement: { key: string, type: 'file' | 'folder' | 'gist' }[],

@ -170,7 +170,7 @@
"remix-ui-permission-handler": { "remix-ui-permission-handler": {
"tags": [] "tags": []
}, },
"remix-ui-file-states": { "remix-ui-file-decorators": {
"tags": [] "tags": []
} }
}, },

@ -45,7 +45,7 @@
"workspace-schematic": "nx workspace-schematic", "workspace-schematic": "nx workspace-schematic",
"dep-graph": "nx dep-graph", "dep-graph": "nx dep-graph",
"help": "nx help", "help": "nx help",
"lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remix-ws-templates,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-helper,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox,remix-ui-settings,remix-core-plugin,remix-ui-renderer,remix-ui-publish-to-storage,remix-ui-solidity-compiler,solidity-unit-testing,remix-ui-plugin-manager,remix-ui-terminal,remix-ui-editor,remix-ui-app,remix-ui-tabs,remix-ui-panel,remix-ui-run-tab,remix-ui-permission-handler,remix-ui-search,remix-ui-file-states", "lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remix-ws-templates,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-helper,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox,remix-ui-settings,remix-core-plugin,remix-ui-renderer,remix-ui-publish-to-storage,remix-ui-solidity-compiler,solidity-unit-testing,remix-ui-plugin-manager,remix-ui-terminal,remix-ui-editor,remix-ui-app,remix-ui-tabs,remix-ui-panel,remix-ui-run-tab,remix-ui-permission-handler,remix-ui-search,remix-ui-file-decorators",
"build:libs": "nx run-many --target=build --parallel=false --with-deps=true --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remix-ws-templates,remixd", "build:libs": "nx run-many --target=build --parallel=false --with-deps=true --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remix-ws-templates,remixd",
"test:libs": "nx run-many --target=test --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd", "test:libs": "nx run-many --target=test --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"publish:libs": "yarn run build:libs && lerna publish --skip-git && yarn run bumpVersion:libs", "publish:libs": "yarn run build:libs && lerna publish --skip-git && yarn run bumpVersion:libs",

@ -52,7 +52,7 @@
"@remix-ui/modal-dialog": ["libs/remix-ui/modal-dialog/src/index.ts"], "@remix-ui/modal-dialog": ["libs/remix-ui/modal-dialog/src/index.ts"],
"@remix-ui/toaster": ["libs/remix-ui/toaster/src/index.ts"], "@remix-ui/toaster": ["libs/remix-ui/toaster/src/index.ts"],
"@remix-ui/file-explorer": ["libs/remix-ui/file-explorer/src/index.ts"], "@remix-ui/file-explorer": ["libs/remix-ui/file-explorer/src/index.ts"],
"@remix-ui/file-states": ["libs/remix-ui/file-states/src/index.ts"], "@remix-ui/file-decorators": ["libs/remix-ui/file-decorators/src/index.ts"],
"@remix-ui/workspace": ["libs/remix-ui/workspace/src/index.ts"], "@remix-ui/workspace": ["libs/remix-ui/workspace/src/index.ts"],
"@remix-ui/static-analyser": [ "@remix-ui/static-analyser": [
"libs/remix-ui/static-analyser/src/index.ts" "libs/remix-ui/static-analyser/src/index.ts"

@ -1125,17 +1125,17 @@
} }
} }
}, },
"remix-ui-file-states": { "remix-ui-file-decorators": {
"root": "libs/remix-ui/file-states", "root": "libs/remix-ui/file-decorators",
"sourceRoot": "libs/remix-ui/file-states/src", "sourceRoot": "libs/remix-ui/file-decorators/src",
"projectType": "library", "projectType": "library",
"architect": { "architect": {
"lint": { "lint": {
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/file-states/tsconfig.lib.json"], "tsConfig": ["libs/remix-ui/file-decorators/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/file-states/**/*"] "exclude": ["**/node_modules/**", "!libs/remix-ui/file-decorators/**/*"]
} }
} }
} }

Loading…
Cancel
Save