@multiplatform.one/table 7.7.6 → 7.8.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 (357) hide show
  1. package/dist/cjs/components/ColumnHeaderFilter/index.cjs +74 -38
  2. package/dist/cjs/components/ColumnHeaderFilter/index.native.js +97 -51
  3. package/dist/cjs/components/ColumnHeaderFilter/index.native.js.map +1 -1
  4. package/dist/cjs/components/DataTable/DataTableCardControls.cjs +0 -1
  5. package/dist/cjs/components/DataTable/DataTableCardControls.native.js +0 -1
  6. package/dist/cjs/components/DataTable/DataTableCardControls.native.js.map +1 -1
  7. package/dist/cjs/components/DataTable/DataTableRoot.cjs +33 -6
  8. package/dist/cjs/components/DataTable/DataTableRoot.native.js +29 -4
  9. package/dist/cjs/components/DataTable/DataTableRoot.native.js.map +1 -1
  10. package/dist/cjs/components/DataTable/DataTableStateContext.native.js.map +1 -1
  11. package/dist/cjs/components/DataTableBulkActions/index.cjs +0 -4
  12. package/dist/cjs/components/DataTableBulkActions/index.native.js +0 -4
  13. package/dist/cjs/components/DataTableBulkActions/index.native.js.map +1 -1
  14. package/dist/cjs/components/DataTableCell/index.cjs +16 -1
  15. package/dist/cjs/components/DataTableCell/index.native.js +16 -1
  16. package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
  17. package/dist/cjs/components/DataTableDateFilter/index.cjs +0 -7
  18. package/dist/cjs/components/DataTableDateFilter/index.native.js +0 -7
  19. package/dist/cjs/components/DataTableDateFilter/index.native.js.map +1 -1
  20. package/dist/cjs/components/DataTableFilterButton/index.cjs +0 -1
  21. package/dist/cjs/components/DataTableFilterButton/index.native.js +0 -1
  22. package/dist/cjs/components/DataTableFilterChips/FilterChip.cjs +2 -1
  23. package/dist/cjs/components/DataTableFilterChips/FilterChip.native.js +4 -1
  24. package/dist/cjs/components/DataTableFilterChips/FilterChip.native.js.map +1 -1
  25. package/dist/cjs/components/DataTableFilters/index.cjs +0 -1
  26. package/dist/cjs/components/DataTableFilters/index.native.js +0 -1
  27. package/dist/cjs/components/DataTableGrouping/index.cjs +0 -8
  28. package/dist/cjs/components/DataTableGrouping/index.native.js +0 -8
  29. package/dist/cjs/components/DataTableGrouping/index.native.js.map +1 -1
  30. package/dist/cjs/components/DataTablePagination/index.cjs +29 -13
  31. package/dist/cjs/components/DataTablePagination/index.native.js +30 -14
  32. package/dist/cjs/components/DataTablePagination/index.native.js.map +1 -1
  33. package/dist/cjs/components/DataTableSearch/index.cjs +0 -3
  34. package/dist/cjs/components/DataTableSearch/index.native.js +0 -3
  35. package/dist/cjs/components/DataTableSearch/index.native.js.map +1 -1
  36. package/dist/cjs/components/DataTableToolbar/index.cjs +0 -1
  37. package/dist/cjs/components/DataTableToolbar/index.native.js +0 -1
  38. package/dist/cjs/components/DataTableToolbar/index.native.js.map +1 -1
  39. package/dist/cjs/components/DataTableViewOptions/index.cjs +0 -5
  40. package/dist/cjs/components/DataTableViewOptions/index.native.js +0 -5
  41. package/dist/cjs/components/DataTableViewOptions/index.native.js.map +1 -1
  42. package/dist/cjs/components/FilterManager.cjs +0 -1
  43. package/dist/cjs/components/FilterManager.native.js +0 -1
  44. package/dist/cjs/components/FilterManager.native.js.map +1 -1
  45. package/dist/cjs/components/FilterOperatorValueEditor/index.cjs +2 -2
  46. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js +2 -2
  47. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  48. package/dist/cjs/components/TableInput/index.cjs +2 -1
  49. package/dist/cjs/components/TableInput/index.native.js +4 -2
  50. package/dist/cjs/components/TableInput/index.native.js.map +1 -1
  51. package/dist/cjs/hooks/useDataTable.cjs +24 -8
  52. package/dist/cjs/hooks/useDataTable.native.js +28 -8
  53. package/dist/cjs/hooks/useDataTable.native.js.map +1 -1
  54. package/dist/cjs/index.cjs +12 -0
  55. package/dist/cjs/index.native.js +12 -0
  56. package/dist/cjs/index.native.js.map +1 -1
  57. package/dist/cjs/shared/devWarn.cjs +70 -0
  58. package/dist/cjs/shared/devWarn.native.js +84 -0
  59. package/dist/cjs/shared/devWarn.native.js.map +1 -0
  60. package/dist/cjs/utils/columnAlign.cjs +4 -1
  61. package/dist/cjs/utils/columnAlign.native.js +4 -1
  62. package/dist/cjs/utils/columnAlign.native.js.map +1 -1
  63. package/dist/cjs/utils/filterFields.cjs +5 -0
  64. package/dist/cjs/utils/filterFields.native.js +5 -0
  65. package/dist/cjs/utils/filterFields.native.js.map +1 -1
  66. package/dist/cjs/utils/filterUtils.cjs +30 -0
  67. package/dist/cjs/utils/filterUtils.native.js +50 -0
  68. package/dist/cjs/utils/filterUtils.native.js.map +1 -1
  69. package/dist/cjs/utils/money.cjs +137 -0
  70. package/dist/cjs/utils/money.native.js +165 -0
  71. package/dist/cjs/utils/money.native.js.map +1 -0
  72. package/dist/cjs/utils/rowAccumulator.cjs +48 -0
  73. package/dist/cjs/utils/rowAccumulator.native.js +51 -0
  74. package/dist/cjs/utils/rowAccumulator.native.js.map +1 -0
  75. package/dist/esm/components/ColumnHeaderFilter/index.mjs +78 -42
  76. package/dist/esm/components/ColumnHeaderFilter/index.mjs.map +1 -1
  77. package/dist/esm/components/ColumnHeaderFilter/index.native.js +101 -55
  78. package/dist/esm/components/ColumnHeaderFilter/index.native.js.map +1 -1
  79. package/dist/esm/components/DataTable/DataTableCardControls.mjs +0 -1
  80. package/dist/esm/components/DataTable/DataTableCardControls.mjs.map +1 -1
  81. package/dist/esm/components/DataTable/DataTableCardControls.native.js +0 -1
  82. package/dist/esm/components/DataTable/DataTableCardControls.native.js.map +1 -1
  83. package/dist/esm/components/DataTable/DataTableRoot.mjs +33 -6
  84. package/dist/esm/components/DataTable/DataTableRoot.mjs.map +1 -1
  85. package/dist/esm/components/DataTable/DataTableRoot.native.js +29 -4
  86. package/dist/esm/components/DataTable/DataTableRoot.native.js.map +1 -1
  87. package/dist/esm/components/DataTable/DataTableStateContext.mjs.map +1 -1
  88. package/dist/esm/components/DataTable/DataTableStateContext.native.js.map +1 -1
  89. package/dist/esm/components/DataTableBulkActions/index.mjs +0 -4
  90. package/dist/esm/components/DataTableBulkActions/index.mjs.map +1 -1
  91. package/dist/esm/components/DataTableBulkActions/index.native.js +0 -4
  92. package/dist/esm/components/DataTableBulkActions/index.native.js.map +1 -1
  93. package/dist/esm/components/DataTableCell/index.mjs +17 -2
  94. package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
  95. package/dist/esm/components/DataTableCell/index.native.js +17 -2
  96. package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
  97. package/dist/esm/components/DataTableDateFilter/index.mjs +0 -7
  98. package/dist/esm/components/DataTableDateFilter/index.mjs.map +1 -1
  99. package/dist/esm/components/DataTableDateFilter/index.native.js +0 -7
  100. package/dist/esm/components/DataTableDateFilter/index.native.js.map +1 -1
  101. package/dist/esm/components/DataTableFilterButton/index.mjs +0 -1
  102. package/dist/esm/components/DataTableFilterButton/index.mjs.map +1 -1
  103. package/dist/esm/components/DataTableFilterButton/index.native.js +0 -1
  104. package/dist/esm/components/DataTableFilterChips/FilterChip.mjs +3 -2
  105. package/dist/esm/components/DataTableFilterChips/FilterChip.mjs.map +1 -1
  106. package/dist/esm/components/DataTableFilterChips/FilterChip.native.js +5 -2
  107. package/dist/esm/components/DataTableFilterChips/FilterChip.native.js.map +1 -1
  108. package/dist/esm/components/DataTableFilters/index.mjs +0 -1
  109. package/dist/esm/components/DataTableFilters/index.mjs.map +1 -1
  110. package/dist/esm/components/DataTableFilters/index.native.js +0 -1
  111. package/dist/esm/components/DataTableGrouping/index.mjs +0 -8
  112. package/dist/esm/components/DataTableGrouping/index.mjs.map +1 -1
  113. package/dist/esm/components/DataTableGrouping/index.native.js +0 -8
  114. package/dist/esm/components/DataTableGrouping/index.native.js.map +1 -1
  115. package/dist/esm/components/DataTablePagination/index.mjs +30 -14
  116. package/dist/esm/components/DataTablePagination/index.mjs.map +1 -1
  117. package/dist/esm/components/DataTablePagination/index.native.js +31 -15
  118. package/dist/esm/components/DataTablePagination/index.native.js.map +1 -1
  119. package/dist/esm/components/DataTableSearch/index.mjs +0 -3
  120. package/dist/esm/components/DataTableSearch/index.mjs.map +1 -1
  121. package/dist/esm/components/DataTableSearch/index.native.js +0 -3
  122. package/dist/esm/components/DataTableSearch/index.native.js.map +1 -1
  123. package/dist/esm/components/DataTableToolbar/index.mjs +0 -1
  124. package/dist/esm/components/DataTableToolbar/index.mjs.map +1 -1
  125. package/dist/esm/components/DataTableToolbar/index.native.js +0 -1
  126. package/dist/esm/components/DataTableToolbar/index.native.js.map +1 -1
  127. package/dist/esm/components/DataTableViewOptions/index.mjs +0 -5
  128. package/dist/esm/components/DataTableViewOptions/index.mjs.map +1 -1
  129. package/dist/esm/components/DataTableViewOptions/index.native.js +0 -5
  130. package/dist/esm/components/DataTableViewOptions/index.native.js.map +1 -1
  131. package/dist/esm/components/FilterManager.mjs +0 -1
  132. package/dist/esm/components/FilterManager.mjs.map +1 -1
  133. package/dist/esm/components/FilterManager.native.js +0 -1
  134. package/dist/esm/components/FilterManager.native.js.map +1 -1
  135. package/dist/esm/components/FilterOperatorValueEditor/index.mjs +2 -2
  136. package/dist/esm/components/FilterOperatorValueEditor/index.mjs.map +1 -1
  137. package/dist/esm/components/FilterOperatorValueEditor/index.native.js +2 -2
  138. package/dist/esm/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  139. package/dist/esm/components/TableInput/index.mjs +3 -2
  140. package/dist/esm/components/TableInput/index.mjs.map +1 -1
  141. package/dist/esm/components/TableInput/index.native.js +5 -3
  142. package/dist/esm/components/TableInput/index.native.js.map +1 -1
  143. package/dist/esm/hooks/useDataTable.mjs +24 -8
  144. package/dist/esm/hooks/useDataTable.mjs.map +1 -1
  145. package/dist/esm/hooks/useDataTable.native.js +28 -8
  146. package/dist/esm/hooks/useDataTable.native.js.map +1 -1
  147. package/dist/esm/index.mjs +3 -1
  148. package/dist/esm/index.mjs.map +1 -1
  149. package/dist/esm/index.native.js +3 -1
  150. package/dist/esm/index.native.js.map +1 -1
  151. package/dist/esm/shared/devWarn.mjs +43 -0
  152. package/dist/esm/shared/devWarn.mjs.map +1 -0
  153. package/dist/esm/shared/devWarn.native.js +54 -0
  154. package/dist/esm/shared/devWarn.native.js.map +1 -0
  155. package/dist/esm/utils/columnAlign.mjs +4 -1
  156. package/dist/esm/utils/columnAlign.mjs.map +1 -1
  157. package/dist/esm/utils/columnAlign.native.js +4 -1
  158. package/dist/esm/utils/columnAlign.native.js.map +1 -1
  159. package/dist/esm/utils/filterFields.mjs +5 -0
  160. package/dist/esm/utils/filterFields.mjs.map +1 -1
  161. package/dist/esm/utils/filterFields.native.js +5 -0
  162. package/dist/esm/utils/filterFields.native.js.map +1 -1
  163. package/dist/esm/utils/filterUtils.mjs +30 -0
  164. package/dist/esm/utils/filterUtils.mjs.map +1 -1
  165. package/dist/esm/utils/filterUtils.native.js +50 -0
  166. package/dist/esm/utils/filterUtils.native.js.map +1 -1
  167. package/dist/esm/utils/money.mjs +104 -0
  168. package/dist/esm/utils/money.mjs.map +1 -0
  169. package/dist/esm/utils/money.native.js +129 -0
  170. package/dist/esm/utils/money.native.js.map +1 -0
  171. package/dist/esm/utils/rowAccumulator.mjs +23 -0
  172. package/dist/esm/utils/rowAccumulator.mjs.map +1 -0
  173. package/dist/esm/utils/rowAccumulator.native.js +23 -0
  174. package/dist/esm/utils/rowAccumulator.native.js.map +1 -0
  175. package/dist/jsx/components/ColumnHeaderFilter/index.mjs +78 -42
  176. package/dist/jsx/components/ColumnHeaderFilter/index.mjs.map +1 -1
  177. package/dist/jsx/components/ColumnHeaderFilter/index.native.js +97 -51
  178. package/dist/jsx/components/ColumnHeaderFilter/index.native.js.map +1 -1
  179. package/dist/jsx/components/DataTable/DataTableCardControls.mjs +0 -1
  180. package/dist/jsx/components/DataTable/DataTableCardControls.mjs.map +1 -1
  181. package/dist/jsx/components/DataTable/DataTableCardControls.native.js +0 -1
  182. package/dist/jsx/components/DataTable/DataTableCardControls.native.js.map +1 -1
  183. package/dist/jsx/components/DataTable/DataTableRoot.mjs +33 -6
  184. package/dist/jsx/components/DataTable/DataTableRoot.mjs.map +1 -1
  185. package/dist/jsx/components/DataTable/DataTableRoot.native.js +29 -4
  186. package/dist/jsx/components/DataTable/DataTableRoot.native.js.map +1 -1
  187. package/dist/jsx/components/DataTable/DataTableStateContext.mjs.map +1 -1
  188. package/dist/jsx/components/DataTable/DataTableStateContext.native.js.map +1 -1
  189. package/dist/jsx/components/DataTableBulkActions/index.mjs +0 -4
  190. package/dist/jsx/components/DataTableBulkActions/index.mjs.map +1 -1
  191. package/dist/jsx/components/DataTableBulkActions/index.native.js +0 -4
  192. package/dist/jsx/components/DataTableBulkActions/index.native.js.map +1 -1
  193. package/dist/jsx/components/DataTableCell/index.mjs +17 -2
  194. package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
  195. package/dist/jsx/components/DataTableCell/index.native.js +16 -1
  196. package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
  197. package/dist/jsx/components/DataTableDateFilter/index.mjs +0 -7
  198. package/dist/jsx/components/DataTableDateFilter/index.mjs.map +1 -1
  199. package/dist/jsx/components/DataTableDateFilter/index.native.js +0 -7
  200. package/dist/jsx/components/DataTableDateFilter/index.native.js.map +1 -1
  201. package/dist/jsx/components/DataTableFilterButton/index.mjs +0 -1
  202. package/dist/jsx/components/DataTableFilterButton/index.mjs.map +1 -1
  203. package/dist/jsx/components/DataTableFilterButton/index.native.js +0 -1
  204. package/dist/jsx/components/DataTableFilterChips/FilterChip.mjs +3 -2
  205. package/dist/jsx/components/DataTableFilterChips/FilterChip.mjs.map +1 -1
  206. package/dist/jsx/components/DataTableFilterChips/FilterChip.native.js +4 -1
  207. package/dist/jsx/components/DataTableFilterChips/FilterChip.native.js.map +1 -1
  208. package/dist/jsx/components/DataTableFilters/index.mjs +0 -1
  209. package/dist/jsx/components/DataTableFilters/index.mjs.map +1 -1
  210. package/dist/jsx/components/DataTableFilters/index.native.js +0 -1
  211. package/dist/jsx/components/DataTableGrouping/index.mjs +0 -8
  212. package/dist/jsx/components/DataTableGrouping/index.mjs.map +1 -1
  213. package/dist/jsx/components/DataTableGrouping/index.native.js +0 -8
  214. package/dist/jsx/components/DataTableGrouping/index.native.js.map +1 -1
  215. package/dist/jsx/components/DataTablePagination/index.mjs +30 -14
  216. package/dist/jsx/components/DataTablePagination/index.mjs.map +1 -1
  217. package/dist/jsx/components/DataTablePagination/index.native.js +30 -14
  218. package/dist/jsx/components/DataTablePagination/index.native.js.map +1 -1
  219. package/dist/jsx/components/DataTableSearch/index.mjs +0 -3
  220. package/dist/jsx/components/DataTableSearch/index.mjs.map +1 -1
  221. package/dist/jsx/components/DataTableSearch/index.native.js +0 -3
  222. package/dist/jsx/components/DataTableSearch/index.native.js.map +1 -1
  223. package/dist/jsx/components/DataTableToolbar/index.mjs +0 -1
  224. package/dist/jsx/components/DataTableToolbar/index.mjs.map +1 -1
  225. package/dist/jsx/components/DataTableToolbar/index.native.js +0 -1
  226. package/dist/jsx/components/DataTableToolbar/index.native.js.map +1 -1
  227. package/dist/jsx/components/DataTableViewOptions/index.mjs +0 -5
  228. package/dist/jsx/components/DataTableViewOptions/index.mjs.map +1 -1
  229. package/dist/jsx/components/DataTableViewOptions/index.native.js +0 -5
  230. package/dist/jsx/components/DataTableViewOptions/index.native.js.map +1 -1
  231. package/dist/jsx/components/FilterManager.mjs +0 -1
  232. package/dist/jsx/components/FilterManager.mjs.map +1 -1
  233. package/dist/jsx/components/FilterManager.native.js +0 -1
  234. package/dist/jsx/components/FilterManager.native.js.map +1 -1
  235. package/dist/jsx/components/FilterOperatorValueEditor/index.mjs +2 -2
  236. package/dist/jsx/components/FilterOperatorValueEditor/index.mjs.map +1 -1
  237. package/dist/jsx/components/FilterOperatorValueEditor/index.native.js +2 -2
  238. package/dist/jsx/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  239. package/dist/jsx/components/TableInput/index.mjs +3 -2
  240. package/dist/jsx/components/TableInput/index.mjs.map +1 -1
  241. package/dist/jsx/components/TableInput/index.native.js +4 -2
  242. package/dist/jsx/components/TableInput/index.native.js.map +1 -1
  243. package/dist/jsx/hooks/useDataTable.mjs +24 -8
  244. package/dist/jsx/hooks/useDataTable.mjs.map +1 -1
  245. package/dist/jsx/hooks/useDataTable.native.js +28 -8
  246. package/dist/jsx/hooks/useDataTable.native.js.map +1 -1
  247. package/dist/jsx/index.js +3 -1
  248. package/dist/jsx/index.js.map +1 -1
  249. package/dist/jsx/index.mjs +3 -1
  250. package/dist/jsx/index.mjs.map +1 -1
  251. package/dist/jsx/index.native.js +12 -0
  252. package/dist/jsx/index.native.js.map +1 -1
  253. package/dist/jsx/shared/devWarn.mjs +43 -0
  254. package/dist/jsx/shared/devWarn.mjs.map +1 -0
  255. package/dist/jsx/shared/devWarn.native.js +84 -0
  256. package/dist/jsx/shared/devWarn.native.js.map +1 -0
  257. package/dist/jsx/utils/columnAlign.mjs +4 -1
  258. package/dist/jsx/utils/columnAlign.mjs.map +1 -1
  259. package/dist/jsx/utils/columnAlign.native.js +4 -1
  260. package/dist/jsx/utils/columnAlign.native.js.map +1 -1
  261. package/dist/jsx/utils/filterFields.mjs +5 -0
  262. package/dist/jsx/utils/filterFields.mjs.map +1 -1
  263. package/dist/jsx/utils/filterFields.native.js +5 -0
  264. package/dist/jsx/utils/filterFields.native.js.map +1 -1
  265. package/dist/jsx/utils/filterUtils.mjs +30 -0
  266. package/dist/jsx/utils/filterUtils.mjs.map +1 -1
  267. package/dist/jsx/utils/filterUtils.native.js +50 -0
  268. package/dist/jsx/utils/filterUtils.native.js.map +1 -1
  269. package/dist/jsx/utils/money.mjs +104 -0
  270. package/dist/jsx/utils/money.mjs.map +1 -0
  271. package/dist/jsx/utils/money.native.js +165 -0
  272. package/dist/jsx/utils/money.native.js.map +1 -0
  273. package/dist/jsx/utils/rowAccumulator.mjs +23 -0
  274. package/dist/jsx/utils/rowAccumulator.mjs.map +1 -0
  275. package/dist/jsx/utils/rowAccumulator.native.js +51 -0
  276. package/dist/jsx/utils/rowAccumulator.native.js.map +1 -0
  277. package/package.json +12 -12
  278. package/src/components/ColumnHeaderFilter/ColumnHeaderFilter.spec.tsx +142 -7
  279. package/src/components/ColumnHeaderFilter/ColumnHeaderFilter.stories.tsx +46 -9
  280. package/src/components/ColumnHeaderFilter/index.tsx +104 -47
  281. package/src/components/DataTable/DataTable.stories.tsx +186 -14
  282. package/src/components/DataTable/DataTableCardControls.tsx +11 -13
  283. package/src/components/DataTable/DataTableRoot.tsx +40 -14
  284. package/src/components/DataTable/DataTableStateContext.ts +7 -0
  285. package/src/components/DataTable/columnWidthLayout.spec.tsx +195 -0
  286. package/src/components/DataTable/headerSorting.spec.tsx +25 -0
  287. package/src/components/DataTable/moneyColumn.spec.tsx +151 -0
  288. package/src/components/DataTable/rowAccumulator.spec.tsx +171 -0
  289. package/src/components/DataTableBulkActions/index.tsx +0 -4
  290. package/src/components/DataTableCell/index.tsx +28 -2
  291. package/src/components/DataTableColumnPinning/DataTableColumnPinning.stories.tsx +0 -4
  292. package/src/components/DataTableDateFilter/index.tsx +0 -7
  293. package/src/components/DataTableFilterButton/index.tsx +1 -1
  294. package/src/components/DataTableFilterChips/FilterChip.spec.tsx +6 -0
  295. package/src/components/DataTableFilterChips/FilterChip.tsx +3 -2
  296. package/src/components/DataTableFilters/index.tsx +1 -1
  297. package/src/components/DataTableGrouping/DataTableGrouping.stories.tsx +0 -4
  298. package/src/components/DataTableGrouping/index.tsx +1 -13
  299. package/src/components/DataTablePagination/DataTablePagination.stories.tsx +14 -1
  300. package/src/components/DataTablePagination/DataTablePagination.width.spec.tsx +51 -0
  301. package/src/components/DataTablePagination/index.tsx +60 -16
  302. package/src/components/DataTableSearch/index.tsx +2 -3
  303. package/src/components/DataTableToolbar/DataTableToolbar.stories.tsx +2 -4
  304. package/src/components/DataTableToolbar/index.tsx +1 -5
  305. package/src/components/DataTableViewOptions/index.tsx +2 -13
  306. package/src/components/FilterManager.tsx +1 -3
  307. package/src/components/FilterOperatorValueEditor/FilterOperatorValueEditor.spec.tsx +1 -1
  308. package/src/components/FilterOperatorValueEditor/FilterOperatorValueEditor.stories.tsx +1 -0
  309. package/src/components/FilterOperatorValueEditor/index.tsx +10 -2
  310. package/src/components/TableInput/index.native.tsx +3 -2
  311. package/src/components/TableInput/index.tsx +3 -2
  312. package/src/hooks/useDataTable.ts +43 -7
  313. package/src/index.ts +13 -0
  314. package/src/shared/devWarn.ts +97 -0
  315. package/src/types.ts +71 -2
  316. package/src/utils/columnAlign.ts +9 -1
  317. package/src/utils/filterFields.ts +23 -1
  318. package/src/utils/filterUtils.ts +50 -1
  319. package/src/utils/money.spec.ts +156 -0
  320. package/src/utils/money.ts +202 -0
  321. package/src/utils/rowAccumulator.ts +136 -0
  322. package/types/components/ColumnHeaderFilter/index.d.ts +9 -2
  323. package/types/components/ColumnHeaderFilter/index.d.ts.map +1 -1
  324. package/types/components/DataTable/DataTableCardControls.d.ts.map +1 -1
  325. package/types/components/DataTable/DataTableRoot.d.ts +1 -1
  326. package/types/components/DataTable/DataTableRoot.d.ts.map +1 -1
  327. package/types/components/DataTable/DataTableStateContext.d.ts +7 -0
  328. package/types/components/DataTable/DataTableStateContext.d.ts.map +1 -1
  329. package/types/components/DataTableBulkActions/index.d.ts.map +1 -1
  330. package/types/components/DataTableCell/index.d.ts.map +1 -1
  331. package/types/components/DataTableDateFilter/index.d.ts.map +1 -1
  332. package/types/components/DataTableFilterChips/FilterChip.d.ts.map +1 -1
  333. package/types/components/DataTableGrouping/index.d.ts.map +1 -1
  334. package/types/components/DataTablePagination/index.d.ts.map +1 -1
  335. package/types/components/DataTableSearch/index.d.ts.map +1 -1
  336. package/types/components/DataTableToolbar/index.d.ts.map +1 -1
  337. package/types/components/DataTableViewOptions/index.d.ts.map +1 -1
  338. package/types/components/FilterManager.d.ts.map +1 -1
  339. package/types/components/FilterOperatorValueEditor/index.d.ts.map +1 -1
  340. package/types/components/TableInput/index.d.ts.map +1 -1
  341. package/types/components/TableInput/index.native.d.ts.map +1 -1
  342. package/types/hooks/useDataTable.d.ts +1 -1
  343. package/types/hooks/useDataTable.d.ts.map +1 -1
  344. package/types/index.d.ts +3 -0
  345. package/types/index.d.ts.map +1 -1
  346. package/types/shared/devWarn.d.ts +50 -0
  347. package/types/shared/devWarn.d.ts.map +1 -0
  348. package/types/types.d.ts +71 -3
  349. package/types/types.d.ts.map +1 -1
  350. package/types/utils/columnAlign.d.ts.map +1 -1
  351. package/types/utils/filterFields.d.ts.map +1 -1
  352. package/types/utils/filterUtils.d.ts +1 -1
  353. package/types/utils/filterUtils.d.ts.map +1 -1
  354. package/types/utils/money.d.ts +94 -0
  355. package/types/utils/money.d.ts.map +1 -0
  356. package/types/utils/rowAccumulator.d.ts +107 -0
  357. package/types/utils/rowAccumulator.d.ts.map +1 -0
@@ -23,7 +23,7 @@ import { TrashIcon, PlusIcon } from "@phosphor-icons/react";
23
23
  import { useTranslation } from "react-i18next";
24
24
  import { getTableCellPadding } from "@multiplatform.one/table-primitives";
25
25
  import { hairline, useResolvedKnobs } from "@multiplatform.one/theme";
26
- import { Text, View, XStack, YStack } from "tamagui";
26
+ import { Text, View, XStack, YStack, useTheme } from "tamagui";
27
27
  import type {
28
28
  DataTableColumn,
29
29
  DataTableData,
@@ -212,6 +212,7 @@ function TableInputInner<TData extends DataTableData>({
212
212
  compact,
213
213
  t,
214
214
  }: TableInputInnerProps<TData>) {
215
+ const theme = useTheme();
215
216
  const { knobProps } = useResolvedKnobs({ compact });
216
217
  const cellPad = getTableCellPadding(knobProps.space);
217
218
  const chromePx = knobProps.nestedControl.px;
@@ -332,7 +333,7 @@ function TableInputInner<TData extends DataTableData>({
332
333
  row: index + 1,
333
334
  })}
334
335
  >
335
- <TrashIcon size={16} color="$red10" />
336
+ <TrashIcon size={16} color={theme.red10?.val ?? "currentColor"} />
336
337
  </Button>
337
338
  </View>
338
339
  )}
@@ -38,7 +38,7 @@ import { TrashIcon, PlusIcon } from "@phosphor-icons/react";
38
38
  import { useTranslation } from "react-i18next";
39
39
  import { Table, getTableCellPadding } from "@multiplatform.one/table-primitives";
40
40
  import { hairline, useResolvedKnobs } from "@multiplatform.one/theme";
41
- import { Text, View, XStack, YStack, isWeb } from "tamagui";
41
+ import { Text, View, XStack, YStack, isWeb, useTheme } from "tamagui";
42
42
  import type {
43
43
  DataTableColumn,
44
44
  DataTableData,
@@ -240,6 +240,7 @@ function TableInputInner<TData extends DataTableData>({
240
240
  enableReorder,
241
241
  t,
242
242
  }: TableInputInnerProps<TData>) {
243
+ const theme = useTheme();
243
244
  const { knobProps } = useResolvedKnobs({ compact });
244
245
  const cellPad = getTableCellPadding(knobProps.space);
245
246
  const chromePx = knobProps.nestedControl.px;
@@ -471,7 +472,7 @@ function TableInputInner<TData extends DataTableData>({
471
472
  row: row.index + 1,
472
473
  })}
473
474
  >
474
- <TrashIcon size={16} color="$red10" />
475
+ <TrashIcon size={16} color={theme.red10?.val ?? "currentColor"} />
475
476
  </Button>
476
477
  );
477
478
  },
@@ -171,6 +171,7 @@ export const createUrlStrategy = {
171
171
  import React, { useMemo, useState, useCallback, useEffect, useRef } from "react";
172
172
  import type {
173
173
  DataTableData,
174
+ DataTableField,
174
175
  DataTableInfiniteState,
175
176
  DataTableState,
176
177
  UseDataTableOptions,
@@ -183,6 +184,8 @@ import {
183
184
  resolveFilterFields,
184
185
  } from "../utils/filterFields";
185
186
  import { applyFilterToValue } from "../utils/filterUtils";
187
+ import { accumulateRows } from "../utils/rowAccumulator";
188
+ import { moneyComparator } from "../utils/money";
186
189
  import { DEFAULT_PAGE_SIZE } from "../utils/paginationDefaults";
187
190
  import { deserializeTableState, serializeTableState } from "../utils/urlStateUtils";
188
191
  import { useInfiniteRows } from "./useInfiniteRows";
@@ -190,6 +193,13 @@ import { useTableFiltering } from "./useTableFiltering";
190
193
  import { useTableSorting } from "./useTableSorting";
191
194
  import { useTableViews } from "./useTableViews";
192
195
 
196
+ /**
197
+ * TanStack sort for a `money` column: exact decimal-string ordering, so
198
+ * "9.00" sorts below "10.00" (MPO-252).
199
+ */
200
+ const moneySortingFn = (rowA: any, rowB: any, columnId: string): number =>
201
+ moneyComparator(rowA.getValue(columnId), rowB.getValue(columnId));
202
+
193
203
  export function useDataTable<TData extends DataTableData = DataTableData>({
194
204
  data = [],
195
205
  columns = [],
@@ -208,6 +218,7 @@ export function useDataTable<TData extends DataTableData = DataTableData>({
208
218
  enablePagination: _enablePagination = true,
209
219
  paginationMode = "pages",
210
220
  loadPage,
221
+ rowAccumulator,
211
222
  enableColumnVisibility = true,
212
223
  enableColumnOrdering: _enableColumnOrdering = false,
213
224
  enableColumnResizing = false,
@@ -360,6 +371,21 @@ export function useDataTable<TData extends DataTableData = DataTableData>({
360
371
  // pre-filtered/sorted, so manual modes are forced on. In infinite mode the
361
372
  // pagination row model is never applied (rows accumulate instead of slice).
362
373
  const effectiveData = isLoaderMode ? infiniteRows.rows : data;
374
+
375
+ // ---- Row accumulator (MPO-250) ----
376
+ //
377
+ // The fold runs HERE — over the whole row set, before the advanced filters
378
+ // below and before TanStack's filter/sort/paginate models — because a
379
+ // derived column like a running balance depends on every row before it and
380
+ // would restart on page two otherwise. `order` (when given) is also the
381
+ // order the rows reach the table in: a fold order is the order its column
382
+ // reads down. Sorting on another column reorders for display and does NOT
383
+ // refold; see the contract on RowAccumulator.
384
+ const { rows: accumulatedData, total: foldTotal } = useMemo(
385
+ () => accumulateRows(effectiveData, rowAccumulator),
386
+ [effectiveData, rowAccumulator],
387
+ );
388
+
363
389
  const effectiveManualFiltering = manualFiltering || isLoaderMode;
364
390
  const effectiveManualSorting = manualSorting || isLoaderMode;
365
391
  const effectiveManualPagination = manualPagination || infiniteEnabled;
@@ -375,9 +401,9 @@ export function useDataTable<TData extends DataTableData = DataTableData>({
375
401
  // including hidden / non-column fields. Server-side tables (manualFiltering)
376
402
  // map the same filters to query params instead.
377
403
  const preFilteredData = useMemo(() => {
378
- if (effectiveManualFiltering) return effectiveData;
379
- return applyFiltersToData(effectiveData, filtering.advancedFilters, filterFields);
380
- }, [effectiveData, effectiveManualFiltering, filtering.advancedFilters, filterFields]);
404
+ if (effectiveManualFiltering) return accumulatedData;
405
+ return applyFiltersToData(accumulatedData, filtering.advancedFilters, filterFields);
406
+ }, [accumulatedData, effectiveManualFiltering, filtering.advancedFilters, filterFields]);
381
407
 
382
408
  // Column-funnel filters store FilterValue objects ({operator, value}) —
383
409
  // interpret them with the shared operator semantics instead of TanStack's
@@ -455,13 +481,20 @@ export function useDataTable<TData extends DataTableData = DataTableData>({
455
481
  const normalizedColumns = useMemo(
456
482
  () =>
457
483
  columns.map((column) => {
458
- const { sortable, filterable } = column as {
484
+ const { sortable, filterable, field } = column as {
459
485
  sortable?: boolean;
460
486
  filterable?: boolean;
487
+ field?: DataTableField;
461
488
  };
462
- if (sortable === undefined && filterable === undefined) return column;
489
+ // A money column holds a decimal STRING, and TanStack's auto sort
490
+ // would compare it as text — "9.00" above "10.00", silently wrong on
491
+ // every amount column (MPO-252). Give it the exact comparator so
492
+ // `sortable: true` is safe to turn on. An explicit sortingFn wins.
493
+ const needsMoneySort = field === "money" && column.sortingFn === undefined;
494
+ if (sortable === undefined && filterable === undefined && !needsMoneySort) return column;
463
495
  return {
464
496
  ...column,
497
+ ...(needsMoneySort ? { sortingFn: moneySortingFn } : {}),
465
498
  ...(sortable !== undefined && column.enableSorting === undefined
466
499
  ? { enableSorting: sortable }
467
500
  : {}),
@@ -596,9 +629,9 @@ export function useDataTable<TData extends DataTableData = DataTableData>({
596
629
  // ---- Computed Values ----
597
630
 
598
631
  const filteredData = useMemo(() => {
599
- if (effectiveManualFiltering) return effectiveData;
632
+ if (effectiveManualFiltering) return accumulatedData;
600
633
  return table.getFilteredRowModel().rows.map((row) => row.original);
601
- }, [effectiveData, table, effectiveManualFiltering]);
634
+ }, [accumulatedData, table, effectiveManualFiltering]);
602
635
 
603
636
  const paginatedData = useMemo(() => {
604
637
  if (effectiveManualPagination) return filteredData;
@@ -708,6 +741,9 @@ export function useDataTable<TData extends DataTableData = DataTableData>({
708
741
  activeFilterCount: filtering.activeFilterCount,
709
742
  filterFields,
710
743
  infinite,
744
+ // The fold read once at the end — a footer cell renders it through
745
+ // useDataTableState() (MPO-250).
746
+ foldTotal,
711
747
  };
712
748
 
713
749
  // ---- State Change Callback ----
package/src/index.ts CHANGED
@@ -157,6 +157,19 @@ export {
157
157
  resolveFilterFields,
158
158
  } from "./utils/filterFields";
159
159
  export * from "./utils/urlStateUtils";
160
+ export {
161
+ addMoney,
162
+ compareMoney,
163
+ formatMoney,
164
+ isMoneyString,
165
+ isNegativeMoney,
166
+ moneyComparator,
167
+ subtractMoney,
168
+ sumMoney,
169
+ sumMoneyByCurrency,
170
+ } from "./utils/money";
171
+ export { accumulateRows } from "./utils/rowAccumulator";
172
+ export type { RowAccumulator, RowAccumulatorResult } from "./utils/rowAccumulator";
160
173
  export {
161
174
  getDefaultColumnAlign,
162
175
  alignToFlex,
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Table DEV guardrails — same shape as the `@multiplatform.one/theme` §5
3
+ * catalog (`devWarn` / `warnBareDisabled` / `warnSizeRecipeEscape`): a
4
+ * module-level seen set, a `NODE_ENV === "production"` no-op, and helpers
5
+ * called inline during render.
6
+ *
7
+ * The prefix is `[table]`, not `[theme]`: the codes here are table-local and
8
+ * the story-hygiene harvester (`p-results/story-hygiene/verify.mjs`) collects
9
+ * on the `[theme]` prefix.
10
+ */
11
+
12
+ import type { DataTableProps } from "../types";
13
+
14
+ export type TableDevWarnCode =
15
+ /**
16
+ * A column declares a width (`width` or TanStack `size`) that the resolved
17
+ * `columnLayout` discards before it reaches the cell box.
18
+ */
19
+ "column-width-ignored";
20
+
21
+ export type TableDevWarnDetail = {
22
+ component?: string;
23
+ id?: string;
24
+ columns?: string;
25
+ columnLayout?: string;
26
+ suggest?: string;
27
+ };
28
+
29
+ const seen = new Set<string>();
30
+
31
+ function isDev(): boolean {
32
+ return process.env.NODE_ENV !== "production";
33
+ }
34
+
35
+ /** Emit a once-per-key console warning in non-production builds. */
36
+ export function tableDevWarn(code: TableDevWarnCode, detail: TableDevWarnDetail = {}): void {
37
+ if (!isDev()) return;
38
+ const key = [code, detail.component ?? "", detail.id ?? "", detail.columns ?? ""].join(":");
39
+ if (seen.has(key)) return;
40
+ seen.add(key);
41
+ const bits = [
42
+ detail.component ? `component=${detail.component}` : undefined,
43
+ detail.id ? `id=${detail.id}` : undefined,
44
+ detail.columns ? `columns=${detail.columns}` : undefined,
45
+ detail.columnLayout ? `columnLayout=${detail.columnLayout}` : undefined,
46
+ detail.suggest ? `suggest=${detail.suggest}` : undefined,
47
+ ].filter(Boolean);
48
+ const suffix = bits.length ? ` (${bits.join(", ")})` : "";
49
+ console.warn(`[table] ${code}${suffix}`);
50
+ }
51
+
52
+ /** @internal test helper */
53
+ export function __resetTableDevWarnSeen(): void {
54
+ seen.clear();
55
+ }
56
+
57
+ type WidthBearingColumn = {
58
+ id?: unknown;
59
+ accessorKey?: unknown;
60
+ width?: unknown;
61
+ size?: unknown;
62
+ };
63
+
64
+ function columnKey(column: WidthBearingColumn, index: number): string {
65
+ if (typeof column.id === "string" && column.id) return column.id;
66
+ if (typeof column.accessorKey === "string" && column.accessorKey) return column.accessorKey;
67
+ return `#${index}`;
68
+ }
69
+
70
+ /**
71
+ * MPO-236 — `columnLayout: "equal"` (the default) flexes every column to the
72
+ * same width, so a per-column `width` / `size` never reaches the cell box and
73
+ * the render is byte-identical to one with no widths at all.
74
+ *
75
+ * `enableColumnResizing` is the exemption: it swaps the cell flex to
76
+ * `flexBasis: "auto"`, under which a declared width does survive.
77
+ */
78
+ export function warnColumnWidthIgnored(args: {
79
+ columnLayout?: DataTableProps["columnLayout"];
80
+ enableColumnResizing?: boolean;
81
+ columns: readonly WidthBearingColumn[];
82
+ component?: string;
83
+ }): void {
84
+ if (!isDev()) return;
85
+ if (args.columnLayout !== "equal" || args.enableColumnResizing) return;
86
+ const offenders = args.columns
87
+ .map((column, index) => ({ column, index }))
88
+ .filter(({ column }) => typeof column.width === "number" || typeof column.size === "number")
89
+ .map(({ column, index }) => columnKey(column, index));
90
+ if (!offenders.length) return;
91
+ tableDevWarn("column-width-ignored", {
92
+ component: args.component ?? "DataTable",
93
+ columns: offenders.join(","),
94
+ columnLayout: "equal",
95
+ suggest: 'columnLayout="auto" or "scroll", or enableColumnResizing',
96
+ });
97
+ }
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ColumnDef, ColumnFiltersState, Row } from "@tanstack/react-table";
2
+ import type { RowAccumulator } from "./utils/rowAccumulator";
2
3
  import type { UrlStateStrategy } from "./hooks/useDataTable";
3
4
  import type { AnyFormApi } from "@multiplatform.one/forms";
4
5
  import type React from "react";
@@ -50,6 +51,12 @@ export type DataTableField =
50
51
  // Numeric inputs
51
52
  | "stepper"
52
53
  | "slider"
54
+ /**
55
+ * A Currency field: a decimal STRING, formatted for display and ordered
56
+ * numerically. Read-only presentation — money is edited through the form
57
+ * surfaces, not typed into a grid cell (MPO-252).
58
+ */
59
+ | "money"
53
60
  // Selection inputs
54
61
  | "select"
55
62
  | "combobox"
@@ -113,8 +120,17 @@ export interface DataTableFieldProps {
113
120
  step?: number;
114
121
  /** Precision for numeric fields */
115
122
  precision?: number;
116
- /** Currency code for currency fields */
123
+ /** Currency code for currency fields. A column-level constant. */
117
124
  currency?: string;
125
+ /**
126
+ * Row-data key holding this row's currency code, for a `money` column
127
+ * (MPO-252). A ledger genuinely mixes currencies row by row, so the row's
128
+ * own code WINS over the column-level `currency` constant; `currency` is
129
+ * the fallback for the rows that carry none.
130
+ */
131
+ currencyField?: string;
132
+ /** BCP-47 locale for money formatting. Defaults to the runtime's. */
133
+ locale?: string;
118
134
  /** Doctype for link fields (Frappe) */
119
135
  doctype?: string;
120
136
  /** Default country for phone input (ISO 3166-1 alpha-2) */
@@ -164,6 +180,12 @@ export type DataTableColumn<TData extends DataTableData = DataTableData> = Colum
164
180
  sortable?: boolean;
165
181
  searchable?: boolean;
166
182
  hidden?: boolean;
183
+ /**
184
+ * Column width intent, in px. Read by the view-options width slider and by
185
+ * saved views; the RENDERED cell box comes from TanStack `size`. Under the
186
+ * default `columnLayout: "equal"` neither one survives, and declaring
187
+ * either warns (see `columnLayout`).
188
+ */
167
189
  width?: number;
168
190
  minWidth?: number;
169
191
  maxWidth?: number;
@@ -423,9 +445,27 @@ export interface DataTableState {
423
445
 
424
446
  // Component Props Types
425
447
 
448
+ export type { RowAccumulator, RowAccumulatorResult } from "./utils/rowAccumulator";
449
+
426
450
  export interface DataTableProps<TData extends DataTableData = DataTableData> {
427
451
  data: TData[];
428
452
  columns: DataTableColumn<TData>[];
453
+ /**
454
+ * A fold over the ordered row set, run ONCE before filtering, sorting and
455
+ * pagination — the seam a running balance needs, because the value on row N
456
+ * depends on rows 1..N-1 and a column `cell` renderer only ever sees one
457
+ * row (MPO-250).
458
+ *
459
+ * The derived fields are merged onto each row, so a column names one in
460
+ * `accessorKey` like any other. Declare them on the row type. `total` is
461
+ * published as `computed.foldTotal` for a footer cell.
462
+ *
463
+ * Must be a STABLE reference (module constant or `useMemo`) — the fold
464
+ * re-runs when its identity changes. The full ordering contract, including
465
+ * what happens when the user sorts on another column, is documented on
466
+ * `RowAccumulator` in utils/rowAccumulator.ts.
467
+ */
468
+ rowAccumulator?: RowAccumulator<TData>;
429
469
 
430
470
  // State Management
431
471
  initialState?: Partial<DataTableState>;
@@ -528,6 +568,14 @@ export interface DataTableProps<TData extends DataTableData = DataTableData> {
528
568
  * - 'equal': All columns equal width, no horizontal scroll (default)
529
569
  * - 'auto': Content-based width, last column fills remainder, squishes if needed
530
570
  * - 'scroll': Content-based width, horizontal scroll when content exceeds container
571
+ *
572
+ * Under 'equal' a PER-COLUMN WIDTH IS IGNORED. Every cell gets
573
+ * `flexGrow: 1, flexBasis: 0`, so a column's `width` / `size` never reaches
574
+ * the cell box and the render is identical to one that declared no widths.
575
+ * Use 'auto' or 'scroll' when the widths must hold, or turn on
576
+ * `enableColumnResizing`, which switches the cells to `flexBasis: "auto"`.
577
+ * Declaring a width under 'equal' emits the `[table] column-width-ignored`
578
+ * dev warning rather than failing silently (MPO-236).
531
579
  */
532
580
  columnLayout?: "equal" | "auto" | "scroll";
533
581
 
@@ -762,12 +810,33 @@ export interface UseDataTableReturn<TData extends DataTableData = DataTableData>
762
810
  filterFields: ResolvedFilterField[];
763
811
  /** Present when paginationMode="infinite" */
764
812
  infinite?: DataTableInfiniteState;
813
+ /**
814
+ * `rowAccumulator.total(finalAccumulator, rowCount)` — the fold read once
815
+ * at the end, for a footer/total row. Undefined when the table has no
816
+ * accumulator or the accumulator declares no `total` (MPO-250).
817
+ */
818
+ foldTotal?: unknown;
765
819
  };
766
820
  }
767
821
 
768
822
  // Utility Types
769
823
 
770
- export type DataTableColumnType = "text" | "number" | "date" | "boolean" | "select";
824
+ /**
825
+ * Filter-editor / comparison type for a column.
826
+ *
827
+ * `"money"` (MPO-252) is a decimal STRING, not a number: a Frappe Currency
828
+ * field arrives as "-455.87" because a float would round it. It sorts and
829
+ * filters through the exact BigInt helpers in utils/money.ts, so `"9.00"`
830
+ * orders BELOW `"10.00"` — which the `"text"` fallback got backwards, in
831
+ * silence, on every amount column in the kit.
832
+ *
833
+ * The mixed-currency rule rides this type: TWO CURRENCIES ARE TWO FIGURES AND
834
+ * NEITHER CONVERTS. A money column formats from the row's own currency
835
+ * (`fieldProps.currencyField`), comparison is by magnitude only, and a total
836
+ * over mixed rows is `sumMoneyByCurrency` — one figure per code, never one
837
+ * number. There is no rate in the table and the kit will not invent one.
838
+ */
839
+ export type DataTableColumnType = "text" | "number" | "money" | "date" | "boolean" | "select";
771
840
 
772
841
  export interface DataTableColumnConfig {
773
842
  type: DataTableColumnType;
@@ -17,7 +17,15 @@ export type ColumnAlignInput = {
17
17
  meta?: { align?: ColumnAlign; role?: string };
18
18
  };
19
19
 
20
- const NUMERIC_FIELDS = new Set<DataTableField>(["stepper", "slider", "progress", "duration"]);
20
+ const NUMERIC_FIELDS = new Set<DataTableField>([
21
+ "stepper",
22
+ "slider",
23
+ "progress",
24
+ "duration",
25
+ // A money column right-aligns on its own, without needing a column-level
26
+ // `currency` constant — a ledger reads its code off each row (MPO-252).
27
+ "money",
28
+ ]);
21
29
 
22
30
  /** Booleans + icon-like metrics (EUI: center symbols). */
23
31
  const CENTER_FIELDS = new Set<DataTableField>(["checkbox", "switch", "rating"]);
@@ -41,6 +41,8 @@ export function mapFieldToFilterType(field?: DataTableField): DataTableColumnTyp
41
41
  case "rating":
42
42
  case "duration":
43
43
  return "number";
44
+ case "money":
45
+ return "money";
44
46
  case "datepicker":
45
47
  case "datetimepicker":
46
48
  case "timepicker":
@@ -117,7 +119,9 @@ export function getFilterFieldType(
117
119
  // ── Client-side application ───────────────────────────────────
118
120
 
119
121
  /** "select" fields compare as text; the others map 1:1. */
120
- function comparisonType(type: DataTableColumnType): "text" | "number" | "date" | "boolean" {
122
+ function comparisonType(
123
+ type: DataTableColumnType,
124
+ ): "text" | "number" | "money" | "date" | "boolean" {
121
125
  return type === "select" ? "text" : type;
122
126
  }
123
127
 
@@ -167,6 +171,22 @@ const NUMBER_OPERATORS: FilterOperator[] = [
167
171
  "isGreaterThanOrEqual",
168
172
  ];
169
173
 
174
+ /**
175
+ * Money takes the numeric operators PLUS the range pair — "every posting over
176
+ * 500" and "between 100 and 500" are the two questions asked of an amount
177
+ * column, and typing it "text" offered `contains` instead (MPO-252).
178
+ */
179
+ const MONEY_OPERATORS: FilterOperator[] = [
180
+ "is",
181
+ "isNot",
182
+ "isLessThan",
183
+ "isGreaterThan",
184
+ "isLessThanOrEqual",
185
+ "isGreaterThanOrEqual",
186
+ "isBetween",
187
+ "isNotBetween",
188
+ ];
189
+
170
190
  const DATE_OPERATORS: FilterOperator[] = [
171
191
  "is",
172
192
  "isNot",
@@ -185,6 +205,8 @@ export function operatorsForFilterType(type: DataTableColumnType): FilterOperato
185
205
  switch (type) {
186
206
  case "number":
187
207
  return NUMBER_OPERATORS;
208
+ case "money":
209
+ return MONEY_OPERATORS;
188
210
  case "date":
189
211
  return DATE_OPERATORS;
190
212
  case "boolean":
@@ -1,4 +1,5 @@
1
1
  import { parseCalendarDate, type DateInput } from "@multiplatform.one/theme";
2
+ import { isMoneyString, moneyComparator } from "./money";
2
3
  import { t } from "../shared/t";
3
4
  import type { FilterValue, TableFilter } from "../types";
4
5
 
@@ -51,10 +52,28 @@ export function deserializeFilters(filterString: string): TableFilter[] {
51
52
 
52
53
  // Filter Application Logic
53
54
 
55
+ /**
56
+ * Order a money cell against a money comparand (MPO-252).
57
+ *
58
+ * Money is a decimal STRING, so `Number()` would round it and `<` would
59
+ * compare it lexically — "9.00" above "10.00". Both sides go through the
60
+ * exact BigInt comparator. A comparand that is not a decimal string matches
61
+ * nothing, the way a NaN numeric comparison already did.
62
+ */
63
+ function compareMoneyValues(
64
+ value: unknown,
65
+ filterValue: unknown,
66
+ ordered: (delta: number) => boolean,
67
+ ): boolean {
68
+ const right = typeof filterValue === "number" ? String(filterValue) : filterValue;
69
+ if (!isMoneyString(value) || !isMoneyString(right)) return false;
70
+ return ordered(moneyComparator(value, right));
71
+ }
72
+
54
73
  export function applyFilterToValue(
55
74
  value: any,
56
75
  filterValue: FilterValue,
57
- columnType: "text" | "number" | "date" | "boolean" = "text",
76
+ columnType: "text" | "number" | "money" | "date" | "boolean" = "text",
58
77
  ): boolean {
59
78
  const { operator, value: filterVal, values } = filterValue;
60
79
 
@@ -83,6 +102,9 @@ export function applyFilterToValue(
83
102
 
84
103
  // Equality operators - handle different types
85
104
  case "is":
105
+ if (columnType === "money") {
106
+ return compareMoneyValues(value, filterVal, (delta) => delta === 0);
107
+ }
86
108
  if (columnType === "number") {
87
109
  return Number(value) === Number(filterVal);
88
110
  }
@@ -91,6 +113,9 @@ export function applyFilterToValue(
91
113
  }
92
114
  return stringValue === filterStringValue;
93
115
  case "isNot":
116
+ if (columnType === "money") {
117
+ return !compareMoneyValues(value, filterVal, (delta) => delta === 0);
118
+ }
94
119
  if (columnType === "number") {
95
120
  return Number(value) !== Number(filterVal);
96
121
  }
@@ -98,23 +123,41 @@ export function applyFilterToValue(
98
123
 
99
124
  // Number/Date Filters
100
125
  case "isLessThan":
126
+ if (columnType === "money") {
127
+ return compareMoneyValues(value, filterVal, (delta) => delta < 0);
128
+ }
101
129
  return columnType === "number"
102
130
  ? Number(value) < Number(filterVal)
103
131
  : stringValue < filterStringValue;
104
132
  case "isGreaterThan":
133
+ if (columnType === "money") {
134
+ return compareMoneyValues(value, filterVal, (delta) => delta > 0);
135
+ }
105
136
  return columnType === "number"
106
137
  ? Number(value) > Number(filterVal)
107
138
  : stringValue > filterStringValue;
108
139
  case "isLessThanOrEqual":
140
+ if (columnType === "money") {
141
+ return compareMoneyValues(value, filterVal, (delta) => delta <= 0);
142
+ }
109
143
  return columnType === "number"
110
144
  ? Number(value) <= Number(filterVal)
111
145
  : stringValue <= filterStringValue;
112
146
  case "isGreaterThanOrEqual":
147
+ if (columnType === "money") {
148
+ return compareMoneyValues(value, filterVal, (delta) => delta >= 0);
149
+ }
113
150
  return columnType === "number"
114
151
  ? Number(value) >= Number(filterVal)
115
152
  : stringValue >= filterStringValue;
116
153
  case "isBetween":
117
154
  if (values && values.length >= 2) {
155
+ if (columnType === "money") {
156
+ return (
157
+ compareMoneyValues(value, values[0], (delta) => delta >= 0) &&
158
+ compareMoneyValues(value, values[1], (delta) => delta <= 0)
159
+ );
160
+ }
118
161
  const numValue = Number(value);
119
162
  const min = Number(values[0]);
120
163
  const max = Number(values[1]);
@@ -123,6 +166,12 @@ export function applyFilterToValue(
123
166
  return false;
124
167
  case "isNotBetween":
125
168
  if (values && values.length >= 2) {
169
+ if (columnType === "money") {
170
+ return (
171
+ compareMoneyValues(value, values[0], (delta) => delta < 0) ||
172
+ compareMoneyValues(value, values[1], (delta) => delta > 0)
173
+ );
174
+ }
126
175
  const numValue = Number(value);
127
176
  const min = Number(values[0]);
128
177
  const max = Number(values[1]);