From 7c46be622756be338ee9d3041e984e0a018e944e Mon Sep 17 00:00:00 2001 From: webfussel <fiona@webfussel.de> Date: Mon, 24 Feb 2025 21:38:45 +0100 Subject: [PATCH] add: collapsing and deletion animation Added collapsable cards and animation for deletion and card footer --- app/assets/styles/button.css | 17 ++++++-- app/assets/styles/general.css | 7 +++ app/assets/styles/priceCard.css | 38 ++++++++++++++--- app/assets/styles/toolbar.css | 1 + app/components/Pp/PriceCard.vue | 75 +++++++++++++++++++++++++-------- app/pages/index.vue | 12 +++++- 6 files changed, 121 insertions(+), 29 deletions(-) diff --git a/app/assets/styles/button.css b/app/assets/styles/button.css index ee545bd..c247d2c 100644 --- a/app/assets/styles/button.css +++ b/app/assets/styles/button.css @@ -1,5 +1,9 @@ .Button { --padding: .2rem; + --background: var(--color-main); + --color: var(--color-white); + --background-hover: var(--color-main-dark); + display: flex; justify-content: center; align-items: center; @@ -9,19 +13,26 @@ outline: none; border: none; background: transparent; + color: var(--color); &.cta { - background: var(--color-main); - color: var(--color-white); + background: var(--background); + color: var(--color); padding: .5rem 1.5rem; border-radius: var(--radius-default); box-shadow: var(--box-shadow-z2); &:hover { - background: var(--color-main-dark); + background: var(--background-hover); } } + &.cta.white { + --background: var(--color-white); + --color: var(--color-main); + --background-hover: var(--color-grey); + } + &.icon-button { display: flex; padding: var(--padding); diff --git a/app/assets/styles/general.css b/app/assets/styles/general.css index 2d68fc6..ad3b46c 100644 --- a/app/assets/styles/general.css +++ b/app/assets/styles/general.css @@ -14,7 +14,9 @@ --color-main-light: var(--color-blue-light); --color-main-dark: var(--color-blue-dark); + --box-shadow-upper: 0 -3px 6px rgba(0,0,0,0.16), 0 -3px 6px rgba(0,0,0,0.23); --box-shadow-z2: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); + --box-shadow-inset: inset 0 3px 6px rgba(0,0,0,0.16), inset 0 3px 6px rgba(0,0,0,0.23); } * { @@ -52,6 +54,11 @@ body { flex-direction: column; } +.flex-row { + display: flex; + flex-direction: row; +} + .text-white { color: var(--color-white); } diff --git a/app/assets/styles/priceCard.css b/app/assets/styles/priceCard.css index ff44c5b..92ad006 100644 --- a/app/assets/styles/priceCard.css +++ b/app/assets/styles/priceCard.css @@ -1,34 +1,60 @@ .PriceCard { + --height: auto; width: 100%; + display: grid; + transition: var(--transition-default); + grid-template-rows: auto 1fr auto; + height: var(--height); + + &.deleting { + height: 0; + } + + &.folded { + grid-template-rows: auto 0fr auto; + } & > header { - color: white; + color: var(--color-white); display: flex; justify-content: space-between; align-items: center; font-size: 1.3em; & > .Button { - scale: 0; color: var(--color-white); border: 2px solid var(--color-white); } + } + + & aside { + overflow: hidden; + } + + & footer { + display: flex; + justify-content: space-between; + align-items: center; + + & > .Button.delete { + scale: 0; + } & > .Button.deletable { scale: 1; } } - & > .padding { + & .padding { gap: 1rem; padding: var(--padding-default); } - & > .bg-blue { + & .bg-blue { background: var(--color-blue); } - & > .bg-white { + & .bg-white { background: var(--color-white); } @@ -40,7 +66,7 @@ justify-content: space-between; & > * { - flex-basis: 30%; + flex-basis: 25%; flex-grow: 1; } diff --git a/app/assets/styles/toolbar.css b/app/assets/styles/toolbar.css index fdb3a25..44f305f 100644 --- a/app/assets/styles/toolbar.css +++ b/app/assets/styles/toolbar.css @@ -4,6 +4,7 @@ background: var(--color-main); position: sticky; bottom: 0; + box-shadow: var(--box-shadow-upper); & > .Button { --padding: 1rem; diff --git a/app/components/Pp/PriceCard.vue b/app/components/Pp/PriceCard.vue index 21fc886..0aa0e99 100644 --- a/app/components/Pp/PriceCard.vue +++ b/app/components/Pp/PriceCard.vue @@ -1,27 +1,34 @@ <template> - <form class="PriceCard card"> + <form + ref="root" + class="PriceCard card" + :class="{ folded }" + :style="{ '--height': height }" + @submit.prevent="() => {}" + > <header class="padding bg-blue"> - TODO: IRGENDWAS + {{ name || 'Kein Name' }} <PpButton class="icon-button bg-main" - :class="[deletable && 'deletable']" - @click="emit('remove', uid)" + @click="folded = !folded" > - <Icon name="uil:times" mode="svg" /> + <Icon :name="folded ? 'uil:sort' : 'uil:sorting'" mode="svg" /> </PpButton> </header> - <div class="padding bg-blue flex-col"> - <div class="wrapper"> -<!-- <PpFormInput v-model="name" label="Name" id="n" :uid="uid" type="text" />--> - <PpFormInput v-model="price" label="Preis" id="p" :uid="uid" type="number" :min="0.01" @blur="calculate" /> + <aside> + <div class="input-wrapper padding bg-blue flex-col"> + <div class="wrapper"> + <PpFormInput v-model="name" label="Name" id="n" :uid="uid" type="text" /> + <PpFormInput v-model="price" label="Preis" id="p" :uid="uid" type="number" :min="0.01" @blur="calculate" /> + </div> + <div class="wrapper"> + <PpFormInput v-model="roles" label="Rollen" id="r" :uid="uid" type="number" :max="150" @blur="calculate" /> + <PpFormInput v-model="sheets" label="Blätter" id="b" :uid="uid" type="number" :max="500" @blur="calculate" /> + <PpFormInput v-model="layers" label="Lagen" id="l" :uid="uid" type="number" :max="10" @blur="calculate" /> + </div> </div> - <div class="wrapper"> - <PpFormInput v-model="roles" label="Rollen" id="r" :uid="uid" type="number" :max="150" @blur="calculate" /> - <PpFormInput v-model="sheets" label="Blätter" id="b" :uid="uid" type="number" :max="500" @blur="calculate" /> - <PpFormInput v-model="layers" label="Lagen" id="l" :uid="uid" type="number" :max="10" @blur="calculate" /> - </div> - </div> - <div class="wrapper padding bg-white"> + </aside> + <main class="wrapper padding bg-white"> <div class="info flex-col"> <Icon class="icon" name="uil:toilet-paper" mode="svg" /> <span class="price">{{ intl.format(ppr) }}</span> @@ -37,20 +44,42 @@ <span class="price">{{ intl.format(ppl) }}</span> <span class="pro">Pro 100</span> </div> - </div> + </main> + <footer class="padding bg-blue flex-row"> + <PpButton + class="delete" + :class="{ deletable }" + @click="deleteCard" + > + <Icon name="uil:trash" mode="svg" /> + Entfernen + </PpButton> + <PpButton class="cta white"> + <Icon name="uil:qrcode-scan" mode="svg" /> + Scan + </PpButton> + </footer> </form> </template> <script setup lang="ts"> +import { nextTick } from '@redocly/openapi-core/src/utils' + type Props = { uid: string deletable: boolean } const { uid } = defineProps<Props>() - const emit = defineEmits(['remove']) +const root = ref<HTMLElement>() +const height = ref<string>() +const folded = ref(false) + +onMounted(() => { +}) + const name = ref('') const price = ref(0) const roles = ref(0) @@ -76,4 +105,14 @@ const calculate = () => { if(!layers.value) return ppl.value = (pps.value / layers.value) * 10 } + +const deleteCard = async () => { + root.value?.addEventListener('transitionend', () => { + emit('remove', uid) + }) + + height.value = `${root.value?.offsetHeight}px` + await nextTick() + root.value?.classList.add('deleting') +} </script> diff --git a/app/pages/index.vue b/app/pages/index.vue index 5c51e3a..4530791 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -23,14 +23,22 @@ </template> <script setup lang="ts"> -const initialId = crypto.randomUUID() +const randomUUID = () => { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, char => { + const random = Math.random() * 16 | 0 + const value = char === 'x' ? random : (random & 0x3 | 0x8) + return value.toString(16) + }) +} + +const initialId = randomUUID() const cards = useState('cards', () => [ initialId, ]) const addCard = () => { - cards.value.push(crypto.randomUUID()) + cards.value.push(randomUUID()) } const removeCard = (uuid : string) => {