@multiplatform.one/frappe-ui 7.65.2 → 7.67.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 (290) hide show
  1. package/dist/cjs/FormSidebar.cjs +95 -194
  2. package/dist/cjs/FormSidebar.native.js +107 -217
  3. package/dist/cjs/FormSidebar.native.js.map +1 -1
  4. package/dist/cjs/FrappeConnectionIndicator.cjs +5 -24
  5. package/dist/cjs/FrappeConnectionIndicator.native.js +5 -24
  6. package/dist/cjs/FrappeConnectionIndicator.native.js.map +1 -1
  7. package/dist/cjs/FrappeImageView.cjs +46 -65
  8. package/dist/cjs/FrappeImageView.native.js +46 -68
  9. package/dist/cjs/FrappeImageView.native.js.map +1 -1
  10. package/dist/cjs/FrappeListView.cjs +24 -67
  11. package/dist/cjs/FrappeListView.native.js +25 -71
  12. package/dist/cjs/FrappeListView.native.js.map +1 -1
  13. package/dist/cjs/FrappeRecordView.cjs +4 -0
  14. package/dist/cjs/FrappeRecordView.native.js +4 -0
  15. package/dist/cjs/FrappeRecordView.native.js.map +1 -1
  16. package/dist/cjs/FrappeReportBuilder.cjs +21 -18
  17. package/dist/cjs/FrappeReportBuilder.native.js +21 -18
  18. package/dist/cjs/FrappeReportBuilder.native.js.map +1 -1
  19. package/dist/cjs/SyncErrorNotice.cjs +8 -36
  20. package/dist/cjs/SyncErrorNotice.native.js +8 -36
  21. package/dist/cjs/SyncErrorNotice.native.js.map +1 -1
  22. package/dist/cjs/desk/DeskFormSidebar.cjs +1 -6
  23. package/dist/cjs/desk/DeskFormSidebar.native.js +1 -6
  24. package/dist/cjs/desk/DeskFormSidebar.native.js.map +1 -1
  25. package/dist/cjs/desk/DeskShell.native.js.map +1 -1
  26. package/dist/cjs/fields/Date/catalog.native.js +0 -1
  27. package/dist/cjs/fields/Date/catalog.native.js.map +1 -1
  28. package/dist/cjs/fields/Date/index.native.js +4 -363
  29. package/dist/cjs/fields/Date/index.native.js.map +1 -1
  30. package/dist/cjs/insights/InsightsFacetTags.cjs +16 -92
  31. package/dist/cjs/insights/InsightsFacetTags.native.js +15 -97
  32. package/dist/cjs/insights/InsightsFacetTags.native.js.map +1 -1
  33. package/dist/cjs/list/FacetBadge.cjs +4 -28
  34. package/dist/cjs/list/FacetBadge.native.js +4 -28
  35. package/dist/cjs/list/FacetBadge.native.js.map +1 -1
  36. package/dist/cjs/list/FacetRail.cjs +37 -128
  37. package/dist/cjs/list/FacetRail.native.js +32 -130
  38. package/dist/cjs/list/FacetRail.native.js.map +1 -1
  39. package/dist/cjs/list/RecordCard.cjs +107 -127
  40. package/dist/cjs/list/RecordCard.native.js +86 -96
  41. package/dist/cjs/list/RecordCard.native.js.map +1 -1
  42. package/dist/cjs/list/ShufflePanel.cjs +57 -36
  43. package/dist/cjs/list/ShufflePanel.native.js +64 -39
  44. package/dist/cjs/list/ShufflePanel.native.js.map +1 -1
  45. package/dist/cjs/record/RecordHero.cjs +7 -85
  46. package/dist/cjs/record/RecordHero.native.js +7 -85
  47. package/dist/cjs/record/RecordHero.native.js.map +1 -1
  48. package/dist/cjs/record/RecordPage.cjs +1 -0
  49. package/dist/cjs/record/RecordPage.native.js +1 -0
  50. package/dist/cjs/record/RecordPage.native.js.map +1 -1
  51. package/dist/cjs/report/ReportViewToolbar.cjs +70 -39
  52. package/dist/cjs/report/ReportViewToolbar.native.js +73 -40
  53. package/dist/cjs/report/ReportViewToolbar.native.js.map +1 -1
  54. package/dist/esm/FormSidebar.mjs +98 -197
  55. package/dist/esm/FormSidebar.mjs.map +1 -1
  56. package/dist/esm/FormSidebar.native.js +110 -220
  57. package/dist/esm/FormSidebar.native.js.map +1 -1
  58. package/dist/esm/FrappeConnectionIndicator.mjs +7 -26
  59. package/dist/esm/FrappeConnectionIndicator.mjs.map +1 -1
  60. package/dist/esm/FrappeConnectionIndicator.native.js +7 -26
  61. package/dist/esm/FrappeConnectionIndicator.native.js.map +1 -1
  62. package/dist/esm/FrappeImageView.mjs +48 -67
  63. package/dist/esm/FrappeImageView.mjs.map +1 -1
  64. package/dist/esm/FrappeImageView.native.js +48 -70
  65. package/dist/esm/FrappeImageView.native.js.map +1 -1
  66. package/dist/esm/FrappeListView.mjs +25 -68
  67. package/dist/esm/FrappeListView.mjs.map +1 -1
  68. package/dist/esm/FrappeListView.native.js +26 -72
  69. package/dist/esm/FrappeListView.native.js.map +1 -1
  70. package/dist/esm/FrappeRecordView.mjs +4 -0
  71. package/dist/esm/FrappeRecordView.mjs.map +1 -1
  72. package/dist/esm/FrappeRecordView.native.js +4 -0
  73. package/dist/esm/FrappeRecordView.native.js.map +1 -1
  74. package/dist/esm/FrappeReportBuilder.mjs +21 -18
  75. package/dist/esm/FrappeReportBuilder.mjs.map +1 -1
  76. package/dist/esm/FrappeReportBuilder.native.js +21 -18
  77. package/dist/esm/FrappeReportBuilder.native.js.map +1 -1
  78. package/dist/esm/SyncErrorNotice.mjs +10 -38
  79. package/dist/esm/SyncErrorNotice.mjs.map +1 -1
  80. package/dist/esm/SyncErrorNotice.native.js +10 -38
  81. package/dist/esm/SyncErrorNotice.native.js.map +1 -1
  82. package/dist/esm/desk/DeskFormSidebar.mjs +1 -6
  83. package/dist/esm/desk/DeskFormSidebar.mjs.map +1 -1
  84. package/dist/esm/desk/DeskFormSidebar.native.js +1 -6
  85. package/dist/esm/desk/DeskFormSidebar.native.js.map +1 -1
  86. package/dist/esm/desk/DeskShell.mjs.map +1 -1
  87. package/dist/esm/desk/DeskShell.native.js.map +1 -1
  88. package/dist/esm/fields/Date/catalog.mjs.map +1 -1
  89. package/dist/esm/fields/Date/catalog.native.js +0 -1
  90. package/dist/esm/fields/Date/catalog.native.js.map +1 -1
  91. package/dist/esm/fields/Date/index.native.js +2 -361
  92. package/dist/esm/fields/Date/index.native.js.map +1 -1
  93. package/dist/esm/insights/InsightsFacetTags.mjs +18 -94
  94. package/dist/esm/insights/InsightsFacetTags.mjs.map +1 -1
  95. package/dist/esm/insights/InsightsFacetTags.native.js +17 -99
  96. package/dist/esm/insights/InsightsFacetTags.native.js.map +1 -1
  97. package/dist/esm/list/FacetBadge.mjs +5 -29
  98. package/dist/esm/list/FacetBadge.mjs.map +1 -1
  99. package/dist/esm/list/FacetBadge.native.js +5 -29
  100. package/dist/esm/list/FacetBadge.native.js.map +1 -1
  101. package/dist/esm/list/FacetRail.mjs +40 -131
  102. package/dist/esm/list/FacetRail.mjs.map +1 -1
  103. package/dist/esm/list/FacetRail.native.js +35 -133
  104. package/dist/esm/list/FacetRail.native.js.map +1 -1
  105. package/dist/esm/list/RecordCard.mjs +107 -127
  106. package/dist/esm/list/RecordCard.mjs.map +1 -1
  107. package/dist/esm/list/RecordCard.native.js +86 -96
  108. package/dist/esm/list/RecordCard.native.js.map +1 -1
  109. package/dist/esm/list/ShufflePanel.mjs +59 -38
  110. package/dist/esm/list/ShufflePanel.mjs.map +1 -1
  111. package/dist/esm/list/ShufflePanel.native.js +66 -41
  112. package/dist/esm/list/ShufflePanel.native.js.map +1 -1
  113. package/dist/esm/record/RecordHero.mjs +8 -86
  114. package/dist/esm/record/RecordHero.mjs.map +1 -1
  115. package/dist/esm/record/RecordHero.native.js +8 -86
  116. package/dist/esm/record/RecordHero.native.js.map +1 -1
  117. package/dist/esm/record/RecordPage.mjs +2 -1
  118. package/dist/esm/record/RecordPage.mjs.map +1 -1
  119. package/dist/esm/record/RecordPage.native.js +2 -1
  120. package/dist/esm/record/RecordPage.native.js.map +1 -1
  121. package/dist/esm/report/ReportViewToolbar.mjs +72 -41
  122. package/dist/esm/report/ReportViewToolbar.mjs.map +1 -1
  123. package/dist/esm/report/ReportViewToolbar.native.js +75 -42
  124. package/dist/esm/report/ReportViewToolbar.native.js.map +1 -1
  125. package/dist/jsx/FormSidebar.mjs +98 -197
  126. package/dist/jsx/FormSidebar.mjs.map +1 -1
  127. package/dist/jsx/FormSidebar.native.js +107 -217
  128. package/dist/jsx/FormSidebar.native.js.map +1 -1
  129. package/dist/jsx/FrappeConnectionIndicator.mjs +7 -26
  130. package/dist/jsx/FrappeConnectionIndicator.mjs.map +1 -1
  131. package/dist/jsx/FrappeConnectionIndicator.native.js +5 -24
  132. package/dist/jsx/FrappeConnectionIndicator.native.js.map +1 -1
  133. package/dist/jsx/FrappeImageView.mjs +48 -67
  134. package/dist/jsx/FrappeImageView.mjs.map +1 -1
  135. package/dist/jsx/FrappeImageView.native.js +46 -68
  136. package/dist/jsx/FrappeImageView.native.js.map +1 -1
  137. package/dist/jsx/FrappeListView.mjs +25 -68
  138. package/dist/jsx/FrappeListView.mjs.map +1 -1
  139. package/dist/jsx/FrappeListView.native.js +25 -71
  140. package/dist/jsx/FrappeListView.native.js.map +1 -1
  141. package/dist/jsx/FrappeRecordView.mjs +4 -0
  142. package/dist/jsx/FrappeRecordView.mjs.map +1 -1
  143. package/dist/jsx/FrappeRecordView.native.js +4 -0
  144. package/dist/jsx/FrappeRecordView.native.js.map +1 -1
  145. package/dist/jsx/FrappeReportBuilder.mjs +21 -18
  146. package/dist/jsx/FrappeReportBuilder.mjs.map +1 -1
  147. package/dist/jsx/FrappeReportBuilder.native.js +21 -18
  148. package/dist/jsx/FrappeReportBuilder.native.js.map +1 -1
  149. package/dist/jsx/SyncErrorNotice.mjs +10 -38
  150. package/dist/jsx/SyncErrorNotice.mjs.map +1 -1
  151. package/dist/jsx/SyncErrorNotice.native.js +8 -36
  152. package/dist/jsx/SyncErrorNotice.native.js.map +1 -1
  153. package/dist/jsx/desk/DeskFormSidebar.mjs +1 -6
  154. package/dist/jsx/desk/DeskFormSidebar.mjs.map +1 -1
  155. package/dist/jsx/desk/DeskFormSidebar.native.js +1 -6
  156. package/dist/jsx/desk/DeskFormSidebar.native.js.map +1 -1
  157. package/dist/jsx/desk/DeskShell.mjs.map +1 -1
  158. package/dist/jsx/desk/DeskShell.native.js.map +1 -1
  159. package/dist/jsx/fields/Date/catalog.mjs.map +1 -1
  160. package/dist/jsx/fields/Date/catalog.native.js +0 -1
  161. package/dist/jsx/fields/Date/catalog.native.js.map +1 -1
  162. package/dist/jsx/fields/Date/index.native.js +4 -363
  163. package/dist/jsx/fields/Date/index.native.js.map +1 -1
  164. package/dist/jsx/insights/InsightsFacetTags.mjs +18 -94
  165. package/dist/jsx/insights/InsightsFacetTags.mjs.map +1 -1
  166. package/dist/jsx/insights/InsightsFacetTags.native.js +15 -97
  167. package/dist/jsx/insights/InsightsFacetTags.native.js.map +1 -1
  168. package/dist/jsx/list/FacetBadge.mjs +5 -29
  169. package/dist/jsx/list/FacetBadge.mjs.map +1 -1
  170. package/dist/jsx/list/FacetBadge.native.js +4 -28
  171. package/dist/jsx/list/FacetBadge.native.js.map +1 -1
  172. package/dist/jsx/list/FacetRail.mjs +40 -131
  173. package/dist/jsx/list/FacetRail.mjs.map +1 -1
  174. package/dist/jsx/list/FacetRail.native.js +32 -130
  175. package/dist/jsx/list/FacetRail.native.js.map +1 -1
  176. package/dist/jsx/list/RecordCard.mjs +107 -127
  177. package/dist/jsx/list/RecordCard.mjs.map +1 -1
  178. package/dist/jsx/list/RecordCard.native.js +86 -96
  179. package/dist/jsx/list/RecordCard.native.js.map +1 -1
  180. package/dist/jsx/list/ShufflePanel.mjs +59 -38
  181. package/dist/jsx/list/ShufflePanel.mjs.map +1 -1
  182. package/dist/jsx/list/ShufflePanel.native.js +64 -39
  183. package/dist/jsx/list/ShufflePanel.native.js.map +1 -1
  184. package/dist/jsx/record/RecordHero.mjs +8 -86
  185. package/dist/jsx/record/RecordHero.mjs.map +1 -1
  186. package/dist/jsx/record/RecordHero.native.js +7 -85
  187. package/dist/jsx/record/RecordHero.native.js.map +1 -1
  188. package/dist/jsx/record/RecordPage.mjs +2 -1
  189. package/dist/jsx/record/RecordPage.mjs.map +1 -1
  190. package/dist/jsx/record/RecordPage.native.js +1 -0
  191. package/dist/jsx/record/RecordPage.native.js.map +1 -1
  192. package/dist/jsx/report/ReportViewToolbar.mjs +72 -41
  193. package/dist/jsx/report/ReportViewToolbar.mjs.map +1 -1
  194. package/dist/jsx/report/ReportViewToolbar.native.js +73 -40
  195. package/dist/jsx/report/ReportViewToolbar.native.js.map +1 -1
  196. package/package.json +16 -16
  197. package/src/FormSidebar.stories.tsx +1 -1
  198. package/src/FormSidebar.tsx +128 -253
  199. package/src/FrappeConnectionIndicator.tsx +6 -28
  200. package/src/FrappeImageView.stories.tsx +1 -6
  201. package/src/FrappeImageView.tsx +52 -66
  202. package/src/FrappeListView.controls.spec.tsx +3 -2
  203. package/src/FrappeListView.tsx +23 -55
  204. package/src/FrappeRecordView.tsx +16 -3
  205. package/src/FrappeReportBuilder.tsx +21 -19
  206. package/src/SyncErrorNotice.spec.tsx +7 -12
  207. package/src/SyncErrorNotice.stories.tsx +2 -4
  208. package/src/SyncErrorNotice.tsx +13 -41
  209. package/src/desk/DeskFormSidebar.tsx +1 -2
  210. package/src/desk/DeskShell.tsx +1 -0
  211. package/src/fields/Date/catalog.tsx +6 -7
  212. package/src/fields/Date/nativeIsCatalog.spec.tsx +26 -0
  213. package/src/fields/Date/typeParity.spec.tsx +20 -98
  214. package/src/frappeRemainder.designLaw.spec.ts +8 -7
  215. package/src/insights/InsightsFacetTags.tsx +21 -102
  216. package/src/list/FacetBadge.tsx +7 -29
  217. package/src/list/FacetRail.spec.tsx +11 -10
  218. package/src/list/FacetRail.tsx +49 -161
  219. package/src/list/RecordCard.native.tsx +63 -64
  220. package/src/list/RecordCard.spec.tsx +8 -5
  221. package/src/list/RecordCard.tsx +116 -129
  222. package/src/list/ShufflePanel.tsx +65 -38
  223. package/src/record/RecordHero.tsx +11 -51
  224. package/src/record/RecordPage.tsx +6 -1
  225. package/src/report/ReportViewToolbar.spec.tsx +45 -3
  226. package/src/report/ReportViewToolbar.tsx +109 -56
  227. package/types/FormSidebar.d.ts +13 -3
  228. package/types/FormSidebar.d.ts.map +1 -1
  229. package/types/FrappeConnectionIndicator.d.ts +2 -1
  230. package/types/FrappeConnectionIndicator.d.ts.map +1 -1
  231. package/types/FrappeImageView.d.ts.map +1 -1
  232. package/types/FrappeListView.d.ts.map +1 -1
  233. package/types/FrappeRecordView.d.ts.map +1 -1
  234. package/types/FrappeReportBuilder.d.ts.map +1 -1
  235. package/types/SyncErrorNotice.d.ts +4 -6
  236. package/types/SyncErrorNotice.d.ts.map +1 -1
  237. package/types/desk/DeskFormSidebar.d.ts.map +1 -1
  238. package/types/desk/DeskShell.d.ts.map +1 -1
  239. package/types/fields/Date/catalog.d.ts +5 -4
  240. package/types/fields/Date/catalog.d.ts.map +1 -1
  241. package/types/insights/InsightsFacetTags.d.ts +4 -1
  242. package/types/insights/InsightsFacetTags.d.ts.map +1 -1
  243. package/types/list/FacetBadge.d.ts +3 -3
  244. package/types/list/FacetBadge.d.ts.map +1 -1
  245. package/types/list/FacetRail.d.ts +7 -5
  246. package/types/list/FacetRail.d.ts.map +1 -1
  247. package/types/list/RecordCard.d.ts +6 -6
  248. package/types/list/RecordCard.d.ts.map +1 -1
  249. package/types/list/RecordCard.native.d.ts +4 -3
  250. package/types/list/RecordCard.native.d.ts.map +1 -1
  251. package/types/list/ShufflePanel.d.ts +8 -3
  252. package/types/list/ShufflePanel.d.ts.map +1 -1
  253. package/types/record/RecordHero.d.ts +5 -1
  254. package/types/record/RecordHero.d.ts.map +1 -1
  255. package/types/record/RecordPage.d.ts.map +1 -1
  256. package/types/report/ReportViewToolbar.d.ts.map +1 -1
  257. package/dist/cjs/fields/Date/dateTimePickerLoader.cjs +0 -38
  258. package/dist/cjs/fields/Date/dateTimePickerLoader.native.js +0 -42
  259. package/dist/cjs/fields/Date/dateTimePickerLoader.native.js.map +0 -1
  260. package/dist/cjs/fields/Date/sheetKeyboard.cjs +0 -54
  261. package/dist/cjs/fields/Date/sheetKeyboard.native.js +0 -57
  262. package/dist/cjs/fields/Date/sheetKeyboard.native.js.map +0 -1
  263. package/dist/esm/fields/Date/dateTimePickerLoader.mjs +0 -13
  264. package/dist/esm/fields/Date/dateTimePickerLoader.mjs.map +0 -1
  265. package/dist/esm/fields/Date/dateTimePickerLoader.native.js +0 -14
  266. package/dist/esm/fields/Date/dateTimePickerLoader.native.js.map +0 -1
  267. package/dist/esm/fields/Date/sheetKeyboard.mjs +0 -21
  268. package/dist/esm/fields/Date/sheetKeyboard.mjs.map +0 -1
  269. package/dist/esm/fields/Date/sheetKeyboard.native.js +0 -21
  270. package/dist/esm/fields/Date/sheetKeyboard.native.js.map +0 -1
  271. package/dist/jsx/fields/Date/dateTimePickerLoader.mjs +0 -13
  272. package/dist/jsx/fields/Date/dateTimePickerLoader.mjs.map +0 -1
  273. package/dist/jsx/fields/Date/dateTimePickerLoader.native.js +0 -42
  274. package/dist/jsx/fields/Date/dateTimePickerLoader.native.js.map +0 -1
  275. package/dist/jsx/fields/Date/sheetKeyboard.mjs +0 -21
  276. package/dist/jsx/fields/Date/sheetKeyboard.mjs.map +0 -1
  277. package/dist/jsx/fields/Date/sheetKeyboard.native.js +0 -57
  278. package/dist/jsx/fields/Date/sheetKeyboard.native.js.map +0 -1
  279. package/src/fields/Date/dateTimePickerLoader.ts +0 -25
  280. package/src/fields/Date/index.native.tsx +0 -445
  281. package/src/fields/Date/nativeFallback.spec.tsx +0 -21
  282. package/src/fields/Date/nativePresentation.spec.tsx +0 -278
  283. package/src/fields/Date/sheetKeyboard.spec.ts +0 -60
  284. package/src/fields/Date/sheetKeyboard.ts +0 -45
  285. package/types/fields/Date/dateTimePickerLoader.d.ts +0 -11
  286. package/types/fields/Date/dateTimePickerLoader.d.ts.map +0 -1
  287. package/types/fields/Date/index.native.d.ts +0 -14
  288. package/types/fields/Date/index.native.d.ts.map +0 -1
  289. package/types/fields/Date/sheetKeyboard.d.ts +0 -20
  290. package/types/fields/Date/sheetKeyboard.d.ts.map +0 -1
@@ -17,23 +17,19 @@
17
17
  * DeskParity story.
18
18
  */
19
19
 
20
- import { Avatar, componentColors, getInitials } from "@multiplatform.one/components";
20
+ import {
21
+ Avatar,
22
+ Image,
23
+ Sidebar,
24
+ Text,
25
+ type SidebarSection as MpoSidebarSection,
26
+ } from "@multiplatform.one/components";
21
27
  import { Button } from "@multiplatform.one/forms";
22
28
  import { useResolvedKnobs } from "@multiplatform.one/theme";
23
29
  import { ImageSquareIcon, PlusIcon, UploadIcon, UserIcon, XIcon } from "@phosphor-icons/react";
24
30
  import React from "react";
25
31
  import { useTranslation } from "react-i18next";
26
- import {
27
- Image,
28
- ListItem,
29
- Separator,
30
- Text,
31
- XStack,
32
- YStack,
33
- isWeb,
34
- useTheme,
35
- type YStackProps,
36
- } from "tamagui";
32
+ import { XStack, YStack, useTheme } from "tamagui";
37
33
 
38
34
  // ---------------------------------------------------------------------------
39
35
  // Types
@@ -73,7 +69,7 @@ export interface SidebarMetadataEntry {
73
69
  icon?: React.ReactNode;
74
70
  }
75
71
 
76
- export interface FormSidebarProps extends Omit<YStackProps, "children"> {
72
+ export interface FormSidebarProps {
77
73
  /** Optional image URL to display at the top */
78
74
  imageUrl?: string;
79
75
  /** Accessible label for the image block (defaults to "Document image") */
@@ -87,30 +83,23 @@ export interface FormSidebarProps extends Omit<YStackProps, "children"> {
87
83
  sections?: SidebarSection[];
88
84
  /** Metadata lines shown at the bottom (e.g. created by / modified by) */
89
85
  metadata?: SidebarMetadataEntry[];
90
- }
91
-
92
- // ---------------------------------------------------------------------------
93
- // Helpers
94
- // ---------------------------------------------------------------------------
95
-
96
- // Initials come from the catalog Avatar helper (census #21) — the local copy
97
- // this file carried could drift from the one Timeline/Avatar consume.
98
-
99
- // R12 tint family (decorative, not semantic): orange / blue / purple / pink.
100
- const avatarColors = ["$orange4", "$blue4", "$purple4", "$pink4"];
101
- function avatarColor(index: number): string {
102
- return avatarColors[index % avatarColors.length];
103
- }
104
-
105
- const textColors = ["$orange11", "$blue11", "$purple11", "$pink11"];
106
- function textColor(index: number): string {
107
- return textColors[index % textColors.length];
86
+ /** Rail width (default 280); `"100%"` fills a supporting pane. */
87
+ width?: number | string;
88
+ /** The end-edge hairline (default on); off inside a pane whose seam divides it. */
89
+ divider?: boolean;
90
+ "data-testid"?: string;
108
91
  }
109
92
 
110
93
  // ---------------------------------------------------------------------------
111
94
  // Component
112
95
  // ---------------------------------------------------------------------------
113
96
 
97
+ /**
98
+ * The document rail on the MPO `Sidebar` in its complementary role (LC-175):
99
+ * the image block, the people sections, custom sections and the Info
100
+ * metadata are Sidebar sections, so the rail takes MPO's surface, caption,
101
+ * row and divider look.
102
+ */
114
103
  export function FormSidebar({
115
104
  imageUrl,
116
105
  imageLabel,
@@ -118,7 +107,9 @@ export function FormSidebar({
118
107
  onImageRemove,
119
108
  sections = [],
120
109
  metadata = [],
121
- ...stackProps
110
+ width = 280,
111
+ divider = true,
112
+ "data-testid": testID,
122
113
  }: FormSidebarProps) {
123
114
  const { t } = useTranslation();
124
115
  const { knobProps } = useResolvedKnobs();
@@ -133,237 +124,121 @@ export function FormSidebar({
133
124
  const [erroredUrl, setErroredUrl] = React.useState<string | undefined>();
134
125
  const imageBroken = Boolean(imageUrl) && erroredUrl === imageUrl;
135
126
 
136
- // Scale typography, avatars, icons and the image block with the size token
137
127
  const sz = knobProps.sizeToken;
138
- const avatarSize = sz === "$3" ? "$1" : sz === "$5" ? "$3" : "$2";
139
128
  const imageSize = sz === "$3" ? 160 : sz === "$5" ? 240 : 200;
140
129
 
141
130
  const resolvedImageLabel = imageLabel ?? t("Document image");
142
- const showImageBlock = Boolean(imageUrl) || Boolean(onImageAdd);
143
131
 
144
- return (
145
- <YStack
146
- // Radius-only fragment: the full borderRadius fragment carries
147
- // borderWidth and drew an unintended box border around the whole rail
148
- // (rtl-audit U6); the panel border is the logical end rail below.
149
- {...knobProps.containerRadius}
150
- {...knobProps.panelPadding}
151
- {...knobProps.gapLg}
152
- width={280}
153
- // Logical end edge so the rail mirrors in RTL; tamagui borderEndWidth
154
- // does not compile to working CSS on web (SB-M-1215)
155
- {...(isWeb
156
- ? {
157
- borderInlineEndWidth: 1,
158
- borderInlineEndStyle: "solid" as any,
159
- borderInlineEndColor: "$borderColor",
160
- }
161
- : { borderEndWidth: 1, borderEndColor: "$borderColor" })}
162
- {...stackProps}
163
- >
164
- {/* -- Image section -- */}
165
- {showImageBlock && (
166
- <>
167
- <YStack alignItems="center" gap="$2">
168
- {imageUrl && !imageBroken ? (
169
- <Image
170
- {...knobProps.containerRadius}
171
- // src (string), not source: the RN source object leaks onto
172
- // the DOM <img> as source="[object Object]" on web
173
- src={imageUrl}
174
- alt={resolvedImageLabel}
175
- width={imageSize}
176
- height={imageSize}
177
- objectFit="contain"
178
- onError={() => setErroredUrl(imageUrl)}
179
- />
180
- ) : imageUrl && imageBroken ? (
181
- <YStack
182
- {...knobProps.containerRadius}
183
- width={imageSize}
184
- height={imageSize}
185
- alignItems="center"
186
- justifyContent="center"
187
- gap="$2"
188
- borderWidth={1}
189
- borderStyle="dashed"
190
- borderColor="$borderColor"
191
- aria-label={resolvedImageLabel}
192
- >
193
- <ImageSquareIcon size={iconSize * 2} color={placeholderIconColor} />
194
- <Text color={knobProps.textAccentColor} {...knobProps.label}>
195
- {t("Image failed to load")}
196
- </Text>
197
- </YStack>
198
- ) : (
199
- <YStack
200
- role="button"
201
- tabIndex={0}
132
+ const imageBlock =
133
+ imageUrl || onImageAdd ? (
134
+ <YStack alignItems="center" gap="$2">
135
+ {imageUrl ? (
136
+ <Image
137
+ {...knobProps.containerRadius}
138
+ src={imageBroken ? undefined : imageUrl}
139
+ alt={resolvedImageLabel}
140
+ width={imageSize}
141
+ height={imageSize}
142
+ objectFit="contain"
143
+ onError={() => setErroredUrl(imageUrl)}
144
+ />
145
+ ) : (
146
+ <Button
147
+ chromeless
148
+ icon={ImageSquareIcon}
149
+ aria-label={t("Add image")}
150
+ onPress={onImageAdd}
151
+ />
152
+ )}
153
+ {imageBroken ? (
154
+ <Text color={knobProps.textAccentColor} {...knobProps.label}>
155
+ {t("Image failed to load")}
156
+ </Text>
157
+ ) : null}
158
+ {imageUrl && (onImageAdd || onImageRemove) ? (
159
+ <XStack gap="$2">
160
+ {onImageAdd ? (
161
+ <Button
162
+ size="$2"
163
+ chromeless
164
+ icon={UploadIcon}
165
+ aria-label={t("Replace image")}
202
166
  onPress={onImageAdd}
203
- aria-label={t("Add image")}
204
- width={imageSize}
205
- height={imageSize}
206
- alignItems="center"
207
- justifyContent="center"
208
- cursor="pointer"
209
- gap="$2"
210
- {...knobProps.containerRadius}
211
- borderWidth={1}
212
- borderStyle="dashed"
213
- borderColor="$borderColor"
214
- >
215
- <ImageSquareIcon size={iconSize * 2} color={placeholderIconColor} />
216
- <Text color={knobProps.textAccentColor} {...knobProps.label}>
217
- {t("Add image")}
218
- </Text>
219
- </YStack>
220
- )}
221
-
222
- {imageUrl && (onImageAdd || onImageRemove) && (
223
- <XStack gap="$2">
224
- {onImageAdd && (
225
- <Button
226
- size="$2"
227
- chromeless
228
- icon={UploadIcon}
229
- aria-label={t("Replace image")}
230
- onPress={onImageAdd}
231
- >
232
- {t("Replace")}
233
- </Button>
234
- )}
235
- {onImageRemove && (
167
+ />
168
+ ) : null}
169
+ {onImageRemove ? (
170
+ <Button
171
+ size="$2"
172
+ chromeless
173
+ icon={XIcon}
174
+ aria-label={t("Remove image")}
175
+ onPress={onImageRemove}
176
+ />
177
+ ) : null}
178
+ </XStack>
179
+ ) : null}
180
+ </YStack>
181
+ ) : null;
182
+
183
+ const railSections: MpoSidebarSection[] = [
184
+ ...(imageBlock ? [{ items: [], content: imageBlock }] : []),
185
+ ...sections.map(
186
+ (section): MpoSidebarSection => ({
187
+ title: section.title,
188
+ icon: section.icon,
189
+ action: section.onAdd ? (
190
+ <Button
191
+ size="$2"
192
+ chromeless
193
+ icon={PlusIcon}
194
+ aria-label={t("Add {{section}}", { section: section.title })}
195
+ onPress={section.onAdd}
196
+ />
197
+ ) : undefined,
198
+ ...(section.render
199
+ ? { items: [], content: section.render() }
200
+ : {
201
+ items: (section.items ?? []).map((item) => ({
202
+ label: item.label,
203
+ value: item.id,
204
+ subTitle: item.sublabel,
205
+ icon: <Avatar name={item.label} nested />,
206
+ trailing: section.onRemoveItem ? (
236
207
  <Button
237
208
  size="$2"
238
209
  chromeless
239
210
  icon={XIcon}
240
- aria-label={t("Remove image")}
241
- onPress={onImageRemove}
242
- >
243
- {t("Remove")}
244
- </Button>
245
- )}
246
- </XStack>
247
- )}
248
- </YStack>
249
- <Separator />
250
- </>
251
- )}
252
-
253
- {/* -- Dynamic sections -- */}
254
- {sections.map((section, sectionIdx) => (
255
- <React.Fragment key={section.key}>
256
- <YStack {...knobProps.gap}>
257
- <XStack alignItems="center" justifyContent="space-between">
258
- <XStack alignItems="center" gap="$2">
259
- {section.icon}
260
- <Text color={knobProps.textAccentColor} {...knobProps.label} fontWeight="400">
261
- {section.title}
262
- </Text>
263
- </XStack>
264
- {section.onAdd && (
265
- <Button
266
- size="$2"
267
- circular
268
- chromeless
269
- icon={PlusIcon}
270
- aria-label={t("Add {{section}}", {
271
- section: section.title,
272
- })}
273
- onPress={section.onAdd}
274
- />
275
- )}
276
- </XStack>
277
-
278
- {section.render ? (
279
- section.render()
280
- ) : section.items && section.items.length > 0 ? (
281
- <YStack {...knobProps.gap}>
282
- {section.items.map((item) => (
283
- <XStack key={item.id} gap="$2" alignItems="center">
284
- <Avatar circular size={avatarSize} backgroundColor={avatarColor(sectionIdx)}>
285
- <Avatar.Fallback backgroundColor={avatarColor(sectionIdx)}>
286
- <Text color={textColor(sectionIdx)} {...knobProps.label} fontWeight="700">
287
- {getInitials(item.label)}
288
- </Text>
289
- </Avatar.Fallback>
290
- </Avatar>
291
- <YStack flex={1}>
292
- <Text
293
- color={componentColors.text.primary}
294
- numberOfLines={1}
295
- {...knobProps.label}
296
- >
297
- {item.label}
298
- </Text>
299
- {item.sublabel && (
300
- <Text color={knobProps.textAccentColor} {...knobProps.label}>
301
- {item.sublabel}
302
- </Text>
303
- )}
304
- </YStack>
305
- {section.onRemoveItem && (
306
- <Button
307
- size="$2"
308
- circular
309
- chromeless
310
- icon={XIcon}
311
- aria-label={t("Remove {{name}}", { name: item.label })}
312
- onPress={() => section.onRemoveItem?.(item)}
313
- />
314
- )}
315
- </XStack>
316
- ))}
317
- </YStack>
318
- ) : (
319
- <Text color={knobProps.textAccentColor} {...knobProps.label}>
320
- {section.emptyText ?? t("None")}
321
- </Text>
322
- )}
323
- </YStack>
324
-
325
- {sectionIdx < sections.length - 1 && <Separator />}
326
- </React.Fragment>
327
- ))}
328
-
329
- {/* -- Separator before metadata -- */}
330
- {metadata.length > 0 && sections.length > 0 && <Separator />}
331
-
332
- {/* -- Info / Metadata -- */}
333
- {metadata.length > 0 && (
334
- <YStack gap="$1">
335
- <Text
336
- color={knobProps.textAccentColor}
337
- paddingHorizontal="$2"
338
- {...knobProps.label}
339
- fontWeight="400"
340
- >
341
- {t("Info")}
342
- </Text>
343
- {metadata.map((entry) => (
344
- <ListItem
345
- key={entry.label}
346
- size={knobProps.sizeToken}
347
- icon={
348
- (entry.icon ?? (
349
- <UserIcon
350
- size={iconSize}
351
- color={placeholderIconColor}
352
- style={{ flexShrink: 0 }}
211
+ aria-label={t("Remove {{name}}", { name: item.label })}
212
+ onPress={() => section.onRemoveItem?.(item)}
353
213
  />
354
- )) as any
355
- }
356
- title={
357
- <Text color={knobProps.textAccentColor} {...knobProps.label}>
358
- {entry.label}
359
- </Text>
360
- }
361
- backgroundColor="transparent"
362
- paddingVertical="$1"
363
- />
364
- ))}
365
- </YStack>
366
- )}
367
- </YStack>
214
+ ) : undefined,
215
+ })),
216
+ emptyText: section.emptyText ?? t("None"),
217
+ }),
218
+ }),
219
+ ),
220
+ ...(metadata.length > 0
221
+ ? [
222
+ {
223
+ title: t("Info"),
224
+ items: metadata.map((entry) => ({
225
+ label: entry.label,
226
+ value: entry.label,
227
+ icon: entry.icon ?? <UserIcon size={iconSize} color={placeholderIconColor} />,
228
+ })),
229
+ },
230
+ ]
231
+ : []),
232
+ ];
233
+
234
+ return (
235
+ <Sidebar
236
+ role="complementary"
237
+ aria-label={t("Document details")}
238
+ width={width}
239
+ divider={divider}
240
+ sections={railSections}
241
+ testID={testID}
242
+ />
368
243
  );
369
244
  }
@@ -3,11 +3,10 @@ import {
3
3
  type FrappeConnectionState,
4
4
  type UseFrappeCollectionConfig,
5
5
  } from "@multiplatform.one/frappe";
6
- import { radiusClassProps, useResolvedKnobs } from "@multiplatform.one/theme";
6
+ import { StatusDot } from "@multiplatform.one/components";
7
7
  import React from "react";
8
8
  import { useTranslation } from "react-i18next";
9
- import { XStack, YStack } from "tamagui";
10
- import { Text } from "@multiplatform.one/components";
9
+ import { XStack } from "tamagui";
11
10
 
12
11
  export interface FrappeConnectionIndicatorProps {
13
12
  /** The same connection config the sibling collection hooks use. */
@@ -21,7 +20,8 @@ export interface FrappeConnectionIndicatorProps {
21
20
  }
22
21
 
23
22
  /**
24
- * Compact "Reconnecting" status pill for live Frappe views.
23
+ * Compact "Reconnecting" status for live Frappe views: the MPO `StatusDot`
24
+ * in the warning tone (LC-169, LC-175).
25
25
  *
26
26
  * Shows only while the realtime socket is down after having been up
27
27
  * (`everConnected && !connected`) — during that window updates arrive via
@@ -31,34 +31,12 @@ export interface FrappeConnectionIndicatorProps {
31
31
  */
32
32
  export function FrappeConnectionIndicator({ config, connection }: FrappeConnectionIndicatorProps) {
33
33
  const { t } = useTranslation();
34
- const { knobProps } = useResolvedKnobs();
35
34
  const hooked = useFrappeConnection(config);
36
35
  const { connected, everConnected } = connection ?? hooked;
37
36
  if (connected || !everConnected) return null;
38
37
  return (
39
- <XStack
40
- role="status"
41
- aria-live="polite"
42
- alignSelf="flex-start"
43
- alignItems="center"
44
- gap="$2"
45
- paddingHorizontal="$3"
46
- paddingVertical="$1.5"
47
- {...knobProps.borderRadius}
48
- backgroundColor="$yellow3"
49
- borderColor="$yellow7"
50
- borderWidth={1}
51
- >
52
- <YStack
53
- {...radiusClassProps("R-PILL", "connection status dot")}
54
- width={8}
55
- height={8}
56
- borderRadius={4}
57
- backgroundColor="$yellow10"
58
- />
59
- <Text fontSize="$2" color="$yellow11">
60
- {t("Reconnecting…")}
61
- </Text>
38
+ <XStack role="status" aria-live="polite" alignSelf="flex-start">
39
+ <StatusDot tone="warning">{t("Reconnecting…")}</StatusDot>
62
40
  </XStack>
63
41
  );
64
42
  }
@@ -20,7 +20,7 @@ export default {
20
20
  },
21
21
  },
22
22
  argTypes: {
23
- columns: { control: { type: "number", min: 1, max: 6 } },
23
+ columns: { control: { type: "number", min: 1, max: 8 } },
24
24
  searchText: { control: "text" },
25
25
  },
26
26
  };
@@ -104,7 +104,6 @@ export const Main = (args: Record<string, unknown>) => (
104
104
  />
105
105
  </FrappeProvider>
106
106
  );
107
- Main.args = { columns: 3 };
108
107
  Main.storyName = "Main";
109
108
 
110
109
  /** Zero documents: the grid's empty message must show. */
@@ -139,7 +138,6 @@ export const BrokenImages = () => (
139
138
  doctype="QaGalleryBroken"
140
139
  imageField="picture"
141
140
  titleField="title"
142
- columns={3}
143
141
  onItemClick={action("onItemClick")}
144
142
  />
145
143
  </FrappeProvider>
@@ -177,7 +175,6 @@ export const LongMetadata = () => (
177
175
  imageField="picture"
178
176
  titleField="title"
179
177
  subtitleField="subtitle"
180
- columns={3}
181
178
  />
182
179
  </FrappeProvider>
183
180
  );
@@ -199,7 +196,6 @@ export const Live = (args: { doctype?: string; imageField?: string; titleField?:
199
196
  doctype={args.doctype || "Contact"}
200
197
  imageField={args.imageField || "image"}
201
198
  titleField={args.titleField || "first_name"}
202
- columns={3}
203
199
  onItemClick={action("onItemClick")}
204
200
  />
205
201
  </FrappeStoryProvider>
@@ -232,7 +228,6 @@ export const Filtered = () => (
232
228
  imageField="picture"
233
229
  titleField="title"
234
230
  searchText="Item 1"
235
- columns={3}
236
231
  />
237
232
  </FrappeProvider>
238
233
  );