finish refactoring of visual implementation of local plugin form

pull/1344/head
joseph izang 3 years ago
parent 8f06946872
commit a5cdacc741
  1. 146
      apps/remix-ide/src/app/components/plugin-manager-component.js
  2. 155
      libs/remix-ui/plugin-manager/src/lib/components/localPluginForm.tsx
  3. 3
      libs/remix-ui/plugin-manager/src/lib/components/pluginCard.tsx
  4. 84
      libs/remix-ui/plugin-manager/src/lib/components/rootView.tsx
  5. 23
      libs/remix-ui/plugin-manager/src/lib/remix-ui-plugin-manager.tsx
  6. 99
      libs/remix-ui/plugin-manager/src/types.d.ts

@ -102,7 +102,7 @@ class PluginManagerComponent extends ViewPlugin {
// root: null,
// items: {}
// }
// this.localPlugin = new LocalPlugin()
this.localPlugin = new LocalPlugin()
// this.filter = ''
// this.appManager.event.on('activate', () => { this.reRender() })
// this.appManager.event.on('deactivate', () => { this.reRender() })
@ -130,27 +130,12 @@ class PluginManagerComponent extends ViewPlugin {
}
renderComponent () {
// const props = this.reactProps()
ReactDOM.render(
// <RemixUiPluginManager
// activatePlugin={this.activateP}
// deActivatePlugin={this.deactivate}
// filter={this.filter}
// appManager={this.appManager}
// engine={this.engine}
// profile={this.profile}
// isActive={this.isActive}
// openLocalPlugin={this.openLocalPlugin}
// tileLabel
// _paq={_paq}
// {...props}
// />,
<RemixUiPluginManager
profile={profile}
appManager={this.appManager}
engine={this.engine}
activesCount={3}
inactivesCount={4}
localPlugin={this.localPlugin}
isActive={() => false}
actives={[]}
inactives={[]}
@ -172,81 +157,28 @@ class PluginManagerComponent extends ViewPlugin {
// _paq.push(['trackEvent', 'manager', 'deactivate', name])
// }
// renderItem (profile) {
// const displayName = (profile.displayName) ? profile.displayName : profile.name
// const doclink = profile.documentation ? yo`<a href="${profile.documentation}" class="px-1" title="link to documentation" target="_blank"><i aria-hidden="true" class="fas fa-book"></i></a>`
// : yo``
// // Check version of the plugin
// let versionWarning
// // Alpha
// if (profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)) {
// versionWarning = yo`<small title="Version Alpha" class="${css.versionWarning} plugin-version">alpha</small>`
// }
// // Beta
// if (profile.version && profile.version.match(/\b(\w*beta\w*)\b/g)) {
// versionWarning = yo`<small title="Version Beta" class="${css.versionWarning} plugin-version">beta</small>`
// }
// const activationButton = this.isActive(profile.name)
// ? yo`
// <button
// onclick="${() => this.deactivateP(profile.name)}"
// class="btn btn-secondary btn-sm" data-id="pluginManagerComponentDeactivateButton${profile.name}"
// >
// Deactivate
// </button>
// `
// : yo`
// <button
// onclick="${() => this.activateP(profile.name)}"
// class="btn btn-success btn-sm" data-id="pluginManagerComponentActivateButton${profile.name}"
// >
// Activate
// </button>`
// return yo`
// <article id="remixPluginManagerListItem_${profile.name}" class="list-group-item py-1 mb-1 plugins-list-group-item" title="${displayName}" >
// <div class="${css.row} justify-content-between align-items-center mb-2">
// <h6 class="${css.displayName} plugin-name">
// <div>
// ${displayName}
// ${doclink}
// ${versionWarning}
// </div>
// ${activationButton}
// </h6>
// </div>
// <div class="${css.description} d-flex text-body plugin-text mb-2">
// <img src="${profile.icon}" class="mr-1 mt-1 ${css.pluginIcon}" />
// <span class="${css.descriptiontext}">${profile.description}</span>
// </div>
// </article>
// `
// }
/***************
* SUB-COMPONENT
*/
/**
* Add a local plugin to the list of plugins
*/
// async openLocalPlugin () {
// try {
// const profile = await this.localPlugin.open(this.appManager.getAll())
// if (!profile) return
// if (this.appManager.getIds().includes(profile.name)) {
// throw new Error('This name has already been used')
// }
// const plugin = profile.type === 'iframe' ? new IframePlugin(profile) : new WebsocketPlugin(profile)
// this.engine.register(plugin)
// await this.appManager.activatePlugin(plugin.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}`)
// }
// }
async openLocalPlugin () {
try {
const profile = await this.localPlugin.open(this.appManager.getAll())
if (!profile) return
if (this.appManager.getIds().includes(profile.name)) {
throw new Error('This name has already been used')
}
const plugin = profile.type === 'iframe' ? new IframePlugin(profile) : new WebsocketPlugin(profile)
this.engine.register(plugin)
await this.appManager.activatePlugin(plugin.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}`)
}
}
// filterHelper () {
// const isFiltered = (profile) => (profile.displayName ? profile.displayName : profile.name).toLowerCase().includes(this.filter)
@ -280,48 +212,6 @@ class PluginManagerComponent extends ViewPlugin {
render () {
// Filtering helpers
// const activeTile = actives.length !== 0
// ? yo`
// <nav class="plugins-list-header justify-content-between navbar navbar-expand-lg bg-light navbar-light align-items-center">
// <span class="navbar-brand plugins-list-title">Active Modules</span>
// <span class="badge badge-primary" data-id="pluginManagerComponentActiveTilesCount">${actives.length}</span>
// </nav>
// `
// : ''
// const inactiveTile = inactives.length !== 0
// ? yo`
// <nav class="plugins-list-header justify-content-between navbar navbar-expand-lg bg-light navbar-light align-items-center">
// <span class="navbar-brand plugins-list-title h6 mb-0 mr-2">Inactive Modules</span>
// <span class="badge badge-primary" style = "cursor: default;" data-id="pluginManagerComponentInactiveTilesCount">${inactives.length}</span>
// </nav>
// `
// : ''
// const settings = new PluginManagerSettings().render()
// const rootView = yo`
// <div id='pluginManager' data-id="pluginManagerComponentPluginManager">
// <header class="form-group ${css.pluginSearch} plugins-header py-3 px-4 border-bottom" data-id="pluginManagerComponentPluginManagerHeader">
// <input onkeyup="${e => this.filterPlugins(e)}" class="${css.pluginSearchInput} form-control" placeholder="Search" data-id="pluginManagerComponentSearchInput">
// <button onclick="${_ => this.openLocalPlugin()}" class="${css.pluginSearchButton} btn bg-transparent text-dark border-0 mt-2 text-underline" data-id="pluginManagerComponentPluginSearchButton">
// Connect to a Local Plugin
// </button>
// </header>
// <section data-id="pluginManagerComponentPluginManagerSection">
// ${activeTile}
// <div class="list-group list-group-flush plugins-list-group" data-id="pluginManagerComponentActiveTile">
// ${actives.map(profile => this.renderItem(profile))}
// </div>
// ${inactiveTile}
// <div class="list-group list-group-flush plugins-list-group" data-id="pluginManagerComponentInactiveTile">
// ${inactives.map(profile => this.renderItem(profile))}
// </div>
// </section>
// ${settings}
// </div>
// `
// if (!this.views.root) this.views.root = rootView
return this.htmlElement
}

@ -0,0 +1,155 @@
import React, { FormEvent, MouseEvent, useState } from 'react'
import { FormStateProps } from '../../types'
interface RadioSelectionformState {
pluginType: string
inputLabel: string
radioLabel: string
radioChecked?: boolean
updateProfile: (key: string, e: MouseEvent) => void
}
interface LocalPluginFormProps {
formSubmitHandler: (event: FormEvent, formData: FormStateProps) => void
}
const initialState: FormStateProps = {
name: '',
displayName: '',
url: '',
type: 'iframe',
hash: '',
methods: '',
location: 'sidePanel'
}
function LocalPluginForm ({ formSubmitHandler }: LocalPluginFormProps) {
const [plugin, setPlugin] = useState(initialState)
// const [name, setName] = useState('')
// const [displayName, setDisplayName] = useState('')
// const [methods, setMethods] = useState('')
// const [url, setUrl] = useState('')
// const [type, setType] = useState()
// const [location, setLocation] = useState()
function pluginChangeHandler<P extends keyof FormStateProps> (formProps: P, value: FormStateProps[P]) {
setPlugin({ ...plugin, [formProps]: value })
}
// function handleSubmit (e) {
// console.log('Logging the form submit event', e)
// console.log('state of the plugin', plugin)
// }
// const onValueChange = (event: any) => {
// const value = event.target.type === 'radio' ? event.target.checked : event.target.value
// const name = event.target.name
// if (name === 'name') {
// setName(value)
// } else if (name === 'displayName') {
// setDisplayName(value)
// } else if (name === 'methods') {
// setMethods(value)
// } else if (name === 'url') {
// setUrl(value)
// } else if (name === 'type') {
// setType(value)
// } else if (name === 'location') {
// setLocation(value)
// }
// }
return (
<form id="local-plugin-form" onSubmit={(e) => formSubmitHandler(e, plugin)}>
<div className="form-group">
<label htmlFor="plugin-name">Plugin Name <small>(required)</small></label>
<input className="form-control" onChange={e => pluginChangeHandler('name', e.target.value)} value={plugin.name} id="plugin-name" data-id="localPluginName" placeholder="Should be camelCase"/>
</div>
<div className="form-group">
<label htmlFor="plugin-displayname">Display Name</label>
<input className="form-control" onChange={e => pluginChangeHandler('displayName', e.target.value)} value={plugin.displayName} id="plugin-displayname" data-id="localPluginDisplayName" placeholder="Name in the header"/>
</div>
<div className="form-group">
<label htmlFor="plugin-methods">Api (comma separated list of methods name)</label>
<input className="form-control" onChange={e => pluginChangeHandler('methods', e.target.value)} value={plugin.methods} id="plugin-methods" data-id="localPluginMethods" placeholder="Name in the header"/>
</div>
<div className="form-group">
<label htmlFor="plugin-url">Url <small>(required)</small></label>
<input className="form-control" onChange={e => pluginChangeHandler('url', e.target.value)} value={plugin.url} id="plugin-url" data-id="localPluginUrl" placeholder="ex: https://localhost:8000" />
</div>
<h6>Type of connection <small>(required)</small></h6>
<div className="form-check form-group">
<div className="radio">
<input
className="form-check-input"
type="radio"
name="type"
value="iframe"
id="iframe"
data-id='localPluginRadioButtoniframe'
checked={plugin.type === 'iframe'}
onChange={(e) => pluginChangeHandler('type', e.target.value)}
/>
<label className="form-check-label" htmlFor="iframe">Iframe</label>
</div>
<div className="radio">
<input
className="form-check-input"
type="radio"
name="type"
value="ws"
id="ws"
data-id='localPluginRadioButtonws'
checked={plugin.type === 'ws'}
onChange={(e) => pluginChangeHandler('type', e.target.value)}
/>
<label className="form-check-label" htmlFor="ws">Websocket</label>
</div>
</div>
<h6>Location in remix <small>(required)</small></h6>
<div className="form-check form-group">
<div className="radio">
<input
className="form-check-input"
type="radio"
name="location"
value="sidePanel"
id="sidePanel"
data-id='localPluginRadioButtonsidePanel'
checked={plugin.location === 'sidePanel'}
onChange={(e) => pluginChangeHandler('location', e.target.value)}
/>
<label className="form-check-label" htmlFor="sidePanel">Side Panel</label>
</div>
<div className="radio">
<input
className="form-check-input"
type="radio"
name="location"
value="mainPanel"
id="mainPanel"
data-id='localPluginRadioButtonmainPanel'
checked={plugin.location === 'mainPanel'}
onChange={(e) => pluginChangeHandler('location', e.target.value)}
/>
<label className="form-check-label" htmlFor="mainPanel">Main Panel</label>
</div>
<div className="radio">
<input
className="form-check-input"
type="radio"
name="location"
value="none"
id="none"
data-id='localPluginRadioButtonnone'
checked={plugin.location === 'none'}
onChange={(e) => pluginChangeHandler('location', e.target.value)}
/>
<label className="form-check-label" htmlFor="none">None</label>
</div>
</div>
</form>
)
}
export default LocalPluginForm

@ -1,9 +1,10 @@
import React, { useContext, useState } from 'react'
import { Profile } from '../../types'
import { PluginManagerContext } from '../contexts/pluginmanagercontext'
import '../remix-ui-plugin-manager.css'
import Button from './button'
interface PluginCardProps {
profile: any
profile: Partial<Profile>
}
// eslint-disable-next-line no-empty-pattern

@ -1,7 +1,10 @@
import React, { Fragment, useContext } from 'react'
import React, { FormEvent, Fragment, useContext, useState } from 'react'
import { PluginManagerContext } from '../contexts/pluginmanagercontext'
import ModuleHeading from './moduleHeading'
import PluginCard from './pluginCard'
import { ModalDialog } from '@remix-ui/modal-dialog'
import LocalPluginForm from './localPluginForm'
import { FormStateProps } from '../../types'
interface RootViewProps {
localPluginButtonText: string
@ -9,33 +12,62 @@ interface RootViewProps {
function RootView ({ localPluginButtonText }: RootViewProps) {
const { actives, inactives } = useContext(PluginManagerContext)
const [visible, setVisible] = useState<boolean>(true)
const openModal = () => {
setVisible(false)
}
const closeModal = () => setVisible(true)
const handleSubmit = (evt: FormEvent, formData: FormStateProps) => {
console.log('Data submitted from the form!!!: ', formData)
evt.preventDefault()
closeModal()
}
return (
<div id="pluginManager" data-id="pluginManagerComponentPluginManager">
<header className="form-group remixui_pluginSearch plugins-header py-3 px-4 border-bottom" data-id="pluginManagerComponentPluginManagerHeader">
<input type="text" className="form-control" placeholder="Search" data-id="pluginManagerComponentSearchInput" />
<button className="remixui_pluginSearchButton btn bg-transparent text-dark border-0 mt-2 text-underline" data-id="pluginManagerComponentPluginSearchButton">
<Fragment>
<ModalDialog
handleHide={closeModal}
hide={visible}
title="Local Plugin"
okLabel="OK"
cancelLabel="Cancel"
cancelFn={closeModal}
>
<LocalPluginForm
formSubmitHandler={handleSubmit}
/>
</ModalDialog>
<div id="pluginManager" data-id="pluginManagerComponentPluginManager">
<header className="form-group remixui_pluginSearch plugins-header py-3 px-4 border-bottom" data-id="pluginManagerComponentPluginManagerHeader">
<input type="text" className="form-control" placeholder="Search" data-id="pluginManagerComponentSearchInput" />
<button onClick={openModal} className="remixui_pluginSearchButton btn bg-transparent text-dark border-0 mt-2 text-underline" data-id="pluginManagerComponentPluginSearchButton">
Connect to a Local Plugin
</button>
</header>
<section data-id="pluginManagerComponentPluginManagerSection">
{actives.length === 0 ? (
<Fragment>
<ModuleHeading headingLabel="Active Modules"/>
{actives.map((profile) => (
<PluginCard profile={profile}/>
))}
</Fragment>
) : null }
{inactives.length === 0 ? (
<Fragment>
<ModuleHeading headingLabel="Inactive Modules"/>
{inactives.map((profile) => (
<PluginCard profile={profile}/>
))}
</Fragment>
) : null}
</section>
</div>
</button>
</header>
<section data-id="pluginManagerComponentPluginManagerSection">
{actives.length === 0 ? (
<Fragment>
<ModuleHeading headingLabel="Active Modules"/>
{actives.map((profile) => (
<PluginCard profile={profile}/>
))}
</Fragment>
) : null }
{inactives.length === 0 ? (
<Fragment>
<ModuleHeading headingLabel="Inactive Modules"/>
{inactives.map((profile) => (
<PluginCard profile={profile}/>
))}
</Fragment>
) : null}
</section>
</div>
</Fragment>
)
}

@ -7,12 +7,25 @@ import './remix-ui-plugin-manager.css'
export const RemixUiPluginManager = (props: RemixUiPluginManagerProps) => {
console.log('current state of appmanager', props.appManager)
console.log('The state of props ', props)
// openLocalPlugin () {
// try {
// const profile = await props.localPlugin.open(props.appManager.getAll())
// if (!profile) return
// if (props.appManager.getIds().includes(profile.name)) {
// throw new Error('This name has already been used')
// }
// const plugin = profile.type === 'iframe' ? new IframePlugin(profile) : new WebsocketPlugin(profile)
// props.engine.register(plugin)
// await props.appManager.activatePlugin(plugin.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}`)
// }
// }
return (
// <PluginManagerContextProvider props={props}>
// <RootView
// localPluginButtonText="Connect to a Local Plugin"
// />
// </PluginManagerContextProvider>
<PluginManagerContextProvider props={props}>
<RootView localPluginButtonText="Local Plugin"/>
</PluginManagerContextProvider>

@ -4,6 +4,45 @@ import { EventEmitter } from 'events'
import { Engine } from '@remixproject/engine/lib/engine'
/* eslint-disable camelcase */
// eslint-disable-next-line no-use-before-define
export = LocalPlugin;
declare class LocalPlugin {
/**
* Open a modal to create a local plugin
* @param {Profile[]} plugins The list of the plugins in the store
* @returns {Promise<{api: any, profile: any}>} A promise with the new plugin profile
*/
open(plugins: any[]): Promise<{
api: any;
profile: any;
}>;
profile: any;
/**
* Create the object to add to the plugin-list
*/
create(): any;
updateName({ target }: {
target: any;
}): void;
updateUrl({ target }: {
target: any;
}): void;
updateDisplayName({ target }: {
target: any;
}): void;
updateProfile(key: any, e: any): void;
updateMethods({ target }: {
target: any;
}): void;
/** The form to create a local plugin */
form(): any;
}
declare module 'yo-yo'{
interface yo_yo {
(strings:string[], ...values:any[]):HTMLElement;
@ -74,14 +113,13 @@ export interface PluginManagerContextProviderProps {
openLocalPlugin: () => Promise<void>
filterPlugins: () => void
profile: Profile
inactivesCount: number
activesCount: number
headingLabel: string
}
export interface RemixUiPluginManagerProps {
appManager: RemixAppManager
engine: RemixEngine
localPlugin: LocalPlugin
_paq: _Paq
filter: string
actives: Profile[]
@ -92,8 +130,6 @@ export interface RemixUiPluginManagerProps {
openLocalPlugin: () => Promise<void>
filterPlugins: () => void
profile: Profile
inactivesCount: number
activesCount: number
headingLabel: string
}
/** @class Reference loaders.
@ -108,4 +144,59 @@ declare class PluginLoader {
set(plugin: any, actives: any): void;
get(): any;
}
export type PluginManagerSettings = {
openDialog: () => void
onValidation: () => void
clearPermission: (from: any, to: any, method: any) => void
settings: () => HTMLElement
render: () => HTMLElement
}
export type LocalPluginType = {
'iframe',
'ws'
}
export type DefaultLocalPlugin = {
type: string
hash: string
methods: any
location: string
}
export interface FormStateProps extends DefaultLocalPlugin {
name: string
displayName: string
url: string
}
export type Profile = {
name: 'pluginManager',
displayName: 'Plugin manager',
methods: [],
events: [],
icon: 'assets/img/pluginManager.webp',
description: 'Start/stop services, modules and plugins',
kind: 'settings',
location: 'sidePanel',
documentation: 'https://remix-ide.readthedocs.io/en/latest/plugin_manager.html',
version: any
type: 'iframe' | 'ws'
hash: string
}
export type TileLabel = {
label: 'Active Module' | 'Inactive Modules'
}
export type LocalPlugin = {
create: () => Profile
updateName: (target: string) => void
updateDisplayName: (displayName: string) => void
updateProfile: (key: string, e: Event) => void
updateMethods: (target: any) => void
form: () => HTMLElement
}
export { }

Loading…
Cancel
Save