a11y_agent 0.0.11 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/a11y_agent/version.rb +1 -1
- data/lib/agents/a11y_agent.rb +0 -3
- metadata +1 -50
- data/fixtures/react/.eslintrc +0 -13
- data/fixtures/react/.gitignore +0 -3
- data/fixtures/react/package-lock.json +0 -16453
- data/fixtures/react/package.json +0 -42
- data/fixtures/react/public/index.html +0 -19
- data/fixtures/react/readme.md +0 -42
- data/fixtures/react/src/index.js +0 -16
- data/fixtures/react/src/todo/app.css +0 -87
- data/fixtures/react/src/todo/app.jsx +0 -20
- data/fixtures/react/src/todo/components/footer.jsx +0 -46
- data/fixtures/react/src/todo/components/header.jsx +0 -15
- data/fixtures/react/src/todo/components/input.jsx +0 -46
- data/fixtures/react/src/todo/components/item.jsx +0 -55
- data/fixtures/react/src/todo/components/main.jsx +0 -45
- data/fixtures/react/src/todo/constants.js +0 -7
- data/fixtures/react/src/todo/reducer.js +0 -64
- data/fixtures/react/webpack.common.js +0 -43
- data/fixtures/react/webpack.dev.js +0 -18
- data/fixtures/react/webpack.prod.js +0 -35
- data/fixtures/sample.html +0 -93
- data/fixtures/sample.tsx +0 -100
data/fixtures/react/package.json
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"name": "todomvc-react",
|
3
|
-
"version": "1.0.0",
|
4
|
-
"description": "A TodoMVC written in React.",
|
5
|
-
"private": true,
|
6
|
-
"engines": {
|
7
|
-
"node": ">=18.13.0",
|
8
|
-
"npm": ">=8.19.3"
|
9
|
-
},
|
10
|
-
"scripts": {
|
11
|
-
"build": "webpack --config webpack.prod.js",
|
12
|
-
"dev": "webpack serve --open --config webpack.dev.js",
|
13
|
-
"serve": "http-server ./dist -p 7002 -c-1 --cors",
|
14
|
-
"test": "jest"
|
15
|
-
},
|
16
|
-
"devDependencies": {
|
17
|
-
"@babel/core": "^7.21.0",
|
18
|
-
"@babel/preset-env": "^7.20.2",
|
19
|
-
"@babel/preset-react": "^7.18.6",
|
20
|
-
"babel-loader": "^9.1.2",
|
21
|
-
"copy-webpack-plugin": "^12.0.2",
|
22
|
-
"css-loader": "^6.7.3",
|
23
|
-
"css-minimizer-webpack-plugin": "^4.2.2",
|
24
|
-
"eslint-plugin-react": "^7.32.2",
|
25
|
-
"html-webpack-plugin": "^5.5.0",
|
26
|
-
"http-server": "^14.1.1",
|
27
|
-
"mini-css-extract-plugin": "^2.7.2",
|
28
|
-
"style-loader": "^3.3.1",
|
29
|
-
"webpack": "^5.75.0",
|
30
|
-
"webpack-cli": "^5.0.1",
|
31
|
-
"webpack-dev-server": "^4.11.1",
|
32
|
-
"webpack-merge": "^5.8.0"
|
33
|
-
},
|
34
|
-
"dependencies": {
|
35
|
-
"classnames": "^2.2.5",
|
36
|
-
"react": "^17.0.2",
|
37
|
-
"react-dom": "^17.0.2",
|
38
|
-
"react-router-dom": "^6.8.2",
|
39
|
-
"todomvc-app-css": "^2.4.2",
|
40
|
-
"todomvc-common": "^1.0.5"
|
41
|
-
}
|
42
|
-
}
|
@@ -1,19 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html lang="en" data-framework="react">
|
3
|
-
<head>
|
4
|
-
<meta charset="UTF-8" />
|
5
|
-
<meta name="description" content="A TodoMVC written in React." />
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7
|
-
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
8
|
-
<title>TodoMVC: React</title>
|
9
|
-
</head>
|
10
|
-
<body>
|
11
|
-
<section class="todoapp" id="root"></section>
|
12
|
-
<footer class="info">
|
13
|
-
<p>Double-click to edit a todo</p>
|
14
|
-
<p>Created by the TodoMVC Team</p>
|
15
|
-
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
|
16
|
-
</footer>
|
17
|
-
<script src="./base.js"></script>
|
18
|
-
</body>
|
19
|
-
</html>
|
data/fixtures/react/readme.md
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
# TodoMVC: React
|
2
|
-
|
3
|
-
## Description
|
4
|
-
|
5
|
-
This application uses React 17.0.2 to implement a todo application.
|
6
|
-
|
7
|
-
- [React](https://reactjs.org/) is a JavaScript library for creating user interfaces.
|
8
|
-
|
9
|
-
## Implementation details
|
10
|
-
|
11
|
-
React focuses mainly on providing composable user interfaces to enable developers to build an appealing website or web app. React does not force the user to utilize a particular design pattern, but it does provide useful hooks to implement an MVC pattern, if desired.
|
12
|
-
|
13
|
-
React:\
|
14
|
-
Model: Todo reducer (reducer.js)\
|
15
|
-
View: React ui components\
|
16
|
-
Controller: App component + useReducer hook
|
17
|
-
|
18
|
-
MVC:\
|
19
|
-
Model: Maintains the data and behavior of an application\
|
20
|
-
View: Displays the model in the ui\
|
21
|
-
Controller: Serves as an interface between view & model components
|
22
|
-
|
23
|
-
## Build steps
|
24
|
-
|
25
|
-
To build the static files, this application utilizes webpack. It minifies and optimizes output files and copies all necessary files to a `dist` folder.
|
26
|
-
|
27
|
-
## Requirements
|
28
|
-
|
29
|
-
The only requirement is an installation of Node, to be able to install dependencies and run scripts to serve a local server.
|
30
|
-
|
31
|
-
```
|
32
|
-
* Node (min version: 18.13.0)
|
33
|
-
* NPM (min version: 8.19.3)
|
34
|
-
```
|
35
|
-
|
36
|
-
## Local preview
|
37
|
-
|
38
|
-
```
|
39
|
-
terminal:
|
40
|
-
1. npm install
|
41
|
-
2. npm run serve
|
42
|
-
```
|
data/fixtures/react/src/index.js
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
import React from "react";
|
2
|
-
import { render } from "react-dom";
|
3
|
-
import { HashRouter, Route, Routes } from "react-router-dom";
|
4
|
-
|
5
|
-
import { App } from "./todo/app";
|
6
|
-
import "todomvc-app-css/index.css";
|
7
|
-
import "todomvc-common/base.css";
|
8
|
-
|
9
|
-
render(
|
10
|
-
<HashRouter>
|
11
|
-
<Routes>
|
12
|
-
<Route path="*" element={<App />} />
|
13
|
-
</Routes>
|
14
|
-
</HashRouter>,
|
15
|
-
document.getElementById("root")
|
16
|
-
);
|
@@ -1,87 +0,0 @@
|
|
1
|
-
.toggle-all {
|
2
|
-
width: 40px !important;
|
3
|
-
height: 60px !important;
|
4
|
-
right: auto !important;
|
5
|
-
}
|
6
|
-
|
7
|
-
.toggle-all-label {
|
8
|
-
pointer-events: none;
|
9
|
-
}
|
10
|
-
|
11
|
-
.todo-list {
|
12
|
-
margin: 0;
|
13
|
-
padding: 0;
|
14
|
-
}
|
15
|
-
|
16
|
-
.todo-item {
|
17
|
-
border-bottom: 1px solid #ededed;
|
18
|
-
font-size: 24px;
|
19
|
-
position: relative;
|
20
|
-
}
|
21
|
-
|
22
|
-
.header-title {
|
23
|
-
color: #b83f45;
|
24
|
-
font-size: 80px;
|
25
|
-
font-weight: 200;
|
26
|
-
position: absolute;
|
27
|
-
text-align: center;
|
28
|
-
-webkit-text-rendering: optimizeLegibility;
|
29
|
-
-moz-text-rendering: optimizeLegibility;
|
30
|
-
text-rendering: optimizeLegibility;
|
31
|
-
top: -100px;
|
32
|
-
width: 100%;
|
33
|
-
}
|
34
|
-
|
35
|
-
.filter-item {
|
36
|
-
display: inline-block;
|
37
|
-
margin: 0 5px;
|
38
|
-
}
|
39
|
-
|
40
|
-
.filter-link {
|
41
|
-
border: 1px solid transparent;
|
42
|
-
border-radius: 3px;
|
43
|
-
color: inherit;
|
44
|
-
margin: 3px;
|
45
|
-
padding: 3px 7px;
|
46
|
-
cursor: pointer;
|
47
|
-
}
|
48
|
-
|
49
|
-
.filter-link:hover {
|
50
|
-
border-color: #db7676;
|
51
|
-
}
|
52
|
-
|
53
|
-
.filter-link.selected {
|
54
|
-
border-color: #ce4646;
|
55
|
-
}
|
56
|
-
|
57
|
-
.checkbox-wrapper {
|
58
|
-
position: relative;
|
59
|
-
width: 40px;
|
60
|
-
height: 40px;
|
61
|
-
display: inline-block;
|
62
|
-
}
|
63
|
-
|
64
|
-
.checkbox {
|
65
|
-
width: 100%;
|
66
|
-
height: 100%;
|
67
|
-
margin: 10px 0 0 15px;
|
68
|
-
cursor: pointer;
|
69
|
-
background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23949494%22%20stroke-width%3D%223%22/%3E%3C/svg%3E");
|
70
|
-
}
|
71
|
-
|
72
|
-
.checkbox.checked {
|
73
|
-
background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%2359A193%22%20stroke-width%3D%223%22%2F%3E%3Cpath%20fill%3D%22%233EA390%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22%2F%3E%3C%2Fsvg%3E");
|
74
|
-
}
|
75
|
-
|
76
|
-
.todo-text {
|
77
|
-
display: inline-block;
|
78
|
-
padding: 15px 15px 15px 60px;
|
79
|
-
line-height: 1.2;
|
80
|
-
transition: color .4s;
|
81
|
-
color: #484848;
|
82
|
-
}
|
83
|
-
|
84
|
-
.clear-completed.disabled {
|
85
|
-
opacity: 0.5;
|
86
|
-
cursor: not-allowed;
|
87
|
-
}
|
@@ -1,20 +0,0 @@
|
|
1
|
-
import { useReducer } from "react";
|
2
|
-
import { Header } from "./components/header";
|
3
|
-
import { Main } from "./components/main";
|
4
|
-
import { Footer } from "./components/footer";
|
5
|
-
|
6
|
-
import { todoReducer } from "./reducer";
|
7
|
-
|
8
|
-
import "./app.css";
|
9
|
-
|
10
|
-
export function App() {
|
11
|
-
const [todos, dispatch] = useReducer(todoReducer, []);
|
12
|
-
|
13
|
-
return (
|
14
|
-
<>
|
15
|
-
<Header dispatch={dispatch} />
|
16
|
-
<Main todos={todos} dispatch={dispatch} />
|
17
|
-
<Footer todos={todos} dispatch={dispatch} />
|
18
|
-
</>
|
19
|
-
);
|
20
|
-
}
|
@@ -1,46 +0,0 @@
|
|
1
|
-
import { useCallback, useMemo } from "react";
|
2
|
-
import { useLocation } from "react-router-dom";
|
3
|
-
import classnames from "classnames";
|
4
|
-
|
5
|
-
import { REMOVE_COMPLETED_ITEMS } from "../constants";
|
6
|
-
|
7
|
-
export function Footer({ todos, dispatch }) {
|
8
|
-
const { pathname: route } = useLocation();
|
9
|
-
|
10
|
-
const activeTodos = useMemo(() => todos.filter((todo) => !todo.completed), [todos]);
|
11
|
-
|
12
|
-
const removeCompleted = useCallback(() => dispatch({ type: REMOVE_COMPLETED_ITEMS }), [dispatch]);
|
13
|
-
|
14
|
-
// prettier-ignore
|
15
|
-
if (todos.length === 0)
|
16
|
-
return null;
|
17
|
-
|
18
|
-
return (
|
19
|
-
<div className="footer" data-testid="footer">
|
20
|
-
<div className="todo-count">{`${activeTodos.length} ${activeTodos.length === 1 ? "item" : "items"} left!`}</div>
|
21
|
-
<div className="filters" data-testid="footer-navigation">
|
22
|
-
<div className="filter-item">
|
23
|
-
<div className={classnames("filter-link", { selected: route === "/" })} onClick={() => window.location.hash = "/"}>
|
24
|
-
All
|
25
|
-
</div>
|
26
|
-
</div>
|
27
|
-
<div className="filter-item">
|
28
|
-
<div className={classnames("filter-link", { selected: route === "/active" })} onClick={() => window.location.hash = "/active"}>
|
29
|
-
Active
|
30
|
-
</div>
|
31
|
-
</div>
|
32
|
-
<div className="filter-item">
|
33
|
-
<div className={classnames("filter-link", { selected: route === "/completed" })} onClick={() => window.location.hash = "/completed"}>
|
34
|
-
Completed
|
35
|
-
</div>
|
36
|
-
</div>
|
37
|
-
</div>
|
38
|
-
<div
|
39
|
-
className={classnames("clear-completed", { disabled: activeTodos.length === todos.length })}
|
40
|
-
onClick={activeTodos.length === todos.length ? null : removeCompleted}
|
41
|
-
>
|
42
|
-
Clear completed
|
43
|
-
</div>
|
44
|
-
</div>
|
45
|
-
);
|
46
|
-
}
|
@@ -1,15 +0,0 @@
|
|
1
|
-
import { useCallback } from "react";
|
2
|
-
import { Input } from "./input";
|
3
|
-
|
4
|
-
import { ADD_ITEM } from "../constants";
|
5
|
-
|
6
|
-
export function Header({ dispatch }) {
|
7
|
-
const addItem = useCallback((title) => dispatch({ type: ADD_ITEM, payload: { title } }), [dispatch]);
|
8
|
-
|
9
|
-
return (
|
10
|
-
<div className="header" data-testid="header">
|
11
|
-
<div className="header-title">todos</div>
|
12
|
-
<Input onSubmit={addItem} label="New Todo Input" placeholder="What needs to be done?" />
|
13
|
-
</div>
|
14
|
-
);
|
15
|
-
}
|
@@ -1,46 +0,0 @@
|
|
1
|
-
import { useCallback } from "react";
|
2
|
-
|
3
|
-
const sanitize = (string) => {
|
4
|
-
const map = {
|
5
|
-
"&": "&",
|
6
|
-
"<": "<",
|
7
|
-
">": ">",
|
8
|
-
'"': """,
|
9
|
-
"'": "'",
|
10
|
-
"/": "/",
|
11
|
-
};
|
12
|
-
const reg = /[&<>"'/]/gi;
|
13
|
-
return string.replace(reg, (match) => map[match]);
|
14
|
-
};
|
15
|
-
|
16
|
-
const hasValidMin = (value, min) => {
|
17
|
-
return value.length >= min;
|
18
|
-
};
|
19
|
-
|
20
|
-
export function Input({ onSubmit, placeholder, label, defaultValue, onBlur }) {
|
21
|
-
const handleBlur = useCallback(() => {
|
22
|
-
if (onBlur)
|
23
|
-
onBlur();
|
24
|
-
}, [onBlur]);
|
25
|
-
|
26
|
-
const handleKeyDown = useCallback(
|
27
|
-
(e) => {
|
28
|
-
if (e.key === "Enter") {
|
29
|
-
const value = e.target.value.trim();
|
30
|
-
|
31
|
-
if (!hasValidMin(value, 2))
|
32
|
-
return;
|
33
|
-
|
34
|
-
onSubmit(sanitize(value));
|
35
|
-
e.target.value = "";
|
36
|
-
}
|
37
|
-
},
|
38
|
-
[onSubmit]
|
39
|
-
);
|
40
|
-
|
41
|
-
return (
|
42
|
-
<div className="input-container">
|
43
|
-
<input className="new-todo" id="todo-input" type="text" data-testid="text-input" autoFocus placeholder={placeholder} defaultValue={defaultValue} onBlur={handleBlur} onKeyDown={handleKeyDown} />
|
44
|
-
</div>
|
45
|
-
);
|
46
|
-
}
|
@@ -1,55 +0,0 @@
|
|
1
|
-
import { memo, useState, useCallback } from "react";
|
2
|
-
import classnames from "classnames";
|
3
|
-
|
4
|
-
import { Input } from "./input";
|
5
|
-
|
6
|
-
import { TOGGLE_ITEM, REMOVE_ITEM, UPDATE_ITEM } from "../constants";
|
7
|
-
|
8
|
-
export const Item = memo(function Item({ todo, dispatch, index }) {
|
9
|
-
const [isWritable, setIsWritable] = useState(false);
|
10
|
-
const { title, completed, id } = todo;
|
11
|
-
|
12
|
-
const toggleItem = useCallback(() => dispatch({ type: TOGGLE_ITEM, payload: { id } }), [dispatch]);
|
13
|
-
const removeItem = useCallback(() => dispatch({ type: REMOVE_ITEM, payload: { id } }), [dispatch]);
|
14
|
-
const updateItem = useCallback((id, title) => dispatch({ type: UPDATE_ITEM, payload: { id, title } }), [dispatch]);
|
15
|
-
|
16
|
-
const handleDoubleClick = useCallback(() => {
|
17
|
-
setIsWritable(true);
|
18
|
-
}, []);
|
19
|
-
|
20
|
-
const handleBlur = useCallback(() => {
|
21
|
-
setIsWritable(false);
|
22
|
-
}, []);
|
23
|
-
|
24
|
-
const handleUpdate = useCallback(
|
25
|
-
(title) => {
|
26
|
-
if (title.length === 0)
|
27
|
-
removeItem(id);
|
28
|
-
else
|
29
|
-
updateItem(id, title);
|
30
|
-
|
31
|
-
setIsWritable(false);
|
32
|
-
},
|
33
|
-
[id, removeItem, updateItem]
|
34
|
-
);
|
35
|
-
|
36
|
-
return (
|
37
|
-
<div className={classnames("todo-item", { completed: todo.completed })} data-testid="todo-item">
|
38
|
-
<div className="view">
|
39
|
-
{isWritable ? (
|
40
|
-
<Input onSubmit={handleUpdate} label="Edit Todo Input" defaultValue={title} onBlur={handleBlur} />
|
41
|
-
) : (
|
42
|
-
<>
|
43
|
-
<div className="toggle checkbox-wrapper" data-testid="todo-item-toggle">
|
44
|
-
<div className={classnames("checkbox", { checked: completed })} onClick={toggleItem}></div>
|
45
|
-
</div>
|
46
|
-
<div className="todo-text" data-testid="todo-item-label" onDoubleClick={handleDoubleClick}>
|
47
|
-
{title}
|
48
|
-
</div>
|
49
|
-
<div className="destroy" data-testid="todo-item-button" onClick={removeItem} role="button"></div>
|
50
|
-
</>
|
51
|
-
)}
|
52
|
-
</div>
|
53
|
-
</div>
|
54
|
-
);
|
55
|
-
});
|
@@ -1,45 +0,0 @@
|
|
1
|
-
import { useMemo, useCallback } from "react";
|
2
|
-
import { useLocation } from "react-router-dom";
|
3
|
-
|
4
|
-
import { Item } from "./item";
|
5
|
-
import classnames from "classnames";
|
6
|
-
|
7
|
-
import { TOGGLE_ALL } from "../constants";
|
8
|
-
|
9
|
-
export function Main({ todos, dispatch }) {
|
10
|
-
const { pathname: route } = useLocation();
|
11
|
-
|
12
|
-
const visibleTodos = useMemo(
|
13
|
-
() =>
|
14
|
-
todos.filter((todo) => {
|
15
|
-
if (route === "/active")
|
16
|
-
return !todo.completed;
|
17
|
-
|
18
|
-
if (route === "/completed")
|
19
|
-
return todo.completed;
|
20
|
-
|
21
|
-
return todo;
|
22
|
-
}),
|
23
|
-
[todos, route]
|
24
|
-
);
|
25
|
-
|
26
|
-
const toggleAll = useCallback((e) => dispatch({ type: TOGGLE_ALL, payload: { completed: e.target.checked } }), [dispatch]);
|
27
|
-
|
28
|
-
return (
|
29
|
-
<main className="main" data-testid="main">
|
30
|
-
{visibleTodos.length > 0 ? (
|
31
|
-
<div className="toggle-all-container">
|
32
|
-
<input className="toggle-all" type="checkbox" id="toggle-all" data-testid="toggle-all" checked={visibleTodos.every((todo) => todo.completed)} onChange={toggleAll} />
|
33
|
-
<label className="toggle-all-label" htmlFor="toggle-all">
|
34
|
-
Toggle All Input
|
35
|
-
</label>
|
36
|
-
</div>
|
37
|
-
) : null}
|
38
|
-
<div className={classnames("todo-list")} data-testid="todo-list">
|
39
|
-
{visibleTodos.map((todo, index) => (
|
40
|
-
<Item todo={todo} key={todo.id} dispatch={dispatch} index={index} />
|
41
|
-
))}
|
42
|
-
</div>
|
43
|
-
</main>
|
44
|
-
);
|
45
|
-
}
|
@@ -1,7 +0,0 @@
|
|
1
|
-
export const ADD_ITEM = "ADD_ITEM";
|
2
|
-
export const UPDATE_ITEM = "UPDATE_ITEM";
|
3
|
-
export const REMOVE_ITEM = "REMOVE_ITEM";
|
4
|
-
export const TOGGLE_ITEM = "TOGGLE_ITEM";
|
5
|
-
export const REMOVE_ALL_ITEMS = "REMOVE_ALL_ITEMS";
|
6
|
-
export const TOGGLE_ALL = "TOGGLE_ALL";
|
7
|
-
export const REMOVE_COMPLETED_ITEMS = "REMOVE_COMPLETED_ITEMS";
|
@@ -1,64 +0,0 @@
|
|
1
|
-
import { ADD_ITEM, UPDATE_ITEM, REMOVE_ITEM, TOGGLE_ITEM, REMOVE_ALL_ITEMS, TOGGLE_ALL, REMOVE_COMPLETED_ITEMS } from "./constants";
|
2
|
-
|
3
|
-
/* Borrowed from https://github.com/ai/nanoid/blob/3.0.2/non-secure/index.js
|
4
|
-
|
5
|
-
The MIT License (MIT)
|
6
|
-
|
7
|
-
Copyright 2017 Andrey Sitnik <andrey@sitnik.ru>
|
8
|
-
|
9
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
10
|
-
this software and associated documentation files (the "Software"), to deal in
|
11
|
-
the Software without restriction, including without limitation the rights to
|
12
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
13
|
-
the Software, and to permit persons to whom the Software is furnished to do so,
|
14
|
-
subject to the following conditions:
|
15
|
-
|
16
|
-
The above copyright notice and this permission notice shall be included in all
|
17
|
-
copies or substantial portions of the Software.
|
18
|
-
|
19
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
20
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
21
|
-
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
22
|
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
23
|
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
24
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
25
|
-
|
26
|
-
// This alphabet uses `A-Za-z0-9_-` symbols.
|
27
|
-
// The order of characters is optimized for better gzip and brotli compression.
|
28
|
-
// References to the same file (works both for gzip and brotli):
|
29
|
-
// `'use`, `andom`, and `rict'`
|
30
|
-
// References to the brotli default dictionary:
|
31
|
-
// `-26T`, `1983`, `40px`, `75px`, `bush`, `jack`, `mind`, `very`, and `wolf`
|
32
|
-
let urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
33
|
-
|
34
|
-
function nanoid(size = 21) {
|
35
|
-
let id = "";
|
36
|
-
// A compact alternative for `for (var i = 0; i < step; i++)`.
|
37
|
-
let i = size;
|
38
|
-
while (i--) {
|
39
|
-
// `| 0` is more compact and faster than `Math.floor()`.
|
40
|
-
id += urlAlphabet[(Math.random() * 64) | 0];
|
41
|
-
}
|
42
|
-
return id;
|
43
|
-
}
|
44
|
-
|
45
|
-
export const todoReducer = (state, action) => {
|
46
|
-
switch (action.type) {
|
47
|
-
case ADD_ITEM:
|
48
|
-
return state.concat({ id: nanoid(), title: action.payload.title, completed: false });
|
49
|
-
case UPDATE_ITEM:
|
50
|
-
return state.map((todo) => (todo.id === action.payload.id ? { ...todo, title: action.payload.title } : todo));
|
51
|
-
case REMOVE_ITEM:
|
52
|
-
return state.filter((todo) => todo.id !== action.payload.id);
|
53
|
-
case TOGGLE_ITEM:
|
54
|
-
return state.map((todo) => (todo.id === action.payload.id ? { ...todo, completed: !todo.completed } : todo));
|
55
|
-
case REMOVE_ALL_ITEMS:
|
56
|
-
return [];
|
57
|
-
case TOGGLE_ALL:
|
58
|
-
return state.map((todo) => (todo.completed !== action.payload.completed ? { ...todo, completed: action.payload.completed } : todo));
|
59
|
-
case REMOVE_COMPLETED_ITEMS:
|
60
|
-
return state.filter((todo) => !todo.completed);
|
61
|
-
}
|
62
|
-
|
63
|
-
throw Error(`Unknown action: ${action.type}`);
|
64
|
-
};
|
@@ -1,43 +0,0 @@
|
|
1
|
-
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
2
|
-
const path = require("path");
|
3
|
-
|
4
|
-
module.exports = {
|
5
|
-
entry: {
|
6
|
-
app: path.resolve(__dirname, "src", "index.js"),
|
7
|
-
},
|
8
|
-
plugins: [
|
9
|
-
new HtmlWebpackPlugin({
|
10
|
-
title: "TodoMVC: React",
|
11
|
-
template: path.resolve(__dirname, "public", "index.html"),
|
12
|
-
}),
|
13
|
-
],
|
14
|
-
output: {
|
15
|
-
filename: "[name].bundle.js",
|
16
|
-
path: path.resolve(__dirname, "dist"),
|
17
|
-
clean: true,
|
18
|
-
},
|
19
|
-
resolve: {
|
20
|
-
extensions: [".js", ".jsx"],
|
21
|
-
},
|
22
|
-
module: {
|
23
|
-
rules: [
|
24
|
-
{
|
25
|
-
test: /\.(js|jsx)$/,
|
26
|
-
exclude: /node_modules/,
|
27
|
-
use: {
|
28
|
-
loader: "babel-loader",
|
29
|
-
options: {
|
30
|
-
presets: [
|
31
|
-
["@babel/preset-env", { targets: "defaults" }],
|
32
|
-
["@babel/preset-react", { runtime: "automatic" }],
|
33
|
-
],
|
34
|
-
},
|
35
|
-
},
|
36
|
-
},
|
37
|
-
{
|
38
|
-
test: /\.(png|svg|jpg|jpeg|gif)$/i,
|
39
|
-
type: "asset/resource",
|
40
|
-
},
|
41
|
-
],
|
42
|
-
},
|
43
|
-
};
|
@@ -1,18 +0,0 @@
|
|
1
|
-
const { merge } = require("webpack-merge");
|
2
|
-
const common = require("./webpack.common.js");
|
3
|
-
|
4
|
-
module.exports = merge(common, {
|
5
|
-
mode: "development",
|
6
|
-
devtool: "inline-source-map",
|
7
|
-
devServer: {
|
8
|
-
static: "./dist",
|
9
|
-
},
|
10
|
-
module: {
|
11
|
-
rules: [
|
12
|
-
{
|
13
|
-
test: /\.css$/i,
|
14
|
-
use: ["style-loader", "css-loader"],
|
15
|
-
},
|
16
|
-
],
|
17
|
-
},
|
18
|
-
});
|
@@ -1,35 +0,0 @@
|
|
1
|
-
const { merge } = require("webpack-merge");
|
2
|
-
const common = require("./webpack.common.js");
|
3
|
-
|
4
|
-
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
5
|
-
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
|
6
|
-
const TerserPlugin = require("terser-webpack-plugin");
|
7
|
-
const CopyPlugin = require("copy-webpack-plugin");
|
8
|
-
|
9
|
-
module.exports = merge(common, {
|
10
|
-
mode: "production",
|
11
|
-
devtool: "source-map",
|
12
|
-
plugins: [
|
13
|
-
new MiniCssExtractPlugin({
|
14
|
-
filename: "[name].css",
|
15
|
-
chunkFilename: "[id].css",
|
16
|
-
}),
|
17
|
-
new CopyPlugin({
|
18
|
-
patterns: [
|
19
|
-
{ from: "./node_modules/todomvc-common/base.js", to: "base.js" },
|
20
|
-
],
|
21
|
-
}),
|
22
|
-
],
|
23
|
-
module: {
|
24
|
-
rules: [
|
25
|
-
{
|
26
|
-
test: /\.css$/,
|
27
|
-
use: [MiniCssExtractPlugin.loader, "css-loader"],
|
28
|
-
},
|
29
|
-
],
|
30
|
-
},
|
31
|
-
optimization: {
|
32
|
-
minimize: true,
|
33
|
-
minimizer: [new CssMinimizerPlugin(), new TerserPlugin()],
|
34
|
-
},
|
35
|
-
});
|