Skip to content

Commit

Permalink
Merge pull request #113 from kalix127/fix/animated-list-loop
Browse files Browse the repository at this point in the history
fix: add loop animation for the AnimatedList.vue
  • Loading branch information
rahul-vashishtha authored Jan 17, 2025
2 parents 179865e + 35417a0 commit dfcaeef
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 61 deletions.
29 changes: 4 additions & 25 deletions components/content/inspira/examples/AnimatedListDemo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,32 +49,11 @@ const notifications = [
color: "#1E86FF",
},
{
name: "Payment received",
name: "Task completed",
description: "Inspira UI",
time: "15m ago",
icon: "💸",
color: "#00C9A7",
},
{
name: "User signed up",
description: "Inspira UI",
time: "10m ago",
icon: "👤",
color: "#FFB800",
},
{
name: "New message",
description: "Inspira UI",
time: "5m ago",
icon: "💬",
color: "#FF3D71",
},
{
name: "New event",
description: "Inspira UI",
time: "2m ago",
icon: "🗞️",
color: "#1E86FF",
time: "1m ago",
icon: "",
color: "#45B26B",
},
];
</script>
73 changes: 37 additions & 36 deletions components/content/inspira/ui/animated-list/AnimatedList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,21 @@
>
<!-- Only render the items up to the current index -->
<div
v-for="(item, idx) in itemsToShow"
:key="idx"
v-for="(data, idx) in itemsToShow"
:key="data.id"
v-motion
:initial="getInitial(idx)"
:enter="getEnter(idx)"
:leave="getLeave()"
:class="cn('mx-auto w-full')"
>
<component :is="item" />
<component :is="data.node" />
</div>
</transition-group>
</div>
</template>

<script lang="ts" setup>
import type { Slot, VNodeNormalizedChildren } from "vue";
import { cn } from "~/lib/utils";
interface Props {
Expand All @@ -36,54 +35,56 @@ const props = withDefaults(defineProps<Props>(), {
});
const slots = useSlots();
const index = ref(0);
const displayedItems = ref<{ node: unknown; id: string }[]>([]);
const nextIndex = ref(0);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const slotsArray = ref<any>([]);
onMounted(startLoop);
onMounted(loadComponents);
async function startLoop() {
const notifications = slots.default ? (slots.default()[0].children ?? []) : [];
if (!notifications.length) return;
const itemsToShow = computed(() => {
return slotsArray.value.slice(0, index.value);
});
async function loadComponents() {
slotsArray.value = slots.default ? slots.default()[0].children : [];
while (displayedItems.value.length < notifications.length) {
displayedItems.value.push({
node: notifications[nextIndex.value],
id: `${nextIndex.value}-${Date.now()}`,
});
nextIndex.value = (nextIndex.value + 1) % notifications.length;
await wait(props.delay);
}
while (index.value < slotsArray.value.length) {
index.value++;
while (true) {
displayedItems.value.shift();
displayedItems.value.push({
node: notifications[nextIndex.value],
id: `${nextIndex.value}-${Date.now()}`,
});
nextIndex.value = (nextIndex.value + 1) % notifications.length;
await wait(props.delay);
}
}
const itemsToShow = computed(() => displayedItems.value);
async function wait(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// Get initial animation (all items appear with no scale and opacity 0)
function getInitial(idx: number) {
return idx === index.value - 1
? {
scale: 0,
opacity: 0,
}
: undefined; // Only animate the newly added item
return { scale: 0, opacity: 0 };
}
// Get enter animation (only the latest item animates in)
function getEnter(idx: number) {
return idx === index.value - 1
? {
scale: 1,
opacity: 1,
y: 0,
transition: {
type: "spring",
stiffness: 250,
damping: 40,
},
}
: undefined; // Only animate the newly added item
return {
scale: 1,
opacity: 1,
y: 0,
transition: {
type: "spring",
stiffness: 250,
damping: 40,
},
};
}
// Get leave animation (same for all)
Expand Down

0 comments on commit dfcaeef

Please sign in to comment.