Draft.jsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import React, { Fragment } from 'react';
  2. import PropTypes from 'prop-types';
  3. import Popover from 'react-bootstrap/lib/Popover';
  4. import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
  5. import { withTranslation } from 'react-i18next';
  6. import GrowiRenderer from '../../util/GrowiRenderer';
  7. import RevisionBody from '../Page/RevisionBody';
  8. class Draft extends React.Component {
  9. constructor(props) {
  10. super(props);
  11. this.state = {
  12. html: '',
  13. };
  14. this.growiRenderer = new GrowiRenderer(this.props.crowi, this.props.crowiOriginRenderer, { mode: 'draft' });
  15. this.renderHtml = this.renderHtml.bind(this);
  16. this.renderButton = this.renderButton.bind(this);
  17. this.renderPopover = this.renderPopover.bind(this);
  18. this.copyMarkdownToClipboard = this.copyMarkdownToClipboard.bind(this);
  19. }
  20. async componentDidMount() {
  21. this.setState({ html: await this.renderHtml(this.props.markdown) });
  22. }
  23. renderButton(isExist, markdown) {
  24. if (isExist) {
  25. return (
  26. <a
  27. className="draft-copy"
  28. data-toggle="tooltip"
  29. data-placement="bottom"
  30. title={this.props.t('Copy')}
  31. onClick={this.copyMarkdownToClipboard}
  32. >
  33. <i className="icon-doc" />
  34. </a>
  35. );
  36. }
  37. return (
  38. <Fragment>
  39. <span className="label-draft label label-default">draft</span>
  40. <a
  41. href={`${this.props.path}#edit`}
  42. target="_blank"
  43. rel="noopener noreferrer"
  44. className="draft-delete"
  45. data-toggle="tooltip"
  46. data-placement="bottom"
  47. title={this.props.t('Edit')}
  48. onClick={() => { return this.props.clearDraft(this.props.path) }}
  49. >
  50. <i className="icon-note" />
  51. </a>
  52. </Fragment>
  53. );
  54. }
  55. copyMarkdownToClipboard() {
  56. navigator.clipboard.writeText(this.props.markdown);
  57. }
  58. renderPopover(id, markdown) {
  59. return (
  60. <Popover id={id}>
  61. <RevisionBody html={this.state.html} />
  62. </Popover>
  63. );
  64. }
  65. async renderHtml(markdown) {
  66. const context = {
  67. markdown,
  68. };
  69. const growiRenderer = this.growiRenderer;
  70. const interceptorManager = this.props.crowi.interceptorManager;
  71. const html = await interceptorManager.process('prePreProcess', context)
  72. .then(() => {
  73. context.markdown = growiRenderer.preProcess(context.markdown);
  74. })
  75. .then(() => { return interceptorManager.process('postPreProcess', context) })
  76. .then(() => {
  77. const parsedHTML = growiRenderer.process(context.markdown);
  78. context.parsedHTML = parsedHTML;
  79. })
  80. .then(() => { return interceptorManager.process('prePostProcess', context) })
  81. .then(() => {
  82. context.parsedHTML = growiRenderer.postProcess(context.parsedHTML);
  83. })
  84. .then(() => { return interceptorManager.process('postPostProcess', context) })
  85. .then(() => { return context.parsedHTML });
  86. return html;
  87. }
  88. render() {
  89. const { t } = this.props;
  90. return (
  91. <li className="d-flex align-items-center">
  92. <OverlayTrigger placement="bottom" overlay={this.renderPopover(this.props.path, this.props.markdown)}>
  93. <span
  94. data-toggle="tooltip"
  95. data-placement="bottom"
  96. title={t('Click to copy')}
  97. onClick={this.copyMarkdownToClipboard}
  98. >
  99. <i className="icon-doc"></i>
  100. {this.props.path} {this.props.isExist ? `(${t('page exists')})` : ''}
  101. </span>
  102. </OverlayTrigger>
  103. {this.renderButton(this.props.isExist, this.props.markdown)}
  104. <a
  105. className="text-danger draft-delete"
  106. data-toggle="tooltip"
  107. data-placement="top"
  108. title={t('Delete')}
  109. onClick={() => { return this.props.clearDraft(this.props.path) }}
  110. >
  111. <i className="icon-trash" />
  112. </a>
  113. </li>
  114. );
  115. }
  116. }
  117. Draft.propTypes = {
  118. t: PropTypes.func.isRequired,
  119. crowi: PropTypes.object.isRequired,
  120. crowiOriginRenderer: PropTypes.object.isRequired,
  121. path: PropTypes.string.isRequired,
  122. markdown: PropTypes.string.isRequired,
  123. isExist: PropTypes.bool.isRequired,
  124. clearDraft: PropTypes.func.isRequired,
  125. };
  126. export default withTranslation()(Draft);