├── .editorconfig
├── .eslintrc
├── .gitignore
├── .umirc.js
├── LICENSE
├── README.md
├── mock
└── .gitkeep
├── package-lock.json
├── package.json
├── src
├── assets
│ └── yay.jpg
├── components
│ └── Example.js
├── global.css
├── models
│ └── example.js
├── pages
│ ├── css
│ │ ├── index.css
│ │ └── index.css.map
│ ├── example
│ │ ├── components
│ │ │ ├── Counter.tsx
│ │ │ └── CounterButton.tsx
│ │ ├── index.less
│ │ ├── models
│ │ │ └── counter.tsx
│ │ └── page.tsx
│ └── index.tsx
├── services
│ └── example.js
└── utils
│ ├── delay.tsx
│ └── request.js
├── tsconfig.json
└── tslint.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [Makefile]
16 | indent_style = tab
17 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "umi"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # production
7 | /dist
8 |
9 | # misc
10 | .DS_Store
11 | npm-debug.log*
12 |
13 | # umi
14 | .umi
15 | .umi-production
16 |
--------------------------------------------------------------------------------
/.umirc.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: [
3 | [
4 | "umi-plugin-react",
5 | {
6 | dva: true,
7 | antd: true
8 | }
9 | ]
10 | ],
11 | theme: {
12 | "primary-color": "#0aa679"
13 | },
14 | base: "/react/react-count-game/",
15 | publicPath: "/react/react-count-game/",
16 | routes: [
17 | { path: "/", redirect: "/example", component: "./index.tsx" },
18 | { path: "/example", component: "./example/page.tsx" }
19 | ]
20 | };
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 yyge
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-calculator-count-app
2 |
3 |
4 | ## 项目介绍
5 | + 测试鼠标速度的小游戏
6 | + 基于```react```, ```dva```, ```typescript```, ```antd```
7 | + 学习 ts 之后的练手小游戏
8 |
9 |
10 | ### 安装依赖
11 | ```
12 | npm install
13 | ```
14 |
15 | ### 启动项目
16 | ```
17 | npm start
18 |
19 | 浏览器地址栏接上 /example 即可玩耍
20 | ```
21 |
22 | ### 注意
23 | ```
24 | 非常简单的小游戏, 但是包含了 ts 在 react 中的应用,
25 | tslint.json 可以按需配置
26 | ```
27 |
28 | ### Enjoy
--------------------------------------------------------------------------------
/mock/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ddzy/react-count-game/f91055712fa09e8374fc6db1406053dcc4da91bf/mock/.gitkeep
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "start": "umi dev",
5 | "build": "umi build",
6 | "lint": "eslint --ext .js src test",
7 | "precommit": "npm run lint"
8 | },
9 | "dependencies": {
10 | "antd": "^3.6.6",
11 | "dva": "^2.3.1",
12 | "keymaster": "^1.6.2",
13 | "umi": "^2.13.12",
14 | "umi-plugin-dva": "^0.7.1",
15 | "umi-plugin-react": "^1.0.0-beta.16"
16 | },
17 | "devDependencies": {
18 | "@types/react": "^16.4.6",
19 | "@types/react-dom": "^16.0.6",
20 | "eslint": "^4.14.0",
21 | "eslint-config-umi": "^0.1.1",
22 | "eslint-plugin-flowtype": "^2.34.1",
23 | "eslint-plugin-import": "^2.6.0",
24 | "eslint-plugin-jsx-a11y": "^5.1.1",
25 | "eslint-plugin-react": "^7.1.0",
26 | "husky": "^0.12.0",
27 | "ts-loader": "^4.4.2",
28 | "tslint-react": "^3.6.0",
29 | "typings-for-css-modules-loader": "^1.7.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/assets/yay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ddzy/react-count-game/f91055712fa09e8374fc6db1406053dcc4da91bf/src/assets/yay.jpg
--------------------------------------------------------------------------------
/src/components/Example.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Example = () => {
4 | return (
5 |
6 | Example
7 |
8 | );
9 | };
10 |
11 | Example.propTypes = {
12 | };
13 |
14 | export default Example;
15 |
--------------------------------------------------------------------------------
/src/global.css:
--------------------------------------------------------------------------------
1 |
2 | html, body, :global(#root) {
3 | height: 100%;
4 | font-family: -apple-system, BlinkMacSystemFont,
5 | "Segoe UI", "Roboto", "Oxygen",
6 | "Ubuntu", "Cantarell", "Fira Sans",
7 | "Droid Sans", "Helvetica Neue", sans-serif;
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/src/models/example.js:
--------------------------------------------------------------------------------
1 |
2 | export default {
3 |
4 | namespace: 'example',
5 |
6 | state: {},
7 |
8 | subscriptions: {
9 | setup({ dispatch, history }) {
10 | },
11 | },
12 |
13 | effects: {
14 | *fetch({ payload }, { call, put }) {
15 | yield put({ type: 'save' });
16 | },
17 | },
18 |
19 | reducers: {
20 | save(state, action) {
21 | return { ...state, ...action.payload };
22 | },
23 | },
24 |
25 | };
26 |
--------------------------------------------------------------------------------
/src/pages/css/index.css:
--------------------------------------------------------------------------------
1 | .app-content{box-sizing:border-box;overflow:hidden;width:400px;height:400px;margin:50px auto;border:1px solid #ccc;box-shadow:0 0 4px 4px #dddddd}.counterbox{display:flex;flex-direction:column;box-sizing:border-box;height:100%;padding:10px}.counterbox .counter-show{flex:1;line-height:1.5;font-size:20px;color:#666}.counterbox .counter-currentcount{height:100px;line-height:100px;font-size:30px}.counterbox .counter-button{flex:3;display:flex;justify-content:center;align-items:center;height:100%;margin-top:10px}/*# sourceMappingURL=./index.css.map */
--------------------------------------------------------------------------------
/src/pages/css/index.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../example/index.less"],"names":[],"mappings":"AAEA,aACE,qBAAA,CACA,eAAA,CACA,WAAA,CACA,YAAA,CACA,gBAAA,CACA,qBAAA,CACA,+BAGF,YACE,YAAA,CACA,qBAAA,CACA,qBAAA,CACA,WAAA,CACA,aAIF,WAAY,eACV,MAAA,CACA,eAAA,CACA,cAAA,CACA,WAGF,WAAY,uBACV,YAAA,CACA,iBAAA,CACA,eAGF,WAAY,iBACV,MAAA,CACA,YAAA,CACA,sBAAA,CACA,kBAAA,CACA,WAAA,CACA"}
--------------------------------------------------------------------------------
/src/pages/example/components/Counter.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { connect } from 'dva';
4 |
5 | const styles: NodeRequire = require('../index.less');
6 | import CounterButton from './CounterButton';
7 | import { Dispatch } from '../../../../node_modules/redux';
8 |
9 |
10 | export interface ICounterProps {
11 | current: number;
12 | record?: number;
13 | dispatch: Dispatch<{type: string, paylod?: any}>;
14 | };
15 |
16 |
17 | const Counter: React.SFC = (props: ICounterProps): JSX.Element => {
18 | const { current, record, dispatch } = props;
19 |
20 | /* The button click handler */
21 | const handleClick: React.ReactEventHandler = (event: React.MouseEvent): void => {
22 | console.log(event.currentTarget);
23 | dispatch({
24 | type: 'counter/addRemote',
25 | });
26 |
27 | };
28 |
29 | return (
30 |
31 |
The highest count is: {record}
32 |
33 | Current count is: {current}
34 |
35 |
36 |
37 |
38 |
39 | );
40 | }
41 |
42 |
43 | const mapStateToProps = (state) => {
44 | const { current, record } = state.counter; // state.counter;
45 |
46 | return {
47 | current,
48 | record,
49 | };
50 | }
51 |
52 |
53 | export default connect(mapStateToProps)(Counter);
--------------------------------------------------------------------------------
/src/pages/example/components/CounterButton.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Button } from 'antd';
4 |
5 |
6 | export interface ICounterButtonProps {
7 | onBtnClick: (event: React.MouseEvent) => void;
8 | };
9 |
10 |
11 | const CounterButton: React.SFC = (props: ICounterButtonProps): JSX.Element => {
12 |
13 | return (
14 |
15 | );
16 | };
17 |
18 | export default CounterButton;
--------------------------------------------------------------------------------
/src/pages/example/index.less:
--------------------------------------------------------------------------------
1 | .app-container {}
2 |
3 | .app-content {
4 | box-sizing: border-box;
5 | overflow: hidden;
6 | width: 400px;
7 | height: 400px;
8 | margin: 50px auto;
9 | border: 1px solid #ccc;
10 | box-shadow: 0 0 4px 4px #dddddd;
11 | }
12 |
13 | .counterbox {
14 | display: flex;
15 | flex-direction: column;
16 | box-sizing: border-box;
17 | height: 100%;
18 | padding: 10px;
19 | }
20 |
21 |
22 | .counterbox .counter-show {
23 | flex: 1;
24 | line-height: 1.5;
25 | font-size: 20px;
26 | color: #666;
27 | }
28 |
29 | .counterbox .counter-currentcount {
30 | height: 100px;
31 | line-height: 100px;
32 | font-size: 30px;
33 | }
34 |
35 | .counterbox .counter-button {
36 | flex: 3;
37 | display: flex;
38 | justify-content: center;
39 | align-items: center;
40 | height: 100%;
41 | margin-top: 10px;
42 | }
--------------------------------------------------------------------------------
/src/pages/example/models/counter.tsx:
--------------------------------------------------------------------------------
1 | import key from 'keymaster';
2 |
3 | import delay from "../../../utils/delay";
4 |
5 |
6 | export default {
7 |
8 | namespace: 'counter',
9 |
10 | state: {
11 | current: 0, /* current count */
12 | record: 0, /* highest count */
13 | },
14 |
15 | reducers: {
16 | add (state) {
17 | const newCurrent = state.current + 1;
18 | return {
19 | ...state,
20 | record: newCurrent > state.record ? newCurrent : state.record,
21 | current: newCurrent,
22 | };
23 | },
24 | minus (state) {
25 | const newCurrent = state.current - 1;
26 | return {
27 | ...state,
28 | current: newCurrent,
29 | };
30 | },
31 | },
32 |
33 | effects: {
34 | *addRemote ({ payload }, { call, put }) {
35 | console.log(payload);
36 | yield put({ type: 'add' });
37 | yield call(delay, 1000);
38 | yield put({ type: 'minus' });
39 | },
40 | },
41 |
42 | subscriptions: {
43 | keyboardWather ({dispatch}) {
44 | key('ctrl+up', () => { dispatch({ type: 'addRemote' }) });
45 | },
46 | },
47 |
48 | };
49 |
50 |
--------------------------------------------------------------------------------
/src/pages/example/page.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import Counter from './components/Counter';
4 |
5 | const styles: NodeRequire = require('./index.less');
6 |
7 |
8 | const App = () => {
9 | return (
10 |
15 | );
16 | };
17 |
18 |
19 | export default App;
--------------------------------------------------------------------------------
/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | const HomePage = () => {
4 | return (
5 |
6 | );
7 | };
8 |
9 |
10 | export default HomePage;
--------------------------------------------------------------------------------
/src/services/example.js:
--------------------------------------------------------------------------------
1 | import request from '../utils/request';
2 |
3 | export function query() {
4 | return request('/api/users');
5 | }
6 |
--------------------------------------------------------------------------------
/src/utils/delay.tsx:
--------------------------------------------------------------------------------
1 |
2 | export default function delay(time: number): Promise<(resolve?: () => {}, reject?: () => {}) => {}> {
3 | return new Promise((resolve) => {
4 | setTimeout(() => {
5 | resolve();
6 | }, time);
7 | });
8 | }
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | import fetch from 'dva/fetch';
2 |
3 | function parseJSON(response) {
4 | return response.json();
5 | }
6 |
7 | function checkStatus(response) {
8 | if (response.status >= 200 && response.status < 300) {
9 | return response;
10 | }
11 |
12 | const error = new Error(response.statusText);
13 | error.response = response;
14 | throw error;
15 | }
16 |
17 | /**
18 | * Requests a URL, returning a promise.
19 | *
20 | * @param {string} url The URL we want to request
21 | * @param {object} [options] The options we want to pass to "fetch"
22 | * @return {object} An object containing either "data" or "err"
23 | */
24 | export default function request(url, options) {
25 | return fetch(url, options)
26 | .then(checkStatus)
27 | .then(parseJSON)
28 | .then(data => ({ data }))
29 | .catch(err => ({ err }));
30 | }
31 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strictNullChecks": true,
4 | "moduleResolution": "node",
5 | "allowSyntheticDefaultImports": true,
6 | "experimentalDecorators": true,
7 | "jsx": "preserve",
8 | "noUnusedParameters": true,
9 | "noUnusedLocals": true,
10 | "target": "es6",
11 | "lib": [
12 | "dom",
13 | "es7"
14 | ]
15 | },
16 | "include": [
17 | "src/**/*"
18 | ],
19 | "exclude": [
20 | "node_modules",
21 | "lib",
22 | "es"
23 | ],
24 | "module": "ESNext"
25 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "tslint:latest",
4 | "tslint-react"
5 | ],
6 | "linterOptions": {
7 | "exclude": [
8 | "node_modules/**/*.ts",
9 | "src/**/*.{ts,tsx}"
10 | ]
11 | },
12 | "rules": {
13 | "object-literal-sort-keys": false,
14 | "jsx-no-lambda": false,
15 | "eofline": false,
16 | "no-consecutive-blank-lines": false,
17 | "no-var-requires": false,
18 | "quotemark": false,
19 | "space-within-parens": false,
20 | "no-submodule-imports": false,
21 | "no-implicit-dependencies": false,
22 | "ordered-imports": [ false ],
23 | "jsx-boolean-value": false,
24 | "no-trailing-whitespace": false,
25 | "semicolon": false,
26 | "trailing-comma": false,
27 | "space-in-parents": false,
28 | "typedef-whitespace": false,
29 | "whitespace": false,
30 | "interface-over-type-literal": true,
31 | "no-duplicate-imports": false,
32 | "no-namespace": true,
33 | "no-internal-module": true,
34 | "space-before-function-paren": ["ignore"],
35 | "no-console": ["error", { "allow": ["log", "error"] }],
36 | "no-string-literal": false,
37 | "max-line-length": false
38 | }
39 | }
--------------------------------------------------------------------------------