├── .gitignore
├── .jshintrc
├── imgs
├── fuli.png
├── img.png
├── 小程序.jpg
├── android.png
├── phone_img.png
├── phone_fuli.png
└── phone_item.png
├── .babelrc
├── src
├── data
│ ├── actionType.js
│ └── config.js
├── reducers
│ ├── rootReducer.js
│ ├── modalReducer.js
│ └── navReducer.js
├── index.html
├── action
│ └── customAction.js
├── components
│ ├── main
│ │ ├── Titlebar.js
│ │ └── Navigation.js
│ ├── pager
│ │ ├── ColumnContent.js
│ │ ├── ColumnImg.js
│ │ ├── CardCommon.js
│ │ ├── CardImg.js
│ │ ├── ContentCommon.js
│ │ └── ContentImg.js
│ └── Main.js
├── store
│ └── index.js
├── util
│ └── MyUtil.js
├── index.js
└── containers
│ ├── MainContainer.js
│ └── NavigantionContainer.js
├── dist
└── index.html
├── README.md
├── stylesheets
└── scss
│ └── Gallery.scss
├── package.json
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /.idea
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "esnext": true
3 | }
4 |
--------------------------------------------------------------------------------
/imgs/fuli.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtsky/react-gank/HEAD/imgs/fuli.png
--------------------------------------------------------------------------------
/imgs/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtsky/react-gank/HEAD/imgs/img.png
--------------------------------------------------------------------------------
/imgs/小程序.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtsky/react-gank/HEAD/imgs/小程序.jpg
--------------------------------------------------------------------------------
/imgs/android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtsky/react-gank/HEAD/imgs/android.png
--------------------------------------------------------------------------------
/imgs/phone_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtsky/react-gank/HEAD/imgs/phone_img.png
--------------------------------------------------------------------------------
/imgs/phone_fuli.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtsky/react-gank/HEAD/imgs/phone_fuli.png
--------------------------------------------------------------------------------
/imgs/phone_item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtsky/react-gank/HEAD/imgs/phone_item.png
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "react"
5 | ],
6 | "plugins": []
7 | }
8 |
--------------------------------------------------------------------------------
/src/data/actionType.js:
--------------------------------------------------------------------------------
1 | export const CHANGE_NAVIGATION= 'CHANGE_NAVIGATION';
2 | export const LOAD_DATA = 'LOAD_DATA';
3 | export const SHOW_PROGRESS = 'SHOW_PROGRESS';
4 | export const SHOW_MODAL = 'SHOW_MODAL';
--------------------------------------------------------------------------------
/src/reducers/rootReducer.js:
--------------------------------------------------------------------------------
1 | import {combineReducers} from 'redux-immutable';
2 | import nav from './navReducer';
3 | import modal from './modalReducer';
4 |
5 | const rootReducer = combineReducers({
6 | nav, modal
7 | });
8 |
9 | export default rootReducer;
10 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | react gank
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | react gank
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/reducers/modalReducer.js:
--------------------------------------------------------------------------------
1 | import {handleActions} from 'redux-actions';
2 | import {initState} from '../data/config';
3 | import {SHOW_MODAL} from '../data/actionType';
4 |
5 | const modalReducer = handleActions({
6 |
7 | SHOW_MODAL: (state, {payload})=> {
8 | return state.set('modalSrc', payload.modalSrc);
9 | },
10 |
11 | }, initState.get('modal'));
12 |
13 | export default modalReducer;
--------------------------------------------------------------------------------
/src/action/customAction.js:
--------------------------------------------------------------------------------
1 | import {createAction} from 'redux-actions';
2 | import {CHANGE_NAVIGATION, LOAD_DATA, SHOW_PROGRESS, SHOW_MODAL} from '../data/actionType';
3 | //创建指定类型的action
4 | const changeNav = createAction(CHANGE_NAVIGATION);
5 | const showProgress = createAction(SHOW_PROGRESS);
6 | const showModal = createAction(SHOW_MODAL);
7 | const loadData = createAction(LOAD_DATA);
8 |
9 | export {changeNav, showProgress,showModal,loadData};
--------------------------------------------------------------------------------
/src/data/config.js:
--------------------------------------------------------------------------------
1 | import Immutable from 'immutable';
2 |
3 | const titles = [
4 | "福利",
5 | "Android",
6 | "iOS",
7 | "all",
8 | "前端",
9 | "休息视频",
10 | "拓展资源"];
11 |
12 | //初始化数据
13 | const initState = Immutable.fromJS({
14 | nav: {
15 | index: 0,
16 | showProgress: 'flex',
17 |
18 | },
19 | modal: {
20 | modalSrc: ''
21 | }
22 | });
23 | export {titles, initState} ;
24 |
--------------------------------------------------------------------------------
/src/reducers/navReducer.js:
--------------------------------------------------------------------------------
1 | import {handleActions} from 'redux-actions';
2 | import {initState} from '../data/config';
3 | import {CHANGE_NAVIGATION, SHOW_PROGRESS} from '../data/actionType';
4 |
5 | const navReducer = handleActions({
6 | CHANGE_NAVIGATION: (state, {payload})=> {
7 | return state.set('index', payload.index);
8 | },
9 | SHOW_PROGRESS: (state, {payload})=> {
10 | return state.set('showProgress', payload.showProgress);
11 | },
12 |
13 | }, initState.get('nav'));
14 |
15 | export default navReducer;
--------------------------------------------------------------------------------
/src/components/main/Titlebar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import AppBar from 'material-ui/AppBar';
3 | import FlatButton from 'material-ui/FlatButton';
4 |
5 | const Titlebar = (props)=>(
6 | {window.open("/vue-gank.html","vue-gank");}}/>}/>
8 | );
9 |
10 | Titlebar.propTypes = {
11 | title: React.PropTypes.string,
12 | };
13 |
14 | // Prop 預設值,若對應 props 沒傳入值將會使用 default 值
15 | Titlebar.defaultProps = {
16 | title: '干货集中营',
17 | }
18 |
19 | export default Titlebar;
--------------------------------------------------------------------------------
/src/components/pager/ColumnContent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CardCommon from './CardCommon';
3 | const ColumnContent = (({element, column})=> {
4 | let elemets = [];
5 | for (let index in element) {
6 | let dataInfo = element[index];
7 | elemets.push(
8 |
9 | );
10 | }
11 |
12 | return (
13 |
14 | {elemets}
15 |
16 | );
17 | });
18 |
19 | ColumnContent.propTypes = {
20 | element: React.PropTypes.array.isRequired
21 | };
22 |
23 | export default ColumnContent;
--------------------------------------------------------------------------------
/src/components/pager/ColumnImg.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CardImg from './CardImg';
3 | const ColumnImg = (({element, showModal, column})=> {
4 | let elemets = [];
5 | for (let index in element) {
6 | let dataInfo = element[index];
7 | elemets.push(
8 |
9 | );
10 | }
11 |
12 | return (
13 |
14 | {elemets}
15 |
16 | );
17 | });
18 |
19 | ColumnImg.propTypes = {
20 | element: React.PropTypes.array.isRequired
21 | };
22 |
23 | export default ColumnImg;
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import {createStore, applyMiddleware} from 'redux';
2 | import createLogger from 'redux-logger';
3 | import thunk from 'redux-thunk';
4 | import Immutable from 'immutable';
5 | import rootReducer from '../reducers/rootReducer';
6 |
7 | const initialState = Immutable.Map();
8 | /*export default createStore(
9 | rootReducer,
10 | initialState,
11 | // window.devToolsExtension && window.devToolsExtension(),
12 | applyMiddleware(thunk, createLogger({stateTransformer: state => state.toJS()})),
13 | );*/
14 | //开启浏览器调试
15 | export default createStore(
16 | rootReducer,
17 | initialState,
18 | window.devToolsExtension && window.devToolsExtension(),
19 | //applyMiddleware(thunk ,createLogger({ stateTransformer: state => state.toJS()})),
20 | );
21 |
--------------------------------------------------------------------------------
/src/components/pager/CardCommon.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Card, CardTitle, CardText} from 'material-ui/Card';
3 |
4 | const CardCommon = ({dataInfo,})=> {
5 | return (
6 |
7 |
8 |
9 |
10 | 描述:{dataInfo.desc}
11 |
12 | 地址:{dataInfo.url}
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | CardCommon.propTypes = {
20 | dataInfo: React.PropTypes.object.isRequired,
21 | };
22 |
23 | export default CardCommon;
--------------------------------------------------------------------------------
/src/util/MyUtil.js:
--------------------------------------------------------------------------------
1 | const getBrowserPlatform = function () {
2 | var sUserAgent = navigator.userAgent.toLowerCase();
3 | var bIsIpad = sUserAgent.match(/ipad/i) == "ipad";
4 | var bIsIphoneOs = sUserAgent.match(/iphone os/i) == "iphone os";
5 | var bIsMidp = sUserAgent.match(/midp/i) == "midp";
6 | var bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) == "rv:1.2.3.4";
7 | var bIsUc = sUserAgent.match(/ucweb/i) == "ucweb";
8 | var bIsAndroid = sUserAgent.match(/android/i) == "android";
9 | var bIsCE = sUserAgent.match(/windows ce/i) == "windows ce";
10 | var bIsWM = sUserAgent.match(/windows mobile/i) == "windows mobile";
11 | if (bIsIpad || bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM) {
12 | return "phone";
13 | } else {
14 | return "pc";
15 | }
16 | }
17 |
18 | export {getBrowserPlatform};
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #React-gank
2 | ### react+react-redux+material-ui+whatwg-fetch
3 | #react版 https://jtsky.github.io/react-gank.html
4 | #demo https://github.com/jtsky/react-gank
5 | #vue2.0版 https://jtsky.github.io/vue-gank.html
6 | #demo https://github.com/jtsky/vue-gank
7 | #小程序版 https://github.com/jtsky/WX_Gank
8 | #demo
9 | 
10 | 欢迎扫描试用
11 | ###效果图如下
12 | 
13 | 
14 | 
15 | 
16 | 
17 | 
18 |
--------------------------------------------------------------------------------
/src/components/pager/CardImg.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Card, CardMedia, CardTitle} from 'material-ui/Card';
3 | import {getBrowserPlatform} from '../../util/MyUtil';
4 | const CardImg = ({dataInfo, showModal})=> {
5 | let overlay;
6 | if (getBrowserPlatform() !== 'phone') {
7 | overlay = ;
8 | }
9 |
10 |
11 | return (
12 | showModal(dataInfo.url)}>
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 |
21 | };
22 |
23 | CardImg.propTypes = {
24 | dataInfo: React.PropTypes.object.isRequired,
25 | };
26 |
27 | export default CardImg;
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import {Provider} from 'react-redux';
4 | import injectTapEventPlugin from 'react-tap-event-plugin';
5 | import {browserHistory, Router, Route, IndexRoute} from 'react-router';
6 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
7 | import MainContainer from './containers/MainContainer';
8 | import store from './store';
9 |
10 | // Needed for onTouchTap
11 | // http://stackoverflow.com/a/34015469/988941
12 | //支持移动端触摸事件
13 | injectTapEventPlugin();
14 |
15 | ReactDOM.render(
16 |
17 |
18 |
19 |
20 | {/**/}
21 |
22 |
23 |
24 |
25 | , document.getElementById('app')
26 | );
--------------------------------------------------------------------------------
/stylesheets/scss/Gallery.scss:
--------------------------------------------------------------------------------
1 | .flex-container {
2 | display: -moz-box; /* Firefox */
3 | display: -ms-flexbox; /* IE10 */
4 | display: -webkit-box; /* Safari */
5 | display: -webkit-flex; /* Chrome, WebKit */
6 | display: flex;
7 | width: 100%;
8 | }
9 |
10 | .flex-inline-container {
11 | display: -moz-inline-box; /* Firefox */
12 | display: -ms-inline-flexbox; /* IE10 */
13 | display: -webkit-inline-flex; /* Chrome, WebKit */
14 | display: inline-flex;
15 | width: 100%;
16 |
17 | }
18 |
19 | $percent_width: 10% !default;
20 |
21 | .infinite {
22 | width: 100%;
23 | min-height: 400px;
24 | height: auto !important;
25 | height: 400px;
26 | }
27 |
28 | .column-common {
29 | @extend .flex-inline-container;
30 | width: $percent_width;
31 | height: auto;
32 | flex-direction: column;
33 | }
34 |
35 | $column-list:1 2 3 4 5 6 7 8 9 10;
36 | @each $column in $column-list {
37 | .column-#{$column} {
38 | @extend .column-common;
39 | width: 100 / $column * 1%;
40 | flex-direction: column;
41 | }
42 | }
43 |
44 | .column-img {
45 | width: 100%;
46 | }
47 |
48 | .modal {
49 | width: 100%;
50 | margin: auto 0;
51 | .img {
52 | width: 100%;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/Main.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import Titlebar from './main/Titlebar';
3 | import NavigantionContainer from '../containers/NavigantionContainer';
4 | import FlyModal from 'boron/FlyModal';
5 | import {titles} from '../data/config';
6 | import {showModal} from '../action/customAction';
7 | import store from '../store';
8 | class Main extends Component {
9 | constructor(props) {
10 | super(props);
11 | this.showModal = this.showModal.bind(this);
12 | this.hideModal = this.hideModal.bind(this);
13 | }
14 |
15 | //组件加载完成
16 | componentDidMount() {
17 |
18 | }
19 |
20 | render() {
21 | let {modalSrc} = this.props;
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | );
31 | }
32 |
33 | showModal(url) {
34 | store.dispatch(showModal({modalSrc: url}));
35 | this.refs.modal.show();
36 | }
37 |
38 | hideModal() {
39 | this.refs.modal.hide();
40 | }
41 | }
42 |
43 | Main.propTypes = {
44 | modalSrc: React.PropTypes.string,
45 | };
46 |
47 | // Prop 預設值,若對應 props 沒傳入值將會使用 default 值
48 | Main.defaultProps = {}
49 |
50 | export default Main;
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-gank",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "dev": "webpack-dev-server --devtool eval --progress --colors --content-base build ",
9 | "publish-win": "set NODE_ENV=prod&&webpack -p --progress --colors"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "boron": "^0.2.3",
15 | "immutable": "^3.8.1",
16 | "material-ui": "^0.15.4",
17 | "react": "^15.3.0",
18 | "react-dom": "^15.3.0",
19 | "react-infinite-loader": "^1.0.2",
20 | "react-motion": "^0.4.5",
21 | "react-redux": "^4.4.5",
22 | "react-router": "^2.6.1",
23 | "react-swipeable-views": "^0.7.8",
24 | "react-tap-event-plugin": "^1.0.0",
25 | "redux": "^3.5.2",
26 | "redux-actions": "^0.10.1",
27 | "redux-immutable": "^3.0.7",
28 | "redux-thunk": "^2.1.0",
29 | "whatwg-fetch": "^1.0.0"
30 | },
31 | "devDependencies": {
32 | "babel-core": "^6.14.0",
33 | "babel-eslint": "^6.1.2",
34 | "babel-loader": "^6.2.5",
35 | "babel-preset-es2015": "^6.14.0",
36 | "babel-preset-react": "^6.11.1",
37 | "css-loader": "^0.25.0",
38 | "eslint": "^3.6.0",
39 | "eslint-config-airbnb": "^12.0.0",
40 | "eslint-loader": "^1.5.0",
41 | "eslint-plugin-import": "^1.16.0",
42 | "eslint-plugin-jsx-a11y": "^2.2.2",
43 | "eslint-plugin-react": "^6.3.0",
44 | "html-webpack-plugin": "^2.22.0",
45 | "jsx-loader": "^0.13.2",
46 | "node-sass": "^3.10.1",
47 | "redux-logger": "^2.6.1",
48 | "sass-loader": "^4.0.2",
49 | "style-loader": "^0.13.1",
50 | "webpack": "^1.13.2",
51 | "webpack-dev-server": "^1.16.1"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/main/Navigation.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {Tabs, Tab} from 'material-ui/Tabs';
3 | import SwipeableViews from 'react-swipeable-views';
4 | import ContentImg from '../pager/ContentImg';
5 | import ContentCommon from '../pager/ContentCommon';
6 | import {getBrowserPlatform} from '../../util/MyUtil';
7 |
8 | const styles = {
9 | slide: {
10 | padding: 5,
11 | marginLeft : 'auto',
12 | marginRight : 'auto',
13 | },
14 |
15 | };
16 |
17 |
18 | const Navigation = ({titles, index, onHandleChange, showProgress,showModal})=> {
19 | let imageColumn = 4;
20 | let commonColumn = 3;
21 | let fontSize = 16;
22 | if (getBrowserPlatform() === 'phone') {
23 | imageColumn = 2;
24 | commonColumn = 1;
25 | fontSize = 10;
26 | }
27 |
28 | let titleRows = [];
29 | let pagerRows = [];
30 | titles.forEach((title, index)=> {
31 | titleRows.push();
32 | if (index == 0) {
33 | pagerRows.push(
34 |
35 |
);
36 | } else {
37 | pagerRows.push(
38 |
39 |
);
40 | }
41 |
42 | });
43 | return (
44 |
45 |
48 | {titleRows}
49 |
50 |
53 | {pagerRows}
54 |
55 |
56 |
57 | );
58 |
59 | };
60 |
61 |
62 | Navigation.propTypes = {
63 | titles: React.PropTypes.array.isRequired,
64 | index: React.PropTypes.number.isRequired,
65 | onHandleChange: React.PropTypes.func
66 |
67 | };
68 |
69 | // Prop 預設值,若對應 props 沒傳入值將會使用 default 值
70 | Navigation.defaultProps = {
71 | titles: [],
72 | index: 0
73 | }
74 |
75 |
76 | export default Navigation;
--------------------------------------------------------------------------------
/src/containers/MainContainer.js:
--------------------------------------------------------------------------------
1 | import {connect} from 'react-redux';
2 | import Main from '../components/Main';
3 |
4 | //创建组件容器,可以对传入组件的props值进行过滤组合 类似于自定义中间件
5 | //http://cn.redux.js.org/docs/react-redux/api.html
6 | export default connect(
7 | /*[mapStateToProps(state, [ownProps]): stateProps] (Function)如果定义该参数,组件将会监听 Redux store 的变化。
8 | *任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。
9 | *该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果你省略了这个参数,
10 | *你的组件将不会监听 Redux store。如果指定了该回调函数中的第二个参数 ownProps,
11 | *则该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToProps 也会被调用
12 | *@param state store中取得的全局state
13 | *@ownProps 从父组件传入的props
14 | * return 传给包装组件的props
15 | */
16 | (state, ownProps)=> {
17 | return {
18 | modalSrc : state.getIn(['modal', 'modalSrc'])
19 | };
20 | },
21 | /*
22 | * [mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): 如果传递的是一个对象,
23 | * 那么每个定义在该对象的函数都将被当作 Redux action creator,而且这个对象会与 Redux store 绑定在一起,
24 | * 其中所定义的方法名将作为属性名,合并到组件的 props 中。如果传递的是一个函数,该函数将接收一个 dispatch 函数,
25 | * 然后由你来决定如何返回一个对象,这个对象通过 dispatch 函数与 action creator 以某种方式绑定在一起
26 | * (提示:你也许会用到 Redux 的辅助函数 bindActionCreators())。
27 | * 如果你省略这个 mapDispatchToProps 参数,默认情况下,dispatch 会注入到你的组件 props 中。
28 | * 如果指定了该回调函数中第二个参数 ownProps,该参数的值为传递到组件的 props,而且只要组件接收到新 props,
29 | * mapDispatchToProps 也会被调用。
30 | * */
31 | (dispatch)=> {
32 | return {}
33 | },
34 | /*
35 | * [mergeProps(stateProps, dispatchProps, ownProps): props] (Function): 如果指定了这个参数,
36 | * mapStateToProps() 与 mapDispatchToProps() 的执行结果和组件自身的 props 将传入到这个回调函数中。
37 | * 该回调函数返回的对象将作为 props 传递到被包装的组件中。你也许可以用这个回调函数,根据组件的 props 来筛选部分的 state 数据,
38 | * 或者把 props 中的某个特定变量与 action creator 绑定在一起。如果你省略这个参数,
39 | * 默认情况下返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果
40 | *
41 | * */
42 | (stateProps, dispatchProps, ownProps)=> {
43 | return Object.assign({}, ownProps, stateProps, dispatchProps);
44 | },
45 | /*
46 | * [options] (Object) 如果指定这个参数,可以定制 connector 的行为。
47 | [pure = true] (Boolean): 如果为 true,connector 将执行 shouldComponentUpdate 并且浅对比 mergeProps 的结果,
48 | 避免不必要的更新,前提是当前组件是一个“纯”组件,它不依赖于任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为 true。
49 | [withRef = false] (Boolean): 如果为 true,connector 会保存一个对被包装组件实例的引用,该引用通过 getWrappedInstance() 方法获得。
50 | 默认值为 false
51 | * */
52 | {},
53 | )(Main);
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | // 這邊使用 HtmlWebpackPlugin,將 bundle 好的