Skip to content

Commit 2612d1d

Browse files
requestAnimation was used
1 parent 897738a commit 2612d1d

10 files changed

Lines changed: 121 additions & 35 deletions

File tree

@codexteam/ui/dev/Playground.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ const pages = computed(() => [
114114
{
115115
title: 'Components',
116116
items: [
117+
{
118+
title: 'Alert',
119+
onActivate: () => router.push('/alert'),
120+
isActive: route.path === '/alert',
121+
},
117122
{
118123
title: 'Button',
119124
onActivate: () => router.push('/components/button'),

@codexteam/ui/dev/routes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { RouteRecordRaw } from 'vue-router';
22
import type { Component } from 'vue';
33
import Index from './pages/Index.vue';
4+
import Alert from './pages/components/Alert.vue';
45
import TypeScale from './pages/base-concepts/TypeScale.vue';
56
import ControlsDimensions from './pages/base-concepts/ControlsDimensions.vue';
67
import Sizes from './pages/base-concepts/Sizes.vue';
@@ -41,6 +42,10 @@ const routes: RouteRecordRaw[] = [
4142
path: '/',
4243
component: Index as Component,
4344
},
45+
{
46+
path: '/alert',
47+
component: Alert as Component,
48+
},
4449
{
4550
path: '/type-scale',
4651
component: TypeScale as Component,
File renamed without changes.
File renamed without changes.

codex-ui/src/vue/components/alert/AlertContainer.vue renamed to @codexteam/ui/src/vue/components/alert/AlertContainer.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import Alert from './Alert.vue';
2121
import AlertTransition from './AlertTransition.vue';
2222
import { useAlert } from './useAlert';
23-
2423
const { alerts } = useAlert();
2524
</script>
2625

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<template>
2+
<transition-group
3+
tag="div"
4+
name="alert"
5+
class="alert-container"
6+
>
7+
<slot />
8+
</transition-group>
9+
</template>
10+
11+
<script setup lang="ts">
12+
// No script logic needed for basic transitions
13+
</script>
14+
15+
<style lang="postcss">
16+
/* Enter/Leave transitions */
17+
.alert-enter-active {
18+
transition: all 0.3s ease-out;
19+
}
20+
21+
.alert-leave-active {
22+
transition: all 0.4s ease-in;
23+
position: absolute;
24+
width: 100%;
25+
}
26+
27+
.alert-enter-from {
28+
opacity: 0;
29+
transform: translateX(20px);
30+
}
31+
32+
.alert-enter-to {
33+
opacity: 1;
34+
transform: translateX(0);
35+
}
36+
37+
.alert-leave-to {
38+
opacity: 0;
39+
transform: translateX(-100%);
40+
}
41+
42+
/* Move other items smoothly when one is removed */
43+
.alert-move {
44+
transition: transform 0.4s ease;
45+
}
46+
47+
.alert-container {
48+
position: relative;
49+
display: flex;
50+
flex-direction: column;
51+
gap: 0.75rem;
52+
width: 100%;
53+
}
54+
</style>

codex-ui/src/vue/components/alert/index.ts renamed to @codexteam/ui/src/vue/components/alert/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useAlert } from './useAlert';
1+
import { useAlert } from './useAlert.js';
22
import AlertContainer from './AlertContainer.vue';
33

44
export * from './Alert.types.js';

codex-ui/src/vue/components/alert/useAlert.ts renamed to @codexteam/ui/src/vue/components/alert/useAlert.ts

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Ref } from 'vue';
2-
import { ref } from 'vue';
2+
import { onUnmounted, ref } from 'vue';
33
import { createSharedComposable } from '@vueuse/core';
44
import type { AlertOptions, AlertType } from './Alert.types';
55

@@ -49,14 +49,37 @@ export interface UseAlertComposableState {
4949
*/
5050
export const useAlert = createSharedComposable((): UseAlertComposableState => {
5151
const counter = ref(0);
52+
const maxAlerts = 10; // Default maximum number of alerts
5253
const alerts = ref<AlertOptions[]>([]);
54+
const animationFrameIds = new Map<number, number>();
5355

5456
function removeExpiredAlerts(): void {
5557
const currentTime = new Date().getTime();
5658

5759
alerts.value = alerts.value.filter(alert => alert.timeout > currentTime);
5860
}
5961

62+
function scheduleRemoval(alertId: number, timeout: number): void {
63+
const startTime = performance.now();
64+
65+
const checkExpiry = (timestamp: number): void => {
66+
const elpased = timestamp - startTime;
67+
68+
if (elpased >= timeout) {
69+
removeExpiredAlerts();
70+
animationFrameIds.delete(alertId);
71+
} else {
72+
const frameId = requestAnimationFrame(checkExpiry);
73+
74+
animationFrameIds.set(alertId, frameId);
75+
}
76+
};
77+
78+
const frameId = requestAnimationFrame(checkExpiry);
79+
80+
animationFrameIds.set(alertId, frameId);
81+
}
82+
6083
/**
6184
* Trigger alert component
6285
* @param type alert type (success, error, warning, info and default)
@@ -70,12 +93,41 @@ export const useAlert = createSharedComposable((): UseAlertComposableState => {
7093
const currentTime = new Date().getTime();
7194
const currentTimeout = currentTime + opt.timeout;
7295

73-
opt.id = counter.value++, opt.type = type, opt.timeout = currentTimeout;
96+
if (alerts.value.length >= maxAlerts) {
97+
// Find and remove the oldest alert (smallest ID)
98+
const oldestAlert = alerts.value.reduce((prev, current) => {
99+
if (prev?.id === undefined || current.id === undefined) {
100+
return prev;
101+
}
74102

75-
alerts.value = [...alerts.value, opt];
76-
setInterval(removeExpiredAlerts, currentTimeout);
103+
return (prev.id < current.id) ? prev : current;
104+
});
105+
106+
alerts.value = alerts.value.filter(alert => alert.id !== oldestAlert?.id);
107+
}
108+
109+
const newAlert = {
110+
...opt,
111+
id: counter.value++,
112+
type,
113+
timeout: currentTimeout,
114+
};
115+
116+
alerts.value = [newAlert, ...alerts.value];
117+
118+
requestAnimationFrame(() => {
119+
scheduleRemoval(Number(newAlert.id), opt.timeout);
120+
});
77121
}
78122

123+
onUnmounted(() => {
124+
animationFrameIds.forEach((frameId) => {
125+
cancelAnimationFrame(frameId);
126+
});
127+
128+
animationFrameIds.clear();
129+
});
130+
79131
return {
80132
alerts,
81133
success: (opt: Omit<AlertOptions, 'id'>) => triggerAlert('success', opt),

codex-ui/src/styles/z-axis.pcss

Lines changed: 0 additions & 4 deletions
This file was deleted.

codex-ui/src/vue/components/alert/AlertTransition.vue

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)