feature/react-bare #1
					 19 changed files with 16453 additions and 1 deletions
				
			
		
							
								
								
									
										20
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | /.idea | ||||||
|  | # dependencies | ||||||
|  | /node_modules | ||||||
|  | 
 | ||||||
|  | # testing | ||||||
|  | /coverage | ||||||
|  | 
 | ||||||
|  | # production | ||||||
|  | /build | ||||||
|  | 
 | ||||||
|  | # misc | ||||||
|  | .DS_Store | ||||||
|  | .env.local | ||||||
|  | .env.development.local | ||||||
|  | .env.test.local | ||||||
|  | .env.production.local | ||||||
|  | 
 | ||||||
|  | npm-debug.log* | ||||||
|  | yarn-debug.log* | ||||||
|  | yarn-error.log* | ||||||
							
								
								
									
										9
									
								
								labyrinth-frontend.iml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								labyrinth-frontend.iml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <module type="WEB_MODULE" version="4"> | ||||||
|  |   <component name="NewModuleRootManager" inherit-compiler-output="true"> | ||||||
|  |     <exclude-output /> | ||||||
|  |     <content url="file://$MODULE_DIR$" /> | ||||||
|  |     <orderEntry type="inheritedJdk" /> | ||||||
|  |     <orderEntry type="sourceFolder" forTests="false" /> | ||||||
|  |   </component> | ||||||
|  | </module> | ||||||
							
								
								
									
										14043
									
								
								package-lock.json
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										14043
									
								
								package-lock.json
									
										
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										20
									
								
								package.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								package.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | { | ||||||
|  |   "dependencies": { | ||||||
|  |     "react": "^18.0.0", | ||||||
|  |     "react-dom": "^18.0.0", | ||||||
|  |     "react-scripts": "^5.0.0" | ||||||
|  |   }, | ||||||
|  |   "main": "/index.js", | ||||||
|  |   "devDependencies": { | ||||||
|  |     "react-scripts": "1.0.0" | ||||||
|  |   }, | ||||||
|  |   "name": "ljg0t8", | ||||||
|  |   "description": null, | ||||||
|  |   "version": "0.0.0", | ||||||
|  |   "scripts": { | ||||||
|  |     "start": "react-scripts start", | ||||||
|  |     "build": "react-scripts build", | ||||||
|  |     "test": "react-scripts test --env=jsdom", | ||||||
|  |     "eject": "react-scripts eject" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								public/favicon.ico
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/favicon.ico
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 24 KiB | 
							
								
								
									
										38
									
								
								public/index.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								public/index.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | ||||||
|  | <!doctype html> | ||||||
|  | <html lang="en"> | ||||||
|  | <head> | ||||||
|  |   <meta charset="utf-8"> | ||||||
|  |   <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||||||
|  |   <meta name="theme-color" content="#000000"> | ||||||
|  |   <!-- | ||||||
|  |     manifest.json provides metadata used when your web app is added to the | ||||||
|  |     homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/ | ||||||
|  |   --> | ||||||
|  |   <link rel="manifest" href="%PUBLIC_URL%/manifest.json"> | ||||||
|  |   <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> | ||||||
|  |   <!-- | ||||||
|  |     Notice the use of %PUBLIC_URL% in the tags above. | ||||||
|  |     It will be replaced with the URL of the `public` folder during the build. | ||||||
|  |     Only files inside the `public` folder can be referenced from the HTML. | ||||||
|  | 
 | ||||||
|  |     Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will | ||||||
|  |     work correctly both with client-side routing and a non-root public URL. | ||||||
|  |     Learn how to configure a non-root public URL by running `npm run build`. | ||||||
|  |   --> | ||||||
|  |   <title>A-Maze-R</title> | ||||||
|  |    | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |   <div id="root"></div> | ||||||
|  |   <!-- | ||||||
|  |     This HTML file is a template. | ||||||
|  |     If you open it directly in the browser, you will see an empty page. | ||||||
|  | 
 | ||||||
|  |     You can add webfonts, meta tags, or analytics to this file. | ||||||
|  |     The build step will place the bundled scripts into the <body> tag. | ||||||
|  | 
 | ||||||
|  |     To begin the development, run `npm start` or `yarn start`. | ||||||
|  |     To create a production bundle, use `npm run build` or `yarn build`. | ||||||
|  |   --> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										15
									
								
								public/manifest.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								public/manifest.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | { | ||||||
|  |   "short_name": "React App", | ||||||
|  |   "name": "Create React App Sample", | ||||||
|  |   "icons": [ | ||||||
|  |     { | ||||||
|  |       "src": "favicon.ico", | ||||||
|  |       "sizes": "192x192", | ||||||
|  |       "type": "image/png" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|  |   "start_url": "./index.html", | ||||||
|  |   "display": "standalone", | ||||||
|  |   "theme_color": "#000000", | ||||||
|  |   "background_color": "#ffffff" | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								src/App.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/App.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | import React, {useState} from 'react'; | ||||||
|  | import Maze from "./Maze"; | ||||||
|  | import InputForm from "./InputForm"; | ||||||
|  | 
 | ||||||
|  | export default function App() { | ||||||
|  |     const [maze, setMaze] = useState({}); | ||||||
|  |     let title; | ||||||
|  |     if (!!maze.grid) { | ||||||
|  |         title = <h1>The Maze ({maze.width}x{maze.height})</h1>; | ||||||
|  |     } else { | ||||||
|  |         title = <span/>; | ||||||
|  |     } | ||||||
|  |     return ( | ||||||
|  |         <div> | ||||||
|  |             <InputForm handleResult={setMaze}/> | ||||||
|  |             {title} | ||||||
|  |             <Maze labyrinth={maze} | ||||||
|  |                   showSolution={true}/> | ||||||
|  |         </div> | ||||||
|  |     ); | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								src/Cell.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/Cell.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | import React from 'react'; | ||||||
|  | 
 | ||||||
|  | export default function Cell({spec, rowIndex, cellIndex, showSolution}) { | ||||||
|  |     let classes = "cell r" + rowIndex + " c" + cellIndex; | ||||||
|  |     if (spec.top) classes += " top"; | ||||||
|  |     if (spec.right) classes += " right"; | ||||||
|  |     if (spec.bottom) classes += " bottom"; | ||||||
|  |     if (spec.left) classes += " left"; | ||||||
|  |     if (spec.solution && showSolution) classes += " solution"; | ||||||
|  |     if (spec.user) classes += " user"; | ||||||
|  |     return ( | ||||||
|  |         <div className={classes}/> | ||||||
|  |     ); | ||||||
|  | } | ||||||
							
								
								
									
										111
									
								
								src/InputForm.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/InputForm.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,111 @@ | ||||||
|  | import React, {useState} from 'react'; | ||||||
|  | import ValidatingInputNumberField from "./ValidatingInputNumberField"; | ||||||
|  | 
 | ||||||
|  | export default function InputForm({handleResult}) { | ||||||
|  |     const [width, setWidth] = useState(2); | ||||||
|  |     const [height, setHeight] = useState(2); | ||||||
|  |     const [id, setId] = useState(null); | ||||||
|  |     const [status, setStatus] = useState("typing"); // "typing", "submitting"
 | ||||||
|  | 
 | ||||||
|  |     if (status === "submitted") { | ||||||
|  |         return <span/>; | ||||||
|  |     } | ||||||
|  |     const callAPI = () => { | ||||||
|  |         let url = "https://manuel.friedli.info/labyrinth/create/json?w=" + width + | ||||||
|  |             "&h=" + height; | ||||||
|  |         if (!!id) { | ||||||
|  |             url += "&id=" + id; | ||||||
|  |         } | ||||||
|  |         fetch(url) | ||||||
|  |             .then(response => response.json()) | ||||||
|  |             .then(result => { | ||||||
|  |                 handleResult(result); | ||||||
|  |                 // FIXME doesn't update the contents of the text input field.
 | ||||||
|  |                 setId(_ => result.id); | ||||||
|  |             }) | ||||||
|  |             .catch(reason => { | ||||||
|  |                 console.error("Failed to fetch maze data.", reason); | ||||||
|  |                 // FIXME alert is not user friendly
 | ||||||
|  |                 alert("Failed to fetch maze data: " + reason); | ||||||
|  |             }) | ||||||
|  |             .finally(() => { | ||||||
|  |                 setStatus("typing"); | ||||||
|  |             }); | ||||||
|  |     }; | ||||||
|  |     const handleSubmit = (e) => { | ||||||
|  |         e.preventDefault(); | ||||||
|  |         setStatus("submitting"); | ||||||
|  |         callAPI(); | ||||||
|  |     }; | ||||||
|  |     const validateWidthHeightInput = value => { | ||||||
|  |         if (isNaN(value) || "" === value || (Math.floor(value) !== Number(value))) { | ||||||
|  |             return { | ||||||
|  |                 valid: false, | ||||||
|  |                 message: "Must be an integer greater than 1.", | ||||||
|  |                 value | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |         if (value < 1) { | ||||||
|  |             return { | ||||||
|  |                 valid: false, | ||||||
|  |                 message: "Must be greater than 1.", | ||||||
|  |                 value | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |         return { | ||||||
|  |             valid: true, | ||||||
|  |             value | ||||||
|  |         }; | ||||||
|  |     }; | ||||||
|  |     const validateIdInput = value => { | ||||||
|  |         // FIXME doesn't handle strings with characters correctly (e.g. "asdf" yields an empty value, due to "type=number").
 | ||||||
|  |         if (isNaN(value) || ("" !== value && ((Math.floor(value) !== Number(value))))) { | ||||||
|  |             return { | ||||||
|  |                 valid: false, | ||||||
|  |                 message: "Must be empty or an integer", | ||||||
|  |                 value | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |         return { | ||||||
|  |             valid: true, | ||||||
|  |             value | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     return ( | ||||||
|  |         <form onSubmit={handleSubmit}> | ||||||
|  |             <ValidatingInputNumberField id={"width"} | ||||||
|  |                                         label={"Width"} | ||||||
|  |                                         defaultValue={width} | ||||||
|  |                                         constraints={{ | ||||||
|  |                                             min: 2 | ||||||
|  |                                         }} | ||||||
|  |                                         validatorFn={validateWidthHeightInput} | ||||||
|  |                                         disabled={status === "submitting"} | ||||||
|  |                                         onChange={setWidth} | ||||||
|  |             /><br/> | ||||||
|  |             <ValidatingInputNumberField id={"height"} | ||||||
|  |                                         label={"Height"} | ||||||
|  |                                         defaultValue={height} | ||||||
|  |                                         constraints={{ | ||||||
|  |                                             min: 2 | ||||||
|  |                                         }} | ||||||
|  |                                         validatorFn={validateWidthHeightInput} | ||||||
|  |                                         disabled={status === "submitting"} | ||||||
|  |                                         onChange={setHeight} | ||||||
|  |             /><br/> | ||||||
|  |             <ValidatingInputNumberField id={"id"} | ||||||
|  |                                         label={"ID (optional)"} | ||||||
|  |                                         defaultValue={id} | ||||||
|  |                                         validatorFn={validateIdInput} | ||||||
|  |                                         disabled={status === "submitting"} | ||||||
|  |                                         onChange={setId} | ||||||
|  |             /><br/> | ||||||
|  |             <button type={"submit"} | ||||||
|  |                     disabled={status === "submitting" | ||||||
|  |                         || isNaN(width) | ||||||
|  |                         || isNaN(height) | ||||||
|  |                     }>GO! | ||||||
|  |             </button> | ||||||
|  |         </form> | ||||||
|  |     ); | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								src/Maze.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/Maze.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | import React from 'react'; | ||||||
|  | import Row from "./Row"; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export default function Maze({labyrinth, showSolution = false}) { | ||||||
|  |     if (!labyrinth.grid) { | ||||||
|  |         return <div>No valid maze.</div> | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const maze = labyrinth.grid.map((row, rowIdx) => <Row key={"r" + rowIdx} spec={row} | ||||||
|  |                                                           index={rowIdx} | ||||||
|  |                                                           showSolution={showSolution}/>); | ||||||
|  |     return ( | ||||||
|  |         <div className={"maze"}> | ||||||
|  |             {maze} | ||||||
|  |         </div> | ||||||
|  |     ); | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								src/Row.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Row.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | import React from 'react'; | ||||||
|  | import Cell from "./Cell"; | ||||||
|  | 
 | ||||||
|  | export default function Row({spec, index, showSolution}) { | ||||||
|  |     const cells = spec.map((cell, cellIdx) => <Cell key={"c" + index + "-" + cellIdx} | ||||||
|  |                                                     spec={cell} | ||||||
|  |                                                     rowIndex={index} | ||||||
|  |                                                     cellIndex={cellIdx} | ||||||
|  |                                                     showSolution={showSolution}/>) | ||||||
|  |     return ( | ||||||
|  |         <div className={"row"}> | ||||||
|  |             {cells} | ||||||
|  |         </div> | ||||||
|  |     ); | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								src/ValidatingInputNumberField.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/ValidatingInputNumberField.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | import React, {useState} from 'react'; | ||||||
|  | 
 | ||||||
|  | export default function ValidatingInputNumberField({ | ||||||
|  |                                                        id, | ||||||
|  |                                                        label, | ||||||
|  |                                                        defaultValue = 0, | ||||||
|  |                                                        constraints = {}, | ||||||
|  |                                                        validatorFn = (value) => { | ||||||
|  |                                                            return {valid: true, value}; | ||||||
|  |                                                        }, | ||||||
|  |                                                        disabled = false, | ||||||
|  |                                                        onChange = _ => { | ||||||
|  |                                                        } | ||||||
|  |                                                    }) { | ||||||
|  |     const [error, setError] = useState(null); | ||||||
|  |     const [value, setValue] = useState(defaultValue); | ||||||
|  | 
 | ||||||
|  |     const handleValueChange = (e) => { | ||||||
|  |         const value = e.target.value; | ||||||
|  |         const validation = validatorFn(value); | ||||||
|  |         if (!validation.valid) { | ||||||
|  |             setError(validation.message); | ||||||
|  |         } else { | ||||||
|  |             setError(null); | ||||||
|  |             onChange(validation.value); | ||||||
|  |         } | ||||||
|  |         setValue(validation.value); | ||||||
|  |     }; | ||||||
|  |     let errorComponent; | ||||||
|  |     if (!!error) { | ||||||
|  |         errorComponent = <span>{error}</span>; | ||||||
|  |     } else { | ||||||
|  |         errorComponent = <span/>; | ||||||
|  |     } | ||||||
|  |     return ( | ||||||
|  |         <span> | ||||||
|  |             <label htmlFor={id}>{label}: </label> | ||||||
|  |             <input id={id} | ||||||
|  |                    type={"number"} | ||||||
|  |                    onChange={handleValueChange} | ||||||
|  |                    value={value || ""} | ||||||
|  |                    min={constraints.min || null} | ||||||
|  |                    max={constraints.max || null} | ||||||
|  |                    disabled={disabled} | ||||||
|  |             /> | ||||||
|  |             {errorComponent} | ||||||
|  |         </span> | ||||||
|  |     ); | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								src/createServiceWorker.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/createServiceWorker.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | // In production, we register a service worker to serve assets from local cache.
 | ||||||
|  | 
 | ||||||
|  | // This lets the app load faster on subsequent visits in production, and gives
 | ||||||
|  | // it offline capabilities. However, it also means that developers (and users)
 | ||||||
|  | // will only see deployed updates on the "N+1" visit to a page, since previously
 | ||||||
|  | // cached resources are updated in the background.
 | ||||||
|  | 
 | ||||||
|  | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
 | ||||||
|  | // This link also includes instructions on opting out of this behavior.
 | ||||||
|  | 
 | ||||||
|  | export default function register() { | ||||||
|  |   if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { | ||||||
|  |     window.addEventListener('load', () => { | ||||||
|  |       const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; | ||||||
|  |       navigator.serviceWorker | ||||||
|  |         .register(swUrl) | ||||||
|  |         .then(registration => { | ||||||
|  |           // eslint-disable-next-line no-param-reassign
 | ||||||
|  |           registration.onupdatefound = () => { | ||||||
|  |             const installingWorker = registration.installing; | ||||||
|  |             installingWorker.onstatechange = () => { | ||||||
|  |               if (installingWorker.state === 'installed') { | ||||||
|  |                 if (navigator.serviceWorker.controller) { | ||||||
|  |                   // At this point, the old content will have been purged and
 | ||||||
|  |                   // the fresh content will have been added to the cache.
 | ||||||
|  |                   // It's the perfect time to display a "New content is
 | ||||||
|  |                   // available; please refresh." message in your web app.
 | ||||||
|  |                   console.log('New content is available; please refresh.'); // eslint-disable-line no-console
 | ||||||
|  |                 } else { | ||||||
|  |                   // At this point, everything has been precached.
 | ||||||
|  |                   // It's the perfect time to display a
 | ||||||
|  |                   // "Content is cached for offline use." message.
 | ||||||
|  |                   console.log('Content is cached for offline use.'); // eslint-disable-line no-console
 | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             }; | ||||||
|  |           }; | ||||||
|  |         }) | ||||||
|  |         .catch(error => { | ||||||
|  |           console.error('Error during service worker registration:', error); | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function unregister() { | ||||||
|  |   if ('serviceWorker' in navigator) { | ||||||
|  |     navigator.serviceWorker.ready.then(registration => { | ||||||
|  |       registration.unregister(); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								src/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/index.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | import React, { StrictMode } from "react"; | ||||||
|  | import { createRoot } from "react-dom/client"; | ||||||
|  | import "./styles.css"; | ||||||
|  | 
 | ||||||
|  | import App from "./App"; | ||||||
|  | 
 | ||||||
|  | const root = createRoot(document.getElementById("root")); | ||||||
|  | root.render( | ||||||
|  |   <StrictMode> | ||||||
|  |     <App /> | ||||||
|  |   </StrictMode> | ||||||
|  | ); | ||||||
							
								
								
									
										9
									
								
								src/package.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/package.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | { | ||||||
|  |   "dependencies": { | ||||||
|  |     "react": "^18.0.0", | ||||||
|  |     "react-dom": "^18.0.0", | ||||||
|  |     "react-scripts": "^5.0.0" | ||||||
|  |   }, | ||||||
|  |   "main": "/index.js", | ||||||
|  |   "devDependencies": {} | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								src/public/index.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/public/index.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  |   <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|  |     <title>A-Maze-R</title> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <div id="root"></div> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										134
									
								
								src/styles.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/styles.css
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | ||||||
|  | * { | ||||||
|  |     box-sizing: border-box; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.maze { | ||||||
|  |     display: table; | ||||||
|  |     border-collapse: collapse; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.row { | ||||||
|  |     display: table-row; | ||||||
|  |     margin: 0; | ||||||
|  |     padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.cell { | ||||||
|  |     display: table-cell; | ||||||
|  |     border: 1px solid transparent; | ||||||
|  |     height: 2em; | ||||||
|  |     width: 2em; | ||||||
|  |     margin: 0; | ||||||
|  |     padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.cell.solution { | ||||||
|  |     background-color: lightgray; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.cell.top { | ||||||
|  |     border-top-color: #000; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.cell.right { | ||||||
|  |     border-right-color: #000; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.cell.bottom { | ||||||
|  |     border-bottom-color: #000; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.cell.left { | ||||||
|  |     border-left-color: #000; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | input:invalid { | ||||||
|  |     border-color: #f00; | ||||||
|  | } | ||||||
|  | body { | ||||||
|  |     font-family: sans-serif; | ||||||
|  |     margin: 20px; | ||||||
|  |     padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | h1 { | ||||||
|  |     margin-top: 0; | ||||||
|  |     font-size: 22px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | h2 { | ||||||
|  |     margin-top: 0; | ||||||
|  |     font-size: 20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | h3 { | ||||||
|  |     margin-top: 0; | ||||||
|  |     font-size: 18px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | h4 { | ||||||
|  |     margin-top: 0; | ||||||
|  |     font-size: 16px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | h5 { | ||||||
|  |     margin-top: 0; | ||||||
|  |     font-size: 14px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | h6 { | ||||||
|  |     margin-top: 0; | ||||||
|  |     font-size: 12px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | code { | ||||||
|  |     font-size: 1.2em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ul { | ||||||
|  |     padding-left: 20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | * { | ||||||
|  |     box-sizing: border-box; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | body { | ||||||
|  |     font-family: sans-serif; | ||||||
|  |     margin: 20px; | ||||||
|  |     padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .square { | ||||||
|  |     background: #fff; | ||||||
|  |     border: 1px solid #999; | ||||||
|  |     float: left; | ||||||
|  |     font-size: 24px; | ||||||
|  |     font-weight: bold; | ||||||
|  |     line-height: 34px; | ||||||
|  |     height: 34px; | ||||||
|  |     margin-right: -1px; | ||||||
|  |     margin-top: -1px; | ||||||
|  |     padding: 0; | ||||||
|  |     text-align: center; | ||||||
|  |     width: 34px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .board-row:after { | ||||||
|  |     clear: both; | ||||||
|  |     content: ''; | ||||||
|  |     display: table; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .status { | ||||||
|  |     margin-bottom: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .game { | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .game-info { | ||||||
|  |     margin-left: 20px; | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue