@multiplatform.one/frappe-ui 7.66.0 → 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
@@ -1,10 +1,10 @@
1
1
  import { useDocTypeMeta } from "@multiplatform.one/frappe";
2
- import { AsyncSkeleton, Card, ImageGrid } from "@multiplatform.one/components";
2
+ import { AsyncSkeleton, Image, ImageGrid } from "@multiplatform.one/components";
3
3
  import { ImageSquareIcon } from "@phosphor-icons/react";
4
4
  import React, { useMemo } from "react";
5
5
  import { useTranslation } from "react-i18next";
6
6
  import { Spinner, formCommonColors } from "@multiplatform.one/forms";
7
- import { Image, Text, YStack, useTheme, type YStackProps } from "tamagui";
7
+ import { Text, YStack, useTheme, type YStackProps } from "tamagui";
8
8
  import { isServer } from "@multiplatform.one/platform";
9
9
  import { useResolvedKnobs } from "@multiplatform.one/theme";
10
10
  import { useFrappeConfig } from "./FrappeProvider";
@@ -105,6 +105,7 @@ export function FrappeImageView<TDoc extends { name: string; doctype: string }>(
105
105
  if (ssrItems && ssrItems.length > 0 && imageField && baseURL) {
106
106
  return (
107
107
  <ImageGrid
108
+ variant="photos"
108
109
  items={ssrItems}
109
110
  columns={columns}
110
111
  onItemClick={onItemClick}
@@ -112,14 +113,13 @@ export function FrappeImageView<TDoc extends { name: string; doctype: string }>(
112
113
  testID={`${doctype.toLowerCase()}-list`}
113
114
  {...(stackProps as any)}
114
115
  renderItem={(doc) => (
115
- <FrappeImageCard
116
+ <FrappePhoto
116
117
  doc={doc as Record<string, unknown>}
117
118
  doctype={doctype}
118
119
  imageField={imageField}
119
120
  titleField={titleField}
120
121
  subtitleField={subtitleField}
121
122
  baseURL={baseURL}
122
- onItemClick={onItemClick as ((doc: Partial<unknown>) => void) | undefined}
123
123
  knobProps={knobProps}
124
124
  />
125
125
  )}
@@ -283,6 +283,7 @@ function FrappeImageViewClient<TDoc extends { name: string; doctype: string }>({
283
283
  <>
284
284
  <FrappeReconnectIndicator config={appConfig} />
285
285
  <ImageGrid
286
+ variant="photos"
286
287
  items={items}
287
288
  columns={columns}
288
289
  onItemClick={onItemClick}
@@ -290,14 +291,13 @@ function FrappeImageViewClient<TDoc extends { name: string; doctype: string }>({
290
291
  testID={`${doctype.toLowerCase()}-list`}
291
292
  {...(stackProps as any)}
292
293
  renderItem={(doc) => (
293
- <FrappeImageCard
294
+ <FrappePhoto
294
295
  doc={doc as Record<string, unknown>}
295
296
  doctype={doctype}
296
297
  imageField={effectiveImageField}
297
298
  titleField={effectiveTitleField}
298
299
  subtitleField={subtitleField}
299
300
  baseURL={finalBaseURL}
300
- onItemClick={onItemClick as ((doc: Partial<unknown>) => void) | undefined}
301
301
  knobProps={knobProps}
302
302
  />
303
303
  )}
@@ -307,18 +307,18 @@ function FrappeImageViewClient<TDoc extends { name: string; doctype: string }>({
307
307
  }
308
308
 
309
309
  /**
310
- * Card renderer shared between SSR and client paths so they produce
311
- * matching markup (avoiding hydration mismatches) and the look stays
312
- * defined in one place.
310
+ * One photo in the Photos grid (LC-264), shared between SSR and client paths so they
311
+ * produce matching markup: the image fills a square edge to edge with no card
312
+ * chrome. The title is its accessible name; it shows as a caption only when
313
+ * there is no picture to look at (no URL, or the image failed to load).
313
314
  */
314
- function FrappeImageCard({
315
+ function FrappePhoto({
315
316
  doc,
316
317
  doctype,
317
318
  imageField,
318
319
  titleField,
319
320
  subtitleField,
320
321
  baseURL,
321
- onItemClick,
322
322
  knobProps,
323
323
  }: {
324
324
  doc: Record<string, unknown>;
@@ -327,74 +327,60 @@ function FrappeImageCard({
327
327
  titleField: string;
328
328
  subtitleField?: string;
329
329
  baseURL: string;
330
- onItemClick?: (doc: Partial<unknown>) => void;
331
330
  knobProps: ReturnType<typeof useResolvedKnobs>["knobProps"];
332
331
  }) {
333
- const theme = useTheme();
334
332
  const imageUrl = getImageUrl(doc[imageField], baseURL);
335
- const title = doc[titleField] || doc.name;
333
+ const title = String(doc[titleField] || doc.name);
336
334
  const subtitle = subtitleField ? doc[subtitleField] : undefined;
337
- const iconSize = Number(knobProps.controlIcon?.width) || 16;
338
-
339
- return (
340
- // Catalog Card owns cardSurface (no cherry-picked radius). Inner media
341
- // radius stays UNCLAMPED (R11). No status pip on the tile.
342
- <Card
343
- testID={`${doctype.toLowerCase()}-card-${doc.name}`}
344
- cursor="pointer"
345
- hoverStyle={{ scale: 1.02 }}
346
- pressStyle={{ scale: 0.98 }}
347
- onPress={(e) => {
348
- // Bind click directly to `doc` so the correct item is used even
349
- // if the ImageGrid wrapper's closure captured a stale/recycled item.
350
- (e as unknown as { stopPropagation?: () => void })?.stopPropagation?.();
351
- onItemClick?.(doc as Partial<unknown>);
352
- }}
335
+ const slug = doctype.toLowerCase();
336
+ const caption = (
337
+ <YStack
338
+ width="100%"
339
+ aspectRatio={1}
340
+ alignItems="center"
341
+ justifyContent="center"
342
+ backgroundColor="$color3"
343
+ padding="$2"
344
+ gap="$1"
353
345
  >
346
+ <Text
347
+ testID={`${slug}-name-${doc.name}`}
348
+ {...knobProps.label}
349
+ color={knobProps.textAccentColor}
350
+ textAlign="center"
351
+ numberOfLines={2}
352
+ >
353
+ {title}
354
+ </Text>
355
+ {subtitle != null ? (
356
+ <Text
357
+ testID={`${slug}-subtitle-${doc.name}`}
358
+ {...knobProps.label}
359
+ color={formCommonColors.muted}
360
+ textAlign="center"
361
+ numberOfLines={1}
362
+ >
363
+ {String(subtitle)}
364
+ </Text>
365
+ ) : null}
366
+ </YStack>
367
+ );
368
+ return (
369
+ <YStack testID={`${slug}-card-${doc.name}`} aria-label={title} width="100%">
354
370
  {imageUrl ? (
355
371
  <Image
356
372
  src={imageUrl}
357
- alt={String(title)}
373
+ alt={title}
358
374
  width="100%"
359
375
  aspectRatio={1}
360
376
  objectFit="cover"
361
- backgroundColor="$color2"
362
- {...knobProps.borderRadius}
377
+ borderRadius={0}
378
+ backgroundColor="$color3"
379
+ fallback={caption}
363
380
  />
364
381
  ) : (
365
- <YStack
366
- width="100%"
367
- aspectRatio={1}
368
- backgroundColor="$color2"
369
- {...knobProps.borderRadius}
370
- alignItems="center"
371
- justifyContent="center"
372
- >
373
- <ImageSquareIcon size={iconSize * 2} color={theme.color11?.val ?? "currentColor"} />
374
- </YStack>
382
+ caption
375
383
  )}
376
- <Card.Footer>
377
- <YStack {...knobProps.gap}>
378
- <Text
379
- testID={`${doctype.toLowerCase()}-name-${doc.name}`}
380
- {...knobProps.heading}
381
- fontWeight="400"
382
- numberOfLines={1}
383
- >
384
- {String(title)}
385
- </Text>
386
- {subtitle != null ? (
387
- <Text
388
- testID={`${doctype.toLowerCase()}-subtitle-${doc.name}`}
389
- color={formCommonColors.muted}
390
- {...knobProps.label}
391
- numberOfLines={1}
392
- >
393
- {String(subtitle)}
394
- </Text>
395
- ) : null}
396
- </YStack>
397
- </Card.Footer>
398
- </Card>
384
+ </YStack>
399
385
  );
400
386
  }
@@ -165,7 +165,8 @@ describe("FrappeListView controls write their state back", () => {
165
165
  renderList({}, {}, navigationAt({}, setParams));
166
166
  await waitFor(() => expect(shownCards().length).toBe(6), { timeout: 5000 });
167
167
  const chip = await screen.findByTestId("pokemon-type-fire");
168
- expect(getComputedStyle(chip).height).toBe("32px");
168
+ expect(chip.hasAttribute("data-chip")).toBe(true);
169
+ expect(chip.getAttribute("role")).toBe("radio");
169
170
  fireEvent.click(chip);
170
171
  await expectCards(["charmander"]);
171
172
  expect(setParams).toHaveBeenCalledWith({ type: "fire" });
@@ -228,7 +229,7 @@ describe("FrappeListView routed page with its toolbar off", () => {
228
229
  fireEvent.click(screen.getByTestId("insights"));
229
230
  expect(navigate).toHaveBeenCalledWith("/pokemon/insights");
230
231
  expect(screen.getByText("Skip to results")).toBeTruthy();
231
- fireEvent.click(await screen.findByText("Add Pokemon"));
232
+ fireEvent.click(await screen.findByRole("button", { name: "Add Pokemon" }));
232
233
  expect(navigate).toHaveBeenCalledWith("/pokemon/new");
233
234
  });
234
235
  });
@@ -22,6 +22,7 @@ import {
22
22
  EmptyState,
23
23
  ListPageLayout,
24
24
  ListRow,
25
+ PageBarButton,
25
26
  PageHeader,
26
27
  SkipLink,
27
28
  Tooltip,
@@ -922,7 +923,7 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
922
923
  : [];
923
924
  const headerActions =
924
925
  headerOwnsViews || (headerOwnsTools && onAdd) || headerAccessory || actions?.length ? (
925
- <XStack {...knobProps.gap} alignItems="center" flexWrap="wrap" flexShrink={1} minWidth={0}>
926
+ <XStack alignItems="center">
926
927
  {headerOwnsViews ? (
927
928
  <Tooltip content={t("Switch views")}>
928
929
  <XStack {...secondaryHidden}>
@@ -938,69 +939,35 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
938
939
  {actions?.map((action) => {
939
940
  const Icon = action.icon;
940
941
  const onPress = pressFor(action);
941
- if (sidebarFilters) {
942
- return (
943
- <XStack key={action.label} {...secondaryHidden}>
944
- <Tooltip content={action.hint ?? action.label}>
945
- <Button
946
- outlined
942
+ return (
943
+ <XStack key={action.label} {...secondaryHidden}>
944
+ <Tooltip content={action.hint ?? action.label}>
945
+ {Icon ? (
946
+ <PageBarButton
947
+ icon={Icon as never}
948
+ aria-label={action.label}
947
949
  onPress={onPress}
948
- icon={Icon ? <Icon size={16} /> : undefined}
949
950
  testID={action.testID}
950
- >
951
+ />
952
+ ) : (
953
+ <Button chromeless accentInk onPress={onPress} testID={action.testID}>
951
954
  {action.label}
952
955
  </Button>
953
- </Tooltip>
954
- </XStack>
955
- );
956
- }
957
- return (
958
- <React.Fragment key={action.label}>
959
- <Tooltip content={action.hint ?? action.label}>
960
- {/* responsive-parity: the circular icon twin below carries this action on phones */}
961
- <Button
962
- outlined
963
- onPress={onPress}
964
- icon={Icon ? <Icon size={16} /> : undefined}
965
- $max-sm={{ display: "none" }}
966
- testID={action.testID}
967
- >
968
- {action.label}
969
- </Button>
956
+ )}
970
957
  </Tooltip>
971
- <Button
972
- outlined
973
- circular
974
- onPress={onPress}
975
- icon={Icon ? <Icon size={16} /> : undefined}
976
- display="none"
977
- aria-label={action.label}
978
- $max-sm={{ display: "flex" }}
979
- />
980
- </React.Fragment>
958
+ </XStack>
981
959
  );
982
960
  })}
983
961
  {headerOwnsTools && onAdd ? (
984
- <>
985
- {/* responsive-parity: the short "Add" twin below carries this action on phones */}
986
- <Button
987
- theme="accent"
988
- onPress={onAdd}
989
- icon={<PlusIcon size={16} />}
990
- $max-sm={{ display: "none" }}
991
- >
992
- {t("Add {{doctype}}", { doctype })}
993
- </Button>
994
- <Button
995
- theme="accent"
962
+ <Tooltip content={t("Add {{doctype}}", { doctype })}>
963
+ <PageBarButton
964
+ icon={PlusIcon}
965
+ aria-label={t("Add {{doctype}}", { doctype })}
996
966
  onPress={onAdd}
997
- icon={<PlusIcon size={16} />}
998
- display="none"
999
- $max-sm={{ display: "flex" }}
1000
- >
1001
- {t("Add")}
1002
- </Button>
1003
- </>
967
+ edge="end"
968
+ testID={`${slug}-add`}
969
+ />
970
+ </Tooltip>
1004
971
  ) : null}
1005
972
  </XStack>
1006
973
  ) : undefined;
@@ -1013,6 +980,7 @@ function FrappeListViewBody<TDoc extends { name: string; doctype: string }>({
1013
980
  rows={shuffleRows}
1014
981
  itemLabel={shuffle.label}
1015
982
  renderPick={(doc) => drawCard(doc, 0)}
983
+ grid={gridProps}
1016
984
  onGo={(doc) => {
1017
985
  setShuffleOpen(false);
1018
986
  onItemClick?.(doc);
@@ -1,6 +1,7 @@
1
1
  import { EmptyState, Screen } from "@multiplatform.one/components";
2
2
  import { Button } from "@multiplatform.one/forms";
3
3
  import type { Order } from "@multiplatform.one/frappe";
4
+ import { DETAIL_READABLE_WIDTH } from "@multiplatform.one/theme";
4
5
  import { useMemo } from "react";
5
6
  import { useTranslation } from "react-i18next";
6
7
  import type { FrappeConnection } from "./frappeConnections";
@@ -141,12 +142,18 @@ export function FrappeRecordView<TDoc extends { name: string; doctype: string }>
141
142
  showSidebar={false}
142
143
  showTimeline={false}
143
144
  loadingFallback={
144
- <Screen noPadding gap={0} topBar={backButton}>
145
+ <Screen noPadding gap={0} maxWidth={DETAIL_READABLE_WIDTH} topBar={backButton}>
145
146
  <RecordSkeleton testID={`${slug}-detail-skeleton`} />
146
147
  </Screen>
147
148
  }
148
149
  notFoundFallback={
149
- <Screen scroll={false} noPadding gap={0} topBar={backButton}>
150
+ <Screen
151
+ scroll={false}
152
+ noPadding
153
+ gap={0}
154
+ maxWidth={DETAIL_READABLE_WIDTH}
155
+ topBar={backButton}
156
+ >
150
157
  <EmptyState
151
158
  title={t("{{doctype}} not found", { doctype })}
152
159
  description={t("No {{doctype}} is named {{name}}.", { doctype, name })}
@@ -159,7 +166,13 @@ export function FrappeRecordView<TDoc extends { name: string; doctype: string }>
159
166
  </Screen>
160
167
  }
161
168
  errorFallback={
162
- <Screen scroll={false} noPadding gap={0} topBar={backButton}>
169
+ <Screen
170
+ scroll={false}
171
+ noPadding
172
+ gap={0}
173
+ maxWidth={DETAIL_READABLE_WIDTH}
174
+ topBar={backButton}
175
+ >
163
176
  <EmptyState
164
177
  intent="error"
165
178
  title={t("Couldn't load this {{doctype}}", { doctype })}
@@ -870,25 +870,27 @@ function FrappeReportBuilderBody({
870
870
 
871
871
  const content = (
872
872
  <YStack flex={1} minWidth={0} minHeight={0} {...stackProps}>
873
- <ReportViewToolbar
874
- columns={allColumns}
875
- selected={selectedFieldnames}
876
- onToggleColumn={handleToggleColumn}
877
- onResetColumns={handleResetColumns}
878
- groupBy={groupBy}
879
- onGroupByChange={setGroupBy}
880
- baseFilters={baseFilters}
881
- onClearBaseFilters={baseFilters.length > 0 ? handleClearBaseFilters : undefined}
882
- columnFilterCount={columnTuples.length}
883
- onClearColumnFilters={handleClearColumnFilters}
884
- savedReports={savedReports.reports}
885
- currentReport={currentReport}
886
- onSelectReport={handleSelectReport}
887
- onSaveReport={readsWithoutMeta ? undefined : handleSaveReport}
888
- onDeleteReport={readsWithoutMeta ? undefined : handleDeleteReport}
889
- onExport={exportEnabled ? handleExport : undefined}
890
- exportDisabled={total === 0}
891
- />
873
+ <YStack paddingHorizontal="$3">
874
+ <ReportViewToolbar
875
+ columns={allColumns}
876
+ selected={selectedFieldnames}
877
+ onToggleColumn={handleToggleColumn}
878
+ onResetColumns={handleResetColumns}
879
+ groupBy={groupBy}
880
+ onGroupByChange={setGroupBy}
881
+ baseFilters={baseFilters}
882
+ onClearBaseFilters={baseFilters.length > 0 ? handleClearBaseFilters : undefined}
883
+ columnFilterCount={columnTuples.length}
884
+ onClearColumnFilters={handleClearColumnFilters}
885
+ savedReports={savedReports.reports}
886
+ currentReport={currentReport}
887
+ onSelectReport={handleSelectReport}
888
+ onSaveReport={readsWithoutMeta ? undefined : handleSaveReport}
889
+ onDeleteReport={readsWithoutMeta ? undefined : handleDeleteReport}
890
+ onExport={exportEnabled ? handleExport : undefined}
891
+ exportDisabled={total === 0}
892
+ />
893
+ </YStack>
892
894
  <ScrollView
893
895
  flex={1}
894
896
  minWidth={0}
@@ -1,5 +1,5 @@
1
1
  // SyncErrorNotice contract: the visible half of the rejected-write pattern —
2
- // role="alert" message, Retry re-issues, Dismiss clears, and each affordance
2
+ // role="alert" message, Retry re-issues, the close button clears, and each affordance
3
3
  // only renders when its handler is provided.
4
4
  import { renderWithProviders } from "@multiplatform.one/test-utils";
5
5
  import { cleanup, fireEvent, screen } from "@testing-library/react";
@@ -33,7 +33,9 @@ describe("SyncErrorNotice", () => {
33
33
  );
34
34
  fireEvent.click(await screen.findByText("Retry"));
35
35
  expect(onRetry).toHaveBeenCalledTimes(1);
36
- fireEvent.click(await screen.findByText("Dismiss"));
36
+ const close = await screen.findByRole("button", { name: "Dismiss" });
37
+ expect(close.textContent?.trim()).toBe("");
38
+ fireEvent.click(close);
37
39
  expect(onDismiss).toHaveBeenCalledTimes(1);
38
40
  });
39
41
 
@@ -41,20 +43,13 @@ describe("SyncErrorNotice", () => {
41
43
  renderWithProviders(<SyncErrorNotice message="No actions" />);
42
44
  await screen.findByRole("alert");
43
45
  expect(screen.queryByText("Retry")).toBeNull();
44
- expect(screen.queryByText("Dismiss")).toBeNull();
46
+ expect(screen.queryByRole("button", { name: "Dismiss" })).toBeNull();
45
47
  });
46
48
 
47
- it("honors custom labels", async () => {
49
+ it("honors a custom Retry label", async () => {
48
50
  renderWithProviders(
49
- <SyncErrorNotice
50
- message="Custom"
51
- onRetry={() => {}}
52
- onDismiss={() => {}}
53
- retryLabel="Try again"
54
- dismissLabel="Ignore"
55
- />,
51
+ <SyncErrorNotice message="Custom" onRetry={() => {}} retryLabel="Try again" />,
56
52
  );
57
53
  expect(await screen.findByText("Try again")).toBeTruthy();
58
- expect(await screen.findByText("Ignore")).toBeTruthy();
59
54
  });
60
55
  });
@@ -10,14 +10,13 @@ export default {
10
10
  docs: {
11
11
  description: {
12
12
  component:
13
- "Rejected-write notice: role=alert message with Retry (same mutation id) and Dismiss. Each affordance only renders when its handler is provided.",
13
+ "Rejected-write notice: role=alert message with Retry (same mutation id) and a close button. Each affordance only renders when its handler is provided.",
14
14
  },
15
15
  },
16
16
  },
17
17
  argTypes: {
18
18
  message: { control: "text" },
19
19
  retryLabel: { control: "text" },
20
- dismissLabel: { control: "text" },
21
20
  },
22
21
  };
23
22
 
@@ -40,11 +39,10 @@ export const AllStates = () => (
40
39
  <SyncErrorNotice message="Write rejected — retry only" onRetry={action("onRetry")} />
41
40
  <SyncErrorNotice message="No actions" />
42
41
  <SyncErrorNotice
43
- message="Custom labels"
42
+ message="Custom Retry label"
44
43
  onRetry={action("onRetry")}
45
44
  onDismiss={action("onDismiss")}
46
45
  retryLabel="Try again"
47
- dismissLabel="Ignore"
48
46
  />
49
47
  </YStack>
50
48
  );
@@ -1,8 +1,6 @@
1
- import { Button, Text } from "@multiplatform.one/components";
2
- import { useResolvedKnobs } from "@multiplatform.one/theme";
1
+ import { Alert } from "@multiplatform.one/components";
3
2
  import React from "react";
4
3
  import { useTranslation } from "react-i18next";
5
- import { XStack } from "tamagui";
6
4
 
7
5
  export interface SyncErrorNoticeProps {
8
6
  /** Human-readable failure message (already localized by the caller). */
@@ -12,20 +10,18 @@ export interface SyncErrorNoticeProps {
12
10
  * Omit to hide the Retry affordance.
13
11
  */
14
12
  onRetry?: () => void;
15
- /** Drops the failed entry without retrying. Omit to hide Dismiss. */
13
+ /** Drops the failed entry without retrying: the alert's close button. Omit to hide it. */
16
14
  onDismiss?: () => void;
17
15
  /** Override the Retry button label (defaults to a localized "Retry"). */
18
16
  retryLabel?: string;
19
- /** Override the Dismiss button label (defaults to a localized "Dismiss"). */
20
- dismissLabel?: string;
21
17
  }
22
18
 
23
19
  /**
24
20
  * The rejected-write notice — the visible half of the optimistic mutation
25
- * contract: when the server rejects a write, the optimistic state has
21
+ * contract, on the MPO error `Alert` (LC-175): when the server rejects a write, the optimistic state has
26
22
  * already been rolled back by the sync engine; this primitive surfaces the
27
23
  * error inline at the surface that initiated the write, with Retry
28
- * (re-issues the SAME mutation id) and Dismiss (clears the ledger entry).
24
+ * (re-issues the SAME mutation id) and a close button (clears the ledger entry).
29
25
  *
30
26
  * Pair with `useFrappePendingMutations` from `@multiplatform.one/frappe`:
31
27
  *
@@ -41,41 +37,17 @@ export interface SyncErrorNoticeProps {
41
37
  * ))}
42
38
  * ```
43
39
  */
44
- export function SyncErrorNotice({
45
- message,
46
- onRetry,
47
- onDismiss,
48
- retryLabel,
49
- dismissLabel,
50
- }: SyncErrorNoticeProps) {
40
+ export function SyncErrorNotice({ message, onRetry, onDismiss, retryLabel }: SyncErrorNoticeProps) {
51
41
  const { t } = useTranslation();
52
- const { knobProps } = useResolvedKnobs();
53
42
  return (
54
- <XStack
55
- role="alert"
56
- alignItems="center"
57
- flexWrap="wrap"
58
- gap="$2"
59
- paddingHorizontal="$3"
60
- paddingVertical="$2"
61
- {...knobProps.borderRadius}
62
- backgroundColor="$red3"
63
- borderColor="$red7"
64
- borderWidth={1}
43
+ <Alert
44
+ intent="error"
45
+ actionLabel={onRetry ? (retryLabel ?? t("Retry")) : undefined}
46
+ onAction={onRetry}
47
+ dismissible={Boolean(onDismiss)}
48
+ onDismiss={onDismiss}
65
49
  >
66
- <Text flex={1} fontSize="$2" color="$red11">
67
- {message}
68
- </Text>
69
- {onRetry && (
70
- <Button size="$2" theme="error" {...knobProps.borderRadius} onPress={onRetry}>
71
- {retryLabel ?? t("Retry")}
72
- </Button>
73
- )}
74
- {onDismiss && (
75
- <Button size="$2" chromeless {...knobProps.borderRadius} onPress={onDismiss}>
76
- {dismissLabel ?? t("Dismiss")}
77
- </Button>
78
- )}
79
- </XStack>
50
+ {message}
51
+ </Alert>
80
52
  );
81
53
  }
@@ -20,7 +20,6 @@ import { useDocTypeMeta, useFrappeCollection, useLiveQuery } from "@multiplatfor
20
20
  import { eq } from "@tanstack/db";
21
21
  import React, { useMemo } from "react";
22
22
  import { useTranslation } from "react-i18next";
23
- import { isWeb } from "tamagui";
24
23
  import {
25
24
  buildSidebarSections,
26
25
  mapDocToSidebarMetadata,
@@ -142,7 +141,7 @@ export function DeskFormSidebar({ doctype, name }: DeskFormSidebarProps) {
142
141
  // inline rail width, and drop the end rail — the pane gap separates the
143
142
  // sidebar from the form (stacked below on compact, the rail would float).
144
143
  width="100%"
145
- {...(isWeb ? { borderInlineEndWidth: 0 } : { borderEndWidth: 0 })}
144
+ divider={false}
146
145
  data-testid="desk-form-sidebar"
147
146
  />
148
147
  );
@@ -90,6 +90,7 @@ function useIsNarrowDesk(): boolean {
90
90
  // Component
91
91
  // ---------------------------------------------------------------------------
92
92
 
93
+ // mpo-role-escape: MPO has no app shell (top bar, nav rail, compact drawer) yet; FZ-7996 builds it and moves DeskShell onto it.
93
94
  export function DeskShell({
94
95
  children,
95
96
  breadcrumbs,
@@ -1,6 +1,7 @@
1
1
  /**
2
- * Web `Date` field for the three Frappe date fieldtypes, and the native one
3
- * when the optional `@react-native-community/datetimepicker` peer is absent.
2
+ * The `Date` field for the three Frappe date fieldtypes, on web and native.
3
+ * On iOS and Android the forms pickers open the OS sheet themselves
4
+ * (`OsDateTimeSheet`), so this field has no native fork (FZ-8000).
4
5
  *
5
6
  * The field used to render ONE date-only calendar for all three types, with
6
7
  * only the placeholder varying — so a `Datetime` silently dropped its time
@@ -14,8 +15,8 @@
14
15
  * registry routes the Frappe `Time` fieldtype to, so a
15
16
  * `Date type="time"` and a `Time` field are one control
16
17
  *
17
- * This mirrors what the native half already does (calendar / wheel /
18
- * calendar-then-wheel), and every branch serializes through the one shared
18
+ * On native the same three pickers present the OS calendar, wheel, and
19
+ * calendar-then-wheel, and every branch serializes through the one shared
19
20
  * `wire.ts` body, so a web pick and a native pick of the same moment persist
20
21
  * byte-identical strings.
21
22
  */
@@ -50,8 +51,7 @@ function toPickerDate(value: string | undefined): Date | null {
50
51
  /**
51
52
  * `TimePicker` speaks `HH:mm`; Frappe stores `HH:mm:ss`. Round-tripping
52
53
  * through the shared wire body — rather than padding the string here — is
53
- * what keeps the two platform halves byte-identical, since the native half
54
- * serializes its wheel pick through exactly this call.
54
+ * what keeps a picked time in Frappe's wire shape on every platform.
55
55
  */
56
56
  function toTimeWireValue(picked: string): string {
57
57
  if (!picked) return "";
@@ -130,7 +130,6 @@ export function Date<
130
130
  // No `previousValue`: this picker owns both halves of the value and
131
131
  // preserves an existing time across a day change itself, so carrying
132
132
  // the old time part here would overwrite a time the user just set.
133
- // The native half passes nothing for the same reason.
134
133
  onChange={(date) => emit?.(date ? toFrappeDateValue(date, "datetime") : "")}
135
134
  defaultValue={defaultValue as string}
136
135
  label={fieldProps.label}