├── .gitignore ├── Readme.md ├── Schedule.md ├── assigment └── hex2048 │ ├── .gitignore │ ├── Readme.md │ ├── assets │ ├── cube-coordinates.svg │ └── shifting.svg │ ├── jest-puppeteer.config.js │ ├── jest.e2e.config.js │ ├── jsconfig.json │ ├── package-lock.json │ ├── package.json │ └── src │ ├── fieldUtils.js │ ├── index.js │ ├── rngServer.js │ └── tests │ ├── launch.spec.js │ └── utils.js ├── homeworks ├── 01-bubble-sort │ └── README.md ├── 02-css-instruments │ └── README.md ├── 03-data-structure │ ├── README.md │ └── assets │ │ └── BST_columns.png ├── 05-redux-basic │ ├── 001-combineReducer.md │ ├── 002-basic-redux.md │ └── README.md ├── 06-advanced-redux │ ├── README.md │ ├── craco.config.js │ ├── echo-server │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── server.ts │ │ └── tsconfig.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── components │ │ │ ├── Circle.tsx │ │ │ ├── Loading.tsx │ │ │ ├── Missing.tsx │ │ │ ├── PizzaBasket.tsx │ │ │ ├── PizzaBasketItem.tsx │ │ │ ├── PizzaCount.tsx │ │ │ ├── PizzaDescription.tsx │ │ │ ├── PizzaItem.tsx │ │ │ ├── PizzaList.tsx │ │ │ ├── PizzaName.tsx │ │ │ ├── PizzaPrice.tsx │ │ │ ├── TotalPrice.tsx │ │ │ └── index.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ └── useApp.ts │ │ ├── index.css │ │ ├── index.tsx │ │ ├── logo.svg │ │ ├── react-app-env.d.ts │ │ ├── reportWebVitals.ts │ │ ├── services │ │ │ └── api.ts │ │ ├── setupProxy.js │ │ ├── setupTests.ts │ │ └── types.ts │ ├── tailwind.css │ ├── tsconfig.json │ └── yarn.lock ├── 07-mars-viewer │ └── Readme.md ├── 08-functional-reactive-programming │ └── Readme.md ├── 09-node-chokidar │ └── readme.md ├── 10-caesar │ └── readme.md ├── 11-animations │ └── Readme.md ├── 12-mobx-mars │ └── readme.md └── 13-life │ └── Readme.md ├── lessons ├── 01-basic-react │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── common.css │ │ ├── examples │ │ │ ├── 01-JSX.jsx │ │ │ ├── 02-Components.jsx │ │ │ ├── 03-State.jsx │ │ │ ├── 04-Update-State.jsx │ │ │ ├── 05-Props.jsx │ │ │ ├── 06-Lists.jsx │ │ │ ├── 07-List-Keys.jsx │ │ │ └── 08-Events.jsx │ │ ├── exercies │ │ │ ├── 01-JSX.jsx │ │ │ ├── 02-StateComponent.jsx │ │ │ ├── 03-Props.jsx │ │ │ ├── 04-Events.jsx │ │ │ └── 05-TODO.jsx │ │ ├── index.css │ │ └── index.jsx │ └── yarn.lock ├── 05-basic-unit-tests │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── 01-pure-code.test.ts │ │ ├── 01-pure-code.ts │ │ ├── 02-mocking.test.ts │ │ ├── 02-mocking.ts │ │ ├── 03-mocking-modules.test.ts │ │ ├── 03-mocking-modules.ts │ │ ├── 04-snapshot.test.ts │ │ └── 04-snapshot.ts │ └── yarn.lock ├── 10-advanced-unit-tests │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── jest.config.js │ ├── jest.setup.ts │ ├── package.json │ ├── src │ │ ├── 01-basic.test.tsx │ │ ├── 01-basic.tsx │ │ ├── 02-basic-rtl.test.tsx │ │ ├── 03-timer.test.tsx │ │ ├── 03-timer.tsx │ │ ├── 04-hooks.test.tsx │ │ ├── 04-hooks.tsx │ │ ├── 05-hooks-task.test.ts │ │ ├── 05-hooks-task.ts │ │ ├── 06-hooks-usage.test.tsx │ │ ├── 06-hooks-usage.tsx │ │ ├── 07-snapshot.test.tsx │ │ ├── 07-snapshot.tsx │ │ └── time.ts │ ├── tsconfig.json │ ├── tsconfig.test.json │ └── yarn.lock ├── 12-qa │ ├── .eslintrc.js │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── examples │ │ │ ├── 01-bind-in-render.tsx │ │ │ └── 02-Node-timer.tsx │ │ ├── index.css │ │ ├── index.tsx │ │ ├── react-app-env.d.ts │ │ └── setupTests.ts │ ├── tsconfig.json │ └── yarn.lock ├── 14-network │ ├── 006-websocket-server-node │ │ ├── index.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ └── tsconfig.json │ ├── 007-react-keyboard │ │ ├── .env.development │ │ ├── .gitignore │ │ ├── README.md │ │ ├── craco.config.js │ │ ├── package.json │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── logo192.png │ │ │ ├── logo512.png │ │ │ ├── manifest.json │ │ │ └── robots.txt │ │ ├── src │ │ │ ├── App.css │ │ │ ├── App.tsx │ │ │ ├── components │ │ │ │ ├── Input.tsx │ │ │ │ ├── Keyboard.tsx │ │ │ │ ├── List.tsx │ │ │ │ └── index.ts │ │ │ ├── constants.ts │ │ │ ├── index.css │ │ │ ├── index.tsx │ │ │ ├── logo.svg │ │ │ ├── modules │ │ │ │ ├── emails │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── search │ │ │ │ │ └── index.ts │ │ │ │ └── thunks.ts │ │ │ ├── react-app-env.d.ts │ │ │ ├── reportWebVitals.ts │ │ │ ├── services │ │ │ │ └── api.ts │ │ │ ├── setupProxy.js │ │ │ ├── setupTests.ts │ │ │ └── types.ts │ │ ├── tailwind.css │ │ ├── tsconfig.json │ │ └── yarn.lock │ └── 009-rest-nodejs │ │ ├── Dockerfile │ │ ├── docker-compose.yml │ │ ├── index.ts │ │ ├── nginx │ │ ├── Dockerfile │ │ └── nginx.conf │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── public │ │ ├── error.html │ │ ├── index.html │ │ └── js │ │ │ └── ramda.js │ │ └── tsconfig.json ├── 15-functional-reactive-programming │ ├── README.md │ └── football │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── package.json │ │ ├── src │ │ ├── collision.ts │ │ ├── index.css │ │ ├── index.html │ │ ├── index.ts │ │ ├── renderer.ts │ │ └── vector.ts │ │ ├── tsconfig.json │ │ ├── webpack.config.js │ │ └── yarn.lock ├── 17-node │ └── presentation.pdf ├── 18-webgl │ ├── index.css │ ├── index.html │ ├── script.js │ ├── texture.jpg │ └── webgl-utils.js ├── 20-mobx │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── 01-BigApp.tsx │ │ ├── 02-MobxSimpleStructures.tsx │ │ ├── 03-MobxClass.tsx │ │ ├── 04-Reactions.tsx │ │ ├── 05-Mobx-React-Lite.tsx │ │ ├── index.css │ │ ├── index.tsx │ │ └── react-app-env.d.ts │ ├── tsconfig.json │ └── yarn.lock └── 21-babylonjs │ ├── introduction-final │ ├── README.md │ ├── bin │ │ └── index.html │ ├── package.json │ ├── src │ │ ├── common │ │ │ ├── ExerciseBase.ts │ │ │ └── types.d.ts │ │ └── exercises │ │ │ ├── exercise01 │ │ │ └── exercise01.ts │ │ │ ├── exercise02 │ │ │ └── exercise02.ts │ │ │ ├── exercise03 │ │ │ └── exercise03.ts │ │ │ ├── exercise04 │ │ │ └── exercise04.ts │ │ │ ├── exercise05 │ │ │ └── exercise05.ts │ │ │ ├── exercise06 │ │ │ └── exercise06.ts │ │ │ ├── exercise07 │ │ │ └── exercise07.ts │ │ │ ├── exercise08 │ │ │ └── exercise08.ts │ │ │ ├── exercise09 │ │ │ └── exercise09.ts │ │ │ ├── exercise10 │ │ │ └── exercise10.ts │ │ │ └── index.ts │ ├── tsconfig.json │ ├── types │ │ └── FileTypes.d.ts │ ├── webpack.config.js │ └── yarn.lock │ ├── introduction-start │ ├── README.md │ ├── bin │ │ └── index.html │ ├── package.json │ ├── src │ │ ├── common │ │ │ ├── ExerciseBase.ts │ │ │ └── types.d.ts │ │ └── exercises │ │ │ ├── exercise01 │ │ │ └── exercise01.ts │ │ │ ├── exercise02 │ │ │ └── exercise02.ts │ │ │ ├── exercise03 │ │ │ └── exercise03.ts │ │ │ ├── exercise04 │ │ │ └── exercise04.ts │ │ │ ├── exercise05 │ │ │ └── exercise05.ts │ │ │ ├── exercise06 │ │ │ └── exercise06.ts │ │ │ ├── exercise07 │ │ │ └── exercise07.ts │ │ │ ├── exercise08 │ │ │ └── exercise08.ts │ │ │ ├── exercise09 │ │ │ └── exercise09.ts │ │ │ ├── exercise10 │ │ │ └── exercise10.ts │ │ │ └── index.ts │ ├── tsconfig.json │ ├── types │ │ └── FileTypes.d.ts │ └── webpack.config.js │ ├── maze-01-start │ ├── README.md │ ├── assets │ │ └── maze.json │ ├── bin │ │ └── index.html │ ├── package.json │ ├── src │ │ ├── Maze.ts │ │ ├── common │ │ │ ├── GameBase.ts │ │ │ └── utils.ts │ │ ├── components │ │ │ ├── mazeMap.ts │ │ │ └── player.ts │ │ ├── consts.ts │ │ ├── index.ts │ │ └── utils │ │ │ └── playerInput.ts │ ├── tsconfig.json │ ├── types │ │ └── FileTypes.d.ts │ ├── webpack.config.js │ └── yarn.lock │ └── maze-02-start │ ├── README.md │ ├── assets │ └── maze.json │ ├── bin │ └── index.html │ ├── package.json │ ├── src │ ├── Maze.ts │ ├── common │ │ ├── GameBase.ts │ │ └── utils.ts │ ├── components │ │ ├── mazeMap.ts │ │ └── player.ts │ ├── consts.ts │ ├── index.ts │ └── utils │ │ └── playerInput.ts │ ├── tsconfig.json │ ├── types │ └── FileTypes.d.ts │ ├── webpack.config.js │ └── yarn.lock └── presentations ├── BabylonJS ├── introduction.pdf ├── maze-01.pdf └── maze-02.pdf ├── CSS tools └── CSS tools.md ├── Network.pdf ├── OldSchedules ├── 2021-q2-q3 │ └── Schedule.md └── 2022-q1-q3 │ └── Schedule.md ├── React ├── 1. React Basic.pdf ├── 2. React Middle.pdf ├── 3. React Advanced.pdf └── 4. React Patterns.pdf ├── Redux └── 1 Redux Basic.pdf ├── TypeScript ├── 1. TypeScript. Basic.pptx ├── 2. TypeScript. Middle.pptx ├── 3. TypeScript. Advanced.pptx ├── README.md └── ts-declaration-files │ ├── .editorconfig │ ├── README.md │ ├── package-lock.json │ ├── src │ ├── asserts │ │ └── ts_guru.png │ ├── constants.ts │ ├── global-types │ │ ├── CustomUtilityTypes.d.ts │ │ ├── declaration.d.ts │ │ └── global.d.ts │ ├── index.ts │ ├── lib │ │ ├── superUtils.d.ts │ │ └── superUtils.js │ └── types.ts │ └── tsconfig.json ├── UnitTesting ├── 01-basic-react-setup │ ├── .gitignore │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── components │ │ │ ├── Todos.spec.tsx │ │ │ └── Todos.tsx │ │ ├── index.html │ │ └── index.tsx │ ├── tsconfig.json │ └── yarn.lock └── 01-basic-setup │ ├── README.md │ ├── jest.config.js │ ├── package.json │ ├── src │ ├── fibonacci.spec.ts │ └── fibonacci.ts │ ├── tsconfig.json │ └── yarn.lock ├── WebGL.pdf └── Webpack.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /assigment/hex2048/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /assigment/hex2048/jest-puppeteer.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | launch: { 3 | headless: false, 4 | }, 5 | browsers: "chromium", 6 | browserContext: "default", 7 | } 8 | -------------------------------------------------------------------------------- /assigment/hex2048/jest.e2e.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "jest-puppeteer", 3 | } 4 | -------------------------------------------------------------------------------- /assigment/hex2048/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "typeAcquisition": { 3 | "include": ["jest"] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /assigment/hex2048/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rng-server", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "private": true, 6 | "scripts": { 7 | "rng-server": "node src/index.js", 8 | "test-game": "jest --runInBand --config jest.e2e.config.js" 9 | }, 10 | "devDependencies": { 11 | "jest": "^26.6.3", 12 | "jest-puppeteer": "^4.4.0", 13 | "puppeteer": "^7.1.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /assigment/hex2048/src/fieldUtils.js: -------------------------------------------------------------------------------- 1 | const minusToPlusN = (n, f) => { 2 | Array.from({ length: n * 2 + 1 }, (_, x) => n - x).forEach(f) 3 | } 4 | 5 | const getFieldPoints = radius => { 6 | let points = [] 7 | minusToPlusN(radius - 1, x => 8 | minusToPlusN(radius - 1, y => minusToPlusN(radius - 1, z => x + y + z === 0 && points.push({ x, y, z }))), 9 | ) 10 | return points 11 | } 12 | 13 | const pickRandomN = (array, n) => 14 | array 15 | .map(a => ({ order: rng(), value: a })) 16 | .sort((a, b) => a.order - b.order) 17 | .map(a => a.value) 18 | .slice(0, n) 19 | 20 | const rng = () => Math.random() 21 | const min = (a, b) => Math.min(a, b) 22 | const arePointsSame = (a, b) => !["x", "y", "z"].some(v => a[v] !== b[v]) 23 | 24 | function getRNGPoints(radius, userPoints) { 25 | const availablePositions = getFieldPoints(radius).filter(a => userPoints.every(b => !arePointsSame(a, b))) 26 | const pointsCount = min(availablePositions.length, userPoints.length === 0 ? 3 : 1 + (rng() > 0.8 ? 1 : 0)) 27 | const selectedValue = userPoints.length === 0 ? 2 : rng() > 0.5 ? 2 : 4 28 | return pickRandomN(availablePositions, pointsCount).map(p => ({ ...p, value: selectedValue })) 29 | } 30 | 31 | module.exports = { 32 | minusToPlusN, 33 | getFieldPoints, 34 | pickRandomN, 35 | rng, 36 | min, 37 | arePointsSame, 38 | getRNGPoints, 39 | } 40 | -------------------------------------------------------------------------------- /assigment/hex2048/src/index.js: -------------------------------------------------------------------------------- 1 | const { Server } = require("./rngServer") 2 | 3 | const server = new Server() 4 | server.start() 5 | -------------------------------------------------------------------------------- /assigment/hex2048/src/tests/utils.js: -------------------------------------------------------------------------------- 1 | const { getFieldPoints } = require("../fieldUtils") 2 | 3 | const getDataValue = e => e.evaluate(e => e.getAttribute("data-value")) 4 | const getDataStatus = e => e.evaluate(e => e.getAttribute("data-status")) 5 | const setRngServerUrl = async page => { 6 | try { 7 | const { value, selected } = await page.evaluate(() => { 8 | const { value, selected } = document.getElementById("localhost") 9 | return { value, selected } 10 | }) 11 | 12 | if (!selected) { 13 | await page.waitForSelector("#url-server") 14 | await page.select("#url-server", value) 15 | } 16 | } catch {} 17 | } 18 | 19 | async function readDOMField(page, radius) { 20 | const fieldPoints = getFieldPoints(radius) 21 | 22 | return await Promise.all( 23 | fieldPoints.map(async ({ x, y, z }) => { 24 | const element = await page.waitForSelector(`[data-x="${x}"][data-y="${y}"][data-z="${z}"]`) 25 | const value = parseInt(await getDataValue(element, "data-value")) 26 | return { x, y, z, value } 27 | }), 28 | ) 29 | } 30 | 31 | module.exports = { 32 | getDataStatus, 33 | readDOMField, 34 | setRngServerUrl, 35 | } 36 | -------------------------------------------------------------------------------- /homeworks/02-css-instruments/README.md: -------------------------------------------------------------------------------- 1 | # Homework 2 | 3 | **Deadline: 18.04.2021 23:59 Minsk** 4 | 5 | 1. Play games [https://flexboxfroggy.com]() and [http://cssgridgarden.com]() to 6 | master your flexbox and grid skills 7 | 2. Create a calendar layout using CSS Grid 8 | [https://codesandbox.io/s/layout-practice-uwty8]() 9 | 3. (optional, but extra plus) Make the layout of Yandex.Images 10 | ([https://yandex.by/images/search?text=%D0%9C%D0%B0%D0%BB%D0%B0%D0%BC%D1%83%D1%82&nl=1&source=morda]()) 11 | using anything you like. You could use Flexbox, Grid, JS, React, Typescript, 12 | whatever. For CSS I would recommend to use CSS Modules or Styled Components 13 | for some extra practice. You could download and serve the images or use an 14 | existing API (e.g. unsplash) for your gallery. 15 | -------------------------------------------------------------------------------- /homeworks/03-data-structure/assets/BST_columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/homeworks/03-data-structure/assets/BST_columns.png -------------------------------------------------------------------------------- /homeworks/05-redux-basic/001-combineReducer.md: -------------------------------------------------------------------------------- 1 | 2 | From lesson we already have implementation of function which is `createStore`. 3 | Codesandbox can be forked. 4 | 5 | The task is to implement `combineReducer` function which should match to `https://redux.js.org/api/combinereducers`. 6 | 7 | ``` 8 | const app = combineReducer({ 9 | counter: counter, 10 | counter2: counter2, 11 | }); 12 | 13 | const appStore = createStore(app); 14 | ``` 15 | 16 | Expected: 17 | The reducer counter2 should have diffenent actions type name and they should be dispatched after counter's actions. 18 | -------------------------------------------------------------------------------- /homeworks/05-redux-basic/README.md: -------------------------------------------------------------------------------- 1 | # Redux basic 2 | 3 | **Deadline: 02.05.2021 23:59 Minsk** 4 | 5 | ## Task submission 6 | 7 | Create a `05-redux-basic` folder inside the `homeworks` folder in your 8 | `evo-ts-bootcamp` repository on [GitHub](https://github.com/). 9 | 10 | Create a new branch from your main branch, implement the task and create a 11 | Pull Request to your repository. Change accordingly the `README` file to describe 12 | your application. 13 | 14 | Please share the link of your Pull Request in appropriate Slack channel: 15 | [ts-bootcamp-common-2021](https://evolutiongaming.slack.com/archives/C01TBBGC18U) 16 | in the thread with the homework announcement when it is ready for review. 17 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/README.md: -------------------------------------------------------------------------------- 1 | [pizza-app](https://github.com/vladislavkovaliov/pizza-app) 2 | 3 | **Deadline: 05.05.2021 23:59 Minsk** 4 | 5 | Description task: 6 | 7 | I would like to collect info what happens on pizza site. For that you have to integrate [redux](https://react-redux.js.org/introduction/getting-started) and [redux-thunk](https://github.com/reduxjs/redux-thunk). Both of them should simplify your development process and make extending simple. 8 | 9 | Action names and description: 10 | | Action | Description | 11 | | ------ | ------ | 12 | | `PIZZA_VIEWED` | when list of pizzas is viewed | 13 | | `PIZZA_SELECTED` | when a user clicks on pizza from left list of pizza and pizza is inserted in basket | 14 | | `PIZZA_ADDED_INTO_BASKET` | when a pizza is inserted into a basket | 15 | | `PIZZA_REMOVED_FROM_BASKET` | when a pizza is deleted from a basket | 16 | 17 | Expected result: 18 | 19 | For now it should be enough to print those information into console and make a POST call to `http://localhost:3001/log`. 20 | ``` 21 | fetch('http://localhost:3001/log', { 22 | method: 'POST', 23 | headers: { 24 | 'Content-Type': 'application/json' 25 | }, 26 | body: JSON.stringify({ 27 | 28 | }) 29 | }).then((json) => { 30 | console.log(json); 31 | }).catch((ex) => { 32 | console.log(ex) 33 | }); 34 | ``` 35 | 36 | --- 37 | Event structure of pizza is selected event: 38 | ``` 39 | { 40 | eventName: "PIZZA_SELECTED", 41 | pizzaName: Italian pizza, 42 | pizzaPrice: 5.43 43 | } 44 | ``` 45 | --- 46 | Event structure of pizza is added into the basket: 47 | ``` 48 | { 49 | eventName: "PIZZA_ADDED_INTO_BASKET", 50 | pizzaName: Italian pizza, 51 | pizzaPrice: 5.43 52 | } 53 | ``` 54 | Event structure of pizza is removed into the basket: 55 | ``` 56 | { 57 | eventName: "PIZZA_REMOVED_FROM_BASKET", 58 | pizzaName: Italian pizza, 59 | pizzaPrice: 5.43 60 | } 61 | ``` 62 | Event structure of list is viewed: 63 | ``` 64 | { 65 | eventName: "PIZZA_VIEWED", 66 | } 67 | ``` 68 | The structure of redux can be chosen by you. 69 | --- 70 | How to run echo-server. Go to `echo-server` folder. Install package: 71 | ``` 72 | npm install 73 | ``` 74 | Run server: 75 | ``` 76 | ts-node ./server.ts 77 | ``` 78 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/craco.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | style: { 3 | postcss: { 4 | plugins: [ 5 | require('tailwindcss'), 6 | require('autoprefixer'), 7 | ], 8 | }, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/echo-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "echo-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@types/body-parser": "^1.19.0", 13 | "@types/cors": "^2.8.10", 14 | "@types/express": "^4.17.11", 15 | "body-parser": "^1.19.0", 16 | "cors": "^2.8.5", 17 | "express": "^4.17.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/echo-server/server.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import cors from "cors"; 3 | import bodyParser from "body-parser"; 4 | 5 | const app = express(); 6 | const port = 3001; 7 | 8 | app.use(cors()); 9 | app.use(bodyParser.json()); 10 | 11 | app.get("/", (req, res) => { 12 | res.send("re"); 13 | }); 14 | 15 | app.post("/log", (req, res) => { 16 | console.log(req); 17 | res.send("re") 18 | }); 19 | 20 | app.listen(port, () => { 21 | console.debug(`Example app listening at http://localhost:${port}`); 22 | }); 23 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/echo-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "rootDir": "./", 6 | "outDir": "./build", 7 | "esModuleInterop": true, 8 | "strict": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "002-bucket-ui", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@craco/craco": "^6.1.1", 7 | "@tailwindcss/postcss7-compat": "^2.0.4", 8 | "@testing-library/jest-dom": "^5.11.4", 9 | "@testing-library/react": "^11.1.0", 10 | "@testing-library/user-event": "^12.1.10", 11 | "@types/jest": "^26.0.15", 12 | "@types/node": "^12.0.0", 13 | "@types/ramda": "^0.27.39", 14 | "@types/react": "^17.0.0", 15 | "@types/react-dom": "^17.0.0", 16 | "@types/react-redux": "^7.1.16", 17 | "@types/redux": "^3.6.0", 18 | "@types/redux-thunk": "^2.1.0", 19 | "@types/reselect": "^2.2.0", 20 | "autoprefixer": "^9", 21 | "http-proxy-middleware": "^1.1.0", 22 | "postcss": "^7", 23 | "ramda": "^0.27.1", 24 | "react": "^17.0.2", 25 | "react-dom": "^17.0.2", 26 | "react-redux": "^7.2.3", 27 | "react-scripts": "4.0.3", 28 | "redux": "^4.0.5", 29 | "redux-devtools-extension": "^2.13.9", 30 | "redux-thunk": "^2.3.0", 31 | "reselect": "^4.0.0", 32 | "tailwindcss": "npm:@tailwindcss/postcss7-compat", 33 | "typescript": "^4.1.2", 34 | "web-vitals": "^1.0.1" 35 | }, 36 | "scripts": { 37 | "start": "craco start", 38 | "build": "react-scripts build", 39 | "test": "react-scripts test", 40 | "eject": "react-scripts eject" 41 | }, 42 | "eslintConfig": { 43 | "extends": [ 44 | "react-app", 45 | "react-app/jest" 46 | ] 47 | }, 48 | "browserslist": { 49 | "production": [ 50 | ">0.2%", 51 | "not dead", 52 | "not op_mini all" 53 | ], 54 | "development": [ 55 | "last 1 chrome version", 56 | "last 1 firefox version", 57 | "last 1 safari version" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/homeworks/06-advanced-redux/public/favicon.ico -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/homeworks/06-advanced-redux/public/logo192.png -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/homeworks/06-advanced-redux/public/logo512.png -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/homeworks/06-advanced-redux/src/App.css -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react'; 2 | import './App.css'; 3 | import { 4 | Loading, 5 | Missing, 6 | PizzaList, 7 | PizzaBasket, 8 | TotalPrice 9 | } from "./components"; 10 | import * as R from "ramda"; 11 | import { useApp } from "./hooks"; 12 | 13 | 14 | function App() { 15 | const { totalPrice, pizza, plusPizzaBucket, minusPizzaBucket, bucket } = 16 | useApp(); 17 | const handleMinusPizza = 18 | useCallback((_id: string) => { 19 | minusPizzaBucket(_id); 20 | }, [pizza, bucket]); 21 | const handleAddPizza = 22 | useCallback((_id: string) => { 23 | plusPizzaBucket(_id); 24 | }, [pizza, bucket]); 25 | 26 | const pizzaList = R.cond([ 27 | [R.isEmpty, Loading], 28 | [R.T, (xs) => PizzaList({ pizza: xs, onAdd: handleAddPizza })], 29 | ]); 30 | const pizzaBucket = R.cond([ 31 | [R.isEmpty, Missing], 32 | [R.T, (xs) => PizzaBasket({ pizza: xs, onMinus: handleMinusPizza })], 33 | ]); 34 | 35 | return ( 36 |
37 |
38 |
39 | {pizzaList(pizza)} 40 |
41 |
42 |
43 |
44 | 45 | {pizzaBucket(bucket)} 46 |
47 | 50 |
51 |
52 |
53 |
54 | ); 55 | } 56 | 57 | export default App; 58 | 59 | 60 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/Circle.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface CircleProps { 4 | type: "plus" | "minus"; 5 | onClick: () => void; 6 | } 7 | 8 | export function Circle({ type , onClick }: CircleProps) { 9 | return ( 10 |
11 |
14 | {type === "plus" ? "+" : "-"} 15 |
16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/Loading.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export function Loading() { 4 | return ( 5 |
Loading
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/Missing.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export function Missing() { 4 | return ( 5 |
Missing
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/PizzaBasket.tsx: -------------------------------------------------------------------------------- 1 | import * as R from "ramda"; 2 | import React from "react"; 3 | import {Pizza} from "../types"; 4 | import {PizzaBasketItem} from "./PizzaBasketItem"; 5 | 6 | interface PizzaBucketProps { 7 | pizza: Array, 8 | onMinus: (_id: string) => void; 9 | } 10 | 11 | export function PizzaBasket({pizza, onMinus}: PizzaBucketProps) { 12 | return R.map((p) => 13 | , pizza); 21 | } 22 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/PizzaCount.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface PizzaCountProps { 4 | count: number 5 | } 6 | 7 | export function PizzaCount({ count }: PizzaCountProps) { 8 | return ( 9 |

x{count}

10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/PizzaDescription.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface PizzaDescriptionProps { 4 | desc: string; 5 | } 6 | 7 | export function PizzaDescription({ desc }: PizzaDescriptionProps) { 8 | return ( 9 |

{desc}

10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/PizzaList.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { PizzaItem } from "./PizzaItem"; 3 | import * as R from "ramda"; 4 | 5 | interface PizzaListProps { 6 | pizza: { 7 | _id: string; 8 | name: string; 9 | price: number; 10 | }[]; 11 | onAdd: (_id: string) => void; 12 | } 13 | 14 | export function PizzaList({ pizza, onAdd }: PizzaListProps) { 15 | return R.map((p) => 16 | , pizza); 23 | } 24 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/PizzaName.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface PizzaNameProps { 4 | name: string; 5 | } 6 | 7 | export function PizzaName({ name }: PizzaNameProps) { 8 | return ( 9 |
{name}
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/PizzaPrice.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface PizzaPriceProps { 4 | price: number; 5 | } 6 | 7 | export function PizzaPrice({ price }: PizzaPriceProps) { 8 | return ( 9 |

${price}

10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/TotalPrice.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {PizzaPrice} from "./PizzaPrice"; 3 | 4 | interface TotalPriceProps { 5 | price: number; 6 | } 7 | 8 | export function TotalPrice({ price }: TotalPriceProps) { 9 | return ( 10 |
11 | Total price: 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { PizzaBasketItem } from "./PizzaBasketItem"; 2 | export { PizzaItem } from "./PizzaItem"; 3 | export { Circle } from "./Circle"; 4 | export { PizzaPrice } from "./PizzaPrice"; 5 | export { PizzaName } from "./PizzaName"; 6 | export { PizzaCount } from "./PizzaCount"; 7 | export { PizzaDescription } from "./PizzaDescription"; 8 | export { PizzaList } from "./PizzaList"; 9 | export { PizzaBasket } from "./PizzaBasket"; 10 | export { Missing } from "./Missing"; 11 | export { Loading } from "./Loading"; 12 | export { TotalPrice } from "./TotalPrice"; 13 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useApp } from "./useApp"; 2 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/hooks/useApp.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Pizza} from "../types"; 3 | import {getPizza} from "../services/api"; 4 | import * as R from "ramda"; 5 | 6 | export function useApp() { 7 | const [pizza, setPizza] = 8 | React.useState([]); 9 | const [backet, setBacket] = 10 | React.useState([]); 11 | 12 | React.useEffect(() => { 13 | getPizza() 14 | .then(pizza => { setPizza(pizza.items) }); 15 | }, []); 16 | 17 | const plusPizzaBucket = React.useCallback((_id: string) => { 18 | const p = pizza.filter(x => x._id === _id)[0]; 19 | setBacket([...backet, p]); 20 | }, [pizza, backet]); 21 | const minusPizzaBucket = React.useCallback((_id: string) => { 22 | const idx = R.findLastIndex((x: Pizza) => x._id === _id)(backet); 23 | if (idx !== -1) { 24 | setBacket(R.remove(idx, 1, backet)); 25 | } 26 | }, [backet]); 27 | 28 | const validBasket = R.compose( 29 | R.values, 30 | R.mapObjIndexed((value: Pizza[]) => { 31 | return value.reduce((acc, p) => { 32 | return { 33 | ...p, 34 | price: acc.price + p.price, 35 | count: acc.count + 1, 36 | }; 37 | }, { count: 0, price: 0 }); 38 | }), 39 | R.groupBy((x: Pizza) => x._id), 40 | )(backet); 41 | 42 | return { 43 | totalPrice: validBasket 44 | .reduce((acc, p: Pizza) => 45 | acc + p.price, 0), 46 | pizza, 47 | bucket: validBasket, 48 | plusPizzaBucket, 49 | minusPizzaBucket, 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html { 6 | height: 100%; 7 | } 8 | 9 | body { 10 | height: inherit; 11 | margin: 0; 12 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 13 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 14 | sans-serif; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | code { 20 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 21 | monospace; 22 | } 23 | 24 | #root { 25 | height: inherit; 26 | } 27 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/services/api.ts: -------------------------------------------------------------------------------- 1 | 2 | export async function getPizza() { 3 | return { 4 | items: [ 5 | { 6 | name: "Los-Angeles pizza", 7 | price: 34, 8 | _id: "606a0743b3b2f0b339363de2" 9 | }, 10 | { 11 | name: "Chicago pizza", 12 | price: 100, 13 | _id: "606a0743b3b2f0b339363de3" 14 | }, 15 | { 16 | name: "New-York pizza", 17 | price: 15.42, 18 | _id: "606a07ea1a45d242a7b77fe2" 19 | }, 20 | { 21 | name: "Midtown pizza", 22 | price: 20.45, 23 | _id: "606a07ea1a45d242a7b77fe3" 24 | } 25 | ] 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/setupProxy.js: -------------------------------------------------------------------------------- 1 | const { createProxyMiddleware } = require('http-proxy-middleware'); 2 | 3 | module.exports = function(app) { 4 | app 5 | .use( 6 | '/pizza', 7 | createProxyMiddleware({ 8 | target: 'http://localhost:5000', 9 | changeOrigin: false, 10 | }) 11 | ); 12 | app.use( 13 | '/log', 14 | createProxyMiddleware({ 15 | target: 'http://localhost:5000', 16 | changeOrigin: false, 17 | }) 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Pizza = { 2 | name: string; 3 | price: number; 4 | _id: string; 5 | } 6 | 7 | export type State = { 8 | pizza: Pizza[]; 9 | basket: Array; 10 | } 11 | -------------------------------------------------------------------------------- /homeworks/06-advanced-redux/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 | -------------------------------------------------------------------------------- /homeworks/07-mars-viewer/Readme.md: -------------------------------------------------------------------------------- 1 | # Mars viewer 2 | 3 | **Deadline: 23.05.2021 23:59 Minsk** 4 | 5 | ## Task description 6 | 7 | Your task is to develop a **Mars viewer** app. 8 | 9 | Please take a look our example: [Mars viewer](http://mars-viewer.surge.sh/). 10 | 11 | What is required: 12 | 13 | - Use [Create React App with TypeScript](https://create-react-app.dev/docs/adding-typescript/). 14 | - Use React for rendering 15 | - Use redux or redux-toolkit. You can use create-react-app template for such purpose: `npx create-react-app --template redux-typescript mars-viewer` 16 | - Implement: 17 | - select sols 18 | - keep fetched data from nasa api in redux-store 19 | - add to favourites 20 | - remove from favourites 21 | - Define types as much and clear as possible. 22 | - Deploy your implementation on the Internet (free resources: 23 | [gh-pages](https://pages.github.com/), [surge](http://surge.sh), 24 | [netlify](http://netlify.com), [vercel](https://vercel.com)). 25 | - Have to work on the latest Google Chrome on the desktop (all other devices and 26 | browsers are up to you). 27 | 28 | To implement this application, you need to sign up on [NASA API](https://api.nasa.gov/), and you need to use API part "Mars Rover Photos." All requests should include your API key from NASA. 29 | 30 | 31 | All other ideas are up to you and your imagination. For example: 32 | 33 | - Ability to zoom photos by click 34 | - Ability to choose other rovers. 35 | 36 | And anything you want to implement. 37 | 38 | ## Task submission 39 | 40 | Create a new branch from your main branch, implement the task and create a 41 | Pull Request to your repository. Change accordingly the `README` file to describe 42 | your application and add the deployed link there as well. 43 | 44 | Please share the link of your Pull Request in appropriate Slack channel: 45 | [ts-bootcamp-common-2021](https://evolutiongaming.slack.com/archives/C01TBBGC18U) 46 | in the thread with the homework announcement when it is ready for review. 47 | 48 | -------------------------------------------------------------------------------- /homeworks/08-functional-reactive-programming/Readme.md: -------------------------------------------------------------------------------- 1 | # Functional Reactive Programming 2 | 3 | **Deadline: 21.05.2021 23:59 Minsk** 4 | 5 | ## Task description 6 | 7 | Your task is to develop a simple game for catch cats. 8 | 9 | Please take a look our example: [Cats Catcher](https://frp-example.surge.sh/). 10 | 11 | What is required: 12 | 13 | - Use [RxJS](https://www.learnrxjs.io) or any other FRP libraries like 14 | [KefirJS](https://kefirjs.github.io/kefir/) or 15 | [MostJS](https://github.com/cujojs/most) or other. 16 | - Avoid of using imperative code as much as possible. 17 | - Implement: 18 | - board with multiple holes (hardcoded or dynamic generated) 19 | - a cat should appear every second in a free random place 20 | - should be possible to catch a cat 21 | - cat catch count 22 | - Define types as much and clear as possible. 23 | - Deploy your implementation on the Internet (free resources: 24 | [gh-pages](https://pages.github.com/), [surge](http://surge.sh), 25 | [netlify](http://netlify.com), [vercel](https://vercel.com)). 26 | - Have to work on the latest Google Chrome on the desktop (all other devices and 27 | browsers are up to you). 28 | 29 | All other ideas are up to you and your imagination. For example: 30 | 31 | - Implement "game over" when a user doesn't catch defined amount of cats 32 | - Dynamic generated board 33 | - Cover code by unit tests 34 | - Using React or Canvas or Native DOM elements are up to you. 35 | 36 | ## Task submission 37 | 38 | Create a new branch from your main branch, implement the task and create a 39 | Pull Request to your repository. Change accordingly the `README` file to describe 40 | your application and add the deployed link there as well. 41 | 42 | Please share the link of your Pull Request in appropriate Slack channel: 43 | [ts-bootcamp-common-2021](https://evolutiongaming.slack.com/archives/C01TBBGC18U) 44 | in the thread with the homework announcement when it is ready for review. 45 | 46 | -------------------------------------------------------------------------------- /homeworks/11-animations/Readme.md: -------------------------------------------------------------------------------- 1 | # Animations 2 | 3 | **Deadline: 30.05.2021 23:59 Minsk** 4 | 5 | 6 | Using CSS or/and SVG, you need to implement loading screen animation from this [video](https://interfaceingame.com/screenshots/overwatch-finding-game/). 7 | 8 | In your implementation, we expect to see blinking hexagons in the center and circles with radio moving stripes. 9 | 10 | ## Task submission 11 | 12 | Create a `11-animations` folder inside the `homeworks` folder in your 13 | `evo-ts-bootcamp` repository on [GitHub](https://github.com/). 14 | 15 | Create a new branch from your main branch, implement the task and create a 16 | Pull Request to your repository. Change accordingly the `README` file to describe 17 | your application and add the coverage report there. 18 | 19 | Please share the link of your Pull Request in appropriate Slack channel: 20 | [ts-bootcamp-common-2021](https://evolutiongaming.slack.com/archives/C01TBBGC18U) 21 | in the thread with the homework announcement when it is ready for review. 22 | -------------------------------------------------------------------------------- /homeworks/12-mobx-mars/readme.md: -------------------------------------------------------------------------------- 1 | # Mobx mars viewer 2 | 3 | **Deadline: 13.06.2021 23:59 Minsk** 4 | 5 | ## Task description 6 | 7 | Your task is to develop a **Mars viewer** app **on mobx**!. 8 | 9 | Please take a look our example: [Mars viewer](http://mars-viewer.surge.sh/). 10 | 11 | What is required: 12 | 13 | - Use [Create React App with TypeScript](https://create-react-app.dev/docs/adding-typescript/). 14 | - Use React for rendering 15 | - Use mobx. You can use create-react-app template for such purpose: `npx create-react-app --template typescript mobx-mars && cd mobx-mars && yarn add mobx mobx-react-lite` 16 | - Implement: 17 | - select sols 18 | - keep fetched data from nasa api in mobx-store 19 | - add to favourites 20 | - remove from favourites 21 | - Define types as much and clear as possible. 22 | - Deploy your implementation on the Internet (free resources: 23 | [gh-pages](https://pages.github.com/), [surge](http://surge.sh), 24 | [netlify](http://netlify.com), [vercel](https://vercel.com)). 25 | - Have to work on the latest Google Chrome on the desktop (all other devices and 26 | browsers are up to you). 27 | 28 | To implement this application, you need to sign up on [NASA API](https://api.nasa.gov/), and you need to use API part "Mars Rover Photos." All requests should include your API key from NASA. 29 | 30 | 31 | All other ideas are up to you and your imagination. For example: 32 | 33 | - Ability to zoom photos by click 34 | - Ability to choose other rovers. 35 | 36 | And anything you want to implement. 37 | 38 | ## Task submission 39 | 40 | Create a new branch from your main branch, implement the task and create a 41 | Pull Request to your repository. Change accordingly the `README` file to describe 42 | your application and add the deployed link there as well. 43 | 44 | Please share the link of your Pull Request in appropriate Slack channel: 45 | [ts-bootcamp-common-2021](https://evolutiongaming.slack.com/archives/C01TBBGC18U) 46 | in the thread with the homework announcement when it is ready for review. 47 | 48 | -------------------------------------------------------------------------------- /lessons/01-basic-react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /lessons/01-basic-react/README.md: -------------------------------------------------------------------------------- 1 | # 01 Basic React 2 | -------------------------------------------------------------------------------- /lessons/01-basic-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "01-basic-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^17.0.2", 7 | "react-dom": "^17.0.2", 8 | "react-scripts": "4.0.3" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": [ 18 | "react-app", 19 | "react-app/jest" 20 | ] 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lessons/01-basic-react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/01-basic-react/public/favicon.ico -------------------------------------------------------------------------------- /lessons/01-basic-react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | React App 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /lessons/01-basic-react/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/01-basic-react/public/logo192.png -------------------------------------------------------------------------------- /lessons/01-basic-react/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/01-basic-react/public/logo512.png -------------------------------------------------------------------------------- /lessons/01-basic-react/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /lessons/01-basic-react/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/common.css: -------------------------------------------------------------------------------- 1 | .app { 2 | background-color: #282c34; 3 | color: white; 4 | } 5 | 6 | .cube { 7 | width: 100px; 8 | height: 100px; 9 | position: absolute; 10 | background: grey; 11 | } 12 | 13 | .cube-left { 14 | position: absolute; 15 | left: 0; 16 | bottom: 50%; 17 | transform: translateY(50%); 18 | } 19 | 20 | .cube-right { 21 | position: absolute; 22 | right: 0; 23 | bottom: 50%; 24 | transform: translateY(50%); 25 | } 26 | 27 | .cube-up { 28 | position: absolute; 29 | right: 50%; 30 | transform: translateX(50%); 31 | top: 0; 32 | } 33 | 34 | .cube-down { 35 | position: absolute; 36 | right: 50%; 37 | transform: translateX(50%); 38 | bottom: 0; 39 | } 40 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/examples/01-JSX.jsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | function CreateElement() { 4 | const data = "Text" 5 | 6 | return React.createElement( 7 | "main", 8 | { 9 | id: "asd", 10 | style: { color: "red" }, 11 | }, 12 | [ 13 | React.createElement( 14 | "p", 15 | null, 16 | `data ${data}`, 17 | ), 18 | [(1, 2, 3)].map(el => 19 | React.createElement("p", null, el), 20 | ), 21 | ], 22 | ) 23 | } 24 | 25 | function Jsx() { 26 | const data = "Text" 27 | 28 | return ( 29 |
30 |

data {data}

31 |
32 | ) 33 | } 34 | 35 | function Interations() { 36 | return ( 37 |

38 | {[1, 2, 3].map(el => ( 39 | {el} 40 | ))} 41 |

42 | ) 43 | } 44 | 45 | function Conditional() { 46 | const conditional = Math.random() > 0.5 47 | return ( 48 |
49 | {conditional ? ( 50 |

{`Math.random < 0.5`}

51 | ) : ( 52 |

{`Math.random > 0.5`}

53 | )} 54 |
55 | ) 56 | } 57 | 58 | function Radio() { 59 | return ( 60 |
61 | 70 | 79 | 88 |
89 | ) 90 | } 91 | 92 | export { CreateElement as App } 93 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/examples/02-Components.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | class ClassComponent extends Component { 4 | constructor(props) { 5 | super(props) 6 | console.log("constructor") 7 | } 8 | 9 | componentDidMount() { 10 | console.log("componentDidMount") 11 | } 12 | 13 | componentWillUnmount() { 14 | console.log("componentWillUnmount") 15 | } 16 | 17 | render() { 18 | console.log("render") 19 | return

Hello, Class!

20 | } 21 | } 22 | 23 | const FunctionComponent = () => ( 24 |

Hello, Function!

25 | ) 26 | 27 | export class App extends Component { 28 | render() { 29 | return ( 30 |
31 | 32 | 33 |
34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/examples/03-State.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | export class App extends Component { 4 | time = new Date().toUTCString() 5 | /* 6 | constructor(props) { 7 | super(props) 8 | 9 | this.state = { 10 | time: new Date().toUTCString(), 11 | } 12 | } 13 | */ 14 | componentDidMount() { 15 | 16 | this.intervalId = setInterval(() => { 17 | const newTime = new Date().toUTCString() 18 | 19 | this.time = newTime 20 | 21 | console.log("time has changed,", newTime) 22 | }, 1000) 23 | 24 | } 25 | 26 | componentWillUnmount( ) { 27 | clearInterval(this.intervalId) 28 | } 29 | 30 | render() { 31 | return

{this.time}

32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/examples/04-Update-State.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | export class App extends Component { 4 | constructor(props) { 5 | super(props) 6 | 7 | this.state = { 8 | foo: 1, 9 | data: { a: 10, b: 13 }, 10 | } 11 | 12 | /* setTimeout(() => { 13 | this.setState(state => ({ 14 | data: { 15 | ...state.data, 16 | a: state.data.a + 10, 17 | }, 18 | })) 19 | this.setState(state => ({ 20 | data: { 21 | ...state.data, 22 | a: state.data.a + 15, 23 | }, 24 | })) 25 | }, 1000) */ 26 | } 27 | 28 | componentDidMount() { 29 | setTimeout(() => { 30 | this.setState(state => ({ 31 | foo: state.foo + 1 32 | // data: { 33 | // ...state.data, 34 | // a: state.data.a + 15, 35 | // }, 36 | })) 37 | }, 1000) 38 | } 39 | 40 | render() { 41 | console.log("render") 42 | return ( 43 |

44 | {this.state.foo} 45 | {/* {this.state.data.b} {this.state.data.a} */} 46 |

47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/examples/05-Props.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | class Child extends Component { 4 | componentDidMount() { 5 | console.log("mount") 6 | } 7 | render() { 8 | console.log("render") 9 | const { counter } = this.props 10 | return ( 11 |
12 |

Children counter: {counter}

13 |
14 | ) 15 | } 16 | } 17 | 18 | class SmallI extends Component { 19 | render() { 20 | const { children } = this.props 21 | 22 | return ( 23 | {children} 24 | ) 25 | } 26 | } 27 | 28 | export class App extends Component { 29 | state = { 30 | counter: 0, 31 | } 32 | 33 | componentDidMount() { 34 | this.intervalId = setInterval(() => { 35 | this.setState(({ counter }) => ({ 36 | counter: counter + 1, 37 | })) 38 | }, 1000) 39 | } 40 | 41 | // componentWillUnmount() {} 42 | 43 | render() { 44 | return ( 45 |
46 |

47 | Parent 48 |

49 | 50 |
51 | ) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/examples/06-Lists.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | 4 | 5 | export class App extends Component { 6 | userList = ["Artem", "Anton", "Boris"] 7 | 8 | render() { 9 | return ( 10 |
    11 | {this.userList.map(( user ) => ( 12 |
  • {user}
  • 13 | ))} 14 |
15 | ) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/examples/07-List-Keys.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | class ListItem extends Component { 4 | constructor(props) { 5 | super(props) 6 | console.log("constructor") 7 | } 8 | 9 | componentDidMount() { 10 | console.log("componentDidMount") 11 | } 12 | 13 | componentWillUnmount() { 14 | console.log("componentWillUnmount") 15 | } 16 | 17 | render() { 18 | return
  • {this.props.children}
  • 19 | } 20 | } 21 | 22 | export class App extends Component { 23 | state = { 24 | v: 1, 25 | } 26 | componentDidMount() { 27 | setInterval( 28 | () => 29 | this.setState(({ v }) => ({ v: v + 1 })), 30 | 2000, 31 | ) 32 | } 33 | render() { 34 | return ( 35 |
      36 | {[1].map(item => ( 37 | {item} 38 | ))} 39 |
    40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/examples/08-Events.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | export class App extends Component { 4 | state = { 5 | counter: 0, 6 | } 7 | 8 | handleClick = (e) => { 9 | this.setState(({ counter }) => ({ 10 | counter: counter + 1, 11 | })) 12 | } 13 | 14 | componentDidMount() { 15 | } 16 | 17 | componentWillUnmount() { 18 | } 19 | 20 | render() { 21 | return ( 22 |
    23 |

    counter: {this.state.counter}

    24 | 27 |
    28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/exercies/01-JSX.jsx: -------------------------------------------------------------------------------- 1 | export function App() { 2 | return ( 3 | /* Instead of null write html list of checkboxes: 4 | • [ ] Milk 5 | • [ ] Bread 6 | • [ ] Butter 7 | • [ ] Yogurt 8 | */ 9 | null 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/exercies/02-StateComponent.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | export class App extends Component { 4 | /* 5 | Write a countdown timer. 6 | After 20 seconds, print the word "boom". 7 | */ 8 | componentDidMount() {} 9 | 10 | componentWillUnmount() {} 11 | 12 | render() { 13 | return null 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/exercies/03-Props.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | class RandomRender extends Component { 4 | render() { 5 | /*Write a component that, depending on the props, displays the child */ 6 | return null 7 | } 8 | } 9 | 10 | export class App extends Component { 11 | render() { 12 | return ( 13 |
    14 |

    Random render

    15 | I'm here! 16 |
    17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/exercies/04-Events.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | export class App extends Component { 4 | /* 5 | Use the buttons to control the position of the cube. 6 | (style: {transform: translate}) 7 | */ 8 | render () { 9 | return
    10 | 11 | 12 | 13 | 14 |
    15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/exercies/05-TODO.jsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | class TodoApp extends Component { 4 | /* Implement todo app */ 5 | state = { 6 | input: "", 7 | todos: [{ 8 | done: false, 9 | text: "Buy milk" 10 | }] 11 | } 12 | 13 | handleKeyPress = (event) => { 14 | if (event.key === "Enter") { 15 | 16 | } 17 | } 18 | 19 | 20 | render () { 21 | return
    22 |
      23 | 24 |
      25 | } 26 | 27 | renderTodoItem = (item) => { 28 | return
    • 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lessons/01-basic-react/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 | text-align: center; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /lessons/01-basic-react/src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom" 3 | 4 | import "./index.css" 5 | import "./common.css" 6 | 7 | // import { App } from "./examples/01-JSX" 8 | // import { App } from "./examples/02-Components" 9 | // import { App } from "./examples/03-State" 10 | // import { App } from "./examples/04-Update-State" 11 | // import { App } from "./examples/05-Props" 12 | // import { App } from "./examples/06-Lists" 13 | // import { App } from "./examples/08-Events" 14 | 15 | import { App } from "./exercies/04-Events" 16 | 17 | 18 | ReactDOM.render( 19 | , 20 | document.getElementById("root"), 21 | ) 22 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/README.md: -------------------------------------------------------------------------------- 1 | # Basic Unit Tests 2 | 3 | In this lecture we will learn: 4 | 5 | - how to adapt the `jest config` to work with `typescript`, 6 | - how to write unit tests using `jest` and `typescript`, 7 | - what is snapshot testing, 8 | - what is TDD. 9 | 10 | ### Getting Started 11 | 12 | #### Prerequisites 13 | 14 | ``` 15 | $ yarn 16 | $ yarn add --dev jest typescript 17 | ``` 18 | 19 | #### Installing 20 | 21 | ``` 22 | $ yarn add --dev ts-jest @types/jest 23 | ``` 24 | 25 | #### Creating config 26 | 27 | ``` 28 | $ yarn ts-jest config:init 29 | ``` 30 | 31 | #### Running tests 32 | 33 | ``` 34 | $ yarn test 35 | ``` 36 | 37 | #### Using typescript configuration file 38 | 39 | Create `tsconfig.json` file: 40 | 41 | ```json 42 | { 43 | "compilerOptions": { 44 | "strict": true, 45 | "noEmit": true, 46 | "skipLibCheck": true, 47 | "moduleResolution": "node" 48 | } 49 | } 50 | ``` 51 | 52 | And `tsconfig.test.json` file: 53 | 54 | ```json 55 | { 56 | "extends": "./tsconfig.json" 57 | } 58 | ``` 59 | 60 | Add path to `tsconfig` file: 61 | 62 | ```js 63 | module.exports = { 64 | preset: 'ts-jest', 65 | testEnvironment: 'node', 66 | globals: { 67 | 'ts-jest': { 68 | tsconfig: 'tsconfig.test.json', 69 | }, 70 | }, 71 | }; 72 | ``` 73 | 74 | ### Unit tests Overview 75 | 76 | Go through `src/01-pure-code.ts` 77 | 78 | Go through `src/02-mocking.ts` 79 | 80 | Go through `src/03-mocking-modules.ts` 81 | 82 | Go through `src/04-snapshot.ts` 83 | 84 | ## Useful links 85 | 86 | - [Jest API Docs](https://jestjs.io/docs/api) 87 | - [Mock Functions](https://jestjs.io/docs/mock-functions) 88 | - [A guide to unit testing in JavaScript](https://github.com/mawrkus/js-unit-testing-guide) 89 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic-unit-tests", 3 | "version": "0.0.1", 4 | "description": "05. Basic Unit Tests Lesson.", 5 | "license": "MIT", 6 | "scripts": { 7 | "test": "jest", 8 | "test:watch": "jest --watch-all --coverage" 9 | }, 10 | "devDependencies": { 11 | }, 12 | "dependencies": { 13 | "axios": "^0.21.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/src/01-pure-code.test.ts: -------------------------------------------------------------------------------- 1 | import { fibonacci, isPrime } from "./01-pure-code" 2 | 3 | describe("01-pure-code", () => { 4 | /** 5 | * Example 1 6 | */ 7 | describe("fibonacci", () => { 8 | it("asserts 3th number = 2", () => { 9 | expect(fibonacci(3)).toEqual(2) 10 | }) 11 | 12 | it("asserts 10th number = 55", () => { 13 | expect(fibonacci(10)).toEqual(55) 14 | }) 15 | 16 | it("asserts 0th number to be defined", () => { 17 | expect(fibonacci(0)).toBeDefined() 18 | }) 19 | }) 20 | 21 | /** 22 | * Example 2 23 | */ 24 | describe.each([ 25 | [0, 0], 26 | [1, 1], 27 | [2, 1], 28 | [3, 2], 29 | [4, 3], 30 | [5, 5], 31 | [6, 8], 32 | [7, 13], 33 | ])("asserts %i number = %i", (n, expected) => { 34 | expect(fibonacci(n)).toEqual(expected) 35 | }) 36 | 37 | /** 38 | * Exercise 1 39 | */ 40 | describe("isPrime", () => { 41 | it.todo("asserts 19 to be prime") 42 | 43 | it.todo("asserts 25 not to be prime") 44 | 45 | it.todo("throws an error for NaN argument") 46 | 47 | it.todo("throws an error for infinite argument") 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/src/01-pure-code.ts: -------------------------------------------------------------------------------- 1 | export const fibonacci = (n: number): number => 2 | n < 1 3 | ? 0 4 | : n <= 2 5 | ? 1 6 | : fibonacci(n - 1) + fibonacci(n - 2) 7 | 8 | export const isPrime = (num: number): boolean => { 9 | if (Number.isNaN(num)) { 10 | throw new Error("NaN argument passed.") 11 | } 12 | 13 | if (!Number.isFinite(num)) { 14 | throw new Error("Infinite argument passed.") 15 | } 16 | 17 | for (let i = 2, s = Math.sqrt(num); i <= s; i++) { 18 | if (num % i === 0) { 19 | return false 20 | } 21 | } 22 | 23 | return num > 1 24 | } 25 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/src/02-mocking.ts: -------------------------------------------------------------------------------- 1 | export interface KeyValueStore { 2 | put(k: number, v: number): void; 3 | get(k: number): number | undefined; 4 | } 5 | 6 | export const keyValueStore = (): KeyValueStore => { 7 | const store = new Map() 8 | 9 | return { 10 | get: (k) => store.get(k), 11 | put: (k, v) => { 12 | store.set(k, v); 13 | }, 14 | } 15 | } 16 | 17 | export const useKV = (kv: KeyValueStore) => { 18 | kv.put(0, 1) 19 | kv.put(1, 2) 20 | kv.put(2, 3) 21 | 22 | return kv.get(2) 23 | } 24 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/src/03-mocking-modules.test.ts: -------------------------------------------------------------------------------- 1 | import { fetchTodos } from "./03-mocking-modules" 2 | import axios from "axios" 3 | import { mocked } from "ts-jest/utils" 4 | 5 | jest.mock("axios") 6 | 7 | const mockAxios = mocked(axios, true) 8 | afterEach(() => { 9 | mockAxios.mockReset() 10 | }) 11 | 12 | describe.skip("03-mocking-modules", () => { 13 | describe("fetchTodos", () => { 14 | /** 15 | * Example 1 16 | */ 17 | it("asserts using a proper API url", async () => { 18 | await fetchTodos() 19 | expect(axios.get).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/todos/') 20 | }) 21 | 22 | /** 23 | * Example 2 24 | */ 25 | it("asserts using a proper API url with params", async () => { 26 | await fetchTodos(1) 27 | expect(axios.get).toHaveBeenCalledWith('https://jsonplaceholder.typicode.com/todos/1') 28 | }) 29 | 30 | /** 31 | * Exercise 1 32 | */ 33 | it.todo("asserts returning a list of todos") 34 | 35 | /** 36 | * Exercise 2 37 | */ 38 | it.todo("asserts returning an empty list if there is an error") 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/src/03-mocking-modules.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | 3 | export interface Todo { 4 | userId: number; 5 | id: number; 6 | title: string; 7 | completed: boolean; 8 | } 9 | 10 | interface FetchTodos { 11 | (): Promise; 12 | (id?: number): Promise 13 | } 14 | 15 | export const fetchTodos: FetchTodos = async (id?: number) => { 16 | try { 17 | const response = await axios.get(`https://jsonplaceholder.typicode.com/todos/${id ?? ''}`) 18 | return response.data 19 | } catch { 20 | return [] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/src/04-snapshot.test.ts: -------------------------------------------------------------------------------- 1 | import { createDocument, createParagraph } from "./04-snapshot" 2 | 3 | describe.skip("04-snapshot", () => { 4 | /** 5 | * Example 1 6 | */ 7 | describe("createParagraph", () => { 8 | it("asserts returning

      element", () => { 9 | expect(createParagraph("This is Paragraph")).toMatchSnapshot() 10 | }) 11 | }) 12 | 13 | describe("createDocument", () => { 14 | it.todo("asserts returning proper document") 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/src/04-snapshot.ts: -------------------------------------------------------------------------------- 1 | export const createParagraph = (text: string): string => 2 | `

      ${text}

      ` 3 | 4 | export const createDocument = (title: string, content: string) => ({ 5 | head: { 6 | title, 7 | }, 8 | body: { 9 | content, 10 | }, 11 | }) 12 | -------------------------------------------------------------------------------- /lessons/05-basic-unit-tests/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | axios@^0.21.1: 6 | version "0.21.1" 7 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" 8 | integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== 9 | dependencies: 10 | follow-redirects "^1.10.0" 11 | 12 | follow-redirects@^1.10.0: 13 | version "1.13.3" 14 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" 15 | integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== 16 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testEnvironment: "jsdom", 4 | globals: { 5 | "ts-jest": { 6 | tsconfig: "tsconfig.test.json", 7 | }, 8 | }, 9 | setupFilesAfterEnv: ["./jest.setup.ts"], 10 | } 11 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/jest.setup.ts: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom" 2 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "advanced-unit-tests", 3 | "version": "0.0.1", 4 | "description": "10. Advanced Unit Tests Lesson.", 5 | "license": "MIT", 6 | "scripts": { 7 | "test": "jest", 8 | "test:simple-check": "jest 01-basic.test.tsx", 9 | "test:watch": "jest --watch-all --coverage" 10 | }, 11 | "devDependencies": { 12 | "@testing-library/jest-dom": "^5.12.0", 13 | "@testing-library/react": "^11.2.6", 14 | "@testing-library/react-hooks": "^5.1.2", 15 | "@types/jest": "^26.0.22", 16 | "@types/react": "^17.0.4", 17 | "@types/react-dom": "^17.0.3", 18 | "jest": "^26.6.3", 19 | "ts-jest": "^26.5.4", 20 | "typescript": "^4.2.4" 21 | }, 22 | "dependencies": { 23 | "react": "^17.0.2", 24 | "react-dom": "^17.0.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/01-basic.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as ReactDom from "react-dom" 3 | import { Message } from "./01-basic" 4 | 5 | describe("01-basic", () => { 6 | describe("Message", () => { 7 | /** 8 | * Without using any library except react, react-dom & jest 9 | */ 10 | it("assert that Message renders a div with the correct message inside", () => { 11 | const container = document.createElement("div") 12 | 13 | ReactDom.render(, container) 14 | 15 | expect( 16 | container.querySelector("div [data-testid='message-box']")?.innerHTML 17 | ).toBe("hello") 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/01-basic.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | export const Message = ({ message }: { message: string }) => ( 4 |
      7 | {message} 8 |
      9 | ) 10 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/02-basic-rtl.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { render } from "@testing-library/react" 3 | import { Message } from "./01-basic" 4 | 5 | describe("02-basic-RTL", () => { 6 | describe("Message", () => { 7 | it("assert that Message renders a div with the correct message inside", () => { 8 | const { getByTestId } = render() 9 | expect(getByTestId("message-box").innerHTML).toBe("hello") 10 | }) 11 | 12 | it("assert that Message renders a div with the correct message inside (jest-dom-testing-library)", () => { 13 | const { getByTestId } = render() 14 | expect(getByTestId("message-box")).toContainHTML("hello") 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/03-timer.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { act, render } from "@testing-library/react" 3 | import { Timer } from "./03-timer" 4 | 5 | beforeEach(() => { 6 | jest 7 | .useFakeTimers("modern") 8 | .setSystemTime(new Date("2021-05-03").getTime()) 9 | }) 10 | 11 | afterEach(() => { 12 | jest 13 | .useRealTimers() 14 | }) 15 | 16 | describe("03-timer", () => { 17 | describe("Timer", () => { 18 | it("renders a component", () => { 19 | expect(render()).toBeDefined() 20 | }) 21 | 22 | it("renders current datetime by default", () => { 23 | const { getByTestId } = render() 24 | expect(getByTestId("datetime")).toHaveTextContent("2021-05-03 00:00:00") 25 | }) 26 | 27 | it("does not change datetime until start button is clicked", () => { 28 | const { getByTestId } = render() 29 | act(() => { 30 | jest.advanceTimersByTime(1000) 31 | }) 32 | expect(getByTestId("datetime")).toHaveTextContent("2021-05-03 00:00:00") 33 | }) 34 | 35 | it.todo("changes datetime each seconds when start button is clicked") 36 | 37 | it.todo("stops updating datetime when stop button is clicked") 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/03-timer.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react" 2 | import { getUTCDatetime } from "./time" 3 | 4 | export const Timer = () => { 5 | const [datetime, setDatetime] = useState(getUTCDatetime()) 6 | const [running, setRunning] = useState(false) 7 | 8 | useEffect(() => { 9 | if (!running) { 10 | return 11 | } 12 | 13 | const intervalId = setInterval(() => setDatetime(getUTCDatetime()), 1000) 14 | return () => { 15 | clearInterval(intervalId) 16 | } 17 | }, [running]) 18 | 19 | return ( 20 | <> 21 |
      {datetime}
      22 | 23 | 24 | 25 | ) 26 | } 27 | 28 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/04-hooks.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { act, renderHook } from "@testing-library/react-hooks" 3 | import { useTimer } from "./04-hooks" 4 | 5 | beforeEach(() => { 6 | jest 7 | .useFakeTimers("modern") 8 | .setSystemTime(new Date("2021-05-03").getTime()) 9 | }) 10 | 11 | afterEach(() => { 12 | jest 13 | .useRealTimers() 14 | }) 15 | 16 | describe("04-hooks", () => { 17 | describe("useTimer", () => { 18 | it("renders a hook", () => { 19 | expect(renderHook(() => useTimer())).toBeDefined() 20 | }) 21 | 22 | it("has a proper default value", () => { 23 | const { result } = renderHook(() => useTimer()) 24 | expect(result.current.datetime).toBe("2021-05-03 00:00:00") 25 | }) 26 | 27 | it("does not change datetime until start is fired", () => { 28 | const { result } = renderHook(() => useTimer()) 29 | act(() => { 30 | jest.advanceTimersByTime(1000) 31 | }) 32 | expect(result.current.datetime).toBe("2021-05-03 00:00:00") 33 | }) 34 | 35 | it("changes datetime each seconds after start is fired", () => { 36 | const { result } = renderHook(() => useTimer()) 37 | act(() => { 38 | result.current.start() 39 | }) 40 | act(() => { 41 | jest.advanceTimersByTime(1000) 42 | }) 43 | 44 | expect(result.current.datetime).toBe("2021-05-03 00:00:01") 45 | }) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/04-hooks.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useEffect, useState } from "react" 2 | import { getUTCDatetime } from "./time" 3 | 4 | export const useTimer = () => { 5 | const [datetime, setDatetime] = useState(getUTCDatetime()) 6 | const [running, setRunning] = useState(false) 7 | 8 | useEffect(() => { 9 | if (!running) { 10 | return 11 | } 12 | 13 | const intervalId = setInterval(() => setDatetime(getUTCDatetime()), 1000) 14 | return () => { 15 | clearInterval(intervalId) 16 | } 17 | }, [running]) 18 | 19 | const start = useCallback(() => setRunning(true), []) 20 | const stop = useCallback(() => setRunning(false), []) 21 | 22 | return { 23 | datetime, 24 | start, 25 | stop, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/05-hooks-task.test.ts: -------------------------------------------------------------------------------- 1 | import { act, renderHook } from "@testing-library/react-hooks" 2 | import { useEventListener } from "./05-hooks-task" 3 | 4 | describe("05-hooks-task", () => { 5 | describe("useEventListener", () => { 6 | const eventName = "click" 7 | const handler = jest.fn() 8 | const listeners = new Map void>() 9 | const element = document.createElement("div") 10 | 11 | beforeEach(() => { 12 | spyOn(element, "addEventListener").and.callFake((eventName: string, handler: (...args: any[]) => void) => { 13 | listeners.set(eventName, handler) 14 | }) 15 | 16 | spyOn(element, "removeEventListener").and.callFake((eventName: string, handler: (...args: any[]) => void) => { 17 | listeners.delete(eventName) 18 | }) 19 | }) 20 | 21 | afterEach(() => { 22 | listeners.clear() 23 | handler.mockReset() 24 | }) 25 | 26 | it.todo("renders a hook") 27 | 28 | it.todo("adds correct event listener") 29 | 30 | it.todo("removes attached event listener") 31 | 32 | it.todo("re-attaches event listener if event name is changed") 33 | 34 | it.todo("does not re-attach event listener if event handler is changed") 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/05-hooks-task.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react" 2 | 3 | type Handler = (event: Event) => void 4 | type Props = { 5 | eventName: string; 6 | handler: Handler; 7 | element: Element; 8 | } 9 | 10 | export const useEventListener = ({ eventName, handler, element }: Props) => { 11 | const savedHandler = useRef(handler) 12 | 13 | useEffect(() => { 14 | savedHandler.current = handler 15 | }, [handler]) 16 | 17 | useEffect(() => { 18 | const isSupported = element && element.addEventListener 19 | if (!isSupported) { 20 | return 21 | } 22 | 23 | const eventListener: Handler = (event) => savedHandler.current(event) 24 | element.addEventListener(eventName, eventListener) 25 | 26 | return () => { 27 | element.removeEventListener(eventName, eventListener) 28 | } 29 | }, [eventName, element]) 30 | } 31 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/06-hooks-usage.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { act, render } from "@testing-library/react" 3 | import { mocked } from "ts-jest/utils" 4 | import { Timer } from "./06-hooks-usage" 5 | import { useTimer } from "./04-hooks" 6 | 7 | jest.mock("./04-hooks") 8 | 9 | const mockUseTimer = mocked(useTimer) 10 | afterEach(() => { 11 | mockUseTimer.mockReset() 12 | }) 13 | 14 | describe("06-hooks-usage", () => { 15 | describe("Timer", () => { 16 | it.todo("uses hook value by default") 17 | it.todo("handles click on start button") 18 | it.todo("handles click on stop button") 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/06-hooks-usage.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { useTimer } from "./04-hooks" 3 | 4 | export const Timer = () => { 5 | const { datetime, start, stop } = useTimer() 6 | 7 | return ( 8 | <> 9 |
      {datetime}
      10 | 11 | 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/07-snapshot.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { render } from "@testing-library/react" 3 | import { Message } from "./07-snapshot" 4 | 5 | describe("07-snapshot", () => { 6 | describe("Message", () => { 7 | it("has a proper content #1", () => { 8 | const { container } = render( 9 | 13 | ) 14 | expect(container.firstChild).toMatchSnapshot() 15 | }) 16 | 17 | it("has a proper content #2", () => { 18 | const { container } = render( 19 | 22 | ) 23 | expect(container.firstChild).toMatchSnapshot() 24 | }) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/07-snapshot.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | export const Message = ({ message, color }: { message: string, color?: string }) => ( 4 |
      9 | {message} 10 |
      11 | ) 12 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/src/time.ts: -------------------------------------------------------------------------------- 1 | export const getUTCDatetime = () => formatDatetime(new Date()) 2 | 3 | const formatDatetime = (date: Date) => `${formatDate(date)} ${formatTime(date)}` 4 | 5 | const formatDate = (date: Date) => 6 | `${date.getUTCFullYear()}-${padZero(date.getUTCMonth() + 1)}-${padZero(date.getUTCDate())}` 7 | 8 | const formatTime = (date: Date) => 9 | `${padZero(date.getUTCHours())}:${padZero(date.getUTCMinutes())}:${padZero(date.getUTCSeconds())}` 10 | 11 | const padZero = (value: number) => value < 10 ? "0" + value : value.toString() 12 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "noEmit": true, 5 | "skipLibCheck": true, 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "jsx": "react" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lessons/10-advanced-unit-tests/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /lessons/12-qa/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["airbnb-typescript"], 3 | 4 | parserOptions: { 5 | sourceType: "module", 6 | tsconfigRootDir: __dirname, 7 | project: "./tsconfig.json", 8 | }, 9 | 10 | rules: { 11 | "import/prefer-default-export": "off", 12 | "react/state-in-constructor": "off" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/12-qa/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /lessons/12-qa/README.md: -------------------------------------------------------------------------------- 1 | ## Q&A 2 | 3 | - clean чистить тесты, стили, компоненты от CRA 4 | - NodeJS.Timeout 5 | - private props and methods without starting _ (e.g., _length) 6 | - bitwise operators - do you really need it? 7 | - не стесняйтесь использовать destructuring для props & state 8 | - business logic должна быть отделена от компонента 9 | - naming (variables, args, functions, etc) - не сокращать, не использовать одну букву, не писать непонятные аббревиатуры и тд 10 | - bind in render (or arrow functions) 11 | - не нужно писать отдельные типы для функции, где не уместно это 12 | - classnames 13 | - eslint / airbnb 14 | -------------------------------------------------------------------------------- /lessons/12-qa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "12-qa", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "@types/jest": "^26.0.15", 10 | "@types/node": "^12.0.0", 11 | "@types/react": "^17.0.0", 12 | "@types/react-dom": "^17.0.0", 13 | "@typescript-eslint/eslint-plugin": "^4.22.0", 14 | "eslint-config-airbnb-typescript": "^12.3.1", 15 | "eslint-plugin-import": "^2.22.1", 16 | "eslint-plugin-jsx-a11y": "^6.4.1", 17 | "eslint-plugin-react": "^7.23.2", 18 | "eslint-plugin-react-hooks": "^4.2.0", 19 | "react": "^17.0.2", 20 | "react-dom": "^17.0.2", 21 | "react-scripts": "4.0.3", 22 | "typescript": "^4.1.2", 23 | "web-vitals": "^1.0.1" 24 | }, 25 | "scripts": { 26 | "start": "react-scripts start", 27 | "build": "react-scripts build", 28 | "test": "react-scripts test", 29 | "eject": "react-scripts eject" 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lessons/12-qa/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/12-qa/public/favicon.ico -------------------------------------------------------------------------------- /lessons/12-qa/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
      32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /lessons/12-qa/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/12-qa/public/logo192.png -------------------------------------------------------------------------------- /lessons/12-qa/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/12-qa/public/logo512.png -------------------------------------------------------------------------------- /lessons/12-qa/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /lessons/12-qa/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /lessons/12-qa/src/examples/01-bind-in-render.tsx: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | 3 | interface AppState { 4 | counter: number; 5 | } 6 | 7 | class BindInRender extends Component<{}, AppState> { 8 | state = { 9 | counter: 0, 10 | }; 11 | 12 | onIncrease = () => { 13 | this.setState((s) => ({ counter: s.counter + 1 })); 14 | }; 15 | 16 | onDecrease = () => { 17 | this.setState((state) => ({ ...state, counter: state.counter - 1 })); 18 | }; 19 | 20 | render() { 21 | const { counter } = this.state; 22 | 23 | return ( 24 |
      25 |

      {counter}

      26 | 27 | 28 |
      29 | ); 30 | } 31 | } 32 | 33 | export { BindInRender as App }; 34 | -------------------------------------------------------------------------------- /lessons/12-qa/src/examples/02-Node-timer.tsx: -------------------------------------------------------------------------------- 1 | import { Component } from "react" 2 | 3 | class NodeTimer extends Component { 4 | timerId: number | undefined 5 | 6 | state = { 7 | timer: 0, 8 | } 9 | 10 | componentDidMount() { 11 | this.timerId = window.setInterval(() => { 12 | this.setState({ timer: this.state.timer + 1 }) 13 | }, 1000) 14 | } 15 | 16 | componentWillUnmount() { 17 | clearInterval(this.timerId) 18 | } 19 | 20 | render() { 21 | return

      timer: {this.state.timer}

      22 | } 23 | } 24 | 25 | export { NodeTimer as App } 26 | -------------------------------------------------------------------------------- /lessons/12-qa/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /lessons/12-qa/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import ReactDOM from "react-dom" 3 | import "./index.css" 4 | import { App } from "./examples/01-bind-in-render" 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById("root"), 11 | ) 12 | 13 | -------------------------------------------------------------------------------- /lessons/12-qa/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /lessons/12-qa/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import "@testing-library/jest-dom" 6 | -------------------------------------------------------------------------------- /lessons/12-qa/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 | "noImplicitAny": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "module": "esnext", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "noEmit": true, 22 | "jsx": "react-jsx" 23 | }, 24 | "include": [ 25 | "src" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /lessons/14-network/006-websocket-server-node/index.ts: -------------------------------------------------------------------------------- 1 | import WebSocket from "ws"; 2 | import faker from "faker"; 3 | 4 | const wss = new WebSocket.Server({ 5 | port: 8080, 6 | }); 7 | 8 | const emails = Array 9 | .from({length: 100}, (_, i) => 10 | faker.internet.email()); 11 | 12 | wss.on('connection', function connection(ws) { 13 | ws.on('message', function incoming(message) { 14 | console.log(message); 15 | if (typeof message === "string") { 16 | try { 17 | const { data } = JSON.parse(message); 18 | const filteredEmails = emails 19 | .filter(email => email.match(new RegExp(data, 'g'))); 20 | ws.send(JSON.stringify(filteredEmails)); 21 | } catch (ex) { 22 | console.error(ex); 23 | } 24 | } 25 | }); 26 | 27 | ws.send(JSON.stringify(emails)); 28 | }); 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /lessons/14-network/006-websocket-server-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "006-websocket-server-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@types/express": "^4.17.11", 14 | "@types/faker": "^5.5.5", 15 | "@types/ws": "^7.4.4", 16 | "express": "^4.17.1", 17 | "faker": "^5.5.3", 18 | "ws": "^7.4.5" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lessons/14-network/006-websocket-server-node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "rootDir": "./", 6 | "outDir": "./build", 7 | "esModuleInterop": true, 8 | "strict": true 9 | } 10 | } -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/.env.development: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | NAME=vlad_kovaliov 3 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/craco.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | style: { 3 | postcss: { 4 | plugins: [ 5 | require('tailwindcss'), 6 | require('autoprefixer'), 7 | ], 8 | }, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "002-bucket-ui", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@craco/craco": "^6.1.1", 7 | "@tailwindcss/postcss7-compat": "^2.0.4", 8 | "@testing-library/jest-dom": "^5.11.4", 9 | "@testing-library/react": "^11.1.0", 10 | "@testing-library/user-event": "^12.1.10", 11 | "@types/jest": "^26.0.15", 12 | "@types/node": "^12.0.0", 13 | "@types/ramda": "^0.27.39", 14 | "@types/react": "^17.0.0", 15 | "@types/react-dom": "^17.0.0", 16 | "@types/react-redux": "^7.1.16", 17 | "@types/redux": "^3.6.0", 18 | "@types/redux-thunk": "^2.1.0", 19 | "@types/reselect": "^2.2.0", 20 | "autoprefixer": "^9", 21 | "http-proxy-middleware": "^1.1.0", 22 | "postcss": "^7", 23 | "ramda": "^0.27.1", 24 | "react": "^17.0.2", 25 | "react-dom": "^17.0.2", 26 | "react-redux": "^7.2.3", 27 | "react-scripts": "4.0.3", 28 | "react-simple-keyboard": "^3.0.85", 29 | "redux": "^4.0.5", 30 | "redux-devtools-extension": "^2.13.9", 31 | "redux-thunk": "^2.3.0", 32 | "reselect": "^4.0.0", 33 | "simple-keyboard": "^3.0.65", 34 | "tailwindcss": "npm:@tailwindcss/postcss7-compat", 35 | "typescript": "^4.1.2", 36 | "web-vitals": "^1.0.1" 37 | }, 38 | "scripts": { 39 | "start": "craco start", 40 | "build": "react-scripts build", 41 | "test": "react-scripts test", 42 | "eject": "react-scripts eject" 43 | }, 44 | "eslintConfig": { 45 | "extends": [ 46 | "react-app", 47 | "react-app/jest" 48 | ] 49 | }, 50 | "browserslist": { 51 | "production": [ 52 | ">0.2%", 53 | "not dead", 54 | "not op_mini all" 55 | ], 56 | "development": [ 57 | "last 1 chrome version", 58 | "last 1 firefox version", 59 | "last 1 safari version" 60 | ] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/14-network/007-react-keyboard/public/favicon.ico -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
      32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/14-network/007-react-keyboard/public/logo192.png -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/14-network/007-react-keyboard/public/logo512.png -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/14-network/007-react-keyboard/src/App.css -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import './App.css'; 3 | import 'react-simple-keyboard/build/css/index.css'; 4 | import { List, InputConnected, KeyboardConnected } from "./components"; 5 | 6 | 7 | 8 | function App() { 9 | return ( 10 |
      11 |
      12 | 13 |
      14 |
      15 | 16 |
      17 |
      18 | 19 |
      20 |
      21 | ); 22 | } 23 | 24 | export default App; 25 | 26 | 27 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/components/Input.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {useSelector} from "react-redux"; 3 | import {State} from "../types"; 4 | 5 | export function Input({ text }: { text: string; }) { 6 | return ( 7 |
      8 | 9 |
      10 | ); 11 | } 12 | 13 | export function InputConnected() { 14 | const search = useSelector((state: State) => state.search); 15 | return ( 16 | 17 | ); 18 | } -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/components/Keyboard.tsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from "react"; 2 | import {useDispatch} from "react-redux"; 3 | import {change, search} from "../modules/thunks"; 4 | import Keyboard from "react-simple-keyboard"; 5 | 6 | 7 | export function KeyboardConnected() { 8 | const [layout, setLayout] = useState("default"); 9 | const dispatch = useDispatch(); 10 | const handleChange = React.useCallback((event) => { 11 | dispatch(change(event)); 12 | }, [dispatch]); 13 | const handleKeyPress = React.useCallback((event) => { 14 | switch (event) { 15 | case "{enter}": { 16 | dispatch(search()); 17 | break; 18 | } 19 | case "{shift}": 20 | case "{lock}": { 21 | setLayout(layout === "default" ? "shift" : "default"); 22 | break; 23 | } 24 | } 25 | }, [dispatch, layout, setLayout]); 26 | 27 | return ( 28 | 33 | ) 34 | } -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/components/List.tsx: -------------------------------------------------------------------------------- 1 | import {useSelector} from "react-redux"; 2 | import React from "react"; 3 | 4 | export function List() { 5 | const emails = useSelector((state: { emails: [] }) => state.emails); 6 | return ( 7 |
      8 |
        9 | {emails.map(email => ( 10 |
      • {email}
      • 11 | ))} 12 |
      13 |
      14 | ) 15 | } 16 | 17 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Input"; 2 | export * from "./List"; 3 | export * from "./Keyboard"; -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const KEYBOARD_ENTER = "keyboard/enter"; 2 | export const KEYBOARD_CHANGE = "keyboard/change"; 3 | export const EMAIL_UPDATES = "emails/update"; -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html { 6 | height: 100%; 7 | } 8 | 9 | body { 10 | height: inherit; 11 | margin: 0; 12 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 13 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 14 | sans-serif; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | code { 20 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 21 | monospace; 22 | } 23 | 24 | #root { 25 | height: inherit; 26 | } 27 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | // bc1ba4dcc67846e1b8b5560f2dbad36a 7 | 8 | import { Provider } from "react-redux"; 9 | import { store } from "./modules"; 10 | 11 | ReactDOM.render( 12 | 13 | 14 | 15 | 16 | , 17 | document.getElementById('root') 18 | ); 19 | 20 | // If you want to start measuring performance in your app, pass a function 21 | // to log results (for example: reportWebVitals(console.log)) 22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 23 | reportWebVitals(); 24 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/modules/emails/index.ts: -------------------------------------------------------------------------------- 1 | import {EMAIL_UPDATES} from "../../constants"; 2 | 3 | export function emails(emails = [], action: any) { 4 | const { type, payload } = action; 5 | switch(type) { 6 | case EMAIL_UPDATES: 7 | return payload; 8 | default: { 9 | return emails; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/modules/index.ts: -------------------------------------------------------------------------------- 1 | import { createStore, combineReducers, applyMiddleware } from "redux"; 2 | import thunk from "redux-thunk"; 3 | 4 | import { composeWithDevTools } from 'redux-devtools-extension'; 5 | import { emails } from "./emails"; 6 | import { search } from "./search"; 7 | import {EMAIL_UPDATES} from "../constants"; 8 | 9 | const ws = new WebSocket("ws://localhost:8080"); 10 | 11 | export const store = createStore( 12 | combineReducers({ 13 | emails: emails, 14 | search: search, 15 | }), 16 | composeWithDevTools( 17 | applyMiddleware( 18 | thunk.withExtraArgument(ws) 19 | ) 20 | ), 21 | ); 22 | 23 | ws.onmessage = (msg) => { 24 | try { 25 | console.log(msg); 26 | const emails: string[] = JSON.parse(msg.data); 27 | store.dispatch({ 28 | type: EMAIL_UPDATES, 29 | payload: emails, 30 | }); 31 | } catch (ex) { 32 | console.error(ex); 33 | } 34 | }; 35 | 36 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/modules/search/index.ts: -------------------------------------------------------------------------------- 1 | import {KEYBOARD_CHANGE} from "../../constants"; 2 | 3 | export function search(search = "", action: any) { 4 | const { type, payload } = action; 5 | switch (type) { 6 | case KEYBOARD_CHANGE: { 7 | return payload; 8 | } 9 | default: { 10 | return search; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/modules/thunks.ts: -------------------------------------------------------------------------------- 1 | import {ThunkAction} from "redux-thunk"; 2 | import {State} from "../types"; 3 | import {Action} from "redux"; 4 | import {KEYBOARD_CHANGE, KEYBOARD_ENTER} from "../constants"; 5 | 6 | export function search(): ThunkAction, State, WebSocket, Action> { 7 | return (dispatch, getState, ws) => { 8 | return new Promise((res) => { 9 | ws.send(JSON.stringify({ search: getState().search })); 10 | dispatch({ type: KEYBOARD_ENTER }); 11 | res(); 12 | }); 13 | } 14 | } 15 | 16 | export function change(value: string): ThunkAction, State, WebSocket, Action> { 17 | return (dispatch, getState, ws) => { 18 | return new Promise((res) => { 19 | ws.send(JSON.stringify({ data: value })); 20 | dispatch({ type: KEYBOARD_CHANGE, payload: value }); 21 | res(value); 22 | }); 23 | }; 24 | } -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/services/api.ts: -------------------------------------------------------------------------------- 1 | 2 | export async function getPizza() { 3 | const res = await fetch("/pizza"); 4 | const json = await res.json(); 5 | 6 | return { 7 | items: json._items, 8 | }; 9 | } 10 | 11 | interface LogPayload { 12 | name: string; 13 | timestamp: string; 14 | } 15 | 16 | export async function log(payload: LogPayload = { 17 | name: process.env.NAME!, 18 | timestamp: "123", 19 | }) { 20 | await fetch("/log", { 21 | body: JSON.stringify(payload), 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/setupProxy.js: -------------------------------------------------------------------------------- 1 | const { createProxyMiddleware } = require('http-proxy-middleware'); 2 | 3 | module.exports = function(app) { 4 | app 5 | .use( 6 | '/pizza', 7 | createProxyMiddleware({ 8 | target: 'http://localhost:5000', 9 | changeOrigin: false, 10 | }) 11 | ); 12 | app.use( 13 | '/log', 14 | createProxyMiddleware({ 15 | target: 'http://localhost:5000', 16 | changeOrigin: false, 17 | }) 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface State { 2 | emails: [], 3 | search: string, 4 | } -------------------------------------------------------------------------------- /lessons/14-network/007-react-keyboard/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 | -------------------------------------------------------------------------------- /lessons/14-network/009-rest-nodejs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine as build 2 | 3 | WORKDIR /usr/src/app 4 | ENV PATH /usr/src/app/node_modules/.bin:$PATH 5 | 6 | ARG PORT 7 | 8 | ENV PORT=$PORT 9 | 10 | COPY package.json /usr/src/app/package.json 11 | RUN npm install 12 | 13 | COPY . /usr/src/app 14 | 15 | EXPOSE $PORT 16 | CMD ["ts-node", "index.ts"] 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lessons/14-network/009-rest-nodejs/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.2' 2 | 3 | services: 4 | nodeserver: 5 | container_name: my-container-name 6 | hostname: nodeserver 7 | build: 8 | context: . 9 | args: 10 | - PORT=8081 11 | volumes: 12 | - '.:/usr/src/app' 13 | - '/usr/src/app/node_modules' 14 | networks: 15 | - my-network 16 | nodeserver2: 17 | container_name: my-container-name2 18 | hostname: nodeserver2 19 | build: 20 | context: . 21 | args: 22 | - PORT=8082 23 | volumes: 24 | - '.:/usr/src/app' 25 | - '/usr/src/app/node_modules' 26 | networks: 27 | - my-network 28 | nginx: 29 | container_name: my-nginx 30 | build: 31 | context: ./nginx 32 | ports: 33 | - '4000:80' 34 | networks: 35 | - my-network 36 | depends_on: 37 | - nodeserver 38 | - nodeserver2 39 | networks: 40 | my-network: 41 | external: true -------------------------------------------------------------------------------- /lessons/14-network/009-rest-nodejs/index.ts: -------------------------------------------------------------------------------- 1 | import WebSocket from "ws"; 2 | import faker from "faker"; 3 | import express from "express"; 4 | import * as path from "path"; 5 | import cors from "cors"; 6 | 7 | 8 | const app = express(); 9 | 10 | app.use(cors()); 11 | 12 | const emails = Array 13 | .from({length: 100}, (_, i) => 14 | faker.internet.email()); 15 | 16 | 17 | app.use(function (req, res, next) { 18 | res.set('Cache-control', 'public, max-age=360000'); 19 | next(); 20 | }) 21 | 22 | app.use(express.static(path.join(__dirname, "public"))) 23 | 24 | app.get("/emails", (_, res) => { 25 | res.json(emails); 26 | }) 27 | 28 | app.listen(process.env.PORT, () => { 29 | console.info("Server is run on ", process.env.PORT); 30 | }); 31 | 32 | 33 | -------------------------------------------------------------------------------- /lessons/14-network/009-rest-nodejs/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.16.0-alpine 2 | # COPY --from=build /usr/src/app/public /usr/share/nginx/html 3 | RUN rm /etc/nginx/conf.d/default.conf 4 | COPY nginx.conf /etc/nginx/conf.d 5 | EXPOSE 80 6 | EXPOSE 81 7 | EXPOSE 82 8 | CMD ["nginx", "-g", "daemon off;"] 9 | -------------------------------------------------------------------------------- /lessons/14-network/009-rest-nodejs/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name localhost; 4 | 5 | location /api1 { 6 | proxy_pass http://nodeserver:8081/; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lessons/14-network/009-rest-nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "006-websocket-server-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@types/cors": "^2.8.10", 14 | "@types/express": "^4.17.11", 15 | "@types/faker": "^5.5.5", 16 | "@types/ws": "^7.4.4", 17 | "cors": "^2.8.5", 18 | "express": "^4.17.1", 19 | "faker": "^5.5.3", 20 | "ts-node": "^9.1.1", 21 | "tsc": "^2.0.3", 22 | "typescript": "^4.2.4", 23 | "ws": "^7.4.5" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/14-network/009-rest-nodejs/public/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

      Error page

      7 | 8 | -------------------------------------------------------------------------------- /lessons/14-network/009-rest-nodejs/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

      Hello!

      8 | 9 | -------------------------------------------------------------------------------- /lessons/14-network/009-rest-nodejs/public/js/ramda.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/14-network/009-rest-nodejs/public/js/ramda.js -------------------------------------------------------------------------------- /lessons/14-network/009-rest-nodejs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "rootDir": "./", 6 | "outDir": "./build", 7 | "esModuleInterop": true, 8 | "strict": true 9 | } 10 | } -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | node_modules 4 | -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frp-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "rxjs": "^7.0.1" 7 | }, 8 | "devDependencies": { 9 | "@babel/core": "^7.14.2", 10 | "@babel/plugin-transform-runtime": "^7.14.2", 11 | "@babel/preset-typescript": "^7.13.0", 12 | "babel-loader": "^8.2.2", 13 | "css-loader": "^5.2.4", 14 | "html-webpack-plugin": "^5.3.1", 15 | "style-loader": "^2.0.0", 16 | "svg-inline-loader": "^0.8.2", 17 | "typescript": "^4.2.4", 18 | "webpack": "^5.37.0", 19 | "webpack-cli": "^4.7.0", 20 | "webpack-dev-server": "^4.0.0-beta.0" 21 | }, 22 | "scripts": { 23 | "start": "./node_modules/.bin/webpack serve --mode development", 24 | "build": "./node_modules/.bin/webpack --mode production" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/src/collision.ts: -------------------------------------------------------------------------------- 1 | import { bottom, left, reflect, right, top, Vector, x, y } from './vector' 2 | 3 | export const topCollide = (v: Vector) => y(v) <= 0 4 | export const bottomCollide = (v: Vector) => y(v) >= 800 - 100 5 | export const rightCollide = (v: Vector) => x(v) >= 1000 - 100 6 | export const leftCollide = (v: Vector) => x(v) < 0 7 | 8 | export const direction = (v: Vector, d: Vector): Vector => 9 | topCollide(v) 10 | ? reflect(d, top()) 11 | : bottomCollide(v) 12 | ? reflect(d, bottom()) 13 | : rightCollide(v) 14 | ? reflect(d, right()) 15 | : leftCollide(v) 16 | ? reflect(d, left()) 17 | : d 18 | 19 | export const collide = (player: Vector, ball: Vector) => 20 | y(ball) + 50 >= y(player) && 21 | y(ball) + 50 <= y(player) + 100 22 | -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/src/index.css: -------------------------------------------------------------------------------- 1 | .app { 2 | position: relative; 3 | background-color: green; 4 | width: 1000px; 5 | height: 800px; 6 | } 7 | 8 | .ball { 9 | position: absolute; 10 | width: 100px; 11 | height: 100px; 12 | border-radius: 50%; 13 | background-color: white; 14 | } 15 | 16 | .player { 17 | position: absolute; 18 | width: 20px; 19 | height: 100px; 20 | background-color: yellow; 21 | } 22 | 23 | .message { 24 | display: none; 25 | position: absolute; 26 | width: 100%; 27 | height: 100%; 28 | line-height: 800px; 29 | font-size: 100px; 30 | text-align: center; 31 | color: red; 32 | } 33 | 34 | .message.visible { 35 | display: block; 36 | } 37 | -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FRP Example 6 | 7 | 8 |
      9 |
      10 |
      11 |
      Goal!
      12 |
      13 | 14 | 15 | -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/src/index.ts: -------------------------------------------------------------------------------- 1 | import { interval, animationFrameScheduler, combineLatest, fromEvent, timer } from 'rxjs' 2 | import { filter, map, pluck, scan, startWith, switchMap, tap } from 'rxjs/operators' 3 | import { add, scalar, subtract, vector, top } from './vector' 4 | import { collide, direction, leftCollide } from './collision' 5 | import { hideMessage, render, showMessage } from './renderer' 6 | import './index.css' 7 | 8 | const initialVector = () => vector(0, 300) 9 | const initialDirection = () => vector(10, -10) 10 | const step = () => scalar(top(), 100) 11 | 12 | const player$ = fromEvent(document, 'keyup') 13 | .pipe( 14 | startWith({ code: '' }), 15 | pluck('code'), 16 | scan((acc, key) => 17 | key === 'ArrowUp' 18 | ? add(acc, step()) 19 | : key === 'ArrowDown' 20 | ? subtract(acc, step()) 21 | : acc, 22 | initialVector(), 23 | ), 24 | ) 25 | 26 | const ball$ = interval(0, animationFrameScheduler) 27 | .pipe( 28 | scan(([ballPosition, dir]) => [ 29 | add(ballPosition, direction(ballPosition, dir)), 30 | direction(ballPosition, dir), 31 | ], 32 | [initialVector(), initialDirection()], 33 | ), 34 | map(([ballPosition]) => ballPosition), 35 | ) 36 | 37 | const game$ = combineLatest([player$, ball$]) 38 | .pipe( 39 | tap(render), 40 | filter(([player, ball]) => leftCollide(ball) && !collide(player, ball)), 41 | tap(showMessage), 42 | switchMap(() => 43 | timer(1000).pipe(tap(hideMessage)), 44 | ), 45 | ) 46 | 47 | game$.subscribe() 48 | -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/src/renderer.ts: -------------------------------------------------------------------------------- 1 | import { Vector, x, y } from './vector' 2 | 3 | const ballDomNode = document.getElementById('ball')! 4 | const playerDomNode = document.getElementById('player')! 5 | const messageDomNode = document.getElementById('message')! 6 | 7 | export const render = ([player, ball]: [Vector, Vector]) => { 8 | ballDomNode.style.top = y(ball) + 'px' 9 | ballDomNode.style.left = x(ball) + 'px' 10 | 11 | playerDomNode.style.top = y(player) + 'px' 12 | playerDomNode.style.left = x(player) + 'px' 13 | } 14 | 15 | export const showMessage = () => messageDomNode.classList.add('visible') 16 | 17 | export const hideMessage = () => messageDomNode.classList.remove('visible') 18 | -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/src/vector.ts: -------------------------------------------------------------------------------- 1 | export type Vector = [number, number] 2 | 3 | export const vector = (x: number, y: number): Vector => [x, y] 4 | 5 | export const x = (v: Vector) => v[0] 6 | 7 | export const y = (v: Vector) => v[1] 8 | 9 | export const add = (a: Vector, b: Vector) => vector(x(a) + x(b), y(a) + y(b)) 10 | 11 | export const subtract = (a: Vector, b: Vector) => vector(x(a) - x(b), y(a) - y(b)) 12 | 13 | export const scalar = (v: Vector, c: number) => vector(x(v) * c, y(v) * c) 14 | 15 | export const dot = (a: Vector, b: Vector) => x(a) * x(b) + y(a) * y(b) 16 | 17 | export const reflect = (a: Vector, n: Vector) => subtract(a, scalar(n, 2 * dot(a, n))) 18 | 19 | export const top = () => vector(0, -1) 20 | 21 | export const bottom = () => vector(0, 1) 22 | 23 | export const left = () => vector(1, 0) 24 | 25 | export const right = () => vector(-1, 0) 26 | -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "allowSyntheticDefaultImports": true, 5 | "isolatedModules": true, 6 | "noImplicitAny": true, 7 | "noImplicitReturns": true, 8 | "noImplicitThis": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "noUnusedParameters": true, 11 | "noUnusedLocals": true, 12 | "strictNullChecks": true, 13 | "sourceMap": true, 14 | "experimentalDecorators": true, 15 | "downlevelIteration": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "declaration": true, 20 | "typeRoots": [ 21 | "node_modules/@types" 22 | ], 23 | "target": "ES2017", 24 | "lib": [ 25 | "es2019", 26 | "dom" 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lessons/15-functional-reactive-programming/football/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const HtmlWebpackPlugin = require('html-webpack-plugin') 3 | const path = require('path') 4 | 5 | module.exports = (env, argv) => { 6 | return { 7 | mode: argv.mode || 'none', 8 | entry: './src/index.ts', 9 | output: { 10 | path: path.resolve(__dirname, 'dist'), 11 | }, 12 | resolve: { 13 | extensions: ['.js', '.ts'], 14 | }, 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.ts$/, 19 | use: { 20 | loader: 'babel-loader', 21 | options: { 22 | presets: [ 23 | '@babel/preset-typescript', 24 | ], 25 | plugins: [ 26 | ['@babel/plugin-transform-runtime'], 27 | ], 28 | }, 29 | }, 30 | }, 31 | { 32 | test: /\.css$/i, 33 | use: ['style-loader', 'css-loader'], 34 | }, 35 | { 36 | test: /\.svg$/, 37 | loader: 'svg-inline-loader', 38 | }, 39 | ], 40 | }, 41 | plugins: [ 42 | new webpack.ProgressPlugin(), 43 | new HtmlWebpackPlugin({ 44 | template: './src/index.html', 45 | }), 46 | ], 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lessons/17-node/presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/17-node/presentation.pdf -------------------------------------------------------------------------------- /lessons/18-webgl/texture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/18-webgl/texture.jpg -------------------------------------------------------------------------------- /lessons/20-mobx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lesson-mobx-1", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "@types/jest": "^26.0.15", 10 | "@types/node": "^12.0.0", 11 | "@types/react": "^17.0.0", 12 | "@types/react-dom": "^17.0.0", 13 | "classnames": "^2.3.1", 14 | "mobx": "^6.3.2", 15 | "mobx-react-lite": "^3.2.0", 16 | "react": "^17.0.2", 17 | "react-dom": "^17.0.2", 18 | "react-scripts": "4.0.3", 19 | "typescript": "^4.1.2", 20 | "web-vitals": "^1.0.1" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lessons/20-mobx/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/20-mobx/public/favicon.ico -------------------------------------------------------------------------------- /lessons/20-mobx/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
      32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /lessons/20-mobx/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/20-mobx/public/logo192.png -------------------------------------------------------------------------------- /lessons/20-mobx/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/lessons/20-mobx/public/logo512.png -------------------------------------------------------------------------------- /lessons/20-mobx/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /lessons/20-mobx/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /lessons/20-mobx/src/02-MobxSimpleStructures.tsx: -------------------------------------------------------------------------------- 1 | import { autorun, observable, ObservableMap, toJS } from "mobx" 2 | 3 | export const App = () => { 4 | const map = new ObservableMap() 5 | const array = observable([] as string[]) 6 | const object = observable({} as Record) 7 | 8 | // console.log(array) 9 | 10 | const disposer = autorun(() => { 11 | // console.log("[autorun map]", toJS(map)) 12 | // console.log("[autorun arr]", toJS(array)) 13 | console.log("[autorun obj]", toJS(object)) 14 | console.log("^^^^^^^^^^^^") 15 | }) 16 | 17 | console.log("------------") 18 | 19 | map.set("a", 123) 20 | 21 | array.push("value") 22 | 23 | object["123"] = "asd" 24 | object.asd = "123" 25 | 26 | console.log(toJS(object)) 27 | 28 | 29 | disposer() 30 | 31 | return

      Empty app

      32 | } 33 | -------------------------------------------------------------------------------- /lessons/20-mobx/src/03-MobxClass.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { action, makeAutoObservable } from "mobx" 3 | import { observer } from "mobx-react-lite" 4 | 5 | class Timer { 6 | secondsPassed = 0 7 | private intervalId: number | undefined = undefined 8 | 9 | constructor() { 10 | makeAutoObservable(this, { 11 | startTimer: action.bound, 12 | endTimer: action.bound, 13 | }) 14 | } 15 | 16 | get isTimerStarted() { 17 | return this.intervalId !== undefined 18 | } 19 | 20 | private increaseTimer() { 21 | this.secondsPassed += 1 22 | } 23 | 24 | startTimer() { 25 | this.intervalId = window.setInterval(() => { 26 | this.increaseTimer() 27 | }, 1000) 28 | } 29 | 30 | endTimer() { 31 | clearInterval(this.intervalId) 32 | this.intervalId = undefined 33 | } 34 | } 35 | 36 | 37 | const TimerComponent: React.FC<{ timer: Timer }> = observer(({ timer }) => { 38 | return ( 39 | <> 40 | Seconds passed: {timer.secondsPassed} 41 |
      42 |
      43 | 46 | 49 | 50 | ) 51 | }) 52 | 53 | export const App = () => { 54 | const myTimer = new Timer() 55 | return 56 | } 57 | -------------------------------------------------------------------------------- /lessons/20-mobx/src/04-Reactions.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | makeObservable, 3 | observable, 4 | computed, 5 | autorun, 6 | when, 7 | reaction, 8 | } from "mobx" 9 | 10 | class OrderLine { 11 | price = 0 12 | amount = 1 13 | 14 | constructor(price: number) { 15 | makeObservable(this, { 16 | price: observable, 17 | amount: observable, 18 | total: computed, 19 | }) 20 | this.price = price 21 | } 22 | 23 | get total() { 24 | console.log("[total]Computing...") 25 | return this.price * this.amount 26 | } 27 | } 28 | 29 | const order = new OrderLine(0) 30 | 31 | // autorun(() => { 32 | // console.log("[Autorun] Total: " + order.total) 33 | // }) 34 | 35 | // reaction( 36 | // () => order.amount, 37 | // (amount, prevAmount) => { 38 | // console.log("reaction", amount, prevAmount) 39 | // }, 40 | // ) 41 | 42 | when( 43 | () => order.amount > 1, 44 | () => { 45 | console.log(order.amount) 46 | }, 47 | ) 48 | 49 | 50 | order.amount = 5 51 | order.amount = 4 52 | order.price = 2 53 | 54 | // order.price = 3 55 | 56 | console.log("--------") 57 | 58 | export const App = () =>

      Empty app

      59 | -------------------------------------------------------------------------------- /lessons/20-mobx/src/05-Mobx-React-Lite.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { action, makeAutoObservable } from "mobx" 3 | import { observer } from "mobx-react-lite" 4 | import classnames from "classnames" 5 | 6 | // Stores 7 | const emojis = ["🙂", "😵‍💫", "🤔", "🥳", "🤩", "😤", "👨‍💻", "🧦", "🍚"] 8 | 9 | export class Emoji { 10 | id = Math.random() 11 | icon = "" 12 | rotated = false 13 | 14 | constructor(title: string) { 15 | makeAutoObservable(this, { rotate: action.bound }) 16 | this.icon = title 17 | } 18 | 19 | rotate() { 20 | this.rotated = !this.rotated 21 | } 22 | } 23 | 24 | export class RandomEmojiList { 25 | emojies: Emoji[] = [] 26 | constructor() { 27 | makeAutoObservable(this, { addRandomEmoji: action.bound }) 28 | } 29 | addRandomEmoji() { 30 | const emojiIcon = emojis[Math.floor(Math.random() * emojis.length)] 31 | const emoji = new Emoji(emojiIcon) 32 | this.emojies.push(emoji) 33 | } 34 | } 35 | // Clay 36 | const StoreContext = React.createContext(undefined!) 37 | const StoreProvider: React.FC<{ store: RandomEmojiList }> = ({ 38 | children, 39 | store, 40 | }) => {children} 41 | 42 | const useStore = () => { 43 | const store = React.useContext(StoreContext) 44 | return store 45 | } 46 | 47 | // Component 48 | const EmojiListComponent = observer(() => { 49 | const EmojiList = useStore() 50 | return ( 51 |
      52 |

      Emoji list

      53 | 54 | 55 | 56 |
        57 | {EmojiList.emojies.map((emoji) => ( 58 | 59 | ))} 60 |
      61 |
      62 | ) 63 | }) 64 | 65 | const EmojiElement: React.FC<{ emoji: Emoji }> = observer(({ emoji }) => ( 66 |
    • 67 |
      68 | {emoji.icon} 69 |
      70 |
    • 71 | )) 72 | 73 | // Bound 74 | const store = new RandomEmojiList() 75 | export const App = () => ( 76 | 77 | 78 | 79 | ) 80 | -------------------------------------------------------------------------------- /lessons/20-mobx/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 | padding: 40px; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | 16 | .emoji { 17 | display: inline-block; 18 | user-select: none; 19 | font-size: 25px; 20 | } 21 | .emoji.rotated { 22 | transform: rotate(180deg); 23 | } 24 | -------------------------------------------------------------------------------- /lessons/20-mobx/src/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from "react-dom" 2 | import "./index.css" 3 | // import { App } from "./01-BigApp" 4 | // import { App } from "./02-MobxSimpleStructures" 5 | // import { App } from "./03-MobxClass" 6 | // import { App } from "./04-Reactions" 7 | import { App } from "./05-Mobx-React-Lite" 8 | 9 | ReactDOM.render(, document.getElementById("root")) 10 | -------------------------------------------------------------------------------- /lessons/20-mobx/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /lessons/20-mobx/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 | "useDefineForClassFields": true, 17 | "module": "esnext", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "experimentalDecorators": true, 22 | "noEmit": true, 23 | "jsx": "react-jsx" 24 | }, 25 | "include": [ 26 | "src" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Nuts and bolts of BabylonJS 3 | 4 | ## Project files for TS Bootcamp / BabylonJS Day 1 5 | 6 | ### How to install 7 | 8 | Run this command in root directory 9 | 10 | ```sh 11 | yarn install 12 | ``` 13 | 14 | ### How to launch excersises 15 | These are start files, which need to complete. To change task, just comment current one and comment out desired one in `./src/excercises/index.ts` file 16 | 17 | Run this command in root directory 18 | 19 | ```sh 20 | yarn start 21 | ``` -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/bin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 22 | Nuts and bolts of BabylonJS 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuts-and-bolts-of-babylonjs", 3 | "version": "1.0.0", 4 | "description": "Source files for Nuts and bolts of BabylonJS", 5 | "author": "Uldis Baurovskis , Armands Baurovskis ", 6 | "license": "MIT", 7 | "private": true, 8 | "scripts": { 9 | "start": "webpack serve --entry=./src/exercises/index.ts" 10 | }, 11 | "devDependencies": { 12 | "html-webpack-plugin": "^5.5.0", 13 | "ts-loader": "^9.2.6", 14 | "typescript": "^4.5.2", 15 | "webpack": "^5.65.0", 16 | "webpack-cli": "^4.9.1", 17 | "webpack-dev-server": "^4.6.0", 18 | "webpack-glsl-minify": "^1.4.2", 19 | "file-loader": "^6.2.0" 20 | }, 21 | "dependencies": { 22 | "@babylonjs/core": "5.0.0-alpha.64", 23 | "@babylonjs/inspector": "5.0.0-alpha.64", 24 | "@babylonjs/materials": "5.0.0-alpha.64" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/common/ExerciseBase.ts: -------------------------------------------------------------------------------- 1 | import { HemisphericLight, ArcRotateCamera, Engine, Scene, Vector3 } from "@babylonjs/core"; 2 | import { Exercise } from "./types"; 3 | 4 | export abstract class ExerciseBase implements Exercise { 5 | protected readonly engine: Engine; 6 | protected readonly canvas: HTMLCanvasElement; 7 | protected readonly scene: Scene; 8 | 9 | public constructor() { 10 | this.canvas = this.createCanvas(); 11 | this.engine = this.createEngine(this.canvas); 12 | this.scene = this.createScene(this.engine); 13 | this.createCamera(this.scene); 14 | this.createLight(this.scene); 15 | this.addContent(); 16 | window.addEventListener("resize", this.onResize); 17 | this.engine.runRenderLoop(this.onRender); 18 | } 19 | public start(): void { 20 | this.onResize(); 21 | } 22 | protected createCanvas(): HTMLCanvasElement { 23 | const canvas = document.createElement("canvas"); 24 | document.body.appendChild(canvas); 25 | return canvas; 26 | } 27 | protected createEngine(canvas: HTMLCanvasElement): Engine { 28 | return new Engine(canvas, true, {}, true); 29 | } 30 | protected createScene(engine: Engine): Scene { 31 | return new Scene(engine, {}); 32 | } 33 | protected createCamera(scene: Scene): void { 34 | const camera = new ArcRotateCamera("camera", -Math.PI * 0.5, Math.PI * 0.25, 12, Vector3.Zero(), scene); 35 | camera.attachControl(); 36 | } 37 | protected createLight(scene: Scene) { 38 | const lights = new HemisphericLight("light", new Vector3(0, 1, 0), scene); 39 | } 40 | protected abstract addContent(): void; 41 | private onRender = () => { 42 | this.scene.render(); 43 | } 44 | private onResize = () => { 45 | this.engine.resize(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/common/types.d.ts: -------------------------------------------------------------------------------- 1 | export interface Exercise { 2 | start(): void; 3 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/exercises/exercise01/exercise01.ts: -------------------------------------------------------------------------------- 1 | import { ArcRotateCamera, Engine, HemisphericLight, Scene, Vector3 } from "@babylonjs/core"; 2 | import { Exercise } from "../../common/types"; 3 | 4 | export class Exercise01 implements Exercise { 5 | public start(): void { 6 | const canvas = document.createElement("canvas"); 7 | document.body.appendChild(canvas); 8 | const engine = new Engine(canvas, true, {}, true); 9 | const scene = new Scene(engine, {}); 10 | const light = new HemisphericLight("light", new Vector3(0, 1, 0), scene); 11 | const camera = new ArcRotateCamera("camera", -Math.PI * 0.5, Math.PI * 0.25, 12, Vector3.Zero(), scene); 12 | engine.runRenderLoop(() => { 13 | scene.render(); 14 | }); 15 | const onResize = () => { 16 | engine.resize(); 17 | }; 18 | window.addEventListener("resize", onResize); 19 | onResize(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/exercises/exercise02/exercise02.ts: -------------------------------------------------------------------------------- 1 | import { MeshBuilder } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | export class Exercise02 extends ExerciseBase { 5 | protected addContent() { 6 | const sphere = MeshBuilder.CreateSphere("sphere", { diameter: 1 }, this.scene); 7 | const box = MeshBuilder.CreateBox("box", { size: 1 }, this.scene); 8 | const cylinder = MeshBuilder.CreateCylinder("cylinder", { height: 1, diameter: 1 }, this.scene); 9 | box.position.x += 2; 10 | 11 | cylinder.position.x -= 2; 12 | cylinder.scaling.setAll(2); 13 | } 14 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/exercises/exercise03/exercise03.ts: -------------------------------------------------------------------------------- 1 | import { Color3, MeshBuilder, StandardMaterial } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | export class Exercise03 extends ExerciseBase { 5 | protected addContent() { 6 | const sphere = MeshBuilder.CreateSphere("sphere", { diameter: 1 }, this.scene); 7 | const material = new StandardMaterial("material", this.scene); 8 | material.diffuseColor = new Color3(1, 0, 1); 9 | sphere.material = material; 10 | } 11 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/exercises/exercise04/exercise04.ts: -------------------------------------------------------------------------------- 1 | import { MeshBuilder, StandardMaterial } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | export class Exercise04 extends ExerciseBase { 5 | protected addContent() { 6 | const sphere = MeshBuilder.CreateSphere("sphere", { diameter: 1 }, this.scene); 7 | const box = MeshBuilder.CreateBox("box", { size: 1 }, this.scene); 8 | const cylinder = MeshBuilder.CreateCylinder("cylinder", { height: 1, diameter: 1 }, this.scene); 9 | 10 | box.position.x += 2; 11 | cylinder.position.x -= 2; 12 | 13 | const material = new StandardMaterial("material", this.scene); 14 | material.wireframe = true; 15 | sphere.material = material; 16 | box.material = material; 17 | cylinder.material = material; 18 | } 19 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/exercises/exercise05/exercise05.ts: -------------------------------------------------------------------------------- 1 | import { Color3, MeshBuilder, StandardMaterial, Texture } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | /** 5 | * url: https://www.babylonjs-playground.com/textures/bloc.jpg 6 | */ 7 | export class Exercise05 extends ExerciseBase { 8 | protected addContent() { 9 | const mesh = MeshBuilder.CreateBox("mesh", { size: 2 }, this.scene); 10 | const material = new StandardMaterial("material", this.scene); 11 | mesh.material = material; 12 | 13 | material.diffuseTexture = new Texture("https://www.babylonjs-playground.com/textures/bloc.jpg", this.scene); 14 | } 15 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/exercises/exercise06/exercise06.ts: -------------------------------------------------------------------------------- 1 | import { Color3, MeshBuilder, StandardMaterial, Texture } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | /** 5 | * Diffuse texture: https://www.babylonjs-playground.com/textures/bloc.jpg 6 | * Emissive texture: https://www.babylonjs-playground.com/textures/bloc.jpg 7 | * Specular texture: https://www.babylonjs-playground.com/textures/reflectivity.png 8 | * Opacity texture: https://www.babylonjs-playground.com/textures/palm.png 9 | */ 10 | export class Exercise06 extends ExerciseBase { 11 | protected addContent() { 12 | const mesh = MeshBuilder.CreateBox("mesh", { size: 2 }, this.scene); 13 | const material = new StandardMaterial("material", this.scene); 14 | mesh.material = material; 15 | 16 | material.diffuseColor = new Color3(1, 0, 0); 17 | // color texture 18 | material.diffuseTexture = new Texture("https://www.babylonjs-playground.com/textures/bloc.jpg", this.scene); 19 | 20 | material.emissiveColor = new Color3(1, 0, 0); 21 | // light texture 22 | material.emissiveTexture = new Texture("https://www.babylonjs-playground.com/textures/bloc.jpg", this.scene); 23 | 24 | material.specularColor = new Color3(1, 1, 1); 25 | material.specularPower = 5; 26 | 27 | // reflectivity texture 28 | material.specularTexture = new Texture("https://www.babylonjs-playground.com/textures/reflectivity.png", this.scene); 29 | 30 | // mask 31 | material.opacityTexture = new Texture("https://www.babylonjs-playground.com/textures/palm.png", this.scene); 32 | 33 | material.alpha = 0.5; 34 | } 35 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/exercises/exercise09/exercise09.ts: -------------------------------------------------------------------------------- 1 | import { MeshBuilder, Scene, FreeCamera, Vector3, ArcRotateCamera } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | /** 5 | * https://doc.babylonjs.com/divingDeeper/cameras/camera_introduction 6 | */ 7 | export class Exercise09 extends ExerciseBase { 8 | protected addContent() { 9 | 10 | const size = 2; 11 | const ground = MeshBuilder.CreateGround("ground", { width: size * 3, height: size * 3 }, this.scene); 12 | const mesh = MeshBuilder.CreateBox("mesh", { size: size }, this.scene); 13 | mesh.position.y += size * 0.5; 14 | 15 | const freeCamera = new FreeCamera("freeCamera", new Vector3(0, 5, -15), this.scene); 16 | freeCamera.setTarget(new Vector3(0, 0, 0)); 17 | freeCamera.attachControl(); 18 | 19 | const arcRotateCamera = new ArcRotateCamera("arcCamera", -Math.PI * 0.5, Math.PI * 0.25, 12, Vector3.Zero(), this.scene); 20 | arcRotateCamera.attachControl(); 21 | 22 | this.scene.switchActiveCamera(arcRotateCamera); 23 | } 24 | 25 | protected createCamera(scene: Scene): void { 26 | // removed for exercise 27 | } 28 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/exercises/exercise10/exercise10.ts: -------------------------------------------------------------------------------- 1 | import "@babylonjs/loaders/glTF"; 2 | import { AssetsManager } from "@babylonjs/core"; 3 | import { ExerciseBase } from "../../common/ExerciseBase"; 4 | 5 | /** 6 | * BoomBox https://www.babylonjs-playground.com/scenes/BoomBox.glb 7 | */ 8 | export class Exercise10 extends ExerciseBase { 9 | protected addContent() { 10 | const assetManager = new AssetsManager(this.scene); 11 | assetManager.useDefaultLoadingScreen = false; 12 | assetManager.addMeshTask( 13 | "load-boom-box", 14 | "", 15 | "https://www.babylonjs-playground.com/scenes/BoomBox.glb", 16 | "" 17 | ); 18 | assetManager 19 | .loadAsync() 20 | .then(() => { 21 | // Too small to see 22 | this.scene.getMeshByName("BoomBox").scaling.set(100, 100, 100); 23 | }) 24 | .catch((e) => { 25 | console.log("error", e); 26 | // error on loading 27 | }); 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/src/exercises/index.ts: -------------------------------------------------------------------------------- 1 | import { Exercise01 } from "./exercise01/exercise01"; 2 | import { Exercise02 } from "./exercise02/exercise02"; 3 | import { Exercise03 } from "./exercise03/exercise03"; 4 | import { Exercise04 } from "./exercise04/exercise04"; 5 | import { Exercise05 } from "./exercise05/exercise05"; 6 | import { Exercise06 } from "./exercise06/exercise06"; 7 | import { Exercise07 } from "./exercise07/exercise07"; 8 | import { Exercise08 } from "./exercise08/exercise08"; 9 | import { Exercise09 } from "./exercise09/exercise09"; 10 | import { Exercise10 } from "./exercise10/exercise10"; 11 | 12 | new Exercise01().start(); 13 | // new Exercise02().start(); 14 | // new Exercise03().start(); 15 | // new Exercise04().start(); 16 | // new Exercise05().start(); 17 | // new Exercise06().start(); 18 | // new Exercise07().start(); 19 | // new Exercise08().start(); 20 | // new Exercise09().start(); 21 | // new Exercise10().start(); -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", 4 | "outDir": "./bin/", 5 | "moduleResolution": "node", 6 | "noImplicitAny": false, 7 | "module": "es6", 8 | "target": "es6", 9 | "jsx": "react", 10 | "allowJs": true, 11 | "typeRoots": ["./types", "node_modules/@types"], 12 | "resolveJsonModule": true, 13 | "allowSyntheticDefaultImports": true 14 | }, 15 | "include": [ 16 | "src/**/*.ts", 17 | "src/**/*.d.ts" 18 | ] 19 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/types/FileTypes.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.png" { 2 | var t: string; 3 | export default t; 4 | } 5 | 6 | declare module "*.jpg" { 7 | var t: string; 8 | export default t; 9 | } 10 | 11 | declare module "*.glsl" { 12 | var t: "String(GLSLScript)"; 13 | export default t; 14 | } 15 | 16 | declare module "*.babylon" { 17 | const url: "String(Babylon)"; 18 | export default url; 19 | } 20 | 21 | declare module "*.json" { 22 | var t: any; 23 | export default t; 24 | } 25 | 26 | declare module "*.env" { 27 | var t: "String(BabylonEnv)"; 28 | export default t; 29 | } 30 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-final/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: './src/index.ts', 6 | mode: 'development', 7 | devtool: 'source-map', 8 | output: { 9 | filename: '[name].main.js', 10 | path: path.resolve(__dirname, 'bin'), 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.tsx?$/, 16 | use: 'ts-loader', 17 | exclude: /node_modules/, 18 | }, 19 | { 20 | test: /\.glsl$/, 21 | use: { 22 | loader: "webpack-glsl-minify", 23 | options: { 24 | output: "source", 25 | esModule: true, 26 | preserveAll: true, 27 | }, 28 | }, 29 | exclude: /node_modules/, 30 | }, 31 | { 32 | test: /\.(png|jpe?g|gif)$/i, 33 | use: [ 34 | { 35 | loader: 'file-loader', 36 | }, 37 | ], 38 | }, 39 | ] 40 | }, 41 | resolve: { 42 | extensions: [ '.tsx', '.ts', '.js' ], 43 | }, 44 | plugins: [ 45 | new HtmlWebpackPlugin({ 46 | template: './bin/index.html', 47 | }), 48 | ], 49 | devServer: { 50 | compress: true, 51 | port: 4444, 52 | client: { 53 | overlay: true, 54 | progress: true, 55 | }, 56 | hot: true, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Nuts and bolts of BabylonJS 3 | 4 | ## Project files for TS Bootcamp / BabylonJS Day 1 5 | 6 | ### How to install 7 | 8 | Run this command in root directory 9 | 10 | ```sh 11 | yarn install 12 | ``` 13 | 14 | ### How to launch excersises 15 | These are start files, which need to complete. To change task, just comment current one and comment out desired one in `./src/excercises/index.ts` file 16 | 17 | Run this command in root directory 18 | 19 | ```sh 20 | yarn start 21 | ``` -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/bin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 22 | Nuts and bolts of BabylonJS 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuts-and-bolts-of-babylonjs", 3 | "version": "1.0.0", 4 | "description": "Source files for Nuts and bolts of BabylonJS", 5 | "author": "Uldis Baurovskis , Armands Baurovskis ", 6 | "license": "MIT", 7 | "private": true, 8 | "scripts": { 9 | "start": "webpack serve --entry=./src/exercises/index.ts" 10 | }, 11 | "devDependencies": { 12 | "html-webpack-plugin": "^5.5.0", 13 | "ts-loader": "^9.2.6", 14 | "typescript": "^4.5.2", 15 | "webpack": "^5.65.0", 16 | "webpack-cli": "^4.9.1", 17 | "webpack-dev-server": "^4.6.0", 18 | "webpack-glsl-minify": "^1.4.2", 19 | "file-loader": "^6.2.0" 20 | }, 21 | "dependencies": { 22 | "@babylonjs/core": "5.0.0-alpha.64", 23 | "@babylonjs/inspector": "5.0.0-alpha.64", 24 | "@babylonjs/materials": "5.0.0-alpha.64" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/common/ExerciseBase.ts: -------------------------------------------------------------------------------- 1 | import { HemisphericLight, ArcRotateCamera, Engine, Scene, Vector3 } from "@babylonjs/core"; 2 | import { Exercise } from "./types"; 3 | 4 | export abstract class ExerciseBase implements Exercise { 5 | protected readonly engine: Engine; 6 | protected readonly canvas: HTMLCanvasElement; 7 | protected readonly scene: Scene; 8 | 9 | public constructor() { 10 | this.canvas = this.createCanvas(); 11 | this.engine = this.createEngine(this.canvas); 12 | this.scene = this.createScene(this.engine); 13 | this.createCamera(this.scene); 14 | this.createLight(this.scene); 15 | this.addContent(); 16 | window.addEventListener("resize", this.onResize); 17 | this.engine.runRenderLoop(this.onRender); 18 | } 19 | public start(): void { 20 | this.onResize(); 21 | } 22 | protected createCanvas(): HTMLCanvasElement { 23 | const canvas = document.createElement("canvas"); 24 | document.body.appendChild(canvas); 25 | return canvas; 26 | } 27 | protected createEngine(canvas: HTMLCanvasElement): Engine { 28 | return new Engine(canvas, true, {}, true); 29 | } 30 | protected createScene(engine: Engine): Scene { 31 | return new Scene(engine, {}); 32 | } 33 | protected createCamera(scene: Scene): void { 34 | const camera = new ArcRotateCamera("camera", -Math.PI * 0.5, Math.PI * 0.25, 12, Vector3.Zero(), scene); 35 | camera.attachControl(); 36 | } 37 | protected createLight(scene: Scene) { 38 | const lights = new HemisphericLight("light", new Vector3(0, 1, 0), scene); 39 | } 40 | protected abstract addContent(): void; 41 | private onRender = () => { 42 | this.scene.render(); 43 | } 44 | private onResize = () => { 45 | this.engine.resize(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/common/types.d.ts: -------------------------------------------------------------------------------- 1 | export interface Exercise { 2 | start(): void; 3 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/exercise01/exercise01.ts: -------------------------------------------------------------------------------- 1 | import { ArcRotateCamera, Engine, HemisphericLight, Scene, Vector3, Color4 } from "@babylonjs/core"; 2 | import { Exercise } from "../../common/types"; 3 | 4 | export class Exercise01 implements Exercise { 5 | public start(): void { 6 | // TODO: implmenet logic here 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/exercise02/exercise02.ts: -------------------------------------------------------------------------------- 1 | import { ExerciseBase } from "../../common/ExerciseBase"; 2 | 3 | export class Exercise02 extends ExerciseBase { 4 | protected addContent() { 5 | // TODO: implmenet logic here 6 | } 7 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/exercise03/exercise03.ts: -------------------------------------------------------------------------------- 1 | import { MeshBuilder } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | export class Exercise03 extends ExerciseBase { 5 | protected addContent() { 6 | const sphere = MeshBuilder.CreateSphere("sphere", { diameter: 1 }, this.scene); 7 | // TODO: implmenet logic here 8 | } 9 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/exercise04/exercise04.ts: -------------------------------------------------------------------------------- 1 | import { MeshBuilder, StandardMaterial } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | export class Exercise04 extends ExerciseBase { 5 | protected addContent() { 6 | const sphere = MeshBuilder.CreateSphere("sphere", { diameter: 1 }, this.scene); 7 | const box = MeshBuilder.CreateBox("box", { size: 1 }, this.scene); 8 | const cylinder = MeshBuilder.CreateCylinder("cylinder", { height: 1, diameter: 1 }, this.scene); 9 | 10 | box.position.x += 2; 11 | cylinder.position.x -= 2; 12 | 13 | const material = new StandardMaterial("material", this.scene); 14 | // TODO: implmenet logic here 15 | } 16 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/exercise05/exercise05.ts: -------------------------------------------------------------------------------- 1 | import { Color3, MeshBuilder, StandardMaterial, Texture } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | /** 5 | * url: https://www.babylonjs-playground.com/textures/bloc.jpg 6 | */ 7 | export class Exercise05 extends ExerciseBase { 8 | protected addContent() { 9 | const mesh = MeshBuilder.CreateBox("mesh", { size: 2 }, this.scene); 10 | const material = new StandardMaterial("material", this.scene); 11 | mesh.material = material; 12 | // TODO: implmenet logic here 13 | } 14 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/exercise06/exercise06.ts: -------------------------------------------------------------------------------- 1 | import { Color3, MeshBuilder, StandardMaterial, Texture } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | /** 5 | * Diffuse texture: https://www.babylonjs-playground.com/textures/bloc.jpg 6 | * Emissive texture: https://www.babylonjs-playground.com/textures/bloc.jpg 7 | * Specular texture: https://www.babylonjs-playground.com/textures/reflectivity.png 8 | * Opacity texture: https://www.babylonjs-playground.com/textures/palm.png 9 | */ 10 | export class Exercise06 extends ExerciseBase { 11 | protected addContent() { 12 | const mesh = MeshBuilder.CreateBox("mesh", { size: 2 }, this.scene); 13 | const material = new StandardMaterial("material", this.scene); 14 | mesh.material = material; 15 | // TODO: implmenet logic here 16 | } 17 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/exercise07/exercise07.ts: -------------------------------------------------------------------------------- 1 | import { Color3, MeshBuilder, StandardMaterial, Texture, Animation, Vector3 } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | export class Exercise07 extends ExerciseBase { 5 | protected addContent() { 6 | const mesh = MeshBuilder.CreateBox("mesh1", { size: 2 }, this.scene); 7 | mesh.material = new StandardMaterial("material", this.scene); 8 | mesh.material.animations = []; 9 | // TODO: implmenet logic here 10 | } 11 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/exercise08/exercise08.ts: -------------------------------------------------------------------------------- 1 | import { MeshBuilder, Scene } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | /** 5 | * https://doc.babylonjs.com/divingDeeper/lights/lights_introduction 6 | */ 7 | export class Exercise08 extends ExerciseBase { 8 | protected addContent() { 9 | 10 | const size = 2; 11 | 12 | const ground = MeshBuilder.CreateGround("ground", { width: size * 3, height: size * 3 }, this.scene); 13 | const meshLeft = MeshBuilder.CreateBox("meshLeft", { size: size }, this.scene); 14 | const meshRight = MeshBuilder.CreateBox("meshRight", { size: size }, this.scene); 15 | const meshTop = MeshBuilder.CreateBox("meshTop", { size: size }, this.scene); 16 | const meshBottom = MeshBuilder.CreateBox("meshBottom", { size: size }, this.scene); 17 | 18 | meshLeft.position.x = -size; 19 | meshRight.position.x = size; 20 | meshTop.position.z = -size; 21 | meshBottom.position.z = size; 22 | 23 | meshLeft.position.y = size * 0.5; 24 | meshRight.position.y = size * 0.5; 25 | meshTop.position.y = size * 0.5; 26 | meshBottom.position.y = size * 0.5; 27 | 28 | // TODO: implmenet logic here 29 | } 30 | 31 | protected createLight(scene: Scene): void { 32 | // leave blank for exercise to test lights 33 | } 34 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/exercise09/exercise09.ts: -------------------------------------------------------------------------------- 1 | import { MeshBuilder, Scene, FreeCamera, Vector3, ArcRotateCamera } from "@babylonjs/core"; 2 | import { ExerciseBase } from "../../common/ExerciseBase"; 3 | 4 | /** 5 | * https://doc.babylonjs.com/divingDeeper/cameras/camera_introduction 6 | */ 7 | export class Exercise09 extends ExerciseBase { 8 | protected addContent() { 9 | const size = 2; 10 | const ground = MeshBuilder.CreateGround("ground", { width: size * 3, height: size * 3 }, this.scene); 11 | const mesh = MeshBuilder.CreateBox("mesh", { size: size }, this.scene); 12 | mesh.position.y += size * 0.5; 13 | // TODO: implmenet logic here 14 | } 15 | 16 | protected createCamera(scene: Scene): void { 17 | // removed for exercise 18 | } 19 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/exercise10/exercise10.ts: -------------------------------------------------------------------------------- 1 | import "@babylonjs/loaders/glTF"; 2 | import { AssetsManager } from "@babylonjs/core"; 3 | import { ExerciseBase } from "../../common/ExerciseBase"; 4 | 5 | /** 6 | * BoomBox https://www.babylonjs-playground.com/scenes/BoomBox.glb 7 | */ 8 | export class Exercise10 extends ExerciseBase { 9 | protected addContent() { 10 | // TODO: implmenet logic here 11 | } 12 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/src/exercises/index.ts: -------------------------------------------------------------------------------- 1 | import { Exercise01 } from "./exercise01/exercise01"; 2 | import { Exercise02 } from "./exercise02/exercise02"; 3 | import { Exercise03 } from "./exercise03/exercise03"; 4 | import { Exercise04 } from "./exercise04/exercise04"; 5 | import { Exercise05 } from "./exercise05/exercise05"; 6 | import { Exercise07 } from "./exercise07/exercise07"; 7 | import { Exercise08 } from "./exercise08/exercise08"; 8 | import { Exercise09 } from "./exercise09/exercise09"; 9 | import { Exercise10 } from "./exercise10/exercise10"; 10 | 11 | new Exercise01().start(); 12 | // new Exercise02().start(); 13 | // new Exercise03().start(); 14 | // new Exercise04().start(); 15 | // new Exercise05().start(); 16 | // new Exercise06().start(); 17 | // new Exercise07().start(); 18 | // new Exercise08().start(); 19 | // new Exercise09().start(); 20 | // new Exercise10().start(); -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", 4 | "outDir": "./bin/", 5 | "moduleResolution": "node", 6 | "noImplicitAny": false, 7 | "module": "es6", 8 | "target": "es6", 9 | "jsx": "react", 10 | "allowJs": true, 11 | "typeRoots": ["./types", "node_modules/@types"], 12 | "resolveJsonModule": true, 13 | "allowSyntheticDefaultImports": true 14 | }, 15 | "include": [ 16 | "src/**/*.ts", 17 | "src/**/*.d.ts" 18 | ] 19 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/types/FileTypes.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.png" { 2 | var t: string; 3 | export default t; 4 | } 5 | 6 | declare module "*.jpg" { 7 | var t: string; 8 | export default t; 9 | } 10 | 11 | declare module "*.glsl" { 12 | var t: "String(GLSLScript)"; 13 | export default t; 14 | } 15 | 16 | declare module "*.babylon" { 17 | const url: "String(Babylon)"; 18 | export default url; 19 | } 20 | 21 | declare module "*.json" { 22 | var t: any; 23 | export default t; 24 | } 25 | 26 | declare module "*.env" { 27 | var t: "String(BabylonEnv)"; 28 | export default t; 29 | } 30 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/introduction-start/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: './src/index.ts', 6 | mode: 'development', 7 | devtool: 'source-map', 8 | output: { 9 | filename: '[name].main.js', 10 | path: path.resolve(__dirname, 'bin'), 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.tsx?$/, 16 | use: 'ts-loader', 17 | exclude: /node_modules/, 18 | }, 19 | { 20 | test: /\.glsl$/, 21 | use: { 22 | loader: "webpack-glsl-minify", 23 | options: { 24 | output: "source", 25 | esModule: true, 26 | preserveAll: true, 27 | }, 28 | }, 29 | exclude: /node_modules/, 30 | }, 31 | { 32 | test: /\.(png|jpe?g|gif)$/i, 33 | use: [ 34 | { 35 | loader: 'file-loader', 36 | }, 37 | ], 38 | }, 39 | ] 40 | }, 41 | resolve: { 42 | extensions: [ '.tsx', '.ts', '.js' ], 43 | }, 44 | plugins: [ 45 | new HtmlWebpackPlugin({ 46 | template: './bin/index.html', 47 | }), 48 | ], 49 | devServer: { 50 | compress: true, 51 | port: 4444, 52 | client: { 53 | overlay: true, 54 | progress: true, 55 | }, 56 | hot: true, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Rat race 3 | 4 | ## Project files for TS Bootcamp / BabylonJS Day 2 5 | 6 | ### How to install 7 | 8 | Run this command in root directory 9 | 10 | ```sh 11 | yarn install 12 | ``` 13 | 14 | ### How to launch the game 15 | Run this command in root directory 16 | 17 | ```sh 18 | yarn start 19 | ``` 20 | 21 | ### If having issues with import in VS Code id 22 | 23 | In Visual Studio Code, menu File → Preferences → Settings → User Settings 24 | Type `importModuleSpecifier` 25 | 26 | TypeScript › Preferences: Import Module Specifier 27 | Choose `relative` -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/assets/maze.json: -------------------------------------------------------------------------------- 1 | { 2 | "maze":[ 3 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 4 | 1,1,0,0,0,1,1,1,1,1,1,0,0,0,1,1,0,0,0,1, 5 | 1,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1, 6 | 1,0,1,1,0,1,0,0,0,0,1,1,1,0,1,1,0,0,0,1, 7 | 1,0,1,1,0,1,0,0,0,0,1,1,1,0,1,1,1,1,0,1, 8 | 1,0,1,1,0,1,0,0,0,0,1,1,1,0,1,1,1,1,0,1, 9 | 1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 10 | 1,0,0,0,0,1,1,1,1,1,0,1,0,1,1,0,1,1,0,1, 11 | 1,1,1,0,1,1,1,1,1,1,0,0,0,1,1,0,1,1,0,1, 12 | 1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1, 13 | 1,1,0,0,0,1,1,1,0,0,0,0,0,1,1,0,1,1,0,1, 14 | 1,1,0,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,0,1, 15 | 1,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,0,1, 16 | 1,1,1,0,1,0,1,1,0,1,0,0,0,1,1,0,0,0,0,1, 17 | 1,0,0,0,1,0,0,0,0,1,0,0,0,1,1,0,1,1,0,1, 18 | 1,0,1,1,1,1,0,1,1,1,1,1,0,0,0,0,1,1,0,1, 19 | 1,0,1,1,0,1,0,1,1,1,1,1,0,1,1,1,1,0,0,1, 20 | 1,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,1,0,1,1, 21 | 1,2,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1, 22 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 23 | ] 24 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/bin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 22 | Maze 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "maze-babylonjs", 3 | "version": "1.0.0", 4 | "description": "Source files for Maze in BabylonJS", 5 | "author": "Uldis Baurovskis , Armands Baurovskis ", 6 | "license": "MIT", 7 | "private": true, 8 | "scripts": { 9 | "start": "webpack serve" 10 | }, 11 | "devDependencies": { 12 | "html-webpack-plugin": "^5.5.0", 13 | "ts-loader": "^9.2.6", 14 | "typescript": "^4.5.2", 15 | "webpack": "^5.65.0", 16 | "webpack-cli": "^4.9.1", 17 | "webpack-dev-server": "^4.6.0", 18 | "webpack-glsl-minify": "^1.4.2", 19 | "file-loader": "^6.2.0" 20 | }, 21 | "dependencies": { 22 | "@babylonjs/core": "5.0.0-alpha.64", 23 | "@babylonjs/inspector": "5.0.0-alpha.64", 24 | "@babylonjs/materials": "5.0.0-alpha.64" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/src/Maze.ts: -------------------------------------------------------------------------------- 1 | import "@babylonjs/core/Debug/debugLayer"; 2 | import "@babylonjs/inspector"; 3 | 4 | import { GameBase } from "./common/GameBase"; 5 | 6 | export class Maze extends GameBase { 7 | protected addContent(): void { 8 | this.boot() 9 | .then(() => { 10 | // TODO: Fill 11 | }) 12 | } 13 | 14 | private boot(): Promise { 15 | // TODO: Fill 16 | return Promise.resolve(); 17 | } 18 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/src/common/GameBase.ts: -------------------------------------------------------------------------------- 1 | import { HemisphericLight, Engine, Scene, Vector3, ArcRotateCamera } from "@babylonjs/core"; 2 | 3 | export abstract class GameBase { 4 | protected readonly engine: Engine; 5 | protected readonly canvas: HTMLCanvasElement; 6 | protected readonly scene: Scene; 7 | 8 | public constructor() { 9 | this.canvas = this.createCanvas(); 10 | this.engine = this.createEngine(this.canvas); 11 | this.scene = this.createScene(this.engine); 12 | this.createCamera(this.scene); 13 | this.createLight(this.scene); 14 | this.addContent(); 15 | window.addEventListener("resize", this.onResize); 16 | this.engine.runRenderLoop(this.onRender); 17 | } 18 | public start(): void { 19 | this.onResize(); 20 | } 21 | protected createCanvas(): HTMLCanvasElement { 22 | const canvas = document.createElement("canvas"); 23 | document.body.appendChild(canvas); 24 | return canvas; 25 | } 26 | protected createEngine(canvas: HTMLCanvasElement): Engine { 27 | return new Engine(canvas, true, {}, true); 28 | } 29 | protected createScene(engine: Engine): Scene { 30 | return new Scene(engine, {}); 31 | } 32 | protected createCamera(scene: Scene) { 33 | const camera = new ArcRotateCamera("camera", -Math.PI * 0.5, Math.PI * 0.25, 12, Vector3.Zero(), scene); 34 | camera.attachControl(); 35 | } 36 | protected createLight(scene: Scene) { 37 | const lights = new HemisphericLight("light", new Vector3(0, 1, 0), scene); 38 | } 39 | protected abstract addContent(): void; 40 | private onRender = () => { 41 | this.scene.render(); 42 | } 43 | private onResize = () => { 44 | this.engine.resize(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/src/common/utils.ts: -------------------------------------------------------------------------------- 1 | import { TransformNode, Vector3 } from "@babylonjs/core"; 2 | 3 | export function getSize(node: TransformNode): Vector3 { 4 | const sizes = node.getHierarchyBoundingVectors(); 5 | return new Vector3( 6 | sizes.max.x - sizes.min.x, 7 | sizes.max.y - sizes.min.y, 8 | sizes.max.z - sizes.min.z, 9 | ); 10 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/src/components/mazeMap.ts: -------------------------------------------------------------------------------- 1 | import { Scene, Vector3 } from "@babylonjs/core"; 2 | 3 | enum Cell { 4 | Wall = 1, 5 | Ground = 0, 6 | PlayerStart = 2, 7 | } 8 | 9 | const cellSize = 1.5; 10 | const wallHeight = 2; 11 | 12 | export class MazeMap { 13 | public readonly playerStartPosition: Vector3 = new Vector3(0, 0, 0); 14 | public constructor(scene: Scene, source: Cell[]) { 15 | this.generateMap(scene, source); 16 | } 17 | private generateMap(scene: Scene, source: Cell[]): void { 18 | // TODO: Fill 19 | } 20 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/src/components/player.ts: -------------------------------------------------------------------------------- 1 | import { Mesh, Scene } from "@babylonjs/core"; 2 | 3 | enum PlayerAnimation { 4 | Idle = "YBot_Idle", 5 | Walk = "YBot_Walk", 6 | Run = "YBot_Run", 7 | } 8 | 9 | export class Player extends Mesh { 10 | public constructor(scene: Scene, skinName: string) { 11 | super("player", scene); 12 | // TODO: Fill 13 | } 14 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/src/consts.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Player https://www.babylonjs-playground.com/scenes/dummy3.babylon 3 | * Skybox https://www.babylonjs-playground.com/textures/country.env 4 | */ 5 | 6 | export enum Model { 7 | Player = "YBot", 8 | } 9 | 10 | export enum TextureId { 11 | Skybox = "https://www.babylonjs-playground.com/textures/country.env", 12 | Ground = "https://www.babylonjs-playground.com/textures/ground.jpg", 13 | Wall = "https://www.babylonjs-playground.com/textures/bloc.jpg", 14 | } 15 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Maze } from "./Maze"; 2 | 3 | new Maze().start(); -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/src/utils/playerInput.ts: -------------------------------------------------------------------------------- 1 | import { Scene } from "@babylonjs/core"; 2 | 3 | export class PlayerInput { 4 | public constructor(scene: Scene) { 5 | this.registerKeyboardEvents(scene); 6 | } 7 | 8 | private registerKeyboardEvents(scene: Scene) { 9 | // TODO: Fill 10 | } 11 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", 4 | "outDir": "./bin/", 5 | "moduleResolution": "node", 6 | "noImplicitAny": false, 7 | "module": "es6", 8 | "target": "es6", 9 | "jsx": "react", 10 | "allowJs": true, 11 | "typeRoots": ["./types", "node_modules/@types"], 12 | "resolveJsonModule": true, 13 | "allowSyntheticDefaultImports": true 14 | }, 15 | "include": [ 16 | "src/**/*.ts", 17 | "src/**/*.d.ts" 18 | ] 19 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/types/FileTypes.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.png" { 2 | var t: string; 3 | export default t; 4 | } 5 | 6 | declare module "*.jpg" { 7 | var t: string; 8 | export default t; 9 | } 10 | 11 | declare module "*.glsl" { 12 | var t: "String(GLSLScript)"; 13 | export default t; 14 | } 15 | 16 | declare module "*.babylon" { 17 | const url: "String(Babylon)"; 18 | export default url; 19 | } 20 | 21 | declare module "*.json" { 22 | var t: any; 23 | export default t; 24 | } 25 | 26 | declare module "*.env" { 27 | var t: "String(BabylonEnv)"; 28 | export default t; 29 | } 30 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-01-start/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: './src/index.ts', 6 | mode: 'development', 7 | devtool: 'source-map', 8 | output: { 9 | filename: '[name].main.js', 10 | path: path.resolve(__dirname, 'bin'), 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.tsx?$/, 16 | use: 'ts-loader', 17 | exclude: /node_modules/, 18 | }, 19 | { 20 | test: /\.glsl$/, 21 | use: { 22 | loader: "webpack-glsl-minify", 23 | options: { 24 | output: "source", 25 | esModule: true, 26 | preserveAll: true, 27 | }, 28 | }, 29 | exclude: /node_modules/, 30 | }, 31 | { 32 | test: /\.(png|jpe?g|gif)$/i, 33 | use: [ 34 | { 35 | loader: 'file-loader', 36 | }, 37 | ], 38 | }, 39 | ] 40 | }, 41 | resolve: { 42 | extensions: [ '.tsx', '.ts', '.js' ], 43 | }, 44 | plugins: [ 45 | new HtmlWebpackPlugin({ 46 | template: './bin/index.html', 47 | }), 48 | ], 49 | devServer: { 50 | compress: true, 51 | port: 4444, 52 | client: { 53 | overlay: true, 54 | progress: true, 55 | }, 56 | hot: true, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Rat race 3 | 4 | ## Project files for TS Bootcamp / BabylonJS Day 3 5 | 6 | ### How to install 7 | 8 | Run this command in root directory 9 | 10 | ```sh 11 | yarn install 12 | ``` 13 | 14 | ### How to launch the game 15 | Run this command in root directory 16 | 17 | ```sh 18 | yarn start 19 | ``` 20 | 21 | ### If having issues with import in VS Code id 22 | 23 | In Visual Studio Code, menu File → Preferences → Settings → User Settings 24 | Type `importModuleSpecifier` 25 | 26 | TypeScript › Preferences: Import Module Specifier 27 | Choose `relative` -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/assets/maze.json: -------------------------------------------------------------------------------- 1 | { 2 | "maze":[ 3 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 4 | 1,1,0,0,0,1,1,1,1,1,1,0,0,0,1,1,0,0,0,1, 5 | 1,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1, 6 | 1,0,1,1,0,1,0,0,0,0,1,1,1,0,1,1,0,0,0,1, 7 | 1,0,1,1,0,1,0,0,0,0,1,1,1,0,1,1,1,1,0,1, 8 | 1,0,1,1,0,1,0,0,0,0,1,1,1,0,1,1,1,1,0,1, 9 | 1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1, 10 | 1,0,0,0,0,1,1,1,1,1,0,1,0,1,1,0,1,1,0,1, 11 | 1,1,1,0,1,1,1,1,1,1,0,0,0,1,1,0,1,1,0,1, 12 | 1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1, 13 | 1,1,0,0,0,1,1,1,0,0,0,0,0,1,1,0,1,1,0,1, 14 | 1,1,0,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,0,1, 15 | 1,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,0,1, 16 | 1,1,1,0,1,0,1,1,0,1,0,0,0,1,1,0,0,0,0,1, 17 | 1,0,0,0,1,0,0,0,0,1,0,0,0,1,1,0,1,1,0,1, 18 | 1,0,1,1,1,1,0,1,1,1,1,1,0,0,0,0,1,1,0,1, 19 | 1,3,1,1,0,1,0,1,1,1,1,1,0,1,1,1,1,0,0,1, 20 | 1,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,1,0,1,1, 21 | 1,2,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1, 22 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 23 | ] 24 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/bin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 22 | Maze 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "maze-babylonjs", 3 | "version": "1.0.0", 4 | "description": "Source files for Maze in BabylonJS", 5 | "author": "Uldis Baurovskis , Armands Baurovskis ", 6 | "license": "MIT", 7 | "private": true, 8 | "scripts": { 9 | "start": "webpack serve" 10 | }, 11 | "devDependencies": { 12 | "html-webpack-plugin": "^5.5.0", 13 | "ts-loader": "^9.2.6", 14 | "typescript": "^4.5.2", 15 | "webpack": "^5.65.0", 16 | "webpack-cli": "^4.9.1", 17 | "webpack-dev-server": "^4.6.0", 18 | "webpack-glsl-minify": "^1.4.2", 19 | "file-loader": "^6.2.0" 20 | }, 21 | "dependencies": { 22 | "@babylonjs/core": "5.0.0-alpha.64", 23 | "@babylonjs/inspector": "5.0.0-alpha.64", 24 | "@babylonjs/materials": "5.0.0-alpha.64" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/src/Maze.ts: -------------------------------------------------------------------------------- 1 | import "@babylonjs/core/Debug/debugLayer"; 2 | import "@babylonjs/inspector"; 3 | 4 | import { GameBase } from "./common/GameBase"; 5 | import maze from "../assets/maze.json"; 6 | import { MazeMap } from "./components/mazeMap"; 7 | import { AssetsManager, CubeTexture } from "@babylonjs/core"; 8 | import { Player } from "./components/player"; 9 | import { Model, TextureId } from "./consts"; 10 | 11 | 12 | export class Maze extends GameBase { 13 | protected addContent(): void { 14 | this.boot() 15 | .then(() => { 16 | const map = new MazeMap(this.scene, maze.maze); 17 | 18 | const player = new Player(this.scene, Model.Player); 19 | player.position.copyFrom(map.playerStartPosition); 20 | 21 | const skyBoxTexture = this.scene.getTextureByName(TextureId.Skybox); 22 | this.scene.createDefaultSkybox(skyBoxTexture); 23 | this.scene.environmentTexture = skyBoxTexture; 24 | }) 25 | } 26 | 27 | private boot(): Promise { 28 | const assetManager = new AssetsManager(this.scene); 29 | assetManager.addMeshTask( 30 | "load-player", 31 | "", 32 | "https://www.babylonjs-playground.com/scenes/dummy3.babylon", 33 | "" 34 | ); 35 | assetManager.addCubeTextureTask("load-skybox", TextureId.Skybox); 36 | assetManager.addTextureTask("load-ground", TextureId.Ground); 37 | assetManager.addTextureTask("load-wall", TextureId.Wall); 38 | return assetManager.loadAsync(); 39 | } 40 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/src/common/GameBase.ts: -------------------------------------------------------------------------------- 1 | import { HemisphericLight, Engine, Scene, Vector3, ArcRotateCamera } from "@babylonjs/core"; 2 | 3 | export abstract class GameBase { 4 | protected readonly engine: Engine; 5 | protected readonly canvas: HTMLCanvasElement; 6 | protected readonly scene: Scene; 7 | 8 | public constructor() { 9 | this.canvas = this.createCanvas(); 10 | this.engine = this.createEngine(this.canvas); 11 | this.scene = this.createScene(this.engine); 12 | this.createCamera(this.scene); 13 | this.createLight(this.scene); 14 | this.addContent(); 15 | window.addEventListener("resize", this.onResize); 16 | this.engine.runRenderLoop(this.onRender); 17 | } 18 | public start(): void { 19 | this.onResize(); 20 | } 21 | protected createCanvas(): HTMLCanvasElement { 22 | const canvas = document.createElement("canvas"); 23 | document.body.appendChild(canvas); 24 | return canvas; 25 | } 26 | protected createEngine(canvas: HTMLCanvasElement): Engine { 27 | return new Engine(canvas, true, {}, true); 28 | } 29 | protected createScene(engine: Engine): Scene { 30 | return new Scene(engine, {}); 31 | } 32 | protected createCamera(scene: Scene) { 33 | const camera = new ArcRotateCamera("camera", -Math.PI * 0.5, Math.PI * 0.25, 12, Vector3.Zero(), scene); 34 | camera.attachControl(); 35 | } 36 | protected createLight(scene: Scene) { 37 | const lights = new HemisphericLight("light", new Vector3(0, 1, 0), scene); 38 | } 39 | protected abstract addContent(): void; 40 | private onRender = () => { 41 | this.scene.render(); 42 | } 43 | private onResize = () => { 44 | this.engine.resize(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/src/common/utils.ts: -------------------------------------------------------------------------------- 1 | import { TransformNode, Vector3 } from "@babylonjs/core"; 2 | 3 | export function getSize(node: TransformNode): Vector3 { 4 | const sizes = node.getHierarchyBoundingVectors(); 5 | return new Vector3( 6 | sizes.max.x - sizes.min.x, 7 | sizes.max.y - sizes.min.y, 8 | sizes.max.z - sizes.min.z, 9 | ); 10 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/src/consts.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Player https://www.babylonjs-playground.com/scenes/dummy3.babylon 3 | * Skybox https://www.babylonjs-playground.com/textures/country.env 4 | * Decal https://www.babylonjs-playground.com/textures/impact.png 5 | */ 6 | 7 | export enum Model { 8 | Player = "YBot", 9 | } 10 | 11 | export enum TextureId { 12 | Skybox = "https://www.babylonjs-playground.com/textures/country.env", 13 | Ground = "https://www.babylonjs-playground.com/textures/ground.jpg", 14 | Wall = "https://www.babylonjs-playground.com/textures/bloc.jpg", 15 | Decal = "https://www.babylonjs-playground.com/textures/impact.png", 16 | NormalMapWall = "https://www.babylonjs-playground.com/textures/rockn.png", 17 | } 18 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Maze } from "./Maze"; 2 | 3 | new Maze().start(); -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/src/utils/playerInput.ts: -------------------------------------------------------------------------------- 1 | import { KeyboardEventTypes, Scene } from "@babylonjs/core"; 2 | 3 | export class PlayerInput { 4 | public moveForward: boolean = false; 5 | public moveBackward: boolean = false; 6 | public moveLeft: boolean = false; 7 | public moveRight: boolean = false; 8 | 9 | public constructor(scene: Scene) { 10 | this.registerKeyboardEvents(scene); 11 | } 12 | 13 | private registerKeyboardEvents(scene: Scene) { 14 | scene.onKeyboardObservable.add((info) => { 15 | switch(info.event.code) { 16 | case "KeyW": 17 | this.moveForward = info.type === KeyboardEventTypes.KEYDOWN; 18 | break; 19 | case "KeyS": 20 | this.moveBackward = info.type === KeyboardEventTypes.KEYDOWN; 21 | break; 22 | case "KeyA": 23 | this.moveLeft = info.type === KeyboardEventTypes.KEYDOWN; 24 | break; 25 | case "KeyD": 26 | this.moveRight = info.type === KeyboardEventTypes.KEYDOWN; 27 | break; 28 | } 29 | }); 30 | } 31 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", 4 | "outDir": "./bin/", 5 | "moduleResolution": "node", 6 | "noImplicitAny": false, 7 | "module": "es6", 8 | "target": "es6", 9 | "jsx": "react", 10 | "allowJs": true, 11 | "typeRoots": ["./types", "node_modules/@types"], 12 | "resolveJsonModule": true, 13 | "allowSyntheticDefaultImports": true 14 | }, 15 | "include": [ 16 | "src/**/*.ts", 17 | "src/**/*.d.ts", 18 | "./types", 19 | ] 20 | } -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/types/FileTypes.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.png" { 2 | var t: string; 3 | export default t; 4 | } 5 | 6 | declare module "*.jpg" { 7 | var t: string; 8 | export default t; 9 | } 10 | 11 | declare module "*.glsl" { 12 | var t: "String(GLSLScript)"; 13 | export default t; 14 | } 15 | 16 | declare module "*.babylon" { 17 | const url: "String(Babylon)"; 18 | export default url; 19 | } 20 | 21 | declare module "*.json" { 22 | var t: any; 23 | export default t; 24 | } 25 | 26 | declare module "*.env" { 27 | var t: "String(BabylonEnv)"; 28 | export default t; 29 | } 30 | -------------------------------------------------------------------------------- /lessons/21-babylonjs/maze-02-start/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: './src/index.ts', 6 | mode: 'development', 7 | devtool: 'source-map', 8 | output: { 9 | filename: '[name].main.js', 10 | path: path.resolve(__dirname, 'bin'), 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.tsx?$/, 16 | use: 'ts-loader', 17 | exclude: /node_modules/, 18 | }, 19 | { 20 | test: /\.glsl$/, 21 | use: { 22 | loader: "webpack-glsl-minify", 23 | options: { 24 | output: "source", 25 | esModule: true, 26 | preserveAll: true, 27 | }, 28 | }, 29 | exclude: /node_modules/, 30 | }, 31 | { 32 | test: /\.(png|jpe?g|gif)$/i, 33 | use: [ 34 | { 35 | loader: 'file-loader', 36 | }, 37 | ], 38 | }, 39 | ] 40 | }, 41 | resolve: { 42 | extensions: [ '.tsx', '.ts', '.js' ], 43 | }, 44 | plugins: [ 45 | new HtmlWebpackPlugin({ 46 | template: './bin/index.html', 47 | }), 48 | ], 49 | devServer: { 50 | compress: true, 51 | port: 4444, 52 | client: { 53 | overlay: true, 54 | progress: true, 55 | }, 56 | hot: true, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /presentations/BabylonJS/introduction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/BabylonJS/introduction.pdf -------------------------------------------------------------------------------- /presentations/BabylonJS/maze-01.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/BabylonJS/maze-01.pdf -------------------------------------------------------------------------------- /presentations/BabylonJS/maze-02.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/BabylonJS/maze-02.pdf -------------------------------------------------------------------------------- /presentations/CSS tools/CSS tools.md: -------------------------------------------------------------------------------- 1 | # CSS Instruments 2 | 3 | [Presentation](https://thebeercake.github.io/css-lecture/) 4 | [Presentation source](https://github.com/TheBeerCake/css-lecture) 5 | 6 | ## List of useful resources 7 | 8 | - [Specificity Calculator](https://specificity.keegan.st/) 9 | - [A Guide to Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) 10 | - [CSS Modules](https://github.com/css-modules/css-modules) 11 | - [Styled Components](https://styled-components.com/) 12 | - [CSS Custom Properties](https://www.smashingmagazine.com/2017/04/start-using-css-custom-properties/) 13 | - [CSS Custom Properties Performance](https://lisilinhart.info/posts/css-variables-performance/) 14 | - [Difference between flex-basis and width](https://www.freecodecamp.org/news/flexboxs-flex-basis-explained-83d1a01413b7/) 15 | - [Complete guide to Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) 16 | - [Grid by Example by Rachel Andrew](https://gridbyexample.com/) 17 | - [The Explicit and Implicit grid explained](https://www.youtube.com/watch?v=cMWnIX3ukLI) 18 | - [Understanding CSS Grid Container](https://www.smashingmagazine.com/2020/01/understanding-css-grid-container/) 19 | - [Masonry with CSS by Tobias Ahlin Bjerrome](https://tobiasahlin.com/blog/masonry-with-css/) 20 | - [Blog by Ahmad Shadeed](https://ishadeed.com/articles/) 21 | - [Guess-css.app](https://www.guess-css.app/) 22 | - [Taking CSS Linting to the Next Level with Stylelint](https://www.sitepoint.com/taking-css-linting-next-level-stylelint/) 23 | - [Can I use ...?](https://caniuse.com/) 24 | -------------------------------------------------------------------------------- /presentations/Network.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/Network.pdf -------------------------------------------------------------------------------- /presentations/React/1. React Basic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/React/1. React Basic.pdf -------------------------------------------------------------------------------- /presentations/React/2. React Middle.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/React/2. React Middle.pdf -------------------------------------------------------------------------------- /presentations/React/3. React Advanced.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/React/3. React Advanced.pdf -------------------------------------------------------------------------------- /presentations/React/4. React Patterns.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/React/4. React Patterns.pdf -------------------------------------------------------------------------------- /presentations/Redux/1 Redux Basic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/Redux/1 Redux Basic.pdf -------------------------------------------------------------------------------- /presentations/TypeScript/1. TypeScript. Basic.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/TypeScript/1. TypeScript. Basic.pptx -------------------------------------------------------------------------------- /presentations/TypeScript/2. TypeScript. Middle.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/TypeScript/2. TypeScript. Middle.pptx -------------------------------------------------------------------------------- /presentations/TypeScript/3. TypeScript. Advanced.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/TypeScript/3. TypeScript. Advanced.pptx -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 4 6 | indent_style = space 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.coffee] 12 | indent_size = 2 13 | 14 | [*.json] 15 | indent_size = 2 16 | 17 | [{tsconfig.json,tsconfig.*.json}] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/README.md: -------------------------------------------------------------------------------- 1 | # TypeScript Declaration Files Playground 2 | 3 | ## Description 4 | 5 | Here you can find examples of using declaration files (`.d.ts`) for: 6 | 7 | - Defining types for `.js` files (`lib` folder). 8 | - Declaring global objects and modules for further usage in the project 9 | (`global-types/declaration.d.ts`). 10 | - Expanding native interfaces or third-party modules' functionality 11 | (`global-types/global.d.ts`). 12 | - Adding additional global utility types 13 | (`global-types/CustomUtilityTypes.d.ts`). 14 | 15 | To play with it by yourself, just go to the `ts-declaration-files` folder and 16 | run `npm install`. And feel free to do what you want! 17 | 18 | This example is some kind of part of lecture 19 | [TypeScript. Advanced.pptx](../3.%20TypeScript.%20Advanced.pptx) but feel free 20 | to consider it at any time on your own. 21 | 22 | ## Useful Links 23 | 24 | To dive very deep to this theme you need to reference to the topic 25 | [Declaration Files](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html). 26 | 27 | To use separate defined TypeScript types or contribute types to some packages, 28 | (it is concerned only those package that do not include types on their own) 29 | see [Definitely Typed](https://definitelytyped.org/) and their 30 | [GitHub](https://github.com/DefinitelyTyped/DefinitelyTyped). 31 | -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "typescript": { 6 | "version": "4.2.4", 7 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", 8 | "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/src/asserts/ts_guru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/TypeScript/ts-declaration-files/src/asserts/ts_guru.png -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/src/constants.ts: -------------------------------------------------------------------------------- 1 | export enum Direction { 2 | Up = 'up', 3 | Down = 'down', 4 | Right = 'right', 5 | Left = 'left', 6 | } 7 | 8 | // export type { Direction }; 9 | -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/src/global-types/CustomUtilityTypes.d.ts: -------------------------------------------------------------------------------- 1 | type PartialRecord = Partial>; 2 | -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/src/global-types/declaration.d.ts: -------------------------------------------------------------------------------- 1 | declare const JSON_007: { 2 | stringify(): void; 3 | }; 4 | 5 | declare module '*.png' { 6 | const src: string; 7 | export default src; 8 | } 9 | -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/src/global-types/global.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | DEV_MODE?: boolean; 3 | } 4 | 5 | interface Array { 6 | customFilter(): T[]; 7 | } 8 | -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/src/index.ts: -------------------------------------------------------------------------------- 1 | import SURNAME, { compact, AUTHOR_NAME } from "./lib/superUtils"; 2 | // import type { Direction } from "./constants"; 3 | import { Direction } from "./constants"; 4 | // declare modules/object/variables: ./global-types/declaration.d.ts 5 | import * as TSGuru from "./asserts/ts_guru.png"; 6 | 7 | const DIRECTIONS: Direction[] = [ 8 | Direction.Down, 9 | Direction.Right, 10 | ]; 11 | 12 | const DIRTY_DIRECTIONS: (Direction | null | undefined)[] = [ 13 | Direction.Down, 14 | undefined, 15 | Direction.Right, 16 | null, 17 | ]; 18 | 19 | // declare types for js files: ./lib/superUtils 20 | const onlyRealDirections = compact(DIRTY_DIRECTIONS); 21 | 22 | const authorName = AUTHOR_NAME; 23 | const authorSurname = SURNAME; 24 | 25 | // define types for js files: ./global-types/CustomUtilityTypes.d.ts 26 | const NumberByDirection: PartialRecord = { 27 | [Direction.Right]: 312, 28 | }; 29 | 30 | // declare modules/object/variables: ./global-types/declaration.d.ts 31 | JSON_007.stringify(); 32 | 33 | // extend native objects: ./global-types/global.d.ts 34 | window.DEV_MODE; 35 | [].customFilter(); 36 | -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/src/lib/superUtils.d.ts: -------------------------------------------------------------------------------- 1 | // `declare` specifies a type to an already existing variable, not declaring a new one. 2 | 3 | export declare function compact(array: T[]): NonNullable[]; 4 | 5 | export declare const AUTHOR_NAME: string; 6 | 7 | // Top-level declarations in .d.ts files must start with either a 'declare' or 'export' modifier. 8 | // An implementation cannot be declared in ambient contexts. 9 | // export function asd(arg: string): number; 10 | 11 | // Initializers are not allowed in ambient contexts. 12 | // const DEFAULT_VALUE_1: Record = {}; 13 | // A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference. 14 | // const DEFAULT_VALUE_2 = {}; 15 | 16 | declare const AUTHOR_SURNAME: 'Kiselyov'; 17 | // declare const AUTHOR_SURNAME = 'Kiselyov'; // same 18 | export default AUTHOR_SURNAME; 19 | 20 | 21 | 22 | // export declare namespace superUtils { 23 | // function compact(array: T[]): NonNullable[]; 24 | 25 | // const AUTHOR_NAME: string; 26 | 27 | // const AUTHOR_SURNAME: 'Kiselyov'; 28 | // } 29 | -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/src/lib/superUtils.js: -------------------------------------------------------------------------------- 1 | export function compact(array) { 2 | return array.filter(Boolean); 3 | } 4 | 5 | export const AUTHOR_NAME = 'Dmitry'; 6 | 7 | const AUTHOR_SURNAME = 'Kiselyov'; 8 | export default AUTHOR_SURNAME; 9 | -------------------------------------------------------------------------------- /presentations/TypeScript/ts-declaration-files/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { Direction } from "./constants"; 2 | 3 | export interface SomeObject { 4 | direction: Direction; 5 | } 6 | -------------------------------------------------------------------------------- /presentations/UnitTesting/01-basic-react-setup/.gitignore: -------------------------------------------------------------------------------- 1 | .parcel-cache 2 | dist 3 | -------------------------------------------------------------------------------- /presentations/UnitTesting/01-basic-react-setup/README.md: -------------------------------------------------------------------------------- 1 | - `yarn` 2 | - `yarn start` 3 | - `yarn test` or `yarn test --watch` 4 | 5 | ### Remember: 6 | - we use parcel here only to be able to bundle and serve our app. 7 | - React testing library it is like additional framework on top of jest using which we can easily test react components 8 | - And all these unit tests are run on node (backend), so for UI testing we have set jest environment `jsdom` which lets us to use most of DOM features we have on browser, like `document.querySelector` which we otherwise wouldn't have if jest environment would be node. 9 | -------------------------------------------------------------------------------- /presentations/UnitTesting/01-basic-react-setup/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'jsdom', 5 | }; 6 | -------------------------------------------------------------------------------- /presentations/UnitTesting/01-basic-react-setup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "parcel ./src/index.html", 4 | "test": "jest" 5 | }, 6 | "devDependencies": { 7 | "@testing-library/react": "^12.1.3", 8 | "@types/jest": "^27.4.0", 9 | "@types/react": "^17.0.39", 10 | "@types/react-dom": "^17.0.11", 11 | "jest": "^27.5.1", 12 | "parcel": "^2.3.2", 13 | "ts-jest": "^27.1.3", 14 | "typescript": "^4.5.5" 15 | }, 16 | "dependencies": { 17 | "react": "^17.0.2", 18 | "react-dom": "^17.0.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /presentations/UnitTesting/01-basic-react-setup/src/components/Todos.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export type Todo = { id: number,title: string }; 4 | export type TodosProps = {getTodos: () => Promise>}; 5 | 6 | export const Todos: React.FC = ({ getTodos }) => { 7 | const [todos, setTodos] = React.useState>([]); 8 | React.useEffect(() => { getTodos().then(setTodos) }, [getTodos]); 9 | 10 | return ( 11 | <> 12 |
      TODOS:
      13 |
        14 | {todos.map(t =>
      • {t.title}
      • )} 15 |
      16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /presentations/UnitTesting/01-basic-react-setup/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |