├── .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 |
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 |
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 |
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 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/presentations/UnitTesting/01-basic-react-setup/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {render} from "react-dom";
3 | import {Todos} from "./components/Todos";
4 |
5 | const fetcherMiddleware = {
6 | getTodos: () => window
7 | .fetch("https://jsonplaceholder.typicode.com/todos")
8 | .then((r) => r.json())
9 | }
10 |
11 |
12 | render(, document.getElementById("root"))
13 |
--------------------------------------------------------------------------------
/presentations/UnitTesting/01-basic-setup/README.md:
--------------------------------------------------------------------------------
1 | - `yarn` or `npm install`
2 | - Since we have defined script alias in package.json we can run this
3 | - `yarn test` or `npm test`
4 | - `yarn test --watch` or `npm test -- --watch`
5 |
--------------------------------------------------------------------------------
/presentations/UnitTesting/01-basic-setup/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2 | module.exports = {
3 | preset: 'ts-jest',
4 | testEnvironment: 'node',
5 | };
--------------------------------------------------------------------------------
/presentations/UnitTesting/01-basic-setup/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "test": "jest"
4 | },
5 | "devDependencies": {
6 | "@types/jest": "^27.4.0",
7 | "jest": "^27.5.1",
8 | "ts-jest": "^27.1.3",
9 | "typescript": "^4.5.5"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/presentations/UnitTesting/01-basic-setup/src/fibonacci.spec.ts:
--------------------------------------------------------------------------------
1 | import {fibonacciSpec} from "./fibonacci";
2 |
3 | describe("fibonacci", function () {
4 | it.each([
5 | {input: 0, expected: 0},
6 | {input: 1, expected: 1},
7 | {input: 2, expected: 1},
8 | {input: 3, expected: 2},
9 | {input: 10, expected: 55},
10 | ])("should return $expected for input $input", ({ input, expected }) => {
11 | expect(fibonacciSpec(input)).toEqual(expected)
12 | })
13 | });
14 |
--------------------------------------------------------------------------------
/presentations/UnitTesting/01-basic-setup/src/fibonacci.ts:
--------------------------------------------------------------------------------
1 | export function fibonacciSpec(i: number): number {
2 | return i < 2 ? i : fibonacciSpec(i - 2) + fibonacciSpec(i - 1);
3 | }
4 |
--------------------------------------------------------------------------------
/presentations/WebGL.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/WebGL.pdf
--------------------------------------------------------------------------------
/presentations/Webpack.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evolution-gaming/typescript-bootcamp/549a85a2276ce10bda38eab1b77b875850a4c3bb/presentations/Webpack.pdf
--------------------------------------------------------------------------------