├── .DS_Store ├── css ├── .DS_Store ├── font-icons │ ├── .DS_Store │ ├── fonts │ │ ├── .DS_Store │ │ ├── icomoon.eot │ │ ├── icomoon.ttf │ │ ├── icomoon.woff │ │ ├── material-ui-icons.eot │ │ ├── material-ui-icons.ttf │ │ ├── material-ui-icons.woff │ │ ├── icomoon.svg │ │ └── material-ui-icons.svg │ └── style.css ├── main.css ├── github.css └── codemirror.css ├── routes ├── .DS_Store ├── message │ ├── containers │ │ ├── AddMessage.js │ │ ├── MessageItem.js │ │ └── MessageList.js │ ├── index.jsx │ └── components │ │ ├── MessageList.jsx │ │ ├── AddMessage.jsx │ │ └── MessageItem.jsx ├── article │ ├── index.jsx │ ├── containers │ │ ├── AddComment.js │ │ ├── CommentList.js │ │ ├── CommentItem.js │ │ └── Article.js │ └── components │ │ ├── CommentList.jsx │ │ ├── Comment.jsx │ │ └── Article.jsx └── home │ ├── containers │ ├── Pagination.js │ └── ArticleList.js │ ├── index.jsx │ └── components │ ├── ArticleCard.jsx │ ├── ArticleList.jsx │ └── Pagination.jsx ├── components ├── .DS_Store ├── Loading.jsx ├── MarkdownEle.jsx ├── Bottom.jsx ├── ShowSnackbar.jsx ├── RightMenu.jsx ├── App.jsx └── GlobalNav.jsx ├── store └── index.js ├── reducers ├── article.js ├── comment.js ├── messages.js ├── category.js ├── articleList.js ├── resState.js ├── index.js └── pagination.js ├── containers ├── ShowSnackbar.js ├── GlobalNav.js └── RightMenu.js ├── index.html ├── README.md ├── package.json ├── webpack.config.js ├── test.jsx └── actions └── index.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/.DS_Store -------------------------------------------------------------------------------- /css/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/css/.DS_Store -------------------------------------------------------------------------------- /routes/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/routes/.DS_Store -------------------------------------------------------------------------------- /components/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/components/.DS_Store -------------------------------------------------------------------------------- /css/font-icons/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/css/font-icons/.DS_Store -------------------------------------------------------------------------------- /css/font-icons/fonts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/css/font-icons/fonts/.DS_Store -------------------------------------------------------------------------------- /css/font-icons/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/css/font-icons/fonts/icomoon.eot -------------------------------------------------------------------------------- /css/font-icons/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/css/font-icons/fonts/icomoon.ttf -------------------------------------------------------------------------------- /css/font-icons/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/css/font-icons/fonts/icomoon.woff -------------------------------------------------------------------------------- /css/font-icons/fonts/material-ui-icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/css/font-icons/fonts/material-ui-icons.eot -------------------------------------------------------------------------------- /css/font-icons/fonts/material-ui-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/css/font-icons/fonts/material-ui-icons.ttf -------------------------------------------------------------------------------- /css/font-icons/fonts/material-ui-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsx/myBlog/HEAD/css/font-icons/fonts/material-ui-icons.woff -------------------------------------------------------------------------------- /store/index.js: -------------------------------------------------------------------------------- 1 | import {createStore,applyMiddleware} from 'redux' 2 | import thunk from 'redux-thunk' 3 | import reducer from '../reducers' 4 | const finalCreactStore = applyMiddleware(thunk)(createStore); 5 | const store = finalCreactStore(reducer); 6 | export default store; -------------------------------------------------------------------------------- /reducers/article.js: -------------------------------------------------------------------------------- 1 | import { SET_ARTICLE } from '../actions' 2 | export default function article(state = {}, action) { 3 | switch(action.type){ 4 | case SET_ARTICLE:{ 5 | return action.article; 6 | } 7 | default:{ 8 | return state; 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /reducers/comment.js: -------------------------------------------------------------------------------- 1 | import { SET_COMMENT } from '../actions' 2 | export default function article(state = [], action) { 3 | switch(action.type){ 4 | case SET_COMMENT:{ 5 | return action.comment; 6 | } 7 | default:{ 8 | return state; 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /reducers/messages.js: -------------------------------------------------------------------------------- 1 | import { SET_MESSAGE } from '../actions' 2 | export default function messages(state={},action) { 3 | switch (action.type) { 4 | case SET_MESSAGE:{ 5 | return action.messages 6 | } 7 | default:{ 8 | return state 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /reducers/category.js: -------------------------------------------------------------------------------- 1 | import { SET_CATEGORY } from '../actions' 2 | export default function category(state = [],action) { 3 | switch(action.type){ 4 | case SET_CATEGORY:{ 5 | return action.category; 6 | } 7 | default:{ 8 | return state; 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /reducers/articleList.js: -------------------------------------------------------------------------------- 1 | import { SET_ARTICLE_LIST } from '../actions' 2 | export default function articleList(state=[],action) { 3 | switch(action.type){ 4 | case SET_ARTICLE_LIST:{ 5 | return action.articles; 6 | } 7 | default:{ 8 | return state; 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /containers/ShowSnackbar.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import ShowSnackbar from '../components/ShowSnackbar.jsx' 4 | function mapStateToProps(state) { 5 | return { 6 | message:state.resState.message 7 | } 8 | } 9 | export default connect(mapStateToProps)(ShowSnackbar); -------------------------------------------------------------------------------- /components/Loading.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import CircularProgress from 'material-ui/CircularProgress' 3 | const styles = { 4 | marginTop:'25%', 5 | marginBottom:'25%', 6 | marginLeft:'46%' 7 | }; 8 | const Loading = React.createClass({ 9 | render:function () { 10 | return ( 11 | 12 | ); 13 | } 14 | }); 15 | export default Loading; -------------------------------------------------------------------------------- /routes/message/containers/AddMessage.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import { sendMessage } from '../../../actions' 4 | import AddMessage from '../components/AddMessage.jsx' 5 | function mapStateToProps(state) { 6 | return {}; 7 | } 8 | function mapDispatchToProps(dispatch) { 9 | return bindActionCreators({ sendMessage },dispatch); 10 | } 11 | export default connect(mapStateToProps,mapDispatchToProps)(AddMessage); -------------------------------------------------------------------------------- /reducers/resState.js: -------------------------------------------------------------------------------- 1 | import { SET_LOADING, SET_SNACKBAR } from '../actions' 2 | export default function resState(state = {},action) { 3 | switch (action.type) { 4 | case SET_LOADING:{ 5 | return Object.assign({},state,{isLoading:action.isLoading}); 6 | } 7 | case SET_SNACKBAR:{ 8 | return Object.assign({},state,{message:action.message}); 9 | } 10 | default:{ 11 | return state; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /containers/GlobalNav.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import GlobalNav from '../components/GlobalNav.jsx' 4 | import { getCategory } from '../actions' 5 | function mapStateToProps(state) { 6 | return { 7 | category:state.category 8 | } 9 | } 10 | function mapDispatchToProps(dispatch) { 11 | return bindActionCreators({ getCategory },dispatch); 12 | } 13 | export default connect(mapStateToProps,mapDispatchToProps)(GlobalNav); -------------------------------------------------------------------------------- /containers/RightMenu.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import RightMenu from '../components/RightMenu.jsx' 4 | import { getCategory } from '../actions' 5 | function mapStateToProps(state) { 6 | return { 7 | category:state.category 8 | } 9 | } 10 | function mapDispatchToProps(dispatch) { 11 | return bindActionCreators({ getCategory },dispatch); 12 | } 13 | export default connect(mapStateToProps,mapDispatchToProps)(RightMenu); -------------------------------------------------------------------------------- /routes/message/containers/MessageItem.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import { getMessage, sendReply } from '../../../actions' 4 | import MessageItem from '../components/MessageItem.jsx' 5 | function mapStateToProps(state) { 6 | return {} 7 | } 8 | function mapDispatchToProps(dispatch) { 9 | return bindActionCreators({ getMessage, sendReply },dispatch); 10 | } 11 | export default connect(mapStateToProps,mapDispatchToProps)(MessageItem); -------------------------------------------------------------------------------- /routes/article/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Article from './containers/Article.js' 3 | import Comment from './components/Comment.jsx' 4 | const ArticlePage = React.createClass({ 5 | render:function () { 6 | console.log('render article') 7 | return ( 8 |
9 |
10 | 11 |
12 | ); 13 | } 14 | }); 15 | module.exports = ArticlePage; 16 | // export default ArticlePage; -------------------------------------------------------------------------------- /reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import pagination from './pagination.js' 3 | import articleList from './articleList.js' 4 | import messages from './messages.js' 5 | import article from './article.js' 6 | import comment from './comment.js' 7 | import category from './category.js' 8 | import resState from './resState.js' 9 | const rootReducer = combineReducers({ 10 | pagination, 11 | articleList, 12 | messages, 13 | article, 14 | comment, 15 | category, 16 | resState 17 | }); 18 | export default rootReducer; -------------------------------------------------------------------------------- /routes/article/containers/AddComment.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import { sendComment } from '../../../actions' 4 | import AddMessage from '../../message/components/AddMessage.jsx' 5 | function mapStateToProps(state) { 6 | return { 7 | id:state.article.id 8 | } 9 | } 10 | function mapDispatchToProps(dispatch) { 11 | return bindActionCreators({ 12 | sendMessage:sendComment 13 | },dispatch); 14 | } 15 | export default connect(mapStateToProps,mapDispatchToProps)(AddMessage); -------------------------------------------------------------------------------- /routes/article/containers/CommentList.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import { getComment } from '../../../actions' 4 | import CommentList from '../components/CommentList.jsx' 5 | function mapStateToProps(state) { 6 | return { 7 | comment:state.comment 8 | } 9 | } 10 | function mapDispatchToProps(dispatch) { 11 | return bindActionCreators({ 12 | getComment:getComment 13 | },dispatch); 14 | } 15 | export default connect(mapStateToProps,mapDispatchToProps)(CommentList); -------------------------------------------------------------------------------- /reducers/pagination.js: -------------------------------------------------------------------------------- 1 | import { SET_MAX_PAGE , SET_CUR_PAGE } from '../actions' 2 | const defaultState = { 3 | curPage:1, 4 | maxPage:1 5 | }; 6 | export default function pagination(state=defaultState,action) { 7 | switch(action.type){ 8 | case SET_MAX_PAGE:{ 9 | return Object.assign({},state,{maxPage:action.maxPage}); 10 | } 11 | case SET_CUR_PAGE:{ 12 | return Object.assign({},state,{curPage:action.curPage}); 13 | } 14 | default:{ 15 | return state; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /routes/home/containers/Pagination.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import Pagination from '../components/Pagination.jsx' 4 | import { getMaxPage } from '../../../actions' 5 | function mapSateToProps(state) { 6 | return { 7 | maxPage:state.pagination.maxPage, 8 | curPage:state.pagination.curPage 9 | } 10 | } 11 | function mapDispatchToProps(dispatch) { 12 | return bindActionCreators({ getMaxPage },dispatch); 13 | } 14 | export default connect(mapSateToProps,mapDispatchToProps)(Pagination); -------------------------------------------------------------------------------- /routes/article/containers/CommentItem.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import { getComment, sendReplyToComment } from '../../../actions' 4 | import MessageItem from '../../message/components/MessageItem.jsx' 5 | function mapStateToProps(state) { 6 | return {} 7 | } 8 | function mapDispatchToProps(dispatch) { 9 | return bindActionCreators({ 10 | getMessage:getComment, 11 | sendReply:sendReplyToComment 12 | },dispatch); 13 | } 14 | export default connect(mapStateToProps,mapDispatchToProps)(MessageItem); -------------------------------------------------------------------------------- /routes/message/containers/MessageList.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import { getMessage,setLoading } from '../../../actions' 4 | import MessageList from '../components/MessageList.jsx' 5 | function mapStateToProps(state) { 6 | return { 7 | messages:state.messages, 8 | isLoading:state.resState.isLoading 9 | } 10 | } 11 | function mapDispatchToProps(dispatch) { 12 | return bindActionCreators({ getMessage, setLoading },dispatch); 13 | } 14 | export default connect(mapStateToProps,mapDispatchToProps)(MessageList); -------------------------------------------------------------------------------- /components/MarkdownEle.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import marked from 'marked' 3 | const MarkdownEle = React.createClass({ 4 | componentWillMount:function () { 5 | marked.setOptions({ 6 | highlight: function (code) { 7 | return require('highlight.js').highlightAuto(code).value; 8 | } 9 | }); 10 | }, 11 | render:function () { 12 | return ( 13 |
16 |
17 | ); 18 | } 19 | }); 20 | export default MarkdownEle; -------------------------------------------------------------------------------- /routes/home/containers/ArticleList.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import ArticleList from '../components/ArticleList.jsx' 4 | import { setArticles , LIMIT, getArticles, setLoading } from '../../../actions' 5 | function mapStateToProps(state) { 6 | return { 7 | articles:state.articleList, 8 | isLoading:state.resState.isLoading 9 | } 10 | } 11 | function mapDispatchToProps(dispatch) { 12 | return bindActionCreators({ setArticles , LIMIT, getArticles, setLoading },dispatch); 13 | } 14 | export default connect(mapStateToProps,mapDispatchToProps)(ArticleList); -------------------------------------------------------------------------------- /routes/article/containers/Article.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { bindActionCreators } from 'redux' 3 | import { getArticle, setLoading } from '../../../actions' 4 | import Article from '../components/Article.jsx' 5 | function mapStateToProps(state) { 6 | return { 7 | title:state.article.title, 8 | time:state.article.time, 9 | content:state.article.content, 10 | isLoading:state.resState.isLoading 11 | } 12 | } 13 | function mapDispatchToProps(dispatch) { 14 | return bindActionCreators({ getArticle, setLoading }, dispatch); 15 | } 16 | export default connect(mapStateToProps,mapDispatchToProps)(Article); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Material-UI Example 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Simple React Application 2 | 3 | An example react application with react-route, redux, react-redux, and material 4 | 5 | [myblog:http://blog.mdzzapp.com](http://blog.mdzzapp.com) 6 | 7 | #### Under the hood 8 | 9 | - [React](https://github.com/facebook/react) 10 | - [Redux](https://github.com/reactjs/redux) 11 | - [React Router](https://github.com/reactjs/react-router) 2.0 12 | - [Babel 6](https://github.com/babel/babel) 13 | - [Webpack](https://github.com/webpack/webpack) with vanilla Hot Module Replacement 14 | 15 | #### state design 16 | 17 | - pagination 18 | - articleList 19 | - messages 20 | - article 21 | - comment 22 | - category 23 | - resState 24 | 25 | #### How to install 26 | 27 | 1. git clone this project 28 | 2. cd into the folder 29 | 3. run npm install 30 | 4. run webpack -------------------------------------------------------------------------------- /routes/article/components/CommentList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { List } from 'material-ui/List' 3 | import Subheader from 'material-ui/Subheader' 4 | import CommentItem from '../containers/CommentItem.js' 5 | const CommentList = React.createClass({ 6 | componentDidMount:function () { 7 | this.props.getComment() 8 | }, 9 | render:function () { 10 | var { comment } = this.props; 11 | return ( 12 | 13 | { 14 | comment? 15 | comment.map(function (item) { 16 | return ( 17 | 18 | ); 19 | }):'' 20 | } 21 | 22 | ); 23 | } 24 | }); 25 | export default CommentList; -------------------------------------------------------------------------------- /routes/home/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { GridList, GridTile } from 'material-ui/GridList' 3 | import Divider from 'material-ui/Divider' 4 | import ArticleList from './containers/ArticleList.js' 5 | import Pagination from './containers/Pagination.js' 6 | const Home = React.createClass({ 7 | render:function () { 8 | if(this.props.params){ 9 | var page = this.props.params.page || 1; 10 | var category = this.props.params.category || ''; 11 | }else{ 12 | var page = 1; 13 | var category = ''; 14 | } 15 | return( 16 |
17 | 18 | 19 | 20 |
21 | ); 22 | } 23 | 24 | }); 25 | module.exports = Home; 26 | // export default Home; -------------------------------------------------------------------------------- /components/Bottom.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import FontIcon from 'material-ui/FontIcon' 3 | var styles = { 4 | bottom:{ 5 | backgroundColor:'rgb(33,33,33)', 6 | textAlign:'center', 7 | padding:'40px 10px', 8 | color:'#444', 9 | marginTop:'50px' 10 | } 11 | } 12 | const Bottom = React.createClass({ 13 | render:function () { 14 | return ( 15 |
17 |

Code crafted by MDZZ

18 |

19 | 20 | 24 | 25 |

26 |
27 | ); 28 | } 29 | }) 30 | export default Bottom; -------------------------------------------------------------------------------- /routes/article/components/Comment.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Tabs, Tab } from 'material-ui/Tabs' 3 | import Paper from 'material-ui/Paper' 4 | import CommentList from '../containers/CommentList.js' 5 | import AddComment from '../containers/AddComment.js' 6 | import Article from '../containers/Article.js' 7 | const styles = { 8 | paper:{ 9 | margin:'20px 15px 10px 15px' 10 | } 11 | }; 12 | const Comment = React.createClass({ 13 | render:function () { 14 | return( 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | }); 30 | export default Comment; -------------------------------------------------------------------------------- /components/ShowSnackbar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Snackbar from 'material-ui/Snackbar' 3 | const ShowSnackbar = React.createClass({ 4 | getInitialState:function () { 5 | return { 6 | open:false, 7 | } 8 | }, 9 | hanleRequestClose:function () { 10 | this.setState({ 11 | open:false, 12 | }); 13 | }, 14 | componentWillReceiveProps:function (nextProps) { 15 | if(nextProps.message !== ''){ 16 | this.setState({ 17 | open:true 18 | }); 19 | } 20 | }, 21 | render:function () { 22 | return ( 23 | {this.hanleRequestClose()}} 27 | autoHideDuration = {3000} 28 | /> 29 | ); 30 | } 31 | }); 32 | export default ShowSnackbar; -------------------------------------------------------------------------------- /routes/message/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import MessageList from './containers/MessageList.js' 3 | import AddMessage from './containers/AddMessage.js' 4 | import Divider from 'material-ui/Divider' 5 | import Subheader from 'material-ui/Subheader' 6 | import Paper from 'material-ui/Paper' 7 | const styles = { 8 | paper:{ 9 | margin:'20px 15px 10px 15px' 10 | }, 11 | subheader:{ 12 | color:'black', 13 | } 14 | } 15 | const Message = React.createClass({ 16 | render:function () { 17 | return ( 18 |
19 | 20 |

留言列表

21 | 22 | 23 |
24 | 25 |

我要留言

26 | 27 | 28 |
29 |
30 | ); 31 | } 32 | }); 33 | module.exports = Message; 34 | // export default Message; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blig", 3 | "version": "1.0.0", 4 | "description": "a simple react application", 5 | "main": "test.jsx", 6 | "dependencies": { 7 | "babel-core": "^6.9.0", 8 | "babel-preset-es2015": "^6.9.0", 9 | "marked": "^0.3.5", 10 | "highlight.js": "^9.4.0", 11 | "babel-preset-react": "^6.5.0", 12 | "react": "^15.1.0", 13 | "babel-loader": "^6.2.4", 14 | "react-dom": "^15.1.0", 15 | "react-addons-css-transition-group": "^15.1.0", 16 | "react-redux": "^4.4.5", 17 | "material-ui": "^0.15.0", 18 | "redux": "^3.5.2", 19 | "redux-thunk": "^2.1.0", 20 | "react-router": "^2.4.1", 21 | "react-tap-event-plugin": "^1.0.0", 22 | "reqwest": "^2.0.5", 23 | "webpack": "^1.12.14" 24 | }, 25 | "devDependencies": {}, 26 | "scripts": { 27 | "test": "echo \"Error: no test specified\" && exit 1" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/redsx/myBlog.git" 32 | }, 33 | "keywords": [ 34 | "react", 35 | "blog" 36 | ], 37 | "author": "MDZZ", 38 | "license": "ISC" 39 | } 40 | -------------------------------------------------------------------------------- /routes/home/components/ArticleCard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import { Card, CardActions, CardText, CardTitle } from 'material-ui/Card' 4 | import RaisedButton from 'material-ui/RaisedButton' 5 | const styles = { 6 | card:{ 7 | margin:'20px' 8 | }, 9 | span:{ 10 | fontSize:'1.2rem' 11 | } 12 | } 13 | const ArticleCard = React.createClass({ 14 | render:function () { 15 | var { time, title, introduce } = this.props.article 16 | return ( 17 | 20 | 24 | 25 | 26 | {introduce} 27 | 28 | 29 | 30 | 34 | 35 | 36 | 37 | ); 38 | } 39 | }); 40 | export default ArticleCard; -------------------------------------------------------------------------------- /routes/article/components/Article.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import MarkdownEle from '../../../components/MarkdownEle.jsx' 3 | import Paper from 'material-ui/Paper' 4 | import Loading from '../../../components/Loading.jsx' 5 | const styles = { 6 | paper:{ 7 | margin:'20px 15px 10px 15px', 8 | padding:'10px 15px' 9 | } 10 | }; 11 | const Article = React.createClass({ 12 | componentWillMount:function () { 13 | this.props.setLoading(true); 14 | this.props.getArticle(this.props.articleTitle); 15 | }, 16 | shouldComponentUpdate:function (nextProps,nextState) { 17 | if(this.props.articleTitle !== nextProps.articleTitle){ 18 | this.props.setLoading(true); 19 | this.props.getArticle(nextProps.articleTitle); 20 | } 21 | return true; 22 | }, 23 | render:function () { 24 | var { title, content, time } = this.props; 25 | return ( 26 | this.props.isLoading? 27 | 28 | : 29 | 32 |

{ title }

33 |
{time }
34 | 35 |
36 | ); 37 | } 38 | }); 39 | export default Article; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | module.exports = { 3 | entry:{ 4 | bundle:'./test.jsx', 5 | vendor:['react','redux','react-dom','react-redux','reqwest','react-router','redux-thunk'] 6 | }, 7 | output:{ 8 | path: __dirname+'/bundle/', 9 | publicPath:'bundle/', 10 | filename: '[name].bundle.js', 11 | chunkFilename: '[name].chunk.js' 12 | }, 13 | watch:true, 14 | resolve:{ 15 | alias:{ 16 | //... 17 | } 18 | }, 19 | module: { 20 | loaders: [ 21 | { 22 | test: /\.jsx?$/, 23 | exclude: /(node_modules|bower_components)/, 24 | loader: 'babel-loader', // 'babel-loader' is also a legal name to reference 25 | query: { 26 | presets: ['react', 'es2015'] 27 | } 28 | } 29 | ] 30 | }, 31 | plugins: [ 32 | new webpack.optimize.UglifyJsPlugin({ 33 | compress: { 34 | warnings: false 35 | } 36 | }), 37 | new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'), 38 | //new webpack.optimize.DedupePlugin(), 39 | new webpack.optimize.OccurenceOrderPlugin(), 40 | new webpack.DefinePlugin({ 41 | 'process.env.NODE_ENV': JSON.stringify('production') 42 | }) 43 | ] 44 | } -------------------------------------------------------------------------------- /routes/home/components/ArticleList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import ArticleCard from './ArticleCard.jsx' 4 | import Loading from '../../../components/Loading.jsx' 5 | const ArticleList = React.createClass({ 6 | componentDidMount:function () { 7 | var page = this.props.page || 1; 8 | var category = this.props.category || ''; 9 | this.props.setLoading(true); 10 | this.props.getArticles(page,category); 11 | }, 12 | componentWillReceiveProps:function (nextProps) { 13 | if(this.props.page && (this.props.page !== nextProps.page || this.props.category !== nextProps.category)){ 14 | var page = nextProps.page || 1; 15 | var category = nextProps.category || ''; 16 | this.props.setLoading(true); 17 | this.props.getArticles(page,category); 18 | } 19 | }, 20 | render:function () { 21 | var articles = this.props.articles; 22 | return ( 23 | this.props.isLoading ? 24 | 25 | : 26 |
27 | { 28 | articles.map(function (item) { 29 | return ( 30 | 34 | ); 35 | }) 36 | } 37 |
38 | ) 39 | 40 | } 41 | }); 42 | export default ArticleList; -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | /* custom font icons */ 2 | @import "font-icons/style.css"; 3 | 4 | /* hightlight.js for syntax highlighting */ 5 | @import "github.css"; 6 | 7 | a { 8 | color: #444; 9 | text-decoration: none; 10 | } 11 | 12 | a:hover { 13 | color: #ff4081; 14 | text-decoration: none; 15 | } 16 | 17 | html { 18 | font-family: 'Roboto', sans-serif; 19 | -webkit-font-smoothing: antialiased; 20 | } 21 | 22 | body { 23 | background: #f5f6f9; 24 | } 25 | 26 | body, h1, h2, h3, h4, h5, h6 { 27 | margin: 0; 28 | } 29 | 30 | body { 31 | font-size: 15px; 32 | line-height: 24px; 33 | } 34 | .muidocs-checkbox-example { 35 | border: 2px solid #FF9800; 36 | background-color: #4CAF50; 37 | } 38 | 39 | /*My Own Settings*/ 40 | .pagination li { 41 | display: inline-block; 42 | font-size: 1.2rem; 43 | padding: 0 10px; 44 | line-height: 30px; 45 | border-radius: 3px; 46 | text-align: center; 47 | margin-left: 2px; 48 | margin-right: 2px; 49 | } 50 | 51 | .pagination li a { 52 | color: #444; 53 | } 54 | 55 | .pagination li.active a { 56 | color: #fff; 57 | } 58 | 59 | .pagination li.active { 60 | background-color: #ee6e73; 61 | } 62 | 63 | .pagination li.disabled a { 64 | cursor: default; 65 | color: #999; 66 | } 67 | 68 | .pagination li i { 69 | font-size: 2.2rem; 70 | vertical-align: middle; 71 | } 72 | 73 | .pagination li.pages ul li { 74 | display: inline-block; 75 | float: none; 76 | } 77 | 78 | @media only screen and (max-width: 992px) { 79 | .pagination { 80 | width: 100%; 81 | } 82 | .pagination li.prev, 83 | .pagination li.next { 84 | width: 10%; 85 | } 86 | .pagination li.pages { 87 | width: 80%; 88 | overflow: hidden; 89 | white-space: nowrap; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {render} from 'react-dom' 3 | import {Provider} from 'react-redux' 4 | import { Router, Route, IndexRoute, browserHistory, hashHistory } from 'react-router' 5 | import injectTapEventPlugin from 'react-tap-event-plugin' 6 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider' 7 | import getMuiTheme from 'material-ui/styles/getMuiTheme' 8 | 9 | import App from './components/App.jsx' 10 | // import Home from './routes/home/index.jsx' 11 | // import Message from './routes/message/index.jsx' 12 | // import Article from './routes/article/index.jsx' 13 | import store from './store' 14 | injectTapEventPlugin(); 15 | render( 16 | 17 |
18 | 19 | 20 | { 22 | require.ensure([] , (require) => { 23 | cb(null,require('./routes/home/index.jsx')); 24 | },'home'); 25 | } 26 | } /> 27 | { 29 | require.ensure([] , (require) => { 30 | cb(null,require('./routes/home/index.jsx')); 31 | },'home'); 32 | } 33 | } /> 34 | { 36 | require.ensure([] , (require) => { 37 | cb(null,require('./routes/message/index.jsx')); 38 | },'message'); 39 | } 40 | } /> 41 | { 43 | require.ensure([] , (require) => { 44 | cb(null,require('./routes/article/index.jsx')); 45 | },'article'); 46 | } 47 | } /> 48 | 49 | 50 |
51 |
, 52 | document.getElementById('App') 53 | ) -------------------------------------------------------------------------------- /css/github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | color: #333; 12 | background: #f8f8f8; 13 | -webkit-text-size-adjust: none; 14 | } 15 | 16 | .hljs-comment, 17 | .hljs-template_comment, 18 | .diff .hljs-header, 19 | .hljs-javadoc { 20 | color: #998; 21 | font-style: italic; 22 | } 23 | 24 | .hljs-keyword, 25 | .css .rule .hljs-keyword, 26 | .hljs-winutils, 27 | .nginx .hljs-title, 28 | .hljs-subst, 29 | .hljs-request, 30 | .hljs-status { 31 | color: #333; 32 | font-weight: bold; 33 | } 34 | 35 | .hljs-number, 36 | .hljs-hexcolor, 37 | .ruby .hljs-constant { 38 | color: #008080; 39 | } 40 | 41 | .hljs-string, 42 | .hljs-tag .hljs-value, 43 | .hljs-phpdoc, 44 | .hljs-dartdoc, 45 | .tex .hljs-formula { 46 | color: #d14; 47 | } 48 | 49 | .hljs-title, 50 | .hljs-id, 51 | .scss .hljs-preprocessor { 52 | color: #900; 53 | font-weight: bold; 54 | } 55 | 56 | .hljs-list .hljs-keyword, 57 | .hljs-subst { 58 | font-weight: normal; 59 | } 60 | 61 | .hljs-class .hljs-title, 62 | .hljs-type, 63 | .vhdl .hljs-literal, 64 | .tex .hljs-command { 65 | color: #458; 66 | font-weight: bold; 67 | } 68 | 69 | .hljs-tag, 70 | .hljs-tag .hljs-title, 71 | .hljs-rules .hljs-property, 72 | .django .hljs-tag .hljs-keyword { 73 | color: #000080; 74 | font-weight: normal; 75 | } 76 | 77 | .hljs-attribute, 78 | .hljs-variable, 79 | .lisp .hljs-body { 80 | color: #008080; 81 | } 82 | 83 | .hljs-regexp { 84 | color: #009926; 85 | } 86 | 87 | .hljs-symbol, 88 | .ruby .hljs-symbol .hljs-string, 89 | .lisp .hljs-keyword, 90 | .clojure .hljs-keyword, 91 | .scheme .hljs-keyword, 92 | .tex .hljs-special, 93 | .hljs-prompt { 94 | color: #990073; 95 | } 96 | 97 | .hljs-built_in { 98 | color: #0086b3; 99 | } 100 | 101 | .hljs-preprocessor, 102 | .hljs-pragma, 103 | .hljs-pi, 104 | .hljs-doctype, 105 | .hljs-shebang, 106 | .hljs-cdata { 107 | color: #999; 108 | font-weight: bold; 109 | } 110 | 111 | .hljs-deletion { 112 | background: #fdd; 113 | } 114 | 115 | .hljs-addition { 116 | background: #dfd; 117 | } 118 | 119 | .diff .hljs-change { 120 | background: #0086b3; 121 | } 122 | 123 | .hljs-chunk { 124 | color: #aaa; 125 | } 126 | -------------------------------------------------------------------------------- /routes/message/components/MessageList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { List } from 'material-ui/List' 3 | import Subheader from 'material-ui/Subheader' 4 | import MessageItem from '../containers/MessageItem.js' 5 | import Divider from 'material-ui/Divider' 6 | import Loading from '../../../components/Loading.jsx' 7 | const styles = { 8 | text:{ 9 | textAlign:'center' 10 | }, 11 | pointer:{ 12 | cursor:'pointer' 13 | } 14 | } 15 | const MessageList = React.createClass({ 16 | getInitialState:function () { 17 | return { 18 | isLoadedAll:false 19 | } 20 | }, 21 | componentWillMount:function () { 22 | this.props.setLoading(true); 23 | this.props.getMessage(1); 24 | }, 25 | 26 | handleClick:function () { 27 | var { curPage } = this.props.messages; 28 | curPage = parseInt(curPage); 29 | this.props.getMessage(curPage + 1); 30 | }, 31 | render:function () { 32 | var { curPage, maxPage, messageArr } = this.props.messages; 33 | curPage = parseInt(curPage); 34 | maxPage = parseInt(maxPage); 35 | return ( 36 | this.props.isLoading? 37 | 38 | : 39 | 40 | { 41 | messageArr? 42 | messageArr.map(function (item) { 43 | return ( 44 | 45 | ); 46 | }):'' 47 | } 48 | 49 |
52 | { 53 | curPage === maxPage ? 56 | 😆已经全部加载了😆 57 | :this.handleClick()} 60 | > 61 | 加载更多 62 | 63 | 64 | } 65 |
66 |
67 | ); 68 | } 69 | }); 70 | export default MessageList; -------------------------------------------------------------------------------- /components/RightMenu.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import reqwest from 'reqwest' 3 | import {Link} from 'react-router' 4 | import Subheader from 'material-ui/Subheader' 5 | import { List, ListItem, MakeSelectable } from 'material-ui/List' 6 | import { Step, Stepper, StepLabel, StepContent, StepButton } from 'material-ui/Stepper' 7 | const StepMenu = React.createClass({ 8 | getInitialState:function () { 9 | return { 10 | stepIndex:0, 11 | } 12 | }, 13 | componentDidMount:function () { 14 | this.props.getCategory(); 15 | 16 | }, 17 | componentDidUpdate:function () { 18 | var stepContent = document.querySelectorAll('.stepContent>div'); 19 | for(var i=0;i 33 | {this.handleSetIndex(num)}} 35 | > 36 | {key} 37 | 38 | 41 | 42 | { 43 | titles.map(function (title) { 44 | return {title} 45 | }) 46 | } 47 | 48 | 49 | 50 | ); 51 | }, 52 | render:function () { 53 | var cont = []; 54 | var i = 0; 55 | var data = this.props.category; 56 | for( var i=0 ; i 61 | 最近的文章 62 | 67 | {cont} 68 | 69 | 70 | ); 71 | } 72 | }); 73 | export default StepMenu; -------------------------------------------------------------------------------- /css/font-icons/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /components/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import GlobalNav from '../containers/GlobalNav.js' 3 | import RightMenu from '../containers/RightMenu.js' 4 | import Bottom from './Bottom.jsx' 5 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider' 6 | import getMuiTheme from 'material-ui/styles/getMuiTheme' 7 | import Paper from 'material-ui/Paper' 8 | import { GridList, GridTile } from 'material-ui/GridList' 9 | import ShowSnackbar from '../containers/ShowSnackbar.js' 10 | 11 | const styles = { 12 | paper:{ 13 | margin:'20px 15px 10px 15px' 14 | } 15 | } 16 | const App = React.createClass({ 17 | getInitialState:function () { 18 | if(window.innerWidth >980){ 19 | return { 20 | smallScreeen:false 21 | } 22 | }else{ 23 | return { 24 | smallScreeen:true 25 | } 26 | } 27 | }, 28 | componentDidMount:function () { 29 | var self = this; 30 | window.addEventListener('resize',function(event){ 31 | if( self.state.smallScreeen && window.innerWidth>980 ){ 32 | self.setState({smallScreeen:false}) 33 | } 34 | if( !self.state.smallScreeen && window.innerWidth<=980 ){ 35 | self.setState({smallScreeen:true}) 36 | } 37 | }); 38 | }, 39 | render:function () { 40 | var col = 2; 41 | if(this.state.smallScreeen){ 42 | col = 3 43 | } 44 | return ( 45 | 46 |
47 | 48 |
53 |
59 | {this.props.children} 60 |
61 |
67 | 68 | { 69 | this.state.smallScreeen? '': 70 | } 71 | 72 |
73 |
78 |
79 | 80 | 81 |
82 |
83 | ) 84 | } 85 | }); 86 | export default App -------------------------------------------------------------------------------- /routes/home/components/Pagination.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router' 3 | import FlatButton from 'material-ui/FlatButton' 4 | import ArrowPre from 'material-ui/svg-icons/navigation/chevron-left' 5 | import ArrowNext from 'material-ui/svg-icons/navigation/chevron-right' 6 | const styles = { 7 | button:{ 8 | minWidth:'2rem' 9 | }, 10 | pagination:{ 11 | margin:'20px 10px' 12 | } 13 | } 14 | const Pagination = React.createClass({ 15 | componentDidMount:function () { 16 | var category = this.props.category || ''; 17 | this.props.getMaxPage(category); 18 | }, 19 | componentWillReceiveProps:function (nextProps) { 20 | console.log(); 21 | if(this.props.curPage !== nextProps.curPage || this.props.category !== nextProps.category ){ 22 | var category = nextProps.category || ''; 23 | this.props.getMaxPage(category); 24 | } 25 | }, 26 | render:function () { 27 | var maxPage = parseInt(this.props.maxPage); 28 | var curPage = parseInt(this.props.curPage); 29 | var route = this.props.category === '' ? '/page/':('/page/'+this.props.category+'/'); 30 | var cont = []; 31 | var i = curPage > 2 ? curPage-2 : curPage 32 | var n = maxPage > curPage+2 ? curPage+2 : maxPage 33 | for(; i <= n ; i++ ){ 34 | if( curPage === i ){ 35 | cont.push( 36 | 41 | {i} 42 | 43 | ); 44 | }else{ 45 | cont.push( 46 | 47 | 51 | {i} 52 | 53 | 54 | ); 55 | } 56 | } 57 | return ( 58 |
59 | { 60 | curPage === 1? } 62 | disabled = {true} 63 | style = {styles.button} 64 | /> : 65 | } 67 | style = {styles.button} 68 | /> 69 | 70 | } 71 | {cont} 72 | { 73 | curPage === maxPage? } 75 | disabled = {true} 76 | style = {styles.button} 77 | /> : 78 | } 80 | style = {styles.button} 81 | /> 82 | 83 | } 84 |
85 | ); 86 | } 87 | }); 88 | export default Pagination; -------------------------------------------------------------------------------- /routes/message/components/AddMessage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import TextField from 'material-ui/TextField' 3 | import RaisedButton from 'material-ui/RaisedButton' 4 | const AddMessage = React.createClass({ 5 | getInitialState:function () { 6 | return { 7 | disabled:true, 8 | errName:'', 9 | errContent:'' 10 | } 11 | }, 12 | handleCheckButton:function () { 13 | var message = {}; 14 | message.name = this.refs.name.input.value.trim(); 15 | message.content = document.getElementById('content').value.trim(); 16 | if(message.name !== '' && message.content !== ''){ 17 | this.setState({disabled:false}); 18 | }else{ 19 | this.setState({disabled:true}); 20 | } 21 | }, 22 | handleCheckNameError:function () { 23 | var name = this.refs.name.input.value.trim(); 24 | if(name === '' && this.state.errName === ''){ 25 | this.setState({errName:'name不能为空'}); 26 | } 27 | if(name !== '' && this.state.errName !== ''){ 28 | this.setState({errName:''}); 29 | } 30 | this.handleCheckButton(); 31 | }, 32 | handleCheckContentError:function () { 33 | var content = document.getElementById('content').value.trim(); 34 | if(content === '' && this.state.errContent === ''){ 35 | this.setState({errContent:'content不能为空'}); 36 | } 37 | if(content !== '' && this.state.errContent !== ''){ 38 | this.setState({errContent:''}); 39 | } 40 | this.handleCheckButton(); 41 | }, 42 | handleClick:function () { 43 | var message = {}; 44 | var content = document.getElementById('content') 45 | message.name = this.refs.name.input.value.trim(); 46 | message.email = this.refs.email.input.value.trim(); 47 | message.content = content.value.trim(); 48 | if(this.props.id){ 49 | message.id = this.props.id; 50 | } 51 | console.log(message); 52 | this.refs.name.input.value = ''; 53 | this.refs.email.input.value = ''; 54 | content.value = ''; 55 | this.props.sendMessage(message); 56 | this.setState({disabled:true}); 57 | }, 58 | render:function () { 59 | return ( 60 |
65 | {this.handleCheckNameError()}} 69 | errorText = {this.state.errName} 70 | style = {{ 71 | width:'40%' 72 | }} 73 | > 74 | 81 | {this.handleCheckContentError()}} 86 | errorText = {this.state.errContent} 87 | fullWidth = {true} 88 | multiLine = {true} 89 | maxRow = {5} 90 | > 91 | {this.handleClick()}} 95 | label = '提交' 96 | /> 97 |
98 | ); 99 | } 100 | }); 101 | export default AddMessage; -------------------------------------------------------------------------------- /css/font-icons/fonts/material-ui-icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /components/GlobalNav.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router' 3 | import AppBar from 'material-ui/AppBar' 4 | import Drawer from 'material-ui/Drawer' 5 | import { List, ListItem } from 'material-ui/List' 6 | import Subheader from 'material-ui/Subheader' 7 | import FontIcon from 'material-ui/FontIcon' 8 | import IconButton from 'material-ui/IconButton' 9 | import ActionInfo from 'material-ui/svg-icons/action/info' 10 | import ActionList from 'material-ui/svg-icons/action/list' 11 | import ActionGrade from 'material-ui/svg-icons/action/grade' 12 | import FileFolder from 'material-ui/svg-icons/file/folder' 13 | import ContentDrafts from 'material-ui/svg-icons/content/drafts' 14 | import Divider from 'material-ui/Divider' 15 | const styles = { 16 | title:{ 17 | cursor:'pointer' 18 | }, 19 | iconRight:{ 20 | marginLeft:'' 21 | } 22 | } 23 | 24 | const GlobalNav = React.createClass({ 25 | getInitialState:function () { 26 | return { 27 | open:false 28 | } 29 | }, 30 | componentDidMount:function () { 31 | this.props.getCategory(); 32 | }, 33 | handleToggle:function () { 34 | this.setState({ 35 | open:!this.state.open 36 | }); 37 | }, 38 | handleClose:function () { 39 | console.log('handleClose') 40 | this.setState({ 41 | open:false 42 | }); 43 | }, 44 | generateItem:function () { 45 | return this.props.category.map((item) => { 46 | return ( 47 | } 50 | > 51 | {this.handleToggle()}} 54 | > 55 | {item.category} 56 | 57 | 58 | 59 | ); 60 | }) 61 | }, 62 | render:function () { 63 | return ( 64 |
65 | MDZZ} 67 | onLeftIconButtonTouchTap = {()=>{this.handleToggle()}} 68 | iconElementRight = { 69 | 70 | 71 | 75 | 76 | 77 | } 78 | iconStyleRight = {styles.iconRight} 79 | /> 80 | { this.setState({open:open})}} 85 | > 86 | 87 |

MDZZ

88 | 89 | 92 | {this.handleToggle()}} 95 | leftIcon = {} 96 | /> 97 | 98 | 99 | } 102 | primaryTogglesNestedList = {true} 103 | initiallyOpen = {true} 104 | nestedItems = {this.generateItem()} 105 | /> 106 | 109 | {this.handleToggle()}} 112 | leftIcon = {} 113 | /> 114 | 115 |

关于我

116 | 117 | 118 | 121 | 122 | 123 | 126 | 127 | 128 | 131 | 132 |
133 |
134 |
135 | ); 136 | } 137 | }); 138 | 139 | 140 | 141 | export default GlobalNav; -------------------------------------------------------------------------------- /routes/message/components/MessageItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ListItem } from 'material-ui/List' 3 | import Avatar from 'material-ui/Avatar' 4 | import Communication from 'material-ui/svg-icons/communication/chat-bubble' 5 | import FlatButton from 'material-ui/FlatButton' 6 | import IconButton from 'material-ui/IconButton' 7 | import Dialog from 'material-ui/Dialog' 8 | import TextField from 'material-ui/TextField' 9 | const src = ''; 10 | const styles = { 11 | icon:{ 12 | color:'rgba(177,177,177,1)' 13 | }, 14 | content:{ 15 | padding:'0.5rem 0' 16 | }, 17 | time:{ 18 | fontSize:'0.8rem', 19 | color:'#666' 20 | } 21 | } 22 | const MessageItem = React.createClass({ 23 | getInitialState:function () { 24 | return { 25 | open:false, 26 | disabled:true, 27 | errName:'', 28 | errContent:'' 29 | } 30 | }, 31 | handleClose:function () { 32 | this.setState({open:false}); 33 | }, 34 | handleOpen:function () { 35 | this.setState({open:true}); 36 | }, 37 | handleSendReply:function () { 38 | var reply = {}; 39 | var name = document.getElementById('name'); 40 | var content = document.getElementById('reply'); 41 | reply.id = this.props.message.id; 42 | reply.name = name.value.trim(); 43 | reply.content = content.value.trim(); 44 | this.setState({ 45 | open:false, 46 | disabled:true 47 | }); 48 | this.props.sendReply(reply); 49 | }, 50 | handleCheckButton:function () { 51 | var reply = {}; 52 | reply.name = document.getElementById('name').value.trim(); 53 | reply.content = document.getElementById('reply').value.trim(); 54 | if( reply.name !== '' && reply.content !== '' ){ 55 | this.setState({disabled:false}); 56 | }else{ 57 | this.setState({disabled:true}); 58 | } 59 | }, 60 | handleCheckName:function () { 61 | var name = document.getElementById('name').value.trim(); 62 | if( name === '' && this.state.errName === '' ){ 63 | this.setState({errName:'name 不能为空'}); 64 | } 65 | if( name !== '' && this.state.errName !== '' ){ 66 | this.setState({errName:''}); 67 | } 68 | this.handleCheckButton(); 69 | }, 70 | handleCheckContent:function () { 71 | var content = document.getElementById('reply').value.trim(); 72 | if( content === '' && this.state.errContent === '' ){ 73 | this.setState({errContent:'content 不能为空'}); 74 | } 75 | if( content !== '' && this.state.errContent !== '' ){ 76 | this.setState({errContent:''}); 77 | } 78 | this.handleCheckButton(); 79 | }, 80 | replyItem:function (reply = []) { 81 | var replyItems = []; 82 | for(var i = 0; i} 86 | key = {i} 87 | > 88 | {reply[i].name} 89 |    {reply[i].time} 90 |
93 | {reply[i].content} 94 |
95 | 96 | ); 97 | } 98 | return replyItems; 99 | }, 100 | render:function () { 101 | const { name, content, time, reply } = this.props.message; 102 | const replyItems = this.replyItem(reply); 103 | const actions = [ 104 | {this.handleSendReply()}} 109 | keyboardFocused = {true} 110 | />, 111 | {this.handleClose()}} 115 | />, 116 | ]; 117 | const rightIconButton = ( 118 | {this.handleOpen()}} 122 | > 123 | 126 | 127 | ); 128 | return ( 129 | } 131 | rightIconButton = { rightIconButton } 132 | primaryTogglesNestedList = { true } 133 | nestedItems = { replyItems } 134 | > 135 |
136 | {name} 137 |    {time} 138 |
141 | {content} 142 |
143 |
144 | 149 | {this.handleCheckName()}} 154 | /> 155 |
156 | {this.handleCheckContent()}} 161 | multiLine = {true} 162 | fullWidth = {true} 163 | rowsMax = {5} 164 | /> 165 |
166 |
167 | ); 168 | } 169 | }); 170 | export default MessageItem; -------------------------------------------------------------------------------- /css/font-icons/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'material-ui-icons'; 3 | src: url('fonts/material-ui-icons.eot'); 4 | } 5 | @font-face { 6 | font-family: 'material-ui-icons'; 7 | src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMghi/NsAAAC8AAAAYGNtYXDMfszDAAABHAAAAGRnYXNwAAAAEAAAAYAAAAAIZ2x5Zp6RlyoAAAGIAAAELGhlYWQDHAqpAAAFtAAAADZoaGVhA+IB8AAABewAAAAkaG10eBcAAroAAAYQAAAAPGxvY2EFugcGAAAGTAAAACBtYXhwABUAUgAABmwAAAAgbmFtZT0DC0MAAAaMAAABn3Bvc3QAAwAAAAAILAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADmJQHg/+AAIAHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEAFAAAAAQABAAAwAAAAEAIOYH5gvmEOYl//3//wAAAAAAIOYA5gvmEOYl//3//wAB/+MaBBoBGf0Z6QADAAEAAAAAAAAAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAABQBAABUBwAGVAAkADgATABcAGwAAEzMnBzMVIxc3IzczFSM1NTMVIzU3FzM1AzczFatAVlVAQFVWQCrr6+vrJyqaxCqaAUBVVdVWVlUrK1UqKlYrK/7VKysAAAABAEAAFQHAAZUAJAAANx4BFzc+ARceATMyFh0BFAYjIi4CNTQ2OwEyFhUUFhcWBg8BjRdJLS8EDAYRJxQIDQ0IS4ViOQ0ISwkMBwUCAgUv7y1IFy4FAwIGBwwJSgkNOWOESwkMDAkUJhIGCwUvAAAAAwAAAGsCAAFVABoAJwA0AAABIgYVFBYXIz4BNTQmIyIGFRQWMyEyNjU0JiMFIiY1NDYzMhYVFAYjISImNTQ2MzIWFRQGIwGLMUUPDGAMD0UxMEVFMAEWMEVFMP7qHysrHx8sLB8BFh8sLB8fKysfAVVEMRUmEBAmFTFERDExREQxMUTALB8fLCwfHywsHx8sLB8fLAAAAAABAIAAgAGAAR4ABQAAAQcnBxc3AWJiYh6AgAEeYmIegIAAAAABACsAFQHVAasACQAAJRcnNy8BDwEXBwEAhCN0mTw8mXQjZVCWZQ2Ojg1llgAAAAABACsAKwHVAZUACgAANzUzFTM1MycHMxXVVmpA1dVAK4CAqsDAqgAAAgArAAAB1QGrABQAHwAAASIOAhUUHgIzMj4CNTQuAiMTJwc3Jz8BHwEHFwEALE46ISE6TiwsTjohITpOLFpaWhhQaSkpaVAYAasiOk4sLE45IiI5TiwsTjoi/qo3N2dFCWFhCUVnAAACABUAFQHrAcAABAAiAAA3MxEjESU0JisBNzU0Ji8BBw4BHQEUFjsBMjY/AT4BPQEjNxVWVgHWGRKHFQUEF4wGBxkSwA0VBUEBAgEBFQEA/wDrEhlhBwcLBRaNBRAJ1RIZDwuXAwgEKQIAAAABAMsAawE1AUAAAgAAPwEny2pqa2prAAACAC0AAgHUAaoABwATAAABFTMuAycHDgEVFBYzMjY3IzUBANQEJTpJKCtQWG5PSGoIzwGq1ShKOSUFMAlrSE9tWVDPAAEALAAHAdQBpABPAAABIg4CFRQWFxY2NTwBNQYmMS4BMSY2MR4BMRY2Nz4BNy4BNTQ2Ny4BNzAWFz4BMzIWFz4BMRYGBx4BFRQGBx4BFRwBFRQWNz4BNTQuAiMBACxNOiFSPwgGLBsIEA4PEBEOJwkBCAQjPQsKAQUIHB8NGg4OGg0fGwkFAQoLPSQGCQYIP1IhOk0sAaQhOk0sRm4VAggEBBYNCSITDAoDARQZAwQKDgQEKD0SHAsEHhYBFAMEBAMUARYeBAscEj0oBAUTDxUgBQQIAhVuRixNOiEAAAAAAQAAAAEAAJWo+pJfDzz1AAsCAAAAAADQ/uT2AAAAAND+5PYAAAAAAgABwAAAAAgAAgAAAAAAAAABAAAB4P/gAAACAAAAAAACAAABAAAAAAAAAAAAAAAAAAAADwAAAAAAAAAAAAAAAAEAAAACAABAAgAAQAIAAAACAACAAgAAKwIAACsCAAArAgAAFQIAAMsCAAAtAgAALAAAAAAACgAUAB4ATACEANAA4gD6AQ4BQgF4AYQBpgIWAAEAAAAPAFAABQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQAiAAAAAQAAAAAAAgAOAI0AAQAAAAAAAwAiADgAAQAAAAAABAAiAJsAAQAAAAAABQAWACIAAQAAAAAABgARAFoAAQAAAAAACgA0AL0AAwABBAkAAQAiAAAAAwABBAkAAgAOAI0AAwABBAkAAwAiADgAAwABBAkABAAiAJsAAwABBAkABQAWACIAAwABBAkABgAiAGsAAwABBAkACgA0AL0AbQBhAHQAZQByAGkAYQBsAC0AdQBpAC0AaQBjAG8AbgBzAFYAZQByAHMAaQBvAG4AIAAxAC4AMABtAGEAdABlAHIAaQBhAGwALQB1AGkALQBpAGMAbwBuAHNtYXRlcmlhbC11aS1pY29ucwBtAGEAdABlAHIAaQBhAGwALQB1AGkALQBpAGMAbwBuAHMAUgBlAGcAdQBsAGEAcgBtAGEAdABlAHIAaQBhAGwALQB1AGkALQBpAGMAbwBuAHMARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('truetype'), 8 | url(data:application/font-woff;charset=utf-8;base64,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) format('woff'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | [class^="muidocs-icon-"], [class*=" muidocs-icon-"] { 14 | font-family: 'material-ui-icons'; 15 | speak: none; 16 | font-style: normal; 17 | font-weight: normal; 18 | font-variant: normal; 19 | text-transform: none; 20 | line-height: 1; 21 | 22 | /* Better Font Rendering =========== */ 23 | -webkit-font-smoothing: antialiased; 24 | -moz-osx-font-smoothing: grayscale; 25 | } 26 | 27 | .muidocs-icon-communication-phone:before { 28 | content: "\e601"; 29 | } 30 | 31 | .muidocs-icon-communication-voicemail:before { 32 | content: "\e602"; 33 | } 34 | 35 | .muidocs-icon-navigation-expand-more:before { 36 | content: "\e603"; 37 | } 38 | 39 | .muidocs-icon-action-grade:before { 40 | content: "\e604"; 41 | } 42 | 43 | .muidocs-icon-action-home:before { 44 | content: "\e605"; 45 | } 46 | 47 | .muidocs-icon-action-stars:before { 48 | content: "\e606"; 49 | } 50 | 51 | .muidocs-icon-action-thumb-up:before { 52 | content: "\e607"; 53 | } 54 | 55 | .muidocs-icon-custom-sort:before { 56 | content: "\e600"; 57 | } 58 | 59 | .muidocs-icon-custom-github:before { 60 | content: "\e625"; 61 | } 62 | 63 | .muidocs-icon-custom-arrow-drop-right:before { 64 | content: "\e60b"; 65 | } 66 | 67 | .muidocs-icon-custom-pie:before { 68 | content: "\e610"; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /actions/index.js: -------------------------------------------------------------------------------- 1 | import reqwest from 'reqwest' 2 | //pagination 3 | export const LIMIT = 5; 4 | export const SET_MAX_PAGE = 'SET_MAX_PAGE'; 5 | export const SET_CUR_PAGE = 'SET_CUR_PAGE'; 6 | export const setMaxPage = (maxPage) => { 7 | return { 8 | type:SET_MAX_PAGE, 9 | maxPage 10 | } 11 | }; 12 | export const getMaxPage = (category = '') => { 13 | return (dispatch,getState)=>{ 14 | reqwest({ 15 | url:'/countArticle', 16 | method:'get', 17 | type:'json', 18 | data: { 19 | category:category 20 | }, 21 | success:function (data) { 22 | let maxPage =Math.ceil(parseInt(data.count)/LIMIT); 23 | dispatch(setMaxPage(maxPage)); 24 | }, 25 | error:function () { 26 | dispatch(setSnackbar('获取分页数据失败T_T')) 27 | } 28 | }); 29 | } 30 | } 31 | export const setCurPage = (curPage) => { 32 | return { 33 | type:SET_CUR_PAGE, 34 | curPage 35 | } 36 | } 37 | //articleList 38 | export const SET_ARTICLE_LIST = 'SET_ARTICLE_LIST'; 39 | export const setArticleList = (articles)=>{ 40 | return { 41 | type:SET_ARTICLE_LIST, 42 | articles 43 | } 44 | } 45 | export const getArticles = (curPage,category = '') => { 46 | return (dispatch,getState)=>{ 47 | reqwest({ 48 | url:'/article/list', 49 | method:'get', 50 | type:'json', 51 | data:{ 52 | limit:LIMIT, 53 | page:curPage, 54 | category:category 55 | }, 56 | error:function (error) { 57 | dispatch(setSnackbar('加载文章列表失败了T—T 刷新试试')); 58 | }, 59 | success:function (data) { 60 | dispatch(setLoading(false)); 61 | dispatch(setArticleList(data.articleList)); 62 | dispatch(setCurPage(curPage)); 63 | } 64 | }); 65 | } 66 | } 67 | //message 68 | export const SET_MESSAGE = 'SET_MESSAGE'; 69 | export const setMessage = (messages)=>{ 70 | return { 71 | type:SET_MESSAGE, 72 | messages 73 | } 74 | } 75 | export const getMessage = (page = 1) => { 76 | return (dispatch,getState)=>{ 77 | var state = getState(); 78 | reqwest({ 79 | url:'/message/list', 80 | method:'get', 81 | type:'json', 82 | data:{ 83 | page:page, 84 | limit:LIMIT 85 | }, 86 | success:function (data) { 87 | dispatch(setLoading(false)); 88 | dispatch(setMessage(data.messages)); 89 | }, 90 | error:function (error) { 91 | dispatch(setSnackbar('获取评论失败了T—T 查看一下网络状态')); 92 | } 93 | }); 94 | } 95 | } 96 | export const sendMessage = (message) => { 97 | return (dispatch,getState)=>{ 98 | var page = (getState()).messages.curPage; 99 | reqwest({ 100 | url:'/message/add', 101 | method:'get', 102 | type:'json', 103 | data:message, 104 | success:function (data) { 105 | dispatch(setSnackbar('留言成功^_^')); 106 | dispatch(getMessage(page)); 107 | }, 108 | error:function () { 109 | dispatch(setSnackbar('评论失败了T—T 查看一下网络状态')); 110 | } 111 | }); 112 | } 113 | } 114 | export const sendReply = (reply) => { 115 | return (dispatch,getState)=>{ 116 | var page = (getState()).messages.curPage; 117 | reqwest({ 118 | url:'/reply/add', 119 | method:'get', 120 | type:'json', 121 | data:reply, 122 | success:function (data) { 123 | dispatch(setSnackbar('回复成功^_^')); 124 | dispatch(getMessage(page)); 125 | }, 126 | error:function (data) { 127 | dispatch(setSnackbar('回复失败了T—T 查看一下网络状态')); 128 | } 129 | }); 130 | } 131 | } 132 | //article 133 | export const SET_ARTICLE = 'SET_ARTICLE'; 134 | export const setArticle = (article) => { 135 | return { 136 | type:SET_ARTICLE, 137 | article 138 | } 139 | } 140 | export const getArticle = (title) => { 141 | return (dispatch) => { 142 | reqwest({ 143 | url:'/getArticle', 144 | method:'get', 145 | type:'json', 146 | data:{ title }, 147 | success:function (data) { 148 | if(data.article){ 149 | dispatch(setLoading(false)); 150 | dispatch(setArticle(data.article)); 151 | dispatch(getComment()); 152 | } 153 | }, 154 | error:function (error) { 155 | console.log(error); 156 | } 157 | }); 158 | } 159 | } 160 | //comment 161 | export const SET_COMMENT = 'SET_COMMENT'; 162 | export const setComment = (comment) => { 163 | return { 164 | type:SET_COMMENT, 165 | comment 166 | } 167 | } 168 | export const getComment = () => { 169 | return (dispatch,getState) => { 170 | var state = getState(); 171 | if(state.article.id){ 172 | reqwest({ 173 | url:'/getComment', 174 | method:'get', 175 | type:'json', 176 | data:{id:state.article.id}, 177 | success:function (data) { 178 | dispatch(setComment(data.comment)); 179 | }, 180 | error:function (error) { 181 | dispatch(setSnackbar('获取评论失败了T—T 查看一下网络状态')); 182 | } 183 | }); 184 | } 185 | } 186 | } 187 | export const sendComment = (comment) => { 188 | return (dispatch,getState)=>{ 189 | reqwest({ 190 | url:'/comment/add', 191 | method:'get', 192 | type:'json', 193 | data:comment, 194 | success:function (data) { 195 | dispatch(setSnackbar('留言成功^_^')); 196 | dispatch(getComment()); 197 | }, 198 | error:function () { 199 | dispatch(setSnackbar('评论失败了T—T 查看一下网络状态')); 200 | } 201 | }); 202 | } 203 | } 204 | export const sendReplyToComment = (reply) => { 205 | return (dispatch,getState)=>{ 206 | reqwest({ 207 | url:'/comment/reply', 208 | method:'get', 209 | type:'json', 210 | data:reply, 211 | success:function (data) { 212 | dispatch(setSnackbar('回复成功^_^')); 213 | dispatch(getComment()); 214 | }, 215 | error:function (data) { 216 | dispatch(setSnackbar('回复失败了T—T 查看一下网络状态')); 217 | } 218 | }); 219 | } 220 | } 221 | //category 222 | export const SET_CATEGORY = 'SET_CATEGORY'; 223 | export const setCategory = (category) => { 224 | return { 225 | type:SET_CATEGORY, 226 | category 227 | } 228 | } 229 | export const getCategory = () => { 230 | return (dispatch) => { 231 | reqwest({ 232 | url:'/newArticleList', 233 | method:'get', 234 | type:'json', 235 | success:function (data) { 236 | dispatch(setCategory(data.newArticleList)); 237 | } 238 | }); 239 | } 240 | } 241 | //resState 242 | export const SET_LOADING = 'SET_LOADING'; 243 | export const setLoading = (isLoading) => { 244 | return { 245 | type:SET_LOADING, 246 | isLoading 247 | } 248 | } 249 | export const SET_SNACKBAR = 'SET_SNACKBAR'; 250 | export const setSnackbar = (message) => { 251 | return { 252 | type:SET_SNACKBAR, 253 | message 254 | } 255 | } -------------------------------------------------------------------------------- /css/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: auto; 7 | color: black; 8 | padding-left: 24px; 9 | padding-top: 24px; 10 | border-radius: 0 0 2px 2px; 11 | border-top: solid 1px #e0e0e0; 12 | } 13 | 14 | /* PADDING */ 15 | 16 | .CodeMirror-lines { 17 | padding: 4px 0; /* Vertical padding around content */ 18 | } 19 | .CodeMirror pre { 20 | padding: 0 4px; /* Horizontal padding of content */ 21 | } 22 | 23 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 24 | background-color: white; /* The little square between H and V scrollbars */ 25 | } 26 | 27 | /* GUTTER */ 28 | 29 | .CodeMirror-gutters { 30 | border-right: 1px solid #ddd; 31 | background-color: #f7f7f7; 32 | white-space: nowrap; 33 | } 34 | .CodeMirror-linenumbers {} 35 | .CodeMirror-linenumber { 36 | padding: 0 3px 0 5px; 37 | min-width: 20px; 38 | text-align: right; 39 | color: #999; 40 | white-space: nowrap; 41 | } 42 | 43 | .CodeMirror-guttermarker { color: black; } 44 | .CodeMirror-guttermarker-subtle { color: #999; } 45 | 46 | /* CURSOR */ 47 | 48 | .CodeMirror div.CodeMirror-cursor { 49 | border-left: 1px solid black; 50 | } 51 | /* Shown when moving in bi-directional text */ 52 | .CodeMirror div.CodeMirror-secondarycursor { 53 | border-left: 1px solid silver; 54 | } 55 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursor { 56 | width: auto; 57 | border: 0; 58 | background: #7e7; 59 | } 60 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursors { 61 | z-index: 1; 62 | } 63 | 64 | .cm-animate-fat-cursor { 65 | width: auto; 66 | border: 0; 67 | -webkit-animation: blink 1.06s steps(1) infinite; 68 | -moz-animation: blink 1.06s steps(1) infinite; 69 | animation: blink 1.06s steps(1) infinite; 70 | background-color: #7e7; 71 | } 72 | @-moz-keyframes blink { 73 | 0% {} 74 | 50% { background-color: transparent; } 75 | 100% {} 76 | } 77 | @-webkit-keyframes blink { 78 | 0% {} 79 | 50% { background-color: transparent; } 80 | 100% {} 81 | } 82 | @keyframes blink { 83 | 0% {} 84 | 50% { background-color: transparent; } 85 | 100% {} 86 | } 87 | 88 | /* Can style cursor different in overwrite (non-insert) mode */ 89 | div.CodeMirror-overwrite div.CodeMirror-cursor {} 90 | 91 | .cm-tab { display: inline-block; text-decoration: inherit; } 92 | 93 | .CodeMirror-ruler { 94 | border-left: 1px solid #ccc; 95 | position: absolute; 96 | } 97 | 98 | /* DEFAULT THEME */ 99 | 100 | .cm-s-default .cm-header {color: blue;} 101 | .cm-s-default .cm-quote {color: #090;} 102 | .cm-negative {color: #d44;} 103 | .cm-positive {color: #292;} 104 | .cm-header, .cm-strong {font-weight: bold;} 105 | .cm-em {font-style: italic;} 106 | .cm-link {text-decoration: underline;} 107 | .cm-strikethrough {text-decoration: line-through;} 108 | 109 | .cm-s-default .cm-keyword {color: #708;} 110 | .cm-s-default .cm-atom {color: #219;} 111 | .cm-s-default .cm-number {color: #164;} 112 | .cm-s-default .cm-def {color: #00f;} 113 | .cm-s-default .cm-variable, 114 | .cm-s-default .cm-punctuation, 115 | .cm-s-default .cm-property, 116 | .cm-s-default .cm-operator {} 117 | .cm-s-default .cm-variable-2 {color: #05a;} 118 | .cm-s-default .cm-variable-3 {color: #085;} 119 | .cm-s-default .cm-comment {color: #a50;} 120 | .cm-s-default .cm-string {color: #a11;} 121 | .cm-s-default .cm-string-2 {color: #f50;} 122 | .cm-s-default .cm-meta {color: #555;} 123 | .cm-s-default .cm-qualifier {color: #555;} 124 | .cm-s-default .cm-builtin {color: #30a;} 125 | .cm-s-default .cm-bracket {color: #997;} 126 | .cm-s-default .cm-tag {color: #170;} 127 | .cm-s-default .cm-attribute {color: #00c;} 128 | .cm-s-default .cm-hr {color: #999;} 129 | .cm-s-default .cm-link {color: #00c;} 130 | 131 | .cm-s-default .cm-error {color: #f00;} 132 | .cm-invalidchar {color: #f00;} 133 | 134 | .CodeMirror-composing { border-bottom: 2px solid; } 135 | 136 | /* Default styles for common addons */ 137 | 138 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 139 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 140 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 141 | .CodeMirror-activeline-background {background: #e8f2ff;} 142 | 143 | /* STOP */ 144 | 145 | /* The rest of this file contains styles related to the mechanics of 146 | the editor. You probably shouldn't touch them. */ 147 | 148 | .CodeMirror { 149 | position: relative; 150 | overflow: hidden; 151 | background: #f8f8f8; 152 | } 153 | 154 | .CodeMirror-scroll { 155 | overflow: scroll !important; /* Things will break if this is overridden */ 156 | /* 30px is the magic margin used to hide the element's real scrollbars */ 157 | /* See overflow: hidden in .CodeMirror */ 158 | margin-bottom: -30px; margin-right: -30px; 159 | padding-bottom: 30px; 160 | height: 100%; 161 | outline: none; /* Prevent dragging from highlighting the element */ 162 | position: relative; 163 | } 164 | .CodeMirror-sizer { 165 | position: relative; 166 | border-right: 30px solid transparent; 167 | } 168 | 169 | /* The fake, visible scrollbars. Used to force redraw during scrolling 170 | before actuall scrolling happens, thus preventing shaking and 171 | flickering artifacts. */ 172 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 173 | position: absolute; 174 | z-index: 6; 175 | display: none; 176 | } 177 | .CodeMirror-vscrollbar { 178 | right: 0; top: 0; 179 | overflow-x: hidden; 180 | overflow-y: hidden; 181 | } 182 | .CodeMirror-hscrollbar { 183 | bottom: 0; left: 0; 184 | overflow-y: hidden; 185 | overflow-x: scroll; 186 | } 187 | .CodeMirror-scrollbar-filler { 188 | right: 0; bottom: 0; 189 | } 190 | .CodeMirror-gutter-filler { 191 | left: 0; bottom: 0; 192 | } 193 | 194 | .CodeMirror-gutters { 195 | position: absolute; left: 0; top: 0; 196 | z-index: 3; 197 | } 198 | .CodeMirror-gutter { 199 | white-space: normal; 200 | height: 100%; 201 | display: inline-block; 202 | margin-bottom: -30px; 203 | /* Hack to make IE7 behave */ 204 | *zoom:1; 205 | *display:inline; 206 | } 207 | .CodeMirror-gutter-wrapper { 208 | position: absolute; 209 | z-index: 4; 210 | background: none !important; 211 | border: none !important; 212 | } 213 | .CodeMirror-gutter-background { 214 | position: absolute; 215 | top: 0; bottom: 0; 216 | z-index: 4; 217 | } 218 | .CodeMirror-gutter-elt { 219 | position: absolute; 220 | cursor: default; 221 | z-index: 4; 222 | } 223 | .CodeMirror-gutter-wrapper { 224 | -webkit-user-select: none; 225 | -moz-user-select: none; 226 | user-select: none; 227 | } 228 | 229 | .CodeMirror-lines { 230 | cursor: text; 231 | min-height: 1px; /* prevents collapsing before first draw */ 232 | } 233 | .CodeMirror pre { 234 | /* Reset some styles that the rest of the page might have set */ 235 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 236 | border-width: 0; 237 | background: transparent; 238 | font-family: inherit; 239 | font-size: inherit; 240 | margin: 0; 241 | white-space: pre; 242 | word-wrap: normal; 243 | line-height: inherit; 244 | color: inherit; 245 | z-index: 2; 246 | position: relative; 247 | overflow: visible; 248 | -webkit-tap-highlight-color: transparent; 249 | } 250 | .CodeMirror-wrap pre { 251 | word-wrap: break-word; 252 | white-space: pre-wrap; 253 | word-break: normal; 254 | } 255 | 256 | .CodeMirror-linebackground { 257 | position: absolute; 258 | left: 0; right: 0; top: 0; bottom: 0; 259 | z-index: 0; 260 | } 261 | 262 | .CodeMirror-linewidget { 263 | position: relative; 264 | z-index: 2; 265 | overflow: auto; 266 | } 267 | 268 | .CodeMirror-widget {} 269 | 270 | .CodeMirror-code { 271 | outline: none; 272 | } 273 | 274 | /* Force content-box sizing for the elements where we expect it */ 275 | .CodeMirror-scroll, 276 | .CodeMirror-sizer, 277 | .CodeMirror-gutter, 278 | .CodeMirror-gutters, 279 | .CodeMirror-linenumber { 280 | -moz-box-sizing: content-box; 281 | box-sizing: content-box; 282 | } 283 | 284 | .CodeMirror-measure { 285 | position: absolute; 286 | width: 100%; 287 | height: 0; 288 | overflow: hidden; 289 | visibility: hidden; 290 | } 291 | .CodeMirror-measure pre { position: static; } 292 | 293 | .CodeMirror div.CodeMirror-cursor { 294 | position: absolute; 295 | border-right: none; 296 | width: 0; 297 | } 298 | 299 | div.CodeMirror-cursors { 300 | visibility: hidden; 301 | position: relative; 302 | z-index: 3; 303 | } 304 | .CodeMirror-focused div.CodeMirror-cursors { 305 | visibility: visible; 306 | } 307 | 308 | .CodeMirror-selected { background: #d9d9d9; } 309 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 310 | .CodeMirror-crosshair { cursor: crosshair; } 311 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 312 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 313 | 314 | .cm-searching { 315 | background: #ffa; 316 | background: rgba(255, 255, 0, .4); 317 | } 318 | 319 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 320 | .CodeMirror span { *vertical-align: text-bottom; } 321 | 322 | /* Used to force a border model for a node */ 323 | .cm-force-border { padding-right: .1px; } 324 | 325 | @media print { 326 | /* Hide the cursor when printing */ 327 | .CodeMirror div.CodeMirror-cursors { 328 | visibility: hidden; 329 | } 330 | } 331 | 332 | /* See issue #2901 */ 333 | .cm-tab-wrap-hack:after { content: ''; } 334 | 335 | /* Help users use markselection to safely style text background */ 336 | span.CodeMirror-selectedtext { background: none; } 337 | --------------------------------------------------------------------------------