@geniusdynamics/ns8-ui-lib 1.0.0

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.
Files changed (310) hide show
  1. package/README.md +167 -0
  2. package/dist/index.d.ts +14 -0
  3. package/dist/ns8-ui-lib.es.js +4278 -0
  4. package/dist/ns8-ui-lib.es.js.map +1 -0
  5. package/dist/ns8-ui-lib.umd.js +7 -0
  6. package/dist/ns8-ui-lib.umd.js.map +1 -0
  7. package/dist/src/App.vue.d.ts +2 -0
  8. package/dist/src/components/HelloWorld.vue.d.ts +5 -0
  9. package/dist/src/components/NS/cards/NSBackupCard.vue.d.ts +51 -0
  10. package/dist/src/components/NS/cards/NSSystemInfoCard.vue.d.ts +58 -0
  11. package/dist/src/components/NS/cards/NSSystemdServiceCard.vue.d.ts +47 -0
  12. package/dist/src/components/NS/cards/index.d.ts +3 -0
  13. package/dist/src/components/NS/checkbox/NSCheckbox.vue.d.ts +37 -0
  14. package/dist/src/components/NS/checkbox/index.d.ts +1 -0
  15. package/dist/src/components/NS/data-table/NSDataTable.vue.d.ts +98 -0
  16. package/dist/src/components/NS/data-table/index.d.ts +1 -0
  17. package/dist/src/components/NS/empty-state/NSEmptyState.vue.d.ts +45 -0
  18. package/dist/src/components/NS/empty-state/index.d.ts +1 -0
  19. package/dist/src/components/NS/index.d.ts +35 -0
  20. package/dist/src/components/NS/inline-notification/NSInlineNotification.vue.d.ts +44 -0
  21. package/dist/src/components/NS/inline-notification/index.d.ts +1 -0
  22. package/dist/src/components/NS/lottie-animation/NSLottieAnimation.vue.d.ts +75 -0
  23. package/dist/src/components/NS/lottie-animation/index.d.ts +1 -0
  24. package/dist/src/components/NS/modal/NSModal.vue.d.ts +56 -0
  25. package/dist/src/components/NS/modal/NSModalTrigger.vue.d.ts +64 -0
  26. package/dist/src/components/NS/modal/index.d.ts +2 -0
  27. package/dist/src/components/NS/pagination/NSPagination.vue.d.ts +36 -0
  28. package/dist/src/components/NS/pagination/index.d.ts +1 -0
  29. package/dist/src/components/NS/progress/NSProgress.vue.d.ts +37 -0
  30. package/dist/src/components/NS/progress/NSProgressBar.vue.d.ts +39 -0
  31. package/dist/src/components/NS/progress/index.d.ts +2 -0
  32. package/dist/src/components/NS/slider/NSByteSlider.vue.d.ts +50 -0
  33. package/dist/src/components/NS/slider/NSSlider.vue.d.ts +49 -0
  34. package/dist/src/components/NS/slider/index.d.ts +2 -0
  35. package/dist/src/components/NS/tag/NSTag.vue.d.ts +41 -0
  36. package/dist/src/components/NS/tag/index.d.ts +1 -0
  37. package/dist/src/components/NS/text-input/NSTextInput.vue.d.ts +67 -0
  38. package/dist/src/components/NS/text-input/index.d.ts +1 -0
  39. package/dist/src/components/NS/toast-notification/NSToastNotification.vue.d.ts +44 -0
  40. package/dist/src/components/NS/toast-notification/index.d.ts +1 -0
  41. package/dist/src/components/NS/toggle/NSToggle.vue.d.ts +51 -0
  42. package/dist/src/components/NS/toggle/index.d.ts +1 -0
  43. package/dist/src/components/NS/wizard/NSWizard.vue.d.ts +86 -0
  44. package/dist/src/components/NS/wizard/index.d.ts +1 -0
  45. package/dist/src/components/ui/button/Button.vue.d.ts +27 -0
  46. package/dist/src/components/ui/button/index.d.ts +7 -0
  47. package/dist/src/components/ui/card/Card.vue.d.ts +21 -0
  48. package/dist/src/components/ui/card/CardAction.vue.d.ts +21 -0
  49. package/dist/src/components/ui/card/CardContent.vue.d.ts +21 -0
  50. package/dist/src/components/ui/card/CardDescription.vue.d.ts +21 -0
  51. package/dist/src/components/ui/card/CardFooter.vue.d.ts +21 -0
  52. package/dist/src/components/ui/card/CardHeader.vue.d.ts +21 -0
  53. package/dist/src/components/ui/card/CardTitle.vue.d.ts +21 -0
  54. package/dist/src/components/ui/card/index.d.ts +7 -0
  55. package/dist/src/components/ui/checkbox/NsCheckbox.vue.d.ts +49 -0
  56. package/dist/src/components/ui/checkbox/index.d.ts +11 -0
  57. package/dist/src/components/ui/dialog/Dialog.vue.d.ts +28 -0
  58. package/dist/src/components/ui/dialog/index.d.ts +1 -0
  59. package/dist/src/components/ui/empty-state/NsEmptyState.vue.d.ts +45 -0
  60. package/dist/src/components/ui/empty-state/index.d.ts +11 -0
  61. package/dist/src/components/ui/inline-notification/NsInlineNotification.vue.d.ts +45 -0
  62. package/dist/src/components/ui/inline-notification/index.d.ts +6 -0
  63. package/dist/src/components/ui/input/Input.vue.d.ts +12 -0
  64. package/dist/src/components/ui/input/index.d.ts +1 -0
  65. package/dist/src/components/ui/input-group/InputGroup.vue.d.ts +21 -0
  66. package/dist/src/components/ui/input-group/InputGroupAddon.vue.d.ts +25 -0
  67. package/dist/src/components/ui/input-group/InputGroupButton.vue.d.ts +22 -0
  68. package/dist/src/components/ui/input-group/InputGroupInput.vue.d.ts +6 -0
  69. package/dist/src/components/ui/input-group/InputGroupText.vue.d.ts +21 -0
  70. package/dist/src/components/ui/input-group/InputGroupTextarea.vue.d.ts +6 -0
  71. package/dist/src/components/ui/input-group/index.d.ts +22 -0
  72. package/dist/src/components/ui/item/Item.vue.d.ts +27 -0
  73. package/dist/src/components/ui/item/ItemActions.vue.d.ts +21 -0
  74. package/dist/src/components/ui/item/ItemContent.vue.d.ts +21 -0
  75. package/dist/src/components/ui/item/ItemDescription.vue.d.ts +21 -0
  76. package/dist/src/components/ui/item/ItemFooter.vue.d.ts +21 -0
  77. package/dist/src/components/ui/item/ItemGroup.vue.d.ts +21 -0
  78. package/dist/src/components/ui/item/ItemHeader.vue.d.ts +21 -0
  79. package/dist/src/components/ui/item/ItemMedia.vue.d.ts +23 -0
  80. package/dist/src/components/ui/item/ItemSeparator.vue.d.ts +7 -0
  81. package/dist/src/components/ui/item/ItemTitle.vue.d.ts +21 -0
  82. package/dist/src/components/ui/item/index.d.ts +20 -0
  83. package/dist/src/components/ui/label/Label.vue.d.ts +22 -0
  84. package/dist/src/components/ui/label/index.d.ts +1 -0
  85. package/dist/src/components/ui/scroll-area/ScrollArea.vue.d.ts +22 -0
  86. package/dist/src/components/ui/scroll-area/ScrollBar.vue.d.ts +9 -0
  87. package/dist/src/components/ui/scroll-area/index.d.ts +2 -0
  88. package/dist/src/components/ui/select/Select.vue.d.ts +28 -0
  89. package/dist/src/components/ui/select/SelectContent.vue.d.ts +32 -0
  90. package/dist/src/components/ui/select/SelectGroup.vue.d.ts +18 -0
  91. package/dist/src/components/ui/select/SelectItem.vue.d.ts +23 -0
  92. package/dist/src/components/ui/select/SelectItemText.vue.d.ts +18 -0
  93. package/dist/src/components/ui/select/SelectLabel.vue.d.ts +22 -0
  94. package/dist/src/components/ui/select/SelectScrollDownButton.vue.d.ts +22 -0
  95. package/dist/src/components/ui/select/SelectScrollUpButton.vue.d.ts +22 -0
  96. package/dist/src/components/ui/select/SelectSeparator.vue.d.ts +7 -0
  97. package/dist/src/components/ui/select/SelectTrigger.vue.d.ts +25 -0
  98. package/dist/src/components/ui/select/SelectValue.vue.d.ts +18 -0
  99. package/dist/src/components/ui/select/index.d.ts +11 -0
  100. package/dist/src/components/ui/separator/Separator.vue.d.ts +10 -0
  101. package/dist/src/components/ui/separator/index.d.ts +1 -0
  102. package/dist/src/components/ui/sheet/Sheet.vue.d.ts +25 -0
  103. package/dist/src/components/ui/sheet/SheetClose.vue.d.ts +18 -0
  104. package/dist/src/components/ui/sheet/SheetContent.vue.d.ts +39 -0
  105. package/dist/src/components/ui/sheet/SheetDescription.vue.d.ts +22 -0
  106. package/dist/src/components/ui/sheet/SheetFooter.vue.d.ts +21 -0
  107. package/dist/src/components/ui/sheet/SheetHeader.vue.d.ts +21 -0
  108. package/dist/src/components/ui/sheet/SheetOverlay.vue.d.ts +22 -0
  109. package/dist/src/components/ui/sheet/SheetTitle.vue.d.ts +22 -0
  110. package/dist/src/components/ui/sheet/SheetTrigger.vue.d.ts +18 -0
  111. package/dist/src/components/ui/sheet/index.d.ts +8 -0
  112. package/dist/src/components/ui/sidebar/Sidebar.vue.d.ts +24 -0
  113. package/dist/src/components/ui/sidebar/SidebarContent.vue.d.ts +21 -0
  114. package/dist/src/components/ui/sidebar/SidebarFooter.vue.d.ts +21 -0
  115. package/dist/src/components/ui/sidebar/SidebarGroup.vue.d.ts +21 -0
  116. package/dist/src/components/ui/sidebar/SidebarGroupAction.vue.d.ts +22 -0
  117. package/dist/src/components/ui/sidebar/SidebarGroupContent.vue.d.ts +21 -0
  118. package/dist/src/components/ui/sidebar/SidebarGroupLabel.vue.d.ts +22 -0
  119. package/dist/src/components/ui/sidebar/SidebarHeader.vue.d.ts +21 -0
  120. package/dist/src/components/ui/sidebar/SidebarInput.vue.d.ts +21 -0
  121. package/dist/src/components/ui/sidebar/SidebarInset.vue.d.ts +21 -0
  122. package/dist/src/components/ui/sidebar/SidebarMenu.vue.d.ts +21 -0
  123. package/dist/src/components/ui/sidebar/SidebarMenuAction.vue.d.ts +25 -0
  124. package/dist/src/components/ui/sidebar/SidebarMenuBadge.vue.d.ts +21 -0
  125. package/dist/src/components/ui/sidebar/SidebarMenuButton.vue.d.ts +27 -0
  126. package/dist/src/components/ui/sidebar/SidebarMenuButtonChild.vue.d.ts +30 -0
  127. package/dist/src/components/ui/sidebar/SidebarMenuItem.vue.d.ts +21 -0
  128. package/dist/src/components/ui/sidebar/SidebarMenuSkeleton.vue.d.ts +7 -0
  129. package/dist/src/components/ui/sidebar/SidebarMenuSub.vue.d.ts +21 -0
  130. package/dist/src/components/ui/sidebar/SidebarMenuSubButton.vue.d.ts +27 -0
  131. package/dist/src/components/ui/sidebar/SidebarMenuSubItem.vue.d.ts +21 -0
  132. package/dist/src/components/ui/sidebar/SidebarProvider.vue.d.ts +30 -0
  133. package/dist/src/components/ui/sidebar/SidebarRail.vue.d.ts +21 -0
  134. package/dist/src/components/ui/sidebar/SidebarSeparator.vue.d.ts +21 -0
  135. package/dist/src/components/ui/sidebar/SidebarTrigger.vue.d.ts +6 -0
  136. package/dist/src/components/ui/sidebar/index.d.ts +37 -0
  137. package/dist/src/components/ui/sidebar/utils.d.ts +56 -0
  138. package/dist/src/components/ui/skeleton/Skeleton.vue.d.ts +6 -0
  139. package/dist/src/components/ui/skeleton/index.d.ts +1 -0
  140. package/dist/src/components/ui/sonner/Sonner.vue.d.ts +3 -0
  141. package/dist/src/components/ui/sonner/index.d.ts +1 -0
  142. package/dist/src/components/ui/switch/Switch.vue.d.ts +28 -0
  143. package/dist/src/components/ui/switch/index.d.ts +1 -0
  144. package/dist/src/components/ui/table/Table.vue.d.ts +21 -0
  145. package/dist/src/components/ui/table/TableBody.vue.d.ts +21 -0
  146. package/dist/src/components/ui/table/TableCaption.vue.d.ts +21 -0
  147. package/dist/src/components/ui/table/TableCell.vue.d.ts +21 -0
  148. package/dist/src/components/ui/table/TableEmpty.vue.d.ts +24 -0
  149. package/dist/src/components/ui/table/TableFooter.vue.d.ts +21 -0
  150. package/dist/src/components/ui/table/TableHead.vue.d.ts +21 -0
  151. package/dist/src/components/ui/table/TableHeader.vue.d.ts +21 -0
  152. package/dist/src/components/ui/table/TableRow.vue.d.ts +21 -0
  153. package/dist/src/components/ui/table/index.d.ts +9 -0
  154. package/dist/src/components/ui/table/utils.d.ts +3 -0
  155. package/dist/src/components/ui/tabs/Tabs.vue.d.ts +28 -0
  156. package/dist/src/components/ui/tabs/TabsContent.vue.d.ts +22 -0
  157. package/dist/src/components/ui/tabs/TabsList.vue.d.ts +22 -0
  158. package/dist/src/components/ui/tabs/TabsTrigger.vue.d.ts +22 -0
  159. package/dist/src/components/ui/tabs/index.d.ts +4 -0
  160. package/dist/src/components/ui/tag/NsTag.vue.d.ts +42 -0
  161. package/dist/src/components/ui/tag/index.d.ts +8 -0
  162. package/dist/src/components/ui/text-input/NsTextInput.vue.d.ts +79 -0
  163. package/dist/src/components/ui/text-input/index.d.ts +15 -0
  164. package/dist/src/components/ui/textarea/Textarea.vue.d.ts +12 -0
  165. package/dist/src/components/ui/textarea/index.d.ts +1 -0
  166. package/dist/src/components/ui/toggle/NsToggle.vue.d.ts +54 -0
  167. package/dist/src/components/ui/toggle/index.d.ts +15 -0
  168. package/dist/src/components/ui/tooltip/Tooltip.vue.d.ts +24 -0
  169. package/dist/src/components/ui/tooltip/TooltipContent.vue.d.ts +30 -0
  170. package/dist/src/components/ui/tooltip/TooltipProvider.vue.d.ts +20 -0
  171. package/dist/src/components/ui/tooltip/TooltipTrigger.vue.d.ts +18 -0
  172. package/dist/src/components/ui/tooltip/index.d.ts +4 -0
  173. package/dist/src/composables/index.d.ts +8 -0
  174. package/dist/src/composables/useDateTimeService.d.ts +10 -0
  175. package/dist/src/composables/useFilterService.d.ts +8 -0
  176. package/dist/src/composables/useIconService.d.ts +153 -0
  177. package/dist/src/composables/usePageTitleService.d.ts +3 -0
  178. package/dist/src/composables/useQueryParamService.d.ts +13 -0
  179. package/dist/src/composables/useStorageService.d.ts +5 -0
  180. package/dist/src/composables/useTaskService.d.ts +18 -0
  181. package/dist/src/composables/useUtilService.d.ts +27 -0
  182. package/dist/src/lib/utils.d.ts +2 -0
  183. package/dist/src/main.d.ts +0 -0
  184. package/dist/vite.svg +1 -0
  185. package/package.json +80 -0
  186. package/src/App.vue +30 -0
  187. package/src/components/HelloWorld.vue +41 -0
  188. package/src/components/NS/CompleteDemo.vue +475 -0
  189. package/src/components/NS/Demo.vue +191 -0
  190. package/src/components/NS/cards/NSBackupCard.vue +272 -0
  191. package/src/components/NS/cards/NSSystemInfoCard.vue +252 -0
  192. package/src/components/NS/cards/NSSystemdServiceCard.vue +250 -0
  193. package/src/components/NS/checkbox/NSCheckbox.vue +104 -0
  194. package/src/components/NS/data-table/NSDataTable.vue +434 -0
  195. package/src/components/NS/empty-state/NSEmptyState.vue +188 -0
  196. package/src/components/NS/inline-notification/NSInlineNotification.vue +162 -0
  197. package/src/components/NS/lottie-animation/NSLottieAnimation.vue +255 -0
  198. package/src/components/NS/modal/NSModal.vue +193 -0
  199. package/src/components/NS/modal/NSModalTrigger.vue +109 -0
  200. package/src/components/NS/pagination/NSPagination.vue +247 -0
  201. package/src/components/NS/progress/NSProgress.vue +115 -0
  202. package/src/components/NS/progress/NSProgressBar.vue +137 -0
  203. package/src/components/NS/slider/NSByteSlider.vue +144 -0
  204. package/src/components/NS/slider/NSSlider.vue +142 -0
  205. package/src/components/NS/tag/NSTag.vue +139 -0
  206. package/src/components/NS/text-input/NSTextInput.vue +242 -0
  207. package/src/components/NS/toast-notification/NSToastNotification.vue +163 -0
  208. package/src/components/NS/toggle/NSToggle.vue +156 -0
  209. package/src/components/NS/wizard/NSWizard.vue +399 -0
  210. package/src/components/ui/button/Button.vue +29 -0
  211. package/src/components/ui/card/Card.vue +22 -0
  212. package/src/components/ui/card/CardAction.vue +17 -0
  213. package/src/components/ui/card/CardContent.vue +17 -0
  214. package/src/components/ui/card/CardDescription.vue +17 -0
  215. package/src/components/ui/card/CardFooter.vue +17 -0
  216. package/src/components/ui/card/CardHeader.vue +17 -0
  217. package/src/components/ui/card/CardTitle.vue +17 -0
  218. package/src/components/ui/checkbox/NsCheckbox.vue +123 -0
  219. package/src/components/ui/dialog/Dialog.vue +68 -0
  220. package/src/components/ui/empty-state/NsEmptyState.vue +149 -0
  221. package/src/components/ui/inline-notification/NsInlineNotification.vue +163 -0
  222. package/src/components/ui/input/Input.vue +33 -0
  223. package/src/components/ui/input-group/InputGroup.vue +35 -0
  224. package/src/components/ui/input-group/InputGroupAddon.vue +36 -0
  225. package/src/components/ui/input-group/InputGroupButton.vue +21 -0
  226. package/src/components/ui/input-group/InputGroupInput.vue +19 -0
  227. package/src/components/ui/input-group/InputGroupText.vue +19 -0
  228. package/src/components/ui/input-group/InputGroupTextarea.vue +19 -0
  229. package/src/components/ui/item/Item.vue +27 -0
  230. package/src/components/ui/item/ItemActions.vue +17 -0
  231. package/src/components/ui/item/ItemContent.vue +17 -0
  232. package/src/components/ui/item/ItemDescription.vue +21 -0
  233. package/src/components/ui/item/ItemFooter.vue +17 -0
  234. package/src/components/ui/item/ItemGroup.vue +18 -0
  235. package/src/components/ui/item/ItemHeader.vue +17 -0
  236. package/src/components/ui/item/ItemMedia.vue +21 -0
  237. package/src/components/ui/item/ItemSeparator.vue +18 -0
  238. package/src/components/ui/item/ItemTitle.vue +17 -0
  239. package/src/components/ui/label/Label.vue +26 -0
  240. package/src/components/ui/scroll-area/ScrollArea.vue +33 -0
  241. package/src/components/ui/scroll-area/ScrollBar.vue +32 -0
  242. package/src/components/ui/select/Select.vue +19 -0
  243. package/src/components/ui/select/SelectContent.vue +51 -0
  244. package/src/components/ui/select/SelectGroup.vue +15 -0
  245. package/src/components/ui/select/SelectItem.vue +44 -0
  246. package/src/components/ui/select/SelectItemText.vue +15 -0
  247. package/src/components/ui/select/SelectLabel.vue +17 -0
  248. package/src/components/ui/select/SelectScrollDownButton.vue +26 -0
  249. package/src/components/ui/select/SelectScrollUpButton.vue +26 -0
  250. package/src/components/ui/select/SelectSeparator.vue +19 -0
  251. package/src/components/ui/select/SelectTrigger.vue +33 -0
  252. package/src/components/ui/select/SelectValue.vue +15 -0
  253. package/src/components/ui/separator/Separator.vue +29 -0
  254. package/src/components/ui/sheet/Sheet.vue +19 -0
  255. package/src/components/ui/sheet/SheetClose.vue +15 -0
  256. package/src/components/ui/sheet/SheetContent.vue +62 -0
  257. package/src/components/ui/sheet/SheetDescription.vue +21 -0
  258. package/src/components/ui/sheet/SheetFooter.vue +16 -0
  259. package/src/components/ui/sheet/SheetHeader.vue +15 -0
  260. package/src/components/ui/sheet/SheetOverlay.vue +21 -0
  261. package/src/components/ui/sheet/SheetTitle.vue +21 -0
  262. package/src/components/ui/sheet/SheetTrigger.vue +15 -0
  263. package/src/components/ui/sidebar/Sidebar.vue +96 -0
  264. package/src/components/ui/sidebar/SidebarContent.vue +18 -0
  265. package/src/components/ui/sidebar/SidebarFooter.vue +18 -0
  266. package/src/components/ui/sidebar/SidebarGroup.vue +18 -0
  267. package/src/components/ui/sidebar/SidebarGroupAction.vue +27 -0
  268. package/src/components/ui/sidebar/SidebarGroupContent.vue +18 -0
  269. package/src/components/ui/sidebar/SidebarGroupLabel.vue +25 -0
  270. package/src/components/ui/sidebar/SidebarHeader.vue +18 -0
  271. package/src/components/ui/sidebar/SidebarInput.vue +22 -0
  272. package/src/components/ui/sidebar/SidebarInset.vue +21 -0
  273. package/src/components/ui/sidebar/SidebarMenu.vue +18 -0
  274. package/src/components/ui/sidebar/SidebarMenuAction.vue +35 -0
  275. package/src/components/ui/sidebar/SidebarMenuBadge.vue +26 -0
  276. package/src/components/ui/sidebar/SidebarMenuButton.vue +48 -0
  277. package/src/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
  278. package/src/components/ui/sidebar/SidebarMenuItem.vue +18 -0
  279. package/src/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
  280. package/src/components/ui/sidebar/SidebarMenuSub.vue +22 -0
  281. package/src/components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
  282. package/src/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
  283. package/src/components/ui/sidebar/SidebarProvider.vue +82 -0
  284. package/src/components/ui/sidebar/SidebarRail.vue +33 -0
  285. package/src/components/ui/sidebar/SidebarSeparator.vue +19 -0
  286. package/src/components/ui/sidebar/SidebarTrigger.vue +27 -0
  287. package/src/components/ui/skeleton/Skeleton.vue +17 -0
  288. package/src/components/ui/sonner/Sonner.vue +42 -0
  289. package/src/components/ui/switch/Switch.vue +38 -0
  290. package/src/components/ui/table/Table.vue +16 -0
  291. package/src/components/ui/table/TableBody.vue +17 -0
  292. package/src/components/ui/table/TableCaption.vue +17 -0
  293. package/src/components/ui/table/TableCell.vue +22 -0
  294. package/src/components/ui/table/TableEmpty.vue +34 -0
  295. package/src/components/ui/table/TableFooter.vue +17 -0
  296. package/src/components/ui/table/TableHead.vue +17 -0
  297. package/src/components/ui/table/TableHeader.vue +17 -0
  298. package/src/components/ui/table/TableRow.vue +17 -0
  299. package/src/components/ui/tabs/Tabs.vue +24 -0
  300. package/src/components/ui/tabs/TabsContent.vue +21 -0
  301. package/src/components/ui/tabs/TabsList.vue +24 -0
  302. package/src/components/ui/tabs/TabsTrigger.vue +26 -0
  303. package/src/components/ui/tag/NsTag.vue +114 -0
  304. package/src/components/ui/text-input/NsTextInput.vue +269 -0
  305. package/src/components/ui/textarea/Textarea.vue +28 -0
  306. package/src/components/ui/toggle/NsToggle.vue +126 -0
  307. package/src/components/ui/tooltip/Tooltip.vue +19 -0
  308. package/src/components/ui/tooltip/TooltipContent.vue +34 -0
  309. package/src/components/ui/tooltip/TooltipProvider.vue +14 -0
  310. package/src/components/ui/tooltip/TooltipTrigger.vue +15 -0
@@ -0,0 +1,142 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { SliderRoot, SliderTrack, SliderRange, SliderThumb } from 'radix-vue'
4
+ import { cn } from '@/lib/utils'
5
+
6
+ interface Props {
7
+ modelValue?: number | number[]
8
+ defaultValue?: number | number[]
9
+ min?: number
10
+ max?: number
11
+ step?: number
12
+ disabled?: boolean
13
+ orientation?: 'horizontal' | 'vertical'
14
+ showLabel?: boolean
15
+ label?: string
16
+ showValue?: boolean
17
+ formatValue?: (value: number) => string
18
+ class?: string
19
+ trackClass?: string
20
+ thumbClass?: string
21
+ }
22
+
23
+ const props = withDefaults(defineProps<Props>(), {
24
+ min: 0,
25
+ max: 100,
26
+ step: 1,
27
+ orientation: 'horizontal',
28
+ showLabel: false,
29
+ showValue: true,
30
+ formatValue: (value: number) => value.toString(),
31
+ })
32
+
33
+ const emit = defineEmits<{
34
+ 'update:modelValue': [value: number | number[]]
35
+ }>()
36
+
37
+ const value = computed({
38
+ get: () => props.modelValue ?? props.defaultValue ?? 0,
39
+ set: (value: number | number[]) => {
40
+ emit('update:modelValue', value)
41
+ }
42
+ })
43
+
44
+ const isRange = computed(() => Array.isArray(value.value))
45
+
46
+ const displayValue = computed(() => {
47
+ if (isRange.value) {
48
+ return (value.value as number[]).map(v => props.formatValue(v)).join(' - ')
49
+ }
50
+ return props.formatValue(value.value as number)
51
+ })
52
+
53
+ const containerClasses = computed(() => {
54
+ const baseClasses = 'relative'
55
+
56
+ return cn(baseClasses, props.class)
57
+ })
58
+
59
+ const rootClasses = computed(() => {
60
+ const baseClasses = 'relative flex w-full touch-none select-none items-center'
61
+
62
+ const orientationClasses = {
63
+ horizontal: 'flex-col',
64
+ vertical: 'w-auto h-full flex-row'
65
+ }
66
+
67
+ return cn(baseClasses, orientationClasses[props.orientation])
68
+ })
69
+
70
+ const trackClasses = computed(() => {
71
+ const baseClasses = 'relative h-2 w-full grow overflow-hidden rounded-full bg-secondary'
72
+
73
+ const orientationClasses = {
74
+ horizontal: 'h-2 w-full',
75
+ vertical: 'w-2 h-full'
76
+ }
77
+
78
+ return cn(baseClasses, orientationClasses[props.orientation], props.trackClass)
79
+ })
80
+
81
+ const rangeClasses = computed(() => {
82
+ const baseClasses = 'absolute h-full bg-primary'
83
+
84
+ return cn(baseClasses)
85
+ })
86
+
87
+ const thumbClasses = computed(() => {
88
+ const baseClasses = 'block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50'
89
+
90
+ return cn(baseClasses, props.thumbClass)
91
+ })
92
+
93
+ const labelClasses = computed(() => {
94
+ return 'text-sm font-medium text-foreground'
95
+ })
96
+
97
+ const valueClasses = computed(() => {
98
+ return 'text-sm text-muted-foreground'
99
+ })
100
+ </script>
101
+
102
+ <template>
103
+ <div :class="containerClasses">
104
+ <!-- Label -->
105
+ <div
106
+ v-if="showLabel && (label || $slots.label)"
107
+ class="flex items-center justify-between mb-2"
108
+ >
109
+ <div :class="labelClasses">
110
+ <slot name="label">{{ label }}</slot>
111
+ </div>
112
+ <div v-if="showValue" :class="valueClasses">
113
+ {{ displayValue }}
114
+ </div>
115
+ </div>
116
+
117
+ <!-- Slider -->
118
+ <SliderRoot
119
+ :class="rootClasses"
120
+ v-model="value"
121
+ :min="min"
122
+ :max="max"
123
+ :step="step"
124
+ :disabled="disabled"
125
+ :orientation="orientation"
126
+ >
127
+ <SliderTrack :class="trackClasses">
128
+ <SliderRange :class="rangeClasses" />
129
+ </SliderTrack>
130
+ <SliderThumb
131
+ v-for="(_, index) in isRange ? 2 : 1"
132
+ :key="index"
133
+ :class="thumbClasses"
134
+ />
135
+ </SliderRoot>
136
+
137
+ <!-- Custom value display -->
138
+ <div v-if="$slots.value" class="mt-2">
139
+ <slot name="value" :value="value" :display-value="displayValue" />
140
+ </div>
141
+ </div>
142
+ </template>
@@ -0,0 +1,139 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { X } from 'lucide-vue-next'
4
+ import { cn } from '@/lib/utils'
5
+
6
+ interface Props {
7
+ label?: string
8
+ variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'success' | 'warning' | 'info' | 'gray'
9
+ size?: 'default' | 'sm' | 'lg'
10
+ interactive?: boolean
11
+ removable?: boolean
12
+ disabled?: boolean
13
+ icon?: string | object
14
+ clearAriaLabel?: string
15
+ title?: string
16
+ class?: string
17
+ }
18
+
19
+ const props = withDefaults(defineProps<Props>(), {
20
+ variant: 'default',
21
+ size: 'default',
22
+ interactive: false,
23
+ removable: false,
24
+ clearAriaLabel: 'Remove tag',
25
+ })
26
+
27
+ const emit = defineEmits<{
28
+ remove: []
29
+ click: [event: Event]
30
+ }>()
31
+
32
+ const isClickable = computed(() => props.interactive && !props.disabled)
33
+ const isFilter = computed(() => props.removable)
34
+
35
+ const tagClasses = computed(() => {
36
+ const baseClasses = 'inline-flex items-center gap-1 rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2'
37
+
38
+ const variantClasses = {
39
+ default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
40
+ secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
41
+ destructive: 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
42
+ outline: 'text-foreground',
43
+ success: 'border-transparent bg-green-500 text-white hover:bg-green-600',
44
+ warning: 'border-transparent bg-orange-500 text-white hover:bg-orange-600',
45
+ info: 'border-transparent bg-blue-500 text-white hover:bg-blue-600',
46
+ gray: 'border-transparent bg-gray-100 text-gray-800 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700'
47
+ }
48
+
49
+ const sizeClasses = {
50
+ default: 'px-2.5 py-0.5 text-xs',
51
+ sm: 'px-2 py-0.5 text-xs',
52
+ lg: 'px-3 py-1 text-sm'
53
+ }
54
+
55
+ return cn(
56
+ baseClasses,
57
+ variantClasses[props.variant],
58
+ sizeClasses[props.size],
59
+ {
60
+ 'cursor-pointer hover:opacity-80': isClickable.value,
61
+ 'opacity-50 cursor-not-allowed': disabled,
62
+ 'cursor-default': isFilter.value && !interactive,
63
+ },
64
+ props.class
65
+ )
66
+ })
67
+
68
+ const handleClick = (event: Event) => {
69
+ if (isClickable.value) {
70
+ emit('click', event)
71
+ }
72
+ }
73
+
74
+ const handleRemove = (event: Event) => {
75
+ event.stopPropagation()
76
+ if (!props.disabled) {
77
+ emit('remove')
78
+ }
79
+ }
80
+
81
+ const handleKeydown = (event: KeyboardEvent) => {
82
+ if (isFilter.value && (event.key === 'Enter' || event.key === ' ')) {
83
+ event.preventDefault()
84
+ handleRemove(event)
85
+ }
86
+ }
87
+ </script>
88
+
89
+ <template>
90
+ <component
91
+ :is="isClickable ? 'button' : 'div'"
92
+ :class="tagClasses"
93
+ :disabled="disabled"
94
+ :title="title || label"
95
+ @click="handleClick"
96
+ @keydown="handleKeydown"
97
+ :aria-label="label"
98
+ role="button"
99
+ tabindex="0"
100
+ >
101
+ <!-- Icon slot/component -->
102
+ <div v-if="icon && !isFilter" class="flex items-center justify-center">
103
+ <component
104
+ v-if="typeof icon === 'object'"
105
+ :is="icon"
106
+ class="h-3 w-3"
107
+ />
108
+ <div
109
+ v-else-if="typeof icon === 'string'"
110
+ v-html="icon"
111
+ class="h-3 w-3"
112
+ />
113
+ </div>
114
+
115
+ <!-- Label content -->
116
+ <span class="truncate max-w-[200px]">
117
+ <slot>{{ label }}</slot>
118
+ </span>
119
+
120
+ <!-- Remove button for filter tags -->
121
+ <button
122
+ v-if="isFilter"
123
+ type="button"
124
+ :class="cn(
125
+ 'flex items-center justify-center rounded-full hover:bg-black/10 dark:hover:bg-white/10 transition-colors',
126
+ 'ml-1 -mr-1 h-4 w-4 p-0',
127
+ {
128
+ 'opacity-50 cursor-not-allowed': disabled,
129
+ }
130
+ )"
131
+ :disabled="disabled"
132
+ :aria-label="clearAriaLabel"
133
+ :title="clearAriaLabel"
134
+ @click="handleRemove"
135
+ >
136
+ <X class="h-3 w-3" />
137
+ </button>
138
+ </component>
139
+ </template>
@@ -0,0 +1,242 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref, nextTick, useId } from 'vue'
3
+ import { Eye, EyeOff, AlertCircle, AlertTriangle, CheckCircle } from 'lucide-vue-next'
4
+ import { cn } from '@/lib/utils'
5
+
6
+ interface Props {
7
+ modelValue?: string
8
+ value?: string
9
+ defaultValue?: string
10
+ type?: string
11
+ placeholder?: string
12
+ disabled?: boolean
13
+ readonly?: boolean
14
+ required?: boolean
15
+ name?: string
16
+ id?: string
17
+ label?: string
18
+ description?: string
19
+ errorMessage?: string
20
+ warningMessage?: string
21
+ successMessage?: string
22
+ helperText?: string
23
+ prefix?: string
24
+ suffix?: string
25
+ showPasswordToggle?: boolean
26
+ class?: string
27
+ labelClass?: string
28
+ inputClass?: string
29
+ containerClass?: string
30
+ }
31
+
32
+ const props = withDefaults(defineProps<Props>(), {
33
+ type: 'text',
34
+ showPasswordToggle: false,
35
+ })
36
+
37
+ const emit = defineEmits<{
38
+ 'update:modelValue': [value: string]
39
+ 'update:value': [value: string]
40
+ 'focus': [event: FocusEvent]
41
+ 'blur': [event: BlurEvent]
42
+ 'input': [event: Event]
43
+ 'change': [event: Event]
44
+ }>()
45
+
46
+ const uniqueId = useId()
47
+ const inputId = props.id || uniqueId
48
+
49
+ const isPasswordVisible = ref(false)
50
+ const isFocused = ref(false)
51
+ const inputRef = ref<HTMLInputElement>()
52
+
53
+ const isPassword = computed(() => props.type === 'password')
54
+ const actualType = computed(() => {
55
+ if (isPassword.value) {
56
+ return isPasswordVisible.value ? 'text' : 'password'
57
+ }
58
+ return props.type
59
+ })
60
+
61
+ const inputVariant = computed(() => {
62
+ if (props.errorMessage) return 'destructive'
63
+ if (props.warningMessage) return 'warning'
64
+ if (props.successMessage) return 'success'
65
+ return 'default'
66
+ })
67
+
68
+ const displayMessage = computed(() => {
69
+ if (props.errorMessage) return props.errorMessage
70
+ if (props.warningMessage) return props.warningMessage
71
+ if (props.successMessage) return props.successMessage
72
+ if (props.helperText) return props.helperText
73
+ return ''
74
+ })
75
+
76
+ const StatusIcon = computed(() => {
77
+ if (props.errorMessage) return AlertCircle
78
+ if (props.warningMessage) return AlertTriangle
79
+ if (props.successMessage) return CheckCircle
80
+ return null
81
+ })
82
+
83
+ const internalValue = computed({
84
+ get: () => props.modelValue ?? props.value ?? '',
85
+ set: (value: string) => {
86
+ emit('update:modelValue', value)
87
+ emit('update:value', value)
88
+ }
89
+ })
90
+
91
+ const togglePasswordVisibility = () => {
92
+ isPasswordVisible.value = !isPasswordVisible.value
93
+ }
94
+
95
+ const handleInput = (event: Event) => {
96
+ const target = event.target as HTMLInputElement
97
+ internalValue.value = target.value
98
+ emit('input', event)
99
+ }
100
+
101
+ const handleChange = (event: Event) => {
102
+ emit('change', event)
103
+ }
104
+
105
+ const handleFocus = (event: FocusEvent) => {
106
+ isFocused.value = true
107
+ emit('focus', event)
108
+ }
109
+
110
+ const handleBlur = (event: BlurEvent) => {
111
+ isFocused.value = false
112
+ emit('blur', event)
113
+ }
114
+
115
+ const focus = () => {
116
+ nextTick(() => {
117
+ inputRef.value?.focus()
118
+ })
119
+ }
120
+
121
+ defineExpose({
122
+ focus,
123
+ inputRef,
124
+ })
125
+ </script>
126
+
127
+ <template>
128
+ <div :class="cn('grid w-full max-w-sm gap-1.5', containerClass)">
129
+ <label
130
+ v-if="label || $slots.label"
131
+ :for="inputId"
132
+ :class="cn(
133
+ 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
134
+ {
135
+ 'text-destructive': errorMessage,
136
+ 'text-orange-600': warningMessage,
137
+ 'text-green-600': successMessage
138
+ },
139
+ labelClass
140
+ )"
141
+ >
142
+ <slot name="label">{{ label }}</slot>
143
+ </label>
144
+
145
+ <div class="relative">
146
+ <span
147
+ v-if="prefix"
148
+ :class="cn(
149
+ 'absolute left-3 top-1/2 transform -translate-y-1/2 text-sm text-muted-foreground',
150
+ {
151
+ 'text-destructive': errorMessage,
152
+ 'text-orange-600': warningMessage,
153
+ 'text-green-600': successMessage
154
+ }
155
+ )"
156
+ >
157
+ {{ prefix }}
158
+ </span>
159
+
160
+ <input
161
+ :id="inputId"
162
+ ref="inputRef"
163
+ :type="actualType"
164
+ :class="cn(
165
+ 'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
166
+ {
167
+ 'pl-8': prefix,
168
+ 'pr-16': (isPassword && showPasswordToggle) || StatusIcon,
169
+ 'pr-12': StatusIcon && !(isPassword && showPasswordToggle),
170
+ 'border-destructive text-destructive focus:border-destructive': inputVariant === 'destructive',
171
+ 'border-orange-500 text-orange-600 focus:border-orange-500': inputVariant === 'warning',
172
+ 'border-green-500 text-green-600 focus:border-green-500': inputVariant === 'success',
173
+ 'ring-2 ring-ring ring-offset-2': isFocused,
174
+ },
175
+ inputClass
176
+ )"
177
+ :placeholder="placeholder"
178
+ :disabled="disabled"
179
+ :readonly="readonly"
180
+ :required="required"
181
+ :name="name"
182
+ :value="internalValue"
183
+ @input="handleInput"
184
+ @change="handleChange"
185
+ @focus="handleFocus"
186
+ @blur="handleBlur"
187
+ v-bind="$attrs"
188
+ />
189
+
190
+ <div
191
+ v-if="StatusIcon || (isPassword && showPasswordToggle)"
192
+ class="absolute right-0 top-1/2 transform -translate-y-1/2 flex items-center gap-1 pr-3"
193
+ >
194
+ <component
195
+ v-if="StatusIcon"
196
+ :is="StatusIcon"
197
+ :class="cn('h-4 w-4', {
198
+ 'text-destructive': inputVariant === 'destructive',
199
+ 'text-orange-600': inputVariant === 'warning',
200
+ 'text-green-600': inputVariant === 'success'
201
+ })"
202
+ />
203
+
204
+ <button
205
+ v-if="isPassword && showPasswordToggle"
206
+ type="button"
207
+ :class="cn(
208
+ 'text-muted-foreground hover:text-foreground transition-colors',
209
+ {
210
+ 'pointer-events-none': disabled
211
+ }
212
+ )"
213
+ :disabled="disabled"
214
+ @click="togglePasswordVisibility"
215
+ :title="isPasswordVisible ? 'Hide password' : 'Show password'"
216
+ >
217
+ <EyeOff v-if="isPasswordVisible" class="h-4 w-4" />
218
+ <Eye v-else class="h-4 w-4" />
219
+ </button>
220
+ </div>
221
+ </div>
222
+
223
+ <p
224
+ v-if="displayMessage"
225
+ :class="cn('text-sm flex items-start gap-1', {
226
+ 'text-muted-foreground': !errorMessage && !warningMessage && !successMessage,
227
+ 'text-destructive': errorMessage,
228
+ 'text-orange-600': warningMessage,
229
+ 'text-green-600': successMessage
230
+ })"
231
+ >
232
+ <component
233
+ v-if="StatusIcon"
234
+ :is="StatusIcon"
235
+ class="h-4 w-4 mt-0.5 flex-shrink-0"
236
+ />
237
+ <span>{{ displayMessage }}</span>
238
+ </p>
239
+
240
+ <slot name="extra" />
241
+ </div>
242
+ </template>
@@ -0,0 +1,163 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { X, AlertCircle, AlertTriangle, CheckCircle, Info, Loader2 } from 'lucide-vue-next'
4
+ import { cn } from '@/lib/utils'
5
+
6
+ interface Props {
7
+ title?: string
8
+ description?: string
9
+ variant?: 'default' | 'destructive' | 'warning' | 'success' | 'info'
10
+ duration?: number
11
+ showCloseButton?: boolean
12
+ actionLabel?: string
13
+ closeAriaLabel?: string
14
+ position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center'
15
+ loading?: boolean
16
+ class?: string
17
+ icon?: object
18
+ }
19
+
20
+ const props = withDefaults(defineProps<Props>(), {
21
+ variant: 'info',
22
+ duration: 5000,
23
+ showCloseButton: true,
24
+ closeAriaLabel: 'Close notification',
25
+ position: 'top-right',
26
+ loading: false,
27
+ })
28
+
29
+ const emit = defineEmits<{
30
+ close: []
31
+ action: []
32
+ }>()
33
+
34
+ const NotificationIcon = computed(() => {
35
+ if (props.icon) {
36
+ return props.icon
37
+ }
38
+
39
+ switch (props.variant) {
40
+ case 'destructive':
41
+ return AlertCircle
42
+ case 'warning':
43
+ return AlertTriangle
44
+ case 'success':
45
+ return CheckCircle
46
+ case 'info':
47
+ default:
48
+ return Info
49
+ }
50
+ })
51
+
52
+ const containerClasses = computed(() => {
53
+ const baseClasses = 'group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:animate-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full md:data-[state=open]:slide-in-from-right-full'
54
+
55
+ const variantClasses = {
56
+ default: 'border-border bg-background text-foreground',
57
+ destructive: 'border-destructive/50 bg-destructive text-destructive-foreground',
58
+ warning: 'border-orange-500/50 bg-orange-500 text-white',
59
+ success: 'border-green-500/50 bg-green-500 text-white',
60
+ info: 'border-blue-500/50 bg-blue-500 text-white'
61
+ }
62
+
63
+ return cn(baseClasses, variantClasses[props.variant], props.class)
64
+ })
65
+
66
+ const positionClasses = computed(() => {
67
+ const baseClasses = 'fixed z-50 flex flex-col-reverse p-4'
68
+
69
+ const positionMap = {
70
+ 'top-right': 'top-0 right-0',
71
+ 'top-left': 'top-0 left-0',
72
+ 'bottom-right': 'bottom-0 right-0',
73
+ 'bottom-left': 'bottom-0 left-0',
74
+ 'top-center': 'top-0 left-1/2 -translate-x-1/2',
75
+ 'bottom-center': 'bottom-0 left-1/2 -translate-x-1/2'
76
+ }
77
+
78
+ return cn(baseClasses, positionMap[props.position])
79
+ })
80
+ </script>
81
+
82
+ <template>
83
+ <div :class="positionClasses">
84
+ <div :class="containerClasses">
85
+ <!-- Icon -->
86
+ <component
87
+ :is="NotificationIcon"
88
+ class="h-5 w-5 shrink-0"
89
+ />
90
+
91
+ <!-- Loading spinner -->
92
+ <Loader2
93
+ v-if="loading"
94
+ class="h-5 w-5 shrink-0 animate-spin"
95
+ />
96
+
97
+ <!-- Content -->
98
+ <div class="grid gap-1">
99
+ <!-- Title -->
100
+ <div v-if="title || $slots.title" class="text-sm font-semibold">
101
+ <slot name="title">{{ title }}</slot>
102
+ </div>
103
+
104
+ <!-- Description -->
105
+ <div v-if="description || $slots.description" class="text-sm opacity-90">
106
+ <slot name="description">{{ description }}</slot>
107
+ </div>
108
+
109
+ <!-- Action button -->
110
+ <button
111
+ v-if="actionLabel"
112
+ type="button"
113
+ :class="cn(
114
+ 'inline-flex items-center text-sm font-medium underline underline-offset-2 hover:no-underline',
115
+ {
116
+ 'text-destructive-foreground': variant === 'destructive',
117
+ 'text-white': ['warning', 'success', 'info'].includes(variant),
118
+ 'text-foreground': variant === 'default'
119
+ }
120
+ )"
121
+ @click="emit('action')"
122
+ >
123
+ {{ actionLabel }}
124
+ </button>
125
+ </div>
126
+
127
+ <!-- Close button -->
128
+ <button
129
+ v-if="showCloseButton"
130
+ type="button"
131
+ :aria-label="closeAriaLabel"
132
+ :class="cn(
133
+ 'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100',
134
+ {
135
+ 'text-destructive-foreground/50 hover:text-destructive-foreground': variant === 'destructive',
136
+ 'text-white/50 hover:text-white': ['warning', 'success', 'info'].includes(variant)
137
+ }
138
+ )"
139
+ @click="emit('close')"
140
+ >
141
+ <X class="h-4 w-4" />
142
+ </button>
143
+
144
+ <!-- Progress bar for auto-dismiss -->
145
+ <div
146
+ v-if="duration > 0"
147
+ class="absolute bottom-0 left-0 h-1 bg-black/20 dark:bg-white/20 animate-[shrink_x_var(--duration)_linear_forwards]"
148
+ :style="{ '--duration': `${duration}ms` }"
149
+ />
150
+ </div>
151
+ </div>
152
+ </template>
153
+
154
+ <style scoped>
155
+ @keyframes shrink-x {
156
+ from {
157
+ width: 100%;
158
+ }
159
+ to {
160
+ width: 0%;
161
+ }
162
+ }
163
+ </style>