├── src
├── scss
│ ├── app.scss
│ ├── index.scss
│ └── text-colors.scss
├── helpers
│ └── index.js
├── App.test.js
├── components
│ ├── Maybe
│ │ └── index.js
│ ├── ActiveTasks
│ │ └── index.js
│ ├── Title
│ │ └── index.js
│ ├── Filter
│ │ └── index.js
│ ├── AddTask
│ │ └── index.js
│ ├── Map
│ │ └── index.js
│ ├── Line
│ │ └── index.js
│ ├── List
│ │ └── index.js
│ └── Colors
│ │ └── index.js
├── state
│ ├── index.js
│ └── todoListStore.js
├── index.js
├── App.js
├── logo.svg
├── containers
│ └── TodoList
│ │ └── index.js
└── serviceWorker.js
├── public
├── favicon.ico
├── manifest.json
└── index.html
├── .eslintrc
├── .prettierrc
├── .editorconfig
├── config
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── paths.js
├── env.js
├── webpackDevServer.config.js
├── webpack.config.dev.js
└── webpack.config.prod.js
├── .github
└── ISSUE_TEMPLATE
│ └── --------------.md
├── .gitignore
├── .babelrc
├── README.md
├── scripts
├── test.js
├── start.js
└── build.js
└── package.json
/src/scss/app.scss:
--------------------------------------------------------------------------------
1 | @import "./text-colors.scss";
2 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iran-react-community/todo-list/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "settings": {
4 | "react": {
5 | "version": "latest"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/helpers/index.js:
--------------------------------------------------------------------------------
1 | export const generateString = (length = -1) => {
2 | return Math.random()
3 | .toString(16)
4 | .slice(2, length);
5 | };
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 160,
3 | "tabWidth": 4,
4 | "useTabs": true,
5 | "semi": true,
6 | "trailingComma": "all",
7 | "overrides": [
8 | {
9 | "files": ".prettierrc",
10 | "options": { "parser": "json" }
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import App from "./App";
4 |
5 | it("renders without crashing", () => {
6 | const div = document.createElement("div");
7 | ReactDOM.render( , div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/components/Maybe/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 |
3 | const Maybe = ({ children, condition }) => (condition ? children : null);
4 |
5 | Maybe.propTypes = {
6 | children: PropTypes.node.isRequired,
7 | condition: PropTypes.bool.isRequired,
8 | };
9 |
10 | export default Maybe;
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = tab
6 | indent_size = 4
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 | [*.json]
16 | indent_style = space
17 | indent_size = 2
18 |
--------------------------------------------------------------------------------
/src/components/ActiveTasks/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 |
4 | const ActiveBox = styled.div`
5 | float: right;
6 | margin-top: 5px;
7 | `;
8 |
9 | const ActiveTasks = ({ value }) => {
10 | return {value}/Active Tasks ;
11 | };
12 |
13 | export default ActiveTasks;
14 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | // This is a custom Jest transformer turning style imports into empty objects.
2 | // http://facebook.github.io/jest/docs/en/webpack.html
3 |
4 | module.exports = {
5 | process() {
6 | return "module.exports = {};";
7 | },
8 | getCacheKey() {
9 | // The output is always the same.
10 | return "cssTransform";
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/--------------.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: ثبت موضوع جدید
3 | about: چینش درست فارسی
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
12 | برای چینش درست فارسی داخل این تگ باز شده بنویسید، متاسفانه داخل مارکآپ، کدهای مارکدان کار نمیکند. برای استفاده از کدهای مارکدان بیرون تگ باز شده بنویسید.
13 |
14 |
15 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # IDE & Code editors
4 | .idea/
5 |
6 | # dependencies
7 | /node_modules
8 | /.pnp
9 | .pnp.js
10 |
11 | /ignore
12 |
13 | # testing
14 | /coverage
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | .env.local
22 | .env.development.local
23 | .env.test.local
24 | .env.production.local
25 |
26 | npm-debug.log*
27 | yarn-debug.log*
28 | yarn-error.log*
29 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "react-app",
4 | [
5 | "@emotion/babel-preset-css-prop",
6 | {
7 | "autoLabel": true,
8 | "labelFormat": "[local]"
9 | }
10 | ]
11 | ],
12 | "plugins": [
13 | [
14 | "@babel/plugin-proposal-decorators",
15 | {
16 | "legacy": true
17 | }
18 | ],
19 | [
20 | "@babel/plugin-proposal-class-properties",
21 | {
22 | "loose": true
23 | }
24 | ],
25 | [
26 | "import",
27 | {
28 | "libraryName": "antd",
29 | "libraryDirectory": "es",
30 | "style": true
31 | }
32 | ]
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/Title/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 |
4 | import Line from "../Line";
5 | import { GradTealBlue } from "../Colors";
6 |
7 | const Wrap = styled.div`
8 | text-align: center;
9 | font-size: 1.3em;
10 | width: 65%;
11 | margin: 10px auto;
12 | color: #373352;
13 | font-weight: 300;
14 | margin-bottom: 30px;
15 | `;
16 |
17 | const Title = () => {
18 | return (
19 |
20 | To-do list application
21 |
22 |
23 | );
24 | };
25 |
26 | export default Title;
27 |
--------------------------------------------------------------------------------
/src/components/Filter/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 | import { Radio } from "antd";
4 |
5 | const FilterBox = styled.div`
6 | margin-bottom: 40px;
7 | display: inline-block;
8 | `;
9 |
10 | const FilterTodo = ({ onChange }) => {
11 | return (
12 |
13 | onChange(e.target.value)}>
14 | All
15 | Completed
16 | Active
17 |
18 |
19 | );
20 | };
21 |
22 | export default FilterTodo;
23 |
--------------------------------------------------------------------------------
/src/state/index.js:
--------------------------------------------------------------------------------
1 | import { observable } from "mobx";
2 | import { create } from "mobx-persist";
3 |
4 | import todoListStore from "./todoListStore";
5 |
6 | // Compact and organization other stores and main store in one place.
7 | export default class AppStore {
8 | @observable serverSide = false;
9 |
10 | constructor() {
11 | this.todoListStore = new todoListStore(this);
12 |
13 | // Mobx Persist data [into localStorage by default]
14 | const hydratedTodoList = create();
15 | hydratedTodoList("todoList", this.todoListStore);
16 | }
17 |
18 | stores() {
19 | const appStore = this;
20 | return {
21 | appStore,
22 | todoList: this.todoListStore,
23 | };
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | import App from "./App";
5 | // Store and mobx
6 | import { Provider } from "mobx-react";
7 | import AppStore from "./state";
8 |
9 | import * as serviceWorker from "./serviceWorker";
10 |
11 | import "./scss/index.scss";
12 | // create an instance of main app store
13 | const appStore = new AppStore();
14 |
15 | ReactDOM.render(
16 |
17 |
18 | ,
19 | document.getElementById("root"),
20 | );
21 |
22 | // If you want your app to work offline and load faster, you can change
23 | // unregister() to register() below. Note this comes with some pitfalls.
24 | // Learn more about service workers: http://bit.ly/CRA-PWA
25 | serviceWorker.unregister();
26 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | // This is a custom Jest transformer turning file imports into filenames.
4 | // http://facebook.github.io/jest/docs/en/webpack.html
5 |
6 | module.exports = {
7 | process(src, filename) {
8 | const assetFilename = JSON.stringify(path.basename(filename));
9 |
10 | if (filename.match(/\.svg$/)) {
11 | return `module.exports = {
12 | __esModule: true,
13 | default: ${assetFilename},
14 | ReactComponent: (props) => ({
15 | $$typeof: Symbol.for('react.element'),
16 | type: 'svg',
17 | ref: null,
18 | key: null,
19 | props: Object.assign({}, props, {
20 | children: ${assetFilename}
21 | })
22 | }),
23 | };`;
24 | }
25 |
26 | return `module.exports = ${assetFilename};`;
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/AddTask/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 | import { Input } from "antd";
4 |
5 | const Search = Input.Search;
6 |
7 | const SearchBox = styled.div`
8 | margin-bottom: 10px;
9 | `;
10 |
11 | class AddTask extends React.PureComponent {
12 | constructor(props) {
13 | super(props);
14 |
15 | this.onSearch = this.onSearch.bind(this);
16 | }
17 |
18 | onSearch(value) {
19 | if(!value) return;
20 |
21 | this.props.onChange(value);
22 |
23 | this.refs.todoInput.input.input.value = "";
24 | }
25 |
26 | componentDidMount() {
27 | this.refs.todoInput.focus();
28 | }
29 |
30 | render() {
31 | return (
32 |
33 |
34 |
35 | );
36 | }
37 | }
38 |
39 | export default AddTask;
40 |
--------------------------------------------------------------------------------
/src/components/Map/index.js:
--------------------------------------------------------------------------------
1 | import React, { cloneElement } from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import Maybe from "../Maybe";
5 | import { generateString } from "../../helpers";
6 | /**
7 | * @description The component automatically transmits map callback data to all its sub - component children as prop.
8 | * @template
9 | *
10 | * // Now DisplayItems has 2 prop by default which the value and index prop are adapted from map callback.
11 | *
12 | */
13 | const Map = ({ children, data, name = generateString() }) => {
14 | const childComponents = data.map((value, index) => cloneElement(children, { value, index, key: `$${name}_${index}` }));
15 |
16 | return {childComponents} ;
17 | };
18 |
19 | Map.propTypes = {
20 | children: PropTypes.node.isRequired,
21 | data: PropTypes.array.isRequired,
22 | };
23 |
24 | export default Map;
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | A simple and small todo list application built with:
2 |
3 | - React 16.8
4 | - Ant Design _for User interface_
5 | - MobX _for state management_
6 | - Webpack 4 _for module bundling_
7 | - EmotionJS _for using css-in-js_
8 |
9 | Demo: https://iran-react-community.github.io/todo-list/
10 |
11 | Regards to [TodoMVC](http://todomvc.com)
12 |
13 | ## Available Scripts
14 |
15 | In the project directory, you can run:
16 |
17 | ### `npm start`
18 |
19 | Runs the app in the development mode.
20 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import styled from "@emotion/styled";
3 | import { Layout } from "antd";
4 |
5 | import TodoList from "./containers/TodoList";
6 |
7 | import "./scss/app.scss";
8 |
9 | const { Content, Footer } = Layout;
10 | const Wrapper = styled.div`
11 | width: 560px;
12 | /* min-height: 300px; */
13 | margin: 50px auto;
14 | background-color: #fff;
15 | border-radius: 25px;
16 | padding: 20px;
17 | box-shadow: rgba(50, 50, 50, 0.1) 0px 10px 35px;
18 | `;
19 |
20 | class BootCamp extends Component {
21 | render() {
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 | }
36 |
37 | export default BootCamp;
38 |
--------------------------------------------------------------------------------
/src/scss/index.scss:
--------------------------------------------------------------------------------
1 | :root {
2 | /* Silver family */
3 | --silver: "#f7f7f7";
4 | --silver-light: "#838ba1";
5 | --silver-lighter: "#eaebee";
6 | --silver-dark: "#c4c8d1";
7 | /* Gray family */
8 | --gray: "#38404d";
9 | --gray-light: "#f8f9fe";
10 | --gray-lighter: "#ccd8ed";
11 | /* Blue family */
12 | --blue: "#3c74ff";
13 | --blue-light: "#1a2d5e";
14 | --blue-lighter: "#c9d8ff";
15 | --blue-lighter-1: "#eaf2ff";
16 | --blue-dark: "#2459f1";
17 | --blue-darker: "#3e6eff";
18 | /* Yellow family */
19 | --yellow: "#ffcb00";
20 | --yellow-light: "#fff3bf";
21 | /* Green family */
22 | --green: "#00ca88";
23 | --gray-darker: "#50586e";
24 | --font-family: "Roboto Mono", monospace;
25 | }
26 |
27 | @import url("https://fonts.googleapis.com/css?family=Roboto+Mono");
28 | @import "~antd/dist/antd.css";
29 |
30 | body {
31 | margin: 0;
32 | padding: 0;
33 | font-size: 16px;
34 | -webkit-font-smoothing: antialiased;
35 | -moz-osx-font-smoothing: grayscale;
36 | background: #f0f2f5 !important;
37 | color: #38404d !important;
38 | cursor: default;
39 | font-family: var(--font-family) !important;
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/Line/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import propTypes from "prop-types";
3 |
4 | import styled from "@emotion/styled";
5 | import { keyframes } from "@emotion/core";
6 |
7 | const LineBox = styled.div`
8 | height: 2px;
9 | width: 100%;
10 | overflow: hidden;
11 | width: 100%;
12 | margin: 24px auto 40px;
13 | `;
14 |
15 | const lineAnimate = keyframes`
16 | 0%,
17 | 25% {
18 | transform: translateX(-64px);
19 | }
20 | 75%,
21 | 100% {
22 | transform: translateX(372px);
23 | }
24 | `;
25 |
26 | const LineBar = styled.div`
27 | height: 100%;
28 | width: 64px;
29 | transform: translateX(-64px);
30 | animation: ${lineAnimate} 3s ease-in-out 1.5s infinite;
31 | background: ${({ from, to }) => `linear-gradient(to right, ${from} 0%, ${to} 100%)`};
32 | `;
33 |
34 | const Line = ({ from, to }) => (
35 |
36 |
37 |
38 | );
39 |
40 | Line.propTypes = {
41 | from: propTypes.string,
42 | to: propTypes.string,
43 | };
44 | Line.defaultProps = {
45 | from: "rgba(24, 144, 255, 0)",
46 | to: "#1890ff",
47 | };
48 |
49 | export default React.memo(Line);
50 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 |
19 | const jest = require('jest');
20 | const execSync = require('child_process').execSync;
21 | let argv = process.argv.slice(2);
22 |
23 | function isInGitRepository() {
24 | try {
25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26 | return true;
27 | } catch (e) {
28 | return false;
29 | }
30 | }
31 |
32 | function isInMercurialRepository() {
33 | try {
34 | execSync('hg --cwd . root', { stdio: 'ignore' });
35 | return true;
36 | } catch (e) {
37 | return false;
38 | }
39 | }
40 |
41 | // Watch unless on CI, in coverage mode, or explicitly running all tests
42 | if (
43 | !process.env.CI &&
44 | argv.indexOf('--coverage') === -1 &&
45 | argv.indexOf('--watchAll') === -1
46 | ) {
47 | // https://github.com/facebook/create-react-app/issues/5210
48 | const hasSourceControl = isInGitRepository() || isInMercurialRepository();
49 | argv.push(hasSourceControl ? '--watch' : '--watchAll');
50 | }
51 |
52 |
53 | jest.run(argv);
54 |
--------------------------------------------------------------------------------
/src/components/List/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 |
4 | // ant components
5 | import List from "antd/es/list";
6 | import Button from "antd/es/button";
7 | import { Tooltip, Checkbox } from "antd";
8 |
9 | const TodoBox = styled.div`
10 | background: ${({ done }) => (done ? "#92C65A" : "#f5f5f5")};
11 | padding: 15px;
12 | border-radius: 5px;
13 | box-shadow: 0px 5px 15px rgba(50, 50, 50, 0.1);
14 | border: 1px solid ${({ done }) => (done ? "#92c65a" : "#cfcfcf")};
15 |
16 | & .ant-checkbox-checked {
17 | &:before {
18 | border-color: #53861d;
19 | }
20 |
21 | .ant-checkbox-inner {
22 | background-color: #53861d;
23 | border-color: #53861d;
24 | }
25 | }
26 |
27 | & .ant-checkbox-wrapper {
28 | user-select: none;
29 |
30 | & > span:last-of-type {
31 | margin-left: 5px;
32 | text-decoration: ${({ done }) => (done ? "line-through" : "none")};
33 | color: ${({ done }) => (done ? "#FFF" : "rgba(0, 0, 0, 0.65)")};
34 | } /* Checkbox label */
35 | } /* checkbox wrapper */
36 | `;
37 |
38 | const TodoList = ({ value: { value, done }, index, onChange, onDelete }) => {
39 | return (
40 |
41 |
42 | onChange({ index, done: e.target.checked })}>
43 | {value}
44 |
45 |
46 | onDelete(index)} />
47 |
48 |
49 |
50 | );
51 | };
52 |
53 | export default TodoList;
54 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | Todo List App
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
--------------------------------------------------------------------------------
/src/state/todoListStore.js:
--------------------------------------------------------------------------------
1 | import { observable, computed, action, toJS } from "mobx";
2 | import { persist } from "mobx-persist";
3 |
4 | class TodoListStore {
5 | @persist("list") @observable todos = [
6 | { value: "Get groceries for dinner", done: true },
7 | { value: "Pick up Amit's Birthday present", done: false },
8 | { value: "Book flights to Vancouver", done: false },
9 | { value: "Finalize presentation", done: false },
10 | ];
11 | @persist("list") @observable filteredTodo = [];
12 |
13 | constructor() {
14 | this.filteredTodo = this.todos;
15 | }
16 |
17 | // Get the latest list of todos
18 | @computed
19 | get getTaskList() {
20 | return toJS(this.filteredTodo);
21 | }
22 | // get length of active todos
23 | @computed
24 | get getNumberOfActiveTasks() {
25 | return toJS(this.filteredTodo.filter(({ done }) => !done)).length;
26 | }
27 | // get length of completed todos
28 | @computed
29 | get getNumberOfCompletedTasks() {
30 | return toJS(this.filteredTodo.filter(({ done }) => !!done)).length;
31 | }
32 | // Update a task which it was done status by index
33 | @action updateTaskStatus = ({ index, done }) => {
34 | this.todos[index].done = done;
35 |
36 | this.filteredTodo = this.todos;
37 | };
38 | // Delete a task by index
39 | @action deleteTask = index => {
40 | this.todos = this.todos.filter((task, taskIndex) => taskIndex !== index);
41 |
42 | this.filteredTodo = this.todos;
43 | };
44 | // Delete all completed tasks
45 | @action deleteCompletedTasks = () => {
46 | this.todos = this.todos.filter(({ done }) => !done);
47 |
48 | this.filteredTodo = this.todos;
49 | };
50 | // Add a new task into todo list
51 | @action addNewTask = value => {
52 | this.todos.unshift({ value, done: false });
53 |
54 | this.filteredTodo = this.todos;
55 | };
56 |
57 | // Filter todo list via by status argv.
58 | @action filterTasksBy = status => {
59 | this.filteredTodo = this.todos.filter(({ done }) => {
60 | switch (status) {
61 | case "completed":
62 | return !!done;
63 | case "active":
64 | return !done;
65 | default:
66 | return true;
67 | }
68 | });
69 | };
70 | }
71 |
72 | export default TodoListStore;
73 |
--------------------------------------------------------------------------------
/src/scss/text-colors.scss:
--------------------------------------------------------------------------------
1 | .grad-blue-purple {
2 | display: inline-block;
3 |
4 | background: -webkit-linear-gradient(left, #3f9dec, #a637ca);
5 | background: linear-gradient(to right, #3f9dec, #a637ca);
6 | -webkit-background-clip: text;
7 |
8 | -webkit-text-fill-color: transparent;
9 | }
10 |
11 | .grad-purple-pink {
12 | display: inline-block;
13 |
14 | background: -webkit-linear-gradient(left, #6758c5, #e072c3);
15 | background: linear-gradient(to right, #6758c5, #e072c3);
16 | -webkit-background-clip: text;
17 |
18 | -webkit-text-fill-color: transparent;
19 | }
20 |
21 | .grad-pink-purple {
22 | display: inline-block;
23 |
24 | background: -webkit-linear-gradient(right, #6758c5, #e072c3);
25 | background: linear-gradient(to left, #6758c5, #e072c3);
26 | -webkit-background-clip: text;
27 |
28 | -webkit-text-fill-color: transparent;
29 | }
30 |
31 | .grad-peach-purple {
32 | display: inline-block;
33 |
34 | background: -webkit-linear-gradient(left, #f98ea6, #9966d7);
35 | background: linear-gradient(to right, #f98ea6, #9966d7);
36 | -webkit-background-clip: text;
37 |
38 | -webkit-text-fill-color: transparent;
39 | }
40 |
41 | .grad-blue-indigo {
42 | display: inline-block;
43 |
44 | background: -webkit-linear-gradient(left, #5fb4e0, #4e4ecf);
45 | background: linear-gradient(to right, #5fb4e0, #4e4ecf);
46 | -webkit-background-clip: text;
47 |
48 | -webkit-text-fill-color: transparent;
49 | }
50 |
51 | .grad-indigo {
52 | display: inline-block;
53 |
54 | background: -webkit-linear-gradient(left, #7b7eda, #6c71c0);
55 | background: -webkit-linear-gradient(left, #6c71c0, #7b7eda);
56 | background: linear-gradient(to right, #6c71c0, #7b7eda);
57 | -webkit-background-clip: text;
58 |
59 | -webkit-text-fill-color: transparent;
60 | }
61 |
62 | .grad-teal-blue {
63 | display: inline-block;
64 |
65 | background: -webkit-linear-gradient(left, #00b5c0, #0091d9);
66 | background: linear-gradient(to right, #00b5c0, #0091d9);
67 | -webkit-background-clip: text;
68 |
69 | -webkit-text-fill-color: transparent;
70 | }
71 |
72 | .grad-orange-pink {
73 | display: inline-block;
74 |
75 | background: -webkit-linear-gradient(left, #fe9ba0, #e072c3);
76 | background: linear-gradient(to right, #fe9ba0, #e072c3);
77 | -webkit-background-clip: text;
78 |
79 | -webkit-text-fill-color: transparent;
80 | }
81 |
--------------------------------------------------------------------------------
/src/components/Colors/index.js:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 |
3 | export const GradBluePurple = styled.p`
4 | display: inline-block;
5 |
6 | background: -webkit-linear-gradient(left, #3f9dec, #a637ca);
7 | background: linear-gradient(to right, #3f9dec, #a637ca);
8 |
9 | -webkit-background-clip: text;
10 | -webkit-text-fill-color: transparent;
11 | `;
12 |
13 | export const GradPurplePink = styled.p`
14 | display: inline-block;
15 |
16 | background: -webkit-linear-gradient(left, #6758c5, #e072c3);
17 | background: linear-gradient(to right, #6758c5, #e072c3);
18 |
19 | -webkit-background-clip: text;
20 | -webkit-text-fill-color: transparent;
21 | `;
22 |
23 | export const GradPinkPurple = styled.p`
24 | display: inline-block;
25 |
26 | background: -webkit-linear-gradient(right, #6758c5, #e072c3);
27 | background: linear-gradient(to left, #6758c5, #e072c3);
28 |
29 | -webkit-background-clip: text;
30 | -webkit-text-fill-color: transparent;
31 | `;
32 |
33 | export const GradPeachPurple = styled.p`
34 | display: inline-block;
35 |
36 | background: -webkit-linear-gradient(left, #f98ea6, #9966d7);
37 | background: linear-gradient(to right, #f98ea6, #9966d7);
38 |
39 | -webkit-background-clip: text;
40 | -webkit-text-fill-color: transparent;
41 | `;
42 |
43 | export const GradBlueIndigo = styled.p`
44 | display: inline-block;
45 |
46 | background: -webkit-linear-gradient(left, #5fb4e0, #4e4ecf);
47 | background: linear-gradient(to right, #5fb4e0, #4e4ecf);
48 |
49 | -webkit-background-clip: text;
50 | -webkit-text-fill-color: transparent;
51 | `;
52 |
53 | export const GradIndigo = styled.p`
54 | display: inline-block;
55 |
56 | background: -webkit-linear-gradient(left, #7b7eda, #6c71c0);
57 | background: -webkit-linear-gradient(left, #6c71c0, #7b7eda);
58 | background: linear-gradient(to right, #6c71c0, #7b7eda);
59 |
60 | -webkit-background-clip: text;
61 | -webkit-text-fill-color: transparent;
62 | `;
63 |
64 | export const GradTealBlue = styled.p`
65 | display: inline-block;
66 |
67 | background: -webkit-linear-gradient(left, #00b5c0, #0091d9);
68 | background: linear-gradient(to right, #00b5c0, #0091d9);
69 |
70 | -webkit-background-clip: text;
71 | -webkit-text-fill-color: transparent;
72 | `;
73 |
74 | export const GradOrangePink = styled.p`
75 | display: inline-block;
76 |
77 | background: -webkit-linear-gradient(left, #fe9ba0, #e072c3);
78 | background: linear-gradient(to right, #fe9ba0, #e072c3);
79 |
80 | -webkit-background-clip: text;
81 | -webkit-text-fill-color: transparent;
82 | `;
83 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/containers/TodoList/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component, Fragment } from "react";
2 | import { Empty, Button } from "antd";
3 | // store
4 | import { observer, inject } from "mobx-react";
5 |
6 | import Maybe from "../../components/Maybe";
7 | import Map from "../../components/Map";
8 |
9 | import Title from "../../components/Title";
10 | import List from "../../components/List";
11 | import AddTask from "../../components/AddTask";
12 | import FilterTodo from "../../components/Filter";
13 | import ActiveTasks from "../../components/ActiveTasks";
14 |
15 | @inject("todoList")
16 | @observer
17 | class TaskList extends Component {
18 | constructor(props) {
19 | super(props);
20 |
21 | // binding prototypes
22 | this.addNewTask = this.addNewTask.bind(this);
23 | this.onChangeTask = this.onChangeTask.bind(this);
24 | this.onDeleteTask = this.onDeleteTask.bind(this);
25 | this.onChangeFilter = this.onChangeFilter.bind(this);
26 | this.deleteCompletedTasks = this.deleteCompletedTasks.bind(this);
27 | }
28 |
29 | /**
30 | * Update a task by index and replace the new done value with old value.
31 | * @param {Object} {index, done}
32 | */
33 | onChangeTask({ index, done }) {
34 | this.props.todoList.updateTaskStatus({ index, done });
35 | }
36 |
37 | /**
38 | * Delete a task by index
39 | * @param {Number} index
40 | */
41 | onDeleteTask(index) {
42 | this.props.todoList.deleteTask(index);
43 | }
44 | /**
45 | * Delete all completed tasks
46 | */
47 | deleteCompletedTasks() {
48 | this.props.todoList.deleteCompletedTasks();
49 | }
50 | /**
51 | * Change tasks filter to "uncompleted", "active" or "all"
52 | * @param {String} filter
53 | */
54 | onChangeFilter(filter) {
55 | this.props.todoList.filterTasksBy(filter);
56 | }
57 | /**
58 | * Add a new task from search input box value
59 | * @param {String} value
60 | */
61 | addNewTask(value) {
62 | this.props.todoList.addNewTask(value);
63 | }
64 |
65 | render() {
66 | const { todoList } = this.props;
67 |
68 | return (
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | Delete all completed tasks
85 |
86 |
87 | );
88 | }
89 | }
90 |
91 | export default TaskList;
92 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const fs = require("fs");
3 | const url = require("url");
4 |
5 | // Make sure any symlinks in the project folder are resolved:
6 | // https://github.com/facebook/create-react-app/issues/637
7 | const appDirectory = fs.realpathSync(process.cwd());
8 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
9 |
10 | const envPublicUrl = process.env.PUBLIC_URL;
11 |
12 | function ensureSlash(inputPath, needsSlash) {
13 | const hasSlash = inputPath.endsWith("/");
14 | if (hasSlash && !needsSlash) {
15 | return inputPath.substr(0, inputPath.length - 1);
16 | } else if (!hasSlash && needsSlash) {
17 | return `${inputPath}/`;
18 | } else {
19 | return inputPath;
20 | }
21 | }
22 |
23 | const getPublicUrl = appPackageJson => envPublicUrl || require(appPackageJson).homepage;
24 |
25 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
26 | // "public path" at which the app is served.
27 | // Webpack needs to know it to put the right