@multiplatform.one/table 7.11.0 → 7.15.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 (265) 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/DataTableCell/CellModalEditor.cjs +72 -70
  12. package/dist/cjs/components/DataTableCell/index.cjs +2 -2
  13. package/dist/cjs/components/DataTableCell/index.native.js +2 -11
  14. package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
  15. package/dist/cjs/components/DataTableDateFilter/index.cjs +135 -158
  16. package/dist/cjs/components/DataTableDateFilter/index.native.js +153 -183
  17. package/dist/cjs/components/DataTableDateFilter/index.native.js.map +1 -1
  18. package/dist/cjs/components/DataTableExport/index.cjs +122 -139
  19. package/dist/cjs/components/DataTableExport/index.native.js +135 -157
  20. package/dist/cjs/components/DataTableExport/index.native.js.map +1 -1
  21. package/dist/cjs/components/DataTableGrouping/index.cjs +192 -212
  22. package/dist/cjs/components/DataTableGrouping/index.native.js +209 -230
  23. package/dist/cjs/components/DataTableGrouping/index.native.js.map +1 -1
  24. package/dist/cjs/components/DataTableSearch/index.cjs +103 -115
  25. package/dist/cjs/components/DataTableSearch/index.native.js +111 -123
  26. package/dist/cjs/components/DataTableSearch/index.native.js.map +1 -1
  27. package/dist/cjs/components/DataTableToolbar/index.cjs +44 -61
  28. package/dist/cjs/components/DataTableToolbar/index.native.js +46 -65
  29. package/dist/cjs/components/DataTableToolbar/index.native.js.map +1 -1
  30. package/dist/cjs/components/DataTableViewOptions/index.cjs +38 -54
  31. package/dist/cjs/components/DataTableViewOptions/index.native.js +40 -56
  32. package/dist/cjs/components/DataTableViewOptions/index.native.js.map +1 -1
  33. package/dist/cjs/components/FilterOperatorValueEditor/index.cjs +3 -11
  34. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js +3 -11
  35. package/dist/cjs/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  36. package/dist/cjs/components/SimpleTable/index.cjs +2 -6
  37. package/dist/cjs/components/SimpleTable/index.native.js +2 -6
  38. package/dist/cjs/components/SimpleTable/index.native.js.map +1 -1
  39. package/dist/cjs/components/TableInput/RowDetailEditor.cjs +190 -0
  40. package/dist/cjs/components/TableInput/RowDetailEditor.native.js +172 -0
  41. package/dist/cjs/components/TableInput/RowDetailEditor.native.js.map +1 -0
  42. package/dist/cjs/components/TableInput/index.cjs +42 -5
  43. package/dist/cjs/components/TableInput/index.native.js +64 -1
  44. package/dist/cjs/components/TableInput/index.native.js.map +1 -1
  45. package/dist/cjs/components/TableInput/shared.cjs +8 -0
  46. package/dist/cjs/components/TableInput/shared.native.js +10 -0
  47. package/dist/cjs/components/TableInput/shared.native.js.map +1 -1
  48. package/dist/cjs/hooks/useDataTable.cjs +11 -1
  49. package/dist/cjs/hooks/useDataTable.native.js +12 -2
  50. package/dist/cjs/hooks/useDataTable.native.js.map +1 -1
  51. package/dist/cjs/index.native.js.map +1 -1
  52. package/dist/cjs/utils/rowPress.cjs +4 -1
  53. package/dist/cjs/utils/rowPress.native.js +4 -1
  54. package/dist/cjs/utils/rowPress.native.js.map +1 -1
  55. package/dist/esm/components/ChildTable/index.mjs +8 -2
  56. package/dist/esm/components/ChildTable/index.mjs.map +1 -1
  57. package/dist/esm/components/ChildTable/index.native.js +8 -2
  58. package/dist/esm/components/ChildTable/index.native.js.map +1 -1
  59. package/dist/esm/components/ColumnHeaderFilter/index.mjs +80 -114
  60. package/dist/esm/components/ColumnHeaderFilter/index.mjs.map +1 -1
  61. package/dist/esm/components/ColumnHeaderFilter/index.native.js +92 -132
  62. package/dist/esm/components/ColumnHeaderFilter/index.native.js.map +1 -1
  63. package/dist/esm/components/DataTable/DataTableRoot.mjs +17 -1
  64. package/dist/esm/components/DataTable/DataTableRoot.mjs.map +1 -1
  65. package/dist/esm/components/DataTable/DataTableRoot.native.js +25 -2
  66. package/dist/esm/components/DataTable/DataTableRoot.native.js.map +1 -1
  67. package/dist/esm/components/DataTableCell/CellModalEditor.mjs +76 -74
  68. package/dist/esm/components/DataTableCell/CellModalEditor.mjs.map +1 -1
  69. package/dist/esm/components/DataTableCell/index.mjs +3 -3
  70. package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
  71. package/dist/esm/components/DataTableCell/index.native.js +3 -12
  72. package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
  73. package/dist/esm/components/DataTableDateFilter/index.mjs +137 -160
  74. package/dist/esm/components/DataTableDateFilter/index.mjs.map +1 -1
  75. package/dist/esm/components/DataTableDateFilter/index.native.js +155 -185
  76. package/dist/esm/components/DataTableDateFilter/index.native.js.map +1 -1
  77. package/dist/esm/components/DataTableExport/index.mjs +124 -141
  78. package/dist/esm/components/DataTableExport/index.mjs.map +1 -1
  79. package/dist/esm/components/DataTableExport/index.native.js +137 -159
  80. package/dist/esm/components/DataTableExport/index.native.js.map +1 -1
  81. package/dist/esm/components/DataTableGrouping/index.mjs +194 -214
  82. package/dist/esm/components/DataTableGrouping/index.mjs.map +1 -1
  83. package/dist/esm/components/DataTableGrouping/index.native.js +211 -232
  84. package/dist/esm/components/DataTableGrouping/index.native.js.map +1 -1
  85. package/dist/esm/components/DataTableSearch/index.mjs +105 -117
  86. package/dist/esm/components/DataTableSearch/index.mjs.map +1 -1
  87. package/dist/esm/components/DataTableSearch/index.native.js +113 -125
  88. package/dist/esm/components/DataTableSearch/index.native.js.map +1 -1
  89. package/dist/esm/components/DataTableToolbar/index.mjs +46 -63
  90. package/dist/esm/components/DataTableToolbar/index.mjs.map +1 -1
  91. package/dist/esm/components/DataTableToolbar/index.native.js +48 -67
  92. package/dist/esm/components/DataTableToolbar/index.native.js.map +1 -1
  93. package/dist/esm/components/DataTableViewOptions/index.mjs +40 -56
  94. package/dist/esm/components/DataTableViewOptions/index.mjs.map +1 -1
  95. package/dist/esm/components/DataTableViewOptions/index.native.js +42 -58
  96. package/dist/esm/components/DataTableViewOptions/index.native.js.map +1 -1
  97. package/dist/esm/components/FilterOperatorValueEditor/index.mjs +3 -11
  98. package/dist/esm/components/FilterOperatorValueEditor/index.mjs.map +1 -1
  99. package/dist/esm/components/FilterOperatorValueEditor/index.native.js +3 -11
  100. package/dist/esm/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  101. package/dist/esm/components/SimpleTable/index.mjs +3 -7
  102. package/dist/esm/components/SimpleTable/index.mjs.map +1 -1
  103. package/dist/esm/components/SimpleTable/index.native.js +3 -7
  104. package/dist/esm/components/SimpleTable/index.native.js.map +1 -1
  105. package/dist/esm/components/TableInput/RowDetailEditor.mjs +165 -0
  106. package/dist/esm/components/TableInput/RowDetailEditor.mjs.map +1 -0
  107. package/dist/esm/components/TableInput/RowDetailEditor.native.js +144 -0
  108. package/dist/esm/components/TableInput/RowDetailEditor.native.js.map +1 -0
  109. package/dist/esm/components/TableInput/index.mjs +42 -5
  110. package/dist/esm/components/TableInput/index.mjs.map +1 -1
  111. package/dist/esm/components/TableInput/index.native.js +64 -1
  112. package/dist/esm/components/TableInput/index.native.js.map +1 -1
  113. package/dist/esm/components/TableInput/shared.mjs +7 -1
  114. package/dist/esm/components/TableInput/shared.mjs.map +1 -1
  115. package/dist/esm/components/TableInput/shared.native.js +9 -1
  116. package/dist/esm/components/TableInput/shared.native.js.map +1 -1
  117. package/dist/esm/hooks/useDataTable.mjs +11 -1
  118. package/dist/esm/hooks/useDataTable.mjs.map +1 -1
  119. package/dist/esm/hooks/useDataTable.native.js +12 -2
  120. package/dist/esm/hooks/useDataTable.native.js.map +1 -1
  121. package/dist/esm/index.mjs.map +1 -1
  122. package/dist/esm/index.native.js.map +1 -1
  123. package/dist/esm/utils/rowPress.mjs +4 -1
  124. package/dist/esm/utils/rowPress.mjs.map +1 -1
  125. package/dist/esm/utils/rowPress.native.js +4 -1
  126. package/dist/esm/utils/rowPress.native.js.map +1 -1
  127. package/dist/jsx/components/ChildTable/index.mjs +8 -2
  128. package/dist/jsx/components/ChildTable/index.mjs.map +1 -1
  129. package/dist/jsx/components/ChildTable/index.native.js +8 -2
  130. package/dist/jsx/components/ChildTable/index.native.js.map +1 -1
  131. package/dist/jsx/components/ColumnHeaderFilter/index.mjs +80 -114
  132. package/dist/jsx/components/ColumnHeaderFilter/index.mjs.map +1 -1
  133. package/dist/jsx/components/ColumnHeaderFilter/index.native.js +89 -129
  134. package/dist/jsx/components/ColumnHeaderFilter/index.native.js.map +1 -1
  135. package/dist/jsx/components/DataTable/DataTableRoot.mjs +17 -1
  136. package/dist/jsx/components/DataTable/DataTableRoot.mjs.map +1 -1
  137. package/dist/jsx/components/DataTable/DataTableRoot.native.js +25 -2
  138. package/dist/jsx/components/DataTable/DataTableRoot.native.js.map +1 -1
  139. package/dist/jsx/components/DataTableCell/CellModalEditor.mjs +76 -74
  140. package/dist/jsx/components/DataTableCell/CellModalEditor.mjs.map +1 -1
  141. package/dist/jsx/components/DataTableCell/index.mjs +3 -3
  142. package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
  143. package/dist/jsx/components/DataTableCell/index.native.js +2 -11
  144. package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
  145. package/dist/jsx/components/DataTableDateFilter/index.mjs +137 -160
  146. package/dist/jsx/components/DataTableDateFilter/index.mjs.map +1 -1
  147. package/dist/jsx/components/DataTableDateFilter/index.native.js +153 -183
  148. package/dist/jsx/components/DataTableDateFilter/index.native.js.map +1 -1
  149. package/dist/jsx/components/DataTableExport/index.mjs +124 -141
  150. package/dist/jsx/components/DataTableExport/index.mjs.map +1 -1
  151. package/dist/jsx/components/DataTableExport/index.native.js +135 -157
  152. package/dist/jsx/components/DataTableExport/index.native.js.map +1 -1
  153. package/dist/jsx/components/DataTableGrouping/index.mjs +194 -214
  154. package/dist/jsx/components/DataTableGrouping/index.mjs.map +1 -1
  155. package/dist/jsx/components/DataTableGrouping/index.native.js +209 -230
  156. package/dist/jsx/components/DataTableGrouping/index.native.js.map +1 -1
  157. package/dist/jsx/components/DataTableSearch/index.mjs +105 -117
  158. package/dist/jsx/components/DataTableSearch/index.mjs.map +1 -1
  159. package/dist/jsx/components/DataTableSearch/index.native.js +111 -123
  160. package/dist/jsx/components/DataTableSearch/index.native.js.map +1 -1
  161. package/dist/jsx/components/DataTableToolbar/index.mjs +46 -63
  162. package/dist/jsx/components/DataTableToolbar/index.mjs.map +1 -1
  163. package/dist/jsx/components/DataTableToolbar/index.native.js +46 -65
  164. package/dist/jsx/components/DataTableToolbar/index.native.js.map +1 -1
  165. package/dist/jsx/components/DataTableViewOptions/index.mjs +40 -56
  166. package/dist/jsx/components/DataTableViewOptions/index.mjs.map +1 -1
  167. package/dist/jsx/components/DataTableViewOptions/index.native.js +40 -56
  168. package/dist/jsx/components/DataTableViewOptions/index.native.js.map +1 -1
  169. package/dist/jsx/components/FilterOperatorValueEditor/index.mjs +3 -11
  170. package/dist/jsx/components/FilterOperatorValueEditor/index.mjs.map +1 -1
  171. package/dist/jsx/components/FilterOperatorValueEditor/index.native.js +3 -11
  172. package/dist/jsx/components/FilterOperatorValueEditor/index.native.js.map +1 -1
  173. package/dist/jsx/components/SimpleTable/index.mjs +3 -7
  174. package/dist/jsx/components/SimpleTable/index.mjs.map +1 -1
  175. package/dist/jsx/components/SimpleTable/index.native.js +2 -6
  176. package/dist/jsx/components/SimpleTable/index.native.js.map +1 -1
  177. package/dist/jsx/components/TableInput/RowDetailEditor.mjs +165 -0
  178. package/dist/jsx/components/TableInput/RowDetailEditor.mjs.map +1 -0
  179. package/dist/jsx/components/TableInput/RowDetailEditor.native.js +172 -0
  180. package/dist/jsx/components/TableInput/RowDetailEditor.native.js.map +1 -0
  181. package/dist/jsx/components/TableInput/index.mjs +42 -5
  182. package/dist/jsx/components/TableInput/index.mjs.map +1 -1
  183. package/dist/jsx/components/TableInput/index.native.js +64 -1
  184. package/dist/jsx/components/TableInput/index.native.js.map +1 -1
  185. package/dist/jsx/components/TableInput/shared.mjs +7 -1
  186. package/dist/jsx/components/TableInput/shared.mjs.map +1 -1
  187. package/dist/jsx/components/TableInput/shared.native.js +10 -0
  188. package/dist/jsx/components/TableInput/shared.native.js.map +1 -1
  189. package/dist/jsx/hooks/useDataTable.mjs +11 -1
  190. package/dist/jsx/hooks/useDataTable.mjs.map +1 -1
  191. package/dist/jsx/hooks/useDataTable.native.js +12 -2
  192. package/dist/jsx/hooks/useDataTable.native.js.map +1 -1
  193. package/dist/jsx/index.js.map +1 -1
  194. package/dist/jsx/index.mjs.map +1 -1
  195. package/dist/jsx/index.native.js.map +1 -1
  196. package/dist/jsx/utils/rowPress.mjs +4 -1
  197. package/dist/jsx/utils/rowPress.mjs.map +1 -1
  198. package/dist/jsx/utils/rowPress.native.js +4 -1
  199. package/dist/jsx/utils/rowPress.native.js.map +1 -1
  200. package/package.json +14 -14
  201. package/src/components/ChildTable/childTable.cellHidden.spec.tsx +57 -0
  202. package/src/components/ChildTable/childTable.rowDetail.spec.tsx +120 -0
  203. package/src/components/ChildTable/index.tsx +16 -0
  204. package/src/components/ColumnHeaderFilter/index.tsx +89 -116
  205. package/src/components/DataTable/DataTable.stories.tsx +71 -6
  206. package/src/components/DataTable/DataTableRoot.tsx +27 -4
  207. package/src/components/DataTable/dataTableMobileCards.spec.tsx +1 -4
  208. package/src/components/DataTable/headerContainment.spec.tsx +91 -0
  209. package/src/components/DataTable/rowPress.spec.tsx +2 -6
  210. package/src/components/DataTableBulkActions/DataTableBulkActions.stories.tsx +5 -4
  211. package/src/components/DataTableCell/CellModalEditor.tsx +95 -75
  212. package/src/components/DataTableCell/cellDraftPolicy.spec.tsx +12 -2
  213. package/src/components/DataTableCell/cellModalEditor.spec.tsx +75 -49
  214. package/src/components/DataTableCell/dataTableCell.spec.tsx +31 -14
  215. package/src/components/DataTableCell/index.tsx +3 -2
  216. package/src/components/DataTableDateFilter/DataTableDateFilter.spec.tsx +6 -3
  217. package/src/components/DataTableDateFilter/index.tsx +151 -180
  218. package/src/components/DataTableExport/index.tsx +135 -142
  219. package/src/components/DataTableGrouping/index.tsx +193 -195
  220. package/src/components/DataTableSearch/index.tsx +111 -109
  221. package/src/components/DataTableToolbar/index.tsx +39 -46
  222. package/src/components/DataTableViewOptions/DataTableViewOptions.stories.tsx +52 -0
  223. package/src/components/DataTableViewOptions/index.tsx +32 -40
  224. package/src/components/FilterOperatorValueEditor/FilterOperatorValueEditor.spec.tsx +64 -2
  225. package/src/components/FilterOperatorValueEditor/index.tsx +0 -8
  226. package/src/components/SimpleTable/index.tsx +3 -2
  227. package/src/components/SimpleTable/simpleTable.spec.tsx +15 -0
  228. package/src/components/Table/accessibility.spec.tsx +7 -19
  229. package/src/components/TableInput/RowDetailEditor.native.tsx +134 -0
  230. package/src/components/TableInput/RowDetailEditor.tsx +173 -0
  231. package/src/components/TableInput/index.native.tsx +49 -1
  232. package/src/components/TableInput/index.tsx +65 -10
  233. package/src/components/TableInput/shared.ts +29 -0
  234. package/src/components/tableDropdowns.spec.tsx +172 -0
  235. package/src/hooks/useDataTable.spec.ts +39 -0
  236. package/src/hooks/useDataTable.ts +15 -1
  237. package/src/index.ts +1 -0
  238. package/src/utils/rowPress.ts +3 -0
  239. package/types/components/ChildTable/index.d.ts +10 -0
  240. package/types/components/ChildTable/index.d.ts.map +1 -1
  241. package/types/components/ColumnHeaderFilter/index.d.ts +1 -1
  242. package/types/components/ColumnHeaderFilter/index.d.ts.map +1 -1
  243. package/types/components/DataTable/DataTableRoot.d.ts.map +1 -1
  244. package/types/components/DataTableCell/CellModalEditor.d.ts +10 -7
  245. package/types/components/DataTableCell/CellModalEditor.d.ts.map +1 -1
  246. package/types/components/DataTableCell/index.d.ts.map +1 -1
  247. package/types/components/DataTableDateFilter/index.d.ts.map +1 -1
  248. package/types/components/DataTableGrouping/index.d.ts.map +1 -1
  249. package/types/components/DataTableSearch/index.d.ts.map +1 -1
  250. package/types/components/FilterOperatorValueEditor/index.d.ts.map +1 -1
  251. package/types/components/SimpleTable/index.d.ts.map +1 -1
  252. package/types/components/TableInput/RowDetailEditor.d.ts +24 -0
  253. package/types/components/TableInput/RowDetailEditor.d.ts.map +1 -0
  254. package/types/components/TableInput/RowDetailEditor.native.d.ts +11 -0
  255. package/types/components/TableInput/RowDetailEditor.native.d.ts.map +1 -0
  256. package/types/components/TableInput/index.d.ts +11 -2
  257. package/types/components/TableInput/index.d.ts.map +1 -1
  258. package/types/components/TableInput/index.native.d.ts +8 -1
  259. package/types/components/TableInput/index.native.d.ts.map +1 -1
  260. package/types/components/TableInput/shared.d.ts +11 -0
  261. package/types/components/TableInput/shared.d.ts.map +1 -1
  262. package/types/hooks/useDataTable.d.ts.map +1 -1
  263. package/types/index.d.ts +1 -0
  264. package/types/index.d.ts.map +1 -1
  265. 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
  })}