Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(staggered-fade-and-slide): use Vue native staggered transition #1541

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export { default as TestCollapseWidth } from './test-collapse-width.vue';
export { default as TestCrossFade } from './test-cross-fade.vue';
export { default as TestFade } from './test-fade.vue';
export { default as TestFadeAndSlide } from './test-fade-and-slide.vue';
export { default as TestStaggeredFadeAndSlide } from './test-staggered-fade-and-slide.vue';
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<template>
<h1>Dinamic content:</h1>
<button @click="insert">Insert at random index</button>
<button @click="reset">Reset</button>

<Vue3StaggeredFadeAndSlide :stagger="500">
<li v-for="item in items" :key="item.id">
{{ item.id }} - {{ item.name }}
<button @click="remove(item)">x</button>
</li>
</Vue3StaggeredFadeAndSlide>

<br />
<h1>Animation as prop</h1>
<BaseSuggestions :suggestions="suggestions" :animation="Vue3StaggeredFadeAndSlide" :stagger="50">
<template #default="{ suggestion }">
<span>{{ suggestion.query }}</span>
</template>
</BaseSuggestions>

<br />
<h1>Static content:</h1>
<Vue3StaggeredFadeAndSlide :stagger="50">
<li key="1">Element to animate</li>
<li key="2">Element to animate</li>
<li key="3">Element to animate</li>
</Vue3StaggeredFadeAndSlide>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import Vue3StaggeredFadeAndSlide from '../../../../x-components/src/components/animations/vue3-staggered-fade-and-slide.vue';
import BaseSuggestions from '../../../../x-components/src/components/suggestions/base-suggestions.vue';
import {
getQuerySuggestionsStub,
createResultStub,
getResultsStub
} from '../../../../x-components/src/__stubs__';

const suggestions = getQuerySuggestionsStub('chip', 5);
const getInitialItems = () => getResultsStub(5);
const items = ref(getInitialItems());
let id = items.value.length + 1;

/**
* Insert a new item at a random index.
*/
function insert() {
const i = Math.round(Math.random() * items.value.length);
items.value.splice(i, 0, createResultStub(`Product ${id++}`));
}

/**
* Reset the list of items.
*/
function reset() {
items.value = getInitialItems();
id = items.value.length + 1;
}

/**
* Remove an item from the list.
*
* @param item - The item to remove.
*/
function remove(item: any) {
const i = items.value.indexOf(item);
if (i > -1) {
items.value.splice(i, 1);
}
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
style="--x-size-min-width-grid-item: 150px"
class="x-gap-12"
:columns="4"
:animation="Vue3StaggeredFadeAndSlide"
>
<template #result="{ item: result }">
<BaseResultLink :result="result">
Expand All @@ -38,6 +39,7 @@
import ClearSearchInput from '../../../x-components/src/x-modules/search-box/components/clear-search-input.vue';
import SearchButton from '../../../x-components/src/x-modules/search-box/components/search-button.vue';
import { use$x } from '../../../x-components/src/composables/use-$x';
import Vue3StaggeredFadeAndSlide from '../../../x-components/src/components/animations/vue3-staggered-fade-and-slide.vue';
// TODO `$x` name cannot be used while XPlugin defines its own `this.$x` in the mixin
const _$x = use$x();
</script>
Expand Down
6 changes: 6 additions & 0 deletions packages/_vue3-migration-test/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
TestFacets,
TestFade,
TestFadeAndSlide,
TestStaggeredFadeAndSlide,
TestScroll,
TestSortDropdown,
TestSortList,
Expand Down Expand Up @@ -87,6 +88,11 @@ const routes = [
name: 'FadeAndSlide',
component: TestFadeAndSlide
},
{
path: '/staggered-fade-and-slide',
name: 'StaggeredFadeAndSlide',
component: TestStaggeredFadeAndSlide
},
{
path: '/base-dropdown',
name: 'BaseDropdown',
Expand Down
1 change: 1 addition & 0 deletions packages/x-components/src/components/animations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export { default as CrossFade } from './cross-fade.vue';
export { default as Fade } from './fade.vue';
export { default as FadeAndSlide } from './fade-and-slide.vue';
export { default as StaggeredFadeAndSlide } from './staggered-fade-and-slide.vue';
export { default as Vue3StaggeredFadeAndSlide } from './vue3-staggered-fade-and-slide.vue';
export { default as StaggeringTransitionGroup } from './staggering-transition-group.vue';
export { createDirectionalAnimationFactory } from './create-directional-animation-factory';
export { animateClipPath } from './animate-clip-path/animate-clip-path.factory';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<template>
<!-- eslint-disable vue/attributes-order -->
<TransitionGroup
v-bind="$attrs"
v-on="$listeners"
@enter="enter"
@after-enter="afterEnter"
:appear="appear"
:name="name"
:tag="tag"
>
<slot />
</TransitionGroup>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import { useDisableAnimation } from './use-disable-animation';

/**
* Renders a transition group wrapping the elements passed in the default slot and animating
* them with a staggered fade and slide animation.
*
* @public
*/
export default defineComponent({
name: 'StaggeredFadeAndSlide',
inheritAttrs: false,
props: {
/**
* Indicates if the transition must be applied on the initial render of the node.
*/
appear: {
type: Boolean,
default: true
},
/**
* The tag of the node to render to the DOM.
*/
tag: {
type: String,
default: 'div'
},
/**
* The time in ms to stagger each item.
*/
stagger: {
type: Number,
default: 25
}
},
setup(props) {
/**
* The duration of the transition in ms.
*/
const transitionDuration = 250;
/**
* The counter to keep track of the staggered delay.
*/
const staggerCounter = ref(0);
/**
* The counter to keep track of the animations.
*/
const animationList = ref<HTMLElement[]>([]);
/**
* The name of the animation.
*/
const animationName = ref('x-staggered-fade-and-slide');
/**
* The name of the animation.
*/
const { name } = useDisableAnimation(animationName.value);

/**
* Calculate the delay for the next element.
*
* @returns The delay in ms.
*/
function getNextTransitionDelay(): number {
return staggerCounter.value++ * props.stagger;
}

/**
* The enter transition.
*
* @param el
* @param done
*/
function enter(el: HTMLElement, done: () => void) {
animationList.value.push(el);
const delay = getNextTransitionDelay();
el.style.transitionDelay = `${delay}ms`;
setTimeout(() => {
el.style.transitionDelay = '0ms';
done();
}, transitionDuration + delay);
}

/**
* The after enter transition.
*
* @param el
*/
function afterEnter(el: HTMLElement) {
animationList.value = animationList.value.filter(item => item !== el);
if (animationList.value.length === 0) {
staggerCounter.value = 0;
}
}

return {
name,
enter,
afterEnter
};
}
});
</script>

<style lang="scss">
$transition-duration: 0.25s;
/* 1. Declare transitions */
.x-staggered-fade-and-slide-enter-active,
.x-staggered-fade-and-slide-leave-active {
transition: $transition-duration ease-out;
transition-property: opacity, transform;
}

.x-staggered-fade-and-slide-move {
transition: transform $transition-duration ease-out;
}

/* 2. declare enter from and leave to state */
.x-staggered-fade-and-slide-enter,
.x-staggered-fade-and-slide-enter-from,
.x-staggered-fade-and-slide-leave-to {
opacity: 0;
transform: translate3d(0, 50%, 0);
}

/* 3. ensure leaving items are taken out of layout flow so that moving
animations can be calculated correctly. */
.x-staggered-fade-and-slide-leave-active {
position: absolute;
}
</style>

<docs lang="mdx">
The Staggered fade and slide components works as the normal fade and slide components, but it also
adds a configurable delay to each transition.

## Example

### Used with animatable components

```vue
<AnimatableComponent :animation="StaggeredFadeAndSlide" />
```

### Used as a regular component:

This components exposes all the props and events of the Staggering transition group, like the `tag`
or the `stagger` props:

```vue
<StaggeredFadeAndSlide tag="ul" :stagger="50">
<li key="1">Element to animate</li>
<li key="2">Element to animate</li>
<li key="3">Element to animate</li>
</StaggeredFadeAndSlide>
```
</docs>
Loading
Loading