HackmdEditor.jsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import loggerFactory from '@alias/logger';
  4. import connectToChild from 'penpal/lib/connectToChild';
  5. const DEBUG_PENPAL = false;
  6. const logger = loggerFactory('growi:HackmdEditor');
  7. export default class HackmdEditor extends React.PureComponent {
  8. constructor(props) {
  9. super(props);
  10. this.hackmd = null;
  11. this.initHackmdWithPenpal = this.initHackmdWithPenpal.bind(this);
  12. this.notifyBodyChangesHandler = this.notifyBodyChangesHandler.bind(this);
  13. this.saveWithShortcutHandler = this.saveWithShortcutHandler.bind(this);
  14. }
  15. componentDidMount() {
  16. // append iframe with penpal
  17. this.initHackmdWithPenpal();
  18. }
  19. async initHackmdWithPenpal() {
  20. const _this = this; // for in methods scope
  21. const iframe = document.createElement('iframe');
  22. iframe.src = `${this.props.hackmdUri}/${this.props.pageIdOnHackmd}?both`;
  23. this.iframeContainer.appendChild(iframe);
  24. const connection = connectToChild({
  25. iframe,
  26. methods: { // expose methods to HackMD
  27. notifyBodyChanges(document) {
  28. _this.notifyBodyChangesHandler(document);
  29. },
  30. saveWithShortcut(document) {
  31. _this.saveWithShortcutHandler(document);
  32. },
  33. },
  34. timeout: 15000,
  35. debug: DEBUG_PENPAL,
  36. });
  37. try {
  38. const child = await connection.promise;
  39. this.hackmd = child;
  40. if (this.props.initializationMarkdown != null) {
  41. child.setValueOnInit(this.props.initializationMarkdown);
  42. }
  43. }
  44. catch (err) {
  45. logger.error(err);
  46. if (this.props.onPenpalErrorOccured != null) {
  47. this.props.onPenpalErrorOccured(err);
  48. }
  49. }
  50. }
  51. /**
  52. * return markdown document of HackMD
  53. * @return {Promise<string>}
  54. */
  55. getValue() {
  56. return this.hackmd.getValue();
  57. }
  58. setValue(newValue) {
  59. this.hackmd.setValue(newValue);
  60. }
  61. notifyBodyChangesHandler(body) {
  62. // dispatch onChange() when there is difference from 'initializationMarkdown' props
  63. if (this.props.onChange != null && body !== this.props.initializationMarkdown) {
  64. this.props.onChange(body);
  65. }
  66. }
  67. saveWithShortcutHandler(document) {
  68. if (this.props.onSaveWithShortcut != null) {
  69. this.props.onSaveWithShortcut(document);
  70. }
  71. }
  72. render() {
  73. return (
  74. // will be rendered in componentDidMount
  75. <div id="iframe-hackmd-container" ref={(c) => { this.iframeContainer = c }}></div>
  76. );
  77. }
  78. }
  79. HackmdEditor.propTypes = {
  80. hackmdUri: PropTypes.string.isRequired,
  81. pageIdOnHackmd: PropTypes.string.isRequired,
  82. initializationMarkdown: PropTypes.string,
  83. onChange: PropTypes.func,
  84. onSaveWithShortcut: PropTypes.func,
  85. onPenpalErrorOccured: PropTypes.func,
  86. };