diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..61f1e4ed0c1981310b6c80b02f95ca647d4a3f5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,114 @@ +# Database +*.db + +# System +.DS_Store + +# Editor Settings +.hintrc + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and *not* Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port diff --git a/src/main.js b/src/main.js index 7f5b3201539f3e769c579e8ebf96dfe56af79b64..67120ff7526ff5b879532dab859656ae8cdcabe7 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,202 @@ import * as helpers from "./modules/helpers.js"; -function init() {} +const navigation = { + label: "Menu", + path: "/menu", + command: "menu", + depth: 0, + submenu: { + call: { + label: "Call", + path: "/menu/call", + command: "call", + depth: 1, + submenu: { + emergency: { + label: "Emergency", + command: "emergency", + path: "/menu/call/emergency", + depth: 2, + message: "Calling Emergency", + }, + contact1: { + label: "Flute Inc.", + command: "contact A", + path: "/menu/call/contact1", + depth: 2, + message: "Calling Flute Inc.", + }, + contact2: { + label: "Brays AG", + command: "contact B", + path: "/menu/call/contact2", + depth: 2, + message: "Calling Brays AG", + }, + }, + }, + check: { + label: "Check", + path: "/menu/check", + command: "check", + depth: 1, + submenu: { + daily: { + label: "Daily", + command: "daily", + path: "/menu/check/daily", + depth: 2, + message: "Displaying Daily Checklist", + }, + weekly: { + label: "Weekly", + command: "weekly", + path: "/menu/check/weekly", + depth: 2, + message: "Displaying Weekly Checklist", + }, + monthly: { + label: "Monthly", + command: "monthly", + path: "/menu/check/monthly", + depth: 2, + message: "Displaying Monthly Checklist", + }, + }, + }, + apps: { + label: "Apps", + path: "/menu/apps", + command: "apps", + depth: 1, + submenu: { + record: { + label: "Record", + command: "record", + path: "/menu/apps/record", + depth: 2, + message: "Opening Recording App", + }, + breakout: { + label: "Breakout", + command: "breakout", + path: "/menu/apps/breakout", + depth: 2, + message: "Opening Breakout App", + }, + statistics: { + label: "Statistics", + command: "statistics", + path: "/menu/apps/statistics", + depth: 2, + message: "Opening Statistics App", + }, + }, + }, + }, +}; + +const backItem = { + label: "Back", + path: "../", + command: "back", +}; + +// init application +function init() { + // build navigation + const appEl = document.getElementById("app"); + const navigationEl = document.createElement("nav"); + navigationEl.id = "navigation"; + const listEl = document.createElement("ul"); + navigationEl.appendChild(listEl); + appEl.replaceChildren(navigationEl); + + resetNavigationEl(); +} + +function resetNavigationEl() { + const navigationEl = document.getElementById("navigation"); + const listEl = navigationEl.querySelector("ul"); + let itemEl = buildNavigationItemEl(navigation); + listEl.replaceChildren(itemEl); +} + +// build navigation item +function buildNavigationItemEl(item) { + const itemEl = document.createElement("li"); + const linkEl = document.createElement("a"); + linkEl.href = item.path; + linkEl.innerHTML = item.label; + linkEl.dataset.command = item.command; + itemEl.appendChild(linkEl); + + linkEl.addEventListener("click", onMouseClick); + + return itemEl; +} + +// on menu item mouseclick +function onMouseClick(event) { + event.preventDefault(); + const path = event.target.getAttribute("href"); + navigateTo(path); +} + +// navigate to page +function navigateTo(path) { + if (path === "/") resetNavigationEl(); + else { + let navObj = helpers.findNestedObject(navigation, "path", path); + + // is menu + if (navObj.hasOwnProperty("submenu")) updateNavigation(navObj); + else { + //is final page + let appEl = document.getElementById("app"); + let messageEl = document.createElement("div"); + messageEl.innerHTML = navObj.message; + appEl.replaceChildren(messageEl); + + let exitEl = document.createElement("a"); + exitEl.href = "/"; + exitEl.innerHTML = "Exit"; + appEl.appendChild(exitEl); + } + } +} + +// update Navigation +function updateNavigation(object) { + // update navigation + const listEl = document.createElement("ul"); + + // is navigation + for (let key in object.submenu) { + let itemEl = buildNavigationItemEl(object.submenu[key]); + listEl.appendChild(itemEl); + } + + // add "back" button + let backItemEl; + if (object.depth === 0) { + backItemEl = buildNavigationItemEl({ + label: "Close", + path: "/", + command: "close", + }); + } else if (object.depth === 1) { + backItemEl = buildNavigationItemEl({ + label: "Back", + path: "/menu", + command: "back", + }); + } + + listEl.appendChild(backItemEl); + + const navigationEl = document.getElementById("navigation"); + navigationEl.replaceChildren(listEl); +} init(); diff --git a/src/modules/helpers.js b/src/modules/helpers.js index 376653a2674d667d69adf3be84084e11f2443604..eed7762d1e040a5667a54825a318ae876f62740c 100644 --- a/src/modules/helpers.js +++ b/src/modules/helpers.js @@ -1,3 +1,28 @@ +// map value range export function map(num, in_min, in_max, out_min, out_max) { return ((num - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min; } + +// find nested object with key value pair +// https://stackoverflow.com/questions/45336281/javascript-find-by-value-deep-in-a-nested-object-array +export function findNestedObject(obj, key, value) { + // Base case + if (obj[key] === value) { + return obj; + } else { + var keys = Object.keys(obj); // add this line to iterate over the keys + + for (var i = 0, len = keys.length; i < len; i++) { + var k = keys[i]; // use this key for iteration, instead of index "i" + + // add "obj[k] &&" to ignore null values + if (obj[k] && typeof obj[k] == "object") { + var found = findNestedObject(obj[k], key, value); + if (found) { + // If the object was found in the recursive call, bubble it up. + return found; + } + } + } + } +} \ No newline at end of file diff --git a/src/styles/main.css b/src/styles/main.css index 7222b3eafcc970da2aab4bf4b8d99595c08a6b2a..8f77ff8ed8d761b9182bd5553d57a3c8eb5d9026 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -1,11 +1,19 @@ @import "config.css"; #app { + color: var(--color-white); + background: black; + height: 100vh; + font: 300 5vw/1.2em Roboto, sans-serif; display: grid; grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "main"; - height: 100vh; - background: black; + justify-items: center; + align-items: center; +} +a { + color: inherit; + text-decoration: none; } #log { position: absolute; diff --git a/src/styles/main.less b/src/styles/main.less index ec7065416073a7b90b5a1c1b4511b13c60fa61e2..9aaeb2f799c04c1416b372fffb95a9259bcf1fca 100644 --- a/src/styles/main.less +++ b/src/styles/main.less @@ -1,12 +1,21 @@ @import "config.css"; #app { + color: var(--color-white); + background: black; + height: 100vh; + font: 300 5vw/1.2em Roboto, sans-serif; display: grid; grid-template-columns: 1fr; grid-template-rows: 1fr; grid-template-areas: "main"; - height: 100vh; - background: black; + justify-items: center; + align-items: center; +} + +a { + color: inherit; + text-decoration: none; } #log {