diff --git a/apps/remix-ide-e2e/nightwatch.ts b/apps/remix-ide-e2e/nightwatch.ts index 2bc20e8274e..d8a111a617b 100644 --- a/apps/remix-ide-e2e/nightwatch.ts +++ b/apps/remix-ide-e2e/nightwatch.ts @@ -68,7 +68,13 @@ module.exports = { desiredCapabilities: { browserName: 'firefox', javascriptEnabled: true, - acceptSslCerts: true + acceptSslCerts: true, + 'moz:firefoxOptions': { + args: [ + '-width=2560', + '-height=1440' + ] + } } }, @@ -78,7 +84,11 @@ module.exports = { javascriptEnabled: true, acceptSslCerts: true, 'moz:firefoxOptions': { - args: ['-headless'] + args: [ + '-headless', + '-width=2560', + '-height=1440' + ] } } } diff --git a/apps/remix-ide-e2e/src/helpers/init.ts b/apps/remix-ide-e2e/src/helpers/init.ts index a5926bd0288..e934ae2f7b8 100644 --- a/apps/remix-ide-e2e/src/helpers/init.ts +++ b/apps/remix-ide-e2e/src/helpers/init.ts @@ -9,6 +9,7 @@ export default function (browser: NightwatchBrowser, callback: VoidFunction, url .switchBrowserTab(0) .waitForElementVisible('[id="remixTourSkipbtn"]') .click('[id="remixTourSkipbtn"]') + .maximizeWindow() .fullscreenWindow(() => { if (preloadPlugins) { initModules(browser, () => { diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 22cc43679b7..338af4bbecc 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -2,7 +2,6 @@ import { RunTab, makeUdapp } from './app/udapp' import { RemixEngine } from './remixEngine' import { RemixAppManager } from './remixAppManager' -import { MainView } from './app/panels/main-view' import { ThemeModule } from './app/tabs/theme-module' import { NetworkModule } from './app/tabs/network-module' import { Web3ProviderModule } from './app/tabs/web3-provider' @@ -11,7 +10,6 @@ import { HiddenPanel } from './app/components/hidden-panel' import { VerticalIcons } from './app/components/vertical-icons' import { LandingPage } from './app/ui/landing-page/landing-page' import { MainPanel } from './app/components/main-panel' -import { FramingService } from './framingService' import { WalkthroughService } from './walkthroughService' @@ -20,6 +18,7 @@ import { OffsetToLineColumnConverter, CompilerMetadata, CompilerArtefacts, Fetch import migrateFileSystem from './migrateFileSystem' import Registry from './app/state/registry' import { ConfigPlugin } from './app/plugins/config' +import { Layout } from './app/panels/layout' import { ModalPlugin } from './app/plugins/modal' const isElectron = require('is-electron') @@ -50,6 +49,7 @@ const TestTab = require('./app/tabs/test-tab') const FilePanel = require('./app/panels/file-panel') const Editor = require('./app/editor/editor') const Terminal = require('./app/panels/terminal') +const { TabProxy } = require('./app/panels/tab-proxy.js') class AppComponent { constructor () { @@ -67,13 +67,27 @@ class AppComponent { // load file system self._components.filesProviders = {} self._components.filesProviders.browser = new FileProvider('browser') - Registry.getInstance().put({ api: self._components.filesProviders.browser, name: 'fileproviders/browser' }) - self._components.filesProviders.localhost = new RemixDProvider(self.appManager) - Registry.getInstance().put({ api: self._components.filesProviders.localhost, name: 'fileproviders/localhost' }) + Registry.getInstance().put({ + api: self._components.filesProviders.browser, + name: 'fileproviders/browser' + }) + self._components.filesProviders.localhost = new RemixDProvider( + self.appManager + ) + Registry.getInstance().put({ + api: self._components.filesProviders.localhost, + name: 'fileproviders/localhost' + }) self._components.filesProviders.workspace = new WorkspaceFileProvider() - Registry.getInstance().put({ api: self._components.filesProviders.workspace, name: 'fileproviders/workspace' }) + Registry.getInstance().put({ + api: self._components.filesProviders.workspace, + name: 'fileproviders/workspace' + }) - Registry.getInstance().put({ api: self._components.filesProviders, name: 'fileproviders' }) + Registry.getInstance().put({ + api: self._components.filesProviders, + name: 'fileproviders' + }) migrateFileSystem(self._components.filesProviders.browser) } @@ -83,6 +97,7 @@ class AppComponent { // APP_MANAGER const appManager = self.appManager const pluginLoader = self.appManager.pluginLoader + self.panels = {} self.workspace = pluginLoader.get() self.engine = new RemixEngine() self.engine.register(appManager) @@ -92,8 +107,15 @@ class AppComponent { 'remix-beta.ethereum.org': 25, 'remix.ethereum.org': 23 } - self.showMatamo = (matomoDomains[window.location.hostname] && !Registry.getInstance().get('config').api.exists('settings/matomo-analytics')) - self.walkthroughService = new WalkthroughService(appManager, self.showMatamo) + self.showMatamo = + matomoDomains[window.location.hostname] && + !Registry.getInstance() + .get('config') + .api.exists('settings/matomo-analytics') + self.walkthroughService = new WalkthroughService( + appManager, + self.showMatamo + ) const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080'] // workaround for Electron support @@ -114,7 +136,9 @@ class AppComponent { // ----------------- editor service ---------------------------- const editor = new Editor() // wrapper around ace editor Registry.getInstance().put({ api: editor, name: 'editor' }) - editor.event.register('requiringToSaveCurrentfile', () => fileManager.saveCurrentFile()) + editor.event.register('requiringToSaveCurrentfile', () => + fileManager.saveCurrentFile() + ) // ----------------- fileManager service ---------------------------- const fileManager = new FileManager(editor, appManager) @@ -131,7 +155,10 @@ class AppComponent { const compilerMetadataGenerator = new CompilerMetadata() // ----------------- compilation result service (can keep track of compilation results) ---------------------------- const compilersArtefacts = new CompilerArtefacts() // store all the compilation results (key represent a compiler name) - Registry.getInstance().put({ api: compilersArtefacts, name: 'compilersartefacts' }) + Registry.getInstance().put({ + api: compilersArtefacts, + name: 'compilersartefacts' + }) // service which fetch contract artifacts from sourve-verify, put artifacts in remix and compile it const fetchAndCompile = new FetchAndCompile() @@ -142,19 +169,22 @@ class AppComponent { const hardhatProvider = new HardhatProvider(blockchain) // ----------------- convert offset to line/column service ----------- const offsetToLineColumnConverter = new OffsetToLineColumnConverter() - Registry.getInstance().put({ api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter' }) + Registry.getInstance().put({ + api: offsetToLineColumnConverter, + name: 'offsettolinecolumnconverter' + }) // -------------------Terminal---------------------------------------- - makeUdapp(blockchain, compilersArtefacts, (domEl) => terminal.logHtml(domEl)) + makeUdapp(blockchain, compilersArtefacts, domEl => terminal.logHtml(domEl)) const terminal = new Terminal( { appManager, blockchain }, { - getPosition: (event) => { + getPosition: event => { const limitUp = 36 const limitDown = 20 const height = window.innerHeight - let newpos = (event.pageY < limitUp) ? limitUp : event.pageY - newpos = (newpos < height - limitDown) ? newpos : height - limitDown + let newpos = event.pageY < limitUp ? limitUp : event.pageY + newpos = newpos < height - limitDown ? newpos : height - limitDown return height - newpos } } @@ -164,8 +194,10 @@ class AppComponent { self.modal = new ModalPlugin() const configPlugin = new ConfigPlugin() + self.layout = new Layout() self.engine.register([ + self.layout, self.modal, self.gistHandler, configPlugin, @@ -189,22 +221,27 @@ class AppComponent { // LAYOUT & SYSTEM VIEWS const appPanel = new MainPanel() - self.mainview = new MainView(contextualListener, editor, appPanel, fileManager, appManager, terminal) Registry.getInstance().put({ api: self.mainview, name: 'mainview' }) - - self.engine.register([ - appPanel, - self.mainview.tabProxy - ]) + const tabProxy = new TabProxy(fileManager, editor) + self.engine.register([appPanel, tabProxy]) // those views depend on app_manager self.menuicons = new VerticalIcons(appManager) self.sidePanel = new SidePanel(appManager, self.menuicons) self.hiddenPanel = new HiddenPanel() - const pluginManagerComponent = new PluginManagerComponent(appManager, self.engine) + const pluginManagerComponent = new PluginManagerComponent( + appManager, + self.engine + ) const filePanel = new FilePanel(appManager) - const landingPage = new LandingPage(appManager, self.menuicons, fileManager, filePanel, contentImport) + const landingPage = new LandingPage( + appManager, + self.menuicons, + fileManager, + filePanel, + contentImport + ) self.settings = new SettingsTab( Registry.getInstance().get('config').api, editor, @@ -222,7 +259,10 @@ class AppComponent { ]) // CONTENT VIEWS & DEFAULT PLUGINS - const compileTab = new CompileTab(Registry.getInstance().get('config').api, Registry.getInstance().get('filemanager').api) + const compileTab = new CompileTab( + Registry.getInstance().get('config').api, + Registry.getInstance().get('filemanager').api + ) const run = new RunTab( blockchain, Registry.getInstance().get('config').api, @@ -231,7 +271,6 @@ class AppComponent { filePanel, Registry.getInstance().get('compilersartefacts').api, networkModule, - self.mainview, Registry.getInstance().get('fileproviders/browser').api ) const analysis = new AnalysisTab() @@ -256,6 +295,13 @@ class AppComponent { filePanel.hardhatHandle, filePanel.slitherHandle ]) + + self.layout.panels = { + tabs: { plugin: tabProxy, active: true }, + editor: { plugin: editor, active: true }, + main: { plugin: appPanel, active: false }, + terminal: { plugin: terminal, active: true, minimized: false } + } } async activate () { @@ -270,9 +316,9 @@ class AppComponent { try { self.engine.register(await self.appManager.registeredPlugins()) } catch (e) { - console.log('couldn\'t register iframe plugins', e.message) + console.log("couldn't register iframe plugins", e.message) } - + await self.appManager.activatePlugin(['layout']) await self.appManager.activatePlugin(['modal']) await self.appManager.activatePlugin(['editor']) await self.appManager.activatePlugin(['theme', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) @@ -284,48 +330,56 @@ class AppComponent { await self.appManager.activatePlugin(['settings']) await self.appManager.activatePlugin(['walkthrough']) - self.appManager.on('filePanel', 'workspaceInitializationCompleted', async () => { - await self.appManager.registerContextMenuItems() - }) + self.appManager.on( + 'filePanel', + 'workspaceInitializationCompleted', + async () => { + await self.appManager.registerContextMenuItems() + } + ) await self.appManager.activatePlugin(['filePanel']) // Set workspace after initial activation self.appManager.on('editor', 'editorMounted', () => { if (Array.isArray(self.workspace)) { - self.appManager.activatePlugin(self.workspace).then(async () => { - try { - if (params.deactivate) { - await self.appManager.deactivatePlugin(params.deactivate.split(',')) + self.appManager + .activatePlugin(self.workspace) + .then(async () => { + try { + if (params.deactivate) { + await self.appManager.deactivatePlugin( + params.deactivate.split(',') + ) + } + } catch (e) { + console.log(e) + } + if (params.code) { + // if code is given in url we focus on solidity plugin + self.menuicons.select('solidity') + } else { + // If plugins are loaded from the URL params, we focus on the last one. + if ( + self.appManager.pluginLoader.current === 'queryParams' && + self.workspace.length > 0 + ) { self.menuicons.select(self.workspace[self.workspace.length - 1]) } } - } catch (e) { - console.log(e) - } - if (params.code) { - // if code is given in url we focus on solidity plugin - self.menuicons.select('solidity') - } else { - // If plugins are loaded from the URL params, we focus on the last one. - if (self.appManager.pluginLoader.current === 'queryParams' && self.workspace.length > 0) self.menuicons.select(self.workspace[self.workspace.length - 1]) - } - - if (params.call) { - const callDetails = params.call.split('//') - if (callDetails.length > 1) { - toolTip(`initiating ${callDetails[0]} ...`) - // @todo(remove the timeout when activatePlugin is on 0.3.0) - self.appManager.call(...callDetails).catch(console.error) + + if (params.call) { + const callDetails = params.call.split('//') + if (callDetails.length > 1) { + toolTip(`initiating ${callDetails[0]} ...`) + // @todo(remove the timeout when activatePlugin is on 0.3.0) + self.appManager.call(...callDetails).catch(console.error) + } } - } - }).catch(console.error) + }) + .catch(console.error) } }) // activate solidity plugin self.appManager.activatePlugin(['solidity', 'udapp']) // Load and start the service who manager layout and frame - const framingService = new FramingService(self.sidePanel, self.menuicons, self.mainview, null) - - if (params.embed) framingService.embed() - framingService.start(params) } } diff --git a/apps/remix-ide/src/app/components/hidden-panel.js b/apps/remix-ide/src/app/components/hidden-panel.js deleted file mode 100644 index 77f67c6f4ca..00000000000 --- a/apps/remix-ide/src/app/components/hidden-panel.js +++ /dev/null @@ -1,31 +0,0 @@ -import { AbstractPanel } from './panel' -import * as packageJson from '../../../../../package.json' -const csjs = require('csjs-inject') -const yo = require('yo-yo') - -const css = csjs` - .pluginsContainer { - display: none; - } -` - -const profile = { - name: 'hiddenPanel', - displayName: 'Hidden Panel', - description: '', - version: packageJson.version, - methods: ['addView', 'removeView'] -} - -export class HiddenPanel extends AbstractPanel { - constructor () { - super(profile) - } - - render () { - return yo` -
- ${this.view} -
` - } -} diff --git a/apps/remix-ide/src/app/components/hidden-panel.tsx b/apps/remix-ide/src/app/components/hidden-panel.tsx new file mode 100644 index 00000000000..bfdff5a11aa --- /dev/null +++ b/apps/remix-ide/src/app/components/hidden-panel.tsx @@ -0,0 +1,37 @@ +// eslint-disable-next-line no-use-before-define +import React from 'react' +import ReactDOM from 'react-dom' // eslint-disable-line +import { AbstractPanel } from './panel' +import * as packageJson from '../../../../../package.json' +import { RemixPluginPanel } from '@remix-ui/panel' + +const profile = { + name: 'hiddenPanel', + displayName: 'Hidden Panel', + description: '', + version: packageJson.version, + methods: ['addView', 'removeView'] +} + +export class HiddenPanel extends AbstractPanel { + el: HTMLElement + constructor () { + super(profile) + this.el = document.createElement('div') + this.el.setAttribute('class', 'pluginsContainer') + } + + addView (profile: any, view: any): void { + super.removeView(profile) + super.addView(profile, view) + this.renderComponent() + } + + render () { + return this.el + } + + renderComponent () { + ReactDOM.render(} plugins={this.plugins}/>, this.el) + } +} diff --git a/apps/remix-ide/src/app/components/main-panel.js b/apps/remix-ide/src/app/components/main-panel.js deleted file mode 100644 index 66e6a502450..00000000000 --- a/apps/remix-ide/src/app/components/main-panel.js +++ /dev/null @@ -1,38 +0,0 @@ -import { AbstractPanel } from './panel' -import * as packageJson from '../../../../../package.json' -const yo = require('yo-yo') -const csjs = require('csjs-inject') - -const css = csjs` - .pluginsContainer { - height: 100%; - display: flex; - overflow-y: hidden; - } -` - -const profile = { - name: 'mainPanel', - displayName: 'Main Panel', - description: '', - version: packageJson.version, - methods: ['addView', 'removeView'] -} - -export class MainPanel extends AbstractPanel { - constructor () { - super(profile) - } - - focus (name) { - this.emit('focusChanged', name) - super.focus(name) - } - - render () { - return yo` -
- ${this.view} -
` - } -} diff --git a/apps/remix-ide/src/app/components/main-panel.tsx b/apps/remix-ide/src/app/components/main-panel.tsx new file mode 100644 index 00000000000..b9d180f194b --- /dev/null +++ b/apps/remix-ide/src/app/components/main-panel.tsx @@ -0,0 +1,57 @@ +import React from 'react' // eslint-disable-line +import { AbstractPanel } from './panel' +import ReactDOM from 'react-dom' // eslint-disable-line +import { RemixPluginPanel } from '@remix-ui/panel' +import packageJson from '../../../../../package.json' + +const profile = { + name: 'mainPanel', + displayName: 'Main Panel', + description: '', + version: packageJson.version, + methods: ['addView', 'removeView', 'showContent'] +} + +export class MainPanel extends AbstractPanel { + element: HTMLDivElement + constructor (config) { + super(profile) + this.element = document.createElement('div') + this.element.setAttribute('data-id', 'mainPanelPluginsContainer') + this.element.setAttribute('style', 'height: 100%; width: 100%;') + // this.config = config + } + + onActivation () { + this.renderComponent() + } + + focus (name) { + this.emit('focusChanged', name) + super.focus(name) + this.renderComponent() + } + + addView (profile, view) { + super.addView(profile, view) + this.renderComponent() + } + + removeView (profile) { + super.removeView(profile) + this.renderComponent() + } + + async showContent (name) { + super.showContent(name) + this.renderComponent() + } + + render () { + return this.element + } + + renderComponent () { + ReactDOM.render(} plugins={this.plugins}/>, this.element) + } +} diff --git a/apps/remix-ide/src/app/components/panel.js b/apps/remix-ide/src/app/components/panel.js deleted file mode 100644 index 08d9abf20fc..00000000000 --- a/apps/remix-ide/src/app/components/panel.js +++ /dev/null @@ -1,111 +0,0 @@ -import { EventEmitter } from 'events' -import { HostPlugin } from '@remixproject/engine-web' -const csjs = require('csjs-inject') -const yo = require('yo-yo') - -const css = csjs` - .plugins { - height: 100%; - } - .plugItIn { - display : none; - height : 100%; - } - .plugItIn > div { - overflow-y : auto; - overflow-x : hidden; - height : 100%; - width : 100%; - } - .plugItIn.active { - display : block; - } - .pluginsContainer { - height : 100%; - overflow-y : hidden; - } -` - -/** Abstract class used for hosting the view of a plugin */ -export class AbstractPanel extends HostPlugin { - constructor (profile) { - super(profile) - this.events = new EventEmitter() - this.contents = {} - this.active = undefined - - // View where the plugin HTMLElement leaves - this.view = yo`
` - } - - /** - * Add the plugin to the panel - * @param {String} name the name of the plugin - * @param {HTMLElement} content the HTMLContent of the plugin - */ - add (view, name) { - if (this.contents[name]) throw new Error(`Plugin ${name} already rendered`) - view.style.height = '100%' - view.style.width = '100%' - view.style.border = '0' - - const isIframe = view.tagName === 'IFRAME' - view.style.display = isIframe ? 'none' : 'block' - const loading = isIframe ? yo` -
-
- Loading... -
-
- ` : '' - this.contents[name] = yo`
${view}${loading}
` - - if (view.tagName === 'IFRAME') { - view.addEventListener('load', () => { - if (this.contents[name].contains(loading)) { - this.contents[name].removeChild(loading) - } - view.style.display = 'block' - }) - } - this.contents[name].style.display = 'none' - this.view.appendChild(this.contents[name]) - } - - addView (profile, view) { - this.add(view, profile.name) - } - - removeView (profile) { - this.remove(profile.name) - } - - /** - * Remove a plugin from the panel - * @param {String} name The name of the plugin to remove - */ - remove (name) { - const el = this.contents[name] - delete this.contents[name] - if (el) el.parentElement.removeChild(el) - if (name === this.active) this.active = undefined - } - - /** - * Display the content of this specific plugin - * @param {String} name The name of the plugin to display the content - */ - showContent (name) { - if (!this.contents[name]) throw new Error(`Plugin ${name} is not yet activated`) - // hiding the current view and display the `moduleName` - if (this.active) { - this.contents[this.active].style.display = 'none' - } - this.contents[name].style.display = 'flex' - this.active = name - } - - focus (name) { - this.showContent(name) - } -} diff --git a/apps/remix-ide/src/app/components/panel.ts b/apps/remix-ide/src/app/components/panel.ts new file mode 100644 index 00000000000..a5747188af2 --- /dev/null +++ b/apps/remix-ide/src/app/components/panel.ts @@ -0,0 +1,63 @@ +import React from 'react' // eslint-disable-line +import { EventEmitter } from 'events' +import { HostPlugin } from '@remixproject/engine-web' // eslint-disable-line +import { PluginRecord } from 'libs/remix-ui/panel/src/lib/types' +const EventManager = require('../../lib/events') + +export class AbstractPanel extends HostPlugin { + events: EventEmitter + event: any + public plugins: Record = {} + constructor (profile) { + super(profile) + this.events = new EventEmitter() + this.event = new EventManager() + } + + currentFocus (): string { + return Object.values(this.plugins).find(plugin => { + return plugin.active + }).profile.name + } + + addView (profile, view) { + if (this.plugins[profile.name]) throw new Error(`Plugin ${profile.name} already rendered`) + this.plugins[profile.name] = { + profile: profile, + view: view, + active: false, + class: 'plugItIn active' + } + } + + removeView (profile) { + this.emit('pluginDisabled', profile.name) + this.call('menuicons', 'unlinkContent', profile) + this.remove(profile.name) + } + + /** + * Remove a plugin from the panel + * @param {String} name The name of the plugin to remove + */ + remove (name) { + delete this.plugins[name] + } + + /** + * Display the content of this specific plugin + * @param {String} name The name of the plugin to display the content + */ + showContent (name) { + if (!this.plugins[name]) throw new Error(`Plugin ${name} is not yet activated`) + + Object.values(this.plugins).forEach(plugin => { + plugin.active = false + }) + this.plugins[name].active = true + } + + focus (name) { + this.showContent(name) + } +} diff --git a/apps/remix-ide/src/app/components/side-panel.js b/apps/remix-ide/src/app/components/side-panel.js deleted file mode 100644 index 0cc09d84d4e..00000000000 --- a/apps/remix-ide/src/app/components/side-panel.js +++ /dev/null @@ -1,156 +0,0 @@ -import { AbstractPanel } from './panel' -import * as packageJson from '../../../../../package.json' -const csjs = require('csjs-inject') -const yo = require('yo-yo') - -const css = csjs` - .panel { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - flex: auto; - } - .swapitTitle { - margin: 0; - text-transform: uppercase; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .swapitTitle i{ - padding-left: 6px; - font-size: 14px; - } - .swapitHeader { - display: flex; - align-items: center; - padding: 16px 24px 15px; - justify-content: space-between; - } - .icons i { - height: 80%; - cursor: pointer; - } - .pluginsContainer { - height: 100%; - overflow-y: auto; - } - .titleInfo { - padding-left: 10px; - } - .versionBadge { - background-color: var(--light); - padding: 0 7px; - font-weight: bolder; - margin-left: 5px; - text-transform: lowercase; - cursor: default; - } -` - -const sidePanel = { - name: 'sidePanel', - displayName: 'Side Panel', - description: '', - version: packageJson.version, - methods: ['addView', 'removeView'] -} - -// TODO merge with vertical-icons.js -export class SidePanel extends AbstractPanel { - constructor (appManager, verticalIcons) { - super(sidePanel) - this.appManager = appManager - this.header = yo`
` - this.renderHeader() - this.verticalIcons = verticalIcons - - // Toggle content - verticalIcons.events.on('toggleContent', (name) => { - if (!this.contents[name]) return - if (this.active === name) { - // TODO: Only keep `this.emit` (issue#2210) - this.emit('toggle', name) - this.events.emit('toggle', name) - return - } - this.showContent(name) - // TODO: Only keep `this.emit` (issue#2210) - this.emit('showing', name) - this.events.emit('showing', name) - }) - // Force opening - verticalIcons.events.on('showContent', (name) => { - if (!this.contents[name]) return - this.showContent(name) - // TODO: Only keep `this.emit` (issue#2210) - this.emit('showing', name) - this.events.emit('showing', name) - }) - } - - focus (name) { - this.emit('focusChanged', name) - super.focus(name) - } - - removeView (profile) { - super.removeView(profile) - this.emit('pluginDisabled', profile.name) - this.verticalIcons.unlinkContent(profile) - } - - addView (profile, view) { - super.addView(profile, view) - this.verticalIcons.linkContent(profile) - } - - /** - * Display content and update the header - * @param {String} name The name of the plugin to display - */ - async showContent (name) { - super.showContent(name) - this.renderHeader() - this.emit('focusChanged', name) - } - - /** The header of the side panel */ - async renderHeader () { - let name = ' - ' - let docLink = '' - let versionWarning - if (this.active) { - const profile = await this.appManager.getProfile(this.active) - name = profile.displayName ? profile.displayName : profile.name - docLink = profile.documentation ? yo`` : '' - if (profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)) { - versionWarning = yo`alpha` - } - // Beta - if (profile.version && profile.version.match(/\b(\w*beta\w*)\b/g)) { - versionWarning = yo`beta` - } - } - - const header = yo` -
-
${name}
- ${docLink} - ${versionWarning} -
- ` - yo.update(this.header, header) - } - - render () { - return yo` -
- ${this.header} -
- ${this.view} -
-
` - } -} diff --git a/apps/remix-ide/src/app/components/side-panel.tsx b/apps/remix-ide/src/app/components/side-panel.tsx new file mode 100644 index 00000000000..8b64d64e451 --- /dev/null +++ b/apps/remix-ide/src/app/components/side-panel.tsx @@ -0,0 +1,95 @@ +// eslint-disable-next-line no-use-before-define +import React from 'react' +import ReactDOM from 'react-dom' +import { AbstractPanel } from './panel' +import { RemixPluginPanel } from '@remix-ui/panel' +import packageJson from '../../../../../package.json' +import { RemixAppManager } from '../../remixAppManager' +import { VerticalIcons } from 'libs/remix-ui/vertical-icons-panel/types/vertical-icons-panel' +import RemixUIPanelHeader from 'libs/remix-ui/panel/src/lib/plugins/panel-header' +// const csjs = require('csjs-inject') + +const sidePanel = { + name: 'sidePanel', + displayName: 'Side Panel', + description: '', + version: packageJson.version, + methods: ['addView', 'removeView'] +} + +// TODO merge with vertical-icons.js +export class SidePanel extends AbstractPanel { + appManager: RemixAppManager + sideelement: any + verticalIcons: VerticalIcons; + constructor (appManager: RemixAppManager, verticalIcons: VerticalIcons) { + super(sidePanel) + this.appManager = appManager + this.sideelement = document.createElement('section') + this.sideelement.setAttribute('class', 'panel plugin-manager') + this.verticalIcons = verticalIcons + + // Toggle content + verticalIcons.events.on('toggleContent', (name) => { + if (!this.plugins[name]) return + if (this.plugins[name].active) { + // TODO: Only keep `this.emit` (issue#2210) + this.emit('toggle', name) + this.events.emit('toggle', name) + return + } + this.showContent(name) + // TODO: Only keep `this.emit` (issue#2210) + this.emit('showing', name) + this.events.emit('showing', name) + }) + // Force opening + verticalIcons.events.on('showContent', (name) => { + if (!this.plugins[name]) return + this.showContent(name) + // TODO: Only keep `this.emit` (issue#2210) + this.emit('showing', name) + this.events.emit('showing', name) + }) + } + + onActivation () { + this.renderComponent() + } + + focus (name) { + this.emit('focusChanged', name) + super.focus(name) + } + + removeView (profile) { + super.removeView(profile) + this.emit('pluginDisabled', profile.name) + this.call('menuicons', 'unlinkContent', profile) + this.renderComponent() + } + + addView (profile, view) { + super.addView(profile, view) + this.verticalIcons.linkContent(profile) + this.renderComponent() + } + + /** + * Display content and update the header + * @param {String} name The name of the plugin to display + */ + async showContent (name) { + super.showContent(name) + this.emit('focusChanged', name) + this.renderComponent() + } + + render () { + return this.sideelement + } + + renderComponent () { + ReactDOM.render(} plugins={this.plugins}/>, this.sideelement) + } +} diff --git a/apps/remix-ide/src/app/components/vertical-icons.js b/apps/remix-ide/src/app/components/vertical-icons.js index cd86b1d796a..69f61329396 100644 --- a/apps/remix-ide/src/app/components/vertical-icons.js +++ b/apps/remix-ide/src/app/components/vertical-icons.js @@ -15,7 +15,7 @@ const profile = { displayName: 'Vertical Icons', description: '', version: packageJson.version, - methods: ['select'] + methods: ['select', 'unlinkContent'] } // TODO merge with side-panel.js. VerticalIcons should not be a plugin diff --git a/apps/remix-ide/src/app/panels/layout.ts b/apps/remix-ide/src/app/panels/layout.ts new file mode 100644 index 00000000000..451c2aefe70 --- /dev/null +++ b/apps/remix-ide/src/app/panels/layout.ts @@ -0,0 +1,94 @@ +import { Plugin } from '@remixproject/engine' +import { Profile } from '@remixproject/plugin-utils' +import { EventEmitter } from 'events' +import QueryParams from '../../lib/query-params' + +const profile: Profile = { + name: 'layout', + description: 'layout', + methods: ['minimize'] +} + +interface panelState { + active: boolean + plugin: Plugin + minimized: boolean +} +interface panels { + tabs: panelState + editor: panelState + main: panelState + terminal: panelState +} + +export class Layout extends Plugin { + event: any + panels: panels + constructor () { + super(profile) + this.event = new EventEmitter() + } + + async onActivation (): Promise { + this.on('fileManager', 'currentFileChanged', () => { + this.panels.editor.active = true + this.panels.main.active = false + this.event.emit('change', null) + }) + this.on('tabs', 'openFile', () => { + this.panels.editor.active = true + this.panels.main.active = false + this.event.emit('change', null) + }) + this.on('tabs', 'switchApp', (name: string) => { + this.call('mainPanel', 'showContent', name) + this.panels.editor.active = false + this.panels.main.active = true + this.event.emit('change', null) + }) + this.on('tabs', 'closeApp', (name: string) => { + this.panels.editor.active = true + this.panels.main.active = false + this.event.emit('change', null) + }) + this.on('tabs', 'tabCountChanged', async count => { + if (!count) await this.call('manager', 'activatePlugin', 'home') + }) + this.on('manager', 'activate', (profile: Profile) => { + switch (profile.name) { + case 'filePanel': + this.call('menuicons', 'select', 'filePanel') + break + } + }) + document.addEventListener('keypress', e => { + if (e.shiftKey && e.ctrlKey) { + if (e.code === 'KeyF') { + // Ctrl+Shift+F + this.call('menuicons', 'select', 'filePanel') + } else if (e.code === 'KeyA') { + // Ctrl+Shift+A + this.call('menuicons', 'select', 'pluginManager') + } else if (e.code === 'KeyS') { + // Ctrl+Shift+S + this.call('menuicons', 'select', 'settings') + } + e.preventDefault() + } + }) + const queryParams = new QueryParams() + const params = queryParams.get() + if (params.minimizeterminal || params.embed) { + this.panels.terminal.minimized = true + this.event.emit('change', null) + } + if (params.minimizesidepanel || params.embed) { + this.event.emit('minimizesidepanel') + } + } + + minimize (name: string, minimized:boolean): void { + this.panels[name].minimized = minimized + this.event.emit('change', null) + } +} diff --git a/apps/remix-ide/src/app/panels/main-view.js b/apps/remix-ide/src/app/panels/main-view.js deleted file mode 100644 index bf3a07a35b5..00000000000 --- a/apps/remix-ide/src/app/panels/main-view.js +++ /dev/null @@ -1,204 +0,0 @@ -import Registry from '../state/registry' - -var yo = require('yo-yo') -var EventManager = require('../../lib/events') - -var { TabProxy } = require('./tab-proxy.js') - -var csjs = require('csjs-inject') - -var css = csjs` - .mainview { - display : flex; - flex-direction : column; - height : 100%; - width : 100%; - } -` - -// @todo(#650) Extract this into two classes: MainPanel (TabsProxy + Iframe/Editor) & BottomPanel (Terminal) -export class MainView { - constructor (contextualListener, editor, mainPanel, fileManager, appManager, terminal) { - var self = this - self.event = new EventManager() - self._view = {} - self._components = {} - self._components.registry = Registry.getInstance() - self.contextualListener = contextualListener - self.editor = editor - self.fileManager = fileManager - self.mainPanel = mainPanel - self.txListener = Registry.getInstance().get('txlistener').api - self._components.terminal = terminal - this.appManager = appManager - this.init() - } - - showApp (name) { - this.fileManager.unselectCurrentFile() - this.mainPanel.showContent(name) - this._view.editor.style.display = 'none' - this._view.mainPanel.style.display = 'block' - } - - getAppPanel () { - return this.mainPanel - } - - init () { - var self = this - self._deps = { - config: self._components.registry.get('config').api, - fileManager: self._components.registry.get('filemanager').api - } - - self.tabProxy = new TabProxy(self.fileManager, self.editor) - /* - We listen here on event from the tab component to display / hide the editor and mainpanel - depending on the content that should be displayed - */ - self.fileManager.events.on('currentFileChanged', (file) => { - // we check upstream for "fileChanged" - self._view.editor.style.display = 'block' - self._view.mainPanel.style.display = 'none' - }) - self.tabProxy.event.on('openFile', (file) => { - self._view.editor.style.display = 'block' - self._view.mainPanel.style.display = 'none' - }) - self.tabProxy.event.on('closeFile', (file) => { - }) - self.tabProxy.event.on('switchApp', self.showApp.bind(self)) - self.tabProxy.event.on('closeApp', (name) => { - self._view.editor.style.display = 'block' - self._view.mainPanel.style.display = 'none' - }) - self.tabProxy.event.on('tabCountChanged', (count) => { - if (!count) this.editor.displayEmptyReadOnlySession() - }) - self.data = { - _layout: { - top: { - offset: self._terminalTopOffset(), - show: true - } - } - } - - self._components.terminal.event.register('resize', delta => self._adjustLayout('top', delta)) - if (self.txListener) { - self._components.terminal.event.register('listenOnNetWork', (listenOnNetWork) => { - self.txListener.setListenOnNetwork(listenOnNetWork) - }) - } - } - - _terminalTopOffset () { - return this._deps.config.get('terminal-top-offset') || 150 - } - - _adjustLayout (direction, delta) { - var limitUp = 0 - var limitDown = 32 - var containerHeight = window.innerHeight - limitUp // - menu bar containerHeight - var self = this - var layout = self.data._layout[direction] - if (layout) { - if (delta === undefined) { - layout.show = !layout.show - if (layout.show) delta = layout.offset - else delta = 0 - } else { - layout.show = true - self._deps.config.set(`terminal-${direction}-offset`, delta) - layout.offset = delta - } - } - var tmp = delta - limitDown - delta = tmp > 0 ? tmp : 0 - if (direction === 'top') { - var mainPanelHeight = containerHeight - delta - mainPanelHeight = mainPanelHeight < 0 ? 0 : mainPanelHeight - self._view.editor.style.height = `${mainPanelHeight}px` - self._view.mainPanel.style.height = `${mainPanelHeight}px` - self._view.terminal.style.height = `${delta}px` // - menu bar height - self.editor.resize((document.querySelector('#editorWrap') || {}).checked) - self._components.terminal.scroll2bottom() - } - } - - minimizeTerminal () { - this._adjustLayout('top') - } - - showTerminal (offset) { - this._adjustLayout('top', offset || this._terminalTopOffset()) - } - - getTerminal () { - return this._components.terminal - } - - getEditor () { - var self = this - return self.editor - } - - refresh () { - var self = this - self._view.tabs.onmouseenter() - } - - log (data = {}) { - var self = this - var command = self._components.terminal.commands[data.type] - if (typeof command === 'function') command(data.value) - } - - logMessage (msg) { - var self = this - self.log({ type: 'log', value: msg }) - } - - logHtmlMessage (msg) { - var self = this - self.log({ type: 'html', value: msg }) - } - - render () { - var self = this - if (self._view.mainview) return self._view.mainview - self._view.editor = self.editor.render() - self._view.editor.style.display = 'none' - self._view.mainPanel = self.mainPanel.render() - self._view.terminal = self._components.terminal.render() - - self._view.mainview = yo` -
- ${self.tabProxy.renderTabsbar()} - ${self._view.editor} - ${self._view.mainPanel} -
- ${self._view.terminal} -
- ` - - // INIT - self._adjustLayout('top', self.data._layout.top.offset) - - document.addEventListener('keydown', (e) => { - if (e.altKey && e.keyCode === 84) self.tabProxy.switchNextTab() // alt + t - }) - - return self._view.mainview - } - - registerCommand (name, command, opts) { - var self = this - return self._components.terminal.registerCommand(name, command, opts) - } - - updateTerminalFilter (filter) { - this._components.terminal.updateJournal(filter) - } -} diff --git a/apps/remix-ide/src/app/panels/tab-proxy.js b/apps/remix-ide/src/app/panels/tab-proxy.js index 5b65254830e..267b54d8533 100644 --- a/apps/remix-ide/src/app/panels/tab-proxy.js +++ b/apps/remix-ide/src/app/panels/tab-proxy.js @@ -22,6 +22,7 @@ export class TabProxy extends Plugin { this._view = {} this._handlers = {} this.loadedTabs = [] + this.el = document.createElement('div') } onActivation () { @@ -72,10 +73,12 @@ export class TabProxy extends Plugin { this.addTab(workspacePath, '', () => { this.fileManager.open(file) this.event.emit('openFile', file) + this.emit('openFile', file) }, () => { this.fileManager.closeFile(file) this.event.emit('closeFile', file) + this.emit('closeFile', file) }) this.tabsApi.activateTab(workspacePath) } else { @@ -88,10 +91,12 @@ export class TabProxy extends Plugin { this.addTab(path, '', () => { this.fileManager.open(file) this.event.emit('openFile', file) + this.emit('openFile', file) }, () => { this.fileManager.closeFile(file) this.event.emit('closeFile', file) + this.emit('closeFile', file) }) this.tabsApi.activateTab(path) } @@ -132,9 +137,9 @@ export class TabProxy extends Plugin { this.addTab( name, displayName, - () => this.event.emit('switchApp', name), + () => this.emit('switchApp', name), () => { - this.event.emit('closeApp', name) + this.emit('closeApp', name) this.call('manager', 'deactivatePlugin', name) }, icon @@ -149,7 +154,7 @@ export class TabProxy extends Plugin { } focus (name) { - this.event.emit('switchApp', name) + this.emit('switchApp', name) this.tabsApi.activateTab(name) } @@ -199,6 +204,7 @@ export class TabProxy extends Plugin { () => { this.fileManager.closeFile(newName) this.event.emit('closeFile', newName) + this.emit('closeFile', newName) }) this.removeTab(oldName) } @@ -285,7 +291,7 @@ export class TabProxy extends Plugin { if (this.loadedTabs[index]) { const name = this.loadedTabs[index].name if (this._handlers[name]) this._handlers[name].switchTo() - this.event.emit('tabCountChanged', this.loadedTabs.length) + this.emit('tabCountChanged', this.loadedTabs.length) } } @@ -293,7 +299,7 @@ export class TabProxy extends Plugin { if (this.loadedTabs[index]) { const name = this.loadedTabs[index].name if (this._handlers[name]) this._handlers[name].close() - this.event.emit('tabCountChanged', this.loadedTabs.length) + this.emit('tabCountChanged', this.loadedTabs.length) } } @@ -308,8 +314,6 @@ export class TabProxy extends Plugin { } renderTabsbar () { - this.el = document.createElement('div') - this.renderComponent() return this.el } } diff --git a/apps/remix-ide/src/app/panels/terminal.js b/apps/remix-ide/src/app/panels/terminal.js index 272284c53cd..6309327cb69 100644 --- a/apps/remix-ide/src/app/panels/terminal.js +++ b/apps/remix-ide/src/app/panels/terminal.js @@ -20,7 +20,7 @@ function register (api) { KONSOLES.push(api) } const profile = { displayName: 'Terminal', name: 'terminal', - methods: ['log'], + methods: ['log', 'logHtml'], events: [], description: ' - ', version: packageJson.version diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index b966287690d..48f5484979d 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -34,14 +34,14 @@ const profile = { } export class RunTab extends ViewPlugin { - constructor (blockchain, config, fileManager, editor, filePanel, compilersArtefacts, networkModule, mainView, fileProvider) { + constructor (blockchain, config, fileManager, editor, filePanel, compilersArtefacts, networkModule, fileProvider) { super(profile) this.event = new EventManager() this.config = config this.blockchain = blockchain this.fileManager = fileManager this.editor = editor - this.logCallback = (msg) => { mainView.getTerminal().logHtml(yo`
${msg}
`) } + this.logCallback = (msg) => { this.call('terminal', 'logHtml', yo`
${msg}
`) } this.filePanel = filePanel this.compilersArtefacts = compilersArtefacts this.networkModule = networkModule diff --git a/apps/remix-ide/src/framingService.js b/apps/remix-ide/src/framingService.js deleted file mode 100644 index ed029b7a65c..00000000000 --- a/apps/remix-ide/src/framingService.js +++ /dev/null @@ -1,34 +0,0 @@ -export class FramingService { - constructor (sidePanel, verticalIcons, mainView, resizeFeature) { - this.sidePanel = sidePanel - this.verticalIcons = verticalIcons - this.mainPanel = mainView.getAppPanel() - this.mainView = mainView - this.resizeFeature = resizeFeature - } - - start (params) { - this.verticalIcons.select('filePanel') - - document.addEventListener('keypress', (e) => { - if (e.shiftKey && e.ctrlKey) { - if (e.code === 'KeyF') { // Ctrl+Shift+F - this.verticalIcons.select('filePanel') - } else if (e.code === 'KeyA') { // Ctrl+Shift+A - this.verticalIcons.select('pluginManager') - } else if (e.code === 'KeyS') { // Ctrl+Shift+S - this.verticalIcons.select('settings') - } - e.preventDefault() - } - }) - - if (params.minimizeterminal) this.mainView.minimizeTerminal() - if (params.minimizesidepanel) this.resizeFeature.hidePanel() - } - - embed () { - this.mainView.minimizeTerminal() - this.resizeFeature.hidePanel() - } -} diff --git a/apps/remix-ide/src/lib/panels-resize.js b/apps/remix-ide/src/lib/panels-resize.js deleted file mode 100644 index 157535a8695..00000000000 --- a/apps/remix-ide/src/lib/panels-resize.js +++ /dev/null @@ -1,88 +0,0 @@ -const yo = require('yo-yo') -const csjs = require('csjs-inject') - -const css = csjs` - .dragbar { - width : 2px; - height : 100%; - cursor : col-resize; - z-index : 999; - } - .ghostbar { - width : 3px; - background-color : var(--primary); - opacity : 0.5; - position : absolute; - cursor : col-resize; - z-index : 9999; - top : 0; - bottom : 0; - } -` - -export default class PanelsResize { - constructor (panel) { - this.panel = panel - const string = panel.style.minWidth - this.minWidth = string.length > 2 ? parseInt(string.substring(0, string.length - 2)) : 0 - } - - render () { - this.ghostbar = yo`
` - - const mousedown = (event) => { - event.preventDefault() - if (event.which === 1) { - moveGhostbar(event) - document.body.appendChild(this.ghostbar) - document.addEventListener('mousemove', moveGhostbar) - document.addEventListener('mouseup', removeGhostbar) - document.addEventListener('keydown', cancelGhostbar) - } - } - - const cancelGhostbar = (event) => { - if (event.keyCode === 27) { - document.body.removeChild(this.ghostbar) - document.removeEventListener('mousemove', moveGhostbar) - document.removeEventListener('mouseup', removeGhostbar) - document.removeEventListener('keydown', cancelGhostbar) - } - } - - const moveGhostbar = (event) => { - this.ghostbar.style.left = event.x + 'px' - } - - const removeGhostbar = (event) => { - document.body.removeChild(this.ghostbar) - document.removeEventListener('mousemove', moveGhostbar) - document.removeEventListener('mouseup', removeGhostbar) - document.removeEventListener('keydown', cancelGhostbar) - this.setPosition(event) - } - - return yo`
` - } - - calculatePanelWidth (event) { - return event.x - this.panel.offsetLeft - } - - setPosition (event) { - const panelWidth = this.calculatePanelWidth(event) - // close the panel if the width is less than a minWidth - if (panelWidth > this.minWidth - 10 || this.panel.style.display === 'none') { - this.panel.style.width = panelWidth + 'px' - this.showPanel() - } else this.hidePanel() - } - - hidePanel () { - this.panel.style.display = 'none' - } - - showPanel () { - this.panel.style.display = 'flex' - } -} diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js index 74410cadf0a..5f5fa2f8eb6 100644 --- a/apps/remix-ide/src/remixAppManager.js +++ b/apps/remix-ide/src/remixAppManager.js @@ -1,20 +1,20 @@ /* global localStorage, fetch */ import { PluginManager } from '@remixproject/engine' -import { IframePlugin } from '@remixproject/engine-web' import { EventEmitter } from 'events' import QueryParams from './lib/query-params' import { PermissionHandler } from './app/ui/persmission-handler' +import { IframePlugin } from '@remixproject/engine-web' const _paq = window._paq = window._paq || [] const requiredModules = [ // services + layout views + system views 'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', - 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity-logic', 'gistHandler'] + 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity-logic', 'gistHandler', 'layout', 'modal'] const dependentModules = ['git', 'hardhat', 'slither'] // module which shouldn't be manually activated (e.g git is activated by remixd) export function isNative (name) { - const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'hardhat-provider', 'solidityStaticAnalysis', 'solidityUnitTesting'] + const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'hardhat-provider', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'modal'] return nativePlugins.includes(name) || requiredModules.includes(name) } @@ -78,6 +78,7 @@ export class RemixAppManager extends PluginManager { onPluginActivated (plugin) { this.pluginLoader.set(plugin, this.actives) this.event.emit('activate', plugin) + this.emit('activate', plugin) if (!requiredModules.includes(plugin.name)) _paq.push(['trackEvent', 'pluginManager', 'activate', plugin.name]) } @@ -131,6 +132,7 @@ export class RemixAppManager extends PluginManager { } return plugins.map(plugin => { return new IframePlugin(plugin) + // return new IframeReactPlugin(plugin) }) } diff --git a/apps/remix-ide/tsconfig.json b/apps/remix-ide/tsconfig.json index 052537730e5..04d219a926b 100644 --- a/apps/remix-ide/tsconfig.json +++ b/apps/remix-ide/tsconfig.json @@ -4,6 +4,7 @@ "jsx": "react", "allowJs": true, "esModuleInterop": true, + "resolveJsonModule": true, "allowSyntheticDefaultImports": true, "types": ["node", "jest"], "module": "es6", diff --git a/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.css b/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.css index a423dd6605b..1330c1179c0 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.css +++ b/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.css @@ -1,26 +1,27 @@ /* dragbar UI */ -.dragbar { - display : block; - height : 100%; - position : absolute; - left: 0px; - top: 0px; - width: 0.3em; - z-index: 9999; - } - - .overlay { - position: absolute; - left: 0; - top: 0; - width: 100vw; - height: 100vh; - display: block; - z-index: 9998; - } - - .dragbar:hover, .dragbar.ondrag{ - background-color: var(--secondary); - cursor:col-resize; - } \ No newline at end of file +.dragbar { + display: block; + height: 100%; + position: absolute; + left: 0px; + top: 0px; + width: 0.3em; + z-index: 9999; +} + +.overlay { + position: absolute; + left: 0; + top: 0; + width: 100vw; + height: 100vh; + display: block; + z-index: 9998; +} + +.dragbar:hover, +.dragbar.ondrag { + background-color: var(--secondary); + cursor: col-resize; +} diff --git a/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx b/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx index 29c4eeda6bc..429e49f6388 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx @@ -15,18 +15,23 @@ const DragBar = (props: IRemixDragBarUi) => { const [offset, setOffSet] = useState(0) const nodeRef = React.useRef(null) // fix for strictmode - useEffect(() => { - // arbitrary time out to wait the the UI to be completely done - setTimeout(() => { - setOffSet(props.refObject.current.offsetLeft) - setDragBarPosX(offset + props.refObject.current.offsetWidth) - }, 1000) - }, []) - useEffect(() => { setDragBarPosX(offset + (props.hidden ? 0 : props.refObject.current.offsetWidth)) }, [props.hidden, offset]) + const handleResize = () => { + setOffSet(props.refObject.current.offsetLeft) + setDragBarPosX(props.refObject.current.offsetLeft + props.refObject.current.offsetWidth) + } + + useEffect(() => { + window.addEventListener('resize', handleResize) + // TODO: not a good way to wait on the ref doms element to be rendered of course + setTimeout(() => + handleResize(), 2000) + return () => window.removeEventListener('resize', handleResize) + }, []) + function stopDrag (e: MouseEvent, data: any) { setDragState(false) if (data.x < props.minWidth) { diff --git a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx index e86e4ed63db..27d5a7b832d 100644 --- a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useRef, useState } from 'react' import './style/remix-app.css' +import { RemixUIMainPanel } from '@remix-ui/panel' import RemixSplashScreen from './components/splashscreen' import MatomoDialog from './components/modals/matomo' import OriginWarning from './components/modals/origin-warning' @@ -62,6 +63,13 @@ const RemixApp = (props: IRemixAppUi) => { props.app.sidePanel.events.on('showing', () => { setHideSidePanel(false) }) + + props.app.layout.event.on('minimizesidepanel', () => { + // the 'showing' event always fires from sidepanel, so delay this a bit + setTimeout(() => { + setHideSidePanel(true) + }, 1000) + }) } const components = { @@ -75,7 +83,8 @@ const RemixApp = (props: IRemixAppUi) => { settings: props.app.settings, showMatamo: props.app.showMatamo, appManager: props.app.appManager, - modal: props.app.modal + modal: props.app.modal, + layout: props.app.layout } return ( @@ -88,8 +97,9 @@ const RemixApp = (props: IRemixAppUi) => { {components.iconPanel} {components.sidePanel} - {components.mainPanel} - +
+ +
{components.hiddenPanel} diff --git a/libs/remix-ui/panel/.babelrc b/libs/remix-ui/panel/.babelrc new file mode 100644 index 00000000000..64a37486919 --- /dev/null +++ b/libs/remix-ui/panel/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@nrwl/react/babel"], + "plugins": [] +} \ No newline at end of file diff --git a/libs/remix-ui/panel/.eslintrc.json b/libs/remix-ui/panel/.eslintrc.json new file mode 100644 index 00000000000..5a1c541d113 --- /dev/null +++ b/libs/remix-ui/panel/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nrwl/nx/react", "../../../.eslintrc"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/libs/remix-ui/panel/README.md b/libs/remix-ui/panel/README.md new file mode 100644 index 00000000000..fa765fcb391 --- /dev/null +++ b/libs/remix-ui/panel/README.md @@ -0,0 +1,7 @@ +# remix-ui-side-panel + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test remix-ui-side-panel` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/remix-ui/panel/src/index.ts b/libs/remix-ui/panel/src/index.ts new file mode 100644 index 00000000000..c61d9612f13 --- /dev/null +++ b/libs/remix-ui/panel/src/index.ts @@ -0,0 +1,2 @@ +export { default as RemixPluginPanel } from './lib/plugins/remix-ui-panel' +export { default as RemixUIMainPanel } from './lib/main/main-panel' diff --git a/libs/remix-ui/panel/src/lib/dragbar/dragbar.css b/libs/remix-ui/panel/src/lib/dragbar/dragbar.css new file mode 100644 index 00000000000..1ad9f9de856 --- /dev/null +++ b/libs/remix-ui/panel/src/lib/dragbar/dragbar.css @@ -0,0 +1,27 @@ +/* dragbar UI */ + +.dragbar_terminal { + display: block; + width: 100%; + position: absolute; + left: 0px; + top: 0px; + height: 0.3em; + z-index: 9999; +} + +.overlay { + position: absolute; + left: 0; + top: 0; + width: 100vw; + height: 100vh; + display: block; + z-index: 900; +} + +.dragbar_terminal:hover, +.dragbar_terminal.ondrag { + background-color: var(--secondary); + cursor: row-resize; +} diff --git a/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx b/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx new file mode 100644 index 00000000000..232f23ff10d --- /dev/null +++ b/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx @@ -0,0 +1,51 @@ +// eslint-disable-next-line no-use-before-define +import React, { useEffect, useState } from 'react' +import Draggable from 'react-draggable' +import './dragbar.css' + +interface IRemixDragBarUi { + refObject: React.MutableRefObject; + setHideStatus: (hide: boolean) => void; + hidden: boolean + minHeight?: number +} + +const DragBar = (props: IRemixDragBarUi) => { + const [dragState, setDragState] = useState(false) + const [dragBarPosY, setDragBarPosY] = useState(0) + const nodeRef = React.useRef(null) // fix for strictmode + + function stopDrag (e: MouseEvent, data: any) { + const h = window.innerHeight - data.y + props.refObject.current.setAttribute('style', `height: ${h}px;`) + setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) + setDragState(false) + } + const handleResize = () => { + setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) + } + + useEffect(() => { + handleResize() + }, [props.hidden]) + + useEffect(() => { + window.addEventListener('resize', handleResize) + // TODO: not a good way to wait on the ref doms element to be rendered of course + setTimeout(() => + handleResize(), 2000) + return () => window.removeEventListener('resize', handleResize) + }, []) + + function startDrag () { + setDragState(true) + } + return <> +
+ +
+
+ +} + +export default DragBar diff --git a/libs/remix-ui/panel/src/lib/main/main-panel.css b/libs/remix-ui/panel/src/lib/main/main-panel.css new file mode 100644 index 00000000000..d569338fab1 --- /dev/null +++ b/libs/remix-ui/panel/src/lib/main/main-panel.css @@ -0,0 +1,8 @@ +.mainview { + display : flex; + flex-direction : column; + height : 100%; + width : 100%; + position: relative; + } + diff --git a/libs/remix-ui/panel/src/lib/main/main-panel.tsx b/libs/remix-ui/panel/src/lib/main/main-panel.tsx new file mode 100644 index 00000000000..4fb00ddf65c --- /dev/null +++ b/libs/remix-ui/panel/src/lib/main/main-panel.tsx @@ -0,0 +1,60 @@ +/* eslint-disable no-unused-expressions */ +import { AppContext } from 'libs/remix-ui/app/src/lib/remix-app/context/context' +import React, { useContext, useEffect, useLayoutEffect, useRef, useState } from 'react' // eslint-disable-line +import DragBar from '../dragbar/dragbar' +import RemixUIPanelPlugin from '../plugins/panel-plugin' +import { PluginRecord } from '../types' +import './main-panel.css' + +const RemixUIMainPanel = () => { + const appContext = useContext(AppContext) + const [plugins, setPlugins] = useState([]) + const editorRef = useRef(null) + const mainPanelRef = useRef(null) + const tabsRef = useRef(null) + const terminalRef = useRef(null) + + const refs = [tabsRef, editorRef, mainPanelRef, terminalRef] + + const renderPanels = () => { + if (appContext) { + const pluginPanels: PluginRecord[] = [] + Object.values(appContext.layout.panels).map((panel: any) => { + pluginPanels.push({ + profile: panel.plugin.profile, + active: panel.active, + view: panel.plugin.profile.name === 'tabs' ? panel.plugin.renderTabsbar() : panel.plugin.render(), + class: panel.plugin.profile.name + '-wrap ' + (panel.minimized ? 'minimized' : ''), + minimized: panel.minimized + }) + }) + setPlugins(pluginPanels) + } + } + + useEffect(() => { + renderPanels() + appContext.layout.event.on('change', () => { + renderPanels() + }) + }, []) + + return ( +
+ {Object.values(plugins).map((pluginRecord, i) => { + return ( + + {(pluginRecord.profile.name === 'terminal') ? : null} + + + ) + })} +
+ ) +} + +export default RemixUIMainPanel diff --git a/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx b/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx new file mode 100644 index 00000000000..5acd50541a3 --- /dev/null +++ b/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx @@ -0,0 +1,27 @@ +/* eslint-disable jsx-a11y/anchor-has-content */ +import React, { useEffect, useRef, useState } from 'react' // eslint-disable-line +import { PluginRecord } from '../types' +import './panel.css' + +export interface RemixPanelProps { + plugins: Record; + } +const RemixUIPanelHeader = (props: RemixPanelProps) => { + const [plugin, setPlugin] = useState() + + useEffect(() => { + if (props.plugins) { + const p = Object.values(props.plugins).find((pluginRecord) => { + return pluginRecord.active === true + }) + setPlugin(p) + } + }, [props]) + + return ( +
{plugin?.profile.displayName || plugin?.profile.name}
+ {plugin?.profile.documentation ? () : ''} +
) +} + +export default RemixUIPanelHeader diff --git a/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx b/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx new file mode 100644 index 00000000000..9eb30391bc6 --- /dev/null +++ b/libs/remix-ui/panel/src/lib/plugins/panel-plugin.tsx @@ -0,0 +1,37 @@ +/* eslint-disable no-undef */ +import React, { forwardRef, useEffect, useRef, useState } from 'react' // eslint-disable-line +import { PluginRecord } from '../types' +import './panel.css' +interface panelPLuginProps { + pluginRecord: PluginRecord +} + +const RemixUIPanelPlugin = (props: panelPLuginProps, panelRef: any) => { + const localRef = useRef(null) + const [view, setView] = useState() + useEffect(() => { + const ref:any = panelRef || localRef + if (ref.current) { + if (props.pluginRecord.view) { + if (React.isValidElement(props.pluginRecord.view)) { + setView(props.pluginRecord.view) + } else { + ref.current.appendChild(props.pluginRecord.view) + } + } + } + }, []) + + return ( +
+ {view} +
+ ) +} + +export default forwardRef(RemixUIPanelPlugin) diff --git a/libs/remix-ui/panel/src/lib/plugins/panel.css b/libs/remix-ui/panel/src/lib/plugins/panel.css new file mode 100644 index 00000000000..d2b2133667e --- /dev/null +++ b/libs/remix-ui/panel/src/lib/plugins/panel.css @@ -0,0 +1,110 @@ +.panel { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + flex: auto; +} + +.swapitTitle { + margin: 0; + text-transform: uppercase; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.swapitTitle i { + padding-left: 6px; + font-size: 14px; +} + +.swapitHeader { + display: flex; + align-items: center; + padding: 16px 24px 15px; + justify-content: space-between; + text-transform: uppercase; +} + +.icons i { + height: 80%; + cursor: pointer; +} + +.pluginsContainer { + height: 100%; + overflow-y: auto; +} + +.titleInfo { + padding-left: 10px; +} + +.versionBadge { + background-color: var(--light); + padding: 0 7px; + font-weight: bolder; + margin-left: 5px; + text-transform: lowercase; + cursor: default; +} + +iframe { + height: 100%; + width: 100%; + border: 0; +} + +.plugins { + height: 100%; +} + +.plugItIn { + display: none; + height: 100%; +} + +.plugItIn>div { + overflow-y: auto; + overflow-x: hidden; + height: 100%; + width: 100%; +} + +.plugItIn.active { + display: block; +} + +.pluginsContainer { + height: 100%; + overflow-y: hidden; +} + +#editorView { + height: 100%; + width: 100%; + border: 0; + display: block; +} + +#mainPanel { + height: 100%; + width: 100%; + border: 0; + display: block; +} + +.mainPanel-wrap, .editor-wrap { + flex: 1; + min-height: 100px; +} + +.terminal-wrap { + min-height: 35px; + height: 20%; +} + +.terminal-wrap.minimized { + height: 2rem !important; +} diff --git a/libs/remix-ui/panel/src/lib/plugins/remix-ui-panel.tsx b/libs/remix-ui/panel/src/lib/plugins/remix-ui-panel.tsx new file mode 100644 index 00000000000..37fa018c92a --- /dev/null +++ b/libs/remix-ui/panel/src/lib/plugins/remix-ui-panel.tsx @@ -0,0 +1,29 @@ +/* eslint-disable no-undef */ +import React, { useEffect, useState } from 'react' // eslint-disable-line +import './panel.css' +import RemixUIPanelPlugin from './panel-plugin' +import { PluginRecord } from '../types' + +/* eslint-disable-next-line */ +export interface RemixPanelProps { + plugins: Record + header: JSX.Element +} + +export function RemixPluginPanel (props: RemixPanelProps) { + return ( + <> + {props.header} +
+
+ {Object.values(props.plugins).map((pluginRecord) => { + return + })} +
+
+ + + ) +} + +export default RemixPluginPanel diff --git a/libs/remix-ui/panel/src/lib/types/index.ts b/libs/remix-ui/panel/src/lib/types/index.ts new file mode 100644 index 00000000000..f8407033ab4 --- /dev/null +++ b/libs/remix-ui/panel/src/lib/types/index.ts @@ -0,0 +1,9 @@ +import { Profile } from '@remixproject/plugin-utils' + +export type PluginRecord = { + profile: Profile + view: any + active: boolean + class?: string + minimized?: boolean + } diff --git a/libs/remix-ui/panel/tsconfig.json b/libs/remix-ui/panel/tsconfig.json new file mode 100644 index 00000000000..8bd701c578c --- /dev/null +++ b/libs/remix-ui/panel/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/libs/remix-ui/panel/tsconfig.lib.json b/libs/remix-ui/panel/tsconfig.lib.json new file mode 100644 index 00000000000..b560bc4dec6 --- /dev/null +++ b/libs/remix-ui/panel/tsconfig.lib.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": ["**/*.spec.ts", "**/*.spec.tsx"], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/libs/remix-ui/plugin-manager/src/types.d.ts b/libs/remix-ui/plugin-manager/src/types.d.ts index d30ae314570..66ee57bc77e 100644 --- a/libs/remix-ui/plugin-manager/src/types.d.ts +++ b/libs/remix-ui/plugin-manager/src/types.d.ts @@ -4,6 +4,7 @@ import { EventEmitter } from 'events' import { Engine } from '@remixproject/engine/lib/engine' import { PluginBase, Profile } from '@remixproject/plugin-utils' import { IframePlugin, ViewPlugin, WebsocketPlugin } from '@remixproject/engine-web' +import { IframeReactPlugin } from '@remix-ui/app' /* eslint-disable camelcase */ interface SetPluginOptionType { @@ -88,7 +89,7 @@ export class PluginManagerComponent extends ViewPlugin extends Plugin implements render(): HTMLDivElement getAndFilterPlugins: (filter?: string, profiles?: Profile[]) => void triggerEngineEventListener: () => void - activateAndRegisterLocalPlugin: (localPlugin: IframePlugin | WebsocketPlugin) => Promise + activateAndRegisterLocalPlugin: (localPlugin: IframePlugin | IframeReactPlugin | WebsocketPlugin) => Promise activeProfiles: string[] _paq: any } diff --git a/libs/remix-ui/solidity-unit-testing/.babelrc b/libs/remix-ui/solidity-unit-testing/.babelrc index 09d67939cc9..64a37486919 100644 --- a/libs/remix-ui/solidity-unit-testing/.babelrc +++ b/libs/remix-ui/solidity-unit-testing/.babelrc @@ -1,4 +1,4 @@ { "presets": ["@nrwl/react/babel"], "plugins": [] -} +} \ No newline at end of file diff --git a/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts b/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts index dafe6f78cf1..d0efd289b58 100644 --- a/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts +++ b/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts @@ -106,8 +106,8 @@ export const registerErrorScriptRunnerAction = (on, commandName, commandFn, disp }) } -export const listenOnNetworkAction = async (event, isListening) => { - event.trigger('listenOnNetWork', [isListening]) +export const listenOnNetworkAction = async (plugins, isListening) => { + plugins.txListener.setListenOnNetwork(isListening) } export const initListeningOnNetwork = (plugins, dispatch: React.Dispatch) => { diff --git a/libs/remix-ui/terminal/src/lib/custom-hooks/useDragTerminal.tsx b/libs/remix-ui/terminal/src/lib/custom-hooks/useDragTerminal.tsx deleted file mode 100644 index aba4bbf6a56..00000000000 --- a/libs/remix-ui/terminal/src/lib/custom-hooks/useDragTerminal.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { useEffect, useState } from 'react' - -export const useDragTerminal = (minHeight: number, defaultPosition: number) => { - const [isOpen, setIsOpen] = useState(defaultPosition > minHeight) - const [lastYPosition, setLastYPosition] = useState(0) - const [terminalPosition, setTerminalPosition] = useState(defaultPosition) - // Used to save position of the terminal when it is closed - const [lastTerminalPosition, setLastTerminalPosition] = useState(defaultPosition) - const [isDragging, setIsDragging] = useState(false) - - const handleDraggingStart = (event: React.MouseEvent) => { - setLastYPosition(event.clientY) - setIsDragging(true) - } - - const handleDragging = (event: MouseEvent) => { - event.preventDefault() - - if (isDragging) { - const mouseYPosition = event.clientY - const difference = lastYPosition - mouseYPosition - const newTerminalPosition = terminalPosition + difference - setTerminalPosition(newTerminalPosition) - setLastYPosition(mouseYPosition) - } - } - - const handleDraggingEnd = () => { - if (!isDragging) return - - setIsDragging(false) - - // Check terminal position to determine if it should be open or closed - setIsOpen(terminalPosition > minHeight) - } - - const handleToggleTerminal = (event: React.MouseEvent) => { - event.preventDefault() - event.stopPropagation() - - if (isOpen) { - setLastTerminalPosition(terminalPosition) - setLastYPosition(0) - setTerminalPosition(minHeight) - } else { - setTerminalPosition(lastTerminalPosition <= minHeight ? 323 : lastTerminalPosition) - } - - setIsOpen(!isOpen) - } - - // Add event listeners for dragging - useEffect(() => { - document.addEventListener('mousemove', handleDragging) - document.addEventListener('mouseup', handleDraggingEnd) - - return () => { - document.removeEventListener('mousemove', handleDragging) - document.removeEventListener('mouseup', handleDraggingEnd) - } - }, [handleDragging, handleDraggingEnd]) - - // Reset terminal position - useEffect(() => { - if (!terminalPosition) { - setTerminalPosition(defaultPosition) - } - }, [terminalPosition, setTerminalPosition]) - - return { - isOpen, - terminalPosition, - isDragging, - handleDraggingStart, - handleToggleTerminal - } -} diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx index 46dbea432d4..3536750efcc 100644 --- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx +++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import React, { useState, useEffect, useReducer, useRef, SyntheticEvent, MouseEvent } from 'react' // eslint-disable-line import { registerCommandAction, registerLogScriptRunnerAction, registerInfoScriptRunnerAction, registerErrorScriptRunnerAction, registerWarnScriptRunnerAction, listenOnNetworkAction, initListeningOnNetwork } from './actions/terminalAction' import { initialState, registerCommandReducer, addCommandHistoryReducer, registerScriptRunnerReducer } from './reducers/terminalReducer' @@ -17,7 +18,6 @@ import RenderKnownTransactions from './components/RenderKnownTransactions' // es import parse from 'html-react-parser' import { EMPTY_BLOCK, KNOWN_TRANSACTION, RemixUiTerminalProps, UNKNOWN_TRANSACTION } from './types/terminalTypes' import { wrapScript } from './utils/wrapScript' -import { useDragTerminal } from './custom-hooks/useDragTerminal' /* eslint-disable-next-line */ export interface ClipboardEvent extends SyntheticEvent { @@ -28,7 +28,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { const { call, _deps, on, config, event, version } = props.plugin const [_cmdIndex, setCmdIndex] = useState(-1) const [_cmdTemp, setCmdTemp] = useState('') - + const [isOpen, setIsOpen] = useState(true) const [newstate, dispatch] = useReducer(registerCommandReducer, initialState) const [cmdHistory, cmdHistoryDispatch] = useReducer(addCommandHistoryReducer, initialState) const [, scriptRunnerDispatch] = useReducer(registerScriptRunnerReducer, initialState) @@ -79,24 +79,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { const terminalMenuOffsetHeight = (terminalMenu.current && terminalMenu.current.offsetHeight) || 35 const terminalDefaultPosition = config.get('terminal-top-offset') - const { - isOpen, - isDragging, - terminalPosition, - handleDraggingStart, - handleToggleTerminal - } = useDragTerminal(terminalMenuOffsetHeight, terminalDefaultPosition) - - // Check open state - useEffect(() => { - const resizeValue = isOpen ? [config.get('terminal-top-offset')] : [] - event.trigger('resize', resizeValue) - }, [isOpen]) - - useEffect(() => { - event.trigger('resize', [terminalPosition]) - }, [terminalPosition]) - const scrollToBottom = () => { messagesEndRef.current.scrollIntoView({ behavior: 'smooth' }) } @@ -106,6 +88,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { logHtml: (html) => { scriptRunnerDispatch({ type: 'html', payload: { message: [html.innerText] } }) }, + log: (message) => { scriptRunnerDispatch({ type: 'log', payload: { message: [message] } }) } @@ -332,8 +315,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { const listenOnNetwork = (e: any) => { const isListening = e.target.checked - // setIsListeningOnNetwork(isListening) - listenOnNetworkAction(event, isListening) + listenOnNetworkAction(props.plugin, isListening) } const onChange = (event: any) => { @@ -426,10 +408,14 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: 0, showSuggestions: false })) } + const handleToggleTerminal = () => { + setIsOpen(!isOpen) + props.plugin.call('layout', 'minimize', props.plugin.profile.name, isOpen) + } + return ( -
+
-
diff --git a/nx.json b/nx.json index e1bc0d858ac..f2aa0098999 100644 --- a/nx.json +++ b/nx.json @@ -145,6 +145,9 @@ "remix-ui-tabs": { "tags": [] }, + "remix-ui-panel": { + "tags": [] + }, "remix-ui-theme-module": { "tags": [] }, diff --git a/package.json b/package.json index 03e89015e97..0a838a7a407 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "workspace-schematic": "nx workspace-schematic", "dep-graph": "nx dep-graph", "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,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", + "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,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", "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,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": "npm run build:libs && lerna publish --skip-git && npm run bumpVersion:libs", @@ -162,7 +162,7 @@ "chokidar": "^2.1.8", "color-support": "^1.1.3", "commander": "^2.20.3", - "core-js": "^3.19.3", + "core-js": "^3.6.5", "deep-equal": "^1.0.1", "document-register-element": "1.13.1", "ethereumjs-util": "^7.0.10", diff --git a/tsconfig.base.json b/tsconfig.base.json index 4ef2c10dcb2..21dfeb4ee8e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -69,8 +69,11 @@ "@remix-ui/tabs": ["libs/remix-ui/tabs/src/index.ts"], "@remix-ui/helper": ["libs/remix-ui/helper/src/index.ts"], "@remix-ui/app": ["libs/remix-ui/app/src/index.ts"], - "@remix-ui/vertical-icons-panel": ["libs/remix-ui/vertical-icons-panel/src/index.ts"], + "@remix-ui/vertical-icons-panel": [ + "libs/remix-ui/vertical-icons-panel/src/index.ts" + ], "@remix-ui/theme-module": ["libs/remix-ui/theme-module/src/index.ts"], + "@remix-ui/panel": ["libs/remix-ui/panel/src/index.ts"], "@remix-ui/editor-context-view": ["libs/remix-ui/editor-context-view/src/index.ts"], "@remix-ui/solidity-unit-testing": [ "libs/remix-ui/solidity-unit-testing/src/index.ts" diff --git a/workspace.json b/workspace.json index d4a9f6fcd40..76b236717f3 100644 --- a/workspace.json +++ b/workspace.json @@ -1107,6 +1107,21 @@ } } }, + "remix-ui-panel": { + "root": "libs/remix-ui/panel", + "sourceRoot": "libs/remix-ui/panel/src", + "projectType": "library", + "architect": { + "lint": { + "builder": "@nrwl/linter:lint", + "options": { + "linter": "eslint", + "tsConfig": ["libs/remix-ui/panel/tsconfig.lib.json"], + "exclude": ["**/node_modules/**", "!libs/remix-ui/panel/**/*"] + } + } + } + }, "solidity-unit-testing": { "root": "libs/remix-ui/solidity-unit-testing", "sourceRoot": "libs/remix-ui/solidity-unit-testing/src",