commit
f5b6bde34f
@ -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` |
||||
<div class=${css.pluginsContainer}> |
||||
${this.view} |
||||
</div>` |
||||
} |
||||
} |
@ -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(<RemixPluginPanel header={<></>} plugins={this.plugins}/>, this.el) |
||||
} |
||||
} |
@ -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` |
||||
<div class=${css.pluginsContainer} data-id="mainPanelPluginsContainer" id='mainPanelPluginsContainer-id'> |
||||
${this.view} |
||||
</div>` |
||||
} |
||||
} |
@ -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(<RemixPluginPanel header={<></>} plugins={this.plugins}/>, this.element) |
||||
} |
||||
} |
@ -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`<div id="plugins" class="${css.plugins}"></div>` |
||||
} |
||||
|
||||
/** |
||||
* 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` |
||||
<div class="d-flex justify-content-center align-items-center"> |
||||
<div class="spinner-border" role="status"> |
||||
<span class="sr-only">Loading...</span> |
||||
</div> |
||||
</div> |
||||
` : ''
|
||||
this.contents[name] = yo`<div class="${css.plugItIn}" >${view}${loading}</div>` |
||||
|
||||
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) |
||||
} |
||||
} |
@ -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<string, PluginRecord> = {} |
||||
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) |
||||
} |
||||
} |
@ -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`<header></header>` |
||||
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`<a href="${profile.documentation}" class="${css.titleInfo}" title="link to documentation" target="_blank"><i aria-hidden="true" class="fas fa-book"></i></a>` : '' |
||||
if (profile.version && profile.version.match(/\b(\w*alpha\w*)\b/g)) { |
||||
versionWarning = yo`<small title="Version Alpha" class="badge-light ${css.versionBadge}">alpha</small>` |
||||
} |
||||
// Beta
|
||||
if (profile.version && profile.version.match(/\b(\w*beta\w*)\b/g)) { |
||||
versionWarning = yo`<small title="Version Beta" class="badge-light ${css.versionBadge}">beta</small>` |
||||
} |
||||
} |
||||
|
||||
const header = yo` |
||||
<header class="${css.swapitHeader}"> |
||||
<h6 class="${css.swapitTitle}" data-id="sidePanelSwapitTitle">${name}</h6> |
||||
${docLink} |
||||
${versionWarning} |
||||
</header> |
||||
` |
||||
yo.update(this.header, header) |
||||
} |
||||
|
||||
render () { |
||||
return yo` |
||||
<section class="${css.panel} plugin-manager"> |
||||
${this.header} |
||||
<div class="${css.pluginsContainer}"> |
||||
${this.view} |
||||
</div> |
||||
</section>` |
||||
} |
||||
} |
@ -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(<RemixPluginPanel header={<RemixUIPanelHeader plugins={this.plugins}></RemixUIPanelHeader>} plugins={this.plugins}/>, this.sideelement) |
||||
} |
||||
} |
@ -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<void> { |
||||
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) |
||||
} |
||||
} |
@ -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` |
||||
<div class=${css.mainview}> |
||||
${self.tabProxy.renderTabsbar()} |
||||
${self._view.editor} |
||||
${self._view.mainPanel} |
||||
<div class="${css.contextview} contextview"></div> |
||||
${self._view.terminal} |
||||
</div> |
||||
` |
||||
|
||||
// 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) |
||||
} |
||||
} |
@ -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() |
||||
} |
||||
} |
@ -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`<div class=${css.ghostbar}></div>` |
||||
|
||||
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`<div onmousedown=${mousedown} class=${css.dragbar}></div>` |
||||
} |
||||
|
||||
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' |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
.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; |
||||
} |
||||
|
@ -0,0 +1,4 @@ |
||||
{ |
||||
"presets": ["@nrwl/react/babel"], |
||||
"plugins": [] |
||||
} |
@ -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": {} |
||||
} |
||||
] |
||||
} |
@ -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). |
@ -0,0 +1,2 @@ |
||||
export { default as RemixPluginPanel } from './lib/plugins/remix-ui-panel' |
||||
export { default as RemixUIMainPanel } from './lib/main/main-panel' |
@ -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; |
||||
} |
@ -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<any>; |
||||
setHideStatus: (hide: boolean) => void; |
||||
hidden: boolean |
||||
minHeight?: number |
||||
} |
||||
|
||||
const DragBar = (props: IRemixDragBarUi) => { |
||||
const [dragState, setDragState] = useState<boolean>(false) |
||||
const [dragBarPosY, setDragBarPosY] = useState<number>(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 <> |
||||
<div className={`overlay ${dragState ? '' : 'd-none'}`} ></div> |
||||
<Draggable nodeRef={nodeRef} position={{ x: 0, y: dragBarPosY }} onStart={startDrag} onStop={stopDrag} axis="y"> |
||||
<div ref={nodeRef} className={`dragbar_terminal ${dragState ? 'ondrag' : ''}`}></div> |
||||
</Draggable> |
||||
</> |
||||
} |
||||
|
||||
export default DragBar |
@ -0,0 +1,8 @@ |
||||
.mainview { |
||||
display : flex; |
||||
flex-direction : column; |
||||
height : 100%; |
||||
width : 100%; |
||||
position: relative; |
||||
} |
||||
|
@ -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<PluginRecord[]>([]) |
||||
const editorRef = useRef<HTMLDivElement>(null) |
||||
const mainPanelRef = useRef<HTMLDivElement>(null) |
||||
const tabsRef = useRef<HTMLDivElement>(null) |
||||
const terminalRef = useRef<HTMLDivElement>(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 ( |
||||
<div className="mainview"> |
||||
{Object.values(plugins).map((pluginRecord, i) => { |
||||
return ( |
||||
<React.Fragment key={`mainView${i}`}> |
||||
{(pluginRecord.profile.name === 'terminal') ? <DragBar key='dragbar-terminal' hidden={pluginRecord.minimized || false} setHideStatus={() => {}} refObject={terminalRef}></DragBar> : null} |
||||
<RemixUIPanelPlugin |
||||
ref={refs[i]} |
||||
key={pluginRecord.profile.name} |
||||
pluginRecord={pluginRecord} |
||||
/> |
||||
</React.Fragment> |
||||
) |
||||
})} |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default RemixUIMainPanel |
@ -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<string, PluginRecord>; |
||||
} |
||||
const RemixUIPanelHeader = (props: RemixPanelProps) => { |
||||
const [plugin, setPlugin] = useState<PluginRecord>() |
||||
|
||||
useEffect(() => { |
||||
if (props.plugins) { |
||||
const p = Object.values(props.plugins).find((pluginRecord) => { |
||||
return pluginRecord.active === true |
||||
}) |
||||
setPlugin(p) |
||||
} |
||||
}, [props]) |
||||
|
||||
return ( |
||||
<header className='swapitHeader'><h6 data-id='sidePanelSwapitTitle'>{plugin?.profile.displayName || plugin?.profile.name}</h6> |
||||
{plugin?.profile.documentation ? (<a href={plugin.profile.documentation} className="titleInfo" title="link to documentation" target="_blank" rel="noreferrer"><i aria-hidden="true" className="fas fa-book"></i></a>) : ''} |
||||
</header>) |
||||
} |
||||
|
||||
export default RemixUIPanelHeader |
@ -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<HTMLDivElement>(null) |
||||
const [view, setView] = useState<JSX.Element | HTMLDivElement>() |
||||
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 ( |
||||
<div |
||||
className={ |
||||
props.pluginRecord.active ? `${props.pluginRecord.class}` : 'd-none' |
||||
} |
||||
ref={panelRef || localRef} |
||||
> |
||||
{view} |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default forwardRef(RemixUIPanelPlugin) |
@ -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; |
||||
} |
@ -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<string, PluginRecord> |
||||
header: JSX.Element |
||||
} |
||||
|
||||
export function RemixPluginPanel (props: RemixPanelProps) { |
||||
return ( |
||||
<> |
||||
{props.header} |
||||
<div className="pluginsContainer"> |
||||
<div className='plugins' id='plugins'> |
||||
{Object.values(props.plugins).map((pluginRecord) => { |
||||
return <RemixUIPanelPlugin key={pluginRecord.profile.name} pluginRecord={pluginRecord} /> |
||||
})} |
||||
</div> |
||||
</div> |
||||
</> |
||||
|
||||
) |
||||
} |
||||
|
||||
export default RemixPluginPanel |
@ -0,0 +1,9 @@ |
||||
import { Profile } from '@remixproject/plugin-utils' |
||||
|
||||
export type PluginRecord = { |
||||
profile: Profile |
||||
view: any |
||||
active: boolean |
||||
class?: string |
||||
minimized?: boolean |
||||
} |
@ -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" |
||||
} |
||||
] |
||||
} |
@ -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"] |
||||
} |
@ -1,4 +1,4 @@ |
||||
{ |
||||
"presets": ["@nrwl/react/babel"], |
||||
"plugins": [] |
||||
} |
||||
} |
@ -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<HTMLElement>) => { |
||||
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 |
||||
} |
||||
} |
Loading…
Reference in new issue