updates
This commit is contained in:
parent
0c4c5b899b
commit
532c9372ea
@ -16,20 +16,43 @@ class CalculatorController < ApplicationController
|
|||||||
ingredient = Ingredient.find_by_ingredient_id(ingredient_id)
|
ingredient = Ingredient.find_by_ingredient_id(ingredient_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
data = {errors: [], output: ''}
|
data = {errors: Hash.new { |h, k| h[k] = [] }, output: ''}
|
||||||
|
|
||||||
begin
|
UnitConversion::with_custom_units(ingredient ? ingredient.custom_units : []) do
|
||||||
UnitConversion::with_custom_units(ingredient ? ingredient.custom_units : []) do
|
density_unit = nil
|
||||||
unit = UnitConversion.parse(input)
|
begin
|
||||||
|
if density
|
||||||
|
density_unit = UnitConversion.parse(density)
|
||||||
|
unless density_unit.density?
|
||||||
|
data[:errors][:density] << 'not a density unit'
|
||||||
|
density_unit = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue UnitConversion::UnparseableUnitError => e
|
||||||
|
data[:errors][:density] << 'invalid string'
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
input_unit = UnitConversion.parse(input)
|
||||||
|
rescue UnitConversion::UnparseableUnitError => e
|
||||||
|
data[:errors][:input] << 'invalid string'
|
||||||
|
end
|
||||||
|
|
||||||
|
if !input_unit.nil?
|
||||||
if output_unit.present?
|
if output_unit.present?
|
||||||
unit = unit.convert(output_unit, density)
|
begin
|
||||||
data[:output] = unit.to_s
|
input_unit = input_unit.convert(output_unit, density_unit)
|
||||||
|
rescue UnitConversion::UnparseableUnitError => e
|
||||||
|
data[:errors][:output_unit] << e.message
|
||||||
|
end
|
||||||
else
|
else
|
||||||
data[:output] = unit.auto_unit.to_s
|
input_unit = input_unit.auto_unit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue UnitConversion::UnparseableUnitError => e
|
|
||||||
data[:errors] << e.message
|
if data[:errors].empty?
|
||||||
|
data[:output] = input_unit.to_s
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: data
|
render json: data
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import { mapMutations, mapState } from "vuex";
|
import { mapMutations, mapState } from "vuex";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
import TWEEN from '@tweenjs/tween.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@ -44,9 +45,15 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
|
// Setup global animation loop
|
||||||
|
function animate () {
|
||||||
|
TWEEN.update();
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
animate();
|
||||||
|
|
||||||
if (this.user === null && this.authChecked === false) {
|
if (this.user === null && this.authChecked === false) {
|
||||||
this.checkAuthentication();
|
this.checkAuthentication();
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,55 +1,98 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition
|
<transition
|
||||||
name="expand"
|
name="expand"
|
||||||
:duration="500"
|
|
||||||
@enter="enter"
|
@enter="enter"
|
||||||
@after-enter="afterEnter"
|
@leave="leave"
|
||||||
@leave="leave">
|
@enter-cancel="cancel"
|
||||||
|
@leave-cancel="cancel">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import TWEEN from '@tweenjs/tween.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
props: {
|
||||||
|
expandTime: {
|
||||||
|
type: Number,
|
||||||
|
default: 250
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
animation: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
cancel () {
|
||||||
|
if (this.animation) {
|
||||||
|
this.animation.stop();
|
||||||
|
this.animation = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
enter(element, done) {
|
enter(element, done) {
|
||||||
const width = getComputedStyle(element).width;
|
const width = parseInt(getComputedStyle(element).width);
|
||||||
|
const paddingTop = parseInt(getComputedStyle(element).paddingTop);
|
||||||
|
const paddingBottom = parseInt(getComputedStyle(element).paddingBottom);
|
||||||
|
|
||||||
element.style.width = width;
|
element.style.width = width;
|
||||||
element.style.position = 'absolute';
|
element.style.position = 'absolute';
|
||||||
element.style.visibility = 'hidden';
|
element.style.visibility = 'hidden';
|
||||||
element.style.height = 'auto';
|
element.style.height = 'auto';
|
||||||
|
|
||||||
const height = getComputedStyle(element).height;
|
const height = parseInt(getComputedStyle(element).height);
|
||||||
|
|
||||||
element.style.width = null;
|
element.style.width = null;
|
||||||
element.style.position = null;
|
element.style.position = null;
|
||||||
element.style.visibility = null;
|
element.style.visibility = null;
|
||||||
|
element.style.overflow = 'hidden';
|
||||||
element.style.height = 0;
|
element.style.height = 0;
|
||||||
|
|
||||||
// Trigger the animation.
|
this.animation = new TWEEN.Tween({height: 0, paddingTop: 0, paddingBottom: 0})
|
||||||
// We use `setTimeout` because we need
|
.to({height: height, paddingTop: paddingTop, paddingBottom: paddingBottom}, this.expandTime)
|
||||||
// to make sure the browser has finished
|
.onUpdate(obj => {
|
||||||
// painting after setting the `height`
|
element.style.height = obj.height + "px";
|
||||||
// to `0` in the line above.
|
element.style.paddingBottom = obj.paddingBottom + "px";
|
||||||
setTimeout(() => {
|
element.style.paddingTop = obj.paddingTop + "px";
|
||||||
element.style.height = height;
|
})
|
||||||
});
|
.onComplete(() => {
|
||||||
},
|
this.animation = null;
|
||||||
|
element.removeAttribute('style');
|
||||||
afterEnter(element) {
|
element.style.opacity = 0.99;
|
||||||
element.style.height = 'auto';
|
setTimeout(() => {
|
||||||
|
// Fixes odd drawing bug in Chrome
|
||||||
|
element.style.opacity = 1.0;
|
||||||
|
}, 1000);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.start();
|
||||||
},
|
},
|
||||||
|
|
||||||
leave(element, done) {
|
leave(element, done) {
|
||||||
const height = getComputedStyle(element).height;
|
const height = parseInt(getComputedStyle(element).height);
|
||||||
|
const paddingTop = parseInt(getComputedStyle(element).paddingTop);
|
||||||
|
const paddingBottom = parseInt(getComputedStyle(element).paddingBottom);
|
||||||
|
|
||||||
element.style.height = height;
|
element.style.overflow = 'hidden';
|
||||||
|
|
||||||
setTimeout(() => {
|
this.animation = new TWEEN.Tween({height: height, paddingTop: paddingTop, paddingBottom: paddingBottom})
|
||||||
element.style.height = 0;
|
.to({height: 0, paddingTop: 0, paddingBottom: 0}, this.expandTime)
|
||||||
});
|
.onUpdate(obj => {
|
||||||
|
element.style.height = obj.height + "px";
|
||||||
|
element.style.paddingBottom = obj.paddingBottom + "px";
|
||||||
|
element.style.paddingTop = obj.paddingTop + "px";
|
||||||
|
})
|
||||||
|
.onComplete(() => {
|
||||||
|
this.animation = null;
|
||||||
|
element.removeAttribute('style');
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
person: new IconData('person'),
|
person: new IconData('person'),
|
||||||
star: new IconData('star'),
|
star: new IconData('star'),
|
||||||
'star-empty': new IconData('star-empty'),
|
'star-empty': new IconData('star-empty'),
|
||||||
|
warning: new IconData('warning'),
|
||||||
x: new IconData('x')
|
x: new IconData('x')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
import Pencil from "../iconic/svg/smart/pencil";
|
import Pencil from "../iconic/svg/smart/pencil";
|
||||||
import Star from "../iconic/svg/smart/star";
|
import Star from "../iconic/svg/smart/star";
|
||||||
import StarEmpty from "../iconic/svg/smart/star-empty";
|
import StarEmpty from "../iconic/svg/smart/star-empty";
|
||||||
|
import Warning from "../iconic/svg/smart/warning";
|
||||||
import X from "../iconic/svg/smart/x";
|
import X from "../iconic/svg/smart/x";
|
||||||
|
|
||||||
const APIS = {};
|
const APIS = {};
|
||||||
@ -36,6 +37,7 @@
|
|||||||
person: Person,
|
person: Person,
|
||||||
star: Star,
|
star: Star,
|
||||||
'star-empty': StarEmpty,
|
'star-empty': StarEmpty,
|
||||||
|
warning: Warning,
|
||||||
x: X
|
x: X
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label v-if="label.length" class="label is-small-mobile">{{ label }}</label>
|
<label v-if="label.length" class="label is-small-mobile">{{ label }}</label>
|
||||||
<div class="control">
|
<div :class="controlClasses">
|
||||||
<textarea v-if="isTextarea" class="textarea is-small-mobile" :value="value" @input="input"></textarea>
|
<textarea v-if="isTextarea" :class="inputClasses" :value="value" @input="input" :disabled="disabled"></textarea>
|
||||||
<input v-else :type="type" class="input is-small-mobile" :value="value" @input="input">
|
<input v-else :type="type" :class="inputClasses" :value="value" @input="input" :disabled="disabled">
|
||||||
|
<app-icon class="is-right" icon="warning" v-if="validationError !== null"></app-icon>
|
||||||
</div>
|
</div>
|
||||||
|
<p v-if="helpMessage !== null" :class="helpClasses">
|
||||||
|
{{ helpMessage }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -26,12 +30,54 @@
|
|||||||
required: false,
|
required: false,
|
||||||
type: String,
|
type: String,
|
||||||
default: "text"
|
default: "text"
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
validationError: {
|
||||||
|
required: false,
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isTextarea() {
|
isTextarea() {
|
||||||
return this.type === "textarea";
|
return this.type === "textarea";
|
||||||
|
},
|
||||||
|
|
||||||
|
controlClasses() {
|
||||||
|
return [
|
||||||
|
"control",
|
||||||
|
{
|
||||||
|
"has-icons-right": this.validationError !== null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
inputClasses() {
|
||||||
|
return [
|
||||||
|
"is-small-mobile",
|
||||||
|
{
|
||||||
|
"textarea": this.isTextarea,
|
||||||
|
"input": !this.isTextarea,
|
||||||
|
"is-danger": this.validationError !== null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
helpMessage() {
|
||||||
|
return this.validationError;
|
||||||
|
},
|
||||||
|
|
||||||
|
helpClasses() {
|
||||||
|
return [
|
||||||
|
"help",
|
||||||
|
{
|
||||||
|
"is-danger": this.validationError !== null
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
{{food.name}}
|
<h3 class="title">
|
||||||
|
{{food.name}}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="columns task-item-edit">
|
||||||
|
|
||||||
<div class="field">
|
<div class="field column">
|
||||||
<label class="label is-small">Name</label>
|
<label class="label is-small">Name</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input is-small" type="text" placeholder="Name" v-model="taskItem.name" @keydown="inputKeydown" ref="nameInput">
|
<input class="input is-small" type="text" placeholder="Name" v-model="taskItem.name" @keydown="inputKeydown" ref="nameInput">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field column">
|
||||||
<label class="label is-small">Quantity</label>
|
<label class="label is-small">Quantity</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input is-small" type="text" placeholder="Qty" v-model="taskItem.quantity" @keydown="inputKeydown">
|
<input class="input is-small" type="text" placeholder="Qty" v-model="taskItem.quantity" @keydown="inputKeydown">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field column">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-primary" @click="save">Add</button>
|
<button class="button is-primary" @click="save">Add</button>
|
||||||
</div>
|
</div>
|
||||||
@ -61,4 +61,8 @@
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.task-item-edit {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
@ -1,43 +1,61 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<app-expand-transition name="fade">
|
<div class="panel">
|
||||||
<task-item-edit @save="save" :task-item="newItem" v-if="showAddItem" ref="itemEdit"></task-item-edit>
|
<p class="panel-heading">
|
||||||
</app-expand-transition>
|
{{taskList.name}} ({{completedItemCount}} / {{taskList.task_items.length}})
|
||||||
|
</p>
|
||||||
|
|
||||||
<table class="table">
|
<div class="panel-block">
|
||||||
<thead>
|
<button class="button is-fullwidth is-primary" @click="toggleShowAddItem">{{ showAddItem ? 'Done' : 'New Item' }}</button>
|
||||||
<tr>
|
</div>
|
||||||
<th></th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Quantity</th>
|
|
||||||
<th>
|
|
||||||
<button class="button" @click="toggleShowAddItem">{{ showAddItem ? 'Done' : 'New Item' }}</button>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<transition-group tag="tbody" name="list-item-move">
|
<app-expand-transition>
|
||||||
<tr v-for="i in taskItems" :key="i.id" @click="toggleItem(i)">
|
<div class="panel-block" v-if="showAddItem">
|
||||||
<td>
|
<task-item-edit @save="save" :task-item="newItem" ref="itemEdit"></task-item-edit>
|
||||||
<div class="check">
|
</div>
|
||||||
<app-icon v-if="i.completed" icon="check"></app-icon>
|
</app-expand-transition>
|
||||||
<span class="icon" v-else></span>
|
|
||||||
</div>
|
<transition-group tag="div" name="list-item-move">
|
||||||
</td>
|
<a v-for="i in taskItems" :key="i.id" @click="toggleItem(i)" class="panel-block">
|
||||||
<td>{{ i.name }}</td>
|
<div class="check">
|
||||||
<td>{{ i.quantity }}</td>
|
<app-icon v-if="i.completed" icon="check"></app-icon>
|
||||||
<td></td>
|
<span class="icon" v-else></span>
|
||||||
</tr>
|
</div>
|
||||||
|
<span>{{ i.quantity }} {{ i.name }}</span>
|
||||||
|
</a>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
<tbody v-if="taskItems.length === 0">
|
|
||||||
<tr>
|
<app-expand-transition>
|
||||||
<td colspan="4">
|
<div class="panel-block" v-if="uncompletedItemCount > 0">
|
||||||
No Items
|
<button class="button is-fullwidth is-link" @click="completeAllItems">
|
||||||
</td>
|
<span class="check">
|
||||||
</tr>
|
<app-icon icon="check"></app-icon>
|
||||||
</tbody>
|
</span>
|
||||||
</table>
|
<span>Check All</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</app-expand-transition>
|
||||||
|
<app-expand-transition>
|
||||||
|
<div class="panel-block" v-if="completedItemCount > 0">
|
||||||
|
<button class="button is-fullwidth is-link" @click="unCompleteAllItems">
|
||||||
|
<span class="check">
|
||||||
|
<span class="icon"></span>
|
||||||
|
</span>
|
||||||
|
<span>Uncheck All</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</app-expand-transition>
|
||||||
|
<app-expand-transition>
|
||||||
|
<div class="panel-block" v-if="completedItemCount > 0">
|
||||||
|
<button class="button is-fullwidth is-link" @click="deleteCompletedItems">
|
||||||
|
<app-icon icon="x" class="is-text-danger"></app-icon>
|
||||||
|
<span>Clear Completed</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</app-expand-transition>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -76,6 +94,13 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
completedItemCount() {
|
||||||
|
return this.taskList === null ? 0 : this.taskList.task_items.filter(i => i.completed).length;
|
||||||
|
},
|
||||||
|
|
||||||
|
uncompletedItemCount() {
|
||||||
|
return this.taskList === null ? 0 : this.taskList.task_items.filter(i => !i.completed).length;
|
||||||
|
},
|
||||||
completedTaskItems() {
|
completedTaskItems() {
|
||||||
return (this.taskList ? this.taskList.task_items : []).filter(i => i.completed);
|
return (this.taskList ? this.taskList.task_items : []).filter(i => i.completed);
|
||||||
},
|
},
|
||||||
@ -93,6 +118,7 @@
|
|||||||
...mapActions([
|
...mapActions([
|
||||||
'createTaskItem',
|
'createTaskItem',
|
||||||
'updateTaskItem',
|
'updateTaskItem',
|
||||||
|
'deleteTaskItems',
|
||||||
'completeTaskItems'
|
'completeTaskItems'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -120,7 +146,38 @@
|
|||||||
toggleShowAddItem() {
|
toggleShowAddItem() {
|
||||||
this.newItem = newItemTemplate(this.taskList.id);
|
this.newItem = newItemTemplate(this.taskList.id);
|
||||||
this.showAddItem = !this.showAddItem;
|
this.showAddItem = !this.showAddItem;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
completeAllItems() {
|
||||||
|
const toComplete = this.taskList.task_items.filter(i => !i.completed);
|
||||||
|
this.loadResource(
|
||||||
|
this.completeTaskItems({
|
||||||
|
taskList: this.taskList,
|
||||||
|
taskItems: toComplete,
|
||||||
|
completed: true
|
||||||
|
})
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
unCompleteAllItems() {
|
||||||
|
const toUnComplete = this.taskList.task_items.filter(i => i.completed);
|
||||||
|
this.loadResource(
|
||||||
|
this.completeTaskItems({
|
||||||
|
taskList: this.taskList,
|
||||||
|
taskItems: toUnComplete,
|
||||||
|
completed: false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteCompletedItems() {
|
||||||
|
this.loadResource(
|
||||||
|
this.deleteTaskItems({
|
||||||
|
taskList: this.taskList,
|
||||||
|
taskItems: this.taskList.task_items.filter(i => i.completed)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -147,9 +204,22 @@
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.check {
|
.check {
|
||||||
border: 2px solid $link;
|
display: inline-flex;
|
||||||
display: flex;
|
margin-right: 1.5rem;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
position: relative;
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -3px;
|
||||||
|
left: -3px;
|
||||||
|
bottom: -3px;
|
||||||
|
right: -3px;
|
||||||
|
border: 2px solid currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
@ -2,59 +2,41 @@
|
|||||||
<div>
|
<div>
|
||||||
<h1 class="title">Calculator</h1>
|
<h1 class="title">Calculator</h1>
|
||||||
|
|
||||||
<div v-for="err in errors" :key="err" class="notification is-warning">
|
<div class="box">
|
||||||
{{err}}
|
|
||||||
</div>
|
<div class="columns">
|
||||||
|
<app-text-field label="Input" v-model="input" class="column" :validation-error="inputErrors"></app-text-field>
|
||||||
|
<app-text-field label="Output Unit" v-model="outputUnit" class="column" :validation-error="outputUnitErrors"></app-text-field>
|
||||||
|
|
||||||
<div class="columns">
|
|
||||||
<div class="field column">
|
|
||||||
<label class="label">Input</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="text" placeholder="input" v-model="input">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="field column">
|
|
||||||
<label class="label">Output Unit</label>
|
<div class="columns">
|
||||||
<div class="control">
|
<div class="field column">
|
||||||
<input class="input" type="text" placeholder="unit" v-model="outputUnit">
|
<label class="label">Ingredient</label>
|
||||||
|
<div class="control">
|
||||||
|
<app-autocomplete
|
||||||
|
:inputClass="{'is-success': ingredient !== null}"
|
||||||
|
ref="autocomplete"
|
||||||
|
v-model="ingredient_name"
|
||||||
|
:minLength="2"
|
||||||
|
valueAttribute="name"
|
||||||
|
labelAttribute="name"
|
||||||
|
placeholder=""
|
||||||
|
@optionSelected="searchItemSelected"
|
||||||
|
:onGetOptions="updateSearchItems"
|
||||||
|
>
|
||||||
|
</app-autocomplete>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<app-text-field label="Density" v-model="density" class="column" :disabled="ingredient !== null" :validation-error="densityErrors"></app-text-field>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<app-text-field label="Output" v-model="output" disabled></app-text-field>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="columns">
|
|
||||||
<div class="field column">
|
|
||||||
<label class="label">Ingredient</label>
|
|
||||||
<div class="control">
|
|
||||||
<app-autocomplete
|
|
||||||
:inputClass="{'is-success': ingredient !== null}"
|
|
||||||
ref="autocomplete"
|
|
||||||
v-model="ingredient_name"
|
|
||||||
:minLength="2"
|
|
||||||
valueAttribute="name"
|
|
||||||
labelAttribute="name"
|
|
||||||
placeholder=""
|
|
||||||
@optionSelected="searchItemSelected"
|
|
||||||
:onGetOptions="updateSearchItems"
|
|
||||||
>
|
|
||||||
</app-autocomplete>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field column">
|
|
||||||
<label class="label">Density</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="text" placeholder="8.345 lb/gallon" v-model="density" :disabled="ingredient !== null">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field">
|
|
||||||
<label class="label">Output</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="text" disabled="disabled" v-model="output">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -72,10 +54,36 @@
|
|||||||
ingredient: null,
|
ingredient: null,
|
||||||
density: '',
|
density: '',
|
||||||
output: '',
|
output: '',
|
||||||
errors: []
|
errors: {}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
inputErrors() {
|
||||||
|
if (this.errors.input && this.errors.input.length > 0) {
|
||||||
|
return this.errors.input.join(", ");
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
outputUnitErrors() {
|
||||||
|
if (this.errors.output_unit && this.errors.output_unit.length > 0) {
|
||||||
|
return this.errors.output_unit.join(", ");
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
densityErrors() {
|
||||||
|
if (this.errors.density && this.errors.density.length > 0) {
|
||||||
|
return this.errors.density.join(", ");
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
updateSearchItems(text) {
|
updateSearchItems(text) {
|
||||||
return api.getSearchIngredients(text);
|
return api.getSearchIngredients(text);
|
||||||
@ -88,12 +96,14 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateOutput: debounce(function() {
|
updateOutput: debounce(function() {
|
||||||
this.loadResource(api.getCalculate(this.input, this.outputUnit, this.ingredient ? this.ingredient.ingredient_id : null, this.density)
|
if (this.input && this.input.length > 0) {
|
||||||
.then(data => {
|
this.loadResource(api.getCalculate(this.input, this.outputUnit, this.ingredient ? this.ingredient.ingredient_id : null, this.density)
|
||||||
this.output = data.output;
|
.then(data => {
|
||||||
this.errors = data.errors;
|
this.output = data.output;
|
||||||
})
|
this.errors = data.errors;
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}, 500)
|
}, 500)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,30 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="title is-3">Tasks</h1>
|
<h1 class="title is-3">
|
||||||
|
Tasks
|
||||||
|
<app-dropdown button-class="is-primary" :open="showListDropdown" :label="listSelectLabel" @open="showListDropdown = true" @close="showListDropdown = false">
|
||||||
|
|
||||||
<app-dropdown button-class="is-primary" :open="showListDropdown" :label="listSelectLabel" @open="showListDropdown = true" @close="showListDropdown = false">
|
<task-list-dropdown-item v-for="l in taskLists" :key="l.id" :task-list="l" :active="currentTaskList !== null && currentTaskList.id === l.id" @select="selectList" @delete="deleteList"></task-list-dropdown-item>
|
||||||
|
|
||||||
<task-list-dropdown-item v-for="l in taskLists" :key="l.id" :task-list="l" :active="currentTaskList !== null && currentTaskList.id === l.id" @select="selectList" @delete="deleteList"></task-list-dropdown-item>
|
<hr class="dropdown-divider" v-if="taskLists.length > 0">
|
||||||
|
<div class="dropdown-item">
|
||||||
|
<task-list-mini-form :task-list="newList" :validation-errors="newListValidationErrors" @save="saveNewList"></task-list-mini-form>
|
||||||
|
</div>
|
||||||
|
</app-dropdown>
|
||||||
|
</h1>
|
||||||
|
|
||||||
<hr class="dropdown-divider" v-if="taskLists.length > 0">
|
<div class="columns" v-if="currentTaskList !== null">
|
||||||
<div class="dropdown-item">
|
<div class="column is-6-widescreen is-8-desktop is-10-tablet">
|
||||||
<task-list-mini-form :task-list="newList" :validation-errors="newListValidationErrors" @save="saveNewList"></task-list-mini-form>
|
|
||||||
</div>
|
|
||||||
</app-dropdown>
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
<div v-if="currentTaskList !== null">
|
|
||||||
|
|
||||||
<div class="box">
|
|
||||||
<button class="button" @click="deleteCompletedItems" v-if="completedItemCount > 0">Clear Completed</button>
|
|
||||||
<button class="button" @click="completeAllItems" v-if="uncompletedItemCount > 0">Check All</button>
|
|
||||||
<button class="button" @click="unCompleteAllItems" v-if="completedItemCount > 0">Uncheck All</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="box">
|
|
||||||
<task-item-list :task-list="currentTaskList"></task-item-list>
|
<task-item-list :task-list="currentTaskList"></task-item-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -66,14 +58,6 @@
|
|||||||
} else {
|
} else {
|
||||||
return this.currentTaskList.name;
|
return this.currentTaskList.name;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
completedItemCount() {
|
|
||||||
return this.currentTaskList === null ? 0 : this.currentTaskList.task_items.filter(i => i.completed).length;
|
|
||||||
},
|
|
||||||
|
|
||||||
uncompletedItemCount() {
|
|
||||||
return this.currentTaskList === null ? 0 : this.currentTaskList.task_items.filter(i => !i.completed).length;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -109,36 +93,7 @@
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
completeAllItems() {
|
|
||||||
const toComplete = this.currentTaskList.task_items.filter(i => !i.completed);
|
|
||||||
this.loadResource(
|
|
||||||
this.completeTaskItems({
|
|
||||||
taskList: this.currentTaskList,
|
|
||||||
taskItems: toComplete,
|
|
||||||
completed: true
|
|
||||||
})
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
unCompleteAllItems() {
|
|
||||||
const toUnComplete = this.currentTaskList.task_items.filter(i => i.completed);
|
|
||||||
this.loadResource(
|
|
||||||
this.completeTaskItems({
|
|
||||||
taskList: this.currentTaskList,
|
|
||||||
taskItems: toUnComplete,
|
|
||||||
completed: false
|
|
||||||
})
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteCompletedItems() {
|
|
||||||
this.loadResource(
|
|
||||||
this.deleteTaskItems({
|
|
||||||
taskList: this.currentTaskList,
|
|
||||||
taskItems: this.currentTaskList.task_items.filter(i => i.completed)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteAllItems() {
|
deleteAllItems() {
|
||||||
this.loadResource(
|
this.loadResource(
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
.expand-enter-active,
|
.expand-enter-active,
|
||||||
.expand-leave-active {
|
.expand-leave-active {
|
||||||
transition: height .5s ease-in-out;
|
// transition: height .5s ease-in-out;
|
||||||
overflow: hidden;
|
// overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
@import "~bulma/sass/components/message";
|
@import "~bulma/sass/components/message";
|
||||||
@import "~bulma/sass/components/modal";
|
@import "~bulma/sass/components/modal";
|
||||||
@import "~bulma/sass/components/pagination";
|
@import "~bulma/sass/components/pagination";
|
||||||
|
@import "~bulma/sass/components/panel";
|
||||||
@import "~bulma/sass/elements/_all";
|
@import "~bulma/sass/elements/_all";
|
||||||
@import "~bulma/sass/grid/columns";
|
@import "~bulma/sass/grid/columns";
|
||||||
@import "~bulma/sass/layout/section";
|
@import "~bulma/sass/layout/section";
|
||||||
@ -36,6 +37,7 @@ body {
|
|||||||
.container {
|
.container {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
|
min-height: 75vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,13 +4,10 @@ class Ingredient < ApplicationRecord
|
|||||||
class << self
|
class << self
|
||||||
|
|
||||||
def find_by_ingredient_id(ingredient_id)
|
def find_by_ingredient_id(ingredient_id)
|
||||||
puts "looking up |#{ingredient_id}|"
|
|
||||||
case ingredient_id
|
case ingredient_id
|
||||||
when /^R(\d+)$/
|
when /^R(\d+)$/
|
||||||
puts 'rec'
|
|
||||||
Recipe.find($1)
|
Recipe.find($1)
|
||||||
when /^F(\d+)$/
|
when /^F(\d+)$/
|
||||||
puts 'food'
|
|
||||||
Food.find($1)
|
Food.find($1)
|
||||||
else
|
else
|
||||||
raise ActiveRecord::RecordNotFound
|
raise ActiveRecord::RecordNotFound
|
||||||
|
@ -24,7 +24,7 @@ module UnitConversion
|
|||||||
|
|
||||||
def convert(value_unit)
|
def convert(value_unit)
|
||||||
|
|
||||||
input = value_unit.unitwise
|
input = value_unit.unitwise * 1.0
|
||||||
|
|
||||||
if value_unit.volume? && @target_unit.mass?
|
if value_unit.volume? && @target_unit.mass?
|
||||||
raise MissingDensityError, "Cannot convert #{value_unit.unit} to #{@target_unit} without density" unless @density
|
raise MissingDensityError, "Cannot convert #{value_unit.unit} to #{@target_unit} without density" unless @density
|
||||||
@ -36,7 +36,7 @@ module UnitConversion
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
input = input.convert_to @target_unit.unit
|
input = input.convert_to @target_unit.unit
|
||||||
rescue Unitwise::ConversionError => err
|
rescue Unitwise::ConversionError, Unitwise::ExpressionError => err
|
||||||
raise ConversionError, err.message
|
raise ConversionError, err.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ module UnitConversion
|
|||||||
def compatible?(unit_str)
|
def compatible?(unit_str)
|
||||||
begin
|
begin
|
||||||
unitwise.compatible_with? Unitwise(1, unit_str)
|
unitwise.compatible_with? Unitwise(1, unit_str)
|
||||||
rescue UnknownUnitError
|
rescue TypeError, Unitwise::ConversionError, Unitwise::ExpressionError
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -19,6 +19,10 @@ module Unitwise
|
|||||||
|
|
||||||
def self.with_custom_units(unit_list, &block)
|
def self.with_custom_units(unit_list, &block)
|
||||||
|
|
||||||
|
if unit_list.empty?
|
||||||
|
return block.call
|
||||||
|
end
|
||||||
|
|
||||||
atoms = []
|
atoms = []
|
||||||
ret_val = nil
|
ret_val = nil
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rails/webpacker": "^3.5.5",
|
"@rails/webpacker": "^3.5.5",
|
||||||
|
"@tweenjs/tween.js": "^17.2.0",
|
||||||
"autosize": "^4.0.1",
|
"autosize": "^4.0.1",
|
||||||
"bulma": "^0.7.1",
|
"bulma": "^0.7.1",
|
||||||
"caniuse-lite": "^1.0.30000815",
|
"caniuse-lite": "^1.0.30000815",
|
||||||
|
@ -32,6 +32,10 @@
|
|||||||
webpack "^3.12.0"
|
webpack "^3.12.0"
|
||||||
webpack-manifest-plugin "^1.3.2"
|
webpack-manifest-plugin "^1.3.2"
|
||||||
|
|
||||||
|
"@tweenjs/tween.js@^17.2.0":
|
||||||
|
version "17.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tweenjs/tween.js/-/tween.js-17.2.0.tgz#21f89b709bafc4b303adae7a83b4f35a0d9e4796"
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "10.9.4"
|
version "10.9.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897"
|
||||||
|
Loading…
Reference in New Issue
Block a user