├── .editorconfig ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── index.html ├── package-lock.json ├── package.json ├── public └── ogp.png ├── src ├── App.tsx ├── Routes.tsx ├── const.ts ├── index.css ├── main.tsx ├── pages │ ├── QuestionPage │ │ ├── QuestionPage.module.css │ │ ├── QuizPage │ │ │ └── index.tsx │ │ ├── TutorialPage │ │ │ └── index.tsx │ │ ├── components │ │ │ ├── GridArea.module.css │ │ │ ├── GridArea.tsx │ │ │ ├── GridAreaExtensionControl.module.css │ │ │ ├── GridAreaExtensionControl.tsx │ │ │ └── assets │ │ │ │ └── plus.png │ │ ├── hooks │ │ │ └── useNextPage.ts │ │ ├── index.tsx │ │ └── logic │ │ │ ├── useGridExtension.ts │ │ │ ├── useGridItemSelection.ts │ │ │ └── useQuizPageLogic.tsx │ └── TopPage │ │ ├── TopPage.module.css │ │ ├── index.tsx │ │ └── logo.png ├── questions │ ├── GridPosition.ts │ ├── QuestionData.ts │ ├── loadQuestion.ts │ └── v1 │ │ ├── 1.tsx │ │ ├── 10.tsx │ │ ├── 11.tsx │ │ ├── 12.tsx │ │ ├── 13.tsx │ │ ├── 14.tsx │ │ ├── 15.tsx │ │ ├── 16.tsx │ │ ├── 17.tsx │ │ ├── 18.tsx │ │ ├── 19.tsx │ │ ├── 2.tsx │ │ ├── 20.tsx │ │ ├── 21.tsx │ │ ├── 22.tsx │ │ ├── 23.tsx │ │ ├── 24.tsx │ │ ├── 25.tsx │ │ ├── 26.tsx │ │ ├── 27.tsx │ │ ├── 28.tsx │ │ ├── 29.tsx │ │ ├── 3.tsx │ │ ├── 30.tsx │ │ ├── 31.tsx │ │ ├── 32.tsx │ │ ├── 33.tsx │ │ ├── 34.tsx │ │ ├── 35.tsx │ │ ├── 36.tsx │ │ ├── 37.tsx │ │ ├── 38.tsx │ │ ├── 39.tsx │ │ ├── 4.tsx │ │ ├── 40.tsx │ │ ├── 41.tsx │ │ ├── 42.tsx │ │ ├── 43.tsx │ │ ├── 44.tsx │ │ ├── 45.tsx │ │ ├── 46.tsx │ │ ├── 47.tsx │ │ ├── 48.tsx │ │ ├── 49.tsx │ │ ├── 5.tsx │ │ ├── 50.tsx │ │ ├── 51.tsx │ │ ├── 52.tsx │ │ ├── 53.tsx │ │ ├── 6.tsx │ │ ├── 7.tsx │ │ ├── 8.tsx │ │ ├── 9.tsx │ │ └── tutorial │ │ ├── Congratulations.tsx │ │ ├── Tutorial.module.css │ │ ├── Tutorial1.tsx │ │ ├── Tutorial2.tsx │ │ └── Tutorial3.tsx ├── utils │ ├── arrayShallowEqual.ts │ ├── hooks │ │ └── useStateReset.ts │ ├── i18n │ │ ├── DefineLanguageRoute.tsx │ │ ├── LanguageContext.tsx │ │ └── language.ts │ ├── indent.ts │ ├── range.ts │ └── simpleParseCss.ts └── vite-env.d.ts ├── tsconfig.json └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | indent_style = space -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.organizeImports": true 4 | } 5 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 23 | CSS Grid Mastery Quiz 24 | 25 | 26 |
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-grid-quiz", 3 | "version": "0.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "css-grid-quiz", 9 | "version": "0.0.0", 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-location": "^3.3.3" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "prettier": "^2.5.1", 20 | "typescript": "^4.5.4", 21 | "vite": "^2.7.2" 22 | } 23 | }, 24 | "node_modules/@babel/code-frame": { 25 | "version": "7.16.7", 26 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", 27 | "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", 28 | "dev": true, 29 | "dependencies": { 30 | "@babel/highlight": "^7.16.7" 31 | }, 32 | "engines": { 33 | "node": ">=6.9.0" 34 | } 35 | }, 36 | "node_modules/@babel/compat-data": { 37 | "version": "7.16.8", 38 | "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", 39 | "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", 40 | "dev": true, 41 | "engines": { 42 | "node": ">=6.9.0" 43 | } 44 | }, 45 | "node_modules/@babel/core": { 46 | "version": "7.16.7", 47 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", 48 | "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", 49 | "dev": true, 50 | "dependencies": { 51 | "@babel/code-frame": "^7.16.7", 52 | "@babel/generator": "^7.16.7", 53 | "@babel/helper-compilation-targets": "^7.16.7", 54 | "@babel/helper-module-transforms": "^7.16.7", 55 | "@babel/helpers": "^7.16.7", 56 | "@babel/parser": "^7.16.7", 57 | "@babel/template": "^7.16.7", 58 | "@babel/traverse": "^7.16.7", 59 | "@babel/types": "^7.16.7", 60 | "convert-source-map": "^1.7.0", 61 | "debug": "^4.1.0", 62 | "gensync": "^1.0.0-beta.2", 63 | "json5": "^2.1.2", 64 | "semver": "^6.3.0", 65 | "source-map": "^0.5.0" 66 | }, 67 | "engines": { 68 | "node": ">=6.9.0" 69 | }, 70 | "funding": { 71 | "type": "opencollective", 72 | "url": "https://opencollective.com/babel" 73 | } 74 | }, 75 | "node_modules/@babel/generator": { 76 | "version": "7.16.8", 77 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", 78 | "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", 79 | "dependencies": { 80 | "@babel/types": "^7.16.8", 81 | "jsesc": "^2.5.1", 82 | "source-map": "^0.5.0" 83 | }, 84 | "engines": { 85 | "node": ">=6.9.0" 86 | } 87 | }, 88 | "node_modules/@babel/helper-annotate-as-pure": { 89 | "version": "7.16.7", 90 | "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", 91 | "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", 92 | "dev": true, 93 | "dependencies": { 94 | "@babel/types": "^7.16.7" 95 | }, 96 | "engines": { 97 | "node": ">=6.9.0" 98 | } 99 | }, 100 | "node_modules/@babel/helper-compilation-targets": { 101 | "version": "7.16.7", 102 | "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", 103 | "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", 104 | "dev": true, 105 | "dependencies": { 106 | "@babel/compat-data": "^7.16.4", 107 | "@babel/helper-validator-option": "^7.16.7", 108 | "browserslist": "^4.17.5", 109 | "semver": "^6.3.0" 110 | }, 111 | "engines": { 112 | "node": ">=6.9.0" 113 | }, 114 | "peerDependencies": { 115 | "@babel/core": "^7.0.0" 116 | } 117 | }, 118 | "node_modules/@babel/helper-environment-visitor": { 119 | "version": "7.16.7", 120 | "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", 121 | "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", 122 | "dev": true, 123 | "dependencies": { 124 | "@babel/types": "^7.16.7" 125 | }, 126 | "engines": { 127 | "node": ">=6.9.0" 128 | } 129 | }, 130 | "node_modules/@babel/helper-function-name": { 131 | "version": "7.16.7", 132 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", 133 | "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", 134 | "dev": true, 135 | "dependencies": { 136 | "@babel/helper-get-function-arity": "^7.16.7", 137 | "@babel/template": "^7.16.7", 138 | "@babel/types": "^7.16.7" 139 | }, 140 | "engines": { 141 | "node": ">=6.9.0" 142 | } 143 | }, 144 | "node_modules/@babel/helper-get-function-arity": { 145 | "version": "7.16.7", 146 | "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", 147 | "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", 148 | "dev": true, 149 | "dependencies": { 150 | "@babel/types": "^7.16.7" 151 | }, 152 | "engines": { 153 | "node": ">=6.9.0" 154 | } 155 | }, 156 | "node_modules/@babel/helper-hoist-variables": { 157 | "version": "7.16.7", 158 | "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", 159 | "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", 160 | "dev": true, 161 | "dependencies": { 162 | "@babel/types": "^7.16.7" 163 | }, 164 | "engines": { 165 | "node": ">=6.9.0" 166 | } 167 | }, 168 | "node_modules/@babel/helper-module-imports": { 169 | "version": "7.16.7", 170 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", 171 | "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", 172 | "dependencies": { 173 | "@babel/types": "^7.16.7" 174 | }, 175 | "engines": { 176 | "node": ">=6.9.0" 177 | } 178 | }, 179 | "node_modules/@babel/helper-module-transforms": { 180 | "version": "7.16.7", 181 | "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", 182 | "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", 183 | "dev": true, 184 | "dependencies": { 185 | "@babel/helper-environment-visitor": "^7.16.7", 186 | "@babel/helper-module-imports": "^7.16.7", 187 | "@babel/helper-simple-access": "^7.16.7", 188 | "@babel/helper-split-export-declaration": "^7.16.7", 189 | "@babel/helper-validator-identifier": "^7.16.7", 190 | "@babel/template": "^7.16.7", 191 | "@babel/traverse": "^7.16.7", 192 | "@babel/types": "^7.16.7" 193 | }, 194 | "engines": { 195 | "node": ">=6.9.0" 196 | } 197 | }, 198 | "node_modules/@babel/helper-plugin-utils": { 199 | "version": "7.16.7", 200 | "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", 201 | "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", 202 | "dev": true, 203 | "engines": { 204 | "node": ">=6.9.0" 205 | } 206 | }, 207 | "node_modules/@babel/helper-simple-access": { 208 | "version": "7.16.7", 209 | "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", 210 | "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", 211 | "dev": true, 212 | "dependencies": { 213 | "@babel/types": "^7.16.7" 214 | }, 215 | "engines": { 216 | "node": ">=6.9.0" 217 | } 218 | }, 219 | "node_modules/@babel/helper-split-export-declaration": { 220 | "version": "7.16.7", 221 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", 222 | "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", 223 | "dev": true, 224 | "dependencies": { 225 | "@babel/types": "^7.16.7" 226 | }, 227 | "engines": { 228 | "node": ">=6.9.0" 229 | } 230 | }, 231 | "node_modules/@babel/helper-validator-identifier": { 232 | "version": "7.16.7", 233 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", 234 | "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", 235 | "engines": { 236 | "node": ">=6.9.0" 237 | } 238 | }, 239 | "node_modules/@babel/helper-validator-option": { 240 | "version": "7.16.7", 241 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", 242 | "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", 243 | "dev": true, 244 | "engines": { 245 | "node": ">=6.9.0" 246 | } 247 | }, 248 | "node_modules/@babel/helpers": { 249 | "version": "7.16.7", 250 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", 251 | "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", 252 | "dev": true, 253 | "dependencies": { 254 | "@babel/template": "^7.16.7", 255 | "@babel/traverse": "^7.16.7", 256 | "@babel/types": "^7.16.7" 257 | }, 258 | "engines": { 259 | "node": ">=6.9.0" 260 | } 261 | }, 262 | "node_modules/@babel/highlight": { 263 | "version": "7.16.7", 264 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", 265 | "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", 266 | "dev": true, 267 | "dependencies": { 268 | "@babel/helper-validator-identifier": "^7.16.7", 269 | "chalk": "^2.0.0", 270 | "js-tokens": "^4.0.0" 271 | }, 272 | "engines": { 273 | "node": ">=6.9.0" 274 | } 275 | }, 276 | "node_modules/@babel/parser": { 277 | "version": "7.16.8", 278 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", 279 | "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", 280 | "dev": true, 281 | "bin": { 282 | "parser": "bin/babel-parser.js" 283 | }, 284 | "engines": { 285 | "node": ">=6.0.0" 286 | } 287 | }, 288 | "node_modules/@babel/plugin-syntax-jsx": { 289 | "version": "7.16.7", 290 | "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", 291 | "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", 292 | "dev": true, 293 | "dependencies": { 294 | "@babel/helper-plugin-utils": "^7.16.7" 295 | }, 296 | "engines": { 297 | "node": ">=6.9.0" 298 | }, 299 | "peerDependencies": { 300 | "@babel/core": "^7.0.0-0" 301 | } 302 | }, 303 | "node_modules/@babel/plugin-transform-react-jsx": { 304 | "version": "7.16.7", 305 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz", 306 | "integrity": "sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag==", 307 | "dev": true, 308 | "dependencies": { 309 | "@babel/helper-annotate-as-pure": "^7.16.7", 310 | "@babel/helper-module-imports": "^7.16.7", 311 | "@babel/helper-plugin-utils": "^7.16.7", 312 | "@babel/plugin-syntax-jsx": "^7.16.7", 313 | "@babel/types": "^7.16.7" 314 | }, 315 | "engines": { 316 | "node": ">=6.9.0" 317 | }, 318 | "peerDependencies": { 319 | "@babel/core": "^7.0.0-0" 320 | } 321 | }, 322 | "node_modules/@babel/plugin-transform-react-jsx-development": { 323 | "version": "7.16.7", 324 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz", 325 | "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==", 326 | "dev": true, 327 | "dependencies": { 328 | "@babel/plugin-transform-react-jsx": "^7.16.7" 329 | }, 330 | "engines": { 331 | "node": ">=6.9.0" 332 | }, 333 | "peerDependencies": { 334 | "@babel/core": "^7.0.0-0" 335 | } 336 | }, 337 | "node_modules/@babel/plugin-transform-react-jsx-self": { 338 | "version": "7.16.7", 339 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz", 340 | "integrity": "sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA==", 341 | "dev": true, 342 | "dependencies": { 343 | "@babel/helper-plugin-utils": "^7.16.7" 344 | }, 345 | "engines": { 346 | "node": ">=6.9.0" 347 | }, 348 | "peerDependencies": { 349 | "@babel/core": "^7.0.0-0" 350 | } 351 | }, 352 | "node_modules/@babel/plugin-transform-react-jsx-source": { 353 | "version": "7.16.7", 354 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz", 355 | "integrity": "sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw==", 356 | "dev": true, 357 | "dependencies": { 358 | "@babel/helper-plugin-utils": "^7.16.7" 359 | }, 360 | "engines": { 361 | "node": ">=6.9.0" 362 | }, 363 | "peerDependencies": { 364 | "@babel/core": "^7.0.0-0" 365 | } 366 | }, 367 | "node_modules/@babel/runtime": { 368 | "version": "7.16.7", 369 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", 370 | "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", 371 | "dependencies": { 372 | "regenerator-runtime": "^0.13.4" 373 | }, 374 | "engines": { 375 | "node": ">=6.9.0" 376 | } 377 | }, 378 | "node_modules/@babel/template": { 379 | "version": "7.16.7", 380 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", 381 | "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", 382 | "dev": true, 383 | "dependencies": { 384 | "@babel/code-frame": "^7.16.7", 385 | "@babel/parser": "^7.16.7", 386 | "@babel/types": "^7.16.7" 387 | }, 388 | "engines": { 389 | "node": ">=6.9.0" 390 | } 391 | }, 392 | "node_modules/@babel/traverse": { 393 | "version": "7.16.8", 394 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", 395 | "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", 396 | "dev": true, 397 | "dependencies": { 398 | "@babel/code-frame": "^7.16.7", 399 | "@babel/generator": "^7.16.8", 400 | "@babel/helper-environment-visitor": "^7.16.7", 401 | "@babel/helper-function-name": "^7.16.7", 402 | "@babel/helper-hoist-variables": "^7.16.7", 403 | "@babel/helper-split-export-declaration": "^7.16.7", 404 | "@babel/parser": "^7.16.8", 405 | "@babel/types": "^7.16.8", 406 | "debug": "^4.1.0", 407 | "globals": "^11.1.0" 408 | }, 409 | "engines": { 410 | "node": ">=6.9.0" 411 | } 412 | }, 413 | "node_modules/@babel/types": { 414 | "version": "7.16.8", 415 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", 416 | "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", 417 | "dependencies": { 418 | "@babel/helper-validator-identifier": "^7.16.7", 419 | "to-fast-properties": "^2.0.0" 420 | }, 421 | "engines": { 422 | "node": ">=6.9.0" 423 | } 424 | }, 425 | "node_modules/@rollup/pluginutils": { 426 | "version": "4.1.2", 427 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz", 428 | "integrity": "sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ==", 429 | "dev": true, 430 | "dependencies": { 431 | "estree-walker": "^2.0.1", 432 | "picomatch": "^2.2.2" 433 | }, 434 | "engines": { 435 | "node": ">= 8.0.0" 436 | } 437 | }, 438 | "node_modules/@types/prop-types": { 439 | "version": "15.7.4", 440 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", 441 | "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", 442 | "dev": true 443 | }, 444 | "node_modules/@types/react": { 445 | "version": "17.0.38", 446 | "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", 447 | "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", 448 | "dev": true, 449 | "dependencies": { 450 | "@types/prop-types": "*", 451 | "@types/scheduler": "*", 452 | "csstype": "^3.0.2" 453 | } 454 | }, 455 | "node_modules/@types/react-dom": { 456 | "version": "17.0.11", 457 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", 458 | "integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==", 459 | "dev": true, 460 | "dependencies": { 461 | "@types/react": "*" 462 | } 463 | }, 464 | "node_modules/@types/scheduler": { 465 | "version": "0.16.2", 466 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", 467 | "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", 468 | "dev": true 469 | }, 470 | "node_modules/@vitejs/plugin-react": { 471 | "version": "1.1.4", 472 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.1.4.tgz", 473 | "integrity": "sha512-cMUBDonNY8PPeHWjIrYKbRn6bLSunh/Ixo2XLLBd3DM0uYBZft+c+04zkGhhN1lAwvoRKJ2FdtvhGhPgViHc6w==", 474 | "dev": true, 475 | "dependencies": { 476 | "@babel/core": "^7.16.5", 477 | "@babel/plugin-transform-react-jsx": "^7.16.5", 478 | "@babel/plugin-transform-react-jsx-development": "^7.16.5", 479 | "@babel/plugin-transform-react-jsx-self": "^7.16.5", 480 | "@babel/plugin-transform-react-jsx-source": "^7.16.5", 481 | "@rollup/pluginutils": "^4.1.2", 482 | "react-refresh": "^0.11.0", 483 | "resolve": "^1.20.0" 484 | }, 485 | "engines": { 486 | "node": ">=12.0.0" 487 | } 488 | }, 489 | "node_modules/ansi-styles": { 490 | "version": "3.2.1", 491 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 492 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 493 | "dev": true, 494 | "dependencies": { 495 | "color-convert": "^1.9.0" 496 | }, 497 | "engines": { 498 | "node": ">=4" 499 | } 500 | }, 501 | "node_modules/babylon": { 502 | "version": "7.0.0-beta.47", 503 | "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz", 504 | "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==", 505 | "bin": { 506 | "babylon": "bin/babylon.js" 507 | }, 508 | "engines": { 509 | "node": ">=6.0.0" 510 | } 511 | }, 512 | "node_modules/browserslist": { 513 | "version": "4.19.1", 514 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", 515 | "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", 516 | "dev": true, 517 | "dependencies": { 518 | "caniuse-lite": "^1.0.30001286", 519 | "electron-to-chromium": "^1.4.17", 520 | "escalade": "^3.1.1", 521 | "node-releases": "^2.0.1", 522 | "picocolors": "^1.0.0" 523 | }, 524 | "bin": { 525 | "browserslist": "cli.js" 526 | }, 527 | "engines": { 528 | "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 529 | }, 530 | "funding": { 531 | "type": "opencollective", 532 | "url": "https://opencollective.com/browserslist" 533 | } 534 | }, 535 | "node_modules/caniuse-lite": { 536 | "version": "1.0.30001300", 537 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz", 538 | "integrity": "sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==", 539 | "dev": true, 540 | "funding": { 541 | "type": "opencollective", 542 | "url": "https://opencollective.com/browserslist" 543 | } 544 | }, 545 | "node_modules/chalk": { 546 | "version": "2.4.2", 547 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 548 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 549 | "dev": true, 550 | "dependencies": { 551 | "ansi-styles": "^3.2.1", 552 | "escape-string-regexp": "^1.0.5", 553 | "supports-color": "^5.3.0" 554 | }, 555 | "engines": { 556 | "node": ">=4" 557 | } 558 | }, 559 | "node_modules/color-convert": { 560 | "version": "1.9.3", 561 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 562 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 563 | "dev": true, 564 | "dependencies": { 565 | "color-name": "1.1.3" 566 | } 567 | }, 568 | "node_modules/color-name": { 569 | "version": "1.1.3", 570 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 571 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 572 | "dev": true 573 | }, 574 | "node_modules/convert-source-map": { 575 | "version": "1.8.0", 576 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", 577 | "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", 578 | "dev": true, 579 | "dependencies": { 580 | "safe-buffer": "~5.1.1" 581 | } 582 | }, 583 | "node_modules/core-js": { 584 | "version": "3.20.3", 585 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", 586 | "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", 587 | "hasInstallScript": true, 588 | "funding": { 589 | "type": "opencollective", 590 | "url": "https://opencollective.com/core-js" 591 | } 592 | }, 593 | "node_modules/csstype": { 594 | "version": "3.0.10", 595 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", 596 | "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", 597 | "dev": true 598 | }, 599 | "node_modules/debug": { 600 | "version": "4.3.3", 601 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 602 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 603 | "dev": true, 604 | "dependencies": { 605 | "ms": "2.1.2" 606 | }, 607 | "engines": { 608 | "node": ">=6.0" 609 | }, 610 | "peerDependenciesMeta": { 611 | "supports-color": { 612 | "optional": true 613 | } 614 | } 615 | }, 616 | "node_modules/electron-to-chromium": { 617 | "version": "1.4.48", 618 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.48.tgz", 619 | "integrity": "sha512-RT3SEmpv7XUA+tKXrZGudAWLDpa7f8qmhjcLaM6OD/ERxjQ/zAojT8/Vvo0BSzbArkElFZ1WyZ9FuwAYbkdBNA==", 620 | "dev": true 621 | }, 622 | "node_modules/esbuild": { 623 | "version": "0.13.15", 624 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz", 625 | "integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==", 626 | "dev": true, 627 | "hasInstallScript": true, 628 | "bin": { 629 | "esbuild": "bin/esbuild" 630 | }, 631 | "optionalDependencies": { 632 | "esbuild-android-arm64": "0.13.15", 633 | "esbuild-darwin-64": "0.13.15", 634 | "esbuild-darwin-arm64": "0.13.15", 635 | "esbuild-freebsd-64": "0.13.15", 636 | "esbuild-freebsd-arm64": "0.13.15", 637 | "esbuild-linux-32": "0.13.15", 638 | "esbuild-linux-64": "0.13.15", 639 | "esbuild-linux-arm": "0.13.15", 640 | "esbuild-linux-arm64": "0.13.15", 641 | "esbuild-linux-mips64le": "0.13.15", 642 | "esbuild-linux-ppc64le": "0.13.15", 643 | "esbuild-netbsd-64": "0.13.15", 644 | "esbuild-openbsd-64": "0.13.15", 645 | "esbuild-sunos-64": "0.13.15", 646 | "esbuild-windows-32": "0.13.15", 647 | "esbuild-windows-64": "0.13.15", 648 | "esbuild-windows-arm64": "0.13.15" 649 | } 650 | }, 651 | "node_modules/esbuild-android-arm64": { 652 | "version": "0.13.15", 653 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz", 654 | "integrity": "sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==", 655 | "cpu": [ 656 | "arm64" 657 | ], 658 | "dev": true, 659 | "optional": true, 660 | "os": [ 661 | "android" 662 | ] 663 | }, 664 | "node_modules/esbuild-darwin-64": { 665 | "version": "0.13.15", 666 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz", 667 | "integrity": "sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==", 668 | "cpu": [ 669 | "x64" 670 | ], 671 | "dev": true, 672 | "optional": true, 673 | "os": [ 674 | "darwin" 675 | ] 676 | }, 677 | "node_modules/esbuild-darwin-arm64": { 678 | "version": "0.13.15", 679 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz", 680 | "integrity": "sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==", 681 | "cpu": [ 682 | "arm64" 683 | ], 684 | "dev": true, 685 | "optional": true, 686 | "os": [ 687 | "darwin" 688 | ] 689 | }, 690 | "node_modules/esbuild-freebsd-64": { 691 | "version": "0.13.15", 692 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz", 693 | "integrity": "sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==", 694 | "cpu": [ 695 | "x64" 696 | ], 697 | "dev": true, 698 | "optional": true, 699 | "os": [ 700 | "freebsd" 701 | ] 702 | }, 703 | "node_modules/esbuild-freebsd-arm64": { 704 | "version": "0.13.15", 705 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz", 706 | "integrity": "sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==", 707 | "cpu": [ 708 | "arm64" 709 | ], 710 | "dev": true, 711 | "optional": true, 712 | "os": [ 713 | "freebsd" 714 | ] 715 | }, 716 | "node_modules/esbuild-linux-32": { 717 | "version": "0.13.15", 718 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz", 719 | "integrity": "sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==", 720 | "cpu": [ 721 | "ia32" 722 | ], 723 | "dev": true, 724 | "optional": true, 725 | "os": [ 726 | "linux" 727 | ] 728 | }, 729 | "node_modules/esbuild-linux-64": { 730 | "version": "0.13.15", 731 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz", 732 | "integrity": "sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==", 733 | "cpu": [ 734 | "x64" 735 | ], 736 | "dev": true, 737 | "optional": true, 738 | "os": [ 739 | "linux" 740 | ] 741 | }, 742 | "node_modules/esbuild-linux-arm": { 743 | "version": "0.13.15", 744 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz", 745 | "integrity": "sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==", 746 | "cpu": [ 747 | "arm" 748 | ], 749 | "dev": true, 750 | "optional": true, 751 | "os": [ 752 | "linux" 753 | ] 754 | }, 755 | "node_modules/esbuild-linux-arm64": { 756 | "version": "0.13.15", 757 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz", 758 | "integrity": "sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==", 759 | "cpu": [ 760 | "arm64" 761 | ], 762 | "dev": true, 763 | "optional": true, 764 | "os": [ 765 | "linux" 766 | ] 767 | }, 768 | "node_modules/esbuild-linux-mips64le": { 769 | "version": "0.13.15", 770 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz", 771 | "integrity": "sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==", 772 | "cpu": [ 773 | "mips64el" 774 | ], 775 | "dev": true, 776 | "optional": true, 777 | "os": [ 778 | "linux" 779 | ] 780 | }, 781 | "node_modules/esbuild-linux-ppc64le": { 782 | "version": "0.13.15", 783 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz", 784 | "integrity": "sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==", 785 | "cpu": [ 786 | "ppc64" 787 | ], 788 | "dev": true, 789 | "optional": true, 790 | "os": [ 791 | "linux" 792 | ] 793 | }, 794 | "node_modules/esbuild-netbsd-64": { 795 | "version": "0.13.15", 796 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz", 797 | "integrity": "sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==", 798 | "cpu": [ 799 | "x64" 800 | ], 801 | "dev": true, 802 | "optional": true, 803 | "os": [ 804 | "netbsd" 805 | ] 806 | }, 807 | "node_modules/esbuild-openbsd-64": { 808 | "version": "0.13.15", 809 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz", 810 | "integrity": "sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==", 811 | "cpu": [ 812 | "x64" 813 | ], 814 | "dev": true, 815 | "optional": true, 816 | "os": [ 817 | "openbsd" 818 | ] 819 | }, 820 | "node_modules/esbuild-sunos-64": { 821 | "version": "0.13.15", 822 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz", 823 | "integrity": "sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==", 824 | "cpu": [ 825 | "x64" 826 | ], 827 | "dev": true, 828 | "optional": true, 829 | "os": [ 830 | "sunos" 831 | ] 832 | }, 833 | "node_modules/esbuild-windows-32": { 834 | "version": "0.13.15", 835 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz", 836 | "integrity": "sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==", 837 | "cpu": [ 838 | "ia32" 839 | ], 840 | "dev": true, 841 | "optional": true, 842 | "os": [ 843 | "win32" 844 | ] 845 | }, 846 | "node_modules/esbuild-windows-64": { 847 | "version": "0.13.15", 848 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz", 849 | "integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==", 850 | "cpu": [ 851 | "x64" 852 | ], 853 | "dev": true, 854 | "optional": true, 855 | "os": [ 856 | "win32" 857 | ] 858 | }, 859 | "node_modules/esbuild-windows-arm64": { 860 | "version": "0.13.15", 861 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz", 862 | "integrity": "sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==", 863 | "cpu": [ 864 | "arm64" 865 | ], 866 | "dev": true, 867 | "optional": true, 868 | "os": [ 869 | "win32" 870 | ] 871 | }, 872 | "node_modules/escalade": { 873 | "version": "3.1.1", 874 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 875 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 876 | "dev": true, 877 | "engines": { 878 | "node": ">=6" 879 | } 880 | }, 881 | "node_modules/escape-string-regexp": { 882 | "version": "1.0.5", 883 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 884 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 885 | "dev": true, 886 | "engines": { 887 | "node": ">=0.8.0" 888 | } 889 | }, 890 | "node_modules/estree-walker": { 891 | "version": "2.0.2", 892 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 893 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 894 | "dev": true 895 | }, 896 | "node_modules/fast-async": { 897 | "version": "7.0.6", 898 | "resolved": "https://registry.npmjs.org/fast-async/-/fast-async-7.0.6.tgz", 899 | "integrity": "sha512-/iUa3eSQC+Xh5tN6QcVLsEsN7b1DaPIoTZo++VpLLIxtdNW2tEmMZex4TcrMeRnBwMOpZwue2CB171wjt5Kgqg==", 900 | "dependencies": { 901 | "@babel/generator": "^7.0.0-beta.44", 902 | "@babel/helper-module-imports": "^7.0.0-beta.44", 903 | "babylon": "^7.0.0-beta.44", 904 | "nodent-runtime": "^3.2.1", 905 | "nodent-transform": "^3.2.4" 906 | } 907 | }, 908 | "node_modules/fsevents": { 909 | "version": "2.3.2", 910 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 911 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 912 | "dev": true, 913 | "hasInstallScript": true, 914 | "optional": true, 915 | "os": [ 916 | "darwin" 917 | ], 918 | "engines": { 919 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 920 | } 921 | }, 922 | "node_modules/function-bind": { 923 | "version": "1.1.1", 924 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 925 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 926 | "dev": true 927 | }, 928 | "node_modules/gensync": { 929 | "version": "1.0.0-beta.2", 930 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 931 | "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", 932 | "dev": true, 933 | "engines": { 934 | "node": ">=6.9.0" 935 | } 936 | }, 937 | "node_modules/globals": { 938 | "version": "11.12.0", 939 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 940 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 941 | "dev": true, 942 | "engines": { 943 | "node": ">=4" 944 | } 945 | }, 946 | "node_modules/has": { 947 | "version": "1.0.3", 948 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 949 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 950 | "dev": true, 951 | "dependencies": { 952 | "function-bind": "^1.1.1" 953 | }, 954 | "engines": { 955 | "node": ">= 0.4.0" 956 | } 957 | }, 958 | "node_modules/has-flag": { 959 | "version": "3.0.0", 960 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 961 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 962 | "dev": true, 963 | "engines": { 964 | "node": ">=4" 965 | } 966 | }, 967 | "node_modules/history": { 968 | "version": "5.2.0", 969 | "resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz", 970 | "integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==", 971 | "dependencies": { 972 | "@babel/runtime": "^7.7.6" 973 | } 974 | }, 975 | "node_modules/is-core-module": { 976 | "version": "2.8.1", 977 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", 978 | "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", 979 | "dev": true, 980 | "dependencies": { 981 | "has": "^1.0.3" 982 | }, 983 | "funding": { 984 | "url": "https://github.com/sponsors/ljharb" 985 | } 986 | }, 987 | "node_modules/js-tokens": { 988 | "version": "4.0.0", 989 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 990 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 991 | }, 992 | "node_modules/jsesc": { 993 | "version": "2.5.2", 994 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 995 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", 996 | "bin": { 997 | "jsesc": "bin/jsesc" 998 | }, 999 | "engines": { 1000 | "node": ">=4" 1001 | } 1002 | }, 1003 | "node_modules/json5": { 1004 | "version": "2.2.0", 1005 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", 1006 | "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", 1007 | "dev": true, 1008 | "dependencies": { 1009 | "minimist": "^1.2.5" 1010 | }, 1011 | "bin": { 1012 | "json5": "lib/cli.js" 1013 | }, 1014 | "engines": { 1015 | "node": ">=6" 1016 | } 1017 | }, 1018 | "node_modules/loose-envify": { 1019 | "version": "1.4.0", 1020 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 1021 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 1022 | "dependencies": { 1023 | "js-tokens": "^3.0.0 || ^4.0.0" 1024 | }, 1025 | "bin": { 1026 | "loose-envify": "cli.js" 1027 | } 1028 | }, 1029 | "node_modules/minimist": { 1030 | "version": "1.2.5", 1031 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 1032 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 1033 | "dev": true 1034 | }, 1035 | "node_modules/ms": { 1036 | "version": "2.1.2", 1037 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1038 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1039 | "dev": true 1040 | }, 1041 | "node_modules/nanoid": { 1042 | "version": "3.2.0", 1043 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", 1044 | "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", 1045 | "dev": true, 1046 | "bin": { 1047 | "nanoid": "bin/nanoid.cjs" 1048 | }, 1049 | "engines": { 1050 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1051 | } 1052 | }, 1053 | "node_modules/node-releases": { 1054 | "version": "2.0.1", 1055 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", 1056 | "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", 1057 | "dev": true 1058 | }, 1059 | "node_modules/nodent-runtime": { 1060 | "version": "3.2.1", 1061 | "resolved": "https://registry.npmjs.org/nodent-runtime/-/nodent-runtime-3.2.1.tgz", 1062 | "integrity": "sha512-7Ws63oC+215smeKJQCxzrK21VFVlCFBkwl0MOObt0HOpVQXs3u483sAmtkF33nNqZ5rSOQjB76fgyPBmAUrtCA==", 1063 | "hasInstallScript": true 1064 | }, 1065 | "node_modules/nodent-transform": { 1066 | "version": "3.2.9", 1067 | "resolved": "https://registry.npmjs.org/nodent-transform/-/nodent-transform-3.2.9.tgz", 1068 | "integrity": "sha512-4a5FH4WLi+daH/CGD5o/JWRR8W5tlCkd3nrDSkxbOzscJTyTUITltvOJeQjg3HJ1YgEuNyiPhQbvbtRjkQBByQ==" 1069 | }, 1070 | "node_modules/object-assign": { 1071 | "version": "4.1.1", 1072 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1073 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 1074 | "engines": { 1075 | "node": ">=0.10.0" 1076 | } 1077 | }, 1078 | "node_modules/path-parse": { 1079 | "version": "1.0.7", 1080 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1081 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1082 | "dev": true 1083 | }, 1084 | "node_modules/picocolors": { 1085 | "version": "1.0.0", 1086 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1087 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 1088 | "dev": true 1089 | }, 1090 | "node_modules/picomatch": { 1091 | "version": "2.3.1", 1092 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1093 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1094 | "dev": true, 1095 | "engines": { 1096 | "node": ">=8.6" 1097 | }, 1098 | "funding": { 1099 | "url": "https://github.com/sponsors/jonschlinkert" 1100 | } 1101 | }, 1102 | "node_modules/postcss": { 1103 | "version": "8.4.5", 1104 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", 1105 | "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", 1106 | "dev": true, 1107 | "dependencies": { 1108 | "nanoid": "^3.1.30", 1109 | "picocolors": "^1.0.0", 1110 | "source-map-js": "^1.0.1" 1111 | }, 1112 | "engines": { 1113 | "node": "^10 || ^12 || >=14" 1114 | }, 1115 | "funding": { 1116 | "type": "opencollective", 1117 | "url": "https://opencollective.com/postcss/" 1118 | } 1119 | }, 1120 | "node_modules/prettier": { 1121 | "version": "2.5.1", 1122 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", 1123 | "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", 1124 | "dev": true, 1125 | "bin": { 1126 | "prettier": "bin-prettier.js" 1127 | }, 1128 | "engines": { 1129 | "node": ">=10.13.0" 1130 | } 1131 | }, 1132 | "node_modules/react": { 1133 | "version": "17.0.2", 1134 | "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", 1135 | "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", 1136 | "dependencies": { 1137 | "loose-envify": "^1.1.0", 1138 | "object-assign": "^4.1.1" 1139 | }, 1140 | "engines": { 1141 | "node": ">=0.10.0" 1142 | } 1143 | }, 1144 | "node_modules/react-dom": { 1145 | "version": "17.0.2", 1146 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", 1147 | "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", 1148 | "dependencies": { 1149 | "loose-envify": "^1.1.0", 1150 | "object-assign": "^4.1.1", 1151 | "scheduler": "^0.20.2" 1152 | }, 1153 | "peerDependencies": { 1154 | "react": "17.0.2" 1155 | } 1156 | }, 1157 | "node_modules/react-location": { 1158 | "version": "3.3.3", 1159 | "resolved": "https://registry.npmjs.org/react-location/-/react-location-3.3.3.tgz", 1160 | "integrity": "sha512-TUovnQxd17VUllxkouWoVouZByKzYN3g4rRnTraH8Fd/hrtdheC8xmfmNOZUsaO2iHvBVDaShknBpWzFtGeahg==", 1161 | "dependencies": { 1162 | "@babel/runtime": "^7.15.4", 1163 | "core-js": "^3.18.3", 1164 | "fast-async": "7", 1165 | "history": "^5.0.0", 1166 | "ts-toolbelt": "^9.6.0" 1167 | }, 1168 | "funding": { 1169 | "type": "github", 1170 | "url": "https://github.com/sponsors/tannerlinsley" 1171 | }, 1172 | "peerDependencies": { 1173 | "react": ">=16", 1174 | "react-dom": ">=16" 1175 | } 1176 | }, 1177 | "node_modules/react-refresh": { 1178 | "version": "0.11.0", 1179 | "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", 1180 | "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", 1181 | "dev": true, 1182 | "engines": { 1183 | "node": ">=0.10.0" 1184 | } 1185 | }, 1186 | "node_modules/regenerator-runtime": { 1187 | "version": "0.13.9", 1188 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", 1189 | "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" 1190 | }, 1191 | "node_modules/resolve": { 1192 | "version": "1.21.0", 1193 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", 1194 | "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", 1195 | "dev": true, 1196 | "dependencies": { 1197 | "is-core-module": "^2.8.0", 1198 | "path-parse": "^1.0.7", 1199 | "supports-preserve-symlinks-flag": "^1.0.0" 1200 | }, 1201 | "bin": { 1202 | "resolve": "bin/resolve" 1203 | }, 1204 | "funding": { 1205 | "url": "https://github.com/sponsors/ljharb" 1206 | } 1207 | }, 1208 | "node_modules/rollup": { 1209 | "version": "2.64.0", 1210 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.64.0.tgz", 1211 | "integrity": "sha512-+c+lbw1lexBKSMb1yxGDVfJ+vchJH3qLbmavR+awDinTDA2C5Ug9u7lkOzj62SCu0PKUExsW36tpgW7Fmpn3yQ==", 1212 | "dev": true, 1213 | "bin": { 1214 | "rollup": "dist/bin/rollup" 1215 | }, 1216 | "engines": { 1217 | "node": ">=10.0.0" 1218 | }, 1219 | "optionalDependencies": { 1220 | "fsevents": "~2.3.2" 1221 | } 1222 | }, 1223 | "node_modules/safe-buffer": { 1224 | "version": "5.1.2", 1225 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1226 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1227 | "dev": true 1228 | }, 1229 | "node_modules/scheduler": { 1230 | "version": "0.20.2", 1231 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", 1232 | "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", 1233 | "dependencies": { 1234 | "loose-envify": "^1.1.0", 1235 | "object-assign": "^4.1.1" 1236 | } 1237 | }, 1238 | "node_modules/semver": { 1239 | "version": "6.3.0", 1240 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1241 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1242 | "dev": true, 1243 | "bin": { 1244 | "semver": "bin/semver.js" 1245 | } 1246 | }, 1247 | "node_modules/source-map": { 1248 | "version": "0.5.7", 1249 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1250 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 1251 | "engines": { 1252 | "node": ">=0.10.0" 1253 | } 1254 | }, 1255 | "node_modules/source-map-js": { 1256 | "version": "1.0.2", 1257 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1258 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 1259 | "dev": true, 1260 | "engines": { 1261 | "node": ">=0.10.0" 1262 | } 1263 | }, 1264 | "node_modules/supports-color": { 1265 | "version": "5.5.0", 1266 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1267 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1268 | "dev": true, 1269 | "dependencies": { 1270 | "has-flag": "^3.0.0" 1271 | }, 1272 | "engines": { 1273 | "node": ">=4" 1274 | } 1275 | }, 1276 | "node_modules/supports-preserve-symlinks-flag": { 1277 | "version": "1.0.0", 1278 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1279 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1280 | "dev": true, 1281 | "engines": { 1282 | "node": ">= 0.4" 1283 | }, 1284 | "funding": { 1285 | "url": "https://github.com/sponsors/ljharb" 1286 | } 1287 | }, 1288 | "node_modules/to-fast-properties": { 1289 | "version": "2.0.0", 1290 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 1291 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", 1292 | "engines": { 1293 | "node": ">=4" 1294 | } 1295 | }, 1296 | "node_modules/ts-toolbelt": { 1297 | "version": "9.6.0", 1298 | "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", 1299 | "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==" 1300 | }, 1301 | "node_modules/typescript": { 1302 | "version": "4.5.4", 1303 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", 1304 | "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", 1305 | "dev": true, 1306 | "bin": { 1307 | "tsc": "bin/tsc", 1308 | "tsserver": "bin/tsserver" 1309 | }, 1310 | "engines": { 1311 | "node": ">=4.2.0" 1312 | } 1313 | }, 1314 | "node_modules/vite": { 1315 | "version": "2.7.13", 1316 | "resolved": "https://registry.npmjs.org/vite/-/vite-2.7.13.tgz", 1317 | "integrity": "sha512-Mq8et7f3aK0SgSxjDNfOAimZGW9XryfHRa/uV0jseQSilg+KhYDSoNb9h1rknOy6SuMkvNDLKCYAYYUMCE+IgQ==", 1318 | "dev": true, 1319 | "dependencies": { 1320 | "esbuild": "^0.13.12", 1321 | "postcss": "^8.4.5", 1322 | "resolve": "^1.20.0", 1323 | "rollup": "^2.59.0" 1324 | }, 1325 | "bin": { 1326 | "vite": "bin/vite.js" 1327 | }, 1328 | "engines": { 1329 | "node": ">=12.2.0" 1330 | }, 1331 | "optionalDependencies": { 1332 | "fsevents": "~2.3.2" 1333 | }, 1334 | "peerDependencies": { 1335 | "less": "*", 1336 | "sass": "*", 1337 | "stylus": "*" 1338 | }, 1339 | "peerDependenciesMeta": { 1340 | "less": { 1341 | "optional": true 1342 | }, 1343 | "sass": { 1344 | "optional": true 1345 | }, 1346 | "stylus": { 1347 | "optional": true 1348 | } 1349 | } 1350 | } 1351 | }, 1352 | "dependencies": { 1353 | "@babel/code-frame": { 1354 | "version": "7.16.7", 1355 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", 1356 | "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", 1357 | "dev": true, 1358 | "requires": { 1359 | "@babel/highlight": "^7.16.7" 1360 | } 1361 | }, 1362 | "@babel/compat-data": { 1363 | "version": "7.16.8", 1364 | "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", 1365 | "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", 1366 | "dev": true 1367 | }, 1368 | "@babel/core": { 1369 | "version": "7.16.7", 1370 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz", 1371 | "integrity": "sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==", 1372 | "dev": true, 1373 | "requires": { 1374 | "@babel/code-frame": "^7.16.7", 1375 | "@babel/generator": "^7.16.7", 1376 | "@babel/helper-compilation-targets": "^7.16.7", 1377 | "@babel/helper-module-transforms": "^7.16.7", 1378 | "@babel/helpers": "^7.16.7", 1379 | "@babel/parser": "^7.16.7", 1380 | "@babel/template": "^7.16.7", 1381 | "@babel/traverse": "^7.16.7", 1382 | "@babel/types": "^7.16.7", 1383 | "convert-source-map": "^1.7.0", 1384 | "debug": "^4.1.0", 1385 | "gensync": "^1.0.0-beta.2", 1386 | "json5": "^2.1.2", 1387 | "semver": "^6.3.0", 1388 | "source-map": "^0.5.0" 1389 | } 1390 | }, 1391 | "@babel/generator": { 1392 | "version": "7.16.8", 1393 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", 1394 | "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", 1395 | "requires": { 1396 | "@babel/types": "^7.16.8", 1397 | "jsesc": "^2.5.1", 1398 | "source-map": "^0.5.0" 1399 | } 1400 | }, 1401 | "@babel/helper-annotate-as-pure": { 1402 | "version": "7.16.7", 1403 | "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", 1404 | "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", 1405 | "dev": true, 1406 | "requires": { 1407 | "@babel/types": "^7.16.7" 1408 | } 1409 | }, 1410 | "@babel/helper-compilation-targets": { 1411 | "version": "7.16.7", 1412 | "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", 1413 | "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", 1414 | "dev": true, 1415 | "requires": { 1416 | "@babel/compat-data": "^7.16.4", 1417 | "@babel/helper-validator-option": "^7.16.7", 1418 | "browserslist": "^4.17.5", 1419 | "semver": "^6.3.0" 1420 | } 1421 | }, 1422 | "@babel/helper-environment-visitor": { 1423 | "version": "7.16.7", 1424 | "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", 1425 | "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", 1426 | "dev": true, 1427 | "requires": { 1428 | "@babel/types": "^7.16.7" 1429 | } 1430 | }, 1431 | "@babel/helper-function-name": { 1432 | "version": "7.16.7", 1433 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", 1434 | "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", 1435 | "dev": true, 1436 | "requires": { 1437 | "@babel/helper-get-function-arity": "^7.16.7", 1438 | "@babel/template": "^7.16.7", 1439 | "@babel/types": "^7.16.7" 1440 | } 1441 | }, 1442 | "@babel/helper-get-function-arity": { 1443 | "version": "7.16.7", 1444 | "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", 1445 | "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", 1446 | "dev": true, 1447 | "requires": { 1448 | "@babel/types": "^7.16.7" 1449 | } 1450 | }, 1451 | "@babel/helper-hoist-variables": { 1452 | "version": "7.16.7", 1453 | "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", 1454 | "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", 1455 | "dev": true, 1456 | "requires": { 1457 | "@babel/types": "^7.16.7" 1458 | } 1459 | }, 1460 | "@babel/helper-module-imports": { 1461 | "version": "7.16.7", 1462 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", 1463 | "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", 1464 | "requires": { 1465 | "@babel/types": "^7.16.7" 1466 | } 1467 | }, 1468 | "@babel/helper-module-transforms": { 1469 | "version": "7.16.7", 1470 | "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", 1471 | "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", 1472 | "dev": true, 1473 | "requires": { 1474 | "@babel/helper-environment-visitor": "^7.16.7", 1475 | "@babel/helper-module-imports": "^7.16.7", 1476 | "@babel/helper-simple-access": "^7.16.7", 1477 | "@babel/helper-split-export-declaration": "^7.16.7", 1478 | "@babel/helper-validator-identifier": "^7.16.7", 1479 | "@babel/template": "^7.16.7", 1480 | "@babel/traverse": "^7.16.7", 1481 | "@babel/types": "^7.16.7" 1482 | } 1483 | }, 1484 | "@babel/helper-plugin-utils": { 1485 | "version": "7.16.7", 1486 | "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", 1487 | "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", 1488 | "dev": true 1489 | }, 1490 | "@babel/helper-simple-access": { 1491 | "version": "7.16.7", 1492 | "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", 1493 | "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", 1494 | "dev": true, 1495 | "requires": { 1496 | "@babel/types": "^7.16.7" 1497 | } 1498 | }, 1499 | "@babel/helper-split-export-declaration": { 1500 | "version": "7.16.7", 1501 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", 1502 | "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", 1503 | "dev": true, 1504 | "requires": { 1505 | "@babel/types": "^7.16.7" 1506 | } 1507 | }, 1508 | "@babel/helper-validator-identifier": { 1509 | "version": "7.16.7", 1510 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", 1511 | "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==" 1512 | }, 1513 | "@babel/helper-validator-option": { 1514 | "version": "7.16.7", 1515 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", 1516 | "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", 1517 | "dev": true 1518 | }, 1519 | "@babel/helpers": { 1520 | "version": "7.16.7", 1521 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", 1522 | "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", 1523 | "dev": true, 1524 | "requires": { 1525 | "@babel/template": "^7.16.7", 1526 | "@babel/traverse": "^7.16.7", 1527 | "@babel/types": "^7.16.7" 1528 | } 1529 | }, 1530 | "@babel/highlight": { 1531 | "version": "7.16.7", 1532 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", 1533 | "integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", 1534 | "dev": true, 1535 | "requires": { 1536 | "@babel/helper-validator-identifier": "^7.16.7", 1537 | "chalk": "^2.0.0", 1538 | "js-tokens": "^4.0.0" 1539 | } 1540 | }, 1541 | "@babel/parser": { 1542 | "version": "7.16.8", 1543 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.8.tgz", 1544 | "integrity": "sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==", 1545 | "dev": true 1546 | }, 1547 | "@babel/plugin-syntax-jsx": { 1548 | "version": "7.16.7", 1549 | "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", 1550 | "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", 1551 | "dev": true, 1552 | "requires": { 1553 | "@babel/helper-plugin-utils": "^7.16.7" 1554 | } 1555 | }, 1556 | "@babel/plugin-transform-react-jsx": { 1557 | "version": "7.16.7", 1558 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz", 1559 | "integrity": "sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag==", 1560 | "dev": true, 1561 | "requires": { 1562 | "@babel/helper-annotate-as-pure": "^7.16.7", 1563 | "@babel/helper-module-imports": "^7.16.7", 1564 | "@babel/helper-plugin-utils": "^7.16.7", 1565 | "@babel/plugin-syntax-jsx": "^7.16.7", 1566 | "@babel/types": "^7.16.7" 1567 | } 1568 | }, 1569 | "@babel/plugin-transform-react-jsx-development": { 1570 | "version": "7.16.7", 1571 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz", 1572 | "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==", 1573 | "dev": true, 1574 | "requires": { 1575 | "@babel/plugin-transform-react-jsx": "^7.16.7" 1576 | } 1577 | }, 1578 | "@babel/plugin-transform-react-jsx-self": { 1579 | "version": "7.16.7", 1580 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.16.7.tgz", 1581 | "integrity": "sha512-oe5VuWs7J9ilH3BCCApGoYjHoSO48vkjX2CbA5bFVhIuO2HKxA3vyF7rleA4o6/4rTDbk6r8hBW7Ul8E+UZrpA==", 1582 | "dev": true, 1583 | "requires": { 1584 | "@babel/helper-plugin-utils": "^7.16.7" 1585 | } 1586 | }, 1587 | "@babel/plugin-transform-react-jsx-source": { 1588 | "version": "7.16.7", 1589 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.16.7.tgz", 1590 | "integrity": "sha512-rONFiQz9vgbsnaMtQlZCjIRwhJvlrPET8TabIUK2hzlXw9B9s2Ieaxte1SCOOXMbWRHodbKixNf3BLcWVOQ8Bw==", 1591 | "dev": true, 1592 | "requires": { 1593 | "@babel/helper-plugin-utils": "^7.16.7" 1594 | } 1595 | }, 1596 | "@babel/runtime": { 1597 | "version": "7.16.7", 1598 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", 1599 | "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", 1600 | "requires": { 1601 | "regenerator-runtime": "^0.13.4" 1602 | } 1603 | }, 1604 | "@babel/template": { 1605 | "version": "7.16.7", 1606 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", 1607 | "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", 1608 | "dev": true, 1609 | "requires": { 1610 | "@babel/code-frame": "^7.16.7", 1611 | "@babel/parser": "^7.16.7", 1612 | "@babel/types": "^7.16.7" 1613 | } 1614 | }, 1615 | "@babel/traverse": { 1616 | "version": "7.16.8", 1617 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.8.tgz", 1618 | "integrity": "sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==", 1619 | "dev": true, 1620 | "requires": { 1621 | "@babel/code-frame": "^7.16.7", 1622 | "@babel/generator": "^7.16.8", 1623 | "@babel/helper-environment-visitor": "^7.16.7", 1624 | "@babel/helper-function-name": "^7.16.7", 1625 | "@babel/helper-hoist-variables": "^7.16.7", 1626 | "@babel/helper-split-export-declaration": "^7.16.7", 1627 | "@babel/parser": "^7.16.8", 1628 | "@babel/types": "^7.16.8", 1629 | "debug": "^4.1.0", 1630 | "globals": "^11.1.0" 1631 | } 1632 | }, 1633 | "@babel/types": { 1634 | "version": "7.16.8", 1635 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", 1636 | "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", 1637 | "requires": { 1638 | "@babel/helper-validator-identifier": "^7.16.7", 1639 | "to-fast-properties": "^2.0.0" 1640 | } 1641 | }, 1642 | "@rollup/pluginutils": { 1643 | "version": "4.1.2", 1644 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.2.tgz", 1645 | "integrity": "sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ==", 1646 | "dev": true, 1647 | "requires": { 1648 | "estree-walker": "^2.0.1", 1649 | "picomatch": "^2.2.2" 1650 | } 1651 | }, 1652 | "@types/prop-types": { 1653 | "version": "15.7.4", 1654 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", 1655 | "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", 1656 | "dev": true 1657 | }, 1658 | "@types/react": { 1659 | "version": "17.0.38", 1660 | "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", 1661 | "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", 1662 | "dev": true, 1663 | "requires": { 1664 | "@types/prop-types": "*", 1665 | "@types/scheduler": "*", 1666 | "csstype": "^3.0.2" 1667 | } 1668 | }, 1669 | "@types/react-dom": { 1670 | "version": "17.0.11", 1671 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", 1672 | "integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==", 1673 | "dev": true, 1674 | "requires": { 1675 | "@types/react": "*" 1676 | } 1677 | }, 1678 | "@types/scheduler": { 1679 | "version": "0.16.2", 1680 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", 1681 | "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", 1682 | "dev": true 1683 | }, 1684 | "@vitejs/plugin-react": { 1685 | "version": "1.1.4", 1686 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.1.4.tgz", 1687 | "integrity": "sha512-cMUBDonNY8PPeHWjIrYKbRn6bLSunh/Ixo2XLLBd3DM0uYBZft+c+04zkGhhN1lAwvoRKJ2FdtvhGhPgViHc6w==", 1688 | "dev": true, 1689 | "requires": { 1690 | "@babel/core": "^7.16.5", 1691 | "@babel/plugin-transform-react-jsx": "^7.16.5", 1692 | "@babel/plugin-transform-react-jsx-development": "^7.16.5", 1693 | "@babel/plugin-transform-react-jsx-self": "^7.16.5", 1694 | "@babel/plugin-transform-react-jsx-source": "^7.16.5", 1695 | "@rollup/pluginutils": "^4.1.2", 1696 | "react-refresh": "^0.11.0", 1697 | "resolve": "^1.20.0" 1698 | } 1699 | }, 1700 | "ansi-styles": { 1701 | "version": "3.2.1", 1702 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 1703 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 1704 | "dev": true, 1705 | "requires": { 1706 | "color-convert": "^1.9.0" 1707 | } 1708 | }, 1709 | "babylon": { 1710 | "version": "7.0.0-beta.47", 1711 | "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz", 1712 | "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==" 1713 | }, 1714 | "browserslist": { 1715 | "version": "4.19.1", 1716 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", 1717 | "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", 1718 | "dev": true, 1719 | "requires": { 1720 | "caniuse-lite": "^1.0.30001286", 1721 | "electron-to-chromium": "^1.4.17", 1722 | "escalade": "^3.1.1", 1723 | "node-releases": "^2.0.1", 1724 | "picocolors": "^1.0.0" 1725 | } 1726 | }, 1727 | "caniuse-lite": { 1728 | "version": "1.0.30001300", 1729 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz", 1730 | "integrity": "sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==", 1731 | "dev": true 1732 | }, 1733 | "chalk": { 1734 | "version": "2.4.2", 1735 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 1736 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 1737 | "dev": true, 1738 | "requires": { 1739 | "ansi-styles": "^3.2.1", 1740 | "escape-string-regexp": "^1.0.5", 1741 | "supports-color": "^5.3.0" 1742 | } 1743 | }, 1744 | "color-convert": { 1745 | "version": "1.9.3", 1746 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 1747 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 1748 | "dev": true, 1749 | "requires": { 1750 | "color-name": "1.1.3" 1751 | } 1752 | }, 1753 | "color-name": { 1754 | "version": "1.1.3", 1755 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 1756 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 1757 | "dev": true 1758 | }, 1759 | "convert-source-map": { 1760 | "version": "1.8.0", 1761 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", 1762 | "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", 1763 | "dev": true, 1764 | "requires": { 1765 | "safe-buffer": "~5.1.1" 1766 | } 1767 | }, 1768 | "core-js": { 1769 | "version": "3.20.3", 1770 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", 1771 | "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==" 1772 | }, 1773 | "csstype": { 1774 | "version": "3.0.10", 1775 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", 1776 | "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", 1777 | "dev": true 1778 | }, 1779 | "debug": { 1780 | "version": "4.3.3", 1781 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 1782 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 1783 | "dev": true, 1784 | "requires": { 1785 | "ms": "2.1.2" 1786 | } 1787 | }, 1788 | "electron-to-chromium": { 1789 | "version": "1.4.48", 1790 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.48.tgz", 1791 | "integrity": "sha512-RT3SEmpv7XUA+tKXrZGudAWLDpa7f8qmhjcLaM6OD/ERxjQ/zAojT8/Vvo0BSzbArkElFZ1WyZ9FuwAYbkdBNA==", 1792 | "dev": true 1793 | }, 1794 | "esbuild": { 1795 | "version": "0.13.15", 1796 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.15.tgz", 1797 | "integrity": "sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==", 1798 | "dev": true, 1799 | "requires": { 1800 | "esbuild-android-arm64": "0.13.15", 1801 | "esbuild-darwin-64": "0.13.15", 1802 | "esbuild-darwin-arm64": "0.13.15", 1803 | "esbuild-freebsd-64": "0.13.15", 1804 | "esbuild-freebsd-arm64": "0.13.15", 1805 | "esbuild-linux-32": "0.13.15", 1806 | "esbuild-linux-64": "0.13.15", 1807 | "esbuild-linux-arm": "0.13.15", 1808 | "esbuild-linux-arm64": "0.13.15", 1809 | "esbuild-linux-mips64le": "0.13.15", 1810 | "esbuild-linux-ppc64le": "0.13.15", 1811 | "esbuild-netbsd-64": "0.13.15", 1812 | "esbuild-openbsd-64": "0.13.15", 1813 | "esbuild-sunos-64": "0.13.15", 1814 | "esbuild-windows-32": "0.13.15", 1815 | "esbuild-windows-64": "0.13.15", 1816 | "esbuild-windows-arm64": "0.13.15" 1817 | } 1818 | }, 1819 | "esbuild-android-arm64": { 1820 | "version": "0.13.15", 1821 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz", 1822 | "integrity": "sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==", 1823 | "dev": true, 1824 | "optional": true 1825 | }, 1826 | "esbuild-darwin-64": { 1827 | "version": "0.13.15", 1828 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz", 1829 | "integrity": "sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==", 1830 | "dev": true, 1831 | "optional": true 1832 | }, 1833 | "esbuild-darwin-arm64": { 1834 | "version": "0.13.15", 1835 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz", 1836 | "integrity": "sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==", 1837 | "dev": true, 1838 | "optional": true 1839 | }, 1840 | "esbuild-freebsd-64": { 1841 | "version": "0.13.15", 1842 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz", 1843 | "integrity": "sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==", 1844 | "dev": true, 1845 | "optional": true 1846 | }, 1847 | "esbuild-freebsd-arm64": { 1848 | "version": "0.13.15", 1849 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz", 1850 | "integrity": "sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==", 1851 | "dev": true, 1852 | "optional": true 1853 | }, 1854 | "esbuild-linux-32": { 1855 | "version": "0.13.15", 1856 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz", 1857 | "integrity": "sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==", 1858 | "dev": true, 1859 | "optional": true 1860 | }, 1861 | "esbuild-linux-64": { 1862 | "version": "0.13.15", 1863 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz", 1864 | "integrity": "sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==", 1865 | "dev": true, 1866 | "optional": true 1867 | }, 1868 | "esbuild-linux-arm": { 1869 | "version": "0.13.15", 1870 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz", 1871 | "integrity": "sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==", 1872 | "dev": true, 1873 | "optional": true 1874 | }, 1875 | "esbuild-linux-arm64": { 1876 | "version": "0.13.15", 1877 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz", 1878 | "integrity": "sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==", 1879 | "dev": true, 1880 | "optional": true 1881 | }, 1882 | "esbuild-linux-mips64le": { 1883 | "version": "0.13.15", 1884 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz", 1885 | "integrity": "sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==", 1886 | "dev": true, 1887 | "optional": true 1888 | }, 1889 | "esbuild-linux-ppc64le": { 1890 | "version": "0.13.15", 1891 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz", 1892 | "integrity": "sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==", 1893 | "dev": true, 1894 | "optional": true 1895 | }, 1896 | "esbuild-netbsd-64": { 1897 | "version": "0.13.15", 1898 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz", 1899 | "integrity": "sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==", 1900 | "dev": true, 1901 | "optional": true 1902 | }, 1903 | "esbuild-openbsd-64": { 1904 | "version": "0.13.15", 1905 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz", 1906 | "integrity": "sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==", 1907 | "dev": true, 1908 | "optional": true 1909 | }, 1910 | "esbuild-sunos-64": { 1911 | "version": "0.13.15", 1912 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz", 1913 | "integrity": "sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==", 1914 | "dev": true, 1915 | "optional": true 1916 | }, 1917 | "esbuild-windows-32": { 1918 | "version": "0.13.15", 1919 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz", 1920 | "integrity": "sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==", 1921 | "dev": true, 1922 | "optional": true 1923 | }, 1924 | "esbuild-windows-64": { 1925 | "version": "0.13.15", 1926 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz", 1927 | "integrity": "sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==", 1928 | "dev": true, 1929 | "optional": true 1930 | }, 1931 | "esbuild-windows-arm64": { 1932 | "version": "0.13.15", 1933 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz", 1934 | "integrity": "sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==", 1935 | "dev": true, 1936 | "optional": true 1937 | }, 1938 | "escalade": { 1939 | "version": "3.1.1", 1940 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1941 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1942 | "dev": true 1943 | }, 1944 | "escape-string-regexp": { 1945 | "version": "1.0.5", 1946 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 1947 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 1948 | "dev": true 1949 | }, 1950 | "estree-walker": { 1951 | "version": "2.0.2", 1952 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 1953 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 1954 | "dev": true 1955 | }, 1956 | "fast-async": { 1957 | "version": "7.0.6", 1958 | "resolved": "https://registry.npmjs.org/fast-async/-/fast-async-7.0.6.tgz", 1959 | "integrity": "sha512-/iUa3eSQC+Xh5tN6QcVLsEsN7b1DaPIoTZo++VpLLIxtdNW2tEmMZex4TcrMeRnBwMOpZwue2CB171wjt5Kgqg==", 1960 | "requires": { 1961 | "@babel/generator": "^7.0.0-beta.44", 1962 | "@babel/helper-module-imports": "^7.0.0-beta.44", 1963 | "babylon": "^7.0.0-beta.44", 1964 | "nodent-runtime": "^3.2.1", 1965 | "nodent-transform": "^3.2.4" 1966 | } 1967 | }, 1968 | "fsevents": { 1969 | "version": "2.3.2", 1970 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1971 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1972 | "dev": true, 1973 | "optional": true 1974 | }, 1975 | "function-bind": { 1976 | "version": "1.1.1", 1977 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1978 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 1979 | "dev": true 1980 | }, 1981 | "gensync": { 1982 | "version": "1.0.0-beta.2", 1983 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 1984 | "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", 1985 | "dev": true 1986 | }, 1987 | "globals": { 1988 | "version": "11.12.0", 1989 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 1990 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 1991 | "dev": true 1992 | }, 1993 | "has": { 1994 | "version": "1.0.3", 1995 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1996 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1997 | "dev": true, 1998 | "requires": { 1999 | "function-bind": "^1.1.1" 2000 | } 2001 | }, 2002 | "has-flag": { 2003 | "version": "3.0.0", 2004 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 2005 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 2006 | "dev": true 2007 | }, 2008 | "history": { 2009 | "version": "5.2.0", 2010 | "resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz", 2011 | "integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==", 2012 | "requires": { 2013 | "@babel/runtime": "^7.7.6" 2014 | } 2015 | }, 2016 | "is-core-module": { 2017 | "version": "2.8.1", 2018 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", 2019 | "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", 2020 | "dev": true, 2021 | "requires": { 2022 | "has": "^1.0.3" 2023 | } 2024 | }, 2025 | "js-tokens": { 2026 | "version": "4.0.0", 2027 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 2028 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 2029 | }, 2030 | "jsesc": { 2031 | "version": "2.5.2", 2032 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 2033 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" 2034 | }, 2035 | "json5": { 2036 | "version": "2.2.0", 2037 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", 2038 | "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", 2039 | "dev": true, 2040 | "requires": { 2041 | "minimist": "^1.2.5" 2042 | } 2043 | }, 2044 | "loose-envify": { 2045 | "version": "1.4.0", 2046 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 2047 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 2048 | "requires": { 2049 | "js-tokens": "^3.0.0 || ^4.0.0" 2050 | } 2051 | }, 2052 | "minimist": { 2053 | "version": "1.2.5", 2054 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 2055 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 2056 | "dev": true 2057 | }, 2058 | "ms": { 2059 | "version": "2.1.2", 2060 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 2061 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 2062 | "dev": true 2063 | }, 2064 | "nanoid": { 2065 | "version": "3.2.0", 2066 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", 2067 | "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", 2068 | "dev": true 2069 | }, 2070 | "node-releases": { 2071 | "version": "2.0.1", 2072 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", 2073 | "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", 2074 | "dev": true 2075 | }, 2076 | "nodent-runtime": { 2077 | "version": "3.2.1", 2078 | "resolved": "https://registry.npmjs.org/nodent-runtime/-/nodent-runtime-3.2.1.tgz", 2079 | "integrity": "sha512-7Ws63oC+215smeKJQCxzrK21VFVlCFBkwl0MOObt0HOpVQXs3u483sAmtkF33nNqZ5rSOQjB76fgyPBmAUrtCA==" 2080 | }, 2081 | "nodent-transform": { 2082 | "version": "3.2.9", 2083 | "resolved": "https://registry.npmjs.org/nodent-transform/-/nodent-transform-3.2.9.tgz", 2084 | "integrity": "sha512-4a5FH4WLi+daH/CGD5o/JWRR8W5tlCkd3nrDSkxbOzscJTyTUITltvOJeQjg3HJ1YgEuNyiPhQbvbtRjkQBByQ==" 2085 | }, 2086 | "object-assign": { 2087 | "version": "4.1.1", 2088 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 2089 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 2090 | }, 2091 | "path-parse": { 2092 | "version": "1.0.7", 2093 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 2094 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 2095 | "dev": true 2096 | }, 2097 | "picocolors": { 2098 | "version": "1.0.0", 2099 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 2100 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 2101 | "dev": true 2102 | }, 2103 | "picomatch": { 2104 | "version": "2.3.1", 2105 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 2106 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 2107 | "dev": true 2108 | }, 2109 | "postcss": { 2110 | "version": "8.4.5", 2111 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", 2112 | "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", 2113 | "dev": true, 2114 | "requires": { 2115 | "nanoid": "^3.1.30", 2116 | "picocolors": "^1.0.0", 2117 | "source-map-js": "^1.0.1" 2118 | } 2119 | }, 2120 | "prettier": { 2121 | "version": "2.5.1", 2122 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", 2123 | "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", 2124 | "dev": true 2125 | }, 2126 | "react": { 2127 | "version": "17.0.2", 2128 | "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", 2129 | "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", 2130 | "requires": { 2131 | "loose-envify": "^1.1.0", 2132 | "object-assign": "^4.1.1" 2133 | } 2134 | }, 2135 | "react-dom": { 2136 | "version": "17.0.2", 2137 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", 2138 | "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", 2139 | "requires": { 2140 | "loose-envify": "^1.1.0", 2141 | "object-assign": "^4.1.1", 2142 | "scheduler": "^0.20.2" 2143 | } 2144 | }, 2145 | "react-location": { 2146 | "version": "3.3.3", 2147 | "resolved": "https://registry.npmjs.org/react-location/-/react-location-3.3.3.tgz", 2148 | "integrity": "sha512-TUovnQxd17VUllxkouWoVouZByKzYN3g4rRnTraH8Fd/hrtdheC8xmfmNOZUsaO2iHvBVDaShknBpWzFtGeahg==", 2149 | "requires": { 2150 | "@babel/runtime": "^7.15.4", 2151 | "core-js": "^3.18.3", 2152 | "fast-async": "7", 2153 | "history": "^5.0.0", 2154 | "ts-toolbelt": "^9.6.0" 2155 | } 2156 | }, 2157 | "react-refresh": { 2158 | "version": "0.11.0", 2159 | "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", 2160 | "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", 2161 | "dev": true 2162 | }, 2163 | "regenerator-runtime": { 2164 | "version": "0.13.9", 2165 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", 2166 | "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" 2167 | }, 2168 | "resolve": { 2169 | "version": "1.21.0", 2170 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", 2171 | "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", 2172 | "dev": true, 2173 | "requires": { 2174 | "is-core-module": "^2.8.0", 2175 | "path-parse": "^1.0.7", 2176 | "supports-preserve-symlinks-flag": "^1.0.0" 2177 | } 2178 | }, 2179 | "rollup": { 2180 | "version": "2.64.0", 2181 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.64.0.tgz", 2182 | "integrity": "sha512-+c+lbw1lexBKSMb1yxGDVfJ+vchJH3qLbmavR+awDinTDA2C5Ug9u7lkOzj62SCu0PKUExsW36tpgW7Fmpn3yQ==", 2183 | "dev": true, 2184 | "requires": { 2185 | "fsevents": "~2.3.2" 2186 | } 2187 | }, 2188 | "safe-buffer": { 2189 | "version": "5.1.2", 2190 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 2191 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 2192 | "dev": true 2193 | }, 2194 | "scheduler": { 2195 | "version": "0.20.2", 2196 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", 2197 | "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", 2198 | "requires": { 2199 | "loose-envify": "^1.1.0", 2200 | "object-assign": "^4.1.1" 2201 | } 2202 | }, 2203 | "semver": { 2204 | "version": "6.3.0", 2205 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 2206 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 2207 | "dev": true 2208 | }, 2209 | "source-map": { 2210 | "version": "0.5.7", 2211 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 2212 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" 2213 | }, 2214 | "source-map-js": { 2215 | "version": "1.0.2", 2216 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 2217 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 2218 | "dev": true 2219 | }, 2220 | "supports-color": { 2221 | "version": "5.5.0", 2222 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 2223 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 2224 | "dev": true, 2225 | "requires": { 2226 | "has-flag": "^3.0.0" 2227 | } 2228 | }, 2229 | "supports-preserve-symlinks-flag": { 2230 | "version": "1.0.0", 2231 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 2232 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 2233 | "dev": true 2234 | }, 2235 | "to-fast-properties": { 2236 | "version": "2.0.0", 2237 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 2238 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" 2239 | }, 2240 | "ts-toolbelt": { 2241 | "version": "9.6.0", 2242 | "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", 2243 | "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==" 2244 | }, 2245 | "typescript": { 2246 | "version": "4.5.4", 2247 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", 2248 | "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", 2249 | "dev": true 2250 | }, 2251 | "vite": { 2252 | "version": "2.7.13", 2253 | "resolved": "https://registry.npmjs.org/vite/-/vite-2.7.13.tgz", 2254 | "integrity": "sha512-Mq8et7f3aK0SgSxjDNfOAimZGW9XryfHRa/uV0jseQSilg+KhYDSoNb9h1rknOy6SuMkvNDLKCYAYYUMCE+IgQ==", 2255 | "dev": true, 2256 | "requires": { 2257 | "esbuild": "^0.13.12", 2258 | "fsevents": "~2.3.2", 2259 | "postcss": "^8.4.5", 2260 | "resolve": "^1.20.0", 2261 | "rollup": "^2.59.0" 2262 | } 2263 | } 2264 | } 2265 | } 2266 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-grid-quiz", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite --host 0.0.0.0", 6 | "build": "tsc && vite build && cp -r ./public ./dist/", 7 | "preview": "vite preview", 8 | "typecheck": "tsc --watch" 9 | }, 10 | "dependencies": { 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-location": "^3.3.3" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.33", 17 | "@types/react-dom": "^17.0.10", 18 | "@vitejs/plugin-react": "^1.0.7", 19 | "prettier": "^2.5.1", 20 | "typescript": "^4.5.4", 21 | "vite": "^2.7.2" 22 | } 23 | } -------------------------------------------------------------------------------- /public/ogp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uhyo/css-grid-quiz/e5e69d18440cb20582152dbf51f828acb824ba12/public/ogp.png -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Routes } from "./Routes"; 2 | 3 | function App() { 4 | return ; 5 | } 6 | 7 | export default App; 8 | -------------------------------------------------------------------------------- /src/Routes.tsx: -------------------------------------------------------------------------------- 1 | import { ReactLocation, Route, Router } from "react-location"; 2 | import { QuestionPage } from "./pages/QuestionPage"; 3 | import { TopPage } from "./pages/TopPage"; 4 | import { loadQuestionV1 } from "./questions/loadQuestion"; 5 | import { DefineLanguageRoute } from "./utils/i18n/DefineLanguageRoute"; 6 | 7 | const routes: Route[] = [ 8 | { 9 | element: , 10 | children: [ 11 | { 12 | path: "/", 13 | element: , 14 | }, 15 | { 16 | path: "/quiz", 17 | children: [ 18 | { 19 | path: "/v1", 20 | children: [ 21 | { 22 | path: ":id", 23 | loader: async ({ params }) => { 24 | return { 25 | quizData: await loadQuestionV1(params.id), 26 | }; 27 | }, 28 | element: , 29 | }, 30 | ], 31 | }, 32 | ], 33 | }, 34 | ], 35 | }, 36 | ]; 37 | 38 | const location = new ReactLocation(); 39 | 40 | export const Routes: React.VFC = () => { 41 | return ; 42 | }; 43 | -------------------------------------------------------------------------------- /src/const.ts: -------------------------------------------------------------------------------- 1 | export const appUrl = "https://css-grid-mastery.uhyo.space"; 2 | -------------------------------------------------------------------------------- /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 | --body-bg-color: white; 10 | --body-fg-color: black; 11 | background-color: var(--body-bg-color); 12 | color: var(--body-fg-color); 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | body { 17 | --body-bg-color: #444444; 18 | --body-fg-color: white; 19 | } 20 | } 21 | 22 | pre { 23 | margin: 0; 24 | } 25 | 26 | button { 27 | appearance: none; 28 | margin: 0; 29 | padding: 0; 30 | border: none; 31 | background: none; 32 | font: inherit; 33 | font-size: calc(10px + 2vmin); 34 | } 35 | 36 | code { 37 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 38 | monospace; 39 | } 40 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /src/pages/QuestionPage/QuestionPage.module.css: -------------------------------------------------------------------------------- 1 | .page { 2 | max-width: 1024px; 3 | margin: 0 auto; 4 | display: grid; 5 | grid-template: 6 | "main title" auto 7 | "main code" 1fr 8 | "cont cont" 80px / 1fr auto; 9 | 10 | --grid-bg-color: #ffffff; 11 | --grid-gap-color: #222222; 12 | --grid-fg-color: #666666; 13 | --code-bg-color: #d3d3d3; 14 | --code-fg-color: black; 15 | --cont-bg-color: #e8e8e8; 16 | --grid-active-bg-color: #88ddff; 17 | --grid-active-fg-color: #2233ff; 18 | 19 | --grid-font-size: 24px; 20 | } 21 | 22 | @media (max-width: 700px) { 23 | .page { 24 | grid-template: 25 | "title" auto 26 | "code" auto 27 | "main" auto 28 | "cont" 80px / 1fr; 29 | } 30 | .page code { 31 | font-size: 1em; 32 | } 33 | } 34 | 35 | @media (max-width: 750px), (max-height: 460px) { 36 | .page { 37 | --grid-font-size: 18px; 38 | } 39 | } 40 | 41 | @media (prefers-color-scheme: dark) { 42 | .page { 43 | --grid-bg-color: #222222; 44 | --grid-gap-color: #ffffff; 45 | --grid-fg-color: #aaaaaa; 46 | --cont-bg-color: #292929; 47 | --code-bg-color: #494949; 48 | --code-fg-color: white; 49 | --grid-active-bg-color: #086f98; 50 | --grid-active-fg-color: #dddeeb; 51 | } 52 | } 53 | 54 | .titleArea { 55 | grid-area: title; 56 | margin: 0; 57 | height: 40px; 58 | padding: 0 0.5em; 59 | display: flex; 60 | flex-flow: column nowrap; 61 | justify-content: center; 62 | background-color: #eeffee; 63 | color: black; 64 | font-size: 1.2em; 65 | } 66 | 67 | .mainArea { 68 | grid-area: main; 69 | } 70 | 71 | .mainGridContainer { 72 | max-width: calc(100vh - 80px); 73 | max-height: calc(100vh - 80px); 74 | margin: 0 0 0 auto; 75 | display: grid; 76 | grid-template: auto 1fr / 1fr; 77 | z-index: 0; 78 | } 79 | 80 | .mainGrid { 81 | grid-area: 1 / 1; 82 | } 83 | 84 | .cheatGrid { 85 | grid-area: 1 / 1; 86 | z-index: 1; 87 | pointer-events: none; 88 | } 89 | 90 | .defs { 91 | max-height: calc(100vh - 120px); 92 | grid-area: code; 93 | background-color: var(--code-bg-color); 94 | color: var(--code-fg-color); 95 | overflow: auto; 96 | scrollbar-gutter: stable; 97 | } 98 | 99 | .eachDef { 100 | padding: 0.5em; 101 | } 102 | 103 | .normalItem, .selectedItem { 104 | display: flex; 105 | flex-flow: row nowrap; 106 | align-items: center; 107 | justify-content: center; 108 | background-color: var(--grid-bg-color); 109 | color: var(--grid-fg-color); 110 | font-size: var(--grid-font-size); 111 | } 112 | 113 | .cheatItem { 114 | background-color: #ffff44; 115 | opacity: 0.5; 116 | } 117 | 118 | .cheatSubgrid { 119 | display: grid; 120 | background-color: #ffbbbb; 121 | opacity: 0.2; 122 | } 123 | 124 | .controlGrid { 125 | grid-area: cont; 126 | background-size: auto auto; 127 | background-color: var(--body-bg-color); 128 | background-image: repeating-linear-gradient(120deg, transparent, transparent 25px, var(--cont-bg-color) 25px, var(--cont-bg-color) 40px ); 129 | display: grid; 130 | grid-template-columns: 1fr auto auto auto; 131 | align-items: center; 132 | padding: 0 1em; 133 | gap: 1em; 134 | } 135 | 136 | .controlGrid.hasCheatNote { 137 | grid-template-rows: auto auto; 138 | } 139 | 140 | .controlGrid :is(a, button) { 141 | display: inline-block; 142 | width: max-content; 143 | border-radius: 0.75em; 144 | padding: 0.25em 0.5em; 145 | background-color: #888888; 146 | color: white; 147 | font-weight: bold; 148 | text-decoration: none; 149 | font-size: calc(10px + 2vmin); 150 | } 151 | 152 | .controlGrid button.goToTop { 153 | justify-self: start; 154 | } 155 | 156 | .controlGrid button.check { 157 | background-color: #0cb826; 158 | } 159 | 160 | .controlGrid button.cheat { 161 | background-color: #eebc18; 162 | color: black; 163 | } 164 | 165 | .controlGrid button.wrong { 166 | background-color: #1d6fb6; 167 | } 168 | 169 | .controlGrid .cheatNotice { 170 | grid-column: 1 / -1; 171 | grid-row: 2; 172 | margin: 0; 173 | font-size: 0.9em; 174 | } 175 | 176 | .tutorialArea { 177 | grid-row: main; 178 | grid-column: 1 / -1; 179 | padding: 1em; 180 | } -------------------------------------------------------------------------------- /src/pages/QuestionPage/QuizPage/index.tsx: -------------------------------------------------------------------------------- 1 | import { memo, useEffect, useMemo, useRef } from "react"; 2 | import { Link } from "react-location"; 3 | import { QuizData } from "../../../questions/QuestionData"; 4 | import { useI18n } from "../../../utils/i18n/LanguageContext"; 5 | import { indent } from "../../../utils/indent"; 6 | import { simpleParseCss } from "../../../utils/simpleParseCss"; 7 | import { GridArea } from "../components/GridArea"; 8 | import { GridAreaExtensionControl } from "../components/GridAreaExtensionControl"; 9 | import { ButtonState, useQuizPageLogic } from "../logic/useQuizPageLogic"; 10 | import classes from "../QuestionPage.module.css"; 11 | 12 | type Props = { 13 | quizId: string; 14 | quizData: QuizData; 15 | cheat: boolean; 16 | }; 17 | 18 | export const QuizPage: React.VFC = ({ quizId, quizData, cheat }) => { 19 | const { gridStyle, subgridStyle, itemStyle, gridDef, extensible } = quizData; 20 | const pageTitleRef = useRef(null); 21 | 22 | useEffect(() => { 23 | // a11y 24 | pageTitleRef.current?.focus(); 25 | }, [quizId]); 26 | 27 | const { gridStyleDisp, gridStyleObj } = useMemo( 28 | () => ({ 29 | gridStyleDisp: `.grid-container { 30 | display: grid; 31 | ${indent(gridStyle)} 32 | }`, 33 | gridStyleObj: simpleParseCss(gridStyle), 34 | }), 35 | [gridStyle] 36 | ); 37 | const { subgridStyleDisp, subgridStyleObj } = useMemo(() => { 38 | if (!subgridStyle) { 39 | return { subgridStyleDisp: undefined, subgridStyleObj: undefined }; 40 | } 41 | return { 42 | subgridStyleDisp: `.subgrid { 43 | display: grid; 44 | ${indent(subgridStyle)} 45 | }`, 46 | subgridStyleObj: simpleParseCss(subgridStyle), 47 | }; 48 | }, [subgridStyle]); 49 | const { itemStyleDisp, itemStyleObj } = useMemo( 50 | () => ({ 51 | itemStyleDisp: `.grid-item { 52 | ${indent(itemStyle)} 53 | }`, 54 | itemStyleObj: simpleParseCss(itemStyle), 55 | }), 56 | [itemStyle] 57 | ); 58 | 59 | const { 60 | selectedItems, 61 | buttonState, 62 | extension, 63 | toggleItem, 64 | extendGrid, 65 | check, 66 | reset, 67 | getCheat, 68 | } = useQuizPageLogic(quizId, quizData, cheat); 69 | 70 | const mainGrid = ( 71 |
72 | 81 | {cheat && ( 82 | 88 | {subgridStyleObj ? ( 89 |
90 |
91 |
92 | ) : ( 93 |
94 | )} 95 | 96 | )} 97 |
98 | ); 99 | 100 | return ( 101 |
102 |

108 | Page {quizId} 109 |

110 | 115 |
116 | {extensible ? ( 117 | 118 | {mainGrid} 119 | 120 | ) : ( 121 | mainGrid 122 | )} 123 |
124 | 132 |
133 | ); 134 | }; 135 | 136 | const GridDefs: React.VFC<{ 137 | gridStyleDisp: string; 138 | subgridStyleDisp: string | undefined; 139 | itemStyleDisp: string; 140 | }> = memo(({ gridStyleDisp, subgridStyleDisp, itemStyleDisp }) => { 141 | return ( 142 |
143 |
144 |         {gridStyleDisp}
145 |       
146 | {subgridStyleDisp ? ( 147 |
148 |           {subgridStyleDisp}
149 |         
150 | ) : null} 151 |
152 |         {itemStyleDisp}
153 |       
154 |
155 | ); 156 | }); 157 | 158 | const ControlGrid: React.VFC<{ 159 | buttonState: ButtonState; 160 | isCheat: boolean; 161 | isSubgrid: boolean; 162 | reset: () => void; 163 | check: () => void; 164 | getCheat: (() => void) | undefined; 165 | }> = memo(({ buttonState, isCheat, isSubgrid, reset, check, getCheat }) => { 166 | const langs = useI18n({ 167 | en: { 168 | goToTop: "Go to Top", 169 | correct: "Correct!", 170 | wrong: "Wrong…", 171 | subgridCheatNote: 172 | "Note: cheat for subgrids only works for browsers that support subgrid.", 173 | }, 174 | ja: { 175 | goToTop: "トップへ", 176 | correct: "正解!", 177 | wrong: "不正解…", 178 | subgridCheatNote: 179 | "注意:subgridに対するチートはブラウザがsubgridをサポートしている場合のみ正常に動作します。", 180 | }, 181 | }); 182 | return ( 183 |
184 | 185 | {langs.goToTop} 186 | 187 | {getCheat !== undefined ? ( 188 | 191 | ) : ( 192 | 193 | )} 194 | 195 | {buttonState === "check" ? ( 196 | 199 | ) : buttonState === "correct" ? ( 200 | 203 | ) : buttonState === "wrong" ? ( 204 | 207 | ) : null} 208 | {isCheat && isSubgrid ? ( 209 |

{langs.subgridCheatNote}

210 | ) : null} 211 |
212 | ); 213 | }); 214 | -------------------------------------------------------------------------------- /src/pages/QuestionPage/TutorialPage/index.tsx: -------------------------------------------------------------------------------- 1 | import { Tutorial } from "../../../questions/QuestionData"; 2 | import { useI18n } from "../../../utils/i18n/LanguageContext"; 3 | import { useNextPage } from "../hooks/useNextPage"; 4 | import classes from "../QuestionPage.module.css"; 5 | 6 | type Props = { 7 | id: string; 8 | tutorial: Tutorial; 9 | }; 10 | 11 | export const TutorialPage: React.VFC = ({ id, tutorial }) => { 12 | const { goToNextPage } = useNextPage(id); 13 | const langs = useI18n({ 14 | en: { 15 | proceed: "Proceed", 16 | }, 17 | ja: { 18 | proceed: "進む", 19 | }, 20 | }); 21 | return ( 22 |
23 |
{tutorial.contents}
24 |
25 | {!tutorial.noNext && ( 26 | 29 | )} 30 |
31 |
32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/pages/QuestionPage/components/GridArea.module.css: -------------------------------------------------------------------------------- 1 | .grid { 2 | display: grid; 3 | gap: 1px; 4 | aspect-ratio: 1 / 1; 5 | padding: 1px; 6 | grid-auto-columns: 1fr; 7 | grid-auto-rows: 1fr; 8 | } 9 | 10 | .gridHasGrid { 11 | background-color: var(--grid-gap-color); 12 | } 13 | 14 | .gridHasGrid :where(.normalItem, .selectedItem) { 15 | display: flex; 16 | flex-flow: row nowrap; 17 | align-items: center; 18 | justify-content: center; 19 | background-color: var(--grid-bg-color); 20 | color: var(--grid-fg-color); 21 | font-size: var(--grid-font-size); 22 | overflow: hidden; 23 | } 24 | 25 | .selectedItem { 26 | background-color: var(--grid-active-bg-color); 27 | color: var(--grid-active-fg-color); 28 | } -------------------------------------------------------------------------------- /src/pages/QuestionPage/components/GridArea.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from "react"; 2 | import { GridPosition } from "../../../questions/GridPosition"; 3 | import { range } from "../../../utils/range"; 4 | import { GridExtensionState } from "../logic/useGridExtension"; 5 | import classes from "./GridArea.module.css"; 6 | 7 | type GridDef = { rows: number; columns: number }; 8 | 9 | type SubgridDef = { 10 | style: React.CSSProperties; 11 | }; 12 | 13 | type PropsBase = { 14 | gridDef: GridDef; 15 | className?: string; 16 | style?: React.CSSProperties; 17 | children?: React.ReactNode; 18 | }; 19 | 20 | type PropsHasGrid = PropsBase & { 21 | hasGrid: true; 22 | toggleItem: (column: number, row: number) => void; 23 | selectedItems: readonly GridPosition[]; 24 | extension?: GridExtensionState; 25 | }; 26 | 27 | type PropsNoGrid = PropsBase & { 28 | hasGrid?: false; 29 | }; 30 | 31 | type Props = PropsHasGrid | PropsNoGrid; 32 | 33 | export const GridArea: React.VFC = (props) => { 34 | const { hasGrid, gridDef, className, style, children } = props; 35 | 36 | const extension = (hasGrid && props.extension) || { 37 | top: 0, 38 | right: 0, 39 | bottom: 0, 40 | left: 0, 41 | }; 42 | const gridContents = hasGrid ? ( 43 | <> 44 | {Array.from( 45 | range(1 - extension.top, gridDef.rows + extension.bottom + 1) 46 | ).map((row) => ( 47 | 48 | {Array.from( 49 | range(1 - extension.left, gridDef.columns + extension.right + 1) 50 | ).map((column) => { 51 | const isSelected = props.selectedItems.includes(`${column},${row}`); 52 | return ( 53 | 66 | ); 67 | })} 68 | 69 | ))} 70 | 71 | ) : null; 72 | 73 | return ( 74 |
82 | {gridContents} 83 | {children} 84 |
85 | ); 86 | }; 87 | 88 | function isInGrid(column: number, row: number, gridDef: GridDef) { 89 | return ( 90 | column >= 1 && column <= gridDef.columns && row >= 1 && row <= gridDef.rows 91 | ); 92 | } 93 | 94 | function getGridPlacelement( 95 | column: number, 96 | row: number, 97 | gridDef: GridDef 98 | ): React.CSSProperties { 99 | const gridRow = 100 | 1 <= row 101 | ? `${row}` 102 | : // 0 -> -(gridDef.rows+2), -1 -> -(gridDef.rows+3) ... 103 | `${-gridDef.rows + row - 2}`; 104 | 105 | const gridColumn = 106 | 1 <= column ? `${column}` : `${-gridDef.columns + column - 2}`; 107 | return { 108 | gridRow, 109 | gridColumn, 110 | }; 111 | } 112 | -------------------------------------------------------------------------------- /src/pages/QuestionPage/components/GridAreaExtensionControl.module.css: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | display: grid; 3 | grid-template-columns: auto 1fr auto; 4 | grid-template-rows: auto 1fr auto; 5 | } 6 | 7 | .main { 8 | grid-column: 2; 9 | grid-row: 2; 10 | } 11 | 12 | .button { 13 | display: flex; 14 | flex-flow: column nowrap; 15 | justify-content: center; 16 | align-items: center; 17 | margin: 4px; 18 | width: 32px; 19 | height: 32px; 20 | background-color: #ffffaa; 21 | border: 1px solid #aaaa22; 22 | border-radius: 4px; 23 | } 24 | 25 | .left { 26 | grid-column: 1; 27 | grid-row: 2; 28 | width: max-content; 29 | height: max-content; 30 | align-self: center; 31 | } 32 | 33 | .top { 34 | grid-column: 2; 35 | grid-row: 1; 36 | width: max-content; 37 | height: max-content; 38 | justify-self: center; 39 | } 40 | 41 | .right { 42 | grid-column: 3; 43 | grid-row: 2; 44 | width: max-content; 45 | height: max-content; 46 | align-self: center; 47 | } 48 | 49 | .bottom { 50 | grid-column: 2; 51 | grid-row: 3; 52 | width: max-content; 53 | height: max-content; 54 | justify-self: center; 55 | } -------------------------------------------------------------------------------- /src/pages/QuestionPage/components/GridAreaExtensionControl.tsx: -------------------------------------------------------------------------------- 1 | import { EdgeDirection } from "../logic/useGridExtension"; 2 | import plusImage from "./assets/plus.png"; 3 | import classes from "./GridAreaExtensionControl.module.css"; 4 | 5 | type Props = { 6 | children?: React.ReactNode; 7 | onExtend: (direction: EdgeDirection) => void; 8 | }; 9 | 10 | export const GridAreaExtensionControl: React.VFC = ({ 11 | children, 12 | onExtend, 13 | }) => { 14 | return ( 15 |
16 |
17 | 24 |
25 |
26 | 33 |
34 |
35 | 42 |
43 |
44 | 51 |
52 |
{children}
53 |
54 | ); 55 | }; 56 | -------------------------------------------------------------------------------- /src/pages/QuestionPage/components/assets/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uhyo/css-grid-quiz/e5e69d18440cb20582152dbf51f828acb824ba12/src/pages/QuestionPage/components/assets/plus.png -------------------------------------------------------------------------------- /src/pages/QuestionPage/hooks/useNextPage.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { useNavigate } from "react-location"; 3 | 4 | export function useNextPage(id: string) { 5 | const navigate = useNavigate(); 6 | const idNum = Number(id); 7 | const nextPageUrl = `/quiz/v1/${idNum + 1}`; 8 | const goToNextPage = useCallback(() => { 9 | navigate({ 10 | to: nextPageUrl, 11 | search: (old) => ({ 12 | cheat: undefined, 13 | lang: old?.lang, 14 | }), 15 | }); 16 | }, [nextPageUrl]); 17 | return { 18 | goToNextPage, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/QuestionPage/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useMatch, useSearch } from "react-location"; 3 | import { QuestionData } from "../../questions/QuestionData"; 4 | import { QuizPage } from "./QuizPage"; 5 | import { TutorialPage } from "./TutorialPage"; 6 | 7 | export const QuestionPage: React.VFC = () => { 8 | const { 9 | params: { id }, 10 | data: { quizData }, 11 | } = useMatch<{ 12 | LoaderData: { 13 | quizData: QuestionData; 14 | }; 15 | }>(); 16 | const { cheat } = useSearch<{ 17 | Search: { cheat?: string }; 18 | }>(); 19 | useEffect(() => { 20 | window.scrollTo({ 21 | top: 0, 22 | behavior: "smooth", 23 | }); 24 | }, [id]); 25 | if (quizData === undefined) { 26 | return null; 27 | } 28 | if (quizData.type === "tutorial") { 29 | return ; 30 | } 31 | return ; 32 | }; 33 | -------------------------------------------------------------------------------- /src/pages/QuestionPage/logic/useGridExtension.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { useStateReset } from "../../../utils/hooks/useStateReset"; 3 | 4 | export type EdgeDirection = "top" | "right" | "bottom" | "left"; 5 | 6 | export type GridExtensionState = { 7 | readonly top: number; 8 | readonly right: number; 9 | readonly bottom: number; 10 | readonly left: number; 11 | }; 12 | 13 | export function useGridExtension(deps: readonly unknown[]) { 14 | const [extension, setExtension] = useStateReset( 15 | deps, 16 | () => ({ 17 | top: 0, 18 | right: 0, 19 | bottom: 0, 20 | left: 0, 21 | }) 22 | ); 23 | 24 | const extend = useCallback( 25 | (direction: EdgeDirection, amount = 1) => { 26 | setExtension((prev) => { 27 | return { ...prev, [direction]: prev[direction] + amount }; 28 | }); 29 | }, 30 | [setExtension] 31 | ); 32 | 33 | return { 34 | extension, 35 | extend, 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /src/pages/QuestionPage/logic/useGridItemSelection.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from "react"; 2 | import { GridPosition } from "../../../questions/GridPosition"; 3 | import { useStateReset } from "../../../utils/hooks/useStateReset"; 4 | 5 | export function useGridItemSelection( 6 | deps: readonly unknown[], 7 | initialState?: readonly GridPosition[] 8 | ) { 9 | const [selectedItems, setSelectedItems] = useStateReset< 10 | readonly GridPosition[] 11 | >(deps, () => initialState ?? []); 12 | const toggleItem = useCallback( 13 | (column: number, row: number) => { 14 | setSelectedItems((selectedItems) => { 15 | const newSelectedItems = [...selectedItems]; 16 | const itemKey: GridPosition = `${column},${row}`; 17 | if (newSelectedItems.includes(itemKey)) { 18 | newSelectedItems.splice(newSelectedItems.indexOf(itemKey), 1); 19 | } else { 20 | newSelectedItems.push(itemKey); 21 | } 22 | return newSelectedItems; 23 | }); 24 | }, 25 | [setSelectedItems] 26 | ); 27 | 28 | return { 29 | selectedItems, 30 | toggleItem, 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/pages/QuestionPage/logic/useQuizPageLogic.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useMemo, useReducer } from "react"; 2 | import { useNavigate } from "react-location"; 3 | import { GridPosition } from "../../../questions/GridPosition"; 4 | import { QuizData } from "../../../questions/QuestionData"; 5 | import { useStateReset } from "../../../utils/hooks/useStateReset"; 6 | import { useNextPage } from "../hooks/useNextPage"; 7 | import { 8 | EdgeDirection, 9 | GridExtensionState, 10 | useGridExtension, 11 | } from "./useGridExtension"; 12 | import { useGridItemSelection } from "./useGridItemSelection"; 13 | 14 | export type ButtonState = "check" | "correct" | "wrong"; 15 | 16 | export function useQuizPageLogic( 17 | quizId: string, 18 | data: QuizData, 19 | isCheat: boolean 20 | ) { 21 | const { goToNextPage } = useNextPage(quizId); 22 | const navigate = useNavigate(); 23 | const [resetCount, reset] = useReducer((c: number) => c + 1, 0); 24 | const [wrongCount, addWrong] = useStateReset([quizId], () => 0); 25 | const { selectedItems, toggleItem } = useGridItemSelection([ 26 | quizId, 27 | resetCount, 28 | ]); 29 | const { extension, extend: extendGrid } = useGridExtension([ 30 | quizId, 31 | resetCount, 32 | ]); 33 | 34 | // if cheat is enabled but extension is not enough, extend 35 | useAutoExtension(extension, data.gridDef, extendGrid, data.answer, isCheat); 36 | 37 | const [buttonState, setButtonState] = useStateReset( 38 | [quizId], 39 | () => "check" 40 | ); 41 | 42 | const check = useCallback(() => { 43 | if (checkAnswer(selectedItems, data.answer)) { 44 | setButtonState("correct"); 45 | } else { 46 | setButtonState("wrong"); 47 | } 48 | }, [selectedItems, data]); 49 | 50 | useEffect(() => { 51 | if (buttonState === "correct") { 52 | const timer = setTimeout(() => { 53 | goToNextPage(); 54 | }, 300); 55 | return () => clearTimeout(timer); 56 | } 57 | if (buttonState === "wrong") { 58 | addWrong((c) => c + 1); 59 | const timer = setTimeout(() => { 60 | setButtonState("check"); 61 | }, 2500); 62 | return () => clearTimeout(timer); 63 | } 64 | return undefined; 65 | }, [buttonState]); 66 | 67 | const getCheat = useMemo(() => { 68 | if (wrongCount < 3) { 69 | // no cheat available yet 70 | return undefined; 71 | } 72 | return () => { 73 | navigate({ 74 | search: (old) => 75 | isCheat 76 | ? { lang: old?.lang } 77 | : { 78 | cheat: "1", 79 | lang: old?.lang, 80 | }, 81 | }); 82 | }; 83 | }, [wrongCount, isCheat]); 84 | 85 | return { 86 | selectedItems, 87 | toggleItem, 88 | extension, 89 | extendGrid, 90 | buttonState, 91 | check, 92 | reset, 93 | getCheat, 94 | }; 95 | } 96 | 97 | function checkAnswer( 98 | selectedItems: readonly GridPosition[], 99 | answer: readonly GridPosition[] 100 | ) { 101 | // TODO: O(N^2) 102 | return ( 103 | selectedItems.length === answer.length && 104 | selectedItems.every((item) => answer.includes(item)) 105 | ); 106 | } 107 | 108 | function useAutoExtension( 109 | extension: GridExtensionState, 110 | gridDef: { rows: number; columns: number }, 111 | extendGrid: (direction: EdgeDirection, amount: number) => void, 112 | answer: readonly GridPosition[], 113 | isCheat: boolean 114 | ) { 115 | useEffect(() => { 116 | if (!isCheat) { 117 | return; 118 | } 119 | let topEdge = 1; 120 | let rightEdge = gridDef.columns; 121 | let bottomEdge = gridDef.rows; 122 | let leftEdge = 1; 123 | 124 | for (const item of answer) { 125 | const [column, row] = item.split(",").map(Number); 126 | if (column < leftEdge) { 127 | leftEdge = column; 128 | } 129 | if (column > rightEdge) { 130 | rightEdge = column; 131 | } 132 | if (row < topEdge) { 133 | topEdge = row; 134 | } 135 | if (row > bottomEdge) { 136 | bottomEdge = row; 137 | } 138 | } 139 | const leftExtension = 1 - leftEdge; 140 | if (extension.left < leftExtension) { 141 | extendGrid("left", leftExtension - extension.left); 142 | } 143 | const rightExtension = rightEdge - gridDef.columns; 144 | if (extension.right < rightExtension) { 145 | extendGrid("right", rightExtension - extension.right); 146 | } 147 | const topExtension = 1 - topEdge; 148 | if (extension.top < topExtension) { 149 | extendGrid("top", topExtension - extension.top); 150 | } 151 | const bottomExtension = bottomEdge - gridDef.rows; 152 | if (extension.bottom < bottomExtension) { 153 | extendGrid("bottom", bottomExtension - extension.bottom); 154 | } 155 | }, [isCheat, answer, extension, gridDef]); 156 | } 157 | -------------------------------------------------------------------------------- /src/pages/TopPage/TopPage.module.css: -------------------------------------------------------------------------------- 1 | .topPage { 2 | width: max-content; 3 | max-width: calc(100% - 16px); 4 | margin: 0 auto; 5 | padding: 8px; 6 | } 7 | 8 | .topPage > header { 9 | text-align: center; 10 | } 11 | 12 | .logo { 13 | width: 500px; 14 | height: auto; 15 | max-width: 100%; 16 | } 17 | 18 | .tutorialLink { 19 | text-align: center; 20 | } 21 | 22 | .tutorialLink > a { 23 | display: inline-block; 24 | border-radius: 0.5em; 25 | padding: 0.5em; 26 | background-color: #777777; 27 | color: white; 28 | font-weight: bold; 29 | font-size: 1.5em; 30 | text-decoration: none; 31 | } 32 | 33 | .topPage hr { 34 | border: none; 35 | border-top: 1px solid #cccccc; 36 | } -------------------------------------------------------------------------------- /src/pages/TopPage/index.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | import { Link } from "react-location"; 3 | import { appUrl } from "../../const"; 4 | import { useI18n } from "../../utils/i18n/LanguageContext"; 5 | import logoImage from "./logo.png"; 6 | import classes from "./TopPage.module.css"; 7 | 8 | export const TopPage: React.VFC = () => { 9 | const shareOnTwitterlink = useMemo(() => { 10 | return `https://twitter.com/intent/tweet?text=${encodeURIComponent( 11 | "CSS Grid Mastery Quiz\n" 12 | )}&url=${encodeURIComponent(appUrl)}`; 13 | }, []); 14 | 15 | const langs = useI18n({ 16 | en: { 17 | intro: ( 18 | <> 19 |

20 | Learn how CSS Grid works through a number of CSS Grid questions! 21 |

22 |

23 | With the current version, you can learn how grid placement 24 | properties work. 25 |

26 | 27 | ), 28 | proceed: "Proceed to Tutorial", 29 | progress: ( 30 | <> 31 |

How do I save my progress?

32 |

33 | To save your progress, just save current URL. To continue, open that 34 | URL and go on! 35 |

36 | 37 | ), 38 | }, 39 | ja: { 40 | intro: ( 41 | <> 42 |

数々のCSS Gridの問題を解いてCSS Gridの仕組みを学習しよう!

43 |

44 | 現在のバージョンでは、グリッドアイテムの配置にかかわるプロパティの挙動を学ぶことができます。 45 |

46 | 47 | ), 48 | proceed: "チュートリアルに進む", 49 | progress: ( 50 | <> 51 |

進捗を保存する方法

52 |

53 | 現在の進捗を保存するには、現在のURLを保存しておくだけで構いません。そのURLを開けば再開できます。 54 |

55 | 56 | ), 57 | }, 58 | }); 59 | 60 | return ( 61 |
62 |
63 |

64 | CSS Grid Mastery Quiz 71 |

72 |

73 | 74 | English 75 | {" "} 76 | |{" "} 77 | 84 | 日本語 85 | 86 |

87 |
88 | {langs.intro} 89 |

90 | 91 | {langs.proceed} 92 | 93 |

94 | {langs.progress} 95 |
96 |

97 | 98 | GitHub 99 | 100 |

101 |

102 | 103 | Share on Twitter 104 | 105 |

106 |

107 | 108 | This site is using{" "} 109 | 110 | Twemoji 111 | 112 | . 113 | 114 |

115 |
116 | ); 117 | }; 118 | -------------------------------------------------------------------------------- /src/pages/TopPage/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uhyo/css-grid-quiz/e5e69d18440cb20582152dbf51f828acb824ba12/src/pages/TopPage/logo.png -------------------------------------------------------------------------------- /src/questions/GridPosition.ts: -------------------------------------------------------------------------------- 1 | export type GridPosition = `${number},${number}`; 2 | -------------------------------------------------------------------------------- /src/questions/QuestionData.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { GridPosition } from "./GridPosition"; 3 | 4 | export type QuestionDataBase = { 5 | noNext?: true; 6 | }; 7 | 8 | export type Tutorial = QuestionDataBase & { 9 | type: "tutorial"; 10 | contents: React.ReactElement; 11 | }; 12 | 13 | export type GridQuestion = QuestionDataBase & { 14 | type: "grid"; 15 | /** 16 | * Style of grid containter. 17 | */ 18 | gridStyle: string; 19 | /** 20 | * Style of grid item. 21 | */ 22 | itemStyle: string; 23 | /** 24 | * Rendered size of grid. 25 | */ 26 | gridDef: { 27 | rows: number; 28 | columns: number; 29 | }; 30 | /** 31 | * Whether grid is extensible. 32 | */ 33 | extensible?: boolean; 34 | /** 35 | * Style for subgrid. 36 | */ 37 | subgridStyle?: string; 38 | /** 39 | * Correct answer. 40 | */ 41 | answer: readonly GridPosition[]; 42 | }; 43 | 44 | export type QuizData = GridQuestion; 45 | 46 | export type QuestionData = Tutorial | QuizData; 47 | -------------------------------------------------------------------------------- /src/questions/loadQuestion.ts: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "./QuestionData"; 2 | 3 | export async function loadQuestionV1(id: string): Promise { 4 | try { 5 | const ns = await import(`./v1/${id}.tsx`); 6 | if (ns.data) { 7 | return ns.data; 8 | } 9 | } catch (err) { 10 | console.error(err); 11 | } 12 | throw new Error(`Cannot load quiz for v1/${id}`); 13 | } 14 | -------------------------------------------------------------------------------- /src/questions/v1/1.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | import { Tutorial1 } from "./tutorial/Tutorial1"; 3 | 4 | export const data: QuestionData = { 5 | type: "tutorial", 6 | contents: , 7 | }; 8 | -------------------------------------------------------------------------------- /src/questions/v1/10.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: 2 / span 2; 10 | grid-row: span 2 / auto; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | gridStyle, 16 | itemStyle, 17 | gridDef: { 18 | rows: 4, 19 | columns: 4, 20 | }, 21 | answer: ["2,1", "3,1", "2,2", "3,2"], 22 | }; 23 | -------------------------------------------------------------------------------- /src/questions/v1/11.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const itemStyle = ` 14 | grid-area: b; 15 | `.trim(); 16 | 17 | export const data: QuestionData = { 18 | type: "grid", 19 | gridStyle, 20 | itemStyle, 21 | gridDef: { 22 | rows: 4, 23 | columns: 4, 24 | }, 25 | answer: ["3,1", "4,1", "3,2", "4,2"], 26 | }; 27 | -------------------------------------------------------------------------------- /src/questions/v1/12.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const itemStyle = ` 14 | grid-column: b; 15 | grid-row: c; 16 | `.trim(); 17 | 18 | export const data: QuestionData = { 19 | type: "grid", 20 | gridStyle, 21 | itemStyle, 22 | gridDef: { 23 | rows: 4, 24 | columns: 4, 25 | }, 26 | answer: ["3,3", "4,3", "3,4", "4,4"], 27 | }; 28 | -------------------------------------------------------------------------------- /src/questions/v1/13.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const itemStyle = ` 14 | grid-column: 2; 15 | grid-row: b / c; 16 | `.trim(); 17 | 18 | export const data: QuestionData = { 19 | type: "grid", 20 | gridStyle, 21 | itemStyle, 22 | gridDef: { 23 | rows: 4, 24 | columns: 4, 25 | }, 26 | answer: ["2,1", "2,2", "2,3", "2,4"], 27 | }; 28 | -------------------------------------------------------------------------------- /src/questions/v1/14.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const itemStyle = ` 14 | grid-column: a / span 1; 15 | grid-row: b / span 2; 16 | `.trim(); 17 | 18 | export const data: QuestionData = { 19 | type: "grid", 20 | gridStyle, 21 | itemStyle, 22 | gridDef: { 23 | rows: 4, 24 | columns: 4, 25 | }, 26 | answer: ["1,1", "1,2"], 27 | }; 28 | -------------------------------------------------------------------------------- /src/questions/v1/15.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const itemStyle = ` 14 | grid-column: span 1 / a; 15 | grid-row: c / -1; 16 | `.trim(); 17 | 18 | export const data: QuestionData = { 19 | type: "grid", 20 | gridStyle, 21 | itemStyle, 22 | gridDef: { 23 | rows: 4, 24 | columns: 4, 25 | }, 26 | answer: ["2,3", "2,4"], 27 | }; 28 | -------------------------------------------------------------------------------- /src/questions/v1/16.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const itemStyle = ` 14 | grid-column: 2 / a; 15 | grid-row: auto / b; 16 | `.trim(); 17 | 18 | export const data: QuestionData = { 19 | type: "grid", 20 | gridStyle, 21 | itemStyle, 22 | gridDef: { 23 | rows: 4, 24 | columns: 4, 25 | }, 26 | answer: ["2,2"], 27 | }; 28 | -------------------------------------------------------------------------------- /src/questions/v1/17.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [c] 1fr [d] 1fr [e]; 6 | grid-template-rows: 7 | [v] 1fr [w] 1fr [x] 1fr [y] 1fr [z]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: b / span 2; 12 | grid-row: x; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | gridStyle, 18 | itemStyle, 19 | gridDef: { 20 | rows: 4, 21 | columns: 4, 22 | }, 23 | answer: ["2,3", "3,3"], 24 | }; 25 | -------------------------------------------------------------------------------- /src/questions/v1/18.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [c] 1fr [d] 1fr [e]; 6 | grid-template-rows: 7 | [v] 1fr [w] 1fr [x] 1fr [y] 1fr [z]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: b / c; 12 | grid-row: v / y; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | gridStyle, 18 | itemStyle, 19 | gridDef: { 20 | rows: 4, 21 | columns: 4, 22 | }, 23 | answer: ["2,1", "2,2", "2,3"], 24 | }; 25 | -------------------------------------------------------------------------------- /src/questions/v1/19.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [c] 1fr [d] 1fr [e]; 6 | grid-template-rows: 7 | [v] 1fr [w] 1fr [x] 1fr [y] 1fr [z]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: a / span d; 12 | grid-row: span v / 3; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | gridStyle, 18 | itemStyle, 19 | gridDef: { 20 | rows: 4, 21 | columns: 4, 22 | }, 23 | answer: ["1,1", "2,1", "3,1", "1,2", "2,2", "3,2"], 24 | }; 25 | -------------------------------------------------------------------------------- /src/questions/v1/2.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: 3; 10 | grid-row: 2; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | gridStyle, 16 | itemStyle, 17 | gridDef: { 18 | rows: 4, 19 | columns: 4, 20 | }, 21 | answer: ["3,2"], 22 | }; 23 | -------------------------------------------------------------------------------- /src/questions/v1/20.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [a] 1fr [b] 1fr [a b]; 6 | grid-template-rows: 7 | [x] 1fr [y] 1fr [x] 1fr [y] 1fr [x y]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: a / span 2; 12 | grid-row: y; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | gridStyle, 18 | itemStyle, 19 | gridDef: { 20 | rows: 4, 21 | columns: 4, 22 | }, 23 | answer: ["1,2", "2,2"], 24 | }; 25 | -------------------------------------------------------------------------------- /src/questions/v1/21.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [a] 1fr [b] 1fr [a b]; 6 | grid-template-rows: 7 | [x] 1fr [y] 1fr [x] 1fr [y] 1fr [x y]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: a 2; 12 | grid-row: x / y; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | gridStyle, 18 | itemStyle, 19 | gridDef: { 20 | rows: 4, 21 | columns: 4, 22 | }, 23 | answer: ["3,1"], 24 | }; 25 | -------------------------------------------------------------------------------- /src/questions/v1/22.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [a] 1fr [b] 1fr [a b]; 6 | grid-template-rows: 7 | [x] 1fr [y] 1fr [x] 1fr [y] 1fr [x y]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: a 2 / b 3; 12 | grid-row: x / span y 3; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | gridStyle, 18 | itemStyle, 19 | gridDef: { 20 | rows: 4, 21 | columns: 4, 22 | }, 23 | answer: ["3,1", "4,1", "3,2", "4,2", "3,3", "4,3", "3,4", "4,4"], 24 | }; 25 | -------------------------------------------------------------------------------- /src/questions/v1/23.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [a] 1fr [b] 1fr [a b]; 6 | grid-template-rows: 7 | [x] 1fr [y] 1fr [x] 1fr [y] 1fr [x y]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: a / b -2; 12 | grid-row: 2 / span x; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | gridStyle, 18 | itemStyle, 19 | gridDef: { 20 | rows: 4, 21 | columns: 4, 22 | }, 23 | answer: ["1,2", "2,2", "3,2"], 24 | }; 25 | -------------------------------------------------------------------------------- /src/questions/v1/24.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [a] 1fr [b] 1fr [a b]; 6 | grid-template-rows: 7 | [x] 1fr [y] 1fr [x] 1fr [y] 1fr [x y]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: 4 / 2; 12 | grid-row: 2 x / 2 y; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | gridStyle, 18 | itemStyle, 19 | gridDef: { 20 | rows: 4, 21 | columns: 4, 22 | }, 23 | answer: ["2,3", "3,3"], 24 | }; 25 | -------------------------------------------------------------------------------- /src/questions/v1/25.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const itemStyle = ` 14 | grid-column: 2 / a; 15 | grid-row: b-end / span 2; 16 | `.trim(); 17 | 18 | export const data: QuestionData = { 19 | type: "grid", 20 | gridStyle, 21 | itemStyle, 22 | gridDef: { 23 | rows: 4, 24 | columns: 4, 25 | }, 26 | answer: ["2,3", "2,4"], 27 | }; 28 | -------------------------------------------------------------------------------- /src/questions/v1/26.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | import { Tutorial2 } from "./tutorial/Tutorial2"; 3 | 4 | export const data: QuestionData = { 5 | type: "tutorial", 6 | contents: , 7 | }; 8 | -------------------------------------------------------------------------------- /src/questions/v1/27.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: 4 / 6; 10 | grid-row: 3; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | extensible: true, 16 | gridStyle, 17 | itemStyle, 18 | gridDef: { 19 | rows: 4, 20 | columns: 4, 21 | }, 22 | answer: ["4,3", "5,3"], 23 | }; 24 | -------------------------------------------------------------------------------- /src/questions/v1/28.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: span 3 / -4; 10 | grid-row: 2 / 4; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | extensible: true, 16 | gridStyle, 17 | itemStyle, 18 | gridDef: { 19 | rows: 4, 20 | columns: 4, 21 | }, 22 | answer: ["-1,2", "0,2", "1,2", "-1,3", "0,3", "1,3"], 23 | }; 24 | -------------------------------------------------------------------------------- /src/questions/v1/29.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: 8; 10 | grid-row: 6; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | extensible: true, 16 | gridStyle, 17 | itemStyle, 18 | gridDef: { 19 | rows: 4, 20 | columns: 4, 21 | }, 22 | answer: ["8,6"], 23 | }; 24 | -------------------------------------------------------------------------------- /src/questions/v1/3.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: 1 / 3; 10 | grid-row: 2 / 3; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | gridStyle, 16 | itemStyle, 17 | gridDef: { 18 | rows: 4, 19 | columns: 4, 20 | }, 21 | answer: ["1,2", "2,2"], 22 | }; 23 | -------------------------------------------------------------------------------- /src/questions/v1/30.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [a] 1fr [b] 1fr [a b]; 6 | grid-template-rows: 7 | [x] 1fr [y] 1fr [x] 1fr [y] 1fr [x y]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: span 2 / a; 12 | grid-row: y / span y; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | extensible: true, 18 | gridStyle, 19 | itemStyle, 20 | gridDef: { 21 | rows: 4, 22 | columns: 4, 23 | }, 24 | answer: ["-1,2", "0,2", "-1,3", "0,3"], 25 | }; 26 | -------------------------------------------------------------------------------- /src/questions/v1/31.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [a] 1fr [b] 1fr [a b]; 6 | grid-template-rows: 7 | [x] 1fr [y] 1fr [x] 1fr [y] 1fr [x y]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: a 2 / a 4; 12 | grid-row: x -5; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | extensible: true, 18 | gridStyle, 19 | itemStyle, 20 | gridDef: { 21 | rows: 4, 22 | columns: 4, 23 | }, 24 | answer: ["3,-1", "4,-1", "5,-1"], 25 | }; 26 | -------------------------------------------------------------------------------- /src/questions/v1/32.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [a] 1fr [b] 1fr [a b]; 6 | grid-template-rows: 7 | [x] 1fr [y] 1fr [x] 1fr [y] 1fr [x y]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: c -1 / span c; 12 | grid-row: 2; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | extensible: true, 18 | gridStyle, 19 | itemStyle, 20 | gridDef: { 21 | rows: 4, 22 | columns: 4, 23 | }, 24 | answer: ["0,2", "1,2", "2,2", "3,2", "4,2", "5,2"], 25 | }; 26 | -------------------------------------------------------------------------------- /src/questions/v1/33.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [a] 1fr [b] 1fr [a b]; 6 | grid-template-rows: 7 | [x] 1fr [y] 1fr [x] 1fr [y] 1fr [x y]; 8 | `.trim(); 9 | 10 | const itemStyle = ` 11 | grid-column: span a 2 / 2; 12 | grid-row: -2 z; 13 | `.trim(); 14 | 15 | export const data: QuestionData = { 16 | type: "grid", 17 | extensible: true, 18 | gridStyle, 19 | itemStyle, 20 | gridDef: { 21 | rows: 4, 22 | columns: 4, 23 | }, 24 | answer: ["0,-1", "1,-1"], 25 | }; 26 | -------------------------------------------------------------------------------- /src/questions/v1/34.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const itemStyle = ` 14 | grid-column: 2 / span a 2; 15 | grid-row: -2 b; 16 | `.trim(); 17 | 18 | export const data: QuestionData = { 19 | type: "grid", 20 | extensible: true, 21 | gridStyle, 22 | itemStyle, 23 | gridDef: { 24 | rows: 4, 25 | columns: 4, 26 | }, 27 | answer: ["2,-1", "3,-1", "4,-1", "5,-1", "6,-1"], 28 | }; 29 | -------------------------------------------------------------------------------- /src/questions/v1/35.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const itemStyle = ` 14 | grid-column: a-end / span a-start; 15 | grid-row: c 2 / c 4; 16 | `.trim(); 17 | 18 | export const data: QuestionData = { 19 | type: "grid", 20 | extensible: true, 21 | gridStyle, 22 | itemStyle, 23 | gridDef: { 24 | rows: 4, 25 | columns: 4, 26 | }, 27 | answer: ["3,7", "4,7", "5,7", "3,8", "4,8", "5,8"], 28 | }; 29 | -------------------------------------------------------------------------------- /src/questions/v1/36.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: [a] 1fr [b] 1fr [c]; 5 | grid-template-rows: [a] 1fr [b] 1fr [c]; 6 | grid-template-areas: 7 | "c b" 8 | "x a"; 9 | `.trim(); 10 | 11 | const itemStyle = ` 12 | grid-column: a; 13 | grid-row: b; 14 | `.trim(); 15 | 16 | export const data: QuestionData = { 17 | type: "grid", 18 | extensible: true, 19 | gridStyle, 20 | itemStyle, 21 | gridDef: { 22 | rows: 2, 23 | columns: 2, 24 | }, 25 | answer: ["2,1"], 26 | }; 27 | -------------------------------------------------------------------------------- /src/questions/v1/37.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: [a] 1fr [b] 1fr [c]; 5 | grid-template-rows: [a] 1fr [b] 1fr [c]; 6 | grid-template-areas: 7 | "c b" 8 | "x a"; 9 | `.trim(); 10 | 11 | const itemStyle = ` 12 | grid-column: c / 1 c; 13 | grid-row: b 1 / span a; 14 | `.trim(); 15 | 16 | export const data: QuestionData = { 17 | type: "grid", 18 | extensible: true, 19 | gridStyle, 20 | itemStyle, 21 | gridDef: { 22 | rows: 2, 23 | columns: 2, 24 | }, 25 | answer: ["1,2", "2,2", "1,3", "2,3"], 26 | }; 27 | -------------------------------------------------------------------------------- /src/questions/v1/38.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: [a] 1fr [b] 1fr [c]; 5 | grid-template-rows: [a] 1fr [b] 1fr [c]; 6 | grid-template-areas: 7 | "c b" 8 | "x a"; 9 | `.trim(); 10 | 11 | const itemStyle = ` 12 | grid-column: span x / b-end; 13 | grid-row: x-start; 14 | `.trim(); 15 | 16 | export const data: QuestionData = { 17 | type: "grid", 18 | extensible: true, 19 | gridStyle, 20 | itemStyle, 21 | gridDef: { 22 | rows: 2, 23 | columns: 2, 24 | }, 25 | answer: ["0,2", "1,2", "2,2"], 26 | }; 27 | -------------------------------------------------------------------------------- /src/questions/v1/39.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | import { Tutorial3 } from "./tutorial/Tutorial3"; 3 | 4 | export const data: QuestionData = { 5 | type: "tutorial", 6 | contents: , 7 | }; 8 | -------------------------------------------------------------------------------- /src/questions/v1/4.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: 1 / -2; 10 | grid-row: 4; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | gridStyle, 16 | itemStyle, 17 | gridDef: { 18 | rows: 4, 19 | columns: 4, 20 | }, 21 | answer: ["1,4", "2,4", "3,4"], 22 | }; 23 | -------------------------------------------------------------------------------- /src/questions/v1/40.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(5, 1fr); 5 | grid-template-rows: repeat(5, 1fr); 6 | `.trim(); 7 | 8 | const subgridStyle = ` 9 | grid-column: 2 / span 3; 10 | grid-row: 2 / span 3; 11 | grid-template-columns: subgrid; 12 | grid-template-rows: subgrid; 13 | `.trim(); 14 | 15 | const itemStyle = ` 16 | grid-column: 2; 17 | grid-row: 1 / 3; 18 | `.trim(); 19 | 20 | export const data: QuestionData = { 21 | type: "grid", 22 | gridStyle, 23 | subgridStyle, 24 | itemStyle, 25 | gridDef: { 26 | rows: 5, 27 | columns: 5, 28 | }, 29 | answer: ["3,2", "3,3"], 30 | }; 31 | -------------------------------------------------------------------------------- /src/questions/v1/41.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(5, 1fr); 5 | grid-template-rows: repeat(5, 1fr); 6 | `.trim(); 7 | 8 | const subgridStyle = ` 9 | grid-column: 2 / span 3; 10 | grid-row: 2 / span 3; 11 | grid-template-columns: subgrid; 12 | grid-template-rows: subgrid; 13 | `.trim(); 14 | 15 | const itemStyle = ` 16 | grid-column: 2 / span 3; 17 | grid-row: 2; 18 | `.trim(); 19 | 20 | export const data: QuestionData = { 21 | type: "grid", 22 | gridStyle, 23 | subgridStyle, 24 | itemStyle, 25 | gridDef: { 26 | rows: 5, 27 | columns: 5, 28 | }, 29 | answer: ["3,3", "4,3"], 30 | }; 31 | -------------------------------------------------------------------------------- /src/questions/v1/42.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(5, 1fr); 5 | grid-template-rows: repeat(5, 1fr); 6 | `.trim(); 7 | 8 | const subgridStyle = ` 9 | grid-column: 2 / span 3; 10 | grid-row: 2 / span 3; 11 | grid-template-columns: subgrid; 12 | grid-template-rows: subgrid; 13 | `.trim(); 14 | 15 | const itemStyle = ` 16 | grid-column: span 2 / -2; 17 | grid-row: -2 / -4; 18 | `.trim(); 19 | 20 | export const data: QuestionData = { 21 | type: "grid", 22 | gridStyle, 23 | subgridStyle, 24 | itemStyle, 25 | gridDef: { 26 | rows: 5, 27 | columns: 5, 28 | }, 29 | answer: ["2,2", "3,2", "2,3", "3,3"], 30 | }; 31 | -------------------------------------------------------------------------------- /src/questions/v1/43.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(5, 1fr); 5 | grid-template-rows: repeat(5, 1fr); 6 | `.trim(); 7 | 8 | const subgridStyle = ` 9 | grid-column: 2 / span 3; 10 | grid-row: 2 / span 3; 11 | grid-template-columns: subgrid [a] [b] [c] [d]; 12 | grid-template-rows: subgrid [w] [x] [y] [z]; 13 | `.trim(); 14 | 15 | const itemStyle = ` 16 | grid-column: a / c; 17 | grid-row: x / z; 18 | `.trim(); 19 | 20 | export const data: QuestionData = { 21 | type: "grid", 22 | gridStyle, 23 | subgridStyle, 24 | itemStyle, 25 | gridDef: { 26 | rows: 5, 27 | columns: 5, 28 | }, 29 | answer: ["2,3", "3,3", "2,4", "3,4"], 30 | }; 31 | -------------------------------------------------------------------------------- /src/questions/v1/44.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(5, 1fr); 5 | grid-template-rows: repeat(5, 1fr); 6 | `.trim(); 7 | 8 | const subgridStyle = ` 9 | grid-column: 2 / span 3; 10 | grid-row: 2 / span 3; 11 | grid-template-columns: subgrid [a] [b] [c] [d]; 12 | grid-template-rows: subgrid [w] [x] [y] [z]; 13 | `.trim(); 14 | 15 | const itemStyle = ` 16 | grid-column: a / a 2; 17 | grid-row: span w / y; 18 | `.trim(); 19 | 20 | export const data: QuestionData = { 21 | type: "grid", 22 | gridStyle, 23 | subgridStyle, 24 | itemStyle, 25 | gridDef: { 26 | rows: 5, 27 | columns: 5, 28 | }, 29 | answer: ["2,2", "3,2", "4,2", "2,3", "3,3", "4,3"], 30 | }; 31 | -------------------------------------------------------------------------------- /src/questions/v1/45.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [c] 1fr [d] 1fr [e]; 6 | grid-template-rows: 7 | [v] 1fr [w] 1fr [x] 1fr [y] 1fr [z]; 8 | `.trim(); 9 | 10 | const subgridStyle = ` 11 | grid-column: b / e; 12 | grid-row: v / y; 13 | grid-template-columns: subgrid; 14 | grid-template-rows: subgrid; 15 | `.trim(); 16 | 17 | const itemStyle = ` 18 | grid-column: b / d; 19 | grid-row: z / w; 20 | `.trim(); 21 | 22 | export const data: QuestionData = { 23 | type: "grid", 24 | gridStyle, 25 | subgridStyle, 26 | itemStyle, 27 | gridDef: { 28 | rows: 4, 29 | columns: 4, 30 | }, 31 | answer: ["2,2", "3,2", "2,3", "3,3"], 32 | }; 33 | -------------------------------------------------------------------------------- /src/questions/v1/46.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [c] 1fr [d] 1fr [e]; 6 | grid-template-rows: 7 | [v] 1fr [w] 1fr [x] 1fr [y] 1fr [z]; 8 | `.trim(); 9 | 10 | const subgridStyle = ` 11 | grid-column: b / e; 12 | grid-row: v / y; 13 | grid-template-columns: subgrid [f] [] [g]; 14 | grid-template-rows: subgrid [] [h] [] [i]; 15 | `.trim(); 16 | 17 | const itemStyle = ` 18 | grid-column: c / g; 19 | grid-row: h / span 1 i; 20 | `.trim(); 21 | 22 | export const data: QuestionData = { 23 | type: "grid", 24 | gridStyle, 25 | subgridStyle, 26 | itemStyle, 27 | gridDef: { 28 | rows: 4, 29 | columns: 4, 30 | }, 31 | answer: ["3,2", "3,3"], 32 | }; 33 | -------------------------------------------------------------------------------- /src/questions/v1/47.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: 5 | [a] 1fr [b] 1fr [c] 1fr [d] 1fr [e]; 6 | grid-template-rows: 7 | [v] 1fr [w] 1fr [x] 1fr [y] 1fr [z]; 8 | `.trim(); 9 | 10 | const subgridStyle = ` 11 | grid-column: c / -1; 12 | grid-row: v / -2; 13 | grid-template-columns: subgrid [] [f]; 14 | grid-template-rows: subgrid [] [h] [i]; 15 | `.trim(); 16 | 17 | const itemStyle = ` 18 | grid-column: f; 19 | grid-row: h / i; 20 | `.trim(); 21 | 22 | export const data: QuestionData = { 23 | type: "grid", 24 | gridStyle, 25 | subgridStyle, 26 | itemStyle, 27 | gridDef: { 28 | rows: 4, 29 | columns: 4, 30 | }, 31 | answer: ["4,2"], 32 | }; 33 | -------------------------------------------------------------------------------- /src/questions/v1/48.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const subgridStyle = ` 14 | grid-column: a; 15 | grid-row: a; 16 | grid-template-columns: subgrid; 17 | grid-template-rows: subgrid; 18 | `.trim(); 19 | 20 | const itemStyle = ` 21 | grid-column: a / b; 22 | grid-row: 2 / span 2; 23 | `.trim(); 24 | 25 | export const data: QuestionData = { 26 | type: "grid", 27 | gridStyle, 28 | subgridStyle, 29 | itemStyle, 30 | gridDef: { 31 | rows: 4, 32 | columns: 4, 33 | }, 34 | answer: ["1,2", "2,2", "1,3", "2,3"], 35 | }; 36 | -------------------------------------------------------------------------------- /src/questions/v1/49.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const subgridStyle = ` 14 | grid-column: 2 / span 2; 15 | grid-row: 2 / span 2; 16 | grid-template-columns: subgrid; 17 | grid-template-rows: subgrid; 18 | `.trim(); 19 | 20 | const itemStyle = ` 21 | grid-column: a; 22 | grid-row: b / c-end; 23 | `.trim(); 24 | 25 | export const data: QuestionData = { 26 | type: "grid", 27 | gridStyle, 28 | subgridStyle, 29 | itemStyle, 30 | gridDef: { 31 | rows: 4, 32 | columns: 4, 33 | }, 34 | answer: ["2,2", "2,3"], 35 | }; 36 | -------------------------------------------------------------------------------- /src/questions/v1/5.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: 2 / span 2; 10 | grid-row: 1 / 3; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | gridStyle, 16 | itemStyle, 17 | gridDef: { 18 | rows: 4, 19 | columns: 4, 20 | }, 21 | answer: ["2,1", "3,1", "2,2", "3,2"], 22 | }; 23 | -------------------------------------------------------------------------------- /src/questions/v1/50.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const subgridStyle = ` 14 | grid-column: 2 / span 2; 15 | grid-row: 2 / span 2; 16 | grid-template-columns: subgrid; 17 | grid-template-rows: subgrid; 18 | grid-template-areas: 19 | "b a" 20 | "b a"; 21 | `.trim(); 22 | 23 | const itemStyle = ` 24 | grid-column: a; 25 | grid-row: c; 26 | `.trim(); 27 | 28 | export const data: QuestionData = { 29 | type: "grid", 30 | gridStyle, 31 | subgridStyle, 32 | itemStyle, 33 | gridDef: { 34 | rows: 4, 35 | columns: 4, 36 | }, 37 | answer: ["2,3"], 38 | }; 39 | -------------------------------------------------------------------------------- /src/questions/v1/51.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const subgridStyle = ` 14 | grid-column: 2 / span 3; 15 | grid-row: 2 / span 3; 16 | grid-template-columns: subgrid; 17 | grid-template-rows: subgrid; 18 | grid-template-areas: 19 | "b a a" 20 | "b a a" 21 | "b a a; 22 | `.trim(); 23 | 24 | const itemStyle = ` 25 | grid-column: a-start 2; 26 | grid-row: span 2 / b-end -1; 27 | `.trim(); 28 | 29 | export const data: QuestionData = { 30 | type: "grid", 31 | gridStyle, 32 | subgridStyle, 33 | itemStyle, 34 | gridDef: { 35 | rows: 4, 36 | columns: 4, 37 | }, 38 | answer: ["3,3", "3,4"], 39 | }; 40 | -------------------------------------------------------------------------------- /src/questions/v1/52.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | grid-template-areas: 7 | "a a b b" 8 | "a a b b" 9 | "a a c c" 10 | "a a c c"; 11 | `.trim(); 12 | 13 | const subgridStyle = ` 14 | grid-column: 2 / span 3; 15 | grid-row: 2 / span 3; 16 | grid-template-columns: subgrid [] [c-start]; 17 | grid-template-rows: subgrid [] [] [b-end]; 18 | grid-template-areas: 19 | "b a a" 20 | "b a a" 21 | "b a a; 22 | `.trim(); 23 | 24 | const itemStyle = ` 25 | grid-column: c-start 2; 26 | grid-row: 1 / b-end 2; 27 | `.trim(); 28 | 29 | export const data: QuestionData = { 30 | type: "grid", 31 | gridStyle, 32 | subgridStyle, 33 | itemStyle, 34 | gridDef: { 35 | rows: 4, 36 | columns: 4, 37 | }, 38 | answer: ["4,2", "4,3"], 39 | }; 40 | -------------------------------------------------------------------------------- /src/questions/v1/53.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | import { Congratulations } from "./tutorial/Congratulations"; 3 | 4 | export const data: QuestionData = { 5 | type: "tutorial", 6 | contents: , 7 | noNext: true, 8 | }; 9 | -------------------------------------------------------------------------------- /src/questions/v1/6.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: span 2 / 4; 10 | grid-row: 1 / span 3; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | gridStyle, 16 | itemStyle, 17 | gridDef: { 18 | rows: 4, 19 | columns: 4, 20 | }, 21 | answer: ["2,1", "3,1", "2,2", "3,2", "2,3", "3,3"], 22 | }; 23 | -------------------------------------------------------------------------------- /src/questions/v1/7.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-area: 1 / 2 / 3 / 4; 10 | `.trim(); 11 | 12 | export const data: QuestionData = { 13 | type: "grid", 14 | gridStyle, 15 | itemStyle, 16 | gridDef: { 17 | rows: 4, 18 | columns: 4, 19 | }, 20 | answer: ["2,1", "3,1", "2,2", "3,2"], 21 | }; 22 | -------------------------------------------------------------------------------- /src/questions/v1/8.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: auto / 3; 10 | grid-row: 3 / auto; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | gridStyle, 16 | itemStyle, 17 | gridDef: { 18 | rows: 4, 19 | columns: 4, 20 | }, 21 | answer: ["2,3"], 22 | }; 23 | -------------------------------------------------------------------------------- /src/questions/v1/9.tsx: -------------------------------------------------------------------------------- 1 | import { QuestionData } from "../QuestionData"; 2 | 3 | const gridStyle = ` 4 | grid-template-columns: repeat(4, 1fr); 5 | grid-template-rows: repeat(4, 1fr); 6 | `.trim(); 7 | 8 | const itemStyle = ` 9 | grid-column: auto; 10 | grid-row: auto / span 4; 11 | `.trim(); 12 | 13 | export const data: QuestionData = { 14 | type: "grid", 15 | gridStyle, 16 | itemStyle, 17 | gridDef: { 18 | rows: 4, 19 | columns: 4, 20 | }, 21 | answer: ["1,1", "1,2", "1,3", "1,4"], 22 | }; 23 | -------------------------------------------------------------------------------- /src/questions/v1/tutorial/Congratulations.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | import { appUrl } from "../../../const"; 3 | import { useI18n } from "../../../utils/i18n/LanguageContext"; 4 | import classes from "./Tutorial.module.css"; 5 | 6 | export const Congratulations: React.VFC = () => { 7 | const shareText = useI18n({ 8 | en: "I solved all the problems in CSS Grid Mastery Quiz!\n", 9 | ja: "CSS Grid Mastery Quizで全ての問題をクリアしました!\n", 10 | }); 11 | 12 | const twitterIntent = useMemo(() => { 13 | return `https://twitter.com/intent/tweet?text=${encodeURIComponent( 14 | shareText 15 | )}&url=${encodeURIComponent(appUrl)}`; 16 | }, [shareText]); 17 | const contents = useI18n({ 18 | en: ( 19 | <> 20 |

Congratulations!

21 |

You solved all the problems in CSS Grid Mastery Quiz.

22 |

23 | You are now CSS Grid Master! 24 |

25 |

26 | 32 | Share on Twitter 33 | 34 |

35 | 36 | ), 37 | ja: ( 38 | <> 39 |

完全制覇!

40 |

41 | おめでとうございます! CSS Grid Mastery 42 | Quizの全ての問題をクリアしました。 43 |

44 |

45 | あなたはCSS Gridマスターです! 46 |

47 |

48 | 54 | Twitterでシェアする 55 | 56 |

57 | 58 | ), 59 | }); 60 | return
{contents}
; 61 | }; 62 | -------------------------------------------------------------------------------- /src/questions/v1/tutorial/Tutorial.module.css: -------------------------------------------------------------------------------- 1 | .tutorial h1 { 2 | font-size: 2em; 3 | } 4 | 5 | .sampleGridWrapper { 6 | width: 300px; 7 | } 8 | 9 | .shareOnTwitter { 10 | background-color: #00aced; 11 | color: #ffffff; 12 | font-size: 1.5em; 13 | font-weight: bold; 14 | padding: 0.5em; 15 | border-radius: 0.5em; 16 | text-decoration: none; 17 | } -------------------------------------------------------------------------------- /src/questions/v1/tutorial/Tutorial1.tsx: -------------------------------------------------------------------------------- 1 | import { GridArea } from "../../../pages/QuestionPage/components/GridArea"; 2 | import { useGridItemSelection } from "../../../pages/QuestionPage/logic/useGridItemSelection"; 3 | import { useI18n } from "../../../utils/i18n/LanguageContext"; 4 | import classes from "./Tutorial.module.css"; 5 | 6 | export const Tutorial1: React.VFC = () => { 7 | const { gridDef, selectedItems, toggleItem } = useSampleGrid(); 8 | 9 | const code1 = ( 10 |
 11 |       
 12 |         {`
 13 | 
14 |
15 |
16 | `.trim()} 17 |
18 |
19 | ); 20 | const code2 = ( 21 |
 22 |       
 23 |         {`
 24 | .grid-container {
 25 |   display: grid;
 26 |   grid-template-columns: 1fr 1fr;
 27 |   grid-template-rows: 1fr 1fr;
 28 | }
 29 | .grid-item {
 30 |   grid-column: 1;
 31 |   grid-row: 1;
 32 | }
 33 | `.trim()}
 34 |       
 35 |     
36 | ); 37 | const sample = ( 38 |
39 | 45 |
46 | ); 47 | 48 | const contents = useI18n({ 49 | en: ( 50 | <> 51 |

Tutorial #1

52 |

Consider the following HTML structure:

53 | {code1} 54 |

You are given style definitions for above elements. Example:

55 | {code2} 56 |

57 | Click/tap all grid cells occupied by the .grid-item{" "} 58 | element. Press “Check” button to check your answer. 59 |

60 |

Sample:

61 | {sample} 62 |

After making three mistakes, a “Cheat” button appears.

63 | 64 | ), 65 | ja: ( 66 | <> 67 |

チュートリアル #1

68 |

ここでは、次のHTML構造を考えます。

69 | {code1} 70 |

これに対して、次の例のようにスタイル定義が与えられます。

71 | {code2} 72 |

73 | .grid-item 74 | 要素が占めるすべてのグリッドセルをクリック/タップしてください。 75 | その後“Check”ボタンを押して答えが合っているか確かめましょう。 76 |

77 |

例:

78 | {sample} 79 |

3回間違えると“Cheat”ボタンが出現します。

80 | 81 | ), 82 | }); 83 | 84 | return
{contents}
; 85 | }; 86 | 87 | function useSampleGrid() { 88 | const gridDef = { 89 | columns: 2, 90 | rows: 2, 91 | }; 92 | const { selectedItems, toggleItem } = useGridItemSelection([], ["1,1"]); 93 | 94 | return { 95 | gridDef, 96 | selectedItems, 97 | toggleItem, 98 | }; 99 | } 100 | -------------------------------------------------------------------------------- /src/questions/v1/tutorial/Tutorial2.tsx: -------------------------------------------------------------------------------- 1 | import { GridArea } from "../../../pages/QuestionPage/components/GridArea"; 2 | import { GridAreaExtensionControl } from "../../../pages/QuestionPage/components/GridAreaExtensionControl"; 3 | import { useGridExtension } from "../../../pages/QuestionPage/logic/useGridExtension"; 4 | import { useGridItemSelection } from "../../../pages/QuestionPage/logic/useGridItemSelection"; 5 | import { useI18n } from "../../../utils/i18n/LanguageContext"; 6 | import classes from "./Tutorial.module.css"; 7 | 8 | export const Tutorial2: React.VFC = () => { 9 | const { gridDef, selectedItems, extension, toggleItem, extend } = 10 | useSampleGrid(); 11 | 12 | const sample = ( 13 |
14 | 15 | 26 | 27 |
28 | ); 29 | 30 | const contents = useI18n({ 31 | en: ( 32 | <> 33 |

Tutorial #2

34 |

35 | When grid items are placed outside of the grid area explicitly deined 36 | by grid-template-columns and{" "} 37 | grid-template-rows,implicit grid tracks and{" "} 38 | implicit grid lines are generated outside of explicit grid 39 | tracks. 40 |

41 |

42 | From now on, the correct answer may enter those implicit grid cells. 43 |

44 |

45 | To specify such cells, you need to extend the displayed grid 46 | by pressing the plus buttons placed at each edge. 47 |

48 |

49 | To remove extended cells, you need to reset the state of the grid by 50 | pressing “Reset” button. 51 |

52 |

Sample:

53 | {sample} 54 | 55 | ), 56 | ja: ( 57 | <> 58 |

チュートリアル #2

59 |

60 | グリッドアイテムがgrid-template-columnsおよび 61 | grid-template-rows 62 | によって明示的に定義されたグリッドエリアの外に配置された場合、 63 | 明示的なグリッドエリアの外側に暗黙のグリッドトラック 64 | が生成されます。 65 |

66 |

67 | これからは、正しい答えが初期表示されているグリッドの範囲を超えて暗黙のグリッドトラックにまたがる可能性があります。 68 |

69 |

70 | そのような回答をするためには、表示されているグリッドを、各辺に配置されたプラスボタンを押すことで拡張する必要があります。 71 |

72 |

73 | 拡張したグリッドセルを元に戻すには、“Reset”ボタンを押して画面を初期化する必要があります。 74 |

75 |

拡張できるグリッドセルのサンプル:

76 | {sample} 77 | 78 | ), 79 | }); 80 | return
{contents}
; 81 | }; 82 | 83 | function useSampleGrid() { 84 | const gridDef = { 85 | columns: 2, 86 | rows: 2, 87 | }; 88 | const { selectedItems, toggleItem } = useGridItemSelection([]); 89 | const { extension, extend } = useGridExtension([]); 90 | 91 | return { 92 | gridDef, 93 | selectedItems, 94 | extension, 95 | toggleItem, 96 | extend, 97 | }; 98 | } 99 | -------------------------------------------------------------------------------- /src/questions/v1/tutorial/Tutorial3.tsx: -------------------------------------------------------------------------------- 1 | import { GridArea } from "../../../pages/QuestionPage/components/GridArea"; 2 | import { useGridItemSelection } from "../../../pages/QuestionPage/logic/useGridItemSelection"; 3 | import { useI18n } from "../../../utils/i18n/LanguageContext"; 4 | import classes from "./Tutorial.module.css"; 5 | 6 | export const Tutorial3: React.VFC = () => { 7 | const { gridDef, selectedItems, toggleItem } = useSampleGrid(); 8 | 9 | const code1 = ( 10 |
 11 |       
 12 |         {`
 13 | 
14 |
15 |
16 |
17 |
18 | `.trim()} 19 |
20 |
21 | ); 22 | 23 | const code2 = ( 24 |
 25 |       
 26 |         {`
 27 | .grid-container {
 28 |   display: grid;
 29 |   grid-template-columns: 1fr 1fr 1fr;
 30 |   grid-template-rows: 1fr 1fr 1fr;
 31 | }
 32 | .subgrid {
 33 |   grid-column: 2 / 4;
 34 |   grid-rows: 1 / 3;
 35 |   display: grid;
 36 |   grid-template-columns: subgrid;
 37 |   grid-template-rows: subgrid;
 38 | }
 39 | .grid-item {
 40 |   grid-column: 1;
 41 |   grid-row: 1;
 42 | }
 43 | `.trim()}
 44 |       
 45 |     
46 | ); 47 | 48 | const sample = ( 49 |
50 | 60 |
61 | ); 62 | 63 | const contents = useI18n({ 64 | en: ( 65 | <> 66 |

Tutorial #3

67 |

68 | A recent version of CSS Grid has the concept of subgrid. A 69 | subgrid itself is a grid item of the parent grid container and also 70 | has its own grid in it. An axis declared as subgrid inherit grid lines 71 | from the parent grid, still maintaining its own coordinate system. 72 |

73 |

From now on, let's consider the following HTML structure:

74 | {code1} 75 |

76 | Now you are given style definitions for these three elements. Example: 77 |

78 | {code2} 79 |

80 | You still need to answer the grid cells occupied by the element at the 81 | bottom of tree, namely .grid-item one. Note that the 82 | coordinates displayed in the answering grid is ones of the parent grid 83 | container (.grid-container). 84 |

85 |

Sample (answer for the above style):

86 | {sample} 87 | 88 | ), 89 | ja: ( 90 | <> 91 |

チュートリアル #3

92 |

93 | 最近のバージョンのCSS Gridには、サブグリッド 94 | という概念があります。サブグリッドは、親のグリッドに 95 | 属するグリッドアイテムでありつつ、自身もグリッドコンテナです。 96 | サブグリッドとして宣言された軸は、親のグリッドトラックをそのまま継承しますが、独自の座標軸を持っています。 97 |

98 |

今後の問題では、次のようなHTML構造を考えます。

99 | {code1} 100 |

101 | そして、次の例のように、これら3つの要素に対するスタイルが与えられます。 102 |

103 | {code2} 104 |

105 | これまで通り、ツリーの末端にある.grid-item 106 | 要素が占めるグリッドセルを解答してください。解答用のグリッドに表示されている座標は親グリッド( 107 | .grid-container)のものである点に注意してください。 108 |

109 |

例(上記の例に対する解答):

110 | {sample} 111 | 112 | ), 113 | }); 114 | 115 | return
{contents}
; 116 | }; 117 | 118 | function useSampleGrid() { 119 | const gridDef = { 120 | columns: 3, 121 | rows: 3, 122 | }; 123 | const { selectedItems, toggleItem } = useGridItemSelection([], ["2,1"]); 124 | 125 | return { 126 | gridDef, 127 | selectedItems, 128 | toggleItem, 129 | }; 130 | } 131 | -------------------------------------------------------------------------------- /src/utils/arrayShallowEqual.ts: -------------------------------------------------------------------------------- 1 | export function arrayShallowEqual( 2 | a: readonly unknown[], 3 | b: readonly unknown[] 4 | ) { 5 | return a.length === b.length && a.every((v, i) => v === b[i]); 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/hooks/useStateReset.ts: -------------------------------------------------------------------------------- 1 | import { useMemo, useState } from "react"; 2 | import { arrayShallowEqual } from "../arrayShallowEqual"; 3 | 4 | /** 5 | * useState, but reset when given deps has changed. 6 | */ 7 | export function useStateReset( 8 | deps: readonly unknown[], 9 | initialValue: () => T 10 | ): [state: T, update: (updater: T | ((current: T) => T)) => void] { 11 | type InternalState = { 12 | deps: readonly unknown[]; 13 | value: T; 14 | }; 15 | const [state, setStateInternal] = useState(() => ({ 16 | deps, 17 | value: initialValue(), 18 | })); 19 | 20 | const setState = (value: T | ((current: T) => T)) => { 21 | setStateInternal((current) => { 22 | if (typeof value !== "function") { 23 | return { 24 | deps, 25 | value, 26 | }; 27 | } 28 | const currentState = arrayShallowEqual(current.deps, deps) 29 | ? current.value 30 | : initialValue(); 31 | return { 32 | deps, 33 | value: (value as (current: T) => T)(currentState), 34 | }; 35 | }); 36 | }; 37 | 38 | const currentState = useMemo(() => { 39 | if (arrayShallowEqual(deps, state.deps)) { 40 | return state.value; 41 | } else { 42 | return initialValue(); 43 | } 44 | }, [state, deps]); 45 | 46 | return [currentState, setState]; 47 | } 48 | -------------------------------------------------------------------------------- /src/utils/i18n/DefineLanguageRoute.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet, useSearch } from "react-location"; 2 | import { defaultLanguage, isLanguage } from "./language"; 3 | import { LanguageProvider } from "./LanguageContext"; 4 | 5 | export const DefineLanguageRoute: React.VFC = () => { 6 | const { lang: rawLang } = useSearch<{ 7 | Search: { 8 | lang: string; 9 | }; 10 | }>(); 11 | const lang = isLanguage(rawLang) ? rawLang : defaultLanguage; 12 | 13 | return ( 14 | 15 | 16 | 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/utils/i18n/LanguageContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from "react"; 2 | import { defaultLanguage, Language } from "./language"; 3 | 4 | const LanguageContext = createContext(defaultLanguage); 5 | 6 | export const LanguageProvider = LanguageContext.Provider; 7 | 8 | export function useI18n(values: { 9 | [K in Language]: T; 10 | }): T { 11 | const language = useContext(LanguageContext); 12 | return values[language]; 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/i18n/language.ts: -------------------------------------------------------------------------------- 1 | const languages = ["en", "ja"] as const; 2 | 3 | export type Language = typeof languages[number]; 4 | 5 | export const defaultLanguage: Language = "en"; 6 | 7 | export function isLanguage(l: unknown): l is Language { 8 | return (languages as readonly unknown[]).includes(l); 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/indent.ts: -------------------------------------------------------------------------------- 1 | export function indent(str: string, space = " ") { 2 | return str.replace(/^(?!$)/gm, space); 3 | } 4 | -------------------------------------------------------------------------------- /src/utils/range.ts: -------------------------------------------------------------------------------- 1 | export function* range(start: number, end: number) { 2 | for (let i = start; i < end; i++) { 3 | yield i; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/simpleParseCss.ts: -------------------------------------------------------------------------------- 1 | export function simpleParseCss(css: string): Record { 2 | const matches = css.matchAll(/([\w-]+)\s*:\s*([^;]+);/g); 3 | return Object.fromEntries( 4 | Array.from(matches).map(([, key, value]) => [ 5 | key.replace(/-[a-z]/g, (match) => match[1].toUpperCase()), 6 | value, 7 | ]) 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": false, 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 | } 21 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------