@multiplatform.one/frappe-ui 7.46.0 → 7.47.1

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 (463) hide show
  1. package/dist/cjs/FileBrowser/index.cjs +1 -1
  2. package/dist/cjs/FileBrowser/index.native.js +1 -1
  3. package/dist/cjs/FileBrowser/index.native.js.map +1 -1
  4. package/dist/cjs/FrappeCalendar.cjs +2 -2
  5. package/dist/cjs/FrappeCalendar.native.js +2 -2
  6. package/dist/cjs/FrappeCalendar.native.js.map +1 -1
  7. package/dist/cjs/FrappeConnectionIndicator.cjs +2 -1
  8. package/dist/cjs/FrappeConnectionIndicator.native.js +2 -1
  9. package/dist/cjs/FrappeConnectionIndicator.native.js.map +1 -1
  10. package/dist/cjs/FrappeDashboard.cjs +1 -1
  11. package/dist/cjs/FrappeDashboard.native.js +1 -1
  12. package/dist/cjs/FrappeDashboard.native.js.map +1 -1
  13. package/dist/cjs/FrappeForm.cjs +54 -29
  14. package/dist/cjs/FrappeForm.native.js +56 -28
  15. package/dist/cjs/FrappeForm.native.js.map +1 -1
  16. package/dist/cjs/FrappeFormActions.cjs +121 -0
  17. package/dist/cjs/FrappeFormActions.native.js +123 -0
  18. package/dist/cjs/FrappeFormActions.native.js.map +1 -1
  19. package/dist/cjs/FrappeInsights.cjs +4 -1
  20. package/dist/cjs/FrappeInsights.native.js +4 -6
  21. package/dist/cjs/FrappeInsights.native.js.map +1 -1
  22. package/dist/cjs/FrappeKanban.cjs +1 -1
  23. package/dist/cjs/FrappeKanban.native.js +1 -1
  24. package/dist/cjs/FrappeKanban.native.js.map +1 -1
  25. package/dist/cjs/FrappeListView.cjs +1 -1
  26. package/dist/cjs/FrappeListView.native.js +4 -7
  27. package/dist/cjs/FrappeListView.native.js.map +1 -1
  28. package/dist/cjs/FrappeReconnectIndicator.cjs +2 -1
  29. package/dist/cjs/FrappeReconnectIndicator.native.js +2 -1
  30. package/dist/cjs/FrappeReconnectIndicator.native.js.map +1 -1
  31. package/dist/cjs/SyncErrorNotice.cjs +1 -1
  32. package/dist/cjs/SyncErrorNotice.native.js +1 -1
  33. package/dist/cjs/SyncErrorNotice.native.js.map +1 -1
  34. package/dist/cjs/desk/DeskFormPage.cjs +28 -42
  35. package/dist/cjs/desk/DeskFormPage.native.js +42 -58
  36. package/dist/cjs/desk/DeskFormPage.native.js.map +1 -1
  37. package/dist/cjs/desk/DeskShell.cjs +1 -1
  38. package/dist/cjs/desk/DeskShell.native.js +1 -1
  39. package/dist/cjs/desk/DeskShell.native.js.map +1 -1
  40. package/dist/cjs/desk/awesomebar.cjs +1 -1
  41. package/dist/cjs/desk/awesomebar.native.js +1 -1
  42. package/dist/cjs/desk/awesomebar.native.js.map +1 -1
  43. package/dist/cjs/fields/CodeEditor/index.cjs +1 -1
  44. package/dist/cjs/fields/CodeEditor/index.native.js +1 -1
  45. package/dist/cjs/fields/CodeEditor/index.native.js.map +1 -1
  46. package/dist/cjs/fields/Date/index.native.js +5 -4
  47. package/dist/cjs/fields/Date/index.native.js.map +1 -1
  48. package/dist/cjs/fields/FileUpload.cjs +10 -8
  49. package/dist/cjs/fields/FileUpload.native.js +10 -8
  50. package/dist/cjs/fields/FileUpload.native.js.map +1 -1
  51. package/dist/cjs/fields/FileUploadField/index.cjs +50 -4
  52. package/dist/cjs/fields/FileUploadField/index.native.js +54 -5
  53. package/dist/cjs/fields/FileUploadField/index.native.js.map +1 -1
  54. package/dist/cjs/fields/Heading/index.cjs +1 -1
  55. package/dist/cjs/fields/Heading/index.native.js +1 -1
  56. package/dist/cjs/fields/Heading/index.native.js.map +1 -1
  57. package/dist/cjs/fields/MarkdownEditor/index.native.js +3 -2
  58. package/dist/cjs/fields/MarkdownEditor/index.native.js.map +1 -1
  59. package/dist/cjs/fields/Numeric/index.cjs +3 -2
  60. package/dist/cjs/fields/Numeric/index.native.js +3 -2
  61. package/dist/cjs/fields/Numeric/index.native.js.map +1 -1
  62. package/dist/cjs/fields/Password/index.cjs +18 -62
  63. package/dist/cjs/fields/Password/index.native.js +18 -65
  64. package/dist/cjs/fields/Password/index.native.js.map +1 -1
  65. package/dist/cjs/fields/ReadOnly/index.cjs +2 -1
  66. package/dist/cjs/fields/ReadOnly/index.native.js +2 -1
  67. package/dist/cjs/fields/ReadOnly/index.native.js.map +1 -1
  68. package/dist/cjs/index.cjs +2 -0
  69. package/dist/cjs/index.native.js +2 -0
  70. package/dist/cjs/index.native.js.map +1 -1
  71. package/dist/cjs/insights/InsightsLeaders.cjs +2 -2
  72. package/dist/cjs/insights/InsightsLeaders.native.js +2 -2
  73. package/dist/cjs/insights/InsightsLeaders.native.js.map +1 -1
  74. package/dist/cjs/insights/InsightsRecords.cjs +2 -2
  75. package/dist/cjs/insights/InsightsRecords.native.js +2 -2
  76. package/dist/cjs/insights/InsightsRecords.native.js.map +1 -1
  77. package/dist/cjs/list/FacetBadge.cjs +4 -3
  78. package/dist/cjs/list/FacetBadge.native.js +4 -3
  79. package/dist/cjs/list/FacetBadge.native.js.map +1 -1
  80. package/dist/cjs/list/ImageWell.cjs +6 -18
  81. package/dist/cjs/list/ImageWell.native.js +7 -19
  82. package/dist/cjs/list/ImageWell.native.js.map +1 -1
  83. package/dist/cjs/list/RecordCard.cjs +2 -2
  84. package/dist/cjs/list/RecordCard.native.js +2 -2
  85. package/dist/cjs/list/RecordCard.native.js.map +1 -1
  86. package/dist/cjs/list/facetBadgeSteps.cjs +32 -0
  87. package/dist/cjs/list/facetBadgeSteps.native.js +35 -0
  88. package/dist/cjs/list/facetBadgeSteps.native.js.map +1 -0
  89. package/dist/cjs/record/RecordHero.cjs +4 -15
  90. package/dist/cjs/record/RecordHero.native.js +4 -15
  91. package/dist/cjs/record/RecordHero.native.js.map +1 -1
  92. package/dist/cjs/record/RecordPage.cjs +4 -4
  93. package/dist/cjs/record/RecordPage.native.js +7 -10
  94. package/dist/cjs/record/RecordPage.native.js.map +1 -1
  95. package/dist/cjs/report/ReportGrid.cjs +6 -6
  96. package/dist/cjs/report/ReportGrid.native.js +11 -7
  97. package/dist/cjs/report/ReportGrid.native.js.map +1 -1
  98. package/dist/cjs/schemaForm/index.cjs +3 -3
  99. package/dist/cjs/schemaForm/index.native.js +3 -3
  100. package/dist/cjs/schemaForm/index.native.js.map +1 -1
  101. package/dist/esm/FileBrowser/index.mjs +8 -8
  102. package/dist/esm/FileBrowser/index.mjs.map +1 -1
  103. package/dist/esm/FileBrowser/index.native.js +8 -8
  104. package/dist/esm/FileBrowser/index.native.js.map +1 -1
  105. package/dist/esm/FrappeCalendar.mjs +6 -6
  106. package/dist/esm/FrappeCalendar.mjs.map +1 -1
  107. package/dist/esm/FrappeCalendar.native.js +6 -6
  108. package/dist/esm/FrappeCalendar.native.js.map +1 -1
  109. package/dist/esm/FrappeConnectionIndicator.mjs +2 -1
  110. package/dist/esm/FrappeConnectionIndicator.mjs.map +1 -1
  111. package/dist/esm/FrappeConnectionIndicator.native.js +2 -1
  112. package/dist/esm/FrappeConnectionIndicator.native.js.map +1 -1
  113. package/dist/esm/FrappeDashboard.mjs +6 -6
  114. package/dist/esm/FrappeDashboard.mjs.map +1 -1
  115. package/dist/esm/FrappeDashboard.native.js +6 -6
  116. package/dist/esm/FrappeDashboard.native.js.map +1 -1
  117. package/dist/esm/FrappeForm.mjs +52 -27
  118. package/dist/esm/FrappeForm.mjs.map +1 -1
  119. package/dist/esm/FrappeForm.native.js +56 -28
  120. package/dist/esm/FrappeForm.native.js.map +1 -1
  121. package/dist/esm/FrappeFormActions.mjs +122 -2
  122. package/dist/esm/FrappeFormActions.mjs.map +1 -1
  123. package/dist/esm/FrappeFormActions.native.js +124 -2
  124. package/dist/esm/FrappeFormActions.native.js.map +1 -1
  125. package/dist/esm/FrappeInsights.mjs +4 -1
  126. package/dist/esm/FrappeInsights.mjs.map +1 -1
  127. package/dist/esm/FrappeInsights.native.js +4 -6
  128. package/dist/esm/FrappeInsights.native.js.map +1 -1
  129. package/dist/esm/FrappeKanban.mjs +5 -5
  130. package/dist/esm/FrappeKanban.mjs.map +1 -1
  131. package/dist/esm/FrappeKanban.native.js +5 -5
  132. package/dist/esm/FrappeKanban.native.js.map +1 -1
  133. package/dist/esm/FrappeListView.mjs +1 -1
  134. package/dist/esm/FrappeListView.mjs.map +1 -1
  135. package/dist/esm/FrappeListView.native.js +4 -7
  136. package/dist/esm/FrappeListView.native.js.map +1 -1
  137. package/dist/esm/FrappeReconnectIndicator.mjs +2 -1
  138. package/dist/esm/FrappeReconnectIndicator.mjs.map +1 -1
  139. package/dist/esm/FrappeReconnectIndicator.native.js +2 -1
  140. package/dist/esm/FrappeReconnectIndicator.native.js.map +1 -1
  141. package/dist/esm/SyncErrorNotice.mjs +2 -2
  142. package/dist/esm/SyncErrorNotice.mjs.map +1 -1
  143. package/dist/esm/SyncErrorNotice.native.js +2 -2
  144. package/dist/esm/SyncErrorNotice.native.js.map +1 -1
  145. package/dist/esm/desk/DeskFormPage.mjs +29 -43
  146. package/dist/esm/desk/DeskFormPage.mjs.map +1 -1
  147. package/dist/esm/desk/DeskFormPage.native.js +43 -59
  148. package/dist/esm/desk/DeskFormPage.native.js.map +1 -1
  149. package/dist/esm/desk/DeskShell.mjs +1 -1
  150. package/dist/esm/desk/DeskShell.mjs.map +1 -1
  151. package/dist/esm/desk/DeskShell.native.js +1 -1
  152. package/dist/esm/desk/DeskShell.native.js.map +1 -1
  153. package/dist/esm/desk/awesomebar.mjs +1 -1
  154. package/dist/esm/desk/awesomebar.mjs.map +1 -1
  155. package/dist/esm/desk/awesomebar.native.js +1 -1
  156. package/dist/esm/desk/awesomebar.native.js.map +1 -1
  157. package/dist/esm/fields/CodeEditor/index.mjs +3 -3
  158. package/dist/esm/fields/CodeEditor/index.mjs.map +1 -1
  159. package/dist/esm/fields/CodeEditor/index.native.js +3 -3
  160. package/dist/esm/fields/CodeEditor/index.native.js.map +1 -1
  161. package/dist/esm/fields/Date/index.native.js +2 -1
  162. package/dist/esm/fields/Date/index.native.js.map +1 -1
  163. package/dist/esm/fields/FileUpload.mjs +5 -3
  164. package/dist/esm/fields/FileUpload.mjs.map +1 -1
  165. package/dist/esm/fields/FileUpload.native.js +5 -3
  166. package/dist/esm/fields/FileUpload.native.js.map +1 -1
  167. package/dist/esm/fields/FileUploadField/index.mjs +52 -6
  168. package/dist/esm/fields/FileUploadField/index.mjs.map +1 -1
  169. package/dist/esm/fields/FileUploadField/index.native.js +56 -7
  170. package/dist/esm/fields/FileUploadField/index.native.js.map +1 -1
  171. package/dist/esm/fields/Heading/index.mjs +3 -3
  172. package/dist/esm/fields/Heading/index.mjs.map +1 -1
  173. package/dist/esm/fields/Heading/index.native.js +3 -3
  174. package/dist/esm/fields/Heading/index.native.js.map +1 -1
  175. package/dist/esm/fields/MarkdownEditor/index.native.js +2 -1
  176. package/dist/esm/fields/MarkdownEditor/index.native.js.map +1 -1
  177. package/dist/esm/fields/Numeric/index.mjs +2 -1
  178. package/dist/esm/fields/Numeric/index.mjs.map +1 -1
  179. package/dist/esm/fields/Numeric/index.native.js +2 -1
  180. package/dist/esm/fields/Numeric/index.native.js.map +1 -1
  181. package/dist/esm/fields/Password/index.mjs +16 -60
  182. package/dist/esm/fields/Password/index.mjs.map +1 -1
  183. package/dist/esm/fields/Password/index.native.js +16 -63
  184. package/dist/esm/fields/Password/index.native.js.map +1 -1
  185. package/dist/esm/fields/ReadOnly/index.mjs +7 -6
  186. package/dist/esm/fields/ReadOnly/index.mjs.map +1 -1
  187. package/dist/esm/fields/ReadOnly/index.native.js +7 -6
  188. package/dist/esm/fields/ReadOnly/index.native.js.map +1 -1
  189. package/dist/esm/index.mjs +2 -1
  190. package/dist/esm/index.mjs.map +1 -1
  191. package/dist/esm/index.native.js +2 -1
  192. package/dist/esm/index.native.js.map +1 -1
  193. package/dist/esm/insights/InsightsLeaders.mjs +2 -2
  194. package/dist/esm/insights/InsightsLeaders.mjs.map +1 -1
  195. package/dist/esm/insights/InsightsLeaders.native.js +2 -2
  196. package/dist/esm/insights/InsightsLeaders.native.js.map +1 -1
  197. package/dist/esm/insights/InsightsRecords.mjs +2 -2
  198. package/dist/esm/insights/InsightsRecords.mjs.map +1 -1
  199. package/dist/esm/insights/InsightsRecords.native.js +2 -2
  200. package/dist/esm/insights/InsightsRecords.native.js.map +1 -1
  201. package/dist/esm/list/FacetBadge.mjs +4 -3
  202. package/dist/esm/list/FacetBadge.mjs.map +1 -1
  203. package/dist/esm/list/FacetBadge.native.js +4 -3
  204. package/dist/esm/list/FacetBadge.native.js.map +1 -1
  205. package/dist/esm/list/ImageWell.mjs +8 -20
  206. package/dist/esm/list/ImageWell.mjs.map +1 -1
  207. package/dist/esm/list/ImageWell.native.js +9 -21
  208. package/dist/esm/list/ImageWell.native.js.map +1 -1
  209. package/dist/esm/list/RecordCard.mjs +2 -2
  210. package/dist/esm/list/RecordCard.mjs.map +1 -1
  211. package/dist/esm/list/RecordCard.native.js +2 -2
  212. package/dist/esm/list/RecordCard.native.js.map +1 -1
  213. package/dist/esm/list/facetBadgeSteps.mjs +7 -0
  214. package/dist/esm/list/facetBadgeSteps.mjs.map +1 -0
  215. package/dist/esm/list/facetBadgeSteps.native.js +7 -0
  216. package/dist/esm/list/facetBadgeSteps.native.js.map +1 -0
  217. package/dist/esm/record/RecordHero.mjs +4 -15
  218. package/dist/esm/record/RecordHero.mjs.map +1 -1
  219. package/dist/esm/record/RecordHero.native.js +4 -15
  220. package/dist/esm/record/RecordHero.native.js.map +1 -1
  221. package/dist/esm/record/RecordPage.mjs +4 -4
  222. package/dist/esm/record/RecordPage.mjs.map +1 -1
  223. package/dist/esm/record/RecordPage.native.js +7 -10
  224. package/dist/esm/record/RecordPage.native.js.map +1 -1
  225. package/dist/esm/report/ReportGrid.mjs +7 -7
  226. package/dist/esm/report/ReportGrid.mjs.map +1 -1
  227. package/dist/esm/report/ReportGrid.native.js +12 -8
  228. package/dist/esm/report/ReportGrid.native.js.map +1 -1
  229. package/dist/esm/schemaForm/index.mjs +4 -4
  230. package/dist/esm/schemaForm/index.mjs.map +1 -1
  231. package/dist/esm/schemaForm/index.native.js +4 -4
  232. package/dist/esm/schemaForm/index.native.js.map +1 -1
  233. package/dist/jsx/FileBrowser/index.mjs +8 -8
  234. package/dist/jsx/FileBrowser/index.mjs.map +1 -1
  235. package/dist/jsx/FileBrowser/index.native.js +1 -1
  236. package/dist/jsx/FileBrowser/index.native.js.map +1 -1
  237. package/dist/jsx/FrappeCalendar.mjs +6 -6
  238. package/dist/jsx/FrappeCalendar.mjs.map +1 -1
  239. package/dist/jsx/FrappeCalendar.native.js +2 -2
  240. package/dist/jsx/FrappeCalendar.native.js.map +1 -1
  241. package/dist/jsx/FrappeConnectionIndicator.mjs +2 -1
  242. package/dist/jsx/FrappeConnectionIndicator.mjs.map +1 -1
  243. package/dist/jsx/FrappeConnectionIndicator.native.js +2 -1
  244. package/dist/jsx/FrappeConnectionIndicator.native.js.map +1 -1
  245. package/dist/jsx/FrappeDashboard.mjs +6 -6
  246. package/dist/jsx/FrappeDashboard.mjs.map +1 -1
  247. package/dist/jsx/FrappeDashboard.native.js +1 -1
  248. package/dist/jsx/FrappeDashboard.native.js.map +1 -1
  249. package/dist/jsx/FrappeForm.mjs +52 -27
  250. package/dist/jsx/FrappeForm.mjs.map +1 -1
  251. package/dist/jsx/FrappeForm.native.js +56 -28
  252. package/dist/jsx/FrappeForm.native.js.map +1 -1
  253. package/dist/jsx/FrappeFormActions.mjs +122 -2
  254. package/dist/jsx/FrappeFormActions.mjs.map +1 -1
  255. package/dist/jsx/FrappeFormActions.native.js +123 -0
  256. package/dist/jsx/FrappeFormActions.native.js.map +1 -1
  257. package/dist/jsx/FrappeInsights.mjs +4 -1
  258. package/dist/jsx/FrappeInsights.mjs.map +1 -1
  259. package/dist/jsx/FrappeInsights.native.js +4 -6
  260. package/dist/jsx/FrappeInsights.native.js.map +1 -1
  261. package/dist/jsx/FrappeKanban.mjs +5 -5
  262. package/dist/jsx/FrappeKanban.mjs.map +1 -1
  263. package/dist/jsx/FrappeKanban.native.js +1 -1
  264. package/dist/jsx/FrappeKanban.native.js.map +1 -1
  265. package/dist/jsx/FrappeListView.mjs +1 -1
  266. package/dist/jsx/FrappeListView.mjs.map +1 -1
  267. package/dist/jsx/FrappeListView.native.js +4 -7
  268. package/dist/jsx/FrappeListView.native.js.map +1 -1
  269. package/dist/jsx/FrappeReconnectIndicator.mjs +2 -1
  270. package/dist/jsx/FrappeReconnectIndicator.mjs.map +1 -1
  271. package/dist/jsx/FrappeReconnectIndicator.native.js +2 -1
  272. package/dist/jsx/FrappeReconnectIndicator.native.js.map +1 -1
  273. package/dist/jsx/SyncErrorNotice.mjs +2 -2
  274. package/dist/jsx/SyncErrorNotice.mjs.map +1 -1
  275. package/dist/jsx/SyncErrorNotice.native.js +1 -1
  276. package/dist/jsx/SyncErrorNotice.native.js.map +1 -1
  277. package/dist/jsx/desk/DeskFormPage.mjs +29 -43
  278. package/dist/jsx/desk/DeskFormPage.mjs.map +1 -1
  279. package/dist/jsx/desk/DeskFormPage.native.js +42 -58
  280. package/dist/jsx/desk/DeskFormPage.native.js.map +1 -1
  281. package/dist/jsx/desk/DeskShell.mjs +1 -1
  282. package/dist/jsx/desk/DeskShell.mjs.map +1 -1
  283. package/dist/jsx/desk/DeskShell.native.js +1 -1
  284. package/dist/jsx/desk/DeskShell.native.js.map +1 -1
  285. package/dist/jsx/desk/awesomebar.mjs +1 -1
  286. package/dist/jsx/desk/awesomebar.mjs.map +1 -1
  287. package/dist/jsx/desk/awesomebar.native.js +1 -1
  288. package/dist/jsx/desk/awesomebar.native.js.map +1 -1
  289. package/dist/jsx/fields/CodeEditor/index.mjs +3 -3
  290. package/dist/jsx/fields/CodeEditor/index.mjs.map +1 -1
  291. package/dist/jsx/fields/CodeEditor/index.native.js +1 -1
  292. package/dist/jsx/fields/CodeEditor/index.native.js.map +1 -1
  293. package/dist/jsx/fields/Date/index.native.js +5 -4
  294. package/dist/jsx/fields/Date/index.native.js.map +1 -1
  295. package/dist/jsx/fields/FileUpload.mjs +5 -3
  296. package/dist/jsx/fields/FileUpload.mjs.map +1 -1
  297. package/dist/jsx/fields/FileUpload.native.js +10 -8
  298. package/dist/jsx/fields/FileUpload.native.js.map +1 -1
  299. package/dist/jsx/fields/FileUploadField/index.mjs +52 -6
  300. package/dist/jsx/fields/FileUploadField/index.mjs.map +1 -1
  301. package/dist/jsx/fields/FileUploadField/index.native.js +54 -5
  302. package/dist/jsx/fields/FileUploadField/index.native.js.map +1 -1
  303. package/dist/jsx/fields/Heading/index.mjs +3 -3
  304. package/dist/jsx/fields/Heading/index.mjs.map +1 -1
  305. package/dist/jsx/fields/Heading/index.native.js +1 -1
  306. package/dist/jsx/fields/Heading/index.native.js.map +1 -1
  307. package/dist/jsx/fields/MarkdownEditor/index.native.js +3 -2
  308. package/dist/jsx/fields/MarkdownEditor/index.native.js.map +1 -1
  309. package/dist/jsx/fields/Numeric/index.mjs +2 -1
  310. package/dist/jsx/fields/Numeric/index.mjs.map +1 -1
  311. package/dist/jsx/fields/Numeric/index.native.js +3 -2
  312. package/dist/jsx/fields/Numeric/index.native.js.map +1 -1
  313. package/dist/jsx/fields/Password/index.mjs +16 -60
  314. package/dist/jsx/fields/Password/index.mjs.map +1 -1
  315. package/dist/jsx/fields/Password/index.native.js +18 -65
  316. package/dist/jsx/fields/Password/index.native.js.map +1 -1
  317. package/dist/jsx/fields/ReadOnly/index.mjs +7 -6
  318. package/dist/jsx/fields/ReadOnly/index.mjs.map +1 -1
  319. package/dist/jsx/fields/ReadOnly/index.native.js +2 -1
  320. package/dist/jsx/fields/ReadOnly/index.native.js.map +1 -1
  321. package/dist/jsx/index.js +2 -1
  322. package/dist/jsx/index.js.map +1 -1
  323. package/dist/jsx/index.mjs +2 -1
  324. package/dist/jsx/index.mjs.map +1 -1
  325. package/dist/jsx/index.native.js +2 -0
  326. package/dist/jsx/index.native.js.map +1 -1
  327. package/dist/jsx/insights/InsightsLeaders.mjs +2 -2
  328. package/dist/jsx/insights/InsightsLeaders.mjs.map +1 -1
  329. package/dist/jsx/insights/InsightsLeaders.native.js +2 -2
  330. package/dist/jsx/insights/InsightsLeaders.native.js.map +1 -1
  331. package/dist/jsx/insights/InsightsRecords.mjs +2 -2
  332. package/dist/jsx/insights/InsightsRecords.mjs.map +1 -1
  333. package/dist/jsx/insights/InsightsRecords.native.js +2 -2
  334. package/dist/jsx/insights/InsightsRecords.native.js.map +1 -1
  335. package/dist/jsx/list/FacetBadge.mjs +4 -3
  336. package/dist/jsx/list/FacetBadge.mjs.map +1 -1
  337. package/dist/jsx/list/FacetBadge.native.js +4 -3
  338. package/dist/jsx/list/FacetBadge.native.js.map +1 -1
  339. package/dist/jsx/list/ImageWell.mjs +8 -20
  340. package/dist/jsx/list/ImageWell.mjs.map +1 -1
  341. package/dist/jsx/list/ImageWell.native.js +7 -19
  342. package/dist/jsx/list/ImageWell.native.js.map +1 -1
  343. package/dist/jsx/list/RecordCard.mjs +2 -2
  344. package/dist/jsx/list/RecordCard.mjs.map +1 -1
  345. package/dist/jsx/list/RecordCard.native.js +2 -2
  346. package/dist/jsx/list/RecordCard.native.js.map +1 -1
  347. package/dist/jsx/list/facetBadgeSteps.mjs +7 -0
  348. package/dist/jsx/list/facetBadgeSteps.mjs.map +1 -0
  349. package/dist/jsx/list/facetBadgeSteps.native.js +35 -0
  350. package/dist/jsx/list/facetBadgeSteps.native.js.map +1 -0
  351. package/dist/jsx/record/RecordHero.mjs +4 -15
  352. package/dist/jsx/record/RecordHero.mjs.map +1 -1
  353. package/dist/jsx/record/RecordHero.native.js +4 -15
  354. package/dist/jsx/record/RecordHero.native.js.map +1 -1
  355. package/dist/jsx/record/RecordPage.mjs +4 -4
  356. package/dist/jsx/record/RecordPage.mjs.map +1 -1
  357. package/dist/jsx/record/RecordPage.native.js +7 -10
  358. package/dist/jsx/record/RecordPage.native.js.map +1 -1
  359. package/dist/jsx/report/ReportGrid.mjs +7 -7
  360. package/dist/jsx/report/ReportGrid.mjs.map +1 -1
  361. package/dist/jsx/report/ReportGrid.native.js +11 -7
  362. package/dist/jsx/report/ReportGrid.native.js.map +1 -1
  363. package/dist/jsx/schemaForm/index.mjs +4 -4
  364. package/dist/jsx/schemaForm/index.mjs.map +1 -1
  365. package/dist/jsx/schemaForm/index.native.js +3 -3
  366. package/dist/jsx/schemaForm/index.native.js.map +1 -1
  367. package/package.json +16 -16
  368. package/src/FileBrowser/index.tsx +14 -13
  369. package/src/FormSidebar.stories.tsx +2 -1
  370. package/src/FrappeCalendar.stories.tsx +1 -1
  371. package/src/FrappeCalendar.tsx +10 -9
  372. package/src/FrappeConnectionIndicator.stories.tsx +2 -1
  373. package/src/FrappeConnectionIndicator.tsx +2 -1
  374. package/src/FrappeDashboard.stories.tsx +2 -2
  375. package/src/FrappeDashboard.tsx +10 -10
  376. package/src/FrappeForm.lifecycle.spec.tsx +3 -1
  377. package/src/FrappeForm.tsx +106 -38
  378. package/src/FrappeFormActions.tsx +160 -1
  379. package/src/FrappeInsights.spec.tsx +1 -1
  380. package/src/FrappeInsights.tsx +4 -1
  381. package/src/FrappeKanban.stories.tsx +2 -1
  382. package/src/FrappeKanban.tsx +8 -7
  383. package/src/FrappeList.stories.tsx +11 -10
  384. package/src/FrappeListView.card.spec.tsx +1 -1
  385. package/src/FrappeListView.sidebar.spec.tsx +1 -1
  386. package/src/FrappeListView.tsx +1 -1
  387. package/src/FrappeProvider.stories.tsx +2 -1
  388. package/src/FrappeReconnectIndicator.stories.tsx +2 -1
  389. package/src/FrappeReconnectIndicator.tsx +2 -1
  390. package/src/SyncErrorNotice.tsx +2 -2
  391. package/src/desk/DeskFormPage.tsx +42 -62
  392. package/src/desk/DeskFormSidebar.stories.tsx +4 -2
  393. package/src/desk/DeskShell.tsx +1 -1
  394. package/src/desk/awesomebar.tsx +1 -1
  395. package/src/fields/CodeEditor/index.tsx +4 -4
  396. package/src/fields/Date/index.native.tsx +2 -1
  397. package/src/fields/FileUpload.tsx +8 -4
  398. package/src/fields/FileUploadField/FileUploadField.spec.tsx +86 -1
  399. package/src/fields/FileUploadField/FileUploadField.stories.tsx +9 -0
  400. package/src/fields/FileUploadField/index.tsx +67 -6
  401. package/src/fields/Heading/index.tsx +4 -4
  402. package/src/fields/MarkdownEditor/index.native.tsx +2 -1
  403. package/src/fields/Numeric/index.tsx +2 -1
  404. package/src/fields/Password/index.tsx +14 -70
  405. package/src/fields/ReadOnly/index.tsx +12 -11
  406. package/src/fields/TextEditor/TextEditor.stories.tsx +2 -1
  407. package/src/frappeRemainder.designLaw.spec.ts +3 -2
  408. package/src/frappeRoutes.spec.tsx +20 -6
  409. package/src/index.ts +7 -1
  410. package/src/insights/InsightsLeaders.tsx +2 -2
  411. package/src/insights/InsightsRecords.tsx +2 -2
  412. package/src/insights/insightsRecord.ts +2 -2
  413. package/src/list/FacetBadge.tsx +5 -4
  414. package/src/list/ImageWell.spec.tsx +47 -0
  415. package/src/list/ImageWell.tsx +8 -21
  416. package/src/list/RecordCard.native.tsx +2 -2
  417. package/src/list/RecordCard.spec.tsx +1 -1
  418. package/src/list/RecordCard.tsx +4 -4
  419. package/src/list/facetBadgeSteps.ts +2 -0
  420. package/src/record/RecordHero.tsx +4 -11
  421. package/src/record/RecordPage.tsx +4 -4
  422. package/src/report/ReportGrid.native.spec.tsx +152 -0
  423. package/src/report/ReportGrid.tsx +20 -9
  424. package/src/schemaForm/SchemaForm.stories.tsx +2 -1
  425. package/src/schemaForm/index.tsx +6 -6
  426. package/types/FileBrowser/index.d.ts.map +1 -1
  427. package/types/FrappeCalendar.d.ts.map +1 -1
  428. package/types/FrappeConnectionIndicator.d.ts.map +1 -1
  429. package/types/FrappeDashboard.d.ts.map +1 -1
  430. package/types/FrappeForm.d.ts +19 -0
  431. package/types/FrappeForm.d.ts.map +1 -1
  432. package/types/FrappeFormActions.d.ts +7 -0
  433. package/types/FrappeFormActions.d.ts.map +1 -1
  434. package/types/FrappeInsights.d.ts.map +1 -1
  435. package/types/FrappeKanban.d.ts.map +1 -1
  436. package/types/FrappeReconnectIndicator.d.ts.map +1 -1
  437. package/types/desk/DeskFormPage.d.ts +6 -6
  438. package/types/desk/DeskFormPage.d.ts.map +1 -1
  439. package/types/fields/Date/index.native.d.ts.map +1 -1
  440. package/types/fields/FileUpload.d.ts.map +1 -1
  441. package/types/fields/FileUploadField/index.d.ts.map +1 -1
  442. package/types/fields/MarkdownEditor/index.native.d.ts.map +1 -1
  443. package/types/fields/Numeric/index.d.ts.map +1 -1
  444. package/types/fields/Password/index.d.ts +13 -0
  445. package/types/fields/Password/index.d.ts.map +1 -1
  446. package/types/fields/ReadOnly/index.d.ts.map +1 -1
  447. package/types/index.d.ts +2 -1
  448. package/types/index.d.ts.map +1 -1
  449. package/types/insights/insightsRecord.d.ts +2 -2
  450. package/types/insights/insightsRecord.d.ts.map +1 -1
  451. package/types/list/FacetBadge.d.ts +1 -1
  452. package/types/list/FacetBadge.d.ts.map +1 -1
  453. package/types/list/ImageWell.d.ts +5 -4
  454. package/types/list/ImageWell.d.ts.map +1 -1
  455. package/types/list/RecordCard.d.ts +3 -3
  456. package/types/list/RecordCard.d.ts.map +1 -1
  457. package/types/list/RecordCard.native.d.ts +1 -1
  458. package/types/list/RecordCard.native.d.ts.map +1 -1
  459. package/types/list/facetBadgeSteps.d.ts +7 -0
  460. package/types/list/facetBadgeSteps.d.ts.map +1 -0
  461. package/types/record/RecordHero.d.ts +3 -3
  462. package/types/record/RecordHero.d.ts.map +1 -1
  463. package/types/report/ReportGrid.d.ts.map +1 -1
@@ -5,8 +5,9 @@ import { sizeRecipeForToken, useResolvedKnobs, sizeTokenStep } from "@multiplatf
5
5
  import type React from "react";
6
6
  import { useCallback, useEffect, useRef, useState } from "react";
7
7
  import { useTranslation } from "react-i18next";
8
- import { Button, Image, Label, Text, XStack, YStack } from "tamagui";
8
+ import { Button, Image, Label, Text as TamaguiText, XStack, YStack } from "tamagui";
9
9
  import { useFrappeConfig } from "../FrappeProvider";
10
+ import { Text } from "@multiplatform.one/components";
10
11
 
11
12
  export type FrappeFileUploadHandler = (
12
13
  file: File,
@@ -189,7 +190,10 @@ export function FileUpload({
189
190
  } catch (err) {
190
191
  if (
191
192
  controller.signal.aborted ||
192
- (err instanceof DOMException && err.name === "AbortError")
193
+ (err instanceof Error && err.name === "AbortError") ||
194
+ (typeof DOMException !== "undefined" &&
195
+ err instanceof DOMException &&
196
+ err.name === "AbortError")
193
197
  ) {
194
198
  return;
195
199
  }
@@ -290,9 +294,9 @@ export function FileUpload({
290
294
  return (
291
295
  <YStack {...knobProps.gap}>
292
296
  {label && (
293
- <Label>
297
+ <Label {...knobProps.label}>
294
298
  {label}
295
- {required && <Text color="$red10"> *</Text>}
299
+ {required && <TamaguiText color="$red10"> *</TamaguiText>}
296
300
  </Label>
297
301
  )}
298
302
 
@@ -11,9 +11,11 @@
11
11
  * 3. a controlled `value` winning over `defaultValue`, so a saved attachment
12
12
  * paints on a re-render instead of the mount-time default.
13
13
  */
14
+ import { Form, useForm } from "@multiplatform.one/forms";
14
15
  import { renderWithProviders } from "@multiplatform.one/test-utils";
15
- import { cleanup } from "@testing-library/react";
16
+ import { act, cleanup, fireEvent } from "@testing-library/react";
16
17
  import type React from "react";
18
+ import { useState } from "react";
17
19
  import { afterEach, describe, expect, it, vi } from "vitest";
18
20
  import { getFieldComponent, type FieldRenderProps } from "../../fieldRegistry";
19
21
  import { FileUploadField } from ".";
@@ -202,3 +204,86 @@ describe("FileUploadField stored value", () => {
202
204
  expect(result.container.querySelector('[data-file-upload="existing"]')).toBeNull();
203
205
  });
204
206
  });
207
+
208
+ describe("FileUploadField attachment states", () => {
209
+ it("hides source choices until Replace and keeps the library reachable", async () => {
210
+ const result = renderWithProviders(<FileUploadField value="/files/a.png" isImage />);
211
+ expect(result.getButton("From Library")).toBeUndefined();
212
+ expect(result.getButton("Choose File")).toBeUndefined();
213
+ await act(async () => {
214
+ fireEvent.click(result.container.querySelector('[aria-label="Replace"]')!);
215
+ });
216
+ expect(result.getButton("From Library")).toBeTruthy();
217
+ expect(result.getButton("Choose File")).toBeTruthy();
218
+ expect(result.container.querySelector('[data-file-upload="preview"] img')).toBeTruthy();
219
+ });
220
+
221
+ it("shows an empty field after its controlled stored value is removed", () => {
222
+ const result = renderWithProviders(<FileUploadField value="/files/a.png" isImage />);
223
+ result.rerender(<FileUploadField value="" isImage />);
224
+ expect(result.container.querySelector('[data-file-upload="preview"]')).toBeNull();
225
+ expect(result.getButton("From Library")).toBeTruthy();
226
+ });
227
+
228
+ it("keeps locked stored attachments free of replacement actions", () => {
229
+ const result = renderWithProviders(<FileUploadField value="/files/a.png" isImage disabled />);
230
+ expect(result.getButton("Replace")).toBeUndefined();
231
+ expect(result.getButton("From Library")).toBeUndefined();
232
+ expect(result.container.querySelector('[data-file-upload="preview"] img')).toBeTruthy();
233
+ });
234
+ });
235
+
236
+ it("keeps the newly selected file visible when replacing a controlled stored URL", async () => {
237
+ function Controlled() {
238
+ const [value, setValue] = useState<unknown>("/files/old.pdf");
239
+ return <FileUploadField value={value} onChange={setValue} />;
240
+ }
241
+ const result = renderWithProviders(<Controlled />);
242
+ await act(async () => {
243
+ fireEvent.change(nativePicker(result.container)!, {
244
+ target: { files: [new File(["new"], "new.pdf", { type: "application/pdf" })] },
245
+ });
246
+ });
247
+ expect(result.container.textContent).toContain("new.pdf");
248
+ expect(result.container.textContent).not.toContain("Choose File");
249
+ });
250
+
251
+ it("paints and removes a stored URL bound through a form", async () => {
252
+ let readValue: () => unknown = () => undefined;
253
+ function Harness() {
254
+ const form = useForm({ defaultValues: { attachment: "/files/old.png" } });
255
+ readValue = () => form.state.values.attachment;
256
+ return (
257
+ <Form form={form}>
258
+ <FileUploadField form={form} name="attachment" isImage />
259
+ </Form>
260
+ );
261
+ }
262
+ const result = renderWithProviders(<Harness />);
263
+ expect(result.container.querySelector('[data-file-upload="preview"] img')).toBeTruthy();
264
+ await act(async () => {
265
+ fireEvent.click(result.getButton("Remove old.png")!);
266
+ });
267
+ expect(readValue()).toBe("");
268
+ expect(result.container.textContent).toContain("Choose File");
269
+ });
270
+
271
+ it("keeps multiple files visible when replacing a controlled stored URL", async () => {
272
+ function Harness() {
273
+ const [value, setValue] = useState<unknown>("/files/old.pdf");
274
+ return <FileUploadField value={value} onChange={setValue} multiple />;
275
+ }
276
+ const result = renderWithProviders(<Harness />);
277
+ await act(async () => {
278
+ fireEvent.change(nativePicker(result.container)!, {
279
+ target: {
280
+ files: [
281
+ new File(["one"], "one.pdf", { type: "application/pdf" }),
282
+ new File(["two"], "two.pdf", { type: "application/pdf" }),
283
+ ],
284
+ },
285
+ });
286
+ });
287
+ expect(result.container.textContent).toContain("one.pdf");
288
+ expect(result.container.textContent).toContain("two.pdf");
289
+ });
@@ -172,3 +172,12 @@ export const InTable = {
172
172
  );
173
173
  },
174
174
  };
175
+
176
+ export const StoredImage = () => {
177
+ const [value, setValue] = useState<unknown>(
178
+ "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/10.png",
179
+ );
180
+ return (
181
+ <FileUploadField label="Sprite" isImage value={value} onChange={setValue} libraryFiles={[]} />
182
+ );
183
+ };
@@ -3,12 +3,20 @@ import type {
3
3
  FileUploadHandler,
4
4
  FormFieldProps,
5
5
  } from "@multiplatform.one/forms";
6
- import { Button, FileUpload } from "@multiplatform.one/forms";
6
+ import {
7
+ Button,
8
+ FileUpload,
9
+ Field,
10
+ getFieldError,
11
+ mergeFieldHandler,
12
+ useFormField,
13
+ useResolvedValidators,
14
+ } from "@multiplatform.one/forms";
7
15
  import { FolderOpenIcon } from "@phosphor-icons/react";
8
16
  import type { DeepKeys } from "@tanstack/form-core";
9
17
  import { useState } from "react";
10
18
  import { useTranslation } from "react-i18next";
11
- import { useProps } from "tamagui";
19
+ import { useMedia, useProps } from "tamagui";
12
20
  import { AttachLibrary } from "../../FileBrowser/AttachLibrary";
13
21
  import { useFrappeConfig } from "../../FrappeProvider";
14
22
  import type { FileBrowserFile } from "../../FileBrowser/types";
@@ -68,8 +76,51 @@ export type FileUploadFieldProps<
68
76
  export function FileUploadField<
69
77
  TData extends Record<string, unknown> = Record<string, unknown>,
70
78
  TName extends string = string,
79
+ >(props: FileUploadFieldProps<TData, TName>) {
80
+ const { resolvedForm, knobProps, id } = useFormField({ form: props.form, id: props.id });
81
+ const validators = useResolvedValidators(
82
+ props.required,
83
+ props.validators,
84
+ props.label,
85
+ props.name,
86
+ );
87
+ if (!resolvedForm || !props.name) {
88
+ return <FileUploadFieldControl {...props} size={props.size ?? knobProps.sizeToken} />;
89
+ }
90
+ return (
91
+ <Field
92
+ form={resolvedForm}
93
+ name={props.name as any}
94
+ defaultValue={props.defaultValue}
95
+ validators={validators}
96
+ mode={props.mode}
97
+ preserveValue={props.preserveValue}
98
+ >
99
+ {(field) => (
100
+ <FileUploadFieldControl
101
+ {...props}
102
+ id={id}
103
+ name={undefined}
104
+ size={props.size ?? knobProps.sizeToken}
105
+ value={field.state.value}
106
+ error={getFieldError(field, props.error)}
107
+ onChange={(next) => {
108
+ field.handleChange(next as any);
109
+ props.onChange?.(next);
110
+ }}
111
+ onBlur={mergeFieldHandler(field, "handleBlur", props.onBlur)}
112
+ />
113
+ )}
114
+ </Field>
115
+ );
116
+ }
117
+
118
+ function FileUploadFieldControl<
119
+ TData extends Record<string, unknown> = Record<string, unknown>,
120
+ TName extends string = string,
71
121
  >(props: FileUploadFieldProps<TData, TName>) {
72
122
  const { t } = useTranslation();
123
+ const media = useMedia();
73
124
  const {
74
125
  defaultValue,
75
126
  form,
@@ -112,11 +163,15 @@ export function FileUploadField<
112
163
  form={form}
113
164
  validators={validators}
114
165
  defaultValue={(stored ? undefined : resolvedDefaultValue) as any}
115
- existing={stored ? { src: resolveStoredFileUrl(stored, baseURL) } : undefined}
166
+ existing={
167
+ stored
168
+ ? { src: resolveStoredFileUrl(stored, baseURL), isImage: isImage || undefined }
169
+ : undefined
170
+ }
116
171
  onExistingRemove={
117
172
  stored && !disabled && !fieldProps.readOnly ? () => onChange?.("") : undefined
118
173
  }
119
- onValueChange={onChange as any}
174
+ onChange={onChange as any}
120
175
  label={fieldProps.label}
121
176
  labelProps={fieldProps.labelProps}
122
177
  error={fieldProps.error}
@@ -125,6 +180,7 @@ export function FileUploadField<
125
180
  skeleton={fieldProps.skeleton}
126
181
  required={fieldProps.required}
127
182
  id={fieldProps.id}
183
+ onBlur={fieldProps.onBlur}
128
184
  size={fieldProps.size}
129
185
  disabled={disabled}
130
186
  accept={resolvedAccept}
@@ -134,11 +190,16 @@ export function FileUploadField<
134
190
  actions={
135
191
  library ? (
136
192
  <Button
137
- size="$4"
193
+ size={fieldProps.size}
194
+ alignSelf="stretch"
195
+ flex={media.xs ? undefined : 1}
138
196
  outlined
139
197
  icon={FolderOpenIcon}
140
198
  disabled={disabled || fieldProps.readOnly}
141
- onPress={() => setLibraryOpen(true)}
199
+ onPress={(event) => {
200
+ event.stopPropagation();
201
+ setLibraryOpen(true);
202
+ }}
142
203
  >
143
204
  {t("From Library")}
144
205
  </Button>
@@ -1,4 +1,4 @@
1
- import { useRegisterPageHeading } from "@multiplatform.one/components";
1
+ import { useRegisterPageHeading, Text } from "@multiplatform.one/components";
2
2
  import { Skeleton } from "@multiplatform.one/forms";
3
3
  import { sizeRecipeEscape, useResolvedKnobs, sizeTokenStep } from "@multiplatform.one/theme";
4
4
  import type React from "react";
@@ -10,7 +10,7 @@ import {
10
10
  H3,
11
11
  H4,
12
12
  Separator,
13
- Text,
13
+ Text as TamaguiText,
14
14
  XStack,
15
15
  YStack,
16
16
  type YStackProps,
@@ -174,14 +174,14 @@ export function Heading({
174
174
  <YStack gap={descriptionGap} paddingBottom={showDescription ? "$1" : undefined}>
175
175
  {headingContent}
176
176
  {showDescription && (
177
- <Text
177
+ <TamaguiText
178
178
  fontSize={knobProps.sizeToken}
179
179
  {...knobProps.body}
180
180
  color="$color10"
181
181
  paddingInlineStart={canCollapse ? "$6" : undefined}
182
182
  >
183
183
  {resolvedDescription}
184
- </Text>
184
+ </TamaguiText>
185
185
  )}
186
186
  </YStack>
187
187
  {showSeparator && (
@@ -30,9 +30,10 @@ import {
30
30
  import { useEffect, useState, type ComponentType, type ReactNode } from "react";
31
31
  import { useTranslation } from "react-i18next";
32
32
  import type { FontSizeTokens, LabelProps, SizeTokens } from "tamagui";
33
- import { Paragraph, YStack } from "tamagui";
33
+ import { YStack } from "tamagui";
34
34
  import { sizeRecipeForToken, useResolvedKnobs, sizeTokenStep } from "@multiplatform.one/theme";
35
35
  import { EditorPreviewPanes, PreviewSurface } from "../shared/EditorPreviewPanes";
36
+ import { Paragraph } from "@multiplatform.one/components";
36
37
 
37
38
  // ---------------------------------------------------------------------------
38
39
  // Preview (lazy house Markdown renderer)
@@ -9,7 +9,8 @@ import type { FormFieldProps, FieldComponentProps } from "@multiplatform.one/for
9
9
  import { Stepper as FormsStepper, formCommonColors, useForm } from "@multiplatform.one/forms";
10
10
  import type { DeepKeys } from "@tanstack/form-core";
11
11
  import { useEffect, useRef } from "react";
12
- import { Text, XStack, YStack, useProps } from "tamagui";
12
+ import { XStack, YStack, useProps } from "tamagui";
13
+ import { Text } from "@multiplatform.one/components";
13
14
 
14
15
  export type NumericType = "Int" | "Float" | "Currency" | "Percent";
15
16
 
@@ -7,20 +7,21 @@
7
7
  * inside a 46-tall / radius-5 / #f7f7f7 frame; ours is a real Input.Button
8
8
  * tab stop. Frame stays measured mpo 44/9 on $color2 (chain step 1).
9
9
  *
10
- * Reveal uses Input.Button `glyphRing`: chip-dismiss class, 2px offset 0
11
- * on the glyph, not the 44px hit box (LC-71). Size comes from the
12
- * generated end-cap recipe, not a hardcoded square.
10
+ * The reveal is the shared PasswordReveal, the field's trailing action
11
+ * (LC-149): a chromeless eye inside the Box that rings its own glyph (LC-71)
12
+ * and squares off the generated end-cap recipe.
13
13
  */
14
- import { EyeIcon, EyeSlashIcon } from "@phosphor-icons/react";
15
14
  import type { FormFieldProps, FieldComponentProps } from "@multiplatform.one/forms";
16
15
  import {
17
16
  Field,
18
17
  FieldLayout,
19
18
  Input as FormsInput,
19
+ PasswordReveal,
20
20
  Skeleton,
21
21
  getFieldError,
22
22
  mergeFieldHandler,
23
23
  useFormField,
24
+ usePasswordReveal,
24
25
  useResolvedValidators,
25
26
  } from "@multiplatform.one/forms";
26
27
  import {
@@ -31,7 +32,7 @@ import {
31
32
  } from "@multiplatform.one/theme";
32
33
  import type { DeepKeys } from "@tanstack/form-core";
33
34
  import type { SizeTokens, TamaguiElement } from "tamagui";
34
- import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
35
+ import { useCallback, useRef, useState } from "react";
35
36
  import { useProps } from "tamagui";
36
37
 
37
38
  export type PasswordProps<
@@ -69,32 +70,6 @@ export type PasswordProps<
69
70
  size?: import("tamagui").SizeTokens;
70
71
  };
71
72
 
72
- const SHOW_PASSWORD = "Show password";
73
- const HIDE_PASSWORD = "Hide password";
74
-
75
- type Selection = { start: number; end: number };
76
-
77
- function nativeInput(node: unknown, id?: string): HTMLInputElement | null {
78
- const fromRef = node as { selectionStart?: number; setSelectionRange?: unknown } | null;
79
- if (fromRef && typeof fromRef.setSelectionRange === "function") {
80
- return fromRef as HTMLInputElement;
81
- }
82
- if (typeof document === "undefined" || !id) return null;
83
- return document.getElementById(id) as HTMLInputElement | null;
84
- }
85
-
86
- function readSelection(node: unknown, id?: string): Selection | null {
87
- const el = nativeInput(node, id);
88
- if (!el || typeof el.selectionStart !== "number") return null;
89
- return { start: el.selectionStart, end: el.selectionEnd ?? el.selectionStart };
90
- }
91
-
92
- function restoreSelection(node: unknown, sel: Selection | null, id?: string) {
93
- const el = nativeInput(node, id);
94
- el?.focus();
95
- if (sel) el?.setSelectionRange(sel.start, sel.end);
96
- }
97
-
98
73
  export function Password<
99
74
  TData extends Record<string, unknown> = Record<string, unknown>,
100
75
  TName extends string = string,
@@ -137,19 +112,8 @@ export function Password<
137
112
  const isControlled = value !== undefined;
138
113
  const [internalText, setInternalText] = useState(String(defaultValue ?? ""));
139
114
  const text = isControlled ? String(value ?? "") : internalText;
140
- const [revealed, setRevealed] = useState(false);
141
115
  const inputRef = useRef<TamaguiElement>(null);
142
- const pendingSelection = useRef<Selection | null>(null);
143
-
144
- useEffect(() => {
145
- if (!text) setRevealed(false);
146
- }, [text]);
147
-
148
- useLayoutEffect(() => {
149
- if (!pendingSelection.current) return;
150
- restoreSelection(inputRef.current, pendingSelection.current, id);
151
- pendingSelection.current = null;
152
- }, [id, revealed]);
116
+ const { revealed, toggle: toggleReveal } = usePasswordReveal({ inputRef, id, text });
153
117
 
154
118
  const handleChangeText = useCallback(
155
119
  (next: string) => {
@@ -160,11 +124,6 @@ export function Password<
160
124
  [disabled, isControlled, onChange],
161
125
  );
162
126
 
163
- const toggleReveal = useCallback(() => {
164
- pendingSelection.current = readSelection(inputRef.current, id);
165
- setRevealed((open) => !open);
166
- }, [id]);
167
-
168
127
  const boxContract = {
169
128
  size: resolvedSize,
170
129
  "data-size-token": String(resolvedSize),
@@ -193,7 +152,7 @@ export function Password<
193
152
  handleChange?: (text: string) => void,
194
153
  hasError?: boolean,
195
154
  ) => (
196
- <FormsInput.Box {...boxContract} disabled={disabled}>
155
+ <FormsInput.Box {...boxContract} disabled={disabled} paddingInlineEnd={0}>
197
156
  <FormsInput.Area
198
157
  ref={inputRef}
199
158
  {...(areaInputProps as object)}
@@ -215,28 +174,13 @@ export function Password<
215
174
  handleChangeText(next);
216
175
  }}
217
176
  />
218
- <FormsInput.Button
219
- glyphRing
220
- size={resolvedSize}
177
+ <PasswordReveal
178
+ revealed={revealed}
179
+ onToggle={toggleReveal}
180
+ controls={id}
221
181
  disabled={disabled}
222
- aria-label={revealed ? HIDE_PASSWORD : SHOW_PASSWORD}
223
- aria-pressed={revealed}
224
- aria-controls={id}
225
- hitSlop={belowFloor ? pressTargetHitSlop(visualHeight) : undefined}
226
- onPress={disabled || fieldProps.readOnly ? undefined : toggleReveal}
227
- onKeyDown={
228
- ((e: KeyboardEvent) => {
229
- if (disabled || fieldProps.readOnly) return;
230
- if (e.key === "Enter" || e.key === " ") {
231
- e.preventDefault();
232
- e.stopPropagation();
233
- toggleReveal();
234
- }
235
- }) as unknown as () => void
236
- }
237
- >
238
- <FormsInput.Icon aria-hidden>{revealed ? <EyeSlashIcon /> : <EyeIcon />}</FormsInput.Icon>
239
- </FormsInput.Button>
182
+ size={resolvedSize}
183
+ />
240
184
  </FormsInput.Box>
241
185
  );
242
186
 
@@ -18,7 +18,8 @@ import {
18
18
  sizeTokenStep,
19
19
  } from "@multiplatform.one/theme";
20
20
  import { useMemo } from "react";
21
- import { Text, XStack, YStack, type YStackProps } from "tamagui";
21
+ import { Text as TamaguiText, XStack, YStack, type YStackProps } from "tamagui";
22
+ import { Text } from "@multiplatform.one/components";
22
23
 
23
24
  export type ReadOnlyType =
24
25
  | "Data"
@@ -193,15 +194,15 @@ export function ReadOnly({
193
194
  return (
194
195
  <XStack alignItems="center">
195
196
  {renderColorSwatch()}
196
- <Text {...knobProps.body} color={valueColor} fontFamily="$mono">
197
+ <TamaguiText {...knobProps.body} color={valueColor} fontFamily="$mono">
197
198
  {displayValue}
198
- </Text>
199
+ </TamaguiText>
199
200
  </XStack>
200
201
  );
201
202
  }
202
203
  if (type === "Link" && (linkHref || onLinkPress) && !isEmpty) {
203
204
  return (
204
- <Text
205
+ <TamaguiText
205
206
  {...knobProps.body}
206
207
  color="$accentColor"
207
208
  textDecorationLine="underline"
@@ -210,7 +211,7 @@ export function ReadOnly({
210
211
  hoverStyle={{ opacity: 0.8 }}
211
212
  >
212
213
  {displayValue}
213
- </Text>
214
+ </TamaguiText>
214
215
  );
215
216
  }
216
217
  if (type === "Check") {
@@ -236,23 +237,23 @@ export function ReadOnly({
236
237
  </Text>
237
238
  ) : null}
238
239
  </XStack>
239
- <Text {...knobProps.body} color={valueColor}>
240
+ <TamaguiText {...knobProps.body} color={valueColor}>
240
241
  {displayValue}
241
- </Text>
242
+ </TamaguiText>
242
243
  </XStack>
243
244
  );
244
245
  }
245
246
  if (type === "LongText" || type === "Text" || type === "SmallText") {
246
247
  return (
247
- <Text {...knobProps.body} color={valueColor} whiteSpace="pre-wrap">
248
+ <TamaguiText {...knobProps.body} color={valueColor} whiteSpace="pre-wrap">
248
249
  {displayValue}
249
- </Text>
250
+ </TamaguiText>
250
251
  );
251
252
  }
252
253
  return (
253
- <Text {...knobProps.body} color={valueColor}>
254
+ <TamaguiText {...knobProps.body} color={valueColor}>
254
255
  {displayValue}
255
- </Text>
256
+ </TamaguiText>
256
257
  );
257
258
  };
258
259
 
@@ -2,10 +2,11 @@ import { action } from "@multiplatform.one/storybook";
2
2
  import { Button } from "@multiplatform.one/forms";
3
3
  import { DataTable, type DataTableColumn } from "@multiplatform.one/table";
4
4
  import { useState } from "react";
5
- import { Text, YStack } from "tamagui";
5
+ import { YStack } from "tamagui";
6
6
  import { fieldtypeToTableField } from "../../fieldRegistry";
7
7
  import { FrappeStoryProvider } from "../../frappeStoryTarget";
8
8
  import { TextEditor, type TextEditorProps } from ".";
9
+ import { Text } from "@multiplatform.one/components";
9
10
 
10
11
  export default {
11
12
  title: "FrappeUI/TextEditor",
@@ -145,10 +145,11 @@ describe("Frappe remainder wrappers — design-law 100%", () => {
145
145
  expect(source).not.toContain("<Tint");
146
146
  });
147
147
 
148
- it("Desk pages use body fragment, not sizeToken as fontSize; form back is house Button", () => {
148
+ it("Desk pages use body fragment, not sizeToken as fontSize; form back is the house PageBackButton", () => {
149
149
  const form = read("desk/DeskFormPage.tsx");
150
150
  const list = read("desk/DeskListPage.tsx");
151
- expect(form).toMatch(/Button.*from "@multiplatform.one\/forms"/);
151
+ expect(form).toMatch(/page=\{\{\s*back:/);
152
+ expect(read("FrappeForm.tsx")).toMatch(/<PageBackButton\b/);
152
153
  expect(form).not.toMatch(/fontSize=\{knobProps\.sizeToken\}/);
153
154
  expect(list).not.toMatch(/fontSize=\{knobProps\.sizeToken\}/);
154
155
  expect(form).toContain("formCommonColors.muted");
@@ -190,6 +190,16 @@ function button(container: HTMLElement, label: string): HTMLElement {
190
190
  return found as HTMLElement;
191
191
  }
192
192
 
193
+ // LC-145: a record page keeps its secondary actions (Delete) in the top bar's "…" menu.
194
+ async function menuEntry(label: string): Promise<HTMLElement | null> {
195
+ const trigger = screen.queryByTestId("frappe-form-menu");
196
+ if (!trigger) return null;
197
+ fireEvent.click(trigger);
198
+ const entry = await screen.findByRole("menuitem", { name: label }).catch(() => null);
199
+ fireEvent.keyDown(document.activeElement ?? document.body, { key: "Escape" });
200
+ return entry;
201
+ }
202
+
193
203
  function input(container: HTMLElement, label: string): HTMLInputElement {
194
204
  const field = screen.getByLabelText(label, { exact: false });
195
205
  if (field instanceof HTMLInputElement) return field;
@@ -373,8 +383,9 @@ describe("FrappeForm with a route", () => {
373
383
  it("deletes through delete_doc and returns to the list", async () => {
374
384
  const { container, pushed } = renderAt("/moves/d4e5f6", "form");
375
385
  await waitFor(() => expect(input(container, "Move Name").value).toBe("vine-whip"));
376
- await waitFor(() => expect(button(container, "Delete")).toBeTruthy());
377
- fireEvent.click(button(container, "Delete"));
386
+ await screen.findByTestId("frappe-form-menu");
387
+ fireEvent.click(screen.getByTestId("frappe-form-menu"));
388
+ fireEvent.click(await screen.findByRole("menuitem", { name: "Delete" }));
378
389
 
379
390
  await waitFor(() => expect(pushed).toEqual(["/moves"]));
380
391
  expect(confirmDelete).toHaveBeenCalledTimes(1);
@@ -391,7 +402,8 @@ describe("FrappeForm with a route", () => {
391
402
  expect(input(container, "Power").value).toBe("40");
392
403
  expect(screen.queryByText(`Failed to load ${doctype} form`)).toBeNull();
393
404
  expect(() => button(container, "Edit")).toThrow();
394
- expect(() => button(container, "Delete")).toThrow();
405
+ expect(await menuEntry("Edit")).toBeNull();
406
+ expect(await menuEntry("Delete")).toBeNull();
395
407
  });
396
408
 
397
409
  it("draws no field for the collection's own $ keys on a reader's record", async () => {
@@ -415,7 +427,8 @@ describe("FrappeForm with a route", () => {
415
427
  true,
416
428
  );
417
429
  expect(() => button(container, "Edit")).toThrow();
418
- expect(() => button(container, "Delete")).toThrow();
430
+ expect(await menuEntry("Edit")).toBeNull();
431
+ expect(await menuEntry("Delete")).toBeNull();
419
432
  });
420
433
 
421
434
  it("renders a reader's edit page read-only with no Save", async () => {
@@ -444,13 +457,14 @@ describe("FrappeForm with a route", () => {
444
457
  const own = renderAt("/moves/a1b2c3", "form");
445
458
  await waitFor(() => expect(input(own.container, "Move Name").value).toBe("tackle"));
446
459
  await waitFor(() => expect(button(own.container, "Edit")).toBeTruthy());
447
- expect(button(own.container, "Delete")).toBeTruthy();
460
+ expect(await menuEntry("Delete")).not.toBeNull();
448
461
  cleanup();
449
462
 
450
463
  const others = renderAt("/moves/d4e5f6", "form");
451
464
  await waitFor(() => expect(input(others.container, "Move Name").value).toBe("vine-whip"));
452
465
  await sessionSettled();
453
466
  expect(() => button(others.container, "Edit")).toThrow();
454
- expect(() => button(others.container, "Delete")).toThrow();
467
+ expect(await menuEntry("Edit")).toBeNull();
468
+ expect(await menuEntry("Delete")).toBeNull();
455
469
  });
456
470
  });
package/src/index.ts CHANGED
@@ -53,7 +53,12 @@ export {
53
53
  // ---------------------------------------------------------------------------
54
54
 
55
55
  export { FrappeTable, filterValueToFrappeTuple, type FrappeTableProps } from "./FrappeTable";
56
- export { FrappeForm, type FrappeConnectionRows, type FrappeFormProps } from "./FrappeForm";
56
+ export {
57
+ FrappeForm,
58
+ type FrappeConnectionRows,
59
+ type FrappeFormPage,
60
+ type FrappeFormProps,
61
+ } from "./FrappeForm";
57
62
  export {
58
63
  useFrappeForm,
59
64
  type FrappeFormConnectionConfig,
@@ -120,6 +125,7 @@ export {
120
125
  } from "./list/ListToolsSheet";
121
126
  export { ShufflePanel, type ShufflePanelProps } from "./list/ShufflePanel";
122
127
  export { FacetBadge, type FacetBadgeProps } from "./list/FacetBadge";
128
+ export { facetBadgeSteps } from "./list/facetBadgeSteps";
123
129
  export { ImageWell, type ImageWellProps } from "./list/ImageWell";
124
130
  export { RecordCard, type RecordCardBadge, type RecordCardProps } from "./list/RecordCard";
125
131
  export { recordTitle, type FrappeRecordLook, type FrappeRecordSlide } from "./list/recordLook";
@@ -78,14 +78,14 @@ export function InsightsLeaders<TDoc>({
78
78
  accessorKey: "title",
79
79
  header: t("Record"),
80
80
  cell: ({ row }) => {
81
- const tints = record.tints(row.original.doc);
81
+ const tint = record.tint(row.original.doc);
82
82
  const code = record.code(row.original.doc);
83
83
  return (
84
84
  <XStack gap="$2" alignItems="center" minWidth={0}>
85
85
  <ImageWell
86
86
  sources={record.images(row.original.doc)}
87
87
  alt={row.original.title}
88
- tints={tints}
88
+ tint={tint}
89
89
  inset={0.04}
90
90
  width={nestedPx}
91
91
  data-nested-px={nestedPx}