@multiplatform.one/table 7.11.0 → 7.16.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 (277) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/components/ChildTable/index.cjs +8 -2
  3. package/dist/cjs/components/ChildTable/index.native.js +8 -2
  4. package/dist/cjs/components/ChildTable/index.native.js.map +1 -1
  5. package/dist/cjs/components/ColumnHeaderFilter/index.cjs +77 -111
  6. package/dist/cjs/components/ColumnHeaderFilter/index.native.js +89 -129
  7. package/dist/cjs/components/ColumnHeaderFilter/index.native.js.map +1 -1
  8. package/dist/cjs/components/DataTable/DataTableRoot.cjs +17 -1
  9. package/dist/cjs/components/DataTable/DataTableRoot.native.js +25 -2
  10. package/dist/cjs/components/DataTable/DataTableRoot.native.js.map +1 -1
  11. package/dist/cjs/components/DataTableBulkActions/index.cjs +1 -1
  12. package/dist/cjs/components/DataTableBulkActions/index.native.js +1 -1
  13. package/dist/cjs/components/DataTableBulkActions/index.native.js.map +1 -1
  14. package/dist/cjs/components/DataTableCell/CellModalEditor.cjs +72 -70
  15. package/dist/cjs/components/DataTableCell/index.cjs +2 -2
  16. package/dist/cjs/components/DataTableCell/index.native.js +2 -11
  17. package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
  18. package/dist/cjs/components/DataTableDateFilter/index.cjs +135 -158
  19. package/dist/cjs/components/DataTableDateFilter/index.native.js +153 -183
  20. package/dist/cjs/components/DataTableDateFilter/index.native.js.map +1 -1
  21. package/dist/cjs/components/DataTableExport/index.cjs +122 -139
  22. package/dist/cjs/components/DataTableExport/index.native.js +135 -157
  23. package/dist/cjs/components/DataTableExport/index.native.js.map +1 -1
  24. package/dist/cjs/components/DataTableGrouping/index.cjs +192 -212
  25. package/dist/cjs/components/DataTableGrouping/index.native.js +209 -230
  26. package/dist/cjs/components/DataTableGrouping/index.native.js.map +1 -1
  27. package/dist/cjs/components/DataTableSearch/index.cjs +103 -115
  28. package/dist/cjs/components/DataTableSearch/index.native.js +111 -123
  29. package/dist/cjs/components/DataTableSearch/index.native.js.map +1 -1
  30. package/dist/cjs/components/DataTableToolbar/index.cjs +44 -61
  31. package/dist/cjs/components/DataTableToolbar/index.native.js +46 -65
  32. package/dist/cjs/components/DataTableToolbar/index.native.js.map +1 -1
  33. package/dist/cjs/components/DataTableViewOptions/index.cjs +38 -54
  34. package/dist/cjs/components/DataTableViewOptions/index.native.js +40 -56
  35. package/dist/cjs/components/DataTableViewOptions/index.native.js.map +1 -1
  36. package/dist/cjs/components/FilterOperatorValueEditor/index.cjs +3 -11
  37. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js +3 -11
  38. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  39. package/dist/cjs/components/SimpleTable/index.cjs +2 -6
  40. package/dist/cjs/components/SimpleTable/index.native.js +2 -6
  41. package/dist/cjs/components/SimpleTable/index.native.js.map +1 -1
  42. package/dist/cjs/components/TableInput/RowDetailEditor.cjs +190 -0
  43. package/dist/cjs/components/TableInput/RowDetailEditor.native.js +172 -0
  44. package/dist/cjs/components/TableInput/RowDetailEditor.native.js.map +1 -0
  45. package/dist/cjs/components/TableInput/index.cjs +42 -5
  46. package/dist/cjs/components/TableInput/index.native.js +64 -1
  47. package/dist/cjs/components/TableInput/index.native.js.map +1 -1
  48. package/dist/cjs/components/TableInput/shared.cjs +8 -0
  49. package/dist/cjs/components/TableInput/shared.native.js +10 -0
  50. package/dist/cjs/components/TableInput/shared.native.js.map +1 -1
  51. package/dist/cjs/hooks/useDataTable.cjs +11 -1
  52. package/dist/cjs/hooks/useDataTable.native.js +12 -2
  53. package/dist/cjs/hooks/useDataTable.native.js.map +1 -1
  54. package/dist/cjs/index.native.js.map +1 -1
  55. package/dist/cjs/utils/rowPress.cjs +4 -1
  56. package/dist/cjs/utils/rowPress.native.js +4 -1
  57. package/dist/cjs/utils/rowPress.native.js.map +1 -1
  58. package/dist/esm/components/ChildTable/index.mjs +8 -2
  59. package/dist/esm/components/ChildTable/index.mjs.map +1 -1
  60. package/dist/esm/components/ChildTable/index.native.js +8 -2
  61. package/dist/esm/components/ChildTable/index.native.js.map +1 -1
  62. package/dist/esm/components/ColumnHeaderFilter/index.mjs +80 -114
  63. package/dist/esm/components/ColumnHeaderFilter/index.mjs.map +1 -1
  64. package/dist/esm/components/ColumnHeaderFilter/index.native.js +92 -132
  65. package/dist/esm/components/ColumnHeaderFilter/index.native.js.map +1 -1
  66. package/dist/esm/components/DataTable/DataTableRoot.mjs +17 -1
  67. package/dist/esm/components/DataTable/DataTableRoot.mjs.map +1 -1
  68. package/dist/esm/components/DataTable/DataTableRoot.native.js +25 -2
  69. package/dist/esm/components/DataTable/DataTableRoot.native.js.map +1 -1
  70. package/dist/esm/components/DataTableBulkActions/index.mjs +1 -1
  71. package/dist/esm/components/DataTableBulkActions/index.mjs.map +1 -1
  72. package/dist/esm/components/DataTableBulkActions/index.native.js +1 -1
  73. package/dist/esm/components/DataTableBulkActions/index.native.js.map +1 -1
  74. package/dist/esm/components/DataTableCell/CellModalEditor.mjs +76 -74
  75. package/dist/esm/components/DataTableCell/CellModalEditor.mjs.map +1 -1
  76. package/dist/esm/components/DataTableCell/index.mjs +3 -3
  77. package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
  78. package/dist/esm/components/DataTableCell/index.native.js +3 -12
  79. package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
  80. package/dist/esm/components/DataTableDateFilter/index.mjs +137 -160
  81. package/dist/esm/components/DataTableDateFilter/index.mjs.map +1 -1
  82. package/dist/esm/components/DataTableDateFilter/index.native.js +155 -185
  83. package/dist/esm/components/DataTableDateFilter/index.native.js.map +1 -1
  84. package/dist/esm/components/DataTableExport/index.mjs +124 -141
  85. package/dist/esm/components/DataTableExport/index.mjs.map +1 -1
  86. package/dist/esm/components/DataTableExport/index.native.js +137 -159
  87. package/dist/esm/components/DataTableExport/index.native.js.map +1 -1
  88. package/dist/esm/components/DataTableGrouping/index.mjs +194 -214
  89. package/dist/esm/components/DataTableGrouping/index.mjs.map +1 -1
  90. package/dist/esm/components/DataTableGrouping/index.native.js +211 -232
  91. package/dist/esm/components/DataTableGrouping/index.native.js.map +1 -1
  92. package/dist/esm/components/DataTableSearch/index.mjs +105 -117
  93. package/dist/esm/components/DataTableSearch/index.mjs.map +1 -1
  94. package/dist/esm/components/DataTableSearch/index.native.js +113 -125
  95. package/dist/esm/components/DataTableSearch/index.native.js.map +1 -1
  96. package/dist/esm/components/DataTableToolbar/index.mjs +46 -63
  97. package/dist/esm/components/DataTableToolbar/index.mjs.map +1 -1
  98. package/dist/esm/components/DataTableToolbar/index.native.js +48 -67
  99. package/dist/esm/components/DataTableToolbar/index.native.js.map +1 -1
  100. package/dist/esm/components/DataTableViewOptions/index.mjs +40 -56
  101. package/dist/esm/components/DataTableViewOptions/index.mjs.map +1 -1
  102. package/dist/esm/components/DataTableViewOptions/index.native.js +42 -58
  103. package/dist/esm/components/DataTableViewOptions/index.native.js.map +1 -1
  104. package/dist/esm/components/FilterOperatorValueEditor/index.mjs +3 -11
  105. package/dist/esm/components/FilterOperatorValueEditor/index.mjs.map +1 -1
  106. package/dist/esm/components/FilterOperatorValueEditor/index.native.js +3 -11
  107. package/dist/esm/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  108. package/dist/esm/components/SimpleTable/index.mjs +3 -7
  109. package/dist/esm/components/SimpleTable/index.mjs.map +1 -1
  110. package/dist/esm/components/SimpleTable/index.native.js +3 -7
  111. package/dist/esm/components/SimpleTable/index.native.js.map +1 -1
  112. package/dist/esm/components/TableInput/RowDetailEditor.mjs +165 -0
  113. package/dist/esm/components/TableInput/RowDetailEditor.mjs.map +1 -0
  114. package/dist/esm/components/TableInput/RowDetailEditor.native.js +144 -0
  115. package/dist/esm/components/TableInput/RowDetailEditor.native.js.map +1 -0
  116. package/dist/esm/components/TableInput/index.mjs +42 -5
  117. package/dist/esm/components/TableInput/index.mjs.map +1 -1
  118. package/dist/esm/components/TableInput/index.native.js +64 -1
  119. package/dist/esm/components/TableInput/index.native.js.map +1 -1
  120. package/dist/esm/components/TableInput/shared.mjs +7 -1
  121. package/dist/esm/components/TableInput/shared.mjs.map +1 -1
  122. package/dist/esm/components/TableInput/shared.native.js +9 -1
  123. package/dist/esm/components/TableInput/shared.native.js.map +1 -1
  124. package/dist/esm/hooks/useDataTable.mjs +11 -1
  125. package/dist/esm/hooks/useDataTable.mjs.map +1 -1
  126. package/dist/esm/hooks/useDataTable.native.js +12 -2
  127. package/dist/esm/hooks/useDataTable.native.js.map +1 -1
  128. package/dist/esm/index.mjs.map +1 -1
  129. package/dist/esm/index.native.js.map +1 -1
  130. package/dist/esm/utils/rowPress.mjs +4 -1
  131. package/dist/esm/utils/rowPress.mjs.map +1 -1
  132. package/dist/esm/utils/rowPress.native.js +4 -1
  133. package/dist/esm/utils/rowPress.native.js.map +1 -1
  134. package/dist/jsx/components/ChildTable/index.mjs +8 -2
  135. package/dist/jsx/components/ChildTable/index.mjs.map +1 -1
  136. package/dist/jsx/components/ChildTable/index.native.js +8 -2
  137. package/dist/jsx/components/ChildTable/index.native.js.map +1 -1
  138. package/dist/jsx/components/ColumnHeaderFilter/index.mjs +80 -114
  139. package/dist/jsx/components/ColumnHeaderFilter/index.mjs.map +1 -1
  140. package/dist/jsx/components/ColumnHeaderFilter/index.native.js +89 -129
  141. package/dist/jsx/components/ColumnHeaderFilter/index.native.js.map +1 -1
  142. package/dist/jsx/components/DataTable/DataTableRoot.mjs +17 -1
  143. package/dist/jsx/components/DataTable/DataTableRoot.mjs.map +1 -1
  144. package/dist/jsx/components/DataTable/DataTableRoot.native.js +25 -2
  145. package/dist/jsx/components/DataTable/DataTableRoot.native.js.map +1 -1
  146. package/dist/jsx/components/DataTableBulkActions/index.mjs +1 -1
  147. package/dist/jsx/components/DataTableBulkActions/index.mjs.map +1 -1
  148. package/dist/jsx/components/DataTableBulkActions/index.native.js +1 -1
  149. package/dist/jsx/components/DataTableBulkActions/index.native.js.map +1 -1
  150. package/dist/jsx/components/DataTableCell/CellModalEditor.mjs +76 -74
  151. package/dist/jsx/components/DataTableCell/CellModalEditor.mjs.map +1 -1
  152. package/dist/jsx/components/DataTableCell/index.mjs +3 -3
  153. package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
  154. package/dist/jsx/components/DataTableCell/index.native.js +2 -11
  155. package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
  156. package/dist/jsx/components/DataTableDateFilter/index.mjs +137 -160
  157. package/dist/jsx/components/DataTableDateFilter/index.mjs.map +1 -1
  158. package/dist/jsx/components/DataTableDateFilter/index.native.js +153 -183
  159. package/dist/jsx/components/DataTableDateFilter/index.native.js.map +1 -1
  160. package/dist/jsx/components/DataTableExport/index.mjs +124 -141
  161. package/dist/jsx/components/DataTableExport/index.mjs.map +1 -1
  162. package/dist/jsx/components/DataTableExport/index.native.js +135 -157
  163. package/dist/jsx/components/DataTableExport/index.native.js.map +1 -1
  164. package/dist/jsx/components/DataTableGrouping/index.mjs +194 -214
  165. package/dist/jsx/components/DataTableGrouping/index.mjs.map +1 -1
  166. package/dist/jsx/components/DataTableGrouping/index.native.js +209 -230
  167. package/dist/jsx/components/DataTableGrouping/index.native.js.map +1 -1
  168. package/dist/jsx/components/DataTableSearch/index.mjs +105 -117
  169. package/dist/jsx/components/DataTableSearch/index.mjs.map +1 -1
  170. package/dist/jsx/components/DataTableSearch/index.native.js +111 -123
  171. package/dist/jsx/components/DataTableSearch/index.native.js.map +1 -1
  172. package/dist/jsx/components/DataTableToolbar/index.mjs +46 -63
  173. package/dist/jsx/components/DataTableToolbar/index.mjs.map +1 -1
  174. package/dist/jsx/components/DataTableToolbar/index.native.js +46 -65
  175. package/dist/jsx/components/DataTableToolbar/index.native.js.map +1 -1
  176. package/dist/jsx/components/DataTableViewOptions/index.mjs +40 -56
  177. package/dist/jsx/components/DataTableViewOptions/index.mjs.map +1 -1
  178. package/dist/jsx/components/DataTableViewOptions/index.native.js +40 -56
  179. package/dist/jsx/components/DataTableViewOptions/index.native.js.map +1 -1
  180. package/dist/jsx/components/FilterOperatorValueEditor/index.mjs +3 -11
  181. package/dist/jsx/components/FilterOperatorValueEditor/index.mjs.map +1 -1
  182. package/dist/jsx/components/FilterOperatorValueEditor/index.native.js +3 -11
  183. package/dist/jsx/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  184. package/dist/jsx/components/SimpleTable/index.mjs +3 -7
  185. package/dist/jsx/components/SimpleTable/index.mjs.map +1 -1
  186. package/dist/jsx/components/SimpleTable/index.native.js +2 -6
  187. package/dist/jsx/components/SimpleTable/index.native.js.map +1 -1
  188. package/dist/jsx/components/TableInput/RowDetailEditor.mjs +165 -0
  189. package/dist/jsx/components/TableInput/RowDetailEditor.mjs.map +1 -0
  190. package/dist/jsx/components/TableInput/RowDetailEditor.native.js +172 -0
  191. package/dist/jsx/components/TableInput/RowDetailEditor.native.js.map +1 -0
  192. package/dist/jsx/components/TableInput/index.mjs +42 -5
  193. package/dist/jsx/components/TableInput/index.mjs.map +1 -1
  194. package/dist/jsx/components/TableInput/index.native.js +64 -1
  195. package/dist/jsx/components/TableInput/index.native.js.map +1 -1
  196. package/dist/jsx/components/TableInput/shared.mjs +7 -1
  197. package/dist/jsx/components/TableInput/shared.mjs.map +1 -1
  198. package/dist/jsx/components/TableInput/shared.native.js +10 -0
  199. package/dist/jsx/components/TableInput/shared.native.js.map +1 -1
  200. package/dist/jsx/hooks/useDataTable.mjs +11 -1
  201. package/dist/jsx/hooks/useDataTable.mjs.map +1 -1
  202. package/dist/jsx/hooks/useDataTable.native.js +12 -2
  203. package/dist/jsx/hooks/useDataTable.native.js.map +1 -1
  204. package/dist/jsx/index.js.map +1 -1
  205. package/dist/jsx/index.mjs.map +1 -1
  206. package/dist/jsx/index.native.js.map +1 -1
  207. package/dist/jsx/utils/rowPress.mjs +4 -1
  208. package/dist/jsx/utils/rowPress.mjs.map +1 -1
  209. package/dist/jsx/utils/rowPress.native.js +4 -1
  210. package/dist/jsx/utils/rowPress.native.js.map +1 -1
  211. package/package.json +14 -14
  212. package/src/components/ChildTable/childTable.cellHidden.spec.tsx +57 -0
  213. package/src/components/ChildTable/childTable.rowDetail.spec.tsx +120 -0
  214. package/src/components/ChildTable/index.tsx +16 -0
  215. package/src/components/ColumnHeaderFilter/index.tsx +89 -116
  216. package/src/components/DataTable/DataTable.stories.tsx +71 -6
  217. package/src/components/DataTable/DataTableRoot.tsx +27 -4
  218. package/src/components/DataTable/dataTableMobileCards.spec.tsx +1 -4
  219. package/src/components/DataTable/headerContainment.spec.tsx +91 -0
  220. package/src/components/DataTable/rowPress.spec.tsx +2 -6
  221. package/src/components/DataTableBulkActions/DataTableBulkActions.stories.tsx +5 -4
  222. package/src/components/DataTableBulkActions/index.tsx +1 -1
  223. package/src/components/DataTableCell/CellModalEditor.tsx +95 -75
  224. package/src/components/DataTableCell/cellDraftPolicy.spec.tsx +12 -2
  225. package/src/components/DataTableCell/cellModalEditor.spec.tsx +75 -49
  226. package/src/components/DataTableCell/dataTableCell.spec.tsx +31 -14
  227. package/src/components/DataTableCell/index.tsx +3 -2
  228. package/src/components/DataTableDateFilter/DataTableDateFilter.spec.tsx +6 -3
  229. package/src/components/DataTableDateFilter/index.tsx +151 -180
  230. package/src/components/DataTableExport/index.tsx +135 -142
  231. package/src/components/DataTableGrouping/index.tsx +193 -195
  232. package/src/components/DataTableSearch/index.tsx +111 -109
  233. package/src/components/DataTableToolbar/index.tsx +39 -46
  234. package/src/components/DataTableViewOptions/DataTableViewOptions.stories.tsx +52 -0
  235. package/src/components/DataTableViewOptions/index.tsx +32 -40
  236. package/src/components/FilterOperatorValueEditor/FilterOperatorValueEditor.spec.tsx +64 -2
  237. package/src/components/FilterOperatorValueEditor/index.tsx +0 -8
  238. package/src/components/SimpleTable/index.tsx +3 -2
  239. package/src/components/SimpleTable/simpleTable.spec.tsx +15 -0
  240. package/src/components/Table/accessibility.spec.tsx +7 -19
  241. package/src/components/TableInput/RowDetailEditor.native.tsx +134 -0
  242. package/src/components/TableInput/RowDetailEditor.tsx +173 -0
  243. package/src/components/TableInput/index.native.tsx +49 -1
  244. package/src/components/TableInput/index.tsx +65 -10
  245. package/src/components/TableInput/shared.ts +29 -0
  246. package/src/components/tableDropdowns.spec.tsx +172 -0
  247. package/src/hooks/useDataTable.spec.ts +39 -0
  248. package/src/hooks/useDataTable.ts +15 -1
  249. package/src/index.ts +1 -0
  250. package/src/utils/rowPress.ts +3 -0
  251. package/types/components/ChildTable/index.d.ts +10 -0
  252. package/types/components/ChildTable/index.d.ts.map +1 -1
  253. package/types/components/ColumnHeaderFilter/index.d.ts +1 -1
  254. package/types/components/ColumnHeaderFilter/index.d.ts.map +1 -1
  255. package/types/components/DataTable/DataTableRoot.d.ts.map +1 -1
  256. package/types/components/DataTableCell/CellModalEditor.d.ts +10 -7
  257. package/types/components/DataTableCell/CellModalEditor.d.ts.map +1 -1
  258. package/types/components/DataTableCell/index.d.ts.map +1 -1
  259. package/types/components/DataTableDateFilter/index.d.ts.map +1 -1
  260. package/types/components/DataTableGrouping/index.d.ts.map +1 -1
  261. package/types/components/DataTableSearch/index.d.ts.map +1 -1
  262. package/types/components/FilterOperatorValueEditor/index.d.ts.map +1 -1
  263. package/types/components/SimpleTable/index.d.ts.map +1 -1
  264. package/types/components/TableInput/RowDetailEditor.d.ts +24 -0
  265. package/types/components/TableInput/RowDetailEditor.d.ts.map +1 -0
  266. package/types/components/TableInput/RowDetailEditor.native.d.ts +11 -0
  267. package/types/components/TableInput/RowDetailEditor.native.d.ts.map +1 -0
  268. package/types/components/TableInput/index.d.ts +11 -2
  269. package/types/components/TableInput/index.d.ts.map +1 -1
  270. package/types/components/TableInput/index.native.d.ts +8 -1
  271. package/types/components/TableInput/index.native.d.ts.map +1 -1
  272. package/types/components/TableInput/shared.d.ts +11 -0
  273. package/types/components/TableInput/shared.d.ts.map +1 -1
  274. package/types/hooks/useDataTable.d.ts.map +1 -1
  275. package/types/index.d.ts +1 -0
  276. package/types/index.d.ts.map +1 -1
  277. package/types/utils/rowPress.d.ts.map +1 -1
@@ -2,9 +2,14 @@ import { SlidersHorizontalIcon } from "@phosphor-icons/react";
2
2
  import { sizeRecipeForToken, useResolvedKnobs } from "@multiplatform.one/theme";
3
3
  import type React from "react";
4
4
  import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
5
- import { PopoverContent } from "@multiplatform.one/components";
6
- import { Button, Checkbox, SearchInput, formCommonColors } from "@multiplatform.one/forms";
7
- import { Popover, ScrollView, Separator, Text, View, XStack, YStack, isWeb } from "tamagui";
5
+ import {
6
+ Button,
7
+ Checkbox,
8
+ FloatingPanel,
9
+ SearchInput,
10
+ formCommonColors,
11
+ } from "@multiplatform.one/forms";
12
+ import { Separator, Text, View, XStack, YStack, isWeb } from "tamagui";
8
13
  import { useDataTableState } from "../DataTable/DataTableStateContext";
9
14
  import { t } from "../../shared/t";
10
15
  import { bidiIsolate } from "../../utils/withInterp";
@@ -165,7 +170,7 @@ export function DataTableSearch<T = any>(props: DataTableSearchProps<T>) {
165
170
  </View>
166
171
 
167
172
  {showAdvancedOptions && (
168
- <SearchOptionsPopover
173
+ <SearchOptionsPanel
169
174
  searchOptions={searchOptions}
170
175
  onOptionsChange={handleOptionsChange}
171
176
  columns={searchableColumns}
@@ -201,7 +206,7 @@ export function DataTableSearch<T = any>(props: DataTableSearchProps<T>) {
201
206
  );
202
207
  }
203
208
 
204
- interface SearchOptionsPopoverProps {
209
+ interface SearchOptionsPanelProps {
205
210
  searchOptions: SearchOptions;
206
211
  onOptionsChange: (options: Partial<SearchOptions>) => void;
207
212
  columns: Array<{
@@ -217,7 +222,7 @@ interface SearchOptionsPopoverProps {
217
222
  optionsActive: boolean;
218
223
  }
219
224
 
220
- function SearchOptionsPopover({
225
+ function SearchOptionsPanel({
221
226
  searchOptions,
222
227
  onOptionsChange,
223
228
  columns,
@@ -226,7 +231,7 @@ function SearchOptionsPopover({
226
231
  disabled,
227
232
  iconPx,
228
233
  optionsActive,
229
- }: SearchOptionsPopoverProps) {
234
+ }: SearchOptionsPanelProps) {
230
235
  const { knobProps } = useResolvedKnobs();
231
236
  const [open, setOpen] = useState(false);
232
237
 
@@ -245,113 +250,110 @@ function SearchOptionsPopover({
245
250
  onOptionsChange({ columns: [] });
246
251
  };
247
252
 
248
- const sz = knobProps.sizeToken;
249
- const panelMaxHeight = sz === "$3" ? 320 : sz === "$5" ? 480 : 400;
253
+ const trigger = (
254
+ <Button
255
+ size={knobProps.sizeToken}
256
+ accent={optionsActive}
257
+ icon={<SlidersHorizontalIcon size={iconPx} weight={optionsActive ? "fill" : "regular"} />}
258
+ disabled={disabled}
259
+ aria-label={t("Search options")}
260
+ aria-expanded={open}
261
+ aria-haspopup="dialog"
262
+ />
263
+ );
250
264
 
251
265
  return (
252
- <Popover open={open} onOpenChange={setOpen} stayInFrame allowFlip resize>
253
- <Popover.Trigger asChild>
254
- <Button
255
- size={knobProps.sizeToken}
256
- accent={optionsActive}
257
- icon={<SlidersHorizontalIcon size={iconPx} weight={optionsActive ? "fill" : "regular"} />}
258
- disabled={disabled}
259
- aria-label={t("Search options")}
260
- aria-expanded={open}
261
- aria-haspopup="dialog"
262
- />
263
- </Popover.Trigger>
266
+ <FloatingPanel
267
+ open={open}
268
+ onOpenChange={setOpen}
269
+ trigger={trigger}
270
+ triggerSizing="content"
271
+ widthMode="fit-content"
272
+ disabled={disabled}
273
+ scrollable
274
+ >
275
+ <YStack {...knobProps.gapLg} minWidth={280} data-dismiss-class="compose">
276
+ <Text {...knobProps.heading} {...knobProps.label} fontWeight="400">
277
+ {t("Search Options")}
278
+ </Text>
264
279
 
265
- <PopoverContent padding={0} overflow="hidden" alignItems="stretch" minWidth={280}>
266
- <ScrollView flexShrink={1} maxHeight={panelMaxHeight}>
267
- <YStack {...knobProps.panelPadding} {...knobProps.gapLg}>
268
- <Text {...knobProps.heading} {...knobProps.label} fontWeight="400">
269
- {t("Search Options")}
280
+ <YStack {...knobProps.gap}>
281
+ <Text {...knobProps.label} fontWeight="400">
282
+ {t("Search Settings")}
283
+ </Text>
284
+
285
+ <Checkbox
286
+ compact
287
+ label={t("Case sensitive")}
288
+ checked={searchOptions.caseSensitive}
289
+ onCheckedChange={(checked) => onOptionsChange({ caseSensitive: checked as boolean })}
290
+ />
291
+
292
+ <Checkbox
293
+ compact
294
+ label={t("Whole word only")}
295
+ checked={searchOptions.wholeWord}
296
+ onCheckedChange={(checked) => onOptionsChange({ wholeWord: checked as boolean })}
297
+ />
298
+
299
+ <Checkbox
300
+ compact
301
+ label={t("Use regex")}
302
+ checked={searchOptions.regex}
303
+ onCheckedChange={(checked) => onOptionsChange({ regex: checked as boolean })}
304
+ />
305
+
306
+ <Checkbox
307
+ compact
308
+ label={t("Highlight results")}
309
+ checked={searchOptions.highlightResults}
310
+ onCheckedChange={(checked) => onOptionsChange({ highlightResults: checked as boolean })}
311
+ />
312
+ </YStack>
313
+
314
+ <Separator />
315
+
316
+ <YStack {...knobProps.gap}>
317
+ <XStack justifyContent="space-between" alignItems="center">
318
+ <Text {...knobProps.label} fontWeight="400">
319
+ {t("Search Columns")}
270
320
  </Text>
271
-
272
- <YStack {...knobProps.gap}>
273
- <Text {...knobProps.label} fontWeight="400">
274
- {t("Search Settings")}
275
- </Text>
276
-
277
- <Checkbox
278
- compact
279
- label={t("Case sensitive")}
280
- checked={searchOptions.caseSensitive}
281
- onCheckedChange={(checked) =>
282
- onOptionsChange({ caseSensitive: checked as boolean })
283
- }
284
- />
285
-
286
- <Checkbox
287
- compact
288
- label={t("Whole word only")}
289
- checked={searchOptions.wholeWord}
290
- onCheckedChange={(checked) => onOptionsChange({ wholeWord: checked as boolean })}
291
- />
292
-
293
- <Checkbox
294
- compact
295
- label={t("Use regex")}
296
- checked={searchOptions.regex}
297
- onCheckedChange={(checked) => onOptionsChange({ regex: checked as boolean })}
298
- />
299
-
300
- <Checkbox
301
- compact
302
- label={t("Highlight results")}
303
- checked={searchOptions.highlightResults}
304
- onCheckedChange={(checked) =>
305
- onOptionsChange({ highlightResults: checked as boolean })
306
- }
307
- />
308
- </YStack>
309
-
310
- <Separator />
311
-
312
- <YStack {...knobProps.gap}>
313
- <XStack justifyContent="space-between" alignItems="center">
314
- <Text {...knobProps.label} fontWeight="400">
315
- {t("Search Columns")}
316
- </Text>
317
- <Text {...knobProps.label} color={formCommonColors.muted}>
318
- {bidiIsolate(
319
- t("{{selected}} of {{total}}", {
320
- selected: selectedColumnCount,
321
- total: totalColumnCount,
322
- }),
323
- )}
324
- </Text>
325
- </XStack>
326
-
327
- <XStack {...knobProps.gap}>
328
- <Button size="$2" compact onPress={handleSelectAllColumns}>
329
- {t("All")}
330
- </Button>
331
- <Button size="$2" compact onPress={handleSelectNoColumns}>
332
- {t("None")}
333
- </Button>
334
- </XStack>
335
-
336
- <YStack gap="$1">
337
- {columns.map((col) => {
338
- const key = col.accessorKey || col.id || "";
339
- return (
340
- <Checkbox
341
- key={key}
342
- compact
343
- label={col.header || key}
344
- checked={searchOptions.columns.includes(key)}
345
- onCheckedChange={(checked) => handleColumnToggle(key, checked as boolean)}
346
- />
347
- );
348
- })}
349
- </YStack>
350
- </YStack>
321
+ <Text {...knobProps.label} color={formCommonColors.muted}>
322
+ {bidiIsolate(
323
+ t("{{selected}} of {{total}}", {
324
+ selected: selectedColumnCount,
325
+ total: totalColumnCount,
326
+ }),
327
+ )}
328
+ </Text>
329
+ </XStack>
330
+
331
+ <XStack {...knobProps.gap}>
332
+ <Button size="$2" compact onPress={handleSelectAllColumns}>
333
+ {t("All")}
334
+ </Button>
335
+ <Button size="$2" compact onPress={handleSelectNoColumns}>
336
+ {t("None")}
337
+ </Button>
338
+ </XStack>
339
+
340
+ <YStack gap="$1">
341
+ {columns.map((col) => {
342
+ const key = col.accessorKey || col.id || "";
343
+ return (
344
+ <Checkbox
345
+ key={key}
346
+ compact
347
+ label={col.header || key}
348
+ checked={searchOptions.columns.includes(key)}
349
+ onCheckedChange={(checked) => handleColumnToggle(key, checked as boolean)}
350
+ />
351
+ );
352
+ })}
351
353
  </YStack>
352
- </ScrollView>
353
- </PopoverContent>
354
- </Popover>
354
+ </YStack>
355
+ </YStack>
356
+ </FloatingPanel>
355
357
  );
356
358
  }
357
359
 
@@ -1,11 +1,11 @@
1
1
  import { CaretDownIcon, FloppyDiskIcon } from "@phosphor-icons/react";
2
2
  import { DropdownMenu } from "@multiplatform.one/components";
3
- import { Button, Input, SearchInput, Spinner } from "@multiplatform.one/forms";
3
+ import { Button, FloatingPanel, Input, SearchInput, Spinner } from "@multiplatform.one/forms";
4
4
  import { hairline, useResolvedKnobs } from "@multiplatform.one/theme";
5
5
  import { RovingFocusGroup } from "@tamagui/roving-focus";
6
6
  import type React from "react";
7
7
  import { useState } from "react";
8
- import { Popover, XStack, YStack } from "tamagui";
8
+ import { XStack, YStack } from "tamagui";
9
9
  import { t } from "../../shared/t";
10
10
  import type { TableView } from "../../types";
11
11
  import { iconSizeForToken } from "../../utils/chromeIcons";
@@ -158,8 +158,12 @@ function ToolbarLayout({
158
158
  viewPickerContent
159
159
  );
160
160
  const saveView = showSave && (
161
- <Popover open={showSaveViewDialog} onOpenChange={setShowSaveViewDialog}>
162
- <Popover.Trigger asChild>
161
+ <FloatingPanel
162
+ open={showSaveViewDialog}
163
+ onOpenChange={setShowSaveViewDialog}
164
+ triggerSizing="content"
165
+ widthMode="fit-content"
166
+ trigger={
163
167
  <RovingFocusGroup.Item focusable asChild>
164
168
  <Button
165
169
  chromeless
@@ -168,48 +172,37 @@ function ToolbarLayout({
168
172
  aria-label={t("Save view")}
169
173
  />
170
174
  </RovingFocusGroup.Item>
171
- </Popover.Trigger>
172
- <Popover.Content
173
- {...knobProps.elevatedSurface}
174
- {...knobProps.borderRadius}
175
- borderColor="$color6"
176
- // LC-24: enter/exitStyle need a `transition` driver on the
177
- // same element ({...string} spread set nothing).
178
- transition={knobProps.transition}
179
- enterStyle={{ x: 0, y: -10, opacity: 0 }}
180
- exitStyle={{ x: 0, y: -10, opacity: 0 }}
181
- data-dismiss-class="compose"
182
- >
183
- <YStack {...knobProps.gap} {...knobProps.panelPadding} minWidth={240}>
184
- <Input
185
- placeholder={t("View name")}
186
- value={newViewName}
187
- onChangeText={setNewViewName}
188
- size={searchSize}
189
- {...(compact === undefined ? {} : { compact })}
190
- inputProps={{
191
- "aria-label": t("View name"),
192
- onKeyDown: (e: React.KeyboardEvent) => {
193
- if (e.key === "Enter") handleSaveView();
194
- },
195
- }}
196
- />
197
- <XStack {...knobProps.gap} justifyContent="flex-end">
198
- <Button size={controlSize} onPress={() => setShowSaveViewDialog(false)}>
199
- {t("Cancel")}
200
- </Button>
201
- <Button
202
- size={controlSize}
203
- onPress={handleSaveView}
204
- disabled={!newViewName.trim()}
205
- disabledReason={t("Enter a view name")}
206
- >
207
- {t("Save")}
208
- </Button>
209
- </XStack>
210
- </YStack>
211
- </Popover.Content>
212
- </Popover>
175
+ }
176
+ >
177
+ <YStack {...knobProps.gap} minWidth={240} data-dismiss-class="compose">
178
+ <Input
179
+ placeholder={t("View name")}
180
+ value={newViewName}
181
+ onChangeText={setNewViewName}
182
+ size={searchSize}
183
+ {...(compact === undefined ? {} : { compact })}
184
+ inputProps={{
185
+ "aria-label": t("View name"),
186
+ onKeyDown: (e: React.KeyboardEvent) => {
187
+ if (e.key === "Enter") handleSaveView();
188
+ },
189
+ }}
190
+ />
191
+ <XStack {...knobProps.gap} justifyContent="flex-end">
192
+ <Button size={controlSize} onPress={() => setShowSaveViewDialog(false)}>
193
+ {t("Cancel")}
194
+ </Button>
195
+ <Button
196
+ size={controlSize}
197
+ onPress={handleSaveView}
198
+ disabled={!newViewName.trim()}
199
+ disabledReason={t("Enter a view name")}
200
+ >
201
+ {t("Save")}
202
+ </Button>
203
+ </XStack>
204
+ </YStack>
205
+ </FloatingPanel>
213
206
  );
214
207
  const showSecondary = showViewPicker || showSave || enableExport || isFetching;
215
208
 
@@ -2,6 +2,7 @@ import { action } from "@multiplatform.one/storybook";
2
2
  import React from "react";
3
3
  import { formatAbsoluteDate } from "@multiplatform.one/theme";
4
4
  import { Card, Text, View, XStack, YStack } from "tamagui";
5
+ import type { TableView } from "../../types";
5
6
  import { DataTableViewOptions } from "./index";
6
7
 
7
8
  // Columns definition
@@ -420,3 +421,54 @@ export const AllOptionsCombined = () => {
420
421
  </YStack>
421
422
  );
422
423
  };
424
+
425
+ const savedViews: TableView[] = [
426
+ {
427
+ id: "all",
428
+ name: "All products",
429
+ columns: {},
430
+ filters: [],
431
+ sorting: [],
432
+ pagination: { pageIndex: 0, pageSize: 10 },
433
+ },
434
+ {
435
+ id: "in-stock",
436
+ name: "In stock",
437
+ columns: {},
438
+ filters: [],
439
+ sorting: [],
440
+ pagination: { pageIndex: 0, pageSize: 10 },
441
+ },
442
+ ];
443
+
444
+ export const WithSavedViews = () => {
445
+ const [views, setViews] = React.useState<TableView[]>(savedViews);
446
+ const [activeViewId, setActiveViewId] = React.useState("all");
447
+
448
+ return (
449
+ <YStack gap="$4" padding="$4">
450
+ <Text fontSize="$4" fontWeight="600">
451
+ Saved Views
452
+ </Text>
453
+
454
+ <DataTableViewOptions
455
+ columns={transformedColumns}
456
+ onColumnVisibilityChange={() => {}}
457
+ views={views}
458
+ activeViewId={activeViewId}
459
+ showViewManagement
460
+ onSaveView={(name) => {
461
+ const id = name.toLowerCase().replace(/\s+/g, "-");
462
+ setViews((prev) => [...prev, { ...savedViews[0], id, name }]);
463
+ setActiveViewId(id);
464
+ action("save-view")(name);
465
+ }}
466
+ onLoadView={(viewId) => {
467
+ setActiveViewId(viewId);
468
+ action("load-view")(viewId);
469
+ }}
470
+ onResetToDefault={() => {}}
471
+ />
472
+ </YStack>
473
+ );
474
+ };
@@ -7,9 +7,9 @@ import {
7
7
  } from "@phosphor-icons/react";
8
8
  import { getGroupPosition, stackRadiusProps, useResolvedKnobs } from "@multiplatform.one/theme";
9
9
  import React, { useState } from "react";
10
- import { Button, Input, Slider, Switch } from "@multiplatform.one/forms";
10
+ import { Button, FloatingPanel, Input, Slider, Switch } from "@multiplatform.one/forms";
11
11
  import { DropdownMenu } from "@multiplatform.one/components";
12
- import { Popover, ScrollView, Separator, Text, View, XStack, YStack, useTheme } from "tamagui";
12
+ import { ScrollView, Separator, Text, View, XStack, YStack, useTheme } from "tamagui";
13
13
  import type { TableView } from "../../types";
14
14
  import { t } from "../../shared/t";
15
15
  import { iconSizeForToken } from "../../utils/chromeIcons";
@@ -415,47 +415,39 @@ function ViewManagementSection({
415
415
  </Text>
416
416
 
417
417
  {/* Save Current View */}
418
- <Popover open={showSaveDialog} onOpenChange={setShowSaveDialog}>
419
- <Popover.Trigger asChild>
418
+ <FloatingPanel
419
+ open={showSaveDialog}
420
+ onOpenChange={setShowSaveDialog}
421
+ trigger={
420
422
  <Button size={knobProps.sizeToken} icon={<FloppyDiskIcon size={iconPx} />}>
421
423
  {t("Save Current View")}
422
424
  </Button>
423
- </Popover.Trigger>
424
- <Popover.Content
425
- {...knobProps.elevatedSurface}
426
- enterStyle={{ x: 0, y: -10, opacity: 0 }}
427
- exitStyle={{ x: 0, y: -10, opacity: 0 }}
428
- transition={knobProps.transition}
429
- >
430
- <YStack {...knobProps.panelPadding} gap="$3" minWidth={250}>
431
- <Text {...knobProps.label} fontWeight="400">
432
- {t("Save View")}
433
- </Text>
434
- <Input
435
- placeholder={t("Enter view name...")}
436
- value={viewName}
437
- onChangeText={setViewName}
438
- inputProps={{
439
- onKeyDown: (e: React.KeyboardEvent) => {
440
- if (e.key === "Enter" && viewName.trim()) handleSaveView();
441
- },
442
- }}
443
- />
444
- <XStack gap="$2" justifyContent="flex-end">
445
- <Button size={knobProps.sizeToken} onPress={() => setShowSaveDialog(false)}>
446
- {t("Cancel")}
447
- </Button>
448
- <Button
449
- size={knobProps.sizeToken}
450
- onPress={handleSaveView}
451
- disabled={!viewName.trim()}
452
- >
453
- {t("Save")}
454
- </Button>
455
- </XStack>
456
- </YStack>
457
- </Popover.Content>
458
- </Popover>
425
+ }
426
+ >
427
+ <YStack gap="$3" minWidth={250} data-dismiss-class="compose">
428
+ <Text {...knobProps.label} fontWeight="400">
429
+ {t("Save View")}
430
+ </Text>
431
+ <Input
432
+ placeholder={t("Enter view name...")}
433
+ value={viewName}
434
+ onChangeText={setViewName}
435
+ inputProps={{
436
+ onKeyDown: (e: React.KeyboardEvent) => {
437
+ if (e.key === "Enter" && viewName.trim()) handleSaveView();
438
+ },
439
+ }}
440
+ />
441
+ <XStack gap="$2" justifyContent="flex-end">
442
+ <Button size={knobProps.sizeToken} onPress={() => setShowSaveDialog(false)}>
443
+ {t("Cancel")}
444
+ </Button>
445
+ <Button size={knobProps.sizeToken} onPress={handleSaveView} disabled={!viewName.trim()}>
446
+ {t("Save")}
447
+ </Button>
448
+ </XStack>
449
+ </YStack>
450
+ </FloatingPanel>
459
451
 
460
452
  {/* Saved Views List */}
461
453
  {views.length > 0 && (
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { fireEvent, screen, waitFor } from "@testing-library/react";
9
9
  import { renderWithProviders } from "@multiplatform.one/test-utils";
10
+ import { type Knobs, Preset } from "@multiplatform.one/theme";
10
11
  import React, { useState } from "react";
11
12
  import { describe, expect, it, vi } from "vitest";
12
13
  import type { DataTableColumnType, FilterOperator } from "../../types";
@@ -28,14 +29,16 @@ const selectTriggers = (container: HTMLElement) =>
28
29
  const textInputs = (container: HTMLElement) =>
29
30
  Array.from(container.querySelectorAll("input")) as HTMLInputElement[];
30
31
 
32
+ /** The listbox portals out to the page (MPO-335, LC-90), so options are read from it. */
31
33
  async function openSelect(container: HTMLElement, trigger: HTMLElement) {
34
+ const page = container.ownerDocument.body;
32
35
  fireEvent.pointerDown(trigger);
33
36
  fireEvent.mouseDown(trigger);
34
37
  fireEvent.click(trigger);
35
38
  await waitFor(() => {
36
- expect(container.querySelectorAll('[role="option"]').length).toBeGreaterThan(0);
39
+ expect(page.querySelectorAll('[role="option"]').length).toBeGreaterThan(0);
37
40
  });
38
- return Array.from(container.querySelectorAll('[role="option"]')) as HTMLElement[];
41
+ return Array.from(page.querySelectorAll('[role="option"]')) as HTMLElement[];
39
42
  }
40
43
 
41
44
  /**
@@ -258,4 +261,63 @@ describe("FilterOperatorValueEditor", () => {
258
261
  await waitFor(() => expect(onValue).toHaveBeenLastCalledWith("in-progress"));
259
262
  });
260
263
  });
264
+ describe("knob laws (MPO-118)", () => {
265
+ const valueBox = (container: HTMLElement) =>
266
+ container.querySelector("[data-mp-input-field='true']") as HTMLElement;
267
+
268
+ const padX = (el: Element) =>
269
+ String((el as HTMLElement).className || "")
270
+ .split(" ")
271
+ .filter((c) => c.startsWith("_px-") || c.startsWith("_pl-") || c.startsWith("_pr-"))
272
+ .sort();
273
+
274
+ function renderAtKnobs(overrides: Partial<Knobs>) {
275
+ return renderWithProviders(
276
+ <Preset overrides={overrides}>
277
+ <FilterOperatorValueEditor
278
+ fieldType="text"
279
+ operator="contains"
280
+ value=""
281
+ onOperatorChange={vi.fn()}
282
+ onValueChange={vi.fn()}
283
+ />
284
+ </Preset>,
285
+ );
286
+ }
287
+
288
+ it("size small: Condition and Value both paint the small recipe", () => {
289
+ const { container } = renderAtKnobs({ size: "small" });
290
+ const box = valueBox(container);
291
+ expect(box.getAttribute("data-size")).toBe("$3");
292
+ expect(box.getAttribute("data-visual-height")).toBe("36");
293
+ expect(selectTriggers(container)[0].getAttribute("data-visual-height")).toBe("36");
294
+ });
295
+
296
+ it("size large: the Value input follows the knob instead of a pinned $3", () => {
297
+ const { container } = renderAtKnobs({ size: "large" });
298
+ const box = valueBox(container);
299
+ expect(box.getAttribute("data-size")).toBe("$5");
300
+ expect(box.getAttribute("data-visual-height")).toBe("52");
301
+ expect(selectTriggers(container)[0].getAttribute("data-visual-height")).toBe("52");
302
+ });
303
+
304
+ it("density comfortable: compact is not forced on the inner fields", () => {
305
+ const { container } = renderAtKnobs({ density: "comfortable" });
306
+ expect(valueBox(container).getAttribute("data-density")).toBe("comfortable");
307
+ });
308
+
309
+ it("density compact: the inner fields step with the density knob and keep their height", () => {
310
+ const comfortable = renderAtKnobs({ density: "comfortable", size: "medium" });
311
+ const comfortablePad = padX(selectTriggers(comfortable.container)[0]);
312
+ const comfortableHeight = valueBox(comfortable.container).getAttribute("data-visual-height");
313
+ comfortable.unmount();
314
+
315
+ const { container } = renderAtKnobs({ density: "compact", size: "medium" });
316
+ const box = valueBox(container);
317
+ expect(box.getAttribute("data-density")).toBe("compact");
318
+ expect(box.getAttribute("data-visual-height")).toBe(comfortableHeight);
319
+ expect(comfortablePad.length).toBeGreaterThan(0);
320
+ expect(padX(selectTriggers(container)[0])).not.toEqual(comfortablePad);
321
+ });
322
+ });
261
323
  });
@@ -72,8 +72,6 @@ export function FilterOperatorValueEditor({
72
72
  }
73
73
  }}
74
74
  options={operatorOptions}
75
- size="$3"
76
- compact
77
75
  />
78
76
 
79
77
  {needsValueInput && (
@@ -89,8 +87,6 @@ export function FilterOperatorValueEditor({
89
87
  }}
90
88
  options={booleanOptions}
91
89
  placeholder={t("Select...")}
92
- size="$3"
93
- compact
94
90
  />
95
91
  ) : fieldType === "select" && options ? (
96
92
  <Select
@@ -103,8 +99,6 @@ export function FilterOperatorValueEditor({
103
99
  }}
104
100
  options={selectOptions}
105
101
  placeholder={t("Select...")}
106
- size="$3"
107
- compact
108
102
  />
109
103
  ) : (
110
104
  <Input
@@ -118,8 +112,6 @@ export function FilterOperatorValueEditor({
118
112
  ? t("0.00")
119
113
  : t("Enter value...")
120
114
  }
121
- size="$3"
122
- compact
123
115
  {...((fieldType === "number" || fieldType === "money") && {
124
116
  keyboardType: "numeric",
125
117
  })}