@sincpro/web-ui 0.0.0-stage → 0.2.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 (376) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -2
  3. package/dist/components/activity/Activity.d.ts +69 -0
  4. package/dist/components/activity/Activity.js +279 -0
  5. package/dist/components/activity/Messages.d.ts +66 -0
  6. package/dist/components/activity/Messages.js +46 -0
  7. package/dist/components/activity/Timeline.d.ts +21 -0
  8. package/dist/components/activity/Timeline.js +30 -0
  9. package/dist/components/activity/index.d.ts +3 -0
  10. package/dist/components/activity/index.js +4 -0
  11. package/dist/components/assist/AssistMark.d.ts +64 -0
  12. package/dist/components/assist/AssistMark.js +108 -0
  13. package/dist/components/assist/AssistPending.d.ts +24 -0
  14. package/dist/components/assist/AssistPending.js +26 -0
  15. package/dist/components/assist/AssistSuggestion.d.ts +42 -0
  16. package/dist/components/assist/AssistSuggestion.js +55 -0
  17. package/dist/components/assist/ChangeReview.d.ts +56 -0
  18. package/dist/components/assist/ChangeReview.js +121 -0
  19. package/dist/components/assist/assist.d.ts +77 -0
  20. package/dist/components/assist/assist.js +87 -0
  21. package/dist/components/assist/index.d.ts +5 -0
  22. package/dist/components/assist/index.js +7 -0
  23. package/dist/components/form/Form.d.ts +52 -0
  24. package/dist/components/form/Form.js +49 -0
  25. package/dist/components/form/Sheet.d.ts +47 -0
  26. package/dist/components/form/Sheet.js +90 -0
  27. package/dist/components/form/index.d.ts +2 -0
  28. package/dist/components/form/index.js +4 -0
  29. package/dist/components/frame/Chart.d.ts +13 -0
  30. package/dist/components/frame/Chart.js +24 -0
  31. package/dist/components/frame/FrameView.d.ts +33 -0
  32. package/dist/components/frame/FrameView.js +125 -0
  33. package/dist/components/frame/index.d.ts +3 -0
  34. package/dist/components/frame/index.js +3 -0
  35. package/dist/components/frame/model.d.ts +43 -0
  36. package/dist/components/frame/model.js +101 -0
  37. package/dist/components/index.d.ts +10 -0
  38. package/dist/components/index.js +11 -0
  39. package/dist/components/lines/LineItems.d.ts +111 -0
  40. package/dist/components/lines/LineItems.js +317 -0
  41. package/dist/components/lines/index.d.ts +1 -0
  42. package/dist/components/lines/index.js +2 -0
  43. package/dist/components/lines/lines.d.ts +23 -0
  44. package/dist/components/lines/lines.js +26 -0
  45. package/dist/components/listing/ColumnsMenu.d.ts +20 -0
  46. package/dist/components/listing/ColumnsMenu.js +46 -0
  47. package/dist/components/listing/GroupingLevels.d.ts +35 -0
  48. package/dist/components/listing/GroupingLevels.js +45 -0
  49. package/dist/components/listing/Listing.d.ts +27 -0
  50. package/dist/components/listing/Listing.js +86 -0
  51. package/dist/components/listing/ListingBar.d.ts +112 -0
  52. package/dist/components/listing/ListingBar.js +144 -0
  53. package/dist/components/listing/Pagination.d.ts +36 -0
  54. package/dist/components/listing/Pagination.js +69 -0
  55. package/dist/components/listing/RecordCard.d.ts +33 -0
  56. package/dist/components/listing/RecordCard.js +22 -0
  57. package/dist/components/listing/RecordLink.d.ts +7 -0
  58. package/dist/components/listing/RecordLink.js +22 -0
  59. package/dist/components/listing/Row.d.ts +110 -0
  60. package/dist/components/listing/Row.js +123 -0
  61. package/dist/components/listing/SortMenu.d.ts +62 -0
  62. package/dist/components/listing/SortMenu.js +105 -0
  63. package/dist/components/listing/ViewSwitcher.d.ts +24 -0
  64. package/dist/components/listing/ViewSwitcher.js +16 -0
  65. package/dist/components/listing/columns.d.ts +47 -0
  66. package/dist/components/listing/columns.js +72 -0
  67. package/dist/components/listing/grouping.d.ts +35 -0
  68. package/dist/components/listing/grouping.js +47 -0
  69. package/dist/components/listing/index.d.ts +17 -0
  70. package/dist/components/listing/index.js +20 -0
  71. package/dist/components/listing/modelColumns.d.ts +50 -0
  72. package/dist/components/listing/modelColumns.js +103 -0
  73. package/dist/components/listing/pages.d.ts +78 -0
  74. package/dist/components/listing/pages.js +135 -0
  75. package/dist/components/listing/selection.d.ts +21 -0
  76. package/dist/components/listing/selection.js +43 -0
  77. package/dist/components/listing/set.d.ts +48 -0
  78. package/dist/components/listing/set.js +24 -0
  79. package/dist/components/listing/tree.d.ts +28 -0
  80. package/dist/components/listing/tree.js +23 -0
  81. package/dist/components/navigation/CommandPalette.d.ts +40 -0
  82. package/dist/components/navigation/CommandPalette.js +114 -0
  83. package/dist/components/navigation/NavigationTree.d.ts +31 -0
  84. package/dist/components/navigation/NavigationTree.js +79 -0
  85. package/dist/components/navigation/commands.d.ts +30 -0
  86. package/dist/components/navigation/commands.js +30 -0
  87. package/dist/components/navigation/index.d.ts +3 -0
  88. package/dist/components/navigation/index.js +4 -0
  89. package/dist/components/record/Breadcrumb.d.ts +42 -0
  90. package/dist/components/record/Breadcrumb.js +98 -0
  91. package/dist/components/record/DirtyActions.d.ts +26 -0
  92. package/dist/components/record/DirtyActions.js +22 -0
  93. package/dist/components/record/Pager.d.ts +18 -0
  94. package/dist/components/record/Pager.js +33 -0
  95. package/dist/components/record/RecordHeader.d.ts +75 -0
  96. package/dist/components/record/RecordHeader.js +104 -0
  97. package/dist/components/record/RecordNotices.d.ts +37 -0
  98. package/dist/components/record/RecordNotices.js +75 -0
  99. package/dist/components/record/RecordSkeleton.d.ts +3 -0
  100. package/dist/components/record/RecordSkeleton.js +21 -0
  101. package/dist/components/record/SmartButtons.d.ts +35 -0
  102. package/dist/components/record/SmartButtons.js +38 -0
  103. package/dist/components/record/StatusBar.d.ts +51 -0
  104. package/dist/components/record/StatusBar.js +168 -0
  105. package/dist/components/record/ThumbBar.d.ts +36 -0
  106. package/dist/components/record/ThumbBar.js +86 -0
  107. package/dist/components/record/index.d.ts +6 -0
  108. package/dist/components/record/index.js +8 -0
  109. package/dist/components/search/CriteriaBuilder.d.ts +58 -0
  110. package/dist/components/search/CriteriaBuilder.js +278 -0
  111. package/dist/components/search/CriteriaChips.d.ts +22 -0
  112. package/dist/components/search/CriteriaChips.js +64 -0
  113. package/dist/components/search/FilterBuilder.d.ts +142 -0
  114. package/dist/components/search/FilterBuilder.js +421 -0
  115. package/dist/components/search/JoinWord.d.ts +8 -0
  116. package/dist/components/search/JoinWord.js +47 -0
  117. package/dist/components/search/JsonEditor.d.ts +19 -0
  118. package/dist/components/search/JsonEditor.js +78 -0
  119. package/dist/components/search/SearchBar.d.ts +78 -0
  120. package/dist/components/search/SearchBar.js +387 -0
  121. package/dist/components/search/criteria.d.ts +116 -0
  122. package/dist/components/search/criteria.js +159 -0
  123. package/dist/components/search/datePeriods.d.ts +122 -0
  124. package/dist/components/search/datePeriods.js +134 -0
  125. package/dist/components/search/dateRanges.d.ts +54 -0
  126. package/dist/components/search/dateRanges.js +89 -0
  127. package/dist/components/search/index.d.ts +9 -0
  128. package/dist/components/search/index.js +20 -0
  129. package/dist/components/search/model.d.ts +54 -0
  130. package/dist/components/search/model.js +37 -0
  131. package/dist/components/search/query.d.ts +166 -0
  132. package/dist/components/search/query.js +436 -0
  133. package/dist/components/shell/AppFrame.d.ts +14 -0
  134. package/dist/components/shell/AppFrame.js +80 -0
  135. package/dist/components/shell/TopBar.d.ts +73 -0
  136. package/dist/components/shell/TopBar.js +54 -0
  137. package/dist/components/shell/headerSlot.d.ts +17 -0
  138. package/dist/components/shell/headerSlot.js +21 -0
  139. package/dist/components/shell/index.d.ts +2 -0
  140. package/dist/components/shell/index.js +3 -0
  141. package/dist/foundations/aria.d.ts +14 -0
  142. package/dist/foundations/aria.js +18 -0
  143. package/dist/foundations/calendar.d.ts +28 -0
  144. package/dist/foundations/calendar.js +183 -0
  145. package/dist/foundations/copy.d.ts +703 -0
  146. package/dist/foundations/copy.es.d.ts +2 -0
  147. package/dist/foundations/copy.es.js +430 -0
  148. package/dist/foundations/copy.js +520 -0
  149. package/dist/foundations/density.d.ts +16 -0
  150. package/dist/foundations/density.js +21 -0
  151. package/dist/foundations/grouping.d.ts +281 -0
  152. package/dist/foundations/grouping.js +0 -0
  153. package/dist/foundations/index.d.ts +14 -0
  154. package/dist/foundations/index.js +30 -0
  155. package/dist/foundations/layout.d.ts +9 -0
  156. package/dist/foundations/layout.js +9 -0
  157. package/dist/foundations/pivot.d.ts +222 -0
  158. package/dist/foundations/pivot.js +198 -0
  159. package/dist/foundations/pivotSheet.d.ts +61 -0
  160. package/dist/foundations/pivotSheet.js +0 -0
  161. package/dist/foundations/slots.d.ts +67 -0
  162. package/dist/foundations/slots.js +63 -0
  163. package/dist/foundations/sticky.d.ts +53 -0
  164. package/dist/foundations/sticky.js +150 -0
  165. package/dist/foundations/surface.d.ts +9 -0
  166. package/dist/foundations/surface.js +26 -0
  167. package/dist/foundations/theme.d.ts +42 -0
  168. package/dist/foundations/theme.js +88 -0
  169. package/dist/foundations/types.d.ts +23 -0
  170. package/dist/foundations/types.js +50 -0
  171. package/dist/foundations/variants.d.ts +19 -0
  172. package/dist/foundations/variants.js +48 -0
  173. package/dist/foundations/xlsx.d.ts +86 -0
  174. package/dist/foundations/xlsx.js +0 -0
  175. package/dist/index.d.ts +19 -0
  176. package/dist/index.js +19 -0
  177. package/dist/primitives/block/Block.d.ts +66 -0
  178. package/dist/primitives/block/Block.js +34 -0
  179. package/dist/primitives/block/Line.d.ts +18 -0
  180. package/dist/primitives/block/Line.js +45 -0
  181. package/dist/primitives/block/SectionGrid.d.ts +30 -0
  182. package/dist/primitives/block/SectionGrid.js +29 -0
  183. package/dist/primitives/block/Steps.d.ts +76 -0
  184. package/dist/primitives/block/Steps.js +43 -0
  185. package/dist/primitives/block/index.d.ts +4 -0
  186. package/dist/primitives/block/index.js +7 -0
  187. package/dist/primitives/button/Button.d.ts +137 -0
  188. package/dist/primitives/button/Button.js +234 -0
  189. package/dist/primitives/button/index.d.ts +1 -0
  190. package/dist/primitives/button/index.js +2 -0
  191. package/dist/primitives/cell/CellStack.d.ts +47 -0
  192. package/dist/primitives/cell/CellStack.js +33 -0
  193. package/dist/primitives/cell/index.d.ts +1 -0
  194. package/dist/primitives/cell/index.js +3 -0
  195. package/dist/primitives/choice/ComboBox.d.ts +19 -0
  196. package/dist/primitives/choice/ComboBox.js +52 -0
  197. package/dist/primitives/choice/RadioGroup.d.ts +18 -0
  198. package/dist/primitives/choice/RadioGroup.js +50 -0
  199. package/dist/primitives/choice/SearchField.d.ts +33 -0
  200. package/dist/primitives/choice/SearchField.js +46 -0
  201. package/dist/primitives/choice/SegmentedControl.d.ts +16 -0
  202. package/dist/primitives/choice/SegmentedControl.js +69 -0
  203. package/dist/primitives/choice/Slider.d.ts +16 -0
  204. package/dist/primitives/choice/Slider.js +46 -0
  205. package/dist/primitives/choice/Switch.d.ts +14 -0
  206. package/dist/primitives/choice/Switch.js +65 -0
  207. package/dist/primitives/choice/ThemeSwitcher.d.ts +10 -0
  208. package/dist/primitives/choice/ThemeSwitcher.js +25 -0
  209. package/dist/primitives/choice/index.d.ts +8 -0
  210. package/dist/primitives/choice/index.js +11 -0
  211. package/dist/primitives/choice/options.d.ts +28 -0
  212. package/dist/primitives/choice/options.js +6 -0
  213. package/dist/primitives/date/Calendar.d.ts +31 -0
  214. package/dist/primitives/date/Calendar.js +134 -0
  215. package/dist/primitives/date/DatePicker.d.ts +42 -0
  216. package/dist/primitives/date/DatePicker.js +103 -0
  217. package/dist/primitives/date/DateTime.d.ts +35 -0
  218. package/dist/primitives/date/DateTime.js +100 -0
  219. package/dist/primitives/date/index.d.ts +3 -0
  220. package/dist/primitives/date/index.js +6 -0
  221. package/dist/primitives/dialog/Dialog.d.ts +118 -0
  222. package/dist/primitives/dialog/Dialog.js +121 -0
  223. package/dist/primitives/dialog/Drill.d.ts +90 -0
  224. package/dist/primitives/dialog/Drill.js +172 -0
  225. package/dist/primitives/dialog/index.d.ts +2 -0
  226. package/dist/primitives/dialog/index.js +4 -0
  227. package/dist/primitives/disclosure/Disclosure.d.ts +36 -0
  228. package/dist/primitives/disclosure/Disclosure.js +52 -0
  229. package/dist/primitives/disclosure/index.d.ts +1 -0
  230. package/dist/primitives/disclosure/index.js +2 -0
  231. package/dist/primitives/field/Checks.d.ts +57 -0
  232. package/dist/primitives/field/Checks.js +72 -0
  233. package/dist/primitives/field/DropZone.d.ts +42 -0
  234. package/dist/primitives/field/DropZone.js +52 -0
  235. package/dist/primitives/field/Field.d.ts +178 -0
  236. package/dist/primitives/field/Field.js +251 -0
  237. package/dist/primitives/field/Suggest.d.ts +48 -0
  238. package/dist/primitives/field/Suggest.js +43 -0
  239. package/dist/primitives/field/aria.d.ts +61 -0
  240. package/dist/primitives/field/aria.js +70 -0
  241. package/dist/primitives/field/context.d.ts +30 -0
  242. package/dist/primitives/field/context.js +13 -0
  243. package/dist/primitives/field/copy.d.ts +16 -0
  244. package/dist/primitives/field/copy.js +69 -0
  245. package/dist/primitives/field/index.d.ts +9 -0
  246. package/dist/primitives/field/index.js +15 -0
  247. package/dist/primitives/field/layout.d.ts +24 -0
  248. package/dist/primitives/field/layout.js +10 -0
  249. package/dist/primitives/field/mode.d.ts +9 -0
  250. package/dist/primitives/field/mode.js +13 -0
  251. package/dist/primitives/field/note.d.ts +55 -0
  252. package/dist/primitives/field/note.js +117 -0
  253. package/dist/primitives/hint/Hint.d.ts +31 -0
  254. package/dist/primitives/hint/Hint.js +42 -0
  255. package/dist/primitives/hint/index.d.ts +1 -0
  256. package/dist/primitives/hint/index.js +1 -0
  257. package/dist/primitives/index.d.ts +20 -0
  258. package/dist/primitives/index.js +21 -0
  259. package/dist/primitives/menu/Menu.d.ts +128 -0
  260. package/dist/primitives/menu/Menu.js +89 -0
  261. package/dist/primitives/menu/index.d.ts +1 -0
  262. package/dist/primitives/menu/index.js +2 -0
  263. package/dist/primitives/notice/Callout.d.ts +49 -0
  264. package/dist/primitives/notice/Callout.js +28 -0
  265. package/dist/primitives/notice/Flag.d.ts +13 -0
  266. package/dist/primitives/notice/Flag.js +31 -0
  267. package/dist/primitives/notice/Notice.d.ts +45 -0
  268. package/dist/primitives/notice/Notice.js +61 -0
  269. package/dist/primitives/notice/Toast.d.ts +55 -0
  270. package/dist/primitives/notice/Toast.js +142 -0
  271. package/dist/primitives/notice/index.d.ts +4 -0
  272. package/dist/primitives/notice/index.js +6 -0
  273. package/dist/primitives/overlay/Popover.d.ts +6 -0
  274. package/dist/primitives/overlay/Popover.js +27 -0
  275. package/dist/primitives/overlay/PopoverTrigger.d.ts +55 -0
  276. package/dist/primitives/overlay/PopoverTrigger.js +38 -0
  277. package/dist/primitives/overlay/Preview.d.ts +19 -0
  278. package/dist/primitives/overlay/Preview.js +60 -0
  279. package/dist/primitives/overlay/Tooltip.d.ts +38 -0
  280. package/dist/primitives/overlay/Tooltip.js +74 -0
  281. package/dist/primitives/overlay/index.d.ts +4 -0
  282. package/dist/primitives/overlay/index.js +7 -0
  283. package/dist/primitives/picker/Picker.d.ts +89 -0
  284. package/dist/primitives/picker/Picker.js +204 -0
  285. package/dist/primitives/picker/index.d.ts +1 -0
  286. package/dist/primitives/picker/index.js +2 -0
  287. package/dist/primitives/surface/Peek.d.ts +60 -0
  288. package/dist/primitives/surface/Peek.js +71 -0
  289. package/dist/primitives/surface/index.d.ts +1 -0
  290. package/dist/primitives/surface/index.js +2 -0
  291. package/dist/primitives/table/CellCopy.d.ts +20 -0
  292. package/dist/primitives/table/CellCopy.js +23 -0
  293. package/dist/primitives/table/CellEditor.d.ts +14 -0
  294. package/dist/primitives/table/CellEditor.js +83 -0
  295. package/dist/primitives/table/DataTable.d.ts +189 -0
  296. package/dist/primitives/table/DataTable.js +330 -0
  297. package/dist/primitives/table/EditableTable.d.ts +191 -0
  298. package/dist/primitives/table/EditableTable.js +617 -0
  299. package/dist/primitives/table/PivotTable.d.ts +14 -0
  300. package/dist/primitives/table/PivotTable.js +0 -0
  301. package/dist/primitives/table/ReportTable.d.ts +38 -0
  302. package/dist/primitives/table/ReportTable.js +106 -0
  303. package/dist/primitives/table/RowActions.d.ts +16 -0
  304. package/dist/primitives/table/RowActions.js +28 -0
  305. package/dist/primitives/table/SheetTable.d.ts +73 -0
  306. package/dist/primitives/table/SheetTable.js +230 -0
  307. package/dist/primitives/table/Table.d.ts +106 -0
  308. package/dist/primitives/table/Table.js +116 -0
  309. package/dist/primitives/table/index.d.ts +7 -0
  310. package/dist/primitives/table/index.js +9 -0
  311. package/dist/primitives/tabs/Tabs.d.ts +91 -0
  312. package/dist/primitives/tabs/Tabs.js +56 -0
  313. package/dist/primitives/tabs/index.d.ts +1 -0
  314. package/dist/primitives/tabs/index.js +1 -0
  315. package/dist/primitives/tag/Tag.d.ts +36 -0
  316. package/dist/primitives/tag/Tag.js +68 -0
  317. package/dist/primitives/tag/Tags.d.ts +57 -0
  318. package/dist/primitives/tag/Tags.js +104 -0
  319. package/dist/primitives/tag/TagsField.d.ts +35 -0
  320. package/dist/primitives/tag/TagsField.js +186 -0
  321. package/dist/primitives/tag/choices.d.ts +38 -0
  322. package/dist/primitives/tag/choices.js +26 -0
  323. package/dist/primitives/tag/index.d.ts +4 -0
  324. package/dist/primitives/tag/index.js +8 -0
  325. package/dist/primitives/toolbar/Toolbar.d.ts +13 -0
  326. package/dist/primitives/toolbar/Toolbar.js +36 -0
  327. package/dist/primitives/toolbar/index.d.ts +1 -0
  328. package/dist/primitives/toolbar/index.js +2 -0
  329. package/dist/primitives/tree/Tree.d.ts +56 -0
  330. package/dist/primitives/tree/Tree.js +74 -0
  331. package/dist/primitives/tree/index.d.ts +1 -0
  332. package/dist/primitives/tree/index.js +3 -0
  333. package/dist/primitives/value/Copyable.d.ts +48 -0
  334. package/dist/primitives/value/Copyable.js +71 -0
  335. package/dist/primitives/value/Fecha.d.ts +12 -0
  336. package/dist/primitives/value/Fecha.js +89 -0
  337. package/dist/primitives/value/Meter.d.ts +29 -0
  338. package/dist/primitives/value/Meter.js +46 -0
  339. package/dist/primitives/value/Metric.d.ts +42 -0
  340. package/dist/primitives/value/Metric.js +38 -0
  341. package/dist/primitives/value/Person.d.ts +90 -0
  342. package/dist/primitives/value/Person.js +208 -0
  343. package/dist/primitives/value/ProgressBar.d.ts +31 -0
  344. package/dist/primitives/value/ProgressBar.js +53 -0
  345. package/dist/primitives/value/Relation.d.ts +74 -0
  346. package/dist/primitives/value/Relation.js +74 -0
  347. package/dist/primitives/value/Totals.d.ts +121 -0
  348. package/dist/primitives/value/Totals.js +54 -0
  349. package/dist/primitives/value/Value.d.ts +40 -0
  350. package/dist/primitives/value/Value.js +80 -0
  351. package/dist/primitives/value/index.d.ts +9 -0
  352. package/dist/primitives/value/index.js +10 -0
  353. package/dist/tokens.css +1062 -0
  354. package/dist/views/index.d.ts +2 -0
  355. package/dist/views/index.js +3 -0
  356. package/dist/views/list/List.d.ts +119 -0
  357. package/dist/views/list/List.js +101 -0
  358. package/dist/views/list/ListView.d.ts +6 -0
  359. package/dist/views/list/ListView.js +295 -0
  360. package/dist/views/list/columns.d.ts +18 -0
  361. package/dist/views/list/columns.js +27 -0
  362. package/dist/views/list/derive.d.ts +87 -0
  363. package/dist/views/list/derive.js +207 -0
  364. package/dist/views/list/index.d.ts +2 -0
  365. package/dist/views/list/index.js +4 -0
  366. package/dist/views/list/presentations.d.ts +39 -0
  367. package/dist/views/list/presentations.js +105 -0
  368. package/dist/views/list/types.d.ts +284 -0
  369. package/dist/views/list/types.js +1 -0
  370. package/dist/views/list/useNarrowList.d.ts +17 -0
  371. package/dist/views/list/useNarrowList.js +32 -0
  372. package/dist/views/record/Record.d.ts +145 -0
  373. package/dist/views/record/Record.js +211 -0
  374. package/dist/views/record/index.d.ts +1 -0
  375. package/dist/views/record/index.js +2 -0
  376. package/package.json +113 -4
@@ -0,0 +1,278 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { blankGroup, ExpressionView, treeFromFilter, treeToFilter, WhereTree, } from "./FilterBuilder.js";
4
+ import { JsonEditor } from "./JsonEditor.js";
5
+ import { filterableNames, } from "./model.js";
6
+ import { currentCopy, fillCopy, useUiCopy } from "../../foundations/copy.js";
7
+ import { GRAIN_ORDER } from "../../foundations/grouping.js";
8
+ import { useSurface } from "../../foundations/surface.js";
9
+ import { Rule } from "../../primitives/block/Block.js";
10
+ import { Button } from "../../primitives/button/index.js";
11
+ import { Tags } from "../../primitives/tag/Tags.js";
12
+ import { Plus, Trash2 } from "lucide-react";
13
+ import { Fragment, useMemo, useState } from "react";
14
+ /**
15
+ * Los cortes de una fecha, de GRUESO a fino, con `null` adelante: sin corte, un
16
+ * campo de fecha parte por instante y cada fila termina en su propio montón.
17
+ *
18
+ * El orden no es una preferencia — un día entero vive adentro de un solo mes— y
19
+ * es el mismo que `GRAIN_ORDER` publica para la barra: una sola dirección para
20
+ * las dos pantallas. La ficha de un nivel rota por esta lista.
21
+ */
22
+ const GRAINS = [null, ...GRAIN_ORDER];
23
+ /** Qué tipos admiten un grano: recortar un texto por mes no significa nada. */
24
+ const DATE_TYPES = new Set(["date", "datetime"]);
25
+ /** Los operadores que un plegado —siempre un número— o `count` admiten en un `having`: los
26
+ * mismos que la definición del modelo publica para un campo numérico, `is null` aparte, que
27
+ * no significa nada sobre un número que el propio motor calculó. */
28
+ const COMPARABLE = ["=", "!=", ">", ">=", "<", "<=", "between"];
29
+ /** Las funciones que un plegado puede pedir, cerradas del lado del servidor porque el nombre
30
+ * llega a SQL. */
31
+ const FOLD_FUNCTIONS = ["sum", "avg", "min", "max", "count"];
32
+ function isEmpty(criteria) {
33
+ return (criteria.where === undefined &&
34
+ (criteria.order ?? []).length === 0 &&
35
+ (criteria.grouping?.by ?? []).length === 0 &&
36
+ Object.keys(criteria.grouping?.totals ?? {}).length === 0 &&
37
+ Object.keys(criteria.specification ?? {}).length === 0);
38
+ }
39
+ /**
40
+ * LA FICHA QUE AGREGA: un «+» que abre la lista de lo que se puede elegir.
41
+ *
42
+ * Es lo mismo que hace el numeral en la barra, y por la misma razón: elegir
43
+ * entre veinte campos es elegir de una LISTA, no desplegar un `<select>` que ya
44
+ * viene con uno puesto y que hay que cambiar. Con el `<select>`, agregar un
45
+ * nivel ponía el primer campo del modelo aunque nadie lo hubiera pedido.
46
+ */
47
+ function AddTag({ label, options, onPick, }) {
48
+ const [open, setOpen] = useState(false);
49
+ if (options.length === 0)
50
+ return null;
51
+ return (_jsxs("span", { className: "relative inline-flex shrink-0", children: [_jsxs("button", { type: "button", onClick: () => setOpen((current) => !current), "aria-expanded": open, className: "inline-flex shrink-0 items-center gap-1 rounded-pill border border-dashed border-line-strong px-2 py-0.5 text-micro text-muted transition-colors duration-(--duration-instant) hover:border-line-strong hover:bg-ink/5 hover:text-ink focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary", children: [_jsx(Plus, { className: "size-3", strokeWidth: 2, "aria-hidden": true }), label] }), open ? (_jsx("span", { className: "absolute top-full left-0 z-40 mt-1 max-h-64 overflow-y-auto rounded-panel border border-line-strong bg-surface p-1 shadow-float", children: _jsx(Tags, { stack: true, label: label, items: options.map((name) => ({
52
+ id: name,
53
+ label: name,
54
+ onSelect: () => {
55
+ onPick(name);
56
+ setOpen(false);
57
+ },
58
+ })) }) })) : null] }));
59
+ }
60
+ /** El corte siguiente, y vuelta a empezar. */
61
+ function nextGrain(grain) {
62
+ const at = GRAINS.indexOf(grain ?? null);
63
+ return GRAINS[(at + 1) % GRAINS.length] ?? null;
64
+ }
65
+ export function CriteriaBuilder({ model, initial, focus, onApply, onCancel, }) {
66
+ const surface = useSurface();
67
+ const names = Object.keys(model.fields).sort();
68
+ /* Nunca un relacional ni un valor-objeto: ninguno de los dos tiene una comparación propia
69
+ (`ops` vacío), así que ninguno sirve de blanco para un filtro, un orden o un nivel. */
70
+ const scalarNames = filterableNames(model);
71
+ const sortable = names.filter((name) => model.fields[name]?.sortable === true);
72
+ const decomposed = initial?.where === undefined ? null : treeFromFilter(initial.where);
73
+ const [tree, setTree] = useState(() => decomposed !== null && decomposed.kind === "group"
74
+ ? decomposed
75
+ : decomposed !== null
76
+ ? { kind: "group", join: "all", children: [decomposed] }
77
+ : blankGroup(model, scalarNames[0]));
78
+ const copy = useUiCopy();
79
+ const [lockedWhere, setLockedWhere] = useState(initial?.where !== undefined && decomposed === null ? initial.where : null);
80
+ const [order, setOrder] = useState(initial?.order ?? []);
81
+ const [levels, setLevels] = useState(initial?.grouping?.by ?? []);
82
+ const [totals, setTotals] = useState(initial?.grouping?.totals ?? {});
83
+ const [groupOrder, setGroupOrder] = useState(initial?.grouping?.order ?? []);
84
+ const [groupLimit, setGroupLimit] = useState(initial?.grouping?.pagination?.limit);
85
+ /* `having` es un filtro como cualquier otro, sobre un modelo sintético cuyos únicos campos
86
+ son los nombres que admite: los de `totals`, más `count`. Reutilizar `WhereTree` sobre ese
87
+ modelo es más simple y más correcto que armar un editor de condiciones aparte. */
88
+ const havingModel = useMemo(() => ({
89
+ aggregate: "having",
90
+ identity: "count",
91
+ fields: Object.fromEntries([
92
+ ["count", { type: "integer", nullable: false, sortable: false, ops: COMPARABLE }],
93
+ ...Object.keys(totals).map((name) => [
94
+ name,
95
+ { type: "number", nullable: false, sortable: false, ops: COMPARABLE },
96
+ ]),
97
+ ]),
98
+ }), [totals]);
99
+ const havingDecomposed = initial?.grouping?.having === undefined ? null : treeFromFilter(initial.grouping.having);
100
+ const [havingTree, setHavingTree] = useState(() => havingDecomposed !== null && havingDecomposed.kind === "group"
101
+ ? havingDecomposed
102
+ : havingDecomposed !== null
103
+ ? { kind: "group", join: "all", children: [havingDecomposed] }
104
+ : blankGroup(havingModel, "count"));
105
+ const [fields, setFields] = useState(Object.keys(initial?.specification ?? {}));
106
+ const [rawText, setRawText] = useState(() => initial === undefined ? "" : JSON.stringify(initial, null, 2));
107
+ const [rawError, setRawError] = useState(null);
108
+ /** Lo que los controles dicen, junto: es lo que se aplica y lo que el JSON muestra. */
109
+ function assemble(next) {
110
+ const where = next.locked ?? lockedWhere ?? treeToFilter(next.tree ?? tree);
111
+ const sorts = next.order ?? order;
112
+ const by = next.levels ?? levels;
113
+ const folds = next.totals ?? totals;
114
+ const having = treeToFilter(next.havingTree ?? havingTree);
115
+ const groupSorts = next.groupOrder ?? groupOrder;
116
+ const limit = "groupLimit" in next ? next.groupLimit : groupLimit;
117
+ const chosen = next.fields ?? fields;
118
+ /* Sin niveles no hay qué plegar, qué filtrar entre montones ni en qué orden o página
119
+ volverían — un total, un `having` o una página puestos y luego vaciados de niveles no
120
+ tienen que sobrevivir sueltos en la criteria que se manda. */
121
+ const grouping = by.length === 0
122
+ ? {}
123
+ : {
124
+ by,
125
+ ...(Object.keys(folds).length === 0 ? {} : { totals: folds }),
126
+ ...(having === undefined ? {} : { having }),
127
+ ...(groupSorts.length === 0 ? {} : { order: groupSorts }),
128
+ ...(limit === undefined ? {} : { pagination: { limit } }),
129
+ };
130
+ return {
131
+ ...(where === undefined ? {} : { where }),
132
+ ...(sorts.length === 0 ? {} : { order: sorts }),
133
+ ...(Object.keys(grouping).length === 0 ? {} : { grouping }),
134
+ ...(chosen.length === 0
135
+ ? {}
136
+ : {
137
+ specification: Object.fromEntries(chosen.map((name) => [name, {}])),
138
+ }),
139
+ };
140
+ }
141
+ /* Tocar cualquier control reescribe el JSON entero: las dos vistas son la misma criteria. */
142
+ function push(next) {
143
+ if (next.tree !== undefined)
144
+ setTree(next.tree);
145
+ if (next.locked !== undefined)
146
+ setLockedWhere(next.locked);
147
+ if (next.order !== undefined)
148
+ setOrder(next.order);
149
+ if (next.levels !== undefined)
150
+ setLevels(next.levels);
151
+ if (next.totals !== undefined)
152
+ setTotals(next.totals);
153
+ if (next.havingTree !== undefined)
154
+ setHavingTree(next.havingTree);
155
+ if (next.groupOrder !== undefined)
156
+ setGroupOrder(next.groupOrder);
157
+ if ("groupLimit" in next)
158
+ setGroupLimit(next.groupLimit);
159
+ if (next.fields !== undefined)
160
+ setFields(next.fields);
161
+ const built = assemble(next);
162
+ setRawText(isEmpty(built) ? "" : JSON.stringify(built, null, 2));
163
+ setRawError(null);
164
+ }
165
+ /* Escribir el JSON rehace los controles, mientras lo escrito sea una criteria. */
166
+ function updateRaw(text) {
167
+ setRawText(text);
168
+ if (text.trim() === "") {
169
+ setRawError(null);
170
+ return;
171
+ }
172
+ let parsed;
173
+ try {
174
+ parsed = JSON.parse(text);
175
+ }
176
+ catch {
177
+ setRawError(currentCopy().builder.invalidJson);
178
+ return;
179
+ }
180
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
181
+ setRawError(currentCopy().builder.notCriteria);
182
+ return;
183
+ }
184
+ setRawError(null);
185
+ const node = parsed.where === undefined ? null : treeFromFilter(parsed.where);
186
+ if (node !== null) {
187
+ setTree(node.kind === "group" ? node : { kind: "group", join: "all", children: [node] });
188
+ setLockedWhere(null);
189
+ }
190
+ else if (parsed.where !== undefined) {
191
+ setLockedWhere(parsed.where);
192
+ }
193
+ else {
194
+ setTree(blankGroup(model, scalarNames[0]));
195
+ setLockedWhere(null);
196
+ }
197
+ setOrder(parsed.order ?? []);
198
+ setLevels(parsed.grouping?.by ?? []);
199
+ setTotals(parsed.grouping?.totals ?? {});
200
+ setGroupOrder(parsed.grouping?.order ?? []);
201
+ setGroupLimit(parsed.grouping?.pagination?.limit);
202
+ const havingNode = parsed.grouping?.having === undefined ? null : treeFromFilter(parsed.grouping.having);
203
+ setHavingTree(havingNode !== null && havingNode.kind === "group"
204
+ ? havingNode
205
+ : havingNode !== null
206
+ ? { kind: "group", join: "all", children: [havingNode] }
207
+ : blankGroup(havingModel, "count"));
208
+ setFields(Object.keys(parsed.specification ?? {}));
209
+ }
210
+ const box = surface === "chrome"
211
+ ? "border-chrome-ink/20 bg-chrome text-chrome-ink"
212
+ : "border-ink/15 bg-surface text-ink";
213
+ const select = `min-w-0 flex-1 basis-40 rounded-control border px-2 py-1 text-meta ${box}`;
214
+ return (_jsxs("div", { className: "flex min-w-0 flex-col gap-3 p-3", children: [_jsxs("section", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(Rule, { label: copy.builder.filter }), _jsx("div", { className: "max-h-72 min-w-0 overflow-auto", children: lockedWhere !== null ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "mb-2 text-meta text-muted", children: copy.search.unreadable }), _jsx(ExpressionView, { node: lockedWhere, model: model })] })) : (_jsx(WhereTree, { model: model, group: tree, onChange: (next) => push({ tree: next }), ...(focus === undefined ? {} : { focus }) })) })] }), _jsxs("section", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(Rule, { label: copy.builder.order }), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1", children: [_jsx(Tags, { items: order.map((sort, index) => ({
215
+ id: `${sort.field}-${index}`,
216
+ label: sort.field,
217
+ note: sort.descending === true ? "↓" : "↑",
218
+ title: sort.descending === true
219
+ ? `${copy.builder.descending} · ${copy.builder.flip}`
220
+ : `${copy.builder.ascending} · ${copy.builder.flip}`,
221
+ onSelect: () => push({
222
+ order: order.map((one, at) => at === index ? { ...one, descending: one.descending !== true } : one),
223
+ }),
224
+ onRemove: () => push({ order: order.filter((_, at) => at !== index) }),
225
+ removeLabel: fillCopy(copy.builder.dropOrderBy, { field: sort.field }),
226
+ })) }), _jsx(AddTag, { label: copy.builder.addOrder, options: sortable.filter((name) => !order.some((one) => one.field === name)), onPick: (name) => push({ order: [...order, { field: name, descending: false }] }) })] }), order.length === 0 ? (_jsx("p", { className: "text-meta text-muted", children: copy.builder.noOrder })) : null] }), _jsxs("section", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(Rule, { label: copy.builder.groupBy }), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1", children: [levels.map((level, index) => (_jsxs(Fragment, { children: [index === 0 ? null : (_jsx("span", { className: "shrink-0 text-micro text-muted", "aria-hidden": true, children: "\u203A" })), _jsx(Tags, { items: [
227
+ {
228
+ id: `${level.field}-${index}`,
229
+ label: level.field,
230
+ ...(level.grain == null ? {} : { note: level.grain }),
231
+ ...(DATE_TYPES.has(model.fields[level.field]?.type ?? "")
232
+ ? {
233
+ title: copy.builder.changeGrain,
234
+ onSelect: () => push({
235
+ levels: levels.map((one, at) => at === index ? { ...one, grain: nextGrain(one.grain) } : one),
236
+ }),
237
+ }
238
+ : {}),
239
+ onRemove: () => push({ levels: levels.filter((_, at) => at !== index) }),
240
+ removeLabel: fillCopy(copy.builder.dropLevel, { field: level.field }),
241
+ },
242
+ ] })] }, `${level.field}-${index}`))), _jsx(AddTag, { label: copy.builder.addGroup, options: scalarNames.filter((name) => !levels.some((one) => one.field === name)), onPick: (name) => push({ levels: [...levels, { field: name }] }) })] }), levels.length === 0 ? (_jsx("p", { className: "text-meta text-muted", children: copy.builder.noGrouping })) : null] }), levels.length > 0 ? (_jsxs(_Fragment, { children: [_jsxs("section", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(Rule, { label: copy.builder.totals }), Object.keys(totals).length === 0 ? (_jsx("p", { className: "text-meta text-muted", children: copy.builder.noTotals })) : null, Object.entries(totals).map(([name, [fn, field]]) => (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [_jsx("select", { value: fn, onChange: (event) => push({
243
+ totals: { ...totals, [name]: [event.target.value, field] },
244
+ }), className: `min-w-0 flex-1 basis-28 rounded-control border px-2 py-1 text-meta ${box}`, children: FOLD_FUNCTIONS.map((one) => (_jsx("option", { value: one, children: one }, one))) }), _jsx("select", { value: field, onChange: (event) => push({ totals: { ...totals, [name]: [fn, event.target.value] } }), className: select, children: scalarNames.map((one) => (_jsx("option", { value: one, children: one }, one))) }), _jsx("span", { className: "shrink-0 text-micro text-muted", children: fillCopy(copy.builder.totalAs, { name }) }), _jsx("button", { type: "button", "aria-label": copy.builder.dropTotal, onClick: () => {
245
+ const { [name]: _dropped, ...rest } = totals;
246
+ push({ totals: rest });
247
+ }, className: "ml-auto shrink-0 text-muted", children: _jsx(Trash2, { className: "size-4" }) })] }, name))), _jsx("div", { children: _jsxs(Button, { role: "ghost", disabled: scalarNames.length === 0, onClick: () => {
248
+ let name = "total";
249
+ let index = 2;
250
+ while (name in totals)
251
+ name = `total_${index++}`;
252
+ push({
253
+ totals: { ...totals, [name]: ["sum", scalarNames[0] ?? ""] },
254
+ });
255
+ }, children: [_jsx(Plus, { className: "size-4" }), " ", copy.builder.addTotal] }) })] }), _jsxs("section", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(Rule, { label: copy.builder.filterPiles }), _jsx("p", { className: "text-meta text-muted", children: copy.builder.pilesNote }), _jsx(WhereTree, { model: havingModel, group: havingTree, onChange: (next) => push({ havingTree: next }) })] }), _jsxs("section", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(Rule, { label: copy.builder.pileOrder }), groupOrder.length === 0 ? (_jsx("p", { className: "text-meta text-muted", children: copy.builder.noPileOrder })) : null, groupOrder.map((sort, index) => (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [_jsx("select", { value: sort.field, onChange: (event) => push({
256
+ groupOrder: groupOrder.map((one, at) => at === index ? { ...one, field: event.target.value } : one),
257
+ }), className: select, children: [
258
+ ...levels.map((level) => level.field),
259
+ ...Object.keys(totals),
260
+ "count",
261
+ ].map((name) => (_jsx("option", { value: name, children: name }, name))) }), _jsxs("select", { value: sort.descending === true ? "desc" : "asc", onChange: (event) => push({
262
+ groupOrder: groupOrder.map((one, at) => at === index
263
+ ? { ...one, descending: event.target.value === "desc" }
264
+ : one),
265
+ }), className: `min-w-0 flex-1 basis-32 rounded-control border px-2 py-1 text-meta ${box}`, children: [_jsx("option", { value: "asc", children: copy.builder.ascending }), _jsx("option", { value: "desc", children: copy.builder.descending })] }), _jsx("button", { type: "button", "aria-label": copy.builder.dropOrder, onClick: () => push({ groupOrder: groupOrder.filter((_, at) => at !== index) }), className: "ml-auto shrink-0 text-muted", children: _jsx(Trash2, { className: "size-4" }) })] }, index))), _jsx("div", { children: _jsxs(Button, { role: "ghost", onClick: () => push({
266
+ groupOrder: [...groupOrder, { field: "count", descending: true }],
267
+ }), children: [_jsx(Plus, { className: "size-4" }), " ", copy.builder.addPileOrder] }) })] }), _jsxs("section", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(Rule, { label: copy.builder.pilePage }), _jsx("p", { className: "text-meta text-muted", children: copy.builder.pilePageNote }), _jsxs("label", { className: "flex items-center gap-2 text-meta", children: [_jsx("input", { type: "checkbox", checked: groupLimit !== undefined, onChange: (event) => push({ groupLimit: event.target.checked ? 20 : undefined }) }), copy.builder.limitPiles] }), groupLimit !== undefined ? (_jsx("input", { type: "number", min: 0, value: groupLimit, onChange: (event) => push({ groupLimit: Number(event.target.value) }), className: `w-32 rounded-control border px-2 py-1 text-meta ${box}` })) : null] })] })) : null, _jsxs("section", { className: "flex min-w-0 flex-col gap-2", children: [_jsx(Rule, { label: copy.builder.fields }), _jsx("p", { className: "text-meta text-muted", children: fields.length === 0
268
+ ? copy.builder.allFields
269
+ : fillCopy(copy.builder.someFields, {
270
+ shown: fields.length,
271
+ total: names.length,
272
+ }) }), _jsx("div", { className: "flex flex-wrap gap-2", children: names.map((name) => {
273
+ const chosen = fields.includes(name);
274
+ return (_jsx("button", { type: "button", onClick: () => push({
275
+ fields: chosen ? fields.filter((one) => one !== name) : [...fields, name],
276
+ }), className: `rounded-pill border px-2 py-0.5 text-micro ${chosen ? "border-accent/40 bg-accent/18 text-ink" : `text-muted ${box}`}`, children: name }, name));
277
+ }) })] }), _jsx(JsonEditor, { value: rawText, onChange: updateRaw, error: rawError, height: "14rem", label: copy.builder.json }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { role: "primary", disabled: rawError !== null, onClick: () => onApply(rawText.trim() === "" ? undefined : JSON.parse(rawText)), children: copy.builder.apply }), onCancel === undefined ? null : (_jsx(Button, { role: "ghost", onClick: onCancel, children: copy.builder.cancel }))] })] }));
278
+ }
@@ -0,0 +1,22 @@
1
+ import { type CriteriaJoin, type CriteriaPath, type SearchCriteria } from "./criteria.js";
2
+ import type { Surface } from "../../foundations/types.js";
3
+ export type CriteriaChipsProps = {
4
+ /** Lo que la barra tiene puesto. Grupos y filtros mezclados: acá se separan. */
5
+ criteria: readonly SearchCriteria[];
6
+ /** Abrir el constructor sobre una instancia. */
7
+ onOpen?: (path: CriteriaPath) => void;
8
+ /** Cambiar cómo se juntan los de un nodo. */
9
+ onJoin?: (path: CriteriaPath, join: CriteriaJoin) => void;
10
+ /** Sacar una instancia, o una rama entera. */
11
+ onRemove?: (path: CriteriaPath) => void;
12
+ /**
13
+ * Cuál nodo está ABIERTO: dónde va a caer lo próximo que se escriba.
14
+ *
15
+ * Se marca porque si no, escribir `#and` y después una condición la mete en un
16
+ * lugar que nadie eligió mirando: el grupo existe, la condición entra, y el
17
+ * único que sabía dónde era el que la escribió.
18
+ */
19
+ open?: CriteriaPath;
20
+ surface?: Surface;
21
+ };
22
+ export declare function CriteriaChips({ criteria, onOpen, onJoin, onRemove, open, surface, }: CriteriaChipsProps): import("react").JSX.Element;
@@ -0,0 +1,64 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { filtersOf, groupsOf, } from "./criteria.js";
4
+ import { JoinWord } from "./JoinWord.js";
5
+ import { useUiCopy } from "../../foundations/copy.js";
6
+ import { bySurface, useSurface } from "../../foundations/surface.js";
7
+ import { ChevronRight, Layers, ListFilter, X } from "lucide-react";
8
+ export function CriteriaChips({ criteria, onOpen, onJoin, onRemove, open, surface, }) {
9
+ const sup = useSurface(surface);
10
+ const copy = useUiCopy();
11
+ const groups = groupsOf(criteria);
12
+ const filters = filtersOf(criteria);
13
+ /* La ruta se cuenta sobre la lista ENTERA y no sobre la mitad dibujada: es la
14
+ identidad de la instancia, y una identidad que cambia según de qué lado se
15
+ la mire no identifica nada. */
16
+ const pathOf = (one) => [
17
+ criteria.findIndex((each) => each.id === one.id),
18
+ ];
19
+ return (_jsxs(_Fragment, { children: [groups.length === 0 ? null : (_jsx(Zone, { lead: copy.search.groupedBy, icon: Layers, surface: sup, children: groups.map((one, at) => (_jsxs("span", { className: "flex min-w-0 items-center gap-1", children: [at === 0 ? null : (_jsx(ChevronRight, { className: "size-3 shrink-0 text-muted", strokeWidth: 2, "aria-hidden": true })), _jsx(Chip, { label: one.label, path: pathOf(one), ...(onOpen === undefined ? {} : { onOpen }), ...(onRemove === undefined ? {} : { onRemove }), surface: sup })] }, one.id))) })), filters.length === 0 ? null : (_jsx(Zone, { lead: copy.search.narrowedBy, icon: ListFilter, surface: sup, children: filters.map((one) => (_jsx(Node, { node: one, path: pathOf(one), ...(onOpen === undefined ? {} : { onOpen }), ...(onJoin === undefined ? {} : { onJoin }), ...(onRemove === undefined ? {} : { onRemove }), ...(open === undefined ? {} : { open }), surface: sup }, one.id))) }))] }));
20
+ }
21
+ function Node({ node, path, onOpen, onJoin, onRemove, open, surface, }) {
22
+ const copy = useUiCopy();
23
+ if (node.kind !== "join") {
24
+ /* Una instancia que no lleva expresión no tiene qué editar: el constructor
25
+ edita lo que VIAJA, y ésta la contesta quien la declaró. Se ve y se saca
26
+ como cualquiera; lo que no hace es prometer una puerta que da a nada. */
27
+ const editable = node.kind === "group" || node.where !== undefined;
28
+ return (_jsx(Chip, { label: node.label, path: path, ...(onOpen === undefined || !editable ? {} : { onOpen }), ...(onRemove === undefined ? {} : { onRemove }), surface: surface }));
29
+ }
30
+ /* El nodo abierto se marca: es donde va a caer lo próximo. Un anillo y no un
31
+ fondo, porque adentro hay fichas que ya tienen el suyo. */
32
+ const isOpen = open !== undefined && open.join(".") === path.join(".");
33
+ return (_jsxs("span", { className: `inline-flex min-w-0 flex-wrap items-center gap-1 rounded-control border px-1.5 py-0.5 ${bySurface(surface, "border-line bg-ink/4", "border-chrome-ink/25 bg-chrome-ink/8")} ${isOpen ? "ring-1 ring-primary/60" : ""}`, children: [_jsx(JoinWord, { join: node.join, ...(onJoin === undefined ? {} : { onChange: (next) => onJoin(path, next) }), surface: surface }), node.of.length === 0 ? (
34
+ /* Un nodo recién abierto no tiene nada adentro, y un corchete vacío se
35
+ lee como un error. Dice que está esperando. */
36
+ _jsx("span", { className: `shrink-0 text-micro italic ${bySurface(surface, "text-muted", "text-chrome-muted")}`, children: copy.search.landsHere })) : null, node.of.map((child, at) => (_jsx(Node, { node: child, path: [...path, at], ...(onOpen === undefined ? {} : { onOpen }), ...(onJoin === undefined ? {} : { onJoin }), ...(onRemove === undefined ? {} : { onRemove }), ...(open === undefined ? {} : { open }), surface: surface }, child.id)))] }));
37
+ }
38
+ /** El contexto, con su palabra adelante. Sin la palabra, las fichas no dicen de
39
+ * qué son. No lleva caja: una caja adentro del campo las separa de él. */
40
+ function Zone({ lead, icon: Icon, surface, children, }) {
41
+ return (_jsxs("span", { className: "inline-flex min-w-0 shrink items-center gap-1", children: [_jsx(Icon, { className: `size-3 shrink-0 ${bySurface(surface, "text-muted", "text-chrome-muted")}`, strokeWidth: 2, "aria-hidden": true }), _jsx("span", { className: `shrink-0 text-micro ${bySurface(surface, "text-muted", "text-chrome-muted")}`, children: lead }), _jsx("span", { className: "flex min-w-0 flex-wrap items-center gap-1", children: children })] }));
42
+ }
43
+ function Chip({ label, path, onOpen, onRemove, surface, }) {
44
+ const copy = useUiCopy();
45
+ const shell = `inline-flex min-w-0 shrink-0 items-center gap-1 rounded-pill border py-0.5 text-micro ${bySurface(surface, "border-accent/40 bg-accent/18 text-ink", "border-chrome-link/35 bg-chrome-link/18 text-chrome-link")} ${onRemove === undefined ? "px-2" : "pr-0.5 pl-2"}`;
46
+ const remove = onRemove === undefined ? null : (_jsx("button", { type: "button", onClick: (event) => {
47
+ /* Sin esto, sacarla también la abre: el click llega acá y después
48
+ sube. */
49
+ event.stopPropagation();
50
+ onRemove(path);
51
+ }, "aria-label": copy.search.removeFilter.replace("{label}", label), className: `inline-flex size-4 shrink-0 items-center justify-center rounded-pill transition-colors duration-(--duration-instant) ${bySurface(surface, "hover:bg-primary/20", "hover:bg-chrome-link/25")}`, children: _jsx(X, { className: "size-3", strokeWidth: 2.5 }) }));
52
+ if (onOpen === undefined) {
53
+ return (_jsxs("span", { className: shell, children: [_jsx("span", { className: "min-w-0 truncate", children: label }), remove] }));
54
+ }
55
+ /* Un botón adentro de otro es HTML inválido —el navegador cierra el de afuera
56
+ antes de tiempo y el click deja de llegar—, así que la ficha que se abre Y se
57
+ saca es un `span` con `role`, y la × adentro es el único botón de verdad. */
58
+ return (_jsxs("span", { role: "button", tabIndex: 0, onClick: () => onOpen(path), onKeyDown: (event) => {
59
+ if (event.key === "Enter" || event.key === " ") {
60
+ event.preventDefault();
61
+ onOpen(path);
62
+ }
63
+ }, title: copy.search.openBuilderHere, className: `${shell} cursor-pointer`, children: [_jsx("span", { className: "min-w-0 truncate", children: label }), remove] }));
64
+ }
@@ -0,0 +1,142 @@
1
+ import type { CriteriaJoin } from "./criteria.js";
2
+ import { type ModelDefinition } from "./model.js";
3
+ /** Una condición, tal como la declara el motor. `operator` sale del `ops` del campo, así que
4
+ * lo que el constructor ofrece es exactamente lo que el modelo acepta. */
5
+ export interface BuiltCondition {
6
+ field: string;
7
+ operator: string;
8
+ value: unknown;
9
+ }
10
+ /** Lo que el filtro puede ser en su forma real: una condición, un grupo `all`/`any` de más
11
+ * filtros —lo que el árbol arma, a cualquier profundidad—, o un `negate` que llegó de otro
12
+ * lado y que el árbol no fabrica, pero que este componente sabe mostrar y editar como JSON. */
13
+ export type BuiltFilter = BuiltCondition | {
14
+ all: BuiltFilter[];
15
+ } | {
16
+ any: BuiltFilter[];
17
+ } | {
18
+ negate: BuiltFilter;
19
+ };
20
+ /** Si esto es una de las formas que el filtro reconoce — condición, o grupo `all`/`any`/
21
+ * `negate`. Un JSON válido que no es ninguna de éstas —`{}`, `{"foo": 1}`, un array suelto—
22
+ * no es un filtro que este componente sepa dibujar, y se trata como error de forma en vez de
23
+ * intentar mostrar cualquier cosa. */
24
+ export declare function isRecognizedFilter(value: unknown): value is BuiltFilter;
25
+ export interface TreeCondition {
26
+ kind: "condition";
27
+ field: string;
28
+ operator: string;
29
+ value: string;
30
+ /** El otro extremo, para los operadores que comparan contra dos —hoy `between`—. Vacío en
31
+ * todos los demás, donde no significa nada. */
32
+ upper?: string;
33
+ }
34
+ export interface TreeGroup {
35
+ kind: "group";
36
+ /** Todas, cualquiera, o NINGUNA. Las tres, y las mismas que la barra: negar
37
+ * algo se podía escribir sólo en el JSON, y una pregunta que el editor no
38
+ * sabe construir es una pregunta que la mayoría no va a hacer. */
39
+ join: CriteriaJoin;
40
+ children: TreeNode[];
41
+ }
42
+ export type TreeNode = TreeCondition | TreeGroup;
43
+ export declare function blankCondition(model: ModelDefinition, first: string | undefined): TreeCondition;
44
+ export declare function blankGroup(model: ModelDefinition, first: string | undefined): TreeGroup;
45
+ /** El árbol como el filtro que viaja.
46
+ *
47
+ * in condición sola
48
+ * out esa condición, sin envolver
49
+ *
50
+ * in grupo con dos hijos, join «any»
51
+ * out {any: [ … , … ]}
52
+ *
53
+ * Un grupo de un solo hijo no se envuelve —es ese hijo—, y un grupo sin hijos desaparece: ni
54
+ * «todas» ni «cualquiera» vacío es un filtro que alguien pidió. La regla vale en cualquier
55
+ * nivel, no sólo en la raíz, así que anidar un grupo de una condición sola es lo mismo que no
56
+ * haberlo anidado. */
57
+ export declare function treeToFilter(node: TreeNode): BuiltFilter | undefined;
58
+ /** El filtro leído de vuelta a un árbol.
59
+ *
60
+ * in {field: "name", operator: "=", value: "labs"}
61
+ * out TreeCondition
62
+ *
63
+ * in {any: [{all: [cond1, cond2]}, cond3]} grupo adentro de grupo
64
+ * out TreeGroup(any) con un TreeGroup(all) y una condición como hijos
65
+ *
66
+ * in {negate: {any: [cond1, cond2]}}
67
+ * out TreeGroup(not) con las dos condiciones como hijos
68
+ *
69
+ * El `negate` de un `any` se abre en el grupo «ninguna» y no queda como un grupo
70
+ * adentro de otro: es lo que `treeToFilter` vuelve a escribir, así que ida y
71
+ * vuelta dan lo mismo. Lo que NO se puede leer —un objeto que no es ni una
72
+ * condición ni ninguna de las tres formas— devuelve `null`, y ahí el componente
73
+ * lo muestra de sólo lectura sin dejar de poder tocarlo como JSON.
74
+ */
75
+ export declare function treeFromFilter(filter: unknown): TreeNode | null;
76
+ /** El filtro dicho en una línea, para una ficha o un rótulo.
77
+ *
78
+ * in {field: "row_count", operator: ">", value: 1000}, con el modelo
79
+ * out 'filas mayor que 1000'
80
+ *
81
+ * in {all: [cond1, cond2]}
82
+ * out 'el nombre contiene labs y filas mayor que 1000'
83
+ *
84
+ * in {any: [cond1, cond2]}
85
+ * out 'el nombre es a o el nombre es b'
86
+ *
87
+ * Cada condición la dice `sayCondition`, que es la misma que usa la barra: sin
88
+ * eso, lo escrito en la barra y lo armado en el diálogo se leen distinto aunque
89
+ * filtren igual, y nadie sabe cuál de las dos frases es la que manda.
90
+ *
91
+ * **El modelo es el que sabe cómo se llama un campo.** Sin él queda el nombre
92
+ * crudo —`row_count`—, que es lo mejor que se puede decir sin preguntarle a
93
+ * nadie.
94
+ *
95
+ * Un grupo anidado se encierra entre paréntesis, que es lo que evita que «a y (b o c)» se
96
+ * lea como «(a y b) o c». Para más de tres condiciones se corta: una ficha que no entra en
97
+ * la barra no dice nada — para eso está abrir el constructor y verlo entero. */
98
+ export declare function describeFilter(node: BuiltFilter, model?: ModelDefinition): string;
99
+ /** El filtro dibujado tal como es, recursivo: una condición es una línea, `all`/`any` son un
100
+ * grupo con su rótulo y sus hijos adentro, `negate` es «no» envolviendo al que niega.
101
+ *
102
+ * Es la mitad de sólo lectura de este archivo, y también sirve sola: mostrar qué dice un
103
+ * filtro que ya está puesto no necesita abrir el constructor. */
104
+ export declare function ExpressionView({ node, model, depth, }: {
105
+ node: BuiltFilter;
106
+ /** Quién sabe cómo se llama un campo. Sin él queda el nombre crudo. */
107
+ model?: ModelDefinition;
108
+ depth?: number;
109
+ }): import("react").JSX.Element;
110
+ export declare function WhereTree({ model, group, onChange, depth, focus, }: {
111
+ model: ModelDefinition;
112
+ group: TreeGroup;
113
+ onChange: (next: TreeGroup) => void;
114
+ depth?: number;
115
+ /**
116
+ * El campo de la condición que hay que mostrar PRIMERO: la que alguien tocó en
117
+ * la barra.
118
+ *
119
+ * Sin esto, tocar una ficha abría el constructor en la regla uno y había que
120
+ * buscar entre doce la que uno venía mirando — que es la manera más rápida de
121
+ * editar la equivocada. Se marca y se lleva a la vista; no se abre en un modo
122
+ * distinto, porque el constructor es el mismo.
123
+ *
124
+ * Marca a CUALQUIER profundidad: el árbol se lo pasa a los grupos que anida.
125
+ *
126
+ * **Es un nombre de campo, no una ruta**, así que dos condiciones sobre el
127
+ * mismo campo en ramas distintas se marcan las dos y la vista va a la primera.
128
+ * Distinguirlas pide que el árbol y las instancias de la barra compartan la
129
+ * identidad, y hoy no la comparten: `whereOf` arma el filtro salteándose los
130
+ * niveles y lo que no viaja, así que la posición en uno no es la posición en
131
+ * el otro.
132
+ */
133
+ focus?: string;
134
+ }): import("react").JSX.Element;
135
+ export declare function FilterBuilder({ model, initial, onApply, onCancel, }: {
136
+ model: ModelDefinition;
137
+ /** El filtro que ya estaba puesto, si había uno. Reabrir el constructor con esto adentro
138
+ * tiene que seguir donde quedó, no empezar en blanco. */
139
+ initial?: BuiltFilter;
140
+ onApply: (where: BuiltFilter | undefined) => void;
141
+ onCancel?: () => void;
142
+ }): import("react").JSX.Element;