Yuki Takei 8 лет назад
Родитель
Сommit
4cc83a38f3

+ 2 - 0
lib/views/_form.html

@@ -15,6 +15,8 @@
 {% endif %}
 <div id="form-box" class="row">
   <form action="/_/edit" id="page-form" method="post" class="col-md-6 {% if isUploadable() %}uploadable{% endif %} page-form">
+
+    <div id="page-editor"></div>
     <textarea name="pageForm[body]" class="form-control" id="form-body">{% if pageForm.body %}{{ pageForm.body }}{% elseif not page.revision.body %}# {{ path|path2name }}{% else %}{{ page.revision.body }}{% endif %}</textarea>
 
     <input type="hidden" name="pageForm[path]" value="{{ path }}">

+ 2 - 0
resource/js/app.js

@@ -6,6 +6,7 @@ import CrowiRenderer from './util/CrowiRenderer';
 
 import HeaderSearchBox  from './components/HeaderSearchBox';
 import SearchPage       from './components/SearchPage';
+import PageEditor       from './components/PageEditor';
 import PageListSearch   from './components/PageListSearch';
 import PageHistory      from './components/PageHistory';
 import PageComments     from './components/PageComments';
@@ -64,6 +65,7 @@ if (isEnabledPlugins) {
 const componentMappings = {
   'search-top': <HeaderSearchBox crowi={crowi} />,
   'search-page': <SearchPage crowi={crowi} />,
+  'page-editor': <PageEditor crowi={crowi} />,
   'page-list-search': <PageListSearch crowi={crowi} />,
   'page-comments-list': <PageComments pageId={pageId} revisionId={pageRevisionId} revisionCreatedAt= {pageRevisionCreatedAt} crowi={crowi} />,
   'page-attachment': <PageAttachment pageId={pageId} pageContent={pageContent} crowi={crowi} />,

+ 81 - 0
resource/js/components/PageEditor.js

@@ -0,0 +1,81 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import Editor from './PageEditor/Editor';
+import Preview from './PageEditor/Preview';
+
+export default class PageEditor extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      markdown: '',
+      html: '',
+    };
+
+    this.onMarkdownChanged = this.onMarkdownChanged.bind(this);
+  }
+
+  /**
+   * the change event handler for `markdown` state
+   * @param {string} value
+   */
+  onMarkdownChanged(value) {
+    this.setState({
+      markdown: value,
+      html: '',
+    });
+
+    this.renderPreview();
+  }
+
+  renderPreview() {
+    // generate options obj
+    const rendererOptions = {
+      // see: https://www.npmjs.com/package/marked
+      marked: {
+        breaks: false
+      }
+    };
+
+    // render html
+    var context = {
+      markdown: this.state.markdown,
+      dom: this.previewElement,
+      currentPagePath: decodeURIComponent(location.pathname)
+    };
+
+    this.props.crowi.interceptorManager.process('preRenderPreview', context)
+      .then(() => crowi.interceptorManager.process('prePreProcess', context))
+      .then(() => {
+        context.markdown = crowiRenderer.preProcess(context.markdown, context.dom);
+      })
+      .then(() => crowi.interceptorManager.process('postPreProcess', context))
+      .then(() => {
+        var parsedHTML = crowiRenderer.render(context.markdown, context.dom, rendererOptions);
+        context['parsedHTML'] = parsedHTML;
+      })
+      .then(() => crowi.interceptorManager.process('postRenderPreview', context))
+      .then(() => crowi.interceptorManager.process('preRenderPreviewHtml', context))
+      .then(() => {
+        this.setState({html: context.parsedHTML});
+      })
+      // process interceptors for post rendering
+      .then(() => crowi.interceptorManager.process('postRenderPreviewHtml', context));
+
+  }
+
+  render() {
+    return (
+      <div>
+        <Editor value={this.state.markdown} onChange={this.onMarkdownChanged} />
+        <Preview html={this.state.html} inputRef={el => this.previewElement = el} />
+      </div>
+    )
+  }
+}
+
+PageEditor.propTypes = {
+  crowi: PropTypes.object.isRequired,
+};

+ 43 - 0
resource/js/components/PageEditor/Editor.js

@@ -0,0 +1,43 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { UnControlled as CodeMirror } from 'react-codemirror2';
+require('codemirror/addon/display/autorefresh');
+require('codemirror/mode/gfm/gfm');
+require('codemirror/lib/codemirror.css');
+
+export default class Editor extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      value: this.props.value,
+    };
+  }
+
+  render() {
+    return (
+      <CodeMirror
+        value={this.state.value}
+        autoFocus={true}
+        options={{
+          mode: 'gfm',
+          lineNumbers: true,
+          autoRefresh: true
+        }}
+        onChange={(editor, data, value) => {
+          if (this.props.onChange != null) {
+            this.props.onChange(value);
+          }
+        }}
+      />
+    )
+  }
+
+}
+
+Editor.propTypes = {
+  value: PropTypes.string,
+  onChange: PropTypes.func,
+};

+ 27 - 0
resource/js/components/PageEditor/Preview.js

@@ -0,0 +1,27 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+export default class Preview extends React.Component {
+
+  constructor(props) {
+    super(props);
+  }
+
+  generateInnerHtml(html) {
+    return {__html: html};
+  }
+
+  render() {
+    return (
+      <div
+        ref={this.props.inputRef}
+        className="wiki preview-body" dangerouslySetInnerHTML={this.generateInnerHtml(this.props.html)}>
+      </div>
+    )
+  }
+}
+
+Preview.propTypes = {
+  html: PropTypes.string,
+  inputRef: PropTypes.func.isRequired,  // for getting div element
+};