diff --git a/src/interface/dashboard.ts b/src/interface/dashboard.ts index fc4ff58..c01e0d1 100644 --- a/src/interface/dashboard.ts +++ b/src/interface/dashboard.ts @@ -1,16 +1,116 @@ import { FarpatchWidget, makeNavView as makeNavItem } from "../interfaces"; +class DashboardItem { + id: string; + name: string; + value: string; + constructor(id: string, name: string, value: string) { + this.id = id; + this.name = name; + this.value = value; + } + render(): HTMLElement { + var field: HTMLElement = document.createElement("div"); + field.classList.add("fieldset-item"); + + var navViewIcon = document.createElement("span"); + navViewIcon.setAttribute("aria-hidden", "true"); + navViewIcon.classList.add("las"); + navViewIcon.classList.add("la-3x"); + navViewIcon.classList.add("la-" + this.id); + navViewIcon.classList.add("icon"); + + var inputStack: HTMLElement = document.createElement("div"); + inputStack.classList.add("input-stack"); + + var label: HTMLElement = document.createElement("label"); + label.setAttribute("for", this.id); + label.setAttribute("id", this.id); + label.setAttribute("aria-hidden", "true"); + label.innerText = this.name; + + var input: HTMLElement = document.createElement("input"); + input.setAttribute("name", this.id); + input.setAttribute("aria-labelledby", this.id); + input.setAttribute("type", "range"); + input.setAttribute("value", this.value); + input.setAttribute("max", "10"); + input.setAttribute("style", "--track-fill: 30%"); + + inputStack.appendChild(label); + inputStack.appendChild(input); + field.appendChild(navViewIcon); + field.appendChild(inputStack); + + return field; + } +} + +class DashboardSection { + id: string; + name: string; + items: DashboardItem[]; + constructor(id: string, name: string, items: DashboardItem[]) { + this.id = id; + this.name = name; + this.items = items; + } + + render(): HTMLElement { + var root: HTMLElement = document.createElement("section"); + var header: HTMLElement = document.createElement("header"); + var h2: HTMLElement = document.createElement("h2"); + h2.innerText = this.name; + header.appendChild(h2); + root.appendChild(header); + + var fieldset: HTMLElement = document.createElement("fieldset"); + for (var i = 0; i < this.items.length; i++) { + fieldset.appendChild(this.items[i].render()); + } + + root.appendChild(fieldset); + + return root; + } +} + export class DashboardWidget implements FarpatchWidget { index: number = 0; - view: HTMLElement = document.createElement("div"); + view: HTMLElement = document.createElement("form"); navItem: HTMLElement; name: string; icon: string = "home"; title: string = "Dashboard"; + sections: DashboardSection[]; + constructor(name: string) { this.name = name; this.navItem = makeNavItem(name, this.icon, this.title); + + this.sections = [ + new DashboardSection("voltages", "Voltages", [ + new DashboardItem("system-voltage", "System", "3.3V"), + new DashboardItem("target-voltage", "Target", "1.8V"), + new DashboardItem("usb-voltage", "USB", "5.0V"), + new DashboardItem("extra-voltage", "Extra", "3.8V"), + ]), + new DashboardSection("network", "Network", [ + new DashboardItem("ip-address", "IP Address", "10.0.0.5"), + new DashboardItem("gdb-port", "GDB Port", "2022"), + new DashboardItem("uart-port", "UART Port", "2023"), + ]), + new DashboardSection("target", "Target", [ + new DashboardItem("detected-devices", "Detected Devices", "STM32F4"), + new DashboardItem("flash-size", "Flash Size", "512k"), + new DashboardItem("ram-size", "RAM Size", "32k"), + ]), + new DashboardSection("about", "About", [ + new DashboardItem("farpatch-version", "Farpatch Version", "5555239293"), + new DashboardItem("bmp-version", "Blackmagic Version", "1.10.0"), + ]), + ]; } updateIndex(index: number): void { @@ -18,32 +118,10 @@ export class DashboardWidget implements FarpatchWidget { } onInit(): void { - this.view.innerHTML = ` -
- -
- - -
-
- `; + for (var i = 0; i < this.sections.length; i++) { + this.view.appendChild(this.sections[i].render()); + } + console.log("Initialized Dashboard Widget"); } onFocus(element: HTMLElement): void { diff --git a/static/css/fancy.css b/static/css/fancy.css new file mode 100644 index 0000000..928b852 --- /dev/null +++ b/static/css/fancy.css @@ -0,0 +1,334 @@ +@custom-media --motionOK (prefers-reduced-motion: no-preference); + +:root { + --surface1: lch(10 0 0); + --surface2: lch(15 0 0); + --surface3: lch(20 0 0); + --surface4: lch(25 0 0); + + --text1: lch(95 0 0); + --text2: lch(75 0 0); + + --brand: lch(64 20 237); + --brand-bg1: lch(70 64 349); + --brand-bg2: lch(60 84 300); + + --brand-bg-gradient: linear-gradient( + to bottom, + var(--brand-bg1), + var(--brand-bg2) + ); + + --thumb-highlight-color: lch(100 0 0 / 20%); + + --space-xxs: .25rem; + --space-xs: .5rem; + --space-sm: 1rem; + --space-md: 1.5rem; + --space-lg: 2rem; + --space-xl: 3rem; + --space-xxl: 6rem; + + --isLTR: 1; + --isRTL: -1; + + &:dir(rtl) { + --isLTR: -1; + --isRTL: 1; + } + + @media (prefers-color-scheme: light) { + & { + --surface1: lch(90 0 0); + --surface2: lch(100 0 0); + --surface3: lch(98 0 0); + --surface4: lch(85 0 0); + + --text1: lch(20 0 0); + --text2: lch(40 0 0); + + --brand: lch(64 40 237); + --brand-bg1: lch(50 64 349); + --brand-bg2: lch(40 84 300); + + --thumb-highlight-color: lch(0 0 0 / 20%); + } + } +} + +form { + max-width: 89vw; + display: grid; + gap: var(--space-xl) var(--space-xxl); + --repeat: auto-fit; + grid-template-columns: + repeat(var(--repeat), minmax(min(10ch, 100%), 35ch)); + align-items: flex-start; + + @media (orientation: landscape) and (width >= 640px) { & { + --repeat: 2; + }} + } + + section { + display: grid; + gap: var(--space-md); + } + + header { + display: grid; + gap: var(--space-xxs); + } + + fieldset { + border: 1px solid var(--surface4); + background: var(--surface4); + padding: 0; + margin: 0; + display: grid; + gap: 1px; + border-radius: var(--space-sm); + overflow: hidden; + transition: box-shadow .3s ease; + + &:focus-within { + box-shadow: 0 5px 20px -10px hsl(0 0% 0% / 50%); + } + } + + input[type="range"] { + --track-height: .5ex; + --track-fill: 0%; + --thumb-size: 3ex; + --thumb-offset: -1.25ex; + --thumb-highlight-size: 0px; + + display: block; + inline-size: 100%; + margin: 1ex 0; + appearance: none; + background: transparent; + outline-offset: 5px; + + @media (hover: none) { + & { + --thumb-size: 30px; + --thumb-offset: -14px; + } + } + + &::-webkit-slider-runnable-track { + appearance: none; + block-size: var(--track-height); + border-radius: 5ex; + background: + linear-gradient( + to right, + transparent var(--track-fill), + var(--surface1) 0% + ), + var(--brand-bg-gradient) fixed; + } + + &::-moz-range-track { + appearance: none; + block-size: var(--track-height); + border-radius: 5ex; + background: + linear-gradient( + to right, + transparent var(--track-fill), + var(--surface1) 0% + ), + var(--brand-bg-gradient) fixed; + } + + &::-webkit-slider-thumb { + appearance: none; + cursor: ew-resize; + border: 3px solid var(--surface3); + block-size: var(--thumb-size); + inline-size: var(--thumb-size); + margin-block-start: var(--thumb-offset); + border-radius: 50%; + background: var(--brand-bg-gradient) fixed; + box-shadow: 0 0 0 var(--thumb-highlight-size) var(--thumb-highlight-color); + + @media (--motionOK) { + & { + transition: box-shadow .1s ease; + } + } + + @nest .fieldset-item:focus-within & { + border-color: var(--surface2); + } + } + + &::-moz-range-thumb { + appearance: none; + cursor: ew-resize; + border: 3px solid var(--surface3); + block-size: var(--thumb-size); + inline-size: var(--thumb-size); + margin-block-start: var(--thumb-offset); + border-radius: 50%; + background: var(--brand-bg-gradient) fixed; + box-shadow: 0 0 0 var(--thumb-highlight-size) var(--thumb-highlight-color); + + @media (--motionOK) { + & { + transition: box-shadow .1s ease; + } + } + + @nest .fieldset-item:focus-within & { + border-color: var(--surface2); + } + } + + &:is(:hover,:active) { + --thumb-highlight-size: 10px; + } + } + + input[type="checkbox"] { + inline-size: var(--space-sm); + block-size: var(--space-sm); + margin: 0; + outline-offset: 5px; + accent-color: var(--brand); + position: relative; + transform-style: preserve-3d; + cursor: pointer; + + &:hover::before { + --thumb-scale: 1; + } + + @media (hover: none) { + & { + inline-size: var(--space-md); + block-size: var(--space-md); + } + } + + &::before { + --thumb-scale: .01; + --thumb-highlight-size: var(--space-xl); + + content: ""; + inline-size: var(--thumb-highlight-size); + block-size: var(--thumb-highlight-size); + clip-path: circle(50%); + position: absolute; + inset-block-start: 50%; + inset-inline-start: 50%; + background: var(--thumb-highlight-color); + transform-origin: center center; + transform: + translateX(calc(var(--isRTL) * 50%)) + translateY(-50%) + translateZ(-1px) + scale(var(--thumb-scale)) + ; + will-change: transform; + + @media (--motionOK) { + & { + transition: transform .2s ease; + } + } + } + } + + .fieldset-item { + background: var(--surface3); + transition: background .2s ease; + + display: grid; + grid-template-columns: var(--space-lg) 1fr; + gap: var(--space-md); + + padding-block: var(--space-sm); + padding-inline: var(--space-md); + + @media (width >= 540px) { + grid-template-columns: var(--space-xxl) 1fr; + gap: var(--space-xs); + padding-block: var(--space-md); + padding-inline: 0 var(--space-xl); + } + + &:focus-within { + background: var(--surface2); + + & svg { + fill: white; + } + + & picture { + clip-path: circle(50%); + background: var(--brand-bg-gradient) fixed; + } + } + + & > :is(.input-stack, label) { + display: grid; + gap: var(--space-xs); + } + + & > .input-stack > label { + display: contents; + } + + & > picture { + block-size: var(--space-xl); + inline-size: var(--space-xl); + clip-path: circle(40%); + display: inline-grid; + place-content: center; + background: var(--surface3) fixed; + + @media (--motionOK) { + & { + transition: clip-path .3s ease; + } + } + } + + & svg { + fill: var(--text2); + block-size: var(--space-md); + } + + & > :is(picture, input[type="checkbox"]) { + place-self: center; + } + } + + small { + color: var(--text2); + line-height: 1.5; + } + + .github-corner { + color: var(--surface1); + fill: var(--surface3); + + &:hover .octo-arm { + animation: octocat-wave 560ms ease-in-out + } + } + + @keyframes octocat-wave{ + 0%,100% { + transform: rotate(0) + } + 20%,60% { + transform: rotate(-25deg) + } + 40%,80% { + transform: rotate(10deg) + } + } \ No newline at end of file diff --git a/static/index.html b/static/index.html index d3bd96a..ac759a4 100644 --- a/static/index.html +++ b/static/index.html @@ -7,75 +7,13 @@ + + Farpatch - - - - - + \ No newline at end of file