├── .eslintrc
├── .prettierrc
├── 01-imperative-to-declarative
├── exercise
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.solution.js
│ │ ├── App.start.js
│ │ ├── index.js
│ │ └── lib
│ │ │ └── index.css
│ └── yarn.lock
└── lecture
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ ├── src
│ ├── App.final.js
│ ├── App.start.js
│ ├── index.js
│ └── lib
│ │ ├── SineWave.js
│ │ ├── createOscillator.js
│ │ └── index.css
│ └── yarn.lock
├── 02-hocs-render-props
├── exercise
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.hoc.js
│ │ ├── App.hooks.js
│ │ ├── App.render-prop.js
│ │ ├── App.start.js
│ │ ├── images
│ │ │ ├── earth.jpg
│ │ │ ├── galaxy.jpg
│ │ │ └── trees.jpg
│ │ ├── index.js
│ │ └── lib
│ │ │ ├── createMediaListener.js
│ │ │ ├── images
│ │ │ ├── earth.jpg
│ │ │ ├── galaxy.jpg
│ │ │ └── trees.jpg
│ │ │ ├── index.css
│ │ │ └── screens.js
│ └── yarn.lock
└── lecture
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ ├── src
│ ├── App.final.hoc.js
│ ├── App.final.render-props.js
│ ├── App.start.js
│ ├── index.js
│ └── lib
│ │ ├── LoadingDots.js
│ │ ├── Map.js
│ │ ├── getAddressFromCoords.js
│ │ └── index.css
│ └── yarn.lock
├── 03-clone-element
├── exercise
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ └── src
│ │ ├── App.solution.js
│ │ ├── App.start.js
│ │ ├── index.js
│ │ └── lib
│ │ ├── index.css
│ │ └── noise.png
└── lecture
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ ├── src
│ ├── App.cloneElement.js
│ ├── App.context.js
│ ├── App.start.js
│ ├── index.js
│ └── lib
│ │ ├── index.css
│ │ └── text.js
│ └── yarn.lock
├── 04-context
├── exercise
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.solution.js
│ │ ├── App.start.js
│ │ ├── index.js
│ │ └── lib
│ │ │ ├── index.css
│ │ │ ├── mariobros.mp3
│ │ │ └── podcast.mp4
│ └── yarn.lock
└── lecture
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ ├── src
│ ├── App.final.js
│ ├── App.start.js
│ ├── index.js
│ └── lib
│ │ ├── index.css
│ │ └── text.js
│ └── yarn.lock
├── 05-portals
├── exercise
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── .App.js.swo
│ │ ├── App.solution.js
│ │ ├── App.start.js
│ │ ├── index.js
│ │ └── lib
│ │ │ ├── .App.js.swo
│ │ │ └── index.css
│ └── yarn.lock
└── lecture
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ ├── src
│ ├── App.final.js
│ ├── App.start.js
│ ├── index.js
│ └── lib
│ │ └── index.css
│ └── yarn.lock
├── 06-wai-aria
├── exercise
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.solution.js
│ │ ├── App.start.js
│ │ ├── index.js
│ │ └── lib
│ │ │ ├── index.css
│ │ │ └── noise.png
│ └── yarn.lock
└── lecture
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ ├── src
│ ├── .App.js.swn
│ ├── .App.js.swo
│ ├── App.final.js
│ ├── App.start.js
│ ├── index.js
│ └── lib
│ │ ├── .App.js.swn
│ │ ├── .App.js.swo
│ │ └── index.css
│ └── yarn.lock
├── 07-gsbu
├── exercise
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.solution.js
│ │ ├── App.start.js
│ │ ├── index.js
│ │ └── lib
│ │ │ ├── FadeIn.js
│ │ │ ├── avatars
│ │ │ ├── EI.png
│ │ │ ├── GC.png
│ │ │ ├── MP.png
│ │ │ └── TG.png
│ │ │ ├── index.css
│ │ │ └── messages.js
│ └── yarn.lock
└── lecture
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ ├── src
│ ├── App.final.js
│ ├── App.start.js
│ ├── index.js
│ └── lib
│ │ └── index.css
│ └── yarn.lock
├── 08-gdsfp
├── exercise
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ └── index.html
│ ├── src
│ │ ├── App.solution.js
│ │ ├── App.start.js
│ │ ├── index.js
│ │ └── lib
│ │ │ ├── digital-7
│ │ │ ├── digital-7 (italic).ttf
│ │ │ ├── digital-7 (mono italic).ttf
│ │ │ ├── digital-7 (mono).ttf
│ │ │ ├── digital-7.ttf
│ │ │ └── readme.txt
│ │ │ └── index.css
│ └── yarn.lock
└── lecture
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ └── index.html
│ ├── src
│ ├── App.final.js
│ ├── App.start.js
│ ├── index.js
│ └── lib
│ │ └── index.css
│ └── yarn.lock
├── 09-suspense
├── exercise
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.solution.js
│ │ ├── App.start.js
│ │ ├── index.js
│ │ └── lib
│ │ │ ├── cache.js
│ │ │ └── index.css
│ └── yarn.lock
└── lecture
│ ├── .gitignore
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ ├── src
│ ├── App.Refactor.js
│ ├── App.js
│ ├── index.js
│ └── lib
│ │ ├── Competitions.js
│ │ ├── ManageScroll.js
│ │ ├── index.css
│ │ └── utils.js
│ └── yarn.lock
├── 10-carousel
├── .eslintrc
├── .gitignore
├── README.md
├── config
│ ├── env.js
│ ├── jest
│ │ ├── cssTransform.js
│ │ └── fileTransform.js
│ ├── paths.js
│ ├── polyfills.js
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── webpackDevServer.config.js
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── scripts
│ ├── build.js
│ ├── start.js
│ └── test.js
└── src
│ ├── .App.Media.start.js.swo
│ ├── App.exercise.js
│ ├── App.solution.js
│ ├── Progress.js
│ ├── index.js
│ ├── useProgress.js
│ └── whatevs
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ ├── index.css
│ └── slides.js
├── README.md
├── package.json
├── scripts
└── install.js
└── yarn.lock
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app"
3 | }
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/.prettierrc
--------------------------------------------------------------------------------
/01-imperative-to-declarative/exercise/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/01-imperative-to-declarative/exercise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "01",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.5.0",
7 | "react-dom": "^16.5.0"
8 | },
9 | "devDependencies": {
10 | "react-scripts": "1.0.10"
11 | },
12 | "scripts": {
13 | "start": "react-scripts start",
14 | "build": "react-scripts build",
15 | "test": "react-scripts test --env=jsdom",
16 | "eject": "react-scripts eject"
17 | }
18 | }
--------------------------------------------------------------------------------
/01-imperative-to-declarative/exercise/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/01-imperative-to-declarative/exercise/public/favicon.ico
--------------------------------------------------------------------------------
/01-imperative-to-declarative/exercise/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | (this.node = n)} width={width} height={height} />;
54 | }
55 | }
56 |
57 | export default SineWave;
58 |
--------------------------------------------------------------------------------
/01-imperative-to-declarative/lecture/src/lib/createOscillator.js:
--------------------------------------------------------------------------------
1 | class Oscillator {
2 | constructor(audioContext) {
3 | this.audioContext = audioContext;
4 | this.oscillatorNode = this.audioContext.createOscillator();
5 | this.oscillatorNode.start(0);
6 | this.gainNode = this.audioContext.createGain();
7 | this.pitchBase = 50;
8 | this.pitchBend = 0;
9 | this.pitchRange = 2000;
10 | this.volume = 0.5;
11 | this.maxVolume = 0.5;
12 | this.frequency = this.pitchBase;
13 | this.hasConnected = false;
14 | this.frequency = this.pitchBase;
15 | }
16 |
17 | play() {
18 | this.oscillatorNode.connect(this.gainNode);
19 | this.hasConnected = true;
20 | }
21 |
22 | stop() {
23 | if (this.hasConnected) {
24 | this.oscillatorNode.disconnect(this.gainNode);
25 | this.hasConnected = false;
26 | }
27 | }
28 |
29 | setType(type) {
30 | this.oscillatorNode.type = type;
31 | }
32 |
33 | setPitchBend(v) {
34 | this.pitchBend = v;
35 | this.frequency = this.pitchBase + this.pitchBend * this.pitchRange;
36 | this.oscillatorNode.frequency.value = this.frequency;
37 | }
38 |
39 | setVolume(v) {
40 | this.volume = this.maxVolume * v;
41 | this.gainNode.gain.value = this.volume;
42 | }
43 |
44 | connect(output) {
45 | this.gainNode.connect(output);
46 | }
47 | }
48 |
49 | function createOscillator() {
50 | const audioContext = new AudioContext();
51 | const oscillator = new Oscillator(audioContext);
52 | oscillator.connect(audioContext.destination);
53 | return oscillator;
54 | }
55 |
56 | export default createOscillator;
57 |
--------------------------------------------------------------------------------
/01-imperative-to-declarative/lecture/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 40px;
3 | background: radial-gradient(
4 | ellipse at center,
5 | #fac695 0%,
6 | #f5ab66 47%,
7 | #ef8d31 100%
8 | );
9 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
10 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
11 | }
12 |
13 | .App {
14 | width: 400px;
15 | height: 400px;
16 | margin: auto;
17 | position: relative;
18 | }
19 |
20 | .theremin {
21 | background: hsla(0, 100%, 100%, 0.5);
22 | cursor: crosshair;
23 | width: 100%;
24 | height: 100%;
25 | }
26 |
27 | .label {
28 | color: white;
29 | text-transform: uppercase;
30 | font-size: 24px;
31 | font-weight: bold;
32 | position: absolute;
33 | }
34 |
35 | .volume {
36 | position: absolute;
37 | line-height: 1em;
38 | bottom: calc(50% - 0.5em);
39 | left: calc(-50% - 1em);
40 | width: 100%;
41 | transform: rotate(-90deg);
42 | text-align: center;
43 | }
44 |
45 | .pitch {
46 | width: 100%;
47 | text-align: center;
48 | bottom: -1.5em;
49 | }
50 |
51 | canvas {
52 | cursor: none;
53 | }
54 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "02",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.8.1",
7 | "react-dom": "^16.8.1",
8 | "react-transition-group": "^2.4.0"
9 | },
10 | "devDependencies": {
11 | "react-scripts": "1.0.10"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/02-hocs-render-props/exercise/public/favicon.ico
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/App.hoc.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import createMediaListener from "./lib/createMediaListener";
3 | import { Galaxy, Trees, Earth } from "./lib/screens";
4 |
5 | const withMedia = queries => Comp => {
6 | const media = createMediaListener(queries);
7 |
8 | return class WithMedia extends React.Component {
9 | state = {
10 | media: media.getState()
11 | };
12 |
13 | componentDidMount() {
14 | media.listen(media => this.setState({ media }));
15 | }
16 |
17 | componentWillUnmount() {
18 | media.dispose();
19 | }
20 |
21 | render() {
22 | return ;
23 | }
24 | };
25 | };
26 |
27 | class App extends React.Component {
28 | render() {
29 | const { media } = this.props;
30 |
31 | return (
32 |
33 | {media.big ? (
34 |
35 | ) : media.tiny ? (
36 |
37 | ) : (
38 |
39 | )}
40 |
41 | );
42 | }
43 | }
44 |
45 | const AppWithMedia = withMedia({
46 | big: "(min-width : 1000px)",
47 | tiny: "(max-width: 600px)"
48 | })(App);
49 |
50 | export default AppWithMedia;
51 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/App.hooks.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import createMediaListener from "./lib/createMediaListener";
3 | import { Galaxy, Trees, Earth } from "./lib/screens";
4 |
5 | const media = createMediaListener({
6 | big: "(min-width : 1000px)",
7 | tiny: "(max-width: 600px)"
8 | });
9 |
10 | //
11 |
12 | //
13 |
14 | class Media extends React.Component {
15 | // static propTypes = {
16 | // query: PropTypes.string.isRequired
17 | // };
18 |
19 | state = { media: media.getState() };
20 |
21 | componentDidMount() {
22 | media.listen(media => this.setState({ media }));
23 | }
24 |
25 | componentDidUpdate(prevProps) {
26 | if (this.props.query !== prevProps.query) {
27 | // query prop changed!
28 | // time to teardown the old listener and setup
29 | // the new listener
30 | }
31 | }
32 |
33 | componentWillUnmount() {
34 | media.dispose();
35 | }
36 |
37 | render() {
38 | return this.props.children(this.state.media);
39 | }
40 | }
41 |
42 | function useMedia(query) {
43 | const [mediaState, updateMedia] = useState(true);
44 |
45 | useEffect(
46 | () => {
47 | // componentDidMount or componentDidUpdate
48 | const match = window.matchMedia(query);
49 |
50 | function handleMatchChange() {
51 | updateMedia(match.matches);
52 | }
53 |
54 | handleMatchChange();
55 |
56 | match.addListener(handleMatchChange);
57 |
58 | return () => {
59 | // componentWillUnmount
60 | match.removeListener(handleMatchChange);
61 | };
62 | },
63 | [query]
64 | );
65 |
66 | return mediaState;
67 | }
68 |
69 | function App() {
70 | const [flipped, setFlipped] = useState(false);
71 |
72 | const bigQuery = "(min-width: 1000px)";
73 | const smallQuery = "(max-width: 600px)";
74 |
75 | const big = useMedia(flipped ? smallQuery : bigQuery);
76 | const small = useMedia(flipped ? bigQuery : smallQuery);
77 |
78 | return (
79 |
80 | setFlipped(!flipped)}
82 | style={{
83 | position: "fixed",
84 | top: 10,
85 | left: 10,
86 | zIndex: Number.MAX_SAFE_INTEGER
87 | }}
88 | >
89 | {flipped
90 | ? "Restore everything to normal, I feel sick"
91 | : "Flip the universe on its head"}
92 |
93 |
94 | {big ? (
95 |
96 | ) : small ? (
97 |
98 | ) : (
99 |
100 | )}
101 |
102 | );
103 | }
104 |
105 | export default App;
106 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/App.render-prop.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import createMediaListener from "./lib/createMediaListener";
3 | import { Galaxy, Trees, Earth } from "./lib/screens";
4 |
5 | class Media extends React.Component {
6 | media = createMediaListener(this.props.queries);
7 |
8 | state = {
9 | media: this.media.getState()
10 | };
11 |
12 | componentDidMount() {
13 | this.media.listen(media => this.setState({ media }));
14 | }
15 |
16 | componentDidUpdate(prevProps) {
17 | if (this.props.queries !== prevProps.queries) {
18 | this.media.dispose();
19 | this.media = createMediaListener(
20 | this.props.queries
21 | );
22 | this.media.listen(media =>
23 | this.setState({ media })
24 | );
25 | }
26 | }
27 |
28 | componentWillUnmount() {
29 | this.media.dispose();
30 | }
31 |
32 | render() {
33 | return this.props.children(this.state.media);
34 | }
35 | }
36 |
37 | class App extends React.Component {
38 | state = {
39 | queries: {
40 | big: "(min-width : 1000px)",
41 | tiny: "(max-width: 600px)"
42 | }
43 | };
44 |
45 | render() {
46 | return (
47 |
48 | {media => (
49 |
50 | {media.big ? (
51 |
52 | ) : media.tiny ? (
53 |
54 | ) : (
55 |
56 | )}
57 |
58 | )}
59 |
60 | );
61 | }
62 | }
63 |
64 | export default App;
65 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/App.start.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import createMediaListener from "./lib/createMediaListener";
3 | import { Galaxy, Trees, Earth } from "./lib/screens";
4 |
5 | const media = createMediaListener({
6 | big: "(min-width : 1000px)",
7 | tiny: "(max-width: 600px)"
8 | });
9 |
10 | class App extends React.Component {
11 | state = {
12 | media: media.getState()
13 | };
14 |
15 | componentDidMount() {
16 | media.listen(media => this.setState({ media }));
17 | }
18 |
19 | componentWillUnmount() {
20 | media.dispose();
21 | }
22 |
23 | render() {
24 | const { media } = this.state;
25 |
26 | return (
27 |
28 | {media.big ? (
29 |
30 | ) : media.tiny ? (
31 |
32 | ) : (
33 |
34 | )}
35 |
36 | );
37 | }
38 | }
39 |
40 | export default App;
41 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/images/earth.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/02-hocs-render-props/exercise/src/images/earth.jpg
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/images/galaxy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/02-hocs-render-props/exercise/src/images/galaxy.jpg
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/images/trees.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/02-hocs-render-props/exercise/src/images/trees.jpg
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.start";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/lib/createMediaListener.js:
--------------------------------------------------------------------------------
1 | /*
2 | const listener = createMediaListener({
3 | mobile: "(max-width: 767px)",
4 | small: "(max-width: 568px), (max-height: 400px)"
5 | })
6 |
7 | listener.listen((state) => {})
8 | listener.getState()
9 | listenter.dispose()
10 | */
11 |
12 | export default media => {
13 | let transientListener = null;
14 |
15 | const mediaKeys = Object.keys(media);
16 |
17 | const queryLists = mediaKeys.reduce((queryLists, key) => {
18 | queryLists[key] = window.matchMedia(media[key]);
19 | return queryLists;
20 | }, {});
21 |
22 | const mediaState = mediaKeys.reduce((state, key) => {
23 | state[key] = queryLists[key].matches;
24 | return state;
25 | }, {});
26 |
27 | const notify = () => {
28 | if (transientListener != null) transientListener(mediaState);
29 | };
30 |
31 | const mutateMediaState = (key, val) => {
32 | mediaState[key] = val;
33 | notify();
34 | };
35 |
36 | const listeners = mediaKeys.reduce((listeners, key) => {
37 | listeners[key] = event => {
38 | mutateMediaState(key, event.matches);
39 | };
40 | return listeners;
41 | }, {});
42 |
43 | const listen = listener => {
44 | transientListener = listener;
45 | mediaKeys.forEach(key => {
46 | queryLists[key].addListener(listeners[key]);
47 | });
48 | };
49 |
50 | const dispose = () => {
51 | transientListener = null;
52 | mediaKeys.forEach(key => {
53 | queryLists[key].removeListener(listeners[key]);
54 | });
55 | };
56 |
57 | const getState = () => mediaState;
58 |
59 | return { listen, dispose, getState };
60 | };
61 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/lib/images/earth.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/02-hocs-render-props/exercise/src/lib/images/earth.jpg
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/lib/images/galaxy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/02-hocs-render-props/exercise/src/lib/images/galaxy.jpg
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/lib/images/trees.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/02-hocs-render-props/exercise/src/lib/images/trees.jpg
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #000;
3 | }
4 |
5 | .fade-enter {
6 | opacity: 0.01;
7 | }
8 |
9 | .fade-enter.fade-enter-active {
10 | opacity: 1;
11 | transition: opacity 200ms ease-in;
12 | }
13 |
14 | .fade-exit {
15 | opacity: 1;
16 | }
17 |
18 | .fade-exit.fade-exit-active {
19 | opacity: 0.01;
20 | transition: opacity 200ms ease-in;
21 | }
22 |
--------------------------------------------------------------------------------
/02-hocs-render-props/exercise/src/lib/screens.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import galaxy from "./images/galaxy.jpg";
3 | import earth from "./images/earth.jpg";
4 | import trees from "./images/trees.jpg";
5 |
6 | const makeBackgroundComponent = background => props => (
7 |
19 | );
20 |
21 | export const Galaxy = makeBackgroundComponent(galaxy);
22 | export const Earth = makeBackgroundComponent(earth);
23 | export const Trees = makeBackgroundComponent(trees);
24 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "02",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.5.0",
7 | "react-dom": "^16.5.0",
8 | "react-google-maps": "^9.4.5"
9 | },
10 | "devDependencies": {
11 | "react-scripts": "1.0.10"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/02-hocs-render-props/lecture/public/favicon.ico
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/src/App.final.hoc.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import LoadingDots from "./lib/LoadingDots";
3 | import Map from "./lib/Map";
4 |
5 | let withGeo = Comp =>
6 | class GeoPosition extends React.Component {
7 | state = {
8 | coords: null,
9 | error: null
10 | };
11 |
12 | componentDidMount() {
13 | this.geoId = navigator.geolocation.watchPosition(
14 | position => {
15 | this.setState({
16 | coords: {
17 | lat: position.coords.latitude,
18 | lng: position.coords.longitude
19 | }
20 | });
21 | },
22 | error => {
23 | this.setState({ error });
24 | }
25 | );
26 | }
27 |
28 | componentWillUnmount() {
29 | navigator.geolocation.clearWatch(this.geoId);
30 | }
31 |
32 | render() {
33 | return ;
34 | }
35 | };
36 |
37 | class App extends React.Component {
38 | render() {
39 | return (
40 |
41 | {this.props.error ? (
42 |
Error: {this.props.error.message}
43 | ) : this.props.coords ? (
44 |
49 | ) : (
50 |
51 | )}
52 |
53 | );
54 | }
55 | }
56 |
57 | export default withGeo(App);
58 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/src/App.final.render-props.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import LoadingDots from "./lib/LoadingDots";
3 | import Map from "./lib/Map";
4 | import getAddressFromCoords from "./lib/getAddressFromCoords";
5 |
6 | class GeoPosition extends React.Component {
7 | state = {
8 | coords: null,
9 | error: null
10 | };
11 |
12 | componentDidMount() {
13 | this.geoId = navigator.geolocation.watchPosition(
14 | position => {
15 | this.setState({
16 | coords: {
17 | lat: position.coords.latitude,
18 | lng: position.coords.longitude
19 | }
20 | });
21 | },
22 | error => {
23 | this.setState({ error });
24 | }
25 | );
26 | }
27 |
28 | componentWillUnmount() {
29 | navigator.geolocation.clearWatch(this.geoId);
30 | }
31 |
32 | render() {
33 | return this.props.children(this.state);
34 | }
35 | }
36 |
37 | class Address extends React.Component {
38 | state = {
39 | address: null
40 | };
41 |
42 | async componentDidMount() {
43 | const { coords: { lat: latitude, lng: longitude } } = this.props;
44 | this.setState({
45 | address: await getAddressFromCoords(latitude, longitude)
46 | });
47 | }
48 |
49 | render() {
50 | return this.props.children(this.state.address);
51 | }
52 | }
53 |
54 | class App extends React.Component {
55 | render() {
56 | return (
57 |
58 | {state => (
59 |
60 | {state.error ? (
61 |
Error: {state.error.message}
62 | ) : state.coords ? (
63 |
64 | {address => (
65 |
70 | )}
71 |
72 | ) : (
73 |
74 | )}
75 |
76 | )}
77 |
78 | );
79 | }
80 | }
81 |
82 | export default App;
83 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/src/App.start.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import LoadingDots from "./lib/LoadingDots";
3 | import Map from "./lib/Map";
4 | import getAddressFromCoords from "./lib/getAddressFromCoords";
5 |
6 | class App extends React.Component {
7 | state = {
8 | coords: null,
9 | error: null
10 | };
11 |
12 | componentDidMount() {
13 | this.geoId = navigator.geolocation.watchPosition(
14 | position => {
15 | this.setState({
16 | coords: {
17 | lat: position.coords.latitude,
18 | lng: position.coords.longitude
19 | }
20 | });
21 | },
22 | error => {
23 | this.setState({ error });
24 | }
25 | );
26 | }
27 |
28 | componentWillUnmount() {
29 | navigator.geolocation.clearWatch(this.geoId);
30 | }
31 |
32 | render() {
33 | return (
34 |
35 | {this.state.error ? (
36 |
Error: {this.state.error.message}
37 | ) : this.state.coords ? (
38 |
43 | ) : (
44 |
45 | )}
46 |
47 | );
48 | }
49 | }
50 |
51 | export default App;
52 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 |
5 | import App from "./App.start";
6 |
7 | ReactDOM.render( , document.getElementById("root"));
8 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/src/lib/LoadingDots.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | class LoadingDots extends React.Component {
4 | static defaultProps = {
5 | interval: 300,
6 | dots: 3
7 | };
8 |
9 | state = { frame: 1 };
10 |
11 | componentDidMount() {
12 | this.interval = setInterval(() => {
13 | this.setState({
14 | frame: this.state.frame + 1
15 | });
16 | }, this.props.interval);
17 | }
18 |
19 | componentWillUnmount() {
20 | clearInterval(this.interval);
21 | }
22 |
23 | render() {
24 | let dots = this.state.frame % (this.props.dots + 1);
25 | let text = "";
26 | while (dots > 0) {
27 | text += ".";
28 | dots--;
29 | }
30 | return {text} ;
31 | }
32 | }
33 |
34 | export default LoadingDots;
35 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/src/lib/Map.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import {
4 | withGoogleMap,
5 | GoogleMap,
6 | Marker,
7 | InfoWindow
8 | } from "react-google-maps";
9 |
10 | const loadMaps = cb => {
11 | // google maps script loading garbage
12 | const KEY = "AIzaSyDFwu1MmuOatqW-283LSCbsxqHcp89ouiw";
13 | const URL = `https://maps.googleapis.com/maps/api/js?key=${KEY}&callback=_mapsLoaded`;
14 | window._mapsLoaded = cb;
15 | const script = document.createElement("script");
16 | script.src = URL;
17 | document.body.appendChild(script);
18 | };
19 |
20 | const InnerMap = withGoogleMap(({ lat, lng, info }) => (
21 |
22 |
23 | {info && (
24 |
25 | {info}
26 |
27 | )}
28 |
29 |
30 | ));
31 |
32 | class Map extends React.Component {
33 | componentWillMount() {
34 | if (!window.google) {
35 | loadMaps(() => {
36 | this.forceUpdate();
37 | });
38 | }
39 | }
40 |
41 | render() {
42 | const { lat, lng, info } = this.props;
43 | return window.google ? (
44 | }
46 | mapElement={
}
47 | lat={lat}
48 | lng={lng}
49 | info={info}
50 | />
51 | ) : null;
52 | }
53 | }
54 |
55 | export default Map;
56 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/src/lib/getAddressFromCoords.js:
--------------------------------------------------------------------------------
1 | const GoogleMapsAPI = "https://maps.googleapis.com/maps/api";
2 |
3 | const getAddressFromCoords = (latitude, longitude) => {
4 | const url = `${GoogleMapsAPI}/geocode/json?latlng=${latitude},${longitude}`;
5 |
6 | return fetch(url)
7 | .then(res => res.json())
8 | .then(json => {
9 | return json.results[0].formatted_address;
10 | });
11 | };
12 |
13 | export default getAddressFromCoords;
14 |
--------------------------------------------------------------------------------
/02-hocs-render-props/lecture/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | background: rgb(229, 227, 223);
4 | }
5 |
6 |
7 | .container,
8 | .map {
9 | width: 100vw;
10 | height: 100vh;
11 | }
12 |
13 | .loading-dots {
14 | font-size: 200px;
15 | display: block;
16 | text-align: center;
17 | color: white;
18 | }
19 |
--------------------------------------------------------------------------------
/03-clone-element/exercise/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/03-clone-element/exercise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "03",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.8.1",
7 | "react-dom": "^16.8.1",
8 | "react-icons": "2"
9 | },
10 | "devDependencies": {
11 | "react-scripts": "1.0.10"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/03-clone-element/exercise/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/03-clone-element/exercise/public/favicon.ico
--------------------------------------------------------------------------------
/03-clone-element/exercise/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/03-clone-element/exercise/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/03-clone-element/exercise/src/App.solution.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import FaPlay from "react-icons/lib/fa/play";
3 | import FaPause from "react-icons/lib/fa/pause";
4 | import FaForward from "react-icons/lib/fa/forward";
5 | import FaBackward from "react-icons/lib/fa/backward";
6 |
7 | function RadioGroup({ defaultValue, legend, children }) {
8 | const [value, setValue] = useState(defaultValue);
9 | const clones = React.Children.map(children, child => {
10 | return React.cloneElement(child, {
11 | isActive: child.props.value === value,
12 | onSelect: () => setValue(child.props.value)
13 | });
14 | });
15 | return (
16 |
17 | {legend}
18 | {clones}
19 |
20 | );
21 | }
22 |
23 | function RadioButton({ onSelect, isActive, children }) {
24 | const className = "radio-button " + (isActive ? "active" : "");
25 | return (
26 |
27 | {children}
28 |
29 | );
30 | }
31 |
32 | function App() {
33 | return (
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | );
51 | }
52 |
53 | export default App;
54 |
--------------------------------------------------------------------------------
/03-clone-element/exercise/src/App.start.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import FaPlay from "react-icons/lib/fa/play";
3 | import FaPause from "react-icons/lib/fa/pause";
4 | import FaForward from "react-icons/lib/fa/forward";
5 | import FaBackward from "react-icons/lib/fa/backward";
6 |
7 | function RadioGroup({ defaultValue, legend, children }) {
8 | return (
9 |
10 | {legend}
11 | {children}
12 |
13 | );
14 | }
15 |
16 | function RadioButton({ children }) {
17 | const isActive = false; // <-- should come from somewhere
18 | const className = "radio-button " + (isActive ? "active" : "");
19 | return {children} ;
20 | }
21 |
22 | function App() {
23 | return (
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | );
41 | }
42 |
43 | export default App;
44 |
--------------------------------------------------------------------------------
/03-clone-element/exercise/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.start";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/03-clone-element/exercise/src/lib/noise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/03-clone-element/exercise/src/lib/noise.png
--------------------------------------------------------------------------------
/03-clone-element/lecture/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/03-clone-element/lecture/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "03",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.5.0",
7 | "react-dom": "^16.5.0",
8 | "react-icons": "^2.2.5"
9 | },
10 | "devDependencies": {
11 | "react-scripts": "1.0.10"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/03-clone-element/lecture/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/03-clone-element/lecture/public/favicon.ico
--------------------------------------------------------------------------------
/03-clone-element/lecture/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/03-clone-element/lecture/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/03-clone-element/lecture/src/App.cloneElement.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import FaAutomobile from "react-icons/lib/fa/automobile";
3 | import FaBed from "react-icons/lib/fa/bed";
4 | import FaPlane from "react-icons/lib/fa/plane";
5 | import FaSpaceShuttle from "react-icons/lib/fa/space-shuttle";
6 | import * as text from "./lib/text";
7 |
8 | class Tabs extends Component {
9 | state = {
10 | activeIndex: 0
11 | };
12 |
13 | selectTabIndex = activeIndex => {
14 | this.setState({ activeIndex });
15 | };
16 |
17 | render() {
18 | const children = React.Children.map(this.props.children, child => {
19 | return React.cloneElement(child, {
20 | activeIndex: this.state.activeIndex,
21 | onSelectTab: this.selectTabIndex
22 | });
23 | });
24 | return {children}
;
25 | }
26 | }
27 |
28 | class TabList extends Component {
29 | render() {
30 | const { activeIndex } = this.props;
31 | const children = React.Children.map(this.props.children, (child, index) => {
32 | return React.cloneElement(child, {
33 | isActive: index === activeIndex,
34 | onSelect: () => this.props.onSelectTab(index)
35 | });
36 | });
37 | return {children}
;
38 | }
39 | }
40 |
41 | class Tab extends Component {
42 | render() {
43 | const { isActive, isDisabled, onSelect } = this.props;
44 | return (
45 |
51 | {this.props.children}
52 |
53 | );
54 | }
55 | }
56 |
57 | class TabPanels extends Component {
58 | render() {
59 | const { activeIndex, children } = this.props;
60 | return {children[activeIndex]}
;
61 | }
62 | }
63 |
64 | class TabPanel extends Component {
65 | render() {
66 | return this.props.children;
67 | }
68 | }
69 |
70 | class DataTabs extends Component {
71 | render() {
72 | const { data } = this.props;
73 | return (
74 |
75 |
76 | {data.map((tab, index) => {tab.label} )}
77 |
78 |
79 | {data.map((tab, index) => (
80 | {tab.content}
81 | ))}
82 |
83 |
84 | );
85 | }
86 | }
87 |
88 | class App extends Component {
89 | render() {
90 | return (
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | {text.cars}
109 | {text.hotels}
110 | {text.flights}
111 | {text.space}
112 |
113 |
114 |
115 | );
116 | }
117 | }
118 |
119 | export default App;
120 |
--------------------------------------------------------------------------------
/03-clone-element/lecture/src/App.context.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | createContext
4 | } from "react";
5 | import FaAutomobile from "react-icons/lib/fa/automobile";
6 | import FaBed from "react-icons/lib/fa/bed";
7 | import FaPlane from "react-icons/lib/fa/plane";
8 | import FaSpaceShuttle from "react-icons/lib/fa/space-shuttle";
9 | import * as text from "./lib/text";
10 |
11 | let TabsContext = createContext();
12 |
13 | class Tabs extends Component {
14 | state = {
15 | activeIndex: 0,
16 | selectTabIndex: activeIndex => {
17 | this.setState({ activeIndex });
18 | }
19 | };
20 |
21 | render() {
22 | return (
23 |
24 |
25 | {this.props.children}
26 |
27 |
28 | );
29 | }
30 | }
31 |
32 | let TabList = props => {
33 | return (
34 |
35 | {context => {
36 | let children = React.Children.map(
37 | props.children,
38 | (child, index) => {
39 | return React.cloneElement(child, {
40 | isActive: index === context.activeIndex,
41 | onActivate: () =>
42 | context.selectTabIndex(index)
43 | });
44 | }
45 | );
46 | return {children}
;
47 | }}
48 |
49 | );
50 | };
51 |
52 | let Tab = props => {
53 | const isDisabled = props.isDisabled;
54 | const isActive = props.isActive;
55 | return (
56 |
66 | {props.children}
67 |
68 | );
69 | };
70 |
71 | let TabPanels = props => {
72 | return (
73 |
74 | {context => (
75 |
76 | {props.children[context.activeIndex]}
77 |
78 | )}
79 |
80 | );
81 | };
82 |
83 | let TabPanel = props => props.children;
84 |
85 | class App extends Component {
86 | render() {
87 | return (
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | {text.cars}
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | {text.cars}
124 | {text.hotels}
125 | {text.flights}
126 | {text.space}
127 |
128 |
129 |
130 | {text.flights}
131 | {text.space}
132 |
133 |
134 |
135 | );
136 | }
137 | }
138 |
139 | export default App;
140 |
--------------------------------------------------------------------------------
/03-clone-element/lecture/src/App.start.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import FaAutomobile from "react-icons/lib/fa/automobile";
3 | import FaBed from "react-icons/lib/fa/bed";
4 | import FaPlane from "react-icons/lib/fa/plane";
5 | import FaSpaceShuttle from "react-icons/lib/fa/space-shuttle";
6 | import * as text from "./lib/text";
7 |
8 | class Tabs extends Component {
9 | state = {
10 | activeIndex: 0
11 | };
12 |
13 | selectTabIndex = activeIndex => {
14 | this.setState({ activeIndex });
15 | };
16 |
17 | render() {
18 | const { data } = this.props;
19 | return (
20 |
21 |
22 | {data.map((tab, index) => {
23 | const isActive = index === this.state.activeIndex;
24 | return (
25 |
this.selectTabIndex(index)}
29 | >
30 | {tab.label}
31 |
32 | );
33 | })}
34 |
35 |
{data[this.state.activeIndex].content}
36 |
37 | );
38 | }
39 | }
40 |
41 | class App extends Component {
42 | render() {
43 | const tabData = [
44 | {
45 | label: ,
46 | content: text.cars
47 | },
48 | {
49 | label: ,
50 | content: text.hotels
51 | },
52 | {
53 | label: ,
54 | content: text.flights
55 | },
56 | {
57 | label: ,
58 | content: text.space
59 | }
60 | ];
61 | return (
62 |
63 |
64 |
65 | );
66 | }
67 | }
68 |
69 | export default App;
70 |
--------------------------------------------------------------------------------
/03-clone-element/lecture/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.start";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/03-clone-element/lecture/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
4 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
5 | }
6 |
7 | h2 {
8 | text-transform: uppercase;
9 | }
10 |
11 | .App {
12 | padding-top: 55px;
13 | color: hsl(240, 5%, 20%);
14 | height: 100vh;
15 | transition: background-color 300ms ease;
16 | background-image: radial-gradient(
17 | ellipse at center,
18 | hsla(0, 100%, 100%, 0.5) 0%,
19 | hsla(0, 100%, 100%, 0) 100%
20 | );
21 | background-color: hsl(199, 94%, 67%);
22 | }
23 |
24 | .Tabs {
25 | width: 500px;
26 | margin: auto;
27 | background: white;
28 | box-shadow: 0px 10px 40px hsla(0, 0%, 0%, 0.25);
29 | }
30 |
31 | .tabs {
32 | display: flex;
33 | border-bottom: solid 2px rgb(231, 236, 238);
34 | }
35 |
36 | .tab {
37 | flex: 1;
38 | padding: 10px;
39 | background: white;
40 | text-align: center;
41 | color: hsl(240, 5%, 60%);
42 | font-size: 200%;
43 | cursor: pointer;
44 | }
45 |
46 | .tab:hover {
47 | color: hsl(240, 5%, 70%);
48 | }
49 |
50 | .tab:active {
51 | transform: scale(0.95);
52 | color: hsl(199, 94%, 67%);
53 | }
54 |
55 | .tab.active {
56 | color: hsl(199, 94%, 67%);
57 | }
58 |
59 | .tab.disabled {
60 | opacity: 0.25;
61 | }
62 |
63 | .panels {
64 | background: rgb(244, 249, 252);
65 | min-height: 300px;
66 | padding: 20px;
67 | }
68 |
--------------------------------------------------------------------------------
/03-clone-element/lecture/src/lib/text.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const cars = (
4 |
5 |
Car rentals
6 |
We offer the finest selection of cars:
7 |
8 | Sedans
9 | Donks
10 | Hoopdies
11 | Ghetto Sleds
12 | Trucks
13 | Wild Wheels
14 |
15 |
16 | );
17 |
18 | export const hotels = (
19 |
20 |
Hotels
21 |
22 | The best hotels at the best price. Whether you're looking for a romantic
23 | get-away or just need a place to crash that offers free breakfast and a
24 | hot tub, we have the best hotels around.
25 |
26 |
27 | );
28 |
29 | export const flights = (
30 |
31 |
Flights
32 |
33 | Cheap fare and great flights. We have the most non-stop from major cities
34 | across the world than anybody else.
35 |
36 |
37 | );
38 |
39 | export const space = (
40 |
41 |
Space
42 |
43 | Sometimes when I watch shows like Passengers, and they're experiencing
44 | intergallactic travel, slingshotting around a star and gazing at it out
45 | the window, or standing outside the ship staring into the universe, I get
46 | actually angry that I won't experience it. Time to find a religion that
47 | believes we can create worlds and travel from one to the other.
48 |
49 |
50 | );
51 |
--------------------------------------------------------------------------------
/04-context/exercise/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/04-context/exercise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "03",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.8.1",
7 | "react-dom": "^16.8.1",
8 | "react-icons": "2"
9 | },
10 | "devDependencies": {
11 | "react-scripts": "1.0.10"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/04-context/exercise/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/04-context/exercise/public/favicon.ico
--------------------------------------------------------------------------------
/04-context/exercise/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/04-context/exercise/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/04-context/exercise/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.solution";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/04-context/exercise/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
3 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
4 | background: radial-gradient(ellipse at center, #fff 0%, #ddd 100%);
5 | height: 100vh;
6 | }
7 |
8 | .exercise {
9 | padding-top: 40px;
10 | width: 500px;
11 | margin: auto;
12 | }
13 |
14 | .audio-player {
15 | display: block;
16 | padding: 10px;
17 | margin: 40px 0;
18 | }
19 |
20 | .progress {
21 | width: 100%;
22 | border: 1px solid #ccc;
23 | background: #fff;
24 | margin: 0.5em 0;
25 | }
26 |
27 | .progress-bar {
28 | height: 1em;
29 | background: hsl(200, 50%, 50%);
30 | }
31 |
32 | .icon-button {
33 | background: none;
34 | border: none;
35 | padding: none;
36 | display: inline-block;
37 | font: inherit;
38 | color: #666;
39 | line-height: 0;
40 | padding: 0;
41 | vertical-align: bottom;
42 | }
43 |
44 | .icon-button:active {
45 | color: #333;
46 | outline: none;
47 | }
48 |
49 | .icon-button:disabled {
50 | color: #ccc;
51 | }
52 |
--------------------------------------------------------------------------------
/04-context/exercise/src/lib/mariobros.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/04-context/exercise/src/lib/mariobros.mp3
--------------------------------------------------------------------------------
/04-context/exercise/src/lib/podcast.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/04-context/exercise/src/lib/podcast.mp4
--------------------------------------------------------------------------------
/04-context/lecture/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/04-context/lecture/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "03",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.8.1",
7 | "react-dom": "^16.8.1",
8 | "react-icons": "^2.2.5"
9 | },
10 | "devDependencies": {
11 | "react-scripts": "1.0.10"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/04-context/lecture/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/04-context/lecture/public/favicon.ico
--------------------------------------------------------------------------------
/04-context/lecture/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/04-context/lecture/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/04-context/lecture/src/App.final.js:
--------------------------------------------------------------------------------
1 | import React, { createContext, useState, useMemo, useContext } from "react";
2 | import FaAutomobile from "react-icons/lib/fa/automobile";
3 | import FaBed from "react-icons/lib/fa/bed";
4 | import FaPlane from "react-icons/lib/fa/plane";
5 | import FaSpaceShuttle from "react-icons/lib/fa/space-shuttle";
6 | import * as text from "./lib/text";
7 |
8 | const TabsContext = createContext();
9 |
10 | function Tabs({ children }) {
11 | const [activeIndex, setActiveIndex] = useState(0);
12 | const context = useMemo(() => {
13 | return {
14 | activeIndex,
15 | onSelectTab: setActiveIndex
16 | };
17 | }, [activeIndex]);
18 |
19 | return (
20 |
21 | {children}
22 |
23 | );
24 | }
25 |
26 | function TabList({ children }) {
27 | const { activeIndex, onSelectTab } = useContext(TabsContext);
28 | const clones = React.Children.map(children, (child, index) => {
29 | return React.cloneElement(child, {
30 | isActive: index === activeIndex,
31 | onSelect: () => onSelectTab(index)
32 | });
33 | });
34 | return {clones}
;
35 | }
36 |
37 | function Tab({ isActive, isDisabled, onSelect, children }) {
38 | return (
39 |
43 | {children}
44 |
45 | );
46 | }
47 |
48 | function TabPanels({ children }) {
49 | const { activeIndex } = useContext(TabsContext);
50 | return {children[activeIndex]}
;
51 | }
52 |
53 | function TabPanel({ children }) {
54 | return children;
55 | }
56 |
57 | function App() {
58 | return (
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | {text.cars}
77 | {text.hotels}
78 | {text.flights}
79 | {text.space}
80 |
81 |
82 |
83 | );
84 | }
85 |
86 | export default App;
87 |
--------------------------------------------------------------------------------
/04-context/lecture/src/App.start.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import FaAutomobile from "react-icons/lib/fa/automobile";
3 | import FaBed from "react-icons/lib/fa/bed";
4 | import FaPlane from "react-icons/lib/fa/plane";
5 | import FaSpaceShuttle from "react-icons/lib/fa/space-shuttle";
6 | import * as text from "./lib/text";
7 |
8 | function Tabs({ children }) {
9 | const [activeIndex, setActiveIndex] = useState(0);
10 | const clones = React.Children.map(children, child => {
11 | return React.cloneElement(child, {
12 | activeIndex,
13 | onSelectTab: setActiveIndex
14 | });
15 | });
16 | return {clones}
;
17 | }
18 |
19 | function TabList({ activeIndex, onSelectTab, children }) {
20 | const clones = React.Children.map(children, (child, index) => {
21 | return React.cloneElement(child, {
22 | isActive: index === activeIndex,
23 | onSelect: () => onSelectTab(index)
24 | });
25 | });
26 | return {clones}
;
27 | }
28 |
29 | function Tab({ isActive, isDisabled, onSelect, children }) {
30 | return (
31 |
35 | {children}
36 |
37 | );
38 | }
39 |
40 | function TabPanels({ activeIndex, children }) {
41 | return {children[activeIndex]}
;
42 | }
43 |
44 | function TabPanel({ children }) {
45 | return children;
46 | }
47 |
48 | function App() {
49 | return (
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | {text.cars}
68 | {text.hotels}
69 | {text.flights}
70 | {text.space}
71 |
72 |
73 |
74 | );
75 | }
76 |
77 | export default App;
78 |
--------------------------------------------------------------------------------
/04-context/lecture/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.start";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/04-context/lecture/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
4 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
5 | }
6 |
7 | h2 {
8 | text-transform: uppercase;
9 | }
10 |
11 | .App {
12 | padding-top: 55px;
13 | color: hsl(240, 5%, 20%);
14 | height: 100vh;
15 | transition: background-color 300ms ease;
16 | background-image: radial-gradient(
17 | ellipse at center,
18 | hsla(0, 100%, 100%, 0.5) 0%,
19 | hsla(0, 100%, 100%, 0) 100%
20 | );
21 | background-color: hsl(199, 94%, 67%);
22 | }
23 |
24 | .Tabs {
25 | width: 500px;
26 | margin: auto;
27 | background: white;
28 | box-shadow: 0px 10px 40px hsla(0, 0%, 0%, 0.25);
29 | }
30 |
31 | .tabs {
32 | display: flex;
33 | border-bottom: solid 2px rgb(231, 236, 238);
34 | }
35 |
36 | .tab {
37 | flex: 1;
38 | padding: 10px;
39 | background: white;
40 | text-align: center;
41 | color: hsl(240, 5%, 60%);
42 | font-size: 200%;
43 | cursor: pointer;
44 | }
45 |
46 | .tab:hover {
47 | color: hsl(240, 5%, 70%);
48 | }
49 |
50 | .tab:active {
51 | transform: scale(0.95);
52 | color: hsl(199, 94%, 67%);
53 | }
54 |
55 | .tab.active {
56 | color: hsl(199, 94%, 67%);
57 | }
58 |
59 | .tab.disabled {
60 | opacity: 0.25;
61 | }
62 |
63 | .panels {
64 | background: rgb(244, 249, 252);
65 | min-height: 300px;
66 | padding: 20px;
67 | }
68 |
--------------------------------------------------------------------------------
/04-context/lecture/src/lib/text.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const cars = (
4 |
5 |
Car rentals
6 |
We offer the finest selection of cars:
7 |
8 | Sedans
9 | Donks
10 | Hoopdies
11 | Ghetto Sleds
12 | Trucks
13 | Wild Wheels
14 |
15 |
16 | );
17 |
18 | export const hotels = (
19 |
20 |
Hotels
21 |
22 | The best hotels at the best price. Whether you're looking for a romantic
23 | get-away or just need a place to crash that offers free breakfast and a
24 | hot tub, we have the best hotels around.
25 |
26 |
27 | );
28 |
29 | export const flights = (
30 |
31 |
Flights
32 |
33 | Cheap fare and great flights. We have the most non-stop from major cities
34 | across the world than anybody else.
35 |
36 |
37 | );
38 |
39 | export const space = (
40 |
41 |
Space
42 |
43 | Sometimes when I watch shows like Passengers, and they're experiencing
44 | intergallactic travel, slingshotting around a star and gazing at it out
45 | the window, or standing outside the ship staring into the universe, I get
46 | actually angry that I won't experience it. Time to find a religion that
47 | believes we can create worlds and travel from one to the other.
48 |
49 |
50 | );
51 |
--------------------------------------------------------------------------------
/05-portals/exercise/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/05-portals/exercise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "04",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reach/rect": "^0.1.1",
7 | "react": "^16.5.0",
8 | "react-dom": "^16.5.0"
9 | },
10 | "devDependencies": {
11 | "react-scripts": "1.0.10"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/05-portals/exercise/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/05-portals/exercise/public/favicon.ico
--------------------------------------------------------------------------------
/05-portals/exercise/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/05-portals/exercise/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/05-portals/exercise/src/.App.js.swo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/05-portals/exercise/src/.App.js.swo
--------------------------------------------------------------------------------
/05-portals/exercise/src/App.solution.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createPortal } from "react-dom";
3 | import PropTypes from "prop-types";
4 | import Rect from "@reach/rect";
5 |
6 | class Portal extends React.Component {
7 | state = {
8 | mounted: false
9 | };
10 |
11 | componentDidMount() {
12 | this.node = document.createElement("div");
13 | document.body.appendChild(this.node);
14 | this.setState({ mounted: true });
15 | }
16 |
17 | componentWillUnmount() {
18 | document.body.removeChild(this.node);
19 | }
20 |
21 | render() {
22 | return this.state.mounted
23 | ? createPortal(this.props.children, this.node)
24 | : null;
25 | }
26 | }
27 |
28 | class Select extends React.Component {
29 | static propTypes = {
30 | onChange: PropTypes.func,
31 | value: PropTypes.any,
32 | defaultValue: PropTypes.any
33 | };
34 |
35 | state = {
36 | value: this.props.defaultValue,
37 | isOpen: false
38 | };
39 |
40 | handleToggle = () => {
41 | this.setState({
42 | isOpen: !this.state.isOpen
43 | });
44 | };
45 |
46 | isControlled() {
47 | return this.props.value != null;
48 | }
49 |
50 | render() {
51 | const { isOpen } = this.state;
52 | let label;
53 | const children = React.Children.map(this.props.children, child => {
54 | const { value } = this.isControlled() ? this.props : this.state;
55 | if (child.props.value === value) {
56 | label = child.props.children;
57 | }
58 |
59 | return React.cloneElement(child, {
60 | onSelect: () => {
61 | if (this.isControlled()) {
62 | this.props.onChange(child.props.value);
63 | } else {
64 | this.setState({ value: child.props.value });
65 | }
66 | }
67 | });
68 | });
69 |
70 | return (
71 |
72 | {({ rect, ref }) => (
73 |
74 |
75 | {label} ▾
76 |
77 | {isOpen && (
78 |
79 |
89 |
90 | )}
91 |
92 | )}
93 |
94 | );
95 | }
96 | }
97 |
98 | class Option extends React.Component {
99 | render() {
100 | return (
101 |
102 | {this.props.children}
103 |
104 | );
105 | }
106 | }
107 |
108 | class App extends React.Component {
109 | render() {
110 | return (
111 |
112 |
113 |
Portal Party
114 |
115 | Tikka Masala
116 | Tandoori Chicken
117 | Dosa
118 | Mint Chutney
119 |
120 |
121 |
122 | );
123 | }
124 | }
125 |
126 | export default App;
127 |
--------------------------------------------------------------------------------
/05-portals/exercise/src/App.start.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Have `Portal` create a new DOM element, append it to document.body and then
4 | render its children into a portal. We want to have portal create the new dom
5 | node when it mounts, and remove it when it unmounts.
6 |
7 | Tips:
8 |
9 | - in componentDidMount, create a new dom node and append it to the body
10 | - `document.createElement('div')`
11 | - `document.body.append(node)`
12 | - in componentWillUnmount, remove the node from the body with
13 | `document.body.removeChild(node)`
14 |
15 | Finally, the menu will be rendered out of DOM context so the styles will be all
16 | wrong, you'll need to provide a `style` prop with fixed position and left/top
17 | values. To help out, we've imported `Rect`. Go check the docs for Rect
18 | (https://ui.reach.tech/rect) and then use it to put the menu by the button.
19 |
20 | */
21 |
22 | import React from "react";
23 | import { createPortal } from "react-dom";
24 | import Rect from "@reach/rect";
25 |
26 | class Portal extends React.Component {
27 | render() {
28 | return this.props.children;
29 | }
30 | }
31 |
32 | class Select extends React.Component {
33 | state = {
34 | value: this.props.defaultValue,
35 | isOpen: false
36 | };
37 |
38 | handleToggle = () => {
39 | this.setState({
40 | isOpen: !this.state.isOpen
41 | });
42 | };
43 |
44 | render() {
45 | const { isOpen } = this.state;
46 | let label;
47 | const children = React.Children.map(this.props.children, child => {
48 | const { value } = this.state;
49 | if (child.props.value === value) {
50 | label = child.props.children;
51 | }
52 |
53 | return React.cloneElement(child, {
54 | onSelect: () => {
55 | this.setState({ value: child.props.value });
56 | }
57 | });
58 | });
59 |
60 | return (
61 |
62 |
63 | {label} ▾
64 |
65 | {isOpen && (
66 |
67 |
68 |
69 | )}
70 |
71 | );
72 | }
73 | }
74 |
75 | class Option extends React.Component {
76 | render() {
77 | return (
78 |
79 | {this.props.children}
80 |
81 | );
82 | }
83 | }
84 |
85 | class App extends React.Component {
86 | render() {
87 | return (
88 |
89 |
90 |
Portal Party
91 |
92 | Tikka Masala
93 | Tandoori Chicken
94 | Dosa
95 | Mint Chutney
96 |
97 |
98 |
99 | );
100 | }
101 | }
102 |
103 | export default App;
104 |
--------------------------------------------------------------------------------
/05-portals/exercise/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.start";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/05-portals/exercise/src/lib/.App.js.swo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/05-portals/exercise/src/lib/.App.js.swo
--------------------------------------------------------------------------------
/05-portals/exercise/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: radial-gradient(
3 | ellipse at center,
4 | hsl(200, 80%, 90%) 0%,
5 | hsl(200, 80%, 80%) 100%
6 | );
7 | height: 100vh;
8 | color: hsl(200, 80%, 20%);
9 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
10 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
11 | }
12 |
13 | .app {
14 | margin: auto;
15 | display: flex;
16 | }
17 |
18 | .block {
19 | margin: 100px 50px;
20 | border-radius: 4px;
21 | flex: 1;
22 | background: hsla(0, 0%, 100%, 0.75);
23 | height: 400px;
24 | padding: 20px;
25 | }
26 |
27 | h2 {
28 | margin: 0 0 20px 0;
29 | text-transform: uppercase;
30 | }
31 |
32 | .select {
33 | display: inline-block;
34 | margin: 4px 0;
35 | position: relative;
36 | }
37 |
38 | .label {
39 | background: hsla(200, 0%, 100%, 0.95);
40 | border: 1px solid hsla(200, 0%, 0%, 0.2);
41 | font: inherit;
42 | padding: 4px 8px;
43 | cursor: default;
44 | }
45 |
46 | .arrow {
47 | float: right;
48 | padding-left: 8px;
49 | color: hsla(200, 0%, 0%, 0.5);
50 | }
51 |
52 | .options {
53 | outline: none;
54 | border: solid 1px hsl(200, 0%, 70%);
55 | overflow: hidden;
56 | border-radius: 4px;
57 | list-style: none;
58 | background: hsla(200, 0%, 95%, 0.95);
59 | box-shadow: 2px 2px 8px hsla(200, 0%, 0%, 0.25);
60 | width: 200px;
61 | padding: 0;
62 | margin: 0;
63 | margin-left: -20px;
64 | }
65 |
66 | .options.is-closed {
67 | display: none;
68 | }
69 |
70 | .option {
71 | padding: 4px 24px;
72 | cursor: default;
73 | position: relative;
74 | }
75 |
76 | .option.selected:before {
77 | content: "✔︎";
78 | position: absolute;
79 | left: 7px;
80 | top: 0.35em;
81 | font-size: 75%;
82 | }
83 |
84 | .option:hover {
85 | background: hsl(200, 80%, 50%);
86 | color: white;
87 | }
88 |
--------------------------------------------------------------------------------
/05-portals/lecture/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/05-portals/lecture/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "04",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.5.0",
7 | "react-dom": "^16.5.0"
8 | },
9 | "devDependencies": {
10 | "react-scripts": "1.0.10"
11 | },
12 | "scripts": {
13 | "start": "react-scripts start",
14 | "build": "react-scripts build",
15 | "test": "react-scripts test --env=jsdom",
16 | "eject": "react-scripts eject"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/05-portals/lecture/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/05-portals/lecture/public/favicon.ico
--------------------------------------------------------------------------------
/05-portals/lecture/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/05-portals/lecture/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/05-portals/lecture/src/App.final.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createPortal } from "react-dom";
3 |
4 | let portalNode = document.createElement("div");
5 | document.body.appendChild(portalNode);
6 |
7 | class Dialog extends React.Component {
8 | render() {
9 | return createPortal(
10 |
20 |
33 | {this.props.children}
34 |
35 |
,
36 | portalNode
37 | );
38 | }
39 | }
40 |
41 | let bustthisstuff = {
42 | height: "300px",
43 | overflow: "hidden",
44 | position: "absolute"
45 | };
46 |
47 | class App extends React.Component {
48 | state = {
49 | showDialog: false
50 | };
51 |
52 | render() {
53 | return (
54 |
55 |
Would you like a banana cream pie?!
56 |
{
58 | this.setState({ showDialog: true });
59 | }}
60 | >
61 | YES
62 |
63 | {this.state.showDialog && (
64 |
65 | Banana Cream Pie Delivery
66 |
86 |
87 | )}
88 |
89 | );
90 | }
91 | }
92 |
93 | export default App;
94 |
--------------------------------------------------------------------------------
/05-portals/lecture/src/App.start.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | class Dialog extends React.Component {
4 | render() {
5 | return (
6 |
16 |
29 | {this.props.children}
30 |
31 |
32 | );
33 | }
34 | }
35 |
36 | let bustthisstuff = {
37 | height: "300px",
38 | overflow: "hidden",
39 | position: "absolute"
40 | };
41 |
42 | class App extends React.Component {
43 | state = {
44 | showDialog: false
45 | };
46 |
47 | render() {
48 | return (
49 |
50 |
Would you like a banana cream pie?!
51 |
{
53 | this.setState({ showDialog: true });
54 | }}
55 | >
56 | YES
57 |
58 | {this.state.showDialog && (
59 |
60 | Banana Cream Pie Delivery
61 |
81 |
82 | )}
83 |
84 | );
85 | }
86 | }
87 |
88 | export default App;
89 |
--------------------------------------------------------------------------------
/05-portals/lecture/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.start";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/05-portals/lecture/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
3 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
4 | }
5 |
--------------------------------------------------------------------------------
/06-wai-aria/exercise/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/06-wai-aria/exercise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "03",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.5.0",
7 | "react-dom": "^16.5.0",
8 | "react-icons": "2"
9 | },
10 | "devDependencies": {
11 | "react-scripts": "1.0.10"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/06-wai-aria/exercise/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/06-wai-aria/exercise/public/favicon.ico
--------------------------------------------------------------------------------
/06-wai-aria/exercise/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/06-wai-aria/exercise/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/06-wai-aria/exercise/src/App.start.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Follow the WAI ARIA Radio Group example at:
4 | https://www.w3.org/TR/wai-aria-practices-1.1/examples/radio/radio-1/radio-1.html
5 |
6 | - Turn the span into a button to get keyboard and focus events
7 | - Use tabIndex to allow only the active button to be tabbable
8 | - Use left/right arrows to select the next/previous radio button
9 | - Tip: you can figure out the next value with React.Children.forEach(fn),
10 | or React.Children.toArray(children).reduce(fn)
11 | - Move the focus in cDU to the newly selected item
12 | - Tip: do it in RadioOption not RadioGroup
13 | - Tip: you'll need a ref
14 | - Add the aria attributes
15 | - radiogroup
16 | - radio
17 | - aria-checked
18 | - aria-label on the icons
19 |
20 | */
21 | import React, { Component } from "react";
22 | import FaPlay from "react-icons/lib/fa/play";
23 | import FaPause from "react-icons/lib/fa/pause";
24 | import FaForward from "react-icons/lib/fa/forward";
25 | import FaBackward from "react-icons/lib/fa/backward";
26 |
27 | class RadioGroup extends Component {
28 | state = {
29 | value: this.props.defaultValue
30 | };
31 |
32 | render() {
33 | const children = React.Children.map(this.props.children, child => {
34 | return React.cloneElement(child, {
35 | isActive: child.props.value === this.state.value,
36 | onSelect: () => this.setState({ value: child.props.value })
37 | });
38 | });
39 | return (
40 |
41 | {this.props.legend}
42 | {children}
43 |
44 | );
45 | }
46 | }
47 |
48 | class RadioButton extends Component {
49 | render() {
50 | const { isActive, onSelect } = this.props;
51 | const className = "radio-button " + (isActive ? "active" : "");
52 | return (
53 |
54 | {this.props.children}
55 |
56 | );
57 | }
58 | }
59 |
60 | class App extends Component {
61 | render() {
62 | return (
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | );
80 | }
81 | }
82 |
83 | export default App;
84 |
--------------------------------------------------------------------------------
/06-wai-aria/exercise/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.start";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/06-wai-aria/exercise/src/lib/noise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/06-wai-aria/exercise/src/lib/noise.png
--------------------------------------------------------------------------------
/06-wai-aria/lecture/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/06-wai-aria/lecture/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "05",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reach/rect": "^0.1.1",
7 | "react": "^16.5.0",
8 | "react-dom": "^16.5.0",
9 | "react-google-maps": "^7.2.0"
10 | },
11 | "devDependencies": {
12 | "react-scripts": "1.0.10"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test --env=jsdom",
18 | "eject": "react-scripts eject"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/06-wai-aria/lecture/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/06-wai-aria/lecture/public/favicon.ico
--------------------------------------------------------------------------------
/06-wai-aria/lecture/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/06-wai-aria/lecture/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/06-wai-aria/lecture/src/.App.js.swn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/06-wai-aria/lecture/src/.App.js.swn
--------------------------------------------------------------------------------
/06-wai-aria/lecture/src/.App.js.swo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/06-wai-aria/lecture/src/.App.js.swo
--------------------------------------------------------------------------------
/06-wai-aria/lecture/src/App.start.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import Rect from "@reach/rect";
4 |
5 | class Select extends React.Component {
6 | static propTypes = {
7 | onChange: PropTypes.func,
8 | value: PropTypes.any,
9 | defaultValue: PropTypes.any
10 | };
11 |
12 | state = {
13 | value: this.props.defaultValue,
14 | isOpen: false
15 | };
16 |
17 | handleToggle = () => {
18 | this.setState({
19 | isOpen: !this.state.isOpen
20 | });
21 | };
22 |
23 | isControlled() {
24 | return this.props.value != null;
25 | }
26 |
27 | render() {
28 | const { isOpen } = this.state;
29 | let label;
30 | const children = React.Children.map(this.props.children, child => {
31 | const { value } = this.isControlled() ? this.props : this.state;
32 | if (child.props.value === value) {
33 | label = child.props.children;
34 | }
35 |
36 | return React.cloneElement(child, {
37 | onSelect: () => {
38 | if (this.isControlled()) {
39 | this.props.onChange(child.props.value);
40 | } else {
41 | this.setState({ value: child.props.value });
42 | }
43 | }
44 | });
45 | });
46 |
47 | return (
48 |
49 | {({ rect, ref }) => (
50 |
51 |
52 | {label} ▾
53 |
54 | {isOpen && (
55 |
65 | )}
66 |
67 | )}
68 |
69 | );
70 | }
71 | }
72 |
73 | class Option extends React.Component {
74 | render() {
75 | return (
76 |
77 | {this.props.children}
78 |
79 | );
80 | }
81 | }
82 |
83 | class App extends React.Component {
84 | state = {
85 | selectValue: "dosa"
86 | };
87 |
88 | setToMintChutney = () => {
89 | this.setState({
90 | selectValue: "mint-chutney"
91 | });
92 | };
93 |
94 | render() {
95 | return (
96 |
97 |
98 |
WAI-ARIA
99 |
100 | Tikka Masala
101 | Tandoori Chicken
102 | Dosa
103 | Mint Chutney
104 |
105 |
106 |
107 | );
108 | }
109 | }
110 |
111 | export default App;
112 |
--------------------------------------------------------------------------------
/06-wai-aria/lecture/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./Disclosure";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/06-wai-aria/lecture/src/lib/.App.js.swn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/06-wai-aria/lecture/src/lib/.App.js.swn
--------------------------------------------------------------------------------
/06-wai-aria/lecture/src/lib/.App.js.swo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/06-wai-aria/lecture/src/lib/.App.js.swo
--------------------------------------------------------------------------------
/06-wai-aria/lecture/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | height: 100vh;
3 | color: hsl(200, 80%, 20%);
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
5 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
6 | }
7 |
8 | .app {
9 | margin: auto;
10 | display: flex;
11 | }
12 |
13 | .block {
14 | margin: 100px 50px;
15 | border-radius: 4px;
16 | flex: 1;
17 | background: hsla(0, 0%, 100%, 0.75);
18 | height: 400px;
19 | padding: 20px;
20 | }
21 |
22 | h2 {
23 | margin: 0 0 20px 0;
24 | text-transform: uppercase;
25 | }
26 |
27 | .select {
28 | display: inline-block;
29 | margin: 4px 0;
30 | }
31 |
32 | .label {
33 | background: hsla(200, 0%, 100%, 0.95);
34 | border: 1px solid hsla(200, 0%, 0%, 0.2);
35 | font: inherit;
36 | padding: 4px 8px;
37 | cursor: default;
38 | }
39 |
40 | .arrow {
41 | float: right;
42 | padding-left: 8px;
43 | color: hsla(200, 0%, 0%, 0.5);
44 | }
45 |
46 | .options {
47 | outline: none;
48 | border: solid 1px hsl(200, 0%, 70%);
49 | overflow: hidden;
50 | border-radius: 4px;
51 | list-style: none;
52 | background: hsla(200, 0%, 95%, 0.95);
53 | box-shadow: 2px 2px 8px hsla(200, 0%, 0%, 0.25);
54 | width: 200px;
55 | padding: 0;
56 | margin: 0;
57 | }
58 |
59 | .options.is-closed {
60 | display: none;
61 | }
62 |
63 | .option {
64 | padding: 4px 24px;
65 | cursor: default;
66 | position: relative;
67 | }
68 |
69 | .option.selected:before {
70 | content: "✔︎";
71 | position: absolute;
72 | left: 7px;
73 | top: 0.35em;
74 | font-size: 75%;
75 | }
76 |
77 | .option.pre-selection {
78 | background: hsl(200, 80%, 50%);
79 | color: white;
80 | }
81 |
82 | dl.faq button {
83 | margin: 0;
84 | padding: 0;
85 | margin-top: 1em;
86 | font-weight: bold;
87 | font-size: 110%;
88 | border: thin solid transparent;
89 | background-color: transparent;
90 | padding-left: 0.125em;
91 | }
92 |
93 | dl dd {
94 | margin: 0;
95 | padding: 0;
96 | margin-left: 1.5em;
97 | }
98 |
99 | dl.faq .desc {
100 | margin: 0;
101 | padding: 0.5em;
102 | font-size: 110%;
103 | background-color: #fffedb;
104 | }
105 |
106 | dl.faq button:hover,
107 | dl.faq button:focus {
108 | background-color: #eee;
109 | }
110 |
111 | dl.faq button:focus {
112 | border-color: #663300;
113 | }
114 |
115 | dl.faq button:hover {
116 | text-decoration: underline;
117 | }
118 |
119 | dl.faq button:active {
120 | background-color: #bbb;
121 | }
122 |
123 | dl.faq button[aria-expanded="false"]:before {
124 | content: "▸";
125 | padding-right: 0.35em;
126 | }
127 |
128 | dl.faq button[aria-expanded="true"]:before {
129 | content: "▾";
130 | padding-right: 0.35em;
131 | }
132 |
--------------------------------------------------------------------------------
/07-gsbu/exercise/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/07-gsbu/exercise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "01",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.5.0",
7 | "react-dom": "^16.5.0"
8 | },
9 | "devDependencies": {
10 | "react-scripts": "1.0.10"
11 | },
12 | "scripts": {
13 | "start": "react-scripts start",
14 | "build": "react-scripts build",
15 | "test": "react-scripts test --env=jsdom",
16 | "eject": "react-scripts eject"
17 | }
18 | }
--------------------------------------------------------------------------------
/07-gsbu/exercise/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/07-gsbu/exercise/public/favicon.ico
--------------------------------------------------------------------------------
/07-gsbu/exercise/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/07-gsbu/exercise/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/07-gsbu/exercise/src/App.solution.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import subscribeToMessages from "./lib/messages";
3 | import FadeIn from "./lib/FadeIn";
4 |
5 | class PinScrollToBottom extends Component {
6 | componentDidMount() {
7 | this.scroll();
8 | }
9 |
10 | componentDidUpdate(prevProps, prevState, atBottom) {
11 | if (atBottom) {
12 | this.scroll();
13 | }
14 | }
15 |
16 | scroll() {
17 | document.documentElement.scrollTop = document.documentElement.scrollHeight;
18 | }
19 |
20 | getSnapshotBeforeUpdate() {
21 | const { scrollHeight, scrollTop, clientHeight } = document.documentElement;
22 | const atBottom = scrollHeight < scrollTop + clientHeight + 20;
23 | return atBottom;
24 | }
25 |
26 | render() {
27 | return this.props.children;
28 | }
29 | }
30 |
31 | class App extends Component {
32 | state = {
33 | messages: []
34 | };
35 |
36 | componentDidMount() {
37 | subscribeToMessages(message => {
38 | this.setState({
39 | messages: this.state.messages.concat([message])
40 | });
41 | });
42 | }
43 |
44 | render() {
45 | const { messages } = this.state;
46 | return (
47 |
48 |
53 |
54 |
55 | {messages.map((message, index) => (
56 |
57 |
58 |
64 | {message.text}
65 |
66 |
67 | ))}
68 |
69 |
70 |
71 | );
72 | }
73 | }
74 |
75 | export default App;
76 |
--------------------------------------------------------------------------------
/07-gsbu/exercise/src/App.start.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | EXERCISE: Edit `PinScrollToBottom` to manage the window's scroll position.
4 | Whenever new messages come in, scroll to the bottom. However, if the user has
5 | scrolled up at all, *don't* scroll them done cause that's super annoying.
6 |
7 | Tips:
8 |
9 | - You can scroll down with:
10 | `document.documentElement.scrollTop = document.documentElement.scrollHeight`
11 | - Measure the scroll position in `getSnapshotBeforeUpdate`
12 | - You'll need these measurements:
13 | `let { scrollTop, scrollHeight, clientHeight} = document.documentElement`
14 |
15 | */
16 |
17 | import React, { Component } from "react";
18 | import subscribeToMessages from "./lib/messages";
19 | import FadeIn from "./lib/FadeIn";
20 |
21 | class PinScrollToBottom extends Component {
22 | render() {
23 | return this.props.children;
24 | }
25 | }
26 |
27 | class App extends Component {
28 | state = {
29 | messages: []
30 | };
31 |
32 | componentDidMount() {
33 | subscribeToMessages(message => {
34 | this.setState({
35 | messages: this.state.messages.concat([message])
36 | });
37 | });
38 | }
39 |
40 | render() {
41 | const { messages } = this.state;
42 | return (
43 |
44 |
49 |
50 |
51 | {messages.map((message, index) => (
52 |
53 |
54 |
60 | {message.text}
61 |
62 |
63 | ))}
64 |
65 |
66 |
67 | );
68 | }
69 | }
70 |
71 | export default App;
72 |
--------------------------------------------------------------------------------
/07-gsbu/exercise/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.start";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/07-gsbu/exercise/src/lib/FadeIn.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | class FadeIn extends Component {
4 | state = {
5 | mounted: false
6 | };
7 |
8 | componentDidMount() {
9 | setTimeout(() => {
10 | this.setState({ mounted: true });
11 | }, 0);
12 | }
13 |
14 | render() {
15 | const { className } = this.props.children.props;
16 | return React.cloneElement(this.props.children, {
17 | className: className + (this.state.mounted ? " fade-in" : "")
18 | });
19 | }
20 | }
21 |
22 | export default FadeIn;
23 |
--------------------------------------------------------------------------------
/07-gsbu/exercise/src/lib/avatars/EI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/07-gsbu/exercise/src/lib/avatars/EI.png
--------------------------------------------------------------------------------
/07-gsbu/exercise/src/lib/avatars/GC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/07-gsbu/exercise/src/lib/avatars/GC.png
--------------------------------------------------------------------------------
/07-gsbu/exercise/src/lib/avatars/MP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/07-gsbu/exercise/src/lib/avatars/MP.png
--------------------------------------------------------------------------------
/07-gsbu/exercise/src/lib/avatars/TG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/07-gsbu/exercise/src/lib/avatars/TG.png
--------------------------------------------------------------------------------
/07-gsbu/exercise/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
3 | }
4 |
5 | .app {
6 | width: 80vw;
7 | margin: auto;
8 | font-size: 3vh;
9 | }
10 |
11 | .messages {
12 | list-style: none;
13 | margin: 0;
14 | padding: 0;
15 | }
16 |
17 | .message {
18 | display: flex;
19 | align-items: top;
20 | margin: 2vh;
21 | opacity: 0;
22 | transition: opacity 650ms ease;
23 | }
24 |
25 | .message.fade-in {
26 | opacity: 1;
27 | }
28 |
29 | .avatar {
30 | width: 15vh;
31 | height: 15vh;
32 | background-size: cover;
33 | border-radius: 50%;
34 | margin: 2vh;
35 | }
36 |
37 | .text {
38 | flex: 1;
39 | padding-top: 1em;
40 | }
41 |
42 | .link {
43 | text-align: center;
44 | margin: 5vh;
45 | }
46 |
47 | .link a {
48 | color: #e94949;
49 | text-decoration: none;
50 | }
51 |
--------------------------------------------------------------------------------
/07-gsbu/lecture/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/07-gsbu/lecture/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "01",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reach/alert": "^0.1.1",
7 | "react": "^16.5.0",
8 | "react-dom": "^16.5.0"
9 | },
10 | "devDependencies": {
11 | "react-scripts": "1.0.10"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/07-gsbu/lecture/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/07-gsbu/lecture/public/favicon.ico
--------------------------------------------------------------------------------
/07-gsbu/lecture/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/07-gsbu/lecture/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/07-gsbu/lecture/src/App.final.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import Alert from "@reach/alert";
3 |
4 | let sleep = ms => new Promise(res => setTimeout(res, ms));
5 |
6 | let subscribeEmail = email =>
7 | new Promise(async resolve => {
8 | console.log("subscribing", email);
9 | await sleep(4000);
10 | console.log("subscribed!");
11 | resolve();
12 | });
13 |
14 | class App extends Component {
15 | state = {
16 | state: "idle" // "subscribing", "subscribed"
17 | };
18 |
19 | inputRef = React.createRef();
20 |
21 | contentRef = React.createRef();
22 |
23 | getSnapshotBeforeUpdate() {
24 | return document.activeElement !== this.contentRef.current;
25 | }
26 |
27 | componentDidUpdate(prevProps, prevState, userMovedFocus) {
28 | if (prevState.state === "idle" && this.state.state === "subscribing") {
29 | this.contentRef.current.focus();
30 | } else if (
31 | !userMovedFocus &&
32 | prevState.state === "subscribed" &&
33 | this.state.state === "idle"
34 | ) {
35 | this.inputRef.current.focus();
36 | }
37 | }
38 |
39 | render() {
40 | let { state } = this.state;
41 | return (
42 |
43 |
44 |
Subscribe to our mailing list!
45 | {state === "idle" ? (
46 |
63 | ) : (
64 |
65 | {state === "subscribing" ? (
66 |
Subscribing ...
67 | ) : state === "subscribed" ? (
68 |
You're subscribed, thanks!
69 | ) : null}
70 |
71 | )}
72 |
73 |
74 |
75 |
76 |
Here's some other stuff on the page
77 |
I am a button
78 |
I am another
79 |
80 | );
81 | }
82 | }
83 |
84 | export default App;
85 |
--------------------------------------------------------------------------------
/07-gsbu/lecture/src/App.start.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import Alert from "@reach/alert";
3 |
4 | let sleep = ms => new Promise(res => setTimeout(res, ms));
5 |
6 | let subscribeEmail = email =>
7 | new Promise(async resolve => {
8 | console.log("subscribing", email);
9 | await sleep(4000);
10 | console.log("subscribed!");
11 | resolve();
12 | });
13 |
14 | class App extends Component {
15 | state = {
16 | state: "idle" // "subscribing", "subscribed"
17 | };
18 |
19 | inputRef = React.createRef();
20 |
21 | contentRef = React.createRef();
22 |
23 | componentDidUpdate() {
24 | let { state } = this.state;
25 | if (state === "idle") {
26 | this.inputRef.current.focus();
27 | } else {
28 | this.contentRef.current.focus();
29 | }
30 | }
31 |
32 | render() {
33 | let { state } = this.state;
34 | return (
35 |
36 |
37 |
Subscribe to our mailing list!
38 | {state === "idle" ? (
39 |
56 | ) : (
57 |
58 | {state === "subscribing" ? (
59 |
Subscribing ...
60 | ) : state === "subscribed" ? (
61 |
You're subscribed, thanks!
62 | ) : null}
63 |
64 | )}
65 |
66 |
67 |
68 |
69 |
Here's some other stuff on the page
70 |
I am a button
71 |
I am another
72 |
73 | );
74 | }
75 | }
76 |
77 | export default App;
78 |
--------------------------------------------------------------------------------
/07-gsbu/lecture/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.final";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/07-gsbu/lecture/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
3 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
4 | }
5 |
6 | hr {
7 | margin-top: 2rem;
8 | margin-bottom: 2rem;
9 | }
10 |
--------------------------------------------------------------------------------
/08-gdsfp/exercise/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # production
7 | build
8 |
9 | # misc
10 | .DS_Store
11 | npm-debug.log
12 |
--------------------------------------------------------------------------------
/08-gdsfp/exercise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "01",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.5.0",
7 | "react-dom": "^16.5.0"
8 | },
9 | "devDependencies": {
10 | "react-scripts": "1.0.10"
11 | },
12 | "scripts": {
13 | "start": "react-scripts start",
14 | "build": "react-scripts build",
15 | "test": "react-scripts test --env=jsdom",
16 | "eject": "react-scripts eject"
17 | }
18 | }
--------------------------------------------------------------------------------
/08-gdsfp/exercise/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/08-gdsfp/exercise/public/favicon.ico
--------------------------------------------------------------------------------
/08-gdsfp/exercise/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Redux TodoMVC Example
7 |
8 |
9 |
10 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/08-gdsfp/exercise/src/App.solution.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | const getQuote = () => {
4 | return 30 + Math.random() * 10;
5 | };
6 |
7 | class App extends Component {
8 | state = {
9 | price: getQuote()
10 | };
11 |
12 | componentDidMount() {
13 | setInterval(this.fetch, 2000);
14 | }
15 |
16 | fetch = async () => {
17 | this.setState({ price: getQuote() });
18 | };
19 |
20 | render() {
21 | return (
22 |
23 |
Stock Price
24 |
25 |
26 | );
27 | }
28 | }
29 |
30 | class PriceDisplay extends React.Component {
31 | state = {
32 | price: this.props.price,
33 | direction: "initial"
34 | };
35 |
36 | static getDerivedStateFromProps(props, state, more) {
37 | if (props.price > state.price) {
38 | return {
39 | price: props.price,
40 | direction: "up"
41 | };
42 | } else if (props.price < state.price) {
43 | return {
44 | price: props.price,
45 | direction: "down"
46 | };
47 | }
48 | return null;
49 | }
50 |
51 | render() {
52 | return (
53 |
68 |
69 | {this.state.direction === "up" && "▲"}
70 | {this.state.direction === "down" && "▼"}
71 |
72 |
73 | {this.props.price.toFixed(2)}
74 |
75 |
76 | );
77 | }
78 | }
79 |
80 | export default App;
81 |
--------------------------------------------------------------------------------
/08-gdsfp/exercise/src/App.start.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | const getQuote = () => {
4 | return 30 + Math.random() * 10;
5 | };
6 |
7 | class App extends Component {
8 | state = {
9 | price: getQuote()
10 | };
11 |
12 | componentDidMount() {
13 | setInterval(this.fetch, 2000);
14 | }
15 |
16 | fetch = async () => {
17 | this.setState({ price: getQuote() });
18 | };
19 |
20 | render() {
21 | return (
22 |
23 |
Stock Price
24 |
25 |
26 | );
27 | }
28 | }
29 |
30 | class PriceDisplay extends React.Component {
31 | render() {
32 | let direction = "up"; // get this from state instead
33 | return (
34 |
49 |
50 | {direction === "up" && "▲"}
51 | {direction === "down" && "▼"}
52 |
53 |
54 | {this.props.price.toFixed(2)}
55 |
56 |
57 | );
58 | }
59 | }
60 |
61 | export default App;
62 |
63 | // import React, { Component } from "react";
64 |
65 | // const getQuote = () => {
66 | // return 30 + Math.random() * 10;
67 | // };
68 |
69 | // class App extends Component {
70 | // state = {
71 | // price: getQuote()
72 | // };
73 |
74 | // componentDidMount() {
75 | // setInterval(this.fetch, 2000);
76 | // }
77 |
78 | // fetch = async () => {
79 | // this.setState({ price: getQuote() });
80 | // };
81 |
82 | // render() {
83 | // return (
84 | //
85 | //
Stock Price
86 | //
87 | //
88 | // );
89 | // }
90 | // }
91 |
92 | // class PriceDisplay extends React.Component {
93 | // state = {};
94 |
95 | // static getDerivedStateFromProps(props, state, more) {
96 | // if (!state.price) {
97 | // return {
98 | // price: props.price,
99 | // direction: "initial"
100 | // };
101 | // } else {
102 | // if (props.price > state.price) {
103 | // return {
104 | // price: props.price,
105 | // direction: "up"
106 | // };
107 | // } else if (props.price < state.price) {
108 | // return {
109 | // price: props.price,
110 | // direction: "down"
111 | // };
112 | // }
113 | // }
114 | // }
115 |
116 | // render() {
117 | // return (
118 | //
135 | //
136 | // {this.state.direction === "up" && "▲"}
137 | // {this.state.direction === "down" && "▼"}
138 | //
139 | //
140 | // {this.props.price.toFixed(2)}
141 | //
142 | //
143 | // );
144 | // }
145 | // }
146 |
147 | // export default App;
148 |
--------------------------------------------------------------------------------
/08-gdsfp/exercise/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.start";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/08-gdsfp/exercise/src/lib/digital-7/digital-7 (italic).ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/08-gdsfp/exercise/src/lib/digital-7/digital-7 (italic).ttf
--------------------------------------------------------------------------------
/08-gdsfp/exercise/src/lib/digital-7/digital-7 (mono italic).ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/08-gdsfp/exercise/src/lib/digital-7/digital-7 (mono italic).ttf
--------------------------------------------------------------------------------
/08-gdsfp/exercise/src/lib/digital-7/digital-7 (mono).ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/08-gdsfp/exercise/src/lib/digital-7/digital-7 (mono).ttf
--------------------------------------------------------------------------------
/08-gdsfp/exercise/src/lib/digital-7/digital-7.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/08-gdsfp/exercise/src/lib/digital-7/digital-7.ttf
--------------------------------------------------------------------------------
/08-gdsfp/exercise/src/lib/digital-7/readme.txt:
--------------------------------------------------------------------------------
1 | True Type Fonts: DIGITAL-7 version 1.02
2 |
3 |
4 | EULA
5 | -==-
6 | The fonts Digital-7 is freeware for home using.
7 |
8 |
9 | DESCRIPTION
10 | -=========-
11 |
12 | This font created specially for program Calculator-7 (download shareware version: http://www.styleseven.com/ and use 7 days fo free).
13 |
14 | The program Calculator-7 offers you the following possibilities:
15 | * calculate using seven operator: addition, subtraction, multiply, divide, percent, square root, 1 divide to X;
16 | * set decimal position (0, 2, 3, float) and round type (up, mathematical, down);
17 | * customize an appearance of work window: scale, fonts for digital panel and buttons, background color;
18 | * customize an appearance of number in digital panel: leading zero for decimal, thousand separator, decimal separator, digit grouping;
19 | * calculate total from clipboard (copy data to clipboard from table or text and press one button).
20 |
21 |
22 | Files in digital-7_font.zip:
23 | readme.txt this file;
24 | digital-7.ttf digital-7 regular font;
25 | digital-7 (italic).ttf digital-7 italic font;
26 | digital-7 (mono).ttf digital-7 mono font;
27 | digital-7 (mono italic).ttf digital-7 mono font.
28 |
29 | Please visit http://www.styleseven.com/ for download our other products as freeware as shareware.
30 | We will welcome any useful suggestions and comments; please send them to ms-7@styleseven.com
31 |
32 |
33 | FREEWARE USE (NOTES)
34 | -=================-
35 | Also you may:
36 | * Use the font in freeware software (credit needed);
37 | * Use the font for your education process.
38 |
39 |
40 | COMMERCIAL OR BUSINESS USE
41 | -========================-
42 |
43 | You can buy font for commercial use here ($24.95): http://store.esellerate.net/s.aspx?s=STR0331655240
44 | You may:
45 | * Include the font to your installation;
46 | * Use one license up to 100 computers in your office.
47 | Please contact us for any questions.
48 |
49 |
50 | WHAT IS NEW?
51 | -==========-
52 |
53 | Version 1.01 April 05 2009
54 | --------------------------
55 | * Change Typeface name for fonts "Digital-7 (mono)" and "Digital-7 (italic)" (now available all fonts for select in application, for example Word Pad).
56 | * Corrected symbol ':'.
57 |
58 | Version 1.01 April 07 2011
59 | --------------------------
60 | * Embedding is allowed.
61 |
62 | Version 1.1 June 07 2013
63 | --------------------------
64 | * Mono Italic font is added.
65 |
66 |
67 | AUTHOR
68 | -====-
69 |
70 | Sizenko Alexander
71 | Style-7
72 | http://www.styleseven.com
73 | Created: October 7 2008
--------------------------------------------------------------------------------
/08-gdsfp/exercise/src/lib/index.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "Digital";
3 | src: url("./digital-7/digital-7.ttf");
4 | }
5 |
6 | body {
7 | background: #eee;
8 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
9 | Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
10 | }
11 |
12 | .App {
13 | text-align: center;
14 | }
15 |
16 | .App-logo {
17 | animation: App-logo-spin infinite 20s linear;
18 | height: 80px;
19 | }
20 |
21 | .App-header {
22 | background-color: #222;
23 | height: 150px;
24 | padding: 20px;
25 | color: white;
26 | }
27 |
28 | .App-title {
29 | font-size: 1.5em;
30 | }
31 |
32 | .App-intro {
33 | font-size: large;
34 | }
35 |
36 | @keyframes App-logo-spin {
37 | from {
38 | transform: rotate(0deg);
39 | }
40 | to {
41 | transform: rotate(360deg);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/08-gdsfp/lecture/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # production
7 | build
8 |
9 | # misc
10 | .DS_Store
11 | npm-debug.log
12 |
--------------------------------------------------------------------------------
/08-gdsfp/lecture/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "01",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reach/router": "^1.2.1",
7 | "react": "^16.5.0",
8 | "react-dom": "^16.5.0"
9 | },
10 | "devDependencies": {
11 | "react-scripts": "1.0.10"
12 | },
13 | "scripts": {
14 | "start": "react-scripts start",
15 | "build": "react-scripts build",
16 | "test": "react-scripts test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/08-gdsfp/lecture/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/08-gdsfp/lecture/public/favicon.ico
--------------------------------------------------------------------------------
/08-gdsfp/lecture/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | getDerivedStateFromProps
7 |
8 |
9 |
10 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/08-gdsfp/lecture/src/App.final.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Router, Link } from "@reach/router";
3 |
4 | const API = "https://contacts.now.sh";
5 |
6 | class Fetch extends Component {
7 | state = {
8 | error: null,
9 | data: null,
10 | url: this.props.url
11 | };
12 |
13 | static getDerivedStateFromProps(props, state) {
14 | if (props.url !== state.url) {
15 | return {
16 | error: null,
17 | data: null,
18 | url: props.url
19 | };
20 | }
21 | return null;
22 | }
23 |
24 | componentDidMount() {
25 | this.fetch();
26 | }
27 |
28 | componentDidUpdate(prevProps) {
29 | if (prevProps.url !== this.props.url) {
30 | this.fetch();
31 | }
32 | }
33 |
34 | async fetch() {
35 | try {
36 | let response = await fetch(this.state.url);
37 | let data = await response.json();
38 | this.setState({ data });
39 | } catch (error) {
40 | this.setState({ error });
41 | }
42 | }
43 |
44 | render() {
45 | return this.props.children(this.state);
46 | }
47 | }
48 |
49 | let Home = () => (
50 |
51 |
Welcome!
52 |
Please select a contact.
53 |
54 | );
55 |
56 | let Contact = ({ contactId }) => (
57 |
58 | {({ error, data }) =>
59 | data ? (
60 |
61 |
Contact: {data.contact.first}
62 |
67 |
68 | ) : error ? (
69 | ERROR! {error.message}
70 | ) : (
71 | Loading...
72 | )
73 | }
74 |
75 | );
76 |
77 | let App = ({ children, contacts }) => (
78 |
79 |
Address Book
80 |
81 | {contacts.map(contact => (
82 |
83 |
84 | {contact.first} {contact.last}
85 |
86 |
87 | ))}
88 |
89 | {children}
90 |
91 | );
92 |
93 | export default () => (
94 |
95 | {({ error, data }) =>
96 | data ? (
97 |
98 |
99 |
100 |
101 |
102 |
103 | ) : error ? (
104 | ERROR! {error.message}
105 | ) : (
106 | Loading...
107 | )
108 | }
109 |
110 | );
111 |
--------------------------------------------------------------------------------
/08-gdsfp/lecture/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./lib/index.css";
4 | import App from "./App.final";
5 |
6 | ReactDOM.render( , document.getElementById("root"));
7 |
--------------------------------------------------------------------------------
/08-gdsfp/lecture/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
3 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
4 | }
5 |
--------------------------------------------------------------------------------
/09-suspense/exercise/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/09-suspense/exercise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "01",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reach/router": "^1.2.1",
7 | "react": "^16.7.0-alpha.0",
8 | "react-cache": "^2.0.0-alpha.0",
9 | "react-dom": "^16.7.0-alpha.0"
10 | },
11 | "devDependencies": {
12 | "react-scripts": "1.0.10"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test --env=jsdom",
18 | "eject": "react-scripts eject"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/09-suspense/exercise/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/09-suspense/exercise/public/favicon.ico
--------------------------------------------------------------------------------
/09-suspense/exercise/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/09-suspense/exercise/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/09-suspense/exercise/src/App.solution.js:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from "react";
2 | import { unstable_createResource as createResource } from "react-cache";
3 | import { Router, Link } from "@reach/router";
4 |
5 | let ContactsResource = createResource(async path => {
6 | let response = await fetch(`https://contacts.now.sh${path}`);
7 | let json = await response.json();
8 | return json;
9 | });
10 |
11 | let ImageResource = createResource(src => {
12 | return new Promise(resolve => {
13 | let img = new Image();
14 | img.src = src;
15 | img.onload = () => {
16 | setTimeout(() => {
17 | resolve(src);
18 | }, 3000);
19 | };
20 | });
21 | });
22 |
23 | function Img({ src, ...props }) {
24 | // eslint-disable-next-line jsx-a11y/alt-text
25 | return ;
26 | }
27 |
28 | function Home() {
29 | let { contacts } = ContactsResource.read("/contacts");
30 | return (
31 |
32 |
Contacts
33 |
34 | {contacts.map(contact => (
35 |
36 |
37 | {contact.first} {contact.last}
38 |
39 |
40 | ))}
41 |
42 |
43 | );
44 | }
45 |
46 | function Contact({ id }) {
47 | let { contact } = ContactsResource.read(`/contacts/${id}`);
48 | return (
49 |
50 |
51 | {contact.first} {contact.last}
52 |
53 |
54 | Loading...}>
55 |
60 |
61 |
62 |
63 | Home
64 |
65 |
66 | );
67 | }
68 |
69 | export default () => (
70 | Loading...}>
71 |
72 |
73 |
74 |
75 |
76 | );
77 |
--------------------------------------------------------------------------------
/09-suspense/exercise/src/App.start.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | 1. Fill in `Contact` to use `ContactsResource` to fetch the contact, use the
4 | path: `/contacts/${id}` like this:
5 |
6 | 2. Notice how we transition before the image loads and then the page jumps
7 | around? We're waiting for the contact data but not the image. Create an
8 | ` ` component and a new `ImgResource` to delay transitioning until
9 | the image is loaded
10 |
11 | Remember, `createResource` needs to return a promise
12 |
13 | Tips:
14 |
15 | ```
16 | // you can load images programatically
17 | let image = new Image();
18 | image.src = someUrl
19 | image.onload = () => {}
20 |
21 | // You can create promises out of anything, here we'll use
22 | // setTimeout to make a "sleep" promise:
23 | function sleep(ms=1000) {
24 | return new Promise((resolve, reject) => {
25 | setTimeout(resolve, ms)
26 | })
27 | }
28 |
29 | // combine those two things to create your `ImageResource`
30 | ```
31 |
32 | 3. Finally, render a `Placeholder` (you'll need to import it from react) around
33 | `Img` with a `2000` delayMs, and then artificially delay your ImageResource by
34 | 3000ms with setTimeout. What happens when you click the links now?
35 |
36 | */
37 |
38 | import React, { Suspense } from "react";
39 | import { unstable_createResource as createResource } from "react-cache";
40 | import { Router, Link } from "@reach/router";
41 |
42 | let ContactsResource = createResource(async path => {
43 | let response = await fetch(`https://contacts.now.sh${path}`);
44 | let json = await response.json();
45 | return json;
46 | });
47 |
48 | function Home() {
49 | let { contacts } = ContactsResource.read("/contacts");
50 | return (
51 |
52 |
Contacts
53 |
54 | {contacts.map(contact => (
55 |
56 |
57 | {contact.first} {contact.last}
58 |
59 |
60 | ))}
61 |
62 |
63 | );
64 | }
65 |
66 | let FAKE_DATA = {
67 | contact: {
68 | first: "Ryan",
69 | last: "Florence",
70 | avatar: "https://placekitten.com/510/510"
71 | }
72 | };
73 |
74 | function Contact({ id }) {
75 | // don't use FAKE_DATA, use `ContactsResource`
76 | let { contact } = FAKE_DATA;
77 | return (
78 |
79 |
80 | {contact.first} {contact.last}
81 |
82 |
83 |
88 |
89 |
90 | Home
91 |
92 |
93 | );
94 | }
95 |
96 | export default () => (
97 | Loading...}>
98 |
99 |
100 |
101 |
102 |
103 | );
104 |
--------------------------------------------------------------------------------
/09-suspense/exercise/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./lib/index.css";
3 | import { createRoot } from "react-dom";
4 | import App from "./App.start";
5 |
6 | createRoot(document.getElementById("root")).render( );
7 |
--------------------------------------------------------------------------------
/09-suspense/exercise/src/lib/cache.js:
--------------------------------------------------------------------------------
1 | import {createCache} from 'simple-cache-provider';
2 |
3 | export let cache;
4 |
5 | function initCache() {
6 | cache = createCache(initCache);
7 | }
8 |
9 | initCache();
10 |
11 |
--------------------------------------------------------------------------------
/09-suspense/exercise/src/lib/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
3 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/09-suspense/lecture/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/09-suspense/lecture/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "01",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reach/component-component": "^0.1.1",
7 | "@reach/router": "^1.2.1",
8 | "glamor": "^2.20.40",
9 | "react": "16.6",
10 | "react-cache": "^2.0.0-alpha.0",
11 | "react-dom": "16.6",
12 | "react-svg-spinner": "^1.0.1"
13 | },
14 | "devDependencies": {
15 | "react-scripts": "1.0.10"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test --env=jsdom",
21 | "eject": "react-scripts eject"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/09-suspense/lecture/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reach/advanced-react-workshop/c78c67a2202eb9481217d14345ecfa3a25837115/09-suspense/lecture/public/favicon.ico
--------------------------------------------------------------------------------
/09-suspense/lecture/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | Advanced React.js Exercise
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/09-suspense/lecture/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/09-suspense/lecture/src/index.js:
--------------------------------------------------------------------------------
1 | import "./lib/index.css";
2 | import React from "react";
3 | import { unstable_createRoot } from "react-dom";
4 | import App from "./App.Refactor";
5 |
6 | unstable_createRoot(document.getElementById("root")).render( );
7 |
--------------------------------------------------------------------------------
/09-suspense/lecture/src/lib/Competitions.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | export default () => Competitions
;
3 |
--------------------------------------------------------------------------------
/09-suspense/lecture/src/lib/ManageScroll.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Location } from "@reach/router";
3 |
4 | window.history.scrollRestoration = "manual";
5 |
6 | let scrollPositions = {};
7 |
8 | class ManageScrollImpl extends React.Component {
9 | componentDidMount() {
10 | try {
11 | // session storage will throw for a few reasons
12 | // - user settings
13 | // - in-cognito/private browsing
14 | // - who knows...
15 | let storage = JSON.parse(sessionStorage.getItem("scrollPositions"));
16 | if (storage) {
17 | scrollPositions = JSON.parse(storage) || {};
18 | let { key } = this.props.location;
19 | if (scrollPositions[key]) {
20 | window.scrollTo(0, scrollPositions[key]);
21 | }
22 | }
23 | } catch (e) {}
24 |
25 | window.addEventListener("scroll", this.listener);
26 | }
27 |
28 | componentWillUnmount() {
29 | window.removeEventListener("scroll", this.listener);
30 | }
31 |
32 | componentDidUpdate() {
33 | const { key } = this.props.location;
34 | if (!scrollPositions[key]) {
35 | // never seen this location before
36 | window.scrollTo(0, 0);
37 | } else {
38 | // seen it
39 | window.scrollTo(0, scrollPositions[key]);
40 | }
41 | }
42 |
43 | listener = () => {
44 | scrollPositions[this.props.location.key] = window.scrollY;
45 | try {
46 | sessionStorage.setItem(
47 | "scrollPositions",
48 | JSON.stringify(scrollPositions)
49 | );
50 | } catch (e) {}
51 | };
52 |
53 | render() {
54 | return null;
55 | }
56 | }
57 |
58 | export default () => (
59 |
60 | {({ location }) => }
61 |
62 | );
63 |
--------------------------------------------------------------------------------
/09-suspense/lecture/src/lib/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | body {
6 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
7 | Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
8 | }
9 |
--------------------------------------------------------------------------------
/10-carousel/.eslintrc:
--------------------------------------------------------------------------------
1 | { "extends": "react-app" }
2 |
--------------------------------------------------------------------------------
/10-carousel/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/10-carousel/config/env.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const paths = require('./paths');
6 |
7 | // Make sure that including paths.js after env.js will read .env variables.
8 | delete require.cache[require.resolve('./paths')];
9 |
10 | const NODE_ENV = process.env.NODE_ENV;
11 | if (!NODE_ENV) {
12 | throw new Error(
13 | 'The NODE_ENV environment variable is required but was not specified.'
14 | );
15 | }
16 |
17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
18 | var dotenvFiles = [
19 | `${paths.dotenv}.${NODE_ENV}.local`,
20 | `${paths.dotenv}.${NODE_ENV}`,
21 | // Don't include `.env.local` for `test` environment
22 | // since normally you expect tests to produce the same
23 | // results for everyone
24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`,
25 | paths.dotenv,
26 | ].filter(Boolean);
27 |
28 | // Load environment variables from .env* files. Suppress warnings using silent
29 | // if this file is missing. dotenv will never modify any environment variables
30 | // that have already been set. Variable expansion is supported in .env files.
31 | // https://github.com/motdotla/dotenv
32 | // https://github.com/motdotla/dotenv-expand
33 | dotenvFiles.forEach(dotenvFile => {
34 | if (fs.existsSync(dotenvFile)) {
35 | require('dotenv-expand')(
36 | require('dotenv').config({
37 | path: dotenvFile,
38 | })
39 | );
40 | }
41 | });
42 |
43 | // We support resolving modules according to `NODE_PATH`.
44 | // This lets you use absolute paths in imports inside large monorepos:
45 | // https://github.com/facebookincubator/create-react-app/issues/253.
46 | // It works similar to `NODE_PATH` in Node itself:
47 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
48 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
49 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
50 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
51 | // We also resolve them to make sure all tools using them work consistently.
52 | const appDirectory = fs.realpathSync(process.cwd());
53 | process.env.NODE_PATH = (process.env.NODE_PATH || '')
54 | .split(path.delimiter)
55 | .filter(folder => folder && !path.isAbsolute(folder))
56 | .map(folder => path.resolve(appDirectory, folder))
57 | .join(path.delimiter);
58 |
59 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
60 | // injected into the application via DefinePlugin in Webpack configuration.
61 | const REACT_APP = /^REACT_APP_/i;
62 |
63 | function getClientEnvironment(publicUrl) {
64 | const raw = Object.keys(process.env)
65 | .filter(key => REACT_APP.test(key))
66 | .reduce(
67 | (env, key) => {
68 | env[key] = process.env[key];
69 | return env;
70 | },
71 | {
72 | // Useful for determining whether we’re running in production mode.
73 | // Most importantly, it switches React into the correct mode.
74 | NODE_ENV: process.env.NODE_ENV || 'development',
75 | // Useful for resolving the correct path to static assets in `public`.
76 | // For example, .
77 | // This should only be used as an escape hatch. Normally you would put
78 | // images into the `src` and `import` them in code to get their paths.
79 | PUBLIC_URL: publicUrl,
80 | }
81 | );
82 | // Stringify all values so we can feed into Webpack DefinePlugin
83 | const stringified = {
84 | 'process.env': Object.keys(raw).reduce((env, key) => {
85 | env[key] = JSON.stringify(raw[key]);
86 | return env;
87 | }, {}),
88 | };
89 |
90 | return { raw, stringified };
91 | }
92 |
93 | module.exports = getClientEnvironment;
94 |
--------------------------------------------------------------------------------
/10-carousel/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/en/webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/10-carousel/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/en/webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`;
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/10-carousel/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebookincubator/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(path, needsSlash) {
15 | const hasSlash = path.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return path.substr(path, path.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${path}/`;
20 | } else {
21 | return path;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage;
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right