├── .gitignore ├── README.md ├── javascript ├── .gitignore ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── App.css │ ├── App.jsx │ ├── App.test.jsx │ ├── app │ │ └── store.js │ ├── features │ │ └── counter │ │ │ ├── Counter.jsx │ │ │ ├── Counter.module.css │ │ │ ├── counterAPI.js │ │ │ ├── counterSlice.js │ │ │ └── counterSlice.spec.js │ ├── index.css │ ├── logo.svg │ └── main.jsx └── vite.config.js └── typescript ├── .gitignore ├── index.html ├── package-lock.json ├── package.json ├── src ├── App.css ├── App.tsx ├── app │ ├── hooks.ts │ └── store.ts ├── features │ └── counter │ │ ├── Counter.module.css │ │ ├── Counter.tsx │ │ ├── counterAPI.ts │ │ └── counterSlice.ts ├── index.css ├── logo.svg ├── main.tsx └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vite-react-template-redux 2 | 3 | The (un)official Redux (JS+TS) template for [Vite](https://vitejs.dev) React. 4 | 5 | ## Usage 6 | 7 | To use a template within your project, use [degit](https://github.com/Rich-Harris/degit) to scaffold your project. 8 | 9 | ### JavaScript 10 | 11 | Use `nvh95/vite-react-template-redux/javascript` to create a project with JavaScript. 12 | 13 | ```sh 14 | npx degit nvh95/vite-react-template-redux/javascript my-app 15 | 16 | # or 17 | 18 | npx degit nvh95/vite-react-template-redux/javascript my-app --yarn 19 | ``` 20 | 21 | ### TypeScript 22 | 23 | Use `nvh95/vite-react-template-redux/typescript` to create a project with TypeScript. 24 | 25 | ```sh 26 | npx degit nvh95/vite-react-template-redux/typescript my-app 27 | 28 | # or 29 | 30 | npx degit nvh95/vite-react-template-redux/typescript my-app --yarn 31 | ``` 32 | 33 | For more information, please refer to: 34 | 35 | - [Getting Started](https://vitejs.dev/guide/#scaffolding-your-first-vite-project) – How to create a new app with Vite. 36 | - [User Guide](https://vitejs.dev) – How to develop apps bootstrapped with Vite. 37 | -------------------------------------------------------------------------------- /javascript/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /javascript/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-react-template-redux", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@ampproject/remapping": { 8 | "version": "2.1.2", 9 | "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", 10 | "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", 11 | "dev": true, 12 | "requires": { 13 | "@jridgewell/trace-mapping": "^0.3.0" 14 | } 15 | }, 16 | "@babel/code-frame": { 17 | "version": "7.16.7", 18 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", 19 | "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", 20 | "dev": true, 21 | "requires": { 22 | "@babel/highlight": "^7.16.7" 23 | } 24 | }, 25 | "@babel/compat-data": { 26 | "version": "7.17.7", 27 | "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", 28 | "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", 29 | "dev": true 30 | }, 31 | "@babel/core": { 32 | "version": "7.17.9", 33 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", 34 | "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==", 35 | "dev": true, 36 | "requires": { 37 | "@ampproject/remapping": "^2.1.0", 38 | "@babel/code-frame": "^7.16.7", 39 | "@babel/generator": "^7.17.9", 40 | "@babel/helper-compilation-targets": "^7.17.7", 41 | "@babel/helper-module-transforms": "^7.17.7", 42 | "@babel/helpers": "^7.17.9", 43 | "@babel/parser": "^7.17.9", 44 | "@babel/template": "^7.16.7", 45 | "@babel/traverse": "^7.17.9", 46 | "@babel/types": "^7.17.0", 47 | "convert-source-map": "^1.7.0", 48 | "debug": "^4.1.0", 49 | "gensync": "^1.0.0-beta.2", 50 | "json5": "^2.2.1", 51 | "semver": "^6.3.0" 52 | } 53 | }, 54 | "@babel/generator": { 55 | "version": "7.17.9", 56 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", 57 | "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", 58 | "dev": true, 59 | "requires": { 60 | "@babel/types": "^7.17.0", 61 | "jsesc": "^2.5.1", 62 | "source-map": "^0.5.0" 63 | } 64 | }, 65 | "@babel/helper-annotate-as-pure": { 66 | "version": "7.16.7", 67 | "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", 68 | "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", 69 | "dev": true, 70 | "requires": { 71 | "@babel/types": "^7.16.7" 72 | } 73 | }, 74 | "@babel/helper-compilation-targets": { 75 | "version": "7.17.7", 76 | "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", 77 | "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", 78 | "dev": true, 79 | "requires": { 80 | "@babel/compat-data": "^7.17.7", 81 | "@babel/helper-validator-option": "^7.16.7", 82 | "browserslist": "^4.17.5", 83 | "semver": "^6.3.0" 84 | } 85 | }, 86 | "@babel/helper-environment-visitor": { 87 | "version": "7.16.7", 88 | "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", 89 | "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", 90 | "dev": true, 91 | "requires": { 92 | "@babel/types": "^7.16.7" 93 | } 94 | }, 95 | "@babel/helper-function-name": { 96 | "version": "7.17.9", 97 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", 98 | "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", 99 | "dev": true, 100 | "requires": { 101 | "@babel/template": "^7.16.7", 102 | "@babel/types": "^7.17.0" 103 | } 104 | }, 105 | "@babel/helper-hoist-variables": { 106 | "version": "7.16.7", 107 | "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", 108 | "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", 109 | "dev": true, 110 | "requires": { 111 | "@babel/types": "^7.16.7" 112 | } 113 | }, 114 | "@babel/helper-module-imports": { 115 | "version": "7.16.7", 116 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", 117 | "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", 118 | "dev": true, 119 | "requires": { 120 | "@babel/types": "^7.16.7" 121 | } 122 | }, 123 | "@babel/helper-module-transforms": { 124 | "version": "7.17.7", 125 | "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", 126 | "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", 127 | "dev": true, 128 | "requires": { 129 | "@babel/helper-environment-visitor": "^7.16.7", 130 | "@babel/helper-module-imports": "^7.16.7", 131 | "@babel/helper-simple-access": "^7.17.7", 132 | "@babel/helper-split-export-declaration": "^7.16.7", 133 | "@babel/helper-validator-identifier": "^7.16.7", 134 | "@babel/template": "^7.16.7", 135 | "@babel/traverse": "^7.17.3", 136 | "@babel/types": "^7.17.0" 137 | } 138 | }, 139 | "@babel/helper-plugin-utils": { 140 | "version": "7.16.7", 141 | "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", 142 | "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", 143 | "dev": true 144 | }, 145 | "@babel/helper-simple-access": { 146 | "version": "7.17.7", 147 | "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", 148 | "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", 149 | "dev": true, 150 | "requires": { 151 | "@babel/types": "^7.17.0" 152 | } 153 | }, 154 | "@babel/helper-split-export-declaration": { 155 | "version": "7.16.7", 156 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", 157 | "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", 158 | "dev": true, 159 | "requires": { 160 | "@babel/types": "^7.16.7" 161 | } 162 | }, 163 | "@babel/helper-validator-identifier": { 164 | "version": "7.16.7", 165 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", 166 | "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", 167 | "dev": true 168 | }, 169 | "@babel/helper-validator-option": { 170 | "version": "7.16.7", 171 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", 172 | "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", 173 | "dev": true 174 | }, 175 | "@babel/helpers": { 176 | "version": "7.17.9", 177 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", 178 | "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", 179 | "dev": true, 180 | "requires": { 181 | "@babel/template": "^7.16.7", 182 | "@babel/traverse": "^7.17.9", 183 | "@babel/types": "^7.17.0" 184 | } 185 | }, 186 | "@babel/highlight": { 187 | "version": "7.17.9", 188 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", 189 | "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", 190 | "dev": true, 191 | "requires": { 192 | "@babel/helper-validator-identifier": "^7.16.7", 193 | "chalk": "^2.0.0", 194 | "js-tokens": "^4.0.0" 195 | } 196 | }, 197 | "@babel/parser": { 198 | "version": "7.17.9", 199 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", 200 | "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", 201 | "dev": true 202 | }, 203 | "@babel/plugin-syntax-jsx": { 204 | "version": "7.16.7", 205 | "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", 206 | "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", 207 | "dev": true, 208 | "requires": { 209 | "@babel/helper-plugin-utils": "^7.16.7" 210 | } 211 | }, 212 | "@babel/plugin-transform-react-jsx": { 213 | "version": "7.17.3", 214 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz", 215 | "integrity": "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==", 216 | "dev": true, 217 | "requires": { 218 | "@babel/helper-annotate-as-pure": "^7.16.7", 219 | "@babel/helper-module-imports": "^7.16.7", 220 | "@babel/helper-plugin-utils": "^7.16.7", 221 | "@babel/plugin-syntax-jsx": "^7.16.7", 222 | "@babel/types": "^7.17.0" 223 | } 224 | }, 225 | "@babel/plugin-transform-react-jsx-development": { 226 | "version": "7.16.7", 227 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz", 228 | "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==", 229 | "dev": true, 230 | "requires": { 231 | "@babel/plugin-transform-react-jsx": "^7.16.7" 232 | } 233 | }, 234 | "@babel/plugin-transform-react-jsx-self": { 235 | "version": "7.16.7", 236 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz", 237 | "integrity": "sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA==", 238 | "dev": true, 239 | "requires": { 240 | "@babel/helper-plugin-utils": "^7.16.7" 241 | } 242 | }, 243 | "@babel/plugin-transform-react-jsx-source": { 244 | "version": "7.16.7", 245 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz", 246 | "integrity": "sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw==", 247 | "dev": true, 248 | "requires": { 249 | "@babel/helper-plugin-utils": "^7.16.7" 250 | } 251 | }, 252 | "@babel/runtime": { 253 | "version": "7.17.9", 254 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", 255 | "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", 256 | "requires": { 257 | "regenerator-runtime": "^0.13.4" 258 | } 259 | }, 260 | "@babel/template": { 261 | "version": "7.16.7", 262 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", 263 | "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", 264 | "dev": true, 265 | "requires": { 266 | "@babel/code-frame": "^7.16.7", 267 | "@babel/parser": "^7.16.7", 268 | "@babel/types": "^7.16.7" 269 | } 270 | }, 271 | "@babel/traverse": { 272 | "version": "7.17.9", 273 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz", 274 | "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", 275 | "dev": true, 276 | "requires": { 277 | "@babel/code-frame": "^7.16.7", 278 | "@babel/generator": "^7.17.9", 279 | "@babel/helper-environment-visitor": "^7.16.7", 280 | "@babel/helper-function-name": "^7.17.9", 281 | "@babel/helper-hoist-variables": "^7.16.7", 282 | "@babel/helper-split-export-declaration": "^7.16.7", 283 | "@babel/parser": "^7.17.9", 284 | "@babel/types": "^7.17.0", 285 | "debug": "^4.1.0", 286 | "globals": "^11.1.0" 287 | } 288 | }, 289 | "@babel/types": { 290 | "version": "7.17.0", 291 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", 292 | "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", 293 | "dev": true, 294 | "requires": { 295 | "@babel/helper-validator-identifier": "^7.16.7", 296 | "to-fast-properties": "^2.0.0" 297 | } 298 | }, 299 | "@jridgewell/resolve-uri": { 300 | "version": "3.0.6", 301 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", 302 | "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", 303 | "dev": true 304 | }, 305 | "@jridgewell/sourcemap-codec": { 306 | "version": "1.4.11", 307 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", 308 | "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", 309 | "dev": true 310 | }, 311 | "@jridgewell/trace-mapping": { 312 | "version": "0.3.9", 313 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 314 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 315 | "dev": true, 316 | "requires": { 317 | "@jridgewell/resolve-uri": "^3.0.3", 318 | "@jridgewell/sourcemap-codec": "^1.4.10" 319 | } 320 | }, 321 | "@reduxjs/toolkit": { 322 | "version": "1.8.1", 323 | "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.1.tgz", 324 | "integrity": "sha512-Q6mzbTpO9nOYRnkwpDlFOAbQnd3g7zj7CtHAZWz5SzE5lcV97Tf8f3SzOO8BoPOMYBFgfZaqTUZqgGu+a0+Fng==", 325 | "requires": { 326 | "immer": "^9.0.7", 327 | "redux": "^4.1.2", 328 | "redux-thunk": "^2.4.1", 329 | "reselect": "^4.1.5" 330 | } 331 | }, 332 | "@rollup/pluginutils": { 333 | "version": "4.2.1", 334 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", 335 | "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", 336 | "dev": true, 337 | "requires": { 338 | "estree-walker": "^2.0.1", 339 | "picomatch": "^2.2.2" 340 | } 341 | }, 342 | "@types/hoist-non-react-statics": { 343 | "version": "3.3.1", 344 | "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", 345 | "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", 346 | "requires": { 347 | "@types/react": "*", 348 | "hoist-non-react-statics": "^3.3.0" 349 | } 350 | }, 351 | "@types/prop-types": { 352 | "version": "15.7.5", 353 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", 354 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" 355 | }, 356 | "@types/react": { 357 | "version": "18.0.6", 358 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.6.tgz", 359 | "integrity": "sha512-bPqwzJRzKtfI0mVYr5R+1o9BOE8UEXefwc1LwcBtfnaAn6OoqMhLa/91VA8aeWfDPJt1kHvYKI8RHcQybZLHHA==", 360 | "requires": { 361 | "@types/prop-types": "*", 362 | "@types/scheduler": "*", 363 | "csstype": "^3.0.2" 364 | } 365 | }, 366 | "@types/react-dom": { 367 | "version": "18.0.2", 368 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.2.tgz", 369 | "integrity": "sha512-UxeS+Wtj5bvLRREz9tIgsK4ntCuLDo0EcAcACgw3E+9wE8ePDr9uQpq53MfcyxyIS55xJ+0B6mDS8c4qkkHLBg==", 370 | "dev": true, 371 | "requires": { 372 | "@types/react": "*" 373 | } 374 | }, 375 | "@types/scheduler": { 376 | "version": "0.16.2", 377 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", 378 | "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" 379 | }, 380 | "@types/use-sync-external-store": { 381 | "version": "0.0.3", 382 | "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", 383 | "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" 384 | }, 385 | "@vitejs/plugin-react": { 386 | "version": "1.3.1", 387 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.3.1.tgz", 388 | "integrity": "sha512-qQS8Y2fZCjo5YmDUplEXl3yn+aueiwxB7BaoQ4nWYJYR+Ai8NXPVLlkLobVMs5+DeyFyg9Lrz6zCzdX1opcvyw==", 389 | "dev": true, 390 | "requires": { 391 | "@babel/core": "^7.17.9", 392 | "@babel/plugin-transform-react-jsx": "^7.17.3", 393 | "@babel/plugin-transform-react-jsx-development": "^7.16.7", 394 | "@babel/plugin-transform-react-jsx-self": "^7.16.7", 395 | "@babel/plugin-transform-react-jsx-source": "^7.16.7", 396 | "@rollup/pluginutils": "^4.2.0", 397 | "react-refresh": "^0.12.0", 398 | "resolve": "^1.22.0" 399 | } 400 | }, 401 | "ansi-styles": { 402 | "version": "3.2.1", 403 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 404 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 405 | "dev": true, 406 | "requires": { 407 | "color-convert": "^1.9.0" 408 | } 409 | }, 410 | "browserslist": { 411 | "version": "4.20.3", 412 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", 413 | "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", 414 | "dev": true, 415 | "requires": { 416 | "caniuse-lite": "^1.0.30001332", 417 | "electron-to-chromium": "^1.4.118", 418 | "escalade": "^3.1.1", 419 | "node-releases": "^2.0.3", 420 | "picocolors": "^1.0.0" 421 | } 422 | }, 423 | "caniuse-lite": { 424 | "version": "1.0.30001332", 425 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", 426 | "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", 427 | "dev": true 428 | }, 429 | "chalk": { 430 | "version": "2.4.2", 431 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 432 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 433 | "dev": true, 434 | "requires": { 435 | "ansi-styles": "^3.2.1", 436 | "escape-string-regexp": "^1.0.5", 437 | "supports-color": "^5.3.0" 438 | } 439 | }, 440 | "color-convert": { 441 | "version": "1.9.3", 442 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 443 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 444 | "dev": true, 445 | "requires": { 446 | "color-name": "1.1.3" 447 | } 448 | }, 449 | "color-name": { 450 | "version": "1.1.3", 451 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 452 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 453 | "dev": true 454 | }, 455 | "convert-source-map": { 456 | "version": "1.8.0", 457 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", 458 | "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", 459 | "dev": true, 460 | "requires": { 461 | "safe-buffer": "~5.1.1" 462 | } 463 | }, 464 | "csstype": { 465 | "version": "3.0.11", 466 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", 467 | "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" 468 | }, 469 | "debug": { 470 | "version": "4.3.4", 471 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 472 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 473 | "dev": true, 474 | "requires": { 475 | "ms": "2.1.2" 476 | } 477 | }, 478 | "electron-to-chromium": { 479 | "version": "1.4.118", 480 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz", 481 | "integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==", 482 | "dev": true 483 | }, 484 | "esbuild": { 485 | "version": "0.14.38", 486 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz", 487 | "integrity": "sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==", 488 | "dev": true, 489 | "requires": { 490 | "esbuild-android-64": "0.14.38", 491 | "esbuild-android-arm64": "0.14.38", 492 | "esbuild-darwin-64": "0.14.38", 493 | "esbuild-darwin-arm64": "0.14.38", 494 | "esbuild-freebsd-64": "0.14.38", 495 | "esbuild-freebsd-arm64": "0.14.38", 496 | "esbuild-linux-32": "0.14.38", 497 | "esbuild-linux-64": "0.14.38", 498 | "esbuild-linux-arm": "0.14.38", 499 | "esbuild-linux-arm64": "0.14.38", 500 | "esbuild-linux-mips64le": "0.14.38", 501 | "esbuild-linux-ppc64le": "0.14.38", 502 | "esbuild-linux-riscv64": "0.14.38", 503 | "esbuild-linux-s390x": "0.14.38", 504 | "esbuild-netbsd-64": "0.14.38", 505 | "esbuild-openbsd-64": "0.14.38", 506 | "esbuild-sunos-64": "0.14.38", 507 | "esbuild-windows-32": "0.14.38", 508 | "esbuild-windows-64": "0.14.38", 509 | "esbuild-windows-arm64": "0.14.38" 510 | } 511 | }, 512 | "esbuild-android-64": { 513 | "version": "0.14.38", 514 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz", 515 | "integrity": "sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw==", 516 | "dev": true, 517 | "optional": true 518 | }, 519 | "esbuild-android-arm64": { 520 | "version": "0.14.38", 521 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz", 522 | "integrity": "sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA==", 523 | "dev": true, 524 | "optional": true 525 | }, 526 | "esbuild-darwin-64": { 527 | "version": "0.14.38", 528 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz", 529 | "integrity": "sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA==", 530 | "dev": true, 531 | "optional": true 532 | }, 533 | "esbuild-darwin-arm64": { 534 | "version": "0.14.38", 535 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz", 536 | "integrity": "sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ==", 537 | "dev": true, 538 | "optional": true 539 | }, 540 | "esbuild-freebsd-64": { 541 | "version": "0.14.38", 542 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz", 543 | "integrity": "sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig==", 544 | "dev": true, 545 | "optional": true 546 | }, 547 | "esbuild-freebsd-arm64": { 548 | "version": "0.14.38", 549 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz", 550 | "integrity": "sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ==", 551 | "dev": true, 552 | "optional": true 553 | }, 554 | "esbuild-linux-32": { 555 | "version": "0.14.38", 556 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz", 557 | "integrity": "sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g==", 558 | "dev": true, 559 | "optional": true 560 | }, 561 | "esbuild-linux-64": { 562 | "version": "0.14.38", 563 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz", 564 | "integrity": "sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q==", 565 | "dev": true, 566 | "optional": true 567 | }, 568 | "esbuild-linux-arm": { 569 | "version": "0.14.38", 570 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz", 571 | "integrity": "sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA==", 572 | "dev": true, 573 | "optional": true 574 | }, 575 | "esbuild-linux-arm64": { 576 | "version": "0.14.38", 577 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz", 578 | "integrity": "sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA==", 579 | "dev": true, 580 | "optional": true 581 | }, 582 | "esbuild-linux-mips64le": { 583 | "version": "0.14.38", 584 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz", 585 | "integrity": "sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ==", 586 | "dev": true, 587 | "optional": true 588 | }, 589 | "esbuild-linux-ppc64le": { 590 | "version": "0.14.38", 591 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz", 592 | "integrity": "sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q==", 593 | "dev": true, 594 | "optional": true 595 | }, 596 | "esbuild-linux-riscv64": { 597 | "version": "0.14.38", 598 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz", 599 | "integrity": "sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ==", 600 | "dev": true, 601 | "optional": true 602 | }, 603 | "esbuild-linux-s390x": { 604 | "version": "0.14.38", 605 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz", 606 | "integrity": "sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ==", 607 | "dev": true, 608 | "optional": true 609 | }, 610 | "esbuild-netbsd-64": { 611 | "version": "0.14.38", 612 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz", 613 | "integrity": "sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q==", 614 | "dev": true, 615 | "optional": true 616 | }, 617 | "esbuild-openbsd-64": { 618 | "version": "0.14.38", 619 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz", 620 | "integrity": "sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ==", 621 | "dev": true, 622 | "optional": true 623 | }, 624 | "esbuild-sunos-64": { 625 | "version": "0.14.38", 626 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz", 627 | "integrity": "sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA==", 628 | "dev": true, 629 | "optional": true 630 | }, 631 | "esbuild-windows-32": { 632 | "version": "0.14.38", 633 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz", 634 | "integrity": "sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw==", 635 | "dev": true, 636 | "optional": true 637 | }, 638 | "esbuild-windows-64": { 639 | "version": "0.14.38", 640 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz", 641 | "integrity": "sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==", 642 | "dev": true, 643 | "optional": true 644 | }, 645 | "esbuild-windows-arm64": { 646 | "version": "0.14.38", 647 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz", 648 | "integrity": "sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw==", 649 | "dev": true, 650 | "optional": true 651 | }, 652 | "escalade": { 653 | "version": "3.1.1", 654 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 655 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 656 | "dev": true 657 | }, 658 | "escape-string-regexp": { 659 | "version": "1.0.5", 660 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 661 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 662 | "dev": true 663 | }, 664 | "estree-walker": { 665 | "version": "2.0.2", 666 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 667 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 668 | "dev": true 669 | }, 670 | "fsevents": { 671 | "version": "2.3.2", 672 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 673 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 674 | "dev": true, 675 | "optional": true 676 | }, 677 | "function-bind": { 678 | "version": "1.1.1", 679 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 680 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 681 | "dev": true 682 | }, 683 | "gensync": { 684 | "version": "1.0.0-beta.2", 685 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 686 | "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", 687 | "dev": true 688 | }, 689 | "globals": { 690 | "version": "11.12.0", 691 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 692 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 693 | "dev": true 694 | }, 695 | "has": { 696 | "version": "1.0.3", 697 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 698 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 699 | "dev": true, 700 | "requires": { 701 | "function-bind": "^1.1.1" 702 | } 703 | }, 704 | "has-flag": { 705 | "version": "3.0.0", 706 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 707 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 708 | "dev": true 709 | }, 710 | "hoist-non-react-statics": { 711 | "version": "3.3.2", 712 | "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", 713 | "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", 714 | "requires": { 715 | "react-is": "^16.7.0" 716 | }, 717 | "dependencies": { 718 | "react-is": { 719 | "version": "16.13.1", 720 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 721 | "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" 722 | } 723 | } 724 | }, 725 | "immer": { 726 | "version": "9.0.12", 727 | "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz", 728 | "integrity": "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==" 729 | }, 730 | "is-core-module": { 731 | "version": "2.9.0", 732 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", 733 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", 734 | "dev": true, 735 | "requires": { 736 | "has": "^1.0.3" 737 | } 738 | }, 739 | "js-tokens": { 740 | "version": "4.0.0", 741 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 742 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 743 | }, 744 | "jsesc": { 745 | "version": "2.5.2", 746 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 747 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", 748 | "dev": true 749 | }, 750 | "json5": { 751 | "version": "2.2.1", 752 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", 753 | "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", 754 | "dev": true 755 | }, 756 | "loose-envify": { 757 | "version": "1.4.0", 758 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 759 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 760 | "requires": { 761 | "js-tokens": "^3.0.0 || ^4.0.0" 762 | } 763 | }, 764 | "ms": { 765 | "version": "2.1.2", 766 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 767 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 768 | "dev": true 769 | }, 770 | "nanoid": { 771 | "version": "3.3.3", 772 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", 773 | "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", 774 | "dev": true 775 | }, 776 | "node-releases": { 777 | "version": "2.0.3", 778 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", 779 | "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", 780 | "dev": true 781 | }, 782 | "path-parse": { 783 | "version": "1.0.7", 784 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 785 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 786 | "dev": true 787 | }, 788 | "picocolors": { 789 | "version": "1.0.0", 790 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 791 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 792 | "dev": true 793 | }, 794 | "picomatch": { 795 | "version": "2.3.1", 796 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 797 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 798 | "dev": true 799 | }, 800 | "postcss": { 801 | "version": "8.4.12", 802 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", 803 | "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", 804 | "dev": true, 805 | "requires": { 806 | "nanoid": "^3.3.1", 807 | "picocolors": "^1.0.0", 808 | "source-map-js": "^1.0.2" 809 | } 810 | }, 811 | "react": { 812 | "version": "18.0.0", 813 | "resolved": "https://registry.npmjs.org/react/-/react-18.0.0.tgz", 814 | "integrity": "sha512-x+VL6wbT4JRVPm7EGxXhZ8w8LTROaxPXOqhlGyVSrv0sB1jkyFGgXxJ8LVoPRLvPR6/CIZGFmfzqUa2NYeMr2A==", 815 | "requires": { 816 | "loose-envify": "^1.1.0" 817 | } 818 | }, 819 | "react-dom": { 820 | "version": "18.0.0", 821 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.0.0.tgz", 822 | "integrity": "sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw==", 823 | "requires": { 824 | "loose-envify": "^1.1.0", 825 | "scheduler": "^0.21.0" 826 | } 827 | }, 828 | "react-is": { 829 | "version": "18.0.0", 830 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.0.0.tgz", 831 | "integrity": "sha512-yUcBYdBBbo3QiPsgYDcfQcIkGZHfxOaoE6HLSnr1sPzMhdyxusbfKOSUbSd/ocGi32dxcj366PsTj+5oggeKKw==" 832 | }, 833 | "react-redux": { 834 | "version": "8.0.1", 835 | "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.1.tgz", 836 | "integrity": "sha512-LMZMsPY4DYdZfLJgd7i79n5Kps5N9XVLCJJeWAaPYTV+Eah2zTuBjTxKtNEbjiyitbq80/eIkm55CYSLqAub3w==", 837 | "requires": { 838 | "@babel/runtime": "^7.12.1", 839 | "@types/hoist-non-react-statics": "^3.3.1", 840 | "@types/use-sync-external-store": "^0.0.3", 841 | "hoist-non-react-statics": "^3.3.2", 842 | "react-is": "^18.0.0", 843 | "use-sync-external-store": "^1.0.0" 844 | } 845 | }, 846 | "react-refresh": { 847 | "version": "0.12.0", 848 | "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.12.0.tgz", 849 | "integrity": "sha512-suLIhrU2IHKL5JEKR/fAwJv7bbeq4kJ+pJopf77jHwuR+HmJS/HbrPIGsTBUVfw7tXPOmYv7UJ7PCaN49e8x4A==", 850 | "dev": true 851 | }, 852 | "redux": { 853 | "version": "4.2.0", 854 | "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", 855 | "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", 856 | "requires": { 857 | "@babel/runtime": "^7.9.2" 858 | } 859 | }, 860 | "redux-thunk": { 861 | "version": "2.4.1", 862 | "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", 863 | "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==" 864 | }, 865 | "regenerator-runtime": { 866 | "version": "0.13.9", 867 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", 868 | "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" 869 | }, 870 | "reselect": { 871 | "version": "4.1.5", 872 | "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz", 873 | "integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==" 874 | }, 875 | "resolve": { 876 | "version": "1.22.0", 877 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", 878 | "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", 879 | "dev": true, 880 | "requires": { 881 | "is-core-module": "^2.8.1", 882 | "path-parse": "^1.0.7", 883 | "supports-preserve-symlinks-flag": "^1.0.0" 884 | } 885 | }, 886 | "rollup": { 887 | "version": "2.70.2", 888 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.2.tgz", 889 | "integrity": "sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==", 890 | "dev": true, 891 | "requires": { 892 | "fsevents": "~2.3.2" 893 | } 894 | }, 895 | "safe-buffer": { 896 | "version": "5.1.2", 897 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 898 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 899 | "dev": true 900 | }, 901 | "scheduler": { 902 | "version": "0.21.0", 903 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", 904 | "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", 905 | "requires": { 906 | "loose-envify": "^1.1.0" 907 | } 908 | }, 909 | "semver": { 910 | "version": "6.3.0", 911 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 912 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 913 | "dev": true 914 | }, 915 | "source-map": { 916 | "version": "0.5.7", 917 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 918 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 919 | "dev": true 920 | }, 921 | "source-map-js": { 922 | "version": "1.0.2", 923 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 924 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 925 | "dev": true 926 | }, 927 | "supports-color": { 928 | "version": "5.5.0", 929 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 930 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 931 | "dev": true, 932 | "requires": { 933 | "has-flag": "^3.0.0" 934 | } 935 | }, 936 | "supports-preserve-symlinks-flag": { 937 | "version": "1.0.0", 938 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 939 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 940 | "dev": true 941 | }, 942 | "to-fast-properties": { 943 | "version": "2.0.0", 944 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 945 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", 946 | "dev": true 947 | }, 948 | "use-sync-external-store": { 949 | "version": "1.0.0", 950 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.0.0.tgz", 951 | "integrity": "sha512-AFVsxg5GkFg8GDcxnl+Z0lMAz9rE8DGJCc28qnBuQF7lac57B5smLcT37aXpXIIPz75rW4g3eXHPjhHwdGskOw==" 952 | }, 953 | "vite": { 954 | "version": "2.9.5", 955 | "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.5.tgz", 956 | "integrity": "sha512-dvMN64X2YEQgSXF1lYabKXw3BbN6e+BL67+P3Vy4MacnY+UzT1AfkHiioFSi9+uiDUiaDy7Ax/LQqivk6orilg==", 957 | "dev": true, 958 | "requires": { 959 | "esbuild": "^0.14.27", 960 | "fsevents": "~2.3.2", 961 | "postcss": "^8.4.12", 962 | "resolve": "^1.22.0", 963 | "rollup": "^2.59.0" 964 | } 965 | } 966 | } 967 | } 968 | -------------------------------------------------------------------------------- /javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-react-template-redux", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@reduxjs/toolkit": "^1.8.1", 12 | "react": "^18.0.0", 13 | "react-dom": "^18.0.0", 14 | "react-redux": "^8.0.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.0", 18 | "@types/react-dom": "^18.0.0", 19 | "@vitejs/plugin-react": "^1.3.0", 20 | "vite": "^2.9.5" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /javascript/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-float infinite 3s ease-in-out; 13 | } 14 | } 15 | 16 | .App-header { 17 | min-height: 100vh; 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | justify-content: center; 22 | font-size: calc(10px + 2vmin); 23 | } 24 | 25 | .App-link { 26 | color: rgb(112, 76, 182); 27 | } 28 | 29 | @keyframes App-logo-float { 30 | 0% { 31 | transform: translateY(0); 32 | } 33 | 50% { 34 | transform: translateY(10px); 35 | } 36 | 100% { 37 | transform: translateY(0px); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /javascript/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import logo from "./logo.svg"; 3 | import { Counter } from "./features/counter/Counter"; 4 | import "./App.css"; 5 | 6 | function App() { 7 | return ( 8 |
9 |
10 | logo 11 | 12 |

13 | Edit src/App.js and save to reload. 14 |

15 | 16 | Learn 17 | 23 | React 24 | 25 | , 26 | 32 | Redux 33 | 34 | , 35 | 41 | Redux Toolkit 42 | 43 | , and 44 | 50 | React Redux 51 | 52 | 53 |
54 |
55 | ); 56 | } 57 | 58 | export default App; 59 | -------------------------------------------------------------------------------- /javascript/src/App.test.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import { Provider } from "react-redux"; 4 | import { store } from "./app/store"; 5 | import App from "./App"; 6 | 7 | test("renders learn react link", () => { 8 | const { getByText } = render( 9 | 10 | 11 | 12 | ); 13 | 14 | expect(getByText(/learn/i)).toBeInTheDocument(); 15 | }); 16 | -------------------------------------------------------------------------------- /javascript/src/app/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import counterReducer from "../features/counter/counterSlice"; 3 | 4 | export const store = configureStore({ 5 | reducer: { 6 | counter: counterReducer, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /javascript/src/features/counter/Counter.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { useSelector, useDispatch } from "react-redux"; 3 | import { 4 | decrement, 5 | increment, 6 | incrementByAmount, 7 | incrementAsync, 8 | incrementIfOdd, 9 | selectCount, 10 | } from "./counterSlice"; 11 | import styles from "./Counter.module.css"; 12 | 13 | export function Counter() { 14 | const count = useSelector(selectCount); 15 | const dispatch = useDispatch(); 16 | const [incrementAmount, setIncrementAmount] = useState("2"); 17 | 18 | const incrementValue = Number(incrementAmount) || 0; 19 | 20 | return ( 21 |
22 |
23 | 30 | {count} 31 | 38 |
39 |
40 | setIncrementAmount(e.target.value)} 45 | /> 46 | 52 | 58 | 64 |
65 |
66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /javascript/src/features/counter/Counter.module.css: -------------------------------------------------------------------------------- 1 | .row { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | } 6 | 7 | .row > button { 8 | margin-left: 4px; 9 | margin-right: 8px; 10 | } 11 | .row:not(:last-child) { 12 | margin-bottom: 16px; 13 | } 14 | 15 | .value { 16 | font-size: 78px; 17 | padding-left: 16px; 18 | padding-right: 16px; 19 | margin-top: 2px; 20 | font-family: "Courier New", Courier, monospace; 21 | } 22 | 23 | .button { 24 | appearance: none; 25 | background: none; 26 | font-size: 32px; 27 | padding-left: 12px; 28 | padding-right: 12px; 29 | outline: none; 30 | border: 2px solid transparent; 31 | color: rgb(112, 76, 182); 32 | padding-bottom: 4px; 33 | cursor: pointer; 34 | background-color: rgba(112, 76, 182, 0.1); 35 | border-radius: 2px; 36 | transition: all 0.15s; 37 | } 38 | 39 | .textbox { 40 | font-size: 32px; 41 | padding: 2px; 42 | width: 64px; 43 | text-align: center; 44 | margin-right: 4px; 45 | } 46 | 47 | .button:hover, 48 | .button:focus { 49 | border: 2px solid rgba(112, 76, 182, 0.4); 50 | } 51 | 52 | .button:active { 53 | background-color: rgba(112, 76, 182, 0.2); 54 | } 55 | 56 | .asyncButton { 57 | composes: button; 58 | position: relative; 59 | } 60 | 61 | .asyncButton:after { 62 | content: ""; 63 | background-color: rgba(112, 76, 182, 0.15); 64 | display: block; 65 | position: absolute; 66 | width: 100%; 67 | height: 100%; 68 | left: 0; 69 | top: 0; 70 | opacity: 0; 71 | transition: width 1s linear, opacity 0.5s ease 1s; 72 | } 73 | 74 | .asyncButton:active:after { 75 | width: 0%; 76 | opacity: 1; 77 | transition: 0s; 78 | } 79 | -------------------------------------------------------------------------------- /javascript/src/features/counter/counterAPI.js: -------------------------------------------------------------------------------- 1 | // A mock function to mimic making an async request for data 2 | export function fetchCount(amount = 1) { 3 | return new Promise((resolve) => 4 | setTimeout(() => resolve({ data: amount }), 500) 5 | ); 6 | } 7 | -------------------------------------------------------------------------------- /javascript/src/features/counter/counterSlice.js: -------------------------------------------------------------------------------- 1 | import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; 2 | import { fetchCount } from "./counterAPI"; 3 | 4 | const initialState = { 5 | value: 0, 6 | status: "idle", 7 | }; 8 | 9 | // The function below is called a thunk and allows us to perform async logic. It 10 | // can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This 11 | // will call the thunk with the `dispatch` function as the first argument. Async 12 | // code can then be executed and other actions can be dispatched. Thunks are 13 | // typically used to make async requests. 14 | export const incrementAsync = createAsyncThunk( 15 | "counter/fetchCount", 16 | async (amount) => { 17 | const response = await fetchCount(amount); 18 | // The value we return becomes the `fulfilled` action payload 19 | return response.data; 20 | } 21 | ); 22 | 23 | export const counterSlice = createSlice({ 24 | name: "counter", 25 | initialState, 26 | // The `reducers` field lets us define reducers and generate associated actions 27 | reducers: { 28 | increment: (state) => { 29 | // Redux Toolkit allows us to write "mutating" logic in reducers. It 30 | // doesn't actually mutate the state because it uses the Immer library, 31 | // which detects changes to a "draft state" and produces a brand new 32 | // immutable state based off those changes 33 | state.value += 1; 34 | }, 35 | decrement: (state) => { 36 | state.value -= 1; 37 | }, 38 | // Use the PayloadAction type to declare the contents of `action.payload` 39 | incrementByAmount: (state, action) => { 40 | state.value += action.payload; 41 | }, 42 | }, 43 | // The `extraReducers` field lets the slice handle actions defined elsewhere, 44 | // including actions generated by createAsyncThunk or in other slices. 45 | extraReducers: (builder) => { 46 | builder 47 | .addCase(incrementAsync.pending, (state) => { 48 | state.status = "loading"; 49 | }) 50 | .addCase(incrementAsync.fulfilled, (state, action) => { 51 | state.status = "idle"; 52 | state.value += action.payload; 53 | }); 54 | }, 55 | }); 56 | 57 | export const { increment, decrement, incrementByAmount } = counterSlice.actions; 58 | 59 | // The function below is called a selector and allows us to select a value from 60 | // the state. Selectors can also be defined inline where they're used instead of 61 | // in the slice file. For example: `useSelector((state: RootState) => state.counter.value)` 62 | export const selectCount = (state) => state.counter.value; 63 | 64 | // We can also write thunks by hand, which may contain both sync and async logic. 65 | // Here's an example of conditionally dispatching actions based on current state. 66 | export const incrementIfOdd = (amount) => (dispatch, getState) => { 67 | const currentValue = selectCount(getState()); 68 | if (currentValue % 2 === 1) { 69 | dispatch(incrementByAmount(amount)); 70 | } 71 | }; 72 | 73 | export default counterSlice.reducer; 74 | -------------------------------------------------------------------------------- /javascript/src/features/counter/counterSlice.spec.js: -------------------------------------------------------------------------------- 1 | import counterReducer, { 2 | increment, 3 | decrement, 4 | incrementByAmount, 5 | } from "./counterSlice"; 6 | 7 | describe("counter reducer", () => { 8 | const initialState = { 9 | value: 3, 10 | status: "idle", 11 | }; 12 | it("should handle initial state", () => { 13 | expect(counterReducer(undefined, { type: "unknown" })).toEqual({ 14 | value: 0, 15 | status: "idle", 16 | }); 17 | }); 18 | 19 | it("should handle increment", () => { 20 | const actual = counterReducer(initialState, increment()); 21 | expect(actual.value).toEqual(4); 22 | }); 23 | 24 | it("should handle decrement", () => { 25 | const actual = counterReducer(initialState, decrement()); 26 | expect(actual.value).toEqual(2); 27 | }); 28 | 29 | it("should handle incrementByAmount", () => { 30 | const actual = counterReducer(initialState, incrementByAmount(2)); 31 | expect(actual.value).toEqual(5); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /javascript/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /javascript/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /javascript/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { Provider } from "react-redux"; 4 | import { store } from "./app/store"; 5 | import App from "./App"; 6 | import "./index.css"; 7 | 8 | ReactDOM.createRoot(document.getElementById("root")).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /javascript/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /typescript/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /typescript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /typescript/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-react-template-redux", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@ampproject/remapping": { 8 | "version": "2.1.2", 9 | "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", 10 | "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", 11 | "dev": true, 12 | "requires": { 13 | "@jridgewell/trace-mapping": "^0.3.0" 14 | } 15 | }, 16 | "@babel/code-frame": { 17 | "version": "7.16.7", 18 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", 19 | "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", 20 | "dev": true, 21 | "requires": { 22 | "@babel/highlight": "^7.16.7" 23 | } 24 | }, 25 | "@babel/compat-data": { 26 | "version": "7.17.7", 27 | "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", 28 | "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", 29 | "dev": true 30 | }, 31 | "@babel/core": { 32 | "version": "7.17.9", 33 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", 34 | "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==", 35 | "dev": true, 36 | "requires": { 37 | "@ampproject/remapping": "^2.1.0", 38 | "@babel/code-frame": "^7.16.7", 39 | "@babel/generator": "^7.17.9", 40 | "@babel/helper-compilation-targets": "^7.17.7", 41 | "@babel/helper-module-transforms": "^7.17.7", 42 | "@babel/helpers": "^7.17.9", 43 | "@babel/parser": "^7.17.9", 44 | "@babel/template": "^7.16.7", 45 | "@babel/traverse": "^7.17.9", 46 | "@babel/types": "^7.17.0", 47 | "convert-source-map": "^1.7.0", 48 | "debug": "^4.1.0", 49 | "gensync": "^1.0.0-beta.2", 50 | "json5": "^2.2.1", 51 | "semver": "^6.3.0" 52 | } 53 | }, 54 | "@babel/generator": { 55 | "version": "7.17.9", 56 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", 57 | "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", 58 | "dev": true, 59 | "requires": { 60 | "@babel/types": "^7.17.0", 61 | "jsesc": "^2.5.1", 62 | "source-map": "^0.5.0" 63 | } 64 | }, 65 | "@babel/helper-annotate-as-pure": { 66 | "version": "7.16.7", 67 | "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", 68 | "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", 69 | "dev": true, 70 | "requires": { 71 | "@babel/types": "^7.16.7" 72 | } 73 | }, 74 | "@babel/helper-compilation-targets": { 75 | "version": "7.17.7", 76 | "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", 77 | "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", 78 | "dev": true, 79 | "requires": { 80 | "@babel/compat-data": "^7.17.7", 81 | "@babel/helper-validator-option": "^7.16.7", 82 | "browserslist": "^4.17.5", 83 | "semver": "^6.3.0" 84 | } 85 | }, 86 | "@babel/helper-environment-visitor": { 87 | "version": "7.16.7", 88 | "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", 89 | "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", 90 | "dev": true, 91 | "requires": { 92 | "@babel/types": "^7.16.7" 93 | } 94 | }, 95 | "@babel/helper-function-name": { 96 | "version": "7.17.9", 97 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", 98 | "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", 99 | "dev": true, 100 | "requires": { 101 | "@babel/template": "^7.16.7", 102 | "@babel/types": "^7.17.0" 103 | } 104 | }, 105 | "@babel/helper-hoist-variables": { 106 | "version": "7.16.7", 107 | "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", 108 | "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", 109 | "dev": true, 110 | "requires": { 111 | "@babel/types": "^7.16.7" 112 | } 113 | }, 114 | "@babel/helper-module-imports": { 115 | "version": "7.16.7", 116 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", 117 | "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", 118 | "dev": true, 119 | "requires": { 120 | "@babel/types": "^7.16.7" 121 | } 122 | }, 123 | "@babel/helper-module-transforms": { 124 | "version": "7.17.7", 125 | "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", 126 | "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", 127 | "dev": true, 128 | "requires": { 129 | "@babel/helper-environment-visitor": "^7.16.7", 130 | "@babel/helper-module-imports": "^7.16.7", 131 | "@babel/helper-simple-access": "^7.17.7", 132 | "@babel/helper-split-export-declaration": "^7.16.7", 133 | "@babel/helper-validator-identifier": "^7.16.7", 134 | "@babel/template": "^7.16.7", 135 | "@babel/traverse": "^7.17.3", 136 | "@babel/types": "^7.17.0" 137 | } 138 | }, 139 | "@babel/helper-plugin-utils": { 140 | "version": "7.16.7", 141 | "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", 142 | "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", 143 | "dev": true 144 | }, 145 | "@babel/helper-simple-access": { 146 | "version": "7.17.7", 147 | "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", 148 | "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", 149 | "dev": true, 150 | "requires": { 151 | "@babel/types": "^7.17.0" 152 | } 153 | }, 154 | "@babel/helper-split-export-declaration": { 155 | "version": "7.16.7", 156 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", 157 | "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", 158 | "dev": true, 159 | "requires": { 160 | "@babel/types": "^7.16.7" 161 | } 162 | }, 163 | "@babel/helper-validator-identifier": { 164 | "version": "7.16.7", 165 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", 166 | "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", 167 | "dev": true 168 | }, 169 | "@babel/helper-validator-option": { 170 | "version": "7.16.7", 171 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", 172 | "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", 173 | "dev": true 174 | }, 175 | "@babel/helpers": { 176 | "version": "7.17.9", 177 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", 178 | "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", 179 | "dev": true, 180 | "requires": { 181 | "@babel/template": "^7.16.7", 182 | "@babel/traverse": "^7.17.9", 183 | "@babel/types": "^7.17.0" 184 | } 185 | }, 186 | "@babel/highlight": { 187 | "version": "7.17.9", 188 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", 189 | "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", 190 | "dev": true, 191 | "requires": { 192 | "@babel/helper-validator-identifier": "^7.16.7", 193 | "chalk": "^2.0.0", 194 | "js-tokens": "^4.0.0" 195 | } 196 | }, 197 | "@babel/parser": { 198 | "version": "7.17.9", 199 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", 200 | "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", 201 | "dev": true 202 | }, 203 | "@babel/plugin-syntax-jsx": { 204 | "version": "7.16.7", 205 | "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", 206 | "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", 207 | "dev": true, 208 | "requires": { 209 | "@babel/helper-plugin-utils": "^7.16.7" 210 | } 211 | }, 212 | "@babel/plugin-transform-react-jsx": { 213 | "version": "7.17.3", 214 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz", 215 | "integrity": "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==", 216 | "dev": true, 217 | "requires": { 218 | "@babel/helper-annotate-as-pure": "^7.16.7", 219 | "@babel/helper-module-imports": "^7.16.7", 220 | "@babel/helper-plugin-utils": "^7.16.7", 221 | "@babel/plugin-syntax-jsx": "^7.16.7", 222 | "@babel/types": "^7.17.0" 223 | } 224 | }, 225 | "@babel/plugin-transform-react-jsx-development": { 226 | "version": "7.16.7", 227 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz", 228 | "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==", 229 | "dev": true, 230 | "requires": { 231 | "@babel/plugin-transform-react-jsx": "^7.16.7" 232 | } 233 | }, 234 | "@babel/plugin-transform-react-jsx-self": { 235 | "version": "7.16.7", 236 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz", 237 | "integrity": "sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA==", 238 | "dev": true, 239 | "requires": { 240 | "@babel/helper-plugin-utils": "^7.16.7" 241 | } 242 | }, 243 | "@babel/plugin-transform-react-jsx-source": { 244 | "version": "7.16.7", 245 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz", 246 | "integrity": "sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw==", 247 | "dev": true, 248 | "requires": { 249 | "@babel/helper-plugin-utils": "^7.16.7" 250 | } 251 | }, 252 | "@babel/runtime": { 253 | "version": "7.17.9", 254 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", 255 | "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", 256 | "requires": { 257 | "regenerator-runtime": "^0.13.4" 258 | } 259 | }, 260 | "@babel/template": { 261 | "version": "7.16.7", 262 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", 263 | "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", 264 | "dev": true, 265 | "requires": { 266 | "@babel/code-frame": "^7.16.7", 267 | "@babel/parser": "^7.16.7", 268 | "@babel/types": "^7.16.7" 269 | } 270 | }, 271 | "@babel/traverse": { 272 | "version": "7.17.9", 273 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz", 274 | "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", 275 | "dev": true, 276 | "requires": { 277 | "@babel/code-frame": "^7.16.7", 278 | "@babel/generator": "^7.17.9", 279 | "@babel/helper-environment-visitor": "^7.16.7", 280 | "@babel/helper-function-name": "^7.17.9", 281 | "@babel/helper-hoist-variables": "^7.16.7", 282 | "@babel/helper-split-export-declaration": "^7.16.7", 283 | "@babel/parser": "^7.17.9", 284 | "@babel/types": "^7.17.0", 285 | "debug": "^4.1.0", 286 | "globals": "^11.1.0" 287 | } 288 | }, 289 | "@babel/types": { 290 | "version": "7.17.0", 291 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", 292 | "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", 293 | "dev": true, 294 | "requires": { 295 | "@babel/helper-validator-identifier": "^7.16.7", 296 | "to-fast-properties": "^2.0.0" 297 | } 298 | }, 299 | "@jridgewell/resolve-uri": { 300 | "version": "3.0.6", 301 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", 302 | "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", 303 | "dev": true 304 | }, 305 | "@jridgewell/sourcemap-codec": { 306 | "version": "1.4.11", 307 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", 308 | "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", 309 | "dev": true 310 | }, 311 | "@jridgewell/trace-mapping": { 312 | "version": "0.3.9", 313 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 314 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 315 | "dev": true, 316 | "requires": { 317 | "@jridgewell/resolve-uri": "^3.0.3", 318 | "@jridgewell/sourcemap-codec": "^1.4.10" 319 | } 320 | }, 321 | "@reduxjs/toolkit": { 322 | "version": "1.8.1", 323 | "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.1.tgz", 324 | "integrity": "sha512-Q6mzbTpO9nOYRnkwpDlFOAbQnd3g7zj7CtHAZWz5SzE5lcV97Tf8f3SzOO8BoPOMYBFgfZaqTUZqgGu+a0+Fng==", 325 | "requires": { 326 | "immer": "^9.0.7", 327 | "redux": "^4.1.2", 328 | "redux-thunk": "^2.4.1", 329 | "reselect": "^4.1.5" 330 | } 331 | }, 332 | "@rollup/pluginutils": { 333 | "version": "4.2.1", 334 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", 335 | "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", 336 | "dev": true, 337 | "requires": { 338 | "estree-walker": "^2.0.1", 339 | "picomatch": "^2.2.2" 340 | } 341 | }, 342 | "@types/hoist-non-react-statics": { 343 | "version": "3.3.1", 344 | "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", 345 | "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", 346 | "requires": { 347 | "@types/react": "*", 348 | "hoist-non-react-statics": "^3.3.0" 349 | } 350 | }, 351 | "@types/prop-types": { 352 | "version": "15.7.5", 353 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", 354 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" 355 | }, 356 | "@types/react": { 357 | "version": "18.0.6", 358 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.6.tgz", 359 | "integrity": "sha512-bPqwzJRzKtfI0mVYr5R+1o9BOE8UEXefwc1LwcBtfnaAn6OoqMhLa/91VA8aeWfDPJt1kHvYKI8RHcQybZLHHA==", 360 | "requires": { 361 | "@types/prop-types": "*", 362 | "@types/scheduler": "*", 363 | "csstype": "^3.0.2" 364 | } 365 | }, 366 | "@types/react-dom": { 367 | "version": "18.0.2", 368 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.2.tgz", 369 | "integrity": "sha512-UxeS+Wtj5bvLRREz9tIgsK4ntCuLDo0EcAcACgw3E+9wE8ePDr9uQpq53MfcyxyIS55xJ+0B6mDS8c4qkkHLBg==", 370 | "dev": true, 371 | "requires": { 372 | "@types/react": "*" 373 | } 374 | }, 375 | "@types/scheduler": { 376 | "version": "0.16.2", 377 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", 378 | "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" 379 | }, 380 | "@types/use-sync-external-store": { 381 | "version": "0.0.3", 382 | "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", 383 | "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" 384 | }, 385 | "@vitejs/plugin-react": { 386 | "version": "1.3.1", 387 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.3.1.tgz", 388 | "integrity": "sha512-qQS8Y2fZCjo5YmDUplEXl3yn+aueiwxB7BaoQ4nWYJYR+Ai8NXPVLlkLobVMs5+DeyFyg9Lrz6zCzdX1opcvyw==", 389 | "dev": true, 390 | "requires": { 391 | "@babel/core": "^7.17.9", 392 | "@babel/plugin-transform-react-jsx": "^7.17.3", 393 | "@babel/plugin-transform-react-jsx-development": "^7.16.7", 394 | "@babel/plugin-transform-react-jsx-self": "^7.16.7", 395 | "@babel/plugin-transform-react-jsx-source": "^7.16.7", 396 | "@rollup/pluginutils": "^4.2.0", 397 | "react-refresh": "^0.12.0", 398 | "resolve": "^1.22.0" 399 | } 400 | }, 401 | "ansi-styles": { 402 | "version": "3.2.1", 403 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 404 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 405 | "dev": true, 406 | "requires": { 407 | "color-convert": "^1.9.0" 408 | } 409 | }, 410 | "browserslist": { 411 | "version": "4.20.3", 412 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", 413 | "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", 414 | "dev": true, 415 | "requires": { 416 | "caniuse-lite": "^1.0.30001332", 417 | "electron-to-chromium": "^1.4.118", 418 | "escalade": "^3.1.1", 419 | "node-releases": "^2.0.3", 420 | "picocolors": "^1.0.0" 421 | } 422 | }, 423 | "caniuse-lite": { 424 | "version": "1.0.30001332", 425 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", 426 | "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", 427 | "dev": true 428 | }, 429 | "chalk": { 430 | "version": "2.4.2", 431 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 432 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 433 | "dev": true, 434 | "requires": { 435 | "ansi-styles": "^3.2.1", 436 | "escape-string-regexp": "^1.0.5", 437 | "supports-color": "^5.3.0" 438 | } 439 | }, 440 | "color-convert": { 441 | "version": "1.9.3", 442 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 443 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 444 | "dev": true, 445 | "requires": { 446 | "color-name": "1.1.3" 447 | } 448 | }, 449 | "color-name": { 450 | "version": "1.1.3", 451 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 452 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 453 | "dev": true 454 | }, 455 | "convert-source-map": { 456 | "version": "1.8.0", 457 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", 458 | "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", 459 | "dev": true, 460 | "requires": { 461 | "safe-buffer": "~5.1.1" 462 | } 463 | }, 464 | "csstype": { 465 | "version": "3.0.11", 466 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", 467 | "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" 468 | }, 469 | "debug": { 470 | "version": "4.3.4", 471 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 472 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 473 | "dev": true, 474 | "requires": { 475 | "ms": "2.1.2" 476 | } 477 | }, 478 | "electron-to-chromium": { 479 | "version": "1.4.118", 480 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz", 481 | "integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==", 482 | "dev": true 483 | }, 484 | "esbuild": { 485 | "version": "0.14.38", 486 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz", 487 | "integrity": "sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==", 488 | "dev": true, 489 | "requires": { 490 | "esbuild-android-64": "0.14.38", 491 | "esbuild-android-arm64": "0.14.38", 492 | "esbuild-darwin-64": "0.14.38", 493 | "esbuild-darwin-arm64": "0.14.38", 494 | "esbuild-freebsd-64": "0.14.38", 495 | "esbuild-freebsd-arm64": "0.14.38", 496 | "esbuild-linux-32": "0.14.38", 497 | "esbuild-linux-64": "0.14.38", 498 | "esbuild-linux-arm": "0.14.38", 499 | "esbuild-linux-arm64": "0.14.38", 500 | "esbuild-linux-mips64le": "0.14.38", 501 | "esbuild-linux-ppc64le": "0.14.38", 502 | "esbuild-linux-riscv64": "0.14.38", 503 | "esbuild-linux-s390x": "0.14.38", 504 | "esbuild-netbsd-64": "0.14.38", 505 | "esbuild-openbsd-64": "0.14.38", 506 | "esbuild-sunos-64": "0.14.38", 507 | "esbuild-windows-32": "0.14.38", 508 | "esbuild-windows-64": "0.14.38", 509 | "esbuild-windows-arm64": "0.14.38" 510 | } 511 | }, 512 | "esbuild-android-64": { 513 | "version": "0.14.38", 514 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz", 515 | "integrity": "sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw==", 516 | "dev": true, 517 | "optional": true 518 | }, 519 | "esbuild-android-arm64": { 520 | "version": "0.14.38", 521 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz", 522 | "integrity": "sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA==", 523 | "dev": true, 524 | "optional": true 525 | }, 526 | "esbuild-darwin-64": { 527 | "version": "0.14.38", 528 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz", 529 | "integrity": "sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA==", 530 | "dev": true, 531 | "optional": true 532 | }, 533 | "esbuild-darwin-arm64": { 534 | "version": "0.14.38", 535 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz", 536 | "integrity": "sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ==", 537 | "dev": true, 538 | "optional": true 539 | }, 540 | "esbuild-freebsd-64": { 541 | "version": "0.14.38", 542 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz", 543 | "integrity": "sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig==", 544 | "dev": true, 545 | "optional": true 546 | }, 547 | "esbuild-freebsd-arm64": { 548 | "version": "0.14.38", 549 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz", 550 | "integrity": "sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ==", 551 | "dev": true, 552 | "optional": true 553 | }, 554 | "esbuild-linux-32": { 555 | "version": "0.14.38", 556 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz", 557 | "integrity": "sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g==", 558 | "dev": true, 559 | "optional": true 560 | }, 561 | "esbuild-linux-64": { 562 | "version": "0.14.38", 563 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz", 564 | "integrity": "sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q==", 565 | "dev": true, 566 | "optional": true 567 | }, 568 | "esbuild-linux-arm": { 569 | "version": "0.14.38", 570 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz", 571 | "integrity": "sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA==", 572 | "dev": true, 573 | "optional": true 574 | }, 575 | "esbuild-linux-arm64": { 576 | "version": "0.14.38", 577 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz", 578 | "integrity": "sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA==", 579 | "dev": true, 580 | "optional": true 581 | }, 582 | "esbuild-linux-mips64le": { 583 | "version": "0.14.38", 584 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz", 585 | "integrity": "sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ==", 586 | "dev": true, 587 | "optional": true 588 | }, 589 | "esbuild-linux-ppc64le": { 590 | "version": "0.14.38", 591 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz", 592 | "integrity": "sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q==", 593 | "dev": true, 594 | "optional": true 595 | }, 596 | "esbuild-linux-riscv64": { 597 | "version": "0.14.38", 598 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz", 599 | "integrity": "sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ==", 600 | "dev": true, 601 | "optional": true 602 | }, 603 | "esbuild-linux-s390x": { 604 | "version": "0.14.38", 605 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz", 606 | "integrity": "sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ==", 607 | "dev": true, 608 | "optional": true 609 | }, 610 | "esbuild-netbsd-64": { 611 | "version": "0.14.38", 612 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz", 613 | "integrity": "sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q==", 614 | "dev": true, 615 | "optional": true 616 | }, 617 | "esbuild-openbsd-64": { 618 | "version": "0.14.38", 619 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz", 620 | "integrity": "sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ==", 621 | "dev": true, 622 | "optional": true 623 | }, 624 | "esbuild-sunos-64": { 625 | "version": "0.14.38", 626 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz", 627 | "integrity": "sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA==", 628 | "dev": true, 629 | "optional": true 630 | }, 631 | "esbuild-windows-32": { 632 | "version": "0.14.38", 633 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz", 634 | "integrity": "sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw==", 635 | "dev": true, 636 | "optional": true 637 | }, 638 | "esbuild-windows-64": { 639 | "version": "0.14.38", 640 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz", 641 | "integrity": "sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==", 642 | "dev": true, 643 | "optional": true 644 | }, 645 | "esbuild-windows-arm64": { 646 | "version": "0.14.38", 647 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz", 648 | "integrity": "sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw==", 649 | "dev": true, 650 | "optional": true 651 | }, 652 | "escalade": { 653 | "version": "3.1.1", 654 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 655 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 656 | "dev": true 657 | }, 658 | "escape-string-regexp": { 659 | "version": "1.0.5", 660 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 661 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 662 | "dev": true 663 | }, 664 | "estree-walker": { 665 | "version": "2.0.2", 666 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 667 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 668 | "dev": true 669 | }, 670 | "fsevents": { 671 | "version": "2.3.2", 672 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 673 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 674 | "dev": true, 675 | "optional": true 676 | }, 677 | "function-bind": { 678 | "version": "1.1.1", 679 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 680 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 681 | "dev": true 682 | }, 683 | "gensync": { 684 | "version": "1.0.0-beta.2", 685 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 686 | "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", 687 | "dev": true 688 | }, 689 | "globals": { 690 | "version": "11.12.0", 691 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 692 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 693 | "dev": true 694 | }, 695 | "has": { 696 | "version": "1.0.3", 697 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 698 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 699 | "dev": true, 700 | "requires": { 701 | "function-bind": "^1.1.1" 702 | } 703 | }, 704 | "has-flag": { 705 | "version": "3.0.0", 706 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 707 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 708 | "dev": true 709 | }, 710 | "hoist-non-react-statics": { 711 | "version": "3.3.2", 712 | "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", 713 | "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", 714 | "requires": { 715 | "react-is": "^16.7.0" 716 | }, 717 | "dependencies": { 718 | "react-is": { 719 | "version": "16.13.1", 720 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 721 | "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" 722 | } 723 | } 724 | }, 725 | "immer": { 726 | "version": "9.0.12", 727 | "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz", 728 | "integrity": "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==" 729 | }, 730 | "is-core-module": { 731 | "version": "2.9.0", 732 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", 733 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", 734 | "dev": true, 735 | "requires": { 736 | "has": "^1.0.3" 737 | } 738 | }, 739 | "js-tokens": { 740 | "version": "4.0.0", 741 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 742 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 743 | }, 744 | "jsesc": { 745 | "version": "2.5.2", 746 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 747 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", 748 | "dev": true 749 | }, 750 | "json5": { 751 | "version": "2.2.1", 752 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", 753 | "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", 754 | "dev": true 755 | }, 756 | "loose-envify": { 757 | "version": "1.4.0", 758 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 759 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 760 | "requires": { 761 | "js-tokens": "^3.0.0 || ^4.0.0" 762 | } 763 | }, 764 | "ms": { 765 | "version": "2.1.2", 766 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 767 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 768 | "dev": true 769 | }, 770 | "nanoid": { 771 | "version": "3.3.3", 772 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", 773 | "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", 774 | "dev": true 775 | }, 776 | "node-releases": { 777 | "version": "2.0.3", 778 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", 779 | "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==", 780 | "dev": true 781 | }, 782 | "path-parse": { 783 | "version": "1.0.7", 784 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 785 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 786 | "dev": true 787 | }, 788 | "picocolors": { 789 | "version": "1.0.0", 790 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 791 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 792 | "dev": true 793 | }, 794 | "picomatch": { 795 | "version": "2.3.1", 796 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 797 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 798 | "dev": true 799 | }, 800 | "postcss": { 801 | "version": "8.4.12", 802 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", 803 | "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", 804 | "dev": true, 805 | "requires": { 806 | "nanoid": "^3.3.1", 807 | "picocolors": "^1.0.0", 808 | "source-map-js": "^1.0.2" 809 | } 810 | }, 811 | "react": { 812 | "version": "18.0.0", 813 | "resolved": "https://registry.npmjs.org/react/-/react-18.0.0.tgz", 814 | "integrity": "sha512-x+VL6wbT4JRVPm7EGxXhZ8w8LTROaxPXOqhlGyVSrv0sB1jkyFGgXxJ8LVoPRLvPR6/CIZGFmfzqUa2NYeMr2A==", 815 | "requires": { 816 | "loose-envify": "^1.1.0" 817 | } 818 | }, 819 | "react-dom": { 820 | "version": "18.0.0", 821 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.0.0.tgz", 822 | "integrity": "sha512-XqX7uzmFo0pUceWFCt7Gff6IyIMzFUn7QMZrbrQfGxtaxXZIcGQzoNpRLE3fQLnS4XzLLPMZX2T9TRcSrasicw==", 823 | "requires": { 824 | "loose-envify": "^1.1.0", 825 | "scheduler": "^0.21.0" 826 | } 827 | }, 828 | "react-is": { 829 | "version": "18.0.0", 830 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.0.0.tgz", 831 | "integrity": "sha512-yUcBYdBBbo3QiPsgYDcfQcIkGZHfxOaoE6HLSnr1sPzMhdyxusbfKOSUbSd/ocGi32dxcj366PsTj+5oggeKKw==" 832 | }, 833 | "react-redux": { 834 | "version": "8.0.1", 835 | "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.1.tgz", 836 | "integrity": "sha512-LMZMsPY4DYdZfLJgd7i79n5Kps5N9XVLCJJeWAaPYTV+Eah2zTuBjTxKtNEbjiyitbq80/eIkm55CYSLqAub3w==", 837 | "requires": { 838 | "@babel/runtime": "^7.12.1", 839 | "@types/hoist-non-react-statics": "^3.3.1", 840 | "@types/use-sync-external-store": "^0.0.3", 841 | "hoist-non-react-statics": "^3.3.2", 842 | "react-is": "^18.0.0", 843 | "use-sync-external-store": "^1.0.0" 844 | } 845 | }, 846 | "react-refresh": { 847 | "version": "0.12.0", 848 | "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.12.0.tgz", 849 | "integrity": "sha512-suLIhrU2IHKL5JEKR/fAwJv7bbeq4kJ+pJopf77jHwuR+HmJS/HbrPIGsTBUVfw7tXPOmYv7UJ7PCaN49e8x4A==", 850 | "dev": true 851 | }, 852 | "redux": { 853 | "version": "4.2.0", 854 | "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", 855 | "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", 856 | "requires": { 857 | "@babel/runtime": "^7.9.2" 858 | } 859 | }, 860 | "redux-thunk": { 861 | "version": "2.4.1", 862 | "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", 863 | "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==" 864 | }, 865 | "regenerator-runtime": { 866 | "version": "0.13.9", 867 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", 868 | "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" 869 | }, 870 | "reselect": { 871 | "version": "4.1.5", 872 | "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz", 873 | "integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==" 874 | }, 875 | "resolve": { 876 | "version": "1.22.0", 877 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", 878 | "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", 879 | "dev": true, 880 | "requires": { 881 | "is-core-module": "^2.8.1", 882 | "path-parse": "^1.0.7", 883 | "supports-preserve-symlinks-flag": "^1.0.0" 884 | } 885 | }, 886 | "rollup": { 887 | "version": "2.70.2", 888 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.2.tgz", 889 | "integrity": "sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==", 890 | "dev": true, 891 | "requires": { 892 | "fsevents": "~2.3.2" 893 | } 894 | }, 895 | "safe-buffer": { 896 | "version": "5.1.2", 897 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 898 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 899 | "dev": true 900 | }, 901 | "scheduler": { 902 | "version": "0.21.0", 903 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", 904 | "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", 905 | "requires": { 906 | "loose-envify": "^1.1.0" 907 | } 908 | }, 909 | "semver": { 910 | "version": "6.3.0", 911 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 912 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 913 | "dev": true 914 | }, 915 | "source-map": { 916 | "version": "0.5.7", 917 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 918 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 919 | "dev": true 920 | }, 921 | "source-map-js": { 922 | "version": "1.0.2", 923 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 924 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 925 | "dev": true 926 | }, 927 | "supports-color": { 928 | "version": "5.5.0", 929 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 930 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 931 | "dev": true, 932 | "requires": { 933 | "has-flag": "^3.0.0" 934 | } 935 | }, 936 | "supports-preserve-symlinks-flag": { 937 | "version": "1.0.0", 938 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 939 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 940 | "dev": true 941 | }, 942 | "to-fast-properties": { 943 | "version": "2.0.0", 944 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 945 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", 946 | "dev": true 947 | }, 948 | "typescript": { 949 | "version": "4.6.3", 950 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", 951 | "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", 952 | "dev": true 953 | }, 954 | "use-sync-external-store": { 955 | "version": "1.0.0", 956 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.0.0.tgz", 957 | "integrity": "sha512-AFVsxg5GkFg8GDcxnl+Z0lMAz9rE8DGJCc28qnBuQF7lac57B5smLcT37aXpXIIPz75rW4g3eXHPjhHwdGskOw==" 958 | }, 959 | "vite": { 960 | "version": "2.9.5", 961 | "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.5.tgz", 962 | "integrity": "sha512-dvMN64X2YEQgSXF1lYabKXw3BbN6e+BL67+P3Vy4MacnY+UzT1AfkHiioFSi9+uiDUiaDy7Ax/LQqivk6orilg==", 963 | "dev": true, 964 | "requires": { 965 | "esbuild": "^0.14.27", 966 | "fsevents": "~2.3.2", 967 | "postcss": "^8.4.12", 968 | "resolve": "^1.22.0", 969 | "rollup": "^2.59.0" 970 | } 971 | } 972 | } 973 | } 974 | -------------------------------------------------------------------------------- /typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-react-template-redux", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "tsc && vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@reduxjs/toolkit": "^1.8.1", 12 | "react": "^18.0.0", 13 | "react-dom": "^18.0.0", 14 | "react-redux": "^8.0.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.0", 18 | "@types/react-dom": "^18.0.0", 19 | "@vitejs/plugin-react": "^1.3.0", 20 | "typescript": "^4.6.3", 21 | "vite": "^2.9.5" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /typescript/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-float infinite 3s ease-in-out; 13 | } 14 | } 15 | 16 | .App-header { 17 | min-height: 100vh; 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | justify-content: center; 22 | font-size: calc(10px + 2vmin); 23 | } 24 | 25 | .App-link { 26 | color: rgb(112, 76, 182); 27 | } 28 | 29 | @keyframes App-logo-float { 30 | 0% { 31 | transform: translateY(0); 32 | } 33 | 50% { 34 | transform: translateY(10px); 35 | } 36 | 100% { 37 | transform: translateY(0px); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /typescript/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import logo from "./logo.svg"; 3 | import { Counter } from "./features/counter/Counter"; 4 | import "./App.css"; 5 | 6 | function App() { 7 | return ( 8 |
9 |
10 | logo 11 | 12 |

13 | Edit src/App.tsx and save to reload. 14 |

15 | 16 | Learn 17 | 23 | React 24 | 25 | , 26 | 32 | Redux 33 | 34 | , 35 | 41 | Redux Toolkit 42 | 43 | , and 44 | 50 | React Redux 51 | 52 | 53 |
54 |
55 | ); 56 | } 57 | 58 | export default App; 59 | -------------------------------------------------------------------------------- /typescript/src/app/hooks.ts: -------------------------------------------------------------------------------- 1 | import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; 2 | import type { RootState, AppDispatch } from "./store"; 3 | 4 | // Use throughout your app instead of plain `useDispatch` and `useSelector` 5 | export const useAppDispatch = () => useDispatch(); 6 | export const useAppSelector: TypedUseSelectorHook = useSelector; 7 | -------------------------------------------------------------------------------- /typescript/src/app/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit"; 2 | import counterReducer from "../features/counter/counterSlice"; 3 | 4 | export const store = configureStore({ 5 | reducer: { 6 | counter: counterReducer, 7 | }, 8 | }); 9 | 10 | export type AppDispatch = typeof store.dispatch; 11 | export type RootState = ReturnType; 12 | export type AppThunk = ThunkAction< 13 | ReturnType, 14 | RootState, 15 | unknown, 16 | Action 17 | >; 18 | -------------------------------------------------------------------------------- /typescript/src/features/counter/Counter.module.css: -------------------------------------------------------------------------------- 1 | .row { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | } 6 | 7 | .row > button { 8 | margin-left: 4px; 9 | margin-right: 8px; 10 | } 11 | 12 | .row:not(:last-child) { 13 | margin-bottom: 16px; 14 | } 15 | 16 | .value { 17 | font-size: 78px; 18 | padding-left: 16px; 19 | padding-right: 16px; 20 | margin-top: 2px; 21 | font-family: "Courier New", Courier, monospace; 22 | } 23 | 24 | .button { 25 | appearance: none; 26 | background: none; 27 | font-size: 32px; 28 | padding-left: 12px; 29 | padding-right: 12px; 30 | outline: none; 31 | border: 2px solid transparent; 32 | color: rgb(112, 76, 182); 33 | padding-bottom: 4px; 34 | cursor: pointer; 35 | background-color: rgba(112, 76, 182, 0.1); 36 | border-radius: 2px; 37 | transition: all 0.15s; 38 | } 39 | 40 | .textbox { 41 | font-size: 32px; 42 | padding: 2px; 43 | width: 64px; 44 | text-align: center; 45 | margin-right: 4px; 46 | } 47 | 48 | .button:hover, 49 | .button:focus { 50 | border: 2px solid rgba(112, 76, 182, 0.4); 51 | } 52 | 53 | .button:active { 54 | background-color: rgba(112, 76, 182, 0.2); 55 | } 56 | 57 | .asyncButton { 58 | composes: button; 59 | position: relative; 60 | } 61 | 62 | .asyncButton:after { 63 | content: ""; 64 | background-color: rgba(112, 76, 182, 0.15); 65 | display: block; 66 | position: absolute; 67 | width: 100%; 68 | height: 100%; 69 | left: 0; 70 | top: 0; 71 | opacity: 0; 72 | transition: width 1s linear, opacity 0.5s ease 1s; 73 | } 74 | 75 | .asyncButton:active:after { 76 | width: 0%; 77 | opacity: 1; 78 | transition: 0s; 79 | } 80 | -------------------------------------------------------------------------------- /typescript/src/features/counter/Counter.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | import { useAppSelector, useAppDispatch } from "../../app/hooks"; 4 | import { 5 | decrement, 6 | increment, 7 | incrementByAmount, 8 | incrementAsync, 9 | incrementIfOdd, 10 | selectCount, 11 | } from "./counterSlice"; 12 | import styles from "./Counter.module.css"; 13 | 14 | export function Counter() { 15 | const count = useAppSelector(selectCount); 16 | const dispatch = useAppDispatch(); 17 | const [incrementAmount, setIncrementAmount] = useState("2"); 18 | 19 | const incrementValue = Number(incrementAmount) || 0; 20 | 21 | return ( 22 |
23 |
24 | 31 | {count} 32 | 39 |
40 |
41 | setIncrementAmount(e.target.value)} 46 | /> 47 | 53 | 59 | 65 |
66 |
67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /typescript/src/features/counter/counterAPI.ts: -------------------------------------------------------------------------------- 1 | // A mock function to mimic making an async request for data 2 | export function fetchCount(amount = 1) { 3 | return new Promise<{ data: number }>((resolve) => 4 | setTimeout(() => resolve({ data: amount }), 500) 5 | ); 6 | } 7 | -------------------------------------------------------------------------------- /typescript/src/features/counter/counterSlice.ts: -------------------------------------------------------------------------------- 1 | import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit"; 2 | import { RootState, AppThunk } from "../../app/store"; 3 | import { fetchCount } from "./counterAPI"; 4 | 5 | export interface CounterState { 6 | value: number; 7 | status: "idle" | "loading" | "failed"; 8 | } 9 | 10 | const initialState: CounterState = { 11 | value: 0, 12 | status: "idle", 13 | }; 14 | 15 | // The function below is called a thunk and allows us to perform async logic. It 16 | // can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This 17 | // will call the thunk with the `dispatch` function as the first argument. Async 18 | // code can then be executed and other actions can be dispatched. Thunks are 19 | // typically used to make async requests. 20 | export const incrementAsync = createAsyncThunk( 21 | "counter/fetchCount", 22 | async (amount: number) => { 23 | const response = await fetchCount(amount); 24 | // The value we return becomes the `fulfilled` action payload 25 | return response.data; 26 | } 27 | ); 28 | 29 | export const counterSlice = createSlice({ 30 | name: "counter", 31 | initialState, 32 | // The `reducers` field lets us define reducers and generate associated actions 33 | reducers: { 34 | increment: (state) => { 35 | // Redux Toolkit allows us to write "mutating" logic in reducers. It 36 | // doesn't actually mutate the state because it uses the Immer library, 37 | // which detects changes to a "draft state" and produces a brand new 38 | // immutable state based off those changes 39 | state.value += 1; 40 | }, 41 | decrement: (state) => { 42 | state.value -= 1; 43 | }, 44 | // Use the PayloadAction type to declare the contents of `action.payload` 45 | incrementByAmount: (state, action: PayloadAction) => { 46 | state.value += action.payload; 47 | }, 48 | }, 49 | // The `extraReducers` field lets the slice handle actions defined elsewhere, 50 | // including actions generated by createAsyncThunk or in other slices. 51 | extraReducers: (builder) => { 52 | builder 53 | .addCase(incrementAsync.pending, (state) => { 54 | state.status = "loading"; 55 | }) 56 | .addCase(incrementAsync.fulfilled, (state, action) => { 57 | state.status = "idle"; 58 | state.value += action.payload; 59 | }) 60 | .addCase(incrementAsync.rejected, (state) => { 61 | state.status = "failed"; 62 | }); 63 | }, 64 | }); 65 | 66 | export const { increment, decrement, incrementByAmount } = counterSlice.actions; 67 | 68 | // The function below is called a selector and allows us to select a value from 69 | // the state. Selectors can also be defined inline where they're used instead of 70 | // in the slice file. For example: `useSelector((state: RootState) => state.counter.value)` 71 | export const selectCount = (state: RootState) => state.counter.value; 72 | 73 | // We can also write thunks by hand, which may contain both sync and async logic. 74 | // Here's an example of conditionally dispatching actions based on current state. 75 | export const incrementIfOdd = 76 | (amount: number): AppThunk => 77 | (dispatch, getState) => { 78 | const currentValue = selectCount(getState()); 79 | if (currentValue % 2 === 1) { 80 | dispatch(incrementByAmount(amount)); 81 | } 82 | }; 83 | 84 | export default counterSlice.reducer; 85 | -------------------------------------------------------------------------------- /typescript/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /typescript/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /typescript/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { Provider } from "react-redux"; 4 | import { store } from "./app/store"; 5 | import App from "./App"; 6 | import "./index.css"; 7 | 8 | ReactDOM.createRoot(document.getElementById("root")!).render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /typescript/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /typescript/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /typescript/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | --------------------------------------------------------------------------------