@geniusdynamics/ns8-ui-lib 1.0.2 → 1.0.4

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.
@@ -4,7 +4,7 @@ interface Backup {
4
4
  date: string;
5
5
  size: string;
6
6
  enabled: boolean;
7
- status?: 'success' | 'error' | 'running' | 'pending';
7
+ status?: "success" | "error" | "running" | "pending";
8
8
  }
9
9
  interface Status {
10
10
  [key: string]: {
@@ -5,8 +5,8 @@ interface Props {
5
5
  lottiePath?: string;
6
6
  lottieData?: any;
7
7
  animationTitle?: string;
8
- size?: 'default' | 'sm' | 'lg';
9
- variant?: 'default' | 'muted';
8
+ size?: "default" | "sm" | "lg";
9
+ variant?: "default" | "muted";
10
10
  loop?: boolean | number;
11
11
  animateOnHover?: boolean;
12
12
  autoPlay?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geniusdynamics/ns8-ui-lib",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Vue 3 library for NethServer 8 UI with TailwindCSS and Shadcn Vue",
5
5
  "keywords": [
6
6
  "nethserver",
@@ -1,111 +1,130 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import { Save, AlertCircle, CheckCircle, Loader2, MoreHorizontal } from 'lucide-vue-next'
4
- import { Button } from '@/components/ui/button'
5
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
6
- import { Skeleton } from '@/components/ui/skeleton'
7
- import { cn } from '@/lib/utils'
2
+ import { computed } from "vue";
3
+ import {
4
+ Save,
5
+ AlertCircle,
6
+ CheckCircle,
7
+ Loader2,
8
+ MoreHorizontal,
9
+ } from "lucide-vue-next";
10
+ import { Button } from "@/components/ui/button";
11
+
12
+ import {
13
+ Card,
14
+ CardContent,
15
+ CardDescription,
16
+ CardHeader,
17
+ CardTitle,
18
+ } from "@/components/ui/card";
19
+ import { Skeleton } from "@/components/ui/skeleton";
20
+ import { cn } from "@/lib/utils";
8
21
 
9
22
  interface Backup {
10
- id: string
11
- name: string
12
- date: string
13
- size: string
14
- enabled: boolean
15
- status?: 'success' | 'error' | 'running' | 'pending'
23
+ id: string;
24
+ name: string;
25
+ date: string;
26
+ size: string;
27
+ enabled: boolean;
28
+ status?: "success" | "error" | "running" | "pending";
16
29
  }
17
30
 
18
31
  interface Status {
19
32
  [key: string]: {
20
- success?: boolean
21
- message?: string
22
- }
33
+ success?: boolean;
34
+ message?: string;
35
+ };
23
36
  }
24
37
 
25
38
  interface Props {
26
- title?: string
27
- loading?: boolean
28
- backups?: Backup[]
29
- status?: Status
30
- noBackupMessage?: string
31
- statusLabel?: string
32
- statusSuccessLabel?: string
33
- backupDisabledLabel?: string
34
- light?: boolean
35
- maxHeight?: string
36
- class?: string
39
+ title?: string;
40
+ loading?: boolean;
41
+ backups?: Backup[];
42
+ status?: Status;
43
+ noBackupMessage?: string;
44
+ statusLabel?: string;
45
+ statusSuccessLabel?: string;
46
+ backupDisabledLabel?: string;
47
+ light?: boolean;
48
+ maxHeight?: string;
49
+ class?: string;
37
50
  }
38
51
 
39
52
  const props = withDefaults(defineProps<Props>(), {
40
- title: 'Backup',
53
+ title: "Backup",
41
54
  loading: false,
42
55
  backups: () => [],
43
56
  status: () => ({}),
44
- noBackupMessage: 'No backups available',
45
- statusLabel: 'Status',
46
- statusSuccessLabel: 'Enabled',
47
- backupDisabledLabel: 'Disabled',
57
+ noBackupMessage: "No backups available",
58
+ statusLabel: "Status",
59
+ statusSuccessLabel: "Enabled",
60
+ backupDisabledLabel: "Disabled",
48
61
  light: false,
49
- })
62
+ });
50
63
 
51
64
  const emit = defineEmits<{
52
- backup: [backupId: string]
53
- restore: [backupId: string]
54
- delete: [backupId: string]
55
- enable: [backupId: string]
56
- disable: [backupId: string]
57
- }>()
65
+ backup: [backupId: string];
66
+ restore: [backupId: string];
67
+ delete: [backupId: string];
68
+ enable: [backupId: string];
69
+ disable: [backupId: string];
70
+ }>();
58
71
 
59
- const hasBackups = computed(() => props.backups.length > 0)
60
- const backupsContainingInstance = computed(() => props.backups.filter(backup => backup.enabled))
61
- const singleBackup = computed(() => backupsContainingInstance.value.length === 1 ? backupsContainingInstance.value[0] : null)
72
+ const hasBackups = computed(() => props.backups.length > 0);
73
+ const backupsContainingInstance = computed(() =>
74
+ props.backups.filter((backup) => backup.enabled),
75
+ );
76
+ const singleBackup = computed(() =>
77
+ backupsContainingInstance.value.length === 1
78
+ ? backupsContainingInstance.value[0]
79
+ : null,
80
+ );
62
81
 
63
82
  const getStatusIcon = (backup: Backup) => {
64
83
  switch (backup.status) {
65
- case 'success':
66
- return CheckCircle
67
- case 'error':
68
- return AlertCircle
69
- case 'running':
70
- return Loader2
84
+ case "success":
85
+ return CheckCircle;
86
+ case "error":
87
+ return AlertCircle;
88
+ case "running":
89
+ return Loader2;
71
90
  default:
72
- return null
91
+ return null;
73
92
  }
74
- }
93
+ };
75
94
 
76
95
  const getStatusClass = (backup: Backup) => {
77
96
  switch (backup.status) {
78
- case 'success':
79
- return 'text-green-600'
80
- case 'error':
81
- return 'text-destructive'
82
- case 'running':
83
- return 'text-blue-600'
97
+ case "success":
98
+ return "text-green-600";
99
+ case "error":
100
+ return "text-destructive";
101
+ case "running":
102
+ return "text-blue-600";
84
103
  default:
85
- return 'text-muted-foreground'
104
+ return "text-muted-foreground";
86
105
  }
87
- }
106
+ };
88
107
 
89
108
  const containerClasses = computed(() => {
90
- return cn('w-full', props.class)
91
- })
109
+ return cn("w-full", props.class);
110
+ });
92
111
 
93
112
  const cardClasses = computed(() => {
94
113
  return cn(
95
- 'transition-all duration-200 hover:shadow-md',
96
- props.light ? 'bg-background/95' : 'bg-background'
97
- )
98
- })
114
+ "transition-all duration-200 hover:shadow-md",
115
+ props.light ? "bg-background/95" : "bg-background",
116
+ );
117
+ });
99
118
 
100
119
  const tableWrapperClasses = computed(() => {
101
- return cn('w-full overflow-auto', {
102
- 'max-h-64': props.maxHeight
103
- })
104
- })
120
+ return cn("w-full overflow-auto", {
121
+ "max-h-64": props.maxHeight,
122
+ });
123
+ });
105
124
 
106
125
  const tableClasses = computed(() => {
107
- return 'w-full min-w-[300px]'
108
- })
126
+ return "w-full min-w-[300px]";
127
+ });
109
128
  </script>
110
129
 
111
130
  <template>
@@ -125,7 +144,10 @@ const tableClasses = computed(() => {
125
144
  </div>
126
145
 
127
146
  <!-- No backups state -->
128
- <div v-else-if="!hasBackups" class="flex items-center gap-3 text-muted-foreground">
147
+ <div
148
+ v-else-if="!hasBackups"
149
+ class="flex items-center gap-3 text-muted-foreground"
150
+ >
129
151
  <AlertCircle class="h-5 w-5" />
130
152
  <span>{{ noBackupMessage }}</span>
131
153
  </div>
@@ -138,10 +160,7 @@ const tableClasses = computed(() => {
138
160
  <div class="flex items-center justify-between py-2 border-b">
139
161
  <span class="text-sm font-medium">{{ statusLabel }}</span>
140
162
  <div class="flex items-center gap-2">
141
- <span
142
- v-if="!singleBackup.enabled"
143
- class="text-destructive"
144
- >
163
+ <span v-if="!singleBackup.enabled" class="text-destructive">
145
164
  {{ backupDisabledLabel }}
146
165
  </span>
147
166
  <span
@@ -231,7 +250,7 @@ const tableClasses = computed(() => {
231
250
  :class="cn('h-4 w-4', getStatusClass(backup))"
232
251
  />
233
252
  <span :class="getStatusClass(backup)">
234
- {{ backup.status || 'Unknown' }}
253
+ {{ backup.status || "Unknown" }}
235
254
  </span>
236
255
  </div>
237
256
  </td>
@@ -269,4 +288,4 @@ const tableClasses = computed(() => {
269
288
  </div>
270
289
  </CardContent>
271
290
  </Card>
272
- </template>
291
+ </template>
@@ -1,133 +1,148 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import { FileX, Search, InBox, AlertCircle, Info } from 'lucide-vue-next'
4
- import { NSLottieAnimation } from '@/components/NS/lottie-animation'
5
- import { cn } from '@/lib/utils'
2
+ import { computed } from "vue";
3
+ import {
4
+ FileX,
5
+ Search,
6
+ AlertCircle,
7
+ Info,
8
+ BoxIcon,
9
+ Inbox,
10
+ } from "lucide-vue-next";
11
+ import { NSLottieAnimation } from "@/components/NS/lottie-animation";
12
+ import { cn } from "@/lib/utils";
6
13
 
7
14
  interface Props {
8
- title?: string
9
- description?: string
10
- icon?: string | object
11
- lottiePath?: string // Path to Lottie JSON file
12
- lottieData?: any // Direct Lottie animation data
13
- animationTitle?: string
14
- size?: 'default' | 'sm' | 'lg'
15
- variant?: 'default' | 'muted'
16
- loop?: boolean | number
17
- animateOnHover?: boolean
18
- autoPlay?: boolean
19
- class?: string
20
- iconClass?: string
21
- titleClass?: string
22
- descriptionClass?: string
15
+ title?: string;
16
+ description?: string;
17
+ icon?: string | object;
18
+ lottiePath?: string; // Path to Lottie JSON file
19
+ lottieData?: any; // Direct Lottie animation data
20
+ animationTitle?: string;
21
+ size?: "default" | "sm" | "lg";
22
+ variant?: "default" | "muted";
23
+ loop?: boolean | number;
24
+ animateOnHover?: boolean;
25
+ autoPlay?: boolean;
26
+ class?: string;
27
+ iconClass?: string;
28
+ titleClass?: string;
29
+ descriptionClass?: string;
23
30
  }
24
31
 
25
32
  const props = withDefaults(defineProps<Props>(), {
26
- title: 'No data available',
27
- size: 'default',
28
- variant: 'default',
33
+ title: "No data available",
34
+ size: "default",
35
+ variant: "default",
29
36
  loop: true,
30
37
  animateOnHover: false,
31
38
  autoPlay: true,
32
- })
39
+ });
33
40
 
34
41
  const DefaultIcon = computed(() => {
35
42
  if (props.icon) {
36
- return null // Use custom icon
43
+ return null; // Use custom icon
37
44
  }
38
-
45
+
39
46
  // Choose appropriate default icon based on title content
40
- const title = props.title?.toLowerCase() || ''
41
-
42
- if (title.includes('search') || title.includes('find') || title.includes('result')) {
43
- return Search
44
- } else if (title.includes('empty') || title.includes('no') || title.includes('data')) {
45
- return FileX
46
- } else if (title.includes('message') || title.includes('notification') || title.includes('alert')) {
47
- return InBox
48
- } else if (title.includes('error') || title.includes('warning')) {
49
- return AlertCircle
47
+ const title = props.title?.toLowerCase() || "";
48
+
49
+ if (
50
+ title.includes("search") ||
51
+ title.includes("find") ||
52
+ title.includes("result")
53
+ ) {
54
+ return Search;
55
+ } else if (
56
+ title.includes("empty") ||
57
+ title.includes("no") ||
58
+ title.includes("data")
59
+ ) {
60
+ return FileX;
61
+ } else if (
62
+ title.includes("message") ||
63
+ title.includes("notification") ||
64
+ title.includes("alert")
65
+ ) {
66
+ return Inbox;
67
+ } else if (title.includes("error") || title.includes("warning")) {
68
+ return AlertCircle;
50
69
  } else {
51
- return Info
70
+ return Info;
52
71
  }
53
- })
72
+ });
54
73
 
55
74
  const iconComponent = computed(() => {
56
- if (typeof props.icon === 'object') {
57
- return props.icon // Vue component
75
+ if (typeof props.icon === "object") {
76
+ return props.icon; // Vue component
58
77
  }
59
-
60
- if (typeof props.icon === 'string') {
78
+
79
+ if (typeof props.icon === "string") {
61
80
  // Map string to Lucide icons
62
81
  const iconMap: Record<string, any> = {
63
- 'file-x': FileX,
64
- 'search': Search,
65
- 'inbox': InBox,
66
- 'alert-circle': AlertCircle,
67
- 'info': Info,
68
- }
69
-
70
- return iconMap[props.icon] || DefaultIcon.value
82
+ "file-x": FileX,
83
+ search: Search,
84
+ inbox: Inbox,
85
+ "alert-circle": AlertCircle,
86
+ info: Info,
87
+ };
88
+
89
+ return iconMap[props.icon] || DefaultIcon.value;
71
90
  }
72
-
73
- return DefaultIcon.value
74
- })
91
+
92
+ return DefaultIcon.value;
93
+ });
75
94
 
76
95
  const containerClasses = computed(() => {
77
96
  const sizeClasses = {
78
- default: 'p-8',
79
- sm: 'p-4',
80
- lg: 'p-12'
81
- }
97
+ default: "p-8",
98
+ sm: "p-4",
99
+ lg: "p-12",
100
+ };
82
101
 
83
102
  const variantClasses = {
84
- default: 'text-foreground',
85
- muted: 'text-muted-foreground'
86
- }
103
+ default: "text-foreground",
104
+ muted: "text-muted-foreground",
105
+ };
87
106
 
88
107
  return cn(
89
- 'flex flex-col items-center justify-center text-center',
108
+ "flex flex-col items-center justify-center text-center",
90
109
  sizeClasses[props.size],
91
110
  variantClasses[props.variant],
92
- props.class
93
- )
94
- })
111
+ props.class,
112
+ );
113
+ });
95
114
 
96
115
  const iconClasses = computed(() => {
97
116
  const sizeClasses = {
98
- default: 'h-16 w-16',
99
- sm: 'h-12 w-12',
100
- lg: 'h-20 w-20'
101
- }
117
+ default: "h-16 w-16",
118
+ sm: "h-12 w-12",
119
+ lg: "h-20 w-20",
120
+ };
102
121
 
103
- return cn('text-muted-foreground', sizeClasses[props.size], props.iconClass)
104
- })
122
+ return cn("text-muted-foreground", sizeClasses[props.size], props.iconClass);
123
+ });
105
124
 
106
125
  const titleClasses = computed(() => {
107
126
  return cn(
108
- 'text-lg font-semibold mb-2',
127
+ "text-lg font-semibold mb-2",
109
128
  {
110
- 'text-foreground': props.variant === 'default',
111
- 'text-muted-foreground': props.variant === 'muted'
129
+ "text-foreground": props.variant === "default",
130
+ "text-muted-foreground": props.variant === "muted",
112
131
  },
113
- props.titleClass
114
- )
115
- })
132
+ props.titleClass,
133
+ );
134
+ });
116
135
 
117
136
  const descriptionClasses = computed(() => {
118
137
  return cn(
119
- 'text-sm max-w-md mb-6',
138
+ "text-sm max-w-md mb-6",
120
139
  {
121
- 'text-muted-foreground': props.variant === 'default',
122
- 'text-muted-foreground/70': props.variant === 'muted'
140
+ "text-muted-foreground": props.variant === "default",
141
+ "text-muted-foreground/70": props.variant === "muted",
123
142
  },
124
- props.descriptionClass
125
- )
126
- })
127
-
128
- const hasIconSlot = computed(() => !!$slots.icon)
129
- const hasDescriptionSlot = computed(() => !!$slots.description)
130
- const hasActionsSlot = computed(() => !!$slots.actions)
143
+ props.descriptionClass,
144
+ );
145
+ });
131
146
  </script>
132
147
 
133
148
  <template>
@@ -147,10 +162,10 @@ const hasActionsSlot = computed(() => !!$slots.actions)
147
162
  :play-on-hover="true"
148
163
  :stop-on-hover-out="true"
149
164
  />
150
-
165
+
151
166
  <!-- Custom icon or default Lucide icon -->
152
167
  <component
153
- v-else-if="iconComponent && !hasIconSlot"
168
+ v-else-if="iconComponent && !$slots.icon"
154
169
  :is="iconComponent"
155
170
  :class="iconClasses"
156
171
  />
@@ -158,25 +173,19 @@ const hasActionsSlot = computed(() => !!$slots.actions)
158
173
  </div>
159
174
 
160
175
  <!-- Title section -->
161
- <h3
162
- v-if="title || $slots.title"
163
- :class="titleClasses"
164
- >
176
+ <h3 v-if="title || $slots.title" :class="titleClasses">
165
177
  <slot name="title">{{ title }}</slot>
166
178
  </h3>
167
179
 
168
180
  <!-- Description section -->
169
- <div
170
- v-if="hasDescriptionSlot || description"
171
- :class="descriptionClasses"
172
- >
181
+ <div v-if="$slots.description || description" :class="descriptionClasses">
173
182
  <slot name="description">
174
183
  <p>{{ description }}</p>
175
184
  </slot>
176
185
  </div>
177
186
 
178
187
  <!-- Actions section -->
179
- <div v-if="hasActionsSlot" class="mt-4">
188
+ <div v-if="$slots.actions" class="mt-4">
180
189
  <slot name="actions" />
181
190
  </div>
182
191
 
@@ -185,4 +194,4 @@ const hasActionsSlot = computed(() => !!$slots.actions)
185
194
  <slot name="extra" />
186
195
  </div>
187
196
  </div>
188
- </template>
197
+ </template>