@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.
- package/README.md +4 -3
- package/dist/client/assets/LayoutBuilder-MPzkVU2e.js.map +1 -1
- package/dist/client/assets/index-D2dHI6hR.js.map +1 -1
- package/dist/client/assets/shell-TreN_uLs.js.map +1 -1
- package/dist/docs/access-model.md +1 -1
- package/dist/docs/build-and-deploy.md +0 -1
- package/dist/docs/cli.md +1 -1
- package/dist/docs/glossary.md +3 -3
- package/dist/docs/query.md +9 -21
- package/dist/docs/status.md +3 -3
- package/dist/embed/react/chunks/LayoutBuilder-Cbxa_wve.js.map +1 -1
- package/dist/embed/react/chunks/app-0UZ1Re1k.js.map +1 -1
- package/dist/embed/react/chunks/page-Bx4o1kpQ.js.map +1 -1
- package/dist/embed/react/chunks/palette-Bb7Lf6eo.js.map +1 -1
- package/dist/embed/react/chunks/shell-DZRACiB9.js.map +1 -1
- package/dist/embed/react/chunks/write-BQO4CFc9.js.map +1 -1
- package/dist/embed/react/index.js +1 -1
- package/dist/server/build/dockerfile.js +1 -1
- package/dist/server/cli/usage.js +2 -2
- package/dist/ui/index.js +2 -2
- package/dist/ui/page.js +2 -2
- package/dist/ui/{shared-N4Ej8yRD.js → shared-1PD7TOTJ.js} +5 -5
- package/dist/ui/{shared-N4Ej8yRD.js.map → shared-1PD7TOTJ.js.map} +1 -1
- package/dist/ui/{shared-D5m86-4o.js → shared-1gMnOu9J.js} +6 -6
- package/dist/ui/{shared-D5m86-4o.js.map → shared-1gMnOu9J.js.map} +1 -1
- package/dist/ui/{shared-Ctaj8mR2.js → shared-5RQMi4BR.js} +3 -3
- package/dist/ui/{shared-Ctaj8mR2.js.map → shared-5RQMi4BR.js.map} +1 -1
- package/dist/ui/{shared-DpLeIATZ.js → shared-B-BUF9fx.js} +2 -2
- package/dist/ui/{shared-DpLeIATZ.js.map → shared-B-BUF9fx.js.map} +1 -1
- package/dist/ui/{shared-DWYnjItP.js → shared-B-MhiPIN.js} +9 -9
- package/dist/ui/{shared-DWYnjItP.js.map → shared-B-MhiPIN.js.map} +1 -1
- package/dist/ui/{shared-0fNuhSbG.js → shared-B07qIMlE.js} +5 -5
- package/dist/ui/{shared-0fNuhSbG.js.map → shared-B07qIMlE.js.map} +1 -1
- package/dist/ui/{shared-lYtMadRU.js → shared-B1SAS_J-.js} +3 -3
- package/dist/ui/{shared-lYtMadRU.js.map → shared-B1SAS_J-.js.map} +1 -1
- package/dist/ui/{shared-CpI0Y8M5.js → shared-B4jgU8h9.js} +6 -6
- package/dist/ui/{shared-CpI0Y8M5.js.map → shared-B4jgU8h9.js.map} +1 -1
- package/dist/ui/{shared-Cxu1c35l.js → shared-B5aBCUi7.js} +30 -30
- package/dist/ui/{shared-Cxu1c35l.js.map → shared-B5aBCUi7.js.map} +1 -1
- package/dist/ui/{shared-CAe-US1q.js → shared-B9H7_Pji.js} +5 -5
- package/dist/ui/{shared-CAe-US1q.js.map → shared-B9H7_Pji.js.map} +1 -1
- package/dist/ui/{shared-Q2bgBaYo.js → shared-BAXr20S-.js} +4 -4
- package/dist/ui/{shared-Q2bgBaYo.js.map → shared-BAXr20S-.js.map} +1 -1
- package/dist/ui/{shared-CyEefFFb.js → shared-BDDUzH-f.js} +19 -19
- package/dist/ui/{shared-CyEefFFb.js.map → shared-BDDUzH-f.js.map} +1 -1
- package/dist/ui/{shared-BdhPI6dq.js → shared-BGmkeDeW.js} +3 -3
- package/dist/ui/{shared-BdhPI6dq.js.map → shared-BGmkeDeW.js.map} +1 -1
- package/dist/ui/{shared-DONIpq2q.js → shared-BHCQ60yN.js} +2 -2
- package/dist/ui/{shared-DONIpq2q.js.map → shared-BHCQ60yN.js.map} +1 -1
- package/dist/ui/{shared-CNmaAv64.js → shared-BHsjawAG.js} +8 -8
- package/dist/ui/{shared-CNmaAv64.js.map → shared-BHsjawAG.js.map} +1 -1
- package/dist/ui/{shared-DgjU9kOy.js → shared-BIQuK_nq.js} +4 -4
- package/dist/ui/{shared-DgjU9kOy.js.map → shared-BIQuK_nq.js.map} +1 -1
- package/dist/ui/{shared-icSQAj21.js → shared-BOe4W2FI.js} +3 -3
- package/dist/ui/{shared-icSQAj21.js.map → shared-BOe4W2FI.js.map} +1 -1
- package/dist/ui/{shared-BlxoOJSk.js → shared-BPpC08ip.js} +3 -3
- package/dist/ui/{shared-BlxoOJSk.js.map → shared-BPpC08ip.js.map} +1 -1
- package/dist/ui/{shared-BtiFL37J.js → shared-BdA3LPCB.js} +2 -2
- package/dist/ui/{shared-BtiFL37J.js.map → shared-BdA3LPCB.js.map} +1 -1
- package/dist/ui/{shared-CvR94vn4.js → shared-Be3nbWsd.js} +11 -11
- package/dist/ui/{shared-CvR94vn4.js.map → shared-Be3nbWsd.js.map} +1 -1
- package/dist/ui/{shared-Bl_nF_IH.js → shared-Bf30ZLoS.js} +8 -4
- package/dist/ui/shared-Bf30ZLoS.js.map +1 -0
- package/dist/ui/{shared-DijrJ_V-.js → shared-BguP_BaR.js} +7 -7
- package/dist/ui/{shared-DijrJ_V-.js.map → shared-BguP_BaR.js.map} +1 -1
- package/dist/ui/{shared-DPAVoWY0.js → shared-BoLbc2WD.js} +17 -2
- package/dist/ui/{shared-DPAVoWY0.js.map → shared-BoLbc2WD.js.map} +1 -1
- package/dist/ui/{shared-BkdBTYwx.js → shared-BttyOW4W.js} +3 -3
- package/dist/ui/{shared-BkdBTYwx.js.map → shared-BttyOW4W.js.map} +1 -1
- package/dist/ui/{shared-CQWLz8uT.js → shared-BumxsBlA.js} +10 -10
- package/dist/ui/{shared-CQWLz8uT.js.map → shared-BumxsBlA.js.map} +1 -1
- package/dist/ui/{shared-Dzu7tFiC.js → shared-Bw73ktBk.js} +3 -3
- package/dist/ui/{shared-Dzu7tFiC.js.map → shared-Bw73ktBk.js.map} +1 -1
- package/dist/ui/{shared-BhAYErSx.js → shared-C1B0GH0e.js} +4 -4
- package/dist/ui/{shared-BhAYErSx.js.map → shared-C1B0GH0e.js.map} +1 -1
- package/dist/ui/{shared-BQJQcGgj.js → shared-C2FsyYr7.js} +4 -4
- package/dist/ui/{shared-BQJQcGgj.js.map → shared-C2FsyYr7.js.map} +1 -1
- package/dist/ui/{shared-B9lw2Ryh.js → shared-C2RU1qjv.js} +4 -4
- package/dist/ui/{shared-B9lw2Ryh.js.map → shared-C2RU1qjv.js.map} +1 -1
- package/dist/ui/{shared-DQYNI9wO.js → shared-C42f07hJ.js} +5 -5
- package/dist/ui/{shared-DQYNI9wO.js.map → shared-C42f07hJ.js.map} +1 -1
- package/dist/ui/{shared-CqCZ8wMY.js → shared-C4LkPCU4.js} +3 -3
- package/dist/ui/{shared-CqCZ8wMY.js.map → shared-C4LkPCU4.js.map} +1 -1
- package/dist/ui/{shared-BhtpgOpa.js → shared-C4wSfz6k.js} +9 -9
- package/dist/ui/{shared-BhtpgOpa.js.map → shared-C4wSfz6k.js.map} +1 -1
- package/dist/ui/{shared-BHc2Izkj.js → shared-C9DZtSFH.js} +2 -2
- package/dist/ui/{shared-BHc2Izkj.js.map → shared-C9DZtSFH.js.map} +1 -1
- package/dist/ui/{shared-DW1YuiNS.js → shared-CA52ClGo.js} +2 -2
- package/dist/ui/{shared-DW1YuiNS.js.map → shared-CA52ClGo.js.map} +1 -1
- package/dist/ui/{shared-BbSbilkh.js → shared-CBciTwm6.js} +9 -9
- package/dist/ui/{shared-BbSbilkh.js.map → shared-CBciTwm6.js.map} +1 -1
- package/dist/ui/{shared-C-J32eBn.js → shared-CL3RLKDA.js} +16 -16
- package/dist/ui/{shared-C-J32eBn.js.map → shared-CL3RLKDA.js.map} +1 -1
- package/dist/ui/{shared-Ck8S4OxW.js → shared-CLq3-_hW.js} +3 -3
- package/dist/ui/{shared-Ck8S4OxW.js.map → shared-CLq3-_hW.js.map} +1 -1
- package/dist/ui/{shared-Q5WefT7G.js → shared-CMjK-Ucd.js} +3 -3
- package/dist/ui/{shared-Q5WefT7G.js.map → shared-CMjK-Ucd.js.map} +1 -1
- package/dist/ui/{shared-D3MAuEA3.js → shared-CPjpSFGh.js} +2 -2
- package/dist/ui/{shared-D3MAuEA3.js.map → shared-CPjpSFGh.js.map} +1 -1
- package/dist/ui/{shared-VshXGwXP.js → shared-CSPhENq3.js} +2 -2
- package/dist/ui/{shared-VshXGwXP.js.map → shared-CSPhENq3.js.map} +1 -1
- package/dist/ui/{shared-DJrJaE73.js → shared-CZJE4Bmp.js} +3 -3
- package/dist/ui/{shared-DJrJaE73.js.map → shared-CZJE4Bmp.js.map} +1 -1
- package/dist/ui/{shared-D7ONOwI7.js → shared-C_BhUL2I.js} +7 -7
- package/dist/ui/{shared-D7ONOwI7.js.map → shared-C_BhUL2I.js.map} +1 -1
- package/dist/ui/{shared-j-2peBIB.js → shared-CbeujwdU.js} +4 -4
- package/dist/ui/{shared-j-2peBIB.js.map → shared-CbeujwdU.js.map} +1 -1
- package/dist/ui/{shared-D5GrOEa2.js → shared-Cbf-o4Dt.js} +4 -4
- package/dist/ui/{shared-D5GrOEa2.js.map → shared-Cbf-o4Dt.js.map} +1 -1
- package/dist/ui/{shared-HwiPIajr.js → shared-CdNmeNHh.js} +3 -3
- package/dist/ui/{shared-HwiPIajr.js.map → shared-CdNmeNHh.js.map} +1 -1
- package/dist/ui/{shared-ZCFt-CPb.js → shared-CdQOMD8J.js} +3 -3
- package/dist/ui/{shared-ZCFt-CPb.js.map → shared-CdQOMD8J.js.map} +1 -1
- package/dist/ui/{shared-ClhXSGGK.js → shared-CdiPYlTh.js} +2 -2
- package/dist/ui/{shared-ClhXSGGK.js.map → shared-CdiPYlTh.js.map} +1 -1
- package/dist/ui/{shared-DA8_pVRZ.js → shared-CepLAhwm.js} +2 -2
- package/dist/ui/{shared-DA8_pVRZ.js.map → shared-CepLAhwm.js.map} +1 -1
- package/dist/ui/{shared-BrYKjrnM.js → shared-Ch82h_Fu.js} +6 -6
- package/dist/ui/{shared-BrYKjrnM.js.map → shared-Ch82h_Fu.js.map} +1 -1
- package/dist/ui/{shared-C4UrE8Fb.js → shared-Chp88all.js} +3 -3
- package/dist/ui/{shared-C4UrE8Fb.js.map → shared-Chp88all.js.map} +1 -1
- package/dist/ui/{shared-CWa80DTO.js → shared-Ci16s2rN.js} +9 -9
- package/dist/ui/{shared-CWa80DTO.js.map → shared-Ci16s2rN.js.map} +1 -1
- package/dist/ui/{shared-tksTLD6Q.js → shared-CiqNkoE1.js} +5 -5
- package/dist/ui/{shared-tksTLD6Q.js.map → shared-CiqNkoE1.js.map} +1 -1
- package/dist/ui/{shared-yt7DjSrq.js → shared-CpMhOq8F.js} +2 -2
- package/dist/ui/{shared-yt7DjSrq.js.map → shared-CpMhOq8F.js.map} +1 -1
- package/dist/ui/{shared-Dm0z5WUl.js → shared-CrWv5JKH.js} +6 -6
- package/dist/ui/{shared-Dm0z5WUl.js.map → shared-CrWv5JKH.js.map} +1 -1
- package/dist/ui/{shared-CQL3FGHc.js → shared-Csfi3Pti.js} +2 -2
- package/dist/ui/{shared-CQL3FGHc.js.map → shared-Csfi3Pti.js.map} +1 -1
- package/dist/ui/{shared-BKNRbm-V.js → shared-CuZt26rm.js} +2 -2
- package/dist/ui/{shared-BKNRbm-V.js.map → shared-CuZt26rm.js.map} +1 -1
- package/dist/ui/{shared-Guq3upiR.js → shared-Cws_z-TS.js} +2 -2
- package/dist/ui/{shared-Guq3upiR.js.map → shared-Cws_z-TS.js.map} +1 -1
- package/dist/ui/{shared-Bw_qZeIj.js → shared-D1KWuCg6.js} +2 -2
- package/dist/ui/{shared-Bw_qZeIj.js.map → shared-D1KWuCg6.js.map} +1 -1
- package/dist/ui/{shared-CuCZKwXW.js → shared-D66ju_Cb.js} +23 -23
- package/dist/ui/{shared-CuCZKwXW.js.map → shared-D66ju_Cb.js.map} +1 -1
- package/dist/ui/{shared-DYfX7NSM.js → shared-DGaQZXnw.js} +2 -2
- package/dist/ui/{shared-DYfX7NSM.js.map → shared-DGaQZXnw.js.map} +1 -1
- package/dist/ui/{shared-B3Hp8tOv.js → shared-DHy41sET.js} +6 -6
- package/dist/ui/{shared-B3Hp8tOv.js.map → shared-DHy41sET.js.map} +1 -1
- package/dist/ui/{shared-CXyd7IKi.js → shared-DKEOMQ6l.js} +2 -2
- package/dist/ui/{shared-CXyd7IKi.js.map → shared-DKEOMQ6l.js.map} +1 -1
- package/dist/ui/{shared-Ccmc8YO0.js → shared-DSUICuaJ.js} +33 -33
- package/dist/ui/{shared-Ccmc8YO0.js.map → shared-DSUICuaJ.js.map} +1 -1
- package/dist/ui/{shared-CL2iw_js.js → shared-DUPMRoO0.js} +13 -13
- package/dist/ui/{shared-CL2iw_js.js.map → shared-DUPMRoO0.js.map} +1 -1
- package/dist/ui/{shared-Dtp4eRul.js → shared-DVfdm6ah.js} +2 -2
- package/dist/ui/{shared-Dtp4eRul.js.map → shared-DVfdm6ah.js.map} +1 -1
- package/dist/ui/{shared-BJ5Ex32G.js → shared-DXM7d7lJ.js} +3 -3
- package/dist/ui/{shared-BJ5Ex32G.js.map → shared-DXM7d7lJ.js.map} +1 -1
- package/dist/ui/{shared-BFr6ld0e.js → shared-DXbMlcev.js} +2 -2
- package/dist/ui/{shared-BFr6ld0e.js.map → shared-DXbMlcev.js.map} +1 -1
- package/dist/ui/{shared-BUp3jv-N.js → shared-D_SkPtJ9.js} +6 -6
- package/dist/ui/{shared-BUp3jv-N.js.map → shared-D_SkPtJ9.js.map} +1 -1
- package/dist/ui/{shared-CdNomVzH.js → shared-Dd21k2ic.js} +4 -4
- package/dist/ui/{shared-CdNomVzH.js.map → shared-Dd21k2ic.js.map} +1 -1
- package/dist/ui/{shared-gRMbxmpu.js → shared-Dh0nHtLZ.js} +8 -8
- package/dist/ui/{shared-gRMbxmpu.js.map → shared-Dh0nHtLZ.js.map} +1 -1
- package/dist/ui/{shared-BKGJgjog.js → shared-DkOdSpSK.js} +3 -3
- package/dist/ui/{shared-BKGJgjog.js.map → shared-DkOdSpSK.js.map} +1 -1
- package/dist/ui/{shared-BMsh72Uq.js → shared-Dkph5ckT.js} +2 -2
- package/dist/ui/{shared-BMsh72Uq.js.map → shared-Dkph5ckT.js.map} +1 -1
- package/dist/ui/{shared-Be1HpDOB.js → shared-Dl1_Nx--.js} +3 -3
- package/dist/ui/{shared-Be1HpDOB.js.map → shared-Dl1_Nx--.js.map} +1 -1
- package/dist/ui/{shared-BQIZWOoJ.js → shared-Dm0IGbAf.js} +2 -2
- package/dist/ui/{shared-BQIZWOoJ.js.map → shared-Dm0IGbAf.js.map} +1 -1
- package/dist/ui/{shared-DSUtcU5S.js → shared-DnIkTpPr.js} +5 -5
- package/dist/ui/{shared-DSUtcU5S.js.map → shared-DnIkTpPr.js.map} +1 -1
- package/dist/ui/{shared-DsxGbYlV.js → shared-Docl_0tq.js} +3 -3
- package/dist/ui/{shared-DsxGbYlV.js.map → shared-Docl_0tq.js.map} +1 -1
- package/dist/ui/{shared-DZskrV26.js → shared-DprrHC72.js} +2 -2
- package/dist/ui/{shared-DZskrV26.js.map → shared-DprrHC72.js.map} +1 -1
- package/dist/ui/{shared-Bmw43Hwf.js → shared-DyKsJTZs.js} +2 -2
- package/dist/ui/{shared-Bmw43Hwf.js.map → shared-DyKsJTZs.js.map} +1 -1
- package/dist/ui/{shared-BLG88Pya.js → shared-FO7vfI3F.js} +4 -4
- package/dist/ui/{shared-BLG88Pya.js.map → shared-FO7vfI3F.js.map} +1 -1
- package/dist/ui/{shared-DLSd80D1.js → shared-HalAVHjv.js} +3 -3
- package/dist/ui/{shared-DLSd80D1.js.map → shared-HalAVHjv.js.map} +1 -1
- package/dist/ui/{shared-D1mMBLdj.js → shared-JvcI-LWp.js} +15 -15
- package/dist/ui/{shared-D1mMBLdj.js.map → shared-JvcI-LWp.js.map} +1 -1
- package/dist/ui/{shared-C6Hv_4O0.js → shared-K7Ym27yg.js} +2 -2
- package/dist/ui/{shared-C6Hv_4O0.js.map → shared-K7Ym27yg.js.map} +1 -1
- package/dist/ui/{shared-B39ln7oi.js → shared-SzlnwfJO.js} +3 -3
- package/dist/ui/{shared-B39ln7oi.js.map → shared-SzlnwfJO.js.map} +1 -1
- package/dist/ui/{shared-DgPn_Jll.js → shared-TrGbd1FY.js} +3 -3
- package/dist/ui/{shared-DgPn_Jll.js.map → shared-TrGbd1FY.js.map} +1 -1
- package/dist/ui/{shared-DrFb-XtA.js → shared-WhbuObEW.js} +2 -2
- package/dist/ui/{shared-DrFb-XtA.js.map → shared-WhbuObEW.js.map} +1 -1
- package/dist/ui/{shared-DiFQljcq.js → shared-XcSKDp6N.js} +11 -11
- package/dist/ui/{shared-DiFQljcq.js.map → shared-XcSKDp6N.js.map} +1 -1
- package/dist/ui/{shared-CMAg58rY.js → shared-Z4uBJU6I.js} +7 -6
- package/dist/ui/shared-Z4uBJU6I.js.map +1 -0
- package/dist/ui/{shared-BnUptuUp.js → shared-bAgeiRa5.js} +2 -2
- package/dist/ui/{shared-BnUptuUp.js.map → shared-bAgeiRa5.js.map} +1 -1
- package/dist/ui/{shared-BxMvSjey.js → shared-cgXG7OgZ.js} +5 -5
- package/dist/ui/{shared-BxMvSjey.js.map → shared-cgXG7OgZ.js.map} +1 -1
- package/dist/ui/{shared-u4CG2kQJ.js → shared-eZ0SB_zm.js} +2 -2
- package/dist/ui/{shared-u4CG2kQJ.js.map → shared-eZ0SB_zm.js.map} +1 -1
- package/dist/ui/{shared-L90bWW8e.js → shared-frKFb79C.js} +2 -2
- package/dist/ui/{shared-L90bWW8e.js.map → shared-frKFb79C.js.map} +1 -1
- package/dist/ui/{shared-Dd34nhkw.js → shared-hAayNSRA.js} +3 -3
- package/dist/ui/{shared-Dd34nhkw.js.map → shared-hAayNSRA.js.map} +1 -1
- package/dist/ui/{shared-DzC64cpp.js → shared-j_J16p2B.js} +2 -2
- package/dist/ui/{shared-DzC64cpp.js.map → shared-j_J16p2B.js.map} +1 -1
- package/dist/ui/{shared-DX7HRIV-.js → shared-tqpl5N3g.js} +6 -6
- package/dist/ui/{shared-DX7HRIV-.js.map → shared-tqpl5N3g.js.map} +1 -1
- package/dist/ui/{shared-BS5dBMpA.js → shared-y-6_ZDts.js} +2 -2
- package/dist/ui/{shared-BS5dBMpA.js.map → shared-y-6_ZDts.js.map} +1 -1
- package/dist/ui/{shared-Cv5v_MP1.js → shared-zG6e1Rhz.js} +13 -13
- package/dist/ui/{shared-Cv5v_MP1.js.map → shared-zG6e1Rhz.js.map} +1 -1
- package/dist/ui/testing.js +1 -1
- package/package.json +4 -4
- package/dist/docs/cli-browser.md +0 -232
- package/dist/docs/database-browser.md +0 -115
- package/dist/docs/keyboard.md +0 -63
- package/dist/docs/mcp.md +0 -86
- package/dist/ui/shared-Bl_nF_IH.js.map +0 -1
- package/dist/ui/shared-CMAg58rY.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-DJrJaE73.js","names":[],"sources":["../../../engine/src/shared/appLinks.ts","../../../engine/src/shared/recordReads.ts"],"sourcesContent":["/** Checked cross-App destinations are navigation intent, never authority. */\nimport type { AppTarget, AppLinkValue } from '../../schema/model.js';\nexport type { AppTarget, AppLinkValue, AppTenantIntent } from '../../schema/model.js';\n\nexport type BoundAppTarget = AppTarget<string | number>;\nexport type AppLinkAction = { label: string; to: AppTarget } | { label: string; view: string };\n\nfunction object(value: unknown, keys: readonly string[], at: string): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))\n throw new Error(`${at} must be a plain object.`);\n const raw = value as Record<string, unknown>;\n for (const key of Reflect.ownKeys(raw)) {\n if (\n typeof key !== 'string' ||\n !keys.includes(key) ||\n !Object.getOwnPropertyDescriptor(raw, key)?.enumerable ||\n !Object.hasOwn(Object.getOwnPropertyDescriptor(raw, key)!, 'value')\n )\n throw new Error(`${at}: unsupported field \"${String(key)}\".`);\n }\n return raw;\n}\n\nfunction text(value: unknown, at: string): string {\n if (typeof value !== 'string' || !value.length || value.length > 1024 || /[\\p{Cc}\\p{Cs}]/u.test(value))\n throw new Error(`${at} needs non-empty text.`);\n return value;\n}\n\nfunction value(raw: unknown, at: string, record: boolean): AppLinkValue {\n if (typeof raw === 'number') {\n if (!Number.isSafeInteger(raw)) throw new Error(`${at} needs a safe integer; use text for opaque or large keys.`);\n return raw;\n }\n if (typeof raw === 'string') return text(raw, at);\n if (raw && typeof raw === 'object' && Object.hasOwn(raw, 'from'))\n throw new Error(`${at} has \"from\", which is now \"column\": the field of this record the value is read from.`);\n const bound = object(raw, ['column'], at);\n if (!record) throw new Error(`${at}: \"column\" bindings require a record surface.`);\n const field = text(bound.column, `${at}.column`);\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(field)) throw new Error(`${at}.column must name one visible field of this record.`);\n return { column: field };\n}\n\n/** Parse closed authoring data. Record bindings are opt-in at the source surface. */\nexport function parseAppTarget(raw: unknown, at = 'App link', record = false): AppTarget {\n const o = object(raw, ['app', 'kind', 'view', 'resource', 'key', 'tenant'], at);\n const app = text(o.app, `${at}.app`);\n if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(app)) throw new Error(`${at}.app is not an App id.`);\n const kind = o.kind;\n if (kind !== 'home' && kind !== 'view' && kind !== 'record') throw new Error(`${at}.kind must be home, view or record.`);\n object(raw, ['app', 'kind', 'tenant', ...(kind === 'view' ? ['view'] : kind === 'record' ? ['resource', 'key'] : [])], at);\n let tenant: AppTarget['tenant'];\n if (Object.hasOwn(o, 'tenant')) {\n const t = object(o.tenant, ['kind', 'id'], `${at}.tenant`);\n if (t.kind === 'choose' || t.kind === 'platform') {\n object(t, ['kind'], `${at}.tenant`);\n tenant = { kind: t.kind };\n } else if (t.kind === 'tenant') {\n const id = value(t.id, `${at}.tenant.id`, record);\n if (typeof id !== 'object' && (String(id).trim() !== String(id) || id === '@platform'))\n throw new Error(`${at}.tenant.id is not a tenant selector.`);\n tenant = { kind: 'tenant', id };\n } else throw new Error(`${at}.tenant.kind must be choose, tenant or platform.`);\n }\n const base = { app, ...(tenant ? { tenant } : {}) };\n if (kind === 'home') return { ...base, kind };\n if (kind === 'view') {\n const view = text(o.view, `${at}.view`);\n if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(view)) throw new Error(`${at}.view is not a view key.`);\n return { ...base, kind, view };\n }\n const resource = text(o.resource, `${at}.resource`);\n if (!/^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(resource)) throw new Error(`${at}.resource is not a resource name.`);\n const keys = o.key && typeof o.key === 'object' ? Object.keys(o.key) : [];\n const key = object(o.key, keys, `${at}.key`);\n if (!keys.length || keys.some((column) => !/^[A-Za-z_][A-Za-z0-9_]*$/.test(column)))\n throw new Error(`${at}.key needs primary-key columns.`);\n return {\n ...base,\n kind,\n resource,\n key: Object.fromEntries(keys.map((column) => [column, value(key[column], `${at}.key.${column}`, record)])),\n };\n}\n\n/** `to` is a view's key, or an App link (`appLink({ … })`): one key says where a link goes. */\nfunction parseLinkTo(to: unknown, at: string, record = false): { view: string } | { to: AppTarget } {\n if (typeof to === 'string') {\n const view = to.trim();\n if (!view || view.length > 1024 || /[\\p{Cc}\\p{Cs}]/u.test(view)) throw new Error(`${at} needs the key of a view, or an appLink().`);\n return { view };\n }\n return { to: parseAppTarget(to, at, record) };\n}\n\nexport function parseAppLinkAction(raw: unknown, at: string, record = false): AppLinkAction {\n const o = object(raw, ['label', 'to'], at);\n if (typeof o.label !== 'string' || !o.label.trim()) throw new Error(`${at}.label needs non-empty text.`);\n return { label: o.label.trim(), ...parseLinkTo(o.to, `${at}.to`, record) } as AppLinkAction;\n}\n\nexport function appTargetFields(target: AppTarget): string[] {\n const values = [\n ...(target.kind === 'record' ? Object.values(target.key) : []),\n ...(target.tenant?.kind === 'tenant' ? [target.tenant.id] : []),\n ];\n return [...new Set(values.flatMap((v) => (typeof v === 'object' ? [v.column] : [])))];\n}\n\n/** Only bind values already served to this reader. Missing/hidden values refuse. */\nexport function bindAppTarget(target: AppTarget, row: Readonly<Record<string, unknown>>, visibleFields: readonly string[]): BoundAppTarget {\n const parsed = parseAppTarget(target, 'App link', true);\n const bind = (v: AppLinkValue): string | number => {\n if (typeof v !== 'object') return v;\n if (!visibleFields.includes(v.column) || !Object.hasOwn(row, v.column))\n throw new Error(`App link: unavailable source field \"${v.column}\".`);\n return value(row[v.column], `App link source \"${v.column}\"`, false) as string | number;\n };\n return parseAppTarget({\n ...parsed,\n ...(parsed.kind === 'record' ? { key: Object.fromEntries(Object.entries(parsed.key).map(([k, v]) => [k, bind(v)])) } : {}),\n ...(parsed.tenant?.kind === 'tenant' ? { tenant: { kind: 'tenant', id: bind(parsed.tenant.id) } } : {}),\n }) as BoundAppTarget;\n}\n","/**\n * What a record page reads, as the requests its sections send.\n *\n * A record page used to reach its reader in three waves of requests — the\n * root and the plan, then a request or two per section, then the history —\n * and at 20 ms to the database each wave was a round of admissions and\n * statements: a lender's loan finished at 0.9 s. The server now answers the\n * whole page in one request (server/http/recordCompose.ts, and the tenant\n * host's own): it plans the page, then asks each section's read on the\n * section's behalf, through the same route and the same grants as the\n * section's own request, and hands every answer back beside the plan. The\n * client keeps them for the requests they answer (client/composedReads.ts),\n * and a section that asks finds its answer waiting.\n *\n * So the one rule is that server and client build each request alike, and\n * the builders live here, used by both: the sections' own modules call them\n * for the request they send, and the server calls them to know what the\n * sections will ask. A request built differently is only asked again — the\n * section sends it, as it always did — so a drift costs a request, never a\n * wrong answer.\n *\n * The record's own row is the one read that is not asked alike: every fields\n * section, the header, the root probe and a custom section's record fields\n * read the one row, each for its columns, and which of them ask together\n * depends on what is drawn (a tab not yet opened asks nothing). The server\n * reads every column any of them names, once, and each is answered with\n * exactly its part (`rowPart`), as the client's own shared read already was\n * (pageFieldsRead.ts).\n */\nimport { findTable, labelPath, primaryKey, type DatabaseSchema, type TableMeta } from './schema.js';\nimport { DEFAULT_HISTORY_LIMIT, kindReading, type PageSection } from './pageModel.js';\nimport { assertNever } from './model.js';\nimport { andQuery, quoteValue } from './query.js';\nimport { tabMatches } from './addressParams.js';\nimport { recordView, referenceProjection } from './referenceModel.js';\nimport { tableAtPrefix } from './viewPaths.js';\nimport type { HttpBreakdownDefinition, HttpFilter } from './resourceHttp.js';\nimport { appTargetFields, type AppTarget } from './appLinks.js';\n\n/** The route a record page is composed on. */\nexport const RECORD_COMPOSE_PATH = '/api/pages/compose';\n\n/** Where a table's rows are named, as the reader's App says it (`recordLabel`, false where it is withheld). */\nexport interface RecordReadNames {\n readonly schema: DatabaseSchema;\n /** Reads in an App's words: references carry their record's name, lists their keys. */\n readonly app: boolean;\n readonly recordLabel: (tableId: string) => string | false | undefined;\n /** Where a table's rows are named, where the reader already knows (the client's labelPath, which is this rule over the same three). */\n readonly namePath?: (table: TableMeta) => string | undefined;\n /** A table by id, where the reader already holds it (the client's findTable, over the same schema). */\n readonly table?: (id: string) => TableMeta | undefined;\n /** A table's row-defined JSON columns (`resources.<r>.defined`): a section showing one also reads its `per`. */\n readonly defined?: (tableId: string) => Readonly<Record<string, { per?: string }>> | undefined;\n}\n\n/** The parts of a section its reads are built from (client pageModel.ts PageSection, shared page.ts). */\nexport interface ReadSection {\n readonly kind: PageSection['kind'];\n readonly hidden?: boolean;\n readonly emptyMode?: 'hide' | 'collapse';\n /** A grouped related list reads its groups, then each group's rows (the rows wait on the groups). */\n readonly group?: unknown;\n readonly presentation?: unknown;\n readonly paths?: readonly string[];\n readonly status?: string;\n readonly subfigure?: string;\n readonly was?: string;\n readonly actions?: ReadonlyArray<{ readonly prefill?: Readonly<Record<string, string>>; readonly to?: AppTarget }>;\n readonly names?: boolean;\n readonly workflow?: { readonly field: string };\n readonly from?: string;\n readonly limit?: number;\n readonly record?: readonly string[];\n readonly when?: string;\n readonly region?: string;\n readonly tab?: string;\n readonly title?: string;\n readonly width?: unknown;\n}\n\n/** A planned view, as the server builds it (ViewDef) or the client reads it off the wire (HttpViewDefinition). */\ninterface ReadView {\n readonly base: string;\n readonly columns?: ReadonlyArray<{ readonly path: string; readonly alias?: string }>;\n readonly filter?: unknown;\n readonly limit?: number;\n}\n\n/** The parts of a section's plan its reads are built from (pagePlans.ts). */\nexport interface ReadPlan<V extends ReadView = ReadView> {\n readonly index: number;\n readonly error?: string;\n readonly empty?: boolean;\n readonly view?: V;\n readonly counts?: boolean;\n readonly totals?: HttpBreakdownDefinition;\n readonly groups?: HttpBreakdownDefinition;\n readonly breakdown?: HttpBreakdownDefinition;\n readonly streams?: ReadonlyArray<TimelineReadStream<V>>;\n readonly reads?: Readonly<Record<string, { readonly error?: string; readonly empty?: boolean; readonly view?: V }>>;\n /** A custom section's aggregate reads, each a breakdown. */\n readonly aggregates?: Readonly<\n Record<string, { readonly error?: string; readonly empty?: boolean; readonly breakdown?: HttpBreakdownDefinition }>\n >;\n}\n\n/** A timeline stream's plan, as far as its reads go (pagePlans.ts): its table, its author, its rows and its far side. */\ninterface TimelineReadStream<V extends ReadView = ReadView> {\n readonly error?: string;\n readonly empty?: boolean;\n readonly from?: string;\n readonly by?: string;\n readonly view?: V;\n readonly key?: readonly string[];\n readonly side?: { readonly error?: string; readonly view?: V };\n}\n\n/**\n * A timeline stream's reads, as its section asks them (kit PageTimeline.tsx): its rows, where its author is a\n * reference with the person's name beside it, read through the reference as a list column of it is; and where the\n * stream has a far side, that side's rows. One rule for the section and the page's planned reads (`recordReads`),\n * so a composed page's answer is the very read the section asks: a record page whose timeline was planned without\n * the author's name asked it again, alone, once the page had drawn (a field report: a fifth round on a record).\n */\nexport function timelineReads<V extends ReadView>(\n names: { readonly table: (id: string) => TableMeta | undefined; readonly namePath: (table: TableMeta) => string | undefined },\n stream: TimelineReadStream<V>,\n): { view: V; side?: V } | undefined {\n if (stream.error || stream.empty || !stream.view) return undefined;\n const table = stream.from === undefined ? undefined : names.table(stream.from);\n const reference = stream.by ? table?.columns.find((column) => column.name === stream.by)?.references : undefined;\n const target = reference ? names.table(reference.table) : undefined;\n const named = target && names.namePath(target);\n const view =\n target && named && !primaryKey(target).includes(named)\n ? ({ ...stream.view, columns: [...(stream.view.columns ?? []), { path: `${stream.by}.${named}` }] } as V)\n : stream.view;\n const side = stream.side?.view && !stream.side.error && stream.key?.length ? stream.side.view : undefined;\n return side ? { view, side } : { view };\n}\n\n/** One request, as a section sends it: its route and its body, without the owner the transport adds. */\nexport interface ReadAsk {\n readonly path: string;\n readonly body: Record<string, unknown>;\n}\n\n/** Where a table's rows are named, as a view path. */\nconst namePathOf = (names: RecordReadNames, table: TableMeta): string | undefined =>\n names.namePath ? names.namePath(table) : labelPath(names.schema, table, names.recordLabel(table.id));\n\n/**\n * The fields a section shows when it names none. In the Workbench, every\n * column: it is the database as it is. In an App, every column a reader\n * reads — not a key the database handed out (a lone numeric or uuid primary\n * key, which opened a lead on \"ID 2\") and not a row version, the same two\n * the scaffold leaves out of the fields it writes (server/scaffold.ts).\n */\nexport function defaultFieldPaths(base: TableMeta, app: boolean): string[] {\n const keys = base.columns.filter((c) => c.primaryKey);\n const surrogate = (c: TableMeta['columns'][number]) =>\n c.primaryKey && keys.length === 1 && (/^(id|uuid|guid)$/i.test(c.name) || /int|serial|number|numeric|uuid/i.test(c.type ?? ''));\n const version = (c: TableMeta['columns'][number]) => /^(version|lock_version|row_version|rowversion|revision)$/i.test(c.name);\n const read = base.columns.filter((c) => !app || !(surrogate(c) || version(c))).map((c) => c.name);\n return read.length ? read : base.columns.map((c) => c.name);\n}\n\n/** A fields section's columns: what it shows, the names of what it references, the record's name, and its workflow's field. */\nexport function fieldsColumns(\n names: RecordReadNames,\n base: TableMeta,\n section: ReadSection,\n): { fetched: string[]; labels: Map<string, string> } {\n const paths = section.paths?.length ? [...section.paths] : defaultFieldPaths(base, names.app);\n const namePath = namePathOf(names, base);\n /* A headline also reads its second figure and every field its actions\n fill a form from; they are fetched, not shown as facts. */\n const extra =\n section.presentation === 'headline'\n ? [\n ...(section.subfigure ? [section.subfigure] : []),\n ...(section.was ? [section.was] : []),\n ...(section.actions ?? []).flatMap((action) => [\n ...Object.values(action.prefill ?? {}),\n ...(action.to ? appTargetFields(action.to) : []),\n ]),\n ].filter((path) => !paths.includes(path))\n : [];\n const projection = referenceProjection(names.schema, base, [...paths, ...new Set(extra)], section.names !== false && names.app, (table) =>\n namePathOf(names, table),\n );\n const fetched = projection.fetched;\n if (namePath && !fetched.includes(namePath)) fetched.push(namePath);\n if (section.workflow && !fetched.includes(section.workflow.field)) fetched.push(section.workflow.field);\n // A row-defined column's fields are the ones its record's `per` picks (definedFields.ts),\n // through a reference too: `requested_item_id.answers` reads `requested_item_id.<per>`.\n for (const path of paths) {\n const through = names.defined ? definedThrough(names.schema, base.id, path, names.defined) : undefined;\n const per = through?.binding.per;\n const perPath = per === undefined ? undefined : through!.prefix ? `${through!.prefix}.${per}` : per;\n if (perPath && !fetched.includes(perPath)) fetched.push(perPath);\n }\n return { fetched, labels: projection.labels };\n}\n\n/**\n * The row-defined column a fields path ends on (`resources.<r>.defined`):\n * the record's own (`specs`) or one through references\n * (`requested_item_id.answers`), with the table it lives on, the hops before\n * it, and its binding. Undefined for any other path. A record shows the\n * column's fields wherever its path is shown, not only on its own page.\n */\nexport function definedThrough<B extends { per?: string }>(\n schema: DatabaseSchema,\n baseId: string,\n path: string,\n defined: (tableId: string) => Readonly<Record<string, B>> | undefined,\n): { table: TableMeta; column: string; prefix: string; binding: B } | undefined {\n const cut = path.lastIndexOf('.');\n const prefix = cut === -1 ? '' : path.slice(0, cut),\n column = path.slice(cut + 1);\n const table = tableAtPrefix(schema, baseId, prefix);\n const bound = table ? defined(table.id) : undefined;\n if (!table || !bound || !Object.hasOwn(bound, column)) return undefined;\n return { table, column, prefix, binding: bound[column] };\n}\n\n/** A record header's columns: its status, its facts, the names of what they reference, and the record's name. */\nexport function headerColumns(\n names: RecordReadNames,\n base: TableMeta,\n section: ReadSection,\n): { fetched: string[]; labels: Map<string, string> } {\n const projection = referenceProjection(\n names.schema,\n base,\n [...(section.status ? [section.status] : []), ...(section.paths ?? [])],\n names.app,\n (table) => namePathOf(names, table),\n );\n const fetched = projection.fetched;\n const namePath = namePathOf(names, base);\n if (namePath && !fetched.includes(namePath)) fetched.push(namePath);\n return { fetched, labels: projection.labels };\n}\n\n/** The key as a filter on the record's own row, one equality per key column. */\nfunction keyFilter(base: TableMeta, key: Readonly<Record<string, unknown>>): HttpFilter<'path'> {\n return { groups: [primaryKey(base).map((column) => ({ path: column, op: '=' as const, value: key[column] as string | number }))] };\n}\n\n/** A read of the record's own row, for columns: the probe (`root`), a fields section (`fields`), the header (`header`), a custom section's record (`<index>:record`). */\nexport function rowBody(pageId: string, base: TableMeta, key: Readonly<Record<string, unknown>>, columns: readonly string[], part: string) {\n return {\n count: 'none' as const,\n view: {\n id: `page:${pageId}:${part}`,\n name: base.name,\n base: base.id,\n columns: columns.map((path) => ({ path })),\n filter: keyFilter(base, key),\n limit: 1 as const,\n },\n };\n}\n\n/** The probe's columns: the key, and the record's name. */\nfunction probeColumns(names: RecordReadNames, base: TableMeta): string[] {\n const namePath = namePathOf(names, base);\n return [...primaryKey(base), ...(namePath ? [namePath] : [])];\n}\n\n/** A related list's first request: its planned view at the reader's page size, with the names and keys a row needs. */\nexport function listView<V extends ReadView>(names: RecordReadNames, section: ReadSection, plan: ReadPlan<V>, rows: number) {\n const table = names.table ? names.table(section.from ?? '') : findTable(names.schema, section.from ?? '');\n const limit = section.limit ?? rows;\n const view: V | undefined = plan.view && !section.limit ? { ...plan.view, limit } : plan.view;\n const explicit = (target: TableMeta) => (names.recordLabel(target.id) === undefined ? undefined : namePathOf(names, target));\n type Columns = Array<{ path: string; alias?: string }>;\n return view && table\n ? recordView(names.schema, table, view as V & { columns?: Columns }, names.app, (target) => namePathOf(names, target), explicit)\n : {\n view: view as (V & { columns: Columns }) | undefined,\n labelColumns: {} as Record<string, string>,\n hidden: [] as string[],\n keyColumns: [] as Array<{ column: string; index: number }>,\n labelColumn: undefined as { path: string; index: number } | undefined,\n };\n}\n\n/** A grouped list's page of groups, as `/api/breakdown` takes it: the request the page's compose asks and the list asks again. */\nexport function groupsBody(groups: HttpBreakdownDefinition, offset: number) {\n return { def: groups, limit: groups.limit, offset };\n}\n\n/** A metric's measure: a count asked as a breakdown with no key, or the plan's own breakdown; none for a plain aggregate. */\nexport function metricMeasure(plan: ReadPlan<ReadView>): HttpBreakdownDefinition | undefined {\n const counted: HttpBreakdownDefinition | undefined =\n plan.counts && plan.view && !plan.breakdown\n ? {\n base: plan.view.base,\n by: [],\n measures: [{ fn: 'count', alias: 'value' }],\n ...(plan.view.filter ? { filter: plan.view.filter as HttpBreakdownDefinition['filter'] } : {}),\n limit: 1,\n }\n : undefined;\n return plan.breakdown ?? counted;\n}\n\n/** Whether the record satisfies a `when`: one bounded query, key and condition composed the way every fold is. */\nexport function whenBody(base: TableMeta, key: Readonly<Record<string, unknown>>, when: string) {\n const keyText = primaryKey(base)\n .map((c) => `${c} = ${quoteValue(String(key[c]))}`)\n .join(' and ');\n return { q: `${base.id} ${andQuery(keyText, when)}`, limit: 1, offset: 0, count: 'none' as const };\n}\n\n/** The first page of a record's history. */\nexport function historyBody(base: TableMeta, key: Readonly<Record<string, unknown>>, limit: number) {\n return { table: base.id, key, limit };\n}\n\n/**\n * The sections a page draws when it opens on `tab`, as PageSections.tsx lays\n * them out: a strip of named tabs draws the one asked for (the first, when\n * none is) and every panel holding the record's own fields; a run of lists\n * made into tabs draws the one asked for; everything else is drawn. The\n * header is drawn in the head. What a closed tab would read is read when it\n * is opened, as ever, and asked for nothing here.\n */\nexport function drawnSections(\n schema: DatabaseSchema,\n page: { sections: readonly ReadSection[]; listTabs?: boolean },\n tab: string | undefined,\n appPage: boolean,\n): Set<number> {\n const drawn = new Set<number>();\n const shownAll = page.sections.map((section, index) => ({ section, index })).filter(({ section }) => !section.hidden);\n const header = shownAll.find(({ section }) => section.kind === 'fields' && section.presentation === 'header');\n if (header) drawn.add(header.index);\n const hasAside = page.sections.some((section) => section.region === 'aside' && !section.hidden);\n const autoTabs = appPage ? page.listTabs === true : page.listTabs !== false;\n const tabbed = (section: ReadSection) =>\n section.kind === 'list' && section.presentation !== 'gallery' && (section.width === undefined || !appPage);\n type Shown = { section: ReadSection; index: number };\n const lay = (items: readonly Shown[], listTabs: boolean, scoped: boolean) => {\n for (let i = 0; i < items.length; i += 1) {\n if (!(listTabs && autoTabs && tabbed(items[i].section))) {\n drawn.add(items[i].index);\n continue;\n }\n let end = i;\n while (end + 1 < items.length && tabbed(items[end + 1].section)) end += 1;\n const run = items.slice(i, end + 1);\n if (run.length === 1) drawn.add(run[0].index);\n else {\n const token = (item: Shown) =>\n scoped\n ? `section:${item.index}`\n : (item.section.title ?? findTable(schema, item.section.from ?? '')?.name ?? item.section.from ?? '');\n const asked = run.findIndex((item) => tabMatches(token(item), tab));\n drawn.add(run[asked === -1 ? 0 : asked].index);\n }\n i = end;\n }\n };\n for (const region of hasAside ? ['main', 'aside'] : [undefined]) {\n const shown = shownAll.filter(\n ({ section }) =>\n !(section.kind === 'fields' && section.presentation === 'header') && (!region || (section.region ?? 'main') === region),\n );\n const scoped = region === 'aside';\n const groups: Array<{ name: string; items: Shown[] }> = [];\n for (const item of shown) {\n if (!item.section.tab) continue;\n const group = groups.find((g) => g.name === item.section.tab);\n if (group) group.items.push(item);\n else groups.push({ name: item.section.tab, items: [item] });\n }\n if (groups.length < 2) {\n lay(shown, true, scoped);\n continue;\n }\n const first = shown.findIndex((item) => item.section.tab);\n lay(shown.slice(0, first), true, scoped);\n const asked = groups.findIndex((group) => tabMatches(`tab:${group.name}`, tab));\n groups.forEach((group, i) => {\n if (i === (asked === -1 ? 0 : asked) || group.items.some(({ section }) => section.kind === 'fields')) lay(group.items, false, scoped);\n });\n lay(\n shown.slice(first).filter((item) => !item.section.tab),\n true,\n scoped,\n );\n }\n return drawn;\n}\n\n/**\n * Every read a record page's sections will ask for when it opens on `tab`,\n * given its plan: the record's row (every column any section drawn reads),\n * and each other request as the section builds it. A hidden section, and\n * one in a closed tab, reads nothing; a section behind a `when` is read\n * too, and its condition asked, since the answer is only kept until it is\n * asked for.\n */\nexport function recordPageReads(\n names: RecordReadNames,\n page: { id: string; sections: readonly ReadSection[]; listTabs?: boolean },\n base: TableMeta,\n plans: readonly ReadPlan[],\n key: Readonly<Record<string, unknown>>,\n options: { rows: number; history: boolean; tab?: string },\n): { row: string[]; parts: Array<{ part: string; columns: string[] }>; reads: ReadAsk[] } {\n const drawn = drawnSections(names.schema, page, options.tab, names.app);\n /* Each reader of the row with its own columns, too: the one read of all of\n them is refused whole where any column is, and then each is asked alone,\n as the sections would have been. */\n const parts = [{ part: 'root', columns: probeColumns(names, base) }];\n const reads: ReadAsk[] = [];\n const planOf = (index: number) => plans.find((plan) => plan.index === index);\n const view = (body: Record<string, unknown>) => reads.push({ path: '/api/view/run', body });\n const breakdown = (def: HttpBreakdownDefinition) => reads.push({ path: '/api/breakdown', body: { def } });\n page.sections.forEach((section, index) => {\n if (section.hidden || !drawn.has(index)) return;\n if (section.when) reads.push({ path: '/api/run', body: whenBody(base, key, section.when) });\n const plan = planOf(index);\n if (section.kind === 'fields') {\n const header = section.presentation === 'header';\n parts.push({\n part: header ? 'header' : 'fields',\n columns: (header ? headerColumns(names, base, section) : fieldsColumns(names, base, section)).fetched,\n });\n return;\n }\n if (section.kind === 'history') {\n if (options.history)\n reads.push({ path: '/api/record-history', body: historyBody(base, key, section.limit ?? DEFAULT_HISTORY_LIMIT) });\n return;\n }\n /* A note reads nothing. Flow runs are fetched lazily by their section, behind the reader's own check\n (server/recordFlows.ts), and a flow's diagram reads its steps when it is drawn, as the reader's rows\n (PageFlowDiagram.tsx). */\n if (kindReading(section.kind, 'none')) return;\n if (section.kind === 'custom' && section.record?.length) parts.push({ part: `${index}:record`, columns: [...section.record] });\n if (!plan || plan.error || plan.empty) return;\n switch (section.kind) {\n case 'metric': {\n const measure = metricMeasure(plan);\n if (measure) breakdown(measure);\n else if (plan.view) view({ view: plan.view });\n return;\n }\n case 'breakdown':\n case 'chart':\n if (plan.breakdown) breakdown(plan.breakdown);\n return;\n case 'timeline':\n for (const stream of plan.streams ?? []) {\n const asked = timelineReads(\n {\n table: (id) => (names.table ? names.table(id) : findTable(names.schema, id)),\n namePath: (table) => namePathOf(names, table),\n },\n stream,\n );\n if (asked) view({ view: asked.view, offset: 0 });\n if (asked?.side) view({ view: asked.side, offset: 0 });\n }\n return;\n case 'custom':\n for (const read of Object.values(plan.reads ?? {}))\n if (!read.error && !read.empty && read.view) view({ view: read.view, offset: 0, count: 'none' });\n for (const total of Object.values(plan.aggregates ?? {}))\n if (!total.error && !total.empty && total.breakdown) breakdown(total.breakdown);\n return;\n case 'list': {\n /* A grouped list asks its groups first; each group's rows are asked of the keys that come back. */\n if (section.group && plan.groups) {\n reads.push({ path: '/api/breakdown', body: groupsBody(plan.groups, 0) });\n if (plan.totals) breakdown(plan.totals);\n return;\n }\n const listed = listView(names, section, plan, options.rows).view;\n if (listed) view({ view: listed, offset: 0 });\n if (plan.totals) breakdown(plan.totals);\n return;\n }\n default:\n assertNever(section.kind, 'page section kind');\n }\n });\n return { row: [...new Set(parts.flatMap((part) => part.columns))], parts, reads };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shared-CZJE4Bmp.js","names":[],"sources":["../../../engine/src/shared/appLinks.ts","../../../engine/src/shared/recordReads.ts"],"sourcesContent":["/** Checked cross-App destinations are navigation intent, never authority. */\nimport type { AppTarget, AppLinkValue } from '../../schema/model.js';\nexport type { AppTarget, AppLinkValue, AppTenantIntent } from '../../schema/model.js';\n\nexport type BoundAppTarget = AppTarget<string | number>;\nexport type AppLinkAction = { label: string; to: AppTarget } | { label: string; view: string };\n\nfunction object(value: unknown, keys: readonly string[], at: string): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))\n throw new Error(`${at} must be a plain object.`);\n const raw = value as Record<string, unknown>;\n for (const key of Reflect.ownKeys(raw)) {\n if (\n typeof key !== 'string' ||\n !keys.includes(key) ||\n !Object.getOwnPropertyDescriptor(raw, key)?.enumerable ||\n !Object.hasOwn(Object.getOwnPropertyDescriptor(raw, key)!, 'value')\n )\n throw new Error(`${at}: unsupported field \"${String(key)}\".`);\n }\n return raw;\n}\n\nfunction text(value: unknown, at: string): string {\n if (typeof value !== 'string' || !value.length || value.length > 1024 || /[\\p{Cc}\\p{Cs}]/u.test(value))\n throw new Error(`${at} needs non-empty text.`);\n return value;\n}\n\nfunction value(raw: unknown, at: string, record: boolean): AppLinkValue {\n if (typeof raw === 'number') {\n if (!Number.isSafeInteger(raw)) throw new Error(`${at} needs a safe integer; use text for opaque or large keys.`);\n return raw;\n }\n if (typeof raw === 'string') return text(raw, at);\n if (raw && typeof raw === 'object' && Object.hasOwn(raw, 'from'))\n throw new Error(`${at} has \"from\", which is now \"column\": the field of this record the value is read from.`);\n const bound = object(raw, ['column'], at);\n if (!record) throw new Error(`${at}: \"column\" bindings require a record surface.`);\n const field = text(bound.column, `${at}.column`);\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(field)) throw new Error(`${at}.column must name one visible field of this record.`);\n return { column: field };\n}\n\n/** Parse closed authoring data. Record bindings are opt-in at the source surface. */\nexport function parseAppTarget(raw: unknown, at = 'App link', record = false): AppTarget {\n const o = object(raw, ['app', 'kind', 'view', 'resource', 'key', 'tenant'], at);\n const app = text(o.app, `${at}.app`);\n if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(app)) throw new Error(`${at}.app is not an App id.`);\n const kind = o.kind;\n if (kind !== 'home' && kind !== 'view' && kind !== 'record') throw new Error(`${at}.kind must be home, view or record.`);\n object(raw, ['app', 'kind', 'tenant', ...(kind === 'view' ? ['view'] : kind === 'record' ? ['resource', 'key'] : [])], at);\n let tenant: AppTarget['tenant'];\n if (Object.hasOwn(o, 'tenant')) {\n const t = object(o.tenant, ['kind', 'id'], `${at}.tenant`);\n if (t.kind === 'choose' || t.kind === 'platform') {\n object(t, ['kind'], `${at}.tenant`);\n tenant = { kind: t.kind };\n } else if (t.kind === 'tenant') {\n const id = value(t.id, `${at}.tenant.id`, record);\n if (typeof id !== 'object' && (String(id).trim() !== String(id) || id === '@platform'))\n throw new Error(`${at}.tenant.id is not a tenant selector.`);\n tenant = { kind: 'tenant', id };\n } else throw new Error(`${at}.tenant.kind must be choose, tenant or platform.`);\n }\n const base = { app, ...(tenant ? { tenant } : {}) };\n if (kind === 'home') return { ...base, kind };\n if (kind === 'view') {\n const view = text(o.view, `${at}.view`);\n if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(view)) throw new Error(`${at}.view is not a view key.`);\n return { ...base, kind, view };\n }\n const resource = text(o.resource, `${at}.resource`);\n if (!/^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(resource)) throw new Error(`${at}.resource is not a resource name.`);\n const keys = o.key && typeof o.key === 'object' ? Object.keys(o.key) : [];\n const key = object(o.key, keys, `${at}.key`);\n if (!keys.length || keys.some((column) => !/^[A-Za-z_][A-Za-z0-9_]*$/.test(column)))\n throw new Error(`${at}.key needs primary-key columns.`);\n return {\n ...base,\n kind,\n resource,\n key: Object.fromEntries(keys.map((column) => [column, value(key[column], `${at}.key.${column}`, record)])),\n };\n}\n\n/** `to` is a view's key, or an App link (`appLink({ … })`): one key says where a link goes. */\nfunction parseLinkTo(to: unknown, at: string, record = false): { view: string } | { to: AppTarget } {\n if (typeof to === 'string') {\n const view = to.trim();\n if (!view || view.length > 1024 || /[\\p{Cc}\\p{Cs}]/u.test(view)) throw new Error(`${at} needs the key of a view, or an appLink().`);\n return { view };\n }\n return { to: parseAppTarget(to, at, record) };\n}\n\nexport function parseAppLinkAction(raw: unknown, at: string, record = false): AppLinkAction {\n const o = object(raw, ['label', 'to'], at);\n if (typeof o.label !== 'string' || !o.label.trim()) throw new Error(`${at}.label needs non-empty text.`);\n return { label: o.label.trim(), ...parseLinkTo(o.to, `${at}.to`, record) } as AppLinkAction;\n}\n\nexport function appTargetFields(target: AppTarget): string[] {\n const values = [\n ...(target.kind === 'record' ? Object.values(target.key) : []),\n ...(target.tenant?.kind === 'tenant' ? [target.tenant.id] : []),\n ];\n return [...new Set(values.flatMap((v) => (typeof v === 'object' ? [v.column] : [])))];\n}\n\n/** Only bind values already served to this reader. Missing/hidden values refuse. */\nexport function bindAppTarget(target: AppTarget, row: Readonly<Record<string, unknown>>, visibleFields: readonly string[]): BoundAppTarget {\n const parsed = parseAppTarget(target, 'App link', true);\n const bind = (v: AppLinkValue): string | number => {\n if (typeof v !== 'object') return v;\n if (!visibleFields.includes(v.column) || !Object.hasOwn(row, v.column))\n throw new Error(`App link: unavailable source field \"${v.column}\".`);\n return value(row[v.column], `App link source \"${v.column}\"`, false) as string | number;\n };\n return parseAppTarget({\n ...parsed,\n ...(parsed.kind === 'record' ? { key: Object.fromEntries(Object.entries(parsed.key).map(([k, v]) => [k, bind(v)])) } : {}),\n ...(parsed.tenant?.kind === 'tenant' ? { tenant: { kind: 'tenant', id: bind(parsed.tenant.id) } } : {}),\n }) as BoundAppTarget;\n}\n","/**\n * What a record page reads, as the requests its sections send.\n *\n * A record page used to reach its reader in three waves of requests — the\n * root and the plan, then a request or two per section, then the history —\n * and at 20 ms to the database each wave was a round of admissions and\n * statements: a lender's loan finished at 0.9 s. The server now answers the\n * whole page in one request (server/http/recordCompose.ts, and the tenant\n * host's own): it plans the page, then asks each section's read on the\n * section's behalf, through the same route and the same grants as the\n * section's own request, and hands every answer back beside the plan. The\n * client keeps them for the requests they answer (client/composedReads.ts),\n * and a section that asks finds its answer waiting.\n *\n * So the one rule is that server and client build each request alike, and\n * the builders live here, used by both: the sections' own modules call them\n * for the request they send, and the server calls them to know what the\n * sections will ask. A request built differently is only asked again — the\n * section sends it, as it always did — so a drift costs a request, never a\n * wrong answer.\n *\n * The record's own row is the one read that is not asked alike: every fields\n * section, the header, the root probe and a custom section's record fields\n * read the one row, each for its columns, and which of them ask together\n * depends on what is drawn (a tab not yet opened asks nothing). The server\n * reads every column any of them names, once, and each is answered with\n * exactly its part (`rowPart`), as the client's own shared read already was\n * (pageFieldsRead.ts).\n */\nimport { findTable, labelPath, primaryKey, type DatabaseSchema, type TableMeta } from './schema.js';\nimport { DEFAULT_HISTORY_LIMIT, kindReading, type PageSection } from './pageModel.js';\nimport { assertNever } from './model.js';\nimport { andQuery, quoteValue } from './query.js';\nimport { tabMatches } from './addressParams.js';\nimport { recordView, referenceProjection } from './referenceModel.js';\nimport { tableAtPrefix } from './viewPaths.js';\nimport type { HttpBreakdownDefinition, HttpFilter } from './resourceHttp.js';\nimport { appTargetFields, type AppTarget } from './appLinks.js';\n\n/** The route a record page is composed on. */\nexport const RECORD_COMPOSE_PATH = '/api/pages/compose';\n\n/** Where a table's rows are named, as the reader's App says it (`recordLabel`, false where it is withheld). */\nexport interface RecordReadNames {\n readonly schema: DatabaseSchema;\n /** Reads in an App's words: references carry their record's name, lists their keys. */\n readonly app: boolean;\n readonly recordLabel: (tableId: string) => string | false | undefined;\n /** Where a table's rows are named, where the reader already knows (the client's labelPath, which is this rule over the same three). */\n readonly namePath?: (table: TableMeta) => string | undefined;\n /** A table by id, where the reader already holds it (the client's findTable, over the same schema). */\n readonly table?: (id: string) => TableMeta | undefined;\n /** A table's row-defined JSON columns (`resources.<r>.defined`): a section showing one also reads its `per`. */\n readonly defined?: (tableId: string) => Readonly<Record<string, { per?: string }>> | undefined;\n}\n\n/** The parts of a section its reads are built from (client pageModel.ts PageSection, shared page.ts). */\nexport interface ReadSection {\n readonly kind: PageSection['kind'];\n readonly hidden?: boolean;\n readonly emptyMode?: 'hide' | 'collapse';\n /** A grouped related list reads its groups, then each group's rows (the rows wait on the groups). */\n readonly group?: unknown;\n readonly presentation?: unknown;\n readonly paths?: readonly string[];\n readonly status?: string;\n readonly subfigure?: string;\n readonly was?: string;\n readonly actions?: ReadonlyArray<{ readonly prefill?: Readonly<Record<string, string>>; readonly to?: AppTarget }>;\n readonly names?: boolean;\n readonly workflow?: { readonly field: string };\n readonly from?: string;\n readonly limit?: number;\n readonly record?: readonly string[];\n readonly when?: string;\n readonly region?: string;\n readonly tab?: string;\n readonly title?: string;\n readonly width?: unknown;\n}\n\n/** A planned view, as the server builds it (ViewDef) or the client reads it off the wire (HttpViewDefinition). */\ninterface ReadView {\n readonly base: string;\n readonly columns?: ReadonlyArray<{ readonly path: string; readonly alias?: string }>;\n readonly filter?: unknown;\n readonly limit?: number;\n}\n\n/** The parts of a section's plan its reads are built from (pagePlans.ts). */\nexport interface ReadPlan<V extends ReadView = ReadView> {\n readonly index: number;\n readonly error?: string;\n readonly empty?: boolean;\n readonly view?: V;\n readonly counts?: boolean;\n readonly totals?: HttpBreakdownDefinition;\n readonly groups?: HttpBreakdownDefinition;\n readonly breakdown?: HttpBreakdownDefinition;\n readonly streams?: ReadonlyArray<TimelineReadStream<V>>;\n readonly reads?: Readonly<Record<string, { readonly error?: string; readonly empty?: boolean; readonly view?: V }>>;\n /** A custom section's aggregate reads, each a breakdown. */\n readonly aggregates?: Readonly<\n Record<string, { readonly error?: string; readonly empty?: boolean; readonly breakdown?: HttpBreakdownDefinition }>\n >;\n}\n\n/** A timeline stream's plan, as far as its reads go (pagePlans.ts): its table, its author, its rows and its far side. */\ninterface TimelineReadStream<V extends ReadView = ReadView> {\n readonly error?: string;\n readonly empty?: boolean;\n readonly from?: string;\n readonly by?: string;\n readonly view?: V;\n readonly key?: readonly string[];\n readonly side?: { readonly error?: string; readonly view?: V };\n}\n\n/**\n * A timeline stream's reads, as its section asks them (kit PageTimeline.tsx): its rows, where its author is a\n * reference with the person's name beside it, read through the reference as a list column of it is; and where the\n * stream has a far side, that side's rows. One rule for the section and the page's planned reads (`recordReads`),\n * so a composed page's answer is the very read the section asks: a record page whose timeline was planned without\n * the author's name asked it again, alone, once the page had drawn (a field report: a fifth round on a record).\n */\nexport function timelineReads<V extends ReadView>(\n names: { readonly table: (id: string) => TableMeta | undefined; readonly namePath: (table: TableMeta) => string | undefined },\n stream: TimelineReadStream<V>,\n): { view: V; side?: V } | undefined {\n if (stream.error || stream.empty || !stream.view) return undefined;\n const table = stream.from === undefined ? undefined : names.table(stream.from);\n const reference = stream.by ? table?.columns.find((column) => column.name === stream.by)?.references : undefined;\n const target = reference ? names.table(reference.table) : undefined;\n const named = target && names.namePath(target);\n const view =\n target && named && !primaryKey(target).includes(named)\n ? ({ ...stream.view, columns: [...(stream.view.columns ?? []), { path: `${stream.by}.${named}` }] } as V)\n : stream.view;\n const side = stream.side?.view && !stream.side.error && stream.key?.length ? stream.side.view : undefined;\n return side ? { view, side } : { view };\n}\n\n/** One request, as a section sends it: its route and its body, without the owner the transport adds. */\nexport interface ReadAsk {\n readonly path: string;\n readonly body: Record<string, unknown>;\n}\n\n/** Where a table's rows are named, as a view path. */\nconst namePathOf = (names: RecordReadNames, table: TableMeta): string | undefined =>\n names.namePath ? names.namePath(table) : labelPath(names.schema, table, names.recordLabel(table.id));\n\n/**\n * The fields a section shows when it names none. In the Workbench, every\n * column: it is the database as it is. In an App, every column a reader\n * reads — not a key the database handed out (a lone numeric or uuid primary\n * key, which opened a lead on \"ID 2\") and not a row version, the same two\n * the scaffold leaves out of the fields it writes (server/scaffold.ts).\n */\nexport function defaultFieldPaths(base: TableMeta, app: boolean): string[] {\n const keys = base.columns.filter((c) => c.primaryKey);\n const surrogate = (c: TableMeta['columns'][number]) =>\n c.primaryKey && keys.length === 1 && (/^(id|uuid|guid)$/i.test(c.name) || /int|serial|number|numeric|uuid/i.test(c.type ?? ''));\n const version = (c: TableMeta['columns'][number]) => /^(version|lock_version|row_version|rowversion|revision)$/i.test(c.name);\n const read = base.columns.filter((c) => !app || !(surrogate(c) || version(c))).map((c) => c.name);\n return read.length ? read : base.columns.map((c) => c.name);\n}\n\n/** A fields section's columns: what it shows, the names of what it references, the record's name, and its workflow's field. */\nexport function fieldsColumns(\n names: RecordReadNames,\n base: TableMeta,\n section: ReadSection,\n): { fetched: string[]; labels: Map<string, string> } {\n const paths = section.paths?.length ? [...section.paths] : defaultFieldPaths(base, names.app);\n const namePath = namePathOf(names, base);\n /* A headline also reads its second figure and every field its actions\n fill a form from; they are fetched, not shown as facts. */\n const extra =\n section.presentation === 'headline'\n ? [\n ...(section.subfigure ? [section.subfigure] : []),\n ...(section.was ? [section.was] : []),\n ...(section.actions ?? []).flatMap((action) => [\n ...Object.values(action.prefill ?? {}),\n ...(action.to ? appTargetFields(action.to) : []),\n ]),\n ].filter((path) => !paths.includes(path))\n : [];\n const projection = referenceProjection(names.schema, base, [...paths, ...new Set(extra)], section.names !== false && names.app, (table) =>\n namePathOf(names, table),\n );\n const fetched = projection.fetched;\n if (namePath && !fetched.includes(namePath)) fetched.push(namePath);\n if (section.workflow && !fetched.includes(section.workflow.field)) fetched.push(section.workflow.field);\n // A row-defined column's fields are the ones its record's `per` picks (definedFields.ts),\n // through a reference too: `requested_item_id.answers` reads `requested_item_id.<per>`.\n for (const path of paths) {\n const through = names.defined ? definedThrough(names.schema, base.id, path, names.defined) : undefined;\n const per = through?.binding.per;\n const perPath = per === undefined ? undefined : through!.prefix ? `${through!.prefix}.${per}` : per;\n if (perPath && !fetched.includes(perPath)) fetched.push(perPath);\n }\n return { fetched, labels: projection.labels };\n}\n\n/**\n * The row-defined column a fields path ends on (`resources.<r>.defined`):\n * the record's own (`specs`) or one through references\n * (`requested_item_id.answers`), with the table it lives on, the hops before\n * it, and its binding. Undefined for any other path. A record shows the\n * column's fields wherever its path is shown, not only on its own page.\n */\nexport function definedThrough<B extends { per?: string }>(\n schema: DatabaseSchema,\n baseId: string,\n path: string,\n defined: (tableId: string) => Readonly<Record<string, B>> | undefined,\n): { table: TableMeta; column: string; prefix: string; binding: B } | undefined {\n const cut = path.lastIndexOf('.');\n const prefix = cut === -1 ? '' : path.slice(0, cut),\n column = path.slice(cut + 1);\n const table = tableAtPrefix(schema, baseId, prefix);\n const bound = table ? defined(table.id) : undefined;\n if (!table || !bound || !Object.hasOwn(bound, column)) return undefined;\n return { table, column, prefix, binding: bound[column] };\n}\n\n/** A record header's columns: its status, its facts, the names of what they reference, and the record's name. */\nexport function headerColumns(\n names: RecordReadNames,\n base: TableMeta,\n section: ReadSection,\n): { fetched: string[]; labels: Map<string, string> } {\n const projection = referenceProjection(\n names.schema,\n base,\n [...(section.status ? [section.status] : []), ...(section.paths ?? [])],\n names.app,\n (table) => namePathOf(names, table),\n );\n const fetched = projection.fetched;\n const namePath = namePathOf(names, base);\n if (namePath && !fetched.includes(namePath)) fetched.push(namePath);\n return { fetched, labels: projection.labels };\n}\n\n/** The key as a filter on the record's own row, one equality per key column. */\nfunction keyFilter(base: TableMeta, key: Readonly<Record<string, unknown>>): HttpFilter<'path'> {\n return { groups: [primaryKey(base).map((column) => ({ path: column, op: '=' as const, value: key[column] as string | number }))] };\n}\n\n/** A read of the record's own row, for columns: the probe (`root`), a fields section (`fields`), the header (`header`), a custom section's record (`<index>:record`). */\nexport function rowBody(pageId: string, base: TableMeta, key: Readonly<Record<string, unknown>>, columns: readonly string[], part: string) {\n return {\n count: 'none' as const,\n view: {\n id: `page:${pageId}:${part}`,\n name: base.name,\n base: base.id,\n columns: columns.map((path) => ({ path })),\n filter: keyFilter(base, key),\n limit: 1 as const,\n },\n };\n}\n\n/** The probe's columns: the key, and the record's name. */\nfunction probeColumns(names: RecordReadNames, base: TableMeta): string[] {\n const namePath = namePathOf(names, base);\n return [...primaryKey(base), ...(namePath ? [namePath] : [])];\n}\n\n/** A related list's first request: its planned view at the reader's page size, with the names and keys a row needs. */\nexport function listView<V extends ReadView>(names: RecordReadNames, section: ReadSection, plan: ReadPlan<V>, rows: number) {\n const table = names.table ? names.table(section.from ?? '') : findTable(names.schema, section.from ?? '');\n const limit = section.limit ?? rows;\n const view: V | undefined = plan.view && !section.limit ? { ...plan.view, limit } : plan.view;\n const explicit = (target: TableMeta) => (names.recordLabel(target.id) === undefined ? undefined : namePathOf(names, target));\n type Columns = Array<{ path: string; alias?: string }>;\n return view && table\n ? recordView(names.schema, table, view as V & { columns?: Columns }, names.app, (target) => namePathOf(names, target), explicit)\n : {\n view: view as (V & { columns: Columns }) | undefined,\n labelColumns: {} as Record<string, string>,\n hidden: [] as string[],\n keyColumns: [] as Array<{ column: string; index: number }>,\n labelColumn: undefined as { path: string; index: number } | undefined,\n };\n}\n\n/** A grouped list's page of groups, as `/api/breakdown` takes it: the request the page's compose asks and the list asks again. */\nexport function groupsBody(groups: HttpBreakdownDefinition, offset: number) {\n return { def: groups, limit: groups.limit, offset };\n}\n\n/** A metric's measure: a count asked as a breakdown with no key, or the plan's own breakdown; none for a plain aggregate. */\nexport function metricMeasure(plan: ReadPlan<ReadView>): HttpBreakdownDefinition | undefined {\n const counted: HttpBreakdownDefinition | undefined =\n plan.counts && plan.view && !plan.breakdown\n ? {\n base: plan.view.base,\n by: [],\n measures: [{ fn: 'count', alias: 'value' }],\n ...(plan.view.filter ? { filter: plan.view.filter as HttpBreakdownDefinition['filter'] } : {}),\n limit: 1,\n }\n : undefined;\n return plan.breakdown ?? counted;\n}\n\n/** Whether the record satisfies a `when`: one bounded query, key and condition composed the way every fold is. */\nexport function whenBody(base: TableMeta, key: Readonly<Record<string, unknown>>, when: string) {\n const keyText = primaryKey(base)\n .map((c) => `${c} = ${quoteValue(String(key[c]))}`)\n .join(' and ');\n return { q: `${base.id} ${andQuery(keyText, when)}`, limit: 1, offset: 0, count: 'none' as const };\n}\n\n/** The first page of a record's history. */\nexport function historyBody(base: TableMeta, key: Readonly<Record<string, unknown>>, limit: number) {\n return { table: base.id, key, limit };\n}\n\n/**\n * The sections a page draws when it opens on `tab`, as PageSections.tsx lays\n * them out: a strip of named tabs draws the one asked for (the first, when\n * none is) and every panel holding the record's own fields; a run of lists\n * made into tabs draws the one asked for; everything else is drawn. The\n * header is drawn in the head. What a closed tab would read is read when it\n * is opened, as ever, and asked for nothing here.\n */\nexport function drawnSections(\n schema: DatabaseSchema,\n page: { sections: readonly ReadSection[]; listTabs?: boolean },\n tab: string | undefined,\n appPage: boolean,\n): Set<number> {\n const drawn = new Set<number>();\n const shownAll = page.sections.map((section, index) => ({ section, index })).filter(({ section }) => !section.hidden);\n const header = shownAll.find(({ section }) => section.kind === 'fields' && section.presentation === 'header');\n if (header) drawn.add(header.index);\n const hasAside = page.sections.some((section) => section.region === 'aside' && !section.hidden);\n const autoTabs = appPage ? page.listTabs === true : page.listTabs !== false;\n const tabbed = (section: ReadSection) =>\n section.kind === 'list' && section.presentation !== 'gallery' && (section.width === undefined || !appPage);\n type Shown = { section: ReadSection; index: number };\n const lay = (items: readonly Shown[], listTabs: boolean, scoped: boolean) => {\n for (let i = 0; i < items.length; i += 1) {\n if (!(listTabs && autoTabs && tabbed(items[i].section))) {\n drawn.add(items[i].index);\n continue;\n }\n let end = i;\n while (end + 1 < items.length && tabbed(items[end + 1].section)) end += 1;\n const run = items.slice(i, end + 1);\n if (run.length === 1) drawn.add(run[0].index);\n else {\n const token = (item: Shown) =>\n scoped\n ? `section:${item.index}`\n : (item.section.title ?? findTable(schema, item.section.from ?? '')?.name ?? item.section.from ?? '');\n const asked = run.findIndex((item) => tabMatches(token(item), tab));\n drawn.add(run[asked === -1 ? 0 : asked].index);\n }\n i = end;\n }\n };\n for (const region of hasAside ? ['main', 'aside'] : [undefined]) {\n const shown = shownAll.filter(\n ({ section }) =>\n !(section.kind === 'fields' && section.presentation === 'header') && (!region || (section.region ?? 'main') === region),\n );\n const scoped = region === 'aside';\n const groups: Array<{ name: string; items: Shown[] }> = [];\n for (const item of shown) {\n if (!item.section.tab) continue;\n const group = groups.find((g) => g.name === item.section.tab);\n if (group) group.items.push(item);\n else groups.push({ name: item.section.tab, items: [item] });\n }\n if (groups.length < 2) {\n lay(shown, true, scoped);\n continue;\n }\n const first = shown.findIndex((item) => item.section.tab);\n lay(shown.slice(0, first), true, scoped);\n const asked = groups.findIndex((group) => tabMatches(`tab:${group.name}`, tab));\n groups.forEach((group, i) => {\n if (i === (asked === -1 ? 0 : asked) || group.items.some(({ section }) => section.kind === 'fields')) lay(group.items, false, scoped);\n });\n lay(\n shown.slice(first).filter((item) => !item.section.tab),\n true,\n scoped,\n );\n }\n return drawn;\n}\n\n/**\n * Every read a record page's sections will ask for when it opens on `tab`,\n * given its plan: the record's row (every column any section drawn reads),\n * and each other request as the section builds it. A hidden section, and\n * one in a closed tab, reads nothing; a section behind a `when` is read\n * too, and its condition asked, since the answer is only kept until it is\n * asked for.\n */\nexport function recordPageReads(\n names: RecordReadNames,\n page: { id: string; sections: readonly ReadSection[]; listTabs?: boolean },\n base: TableMeta,\n plans: readonly ReadPlan[],\n key: Readonly<Record<string, unknown>>,\n options: { rows: number; history: boolean; tab?: string },\n): { row: string[]; parts: Array<{ part: string; columns: string[] }>; reads: ReadAsk[] } {\n const drawn = drawnSections(names.schema, page, options.tab, names.app);\n /* Each reader of the row with its own columns, too: the one read of all of\n them is refused whole where any column is, and then each is asked alone,\n as the sections would have been. */\n const parts = [{ part: 'root', columns: probeColumns(names, base) }];\n const reads: ReadAsk[] = [];\n const planOf = (index: number) => plans.find((plan) => plan.index === index);\n const view = (body: Record<string, unknown>) => reads.push({ path: '/api/view/run', body });\n const breakdown = (def: HttpBreakdownDefinition) => reads.push({ path: '/api/breakdown', body: { def } });\n page.sections.forEach((section, index) => {\n if (section.hidden || !drawn.has(index)) return;\n if (section.when) reads.push({ path: '/api/run', body: whenBody(base, key, section.when) });\n const plan = planOf(index);\n if (section.kind === 'fields') {\n const header = section.presentation === 'header';\n parts.push({\n part: header ? 'header' : 'fields',\n columns: (header ? headerColumns(names, base, section) : fieldsColumns(names, base, section)).fetched,\n });\n return;\n }\n if (section.kind === 'history') {\n if (options.history)\n reads.push({ path: '/api/record-history', body: historyBody(base, key, section.limit ?? DEFAULT_HISTORY_LIMIT) });\n return;\n }\n /* A note reads nothing. Flow runs are fetched lazily by their section, behind the reader's own check\n (server/recordFlows.ts), and a flow's diagram reads its steps when it is drawn, as the reader's rows\n (PageFlowDiagram.tsx). */\n if (kindReading(section.kind, 'none')) return;\n if (section.kind === 'custom' && section.record?.length) parts.push({ part: `${index}:record`, columns: [...section.record] });\n if (!plan || plan.error || plan.empty) return;\n switch (section.kind) {\n case 'metric': {\n const measure = metricMeasure(plan);\n if (measure) breakdown(measure);\n else if (plan.view) view({ view: plan.view });\n return;\n }\n case 'breakdown':\n case 'chart':\n if (plan.breakdown) breakdown(plan.breakdown);\n return;\n case 'timeline':\n for (const stream of plan.streams ?? []) {\n const asked = timelineReads(\n {\n table: (id) => (names.table ? names.table(id) : findTable(names.schema, id)),\n namePath: (table) => namePathOf(names, table),\n },\n stream,\n );\n if (asked) view({ view: asked.view, offset: 0 });\n if (asked?.side) view({ view: asked.side, offset: 0 });\n }\n return;\n case 'custom':\n for (const read of Object.values(plan.reads ?? {}))\n if (!read.error && !read.empty && read.view) view({ view: read.view, offset: 0, count: 'none' });\n for (const total of Object.values(plan.aggregates ?? {}))\n if (!total.error && !total.empty && total.breakdown) breakdown(total.breakdown);\n return;\n case 'list': {\n /* A grouped list asks its groups first; each group's rows are asked of the keys that come back. */\n if (section.group && plan.groups) {\n reads.push({ path: '/api/breakdown', body: groupsBody(plan.groups, 0) });\n if (plan.totals) breakdown(plan.totals);\n return;\n }\n const listed = listView(names, section, plan, options.rows).view;\n if (listed) view({ view: listed, offset: 0 });\n if (plan.totals) breakdown(plan.totals);\n return;\n }\n default:\n assertNever(section.kind, 'page section kind');\n }\n });\n return { row: [...new Set(parts.flatMap((part) => part.columns))], parts, reads };\n}\n"],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Z as Empty, ht as Icon } from "./shared-poc11xxo.js";
|
|
2
2
|
import { F as primaryKey, Jt as formatNumber, f as groupLabel, gt as defineMessages, ut as humanName, yn as keepPreviousData } from "./shared-DJZjSi3J.js";
|
|
3
|
-
import { ut as useQueries } from "./shared-
|
|
4
|
-
import { B as toast, L as state, _ as openRecord, c as columnLabel, d as findTable, s as columnDisplay } from "./shared-
|
|
5
|
-
import { W as namedGroups, ht as positionOf, mt as placements, vt as presentationColumns } from "./shared-
|
|
6
|
-
import { f as Value } from "./shared-
|
|
3
|
+
import { ut as useQueries } from "./shared-BHsjawAG.js";
|
|
4
|
+
import { B as toast, L as state, _ as openRecord, c as columnLabel, d as findTable, s as columnDisplay } from "./shared-BoLbc2WD.js";
|
|
5
|
+
import { W as namedGroups, ht as positionOf, mt as placements, vt as presentationColumns } from "./shared-XcSKDp6N.js";
|
|
6
|
+
import { f as Value } from "./shared-Chp88all.js";
|
|
7
7
|
import { t as usePointerDrag } from "./shared-Ba8y5h2K.js";
|
|
8
8
|
import { t as useOptimisticMoves } from "./shared-mCoqJD_k.js";
|
|
9
|
-
import { n as groupFactPaths, r as groupTable } from "./shared-
|
|
10
|
-
import { l as usePageApi } from "./shared-
|
|
9
|
+
import { n as groupFactPaths, r as groupTable } from "./shared-BdA3LPCB.js";
|
|
10
|
+
import { l as usePageApi } from "./shared-B5aBCUi7.js";
|
|
11
11
|
import { Fragment, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
12
12
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
13
|
// packages/kit/src/client/copy/record/components/PageGroups.ts
|
|
@@ -583,4 +583,4 @@ function GroupedBody({ section, groups, offset, perPage, onOffset, view, labelCo
|
|
|
583
583
|
}
|
|
584
584
|
export { GroupedBody };
|
|
585
585
|
|
|
586
|
-
//# sourceMappingURL=shared-
|
|
586
|
+
//# sourceMappingURL=shared-C_BhUL2I.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-D7ONOwI7.js","names":["say","say"],"sources":["../../../kit/src/client/copy/record/components/PageGroups.ts","../../../kit/src/client/copy/record/components/groupMoves.ts","../../../kit/src/client/components/groupMoves.tsx","../../../kit/src/client/components/PageGroups.tsx"],"sourcesContent":["/** What PageGroups says, in the record page room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n nothingToGroup: 'Nothing to group yet.',\n groupCount: '{shown} of {count}',\n showAll: 'Show all {count}',\n showFewer: 'Show first {count}',\n showAllOf: 'Show all {count} {things} of {group}',\n showFewerOf: 'Show the first {count} {things} of {group}',\n totalOfCount: 'Total of {count}',\n previousGroups: 'Previous groups',\n moreGroups: 'More groups',\n noGroupValue: 'No {caption}',\n});\n","/** What groupMoves says, in the record page room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n movedToGroup: 'Moved “{title}” to {group}.',\n movedToPlace: 'Moved “{title}” to place {index} of {count} in {group}.',\n cannotEnter: '“{title}” can’t be moved to {group} here.',\n wentBack: '“{title}” went back to {group}: {reason}',\n notMoved: 'It was not moved.',\n});\n","/**\n * A grouped list's rows moved by hand (PageGroups.tsx): a row dragged into\n * another group takes that group's value — the same write a board's lane\n * makes, an ordinary update of the grouped column where the reader may write\n * it, or the one command that takes it (`moveToSprint` with `{ sprint }`) —\n * and, where the list is read in a hand order (its sort is one number column\n * the reader may write), a place within the group, numbered as a list view's\n * row is (placeOrder.ts). Alt and ↑/↓ on a row do the same a step at a time:\n * up and down within a ranked group, and past its first or last row into the\n * group before or after.\n *\n * The move is drawn at once (optimisticMoves.ts) and read back once when the\n * last is done; a refusal puts the row back and says the server's sentence.\n * A group the row cannot enter (the empty group, where no write clears the\n * column) is shut while the row is carried, and a row let go over it says so.\n * Rows in a group with no hand order take no place: they land at its end and\n * no gap opens inside it.\n */\nimport { useEffect, useMemo, useRef, useState, type KeyboardEvent, type PointerEvent as ReactPointerEvent } from 'react';\nimport { toast } from '../core.js';\nimport { placements, positionOf, type PlaceFailure } from './placeOrder.js';\nimport { useOptimisticMoves } from './optimisticMoves.js';\nimport { usePointerDrag, type DropPoint, type DropSlot } from './usePointerDrag.js';\nimport { say } from '../copy/record/components/groupMoves.js';\n\n/** What the list hands a grouped body so its rows can move: PageRelated decides the path, the body draws the moves. */\nexport interface GroupMoves {\n /** A row's key under its table's own columns, and its key string as the grid names its `<tr>` (rowKeyString). */\n keyOf: (row: Record<string, unknown>, columns: readonly string[]) => { key: Record<string, unknown>; id: string } | null;\n labelOf: (row: Record<string, unknown>, columns: readonly string[]) => string;\n /** Whether a row may be put in the group with this key (null or undefined: the group with none). */\n enters: (key: unknown) => boolean;\n /** Put the row in the group: null once it applied, else the sentence it was refused with ('' when said already). */\n regroup: (key: Record<string, unknown>, label: string, from: unknown, to: unknown) => Promise<string | null>;\n /** The number column the rows are ranked by, where the list is read in it and the reader may write it. */\n rank?: {\n column: string;\n whole: boolean;\n write: (key: Record<string, unknown>, label: string, value: number, expected: unknown) => Promise<PlaceFailure | null>;\n };\n /** The rows are read again: every group, and the groups' own counts and sums. */\n done: () => Promise<unknown>;\n}\n\n/** The output name the rank is read under when the list does not show it. */\nexport const RANK = '__rank';\nconst GROUP = '__group';\n\ninterface Carried {\n id: string;\n group: number;\n}\ninterface Entry {\n id: string;\n key: Record<string, unknown>;\n title: string;\n position?: number;\n}\n\n/* Each group's rows, named by the group they came from: cached on the answer, so the same answer is the same rows. */\nconst tagged = new WeakMap<object, Array<Record<string, unknown>>>();\nfunction tag(answer: object, rows: Array<Record<string, unknown>>, group: string) {\n let out = tagged.get(answer);\n if (!out || out[0]?.[GROUP] !== group) {\n out = rows.map((row) => ({ ...row, [GROUP]: group }));\n tagged.set(answer, out);\n }\n return out;\n}\n\nexport function useGroupMoves({\n moves,\n groups,\n answers,\n columns,\n label,\n}: {\n moves: GroupMoves | undefined;\n /** Each group's key, in the order drawn. */\n groups: readonly unknown[];\n /** Each group's answer as read (its rows), or undefined while it is asked. */\n answers: ReadonlyArray<{ rows?: unknown[] } | undefined>;\n columns: readonly string[];\n /** A group's name as its header says it. */\n label: (key: unknown) => string;\n}) {\n const text = (key: unknown) => String(key ?? '');\n const zones = useRef(new Map<number, HTMLElement>());\n const refocus = useRef<string | null>(null);\n const [said, setSaid] = useState('');\n const [shut, setShut] = useState<number | null>(null);\n const live = useRef(moves);\n live.current = moves;\n\n const base = answers.flatMap((answer, i) =>\n answer?.rows ? tag(answer, answer.rows as Array<Record<string, unknown>>, text(groups[i])) : [],\n );\n const idOf = (row: Record<string, unknown>) => (moves ? (moves.keyOf(row, columns)?.id ?? null) : null);\n const optimistic = useOptimisticMoves(base, idOf, () => live.current?.done());\n const homeOf = useMemo(() => new Map(base.map((row) => [idOf(row), row[GROUP] as string])), [base]); // eslint-disable-line react-hooks/exhaustive-deps\n\n /** Each group's rows as drawn: what the moves put there, in rank order where the list keeps one, a row moved in\n from elsewhere last where it does not. */\n const bodies = groups.map((key) => {\n const own = optimistic.rows.filter((row) => row[GROUP] === text(key));\n if (moves?.rank) {\n const at = (row: Record<string, unknown>) => positionOf(row[RANK]) ?? Number.POSITIVE_INFINITY;\n return own\n .map((row, i) => [row, i] as const)\n .sort((a, b) => at(a[0]) - at(b[0]) || a[1] - b[1])\n .map(([row]) => row);\n }\n const stayed = own.filter((row) => homeOf.get(idOf(row)) === text(key));\n return [...stayed, ...own.filter((row) => homeOf.get(idOf(row)) !== text(key))];\n });\n /** How many rows a move took into (positive) or out of a group, for its header's count until it is read again. */\n const drift = (i: number) => {\n let by = 0;\n if (!moves || optimistic.rows === base) return by;\n for (const row of optimistic.rows) {\n const id = idOf(row);\n if (id === null) continue;\n const home = homeOf.get(id);\n if (row[GROUP] === text(groups[i]) && home !== text(groups[i])) by++;\n else if (row[GROUP] !== text(groups[i]) && home === text(groups[i])) by--;\n }\n return by;\n };\n\n const entriesOf = (i: number): Entry[] =>\n bodies[i].flatMap((row) => {\n const named = moves?.keyOf(row, columns);\n if (!named) return [];\n const position = positionOf(row[RANK]);\n return [{ id: named.id, key: named.key, title: moves!.labelOf(row, columns), ...(position === undefined ? {} : { position }) }];\n });\n const rowOf = (id: string) => optimistic.rows.find((row) => idOf(row) === id);\n\n /* A moved row is a new element in its new group; the focus follows it there. */\n const itemOf = (id: string) => {\n for (const zone of zones.current.values())\n for (const item of zone.querySelectorAll<HTMLElement>('tr[data-row-key]')) if (item.dataset.rowKey === id) return item;\n return null;\n };\n useEffect(() => {\n const item = refocus.current ? itemOf(refocus.current) : null;\n if (item) {\n item.focus();\n refocus.current = null;\n }\n });\n /* The grid draws its own rows; a row that can move is a Tab stop, with its keys said. */\n useEffect(() => {\n for (const zone of zones.current.values())\n for (const item of zone.querySelectorAll<HTMLElement>('tr[data-row-key]')) {\n if (moves) {\n item.tabIndex = 0;\n item.setAttribute('aria-keyshortcuts', 'Alt+ArrowUp Alt+ArrowDown');\n item.setAttribute('data-draggable', 'true');\n // A row whose move is being saved is quiet, as a board's card is (TriageBoard.css `is-saving`).\n const pending = optimistic.pending(item.dataset.rowKey ?? '');\n item.style.opacity = pending ? '0.7' : '';\n if (pending) item.setAttribute('aria-busy', 'true');\n else item.removeAttribute('aria-busy');\n }\n }\n });\n\n /** Move a row into group `to` at `index` among the rows already there (its end where there is no hand order). */\n const move = (id: string, from: number, to: number, index?: number) => {\n const row = rowOf(id);\n const named = row && moves?.keyOf(row, columns);\n if (!moves || !row || !named) return;\n const title = moves.labelOf(row, columns);\n const group = label(groups[to]);\n if (from !== to && !moves.enters(groups[to])) {\n toast(say('cannotEnter', { title, group }), 'error');\n return;\n }\n const others = entriesOf(to).filter((entry) => entry.id !== id);\n const entry: Entry = { id, key: named.key, title, ...(positionOf(row[RANK]) === undefined ? {} : { position: positionOf(row[RANK]) }) };\n const ranked = moves.rank && index !== undefined ? placements(entry, others, index, moves.rank.whole) : [];\n if (from === to && !ranked.length) return;\n const writes: Array<readonly [string, Record<string, unknown>]> = [];\n if (from !== to) writes.push([id, { [GROUP]: text(groups[to]) }]);\n for (const [each, value] of ranked) writes.push([each.id, { [RANK]: value }]);\n const token = optimistic.begin(writes);\n refocus.current = id;\n const place = index === undefined ? undefined : Math.min(index, others.length) + 1;\n setSaid(\n place !== undefined && moves.rank\n ? say('movedToPlace', { title, index: place, count: others.length + 1, group })\n : say('movedToGroup', { title, group }),\n );\n optimistic.enqueue(async () => {\n try {\n if (from !== to) {\n const refused = await moves.regroup(named.key, title, groups[from], groups[to]);\n if (refused !== null) {\n optimistic.undo(token);\n const sentence = say('wentBack', { title, group: label(groups[from]), reason: refused || say('notMoved') });\n // '' is a refusal its command's own pipeline has said already; once is enough on screen.\n if (refused) toast(sentence, 'error');\n setSaid(sentence);\n return;\n }\n }\n for (const [each, value] of ranked) {\n const failed = await moves.rank!.write(each.key, each.title, value, each.position ?? null);\n if (failed) {\n // The row's group is moved; its place is what the read says, once the queue is done.\n if (failed.refused) toast(failed.message, 'error');\n break;\n }\n }\n optimistic.saved(token);\n } catch (error) {\n optimistic.undo(token);\n throw error;\n }\n });\n };\n\n /* Where a drop lands: the group under the pointer, and in a ranked one before the row in whose upper half it is. */\n const groupOf = (zone: HTMLElement | null | undefined) => {\n for (const [i, each] of zones.current) if (each === zone) return i;\n return undefined;\n };\n const landing = (carried: Carried, at: DropPoint | null) => {\n const to = groupOf(at?.zone);\n if (to === undefined || !at) return undefined;\n if (to !== carried.group && !moves?.enters(groups[to])) return { to, shut: true as const };\n if (!moves?.rank) return to === carried.group ? undefined : { to, before: null as HTMLElement | null };\n const rows = [...at.zone.querySelectorAll<HTMLElement>('tr[data-row-key]')].filter((item) => item.dataset.rowKey !== carried.id);\n let before: HTMLElement | null = null;\n if (at.item) {\n const i = rows.indexOf(at.item);\n before = i < 0 ? null : at.upper ? at.item : (rows[i + 1] ?? null);\n }\n const index = before ? rows.indexOf(before) : rows.length;\n if (to === carried.group) {\n const ids = entriesOf(to).map((entry) => entry.id);\n if (ids.indexOf(carried.id) === index) return undefined;\n }\n return { to, before, index };\n };\n const drag = usePointerDrag<Carried>({\n items: 'tr[data-row-key]',\n gap: true,\n locate: (carried) => itemOf(carried.id),\n over: (carried, at): DropSlot | null => {\n const landed = landing(carried, at);\n setShut(landed && 'shut' in landed ? landed.to : null);\n if (!landed || 'shut' in landed) return null;\n return { zone: zones.current.get(landed.to)!, before: landed.before };\n },\n drop: (carried, at) => {\n setShut(null);\n const landed = landing(carried, at);\n if (!landed) return;\n if ('shut' in landed) {\n const row = rowOf(carried.id);\n toast(say('cannotEnter', { title: row && moves ? moves.labelOf(row, columns) : '', group: label(groups[landed.to]) }), 'error');\n return;\n }\n move(carried.id, carried.group, landed.to, 'index' in landed ? landed.index : undefined);\n },\n end: () => setShut(null),\n });\n\n const groupOfRow = (id: string) => bodies.findIndex((rows) => rows.some((row) => idOf(row) === id));\n /* Alt and ↑/↓: a place up or down in a ranked group, and past its edge into the group before or after. */\n const step = (id: string, by: -1 | 1) => {\n const from = groupOfRow(id);\n if (from < 0) return;\n const ids = entriesOf(from).map((entry) => entry.id);\n const at = ids.indexOf(id);\n if (moves?.rank && at + by >= 0 && at + by < ids.length) {\n move(id, from, from, at + by);\n return;\n }\n for (let to = from + by; to >= 0 && to < groups.length; to += by) {\n if (!moves?.enters(groups[to])) continue;\n move(id, from, to, moves.rank ? (by > 0 ? 0 : entriesOf(to).length) : undefined);\n return;\n }\n };\n\n return {\n movable: Boolean(moves),\n bodies,\n drift,\n pending: (id: string) => optimistic.pending(id),\n /** A group's rows element: where rows land, the shut mark while a row is carried that cannot enter. */\n zone: (i: number) =>\n moves\n ? {\n ref: (element: HTMLElement | null) => {\n if (element) zones.current.set(i, element);\n else zones.current.delete(i);\n },\n 'data-drop-zone': '',\n ...(drag.dragging && shut === i ? { 'data-shut': '', style: { opacity: 0.5 } } : {}),\n onPointerDown: (event: ReactPointerEvent<HTMLElement>) => {\n const item = (event.target as Element).closest<HTMLElement>('tr[data-row-key]');\n if (item && event.currentTarget.contains(item)) drag.start(event, { id: item.dataset.rowKey!, group: i }, item);\n },\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => {\n const item = event.target as HTMLElement;\n if (!(item instanceof HTMLTableRowElement) || !item.dataset.rowKey) return;\n if (event.key === 'Enter' && !event.altKey) {\n item.click();\n return;\n }\n if (!event.altKey || event.metaKey || event.ctrlKey || event.shiftKey) return;\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return;\n event.preventDefault();\n step(item.dataset.rowKey, event.key === 'ArrowUp' ? -1 : 1);\n },\n }\n : {},\n live: moves ? (\n <div className=\"visually-hidden\" aria-live=\"polite\">\n {said}\n </div>\n ) : null,\n };\n}\n","/**\n * A related list read as groups: a header for each record its rows share (an estimate, over its financing options),\n * that group's rows under it, and a page of groups at a time. The groups are one breakdown of the same rows; each\n * group's rows are one list read narrowed to its key, asked once the groups are in. The header's facts are columns of\n * the grouped record read through the rows (`estimate_id.total`), so a header costs no read of its own, and the\n * parent's name comes with the groups (the breakdown names a reference's keys).\n *\n * Where the list says how (`moves`, groupMoves.tsx), a row is dragged into another group, or to its place in a\n * ranked one, and Alt and ↑/↓ do the same from the keyboard; the move is drawn at once and read back once.\n *\n * Nothing here moves a page that is already drawn: a page of groups turned, or a group opened, keeps what stood until\n * the answer is in (`keepPreviousData`), and a group's rows hold their height as one place.\n */\nimport { Fragment, useId, useState, type ReactNode } from 'react';\nimport { keepPreviousData, useQueries } from '@tanstack/react-query';\nimport type { ResourceClientReply } from '@mistriwork/engine/shared/resourceHttp.js';\nimport type { HttpViewDefinition } from '@mistriwork/engine/shared/resourceHttp.js';\nimport { columnDisplay, columnLabel, findTable, openRecord, state } from '../core.js';\nimport { humanName } from '@mistriwork/engine/shared/model.js';\nimport { formatNumber } from '../locale.js';\nimport { GROUP_ROWS_MOST } from '@mistriwork/engine/shared/pageModel.js';\nimport { primaryKey, type DatabaseSchema, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { groupFactPaths, groupTable } from '@mistriwork/engine/shared/pagePaths.js';\nimport type { GridData } from './Grid.js';\nimport { Icon } from './Icon.js';\nimport { Empty } from './Notes.js';\nimport { Value } from './Value.js';\nimport { usePageApi, type SlotProps } from './pageContext.js';\nimport { namedGroups } from './dashboardModel.js';\nimport { presentationColumns } from './columnPresentation.js';\nimport { groupLabel, type DisplayRule } from './valueModel.js';\nimport { say } from '../copy/record/components/PageGroups.js';\nimport { RANK, useGroupMoves, type GroupMoves } from './groupMoves.js';\n\n/** The groups as the breakdown route answers them, without the failure it may answer instead. */\nexport type Groups = Exclude<ResourceClientReply<'/api/breakdown'>, { errors: unknown }>;\n\n/** The list's own conditions with one more on every alternative: the rows of this group. */\nfunction groupView(view: HttpViewDefinition, by: string, key: unknown, limit: number): HttpViewDefinition {\n const held = (view.filter?.groups?.length ? view.filter.groups : [[]]) as Array<Array<Record<string, unknown>>>;\n const condition = key === null || key === undefined ? { path: by, op: 'isNull' } : { path: by, op: '=', value: key };\n return { ...view, filter: { groups: held.map((group) => [...group, condition]) } as HttpViewDefinition['filter'], limit };\n}\n\nexport function GroupedBody({\n section,\n groups,\n offset,\n perPage,\n onOffset,\n view,\n labelColumns,\n rowsPerGroup,\n grid,\n overall,\n moves,\n}: {\n section: SlotProps['section'];\n groups: Groups;\n offset: number;\n perPage: number;\n onOffset: (offset: number) => void;\n /** The list's own planned view, labels and keys added (PageRelated's `presentation`). */\n view: HttpViewDefinition;\n labelColumns: Record<string, string>;\n rowsPerGroup: number;\n /** One group's rows as the list draws its table: with the subtotal row and the words under it, when there are some. */\n grid: (data: GridData, totals?: Record<string, unknown>, totalsLabel?: string) => ReactNode;\n /** Every row's sums, for the foot of the whole list: the words, and each column's figure. */\n overall?: { label: string; sums: Record<string, unknown> };\n /** How a row moves to another group or its place in one; absent, the rows stay where they are. */\n moves?: GroupMoves;\n}) {\n const api = usePageApi();\n const group = section.group!;\n const heading = useId();\n const [open, setOpen] = useState<Record<string, true>>({});\n const from = findTable(section.from ?? '') as TableMeta | undefined;\n const parent = state.schema\n ? (groupTable(state.schema as DatabaseSchema, { from: section.from ?? '', group: section.group }) as TableMeta | undefined)\n : undefined;\n const [keyName] = groups.columns;\n const keyOf = (row: Record<string, unknown>) => row[keyName];\n const keyText = (row: Record<string, unknown>) => String(keyOf(row) ?? '');\n const rows = groups.rows as Array<Record<string, unknown>>;\n const facts = groupFactPaths(section);\n const allowed = parent ? undefined : presentationColumns(state.schema, from, [group.by]).get(group.by)?.allowed;\n /* \"No sprint\": the column's caption in its sentence, an acronym kept as it is (the board's rule, BoardList's `none`). */\n const caption = (columnLabel(section.from ?? '', group.by) as string | null) ?? humanName(group.by.split('.').pop()!.replace(/_id$/, ''));\n const noValue =\n group.empty ?? say('noGroupValue', { caption: /^[A-Z][a-z]/.test(caption) ? caption[0].toLowerCase() + caption.slice(1) : caption });\n const named = namedGroups(groups, keyName, (key) =>\n key === null || key === undefined\n ? noValue\n : groupLabel(columnDisplay(section.from ?? '', group.by) as DisplayRule | null, key, allowed),\n );\n const asked = useQueries({\n queries: rows.map((row) => {\n const limit = open[keyText(row)] ? GROUP_ROWS_MOST : rowsPerGroup;\n /* A ranked list reads each row's place, shown or not, for the number a moved row takes. */\n const ranked =\n moves?.rank && !view.columns?.some((column) => column.path === moves.rank!.column)\n ? { ...view, columns: [...(view.columns ?? []), { path: moves.rank.column, alias: RANK }] }\n : view;\n const narrowed = groupView(ranked, group.by, keyOf(row), limit);\n return {\n queryKey: ['page-list', narrowed, 0],\n queryFn: () => api('/api/view/run', { view: narrowed, offset: 0, count: 'none' }),\n staleTime: 0,\n placeholderData: keepPreviousData,\n };\n }),\n });\n const answers = asked.map((each) =>\n each.data && 'view' in each.data ? (each.data as { rows?: unknown[]; columns?: string[] }) : undefined,\n );\n const shownColumns = answers.find((answer) => answer?.columns)?.columns ?? [];\n const moving = useGroupMoves({ moves, groups: rows.map(keyOf), answers, columns: shownColumns, label: named });\n const things = (section.title ?? from?.name ?? '').toLocaleLowerCase();\n const first = offset + 1;\n const last = offset + rows.length;\n const more = typeof groups.total === 'number' ? offset + rows.length < groups.total : rows.length >= perPage;\n\n return (\n <div className=\"page-groups\">\n {rows.length === 0 ? (\n <Empty className=\"page-group-empty\" text={say('nothingToGroup')} />\n ) : (\n rows.map((row, i) => {\n const key = keyOf(row);\n const id = `${heading}-${i}`;\n const count = (Number(row.group_rows) || 0) + moving.drift(i);\n const answer = asked[i].data;\n const drawn = answer && 'view' in answer ? answer : undefined;\n const body = (moving.movable ? moving.bodies[i] : (drawn?.rows ?? [])) as Array<Record<string, unknown>>;\n const columns = drawn?.columns ?? [];\n const expanded = Boolean(open[keyText(row)]);\n const label = named(key);\n const capped = Math.min(count, expanded ? GROUP_ROWS_MOST : rowsPerGroup);\n /* A subtotal sums every row of the group, not only the ones drawn, and says so when they are fewer. */\n const subtotals = section.totals?.length\n ? Object.fromEntries(section.totals.map((column, n) => [column, row[`total_${n}`] ?? 0]))\n : undefined;\n const partial = count > body.length;\n const recordKey =\n parent && key !== null && key !== undefined && primaryKey(parent).length === 1\n ? { [primaryKey(parent)[0]]: key as string | number }\n : null;\n const lead = body[0];\n return (\n <div key={keyText(row) || `none-${i}`} className=\"page-group\" role=\"group\" aria-labelledby={id}>\n <header className=\"page-group-head\">\n <h3 id={id} className=\"page-group-name\">\n {recordKey && parent ? (\n <button type=\"button\" className=\"page-group-link\" onClick={() => void openRecord(parent.id, recordKey, label)}>\n {label}\n </button>\n ) : (\n label\n )}\n </h3>\n {lead && facts.length > 0 && parent && (\n <span className=\"page-group-facts\">\n {group.show!.map((column, n) => {\n const path = facts[n];\n const said = labelColumns[path] ? lead[labelColumns[path]] : undefined;\n const meta = parent.columns.find((c) => c.name === column);\n const value = lead[path];\n if (value === null || value === undefined || value === '') return null;\n return (\n <Fragment key={column}>\n <span className=\"page-group-fact\">\n {typeof said === 'string' && said !== '' ? (\n said\n ) : (\n <Value col={meta} value={value} tableId={parent.id} links={false} compact />\n )}\n </span>\n </Fragment>\n );\n })}\n </span>\n )}\n <span className=\"page-group-count\">{say('groupCount', { shown: formatNumber(capped), count: formatNumber(count) })}</span>\n {(group.sum ?? []).map((column, n) => {\n const figure = row[`group_sum_${n}`];\n if (figure === null || figure === undefined) return null;\n return (\n <span key={column} className=\"page-group-sum\">\n <Value\n col={from?.columns.find((c) => c.name === column)}\n value={figure}\n tableId={section.from}\n links={false}\n compact\n />\n </span>\n );\n })}\n </header>\n <div className=\"result-table\" id={`${id}-rows`} {...moving.zone(i)}>\n {drawn\n ? grid(\n { ...drawn, rows: body, columns } as GridData,\n subtotals && body.length ? subtotals : undefined,\n partial ? say('totalOfCount', { count: formatNumber(count) }) : undefined,\n )\n : null}\n </div>\n {count > rowsPerGroup && (\n <button\n type=\"button\"\n className=\"ghost page-group-more\"\n aria-expanded={expanded}\n aria-controls={`${id}-rows`}\n aria-label={\n expanded\n ? say('showFewerOf', { count: formatNumber(rowsPerGroup), things, group: label })\n : say('showAllOf', { count: formatNumber(count), things, group: label })\n }\n onClick={() =>\n setOpen((held) =>\n expanded\n ? Object.fromEntries(Object.entries(held).filter(([k]) => k !== keyText(row)))\n : { ...held, [keyText(row)]: true },\n )\n }\n >\n {expanded ? say('showFewer', { count: formatNumber(rowsPerGroup) }) : say('showAll', { count: formatNumber(count) })}\n </button>\n )}\n </div>\n );\n })\n )}\n {moving.live}\n {overall && from && (\n <p className=\"page-group-total\">\n <span className=\"page-group-total-label\">{overall.label}</span>\n {Object.entries(overall.sums).map(([column, figure]) => (\n <span key={column} className=\"page-group-total-figure\">\n <span className=\"page-group-total-caption\">{(columnLabel(from.id, column) as string | null) ?? humanName(column)}</span>\n <Value col={from.columns.find((c) => c.name === column)} value={figure} tableId={from.id} links={false} compact />\n </span>\n ))}\n </p>\n )}\n {(offset > 0 || more) && (\n <div className=\"page-pager\">\n <span className=\"step-control\">\n <button\n type=\"button\"\n className=\"step-button\"\n aria-label={say('previousGroups')}\n disabled={offset === 0}\n onClick={() => onOffset(Math.max(0, offset - perPage))}\n >\n <Icon name=\"caret-left\" size={16} />\n </button>\n <span className=\"page-range step-position\">\n {`${formatNumber(first)}–${formatNumber(last)}${typeof groups.total === 'number' ? say('common.ofTotal', { total: formatNumber(groups.total) }) : ''}`}\n </span>\n <button\n type=\"button\"\n className=\"step-button\"\n aria-label={say('moreGroups')}\n disabled={!more}\n onClick={() => onOffset(offset + perPage)}\n >\n <Icon name=\"caret-right\" size={16} />\n </button>\n </span>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAGA,IAAaA,QAAM,eAAe;CAChC,gBAAgB;CAChB,YAAY;CACZ,SAAS;CACT,WAAW;CACX,WAAW;CACX,aAAa;CACb,cAAc;CACd,gBAAgB;CAChB,YAAY;CACZ,cAAc;AAChB,CAAC;;;ACXD,IAAa,MAAM,eAAe;CAChC,cAAc;CACd,cAAc;CACd,aAAa;CACb,UAAU;CACV,UAAU;AACZ,CAAC;;;;;;;;;;;;;;;;;;;;;ACoCD,IAAa,OAAO;AACpB,IAAM,QAAQ;AAcd,IAAM,yBAAS,IAAI,QAAgD;AACnE,SAAS,IAAI,QAAgB,MAAsC,OAAe;CAChF,IAAI,MAAM,OAAO,IAAI,MAAM;CAC3B,IAAI,CAAC,OAAO,IAAI,EAAE,GAAG,WAAW,OAAO;EACrC,MAAM,KAAK,KAAK,SAAS;GAAE,GAAG;IAAM,QAAQ;EAAM,EAAE;EACpD,OAAO,IAAI,QAAQ,GAAG;CACxB;CACA,OAAO;AACT;AAEA,SAAgB,cAAc,EAC5B,OACA,QACA,SACA,SACA,SAUC;CACD,MAAM,QAAQ,QAAiB,OAAO,OAAO,EAAE;CAC/C,MAAM,QAAQ,uBAAO,IAAI,IAAyB,CAAC;CACnD,MAAM,UAAU,OAAsB,IAAI;CAC1C,MAAM,CAAC,MAAM,WAAW,SAAS,EAAE;CACnC,MAAM,CAAC,MAAM,WAAW,SAAwB,IAAI;CACpD,MAAM,OAAO,OAAO,KAAK;CACzB,KAAK,UAAU;CAEf,MAAM,OAAO,QAAQ,SAAS,QAAQ,MACpC,QAAQ,OAAO,IAAI,QAAQ,OAAO,MAAwC,KAAK,OAAO,EAAE,CAAC,IAAI,CAAC,CAChG;CACA,MAAM,QAAQ,QAAkC,QAAS,MAAM,MAAM,KAAK,OAAO,CAAC,EAAE,MAAM,OAAQ;CAClG,MAAM,aAAa,mBAAmB,MAAM,YAAY,KAAK,SAAS,KAAK,CAAC;CAC5E,MAAM,SAAS,cAAc,IAAI,IAAI,KAAK,KAAK,QAAQ,CAAC,KAAK,GAAG,GAAG,IAAI,MAAgB,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;;;CAIlG,MAAM,SAAS,OAAO,KAAK,QAAQ;EACjC,MAAM,MAAM,WAAW,KAAK,QAAQ,QAAQ,IAAI,WAAW,KAAK,GAAG,CAAC;EACpE,IAAI,OAAO,MAAM;GACf,MAAM,MAAM,QAAiC,WAAW,IAAA,SAAS,KAAK,OAAO;GAC7E,OAAO,IACJ,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,CAAU,CAAC,CAClC,MAAM,GAAG,MAAM,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAClD,KAAK,CAAC,SAAS,GAAG;EACvB;EAEA,OAAO,CAAC,GADO,IAAI,QAAQ,QAAQ,OAAO,IAAI,KAAK,GAAG,CAAC,MAAM,KAAK,GAAG,CAC1D,GAAQ,GAAG,IAAI,QAAQ,QAAQ,OAAO,IAAI,KAAK,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC;CAChF,CAAC;;CAED,MAAM,SAAS,MAAc;EAC3B,IAAI,KAAK;EACT,IAAI,CAAC,SAAS,WAAW,SAAS,MAAM,OAAO;EAC/C,KAAK,MAAM,OAAO,WAAW,MAAM;GACjC,MAAM,KAAK,KAAK,GAAG;GACnB,IAAI,OAAO,MAAM;GACjB,MAAM,OAAO,OAAO,IAAI,EAAE;GAC1B,IAAI,IAAI,WAAW,KAAK,OAAO,EAAE,KAAK,SAAS,KAAK,OAAO,EAAE,GAAG;QAC3D,IAAI,IAAI,WAAW,KAAK,OAAO,EAAE,KAAK,SAAS,KAAK,OAAO,EAAE,GAAG;EACvE;EACA,OAAO;CACT;CAEA,MAAM,aAAa,MACjB,OAAO,EAAE,CAAC,SAAS,QAAQ;EACzB,MAAM,QAAQ,OAAO,MAAM,KAAK,OAAO;EACvC,IAAI,CAAC,OAAO,OAAO,CAAC;EACpB,MAAM,WAAW,WAAW,IAAI,KAAK;EACrC,OAAO,CAAC;GAAE,IAAI,MAAM;GAAI,KAAK,MAAM;GAAK,OAAO,MAAO,QAAQ,KAAK,OAAO;GAAG,GAAI,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS;EAAG,CAAC;CAChI,CAAC;CACH,MAAM,SAAS,OAAe,WAAW,KAAK,MAAM,QAAQ,KAAK,GAAG,MAAM,EAAE;CAG5E,MAAM,UAAU,OAAe;EAC7B,KAAK,MAAM,QAAQ,MAAM,QAAQ,OAAO,GACtC,KAAK,MAAM,QAAQ,KAAK,iBAA8B,kBAAkB,GAAG,IAAI,KAAK,QAAQ,WAAW,IAAI,OAAO;EACpH,OAAO;CACT;CACA,gBAAgB;EACd,MAAM,OAAO,QAAQ,UAAU,OAAO,QAAQ,OAAO,IAAI;EACzD,IAAI,MAAM;GACR,KAAK,MAAM;GACX,QAAQ,UAAU;EACpB;CACF,CAAC;CAED,gBAAgB;EACd,KAAK,MAAM,QAAQ,MAAM,QAAQ,OAAO,GACtC,KAAK,MAAM,QAAQ,KAAK,iBAA8B,kBAAkB,GACtE,IAAI,OAAO;GACT,KAAK,WAAW;GAChB,KAAK,aAAa,qBAAqB,2BAA2B;GAClE,KAAK,aAAa,kBAAkB,MAAM;GAE1C,MAAM,UAAU,WAAW,QAAQ,KAAK,QAAQ,UAAU,EAAE;GAC5D,KAAK,MAAM,UAAU,UAAU,QAAQ;GACvC,IAAI,SAAS,KAAK,aAAa,aAAa,MAAM;QAC7C,KAAK,gBAAgB,WAAW;EACvC;CAEN,CAAC;;CAGD,MAAM,QAAQ,IAAY,MAAc,IAAY,UAAmB;EACrE,MAAM,MAAM,MAAM,EAAE;EACpB,MAAM,QAAQ,OAAO,OAAO,MAAM,KAAK,OAAO;EAC9C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO;EAC9B,MAAM,QAAQ,MAAM,QAAQ,KAAK,OAAO;EACxC,MAAM,QAAQ,MAAM,OAAO,GAAG;EAC9B,IAAI,SAAS,MAAM,CAAC,MAAM,OAAO,OAAO,GAAG,GAAG;GAC5C,MAAM,IAAI,eAAe;IAAE;IAAO;GAAM,CAAC,GAAG,OAAO;GACnD;EACF;EACA,MAAM,SAAS,UAAU,EAAE,CAAC,CAAC,QAAQ,UAAU,MAAM,OAAO,EAAE;EAC9D,MAAM,QAAe;GAAE;GAAI,KAAK,MAAM;GAAK;GAAO,GAAI,WAAW,IAAA,SAAS,MAAM,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,WAAW,IAAI,KAAK,EAAE;EAAG;EACtI,MAAM,SAAS,MAAM,QAAQ,UAAU,KAAA,IAAY,WAAW,OAAO,QAAQ,OAAO,MAAM,KAAK,KAAK,IAAI,CAAC;EACzG,IAAI,SAAS,MAAM,CAAC,OAAO,QAAQ;EACnC,MAAM,SAA4D,CAAC;EACnE,IAAI,SAAS,IAAI,OAAO,KAAK,CAAC,IAAI,GAAG,QAAQ,KAAK,OAAO,GAAG,EAAE,CAAC,CAAC;EAChE,KAAK,MAAM,CAAC,MAAM,UAAU,QAAQ,OAAO,KAAK,CAAC,KAAK,IAAI,GAAG,OAAO,MAAM,CAAC,CAAC;EAC5E,MAAM,QAAQ,WAAW,MAAM,MAAM;EACrC,QAAQ,UAAU;EAClB,MAAM,QAAQ,UAAU,KAAA,IAAY,KAAA,IAAY,KAAK,IAAI,OAAO,OAAO,MAAM,IAAI;EACjF,QACE,UAAU,KAAA,KAAa,MAAM,OACzB,IAAI,gBAAgB;GAAE;GAAO,OAAO;GAAO,OAAO,OAAO,SAAS;GAAG;EAAM,CAAC,IAC5E,IAAI,gBAAgB;GAAE;GAAO;EAAM,CAAC,CAC1C;EACA,WAAW,QAAQ,YAAY;GAC7B,IAAI;IACF,IAAI,SAAS,IAAI;KACf,MAAM,UAAU,MAAM,MAAM,QAAQ,MAAM,KAAK,OAAO,OAAO,OAAO,OAAO,GAAG;KAC9E,IAAI,YAAY,MAAM;MACpB,WAAW,KAAK,KAAK;MACrB,MAAM,WAAW,IAAI,YAAY;OAAE;OAAO,OAAO,MAAM,OAAO,KAAK;OAAG,QAAQ,WAAW,IAAI,UAAU;MAAE,CAAC;MAE1G,IAAI,SAAS,MAAM,UAAU,OAAO;MACpC,QAAQ,QAAQ;MAChB;KACF;IACF;IACA,KAAK,MAAM,CAAC,MAAM,UAAU,QAAQ;KAClC,MAAM,SAAS,MAAM,MAAM,KAAM,MAAM,KAAK,KAAK,KAAK,OAAO,OAAO,KAAK,YAAY,IAAI;KACzF,IAAI,QAAQ;MAEV,IAAI,OAAO,SAAS,MAAM,OAAO,SAAS,OAAO;MACjD;KACF;IACF;IACA,WAAW,MAAM,KAAK;GACxB,SAAS,OAAO;IACd,WAAW,KAAK,KAAK;IACrB,MAAM;GACR;EACF,CAAC;CACH;CAGA,MAAM,WAAW,SAAyC;EACxD,KAAK,MAAM,CAAC,GAAG,SAAS,MAAM,SAAS,IAAI,SAAS,MAAM,OAAO;CAEnE;CACA,MAAM,WAAW,SAAkB,OAAyB;EAC1D,MAAM,KAAK,QAAQ,IAAI,IAAI;EAC3B,IAAI,OAAO,KAAA,KAAa,CAAC,IAAI,OAAO,KAAA;EACpC,IAAI,OAAO,QAAQ,SAAS,CAAC,OAAO,OAAO,OAAO,GAAG,GAAG,OAAO;GAAE;GAAI,MAAM;EAAc;EACzF,IAAI,CAAC,OAAO,MAAM,OAAO,OAAO,QAAQ,QAAQ,KAAA,IAAY;GAAE;GAAI,QAAQ;EAA2B;EACrG,MAAM,OAAO,CAAC,GAAG,GAAG,KAAK,iBAA8B,kBAAkB,CAAC,CAAC,CAAC,QAAQ,SAAS,KAAK,QAAQ,WAAW,QAAQ,EAAE;EAC/H,IAAI,SAA6B;EACjC,IAAI,GAAG,MAAM;GACX,MAAM,IAAI,KAAK,QAAQ,GAAG,IAAI;GAC9B,SAAS,IAAI,IAAI,OAAO,GAAG,QAAQ,GAAG,OAAQ,KAAK,IAAI,MAAM;EAC/D;EACA,MAAM,QAAQ,SAAS,KAAK,QAAQ,MAAM,IAAI,KAAK;EACnD,IAAI,OAAO,QAAQ,OACL;OAAA,UAAU,EAAE,CAAC,CAAC,KAAK,UAAU,MAAM,EAC3C,CAAA,CAAI,QAAQ,QAAQ,EAAE,MAAM,OAAO,OAAO,KAAA;EAAA;EAEhD,OAAO;GAAE;GAAI;GAAQ;EAAM;CAC7B;CACA,MAAM,OAAO,eAAwB;EACnC,OAAO;EACP,KAAK;EACL,SAAS,YAAY,OAAO,QAAQ,EAAE;EACtC,OAAO,SAAS,OAAwB;GACtC,MAAM,SAAS,QAAQ,SAAS,EAAE;GAClC,QAAQ,UAAU,UAAU,SAAS,OAAO,KAAK,IAAI;GACrD,IAAI,CAAC,UAAU,UAAU,QAAQ,OAAO;GACxC,OAAO;IAAE,MAAM,MAAM,QAAQ,IAAI,OAAO,EAAE;IAAI,QAAQ,OAAO;GAAO;EACtE;EACA,OAAO,SAAS,OAAO;GACrB,QAAQ,IAAI;GACZ,MAAM,SAAS,QAAQ,SAAS,EAAE;GAClC,IAAI,CAAC,QAAQ;GACb,IAAI,UAAU,QAAQ;IACpB,MAAM,MAAM,MAAM,QAAQ,EAAE;IAC5B,MAAM,IAAI,eAAe;KAAE,OAAO,OAAO,QAAQ,MAAM,QAAQ,KAAK,OAAO,IAAI;KAAI,OAAO,MAAM,OAAO,OAAO,GAAG;IAAE,CAAC,GAAG,OAAO;IAC9H;GACF;GACA,KAAK,QAAQ,IAAI,QAAQ,OAAO,OAAO,IAAI,WAAW,SAAS,OAAO,QAAQ,KAAA,CAAS;EACzF;EACA,WAAW,QAAQ,IAAI;CACzB,CAAC;CAED,MAAM,cAAc,OAAe,OAAO,WAAW,SAAS,KAAK,MAAM,QAAQ,KAAK,GAAG,MAAM,EAAE,CAAC;CAElG,MAAM,QAAQ,IAAY,OAAe;EACvC,MAAM,OAAO,WAAW,EAAE;EAC1B,IAAI,OAAO,GAAG;EACd,MAAM,MAAM,UAAU,IAAI,CAAC,CAAC,KAAK,UAAU,MAAM,EAAE;EACnD,MAAM,KAAK,IAAI,QAAQ,EAAE;EACzB,IAAI,OAAO,QAAQ,KAAK,MAAM,KAAK,KAAK,KAAK,IAAI,QAAQ;GACvD,KAAK,IAAI,MAAM,MAAM,KAAK,EAAE;GAC5B;EACF;EACA,KAAK,IAAI,KAAK,OAAO,IAAI,MAAM,KAAK,KAAK,OAAO,QAAQ,MAAM,IAAI;GAChE,IAAI,CAAC,OAAO,OAAO,OAAO,GAAG,GAAG;GAChC,KAAK,IAAI,MAAM,IAAI,MAAM,OAAQ,KAAK,IAAI,IAAI,UAAU,EAAE,CAAC,CAAC,SAAU,KAAA,CAAS;GAC/E;EACF;CACF;CAEA,OAAO;EACL,SAAS,QAAQ,KAAK;EACtB;EACA;EACA,UAAU,OAAe,WAAW,QAAQ,EAAE;;EAE9C,OAAO,MACL,QACI;GACE,MAAM,YAAgC;IACpC,IAAI,SAAS,MAAM,QAAQ,IAAI,GAAG,OAAO;SACpC,MAAM,QAAQ,OAAO,CAAC;GAC7B;GACA,kBAAkB;GAClB,GAAI,KAAK,YAAY,SAAS,IAAI;IAAE,aAAa;IAAI,OAAO,EAAE,SAAS,GAAI;GAAE,IAAI,CAAC;GAClF,gBAAgB,UAA0C;IACxD,MAAM,OAAQ,MAAM,OAAmB,QAAqB,kBAAkB;IAC9E,IAAI,QAAQ,MAAM,cAAc,SAAS,IAAI,GAAG,KAAK,MAAM,OAAO;KAAE,IAAI,KAAK,QAAQ;KAAS,OAAO;IAAE,GAAG,IAAI;GAChH;GACA,YAAY,UAAsC;IAChD,MAAM,OAAO,MAAM;IACnB,IAAI,EAAE,gBAAgB,wBAAwB,CAAC,KAAK,QAAQ,QAAQ;IACpE,IAAI,MAAM,QAAQ,WAAW,CAAC,MAAM,QAAQ;KAC1C,KAAK,MAAM;KACX;IACF;IACA,IAAI,CAAC,MAAM,UAAU,MAAM,WAAW,MAAM,WAAW,MAAM,UAAU;IACvE,IAAI,MAAM,QAAQ,aAAa,MAAM,QAAQ,aAAa;IAC1D,MAAM,eAAe;IACrB,KAAK,KAAK,QAAQ,QAAQ,MAAM,QAAQ,YAAY,KAAK,CAAC;GAC5D;EACF,IACA,CAAC;EACP,MAAM,QACJ,oBAAC,OAAD;GAAK,WAAU;GAAkB,aAAU;GACxC,UAAA;EACE,CAAA,IACH;CACN;AACF;;;;;;;;;;;;;;;;ACjSA,SAAS,UAAU,MAA0B,IAAY,KAAc,OAAmC;CACxG,MAAM,OAAQ,KAAK,QAAQ,QAAQ,SAAS,KAAK,OAAO,SAAS,CAAC,CAAC,CAAC;CACpE,MAAM,YAAY,QAAQ,QAAQ,QAAQ,KAAA,IAAY;EAAE,MAAM;EAAI,IAAI;CAAS,IAAI;EAAE,MAAM;EAAI,IAAI;EAAK,OAAO;CAAI;CACnH,OAAO;EAAE,GAAG;EAAM,QAAQ,EAAE,QAAQ,KAAK,KAAK,UAAU,CAAC,GAAG,OAAO,SAAS,CAAC,EAAE;EAAmC;CAAM;AAC1H;AAEA,SAAgB,YAAY,EAC1B,SACA,QACA,QACA,SACA,UACA,MACA,cACA,cACA,MACA,SACA,SAiBC;CACD,MAAM,MAAM,WAAW;CACvB,MAAM,QAAQ,QAAQ;CACtB,MAAM,UAAU,MAAM;CACtB,MAAM,CAAC,MAAM,WAAW,SAA+B,CAAC,CAAC;CACzD,MAAM,OAAO,UAAU,QAAQ,QAAQ,EAAE;CACzC,MAAM,SAAS,MAAM,SAChB,WAAW,MAAM,QAA0B;EAAE,MAAM,QAAQ,QAAQ;EAAI,OAAO,QAAQ;CAAM,CAAC,IAC9F,KAAA;CACJ,MAAM,CAAC,WAAW,OAAO;CACzB,MAAM,SAAS,QAAiC,IAAI;CACpD,MAAM,WAAW,QAAiC,OAAO,MAAM,GAAG,KAAK,EAAE;CACzE,MAAM,OAAO,OAAO;CACpB,MAAM,QAAQ,eAAe,OAAO;CACpC,MAAM,UAAU,SAAS,KAAA,IAAY,oBAAoB,MAAM,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,EAAE;CAExG,MAAM,UAAW,YAAY,QAAQ,QAAQ,IAAI,MAAM,EAAE,KAAuB,UAAU,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAE,QAAQ,QAAQ,EAAE,CAAC;CACxI,MAAM,UACJ,MAAM,SAASC,MAAI,gBAAgB,EAAE,SAAS,cAAc,KAAK,OAAO,IAAI,QAAQ,EAAE,CAAC,YAAY,IAAI,QAAQ,MAAM,CAAC,IAAI,QAAQ,CAAC;CACrI,MAAM,QAAQ,YAAY,QAAQ,UAAU,QAC1C,QAAQ,QAAQ,QAAQ,KAAA,IACpB,UACA,WAAW,cAAc,QAAQ,QAAQ,IAAI,MAAM,EAAE,GAAyB,KAAK,OAAO,CAChG;CACA,MAAM,QAAQ,WAAW,EACvB,SAAS,KAAK,KAAK,QAAQ;EACzB,MAAM,QAAQ,KAAK,QAAQ,GAAG,KAAA,KAAuB;EAMrD,MAAM,WAAW,UAHf,OAAO,QAAQ,CAAC,KAAK,SAAS,MAAM,WAAW,OAAO,SAAS,MAAM,KAAM,MAAM,IAC7E;GAAE,GAAG;GAAM,SAAS,CAAC,GAAI,KAAK,WAAW,CAAC,GAAI;IAAE,MAAM,MAAM,KAAK;IAAQ,OAAO;GAAK,CAAC;EAAE,IACxF,MAC6B,MAAM,IAAI,MAAM,GAAG,GAAG,KAAK;EAC9D,OAAO;GACL,UAAU;IAAC;IAAa;IAAU;GAAC;GACnC,eAAe,IAAI,iBAAiB;IAAE,MAAM;IAAU,QAAQ;IAAG,OAAO;GAAO,CAAC;GAChF,WAAW;GACX,iBAAiB;EACnB;CACF,CAAC,EACH,CAAC;CACD,MAAM,UAAU,MAAM,KAAK,SACzB,KAAK,QAAQ,UAAU,KAAK,OAAQ,KAAK,OAAoD,KAAA,CAC/F;CACA,MAAM,eAAe,QAAQ,MAAM,WAAW,QAAQ,OAAO,CAAC,EAAE,WAAW,CAAC;CAC5E,MAAM,SAAS,cAAc;EAAE;EAAO,QAAQ,KAAK,IAAI,KAAK;EAAG;EAAS,SAAS;EAAc,OAAO;CAAM,CAAC;CAC7G,MAAM,UAAU,QAAQ,SAAS,MAAM,QAAQ,GAAA,CAAI,kBAAkB;CACrE,MAAM,QAAQ,SAAS;CACvB,MAAM,OAAO,SAAS,KAAK;CAC3B,MAAM,OAAO,OAAO,OAAO,UAAU,WAAW,SAAS,KAAK,SAAS,OAAO,QAAQ,KAAK,UAAU;CAErG,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACG,KAAK,WAAW,IACf,oBAAC,OAAD;IAAO,WAAU;IAAmB,MAAMA,MAAI,gBAAgB;GAAI,CAAA,IAElE,KAAK,KAAK,KAAK,MAAM;IACnB,MAAM,MAAM,MAAM,GAAG;IACrB,MAAM,KAAK,GAAG,QAAQ,GAAG;IACzB,MAAM,SAAS,OAAO,IAAI,UAAU,KAAK,KAAK,OAAO,MAAM,CAAC;IAC5D,MAAM,SAAS,MAAM,EAAE,CAAC;IACxB,MAAM,QAAQ,UAAU,UAAU,SAAS,SAAS,KAAA;IACpD,MAAM,OAAQ,OAAO,UAAU,OAAO,OAAO,KAAM,OAAO,QAAQ,CAAC;IACnE,MAAM,UAAU,OAAO,WAAW,CAAC;IACnC,MAAM,WAAW,QAAQ,KAAK,QAAQ,GAAG,EAAE;IAC3C,MAAM,QAAQ,MAAM,GAAG;IACvB,MAAM,SAAS,KAAK,IAAI,OAAO,WAAA,KAA6B,YAAY;IAExE,MAAM,YAAY,QAAQ,QAAQ,SAC9B,OAAO,YAAY,QAAQ,OAAO,KAAK,QAAQ,MAAM,CAAC,QAAQ,IAAI,SAAS,QAAQ,CAAC,CAAC,CAAC,IACtF,KAAA;IACJ,MAAM,UAAU,QAAQ,KAAK;IAC7B,MAAM,YACJ,UAAU,QAAQ,QAAQ,QAAQ,KAAA,KAAa,WAAW,MAAM,CAAC,CAAC,WAAW,IACzE,GAAG,WAAW,MAAM,CAAC,CAAC,KAAK,IAAuB,IAClD;IACN,MAAM,OAAO,KAAK;IAClB,OACE,qBAAC,OAAD;KAAuC,WAAU;KAAa,MAAK;KAAQ,mBAAiB;KAA5F,UAAA;MACE,qBAAC,UAAD;OAAQ,WAAU;OAAlB,UAAA;QACE,oBAAC,MAAD;SAAQ;SAAI,WAAU;SACnB,UAAA,aAAa,SACZ,oBAAC,UAAD;UAAQ,MAAK;UAAS,WAAU;UAAkB,eAAe,KAAK,WAAW,OAAO,IAAI,WAAW,KAAK;UACzG,UAAA;SACK,CAAA,IAER;QAEA,CAAA;QACH,QAAQ,MAAM,SAAS,KAAK,UAC3B,oBAAC,QAAD;SAAM,WAAU;SACb,UAAA,MAAM,KAAM,KAAK,QAAQ,MAAM;UAC9B,MAAM,OAAO,MAAM;UACnB,MAAM,OAAO,aAAa,QAAQ,KAAK,aAAa,SAAS,KAAA;UAC7D,MAAM,OAAO,OAAO,QAAQ,MAAM,MAAM,EAAE,SAAS,MAAM;UACzD,MAAM,QAAQ,KAAK;UACnB,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,IAAI,OAAO;UAClE,OACE,oBAAC,UAAD,EAAA,UACE,oBAAC,QAAD;WAAM,WAAU;WACb,UAAA,OAAO,SAAS,YAAY,SAAS,KACpC,OAEA,oBAAC,OAAD;YAAO,KAAK;YAAa;YAAO,SAAS,OAAO;YAAI,OAAO;YAAO,SAAA;WAAS,CAAA;UAEzE,CAAA,EACE,GARK,MAQL;SAEd,CAAC;QACG,CAAA;QAER,oBAAC,QAAD;SAAM,WAAU;SAAoB,UAAAA,MAAI,cAAc;UAAE,OAAO,aAAa,MAAM;UAAG,OAAO,aAAa,KAAK;SAAE,CAAC;QAAQ,CAAA;SACvH,MAAM,OAAO,CAAC,EAAA,CAAG,KAAK,QAAQ,MAAM;SACpC,MAAM,SAAS,IAAI,aAAa;SAChC,IAAI,WAAW,QAAQ,WAAW,KAAA,GAAW,OAAO;SACpD,OACE,oBAAC,QAAD;UAAmB,WAAU;UAC3B,UAAA,oBAAC,OAAD;WACE,KAAK,MAAM,QAAQ,MAAM,MAAM,EAAE,SAAS,MAAM;WAChD,OAAO;WACP,SAAS,QAAQ;WACjB,OAAO;WACP,SAAA;UACD,CAAA;SACG,GARK,MAQL;QAEV,CAAC;OACK;;MACR,oBAAC,OAAD;OAAK,WAAU;OAAe,IAAI,GAAG,GAAG;OAAQ,GAAI,OAAO,KAAK,CAAC;OAC9D,UAAA,QACG,KACE;QAAE,GAAG;QAAO,MAAM;QAAM;OAAQ,GAChC,aAAa,KAAK,SAAS,YAAY,KAAA,GACvC,UAAUA,MAAI,gBAAgB,EAAE,OAAO,aAAa,KAAK,EAAE,CAAC,IAAI,KAAA,CAClE,IACA;MACD,CAAA;MACJ,QAAQ,gBACP,oBAAC,UAAD;OACE,MAAK;OACL,WAAU;OACV,iBAAe;OACf,iBAAe,GAAG,GAAG;OACrB,cACE,WACIA,MAAI,eAAe;QAAE,OAAO,aAAa,YAAY;QAAG;QAAQ,OAAO;OAAM,CAAC,IAC9EA,MAAI,aAAa;QAAE,OAAO,aAAa,KAAK;QAAG;QAAQ,OAAO;OAAM,CAAC;OAE3E,eACE,SAAS,SACP,WACI,OAAO,YAAY,OAAO,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,MAAM,QAAQ,GAAG,CAAC,CAAC,IAC3E;QAAE,GAAG;SAAO,QAAQ,GAAG,IAAI;OAAK,CACtC;OAGD,UAAA,WAAWA,MAAI,aAAa,EAAE,OAAO,aAAa,YAAY,EAAE,CAAC,IAAIA,MAAI,WAAW,EAAE,OAAO,aAAa,KAAK,EAAE,CAAC;MAC7G,CAAA;KAEP;IAjFK,GAAA,QAAQ,GAAG,KAAK,QAAQ,GAiF7B;GAET,CAAC;GAEF,OAAO;GACP,WAAW,QACV,qBAAC,KAAD;IAAG,WAAU;IAAb,UAAA,CACE,oBAAC,QAAD;KAAM,WAAU;KAA0B,UAAA,QAAQ;IAAY,CAAA,GAC7D,OAAO,QAAQ,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,YAC1C,qBAAC,QAAD;KAAmB,WAAU;KAA7B,UAAA,CACE,oBAAC,QAAD;MAAM,WAAU;MAA6B,UAAA,YAAY,KAAK,IAAI,MAAM,KAAuB,UAAU,MAAM;KAAQ,CAAA,GACvH,oBAAC,OAAD;MAAO,KAAK,KAAK,QAAQ,MAAM,MAAM,EAAE,SAAS,MAAM;MAAG,OAAO;MAAQ,SAAS,KAAK;MAAI,OAAO;MAAO,SAAA;KAAS,CAAA,CAC7G;IAHK,GAAA,MAGL,CACP,CACA;;IAEH,SAAS,KAAK,SACd,oBAAC,OAAD;IAAK,WAAU;IACb,UAAA,qBAAC,QAAD;KAAM,WAAU;KAAhB,UAAA;MACE,oBAAC,UAAD;OACE,MAAK;OACL,WAAU;OACV,cAAYA,MAAI,gBAAgB;OAChC,UAAU,WAAW;OACrB,eAAe,SAAS,KAAK,IAAI,GAAG,SAAS,OAAO,CAAC;OAErD,UAAA,oBAAC,MAAD;QAAM,MAAK;QAAa,MAAM;OAAK,CAAA;MAC7B,CAAA;MACR,oBAAC,QAAD;OAAM,WAAU;OACb,UAAA,GAAG,aAAa,KAAK,EAAE,GAAG,aAAa,IAAI,IAAI,OAAO,OAAO,UAAU,WAAWA,MAAI,kBAAkB,EAAE,OAAO,aAAa,OAAO,KAAK,EAAE,CAAC,IAAI;MAC9I,CAAA;MACN,oBAAC,UAAD;OACE,MAAK;OACL,WAAU;OACV,cAAYA,MAAI,YAAY;OAC5B,UAAU,CAAC;OACX,eAAe,SAAS,SAAS,OAAO;OAExC,UAAA,oBAAC,MAAD;QAAM,MAAK;QAAc,MAAM;OAAK,CAAA;MAC9B,CAAA;KACJ;;GACH,CAAA;EAEJ;;AAET"}
|
|
1
|
+
{"version":3,"file":"shared-C_BhUL2I.js","names":["say","say"],"sources":["../../../kit/src/client/copy/record/components/PageGroups.ts","../../../kit/src/client/copy/record/components/groupMoves.ts","../../../kit/src/client/components/groupMoves.tsx","../../../kit/src/client/components/PageGroups.tsx"],"sourcesContent":["/** What PageGroups says, in the record page room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n nothingToGroup: 'Nothing to group yet.',\n groupCount: '{shown} of {count}',\n showAll: 'Show all {count}',\n showFewer: 'Show first {count}',\n showAllOf: 'Show all {count} {things} of {group}',\n showFewerOf: 'Show the first {count} {things} of {group}',\n totalOfCount: 'Total of {count}',\n previousGroups: 'Previous groups',\n moreGroups: 'More groups',\n noGroupValue: 'No {caption}',\n});\n","/** What groupMoves says, in the record page room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n movedToGroup: 'Moved “{title}” to {group}.',\n movedToPlace: 'Moved “{title}” to place {index} of {count} in {group}.',\n cannotEnter: '“{title}” can’t be moved to {group} here.',\n wentBack: '“{title}” went back to {group}: {reason}',\n notMoved: 'It was not moved.',\n});\n","/**\n * A grouped list's rows moved by hand (PageGroups.tsx): a row dragged into\n * another group takes that group's value — the same write a board's lane\n * makes, an ordinary update of the grouped column where the reader may write\n * it, or the one command that takes it (`moveToSprint` with `{ sprint }`) —\n * and, where the list is read in a hand order (its sort is one number column\n * the reader may write), a place within the group, numbered as a list view's\n * row is (placeOrder.ts). Alt and ↑/↓ on a row do the same a step at a time:\n * up and down within a ranked group, and past its first or last row into the\n * group before or after.\n *\n * The move is drawn at once (optimisticMoves.ts) and read back once when the\n * last is done; a refusal puts the row back and says the server's sentence.\n * A group the row cannot enter (the empty group, where no write clears the\n * column) is shut while the row is carried, and a row let go over it says so.\n * Rows in a group with no hand order take no place: they land at its end and\n * no gap opens inside it.\n */\nimport { useEffect, useMemo, useRef, useState, type KeyboardEvent, type PointerEvent as ReactPointerEvent } from 'react';\nimport { toast } from '../core.js';\nimport { placements, positionOf, type PlaceFailure } from './placeOrder.js';\nimport { useOptimisticMoves } from './optimisticMoves.js';\nimport { usePointerDrag, type DropPoint, type DropSlot } from './usePointerDrag.js';\nimport { say } from '../copy/record/components/groupMoves.js';\n\n/** What the list hands a grouped body so its rows can move: PageRelated decides the path, the body draws the moves. */\nexport interface GroupMoves {\n /** A row's key under its table's own columns, and its key string as the grid names its `<tr>` (rowKeyString). */\n keyOf: (row: Record<string, unknown>, columns: readonly string[]) => { key: Record<string, unknown>; id: string } | null;\n labelOf: (row: Record<string, unknown>, columns: readonly string[]) => string;\n /** Whether a row may be put in the group with this key (null or undefined: the group with none). */\n enters: (key: unknown) => boolean;\n /** Put the row in the group: null once it applied, else the sentence it was refused with ('' when said already). */\n regroup: (key: Record<string, unknown>, label: string, from: unknown, to: unknown) => Promise<string | null>;\n /** The number column the rows are ranked by, where the list is read in it and the reader may write it. */\n rank?: {\n column: string;\n whole: boolean;\n write: (key: Record<string, unknown>, label: string, value: number, expected: unknown) => Promise<PlaceFailure | null>;\n };\n /** The rows are read again: every group, and the groups' own counts and sums. */\n done: () => Promise<unknown>;\n}\n\n/** The output name the rank is read under when the list does not show it. */\nexport const RANK = '__rank';\nconst GROUP = '__group';\n\ninterface Carried {\n id: string;\n group: number;\n}\ninterface Entry {\n id: string;\n key: Record<string, unknown>;\n title: string;\n position?: number;\n}\n\n/* Each group's rows, named by the group they came from: cached on the answer, so the same answer is the same rows. */\nconst tagged = new WeakMap<object, Array<Record<string, unknown>>>();\nfunction tag(answer: object, rows: Array<Record<string, unknown>>, group: string) {\n let out = tagged.get(answer);\n if (!out || out[0]?.[GROUP] !== group) {\n out = rows.map((row) => ({ ...row, [GROUP]: group }));\n tagged.set(answer, out);\n }\n return out;\n}\n\nexport function useGroupMoves({\n moves,\n groups,\n answers,\n columns,\n label,\n}: {\n moves: GroupMoves | undefined;\n /** Each group's key, in the order drawn. */\n groups: readonly unknown[];\n /** Each group's answer as read (its rows), or undefined while it is asked. */\n answers: ReadonlyArray<{ rows?: unknown[] } | undefined>;\n columns: readonly string[];\n /** A group's name as its header says it. */\n label: (key: unknown) => string;\n}) {\n const text = (key: unknown) => String(key ?? '');\n const zones = useRef(new Map<number, HTMLElement>());\n const refocus = useRef<string | null>(null);\n const [said, setSaid] = useState('');\n const [shut, setShut] = useState<number | null>(null);\n const live = useRef(moves);\n live.current = moves;\n\n const base = answers.flatMap((answer, i) =>\n answer?.rows ? tag(answer, answer.rows as Array<Record<string, unknown>>, text(groups[i])) : [],\n );\n const idOf = (row: Record<string, unknown>) => (moves ? (moves.keyOf(row, columns)?.id ?? null) : null);\n const optimistic = useOptimisticMoves(base, idOf, () => live.current?.done());\n const homeOf = useMemo(() => new Map(base.map((row) => [idOf(row), row[GROUP] as string])), [base]); // eslint-disable-line react-hooks/exhaustive-deps\n\n /** Each group's rows as drawn: what the moves put there, in rank order where the list keeps one, a row moved in\n from elsewhere last where it does not. */\n const bodies = groups.map((key) => {\n const own = optimistic.rows.filter((row) => row[GROUP] === text(key));\n if (moves?.rank) {\n const at = (row: Record<string, unknown>) => positionOf(row[RANK]) ?? Number.POSITIVE_INFINITY;\n return own\n .map((row, i) => [row, i] as const)\n .sort((a, b) => at(a[0]) - at(b[0]) || a[1] - b[1])\n .map(([row]) => row);\n }\n const stayed = own.filter((row) => homeOf.get(idOf(row)) === text(key));\n return [...stayed, ...own.filter((row) => homeOf.get(idOf(row)) !== text(key))];\n });\n /** How many rows a move took into (positive) or out of a group, for its header's count until it is read again. */\n const drift = (i: number) => {\n let by = 0;\n if (!moves || optimistic.rows === base) return by;\n for (const row of optimistic.rows) {\n const id = idOf(row);\n if (id === null) continue;\n const home = homeOf.get(id);\n if (row[GROUP] === text(groups[i]) && home !== text(groups[i])) by++;\n else if (row[GROUP] !== text(groups[i]) && home === text(groups[i])) by--;\n }\n return by;\n };\n\n const entriesOf = (i: number): Entry[] =>\n bodies[i].flatMap((row) => {\n const named = moves?.keyOf(row, columns);\n if (!named) return [];\n const position = positionOf(row[RANK]);\n return [{ id: named.id, key: named.key, title: moves!.labelOf(row, columns), ...(position === undefined ? {} : { position }) }];\n });\n const rowOf = (id: string) => optimistic.rows.find((row) => idOf(row) === id);\n\n /* A moved row is a new element in its new group; the focus follows it there. */\n const itemOf = (id: string) => {\n for (const zone of zones.current.values())\n for (const item of zone.querySelectorAll<HTMLElement>('tr[data-row-key]')) if (item.dataset.rowKey === id) return item;\n return null;\n };\n useEffect(() => {\n const item = refocus.current ? itemOf(refocus.current) : null;\n if (item) {\n item.focus();\n refocus.current = null;\n }\n });\n /* The grid draws its own rows; a row that can move is a Tab stop, with its keys said. */\n useEffect(() => {\n for (const zone of zones.current.values())\n for (const item of zone.querySelectorAll<HTMLElement>('tr[data-row-key]')) {\n if (moves) {\n item.tabIndex = 0;\n item.setAttribute('aria-keyshortcuts', 'Alt+ArrowUp Alt+ArrowDown');\n item.setAttribute('data-draggable', 'true');\n // A row whose move is being saved is quiet, as a board's card is (TriageBoard.css `is-saving`).\n const pending = optimistic.pending(item.dataset.rowKey ?? '');\n item.style.opacity = pending ? '0.7' : '';\n if (pending) item.setAttribute('aria-busy', 'true');\n else item.removeAttribute('aria-busy');\n }\n }\n });\n\n /** Move a row into group `to` at `index` among the rows already there (its end where there is no hand order). */\n const move = (id: string, from: number, to: number, index?: number) => {\n const row = rowOf(id);\n const named = row && moves?.keyOf(row, columns);\n if (!moves || !row || !named) return;\n const title = moves.labelOf(row, columns);\n const group = label(groups[to]);\n if (from !== to && !moves.enters(groups[to])) {\n toast(say('cannotEnter', { title, group }), 'error');\n return;\n }\n const others = entriesOf(to).filter((entry) => entry.id !== id);\n const entry: Entry = { id, key: named.key, title, ...(positionOf(row[RANK]) === undefined ? {} : { position: positionOf(row[RANK]) }) };\n const ranked = moves.rank && index !== undefined ? placements(entry, others, index, moves.rank.whole) : [];\n if (from === to && !ranked.length) return;\n const writes: Array<readonly [string, Record<string, unknown>]> = [];\n if (from !== to) writes.push([id, { [GROUP]: text(groups[to]) }]);\n for (const [each, value] of ranked) writes.push([each.id, { [RANK]: value }]);\n const token = optimistic.begin(writes);\n refocus.current = id;\n const place = index === undefined ? undefined : Math.min(index, others.length) + 1;\n setSaid(\n place !== undefined && moves.rank\n ? say('movedToPlace', { title, index: place, count: others.length + 1, group })\n : say('movedToGroup', { title, group }),\n );\n optimistic.enqueue(async () => {\n try {\n if (from !== to) {\n const refused = await moves.regroup(named.key, title, groups[from], groups[to]);\n if (refused !== null) {\n optimistic.undo(token);\n const sentence = say('wentBack', { title, group: label(groups[from]), reason: refused || say('notMoved') });\n // '' is a refusal its command's own pipeline has said already; once is enough on screen.\n if (refused) toast(sentence, 'error');\n setSaid(sentence);\n return;\n }\n }\n for (const [each, value] of ranked) {\n const failed = await moves.rank!.write(each.key, each.title, value, each.position ?? null);\n if (failed) {\n // The row's group is moved; its place is what the read says, once the queue is done.\n if (failed.refused) toast(failed.message, 'error');\n break;\n }\n }\n optimistic.saved(token);\n } catch (error) {\n optimistic.undo(token);\n throw error;\n }\n });\n };\n\n /* Where a drop lands: the group under the pointer, and in a ranked one before the row in whose upper half it is. */\n const groupOf = (zone: HTMLElement | null | undefined) => {\n for (const [i, each] of zones.current) if (each === zone) return i;\n return undefined;\n };\n const landing = (carried: Carried, at: DropPoint | null) => {\n const to = groupOf(at?.zone);\n if (to === undefined || !at) return undefined;\n if (to !== carried.group && !moves?.enters(groups[to])) return { to, shut: true as const };\n if (!moves?.rank) return to === carried.group ? undefined : { to, before: null as HTMLElement | null };\n const rows = [...at.zone.querySelectorAll<HTMLElement>('tr[data-row-key]')].filter((item) => item.dataset.rowKey !== carried.id);\n let before: HTMLElement | null = null;\n if (at.item) {\n const i = rows.indexOf(at.item);\n before = i < 0 ? null : at.upper ? at.item : (rows[i + 1] ?? null);\n }\n const index = before ? rows.indexOf(before) : rows.length;\n if (to === carried.group) {\n const ids = entriesOf(to).map((entry) => entry.id);\n if (ids.indexOf(carried.id) === index) return undefined;\n }\n return { to, before, index };\n };\n const drag = usePointerDrag<Carried>({\n items: 'tr[data-row-key]',\n gap: true,\n locate: (carried) => itemOf(carried.id),\n over: (carried, at): DropSlot | null => {\n const landed = landing(carried, at);\n setShut(landed && 'shut' in landed ? landed.to : null);\n if (!landed || 'shut' in landed) return null;\n return { zone: zones.current.get(landed.to)!, before: landed.before };\n },\n drop: (carried, at) => {\n setShut(null);\n const landed = landing(carried, at);\n if (!landed) return;\n if ('shut' in landed) {\n const row = rowOf(carried.id);\n toast(say('cannotEnter', { title: row && moves ? moves.labelOf(row, columns) : '', group: label(groups[landed.to]) }), 'error');\n return;\n }\n move(carried.id, carried.group, landed.to, 'index' in landed ? landed.index : undefined);\n },\n end: () => setShut(null),\n });\n\n const groupOfRow = (id: string) => bodies.findIndex((rows) => rows.some((row) => idOf(row) === id));\n /* Alt and ↑/↓: a place up or down in a ranked group, and past its edge into the group before or after. */\n const step = (id: string, by: -1 | 1) => {\n const from = groupOfRow(id);\n if (from < 0) return;\n const ids = entriesOf(from).map((entry) => entry.id);\n const at = ids.indexOf(id);\n if (moves?.rank && at + by >= 0 && at + by < ids.length) {\n move(id, from, from, at + by);\n return;\n }\n for (let to = from + by; to >= 0 && to < groups.length; to += by) {\n if (!moves?.enters(groups[to])) continue;\n move(id, from, to, moves.rank ? (by > 0 ? 0 : entriesOf(to).length) : undefined);\n return;\n }\n };\n\n return {\n movable: Boolean(moves),\n bodies,\n drift,\n pending: (id: string) => optimistic.pending(id),\n /** A group's rows element: where rows land, the shut mark while a row is carried that cannot enter. */\n zone: (i: number) =>\n moves\n ? {\n ref: (element: HTMLElement | null) => {\n if (element) zones.current.set(i, element);\n else zones.current.delete(i);\n },\n 'data-drop-zone': '',\n ...(drag.dragging && shut === i ? { 'data-shut': '', style: { opacity: 0.5 } } : {}),\n onPointerDown: (event: ReactPointerEvent<HTMLElement>) => {\n const item = (event.target as Element).closest<HTMLElement>('tr[data-row-key]');\n if (item && event.currentTarget.contains(item)) drag.start(event, { id: item.dataset.rowKey!, group: i }, item);\n },\n onKeyDown: (event: KeyboardEvent<HTMLElement>) => {\n const item = event.target as HTMLElement;\n if (!(item instanceof HTMLTableRowElement) || !item.dataset.rowKey) return;\n if (event.key === 'Enter' && !event.altKey) {\n item.click();\n return;\n }\n if (!event.altKey || event.metaKey || event.ctrlKey || event.shiftKey) return;\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return;\n event.preventDefault();\n step(item.dataset.rowKey, event.key === 'ArrowUp' ? -1 : 1);\n },\n }\n : {},\n live: moves ? (\n <div className=\"visually-hidden\" aria-live=\"polite\">\n {said}\n </div>\n ) : null,\n };\n}\n","/**\n * A related list read as groups: a header for each record its rows share (an estimate, over its financing options),\n * that group's rows under it, and a page of groups at a time. The groups are one breakdown of the same rows; each\n * group's rows are one list read narrowed to its key, asked once the groups are in. The header's facts are columns of\n * the grouped record read through the rows (`estimate_id.total`), so a header costs no read of its own, and the\n * parent's name comes with the groups (the breakdown names a reference's keys).\n *\n * Where the list says how (`moves`, groupMoves.tsx), a row is dragged into another group, or to its place in a\n * ranked one, and Alt and ↑/↓ do the same from the keyboard; the move is drawn at once and read back once.\n *\n * Nothing here moves a page that is already drawn: a page of groups turned, or a group opened, keeps what stood until\n * the answer is in (`keepPreviousData`), and a group's rows hold their height as one place.\n */\nimport { Fragment, useId, useState, type ReactNode } from 'react';\nimport { keepPreviousData, useQueries } from '@tanstack/react-query';\nimport type { ResourceClientReply } from '@mistriwork/engine/shared/resourceHttp.js';\nimport type { HttpViewDefinition } from '@mistriwork/engine/shared/resourceHttp.js';\nimport { columnDisplay, columnLabel, findTable, openRecord, state } from '../core.js';\nimport { humanName } from '@mistriwork/engine/shared/model.js';\nimport { formatNumber } from '../locale.js';\nimport { GROUP_ROWS_MOST } from '@mistriwork/engine/shared/pageModel.js';\nimport { primaryKey, type DatabaseSchema, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { groupFactPaths, groupTable } from '@mistriwork/engine/shared/pagePaths.js';\nimport type { GridData } from './Grid.js';\nimport { Icon } from './Icon.js';\nimport { Empty } from './Notes.js';\nimport { Value } from './Value.js';\nimport { usePageApi, type SlotProps } from './pageContext.js';\nimport { namedGroups } from './dashboardModel.js';\nimport { presentationColumns } from './columnPresentation.js';\nimport { groupLabel, type DisplayRule } from './valueModel.js';\nimport { say } from '../copy/record/components/PageGroups.js';\nimport { RANK, useGroupMoves, type GroupMoves } from './groupMoves.js';\n\n/** The groups as the breakdown route answers them, without the failure it may answer instead. */\nexport type Groups = Exclude<ResourceClientReply<'/api/breakdown'>, { errors: unknown }>;\n\n/** The list's own conditions with one more on every alternative: the rows of this group. */\nfunction groupView(view: HttpViewDefinition, by: string, key: unknown, limit: number): HttpViewDefinition {\n const held = (view.filter?.groups?.length ? view.filter.groups : [[]]) as Array<Array<Record<string, unknown>>>;\n const condition = key === null || key === undefined ? { path: by, op: 'isNull' } : { path: by, op: '=', value: key };\n return { ...view, filter: { groups: held.map((group) => [...group, condition]) } as HttpViewDefinition['filter'], limit };\n}\n\nexport function GroupedBody({\n section,\n groups,\n offset,\n perPage,\n onOffset,\n view,\n labelColumns,\n rowsPerGroup,\n grid,\n overall,\n moves,\n}: {\n section: SlotProps['section'];\n groups: Groups;\n offset: number;\n perPage: number;\n onOffset: (offset: number) => void;\n /** The list's own planned view, labels and keys added (PageRelated's `presentation`). */\n view: HttpViewDefinition;\n labelColumns: Record<string, string>;\n rowsPerGroup: number;\n /** One group's rows as the list draws its table: with the subtotal row and the words under it, when there are some. */\n grid: (data: GridData, totals?: Record<string, unknown>, totalsLabel?: string) => ReactNode;\n /** Every row's sums, for the foot of the whole list: the words, and each column's figure. */\n overall?: { label: string; sums: Record<string, unknown> };\n /** How a row moves to another group or its place in one; absent, the rows stay where they are. */\n moves?: GroupMoves;\n}) {\n const api = usePageApi();\n const group = section.group!;\n const heading = useId();\n const [open, setOpen] = useState<Record<string, true>>({});\n const from = findTable(section.from ?? '') as TableMeta | undefined;\n const parent = state.schema\n ? (groupTable(state.schema as DatabaseSchema, { from: section.from ?? '', group: section.group }) as TableMeta | undefined)\n : undefined;\n const [keyName] = groups.columns;\n const keyOf = (row: Record<string, unknown>) => row[keyName];\n const keyText = (row: Record<string, unknown>) => String(keyOf(row) ?? '');\n const rows = groups.rows as Array<Record<string, unknown>>;\n const facts = groupFactPaths(section);\n const allowed = parent ? undefined : presentationColumns(state.schema, from, [group.by]).get(group.by)?.allowed;\n /* \"No sprint\": the column's caption in its sentence, an acronym kept as it is (the board's rule, BoardList's `none`). */\n const caption = (columnLabel(section.from ?? '', group.by) as string | null) ?? humanName(group.by.split('.').pop()!.replace(/_id$/, ''));\n const noValue =\n group.empty ?? say('noGroupValue', { caption: /^[A-Z][a-z]/.test(caption) ? caption[0].toLowerCase() + caption.slice(1) : caption });\n const named = namedGroups(groups, keyName, (key) =>\n key === null || key === undefined\n ? noValue\n : groupLabel(columnDisplay(section.from ?? '', group.by) as DisplayRule | null, key, allowed),\n );\n const asked = useQueries({\n queries: rows.map((row) => {\n const limit = open[keyText(row)] ? GROUP_ROWS_MOST : rowsPerGroup;\n /* A ranked list reads each row's place, shown or not, for the number a moved row takes. */\n const ranked =\n moves?.rank && !view.columns?.some((column) => column.path === moves.rank!.column)\n ? { ...view, columns: [...(view.columns ?? []), { path: moves.rank.column, alias: RANK }] }\n : view;\n const narrowed = groupView(ranked, group.by, keyOf(row), limit);\n return {\n queryKey: ['page-list', narrowed, 0],\n queryFn: () => api('/api/view/run', { view: narrowed, offset: 0, count: 'none' }),\n staleTime: 0,\n placeholderData: keepPreviousData,\n };\n }),\n });\n const answers = asked.map((each) =>\n each.data && 'view' in each.data ? (each.data as { rows?: unknown[]; columns?: string[] }) : undefined,\n );\n const shownColumns = answers.find((answer) => answer?.columns)?.columns ?? [];\n const moving = useGroupMoves({ moves, groups: rows.map(keyOf), answers, columns: shownColumns, label: named });\n const things = (section.title ?? from?.name ?? '').toLocaleLowerCase();\n const first = offset + 1;\n const last = offset + rows.length;\n const more = typeof groups.total === 'number' ? offset + rows.length < groups.total : rows.length >= perPage;\n\n return (\n <div className=\"page-groups\">\n {rows.length === 0 ? (\n <Empty className=\"page-group-empty\" text={say('nothingToGroup')} />\n ) : (\n rows.map((row, i) => {\n const key = keyOf(row);\n const id = `${heading}-${i}`;\n const count = (Number(row.group_rows) || 0) + moving.drift(i);\n const answer = asked[i].data;\n const drawn = answer && 'view' in answer ? answer : undefined;\n const body = (moving.movable ? moving.bodies[i] : (drawn?.rows ?? [])) as Array<Record<string, unknown>>;\n const columns = drawn?.columns ?? [];\n const expanded = Boolean(open[keyText(row)]);\n const label = named(key);\n const capped = Math.min(count, expanded ? GROUP_ROWS_MOST : rowsPerGroup);\n /* A subtotal sums every row of the group, not only the ones drawn, and says so when they are fewer. */\n const subtotals = section.totals?.length\n ? Object.fromEntries(section.totals.map((column, n) => [column, row[`total_${n}`] ?? 0]))\n : undefined;\n const partial = count > body.length;\n const recordKey =\n parent && key !== null && key !== undefined && primaryKey(parent).length === 1\n ? { [primaryKey(parent)[0]]: key as string | number }\n : null;\n const lead = body[0];\n return (\n <div key={keyText(row) || `none-${i}`} className=\"page-group\" role=\"group\" aria-labelledby={id}>\n <header className=\"page-group-head\">\n <h3 id={id} className=\"page-group-name\">\n {recordKey && parent ? (\n <button type=\"button\" className=\"page-group-link\" onClick={() => void openRecord(parent.id, recordKey, label)}>\n {label}\n </button>\n ) : (\n label\n )}\n </h3>\n {lead && facts.length > 0 && parent && (\n <span className=\"page-group-facts\">\n {group.show!.map((column, n) => {\n const path = facts[n];\n const said = labelColumns[path] ? lead[labelColumns[path]] : undefined;\n const meta = parent.columns.find((c) => c.name === column);\n const value = lead[path];\n if (value === null || value === undefined || value === '') return null;\n return (\n <Fragment key={column}>\n <span className=\"page-group-fact\">\n {typeof said === 'string' && said !== '' ? (\n said\n ) : (\n <Value col={meta} value={value} tableId={parent.id} links={false} compact />\n )}\n </span>\n </Fragment>\n );\n })}\n </span>\n )}\n <span className=\"page-group-count\">{say('groupCount', { shown: formatNumber(capped), count: formatNumber(count) })}</span>\n {(group.sum ?? []).map((column, n) => {\n const figure = row[`group_sum_${n}`];\n if (figure === null || figure === undefined) return null;\n return (\n <span key={column} className=\"page-group-sum\">\n <Value\n col={from?.columns.find((c) => c.name === column)}\n value={figure}\n tableId={section.from}\n links={false}\n compact\n />\n </span>\n );\n })}\n </header>\n <div className=\"result-table\" id={`${id}-rows`} {...moving.zone(i)}>\n {drawn\n ? grid(\n { ...drawn, rows: body, columns } as GridData,\n subtotals && body.length ? subtotals : undefined,\n partial ? say('totalOfCount', { count: formatNumber(count) }) : undefined,\n )\n : null}\n </div>\n {count > rowsPerGroup && (\n <button\n type=\"button\"\n className=\"ghost page-group-more\"\n aria-expanded={expanded}\n aria-controls={`${id}-rows`}\n aria-label={\n expanded\n ? say('showFewerOf', { count: formatNumber(rowsPerGroup), things, group: label })\n : say('showAllOf', { count: formatNumber(count), things, group: label })\n }\n onClick={() =>\n setOpen((held) =>\n expanded\n ? Object.fromEntries(Object.entries(held).filter(([k]) => k !== keyText(row)))\n : { ...held, [keyText(row)]: true },\n )\n }\n >\n {expanded ? say('showFewer', { count: formatNumber(rowsPerGroup) }) : say('showAll', { count: formatNumber(count) })}\n </button>\n )}\n </div>\n );\n })\n )}\n {moving.live}\n {overall && from && (\n <p className=\"page-group-total\">\n <span className=\"page-group-total-label\">{overall.label}</span>\n {Object.entries(overall.sums).map(([column, figure]) => (\n <span key={column} className=\"page-group-total-figure\">\n <span className=\"page-group-total-caption\">{(columnLabel(from.id, column) as string | null) ?? humanName(column)}</span>\n <Value col={from.columns.find((c) => c.name === column)} value={figure} tableId={from.id} links={false} compact />\n </span>\n ))}\n </p>\n )}\n {(offset > 0 || more) && (\n <div className=\"page-pager\">\n <span className=\"step-control\">\n <button\n type=\"button\"\n className=\"step-button\"\n aria-label={say('previousGroups')}\n disabled={offset === 0}\n onClick={() => onOffset(Math.max(0, offset - perPage))}\n >\n <Icon name=\"caret-left\" size={16} />\n </button>\n <span className=\"page-range step-position\">\n {`${formatNumber(first)}–${formatNumber(last)}${typeof groups.total === 'number' ? say('common.ofTotal', { total: formatNumber(groups.total) }) : ''}`}\n </span>\n <button\n type=\"button\"\n className=\"step-button\"\n aria-label={say('moreGroups')}\n disabled={!more}\n onClick={() => onOffset(offset + perPage)}\n >\n <Icon name=\"caret-right\" size={16} />\n </button>\n </span>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { At as removeHostListener, Ct as addHostListener, Jt as formatNumber, M as labelColumn, gt as defineMessages, jt as shellBody, kt as portalRoot } from "./shared-DJZjSi3J.js";
|
|
2
|
-
import { L as DropItem, R as DropMenu, z as Typeahead } from "./shared-
|
|
3
|
-
import { B as toast, L as state, d as findTable, f as go, z as tableView } from "./shared-
|
|
2
|
+
import { L as DropItem, R as DropMenu, z as Typeahead } from "./shared-BHsjawAG.js";
|
|
3
|
+
import { B as toast, L as state, d as findTable, f as go, z as tableView } from "./shared-BoLbc2WD.js";
|
|
4
4
|
import { Gn as viewportScale, Wn as viewportBox } from "./shared-CRr4kJXB.js";
|
|
5
|
-
import { r as download } from "./shared-
|
|
5
|
+
import { r as download } from "./shared-Cws_z-TS.js";
|
|
6
6
|
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
7
7
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { createPortal } from "react-dom";
|
|
@@ -1428,4 +1428,4 @@ function SchemaDiagram({ expandable = true }) {
|
|
|
1428
1428
|
}
|
|
1429
1429
|
export { NeighbourhoodDiagram, SchemaDiagram };
|
|
1430
1430
|
|
|
1431
|
-
//# sourceMappingURL=shared-
|
|
1431
|
+
//# sourceMappingURL=shared-CbeujwdU.js.map
|