parsley/app/javascript/components/RecipeEditIngredientEditor.vue

209 lines
5.7 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>
2024-10-02 14:34:50 -05:00
<script setup>
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
import { computed, ref } from "vue";
import { useMediaQueryStore } from "../stores/mediaQuery";
2018-04-01 21:43:23 -05:00
import RecipeEditIngredientItem from "./RecipeEditIngredientItem";
2024-10-02 14:34:50 -05:00
const mediaQueryStore = useMediaQueryStore();
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
const props = defineProps({
ingredients: {
required: true,
type: Array
}
});
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
const isBulkEditing = ref(false);
const bulkEditText = ref(null);
const isMobile = computed(() => mediaQueryStore.mobile);
const visibleIngredients = computed(() => props.ingredients.filter(i => i._destroy !== true));
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
const bulkIngredientPreview = computed(() => {
if (bulkEditText.value === null || bulkEditText.value === "") {
return [];
}
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -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
2024-10-02 14:34:50 -05:00
const magicFunc = function(str) {
if (str === "-") {
return "";
} else {
return str;
}
};
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
const parsed = [];
const lines = bulkEditText.value.replace("\r", "").split("\n");
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
for (let line of lines) {
if (line.length === 0) { continue; }
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
const match = line.match(regex);
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
if (match) {
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};
parsed.push(item);
} else {
parsed.push(null);
2018-04-01 21:43:23 -05:00
}
2024-10-02 14:34:50 -05:00
}
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
return parsed;
});
function createIngredient() {
const sort_orders = props.ingredients.map(i => i.sort_order);
sort_orders.push(0);
const next_sort_order = Math.max(...sort_orders) + 5;
return {
id: null,
quantity: null,
units: null,
name: null,
preparation: null,
ingredient_id: null,
sort_order: next_sort_order
};
}
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
function addIngredient() {
props.ingredients.push(createIngredient());
}
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
function deleteFood(food) {
if (food.id) {
food._destroy = true;
} else {
const idx = props.ingredients.findIndex(i => i === food);
props.ingredients.splice(idx, 1);
}
}
2018-04-01 21:43:23 -05:00
2024-10-02 14:34:50 -05:00
function bulkEditIngredients() {
isBulkEditing.value = true;
2018-04-03 10:29:57 -05:00
2024-10-02 14:34:50 -05:00
let text = [];
2018-04-03 10:29:57 -05:00
2024-10-02 14:34:50 -05:00
for (let item of visibleIngredients.value) {
text.push(
item.quantity + " " +
(item.units || "-") + " " +
(item.name.indexOf(",") >= 0 ? item.name + "|" : item.name) +
(item.preparation ? (", " + item.preparation) : "") +
(item.id ? (" [" + item.id + "]") : "")
);
}
2018-04-03 10:29:57 -05:00
2024-10-02 14:34:50 -05:00
bulkEditText.value = text.join("\n");
}
function cancelBulkEditing() {
isBulkEditing.value = false;
}
2018-04-03 10:29:57 -05:00
2024-10-02 14:34:50 -05:00
function saveBulkEditing() {
const parsed = bulkIngredientPreview.value.filter(i => i !== null);
const existing = [...props.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);
}
2018-04-03 10:29:57 -05:00
}
2024-10-02 14:34:50 -05:00
}
2018-04-03 10:29:57 -05:00
2024-10-02 14:34:50 -05:00
if (newIngredient === null) {
newIngredient = createIngredient();
2018-04-01 21:43:23 -05:00
}
2024-10-02 14:34:50 -05:00
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});
}
props.ingredients.splice(0);
let sortIdx = 0;
for (let n of newList) {
n.sort_order = sortIdx++;
props.ingredients.push(n);
2018-04-01 21:43:23 -05:00
}
2024-10-02 14:34:50 -05:00
isBulkEditing.value = false;
2018-04-01 21:43:23 -05:00
}
</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>