@multiplatform.one/frappe-ui 7.10.0 → 7.15.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 (342) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/FrappeDashboard.cjs +8 -10
  3. package/dist/cjs/FrappeDashboard.native.js +8 -14
  4. package/dist/cjs/FrappeDashboard.native.js.map +1 -1
  5. package/dist/cjs/FrappeForm.cjs +465 -175
  6. package/dist/cjs/FrappeForm.native.js +611 -207
  7. package/dist/cjs/FrappeForm.native.js.map +1 -1
  8. package/dist/cjs/FrappeFormConnections.cjs +125 -0
  9. package/dist/cjs/FrappeFormConnections.native.js +149 -0
  10. package/dist/cjs/FrappeFormConnections.native.js.map +1 -0
  11. package/dist/cjs/FrappeImageView.cjs +7 -9
  12. package/dist/cjs/FrappeImageView.native.js +7 -13
  13. package/dist/cjs/FrappeImageView.native.js.map +1 -1
  14. package/dist/cjs/FrappeList.cjs +29 -4
  15. package/dist/cjs/FrappeList.native.js +37 -5
  16. package/dist/cjs/FrappeList.native.js.map +1 -1
  17. package/dist/cjs/FrappeListView.cjs +102 -29
  18. package/dist/cjs/FrappeListView.native.js +109 -29
  19. package/dist/cjs/FrappeListView.native.js.map +1 -1
  20. package/dist/cjs/FrappeProvider.cjs +7 -2
  21. package/dist/cjs/FrappeProvider.native.js +7 -2
  22. package/dist/cjs/FrappeProvider.native.js.map +1 -1
  23. package/dist/cjs/FrappeReportBuilder.cjs +10 -11
  24. package/dist/cjs/FrappeReportBuilder.native.js +10 -16
  25. package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
  26. package/dist/cjs/FrappeRowsReader.cjs +49 -0
  27. package/dist/cjs/FrappeRowsReader.native.js +53 -0
  28. package/dist/cjs/FrappeRowsReader.native.js.map +1 -0
  29. package/dist/cjs/FrappeTable.cjs +18 -6
  30. package/dist/cjs/FrappeTable.native.js +24 -6
  31. package/dist/cjs/FrappeTable.native.js.map +1 -1
  32. package/dist/cjs/fieldRegistry.cjs +36 -11
  33. package/dist/cjs/fieldRegistry.native.js +59 -11
  34. package/dist/cjs/fieldRegistry.native.js.map +1 -1
  35. package/dist/cjs/fields/Date/index.native.js +5 -2
  36. package/dist/cjs/fields/Date/index.native.js.map +1 -1
  37. package/dist/cjs/fields/Date/sheetKeyboard.cjs +2 -2
  38. package/dist/cjs/fields/Date/sheetKeyboard.native.js +2 -2
  39. package/dist/cjs/fields/Date/sheetKeyboard.native.js.map +1 -1
  40. package/dist/cjs/fields/DateRange/index.cjs +2 -1
  41. package/dist/cjs/fields/DateRange/index.native.js +4 -1
  42. package/dist/cjs/fields/DateRange/index.native.js.map +1 -1
  43. package/dist/cjs/formEngine/index.cjs +4 -2
  44. package/dist/cjs/formEngine/index.native.js +3 -1
  45. package/dist/cjs/formEngine/index.native.js.map +1 -1
  46. package/dist/cjs/formEngine/useSessionRoles.cjs +77 -0
  47. package/dist/cjs/formEngine/useSessionRoles.native.js +84 -0
  48. package/dist/cjs/formEngine/useSessionRoles.native.js.map +1 -0
  49. package/dist/cjs/frappeConnections.cjs +41 -0
  50. package/dist/cjs/frappeConnections.native.js +50 -0
  51. package/dist/cjs/frappeConnections.native.js.map +1 -0
  52. package/dist/cjs/frappeNavigation.cjs +107 -0
  53. package/dist/cjs/frappeNavigation.native.js +112 -0
  54. package/dist/cjs/frappeNavigation.native.js.map +1 -0
  55. package/dist/cjs/index.cjs +18 -1
  56. package/dist/cjs/index.native.js +18 -1
  57. package/dist/cjs/index.native.js.map +1 -1
  58. package/dist/cjs/schemaForm/index.cjs +4 -1
  59. package/dist/cjs/schemaForm/index.native.js +4 -1
  60. package/dist/cjs/schemaForm/index.native.js.map +1 -1
  61. package/dist/cjs/useConnectionFilters.cjs +146 -0
  62. package/dist/cjs/useConnectionFilters.native.js +213 -0
  63. package/dist/cjs/useConnectionFilters.native.js.map +1 -0
  64. package/dist/cjs/useFrappePagination.cjs +37 -18
  65. package/dist/cjs/useFrappePagination.native.js +59 -23
  66. package/dist/cjs/useFrappePagination.native.js.map +1 -1
  67. package/dist/cjs/useFrappeRows.native.js.map +1 -1
  68. package/dist/esm/FrappeDashboard.mjs +9 -11
  69. package/dist/esm/FrappeDashboard.mjs.map +1 -1
  70. package/dist/esm/FrappeDashboard.native.js +9 -15
  71. package/dist/esm/FrappeDashboard.native.js.map +1 -1
  72. package/dist/esm/FrappeForm.mjs +471 -181
  73. package/dist/esm/FrappeForm.mjs.map +1 -1
  74. package/dist/esm/FrappeForm.native.js +599 -195
  75. package/dist/esm/FrappeForm.native.js.map +1 -1
  76. package/dist/esm/FrappeFormConnections.mjs +99 -0
  77. package/dist/esm/FrappeFormConnections.mjs.map +1 -0
  78. package/dist/esm/FrappeFormConnections.native.js +120 -0
  79. package/dist/esm/FrappeFormConnections.native.js.map +1 -0
  80. package/dist/esm/FrappeImageView.mjs +8 -10
  81. package/dist/esm/FrappeImageView.mjs.map +1 -1
  82. package/dist/esm/FrappeImageView.native.js +8 -14
  83. package/dist/esm/FrappeImageView.native.js.map +1 -1
  84. package/dist/esm/FrappeList.mjs +31 -6
  85. package/dist/esm/FrappeList.mjs.map +1 -1
  86. package/dist/esm/FrappeList.native.js +39 -7
  87. package/dist/esm/FrappeList.native.js.map +1 -1
  88. package/dist/esm/FrappeListView.mjs +103 -30
  89. package/dist/esm/FrappeListView.mjs.map +1 -1
  90. package/dist/esm/FrappeListView.native.js +110 -30
  91. package/dist/esm/FrappeListView.native.js.map +1 -1
  92. package/dist/esm/FrappeProvider.mjs +7 -2
  93. package/dist/esm/FrappeProvider.mjs.map +1 -1
  94. package/dist/esm/FrappeProvider.native.js +7 -2
  95. package/dist/esm/FrappeProvider.native.js.map +1 -1
  96. package/dist/esm/FrappeReportBuilder.mjs +11 -12
  97. package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
  98. package/dist/esm/FrappeReportBuilder.native.js +11 -17
  99. package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
  100. package/dist/esm/FrappeRowsReader.mjs +24 -0
  101. package/dist/esm/FrappeRowsReader.mjs.map +1 -0
  102. package/dist/esm/FrappeRowsReader.native.js +25 -0
  103. package/dist/esm/FrappeRowsReader.native.js.map +1 -0
  104. package/dist/esm/FrappeTable.mjs +18 -6
  105. package/dist/esm/FrappeTable.mjs.map +1 -1
  106. package/dist/esm/FrappeTable.native.js +24 -6
  107. package/dist/esm/FrappeTable.native.js.map +1 -1
  108. package/dist/esm/fieldRegistry.mjs +35 -12
  109. package/dist/esm/fieldRegistry.mjs.map +1 -1
  110. package/dist/esm/fieldRegistry.native.js +58 -12
  111. package/dist/esm/fieldRegistry.native.js.map +1 -1
  112. package/dist/esm/fields/Date/index.native.js +6 -3
  113. package/dist/esm/fields/Date/index.native.js.map +1 -1
  114. package/dist/esm/fields/Date/sheetKeyboard.mjs +2 -2
  115. package/dist/esm/fields/Date/sheetKeyboard.mjs.map +1 -1
  116. package/dist/esm/fields/Date/sheetKeyboard.native.js +2 -2
  117. package/dist/esm/fields/Date/sheetKeyboard.native.js.map +1 -1
  118. package/dist/esm/fields/DateRange/index.mjs +2 -1
  119. package/dist/esm/fields/DateRange/index.mjs.map +1 -1
  120. package/dist/esm/fields/DateRange/index.native.js +4 -1
  121. package/dist/esm/fields/DateRange/index.native.js.map +1 -1
  122. package/dist/esm/formEngine/index.mjs +2 -1
  123. package/dist/esm/formEngine/index.mjs.map +1 -1
  124. package/dist/esm/formEngine/index.native.js +2 -1
  125. package/dist/esm/formEngine/index.native.js.map +1 -1
  126. package/dist/esm/formEngine/useSessionRoles.mjs +52 -0
  127. package/dist/esm/formEngine/useSessionRoles.mjs.map +1 -0
  128. package/dist/esm/formEngine/useSessionRoles.native.js +56 -0
  129. package/dist/esm/formEngine/useSessionRoles.native.js.map +1 -0
  130. package/dist/esm/frappeConnections.mjs +15 -0
  131. package/dist/esm/frappeConnections.mjs.map +1 -0
  132. package/dist/esm/frappeConnections.native.js +21 -0
  133. package/dist/esm/frappeConnections.native.js.map +1 -0
  134. package/dist/esm/frappeNavigation.mjs +78 -0
  135. package/dist/esm/frappeNavigation.mjs.map +1 -0
  136. package/dist/esm/frappeNavigation.native.js +80 -0
  137. package/dist/esm/frappeNavigation.native.js.map +1 -0
  138. package/dist/esm/index.mjs +7 -3
  139. package/dist/esm/index.mjs.map +1 -1
  140. package/dist/esm/index.native.js +7 -3
  141. package/dist/esm/index.native.js.map +1 -1
  142. package/dist/esm/schemaForm/index.mjs +4 -1
  143. package/dist/esm/schemaForm/index.mjs.map +1 -1
  144. package/dist/esm/schemaForm/index.native.js +4 -1
  145. package/dist/esm/schemaForm/index.native.js.map +1 -1
  146. package/dist/esm/useConnectionFilters.mjs +121 -0
  147. package/dist/esm/useConnectionFilters.mjs.map +1 -0
  148. package/dist/esm/useConnectionFilters.native.js +185 -0
  149. package/dist/esm/useConnectionFilters.native.js.map +1 -0
  150. package/dist/esm/useFrappePagination.mjs +38 -19
  151. package/dist/esm/useFrappePagination.mjs.map +1 -1
  152. package/dist/esm/useFrappePagination.native.js +60 -24
  153. package/dist/esm/useFrappePagination.native.js.map +1 -1
  154. package/dist/esm/useFrappeRows.mjs.map +1 -1
  155. package/dist/esm/useFrappeRows.native.js.map +1 -1
  156. package/dist/jsx/FrappeDashboard.mjs +9 -11
  157. package/dist/jsx/FrappeDashboard.mjs.map +1 -1
  158. package/dist/jsx/FrappeDashboard.native.js +8 -14
  159. package/dist/jsx/FrappeDashboard.native.js.map +1 -1
  160. package/dist/jsx/FrappeForm.mjs +471 -181
  161. package/dist/jsx/FrappeForm.mjs.map +1 -1
  162. package/dist/jsx/FrappeForm.native.js +611 -207
  163. package/dist/jsx/FrappeForm.native.js.map +1 -1
  164. package/dist/jsx/FrappeFormConnections.mjs +99 -0
  165. package/dist/jsx/FrappeFormConnections.mjs.map +1 -0
  166. package/dist/jsx/FrappeFormConnections.native.js +149 -0
  167. package/dist/jsx/FrappeFormConnections.native.js.map +1 -0
  168. package/dist/jsx/FrappeImageView.mjs +8 -10
  169. package/dist/jsx/FrappeImageView.mjs.map +1 -1
  170. package/dist/jsx/FrappeImageView.native.js +7 -13
  171. package/dist/jsx/FrappeImageView.native.js.map +1 -1
  172. package/dist/jsx/FrappeList.mjs +31 -6
  173. package/dist/jsx/FrappeList.mjs.map +1 -1
  174. package/dist/jsx/FrappeList.native.js +37 -5
  175. package/dist/jsx/FrappeList.native.js.map +1 -1
  176. package/dist/jsx/FrappeListView.mjs +103 -30
  177. package/dist/jsx/FrappeListView.mjs.map +1 -1
  178. package/dist/jsx/FrappeListView.native.js +109 -29
  179. package/dist/jsx/FrappeListView.native.js.map +1 -1
  180. package/dist/jsx/FrappeProvider.mjs +7 -2
  181. package/dist/jsx/FrappeProvider.mjs.map +1 -1
  182. package/dist/jsx/FrappeProvider.native.js +7 -2
  183. package/dist/jsx/FrappeProvider.native.js.map +1 -1
  184. package/dist/jsx/FrappeReportBuilder.mjs +11 -12
  185. package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
  186. package/dist/jsx/FrappeReportBuilder.native.js +10 -16
  187. package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
  188. package/dist/jsx/FrappeRowsReader.mjs +24 -0
  189. package/dist/jsx/FrappeRowsReader.mjs.map +1 -0
  190. package/dist/jsx/FrappeRowsReader.native.js +53 -0
  191. package/dist/jsx/FrappeRowsReader.native.js.map +1 -0
  192. package/dist/jsx/FrappeTable.mjs +18 -6
  193. package/dist/jsx/FrappeTable.mjs.map +1 -1
  194. package/dist/jsx/FrappeTable.native.js +24 -6
  195. package/dist/jsx/FrappeTable.native.js.map +1 -1
  196. package/dist/jsx/fieldRegistry.mjs +35 -12
  197. package/dist/jsx/fieldRegistry.mjs.map +1 -1
  198. package/dist/jsx/fieldRegistry.native.js +59 -11
  199. package/dist/jsx/fieldRegistry.native.js.map +1 -1
  200. package/dist/jsx/fields/Date/index.native.js +5 -2
  201. package/dist/jsx/fields/Date/index.native.js.map +1 -1
  202. package/dist/jsx/fields/Date/sheetKeyboard.mjs +2 -2
  203. package/dist/jsx/fields/Date/sheetKeyboard.mjs.map +1 -1
  204. package/dist/jsx/fields/Date/sheetKeyboard.native.js +2 -2
  205. package/dist/jsx/fields/Date/sheetKeyboard.native.js.map +1 -1
  206. package/dist/jsx/fields/DateRange/index.mjs +2 -1
  207. package/dist/jsx/fields/DateRange/index.mjs.map +1 -1
  208. package/dist/jsx/fields/DateRange/index.native.js +4 -1
  209. package/dist/jsx/fields/DateRange/index.native.js.map +1 -1
  210. package/dist/jsx/formEngine/index.mjs +2 -1
  211. package/dist/jsx/formEngine/index.mjs.map +1 -1
  212. package/dist/jsx/formEngine/index.native.js +3 -1
  213. package/dist/jsx/formEngine/index.native.js.map +1 -1
  214. package/dist/jsx/formEngine/useSessionRoles.mjs +52 -0
  215. package/dist/jsx/formEngine/useSessionRoles.mjs.map +1 -0
  216. package/dist/jsx/formEngine/useSessionRoles.native.js +84 -0
  217. package/dist/jsx/formEngine/useSessionRoles.native.js.map +1 -0
  218. package/dist/jsx/frappeConnections.mjs +15 -0
  219. package/dist/jsx/frappeConnections.mjs.map +1 -0
  220. package/dist/jsx/frappeConnections.native.js +50 -0
  221. package/dist/jsx/frappeConnections.native.js.map +1 -0
  222. package/dist/jsx/frappeNavigation.mjs +78 -0
  223. package/dist/jsx/frappeNavigation.mjs.map +1 -0
  224. package/dist/jsx/frappeNavigation.native.js +112 -0
  225. package/dist/jsx/frappeNavigation.native.js.map +1 -0
  226. package/dist/jsx/index.js +7 -3
  227. package/dist/jsx/index.js.map +1 -1
  228. package/dist/jsx/index.mjs +7 -3
  229. package/dist/jsx/index.mjs.map +1 -1
  230. package/dist/jsx/index.native.js +18 -1
  231. package/dist/jsx/index.native.js.map +1 -1
  232. package/dist/jsx/schemaForm/index.mjs +4 -1
  233. package/dist/jsx/schemaForm/index.mjs.map +1 -1
  234. package/dist/jsx/schemaForm/index.native.js +4 -1
  235. package/dist/jsx/schemaForm/index.native.js.map +1 -1
  236. package/dist/jsx/useConnectionFilters.mjs +121 -0
  237. package/dist/jsx/useConnectionFilters.mjs.map +1 -0
  238. package/dist/jsx/useConnectionFilters.native.js +213 -0
  239. package/dist/jsx/useConnectionFilters.native.js.map +1 -0
  240. package/dist/jsx/useFrappePagination.mjs +38 -19
  241. package/dist/jsx/useFrappePagination.mjs.map +1 -1
  242. package/dist/jsx/useFrappePagination.native.js +59 -23
  243. package/dist/jsx/useFrappePagination.native.js.map +1 -1
  244. package/dist/jsx/useFrappeRows.mjs.map +1 -1
  245. package/dist/jsx/useFrappeRows.native.js.map +1 -1
  246. package/package.json +26 -22
  247. package/src/FileBrowser/AttachLibrary.stories.tsx +23 -0
  248. package/src/FrappeDashboard.tsx +11 -16
  249. package/src/FrappeForm.connections.spec.tsx +280 -0
  250. package/src/FrappeForm.connections.stories.tsx +93 -0
  251. package/src/FrappeForm.deskSemantics.spec.tsx +284 -0
  252. package/src/FrappeForm.tsx +560 -145
  253. package/src/FrappeFormConnections.tsx +148 -0
  254. package/src/FrappeGuestMeta.spec.tsx +143 -0
  255. package/src/FrappeImageView.tsx +8 -12
  256. package/src/FrappeList.tsx +71 -15
  257. package/src/FrappeListView.cards.spec.tsx +175 -0
  258. package/src/FrappeListView.cards.stories.tsx +131 -0
  259. package/src/FrappeListView.connections.spec.tsx +192 -0
  260. package/src/FrappeListView.connections.stories.tsx +99 -0
  261. package/src/FrappeListView.seed.spec.tsx +228 -0
  262. package/src/FrappeListView.tsx +177 -31
  263. package/src/FrappeProvider.catchUp.spec.tsx +502 -0
  264. package/src/FrappeProvider.tsx +15 -3
  265. package/src/FrappeReportBuilder.tsx +11 -19
  266. package/src/FrappeRowsReader.tsx +38 -0
  267. package/src/FrappeTable.optionalProps.spec.tsx +249 -0
  268. package/src/FrappeTable.spec.tsx +32 -42
  269. package/src/FrappeTable.stories.spec.tsx +55 -0
  270. package/src/FrappeTable.stories.tsx +28 -37
  271. package/src/FrappeTable.tsx +108 -45
  272. package/src/childTableDependsOn.spec.tsx +85 -0
  273. package/src/desk/DeskFormSidebar.spec.tsx +20 -0
  274. package/src/desk/DeskFormSidebar.stories.tsx +42 -0
  275. package/src/fieldRegistry.spec.tsx +27 -64
  276. package/src/fieldRegistry.ts +71 -14
  277. package/src/fields/Date/index.native.tsx +4 -2
  278. package/src/fields/Date/sheetKeyboard.spec.ts +14 -5
  279. package/src/fields/Date/sheetKeyboard.ts +12 -4
  280. package/src/fields/DateRange/DateRange.spec.tsx +67 -0
  281. package/src/fields/DateRange/index.tsx +5 -1
  282. package/src/fields/LinkTree.spec.tsx +8 -0
  283. package/src/fields/LinkTree.stories.tsx +28 -5
  284. package/src/fields/Select/Select.hydration.spec.tsx +177 -0
  285. package/src/formEngine/doctypeMeta.ts +15 -114
  286. package/src/formEngine/index.ts +2 -0
  287. package/src/formEngine/seam.spec.ts +23 -4
  288. package/src/formEngine/useSessionRoles.ts +73 -0
  289. package/src/frappeConnections.ts +50 -0
  290. package/src/frappeNavigation.tsx +112 -0
  291. package/src/frappeRoutes.spec.tsx +335 -0
  292. package/src/index.ts +28 -1
  293. package/src/schemaForm/index.tsx +1 -0
  294. package/src/useConnectionFilters.tsx +157 -0
  295. package/src/useFrappePagination.ts +70 -26
  296. package/src/useFrappeRows.sharedPath.spec.tsx +206 -0
  297. package/src/useFrappeRows.ts +5 -4
  298. package/types/FrappeDashboard.d.ts +3 -4
  299. package/types/FrappeDashboard.d.ts.map +1 -1
  300. package/types/FrappeForm.d.ts +57 -5
  301. package/types/FrappeForm.d.ts.map +1 -1
  302. package/types/FrappeFormConnections.d.ts +18 -0
  303. package/types/FrappeFormConnections.d.ts.map +1 -0
  304. package/types/FrappeImageView.d.ts +2 -3
  305. package/types/FrappeImageView.d.ts.map +1 -1
  306. package/types/FrappeList.d.ts +20 -2
  307. package/types/FrappeList.d.ts.map +1 -1
  308. package/types/FrappeListView.d.ts +53 -3
  309. package/types/FrappeListView.d.ts.map +1 -1
  310. package/types/FrappeProvider.d.ts +10 -3
  311. package/types/FrappeProvider.d.ts.map +1 -1
  312. package/types/FrappeReportBuilder.d.ts.map +1 -1
  313. package/types/FrappeRowsReader.d.ts +26 -0
  314. package/types/FrappeRowsReader.d.ts.map +1 -0
  315. package/types/FrappeTable.d.ts +39 -8
  316. package/types/FrappeTable.d.ts.map +1 -1
  317. package/types/fieldRegistry.d.ts +17 -3
  318. package/types/fieldRegistry.d.ts.map +1 -1
  319. package/types/fields/Date/index.native.d.ts.map +1 -1
  320. package/types/fields/Date/sheetKeyboard.d.ts +4 -3
  321. package/types/fields/Date/sheetKeyboard.d.ts.map +1 -1
  322. package/types/fields/DateRange/index.d.ts.map +1 -1
  323. package/types/formEngine/doctypeMeta.d.ts +10 -114
  324. package/types/formEngine/doctypeMeta.d.ts.map +1 -1
  325. package/types/formEngine/index.d.ts +1 -0
  326. package/types/formEngine/index.d.ts.map +1 -1
  327. package/types/formEngine/useSessionRoles.d.ts +19 -0
  328. package/types/formEngine/useSessionRoles.d.ts.map +1 -0
  329. package/types/frappeConnections.d.ts +33 -0
  330. package/types/frappeConnections.d.ts.map +1 -0
  331. package/types/frappeNavigation.d.ts +49 -0
  332. package/types/frappeNavigation.d.ts.map +1 -0
  333. package/types/index.d.ts +7 -3
  334. package/types/index.d.ts.map +1 -1
  335. package/types/schemaForm/index.d.ts.map +1 -1
  336. package/types/useConnectionFilters.d.ts +34 -0
  337. package/types/useConnectionFilters.d.ts.map +1 -0
  338. package/types/useFrappePagination.d.ts +18 -5
  339. package/types/useFrappePagination.d.ts.map +1 -1
  340. package/types/useFrappeRows.d.ts +5 -4
  341. package/types/useFrappeRows.d.ts.map +1 -1
  342. package/src/FileBrowser/AttachLibrary.spec.tsx +0 -74
@@ -6,12 +6,24 @@ import { useTranslation } from "react-i18next";
6
6
  import { Button, SearchInput, Spinner } from "@multiplatform.one/forms";
7
7
  import { Text, View, XStack, YStack, isWeb, useTheme, type YStackProps } from "tamagui";
8
8
  import { SavedFilterDropdown, useSavedFilters, type ParsedFilter } from "@multiplatform.one/table";
9
- import { Avatar, ViewSwitcher, componentColors } from "@multiplatform.one/components";
9
+ import {
10
+ AsyncBoundary,
11
+ Avatar,
12
+ ListPageLayout,
13
+ PageHeader,
14
+ ViewSwitcher,
15
+ componentColors,
16
+ type ImageGridProps,
17
+ } from "@multiplatform.one/components";
10
18
  import { pressTargetStyle, useResolvedKnobs } from "@multiplatform.one/theme";
11
19
  import { storage } from "@multiplatform.one/store";
12
20
  import { useFrappeConfig } from "./FrappeProvider";
13
- import { FrappeList } from "./FrappeList";
21
+ import { FrappeList, type FrappeListProps } from "./FrappeList";
14
22
  import { FrappeTable } from "./FrappeTable";
23
+ import { frappeRouteHref, useFrappeRouter } from "./frappeNavigation";
24
+ import { connectionsFromMeta, type FrappeConnection } from "./frappeConnections";
25
+ import { isPermissionError } from "./liveErrors";
26
+ import { useConnectionFilters, type FrappeDoctypeFilter } from "./useConnectionFilters";
15
27
 
16
28
  interface ListFieldConfig {
17
29
  key: string;
@@ -19,7 +31,7 @@ interface ListFieldConfig {
19
31
  render?: (value: any, doc: any) => React.ReactNode;
20
32
  }
21
33
 
22
- export type ListViewMode = "list" | "table";
34
+ export type ListViewMode = "list" | "table" | "grid";
23
35
 
24
36
  export interface FrappeListViewProps<TDoc extends { name: string; doctype: string }> extends Omit<
25
37
  YStackProps,
@@ -27,12 +39,58 @@ export interface FrappeListViewProps<TDoc extends { name: string; doctype: strin
27
39
  > {
28
40
  doctype: string;
29
41
  fields?: readonly (keyof TDoc & string)[];
30
- filters?: Partial<TDoc> | Array<[keyof TDoc & string, string, unknown]>;
42
+ /**
43
+ * Doctypes linking back to this one (desk Connections). A four-part filter
44
+ * on one of them, `[doctype, field, operator, value]`, narrows this list to
45
+ * the records its matching rows name. Defaults to the doctype's meta.links.
46
+ */
47
+ connections?: readonly FrappeConnection[];
48
+ filters?: Partial<TDoc> | Array<[keyof TDoc & string, string, unknown] | FrappeDoctypeFilter>;
31
49
  defaultViewMode?: ListViewMode;
50
+ /** Controlled view mode; the stored per-doctype choice is then ignored. */
51
+ viewMode?: ListViewMode;
52
+ onViewModeChange?: (mode: ListViewMode) => void;
32
53
  onItemClick?: (doc: Partial<TDoc>) => void;
33
54
  onAdd?: () => void;
55
+ /**
56
+ * The path the doctype's pages live under (`/moves`). Given, the view is
57
+ * the doctype's list page: it carries the list page template with the
58
+ * doctype as its title, a row opens `route/<name>` and Add opens
59
+ * `route/new` through the FrappeProvider's navigation. Add waits for the
60
+ * doctype's meta, so a reader who cannot load it is not offered a form.
61
+ */
62
+ route?: string;
63
+ /**
64
+ * The query the list reads, beyond fields and filters. Give a route loader
65
+ * the same order and limit (`loadDocList`) so its server page and the live
66
+ * page match; `initialRows` is that server page.
67
+ */
68
+ orderBy?: FrappeListProps<TDoc>["orderBy"];
69
+ limit?: number;
70
+ initialRows?: readonly Partial<TDoc>[];
34
71
  columns?: DataTableColumn<Partial<TDoc>>[];
35
72
  displayFields?: ListFieldConfig[];
73
+ /**
74
+ * Per-row card slot. Given, the view gains a card grid (`grid` mode, the
75
+ * catalog ImageGrid) that draws each row with it; loading, empty and error
76
+ * stay the view's own states.
77
+ */
78
+ renderCard?: (doc: Partial<TDoc>, index: number) => React.ReactNode;
79
+ /** Card grid: column count, tile rung and media aspect. */
80
+ gridProps?: Pick<ImageGridProps<Partial<TDoc>>, "columns" | "tileSize" | "mediaAspect">;
81
+ /** Replaces the default empty message in list and card modes. */
82
+ emptyState?: React.ReactNode;
83
+ /**
84
+ * Error-state retry. Defaults to remounting the list, which restarts its
85
+ * sync only when nothing else still holds the failed collection.
86
+ */
87
+ onRetry?: () => void;
88
+ /** Rows revealed per page in list and card modes (default 20). */
89
+ pageSize?: number;
90
+ /** False drops the list/card search band (a screen that owns its own search). */
91
+ showSearch?: boolean;
92
+ /** False renders the view without its card surface, flush in the page. */
93
+ surface?: boolean;
36
94
  showToolbar?: boolean;
37
95
  showFilterSidebar?: boolean;
38
96
  /** @deprecated The toolbar always renders the List/Table switcher now. */
@@ -90,7 +148,7 @@ function resolveImageUrl(value: unknown, baseURL: string): string {
90
148
  async function getStoredViewMode(doctype: string): Promise<ListViewMode | null> {
91
149
  try {
92
150
  const stored = await Promise.resolve(storage.getItem(`${storageKeyPrefix}${doctype}`));
93
- if (stored === "list" || stored === "table") return stored;
151
+ if (stored === "list" || stored === "table" || stored === "grid") return stored;
94
152
  } catch {
95
153
  // storage not available
96
154
  }
@@ -114,12 +172,26 @@ async function setStoredViewMode(doctype: string, mode: ListViewMode): Promise<v
114
172
  export function FrappeListView<TDoc extends { name: string; doctype: string }>({
115
173
  doctype,
116
174
  fields,
175
+ connections,
117
176
  filters,
118
177
  defaultViewMode = "list",
119
- onItemClick,
120
- onAdd,
178
+ viewMode: controlledViewMode,
179
+ onViewModeChange,
180
+ onItemClick: onItemClickProp,
181
+ onAdd: onAddProp,
182
+ route,
183
+ orderBy,
184
+ limit,
185
+ initialRows,
121
186
  columns,
122
187
  displayFields,
188
+ renderCard,
189
+ gridProps,
190
+ emptyState,
191
+ onRetry,
192
+ pageSize,
193
+ showSearch = true,
194
+ surface = true,
123
195
  showToolbar = true,
124
196
  showFilterSidebar = false,
125
197
  // deprecated: switcher always renders; destructured so it stays out of stackProps
@@ -130,6 +202,7 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
130
202
  ...stackProps
131
203
  }: FrappeListViewProps<TDoc>) {
132
204
  const config = useFrappeConfig();
205
+ const navigate = useFrappeRouter()?.navigate;
133
206
  const { knobProps } = useResolvedKnobs();
134
207
  const { t } = useTranslation();
135
208
  const theme = useTheme();
@@ -153,7 +226,10 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
153
226
  fixtures: config?.fixtures,
154
227
  };
155
228
 
156
- const [viewMode, setViewModeState] = useState<ListViewMode>(defaultViewMode);
229
+ const [storedViewMode, setViewModeState] = useState<ListViewMode>(defaultViewMode);
230
+ const requestedViewMode = controlledViewMode ?? storedViewMode;
231
+ const viewMode: ListViewMode =
232
+ requestedViewMode === "grid" && !renderCard ? "list" : requestedViewMode;
157
233
 
158
234
  useEffect(() => {
159
235
  let cancelled = false;
@@ -171,10 +247,12 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
171
247
 
172
248
  const setViewMode = useCallback(
173
249
  (mode: ListViewMode) => {
250
+ onViewModeChange?.(mode);
251
+ if (controlledViewMode !== undefined) return;
174
252
  setViewModeState(mode);
175
253
  void setStoredViewMode(doctype, mode);
176
254
  },
177
- [doctype],
255
+ [doctype, controlledViewMode, onViewModeChange],
178
256
  );
179
257
 
180
258
  const {
@@ -183,6 +261,27 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
183
261
  error: metaError,
184
262
  } = useDocTypeMeta(connectionConfig, doctype);
185
263
 
264
+ const onItemClick =
265
+ onItemClickProp ??
266
+ (route !== undefined && navigate
267
+ ? (doc: Partial<TDoc>) => {
268
+ if (doc.name) navigate(frappeRouteHref(route, { mode: "view", name: doc.name }));
269
+ }
270
+ : undefined);
271
+ const onAdd =
272
+ onAddProp ??
273
+ (route !== undefined && navigate && meta
274
+ ? () => navigate(frappeRouteHref(route, { mode: "create" }))
275
+ : undefined);
276
+ const asPage = (view: React.ReactElement) =>
277
+ route === undefined ? (
278
+ view
279
+ ) : (
280
+ <ListPageLayout scroll={false} header={<PageHeader title={doctype} divider={false} />}>
281
+ {view}
282
+ </ListPageLayout>
283
+ );
284
+
186
285
  // Guest fallback: DocType meta is permission-gated (Guests get 403) while
187
286
  // the documents themselves are often guest-readable. Peek one doc with
188
287
  // fields=["*"] and derive display fields from its keys so the views render
@@ -352,6 +451,18 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
352
451
  setActiveFilters(filters);
353
452
  }, [filters]);
354
453
 
454
+ const effectiveConnections = useMemo(
455
+ () => connections ?? (isMetaLoading ? undefined : connectionsFromMeta(meta)),
456
+ [connections, isMetaLoading, meta],
457
+ );
458
+ const linkFilters = useConnectionFilters(
459
+ connectionConfig,
460
+ doctype,
461
+ activeFilters as Parameters<typeof useConnectionFilters>[2],
462
+ effectiveConnections,
463
+ );
464
+ const resolvedFilters = linkFilters.filters as typeof activeFilters;
465
+
355
466
  // List-mode search: a debounced server-side `like` on the doctype's title
356
467
  // field, merged into the filters useFrappePagination already accepts (the
357
468
  // pagination layer has no orFilters plumbing, so one well-chosen field
@@ -368,21 +479,21 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
368
479
 
369
480
  const listFilters = useMemo(() => {
370
481
  const query = debouncedSearch.trim();
371
- if (!query) return activeFilters;
482
+ if (!query) return resolvedFilters;
372
483
  const searchFilter = [searchField, "like", `%${query}%`] as [
373
484
  keyof TDoc & string,
374
485
  string,
375
486
  unknown,
376
487
  ];
377
- if (!activeFilters) return [searchFilter];
378
- if (Array.isArray(activeFilters)) return [...activeFilters, searchFilter];
488
+ if (!resolvedFilters) return [searchFilter];
489
+ if (Array.isArray(resolvedFilters)) return [...resolvedFilters, searchFilter];
379
490
  return [
380
- ...Object.entries(activeFilters).map(
491
+ ...Object.entries(resolvedFilters).map(
381
492
  ([key, value]) => [key, "=", value] as [keyof TDoc & string, string, unknown],
382
493
  ),
383
494
  searchFilter,
384
495
  ];
385
- }, [activeFilters, debouncedSearch, searchField]);
496
+ }, [resolvedFilters, debouncedSearch, searchField]);
386
497
 
387
498
  // Axiom 6 / DG-TBL-06: zero rows under active filters or search must not
388
499
  // render the same chrome as a truly empty doctype — the no-results message
@@ -423,8 +534,10 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
423
534
  [savedFiltersHook],
424
535
  );
425
536
 
426
- if (isMetaLoading) {
427
- return (
537
+ // Named fields need no schema to read or draw: the view renders now (on
538
+ // the server too) and labels settle when meta lands.
539
+ if (isMetaLoading && !fields) {
540
+ return asPage(
428
541
  <YStack
429
542
  {...knobProps.panelPadding}
430
543
  alignItems="center"
@@ -435,7 +548,7 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
435
548
  <Text {...knobProps.body} color={knobProps.textAccentColor} marginTop="$2">
436
549
  {t("Loading {{doctype}} schema...", { doctype })}
437
550
  </Text>
438
- </YStack>
551
+ </YStack>,
439
552
  );
440
553
  }
441
554
 
@@ -456,6 +569,7 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
456
569
  <XStack alignItems="center" {...knobProps.gap}>
457
570
  <ViewSwitcher
458
571
  views={[
572
+ ...(renderCard ? [{ type: "grid", label: t("Cards") }] : []),
459
573
  { type: "list", label: t("List") },
460
574
  { type: "table", label: t("Table") },
461
575
  ]}
@@ -576,17 +690,23 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
576
690
  );
577
691
 
578
692
  const renderContent = () => {
579
- if (viewMode === "list") {
693
+ if (viewMode === "list" || viewMode === "grid") {
694
+ const isGrid = viewMode === "grid";
580
695
  return (
581
- <YStack flex={1}>
582
- {renderListSearch()}
696
+ <YStack flex={isGrid ? undefined : 1}>
697
+ {showSearch ? renderListSearch() : null}
583
698
  <FrappeList
584
- key={`list-${refreshKey}`}
699
+ key={`${viewMode}-${refreshKey}`}
700
+ layout={isGrid ? "grid" : "list"}
701
+ gridProps={gridProps}
702
+ emptyState={emptyState}
703
+ pageSize={pageSize}
704
+ onRetry={onRetry ?? handleRefresh}
585
705
  doctype={doctype}
586
706
  fields={effectiveFields as readonly ("name" | "doctype")[]}
587
707
  filters={listFilters as Partial<{ name: string; doctype: string }>}
588
708
  renderItem={
589
- renderListItem as unknown as (
709
+ (isGrid && renderCard ? renderCard : renderListItem) as unknown as (
590
710
  item: Partial<{ name: string; doctype: string }>,
591
711
  index: number,
592
712
  ) => React.ReactNode
@@ -599,6 +719,11 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
599
719
  baseURL={finalBaseURL}
600
720
  auth={finalAuth}
601
721
  syncOptions={finalSyncOptions}
722
+ orderBy={orderBy as FrappeListProps<{ name: string; doctype: string }>["orderBy"]}
723
+ limit={limit}
724
+ initialRows={
725
+ initialRows as FrappeListProps<{ name: string; doctype: string }>["initialRows"]
726
+ }
602
727
  // Only override when narrowing is active: FrappeList's own
603
728
  // "No {doctype} records found" stays the true-empty message.
604
729
  {...(noResultsMessage ? { emptyMessage: noResultsMessage } : {})}
@@ -612,7 +737,7 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
612
737
  key={`table-${refreshKey}`}
613
738
  doctype={doctype}
614
739
  fields={effectiveFields}
615
- filters={activeFilters}
740
+ filters={resolvedFilters as Parameters<typeof FrappeTable<TDoc>>[0]["filters"]}
616
741
  columns={effectiveColumns}
617
742
  onRowClick={onItemClick}
618
743
  baseURL={finalBaseURL}
@@ -622,24 +747,45 @@ export function FrappeListView<TDoc extends { name: string; doctype: string }>({
622
747
  );
623
748
  };
624
749
 
625
- return (
750
+ // A connection filter's matching records are known only once its linked
751
+ // rows are read; until then the view waits instead of listing everything.
752
+ const renderResolvedContent = () => {
753
+ if (linkFilters.error) {
754
+ return (
755
+ <AsyncBoundary
756
+ layout="list"
757
+ error={linkFilters.error}
758
+ errorTitle={
759
+ isPermissionError(linkFilters.error)
760
+ ? t("You do not have permission to view {{doctype}}", { doctype })
761
+ : t("Error loading {{doctype}}", { doctype })
762
+ }
763
+ />
764
+ );
765
+ }
766
+ if (linkFilters.pending) return <AsyncBoundary layout="list" loading />;
767
+ return renderContent();
768
+ };
769
+
770
+ return asPage(
626
771
  <YStack
627
- flex={1}
628
- {...knobProps.surface}
629
- {...knobProps.containerRadius}
630
- overflow="hidden"
772
+ flex={viewMode === "grid" ? undefined : 1}
773
+ {...(surface ? knobProps.surface : {})}
774
+ {...(surface ? knobProps.containerRadius : {})}
775
+ overflow={surface ? "hidden" : undefined}
631
776
  testID={`${doctype.toLowerCase()}-list`}
632
777
  {...stackProps}
633
778
  >
634
779
  {renderToolbar()}
780
+ {linkFilters.readers}
635
781
  {showFilterSidebar ? (
636
782
  <XStack flex={1}>
637
783
  {renderFilterSidebar()}
638
- <YStack flex={1}>{renderContent()}</YStack>
784
+ <YStack flex={1}>{renderResolvedContent()}</YStack>
639
785
  </XStack>
640
786
  ) : (
641
- renderContent()
787
+ renderResolvedContent()
642
788
  )}
643
- </YStack>
789
+ </YStack>,
644
790
  );
645
791
  }