tasks
This commit is contained in:
parent
41117e2e7f
commit
4b25f753f1
@ -245,4 +245,4 @@ DEPENDENCIES
|
|||||||
webpacker (= 3.5.3)
|
webpacker (= 3.5.3)
|
||||||
|
|
||||||
BUNDLED WITH
|
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>
|
<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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
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
|
// 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;
|
@ -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%;
|
||||||
|
Loading…
Reference in New Issue
Block a user