Skip to content

Commit

Permalink
Merge pull request ElemeFE#78 from e1emeb0t/dev
Browse files Browse the repository at this point in the history
Rewrite Markdown parser & introduce Transition
  • Loading branch information
inter-action authored Nov 2, 2016
2 parents 38bd1d8 + 2e8955e commit 9ae1e85
Show file tree
Hide file tree
Showing 18 changed files with 389 additions and 287 deletions.
42 changes: 25 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ npm i && npm start

## 组件接口

目前提供了3个基础组件, Component, PropTypes和View.
目前提供了3个基础组件, Component, PropTypes, Transition和View.

### Component

Expand All @@ -40,6 +40,30 @@ npm i && npm start
继承了React所有的PropTypes, 并提供了一些通用的自定义类型, 所有的自定义组件不再使用React.PropTypes.

* range(min, max)
* regex

### Transition

目前只是`react-addons-css-transition-group`的封装.

* 属性
* name [String], Vue的`transition`的实现, 参考[3].
* duration [String/Number]
* component [String]
* className [String]
* style [Object]

```js
// Vue
<transition name="el-alert-fade">
...
</transition>
// React
<Transition name="el-alert-fade" duration="300">
...
</Transition>
```

### View

Expand All @@ -56,22 +80,6 @@ npm i && npm start
</View>
```

* transition [String], Vue的`transition`的实现, 参考[3].

```js
// Vue
<transition name="el-alert-fade">
...
</transition>
// React
<View transition="el-alert-fade">
...
</View>
```

> 注意: React.Children.count(props.children) === 1

## 单元测试

项目使用**jest**来做单元测试, 执行以下命令:
Expand Down
1 change: 1 addition & 0 deletions libs/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { default as Markdown } from './markdown';
export { default as Transition } from './transition';
export { default as Component } from './component';
export { default as PropTypes } from './props';
export { default as View } from './view';
107 changes: 107 additions & 0 deletions libs/markdown/canvas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { transform } from 'babel-standalone';
import highlight from 'highlight.js';
import marked from 'marked';

import Transition from '../transition';

export default class Canvas extends Component {
constructor(props) {
super(props);

this.state = {};
}

componentWillMount() {
marked.setOptions({
highlight: code => {
return highlight.highlightAuto(code).value;
}
});
}

componentDidMount() {
this.renderSource();
}

componentDidUpdate() {
this.renderSource();
}

getHeight() {
return Math.max(this.refs.highlight.offsetHeight, this.refs.description && this.refs.description.offsetHeight || 0);
}

blockControl() {
this.setState({
showBlock: !this.state.showBlock
});
}

renderSource() {
const Element = require('../../src');

const div = this.refs.source;
const args = ['context', 'React'], argv = [this.props.context, React];

for (const key in Element) {
args.push(key);
argv.push(Element[key]);
}

args.push(this.component);

if (div instanceof HTMLElement) {
ReactDOM.unmountComponentAtNode(div);
}

ReactDOM.render(new Function(...args).apply(null, argv), div);
}

render() {
const name = this.props.component.toLowerCase();
const document = this.props.children.match(/([^]*)\n?(```[^]+```)/);
const source = document[2].match(/```(.*)\n([^]+)```/);
const description = marked(document[1]);
const highlight = marked(document[2]);

let code = source[2];

if (!/^js|javascript/i.test(source[1])) {
code = `<div>${source[2]}</div>`
}

this.component = transform(code.replace(/this/g, 'context'), {
presets: ['es2015', 'react']
}).code.replace(/React.createElement/, 'return React.createElement');

return (
<div className={`demo-block demo-box demo-${name}`}>
<div className="source" ref="source"></div>
<div className="meta" style={{
height: this.state.showBlock ? this.getHeight() : 0
}}>
{ description && <div ref="description" className="description" dangerouslySetInnerHTML={{ __html: description }}></div> }
<div ref="highlight" className="highlight" dangerouslySetInnerHTML={{ __html: highlight }}></div>
</div>
<div className="demo-block-control" onClick={this.blockControl.bind(this)}>
{
this.state.showBlock ? (
<i className="el-icon-caret-top"></i>
) : (
<i className="el-icon-caret-bottom"></i>
)
}
</div>
</div>
)
}
}

/* eslint-disable */
Canvas.propTypes = {
component: PropTypes.string.isRequired,
context: PropTypes.any
}
/* eslint-enable */
79 changes: 10 additions & 69 deletions libs/markdown/index.js
Original file line number Diff line number Diff line change
@@ -1,97 +1,38 @@
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { transform } from 'babel-standalone';
import highlight from 'highlight.js';
import marked from 'marked';

import Canvas from './canvas';

export default class Markdown extends Component {
constructor(props) {
super(props);

marked.setOptions({
highlight: code => {
return highlight.highlightAuto(code).value;
}
});

this.components = new Map;
this.renderer = new marked.Renderer();
this.renderer.code = (text) => {
if (/demo-block/.test(text)) {
return text;
} else {
return `
<div class="demo-block">
<pre class="fixed">
<code>${highlight.highlightAuto(text).value}</code>
</pre>
</div>
`
}
}
}

componentDidMount() {
this.renderSource();
this.renderDOM();
}

componentDidUpdate() {
this.renderSource();
this.renderDOM();
}

renderSource() {
const Element = require('../../src');

renderDOM() {
for (const [id, component] of this.components) {
const div = document.getElementById(id), args = ['context', 'React'], argv = [this.props.context, React];

for (const key in Element) {
args.push(key);
argv.push(Element[key]);
}

args.push(component);

if (div instanceof HTMLElement) {
ReactDOM.unmountComponentAtNode(div);
}

ReactDOM.render(new Function(...args).apply(null, argv), div);
ReactDOM.render(component, document.getElementById(id))
}
}

render() {
const html = marked(this.props.children.replace(/:::\s?demo\s?([^]+?):::/g, (match, p1, offset) => {
return p1.replace(/([^]*)\n?(```[^]+```)/, (match, p1, p2) => {
const id = offset.toString(36);
const matched = p2.match(/```(.*)\n([^]+)```/);
const lang = matched[1], code = matched[2].replace(/this/g, 'context');

let transformTarget = null;

if (lang === 'javascript'){
transformTarget = code
}else{
transformTarget = `<div>${code}</div>`
}

const component = transform(transformTarget, {
presets: ['es2015', 'react']
}).code.replace(/React.createElement/, 'return React.createElement');
const id = offset.toString(36);

this.components.set(id, component);
this.components.set(id, React.createElement(Canvas, this.props, p1));

return `
<div class="demo-block demo-box demo-${this.props.component.toLowerCase()}">
<div class="source" id="${id}"></div>
<div class="meta">
${p1 && `<div class="description">${marked(p1)}</div>`}
<div class="highlight ${!p1 && 'full-width'}">${marked(p2)}</div>
</div>
</div>
`
})
}), { renderer: this.renderer });
return `<div id=${id}></div>`;
}));

/* eslint-disable */
return (
Expand Down
31 changes: 31 additions & 0 deletions libs/transition/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { Component, PropTypes } from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';

export default class Transition extends Component {
render() {
return React.createElement(ReactCSSTransitionGroup, {
transitionName: this.props.name,
transitionEnterTimeout: Number(this.props.duration),
transitionLeaveTimeout: Number(this.props.duration),
component: this.props.component,
className: this.props.className,
style: this.props.style
}, React.Children.map(this.props.children, element => {
return React.cloneElement(element, {
key: Math.random().toString()
})
}));
}
}

Transition.propTypes = {
name: PropTypes.string.isRequired,
duration: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
component: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object
};

Transition.defaultProps = {
duration: 300
}
36 changes: 18 additions & 18 deletions libs/view/index.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import React, { Component, PropTypes } from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';

export default class View extends Component {
render() {
const hidden = this.props.hasOwnProperty('show') && !this.props.show;
const element = React.Children.only(this.props.children);
const children = React.cloneElement(element, hidden && {
key: element,
style: Object.assign({}, element.props.style, {
display: 'none'
})
});
const style = this.props.hasOwnProperty('show') && !this.props.show && {
display: 'none'
};

if (this.props.transition) {
return (
<ReactCSSTransitionGroup transitionName={this.props.transition} transitionEnterTimeout={500} transitionLeaveTimeout={300} key={this.props.transitionkey}>
{children}
</ReactCSSTransitionGroup>
)
if (React.Children.count(this.props.children) > 1) {
return React.createElement(this.props.component, {
style: Object.assign({}, this.props.style, style),
className: this.props.className
}, this.props.children);
} else {
return children;
return React.cloneElement(this.props.children, {
style: Object.assign({}, this.props.children.props.style, style)
});
}
}
}

/* eslint-disable */
View.propTypes = {
show: PropTypes.any,
transition: PropTypes.string,
transitionKey: PropTypes.string
component: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object
};
/* eslint-enable */

View.defaultProps = {
component: 'span'
}
Loading

0 comments on commit 9ae1e85

Please sign in to comment.