├── .editorconfig ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── sandbox.config.json ├── src ├── components │ ├── application.tsx │ ├── controls.jsx │ └── name-badge.tsx ├── global.d.ts ├── index.css ├── index.tsx ├── lib │ └── sleep.ts └── react-app-env.d.ts ├── tailwind.config.js └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,ts,jsx,tsx}] 8 | indent_style = space 9 | indent_size = 2 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | 5 | /node_modules 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | 11 | /coverage 12 | 13 | # production 14 | 15 | /build 16 | 17 | # misc 18 | 19 | .DS_Store 20 | .env.local 21 | .env.development.local 22 | .env.test.local 23 | .env.production.local 24 | 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log\* 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React and TypeScript Example 2 | 3 | This is an exmaple application from Steve's React and TypeScript course. 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "name-badge", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@reduxjs/toolkit": "^1.9.0", 7 | "clsx": "^1.2.1", 8 | "lodash.shuffle": "^4.2.0", 9 | "prop-types": "^15.8.1", 10 | "react": "^18.2.0", 11 | "react-dom": "^18.2.0", 12 | "react-redux": "^8.0.5", 13 | "react-scripts": "^5.0.1", 14 | "uuid": "^9.0.0" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | }, 40 | "devDependencies": { 41 | "@testing-library/jest-dom": "^5.16.5", 42 | "@testing-library/react": "^13.4.0", 43 | "@testing-library/user-event": "^14.4.3", 44 | "@types/jest": "^29.2.2", 45 | "@types/node": "^18.11.9", 46 | "@types/react": "^18.0.24", 47 | "@types/react-dom": "^18.0.8", 48 | "autoprefixer": "^10.4.13", 49 | "postcss": "^8.4.18", 50 | "prettier": "^2.7.1", 51 | "prettier-plugin-tailwindcss": "^0.1.13", 52 | "tailwindcss": "^3.2.2", 53 | "typescript": "^4.8.4" 54 | }, 55 | "prettier": { 56 | "printWidth": 80, 57 | "tabWidth": 2, 58 | "useTabs": false, 59 | "semi": true, 60 | "singleQuote": true, 61 | "trailingComma": "all", 62 | "bracketSpacing": true, 63 | "jsxBracketSameLine": false, 64 | "fluid": false 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekinney/name-badges/d8cc8537659bdcf69a862d16099b88b76b65adf3/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekinney/name-badges/d8cc8537659bdcf69a862d16099b88b76b65adf3/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekinney/name-badges/d8cc8537659bdcf69a862d16099b88b76b65adf3/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekinney/name-badges/d8cc8537659bdcf69a862d16099b88b76b65adf3/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekinney/name-badges/d8cc8537659bdcf69a862d16099b88b76b65adf3/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekinney/name-badges/d8cc8537659bdcf69a862d16099b88b76b65adf3/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | TypeScript Example 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekinney/name-badges/d8cc8537659bdcf69a862d16099b88b76b65adf3/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekinney/name-badges/d8cc8537659bdcf69a862d16099b88b76b65adf3/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "An Example Application", 3 | "short_name": "Example", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /sandbox.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "infiniteLoopProtection": true, 3 | "hardReloadOnChange": false, 4 | "view": "browser" 5 | } 6 | -------------------------------------------------------------------------------- /src/components/application.tsx: -------------------------------------------------------------------------------- 1 | // import ControlPanel from './controls'; 2 | import NameBadge from './name-badge'; 3 | 4 | const Application = () => ( 5 |
6 | 7 |
8 | ); 9 | 10 | export default Application; 11 | -------------------------------------------------------------------------------- /src/components/controls.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | const ControlPanel = ({ name, onChange }) => { 4 | return ( 5 |
event.preventDefault()} 8 | > 9 |
10 | 11 | 18 |
19 |
20 | ); 21 | }; 22 | 23 | ControlPanel.propTypes = { 24 | name: PropTypes.string, 25 | onChange: PropTypes.func, 26 | }; 27 | 28 | export default ControlPanel; 29 | -------------------------------------------------------------------------------- /src/components/name-badge.tsx: -------------------------------------------------------------------------------- 1 | const NameBadge = () => { 2 | return ( 3 |
4 |
5 |

HELLO

6 |

My name is…

7 |
8 |
9 |

Steve

10 |
11 |
13 | ); 14 | }; 15 | 16 | export default NameBadge; 17 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevekinney/name-badges/d8cc8537659bdcf69a862d16099b88b76b65adf3/src/global.d.ts -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | *, 6 | html, 7 | body { 8 | @apply box-border; 9 | } 10 | 11 | html, 12 | body { 13 | @apply m-0 h-screen w-screen p-0; 14 | } 15 | 16 | #root { 17 | @apply h-screen w-screen; 18 | } 19 | 20 | h1, 21 | h2, 22 | h3, 23 | h4, 24 | h5, 25 | h6 { 26 | @apply font-bold leading-tight first:mt-0; 27 | } 28 | 29 | h1 { 30 | @apply text-4xl leading-tight; 31 | } 32 | 33 | h2 { 34 | @apply text-3xl; 35 | } 36 | 37 | h3 { 38 | @apply text-2xl; 39 | } 40 | 41 | h4 { 42 | @apply text-xl; 43 | } 44 | 45 | h5 { 46 | @apply text-lg; 47 | } 48 | 49 | h6 { 50 | @apply text-base; 51 | } 52 | 53 | p, 54 | ul, 55 | ol, 56 | dl { 57 | @apply leading-relaxed; 58 | } 59 | 60 | input { 61 | @apply rounded-sm border-2 border-primary-700 bg-primary-100 p-2 placeholder-primary-400 accent-primary-600 shadow-sm transition-all ease-in invalid:border-red-800 invalid:bg-red-200 out-of-range:border-red-800 out-of-range:bg-red-200 hover:bg-primary-200 hover:accent-primary-700 focus:bg-primary-50 focus:outline-none disabled:border-slate-500 disabled:bg-slate-100 disabled:placeholder-slate-400; 62 | } 63 | 64 | button, 65 | button[type='button'], 66 | button[type='reset'], 67 | button[type='submit'] input[type='button'], 68 | input[type='submit'], 69 | input[type='reset'] { 70 | @apply inline-flex items-center justify-center border-2 border-primary-700 bg-primary-100 px-4 py-2 transition-colors ease-in hover:bg-primary-200 active:bg-primary-300; 71 | } 72 | 73 | input[type='checkbox'], 74 | input[type='radio'] { 75 | @apply hover:accent-primary-700; 76 | } 77 | 78 | input[type='range'] { 79 | @apply h-2 w-full cursor-pointer appearance-none rounded-lg bg-primary-100; 80 | } 81 | 82 | input[type='submit'], 83 | button[type='submit'] { 84 | @apply bg-primary-500 font-semibold text-white hover:bg-primary-600 active:bg-primary-700; 85 | } 86 | 87 | input[type='color'] { 88 | @apply h-12 p-1; 89 | } 90 | 91 | input[type='file']::-webkit-file-upload-button { 92 | @apply placeholder-red-400 accent-red-800; 93 | } 94 | 95 | input[type='date'], 96 | input[type='datetime'], 97 | input[type='datetime-local'], 98 | input[type='month'], 99 | input[type='week'], 100 | input[type='time'] { 101 | @apply text-primary-900; 102 | } 103 | 104 | label { 105 | @apply font-normal; 106 | } 107 | 108 | input:checked + label { 109 | @apply pl-2 font-semibold text-primary-900 transition-colors hover:text-primary-800; 110 | } 111 | 112 | select { 113 | @apply block w-full rounded border-2 border-purple-700 px-4 py-2 pr-8 shadow hover:bg-primary-200 hover:accent-primary-700 focus:bg-primary-50 focus:outline-none disabled:border-slate-500 disabled:bg-slate-100 disabled:placeholder-slate-400; 114 | } 115 | 116 | .application { 117 | @apply flex h-full flex-col place-content-center items-center gap-8 bg-slate-600; 118 | } 119 | 120 | .badge { 121 | @apply flex h-96 w-[600px] flex-col rounded-xl border-2 border-slate-900 bg-red-700 shadow-lg; 122 | } 123 | 124 | .badge-header { 125 | @apply rounded-t-xl py-3 text-center font-black uppercase text-white; 126 | } 127 | 128 | .badge-body { 129 | @apply flex flex-grow place-content-center items-center bg-white; 130 | } 131 | 132 | .badge-name { 133 | @apply font-serif text-6xl; 134 | } 135 | 136 | .badge-footer { 137 | @apply h-11 rounded-b-xl bg-red-700; 138 | } 139 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import Application from './components/application'; 4 | 5 | import './index.css'; 6 | 7 | const root = ReactDOM.createRoot( 8 | document.getElementById('root') as HTMLElement, 9 | ); 10 | 11 | root.render( 12 | 13 | 14 | , 15 | ); 16 | -------------------------------------------------------------------------------- /src/lib/sleep.ts: -------------------------------------------------------------------------------- 1 | const defaultDuration = 500; 2 | 3 | export const sleep = (duration = defaultDuration): Promise => { 4 | return new Promise((resolve) => { 5 | setTimeout(() => { 6 | resolve(undefined); 7 | }, duration); 8 | }); 9 | }; 10 | 11 | export const block = (duration = defaultDuration) => { 12 | const start = performance.now(); 13 | while (performance.now() < start + duration) {} 14 | }; 15 | 16 | export const makeExpensive = any>( 17 | fn: T, 18 | duration = defaultDuration, 19 | ): ((...args: Parameters) => ReturnType) => { 20 | return (...args: Parameters): ReturnType => { 21 | block(duration); 22 | return fn(...args); 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | 3 | const colors = require('tailwindcss/colors'); 4 | 5 | module.exports = { 6 | content: ['./src/**/*.{js,jsx,ts,tsx}'], 7 | theme: { 8 | extend: { 9 | colors: { ...colors, primary: colors.slate }, 10 | }, 11 | }, 12 | plugins: [], 13 | }; 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | --------------------------------------------------------------------------------