This commit is contained in:
Dan Elbert 2018-09-05 17:49:21 -05:00
parent 41117e2e7f
commit 4b25f753f1
10 changed files with 336 additions and 49 deletions

View File

@ -245,4 +245,4 @@ DEPENDENCIES
webpacker (= 3.5.3) webpacker (= 3.5.3)
BUNDLED WITH BUNDLED WITH
1.16.1 1.16.2

View File

@ -0,0 +1,66 @@
<template>
<transition
name="expand"
:duration="500"
@enter="enter"
@after-enter="afterEnter"
@leave="leave">
<slot></slot>
</transition>
</template>
<script>
export default {
methods: {
enter(element, done) {
const width = getComputedStyle(element).width;
element.style.width = width;
element.style.position = 'absolute';
element.style.visibility = 'hidden';
element.style.height = 'auto';
const height = getComputedStyle(element).height;
element.style.width = null;
element.style.position = null;
element.style.visibility = null;
element.style.height = 0;
// Trigger the animation.
// We use `setTimeout` because we need
// to make sure the browser has finished
// painting after setting the `height`
// to `0` in the line above.
setTimeout(() => {
element.style.height = height;
});
},
afterEnter(element) {
element.style.height = 'auto';
},
leave(element, done) {
const height = getComputedStyle(element).height;
element.style.height = height;
setTimeout(() => {
element.style.height = 0;
});
}
}
}
</script>
<style scoped>
* {
will-change: height;
transform: translateZ(0);
backface-visibility: hidden;
perspective: 1000px;
}
</style>

View File

@ -0,0 +1,60 @@
<template>
<div>
<div class="field">
<label class="label is-small">Name</label>
<div class="control">
<input class="input is-small" type="text" placeholder="Name" v-model="taskItem.name" @keydown="inputKeydown" ref="nameInput">
</div>
</div>
<div class="field">
<label class="label is-small">Quantity</label>
<div class="control">
<input class="input is-small" type="text" placeholder="Qty" v-model="taskItem.quantity" @keydown="inputKeydown">
</div>
</div>
<div class="field">
<div class="control">
<button class="button is-primary">Add</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
taskItem: {
required: true,
type: Object
}
},
methods: {
inputKeydown(evt) {
switch (evt.key) {
case "Enter":
evt.preventDefault();
this.save();
}
},
save() {
this.$emit("save", this.taskItem);
}
},
mounted() {
this.$refs.nameInput.focus();
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,98 @@
<template>
<div>
<app-expand-transition name="fade">
<task-item-edit :task-item="newItem" v-if="showAddItem"></task-item-edit>
</app-expand-transition>
<table class="table is-narrow">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Quantity</th>
<th>
<button class="button" @click="toggleShowAddItem">{{ showAddItem ? 'Done' : 'New Item' }}</button>
</th>
</tr>
</thead>
<tbody>
<tr v-for="i in taskItems">
<td></td>
<td>{{ i.name }}</td>
<td>{{ i.quantity }}</td>
<td></td>
</tr>
<tr v-if="taskItems.length === 0">
<td colspan="4">
No Items
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import TaskItemEdit from "./TaskItemEdit";
const newItemTemplate = function() {
return {
name: '',
quantity: ''
};
};
export default {
props: {
taskItems: {
required: true,
type: Array
}
},
data() {
return {
showAddItem: false,
newItem: newItemTemplate(),
newItemValidationErrors: {}
};
},
methods: {
toggleShowAddItem() {
this.showAddItem = !this.showAddItem;
if (this.showAddItem) {
this.$nextTick(() => {
//this.$refs.quantityInput.focus();
});
}
}
},
components: {
TaskItemEdit
}
}
</script>
<style lang="scss" scoped>
.columns {
margin-top: 0;
margin-bottom: 0;
}
.column {
padding-top: 0;
padding-bottom: 0;
margin-top: 0;
margin-bottom: 0;
}
</style>

View File

@ -0,0 +1,72 @@
<template>
<div class="dropdown-item" @mouseover="hovering = true" @mouseleave="hovering = false" :class="{hovered: hovering, 'is-active': active}" @click="selectList">
<span>{{taskList.name}}</span>
<button @click.stop="confirmingDelete = true" class="button is-small is-danger is-pulled-right"><app-icon icon="x" size="sm"></app-icon></button>
<div class="is-clearfix"></div>
<app-modal :open="confirmingDelete" :title="'Delete ' + taskList.name + '?'" @dismiss="confirmingDelete = false">
<button class="button is-danger" @click="deleteList">Confirm</button>
<button class="button is-primary" @click="confirmingDelete = false">Cancel</button>
</app-modal>
</div>
</template>
<script>
export default {
props: {
taskList: {
type: Object,
required: true
},
active: {
type: Boolean,
required: false,
default: false
}
},
data() {
return {
hovering: false,
confirmingDelete: false
};
},
methods: {
selectList() {
this.$emit("select", this.taskList);
},
deleteList() {
this.confirmingDelete = false;
this.$emit("delete", this.taskList);
}
}
}
</script>
<style lang="scss" scoped>
@import "~styles/variables";
div.dropdown-item {
cursor: pointer;
&.hovered {
color: $black;
background-color: $background;
}
&.is-active {
color: $link-invert;
background-color: $link;
}
}
</style>

View File

@ -2,16 +2,9 @@
<div> <div>
<h1>Tasks</h1> <h1>Tasks</h1>
<app-modal :open="listToDelete !== null" :title="'Delete ' + (listToDelete !== null ? listToDelete.name : '') + '?'" @dismiss="listToDelete = null">
<button class="button is-danger" @click="confirmListDelete">Confirm</button>
<button class="button is-primary" @click="listToDelete = null">Cancel</button>
</app-modal>
<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">
<div class="dropdown-item" v-for="l in taskLists">
<a href="#" @click="selectList(l)">{{l.name}}<button @click.stop="listToDelete = l" class="button is-small is-danger is-pulled-right"><app-icon icon="x" size="sm"></app-icon></button></a>
</div> <task-list-dropdown-item v-for="l in taskLists" :task-list="l" :active="currentList !== null && currentList.id === l.id" @select="selectList" @delete="deleteList"></task-list-dropdown-item>
<hr class="dropdown-divider" v-if="taskLists.length > 0"> <hr class="dropdown-divider" v-if="taskLists.length > 0">
<div class="dropdown-item"> <div class="dropdown-item">
@ -20,29 +13,9 @@
</app-dropdown> </app-dropdown>
<div v-if="currentList !== null"> <div v-if="currentList !== null">
<table class="table">
<tr>
<th></th>
<th>Name</th>
<th>Quantity</th>
<th></th>
</tr>
<tr v-if="showAddItem">
<th></th>
<th><input type="text"></th>
<th><input type="text"></th>
<th><button type="button" class="button">Add</button></th>
</tr>
<tr v-for="i in currentList.task_items">
<td></td>
<td>{{i.name}}</td>
<td>{{i.quantity}}</td>
</tr>
</table>
<div v-if="currentList.task_items.length === 0"> <task-item-list :task-items="currentList.task_items"></task-item-list>
No Items
</div>
</div> </div>
</div> </div>
@ -53,6 +26,8 @@
import api from "../lib/Api"; import api from "../lib/Api";
import * as Errors from '../lib/Errors'; import * as Errors from '../lib/Errors';
import TaskListMiniForm from "./TaskListMiniForm"; import TaskListMiniForm from "./TaskListMiniForm";
import TaskListDropdownItem from "./TaskListDropdownItem";
import TaskItemList from "./TaskItemList";
const newListTemplate = function() { const newListTemplate = function() {
return { return {
@ -60,13 +35,6 @@
}; };
}; };
const newItemTemplate = function() {
return {
name: '',
quantity: ''
};
};
export default { export default {
data() { data() {
return { return {
@ -74,11 +42,7 @@
showListDropdown: false, showListDropdown: false,
currentList: null, currentList: null,
newList: newListTemplate(), newList: newListTemplate(),
newListValidationErrors: {}, newListValidationErrors: {}
showAddItem: false,
newItem: newItemTemplate(),
newItemValidationErrors: {},
listToDelete: null
} }
}, },
@ -93,8 +57,8 @@
}, },
methods: { methods: {
selectList(l) { selectList(list) {
this.currentList = l; this.currentList = list;
this.showListDropdown = false; this.showListDropdown = false;
}, },
@ -124,11 +88,10 @@
} }
}, },
confirmListDelete() { deleteList(list) {
this.loadResource( this.loadResource(
api.deleteTaskList(this.listToDelete) api.deleteTaskList(list)
.then(() => this.updateData()) .then(() => this.updateData())
.then(() => this.listToDelete = null)
); );
} }
}, },
@ -138,7 +101,9 @@
}, },
components: { components: {
TaskListMiniForm TaskListMiniForm,
TaskListDropdownItem,
TaskItemList
} }
} }

View File

@ -15,6 +15,7 @@ import AppConfirm from "../components/AppConfirm";
import AppDateTime from "../components/AppDateTime"; import AppDateTime from "../components/AppDateTime";
import AppDatePicker from "../components/AppDatePicker"; import AppDatePicker from "../components/AppDatePicker";
import AppDropdown from "../components/AppDropdown"; import AppDropdown from "../components/AppDropdown";
import AppExpandTransition from "../components/AppExpandTransition";
import AppIcon from "../components/AppIcon"; import AppIcon from "../components/AppIcon";
import AppModal from "../components/AppModal"; import AppModal from "../components/AppModal";
import AppNavbar from "../components/AppNavbar"; import AppNavbar from "../components/AppNavbar";
@ -29,6 +30,7 @@ Vue.component("AppConfirm", AppConfirm);
Vue.component("AppDateTime", AppDateTime); Vue.component("AppDateTime", AppDateTime);
Vue.component("AppDatePicker", AppDatePicker); Vue.component("AppDatePicker", AppDatePicker);
Vue.component("AppDropdown", AppDropdown); Vue.component("AppDropdown", AppDropdown);
Vue.component("AppExpandTransition", AppExpandTransition);
Vue.component("AppIcon", AppIcon); Vue.component("AppIcon", AppIcon);
Vue.component("AppModal", AppModal); Vue.component("AppModal", AppModal);
Vue.component("AppNavbar", AppNavbar); Vue.component("AppNavbar", AppNavbar);

View File

@ -0,0 +1,21 @@
.fade-enter-active, .fade-leave-active {
transition: opacity .5s, max-height .5s;
max-height: 300px;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
max-height: 0;
}
.expand-enter-active,
.expand-leave-active {
transition: height .5s ease-in-out;
overflow: hidden;
}
//.expand-enter,
//.expand-leave-to {
// height: 0;
//}

View File

@ -30,4 +30,6 @@ $modal-content-width: 750px;
// Make all Bulma variables and functions available // Make all Bulma variables and functions available
@import "~bulma/sass/utilities/initial-variables"; @import "~bulma/sass/utilities/initial-variables";
@import "~bulma/sass/utilities/functions"; @import "~bulma/sass/utilities/functions";
@import "~bulma/sass/utilities/derived-variables";
//$dropdown-item-hover-background-color: $white-ter;

View File

@ -15,6 +15,7 @@
@import "./responsive_controls"; @import "./responsive_controls";
@import "./wide_modal"; @import "./wide_modal";
@import "./iconic"; @import "./iconic";
@import "./transitions";
html { html {
height: 100%; height: 100%;