@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,10 +1,10 @@
|
|
|
1
1
|
import { n as __exportAll } from "./shared-C-UrYobY.js";
|
|
2
2
|
import { Z as Empty } from "./shared-poc11xxo.js";
|
|
3
|
-
import "./shared-
|
|
3
|
+
import "./shared-BoLbc2WD.js";
|
|
4
4
|
import { Wn as viewportBox } from "./shared-CRr4kJXB.js";
|
|
5
|
-
import { t as onDemand } from "./shared-
|
|
6
|
-
import { a as StackedBars, n as DonutChart, r as LineChart, t as BarChart } from "./shared-
|
|
7
|
-
import { F as EXTRA_SHAPES } from "./shared-
|
|
5
|
+
import { t as onDemand } from "./shared-B-BUF9fx.js";
|
|
6
|
+
import { a as StackedBars, n as DonutChart, r as LineChart, t as BarChart } from "./shared-DKEOMQ6l.js";
|
|
7
|
+
import { F as EXTRA_SHAPES } from "./shared-XcSKDp6N.js";
|
|
8
8
|
import { useLayoutEffect, useRef, useState } from "react";
|
|
9
9
|
import { jsx } from "react/jsx-runtime";
|
|
10
10
|
// packages/kit/src/client/components/groupOrder.ts
|
|
@@ -80,7 +80,7 @@ var ChartFigure_exports = /* @__PURE__ */ __exportAll({
|
|
|
80
80
|
ChartShapeFigure: () => ChartShapeFigure,
|
|
81
81
|
EXTRA_SHAPES: () => EXTRA_SHAPES
|
|
82
82
|
});
|
|
83
|
-
var ChartShape = onDemand(() => import("./shared-
|
|
83
|
+
var ChartShape = onDemand(() => import("./shared-BPpC08ip.js"), "ChartShape");
|
|
84
84
|
function ChartFigure({ answer, shape, limit, target, height, say, label, seriesLabel, vocabulary, seriesVocabulary, scale, seriesScale, seriesTone, tone, sayLegend, onPick, empty, emptyDetail, emptyAction, curve, total, dots, arrange }) {
|
|
85
85
|
const stacked = shape === "stacked";
|
|
86
86
|
const donut = shape === "donut";
|
|
@@ -210,4 +210,4 @@ function ChartWait({ shape, height, rows }) {
|
|
|
210
210
|
}
|
|
211
211
|
export { orderDeclared as a, declaredOrder as i, ChartFigure_exports as n, ChartShapeFigure as r, ChartFigure as t };
|
|
212
212
|
|
|
213
|
-
//# sourceMappingURL=shared-
|
|
213
|
+
//# sourceMappingURL=shared-D_SkPtJ9.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-BUp3jv-N.js","names":[],"sources":["../../../kit/src/client/components/groupOrder.ts","../../../kit/src/client/components/ChartFigure.tsx"],"sourcesContent":["/**\n * The order a chart and a breakdown draw their groups in, where the key declares one (Dashboard.tsx, ChartFigure.tsx).\n * It lives apart from dashboardModel.ts because only those on-demand parts read it: the shell's first paint carries\n * none of it, and a record page's own figures keep dashboardModel's orderGroups.\n */\n\n/**\n * Groups in the order their key declares: the values its vocabulary names first, in that order, and anything else\n * biggest first after them. A `scale` — an age in days, a priority 1 to 5, a month — runs in its own order instead: a\n * histogram sorted by its counts read 0, 1, 6, 3, 2.\n */\nexport function orderDeclared<T extends Record<string, unknown>>(\n rows: readonly T[],\n key: string,\n value: string,\n vocabulary?: readonly string[],\n scale?: boolean,\n): T[] {\n const rank = new Map((vocabulary ?? []).map((word, at) => [word, at]));\n const place = (row: T) => (row[key] == null ? undefined : rank.get(String(row[key]))) ?? Number.POSITIVE_INFINITY;\n return [...rows].sort((a, b) => {\n const first = place(a),\n second = place(b);\n if (first !== second) return first - second;\n if (scale) {\n const order = scaleOrder(a[key], b[key]);\n if (order) return order;\n }\n return (Number(b[value]) || 0) - (Number(a[value]) || 0);\n });\n}\n\n/** Two steps of a scale in its order, the empty one last: numbers by their size, anything else (a period) as text. Only\n compared, never said, so a key past 2^53 losing its last digits here moves nothing a reader can see. */\nfunction scaleOrder(a: unknown, b: unknown): number {\n if (a == null || a === '') return b == null || b === '' ? 0 : 1;\n if (b == null || b === '') return -1;\n const x = Number(a),\n y = Number(b);\n if (Number.isFinite(x) && Number.isFinite(y)) return x - y;\n return String(a) < String(b) ? -1 : String(a) > String(b) ? 1 : 0;\n}\n\n/**\n * Whether a group key is a scale — its values steps in an order of their own\n * rather than categories: a bucket of time (`month opened_on`, `hour at`), or\n * a number that is neither a key nor a reference (an age in days, a priority\n * 1 to 5, a score). An id is a number too, but 17 is not \"after\" 16. Such a\n * key keeps its order in a breakdown and a chart, and its series are drawn\n * as steps of one hue (Chart.tsx, a donut's `ordered`).\n */\nexport function isScale(\n column: { type?: string; references?: unknown; primaryKey?: boolean } | undefined,\n by: string | undefined,\n): boolean {\n if (by && /^(day|month|quarter|year|weekday|hour)\\s/i.test(by.trim())) return true;\n return Boolean(\n column && !column.references && !column.primaryKey && /int|serial|numeric|decimal|real|double|float|number/i.test(column.type ?? ''),\n );\n}\n\n/**\n * The order a group key declares, where it declares one: the values the\n * schema allows (a CHECK list, an enum, a machine's states), else the values\n * its display rule names, in the order the rule names them — a priority\n * stored as 1 to 5 and said Urgent to Planning reads in that order, not by\n * its counts — and whether it is a scale (isScale). Plain text declares\n * neither, and is read biggest first.\n */\nexport function declaredOrder(\n column: { type?: string; references?: unknown; primaryKey?: boolean; allowed?: readonly string[] } | undefined,\n rule: { values?: Record<string, string> } | null | undefined,\n by: string | undefined,\n): { vocabulary?: string[]; scale: boolean } {\n const named = rule?.values && !column?.references ? Object.keys(rule.values) : [];\n const vocabulary = column?.allowed?.length ? [...column.allowed] : named.length ? named : undefined;\n return { ...(vocabulary ? { vocabulary } : {}), scale: isScale(column, by) };\n}\n","/**\n * A breakdown's answer drawn as a chart — the one drawing a dashboard's chart\n * and a record page's chart share, so a line reads the same about a whole\n * table and about one customer. The caller asks for the groups; this measures\n * the width it has, picks the shape and says what an empty answer means.\n */\nimport { useLayoutEffect, useRef, useState, type ReactNode } from 'react';\nimport { viewportBox } from '../core.js';\nimport { BarChart, DonutChart, LineChart, StackedBars } from './Chart.js';\nimport { Empty } from './Notes.js';\nimport { orderDeclared } from './groupOrder.js';\nimport { onDemand } from './onDemand.js';\nimport type { ExtraShape } from './ChartShapes.js';\n\nexport type { ExtraShape };\n/* The shapes drawn by the part on demand: said beside the dashboard's preload (dashboardModel.ts), which asks for it early. */\nexport { EXTRA_SHAPES } from './dashboardModel.js';\nconst ChartShape = onDemand(() => import('./ChartShapes.js'), 'ChartShape');\n\ninterface Answer {\n columns?: string[];\n rows?: Array<Record<string, unknown>>;\n}\n\ninterface ChartFigureProps {\n /** The breakdown's answer: key columns first, the measure last. Absent while it loads. */\n answer: Answer | undefined;\n shape?: 'line' | 'bar' | 'donut' | 'stacked';\n limit?: number;\n target?: number;\n height?: number;\n say: (value: unknown) => string;\n /** A first key's value as its reader says it, and the second's for stacked bars. */\n label: (key: unknown) => string;\n seriesLabel?: (key: unknown) => string;\n /** The first key's vocabulary, which a donut's slices go round in. */\n vocabulary?: readonly string[];\n /** The second key's vocabulary: a line per value keeps the color that value's slice has in a donut. */\n seriesVocabulary?: readonly string[];\n /** The first key is a scale (groupOrder.ts, isScale): its groups keep its order, and a donut's slices are steps\n of one hue. */\n scale?: boolean;\n /** The second key is a scale: stacked bars' series keep its order and are steps of one hue. */\n seriesScale?: boolean;\n /** The second key's value as its display map's tone (good, warn, bad, info, neutral), for stacked bars to draw in. */\n seriesTone?: (key: string) => string | undefined;\n /** A first-key category's tone (a state's), which bars and slices over that key wear. */\n tone?: (key: unknown) => string | undefined;\n /** How stacked bars' legend says its totals, given all of them, so one rule says every figure. */\n sayLegend?: (totals: readonly number[]) => (value: unknown) => string;\n /** A slice or a bar as a way into its rows, where the key is a plain column. */\n onPick?: ((value: unknown) => void) | null;\n /** What an answer with no groups says. */\n empty: string;\n /** A second line and a way out under the empty sentence (a section over a window the reader can widen). */\n emptyDetail?: ReactNode;\n emptyAction?: ReactNode;\n /** A line's curve: smooth draws a monotone curve through its points. */\n curve?: 'smooth' | 'straight';\n /** A donut's words for the figure in its middle. */\n total?: string;\n /** How a donut's slices and a bar chart's categories are arranged: 'answer' keeps the order they were asked in (the\n page's chart asked for the key's natural order, or a column's), 'value' is biggest first whatever the key's\n vocabulary. Absent, the key's vocabulary or scale, else biggest first. */\n arrange?: 'answer' | 'value';\n /** A line with too few filled buckets to draw a line through keeps its shape and stands its points alone. */\n dots?: boolean;\n}\n\nexport function ChartFigure({\n answer,\n shape,\n limit,\n target,\n height,\n say,\n label,\n seriesLabel,\n vocabulary,\n seriesVocabulary,\n scale,\n seriesScale,\n seriesTone,\n tone,\n sayLegend,\n onPick,\n empty,\n emptyDetail,\n emptyAction,\n curve,\n total,\n dots,\n arrange,\n}: ChartFigureProps) {\n const stacked = shape === 'stacked';\n const donut = shape === 'donut';\n const bars = shape === 'bar';\n const host = useRef<HTMLDivElement>(null);\n const [wide, setWide] = useState(0);\n const columns = answer?.columns ?? [];\n /* A donut's slices go round in the order its groups are read, as a\n breakdown's bars do; so do bars over categories. Bars over periods keep\n the calendar's order. A line over a key with declared choices (a stage)\n walks them in their declared order, not the answer's alphabetical one.\n A scale (an age in days, a priority) keeps its own order everywhere. */\n const periods = (answer?.rows ?? []).every((row) => /^\\d{4}(-\\d{2}(-\\d{2})?|-Q[1-4])?$/.test(String(row[columns[0]])));\n const answered =\n arrange === 'answer' && (donut || bars)\n ? (answer?.rows ?? []).slice(0, limit)\n : arrange === 'value' && (donut || (bars && !periods))\n ? orderDeclared(answer?.rows ?? [], columns[0], columns[1]).slice(0, limit)\n : donut || (bars && !periods) || (!stacked && !periods && columns.length < 3 && ((vocabulary?.length ?? 0) > 0 || Boolean(scale)))\n ? orderDeclared(answer?.rows ?? [], columns[0], columns[1], vocabulary, scale).slice(0, limit)\n : (answer?.rows ?? []);\n /* A line over a two-key answer is a line per value of the second key. */\n const multi = !stacked && !donut && !bars && columns.length >= 3;\n const rows = answered.map((r) => ({\n key: r[columns[0]],\n value: r[columns[stacked || multi ? 2 : 1]],\n series: stacked || multi ? r[columns[1]] : undefined,\n }));\n /* The viewBox matches the width the chart actually has — measured,\n because a guess from the declared section width scales tick text\n with the monitor. By the time the data is back the host has layout. */\n useLayoutEffect(() => {\n const element = host.current;\n if (!element || !answer || !rows.length) return;\n const measure = () => setWide(Math.max(0, Math.round(viewportBox(element).width)));\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n return () => observer.disconnect();\n }, [answer]); // eslint-disable-line react-hooks/exhaustive-deps\n if (answer && !rows.length) return <Empty text={empty} detail={emptyDetail} action={emptyAction} />;\n /* Before the answer, the chart's room at about the height it will draw at, ruled as its gridlines will be: an empty\n host was nothing tall, and the chart arriving pushed every section under it down. */\n if (!answer) return <ChartWait shape={stacked ? 'rows' : donut ? 'donut' : 'plot'} height={height} rows={limit} />;\n return (\n <div className=\"chart-host\" ref={host}>\n {answer &&\n wide > 0 &&\n (donut ? (\n <DonutChart rows={rows} say={say} label={label} onPick={onPick} total={total} ordered={scale} tone={tone} />\n ) : bars ? (\n <BarChart rows={rows} say={say} label={label} wide={wide} high={height} target={target} tone={tone} />\n ) : stacked ? (\n <StackedBars\n rows={rows}\n say={say}\n target={target}\n limit={limit}\n label={label}\n seriesLabel={seriesLabel}\n onPick={onPick}\n seriesVocabulary={seriesVocabulary}\n seriesOrdered={seriesScale}\n seriesTone={seriesTone}\n sayLegend={sayLegend}\n />\n ) : (\n <LineChart\n rows={rows}\n say={say}\n wide={wide}\n high={height}\n target={target}\n smooth={curve === 'smooth'}\n dots={dots}\n seriesLabel={seriesLabel}\n seriesVocabulary={seriesVocabulary}\n onPick={onPick}\n />\n ))}\n </div>\n );\n}\n\ninterface ChartShapeFigureProps {\n shape: ExtraShape;\n answer: Answer | undefined;\n say: (value: unknown) => string;\n sayX?: (value: unknown) => string;\n names?: { x: string; y: string };\n label: (key: unknown) => string;\n seriesLabel?: (key: unknown) => string;\n vocabulary?: readonly string[];\n seriesVocabulary?: readonly string[];\n limit?: number;\n target?: number;\n height?: number;\n empty: string;\n /** A second line and a way out under the empty sentence (a section over a window the reader can widen). */\n emptyDetail?: ReactNode;\n emptyAction?: ReactNode;\n}\n\n/** One of the further shapes, measured as the others are and drawn by the part on demand. */\nexport function ChartShapeFigure({ answer, empty, emptyDetail, emptyAction, height, ...rest }: ChartShapeFigureProps) {\n const host = useRef<HTMLDivElement>(null);\n const [wide, setWide] = useState(0);\n const rows = answer?.rows ?? [];\n useLayoutEffect(() => {\n const element = host.current;\n if (!element || !answer || !rows.length) return;\n const measure = () => setWide(Math.max(0, Math.round(viewportBox(element).width)));\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n return () => observer.disconnect();\n }, [answer]); // eslint-disable-line react-hooks/exhaustive-deps\n if (answer && !rows.length) return <Empty text={empty} detail={emptyDetail} action={emptyAction} />;\n if (!answer) return <ChartWait shape=\"plot\" height={height} />;\n return (\n <div className=\"chart-host\" ref={host}>\n {answer && wide > 0 && <ChartShape {...rest} answer={answer} wide={wide} high={height} />}\n </div>\n );\n}\n\n/**\n * A chart's room while it is read: a plot at the line's own measure (a third of its width, 160px to 240px, or the\n * section's `height`), a stack at a row a group, a donut at its disc. Quiet gridlines and no sweep: a chart is a\n * figure, and a moving placeholder in it read as data dancing (docs/design.md, Motion, rule 8).\n */\nfunction ChartWait({ shape, height, rows }: { shape: 'plot' | 'rows' | 'donut'; height?: number; rows?: number }) {\n const style =\n shape === 'rows'\n ? { height: `${Math.min(Math.max(rows ?? 4, 2), 8) * 30 + 32}px` }\n : shape === 'donut'\n ? { height: '200px' }\n : height\n ? { height: `${Math.max(120, Math.round(height))}px` }\n : undefined;\n return <div className={`chart-host chart-wait chart-wait-${shape}`} style={style} role=\"status\" aria-busy=\"true\" />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAWA,SAAgB,cACd,MACA,KACA,OACA,YACA,OACK;CACL,MAAM,OAAO,IAAI,KAAK,cAAc,CAAC,EAAA,CAAG,KAAK,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACrE,MAAM,SAAS,SAAY,IAAI,QAAQ,OAAO,KAAA,IAAY,KAAK,IAAI,OAAO,IAAI,IAAI,CAAC,MAAM,OAAO;CAChG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM;EAC9B,MAAM,QAAQ,MAAM,CAAC,GACnB,SAAS,MAAM,CAAC;EAClB,IAAI,UAAU,QAAQ,OAAO,QAAQ;EACrC,IAAI,OAAO;GACT,MAAM,QAAQ,WAAW,EAAE,MAAM,EAAE,IAAI;GACvC,IAAI,OAAO,OAAO;EACpB;EACA,QAAQ,OAAO,EAAE,MAAM,KAAK,MAAM,OAAO,EAAE,MAAM,KAAK;CACxD,CAAC;AACH;;;AAIA,SAAS,WAAW,GAAY,GAAoB;CAClD,IAAI,KAAK,QAAQ,MAAM,IAAI,OAAO,KAAK,QAAQ,MAAM,KAAK,IAAI;CAC9D,IAAI,KAAK,QAAQ,MAAM,IAAI,OAAO;CAClC,MAAM,IAAI,OAAO,CAAC,GAChB,IAAI,OAAO,CAAC;CACd,IAAI,OAAO,SAAS,CAAC,KAAK,OAAO,SAAS,CAAC,GAAG,OAAO,IAAI;CACzD,OAAO,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,IAAI;AAClE;;;;;;;;;AAUA,SAAgB,QACd,QACA,IACS;CACT,IAAI,MAAM,4CAA4C,KAAK,GAAG,KAAK,CAAC,GAAG,OAAO;CAC9E,OAAO,QACL,UAAU,CAAC,OAAO,cAAc,CAAC,OAAO,cAAc,uDAAuD,KAAK,OAAO,QAAQ,EAAE,CACrI;AACF;;;;;;;;;AAUA,SAAgB,cACd,QACA,MACA,IAC2C;CAC3C,MAAM,QAAQ,MAAM,UAAU,CAAC,QAAQ,aAAa,OAAO,KAAK,KAAK,MAAM,IAAI,CAAC;CAChF,MAAM,aAAa,QAAQ,SAAS,SAAS,CAAC,GAAG,OAAO,OAAO,IAAI,MAAM,SAAS,QAAQ,KAAA;CAC1F,OAAO;EAAE,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;EAAI,OAAO,QAAQ,QAAQ,EAAE;CAAE;AAC7E;;;;;;;;;;;;;AC5DA,IAAM,aAAa,eAAe,OAAO,yBAAqB,YAAY;AAoD1E,SAAgB,YAAY,EAC1B,QACA,OACA,OACA,QACA,QACA,KACA,OACA,aACA,YACA,kBACA,OACA,aACA,YACA,MACA,WACA,QACA,OACA,aACA,aACA,OACA,OACA,MACA,WACmB;CACnB,MAAM,UAAU,UAAU;CAC1B,MAAM,QAAQ,UAAU;CACxB,MAAM,OAAO,UAAU;CACvB,MAAM,OAAO,OAAuB,IAAI;CACxC,MAAM,CAAC,MAAM,WAAW,SAAS,CAAC;CAClC,MAAM,UAAU,QAAQ,WAAW,CAAC;CAMpC,MAAM,WAAW,QAAQ,QAAQ,CAAC,EAAA,CAAG,OAAO,QAAQ,oCAAoC,KAAK,OAAO,IAAI,QAAQ,GAAG,CAAC,CAAC;CACrH,MAAM,WACJ,YAAY,aAAa,SAAS,SAC7B,QAAQ,QAAQ,CAAC,EAAA,CAAG,MAAM,GAAG,KAAK,IACnC,YAAY,YAAY,SAAU,QAAQ,CAAC,WACzC,cAAc,QAAQ,QAAQ,CAAC,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC,MAAM,GAAG,KAAK,IACxE,SAAU,QAAQ,CAAC,WAAa,CAAC,WAAW,CAAC,WAAW,QAAQ,SAAS,OAAO,YAAY,UAAU,KAAK,KAAK,QAAQ,KAAK,KAC3H,cAAc,QAAQ,QAAQ,CAAC,GAAG,QAAQ,IAAI,QAAQ,IAAI,YAAY,KAAK,CAAC,CAAC,MAAM,GAAG,KAAK,IAC1F,QAAQ,QAAQ,CAAC;CAE5B,MAAM,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC,QAAQ,QAAQ,UAAU;CAC/D,MAAM,OAAO,SAAS,KAAK,OAAO;EAChC,KAAK,EAAE,QAAQ;EACf,OAAO,EAAE,QAAQ,WAAW,QAAQ,IAAI;EACxC,QAAQ,WAAW,QAAQ,EAAE,QAAQ,MAAM,KAAA;CAC7C,EAAE;CAIF,sBAAsB;EACpB,MAAM,UAAU,KAAK;EACrB,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,QAAQ;EACzC,MAAM,gBAAgB,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;EACjF,QAAQ;EACR,MAAM,WAAW,IAAI,eAAe,OAAO;EAC3C,SAAS,QAAQ,OAAO;EACxB,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,MAAM,CAAC;CACX,IAAI,UAAU,CAAC,KAAK,QAAQ,OAAO,oBAAC,OAAD;EAAO,MAAM;EAAO,QAAQ;EAAa,QAAQ;CAAc,CAAA;CAGlG,IAAI,CAAC,QAAQ,OAAO,oBAAC,WAAD;EAAW,OAAO,UAAU,SAAS,QAAQ,UAAU;EAAgB;EAAQ,MAAM;CAAQ,CAAA;CACjH,OACE,oBAAC,OAAD;EAAK,WAAU;EAAa,KAAK;EAC9B,UAAA,UACC,OAAO,MACN,QACC,oBAAC,YAAD;GAAkB;GAAW;GAAY;GAAe;GAAe;GAAO,SAAS;GAAa;EAAO,CAAA,IACzG,OACF,oBAAC,UAAD;GAAgB;GAAW;GAAY;GAAa;GAAM,MAAM;GAAgB;GAAc;EAAO,CAAA,IACnG,UACF,oBAAC,aAAD;GACQ;GACD;GACG;GACD;GACA;GACM;GACL;GACU;GAClB,eAAe;GACH;GACD;EACZ,CAAA,IAED,oBAAC,WAAD;GACQ;GACD;GACC;GACN,MAAM;GACE;GACR,QAAQ,UAAU;GACZ;GACO;GACK;GACV;EACT,CAAA;CAEF,CAAA;AAET;;AAsBA,SAAgB,iBAAiB,EAAE,QAAQ,OAAO,aAAa,aAAa,QAAQ,GAAG,QAA+B;CACpH,MAAM,OAAO,OAAuB,IAAI;CACxC,MAAM,CAAC,MAAM,WAAW,SAAS,CAAC;CAClC,MAAM,OAAO,QAAQ,QAAQ,CAAC;CAC9B,sBAAsB;EACpB,MAAM,UAAU,KAAK;EACrB,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,QAAQ;EACzC,MAAM,gBAAgB,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;EACjF,QAAQ;EACR,MAAM,WAAW,IAAI,eAAe,OAAO;EAC3C,SAAS,QAAQ,OAAO;EACxB,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,MAAM,CAAC;CACX,IAAI,UAAU,CAAC,KAAK,QAAQ,OAAO,oBAAC,OAAD;EAAO,MAAM;EAAO,QAAQ;EAAa,QAAQ;CAAc,CAAA;CAClG,IAAI,CAAC,QAAQ,OAAO,oBAAC,WAAD;EAAW,OAAM;EAAe;CAAS,CAAA;CAC7D,OACE,oBAAC,OAAD;EAAK,WAAU;EAAa,KAAK;EAC9B,UAAA,UAAU,OAAO,KAAK,oBAAC,YAAD;GAAY,GAAI;GAAc;GAAc;GAAM,MAAM;EAAS,CAAA;CACrF,CAAA;AAET;;;;;;AAOA,SAAS,UAAU,EAAE,OAAO,QAAQ,QAA8E;CAChH,MAAM,QACJ,UAAU,SACN,EAAE,QAAQ,GAAG,KAAK,IAAI,KAAK,IAAI,QAAQ,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,IAAI,IAC/D,UAAU,UACR,EAAE,QAAQ,QAAQ,IAClB,SACE,EAAE,QAAQ,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,MAAM,CAAC,EAAE,IAAI,IACnD,KAAA;CACV,OAAO,oBAAC,OAAD;EAAK,WAAW,oCAAoC;EAAgB;EAAO,MAAK;EAAS,aAAU;CAAQ,CAAA;AACpH"}
|
|
1
|
+
{"version":3,"file":"shared-D_SkPtJ9.js","names":[],"sources":["../../../kit/src/client/components/groupOrder.ts","../../../kit/src/client/components/ChartFigure.tsx"],"sourcesContent":["/**\n * The order a chart and a breakdown draw their groups in, where the key declares one (Dashboard.tsx, ChartFigure.tsx).\n * It lives apart from dashboardModel.ts because only those on-demand parts read it: the shell's first paint carries\n * none of it, and a record page's own figures keep dashboardModel's orderGroups.\n */\n\n/**\n * Groups in the order their key declares: the values its vocabulary names first, in that order, and anything else\n * biggest first after them. A `scale` — an age in days, a priority 1 to 5, a month — runs in its own order instead: a\n * histogram sorted by its counts read 0, 1, 6, 3, 2.\n */\nexport function orderDeclared<T extends Record<string, unknown>>(\n rows: readonly T[],\n key: string,\n value: string,\n vocabulary?: readonly string[],\n scale?: boolean,\n): T[] {\n const rank = new Map((vocabulary ?? []).map((word, at) => [word, at]));\n const place = (row: T) => (row[key] == null ? undefined : rank.get(String(row[key]))) ?? Number.POSITIVE_INFINITY;\n return [...rows].sort((a, b) => {\n const first = place(a),\n second = place(b);\n if (first !== second) return first - second;\n if (scale) {\n const order = scaleOrder(a[key], b[key]);\n if (order) return order;\n }\n return (Number(b[value]) || 0) - (Number(a[value]) || 0);\n });\n}\n\n/** Two steps of a scale in its order, the empty one last: numbers by their size, anything else (a period) as text. Only\n compared, never said, so a key past 2^53 losing its last digits here moves nothing a reader can see. */\nfunction scaleOrder(a: unknown, b: unknown): number {\n if (a == null || a === '') return b == null || b === '' ? 0 : 1;\n if (b == null || b === '') return -1;\n const x = Number(a),\n y = Number(b);\n if (Number.isFinite(x) && Number.isFinite(y)) return x - y;\n return String(a) < String(b) ? -1 : String(a) > String(b) ? 1 : 0;\n}\n\n/**\n * Whether a group key is a scale — its values steps in an order of their own\n * rather than categories: a bucket of time (`month opened_on`, `hour at`), or\n * a number that is neither a key nor a reference (an age in days, a priority\n * 1 to 5, a score). An id is a number too, but 17 is not \"after\" 16. Such a\n * key keeps its order in a breakdown and a chart, and its series are drawn\n * as steps of one hue (Chart.tsx, a donut's `ordered`).\n */\nexport function isScale(\n column: { type?: string; references?: unknown; primaryKey?: boolean } | undefined,\n by: string | undefined,\n): boolean {\n if (by && /^(day|month|quarter|year|weekday|hour)\\s/i.test(by.trim())) return true;\n return Boolean(\n column && !column.references && !column.primaryKey && /int|serial|numeric|decimal|real|double|float|number/i.test(column.type ?? ''),\n );\n}\n\n/**\n * The order a group key declares, where it declares one: the values the\n * schema allows (a CHECK list, an enum, a machine's states), else the values\n * its display rule names, in the order the rule names them — a priority\n * stored as 1 to 5 and said Urgent to Planning reads in that order, not by\n * its counts — and whether it is a scale (isScale). Plain text declares\n * neither, and is read biggest first.\n */\nexport function declaredOrder(\n column: { type?: string; references?: unknown; primaryKey?: boolean; allowed?: readonly string[] } | undefined,\n rule: { values?: Record<string, string> } | null | undefined,\n by: string | undefined,\n): { vocabulary?: string[]; scale: boolean } {\n const named = rule?.values && !column?.references ? Object.keys(rule.values) : [];\n const vocabulary = column?.allowed?.length ? [...column.allowed] : named.length ? named : undefined;\n return { ...(vocabulary ? { vocabulary } : {}), scale: isScale(column, by) };\n}\n","/**\n * A breakdown's answer drawn as a chart — the one drawing a dashboard's chart\n * and a record page's chart share, so a line reads the same about a whole\n * table and about one customer. The caller asks for the groups; this measures\n * the width it has, picks the shape and says what an empty answer means.\n */\nimport { useLayoutEffect, useRef, useState, type ReactNode } from 'react';\nimport { viewportBox } from '../core.js';\nimport { BarChart, DonutChart, LineChart, StackedBars } from './Chart.js';\nimport { Empty } from './Notes.js';\nimport { orderDeclared } from './groupOrder.js';\nimport { onDemand } from './onDemand.js';\nimport type { ExtraShape } from './ChartShapes.js';\n\nexport type { ExtraShape };\n/* The shapes drawn by the part on demand: said beside the dashboard's preload (dashboardModel.ts), which asks for it early. */\nexport { EXTRA_SHAPES } from './dashboardModel.js';\nconst ChartShape = onDemand(() => import('./ChartShapes.js'), 'ChartShape');\n\ninterface Answer {\n columns?: string[];\n rows?: Array<Record<string, unknown>>;\n}\n\ninterface ChartFigureProps {\n /** The breakdown's answer: key columns first, the measure last. Absent while it loads. */\n answer: Answer | undefined;\n shape?: 'line' | 'bar' | 'donut' | 'stacked';\n limit?: number;\n target?: number;\n height?: number;\n say: (value: unknown) => string;\n /** A first key's value as its reader says it, and the second's for stacked bars. */\n label: (key: unknown) => string;\n seriesLabel?: (key: unknown) => string;\n /** The first key's vocabulary, which a donut's slices go round in. */\n vocabulary?: readonly string[];\n /** The second key's vocabulary: a line per value keeps the color that value's slice has in a donut. */\n seriesVocabulary?: readonly string[];\n /** The first key is a scale (groupOrder.ts, isScale): its groups keep its order, and a donut's slices are steps\n of one hue. */\n scale?: boolean;\n /** The second key is a scale: stacked bars' series keep its order and are steps of one hue. */\n seriesScale?: boolean;\n /** The second key's value as its display map's tone (good, warn, bad, info, neutral), for stacked bars to draw in. */\n seriesTone?: (key: string) => string | undefined;\n /** A first-key category's tone (a state's), which bars and slices over that key wear. */\n tone?: (key: unknown) => string | undefined;\n /** How stacked bars' legend says its totals, given all of them, so one rule says every figure. */\n sayLegend?: (totals: readonly number[]) => (value: unknown) => string;\n /** A slice or a bar as a way into its rows, where the key is a plain column. */\n onPick?: ((value: unknown) => void) | null;\n /** What an answer with no groups says. */\n empty: string;\n /** A second line and a way out under the empty sentence (a section over a window the reader can widen). */\n emptyDetail?: ReactNode;\n emptyAction?: ReactNode;\n /** A line's curve: smooth draws a monotone curve through its points. */\n curve?: 'smooth' | 'straight';\n /** A donut's words for the figure in its middle. */\n total?: string;\n /** How a donut's slices and a bar chart's categories are arranged: 'answer' keeps the order they were asked in (the\n page's chart asked for the key's natural order, or a column's), 'value' is biggest first whatever the key's\n vocabulary. Absent, the key's vocabulary or scale, else biggest first. */\n arrange?: 'answer' | 'value';\n /** A line with too few filled buckets to draw a line through keeps its shape and stands its points alone. */\n dots?: boolean;\n}\n\nexport function ChartFigure({\n answer,\n shape,\n limit,\n target,\n height,\n say,\n label,\n seriesLabel,\n vocabulary,\n seriesVocabulary,\n scale,\n seriesScale,\n seriesTone,\n tone,\n sayLegend,\n onPick,\n empty,\n emptyDetail,\n emptyAction,\n curve,\n total,\n dots,\n arrange,\n}: ChartFigureProps) {\n const stacked = shape === 'stacked';\n const donut = shape === 'donut';\n const bars = shape === 'bar';\n const host = useRef<HTMLDivElement>(null);\n const [wide, setWide] = useState(0);\n const columns = answer?.columns ?? [];\n /* A donut's slices go round in the order its groups are read, as a\n breakdown's bars do; so do bars over categories. Bars over periods keep\n the calendar's order. A line over a key with declared choices (a stage)\n walks them in their declared order, not the answer's alphabetical one.\n A scale (an age in days, a priority) keeps its own order everywhere. */\n const periods = (answer?.rows ?? []).every((row) => /^\\d{4}(-\\d{2}(-\\d{2})?|-Q[1-4])?$/.test(String(row[columns[0]])));\n const answered =\n arrange === 'answer' && (donut || bars)\n ? (answer?.rows ?? []).slice(0, limit)\n : arrange === 'value' && (donut || (bars && !periods))\n ? orderDeclared(answer?.rows ?? [], columns[0], columns[1]).slice(0, limit)\n : donut || (bars && !periods) || (!stacked && !periods && columns.length < 3 && ((vocabulary?.length ?? 0) > 0 || Boolean(scale)))\n ? orderDeclared(answer?.rows ?? [], columns[0], columns[1], vocabulary, scale).slice(0, limit)\n : (answer?.rows ?? []);\n /* A line over a two-key answer is a line per value of the second key. */\n const multi = !stacked && !donut && !bars && columns.length >= 3;\n const rows = answered.map((r) => ({\n key: r[columns[0]],\n value: r[columns[stacked || multi ? 2 : 1]],\n series: stacked || multi ? r[columns[1]] : undefined,\n }));\n /* The viewBox matches the width the chart actually has — measured,\n because a guess from the declared section width scales tick text\n with the monitor. By the time the data is back the host has layout. */\n useLayoutEffect(() => {\n const element = host.current;\n if (!element || !answer || !rows.length) return;\n const measure = () => setWide(Math.max(0, Math.round(viewportBox(element).width)));\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n return () => observer.disconnect();\n }, [answer]); // eslint-disable-line react-hooks/exhaustive-deps\n if (answer && !rows.length) return <Empty text={empty} detail={emptyDetail} action={emptyAction} />;\n /* Before the answer, the chart's room at about the height it will draw at, ruled as its gridlines will be: an empty\n host was nothing tall, and the chart arriving pushed every section under it down. */\n if (!answer) return <ChartWait shape={stacked ? 'rows' : donut ? 'donut' : 'plot'} height={height} rows={limit} />;\n return (\n <div className=\"chart-host\" ref={host}>\n {answer &&\n wide > 0 &&\n (donut ? (\n <DonutChart rows={rows} say={say} label={label} onPick={onPick} total={total} ordered={scale} tone={tone} />\n ) : bars ? (\n <BarChart rows={rows} say={say} label={label} wide={wide} high={height} target={target} tone={tone} />\n ) : stacked ? (\n <StackedBars\n rows={rows}\n say={say}\n target={target}\n limit={limit}\n label={label}\n seriesLabel={seriesLabel}\n onPick={onPick}\n seriesVocabulary={seriesVocabulary}\n seriesOrdered={seriesScale}\n seriesTone={seriesTone}\n sayLegend={sayLegend}\n />\n ) : (\n <LineChart\n rows={rows}\n say={say}\n wide={wide}\n high={height}\n target={target}\n smooth={curve === 'smooth'}\n dots={dots}\n seriesLabel={seriesLabel}\n seriesVocabulary={seriesVocabulary}\n onPick={onPick}\n />\n ))}\n </div>\n );\n}\n\ninterface ChartShapeFigureProps {\n shape: ExtraShape;\n answer: Answer | undefined;\n say: (value: unknown) => string;\n sayX?: (value: unknown) => string;\n names?: { x: string; y: string };\n label: (key: unknown) => string;\n seriesLabel?: (key: unknown) => string;\n vocabulary?: readonly string[];\n seriesVocabulary?: readonly string[];\n limit?: number;\n target?: number;\n height?: number;\n empty: string;\n /** A second line and a way out under the empty sentence (a section over a window the reader can widen). */\n emptyDetail?: ReactNode;\n emptyAction?: ReactNode;\n}\n\n/** One of the further shapes, measured as the others are and drawn by the part on demand. */\nexport function ChartShapeFigure({ answer, empty, emptyDetail, emptyAction, height, ...rest }: ChartShapeFigureProps) {\n const host = useRef<HTMLDivElement>(null);\n const [wide, setWide] = useState(0);\n const rows = answer?.rows ?? [];\n useLayoutEffect(() => {\n const element = host.current;\n if (!element || !answer || !rows.length) return;\n const measure = () => setWide(Math.max(0, Math.round(viewportBox(element).width)));\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n return () => observer.disconnect();\n }, [answer]); // eslint-disable-line react-hooks/exhaustive-deps\n if (answer && !rows.length) return <Empty text={empty} detail={emptyDetail} action={emptyAction} />;\n if (!answer) return <ChartWait shape=\"plot\" height={height} />;\n return (\n <div className=\"chart-host\" ref={host}>\n {answer && wide > 0 && <ChartShape {...rest} answer={answer} wide={wide} high={height} />}\n </div>\n );\n}\n\n/**\n * A chart's room while it is read: a plot at the line's own measure (a third of its width, 160px to 240px, or the\n * section's `height`), a stack at a row a group, a donut at its disc. Quiet gridlines and no sweep: a chart is a\n * figure, and a moving placeholder in it read as data dancing (docs/design.md, Motion, rule 8).\n */\nfunction ChartWait({ shape, height, rows }: { shape: 'plot' | 'rows' | 'donut'; height?: number; rows?: number }) {\n const style =\n shape === 'rows'\n ? { height: `${Math.min(Math.max(rows ?? 4, 2), 8) * 30 + 32}px` }\n : shape === 'donut'\n ? { height: '200px' }\n : height\n ? { height: `${Math.max(120, Math.round(height))}px` }\n : undefined;\n return <div className={`chart-host chart-wait chart-wait-${shape}`} style={style} role=\"status\" aria-busy=\"true\" />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAWA,SAAgB,cACd,MACA,KACA,OACA,YACA,OACK;CACL,MAAM,OAAO,IAAI,KAAK,cAAc,CAAC,EAAA,CAAG,KAAK,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACrE,MAAM,SAAS,SAAY,IAAI,QAAQ,OAAO,KAAA,IAAY,KAAK,IAAI,OAAO,IAAI,IAAI,CAAC,MAAM,OAAO;CAChG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM;EAC9B,MAAM,QAAQ,MAAM,CAAC,GACnB,SAAS,MAAM,CAAC;EAClB,IAAI,UAAU,QAAQ,OAAO,QAAQ;EACrC,IAAI,OAAO;GACT,MAAM,QAAQ,WAAW,EAAE,MAAM,EAAE,IAAI;GACvC,IAAI,OAAO,OAAO;EACpB;EACA,QAAQ,OAAO,EAAE,MAAM,KAAK,MAAM,OAAO,EAAE,MAAM,KAAK;CACxD,CAAC;AACH;;;AAIA,SAAS,WAAW,GAAY,GAAoB;CAClD,IAAI,KAAK,QAAQ,MAAM,IAAI,OAAO,KAAK,QAAQ,MAAM,KAAK,IAAI;CAC9D,IAAI,KAAK,QAAQ,MAAM,IAAI,OAAO;CAClC,MAAM,IAAI,OAAO,CAAC,GAChB,IAAI,OAAO,CAAC;CACd,IAAI,OAAO,SAAS,CAAC,KAAK,OAAO,SAAS,CAAC,GAAG,OAAO,IAAI;CACzD,OAAO,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,IAAI;AAClE;;;;;;;;;AAUA,SAAgB,QACd,QACA,IACS;CACT,IAAI,MAAM,4CAA4C,KAAK,GAAG,KAAK,CAAC,GAAG,OAAO;CAC9E,OAAO,QACL,UAAU,CAAC,OAAO,cAAc,CAAC,OAAO,cAAc,uDAAuD,KAAK,OAAO,QAAQ,EAAE,CACrI;AACF;;;;;;;;;AAUA,SAAgB,cACd,QACA,MACA,IAC2C;CAC3C,MAAM,QAAQ,MAAM,UAAU,CAAC,QAAQ,aAAa,OAAO,KAAK,KAAK,MAAM,IAAI,CAAC;CAChF,MAAM,aAAa,QAAQ,SAAS,SAAS,CAAC,GAAG,OAAO,OAAO,IAAI,MAAM,SAAS,QAAQ,KAAA;CAC1F,OAAO;EAAE,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;EAAI,OAAO,QAAQ,QAAQ,EAAE;CAAE;AAC7E;;;;;;;;;;;;;AC5DA,IAAM,aAAa,eAAe,OAAO,yBAAqB,YAAY;AAoD1E,SAAgB,YAAY,EAC1B,QACA,OACA,OACA,QACA,QACA,KACA,OACA,aACA,YACA,kBACA,OACA,aACA,YACA,MACA,WACA,QACA,OACA,aACA,aACA,OACA,OACA,MACA,WACmB;CACnB,MAAM,UAAU,UAAU;CAC1B,MAAM,QAAQ,UAAU;CACxB,MAAM,OAAO,UAAU;CACvB,MAAM,OAAO,OAAuB,IAAI;CACxC,MAAM,CAAC,MAAM,WAAW,SAAS,CAAC;CAClC,MAAM,UAAU,QAAQ,WAAW,CAAC;CAMpC,MAAM,WAAW,QAAQ,QAAQ,CAAC,EAAA,CAAG,OAAO,QAAQ,oCAAoC,KAAK,OAAO,IAAI,QAAQ,GAAG,CAAC,CAAC;CACrH,MAAM,WACJ,YAAY,aAAa,SAAS,SAC7B,QAAQ,QAAQ,CAAC,EAAA,CAAG,MAAM,GAAG,KAAK,IACnC,YAAY,YAAY,SAAU,QAAQ,CAAC,WACzC,cAAc,QAAQ,QAAQ,CAAC,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC,MAAM,GAAG,KAAK,IACxE,SAAU,QAAQ,CAAC,WAAa,CAAC,WAAW,CAAC,WAAW,QAAQ,SAAS,OAAO,YAAY,UAAU,KAAK,KAAK,QAAQ,KAAK,KAC3H,cAAc,QAAQ,QAAQ,CAAC,GAAG,QAAQ,IAAI,QAAQ,IAAI,YAAY,KAAK,CAAC,CAAC,MAAM,GAAG,KAAK,IAC1F,QAAQ,QAAQ,CAAC;CAE5B,MAAM,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC,QAAQ,QAAQ,UAAU;CAC/D,MAAM,OAAO,SAAS,KAAK,OAAO;EAChC,KAAK,EAAE,QAAQ;EACf,OAAO,EAAE,QAAQ,WAAW,QAAQ,IAAI;EACxC,QAAQ,WAAW,QAAQ,EAAE,QAAQ,MAAM,KAAA;CAC7C,EAAE;CAIF,sBAAsB;EACpB,MAAM,UAAU,KAAK;EACrB,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,QAAQ;EACzC,MAAM,gBAAgB,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;EACjF,QAAQ;EACR,MAAM,WAAW,IAAI,eAAe,OAAO;EAC3C,SAAS,QAAQ,OAAO;EACxB,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,MAAM,CAAC;CACX,IAAI,UAAU,CAAC,KAAK,QAAQ,OAAO,oBAAC,OAAD;EAAO,MAAM;EAAO,QAAQ;EAAa,QAAQ;CAAc,CAAA;CAGlG,IAAI,CAAC,QAAQ,OAAO,oBAAC,WAAD;EAAW,OAAO,UAAU,SAAS,QAAQ,UAAU;EAAgB;EAAQ,MAAM;CAAQ,CAAA;CACjH,OACE,oBAAC,OAAD;EAAK,WAAU;EAAa,KAAK;EAC9B,UAAA,UACC,OAAO,MACN,QACC,oBAAC,YAAD;GAAkB;GAAW;GAAY;GAAe;GAAe;GAAO,SAAS;GAAa;EAAO,CAAA,IACzG,OACF,oBAAC,UAAD;GAAgB;GAAW;GAAY;GAAa;GAAM,MAAM;GAAgB;GAAc;EAAO,CAAA,IACnG,UACF,oBAAC,aAAD;GACQ;GACD;GACG;GACD;GACA;GACM;GACL;GACU;GAClB,eAAe;GACH;GACD;EACZ,CAAA,IAED,oBAAC,WAAD;GACQ;GACD;GACC;GACN,MAAM;GACE;GACR,QAAQ,UAAU;GACZ;GACO;GACK;GACV;EACT,CAAA;CAEF,CAAA;AAET;;AAsBA,SAAgB,iBAAiB,EAAE,QAAQ,OAAO,aAAa,aAAa,QAAQ,GAAG,QAA+B;CACpH,MAAM,OAAO,OAAuB,IAAI;CACxC,MAAM,CAAC,MAAM,WAAW,SAAS,CAAC;CAClC,MAAM,OAAO,QAAQ,QAAQ,CAAC;CAC9B,sBAAsB;EACpB,MAAM,UAAU,KAAK;EACrB,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,QAAQ;EACzC,MAAM,gBAAgB,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;EACjF,QAAQ;EACR,MAAM,WAAW,IAAI,eAAe,OAAO;EAC3C,SAAS,QAAQ,OAAO;EACxB,aAAa,SAAS,WAAW;CACnC,GAAG,CAAC,MAAM,CAAC;CACX,IAAI,UAAU,CAAC,KAAK,QAAQ,OAAO,oBAAC,OAAD;EAAO,MAAM;EAAO,QAAQ;EAAa,QAAQ;CAAc,CAAA;CAClG,IAAI,CAAC,QAAQ,OAAO,oBAAC,WAAD;EAAW,OAAM;EAAe;CAAS,CAAA;CAC7D,OACE,oBAAC,OAAD;EAAK,WAAU;EAAa,KAAK;EAC9B,UAAA,UAAU,OAAO,KAAK,oBAAC,YAAD;GAAY,GAAI;GAAc;GAAc;GAAM,MAAM;EAAS,CAAA;CACrF,CAAA;AAET;;;;;;AAOA,SAAS,UAAU,EAAE,OAAO,QAAQ,QAA8E;CAChH,MAAM,QACJ,UAAU,SACN,EAAE,QAAQ,GAAG,KAAK,IAAI,KAAK,IAAI,QAAQ,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,IAAI,IAC/D,UAAU,UACR,EAAE,QAAQ,QAAQ,IAClB,SACE,EAAE,QAAQ,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,MAAM,CAAC,EAAE,IAAI,IACnD,KAAA;CACV,OAAO,oBAAC,OAAD;EAAK,WAAW,oCAAoC;EAAgB;EAAO,MAAK;EAAS,aAAU;CAAQ,CAAA;AACpH"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { D as subscribeSessionIdentity, E as sessionIdentity, it as useQuery } from "./shared-poc11xxo.js";
|
|
2
2
|
import { Pn as useQueryClient, gt as defineMessages } from "./shared-DJZjSi3J.js";
|
|
3
|
-
import { L as state, i as api, st as ownedBy } from "./shared-
|
|
3
|
+
import { L as state, i as api, st as ownedBy } from "./shared-BoLbc2WD.js";
|
|
4
4
|
import { t as Modal } from "./shared-CRr4kJXB.js";
|
|
5
5
|
import "./shared-DpVb48sM.js";
|
|
6
|
-
import "./shared-
|
|
7
|
-
import { t as FormSurface } from "./shared-
|
|
6
|
+
import "./shared-DGaQZXnw.js";
|
|
7
|
+
import { t as FormSurface } from "./shared-B4jgU8h9.js";
|
|
8
8
|
import { useSyncExternalStore } from "react";
|
|
9
9
|
import { jsx } from "react/jsx-runtime";
|
|
10
10
|
// packages/mistri/src/shared/commandRoutes.ts
|
|
@@ -185,4 +185,4 @@ function SignedInFormDialog({ form, appName, prefill, initial, onClose }) {
|
|
|
185
185
|
}
|
|
186
186
|
export { SignedInFormDialog };
|
|
187
187
|
|
|
188
|
-
//# sourceMappingURL=shared-
|
|
188
|
+
//# sourceMappingURL=shared-Dd21k2ic.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-CdNomVzH.js","names":[],"sources":["../../src/shared/commandRoutes.ts","../../src/client/copy/forms/components/FormRoom.ts","../../src/client/components/FormRoom.tsx","../../src/client/components/SignedInFormDialog.tsx"],"sourcesContent":["/**\n * The business-command routes, in one place: the server dispatches on them\n * (server/commands/businessCommandHttp.ts) and `@mistriwork/mistri/testing` sends to\n * them, so a test's request reaches the route a browser's does.\n */\nexport const COMMAND_ROUTES = {\n prepare: '/api/commands/prepare',\n execute: '/api/commands/execute',\n status: '/api/commands/status',\n availability: '/api/commands/availability',\n /** A signed-in form's step checked: the answers so far judged, nothing run (server/commands/commandCheck.ts). */\n check: '/api/commands/check',\n} as const;\n\nexport const COMMAND_ROUTE_PATHS: readonly string[] = Object.values(COMMAND_ROUTES);\n","/** What FormRoom says, in the forms room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '@mistriwork/kit/client/messages.js';\n\nexport const say = defineMessages({\n noFormCalledInThisApp: 'No form called \"{pageId}\" in this app.',\n});\n","/**\n * A signed-in command form as a room of the App: the form the payload\n * served under its view key — served only to a role granted its command —\n * drawn in the content pane with the App's menu beside it, and submitted\n * through the signed-in command route under the visitor's own session and\n * grant, never as the public. The envelope names no record: a form's\n * command creates its rows, so the key is empty and the revision zero.\n */\nimport { useEffect, useSyncExternalStore } from 'react';\nimport { useQuery, useQueryClient } from '@tanstack/react-query';\nimport { api, currentEntry, renderCrumbs, state } from '@mistriwork/kit/client/core.js';\nimport { activeApp } from '../appState.js';\nimport { ownedBy } from '@mistriwork/kit/client/requestIdentity.js';\nimport { COMMAND_ROUTES } from '../../shared/commandRoutes.js';\nimport { sessionIdentity, subscribeSessionIdentity } from '@mistriwork/kit/client/sessionIdentity.js';\nimport type { CommandInputValue } from '../commandState.js';\nimport { formAt, type ServedForm as RoomForm } from '@mistriwork/kit/client/components/appRooms.js';\nimport type { AppPayloadApp } from '../../shared/appPayload.js';\n\n/** A signed-in form as the App serves it: the whole form, which the kit's room type reads only part of. */\ntype ServedForm = RoomForm<AppPayloadApp>;\nimport { FormSurface, type FormPresentation, type FormReceipt } from './FormSurface.js';\nimport type { CommandQuestions } from './CommandFields.js';\nimport type { PageEntry } from '@mistriwork/kit/client/components/pageModel.js';\nimport { say } from '../copy/forms/components/FormRoom.js';\n\n/** The signed-in submission: the ordinary command envelope, naming no record. */\nasync function submitSignedInForm(\n connection: string | null | undefined,\n form: Pick<ServedForm, 'command'>,\n submission: string,\n input: CommandInputValue,\n): Promise<FormReceipt> {\n const answer = (await api('/api/commands/execute', {\n command: form.command.id,\n commandVersion: form.command.version,\n key: {},\n expectedVersion: 0,\n idempotencyKey: submission,\n input,\n ...ownedBy(connection),\n })) as { receiptId: string; status: string; result?: unknown };\n const reference = answer.result && typeof answer.result === 'object' ? (answer.result as { reference?: unknown }).reference : undefined;\n /* What the handler said it did (\"Posted 55 rent charges for October 2026.\"): the page is the only place a person who\n ran it learns whether it did anything, and \"Received\" alone cannot tell a run that posted from one that found nothing to. */\n const said = answer.result && typeof answer.result === 'object' ? (answer.result as { message?: unknown }).message : undefined;\n return {\n ...(typeof reference === 'string' && reference.trim() ? { reference } : {}),\n ...(typeof said === 'string' && said.trim() ? { message: said.trim() } : {}),\n receipt: { id: answer.receiptId, status: answer.status },\n };\n}\n\n/** What the surface draws of a served form: its words, inputs and steps. */\nexport function formPresentation(form: ServedForm): FormPresentation {\n return {\n id: form.id,\n label: form.label,\n input: form.command.input,\n ...(form.description === undefined ? {} : { description: form.description }),\n ...(form.steps === undefined ? {} : { steps: form.steps }),\n ...(form.style === undefined ? {} : { style: form.style }),\n ...(form.note === undefined ? {} : { note: form.note }),\n ...(form.review === false ? { review: false as const } : {}),\n ...(form.submitLabel === undefined ? {} : { submitLabel: form.submitLabel }),\n ...(form.done === undefined ? {} : { done: form.done }),\n ...((form as { inputs?: FormPresentation['inputs'] }).inputs\n ? { inputs: (form as { inputs?: FormPresentation['inputs'] }).inputs }\n : {}),\n };\n}\n\nexport function FormRoom({ entry }: { entry: PageEntry }) {\n const app = activeApp();\n const form = app ? formAt(app, entry.pageId) : undefined;\n useEffect(() => {\n /* A form reached by pasted link painted its crumb before this room\n could say its name. Same correction the dashboard makes. */\n if (form && currentEntry() === entry && entry.label !== form.label) {\n entry.label = form.label;\n renderCrumbs();\n }\n }, [form, entry]);\n if (!app || !form) return <p className=\"note\">{say('noFormCalledInThisApp', { pageId: entry.pageId })}</p>;\n return <FormBody form={form} appName={app.name} />;\n}\n\n/** The form itself, for its room, for a home that names it, and over the page a component opened it from (FormDialog.tsx). */\nexport function FormBody({\n form,\n appName,\n prefill,\n initial,\n onDismiss,\n}: {\n form: ServedForm;\n appName: string;\n /** Where it starts, as a query string, when it is not drawn at its own address. */\n prefill?: string;\n /** Where it starts, as a component gave it: every input, over what the command prepared. */\n initial?: Readonly<Record<string, unknown>>;\n /** Drawn over a page: back to it, from Cancel or from Done once received. */\n onDismiss?: () => void;\n}) {\n const identity = useSyncExternalStore(subscribeSessionIdentity, sessionIdentity, sessionIdentity);\n const client = useQueryClient();\n const connection = state.activeConnection as string | null;\n /* A command that starts its form (`initial()`, `default: me`) is prepared as the form opens, over no\n record: the key is empty, as its submission's is, and one that asks questions defined in rows\n reads them (G40). The form waits for it, so nothing the reader types is overwritten when it lands. */\n const prepares = !form.error && form.command.prepares === true;\n const prepared = useQuery({\n queryKey: ['form-prepare', identity.epoch, connection, form.command.id, form.command.version],\n retry: false,\n staleTime: 0,\n gcTime: 0,\n enabled: prepares,\n queryFn: async (): Promise<{ initial: Record<string, unknown>; questions?: CommandQuestions }> => {\n const result = await api('/api/commands/prepare', {\n ...ownedBy(connection),\n command: form.command.id,\n commandVersion: form.command.version,\n key: {},\n });\n const initial = result?.initial && typeof result.initial === 'object' && !Array.isArray(result.initial) ? result.initial : {};\n // Its questions defined in rows, as the server read them for this reader.\n const questions = result?.defined && typeof result.defined === 'object' ? (result.defined as CommandQuestions) : undefined;\n return { initial, ...(questions ? { questions } : {}) };\n },\n });\n if (form.error) return <p className=\"note\">{form.error}</p>;\n if (prepares && prepared.error) return <p className=\"note\">{prepared.error.message}</p>;\n if (prepares && !prepared.data) return <p className=\"note\" aria-busy=\"true\" />;\n return (\n <FormSurface\n key={form.id}\n form={formPresentation(form)}\n appName={appName}\n again={!onDismiss}\n inDialog={Boolean(onDismiss)}\n connection={connection ?? undefined}\n identityEpoch={identity.epoch}\n {...(prepared.data || initial ? { initial: { ...prepared.data?.initial, ...initial } } : {})}\n {...(prepared.data?.questions ? { questions: prepared.data.questions } : {})}\n {...(prefill === undefined ? {} : { prefill })}\n {...(onDismiss ? { onDismiss } : {})}\n checkStep={(_step, input, names) => checkSignedInStep(connection, form, names, input)}\n submit={async (submission, input) => {\n const receipt = await submitSignedInForm(connection, form, submission, input);\n // What the form created shows in every list that reads it.\n void client.invalidateQueries();\n return receipt;\n }}\n />\n );\n}\n\n/**\n * A signed-in form's step checked by the server (`/api/commands/check`): the same reader, `when`s and rules the run\n * uses, over the fields asked so far, nothing run. A check that cannot be asked — a host with none (a tenant's, a\n * row policy's), the budget spent, offline — is no word: the step goes on and the submission is judged whole.\n */\nasync function checkSignedInStep(\n connection: string | null,\n form: ServedForm,\n names: readonly string[],\n input: unknown,\n): Promise<Record<string, string> | undefined> {\n try {\n const result = (await api(COMMAND_ROUTES.check, {\n ...ownedBy(connection),\n command: form.command.id,\n commandVersion: form.command.version,\n fields: [...names],\n input,\n })) as { problems?: unknown } | undefined;\n const problems = result?.problems && typeof result.problems === 'object' ? (result.problems as Record<string, unknown>) : {};\n return Object.fromEntries(Object.entries(problems).filter((entry): entry is [string, string] => typeof entry[1] === 'string'));\n } catch {\n return undefined;\n }\n}\n","/**\n * A signed-in form over the page a component sent the reader to it from: the\n * form its room draws (FormBody), submitted through the same signed-in\n * command route under the reader's own session and grant. \"Join to react\"\n * on a post opens the Join form here rather than in its room, so the reader\n * who joins is still on the post, whose reads the submit refreshes.\n *\n * Its own module, fetched the first time a component opens one: the form's\n * room reads the nav store, which the public form's dialog must not\n * (FormDialog.tsx), and nothing on a first paint needs either.\n */\nimport { FormBody } from './FormRoom.js';\nimport type { ServedForm as RoomForm } from '@mistriwork/kit/client/components/appRooms.js';\nimport type { AppPayloadApp } from '../../shared/appPayload.js';\n\n/** A signed-in form as the App serves it: the whole form, which the kit's room type reads only part of. */\ntype ServedForm = RoomForm<AppPayloadApp>;\nimport { Modal } from '@mistriwork/ui/Modal.js';\nimport './FormDialog.css';\n\nexport function SignedInFormDialog({\n form,\n appName,\n prefill,\n initial,\n onClose,\n}: {\n form: ServedForm;\n appName: string;\n /** Where it starts, as an address says it: only its `prefillable` inputs read it. */\n prefill?: string;\n /** Where it starts, as a component gave it (`useCommand(…).open(record, input)`): every input, as typed values. */\n initial?: Readonly<Record<string, unknown>>;\n onClose: () => void;\n}) {\n return (\n <Modal className=\"form-dialog\" onClose={onClose} ariaLabel={form.label}>\n <FormBody\n form={form}\n appName={appName}\n {...(prefill === undefined ? {} : { prefill })}\n {...(initial ? { initial } : {})}\n onDismiss={onClose}\n />\n </Modal>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shared-Dd21k2ic.js","names":[],"sources":["../../src/shared/commandRoutes.ts","../../src/client/copy/forms/components/FormRoom.ts","../../src/client/components/FormRoom.tsx","../../src/client/components/SignedInFormDialog.tsx"],"sourcesContent":["/**\n * The business-command routes, in one place: the server dispatches on them\n * (server/commands/businessCommandHttp.ts) and `@mistriwork/mistri/testing` sends to\n * them, so a test's request reaches the route a browser's does.\n */\nexport const COMMAND_ROUTES = {\n prepare: '/api/commands/prepare',\n execute: '/api/commands/execute',\n status: '/api/commands/status',\n availability: '/api/commands/availability',\n /** A signed-in form's step checked: the answers so far judged, nothing run (server/commands/commandCheck.ts). */\n check: '/api/commands/check',\n} as const;\n\nexport const COMMAND_ROUTE_PATHS: readonly string[] = Object.values(COMMAND_ROUTES);\n","/** What FormRoom says, in the forms room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '@mistriwork/kit/client/messages.js';\n\nexport const say = defineMessages({\n noFormCalledInThisApp: 'No form called \"{pageId}\" in this app.',\n});\n","/**\n * A signed-in command form as a room of the App: the form the payload\n * served under its view key — served only to a role granted its command —\n * drawn in the content pane with the App's menu beside it, and submitted\n * through the signed-in command route under the visitor's own session and\n * grant, never as the public. The envelope names no record: a form's\n * command creates its rows, so the key is empty and the revision zero.\n */\nimport { useEffect, useSyncExternalStore } from 'react';\nimport { useQuery, useQueryClient } from '@tanstack/react-query';\nimport { api, currentEntry, renderCrumbs, state } from '@mistriwork/kit/client/core.js';\nimport { activeApp } from '../appState.js';\nimport { ownedBy } from '@mistriwork/kit/client/requestIdentity.js';\nimport { COMMAND_ROUTES } from '../../shared/commandRoutes.js';\nimport { sessionIdentity, subscribeSessionIdentity } from '@mistriwork/kit/client/sessionIdentity.js';\nimport type { CommandInputValue } from '../commandState.js';\nimport { formAt, type ServedForm as RoomForm } from '@mistriwork/kit/client/components/appRooms.js';\nimport type { AppPayloadApp } from '../../shared/appPayload.js';\n\n/** A signed-in form as the App serves it: the whole form, which the kit's room type reads only part of. */\ntype ServedForm = RoomForm<AppPayloadApp>;\nimport { FormSurface, type FormPresentation, type FormReceipt } from './FormSurface.js';\nimport type { CommandQuestions } from './CommandFields.js';\nimport type { PageEntry } from '@mistriwork/kit/client/components/pageModel.js';\nimport { say } from '../copy/forms/components/FormRoom.js';\n\n/** The signed-in submission: the ordinary command envelope, naming no record. */\nasync function submitSignedInForm(\n connection: string | null | undefined,\n form: Pick<ServedForm, 'command'>,\n submission: string,\n input: CommandInputValue,\n): Promise<FormReceipt> {\n const answer = (await api('/api/commands/execute', {\n command: form.command.id,\n commandVersion: form.command.version,\n key: {},\n expectedVersion: 0,\n idempotencyKey: submission,\n input,\n ...ownedBy(connection),\n })) as { receiptId: string; status: string; result?: unknown };\n const reference = answer.result && typeof answer.result === 'object' ? (answer.result as { reference?: unknown }).reference : undefined;\n /* What the handler said it did (\"Posted 55 rent charges for October 2026.\"): the page is the only place a person who\n ran it learns whether it did anything, and \"Received\" alone cannot tell a run that posted from one that found nothing to. */\n const said = answer.result && typeof answer.result === 'object' ? (answer.result as { message?: unknown }).message : undefined;\n return {\n ...(typeof reference === 'string' && reference.trim() ? { reference } : {}),\n ...(typeof said === 'string' && said.trim() ? { message: said.trim() } : {}),\n receipt: { id: answer.receiptId, status: answer.status },\n };\n}\n\n/** What the surface draws of a served form: its words, inputs and steps. */\nexport function formPresentation(form: ServedForm): FormPresentation {\n return {\n id: form.id,\n label: form.label,\n input: form.command.input,\n ...(form.description === undefined ? {} : { description: form.description }),\n ...(form.steps === undefined ? {} : { steps: form.steps }),\n ...(form.style === undefined ? {} : { style: form.style }),\n ...(form.note === undefined ? {} : { note: form.note }),\n ...(form.review === false ? { review: false as const } : {}),\n ...(form.submitLabel === undefined ? {} : { submitLabel: form.submitLabel }),\n ...(form.done === undefined ? {} : { done: form.done }),\n ...((form as { inputs?: FormPresentation['inputs'] }).inputs\n ? { inputs: (form as { inputs?: FormPresentation['inputs'] }).inputs }\n : {}),\n };\n}\n\nexport function FormRoom({ entry }: { entry: PageEntry }) {\n const app = activeApp();\n const form = app ? formAt(app, entry.pageId) : undefined;\n useEffect(() => {\n /* A form reached by pasted link painted its crumb before this room\n could say its name. Same correction the dashboard makes. */\n if (form && currentEntry() === entry && entry.label !== form.label) {\n entry.label = form.label;\n renderCrumbs();\n }\n }, [form, entry]);\n if (!app || !form) return <p className=\"note\">{say('noFormCalledInThisApp', { pageId: entry.pageId })}</p>;\n return <FormBody form={form} appName={app.name} />;\n}\n\n/** The form itself, for its room, for a home that names it, and over the page a component opened it from (FormDialog.tsx). */\nexport function FormBody({\n form,\n appName,\n prefill,\n initial,\n onDismiss,\n}: {\n form: ServedForm;\n appName: string;\n /** Where it starts, as a query string, when it is not drawn at its own address. */\n prefill?: string;\n /** Where it starts, as a component gave it: every input, over what the command prepared. */\n initial?: Readonly<Record<string, unknown>>;\n /** Drawn over a page: back to it, from Cancel or from Done once received. */\n onDismiss?: () => void;\n}) {\n const identity = useSyncExternalStore(subscribeSessionIdentity, sessionIdentity, sessionIdentity);\n const client = useQueryClient();\n const connection = state.activeConnection as string | null;\n /* A command that starts its form (`initial()`, `default: me`) is prepared as the form opens, over no\n record: the key is empty, as its submission's is, and one that asks questions defined in rows\n reads them (G40). The form waits for it, so nothing the reader types is overwritten when it lands. */\n const prepares = !form.error && form.command.prepares === true;\n const prepared = useQuery({\n queryKey: ['form-prepare', identity.epoch, connection, form.command.id, form.command.version],\n retry: false,\n staleTime: 0,\n gcTime: 0,\n enabled: prepares,\n queryFn: async (): Promise<{ initial: Record<string, unknown>; questions?: CommandQuestions }> => {\n const result = await api('/api/commands/prepare', {\n ...ownedBy(connection),\n command: form.command.id,\n commandVersion: form.command.version,\n key: {},\n });\n const initial = result?.initial && typeof result.initial === 'object' && !Array.isArray(result.initial) ? result.initial : {};\n // Its questions defined in rows, as the server read them for this reader.\n const questions = result?.defined && typeof result.defined === 'object' ? (result.defined as CommandQuestions) : undefined;\n return { initial, ...(questions ? { questions } : {}) };\n },\n });\n if (form.error) return <p className=\"note\">{form.error}</p>;\n if (prepares && prepared.error) return <p className=\"note\">{prepared.error.message}</p>;\n if (prepares && !prepared.data) return <p className=\"note\" aria-busy=\"true\" />;\n return (\n <FormSurface\n key={form.id}\n form={formPresentation(form)}\n appName={appName}\n again={!onDismiss}\n inDialog={Boolean(onDismiss)}\n connection={connection ?? undefined}\n identityEpoch={identity.epoch}\n {...(prepared.data || initial ? { initial: { ...prepared.data?.initial, ...initial } } : {})}\n {...(prepared.data?.questions ? { questions: prepared.data.questions } : {})}\n {...(prefill === undefined ? {} : { prefill })}\n {...(onDismiss ? { onDismiss } : {})}\n checkStep={(_step, input, names) => checkSignedInStep(connection, form, names, input)}\n submit={async (submission, input) => {\n const receipt = await submitSignedInForm(connection, form, submission, input);\n // What the form created shows in every list that reads it.\n void client.invalidateQueries();\n return receipt;\n }}\n />\n );\n}\n\n/**\n * A signed-in form's step checked by the server (`/api/commands/check`): the same reader, `when`s and rules the run\n * uses, over the fields asked so far, nothing run. A check that cannot be asked — a host with none (a tenant's, a\n * row policy's), the budget spent, offline — is no word: the step goes on and the submission is judged whole.\n */\nasync function checkSignedInStep(\n connection: string | null,\n form: ServedForm,\n names: readonly string[],\n input: unknown,\n): Promise<Record<string, string> | undefined> {\n try {\n const result = (await api(COMMAND_ROUTES.check, {\n ...ownedBy(connection),\n command: form.command.id,\n commandVersion: form.command.version,\n fields: [...names],\n input,\n })) as { problems?: unknown } | undefined;\n const problems = result?.problems && typeof result.problems === 'object' ? (result.problems as Record<string, unknown>) : {};\n return Object.fromEntries(Object.entries(problems).filter((entry): entry is [string, string] => typeof entry[1] === 'string'));\n } catch {\n return undefined;\n }\n}\n","/**\n * A signed-in form over the page a component sent the reader to it from: the\n * form its room draws (FormBody), submitted through the same signed-in\n * command route under the reader's own session and grant. \"Join to react\"\n * on a post opens the Join form here rather than in its room, so the reader\n * who joins is still on the post, whose reads the submit refreshes.\n *\n * Its own module, fetched the first time a component opens one: the form's\n * room reads the nav store, which the public form's dialog must not\n * (FormDialog.tsx), and nothing on a first paint needs either.\n */\nimport { FormBody } from './FormRoom.js';\nimport type { ServedForm as RoomForm } from '@mistriwork/kit/client/components/appRooms.js';\nimport type { AppPayloadApp } from '../../shared/appPayload.js';\n\n/** A signed-in form as the App serves it: the whole form, which the kit's room type reads only part of. */\ntype ServedForm = RoomForm<AppPayloadApp>;\nimport { Modal } from '@mistriwork/ui/Modal.js';\nimport './FormDialog.css';\n\nexport function SignedInFormDialog({\n form,\n appName,\n prefill,\n initial,\n onClose,\n}: {\n form: ServedForm;\n appName: string;\n /** Where it starts, as an address says it: only its `prefillable` inputs read it. */\n prefill?: string;\n /** Where it starts, as a component gave it (`useCommand(…).open(record, input)`): every input, as typed values. */\n initial?: Readonly<Record<string, unknown>>;\n onClose: () => void;\n}) {\n return (\n <Modal className=\"form-dialog\" onClose={onClose} ariaLabel={form.label}>\n <FormBody\n form={form}\n appName={appName}\n {...(prefill === undefined ? {} : { prefill })}\n {...(initial ? { initial } : {})}\n onDismiss={onClose}\n />\n </Modal>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { ht as Icon, it as useQuery, tt as useMessageLanguage } from "./shared-poc11xxo.js";
|
|
2
2
|
import { F as primaryKey, d as dressFor, g as maskedText, gt as defineMessages, m as initialsOf, p as hueOf, ut as humanName } from "./shared-DJZjSi3J.js";
|
|
3
|
-
import { j as useChangedRows } from "./shared-
|
|
4
|
-
import { L as state, _ as openRecord, c as columnLabel, d as findTable, i as api, p as keyOf, s as columnDisplay, st as ownedBy, w as quote } from "./shared-
|
|
3
|
+
import { j as useChangedRows } from "./shared-BHsjawAG.js";
|
|
4
|
+
import { L as state, _ as openRecord, c as columnLabel, d as findTable, i as api, p as keyOf, s as columnDisplay, st as ownedBy, w as quote } from "./shared-BoLbc2WD.js";
|
|
5
5
|
import { a as served } from "./shared-sZ8B7ZTw.js";
|
|
6
6
|
import { Kn as viewportSize, Wn as viewportBox } from "./shared-CRr4kJXB.js";
|
|
7
|
-
import { ot as useVirtualizer } from "./shared-
|
|
8
|
-
import { f as Value, y as opensRecord } from "./shared-
|
|
7
|
+
import { ot as useVirtualizer } from "./shared-XcSKDp6N.js";
|
|
8
|
+
import { f as Value, y as opensRecord } from "./shared-Chp88all.js";
|
|
9
9
|
import { n as cardsImageSort, r as factWord } from "./shared-CS6uipL4.js";
|
|
10
|
-
import { n as useListOrder } from "./shared-
|
|
11
|
-
import { n as namedCell } from "./shared-
|
|
10
|
+
import { n as useListOrder } from "./shared-1PD7TOTJ.js";
|
|
11
|
+
import { n as namedCell } from "./shared-DyKsJTZs.js";
|
|
12
12
|
import { i as imageSources, r as imageFallbackSet, t as CARD_IMAGE_SIZES } from "./shared-BLkDQ3VT.js";
|
|
13
|
-
import { a as inlineFileAddress } from "./shared-
|
|
13
|
+
import { a as inlineFileAddress } from "./shared-BOe4W2FI.js";
|
|
14
14
|
import { useCallback, useLayoutEffect, useRef, useState } from "react";
|
|
15
15
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
16
16
|
var OVERSCAN = 6;
|
|
@@ -460,4 +460,4 @@ function CardPicture({ src, avatar, title, first = false }) {
|
|
|
460
460
|
}
|
|
461
461
|
export { CardsList, cardImageAddress };
|
|
462
462
|
|
|
463
|
-
//# sourceMappingURL=shared-
|
|
463
|
+
//# sourceMappingURL=shared-Dh0nHtLZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-gRMbxmpu.js","names":[],"sources":["../../../kit/src/client/components/useRowWindow.ts","../../../kit/src/client/copy/lists/components/CardsList.ts","../../../kit/src/client/components/CardsList.tsx"],"sourcesContent":["/**\n * Windowed drawing for the lists that are not the grid: cards, a queue and a\n * checklist draw every row they are handed, and a page of a thousand cards is\n * a thousand pictures, facts and buttons laid out before the first paint.\n *\n * Only the *drawing* is windowed. The rows handed in are still the whole page\n * — the pager, the record trail, export and a queue's selection all count\n * them as before — and at or under the threshold nothing changes at all, so\n * an ordinary page draws exactly what it drew. Above it, the rows around the\n * viewport are drawn between two spacers that hold the rest of the page's\n * height, measured on the zoom-aware ruler (core.js) because the density\n * zoom puts spacer heights and scroll offsets in layout pixels.\n *\n * A list laid out in columns (cards) is windowed by line: `columns` says how\n * many items stand side by side, and a line is drawn or not as a whole, so\n * the grid never shifts a card into another column as it scrolls.\n */\nimport { useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport { viewportBox, viewportSize } from '../core.js';\n\n/**\n * Past a hundred rows a page is windowed; at or under it nothing is. Lower\n * than the grid's 300 (Grid.tsx) on purpose: a card or a queue row is a\n * dozen grid cells' worth of layout, and the App's largest page size is 250,\n * which the grid's threshold would never reach. Every smaller page size (10\n * to 100) still draws exactly what it drew.\n */\nexport const WINDOW_THRESHOLD = 100;\nconst OVERSCAN = 6;\n\nexport interface RowWindow<E extends HTMLElement> {\n /** The list's element: what is measured, and where the scroller is looked for. */\n ref: (element: E | null) => void;\n windowed: boolean;\n /** The item indexes to draw, in order: every one when not windowed. */\n indices: number[];\n /** Spacer heights before and after the drawn items, in layout pixels; 0 draws no spacer. */\n before: number;\n after: number;\n /** A drawn item's ref, beside `data-index={lineOf(index)}`: how a row's real height is learned. */\n measure: (element: Element | null) => void;\n lineOf: (index: number) => number;\n /** Bring an item into the window, so a keyboard move to a row not yet drawn has a row to land on. */\n scrollTo: (index: number) => void;\n}\n\nexport function useRowWindow<E extends HTMLElement>(\n count: number,\n options: {\n /** How many items stand side by side in the list, read from its element. */\n columns?: (list: E) => number;\n /** A first guess at one line's height, corrected by measurement. */\n estimate?: number;\n } = {},\n): RowWindow<E> {\n const windowed = count > WINDOW_THRESHOLD;\n const [list, setList] = useState<E | null>(null);\n /* The element is kept aside and only becomes state once the page is long\n enough to window: an ordinary list must not pay a second render for it. */\n const element = useRef<E | null>(null);\n const windowedNow = useRef(windowed);\n windowedNow.current = windowed;\n const ref = useCallback((node: E | null) => {\n element.current = node;\n if (windowedNow.current) setList(node);\n }, []);\n useLayoutEffect(() => {\n if (windowed) setList(element.current);\n }, [windowed]);\n const [scroller, setScroller] = useState<HTMLElement | null>(null);\n const [columns, setColumns] = useState(1);\n const [margin, setMargin] = useState(0);\n const [gap, setGap] = useState(0);\n const columnsOf = useRef(options.columns);\n columnsOf.current = options.columns;\n useLayoutEffect(() => {\n if (!windowed || !list) return;\n setScroller((list.closest('.content') as HTMLElement | null) ?? (document.scrollingElement as HTMLElement | null));\n }, [windowed, list]);\n const lines = windowed ? Math.ceil(count / columns) : 0;\n const virtualizer = useVirtualizer({\n count: lines,\n getScrollElement: () => scroller,\n estimateSize: () => options.estimate ?? 72,\n overscan: OVERSCAN,\n scrollMargin: margin,\n gap,\n enabled: windowed && Boolean(scroller),\n // Drawn from the first paint, before the scroller is found and measured: a window's worth, not nothing.\n initialRect: { width: 0, height: windowed ? viewportSize().height : 0 },\n // Measured in layout pixels: the default reads screen pixels, which the density zoom scales.\n measureElement: (element) => viewportBox(element).height,\n });\n /* Where the list starts inside what scrolls, how many items share a line\n and the gap between lines, read after each paint: all three move with the\n room's width and the header above the list, none of them per scroll. */\n useLayoutEffect(() => {\n if (!windowed || !list || !scroller) return;\n const style = getComputedStyle(list);\n const across = Math.max(1, columnsOf.current?.(list) ?? 1);\n if (across !== columns) setColumns(across);\n const rowGap = Number.parseFloat(style.rowGap) || 0;\n if (rowGap !== gap) setGap(rowGap);\n const top =\n scroller === document.scrollingElement\n ? viewportBox(list).top + scroller.scrollTop\n : viewportBox(list).top - viewportBox(scroller).top + scroller.scrollTop;\n if (Math.abs(top - margin) >= 1) setMargin(top);\n });\n\n const lineOf = (index: number) => Math.floor(index / columns);\n if (!windowed) {\n return {\n ref,\n windowed,\n indices: Array.from({ length: count }, (_, i) => i),\n before: 0,\n after: 0,\n measure: () => {},\n lineOf,\n scrollTo: () => {},\n };\n }\n let items: Array<{ index: number; start: number; end: number }> = virtualizer.getVirtualItems();\n /* A scroller that measures no height (not laid out yet, or no layout at\n all) would draw nothing: the first screenful stands in until it does. */\n if (!items.length && lines) {\n const size = options.estimate ?? 72;\n items = Array.from({ length: Math.min(lines, Math.ceil(viewportSize().height / size) + OVERSCAN) }, (_, index) => ({\n index,\n start: margin + index * (size + gap),\n end: margin + index * (size + gap) + size,\n }));\n }\n const total = virtualizer.getTotalSize() || lines * ((options.estimate ?? 72) + gap) - gap;\n const indices: number[] = [];\n for (const item of items) {\n for (let i = item.index * columns; i < Math.min(count, (item.index + 1) * columns); i++) indices.push(i);\n }\n /* A spacer is an item of the list too, so the list's own gap stands beside\n it once more than the lines it replaces: taken off its height. */\n const before = items.length ? Math.max(0, items[0].start - margin - gap) : 0;\n const after = items.length ? Math.max(0, total - (items[items.length - 1].end - margin) - gap) : 0;\n return {\n ref,\n windowed,\n indices,\n before,\n after,\n measure: virtualizer.measureElement,\n lineOf,\n scrollTo: (index) => virtualizer.scrollToIndex(lineOf(index), { align: 'auto' }),\n };\n}\n\n/**\n * The list element's ref, for both the window and another owner of the same\n * element (a hand order's zone). Stable across renders: a fresh callback each\n * paint would hand the window null and the element again every time.\n */\nexport function useListRef<E extends HTMLElement>(win: RowWindow<E>, other?: (element: E | null) => void): (element: E | null) => void {\n const also = useRef(other);\n also.current = other;\n const own = win.ref;\n return useCallback(\n (element: E | null) => {\n also.current?.(element);\n own(element);\n },\n [own],\n );\n}\n","/** What CardsList says, in the lists and grid room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n cards: 'Cards',\n});\n","/**\n * A list's rows as cards: a picture at a fixed ratio, the title, one\n * prominent figure, a few facts under their captions and a badge. The whole\n * card opens its record — the title is the one button, stretched over the\n * card, so the card is one tab stop with the house focus ring. One to four\n * columns, by the room's width rather than the window's: a card list in a\n * peek or a split pane reads as a phone would.\n *\n * The picture is a column holding an image address, or the first file of a\n * related file resource, asked once for every card on the page and drawn\n * inline through the file route. Images load lazily into boxes that already\n * have their size, so nothing moves as they arrive. A configured image that\n * is missing shows a placeholder; a card without image configuration starts\n * with its text.\n */\nimport { useMessageLanguage } from './useMessageLanguage.js';\nimport { useState } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { api, columnDisplay, columnLabel, findTable, keyOf, openRecord, quote, state } from '../core.js';\nimport { opensRecord } from '../recordIntent.js';\nimport type { TrailList } from '../recordTrail.js';\nimport { ownedBy } from '../requestIdentity.js';\nimport { primaryKey, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { humanName } from '@mistriwork/engine/shared/model.js';\nimport { cardsImageSort, factWord, type AppCardsList } from '@mistriwork/engine/shared/appCards.js';\nimport { Value } from './Value.js';\nimport { dressFor, hueOf, initialsOf, maskedText, type DisplayRule } from './valueModel.js';\nimport { Icon, type IconName } from './Icon.js';\nimport { useListOrder, type ListOrder } from './useListOrder.js';\nimport { useChangedRows } from '../liveState.js';\nimport { useListRef, useRowWindow } from './useRowWindow.js';\nimport { namedCell } from './listColumn.js';\nimport { say } from '../copy/lists/components/CardsList.js';\nimport { served } from '../addressMount.js';\nimport { CARD_IMAGE_SIZES, imageSources, imageFallbackSet } from '@mistriwork/engine/shared/imageWidths.js';\nimport { inlineFileAddress } from './cardsModel.js';\n\n/** An image address a card may draw: https, or a path on this server. Never data:, javascript: or a protocol-relative host. */\nexport function cardImageAddress(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n const text = value.trim();\n return /^https:\\/\\/[^\\s]+$/i.test(text) || /^\\/(?!\\/)[^\\s]*$/.test(text) ? text : undefined;\n}\n\nconst literal = (value: unknown) => (typeof value === 'number' ? String(value) : quote(String(value)));\n\n/**\n * The lead file of each card's record, when the picture comes from a file\n * resource: one question for the whole page, sorted by the owning record and\n * then the author's order, so the first row seen for a record is its lead.\n * With `about`, the files are the record that reference names (a saved car's\n * car), asked by the reference's value on each row.\n */\nfunction useLeadFiles(\n table: TableMeta,\n spec: AppCardsList,\n rows: Array<Record<string, unknown>>,\n): (row: Record<string, unknown>) => string | undefined {\n const image = typeof spec.image === 'object' ? spec.image : undefined;\n const from = image ? (findTable(image.from) as TableMeta | undefined) : undefined;\n const connection = state.activeConnection ?? null;\n const foreignKeys = (state.schema?.foreignKeys ?? []) as Array<{\n from: { table: string; columns: string[] };\n to: { table: string; columns: string[] };\n }>;\n /* The record the files belong to: the row's own, or the one its `about` reference names. */\n const owner = image?.about\n ? foreignKeys.find((fk) => fk.from.table === table.id && fk.from.columns.length === 1 && fk.from.columns[0] === image.about)?.to.table\n : table.id;\n const reference =\n image && from && owner\n ? foreignKeys.find(\n (fk) => fk.from.table === from.id && fk.to.table === owner && fk.from.columns.length === 1 && fk.from.columns[0] === image.whose,\n )\n : undefined;\n // The row's own column holding that record's key: the key itself, or the reference to it.\n const target = reference ? (image?.about ?? reference.to.columns[0]) : undefined;\n const owners = target ? [...new Set(rows.map((row) => row[target]).filter((value) => value !== null && value !== undefined))] : [];\n const keys = from ? primaryKey(from) : [];\n const sort = image?.sort ? cardsImageSort(image.sort) : undefined;\n const text =\n image && from && owners.length && keys.length\n ? `${from.id} ${image.whose} in (${owners.map(literal).join(', ')}) sort ${image.whose}${sort ? `, ${sort.column} ${sort.direction}` : ''} show ${[...new Set([...keys, image.whose])].join(', ')}`\n : '';\n const answer = useQuery({\n queryKey: ['run', connection ?? '', 'card-images', text],\n enabled: Boolean(text),\n staleTime: 60_000,\n queryFn: async () => {\n const data = (await api('/api/run', {\n q: text,\n limit: 500,\n offset: 0,\n count: 'none',\n ...(connection ? ownedBy(connection) : {}),\n })) as {\n rows?: Array<Record<string, unknown>>;\n errors?: Array<{ message: string }>;\n };\n if (data.errors?.length) throw new Error(data.errors[0].message);\n return data.rows ?? [];\n },\n });\n const lead = new Map<string, string>();\n if (image && from) {\n for (const row of answer.data ?? []) {\n const owner = String(row[image.whose]);\n if (!lead.has(owner))\n lead.set(owner, inlineFileAddress(from.id, Object.fromEntries(keys.map((column) => [column, row[column]])), connection, 'thumb'));\n }\n }\n return (row) => (target ? lead.get(String(row[target])) : undefined);\n}\n\ninterface CardsListProps {\n table: TableMeta;\n rows: Array<Record<string, unknown>>;\n spec: AppCardsList;\n interactive?: boolean;\n /** Lets an otherwise read-only card open its record, without enabling writes or ordering. */\n recordNavigation?: boolean;\n label?: string;\n /** The list these cards are a page of, so a record opened here steps past the page (recordTrail.ts). */\n list?: TrailList;\n /** The view's hand order, where it keeps one (useListOrder.tsx). */\n order?: ListOrder;\n /** A reference column → the fetched path its record's name is read from (appListModel's referenceNames). */\n referenceLabels?: Record<string, string>;\n}\n\n/* Cards stand in columns where the room is wide enough: a drop lands left or right of a card, not above it. */\nconst inColumns = (grid: HTMLElement) => getComputedStyle(grid).gridTemplateColumns.trim().split(/\\s+/).length > 1;\n\nexport function CardsList({\n table,\n rows,\n spec,\n interactive = true,\n recordNavigation = false,\n label,\n list,\n order,\n referenceLabels,\n}: CardsListProps) {\n useMessageLanguage();\n const leadOf = useLeadFiles(table, spec, rows);\n /* The card's name wherever it is said — the title, the record it opens, the order's announcements — masked where its rule says. */\n const titleOf = (row: Record<string, unknown>) => {\n // A title that is a reference is its record's name, where the list fetched one (listColumn.ts).\n const named = namedCell(table, spec.primary, row, referenceLabels);\n if (named.column?.name !== spec.primary) return String(named.value);\n const rule = columnDisplay(table.id, spec.primary) as DisplayRule | null;\n const masked = maskedText(rule, row[spec.primary]);\n if (masked !== undefined && masked !== null) return masked;\n const value = row[spec.primary];\n if (value === null || value === undefined) return say('common.untitled');\n /* The column's display rule says the title as the grid does (an enum's words, a figure's format): a card titled\n each record by the stored key where the rule had words for it. A document, a code and a picture have no words\n to say; they stay the value. */\n const dress = dressFor(rule, value);\n if (!dress || dress.kind === 'json' || dress.kind === 'qr' || dress.kind === 'signature' || dress.kind === 'richText')\n return String(value);\n return 'label' in dress ? dress.label : dress.text;\n };\n const ordering = useListOrder({ table, rows, order, interactive, title: titleOf, across: inColumns });\n // A card another person just changed wears the grid's fading tint (docs/design.md, rule 11).\n const changedRows = useChangedRows(table.id, rows, (row) => keyOf(table, row) as Record<string, unknown> | null);\n const image = spec.image;\n const records: Array<{ key: Record<string, unknown>; label?: string }> = rows.flatMap((row) => {\n const key = keyOf(table, row) as Record<string, unknown> | null;\n return key ? [{ key, label: titleOf(row) }] : [];\n });\n /* A rollup or a label path dresses as what it reads, and a reference the list\n fetched a name for says the name (listColumn.ts). */\n const said = (row: Record<string, unknown>, name: string) => {\n const { column, value } = namedCell(table, name, row, referenceLabels);\n return <Value col={column} value={value} tableId={table.id} compact />;\n };\n // A line or a fact may be a label path (\"dealer_id.name\"): its caption is the App's, else its last step's.\n const caption = (name: string) => (columnLabel(table.id, name) as string | null) ?? humanName(name.split('.').pop() ?? name);\n const present = (row: Record<string, unknown>, name: string) => row[name] !== null && row[name] !== undefined && row[name] !== '';\n const canOpen = interactive || recordNavigation;\n /* A fact as a card says it, on its line under the figure or along the foot: its value, and its own word after it\n (\"1 owner\") or before it (\"Born …\") as its caption for everyone; without one, the caption only assistive\n technology hears. */\n const fact = (row: Record<string, unknown>, name: string, className: string) => {\n const word =\n spec.factLabels && Object.hasOwn(spec.factLabels, name)\n ? factWord(\n spec.factLabels[name],\n row[name],\n columnDisplay(table.id, name) as { format?: string; unit?: string; bar?: boolean } | null,\n )\n : undefined;\n const before = word !== undefined && spec.factLabels![name].position === 'before';\n return (\n <span key={name} className={className}>\n {word === undefined && <span className=\"visually-hidden\">{caption(name)}: </span>}\n {before && <span className=\"cards-summary-word\">{word} </span>}\n {said(row, name)}\n {word !== undefined && !before && <span className=\"cards-summary-word\"> {word}</span>}\n </span>\n );\n };\n // A long page is drawn a line of cards at a time; the page itself is unchanged (useRowWindow.ts).\n const win = useRowWindow<HTMLUListElement>(rows.length, {\n columns: (grid) => getComputedStyle(grid).gridTemplateColumns.trim().split(/\\s+/).length,\n estimate: 280,\n });\n const listRef = useListRef(win, ordering.zone.ref);\n return (\n <div className=\"cards\">\n <ul className=\"cards-grid\" aria-label={label ?? say('cards')} {...ordering.zone} ref={listRef}>\n {win.before > 0 && <li className=\"cards-spacer\" aria-hidden=\"true\" style={{ height: `${win.before}px` }} />}\n {win.indices.map((index) => {\n const row = ordering.rows[index];\n const key = keyOf(table, row) as Record<string, unknown> | null;\n const id = ordering.id(row);\n const title = titleOf(row);\n const src = typeof image === 'string' ? cardImageAddress(row[image]) : leadOf(row);\n const avatar = spec.media === 'avatar';\n /* An icon tile leads the card in a picture's place: it is no picture, so the card keeps its text layout. */\n const tile = spec.media === 'icon';\n const hasMedia = image !== undefined || avatar;\n /* The summary as words, masked where its rule says: the whole of it rides the title attribute, since three lines may not hold it. */\n const description =\n spec.description && present(row, spec.description)\n ? (maskedText(columnDisplay(table.id, spec.description) as DisplayRule | null, row[spec.description]) ??\n String(row[spec.description]))\n : undefined;\n /* The icon column's value as its display rule marks it everywhere else; a value the rule leaves unmapped draws none. */\n const dressed = spec.icon ? dressFor(columnDisplay(table.id, spec.icon) as DisplayRule | null, row[spec.icon]) : null;\n const mark =\n (dressed?.kind === 'tone' || dressed?.kind === 'mark') && dressed.icon\n ? { name: dressed.icon, label: dressed.label, tone: dressed.kind === 'tone' ? dressed.tone : undefined }\n : null;\n const badge =\n spec.badge && row[spec.badge] != null && row[spec.badge] !== '' ? (\n <span className=\"cards-badge\">{said(row, spec.badge)}</span>\n ) : null;\n return (\n <li\n key={key ? JSON.stringify(key) : index}\n className=\"cards-item\"\n {...(win.windowed ? { 'data-index': win.lineOf(index), ref: win.measure } : {})}\n {...(id ? ordering.item(id) : {})}\n onKeyDown={id ? (event) => ordering.keyDown(event, id) : undefined}\n >\n <article\n className={`cards-card${hasMedia ? ' has-media' : ''}${avatar ? ' cards-profile' : ''}${changedRows.has(row) ? ` row-changed-${changedRows.get(row)}` : ''}`}\n >\n {hasMedia && <CardPicture src={src} avatar={avatar} title={title} first={index < FIRST_ROW} />}\n {id && ordering.menu(id)}\n <div className=\"cards-body\">\n {/* The tile holds its place where a value has no mark, so every card's title starts on one line. */}\n {tile && (\n <span className={`cards-tile${mark?.tone ? ` tone-${mark.tone}` : ''}`}>\n {mark && <Icon name={mark.name as IconName} label={mark.label} size={22} />}\n </span>\n )}\n <h3 className=\"cards-title\">\n {mark && !tile && <Icon name={mark.name as IconName} label={mark.label} className=\"cards-icon\" />}\n {canOpen && key ? (\n <button\n type=\"button\"\n className=\"cards-open\"\n data-order-focus=\"\"\n aria-keyshortcuts={ordering.shortcuts}\n data-opens={opensRecord(table.id, key)}\n onClick={() => void openRecord(table.id, key, title, 'push', { records, list })}\n >\n {title}\n </button>\n ) : (\n <span>{title}</span>\n )}\n </h3>\n {description !== undefined && (\n <p className=\"cards-description\" title={description}>\n {description}\n </p>\n )}\n {(!hasMedia || avatar) && badge}\n {spec.value && row[spec.value] != null && <p className=\"cards-value\">{said(row, spec.value)}</p>}\n {spec.facts?.some((name) => present(row, name)) ? (\n <p className=\"cards-summary\">\n {spec.facts.filter((name) => present(row, name)).map((name) => fact(row, name, 'cards-summary-item'))}\n </p>\n ) : null}\n {spec.lines?.length ? (\n <dl className=\"cards-facts\">\n {spec.lines.map((name) => (\n <div key={name} className=\"cards-fact\">\n <dt>{caption(name)}</dt>\n <dd>{said(row, name)}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n </div>\n {/* The foot stands on every card that declares one, filled or not, so a row of cards ends on one line. */}\n {spec.footer?.length ? (\n <p className=\"cards-foot\">\n {spec.footer.some((name) => present(row, name))\n ? spec.footer.filter((name) => present(row, name)).map((name) => fact(row, name, 'cards-foot-item'))\n : '\\u00a0'}\n </p>\n ) : null}\n {hasMedia && !avatar && badge}\n </article>\n </li>\n );\n })}\n {win.after > 0 && <li className=\"cards-spacer\" aria-hidden=\"true\" style={{ height: `${win.after}px` }} />}\n </ul>\n {ordering.live}\n </div>\n );\n}\n\n/**\n * A file's two sizes, for the browser to choose between: the thumbnail\n * (320 wide) where a card is drawn at about 300 pixels, the file itself on a\n * dense screen or a phone's full-width card, where the thumbnail would be\n * soft. A file resource with no thumbnail answers both with the file.\n */\nfunction sources(src: string): { srcSet?: string; sizes?: string } {\n // A card is drawn at most 300 pixels wide: up to three times that on a dense screen.\n const srcSet = src.startsWith(served('/api/file?')) ? imageFallbackSet(src, 320, 960) : undefined;\n return srcSet ? { srcSet, sizes: CARD_IMAGE_SIZES } : {};\n}\n\n/** The same widths in each modern format, as their own addresses: a browser picks by `type`, a CDN keeps one copy of each. */\nfunction typeSources(src: string): Array<{ type: string; srcSet: string }> {\n return src.startsWith(served('/api/file?')) ? imageSources(src, 320, 960) : [];\n}\n\n/* The cards a wide window shows in its first row: their pictures are the\n page's largest paint, so they are asked for first and not left to the\n lazy loader, which waits for layout to say they are in view. */\nconst FIRST_ROW = 4;\n\n/** An authored picture, or a neutral placeholder when that picture is unavailable. Avatar mode keeps the name in the card title and makes its image decorative. */\nfunction CardPicture({ src, avatar, title, first = false }: { src: string | undefined; avatar: boolean; title: string; first?: boolean }) {\n const [broken, setBroken] = useState<string>();\n const shown = src && broken !== src ? src : undefined;\n return (\n <div className={`cards-media${avatar ? ' cards-avatar' : ''}`}>\n {shown ? (\n <picture>\n {typeSources(shown).map((source) => (\n <source key={source.type} type={source.type} srcSet={source.srcSet} sizes={CARD_IMAGE_SIZES} />\n ))}\n <img\n className=\"cards-image\"\n src={shown}\n {...sources(shown)}\n alt=\"\"\n loading={first ? 'eager' : 'lazy'}\n fetchPriority={first ? 'high' : undefined}\n decoding=\"async\"\n onError={() => setBroken(shown)}\n />\n </picture>\n ) : avatar ? (\n <span className={`cards-avatar-mark avatar avatar-c${hueOf(title)}`} aria-hidden=\"true\">\n {initialsOf(title)}\n </span>\n ) : (\n <span className=\"cards-placeholder\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 64 32\" width=\"64\" height=\"32\" focusable=\"false\">\n <rect x=\"8\" y=\"6\" width=\"48\" height=\"20\" rx=\"2\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" />\n <circle cx=\"23\" cy=\"13\" r=\"3\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" />\n <path\n d=\"m12 23 13-8 8 6 7-5 12 7\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </span>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shared-Dh0nHtLZ.js","names":[],"sources":["../../../kit/src/client/components/useRowWindow.ts","../../../kit/src/client/copy/lists/components/CardsList.ts","../../../kit/src/client/components/CardsList.tsx"],"sourcesContent":["/**\n * Windowed drawing for the lists that are not the grid: cards, a queue and a\n * checklist draw every row they are handed, and a page of a thousand cards is\n * a thousand pictures, facts and buttons laid out before the first paint.\n *\n * Only the *drawing* is windowed. The rows handed in are still the whole page\n * — the pager, the record trail, export and a queue's selection all count\n * them as before — and at or under the threshold nothing changes at all, so\n * an ordinary page draws exactly what it drew. Above it, the rows around the\n * viewport are drawn between two spacers that hold the rest of the page's\n * height, measured on the zoom-aware ruler (core.js) because the density\n * zoom puts spacer heights and scroll offsets in layout pixels.\n *\n * A list laid out in columns (cards) is windowed by line: `columns` says how\n * many items stand side by side, and a line is drawn or not as a whole, so\n * the grid never shifts a card into another column as it scrolls.\n */\nimport { useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport { viewportBox, viewportSize } from '../core.js';\n\n/**\n * Past a hundred rows a page is windowed; at or under it nothing is. Lower\n * than the grid's 300 (Grid.tsx) on purpose: a card or a queue row is a\n * dozen grid cells' worth of layout, and the App's largest page size is 250,\n * which the grid's threshold would never reach. Every smaller page size (10\n * to 100) still draws exactly what it drew.\n */\nexport const WINDOW_THRESHOLD = 100;\nconst OVERSCAN = 6;\n\nexport interface RowWindow<E extends HTMLElement> {\n /** The list's element: what is measured, and where the scroller is looked for. */\n ref: (element: E | null) => void;\n windowed: boolean;\n /** The item indexes to draw, in order: every one when not windowed. */\n indices: number[];\n /** Spacer heights before and after the drawn items, in layout pixels; 0 draws no spacer. */\n before: number;\n after: number;\n /** A drawn item's ref, beside `data-index={lineOf(index)}`: how a row's real height is learned. */\n measure: (element: Element | null) => void;\n lineOf: (index: number) => number;\n /** Bring an item into the window, so a keyboard move to a row not yet drawn has a row to land on. */\n scrollTo: (index: number) => void;\n}\n\nexport function useRowWindow<E extends HTMLElement>(\n count: number,\n options: {\n /** How many items stand side by side in the list, read from its element. */\n columns?: (list: E) => number;\n /** A first guess at one line's height, corrected by measurement. */\n estimate?: number;\n } = {},\n): RowWindow<E> {\n const windowed = count > WINDOW_THRESHOLD;\n const [list, setList] = useState<E | null>(null);\n /* The element is kept aside and only becomes state once the page is long\n enough to window: an ordinary list must not pay a second render for it. */\n const element = useRef<E | null>(null);\n const windowedNow = useRef(windowed);\n windowedNow.current = windowed;\n const ref = useCallback((node: E | null) => {\n element.current = node;\n if (windowedNow.current) setList(node);\n }, []);\n useLayoutEffect(() => {\n if (windowed) setList(element.current);\n }, [windowed]);\n const [scroller, setScroller] = useState<HTMLElement | null>(null);\n const [columns, setColumns] = useState(1);\n const [margin, setMargin] = useState(0);\n const [gap, setGap] = useState(0);\n const columnsOf = useRef(options.columns);\n columnsOf.current = options.columns;\n useLayoutEffect(() => {\n if (!windowed || !list) return;\n setScroller((list.closest('.content') as HTMLElement | null) ?? (document.scrollingElement as HTMLElement | null));\n }, [windowed, list]);\n const lines = windowed ? Math.ceil(count / columns) : 0;\n const virtualizer = useVirtualizer({\n count: lines,\n getScrollElement: () => scroller,\n estimateSize: () => options.estimate ?? 72,\n overscan: OVERSCAN,\n scrollMargin: margin,\n gap,\n enabled: windowed && Boolean(scroller),\n // Drawn from the first paint, before the scroller is found and measured: a window's worth, not nothing.\n initialRect: { width: 0, height: windowed ? viewportSize().height : 0 },\n // Measured in layout pixels: the default reads screen pixels, which the density zoom scales.\n measureElement: (element) => viewportBox(element).height,\n });\n /* Where the list starts inside what scrolls, how many items share a line\n and the gap between lines, read after each paint: all three move with the\n room's width and the header above the list, none of them per scroll. */\n useLayoutEffect(() => {\n if (!windowed || !list || !scroller) return;\n const style = getComputedStyle(list);\n const across = Math.max(1, columnsOf.current?.(list) ?? 1);\n if (across !== columns) setColumns(across);\n const rowGap = Number.parseFloat(style.rowGap) || 0;\n if (rowGap !== gap) setGap(rowGap);\n const top =\n scroller === document.scrollingElement\n ? viewportBox(list).top + scroller.scrollTop\n : viewportBox(list).top - viewportBox(scroller).top + scroller.scrollTop;\n if (Math.abs(top - margin) >= 1) setMargin(top);\n });\n\n const lineOf = (index: number) => Math.floor(index / columns);\n if (!windowed) {\n return {\n ref,\n windowed,\n indices: Array.from({ length: count }, (_, i) => i),\n before: 0,\n after: 0,\n measure: () => {},\n lineOf,\n scrollTo: () => {},\n };\n }\n let items: Array<{ index: number; start: number; end: number }> = virtualizer.getVirtualItems();\n /* A scroller that measures no height (not laid out yet, or no layout at\n all) would draw nothing: the first screenful stands in until it does. */\n if (!items.length && lines) {\n const size = options.estimate ?? 72;\n items = Array.from({ length: Math.min(lines, Math.ceil(viewportSize().height / size) + OVERSCAN) }, (_, index) => ({\n index,\n start: margin + index * (size + gap),\n end: margin + index * (size + gap) + size,\n }));\n }\n const total = virtualizer.getTotalSize() || lines * ((options.estimate ?? 72) + gap) - gap;\n const indices: number[] = [];\n for (const item of items) {\n for (let i = item.index * columns; i < Math.min(count, (item.index + 1) * columns); i++) indices.push(i);\n }\n /* A spacer is an item of the list too, so the list's own gap stands beside\n it once more than the lines it replaces: taken off its height. */\n const before = items.length ? Math.max(0, items[0].start - margin - gap) : 0;\n const after = items.length ? Math.max(0, total - (items[items.length - 1].end - margin) - gap) : 0;\n return {\n ref,\n windowed,\n indices,\n before,\n after,\n measure: virtualizer.measureElement,\n lineOf,\n scrollTo: (index) => virtualizer.scrollToIndex(lineOf(index), { align: 'auto' }),\n };\n}\n\n/**\n * The list element's ref, for both the window and another owner of the same\n * element (a hand order's zone). Stable across renders: a fresh callback each\n * paint would hand the window null and the element again every time.\n */\nexport function useListRef<E extends HTMLElement>(win: RowWindow<E>, other?: (element: E | null) => void): (element: E | null) => void {\n const also = useRef(other);\n also.current = other;\n const own = win.ref;\n return useCallback(\n (element: E | null) => {\n also.current?.(element);\n own(element);\n },\n [own],\n );\n}\n","/** What CardsList says, in the lists and grid room (packages/kit/src/client/messages.ts says how the catalog is split). */\nimport { defineMessages } from '../../../messages.js';\n\nexport const say = defineMessages({\n cards: 'Cards',\n});\n","/**\n * A list's rows as cards: a picture at a fixed ratio, the title, one\n * prominent figure, a few facts under their captions and a badge. The whole\n * card opens its record — the title is the one button, stretched over the\n * card, so the card is one tab stop with the house focus ring. One to four\n * columns, by the room's width rather than the window's: a card list in a\n * peek or a split pane reads as a phone would.\n *\n * The picture is a column holding an image address, or the first file of a\n * related file resource, asked once for every card on the page and drawn\n * inline through the file route. Images load lazily into boxes that already\n * have their size, so nothing moves as they arrive. A configured image that\n * is missing shows a placeholder; a card without image configuration starts\n * with its text.\n */\nimport { useMessageLanguage } from './useMessageLanguage.js';\nimport { useState } from 'react';\nimport { useQuery } from '@tanstack/react-query';\nimport { api, columnDisplay, columnLabel, findTable, keyOf, openRecord, quote, state } from '../core.js';\nimport { opensRecord } from '../recordIntent.js';\nimport type { TrailList } from '../recordTrail.js';\nimport { ownedBy } from '../requestIdentity.js';\nimport { primaryKey, type TableMeta } from '@mistriwork/engine/shared/schema.js';\nimport { humanName } from '@mistriwork/engine/shared/model.js';\nimport { cardsImageSort, factWord, type AppCardsList } from '@mistriwork/engine/shared/appCards.js';\nimport { Value } from './Value.js';\nimport { dressFor, hueOf, initialsOf, maskedText, type DisplayRule } from './valueModel.js';\nimport { Icon, type IconName } from './Icon.js';\nimport { useListOrder, type ListOrder } from './useListOrder.js';\nimport { useChangedRows } from '../liveState.js';\nimport { useListRef, useRowWindow } from './useRowWindow.js';\nimport { namedCell } from './listColumn.js';\nimport { say } from '../copy/lists/components/CardsList.js';\nimport { served } from '../addressMount.js';\nimport { CARD_IMAGE_SIZES, imageSources, imageFallbackSet } from '@mistriwork/engine/shared/imageWidths.js';\nimport { inlineFileAddress } from './cardsModel.js';\n\n/** An image address a card may draw: https, or a path on this server. Never data:, javascript: or a protocol-relative host. */\nexport function cardImageAddress(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n const text = value.trim();\n return /^https:\\/\\/[^\\s]+$/i.test(text) || /^\\/(?!\\/)[^\\s]*$/.test(text) ? text : undefined;\n}\n\nconst literal = (value: unknown) => (typeof value === 'number' ? String(value) : quote(String(value)));\n\n/**\n * The lead file of each card's record, when the picture comes from a file\n * resource: one question for the whole page, sorted by the owning record and\n * then the author's order, so the first row seen for a record is its lead.\n * With `about`, the files are the record that reference names (a saved car's\n * car), asked by the reference's value on each row.\n */\nfunction useLeadFiles(\n table: TableMeta,\n spec: AppCardsList,\n rows: Array<Record<string, unknown>>,\n): (row: Record<string, unknown>) => string | undefined {\n const image = typeof spec.image === 'object' ? spec.image : undefined;\n const from = image ? (findTable(image.from) as TableMeta | undefined) : undefined;\n const connection = state.activeConnection ?? null;\n const foreignKeys = (state.schema?.foreignKeys ?? []) as Array<{\n from: { table: string; columns: string[] };\n to: { table: string; columns: string[] };\n }>;\n /* The record the files belong to: the row's own, or the one its `about` reference names. */\n const owner = image?.about\n ? foreignKeys.find((fk) => fk.from.table === table.id && fk.from.columns.length === 1 && fk.from.columns[0] === image.about)?.to.table\n : table.id;\n const reference =\n image && from && owner\n ? foreignKeys.find(\n (fk) => fk.from.table === from.id && fk.to.table === owner && fk.from.columns.length === 1 && fk.from.columns[0] === image.whose,\n )\n : undefined;\n // The row's own column holding that record's key: the key itself, or the reference to it.\n const target = reference ? (image?.about ?? reference.to.columns[0]) : undefined;\n const owners = target ? [...new Set(rows.map((row) => row[target]).filter((value) => value !== null && value !== undefined))] : [];\n const keys = from ? primaryKey(from) : [];\n const sort = image?.sort ? cardsImageSort(image.sort) : undefined;\n const text =\n image && from && owners.length && keys.length\n ? `${from.id} ${image.whose} in (${owners.map(literal).join(', ')}) sort ${image.whose}${sort ? `, ${sort.column} ${sort.direction}` : ''} show ${[...new Set([...keys, image.whose])].join(', ')}`\n : '';\n const answer = useQuery({\n queryKey: ['run', connection ?? '', 'card-images', text],\n enabled: Boolean(text),\n staleTime: 60_000,\n queryFn: async () => {\n const data = (await api('/api/run', {\n q: text,\n limit: 500,\n offset: 0,\n count: 'none',\n ...(connection ? ownedBy(connection) : {}),\n })) as {\n rows?: Array<Record<string, unknown>>;\n errors?: Array<{ message: string }>;\n };\n if (data.errors?.length) throw new Error(data.errors[0].message);\n return data.rows ?? [];\n },\n });\n const lead = new Map<string, string>();\n if (image && from) {\n for (const row of answer.data ?? []) {\n const owner = String(row[image.whose]);\n if (!lead.has(owner))\n lead.set(owner, inlineFileAddress(from.id, Object.fromEntries(keys.map((column) => [column, row[column]])), connection, 'thumb'));\n }\n }\n return (row) => (target ? lead.get(String(row[target])) : undefined);\n}\n\ninterface CardsListProps {\n table: TableMeta;\n rows: Array<Record<string, unknown>>;\n spec: AppCardsList;\n interactive?: boolean;\n /** Lets an otherwise read-only card open its record, without enabling writes or ordering. */\n recordNavigation?: boolean;\n label?: string;\n /** The list these cards are a page of, so a record opened here steps past the page (recordTrail.ts). */\n list?: TrailList;\n /** The view's hand order, where it keeps one (useListOrder.tsx). */\n order?: ListOrder;\n /** A reference column → the fetched path its record's name is read from (appListModel's referenceNames). */\n referenceLabels?: Record<string, string>;\n}\n\n/* Cards stand in columns where the room is wide enough: a drop lands left or right of a card, not above it. */\nconst inColumns = (grid: HTMLElement) => getComputedStyle(grid).gridTemplateColumns.trim().split(/\\s+/).length > 1;\n\nexport function CardsList({\n table,\n rows,\n spec,\n interactive = true,\n recordNavigation = false,\n label,\n list,\n order,\n referenceLabels,\n}: CardsListProps) {\n useMessageLanguage();\n const leadOf = useLeadFiles(table, spec, rows);\n /* The card's name wherever it is said — the title, the record it opens, the order's announcements — masked where its rule says. */\n const titleOf = (row: Record<string, unknown>) => {\n // A title that is a reference is its record's name, where the list fetched one (listColumn.ts).\n const named = namedCell(table, spec.primary, row, referenceLabels);\n if (named.column?.name !== spec.primary) return String(named.value);\n const rule = columnDisplay(table.id, spec.primary) as DisplayRule | null;\n const masked = maskedText(rule, row[spec.primary]);\n if (masked !== undefined && masked !== null) return masked;\n const value = row[spec.primary];\n if (value === null || value === undefined) return say('common.untitled');\n /* The column's display rule says the title as the grid does (an enum's words, a figure's format): a card titled\n each record by the stored key where the rule had words for it. A document, a code and a picture have no words\n to say; they stay the value. */\n const dress = dressFor(rule, value);\n if (!dress || dress.kind === 'json' || dress.kind === 'qr' || dress.kind === 'signature' || dress.kind === 'richText')\n return String(value);\n return 'label' in dress ? dress.label : dress.text;\n };\n const ordering = useListOrder({ table, rows, order, interactive, title: titleOf, across: inColumns });\n // A card another person just changed wears the grid's fading tint (docs/design.md, rule 11).\n const changedRows = useChangedRows(table.id, rows, (row) => keyOf(table, row) as Record<string, unknown> | null);\n const image = spec.image;\n const records: Array<{ key: Record<string, unknown>; label?: string }> = rows.flatMap((row) => {\n const key = keyOf(table, row) as Record<string, unknown> | null;\n return key ? [{ key, label: titleOf(row) }] : [];\n });\n /* A rollup or a label path dresses as what it reads, and a reference the list\n fetched a name for says the name (listColumn.ts). */\n const said = (row: Record<string, unknown>, name: string) => {\n const { column, value } = namedCell(table, name, row, referenceLabels);\n return <Value col={column} value={value} tableId={table.id} compact />;\n };\n // A line or a fact may be a label path (\"dealer_id.name\"): its caption is the App's, else its last step's.\n const caption = (name: string) => (columnLabel(table.id, name) as string | null) ?? humanName(name.split('.').pop() ?? name);\n const present = (row: Record<string, unknown>, name: string) => row[name] !== null && row[name] !== undefined && row[name] !== '';\n const canOpen = interactive || recordNavigation;\n /* A fact as a card says it, on its line under the figure or along the foot: its value, and its own word after it\n (\"1 owner\") or before it (\"Born …\") as its caption for everyone; without one, the caption only assistive\n technology hears. */\n const fact = (row: Record<string, unknown>, name: string, className: string) => {\n const word =\n spec.factLabels && Object.hasOwn(spec.factLabels, name)\n ? factWord(\n spec.factLabels[name],\n row[name],\n columnDisplay(table.id, name) as { format?: string; unit?: string; bar?: boolean } | null,\n )\n : undefined;\n const before = word !== undefined && spec.factLabels![name].position === 'before';\n return (\n <span key={name} className={className}>\n {word === undefined && <span className=\"visually-hidden\">{caption(name)}: </span>}\n {before && <span className=\"cards-summary-word\">{word} </span>}\n {said(row, name)}\n {word !== undefined && !before && <span className=\"cards-summary-word\"> {word}</span>}\n </span>\n );\n };\n // A long page is drawn a line of cards at a time; the page itself is unchanged (useRowWindow.ts).\n const win = useRowWindow<HTMLUListElement>(rows.length, {\n columns: (grid) => getComputedStyle(grid).gridTemplateColumns.trim().split(/\\s+/).length,\n estimate: 280,\n });\n const listRef = useListRef(win, ordering.zone.ref);\n return (\n <div className=\"cards\">\n <ul className=\"cards-grid\" aria-label={label ?? say('cards')} {...ordering.zone} ref={listRef}>\n {win.before > 0 && <li className=\"cards-spacer\" aria-hidden=\"true\" style={{ height: `${win.before}px` }} />}\n {win.indices.map((index) => {\n const row = ordering.rows[index];\n const key = keyOf(table, row) as Record<string, unknown> | null;\n const id = ordering.id(row);\n const title = titleOf(row);\n const src = typeof image === 'string' ? cardImageAddress(row[image]) : leadOf(row);\n const avatar = spec.media === 'avatar';\n /* An icon tile leads the card in a picture's place: it is no picture, so the card keeps its text layout. */\n const tile = spec.media === 'icon';\n const hasMedia = image !== undefined || avatar;\n /* The summary as words, masked where its rule says: the whole of it rides the title attribute, since three lines may not hold it. */\n const description =\n spec.description && present(row, spec.description)\n ? (maskedText(columnDisplay(table.id, spec.description) as DisplayRule | null, row[spec.description]) ??\n String(row[spec.description]))\n : undefined;\n /* The icon column's value as its display rule marks it everywhere else; a value the rule leaves unmapped draws none. */\n const dressed = spec.icon ? dressFor(columnDisplay(table.id, spec.icon) as DisplayRule | null, row[spec.icon]) : null;\n const mark =\n (dressed?.kind === 'tone' || dressed?.kind === 'mark') && dressed.icon\n ? { name: dressed.icon, label: dressed.label, tone: dressed.kind === 'tone' ? dressed.tone : undefined }\n : null;\n const badge =\n spec.badge && row[spec.badge] != null && row[spec.badge] !== '' ? (\n <span className=\"cards-badge\">{said(row, spec.badge)}</span>\n ) : null;\n return (\n <li\n key={key ? JSON.stringify(key) : index}\n className=\"cards-item\"\n {...(win.windowed ? { 'data-index': win.lineOf(index), ref: win.measure } : {})}\n {...(id ? ordering.item(id) : {})}\n onKeyDown={id ? (event) => ordering.keyDown(event, id) : undefined}\n >\n <article\n className={`cards-card${hasMedia ? ' has-media' : ''}${avatar ? ' cards-profile' : ''}${changedRows.has(row) ? ` row-changed-${changedRows.get(row)}` : ''}`}\n >\n {hasMedia && <CardPicture src={src} avatar={avatar} title={title} first={index < FIRST_ROW} />}\n {id && ordering.menu(id)}\n <div className=\"cards-body\">\n {/* The tile holds its place where a value has no mark, so every card's title starts on one line. */}\n {tile && (\n <span className={`cards-tile${mark?.tone ? ` tone-${mark.tone}` : ''}`}>\n {mark && <Icon name={mark.name as IconName} label={mark.label} size={22} />}\n </span>\n )}\n <h3 className=\"cards-title\">\n {mark && !tile && <Icon name={mark.name as IconName} label={mark.label} className=\"cards-icon\" />}\n {canOpen && key ? (\n <button\n type=\"button\"\n className=\"cards-open\"\n data-order-focus=\"\"\n aria-keyshortcuts={ordering.shortcuts}\n data-opens={opensRecord(table.id, key)}\n onClick={() => void openRecord(table.id, key, title, 'push', { records, list })}\n >\n {title}\n </button>\n ) : (\n <span>{title}</span>\n )}\n </h3>\n {description !== undefined && (\n <p className=\"cards-description\" title={description}>\n {description}\n </p>\n )}\n {(!hasMedia || avatar) && badge}\n {spec.value && row[spec.value] != null && <p className=\"cards-value\">{said(row, spec.value)}</p>}\n {spec.facts?.some((name) => present(row, name)) ? (\n <p className=\"cards-summary\">\n {spec.facts.filter((name) => present(row, name)).map((name) => fact(row, name, 'cards-summary-item'))}\n </p>\n ) : null}\n {spec.lines?.length ? (\n <dl className=\"cards-facts\">\n {spec.lines.map((name) => (\n <div key={name} className=\"cards-fact\">\n <dt>{caption(name)}</dt>\n <dd>{said(row, name)}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n </div>\n {/* The foot stands on every card that declares one, filled or not, so a row of cards ends on one line. */}\n {spec.footer?.length ? (\n <p className=\"cards-foot\">\n {spec.footer.some((name) => present(row, name))\n ? spec.footer.filter((name) => present(row, name)).map((name) => fact(row, name, 'cards-foot-item'))\n : '\\u00a0'}\n </p>\n ) : null}\n {hasMedia && !avatar && badge}\n </article>\n </li>\n );\n })}\n {win.after > 0 && <li className=\"cards-spacer\" aria-hidden=\"true\" style={{ height: `${win.after}px` }} />}\n </ul>\n {ordering.live}\n </div>\n );\n}\n\n/**\n * A file's two sizes, for the browser to choose between: the thumbnail\n * (320 wide) where a card is drawn at about 300 pixels, the file itself on a\n * dense screen or a phone's full-width card, where the thumbnail would be\n * soft. A file resource with no thumbnail answers both with the file.\n */\nfunction sources(src: string): { srcSet?: string; sizes?: string } {\n // A card is drawn at most 300 pixels wide: up to three times that on a dense screen.\n const srcSet = src.startsWith(served('/api/file?')) ? imageFallbackSet(src, 320, 960) : undefined;\n return srcSet ? { srcSet, sizes: CARD_IMAGE_SIZES } : {};\n}\n\n/** The same widths in each modern format, as their own addresses: a browser picks by `type`, a CDN keeps one copy of each. */\nfunction typeSources(src: string): Array<{ type: string; srcSet: string }> {\n return src.startsWith(served('/api/file?')) ? imageSources(src, 320, 960) : [];\n}\n\n/* The cards a wide window shows in its first row: their pictures are the\n page's largest paint, so they are asked for first and not left to the\n lazy loader, which waits for layout to say they are in view. */\nconst FIRST_ROW = 4;\n\n/** An authored picture, or a neutral placeholder when that picture is unavailable. Avatar mode keeps the name in the card title and makes its image decorative. */\nfunction CardPicture({ src, avatar, title, first = false }: { src: string | undefined; avatar: boolean; title: string; first?: boolean }) {\n const [broken, setBroken] = useState<string>();\n const shown = src && broken !== src ? src : undefined;\n return (\n <div className={`cards-media${avatar ? ' cards-avatar' : ''}`}>\n {shown ? (\n <picture>\n {typeSources(shown).map((source) => (\n <source key={source.type} type={source.type} srcSet={source.srcSet} sizes={CARD_IMAGE_SIZES} />\n ))}\n <img\n className=\"cards-image\"\n src={shown}\n {...sources(shown)}\n alt=\"\"\n loading={first ? 'eager' : 'lazy'}\n fetchPriority={first ? 'high' : undefined}\n decoding=\"async\"\n onError={() => setBroken(shown)}\n />\n </picture>\n ) : avatar ? (\n <span className={`cards-avatar-mark avatar avatar-c${hueOf(title)}`} aria-hidden=\"true\">\n {initialsOf(title)}\n </span>\n ) : (\n <span className=\"cards-placeholder\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 64 32\" width=\"64\" height=\"32\" focusable=\"false\">\n <rect x=\"8\" y=\"6\" width=\"48\" height=\"20\" rx=\"2\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" />\n <circle cx=\"23\" cy=\"13\" r=\"3\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" />\n <path\n d=\"m12 23 13-8 8 6 7-5 12 7\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </span>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -11,11 +11,11 @@ import { nn as installShell, rn as shell } from "./shared-DJZjSi3J.js";
|
|
|
11
11
|
function installPublicInputs(served) {
|
|
12
12
|
const prior = shell().components;
|
|
13
13
|
installShell({ components: {
|
|
14
|
-
load: (id) => Object.hasOwn(served, id) ? import("./shared-
|
|
15
|
-
builtIn: (name) => import("./shared-
|
|
14
|
+
load: (id) => Object.hasOwn(served, id) ? import("./shared-B-MhiPIN.js").then(({ loadServedComponent }) => loadServedComponent(id, served[id])) : prior.load(id),
|
|
15
|
+
builtIn: (name) => import("./shared-C4LkPCU4.js").then(({ builtInInput }) => builtInInput(name)),
|
|
16
16
|
useKey: prior.useKey
|
|
17
17
|
} });
|
|
18
18
|
}
|
|
19
19
|
export { installPublicInputs };
|
|
20
20
|
|
|
21
|
-
//# sourceMappingURL=shared-
|
|
21
|
+
//# sourceMappingURL=shared-DkOdSpSK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-
|
|
1
|
+
{"version":3,"file":"shared-DkOdSpSK.js","names":[],"sources":["../../src/client/components/sdk/publicInputs.ts"],"sourcesContent":["/**\n * A public form's page draws the App's own input components too: the form's payload says where each one it draws is\n * served (`components`, http/formRoutes.ts), and this installs the shell's loader over them, so the kit's input slot\n * (inputRenderers.tsx) finds them as a signed-in form does through the App payload (appComponents.ts). Loaded only by\n * a form that draws one (FormSurface.tsx formArrives), never in the public first paint; the module itself, and the\n * page's side of `@mistriwork/mistri/ui` it needs, come the first time a field is drawn by one, the platform's control standing\n * in until then.\n */\nimport { installShell, shell } from '@mistriwork/kit/client/shellMode.js';\n\ntype ServedInputs = Readonly<Record<string, { href: string; css?: string }>>;\n\nexport function installPublicInputs(served: ServedInputs): void {\n const prior = shell().components;\n installShell({\n components: {\n load: (id) =>\n Object.hasOwn(served, id)\n ? import('./componentLoader.js').then(({ loadServedComponent }) => loadServedComponent(id, served[id]))\n : prior.load(id),\n builtIn: (name) => import('../builtInInputs.js').then(({ builtInInput }) => builtInInput(name)),\n useKey: prior.useKey,\n },\n });\n}\n"],"mappings":";;;;;;;;;;AAYA,SAAgB,oBAAoB,QAA4B;CAC9D,MAAM,QAAQ,MAAM,CAAC,CAAC;CACtB,aAAa,EACX,YAAY;EACV,OAAO,OACL,OAAO,OAAO,QAAQ,EAAE,IACpB,OAAO,uBAAuB,CAAC,MAAM,EAAE,0BAA0B,oBAAoB,IAAI,OAAO,GAAG,CAAC,IACpG,MAAM,KAAK,EAAE;EACnB,UAAU,SAAS,OAAO,uBAAsB,CAAC,MAAM,EAAE,mBAAmB,aAAa,IAAI,CAAC;EAC9F,QAAQ,MAAM;CAChB,EACF,CAAC;AACH"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { I as quoteValue } from "./shared-poc11xxo.js";
|
|
2
2
|
import { B as isLabelTemplate, D as columnKind, H as writesMask, V as parseLabelTemplate, Y as MENTION_HANDLE } from "./shared-DJZjSi3J.js";
|
|
3
|
-
import { D as refsFrom, d as findTable, i as api, st as ownedBy } from "./shared-
|
|
3
|
+
import { D as refsFrom, d as findTable, i as api, st as ownedBy } from "./shared-BoLbc2WD.js";
|
|
4
4
|
// packages/engine/src/shared/mentionLabel.ts
|
|
5
5
|
/**
|
|
6
6
|
* What a mention's declaration says and whether the catalog can serve it —
|
|
@@ -129,4 +129,4 @@ function readable(source, handle, label) {
|
|
|
129
129
|
}
|
|
130
130
|
export { mentionRows };
|
|
131
131
|
|
|
132
|
-
//# sourceMappingURL=shared-
|
|
132
|
+
//# sourceMappingURL=shared-Dkph5ckT.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-BMsh72Uq.js","names":[],"sources":["../../../engine/src/shared/mentionLabel.ts","../../../engine/src/shared/mentionLookup.ts","../../../kit/src/client/components/mentionRows.ts"],"sourcesContent":["/**\n * What a mention's declaration says and whether the catalog can serve it —\n * the half the validators read (command.ts, app.ts, appResourceBlocks.ts),\n * apart from mentionLookup.ts, the query an @ sends, so a first page that\n * only validates does not carry the lookup (the built suite's cold-visit\n * ceiling holds it).\n */\nimport { isLabelTemplate, parseLabelTemplate, writesMask } from './recordLabel.js';\n\n/** A resource of people or teams an @ names: the resource, the column stored, the column or template shown. */\nexport interface MentionSource {\n from: string;\n by: string;\n labelColumn?: string;\n}\n\n/** The columns a mention's label reads, in order — one column, or each `{column}` of a template — or why it is not one. */\nexport function mentionLabelColumns(label: string): { columns: string[] } | { problem: string } {\n if (!isLabelTemplate(label)) return { columns: [label] };\n if (writesMask(label)) return { problem: `\"${label}\" writes a mask or words itself; a column's display rule says them.` };\n const parsed = parseLabelTemplate(label);\n if ('problem' in parsed) return parsed;\n /* Read with the person's row, in the one query the @ sends: a path through a reference is not in it. */\n const far = parsed.paths.find((path) => path.includes('.') || path.includes('+'));\n if (far) return { problem: `{${far}} in \"${label}\" is not a column; a mention's label names the people's own columns.` };\n return { columns: [...new Set(parsed.paths)] };\n}\n\n/** Why a mention's `by` and `labelColumn` cannot be read from `table`, naming the first column it lacks; undefined when they can. */\nexport function mentionSourceProblem(\n source: MentionSource,\n table: { name: string; columns: readonly { name: string }[] },\n): string | undefined {\n const has = (column: string) => table.columns.some((c) => c.name === column);\n if (!has(source.by)) return `\"by\" names \"${source.by}\", which is not a column of \"${table.name}\".`;\n if (source.labelColumn === undefined) return undefined;\n const read = mentionLabelColumns(source.labelColumn);\n if ('problem' in read) return `\"labelColumn\": ${read.problem}`;\n const missing = read.columns.find((column) => !has(column));\n return missing === undefined\n ? undefined\n : `\"labelColumn\" ${isLabelTemplate(source.labelColumn) ? `\"${source.labelColumn}\" names` : 'names'} \"${missing}\", which is not a column of \"${table.name}\".`;\n}\n","/**\n * Who an @ names, as a rich text column or input declares it:\n * `mentions: { from, by, labelColumn }`. `by` is what is stored (the span's\n * `data-id`, which a `notify` rule finds); `labelColumn` is what the list and the\n * mention say — one column, or a record label template of the people's own\n * columns, `'{first_name} {last_name}'` (recordLabel.ts's grammar), so two\n * Morgans read as \"Morgan Hughes\" and \"Morgan Tran\".\n *\n * One reading for the browser, which asks, and the server, which checks the\n * declaration: the query an @ sends, and the name a row is said by.\n */\nimport { columnKind } from './schema.js';\nimport { quoteValue } from './query.js';\nimport { isLabelTemplate, parseLabelTemplate } from './recordLabel.js';\nimport { mentionLabelColumns, type MentionSource } from './mentionLabel.js';\nimport { MENTION_QUERY_WORDS } from './richText.js';\n\nexport type { MentionSource };\n\n/** A person's name as the label says it, from their row: the column, or the template filled and its spaces tidied. */\nexport function mentionLabelText(source: MentionSource, row: Record<string, unknown>): string {\n const label = source.labelColumn ?? source.by;\n const said = (value: unknown) => (value === null || value === undefined ? '' : String(value));\n if (!isLabelTemplate(label)) return said(row[label]);\n const parsed = parseLabelTemplate(label);\n if ('problem' in parsed) return '';\n return parsed.parts\n .map((part) => ('text' in part ? part.text : said(row[part.path])))\n .join('')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/* A column a word can be looked for in: text the engine compares as text. A UUID is not one — Postgres has no\n ILIKE for it, and nobody types a key's first letters — nor is JSON. */\nconst searchable = (column: { type?: string } | undefined) =>\n !column?.type || (columnKind(column.type) === 'text' && !/uuid|json/i.test(column.type));\n\n/** The most words of what was typed that are each looked for; the rest narrow nothing. */\nconst MOST_WORDS = MENTION_QUERY_WORDS;\n\n/**\n * The query an @ sends for what was typed after it: the people whose label's\n * words — any of them, by its start, in any case — or whose `by` value start\n * with each typed word, shown by the label's columns and sorted by them.\n * `columns` are the resource's, where the catalog has them, so a key or a\n * number is not searched as text.\n */\nexport function mentionQuery(\n source: MentionSource,\n typed: string,\n columns?: readonly { name: string; type?: string }[],\n /** Read beside the label, never looked in: a team's key its link rows name. */\n also: readonly string[] = [],\n): string {\n const read = mentionLabelColumns(source.labelColumn ?? source.by);\n const shown = 'problem' in read ? [source.by] : read.columns;\n const column = (name: string) => columns?.find((c) => c.name === name);\n const looked = [...new Set([...shown, source.by])].filter((name) => searchable(column(name)));\n /* A word's start: the value's, or after a space in it (\"Mary Ann\"). The language spreads the ands over the ors,\n which it caps, so the words looked for are few. */\n const words = typed\n .trim()\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, looked.length > 4 ? 1 : MOST_WORDS);\n const filter = looked.length\n ? words.map(\n (word) =>\n `(${looked.flatMap((name) => [`${name} startswith ${quoteValue(word)}`, `${name} contains ${quoteValue(` ${word}`)}`]).join(' or ')})`,\n )\n : [];\n return [source.from, filter.join(' and '), `show ${[...new Set([source.by, ...shown, ...also])].join(', ')}`, `sort ${shown.join(', ')}`]\n .filter(Boolean)\n .join(' ');\n}\n","/**\n * The rows an @ may name — the half of RichTextField.tsx that a first page does\n * not need (a public form never offers mentions), loaded with the first @.\n */\nimport { api, findTable, refsFrom } from '../core.js';\nimport { ownedBy } from '../requestIdentity.js';\nimport { MENTION_HANDLE } from '@mistriwork/engine/shared/richText.js';\nimport { mentionLabelText, mentionQuery, type MentionSource as MentionResource } from '@mistriwork/engine/shared/mentionLookup.js';\nimport { quoteValue } from '@mistriwork/engine/shared/query.js';\nimport type { MentionPerson } from './RichTextEditor.js';\n\n/* A team's link rows (`through: 'team_member'`): its reference to the teams, and the team column it points at. */\nfunction linkToTeams(source: MentionResource & { through?: string }): { column: string; team: string } | undefined {\n if (!source.through) return undefined;\n const teams = findTable(source.from) as { id?: string } | undefined;\n const links = (refsFrom(source.through) ?? []) as Array<{ from: { columns: string[] }; to: { table: string; columns: string[] } }>;\n const link = links.find((fk) => fk.to.table === teams?.id && fk.from.columns.length === 1);\n return link && { column: link.from.columns[0], team: link.to.columns[0] };\n}\n\n/* Of the teams found, those a link row names: a team with no member has nobody a mention would tell.\n Read as the reader may read the link rows; where they cannot, no team is offered rather than every one. */\nasync function withMembers(\n source: MentionResource & { through?: string },\n rows: Array<Record<string, unknown>>,\n connection: string | null | undefined,\n): Promise<Array<Record<string, unknown>>> {\n const link = linkToTeams(source);\n if (!link) return rows;\n const keys = [...new Set(rows.map((row) => row[link.team]).filter((key) => typeof key === 'string' || typeof key === 'number'))];\n if (!keys.length) return [];\n const q = `${source.through} ${link.column} in (${keys.map(quoteValue).join(', ')}) show ${link.column}`;\n const data = (await api('/api/run', { ...ownedBy(connection ?? null), q, limit: 500, offset: 0, count: 'none' })) as {\n rows?: Array<Record<string, unknown>>;\n };\n const named = new Set((data.rows ?? []).map((row) => String(row[link.column])));\n return rows.filter((row) => named.has(String(row[link.team])));\n}\n\n/* One resource's rows an @ may name, by what was typed: read as the reader may read it. The query and the name a\n row is said by are shared/mentionLookup.ts's, so a label template ('{first_name} {last_name}') reads the same\n here as where the server checks it. */\nexport async function mentionRows(\n source: MentionResource & { through?: string },\n typed: string,\n connection: string | null | undefined,\n kind?: 'group',\n): Promise<MentionPerson[]> {\n const table = findTable(source.from) as { columns?: Array<{ name: string; type?: string }> } | undefined;\n const link = kind === 'group' ? linkToTeams(source) : undefined;\n const q = mentionQuery(source, typed, table?.columns, link ? [link.team] : []);\n /* Teams narrowed by their members are read a few more, so eight can still be offered once the empty ones go. */\n const data = (await api('/api/run', { ...ownedBy(connection ?? null), q, limit: link ? 24 : 8, offset: 0, count: 'none' })) as {\n rows?: Array<Record<string, unknown>>;\n };\n const rows = link ? (await withMembers(source, data.rows ?? [], connection)).slice(0, 8) : (data.rows ?? []);\n return rows.flatMap((row) => {\n const id = row[source.by];\n const handle =\n typeof id === 'string' && MENTION_HANDLE.test(id) ? id : typeof id === 'number' && Number.isSafeInteger(id) ? String(id) : undefined;\n if (handle === undefined) return [];\n const label = mentionLabelText(source, row) || handle;\n return [{ id: handle, label, ...(kind || !readable(source, handle, label) ? {} : { handle }), ...(kind ? { kind } : {}) }];\n });\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n/* Whether the stored handle is one a person would say after the @ (\"@morgan\"), shown beside the name to tell two\n Morgans apart. A key the database handed out — the people's primary key, a number, a UUID — is not: the list\n said \"Morgan Hughes @3f2a…\". The name is what the mention reads either way. */\nfunction readable(source: { from: string; by: string }, handle: string, label: string): boolean {\n const people = findTable(source.from) as { columns?: Array<{ name: string; primaryKey?: boolean }> } | undefined;\n if (people?.columns?.some((column) => column.name === source.by && column.primaryKey)) return false;\n /* Nor is an email: the list would say \"Vic Ramos @vramos@example.test\"; the label's words tell people apart. */\n return handle !== label && !/^\\d+$/.test(handle) && !UUID.test(handle) && !handle.includes('@');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shared-Dkph5ckT.js","names":[],"sources":["../../../engine/src/shared/mentionLabel.ts","../../../engine/src/shared/mentionLookup.ts","../../../kit/src/client/components/mentionRows.ts"],"sourcesContent":["/**\n * What a mention's declaration says and whether the catalog can serve it —\n * the half the validators read (command.ts, app.ts, appResourceBlocks.ts),\n * apart from mentionLookup.ts, the query an @ sends, so a first page that\n * only validates does not carry the lookup (the built suite's cold-visit\n * ceiling holds it).\n */\nimport { isLabelTemplate, parseLabelTemplate, writesMask } from './recordLabel.js';\n\n/** A resource of people or teams an @ names: the resource, the column stored, the column or template shown. */\nexport interface MentionSource {\n from: string;\n by: string;\n labelColumn?: string;\n}\n\n/** The columns a mention's label reads, in order — one column, or each `{column}` of a template — or why it is not one. */\nexport function mentionLabelColumns(label: string): { columns: string[] } | { problem: string } {\n if (!isLabelTemplate(label)) return { columns: [label] };\n if (writesMask(label)) return { problem: `\"${label}\" writes a mask or words itself; a column's display rule says them.` };\n const parsed = parseLabelTemplate(label);\n if ('problem' in parsed) return parsed;\n /* Read with the person's row, in the one query the @ sends: a path through a reference is not in it. */\n const far = parsed.paths.find((path) => path.includes('.') || path.includes('+'));\n if (far) return { problem: `{${far}} in \"${label}\" is not a column; a mention's label names the people's own columns.` };\n return { columns: [...new Set(parsed.paths)] };\n}\n\n/** Why a mention's `by` and `labelColumn` cannot be read from `table`, naming the first column it lacks; undefined when they can. */\nexport function mentionSourceProblem(\n source: MentionSource,\n table: { name: string; columns: readonly { name: string }[] },\n): string | undefined {\n const has = (column: string) => table.columns.some((c) => c.name === column);\n if (!has(source.by)) return `\"by\" names \"${source.by}\", which is not a column of \"${table.name}\".`;\n if (source.labelColumn === undefined) return undefined;\n const read = mentionLabelColumns(source.labelColumn);\n if ('problem' in read) return `\"labelColumn\": ${read.problem}`;\n const missing = read.columns.find((column) => !has(column));\n return missing === undefined\n ? undefined\n : `\"labelColumn\" ${isLabelTemplate(source.labelColumn) ? `\"${source.labelColumn}\" names` : 'names'} \"${missing}\", which is not a column of \"${table.name}\".`;\n}\n","/**\n * Who an @ names, as a rich text column or input declares it:\n * `mentions: { from, by, labelColumn }`. `by` is what is stored (the span's\n * `data-id`, which a `notify` rule finds); `labelColumn` is what the list and the\n * mention say — one column, or a record label template of the people's own\n * columns, `'{first_name} {last_name}'` (recordLabel.ts's grammar), so two\n * Morgans read as \"Morgan Hughes\" and \"Morgan Tran\".\n *\n * One reading for the browser, which asks, and the server, which checks the\n * declaration: the query an @ sends, and the name a row is said by.\n */\nimport { columnKind } from './schema.js';\nimport { quoteValue } from './query.js';\nimport { isLabelTemplate, parseLabelTemplate } from './recordLabel.js';\nimport { mentionLabelColumns, type MentionSource } from './mentionLabel.js';\nimport { MENTION_QUERY_WORDS } from './richText.js';\n\nexport type { MentionSource };\n\n/** A person's name as the label says it, from their row: the column, or the template filled and its spaces tidied. */\nexport function mentionLabelText(source: MentionSource, row: Record<string, unknown>): string {\n const label = source.labelColumn ?? source.by;\n const said = (value: unknown) => (value === null || value === undefined ? '' : String(value));\n if (!isLabelTemplate(label)) return said(row[label]);\n const parsed = parseLabelTemplate(label);\n if ('problem' in parsed) return '';\n return parsed.parts\n .map((part) => ('text' in part ? part.text : said(row[part.path])))\n .join('')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/* A column a word can be looked for in: text the engine compares as text. A UUID is not one — Postgres has no\n ILIKE for it, and nobody types a key's first letters — nor is JSON. */\nconst searchable = (column: { type?: string } | undefined) =>\n !column?.type || (columnKind(column.type) === 'text' && !/uuid|json/i.test(column.type));\n\n/** The most words of what was typed that are each looked for; the rest narrow nothing. */\nconst MOST_WORDS = MENTION_QUERY_WORDS;\n\n/**\n * The query an @ sends for what was typed after it: the people whose label's\n * words — any of them, by its start, in any case — or whose `by` value start\n * with each typed word, shown by the label's columns and sorted by them.\n * `columns` are the resource's, where the catalog has them, so a key or a\n * number is not searched as text.\n */\nexport function mentionQuery(\n source: MentionSource,\n typed: string,\n columns?: readonly { name: string; type?: string }[],\n /** Read beside the label, never looked in: a team's key its link rows name. */\n also: readonly string[] = [],\n): string {\n const read = mentionLabelColumns(source.labelColumn ?? source.by);\n const shown = 'problem' in read ? [source.by] : read.columns;\n const column = (name: string) => columns?.find((c) => c.name === name);\n const looked = [...new Set([...shown, source.by])].filter((name) => searchable(column(name)));\n /* A word's start: the value's, or after a space in it (\"Mary Ann\"). The language spreads the ands over the ors,\n which it caps, so the words looked for are few. */\n const words = typed\n .trim()\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, looked.length > 4 ? 1 : MOST_WORDS);\n const filter = looked.length\n ? words.map(\n (word) =>\n `(${looked.flatMap((name) => [`${name} startswith ${quoteValue(word)}`, `${name} contains ${quoteValue(` ${word}`)}`]).join(' or ')})`,\n )\n : [];\n return [source.from, filter.join(' and '), `show ${[...new Set([source.by, ...shown, ...also])].join(', ')}`, `sort ${shown.join(', ')}`]\n .filter(Boolean)\n .join(' ');\n}\n","/**\n * The rows an @ may name — the half of RichTextField.tsx that a first page does\n * not need (a public form never offers mentions), loaded with the first @.\n */\nimport { api, findTable, refsFrom } from '../core.js';\nimport { ownedBy } from '../requestIdentity.js';\nimport { MENTION_HANDLE } from '@mistriwork/engine/shared/richText.js';\nimport { mentionLabelText, mentionQuery, type MentionSource as MentionResource } from '@mistriwork/engine/shared/mentionLookup.js';\nimport { quoteValue } from '@mistriwork/engine/shared/query.js';\nimport type { MentionPerson } from './RichTextEditor.js';\n\n/* A team's link rows (`through: 'team_member'`): its reference to the teams, and the team column it points at. */\nfunction linkToTeams(source: MentionResource & { through?: string }): { column: string; team: string } | undefined {\n if (!source.through) return undefined;\n const teams = findTable(source.from) as { id?: string } | undefined;\n const links = (refsFrom(source.through) ?? []) as Array<{ from: { columns: string[] }; to: { table: string; columns: string[] } }>;\n const link = links.find((fk) => fk.to.table === teams?.id && fk.from.columns.length === 1);\n return link && { column: link.from.columns[0], team: link.to.columns[0] };\n}\n\n/* Of the teams found, those a link row names: a team with no member has nobody a mention would tell.\n Read as the reader may read the link rows; where they cannot, no team is offered rather than every one. */\nasync function withMembers(\n source: MentionResource & { through?: string },\n rows: Array<Record<string, unknown>>,\n connection: string | null | undefined,\n): Promise<Array<Record<string, unknown>>> {\n const link = linkToTeams(source);\n if (!link) return rows;\n const keys = [...new Set(rows.map((row) => row[link.team]).filter((key) => typeof key === 'string' || typeof key === 'number'))];\n if (!keys.length) return [];\n const q = `${source.through} ${link.column} in (${keys.map(quoteValue).join(', ')}) show ${link.column}`;\n const data = (await api('/api/run', { ...ownedBy(connection ?? null), q, limit: 500, offset: 0, count: 'none' })) as {\n rows?: Array<Record<string, unknown>>;\n };\n const named = new Set((data.rows ?? []).map((row) => String(row[link.column])));\n return rows.filter((row) => named.has(String(row[link.team])));\n}\n\n/* One resource's rows an @ may name, by what was typed: read as the reader may read it. The query and the name a\n row is said by are shared/mentionLookup.ts's, so a label template ('{first_name} {last_name}') reads the same\n here as where the server checks it. */\nexport async function mentionRows(\n source: MentionResource & { through?: string },\n typed: string,\n connection: string | null | undefined,\n kind?: 'group',\n): Promise<MentionPerson[]> {\n const table = findTable(source.from) as { columns?: Array<{ name: string; type?: string }> } | undefined;\n const link = kind === 'group' ? linkToTeams(source) : undefined;\n const q = mentionQuery(source, typed, table?.columns, link ? [link.team] : []);\n /* Teams narrowed by their members are read a few more, so eight can still be offered once the empty ones go. */\n const data = (await api('/api/run', { ...ownedBy(connection ?? null), q, limit: link ? 24 : 8, offset: 0, count: 'none' })) as {\n rows?: Array<Record<string, unknown>>;\n };\n const rows = link ? (await withMembers(source, data.rows ?? [], connection)).slice(0, 8) : (data.rows ?? []);\n return rows.flatMap((row) => {\n const id = row[source.by];\n const handle =\n typeof id === 'string' && MENTION_HANDLE.test(id) ? id : typeof id === 'number' && Number.isSafeInteger(id) ? String(id) : undefined;\n if (handle === undefined) return [];\n const label = mentionLabelText(source, row) || handle;\n return [{ id: handle, label, ...(kind || !readable(source, handle, label) ? {} : { handle }), ...(kind ? { kind } : {}) }];\n });\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n/* Whether the stored handle is one a person would say after the @ (\"@morgan\"), shown beside the name to tell two\n Morgans apart. A key the database handed out — the people's primary key, a number, a UUID — is not: the list\n said \"Morgan Hughes @3f2a…\". The name is what the mention reads either way. */\nfunction readable(source: { from: string; by: string }, handle: string, label: string): boolean {\n const people = findTable(source.from) as { columns?: Array<{ name: string; primaryKey?: boolean }> } | undefined;\n if (people?.columns?.some((column) => column.name === source.by && column.primaryKey)) return false;\n /* Nor is an email: the list would say \"Vic Ramos @vramos@example.test\"; the label's words tell people apart. */\n return handle !== label && !/^\\d+$/.test(handle) && !UUID.test(handle) && !handle.includes('@');\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,SAAgB,oBAAoB,OAA4D;CAC9F,IAAI,CAAC,gBAAgB,KAAK,GAAG,OAAO,EAAE,SAAS,CAAC,KAAK,EAAE;CACvD,IAAI,WAAW,KAAK,GAAG,OAAO,EAAE,SAAS,IAAI,MAAM,qEAAqE;CACxH,MAAM,SAAS,mBAAmB,KAAK;CACvC,IAAI,aAAa,QAAQ,OAAO;CAEhC,MAAM,MAAM,OAAO,MAAM,MAAM,SAAS,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,GAAG,CAAC;CAChF,IAAI,KAAK,OAAO,EAAE,SAAS,IAAI,IAAI,QAAQ,MAAM,sEAAsE;CACvH,OAAO,EAAE,SAAS,CAAC,GAAG,IAAI,IAAI,OAAO,KAAK,CAAC,EAAE;AAC/C;;;;;;;;;;;;;;ACNA,SAAgB,iBAAiB,QAAuB,KAAsC;CAC5F,MAAM,QAAQ,OAAO,eAAe,OAAO;CAC3C,MAAM,QAAQ,UAAoB,UAAU,QAAQ,UAAU,KAAA,IAAY,KAAK,OAAO,KAAK;CAC3F,IAAI,CAAC,gBAAgB,KAAK,GAAG,OAAO,KAAK,IAAI,MAAM;CACnD,MAAM,SAAS,mBAAmB,KAAK;CACvC,IAAI,aAAa,QAAQ,OAAO;CAChC,OAAO,OAAO,MACX,KAAK,SAAU,UAAU,OAAO,KAAK,OAAO,KAAK,IAAI,KAAK,KAAK,CAAE,CAAC,CAClE,KAAK,EAAE,CAAC,CACR,QAAQ,QAAQ,GAAG,CAAC,CACpB,KAAK;AACV;AAIA,IAAM,cAAc,WAClB,CAAC,QAAQ,QAAS,WAAW,OAAO,IAAI,MAAM,UAAU,CAAC,aAAa,KAAK,OAAO,IAAI;;AAGxF,IAAM,aAAA;;;;;;;;AASN,SAAgB,aACd,QACA,OACA,SAEA,OAA0B,CAAC,GACnB;CACR,MAAM,OAAO,oBAAoB,OAAO,eAAe,OAAO,EAAE;CAChE,MAAM,QAAQ,aAAa,OAAO,CAAC,OAAO,EAAE,IAAI,KAAK;CACrD,MAAM,UAAU,SAAiB,SAAS,MAAM,MAAM,EAAE,SAAS,IAAI;CACrE,MAAM,SAAS,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,OAAO,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,SAAS,WAAW,OAAO,IAAI,CAAC,CAAC;CAG5F,MAAM,QAAQ,MACX,KAAK,CAAC,CACN,MAAM,KAAK,CAAC,CACZ,OAAO,OAAO,CAAC,CACf,MAAM,GAAG,OAAO,SAAS,IAAI,IAAI,UAAU;CAC9C,MAAM,SAAS,OAAO,SAClB,MAAM,KACH,SACC,IAAI,OAAO,SAAS,SAAS,CAAC,GAAG,KAAK,cAAc,WAAW,IAAI,KAAK,GAAG,KAAK,YAAY,WAAW,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,EACxI,IACA,CAAC;CACL,OAAO;EAAC,OAAO;EAAM,OAAO,KAAK,OAAO;EAAG,QAAQ,CAAC,mBAAG,IAAI,IAAI;GAAC,OAAO;GAAI,GAAG;GAAO,GAAG;EAAI,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;EAAK,QAAQ,MAAM,KAAK,IAAI;CAAG,CAAC,CACtI,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;AACb;;;;;;AC/DA,SAAS,YAAY,QAA8F;CACjH,IAAI,CAAC,OAAO,SAAS,OAAO,KAAA;CAC5B,MAAM,QAAQ,UAAU,OAAO,IAAI;CAEnC,MAAM,QADS,SAAS,OAAO,OAAO,KAAK,CAAC,EAAA,CACzB,MAAM,OAAO,GAAG,GAAG,UAAU,OAAO,MAAM,GAAG,KAAK,QAAQ,WAAW,CAAC;CACzF,OAAO,QAAQ;EAAE,QAAQ,KAAK,KAAK,QAAQ;EAAI,MAAM,KAAK,GAAG,QAAQ;CAAG;AAC1E;AAIA,eAAe,YACb,QACA,MACA,YACyC;CACzC,MAAM,OAAO,YAAY,MAAM;CAC/B,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,OAAO,CAAC,GAAG,IAAI,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,CAAC,CAAC,QAAQ,QAAQ,OAAO,QAAQ,YAAY,OAAO,QAAQ,QAAQ,CAAC,CAAC;CAC/H,IAAI,CAAC,KAAK,QAAQ,OAAO,CAAC;CAC1B,MAAM,IAAI,GAAG,OAAO,QAAQ,GAAG,KAAK,OAAO,OAAO,KAAK,IAAI,UAAU,CAAC,CAAC,KAAK,IAAI,EAAE,SAAS,KAAK;CAChG,MAAM,OAAQ,MAAM,IAAI,YAAY;EAAE,GAAG,QAAQ,cAAc,IAAI;EAAG;EAAG,OAAO;EAAK,QAAQ;EAAG,OAAO;CAAO,CAAC;CAG/G,MAAM,QAAQ,IAAI,KAAK,KAAK,QAAQ,CAAC,EAAA,CAAG,KAAK,QAAQ,OAAO,IAAI,KAAK,OAAO,CAAC,CAAC;CAC9E,OAAO,KAAK,QAAQ,QAAQ,MAAM,IAAI,OAAO,IAAI,KAAK,KAAK,CAAC,CAAC;AAC/D;AAKA,eAAsB,YACpB,QACA,OACA,YACA,MAC0B;CAC1B,MAAM,QAAQ,UAAU,OAAO,IAAI;CACnC,MAAM,OAAO,SAAS,UAAU,YAAY,MAAM,IAAI,KAAA;CACtD,MAAM,IAAI,aAAa,QAAQ,OAAO,OAAO,SAAS,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;CAE7E,MAAM,OAAQ,MAAM,IAAI,YAAY;EAAE,GAAG,QAAQ,cAAc,IAAI;EAAG;EAAG,OAAO,OAAO,KAAK;EAAG,QAAQ;EAAG,OAAO;CAAO,CAAC;CAIzH,QADa,QAAQ,MAAM,YAAY,QAAQ,KAAK,QAAQ,CAAC,GAAG,UAAU,EAAA,CAAG,MAAM,GAAG,CAAC,IAAK,KAAK,QAAQ,CAAC,EAAA,CAC9F,SAAS,QAAQ;EAC3B,MAAM,KAAK,IAAI,OAAO;EACtB,MAAM,SACJ,OAAO,OAAO,YAAY,eAAe,KAAK,EAAE,IAAI,KAAK,OAAO,OAAO,YAAY,OAAO,cAAc,EAAE,IAAI,OAAO,EAAE,IAAI,KAAA;EAC7H,IAAI,WAAW,KAAA,GAAW,OAAO,CAAC;EAClC,MAAM,QAAQ,iBAAiB,QAAQ,GAAG,KAAK;EAC/C,OAAO,CAAC;GAAE,IAAI;GAAQ;GAAO,GAAI,QAAQ,CAAC,SAAS,QAAQ,QAAQ,KAAK,IAAI,CAAC,IAAI,EAAE,OAAO;GAAI,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;EAAG,CAAC;CAC3H,CAAC;AACH;AAEA,IAAM,OAAO;AAIb,SAAS,SAAS,QAAsC,QAAgB,OAAwB;CAE9F,IADe,UAAU,OAAO,IAC5B,CAAA,EAAQ,SAAS,MAAM,WAAW,OAAO,SAAS,OAAO,MAAM,OAAO,UAAU,GAAG,OAAO;CAE9F,OAAO,WAAW,SAAS,CAAC,QAAQ,KAAK,MAAM,KAAK,CAAC,KAAK,KAAK,MAAM,KAAK,CAAC,OAAO,SAAS,GAAG;AAChG"}
|