frappe-ui 1.0.0-beta.2 → 1.0.0-beta.21

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 (692) hide show
  1. package/experimental/FloatingWindow/FloatingWindow.cy.ts +328 -0
  2. package/experimental/FloatingWindow/FloatingWindow.vue +303 -0
  3. package/experimental/FloatingWindow/index.ts +8 -0
  4. package/experimental/FloatingWindow/types.ts +31 -0
  5. package/experimental/FloatingWindow/useFloatingWindow.ts +236 -0
  6. package/experimental/MultiEmailInput/MultiEmailInput.cy.ts +155 -0
  7. package/experimental/MultiEmailInput/MultiEmailInput.vue +472 -0
  8. package/experimental/MultiEmailInput/index.ts +17 -0
  9. package/experimental/MultiEmailInput/stories/AsyncSuggestions.vue +54 -0
  10. package/experimental/MultiEmailInput/stories/Basic.vue +18 -0
  11. package/experimental/MultiEmailInput/stories/CustomChip.vue +39 -0
  12. package/experimental/MultiEmailInput/stories/Labeling.vue +28 -0
  13. package/experimental/MultiEmailInput/types.ts +138 -0
  14. package/experimental/MultiEmailInput/utils.ts +60 -0
  15. package/experimental.ts +12 -0
  16. package/frappe/Billing/SignupBanner.vue +1 -1
  17. package/frappe/Billing/TrialBanner.vue +1 -1
  18. package/frappe/DataImport/DataImport.vue +1 -1
  19. package/frappe/DataImport/DataImportList.vue +1 -1
  20. package/frappe/DataImport/ImportSteps.vue +6 -6
  21. package/frappe/DataImport/MappingStep.vue +1 -1
  22. package/frappe/DataImport/PreviewStep.vue +9 -9
  23. package/frappe/DataImport/TemplateModal.vue +1 -1
  24. package/frappe/DataImport/UploadStep.vue +5 -5
  25. package/frappe/Filter/Filter.vue +1 -1
  26. package/frappe/Help/HelpModal.vue +1 -1
  27. package/frappe/Link/stories/Suffix.vue +2 -2
  28. package/frappe/Onboarding/GettingStartedBanner.vue +1 -1
  29. package/frappe/drive/components/AppSwitcher.vue +1 -1
  30. package/frappe/drive/components/InfoDialog.vue +3 -3
  31. package/frappe/drive/components/MoveDialog.vue +2 -2
  32. package/frappe/drive/components/Select/Select.vue +2 -2
  33. package/frappe/drive/components/ShareDialog.vue +1 -1
  34. package/frappe/telemetry/index.ts +88 -14
  35. package/frappe/telemetry/pulse.ts +78 -164
  36. package/frappe/telemetry/telemetry.test.ts +13 -79
  37. package/icons/IconPicker.vue +1 -1
  38. package/package.json +104 -34
  39. package/src/components/Alert/Alert.md +4 -0
  40. package/src/components/Alert/Alert.vue +4 -4
  41. package/src/components/Autocomplete/Autocomplete.vue +6 -6
  42. package/src/components/Avatar/Avatar.md +4 -0
  43. package/src/components/Avatar/Avatar.vue +23 -4
  44. package/src/components/Badge/Badge.api.md +1 -1
  45. package/src/components/Badge/Badge.cy.ts +34 -16
  46. package/src/components/Badge/Badge.md +20 -4
  47. package/src/components/Badge/Badge.vue +60 -45
  48. package/src/components/Badge/stories/CallMeta.vue +35 -0
  49. package/src/components/Badge/stories/EventStatus.vue +16 -0
  50. package/src/components/Badge/stories/ListStatus.vue +91 -0
  51. package/src/components/Badge/stories/Reactions.vue +33 -0
  52. package/src/components/Badge/stories/RowTags.vue +39 -0
  53. package/src/components/Badge/types.ts +1 -2
  54. package/src/components/BottomSheet/BottomSheet.api.md +57 -0
  55. package/src/components/BottomSheet/BottomSheet.vue +232 -0
  56. package/src/components/BottomSheet/index.ts +6 -0
  57. package/src/components/BottomSheet/types.ts +23 -0
  58. package/src/components/Breadcrumbs/Breadcrumbs.md +4 -0
  59. package/src/components/Breadcrumbs/Breadcrumbs.vue +3 -3
  60. package/src/components/Button/Button.api.md +37 -32
  61. package/src/components/Button/Button.cy.ts +18 -2
  62. package/src/components/Button/Button.md +20 -8
  63. package/src/components/Button/Button.vue +304 -284
  64. package/src/components/Button/stories/InlineActions.vue +10 -0
  65. package/src/components/Button/stories/LiveClassCard.vue +34 -0
  66. package/src/components/Button/stories/SectionAction.vue +17 -0
  67. package/src/components/Button/stories/SectionControls.vue +28 -0
  68. package/src/components/Button/stories/SelectionToolbar.vue +20 -0
  69. package/src/components/Button/stories/StackedActions.vue +10 -0
  70. package/src/components/Button/types.ts +38 -32
  71. package/src/components/Calendar/Calendar.api.md +16 -16
  72. package/src/components/Calendar/Calendar.vue +100 -73
  73. package/src/components/Calendar/CalendarDaily.vue +21 -20
  74. package/src/components/Calendar/CalendarMonthEvent.vue +30 -9
  75. package/src/components/Calendar/CalendarMonthly.vue +60 -39
  76. package/src/components/Calendar/CalendarTimeMarker.vue +9 -8
  77. package/src/components/Calendar/CalendarWeekDayEvent.vue +98 -42
  78. package/src/components/Calendar/CalendarWeekly.vue +77 -41
  79. package/src/components/Calendar/EventModalContent.vue +32 -16
  80. package/src/components/Calendar/NewEventModal.vue +19 -16
  81. package/src/components/Calendar/ShowMoreCalendarEvent.vue +11 -17
  82. package/src/components/Calendar/{calendarUtils.js → calendarUtils.ts} +76 -52
  83. package/src/components/Calendar/composables/{useCalendarData.js → useCalendarData.ts} +23 -11
  84. package/src/components/Calendar/composables/{useEventModal.js → useEventModal.ts} +16 -12
  85. package/src/components/Calendar/index.ts +12 -2
  86. package/src/components/Calendar/stories/CustomHeader.vue +1 -1
  87. package/src/components/Calendar/types.ts +106 -0
  88. package/src/components/Calendar/{useEventBase.js → useEventBase.ts} +40 -26
  89. package/src/components/Card.vue +2 -2
  90. package/src/components/Charts/ECharts.vue +1 -1
  91. package/src/components/Charts/NumberChart.vue +7 -7
  92. package/src/components/Charts/eChartOptions.ts +28 -13
  93. package/src/components/Checkbox/Checkbox.api.md +6 -0
  94. package/src/components/Checkbox/Checkbox.cy.ts +10 -2
  95. package/src/components/Checkbox/Checkbox.md +28 -0
  96. package/src/components/Checkbox/Checkbox.vue +9 -22
  97. package/src/components/Checkbox/stories/Group.vue +27 -0
  98. package/src/components/Checkbox/stories/HorizontalGroup.vue +24 -0
  99. package/src/components/Checkbox/stories/SettingRow.vue +23 -0
  100. package/src/components/Checkbox/stories/WithDescription.vue +27 -0
  101. package/src/components/Checkbox/types.ts +7 -0
  102. package/src/components/CodeEditor/CodeEditor.api.md +139 -0
  103. package/src/components/CodeEditor/CodeEditor.cy.ts +160 -0
  104. package/src/components/CodeEditor/CodeEditor.md +104 -0
  105. package/src/components/CodeEditor/CodeEditor.vue +556 -0
  106. package/src/components/CodeEditor/CodePreview.test.ts +49 -0
  107. package/src/components/CodeEditor/CodePreview.vue +45 -0
  108. package/src/components/CodeEditor/index.ts +17 -0
  109. package/src/components/CodeEditor/languages.ts +63 -0
  110. package/src/components/CodeEditor/stories/Default.vue +16 -0
  111. package/src/components/CodeEditor/stories/Labeling.vue +25 -0
  112. package/src/components/CodeEditor/stories/MaxHeight.vue +24 -0
  113. package/src/components/CodeEditor/stories/Preview.vue +36 -0
  114. package/src/components/CodeEditor/stories/Sizes.vue +15 -0
  115. package/src/components/CodeEditor/stories/Variants.vue +13 -0
  116. package/src/components/CodeEditor/theme.ts +272 -0
  117. package/src/components/CodeEditor/types.ts +69 -0
  118. package/src/components/Combobox/Combobox.api.md +19 -20
  119. package/src/components/Combobox/Combobox.cy.ts +54 -14
  120. package/src/components/Combobox/Combobox.md +4 -0
  121. package/src/components/Combobox/Combobox.vue +233 -247
  122. package/src/components/Combobox/ComboboxResults.vue +1 -1
  123. package/src/components/Combobox/stories/Clearable.vue +2 -2
  124. package/src/components/Combobox/stories/CreateNew.vue +1 -1
  125. package/src/components/Combobox/stories/Footer.vue +10 -2
  126. package/src/components/Combobox/stories/InDialog.vue +1 -1
  127. package/src/components/Combobox/types.ts +8 -29
  128. package/src/components/CommandPalette/CommandPaletteItem.vue +1 -1
  129. package/src/components/ContextMenu/ContextMenu.api.md +52 -0
  130. package/src/components/ContextMenu/ContextMenu.cy.ts +191 -0
  131. package/src/components/ContextMenu/ContextMenu.md +25 -0
  132. package/src/components/ContextMenu/ContextMenu.vue +106 -0
  133. package/src/components/ContextMenu/index.ts +2 -0
  134. package/src/components/ContextMenu/stories/FileList.vue +105 -0
  135. package/src/components/ContextMenu/stories/Groups.vue +95 -0
  136. package/src/components/ContextMenu/stories/Simple.vue +72 -0
  137. package/src/components/ContextMenu/types.ts +39 -0
  138. package/src/components/DatePicker/CalendarPanel.vue +128 -86
  139. package/src/components/DatePicker/DatePicker.vue +3 -9
  140. package/src/components/DatePicker/DateRangePicker.vue +4 -8
  141. package/src/components/DatePicker/DateTimePicker.cy.ts +4 -2
  142. package/src/components/DatePicker/DateTimePicker.vue +3 -9
  143. package/src/components/DatePicker/composables.ts +15 -37
  144. package/src/components/DatePicker/stories/Range.vue +1 -1
  145. package/src/components/DatePicker/types.ts +1 -1
  146. package/src/components/DesktopShell/DesktopShell.api.md +40 -0
  147. package/src/components/DesktopShell/DesktopShell.cy.ts +39 -0
  148. package/src/components/DesktopShell/DesktopShell.md +31 -0
  149. package/src/components/DesktopShell/DesktopShell.vue +81 -0
  150. package/src/components/DesktopShell/index.ts +3 -0
  151. package/src/components/DesktopShell/stories/Default.vue +97 -0
  152. package/src/components/DesktopShell/types.ts +2 -0
  153. package/src/components/Dialog/Dialog.api.md +7 -8
  154. package/src/components/Dialog/Dialog.md +4 -0
  155. package/src/components/Dialog/Dialog.vue +8 -7
  156. package/src/components/Dialog/stories/CommandPalette.vue +1 -1
  157. package/src/components/Dialog/stories/Wizard.vue +1 -1
  158. package/src/components/Dialogs.vue +11 -9
  159. package/src/components/Divider/Divider.api.md +1 -1
  160. package/src/components/Divider/Divider.md +4 -0
  161. package/src/components/Dropdown/Dropdown.api.md +10 -35
  162. package/src/components/Dropdown/Dropdown.md +10 -6
  163. package/src/components/Dropdown/Dropdown.vue +33 -47
  164. package/src/components/Dropdown/stories/{05_KebabMenu.vue → KebabMenu.vue} +3 -1
  165. package/src/components/Dropdown/stories/{06_UserMenu.vue → UserMenu.vue} +1 -1
  166. package/src/components/Dropdown/types.ts +25 -189
  167. package/src/components/Duration/Duration.api.md +92 -0
  168. package/src/components/Duration/Duration.cy.ts +183 -0
  169. package/src/components/Duration/Duration.md +98 -0
  170. package/src/components/Duration/Duration.vue +114 -0
  171. package/src/components/Duration/duration.ts +185 -0
  172. package/src/components/Duration/index.ts +8 -0
  173. package/src/components/Duration/stories/Default.vue +10 -0
  174. package/src/components/Duration/stories/Formats.vue +16 -0
  175. package/src/components/Duration/stories/RealWorld.vue +50 -0
  176. package/src/components/Duration/stories/Sizes.vue +15 -0
  177. package/src/components/Duration/stories/States.vue +21 -0
  178. package/src/components/Duration/types.ts +52 -0
  179. package/src/components/ErrorMessage/ErrorMessage.cy.ts +1 -1
  180. package/src/components/ErrorMessage/ErrorMessage.md +4 -0
  181. package/src/components/ErrorMessage/ErrorMessage.vue +1 -1
  182. package/src/components/FileUploader/FileUploader.api.md +36 -11
  183. package/src/components/FileUploader/FileUploader.cy.ts +66 -0
  184. package/src/components/FileUploader/FileUploader.md +4 -1
  185. package/src/components/FileUploader/FileUploader.vue +147 -106
  186. package/src/components/FileUploader/index.ts +6 -0
  187. package/src/components/FileUploader/types.ts +43 -0
  188. package/src/components/FormControl/FormControl.api.md +3 -4
  189. package/src/components/FormControl/FormControl.md +4 -0
  190. package/src/components/FormControl/stories/RealForm.vue +3 -3
  191. package/src/components/FormLabel.vue +5 -8
  192. package/src/components/HoverCard/HoverCard.api.md +100 -0
  193. package/src/components/HoverCard/HoverCard.cy.ts +103 -0
  194. package/src/components/HoverCard/HoverCard.md +52 -0
  195. package/src/components/HoverCard/HoverCard.vue +91 -0
  196. package/src/components/HoverCard/index.ts +2 -0
  197. package/src/components/HoverCard/stories/Arrow.vue +28 -0
  198. package/src/components/HoverCard/stories/Delays.vue +16 -0
  199. package/src/components/HoverCard/stories/Example.vue +28 -0
  200. package/src/components/HoverCard/types.ts +47 -0
  201. package/src/components/Icon/Icon.cy.ts +78 -0
  202. package/src/components/Icon/Icon.vue +50 -0
  203. package/src/components/Icon/index.ts +1 -0
  204. package/src/components/InputLabeling/InputDescription.vue +1 -1
  205. package/src/components/InputLabeling/InputError.vue +1 -1
  206. package/src/components/InputLabeling/InputLabel.vue +21 -7
  207. package/src/components/InputLabeling/RequiredIndicator.vue +1 -1
  208. package/src/components/ItemListRow/ItemListRow.vue +1 -1
  209. package/src/components/ItemListRow/stories/RowStates.vue +1 -1
  210. package/src/components/KeyboardShortcut.vue +4 -8
  211. package/src/components/KeyboardShortcutsModal/KeyboardShortcutsModal.api.md +22 -14
  212. package/src/components/KeyboardShortcutsModal/KeyboardShortcutsModal.vue +2 -2
  213. package/src/components/ListFilter/ListFilter.api.md +5 -6
  214. package/src/components/ListFilter/ListFilter.vue +161 -57
  215. package/src/components/ListFilter/NestedPopover.vue +17 -10
  216. package/src/components/ListFilter/SearchComplete.vue +31 -38
  217. package/src/components/ListItem.vue +1 -1
  218. package/src/components/ListView/ListEmptyState.vue +1 -1
  219. package/src/components/ListView/ListGroupHeader.vue +2 -2
  220. package/src/components/ListView/ListRow.vue +1 -1
  221. package/src/components/ListView/ListSelectBanner.vue +1 -1
  222. package/src/components/ListView/ListView.api.md +322 -6
  223. package/src/components/ListView/index.ts +25 -0
  224. package/src/components/ListView/stories/CustomList.vue +1 -1
  225. package/src/components/ListView/stories/GroupedRows.vue +1 -1
  226. package/src/components/LoadingIndicator.vue +13 -22
  227. package/src/components/LoadingText.vue +3 -13
  228. package/src/components/{Dropdown/DropdownMenuList.vue → Menu/Menu.vue} +110 -75
  229. package/src/components/{Dropdown/DropdownMenuItemContent.vue → Menu/MenuItemContent.vue} +23 -33
  230. package/src/components/{Dropdown/DropdownRenderContent.vue → Menu/MenuRenderContent.vue} +1 -1
  231. package/src/components/Menu/types.ts +225 -0
  232. package/src/components/{Dropdown → Menu}/utils.ts +36 -40
  233. package/src/components/MobileNav/MobileNav.api.md +70 -0
  234. package/src/components/MobileNav/MobileNav.cy.ts +89 -0
  235. package/src/components/MobileNav/MobileNav.md +34 -0
  236. package/src/components/MobileNav/MobileNav.vue +15 -0
  237. package/src/components/MobileNav/MobileNavItem.vue +125 -0
  238. package/src/components/MobileNav/index.ts +4 -0
  239. package/src/components/MobileNav/stories/Default.vue +40 -0
  240. package/src/components/MobileNav/types.ts +33 -0
  241. package/src/components/MobileShell/MobileShell.api.md +23 -0
  242. package/src/components/MobileShell/MobileShell.cy.ts +25 -0
  243. package/src/components/MobileShell/MobileShell.md +24 -0
  244. package/src/components/MobileShell/MobileShell.vue +41 -0
  245. package/src/components/MobileShell/index.ts +3 -0
  246. package/src/components/MobileShell/stories/Default.vue +75 -0
  247. package/src/components/MobileShell/types.ts +7 -0
  248. package/src/components/MonthPicker/MonthPicker.cy.ts +16 -0
  249. package/src/components/MonthPicker/MonthPicker.vue +19 -8
  250. package/src/components/MultiSelect/MultiSelect.api.md +3 -4
  251. package/src/components/MultiSelect/MultiSelect.md +42 -10
  252. package/src/components/MultiSelect/MultiSelect.vue +199 -227
  253. package/src/components/MultiSelect/MultiSelectResults.vue +1 -1
  254. package/src/components/MultiSelect/stories/Members.vue +1 -1
  255. package/src/components/MultiSelect/stories/TagsTrigger.vue +3 -3
  256. package/src/components/MultiSelect/types.ts +3 -3
  257. package/src/components/PageHeader/PageHeader.api.md +112 -0
  258. package/src/components/PageHeader/PageHeader.cy.ts +68 -0
  259. package/src/components/PageHeader/PageHeader.md +25 -0
  260. package/src/components/PageHeader/PageHeader.vue +13 -0
  261. package/src/components/PageHeader/PageHeaderBackButton.vue +29 -0
  262. package/src/components/PageHeader/PageHeaderBase.vue +51 -0
  263. package/src/components/PageHeader/PageHeaderMobile.vue +68 -0
  264. package/src/components/PageHeader/PageHeaderMobileTitle.vue +16 -0
  265. package/src/components/PageHeader/PageHeaderTarget.vue +13 -0
  266. package/src/components/PageHeader/PageHeaderTitle.vue +12 -0
  267. package/src/components/PageHeader/index.ts +7 -0
  268. package/src/components/PageHeader/stories/Default.vue +19 -0
  269. package/src/components/PageHeader/stories/Mobile.vue +24 -0
  270. package/src/components/PageHeader/target.ts +19 -0
  271. package/src/components/PageHeader/types.ts +19 -0
  272. package/src/components/Password/Password.md +4 -0
  273. package/src/components/Password/Password.vue +2 -2
  274. package/src/components/Popover/Popover.api.md +126 -42
  275. package/src/components/Popover/Popover.cy.ts +425 -41
  276. package/src/components/Popover/Popover.md +99 -4
  277. package/src/components/Popover/Popover.vue +288 -215
  278. package/src/components/Popover/index.ts +8 -1
  279. package/src/components/Popover/stories/Arrow.vue +16 -0
  280. package/src/components/Popover/stories/Bare.vue +20 -0
  281. package/src/components/Popover/stories/Click.vue +7 -4
  282. package/src/components/Popover/stories/Controlled.vue +22 -0
  283. package/src/components/Popover/stories/Dismissible.vue +19 -0
  284. package/src/components/Popover/stories/MatchTriggerWidth.vue +16 -0
  285. package/src/components/Popover/stories/SideAlign.vue +17 -0
  286. package/src/components/Popover/types.ts +101 -23
  287. package/src/components/Progress/Progress.cy.ts +2 -2
  288. package/src/components/Progress/Progress.md +4 -0
  289. package/src/components/Progress/Progress.vue +4 -4
  290. package/src/components/Rail/Rail.api.md +91 -0
  291. package/src/components/Rail/Rail.cy.ts +84 -0
  292. package/src/components/Rail/Rail.md +30 -0
  293. package/src/components/Rail/Rail.vue +16 -0
  294. package/src/components/Rail/RailItem.vue +134 -0
  295. package/src/components/Rail/RailItemBadge.vue +64 -0
  296. package/src/components/Rail/index.ts +2 -0
  297. package/src/components/Rail/stories/Default.vue +44 -0
  298. package/src/components/Rail/types.ts +37 -0
  299. package/src/components/Rating/Rating.md +4 -0
  300. package/src/components/Rating/Rating.vue +5 -8
  301. package/src/components/Rating/stories/CustomSlot.vue +2 -2
  302. package/src/components/ScrollArea/ScrollArea.api.md +59 -0
  303. package/src/components/ScrollArea/ScrollArea.vue +46 -0
  304. package/src/components/ScrollArea/ScrollBar.vue +101 -0
  305. package/src/components/ScrollArea/index.ts +2 -0
  306. package/src/components/Select/Select.cy.ts +16 -15
  307. package/src/components/Select/Select.md +4 -0
  308. package/src/components/Select/Select.vue +217 -203
  309. package/src/components/Select/stories/Example.vue +4 -3
  310. package/src/components/Select/stories/States.vue +1 -1
  311. package/src/components/Select/stories/TriggerSlots.vue +2 -2
  312. package/src/components/Select/utils.ts +4 -3
  313. package/src/components/SettingsDialog/SettingsBody.vue +23 -0
  314. package/src/components/SettingsDialog/SettingsContent.vue +7 -0
  315. package/src/components/SettingsDialog/SettingsDialog.api.md +273 -0
  316. package/src/components/SettingsDialog/SettingsDialog.cy.ts +126 -0
  317. package/src/components/SettingsDialog/SettingsDialog.md +43 -0
  318. package/src/components/SettingsDialog/SettingsDialog.vue +76 -0
  319. package/src/components/SettingsDialog/SettingsHeader.vue +36 -0
  320. package/src/components/SettingsDialog/SettingsNavGroup.vue +20 -0
  321. package/src/components/SettingsDialog/SettingsNavItem.vue +33 -0
  322. package/src/components/SettingsDialog/SettingsPanel.cy.ts +77 -0
  323. package/src/components/SettingsDialog/SettingsPanel.vue +26 -0
  324. package/src/components/SettingsDialog/SettingsRow.vue +52 -0
  325. package/src/components/SettingsDialog/SettingsSidebar.vue +16 -0
  326. package/src/components/SettingsDialog/index.ts +10 -0
  327. package/src/components/SettingsDialog/stories/Default.vue +99 -0
  328. package/src/components/SettingsDialog/stories/PanelBasic.vue +52 -0
  329. package/src/components/SettingsDialog/stories/panels/NotificationsPanel.vue +57 -0
  330. package/src/components/SettingsDialog/stories/panels/PreferencesPanel.vue +57 -0
  331. package/src/components/SettingsDialog/stories/panels/ProfilePanel.vue +38 -0
  332. package/src/components/SettingsDialog/stories/panels/UsersPanel.vue +83 -0
  333. package/src/components/SettingsDialog/types.ts +21 -0
  334. package/src/components/Sidebar/Sidebar.api.md +124 -65
  335. package/src/components/Sidebar/Sidebar.cy.ts +127 -76
  336. package/src/components/Sidebar/Sidebar.md +47 -3
  337. package/src/components/Sidebar/Sidebar.vue +64 -56
  338. package/src/components/Sidebar/SidebarCollapseToggle.vue +23 -0
  339. package/src/components/Sidebar/SidebarHeader.vue +76 -52
  340. package/src/components/Sidebar/SidebarItem.vue +159 -95
  341. package/src/components/Sidebar/SidebarItemIcon.vue +25 -0
  342. package/src/components/Sidebar/SidebarLabel.vue +34 -0
  343. package/src/components/Sidebar/SidebarSection.vue +29 -14
  344. package/src/components/Sidebar/index.ts +13 -2
  345. package/src/components/Sidebar/stories/Collapsed.vue +40 -0
  346. package/src/components/Sidebar/stories/Default.vue +153 -0
  347. package/src/components/Sidebar/stories/Legacy.vue +36 -0
  348. package/src/components/Sidebar/types.ts +105 -20
  349. package/src/components/Skeleton/Skeleton.cy.ts +21 -0
  350. package/src/components/Skeleton/Skeleton.md +21 -0
  351. package/src/components/Skeleton/Skeleton.vue +6 -0
  352. package/src/components/Skeleton/index.ts +1 -0
  353. package/src/components/Skeleton/stories/Card.vue +12 -0
  354. package/src/components/Skeleton/stories/List.vue +15 -0
  355. package/src/components/Skeleton/stories/Text.vue +11 -0
  356. package/src/components/Slider/Slider.api.md +1 -1
  357. package/src/components/Slider/Slider.cy.ts +29 -0
  358. package/src/components/Slider/Slider.md +10 -0
  359. package/src/components/Slider/Slider.vue +37 -9
  360. package/src/components/Slider/stories/NegativeValues.vue +12 -0
  361. package/src/components/Slider/types.ts +1 -1
  362. package/src/components/Spinner/Spinner.api.md +32 -0
  363. package/src/components/Spinner/Spinner.cy.ts +87 -0
  364. package/src/components/Spinner/Spinner.md +21 -0
  365. package/src/components/Spinner/Spinner.vue +168 -0
  366. package/src/components/Spinner/index.ts +2 -0
  367. package/src/components/Spinner/stories/InContext.vue +49 -0
  368. package/src/components/Spinner/stories/Sizes.vue +12 -0
  369. package/src/components/Spinner/stories/Themes.vue +10 -0
  370. package/src/components/Spinner/stories/Track.vue +12 -0
  371. package/src/components/Spinner/types.ts +14 -0
  372. package/src/components/Switch/Switch.md +4 -0
  373. package/src/components/Switch/Switch.vue +6 -23
  374. package/src/components/TabButtons/Pill.vue +161 -0
  375. package/src/components/TabButtons/TabButtons.api.md +52 -2
  376. package/src/components/TabButtons/TabButtons.cy.ts +18 -5
  377. package/src/components/TabButtons/TabButtons.md +49 -0
  378. package/src/components/TabButtons/TabButtons.vue +207 -109
  379. package/src/components/TabButtons/index.ts +11 -1
  380. package/src/components/TabButtons/pillTypes.ts +43 -0
  381. package/src/components/TabButtons/stories/PrefixSuffix.vue +65 -0
  382. package/src/components/TabButtons/stories/Sizes.vue +26 -0
  383. package/src/components/TabButtons/stories/Variants.vue +40 -0
  384. package/src/components/TabButtons/stories/Vertical.vue +64 -0
  385. package/src/components/TabButtons/types.ts +49 -0
  386. package/src/components/Tabs/Tabs.md +4 -0
  387. package/src/components/Tabs/Tabs.vue +1 -1
  388. package/src/components/TextEditor/TextEditor.cy.ts +1 -1
  389. package/src/components/TextEditor/TextEditor.md +3 -0
  390. package/src/components/TextEditor/TextEditor.vue +7 -4
  391. package/src/components/TextEditor/components/CodeBlockComponent.vue +4 -194
  392. package/src/components/TextEditor/components/FontColor.vue +1 -1
  393. package/src/components/TextEditor/components/LinkPopup.vue +1 -1
  394. package/src/components/TextEditor/components/MediaNodeView.vue +17 -17
  395. package/src/components/TextEditor/components/MentionList.vue +1 -1
  396. package/src/components/TextEditor/components/TextEditorBubbleMenu.vue +14 -5
  397. package/src/components/TextEditor/components/TextEditorFixedMenu.vue +3 -3
  398. package/src/components/TextEditor/components/{Menu.vue → TextEditorMenu.vue} +3 -3
  399. package/src/components/TextEditor/extensions/iframe/InsertIframe.vue +2 -2
  400. package/src/components/TextEditor/extensions/iframe/iframe-extension.ts +62 -19
  401. package/src/components/TextEditor/extensions/image-group/ImageGroupNodeView.vue +1 -1
  402. package/src/components/TextEditor/extensions/image-group/ImageGroupUploadDialog.vue +4 -4
  403. package/src/components/TextEditor/extensions/suggestion/SuggestionList.vue +1 -1
  404. package/src/components/TextEditor/extensions/toc-node/TocNodeView.vue +1 -1
  405. package/src/components/TextEditor/hljs-github.css +197 -0
  406. package/src/components/TextEditor/index.ts +13 -0
  407. package/src/components/TextInput/TextInput.cy.ts +8 -3
  408. package/src/components/TextInput/TextInput.md +4 -0
  409. package/src/components/TextInput/TextInput.vue +11 -15
  410. package/src/components/TextInput/stories/Slots.vue +1 -1
  411. package/src/components/Textarea/Textarea.cy.ts +1 -1
  412. package/src/components/Textarea/Textarea.md +4 -0
  413. package/src/components/Textarea/Textarea.vue +11 -15
  414. package/src/components/ThemeSwitcher/ThemePreview.vue +74 -0
  415. package/src/components/ThemeSwitcher/ThemeSwitcher.api.md +82 -0
  416. package/src/components/ThemeSwitcher/ThemeSwitcher.cy.ts +101 -0
  417. package/src/components/ThemeSwitcher/ThemeSwitcher.md +70 -0
  418. package/src/components/ThemeSwitcher/ThemeSwitcher.vue +145 -0
  419. package/src/components/ThemeSwitcher/index.ts +2 -0
  420. package/src/components/ThemeSwitcher/previews/PreviewWindow.vue +74 -0
  421. package/src/components/ThemeSwitcher/stories/Branded.vue +12 -0
  422. package/src/components/ThemeSwitcher/stories/Default.vue +7 -0
  423. package/src/components/ThemeSwitcher/stories/Menu.vue +53 -0
  424. package/src/components/ThemeSwitcher/stories/Toggle.vue +25 -0
  425. package/src/components/ThemeSwitcher/types.ts +35 -0
  426. package/src/components/TimePicker/TimePicker.api.md +67 -15
  427. package/src/components/TimePicker/TimePicker.cy.ts +130 -14
  428. package/src/components/TimePicker/TimePicker.md +6 -0
  429. package/src/components/TimePicker/TimePicker.vue +74 -19
  430. package/src/components/TimePicker/stories/Labeling.vue +21 -0
  431. package/src/components/TimePicker/stories/Range.vue +1 -1
  432. package/src/components/TimePicker/stories/SizesAndVariants.vue +23 -0
  433. package/src/components/TimePicker/stories/TwentyFour.vue +1 -1
  434. package/src/components/TimePicker/types.ts +15 -3
  435. package/src/components/TimePicker/utils.ts +82 -21
  436. package/src/components/Toast/Toast.md +1 -1
  437. package/src/components/Toast/Toast.sanitize.test.ts +50 -0
  438. package/src/components/Toast/Toast.test.ts +5 -0
  439. package/src/components/Toast/Toast.vue +7 -7
  440. package/src/components/Toast/ToastProvider.vue +18 -9
  441. package/src/components/Toast/stories/CustomIcon.vue +1 -1
  442. package/src/components/Toast/toast.ts +32 -8
  443. package/src/components/Tooltip/Tooltip.api.md +43 -5
  444. package/src/components/Tooltip/Tooltip.cy.ts +48 -0
  445. package/src/components/Tooltip/Tooltip.md +10 -0
  446. package/src/components/Tooltip/Tooltip.vue +20 -5
  447. package/src/components/Tooltip/TooltipBubble.vue +2 -2
  448. package/src/components/Tooltip/TooltipProvider.vue +53 -0
  449. package/src/components/Tooltip/index.ts +1 -0
  450. package/src/components/Tooltip/stories/Group.vue +22 -0
  451. package/src/components/Tooltip/stories/Slots.vue +2 -2
  452. package/src/components/Tooltip/types.ts +1 -1
  453. package/src/components/Tree/Tree.api.md +76 -18
  454. package/src/components/Tree/Tree.md +144 -2
  455. package/src/components/Tree/Tree.vue +276 -97
  456. package/src/components/Tree/TreeItem.vue +263 -0
  457. package/src/components/Tree/index.ts +9 -1
  458. package/src/components/Tree/stories/DragDrop.vue +94 -0
  459. package/src/components/Tree/stories/Example.vue +11 -34
  460. package/src/components/Tree/stories/ExpandAll.vue +48 -0
  461. package/src/components/Tree/stories/Guides.vue +49 -0
  462. package/src/components/Tree/stories/ListView.vue +74 -0
  463. package/src/components/Tree/tree.cy.ts +218 -90
  464. package/src/components/Tree/types.ts +114 -24
  465. package/src/components/Tree/useTreeDragDrop.ts +203 -0
  466. package/src/components/Tree/useTreeKeyboard.ts +109 -0
  467. package/src/components/VueGridLayout/Layout.vue +1 -1
  468. package/src/components/shared/picker/PickerShell.vue +20 -15
  469. package/src/components/shared/popover/PopoverPanel.vue +67 -0
  470. package/src/components/shared/popover/popoverPanel.css +102 -0
  471. package/src/components/shared/selection/utils.ts +6 -6
  472. package/src/composables/useAutofocusOnOpen.ts +2 -0
  473. package/src/composables/useScreenSize.ts +46 -0
  474. package/src/composables/useScrollContainer.ts +108 -0
  475. package/src/composables/useShortcut.ts +3 -0
  476. package/src/data-fetching/docStore.test.ts +74 -0
  477. package/src/data-fetching/docStore.ts +18 -7
  478. package/src/data-fetching/useCall/types.ts +1 -0
  479. package/src/data-fetching/useCall/useCall.test.ts +43 -0
  480. package/src/data-fetching/useCall/useCall.ts +17 -4
  481. package/src/data-fetching/useDoc/useDoc.test.ts +61 -2
  482. package/src/data-fetching/useDoc/useDoc.ts +35 -15
  483. package/src/data-fetching/useDoctype/useDoctype.ts +6 -5
  484. package/src/data-fetching/useFrappeFetch.ts +17 -4
  485. package/src/data-fetching/useList/types.ts +1 -0
  486. package/src/data-fetching/useList/useList.test.ts +40 -0
  487. package/src/data-fetching/useList/useList.ts +20 -13
  488. package/src/data-fetching/useNewDoc/useNewDoc.ts +9 -5
  489. package/src/directives/focus.ts +3 -3
  490. package/src/index.ts +105 -74
  491. package/src/mocks/handlers.ts +8 -0
  492. package/src/molecules/editor/Editor.cy.ts +6 -1
  493. package/src/molecules/editor/EditorBubbleMenu.vue +1 -1
  494. package/src/molecules/editor/EditorContent.vue +50 -9
  495. package/src/molecules/editor/EditorFixedMenu.vue +2 -1
  496. package/src/molecules/editor/EditorFloatingMenu.vue +1 -1
  497. package/src/molecules/editor/EditorTableMenu.vue +303 -0
  498. package/src/molecules/editor/MenuItems.vue +81 -69
  499. package/src/molecules/editor/commands.ts +10 -2
  500. package/src/molecules/editor/components/AttachmentNodeView.vue +115 -0
  501. package/src/molecules/editor/components/EditorDropZone.vue +105 -0
  502. package/src/molecules/editor/components/EditorPopover.vue +89 -0
  503. package/src/molecules/editor/components/MediaNodeView.vue +156 -49
  504. package/src/molecules/editor/components/MediaResizeHandles.vue +38 -0
  505. package/src/molecules/editor/components/MediaToolbar.vue +108 -54
  506. package/src/molecules/editor/components/TableContextMenu.vue +144 -0
  507. package/src/molecules/editor/components/UploadProgressIndicator.vue +33 -0
  508. package/src/molecules/editor/components/VideoControls.vue +208 -0
  509. package/src/molecules/editor/components/font-color/ColorSwatchGrid.vue +3 -5
  510. package/src/molecules/editor/components/font-color/fontColorController.ts +28 -23
  511. package/src/molecules/editor/components/font-color/swatches.ts +1 -1
  512. package/src/molecules/editor/components/media-node-view-controller.ts +4 -4
  513. package/src/molecules/editor/components/media-node-view-utils.test.ts +0 -13
  514. package/src/molecules/editor/components/media-node-view-utils.ts +0 -10
  515. package/src/molecules/editor/components/table-color/tableCellColorController.ts +80 -0
  516. package/src/molecules/editor/components/table-size-picker/TableSizePicker.vue +92 -0
  517. package/src/molecules/editor/components/table-size-picker/tableSizePickerController.ts +40 -0
  518. package/src/molecules/editor/composables/useEditorFileDrop.ts +117 -0
  519. package/src/molecules/editor/composables/useFloatingPopup.ts +46 -2
  520. package/src/molecules/editor/composables/useNodeViewResize.test.ts +60 -11
  521. package/src/molecules/editor/composables/useNodeViewResize.ts +80 -21
  522. package/src/molecules/editor/composables/useTableCellColorState.ts +78 -0
  523. package/src/molecules/editor/composables/useWindowFileDragging.ts +68 -0
  524. package/src/molecules/editor/extensions/attachment/attachment-engine.ts +250 -0
  525. package/src/molecules/editor/extensions/attachment/attachment-extension.ts +248 -0
  526. package/src/molecules/editor/extensions/attachment/attachment.flow.test.ts +80 -0
  527. package/src/molecules/editor/extensions/attachment/attachment.roundtrip.test.ts +50 -0
  528. package/src/molecules/editor/extensions/attachment/index.ts +5 -0
  529. package/src/molecules/editor/extensions/code-block/CodeBlockComponent.css +16 -26
  530. package/src/molecules/editor/extensions/code-block/CodeBlockComponent.vue +107 -22
  531. package/src/molecules/editor/extensions/content-paste/content-paste-extension.ts +5 -3
  532. package/src/molecules/editor/extensions/iframe/IframeInsertDialog.vue +34 -7
  533. package/src/molecules/editor/extensions/iframe/IframeNodeView.vue +54 -48
  534. package/src/molecules/editor/extensions/iframe/iframe-allowlist.ts +33 -4
  535. package/src/molecules/editor/extensions/iframe/iframe-commands.ts +49 -5
  536. package/src/molecules/editor/extensions/iframe/iframe-embed-utils.ts +30 -0
  537. package/src/molecules/editor/extensions/iframe/iframe-extension.ts +4 -4
  538. package/src/molecules/editor/extensions/iframe/iframeInsertDialogController.ts +12 -1
  539. package/src/molecules/editor/extensions/iframe/index.ts +4 -4
  540. package/src/molecules/editor/extensions/iframe/useIframeDialog.ts +25 -13
  541. package/src/molecules/editor/extensions/image/image-extension.ts +29 -11
  542. package/src/molecules/editor/extensions/image-group/ImageGroupGrid.vue +27 -11
  543. package/src/molecules/editor/extensions/image-group/ImageGroupGridCell.vue +55 -11
  544. package/src/molecules/editor/extensions/image-group/ImageGroupNodeView.vue +64 -31
  545. package/src/molecules/editor/extensions/image-group/ImageGroupUploadDialog.vue +84 -40
  546. package/src/molecules/editor/extensions/image-group/image-group-extension.ts +4 -3
  547. package/src/molecules/editor/extensions/image-group/useImageGroupDialog.ts +116 -17
  548. package/src/molecules/editor/extensions/link/LinkEditorPopup.vue +214 -71
  549. package/src/molecules/editor/extensions/link/link-click-plugin.ts +10 -15
  550. package/src/molecules/editor/extensions/link/link-commands.ts +24 -3
  551. package/src/molecules/editor/extensions/link/link-extension.ts +11 -2
  552. package/src/molecules/editor/extensions/link/link-popup-controller.test.ts +49 -0
  553. package/src/molecules/editor/extensions/link/link-popup-controller.ts +67 -2
  554. package/src/molecules/editor/extensions/link/link-shortcut-plugin.ts +7 -2
  555. package/src/molecules/editor/extensions/media-drop/media-drop-extension.ts +177 -0
  556. package/src/molecules/editor/extensions/shared/media-dimensions.ts +36 -3
  557. package/src/molecules/editor/extensions/shared/media-node-ops.ts +17 -0
  558. package/src/molecules/editor/extensions/shared/media-plugin.test.ts +60 -0
  559. package/src/molecules/editor/extensions/shared/media-plugin.ts +7 -37
  560. package/src/molecules/editor/extensions/shared/media-upload-engine.test.ts +39 -2
  561. package/src/molecules/editor/extensions/shared/media-upload-engine.ts +104 -10
  562. package/src/molecules/editor/extensions/shared/media-upload-state.ts +55 -0
  563. package/src/molecules/editor/extensions/shared/media-upload-types.ts +52 -1
  564. package/src/molecules/editor/extensions/shared/suggestion-helpers.ts +20 -0
  565. package/src/molecules/editor/extensions/shared/suggestion-types.ts +7 -0
  566. package/src/molecules/editor/extensions/shared/upload-types.ts +1 -0
  567. package/src/molecules/editor/extensions/slash-commands/slash-commands-extension.test.ts +38 -0
  568. package/src/molecules/editor/extensions/slash-commands/slash-commands-extension.ts +119 -55
  569. package/src/molecules/editor/extensions/suggestion/SuggestionList.vue +62 -22
  570. package/src/molecules/editor/extensions/suggestion/SuggestionListItem.vue +1 -1
  571. package/src/molecules/editor/extensions/suggestion/createSuggestionExtension.ts +4 -0
  572. package/src/molecules/editor/extensions/table/drag-scroll.ts +154 -0
  573. package/src/molecules/editor/extensions/table/table-cell-color.ts +188 -0
  574. package/src/molecules/editor/extensions/table/table-navigation.ts +560 -0
  575. package/src/molecules/editor/extensions/table/table-selection-overlay.ts +137 -0
  576. package/src/molecules/editor/extensions/toc-node/TocNodeView.vue +1 -1
  577. package/src/molecules/editor/extensions/toc-node/toc-render.ts +7 -3
  578. package/src/molecules/editor/extensions/video/video-extension.ts +72 -5
  579. package/src/molecules/editor/extensions.ts +176 -8
  580. package/src/molecules/editor/index.ts +9 -4
  581. package/src/molecules/editor/kits.test.ts +21 -1
  582. package/src/molecules/editor/kits.ts +56 -33
  583. package/src/molecules/editor/menu.test.ts +3 -1
  584. package/src/molecules/editor/menu.ts +180 -18
  585. package/src/molecules/editor/stories/Comment.vue +4 -4
  586. package/src/molecules/editor/stories/Inline.vue +1 -1
  587. package/src/molecules/editor/stories/Primitives.vue +1 -1
  588. package/src/molecules/editor/stories/RichText.vue +1 -1
  589. package/src/molecules/editor/style.css +80 -3
  590. package/src/molecules/editor/useEditor.test.ts +4 -0
  591. package/src/molecules/editor/useEditor.ts +7 -3
  592. package/src/molecules/list/List.cy.ts +626 -0
  593. package/src/molecules/list/List.vue +93 -0
  594. package/src/molecules/list/ListCell.vue +15 -0
  595. package/src/molecules/list/ListGroup.vue +40 -0
  596. package/src/molecules/list/ListHeader.vue +62 -0
  597. package/src/molecules/list/ListHeaderCell.vue +26 -0
  598. package/src/molecules/list/ListHeaderCellSort.vue +106 -0
  599. package/src/molecules/list/ListRow.vue +55 -0
  600. package/src/molecules/list/ListRowBase.vue +103 -0
  601. package/src/molecules/list/ListRows.vue +120 -0
  602. package/src/molecules/list/index.ts +24 -0
  603. package/src/molecules/list/list-context.ts +44 -0
  604. package/src/molecules/list/list.api.md +276 -0
  605. package/src/molecules/list/list.md +119 -0
  606. package/src/molecules/list/stories/Columns.vue +103 -0
  607. package/src/molecules/list/stories/Feed.vue +105 -0
  608. package/src/molecules/list/stories/Virtual.vue +31 -0
  609. package/src/molecules/list/style.css +173 -0
  610. package/src/molecules/list/types.ts +82 -0
  611. package/src/molecules/list/useVirtualRows.ts +80 -0
  612. package/src/resources/documentResource.d.ts +28 -0
  613. package/src/resources/documentResource.js +32 -4
  614. package/src/resources/documentResource.test.ts +111 -0
  615. package/src/resources/listResource.d.ts +33 -0
  616. package/src/resources/plugin.d.ts +7 -0
  617. package/src/resources/resources.d.ts +35 -0
  618. package/src/utils/{call.js → call.ts} +50 -9
  619. package/src/utils/config.ts +16 -1
  620. package/src/utils/confirmDialog.d.ts +17 -0
  621. package/src/utils/debounce.ts +6 -7
  622. package/src/utils/dialog.cy.ts +9 -9
  623. package/src/utils/fileSize.ts +36 -0
  624. package/src/utils/fileUploadHandler.ts +46 -6
  625. package/src/utils/frappeRequest.test.ts +79 -0
  626. package/src/utils/{frappeRequest.js → frappeRequest.ts} +76 -13
  627. package/src/utils/pageMeta.ts +1 -1
  628. package/src/utils/plugin.ts +58 -0
  629. package/src/utils/{request.js → request.ts} +28 -3
  630. package/src/utils/{socketio.js → socketio.ts} +11 -1
  631. package/src/utils/theme.ts +48 -57
  632. package/src/utils/useFileUpload.ts +80 -23
  633. package/tailwind/audit-token-drift.cjs +176 -0
  634. package/tailwind/colorPalette.js +69 -8
  635. package/tailwind/colors.json +1115 -397
  636. package/tailwind/figma-tokens-to-theme.js +364 -0
  637. package/tailwind/generated/colors.json +1217 -0
  638. package/tailwind/generated/effects.json +41 -0
  639. package/tailwind/generated/radius.json +20 -0
  640. package/tailwind/generated/typography.json +479 -0
  641. package/tailwind/migrate-tokens-v2.js +453 -0
  642. package/tailwind/migrate-tokens-v2.test.js +55 -0
  643. package/tailwind/plugin.js +220 -15
  644. package/tailwind/preset.js +15 -0
  645. package/tailwind/tokens.js +35 -37
  646. package/tsconfig.base.json +2 -2
  647. package/vitepress/components/Docs/CommandPalette.vue +214 -0
  648. package/vitepress/components/Docs/Demo.vue +60 -0
  649. package/vitepress/components/Docs/EmitsTable.vue +99 -0
  650. package/vitepress/components/Docs/MobileNavSheet.vue +148 -0
  651. package/vitepress/components/Docs/OnThisPage.vue +74 -0
  652. package/vitepress/components/Docs/PrevNextBtns.vue +58 -0
  653. package/vitepress/components/Docs/PropsTable.vue +185 -0
  654. package/vitepress/components/Docs/Search.vue +34 -0
  655. package/vitepress/components/Docs/Sidebar.vue +91 -0
  656. package/vitepress/components/Docs/SlotsTable.vue +99 -0
  657. package/vitepress/components/Docs/index.ts +13 -0
  658. package/vitepress/components/Docs/sidebarList.ts +33 -0
  659. package/vitepress/components/Layout.vue +58 -0
  660. package/vitepress/components/Navbar.vue +126 -0
  661. package/vitepress/components/index.ts +3 -0
  662. package/vitepress/composables/index.ts +3 -0
  663. package/vitepress/composables/useTheme.ts +39 -0
  664. package/vitepress/css/fonts/Inter-Italic.var.woff2 +0 -0
  665. package/vitepress/css/fonts/Inter.var.woff2 +0 -0
  666. package/vitepress/css/style.css +198 -0
  667. package/vitepress/index.d.ts +23 -0
  668. package/vitepress/index.node.ts +175 -0
  669. package/vitepress/index.ts +17 -0
  670. package/vitepress/plugins/colocatedComponentDocs.ts +164 -0
  671. package/vitepress/plugins/componentTransformer.ts +286 -0
  672. package/vitepress/plugins/index.ts +12 -0
  673. package/vitepress/state.ts +10 -0
  674. package/vitepress/theme/index.ts +21 -0
  675. package/frappe/telemetry/utils.ts +0 -15
  676. package/src/components/Badge/stories/Themes.vue +0 -10
  677. package/src/components/Badge/stories/Variants.vue +0 -10
  678. package/src/components/Button/stories/Icons.vue +0 -27
  679. package/src/components/Button/stories/Sizes.vue +0 -11
  680. package/src/components/Button/stories/Themes.vue +0 -10
  681. package/src/components/Button/stories/Variants.vue +0 -10
  682. package/src/components/Popover/stories/Hover.vue +0 -14
  683. package/src/components/Resource.vue +0 -21
  684. package/src/components/Sidebar/stories/Example.vue +0 -76
  685. package/src/components/Spinner.vue +0 -3
  686. package/src/components/TabButtons/TabButtons.story.vue +0 -22
  687. package/src/utils/plugin.js +0 -24
  688. /package/src/components/Dropdown/stories/{02_Shortcuts.vue → Shortcuts.vue} +0 -0
  689. /package/src/components/Dropdown/stories/{01_Simple.vue → Simple.vue} +0 -0
  690. /package/src/components/Dropdown/stories/{03_Submenus.vue → Submenus.vue} +0 -0
  691. /package/src/components/Dropdown/stories/{04_Switches.vue → Switches.vue} +0 -0
  692. /package/src/components/{Dropdown/DropdownRenderContentAsChild.vue → Menu/MenuRenderContentAsChild.vue} +0 -0
@@ -0,0 +1,108 @@
1
+ import {
2
+ computed,
3
+ onBeforeUnmount,
4
+ ref,
5
+ watch,
6
+ type ComputedRef,
7
+ } from 'vue'
8
+
9
+ /**
10
+ * Module-level registry connecting an app's shell to its scroll element across
11
+ * the component tree — the shell that owns the scroll region and a routed page
12
+ * that wants to read/drive it aren't ancestor and descendant.
13
+ *
14
+ * A stack (not a single ref) so that a desktop <-> mobile layout swap hands the
15
+ * active container over cleanly regardless of mount/unmount order, mirroring the
16
+ * PageHeader target registry (`components/PageHeader/target.ts`).
17
+ */
18
+ const containers = ref<HTMLElement[]>([])
19
+
20
+ /** The current scroll container — the most recently registered one. */
21
+ export const activeScrollContainer: ComputedRef<HTMLElement | null> = computed(
22
+ () => containers.value[containers.value.length - 1] ?? null,
23
+ )
24
+
25
+ /** Register a shell's scroll element. Called by the shell, not by app code. */
26
+ export function registerScrollContainer(el: HTMLElement) {
27
+ containers.value = [...containers.value, el]
28
+ }
29
+
30
+ /** Remove a previously registered scroll element. */
31
+ export function unregisterScrollContainer(el: HTMLElement) {
32
+ containers.value = containers.value.filter((c) => c !== el)
33
+ }
34
+
35
+ /**
36
+ * Plain (non-hook) accessor for the active scroll container. Usable outside a
37
+ * component `setup()` — e.g. inside a vue-router `scrollBehavior` or navigation
38
+ * guard, where composables can't run. Returns `null` if no shell is mounted.
39
+ */
40
+ export function getScrollContainer(): HTMLElement | null {
41
+ return activeScrollContainer.value
42
+ }
43
+
44
+ /** Scroll the active container. No-op when none is registered. */
45
+ export function scrollTo(options: ScrollToOptions = {}) {
46
+ activeScrollContainer.value?.scrollTo(options)
47
+ }
48
+
49
+ /** Smooth-scroll the active container to the top. No-op when none is registered. */
50
+ export function scrollToTop() {
51
+ activeScrollContainer.value?.scrollTo({ top: 0, behavior: 'smooth' })
52
+ }
53
+
54
+ export interface UseScrollContainerOptions {
55
+ /** Scroll offset (px) past which `isScrolled` flips to true. Default 200. */
56
+ threshold?: number
57
+ }
58
+
59
+ export interface UseScrollContainer {
60
+ /** The live scroll element — read `scrollTop`/`scrollHeight` or call `scrollBy` on it. */
61
+ el: ComputedRef<HTMLElement | null>
62
+ /** Scroll the active container. */
63
+ scrollTo: (options?: ScrollToOptions) => void
64
+ /** Smooth-scroll to the top. */
65
+ scrollToTop: () => void
66
+ /** Reactive: is the container scrolled past `threshold`. */
67
+ isScrolled: ComputedRef<boolean>
68
+ }
69
+
70
+ /**
71
+ * Reactive access to the app shell's scroll container.
72
+ *
73
+ * `isScrolled` tracks the *active* container and re-binds automatically across a
74
+ * layout swap, so it stays correct when desktop and mobile shells hand over.
75
+ */
76
+ export function useScrollContainer(
77
+ options: UseScrollContainerOptions = {},
78
+ ): UseScrollContainer {
79
+ const { threshold = 200 } = options
80
+ const scrollTop = ref(0)
81
+
82
+ const onScroll = () => {
83
+ scrollTop.value = activeScrollContainer.value?.scrollTop ?? 0
84
+ }
85
+
86
+ // Follow the active container as it changes (layout swap): detach from the
87
+ // previous element, attach to the new one, and re-read immediately.
88
+ watch(
89
+ activeScrollContainer,
90
+ (el, prev) => {
91
+ prev?.removeEventListener('scroll', onScroll)
92
+ el?.addEventListener('scroll', onScroll, { passive: true })
93
+ onScroll()
94
+ },
95
+ { immediate: true },
96
+ )
97
+
98
+ onBeforeUnmount(() => {
99
+ activeScrollContainer.value?.removeEventListener('scroll', onScroll)
100
+ })
101
+
102
+ return {
103
+ el: activeScrollContainer,
104
+ scrollTo,
105
+ scrollToTop,
106
+ isScrolled: computed(() => scrollTop.value > threshold),
107
+ }
108
+ }
@@ -190,6 +190,9 @@ function globalKeyupHandler(e: KeyboardEvent) {
190
190
  }
191
191
 
192
192
  function attachGlobalListener() {
193
+ // No-op during SSR — registration still works, listeners attach on the
194
+ // first client-side useShortcut call.
195
+ if (typeof document === 'undefined') return
193
196
  if (listenerAttached) return
194
197
  listenerAttached = true
195
198
  document.addEventListener('keydown', globalKeydownHandler)
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @vitest-environment node
3
+ */
4
+ import { computed, watchSyncEffect } from 'vue'
5
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
6
+ import { docStore } from './docStore'
7
+ import { idbStore } from './idbStore'
8
+
9
+ const DOCTYPE = 'User'
10
+ const identity = <T>(d: T): T => d
11
+
12
+ function idbKey(name: string) {
13
+ return `doc:${DOCTYPE}/${name}`
14
+ }
15
+
16
+ /**
17
+ * Mirror useDoc's `doc` computed: it calls getDoc and dereferences the returned
18
+ * ref *without* optional chaining, so a getDoc that returns undefined throws.
19
+ * A synchronous watcher reproduces the in-browser render effect that re-runs
20
+ * this computed the instant setDoc assigns the ref — the trigger for the crash.
21
+ */
22
+ function subscribeLikeUseDoc(name: string) {
23
+ const doc = computed(() => docStore.getDoc(DOCTYPE, name, identity).value)
24
+ const seen: unknown[] = []
25
+ const stop = watchSyncEffect(() => {
26
+ seen.push(doc.value)
27
+ })
28
+ return { doc, seen, stop }
29
+ }
30
+
31
+ describe('docStore', () => {
32
+ beforeEach(async () => {
33
+ await docStore.clearAll()
34
+ })
35
+ afterEach(async () => {
36
+ await docStore.clearAll()
37
+ })
38
+
39
+ it('first uncached load: setDoc does not crash a synchronously-tracked reader and keeps the IDB copy', async () => {
40
+ const name = 'user1'
41
+ const { doc, stop } = subscribeLikeUseDoc(name)
42
+ // Nothing cached yet.
43
+ expect(doc.value).toBe(null)
44
+
45
+ const record = { doctype: DOCTYPE, name, email: 'user1@example.com' }
46
+ // Regression: before the fix this rejects. Assigning docRef.value synchronously
47
+ // re-enters getDoc, which (lastFetched unset) saw the entry as stale, evicted
48
+ // the ref it had just created, and the reader dereferenced undefined.
49
+ await expect(docStore.setDoc({ ...record })).resolves.toBeUndefined()
50
+
51
+ expect(doc.value).toMatchObject(record)
52
+ // The IDB copy setDoc wrote must survive — no spurious stale-reload eviction.
53
+ expect(await idbStore.get(idbKey(name))).toMatchObject(record)
54
+ stop()
55
+ })
56
+
57
+ it('stale access: getDoc never returns undefined (no self-eviction under its caller)', async () => {
58
+ const name = 'user2'
59
+ const record = { doctype: DOCTYPE, name }
60
+ await docStore.setDoc({ ...record })
61
+
62
+ // Force the entry past the cache timeout, as a long-lived useDoc would be.
63
+ const key = `${DOCTYPE}/${name}`
64
+ ;(
65
+ docStore as unknown as { lastFetched: Map<string, number> }
66
+ ).lastFetched.set(key, Date.now() - 10 * 60 * 1000)
67
+
68
+ const ref = docStore.getDoc(DOCTYPE, name, identity, { staleOnError: true })
69
+ expect(ref).toBeDefined()
70
+ expect(() => ref.value).not.toThrow()
71
+ expect(ref.value).toMatchObject(record)
72
+ expect(await idbStore.get(idbKey(name))).toMatchObject(record)
73
+ })
74
+ })
@@ -38,11 +38,15 @@ class DocStore {
38
38
  if (!this.docs.has(key)) {
39
39
  this.docs.set(key, ref(null))
40
40
  }
41
+ // Mark fresh BEFORE assigning the ref. Assigning docRef.value synchronously
42
+ // re-runs any computed reading this doc (e.g. useDoc's `doc`), which calls
43
+ // getDoc again — if the entry still looked stale at that point it would kick
44
+ // off a needless reload that evicts the IDB copy we just wrote.
45
+ this.lastFetched.set(key, Date.now())
41
46
  const docRef = this.docs.get(key)
42
47
  if (docRef) {
43
48
  docRef.value = doc
44
49
  }
45
- this.lastFetched.set(key, Date.now())
46
50
  } catch (error) {
47
51
  console.error('Failed to set doc in IDB:', error)
48
52
  throw error
@@ -52,9 +56,10 @@ class DocStore {
52
56
  getDoc(
53
57
  doctype: string,
54
58
  name: MaybeRefOrGetter<string>,
55
- transform: (doc: Doc) => Doc,
59
+ transform?: (doc: Doc) => Doc,
60
+ options: { staleOnError?: boolean } = {},
56
61
  ): Ref<Doc | null> {
57
- const nameStr = toValue(name)
62
+ const nameStr = toValue(name)?.trim()
58
63
  if (!doctype || !nameStr) {
59
64
  throw new Error('doctype and name are required')
60
65
  }
@@ -62,9 +67,9 @@ class DocStore {
62
67
 
63
68
  if (!this.docs.has(key)) {
64
69
  this.docs.set(key, ref(null))
65
- this.loadDoc(key, true, transform)
70
+ this.loadDoc(key, true, transform, options)
66
71
  } else if (this.isStale(key)) {
67
- this.loadDoc(key, false, transform)
72
+ this.loadDoc(key, false, transform, options)
68
73
  }
69
74
 
70
75
  return this.docs.get(key)!
@@ -73,11 +78,17 @@ class DocStore {
73
78
  private async loadDoc(
74
79
  key: DocKey,
75
80
  isFirstLoad: boolean,
76
- transform: (doc: Doc) => Doc,
81
+ transform?: (doc: Doc) => Doc,
82
+ options: { staleOnError?: boolean } = {},
77
83
  ) {
78
84
  try {
79
85
  if (!isFirstLoad && this.isStale(key)) {
80
- await this.cleanup(key)
86
+ this.lastFetched.delete(key)
87
+ if (!options.staleOnError) {
88
+ // Keep the IDB copy only when callers explicitly opt into stale
89
+ // read-only fallback, such as offline-capable routes.
90
+ await idbStore.delete(this.storePrefix + key)
91
+ }
81
92
  }
82
93
 
83
94
  const idbDoc = (await idbStore.get(this.storePrefix + key)) as Doc | null
@@ -11,6 +11,7 @@ export interface UseCallOptions<
11
11
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
12
12
  params?: TParams | (() => TParams)
13
13
  cacheKey?: CacheKey
14
+ staleOnError?: boolean
14
15
  immediate?: boolean
15
16
  refetch?: boolean
16
17
  baseUrl?: string
@@ -207,4 +207,47 @@ describe('useCall', () => {
207
207
  await waitUntilValueChanges(() => secondCall.data)
208
208
  expect(secondCall.data).toBe('pong')
209
209
  })
210
+
211
+ it('keeps cached data visible when a refetch fails', async () => {
212
+ const cacheKey = 'offline-ping'
213
+ const call = useCall({
214
+ url: url('/api/v2/method/ping'),
215
+ cacheKey,
216
+ })
217
+
218
+ await waitUntilValueChanges(() => call.data)
219
+ expect(call.data).toBe('pong')
220
+
221
+ const offlineCall = useCall({
222
+ url: url('/api/v2/method/network-error'),
223
+ cacheKey,
224
+ staleOnError: true,
225
+ })
226
+
227
+ await waitUntilValueChanges(() => offlineCall.data)
228
+ await waitUntilValueChanges(() => offlineCall.loading)
229
+
230
+ expect(offlineCall.error).toBeTruthy()
231
+ expect(offlineCall.data).toBe('pong')
232
+ })
233
+
234
+ it('does not parse empty network failures as Frappe errors', async () => {
235
+ const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
236
+ const networkCall = useCall({
237
+ url: url('/api/v2/method/network-error'),
238
+ immediate: false,
239
+ })
240
+
241
+ networkCall.fetch()
242
+ await networkCall.promise.catch(() => {})
243
+ await waitUntilValueChanges(() => networkCall.loading)
244
+
245
+ expect(networkCall.error).toBeTruthy()
246
+ expect(logSpy).not.toHaveBeenCalledWith(
247
+ 'Error parsing error response:',
248
+ expect.anything(),
249
+ )
250
+
251
+ logSpy.mockRestore()
252
+ })
210
253
  })
@@ -1,6 +1,6 @@
1
1
  import { computed, reactive, readonly, ref, unref } from 'vue'
2
2
  import { AfterFetchContext, UseFetchOptions } from '@vueuse/core'
3
- import { useFrappeFetch } from '../useFrappeFetch'
3
+ import { FrappeResponseError, useFrappeFetch } from '../useFrappeFetch'
4
4
  import { unrefObject, makeGetParams, normalizeCacheKey } from '../utils'
5
5
  import { idbStore } from '../idbStore'
6
6
  import { BasicParams, UseCallOptions } from './types'
@@ -17,6 +17,7 @@ export function useCall<TResponse, TParams extends BasicParams = undefined>(
17
17
  baseUrl = '',
18
18
  initialData,
19
19
  cacheKey,
20
+ staleOnError = false,
20
21
  transform,
21
22
  beforeSubmit,
22
23
  onSuccess,
@@ -177,7 +178,15 @@ export function useCall<TResponse, TParams extends BasicParams = undefined>(
177
178
  let cachedResponse = ref<TResponse | null>(null)
178
179
 
179
180
  const _data = computed(() => {
180
- if (normalizedCacheKey && (out.loading || !out.isFinished)) {
181
+ if (
182
+ normalizedCacheKey &&
183
+ cachedResponse.value != null &&
184
+ canUseCachedFallback(error.value, staleOnError) &&
185
+ (out.loading ||
186
+ !out.isFinished ||
187
+ data.value?.data == null ||
188
+ error.value)
189
+ ) {
181
190
  let cachedData = cachedResponse.value as TResponse
182
191
  if (transform) {
183
192
  let returnValue = transform(cachedData)
@@ -219,7 +228,11 @@ export function useCall<TResponse, TParams extends BasicParams = undefined>(
219
228
 
220
229
  return out as Omit<typeof out, 'submit'> & {
221
230
  submit: TParams extends undefined
222
- ? (params?: never) => Promise<void>
223
- : (params: TParams) => Promise<void>
231
+ ? (params?: never) => Promise<TResponse | null>
232
+ : (params?: TParams) => Promise<TResponse | null>
224
233
  }
225
234
  }
235
+
236
+ function canUseCachedFallback(error: unknown, staleOnError: boolean) {
237
+ return !error || (staleOnError && !(error instanceof FrappeResponseError))
238
+ }
@@ -4,6 +4,7 @@
4
4
  import { ref } from 'vue'
5
5
  import { baseUrl, waitUntilValueChanges } from '../../mocks/utils'
6
6
  import { useCall, useDoc } from '../index'
7
+ import { docStore } from '../docStore'
7
8
 
8
9
  describe('useDoc', () => {
9
10
  it('it returns expected object', async () => {
@@ -104,7 +105,7 @@ describe('useDoc', () => {
104
105
  await waitUntilValueChanges(() => user.doc)
105
106
 
106
107
  // Initial email value
107
- expect(user.doc.email).toBe('user1@example.com')
108
+ expect(user.doc!.email).toBe('user1@example.com')
108
109
 
109
110
  // Update email
110
111
  const newEmail = 'updated@example.com'
@@ -114,6 +115,64 @@ describe('useDoc', () => {
114
115
  await waitUntilValueChanges(() => user.doc)
115
116
 
116
117
  // Verify that the doc was updated
117
- expect(user.doc.email).toBe(newEmail)
118
+ expect(user.doc!.email).toBe(newEmail)
119
+ })
120
+
121
+ it('does not bind or fetch while the name is empty', async () => {
122
+ interface User {
123
+ name: string
124
+ email: string
125
+ }
126
+
127
+ const user = useDoc<User>({
128
+ baseUrl,
129
+ doctype: 'User',
130
+ name: '',
131
+ })
132
+
133
+ // No name → nothing to bind and no request should fire.
134
+ expect(user.doc).toBe(null)
135
+ expect(user.loading).toBe(false)
136
+ })
137
+
138
+ it('binds to the doc once a late-resolving name is set', async () => {
139
+ interface User {
140
+ name: string
141
+ email: string
142
+ first_name: string
143
+ last_name: string
144
+ }
145
+
146
+ // Name is empty at setup (e.g. bound to a doc that is still loading).
147
+ const name = ref('')
148
+ const user = useDoc<User>({
149
+ baseUrl,
150
+ doctype: 'User',
151
+ name,
152
+ })
153
+
154
+ // Nothing fetched or bound yet.
155
+ expect(user.doc).toBe(null)
156
+ expect(user.loading).toBe(false)
157
+
158
+ // Name resolves after setup — the doc ref should re-point to the real slot.
159
+ name.value = 'user1'
160
+
161
+ await waitUntilValueChanges(() => user.loading, true)
162
+ await waitUntilValueChanges(() => user.loading, false)
163
+
164
+ expect(user.doc).toStrictEqual({
165
+ doctype: 'User',
166
+ name: 'user1',
167
+ email: 'user1@example.com',
168
+ first_name: 'User',
169
+ last_name: '1',
170
+ })
171
+ })
172
+
173
+ it('throws when getDoc is called with a whitespace-only name', () => {
174
+ expect(() => docStore.getDoc('User', ' ', (d) => d)).toThrow(
175
+ 'doctype and name are required',
176
+ )
118
177
  })
119
178
  })
@@ -24,8 +24,10 @@ type TransformMethods<T> = {
24
24
  : never
25
25
  }
26
26
 
27
- interface DocMethodOption<T = any>
28
- extends Omit<UseCallOptions<T>, 'url' | 'baseUrl'> {
27
+ interface DocMethodOption<T = any> extends Omit<
28
+ UseCallOptions<T>,
29
+ 'url' | 'baseUrl'
30
+ > {
29
31
  name: string
30
32
  }
31
33
 
@@ -36,6 +38,7 @@ interface UseDocOptions<TDoc> {
36
38
  url?: string
37
39
  methods?: Record<string, string | DocMethodOption>
38
40
  immediate?: boolean
41
+ staleOnError?: boolean
39
42
  transform?: (doc: TDoc & { doctype: string }) => TDoc & { doctype: string }
40
43
  }
41
44
 
@@ -49,6 +52,7 @@ export function useDoc<TDoc extends { name: string }, TMethods = {}>(
49
52
  url: customUrl = '',
50
53
  methods = {},
51
54
  immediate = true,
55
+ staleOnError = false,
52
56
  transform,
53
57
  } = options
54
58
 
@@ -72,7 +76,10 @@ export function useDoc<TDoc extends { name: string }, TMethods = {}>(
72
76
  }
73
77
 
74
78
  const fetchOptions: UseFetchOptions = {
75
- immediate,
79
+ // Don't fire the initial GET while the name is still unresolved (it would
80
+ // hit the malformed `/api/v2/document/<doctype>/` URL). refetch:true makes
81
+ // the request fire automatically once the name resolves and the URL changes.
82
+ immediate: immediate && Boolean(toValue(name)?.trim()),
76
83
  refetch: true,
77
84
  afterFetch(ctx: AfterFetchContext<{ data: TDoc }>) {
78
85
  if (ctx.data) {
@@ -130,11 +137,10 @@ export function useDoc<TDoc extends { name: string }, TMethods = {}>(
130
137
  immediate: false,
131
138
  refetch: false,
132
139
  onSuccess(data) {
133
- let docWithType = { ...data, doctype }
134
- if (transform) {
135
- docWithType = transform(docWithType)
136
- }
137
- docStore.setDoc(docWithType)
140
+ // Store the untransformed doc; the `doc` computed applies `transform` on
141
+ // read. Transforming here too would run it twice (a bug for any
142
+ // non-idempotent transform). Mirrors afterFetch.
143
+ docStore.setDoc({ ...data, doctype })
138
144
  listStore.updateRow(doctype, data)
139
145
  },
140
146
  })
@@ -152,14 +158,28 @@ export function useDoc<TDoc extends { name: string }, TMethods = {}>(
152
158
  },
153
159
  })
154
160
 
155
- let doc = docStore.getDoc(doctype, name, transform) as Ref<TDoc | null>
156
- if (doc.value && transform) {
157
- try {
158
- doc.value = transform(doc.value)
159
- } catch (e) {
160
- docStore.removeDoc(doctype, toValue(name))
161
+ // Bind reactively to the document keyed by the *current* name. Resolving the
162
+ // name once at setup would statically bind to whatever it was then — if the
163
+ // name resolves after setup (e.g. while the GET is still in flight) the ref
164
+ // would never re-point to the real cache slot. A computed re-evaluates when
165
+ // the name resolves and when the store ref is populated.
166
+ const doc = computed<TDoc | null>(() => {
167
+ const nameStr = toValue(name)?.trim()
168
+ if (!nameStr) return null
169
+ const storeRef = docStore.getDoc(doctype, nameStr, transform as any, {
170
+ staleOnError,
171
+ }) as Ref<TDoc | null>
172
+ let value = storeRef.value
173
+ if (value && transform) {
174
+ try {
175
+ value = transform(value as TDoc & { doctype: string })
176
+ } catch (e) {
177
+ docStore.removeDoc(doctype, nameStr)
178
+ return null
179
+ }
161
180
  }
162
- }
181
+ return value
182
+ })
163
183
  let out = reactive({
164
184
  doc,
165
185
  error,
@@ -7,7 +7,7 @@ interface UseDoctypeOptions {
7
7
  baseUrl?: string
8
8
  }
9
9
 
10
- export function useDoctype<T>(
10
+ export function useDoctype<T extends Record<string, any>>(
11
11
  doctype: string,
12
12
  options: UseDoctypeOptions = {},
13
13
  ) {
@@ -37,7 +37,8 @@ function useInsert<T>(doctype: string, options: UseDoctypeOptions = {}) {
37
37
 
38
38
  return {
39
39
  ...out,
40
- submit: (params: Partial<T>) => out.submit(params).then(() => out.data),
40
+ submit: (params: Partial<T>) =>
41
+ out.submit(params as any).then(() => out.data),
41
42
  }
42
43
  }
43
44
 
@@ -109,7 +110,7 @@ function useRunDocMethod(doctype: string, options: UseDoctypeOptions = {}) {
109
110
  validateError.value = null
110
111
  }
111
112
  }
112
- return runDocMethod.submit(params)
113
+ return runDocMethod.submit(params as any)
113
114
  },
114
115
  isLoading: (name: string, method: string) => {
115
116
  return (
@@ -158,7 +159,7 @@ function useRunMethod(doctype: string, options: UseDoctypeOptions = {}) {
158
159
  validateError.value = null
159
160
  }
160
161
  }
161
- return runMethod.submit(params)
162
+ return runMethod.submit(params as any)
162
163
  },
163
164
  isLoading: (method: string) => {
164
165
  return (
@@ -190,7 +191,7 @@ function useSetValue<T>(doctype: string, options: UseDoctypeOptions = {}) {
190
191
  ...setValue,
191
192
  submit: ({ name, ...values }: SetValueParams) => {
192
193
  url.value = `/api/v2/document/${doctype}/${name}`
193
- return setValue.submit({ name, ...values })
194
+ return setValue.submit({ name, ...values } as any)
194
195
  },
195
196
  }
196
197
  }
@@ -25,8 +25,13 @@ export class FrappeResponseError extends Error {
25
25
  this.indicator = options.indicator
26
26
 
27
27
  // Maintains proper stack trace for where our error was thrown (only available on V8)
28
- if (Error.captureStackTrace) {
29
- Error.captureStackTrace(this, FrappeResponseError)
28
+ const captureStackTrace = (
29
+ Error as ErrorConstructor & {
30
+ captureStackTrace?: (target: object, constructor?: Function) => void
31
+ }
32
+ ).captureStackTrace
33
+ if (captureStackTrace) {
34
+ captureStackTrace(this, FrappeResponseError)
30
35
  }
31
36
  }
32
37
  }
@@ -77,9 +82,17 @@ export const useFrappeFetch = createFetch({
77
82
  indicator: string
78
83
  }
79
84
  try {
85
+ if (!ctx.data) {
86
+ return ctx
87
+ }
88
+
80
89
  let errorResponse = JSON.parse(ctx.data)
81
- let errors: Array<FrappeError> = errorResponse.errors
82
- let error = errors[0] // assuming only one error for now
90
+ let errors: Array<FrappeError> | undefined = errorResponse.errors
91
+ let error = errors?.[0] // assuming only one error for now
92
+ if (!error) {
93
+ return ctx
94
+ }
95
+
83
96
  let errorDescription = error.message
84
97
  ? `: ${error.message}`
85
98
  : error.exception
@@ -34,6 +34,7 @@ export interface UseListOptions<T> {
34
34
  parent?: string
35
35
  debug?: boolean
36
36
  cacheKey?: CacheKey
37
+ staleOnError?: boolean
37
38
  initialData?: T[]
38
39
  immediate?: boolean
39
40
  refetch?: boolean
@@ -212,4 +212,44 @@ describe('useList', () => {
212
212
  { name: 'User2', email: 'user2@example.com' },
213
213
  ])
214
214
  })
215
+
216
+ it('keeps cached data visible when a refetch fails', async () => {
217
+ interface User {
218
+ name: string
219
+ email: string
220
+ }
221
+
222
+ const cacheKey = 'offline-users'
223
+ const cachedUsers = useList<User>({
224
+ baseUrl,
225
+ doctype: 'User',
226
+ fields: ['name', 'email'],
227
+ cacheKey,
228
+ limit: 2,
229
+ })
230
+
231
+ await waitUntilValueChanges(() => cachedUsers.data)
232
+ expect(cachedUsers.data).toStrictEqual([
233
+ { name: 'User1', email: 'user1@example.com' },
234
+ { name: 'User2', email: 'user2@example.com' },
235
+ ])
236
+
237
+ const offlineUsers = useList<User>({
238
+ baseUrl,
239
+ doctype: 'InvalidDoctype',
240
+ fields: ['name', 'email'],
241
+ cacheKey,
242
+ staleOnError: true,
243
+ limit: 2,
244
+ })
245
+
246
+ await waitUntilValueChanges(() => offlineUsers.data)
247
+ await waitUntilValueChanges(() => offlineUsers.loading)
248
+
249
+ expect(offlineUsers.error).toBeTruthy()
250
+ expect(offlineUsers.data).toStrictEqual([
251
+ { name: 'User1', email: 'user1@example.com' },
252
+ { name: 'User2', email: 'user2@example.com' },
253
+ ])
254
+ })
215
255
  })