mirror of
https://gitlab.com/wirelos/sprocket-ui.git
synced 2025-12-16 22:36:43 +01:00
copy old frontend
This commit is contained in:
8
src/app/components/Clock.js
Normal file
8
src/app/components/Clock.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import $ from 'jquery';
|
||||
import Component from '../core/Component';
|
||||
|
||||
export default class Clock extends Component {
|
||||
constructor(ctx, node, template) {
|
||||
//super(ctx, node, template);
|
||||
}
|
||||
}
|
||||
40
src/app/components/LedStripPatternSwitch.js
Normal file
40
src/app/components/LedStripPatternSwitch.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import $ from 'jquery';
|
||||
import Switch from './base/Switch/Switch';
|
||||
import Store from '../core/store/RestStore';
|
||||
|
||||
export default class LedStripPatternSwitch extends Switch {
|
||||
|
||||
constructor(ctx, node) {
|
||||
super(ctx, node);
|
||||
this.store = new Store(this.config.endpoint);
|
||||
this.modes = ["init", "Color", "Pattern", "octoPrint"];
|
||||
this.patterns = [ "none", "Rainbow", "Scanner", "ColorWipe", "TheaterChase"];
|
||||
}
|
||||
|
||||
onClick(evt) {
|
||||
// FIXME separate mode from pattern
|
||||
let payload = {
|
||||
id: this.patterns.indexOf(this.config.name),
|
||||
pattern: this.config.name.substring(0,1),
|
||||
group: this.config.group,
|
||||
mode: 'P',
|
||||
state: this.state
|
||||
};
|
||||
this.ctx.mediator.trigger('/ledStripPreset/switched', payload);
|
||||
}
|
||||
|
||||
subscribe() {
|
||||
this.ctx.mediator.on('/ledStripPreset/switched', (payload) => {
|
||||
this.store.save(payload);
|
||||
if (payload.state
|
||||
&& payload.id !== this.patterns.indexOf(this.config.name)
|
||||
&& payload.group === this.config.group) {
|
||||
if (this.state) {
|
||||
this.state = false;
|
||||
this.node.find('input').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
27
src/app/components/ParamColor.js
Normal file
27
src/app/components/ParamColor.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import $ from 'jquery';
|
||||
import ColorPicker from './base/ColorPicker/ColorPicker';
|
||||
import Store from '../core/store/RestStore';
|
||||
|
||||
export default class ParamColor extends ColorPicker {
|
||||
|
||||
constructor(ctx, node) {
|
||||
super(ctx, node);
|
||||
//this.store = new Store(this.config.endpoint);
|
||||
this.ws = new WebSocket(this.config.endpoint.indexOf('/') > -1 ? "ws://" + window.location.host + this.config.endpoint : this.config.endpoint );
|
||||
this.ws.onopen = (event) => {
|
||||
console.log('open ' + this.config.endpoint);
|
||||
};
|
||||
this.ws.onmessage = (event) => {
|
||||
console.log(event.data);
|
||||
};
|
||||
}
|
||||
|
||||
onChange(evt) {
|
||||
/*this.store.save({
|
||||
rgb: parseInt(this.value.replace('#', '0x'))
|
||||
});*/
|
||||
let cmd = this.config.id + parseInt(this.value.replace('#', '0x'));
|
||||
this.ws.send(cmd);
|
||||
}
|
||||
|
||||
}
|
||||
19
src/app/components/ParamSlider.js
Normal file
19
src/app/components/ParamSlider.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import $ from 'jquery';
|
||||
import Slider from './base/Slider/Slider';
|
||||
import Store from '../core/store/RestStore';
|
||||
|
||||
export default class ParamSlider extends Slider {
|
||||
|
||||
constructor(ctx, node) {
|
||||
super(ctx, node);
|
||||
this.store = new Store(this.config.endpoint);
|
||||
}
|
||||
|
||||
onChange(evt) {
|
||||
this.store.save({
|
||||
param: this.config.name,
|
||||
value: this.value
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
19
src/app/components/ParamText.js
Normal file
19
src/app/components/ParamText.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import $ from 'jquery';
|
||||
import TextInput from './base/TextInput/TextInput';
|
||||
import Store from '../core/store/RestStore';
|
||||
|
||||
export default class ParamText extends TextInput {
|
||||
|
||||
constructor(ctx, node) {
|
||||
super(ctx, node);
|
||||
this.store = new Store(this.config.endpoint);
|
||||
}
|
||||
|
||||
onInput(evt) {
|
||||
let obj = {};
|
||||
obj[this.config.name] = this.value;
|
||||
//this.store.save(obj);
|
||||
console.log(this.value);
|
||||
}
|
||||
|
||||
}
|
||||
37
src/app/components/ParamWs.js
Normal file
37
src/app/components/ParamWs.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import $ from 'jquery';
|
||||
import TextInput from './base/TextInput/TextInput';
|
||||
import Store from '../core/store/RestStore';
|
||||
|
||||
export default class ParamWs extends TextInput {
|
||||
|
||||
constructor(ctx, node) {
|
||||
super(ctx, node);
|
||||
//this.store = new Store(this.config.endpoint);
|
||||
|
||||
this.ws = new WebSocket(this.config.endpoint.indexOf('/') > -1 ? "ws://" + window.location.host + this.config.endpoint : this.config.endpoint );
|
||||
this.ws.onopen = (event) => {
|
||||
console.log('open ' + this.config.endpoint);
|
||||
};
|
||||
this.ws.onmessage = (event) => {
|
||||
//console.log(event.data);
|
||||
this.node.find('input').val(event.data);
|
||||
};
|
||||
|
||||
this.ctx.mediator.on(this.config.endpoint, this.onMessage.bind(this));
|
||||
}
|
||||
|
||||
onMessage(msg) {
|
||||
//console.log('onMsg: ' + msg);
|
||||
//this.node.val(msg);
|
||||
}
|
||||
|
||||
onInput(evt) {
|
||||
let obj = {};
|
||||
obj[this.config.name] = this.value;
|
||||
//this.store.save(obj);
|
||||
console.log(this.value);
|
||||
this.ws.send(this.value);
|
||||
this.ctx.mediator.trigger(this.config.endpoint, this.value);
|
||||
}
|
||||
|
||||
}
|
||||
35
src/app/components/PresetSwitch.js
Normal file
35
src/app/components/PresetSwitch.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import $ from 'jquery';
|
||||
import Switch from './base/Switch/Switch';
|
||||
import Store from '../core/store/RestStore';
|
||||
|
||||
export default class PresetSwitch extends Switch {
|
||||
|
||||
constructor(ctx, node) {
|
||||
super(ctx, node);
|
||||
this.store = new Store(this.config.endpoint);
|
||||
}
|
||||
|
||||
onClick(evt) {
|
||||
let payload = {
|
||||
preset: this.config.name,
|
||||
group: this.config.group,
|
||||
state: this.state
|
||||
};
|
||||
this.store.save(payload);
|
||||
this.ctx.mediator.trigger('/preset/switched', payload);
|
||||
}
|
||||
|
||||
subscribe() {
|
||||
this.ctx.mediator.on('/preset/switched', (payload) => {
|
||||
if (payload.state
|
||||
&& payload.preset !== this.config.name
|
||||
&& payload.group === this.config.group) {
|
||||
if (this.state) {
|
||||
this.state = false;
|
||||
this.node.find('input').click();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
2
src/app/components/base/ColorPicker/ColorPicker.html
Normal file
2
src/app/components/base/ColorPicker/ColorPicker.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<label for="{{name}}">{{label}}</label>
|
||||
<input type="color" class="{{name}} ColorPicker" name="{{name}}">
|
||||
34
src/app/components/base/ColorPicker/ColorPicker.js
Normal file
34
src/app/components/base/ColorPicker/ColorPicker.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import $ from 'jquery';
|
||||
import Component from '../../../core/Component';
|
||||
import markup from './ColorPicker.html';
|
||||
|
||||
export default class ColorPicker extends Component {
|
||||
|
||||
constructor(ctx, node, template) {
|
||||
super(ctx, node, template || markup);
|
||||
this.render(this.config);
|
||||
}
|
||||
|
||||
onChange(evt) {}
|
||||
|
||||
inputChange(evt) {
|
||||
this.value = evt.target.value;
|
||||
this.onChange(evt);
|
||||
}
|
||||
|
||||
|
||||
subscribe() {
|
||||
// change to event input when ws is implemented
|
||||
this.node.delegate('input', 'input',
|
||||
this.inputChange.bind(this));
|
||||
}
|
||||
|
||||
/* delegate() {
|
||||
return [{
|
||||
selector: 'input',
|
||||
event: 'input',
|
||||
handler: this.inputChange.bind(this)
|
||||
}];
|
||||
} */
|
||||
|
||||
}
|
||||
2
src/app/components/base/Slider/Slider.html
Normal file
2
src/app/components/base/Slider/Slider.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<label>{{label}}</label>
|
||||
<input type="range" name="{{name}}" value="{{value}}" min="{{min}}" max="{{max}}">
|
||||
25
src/app/components/base/Slider/Slider.js
Normal file
25
src/app/components/base/Slider/Slider.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import $ from 'jquery';
|
||||
import Component from '../../../core/Component';
|
||||
import markup from './Slider.html';
|
||||
|
||||
export default class Slider extends Component {
|
||||
|
||||
constructor(ctx, node, template) {
|
||||
super(ctx, node, template || markup);
|
||||
this.render(this.config);
|
||||
}
|
||||
|
||||
onChange(evt) {}
|
||||
|
||||
sliderChange(evt) {
|
||||
this.value = evt.target.value;
|
||||
this.onChange(evt);
|
||||
}
|
||||
|
||||
|
||||
subscribe() {
|
||||
this.node.delegate('input', 'input',
|
||||
this.sliderChange.bind(this));
|
||||
}
|
||||
|
||||
}
|
||||
5
src/app/components/base/Switch/Switch.html
Normal file
5
src/app/components/base/Switch/Switch.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<label for="{{name}}">{{label}}</label>
|
||||
<label class="switch {{name}}" name="{{name}}">
|
||||
<input type="checkbox" name="{{name}}">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
30
src/app/components/base/Switch/Switch.js
Normal file
30
src/app/components/base/Switch/Switch.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import $ from 'jquery';
|
||||
import Component from '../../../core/Component';
|
||||
import markup from './Switch.html';
|
||||
|
||||
export default class Switch extends Component {
|
||||
|
||||
constructor(ctx, node, template) {
|
||||
super(ctx, node, template || markup);
|
||||
this.state = false;
|
||||
this.render(this.config);
|
||||
this._subscribe();
|
||||
}
|
||||
|
||||
onClick(evt) {}
|
||||
|
||||
switchState(evt) {
|
||||
this.state = !this.state;
|
||||
}
|
||||
|
||||
clickSwitch(evt) {
|
||||
this.switchState(evt);
|
||||
this.onClick(evt);
|
||||
}
|
||||
|
||||
_subscribe() {
|
||||
this.node.delegate('.slider', 'click',
|
||||
this.clickSwitch.bind(this));
|
||||
}
|
||||
|
||||
}
|
||||
2
src/app/components/base/TextInput/TextInput.html
Normal file
2
src/app/components/base/TextInput/TextInput.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<label for="{{name}}">{{label}}</label>
|
||||
<input type="text" class="{{name}}" name="{{name}}" placeholder="{{placeholder}}">
|
||||
25
src/app/components/base/TextInput/TextInput.js
Normal file
25
src/app/components/base/TextInput/TextInput.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import $ from 'jquery';
|
||||
import Component from '../../../core/Component';
|
||||
import markup from './TextInput.html';
|
||||
|
||||
export default class TextInput extends Component {
|
||||
|
||||
constructor(ctx, node, template) {
|
||||
super(ctx, node, template || markup);
|
||||
this.render(this.config);
|
||||
}
|
||||
|
||||
onChange(evt) {}
|
||||
|
||||
inputChange(evt) {
|
||||
this.value = evt.target.value;
|
||||
this.onInput(evt);
|
||||
}
|
||||
|
||||
|
||||
subscribe() {
|
||||
this.node.delegate('input', 'input',
|
||||
this.inputChange.bind(this));
|
||||
}
|
||||
|
||||
}
|
||||
10
src/app/components/exports.js
Normal file
10
src/app/components/exports.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export { default as Switch } from './base/Switch/Switch'
|
||||
export { default as Slider } from './base/Slider/Slider'
|
||||
export { default as TextInput } from './base/TextInput/TextInput'
|
||||
export { default as ColorPicker } from './base/ColorPicker/ColorPicker'
|
||||
export { default as PresetSwitch } from './PresetSwitch'
|
||||
export { default as ParamSlider } from './ParamSlider'
|
||||
export { default as ParamText } from './ParamText'
|
||||
export { default as ParamColor } from './ParamColor'
|
||||
export { default as ParamWs } from './ParamWs'
|
||||
export { default as LedStripPatternSwitch } from './LedStripPatternSwitch'
|
||||
82
src/app/core/App.js
Normal file
82
src/app/core/App.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import $ from 'jquery';
|
||||
import Mediator from './Mediator';
|
||||
|
||||
export default class App {
|
||||
|
||||
constructor(ctx) {
|
||||
this.ctx = ctx;
|
||||
this.dataStores = [];
|
||||
this.templates = [];
|
||||
this.mediator = new Mediator();
|
||||
this.ws = {};
|
||||
}
|
||||
|
||||
withDataStore(dataStore) {
|
||||
this.dataStores[dataStore.constructor.name] = dataStore;
|
||||
return this;
|
||||
}
|
||||
|
||||
components(components) {
|
||||
this.components = [];
|
||||
for (let c in components) {
|
||||
let nodes = this.ctx.find('.' + c);
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let component = new components[c](this, $(nodes[i]));
|
||||
this.components.push(component);
|
||||
};
|
||||
}
|
||||
this._beforeInitComponents();
|
||||
this._initComponents();
|
||||
return this;
|
||||
}
|
||||
|
||||
websocket(websocket) {
|
||||
this.ws = websocket;
|
||||
}
|
||||
|
||||
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.bind(this));
|
||||
}
|
||||
|
||||
_beforeInitComponents() {
|
||||
this.components.forEach(this._beforeInitComponent.bind(this));
|
||||
}
|
||||
|
||||
_initComponent(component) {
|
||||
component.init();
|
||||
}
|
||||
|
||||
_beforeInitComponent(component) {
|
||||
component.beforeInit();
|
||||
}
|
||||
|
||||
render() {
|
||||
for (let component in this.components) {
|
||||
this.components[component].render();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
40
src/app/core/Component.js
Normal file
40
src/app/core/Component.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import Mustache from 'mustache';
|
||||
import $ from 'jquery';
|
||||
import DataField from './data/DataField';
|
||||
|
||||
|
||||
export default class Component {
|
||||
|
||||
constructor(ctx, node, template) {
|
||||
this.ctx = ctx;
|
||||
this.node = node;
|
||||
this.component = this.constructor.name;
|
||||
this.config = this.node.data();
|
||||
this.data = {};
|
||||
this.markup = template;
|
||||
}
|
||||
|
||||
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();
|
||||
let rendered = Mustache.render(this.markup, data);
|
||||
this.node.html(rendered);
|
||||
this.bindData();
|
||||
}
|
||||
|
||||
}
|
||||
20
src/app/core/Mediator.js
Normal file
20
src/app/core/Mediator.js
Normal file
@@ -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 || {});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
30
src/app/core/WsMediator.js
Normal file
30
src/app/core/WsMediator.js
Normal file
@@ -0,0 +1,30 @@
|
||||
export default class WsMediator {
|
||||
|
||||
// FIXME mediator should manage all endpoints?
|
||||
constructor(endpoint) {
|
||||
this.events = [];
|
||||
this.endpoint = endpoint;
|
||||
this.ws = new WebSocket("ws://" + this.endpoint);
|
||||
this.ws.onopen = (event) => {
|
||||
console.log('init WsMediator');
|
||||
};
|
||||
this.ws.onmessage = (event) => {
|
||||
console.log(event.data);
|
||||
this.trigger(this.endpoint, event.data);
|
||||
};
|
||||
}
|
||||
|
||||
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.ws.send(this.events[event][i](args || {}));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
28
src/app/core/data/DataBinding.js
Normal file
28
src/app/core/data/DataBinding.js
Normal file
@@ -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];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
23
src/app/core/data/DataField.js
Normal file
23
src/app/core/data/DataField.js
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
9
src/app/core/store/DataStore.js
Normal file
9
src/app/core/store/DataStore.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export default class DataStore {
|
||||
constructor(mediator){
|
||||
this.mediator = mediator;
|
||||
}
|
||||
load(entry){}
|
||||
save(entry){}
|
||||
add(entry){}
|
||||
delete(entry){}
|
||||
}
|
||||
37
src/app/core/store/RestStore.js
Normal file
37
src/app/core/store/RestStore.js
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
6
src/app/core/store/StoreAction.js
Normal file
6
src/app/core/store/StoreAction.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
LOAD: 'load',
|
||||
SAVE: 'save',
|
||||
ADD: 'add',
|
||||
DELETE: 'delete'
|
||||
}
|
||||
10
src/app/jscolor.js
Normal file
10
src/app/jscolor.js
Normal file
File diff suppressed because one or more lines are too long
99
src/app/main.js
Normal file
99
src/app/main.js
Normal file
@@ -0,0 +1,99 @@
|
||||
"use strict";
|
||||
|
||||
import $ from 'jquery';
|
||||
import App from './core/App';
|
||||
import * as components from './components/exports';
|
||||
|
||||
$(() => {
|
||||
new App($('body'))
|
||||
.components(components);
|
||||
/* .websocket({
|
||||
led: new WebSocket('ws://ledstrip/')
|
||||
}); */
|
||||
});
|
||||
|
||||
|
||||
/* let click = element => element.click();
|
||||
let check = element => element.setAttribute('checked', 'checked');
|
||||
let uncheck = element => element.removeAttribute('checked');
|
||||
|
||||
let endpoint = "http://192.168.1.134";
|
||||
|
||||
let switchElementState = element => {
|
||||
let state = element.getAttribute('data-state') == 'false';
|
||||
element.setAttribute('data-state', state);
|
||||
return state;
|
||||
};
|
||||
let bindData = (element, attribute, data) => {
|
||||
element.setAttribute('data-' + attribute, data);
|
||||
};
|
||||
let leadingZero = (time) => {
|
||||
return (time < 10 ? "0" : "") + time;
|
||||
};
|
||||
let setMode = (mode) => {
|
||||
var formData = new FormData();
|
||||
formData.append("preset", mode);
|
||||
Sui.http.ajax({
|
||||
endpoint: endpoint + '/matrix/mode',
|
||||
method: 'POST',
|
||||
data: formData
|
||||
});
|
||||
};
|
||||
|
||||
let setScrollText = (txt) => {
|
||||
var formData = new FormData();
|
||||
formData.append("scrollText", txt);
|
||||
Sui.http.ajax({
|
||||
endpoint: endpoint + '/matrix/text',
|
||||
method: 'POST',
|
||||
data: formData
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Sui.ready(() => {
|
||||
let debugResponse = (data) => {
|
||||
document.querySelector('#response').innerHTML = data;
|
||||
};
|
||||
|
||||
Sui.http.ajax({
|
||||
endpoint: '/wifiConfig',
|
||||
method: 'GET'
|
||||
}, (data) => {
|
||||
Sui.select('[name="ssid"]').forEach((el) => {
|
||||
el.value = data;
|
||||
});
|
||||
});
|
||||
|
||||
// init collapsible containers
|
||||
Sui.select('.collapsible').forEach((container) => {
|
||||
container.querySelector('.heading').addEventListener('click', (item) => {
|
||||
container.classList.toggle('open');
|
||||
});
|
||||
});
|
||||
|
||||
let switchState = (evt) => { //evt.preventDefault();
|
||||
let state = !(evt.target.getAttribute('data-state') == 'true');
|
||||
evt.target.setAttribute('data-state', state);
|
||||
setMode(el.getAttribute('name'));
|
||||
};
|
||||
|
||||
let elClick = (el) => {
|
||||
el.addEventListener('click', switchState);
|
||||
return el;
|
||||
};
|
||||
Sui.select('.switch.demo').forEach(elClick);
|
||||
|
||||
setInterval(() => {
|
||||
let time = new Date();
|
||||
let txt = leadingZero(time.getHours()) + ':' + leadingZero(time.getMinutes());
|
||||
var formData = new FormData();
|
||||
formData.append("staticText", txt);
|
||||
Sui.http.ajax({
|
||||
endpoint: endpoint + '/matrix/text',
|
||||
method: 'POST',
|
||||
data: formData
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
}); */
|
||||
88
src/app/pmjq.js
Normal file
88
src/app/pmjq.js
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Poor Man's JQuery
|
||||
* Lightweight DOM manipulation utility
|
||||
*/
|
||||
var $ = function(selector){
|
||||
|
||||
var that = this;
|
||||
var element = selector.nodeType === 1 ? selector : {};
|
||||
|
||||
// utility functions
|
||||
this.util = {
|
||||
// build a fragment from a given html string
|
||||
buildFragment: function(html){
|
||||
// set the html to a temporary element
|
||||
var nodeHolder = document.createElement('div');
|
||||
nodeHolder.innerHTML = html;
|
||||
// create a document fragment and append all input nodes
|
||||
var fragment = document.createDocumentFragment();
|
||||
while(nodeHolder.firstChild){
|
||||
fragment.appendChild(nodeHolder.firstChild);
|
||||
}
|
||||
return fragment;
|
||||
}
|
||||
};
|
||||
// check if the input selector is already an element or a css selector
|
||||
if(selector.nodeType === 1){ // is element
|
||||
if(selector.is$ ? selector.is$() : false){ // check if the element is already extended
|
||||
return selector;
|
||||
}
|
||||
element = selector; // set the element to be extended
|
||||
} else { // the element is in fact a css selector
|
||||
element = document.querySelector(selector); // search for the element
|
||||
}
|
||||
|
||||
// overload the innerHTML attribute
|
||||
element.html = function(val){
|
||||
this.innerHTML = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
// overload the value attribute
|
||||
element.val = function(val){
|
||||
this.value = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
// append the given string as child fragment
|
||||
element.append = function(html){
|
||||
var fragment = that.util.buildFragment(html);
|
||||
this.appendChild(fragment);
|
||||
return this;
|
||||
};
|
||||
|
||||
// prepend the given string as child fragment
|
||||
element.prepend = function(html){
|
||||
var fragment = that.util.buildFragment(html);
|
||||
this.insertBefore(fragment,this.firstChild);
|
||||
return this;
|
||||
};
|
||||
|
||||
// search for an element inside of an element
|
||||
element.find = function(what){
|
||||
var found = $(what);
|
||||
if(found){
|
||||
return $(found);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get parent of the current element
|
||||
element.parent = function(){
|
||||
if(this.parentElement){
|
||||
return $(this.parentElement);
|
||||
}
|
||||
}
|
||||
|
||||
// indicates that this element is a $ function
|
||||
element.is$ = function(){
|
||||
return true;
|
||||
}
|
||||
|
||||
element.on = function(event, func, useCapture) {
|
||||
this.addEventListener(event, func, useCapture);
|
||||
return this;
|
||||
}
|
||||
|
||||
return element;
|
||||
};
|
||||
73
src/app/sui.js
Normal file
73
src/app/sui.js
Normal file
@@ -0,0 +1,73 @@
|
||||
var Sui = {
|
||||
ready: (callback) => {
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
callback();
|
||||
}, false);
|
||||
},
|
||||
select: (selector) => {
|
||||
return document.querySelectorAll(selector);
|
||||
},
|
||||
link: (node) => {
|
||||
return (actuator) => {
|
||||
let update = actuator.handler || function(actuator) {
|
||||
Sui.http.ajax({
|
||||
method: actuator.method,
|
||||
endpoint: actuator.api,
|
||||
data: actuator.data ?
|
||||
actuator.data.call(this) : [this.value],
|
||||
cache: false
|
||||
}, actuator.onResponse || null);
|
||||
};
|
||||
Sui.select(actuator.selector).forEach( (domEl) =>{
|
||||
let handle = function(event) {
|
||||
update.call(this, actuator);
|
||||
}
|
||||
domEl.addEventListener(actuator.event, handle)
|
||||
});
|
||||
};
|
||||
},
|
||||
util: {
|
||||
|
||||
/**
|
||||
* serialize a flat json object
|
||||
*/
|
||||
serialize: (obj) => {
|
||||
var str = [];
|
||||
for(var p in obj){
|
||||
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
|
||||
}
|
||||
return str.join("&");
|
||||
}
|
||||
},
|
||||
http: {
|
||||
|
||||
/**
|
||||
* ajax request
|
||||
* {
|
||||
* method: <'GET', 'POST', whatever >,
|
||||
* endpoint: <URL to the service endpoint>,
|
||||
* async: <true or false>, (default true)
|
||||
* data: <json object with data to transmit>,
|
||||
* cache: <true or false> (default false)
|
||||
* }
|
||||
*/
|
||||
ajax: (config, callback) => {
|
||||
var cache = config.cache || false;
|
||||
var data = config.data || {};
|
||||
if(!cache) {
|
||||
data['_'] = new Date().getTime();
|
||||
}
|
||||
var serializedData = data; //Sui.util.serialize(data);
|
||||
var endPointUrl = (config.method === 'GET' || config.method === 'DELETE') && data ? config.endpoint+'?'+serializedData : config.endpoint;
|
||||
var postData = config.method === 'POST' || config.method === 'PUT' ? serializedData : null;
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open(config.method, endPointUrl, config.async || true);
|
||||
request.onreadystatechange = function () {
|
||||
// TODO handle response properly
|
||||
callback ? callback(request.responseText, request.status) : undefined;
|
||||
};
|
||||
request.send(postData);
|
||||
}
|
||||
}
|
||||
};
|
||||
BIN
src/pages/favicon-32x32.png
Normal file
BIN
src/pages/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
BIN
src/pages/favicon.ico
Normal file
BIN
src/pages/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
100
src/pages/index.html
Normal file
100
src/pages/index.html
Normal file
@@ -0,0 +1,100 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>ESP Kit</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-32x32.png">
|
||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||
<script src="script.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="sui">
|
||||
<div class="content">
|
||||
<!-- <form class="param-control container open collapsible">
|
||||
<span class="heading">Debug</span>
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li class="form-row ParamWs"
|
||||
data-name="scrollText"
|
||||
data-label="Audio"
|
||||
data-placeholder="some scroll text"
|
||||
data-endpoint="audiosprocket.lan/audio">
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
</div>
|
||||
</form> -->
|
||||
<form class="param-control container collapsible open" action="#" method="POST">
|
||||
<span class="heading">Strip</span>
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li class="form-row ParamColor"
|
||||
data-id="C"
|
||||
data-name="color"
|
||||
data-label="Color"
|
||||
data-endpoint="/patterns">
|
||||
</li>
|
||||
<li class="form-row ParamColor"
|
||||
data-id="D"
|
||||
data-name="color2"
|
||||
data-label="Color 2"
|
||||
data-endpoint="/patterns">
|
||||
</li>
|
||||
<li class="form-row LedStripPatternSwitch"
|
||||
data-group="stripPattern"
|
||||
data-name="Rainbow"
|
||||
data-label="Rainbow"
|
||||
data-endpoint="/strip/pattern">
|
||||
</li>
|
||||
<li class="form-row LedStripPatternSwitch"
|
||||
data-group="stripPattern"
|
||||
data-name="Scanner"
|
||||
data-label="Scanner"
|
||||
data-endpoint="/strip/pattern">
|
||||
</li>
|
||||
<li class="form-row LedStripPatternSwitch"
|
||||
data-group="stripPattern"
|
||||
data-name="TheaterChase"
|
||||
data-label="Theater Chase"
|
||||
data-endpoint="/strip/pattern">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
<div class="settings container collapsible open">
|
||||
<span class="heading">WiFi Settings</span>
|
||||
<div class="content">
|
||||
<form action="/wifiConfig" method="POST">
|
||||
<!-- <li class="form-row">
|
||||
<label for="ap">AP Mode</label>
|
||||
<label class="switch ap-mode">
|
||||
<input type="checkbox" name="apMode">
|
||||
<span class="slider round" data-bind="apMode" data-state="false"></span>
|
||||
</label>
|
||||
</li -->
|
||||
<li class="form-row">
|
||||
<label for="ssid">SSID</label>
|
||||
<input type="text" name="ssid" placeholder="Default AP: Th1ngs4P">
|
||||
</li>
|
||||
<li class="form-row">
|
||||
<label for="password">PW</label>
|
||||
<input type="password" name="password" placeholder="Default: th3r31sn0sp00n">
|
||||
</li>
|
||||
<li class="form-row">
|
||||
<label for="hostName">Hostname</label>
|
||||
<input type="text" name="hostName" placeholder="Default: 192.168.1.143">
|
||||
</li>
|
||||
<li class="form-row">
|
||||
<button type="submit">Save</button>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
93
src/pages/matrix.html
Normal file
93
src/pages/matrix.html
Normal file
@@ -0,0 +1,93 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>ESP Kit</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-32x32.png">
|
||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||
<script src="script.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="sui">
|
||||
<div class="content">
|
||||
<form class="param-control container open collapsible">
|
||||
<span class="heading">Debug</span>
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li class="form-row ParamWs"
|
||||
data-name="scrollText"
|
||||
data-label="Audio"
|
||||
data-placeholder="some scroll text"
|
||||
data-endpoint="audiosprocket.lan/audio">
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
</div>
|
||||
</form>
|
||||
<form class="param-control container collapsible open" action="#" method="POST">
|
||||
<span class="heading">Strip</span>
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li class="form-row ParamColor"
|
||||
data-name="color"
|
||||
data-label="Color"
|
||||
data-endpoint="/patterns">
|
||||
</li>
|
||||
<li class="form-row LedStripPatternSwitch"
|
||||
data-group="stripPattern"
|
||||
data-name="Rainbow"
|
||||
data-label="Rainbow"
|
||||
data-endpoint="/strip/pattern">
|
||||
</li>
|
||||
<li class="form-row LedStripPatternSwitch"
|
||||
data-group="stripPattern"
|
||||
data-name="Scanner"
|
||||
data-label="Scanner"
|
||||
data-endpoint="/strip/pattern">
|
||||
</li>
|
||||
<li class="form-row LedStripPatternSwitch"
|
||||
data-group="stripPattern"
|
||||
data-name="TheaterChase"
|
||||
data-label="Theater Chase"
|
||||
data-endpoint="/strip/pattern">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
<div class="settings container collapsible open">
|
||||
<span class="heading">WiFi Settings</span>
|
||||
<div class="content">
|
||||
<form action="/wifiConfig" method="POST">
|
||||
<!-- <li class="form-row">
|
||||
<label for="ap">AP Mode</label>
|
||||
<label class="switch ap-mode">
|
||||
<input type="checkbox" name="apMode">
|
||||
<span class="slider round" data-bind="apMode" data-state="false"></span>
|
||||
</label>
|
||||
</li -->
|
||||
<li class="form-row">
|
||||
<label for="ssid">SSID</label>
|
||||
<input type="text" name="ssid" placeholder="Default AP: Th1ngs4P">
|
||||
</li>
|
||||
<li class="form-row">
|
||||
<label for="password">PW</label>
|
||||
<input type="password" name="password" placeholder="Default: th3r31sn0sp00n">
|
||||
</li>
|
||||
<li class="form-row">
|
||||
<label for="hostName">Hostname</label>
|
||||
<input type="text" name="hostName" placeholder="Default: 192.168.1.143">
|
||||
</li>
|
||||
<li class="form-row">
|
||||
<button type="submit">Save</button>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
41
src/styles/base.less
Normal file
41
src/styles/base.less
Normal file
@@ -0,0 +1,41 @@
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.shown {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.sui {
|
||||
background: @color-black;
|
||||
color: @color-main-light;
|
||||
font-family: @default-font;
|
||||
font-size: @default-font-size;
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
> .content {
|
||||
padding: @default-padding;
|
||||
}
|
||||
|
||||
label {
|
||||
color: @color-grey;
|
||||
}
|
||||
|
||||
button {
|
||||
background: @color-main;
|
||||
color: @color-white;
|
||||
font-size: 0.9em;
|
||||
border: 0;
|
||||
padding: .8em;
|
||||
margin: 0 .4em;
|
||||
}
|
||||
}
|
||||
26
src/styles/container.less
Normal file
26
src/styles/container.less
Normal file
@@ -0,0 +1,26 @@
|
||||
.container {
|
||||
background: @color-container;
|
||||
padding: @default-padding;
|
||||
border-radius: @default-border-radius;
|
||||
border: solid 1px @default-border-color;
|
||||
margin-bottom: @default-margin-small;
|
||||
&.collapsible {
|
||||
> .heading {
|
||||
margin-bottom: 0;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
> .content {
|
||||
.hidden;
|
||||
}
|
||||
&.open {
|
||||
> .heading {
|
||||
margin-bottom: @default-margin;
|
||||
}
|
||||
> .content {
|
||||
.shown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/styles/form/color.less
Normal file
7
src/styles/form/color.less
Normal file
@@ -0,0 +1,7 @@
|
||||
.ColorPicker {
|
||||
flex: none !important;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
height: 50px;
|
||||
width: 75px;
|
||||
}
|
||||
24
src/styles/form/input.less
Normal file
24
src/styles/form/input.less
Normal file
@@ -0,0 +1,24 @@
|
||||
.sui {
|
||||
|
||||
input {
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
input[type="text"], input[type="password"] {
|
||||
height: @input-text-height;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: @color-white;
|
||||
&:focus, &:hover {
|
||||
outline: none;
|
||||
border-bottom: solid 1px @color-main;
|
||||
box-shadow: 3px @color-main;
|
||||
}
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
transform: scale(@input-checkbox-scale);
|
||||
}
|
||||
input[type="range"] {
|
||||
|
||||
}
|
||||
}
|
||||
48
src/styles/form/layout.less
Normal file
48
src/styles/form/layout.less
Normal file
@@ -0,0 +1,48 @@
|
||||
form {
|
||||
.form-row {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
padding: .2em;
|
||||
|
||||
}
|
||||
.form-row > label {
|
||||
padding: .5em 1em .5em 0;
|
||||
flex: 1;
|
||||
}
|
||||
.form-row > label + label {
|
||||
flex: 0;
|
||||
}
|
||||
.form-row > label.switch + label {
|
||||
flex: 1;
|
||||
}
|
||||
.form-row > input {
|
||||
flex: 2;
|
||||
|
||||
}
|
||||
.form-row > span {
|
||||
flex: 2;
|
||||
text-align: right;
|
||||
}
|
||||
.form-row input[type="checkbox"] {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.form-row input[type="range"] {
|
||||
|
||||
}
|
||||
@media screen and (min-width: 768px) {
|
||||
.form-row > input, .form-row > span {
|
||||
flex: 3;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 992px) {
|
||||
.form-row > input, .form-row > span {
|
||||
flex: 4;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1200px) {
|
||||
.form-row > input, .form-row > span {
|
||||
flex: 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
87
src/styles/form/slider.less
Normal file
87
src/styles/form/slider.less
Normal file
@@ -0,0 +1,87 @@
|
||||
input[type=range] {
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
margin: 8.4px 0;
|
||||
padding: 0 !important;
|
||||
background-color: transparent;
|
||||
}
|
||||
input[type=range]:focus {
|
||||
outline: none;
|
||||
}
|
||||
input[type=range]::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 3.2px;
|
||||
cursor: pointer;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
background: #097479;
|
||||
border-radius: 1.3px;
|
||||
border: 0.2px solid #010101;
|
||||
}
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
box-shadow: 0.6px 0.6px 2.8px #000000, 0px 0px 0.6px #0d0d0d;
|
||||
border: 0.4px solid #000000;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50px;
|
||||
background: #0eb8c0;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
margin-top: -8.6px;
|
||||
}
|
||||
input[type=range]:focus::-webkit-slider-runnable-track {
|
||||
background: #0eb4bb;
|
||||
}
|
||||
input[type=range]::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 3.2px;
|
||||
cursor: pointer;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
background: #097479;
|
||||
border-radius: 1.3px;
|
||||
border: 0.2px solid #010101;
|
||||
}
|
||||
input[type=range]::-moz-range-thumb {
|
||||
box-shadow: 0.6px 0.6px 2.8px #000000, 0px 0px 0.6px #0d0d0d;
|
||||
border: 0.4px solid #000000;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50px;
|
||||
background: #0eb8c0;
|
||||
cursor: pointer;
|
||||
}
|
||||
input[type=range]::-ms-track {
|
||||
width: 100%;
|
||||
height: 3.2px;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
color: transparent;
|
||||
}
|
||||
input[type=range]::-ms-fill-lower {
|
||||
background: #043437;
|
||||
border: 0.2px solid #010101;
|
||||
border-radius: 2.6px;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
input[type=range]::-ms-fill-upper {
|
||||
background: #097479;
|
||||
border: 0.2px solid #010101;
|
||||
border-radius: 2.6px;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
input[type=range]::-ms-thumb {
|
||||
box-shadow: 0.6px 0.6px 2.8px #000000, 0px 0px 0.6px #0d0d0d;
|
||||
border: 0.4px solid #000000;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50px;
|
||||
background: #0eb8c0;
|
||||
cursor: pointer;
|
||||
height: 3.2px;
|
||||
}
|
||||
input[type=range]:focus::-ms-fill-lower {
|
||||
background: #097479;
|
||||
}
|
||||
input[type=range]:focus::-ms-fill-upper {
|
||||
background: #0eb4bb;
|
||||
}
|
||||
61
src/styles/form/switch.less
Normal file
61
src/styles/form/switch.less
Normal file
@@ -0,0 +1,61 @@
|
||||
/* The switch - the box around the slider */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline;
|
||||
|
||||
input {
|
||||
display:none;
|
||||
}
|
||||
|
||||
/* The slider */
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: @color-grey2;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
margin-left: -50px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: @color-main;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px @color-main-light;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
height: 34px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
5
src/styles/heading.less
Normal file
5
src/styles/heading.less
Normal file
@@ -0,0 +1,5 @@
|
||||
.heading {
|
||||
font-size: 1.2em;
|
||||
display: block;
|
||||
margin-bottom: @default-margin;
|
||||
}
|
||||
11
src/styles/main.less
Normal file
11
src/styles/main.less
Normal file
@@ -0,0 +1,11 @@
|
||||
@import "theme.less";
|
||||
|
||||
@import "base.less";
|
||||
@import "heading.less";
|
||||
@import "container.less";
|
||||
|
||||
@import "form/slider.less";
|
||||
@import "form/switch.less";
|
||||
@import "form/layout.less";
|
||||
@import "form/input.less";
|
||||
@import "form/color.less";
|
||||
19
src/styles/theme.less
Normal file
19
src/styles/theme.less
Normal file
@@ -0,0 +1,19 @@
|
||||
@default-font: "Open Sans";
|
||||
|
||||
@color-black: #000000;
|
||||
@color-white: #eeeeee;
|
||||
@color-grey: #b3b2b2;
|
||||
@color-grey2: #7b7b7b;
|
||||
@color-main: #097479;
|
||||
@color-main-light: #0eb8c0;
|
||||
@color-container: #333333;
|
||||
|
||||
@default-padding: 16px;
|
||||
@default-margin: 16px;
|
||||
@default-margin-small: 8px;
|
||||
@default-border-radius: 2px;
|
||||
@default-border-color: #555555;
|
||||
@default-font-size: 16px;
|
||||
|
||||
@input-text-height: 16px;
|
||||
@input-checkbox-scale: 2;
|
||||
Reference in New Issue
Block a user