Jambricks
An #energyhack19
by loleg
HTML
<link rel="stylesheet" href="https://unpkg.com/[email protected]/index.css">
<script src="https://unpkg.com/vue@latest/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/gh/okfn/csv.js/csv.min.js"></script>
<section class="brickapp"><div class="todoapp">
<header class="header">
<h1>🧱 Jambricks</h1>
<input class="new-todo"
autofocus autocomplete="off"
placeholder="Identify yourself or device"
v-model="newNamer"
@keyup.enter="addBrick">
<input class="brick-value"
autocomplete="off"
placeholder="How many Watts? (W = Volts x Amperes)"
v-model="newWatts"
@keyup.enter="addBrick">
<input class="brick-value"
autocomplete="off"
placeholder="How many Grams? (g)"
v-model="newGrams"
@keyup.enter="addBrick">
</header>
<section class="main" v-show="bricks.length" v-cloak>
<ul class="brick-list">
<li v-for="brick in bricks"
class="todo"
:key="brick.id"
:class="{ editing: brick == editedBrick }">
<div class="view" @dblclick="editBrick(brick)">
<button class="destroy" @click="removeBrick(brick)">â™·</button>
<label class="brick-name">{{ brick.namer }}</label>
<label class="brick-num"><i>{{ brick.watts }}</i> W</label>
<label class="brick-num"><i>{{ brick.grams }}</i> g</label>
</div>
<!--<input class="edit" type="text"
v-model="brick.watts"
v-brick-focus="brick == editedBrick"
@blur="doneEdit(brick)"
@keyup.enter="doneEdit(brick)"
@keyup.esc="cancelEdit(brick)">-->
</li>
</ul>
</section>
<footer class="footer" v-show="bricks.length" v-cloak>
<span class="brick-count">
<strong>{{ remaining }}</strong> {{ remaining | pluralize }} 🧱
<strong>{{ totalWatts }}</strong> W /
<strong>{{ totalGrams }}</strong> g
</span>
</footer>
</div>
<div class="brick-actions">
...
CSS
[v-cloak] { display: none; }
.destroy {
color: darkred;
float: right;
opacity: 0.4;
}
.brick-value {
width: 48%;
border: none;
text-align: center;
}
.brick-list {
list-style: none;
margin: 0;
padding: 0;
}
.brick-list li {
display: inline-block;
width: 7em;
border: 2px solid red;
box-shadow: 2px 2px 0px red;
background: lightyellow;
margin: 5px;
padding: 10px;
vertical-align: top;
}
.brick-list .brick-name {
font-size: 140%;
display: block;
margin-bottom: 0.5em;
color: green;
}
.brick-list .brick-num i {
color: brown;
font-style: normal;
font-size: 125%;
}
.brick-actions button { opacity: 0.5; }
.brick-actions {
margin-top: 1em;
text-align: center;
}
button:hover { opacity: 1; }
JavaScript
var STORAGE_KEY = 'jambricks'
var brickStorage = {
fetch: function () {
var bricks = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
bricks.forEach(function (brick, index) {
brick.id = index
})
brickStorage.uid = bricks.length
return bricks
},
save: function (bricks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(bricks))
}
}
// app Vue instance
var app = new Vue({
// app initial state
data: {
bricks: brickStorage.fetch(),
newNamer: '',
newWatts: '',
newGrams: '',
editedBrick: null
},
// watch todos change for localStorage persistence
watch: {
bricks: {
handler: function (bricks) {
brickStorage.save(bricks)
},
deep: true
}
},
// computed properties
// http://vuejs.org/guide/computed.html
computed: {
remaining: function () {
return this.bricks.length
},
totalWatts: function () {
return this.bricks.reduce((a,o,i,p) => a + o.watts, 0);
},
totalGrams: function() {
return this.bricks.reduce((a,o,i,p) => a + o.grams, 0);
}
},
filters: {
pluralize: function (n) {
return n === 1 ? 'brick' : 'bricks'
}
},
// methods that implement data logic.
// note there's no DOM manipulation here at all.
methods: {
addBrick: function () {
if (!(this.newNamer && this.newNamer.trim() &&
this.newWatts && this.newWatts.trim() &&
this.newGrams && this.newGrams.trim())) {
return
}
this.bricks.push({
id: brickStorage.uid++,
namer: this.newNamer.trim(),
watts: parseFloat(this.newWatts),
grams: parseFloat(this.newGrams)
})
this.newNamer = ''
this.newWatts = ''
this.newGrams = ''
},
removeBrick: function (brick) {
this.bricks.splice(this.bricks.indexOf(brick), 1)
},
editBrick: function (brick) {
this.beforeEditCache = JSON.parse(JSON.stringify(brick))
this.editedBrick...