@multiplatform.one/frappe-ui 7.7.6 → 7.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (491) hide show
  1. package/dist/cjs/ChartCard.native.js.map +1 -1
  2. package/dist/cjs/FileBrowser/AttachLibrary.native.js.map +1 -1
  3. package/dist/cjs/FileBrowser/folderTree.native.js.map +1 -1
  4. package/dist/cjs/FileBrowser/index.native.js.map +1 -1
  5. package/dist/cjs/FileBrowser/types.native.js.map +1 -1
  6. package/dist/cjs/FileBrowser/viewPersistence.native.js.map +1 -1
  7. package/dist/cjs/FormSidebar.native.js.map +1 -1
  8. package/dist/cjs/FrappeAssignment.native.js.map +1 -1
  9. package/dist/cjs/FrappeCalendar.cjs +20 -17
  10. package/dist/cjs/FrappeCalendar.native.js +22 -21
  11. package/dist/cjs/FrappeCalendar.native.js.map +1 -1
  12. package/dist/cjs/FrappeChartCard.native.js.map +1 -1
  13. package/dist/cjs/FrappeConnectionIndicator.native.js.map +1 -1
  14. package/dist/cjs/FrappeDashboard.native.js.map +1 -1
  15. package/dist/cjs/FrappeForm.cjs +1 -1
  16. package/dist/cjs/FrappeForm.native.js +2 -2
  17. package/dist/cjs/FrappeForm.native.js.map +1 -1
  18. package/dist/cjs/FrappeGantt.cjs +25 -34
  19. package/dist/cjs/FrappeGantt.native.js +27 -38
  20. package/dist/cjs/FrappeGantt.native.js.map +1 -1
  21. package/dist/cjs/FrappeImageView.cjs +4 -2
  22. package/dist/cjs/FrappeImageView.native.js +8 -4
  23. package/dist/cjs/FrappeImageView.native.js.map +1 -1
  24. package/dist/cjs/FrappeKPICard.cjs +26 -1
  25. package/dist/cjs/FrappeKPICard.native.js +26 -1
  26. package/dist/cjs/FrappeKPICard.native.js.map +1 -1
  27. package/dist/cjs/FrappeKanban.cjs +16 -17
  28. package/dist/cjs/FrappeKanban.native.js +18 -21
  29. package/dist/cjs/FrappeKanban.native.js.map +1 -1
  30. package/dist/cjs/FrappeLikedBy.cjs +1 -1
  31. package/dist/cjs/FrappeLikedBy.native.js +1 -1
  32. package/dist/cjs/FrappeLikedBy.native.js.map +1 -1
  33. package/dist/cjs/FrappeList.cjs +2 -2
  34. package/dist/cjs/FrappeList.native.js +2 -2
  35. package/dist/cjs/FrappeList.native.js.map +1 -1
  36. package/dist/cjs/FrappeListView.native.js.map +1 -1
  37. package/dist/cjs/FrappeProvider.native.js.map +1 -1
  38. package/dist/cjs/FrappeReconnectIndicator.native.js.map +1 -1
  39. package/dist/cjs/FrappeReportBuilder.cjs +2 -2
  40. package/dist/cjs/FrappeReportBuilder.native.js +2 -2
  41. package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
  42. package/dist/cjs/FrappeReportView.native.js +3 -3
  43. package/dist/cjs/FrappeReportView.native.js.map +1 -1
  44. package/dist/cjs/FrappeTable.cjs +35 -38
  45. package/dist/cjs/FrappeTable.native.js +38 -39
  46. package/dist/cjs/FrappeTable.native.js.map +1 -1
  47. package/dist/cjs/FrappeTreeView.cjs +85 -50
  48. package/dist/cjs/FrappeTreeView.native.js +129 -50
  49. package/dist/cjs/FrappeTreeView.native.js.map +1 -1
  50. package/dist/cjs/SyncErrorNotice.native.js.map +1 -1
  51. package/dist/cjs/commentActions.cjs +1 -1
  52. package/dist/cjs/commentActions.native.js +1 -1
  53. package/dist/cjs/commentActions.native.js.map +1 -1
  54. package/dist/cjs/components/attachments.native.js.map +1 -1
  55. package/dist/cjs/components/tags.native.js.map +1 -1
  56. package/dist/cjs/components/timeline.cjs +1 -1
  57. package/dist/cjs/components/timeline.native.js +1 -1
  58. package/dist/cjs/components/timeline.native.js.map +1 -1
  59. package/dist/cjs/dependsOn.native.js.map +1 -1
  60. package/dist/cjs/desk/DeskFormPage.native.js.map +1 -1
  61. package/dist/cjs/desk/DeskFormSidebar.native.js.map +1 -1
  62. package/dist/cjs/desk/DeskListPage.native.js.map +1 -1
  63. package/dist/cjs/desk/DeskShell.cjs +2 -0
  64. package/dist/cjs/desk/DeskShell.native.js +2 -0
  65. package/dist/cjs/desk/DeskShell.native.js.map +1 -1
  66. package/dist/cjs/desk/DeskSidebar.native.js.map +1 -1
  67. package/dist/cjs/desk/awesomebar.cjs +1 -1
  68. package/dist/cjs/desk/awesomebar.native.js +1 -1
  69. package/dist/cjs/desk/awesomebar.native.js.map +1 -1
  70. package/dist/cjs/fieldOverrides.native.js.map +1 -1
  71. package/dist/cjs/fieldRegistry.native.js +31 -31
  72. package/dist/cjs/fieldRegistry.native.js.map +1 -1
  73. package/dist/cjs/fields/Barcode/index.native.js.map +1 -1
  74. package/dist/cjs/fields/Check/index.native.js.map +1 -1
  75. package/dist/cjs/fields/CodeEditor/index.native.js.map +1 -1
  76. package/dist/cjs/fields/Color/index.native.js.map +1 -1
  77. package/dist/cjs/fields/Data/index.native.js.map +1 -1
  78. package/dist/cjs/fields/Date/sheetKeyboard.native.js.map +1 -1
  79. package/dist/cjs/fields/Date/types.native.js.map +1 -1
  80. package/dist/cjs/fields/Date/wire.native.js.map +1 -1
  81. package/dist/cjs/fields/DateRange/index.native.js.map +1 -1
  82. package/dist/cjs/fields/Duration/index.native.js.map +1 -1
  83. package/dist/cjs/fields/DynamicLink.native.js.map +1 -1
  84. package/dist/cjs/fields/FileUpload.native.js.map +1 -1
  85. package/dist/cjs/fields/FileUploadField/index.native.js.map +1 -1
  86. package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
  87. package/dist/cjs/fields/HTML/index.native.js.map +1 -1
  88. package/dist/cjs/fields/Heading/index.native.js.map +1 -1
  89. package/dist/cjs/fields/Icon/index.native.js.map +1 -1
  90. package/dist/cjs/fields/Link.native.js.map +1 -1
  91. package/dist/cjs/fields/LinkTree.native.js.map +1 -1
  92. package/dist/cjs/fields/LongText/index.native.js.map +1 -1
  93. package/dist/cjs/fields/MentionInput/index.cjs +4 -1
  94. package/dist/cjs/fields/MentionInput/index.native.js +4 -1
  95. package/dist/cjs/fields/MentionInput/index.native.js.map +1 -1
  96. package/dist/cjs/fields/Numeric/index.native.js.map +1 -1
  97. package/dist/cjs/fields/OTPInput/index.native.js.map +1 -1
  98. package/dist/cjs/fields/Password/index.native.js.map +1 -1
  99. package/dist/cjs/fields/Phone/index.native.js.map +1 -1
  100. package/dist/cjs/fields/Progress/index.native.js.map +1 -1
  101. package/dist/cjs/fields/RadioGroup/index.native.js.map +1 -1
  102. package/dist/cjs/fields/Rating/index.native.js.map +1 -1
  103. package/dist/cjs/fields/ReadOnly/index.native.js.map +1 -1
  104. package/dist/cjs/fields/Select/index.native.js.map +1 -1
  105. package/dist/cjs/fields/Signature/index.native.js.map +1 -1
  106. package/dist/cjs/fields/Slider/index.native.js.map +1 -1
  107. package/dist/cjs/fields/SmallText/index.native.js.map +1 -1
  108. package/dist/cjs/fields/Switch/index.native.js.map +1 -1
  109. package/dist/cjs/fields/TableMultiSelect.native.js.map +1 -1
  110. package/dist/cjs/fields/TextEditor/types.native.js.map +1 -1
  111. package/dist/cjs/fields/TimePicker/index.native.js.map +1 -1
  112. package/dist/cjs/fields/ToggleGroup/index.native.js.map +1 -1
  113. package/dist/cjs/fields/shared/EditorPreviewPanes.native.js.map +1 -1
  114. package/dist/cjs/filterFields.native.js.map +1 -1
  115. package/dist/cjs/formEngine/createPrefill.native.js.map +1 -1
  116. package/dist/cjs/formEngine/dirtyTracking.native.js.map +1 -1
  117. package/dist/cjs/formEngine/doctypeMeta.native.js.map +1 -1
  118. package/dist/cjs/formEngine/fetchFrom.native.js.map +1 -1
  119. package/dist/cjs/formEngine/fieldPermissions.native.js.map +1 -1
  120. package/dist/cjs/formEngine/formActions.native.js.map +1 -1
  121. package/dist/cjs/formEngine/formLayout.native.js.map +1 -1
  122. package/dist/cjs/formEngine/index.native.js.map +1 -1
  123. package/dist/cjs/formEngine/useFormEngine.native.js.map +1 -1
  124. package/dist/cjs/frappeEventColor.native.js.map +1 -1
  125. package/dist/cjs/frappeStoryTarget.native.js.map +1 -1
  126. package/dist/cjs/frappeUsers.native.js.map +1 -1
  127. package/dist/cjs/index.cjs +4 -0
  128. package/dist/cjs/index.native.js +38 -34
  129. package/dist/cjs/index.native.js.map +1 -1
  130. package/dist/cjs/liveErrors.native.js.map +1 -1
  131. package/dist/cjs/resolveControl.native.js.map +1 -1
  132. package/dist/cjs/schemaForm/SchemaWizard.native.js.map +1 -1
  133. package/dist/cjs/schemaForm/applyFieldOrder.native.js.map +1 -1
  134. package/dist/cjs/schemaForm/index.cjs +10 -9
  135. package/dist/cjs/schemaForm/index.native.js +11 -10
  136. package/dist/cjs/schemaForm/index.native.js.map +1 -1
  137. package/dist/cjs/shared/devWarn.cjs +40 -0
  138. package/dist/cjs/shared/devWarn.native.js +45 -0
  139. package/dist/cjs/shared/devWarn.native.js.map +1 -0
  140. package/dist/cjs/shared/useKeyboardInset.native.js.map +1 -1
  141. package/dist/cjs/syncError.native.js.map +1 -1
  142. package/dist/cjs/useFrappeAggregate.cjs +15 -8
  143. package/dist/cjs/useFrappeAggregate.native.js +15 -9
  144. package/dist/cjs/useFrappeAggregate.native.js.map +1 -1
  145. package/dist/cjs/useFrappeFacets.cjs +10 -9
  146. package/dist/cjs/useFrappeFacets.native.js +10 -10
  147. package/dist/cjs/useFrappeFacets.native.js.map +1 -1
  148. package/dist/cjs/useFrappePagination.native.js.map +1 -1
  149. package/dist/cjs/useFrappeRows.cjs +113 -0
  150. package/dist/cjs/useFrappeRows.native.js +129 -0
  151. package/dist/cjs/useFrappeRows.native.js.map +1 -0
  152. package/dist/esm/ChartCard.native.js.map +1 -1
  153. package/dist/esm/FileBrowser/AttachLibrary.native.js.map +1 -1
  154. package/dist/esm/FileBrowser/folderTree.native.js.map +1 -1
  155. package/dist/esm/FileBrowser/index.native.js.map +1 -1
  156. package/dist/esm/FileBrowser/types.native.js.map +1 -1
  157. package/dist/esm/FileBrowser/viewPersistence.native.js.map +1 -1
  158. package/dist/esm/FormSidebar.native.js.map +1 -1
  159. package/dist/esm/FrappeAssignment.native.js.map +1 -1
  160. package/dist/esm/FrappeCalendar.mjs +20 -17
  161. package/dist/esm/FrappeCalendar.mjs.map +1 -1
  162. package/dist/esm/FrappeCalendar.native.js +22 -21
  163. package/dist/esm/FrappeCalendar.native.js.map +1 -1
  164. package/dist/esm/FrappeChartCard.native.js.map +1 -1
  165. package/dist/esm/FrappeConnectionIndicator.native.js.map +1 -1
  166. package/dist/esm/FrappeDashboard.native.js.map +1 -1
  167. package/dist/esm/FrappeForm.mjs +1 -1
  168. package/dist/esm/FrappeForm.mjs.map +1 -1
  169. package/dist/esm/FrappeForm.native.js +1 -1
  170. package/dist/esm/FrappeForm.native.js.map +1 -1
  171. package/dist/esm/FrappeGantt.mjs +27 -36
  172. package/dist/esm/FrappeGantt.mjs.map +1 -1
  173. package/dist/esm/FrappeGantt.native.js +29 -40
  174. package/dist/esm/FrappeGantt.native.js.map +1 -1
  175. package/dist/esm/FrappeImageView.mjs +5 -3
  176. package/dist/esm/FrappeImageView.mjs.map +1 -1
  177. package/dist/esm/FrappeImageView.native.js +9 -5
  178. package/dist/esm/FrappeImageView.native.js.map +1 -1
  179. package/dist/esm/FrappeKPICard.mjs +27 -2
  180. package/dist/esm/FrappeKPICard.mjs.map +1 -1
  181. package/dist/esm/FrappeKPICard.native.js +27 -2
  182. package/dist/esm/FrappeKPICard.native.js.map +1 -1
  183. package/dist/esm/FrappeKanban.mjs +16 -17
  184. package/dist/esm/FrappeKanban.mjs.map +1 -1
  185. package/dist/esm/FrappeKanban.native.js +18 -21
  186. package/dist/esm/FrappeKanban.native.js.map +1 -1
  187. package/dist/esm/FrappeLikedBy.mjs +1 -1
  188. package/dist/esm/FrappeLikedBy.mjs.map +1 -1
  189. package/dist/esm/FrappeLikedBy.native.js +1 -1
  190. package/dist/esm/FrappeLikedBy.native.js.map +1 -1
  191. package/dist/esm/FrappeList.mjs +2 -2
  192. package/dist/esm/FrappeList.mjs.map +1 -1
  193. package/dist/esm/FrappeList.native.js +2 -2
  194. package/dist/esm/FrappeList.native.js.map +1 -1
  195. package/dist/esm/FrappeListView.native.js.map +1 -1
  196. package/dist/esm/FrappeProvider.mjs.map +1 -1
  197. package/dist/esm/FrappeProvider.native.js.map +1 -1
  198. package/dist/esm/FrappeReconnectIndicator.native.js.map +1 -1
  199. package/dist/esm/FrappeReportBuilder.mjs +2 -2
  200. package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
  201. package/dist/esm/FrappeReportBuilder.native.js +2 -2
  202. package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
  203. package/dist/esm/FrappeReportView.native.js.map +1 -1
  204. package/dist/esm/FrappeTable.mjs +36 -39
  205. package/dist/esm/FrappeTable.mjs.map +1 -1
  206. package/dist/esm/FrappeTable.native.js +39 -40
  207. package/dist/esm/FrappeTable.native.js.map +1 -1
  208. package/dist/esm/FrappeTreeView.mjs +89 -55
  209. package/dist/esm/FrappeTreeView.mjs.map +1 -1
  210. package/dist/esm/FrappeTreeView.native.js +133 -55
  211. package/dist/esm/FrappeTreeView.native.js.map +1 -1
  212. package/dist/esm/SyncErrorNotice.native.js.map +1 -1
  213. package/dist/esm/commentActions.mjs +1 -1
  214. package/dist/esm/commentActions.mjs.map +1 -1
  215. package/dist/esm/commentActions.native.js +1 -1
  216. package/dist/esm/commentActions.native.js.map +1 -1
  217. package/dist/esm/components/attachments.native.js.map +1 -1
  218. package/dist/esm/components/tags.native.js.map +1 -1
  219. package/dist/esm/components/timeline.mjs +1 -1
  220. package/dist/esm/components/timeline.mjs.map +1 -1
  221. package/dist/esm/components/timeline.native.js +1 -1
  222. package/dist/esm/components/timeline.native.js.map +1 -1
  223. package/dist/esm/dependsOn.native.js.map +1 -1
  224. package/dist/esm/desk/DeskFormPage.native.js.map +1 -1
  225. package/dist/esm/desk/DeskFormSidebar.native.js.map +1 -1
  226. package/dist/esm/desk/DeskListPage.native.js.map +1 -1
  227. package/dist/esm/desk/DeskShell.mjs +2 -0
  228. package/dist/esm/desk/DeskShell.mjs.map +1 -1
  229. package/dist/esm/desk/DeskShell.native.js +2 -0
  230. package/dist/esm/desk/DeskShell.native.js.map +1 -1
  231. package/dist/esm/desk/DeskSidebar.native.js.map +1 -1
  232. package/dist/esm/desk/awesomebar.mjs +1 -1
  233. package/dist/esm/desk/awesomebar.mjs.map +1 -1
  234. package/dist/esm/desk/awesomebar.native.js +1 -1
  235. package/dist/esm/desk/awesomebar.native.js.map +1 -1
  236. package/dist/esm/fieldOverrides.native.js.map +1 -1
  237. package/dist/esm/fieldRegistry.native.js.map +1 -1
  238. package/dist/esm/fields/Barcode/index.native.js.map +1 -1
  239. package/dist/esm/fields/Check/index.native.js.map +1 -1
  240. package/dist/esm/fields/CodeEditor/index.native.js.map +1 -1
  241. package/dist/esm/fields/Color/index.native.js.map +1 -1
  242. package/dist/esm/fields/Data/index.native.js.map +1 -1
  243. package/dist/esm/fields/Date/sheetKeyboard.native.js.map +1 -1
  244. package/dist/esm/fields/Date/types.native.js.map +1 -1
  245. package/dist/esm/fields/Date/wire.native.js.map +1 -1
  246. package/dist/esm/fields/DateRange/index.native.js.map +1 -1
  247. package/dist/esm/fields/Duration/index.native.js.map +1 -1
  248. package/dist/esm/fields/DynamicLink.native.js.map +1 -1
  249. package/dist/esm/fields/FileUpload.native.js.map +1 -1
  250. package/dist/esm/fields/FileUploadField/index.native.js.map +1 -1
  251. package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
  252. package/dist/esm/fields/HTML/index.native.js.map +1 -1
  253. package/dist/esm/fields/Heading/index.native.js.map +1 -1
  254. package/dist/esm/fields/Icon/index.native.js.map +1 -1
  255. package/dist/esm/fields/Link.native.js.map +1 -1
  256. package/dist/esm/fields/LinkTree.native.js.map +1 -1
  257. package/dist/esm/fields/LongText/index.native.js.map +1 -1
  258. package/dist/esm/fields/MentionInput/index.mjs +4 -1
  259. package/dist/esm/fields/MentionInput/index.mjs.map +1 -1
  260. package/dist/esm/fields/MentionInput/index.native.js +4 -1
  261. package/dist/esm/fields/MentionInput/index.native.js.map +1 -1
  262. package/dist/esm/fields/Numeric/index.native.js.map +1 -1
  263. package/dist/esm/fields/OTPInput/index.native.js.map +1 -1
  264. package/dist/esm/fields/Password/index.native.js.map +1 -1
  265. package/dist/esm/fields/Phone/index.native.js.map +1 -1
  266. package/dist/esm/fields/Progress/index.native.js.map +1 -1
  267. package/dist/esm/fields/RadioGroup/index.native.js.map +1 -1
  268. package/dist/esm/fields/Rating/index.native.js.map +1 -1
  269. package/dist/esm/fields/ReadOnly/index.native.js.map +1 -1
  270. package/dist/esm/fields/Select/index.native.js.map +1 -1
  271. package/dist/esm/fields/Signature/index.native.js.map +1 -1
  272. package/dist/esm/fields/Slider/index.native.js.map +1 -1
  273. package/dist/esm/fields/SmallText/index.native.js.map +1 -1
  274. package/dist/esm/fields/Switch/index.native.js.map +1 -1
  275. package/dist/esm/fields/TableMultiSelect.native.js.map +1 -1
  276. package/dist/esm/fields/TextEditor/types.native.js.map +1 -1
  277. package/dist/esm/fields/TimePicker/index.native.js.map +1 -1
  278. package/dist/esm/fields/ToggleGroup/index.native.js.map +1 -1
  279. package/dist/esm/fields/shared/EditorPreviewPanes.native.js.map +1 -1
  280. package/dist/esm/filterFields.native.js.map +1 -1
  281. package/dist/esm/formEngine/createPrefill.native.js.map +1 -1
  282. package/dist/esm/formEngine/dirtyTracking.native.js.map +1 -1
  283. package/dist/esm/formEngine/doctypeMeta.native.js.map +1 -1
  284. package/dist/esm/formEngine/fetchFrom.native.js.map +1 -1
  285. package/dist/esm/formEngine/fieldPermissions.native.js.map +1 -1
  286. package/dist/esm/formEngine/formActions.native.js.map +1 -1
  287. package/dist/esm/formEngine/formLayout.native.js.map +1 -1
  288. package/dist/esm/formEngine/index.native.js.map +1 -1
  289. package/dist/esm/formEngine/useFormEngine.native.js.map +1 -1
  290. package/dist/esm/frappeEventColor.native.js.map +1 -1
  291. package/dist/esm/frappeStoryTarget.native.js.map +1 -1
  292. package/dist/esm/frappeUsers.native.js.map +1 -1
  293. package/dist/esm/index.mjs +2 -1
  294. package/dist/esm/index.mjs.map +1 -1
  295. package/dist/esm/index.native.js +2 -1
  296. package/dist/esm/index.native.js.map +1 -1
  297. package/dist/esm/liveErrors.native.js.map +1 -1
  298. package/dist/esm/resolveControl.native.js.map +1 -1
  299. package/dist/esm/schemaForm/SchemaWizard.native.js.map +1 -1
  300. package/dist/esm/schemaForm/applyFieldOrder.native.js.map +1 -1
  301. package/dist/esm/schemaForm/index.mjs +8 -7
  302. package/dist/esm/schemaForm/index.mjs.map +1 -1
  303. package/dist/esm/schemaForm/index.native.js +8 -7
  304. package/dist/esm/schemaForm/index.native.js.map +1 -1
  305. package/dist/esm/shared/devWarn.mjs +14 -0
  306. package/dist/esm/shared/devWarn.mjs.map +1 -0
  307. package/dist/esm/shared/devWarn.native.js +16 -0
  308. package/dist/esm/shared/devWarn.native.js.map +1 -0
  309. package/dist/esm/shared/useKeyboardInset.native.js.map +1 -1
  310. package/dist/esm/syncError.native.js.map +1 -1
  311. package/dist/esm/useFrappeAggregate.mjs +15 -8
  312. package/dist/esm/useFrappeAggregate.mjs.map +1 -1
  313. package/dist/esm/useFrappeAggregate.native.js +15 -9
  314. package/dist/esm/useFrappeAggregate.native.js.map +1 -1
  315. package/dist/esm/useFrappeFacets.mjs +10 -9
  316. package/dist/esm/useFrappeFacets.mjs.map +1 -1
  317. package/dist/esm/useFrappeFacets.native.js +10 -10
  318. package/dist/esm/useFrappeFacets.native.js.map +1 -1
  319. package/dist/esm/useFrappePagination.native.js.map +1 -1
  320. package/dist/esm/useFrappeRows.mjs +86 -0
  321. package/dist/esm/useFrappeRows.mjs.map +1 -0
  322. package/dist/esm/useFrappeRows.native.js +99 -0
  323. package/dist/esm/useFrappeRows.native.js.map +1 -0
  324. package/dist/jsx/FrappeCalendar.mjs +20 -17
  325. package/dist/jsx/FrappeCalendar.mjs.map +1 -1
  326. package/dist/jsx/FrappeCalendar.native.js +22 -21
  327. package/dist/jsx/FrappeCalendar.native.js.map +1 -1
  328. package/dist/jsx/FrappeForm.mjs +1 -1
  329. package/dist/jsx/FrappeForm.mjs.map +1 -1
  330. package/dist/jsx/FrappeForm.native.js +1 -1
  331. package/dist/jsx/FrappeForm.native.js.map +1 -1
  332. package/dist/jsx/FrappeGantt.mjs +27 -36
  333. package/dist/jsx/FrappeGantt.mjs.map +1 -1
  334. package/dist/jsx/FrappeGantt.native.js +27 -38
  335. package/dist/jsx/FrappeGantt.native.js.map +1 -1
  336. package/dist/jsx/FrappeImageView.mjs +5 -3
  337. package/dist/jsx/FrappeImageView.mjs.map +1 -1
  338. package/dist/jsx/FrappeImageView.native.js +8 -4
  339. package/dist/jsx/FrappeImageView.native.js.map +1 -1
  340. package/dist/jsx/FrappeKPICard.mjs +27 -2
  341. package/dist/jsx/FrappeKPICard.mjs.map +1 -1
  342. package/dist/jsx/FrappeKPICard.native.js +26 -1
  343. package/dist/jsx/FrappeKPICard.native.js.map +1 -1
  344. package/dist/jsx/FrappeKanban.mjs +16 -17
  345. package/dist/jsx/FrappeKanban.mjs.map +1 -1
  346. package/dist/jsx/FrappeKanban.native.js +18 -21
  347. package/dist/jsx/FrappeKanban.native.js.map +1 -1
  348. package/dist/jsx/FrappeLikedBy.mjs +1 -1
  349. package/dist/jsx/FrappeLikedBy.mjs.map +1 -1
  350. package/dist/jsx/FrappeLikedBy.native.js +1 -1
  351. package/dist/jsx/FrappeLikedBy.native.js.map +1 -1
  352. package/dist/jsx/FrappeList.mjs +2 -2
  353. package/dist/jsx/FrappeList.mjs.map +1 -1
  354. package/dist/jsx/FrappeList.native.js +2 -2
  355. package/dist/jsx/FrappeList.native.js.map +1 -1
  356. package/dist/jsx/FrappeProvider.mjs.map +1 -1
  357. package/dist/jsx/FrappeProvider.native.js.map +1 -1
  358. package/dist/jsx/FrappeReportBuilder.mjs +2 -2
  359. package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
  360. package/dist/jsx/FrappeReportBuilder.native.js +2 -2
  361. package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
  362. package/dist/jsx/FrappeTable.mjs +36 -39
  363. package/dist/jsx/FrappeTable.mjs.map +1 -1
  364. package/dist/jsx/FrappeTable.native.js +38 -39
  365. package/dist/jsx/FrappeTable.native.js.map +1 -1
  366. package/dist/jsx/FrappeTreeView.mjs +89 -55
  367. package/dist/jsx/FrappeTreeView.mjs.map +1 -1
  368. package/dist/jsx/FrappeTreeView.native.js +129 -50
  369. package/dist/jsx/FrappeTreeView.native.js.map +1 -1
  370. package/dist/jsx/commentActions.mjs +1 -1
  371. package/dist/jsx/commentActions.mjs.map +1 -1
  372. package/dist/jsx/commentActions.native.js +1 -1
  373. package/dist/jsx/commentActions.native.js.map +1 -1
  374. package/dist/jsx/components/timeline.mjs +1 -1
  375. package/dist/jsx/components/timeline.mjs.map +1 -1
  376. package/dist/jsx/components/timeline.native.js +1 -1
  377. package/dist/jsx/desk/DeskShell.mjs +2 -0
  378. package/dist/jsx/desk/DeskShell.mjs.map +1 -1
  379. package/dist/jsx/desk/DeskShell.native.js +2 -0
  380. package/dist/jsx/desk/DeskShell.native.js.map +1 -1
  381. package/dist/jsx/desk/awesomebar.mjs +1 -1
  382. package/dist/jsx/desk/awesomebar.mjs.map +1 -1
  383. package/dist/jsx/desk/awesomebar.native.js +1 -1
  384. package/dist/jsx/desk/awesomebar.native.js.map +1 -1
  385. package/dist/jsx/fields/MentionInput/index.mjs +4 -1
  386. package/dist/jsx/fields/MentionInput/index.mjs.map +1 -1
  387. package/dist/jsx/fields/MentionInput/index.native.js +4 -1
  388. package/dist/jsx/fields/MentionInput/index.native.js.map +1 -1
  389. package/dist/jsx/index.js +2 -1
  390. package/dist/jsx/index.js.map +1 -1
  391. package/dist/jsx/index.mjs +2 -1
  392. package/dist/jsx/index.mjs.map +1 -1
  393. package/dist/jsx/index.native.js +4 -0
  394. package/dist/jsx/index.native.js.map +1 -1
  395. package/dist/jsx/schemaForm/index.mjs +8 -7
  396. package/dist/jsx/schemaForm/index.mjs.map +1 -1
  397. package/dist/jsx/schemaForm/index.native.js +10 -9
  398. package/dist/jsx/schemaForm/index.native.js.map +1 -1
  399. package/dist/jsx/shared/devWarn.mjs +14 -0
  400. package/dist/jsx/shared/devWarn.mjs.map +1 -0
  401. package/dist/jsx/shared/devWarn.native.js +45 -0
  402. package/dist/jsx/shared/devWarn.native.js.map +1 -0
  403. package/dist/jsx/useFrappeAggregate.mjs +15 -8
  404. package/dist/jsx/useFrappeAggregate.mjs.map +1 -1
  405. package/dist/jsx/useFrappeAggregate.native.js +15 -9
  406. package/dist/jsx/useFrappeAggregate.native.js.map +1 -1
  407. package/dist/jsx/useFrappeFacets.mjs +10 -9
  408. package/dist/jsx/useFrappeFacets.mjs.map +1 -1
  409. package/dist/jsx/useFrappeFacets.native.js +10 -10
  410. package/dist/jsx/useFrappeFacets.native.js.map +1 -1
  411. package/dist/jsx/useFrappeRows.mjs +86 -0
  412. package/dist/jsx/useFrappeRows.mjs.map +1 -0
  413. package/dist/jsx/useFrappeRows.native.js +129 -0
  414. package/dist/jsx/useFrappeRows.native.js.map +1 -0
  415. package/package.json +17 -16
  416. package/src/FileBrowser/FileBrowser.spec.tsx +11 -1
  417. package/src/FrappeCalendar.tsx +25 -22
  418. package/src/FrappeForm.tsx +7 -3
  419. package/src/FrappeGantt.tsx +29 -39
  420. package/src/FrappeImageView.tsx +5 -3
  421. package/src/FrappeKPICard.spec.tsx +128 -0
  422. package/src/FrappeKPICard.tsx +44 -4
  423. package/src/FrappeKanban.tsx +24 -17
  424. package/src/FrappeLikedBy.tsx +1 -1
  425. package/src/FrappeList.tsx +3 -2
  426. package/src/FrappeProvider.fixturesOnly.spec.tsx +129 -0
  427. package/src/FrappeProvider.tsx +16 -6
  428. package/src/FrappeReportBuilder.tsx +4 -2
  429. package/src/FrappeTable.autoHeight.spec.tsx +142 -0
  430. package/src/FrappeTable.rowAccumulator.spec.tsx +110 -0
  431. package/src/FrappeTable.tsx +75 -48
  432. package/src/FrappeTreeView.stories.tsx +59 -0
  433. package/src/FrappeTreeView.tsx +166 -56
  434. package/src/commentActions.ts +1 -1
  435. package/src/components/timeline.tsx +1 -1
  436. package/src/desk/DeskShell.tsx +2 -0
  437. package/src/desk/awesomebar.tsx +5 -2
  438. package/src/fields/CodeEditor/CodeEditor.spec.tsx +10 -9
  439. package/src/fields/Date/typeParity.spec.tsx +19 -9
  440. package/src/fields/Duration/Duration.spec.tsx +73 -0
  441. package/src/fields/MentionInput/MentionInput.spec.tsx +110 -0
  442. package/src/fields/MentionInput/index.tsx +4 -1
  443. package/src/fields/Password/index.tsx +1 -1
  444. package/src/fields/Progress/Progress.spec.tsx +9 -0
  445. package/src/fields/RadioGroup/RadioGroup.spec.tsx +93 -0
  446. package/src/fields/Signature/Signature.spec.tsx +105 -0
  447. package/src/fields/Slider/Slider.spec.tsx +73 -0
  448. package/src/fields/Switch/Switch.spec.tsx +65 -0
  449. package/src/fields/TextEditor/TextEditor.spec.tsx +2 -3
  450. package/src/fields/TextEditor/TextEditor.stories.tsx +31 -1
  451. package/src/fields/ToggleGroup/ToggleGroup.spec.tsx +110 -0
  452. package/src/frappeViews.designLaw.spec.ts +11 -7
  453. package/src/index.ts +13 -0
  454. package/src/schemaForm/index.tsx +6 -7
  455. package/src/shared/devWarn.ts +42 -0
  456. package/src/useFrappeAggregate.ts +17 -11
  457. package/src/useFrappeFacets.ts +17 -10
  458. package/src/useFrappeRows.spec.tsx +176 -0
  459. package/src/useFrappeRows.ts +293 -0
  460. package/types/FrappeCalendar.d.ts.map +1 -1
  461. package/types/FrappeForm.d.ts +6 -2
  462. package/types/FrappeForm.d.ts.map +1 -1
  463. package/types/FrappeGantt.d.ts.map +1 -1
  464. package/types/FrappeImageView.d.ts.map +1 -1
  465. package/types/FrappeKPICard.d.ts +9 -1
  466. package/types/FrappeKPICard.d.ts.map +1 -1
  467. package/types/FrappeKanban.d.ts.map +1 -1
  468. package/types/FrappeList.d.ts.map +1 -1
  469. package/types/FrappeProvider.d.ts +16 -6
  470. package/types/FrappeProvider.d.ts.map +1 -1
  471. package/types/FrappeReportBuilder.d.ts.map +1 -1
  472. package/types/FrappeTable.d.ts +20 -1
  473. package/types/FrappeTable.d.ts.map +1 -1
  474. package/types/FrappeTreeView.d.ts +38 -1
  475. package/types/FrappeTreeView.d.ts.map +1 -1
  476. package/types/desk/DeskShell.d.ts.map +1 -1
  477. package/types/desk/awesomebar.d.ts.map +1 -1
  478. package/types/fields/MentionInput/index.d.ts.map +1 -1
  479. package/types/fields/Password/index.d.ts +1 -1
  480. package/types/fields/Password/index.d.ts.map +1 -1
  481. package/types/index.d.ts +1 -0
  482. package/types/index.d.ts.map +1 -1
  483. package/types/schemaForm/index.d.ts.map +1 -1
  484. package/types/shared/devWarn.d.ts +23 -0
  485. package/types/shared/devWarn.d.ts.map +1 -0
  486. package/types/useFrappeAggregate.d.ts +2 -1
  487. package/types/useFrappeAggregate.d.ts.map +1 -1
  488. package/types/useFrappeFacets.d.ts +2 -1
  489. package/types/useFrappeFacets.d.ts.map +1 -1
  490. package/types/useFrappeRows.d.ts +186 -0
  491. package/types/useFrappeRows.d.ts.map +1 -0
@@ -0,0 +1,129 @@
1
+ /**
2
+ * A fixture corpus satisfies the connection requirement on its own (MPO-228).
3
+ *
4
+ * `createApp({ frappe: { fixtures } })` mounts a FrappeProvider with NO
5
+ * baseURL, so the collection-backed components must read the corpus instead
6
+ * of demanding a bench URL. These specs pin that: rows render, doctype meta
7
+ * comes from the corpus, and `fetch` is never called — a fixtures-only app
8
+ * runs with no server. The old error still fires when neither is configured.
9
+ */
10
+
11
+ import { InMemoryFixtureProvider } from "@multiplatform.one/frappe";
12
+ import type { DataTableColumn } from "@multiplatform.one/table";
13
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
14
+ import { cleanup, screen } from "@testing-library/react";
15
+ import i18next from "i18next";
16
+ import { initReactI18next } from "react-i18next";
17
+ import { Text } from "tamagui";
18
+ import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
19
+ import { FrappeList } from "./FrappeList";
20
+ import { FrappeProvider } from "./FrappeProvider";
21
+ import { FrappeTable } from "./FrappeTable";
22
+
23
+ beforeAll(async () => {
24
+ await i18next.use(initReactI18next).init({
25
+ lng: "en",
26
+ interpolation: { escapeValue: false },
27
+ resources: { en: { translation: {} } },
28
+ });
29
+ });
30
+
31
+ afterEach(() => {
32
+ cleanup();
33
+ vi.restoreAllMocks();
34
+ });
35
+
36
+ interface Customer {
37
+ name: string;
38
+ doctype: string;
39
+ customer_name: string;
40
+ status: string;
41
+ }
42
+
43
+ const customers: Customer[] = [
44
+ { name: "CUST-001", doctype: "Customer", customer_name: "Acme Corp", status: "Active" },
45
+ { name: "CUST-002", doctype: "Customer", customer_name: "Globex", status: "Lead" },
46
+ ];
47
+
48
+ const columns: DataTableColumn<Partial<Customer>>[] = [
49
+ { id: "name", accessorKey: "name", header: "ID" },
50
+ { id: "customer_name", accessorKey: "customer_name", header: "Customer Name" },
51
+ ];
52
+
53
+ /**
54
+ * Corpus carrying both documents and the getdoctype-shaped meta doc, so the
55
+ * table's `useDocTypeMeta` call is answered locally too (no filterableFields
56
+ * prop, which would skip that path).
57
+ */
58
+ function fixtureCorpus() {
59
+ return new InMemoryFixtureProvider({
60
+ DocType: [
61
+ {
62
+ name: "Customer",
63
+ doctype: "DocType",
64
+ module: "Selling",
65
+ fields: [
66
+ { fieldname: "customer_name", fieldtype: "Data", label: "Customer Name" },
67
+ { fieldname: "status", fieldtype: "Select", label: "Status", options: "Active\nLead" },
68
+ ],
69
+ },
70
+ ],
71
+ Customer: customers,
72
+ });
73
+ }
74
+
75
+ /** Fails the test if anything dials the network. */
76
+ function forbidFetch() {
77
+ return vi.spyOn(globalThis, "fetch").mockImplementation((input) => {
78
+ throw new Error(`unexpected network request: ${String(input)}`);
79
+ });
80
+ }
81
+
82
+ describe("FrappeProvider with fixtures and no baseURL", () => {
83
+ it("renders a FrappeTable off the corpus without touching the network", async () => {
84
+ const fetchSpy = forbidFetch();
85
+ renderWithProviders(
86
+ <FrappeProvider fixtures={fixtureCorpus()}>
87
+ <FrappeTable<Customer>
88
+ doctype="Customer"
89
+ columns={columns}
90
+ layout="table"
91
+ enablePagination={false}
92
+ />
93
+ </FrappeProvider>,
94
+ );
95
+
96
+ expect(await screen.findByText("Acme Corp")).toBeTruthy();
97
+ expect(await screen.findByText("Globex")).toBeTruthy();
98
+ expect(fetchSpy).not.toHaveBeenCalled();
99
+ });
100
+
101
+ it("renders a FrappeList off the corpus without touching the network", async () => {
102
+ const fetchSpy = forbidFetch();
103
+ renderWithProviders(
104
+ <FrappeProvider fixtures={fixtureCorpus()}>
105
+ <FrappeList<Customer>
106
+ doctype="Customer"
107
+ fields={["name", "customer_name"]}
108
+ renderItem={(item) => <Text>{item.customer_name}</Text>}
109
+ />
110
+ </FrappeProvider>,
111
+ );
112
+
113
+ expect(await screen.findByText("Acme Corp")).toBeTruthy();
114
+ expect(fetchSpy).not.toHaveBeenCalled();
115
+ });
116
+ });
117
+
118
+ describe("FrappeProvider with neither baseURL nor fixtures", () => {
119
+ it("still names what is missing", () => {
120
+ vi.spyOn(console, "error").mockImplementation(() => {});
121
+ expect(() =>
122
+ renderWithProviders(
123
+ <FrappeProvider>
124
+ <FrappeTable<Customer> doctype="Customer" columns={columns} layout="table" />
125
+ </FrappeProvider>,
126
+ ),
127
+ ).toThrow(/baseURL or fixtures/);
128
+ });
129
+ });
@@ -7,8 +7,12 @@ import React, { createContext, use, useContext } from "react";
7
7
  * @category Types
8
8
  */
9
9
  export interface FrappeConfig {
10
- /** The base URL of the Frappe server */
11
- baseURL: string;
10
+ /**
11
+ * The base URL of the Frappe server. Required unless `fixtures` is set:
12
+ * a fixture corpus answers every read locally, so a fixtures-only app has
13
+ * no bench to name and nothing dials one.
14
+ */
15
+ baseURL?: string;
12
16
  /** False uses HTTP collections without a realtime transport. Defaults to true. */
13
17
  realtime?: boolean;
14
18
  /**
@@ -52,7 +56,10 @@ export interface FrappeConfig {
52
56
  * can't forward WebSocket upgrades.
53
57
  */
54
58
  socketOptions?: UseFrappeCollectionConfig["socketOptions"];
55
- /** Fixture provider for testing */
59
+ /**
60
+ * Fixture corpus every read and write is served from. Set it without a
61
+ * `baseURL` to run the whole app off fixtures with no server.
62
+ */
56
63
  fixtures?: FixtureProvider;
57
64
  }
58
65
 
@@ -66,8 +73,8 @@ const FrappeContext = createContext<FrappeConfig | undefined>(undefined);
66
73
  export interface FrappeProviderProps {
67
74
  /** Child components */
68
75
  children: React.ReactNode;
69
- /** The base URL of the Frappe server */
70
- baseURL: string;
76
+ /** The base URL of the Frappe server. Required unless `fixtures` is set. */
77
+ baseURL?: string;
71
78
  /** False uses HTTP collections without a realtime transport. Defaults to true. */
72
79
  realtime?: boolean;
73
80
  /** Override port for Socket.IO realtime connection (e.g. 9000 in dev). */
@@ -89,7 +96,10 @@ export interface FrappeProviderProps {
89
96
  syncOptions?: any;
90
97
  /** Extra Socket.IO client options for the realtime connection. */
91
98
  socketOptions?: UseFrappeCollectionConfig["socketOptions"];
92
- /** Fixture provider for testing */
99
+ /**
100
+ * Fixture corpus every read and write is served from. Set it without a
101
+ * `baseURL` to run the whole app off fixtures with no server.
102
+ */
93
103
  fixtures?: FixtureProvider;
94
104
  }
95
105
 
@@ -125,8 +125,10 @@ export function FrappeReportBuilder({
125
125
  const finalAuth = auth ?? config?.auth;
126
126
  const finalSyncOptions = syncOptions ?? config?.syncOptions;
127
127
 
128
- if (!finalBaseURL) {
129
- throw new Error("FrappeReportBuilder requires baseURL prop or FrappeProvider with baseURL");
128
+ if (!finalBaseURL && !config?.fixtures) {
129
+ throw new Error(
130
+ "FrappeReportBuilder requires baseURL prop or FrappeProvider with baseURL or fixtures",
131
+ );
130
132
  }
131
133
 
132
134
  const connectionConfig = {
@@ -0,0 +1,142 @@
1
+ /**
2
+ * MPO-229: FrappeTable must not collapse to a sliver in an auto-height parent.
3
+ *
4
+ * The wrapper YStack used `flex={1}`, which in React Native's flex model is
5
+ * `1 1 0` with a basis of ZERO. A basis-0 chain inside a parent that has no
6
+ * height of its own resolves to 0, so the whole table rendered as a one-row
7
+ * sliver. DataTableRoot's own root already avoids exactly this (grow 1 /
8
+ * shrink 1 / basis auto / minHeight 0, and it carries the note saying why).
9
+ * The FrappeTable wrapper sitting above it reintroduced the trap.
10
+ *
11
+ * Two halves here. The render cases pin the behaviour a caller sees: 12 rows
12
+ * in a plain auto-height parent, and the same table in a fixed-height parent.
13
+ * The source cases are the mechanical guard, because happy-dom runs no flex
14
+ * layout, so a basis-0 regression is invisible to a rendered assertion and
15
+ * only the geometry itself can be checked.
16
+ */
17
+
18
+ import fs from "node:fs";
19
+ import path from "node:path";
20
+ import { InMemoryFixtureProvider } from "@multiplatform.one/frappe";
21
+ import type { DataTableColumn } from "@multiplatform.one/table";
22
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
23
+ import { cleanup, screen } from "@testing-library/react";
24
+ import i18next from "i18next";
25
+ import { initReactI18next } from "react-i18next";
26
+ import { YStack } from "tamagui";
27
+ import { afterEach, beforeAll, describe, expect, it } from "vitest";
28
+ import { FrappeProvider } from "./FrappeProvider";
29
+ import { FrappeTable } from "./FrappeTable";
30
+
31
+ beforeAll(async () => {
32
+ if (!i18next.isInitialized) {
33
+ await i18next.use(initReactI18next).init({
34
+ lng: "en",
35
+ interpolation: { escapeValue: false },
36
+ resources: { en: { translation: {} } },
37
+ });
38
+ }
39
+ });
40
+
41
+ afterEach(() => {
42
+ cleanup();
43
+ });
44
+
45
+ interface Customer {
46
+ name: string;
47
+ doctype: string;
48
+ customer_name: string;
49
+ }
50
+
51
+ const customers: Customer[] = Array.from({ length: 12 }, (_, index) => ({
52
+ name: `CUST-${String(index + 1).padStart(3, "0")}`,
53
+ doctype: "Customer",
54
+ customer_name: `Customer ${index + 1}`,
55
+ }));
56
+
57
+ const columns: DataTableColumn<Partial<Customer>>[] = [
58
+ { id: "name", accessorKey: "name", header: "ID" },
59
+ { id: "customer_name", accessorKey: "customer_name", header: "Customer Name" },
60
+ ];
61
+
62
+ // Supplied so the table does not go to useDocTypeMeta for the filter list.
63
+ const filterFields = [{ id: "customer_name", label: "Customer Name", type: "text" as const }];
64
+
65
+ function renderInParent(parent: React.ReactElement) {
66
+ return renderWithProviders(
67
+ <FrappeProvider
68
+ baseURL="https://frappetable-autoheight.spec.test"
69
+ fixtures={new InMemoryFixtureProvider({ Customer: customers })}
70
+ >
71
+ {parent}
72
+ </FrappeProvider>,
73
+ );
74
+ }
75
+
76
+ const table = (
77
+ <FrappeTable<Customer>
78
+ doctype="Customer"
79
+ columns={columns}
80
+ filterableFields={filterFields}
81
+ layout="table"
82
+ enablePagination={false}
83
+ />
84
+ );
85
+
86
+ const srcDir = path.resolve(__dirname, ".");
87
+
88
+ function read(file: string): string {
89
+ return fs.readFileSync(path.resolve(srcDir, file), "utf-8");
90
+ }
91
+
92
+ function codeOnly(source: string): string {
93
+ return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
94
+ }
95
+
96
+ describe("FrappeTable in an auto-height parent (MPO-229)", () => {
97
+ it("renders every row inside a plain auto-height YStack", async () => {
98
+ renderInParent(<YStack>{table}</YStack>);
99
+ await screen.findByText("Customer 1");
100
+ for (const customer of customers) {
101
+ expect(screen.getByText(customer.customer_name)).toBeTruthy();
102
+ }
103
+ expect(screen.queryByText(/No Customer records found/i)).toBeNull();
104
+ });
105
+
106
+ it("still renders every row inside a fixed-height parent", async () => {
107
+ renderInParent(<YStack height={400}>{table}</YStack>);
108
+ await screen.findByText("Customer 1");
109
+ for (const customer of customers) {
110
+ expect(screen.getByText(customer.customer_name)).toBeTruthy();
111
+ }
112
+ });
113
+
114
+ it("roots grow and shrink on a basis of auto, the same geometry as DataTableRoot", () => {
115
+ const source = codeOnly(read("FrappeTable.tsx"));
116
+
117
+ // flex={1} is 1 1 0 in RN. Basis 0 in an auto-height parent resolves to 0
118
+ // and the table collapses to a one-row sliver.
119
+ expect(source).not.toContain("flex={1}");
120
+
121
+ const tags = source.match(/<YStack\b[^>]*>/g) ?? [];
122
+ const roots = tags.filter((tag) => tag.includes("flexBasis"));
123
+ expect(roots.length).toBe(2);
124
+ for (const root of roots) {
125
+ expect(root).toContain("flexGrow={1}");
126
+ expect(root).toContain("flexShrink={1}");
127
+ expect(root).toContain('flexBasis="auto"');
128
+ expect(root).toContain("minHeight={0}");
129
+ }
130
+ });
131
+
132
+ it("DataTableRoot still carries the geometry FrappeTable copies", () => {
133
+ const source = codeOnly(read("../../table/src/components/DataTable/DataTableRoot.tsx"));
134
+ const tags = source.match(/<View\b[^>]*>/g) ?? [];
135
+ const root = tags.find((tag) => tag.includes("flexBasis"));
136
+ expect(root).toBeTruthy();
137
+ expect(root).toContain("flexGrow={1}");
138
+ expect(root).toContain("flexShrink={1}");
139
+ expect(root).toContain('flexBasis="auto"');
140
+ expect(root).toContain("minHeight={0}");
141
+ });
142
+ });
@@ -0,0 +1,110 @@
1
+ /**
2
+ * A running balance on a FrappeTable (MPO-250).
3
+ *
4
+ * The gap this closes: `FrappeTableProps` omits `data`, so before the row
5
+ * accumulator existed there was NO prop that reached the rows. A consumer who
6
+ * wanted a folded column on a Frappe doctype had to fetch the doctype again
7
+ * itself and hand the folded rows to a bare DataTable — a second read path, a
8
+ * second error path, and no saved filters or refresh channel.
9
+ *
10
+ * What this locks: `rowAccumulator` passes through FrappeTable to the table,
11
+ * folds the rows FrappeTable already fetched, and the figures continue onto
12
+ * page two.
13
+ *
14
+ * Fixture-hermetic: postings ride InMemoryFixtureProvider.
15
+ */
16
+
17
+ import { InMemoryFixtureProvider } from "@multiplatform.one/frappe";
18
+ import type { DataTableColumn, RowAccumulator } from "@multiplatform.one/table";
19
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
20
+ import { cleanup, screen, waitFor } from "@testing-library/react";
21
+ import userEvent from "@testing-library/user-event";
22
+ import i18next from "i18next";
23
+ import { initReactI18next } from "react-i18next";
24
+ import { afterEach, beforeAll, describe, expect, it } from "vitest";
25
+ import { FrappeProvider } from "./FrappeProvider";
26
+ import { FrappeTable } from "./FrappeTable";
27
+
28
+ beforeAll(async () => {
29
+ await i18next.use(initReactI18next).init({
30
+ lng: "en",
31
+ interpolation: { escapeValue: false },
32
+ resources: { en: { translation: {} } },
33
+ });
34
+ });
35
+
36
+ afterEach(() => cleanup());
37
+
38
+ interface Posting {
39
+ name: string;
40
+ doctype: string;
41
+ date: string;
42
+ payee: string;
43
+ amount: number;
44
+ /** Derived by the accumulator — declared on the doc type so a column can
45
+ * name it in accessorKey. */
46
+ running?: number;
47
+ }
48
+
49
+ const postings: Posting[] = Array.from({ length: 8 }, (_, i) => ({
50
+ name: `POST-${String(i + 1).padStart(2, "0")}`,
51
+ doctype: "Posting",
52
+ date: `2026-02-${String(i + 1).padStart(2, "0")}`,
53
+ payee: `Payee ${i + 1}`,
54
+ amount: 10,
55
+ }));
56
+
57
+ const runningBalance: RowAccumulator<Partial<Posting>, number> = {
58
+ initial: 0,
59
+ reduce: (carried, row) => carried + (row.amount ?? 0),
60
+ derive: (carried) => ({ running: carried }),
61
+ order: (a, b) => String(a.date).localeCompare(String(b.date)),
62
+ };
63
+
64
+ const columns: DataTableColumn<Partial<Posting>>[] = [
65
+ { id: "name", accessorKey: "name", header: "ID" },
66
+ { id: "amount", accessorKey: "amount", header: "Amount" },
67
+ {
68
+ id: "running",
69
+ accessorKey: "running",
70
+ header: "Balance",
71
+ sortable: false,
72
+ // Marked so an assertion can tell a balance of 10 from an amount of 10.
73
+ cell: ({ row }) => `bal ${row.original.running}`,
74
+ },
75
+ ];
76
+
77
+ const filterFields = [{ id: "payee", label: "Payee", type: "text" as const }];
78
+
79
+ describe("FrappeTable rowAccumulator", () => {
80
+ it("folds the rows FrappeTable fetched, and page two continues", async () => {
81
+ const user = userEvent.setup();
82
+ renderWithProviders(
83
+ <FrappeProvider
84
+ baseURL="https://frappetable-fold.spec.test"
85
+ fixtures={new InMemoryFixtureProvider({ Posting: postings })}
86
+ >
87
+ <FrappeTable<Posting>
88
+ doctype="Posting"
89
+ columns={columns}
90
+ fields={["name", "date", "payee", "amount"]}
91
+ filterableFields={filterFields}
92
+ rowAccumulator={runningBalance}
93
+ layout="table"
94
+ initialState={{ pagination: { pageIndex: 0, pageSize: 4 } }}
95
+ />
96
+ </FrappeProvider>,
97
+ );
98
+
99
+ await waitFor(() => expect(screen.getByText("POST-01")).toBeTruthy());
100
+ expect(screen.getByText("bal 10")).toBeTruthy();
101
+ expect(screen.getByText("bal 40")).toBeTruthy();
102
+ expect(screen.queryByText("bal 50")).toBeNull();
103
+
104
+ await user.click(screen.getByLabelText("Go to next page"));
105
+ await waitFor(() => expect(screen.getByText("POST-05")).toBeTruthy());
106
+ expect(screen.getByText("bal 50"), "page two continues the fold").toBeTruthy();
107
+ expect(screen.getByText("bal 80")).toBeTruthy();
108
+ expect(screen.queryByText("bal 10"), "page one's opening balance is gone").toBeNull();
109
+ });
110
+ });
@@ -3,9 +3,7 @@ import {
3
3
  Order,
4
4
  matchesFilters,
5
5
  useDocTypeMeta,
6
- useFrappeCollection,
7
6
  useFrappeDoctypeEvents,
8
- useLiveQuery,
9
7
  } from "@multiplatform.one/frappe";
10
8
  import {
11
9
  DataTable,
@@ -25,12 +23,31 @@ import { FilterManager, SavedFilterDropdown, useSavedFilters } from "@multiplatf
25
23
  import { useFrappeConfig } from "./FrappeProvider";
26
24
  import { FrappeReconnectIndicator } from "./FrappeReconnectIndicator";
27
25
  import { docFieldsToFilterFields } from "./filterFields";
28
- import { toLiveLoadError } from "./liveErrors";
26
+ import { useFrappeRows } from "./useFrappeRows";
29
27
 
30
28
  /**
31
29
  * Props for the FrappeTable component.
32
30
  *
33
- * @typeParam TDoc - The document type with at least name and doctype fields
31
+ * `data` is omitted because FrappeTable fetches its own rows. Everything else
32
+ * on `DataTableProps` passes straight through — including `rowAccumulator`
33
+ * (MPO-250), so a Frappe-bound table CAN carry a folded column (a running
34
+ * balance, a per-row cumulative count) without the caller re-fetching the
35
+ * doctype and hand-assembling a DataTable. Declare the derived field on
36
+ * `TDoc` so a column can name it in `accessorKey`:
37
+ *
38
+ * ```tsx
39
+ * const balance: RowAccumulator<Partial<Posting>, string> = {
40
+ * initial: "0",
41
+ * reduce: (carried, row) => addMoney(carried, row.amount ?? "0"),
42
+ * derive: (carried) => ({ running: carried }),
43
+ * order: (a, b) => compareByDateThenName(a, b),
44
+ * };
45
+ * <FrappeTable<Posting & { running?: string }> doctype="Posting" rowAccumulator={balance} … />
46
+ * ```
47
+ *
48
+ * The fold runs over the rows the table HOLDS, before pagination. In
49
+ * `serverSide` mode that is one page, so a running balance there needs the
50
+ * server to send the figure — the fold cannot invent rows it never got.
34
51
  */
35
52
  export interface FrappeTableProps<TDoc extends { name: string; doctype: string }> extends Omit<
36
53
  DataTableProps<Partial<TDoc>>,
@@ -310,8 +327,10 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
310
327
  const finalAuth = auth ?? config?.auth;
311
328
  const finalSyncOptions = syncOptions ?? config?.syncOptions;
312
329
 
313
- if (!finalBaseURL) {
314
- throw new Error("FrappeTable requires baseURL prop or FrappeProvider with baseURL");
330
+ // A fixture corpus is a complete data source, so it satisfies the same
331
+ // requirement a baseURL does: fixture mode never dials a server.
332
+ if (!finalBaseURL && !config?.fixtures) {
333
+ throw new Error("FrappeTable requires baseURL prop or FrappeProvider with baseURL or fixtures");
315
334
  }
316
335
 
317
336
  // One connection config for the live subscription (client mode), the
@@ -594,32 +613,31 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
594
613
  // Always call the hook — toggling `savedFilters` must not change hook order (SB-M-012).
595
614
  const savedFiltersHook = useSavedFilters({ userId });
596
615
 
597
- // Honest error surface: the collection swallows initial-sync failures
598
- // (renders as empty otherwise); onSyncError turns 403/PermissionError and
599
- // network failures into a visible error state.
600
- const handleSyncError = useCallback(
601
- (err: unknown) => {
602
- setErrorState(
603
- toLiveLoadError(err, {
604
- permissionMessage: t("You do not have permission to view {{doctype}}", { doctype }),
605
- genericMessage: t("Error loading {{doctype}}", { doctype }),
606
- }),
607
- );
608
- },
616
+ // The client/live-collection read is the SHARED one (MPO-251): collection
617
+ // + live query + the honest error surface a bare collection swallows
618
+ // (without onSyncError a 403 or a dead server renders as an empty table).
619
+ // `limit: null` keeps the bench's own page length rather than the hook's
620
+ // default — FrappeTable paginates on the rows it is handed, and moving its
621
+ // row ceiling here would move it for every existing consumer.
622
+ const rowsMessages = useMemo(
623
+ () => ({
624
+ permission: t("You do not have permission to view {{doctype}}", { doctype }),
625
+ generic: t("Error loading {{doctype}}", { doctype }),
626
+ queryFailed: t("Failed to load {{doctype}}", { doctype }),
627
+ }),
609
628
  [doctype, t],
610
629
  );
611
-
612
- const collection = useFrappeCollection<TDoc>(
613
- serverSide ? undefined : connectionConfig,
614
- serverSide ? "" : doctype,
615
- serverSide ? undefined : { fields, filters: activeFilters, onSyncError: handleSyncError },
616
- );
617
630
  const {
618
- data: rawData,
619
- isLoading: isRowsLoading,
620
- isError: isLiveError,
621
- } = useLiveQuery((q) => (collection ? q.from({ _list: collection }) : null), [collection]);
622
- const clientRows = useMemo(() => (rawData as TDoc[] | undefined) ?? [], [rawData]);
631
+ rows: clientRows,
632
+ isQueryLoading: isRowsLoading,
633
+ error: liveError,
634
+ } = useFrappeRows<TDoc>(serverSide ? undefined : connectionConfig, doctype, {
635
+ fields,
636
+ filters: activeFilters,
637
+ limit: null,
638
+ enabled: !serverSide,
639
+ messages: rowsMessages,
640
+ });
623
641
 
624
642
  // Keep-rows-and-reconcile for client/live-collection mode: a filter change
625
643
  // swaps the collection (new subscription), which reports loading from
@@ -661,19 +679,6 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
661
679
  ? !infiniteServerMode && serverLoading && hasLoadedOnce
662
680
  : isRowsLoading && lastGoodClientRows.current !== null;
663
681
 
664
- // Reset stale errors when the underlying collection changes (new doctype
665
- // or filters) so a recovered query doesn't keep showing the old failure.
666
- useEffect(() => {
667
- if (!serverSide) setErrorState(null);
668
- }, [collection, serverSide]);
669
-
670
- // Fallback surface for live-query failures the collection itself reports
671
- // (onSyncError above carries the specific message when available).
672
- useEffect(() => {
673
- if (serverSide || !isLiveError) return;
674
- setErrorState((prev) => prev ?? new Error(t("Failed to load {{doctype}}", { doctype })));
675
- }, [serverSide, isLiveError, doctype, t]);
676
-
677
682
  const handleSelectFilter = (filter: any) => {
678
683
  if (filter) {
679
684
  setActiveFilters(filter.filters);
@@ -697,8 +702,12 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
697
702
  await savedFiltersHook.deleteFilter(name);
698
703
  };
699
704
 
700
- if (errorState && error) {
701
- return <>{error(errorState)}</>;
705
+ // serverSide failures come from the fetch above; client/live failures come
706
+ // from useFrappeRows. Only one path is ever live at a time.
707
+ const resolvedError = serverSide ? errorState : liveError;
708
+
709
+ if (resolvedError && error) {
710
+ return <>{error(resolvedError)}</>;
702
711
  }
703
712
 
704
713
  const rowData = rows ?? [];
@@ -706,8 +715,8 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
706
715
  // Infinite serverSide delegates loading/empty chrome to DataTable.
707
716
  // After the first load, NEVER replace DataTable with empty/loading chrome
708
717
  // while a refetch is in flight — that was the named blank-the-list defect.
709
- const boundaryState = errorState
710
- ? { error: errorState }
718
+ const boundaryState = resolvedError
719
+ ? { error: resolvedError }
711
720
  : isRowsLoadingResolved
712
721
  ? { loading: loading ?? true }
713
722
  : rowData.length === 0 && !infiniteServerMode && !isFetching
@@ -715,7 +724,16 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
715
724
  : null;
716
725
  if (boundaryState) {
717
726
  return (
718
- <YStack {...knobProps.gap} flex={1}>
727
+ <YStack
728
+ {...knobProps.gap}
729
+ // flexBasis auto, not flex={1}: a basis-0 chain resolves to 0 in an
730
+ // auto-height parent and collapses the table to a sliver. Same
731
+ // geometry as DataTableRoot's own root.
732
+ flexGrow={1}
733
+ flexShrink={1}
734
+ flexBasis="auto"
735
+ minHeight={0}
736
+ >
719
737
  <FrappeReconnectIndicator config={connectionConfig} />
720
738
  <AsyncBoundary
721
739
  layout={layout === "list" || layout === "cards" ? "list" : "table"}
@@ -728,7 +746,16 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
728
746
  }
729
747
 
730
748
  return (
731
- <YStack {...knobProps.gap} flex={1}>
749
+ <YStack
750
+ {...knobProps.gap}
751
+ // flexBasis auto, not flex={1}: a basis-0 chain resolves to 0 in an
752
+ // auto-height parent and collapses the table to a sliver. Same
753
+ // geometry as DataTableRoot's own root.
754
+ flexGrow={1}
755
+ flexShrink={1}
756
+ flexBasis="auto"
757
+ minHeight={0}
758
+ >
732
759
  <FrappeReconnectIndicator config={connectionConfig} />
733
760
  {savedFilters && savedFiltersHook && (
734
761
  <XStack {...knobProps.gap} padding="$2">