frappe-ui 0.1.261 → 0.1.262

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 (268) hide show
  1. package/frappe/Filter/utils.ts +5 -4
  2. package/frappe/drive/components/AppSwitcher.vue +47 -0
  3. package/frappe/drive/components/DriveLogo.vue +18 -0
  4. package/frappe/drive/components/InfoDialog.vue +207 -0
  5. package/frappe/drive/components/MoveDialog.vue +450 -0
  6. package/frappe/drive/components/RenameDialog.vue +76 -0
  7. package/frappe/drive/components/Select/Select.vue +136 -0
  8. package/frappe/drive/components/Select/types.ts +18 -0
  9. package/frappe/drive/components/ShareDialog.vue +397 -0
  10. package/frappe/drive/components/TagInput/TagInput.vue +113 -0
  11. package/frappe/drive/components/TagInput/types.ts +45 -0
  12. package/frappe/drive/components/TagInput/utils.ts +63 -0
  13. package/frappe/drive/components/TeamSelector.vue +50 -0
  14. package/frappe/drive/index.js +6 -0
  15. package/frappe/drive/js/icons.js +705 -0
  16. package/frappe/drive/js/resources.js +110 -0
  17. package/frappe/drive/js/utils.js +110 -0
  18. package/package.json +46 -31
  19. package/src/components/Alert/Alert.vue +12 -0
  20. package/src/components/Alert/stories/Controlled.vue +16 -0
  21. package/src/components/Alert/stories/Slots.vue +20 -0
  22. package/src/components/Alert/stories/Themes.vue +14 -0
  23. package/src/components/Alert/types.ts +26 -1
  24. package/src/components/Avatar/Avatar.vue +9 -0
  25. package/src/components/Avatar/stories/Shapes.vue +12 -0
  26. package/src/components/Avatar/stories/Sizes.vue +10 -0
  27. package/src/components/Avatar/types.ts +7 -0
  28. package/src/components/Badge/Badge.vue +14 -2
  29. package/src/components/Badge/stories/Themes.vue +10 -0
  30. package/src/components/Badge/stories/Variants.vue +10 -0
  31. package/src/components/Badge/types.ts +8 -0
  32. package/src/components/Breadcrumbs/Breadcrumbs.vue +26 -0
  33. package/src/components/Breadcrumbs/stories/Example.vue +13 -0
  34. package/src/components/Breadcrumbs/stories/Slots.vue +24 -0
  35. package/src/components/Breadcrumbs/types.ts +9 -1
  36. package/src/components/Button/Button.vue +14 -0
  37. package/src/components/Button/stories/Icons.vue +27 -0
  38. package/src/components/Button/stories/Sizes.vue +11 -0
  39. package/src/components/Button/stories/Themes.vue +10 -0
  40. package/src/components/Button/stories/Variants.vue +10 -0
  41. package/src/components/Button/types.ts +29 -1
  42. package/src/components/Calendar/Calendar.vue +0 -1
  43. package/src/components/Calendar/stories/CustomHeader.vue +151 -0
  44. package/src/components/Calendar/stories/Examples.vue +102 -0
  45. package/src/components/Charts/{Charts.story.vue → stories/Examples.vue} +37 -29
  46. package/src/components/Checkbox/stories/Examples.vue +8 -0
  47. package/src/components/Checkbox/types.ts +11 -0
  48. package/src/components/CircularProgressBar/CircularProgressBar.story.vue +62 -59
  49. package/src/components/CircularProgressBar/types.ts +17 -3
  50. package/src/components/Combobox/Combobox.vue +29 -9
  51. package/src/components/Combobox/stories/CustomRender.vue +26 -0
  52. package/src/components/Combobox/stories/Grouped.vue +40 -0
  53. package/src/components/Combobox/stories/OptionSlots.vue +42 -0
  54. package/src/components/Combobox/stories/Simple.vue +19 -0
  55. package/src/components/Combobox/stories/WithIcons.vue +16 -0
  56. package/src/components/Combobox/types.ts +15 -0
  57. package/src/components/DatePicker/DatePicker.vue +1 -1
  58. package/src/components/DatePicker/DateRangePicker.vue +1 -1
  59. package/src/components/DatePicker/DateTimePicker.vue +1 -1
  60. package/src/components/DatePicker/stories/DateTime.vue +16 -0
  61. package/src/components/DatePicker/stories/Examples.vue +11 -0
  62. package/src/components/DatePicker/stories/Range.vue +12 -0
  63. package/src/components/DatePicker/types.ts +1 -1
  64. package/src/components/DatePicker/utils.ts +1 -1
  65. package/src/components/Dialog/Dialog.vue +22 -0
  66. package/src/components/Dialog/stories/Confirm.vue +38 -0
  67. package/src/components/Dialog/stories/Custom.vue +38 -0
  68. package/src/components/Dialog/stories/Interactive.vue +48 -0
  69. package/src/components/Dialog/stories/Modal.vue +21 -0
  70. package/src/components/Dialog/types.ts +5 -0
  71. package/src/components/Dropdown/Dropdown.vue +13 -7
  72. package/src/components/Dropdown/stories/CustomTrigger.vue +19 -0
  73. package/src/components/Dropdown/stories/Examples.vue +17 -0
  74. package/src/components/Dropdown/stories/Grouped.vue +36 -0
  75. package/src/components/Dropdown/stories/Submenus.vue +31 -0
  76. package/src/components/Dropdown/stories/Switches.vue +28 -0
  77. package/src/components/Dropdown/types.ts +13 -1
  78. package/src/components/ErrorMessage/stories/ErrorObject.vue +15 -0
  79. package/src/components/ErrorMessage/stories/Examples.vue +10 -0
  80. package/src/components/ErrorMessage/types.ts +1 -0
  81. package/src/components/FileUploader/stories/Examples.vue +25 -0
  82. package/src/components/FormControl/FormControl.story.vue +85 -101
  83. package/src/components/FormControl/FormControl.vue +13 -0
  84. package/src/components/FormControl/types.ts +6 -0
  85. package/src/components/ListView/stories/CellSlot.vue +65 -0
  86. package/src/components/ListView/stories/CustomList.vue +120 -0
  87. package/src/components/ListView/stories/Empty.vue +40 -0
  88. package/src/components/ListView/stories/Examples.vue +56 -0
  89. package/src/components/ListView/stories/GroupedRows.vue +152 -0
  90. package/src/components/MonthPicker/MonthPicker.vue +7 -1
  91. package/src/components/MonthPicker/stories/Example.vue +10 -0
  92. package/src/components/MonthPicker/types.ts +4 -0
  93. package/src/components/MultiSelect/MultiSelect.vue +8 -0
  94. package/src/components/MultiSelect/stories/Example.vue +24 -0
  95. package/src/components/MultiSelect/stories/Footer.vue +43 -0
  96. package/src/components/MultiSelect/stories/Options.vue +33 -0
  97. package/src/components/MultiSelect/types.ts +5 -0
  98. package/src/components/Password/Password.vue +19 -14
  99. package/src/components/Password/stories/Example.vue +10 -0
  100. package/src/components/Password/types.ts +3 -0
  101. package/src/components/Popover/Popover.vue +39 -7
  102. package/src/components/Popover/stories/Click.vue +14 -0
  103. package/src/components/Popover/stories/Hover.vue +14 -0
  104. package/src/components/Popover/types.ts +15 -6
  105. package/src/components/Progress/Progress.vue +7 -0
  106. package/src/components/Progress/stories/Examples.vue +7 -0
  107. package/src/components/Progress/stories/Hint.vue +7 -0
  108. package/src/components/Progress/stories/Intervals.vue +12 -0
  109. package/src/components/Progress/stories/Sizes.vue +10 -0
  110. package/src/components/Progress/types.ts +11 -0
  111. package/src/components/Rating/stories/Examples.vue +7 -0
  112. package/src/components/Rating/types.ts +10 -1
  113. package/src/components/Select/Select.vue +17 -2
  114. package/src/components/Select/stories/Example.vue +42 -0
  115. package/src/components/Select/stories/OptionSlot.vue +62 -0
  116. package/src/components/Select/stories/TriggerSlots.vue +51 -0
  117. package/src/components/Select/types.ts +14 -1
  118. package/src/components/Sidebar/Sidebar.vue +19 -15
  119. package/src/components/Sidebar/stories/Example.vue +88 -0
  120. package/src/components/Slider/Slider.vue +38 -0
  121. package/src/components/Slider/index.ts +1 -0
  122. package/src/components/Slider/stories/Example.vue +11 -0
  123. package/src/components/Slider/stories/Range.vue +11 -0
  124. package/src/components/Slider/types.ts +5 -0
  125. package/src/components/Switch/Switch.vue +5 -1
  126. package/src/components/Switch/stories/Example.vue +7 -0
  127. package/src/components/Switch/stories/Labels.vue +17 -0
  128. package/src/components/Switch/types.ts +12 -1
  129. package/src/components/TabButtons/TabButtons.story.vue +17 -17
  130. package/src/components/Tabs/Tabs.vue +8 -0
  131. package/src/components/Tabs/stories/Icons.vue +40 -0
  132. package/src/components/Tabs/stories/Orientation.vue +44 -0
  133. package/src/components/Tabs/types.ts +11 -1
  134. package/src/components/TextEditor/TextEditor.vue +34 -154
  135. package/src/components/TextEditor/commands.js +55 -39
  136. package/src/components/TextEditor/{FontColor.vue → components/FontColor.vue} +2 -2
  137. package/src/components/TextEditor/{ImageViewerModal.vue → components/ImageViewerModal.vue} +4 -4
  138. package/src/components/TextEditor/{InsertImage.vue → components/InsertImage.vue} +1 -1
  139. package/src/components/TextEditor/{LinkPopup.vue → components/LinkPopup.vue} +4 -4
  140. package/src/components/TextEditor/components/MediaNodeView.vue +377 -0
  141. package/src/components/TextEditor/components/Menu.vue +206 -0
  142. package/src/components/TextEditor/{TextEditorBubbleMenu.vue → components/TextEditorBubbleMenu.vue} +5 -13
  143. package/src/components/TextEditor/{TextEditorFixedMenu.vue → components/TextEditorFixedMenu.vue} +10 -6
  144. package/src/components/TextEditor/{TextEditorFloatingMenu.vue → components/TextEditorFloatingMenu.vue} +4 -3
  145. package/src/components/TextEditor/components/index.js +0 -0
  146. package/src/components/TextEditor/extensions/code-block.ts +2 -2
  147. package/src/components/TextEditor/extensions/content-paste-extension.ts +45 -37
  148. package/src/components/TextEditor/extensions/copy-styles.ts +159 -0
  149. package/src/components/TextEditor/extensions/highlight/highlight-styles.css +0 -1
  150. package/src/components/TextEditor/extensions/iframe/InsertIframe.vue +13 -3
  151. package/src/components/TextEditor/extensions/image/image-extension.ts +26 -5
  152. package/src/components/TextEditor/extensions/image-group/ImageGroupNodeView.vue +18 -53
  153. package/src/components/TextEditor/extensions/image-group/image-group-extension.ts +71 -0
  154. package/src/components/TextEditor/{image-viewer-extension.ts → extensions/image-viewer-extension.ts} +1 -1
  155. package/src/components/TextEditor/extensions/link/index.ts +1 -0
  156. package/src/components/TextEditor/{link-extension.ts → extensions/link/link-extension.ts} +5 -15
  157. package/src/components/TextEditor/{linkPasteHandler.ts → extensions/link/linkPasteHandler.ts} +1 -1
  158. package/src/components/TextEditor/extensions/mention/mention-extension.ts +22 -1
  159. package/src/components/TextEditor/extensions/slash-commands/slash-commands-extension.ts +13 -0
  160. package/src/components/TextEditor/extensions/toc-node/TocNodeView.vue +438 -0
  161. package/src/components/TextEditor/extensions/toc-node/index.ts +3 -0
  162. package/src/components/TextEditor/extensions/toc-node/toc-node-extension.ts +166 -0
  163. package/src/components/TextEditor/extensions/video-extension.ts +603 -0
  164. package/src/components/TextEditor/index.ts +3 -3
  165. package/src/components/TextEditor/stories/Comment.vue +53 -0
  166. package/src/components/TextEditor/stories/Example.vue +33 -0
  167. package/src/components/TextEditor/style.css +164 -0
  168. package/src/components/TextEditor/types.ts +36 -0
  169. package/src/components/TextInput/TextInput.vue +9 -1
  170. package/src/components/TextInput/stories/List.vue +71 -0
  171. package/src/components/TextInput/types.ts +17 -0
  172. package/src/components/Textarea/stories/Variants.vue +8 -0
  173. package/src/components/Textarea/types.ts +17 -0
  174. package/src/components/TimePicker/TimePicker.vue +18 -0
  175. package/src/components/TimePicker/stories/Basic.vue +15 -0
  176. package/src/components/TimePicker/stories/CustomOptions.vue +26 -0
  177. package/src/components/TimePicker/stories/Range.vue +15 -0
  178. package/src/components/TimePicker/stories/TwentyFour.vue +11 -0
  179. package/src/components/TimePicker/types.ts +38 -0
  180. package/src/components/Tooltip/Tooltip.vue +21 -0
  181. package/src/components/Tooltip/stories/Examples.vue +9 -0
  182. package/src/components/Tooltip/stories/Slots.vue +16 -0
  183. package/src/components/Tooltip/types.ts +21 -0
  184. package/src/components/Tree/Tree.vue +5 -0
  185. package/src/components/Tree/{Tree.story.vue → stories/Example.vue} +15 -25
  186. package/src/components/Tree/types.ts +34 -6
  187. package/src/data-fetching/docStore.ts +17 -5
  188. package/src/data-fetching/useDoc/useDoc.ts +19 -15
  189. package/src/data-fetching/useList/useList.ts +1 -1
  190. package/src/directives/focus.ts +46 -0
  191. package/src/env.d.ts +0 -1
  192. package/src/index.ts +2 -1
  193. package/src/components/Alert/Alert.story.vue +0 -78
  194. package/src/components/Autocomplete/Autocomplete.story.vue +0 -104
  195. package/src/components/Avatar/Avatar.story.vue +0 -28
  196. package/src/components/Badge/Badge.story.vue +0 -26
  197. package/src/components/Breadcrumbs/Breadcrumbs.story.vue +0 -73
  198. package/src/components/Button/Button.story.vue +0 -34
  199. package/src/components/Calendar/Calendar.story.md +0 -197
  200. package/src/components/Calendar/Calendar.story.vue +0 -191
  201. package/src/components/Checkbox/Checkbox.story.vue +0 -22
  202. package/src/components/CircularProgressBar/CircularProgressBar.story.md +0 -43
  203. package/src/components/Combobox/Combobox.story.vue +0 -348
  204. package/src/components/DatePicker/DatePicker.story.vue +0 -36
  205. package/src/components/Dialog/Dialog.story.vue +0 -229
  206. package/src/components/Dropdown/Dropdown.story.vue +0 -225
  207. package/src/components/ErrorMessage/ErrorMessage.story.vue +0 -28
  208. package/src/components/FileUploader/FileUploader.story.vue +0 -34
  209. package/src/components/ListView/ListView.story.md +0 -137
  210. package/src/components/ListView/ListView.story.vue +0 -437
  211. package/src/components/MonthPicker/MonthPicker.story.vue +0 -21
  212. package/src/components/MultiSelect/MultiSelect.story.vue +0 -68
  213. package/src/components/Password/Password.story.vue +0 -14
  214. package/src/components/Popover/Popover.story.vue +0 -29
  215. package/src/components/Progress/Progress.story.vue +0 -34
  216. package/src/components/Rating/Rating.story.vue +0 -18
  217. package/src/components/Select/Select.story.vue +0 -78
  218. package/src/components/Sidebar/Sidebar.story.vue +0 -80
  219. package/src/components/Spinner/Spinner.story.vue +0 -14
  220. package/src/components/Spinner/Spinner.vue +0 -50
  221. package/src/components/Spinner/index.ts +0 -1
  222. package/src/components/Switch/Switch.story.vue +0 -59
  223. package/src/components/Tabs/Tabs.story.vue +0 -100
  224. package/src/components/TextEditor/Menu.vue +0 -110
  225. package/src/components/TextEditor/TextEditor.story.vue +0 -89
  226. package/src/components/TextEditor/extensions/image/ImageNodeView.vue +0 -263
  227. package/src/components/TextEditor/extensions/list-extension.ts +0 -32
  228. package/src/components/TextEditor/icons/align-center.vue +0 -14
  229. package/src/components/TextEditor/icons/align-justify.vue +0 -14
  230. package/src/components/TextEditor/icons/align-left.vue +0 -14
  231. package/src/components/TextEditor/icons/align-right.vue +0 -14
  232. package/src/components/TextEditor/icons/arrow-go-back-line.vue +0 -14
  233. package/src/components/TextEditor/icons/arrow-go-forward-line.vue +0 -14
  234. package/src/components/TextEditor/icons/bold.vue +0 -14
  235. package/src/components/TextEditor/icons/code-view.vue +0 -14
  236. package/src/components/TextEditor/icons/double-quotes-r.vue +0 -14
  237. package/src/components/TextEditor/icons/font-color.vue +0 -14
  238. package/src/components/TextEditor/icons/format-clear.vue +0 -14
  239. package/src/components/TextEditor/icons/h-1.vue +0 -14
  240. package/src/components/TextEditor/icons/h-2.vue +0 -14
  241. package/src/components/TextEditor/icons/h-3.vue +0 -14
  242. package/src/components/TextEditor/icons/h-4.vue +0 -14
  243. package/src/components/TextEditor/icons/h-5.vue +0 -14
  244. package/src/components/TextEditor/icons/h-6.vue +0 -14
  245. package/src/components/TextEditor/icons/image-add-line.vue +0 -14
  246. package/src/components/TextEditor/icons/italic.vue +0 -14
  247. package/src/components/TextEditor/icons/link.vue +0 -14
  248. package/src/components/TextEditor/icons/list-ordered.vue +0 -14
  249. package/src/components/TextEditor/icons/list-task.vue +0 -20
  250. package/src/components/TextEditor/icons/list-unordered.vue +0 -14
  251. package/src/components/TextEditor/icons/readme.md +0 -1
  252. package/src/components/TextEditor/icons/separator.vue +0 -14
  253. package/src/components/TextEditor/icons/strikethrough.vue +0 -14
  254. package/src/components/TextEditor/icons/table-2.vue +0 -14
  255. package/src/components/TextEditor/icons/text.vue +0 -11
  256. package/src/components/TextEditor/icons/underline.vue +0 -14
  257. package/src/components/TextEditor/icons/video-add-line.vue +0 -14
  258. package/src/components/TextEditor/mention.js +0 -72
  259. package/src/components/TextEditor/video-extension.ts +0 -162
  260. package/src/components/TextInput/TextInput.story.vue +0 -81
  261. package/src/components/Textarea/Textarea.story.vue +0 -27
  262. package/src/components/TimePicker/TimePicker.story.vue +0 -138
  263. package/src/components/Tooltip/Tooltip.story.vue +0 -51
  264. package/src/components/Tree/Tree.story.md +0 -56
  265. /package/src/components/TextEditor/{CodeBlockComponent.vue → components/CodeBlockComponent.vue} +0 -0
  266. /package/src/components/TextEditor/{InsertLink.vue → components/InsertLink.vue} +0 -0
  267. /package/src/components/TextEditor/{InsertVideo.vue → components/InsertVideo.vue} +0 -0
  268. /package/src/components/TextEditor/{MentionList.vue → components/MentionList.vue} +0 -0
@@ -0,0 +1,450 @@
1
+ <template>
2
+ <Dialog v-model="open" :options="{ size: 'lg' }" @close="dialogType = ''">
3
+ <template #body-main>
4
+ <div class="p-4 sm:px-6">
5
+ <div class="flex w-full justify-between gap-x-15 mb-4">
6
+ <div class="font-semibold text-2xl flex text-nowrap overflow-hidden">
7
+ <template v-if="props.entities.length > 1">
8
+ Moving {{ props.entities.length }} items
9
+ </template>
10
+ <template v-else>
11
+ Moving "
12
+ <div class="truncate max-w-[80%]">
13
+ {{ props.entities[0].title }}
14
+ </div>
15
+ "
16
+ </template>
17
+ </div>
18
+ <Button class="ml-auto" variant="ghost" @click="dialogType = ''">
19
+ <template #icon>
20
+ <LucideX class="size-4" />
21
+ </template>
22
+ </Button>
23
+ </div>
24
+ <Tabs v-model="tabIndex" as="div" :tabs="tabs">
25
+ <template #tab-panel>
26
+ <div class="py-1 h-64 overflow-auto flex flex-col">
27
+ <TeamSelector
28
+ v-if="tabIndex === 1"
29
+ v-model="chosenTeam"
30
+ class="py-2 px-1"
31
+ />
32
+ <Tree
33
+ v-for="k in tree.children"
34
+ :key="k.value"
35
+ node-key="value"
36
+ :node="k"
37
+ >
38
+ <template
39
+ #node="{ node, hasChildren, isCollapsed, toggleCollapsed }"
40
+ >
41
+ <div
42
+ class="flex items-center cursor-pointer select-none gap-1 h-7 shrink-0"
43
+ @click="openEntity(node)"
44
+ >
45
+ <div
46
+ ref="iconRef"
47
+ @click="
48
+ (e) => {
49
+ if (isCollapsed)
50
+ node.children.forEach((k) =>
51
+ fetchFolderContents(
52
+ k,
53
+ { entity_name: k.value },
54
+ true,
55
+ ),
56
+ )
57
+ toggleCollapsed(e)
58
+ }
59
+ "
60
+ >
61
+ <LucideChevronDown
62
+ v-if="hasChildren && !isCollapsed"
63
+ class="size-3.5"
64
+ />
65
+ <LucideChevronRight
66
+ v-else-if="hasChildren"
67
+ class="size-3.5"
68
+ />
69
+ <div v-else class="ps-3.5" />
70
+ </div>
71
+ <div
72
+ class="flex-grow rounded-sm text-base truncate h-full flex items-center pl-1"
73
+ :class="[
74
+ selected === node.value
75
+ ? 'bg-surface-gray-3'
76
+ : 'hover:bg-surface-gray-2',
77
+ entities[0].parent_entity === node.value
78
+ ? 'cursor-not-allowed hover:bg-surface-white'
79
+ : 'group',
80
+ ]"
81
+ >
82
+ <LucideFolderClosed
83
+ v-if="isCollapsed"
84
+ class="mr-1 size-4"
85
+ />
86
+ <LucideFolder v-else class="mr-1 size-4" />
87
+ <div v-if="node.value === null" class="overflow-visible">
88
+ <Input
89
+ v-model="node.label"
90
+ v-focus
91
+ type="text"
92
+ input-class=" !h-6"
93
+ @click.stop
94
+ @keydown.enter="openEntity(node)"
95
+ />
96
+ </div>
97
+ <span v-else
98
+ >{{ node.label }}
99
+ <span
100
+ v-if="entities[0].parent_entity === node.value"
101
+ class="text-ink-gray-5"
102
+ >(current)</span
103
+ ></span
104
+ >
105
+ <Button
106
+ class="shrink hidden group-hover:block ml-auto"
107
+ :class="{
108
+ '!bg-surface-gray-3': selected === node.value,
109
+ }"
110
+ @click.stop="
111
+ (e) => {
112
+ let obj = {
113
+ parent: node.value,
114
+ value: null,
115
+ label: 'New folder',
116
+ }
117
+ node.children.push(obj)
118
+ if (isCollapsed) toggleCollapsed(e)
119
+ }
120
+ "
121
+ >
122
+ <LucideFolderPlus class="size-4" />
123
+ </Button>
124
+ </div>
125
+ </div>
126
+ </template>
127
+ </Tree>
128
+ <div
129
+ v-if="tree.loading"
130
+ class="text-base flex justify-center flex-1"
131
+ >
132
+ <LoadingIndicator class="w-4.5" />
133
+ </div>
134
+ <div
135
+ v-else-if="!tree.children.length"
136
+ class="flex justify-center flex-1"
137
+ >
138
+ <div
139
+ class="self-center text-sm text-ink-gray-6 flex flex-col gap-2"
140
+ >
141
+ <LucideFolderClosed class="size-5 self-center" />
142
+ No folders found
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </template>
147
+ </Tabs>
148
+ <div class="flex items-center justify-between pt-4">
149
+ <div class="flex items-center my-auto justify-start">
150
+ <p class="text-sm pr-0.5 shrink-0">Moving to:</p>
151
+ <Dropdown
152
+ v-if="dropDownBreadcrumbs.length"
153
+ class="h-7"
154
+ :options="dropDownBreadcrumbs"
155
+ >
156
+ <Button variant="ghost">
157
+ <LucideEllipsis class="size-3.5" />
158
+ </Button>
159
+ </Dropdown>
160
+ <span
161
+ v-if="dropDownBreadcrumbs.length"
162
+ class="text-ink-gray-5 mx-0.5"
163
+ >
164
+ {{ '/' }}
165
+ </span>
166
+ <div class="flex w-24 overflow-hidden">
167
+ <div
168
+ v-for="(crumb, index) in slicedBreadcrumbs"
169
+ :key="index"
170
+ class="flex items-center"
171
+ >
172
+ <span
173
+ v-if="breadcrumbs.length > 1 && index > 0"
174
+ class="text-ink-gray-5 mx-0.5"
175
+ >
176
+ {{ '/' }}
177
+ </span>
178
+ <button
179
+ class="text-base cursor-pointer truncate max-w-20"
180
+ :title="crumb.title"
181
+ :class="
182
+ index === slicedBreadcrumbs.length - 1
183
+ ? 'text-ink-gray-9 text-base font-medium p-1'
184
+ : 'text-ink-gray-5 text-base rounded-[6px] hover:bg-surface-gray-2 p-1'
185
+ "
186
+ @click="closeEntity(crumb.name)"
187
+ >
188
+ {{ crumb.title }}
189
+ </button>
190
+ </div>
191
+ </div>
192
+ </div>
193
+ <Button
194
+ variant="solid"
195
+ class="ml-auto"
196
+ size="sm"
197
+ :disabled="
198
+ entities[0].parent_entity !== selected &&
199
+ chosenTeam === entities[0].team
200
+ "
201
+ :loading="move.loading"
202
+ @click="moveFile"
203
+ >
204
+ <template #prefix>
205
+ <LucideArrowLeftRight class="size-4" />
206
+ </template>
207
+ Move
208
+ </Button>
209
+ </div>
210
+ </div>
211
+ </template>
212
+ </Dialog>
213
+ </template>
214
+ <script setup>
215
+ import { watch, computed, h, ref, reactive } from 'vue'
216
+
217
+ import {
218
+ createResource,
219
+ Dialog,
220
+ Button,
221
+ Tabs,
222
+ Dropdown,
223
+ Tree,
224
+ Input,
225
+ LoadingIndicator,
226
+ toast,
227
+ } from '../../../src'
228
+ import { move, getTeams } from '../js/resources'
229
+
230
+ import { useRoute } from 'vue-router'
231
+
232
+ import LucideBuilding2 from '~icons/lucide/building-2'
233
+ import LucideChevronDown from '~icons/lucide/chevron-down'
234
+ import LucideFolder from '~icons/lucide/folder'
235
+ import LucideHome from '~icons/lucide/home'
236
+ import LucideArrowLeftRight from '~icons/lucide/arrow-left-right'
237
+ import TeamSelector from './TeamSelector.vue'
238
+
239
+ const props = defineProps({
240
+ entities: {
241
+ type: Object,
242
+ required: false,
243
+ default: null,
244
+ },
245
+ })
246
+ const emit = defineEmits(['success', 'complete'])
247
+
248
+ const dialogType = defineModel()
249
+ const open = ref(true)
250
+
251
+ const route = useRoute()
252
+ const tabIndex = ref(route.name == 'Home' ? 0 : 1)
253
+ const chosenTeam = ref(route.params.team || '')
254
+ const tree = reactive({
255
+ name: '',
256
+ label: 'Home',
257
+ children: [],
258
+ options: {
259
+ isCollapsed: true,
260
+ },
261
+ })
262
+
263
+ // State variables
264
+ const selected = ref('')
265
+ const breadcrumbs = ref([
266
+ { name: '', title: tabIndex.value === 0 ? 'Home' : 'Team' },
267
+ ])
268
+
269
+ const tabs = [
270
+ {
271
+ label: 'Home',
272
+ icon: h(LucideHome, { class: 'size-4' }),
273
+ },
274
+ {
275
+ label: 'Teams',
276
+ icon: h(LucideBuilding2, { class: 'size-4' }),
277
+ },
278
+ // {
279
+ // label: "Favourites",
280
+ // icon: h(Star, { class: "size-4" }),
281
+ // },
282
+ ]
283
+
284
+ const folderContents = createResource({
285
+ url: 'drive.api.list.files',
286
+ makeParams: (params) => ({
287
+ ...params,
288
+ team: chosenTeam.value,
289
+ is_active: 1,
290
+ folders: 1,
291
+ }),
292
+ })
293
+
294
+ const fetchFolderContents = (tree, params = {}, nested = false) => {
295
+ folderContents.fetch(params, {
296
+ onSuccess: (data) => {
297
+ tree.children = []
298
+ data.forEach((item) => {
299
+ const node = reactive({
300
+ ...item,
301
+ label: item.title,
302
+ value: item.name,
303
+ children: [],
304
+ })
305
+ node.isCollapsed = true
306
+ tree.children.push(node)
307
+ if (!nested)
308
+ fetchFolderContents(
309
+ node,
310
+ { ...params, entity_name: node.value },
311
+ true,
312
+ )
313
+ })
314
+ tree.loading = false
315
+ },
316
+ })
317
+ }
318
+
319
+ const selectedPerms = createResource({
320
+ url: 'drive.api.permissions.get_entity_with_permissions',
321
+ makeParams: () => ({
322
+ entity_name: selected.value,
323
+ }),
324
+ onSuccess: (data) => {
325
+ const team = getTeams.data[data.team]
326
+ const first = [
327
+ {
328
+ name: '',
329
+ title: team ? team.title : 'Home',
330
+ },
331
+ ]
332
+ breadcrumbs.value = first.concat(data.breadcrumbs.slice(1))
333
+ },
334
+ })
335
+
336
+ watch(
337
+ [tabIndex, chosenTeam],
338
+ ([newValue, team], [prev, _]) => {
339
+ selected.value = ''
340
+ tree.loading = true
341
+ if ((newValue === 1 && !team) || (newValue === 0 && prev == newValue))
342
+ return
343
+ tree.children = []
344
+ switch (newValue) {
345
+ case 0:
346
+ chosenTeam.value = ''
347
+ breadcrumbs.value = [{ name: '', title: 'Home' }]
348
+ fetchFolderContents(tree)
349
+ break
350
+ case 1:
351
+ breadcrumbs.value = [{ name: '', title: getTeams.data[team].title }]
352
+ fetchFolderContents(tree)
353
+ break
354
+ case 2:
355
+ folderContents.fetch({
356
+ entity_name: '',
357
+ favourites_only: true,
358
+ })
359
+ break
360
+ }
361
+ },
362
+ { immediate: true },
363
+ )
364
+
365
+ // Breadcrumb logic
366
+ const slicedBreadcrumbs = computed(() => {
367
+ if (breadcrumbs.value.length > 3) {
368
+ return breadcrumbs.value.slice(-3)
369
+ }
370
+ return breadcrumbs.value
371
+ })
372
+
373
+ const dropDownBreadcrumbs = computed(() => {
374
+ const allExceptLastTwo = breadcrumbs.value.slice(0, -3)
375
+ return allExceptLastTwo.map((item) => {
376
+ return {
377
+ ...item,
378
+ icon: null,
379
+ label: item.title,
380
+ onClick: () => closeEntity(item.name),
381
+ }
382
+ })
383
+ })
384
+
385
+ // New folder logic
386
+ const createdNode = ref(null)
387
+ const createFolder = createResource({
388
+ url: 'drive.api.files.create_folder',
389
+ makeParams(params) {
390
+ return {
391
+ ...params,
392
+ team: chosenTeam.value,
393
+ }
394
+ },
395
+ validate(params) {
396
+ if (!params?.title) return false
397
+ },
398
+ onSuccess(data) {
399
+ createdNode.value.value = data.name
400
+ createdNode.value.children = []
401
+ selected.value = data.name
402
+ selectedPerms.fetch()
403
+ createdNode.value = null
404
+ },
405
+ onError() {
406
+ toast.error('There is already a folder with this name here.')
407
+ },
408
+ })
409
+
410
+ // Selection logic
411
+ function openEntity(node) {
412
+ if (props.entities[0].parent_entity === node.value) return
413
+ if (!node.value) {
414
+ createdNode.value = node
415
+ createFolder.fetch({
416
+ title: node.label,
417
+ personal: tabIndex.value === 0,
418
+ parent: node.parent,
419
+ })
420
+ } else {
421
+ selected.value = node.value
422
+ selectedPerms.fetch({
423
+ entity_name: selected.value,
424
+ })
425
+ }
426
+ }
427
+
428
+ function closeEntity(name) {
429
+ const index = breadcrumbs.value.findIndex((obj) => obj.name === name)
430
+ if (breadcrumbs.value.length > 1 && index !== breadcrumbs.value.length - 1) {
431
+ breadcrumbs.value = breadcrumbs.value.slice(0, index + 1)
432
+ selected.value = breadcrumbs.value[breadcrumbs.value.length - 1].name
433
+ folderContents.fetch({
434
+ entity_name: selected.value,
435
+ personal: selected.value === '' ? 1 : -1,
436
+ })
437
+ }
438
+ }
439
+
440
+ const moveFile = async () => {
441
+ open.value = false
442
+ emit('success')
443
+ await move.submit({
444
+ entity_names: props.entities.map((obj) => obj.name),
445
+ new_parent: selected.value,
446
+ team: chosenTeam.value,
447
+ })
448
+ emit('complete')
449
+ }
450
+ </script>
@@ -0,0 +1,76 @@
1
+ <template>
2
+ <Dialog
3
+ v-model="open"
4
+ :options="{
5
+ title: 'Rename',
6
+ size: 'xs',
7
+ actions: [
8
+ {
9
+ label: 'Confirm',
10
+ variant: 'solid',
11
+ disabled: !newTitle || newTitle === entity.title,
12
+ onClick: submit,
13
+ },
14
+ ],
15
+ }"
16
+ @close="dialogType = ''"
17
+ >
18
+ <template #body-content>
19
+ <div class="flex gap-3">
20
+ <FormControl
21
+ v-model="newTitle"
22
+ v-focus
23
+ class="grow"
24
+ type="text"
25
+ @keyup.enter="submit"
26
+ />
27
+ <div
28
+ v-if="file_ext"
29
+ disabled
30
+ class="w-12 text-ink-gray-7 bg-surface-gray-2 rounded text-center self-center py-1.5 text-sm"
31
+ >
32
+ {{ file_ext }}
33
+ </div>
34
+ </div>
35
+ </template>
36
+ </Dialog>
37
+ </template>
38
+
39
+ <script setup>
40
+ import { ref } from 'vue'
41
+ import { Dialog } from '../../../src'
42
+ import { rename } from '../js/resources'
43
+
44
+ const props = defineProps({ entity: Object, modelValue: String })
45
+ const emit = defineEmits(['update:modelValue', 'success'])
46
+ const dialogType = defineModel()
47
+ const open = ref(true)
48
+
49
+ const newTitle = ref('')
50
+ const file_ext = ref('')
51
+
52
+ if (props.entity.is_group || props.entity.document) {
53
+ newTitle.value = props.entity.title
54
+ } else {
55
+ const parts = props.entity.title.split('.')
56
+ if (parts.length > 1) {
57
+ newTitle.value = parts.slice(0, -1).join('.')
58
+ file_ext.value = parts[parts.length - 1]
59
+ } else {
60
+ newTitle.value = parts[0]
61
+ }
62
+ }
63
+
64
+ const submit = () => {
65
+ const formattedTitle =
66
+ newTitle.value + (file_ext.value ? '.' + file_ext.value : '')
67
+ rename.submit({
68
+ entity_name: props.entity.name,
69
+ new_title: formattedTitle,
70
+ })
71
+ emit('success', {
72
+ name: props.entity.name,
73
+ title: formattedTitle,
74
+ })
75
+ }
76
+ </script>
@@ -0,0 +1,136 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import {
4
+ SelectContent,
5
+ SelectGroup,
6
+ SelectItem,
7
+ SelectItemText,
8
+ SelectItemIndicator,
9
+ SelectLabel,
10
+ SelectPortal,
11
+ SelectRoot,
12
+ SelectSeparator,
13
+ SelectTrigger,
14
+ SelectValue,
15
+ SelectViewport,
16
+ } from 'reka-ui'
17
+ import { SimpleOption } from '../TagInput/types'
18
+ import {
19
+ isGroup,
20
+ getLabel,
21
+ getMultipleLabel,
22
+ getValue,
23
+ getIcon,
24
+ RenderIcon,
25
+ isDisabled,
26
+ } from '../TagInput/utils'
27
+
28
+ import LucideChevronDown from '~icons/lucide/chevron-down'
29
+ import { SelectProps, SelectValue as SelectValue_ } from './types'
30
+
31
+ const props = withDefaults(defineProps<SelectProps>(), {
32
+ placeholder: 'Select an option',
33
+ })
34
+
35
+ const selected = defineModel<SelectValue_>()
36
+ const selectedOption = computed<SimpleOption | SimpleOption[]>(() => {
37
+ if (!selected.value) return null
38
+ if (props.multiple) {
39
+ return selected.value.map((k) =>
40
+ flatOptions.value.find((opt) => getValue(opt) === k),
41
+ )
42
+ }
43
+ return flatOptions.value.find((opt) => getValue(opt) === selected.value)
44
+ })
45
+ const selectedOptionIcon = computed(() =>
46
+ selectedOption.value && !props.multiple
47
+ ? getIcon(selectedOption.value)
48
+ : null,
49
+ )
50
+
51
+ const flatOptions = computed<SimpleOption[]>(() =>
52
+ props.options.flatMap((opt) => (isGroup(opt) ? opt.options : opt)),
53
+ )
54
+
55
+ const labelFunction = (val: SelectValue_, selected = false) => {
56
+ if (props.getLabel) return props.getLabel(val, selected)
57
+ if (!val || (val.map && !val.length)) return props.placeholder
58
+ return (val.map ? getMultipleLabel : getLabel)(val)
59
+ }
60
+ </script>
61
+
62
+ <template>
63
+ <div class="relative">
64
+ <SelectRoot v-model="selected" :multiple>
65
+ <SelectTrigger
66
+ :disabled
67
+ class="flex h-7 w-full overflow-hidden focus:ring-0 rounded bg-surface-gray-2 px-2 py-1 transition-colors hover:bg-surface-gray-3 focus:outline-0 focus:ring-0"
68
+ :class="{ 'opacity-50 pointer-events-none': disabled }"
69
+ >
70
+ <!-- Using SelectValue alone renders the icon too -->
71
+ <SelectValue
72
+ :placeholder
73
+ class="gap-2 text-base h-full flex items-center w-full focus:outline-0 text-ink-gray-8 data-[placeholder]:text-ink-gray-4"
74
+ >
75
+ <RenderIcon v-if="selectedOptionIcon" :icon="selectedOptionIcon" />
76
+ <div class="flex-1 flex justify-start truncate">
77
+ {{ labelFunction(selectedOption, true) }}
78
+ </div>
79
+ <RenderIcon :icon="LucideChevronDown" />
80
+ </SelectValue>
81
+ </SelectTrigger>
82
+
83
+ <SelectPortal>
84
+ <SelectContent
85
+ :hide-when-detached="true"
86
+ :align="'start'"
87
+ class="z-10 min-w-[--reka-select-trigger-width] mt-1 bg-surface-modal overflow-hidden rounded-lg shadow-2xl"
88
+ >
89
+ <SelectViewport
90
+ class="max-h-60 overflow-auto p-1.5"
91
+ :class="{ 'pt-0': isGroup(options[0]) }"
92
+ >
93
+ <template v-for="(optionOrGroup, index) in options" :key="index">
94
+ <component
95
+ :is="isGroup(optionOrGroup) ? SelectGroup : 'div'"
96
+ :class="{ '': isGroup(optionOrGroup) }"
97
+ >
98
+ <template v-if="isGroup(optionOrGroup)">
99
+ <hr v-if="optionOrGroup.group === true" class="my-1.5" />
100
+ <SelectLabel
101
+ v-else
102
+ class="px-2.5 pt-3 pb-1.5 text-sm font-medium text-ink-gray-5 sticky top-0 bg-surface-modal z-10"
103
+ >
104
+ {{ optionOrGroup.group }}
105
+ </SelectLabel>
106
+ </template>
107
+ <SelectItem
108
+ v-for="(option, idx) in optionOrGroup.options || [
109
+ optionOrGroup,
110
+ ]"
111
+ :key="idx"
112
+ :value="getValue(option)"
113
+ :disabled="isDisabled(option)"
114
+ class="text-base leading-none text-ink-gray-7 rounded flex items-center h-7 px-2.5 py-1.5 relative select-none data-[disabled]:opacity-50 data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-surface-gray-3"
115
+ >
116
+ <SelectItemText>
117
+ <span class="flex items-center gap-2 pr-6 flex-1">
118
+ <RenderIcon :icon="getIcon(option)" />
119
+ {{ labelFunction(option) }}
120
+ </span>
121
+ </SelectItemText>
122
+ <SelectItemIndicator
123
+ class="inline-flex ml-auto items-center justify-center"
124
+ >
125
+ <LucideCheck class="size-4" />
126
+ </SelectItemIndicator>
127
+ </SelectItem>
128
+ </component>
129
+ <SelectSeparator />
130
+ </template>
131
+ </SelectViewport>
132
+ </SelectContent>
133
+ </SelectPortal>
134
+ </SelectRoot>
135
+ </div>
136
+ </template>
@@ -0,0 +1,18 @@
1
+ type SelectOption =
2
+ | string
3
+ | {
4
+ label: string
5
+ value: string
6
+ icon?: any
7
+ disabled?: boolean
8
+ }
9
+
10
+ export type SelectValue = string | string[] | undefined
11
+
12
+ export interface SelectProps {
13
+ options: SelectOption[]
14
+ placeholder?: string
15
+ disabled?: boolean
16
+ multiple?: boolean
17
+ getLabel?: Function
18
+ }