mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-04 21:15:22 +02:00
feat: Shopping list UI overhaul - collapsible labels (#4378)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
parent
ae9276b55c
commit
2240ab01d2
1 changed files with 51 additions and 10 deletions
|
@ -57,18 +57,22 @@
|
||||||
<!-- View By Label -->
|
<!-- View By Label -->
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div v-for="(value, key) in itemsByLabel" :key="key" class="mb-6">
|
<div v-for="(value, key) in itemsByLabel" :key="key" class="mb-6">
|
||||||
<div class="text-left">
|
|
||||||
<v-btn
|
<v-btn
|
||||||
:color="getLabelColor(value[0]) ? getLabelColor(value[0]) : '#959595'"
|
:color="getLabelColor(value[0]) ? getLabelColor(value[0]) : '#959595'"
|
||||||
:style="{
|
:style="{
|
||||||
'color': getTextColor(getLabelColor(value[0])),
|
'color': getTextColor(getLabelColor(value[0])),
|
||||||
'letter-spacing': 'normal',
|
'letter-spacing': 'normal',
|
||||||
}"
|
}"
|
||||||
|
@click="toggleShowLabel(key)"
|
||||||
>
|
>
|
||||||
|
<v-icon>
|
||||||
|
{{ labelOpenState[key] ? $globals.icons.chevronDown : $globals.icons.chevronRight }}
|
||||||
|
</v-icon>
|
||||||
{{ key }}
|
{{ key }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
|
||||||
<v-divider/>
|
<v-divider/>
|
||||||
|
<v-expand-transition group>
|
||||||
|
<div v-show="labelOpenState[key]">
|
||||||
<draggable :value="value" handle=".handle" delay="250" :delay-on-touch-only="true" @start="loadingCounter += 1" @end="loadingCounter -= 1" @input="updateIndexUncheckedByLabel(key, $event)">
|
<draggable :value="value" handle=".handle" delay="250" :delay-on-touch-only="true" @start="loadingCounter += 1" @end="loadingCounter -= 1" @input="updateIndexUncheckedByLabel(key, $event)">
|
||||||
<v-lazy v-for="(item, index) in value" :key="item.id" class="ml-2 my-2">
|
<v-lazy v-for="(item, index) in value" :key="item.id" class="ml-2 my-2">
|
||||||
<ShoppingListItem
|
<ShoppingListItem
|
||||||
|
@ -85,6 +89,8 @@
|
||||||
</v-lazy>
|
</v-lazy>
|
||||||
</draggable>
|
</draggable>
|
||||||
</div>
|
</div>
|
||||||
|
</v-expand-transition>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Reorder Labels -->
|
<!-- Reorder Labels -->
|
||||||
|
@ -301,7 +307,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import draggable from "vuedraggable";
|
import draggable from "vuedraggable";
|
||||||
|
|
||||||
import { defineComponent, useRoute, computed, ref, toRefs, onUnmounted, useContext, reactive } from "@nuxtjs/composition-api";
|
import { defineComponent, useRoute, computed, ref, toRefs, onUnmounted, useContext, reactive, watch } from "@nuxtjs/composition-api";
|
||||||
import { useIdle, useToggle } from "@vueuse/core";
|
import { useIdle, useToggle } from "@vueuse/core";
|
||||||
import { useCopyList } from "~/composables/use-copy";
|
import { useCopyList } from "~/composables/use-copy";
|
||||||
import { useUserApi } from "~/composables/api";
|
import { useUserApi } from "~/composables/api";
|
||||||
|
@ -463,6 +469,39 @@ export default defineComponent({
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// =====================================
|
||||||
|
// Collapsables
|
||||||
|
const labelOpenState = ref<{ [key: string]: boolean }>({});
|
||||||
|
|
||||||
|
const initializeLabelOpenStates = () => {
|
||||||
|
if (!shoppingList.value?.listItems) return;
|
||||||
|
|
||||||
|
const existingLabels = new Set(Object.keys(labelOpenState.value));
|
||||||
|
let hasChanges = false;
|
||||||
|
|
||||||
|
for (const item of shoppingList.value.listItems) {
|
||||||
|
const labelName = item.label?.name;
|
||||||
|
if (labelName && !existingLabels.has(labelName) && !(labelName in labelOpenState.value)) {
|
||||||
|
labelOpenState.value[labelName] = true;
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChanges) {
|
||||||
|
labelOpenState.value = { ...labelOpenState.value };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const labelNames = computed(() =>
|
||||||
|
new Set(shoppingList.value?.listItems?.map(item => item.label?.name).filter(Boolean) ?? [])
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(labelNames, initializeLabelOpenStates, { immediate: true });
|
||||||
|
|
||||||
|
function toggleShowLabel(key: string) {
|
||||||
|
labelOpenState.value[key] = !labelOpenState.value[key];
|
||||||
|
}
|
||||||
|
|
||||||
const [showChecked, toggleShowChecked] = useToggle(false);
|
const [showChecked, toggleShowChecked] = useToggle(false);
|
||||||
|
|
||||||
// =====================================
|
// =====================================
|
||||||
|
@ -1090,6 +1129,8 @@ export default defineComponent({
|
||||||
shoppingList,
|
shoppingList,
|
||||||
showChecked,
|
showChecked,
|
||||||
sortByLabels,
|
sortByLabels,
|
||||||
|
labelOpenState,
|
||||||
|
toggleShowLabel,
|
||||||
toggleShowChecked,
|
toggleShowChecked,
|
||||||
uncheckAll,
|
uncheckAll,
|
||||||
openUncheckAll,
|
openUncheckAll,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue