From bd9ca29c34fb028be273fc4901940074d6cd12e7 Mon Sep 17 00:00:00 2001 From: Patrick Balsiger Date: Thu, 2 Nov 2017 22:22:38 +0000 Subject: [PATCH] ui --- README.md | 3 + data/index.html | 143 +++++----------- data/styles.css | 149 ++++++----------- frontend/pages/index.html | 143 +++++----------- frontend/scripts/core/App.js | 62 +++++++ frontend/scripts/core/Component.js | 45 ++++++ frontend/scripts/core/Mediator.js | 20 +++ frontend/scripts/core/data/DataBinding.js | 28 ++++ frontend/scripts/core/data/DataField.js | 23 +++ frontend/scripts/core/store/DataStore.js | 9 ++ frontend/scripts/core/store/RestStore.js | 37 +++++ frontend/scripts/core/store/StoreAction.js | 6 + frontend/styles/main.less | 180 ++++++++------------- frontend/styles/styles.old.css | 119 ++++++++++++++ package.json | 2 +- 15 files changed, 555 insertions(+), 414 deletions(-) create mode 100644 frontend/scripts/core/App.js create mode 100644 frontend/scripts/core/Component.js create mode 100644 frontend/scripts/core/Mediator.js create mode 100644 frontend/scripts/core/data/DataBinding.js create mode 100644 frontend/scripts/core/data/DataField.js create mode 100644 frontend/scripts/core/store/DataStore.js create mode 100644 frontend/scripts/core/store/RestStore.js create mode 100644 frontend/scripts/core/store/StoreAction.js create mode 100644 frontend/styles/styles.old.css diff --git a/README.md b/README.md index cbbf69b..5795fc5 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,9 @@ Frontend development requires NodeJS / npm. The development server watches all files in the frontend directory and automatically builds and copies the frontend code to the data folder. ``` +### install dependencies +npm install + ### run development server on port 8080 npm run dev diff --git a/data/index.html b/data/index.html index d76c8e6..90b0321 100644 --- a/data/index.html +++ b/data/index.html @@ -7,110 +7,51 @@ - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Laserspiro controller - - -
- -
- - - - - - - - -
-
+ +
+
+ + + + + + + + +
+
last uri:
last respone: -
+
-
-
-
-
+
+
+
+
+ Wifi Settings +
+
    +
  • + + +
  • +
  • + + +
  • +
  • +

    Age

    +
      + +
    +
  • +
  • + +
  • +
+
+
+ \ No newline at end of file diff --git a/data/styles.css b/data/styles.css index e4f896e..f62a1b5 100644 --- a/data/styles.css +++ b/data/styles.css @@ -1,108 +1,53 @@ -label { +body { + background: #000000; + color: #0eb8c0; + margin: 0; + padding: 0; +} +body ul { + padding: 0; +} +body ul li { + margin: 0 0 16px 0; +} +body p { + padding: 0; + margin: 0; +} +body .content { + padding: 32px; +} +body .content .heading { + font-size: 1.2em; + margin-bottom: 16px; + display: block; +} +body .content .container { + background: #333333; + padding: 16px; +} +body .content input { + height: 24px; +} +.settings .flex-outer li, +.settings .flex-inner { + display: flex; + flex-wrap: wrap; + align-items: center; +} +.settings .flex-outer > li > label, +.settings .flex-outer li p { + flex: 1 0; +} +.settings .flex-outer > li > label + *, +.settings .flex-inner { + flex: 1 0; +} +.spiro-control label { width: 10%; display: inline-block; } -.slider { +.spiro-control input[type="range"] { width: 80%; display: inline-block; } -body { - font-size: 14px; - font-family: "Bookman Old Style", "Serifa BT", "URW Bookman L", "itc bookman", times, serif; - background: #00979d none repeat scroll 0px 0px; - align: Center; -} -#Main { - width: 600px; - margin: 5px 3px 3px 12px; -} -text#titleGlitch { - fill: #006666; - font-weight: bold; - text-shadow: 2px 2px black; - font-family: sans-serif; - font-size: 36px; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -text#titleText { - fill: #006666; - text-shadow: 1px 1px black; - font-family: sans-serif; - font-size: 26px; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -svg { - border: solid 1px black; - width: 100%; - height: 50px; - display: block; - position: relative; - /*overflow: hidden; */ - background: #66bebe; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; -} -#prototypControls { - margin-top: 3px; - padding: 10px; - border: solid black 1px; - background: #66bebe; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; -} -.sectionTitle { - font-size: 28px; - font-weight: bold; - font-family: "impact", "Bookman Old Style", "Serifa BT", "URW Bookman L", "itc bookman", times, serif; - float: left; - clear: left; - color: #006666; - text-shadow: 2px 2px black; -} -.sectionDesc { - font-weight: bold; - float: left; - clear: left; -} -#ledMacroButtonsCont { - padding: 18px 5px 15px 5px; - text-decoration: none; - float: none; - clear: left; -} -.ledMacroButton { - height: 50px; - background: #00989d; - background-image: -webkit-linear-gradient(top, #00989d, #0a787a); - background-image: -moz-linear-gradient(top, #00989d, #0a787a); - background-image: -ms-linear-gradient(top, #00989d, #0a787a); - background-image: -o-linear-gradient(top, #00989d, #0a787a); - background-image: linear-gradient(to bottom, #00989d, #0a787a); - -webkit-border-radius: 17; - -moz-border-radius: 17; - border-radius: 17px; - text-shadow: 6px 4px 4px #4d424d; - -webkit-box-shadow: 0px 1px 3px #666666; - -moz-box-shadow: 0px 1px 3px #666666; - box-shadow: 0px 1px 3px #666666; - color: #fafafa; - font-size: 12px; - padding: 10px 15px 10px 15px; - border: solid #26b3b3 4px; - text-decoration: none; - margin: 23px 4px 23px 6px; - position: relative; -} -.ledMacroButton:hover { - background: #307070; - background-image: -webkit-linear-gradient(top, #307070, #66bebe); - background-image: -moz-linear-gradient(top, #307070, #66bebe); - background-image: -ms-linear-gradient(top, #307070, #66bebe); - background-image: -o-linear-gradient(top, #307070, #66bebe); - background-image: linear-gradient(to bottom, #307070, #66bebe); - text-decoration: underline; -} diff --git a/frontend/pages/index.html b/frontend/pages/index.html index d76c8e6..90b0321 100644 --- a/frontend/pages/index.html +++ b/frontend/pages/index.html @@ -7,110 +7,51 @@ - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Laserspiro controller - - -
- -
- - - - - - - - -
-
+ +
+
+ + + + + + + + +
+
last uri:
last respone: -
+
-
-
-
-
+
+
+
+
+ Wifi Settings +
+
    +
  • + + +
  • +
  • + + +
  • +
  • +

    Age

    +
      + +
    +
  • +
  • + +
  • +
+
+
+ \ No newline at end of file diff --git a/frontend/scripts/core/App.js b/frontend/scripts/core/App.js new file mode 100644 index 0000000..1c90052 --- /dev/null +++ b/frontend/scripts/core/App.js @@ -0,0 +1,62 @@ +import $ from 'jquery'; + +import * as Components from '../components/exports'; + + +export default class App { + + constructor() { + this.dataStores = []; + } + + withDataStore(dataStore) { + this.dataStores[dataStore.constructor.name] = dataStore; + return this; + } + + getStore(dataStore) { + return this.dataStores[dataStore]; + } + + run(ctx) { + this._loadComponents(ctx); + this._beforeInitComponents(); + this._initComponents(); + return this; + } + + _loadComponents(ctx) { + this.components = this.components || []; + for (let c in Components) { + let nodes = ctx ? ctx.find('.' + c) : []; + for (let i = 0; i < nodes.length; i++) { + let component = new Components[c](this, $(nodes[i])); + this.components.push(component); + }; + } + } + + _initComponents() { + this.components.forEach(this._initComponent); + } + + _beforeInitComponents() { + this.components.forEach(this._beforeInitComponent); + } + + _initComponent(component) { + component.init(); + } + + _beforeInitComponent(component) { + component.beforeInit(); + } + + render() { + for (let component in this.components) { + this.components[component].render(); + } + return this; + } + +} \ No newline at end of file diff --git a/frontend/scripts/core/Component.js b/frontend/scripts/core/Component.js new file mode 100644 index 0000000..67e5fdf --- /dev/null +++ b/frontend/scripts/core/Component.js @@ -0,0 +1,45 @@ +import Mustache from 'mustache'; +import $ from 'jquery'; +import DataField from './data/DataField'; + +export default class Component { + + constructor(ctx, node, withTemplate = true) { + this.ctx = ctx; + this.node = node; + this.component = this.constructor.name; + this.template = withTemplate ? $.get(this.component + '.html') : undefined; //document.querySelector('#' + this.component).import.body.textContent; + this.data = {}; + //console.log('init ' + this.component); + } + + beforeInit() { + this.subscribe(); + } + + init() {} + subscribe() {} + templateHelpers() { return {}; } + + bindData() { + let _this = this; + this.node.find('[data-bind]').each(function() { + var field = $(this); + _this.data[field.data('bind')] = new DataField(field); + }); + } + + render(data) { + if (data) data.helpers = this.templateHelpers(); + return this.template + .then((template) => { + return new Promise((fulfill, reject) => { + this.node.html(Mustache.render(template, data)); + this.bindData(); + //this.ctx._loadComponents(this.node); + fulfill(); + }); + }); + } + +} \ No newline at end of file diff --git a/frontend/scripts/core/Mediator.js b/frontend/scripts/core/Mediator.js new file mode 100644 index 0000000..dbc6559 --- /dev/null +++ b/frontend/scripts/core/Mediator.js @@ -0,0 +1,20 @@ +export default class Mediator { + + constructor() { + this.events = []; + } + + on(event, callback, context){ + this.events[event] = this.events[event] || []; + this.events[event].push(context ? callback.bind(context) : callback); + }; + + trigger(event, args){ + if(this.events[event]){ + for (var i = this.events[event].length - 1; i >= 0; i--) { + this.events[event][i](args || {}); + }; + } + }; + +} diff --git a/frontend/scripts/core/data/DataBinding.js b/frontend/scripts/core/data/DataBinding.js new file mode 100644 index 0000000..7821bce --- /dev/null +++ b/frontend/scripts/core/data/DataBinding.js @@ -0,0 +1,28 @@ +import $ from 'jquery'; + +export default class DataBinding { + + inputChange(node, model = {}) { + node.on('keyup', function() { + model.value = this.value; + }); + return model; + } + + inputHandler() { + return { + set: function(target, prop, newValue) { + if (prop == 'value' && target.id) { + target[prop] = newValue; + $('[data-bind="' + target.id + '"]').val(newValue); + return true; + } else return false; + + }, + get: function(target, name) { + return target[name]; + } + }; + } + +} \ No newline at end of file diff --git a/frontend/scripts/core/data/DataField.js b/frontend/scripts/core/data/DataField.js new file mode 100644 index 0000000..0e87f7f --- /dev/null +++ b/frontend/scripts/core/data/DataField.js @@ -0,0 +1,23 @@ +import DataBinding from './DataBinding'; + +export default class DataField { + + constructor(node, data) { + this.node = node; + this.data = { + id: this.node.data('bind') + }; + this.bind(); + } + bind() { + this.dataBinding = new DataBinding(); + this.dataBinding.inputChange(this.node, this.data); + this.proxy = new Proxy(this.data, this.dataBinding.inputHandler()); + } + get value() { + return this.proxy.value; + } + set value(newValue) { + this.proxy.value = newValue; + } +} \ No newline at end of file diff --git a/frontend/scripts/core/store/DataStore.js b/frontend/scripts/core/store/DataStore.js new file mode 100644 index 0000000..5adf513 --- /dev/null +++ b/frontend/scripts/core/store/DataStore.js @@ -0,0 +1,9 @@ +export default class DataStore { + constructor(mediator){ + this.mediator = mediator; + } + load(entry){} + save(entry){} + add(entry){} + delete(entry){} +} diff --git a/frontend/scripts/core/store/RestStore.js b/frontend/scripts/core/store/RestStore.js new file mode 100644 index 0000000..07d9e3d --- /dev/null +++ b/frontend/scripts/core/store/RestStore.js @@ -0,0 +1,37 @@ +import $ from 'jquery'; +import Mediator from '../Mediator'; +import DataStore from './DataStore'; +import StoreAction from './StoreAction'; + +export default class RestStore extends DataStore { + constructor(endpoint, mediator = new Mediator()){ + super(mediator); + this.endpoint = endpoint; + } + load(entry){ + return this.request(StoreAction.LOAD, 'GET', entry); + } + save(entry){ + return this.request(StoreAction.SAVE, 'POST', entry); + } + add(entry){ + return this.request(StoreAction.ADD, 'PUT', entry); + } + delete(entry){ + return this.request(StoreAction.DELETE, 'POST', entry); + } + request(event, type, payload){ + return $.ajax({ + url: this.endpoint, + type: type, + data: payload + }) + .then(JSON.parse) + .then((response) => { + this.mediator.trigger(event, response); + }); + } + on(event, subscriber, context){ + this.mediator.on(event, subscriber, context); + } +} diff --git a/frontend/scripts/core/store/StoreAction.js b/frontend/scripts/core/store/StoreAction.js new file mode 100644 index 0000000..b09a554 --- /dev/null +++ b/frontend/scripts/core/store/StoreAction.js @@ -0,0 +1,6 @@ +export default { + LOAD: 'load', + SAVE: 'save', + ADD: 'add', + DELETE: 'delete' +} diff --git a/frontend/styles/main.less b/frontend/styles/main.less index 8f7d17e..48c8b41 100644 --- a/frontend/styles/main.less +++ b/frontend/styles/main.less @@ -1,119 +1,81 @@ -label { - width: 10%; - display: inline-block; -} -.slider { - width: 80%; - display: inline-block; -} +@color-black: #000000; +@color-white: #eeeeee; +@color-main: #0eb8c0; +@color-container: #333333; +@default-padding: 16px; +@default-margin: 16px; +@input-height: 24px; body { - font-size: 14px; - font-family: "Bookman Old Style","Serifa BT","URW Bookman L","itc bookman",times,serif; - background: #00979d none repeat scroll 0px 0px; - align: Center; -} -#Main { - width: 600px; - margin: 5px 3px 3px 12px; + background: @color-black; + color: @color-main; + + margin: 0; + padding: 0; + + ul { + padding: 0; + li { + margin: 0 0 @default-margin 0; + } + } + + p { + padding: 0; + margin: 0; + } + + .content { + padding: @default-padding * 2; + .heading { + font-size: 1.2em; + margin-bottom: @default-margin; + display: block; + } + .container { + background: @color-container; + padding: @default-padding; + } + input { + height: @input-height; + } + } + .sidebar { + .item { + + } + } } -text#titleGlitch { - fill: #006666; - font-weight: bold; - text-shadow: 2px 2px black; - font-family: sans-serif; - font-size: 36px; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -text#titleText { - fill: #006666; - text-shadow: 1px 1px black; - font-family: sans-serif; - font-size: 26px; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -svg { - border: solid 1px black; - width: 100%; - height: 50px; - display: block; - position: relative; - /*overflow: hidden; */ - background: #66bebe; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; -} -#prototypControls { - margin-top:3px; - padding: 10px; - border: solid black 1px; - background: #66bebe; - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; -} -.sectionTitle { - font-size: 28px; - font-weight: bold; - font-family: "impact","Bookman Old Style","Serifa BT","URW Bookman L","itc bookman",times,serif; - float: left; - clear: left; - color: #006666; - text-shadow: 2px 2px black; -} -.sectionDesc { - font-weight: bold; - float: left; - clear: left; +.settings { + .flex-outer li, + .flex-inner { + display: flex; + flex-wrap: wrap; + align-items: center; + } + + .flex-outer > li > label, + .flex-outer li p { + flex: 1 0; + } + + .flex-outer > li > label + *, + .flex-inner { + flex: 1 0; + } } - -#ledMacroButtonsCont { - padding: 18px 5px 15px 5px; - text-decoration: none; - float:none; - clear:left; +.spiro-control { + label { + width: 10%; + display: inline-block; + } + input[type="range"] { + width: 80%; + display: inline-block; + } } - - -.ledMacroButton { - height: 50px; - background: #00989d; - background-image: -webkit-linear-gradient(top, #00989d, #0a787a); - background-image: -moz-linear-gradient(top, #00989d, #0a787a); - background-image: -ms-linear-gradient(top, #00989d, #0a787a); - background-image: -o-linear-gradient(top, #00989d, #0a787a); - background-image: linear-gradient(to bottom, #00989d, #0a787a); - -webkit-border-radius: 17; - -moz-border-radius: 17; - border-radius: 17px; - text-shadow: 6px 4px 4px #4d424d; - -webkit-box-shadow: 0px 1px 3px #666666; - -moz-box-shadow: 0px 1px 3px #666666; - box-shadow: 0px 1px 3px #666666; - color: #fafafa; - font-size:12px; - padding: 10px 15px 10px 15px; - border: solid #26b3b3 4px; - text-decoration: none; - margin: 23px 4px 23px 6px; - position: relative; -} - -.ledMacroButton:hover { - background: #307070; - background-image: -webkit-linear-gradient(top, #307070, #66bebe); - background-image: -moz-linear-gradient(top, #307070, #66bebe); - background-image: -ms-linear-gradient(top, #307070, #66bebe); - background-image: -o-linear-gradient(top, #307070, #66bebe); - background-image: linear-gradient(to bottom, #307070, #66bebe); - text-decoration: underline; -} - diff --git a/frontend/styles/styles.old.css b/frontend/styles/styles.old.css new file mode 100644 index 0000000..8f7d17e --- /dev/null +++ b/frontend/styles/styles.old.css @@ -0,0 +1,119 @@ + +label { + width: 10%; + display: inline-block; +} +.slider { + width: 80%; + display: inline-block; +} + + + +body { + font-size: 14px; + font-family: "Bookman Old Style","Serifa BT","URW Bookman L","itc bookman",times,serif; + background: #00979d none repeat scroll 0px 0px; + align: Center; +} +#Main { + width: 600px; + margin: 5px 3px 3px 12px; +} + +text#titleGlitch { + fill: #006666; + font-weight: bold; + text-shadow: 2px 2px black; + font-family: sans-serif; + font-size: 36px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +text#titleText { + fill: #006666; + text-shadow: 1px 1px black; + font-family: sans-serif; + font-size: 26px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +svg { + border: solid 1px black; + width: 100%; + height: 50px; + display: block; + position: relative; + /*overflow: hidden; */ + background: #66bebe; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; +} +#prototypControls { + margin-top:3px; + padding: 10px; + border: solid black 1px; + background: #66bebe; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; +} +.sectionTitle { + font-size: 28px; + font-weight: bold; + font-family: "impact","Bookman Old Style","Serifa BT","URW Bookman L","itc bookman",times,serif; + float: left; + clear: left; + color: #006666; + text-shadow: 2px 2px black; +} +.sectionDesc { + font-weight: bold; + float: left; + clear: left; +} + + +#ledMacroButtonsCont { + padding: 18px 5px 15px 5px; + text-decoration: none; + float:none; + clear:left; +} + + +.ledMacroButton { + height: 50px; + background: #00989d; + background-image: -webkit-linear-gradient(top, #00989d, #0a787a); + background-image: -moz-linear-gradient(top, #00989d, #0a787a); + background-image: -ms-linear-gradient(top, #00989d, #0a787a); + background-image: -o-linear-gradient(top, #00989d, #0a787a); + background-image: linear-gradient(to bottom, #00989d, #0a787a); + -webkit-border-radius: 17; + -moz-border-radius: 17; + border-radius: 17px; + text-shadow: 6px 4px 4px #4d424d; + -webkit-box-shadow: 0px 1px 3px #666666; + -moz-box-shadow: 0px 1px 3px #666666; + box-shadow: 0px 1px 3px #666666; + color: #fafafa; + font-size:12px; + padding: 10px 15px 10px 15px; + border: solid #26b3b3 4px; + text-decoration: none; + margin: 23px 4px 23px 6px; + position: relative; +} + +.ledMacroButton:hover { + background: #307070; + background-image: -webkit-linear-gradient(top, #307070, #66bebe); + background-image: -moz-linear-gradient(top, #307070, #66bebe); + background-image: -ms-linear-gradient(top, #307070, #66bebe); + background-image: -o-linear-gradient(top, #307070, #66bebe); + background-image: linear-gradient(to bottom, #307070, #66bebe); + text-decoration: underline; +} + diff --git a/package.json b/package.json index 79fa3e6..bcfb327 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "watch:css": "nodemon -q -w frontend/styles --ext \".\" --exec \"npm run build:css\"", "watch:js": "nodemon -q -w frontend/scripts --ext \".\" --exec \"npm run build:js\"", "watch:config": "nodemon -q -w config --ext \".\" --exec \"npm run cp:config\"", - "watch:livereload": "cd data && live-server", + "watch:livereload": "cd data && live-server --port=3000", "build:fs": "pio run -t buildfs", "upload:fs": "pio run -t uploadfs", "build:firmware": "pio run -t clean && pio run",