@oneclick.dev/cms-kit 0.0.1

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 (435) hide show
  1. package/README.md +1 -0
  2. package/components/ui/apple-emoji/AppleEmoji.vue +32 -0
  3. package/components/ui/apple-emoji/index.ts +1 -0
  4. package/components/ui/avatar/Avatar.vue +18 -0
  5. package/components/ui/avatar/AvatarFallback.vue +23 -0
  6. package/components/ui/avatar/AvatarImage.vue +16 -0
  7. package/components/ui/avatar/index.ts +3 -0
  8. package/components/ui/badge/Badge.vue +39 -0
  9. package/components/ui/badge/index.ts +1 -0
  10. package/components/ui/breadcrumb/Breadcrumb.vue +17 -0
  11. package/components/ui/breadcrumb/BreadcrumbEllipsis.vue +23 -0
  12. package/components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
  13. package/components/ui/breadcrumb/BreadcrumbLink.vue +20 -0
  14. package/components/ui/breadcrumb/BreadcrumbList.vue +17 -0
  15. package/components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
  16. package/components/ui/breadcrumb/BreadcrumbSeparator.vue +22 -0
  17. package/components/ui/breadcrumb/index.ts +7 -0
  18. package/components/ui/button/Button.vue +39 -0
  19. package/components/ui/button/index.ts +36 -0
  20. package/components/ui/calendar/Calendar.vue +63 -0
  21. package/components/ui/calendar/CalendarCell.vue +25 -0
  22. package/components/ui/calendar/CalendarCellTrigger.vue +41 -0
  23. package/components/ui/calendar/CalendarGrid.vue +25 -0
  24. package/components/ui/calendar/CalendarGridBody.vue +14 -0
  25. package/components/ui/calendar/CalendarGridHead.vue +15 -0
  26. package/components/ui/calendar/CalendarGridRow.vue +24 -0
  27. package/components/ui/calendar/CalendarHeadCell.vue +25 -0
  28. package/components/ui/calendar/CalendarHeader.vue +25 -0
  29. package/components/ui/calendar/CalendarHeading.vue +32 -0
  30. package/components/ui/calendar/CalendarNextButton.vue +34 -0
  31. package/components/ui/calendar/CalendarPrevButton.vue +34 -0
  32. package/components/ui/calendar/index.ts +12 -0
  33. package/components/ui/card/Card.vue +22 -0
  34. package/components/ui/card/CardAction.vue +17 -0
  35. package/components/ui/card/CardContent.vue +17 -0
  36. package/components/ui/card/CardDescription.vue +17 -0
  37. package/components/ui/card/CardFooter.vue +17 -0
  38. package/components/ui/card/CardHeader.vue +17 -0
  39. package/components/ui/card/CardTitle.vue +17 -0
  40. package/components/ui/card/index.ts +7 -0
  41. package/components/ui/checkbox/Checkbox.vue +37 -0
  42. package/components/ui/checkbox/index.ts +1 -0
  43. package/components/ui/code-editor/CodeEditor.vue +88 -0
  44. package/components/ui/code-editor/index.ts +1 -0
  45. package/components/ui/code-editor/interpolationHighlight.ts +39 -0
  46. package/components/ui/collapsible/Collapsible.vue +19 -0
  47. package/components/ui/collapsible/CollapsibleContent.vue +14 -0
  48. package/components/ui/collapsible/CollapsibleTrigger.vue +14 -0
  49. package/components/ui/collapsible/index.ts +3 -0
  50. package/components/ui/combobox/Combobox.vue +17 -0
  51. package/components/ui/combobox/ComboboxAnchor.vue +26 -0
  52. package/components/ui/combobox/ComboboxEmpty.vue +24 -0
  53. package/components/ui/combobox/ComboboxGroup.vue +30 -0
  54. package/components/ui/combobox/ComboboxInput.vue +47 -0
  55. package/components/ui/combobox/ComboboxItem.vue +27 -0
  56. package/components/ui/combobox/ComboboxItemIndicator.vue +26 -0
  57. package/components/ui/combobox/ComboboxList.vue +31 -0
  58. package/components/ui/combobox/ComboboxSeparator.vue +24 -0
  59. package/components/ui/combobox/ComboboxTrigger.vue +27 -0
  60. package/components/ui/combobox/ComboboxViewport.vue +26 -0
  61. package/components/ui/combobox/index.ts +12 -0
  62. package/components/ui/command/Command.vue +93 -0
  63. package/components/ui/command/CommandDialog.vue +31 -0
  64. package/components/ui/command/CommandEmpty.vue +29 -0
  65. package/components/ui/command/CommandGroup.vue +47 -0
  66. package/components/ui/command/CommandInput.vue +41 -0
  67. package/components/ui/command/CommandItem.vue +79 -0
  68. package/components/ui/command/CommandList.vue +28 -0
  69. package/components/ui/command/CommandSeparator.vue +24 -0
  70. package/components/ui/command/CommandShortcut.vue +17 -0
  71. package/components/ui/command/index.ts +25 -0
  72. package/components/ui/context-menu/ContextMenu.vue +18 -0
  73. package/components/ui/context-menu/ContextMenuCheckboxItem.vue +41 -0
  74. package/components/ui/context-menu/ContextMenuContent.vue +37 -0
  75. package/components/ui/context-menu/ContextMenuGroup.vue +14 -0
  76. package/components/ui/context-menu/ContextMenuItem.vue +42 -0
  77. package/components/ui/context-menu/ContextMenuLabel.vue +24 -0
  78. package/components/ui/context-menu/ContextMenuPortal.vue +14 -0
  79. package/components/ui/context-menu/ContextMenuRadioGroup.vue +22 -0
  80. package/components/ui/context-menu/ContextMenuRadioItem.vue +41 -0
  81. package/components/ui/context-menu/ContextMenuSeparator.vue +24 -0
  82. package/components/ui/context-menu/ContextMenuShortcut.vue +17 -0
  83. package/components/ui/context-menu/ContextMenuSub.vue +22 -0
  84. package/components/ui/context-menu/ContextMenuSubContent.vue +36 -0
  85. package/components/ui/context-menu/ContextMenuSubTrigger.vue +35 -0
  86. package/components/ui/context-menu/ContextMenuTrigger.vue +16 -0
  87. package/components/ui/context-menu/index.ts +14 -0
  88. package/components/ui/custom-dialog/CustomDialog.vue +31 -0
  89. package/components/ui/custom-dialog/index.ts +1 -0
  90. package/components/ui/dialog/Dialog.vue +17 -0
  91. package/components/ui/dialog/DialogClose.vue +14 -0
  92. package/components/ui/dialog/DialogContent.vue +49 -0
  93. package/components/ui/dialog/DialogDescription.vue +25 -0
  94. package/components/ui/dialog/DialogFooter.vue +15 -0
  95. package/components/ui/dialog/DialogHeader.vue +17 -0
  96. package/components/ui/dialog/DialogOverlay.vue +23 -0
  97. package/components/ui/dialog/DialogScrollContent.vue +59 -0
  98. package/components/ui/dialog/DialogTitle.vue +25 -0
  99. package/components/ui/dialog/DialogTrigger.vue +14 -0
  100. package/components/ui/dialog/index.ts +10 -0
  101. package/components/ui/drawer/Drawer.vue +22 -0
  102. package/components/ui/drawer/DrawerClose.vue +15 -0
  103. package/components/ui/drawer/DrawerContent.vue +34 -0
  104. package/components/ui/drawer/DrawerDescription.vue +24 -0
  105. package/components/ui/drawer/DrawerFooter.vue +17 -0
  106. package/components/ui/drawer/DrawerHeader.vue +17 -0
  107. package/components/ui/drawer/DrawerOverlay.vue +22 -0
  108. package/components/ui/drawer/DrawerTitle.vue +24 -0
  109. package/components/ui/drawer/DrawerTrigger.vue +15 -0
  110. package/components/ui/drawer/index.ts +9 -0
  111. package/components/ui/dropdown-menu/DropdownMenu.vue +17 -0
  112. package/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +41 -0
  113. package/components/ui/dropdown-menu/DropdownMenuContent.vue +39 -0
  114. package/components/ui/dropdown-menu/DropdownMenuGroup.vue +14 -0
  115. package/components/ui/dropdown-menu/DropdownMenuItem.vue +30 -0
  116. package/components/ui/dropdown-menu/DropdownMenuLabel.vue +22 -0
  117. package/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +22 -0
  118. package/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +42 -0
  119. package/components/ui/dropdown-menu/DropdownMenuSeparator.vue +26 -0
  120. package/components/ui/dropdown-menu/DropdownMenuShortcut.vue +17 -0
  121. package/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
  122. package/components/ui/dropdown-menu/DropdownMenuSubContent.vue +31 -0
  123. package/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +30 -0
  124. package/components/ui/dropdown-menu/DropdownMenuTrigger.vue +16 -0
  125. package/components/ui/dropdown-menu/index.ts +16 -0
  126. package/components/ui/emoji-picker/EmojiPicker.vue +71 -0
  127. package/components/ui/emoji-picker/index.ts +1 -0
  128. package/components/ui/flow-builder/FlowBuilder.vue +48 -0
  129. package/components/ui/flow-builder/FlowBuilderSelect.vue +33 -0
  130. package/components/ui/flow-builder/components/Builder.vue +287 -0
  131. package/components/ui/flow-builder/components/DropzoneBackground.vue +13 -0
  132. package/components/ui/flow-builder/components/EdgeEditor.vue +36 -0
  133. package/components/ui/flow-builder/components/Menu.vue +78 -0
  134. package/components/ui/flow-builder/components/NodeEditor.vue +181 -0
  135. package/components/ui/flow-builder/components/edge-editor/DataEdgeEditor.vue +64 -0
  136. package/components/ui/flow-builder/components/edges/DataEdge.vue +87 -0
  137. package/components/ui/flow-builder/components/node-editor/ApiRequestEditor.vue +150 -0
  138. package/components/ui/flow-builder/components/node-editor/ConfettiEditor.vue +23 -0
  139. package/components/ui/flow-builder/components/node-editor/ConfirmEditor.vue +30 -0
  140. package/components/ui/flow-builder/components/node-editor/FinishFlowEditor.vue +33 -0
  141. package/components/ui/flow-builder/components/node-editor/IntegrationActionEditor.vue +50 -0
  142. package/components/ui/flow-builder/components/node-editor/SetVariableEditor.vue +71 -0
  143. package/components/ui/flow-builder/components/node-editor/ShowDialogEditor.vue +26 -0
  144. package/components/ui/flow-builder/components/node-editor/ShowModuleEditor.vue +92 -0
  145. package/components/ui/flow-builder/components/node-editor/ShowToastEditor.vue +28 -0
  146. package/components/ui/flow-builder/components/node-editor/TransformDataEditor.vue +27 -0
  147. package/components/ui/flow-builder/components/node-editor/VisitUrlEditor.vue +31 -0
  148. package/components/ui/flow-builder/components/nodes/ApiRequestNode.vue +45 -0
  149. package/components/ui/flow-builder/components/nodes/ConfettiNode.vue +32 -0
  150. package/components/ui/flow-builder/components/nodes/ConfirmNode.vue +45 -0
  151. package/components/ui/flow-builder/components/nodes/EmptyNode.vue +26 -0
  152. package/components/ui/flow-builder/components/nodes/FinishFlowNode.vue +30 -0
  153. package/components/ui/flow-builder/components/nodes/IntegrationActionNode.vue +52 -0
  154. package/components/ui/flow-builder/components/nodes/SetVariableNode.vue +34 -0
  155. package/components/ui/flow-builder/components/nodes/ShowDialogNode.vue +34 -0
  156. package/components/ui/flow-builder/components/nodes/ShowModuleNode.vue +34 -0
  157. package/components/ui/flow-builder/components/nodes/ShowToastNode.vue +35 -0
  158. package/components/ui/flow-builder/components/nodes/StartNode.vue +27 -0
  159. package/components/ui/flow-builder/components/nodes/TransformDataNode.vue +34 -0
  160. package/components/ui/flow-builder/components/nodes/VisitUrlNode.vue +34 -0
  161. package/components/ui/flow-builder/components/toolbars/ApiRequestToolbar.vue +45 -0
  162. package/components/ui/flow-builder/components/toolbars/ConfirmToolbar.vue +45 -0
  163. package/components/ui/flow-builder/components/toolbars/DefaultToolbar.vue +42 -0
  164. package/components/ui/flow-builder/components/toolbars/IntegrationActionToolbar.vue +45 -0
  165. package/components/ui/flow-builder/components/variables/Widget.vue +137 -0
  166. package/components/ui/flow-builder/composables/outputRegistry.ts +33 -0
  167. package/components/ui/flow-builder/composables/taskHandlers.js +185 -0
  168. package/components/ui/flow-builder/composables/useLayout.js +60 -0
  169. package/components/ui/flow-builder/composables/useProcessNodeLogic.js +55 -0
  170. package/components/ui/flow-builder/composables/useRunProcess.js +267 -0
  171. package/components/ui/flow-builder/index.ts +5 -0
  172. package/components/ui/flow-builder/types.ts +7 -0
  173. package/components/ui/form/FormControl.vue +17 -0
  174. package/components/ui/form/FormDescription.vue +21 -0
  175. package/components/ui/form/FormItem.vue +22 -0
  176. package/components/ui/form/FormLabel.vue +25 -0
  177. package/components/ui/form/FormMessage.vue +22 -0
  178. package/components/ui/form/index.ts +7 -0
  179. package/components/ui/form/injectionKeys.ts +4 -0
  180. package/components/ui/form/useFormField.ts +30 -0
  181. package/components/ui/form-builder/FormBuilder.vue +45 -0
  182. package/components/ui/form-builder/FormBuilderSelect.vue +42 -0
  183. package/components/ui/form-builder/FormDisplayer.vue +39 -0
  184. package/components/ui/form-builder/components/AdminElementEditor.vue +48 -0
  185. package/components/ui/form-builder/components/AdminToolbar.vue +60 -0
  186. package/components/ui/form-builder/components/AdminToolbarView.vue +19 -0
  187. package/components/ui/form-builder/components/CustomDashboard.vue +312 -0
  188. package/components/ui/form-builder/components/FormBuilderWrapper.vue +333 -0
  189. package/components/ui/form-builder/components/admin/Transformer.vue +25 -0
  190. package/components/ui/form-builder/components/admin/element-setting-views/Button.vue +30 -0
  191. package/components/ui/form-builder/components/admin/element-setting-views/Checkbox.vue +23 -0
  192. package/components/ui/form-builder/components/admin/element-setting-views/Label.vue +13 -0
  193. package/components/ui/form-builder/components/admin/element-setting-views/Radios.vue +85 -0
  194. package/components/ui/form-builder/components/admin/element-setting-views/Scanner.vue +15 -0
  195. package/components/ui/form-builder/components/admin/element-setting-views/Select.vue +75 -0
  196. package/components/ui/form-builder/components/admin/element-setting-views/Text.vue +13 -0
  197. package/components/ui/form-builder/components/admin/element-setting-views/Textarea.vue +20 -0
  198. package/components/ui/form-builder/components/admin/element-setting-views/Textfield.vue +20 -0
  199. package/components/ui/form-builder/components/admin/element-setting-views/index.ts +21 -0
  200. package/components/ui/form-builder/components/admin/setting-views/ApiActionsView.vue +111 -0
  201. package/components/ui/form-builder/components/admin/setting-views/NewElementView.vue +200 -0
  202. package/components/ui/form-builder/components/admin/setting-views/VariablesView.vue +19 -0
  203. package/components/ui/form-builder/components/admin/setting-views/new-element-view/DraggableElement.vue +107 -0
  204. package/components/ui/form-builder/components/admin/setting-views/variables-view/ApiActionVariables.vue +27 -0
  205. package/components/ui/form-builder/components/admin/setting-views/variables-view/RouteQueryParams.vue +47 -0
  206. package/components/ui/form-builder/components/admin/setting-views/variables-view/Transformers.vue +61 -0
  207. package/components/ui/form-builder/components/admin/setting-views/variables-view/Variables.vue +74 -0
  208. package/components/ui/form-builder/components/elements/button/Button.vue +60 -0
  209. package/components/ui/form-builder/components/elements/button/index.ts +1 -0
  210. package/components/ui/form-builder/components/elements/checkbox/Checkbox.vue +40 -0
  211. package/components/ui/form-builder/components/elements/checkbox/index.ts +1 -0
  212. package/components/ui/form-builder/components/elements/file/File.vue +6 -0
  213. package/components/ui/form-builder/components/elements/file/index.ts +1 -0
  214. package/components/ui/form-builder/components/elements/images/Images.vue +28 -0
  215. package/components/ui/form-builder/components/elements/images/index.ts +1 -0
  216. package/components/ui/form-builder/components/elements/index.ts +31 -0
  217. package/components/ui/form-builder/components/elements/items/Items.vue +6 -0
  218. package/components/ui/form-builder/components/elements/items/index.ts +1 -0
  219. package/components/ui/form-builder/components/elements/label/Label.vue +20 -0
  220. package/components/ui/form-builder/components/elements/label/index.ts +1 -0
  221. package/components/ui/form-builder/components/elements/location/Location.vue +6 -0
  222. package/components/ui/form-builder/components/elements/location/index.ts +1 -0
  223. package/components/ui/form-builder/components/elements/radios/Radios.vue +42 -0
  224. package/components/ui/form-builder/components/elements/radios/index.ts +1 -0
  225. package/components/ui/form-builder/components/elements/richtext/Richtext.vue +6 -0
  226. package/components/ui/form-builder/components/elements/richtext/index.ts +1 -0
  227. package/components/ui/form-builder/components/elements/scanner/Scanner.vue +173 -0
  228. package/components/ui/form-builder/components/elements/scanner/index.ts +1 -0
  229. package/components/ui/form-builder/components/elements/select/Select.vue +49 -0
  230. package/components/ui/form-builder/components/elements/select/index.ts +1 -0
  231. package/components/ui/form-builder/components/elements/text/Text.vue +20 -0
  232. package/components/ui/form-builder/components/elements/text/index.ts +1 -0
  233. package/components/ui/form-builder/components/elements/textarea/Textarea.vue +30 -0
  234. package/components/ui/form-builder/components/elements/textarea/index.ts +1 -0
  235. package/components/ui/form-builder/components/elements/textfield/Textfield.vue +30 -0
  236. package/components/ui/form-builder/components/elements/textfield/index.ts +1 -0
  237. package/components/ui/form-builder/index.ts +3 -0
  238. package/components/ui/input/Input.vue +33 -0
  239. package/components/ui/input/index.ts +1 -0
  240. package/components/ui/integration-action-builder/IntegrationActionBuilder.vue +193 -0
  241. package/components/ui/integration-action-builder/InterpolationField.vue +29 -0
  242. package/components/ui/integration-action-builder/index.ts +1 -0
  243. package/components/ui/integration-action-builder/integrations/firebase/AddDocument.vue +43 -0
  244. package/components/ui/integration-action-builder/integrations/firebase/DeleteDocument.vue +6 -0
  245. package/components/ui/integration-action-builder/integrations/firebase/GetCollections.vue +6 -0
  246. package/components/ui/integration-action-builder/integrations/firebase/GetDocumentById.vue +6 -0
  247. package/components/ui/integration-action-builder/integrations/firebase/QueryCollectionGroup.vue +6 -0
  248. package/components/ui/integration-action-builder/integrations/firebase/QueryFirestore.vue +165 -0
  249. package/components/ui/integration-action-builder/integrations/firebase/UpdateDocument.vue +6 -0
  250. package/components/ui/integration-action-builder/integrations/firebase/index.ts +204 -0
  251. package/components/ui/integration-action-builder/integrations/index.ts +9 -0
  252. package/components/ui/integration-action-builder/integrations/openai/AddDocument.vue +43 -0
  253. package/components/ui/integration-action-builder/integrations/openai/GenerateText.vue +40 -0
  254. package/components/ui/integration-action-builder/integrations/openai/components/ModelSelector.vue +75 -0
  255. package/components/ui/integration-action-builder/integrations/openai/index.ts +116 -0
  256. package/components/ui/integration-action-builder/integrations/twilio/MakeCall.vue +40 -0
  257. package/components/ui/integration-action-builder/integrations/twilio/SendSMS.vue +39 -0
  258. package/components/ui/integration-action-builder/integrations/twilio/components/ModelSelector.vue +75 -0
  259. package/components/ui/integration-action-builder/integrations/twilio/index.ts +68 -0
  260. package/components/ui/integration-icon/IntegrationIcon.vue +18 -0
  261. package/components/ui/integration-icon/index.ts +1 -0
  262. package/components/ui/label/Label.vue +28 -0
  263. package/components/ui/label/index.ts +1 -0
  264. package/components/ui/lucide-icon-selector/LucideIconPicker.vue +34 -0
  265. package/components/ui/lucide-icon-selector/LucideIconSelector.vue +55 -0
  266. package/components/ui/lucide-icon-selector/index.ts +2 -0
  267. package/components/ui/media-picker/MediaPicker.vue +40 -0
  268. package/components/ui/media-picker/MediaPickerDialog.vue +306 -0
  269. package/components/ui/media-picker/MediaPickerItems.vue +342 -0
  270. package/components/ui/media-picker/index.ts +2 -0
  271. package/components/ui/media-picker/media-picker-dialog/Dropzone.vue +104 -0
  272. package/components/ui/media-picker/media-picker-dialog/FileItem.vue +108 -0
  273. package/components/ui/media-picker/media-picker-dialog/Filters.vue +78 -0
  274. package/components/ui/menubar/Menubar.vue +36 -0
  275. package/components/ui/menubar/MenubarCheckboxItem.vue +41 -0
  276. package/components/ui/menubar/MenubarContent.vue +44 -0
  277. package/components/ui/menubar/MenubarGroup.vue +14 -0
  278. package/components/ui/menubar/MenubarItem.vue +37 -0
  279. package/components/ui/menubar/MenubarLabel.vue +19 -0
  280. package/components/ui/menubar/MenubarMenu.vue +14 -0
  281. package/components/ui/menubar/MenubarRadioGroup.vue +22 -0
  282. package/components/ui/menubar/MenubarRadioItem.vue +41 -0
  283. package/components/ui/menubar/MenubarSeparator.vue +23 -0
  284. package/components/ui/menubar/MenubarShortcut.vue +17 -0
  285. package/components/ui/menubar/MenubarSub.vue +22 -0
  286. package/components/ui/menubar/MenubarSubContent.vue +39 -0
  287. package/components/ui/menubar/MenubarSubTrigger.vue +27 -0
  288. package/components/ui/menubar/MenubarTrigger.vue +30 -0
  289. package/components/ui/menubar/index.ts +15 -0
  290. package/components/ui/number-field/NumberField.vue +23 -0
  291. package/components/ui/number-field/NumberFieldContent.vue +14 -0
  292. package/components/ui/number-field/NumberFieldDecrement.vue +25 -0
  293. package/components/ui/number-field/NumberFieldIncrement.vue +25 -0
  294. package/components/ui/number-field/NumberFieldInput.vue +16 -0
  295. package/components/ui/number-field/index.ts +5 -0
  296. package/components/ui/pagination/PaginationEllipsis.vue +22 -0
  297. package/components/ui/pagination/PaginationFirst.vue +29 -0
  298. package/components/ui/pagination/PaginationLast.vue +29 -0
  299. package/components/ui/pagination/PaginationNext.vue +29 -0
  300. package/components/ui/pagination/PaginationPrev.vue +29 -0
  301. package/components/ui/pagination/index.ts +10 -0
  302. package/components/ui/popover/Popover.vue +18 -0
  303. package/components/ui/popover/PopoverAnchor.vue +15 -0
  304. package/components/ui/popover/PopoverContent.vue +49 -0
  305. package/components/ui/popover/PopoverTrigger.vue +14 -0
  306. package/components/ui/popover/index.ts +4 -0
  307. package/components/ui/query-builder-dialog/QueryBuilderDialog.vue +389 -0
  308. package/components/ui/query-builder-dialog/index.ts +1 -0
  309. package/components/ui/radio-group/RadioGroup.vue +30 -0
  310. package/components/ui/radio-group/RadioGroupItem.vue +43 -0
  311. package/components/ui/radio-group/index.ts +2 -0
  312. package/components/ui/range-calendar/RangeCalendar.vue +57 -0
  313. package/components/ui/range-calendar/RangeCalendarCell.vue +21 -0
  314. package/components/ui/range-calendar/RangeCalendarCellTrigger.vue +37 -0
  315. package/components/ui/range-calendar/RangeCalendarGrid.vue +21 -0
  316. package/components/ui/range-calendar/RangeCalendarGridBody.vue +11 -0
  317. package/components/ui/range-calendar/RangeCalendarGridHead.vue +11 -0
  318. package/components/ui/range-calendar/RangeCalendarGridRow.vue +18 -0
  319. package/components/ui/range-calendar/RangeCalendarHeadCell.vue +18 -0
  320. package/components/ui/range-calendar/RangeCalendarHeader.vue +18 -0
  321. package/components/ui/range-calendar/RangeCalendarHeading.vue +28 -0
  322. package/components/ui/range-calendar/RangeCalendarNextButton.vue +29 -0
  323. package/components/ui/range-calendar/RangeCalendarPrevButton.vue +29 -0
  324. package/components/ui/range-calendar/index.ts +12 -0
  325. package/components/ui/resizable/ResizableHandle.vue +27 -0
  326. package/components/ui/resizable/ResizablePanel.vue +18 -0
  327. package/components/ui/resizable/ResizablePanelGroup.vue +25 -0
  328. package/components/ui/resizable/index.ts +3 -0
  329. package/components/ui/ripple-button/RippleButton.vue +97 -0
  330. package/components/ui/ripple-button/index.ts +1 -0
  331. package/components/ui/sandbox/Sandbox.vue +35 -0
  332. package/components/ui/sandbox/index.ts +1 -0
  333. package/components/ui/scroll-area/ScrollArea.vue +36 -0
  334. package/components/ui/scroll-area/ScrollBar.vue +34 -0
  335. package/components/ui/scroll-area/index.ts +2 -0
  336. package/components/ui/segmented-control/SegmentedControl.vue +121 -0
  337. package/components/ui/segmented-control/SegmentedControlButton.vue +64 -0
  338. package/components/ui/segmented-control/index.ts +2 -0
  339. package/components/ui/select/Select.vue +18 -0
  340. package/components/ui/select/SelectContent.vue +55 -0
  341. package/components/ui/select/SelectGroup.vue +14 -0
  342. package/components/ui/select/SelectItem.vue +45 -0
  343. package/components/ui/select/SelectItemText.vue +14 -0
  344. package/components/ui/select/SelectLabel.vue +16 -0
  345. package/components/ui/select/SelectScrollDownButton.vue +28 -0
  346. package/components/ui/select/SelectScrollUpButton.vue +28 -0
  347. package/components/ui/select/SelectSeparator.vue +21 -0
  348. package/components/ui/select/SelectTrigger.vue +32 -0
  349. package/components/ui/select/SelectValue.vue +15 -0
  350. package/components/ui/select/index.ts +11 -0
  351. package/components/ui/separator/Separator.vue +28 -0
  352. package/components/ui/separator/index.ts +1 -0
  353. package/components/ui/sheet/Sheet.vue +17 -0
  354. package/components/ui/sheet/SheetClose.vue +14 -0
  355. package/components/ui/sheet/SheetContent.vue +65 -0
  356. package/components/ui/sheet/SheetDescription.vue +20 -0
  357. package/components/ui/sheet/SheetFooter.vue +16 -0
  358. package/components/ui/sheet/SheetHeader.vue +15 -0
  359. package/components/ui/sheet/SheetOverlay.vue +24 -0
  360. package/components/ui/sheet/SheetTitle.vue +20 -0
  361. package/components/ui/sheet/SheetTrigger.vue +14 -0
  362. package/components/ui/sheet/index.ts +8 -0
  363. package/components/ui/sidebar/Sidebar.vue +96 -0
  364. package/components/ui/sidebar/SidebarContent.vue +18 -0
  365. package/components/ui/sidebar/SidebarFooter.vue +18 -0
  366. package/components/ui/sidebar/SidebarGroup.vue +18 -0
  367. package/components/ui/sidebar/SidebarGroupAction.vue +27 -0
  368. package/components/ui/sidebar/SidebarGroupContent.vue +18 -0
  369. package/components/ui/sidebar/SidebarGroupLabel.vue +25 -0
  370. package/components/ui/sidebar/SidebarHeader.vue +18 -0
  371. package/components/ui/sidebar/SidebarInput.vue +22 -0
  372. package/components/ui/sidebar/SidebarInset.vue +21 -0
  373. package/components/ui/sidebar/SidebarMenu.vue +18 -0
  374. package/components/ui/sidebar/SidebarMenuAction.vue +34 -0
  375. package/components/ui/sidebar/SidebarMenuBadge.vue +26 -0
  376. package/components/ui/sidebar/SidebarMenuButton.vue +49 -0
  377. package/components/ui/sidebar/SidebarMenuButtonChild.vue +34 -0
  378. package/components/ui/sidebar/SidebarMenuItem.vue +18 -0
  379. package/components/ui/sidebar/SidebarMenuSkeleton.vue +34 -0
  380. package/components/ui/sidebar/SidebarMenuSub.vue +22 -0
  381. package/components/ui/sidebar/SidebarMenuSubButton.vue +36 -0
  382. package/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
  383. package/components/ui/sidebar/SidebarProvider.vue +81 -0
  384. package/components/ui/sidebar/SidebarRail.vue +33 -0
  385. package/components/ui/sidebar/SidebarSeparator.vue +19 -0
  386. package/components/ui/sidebar/SidebarTrigger.vue +27 -0
  387. package/components/ui/sidebar/index.ts +60 -0
  388. package/components/ui/sidebar/utils.ts +19 -0
  389. package/components/ui/skeleton/Skeleton.vue +17 -0
  390. package/components/ui/skeleton/index.ts +1 -0
  391. package/components/ui/sonner/Sonner.vue +18 -0
  392. package/components/ui/sonner/index.ts +1 -0
  393. package/components/ui/spring-calendar/SpringCalendar.vue +110 -0
  394. package/components/ui/spring-calendar/TextMorph.vue +160 -0
  395. package/components/ui/spring-calendar/index.ts +2 -0
  396. package/components/ui/switch/Switch.vue +41 -0
  397. package/components/ui/switch/index.ts +1 -0
  398. package/components/ui/table/Table.vue +16 -0
  399. package/components/ui/table/TableBody.vue +17 -0
  400. package/components/ui/table/TableCaption.vue +17 -0
  401. package/components/ui/table/TableCell.vue +22 -0
  402. package/components/ui/table/TableEmpty.vue +37 -0
  403. package/components/ui/table/TableFooter.vue +17 -0
  404. package/components/ui/table/TableHead.vue +17 -0
  405. package/components/ui/table/TableHeader.vue +17 -0
  406. package/components/ui/table/TableRow.vue +17 -0
  407. package/components/ui/table/index.ts +9 -0
  408. package/components/ui/table/utils.ts +9 -0
  409. package/components/ui/tabs/Tabs.vue +15 -0
  410. package/components/ui/tabs/TabsContent.vue +22 -0
  411. package/components/ui/tabs/TabsList.vue +25 -0
  412. package/components/ui/tabs/TabsTrigger.vue +29 -0
  413. package/components/ui/tabs/index.ts +4 -0
  414. package/components/ui/tailwind-color-picker/TailwindColorPicker.vue +58 -0
  415. package/components/ui/tailwind-color-picker/index.ts +1 -0
  416. package/components/ui/textarea/Textarea.vue +28 -0
  417. package/components/ui/textarea/index.ts +1 -0
  418. package/components/ui/toggle/Toggle.vue +32 -0
  419. package/components/ui/toggle/index.ts +27 -0
  420. package/components/ui/tooltip/Tooltip.vue +17 -0
  421. package/components/ui/tooltip/TooltipContent.vue +33 -0
  422. package/components/ui/tooltip/TooltipProvider.vue +13 -0
  423. package/components/ui/tooltip/TooltipTrigger.vue +14 -0
  424. package/components/ui/tooltip/index.ts +4 -0
  425. package/composables/useCms.ts +11 -0
  426. package/index.ts +10 -0
  427. package/lib/interpolation.ts +77 -0
  428. package/lib/utils.ts +24 -0
  429. package/package.json +52 -0
  430. package/styles/components/code-editor.css +52 -0
  431. package/styles/components/flow-builder.css +159 -0
  432. package/styles/index.css +2 -0
  433. package/tsconfig.json +19 -0
  434. package/types/index.ts +41 -0
  435. package/vite.config.ts +25 -0
@@ -0,0 +1,306 @@
1
+ <script setup lang="ts">
2
+ import { inject, ref } from 'vue'
3
+ import { Plus, Folder, ChevronLeft, Images } from 'lucide-vue-next'
4
+ import Dropzone from './media-picker-dialog/Dropzone.vue'
5
+ import Filters from './media-picker-dialog/Filters.vue'
6
+ import FileItem from './media-picker-dialog/FileItem.vue'
7
+ import { formatFileSize } from '../../../lib/utils'
8
+ import { cn } from '../../../lib/utils'
9
+
10
+ interface PENDING_FILE {
11
+ id: string
12
+ name: string
13
+ size: number
14
+ file: File,
15
+ contentType: string,
16
+ path: string,
17
+ url: string,
18
+ status: 'pending' | 'uploading' | 'error'
19
+ updated: Date
20
+ }
21
+
22
+ const route = useRoute()
23
+
24
+ const emit = defineEmits(['submit'])
25
+
26
+ const props = defineProps({
27
+ sources: {
28
+ type: Array,
29
+ required: true
30
+ },
31
+ maxFiles: {
32
+ type: Number
33
+ }
34
+ })
35
+
36
+ const dialogOpen = ref(false)
37
+
38
+ const project = ref(route.params.slug as string)
39
+
40
+ const { getMediaSource } = useMediaLibrary()
41
+ const activeSourceId = ref('')
42
+ const activeSource = ref({});
43
+
44
+ const folders = ref<any[]>([])
45
+ const files = ref<any[]>([])
46
+ const fileCount = ref(0)
47
+ const currentPath = ref('')
48
+ const navigationHistory = ref<string[]>([])
49
+ const effectiveBasePath = ref('')
50
+
51
+ const search = ref('')
52
+ const sort: 'name' | 'name-desc' | 'size' | 'size-desc' | 'date' | 'date-desc' = ref('name-asc')
53
+ const selectedFiles = ref<string[]>([])
54
+ provide('search', search)
55
+ provide('sort', sort)
56
+
57
+ const PENDING_FILES = ref<PENDING_FILE[]>([])
58
+ provide('pendingFiles', PENDING_FILES)
59
+
60
+ const filteredFolders = computed(() => {
61
+ let filtered = [...folders.value]
62
+ if (search.value) {
63
+ filtered = filtered.filter(folder => folder.name.toLowerCase().includes(search.value.toLowerCase()))
64
+ }
65
+ return filtered.sort((a, b) => {
66
+ if (sort.value === 'name') return a.name.localeCompare(b.name)
67
+ if (sort.value === 'name-desc') return b.name.localeCompare(a.name)
68
+ return 0
69
+ })
70
+ })
71
+
72
+ const filteredFiles = computed(() => {
73
+ function formatPath(path: string) {
74
+ if (path[0] === '/') path = path.substring(1)
75
+ if (path[path.length - 1] === '/') path = path.substring(0, path.length - 1)
76
+ return path
77
+ }
78
+
79
+ const pendingFiles = PENDING_FILES.value.filter(file => formatPath(file.path) === formatPath(currentPath.value))
80
+
81
+ let filtered = [...files.value]
82
+ if (search.value) {
83
+ filtered = filtered.filter(file => file.name.toLowerCase().includes(search.value.toLowerCase()))
84
+ }
85
+
86
+ return [...filtered, ...pendingFiles].sort((a, b) => {
87
+ if (sort.value === 'name') return a.name.localeCompare(b.name)
88
+ if (sort.value === 'name-desc') return b.name.localeCompare(a.name)
89
+ if (sort.value === 'size') return a.size - b.size
90
+ if (sort.value === 'size-desc') return b.size - a.size
91
+ if (sort.value === 'date') return new Date(a.updated).getTime() - new Date(b.updated).getTime()
92
+ if (sort.value === 'date-desc') return new Date(b.updated).getTime() - new Date(a.updated).getTime()
93
+ return 0
94
+ })
95
+ })
96
+
97
+ watch(() => activeSourceId.value, async () => {
98
+ activeSource.value = await getMediaSource(project.value, activeSourceId.value)
99
+ setEffectiveBasePath()
100
+ currentPath.value = activeSource.value.path
101
+ navigationHistory.value = []
102
+ search.value = ''
103
+ loadContent(activeSource.value.path)
104
+ })
105
+
106
+ const addImage = (file) => {
107
+ emit('select-image', {
108
+ name: file.name.split('/').pop(),
109
+ url: file.url,
110
+ size: file.size,
111
+ contentType: file.contentType,
112
+ path: file.name
113
+ })
114
+ }
115
+
116
+ const loading = ref(false)
117
+ const error = ref('')
118
+
119
+ const reload = () => {
120
+ loadContent(currentPath.value, true)
121
+ }
122
+ provide('reload', reload)
123
+
124
+ const loadContent = async (path?: string, softReload = false) => {
125
+ error.value = ''
126
+ if (!softReload) {
127
+ loading.value = true
128
+ }
129
+
130
+ if (path?.[0] === '/') {
131
+ path = path.substring(1)
132
+ }
133
+
134
+ try {
135
+ const { getStorageContent } = useMediaLibrary()
136
+
137
+ if (effectiveBasePath.value && path !== undefined) {
138
+ const normalizedPath = path === '' ? '' : (path.endsWith('/') ? path : `${path}/`)
139
+ const normalizedBasePath = effectiveBasePath.value === '' ? '' :
140
+ (effectiveBasePath.value.endsWith('/') ? effectiveBasePath.value : `${effectiveBasePath.value}/`)
141
+
142
+ if (path !== '' && !normalizedPath.startsWith(normalizedBasePath)) {
143
+ currentPath.value = effectiveBasePath.value
144
+ } else {
145
+ currentPath.value = path
146
+ }
147
+ } else if (path !== undefined) {
148
+ currentPath.value = path
149
+ }
150
+
151
+ const res = await getStorageContent(activeSource.value.id, currentPath.value);
152
+
153
+ folders.value = (res.folders || []).filter(folder => {
154
+ const folderPath = folder.path
155
+ const parentPath = currentPath.value ? currentPath.value : ''
156
+
157
+ const relativePath = folderPath.startsWith(parentPath) ? folderPath.substring(parentPath.length) : folderPath
158
+ const pathParts = relativePath.split('/').filter(Boolean)
159
+
160
+ return pathParts.length === 1
161
+ })
162
+
163
+ files.value = (res.files || []).filter(file => {
164
+ const filePath = file.name
165
+ const parentPath = currentPath.value ? currentPath.value : ''
166
+
167
+ if (!filePath.startsWith(parentPath)) return false
168
+
169
+ const relativePath = filePath.substring(parentPath.length)
170
+ const pathParts = relativePath.split('/').filter(Boolean)
171
+
172
+ return pathParts.length === 1
173
+ })
174
+
175
+ fileCount.value = files.value.length
176
+ } catch (err) {
177
+ error.value = 'Failed to load files. Please try again.'
178
+ folders.value = []
179
+ files.value = []
180
+ fileCount.value = 0
181
+ } finally {
182
+ loading.value = false
183
+ }
184
+ }
185
+
186
+ const navigateToFolder = (folderPath: string) => {
187
+ search.value = ''
188
+ navigationHistory.value.push(currentPath.value)
189
+ loadContent(folderPath)
190
+ }
191
+
192
+ const navigateBack = () => {
193
+ search.value = ''
194
+ if (navigationHistory.value.length > 0) {
195
+ const previousPath = navigationHistory.value.pop()
196
+
197
+ if (previousPath && (previousPath.startsWith(effectiveBasePath.value) || previousPath === '')) {
198
+ loadContent(previousPath)
199
+ } else {
200
+ navigationHistory.value = []
201
+ loadContent(effectiveBasePath.value)
202
+ }
203
+ }
204
+ }
205
+
206
+ const toggleFile = (file: any) => {
207
+ if (selectedFiles.value.includes(file.url)) {
208
+ selectedFiles.value = selectedFiles.value.filter(url => url !== file.url)
209
+ } else {
210
+ if (props.maxFiles !== undefined && selectedFiles.value.length >= props.maxFiles && props.maxFiles === 1) {
211
+ selectedFiles.value = [file.url]
212
+ } else {
213
+ selectedFiles.value.push(file.url)
214
+ }
215
+ }
216
+ }
217
+
218
+ const onSubmit = () => {
219
+ emit('submit', props.maxFiles === 1 ? selectedFiles.value[0] : selectedFiles.value)
220
+ dialogOpen.value = false
221
+ selectedFiles.value = []
222
+ search.value = ''
223
+ }
224
+
225
+ const setEffectiveBasePath = () => {
226
+ effectiveBasePath.value = props.sources?.[0]?.path || ''
227
+ if (effectiveBasePath.value[0] === '/') {
228
+ effectiveBasePath.value = effectiveBasePath.value.substring(1)
229
+ }
230
+ }
231
+
232
+ onMounted(() => {
233
+ activeSourceId.value = props.sources?.[0] || ''
234
+ setEffectiveBasePath()
235
+ })
236
+ </script>
237
+
238
+ <template>
239
+ <Dialog v-model:open="dialogOpen">
240
+ <DialogTrigger asChild>
241
+ <slot />
242
+ </DialogTrigger>
243
+ <DialogScrollContent class="w-[90%] md:w-[90%] sm:max-w-[1500px]">
244
+ <DialogHeader class="sticky -top-6 pt-10 pb-2 -mt-6 z-10 bg-background">
245
+ <div class="flex items-center gap-2">
246
+ <Button
247
+ v-if="navigationHistory?.length > 0"
248
+ variant="outline"
249
+ size="icon"
250
+ @click="navigateBack"
251
+ >
252
+ <ChevronLeft class="size-4" />
253
+ </Button>
254
+ <DialogTitle class="flex items-center gap-2">
255
+ <MediaSourceSelect :sources="sources" v-model="activeSourceId" />
256
+ </DialogTitle>
257
+ </div>
258
+ <DialogDescription>
259
+ {{ currentPath !== effectiveBasePath.value ? (currentPath === '' ? 'Root' : "/" + currentPath) : '' }}
260
+ </DialogDescription>
261
+ </DialogHeader>
262
+
263
+ <div>
264
+ <div v-if="loading" class="py-8 text-center">
265
+ <div class="inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]"></div>
266
+ <p class="mt-2 text-gray-500">Loading files...</p>
267
+ </div>
268
+
269
+ <div v-else-if="error" class="p-4 text-center">
270
+ <div class="text-red-500 mb-2">{{ error }}</div>
271
+ <Button @click="loadContent(currentPath)" variant="outline">Retry</Button>
272
+ </div>
273
+
274
+ <div v-else>
275
+ <Dropzone :activeSourceId="activeSourceId" :path="currentPath" />
276
+
277
+ <template v-if="files.length > 0 || folders.length > 0">
278
+ <Filters :showNewFolder="activeSource.allowUserFolders" />
279
+
280
+ <div class="grid grid-cols-2 md:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 gap-4 overflow-y-auto">
281
+ <div
282
+ v-for="folder in filteredFolders"
283
+ :key="folder.path"
284
+ @click="navigateToFolder(folder.path)"
285
+ :class="cn('cursor-pointer border rounded-md p-2 hover:bg-muted dark:hover:bg-muted/30 transition-colors flex items-center gap-2',
286
+ selectedFiles
287
+ )"
288
+ >
289
+ <Folder class="size-5 text-blue-600" />
290
+ <span class="truncate">{{ folder.name }}</span>
291
+ </div>
292
+
293
+ <FileItem :file="file" v-for="file in filteredFiles" :key="file.url" :selected="selectedFiles.includes(file.url)" @toggle="toggleFile(file)" />
294
+ </div>
295
+ </template>
296
+ </div>
297
+ </div>
298
+
299
+ <DialogFooter class="sticky -bottom-6 pb-10 pt-2 -mb-6 z-10 bg-background">
300
+ <Button @click="onSubmit" :disabled="selectedFiles?.length === 0">
301
+ {{selectedFiles?.length > 0 ? `Add ${selectedFiles?.length} ${selectedFiles?.length === 1 ? 'file' : 'files'}` : '0 files selected' }}
302
+ </Button>
303
+ </DialogFooter>
304
+ </DialogScrollContent>
305
+ </Dialog>
306
+ </template>
@@ -0,0 +1,342 @@
1
+ <script setup lang="ts">
2
+ import { inject, computed, ref, onMounted, watch, nextTick } from 'vue'
3
+ import Button from '../button/Button.vue'
4
+ import MediaPickerDialog from './MediaPickerDialog.vue'
5
+ import { Minus, Check, GripVertical, Plus, Upload } from 'lucide-vue-next'
6
+ import gsap from 'gsap'
7
+ import { Draggable } from 'gsap/Draggable'
8
+
9
+ // Register the Draggable plugin
10
+ gsap.registerPlugin(Draggable)
11
+
12
+ interface MediaItem {
13
+ id: string
14
+ url: string
15
+ name?: string
16
+ [key: string]: any
17
+ }
18
+
19
+ type MediaItemType = string | MediaItem
20
+
21
+ const sources = inject('sources')
22
+
23
+ // Get the media items from the provider with proper typing
24
+ const items = inject<{ value: MediaItemType[] }>('media-items', { value: [] })
25
+ const updateItems = inject<(newItems: MediaItemType[]) => void>('update-media-items', () => {})
26
+
27
+ const selectedItems = ref([])
28
+ const itemsContainer = ref(null)
29
+ const originalItemsArray = ref(null) // Store the original array during dragging
30
+ const draggingItemIndex = ref(-1) // Track which item is being dragged
31
+ const lastHoveredIndex = ref(-1) // Track the last hovered grid position
32
+
33
+ const showAll = ref(false)
34
+
35
+ const fileSelectorCheckboxState = computed(() => {
36
+ if (selectedItems.value.length === items.value.length) return true
37
+ if (selectedItems.value.length === 0) return false
38
+ return 'indeterminate'
39
+ })
40
+
41
+ const updateChecked = (event, id: string) => {
42
+ // Stop propagation to prevent interfering with drag
43
+ event.stopPropagation()
44
+
45
+ if (selectedItems.value.includes(id)) {
46
+ selectedItems.value = selectedItems.value.filter((item) => item !== id)
47
+ return
48
+ }
49
+ selectedItems.value.push(id)
50
+ }
51
+
52
+ const onFileSelectorCheckboxClick = () => {
53
+ if (fileSelectorCheckboxState.value === true) {
54
+ selectedItems.value = []
55
+ return
56
+ }
57
+ selectedItems.value = items.value.map((item) => item.id)
58
+ }
59
+
60
+ const onRemoveSelectedClick = () => {
61
+ if (!selectedItems.value.length) return
62
+
63
+ const currentArray = [...items.value]
64
+ selectedItems.value.forEach((id) => {
65
+ const index = currentArray.findIndex((item) => item.id === id)
66
+ if (index !== -1) {
67
+ currentArray.splice(index, 1)
68
+ }
69
+ })
70
+ updateItems(currentArray)
71
+ selectedItems.value = []
72
+ }
73
+
74
+ const getDraggableElements = () => {
75
+ if (!itemsContainer.value) return []
76
+ return itemsContainer.value.querySelectorAll('.draggable-item')
77
+ }
78
+
79
+ let draggableInstances = []
80
+ const firstItemPosition = ref({ row: 0, col: 0 }) // Track the position of the first (2x2) item
81
+
82
+
83
+ let rafId = null
84
+ let lastDragEvent = null
85
+ let currentDragTarget = null
86
+
87
+ const onDragHandler = () => {
88
+ if (!lastDragEvent || !currentDragTarget) return
89
+
90
+ const e = lastDragEvent
91
+ const mouseX = e.clientX
92
+ const mouseY = e.clientY
93
+
94
+ // Find the background grid container
95
+ const backgroundGrid = document.querySelector('.background-grid');
96
+ if (!backgroundGrid) return;
97
+
98
+ // Get the grid's position
99
+ const gridRect = backgroundGrid.getBoundingClientRect();
100
+
101
+ // Check if the mouse is inside the grid
102
+ if (mouseX >= gridRect.left && mouseX <= gridRect.right &&
103
+ mouseY >= gridRect.top && mouseY <= gridRect.bottom) {
104
+
105
+ // Get all grid items within the background grid
106
+ const gridItems = backgroundGrid.querySelectorAll('[data-index]');
107
+
108
+ // Find which grid item the mouse is over
109
+ for (let i = 0; i < gridItems.length; i++) {
110
+ const item = gridItems[i];
111
+ const itemRect = item.getBoundingClientRect();
112
+
113
+ if (mouseX >= itemRect.left && mouseX <= itemRect.right &&
114
+ mouseY >= itemRect.top && mouseY <= itemRect.bottom) {
115
+
116
+ // Get info about the hovered grid item
117
+ const dataIndex = parseInt(item.getAttribute('data-index'), 10);
118
+ const isLarge = item.classList.contains('col-span-2');
119
+
120
+ // Only update when hovering over a new grid position
121
+ if (dataIndex !== lastHoveredIndex.value && originalItemsArray.value) {
122
+ lastHoveredIndex.value = dataIndex;
123
+
124
+ let { x: x1, y: y1 } = document.querySelector('.background-grid [data-index="' + draggingItemIndex.value + '"]').getBoundingClientRect()
125
+ let { x: x2, y: y2 } = document.querySelector('.background-grid [data-index="' + dataIndex + '"]').getBoundingClientRect()
126
+
127
+ currentDragTarget.style.top = y1 - y2 + 'px'
128
+ currentDragTarget.style.left = x1 - x2 + 'px'
129
+
130
+ // Create a new array based on the original order
131
+ const newOrder = [...originalItemsArray.value];
132
+
133
+ // Get the item being dragged
134
+ const draggedItem = newOrder[draggingItemIndex.value];
135
+
136
+ // Remove the dragged item from its original position
137
+ newOrder.splice(draggingItemIndex.value, 1);
138
+
139
+ // Insert it at the new position
140
+ newOrder.splice(dataIndex, 0, draggedItem);
141
+
142
+ // Update the displayed order (this will trigger a reactive update)
143
+ updateItems(newOrder);
144
+ }
145
+
146
+ break; // No need to check other items once we found the match
147
+ }
148
+ }
149
+ }
150
+
151
+ rafId = requestAnimationFrame(onDragHandler)
152
+ }
153
+
154
+ const initDraggable = () => {
155
+ draggableInstances.forEach(instance => {
156
+ if (instance && instance.kill) {
157
+ instance.kill()
158
+ }
159
+ })
160
+ draggableInstances = []
161
+
162
+ nextTick(() => {
163
+ const elements = getDraggableElements()
164
+ if (!elements.length) return
165
+
166
+ try {
167
+ const instance = Draggable.create(elements, {
168
+ type: 'x,y',
169
+ edgeResistance: 0.65,
170
+ bounds: itemsContainer.value,
171
+ onPress: function() {
172
+ // Save the original array and dragging item index when starting drag
173
+ originalItemsArray.value = [...items.value]
174
+ draggingItemIndex.value = parseInt(this.target.getAttribute('data-index'), 10)
175
+ lastHoveredIndex.value = -1 // Reset last hovered
176
+ this.target.classList.add('dragging')
177
+ currentDragTarget = this.target
178
+ },
179
+ onRelease: function(e) {
180
+ this.target.classList.remove('dragging')
181
+
182
+ // Cancel any existing animation frame
183
+ if (rafId) {
184
+ cancelAnimationFrame(rafId)
185
+ rafId = null
186
+ }
187
+
188
+ // Create a GSAP animation for smoothly resetting the transform
189
+ gsap.to(this.target, {
190
+ top: 0,
191
+ left: 0,
192
+ x: 0,
193
+ y: 0,
194
+ duration: 0.3,
195
+ ease: 'power3.out',
196
+ clearProps: 'transform',
197
+ onComplete: () => {
198
+ originalItemsArray.value = null
199
+ draggingItemIndex.value = -1
200
+ lastHoveredIndex.value = -1
201
+ }
202
+ });
203
+ },
204
+ onDrag: function(e) {
205
+ lastDragEvent = e
206
+ if (!rafId) {
207
+ rafId = requestAnimationFrame(onDragHandler)
208
+ }
209
+ }
210
+ })
211
+
212
+ draggableInstances.push(instance)
213
+ } catch (err) {
214
+ console.error('Error initializing draggable:', err)
215
+ }
216
+ })
217
+ }
218
+
219
+ const openMedia = (e, id) => {
220
+ console.log('open media', id)
221
+ }
222
+
223
+ const addMedia = (urls: string[]) => {
224
+ items.value.push(...urls)
225
+ updateItems(items.value)
226
+ }
227
+
228
+ onMounted(() => {
229
+ setTimeout(() => {
230
+ initDraggable()
231
+ }, 500)
232
+ })
233
+
234
+ watch(() => items.value.length, () => {
235
+ setTimeout(() => {
236
+ initDraggable()
237
+ }, 100)
238
+ })
239
+ </script>
240
+
241
+ <template>
242
+ <div v-if="selectedItems.length" class="flex items-center justify-between gap-2">
243
+ <Button @click="onFileSelectorCheckboxClick" variant="ghost" size="sm">
244
+ <Checkbox :model-value="fileSelectorCheckboxState" tabindex="-1" class="pointer-events-none data-[state=checked]:bg-black data-[state=checked]:text-white data-[state=checked]:border-muted border-muted group-focus-visible:ring-0">
245
+ <Minus class="size-3" v-if="fileSelectorCheckboxState === 'indeterminate'" />
246
+ <Check class="size-3" v-else />
247
+ </Checkbox>
248
+ <p class="text-muted-foreground text-sm ml-1">{{ selectedItems.length }} files selected</p>
249
+ </Button>
250
+ <Button variant="outline" size="sm" class="text-red-500 hover:text-red-600" @click="onRemoveSelectedClick">Remove</Button>
251
+ </div>
252
+
253
+ <div class="relative">
254
+ <!-- Background grid -->
255
+ <div class="background-grid absolute w-full grid grid-cols-6 gap-2 top-4">
256
+ <div v-for="(item, index) in showAll ? items : items.slice(0, 8)" :key="item.id" class="relative -m-1 p-1" :data-index="index" :class="{
257
+ 'col-start-1 col-span-2 row-start-1 row-span-2': index === 0
258
+ }">
259
+ <div class="w-full h-full bg-muted/80 rounded-md aspect-square"></div>
260
+ <div @click="showAll = true" v-if="!showAll && items.length > 8 && index === 7" class="absolute top-0 left-0 w-[calc(100%-8px)] h-[calc(100%-8px)] bg-black/30 rounded-md aspect-square z-10 flex items-center justify-center backdrop-blur-sm m-1 cursor-pointer">
261
+ + {{ items.length - 7 }}
262
+ </div>
263
+ </div>
264
+ <div class="-m-1 p-1 col-start-1 col-span-2 row-start-1 row-span-2" data-index="0" v-if="items.length == 0">
265
+ <div class="w-full h-full bg-muted/80 rounded-md aspect-square"></div>
266
+ </div>
267
+ <div class="-m-1 p-1" v-else>
268
+ <div class="w-full h-full bg-muted/80 rounded-md aspect-square"></div>
269
+ </div>
270
+ </div>
271
+
272
+ <!-- Draggable items container -->
273
+ <div ref="itemsContainer" class="relative grid grid-cols-6 gap-2 mt-4">
274
+ <!-- Use TransitionGroup for animations during reordering -->
275
+ <TransitionGroup name="media-item" tag="div" class="contents">
276
+ <!-- Draggable items -->
277
+ <div
278
+ v-for="(url, index) in showAll ? items : items.slice(0, 8)"
279
+ :key="url || index"
280
+ :data-index="index"
281
+ class="draggable-item w-full relative"
282
+ :class="{ 'col-start-1 col-span-2 row-start-1 row-span-2': index === 0 }"
283
+ >
284
+ <button
285
+ class="relative flex justify-center items-center w-full h-full rounded-md outline outline-border group overflow-hidden focus-visible:outline-none focus-visible:ring-ring/80 focus-visible:ring-[3px] transition-all duration-200 cursor-grab active:cursor-grabbing"
286
+ @click="(event) => openMedia(event, url)"
287
+ type="button"
288
+ >
289
+ <div class="aspect-square group">
290
+ <img
291
+ :src="url"
292
+ draggable="false"
293
+ class="w-full h-full object-cover group-hover:opacity-75 transition-opacity duration-200 pointer-events-none"
294
+ />
295
+ </div>
296
+ <button @click="(event) => updateChecked(event, url)" class="absolute top-0 left-0 p-1 size-6 flex items-center justify-center" type="button">
297
+ <Checkbox
298
+ :model-value="selectedItems.includes(url)"
299
+ tabindex="-1"
300
+ class="bg-white outline-gray-300 data-[state=checked]:bg-black data-[state=checked]:text-white data-[state=checked]:border-black shadow-xs pointer-events-none"
301
+ />
302
+ </button>
303
+
304
+ <GripVertical class="size-3 absolute top-1 right-1 text-gray-400 hidden group-hover:block" />
305
+ </button>
306
+ </div>
307
+ </TransitionGroup>
308
+
309
+ <!-- New media button -->
310
+ <MediaPickerDialog :sources="sources" @submit="addMedia">
311
+ <button
312
+ class="relative flex justify-center items-center w-full group aspect-square rounded-md cursor-pointer focus-visible:outline-ring focus-visible:outline-[1px] focus-visible:ring-ring/50 focus-visible:ring-[3px] hover:bg-black/10 transition-colors duration-200"
313
+ :class="{ 'col-start-1 col-span-2 row-start-1 row-span-2': items?.length === 0 }"
314
+ type="button"
315
+ >
316
+ <Plus class="size-8" />
317
+ </button>
318
+ </MediaPickerDialog>
319
+ </div>
320
+ </div>
321
+ </template>
322
+
323
+ <style scoped>
324
+ .draggable-item {
325
+ touch-action: none; /* Prevents scrolling while dragging on touch devices */
326
+ position: relative;
327
+ cursor: grab;
328
+ transition: transform 0.3s ease-out; /* Add a base transition for non-GSAP movements */
329
+ }
330
+
331
+ .dragging {
332
+ cursor: grabbing !important;
333
+ z-index: 100 !important;
334
+ user-select: none;
335
+ transition: none; /* Disable transition during dragging */
336
+ }
337
+
338
+ .media-item-enter-active,
339
+ .media-item-leave-active {
340
+ transition: all 0.3s ease;
341
+ }
342
+ </style>
@@ -0,0 +1,2 @@
1
+ export { default as MediaPicker } from './MediaPicker.vue'
2
+ export { default as MediaPickerDialog } from './MediaPickerDialog.vue'