@multiplatform.one/frappe-ui 6.6.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (516) hide show
  1. package/dist/cjs/FrappeAssignment.cjs +2 -0
  2. package/dist/cjs/FrappeAssignment.native.js +2 -0
  3. package/dist/cjs/FrappeAssignment.native.js.map +1 -1
  4. package/dist/cjs/FrappeCalendar.cjs +5 -7
  5. package/dist/cjs/FrappeCalendar.native.js +5 -9
  6. package/dist/cjs/FrappeCalendar.native.js.map +1 -1
  7. package/dist/cjs/FrappeChartCard.cjs +2 -0
  8. package/dist/cjs/FrappeChartCard.native.js +2 -0
  9. package/dist/cjs/FrappeChartCard.native.js.map +1 -1
  10. package/dist/cjs/FrappeDashboard.cjs +11 -22
  11. package/dist/cjs/FrappeDashboard.native.js +20 -22
  12. package/dist/cjs/FrappeDashboard.native.js.map +1 -1
  13. package/dist/cjs/FrappeForm.cjs +3 -1
  14. package/dist/cjs/FrappeForm.native.js +3 -1
  15. package/dist/cjs/FrappeForm.native.js.map +1 -1
  16. package/dist/cjs/FrappeGantt.cjs +5 -7
  17. package/dist/cjs/FrappeGantt.native.js +5 -9
  18. package/dist/cjs/FrappeGantt.native.js.map +1 -1
  19. package/dist/cjs/FrappeImageView.cjs +3 -1
  20. package/dist/cjs/FrappeImageView.native.js +3 -1
  21. package/dist/cjs/FrappeImageView.native.js.map +1 -1
  22. package/dist/cjs/FrappeKPICard.cjs +2 -0
  23. package/dist/cjs/FrappeKPICard.native.js +2 -0
  24. package/dist/cjs/FrappeKPICard.native.js.map +1 -1
  25. package/dist/cjs/FrappeKanban.cjs +3 -1
  26. package/dist/cjs/FrappeKanban.native.js +3 -1
  27. package/dist/cjs/FrappeKanban.native.js.map +1 -1
  28. package/dist/cjs/FrappeLikedBy.cjs +2 -0
  29. package/dist/cjs/FrappeLikedBy.native.js +2 -0
  30. package/dist/cjs/FrappeLikedBy.native.js.map +1 -1
  31. package/dist/cjs/FrappeList.cjs +3 -1
  32. package/dist/cjs/FrappeList.native.js +3 -1
  33. package/dist/cjs/FrappeList.native.js.map +1 -1
  34. package/dist/cjs/FrappeListView.cjs +6 -12
  35. package/dist/cjs/FrappeListView.native.js +13 -12
  36. package/dist/cjs/FrappeListView.native.js.map +1 -1
  37. package/dist/cjs/FrappeProvider.cjs +5 -1
  38. package/dist/cjs/FrappeProvider.native.js +5 -1
  39. package/dist/cjs/FrappeProvider.native.js.map +1 -1
  40. package/dist/cjs/FrappeReportBuilder.cjs +2 -0
  41. package/dist/cjs/FrappeReportBuilder.native.js +2 -0
  42. package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
  43. package/dist/cjs/FrappeReportView.cjs +23 -45
  44. package/dist/cjs/FrappeReportView.native.js +35 -50
  45. package/dist/cjs/FrappeReportView.native.js.map +1 -1
  46. package/dist/cjs/FrappeTable.cjs +105 -31
  47. package/dist/cjs/FrappeTable.native.js +140 -42
  48. package/dist/cjs/FrappeTable.native.js.map +1 -1
  49. package/dist/cjs/FrappeTreeView.cjs +3 -1
  50. package/dist/cjs/FrappeTreeView.native.js +3 -1
  51. package/dist/cjs/FrappeTreeView.native.js.map +1 -1
  52. package/dist/cjs/SyncErrorNotice.cjs +76 -0
  53. package/dist/cjs/SyncErrorNotice.native.js +92 -0
  54. package/dist/cjs/SyncErrorNotice.native.js.map +1 -0
  55. package/dist/cjs/commentActions.cjs +120 -0
  56. package/dist/cjs/commentActions.native.js +129 -0
  57. package/dist/cjs/commentActions.native.js.map +1 -0
  58. package/dist/cjs/components/attachments.cjs +2 -0
  59. package/dist/cjs/components/attachments.native.js +2 -0
  60. package/dist/cjs/components/attachments.native.js.map +1 -1
  61. package/dist/cjs/components/tags.cjs +5 -0
  62. package/dist/cjs/components/tags.native.js +5 -0
  63. package/dist/cjs/components/tags.native.js.map +1 -1
  64. package/dist/cjs/components/timeline.cjs +29 -2
  65. package/dist/cjs/components/timeline.native.js +33 -2
  66. package/dist/cjs/components/timeline.native.js.map +1 -1
  67. package/dist/cjs/desk/DeskFormSidebar.cjs +2 -0
  68. package/dist/cjs/desk/DeskFormSidebar.native.js +2 -0
  69. package/dist/cjs/desk/DeskFormSidebar.native.js.map +1 -1
  70. package/dist/cjs/fieldRegistry.cjs +2 -0
  71. package/dist/cjs/fieldRegistry.native.js +2 -0
  72. package/dist/cjs/fieldRegistry.native.js.map +1 -1
  73. package/dist/cjs/fields/Date/index.cjs +71 -7
  74. package/dist/cjs/fields/Date/index.native.js +325 -37
  75. package/dist/cjs/fields/Date/index.native.js.map +1 -1
  76. package/dist/cjs/fields/Date/types.cjs +18 -0
  77. package/dist/cjs/fields/Date/types.native.js +21 -0
  78. package/dist/cjs/fields/Date/types.native.js.map +1 -0
  79. package/dist/cjs/fields/Date/wire.cjs +4 -8
  80. package/dist/cjs/fields/Date/wire.native.js +4 -11
  81. package/dist/cjs/fields/Date/wire.native.js.map +1 -1
  82. package/dist/cjs/fields/DynamicLink.cjs +2 -0
  83. package/dist/cjs/fields/DynamicLink.native.js +2 -0
  84. package/dist/cjs/fields/DynamicLink.native.js.map +1 -1
  85. package/dist/cjs/fields/FileUpload.cjs +2 -0
  86. package/dist/cjs/fields/FileUpload.native.js +2 -0
  87. package/dist/cjs/fields/FileUpload.native.js.map +1 -1
  88. package/dist/cjs/fields/TableMultiSelect.cjs +6 -1
  89. package/dist/cjs/fields/TableMultiSelect.native.js +6 -1
  90. package/dist/cjs/fields/TableMultiSelect.native.js.map +1 -1
  91. package/dist/cjs/frappeStoryTarget.cjs +88 -0
  92. package/dist/cjs/frappeStoryTarget.native.js +100 -0
  93. package/dist/cjs/frappeStoryTarget.native.js.map +1 -1
  94. package/dist/cjs/frappeUsers.cjs +2 -0
  95. package/dist/cjs/frappeUsers.native.js +2 -0
  96. package/dist/cjs/frappeUsers.native.js.map +1 -1
  97. package/dist/cjs/index.cjs +17 -4
  98. package/dist/cjs/index.native.js +17 -4
  99. package/dist/cjs/index.native.js.map +1 -1
  100. package/dist/cjs/liveErrors.cjs +5 -1
  101. package/dist/cjs/liveErrors.native.js +5 -1
  102. package/dist/cjs/liveErrors.native.js.map +1 -1
  103. package/dist/cjs/useFrappeAggregate.cjs +13 -1
  104. package/dist/cjs/useFrappeAggregate.native.js +14 -1
  105. package/dist/cjs/useFrappeAggregate.native.js.map +1 -1
  106. package/dist/cjs/useFrappeFacets.cjs +329 -0
  107. package/dist/cjs/useFrappeFacets.native.js +632 -0
  108. package/dist/cjs/useFrappeFacets.native.js.map +1 -0
  109. package/dist/esm/FrappeAssignment.mjs +2 -0
  110. package/dist/esm/FrappeAssignment.mjs.map +1 -1
  111. package/dist/esm/FrappeAssignment.native.js +2 -0
  112. package/dist/esm/FrappeAssignment.native.js.map +1 -1
  113. package/dist/esm/FrappeCalendar.mjs +6 -8
  114. package/dist/esm/FrappeCalendar.mjs.map +1 -1
  115. package/dist/esm/FrappeCalendar.native.js +6 -10
  116. package/dist/esm/FrappeCalendar.native.js.map +1 -1
  117. package/dist/esm/FrappeChartCard.mjs +2 -0
  118. package/dist/esm/FrappeChartCard.mjs.map +1 -1
  119. package/dist/esm/FrappeChartCard.native.js +2 -0
  120. package/dist/esm/FrappeChartCard.native.js.map +1 -1
  121. package/dist/esm/FrappeDashboard.mjs +12 -19
  122. package/dist/esm/FrappeDashboard.mjs.map +1 -1
  123. package/dist/esm/FrappeDashboard.native.js +23 -21
  124. package/dist/esm/FrappeDashboard.native.js.map +1 -1
  125. package/dist/esm/FrappeForm.mjs +3 -1
  126. package/dist/esm/FrappeForm.mjs.map +1 -1
  127. package/dist/esm/FrappeForm.native.js +3 -1
  128. package/dist/esm/FrappeForm.native.js.map +1 -1
  129. package/dist/esm/FrappeGantt.mjs +6 -8
  130. package/dist/esm/FrappeGantt.mjs.map +1 -1
  131. package/dist/esm/FrappeGantt.native.js +6 -10
  132. package/dist/esm/FrappeGantt.native.js.map +1 -1
  133. package/dist/esm/FrappeImageView.mjs +3 -1
  134. package/dist/esm/FrappeImageView.mjs.map +1 -1
  135. package/dist/esm/FrappeImageView.native.js +3 -1
  136. package/dist/esm/FrappeImageView.native.js.map +1 -1
  137. package/dist/esm/FrappeKPICard.mjs +2 -0
  138. package/dist/esm/FrappeKPICard.mjs.map +1 -1
  139. package/dist/esm/FrappeKPICard.native.js +2 -0
  140. package/dist/esm/FrappeKPICard.native.js.map +1 -1
  141. package/dist/esm/FrappeKanban.mjs +3 -1
  142. package/dist/esm/FrappeKanban.mjs.map +1 -1
  143. package/dist/esm/FrappeKanban.native.js +3 -1
  144. package/dist/esm/FrappeKanban.native.js.map +1 -1
  145. package/dist/esm/FrappeLikedBy.mjs +2 -0
  146. package/dist/esm/FrappeLikedBy.mjs.map +1 -1
  147. package/dist/esm/FrappeLikedBy.native.js +2 -0
  148. package/dist/esm/FrappeLikedBy.native.js.map +1 -1
  149. package/dist/esm/FrappeList.mjs +3 -1
  150. package/dist/esm/FrappeList.mjs.map +1 -1
  151. package/dist/esm/FrappeList.native.js +3 -1
  152. package/dist/esm/FrappeList.native.js.map +1 -1
  153. package/dist/esm/FrappeListView.mjs +7 -13
  154. package/dist/esm/FrappeListView.mjs.map +1 -1
  155. package/dist/esm/FrappeListView.native.js +15 -14
  156. package/dist/esm/FrappeListView.native.js.map +1 -1
  157. package/dist/esm/FrappeProvider.mjs +5 -1
  158. package/dist/esm/FrappeProvider.mjs.map +1 -1
  159. package/dist/esm/FrappeProvider.native.js +5 -1
  160. package/dist/esm/FrappeProvider.native.js.map +1 -1
  161. package/dist/esm/FrappeReportBuilder.mjs +2 -0
  162. package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
  163. package/dist/esm/FrappeReportBuilder.native.js +2 -0
  164. package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
  165. package/dist/esm/FrappeReportView.mjs +26 -49
  166. package/dist/esm/FrappeReportView.mjs.map +1 -1
  167. package/dist/esm/FrappeReportView.native.js +38 -54
  168. package/dist/esm/FrappeReportView.native.js.map +1 -1
  169. package/dist/esm/FrappeTable.mjs +107 -33
  170. package/dist/esm/FrappeTable.mjs.map +1 -1
  171. package/dist/esm/FrappeTable.native.js +142 -44
  172. package/dist/esm/FrappeTable.native.js.map +1 -1
  173. package/dist/esm/FrappeTreeView.mjs +3 -1
  174. package/dist/esm/FrappeTreeView.mjs.map +1 -1
  175. package/dist/esm/FrappeTreeView.native.js +3 -1
  176. package/dist/esm/FrappeTreeView.native.js.map +1 -1
  177. package/dist/esm/SyncErrorNotice.mjs +51 -0
  178. package/dist/esm/SyncErrorNotice.mjs.map +1 -0
  179. package/dist/esm/SyncErrorNotice.native.js +53 -0
  180. package/dist/esm/SyncErrorNotice.native.js.map +1 -0
  181. package/dist/esm/commentActions.mjs +95 -0
  182. package/dist/esm/commentActions.mjs.map +1 -0
  183. package/dist/esm/commentActions.native.js +101 -0
  184. package/dist/esm/commentActions.native.js.map +1 -0
  185. package/dist/esm/components/attachments.mjs +2 -0
  186. package/dist/esm/components/attachments.mjs.map +1 -1
  187. package/dist/esm/components/attachments.native.js +2 -0
  188. package/dist/esm/components/attachments.native.js.map +1 -1
  189. package/dist/esm/components/tags.mjs +5 -0
  190. package/dist/esm/components/tags.mjs.map +1 -1
  191. package/dist/esm/components/tags.native.js +5 -0
  192. package/dist/esm/components/tags.native.js.map +1 -1
  193. package/dist/esm/components/timeline.mjs +31 -4
  194. package/dist/esm/components/timeline.mjs.map +1 -1
  195. package/dist/esm/components/timeline.native.js +35 -4
  196. package/dist/esm/components/timeline.native.js.map +1 -1
  197. package/dist/esm/desk/DeskFormSidebar.mjs +2 -0
  198. package/dist/esm/desk/DeskFormSidebar.mjs.map +1 -1
  199. package/dist/esm/desk/DeskFormSidebar.native.js +2 -0
  200. package/dist/esm/desk/DeskFormSidebar.native.js.map +1 -1
  201. package/dist/esm/fieldRegistry.mjs +2 -0
  202. package/dist/esm/fieldRegistry.mjs.map +1 -1
  203. package/dist/esm/fieldRegistry.native.js +2 -0
  204. package/dist/esm/fieldRegistry.native.js.map +1 -1
  205. package/dist/esm/fields/Date/index.mjs +73 -9
  206. package/dist/esm/fields/Date/index.mjs.map +1 -1
  207. package/dist/esm/fields/Date/index.native.js +315 -38
  208. package/dist/esm/fields/Date/index.native.js.map +1 -1
  209. package/dist/esm/fields/Date/types.mjs +2 -0
  210. package/dist/esm/fields/Date/types.mjs.map +1 -0
  211. package/dist/esm/fields/Date/types.native.js +2 -0
  212. package/dist/esm/fields/Date/types.native.js.map +1 -0
  213. package/dist/esm/fields/Date/wire.mjs +4 -8
  214. package/dist/esm/fields/Date/wire.mjs.map +1 -1
  215. package/dist/esm/fields/Date/wire.native.js +4 -11
  216. package/dist/esm/fields/Date/wire.native.js.map +1 -1
  217. package/dist/esm/fields/DynamicLink.mjs +2 -0
  218. package/dist/esm/fields/DynamicLink.mjs.map +1 -1
  219. package/dist/esm/fields/DynamicLink.native.js +2 -0
  220. package/dist/esm/fields/DynamicLink.native.js.map +1 -1
  221. package/dist/esm/fields/FileUpload.mjs +2 -0
  222. package/dist/esm/fields/FileUpload.mjs.map +1 -1
  223. package/dist/esm/fields/FileUpload.native.js +2 -0
  224. package/dist/esm/fields/FileUpload.native.js.map +1 -1
  225. package/dist/esm/fields/TableMultiSelect.mjs +7 -2
  226. package/dist/esm/fields/TableMultiSelect.mjs.map +1 -1
  227. package/dist/esm/fields/TableMultiSelect.native.js +7 -2
  228. package/dist/esm/fields/TableMultiSelect.native.js.map +1 -1
  229. package/dist/esm/frappeStoryTarget.mjs +88 -2
  230. package/dist/esm/frappeStoryTarget.mjs.map +1 -1
  231. package/dist/esm/frappeStoryTarget.native.js +100 -2
  232. package/dist/esm/frappeStoryTarget.native.js.map +1 -1
  233. package/dist/esm/frappeUsers.mjs +2 -0
  234. package/dist/esm/frappeUsers.mjs.map +1 -1
  235. package/dist/esm/frappeUsers.native.js +2 -0
  236. package/dist/esm/frappeUsers.native.js.map +1 -1
  237. package/dist/esm/index.mjs +7 -3
  238. package/dist/esm/index.mjs.map +1 -1
  239. package/dist/esm/index.native.js +7 -3
  240. package/dist/esm/index.native.js.map +1 -1
  241. package/dist/esm/liveErrors.mjs +5 -1
  242. package/dist/esm/liveErrors.mjs.map +1 -1
  243. package/dist/esm/liveErrors.native.js +5 -1
  244. package/dist/esm/liveErrors.native.js.map +1 -1
  245. package/dist/esm/useFrappeAggregate.mjs +13 -1
  246. package/dist/esm/useFrappeAggregate.mjs.map +1 -1
  247. package/dist/esm/useFrappeAggregate.native.js +14 -1
  248. package/dist/esm/useFrappeAggregate.native.js.map +1 -1
  249. package/dist/esm/useFrappeFacets.mjs +299 -0
  250. package/dist/esm/useFrappeFacets.mjs.map +1 -0
  251. package/dist/esm/useFrappeFacets.native.js +599 -0
  252. package/dist/esm/useFrappeFacets.native.js.map +1 -0
  253. package/dist/jsx/FrappeAssignment.mjs +2 -0
  254. package/dist/jsx/FrappeAssignment.mjs.map +1 -1
  255. package/dist/jsx/FrappeAssignment.native.js +2 -0
  256. package/dist/jsx/FrappeAssignment.native.js.map +1 -1
  257. package/dist/jsx/FrappeCalendar.mjs +6 -8
  258. package/dist/jsx/FrappeCalendar.mjs.map +1 -1
  259. package/dist/jsx/FrappeCalendar.native.js +5 -9
  260. package/dist/jsx/FrappeCalendar.native.js.map +1 -1
  261. package/dist/jsx/FrappeChartCard.mjs +2 -0
  262. package/dist/jsx/FrappeChartCard.mjs.map +1 -1
  263. package/dist/jsx/FrappeChartCard.native.js +2 -0
  264. package/dist/jsx/FrappeChartCard.native.js.map +1 -1
  265. package/dist/jsx/FrappeDashboard.mjs +12 -19
  266. package/dist/jsx/FrappeDashboard.mjs.map +1 -1
  267. package/dist/jsx/FrappeDashboard.native.js +20 -22
  268. package/dist/jsx/FrappeDashboard.native.js.map +1 -1
  269. package/dist/jsx/FrappeForm.mjs +3 -1
  270. package/dist/jsx/FrappeForm.mjs.map +1 -1
  271. package/dist/jsx/FrappeForm.native.js +3 -1
  272. package/dist/jsx/FrappeForm.native.js.map +1 -1
  273. package/dist/jsx/FrappeGantt.mjs +6 -8
  274. package/dist/jsx/FrappeGantt.mjs.map +1 -1
  275. package/dist/jsx/FrappeGantt.native.js +5 -9
  276. package/dist/jsx/FrappeGantt.native.js.map +1 -1
  277. package/dist/jsx/FrappeImageView.mjs +3 -1
  278. package/dist/jsx/FrappeImageView.mjs.map +1 -1
  279. package/dist/jsx/FrappeImageView.native.js +3 -1
  280. package/dist/jsx/FrappeImageView.native.js.map +1 -1
  281. package/dist/jsx/FrappeKPICard.mjs +2 -0
  282. package/dist/jsx/FrappeKPICard.mjs.map +1 -1
  283. package/dist/jsx/FrappeKPICard.native.js +2 -0
  284. package/dist/jsx/FrappeKPICard.native.js.map +1 -1
  285. package/dist/jsx/FrappeKanban.mjs +3 -1
  286. package/dist/jsx/FrappeKanban.mjs.map +1 -1
  287. package/dist/jsx/FrappeKanban.native.js +3 -1
  288. package/dist/jsx/FrappeKanban.native.js.map +1 -1
  289. package/dist/jsx/FrappeLikedBy.mjs +2 -0
  290. package/dist/jsx/FrappeLikedBy.mjs.map +1 -1
  291. package/dist/jsx/FrappeLikedBy.native.js +2 -0
  292. package/dist/jsx/FrappeLikedBy.native.js.map +1 -1
  293. package/dist/jsx/FrappeList.mjs +3 -1
  294. package/dist/jsx/FrappeList.mjs.map +1 -1
  295. package/dist/jsx/FrappeList.native.js +3 -1
  296. package/dist/jsx/FrappeList.native.js.map +1 -1
  297. package/dist/jsx/FrappeListView.mjs +7 -13
  298. package/dist/jsx/FrappeListView.mjs.map +1 -1
  299. package/dist/jsx/FrappeListView.native.js +13 -12
  300. package/dist/jsx/FrappeListView.native.js.map +1 -1
  301. package/dist/jsx/FrappeProvider.mjs +5 -1
  302. package/dist/jsx/FrappeProvider.mjs.map +1 -1
  303. package/dist/jsx/FrappeProvider.native.js +5 -1
  304. package/dist/jsx/FrappeProvider.native.js.map +1 -1
  305. package/dist/jsx/FrappeReportBuilder.mjs +2 -0
  306. package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
  307. package/dist/jsx/FrappeReportBuilder.native.js +2 -0
  308. package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
  309. package/dist/jsx/FrappeReportView.mjs +26 -49
  310. package/dist/jsx/FrappeReportView.mjs.map +1 -1
  311. package/dist/jsx/FrappeReportView.native.js +35 -50
  312. package/dist/jsx/FrappeReportView.native.js.map +1 -1
  313. package/dist/jsx/FrappeTable.mjs +107 -33
  314. package/dist/jsx/FrappeTable.mjs.map +1 -1
  315. package/dist/jsx/FrappeTable.native.js +140 -42
  316. package/dist/jsx/FrappeTable.native.js.map +1 -1
  317. package/dist/jsx/FrappeTreeView.mjs +3 -1
  318. package/dist/jsx/FrappeTreeView.mjs.map +1 -1
  319. package/dist/jsx/FrappeTreeView.native.js +3 -1
  320. package/dist/jsx/FrappeTreeView.native.js.map +1 -1
  321. package/dist/jsx/SyncErrorNotice.mjs +51 -0
  322. package/dist/jsx/SyncErrorNotice.mjs.map +1 -0
  323. package/dist/jsx/SyncErrorNotice.native.js +92 -0
  324. package/dist/jsx/SyncErrorNotice.native.js.map +1 -0
  325. package/dist/jsx/commentActions.mjs +95 -0
  326. package/dist/jsx/commentActions.mjs.map +1 -0
  327. package/dist/jsx/commentActions.native.js +129 -0
  328. package/dist/jsx/commentActions.native.js.map +1 -0
  329. package/dist/jsx/components/attachments.mjs +2 -0
  330. package/dist/jsx/components/attachments.mjs.map +1 -1
  331. package/dist/jsx/components/attachments.native.js +2 -0
  332. package/dist/jsx/components/attachments.native.js.map +1 -1
  333. package/dist/jsx/components/tags.mjs +5 -0
  334. package/dist/jsx/components/tags.mjs.map +1 -1
  335. package/dist/jsx/components/tags.native.js +5 -0
  336. package/dist/jsx/components/tags.native.js.map +1 -1
  337. package/dist/jsx/components/timeline.mjs +31 -4
  338. package/dist/jsx/components/timeline.mjs.map +1 -1
  339. package/dist/jsx/components/timeline.native.js +33 -2
  340. package/dist/jsx/components/timeline.native.js.map +1 -1
  341. package/dist/jsx/desk/DeskFormSidebar.mjs +2 -0
  342. package/dist/jsx/desk/DeskFormSidebar.mjs.map +1 -1
  343. package/dist/jsx/desk/DeskFormSidebar.native.js +2 -0
  344. package/dist/jsx/desk/DeskFormSidebar.native.js.map +1 -1
  345. package/dist/jsx/fieldRegistry.mjs +2 -0
  346. package/dist/jsx/fieldRegistry.mjs.map +1 -1
  347. package/dist/jsx/fieldRegistry.native.js +2 -0
  348. package/dist/jsx/fieldRegistry.native.js.map +1 -1
  349. package/dist/jsx/fields/Date/index.mjs +73 -9
  350. package/dist/jsx/fields/Date/index.mjs.map +1 -1
  351. package/dist/jsx/fields/Date/index.native.js +325 -37
  352. package/dist/jsx/fields/Date/index.native.js.map +1 -1
  353. package/dist/jsx/fields/Date/types.mjs +2 -0
  354. package/dist/jsx/fields/Date/types.mjs.map +1 -0
  355. package/dist/jsx/fields/Date/types.native.js +21 -0
  356. package/dist/jsx/fields/Date/types.native.js.map +1 -0
  357. package/dist/jsx/fields/Date/wire.mjs +4 -8
  358. package/dist/jsx/fields/Date/wire.mjs.map +1 -1
  359. package/dist/jsx/fields/Date/wire.native.js +4 -11
  360. package/dist/jsx/fields/Date/wire.native.js.map +1 -1
  361. package/dist/jsx/fields/DynamicLink.mjs +2 -0
  362. package/dist/jsx/fields/DynamicLink.mjs.map +1 -1
  363. package/dist/jsx/fields/DynamicLink.native.js +2 -0
  364. package/dist/jsx/fields/DynamicLink.native.js.map +1 -1
  365. package/dist/jsx/fields/FileUpload.mjs +2 -0
  366. package/dist/jsx/fields/FileUpload.mjs.map +1 -1
  367. package/dist/jsx/fields/FileUpload.native.js +2 -0
  368. package/dist/jsx/fields/FileUpload.native.js.map +1 -1
  369. package/dist/jsx/fields/TableMultiSelect.mjs +7 -2
  370. package/dist/jsx/fields/TableMultiSelect.mjs.map +1 -1
  371. package/dist/jsx/fields/TableMultiSelect.native.js +6 -1
  372. package/dist/jsx/fields/TableMultiSelect.native.js.map +1 -1
  373. package/dist/jsx/frappeStoryTarget.mjs +88 -2
  374. package/dist/jsx/frappeStoryTarget.mjs.map +1 -1
  375. package/dist/jsx/frappeStoryTarget.native.js +100 -0
  376. package/dist/jsx/frappeStoryTarget.native.js.map +1 -1
  377. package/dist/jsx/frappeUsers.mjs +2 -0
  378. package/dist/jsx/frappeUsers.mjs.map +1 -1
  379. package/dist/jsx/frappeUsers.native.js +2 -0
  380. package/dist/jsx/frappeUsers.native.js.map +1 -1
  381. package/dist/jsx/index.js +7 -3
  382. package/dist/jsx/index.js.map +1 -1
  383. package/dist/jsx/index.mjs +7 -3
  384. package/dist/jsx/index.mjs.map +1 -1
  385. package/dist/jsx/index.native.js +17 -4
  386. package/dist/jsx/index.native.js.map +1 -1
  387. package/dist/jsx/liveErrors.mjs +5 -1
  388. package/dist/jsx/liveErrors.mjs.map +1 -1
  389. package/dist/jsx/liveErrors.native.js +5 -1
  390. package/dist/jsx/liveErrors.native.js.map +1 -1
  391. package/dist/jsx/useFrappeAggregate.mjs +13 -1
  392. package/dist/jsx/useFrappeAggregate.mjs.map +1 -1
  393. package/dist/jsx/useFrappeAggregate.native.js +14 -1
  394. package/dist/jsx/useFrappeAggregate.native.js.map +1 -1
  395. package/dist/jsx/useFrappeFacets.mjs +299 -0
  396. package/dist/jsx/useFrappeFacets.mjs.map +1 -0
  397. package/dist/jsx/useFrappeFacets.native.js +632 -0
  398. package/dist/jsx/useFrappeFacets.native.js.map +1 -0
  399. package/package.json +21 -21
  400. package/src/FormSidebar.stories.tsx +88 -10
  401. package/src/FrappeAssignment.tsx +2 -0
  402. package/src/FrappeCalendar.tsx +12 -7
  403. package/src/FrappeChartCard.tsx +9 -2
  404. package/src/FrappeDashboard.spec.tsx +95 -0
  405. package/src/FrappeDashboard.tsx +20 -41
  406. package/src/FrappeForm.stories.tsx +53 -46
  407. package/src/FrappeForm.tsx +11 -1
  408. package/src/FrappeGantt.tsx +12 -7
  409. package/src/FrappeImageView.tsx +11 -1
  410. package/src/FrappeKPICard.stories.tsx +4 -1
  411. package/src/FrappeKPICard.tsx +2 -0
  412. package/src/FrappeKanban.tsx +4 -0
  413. package/src/FrappeLikedBy.stories.tsx +26 -10
  414. package/src/FrappeLikedBy.tsx +2 -0
  415. package/src/FrappeList.tsx +11 -1
  416. package/src/FrappeListView.spec.tsx +144 -0
  417. package/src/FrappeListView.tsx +11 -23
  418. package/src/FrappeProvider.tsx +30 -2
  419. package/src/FrappeReportBuilder.tsx +2 -0
  420. package/src/FrappeReportView.spec.tsx +140 -16
  421. package/src/FrappeReportView.stories.tsx +12 -6
  422. package/src/FrappeReportView.tsx +62 -61
  423. package/src/FrappeTable.keepRows.spec.tsx +200 -0
  424. package/src/FrappeTable.retryStorm.spec.tsx +140 -0
  425. package/src/FrappeTable.spec.tsx +222 -0
  426. package/src/FrappeTable.tsx +211 -54
  427. package/src/FrappeTreeView.tsx +11 -1
  428. package/src/SyncErrorNotice.spec.tsx +60 -0
  429. package/src/SyncErrorNotice.tsx +81 -0
  430. package/src/commentActions.ts +157 -0
  431. package/src/components/attachments.tsx +2 -0
  432. package/src/components/tags.tsx +14 -1
  433. package/src/components/timeline.optimistic.spec.tsx +106 -0
  434. package/src/components/timeline.tsx +36 -4
  435. package/src/desk/DeskFormSidebar.tsx +2 -0
  436. package/src/fieldRegistry.ts +2 -0
  437. package/src/fields/Date/index.native.tsx +410 -0
  438. package/src/fields/Date/index.tsx +138 -57
  439. package/src/fields/Date/nativePresentation.spec.tsx +251 -0
  440. package/src/fields/Date/typeParity.spec.tsx +480 -0
  441. package/src/fields/Date/types.ts +53 -0
  442. package/src/fields/Date/wire.spec.ts +63 -1
  443. package/src/fields/Date/wire.ts +17 -17
  444. package/src/fields/DynamicLink.tsx +2 -0
  445. package/src/fields/FileUpload.tsx +2 -0
  446. package/src/fields/TableMultiSelect.tsx +6 -3
  447. package/src/frappeStoryTarget.tsx +104 -1
  448. package/src/frappeUsers.ts +2 -0
  449. package/src/index.spec.ts +62 -0
  450. package/src/index.ts +36 -2
  451. package/src/liveErrors.spec.ts +50 -0
  452. package/src/liveErrors.ts +11 -1
  453. package/src/useFrappeAggregate.ts +20 -1
  454. package/src/useFrappeFacets.spec.tsx +436 -0
  455. package/src/useFrappeFacets.ts +568 -0
  456. package/types/FrappeAssignment.d.ts.map +1 -1
  457. package/types/FrappeCalendar.d.ts.map +1 -1
  458. package/types/FrappeChartCard.d.ts +1 -2
  459. package/types/FrappeChartCard.d.ts.map +1 -1
  460. package/types/FrappeDashboard.d.ts +0 -1
  461. package/types/FrappeDashboard.d.ts.map +1 -1
  462. package/types/FrappeForm.d.ts.map +1 -1
  463. package/types/FrappeGantt.d.ts.map +1 -1
  464. package/types/FrappeKPICard.d.ts.map +1 -1
  465. package/types/FrappeKanban.d.ts.map +1 -1
  466. package/types/FrappeLikedBy.d.ts.map +1 -1
  467. package/types/FrappeList.d.ts.map +1 -1
  468. package/types/FrappeListView.d.ts.map +1 -1
  469. package/types/FrappeProvider.d.ts +18 -1
  470. package/types/FrappeProvider.d.ts.map +1 -1
  471. package/types/FrappeReportBuilder.d.ts.map +1 -1
  472. package/types/FrappeReportView.d.ts +22 -15
  473. package/types/FrappeReportView.d.ts.map +1 -1
  474. package/types/FrappeTable.d.ts +21 -2
  475. package/types/FrappeTable.d.ts.map +1 -1
  476. package/types/FrappeTreeView.d.ts.map +1 -1
  477. package/types/SyncErrorNotice.d.ts +38 -0
  478. package/types/SyncErrorNotice.d.ts.map +1 -0
  479. package/types/commentActions.d.ts +46 -0
  480. package/types/commentActions.d.ts.map +1 -0
  481. package/types/components/attachments.d.ts.map +1 -1
  482. package/types/components/tags.d.ts.map +1 -1
  483. package/types/components/timeline.d.ts.map +1 -1
  484. package/types/desk/DeskFormSidebar.d.ts.map +1 -1
  485. package/types/fieldRegistry.d.ts.map +1 -1
  486. package/types/fields/Date/index.d.ts +22 -12
  487. package/types/fields/Date/index.d.ts.map +1 -1
  488. package/types/fields/Date/index.native.d.ts +14 -0
  489. package/types/fields/Date/index.native.d.ts.map +1 -0
  490. package/types/fields/Date/types.d.ts +27 -0
  491. package/types/fields/Date/types.d.ts.map +1 -0
  492. package/types/fields/Date/wire.d.ts +13 -9
  493. package/types/fields/Date/wire.d.ts.map +1 -1
  494. package/types/fields/DynamicLink.d.ts.map +1 -1
  495. package/types/fields/FileUpload.d.ts.map +1 -1
  496. package/types/fields/TableMultiSelect.d.ts.map +1 -1
  497. package/types/frappeStoryTarget.d.ts +19 -1
  498. package/types/frappeStoryTarget.d.ts.map +1 -1
  499. package/types/frappeUsers.d.ts.map +1 -1
  500. package/types/index.d.ts +6 -2
  501. package/types/index.d.ts.map +1 -1
  502. package/types/liveErrors.d.ts +4 -0
  503. package/types/liveErrors.d.ts.map +1 -1
  504. package/types/useFrappeAggregate.d.ts +4 -0
  505. package/types/useFrappeAggregate.d.ts.map +1 -1
  506. package/types/useFrappeFacets.d.ts +143 -0
  507. package/types/useFrappeFacets.d.ts.map +1 -0
  508. package/dist/cjs/fields/Date/date.native.js +0 -277
  509. package/dist/cjs/fields/Date/date.native.js.map +0 -1
  510. package/dist/esm/fields/Date/date.native.js +0 -238
  511. package/dist/esm/fields/Date/date.native.js.map +0 -1
  512. package/dist/jsx/fields/Date/date.native.js +0 -277
  513. package/dist/jsx/fields/Date/date.native.js.map +0 -1
  514. package/src/fields/Date/date.native.tsx +0 -299
  515. package/types/fields/Date/date.native.d.ts +0 -19
  516. package/types/fields/Date/date.native.d.ts.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  FrappeApp,
3
3
  Order,
4
+ matchesFilters,
4
5
  useDocTypeMeta,
5
6
  useFrappeCollection,
6
7
  useFrappeDoctypeEvents,
@@ -19,7 +20,7 @@ import { AsyncBoundary } from "@multiplatform.one/components";
19
20
  import { useResolvedKnobs } from "@multiplatform.one/theme";
20
21
  import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
21
22
  import { useTranslation } from "react-i18next";
22
- import { Text, View, XStack, YStack } from "tamagui";
23
+ import { XStack, YStack } from "tamagui";
23
24
  import { FilterManager, SavedFilterDropdown, useSavedFilters } from "@multiplatform.one/table";
24
25
  import { useFrappeConfig } from "./FrappeProvider";
25
26
  import { FrappeReconnectIndicator } from "./FrappeReconnectIndicator";
@@ -56,7 +57,14 @@ export interface FrappeTableProps<TDoc extends { name: string; doctype: string }
56
57
  fields?: readonly (keyof TDoc & string)[];
57
58
  /** Initial filter criteria */
58
59
  filters?: Partial<TDoc> | Array<[keyof TDoc & string, string, any]>;
59
- /** Callback when a row is clicked */
60
+ /**
61
+ * Callback when a row is activated. Rides DataTable's first-class
62
+ * `onRowPress` contract: the whole row is ONE pressable unit (no
63
+ * per-cell pressable wrappers), the row set exposes a single roving tab
64
+ * stop (LC-49), Enter AND Space activate the focused row (LC-50), and
65
+ * presses on inner interactive controls (selection checkboxes, buttons,
66
+ * links) never fire the row press.
67
+ */
60
68
  onRowClick?: (row: Partial<TDoc>) => void;
61
69
  /** Custom loading indicator */
62
70
  loading?: React.ReactNode;
@@ -79,6 +87,18 @@ export interface FrappeTableProps<TDoc extends { name: string; doctype: string }
79
87
  * Used for server-side fetches when serverSide is true.
80
88
  */
81
89
  onStateChange?: (state: DataTableState) => void;
90
+ /**
91
+ * In-place refresh channel: bump the value to re-run the current query
92
+ * (serverSide mode) without remounting — rows stay rendered and swap when
93
+ * the response lands (keep-rows-and-reconcile; never key the table on a
94
+ * refresh generation).
95
+ */
96
+ refreshToken?: unknown;
97
+ /**
98
+ * Density. DataTableRoot accepts this; it is not on DataTableProps, so
99
+ * FrappeTable has to name it or callers (backoffice list) fail the build.
100
+ */
101
+ compact?: boolean;
82
102
  }
83
103
 
84
104
  /**
@@ -182,6 +202,26 @@ function isOperatorFilterValue(value: unknown): value is FilterValue {
182
202
  );
183
203
  }
184
204
 
205
+ /** Sort the current page locally so a sort change is visible before reconcile. */
206
+ function sortRowsLocally<T extends Record<string, unknown>>(
207
+ rows: T[],
208
+ orderBy: Array<{ field: string; order: Order }> | undefined,
209
+ ): T[] {
210
+ if (!orderBy?.length) return rows;
211
+ return [...rows].sort((a, b) => {
212
+ for (const { field, order } of orderBy) {
213
+ const av = a[field];
214
+ const bv = b[field];
215
+ if (av == null && bv == null) continue;
216
+ if (av == null) return order === Order.Asc ? -1 : 1;
217
+ if (bv == null) return order === Order.Asc ? 1 : -1;
218
+ if (av < bv) return order === Order.Asc ? -1 : 1;
219
+ if (av > bv) return order === Order.Asc ? 1 : -1;
220
+ }
221
+ return 0;
222
+ });
223
+ }
224
+
185
225
  /** Convert DataTableState to Frappe API params */
186
226
  function stateToFrappeParams(state: DataTableState) {
187
227
  const { pagination, sorting, filters, columnFilters, globalFilter } = state;
@@ -233,9 +273,11 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
233
273
  userId,
234
274
  serverSide = false,
235
275
  onStateChange: onStateChangeProp,
276
+ refreshToken,
236
277
  paginationMode = "pages",
237
278
  loadPage: loadPageProp,
238
279
  filterableFields: filterableFieldsProp,
280
+ layout,
239
281
  ...dataTableProps
240
282
  }: FrappeTableProps<TDoc>) {
241
283
  // Infinite + serverSide: DataTable's page cache drives fetching through a
@@ -250,6 +292,20 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
250
292
  >(initialFilters);
251
293
  const config = useFrappeConfig();
252
294
 
295
+ // Adopt `filters` prop CONTENT changes in place (client/live-collection
296
+ // mode) — the keep-rows counterpart of DataTable's controlledState: outer
297
+ // filter surfaces update the query without remounting the table. Content-
298
+ // keyed so unstable array identities from hosts are safe; saved-filter
299
+ // selections (setActiveFilters below) are not overridden by echoes.
300
+ const filtersPropKey = initialFilters !== undefined ? JSON.stringify(initialFilters) : undefined;
301
+ const adoptedFiltersPropRef = useRef(filtersPropKey);
302
+ useEffect(() => {
303
+ if (filtersPropKey === undefined) return;
304
+ if (adoptedFiltersPropRef.current === filtersPropKey) return;
305
+ adoptedFiltersPropRef.current = filtersPropKey;
306
+ setActiveFilters(JSON.parse(filtersPropKey));
307
+ }, [filtersPropKey]);
308
+
253
309
  const finalBaseURL = baseURL ?? config?.baseURL;
254
310
  const finalAuth = auth ?? config?.auth;
255
311
  const finalSyncOptions = syncOptions ?? config?.syncOptions;
@@ -264,11 +320,21 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
264
320
  () => ({
265
321
  baseURL: finalBaseURL,
266
322
  socketPort: config?.socketPort,
323
+ siteName: config?.siteName,
324
+ socketBaseURL: config?.socketBaseURL,
267
325
  auth: finalAuth,
268
326
  syncOptions: finalSyncOptions,
269
327
  fixtures: config?.fixtures,
270
328
  }),
271
- [finalBaseURL, config?.socketPort, finalAuth, finalSyncOptions, config?.fixtures],
329
+ [
330
+ finalBaseURL,
331
+ config?.socketPort,
332
+ config?.siteName,
333
+ config?.socketBaseURL,
334
+ finalAuth,
335
+ finalSyncOptions,
336
+ config?.fixtures,
337
+ ],
272
338
  );
273
339
 
274
340
  const frappeApp = useMemo(() => {
@@ -287,12 +353,28 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
287
353
  );
288
354
  const filterableFields = filterableFieldsProp ?? metaFilterFields;
289
355
 
356
+ // Accessible row identity (DG-A11Y-06) for DataTable's row-press label
357
+ // and selection-checkbox names: the doctype's title-field value when
358
+ // meta is loaded, else the document name. An empty result defers to
359
+ // DataTable's own fallbacks (first visible cell → positional "Row N").
360
+ const titleField = meta?.title_field;
361
+ const rowLabel = useCallback(
362
+ (row: Partial<TDoc>): string => {
363
+ const fromTitle = titleField ? (row as Record<string, unknown>)[titleField] : undefined;
364
+ if (typeof fromTitle === "string" && fromTitle) return fromTitle;
365
+ if (typeof fromTitle === "number") return String(fromTitle);
366
+ return typeof row.name === "string" ? row.name : "";
367
+ },
368
+ [titleField],
369
+ );
370
+
290
371
  const [serverData, setServerData] = useState<TDoc[]>([]);
291
372
  const [serverRowCount, setServerRowCount] = useState(0);
292
373
  const [serverLoading, setServerLoading] = useState(serverSide);
293
374
  // First fetch completed (success or failure) — later live refetches must
294
375
  // swap rows in place without flashing the skeleton over the table.
295
376
  const [hasLoadedOnce, setHasLoadedOnce] = useState(false);
377
+ const hasLoadedOnceRef = useRef(false);
296
378
  const lastFetchKey = useRef<string>("");
297
379
  const fetchSeq = useRef(0);
298
380
  /** In-flight serverSide request — superseding fetches and unmount abort it. */
@@ -300,6 +382,15 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
300
382
  /** Last table state fetched — realtime refetches re-run the same query. */
301
383
  const lastStateRef = useRef<DataTableState | null>(null);
302
384
 
385
+ /** Filters / sort of the last issued query — detects changes for the
386
+ * optimistic local apply below. */
387
+ const lastFiltersKey = useRef<string>("");
388
+ const lastSortKey = useRef<string>("");
389
+ /** Last successful rows — held as placeholderData while a refetch is in
390
+ * flight so a local apply that would empty the current page cannot blank
391
+ * the body. */
392
+ const lastGoodServerRows = useRef<TDoc[]>([]);
393
+
303
394
  const fetchServerData = useCallback(
304
395
  async (state: DataTableState) => {
305
396
  if (!frappeApp || !serverSide) return;
@@ -308,6 +399,41 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
308
399
  const key = JSON.stringify(params);
309
400
  if (key === lastFetchKey.current) return;
310
401
  lastFetchKey.current = key;
402
+ // Optimistic local apply (keep-rows-and-reconcile / keepPreviousData):
403
+ // filter narrows the CURRENT page when it leaves rows; sort reorders
404
+ // what is on screen; pagination keeps the current page as placeholder
405
+ // until the new page arrives. A local pass that would empty the body
406
+ // is skipped — previous rows stay mounted until reconcile.
407
+ const filtersKey = JSON.stringify(params.filters);
408
+ const sortKey = JSON.stringify(params.orderBy ?? []);
409
+ const alreadyLoaded = lastFiltersKey.current !== "" || lastGoodServerRows.current.length > 0;
410
+ if (alreadyLoaded) {
411
+ const filtersChanged = filtersKey !== lastFiltersKey.current;
412
+ const sortChanged = sortKey !== lastSortKey.current;
413
+ if (filtersChanged || sortChanged) {
414
+ setServerData((previous) => {
415
+ let next = previous;
416
+ if (filtersChanged && params.filters.length > 0) {
417
+ const narrowed = previous.filter((row) =>
418
+ matchesFilters(
419
+ row as unknown as Parameters<typeof matchesFilters>[0],
420
+ params.filters,
421
+ ),
422
+ );
423
+ if (narrowed.length > 0) next = narrowed;
424
+ }
425
+ if (sortChanged && params.orderBy?.length) {
426
+ next = sortRowsLocally(
427
+ next as unknown as Record<string, unknown>[],
428
+ params.orderBy,
429
+ ) as TDoc[];
430
+ }
431
+ return next;
432
+ });
433
+ }
434
+ }
435
+ lastFiltersKey.current = filtersKey;
436
+ lastSortKey.current = sortKey;
311
437
  // Abort the superseded request (SB-M-011): the sequence guard below
312
438
  // already drops its response, aborting stops the wasted transfer too.
313
439
  fetchAbortRef.current?.abort();
@@ -339,17 +465,23 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
339
465
  if (seq !== fetchSeq.current) return;
340
466
  setServerData(data);
341
467
  setServerRowCount(count);
468
+ lastGoodServerRows.current = data;
342
469
  setErrorState(null);
343
470
  } catch (err) {
344
471
  if (seq !== fetchSeq.current) return;
345
472
  // Abort is a cancellation (unmount), not a failure to surface.
346
473
  if (controller.signal.aborted) return;
347
474
  setErrorState(err instanceof Error ? err : new Error(String(err)));
348
- setServerData([]);
349
- setServerRowCount(0);
475
+ // Keep last-good rows on a refetch failure — only the first load
476
+ // may settle as empty.
477
+ if (!hasLoadedOnceRef.current) {
478
+ setServerData([]);
479
+ setServerRowCount(0);
480
+ }
350
481
  } finally {
351
482
  if (seq === fetchSeq.current && !controller.signal.aborted) {
352
483
  setServerLoading(false);
484
+ hasLoadedOnceRef.current = true;
353
485
  setHasLoadedOnce(true);
354
486
  }
355
487
  }
@@ -384,6 +516,19 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
384
516
  [],
385
517
  );
386
518
 
519
+ // In-place refresh: re-run the current query when the host bumps
520
+ // refreshToken. Rows stay rendered (hasLoadedOnce is untouched) and swap
521
+ // when the response lands — the no-remount replacement for keying the
522
+ // table on a refresh generation.
523
+ const refreshTokenSeen = useRef(refreshToken);
524
+ useEffect(() => {
525
+ if (refreshTokenSeen.current === refreshToken) return;
526
+ refreshTokenSeen.current = refreshToken;
527
+ if (!serverSide || infiniteServerMode) return;
528
+ lastFetchKey.current = "";
529
+ if (lastStateRef.current) fetchServerData(lastStateRef.current);
530
+ }, [refreshToken, serverSide, infiniteServerMode, fetchServerData]);
531
+
387
532
  // Page loader for infinite mode: same param mapping as fetchServerData but
388
533
  // per-page and cacheable — DataTable pre-loads page N+1 while N is on
389
534
  // screen. totalCount rides page 0 only (one count query per query state).
@@ -427,17 +572,20 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
427
572
  useEffect(() => {
428
573
  if (serverSide && !infiniteServerMode && frappeApp) {
429
574
  lastFetchKey.current = "";
430
- fetchServerData({
431
- data: [],
432
- columns: columns as any,
433
- pagination: { pageIndex: 0, pageSize: 10 },
434
- sorting: [],
435
- filters: [],
436
- columnFilters: [],
437
- columnVisibility: {},
438
- columnOrder: columns.map((c) => c.id ?? ""),
439
- columnSizing: {},
440
- } as DataTableState);
575
+ fetchServerData(
576
+ lastStateRef.current ??
577
+ ({
578
+ data: [],
579
+ columns: columns as any,
580
+ pagination: { pageIndex: 0, pageSize: 10 },
581
+ sorting: [],
582
+ filters: [],
583
+ columnFilters: [],
584
+ columnVisibility: {},
585
+ columnOrder: columns.map((c) => c.id ?? ""),
586
+ columnSizing: {},
587
+ } as DataTableState),
588
+ );
441
589
  }
442
590
  }, [serverSide, infiniteServerMode, !!frappeApp, fetchServerData]);
443
591
 
@@ -471,16 +619,45 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
471
619
  } = useLiveQuery((q) => (collection ? q.from({ _list: collection }) : null), [collection]);
472
620
  const clientRows = useMemo(() => (rawData as TDoc[] | undefined) ?? [], [rawData]);
473
621
 
474
- const rows = serverSide ? serverData : clientRows;
622
+ // Keep-rows-and-reconcile for client/live-collection mode: a filter change
623
+ // swaps the collection (new subscription), which reports loading from
624
+ // empty — render the LAST GOOD rows, locally narrowed by the new filters
625
+ // (optimistic local apply), until the new collection is ready. Only the
626
+ // very first load may show the skeleton.
627
+ const lastGoodClientRows = useRef<TDoc[] | null>(null);
628
+ if (!serverSide && !isRowsLoading) lastGoodClientRows.current = clientRows;
629
+ const clientRowsResolved = useMemo(() => {
630
+ if (serverSide) return clientRows;
631
+ if (!isRowsLoading || !lastGoodClientRows.current) return clientRows;
632
+ const held = lastGoodClientRows.current;
633
+ if (!activeFilters) return held;
634
+ const narrowed = held.filter((row) =>
635
+ matchesFilters(row as unknown as Parameters<typeof matchesFilters>[0], activeFilters),
636
+ );
637
+ // A local pass that would empty the body is skipped — held rows stay
638
+ // as placeholderData until the new collection reconciles.
639
+ return narrowed.length > 0 ? narrowed : held;
640
+ }, [serverSide, clientRows, isRowsLoading, activeFilters]);
641
+
642
+ const serverRowsResolved =
643
+ serverSide && serverLoading && serverData.length === 0 && lastGoodServerRows.current.length > 0
644
+ ? lastGoodServerRows.current
645
+ : serverData;
646
+ const rows = serverSide ? serverRowsResolved : clientRowsResolved;
475
647
  // Infinite serverSide: DataTable owns loading/empty chrome (skeleton +
476
648
  // honest end state) — FrappeTable must not early-return on its own.
477
649
  // Paged serverSide: only the FIRST load shows the skeleton; live
478
650
  // refetches swap rows in place without flashing (LC-06-adjacent).
651
+ // Client mode: the skeleton shows only before the FIRST ready pass —
652
+ // collection swaps keep the last-good rows rendered.
479
653
  const isRowsLoadingResolved = serverSide
480
654
  ? infiniteServerMode
481
655
  ? false
482
656
  : serverLoading && !hasLoadedOnce
483
- : isRowsLoading;
657
+ : isRowsLoading && lastGoodClientRows.current === null;
658
+ const isFetching = serverSide
659
+ ? !infiniteServerMode && serverLoading && hasLoadedOnce
660
+ : isRowsLoading && lastGoodClientRows.current !== null;
484
661
 
485
662
  // Reset stale errors when the underlying collection changes (new doctype
486
663
  // or filters) so a recovered query doesn't keep showing the old failure.
@@ -525,11 +702,13 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
525
702
  const rowData = rows ?? [];
526
703
  // Honest states via AsyncBoundary (DG-ST-01: error > loading > empty).
527
704
  // Infinite serverSide delegates loading/empty chrome to DataTable.
705
+ // After the first load, NEVER replace DataTable with empty/loading chrome
706
+ // while a refetch is in flight — that was the named blank-the-list defect.
528
707
  const boundaryState = errorState
529
708
  ? { error: errorState }
530
709
  : isRowsLoadingResolved
531
710
  ? { loading: loading ?? true }
532
- : rowData.length === 0 && !infiniteServerMode
711
+ : rowData.length === 0 && !infiniteServerMode && !isFetching
533
712
  ? { empty: true }
534
713
  : null;
535
714
  if (boundaryState) {
@@ -537,7 +716,7 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
537
716
  <YStack {...knobProps.gap} flex={1}>
538
717
  <FrappeReconnectIndicator config={connectionConfig} />
539
718
  <AsyncBoundary
540
- layout="table"
719
+ layout={layout === "list" || layout === "cards" ? "list" : "table"}
541
720
  errorTitle={t("Error loading {{doctype}}", { doctype })}
542
721
  emptyTitle={t("No {{doctype}} records found", { doctype })}
543
722
  {...boundaryState}
@@ -546,39 +725,6 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
546
725
  );
547
726
  }
548
727
 
549
- const columnsWithClick: DataTableColumn<Partial<TDoc>>[] = onRowClick
550
- ? columns.map((col) => ({
551
- ...col,
552
- cell: (props) => {
553
- const content = typeof col.cell === "function" ? col.cell(props) : props.getValue();
554
- return (
555
- <View
556
- onPress={() => onRowClick(props.row.original)}
557
- cursor="pointer"
558
- hoverStyle={{ backgroundColor: "$color2" }}
559
- // Cell containment (LC-06/LC-10): a bare View defaults to
560
- // flexShrink 0 and hugs its content, so long cell content
561
- // (e.g. a ToDo description) painted PAST the column bounds and
562
- // under the neighbor cells (the td itself doesn't clip).
563
- // Shrink with the cell, cap at its box, clip the excess — the
564
- // inner one-line Texts then ellipsize in-column.
565
- minWidth={0}
566
- maxWidth="100%"
567
- flexShrink={1}
568
- overflow="hidden"
569
- >
570
- {/* raw strings crash native (text must be inside <Text>) */}
571
- {typeof content === "string" || typeof content === "number" ? (
572
- <Text fontSize="$3">{content}</Text>
573
- ) : (
574
- (content as React.ReactNode)
575
- )}
576
- </View>
577
- );
578
- },
579
- }))
580
- : columns;
581
-
582
728
  return (
583
729
  <YStack {...knobProps.gap} flex={1}>
584
730
  <FrappeReconnectIndicator config={connectionConfig} />
@@ -607,7 +753,16 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
607
753
  )}
608
754
  <DataTable
609
755
  data={rowData}
610
- columns={columnsWithClick}
756
+ columns={columns}
757
+ // onRowClick (the stable FrappeTable prop name) rides DataTable's
758
+ // first-class row activation: whole-row press target, roving tab
759
+ // stop (LC-49), Enter/Space activation (LC-50), inner interactive
760
+ // controls win. rowLabel names each pressable row and its
761
+ // selection checkbox by document identity. Both spread BEFORE
762
+ // {...dataTableProps} so an explicit consumer onRowPress/rowLabel
763
+ // still ejects (consumer-last house rule).
764
+ {...(onRowClick ? { onRowPress: (row: Partial<TDoc>) => onRowClick(row) } : {})}
765
+ rowLabel={rowLabel}
611
766
  {...(serverSide && {
612
767
  manualFiltering: true,
613
768
  manualPagination: true,
@@ -618,7 +773,9 @@ export function FrappeTable<TDoc extends { name: string; doctype: string }>({
618
773
  paginationMode={paginationMode}
619
774
  {...(resolvedLoadPage ? { loadPage: resolvedLoadPage } : {})}
620
775
  {...(filterableFields ? { filterableFields } : {})}
776
+ isFetching={isFetching}
621
777
  {...dataTableProps}
778
+ {...(layout ? { layout } : {})}
622
779
  />
623
780
  </YStack>
624
781
  );
@@ -130,11 +130,21 @@ export function FrappeTreeView<TDoc extends { name: string; doctype: string }>({
130
130
  () => ({
131
131
  baseURL: finalBaseURL,
132
132
  socketPort: config?.socketPort,
133
+ siteName: config?.siteName,
134
+ socketBaseURL: config?.socketBaseURL,
133
135
  auth: finalAuth,
134
136
  syncOptions: finalSyncOptions,
135
137
  fixtures: config?.fixtures,
136
138
  }),
137
- [finalBaseURL, config?.socketPort, finalAuth, finalSyncOptions, config?.fixtures],
139
+ [
140
+ finalBaseURL,
141
+ config?.socketPort,
142
+ config?.siteName,
143
+ config?.socketBaseURL,
144
+ finalAuth,
145
+ finalSyncOptions,
146
+ config?.fixtures,
147
+ ],
138
148
  );
139
149
 
140
150
  const { meta, isLoading: isMetaLoading, error: metaError } = useDocTypeMeta(appConfig, doctype);
@@ -0,0 +1,60 @@
1
+ // SyncErrorNotice contract: the visible half of the rejected-write pattern —
2
+ // role="alert" message, Retry re-issues, Dismiss clears, and each affordance
3
+ // only renders when its handler is provided.
4
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
5
+ import { cleanup, fireEvent, screen } from "@testing-library/react";
6
+ import i18next from "i18next";
7
+ import { initReactI18next } from "react-i18next";
8
+ import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
9
+ import { SyncErrorNotice } from "./SyncErrorNotice";
10
+
11
+ beforeAll(async () => {
12
+ await i18next.use(initReactI18next).init({
13
+ lng: "en",
14
+ interpolation: { escapeValue: false },
15
+ resources: { en: { translation: {} } },
16
+ });
17
+ });
18
+
19
+ afterEach(cleanup);
20
+
21
+ describe("SyncErrorNotice", () => {
22
+ it("renders the message as an alert", async () => {
23
+ renderWithProviders(<SyncErrorNotice message="Could not add comment" />);
24
+ const alert = await screen.findByRole("alert");
25
+ expect(alert).toHaveTextContent("Could not add comment");
26
+ });
27
+
28
+ it("fires onRetry and onDismiss from their buttons", async () => {
29
+ const onRetry = vi.fn();
30
+ const onDismiss = vi.fn();
31
+ renderWithProviders(
32
+ <SyncErrorNotice message="Write rejected" onRetry={onRetry} onDismiss={onDismiss} />,
33
+ );
34
+ fireEvent.click(await screen.findByText("Retry"));
35
+ expect(onRetry).toHaveBeenCalledTimes(1);
36
+ fireEvent.click(await screen.findByText("Dismiss"));
37
+ expect(onDismiss).toHaveBeenCalledTimes(1);
38
+ });
39
+
40
+ it("hides affordances whose handlers are omitted", async () => {
41
+ renderWithProviders(<SyncErrorNotice message="No actions" />);
42
+ await screen.findByRole("alert");
43
+ expect(screen.queryByText("Retry")).toBeNull();
44
+ expect(screen.queryByText("Dismiss")).toBeNull();
45
+ });
46
+
47
+ it("honors custom labels", async () => {
48
+ renderWithProviders(
49
+ <SyncErrorNotice
50
+ message="Custom"
51
+ onRetry={() => {}}
52
+ onDismiss={() => {}}
53
+ retryLabel="Try again"
54
+ dismissLabel="Ignore"
55
+ />,
56
+ );
57
+ expect(await screen.findByText("Try again")).toBeTruthy();
58
+ expect(await screen.findByText("Ignore")).toBeTruthy();
59
+ });
60
+ });
@@ -0,0 +1,81 @@
1
+ import { Button } from "@multiplatform.one/components";
2
+ import { useResolvedKnobs } from "@multiplatform.one/theme";
3
+ import React from "react";
4
+ import { useTranslation } from "react-i18next";
5
+ import { Text, XStack } from "tamagui";
6
+
7
+ export interface SyncErrorNoticeProps {
8
+ /** Human-readable failure message (already localized by the caller). */
9
+ message: string;
10
+ /**
11
+ * Re-issues the failed write (same mutation id — idempotent server-side).
12
+ * Omit to hide the Retry affordance.
13
+ */
14
+ onRetry?: () => void;
15
+ /** Drops the failed entry without retrying. Omit to hide Dismiss. */
16
+ onDismiss?: () => void;
17
+ /** Override the Retry button label (defaults to a localized "Retry"). */
18
+ retryLabel?: string;
19
+ /** Override the Dismiss button label (defaults to a localized "Dismiss"). */
20
+ dismissLabel?: string;
21
+ }
22
+
23
+ /**
24
+ * The rejected-write notice — the visible half of the optimistic mutation
25
+ * contract: when the server rejects a write, the optimistic state has
26
+ * already been rolled back by the sync engine; this primitive surfaces the
27
+ * error inline at the surface that initiated the write, with Retry
28
+ * (re-issues the SAME mutation id) and Dismiss (clears the ledger entry).
29
+ *
30
+ * Pair with `useFrappePendingMutations` from `@multiplatform.one/frappe`:
31
+ *
32
+ * ```tsx
33
+ * const { failed, retry, dismiss } = useFrappePendingMutations(config, "Comment");
34
+ * {failed.map((entry) => (
35
+ * <SyncErrorNotice
36
+ * key={entry.id}
37
+ * message={entry.error ?? t("Could not save changes")}
38
+ * onRetry={retry}
39
+ * onDismiss={() => dismiss(entry.id)}
40
+ * />
41
+ * ))}
42
+ * ```
43
+ */
44
+ export function SyncErrorNotice({
45
+ message,
46
+ onRetry,
47
+ onDismiss,
48
+ retryLabel,
49
+ dismissLabel,
50
+ }: SyncErrorNoticeProps) {
51
+ const { t } = useTranslation();
52
+ const { knobProps } = useResolvedKnobs();
53
+ return (
54
+ <XStack
55
+ role="alert"
56
+ alignItems="center"
57
+ flexWrap="wrap"
58
+ gap="$2"
59
+ paddingHorizontal="$3"
60
+ paddingVertical="$2"
61
+ {...knobProps.borderRadius}
62
+ backgroundColor="$red3"
63
+ borderColor="$red7"
64
+ borderWidth={1}
65
+ >
66
+ <Text flex={1} fontSize="$2" color="$red11">
67
+ {message}
68
+ </Text>
69
+ {onRetry && (
70
+ <Button size="$2" theme="error" {...knobProps.borderRadius} onPress={onRetry}>
71
+ {retryLabel ?? t("Retry")}
72
+ </Button>
73
+ )}
74
+ {onDismiss && (
75
+ <Button size="$2" chromeless {...knobProps.borderRadius} onPress={onDismiss}>
76
+ {dismissLabel ?? t("Dismiss")}
77
+ </Button>
78
+ )}
79
+ </XStack>
80
+ );
81
+ }