UNPKG

remix-ide

Version:

Minimalistic browser-based Solidity IDE

229 lines (209 loc) 7.71 kB
const yo = require('yo-yo') const csjs = require('csjs-inject') const EventEmitter = require('events') const LocalPlugin = require('./local-plugin') import { Plugin, BaseApi } from 'remix-plugin' import { PluginManagerSettings } from './plugin-manager-settings' import * as packageJson from '../../../package.json' const addToolTip = require('../ui/tooltip') const css = csjs` .pluginSearch { display: flex; flex-direction: column; align-items: center; background-color: var(--light); padding: 10px; position: sticky; top: 0; z-index: 2; margin-bottom: 0px; } .localPluginBtn { margin-top: 15px; } .displayName { text-transform: capitalize; display: flex; flex-direction: column; align-items: flex-start; } .description { text-transform: capitalize; } .row { display: flex; flex-direction: row; } .isStuck { background-color: var(--primary); color: } .versionWarning { background-color: var(--light); padding: 0 7px; font-weight: bolder; margin-top: 5px; text-transform: lowercase; cursor: default; } ` const profile = { name: 'pluginManager', displayName: 'Plugin manager', methods: [], events: [], icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNzU1IDQ1M3EzNyAzOCAzNyA5MC41dC0zNyA5MC41bC00MDEgNDAwIDE1MCAxNTAtMTYwIDE2MHEtMTYzIDE2My0zODkuNSAxODYuNXQtNDExLjUtMTAwLjVsLTM2MiAzNjJoLTE4MXYtMTgxbDM2Mi0zNjJxLTEyNC0xODUtMTAwLjUtNDExLjV0MTg2LjUtMzg5LjVsMTYwLTE2MCAxNTAgMTUwIDQwMC00MDFxMzgtMzcgOTEtMzd0OTAgMzcgMzcgOTAuNS0zNyA5MC41bC00MDAgNDAxIDIzNCAyMzQgNDAxLTQwMHEzOC0zNyA5MS0zN3Q5MCAzN3oiLz48L3N2Zz4=', description: 'Start/stop services, modules and plugins', kind: 'settings', location: 'sidePanel', documentation: 'https://remix-ide.readthedocs.io/en/latest/plugin_manager.html', version: packageJson.version } class PluginManagerComponent extends BaseApi { constructor () { super(profile) this.event = new EventEmitter() this.views = { root: null, items: {} } this.localPlugin = new LocalPlugin() this.filter = '' } setApp (appManager) { this.appManager = appManager } setStore (store) { this.store = store this.store.event.on('activate', (name) => { this.reRender() }) this.store.event.on('deactivate', (name) => { this.reRender() }) this.store.event.on('add', (api) => { this.reRender() }) this.store.event.on('remove', (api) => { this.reRender() }) } renderItem (name) { const api = this.store.getOne(name) if (!api) return const isActive = this.store.actives.includes(name) const displayName = (api.profile.displayName) ? api.profile.displayName : name // Check version of the plugin let versionWarning // Alpha if (api.profile.version && api.profile.version.match(/\b(\w*alpha\w*)\b/g)) { versionWarning = yo`<small title="Version Alpha" class="${css.versionWarning}">alpha</small>` } // Beta if (api.profile.version && api.profile.version.match(/\b(\w*beta\w*)\b/g)) { versionWarning = yo`<small title="Version Beta" class="${css.versionWarning}">beta</small>` } const activationButton = isActive ? yo` <button onclick="${_ => this.appManager.deactivateOne(name)}" class="btn btn-secondary btn-sm"> Deactivate </button>` : yo` <button onclick="${_ => this.appManager.activateOne(name)}" class="btn btn-success btn-sm"> Activate </button>` return yo` <article id="remixPluginManagerListItem_${name}" class="list-group-item py-1" title="${displayName}" > <div class="${css.row} justify-content-between align-items-center"> <h6 class="${css.displayName}"> ${displayName} ${versionWarning} </h6> ${activationButton} </div> <p class="${css.description}">${api.profile.description}</p> </article> ` } /*************** * SUB-COMPONENT */ /** * Add a local plugin to the list of plugins */ async openLocalPlugin () { try { const profile = await this.localPlugin.open(this.store.getAll()) if (!profile) return if (this.store.ids.includes(profile.name)) { throw new Error('This name has already been used') } this.appManager.registerOne(new Plugin(profile)) this.appManager.activateOne(profile.name) } catch (err) { // TODO : Use an alert to handle this error instead of a console.log console.log(`Cannot create Plugin : ${err.message}`) addToolTip(`Cannot create Plugin : ${err.message}`) } } render () { // Filtering helpers const isFiltered = (api) => api.name.toLowerCase().includes(this.filter) const isNotRequired = ({profile}) => !profile.required const sortByName = (a, b) => { const nameA = a.name.toUpperCase() const nameB = b.name.toUpperCase() return (nameA < nameB) ? -1 : (nameA > nameB) ? 1 : 0 } // Filter all active and inactive modules that are not required const { actives, inactives } = this.store.getAll() .filter(isFiltered) .filter(isNotRequired) .sort(sortByName) .reduce(({actives, inactives}, api) => { return this.store.actives.includes(api.name) ? { actives: [...actives, api.name], inactives } : { inactives: [...inactives, api.name], actives } }, { actives: [], inactives: [] }) const activeTile = actives.length !== 0 ? yo` <nav class="navbar navbar-expand-lg navbar-light bg-light justify-content-between align-items-center"> <span class="navbar-brand">Active Modules</span> <span class="badge badge-pill badge-primary">${actives.length}</span> </nav>` : '' const inactiveTile = inactives.length !== 0 ? yo` <nav class="navbar navbar-expand-lg navbar-light bg-light justify-content-between align-items-center"> <span class="navbar-brand">Inactive Modules</span> <span class="badge badge-pill badge-primary" style = "cursor: default;">${inactives.length}</span> </nav>` : '' const settings = new PluginManagerSettings().render() const rootView = yo` <div id='pluginManager'> <header class="form-group ${css.pluginSearch}"> <input onkeyup="${e => this.filterPlugins(e)}" class="form-control" placeholder="Search"> <button onclick="${_ => this.openLocalPlugin()}" class="btn btn-sm text-info ${css.localPluginBtn}"> Connect to a Local Plugin </button> </header> <section> ${activeTile} <div class="list-group list-group-flush"> ${actives.map(name => this.renderItem(name))} </div> ${inactiveTile} <div class="list-group list-group-flush"> ${inactives.map(name => this.renderItem(name))} </div> </section> ${settings} </div> ` if (!this.views.root) this.views.root = rootView return rootView } reRender () { if (this.views.root) { yo.update(this.views.root, this.render()) } } filterPlugins ({ target }) { this.filter = target.value.toLowerCase() this.reRender() } } module.exports = PluginManagerComponent