diff --git a/package-lock.json b/package-lock.json index 35e604c..4d1cd84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "ui-barebones", + "name": "ui", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/src/index.ts b/src/index.ts index b691602..6f598ec 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,16 +1,9 @@ -import { FarpatchWidget } from './interfaces'; -import { UartWidget } from './interface/uart'; -import { DashboardWidget } from './interface/dashboard'; - -var FarpatchWidgets: { [key: string]: FarpatchWidget } = { - "dashboard": new DashboardWidget(), - "uart": new UartWidget(), -}; +import { FarpatchWidget, farpatchWidgets } from './interfaces'; // TODO: Get the current widget from the address bar, if one exists -var currentWidgetName: string = "dashboard"; -var currentWidget: FarpatchWidget = FarpatchWidgets[currentWidgetName]; -var currentWidgetView: HTMLElement = document.getElementById(currentWidgetName + "-view") as HTMLElement; +var currentWidget: FarpatchWidget = farpatchWidgets[0]; +var widgetViews: HTMLElement[] = []; +// var currentWidgetView: HTMLElement = document.getElementById(currentWidgetName + "-view") as HTMLElement; function addToggleSidebarListener(element: HTMLElement) { // Hide or show the sidebar. MaterialUI calls this toggling between @@ -31,97 +24,88 @@ function addToggleSidebarListener(element: HTMLElement) { }, false); } -// On load, add a listener for mouse clicks on the navigation bar. -function setupNavItem(element: HTMLElement) { - // Get the base name of the element in order to set up the callback. - var elementBase = element.id.replace("-button", ""); - if (typeof (FarpatchWidgets[elementBase]) === 'undefined') { - console.log("No widget found for " + elementBase); - // return; - } else { - console.log("Setting up widget for " + elementBase); - var elementContent = document.getElementById(elementBase + "-view"); - if (typeof (elementContent) === 'undefined' || elementContent === null) { - console.log("No element found for " + elementBase + "-view"); - return; - } - FarpatchWidgets[elementBase].onInit(); +function deactivateWidget(widget: FarpatchWidget) { + widget.navItem.classList.remove("sidenav-item-active"); + widgetViews[widget.index].classList.remove("main-content-active"); + widget.onBlur(widgetViews[widget.index]); +} - if (FarpatchWidgets[elementBase] === currentWidget) { - element.classList.add("sidenav-item-active"); - FarpatchWidgets[elementBase].onFocus(elementContent); - currentWidgetView = elementContent; - elementContent.classList.add("main-content-active"); - } else { - console.log("Not initializing widget " + elementBase); - } +function activateWidget(widget: FarpatchWidget) { + widget.navItem.classList.add("sidenav-item-active"); + widgetViews[widget.index].classList.add("main-content-active"); + widget.onFocus(widgetViews[widget.index]); +} + +function switchToWidget(widget: FarpatchWidget) { + if (widget === currentWidget) { + return; } + deactivateWidget(currentWidget); + currentWidget = widget; + activateWidget(widget); +} - // Hook "click" for each navigation item. The listener will: - // 1. Loop through each nav item and remove the "active" class for - // each inactive item and add it to the newly-clicked item. - // 2. Unhide the correct view to activate it - element.addEventListener('click', function () { - var main = document.getElementsByTagName("main")[0]; - var navItems = document.querySelectorAll(".sidenav-item"); - var elementBase = element.id.replace("-button", ""); - if (typeof (FarpatchWidgets[elementBase]) !== 'undefined') { - if (FarpatchWidgets[elementBase] !== currentWidget) { - var newElementContent = document.getElementById(elementBase + "-view"); - if (typeof (newElementContent) === 'undefined' || newElementContent === null) { - console.log("No element found for " + elementBase + "-view"); - return; - } - - currentWidget.onBlur(currentWidgetView); - FarpatchWidgets[elementBase].onFocus(newElementContent); - currentWidget = FarpatchWidgets[elementBase]; - currentWidgetName = elementBase; - currentWidgetView = newElementContent; - } - } - - for (var i = 0; i < navItems.length; i++) { - var navItem = navItems[i]; - if (navItem === element) { - if (!navItem.classList.contains("sidenav-item-active")) { - navItem.classList.add("sidenav-item-active"); - } - } else { - navItem.classList.remove("sidenav-item-active"); - } - } - - var mainViews = document.querySelectorAll(".main-content-active"); - for (var i = 0; i < mainViews.length; i++) { - var mainView = mainViews[i]; - mainView.classList.remove("main-content-active"); - } - - var elementBase = element.id.replace("-button", ""); - var elementContent = document.getElementById(elementBase + "-view"); - if (typeof (elementContent) === 'undefined' || elementContent === null) { - console.log("No element found for " + elementBase + "-view"); - return; - } - elementContent.classList.add("main-content-active"); +// On load, add a listener for mouse clicks on the navigation bar. +function setupNavItem(widget: FarpatchWidget) { + var w = widget; + widget.navItem.addEventListener('click', function () { + switchToWidget(w); }, false); } document.addEventListener('DOMContentLoaded', function () { - var navItems = document.querySelectorAll(".sidenav-item"); - for (var i = 0; i < navItems.length; i++) { - // For the "toggle" button, don't add a listener. Instead, have it - // toggle the visibility of the sidebar. - if (navItems[i].classList.contains("sidenav-item-toggle")) { - addToggleSidebarListener(navItems[i] as HTMLElement); - } else { - setupNavItem(navItems[i] as HTMLElement); - } + // Populate the page + var body = document.getElementsByTagName("body")[0]; + var sidenav = document.createElement("nav"); + var mainView: HTMLElement = document.createElement("main"); + + sidenav.classList.add("sidenav"); + var sidenavList = document.createElement("ul"); + sidenavList.classList.add("sidenav-nav"); + + for (var i = 0; i < farpatchWidgets.length; i++) { + var widget = farpatchWidgets[i]; + widget.onInit(); + + var widgetView = document.createElement("div"); + widgetView.classList.add("main-content"); + widgetView.id = widget.name + "-view"; + widgetViews.push(widgetView); + + mainView.appendChild(widgetView); + sidenavList.appendChild(widget.navItem); + + setupNavItem(widget); } - // var navItems = document.querySelectorAll(".main-viewport"); - // for (var i = 0; i < navItems.length; i++) { - // addNavListener(navItems[i]); - // } + // Add the button to collapse the sidebar + var sidebarFiller = document.createElement("li"); + sidebarFiller.classList.add("sidenav-item-filler"); + sidenavList.appendChild(sidebarFiller); + var toggleSidebar = document.createElement("li"); + toggleSidebar.classList.add("sidenav-item"); + toggleSidebar.classList.add("sidenav-item-toggle"); + toggleSidebar.id = "rail-toggle-button"; + var toggleSidebarLink = document.createElement("a"); + toggleSidebarLink.classList.add("sidenav-link"); + var toggleSidebarIcon = document.createElement("span"); + toggleSidebarIcon.classList.add("las"); + toggleSidebarIcon.classList.add("la-3x"); + toggleSidebarIcon.classList.add("la-bars"); + toggleSidebarIcon.classList.add("icon"); + var toggleSidebarText = document.createElement("span"); + toggleSidebarText.classList.add("link-text"); + toggleSidebarText.innerText = "Hide Sidebar"; + toggleSidebarLink.appendChild(toggleSidebarIcon); + toggleSidebarLink.appendChild(toggleSidebarText); + toggleSidebar.appendChild(toggleSidebarLink); + sidenavList.appendChild(toggleSidebar); + addToggleSidebarListener(toggleSidebar); + + sidenav.appendChild(sidenavList); + body.appendChild(sidenav); + body.appendChild(mainView); + + currentWidget = farpatchWidgets[0]; + activateWidget(farpatchWidgets[0]); }, false); diff --git a/src/interface/dashboard.ts b/src/interface/dashboard.ts index 0b972fa..fc4ff58 100644 --- a/src/interface/dashboard.ts +++ b/src/interface/dashboard.ts @@ -1,10 +1,24 @@ -import { FarpatchWidget } from "../interfaces"; +import { FarpatchWidget, makeNavView as makeNavItem } from "../interfaces"; export class DashboardWidget implements FarpatchWidget { - view: Element = document.createElement("div"); + index: number = 0; + view: HTMLElement = document.createElement("div"); + navItem: HTMLElement; + name: string; + icon: string = "home"; + title: string = "Dashboard"; - onInit(): void { - this.view.innerHTML = ` + constructor(name: string) { + this.name = name; + this.navItem = makeNavItem(name, this.icon, this.title); + } + + updateIndex(index: number): void { + this.index = index; + } + + onInit(): void { + this.view.innerHTML = `
`; - console.log("Initialized Dashboard Widget"); - } - onFocus(element: HTMLElement): void { - console.log("Displaying Dashboard Widget"); - element.appendChild(this.view); - } - onBlur(element: HTMLElement): void { - console.log("Archiving Dashboard Widget"); - element.removeChild(this.view); - } + console.log("Initialized Dashboard Widget"); + } + onFocus(element: HTMLElement): void { + console.log("Displaying Dashboard Widget"); + element.appendChild(this.view); + } + onBlur(element: HTMLElement): void { + console.log("Archiving Dashboard Widget"); + element.removeChild(this.view); + } } diff --git a/src/interface/debug.ts b/src/interface/debug.ts new file mode 100644 index 0000000..d57ebfc --- /dev/null +++ b/src/interface/debug.ts @@ -0,0 +1,76 @@ +import { FarpatchWidget, makeNavView } from "../interfaces"; +import { Terminal } from '@xterm/xterm'; +import { FitAddon } from '@xterm/addon-fit'; +import { SerializeAddon } from '@xterm/addon-serialize'; + +export class DebugWidget implements FarpatchWidget { + name: string; + icon: string = "scroll"; + title: string = "Debug"; + index: number = 0; + + view: HTMLElement; + navItem: HTMLElement; + + terminal: Terminal; + fitAddon: FitAddon; + serializeAddon: SerializeAddon; + resizeFunction: () => void; + initialized: boolean = false; + constructor(name: string) { + this.name = name; + this.navItem = makeNavView(name, this.icon, this.title); + this.view = document.createElement("div"); + this.view.classList.add("terminal"); + this.terminal = new Terminal({ theme: { background: "#000000" } }); + this.fitAddon = new FitAddon(); + this.serializeAddon = new SerializeAddon(); + this.resizeFunction = this.resizeTerminal.bind(this); + } + + updateIndex(index: number): void { + this.index = index; + } + + onInit(): void { + console.log("Initialized Debug Widget"); + } + onFocus(element: HTMLElement): void { + console.log("Displaying Debug Widget"); + if (!this.initialized) { + // Ensure the parent frame doesn't get any scrollbars, since we're taking up the whole view + element.style.overflow = "hidden"; + console.log("Initializing xterm.js"); + // var terminalContainer = document.createElement("div"); + // this.view.appendChild(terminalContainer); + this.terminal.loadAddon(this.fitAddon); + this.terminal.loadAddon(this.serializeAddon); + this.terminal.onKey((e) => { + console.log("Key pressed: " + e.key); + this.terminal.write(e.key); + if (e.key === '\r') { + this.terminal.write('\n'); + } + }); + this.terminal.open(this.view); + this.terminal.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n'); + this.initialized = true; + } + element.appendChild(this.view); + window.addEventListener('resize', this.resizeFunction); + window.setTimeout(() => { + this.terminal.focus(); + this.resizeFunction(); + }, 10); + } + onBlur(element: HTMLElement): void { + console.log("Archiving UART Widget"); + element.removeChild(this.view); + window.removeEventListener('resize', this.resizeFunction); + } + + // Whenever the window is resized, update the size of the terminal + resizeTerminal() { + this.fitAddon.fit(); + } +} diff --git a/src/interface/rtt.ts b/src/interface/rtt.ts new file mode 100644 index 0000000..e961858 --- /dev/null +++ b/src/interface/rtt.ts @@ -0,0 +1,76 @@ +import { FarpatchWidget, makeNavView } from "../interfaces"; +import { Terminal } from '@xterm/xterm'; +import { FitAddon } from '@xterm/addon-fit'; +import { SerializeAddon } from '@xterm/addon-serialize'; + +export class RttWidget implements FarpatchWidget { + name: string; + icon: string = "microchip"; + title: string = "RTT"; + index: number = 0; + + view: HTMLElement; + navItem: HTMLElement; + + terminal: Terminal; + fitAddon: FitAddon; + serializeAddon: SerializeAddon; + resizeFunction: () => void; + initialized: boolean = false; + constructor(name: string) { + this.name = name; + this.navItem = makeNavView(name, this.icon, this.title); + this.view = document.createElement("div"); + this.view.classList.add("terminal"); + this.terminal = new Terminal({ theme: { background: "#000000" } }); + this.fitAddon = new FitAddon(); + this.serializeAddon = new SerializeAddon(); + this.resizeFunction = this.resizeTerminal.bind(this); + } + + updateIndex(index: number): void { + this.index = index; + } + + onInit(): void { + console.log("Initialized RTT Widget"); + } + onFocus(element: HTMLElement): void { + console.log("Displaying RTT Widget"); + if (!this.initialized) { + // Ensure the parent frame doesn't get any scrollbars, since we're taking up the whole view + element.style.overflow = "hidden"; + console.log("Initializing xterm.js"); + // var terminalContainer = document.createElement("div"); + // this.view.appendChild(terminalContainer); + this.terminal.loadAddon(this.fitAddon); + this.terminal.loadAddon(this.serializeAddon); + this.terminal.onKey((e) => { + console.log("Key pressed: " + e.key); + this.terminal.write(e.key); + if (e.key === '\r') { + this.terminal.write('\n'); + } + }); + this.terminal.open(this.view); + this.terminal.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n'); + this.initialized = true; + } + element.appendChild(this.view); + window.addEventListener('resize', this.resizeFunction); + window.setTimeout(() => { + this.terminal.focus(); + this.resizeFunction(); + }, 10); + } + onBlur(element: HTMLElement): void { + console.log("Archiving UART Widget"); + element.removeChild(this.view); + window.removeEventListener('resize', this.resizeFunction); + } + + // Whenever the window is resized, update the size of the terminal + resizeTerminal() { + this.fitAddon.fit(); + } +} diff --git a/src/interface/settings.ts b/src/interface/settings.ts new file mode 100644 index 0000000..a5a4c39 --- /dev/null +++ b/src/interface/settings.ts @@ -0,0 +1,57 @@ +import { FarpatchWidget, makeNavView as makeNavItem } from "../interfaces"; + +export class SettingsWidget implements FarpatchWidget { + index: number = 0; + view: HTMLElement = document.createElement("div"); + navItem: HTMLElement; + name: string; + icon: string = "sliders-h"; + title: string = "Settings"; + + constructor(name: string) { + this.name = name; + this.navItem = makeNavItem(name, this.icon, this.title); + } + + updateIndex(index: number): void { + this.index = index; + } + + onInit(): void { + this.view.innerHTML = ` +
+ +
+ + +
+
+ `; + console.log("Initialized Dashboard Widget"); + } + onFocus(element: HTMLElement): void { + console.log("Displaying Dashboard Widget"); + element.appendChild(this.view); + } + onBlur(element: HTMLElement): void { + console.log("Archiving Dashboard Widget"); + element.removeChild(this.view); + } +} diff --git a/src/interface/uart.ts b/src/interface/uart.ts index 0299652..5fceccc 100644 --- a/src/interface/uart.ts +++ b/src/interface/uart.ts @@ -1,16 +1,25 @@ -import { FarpatchWidget } from "../interfaces"; +import { FarpatchWidget, makeNavView } from "../interfaces"; import { Terminal } from '@xterm/xterm'; import { FitAddon } from '@xterm/addon-fit'; import { SerializeAddon } from '@xterm/addon-serialize'; export class UartWidget implements FarpatchWidget { + name: string; + icon: string = "keyboard"; + title: string = "UART"; + index: number = 0; + view: HTMLElement; + navItem: HTMLElement; + terminal: Terminal; fitAddon: FitAddon; serializeAddon: SerializeAddon; resizeFunction: () => void; initialized: boolean = false; - constructor() { + constructor(name: string) { + this.name = name; + this.navItem = makeNavView(name, this.icon, this.title); this.view = document.createElement("div"); this.view.classList.add("terminal"); this.terminal = new Terminal({ theme: { background: "#000000" } }); @@ -18,6 +27,11 @@ export class UartWidget implements FarpatchWidget { this.serializeAddon = new SerializeAddon(); this.resizeFunction = this.resizeTerminal.bind(this); } + + updateIndex(index: number): void { + this.index = index; + } + onInit(): void { console.log("Initialized UART Widget"); } diff --git a/src/interfaces.ts b/src/interfaces.ts index 3c0421c..bced5ed 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,5 +1,57 @@ + +import { UartWidget } from "./interface/uart"; +import { DashboardWidget } from "./interface/dashboard"; +import { RttWidget } from "./interface/rtt"; +import { DebugWidget } from "./interface/debug"; +import { SettingsWidget } from "./interface/settings"; + export interface FarpatchWidget { + index: number, + name: string, + view: HTMLElement, + navItem: HTMLElement, + icon: string, + title: string, + updateIndex(index: number): void, onInit(): void, onFocus(element: HTMLElement): void, onBlur(element: HTMLElement): void, } + +export function makeNavView(name: string, icon: string, title: string): HTMLElement { + var navView: HTMLElement = document.createElement("li"); + navView.classList.add("sidenav-item"); + navView.id = name + "-button"; + + var navViewLink = document.createElement("a"); + navViewLink.classList.add("sidenav-link"); + + var navViewIcon = document.createElement("span"); + navViewIcon.classList.add("las"); + navViewIcon.classList.add("la-3x"); + navViewIcon.classList.add("la-" + icon); + navViewIcon.classList.add("icon"); + + var navViewText = document.createElement("span"); + navViewText.classList.add("link-text"); + navViewText.innerText = title; + + navViewLink.appendChild(navViewIcon); + navViewLink.appendChild(navViewText); + + navView.appendChild(navViewLink); + + return navView; +} + +export const farpatchWidgets: FarpatchWidget[] = [ + new DashboardWidget("dashboard"), + new UartWidget("uart"), + new RttWidget("rtt"), + new DebugWidget("debug"), + new SettingsWidget("settings"), +]; + +for (var i = 0; i < farpatchWidgets.length; i++) { + farpatchWidgets[i].updateIndex(i); +} \ No newline at end of file diff --git a/static/index.html b/static/index.html index 75067d3..d3bd96a 100644 --- a/static/index.html +++ b/static/index.html @@ -13,7 +13,7 @@ -