@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,387 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { at, criteriaId, filterCriteria, removeAt, replaceAt, } from "./criteria.js";
4
+ import { CriteriaChips } from "./CriteriaChips.js";
5
+ import { describeCondition, opLabel, queryFields, readTyped, suggest, withoutValue, } from "./query.js";
6
+ import { useUiCopy } from "../../foundations/copy.js";
7
+ import { bySurface, useSurface } from "../../foundations/surface.js";
8
+ import { Tags } from "../../primitives/tag/Tags.js";
9
+ import { ChevronRight, CornerDownRight, ListFilter, Search, SlidersHorizontal, } from "lucide-react";
10
+ import { Fragment, useEffect, useId, useRef, useState } from "react";
11
+ function Option({ o, onSelect }) {
12
+ const Icon = o.icon;
13
+ return (_jsxs("button", { type: "button", disabled: o.disabled, onClick: () => onSelect?.(o.id), className: `flex w-full items-start gap-2 rounded-control px-1.5 py-1 text-left transition-colors duration-(--duration-instant) disabled:pointer-events-none disabled:opacity-45 hover:bg-primary/8 ${o.checked === true ? "bg-primary/6" : ""}`, children: [_jsx("span", { className: "mt-0.5 flex size-(--size-icon-sm) shrink-0 items-center justify-center", children: o.checked === true ? (_jsx("span", { className: "size-1.5 rounded-pill bg-primary" })) : Icon !== undefined ? (_jsx(Icon, { className: "size-(--size-icon-sm) text-muted", strokeWidth: 2 })) : (_jsx("span", { className: "size-3 rounded-pill border border-line-strong" })) }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: `truncate text-meta ${o.checked === true ? "font-medium text-ink" : "text-ink"}`, children: o.label }), o.badge === undefined ? null : (_jsx("span", { className: "inline-flex shrink-0 items-center rounded-pill border border-warn/30 bg-warn/10 px-2 py-0.5 text-micro text-warn", children: o.badge }))] }), o.hint === undefined ? null : (_jsx("span", { className: "mt-0.5 block truncate text-micro text-muted", children: o.hint }))] })] }));
14
+ }
15
+ /** Un grupo del panel. Plegable cuando lo pide —una fecha, que trae once
16
+ * opciones— y abierto de entrada si ya tiene algo tildado: si no, lo puesto
17
+ * queda escondido detrás de un chevron y hay que adivinar dónde está. */
18
+ function Group({ group, first, onSelect, }) {
19
+ const active = group.options.some((o) => o.checked === true);
20
+ const [open, setOpen] = useState(active);
21
+ const options = (_jsx(_Fragment, { children: group.options.map((o) => (_jsxs(Fragment, { children: [o.divider === true ? _jsx("div", { className: "my-1 h-px bg-line", role: "separator" }) : null, _jsx(Option, { o: o, onSelect: onSelect })] }, o.id))) }));
22
+ return (_jsxs("div", { className: "flex flex-col gap-0.5", children: [first ? null : _jsx("div", { className: "my-1 h-px bg-line", role: "separator" }), group.label === undefined ? null : group.collapsible !== true ? (
23
+ /* El rótulo del grupo va en la columna del TEXTO, no en la del borde.
24
+ La banda de la columna ya es el título (medium, sobre la línea). Este
25
+ es un escalón abajo: peso normal, y el mismo hueco de la marca para
26
+ que «mostrar sólo» arranque donde arranca «traídos». */
27
+ _jsxs("div", { className: "flex items-center gap-2 px-1.5 pt-1 pb-0.5 text-micro text-muted", children: [_jsx("span", { className: "size-(--size-icon-sm) shrink-0", "aria-hidden": true }), group.label] })) : (_jsxs("button", { type: "button", onClick: () => setOpen(!open), "aria-expanded": open, className: "flex items-center gap-2 rounded-control px-1.5 py-1 text-left transition-colors duration-(--duration-instant) hover:bg-primary/8", children: [_jsx(ChevronRight, { className: `size-(--size-icon-sm) shrink-0 text-muted transition-transform duration-(--duration-instant) ${open ? "rotate-90" : ""}`, strokeWidth: 2, "aria-hidden": true }), _jsx("span", { className: `min-w-0 flex-1 truncate text-meta ${active ? "font-medium text-ink" : "text-ink"}`, children: group.label }), !active ? null : (_jsx("span", { className: "inline-flex shrink-0 items-center rounded-pill border border-accent/40 bg-accent/18 px-2 py-0.5 text-micro tabular-nums text-ink", children: group.options.filter((o) => o.checked === true).length }))] })), group.collapsible === true && !open ? null : (_jsx("div", { className: group.collapsible === true
28
+ ? "flex flex-col gap-0.5 pl-(--spacing-indent)"
29
+ : "flex flex-col gap-0.5", children: options }))] }));
30
+ }
31
+ export function SearchBar({ facets, model, criteria, onCriteriaChange, placeholder, filters, grouping, onOpenGroup, favorites, onFilterSelect, onFavoriteSelect, onOpenBuilder, trailing, surface, }) {
32
+ const copy = useUiCopy();
33
+ const sup = useSurface(surface);
34
+ const [text, setText] = useState("");
35
+ const [panel, setPanel] = useState(false);
36
+ /** El paso en el que está la pregunta que se escribe: sin campo, con campo, o
37
+ * con campo y operador. */
38
+ const [step, setStep] = useState({});
39
+ /** Cuál de lo ofrecido está marcado. Arranca en el primero, que es el que
40
+ * Enter toma sin bajar ni una vez. */
41
+ const [active, setActive] = useState(0);
42
+ /**
43
+ * El grupo ABIERTO: dónde cae lo próximo que se escriba.
44
+ *
45
+ * `null` es «al final de la lista», que es lo normal. Se abre con `#and` y se
46
+ * cierra con Escape o eligiendo otro — un grupo que no se puede cerrar es una
47
+ * trampa en la que todo lo que se escriba después queda adentro sin que nadie
48
+ * lo haya pedido.
49
+ */
50
+ const [openGroup, setOpenGroup] = useState(null);
51
+ /* Cuál es el nodo abierto se DERIVA de la ruta y del árbol, y no se guarda
52
+ aparte: guardado, el día que alguien le cambia la conjunción desde la ficha
53
+ quedan dos verdades sobre la misma cosa. */
54
+ const openNode = openGroup === null ? undefined : at(criteria, openGroup);
55
+ const openJoin = openNode?.kind === "join" ? openNode.join : undefined;
56
+ /**
57
+ * Cerrar el nodo abierto: lo que siga vuelve a caer afuera.
58
+ *
59
+ * Si quedó VACÍO se lo saca. Abrirlo y no poner nada deja un corchete que no
60
+ * agrupa nada, y alguien tendría que descubrir que se saca con la × de una
61
+ * ficha que no dice nada adentro.
62
+ */
63
+ function closeGroup() {
64
+ if (openGroup !== null && openNode?.kind === "join" && openNode.of.length === 0) {
65
+ onCriteriaChange(removeAt(criteria, openGroup));
66
+ }
67
+ setOpenGroup(null);
68
+ input.current?.focus();
69
+ }
70
+ /* Cuántas cosas hay puestas, para el número del embudo. Se cuenta lo de
71
+ ARRIBA y no las hojas: «tres» tiene que ser lo que se ve en la barra, y lo
72
+ que se ve es un grupo, no sus dos condiciones. */
73
+ const puestos = criteria.length;
74
+ const input = useRef(null);
75
+ const box = useRef(null);
76
+ const id = useId();
77
+ /**
78
+ * Los campos por los que se puede preguntar.
79
+ *
80
+ * Con un modelo, salen de él con su tipo, sus operadores y sus relaciones —y
81
+ * entonces la barra sabe que «total» acepta «menor que» y «estado» sólo
82
+ * tres valores. Sin modelo, una faceta es un campo de texto y nada más: es
83
+ * exactamente lo que una lista podía declarar hasta ahora, dibujado por el
84
+ * mismo camino. Dos caminos serían dos barras.
85
+ */
86
+ const fields = model === undefined
87
+ ? facets.map((facet) => ({
88
+ field: facet.id,
89
+ label: facet.label,
90
+ kind: "text",
91
+ /* Las tres que se le pueden hacer a un texto sin saber nada más de
92
+ él. Con una sola —«contiene»— el paso de la comparación ofrecía un
93
+ botón y nada que elegir, que es peor que no tener el paso. Un modelo
94
+ publica las suyas y éstas no se usan. */
95
+ ops: ["like", "=", "!="],
96
+ }))
97
+ : queryFields(model, copy.locale);
98
+ /* Lo marcado vuelve al primero cuando cambia lo ofrecido: dejar la marca en el
99
+ cuarto renglón mientras se sigue escribiendo hace que Enter tome algo que ya
100
+ no es lo que dice la pantalla. */
101
+ const suggestions = suggest({
102
+ ...(step.field === undefined ? {} : { field: step.field }),
103
+ ...(step.op === undefined ? {} : { op: step.op }),
104
+ text,
105
+ }, fields);
106
+ /* Lo escrito, ya leído: hace falta acá para saber si se está eligiendo un
107
+ ATRIBUTO —lo que empieza con `#`— y dibujar fichas en vez de renglones. */
108
+ const asked = readTyped(text);
109
+ /* Se abre cuando hay algo que ofrecer Y alguien empezó: escribiendo, o
110
+ eligiendo un campo, que ya es haber empezado. */
111
+ const open = suggestions.length > 0 && (text.trim() !== "" || step.field !== undefined);
112
+ /* Se eligen FICHAS cuando lo que se elige es un atributo o una comparación, y
113
+ renglones cuando lo que se elige es una pregunta entera. */
114
+ const asTags = asked.attribute === true || (step.field !== undefined && step.op === undefined);
115
+ const shown = suggestions.map((one) => one.id).join("\u0000");
116
+ useEffect(() => {
117
+ setActive(0);
118
+ }, [shown]);
119
+ /** Una columna sin opciones no es una columna: aparecería el disparador y el
120
+ * panel abriría en blanco. Pasar `[]` es lo que hace quien tiene una lista que
121
+ * no declara ningún recorte, y tiene que leerse igual que no pasar nada. */
122
+ const withOptions = (groups) => groups.some((group) => group.options.length > 0);
123
+ const columns = [
124
+ ...(filters === undefined || !withOptions(filters)
125
+ ? []
126
+ : [{ title: copy.search.filters, groups: filters, onSelect: onFilterSelect }]),
127
+ ...(grouping === undefined ? [] : [{ title: copy.search.groupBy, content: grouping }]),
128
+ ...(favorites === undefined || !withOptions(favorites)
129
+ ? []
130
+ : [{ title: copy.search.favorites, groups: favorites, onSelect: onFavoriteSelect }]),
131
+ ];
132
+ useEffect(() => {
133
+ if (!panel)
134
+ return;
135
+ function outside(e) {
136
+ if (box.current?.contains(e.target) === true)
137
+ return;
138
+ setPanel(false);
139
+ }
140
+ function onEscape(e) {
141
+ if (e.key === "Escape")
142
+ setPanel(false);
143
+ }
144
+ document.addEventListener("mousedown", outside);
145
+ document.addEventListener("keydown", onEscape);
146
+ return () => {
147
+ document.removeEventListener("mousedown", outside);
148
+ document.removeEventListener("keydown", onEscape);
149
+ };
150
+ }, [panel]);
151
+ /**
152
+ * UNA PREGUNTA ENTERA entra como instancia, y la barra vuelve al principio.
153
+ *
154
+ * Cae adentro del grupo ABIERTO si hay uno —lo que hace `#and`— y al final de
155
+ * la lista si no. Es lo único que la barra decide sobre dónde van las cosas:
156
+ * el resto lo decide quien tiene la lista.
157
+ */
158
+ function commit(field, op, value) {
159
+ const one = filterCriteria(
160
+ /* El que no lleva valor viaja con `true`, que es lo que el motor espera de
161
+ «está vacío»: mandarle la cadena vacía sería preguntar por el campo que
162
+ vale "". */
163
+ { field: field.field, operator: op, value: withoutValue(op) ? true : value }, describeCondition(field, op, value));
164
+ onCriteriaChange(openGroup === null
165
+ ? [...criteria, one]
166
+ : replaceAt(criteria, openGroup, addTo(at(criteria, openGroup), one)));
167
+ setText("");
168
+ setStep({});
169
+ input.current?.focus();
170
+ }
171
+ /** Lo elegido en el desplegable, según de qué clase sea. */
172
+ function pick(suggestion) {
173
+ if (suggestion.kind === "field") {
174
+ setStep({ field: suggestion.field });
175
+ setText("");
176
+ input.current?.focus();
177
+ return;
178
+ }
179
+ if (suggestion.kind === "op") {
180
+ setStep((current) => ({ ...current, op: suggestion.op }));
181
+ input.current?.focus();
182
+ return;
183
+ }
184
+ if (suggestion.kind === "group") {
185
+ /* Abrir un grupo es poner un nodo VACÍO y recordar que lo que venga cae
186
+ adentro. Vacío y no con lo de antes metido a la fuerza: quien escribe
187
+ `#and` está diciendo «lo que sigue va junto», no «agrupá lo que ya
188
+ puse». */
189
+ const node = {
190
+ id: criteriaId("n"),
191
+ kind: "join",
192
+ join: suggestion.join,
193
+ of: [],
194
+ };
195
+ onCriteriaChange([...criteria, node]);
196
+ setOpenGroup([criteria.length]);
197
+ onOpenGroup?.(suggestion.join);
198
+ setText("");
199
+ input.current?.focus();
200
+ return;
201
+ }
202
+ commit(suggestion.field, suggestion.op, suggestion.value);
203
+ }
204
+ /** El nodo con una instancia más adentro. */
205
+ function addTo(node, one) {
206
+ return node === undefined || node.kind !== "join"
207
+ ? one
208
+ : { ...node, of: [...node.of, one] };
209
+ }
210
+ return (_jsxs("div", { ref: box, className: "relative flex min-w-0 items-center gap-2", children: [_jsxs("div", { className: `flex min-h-(--size-control-md) w-full min-w-0 items-stretch rounded-box border transition-colors duration-(--duration-instant) ${bySurface(sup, "border-line-strong bg-surface focus-within:border-primary/60",
211
+ /* Sobre el cromo lo que define al campo es el BORDE, no el relleno. El
212
+ azul ya es casi negro, así que hundirlo no da contraste (1,05:1) y
213
+ levantarlo da 1,21:1 — en los dos casos el campo no se lee como un
214
+ campo, que era «no llama la atención». La tinta al 45 % es el primer
215
+ borde que pasa 3:1 contra el relleno Y contra el cromo, que es lo que
216
+ 1.4.11 le pide a un control. */
217
+ "border-chrome-ink/45 bg-chrome-ink/8 focus-within:border-chrome-link")}`, children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-1.5 py-1 pl-2.5", children: [_jsx(Search, { className: "size-(--size-icon-md) shrink-0 text-muted", strokeWidth: 2, "aria-hidden": true }), _jsx(CriteriaChips, { criteria: criteria, surface: sup, ...(openGroup === null ? {} : { open: openGroup }), ...(onOpenBuilder === undefined ? {} : { onOpen: onOpenBuilder }), onJoin: (path, join) => {
218
+ const node = at(criteria, path);
219
+ if (node?.kind !== "join")
220
+ return;
221
+ onCriteriaChange(replaceAt(criteria, path, { ...node, join }));
222
+ }, onRemove: (path) => {
223
+ onCriteriaChange(removeAt(criteria, path));
224
+ /* Si lo que se sacó era el grupo abierto, deja de estarlo: escribir
225
+ adentro de algo que ya no existe es escribir en el aire. */
226
+ if (openGroup !== null && path.join(".") === openGroup.join(".")) {
227
+ setOpenGroup(null);
228
+ }
229
+ } }), _jsx(Tags, { surface: sup, items: [
230
+ /* ADENTRO DE QUÉ se está escribiendo, primero: es lo que envuelve
231
+ al resto del borrador, y es la única ficha que se saca para
232
+ CERRAR en vez de para deshacer un paso. Sin ella, `#and` abría
233
+ un grupo y lo único que lo decía era que la próxima pregunta
234
+ aparecía en un lugar que nadie eligió mirando. */
235
+ ...(openJoin === undefined
236
+ ? []
237
+ : [
238
+ {
239
+ id: "draft-group",
240
+ label: copy.search.joins[openJoin === "not" ? "none" : openJoin],
241
+ icon: CornerDownRight,
242
+ selected: true,
243
+ onRemove: closeGroup,
244
+ removeLabel: copy.search.closeGroup,
245
+ },
246
+ ]),
247
+ ...(step.field === undefined
248
+ ? []
249
+ : [
250
+ {
251
+ id: "draft-field",
252
+ label: step.field.label,
253
+ ...(step.field.through === undefined
254
+ ? {}
255
+ : { note: step.field.through }),
256
+ selected: true,
257
+ onRemove: () => {
258
+ setStep({});
259
+ input.current?.focus();
260
+ },
261
+ removeLabel: copy.search.dropField,
262
+ },
263
+ ]),
264
+ ...(step.field === undefined || step.op === undefined
265
+ ? []
266
+ : [
267
+ {
268
+ id: "draft-op",
269
+ label: opLabel(step.op),
270
+ note: step.op,
271
+ selected: true,
272
+ onRemove: () => {
273
+ setStep({ field: step.field });
274
+ input.current?.focus();
275
+ },
276
+ removeLabel: copy.search.dropOp,
277
+ },
278
+ ]),
279
+ ] }), _jsx("input", { ref: input, value: text, onChange: (e) => setText(e.target.value), onKeyDown: (e) => {
280
+ /* Retroceso con el campo vacío saca la ÚLTIMA instancia: el gesto
281
+ que ya existe en cualquier campo de fichas y el único que se
282
+ descubre sin que nadie lo explique. La última y no la última de
283
+ su clase — ahora son una sola lista, así que no hay un orden de
284
+ preferencia que recordar. */
285
+ if (e.key === "Backspace" && text === "") {
286
+ if (criteria.length > 0) {
287
+ onCriteriaChange(removeAt(criteria, [criteria.length - 1]));
288
+ }
289
+ return;
290
+ }
291
+ /* Las flechas RECORREN lo ofrecido y Enter toma lo que esté
292
+ marcado. Sin esto, escribir «#nom» y bajar no hacía nada: la
293
+ lista se veía y no se podía tocar sin el mouse, que es
294
+ justamente lo que una barra de búsqueda no puede pedir. */
295
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
296
+ if (suggestions.length === 0)
297
+ return;
298
+ e.preventDefault();
299
+ setActive((current) => {
300
+ const next = e.key === "ArrowDown" ? current + 1 : current - 1;
301
+ /* Da la vuelta: con cinco campos, bajar seis veces vuelve al
302
+ primero en vez de quedarse trabado en el último. */
303
+ return (next + suggestions.length) % suggestions.length;
304
+ });
305
+ return;
306
+ }
307
+ /* Enter toma lo MARCADO, que arranca siendo lo primero — y lo
308
+ primero es lo más probable: con «test» el primer campo de texto,
309
+ con «<= 100» el único que puede contestarlo. */
310
+ if (e.key === "Enter") {
311
+ e.preventDefault();
312
+ const chosen = suggestions[active] ?? suggestions[0];
313
+ if (chosen !== undefined)
314
+ pick(chosen);
315
+ }
316
+ /* Escape DESHACE, un paso por vez y siempre el último: primero lo
317
+ escrito, después la comparación, después el campo. Quien se
318
+ equivocó de campo quiere cambiarlo, no volver a empezar — y
319
+ quien no está armando nada no espera que le pase nada.
320
+
321
+ Antes sólo desarmaba el paso cuando ya había un campo elegido,
322
+ así que con «vent» a medio escribir no hacía nada: la tecla que
323
+ todo el mundo aprieta para deshacer no deshacía. */
324
+ if (e.key === "Escape") {
325
+ if (text !== "") {
326
+ e.preventDefault();
327
+ setText("");
328
+ return;
329
+ }
330
+ if (step.op !== undefined) {
331
+ e.preventDefault();
332
+ setStep({ field: step.field });
333
+ return;
334
+ }
335
+ if (step.field !== undefined) {
336
+ e.preventDefault();
337
+ setStep({});
338
+ return;
339
+ }
340
+ /* Y al final, el grupo: lo último que se abrió es lo último que
341
+ se deshace. */
342
+ if (openGroup !== null) {
343
+ e.preventDefault();
344
+ closeGroup();
345
+ }
346
+ return;
347
+ }
348
+ }, placeholder: step.field !== undefined
349
+ ? copy.search.typeValue
350
+ : criteria.length === 0
351
+ ? (placeholder ?? copy.search.placeholder)
352
+ : "",
353
+ /* CON UN CAMPO ELEGIDO, lo que se escribe es el VALOR — y se dibuja
354
+ como la ficha que va a ser, atrás de las otras dos. No es un
355
+ adorno: el borrador se lee entero —«nombre · contiene · vent▌»—
356
+ antes de existir como recorte, así que se ve lo que va a quedar
357
+ antes de que quede. Y sigue siendo el input: nada se duplica, y
358
+ nada se convierte en filtro hasta que alguien aprieta Enter o
359
+ elige de la lista.
360
+
361
+ El ancho lo pide el CONTENIDO —`field-sizing`— para que la ficha
362
+ crezca con lo escrito en vez de reservar una banda vacía. Donde no
363
+ esté, el input se estira como siempre. */
364
+ style: step.field === undefined ? undefined : { fieldSizing: "content" }, className: step.field === undefined
365
+ ? `min-w-24 flex-1 bg-transparent text-meta outline-none ${bySurface(sup, "text-ink placeholder:text-muted", "text-chrome-ink placeholder:text-chrome-muted")}`
366
+ : `min-w-16 rounded-pill px-2 py-0.5 text-micro outline-none ${bySurface(sup, "bg-accent/18 text-ink placeholder:text-muted", "bg-chrome-link/20 text-chrome-ink placeholder:text-chrome-muted")}` })] }), columns.length === 0 && onOpenBuilder === undefined ? null : (_jsxs("button", { type: "button", onClick: () => setPanel((v) => !v), "aria-expanded": panel, "aria-controls": panel ? id : undefined, title: copy.search.tools, className: `my-0.5 mr-0.5 inline-flex h-(--size-control-sm) shrink-0 items-center gap-1 self-center rounded-control border px-2 text-micro transition-colors duration-(--duration-instant) focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary ${puestos > 0 || panel
367
+ ? bySurface(sup, "border-accent/40 bg-accent/18 text-ink", "border-chrome-link/35 bg-chrome-link/15 text-chrome-link")
368
+ : bySurface(sup, "border-transparent text-muted hover:bg-ink/5 hover:text-ink", "border-transparent text-chrome-muted hover:bg-chrome-ink/10 hover:text-chrome-ink")}`, children: [_jsx(ListFilter, { className: "size-(--size-icon-sm)", strokeWidth: 2, "aria-hidden": true }), puestos > 0 ? _jsx("span", { className: "text-micro tabular-nums", children: puestos }) : null] }))] }), trailing, open ? (_jsxs("div", { className: `absolute top-full z-30 mt-1 overflow-hidden rounded-panel border border-line-strong bg-surface shadow-float ${asTags ? "left-0 w-max max-w-full" : "right-0 left-0"}`, children: [openJoin === undefined ? null : (_jsxs("div", { className: "flex items-center gap-1.5 border-line border-b px-3 py-1.5 text-micro text-muted", children: [_jsx(CornerDownRight, { className: "size-3 shrink-0", strokeWidth: 2, "aria-hidden": true }), _jsx("span", { className: "min-w-0 truncate", children: copy.search.landsHere }), _jsx("span", { className: "shrink-0 rounded-pill border border-accent/40 bg-accent/18 px-1.5 text-ink", children: copy.search.joins[openJoin === "not" ? "none" : openJoin] })] })), asTags ? (
369
+ /* UNA LISTA, no una tira. Se está ELIGIENDO: el ojo baja por una
370
+ columna y el teclado también. Doce atributos doblados en tres
371
+ renglones son doce cosas que barrer en zigzag para encontrar una. */
372
+ _jsx("div", { className: "max-h-72 overflow-y-auto p-1", children: _jsx(Tags, { stack: true, surface: "canvas", label: step.field !== undefined ? copy.search.askOp : copy.search.askAttribute, items: suggestions.map((one, at) => ({
373
+ id: one.id,
374
+ label: one.label,
375
+ ...(one.kind === "op"
376
+ ? { note: one.op }
377
+ : one.kind === "field" && one.hint !== undefined
378
+ ? { note: one.hint }
379
+ : {}),
380
+ active: at === active,
381
+ onSelect: () => pick(one),
382
+ })) }) })) : (_jsx("div", { className: "p-1", children: suggestions.map((one, at) => (_jsxs("button", { type: "button", onClick: () => pick(one), onMouseEnter: () => setActive(at), className: `flex w-full items-baseline gap-1.5 rounded-control px-2 py-1.5 text-left text-meta transition-colors duration-(--duration-instant) ${at === active ? "bg-primary/8" : "hover:bg-ink/4"}`, children: [_jsx("span", { className: "shrink-0 font-medium text-ink", children: one.label }), one.kind === "condition" ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "shrink-0 text-muted", children: ":" }), _jsx("span", { className: "min-w-0 truncate font-mono text-micro text-ink", children: one.hint })] })) : one.kind === "field" && one.hint !== undefined ? (_jsx("span", { className: "inline-flex shrink-0 items-center rounded-pill border border-line-strong bg-ink/5 px-2 py-0.5 text-micro text-muted", children: one.hint })) : null] }, one.id))) }))] })) : null, panel && (columns.length > 0 || onOpenBuilder !== undefined) ? (_jsxs("div", { id: id, className: "absolute top-full right-0 z-30 mt-1 flex w-max max-w-full flex-wrap overflow-hidden rounded-panel border border-line-strong bg-surface shadow-float", children: [columns.map((c, i) => (_jsxs("div", { className: `flex min-w-48 flex-1 flex-col ${i === 0 ? "" : "border-t border-line sm:border-t-0 sm:border-l"}`, children: [_jsx("div", { className: "border-b border-line px-2.5 py-1.5 text-micro font-medium text-muted", children: c.title }), _jsx("div", { className: "flex flex-col gap-0.5 p-1", children: c.content ??
383
+ c.groups?.map((g, gi) => (_jsx(Group, { group: g, first: gi === 0, onSelect: c.onSelect }, g.label ?? gi))) })] }, c.title))), onOpenBuilder === undefined ? null : (_jsx("div", { className: "w-full border-t border-line p-1", children: _jsxs("button", { type: "button", onClick: () => {
384
+ onOpenBuilder();
385
+ setPanel(false);
386
+ }, className: "flex w-full items-center gap-2 rounded-control px-1.5 py-1 text-left text-meta text-ink transition-colors duration-(--duration-instant) hover:bg-primary/8 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary", children: [_jsx(SlidersHorizontal, { className: "size-(--size-icon-sm)", strokeWidth: 2 }), copy.search.openBuilder] }) }))] })) : null] }));
387
+ }
@@ -0,0 +1,116 @@
1
+ import type { BuiltCondition, BuiltFilter } from "./FilterBuilder.js";
2
+ import type { GroupGrain, GroupLevel } from "../../foundations/grouping.js";
3
+ /** Cómo se juntan las instancias de un nodo. `not` niega lo que tiene adentro. */
4
+ export type CriteriaJoin = "all" | "any" | "not";
5
+ /**
6
+ * UNA INSTANCIA de lo que la barra tiene puesto.
7
+ *
8
+ * Tres formas y ninguna de más: un nivel de agrupado, un filtro, o un nodo que
9
+ * junta filtros. Un nodo NO junta grupos — «año y mes» no se cruza con Y, se
10
+ * anida, y para eso el orden de la lista ya es la jerarquía.
11
+ */
12
+ export type SearchCriteria = {
13
+ id: string;
14
+ kind: "group";
15
+ /** Cómo se lee: «registrado · mes». Lo pone quien conoce el modelo. */
16
+ label: string;
17
+ /** Lo que viaja: por qué campo se parte y con qué grano. */
18
+ level: GroupLevel;
19
+ } | {
20
+ id: string;
21
+ kind: "filter";
22
+ /** Cómo se lee. Un filtro declarado trae el suyo; uno escrito en la barra
23
+ * se describe solo, con las palabras del modelo. */
24
+ label: string;
25
+ /**
26
+ * Lo que viaja, en la forma del motor y sin una clave de más.
27
+ *
28
+ * FALTA cuando el recorte no se puede preguntar: «con entregas pendientes» se define
29
+ * con datos de otro contexto y ningún `where` sobre esta tabla lo
30
+ * contesta. La instancia existe igual —se ve, se cuenta, se saca— y lo
31
+ * contesta quien la declaró. Sin esto, un recorte así no era una instancia
32
+ * de nada: se dibujaba en el menú y tildarlo no hacía nada.
33
+ */
34
+ where?: BuiltCondition | BuiltFilter;
35
+ } | {
36
+ id: string;
37
+ kind: "join";
38
+ join: CriteriaJoin;
39
+ of: readonly SearchCriteria[];
40
+ };
41
+ /** El camino hasta una instancia, por índice desde la raíz. Es su identidad: dos
42
+ * instancias pueden decir lo mismo en ramas distintas, y tocar una no puede
43
+ * abrir la otra. */
44
+ export type CriteriaPath = readonly number[];
45
+ /** Una instancia de agrupado, ya estrechada: los de grupo nunca son un nodo, así
46
+ * que quien los dibuja no tiene que preguntarse si tienen rótulo. */
47
+ export type GroupCriteria = Extract<SearchCriteria, {
48
+ kind: "group";
49
+ }>;
50
+ /** Las instancias de agrupado, en orden. Van SIEMPRE a la izquierda y no se
51
+ * cruzan con nada: el orden ES la jerarquía. */
52
+ export declare function groupsOf(all: readonly SearchCriteria[]): GroupCriteria[];
53
+ /** Las de filtro, que son las que se anidan. */
54
+ export declare function filtersOf(all: readonly SearchCriteria[]): SearchCriteria[];
55
+ /** La instancia que hay en esa ruta, si hay alguna. */
56
+ export declare function at(all: readonly SearchCriteria[], path: CriteriaPath): SearchCriteria | undefined;
57
+ /** El mismo árbol con una instancia cambiada. Devolver uno nuevo y no tocar el
58
+ * viejo es lo que deja comparar antes y después —y deshacer. */
59
+ export declare function replaceAt(all: readonly SearchCriteria[], path: CriteriaPath, next: SearchCriteria): SearchCriteria[];
60
+ /**
61
+ * El mismo árbol sin esa instancia.
62
+ *
63
+ * Un nodo que se queda con un solo hijo se DESARMA: «cualquiera de: (a)» es un
64
+ * paréntesis alrededor de nada, y dejarlo obliga a leer un grupo que no agrupa.
65
+ */
66
+ export declare function removeAt(all: readonly SearchCriteria[], path: CriteriaPath): SearchCriteria[];
67
+ /** Cómo se juntan los de un nodo: `all` → `any` → `not` y vuelta a empezar. Es
68
+ * la misma rueda que el selector muestra abierta, para quien prefiera el
69
+ * atajo. */
70
+ export declare function nextJoin(join: CriteriaJoin): CriteriaJoin;
71
+ /** Un id que no choca con otro. Corto, porque se lee en el DOM. */
72
+ export declare function criteriaId(prefix: string): string;
73
+ /** Una instancia de agrupado, a partir de un nivel ya legible. */
74
+ export declare function groupCriteria(level: GroupLevel, label: string): SearchCriteria;
75
+ /** Una instancia de filtro. `label` es lo único que distingue a uno declarado en
76
+ * código de uno escrito en la barra; sin `where`, es uno que sólo contesta
77
+ * quien lo declaró. */
78
+ export declare function filterCriteria(where: BuiltCondition | BuiltFilter | undefined, label: string, id?: string): SearchCriteria;
79
+ /**
80
+ * Lo que VIAJA: el `where` del motor, armado con las instancias de filtro.
81
+ *
82
+ * Es lo único que sale de acá hacia afuera, y sale sin rótulos, sin ids y sin
83
+ * clases: la UI se queda con lo que la UI necesita.
84
+ */
85
+ export declare function whereOf(all: readonly SearchCriteria[]): BuiltFilter | undefined;
86
+ /** Los niveles que viajan, en orden. */
87
+ export declare function levelsOf(all: readonly SearchCriteria[]): GroupLevel[];
88
+ export type { GroupGrain, GroupLevel };
89
+ /**
90
+ * Los niveles de agrupado, puestos al día.
91
+ *
92
+ * Reemplaza TODAS las instancias de grupo por las que salen de `levels`, y no
93
+ * toca ninguna de filtro. Existe porque el control de niveles es una lista
94
+ * ordenada que se edita entera —se agrega, se saca, se mueve— y reconciliarla
95
+ * instancia por instancia desde afuera es el mismo bucle escrito en cada
96
+ * pantalla.
97
+ *
98
+ * Los de grupo quedan ADELANTE, que es donde se dibujan.
99
+ */
100
+ export declare function withLevels(all: readonly SearchCriteria[], levels: readonly GroupLevel[], labelOf: (level: GroupLevel) => string): SearchCriteria[];
101
+ /**
102
+ * Un filtro CON NOMBRE, puesto o sacado.
103
+ *
104
+ * Es lo único que hace un filtro declarado en código: traer su rótulo y su
105
+ * condición ya pensadas. Tildarlo lo agrega, destildarlo lo saca, y se reconoce
106
+ * por su `id` — el mismo que declaró quien lo escribió, así que sobrevive a
107
+ * guardarlo como favorito y volver a leerlo.
108
+ */
109
+ export declare function toggleNamed(all: readonly SearchCriteria[], named: {
110
+ id: string;
111
+ label: string;
112
+ where?: BuiltCondition | BuiltFilter;
113
+ }): SearchCriteria[];
114
+ /** Si ese filtro con nombre está puesto. Lo usa el panel para dibujar la marca:
115
+ * la marca sale de lo PUESTO y no de un estado aparte que podría discrepar. */
116
+ export declare function hasNamed(all: readonly SearchCriteria[], id: string): boolean;