tasks
This commit is contained in:
parent
41117e2e7f
commit
4b25f753f1
@ -245,4 +245,4 @@ DEPENDENCIES
|
||||
webpacker (= 3.5.3)
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.1
|
||||
1.16.2
|
||||
|
66
app/javascript/components/AppExpandTransition.vue
Normal file
66
app/javascript/components/AppExpandTransition.vue
Normal 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>
|
60
app/javascript/components/TaskItemEdit.vue
Normal file
60
app/javascript/components/TaskItemEdit.vue
Normal 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>
|
98
app/javascript/components/TaskItemList.vue
Normal file
98
app/javascript/components/TaskItemList.vue
Normal 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>
|
72
app/javascript/components/TaskListDropdownItem.vue
Normal file
72
app/javascript/components/TaskListDropdownItem.vue
Normal 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>
|
@ -2,16 +2,9 @@
|
||||
<div>
|
||||
<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">
|
||||
<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">
|
||||
<div class="dropdown-item">
|
||||
@ -20,29 +13,9 @@
|
||||
</app-dropdown>
|
||||
|
||||
<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">
|
||||
No Items
|
||||
</div>
|
||||
<task-item-list :task-items="currentList.task_items"></task-item-list>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -53,6 +26,8 @@
|
||||
import api from "../lib/Api";
|
||||
import * as Errors from '../lib/Errors';
|
||||
import TaskListMiniForm from "./TaskListMiniForm";
|
||||
import TaskListDropdownItem from "./TaskListDropdownItem";
|
||||
import TaskItemList from "./TaskItemList";
|
||||
|
||||
const newListTemplate = function() {
|
||||
return {
|
||||
@ -60,13 +35,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
const newItemTemplate = function() {
|
||||
return {
|
||||
name: '',
|
||||
quantity: ''
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@ -74,11 +42,7 @@
|
||||
showListDropdown: false,
|
||||
currentList: null,
|
||||
newList: newListTemplate(),
|
||||
newListValidationErrors: {},
|
||||
showAddItem: false,
|
||||
newItem: newItemTemplate(),
|
||||
newItemValidationErrors: {},
|
||||
listToDelete: null
|
||||
newListValidationErrors: {}
|
||||
}
|
||||
},
|
||||
|
||||
@ -93,8 +57,8 @@
|
||||
},
|
||||
|
||||
methods: {
|
||||
selectList(l) {
|
||||
this.currentList = l;
|
||||
selectList(list) {
|
||||
this.currentList = list;
|
||||
this.showListDropdown = false;
|
||||
},
|
||||
|
||||
@ -124,11 +88,10 @@
|
||||
}
|
||||
},
|
||||
|
||||
confirmListDelete() {
|
||||
deleteList(list) {
|
||||
this.loadResource(
|
||||
api.deleteTaskList(this.listToDelete)
|
||||
api.deleteTaskList(list)
|
||||
.then(() => this.updateData())
|
||||
.then(() => this.listToDelete = null)
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -138,7 +101,9 @@
|
||||
},
|
||||
|
||||
components: {
|
||||
TaskListMiniForm
|
||||
TaskListMiniForm,
|
||||
TaskListDropdownItem,
|
||||
TaskItemList
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import AppConfirm from "../components/AppConfirm";
|
||||
import AppDateTime from "../components/AppDateTime";
|
||||
import AppDatePicker from "../components/AppDatePicker";
|
||||
import AppDropdown from "../components/AppDropdown";
|
||||
import AppExpandTransition from "../components/AppExpandTransition";
|
||||
import AppIcon from "../components/AppIcon";
|
||||
import AppModal from "../components/AppModal";
|
||||
import AppNavbar from "../components/AppNavbar";
|
||||
@ -29,6 +30,7 @@ Vue.component("AppConfirm", AppConfirm);
|
||||
Vue.component("AppDateTime", AppDateTime);
|
||||
Vue.component("AppDatePicker", AppDatePicker);
|
||||
Vue.component("AppDropdown", AppDropdown);
|
||||
Vue.component("AppExpandTransition", AppExpandTransition);
|
||||
Vue.component("AppIcon", AppIcon);
|
||||
Vue.component("AppModal", AppModal);
|
||||
Vue.component("AppNavbar", AppNavbar);
|
||||
|
21
app/javascript/styles/_transitions.scss
Normal file
21
app/javascript/styles/_transitions.scss
Normal 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;
|
||||
//}
|
@ -30,4 +30,6 @@ $modal-content-width: 750px;
|
||||
// Make all Bulma variables and functions available
|
||||
@import "~bulma/sass/utilities/initial-variables";
|
||||
@import "~bulma/sass/utilities/functions";
|
||||
@import "~bulma/sass/utilities/derived-variables";
|
||||
|
||||
//$dropdown-item-hover-background-color: $white-ter;
|
@ -15,6 +15,7 @@
|
||||
@import "./responsive_controls";
|
||||
@import "./wide_modal";
|
||||
@import "./iconic";
|
||||
@import "./transitions";
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
|
Loading…
Reference in New Issue
Block a user