parsley/app/javascript/components/RecipeEditIngredientEditor.vue

227 lines
6.1 KiB
Vue
Raw Normal View History

2018-04-01 21:43:23 -05:00
<template>
<div>
2018-09-12 14:17:18 -05:00
<h3 class="title is-4">
Ingredients
<button type="button" class="button is-primary" @click="bulkEditIngredients">Bulk Edit</button>
</h3>
2018-05-01 10:55:57 -05:00
<app-modal wide :open="isBulkEditing" title="Edit Ingredients" @dismiss="cancelBulkEditing">
2018-04-04 19:46:02 -05:00
<div class="columns">
2018-05-01 10:55:57 -05:00
<div class="column is-half bulk-input">
<textarea ref="bulkEditTextarea" class="textarea is-size-7-mobile" v-model="bulkEditText"></textarea>
2018-04-01 21:43:23 -05:00
</div>
<div class="column is-half">
<table class="table is-bordered is-narrow is-size-7">
<thead>
2018-04-01 21:43:23 -05:00
<tr>
<th>#</th>
<th>Unit</th>
<th>Name</th>
<th>Prep</th>
</tr>
</thead>
<tbody>
2018-04-01 21:43:23 -05:00
<tr v-for="i in bulkIngredientPreview">
<td>{{i.quantity}}</td>
<td>{{i.units}}</td>
<td>{{i.name}}</td>
<td>{{i.preparation}}</td>
</tr>
</tbody>
2018-04-01 21:43:23 -05:00
</table>
</div>
</div>
<button class="button is-primary" type="button" @click="saveBulkEditing">Save</button>
<button class="button is-secondary" type="button" @click="cancelBulkEditing">Cancel</button>
</app-modal>
2018-05-01 10:55:57 -05:00
<div>
2018-09-11 22:56:26 -05:00
<recipe-edit-ingredient-item v-for="(i, idx) in visibleIngredients" :key="i.id" :ingredient="i" :show-labels="idx === 0 || isMobile" @deleteFood="deleteFood"></recipe-edit-ingredient-item>
2018-05-01 10:55:57 -05:00
</div>
2018-04-01 21:43:23 -05:00
<button type="button" class="button is-primary" @click="addIngredient">Add Ingredient</button>
</div>
</template>
<script>
import RecipeEditIngredientItem from "./RecipeEditIngredientItem";
import { mapState } from "pinia";
import { useMediaQueryStore } from "../stores/mediaQuery";
2018-04-01 21:43:23 -05:00
export default {
props: {
ingredients: {
required: true,
type: Array
}
},
data() {
return {
isBulkEditing: false,
bulkEditText: null
};
},
computed: {
...mapState(useMediaQueryStore, {
isMobile: store => store.mobile
2018-04-01 21:43:23 -05:00
}),
bulkIngredientPreview() {
if (this.bulkEditText === null) {
return [];
}
2018-05-01 10:55:57 -05:00
const regex = /^\s*(?:([\d\/.]+(?:\s+[\d\/]+)?)\s+)?(?:([\w-]+)(?:\s+of)?\s+)?([^,|]+?|.+\|)(?:,\s*([^|]*?))?(?:\s*\[(\d+)\]\s*)?$/i;
2018-04-01 21:43:23 -05:00
const magicFunc = function(str) {
if (str === "-") {
return "";
} else {
return str;
}
};
const parsed = [];
const lines = this.bulkEditText.replace("\r", "").split("\n");
for (let line of lines) {
if (line.length === 0) { continue; }
const match = line.match(regex);
if (match) {
2018-04-03 10:29:57 -05:00
const matchedName = match[3].replace(/\|\s*$/, "");
let item = {quantity: magicFunc(match[1]), units: magicFunc(match[2]), name: magicFunc(matchedName), preparation: magicFunc(match[4]), id: match[5] || null};
2018-04-01 21:43:23 -05:00
parsed.push(item);
} else {
parsed.push(null);
}
}
return parsed;
},
visibleIngredients() {
return this.ingredients.filter(i => i._destroy !== true);
}
},
methods: {
2018-04-03 10:29:57 -05:00
createIngredient() {
2018-09-12 14:17:18 -05:00
const sort_orders = this.ingredients.map(i => i.sort_order);
sort_orders.push(0);
const next_sort_order = Math.max(...sort_orders) + 5;
2018-04-03 10:29:57 -05:00
return {
id: null,
2018-04-01 21:43:23 -05:00
quantity: null,
units: null,
name: null,
preparation: null,
ingredient_id: null,
2018-09-12 14:17:18 -05:00
sort_order: next_sort_order
2018-04-03 10:29:57 -05:00
};
},
addIngredient() {
this.ingredients.push(this.createIngredient());
2018-04-01 21:43:23 -05:00
},
2018-09-11 22:56:26 -05:00
deleteFood(food) {
if (food.id) {
food._destroy = true;
2018-04-01 21:43:23 -05:00
} else {
2018-09-11 22:56:26 -05:00
const idx = this.ingredients.findIndex(i => i === food);
2018-04-01 21:43:23 -05:00
this.ingredients.splice(idx, 1);
}
},
bulkEditIngredients() {
this.isBulkEditing = true;
let text = [];
for (let item of this.visibleIngredients) {
text.push(
item.quantity + " " +
(item.units || "-") + " " +
2018-04-03 10:29:57 -05:00
(item.name.indexOf(",") >= 0 ? item.name + "|" : item.name) +
(item.preparation ? (", " + item.preparation) : "") +
(item.id ? (" [" + item.id + "]") : "")
2018-04-01 21:43:23 -05:00
);
}
this.bulkEditText = text.join("\n");
},
cancelBulkEditing() {
this.isBulkEditing = false;
},
saveBulkEditing() {
2018-04-03 10:29:57 -05:00
const parsed = this.bulkIngredientPreview.filter(i => i !== null);
const existing = [...this.ingredients];
const newList = [];
for (let parsedIngredient of parsed) {
let newIngredient = null;
if (parsedIngredient.id !== null) {
let intId = parseInt(parsedIngredient.id);
let exIdx = existing.findIndex(i => i.id === intId);
if (exIdx >= 0) {
let ex = existing[exIdx];
if (ex.name === parsedIngredient.name) {
newIngredient = ex;
existing.splice(exIdx, 1);
}
}
}
if (newIngredient === null) {
newIngredient = this.createIngredient();
}
newIngredient.quantity = parsedIngredient.quantity;
newIngredient.units = parsedIngredient.units;
newIngredient.name = parsedIngredient.name;
newIngredient.preparation = parsedIngredient.preparation;
newList.push(newIngredient);
}
for (let oldExisting of existing.filter(i => i.id !== null)) {
newList.push({id: oldExisting.id, _destroy: true});
}
this.ingredients.splice(0);
let sortIdx = 0;
for (let n of newList) {
n.sort_order = sortIdx++;
this.ingredients.push(n);
}
2018-04-01 21:43:23 -05:00
this.isBulkEditing = false;
}
},
components: {
RecipeEditIngredientItem
}
}
</script>
<style lang="scss" scoped>
2018-04-04 19:46:02 -05:00
2018-05-01 10:55:57 -05:00
.bulk-input {
2018-04-04 19:46:02 -05:00
2018-05-01 10:55:57 -05:00
textarea {
height: 100%;
min-height: 15rem;
}
}
2018-04-04 19:46:02 -05:00
2018-04-01 21:43:23 -05:00
</style>