@fieldia/viewer 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog.mjs","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/dialog.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAoD,MAAM,UAAU,CAAC;AAkCxG,IAAI,OAAO,GAAG,CAAC,CAAC;AAEhB,MAAM,SAAS,GAAG,oGAAoG,CAAC;AAEvH,MAAM,UAAU,cAAc,CAAC,OAA0B;IACvD,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,EAAE,aAAa,IAAI,QAAQ,CAAC;IACzD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC,IAAI,CAAC;IAChD,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,YAAY,GAAG,CAAC,WAAY,CAAC,WAAW,CAAC,CAAC,CAAE,GAAG,CAAC,aAA6B,CAAC,CAAC,CAAC,IAAI,CAAC;IACrH,MAAM,MAAM,GAAG,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAC/E,MAAM,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,CAAwC,GAAM,EAAE,QAAgC,EAAE,EAAE,IAAa,EAAE,EAAE;QAChH,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QACvC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACrF,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;QACnD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9F,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,GAAG,CAAC,CAAC;IAClH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACvF,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,6BAA6B,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7G,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE;QACtB,4EAA4E;QAC5E,KAAK,EAAE,mCAAmC,OAAO,CAAC,IAAI,IAAI,QAAQ,EAAE;QACpE,cAAc,EAAE,OAAO,CAAC,IAAI,IAAI,WAAW;QAC3C,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,MAAM;QACpB,iBAAiB,EAAE,KAAK,CAAC,EAAE;QAC3B,QAAQ,EAAE,IAAI;KACf,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,GAAG;QAAE,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACtD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,+CAA+C,EAAE,CAAC,CAAC,CAAC;IACzI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IAClD,GAAG,CAAC,gBAAgB,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,4CAA4C,EAAE,CAAC,CAAC;IACtF,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE3B,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IACrE,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,OAAO,CAAC,SAAS;QAAE,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAEpG,MAAM,UAAU,GAAG,GAAG,EAAE,CACtB,CAAC,GAAG,GAAG,CAAC,gBAAgB,CAAc,SAAS,CAAC,CAAC,CAAC,MAAM,CACtD,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAE,OAA6B,CAAC,QAAQ,CAC/F,CAAC;IACJ,2FAA2F;IAC3F,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAc,wEAAwE,CAAC,CAAC;IACnI,CAAC,UAAU,EAAE,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;QACnC,IAAI,GAAG,CAAC,aAAa,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC;YAAE,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC;IAC3F,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,CAAC,KAAc,EAAE,EAAE;YAC/B,IAAI,IAAI;gBAAE,OAAO;YACjB,IAAI,GAAG,IAAI,CAAC;YACZ,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrC,8EAA8E;YAC9E,MAAM,MAAM,GAAqB,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAgB,EAAE,CAAC;YAC7G,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,QAAQ,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,EAAE,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC;QACF,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YAC7C,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;YAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAClF,SAAS,CAAC,QAAQ,GAAG,KAAK,CAAC;YAC3B,IAAI,QAAQ;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACtD,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1D,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YACxC,yEAAyE;YACzE,IAAI,KAAK,CAAC,gBAAgB;gBAAE,OAAO;YACnC,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC3B,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,KAAK,CAAC,KAAK,CAAC,CAAC;YACf,CAAC;iBAAM,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;gBAC/B,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;gBACzB,IAAI,CAAC,GAAG,CAAC,MAAM;oBAAE,OAAO;gBACxB,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;gBACrB,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBACjC,IAAI,KAAK,CAAC,QAAQ,IAAI,GAAG,CAAC,aAAa,KAAK,KAAK,EAAE,CAAC;oBAClD,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,CAAC;qBAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,GAAG,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;oBACzD,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,sHAAsH;AACtH,SAAS,WAAW,CAAC,IAA0B,EAAE,MAA+B,EAAE,SAA8C,EAAE,MAAqB;IACrJ,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC;IAClD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAgB,CAAC;QAChD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,OAAO,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO;QACpC,IAAI,GAAG,GAAG,CAAC;QACX,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CACpB,CAAC,IAAI,EAAE,EAAE;YACP,IAAI,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,KAAK,GAAG;gBAAE,OAAO,CAAC,+BAA+B;YACvG,OAAO,GAAG,IAAI,CAAC;YACf,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjD,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC;oBAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC3H,CAAC;YACD,OAAO,GAAG,KAAK,CAAC;YAChB,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC;QAChD,CAAC,EACD,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAaD;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAA4B;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,EAAE,aAAa,IAAI,QAAQ,CAAC;IACzD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC,IAAI,CAAC;IAChD,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,YAAY,GAAG,CAAC,WAAY,CAAC,WAAW,CAAC,CAAC,CAAE,GAAG,CAAC,aAA6B,CAAC,CAAC,CAAC,IAAI,CAAC;IACrH,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;IACpD,MAAM,EAAE,GAAG,aAAa,EAAE,OAAO,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,CAAwC,GAAM,EAAE,QAAgC,EAAE,EAAE,IAAa,EAAE,EAAE;QAChH,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QACvC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACrF,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;QACnD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9F,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,GAAG,CAAC,CAAC;IAClH,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9K,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,SAAS,EAAE,iBAAiB,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACrH,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACrF,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE;QACtB,KAAK,EAAE,wDAAwD;QAC/D,cAAc,EAAE,OAAO,CAAC,IAAI,IAAI,WAAW;QAC3C,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,MAAM;QACpB,iBAAiB,EAAE,KAAK,CAAC,EAAE;KAC5B,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,GAAG;QAAE,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACtD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,oCAAoC,EAAE,CAAC,CAAC;IAC1E,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,+CAA+C,EAAE,CAAC,CAAC;IACrF,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpB,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,4CAA4C,EAAE,CAAC,CAAC;IACtF,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC3B,KAAK,CAAC,KAAK,EAAE,CAAC;IAEd,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,KAAK,GAAoB,EAAE,CAAC;QAChC,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC;QAChB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,KAAgD,CAAC;QACrD,MAAM,KAAK,GAAG,CAAC,MAA4B,EAAE,EAAE;YAC7C,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,KAAK,EAAE,CAAC;YACR,QAAQ,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,EAAE,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC;QACF,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,IAAI,CAAC,eAAe,CAClB,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBACzB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;gBACnL,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;gBACtD,OAAO,MAAM,CAAC;YAChB,CAAC,CAAC,CACH,CAAC;YACF,IAAI,CAAC,KAAK,CAAC,MAAM;gBAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;YACnF,IAAI,MAAM,IAAI,CAAC;gBAAE,KAAK,CAAC,YAAY,CAAC,uBAAuB,EAAE,GAAG,EAAE,WAAW,MAAM,EAAE,CAAC,CAAC;;gBAClF,KAAK,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC;QACtD,CAAC,CAAC;QACF,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,IAAI,GAAG,EAAE,KAAK,CAAC;YACrB,KAAK,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;gBAC5B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAqB,CAAC,CAAC;gBACzF,IAAI,IAAI,KAAK,KAAK;oBAAE,OAAO,CAAC,uCAAuC;gBACnE,KAAK,GAAG,OAAO,CAAC;gBAChB,MAAM,GAAG,CAAC,CAAC,CAAC;gBACZ,IAAI,EAAE,CAAC;YACT,CAAC,EAAE,GAAG,CAAC,CAAC;QACV,CAAC,CAAC;QACF,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACtC,KAAK,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YAC1C,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;gBACzD,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,KAAK,CAAC,MAAM;oBAAE,OAAO;gBAC1B,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvF,IAAI,EAAE,CAAC;gBACP,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;YAChE,CAAC;iBAAM,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;gBAChD,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YACxC,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC3B,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,KAAK,CAAC,IAAI,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;QACH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { type Form, type FormOptions, type Page, type Locale } from '@fieldia/core';
2
- import { type PreferenceStore, type WidgetFactory } from '@fieldia/widgets';
2
+ import { type IconSet, type PreferenceStore, type WidgetFactory } from '@fieldia/widgets';
3
3
  export type Skin = 'underline' | 'outlined';
4
4
  /** Every word the viewer shows, so a page can be translated. `{n}`, `{total}` and `{time}` are filled in. */
5
5
  export interface ViewerLabels {
@@ -10,6 +10,10 @@ export interface ViewerLabels {
10
10
  submit: string;
11
11
  next: string;
12
12
  back: string;
13
+ /** Passing over an optional step. */
14
+ skip: string;
15
+ /** The list of a wizard's steps, for screen readers. */
16
+ steps: string;
13
17
  stepOf: string;
14
18
  submitted: string;
15
19
  submitAnother: string;
@@ -19,6 +23,11 @@ export interface ViewerLabels {
19
23
  ok: string;
20
24
  cancel: string;
21
25
  loading: string;
26
+ /** A form in a dialog: the button that saves it and closes the dialog, and the × that closes it. */
27
+ saveClose: string;
28
+ close: string;
29
+ /** The × on an alert that may be dismissed. */
30
+ dismiss: string;
22
31
  }
23
32
  export declare const VIEWER_LABELS: Record<Locale, ViewerLabels>;
24
33
  /** English, kept under its old name. */
@@ -45,11 +54,27 @@ export interface ViewerOptions extends Omit<FormOptions, 'page'> {
45
54
  confirm?: (message: string) => Promise<boolean>;
46
55
  /** Where a person's choices about the page's look are kept, such as a table's columns. The browser's storage by default. */
47
56
  preferences?: PreferenceStore;
57
+ /** False when something around the page saves it, such as a dialog: the page's own Save, Discard and Submit stay hidden. */
58
+ showActions?: boolean;
59
+ /**
60
+ * Pages for the models links point to, so a link can show its record in a
61
+ * dialog: Create and edit…, and the button that opens the linked record.
62
+ */
63
+ relatedPages?: Record<string, Page> | ((model: string) => Page | null | undefined);
64
+ /**
65
+ * The app's own icons, or replacements for Fieldia's, by the name a page
66
+ * gives them: the inside of a 24×24 SVG, drawn with lines in the text colour.
67
+ */
68
+ icons?: IconSet;
48
69
  }
49
70
  export interface ViewerHandle {
50
71
  readonly form: Form;
51
72
  readonly element: HTMLElement;
52
73
  setSkin(skin: Skin): void;
74
+ /** Save the record, or take the focus to the first problem when the form refuses. */
75
+ save(): Promise<boolean>;
76
+ /** Check the form without saving, taking the focus to the first problem. */
77
+ check(): boolean;
53
78
  destroy(): void;
54
79
  }
55
80
  /**
@@ -1,5 +1,5 @@
1
1
  import { type Form, type FormOptions, type Page, type Locale } from '@fieldia/core';
2
- import { type PreferenceStore, type WidgetFactory } from '@fieldia/widgets';
2
+ import { type IconSet, type PreferenceStore, type WidgetFactory } from '@fieldia/widgets';
3
3
  export type Skin = 'underline' | 'outlined';
4
4
  /** Every word the viewer shows, so a page can be translated. `{n}`, `{total}` and `{time}` are filled in. */
5
5
  export interface ViewerLabels {
@@ -10,6 +10,10 @@ export interface ViewerLabels {
10
10
  submit: string;
11
11
  next: string;
12
12
  back: string;
13
+ /** Passing over an optional step. */
14
+ skip: string;
15
+ /** The list of a wizard's steps, for screen readers. */
16
+ steps: string;
13
17
  stepOf: string;
14
18
  submitted: string;
15
19
  submitAnother: string;
@@ -19,6 +23,11 @@ export interface ViewerLabels {
19
23
  ok: string;
20
24
  cancel: string;
21
25
  loading: string;
26
+ /** A form in a dialog: the button that saves it and closes the dialog, and the × that closes it. */
27
+ saveClose: string;
28
+ close: string;
29
+ /** The × on an alert that may be dismissed. */
30
+ dismiss: string;
22
31
  }
23
32
  export declare const VIEWER_LABELS: Record<Locale, ViewerLabels>;
24
33
  /** English, kept under its old name. */
@@ -45,11 +54,27 @@ export interface ViewerOptions extends Omit<FormOptions, 'page'> {
45
54
  confirm?: (message: string) => Promise<boolean>;
46
55
  /** Where a person's choices about the page's look are kept, such as a table's columns. The browser's storage by default. */
47
56
  preferences?: PreferenceStore;
57
+ /** False when something around the page saves it, such as a dialog: the page's own Save, Discard and Submit stay hidden. */
58
+ showActions?: boolean;
59
+ /**
60
+ * Pages for the models links point to, so a link can show its record in a
61
+ * dialog: Create and edit…, and the button that opens the linked record.
62
+ */
63
+ relatedPages?: Record<string, Page> | ((model: string) => Page | null | undefined);
64
+ /**
65
+ * The app's own icons, or replacements for Fieldia's, by the name a page
66
+ * gives them: the inside of a 24×24 SVG, drawn with lines in the text colour.
67
+ */
68
+ icons?: IconSet;
48
69
  }
49
70
  export interface ViewerHandle {
50
71
  readonly form: Form;
51
72
  readonly element: HTMLElement;
52
73
  setSkin(skin: Skin): void;
74
+ /** Save the record, or take the focus to the first problem when the form refuses. */
75
+ save(): Promise<boolean>;
76
+ /** Check the form without saving, taking the focus to the first problem. */
77
+ check(): boolean;
53
78
  destroy(): void;
54
79
  }
55
80
  /**
package/src/lib/viewer.js CHANGED
@@ -4,6 +4,7 @@ exports.DEFAULT_LABELS = exports.VIEWER_LABELS = void 0;
4
4
  exports.mountViewer = mountViewer;
5
5
  const core_1 = require("@fieldia/core");
6
6
  const widgets_1 = require("@fieldia/widgets");
7
+ const dialog_1 = require("./dialog");
7
8
  exports.VIEWER_LABELS = {
8
9
  en: {
9
10
  save: 'Save',
@@ -13,6 +14,8 @@ exports.VIEWER_LABELS = {
13
14
  submit: 'Submit',
14
15
  next: 'Next',
15
16
  back: 'Back',
17
+ skip: 'Skip',
18
+ steps: 'Steps',
16
19
  stepOf: 'Step {n} of {total}',
17
20
  submitted: 'Thank you. Your answers were sent.',
18
21
  submitAnother: 'Submit another response',
@@ -22,6 +25,9 @@ exports.VIEWER_LABELS = {
22
25
  ok: 'OK',
23
26
  cancel: 'Cancel',
24
27
  loading: 'Loading…',
28
+ saveClose: 'Save & Close',
29
+ close: 'Close',
30
+ dismiss: 'Dismiss',
25
31
  },
26
32
  ar: {
27
33
  save: 'حفظ',
@@ -31,6 +37,8 @@ exports.VIEWER_LABELS = {
31
37
  submit: 'إرسال',
32
38
  next: 'التالي',
33
39
  back: 'رجوع',
40
+ skip: 'تخطٍّ',
41
+ steps: 'الخطوات',
34
42
  stepOf: 'الخطوة {n} من {total}',
35
43
  submitted: 'شكرًا لك. تم إرسال إجاباتك.',
36
44
  submitAnother: 'إرسال إجابة أخرى',
@@ -40,6 +48,9 @@ exports.VIEWER_LABELS = {
40
48
  ok: 'موافق',
41
49
  cancel: 'إلغاء',
42
50
  loading: 'جارٍ التحميل…',
51
+ saveClose: 'حفظ وإغلاق',
52
+ close: 'إغلاق',
53
+ dismiss: 'إخفاء',
43
54
  },
44
55
  de: {
45
56
  save: 'Speichern',
@@ -49,6 +60,8 @@ exports.VIEWER_LABELS = {
49
60
  submit: 'Absenden',
50
61
  next: 'Weiter',
51
62
  back: 'Zurück',
63
+ skip: 'Überspringen',
64
+ steps: 'Schritte',
52
65
  stepOf: 'Schritt {n} von {total}',
53
66
  submitted: 'Vielen Dank. Ihre Antworten wurden gesendet.',
54
67
  submitAnother: 'Weitere Antwort senden',
@@ -58,6 +71,9 @@ exports.VIEWER_LABELS = {
58
71
  ok: 'OK',
59
72
  cancel: 'Abbrechen',
60
73
  loading: 'Wird geladen…',
74
+ saveClose: 'Speichern und schließen',
75
+ close: 'Schließen',
76
+ dismiss: 'Ausblenden',
61
77
  },
62
78
  fr: {
63
79
  save: 'Enregistrer',
@@ -67,6 +83,8 @@ exports.VIEWER_LABELS = {
67
83
  submit: 'Envoyer',
68
84
  next: 'Suivant',
69
85
  back: 'Retour',
86
+ skip: 'Passer',
87
+ steps: 'Étapes',
70
88
  stepOf: 'Étape {n} sur {total}',
71
89
  submitted: 'Merci. Vos réponses ont été envoyées.',
72
90
  submitAnother: 'Envoyer une autre réponse',
@@ -76,6 +94,9 @@ exports.VIEWER_LABELS = {
76
94
  ok: 'OK',
77
95
  cancel: 'Annuler',
78
96
  loading: 'Chargement…',
97
+ saveClose: 'Enregistrer et fermer',
98
+ close: 'Fermer',
99
+ dismiss: 'Masquer',
79
100
  },
80
101
  };
81
102
  /** English, kept under its old name. */
@@ -95,6 +116,7 @@ function mountViewer(host, options) {
95
116
  const page = checked.page;
96
117
  const locale = options.locale ?? 'en';
97
118
  const preferences = options.preferences ?? (0, widgets_1.browserPreferences)();
119
+ const dialogs = pageDialogs(options);
98
120
  const ownsForm = !options.form;
99
121
  const form = options.form ?? (0, core_1.createForm)({ ...options, page, messages: options.messages ?? core_1.MESSAGES[locale] });
100
122
  const labels = { ...exports.VIEWER_LABELS[locale], ...options.labels };
@@ -114,7 +136,12 @@ function mountViewer(host, options) {
114
136
  node.append(...children);
115
137
  return node;
116
138
  }
117
- const root = el('form', { class: 'fd-form', novalidate: '', 'data-fd-skin': options.skin ?? 'underline', dir, lang: options.locale });
139
+ /** Words with the icon a page names before them, when there is such an icon. */
140
+ function withIcon(name, text) {
141
+ const icon = (0, widgets_1.drawIcon)(doc, name, options.icons);
142
+ return icon ? [icon, text] : [text];
143
+ }
144
+ const root = el('form', { class: 'fd-form', novalidate: '', 'data-fd-skin': options.skin ?? 'underline', dir, lang: options.locale, 'data-max-width': page.maxWidth });
118
145
  const confirm = options.confirm ?? dialogConfirm;
119
146
  // ---- the parts -------------------------------------------------------
120
147
  function fieldItem(node) {
@@ -124,7 +151,7 @@ function mountViewer(host, options) {
124
151
  if (node.colspan)
125
152
  wrapper.style.setProperty('--fd-span', String(node.colspan));
126
153
  const label = el('label', { class: 'fd-label', id: `${id}-label`, for: id }, node.label ?? def.label);
127
- const widget = (0, widgets_1.createWidget)({ form, name: node.field, field: def, node, id, document: doc, labels: widgetLabels, preferences, locale }, options.widgets);
154
+ const widget = (0, widgets_1.createWidget)({ form, name: node.field, field: def, node, id, document: doc, labels: widgetLabels, preferences, locale, dialogs }, options.widgets);
128
155
  if (!['INPUT', 'SELECT', 'TEXTAREA'].includes(widget.element.tagName)) {
129
156
  // `for` stays: a custom field that puts the id on its own input is
130
157
  // labelled natively. Only a wrapper with a role (a radio group, say) may
@@ -168,7 +195,7 @@ function mountViewer(host, options) {
168
195
  });
169
196
  }
170
197
  function buttonItem(node) {
171
- const button = el('button', { type: 'button', class: `fd-button fd-button-${node.style ?? 'secondary'}`, 'data-node': node.id }, node.label);
198
+ const button = el('button', { type: 'button', class: `fd-button fd-button-${node.style ?? 'secondary'}`, 'data-node': node.id }, ...withIcon(node.icon, node.label));
172
199
  button.addEventListener('click', async () => {
173
200
  if (node.confirm && !(await confirm(node.confirm)))
174
201
  return;
@@ -193,7 +220,17 @@ function mountViewer(host, options) {
193
220
  }
194
221
  function grid(children, columns = 1) {
195
222
  const box = el('div', { class: 'fd-grid' });
196
- box.style.setProperty('--fd-columns', String(columns));
223
+ box.style.setProperty('--fd-columns', String((0, core_1.wideColumns)(columns)));
224
+ // Counts given for the narrower widths replace the skin's own stacking there.
225
+ if (typeof columns === 'object') {
226
+ for (const width of ['medium', 'narrow']) {
227
+ const count = columns[width];
228
+ if (count === undefined)
229
+ continue;
230
+ box.setAttribute(`data-columns-${width}`, String(count));
231
+ box.style.setProperty(`--fd-columns-${width}`, String(count));
232
+ }
233
+ }
197
234
  box.append(...children.map(item));
198
235
  return box;
199
236
  }
@@ -202,10 +239,10 @@ function mountViewer(host, options) {
202
239
  function sectionItem(node) {
203
240
  const section = el('fieldset', { class: 'fd-section', 'data-node': node.id });
204
241
  const description = node.description ? el('p', { class: 'fd-section-description' }, node.description) : null;
205
- const content = grid(node.children, node.columns ?? 1);
242
+ const content = grid(node.children, node.columns);
206
243
  if (node.title && node.collapsible) {
207
244
  content.id = uid(`${node.id}-content`);
208
- const toggle = el('button', { type: 'button', class: 'fd-section-toggle', 'aria-controls': content.id }, el('span', { class: 'fd-section-chevron', 'aria-hidden': 'true' }), node.title);
245
+ const toggle = el('button', { type: 'button', class: 'fd-section-toggle', 'aria-controls': content.id }, el('span', { class: 'fd-section-chevron', 'aria-hidden': 'true' }), ...withIcon(node.icon, node.title));
209
246
  let open = node.collapsed !== true;
210
247
  const show = (next) => {
211
248
  open = next;
@@ -221,7 +258,7 @@ function mountViewer(host, options) {
221
258
  section.append(el('legend', { class: 'fd-section-title' }, toggle));
222
259
  }
223
260
  else if (node.title) {
224
- section.append(el('legend', { class: 'fd-section-title' }, node.title));
261
+ section.append(el('legend', { class: 'fd-section-title' }, ...withIcon(node.icon, node.title)));
225
262
  }
226
263
  if (description)
227
264
  section.append(description);
@@ -239,7 +276,7 @@ function mountViewer(host, options) {
239
276
  const parts = node.children.map((tab) => {
240
277
  const tabId = uid(`${tab.id}-tab`);
241
278
  const panelId = uid(`${tab.id}-panel`);
242
- const button = el('button', { type: 'button', class: 'fd-tab', role: 'tab', id: tabId, 'aria-controls': panelId, 'data-node': tab.id }, tab.label);
279
+ const button = el('button', { type: 'button', class: 'fd-tab', role: 'tab', id: tabId, 'aria-controls': panelId, 'data-node': tab.id }, ...withIcon(tab.icon, tab.label));
243
280
  const panel = el('div', { class: 'fd-tabpanel', role: 'tabpanel', id: panelId, 'aria-labelledby': tabId }, grid(tab.children));
244
281
  button.addEventListener('click', () => {
245
282
  picked = active = tab.id;
@@ -375,6 +412,12 @@ function mountViewer(host, options) {
375
412
  discard.hidden = !dirty;
376
413
  });
377
414
  }
415
+ if (options.showActions === false)
416
+ return el('div', {}, box);
417
+ if (page.actionsPosition === 'top') {
418
+ actions.classList.add('fd-actions-top');
419
+ return el('div', {}, actions, box);
420
+ }
378
421
  return el('div', {}, box, actions);
379
422
  }
380
423
  function wizardLayout(node) {
@@ -382,22 +425,44 @@ function mountViewer(host, options) {
382
425
  const progressText = el('div', { class: 'fd-progress-text' });
383
426
  const bar = el('span');
384
427
  box.append(el('div', { class: 'fd-progress' }, progressText, el('div', { class: 'fd-progress-bar' }, bar)));
428
+ // A clickable wizard lists its steps: back to any, forward past those that are complete.
429
+ const list = node.clickable ? el('ol', { class: 'fd-steps-list' }) : null;
430
+ const stepButtons = new Map();
431
+ if (list) {
432
+ for (const step of node.children) {
433
+ const go = el('button', { type: 'button', class: 'fd-step-link' }, ...withIcon(step.icon, step.label));
434
+ go.addEventListener('click', () => {
435
+ if (form.goTo(step.id))
436
+ focusStep();
437
+ else
438
+ focusFirstProblem();
439
+ });
440
+ stepButtons.set(step.id, go);
441
+ list.append(el('li', {}, go));
442
+ }
443
+ box.append(el('nav', { class: 'fd-steps', 'aria-label': labels.steps }, list));
444
+ }
385
445
  const panels = node.children.map((step) => {
386
446
  const panel = el('section', { class: 'fd-step', 'data-node': step.id, 'aria-labelledby': uid(`${step.id}-title`) });
387
- panel.append(el('h2', { class: 'fd-step-title', id: uid(`${step.id}-title`) }, step.label));
447
+ panel.append(el('h2', { class: 'fd-step-title', id: uid(`${step.id}-title`) }, ...withIcon(step.icon, step.label)));
388
448
  if (step.description)
389
449
  panel.append(el('p', { class: 'fd-section-description' }, step.description));
390
450
  panel.append(grid(step.children));
391
451
  box.append(panel);
392
452
  return { step, panel };
393
453
  });
394
- const back = el('button', { type: 'button', class: 'fd-button' }, labels.back);
454
+ const back = el('button', { type: 'button', class: 'fd-button' }, node.backLabel ?? labels.back);
455
+ const skip = el('button', { type: 'button', class: 'fd-button fd-button-link' }, labels.skip);
395
456
  const forward = el('button', { type: 'submit', class: 'fd-button fd-button-primary' });
396
- box.append(el('div', { class: 'fd-wizard-nav' }, back, el('span', { class: 'fd-spacer' }), status, forward));
457
+ box.append(el('div', { class: 'fd-wizard-nav' }, back, el('span', { class: 'fd-spacer' }), status, skip, forward));
397
458
  back.addEventListener('click', () => {
398
459
  if (form.back())
399
460
  focusStep();
400
461
  });
462
+ skip.addEventListener('click', () => {
463
+ if (form.skip())
464
+ focusStep();
465
+ });
401
466
  const isLast = () => {
402
467
  const steps = form.steps();
403
468
  return steps.indexOf(form.getState().step ?? '') === steps.length - 1;
@@ -421,8 +486,20 @@ function mountViewer(host, options) {
421
486
  bar.style.width = `${((index + 1) / Math.max(1, steps.length)) * 100}%`;
422
487
  for (const { step, panel } of panels)
423
488
  panel.hidden = step.id !== state.step;
489
+ const last = index === steps.length - 1;
424
490
  back.hidden = index === 0;
425
- forward.textContent = index === steps.length - 1 ? labels.submit : labels.next;
491
+ skip.hidden = last || !node.children.find((step) => step.id === state.step)?.optional;
492
+ forward.textContent = last ? node.finishLabel ?? labels.submit : node.nextLabel ?? labels.next;
493
+ for (const [id, go] of stepButtons) {
494
+ const at = steps.indexOf(id);
495
+ go.parentElement.hidden = at === -1;
496
+ go.classList.toggle('fd-step-done', at !== -1 && at < index);
497
+ go.classList.toggle('fd-step-skipped', state.skipped.includes(id));
498
+ if (id === state.step)
499
+ go.setAttribute('aria-current', 'step');
500
+ else
501
+ go.removeAttribute('aria-current');
502
+ }
426
503
  });
427
504
  return box;
428
505
  }
@@ -432,14 +509,25 @@ function mountViewer(host, options) {
432
509
  const save = el('button', { type: 'submit', class: 'fd-button fd-button-primary' }, labels.save);
433
510
  const discard = el('button', { type: 'button', class: 'fd-button' }, labels.discard);
434
511
  discard.addEventListener('click', () => form.reset());
435
- actions.append(save, discard, ...(node.buttons ?? []).map(buttonItem), status);
512
+ // Save and Discard lead the header bar, or close the sheet when the page puts them at its foot.
513
+ const atFoot = page.actionsPosition === 'bottom';
514
+ const foot = el('div', { class: 'fd-actions fd-actions-end fd-sheet-foot' });
515
+ if (options.showActions === false)
516
+ actions.append(...(node.buttons ?? []).map(buttonItem));
517
+ else if (atFoot) {
518
+ actions.append(...(node.buttons ?? []).map(buttonItem));
519
+ foot.append(status, discard, save);
520
+ }
521
+ else
522
+ actions.append(save, discard, ...(node.buttons ?? []).map(buttonItem), status);
436
523
  updaters.push((state) => {
437
524
  const dirty = state.dirty.length > 0 && state.status !== 'saving';
438
525
  save.hidden = !dirty;
439
526
  discard.hidden = !dirty;
440
527
  });
441
528
  const header = el('div', { class: 'fd-header' }, actions);
442
- if (node.statusbar)
529
+ const underTitle = node.statusbar?.position === 'title';
530
+ if (node.statusbar && !underTitle)
443
531
  header.append(statusbar(node.statusbar));
444
532
  const card = el('div', { class: 'fd-card' });
445
533
  if (node.ribbon) {
@@ -452,7 +540,9 @@ function mountViewer(host, options) {
452
540
  const stats = el('div', { class: 'fd-stats' });
453
541
  for (const stat of node.statButtons) {
454
542
  const value = el('span', { class: 'fd-stat-value' });
455
- const button = el('button', { type: 'button', class: 'fd-stat', 'data-node': stat.id }, value, el('span', { class: 'fd-stat-label' }, stat.label));
543
+ const icon = (0, widgets_1.drawIcon)(doc, stat.icon, options.icons);
544
+ const words = el('span', { class: 'fd-stat-words' }, value, el('span', { class: 'fd-stat-label' }, stat.label));
545
+ const button = el('button', { type: 'button', class: 'fd-stat', 'data-node': stat.id }, ...(icon ? [icon, words] : [words]));
456
546
  button.addEventListener('click', () => void form.runAction(stat.id));
457
547
  updaters.push((state) => {
458
548
  button.hidden = form.node(stat.id).invisible;
@@ -464,15 +554,45 @@ function mountViewer(host, options) {
464
554
  card.append(stats);
465
555
  }
466
556
  for (const alert of node.alerts ?? []) {
467
- const box = el('div', { class: `fd-alert fd-tone-${alert.tone ?? 'info'}`, role: 'status', 'data-node': alert.id }, alert.message);
468
- hideWhen(box, alert.id);
557
+ const box = el('div', { class: `fd-alert fd-tone-${alert.tone ?? 'info'}`, role: 'status', 'data-node': alert.id }, el('span', { class: 'fd-alert-message' }, alert.message));
558
+ if (alert.dismissible) {
559
+ // Closed, it stays closed while the page is open, whatever its condition does.
560
+ let dismissed = false;
561
+ const close = el('button', { type: 'button', class: 'fd-alert-close', 'aria-label': labels.dismiss }, '×');
562
+ close.addEventListener('click', () => {
563
+ dismissed = true;
564
+ box.hidden = true;
565
+ });
566
+ box.append(close);
567
+ updaters.push(() => {
568
+ box.hidden = dismissed || form.node(alert.id).invisible;
569
+ });
570
+ }
571
+ else
572
+ hideWhen(box, alert.id);
469
573
  card.append(box);
470
574
  }
575
+ if (node.badges?.length) {
576
+ const badges = el('div', { class: 'fd-badges' });
577
+ for (const badge of node.badges) {
578
+ const chip = el('span', { class: `fd-badge fd-tone-${badge.tone ?? 'muted'}`, 'data-node': badge.id }, ...withIcon(badge.icon, badge.label));
579
+ hideWhen(chip, badge.id);
580
+ badges.append(chip);
581
+ }
582
+ updaters.push(() => {
583
+ badges.hidden = [...badges.children].every((chip) => chip.hidden);
584
+ });
585
+ card.append(badges);
586
+ }
471
587
  if (node.title) {
472
588
  const title = el('div', { class: 'fd-title' });
589
+ if (node.title.above?.length)
590
+ title.append(el('div', { class: 'fd-title-above' }, ...node.title.above.map(fieldItem)));
473
591
  title.append(fieldItem({ type: 'field', id: '#title', field: node.title.field, placeholder: node.title.placeholder }));
474
592
  if (node.title.subtitleField)
475
593
  title.append(fieldItem({ type: 'field', id: '#subtitle', field: node.title.subtitleField }));
594
+ if (node.title.below?.length)
595
+ title.append(el('div', { class: 'fd-title-below' }, ...node.title.below.map(fieldItem)));
476
596
  const row = el('div', { class: 'fd-title-row' }, title);
477
597
  if (node.title.avatarField) {
478
598
  const avatar = fieldItem({ type: 'field', id: '#avatar', field: node.title.avatarField });
@@ -481,7 +601,15 @@ function mountViewer(host, options) {
481
601
  }
482
602
  card.append(row);
483
603
  }
604
+ if (node.statusbar && underTitle)
605
+ card.append(el('div', { class: 'fd-title-statusbar' }, statusbar(node.statusbar)));
484
606
  card.append(grid(node.children));
607
+ if (atFoot && options.showActions !== false) {
608
+ card.append(foot);
609
+ updaters.push((state) => {
610
+ foot.hidden = state.dirty.length === 0 && !status.textContent;
611
+ });
612
+ }
485
613
  const layout = el('div', { class: 'fd-sheet-layout' }, card);
486
614
  if (node.sidePanel) {
487
615
  layout.classList.add('fd-has-side');
@@ -497,7 +625,7 @@ function mountViewer(host, options) {
497
625
  if (config.visibleStates)
498
626
  barOptions['visibleStates'] = config.visibleStates;
499
627
  const node = { type: 'field', id: '#statusbar', field: config.field, widget: 'statusbar', options: barOptions };
500
- const widget = (0, widgets_1.createWidget)({ form, name: config.field, field: def, node, id: uid('statusbar'), document: doc, labels: widgetLabels, preferences, locale }, options.widgets);
628
+ const widget = (0, widgets_1.createWidget)({ form, name: config.field, field: def, node, id: uid('statusbar'), document: doc, labels: widgetLabels, preferences, locale, dialogs }, options.widgets);
501
629
  updaters.push((state) => widget.update({ value: state.values[config.field], values: state.values, readonly: form.node('#statusbar').readonly, required: false, invalid: false }));
502
630
  return widget.element;
503
631
  }
@@ -560,6 +688,18 @@ function mountViewer(host, options) {
560
688
  setSkin(skin) {
561
689
  root.setAttribute('data-fd-skin', skin);
562
690
  },
691
+ async save() {
692
+ const saved = await form.save();
693
+ if (!saved)
694
+ focusFirstProblem();
695
+ return saved;
696
+ },
697
+ check() {
698
+ const valid = form.validate();
699
+ if (!valid)
700
+ focusFirstProblem();
701
+ return valid;
702
+ },
563
703
  destroy() {
564
704
  unsubscribe();
565
705
  for (const cleanup of cleanups)
@@ -570,4 +710,85 @@ function mountViewer(host, options) {
570
710
  },
571
711
  };
572
712
  }
713
+ /** The field a page's records are named by: its title, or "name", or its first line of text. */
714
+ function nameFieldOf(page) {
715
+ if (page.layout.type === 'sheet' && page.layout.title?.field)
716
+ return page.layout.title.field;
717
+ if (page.fields['name'])
718
+ return 'name';
719
+ return Object.entries(page.fields).find(([, def]) => def.type === 'char')?.[0] ?? null;
720
+ }
721
+ /** A form made of fields, for values edited in a dialog (a line, for one). */
722
+ function valuesPage(fields, title, readonly) {
723
+ const shown = Object.fromEntries(Object.entries(fields).map(([name, def]) => [name, readonly ? { ...def, readonly: true } : def]));
724
+ return {
725
+ fieldia: '0.1',
726
+ id: 'values',
727
+ title,
728
+ data: { kind: 'record', model: 'values' },
729
+ fields: shown,
730
+ layout: {
731
+ type: 'sections',
732
+ id: 'values',
733
+ children: [{ type: 'section', id: 'values-section', columns: 2, children: Object.keys(shown).map((name) => ({ type: 'field', id: `values-${name}`, field: name })) }],
734
+ },
735
+ };
736
+ }
737
+ /** Only finding and making linked records: values edited in a dialog are no record of the source's to load, save or recalculate. */
738
+ function lookupsOf(source) {
739
+ if (!source)
740
+ return undefined;
741
+ return {
742
+ ...(source.search ? { search: (request) => source.search(request) } : {}),
743
+ ...(source.create ? { create: (request) => source.create(request) } : {}),
744
+ };
745
+ }
746
+ /** The dialogs a page's widgets may open, made from the viewer's own options. */
747
+ function pageDialogs(options) {
748
+ const pageFor = (model) => (typeof options.relatedPages === 'function' ? options.relatedPages(model) : options.relatedPages?.[model]) ?? null;
749
+ // A dialog's page looks and reads like the page that opened it, and can open dialogs of its own.
750
+ const shared = {
751
+ locale: options.locale,
752
+ skin: options.skin,
753
+ dir: options.dir,
754
+ widgets: options.widgets,
755
+ preferences: options.preferences,
756
+ relatedPages: options.relatedPages,
757
+ };
758
+ return {
759
+ canOpen: (model) => pageFor(model) !== null,
760
+ async openRecord(model, request) {
761
+ const related = pageFor(model);
762
+ if (!related)
763
+ return null;
764
+ const nameField = nameFieldOf(related);
765
+ const result = await (0, dialog_1.openFormDialog)({
766
+ ...shared,
767
+ page: related,
768
+ dataSource: options.dataSource,
769
+ recordId: request.recordId ?? null,
770
+ values: request.name && nameField ? { [nameField]: request.name } : undefined,
771
+ title: request.title,
772
+ size: 'large',
773
+ });
774
+ if (!result.saved || result.recordId === null)
775
+ return null;
776
+ const name = nameField ? result.values[nameField] : null;
777
+ return { id: result.recordId, label: typeof name === 'string' && name ? name : request.name ?? request.title };
778
+ },
779
+ searchMore: (request) => (0, dialog_1.openSearchDialog)({ title: request.title, search: request.search, locale: options.locale, skin: options.skin, dir: options.dir }),
780
+ async editValues(request) {
781
+ const result = await (0, dialog_1.openFormDialog)({
782
+ ...shared,
783
+ page: valuesPage(request.fields, request.title, request.readonly === true),
784
+ dataSource: lookupsOf(options.dataSource),
785
+ values: request.values,
786
+ title: request.title,
787
+ mode: 'values',
788
+ ...(request.recompute ? { recompute: request.recompute } : {}),
789
+ });
790
+ return result.saved ? result.values : null;
791
+ },
792
+ };
793
+ }
573
794
  //# sourceMappingURL=viewer.js.map