@mistriwork/mistri 1.0.0-rc.12 → 1.0.0-rc.13

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 (221) hide show
  1. package/README.md +4 -3
  2. package/dist/client/assets/LayoutBuilder-MPzkVU2e.js.map +1 -1
  3. package/dist/client/assets/index-D2dHI6hR.js.map +1 -1
  4. package/dist/client/assets/shell-TreN_uLs.js.map +1 -1
  5. package/dist/docs/access-model.md +1 -1
  6. package/dist/docs/build-and-deploy.md +0 -1
  7. package/dist/docs/cli.md +1 -1
  8. package/dist/docs/glossary.md +3 -3
  9. package/dist/docs/query.md +9 -21
  10. package/dist/docs/status.md +3 -3
  11. package/dist/embed/react/chunks/LayoutBuilder-Cbxa_wve.js.map +1 -1
  12. package/dist/embed/react/chunks/app-0UZ1Re1k.js.map +1 -1
  13. package/dist/embed/react/chunks/page-Bx4o1kpQ.js.map +1 -1
  14. package/dist/embed/react/chunks/palette-Bb7Lf6eo.js.map +1 -1
  15. package/dist/embed/react/chunks/shell-DZRACiB9.js.map +1 -1
  16. package/dist/embed/react/chunks/write-BQO4CFc9.js.map +1 -1
  17. package/dist/embed/react/index.js +1 -1
  18. package/dist/server/build/dockerfile.js +1 -1
  19. package/dist/server/cli/usage.js +2 -2
  20. package/dist/ui/index.js +2 -2
  21. package/dist/ui/page.js +2 -2
  22. package/dist/ui/{shared-N4Ej8yRD.js → shared-1PD7TOTJ.js} +5 -5
  23. package/dist/ui/{shared-N4Ej8yRD.js.map → shared-1PD7TOTJ.js.map} +1 -1
  24. package/dist/ui/{shared-D5m86-4o.js → shared-1gMnOu9J.js} +6 -6
  25. package/dist/ui/{shared-D5m86-4o.js.map → shared-1gMnOu9J.js.map} +1 -1
  26. package/dist/ui/{shared-Ctaj8mR2.js → shared-5RQMi4BR.js} +3 -3
  27. package/dist/ui/{shared-Ctaj8mR2.js.map → shared-5RQMi4BR.js.map} +1 -1
  28. package/dist/ui/{shared-DpLeIATZ.js → shared-B-BUF9fx.js} +2 -2
  29. package/dist/ui/{shared-DpLeIATZ.js.map → shared-B-BUF9fx.js.map} +1 -1
  30. package/dist/ui/{shared-DWYnjItP.js → shared-B-MhiPIN.js} +9 -9
  31. package/dist/ui/{shared-DWYnjItP.js.map → shared-B-MhiPIN.js.map} +1 -1
  32. package/dist/ui/{shared-0fNuhSbG.js → shared-B07qIMlE.js} +5 -5
  33. package/dist/ui/{shared-0fNuhSbG.js.map → shared-B07qIMlE.js.map} +1 -1
  34. package/dist/ui/{shared-lYtMadRU.js → shared-B1SAS_J-.js} +3 -3
  35. package/dist/ui/{shared-lYtMadRU.js.map → shared-B1SAS_J-.js.map} +1 -1
  36. package/dist/ui/{shared-CpI0Y8M5.js → shared-B4jgU8h9.js} +6 -6
  37. package/dist/ui/{shared-CpI0Y8M5.js.map → shared-B4jgU8h9.js.map} +1 -1
  38. package/dist/ui/{shared-Cxu1c35l.js → shared-B5aBCUi7.js} +30 -30
  39. package/dist/ui/{shared-Cxu1c35l.js.map → shared-B5aBCUi7.js.map} +1 -1
  40. package/dist/ui/{shared-CAe-US1q.js → shared-B9H7_Pji.js} +5 -5
  41. package/dist/ui/{shared-CAe-US1q.js.map → shared-B9H7_Pji.js.map} +1 -1
  42. package/dist/ui/{shared-Q2bgBaYo.js → shared-BAXr20S-.js} +4 -4
  43. package/dist/ui/{shared-Q2bgBaYo.js.map → shared-BAXr20S-.js.map} +1 -1
  44. package/dist/ui/{shared-CyEefFFb.js → shared-BDDUzH-f.js} +19 -19
  45. package/dist/ui/{shared-CyEefFFb.js.map → shared-BDDUzH-f.js.map} +1 -1
  46. package/dist/ui/{shared-BdhPI6dq.js → shared-BGmkeDeW.js} +3 -3
  47. package/dist/ui/{shared-BdhPI6dq.js.map → shared-BGmkeDeW.js.map} +1 -1
  48. package/dist/ui/{shared-DONIpq2q.js → shared-BHCQ60yN.js} +2 -2
  49. package/dist/ui/{shared-DONIpq2q.js.map → shared-BHCQ60yN.js.map} +1 -1
  50. package/dist/ui/{shared-CNmaAv64.js → shared-BHsjawAG.js} +8 -8
  51. package/dist/ui/{shared-CNmaAv64.js.map → shared-BHsjawAG.js.map} +1 -1
  52. package/dist/ui/{shared-DgjU9kOy.js → shared-BIQuK_nq.js} +4 -4
  53. package/dist/ui/{shared-DgjU9kOy.js.map → shared-BIQuK_nq.js.map} +1 -1
  54. package/dist/ui/{shared-icSQAj21.js → shared-BOe4W2FI.js} +3 -3
  55. package/dist/ui/{shared-icSQAj21.js.map → shared-BOe4W2FI.js.map} +1 -1
  56. package/dist/ui/{shared-BlxoOJSk.js → shared-BPpC08ip.js} +3 -3
  57. package/dist/ui/{shared-BlxoOJSk.js.map → shared-BPpC08ip.js.map} +1 -1
  58. package/dist/ui/{shared-BtiFL37J.js → shared-BdA3LPCB.js} +2 -2
  59. package/dist/ui/{shared-BtiFL37J.js.map → shared-BdA3LPCB.js.map} +1 -1
  60. package/dist/ui/{shared-CvR94vn4.js → shared-Be3nbWsd.js} +11 -11
  61. package/dist/ui/{shared-CvR94vn4.js.map → shared-Be3nbWsd.js.map} +1 -1
  62. package/dist/ui/{shared-Bl_nF_IH.js → shared-Bf30ZLoS.js} +8 -4
  63. package/dist/ui/shared-Bf30ZLoS.js.map +1 -0
  64. package/dist/ui/{shared-DijrJ_V-.js → shared-BguP_BaR.js} +7 -7
  65. package/dist/ui/{shared-DijrJ_V-.js.map → shared-BguP_BaR.js.map} +1 -1
  66. package/dist/ui/{shared-DPAVoWY0.js → shared-BoLbc2WD.js} +17 -2
  67. package/dist/ui/{shared-DPAVoWY0.js.map → shared-BoLbc2WD.js.map} +1 -1
  68. package/dist/ui/{shared-BkdBTYwx.js → shared-BttyOW4W.js} +3 -3
  69. package/dist/ui/{shared-BkdBTYwx.js.map → shared-BttyOW4W.js.map} +1 -1
  70. package/dist/ui/{shared-CQWLz8uT.js → shared-BumxsBlA.js} +10 -10
  71. package/dist/ui/{shared-CQWLz8uT.js.map → shared-BumxsBlA.js.map} +1 -1
  72. package/dist/ui/{shared-Dzu7tFiC.js → shared-Bw73ktBk.js} +3 -3
  73. package/dist/ui/{shared-Dzu7tFiC.js.map → shared-Bw73ktBk.js.map} +1 -1
  74. package/dist/ui/{shared-BhAYErSx.js → shared-C1B0GH0e.js} +4 -4
  75. package/dist/ui/{shared-BhAYErSx.js.map → shared-C1B0GH0e.js.map} +1 -1
  76. package/dist/ui/{shared-BQJQcGgj.js → shared-C2FsyYr7.js} +4 -4
  77. package/dist/ui/{shared-BQJQcGgj.js.map → shared-C2FsyYr7.js.map} +1 -1
  78. package/dist/ui/{shared-B9lw2Ryh.js → shared-C2RU1qjv.js} +4 -4
  79. package/dist/ui/{shared-B9lw2Ryh.js.map → shared-C2RU1qjv.js.map} +1 -1
  80. package/dist/ui/{shared-DQYNI9wO.js → shared-C42f07hJ.js} +5 -5
  81. package/dist/ui/{shared-DQYNI9wO.js.map → shared-C42f07hJ.js.map} +1 -1
  82. package/dist/ui/{shared-CqCZ8wMY.js → shared-C4LkPCU4.js} +3 -3
  83. package/dist/ui/{shared-CqCZ8wMY.js.map → shared-C4LkPCU4.js.map} +1 -1
  84. package/dist/ui/{shared-BhtpgOpa.js → shared-C4wSfz6k.js} +9 -9
  85. package/dist/ui/{shared-BhtpgOpa.js.map → shared-C4wSfz6k.js.map} +1 -1
  86. package/dist/ui/{shared-BHc2Izkj.js → shared-C9DZtSFH.js} +2 -2
  87. package/dist/ui/{shared-BHc2Izkj.js.map → shared-C9DZtSFH.js.map} +1 -1
  88. package/dist/ui/{shared-DW1YuiNS.js → shared-CA52ClGo.js} +2 -2
  89. package/dist/ui/{shared-DW1YuiNS.js.map → shared-CA52ClGo.js.map} +1 -1
  90. package/dist/ui/{shared-BbSbilkh.js → shared-CBciTwm6.js} +9 -9
  91. package/dist/ui/{shared-BbSbilkh.js.map → shared-CBciTwm6.js.map} +1 -1
  92. package/dist/ui/{shared-C-J32eBn.js → shared-CL3RLKDA.js} +16 -16
  93. package/dist/ui/{shared-C-J32eBn.js.map → shared-CL3RLKDA.js.map} +1 -1
  94. package/dist/ui/{shared-Ck8S4OxW.js → shared-CLq3-_hW.js} +3 -3
  95. package/dist/ui/{shared-Ck8S4OxW.js.map → shared-CLq3-_hW.js.map} +1 -1
  96. package/dist/ui/{shared-Q5WefT7G.js → shared-CMjK-Ucd.js} +3 -3
  97. package/dist/ui/{shared-Q5WefT7G.js.map → shared-CMjK-Ucd.js.map} +1 -1
  98. package/dist/ui/{shared-D3MAuEA3.js → shared-CPjpSFGh.js} +2 -2
  99. package/dist/ui/{shared-D3MAuEA3.js.map → shared-CPjpSFGh.js.map} +1 -1
  100. package/dist/ui/{shared-VshXGwXP.js → shared-CSPhENq3.js} +2 -2
  101. package/dist/ui/{shared-VshXGwXP.js.map → shared-CSPhENq3.js.map} +1 -1
  102. package/dist/ui/{shared-DJrJaE73.js → shared-CZJE4Bmp.js} +3 -3
  103. package/dist/ui/{shared-DJrJaE73.js.map → shared-CZJE4Bmp.js.map} +1 -1
  104. package/dist/ui/{shared-D7ONOwI7.js → shared-C_BhUL2I.js} +7 -7
  105. package/dist/ui/{shared-D7ONOwI7.js.map → shared-C_BhUL2I.js.map} +1 -1
  106. package/dist/ui/{shared-j-2peBIB.js → shared-CbeujwdU.js} +4 -4
  107. package/dist/ui/{shared-j-2peBIB.js.map → shared-CbeujwdU.js.map} +1 -1
  108. package/dist/ui/{shared-D5GrOEa2.js → shared-Cbf-o4Dt.js} +4 -4
  109. package/dist/ui/{shared-D5GrOEa2.js.map → shared-Cbf-o4Dt.js.map} +1 -1
  110. package/dist/ui/{shared-HwiPIajr.js → shared-CdNmeNHh.js} +3 -3
  111. package/dist/ui/{shared-HwiPIajr.js.map → shared-CdNmeNHh.js.map} +1 -1
  112. package/dist/ui/{shared-ZCFt-CPb.js → shared-CdQOMD8J.js} +3 -3
  113. package/dist/ui/{shared-ZCFt-CPb.js.map → shared-CdQOMD8J.js.map} +1 -1
  114. package/dist/ui/{shared-ClhXSGGK.js → shared-CdiPYlTh.js} +2 -2
  115. package/dist/ui/{shared-ClhXSGGK.js.map → shared-CdiPYlTh.js.map} +1 -1
  116. package/dist/ui/{shared-DA8_pVRZ.js → shared-CepLAhwm.js} +2 -2
  117. package/dist/ui/{shared-DA8_pVRZ.js.map → shared-CepLAhwm.js.map} +1 -1
  118. package/dist/ui/{shared-BrYKjrnM.js → shared-Ch82h_Fu.js} +6 -6
  119. package/dist/ui/{shared-BrYKjrnM.js.map → shared-Ch82h_Fu.js.map} +1 -1
  120. package/dist/ui/{shared-C4UrE8Fb.js → shared-Chp88all.js} +3 -3
  121. package/dist/ui/{shared-C4UrE8Fb.js.map → shared-Chp88all.js.map} +1 -1
  122. package/dist/ui/{shared-CWa80DTO.js → shared-Ci16s2rN.js} +9 -9
  123. package/dist/ui/{shared-CWa80DTO.js.map → shared-Ci16s2rN.js.map} +1 -1
  124. package/dist/ui/{shared-tksTLD6Q.js → shared-CiqNkoE1.js} +5 -5
  125. package/dist/ui/{shared-tksTLD6Q.js.map → shared-CiqNkoE1.js.map} +1 -1
  126. package/dist/ui/{shared-yt7DjSrq.js → shared-CpMhOq8F.js} +2 -2
  127. package/dist/ui/{shared-yt7DjSrq.js.map → shared-CpMhOq8F.js.map} +1 -1
  128. package/dist/ui/{shared-Dm0z5WUl.js → shared-CrWv5JKH.js} +6 -6
  129. package/dist/ui/{shared-Dm0z5WUl.js.map → shared-CrWv5JKH.js.map} +1 -1
  130. package/dist/ui/{shared-CQL3FGHc.js → shared-Csfi3Pti.js} +2 -2
  131. package/dist/ui/{shared-CQL3FGHc.js.map → shared-Csfi3Pti.js.map} +1 -1
  132. package/dist/ui/{shared-BKNRbm-V.js → shared-CuZt26rm.js} +2 -2
  133. package/dist/ui/{shared-BKNRbm-V.js.map → shared-CuZt26rm.js.map} +1 -1
  134. package/dist/ui/{shared-Guq3upiR.js → shared-Cws_z-TS.js} +2 -2
  135. package/dist/ui/{shared-Guq3upiR.js.map → shared-Cws_z-TS.js.map} +1 -1
  136. package/dist/ui/{shared-Bw_qZeIj.js → shared-D1KWuCg6.js} +2 -2
  137. package/dist/ui/{shared-Bw_qZeIj.js.map → shared-D1KWuCg6.js.map} +1 -1
  138. package/dist/ui/{shared-CuCZKwXW.js → shared-D66ju_Cb.js} +23 -23
  139. package/dist/ui/{shared-CuCZKwXW.js.map → shared-D66ju_Cb.js.map} +1 -1
  140. package/dist/ui/{shared-DYfX7NSM.js → shared-DGaQZXnw.js} +2 -2
  141. package/dist/ui/{shared-DYfX7NSM.js.map → shared-DGaQZXnw.js.map} +1 -1
  142. package/dist/ui/{shared-B3Hp8tOv.js → shared-DHy41sET.js} +6 -6
  143. package/dist/ui/{shared-B3Hp8tOv.js.map → shared-DHy41sET.js.map} +1 -1
  144. package/dist/ui/{shared-CXyd7IKi.js → shared-DKEOMQ6l.js} +2 -2
  145. package/dist/ui/{shared-CXyd7IKi.js.map → shared-DKEOMQ6l.js.map} +1 -1
  146. package/dist/ui/{shared-Ccmc8YO0.js → shared-DSUICuaJ.js} +33 -33
  147. package/dist/ui/{shared-Ccmc8YO0.js.map → shared-DSUICuaJ.js.map} +1 -1
  148. package/dist/ui/{shared-CL2iw_js.js → shared-DUPMRoO0.js} +13 -13
  149. package/dist/ui/{shared-CL2iw_js.js.map → shared-DUPMRoO0.js.map} +1 -1
  150. package/dist/ui/{shared-Dtp4eRul.js → shared-DVfdm6ah.js} +2 -2
  151. package/dist/ui/{shared-Dtp4eRul.js.map → shared-DVfdm6ah.js.map} +1 -1
  152. package/dist/ui/{shared-BJ5Ex32G.js → shared-DXM7d7lJ.js} +3 -3
  153. package/dist/ui/{shared-BJ5Ex32G.js.map → shared-DXM7d7lJ.js.map} +1 -1
  154. package/dist/ui/{shared-BFr6ld0e.js → shared-DXbMlcev.js} +2 -2
  155. package/dist/ui/{shared-BFr6ld0e.js.map → shared-DXbMlcev.js.map} +1 -1
  156. package/dist/ui/{shared-BUp3jv-N.js → shared-D_SkPtJ9.js} +6 -6
  157. package/dist/ui/{shared-BUp3jv-N.js.map → shared-D_SkPtJ9.js.map} +1 -1
  158. package/dist/ui/{shared-CdNomVzH.js → shared-Dd21k2ic.js} +4 -4
  159. package/dist/ui/{shared-CdNomVzH.js.map → shared-Dd21k2ic.js.map} +1 -1
  160. package/dist/ui/{shared-gRMbxmpu.js → shared-Dh0nHtLZ.js} +8 -8
  161. package/dist/ui/{shared-gRMbxmpu.js.map → shared-Dh0nHtLZ.js.map} +1 -1
  162. package/dist/ui/{shared-BKGJgjog.js → shared-DkOdSpSK.js} +3 -3
  163. package/dist/ui/{shared-BKGJgjog.js.map → shared-DkOdSpSK.js.map} +1 -1
  164. package/dist/ui/{shared-BMsh72Uq.js → shared-Dkph5ckT.js} +2 -2
  165. package/dist/ui/{shared-BMsh72Uq.js.map → shared-Dkph5ckT.js.map} +1 -1
  166. package/dist/ui/{shared-Be1HpDOB.js → shared-Dl1_Nx--.js} +3 -3
  167. package/dist/ui/{shared-Be1HpDOB.js.map → shared-Dl1_Nx--.js.map} +1 -1
  168. package/dist/ui/{shared-BQIZWOoJ.js → shared-Dm0IGbAf.js} +2 -2
  169. package/dist/ui/{shared-BQIZWOoJ.js.map → shared-Dm0IGbAf.js.map} +1 -1
  170. package/dist/ui/{shared-DSUtcU5S.js → shared-DnIkTpPr.js} +5 -5
  171. package/dist/ui/{shared-DSUtcU5S.js.map → shared-DnIkTpPr.js.map} +1 -1
  172. package/dist/ui/{shared-DsxGbYlV.js → shared-Docl_0tq.js} +3 -3
  173. package/dist/ui/{shared-DsxGbYlV.js.map → shared-Docl_0tq.js.map} +1 -1
  174. package/dist/ui/{shared-DZskrV26.js → shared-DprrHC72.js} +2 -2
  175. package/dist/ui/{shared-DZskrV26.js.map → shared-DprrHC72.js.map} +1 -1
  176. package/dist/ui/{shared-Bmw43Hwf.js → shared-DyKsJTZs.js} +2 -2
  177. package/dist/ui/{shared-Bmw43Hwf.js.map → shared-DyKsJTZs.js.map} +1 -1
  178. package/dist/ui/{shared-BLG88Pya.js → shared-FO7vfI3F.js} +4 -4
  179. package/dist/ui/{shared-BLG88Pya.js.map → shared-FO7vfI3F.js.map} +1 -1
  180. package/dist/ui/{shared-DLSd80D1.js → shared-HalAVHjv.js} +3 -3
  181. package/dist/ui/{shared-DLSd80D1.js.map → shared-HalAVHjv.js.map} +1 -1
  182. package/dist/ui/{shared-D1mMBLdj.js → shared-JvcI-LWp.js} +15 -15
  183. package/dist/ui/{shared-D1mMBLdj.js.map → shared-JvcI-LWp.js.map} +1 -1
  184. package/dist/ui/{shared-C6Hv_4O0.js → shared-K7Ym27yg.js} +2 -2
  185. package/dist/ui/{shared-C6Hv_4O0.js.map → shared-K7Ym27yg.js.map} +1 -1
  186. package/dist/ui/{shared-B39ln7oi.js → shared-SzlnwfJO.js} +3 -3
  187. package/dist/ui/{shared-B39ln7oi.js.map → shared-SzlnwfJO.js.map} +1 -1
  188. package/dist/ui/{shared-DgPn_Jll.js → shared-TrGbd1FY.js} +3 -3
  189. package/dist/ui/{shared-DgPn_Jll.js.map → shared-TrGbd1FY.js.map} +1 -1
  190. package/dist/ui/{shared-DrFb-XtA.js → shared-WhbuObEW.js} +2 -2
  191. package/dist/ui/{shared-DrFb-XtA.js.map → shared-WhbuObEW.js.map} +1 -1
  192. package/dist/ui/{shared-DiFQljcq.js → shared-XcSKDp6N.js} +11 -11
  193. package/dist/ui/{shared-DiFQljcq.js.map → shared-XcSKDp6N.js.map} +1 -1
  194. package/dist/ui/{shared-CMAg58rY.js → shared-Z4uBJU6I.js} +7 -6
  195. package/dist/ui/shared-Z4uBJU6I.js.map +1 -0
  196. package/dist/ui/{shared-BnUptuUp.js → shared-bAgeiRa5.js} +2 -2
  197. package/dist/ui/{shared-BnUptuUp.js.map → shared-bAgeiRa5.js.map} +1 -1
  198. package/dist/ui/{shared-BxMvSjey.js → shared-cgXG7OgZ.js} +5 -5
  199. package/dist/ui/{shared-BxMvSjey.js.map → shared-cgXG7OgZ.js.map} +1 -1
  200. package/dist/ui/{shared-u4CG2kQJ.js → shared-eZ0SB_zm.js} +2 -2
  201. package/dist/ui/{shared-u4CG2kQJ.js.map → shared-eZ0SB_zm.js.map} +1 -1
  202. package/dist/ui/{shared-L90bWW8e.js → shared-frKFb79C.js} +2 -2
  203. package/dist/ui/{shared-L90bWW8e.js.map → shared-frKFb79C.js.map} +1 -1
  204. package/dist/ui/{shared-Dd34nhkw.js → shared-hAayNSRA.js} +3 -3
  205. package/dist/ui/{shared-Dd34nhkw.js.map → shared-hAayNSRA.js.map} +1 -1
  206. package/dist/ui/{shared-DzC64cpp.js → shared-j_J16p2B.js} +2 -2
  207. package/dist/ui/{shared-DzC64cpp.js.map → shared-j_J16p2B.js.map} +1 -1
  208. package/dist/ui/{shared-DX7HRIV-.js → shared-tqpl5N3g.js} +6 -6
  209. package/dist/ui/{shared-DX7HRIV-.js.map → shared-tqpl5N3g.js.map} +1 -1
  210. package/dist/ui/{shared-BS5dBMpA.js → shared-y-6_ZDts.js} +2 -2
  211. package/dist/ui/{shared-BS5dBMpA.js.map → shared-y-6_ZDts.js.map} +1 -1
  212. package/dist/ui/{shared-Cv5v_MP1.js → shared-zG6e1Rhz.js} +13 -13
  213. package/dist/ui/{shared-Cv5v_MP1.js.map → shared-zG6e1Rhz.js.map} +1 -1
  214. package/dist/ui/testing.js +1 -1
  215. package/package.json +4 -4
  216. package/dist/docs/cli-browser.md +0 -232
  217. package/dist/docs/database-browser.md +0 -115
  218. package/dist/docs/keyboard.md +0 -63
  219. package/dist/docs/mcp.md +0 -86
  220. package/dist/ui/shared-Bl_nF_IH.js.map +0 -1
  221. package/dist/ui/shared-CMAg58rY.js.map +0 -1
@@ -1,9 +1,9 @@
1
1
  import { N as andQuery, it as useQuery, tt as useMessageLanguage } from "./shared-poc11xxo.js";
2
2
  import { D as columnKind, F as primaryKey, gt as defineMessages } from "./shared-DJZjSi3J.js";
3
- import { B as DATE_OPTIONS, G as unquote, V as boxKind, W as equalsTail, X as conditionTail, Z as holdsCondition, et as squeeze, nt as withCondition, tt as stripCondition, z as Typeahead } from "./shared-CNmaAv64.js";
4
- import { L as state, c as columnLabel, d as findTable, i as api, m as labelPath, q as tryResolvePath, st as ownedBy, w as quote } from "./shared-DPAVoWY0.js";
3
+ import { B as DATE_OPTIONS, G as unquote, V as boxKind, W as equalsTail, X as conditionTail, Z as holdsCondition, et as squeeze, nt as withCondition, tt as stripCondition, z as Typeahead } from "./shared-BHsjawAG.js";
4
+ import { L as state, c as columnLabel, d as findTable, i as api, m as labelPath, q as tryResolvePath, st as ownedBy, w as quote } from "./shared-BoLbc2WD.js";
5
5
  import { t as Dropdown } from "./shared-gtzTT5Z7.js";
6
- import { vt as presentationColumns } from "./shared-DiFQljcq.js";
6
+ import { vt as presentationColumns } from "./shared-XcSKDp6N.js";
7
7
  import { u as splitQuery } from "./shared-DOvMIC4W.js";
8
8
  import { r as hasFilterOf } from "./shared-DUlM0U0V.js";
9
9
  import { useEffect, useRef, useState } from "react";
@@ -681,4 +681,4 @@ function FilterBar({ table, entry, names, navigate }) {
681
681
  }
682
682
  export { rangeOf as C, tagsTail as D, selectedValues as E, takesText as O, numberRangeTail as S, selectedTags as T, hasValuesText as _, boxesFor as a, moreTail as b, MORE_LABELS as c, datePresets as d, facetActive as f, hasCountText as g, facetShapeText as h, boxText as i, valuesTail as k, autoShape as l, facetKind as m, RefBox as n, referenceNameQuery as o, facetCountText as p, boxTail as r, wrongForNumber as s, FilterBar as t, clearFacets as u, moreConditions as v, rangeTail as w, numberRangeOf as x, moreOf as y };
683
683
 
684
- //# sourceMappingURL=shared-DgjU9kOy.js.map
684
+ //# sourceMappingURL=shared-BIQuK_nq.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-DgjU9kOy.js","names":["say","say","_Fragment"],"sources":["../../../kit/src/client/copy/filters/components/filterRailModel.ts","../../../kit/src/client/components/filterRailModel.ts","../../../kit/src/client/copy/filters/components/FilterBoxes.ts","../../../kit/src/client/components/FilterBoxes.tsx"],"sourcesContent":["/** What filterRailModel says, in the filter rail room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n thisWeek: 'This week',\n last7Days: 'Last 7 days',\n last30Days: 'Last 30 days',\n overdue: 'Overdue',\n next7Days: 'Next 7 days',\n noDate: 'No date',\n starts: 'Starts with',\n matchesAPattern: 'Matches a pattern',\n isEmpty: 'Is empty',\n isNotEmpty: 'Is not empty',\n});\n","/**\n * The filter rail's rules, before anything is drawn: which facet a column\n * gets, what a facet reads out of the list's question and writes back into\n * it, and the breakdown that counts a facet's values.\n *\n * The rail writes the same language into the same place as the bar's boxes\n * — one condition tail per column, folded in by withCondition — so a rail\n * selection is a URL, a chip and a tab refinement exactly as a box's is.\n * What it adds are the tails a box has no gesture for: several values at\n * once (`in (a, b)`), and a date's presets beside a range. Pure, so the rail\n * and its tests read one rule.\n */\nimport { quote } from '../core.js';\nimport { splitQuery } from '../clauses.js';\nimport { conditionTail, holdsCondition, squeeze, stripCondition, unquote } from '../filterbar.js';\nimport { andQuery } from '@mistriwork/engine/shared/query.js';\nimport type { ColumnMeta } from '@mistriwork/engine/shared/schema.js';\nimport { say } from '../copy/filters/components/filterRailModel.js';\n\n/** What a facet draws: a checklist of values, a checklist of tags, a toggle, date presets and a range, or a contains box. */\ntype FacetKind = 'values' | 'tags' | 'flag' | 'date' | 'text';\n\n/** A box kind as a facet: a vocabulary and a reference are both a checklist of values; a record's tags, of its tags. */\nexport const facetKind = (boxKind: string): FacetKind =>\n boxKind === 'enum' || boxKind === 'ref' ? 'values' : boxKind === 'flag' || boxKind === 'date' || boxKind === 'tags' ? boxKind : 'text';\n\n/* A value list split on its commas, never on a comma inside quotes. */\nfunction listItems(text: string): string[] {\n const out: string[] = [];\n let item = '';\n let inside: string | null = null;\n for (let i = 0; i < text.length; i++) {\n const ch = text[i];\n if (inside) {\n item += ch;\n if (ch === '\\\\') {\n item += text[++i] ?? '';\n continue;\n }\n if (ch === inside) inside = null;\n } else if (ch === '\"' || ch === \"'\") {\n inside = ch;\n item += ch;\n } else if (ch === ',') {\n out.push(item);\n item = '';\n } else item += ch;\n }\n out.push(item);\n return out.map((s) => unquote(s.trim())).filter(Boolean);\n}\n\n/**\n * The values a tail selects: `= a` one, `in (a, b)` several, none for no\n * tail. Null for a condition a checklist cannot draw — `!= a`, `is empty`\n * typed in Advanced — which stays as written until someone ticks a value.\n */\nexport function selectedValues(tail: string): string[] | null {\n const t = squeeze(tail);\n if (!t) return [];\n if (t.startsWith('= ')) return [unquote(t.slice(2))];\n const list = /^in\\s*[([](.*)[)\\]]$/i.exec(t);\n return list ? listItems(list[1]) : null;\n}\n\n/** The tail for a set of ticked values: one is an equality, several an `in`, none no condition. */\nexport function valuesTail(values: readonly string[]): string {\n if (!values.length) return '';\n if (values.length === 1) return `= ${quote(values[0])}`;\n return `in (${values.map((value) => quote(value)).join(', ')})`;\n}\n\n/**\n * The tags a record's-tags tail asks for: `has a` one, `has (a, b)` any of\n * several, none for no tail. Null for anything else typed by hand.\n */\nexport function selectedTags(tail: string): string[] | null {\n const t = squeeze(tail);\n if (!t) return [];\n const list = /^has\\s*[([](.*)[)\\]]$/i.exec(t);\n if (list) return listItems(list[1]);\n const one = /^has\\s+(.+)$/i.exec(t);\n return one ? [unquote(one[1].trim())] : null;\n}\n\n/** The tail for a set of ticked tags: a record that has any of them. */\nexport function tagsTail(values: readonly string[]): string {\n if (!values.length) return '';\n if (values.length === 1) return `has ${quote(values[0])}`;\n return `has (${values.map((value) => quote(value)).join(', ')})`;\n}\n\n/* A date column's meaning, read off its name and default — the signals a\n catalog carries. A stamp records when something happened (`created_at`,\n `signed_up_on`, a `DEFAULT CURRENT_TIMESTAMP`): it is only ever in the\n past, so it is asked about windows behind today. A deadline says when\n something is due (`due_on`, `expires_at`, `ship_by`): it is asked what is\n overdue and what is coming. Anything else — a birthday, a start date — is\n asked about the days on either side of today. */\nconst STAMP =\n /^(sys_)?(created|inserted|added|opened|raised|issued|ordered|received|signed_up|joined|updated|modified|changed|last_updated|submitted|sent|posted|published|logged|recorded|completed|closed|resolved|shipped|delivered|paid|approved|signed|happened|occurred|deleted|archived|last_[a-z]+)(_at|_on|_date|_time)?$/i;\nconst DEADLINE =\n /(^|_)(due|deadline|expires?|expiry|expiration|ship_by|respond_by|needed_by|renews?|renewal|follow_up|valid_until|until)(_|$)/i;\nconst STAMPED_DEFAULT = /current_timestamp|current_date|now\\(\\)|localtimestamp|getdate|sysdate/i;\n\ntype DateMeaning = 'stamp' | 'deadline' | 'date';\n\n/** What a date column is to the reader: a stamp of the past, a deadline, or a plain date. */\nexport function dateMeaning(column: (Pick<ColumnMeta, 'name'> & Partial<Pick<ColumnMeta, 'default'>>) | undefined): DateMeaning {\n const name = column?.name.split('.').pop() ?? '';\n if (DEADLINE.test(name)) return 'deadline';\n if (STAMP.test(name) || (column?.default !== undefined && STAMPED_DEFAULT.test(column.default))) return 'stamp';\n return 'date';\n}\n\n/* The windows each kind of date is asked about, in the query language's own\n phrases, which resolve on the server, so a link means the same thing\n tomorrow. \"Overdue\" is before today, and is offered only on a deadline:\n on a `created_at` it was every row. */\nconst PRESETS: Record<DateMeaning, ReadonlyArray<readonly [label: string, tail: string]>> = {\n stamp: [\n [say('common.today'), '= today'],\n [say('thisWeek'), '= this week'],\n [say('last7Days'), '= last 7 days'],\n [say('last30Days'), '= last 30 days'],\n ],\n deadline: [\n [say('overdue'), '< today'],\n [say('common.today'), '= today'],\n [say('thisWeek'), '= this week'],\n [say('next7Days'), '= next 7 days'],\n ],\n date: [\n [say('common.today'), '= today'],\n [say('thisWeek'), '= this week'],\n [say('last7Days'), '= last 7 days'],\n [say('next7Days'), '= next 7 days'],\n ],\n};\n\n/** A date facet's presets, fitted to what the column means; \"No date\" only where the column may be empty. */\nexport function datePresets(\n column: (Pick<ColumnMeta, 'name'> & Partial<Pick<ColumnMeta, 'default' | 'nullable'>>) | undefined,\n): ReadonlyArray<readonly [label: string, tail: string]> {\n const presets = PRESETS[dateMeaning(column)];\n return column?.nullable === false ? presets : [...presets, [say('noDate'), 'is empty']];\n}\n\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** The ends of a date range a tail says, '' for an open end; both '' for a tail that is not a range. */\nexport function rangeOf(tail: string): { from: string; to: string } {\n const t = squeeze(tail);\n const between = /^between\\s+(\\S+)\\s+and\\s+(\\S+)$/i.exec(t);\n if (between && ISO_DATE.test(unquote(between[1])) && ISO_DATE.test(unquote(between[2]))) {\n return { from: unquote(between[1]), to: unquote(between[2]) };\n }\n const end = /^(>=|<=)\\s*(\\S+)$/.exec(t);\n if (end && ISO_DATE.test(unquote(end[2]))) return end[1] === '>=' ? { from: unquote(end[2]), to: '' } : { from: '', to: unquote(end[2]) };\n return { from: '', to: '' };\n}\n\n/** A range as a tail: both ends inclusive, in order whichever end was set first; one end is a bound. */\nexport function rangeTail(from: string, to: string): string {\n const [a, b] = from && to && from > to ? [to, from] : [from, to];\n if (a && b) return `between ${a} and ${b}`;\n if (a) return `>= ${a}`;\n if (b) return `<= ${b}`;\n return '';\n}\n\n/* The view's standing condition and the reader's filter, as one condition. */\nconst both = (standing: string, filter: string) => {\n const [a, b] = [squeeze(standing), squeeze(filter)];\n return a && b ? andQuery(a, b) : a || b;\n};\n\n/**\n * The breakdown that counts a facet's values under everything else the\n * list asks. The facet's own condition is left out — the usual faceted\n * count, so a second value is offered with what ticking it would add — and\n * so are the sort and the page, which a count does not have. A reference is\n * grouped by its id and said by its target's label path. The view's\n * standing condition (`status != sold`) is the list's and stays: the reader's\n * question hides it, and a count without it counted rows the list never shows.\n */\nexport function facetCountText(table: string, refinement: string, column: string, labelPath?: string, standing = ''): string {\n const filter = splitQuery(`${table} ${stripCondition(refinement, column)}`).filter ?? '';\n return squeeze(`${table} ${both(standing, filter)} count by ${[column, labelPath].filter(Boolean).join(', ')} sort count desc`);\n}\n\n/**\n * How many of the list's records one value of a `has` facet would show\n * (shared/listFilters.ts): the list's rows, under everything else it asks,\n * with one of the rows that point at each holding the value — records, never\n * those rows, so an account with three won deals counts once. One count per\n * value, since a record is under every value one of its rows holds.\n */\nexport function hasCountText(table: string, refinement: string, key: string, standing = '', value?: string): string {\n const filter = splitQuery(`${table} ${stripCondition(refinement, key)}`).filter ?? '';\n const asked = value === undefined ? key : `${key} = ${quote(value)}`;\n return squeeze(`${table} ${andQuery(both(standing, filter), asked)} count`);\n}\n\n/** The values a `has` facet lists: those the rows that point at the record hold, the most held first, by name where they are references. */\nexport function hasValuesText(child: string, column: string, labelPath?: string): string {\n return squeeze(`${child} count by ${[column, labelPath].filter(Boolean).join(', ')} sort count desc`);\n}\n\n/* ---------- a facet's shape ---------- */\n\n/**\n * A column with no vocabulary, flag, date or reference shows as what its\n * values are. Asked once, of the view's standing rows alone — not of the\n * reader's narrowing, or a price would turn into a checklist of the dozen\n * prices a filter left. A number with more than RANGE_ABOVE distinct values is\n * a range; a few (a priority, a model year) a checklist; text with at most\n * CHECKLIST_MOST values a checklist, more a contains box.\n */\nconst RANGE_ABOVE = 12;\nexport const CHECKLIST_MOST = 50;\nexport type AutoShape = 'values' | 'range' | 'text';\n\n/** The breakdown a facet's shape is judged by: every value of the column the view shows. */\nexport function facetShapeText(table: string, column: string, standing = ''): string {\n return squeeze(`${table} ${squeeze(standing)} count by ${column} sort count desc`);\n}\n\n/** A column's facet from how many values it has; `complete` is false when there were more than were asked for. */\nexport function autoShape(numeric: boolean, distinct: number, complete: boolean): AutoShape {\n if (numeric) return complete && distinct <= RANGE_ABOVE ? 'values' : 'range';\n return complete && distinct <= CHECKLIST_MOST ? 'values' : 'text';\n}\n\n/* ---------- a number's range ---------- */\n\nconst NUMBER = /^-?\\d+(?:\\.\\d+)?$/;\n\n/** The ends of a number range a tail says, '' for an open end; both '' for a tail that is not one. */\nexport function numberRangeOf(tail: string): { min: string; max: string } {\n const t = squeeze(tail);\n const between = /^between\\s+(\\S+)\\s+and\\s+(\\S+)$/i.exec(t);\n if (between && NUMBER.test(between[1]) && NUMBER.test(between[2])) return { min: between[1], max: between[2] };\n const end = /^(>=|<=)\\s*(\\S+)$/.exec(t);\n if (end && NUMBER.test(end[2])) return end[1] === '>=' ? { min: end[2], max: '' } : { min: '', max: end[2] };\n return { min: '', max: '' };\n}\n\n/** A number range as a tail: both ends inclusive and in order, one end a bound, a non-number ignored. */\nexport function numberRangeTail(min: string, max: string): string {\n const [lo, hi] = [min.trim(), max.trim()].map((end) => (NUMBER.test(end) ? end : ''));\n const [a, b] = lo && hi && Number(lo) > Number(hi) ? [hi, lo] : [lo, hi];\n if (a && b) return `between ${a} and ${b}`;\n if (a) return `>= ${a}`;\n if (b) return `<= ${b}`;\n return '';\n}\n\n/** Whether a facet says anything beyond the lit tab: the tab's own condition is the tab's to show. */\nexport function facetActive(refinement: string, column: string, tabQuery = ''): boolean {\n const tail = squeeze(conditionTail(refinement, column));\n // A whole-condition filter (`any opportunity where open`) has no tail: it is on where it is said.\n if (!tail) return holdsCondition(refinement, column) && !holdsCondition(tabQuery, column);\n return tail !== squeeze(conditionTail(tabQuery, column));\n}\n\n/** Every facet's own conditions out, the lit tab's left standing: Clear all empties the rail, not the tab. */\nexport function clearFacets(refinement: string, columns: readonly string[], tabQuery = ''): string {\n return columns.reduce((text, column) => (facetActive(text, column, tabQuery) ? stripCondition(text, column) : text), squeeze(refinement));\n}\n\n/* ---------- the rarer conditions ---------- */\n\n/** A condition a facet keeps behind \"More conditions\": its word, and whether it takes text. */\nexport type MoreCondition = 'startswith' | 'like' | 'empty' | 'notempty';\nexport const MORE_LABELS: Record<MoreCondition, string> = {\n startswith: say('starts'),\n like: say('matchesAPattern'),\n empty: say('isEmpty'),\n notempty: say('isNotEmpty'),\n};\nexport const takesText = (condition: MoreCondition) => condition === 'startswith' || condition === 'like';\n\n/**\n * What a facet offers behind its \"More conditions\": text is asked what it\n * starts with and a pattern — not a vocabulary, whose values are a list to\n * pick from; a column that may be empty, whether it is. A\n * date's \"No date\" preset already asks for empty, so it is not asked twice;\n * a source that takes whole values only (an API) is asked nothing more.\n */\nexport function moreConditions(\n facet: 'values' | 'flag' | 'date' | 'text',\n column: { type?: string; nullable?: boolean; allowed?: readonly string[] } | undefined,\n exact = false,\n): MoreCondition[] {\n if (exact || !column) return [];\n const text = /char|text|string|clob|citext/i.test(column.type ?? '') && !column.allowed?.length;\n const out: MoreCondition[] = text && facet !== 'flag' ? ['startswith', 'like'] : [];\n if (column.nullable !== false) {\n if (facet !== 'date') out.push('empty');\n out.push('notempty');\n }\n return out;\n}\n\n/** Which of them a tail says, with its text; null for a tail that is none of them. */\nexport function moreOf(tail: string): { condition: MoreCondition; text: string } | null {\n const t = squeeze(tail);\n if (t === 'is empty') return { condition: 'empty', text: '' };\n if (t === 'is not empty') return { condition: 'notempty', text: '' };\n const said = /^(startswith|like)\\s+(.+)$/i.exec(t);\n return said ? { condition: said[1].toLowerCase() as MoreCondition, text: unquote(said[2]) } : null;\n}\n\n/** The tail for a rarer condition; '' for one that takes text and has none yet. */\nexport function moreTail(condition: MoreCondition, text = ''): string {\n if (condition === 'empty') return 'is empty';\n if (condition === 'notempty') return 'is not empty';\n const value = text.trim();\n return value ? `${condition} ${quote(value)}` : '';\n}\n","/** What FilterBoxes says, in the filter rail room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n filtersByTheId: 'Filters by {column} — the id, even where the column shows a name.',\n couldNotResolveRecord: 'Could not resolve record name.',\n couldNotLoadRecords: 'Could not load records.',\n lookupUnavailable: 'Lookup unavailable.',\n couldNotLoadRecordsClose: 'Could not load records. Close and reopen to retry.',\n noRecordsAvailable: 'No records available.',\n typeANameAndPick: 'Type a {name} name and pick — or an id.',\n backToTheUnfiltered: 'Back to the unfiltered table',\n});\n","/**\n * Named filter boxes over a table — one grammar, two modes.\n *\n * The workbench mounts the boxes above the plain table view; an app mounts\n * the same boxes inside its own filter section, with tabs and Advanced\n * around them. Both write the same language into the same place — the\n * entry's query text — so a box's state is a condition *tail*: `= open`,\n * `= last 30 days`, `is empty`, or '' for none.\n *\n * The parent holds what the person has set by hand, box by box. Only a\n * touched box speaks on submit: an untouched one used to read the query\n * back through its own narrow eyes and rewrite it, and a tab whose column\n * held two conditions came back holding one. The text already holds\n * whatever the boxes have not touched.\n */\nimport { useMessageLanguage } from './useMessageLanguage.js';\nimport { ownedBy } from '../requestIdentity.js';\nimport { useEffect, useRef, useState } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { api, columnLabel, findTable, labelPath, primaryKey, quote, state } from '../core.js';\nimport { columnKind, type ColumnMeta, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { DATE_OPTIONS, boxKind, conditionTail, equalsTail, squeeze, unquote, withCondition } from '../filterbar.js';\nimport { Dropdown } from './Dropdown.js';\nimport { tagsTail } from './filterRailModel.js';\nimport { presentationColumns } from './columnPresentation.js';\nimport { hasFilterOf } from '@mistriwork/engine/shared/listFilters.js';\nimport { tryResolvePath } from '@mistriwork/engine/shared/view.js';\nimport { Typeahead } from '@mistriwork/ui/Typeahead.js';\nimport { say } from '../copy/filters/components/FilterBoxes.js';\n\ntype BoxKind = 'text' | 'ref' | 'flag' | 'enum' | 'date' | 'tags';\n\n/** What a person set in a box: the text they typed and, for a reference, the row they picked; or a chosen tail. */\nexport interface BoxEdit {\n raw?: string;\n id?: string | null;\n tail?: string;\n}\n\n/** `exact`: the source matches whole values only (an API), so typed text is asked as `=`, never `contains`. */\nexport interface Box {\n column: string;\n label: string;\n kind: BoxKind;\n meta: ColumnMeta | undefined;\n exact?: boolean;\n /** The columns whose values the box suggests as the reader types: the list view's `suggest` (SuggestBox.tsx). */\n suggest?: readonly string[];\n /**\n * The rows that point at the record, where the box asks about them (shared/listFilters.ts): their table, and\n * the column or path of theirs it asks — `meta` is that column — or, `segment`, one of their segments, whose\n * box is a yes with no value.\n */\n has?: { child: string; asks: string; segment: boolean };\n}\n\n/* A column a path ends on, dressed as the box kind reads it: its own name, on the table that holds it, so a\n reference across a hop is a typeahead of its records as one on the list's own table is. */\nfunction kindAt(table: TableMeta | undefined, path: string, meta: ColumnMeta | undefined): BoxKind {\n if (!meta || !table || !state.schema || !path.includes('.')) return boxKind(meta, table?.id) as BoxKind;\n const { resolved } = tryResolvePath(state.schema, table.id, path);\n return boxKind(resolved ? { ...meta, name: resolved.column.name } : meta, resolved?.table.id) as BoxKind;\n}\n\n/** The boxes a table gets, for a set of column names. */\nexport function boxesFor(table: TableMeta | undefined, names: string[], labelOf: (name: string) => string): Box[] {\n const columns = presentationColumns(\n state.schema,\n table,\n names.filter((name) => !hasFilterOf(name)),\n );\n return names.map((name) => {\n const has = hasFilterOf(name);\n if (has) {\n const child = (findTable(has.child) ?? state.schema?.tables.find((t: TableMeta) => t.name === has.child)) as TableMeta | undefined;\n const segment = Boolean(child?.segments && Object.hasOwn(child.segments, has.asks));\n const meta = segment ? undefined : (presentationColumns(state.schema, child, [has.asks]).get(has.asks) as ColumnMeta | undefined);\n return {\n column: name,\n label: labelOf(name),\n kind: segment ? 'flag' : kindAt(child, has.asks, meta),\n meta,\n has: { child: child?.id ?? has.child, asks: has.asks, segment },\n };\n }\n const meta = columns.get(name) as (ColumnMeta & { allowed?: string[] }) | undefined;\n return {\n column: name,\n label: labelOf(name),\n kind: kindAt(table, name, meta),\n meta,\n ...(table?.filterable ? { exact: true } : {}),\n };\n });\n}\n\n/** The tail a box stands for: what was set by hand, else what the text already says. */\n/** A text box over a number column holding words: not sent, since the\n database refuses words compared with a number (PostgreSQL's 22P02), and\n the box says what it takes instead. */\nexport function wrongForNumber(box: Box, raw: string | undefined): boolean {\n const text = (raw ?? '').trim().replace(/^=\\s*/, '');\n return (\n Boolean(text) &&\n box.meta !== undefined &&\n columnKind(box.meta.type) === 'number' &&\n !/^[-+]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[-+]?\\d+)?$/i.test(text)\n );\n}\n\nexport function boxTail(box: Box, edit: BoxEdit | undefined, current: string): string {\n if (!edit) return current;\n if (box.kind === 'text') {\n const raw = (edit.raw ?? '').trim();\n if (!raw) return '';\n if (wrongForNumber(box, raw)) return current;\n const fuzzy = !box.exact && box.meta ? columnKind(box.meta.type) === 'text' : false;\n if (!fuzzy) return equalsTail(raw);\n if (raw.startsWith('=')) return equalsTail(raw.slice(1).trim());\n return `contains ${quote(raw)}`;\n }\n if (box.kind === 'ref') {\n const raw = (edit.raw ?? '').trim();\n if (!raw) return '';\n if (edit.id != null) return `= ${quote(edit.id)}`;\n return equalsTail(raw);\n }\n return edit.tail ?? '';\n}\n\n/** What a text box shows for a tail: the words it searches for, `=` and the\n value for an exact match on text, the value for a number; '' for a\n condition a box cannot say. The bar's box and an App list's search read it. */\nexport function boxText(box: Box, tail: string): string {\n const fuzzy = box.meta ? columnKind(box.meta.type) === 'text' : false;\n if (tail.startsWith('contains ')) return unquote(tail.slice('contains '.length));\n if (tail.startsWith('= ')) return fuzzy ? `=${unquote(tail.slice(2))}` : unquote(tail.slice(2));\n return '';\n}\n\ninterface FilterBoxesProps {\n boxes: Box[];\n /** The visible question, for what each box reads when untouched. */\n refinement: string;\n edits: Record<string, BoxEdit>;\n onEdit: (column: string, edit: BoxEdit) => void;\n /** A pick is a decision: enums, dates, flags and reference picks submit themselves. */\n onSubmit: () => void;\n}\n\nfunction FilterBoxes({ boxes, refinement, edits, onEdit, onSubmit }: FilterBoxesProps) {\n useMessageLanguage();\n return (\n <>\n {boxes.map((box) => (\n <label key={box.column} className=\"applist-field\" data-column={box.column}>\n <span className=\"applist-field-label\">{box.label}</span>\n <BoxControl\n box={box}\n tail={conditionTail(refinement, box.column) as string}\n edit={edits[box.column]}\n onEdit={(e) => onEdit(box.column, e)}\n onSubmit={onSubmit}\n />\n </label>\n ))}\n </>\n );\n}\n\nfunction BoxControl({\n box,\n tail,\n edit,\n onEdit,\n onSubmit,\n}: {\n box: Box;\n tail: string;\n edit: BoxEdit | undefined;\n onEdit: (e: BoxEdit) => void;\n onSubmit: () => void;\n}) {\n const { kind, meta, label, column } = box;\n if (kind === 'text') {\n /* A search box on a text column searches: `contains`, any case, and an\n exact match is one `=` prefix away. A number is named, so it keeps\n plain equality. */\n const fuzzy = meta ? columnKind(meta.type) === 'text' : false;\n const shown = edit?.raw !== undefined ? edit.raw : boxText(box, tail);\n return (\n <input\n type=\"text\"\n className=\"applist-quick applist-quick-text\"\n value={shown}\n aria-label={say('common.filter2', { label })}\n title={fuzzy ? say('common.matchesAnywhereInTheValue') : meta?.references ? say('filtersByTheId', { column }) : undefined}\n onChange={(e) => onEdit({ raw: e.target.value })}\n />\n );\n }\n if (kind === 'ref') {\n return <RefBox box={box} tail={tail} edit={edit} onEdit={onEdit} onSubmit={onSubmit} />;\n }\n if (kind === 'flag') {\n /* A checkbox, not a dropdown: the question a flag box answers is \"only\n the urgent ones?\", and that is one gesture. An untouched checkbox\n leaves a `= false` it cannot draw exactly as it found it. */\n const checked = edit ? edit.tail === '= true' : tail === '= true';\n return (\n <span className=\"applist-check\">\n <input\n type=\"checkbox\"\n className=\"applist-checkbox\"\n aria-label={say('common.only', { label })}\n checked={checked}\n onChange={(e) => {\n onEdit({ tail: e.target.checked ? '= true' : '' });\n queueMicrotask(onSubmit);\n }}\n />\n </span>\n );\n }\n const options: Array<[string, string]> =\n kind === 'date'\n ? (DATE_OPTIONS as Array<[string, string]>)\n : // A list of choices (a row-defined `choices` field) is asked whether it has the value, not whether it is it.\n ((meta as (ColumnMeta & { allowed?: string[] }) | undefined)?.allowed ?? []).map(\n (v) => [v, kind === 'tags' ? tagsTail([v]) : equalsTail(v)] as [string, string],\n );\n /* A condition the menu has no word for — `= last month` typed in\n Advanced — is preserved until the person actually picks something. */\n const chosen = edit ? (edit.tail ?? '') : (options.find(([, value]) => squeeze(value) === tail)?.[1] ?? '');\n return (\n <Dropdown\n items={[{ value: '', label: 'any' }, ...options.map(([l, value]) => ({ value, label: l }))]}\n value={chosen}\n ariaLabel={say('common.filter2', { label })}\n onChange={(value) => {\n onEdit({ tail: value });\n queueMicrotask(onSubmit);\n }}\n />\n );\n}\n\n/**\n * A reference box: type a name, pick a row, and the query says the id.\n * \"nia\", pick \"Nia Andersen\", the query says owner_id = 14. The list wears\n * the house listbox's clothes and the combobox contract.\n */\n/* The table a reference box names rows of, the column it names them by, and the key it writes. */\nfunction referenceTarget(box: Pick<Box, 'meta'>) {\n const meta = box.meta as (ColumnMeta & { references?: { table: string; column: string } }) | undefined;\n const target = findTable(meta?.references?.table ?? '') as TableMeta | undefined;\n return {\n target,\n labelCol: target ? ((labelPath(target) as string | undefined) ?? primaryKey(target)[0]) : undefined,\n pk: target ? ((primaryKey(target) as string[])[0] ?? 'id') : 'id',\n connection: state.activeConnection,\n };\n}\n\n/**\n * The read of the name behind a reference's id — `= 9` is \"Nia Andersen\".\n * One query for one id, so the box that shows it, an App list's chip and\n * the words a condition is said in (useConditionWords.ts) share it.\n */\nexport function referenceNameQuery(meta: ColumnMeta | undefined, id: string | null, enabled = true) {\n const { target, labelCol, pk, connection } = referenceTarget({ meta });\n return {\n queryKey: ['ref-name', connection, target?.id, pk, id, labelCol],\n queryFn: async (): Promise<string | null> => {\n const data = await api('/api/run', {\n ...ownedBy(connection),\n q: `${target!.id} ${pk} = ${quote(id!)} show ${labelCol} limit 1`,\n limit: 1,\n count: 'none',\n });\n if (data.errors?.length) throw new Error(say('couldNotResolveRecord'));\n const row = data.rows?.[0];\n return row ? String(row[labelCol!]) : null;\n },\n enabled: Boolean(enabled && target && labelCol && id && /^[\\w-]+$/.test(id)),\n staleTime: Infinity,\n };\n}\n\n/** The name behind a reference's id, once known. */\nfunction useReferenceName(box: Box, id: string | null, enabled = true) {\n return useQuery<string | null>(referenceNameQuery(box.meta, id, enabled));\n}\n\n/* `startOpen`: the list is up as the box appears, for a picker opened on\n purpose; `portal`: where the list is drawn (Typeahead.tsx) — both for the\n box inside an App list's popover (ListFilterBar.tsx). */\nexport function RefBox({\n box,\n tail,\n edit,\n onEdit,\n onSubmit,\n startOpen = false,\n portal,\n}: {\n box: Box;\n tail: string;\n edit: BoxEdit | undefined;\n onEdit: (e: BoxEdit) => void;\n onSubmit: () => void;\n startOpen?: boolean;\n portal?: 'dialog' | 'body';\n}) {\n const { target, labelCol, pk, connection } = referenceTarget(box);\n const chosenId = edit ? (edit.id ?? null) : tail.startsWith('= ') ? unquote(tail.slice(2).trim()) : null;\n const [open, setOpen] = useState(startOpen);\n const [asked, setAsked] = useState('');\n\n /* An existing `= 9` shows its name as soon as the name is known; the id\n stays in the tooltip, because the id is the truth. */\n const named = useReferenceName(box, chosenId, !edit);\n const raw = edit?.raw ?? (chosenId ? (named.data ?? chosenId) : '');\n\n // One request per pause in typing, eight names at a time.\n useEffect(() => {\n const term = (edit?.raw ?? '').trim();\n const timer = setTimeout(() => setAsked(edit && edit.id == null ? term : ''), 160);\n return () => clearTimeout(timer);\n }, [edit?.raw, edit?.id, edit]);\n const matches = useQuery<Array<{ id: unknown; label: string }>>({\n queryKey: ['ref-search', connection, target?.id, pk, asked, labelCol],\n queryFn: async () => {\n const where = asked ? `${labelCol} contains ${quote(asked)}` : '';\n const data = await api('/api/run', {\n ...ownedBy(connection),\n q: [target!.id, where, `show ${[...new Set([labelCol, pk])].join(', ')} sort ${labelCol} limit 8`].filter(Boolean).join(' '),\n limit: 8,\n count: 'none',\n });\n if (data.errors?.length) throw new Error(say('couldNotLoadRecords'));\n return ((data.rows ?? []) as Array<Record<string, unknown>>).map((row) => ({ id: row[pk], label: String(row[labelCol!]) }));\n },\n enabled: Boolean(target && labelCol && open),\n retry: false,\n });\n const waiting = (edit && edit.id == null ? (edit.raw ?? '').trim() : '') !== asked;\n const options = waiting || matches.isError ? [] : (matches.data ?? []);\n const empty =\n !target || !labelCol\n ? say('lookupUnavailable')\n : waiting || matches.isPending\n ? 'Loading...'\n : matches.isError\n ? say('couldNotLoadRecordsClose')\n : asked\n ? say('common.nothingMatches')\n : say('noRecordsAvailable');\n\n /* An App's reader picks a record by its name; the key under it is the\n Workbench's, where the id is what a person reads (an App labels its\n columns, the Workbench registers none). */\n const appReader = Boolean(target && labelCol && columnLabel(target.id, labelCol) != null);\n const pick = (item: { id: unknown; label: string }) => {\n onEdit({ raw: item.label, id: String(item.id) });\n setOpen(false);\n queueMicrotask(onSubmit);\n };\n return (\n <>\n <Typeahead\n text={raw}\n onText={(next) => onEdit({ raw: next, id: null })}\n open={open}\n onOpenChange={setOpen}\n items={options}\n itemKey={(item) => String(item.id)}\n itemLabel={(item) => item.label}\n itemClassName={(_item, highlighted) => `dd-option${highlighted ? ' active' : ''}`}\n renderItem={(item) => (\n <span className=\"dd-text\">\n <span className=\"dd-label\">{item.label}</span>\n {appReader ? null : <span className=\"dd-detail\">{String(item.id)}</span>}\n </span>\n )}\n onPick={pick}\n inputProps={{\n type: 'text',\n className: 'applist-quick applist-quick-text',\n 'aria-label': say('common.filter2', { label: box.label }),\n title:\n chosenId && (!edit || edit.id != null) && !appReader\n ? `${box.column} = ${chosenId}`\n : say('typeANameAndPick', { name: target?.name ?? '' }),\n }}\n popupClassName=\"dd-popup\"\n listClassName=\"dd-list\"\n empty={empty}\n emptyClassName=\"picker-empty\"\n portal={portal}\n />\n </>\n );\n}\n\n/**\n * The workbench's mount: the same boxes over the plain table view. No tabs\n * and no Advanced here — the query bar at the top is the workbench's\n * advanced, and the boxes write into the same text it shows.\n */\nexport function FilterBar({\n table,\n entry,\n names,\n navigate,\n}: {\n table: TableMeta;\n entry: { table: string; query: string; tab?: string };\n names: string[];\n navigate: (query: string) => void;\n}) {\n const whole = squeeze(entry.query) as string;\n const refinement = whole.startsWith(entry.table) ? whole.slice(entry.table.length).trim() : whole;\n const boxes = boxesFor(table, names, (name) => name);\n const [edits, setEdits] = useState<Record<string, BoxEdit>>({});\n const pending = useRef(edits);\n pending.current = edits;\n const submit = () => {\n (state as { page: number }).page = 0;\n let text = refinement;\n // Touched boxes only: the text already holds everything else.\n for (const box of boxes) {\n const edit = pending.current[box.column];\n if (edit) text = withCondition(text, box.column, boxTail(box, edit, conditionTail(refinement, box.column) as string));\n }\n navigate(squeeze(`${entry.table} ${text}`) as string);\n };\n if (!boxes.length) return null;\n return (\n <form\n className=\"applist-filterbar\"\n onSubmit={(e) => {\n e.preventDefault();\n submit();\n }}\n >\n <div className=\"applist-fields\">\n <FilterBoxes\n boxes={boxes}\n refinement={refinement}\n edits={edits}\n onEdit={(column, edit) => setEdits((m) => ({ ...m, [column]: edit }))}\n onSubmit={submit}\n />\n <button type=\"submit\" className=\"applist-go\">\n {say('common.filter')}\n </button>\n {refinement && (\n <button\n type=\"button\"\n className=\"applist-toggle\"\n title={say('backToTheUnfiltered')}\n onClick={() => {\n (state as { page: number }).page = 0;\n navigate(entry.table);\n }}\n >\n {say('common.clear')}\n </button>\n )}\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-BIQuK_nq.js","names":["say","say","_Fragment"],"sources":["../../../kit/src/client/copy/filters/components/filterRailModel.ts","../../../kit/src/client/components/filterRailModel.ts","../../../kit/src/client/copy/filters/components/FilterBoxes.ts","../../../kit/src/client/components/FilterBoxes.tsx"],"sourcesContent":["/** What filterRailModel says, in the filter rail room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n thisWeek: 'This week',\n last7Days: 'Last 7 days',\n last30Days: 'Last 30 days',\n overdue: 'Overdue',\n next7Days: 'Next 7 days',\n noDate: 'No date',\n starts: 'Starts with',\n matchesAPattern: 'Matches a pattern',\n isEmpty: 'Is empty',\n isNotEmpty: 'Is not empty',\n});\n","/**\n * The filter rail's rules, before anything is drawn: which facet a column\n * gets, what a facet reads out of the list's question and writes back into\n * it, and the breakdown that counts a facet's values.\n *\n * The rail writes the same language into the same place as the bar's boxes\n * — one condition tail per column, folded in by withCondition — so a rail\n * selection is a URL, a chip and a tab refinement exactly as a box's is.\n * What it adds are the tails a box has no gesture for: several values at\n * once (`in (a, b)`), and a date's presets beside a range. Pure, so the rail\n * and its tests read one rule.\n */\nimport { quote } from '../core.js';\nimport { splitQuery } from '../clauses.js';\nimport { conditionTail, holdsCondition, squeeze, stripCondition, unquote } from '../filterbar.js';\nimport { andQuery } from '@mistriwork/engine/shared/query.js';\nimport type { ColumnMeta } from '@mistriwork/engine/shared/schema.js';\nimport { say } from '../copy/filters/components/filterRailModel.js';\n\n/** What a facet draws: a checklist of values, a checklist of tags, a toggle, date presets and a range, or a contains box. */\ntype FacetKind = 'values' | 'tags' | 'flag' | 'date' | 'text';\n\n/** A box kind as a facet: a vocabulary and a reference are both a checklist of values; a record's tags, of its tags. */\nexport const facetKind = (boxKind: string): FacetKind =>\n boxKind === 'enum' || boxKind === 'ref' ? 'values' : boxKind === 'flag' || boxKind === 'date' || boxKind === 'tags' ? boxKind : 'text';\n\n/* A value list split on its commas, never on a comma inside quotes. */\nfunction listItems(text: string): string[] {\n const out: string[] = [];\n let item = '';\n let inside: string | null = null;\n for (let i = 0; i < text.length; i++) {\n const ch = text[i];\n if (inside) {\n item += ch;\n if (ch === '\\\\') {\n item += text[++i] ?? '';\n continue;\n }\n if (ch === inside) inside = null;\n } else if (ch === '\"' || ch === \"'\") {\n inside = ch;\n item += ch;\n } else if (ch === ',') {\n out.push(item);\n item = '';\n } else item += ch;\n }\n out.push(item);\n return out.map((s) => unquote(s.trim())).filter(Boolean);\n}\n\n/**\n * The values a tail selects: `= a` one, `in (a, b)` several, none for no\n * tail. Null for a condition a checklist cannot draw — `!= a`, `is empty`\n * typed in Advanced — which stays as written until someone ticks a value.\n */\nexport function selectedValues(tail: string): string[] | null {\n const t = squeeze(tail);\n if (!t) return [];\n if (t.startsWith('= ')) return [unquote(t.slice(2))];\n const list = /^in\\s*[([](.*)[)\\]]$/i.exec(t);\n return list ? listItems(list[1]) : null;\n}\n\n/** The tail for a set of ticked values: one is an equality, several an `in`, none no condition. */\nexport function valuesTail(values: readonly string[]): string {\n if (!values.length) return '';\n if (values.length === 1) return `= ${quote(values[0])}`;\n return `in (${values.map((value) => quote(value)).join(', ')})`;\n}\n\n/**\n * The tags a record's-tags tail asks for: `has a` one, `has (a, b)` any of\n * several, none for no tail. Null for anything else typed by hand.\n */\nexport function selectedTags(tail: string): string[] | null {\n const t = squeeze(tail);\n if (!t) return [];\n const list = /^has\\s*[([](.*)[)\\]]$/i.exec(t);\n if (list) return listItems(list[1]);\n const one = /^has\\s+(.+)$/i.exec(t);\n return one ? [unquote(one[1].trim())] : null;\n}\n\n/** The tail for a set of ticked tags: a record that has any of them. */\nexport function tagsTail(values: readonly string[]): string {\n if (!values.length) return '';\n if (values.length === 1) return `has ${quote(values[0])}`;\n return `has (${values.map((value) => quote(value)).join(', ')})`;\n}\n\n/* A date column's meaning, read off its name and default — the signals a\n catalog carries. A stamp records when something happened (`created_at`,\n `signed_up_on`, a `DEFAULT CURRENT_TIMESTAMP`): it is only ever in the\n past, so it is asked about windows behind today. A deadline says when\n something is due (`due_on`, `expires_at`, `ship_by`): it is asked what is\n overdue and what is coming. Anything else — a birthday, a start date — is\n asked about the days on either side of today. */\nconst STAMP =\n /^(sys_)?(created|inserted|added|opened|raised|issued|ordered|received|signed_up|joined|updated|modified|changed|last_updated|submitted|sent|posted|published|logged|recorded|completed|closed|resolved|shipped|delivered|paid|approved|signed|happened|occurred|deleted|archived|last_[a-z]+)(_at|_on|_date|_time)?$/i;\nconst DEADLINE =\n /(^|_)(due|deadline|expires?|expiry|expiration|ship_by|respond_by|needed_by|renews?|renewal|follow_up|valid_until|until)(_|$)/i;\nconst STAMPED_DEFAULT = /current_timestamp|current_date|now\\(\\)|localtimestamp|getdate|sysdate/i;\n\ntype DateMeaning = 'stamp' | 'deadline' | 'date';\n\n/** What a date column is to the reader: a stamp of the past, a deadline, or a plain date. */\nexport function dateMeaning(column: (Pick<ColumnMeta, 'name'> & Partial<Pick<ColumnMeta, 'default'>>) | undefined): DateMeaning {\n const name = column?.name.split('.').pop() ?? '';\n if (DEADLINE.test(name)) return 'deadline';\n if (STAMP.test(name) || (column?.default !== undefined && STAMPED_DEFAULT.test(column.default))) return 'stamp';\n return 'date';\n}\n\n/* The windows each kind of date is asked about, in the query language's own\n phrases, which resolve on the server, so a link means the same thing\n tomorrow. \"Overdue\" is before today, and is offered only on a deadline:\n on a `created_at` it was every row. */\nconst PRESETS: Record<DateMeaning, ReadonlyArray<readonly [label: string, tail: string]>> = {\n stamp: [\n [say('common.today'), '= today'],\n [say('thisWeek'), '= this week'],\n [say('last7Days'), '= last 7 days'],\n [say('last30Days'), '= last 30 days'],\n ],\n deadline: [\n [say('overdue'), '< today'],\n [say('common.today'), '= today'],\n [say('thisWeek'), '= this week'],\n [say('next7Days'), '= next 7 days'],\n ],\n date: [\n [say('common.today'), '= today'],\n [say('thisWeek'), '= this week'],\n [say('last7Days'), '= last 7 days'],\n [say('next7Days'), '= next 7 days'],\n ],\n};\n\n/** A date facet's presets, fitted to what the column means; \"No date\" only where the column may be empty. */\nexport function datePresets(\n column: (Pick<ColumnMeta, 'name'> & Partial<Pick<ColumnMeta, 'default' | 'nullable'>>) | undefined,\n): ReadonlyArray<readonly [label: string, tail: string]> {\n const presets = PRESETS[dateMeaning(column)];\n return column?.nullable === false ? presets : [...presets, [say('noDate'), 'is empty']];\n}\n\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** The ends of a date range a tail says, '' for an open end; both '' for a tail that is not a range. */\nexport function rangeOf(tail: string): { from: string; to: string } {\n const t = squeeze(tail);\n const between = /^between\\s+(\\S+)\\s+and\\s+(\\S+)$/i.exec(t);\n if (between && ISO_DATE.test(unquote(between[1])) && ISO_DATE.test(unquote(between[2]))) {\n return { from: unquote(between[1]), to: unquote(between[2]) };\n }\n const end = /^(>=|<=)\\s*(\\S+)$/.exec(t);\n if (end && ISO_DATE.test(unquote(end[2]))) return end[1] === '>=' ? { from: unquote(end[2]), to: '' } : { from: '', to: unquote(end[2]) };\n return { from: '', to: '' };\n}\n\n/** A range as a tail: both ends inclusive, in order whichever end was set first; one end is a bound. */\nexport function rangeTail(from: string, to: string): string {\n const [a, b] = from && to && from > to ? [to, from] : [from, to];\n if (a && b) return `between ${a} and ${b}`;\n if (a) return `>= ${a}`;\n if (b) return `<= ${b}`;\n return '';\n}\n\n/* The view's standing condition and the reader's filter, as one condition. */\nconst both = (standing: string, filter: string) => {\n const [a, b] = [squeeze(standing), squeeze(filter)];\n return a && b ? andQuery(a, b) : a || b;\n};\n\n/**\n * The breakdown that counts a facet's values under everything else the\n * list asks. The facet's own condition is left out — the usual faceted\n * count, so a second value is offered with what ticking it would add — and\n * so are the sort and the page, which a count does not have. A reference is\n * grouped by its id and said by its target's label path. The view's\n * standing condition (`status != sold`) is the list's and stays: the reader's\n * question hides it, and a count without it counted rows the list never shows.\n */\nexport function facetCountText(table: string, refinement: string, column: string, labelPath?: string, standing = ''): string {\n const filter = splitQuery(`${table} ${stripCondition(refinement, column)}`).filter ?? '';\n return squeeze(`${table} ${both(standing, filter)} count by ${[column, labelPath].filter(Boolean).join(', ')} sort count desc`);\n}\n\n/**\n * How many of the list's records one value of a `has` facet would show\n * (shared/listFilters.ts): the list's rows, under everything else it asks,\n * with one of the rows that point at each holding the value — records, never\n * those rows, so an account with three won deals counts once. One count per\n * value, since a record is under every value one of its rows holds.\n */\nexport function hasCountText(table: string, refinement: string, key: string, standing = '', value?: string): string {\n const filter = splitQuery(`${table} ${stripCondition(refinement, key)}`).filter ?? '';\n const asked = value === undefined ? key : `${key} = ${quote(value)}`;\n return squeeze(`${table} ${andQuery(both(standing, filter), asked)} count`);\n}\n\n/** The values a `has` facet lists: those the rows that point at the record hold, the most held first, by name where they are references. */\nexport function hasValuesText(child: string, column: string, labelPath?: string): string {\n return squeeze(`${child} count by ${[column, labelPath].filter(Boolean).join(', ')} sort count desc`);\n}\n\n/* ---------- a facet's shape ---------- */\n\n/**\n * A column with no vocabulary, flag, date or reference shows as what its\n * values are. Asked once, of the view's standing rows alone — not of the\n * reader's narrowing, or a price would turn into a checklist of the dozen\n * prices a filter left. A number with more than RANGE_ABOVE distinct values is\n * a range; a few (a priority, a model year) a checklist; text with at most\n * CHECKLIST_MOST values a checklist, more a contains box.\n */\nconst RANGE_ABOVE = 12;\nexport const CHECKLIST_MOST = 50;\nexport type AutoShape = 'values' | 'range' | 'text';\n\n/** The breakdown a facet's shape is judged by: every value of the column the view shows. */\nexport function facetShapeText(table: string, column: string, standing = ''): string {\n return squeeze(`${table} ${squeeze(standing)} count by ${column} sort count desc`);\n}\n\n/** A column's facet from how many values it has; `complete` is false when there were more than were asked for. */\nexport function autoShape(numeric: boolean, distinct: number, complete: boolean): AutoShape {\n if (numeric) return complete && distinct <= RANGE_ABOVE ? 'values' : 'range';\n return complete && distinct <= CHECKLIST_MOST ? 'values' : 'text';\n}\n\n/* ---------- a number's range ---------- */\n\nconst NUMBER = /^-?\\d+(?:\\.\\d+)?$/;\n\n/** The ends of a number range a tail says, '' for an open end; both '' for a tail that is not one. */\nexport function numberRangeOf(tail: string): { min: string; max: string } {\n const t = squeeze(tail);\n const between = /^between\\s+(\\S+)\\s+and\\s+(\\S+)$/i.exec(t);\n if (between && NUMBER.test(between[1]) && NUMBER.test(between[2])) return { min: between[1], max: between[2] };\n const end = /^(>=|<=)\\s*(\\S+)$/.exec(t);\n if (end && NUMBER.test(end[2])) return end[1] === '>=' ? { min: end[2], max: '' } : { min: '', max: end[2] };\n return { min: '', max: '' };\n}\n\n/** A number range as a tail: both ends inclusive and in order, one end a bound, a non-number ignored. */\nexport function numberRangeTail(min: string, max: string): string {\n const [lo, hi] = [min.trim(), max.trim()].map((end) => (NUMBER.test(end) ? end : ''));\n const [a, b] = lo && hi && Number(lo) > Number(hi) ? [hi, lo] : [lo, hi];\n if (a && b) return `between ${a} and ${b}`;\n if (a) return `>= ${a}`;\n if (b) return `<= ${b}`;\n return '';\n}\n\n/** Whether a facet says anything beyond the lit tab: the tab's own condition is the tab's to show. */\nexport function facetActive(refinement: string, column: string, tabQuery = ''): boolean {\n const tail = squeeze(conditionTail(refinement, column));\n // A whole-condition filter (`any opportunity where open`) has no tail: it is on where it is said.\n if (!tail) return holdsCondition(refinement, column) && !holdsCondition(tabQuery, column);\n return tail !== squeeze(conditionTail(tabQuery, column));\n}\n\n/** Every facet's own conditions out, the lit tab's left standing: Clear all empties the rail, not the tab. */\nexport function clearFacets(refinement: string, columns: readonly string[], tabQuery = ''): string {\n return columns.reduce((text, column) => (facetActive(text, column, tabQuery) ? stripCondition(text, column) : text), squeeze(refinement));\n}\n\n/* ---------- the rarer conditions ---------- */\n\n/** A condition a facet keeps behind \"More conditions\": its word, and whether it takes text. */\nexport type MoreCondition = 'startswith' | 'like' | 'empty' | 'notempty';\nexport const MORE_LABELS: Record<MoreCondition, string> = {\n startswith: say('starts'),\n like: say('matchesAPattern'),\n empty: say('isEmpty'),\n notempty: say('isNotEmpty'),\n};\nexport const takesText = (condition: MoreCondition) => condition === 'startswith' || condition === 'like';\n\n/**\n * What a facet offers behind its \"More conditions\": text is asked what it\n * starts with and a pattern — not a vocabulary, whose values are a list to\n * pick from; a column that may be empty, whether it is. A\n * date's \"No date\" preset already asks for empty, so it is not asked twice;\n * a source that takes whole values only (an API) is asked nothing more.\n */\nexport function moreConditions(\n facet: 'values' | 'flag' | 'date' | 'text',\n column: { type?: string; nullable?: boolean; allowed?: readonly string[] } | undefined,\n exact = false,\n): MoreCondition[] {\n if (exact || !column) return [];\n const text = /char|text|string|clob|citext/i.test(column.type ?? '') && !column.allowed?.length;\n const out: MoreCondition[] = text && facet !== 'flag' ? ['startswith', 'like'] : [];\n if (column.nullable !== false) {\n if (facet !== 'date') out.push('empty');\n out.push('notempty');\n }\n return out;\n}\n\n/** Which of them a tail says, with its text; null for a tail that is none of them. */\nexport function moreOf(tail: string): { condition: MoreCondition; text: string } | null {\n const t = squeeze(tail);\n if (t === 'is empty') return { condition: 'empty', text: '' };\n if (t === 'is not empty') return { condition: 'notempty', text: '' };\n const said = /^(startswith|like)\\s+(.+)$/i.exec(t);\n return said ? { condition: said[1].toLowerCase() as MoreCondition, text: unquote(said[2]) } : null;\n}\n\n/** The tail for a rarer condition; '' for one that takes text and has none yet. */\nexport function moreTail(condition: MoreCondition, text = ''): string {\n if (condition === 'empty') return 'is empty';\n if (condition === 'notempty') return 'is not empty';\n const value = text.trim();\n return value ? `${condition} ${quote(value)}` : '';\n}\n","/** What FilterBoxes says, in the filter rail room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n filtersByTheId: 'Filters by {column} — the id, even where the column shows a name.',\n couldNotResolveRecord: 'Could not resolve record name.',\n couldNotLoadRecords: 'Could not load records.',\n lookupUnavailable: 'Lookup unavailable.',\n couldNotLoadRecordsClose: 'Could not load records. Close and reopen to retry.',\n noRecordsAvailable: 'No records available.',\n typeANameAndPick: 'Type a {name} name and pick — or an id.',\n backToTheUnfiltered: 'Back to the unfiltered table',\n});\n","/**\n * Named filter boxes over a table — one grammar, two modes.\n *\n * The workbench mounts the boxes above the plain table view; an app mounts\n * the same boxes inside its own filter section, with tabs and Advanced\n * around them. Both write the same language into the same place — the\n * entry's query text — so a box's state is a condition *tail*: `= open`,\n * `= last 30 days`, `is empty`, or '' for none.\n *\n * The parent holds what the person has set by hand, box by box. Only a\n * touched box speaks on submit: an untouched one used to read the query\n * back through its own narrow eyes and rewrite it, and a tab whose column\n * held two conditions came back holding one. The text already holds\n * whatever the boxes have not touched.\n */\nimport { useMessageLanguage } from './useMessageLanguage.js';\nimport { ownedBy } from '../requestIdentity.js';\nimport { useEffect, useRef, useState } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { api, columnLabel, findTable, labelPath, primaryKey, quote, state } from '../core.js';\nimport { columnKind, type ColumnMeta, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { DATE_OPTIONS, boxKind, conditionTail, equalsTail, squeeze, unquote, withCondition } from '../filterbar.js';\nimport { Dropdown } from './Dropdown.js';\nimport { tagsTail } from './filterRailModel.js';\nimport { presentationColumns } from './columnPresentation.js';\nimport { hasFilterOf } from '@mistriwork/engine/shared/listFilters.js';\nimport { tryResolvePath } from '@mistriwork/engine/shared/view.js';\nimport { Typeahead } from '@mistriwork/ui/Typeahead.js';\nimport { say } from '../copy/filters/components/FilterBoxes.js';\n\ntype BoxKind = 'text' | 'ref' | 'flag' | 'enum' | 'date' | 'tags';\n\n/** What a person set in a box: the text they typed and, for a reference, the row they picked; or a chosen tail. */\nexport interface BoxEdit {\n raw?: string;\n id?: string | null;\n tail?: string;\n}\n\n/** `exact`: the source matches whole values only (an API), so typed text is asked as `=`, never `contains`. */\nexport interface Box {\n column: string;\n label: string;\n kind: BoxKind;\n meta: ColumnMeta | undefined;\n exact?: boolean;\n /** The columns whose values the box suggests as the reader types: the list view's `suggest` (SuggestBox.tsx). */\n suggest?: readonly string[];\n /**\n * The rows that point at the record, where the box asks about them (shared/listFilters.ts): their table, and\n * the column or path of theirs it asks — `meta` is that column — or, `segment`, one of their segments, whose\n * box is a yes with no value.\n */\n has?: { child: string; asks: string; segment: boolean };\n}\n\n/* A column a path ends on, dressed as the box kind reads it: its own name, on the table that holds it, so a\n reference across a hop is a typeahead of its records as one on the list's own table is. */\nfunction kindAt(table: TableMeta | undefined, path: string, meta: ColumnMeta | undefined): BoxKind {\n if (!meta || !table || !state.schema || !path.includes('.')) return boxKind(meta, table?.id) as BoxKind;\n const { resolved } = tryResolvePath(state.schema, table.id, path);\n return boxKind(resolved ? { ...meta, name: resolved.column.name } : meta, resolved?.table.id) as BoxKind;\n}\n\n/** The boxes a table gets, for a set of column names. */\nexport function boxesFor(table: TableMeta | undefined, names: string[], labelOf: (name: string) => string): Box[] {\n const columns = presentationColumns(\n state.schema,\n table,\n names.filter((name) => !hasFilterOf(name)),\n );\n return names.map((name) => {\n const has = hasFilterOf(name);\n if (has) {\n const child = (findTable(has.child) ?? state.schema?.tables.find((t: TableMeta) => t.name === has.child)) as TableMeta | undefined;\n const segment = Boolean(child?.segments && Object.hasOwn(child.segments, has.asks));\n const meta = segment ? undefined : (presentationColumns(state.schema, child, [has.asks]).get(has.asks) as ColumnMeta | undefined);\n return {\n column: name,\n label: labelOf(name),\n kind: segment ? 'flag' : kindAt(child, has.asks, meta),\n meta,\n has: { child: child?.id ?? has.child, asks: has.asks, segment },\n };\n }\n const meta = columns.get(name) as (ColumnMeta & { allowed?: string[] }) | undefined;\n return {\n column: name,\n label: labelOf(name),\n kind: kindAt(table, name, meta),\n meta,\n ...(table?.filterable ? { exact: true } : {}),\n };\n });\n}\n\n/** The tail a box stands for: what was set by hand, else what the text already says. */\n/** A text box over a number column holding words: not sent, since the\n database refuses words compared with a number (PostgreSQL's 22P02), and\n the box says what it takes instead. */\nexport function wrongForNumber(box: Box, raw: string | undefined): boolean {\n const text = (raw ?? '').trim().replace(/^=\\s*/, '');\n return (\n Boolean(text) &&\n box.meta !== undefined &&\n columnKind(box.meta.type) === 'number' &&\n !/^[-+]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[-+]?\\d+)?$/i.test(text)\n );\n}\n\nexport function boxTail(box: Box, edit: BoxEdit | undefined, current: string): string {\n if (!edit) return current;\n if (box.kind === 'text') {\n const raw = (edit.raw ?? '').trim();\n if (!raw) return '';\n if (wrongForNumber(box, raw)) return current;\n const fuzzy = !box.exact && box.meta ? columnKind(box.meta.type) === 'text' : false;\n if (!fuzzy) return equalsTail(raw);\n if (raw.startsWith('=')) return equalsTail(raw.slice(1).trim());\n return `contains ${quote(raw)}`;\n }\n if (box.kind === 'ref') {\n const raw = (edit.raw ?? '').trim();\n if (!raw) return '';\n if (edit.id != null) return `= ${quote(edit.id)}`;\n return equalsTail(raw);\n }\n return edit.tail ?? '';\n}\n\n/** What a text box shows for a tail: the words it searches for, `=` and the\n value for an exact match on text, the value for a number; '' for a\n condition a box cannot say. The bar's box and an App list's search read it. */\nexport function boxText(box: Box, tail: string): string {\n const fuzzy = box.meta ? columnKind(box.meta.type) === 'text' : false;\n if (tail.startsWith('contains ')) return unquote(tail.slice('contains '.length));\n if (tail.startsWith('= ')) return fuzzy ? `=${unquote(tail.slice(2))}` : unquote(tail.slice(2));\n return '';\n}\n\ninterface FilterBoxesProps {\n boxes: Box[];\n /** The visible question, for what each box reads when untouched. */\n refinement: string;\n edits: Record<string, BoxEdit>;\n onEdit: (column: string, edit: BoxEdit) => void;\n /** A pick is a decision: enums, dates, flags and reference picks submit themselves. */\n onSubmit: () => void;\n}\n\nfunction FilterBoxes({ boxes, refinement, edits, onEdit, onSubmit }: FilterBoxesProps) {\n useMessageLanguage();\n return (\n <>\n {boxes.map((box) => (\n <label key={box.column} className=\"applist-field\" data-column={box.column}>\n <span className=\"applist-field-label\">{box.label}</span>\n <BoxControl\n box={box}\n tail={conditionTail(refinement, box.column) as string}\n edit={edits[box.column]}\n onEdit={(e) => onEdit(box.column, e)}\n onSubmit={onSubmit}\n />\n </label>\n ))}\n </>\n );\n}\n\nfunction BoxControl({\n box,\n tail,\n edit,\n onEdit,\n onSubmit,\n}: {\n box: Box;\n tail: string;\n edit: BoxEdit | undefined;\n onEdit: (e: BoxEdit) => void;\n onSubmit: () => void;\n}) {\n const { kind, meta, label, column } = box;\n if (kind === 'text') {\n /* A search box on a text column searches: `contains`, any case, and an\n exact match is one `=` prefix away. A number is named, so it keeps\n plain equality. */\n const fuzzy = meta ? columnKind(meta.type) === 'text' : false;\n const shown = edit?.raw !== undefined ? edit.raw : boxText(box, tail);\n return (\n <input\n type=\"text\"\n className=\"applist-quick applist-quick-text\"\n value={shown}\n aria-label={say('common.filter2', { label })}\n title={fuzzy ? say('common.matchesAnywhereInTheValue') : meta?.references ? say('filtersByTheId', { column }) : undefined}\n onChange={(e) => onEdit({ raw: e.target.value })}\n />\n );\n }\n if (kind === 'ref') {\n return <RefBox box={box} tail={tail} edit={edit} onEdit={onEdit} onSubmit={onSubmit} />;\n }\n if (kind === 'flag') {\n /* A checkbox, not a dropdown: the question a flag box answers is \"only\n the urgent ones?\", and that is one gesture. An untouched checkbox\n leaves a `= false` it cannot draw exactly as it found it. */\n const checked = edit ? edit.tail === '= true' : tail === '= true';\n return (\n <span className=\"applist-check\">\n <input\n type=\"checkbox\"\n className=\"applist-checkbox\"\n aria-label={say('common.only', { label })}\n checked={checked}\n onChange={(e) => {\n onEdit({ tail: e.target.checked ? '= true' : '' });\n queueMicrotask(onSubmit);\n }}\n />\n </span>\n );\n }\n const options: Array<[string, string]> =\n kind === 'date'\n ? (DATE_OPTIONS as Array<[string, string]>)\n : // A list of choices (a row-defined `choices` field) is asked whether it has the value, not whether it is it.\n ((meta as (ColumnMeta & { allowed?: string[] }) | undefined)?.allowed ?? []).map(\n (v) => [v, kind === 'tags' ? tagsTail([v]) : equalsTail(v)] as [string, string],\n );\n /* A condition the menu has no word for — `= last month` typed in\n Advanced — is preserved until the person actually picks something. */\n const chosen = edit ? (edit.tail ?? '') : (options.find(([, value]) => squeeze(value) === tail)?.[1] ?? '');\n return (\n <Dropdown\n items={[{ value: '', label: 'any' }, ...options.map(([l, value]) => ({ value, label: l }))]}\n value={chosen}\n ariaLabel={say('common.filter2', { label })}\n onChange={(value) => {\n onEdit({ tail: value });\n queueMicrotask(onSubmit);\n }}\n />\n );\n}\n\n/**\n * A reference box: type a name, pick a row, and the query says the id.\n * \"nia\", pick \"Nia Andersen\", the query says owner_id = 14. The list wears\n * the house listbox's clothes and the combobox contract.\n */\n/* The table a reference box names rows of, the column it names them by, and the key it writes. */\nfunction referenceTarget(box: Pick<Box, 'meta'>) {\n const meta = box.meta as (ColumnMeta & { references?: { table: string; column: string } }) | undefined;\n const target = findTable(meta?.references?.table ?? '') as TableMeta | undefined;\n return {\n target,\n labelCol: target ? ((labelPath(target) as string | undefined) ?? primaryKey(target)[0]) : undefined,\n pk: target ? ((primaryKey(target) as string[])[0] ?? 'id') : 'id',\n connection: state.activeConnection,\n };\n}\n\n/**\n * The read of the name behind a reference's id — `= 9` is \"Nia Andersen\".\n * One query for one id, so the box that shows it, an App list's chip and\n * the words a condition is said in (useConditionWords.ts) share it.\n */\nexport function referenceNameQuery(meta: ColumnMeta | undefined, id: string | null, enabled = true) {\n const { target, labelCol, pk, connection } = referenceTarget({ meta });\n return {\n queryKey: ['ref-name', connection, target?.id, pk, id, labelCol],\n queryFn: async (): Promise<string | null> => {\n const data = await api('/api/run', {\n ...ownedBy(connection),\n q: `${target!.id} ${pk} = ${quote(id!)} show ${labelCol} limit 1`,\n limit: 1,\n count: 'none',\n });\n if (data.errors?.length) throw new Error(say('couldNotResolveRecord'));\n const row = data.rows?.[0];\n return row ? String(row[labelCol!]) : null;\n },\n enabled: Boolean(enabled && target && labelCol && id && /^[\\w-]+$/.test(id)),\n staleTime: Infinity,\n };\n}\n\n/** The name behind a reference's id, once known. */\nfunction useReferenceName(box: Box, id: string | null, enabled = true) {\n return useQuery<string | null>(referenceNameQuery(box.meta, id, enabled));\n}\n\n/* `startOpen`: the list is up as the box appears, for a picker opened on\n purpose; `portal`: where the list is drawn (Typeahead.tsx) — both for the\n box inside an App list's popover (ListFilterBar.tsx). */\nexport function RefBox({\n box,\n tail,\n edit,\n onEdit,\n onSubmit,\n startOpen = false,\n portal,\n}: {\n box: Box;\n tail: string;\n edit: BoxEdit | undefined;\n onEdit: (e: BoxEdit) => void;\n onSubmit: () => void;\n startOpen?: boolean;\n portal?: 'dialog' | 'body';\n}) {\n const { target, labelCol, pk, connection } = referenceTarget(box);\n const chosenId = edit ? (edit.id ?? null) : tail.startsWith('= ') ? unquote(tail.slice(2).trim()) : null;\n const [open, setOpen] = useState(startOpen);\n const [asked, setAsked] = useState('');\n\n /* An existing `= 9` shows its name as soon as the name is known; the id\n stays in the tooltip, because the id is the truth. */\n const named = useReferenceName(box, chosenId, !edit);\n const raw = edit?.raw ?? (chosenId ? (named.data ?? chosenId) : '');\n\n // One request per pause in typing, eight names at a time.\n useEffect(() => {\n const term = (edit?.raw ?? '').trim();\n const timer = setTimeout(() => setAsked(edit && edit.id == null ? term : ''), 160);\n return () => clearTimeout(timer);\n }, [edit?.raw, edit?.id, edit]);\n const matches = useQuery<Array<{ id: unknown; label: string }>>({\n queryKey: ['ref-search', connection, target?.id, pk, asked, labelCol],\n queryFn: async () => {\n const where = asked ? `${labelCol} contains ${quote(asked)}` : '';\n const data = await api('/api/run', {\n ...ownedBy(connection),\n q: [target!.id, where, `show ${[...new Set([labelCol, pk])].join(', ')} sort ${labelCol} limit 8`].filter(Boolean).join(' '),\n limit: 8,\n count: 'none',\n });\n if (data.errors?.length) throw new Error(say('couldNotLoadRecords'));\n return ((data.rows ?? []) as Array<Record<string, unknown>>).map((row) => ({ id: row[pk], label: String(row[labelCol!]) }));\n },\n enabled: Boolean(target && labelCol && open),\n retry: false,\n });\n const waiting = (edit && edit.id == null ? (edit.raw ?? '').trim() : '') !== asked;\n const options = waiting || matches.isError ? [] : (matches.data ?? []);\n const empty =\n !target || !labelCol\n ? say('lookupUnavailable')\n : waiting || matches.isPending\n ? 'Loading...'\n : matches.isError\n ? say('couldNotLoadRecordsClose')\n : asked\n ? say('common.nothingMatches')\n : say('noRecordsAvailable');\n\n /* An App's reader picks a record by its name; the key under it is the\n Workbench's, where the id is what a person reads (an App labels its\n columns, the Workbench registers none). */\n const appReader = Boolean(target && labelCol && columnLabel(target.id, labelCol) != null);\n const pick = (item: { id: unknown; label: string }) => {\n onEdit({ raw: item.label, id: String(item.id) });\n setOpen(false);\n queueMicrotask(onSubmit);\n };\n return (\n <>\n <Typeahead\n text={raw}\n onText={(next) => onEdit({ raw: next, id: null })}\n open={open}\n onOpenChange={setOpen}\n items={options}\n itemKey={(item) => String(item.id)}\n itemLabel={(item) => item.label}\n itemClassName={(_item, highlighted) => `dd-option${highlighted ? ' active' : ''}`}\n renderItem={(item) => (\n <span className=\"dd-text\">\n <span className=\"dd-label\">{item.label}</span>\n {appReader ? null : <span className=\"dd-detail\">{String(item.id)}</span>}\n </span>\n )}\n onPick={pick}\n inputProps={{\n type: 'text',\n className: 'applist-quick applist-quick-text',\n 'aria-label': say('common.filter2', { label: box.label }),\n title:\n chosenId && (!edit || edit.id != null) && !appReader\n ? `${box.column} = ${chosenId}`\n : say('typeANameAndPick', { name: target?.name ?? '' }),\n }}\n popupClassName=\"dd-popup\"\n listClassName=\"dd-list\"\n empty={empty}\n emptyClassName=\"picker-empty\"\n portal={portal}\n />\n </>\n );\n}\n\n/**\n * The workbench's mount: the same boxes over the plain table view. No tabs\n * and no Advanced here — the query bar at the top is the workbench's\n * advanced, and the boxes write into the same text it shows.\n */\nexport function FilterBar({\n table,\n entry,\n names,\n navigate,\n}: {\n table: TableMeta;\n entry: { table: string; query: string; tab?: string };\n names: string[];\n navigate: (query: string) => void;\n}) {\n const whole = squeeze(entry.query) as string;\n const refinement = whole.startsWith(entry.table) ? whole.slice(entry.table.length).trim() : whole;\n const boxes = boxesFor(table, names, (name) => name);\n const [edits, setEdits] = useState<Record<string, BoxEdit>>({});\n const pending = useRef(edits);\n pending.current = edits;\n const submit = () => {\n (state as { page: number }).page = 0;\n let text = refinement;\n // Touched boxes only: the text already holds everything else.\n for (const box of boxes) {\n const edit = pending.current[box.column];\n if (edit) text = withCondition(text, box.column, boxTail(box, edit, conditionTail(refinement, box.column) as string));\n }\n navigate(squeeze(`${entry.table} ${text}`) as string);\n };\n if (!boxes.length) return null;\n return (\n <form\n className=\"applist-filterbar\"\n onSubmit={(e) => {\n e.preventDefault();\n submit();\n }}\n >\n <div className=\"applist-fields\">\n <FilterBoxes\n boxes={boxes}\n refinement={refinement}\n edits={edits}\n onEdit={(column, edit) => setEdits((m) => ({ ...m, [column]: edit }))}\n onSubmit={submit}\n />\n <button type=\"submit\" className=\"applist-go\">\n {say('common.filter')}\n </button>\n {refinement && (\n <button\n type=\"button\"\n className=\"applist-toggle\"\n title={say('backToTheUnfiltered')}\n onClick={() => {\n (state as { page: number }).page = 0;\n navigate(entry.table);\n }}\n >\n {say('common.clear')}\n </button>\n )}\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { F as primaryKey } from "./shared-DJZjSi3J.js";
2
- import { ut as useQueries } from "./shared-CNmaAv64.js";
3
- import { L as state, d as findTable, i as api, st as ownedBy, w as quote } from "./shared-DPAVoWY0.js";
2
+ import { ut as useQueries } from "./shared-BHsjawAG.js";
3
+ import { L as state, d as findTable, i as api, st as ownedBy, w as quote } from "./shared-BoLbc2WD.js";
4
4
  import { a as served } from "./shared-sZ8B7ZTw.js";
5
5
  import "./shared-CS6uipL4.js";
6
6
  import { createContext, useMemo } from "react";
@@ -126,4 +126,4 @@ function AvatarPhotos({ columns, rows, children }) {
126
126
  }
127
127
  export { inlineFileAddress as a, useAvatarPhotos as i, AvatarPhotos as n, avatarOwner as r, AvatarPhotoContext as t };
128
128
 
129
- //# sourceMappingURL=shared-icSQAj21.js.map
129
+ //# sourceMappingURL=shared-BOe4W2FI.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-icSQAj21.js","names":[],"sources":["../../../kit/src/client/components/cardsModel.ts","../../../kit/src/client/components/avatarPhotos.tsx"],"sourcesContent":["/**\n * What a list asks of its cards before it draws them, kept out of\n * CardsList.tsx so the cards themselves load on demand (onDemand.tsx): the\n * list decides whether its answer can stand as cards on the first page, and\n * the file route's picture address is written by records, galleries and\n * avatars that draw no card at all.\n */\nimport { served } from '../addressMount.js';\nimport { ownedBy } from '../requestIdentity.js';\nimport { primaryKey, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { cardsColumns, type AppCardsList } from '@mistriwork/engine/shared/appCards.js';\n\n/** A presentation stands in for the grid only when the answer carries every column it reads. */\nexport function canShowCards(table: TableMeta | undefined, columns: string[] | undefined, spec: AppCardsList): table is TableMeta {\n return Boolean(\n table && primaryKey(table).length && [...cardsColumns(spec), ...primaryKey(table)].every((name) => columns?.includes(name)),\n );\n}\n\n/**\n * The file route's inline address for one file row. `thumb` asks for the\n * resource's smaller copy where it declares one (`file.thumbnail`) — what a\n * card or a gallery strip draws at a few hundred pixels — and the file\n * itself where it does not.\n */\nexport function inlineFileAddress(\n tableId: string,\n key: Record<string, unknown>,\n connection: string | null | undefined,\n variant?: 'thumb',\n): string {\n return served(\n `/api/file?${new URLSearchParams({ ...ownedBy(connection ?? null), table: tableId, key: JSON.stringify(key), inline: '1', ...(variant ? { variant } : {}) })}`,\n );\n}\n","/**\n * A person's photo in their avatar: a display rule's `avatar: { from, whose }`\n * names a file resource of photos, and the disc before the name is its first\n * file for the record, drawn through the file route — the reader's own read,\n * so a reader whose grant leaves the photos out sees the initials, as does a\n * record with no photo or one whose picture will not load.\n *\n * One question per page of rows and photo resource, as a card list asks for\n * its pictures (CardsList.tsx): the owners on the page, sorted so the first\n * row seen for each is its lead. The grid reads the answer through a context,\n * so a row that is already drawn is drawn again only where its photo arrived.\n */\nimport { createContext, useMemo, type ReactNode } from 'react';\nimport { useQueries } from '@tanstack/react-query';\nimport { api, findTable, quote, state } from '../core.js';\nimport { ownedBy } from '../requestIdentity.js';\nimport { primaryKey, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { inlineFileAddress } from './cardsModel.js';\nimport type { DisplayRule } from './valueModel.js';\n\ntype Photo = Exclude<NonNullable<DisplayRule['avatar']>, boolean>;\nconst photoOf = (rule: DisplayRule | null | undefined): Photo | undefined =>\n rule?.avatar && typeof rule.avatar === 'object' ? rule.avatar : undefined;\n\n/** A column's photo, by its owner's key: the grid's rows read it here. */\ntype PhotoLookup = (column: string, owner: unknown) => string | undefined;\nexport const AvatarPhotoContext = createContext<PhotoLookup>(() => undefined);\n\n/**\n * The row column that holds the key of the record a column's avatar is of, or\n * undefined where there is none on the row: the table's own key for its own\n * column, the reference for a one-hop label path (`author_id.display_name`\n * shows a member, so its photo is `author_id`'s).\n */\nexport function avatarOwner(\n table: TableMeta | undefined,\n column: string,\n rule: DisplayRule | null | undefined,\n columns: readonly string[] | undefined,\n): string | undefined {\n if (!photoOf(rule) || !table) return undefined;\n const parts = column.split('.');\n if (parts.length === 1) {\n const key = primaryKey(table);\n return key.length === 1 && columns?.includes(key[0]) ? key[0] : undefined;\n }\n return parts.length === 2 && columns?.includes(parts[0]) ? parts[0] : undefined;\n}\n\nconst literal = (value: unknown) => (typeof value === 'number' ? String(value) : quote(String(value)));\n\n/** The photos of the owners on a page of rows, for each column that draws one. */\nexport function useAvatarPhotos(\n columns: ReadonlyArray<{ name: string; display: DisplayRule | null; photoOwner?: string }>,\n rows: ReadonlyArray<Record<string, unknown>>,\n): PhotoLookup {\n const connection = (state as { activeConnection?: string | null }).activeConnection ?? null;\n const asks = columns.flatMap((column) => {\n const photo = photoOf(column.display);\n const from = photo && column.photoOwner ? (findTable(photo.from) as TableMeta | undefined) : undefined;\n const key = from ? primaryKey(from) : [];\n if (!photo || !from || !key.length || !column.photoOwner) return [];\n const owners = [...new Set(rows.map((row) => row[column.photoOwner!]).filter((value) => value !== null && value !== undefined))];\n if (!owners.length) return [];\n const [sortColumn, direction = 'asc'] = (photo.sort ?? '').split(/\\s+/);\n const text =\n `${from.id} ${photo.whose} in (${owners.map(literal).join(', ')}) sort ${photo.whose}${sortColumn ? `, ${sortColumn} ${direction}` : ''}` +\n ` show ${[...new Set([...key, photo.whose])].join(', ')}`;\n return [{ column: column.name, from, key, whose: photo.whose, text }];\n });\n const answers = useQueries({\n queries: asks.map((ask) => ({\n queryKey: ['run', connection ?? '', 'avatar-photos', ask.text],\n staleTime: 60_000,\n queryFn: async () => {\n const data = (await api('/api/run', { q: ask.text, limit: 500, offset: 0, count: 'none', ...ownedBy(connection) })) as {\n rows?: Array<Record<string, unknown>>;\n errors?: Array<{ message: string }>;\n };\n // A reader who may not read the photos keeps the initials: nothing to say about it.\n return data.errors?.length ? [] : (data.rows ?? []);\n },\n })),\n });\n /* The same lookup for as long as the answers are the same ones: every row reads it, and a new\n function each paint would redraw every memoized row of the grid. */\n const stamp = asks.map((ask, i) => `${ask.text}@${answers[i]?.dataUpdatedAt ?? 0}`).join('\\n');\n return useMemo(() => {\n if (!asks.length) return NO_PHOTOS;\n const byColumn = new Map<string, Map<string, string>>();\n asks.forEach((ask, i) => {\n const lead = new Map<string, string>();\n for (const row of answers[i]?.data ?? []) {\n const owner = String(row[ask.whose]);\n if (!lead.has(owner))\n lead.set(\n owner,\n inlineFileAddress(ask.from.id, Object.fromEntries(ask.key.map((column) => [column, row[column]])), connection, 'thumb'),\n );\n }\n byColumn.set(ask.column, lead);\n });\n return (column: string, owner: unknown) =>\n owner === null || owner === undefined ? undefined : byColumn.get(column)?.get(String(owner));\n }, [stamp]); // eslint-disable-line react-hooks/exhaustive-deps\n}\n\nconst NO_PHOTOS: PhotoLookup = () => undefined;\n\n/**\n * The photos for the rows below, for a grid with a column that draws one; a\n * grid without never asks, and never needs a query client to draw.\n */\nexport function AvatarPhotos({\n columns,\n rows,\n children,\n}: {\n columns: ReadonlyArray<{ name: string; display: DisplayRule | null; photoOwner?: string }>;\n rows: ReadonlyArray<Record<string, unknown>>;\n children: ReactNode;\n}) {\n return <AvatarPhotoContext.Provider value={useAvatarPhotos(columns, rows)}>{children}</AvatarPhotoContext.Provider>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-BOe4W2FI.js","names":[],"sources":["../../../kit/src/client/components/cardsModel.ts","../../../kit/src/client/components/avatarPhotos.tsx"],"sourcesContent":["/**\n * What a list asks of its cards before it draws them, kept out of\n * CardsList.tsx so the cards themselves load on demand (onDemand.tsx): the\n * list decides whether its answer can stand as cards on the first page, and\n * the file route's picture address is written by records, galleries and\n * avatars that draw no card at all.\n */\nimport { served } from '../addressMount.js';\nimport { ownedBy } from '../requestIdentity.js';\nimport { primaryKey, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { cardsColumns, type AppCardsList } from '@mistriwork/engine/shared/appCards.js';\n\n/** A presentation stands in for the grid only when the answer carries every column it reads. */\nexport function canShowCards(table: TableMeta | undefined, columns: string[] | undefined, spec: AppCardsList): table is TableMeta {\n return Boolean(\n table && primaryKey(table).length && [...cardsColumns(spec), ...primaryKey(table)].every((name) => columns?.includes(name)),\n );\n}\n\n/**\n * The file route's inline address for one file row. `thumb` asks for the\n * resource's smaller copy where it declares one (`file.thumbnail`) — what a\n * card or a gallery strip draws at a few hundred pixels — and the file\n * itself where it does not.\n */\nexport function inlineFileAddress(\n tableId: string,\n key: Record<string, unknown>,\n connection: string | null | undefined,\n variant?: 'thumb',\n): string {\n return served(\n `/api/file?${new URLSearchParams({ ...ownedBy(connection ?? null), table: tableId, key: JSON.stringify(key), inline: '1', ...(variant ? { variant } : {}) })}`,\n );\n}\n","/**\n * A person's photo in their avatar: a display rule's `avatar: { from, whose }`\n * names a file resource of photos, and the disc before the name is its first\n * file for the record, drawn through the file route — the reader's own read,\n * so a reader whose grant leaves the photos out sees the initials, as does a\n * record with no photo or one whose picture will not load.\n *\n * One question per page of rows and photo resource, as a card list asks for\n * its pictures (CardsList.tsx): the owners on the page, sorted so the first\n * row seen for each is its lead. The grid reads the answer through a context,\n * so a row that is already drawn is drawn again only where its photo arrived.\n */\nimport { createContext, useMemo, type ReactNode } from 'react';\nimport { useQueries } from '@tanstack/react-query';\nimport { api, findTable, quote, state } from '../core.js';\nimport { ownedBy } from '../requestIdentity.js';\nimport { primaryKey, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { inlineFileAddress } from './cardsModel.js';\nimport type { DisplayRule } from './valueModel.js';\n\ntype Photo = Exclude<NonNullable<DisplayRule['avatar']>, boolean>;\nconst photoOf = (rule: DisplayRule | null | undefined): Photo | undefined =>\n rule?.avatar && typeof rule.avatar === 'object' ? rule.avatar : undefined;\n\n/** A column's photo, by its owner's key: the grid's rows read it here. */\ntype PhotoLookup = (column: string, owner: unknown) => string | undefined;\nexport const AvatarPhotoContext = createContext<PhotoLookup>(() => undefined);\n\n/**\n * The row column that holds the key of the record a column's avatar is of, or\n * undefined where there is none on the row: the table's own key for its own\n * column, the reference for a one-hop label path (`author_id.display_name`\n * shows a member, so its photo is `author_id`'s).\n */\nexport function avatarOwner(\n table: TableMeta | undefined,\n column: string,\n rule: DisplayRule | null | undefined,\n columns: readonly string[] | undefined,\n): string | undefined {\n if (!photoOf(rule) || !table) return undefined;\n const parts = column.split('.');\n if (parts.length === 1) {\n const key = primaryKey(table);\n return key.length === 1 && columns?.includes(key[0]) ? key[0] : undefined;\n }\n return parts.length === 2 && columns?.includes(parts[0]) ? parts[0] : undefined;\n}\n\nconst literal = (value: unknown) => (typeof value === 'number' ? String(value) : quote(String(value)));\n\n/** The photos of the owners on a page of rows, for each column that draws one. */\nexport function useAvatarPhotos(\n columns: ReadonlyArray<{ name: string; display: DisplayRule | null; photoOwner?: string }>,\n rows: ReadonlyArray<Record<string, unknown>>,\n): PhotoLookup {\n const connection = (state as { activeConnection?: string | null }).activeConnection ?? null;\n const asks = columns.flatMap((column) => {\n const photo = photoOf(column.display);\n const from = photo && column.photoOwner ? (findTable(photo.from) as TableMeta | undefined) : undefined;\n const key = from ? primaryKey(from) : [];\n if (!photo || !from || !key.length || !column.photoOwner) return [];\n const owners = [...new Set(rows.map((row) => row[column.photoOwner!]).filter((value) => value !== null && value !== undefined))];\n if (!owners.length) return [];\n const [sortColumn, direction = 'asc'] = (photo.sort ?? '').split(/\\s+/);\n const text =\n `${from.id} ${photo.whose} in (${owners.map(literal).join(', ')}) sort ${photo.whose}${sortColumn ? `, ${sortColumn} ${direction}` : ''}` +\n ` show ${[...new Set([...key, photo.whose])].join(', ')}`;\n return [{ column: column.name, from, key, whose: photo.whose, text }];\n });\n const answers = useQueries({\n queries: asks.map((ask) => ({\n queryKey: ['run', connection ?? '', 'avatar-photos', ask.text],\n staleTime: 60_000,\n queryFn: async () => {\n const data = (await api('/api/run', { q: ask.text, limit: 500, offset: 0, count: 'none', ...ownedBy(connection) })) as {\n rows?: Array<Record<string, unknown>>;\n errors?: Array<{ message: string }>;\n };\n // A reader who may not read the photos keeps the initials: nothing to say about it.\n return data.errors?.length ? [] : (data.rows ?? []);\n },\n })),\n });\n /* The same lookup for as long as the answers are the same ones: every row reads it, and a new\n function each paint would redraw every memoized row of the grid. */\n const stamp = asks.map((ask, i) => `${ask.text}@${answers[i]?.dataUpdatedAt ?? 0}`).join('\\n');\n return useMemo(() => {\n if (!asks.length) return NO_PHOTOS;\n const byColumn = new Map<string, Map<string, string>>();\n asks.forEach((ask, i) => {\n const lead = new Map<string, string>();\n for (const row of answers[i]?.data ?? []) {\n const owner = String(row[ask.whose]);\n if (!lead.has(owner))\n lead.set(\n owner,\n inlineFileAddress(ask.from.id, Object.fromEntries(ask.key.map((column) => [column, row[column]])), connection, 'thumb'),\n );\n }\n byColumn.set(ask.column, lead);\n });\n return (column: string, owner: unknown) =>\n owner === null || owner === undefined ? undefined : byColumn.get(column)?.get(String(owner));\n }, [stamp]); // eslint-disable-line react-hooks/exhaustive-deps\n}\n\nconst NO_PHOTOS: PhotoLookup = () => undefined;\n\n/**\n * The photos for the rows below, for a grid with a column that draws one; a\n * grid without never asks, and never needs a query client to draw.\n */\nexport function AvatarPhotos({\n columns,\n rows,\n children,\n}: {\n columns: ReadonlyArray<{ name: string; display: DisplayRule | null; photoOwner?: string }>;\n rows: ReadonlyArray<Record<string, unknown>>;\n children: ReactNode;\n}) {\n return <AvatarPhotoContext.Provider value={useAvatarPhotos(columns, rows)}>{children}</AvatarPhotoContext.Provider>;\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { Yt as formatPercent, gt as defineMessages } from "./shared-DJZjSi3J.js";
2
- import { c as axisMarks, l as periodTick } from "./shared-CXyd7IKi.js";
2
+ import { c as axisMarks, l as periodTick } from "./shared-DKEOMQ6l.js";
3
3
  import { n as niceScale } from "./shared-Cq8JuR8T.js";
4
- import { G as orderGroups, M as textMeasure } from "./shared-DiFQljcq.js";
4
+ import { G as orderGroups, M as textMeasure } from "./shared-XcSKDp6N.js";
5
5
  import { useLayoutEffect, useRef, useState } from "react";
6
6
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
7
  // packages/kit/src/client/copy/dashboards/components/ChartShapes.ts
@@ -719,4 +719,4 @@ function ChartShape(props) {
719
719
  }
720
720
  export { ChartShape, squarify, tileLabel };
721
721
 
722
- //# sourceMappingURL=shared-BlxoOJSk.js.map
722
+ //# sourceMappingURL=shared-BPpC08ip.js.map