popup.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import {Command, Session} from "../session";
  2. import {randomid} from "../utils";
  3. import {outputSpecToHtml} from "../models/output"
  4. import {CommandHandler} from "./base";
  5. export class PopupHandler implements CommandHandler {
  6. session: Session;
  7. accept_command = ['popup', 'close_popup'];
  8. private body = $('body');
  9. constructor(session: Session) {
  10. this.session = session;
  11. }
  12. static current_elem: JQuery<HTMLElement> = null; // 当前正在处于显示中的弹窗元素,表示页面的期望状态
  13. handle_message(msg: Command) {
  14. if (PopupHandler.current_elem) {
  15. // @ts-ignore
  16. PopupHandler.current_elem.modal('hide');
  17. PopupHandler.current_elem = null;
  18. }
  19. if (msg.command == 'popup') {
  20. // 显示弹窗前,先关闭其他弹窗
  21. // @ts-ignore
  22. $('.modal').modal('hide');
  23. let elem = PopupHandler.get_element(msg.spec);
  24. this.body.append(elem);
  25. // 弹窗关闭后就立即销毁
  26. elem.on('hidden.bs.modal', function (e) {
  27. elem.remove();
  28. });
  29. elem.on('shown.bs.modal', function (e) {
  30. // 弹窗显示后,有新弹窗出现或当前弹窗被关闭,则立即关闭当前弹窗
  31. if (elem != PopupHandler.current_elem || !PopupHandler.current_elem) {
  32. // @ts-ignore
  33. elem.modal('hide');
  34. }
  35. });
  36. // @ts-ignore
  37. elem.modal('show');
  38. PopupHandler.current_elem = elem;
  39. } else if (msg.command == 'close_popup') {
  40. // @ts-ignore
  41. $('.modal').modal('hide');
  42. PopupHandler.current_elem = null;
  43. }
  44. }
  45. static get_element(spec: { title: string, content: any[], closable: boolean, implicit_close: boolean, size: string }) {
  46. // https://v4.bootcss.com/docs/components/modal/#options
  47. const tpl = `<div class="modal fade" {{^implicit_close}}data-backdrop="static"{{/implicit_close}} aria-labelledby="model-id-{{ dom_id }}" tabindex="-1" role="dialog" aria-hidden="true">
  48. <div class="modal-dialog modal-dialog-scrollable {{#large}}modal-lg{{/large}} {{#small}}modal-sm{{/small}}" role="document">
  49. <div class="modal-content">
  50. <div class="modal-header">
  51. <h5 class="modal-title" id="model-id-{{ mid }}">{{ title }}</h5>
  52. {{#closable}}
  53. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  54. <span aria-hidden="true">&times;</span>
  55. </button>
  56. {{/closable}}
  57. </div>
  58. <div class="modal-body markdown-body" id="{{ dom_id }}">
  59. {{#content}}
  60. {{& pywebio_output_parse}}
  61. {{/content}}
  62. </div>
  63. <!--
  64. <div class="modal-footer">
  65. <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
  66. <button type="button" class="btn btn-primary">Submit</button>
  67. </div>
  68. -->
  69. </div>
  70. </div>
  71. </div>`;
  72. if (!spec.closable)
  73. spec.implicit_close = false;
  74. let pywebio_output_parse = function () {
  75. if (this.type)
  76. return outputSpecToHtml(this);
  77. else
  78. return outputSpecToHtml({type: 'text', content: this, inline: true});
  79. };
  80. let html = Mustache.render(tpl, {
  81. ...spec, // 字段: content, title, size, implicit_close, closable, dom_id
  82. large: spec.size == 'large',
  83. small: spec.size == 'small',
  84. pywebio_output_parse: pywebio_output_parse
  85. });
  86. return $(html as string);
  87. }
  88. }