Fix card header expansion glitch on card collapse
This commit fixes an issue where the card's header would improperly
expand to full height during card collapse, leading to a less smooth
user experience. Previously, this was caused by the indiscriminate use
of `transition: all` in the `.card__expander`, which included unwanted
properties in the transition during collapse, such as height. This is
solved by using Vue transitions to apply transition only during
expansion.
Changes:
- Introduce a new Vue component, `CardExpandAnimation`:
- Centralizes the animation process, applying the same animation to
both the card and its arrow for consistency.
- Resolves the glitch by adjusting classes exclusively during the
enter animation phase, avoiding unintended side effects during leave
animation phase.
- Adopts a Vue-idiomatic approach for transition management, improving
code readability and maintainability.
- Improves separation of concerns by isolating animation logic from
the component's core functionality, facilitating easier updates or
replacements.
- Remove unnecessary transitions to enhance code simplicity and
performance:
- Remove `transition: all` on `.card__expander`, which was identified
as the cause of the issue.
- Remove unnecessary `transition: all` on `.card`.
- Adjust transitions to specifically target and affect the transform
property (instead of `all`) to optimize animation behavior and
eliminate potential side-effects.
These changes not only fix the issue at hand but also contribute to a
more maintainable and performant codebase by clarifying animation logic
and reducing unnecessary CSS transitions.
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'InstructionSteps', // Define component name for empty component for Vue build and ESLint compatibility.
|
// Empty component for ESLint compatibility, workaround for https://github.com/vuejs/vue-eslint-parser/issues/125.
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,6 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'InstructionSteps', // Define component name for empty component for Vue build and ESLint compatibility.
|
// Empty component for ESLint compatibility, workaround for https://github.com/vuejs/vue-eslint-parser/issues/125.
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<transition name="card-expand-collapse-transition">
|
||||||
|
<slot />
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
// Empty component for ESLint compatibility, workaround for https://github.com/vuejs/vue-eslint-parser/issues/125.
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@use "@/presentation/assets/styles/main" as *;
|
||||||
|
|
||||||
|
.card-expand-collapse-transition-enter-active {
|
||||||
|
transition:
|
||||||
|
opacity 0.3s ease-in-out,
|
||||||
|
max-height 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-expand-collapse-transition-enter-from {
|
||||||
|
opacity: 0; // Fade-in
|
||||||
|
max-height: 0; // Expand
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
ref="cardElement"
|
ref="cardElement"
|
||||||
class="card"
|
class="card"
|
||||||
:class="{
|
:class="{
|
||||||
'is-collapsed': !isExpanded,
|
|
||||||
'is-inactive': activeCategoryId && activeCategoryId !== categoryId,
|
'is-inactive': activeCategoryId && activeCategoryId !== categoryId,
|
||||||
'is-expanded': isExpanded,
|
'is-expanded': isExpanded,
|
||||||
}"
|
}"
|
||||||
@@ -29,20 +28,26 @@
|
|||||||
:category-id="categoryId"
|
:category-id="categoryId"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="card__expander" @click.stop>
|
<CardExpandTransition>
|
||||||
<div class="card__expander__close-button">
|
<div
|
||||||
<FlatButton
|
v-show="isExpanded"
|
||||||
icon="xmark"
|
class="card__expander"
|
||||||
@click="collapse()"
|
@click.stop
|
||||||
/>
|
>
|
||||||
|
<div class="card__expander__close-button">
|
||||||
|
<FlatButton
|
||||||
|
icon="xmark"
|
||||||
|
@click="collapse()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="card__expander__content">
|
||||||
|
<ScriptsTree
|
||||||
|
:category-id="categoryId"
|
||||||
|
:has-top-padding="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card__expander__content">
|
</CardExpandTransition>
|
||||||
<ScriptsTree
|
|
||||||
:category-id="categoryId"
|
|
||||||
:has-top-padding="false"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -56,6 +61,7 @@ import { injectKey } from '@/presentation/injectionSymbols';
|
|||||||
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
|
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
|
||||||
import { sleep } from '@/infrastructure/Threading/AsyncSleep';
|
import { sleep } from '@/infrastructure/Threading/AsyncSleep';
|
||||||
import CardSelectionIndicator from './CardSelectionIndicator.vue';
|
import CardSelectionIndicator from './CardSelectionIndicator.vue';
|
||||||
|
import CardExpandTransition from './CardExpandTransition.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@@ -63,6 +69,7 @@ export default defineComponent({
|
|||||||
AppIcon,
|
AppIcon,
|
||||||
CardSelectionIndicator,
|
CardSelectionIndicator,
|
||||||
FlatButton,
|
FlatButton,
|
||||||
|
CardExpandTransition,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
categoryId: {
|
categoryId: {
|
||||||
@@ -134,8 +141,6 @@ $expanded-margin-top : 30px;
|
|||||||
$card-horizontal-gap : $card-gap;
|
$card-horizontal-gap : $card-gap;
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
|
|
||||||
&__inner {
|
&__inner {
|
||||||
padding-top: $card-inner-padding;
|
padding-top: $card-inner-padding;
|
||||||
padding-right: $card-inner-padding;
|
padding-right: $card-inner-padding;
|
||||||
@@ -149,7 +154,7 @@ $card-horizontal-gap : $card-gap;
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: all 0.2s ease-in-out;
|
transition: transform 0.2s ease-in-out;
|
||||||
|
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -160,9 +165,6 @@ $card-horizontal-gap : $card-gap;
|
|||||||
color: $color-on-secondary;
|
color: $color-on-secondary;
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
&:after {
|
|
||||||
transition: all 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
.card__inner__title {
|
.card__inner__title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -185,7 +187,6 @@ $card-horizontal-gap : $card-gap;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.card__expander {
|
.card__expander {
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: $color-primary-darker;
|
background-color: $color-primary-darker;
|
||||||
color: $color-on-primary;
|
color: $color-on-primary;
|
||||||
@@ -214,22 +215,6 @@ $card-horizontal-gap : $card-gap;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-collapsed {
|
|
||||||
.card__inner {
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__expander {
|
|
||||||
max-height: 0;
|
|
||||||
min-height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-expanded {
|
&.is-expanded {
|
||||||
.card__inner {
|
.card__inner {
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -249,7 +234,6 @@ $card-horizontal-gap : $card-gap;
|
|||||||
|
|
||||||
.card__expander {
|
.card__expander {
|
||||||
margin-top: $expanded-margin-top;
|
margin-top: $expanded-margin-top;
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include hover-or-touch {
|
@include hover-or-touch {
|
||||||
|
|||||||
Reference in New Issue
Block a user