@bl33dz/fa814698dcde12f86a37ac31dd3aedf9 1.0.21 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.0.21",
6
+ "version": "1.0.22",
7
7
  "main": "dist/perisai-ui.umd.js",
8
8
  "module": "dist/perisai-ui.es.js",
9
9
  "scripts": {
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ export * from './shadcn/switch';
12
12
  export * from './shadcn/collapsible';
13
13
  export * from './shadcn/navigation-menu';
14
14
  export * from './shadcn/input-otp';
15
+ export * from './shadcn/context-menu';
15
16
 
16
17
  export * from './ui/tree';
17
18
  export { default as PopoverContent } from './ui/PopoverContent.vue';
@@ -173,3 +174,6 @@ export { default as Tabs } from './ui/tabs.vue';
173
174
  export { default as Textarea } from './ui/textarea.vue';
174
175
  export { default as ThreatGauge } from './ui/threat-gauge.vue';
175
176
  export { default as Toggle } from './ui/toggle.vue';
177
+
178
+ // Toast utilities
179
+ export { toast } from './ui/toast';
@@ -24,7 +24,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
24
24
  data-slot="context-menu-content"
25
25
  v-bind="forwarded"
26
26
  :class="cn(
27
- 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--reka-context-menu-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',
27
+ 'bg-popover text-popover-foreground border-border data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--reka-context-menu-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-lg',
28
28
  props.class,
29
29
  )"
30
30
  >
@@ -30,7 +30,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
30
30
  :data-variant="variant"
31
31
  v-bind="forwarded"
32
32
  :class="cn(
33
- 'focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-foreground data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/40 data-[variant=destructive]:focus:text-destructive-foreground data-[variant=destructive]:*:[svg]:!text-destructive-foreground [&_svg:not([class*=\'text-\'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
33
+ 'focus:bg-accent focus:text-accent-foreground text-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive [&_svg:not([class*=\'text-\'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
34
34
  props.class,
35
35
  )"
36
36
  >
@@ -23,7 +23,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
23
23
  v-bind="forwarded"
24
24
  :class="
25
25
  cn(
26
- 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--reka-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',
26
+ 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--reka-context-menu-content-transform-origin) overflow-hidden rounded-md border border-border p-1 shadow-lg',
27
27
  props.class,
28
28
  )
29
29
  "
package/src/ui/card.vue CHANGED
@@ -55,7 +55,7 @@ const customPaddingValue = computed(() => {
55
55
  <!-- Content container -->
56
56
  <div
57
57
  data-slot="card"
58
- :class="cn('bg-card text-card-foreground flex flex-col gap-3 relative rounded shadow-sm', customPaddingValue, $attrs.class as any)"
58
+ :class="cn('bg-card text-card-foreground flex flex-col gap-3 relative rounded', customPaddingValue, $attrs.class as any)"
59
59
  :style="{ clipPath: clipPathValue, margin: '1px' }"
60
60
  v-bind="$attrs"
61
61
  >
@@ -65,7 +65,7 @@ const customPaddingValue = computed(() => {
65
65
  <div
66
66
  v-else
67
67
  data-slot="card"
68
- :class="cn('bg-gray-900 text-card-foreground flex flex-col gap-3 relative rounded-xl border border-gray-800 shadow-sm', $attrs.class as any)"
68
+ :class="cn('bg-card text-card-foreground flex flex-col gap-3 relative rounded-xl border border-border', $attrs.class as any)"
69
69
  v-bind="$attrs"
70
70
  >
71
71
  <slot />
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div
3
3
  data-slot="dialog-body"
4
- :class="['px-6 py-5 space-y-6 [&>*+*]:border-t [&>*+*]:border-border [&>*+*]:pt-5', $attrs.class]"
4
+ :class="['px-6 py-5 space-y-6', $attrs.class]"
5
5
  v-bind="$attrs"
6
6
  >
7
7
  <slot />
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div
3
3
  data-slot="dialog-header"
4
- :class="['flex flex-col gap-2 text-center pl-4 py-4 pr-5 sm:text-left border-b', $attrs.class]"
4
+ :class="['flex flex-col gap-2 text-center pl-4 py-4 pr-5 sm:text-left border-b border-border', $attrs.class]"
5
5
  v-bind="$attrs"
6
6
  >
7
7
  <slot />
package/src/ui/sonner.vue CHANGED
@@ -1,37 +1,53 @@
1
1
 
2
2
  <template>
3
- <transition-group name="sonner-fade" tag="div" class="fixed z-50 w-full flex flex-col items-center pointer-events-none top-4 left-0">
4
- <div
5
- v-for="(toast, idx) in toasts"
6
- :key="toast.id"
7
- class="bg-card border border-border shadow-lg rounded-lg px-4 py-3 mb-2 flex items-center gap-3 pointer-events-auto min-w-[240px] max-w-xs"
8
- :style="{ opacity: toast.visible ? 1 : 0 }"
9
- >
10
- <span v-if="toast.icon" class="text-xl">{{ toast.icon }}</span>
11
- <div class="flex-1">
12
- <div v-if="toast.title" class="font-semibold text-sm">{{ toast.title }}</div>
13
- <div v-if="toast.description" class="text-xs text-muted-foreground mt-1">{{ toast.description }}</div>
14
- </div>
15
- <button
16
- class="ml-2 text-muted-foreground hover:text-foreground focus:outline-none"
17
- @click="removeToast(toast.id)"
18
- aria-label="Close"
3
+ <div
4
+ class="fixed z-50 pointer-events-none bottom-4 right-4 relative min-h-[80px]"
5
+ @mouseenter="expandStack"
6
+ @mouseleave="collapseStack"
7
+ >
8
+ <transition-group name="sonner-fade" tag="div">
9
+ <div
10
+ v-for="(toast, idx) in toasts"
11
+ :key="toast.id"
12
+ :class="getToastClasses(toast.type, idx)"
13
+ :style="{
14
+ opacity: toast.visible ? 1 : 0,
15
+ transform: isStackExpanded ? `translateY(-${(toasts.length - 1 - idx) * 72}px)` : `translateY(-${(toasts.length - 1 - idx) * 4}px)`,
16
+ zIndex: idx + 1,
17
+ position: 'absolute',
18
+ right: '0',
19
+ top: '0',
20
+ pointerEvents: 'auto'
21
+ }"
19
22
  >
20
- ×
21
- </button>
22
- </div>
23
- </transition-group>
23
+ <span v-if="toast.icon" :class="getIconClasses(toast.type)">{{ toast.icon }}</span>
24
+ <div class="flex-1">
25
+ <div v-if="toast.title" class="font-medium text-sm leading-none">{{ toast.title }}</div>
26
+ <div v-if="toast.description" class="text-xs mt-1 leading-none" :class="getDescriptionClasses(toast.type)">{{ toast.description }}</div>
27
+ </div>
28
+ <button
29
+ :class="getCloseButtonClasses(toast.type)"
30
+ @click="removeToast(toast.id)"
31
+ aria-label="Close"
32
+ >
33
+ ×
34
+ </button>
35
+ </div>
36
+ </transition-group>
37
+ </div>
24
38
  </template>
25
39
 
26
40
  <script setup>
27
41
  import { ref, onUnmounted } from 'vue';
28
42
 
29
43
  const toasts = ref([]);
44
+ const isStackExpanded = ref(false);
30
45
  let idCounter = 0;
31
46
 
32
- function showToast({ title, description, icon, duration = 3000 }) {
47
+ function showToast({ title, description, icon, duration = 3000, type = 'default' }) {
33
48
  const id = ++idCounter;
34
- toasts.value.push({ id, title, description, icon, visible: true });
49
+ console.log('Creating toast:', { id, title, description, icon, type });
50
+ toasts.value.push({ id, title, description, icon, visible: true, type });
35
51
  setTimeout(() => removeToast(id), duration);
36
52
  }
37
53
 
@@ -45,6 +61,87 @@ function removeToast(id) {
45
61
  }
46
62
  }
47
63
 
64
+ function expandStack() {
65
+ isStackExpanded.value = true;
66
+ }
67
+
68
+ function collapseStack() {
69
+ isStackExpanded.value = false;
70
+ }
71
+
72
+ function getToastClasses(type, index) {
73
+ const baseClasses = 'rounded-lg px-4 py-3 flex items-center gap-3 pointer-events-auto w-80 shadow-lg border font-sans transition-all duration-200 ease-out';
74
+ const stackOffset = index * 8; // Stack offset in pixels
75
+
76
+ switch (type) {
77
+ case 'success':
78
+ return `${baseClasses} bg-green-50 text-green-800 border-green-200`;
79
+ case 'error':
80
+ return `${baseClasses} bg-red-50 text-red-800 border-red-200`;
81
+ case 'warning':
82
+ return `${baseClasses} bg-yellow-50 text-yellow-800 border-yellow-200`;
83
+ case 'info':
84
+ return `${baseClasses} bg-blue-50 text-blue-800 border-blue-200`;
85
+ case 'loading':
86
+ return `${baseClasses} bg-gray-50 text-gray-800 border-gray-200`;
87
+ default:
88
+ return `${baseClasses} bg-card text-card-foreground border-border`;
89
+ }
90
+ }
91
+
92
+ function getDescriptionClasses(type) {
93
+ switch (type) {
94
+ case 'success':
95
+ return 'text-green-700';
96
+ case 'error':
97
+ return 'text-red-700';
98
+ case 'warning':
99
+ return 'text-yellow-700';
100
+ case 'info':
101
+ return 'text-blue-700';
102
+ case 'loading':
103
+ return 'text-gray-700';
104
+ default:
105
+ return 'text-muted-foreground';
106
+ }
107
+ }
108
+
109
+ function getCloseButtonClasses(type) {
110
+ const baseClasses = 'ml-2 hover:opacity-80 focus:outline-none transition-opacity rounded-full w-5 h-5 flex items-center justify-center text-xs';
111
+
112
+ switch (type) {
113
+ case 'success':
114
+ return `${baseClasses} text-green-600 hover:bg-green-100`;
115
+ case 'error':
116
+ return `${baseClasses} text-red-600 hover:bg-red-100`;
117
+ case 'warning':
118
+ return `${baseClasses} text-yellow-600 hover:bg-yellow-100`;
119
+ case 'info':
120
+ return `${baseClasses} text-blue-600 hover:bg-blue-100`;
121
+ case 'loading':
122
+ return `${baseClasses} text-gray-600 hover:bg-gray-100`;
123
+ default:
124
+ return `${baseClasses} text-muted-foreground hover:bg-muted`;
125
+ }
126
+ }
127
+
128
+ function getIconClasses(type) {
129
+ switch (type) {
130
+ case 'success':
131
+ return 'text-green-600 text-lg';
132
+ case 'error':
133
+ return 'text-red-600 text-lg';
134
+ case 'warning':
135
+ return 'text-yellow-600 text-lg';
136
+ case 'info':
137
+ return 'text-blue-600 text-lg';
138
+ case 'loading':
139
+ return 'text-gray-600 text-lg';
140
+ default:
141
+ return 'text-muted-foreground text-lg';
142
+ }
143
+ }
144
+
48
145
  // Expose showToast globally (for demo, you may want to use provide/inject or a plugin in real apps)
49
146
  if (typeof window !== 'undefined') {
50
147
  window.$sonner = showToast;
@@ -0,0 +1,55 @@
1
+ // Toast utilities for the Sonner component
2
+
3
+ export interface ToastOptions {
4
+ title: string;
5
+ description?: string;
6
+ icon?: string;
7
+ duration?: number;
8
+ type?: 'success' | 'error' | 'warning' | 'info' | 'loading';
9
+ }
10
+
11
+ export const toast = {
12
+ success: (title: string, description?: string, duration?: number) => {
13
+ if (typeof window !== 'undefined' && window.$sonner) {
14
+ window.$sonner({ title, description, icon: '✅', duration, type: 'success' });
15
+ }
16
+ },
17
+
18
+ error: (title: string, description?: string, duration?: number) => {
19
+ if (typeof window !== 'undefined' && window.$sonner) {
20
+ window.$sonner({ title, description, icon: '❌', duration, type: 'error' });
21
+ }
22
+ },
23
+
24
+ warning: (title: string, description?: string, duration?: number) => {
25
+ if (typeof window !== 'undefined' && window.$sonner) {
26
+ window.$sonner({ title, description, icon: '⚠️', duration, type: 'warning' });
27
+ }
28
+ },
29
+
30
+ info: (title: string, description?: string, duration?: number) => {
31
+ if (typeof window !== 'undefined' && window.$sonner) {
32
+ window.$sonner({ title, description, icon: 'ℹ️', duration, type: 'info' });
33
+ }
34
+ },
35
+
36
+ loading: (title: string, description?: string, duration?: number) => {
37
+ if (typeof window !== 'undefined' && window.$sonner) {
38
+ window.$sonner({ title, description, icon: '⏳', duration, type: 'loading' });
39
+ }
40
+ },
41
+
42
+ // Custom toast method
43
+ show: (options: ToastOptions) => {
44
+ if (typeof window !== 'undefined' && window.$sonner) {
45
+ window.$sonner(options);
46
+ }
47
+ }
48
+ };
49
+
50
+ // Extend Window interface for TypeScript
51
+ declare global {
52
+ interface Window {
53
+ $sonner?: (options: ToastOptions) => void;
54
+ }
55
+ }