├── LICENSE.md
├── README.md
├── ch01
└── README.md
├── ch02
├── hello.html
├── hello.py
└── requirements.txt
├── ch03
├── js_function.html
├── js_function.py
└── requirements.txt
├── ch04
├── mapping.html
├── mapping.py
└── requirements.txt
├── ch05
├── external_jq.html
├── external_jq.py
└── requirements.txt
├── ch06
├── react_hello.html
├── react_hello.py
└── requirements.txt
├── ch07
└── README.md
├── ch08
├── pyreact2.py
├── react_hello2.html
├── react_hello2.py
└── requirements.txt
├── ch09
├── package.json
├── pyreact3.py
├── react_hello3.html
├── react_hello3.py
└── requirements.txt
├── ch10
├── package.json
├── pyreact4.py
├── react_hello4.html
├── react_hello4.py
└── requirements.txt
├── ch11
└── README.md
├── ch12
├── app.py
├── index.html
├── package.json
├── pyreact.py
└── requirements.txt
├── ch13
├── app.py
├── app_v1.py
├── index.html
├── package.json
├── pyreact.py
└── requirements.txt
├── ch14
├── app.py
├── index.html
├── package.json
├── pyreact.py
└── requirements.txt
├── ch15
├── app.py
├── app_v1.py
├── app_v2.py
├── index.html
├── listItems.py
├── package.json
├── pyreact.py
└── requirements.txt
├── ch16
├── app.py
├── index.html
├── package.json
├── pyreact.py
├── requirements.txt
└── todo.jsx
├── ch17
├── app.css
├── app.py
├── app_v1.py
├── app_v2.py
├── app_v3.py
├── index.html
├── index_v1.html
├── index_v2.html
├── package.json
├── pyreact.py
└── requirements.txt
├── ch18
├── app.py
├── appTheme.py
├── appTheme_v1.py
├── app_v1.py
├── app_v2.py
├── index.html
├── package.json
├── pymui.py
├── pymui_v1.py
├── pymui_v2.py
├── pymui_v3.py
├── pyreact.py
└── requirements.txt
├── ch19
├── app.py
├── appTheme.py
├── dev-server.js
├── index.html
├── package.json
├── pymui.py
├── pyreact.py
├── requirements.txt
└── webserver.py
├── ch20
├── app.py
├── dev-server.js
├── index.html
├── jsutils.py
├── jsutils_v1.py
├── package.json
├── pymui.py
├── pyreact.py
├── requirements.txt
└── webserver.py
├── ch21
├── app.py
├── app_v1.py
├── dev-server.js
├── index.html
├── package.json
├── pymui.py
├── pyreact.py
└── requirements.txt
├── ch22
└── jsutils.py
├── ch23
├── package.json
└── version.py
├── ch24
├── app.py
├── dev-server.js
├── index.html
├── jsutils.py
├── package.json
├── pymui.py
├── pyreact.py
├── requirements.txt
├── version.py
└── webserver.py
├── ch25
└── package.json
└── ch26
└── README.md
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 John Sheehan
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4 |
5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## React to Python
2 | ### Part I & II Chapter Code
3 |
4 | This repository contains source code files for *React to Python* Part 1 and Part 2
5 |
6 | Source code for Part 3 of the book is in the [Project Repository](https://github.com/rtp-book/project)
7 |
8 | For information on the *React to Python* book itself, visit [https://pyreact.com](https://pyreact.com)
9 |
--------------------------------------------------------------------------------
/ch01/README.md:
--------------------------------------------------------------------------------
1 | There are no code modules for Chapter 1.
2 |
3 |
--------------------------------------------------------------------------------
/ch02/hello.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ch02/hello.py:
--------------------------------------------------------------------------------
1 | def say_hello():
2 | document.getElementById('destination').innerHTML = "Hello World!"
3 |
4 |
5 | def clear_it():
6 | document.getElementById('destination').innerHTML = ""
7 |
--------------------------------------------------------------------------------
/ch02/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch03/js_function.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 | ?
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ch03/js_function.py:
--------------------------------------------------------------------------------
1 | def get_number():
2 | new_val = int(window.Math.random() * 10)
3 | document.getElementById('myval').innerHTML = new_val
4 |
5 |
--------------------------------------------------------------------------------
/ch03/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch04/mapping.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ch04/mapping.py:
--------------------------------------------------------------------------------
1 | def print_stuff():
2 | console.log("Native JS console.log call")
3 | print("Python print")
4 | console.invalid_method("This will be an error")
5 |
6 |
--------------------------------------------------------------------------------
/ch04/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch05/external_jq.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ch05/external_jq.py:
--------------------------------------------------------------------------------
1 | # __pragma__ ('alias', 'jq', '$')
2 |
3 | def set_click():
4 | def add_item():
5 | jq("ol").append("List item")
6 |
7 | jq("#append_btn").click(add_item)
8 |
9 | jq(document).ready(set_click)
10 |
11 |
--------------------------------------------------------------------------------
/ch05/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch06/react_hello.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ch06/react_hello.py:
--------------------------------------------------------------------------------
1 | def App():
2 | val, setVal = React.useState("")
3 |
4 | def say_hello():
5 | setVal("Hello React!")
6 |
7 | def clear_it():
8 | setVal("")
9 |
10 | return [
11 | React.createElement('button', {'onClick': say_hello}, "Click Me!"),
12 | React.createElement('button', {'onClick': clear_it}, "Clear"),
13 | React.createElement('div', None, val)
14 | ]
15 |
16 | def render():
17 | ReactDOM.render(
18 | React.createElement(App, None),
19 | document.getElementById('root')
20 | )
21 |
22 | document.addEventListener('DOMContentLoaded', render)
23 |
24 |
--------------------------------------------------------------------------------
/ch06/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch07/README.md:
--------------------------------------------------------------------------------
1 | There are no code modules for Chapter 7.
2 |
3 |
--------------------------------------------------------------------------------
/ch08/pyreact2.py:
--------------------------------------------------------------------------------
1 | # __pragma__('skip')
2 | # These are here to quiet the Python linter and are ignored by Transcrypt
3 | React = None
4 | ReactDOM = None
5 | document = None
6 | # __pragma__('noskip')
7 |
8 | # Map React javaScript objects to Python identifiers
9 | createElement = React.createElement
10 | useState = React.useState
11 |
12 | def render(root_component, props, container):
13 | """Loads main react component into DOM"""
14 | def main():
15 | ReactDOM.render(
16 | React.createElement(root_component, props),
17 | document.getElementById(container)
18 | )
19 |
20 | document.addEventListener('DOMContentLoaded', main)
21 |
22 |
--------------------------------------------------------------------------------
/ch08/react_hello2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ch08/react_hello2.py:
--------------------------------------------------------------------------------
1 | from pyreact2 import useState, render, createElement as el
2 |
3 | def App():
4 | val, setVal = useState("")
5 |
6 | def say_hello():
7 | setVal("Hello React!")
8 |
9 | def clear_it():
10 | setVal("")
11 |
12 | return [
13 | el('button', {'onClick': say_hello}, "Click Me!"),
14 | el('button', {'onClick': clear_it}, "Clear"),
15 | el('div', None, val)
16 | ]
17 |
18 | render(App, None, 'root')
19 |
20 |
--------------------------------------------------------------------------------
/ch08/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch09/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch09",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "react": "^16.14.0",
14 | "react-dom": "^16.14.0"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ch09/pyreact3.py:
--------------------------------------------------------------------------------
1 | # __pragma__('skip')
2 | # These are here to quiet the Python linter and are ignored by Transcrypt
3 | window = None
4 | document = None
5 | # __pragma__('noskip')
6 |
7 | # Create local references to the React and ReactDOM JavaScript libraries
8 | React = window.React
9 | ReactDOM = window.ReactDOM
10 |
11 | # Remove the React and ReactDOM JavaScript libraries from the global namespace
12 | # __pragma__('js', 'delete window.React;')
13 | # __pragma__('js', 'delete window.ReactDOM;')
14 |
15 | # Map React javaScript objects to Python identifiers
16 | createElement = React.createElement
17 | useState = React.useState
18 |
19 |
20 | def render(root_component, props, container):
21 | """Loads main react component into DOM"""
22 |
23 | def main():
24 | ReactDOM.render(
25 | React.createElement(root_component, props),
26 | document.getElementById(container)
27 | )
28 |
29 | document.addEventListener('DOMContentLoaded', main)
30 |
31 |
--------------------------------------------------------------------------------
/ch09/react_hello3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ch09/react_hello3.py:
--------------------------------------------------------------------------------
1 | from pyreact3 import useState, render, createElement as el
2 |
3 | def App():
4 | val, setVal = useState("")
5 |
6 | def say_hello():
7 | setVal("Hello React!")
8 |
9 | def clear_it():
10 | setVal("")
11 |
12 | return [
13 | el('button', {'onClick': say_hello}, "Click Me!"),
14 | el('button', {'onClick': clear_it}, "Clear"),
15 | el('div', None, val)
16 | ]
17 |
18 | render(App, None, 'root')
19 |
20 |
--------------------------------------------------------------------------------
/ch09/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch10/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch10",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 react_hello4.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build react_hello4.html --no-source-maps --out-dir dist/prod",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "react": "^16.14.0",
16 | "react-dom": "^16.14.0"
17 | },
18 | "devDependencies": {
19 | "parcel-bundler": "^1.12.4",
20 | "parcel-plugin-transcrypt": "^1.0.20"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ch10/pyreact4.py:
--------------------------------------------------------------------------------
1 | # __pragma__('skip')
2 | # These are here to quiet the Python linter and are ignored by Transcrypt
3 | require = None
4 | document = None
5 | # __pragma__('noskip')
6 |
7 | # Load the React and ReactDOM JavaScript libraries into the local namespace
8 | React = require('react')
9 | ReactDOM = require('react-dom')
10 |
11 | # Map React javaScript objects to Python identifiers
12 | createElement = React.createElement
13 | useState = React.useState
14 |
15 |
16 | def render(root_component, props, container):
17 | """Loads main react component into DOM"""
18 |
19 | def main():
20 | ReactDOM.render(
21 | React.createElement(root_component, props),
22 | document.getElementById(container)
23 | )
24 |
25 | document.addEventListener('DOMContentLoaded', main)
26 |
27 |
--------------------------------------------------------------------------------
/ch10/react_hello4.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch10/react_hello4.py:
--------------------------------------------------------------------------------
1 | from pyreact4 import useState, render, createElement as el
2 |
3 | def App():
4 | val, setVal = useState("")
5 |
6 | def say_hello():
7 | setVal("Hello React!")
8 |
9 | def clear_it():
10 | setVal("")
11 |
12 | return [
13 | el('button', {'onClick': say_hello}, "Click Me!"),
14 | el('button', {'onClick': clear_it}, "Clear"),
15 | el('div', None, val)
16 | ]
17 |
18 | render(App, None, 'root')
19 |
--------------------------------------------------------------------------------
/ch10/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch11/README.md:
--------------------------------------------------------------------------------
1 | There are no code modules for Chapter 11.
2 |
3 |
--------------------------------------------------------------------------------
/ch12/app.py:
--------------------------------------------------------------------------------
1 | from pyreact import alert, useState, render, createElement as el
2 |
3 | def App():
4 | newItem, setNewItem = useState("")
5 |
6 | def handleSubmit():
7 | alert(f"Item is : {newItem}")
8 | setNewItem("")
9 |
10 | def handleChange(event):
11 | target = event['target']
12 | setNewItem(target['value'])
13 |
14 | return el('div', None,
15 | el('label', {'htmlFor': 'editBox'}, "New Item: "),
16 | el('input', {'id': 'editBox',
17 | 'onChange': handleChange,
18 | 'value': newItem
19 | }
20 | ),
21 | el('button', {'onClick': handleSubmit}, "Submit"),
22 | )
23 |
24 | render(App, None, 'root')
25 |
--------------------------------------------------------------------------------
/ch12/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch12/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch12",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "react": "^16.14.0",
16 | "react-dom": "^16.14.0"
17 | },
18 | "devDependencies": {
19 | "parcel-bundler": "^1.12.4",
20 | "parcel-plugin-transcrypt": "^1.0.20"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ch12/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 |
5 | # Map React javaScript objects to Python identifiers
6 | createElement = React.createElement
7 | useState = React.useState
8 |
9 |
10 | def render(root_component, props, container):
11 | def main():
12 | ReactDOM.render(
13 | React.createElement(root_component, props),
14 | document.getElementById(container)
15 | )
16 |
17 | document.addEventListener('DOMContentLoaded', main)
18 |
19 |
20 | # JavaScript function mappings
21 | alert = window.alert
22 |
--------------------------------------------------------------------------------
/ch12/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch13/app.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 |
3 | def App():
4 | newItem, setNewItem = useState("")
5 | listItems, setListItems = useState([])
6 |
7 | def handleSubmit():
8 | new_list = list(listItems) # Make a copy
9 | new_list.append(newItem) # Add the new item
10 | setListItems(new_list) # Update our state
11 | setNewItem("") # Clear the new item value
12 |
13 | def handleChange(event):
14 | target = event['target']
15 | setNewItem(target['value'])
16 |
17 | def ListItems():
18 | items = []
19 | for item in listItems:
20 | element = el('li', {'key': item}, item)
21 | items.append(element)
22 |
23 | return items
24 |
25 | return el('div', None,
26 | el('label', {'htmlFor': 'editBox'}, "New Item: "),
27 | el('input', {'id': 'editBox',
28 | 'onChange': handleChange,
29 | 'value': newItem
30 | }
31 | ),
32 | el('button', {'onClick': handleSubmit}, "Submit"),
33 | el('ol', None,
34 | el(ListItems, None)
35 | ),
36 | )
37 |
38 | render(App, None, 'root')
39 |
40 |
--------------------------------------------------------------------------------
/ch13/app_v1.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 |
3 | def App():
4 | newItem, setNewItem = useState("")
5 | listItems, setListItems = useState([])
6 |
7 | def handleSubmit():
8 | new_list = list(listItems) # Make a copy
9 | new_list.append(newItem) # Add the new item
10 | setListItems(new_list) # Update our state
11 | setNewItem("") # Clear the new item value
12 |
13 | def handleChange(event):
14 | target = event['target']
15 | setNewItem(target['value'])
16 |
17 | def getListItems():
18 | items = []
19 | for item in listItems:
20 | element = el('li', {'key': item}, item)
21 | items.append(element)
22 |
23 | return items
24 |
25 | return el('div', None,
26 | el('label', {'htmlFor': 'editBox'}, "New Item: "),
27 | el('input', {'id': 'editBox',
28 | 'onChange': handleChange,
29 | 'value': newItem
30 | }
31 | ),
32 | el('button', {'onClick': handleSubmit}, "Submit"),
33 | el('ol', None, getListItems()),
34 | )
35 |
36 | render(App, None, 'root')
37 |
38 |
--------------------------------------------------------------------------------
/ch13/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch13/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch13",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "react": "^16.14.0",
16 | "react-dom": "^16.14.0"
17 | },
18 | "devDependencies": {
19 | "parcel-bundler": "^1.12.4",
20 | "parcel-plugin-transcrypt": "^1.0.20"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ch13/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 |
5 | # Map React javaScript objects to Python identifiers
6 | createElement = React.createElement
7 | useState = React.useState
8 |
9 |
10 | def render(root_component, props, container):
11 | def main():
12 | ReactDOM.render(
13 | React.createElement(root_component, props),
14 | document.getElementById(container)
15 | )
16 |
17 | document.addEventListener('DOMContentLoaded', main)
18 |
19 |
20 | # JavaScript function mappings
21 | alert = window.alert
22 |
--------------------------------------------------------------------------------
/ch13/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch14/app.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 |
3 | def App():
4 | newItem, setNewItem = useState("")
5 | listItems, setListItems = useState([])
6 |
7 | def handleSubmit(event):
8 | event.preventDefault()
9 | new_list = list(listItems) # Make a copy
10 | new_list.append(newItem) # Add the new item
11 | setListItems(new_list) # Update our state
12 | setNewItem("") # Clear the new item value
13 |
14 | def handleChange(event):
15 | target = event['target']
16 | setNewItem(target['value'])
17 |
18 | def ListItems():
19 | items = []
20 | for item in listItems:
21 | element = el('li', {'key': item}, item)
22 | items.append(element)
23 |
24 | return items
25 |
26 | return el('form', {'onSubmit': handleSubmit},
27 | el('label', {'htmlFor': 'editBox'}, "New Item: "),
28 | el('input', {'id': 'editBox',
29 | 'onChange': handleChange,
30 | 'value': newItem
31 | }
32 | ),
33 | el('input', {'type': 'submit'}),
34 | el('ol', None,
35 | el(ListItems, None)
36 | ),
37 | )
38 |
39 | render(App, None, 'root')
40 |
41 |
--------------------------------------------------------------------------------
/ch14/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch14/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch14",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "react": "^16.14.0",
16 | "react-dom": "^16.14.0"
17 | },
18 | "devDependencies": {
19 | "parcel-bundler": "^1.12.4",
20 | "parcel-plugin-transcrypt": "^1.0.20"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ch14/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 |
5 | # Map React javaScript objects to Python identifiers
6 | createElement = React.createElement
7 | useState = React.useState
8 |
9 |
10 | def render(root_component, props, container):
11 | def main():
12 | ReactDOM.render(
13 | React.createElement(root_component, props),
14 | document.getElementById(container)
15 | )
16 |
17 | document.addEventListener('DOMContentLoaded', main)
18 |
19 |
20 | # JavaScript function mappings
21 | alert = window.alert
22 |
--------------------------------------------------------------------------------
/ch14/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch15/app.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 | from listItems import ListItems
3 |
4 | def App():
5 | newItem, setNewItem = useState("")
6 | editItem, setEditItem = useState("")
7 | listItems, setListItems = useState([])
8 |
9 | def handleSubmit(event):
10 | event.preventDefault()
11 | new_list = list(listItems) # Make a copy
12 | if len(editItem) > 0: # In edit mode
13 | new_list[new_list.index(editItem)] = newItem
14 | else: # In add mode
15 | new_list.append(newItem) # Add the new item
16 | setListItems(new_list) # Update our state
17 | setNewItem("") # Clear the new item value
18 | setEditItem("") # Clear the edit item value
19 |
20 | def handleChange(event):
21 | target = event['target']
22 | setNewItem(target['value'])
23 |
24 | def handleDelete(item):
25 | new_list = list(listItems) # Make a copy
26 | new_list.remove(item) # Remove the specified item
27 | setListItems(new_list) # Update our state
28 |
29 | def handleEdit(item):
30 | setNewItem(item) # Set the new item value
31 | setEditItem(item) # Set the edit item value
32 |
33 | return el('form', {'onSubmit': handleSubmit},
34 | el('label', {'htmlFor': 'editBox'},
35 | "Add Item: " if len(editItem) == 0 else "Edit Item: "
36 | ),
37 | el('input', {'id': 'editBox',
38 | 'onChange': handleChange,
39 | 'value': newItem
40 | }
41 | ),
42 | el('input', {'type': 'submit'}),
43 | el('ol', None,
44 | el(ListItems, {'listItems': listItems,
45 | 'handleDelete': handleDelete,
46 | 'handleEdit': handleEdit}
47 | )
48 | ),
49 | )
50 |
51 | render(App, None, 'root')
52 |
53 |
--------------------------------------------------------------------------------
/ch15/app_v1.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 |
3 | def App():
4 | newItem, setNewItem = useState("")
5 | listItems, setListItems = useState([])
6 |
7 | def handleSubmit(event):
8 | event.preventDefault()
9 | new_list = list(listItems) # Make a copy
10 | new_list.append(newItem) # Add the new item
11 | setListItems(new_list) # Update our state
12 | setNewItem("") # Clear the new item value
13 |
14 | def handleChange(event):
15 | target = event['target']
16 | setNewItem(target['value'])
17 |
18 | def handleDelete(item):
19 | new_list = list(listItems) # Make a copy
20 | new_list.remove(item) # Remove the specified item
21 | setListItems(new_list) # Update our state
22 |
23 | def ListItem(props):
24 | return el('li', None,
25 | props['item'] + " ",
26 | el('button', {'type': 'button',
27 | 'onClick': lambda: handleDelete(props['item'])
28 | }, "Delete"),
29 | )
30 |
31 | def ListItems():
32 | items = []
33 | for item in listItems:
34 | items.append(el(ListItem, {'key': item, 'item': item}))
35 | return items
36 |
37 | return el('form', {'onSubmit': handleSubmit},
38 | el('label', {'htmlFor': 'editBox'}, "New Item: "),
39 | el('input', {'id': 'editBox',
40 | 'onChange': handleChange,
41 | 'value': newItem
42 | }
43 | ),
44 | el('input', {'type': 'submit'}),
45 | el('ol', None,
46 | el(ListItems, None)
47 | ),
48 | )
49 |
50 | render(App, None, 'root')
51 |
52 |
--------------------------------------------------------------------------------
/ch15/app_v2.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 |
3 | def App():
4 | newItem, setNewItem = useState("")
5 | editItem, setEditItem = useState("")
6 | listItems, setListItems = useState([])
7 |
8 | def handleSubmit(event):
9 | event.preventDefault()
10 | new_list = list(listItems) # Make a copy
11 | if len(editItem) > 0: # In edit mode
12 | new_list[new_list.index(editItem)] = newItem
13 | else: # In add mode
14 | new_list.append(newItem) # Add the new item
15 | setListItems(new_list) # Update our state
16 | setNewItem("") # Clear the new item value
17 | setEditItem("") # Clear the edit item value
18 |
19 | def handleChange(event):
20 | target = event['target']
21 | setNewItem(target['value'])
22 |
23 | def handleDelete(item):
24 | new_list = list(listItems) # Make a copy
25 | new_list.remove(item) # Remove the specified item
26 | setListItems(new_list) # Update our state
27 |
28 | def handleEdit(item):
29 | setNewItem(item) # Set the new item value
30 | setEditItem(item) # Set the edit item value
31 |
32 | def ListItem(props):
33 | return el('li', None,
34 | props['item'] + " ",
35 | el('button', {'type': 'button',
36 | 'onClick': lambda: handleDelete(props['item'])
37 | }, "Delete"
38 | ),
39 | el('button', {'type': 'button',
40 | 'onClick': lambda: handleEdit(props['item'])
41 | }, "Edit"
42 | ),
43 | )
44 |
45 | def ListItems():
46 | return [el(ListItem, {'key': item, 'item': item}) for item in listItems]
47 |
48 | return el('form', {'onSubmit': handleSubmit},
49 | el('label', {'htmlFor': 'editBox'},
50 | "Add Item: " if len(editItem) == 0 else "Edit Item: "
51 | ),
52 | el('input', {'id': 'editBox',
53 | 'onChange': handleChange,
54 | 'value': newItem
55 | }
56 | ),
57 | el('input', {'type': 'submit'}),
58 | el('ol', None,
59 | el(ListItems, None)
60 | ),
61 | )
62 |
63 | render(App, None, 'root')
64 |
65 |
--------------------------------------------------------------------------------
/ch15/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch15/listItems.py:
--------------------------------------------------------------------------------
1 | from pyreact import createElement as el
2 |
3 | def ListItem(props):
4 | item = props['item']
5 | handleDelete = props['handleDelete']
6 | handleEdit = props['handleEdit']
7 |
8 | return el('li', None,
9 | props['item'] + " ",
10 | el('button', {'type': 'button',
11 | 'onClick': lambda: handleDelete(item)
12 | }, "Delete"
13 | ),
14 | el('button', {'type': 'button',
15 | 'onClick': lambda: handleEdit(item)
16 | }, "Edit"
17 | ),
18 | )
19 |
20 | def ListItems(props):
21 | return [el(ListItem, {'key': item,
22 | 'item': item,
23 | 'handleDelete': props['handleDelete'],
24 | 'handleEdit': props['handleEdit']
25 | }
26 | ) for item in props['listItems']]
27 |
28 |
--------------------------------------------------------------------------------
/ch15/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch15",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "react": "^16.14.0",
16 | "react-dom": "^16.14.0"
17 | },
18 | "devDependencies": {
19 | "parcel-bundler": "^1.12.4",
20 | "parcel-plugin-transcrypt": "^1.0.20"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ch15/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 |
5 | # Map React javaScript objects to Python identifiers
6 | createElement = React.createElement
7 | useState = React.useState
8 |
9 |
10 | def render(root_component, props, container):
11 | def main():
12 | ReactDOM.render(
13 | React.createElement(root_component, props),
14 | document.getElementById(container)
15 | )
16 |
17 | document.addEventListener('DOMContentLoaded', main)
18 |
19 |
20 | # JavaScript function mappings
21 | alert = window.alert
22 |
--------------------------------------------------------------------------------
/ch15/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch16/app.py:
--------------------------------------------------------------------------------
1 | from pyreact import setTitle, useEffect, useState, render, createElement as el
2 |
3 | def App():
4 | newTask, setNewTask = useState("")
5 | editTask, setEditTask = useState(None)
6 | taskList, setTaskList = useState([])
7 | taskCount, setTaskCount = useState(0)
8 | taskFilter, setTaskFilter = useState("all")
9 |
10 | def handleSubmit(event):
11 | event.preventDefault()
12 | new_list = list(taskList) # Make a copy
13 | if editTask is not None: # In edit mode
14 | taskIndex = new_list.index(editTask) # Get list position
15 | new_list[taskIndex].update({'name': newTask}) # Update name
16 | else: # In add mode
17 | new_list.append({'name': newTask, 'status': False}) # Add new item
18 | setTaskList(new_list) # Update our state
19 | setNewTask("") # Clear the new item value
20 | setEditTask(None) # Clear the edit item value
21 |
22 | def handleEdit(task):
23 | setNewTask(task['name']) # Set the new item value
24 | setEditTask(task) # Set the edit item value
25 |
26 | def handleDelete(task):
27 | new_list = list(taskList) # Make a copy
28 | new_list.remove(task) # Remove the specified item
29 | setTaskList(new_list) # Update our state
30 |
31 | def handleChange(event):
32 | target = event['target']
33 | if target['name'] == 'taskFilter':
34 | setTaskFilter(target['value'])
35 | else:
36 | setNewTask(target['value'])
37 |
38 | def handleChangeStatus(event, task):
39 | target = event['target']
40 | new_list = list(taskList) # Make a copy
41 | taskIndex = new_list.index(task) # Get list position
42 | new_list[taskIndex].update({'status': target['checked']}) # Update
43 | setTaskList(new_list) # Update our state
44 |
45 | def ListItem(props):
46 | task = props['task']
47 | if taskFilter == "all" or \
48 | (taskFilter == "open" and not task['status']) or \
49 | (taskFilter == "closed" and task['status']):
50 | return el('li', None,
51 | task['name'] + " ",
52 | el('button',
53 | {'type': 'button',
54 | 'onClick': lambda: handleDelete(task)
55 | }, "Delete"
56 | ),
57 | el('button',
58 | {'type': 'button',
59 | 'onClick': lambda: handleEdit(task)
60 | }, "Edit"
61 | ),
62 | el('label', {'htmlFor': 'status'}, " Completed:"),
63 | el('input',
64 | {'type': 'checkbox',
65 | 'id': 'status',
66 | 'onChange': lambda e: handleChangeStatus(e, task),
67 | 'checked': task['status']
68 | }
69 | ),
70 | )
71 | else:
72 | return None
73 |
74 | def ListItems():
75 | return [el(ListItem, {'key': task['name'], 'task': task}) for task in taskList]
76 |
77 | def updateCount():
78 | if taskFilter == 'open':
79 | new_list = [task for task in taskList if not task['status']]
80 | elif taskFilter == 'closed':
81 | new_list = [task for task in taskList if task['status']]
82 | else:
83 | new_list = [task for task in taskList]
84 |
85 | setTaskCount(len(new_list))
86 |
87 | useEffect(lambda: setTitle("ToDo List"), [])
88 | useEffect(updateCount, [taskList, taskFilter])
89 |
90 | return el('form', {'onSubmit': handleSubmit},
91 | el('div', None, f"Number of Tasks: {taskCount}"),
92 | el('div', None,
93 | el('label', {'htmlFor': 'all'}, "All Tasks:"),
94 | el('input', {'type': 'radio',
95 | 'name': 'taskFilter',
96 | 'id': 'all',
97 | 'value': 'all',
98 | 'onChange': handleChange,
99 | 'checked': taskFilter == 'all'
100 | }
101 | ),
102 | el('label', {'htmlFor': 'open'}, " Active:"),
103 | el('input', {'type': 'radio',
104 | 'name': 'taskFilter',
105 | 'id': 'open',
106 | 'value': 'open',
107 | 'onChange': handleChange,
108 | 'checked': taskFilter == 'open'
109 | }
110 | ),
111 | el('label', {'htmlFor': 'closed'}, " Completed:"),
112 | el('input', {'type': 'radio',
113 | 'name': 'taskFilter',
114 | 'id': 'closed',
115 | 'value': 'closed',
116 | 'onChange': handleChange,
117 | 'checked': taskFilter == 'closed'
118 | }
119 | ),
120 | ),
121 | el('label', {'htmlFor': 'editBox'},
122 | "Edit Task: " if editTask is not None else "Add Task: "
123 | ),
124 | el('input', {'id': 'editBox',
125 | 'onChange': handleChange,
126 | 'value': newTask
127 | }
128 | ),
129 | el('input', {'type': 'submit'}),
130 | el('ol', None,
131 | el(ListItems, None)
132 | ),
133 | )
134 |
135 | render(App, None, 'root')
136 |
137 |
--------------------------------------------------------------------------------
/ch16/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch16/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch17",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "react": "^16.14.0",
16 | "react-dom": "^16.14.0"
17 | },
18 | "devDependencies": {
19 | "parcel-bundler": "^1.12.4",
20 | "parcel-plugin-transcrypt": "^1.0.20"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ch16/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 |
5 | # Map React javaScript objects to Python identifiers
6 | createElement = React.createElement
7 | useState = React.useState
8 | useEffect = React.useEffect
9 |
10 |
11 | def render(root_component, props, container):
12 | def main():
13 | ReactDOM.render(
14 | React.createElement(root_component, props),
15 | document.getElementById(container)
16 | )
17 |
18 | document.addEventListener('DOMContentLoaded', main)
19 |
20 |
21 | # JavaScript function mappings
22 | alert = window.alert
23 |
24 |
25 | def setTitle(title):
26 | document.title = title
27 |
28 |
--------------------------------------------------------------------------------
/ch16/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch16/todo.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | class App extends React.Component {
5 | constructor(props) {
6 | super(props);
7 | this.state = {
8 | newTask: "",
9 | editTask: null,
10 | taskList: Array(),
11 | taskCount: 0,
12 | taskFilter: "all"
13 | };
14 | }
15 |
16 | handleSubmit = (event) => {
17 | event.preventDefault();
18 | const taskList = this.state.taskList.slice();
19 | if (this.state.editTask) {
20 | const taskIndex = taskList.findIndex(
21 | (task => task.name === this.state.editTask.name)
22 | );
23 | taskList[taskIndex].name = this.state.newTask;
24 | } else {
25 | taskList.push({name: this.state.newTask, status: false});
26 | }
27 | this.setState({newTask: "", editTask: null, taskList: taskList});
28 | }
29 |
30 |
31 | handleEdit = (task) => {
32 | this.setState({newTask: task.name, editTask: task});
33 | }
34 |
35 | handleDelete = (task) => {
36 | const taskList = this.state.taskList.filter(function (item) {
37 | return item.name !== task.name;
38 | });
39 | this.setState({taskList: taskList});
40 | }
41 |
42 | handleChange = (event) => {
43 | if (event.target.name === "taskFilter") {
44 | this.setState({taskFilter: event.target.value});
45 | } else {
46 | this.setState({newTask: event.target.value});
47 | }
48 | }
49 |
50 | handleChangeStatus = (event, task) => {
51 | const taskList = this.state.taskList.slice();
52 | const taskIndex = taskList.findIndex((item => item.name === task.name));
53 | taskList[taskIndex].status = event.target.checked;
54 | this.setState({taskList: taskList});
55 | }
56 |
57 | renderTask = (task) => {
58 | const taskFilter = this.state.taskFilter
59 | if (taskFilter === "all" ||
60 | (taskFilter === "open" && !task.status) ||
61 | (taskFilter === "closed" && task.status)
62 | ) {
63 | return (
64 | {task.name}
65 |
70 |
75 |
76 | this.handleChangeStatus(event, task)}
79 | checked={task.status}
80 | />
81 |
82 | );
83 | } else {
84 | return null
85 | }
86 |
87 | }
88 |
89 | updateCount() {
90 | let taskList
91 | switch (this.state.taskFilter) {
92 | case "open":
93 | taskList = this.state.taskList.filter((task => !task.status));
94 | break;
95 | case "closed":
96 | taskList = this.state.taskList.filter((task => task.status));
97 | break;
98 | default:
99 | taskList = this.state.taskList.slice();
100 | }
101 |
102 | const taskCount = taskList.length;
103 | this.setState({taskCount: taskCount})
104 | }
105 |
106 | componentDidMount() {
107 | document.title = "ToDo List";
108 | }
109 |
110 | componentDidUpdate(prevProps, prevState) {
111 | if (prevState.taskList !== this.state.taskList ||
112 | prevState.taskFilter !== this.state.taskFilter
113 | ) {
114 | this.updateCount();
115 | }
116 | }
117 |
118 | render() {
119 | const taskFilter = this.state.taskFilter;
120 | return (
121 |
161 | );
162 | }
163 | }
164 |
165 | ReactDOM.render(, document.getElementById('root'));
166 |
167 |
--------------------------------------------------------------------------------
/ch17/app.css:
--------------------------------------------------------------------------------
1 | body{
2 | font-family: Arial, sans-serif;
3 | }
4 | .submitBtn {
5 | margin: 10px;
6 | color: darkgreen;
7 | width: 60px;
8 | }
9 | .deleteBtn {
10 | color: darkred;
11 | }
12 | .editBtn {
13 | color: blue;
14 | }
15 | button {
16 | margin-right: 5px;
17 | width: 60px;
18 | }
19 | .editing {
20 | color: blue;
21 | }
22 | .adding {
23 | color: darkgreen;
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/ch17/app.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 |
3 | def App():
4 | newItem, setNewItem = useState("")
5 | editItem, setEditItem = useState("")
6 | listItems, setListItems = useState([])
7 |
8 | def handleSubmit(event):
9 | event.preventDefault()
10 | new_list = list(listItems) # Make a copy
11 | if len(editItem) > 0: # In edit mode
12 | new_list[new_list.index(editItem)] = newItem
13 | else: # In add mode
14 | new_list.append(newItem) # Add the new item
15 | setListItems(new_list) # Update our state
16 | setNewItem("") # Clear the new item value
17 | setEditItem("") # Clear the edit item value
18 |
19 | def handleChange(event):
20 | target = event['target']
21 | setNewItem(target['value'])
22 |
23 | def handleDelete(item):
24 | new_list = list(listItems) # Make a copy
25 | new_list.remove(item) # Remove the specified item
26 | setListItems(new_list) # Update our state
27 |
28 | def handleEdit(item):
29 | setNewItem(item) # Set the new item value
30 | setEditItem(item) # Set the edit item value
31 |
32 | def ListItem(props):
33 | return el('li', {'className': 'list-group-item p-1'},
34 | el('button',
35 | {'onClick': lambda: handleDelete(props['item']),
36 | 'className': 'btn btn-danger btn-sm mr-2'
37 | },
38 | "Delete"
39 | ),
40 | el('button',
41 | {'onClick': lambda: handleEdit(props['item']),
42 | 'className': 'btn btn-primary btn-sm mr-2'
43 | },
44 | "Edit"
45 | ),
46 | props['item'],
47 | )
48 |
49 | def ListItems():
50 | return [el(ListItem, {'key': item, 'item': item}) for item in listItems]
51 |
52 | if len(editItem) == 0:
53 | editClass = 'text-success'
54 | else:
55 | editClass = 'text-primary'
56 |
57 | return el('div', {'className': 'container m-1'},
58 | el('form', {'onSubmit': handleSubmit,
59 | 'className': 'form-inline col-10 my-2'
60 | },
61 | el('label',
62 | {'htmlFor': 'editBox', 'className': editClass},
63 | "Add Item: " if len(editItem) == 0 else "Edit Item: "
64 | ),
65 | el('input', {'id': 'editBox',
66 | 'onChange': handleChange,
67 | 'value': newItem,
68 | 'className': 'form-control ml-2'
69 | }
70 | ),
71 | el('input', {'type': 'submit',
72 | 'className': 'btn btn-success ml-2'
73 | }
74 | ),
75 | ),
76 | el('ul', {'className': 'list-group col-10 ml-2'},
77 | el(ListItems, None)
78 | ),
79 | )
80 |
81 | render(App, None, 'root')
82 |
83 |
--------------------------------------------------------------------------------
/ch17/app_v1.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 |
3 | def App():
4 | newItem, setNewItem = useState("")
5 | editItem, setEditItem = useState("")
6 | listItems, setListItems = useState([])
7 |
8 | def handleSubmit(event):
9 | event.preventDefault()
10 | new_list = list(listItems) # Make a copy
11 | if len(editItem) > 0: # In edit mode
12 | new_list[new_list.index(editItem)] = newItem
13 | else: # In add mode
14 | new_list.append(newItem) # Add the new item
15 | setListItems(new_list) # Update our state
16 | setNewItem("") # Clear the new item value
17 | setEditItem("") # Clear the edit item value
18 |
19 | def handleChange(event):
20 | target = event['target']
21 | setNewItem(target['value'])
22 |
23 | def handleDelete(item):
24 | new_list = list(listItems) # Make a copy
25 | new_list.remove(item) # Remove the specified item
26 | setListItems(new_list) # Update our state
27 |
28 | def handleEdit(item):
29 | setNewItem(item) # Set the new item value
30 | setEditItem(item) # Set the edit item value
31 |
32 | def ListItem(props):
33 | return el('li', None,
34 | el('button', {'type': 'button',
35 | 'onClick': lambda: handleDelete(props['item']),
36 | 'className': 'deleteBtn'
37 | }, "Delete"
38 | ),
39 | el('button', {'type': 'button',
40 | 'onClick': lambda: handleEdit(props['item']),
41 | 'className': 'editBtn'
42 | }, "Edit"
43 | ),
44 | props['item'],
45 | )
46 |
47 | def ListItems():
48 | return [el(ListItem, {'key': item, 'item': item}) for item in listItems]
49 |
50 | return el('form', {'onSubmit': handleSubmit},
51 | el('label',
52 | {'htmlFor': 'editBox',
53 | 'className': 'adding' if len(editItem) == 0 else 'editing'
54 | },
55 | "Add Item: " if len(editItem) == 0 else "Edit Item: "
56 | ),
57 | el('input', {'id': 'editBox',
58 | 'onChange': handleChange,
59 | 'value': newItem
60 | }
61 | ),
62 | el('input', {'type': 'submit', 'className': 'submitBtn'}),
63 | el('ol', None,
64 | el(ListItems, None)
65 | ),
66 | )
67 |
68 | render(App, None, 'root')
69 |
70 |
--------------------------------------------------------------------------------
/ch17/app_v2.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 |
3 | def App():
4 | newItem, setNewItem = useState("")
5 | editItem, setEditItem = useState("")
6 | listItems, setListItems = useState([])
7 |
8 | def handleSubmit(event):
9 | event.preventDefault()
10 | new_list = list(listItems) # Make a copy
11 | if len(editItem) > 0: # In edit mode
12 | new_list[new_list.index(editItem)] = newItem
13 | else: # In add mode
14 | new_list.append(newItem) # Add the new item
15 | setListItems(new_list) # Update our state
16 | setNewItem("") # Clear the new item value
17 | setEditItem("") # Clear the edit item value
18 |
19 | def handleChange(event):
20 | target = event['target']
21 | setNewItem(target['value'])
22 |
23 | def handleDelete(item):
24 | new_list = list(listItems) # Make a copy
25 | new_list.remove(item) # Remove the specified item
26 | setListItems(new_list) # Update our state
27 |
28 | def handleEdit(item):
29 | setNewItem(item) # Set the new item value
30 | setEditItem(item) # Set the edit item value
31 |
32 | def ListItem(props):
33 | return el('li', None,
34 | el('button', {
35 | 'type': 'button',
36 | 'onClick': lambda: handleDelete(props['item']),
37 | 'style': {'color': 'darkred',
38 | 'marginRight': '5px',
39 | 'width': '60px'
40 | }
41 | }, "Delete"
42 | ),
43 | el('button', {
44 | 'type': 'button',
45 | 'onClick': lambda: handleEdit(props['item']),
46 | 'style': {'color': 'blue',
47 | 'marginRight': '5px',
48 | 'width': '60px'
49 | }
50 | }, "Edit"
51 | ),
52 | props['item'],
53 | )
54 |
55 | def ListItems():
56 | return [el(ListItem, {'key': item, 'item': item}) for item in listItems]
57 |
58 | if len(editItem) == 0:
59 | editStyle = {'color': 'darkgreen'}
60 | else:
61 | editStyle = {'color': 'blue'}
62 |
63 | return el('form', {'onSubmit': handleSubmit,
64 | 'style': {'fontFamily': 'Arial, sans-serif'}
65 | },
66 | el('label',
67 | {'htmlFor': 'editBox', 'style': editStyle},
68 | "Add Item: " if len(editItem) == 0 else "Edit Item: "
69 | ),
70 | el('input', {'id': 'editBox',
71 | 'onChange': handleChange,
72 | 'value': newItem
73 | }
74 | ),
75 | el('input', {'type': 'submit',
76 | 'style': {'margin': '10px',
77 | 'color': 'darkgreen',
78 | 'width': '60px'
79 | }
80 | }
81 | ),
82 | el('ol', None,
83 | el(ListItems, None)
84 | ),
85 | )
86 |
87 | render(App, None, 'root')
88 |
89 |
--------------------------------------------------------------------------------
/ch17/app_v3.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 |
3 | def App():
4 | newItem, setNewItem = useState("")
5 | editItem, setEditItem = useState("")
6 | listItems, setListItems = useState([])
7 |
8 | def handleSubmit(event):
9 | event.preventDefault()
10 | new_list = list(listItems) # Make a copy
11 | if len(editItem) > 0: # In edit mode
12 | new_list[new_list.index(editItem)] = newItem
13 | else: # In add mode
14 | new_list.append(newItem) # Add the new item
15 | setListItems(new_list) # Update our state
16 | setNewItem("") # Clear the new item value
17 | setEditItem("") # Clear the edit item value
18 |
19 | def handleChange(event):
20 | target = event['target']
21 | setNewItem(target['value'])
22 |
23 | def handleDelete(item):
24 | new_list = list(listItems) # Make a copy
25 | new_list.remove(item) # Remove the specified item
26 | setListItems(new_list) # Update our state
27 |
28 | def handleEdit(item):
29 | setNewItem(item) # Set the new item value
30 | setEditItem(item) # Set the edit item value
31 |
32 | def Button(props):
33 | new_props = {'type': 'button'}
34 | new_props.update(props)
35 | new_style = new_props.pop('style', {})
36 | new_style.update({'marginRight': '5px', 'width': '60px'})
37 | new_props.update({'style': new_style})
38 | return el('button', new_props)
39 |
40 | def ListItem(props):
41 | return el('li', None,
42 | el(Button,
43 | {'onClick': lambda: handleDelete(props['item']),
44 | 'style': {'color': 'darkred'}
45 | },
46 | "Delete"
47 | ),
48 | el(Button,
49 | {'onClick': lambda: handleEdit(props['item']),
50 | 'style': {'color': 'blue'}
51 | },
52 | "Edit"
53 | ),
54 | props['item'],
55 | )
56 |
57 | def ListItems():
58 | return [el(ListItem, {'key': item, 'item': item}) for item in listItems]
59 |
60 | if len(editItem) == 0:
61 | editStyle = {'color': 'darkgreen'}
62 | else:
63 | editStyle = {'color': 'blue'}
64 |
65 | return el('form', {'onSubmit': handleSubmit,
66 | 'style': {'fontFamily': 'Arial, sans-serif'}
67 | },
68 | el('label',
69 | {'htmlFor': 'editBox', 'style': editStyle},
70 | "Add Item: " if len(editItem) == 0 else "Edit Item: "
71 | ),
72 | el('input', {'id': 'editBox',
73 | 'onChange': handleChange,
74 | 'value': newItem
75 | }
76 | ),
77 | el('input', {'type': 'submit',
78 | 'style': {'margin': '10px',
79 | 'color': 'darkgreen',
80 | 'width': '60px'
81 | }
82 | }
83 | ),
84 | el('ol', None,
85 | el(ListItems, None)
86 | ),
87 | )
88 |
89 | render(App, None, 'root')
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ch17/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ch17/index_v1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ch17/index_v2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ch17/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch18",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "react": "^16.14.0",
16 | "react-dom": "^16.14.0"
17 | },
18 | "devDependencies": {
19 | "parcel-bundler": "^1.12.4",
20 | "parcel-plugin-transcrypt": "^1.0.20"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ch17/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 |
5 | # Map React javaScript objects to Python identifiers
6 | createElement = React.createElement
7 | useState = React.useState
8 | useEffect = React.useEffect
9 |
10 |
11 | def render(root_component, props, container):
12 | def main():
13 | ReactDOM.render(
14 | React.createElement(root_component, props),
15 | document.getElementById(container)
16 | )
17 |
18 | document.addEventListener('DOMContentLoaded', main)
19 |
20 |
21 | # JavaScript function mappings
22 | alert = window.alert
23 |
24 |
25 | def setTitle(title):
26 | document.title = title
27 |
28 |
--------------------------------------------------------------------------------
/ch17/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch18/app.py:
--------------------------------------------------------------------------------
1 | from appTheme import theme, useStyle, ListButton, StyledButton
2 | from pyreact import useState, render, createElement as el
3 | from pymui import Button, List, ListItem, Typography, Box, TextField
4 | from pymui import Paper, Container, AppBar, ThemeProvider, useTheme
5 |
6 | def App():
7 | newItem, setNewItem = useState("")
8 | editItem, setEditItem = useState("")
9 | listItems, setListItems = useState([])
10 |
11 | def handleSubmit(event):
12 | event.preventDefault()
13 | new_list = list(listItems) # Make a copy
14 | if len(editItem) > 0: # In edit mode
15 | new_list[new_list.index(editItem)] = newItem
16 | else: # In add mode
17 | new_list.append(newItem) # Add the new item
18 | setListItems(new_list) # Update our state
19 | setNewItem("") # Clear the new item value
20 | setEditItem("") # Clear the edit item value
21 |
22 | def handleChange(event):
23 | target = event['target']
24 | setNewItem(target['value'])
25 |
26 | def handleDelete(item):
27 | new_list = list(listItems) # Make a copy
28 | new_list.remove(item) # Remove the specified item
29 | setListItems(new_list) # Update our state
30 |
31 | def handleEdit(item):
32 | setNewItem(item) # Set the new item value
33 | setEditItem(item) # Set the edit item value
34 |
35 | def ItemVu(props):
36 | item = props['item']
37 | theme = useTheme()
38 | specialColor = theme['palette']['special']['main']
39 | classes = useStyle({'bgcolor': specialColor})
40 |
41 | return el(ListItem, {'dense': True},
42 | el(Button,
43 | {'color': 'secondary',
44 | 'className': classes.root,
45 | 'onClick': lambda: handleDelete(item)
46 | },
47 | el('span', {'className': 'material-icons'}, 'delete'),
48 | "Delete"
49 | ),
50 | el(StyledButton,
51 | {'color': 'primary',
52 | 'onClick': lambda: handleEdit(item)
53 | },
54 | el('span', {'className': 'material-icons'}, 'edit'),
55 | "Edit"
56 | ),
57 | el(Typography, {'style': {'color': specialColor}}, item)
58 | )
59 |
60 | def ListItemsVu():
61 | return [el(ItemVu, {'key': item, 'item': item}) for item in listItems]
62 |
63 | if len(editItem) == 0:
64 | editColor = 'secondary'
65 | editLabel = "Add Item:"
66 | else:
67 | editColor = 'primary'
68 | editLabel = "Edit Item:"
69 |
70 | return el(ThemeProvider, {'theme': theme},
71 | el(Container, {'maxWidth': 'sm'},
72 | el(AppBar, {'position': 'static',
73 | 'style': {'marginBottom': '0.5rem'}
74 | },
75 | el(Box, {'width': '100%', 'marginLeft': '0.5rem'},
76 | el(Typography, {'variant': 'h6'}, "React to Python")
77 | )
78 | ),
79 | el(Paper, {'elevation': 2},
80 | el('form', {'onSubmit': handleSubmit,
81 | 'style': {'marginLeft': '1rem'}
82 | },
83 | el(TextField, {'InputLabelProps': {'color': editColor},
84 | 'label': editLabel,
85 | 'value': newItem,
86 | 'onChange': handleChange,
87 | 'autoFocus': True
88 | }
89 | ),
90 | el(Button, {'type': 'submit',
91 | 'size': 'medium'
92 | }, "Submit"),
93 | ),
94 | el(List, None,
95 | el(ListItemsVu, None)
96 | ),
97 | )
98 | )
99 | )
100 |
101 | render(App, None, 'root')
102 |
103 |
--------------------------------------------------------------------------------
/ch18/appTheme.py:
--------------------------------------------------------------------------------
1 | from pymui import createMuiTheme, colors, makeStyles, styled, Button
2 | from pyreact import createElement as el
3 |
4 | theme = createMuiTheme({
5 | 'palette': {
6 | 'primary': colors['teal'],
7 | 'secondary': colors['pink'],
8 | 'special': {
9 | 'main': colors['deepPurple'][600],
10 | 'contrastText': colors['common']['white'],
11 | },
12 | },
13 | 'overrides': {
14 | 'MuiButton': {
15 | 'root': {
16 | 'margin': '0.5rem'
17 | },
18 | },
19 | },
20 | 'props': {
21 | 'MuiButton': {
22 | 'variant': 'contained',
23 | 'size': 'small',
24 | },
25 | 'MuiTextField': {
26 | 'type': 'text',
27 | 'variant': 'outlined',
28 | 'InputLabelProps': {'shrink': True},
29 | 'InputProps': {'margin': 'dense'},
30 | 'margin': 'dense',
31 | },
32 | },
33 | })
34 |
35 | StyledButton = styled(Button)({
36 | 'minWidth': '6rem',
37 | 'margin': '0 0.5rem 0 0',
38 | '&:hover': {'backgroundColor': theme['palette']['special']['main']}
39 | })
40 |
41 | useStyle = makeStyles({
42 | 'root': {
43 | 'minWidth': '6rem',
44 | 'margin': '0 0.5rem 0 0',
45 | '&:hover': {'backgroundColor': lambda props: props['bgcolor']}
46 | }
47 | })
48 |
49 | def ListButton(props):
50 | new_props = {'style': {'minWidth': '6rem', 'margin': '0 0.5rem 0 0'}}
51 | new_props.update(props)
52 | return el(Button, new_props)
53 |
54 |
--------------------------------------------------------------------------------
/ch18/appTheme_v1.py:
--------------------------------------------------------------------------------
1 | from pymui import createMuiTheme, colors
2 |
3 | theme = createMuiTheme({
4 | 'palette': {
5 | 'primary': colors['teal'],
6 | 'secondary': colors['pink'],
7 | 'special': {
8 | 'main': colors['deepPurple'][600],
9 | 'contrastText': colors['common']['white'],
10 | },
11 | },
12 | 'overrides': {
13 | 'MuiButton': {
14 | 'root': {
15 | 'margin': '0.5rem'
16 | },
17 | },
18 | },
19 | 'props': {
20 | 'MuiButton': {
21 | 'variant': 'contained',
22 | 'size': 'small',
23 | },
24 | 'MuiTextField': {
25 | 'type': 'text',
26 | 'variant': 'outlined',
27 | 'InputLabelProps': {'shrink': True},
28 | 'InputProps': {'margin': 'dense'},
29 | 'margin': 'dense',
30 | },
31 | },
32 | })
33 |
34 |
--------------------------------------------------------------------------------
/ch18/app_v1.py:
--------------------------------------------------------------------------------
1 | from pyreact import useState, render, createElement as el
2 | from pymui import Button, List, ListItem, Typography, InputLabel, Input, Box
3 |
4 | def App():
5 | newItem, setNewItem = useState("")
6 | editItem, setEditItem = useState("")
7 | listItems, setListItems = useState([])
8 |
9 | def handleSubmit(event):
10 | event.preventDefault()
11 | new_list = list(listItems) # Make a copy
12 | if len(editItem) > 0: # In edit mode
13 | new_list[new_list.index(editItem)] = newItem
14 | else: # In add mode
15 | new_list.append(newItem) # Add the new item
16 | setListItems(new_list) # Update our state
17 | setNewItem("") # Clear the new item value
18 | setEditItem("") # Clear the edit item value
19 |
20 | def handleChange(event):
21 | target = event['target']
22 | setNewItem(target['value'])
23 |
24 | def handleDelete(item):
25 | new_list = list(listItems) # Make a copy
26 | new_list.remove(item) # Remove the specified item
27 | setListItems(new_list) # Update our state
28 |
29 | def handleEdit(item):
30 | setNewItem(item) # Set the new item value
31 | setEditItem(item) # Set the edit item value
32 |
33 | def ItemVu(props):
34 | return el(ListItem, {'dense': True},
35 | el(Button,
36 | {'variant': 'contained',
37 | 'color': 'secondary',
38 | 'size': 'small',
39 | 'style': {'marginRight': '0.5rem'},
40 | 'onClick': lambda: handleDelete(props['item']),
41 | },
42 | "Delete"
43 | ),
44 | el(Button,
45 | {'variant': 'contained',
46 | 'color': 'primary',
47 | 'size': 'small',
48 | 'style': {'marginRight': '0.5rem'},
49 | 'onClick': lambda: handleEdit(props['item']),
50 | },
51 | "Edit"
52 | ),
53 | el(Typography, {'variant': 'body1'}, props['item'])
54 | )
55 |
56 | def ListItemsVu():
57 | return [el(ItemVu, {'key': item, 'item': item}) for item in listItems]
58 |
59 | if len(editItem) == 0:
60 | editColor = 'secondary'
61 | editLabel = "Add Item:"
62 | else:
63 | editColor = 'primary'
64 | editLabel = "Edit Item:"
65 |
66 | return el(Box, None,
67 | el('form', {'onSubmit': handleSubmit},
68 | el(InputLabel,
69 | {'htmlFor': 'editBox', 'color': editColor},
70 | editLabel
71 | ),
72 | el(Input, {'id': 'editBox',
73 | 'onChange': handleChange,
74 | 'value': newItem
75 | }
76 | ),
77 | el(Button, {'type': 'submit',
78 | 'variant': 'contained',
79 | 'style': {'marginLeft': '0.5rem'}
80 | }, "Submit"),
81 | ),
82 | el(List, None,
83 | el(ListItemsVu, None)
84 | ),
85 | )
86 |
87 | render(App, None, 'root')
88 |
89 |
--------------------------------------------------------------------------------
/ch18/app_v2.py:
--------------------------------------------------------------------------------
1 | from appTheme import theme
2 | from pyreact import useState, render, createElement as el
3 | from pymui import Button, List, ListItem, Typography, Box, TextField
4 | from pymui import Paper, Container, AppBar, ThemeProvider, useTheme
5 |
6 | def App():
7 | newItem, setNewItem = useState("")
8 | editItem, setEditItem = useState("")
9 | listItems, setListItems = useState([])
10 |
11 | def handleSubmit(event):
12 | event.preventDefault()
13 | new_list = list(listItems) # Make a copy
14 | if len(editItem) > 0: # In edit mode
15 | new_list[new_list.index(editItem)] = newItem
16 | else: # In add mode
17 | new_list.append(newItem) # Add the new item
18 | setListItems(new_list) # Update our state
19 | setNewItem("") # Clear the new item value
20 | setEditItem("") # Clear the edit item value
21 |
22 | def handleChange(event):
23 | target = event['target']
24 | setNewItem(target['value'])
25 |
26 | def handleDelete(item):
27 | new_list = list(listItems) # Make a copy
28 | new_list.remove(item) # Remove the specified item
29 | setListItems(new_list) # Update our state
30 |
31 | def handleEdit(item):
32 | setNewItem(item) # Set the new item value
33 | setEditItem(item) # Set the edit item value
34 |
35 | def ItemVu(props):
36 | item = props['item']
37 | current_theme = useTheme()
38 | specialColor = current_theme['palette']['special']['main']
39 | button_style = {'margin': '0 0.5rem 0 0'}
40 |
41 | return el(ListItem, {'dense': True},
42 | el(Button,
43 | {'color': 'secondary',
44 | 'style': button_style,
45 | 'onClick': lambda: handleDelete(item)
46 | },
47 | el('span', {'className': 'material-icons'}, 'delete'),
48 | "Delete"
49 | ),
50 | el(Button,
51 | {'color': 'primary',
52 | 'style': button_style,
53 | 'onClick': lambda: handleEdit(item)
54 | },
55 | el('span', {'className': 'material-icons'}, 'edit'),
56 | "Edit"
57 | ),
58 | el(Typography, {'style': {'color': specialColor}}, item)
59 | )
60 |
61 | def ListItemsVu():
62 | return [el(ItemVu, {'key': item, 'item': item}) for item in listItems]
63 |
64 | if len(editItem) == 0:
65 | editColor = 'secondary'
66 | editLabel = "Add Item:"
67 | else:
68 | editColor = 'primary'
69 | editLabel = "Edit Item:"
70 |
71 | return el(ThemeProvider, {'theme': theme},
72 | el(Container, {'maxWidth': 'sm'},
73 | el(AppBar, {'position': 'static',
74 | 'style': {'marginBottom': '0.5rem'}
75 | },
76 | el(Box, {'width': '100%', 'marginLeft': '0.5rem'},
77 | el(Typography, {'variant': 'h6'}, "React to Python")
78 | )
79 | ),
80 | el(Paper, {'elevation': 2},
81 | el('form', {'onSubmit': handleSubmit,
82 | 'style': {'marginLeft': '1rem'}
83 | },
84 | el(TextField, {'InputLabelProps': {'color': editColor},
85 | 'label': editLabel,
86 | 'value': newItem,
87 | 'onChange': handleChange,
88 | 'autoFocus': True
89 | }
90 | ),
91 | el(Button, {'type': 'submit',
92 | 'size': 'medium'
93 | }, "Submit"),
94 | ),
95 | el(List, None,
96 | el(ListItemsVu, None)
97 | ),
98 | )
99 | )
100 | )
101 |
102 | render(App, None, 'root')
103 |
104 |
--------------------------------------------------------------------------------
/ch18/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ch18/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch19",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "@material-ui/core": "^4.11.0",
16 | "react": "^16.14.0",
17 | "react-dom": "^16.14.0"
18 | },
19 | "devDependencies": {
20 | "parcel-bundler": "^1.12.4",
21 | "parcel-plugin-transcrypt": "^1.0.20"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ch18/pymui.py:
--------------------------------------------------------------------------------
1 | # Basic MUI components
2 | Button = require('@material-ui/core/Button')['default']
3 | List = require('@material-ui/core/List')['default']
4 | ListItem = require('@material-ui/core/ListItem')['default']
5 | Typography = require('@material-ui/core/Typography')['default']
6 | Input = require('@material-ui/core/Input')['default']
7 | InputLabel = require('@material-ui/core/InputLabel')['default']
8 | Box = require('@material-ui/core/Box')['default']
9 | TextField = require('@material-ui/core/TextField')['default']
10 | Paper = require('@material-ui/core/Paper')['default']
11 | AppBar = require('@material-ui/core/AppBar')['default']
12 | Container = require('@material-ui/core/Container')['default']
13 |
14 | # Theming
15 | ThemeProvider = require('@material-ui/styles/ThemeProvider')['default']
16 | useTheme = require('@material-ui/styles/useTheme')['default']
17 | createMuiTheme = require('@material-ui/core/styles/createMuiTheme')['default']
18 | colors = require('@material-ui/core/colors')
19 | makeStyles = require('@material-ui/styles/makeStyles')['default']
20 | styled = require('@material-ui/styles/styled')['default']
21 |
22 |
--------------------------------------------------------------------------------
/ch18/pymui_v1.py:
--------------------------------------------------------------------------------
1 | MaterialUI = require('@material-ui/core')
2 |
3 | # Basic MUI components
4 | Button = MaterialUI.Button
5 | List = MaterialUI.List
6 | ListItem = MaterialUI.ListItem
7 | Typography = MaterialUI.Typography
8 | Input = MaterialUI.Input
9 | InputLabel = MaterialUI.InputLabel
10 | Box = MaterialUI.Box
11 |
12 |
--------------------------------------------------------------------------------
/ch18/pymui_v2.py:
--------------------------------------------------------------------------------
1 | # Basic MUI components
2 | Button = require('@material-ui/core/Button')['default']
3 | List = require('@material-ui/core/List')['default']
4 | ListItem = require('@material-ui/core/ListItem')['default']
5 | Typography = require('@material-ui/core/Typography')['default']
6 | Input = require('@material-ui/core/Input')['default']
7 | InputLabel = require('@material-ui/core/InputLabel')['default']
8 | Box = require('@material-ui/core/Box')['default']
9 |
10 |
--------------------------------------------------------------------------------
/ch18/pymui_v3.py:
--------------------------------------------------------------------------------
1 | # Basic MUI components
2 | Button = require('@material-ui/core/Button')['default']
3 | List = require('@material-ui/core/List')['default']
4 | ListItem = require('@material-ui/core/ListItem')['default']
5 | Typography = require('@material-ui/core/Typography')['default']
6 | Input = require('@material-ui/core/Input')['default']
7 | InputLabel = require('@material-ui/core/InputLabel')['default']
8 | Box = require('@material-ui/core/Box')['default']
9 | TextField = require('@material-ui/core/TextField')['default']
10 | Paper = require('@material-ui/core/Paper')['default']
11 | AppBar = require('@material-ui/core/AppBar')['default']
12 | Container = require('@material-ui/core/Container')['default']
13 |
14 | # Theming
15 | ThemeProvider = require('@material-ui/styles/ThemeProvider')['default']
16 | useTheme = require('@material-ui/styles/useTheme')['default']
17 | createMuiTheme = require('@material-ui/core/styles/createMuiTheme')['default']
18 | colors = require('@material-ui/core/colors')
19 |
20 |
--------------------------------------------------------------------------------
/ch18/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 |
5 | # Map React javaScript objects to Python identifiers
6 | createElement = React.createElement
7 | useState = React.useState
8 | useEffect = React.useEffect
9 |
10 |
11 | def render(root_component, props, container):
12 | def main():
13 | ReactDOM.render(
14 | React.createElement(root_component, props),
15 | document.getElementById(container)
16 | )
17 |
18 | document.addEventListener('DOMContentLoaded', main)
19 |
20 |
21 | # JavaScript function mappings
22 | alert = window.alert
23 |
24 |
25 | def setTitle(title):
26 | document.title = title
27 |
28 |
--------------------------------------------------------------------------------
/ch18/requirements.txt:
--------------------------------------------------------------------------------
1 | Transcrypt==3.7.16
2 |
--------------------------------------------------------------------------------
/ch19/app.py:
--------------------------------------------------------------------------------
1 | from appTheme import theme, useStyle, ListButton, StyledButton
2 | from pyreact import useState, render, createElement as el
3 | from pymui import Button, List, ListItem, Typography, Box, TextField
4 | from pymui import Paper, Container, AppBar, ThemeProvider, useTheme
5 |
6 | def App():
7 | newItem, setNewItem = useState("")
8 | editItem, setEditItem = useState("")
9 | listItems, setListItems = useState([])
10 |
11 | def handleSubmit(event):
12 | event.preventDefault()
13 | new_list = list(listItems) # Make a copy
14 | if len(editItem) > 0: # In edit mode
15 | new_list[new_list.index(editItem)] = newItem
16 | else: # In add mode
17 | new_list.append(newItem) # Add the new item
18 | setListItems(new_list) # Update our state
19 | setNewItem("") # Clear the new item value
20 | setEditItem("") # Clear the edit item value
21 |
22 | def handleChange(event):
23 | target = event['target']
24 | setNewItem(target['value'])
25 |
26 | def handleDelete(item):
27 | new_list = list(listItems) # Make a copy
28 | new_list.remove(item) # Remove the specified item
29 | setListItems(new_list) # Update our state
30 |
31 | def handleEdit(item):
32 | setNewItem(item) # Set the new item value
33 | setEditItem(item) # Set the edit item value
34 |
35 | def ItemVu(props):
36 | item = props['item']
37 | theme = useTheme()
38 | specialColor = theme['palette']['special']['main']
39 | classes = useStyle({'bgcolor': specialColor})
40 |
41 | return el(ListItem, {'dense': True},
42 | el(Button,
43 | {'color': 'secondary',
44 | 'className': classes.root,
45 | 'onClick': lambda: handleDelete(item)
46 | },
47 | el('span', {'className': 'material-icons'}, 'delete'),
48 | "Delete"
49 | ),
50 | el(StyledButton,
51 | {'color': 'primary',
52 | 'onClick': lambda: handleEdit(item)
53 | },
54 | el('span', {'className': 'material-icons'}, 'edit'),
55 | "Edit"
56 | ),
57 | el(Typography, {'style': {'color': specialColor}}, item)
58 | )
59 |
60 | def ListItemsVu():
61 | return [el(ItemVu, {'key': item, 'item': item}) for item in listItems]
62 |
63 | if len(editItem) == 0:
64 | editColor = 'secondary'
65 | editLabel = "Add Item:"
66 | else:
67 | editColor = 'primary'
68 | editLabel = "Edit Item:"
69 |
70 | return el(ThemeProvider, {'theme': theme},
71 | el(Container, {'maxWidth': 'sm'},
72 | el(AppBar, {'position': 'static',
73 | 'style': {'marginBottom': '0.5rem'}
74 | },
75 | el(Box, {'width': '100%', 'marginLeft': '0.5rem'},
76 | el(Typography, {'variant': 'h6'}, "React to Python")
77 | )
78 | ),
79 | el(Paper, {'elevation': 2},
80 | el('form', {'onSubmit': handleSubmit,
81 | 'style': {'marginLeft': '1rem'}
82 | },
83 | el(TextField, {'InputLabelProps': {'color': editColor},
84 | 'label': editLabel,
85 | 'value': newItem,
86 | 'onChange': handleChange,
87 | 'autoFocus': True
88 | }
89 | ),
90 | el(Button, {'type': 'submit',
91 | 'size': 'medium'
92 | }, "Submit"),
93 | ),
94 | el(List, None,
95 | el(ListItemsVu, None)
96 | ),
97 | )
98 | )
99 | )
100 |
101 | render(App, None, 'root')
102 |
103 |
--------------------------------------------------------------------------------
/ch19/appTheme.py:
--------------------------------------------------------------------------------
1 | from pymui import createMuiTheme, colors, makeStyles, styled, Button
2 | from pyreact import createElement as el
3 |
4 | theme = createMuiTheme({
5 | 'palette': {
6 | 'primary': colors['teal'],
7 | 'secondary': colors['pink'],
8 | 'special': {
9 | 'main': colors['deepPurple'][600],
10 | 'contrastText': colors['common']['white'],
11 | },
12 | },
13 | 'overrides': {
14 | 'MuiButton': {
15 | 'root': {
16 | 'margin': '0.5rem'
17 | },
18 | },
19 | },
20 | 'props': {
21 | 'MuiButton': {
22 | 'variant': 'contained',
23 | 'size': 'small',
24 | },
25 | 'MuiTextField': {
26 | 'type': 'text',
27 | 'variant': 'outlined',
28 | 'InputLabelProps': {'shrink': True},
29 | 'InputProps': {'margin': 'dense'},
30 | 'margin': 'dense',
31 | },
32 | },
33 | })
34 |
35 | StyledButton = styled(Button)({
36 | 'minWidth': '6rem',
37 | 'margin': '0 0.5rem 0 0',
38 | '&:hover': {'backgroundColor': theme['palette']['special']['main']}
39 | })
40 |
41 | useStyle = makeStyles({
42 | 'root': {
43 | 'minWidth': '6rem',
44 | 'margin': '0 0.5rem 0 0',
45 | '&:hover': {'backgroundColor': lambda props: props['bgcolor']}
46 | }
47 | })
48 |
49 | def ListButton(props):
50 | new_props = {'style': {'minWidth': '6rem', 'margin': '0 0.5rem 0 0'}}
51 | new_props.update(props)
52 | return el(Button, new_props)
53 |
54 |
--------------------------------------------------------------------------------
/ch19/dev-server.js:
--------------------------------------------------------------------------------
1 | const Bundler = require('parcel-bundler');
2 | const express = require('express');
3 | const { createProxyMiddleware } = require('http-proxy-middleware');
4 |
5 |
6 | const app = express();
7 |
8 | const apiProxy = createProxyMiddleware('/api', {
9 | target: 'http://localhost:8000',
10 | pathRewrite: {'^/api': ''}
11 | });
12 | app.use(apiProxy);
13 |
14 | // parcel options
15 | const options = {minify:false, cache: false, outDir: 'dist/dev', logLevel: 4};
16 |
17 | const bundler = new Bundler('./index.html', options);
18 | app.use(bundler.middleware());
19 |
20 | bundler.on('buildEnd', () => {
21 | console.log('Parcel proxy server has started at: http://localhost:8080');
22 | });
23 |
24 | app.listen(8080);
25 |
26 |
--------------------------------------------------------------------------------
/ch19/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ch19/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch20",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "dev": "node dev-server.js",
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "dependencies": {
16 | "@material-ui/core": "^4.11.0",
17 | "react": "^16.14.0",
18 | "react-dom": "^16.14.0"
19 | },
20 | "devDependencies": {
21 | "express": "^4.17.1",
22 | "http-proxy-middleware": "^1.0.6",
23 | "parcel-bundler": "^1.12.4",
24 | "parcel-plugin-transcrypt": "^1.0.20"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ch19/pymui.py:
--------------------------------------------------------------------------------
1 | # Basic MUI components
2 | Button = require('@material-ui/core/Button')['default']
3 | List = require('@material-ui/core/List')['default']
4 | ListItem = require('@material-ui/core/ListItem')['default']
5 | Typography = require('@material-ui/core/Typography')['default']
6 | Input = require('@material-ui/core/Input')['default']
7 | InputLabel = require('@material-ui/core/InputLabel')['default']
8 | Box = require('@material-ui/core/Box')['default']
9 | TextField = require('@material-ui/core/TextField')['default']
10 | Paper = require('@material-ui/core/Paper')['default']
11 | AppBar = require('@material-ui/core/AppBar')['default']
12 | Container = require('@material-ui/core/Container')['default']
13 |
14 | # Theming
15 | ThemeProvider = require('@material-ui/styles/ThemeProvider')['default']
16 | useTheme = require('@material-ui/styles/useTheme')['default']
17 | createMuiTheme = require('@material-ui/core/styles/createMuiTheme')['default']
18 | colors = require('@material-ui/core/colors')
19 | makeStyles = require('@material-ui/styles/makeStyles')['default']
20 | styled = require('@material-ui/styles/styled')['default']
21 |
22 |
--------------------------------------------------------------------------------
/ch19/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 |
5 | # Map React javaScript objects to Python identifiers
6 | createElement = React.createElement
7 | useState = React.useState
8 | useEffect = React.useEffect
9 |
10 |
11 | def render(root_component, props, container):
12 | def main():
13 | ReactDOM.render(
14 | React.createElement(root_component, props),
15 | document.getElementById(container)
16 | )
17 |
18 | document.addEventListener('DOMContentLoaded', main)
19 |
20 |
21 | # JavaScript function mappings
22 | alert = window.alert
23 |
24 |
25 | def setTitle(title):
26 | document.title = title
27 |
28 |
--------------------------------------------------------------------------------
/ch19/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask>=2.0.0
2 | Transcrypt==3.7.16
3 |
4 |
--------------------------------------------------------------------------------
/ch19/webserver.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, jsonify
2 |
3 | users = [
4 | {"ID": 1, "FirstName": "Valeria", "LastName": "Lammerding",
5 | "Email": "vlammerding0@flickr.com", "JobTitle": "Geologist III",
6 | "Username": "vlammerding0", "Active": False},
7 | {"ID": 2, "FirstName": "Bond", "LastName": "Tomczynski",
8 | "Email": "btomczynski1@ehow.com", "JobTitle": "Environmental Specialist",
9 | "Username": "btomczynski1", "Active": True},
10 | {"ID": 3, "FirstName": "Nowell", "LastName": "Triplet",
11 | "Email": "ntriplet2@sciencedirect.com", "JobTitle": "Business Analyst",
12 | "Username": "ntriplet2", "Active": False},
13 | {"ID": 4, "FirstName": "Patience", "LastName": "Boulds",
14 | "Email": "pboulds3@reverbnation.com", "JobTitle": "Assistant Manager",
15 | "Username": "pboulds3", "Active": True},
16 | {"ID": 5, "FirstName": "Darelle", "LastName": "Lemonby",
17 | "Email": "dlemonby4@prweb.com", "JobTitle": "Staff Accountant I",
18 | "Username": "dlemonby4", "Active": True}
19 | ]
20 |
21 | app = Flask(__name__)
22 |
23 | @app.route('/user/')
24 | def get_user(userid):
25 | person = next((user for user in users if str(user['ID']) == userid), {})
26 | return jsonify(person)
27 |
28 | @app.route('/users')
29 | def get_userlist():
30 | user_list = []
31 | for user in users:
32 | user_name = ', '.join([user['LastName'], user['FirstName']])
33 | user_list.append([user['ID'], user_name])
34 | return jsonify(user_list)
35 |
36 | if __name__ == "__main__":
37 | app.run(debug=True, port=8000)
38 |
39 |
--------------------------------------------------------------------------------
/ch20/app.py:
--------------------------------------------------------------------------------
1 | from pyreact import render, useState, useEffect, createElement as el
2 | from pymui import Box, TextField
3 | from jsutils import fetch
4 |
5 |
6 | def StyledTextField(props):
7 | new_props = {'type': 'text',
8 | 'fullWidth': True,
9 | 'variant': 'outlined',
10 | 'InputLabelProps': {'shrink': True},
11 | 'InputProps': {'margin': 'dense'},
12 | 'margin': 'dense',
13 | }
14 |
15 | new_props.update(props)
16 | return el(TextField, new_props)
17 |
18 |
19 | def UserVu(props):
20 | users = props['users'] if props['users'] else []
21 |
22 | def userToRow(user):
23 | return el('option', {'key': user[0], 'value': user[0]}, user[1])
24 |
25 | return [userToRow(user) for user in users]
26 |
27 |
28 | def App():
29 | users, setUsers = useState([])
30 | userID, setUserID = useState("")
31 | firstName, setFirstName = useState("")
32 | lastName, setLastName = useState("")
33 | username, setUsername = useState("")
34 |
35 | def handleChange(event):
36 | target = event['target']
37 | setUserID(target['value'])
38 | setFirstName("")
39 | setLastName("")
40 | setUsername("")
41 |
42 | def getUser():
43 | def _getUser(data):
44 | user_info = data if data else {}
45 | if len(user_info) > 0:
46 | setFirstName(user_info['FirstName'])
47 | setLastName(user_info['LastName'])
48 | setUsername(user_info['Username'])
49 | else:
50 | setFirstName("")
51 | setLastName("")
52 | setUsername("")
53 |
54 | if len(userID) > 0:
55 | fetch(f"/api/user/{userID}", _getUser)
56 |
57 | def getUsers():
58 | def _getUsers(data):
59 | user_list = data if data else []
60 | user_list.sort(key=lambda user: user[1])
61 | setUsers(user_list)
62 | setUserID("")
63 |
64 | fetch("/api/users", _getUsers)
65 |
66 | useEffect(getUser, [userID])
67 | useEffect(getUsers, [])
68 |
69 | return el(Box, {'key': 'App', 'style': {'width': '200px'}},
70 | el(Box, {'alignItems': 'center'},
71 | el(StyledTextField, {'label': "Select User",
72 | 'value': userID,
73 | 'onChange': handleChange,
74 | 'select': True,
75 | 'SelectProps': {'native': True},
76 | 'autoFocus': True,
77 | },
78 | el('option', {'key': '', 'value': ''}), # Blank row
79 | el(UserVu, {'users': users}),
80 | ),
81 | ),
82 | el(StyledTextField, {'label': 'First Name',
83 | 'value': firstName,
84 | 'disabled': True,
85 | }
86 | ),
87 | el(StyledTextField, {'label': 'Last Name',
88 | 'value': lastName,
89 | 'disabled': True,
90 | }
91 | ),
92 | el(StyledTextField, {'label': 'Username',
93 | 'value': username,
94 | 'disabled': True,
95 | }
96 | ),
97 | ),
98 |
99 | render(App, None, 'root')
100 |
101 |
--------------------------------------------------------------------------------
/ch20/dev-server.js:
--------------------------------------------------------------------------------
1 | const Bundler = require('parcel-bundler');
2 | const express = require('express');
3 | const { createProxyMiddleware } = require('http-proxy-middleware');
4 |
5 |
6 | const app = express();
7 |
8 | const apiProxy = createProxyMiddleware('/api', {
9 | target: 'http://localhost:8000',
10 | pathRewrite: {'^/api': ''}
11 | });
12 | app.use(apiProxy);
13 |
14 | // parcel options
15 | const options = {minify:false, cache: false, outDir: 'dist/dev', logLevel: 4};
16 |
17 | const bundler = new Bundler('./index.html', options);
18 | app.use(bundler.middleware());
19 |
20 | bundler.on('buildEnd', () => {
21 | console.log('Parcel proxy server has started at: http://localhost:8080');
22 | });
23 |
24 | app.listen(8080);
25 |
26 |
--------------------------------------------------------------------------------
/ch20/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ch20/jsutils.py:
--------------------------------------------------------------------------------
1 | console = window.console
2 |
3 | polyfill = require("@babel/polyfill") # required by async/await
4 |
5 | async def fetch(url, callback):
6 | try:
7 | response = await window.fetch(url)
8 | if response.status != 200:
9 | console.error('Fetch error - Status Code: ' + response.status)
10 | else:
11 | data = await response.json()
12 | callback(data)
13 | except object as e:
14 | console.error(e)
15 |
--------------------------------------------------------------------------------
/ch20/jsutils_v1.py:
--------------------------------------------------------------------------------
1 | console = window.console
2 |
3 | def fetch(url, callback):
4 | def check_response(response):
5 | if response.status != 200:
6 | console.error('Fetch error - Status Code: ' + response.status)
7 | return None
8 | return response.json()
9 |
10 | try:
11 | promise = window.fetch(url)
12 | response = promise.then(check_response)
13 | response.then(callback)
14 | promise.catch(console.error)
15 | except object as e:
16 | console.error(e)
17 |
18 |
--------------------------------------------------------------------------------
/ch20/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch21",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "dev": "node dev-server.js",
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "dependencies": {
16 | "@babel/polyfill": "^7.12.1",
17 | "@material-ui/core": "^4.11.0",
18 | "react": "^16.14.0",
19 | "react-dom": "^16.14.0"
20 | },
21 | "devDependencies": {
22 | "express": "^4.17.1",
23 | "http-proxy-middleware": "^1.0.6",
24 | "parcel-bundler": "^1.12.4",
25 | "parcel-plugin-transcrypt": "^1.0.20"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ch20/pymui.py:
--------------------------------------------------------------------------------
1 | # Basic MUI components
2 | Button = require('@material-ui/core/Button')['default']
3 | List = require('@material-ui/core/List')['default']
4 | ListItem = require('@material-ui/core/ListItem')['default']
5 | Typography = require('@material-ui/core/Typography')['default']
6 | Input = require('@material-ui/core/Input')['default']
7 | InputLabel = require('@material-ui/core/InputLabel')['default']
8 | Box = require('@material-ui/core/Box')['default']
9 | TextField = require('@material-ui/core/TextField')['default']
10 | Paper = require('@material-ui/core/Paper')['default']
11 | AppBar = require('@material-ui/core/AppBar')['default']
12 | Container = require('@material-ui/core/Container')['default']
13 |
14 | # Theming
15 | ThemeProvider = require('@material-ui/styles/ThemeProvider')['default']
16 | useTheme = require('@material-ui/styles/useTheme')['default']
17 | createMuiTheme = require('@material-ui/core/styles/createMuiTheme')['default']
18 | colors = require('@material-ui/core/colors')
19 | makeStyles = require('@material-ui/styles/makeStyles')['default']
20 | styled = require('@material-ui/styles/styled')['default']
21 |
22 |
--------------------------------------------------------------------------------
/ch20/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 |
5 | # Map React javaScript objects to Python identifiers
6 | createElement = React.createElement
7 | useState = React.useState
8 | useEffect = React.useEffect
9 |
10 |
11 | def render(root_component, props, container):
12 | def main():
13 | ReactDOM.render(
14 | React.createElement(root_component, props),
15 | document.getElementById(container)
16 | )
17 |
18 | document.addEventListener('DOMContentLoaded', main)
19 |
20 |
21 | # JavaScript function mappings
22 | alert = window.alert
23 |
24 |
25 | def setTitle(title):
26 | document.title = title
27 |
28 |
--------------------------------------------------------------------------------
/ch20/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask>=2.0.0
2 | Transcrypt==3.7.16
3 |
4 |
--------------------------------------------------------------------------------
/ch20/webserver.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, jsonify
2 |
3 | users = [
4 | {"ID": 1, "FirstName": "Valeria", "LastName": "Lammerding",
5 | "Email": "vlammerding0@flickr.com", "JobTitle": "Geologist III",
6 | "Username": "vlammerding0", "Active": False},
7 | {"ID": 2, "FirstName": "Bond", "LastName": "Tomczynski",
8 | "Email": "btomczynski1@ehow.com", "JobTitle": "Environmental Specialist",
9 | "Username": "btomczynski1", "Active": True},
10 | {"ID": 3, "FirstName": "Nowell", "LastName": "Triplet",
11 | "Email": "ntriplet2@sciencedirect.com", "JobTitle": "Business Analyst",
12 | "Username": "ntriplet2", "Active": False},
13 | {"ID": 4, "FirstName": "Patience", "LastName": "Boulds",
14 | "Email": "pboulds3@reverbnation.com", "JobTitle": "Assistant Manager",
15 | "Username": "pboulds3", "Active": True},
16 | {"ID": 5, "FirstName": "Darelle", "LastName": "Lemonby",
17 | "Email": "dlemonby4@prweb.com", "JobTitle": "Staff Accountant I",
18 | "Username": "dlemonby4", "Active": True}
19 | ]
20 |
21 | app = Flask(__name__)
22 |
23 | @app.route('/user/')
24 | def get_user(userid):
25 | person = next((user for user in users if str(user['ID']) == userid), {})
26 | return jsonify(person)
27 |
28 | @app.route('/users')
29 | def get_userlist():
30 | user_list = []
31 | for user in users:
32 | user_name = ', '.join([user['LastName'], user['FirstName']])
33 | user_list.append([user['ID'], user_name])
34 | return jsonify(user_list)
35 |
36 | if __name__ == "__main__":
37 | app.run(debug=True, port=8000)
38 |
39 |
--------------------------------------------------------------------------------
/ch21/app.py:
--------------------------------------------------------------------------------
1 | from pyreact import render, useState, createElement as el
2 | from pyreact import useContext, createContext
3 | from pymui import Box, TextField
4 |
5 | Ctx = createContext()
6 |
7 | def ROTextField(props):
8 | new_props = {'fullWidth': True,
9 | 'variant': 'outlined',
10 | 'InputLabelProps': {'shrink': True},
11 | 'InputProps': {'margin': 'dense'},
12 | 'margin': 'dense',
13 | 'disabled': True
14 | }
15 |
16 | new_props.update(props)
17 | return el(TextField, new_props)
18 |
19 | def Component2(props):
20 | return el(Box, None,
21 | el(ROTextField, {'label': 'Row 2', 'value': 'Row Two'}),
22 | el(Component3, None)
23 | )
24 |
25 | def Component3(props):
26 | return el(Box, None,
27 | el(ROTextField, {'label': 'Row 3', 'value': 'Row Three'}),
28 | el(Component4, None)
29 | )
30 |
31 | def Component4(props):
32 | return el(Box, None,
33 | el(ROTextField, {'label': 'Row 4', 'value': 'Row Four'}),
34 | el(Component5, None)
35 | )
36 |
37 | def Component5(props):
38 | ctx = useContext(Ctx)
39 | testVal = ctx['testVal']
40 | return el(Box, None,
41 | el(ROTextField, {'label': 'Copy From Row 1',
42 | 'value': testVal}
43 | ),
44 | )
45 |
46 | def App():
47 | testVal, setTestVal = useState("")
48 |
49 | def handleChange(event):
50 | target = event['target']
51 | setTestVal(target['value'])
52 |
53 | return el(Ctx.Provider, {'value': {'testVal': testVal, 'otherVal': 42}},
54 | el(Box, {'key': 'App', 'style': {'width': '200px'}},
55 | el(TextField, {'label': 'Row 1',
56 | 'value': testVal,
57 | 'onChange': handleChange}
58 | ),
59 | el(Component2, None)
60 | )
61 | )
62 |
63 | render(App, None, 'root')
64 |
65 |
--------------------------------------------------------------------------------
/ch21/app_v1.py:
--------------------------------------------------------------------------------
1 | from pyreact import render, useState, createElement as el
2 | from pymui import Box, TextField
3 |
4 | def ROTextField(props):
5 | new_props = {'fullWidth': True,
6 | 'variant': 'outlined',
7 | 'InputLabelProps': {'shrink': True},
8 | 'InputProps': {'margin': 'dense'},
9 | 'margin': 'dense',
10 | 'disabled': True
11 | }
12 |
13 | new_props.update(props)
14 | return el(TextField, new_props)
15 |
16 | def Component2(props):
17 | return el(Box, None,
18 | el(ROTextField, {'label': 'Row 2', 'value': 'Row Two'}),
19 | el(Component3, {'testVal2': props['testVal1']})
20 | )
21 |
22 | def Component3(props):
23 | return el(Box, None,
24 | el(ROTextField, {'label': 'Row 3', 'value': 'Row Three'}),
25 | el(Component4, {'testVal3': props['testVal2']})
26 | )
27 |
28 | def Component4(props):
29 | return el(Box, None,
30 | el(ROTextField, {'label': 'Row 4', 'value': 'Row Four'}),
31 | el(Component5, {'testVal4': props['testVal3']})
32 | )
33 |
34 | def Component5(props):
35 | return el(Box, None,
36 | el(ROTextField, {'label': 'Copy From Row 1',
37 | 'value': props['testVal4']}
38 | ),
39 | )
40 |
41 | def App():
42 | testVal, setTestVal = useState("")
43 |
44 | def handleChange(event):
45 | target = event['target']
46 | setTestVal(target['value'])
47 |
48 | return el(Box, {'key': 'App', 'style': {'width': '200px'}},
49 | el(TextField, {'label': 'Row 1',
50 | 'value': testVal,
51 | 'onChange': handleChange}
52 | ),
53 | el(Component2, {'testVal1': testVal})
54 | )
55 |
56 | render(App, None, 'root')
57 |
58 |
--------------------------------------------------------------------------------
/ch21/dev-server.js:
--------------------------------------------------------------------------------
1 | const Bundler = require('parcel-bundler');
2 | const express = require('express');
3 | const { createProxyMiddleware } = require('http-proxy-middleware');
4 |
5 |
6 | const app = express();
7 |
8 | const apiProxy = createProxyMiddleware('/api', {
9 | target: 'http://localhost:8000',
10 | pathRewrite: {'^/api': ''}
11 | });
12 | app.use(apiProxy);
13 |
14 | // parcel options
15 | const options = {minify:false, cache: false, outDir: 'dist/dev', logLevel: 4};
16 |
17 | const bundler = new Bundler('./index.html', options);
18 | app.use(bundler.middleware());
19 |
20 | bundler.on('buildEnd', () => {
21 | console.log('Parcel proxy server has started at: http://localhost:8080');
22 | });
23 |
24 | app.listen(8080);
25 |
26 |
--------------------------------------------------------------------------------
/ch21/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ch21/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch22",
3 | "version": "1.0.0",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "dev": "node dev-server.js",
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "dependencies": {
16 | "@babel/polyfill": "^7.12.1",
17 | "@material-ui/core": "^4.11.0",
18 | "react": "^16.14.0",
19 | "react-dom": "^16.14.0"
20 | },
21 | "devDependencies": {
22 | "express": "^4.17.1",
23 | "http-proxy-middleware": "^1.0.6",
24 | "parcel-bundler": "^1.12.4",
25 | "parcel-plugin-transcrypt": "^1.0.20"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ch21/pymui.py:
--------------------------------------------------------------------------------
1 | # Basic MUI components
2 | Button = require('@material-ui/core/Button')['default']
3 | List = require('@material-ui/core/List')['default']
4 | ListItem = require('@material-ui/core/ListItem')['default']
5 | Typography = require('@material-ui/core/Typography')['default']
6 | Input = require('@material-ui/core/Input')['default']
7 | InputLabel = require('@material-ui/core/InputLabel')['default']
8 | Box = require('@material-ui/core/Box')['default']
9 | TextField = require('@material-ui/core/TextField')['default']
10 | Paper = require('@material-ui/core/Paper')['default']
11 | AppBar = require('@material-ui/core/AppBar')['default']
12 | Container = require('@material-ui/core/Container')['default']
13 |
14 | # Theming
15 | ThemeProvider = require('@material-ui/styles/ThemeProvider')['default']
16 | useTheme = require('@material-ui/styles/useTheme')['default']
17 | createMuiTheme = require('@material-ui/core/styles/createMuiTheme')['default']
18 | colors = require('@material-ui/core/colors')
19 | makeStyles = require('@material-ui/styles/makeStyles')['default']
20 | styled = require('@material-ui/styles/styled')['default']
21 |
22 |
--------------------------------------------------------------------------------
/ch21/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 |
5 | # Map React javaScript objects to Python identifiers
6 | createElement = React.createElement
7 | useState = React.useState
8 | useEffect = React.useEffect
9 | createContext = React.createContext
10 | useContext = React.useContext
11 |
12 |
13 | def render(root_component, props, container):
14 | def main():
15 | ReactDOM.render(
16 | React.createElement(root_component, props),
17 | document.getElementById(container)
18 | )
19 |
20 | document.addEventListener('DOMContentLoaded', main)
21 |
22 |
23 | # JavaScript function mappings
24 | alert = window.alert
25 |
26 |
27 | def setTitle(title):
28 | document.title = title
29 |
30 |
--------------------------------------------------------------------------------
/ch21/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask>=2.0.0
2 | Transcrypt==3.7.16
3 |
4 |
--------------------------------------------------------------------------------
/ch22/jsutils.py:
--------------------------------------------------------------------------------
1 | console = window.console
2 |
3 | deepcopy = require('deepcopy')
4 |
5 | polyfill = require("@babel/polyfill") # required by async/await
6 |
7 | async def fetch(url, callback):
8 | try:
9 | response = await window.fetch(url)
10 | if response.status != 200:
11 | console.error('Fetch error - Status Code: ' + response.status)
12 | else:
13 | data = await response.json()
14 | callback(data)
15 | except object as e:
16 | console.error(e)
17 |
--------------------------------------------------------------------------------
/ch23/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch24",
3 | "version": "1.0.1",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "dev": "node dev-server.js",
10 | "version": "echo \"version = '$npm_package_version'\" > ./version.py;git add ./version.py",
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "keywords": [],
14 | "author": "",
15 | "license": "ISC",
16 | "dependencies": {
17 | "@babel/polyfill": "^7.12.1",
18 | "@material-ui/core": "^4.11.0",
19 | "react": "^16.14.0",
20 | "react-dom": "^16.14.0"
21 | },
22 | "devDependencies": {
23 | "express": "^4.17.1",
24 | "http-proxy-middleware": "^1.0.6",
25 | "parcel-bundler": "^1.12.4",
26 | "parcel-plugin-transcrypt": "^1.0.20"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ch23/version.py:
--------------------------------------------------------------------------------
1 | version = '1.0.1'
2 |
--------------------------------------------------------------------------------
/ch24/app.py:
--------------------------------------------------------------------------------
1 | from pyreact import render, useState, useEffect, createElement as el
2 | from pyreact import ReactGA
3 | from pymui import Box, TextField
4 | from jsutils import fetch
5 |
6 |
7 | GAID = 'UA-100000000-1' # Substitute your own GA Tracking ID here
8 | ReactGA.initialize(GAID, {'titleCase': False, 'debug': False,
9 | 'gaOptions': {'siteSpeedSampleRate': 100}}
10 | )
11 |
12 |
13 | def StyledTextField(props):
14 | new_props = {'type': 'text',
15 | 'fullWidth': True,
16 | 'variant': 'outlined',
17 | 'InputLabelProps': {'shrink': True},
18 | 'InputProps': {'margin': 'dense'},
19 | 'margin': 'dense',
20 | }
21 |
22 | new_props.update(props)
23 | return el(TextField, new_props)
24 |
25 | def UserVu(props):
26 | users = props['users'] if props['users'] else []
27 |
28 | def userToRow(user):
29 | return el('option', {'key': user[0], 'value': user[0]}, user[1])
30 |
31 | return [userToRow(user) for user in users]
32 |
33 |
34 | def App():
35 | users, setUsers = useState([])
36 | userID, setUserID = useState("")
37 | firstName, setFirstName = useState("")
38 | lastName, setLastName = useState("")
39 | username, setUsername = useState("")
40 |
41 | def handleChange(event):
42 | target = event['target']
43 | setUserID(target['value'])
44 | setFirstName("")
45 | setLastName("")
46 | setUsername("")
47 |
48 | def getUser():
49 | def _getUser(data):
50 | user_info = data if data else {}
51 | if len(user_info) > 0:
52 | setFirstName(user_info['FirstName'])
53 | setLastName(user_info['LastName'])
54 | setUsername(user_info['Username'])
55 | else:
56 | setFirstName("")
57 | setLastName("")
58 | setUsername("")
59 |
60 | if len(userID) > 0:
61 | ReactGA.event({'category': 'User',
62 | 'action': 'Select',
63 | 'label': userID}
64 | )
65 | fetch(f"/api/user/{userID}", _getUser)
66 |
67 | def getUsers():
68 | def _getUsers(data):
69 | user_list = data if data else []
70 | user_list.sort(key=lambda user: user[1])
71 | ReactGA.event({'category': 'App',
72 | 'action': 'Load',
73 | 'label': 'Users',
74 | 'nonInteraction': True}
75 | )
76 | setUsers(user_list)
77 | setUserID("")
78 |
79 | fetch("/api/users", _getUsers)
80 |
81 | useEffect(getUser, [userID])
82 | useEffect(getUsers, [])
83 |
84 | return el(Box, {'key': 'App', 'style': {'width': '200px'}},
85 | el(Box, {'alignItems': 'center'},
86 | el(StyledTextField, {'label': "Select User",
87 | 'value': userID,
88 | 'onChange': handleChange,
89 | 'select': True,
90 | 'SelectProps': {'native': True},
91 | 'autoFocus': True,
92 | },
93 | el('option', {'key': '', 'value': ''}), # Blank row
94 | el(UserVu, {'users': users}),
95 | ),
96 | ),
97 | el(StyledTextField, {'label': 'First Name',
98 | 'value': firstName,
99 | 'disabled': True,
100 | }
101 | ),
102 | el(StyledTextField, {'label': 'Last Name',
103 | 'value': lastName,
104 | 'disabled': True,
105 | }
106 | ),
107 | el(StyledTextField, {'label': 'Username',
108 | 'value': username,
109 | 'disabled': True,
110 | }
111 | ),
112 | ),
113 |
114 | render(App, None, 'root')
115 |
116 |
--------------------------------------------------------------------------------
/ch24/dev-server.js:
--------------------------------------------------------------------------------
1 | const Bundler = require('parcel-bundler');
2 | const express = require('express');
3 | const { createProxyMiddleware } = require('http-proxy-middleware');
4 |
5 |
6 | const app = express();
7 |
8 | const apiProxy = createProxyMiddleware('/api', {
9 | target: 'http://localhost:8000',
10 | pathRewrite: {'^/api': ''}
11 | });
12 | app.use(apiProxy);
13 |
14 | // parcel options
15 | const options = {minify:false, cache: false, outDir: 'dist/dev', logLevel: 4};
16 |
17 | const bundler = new Bundler('./index.html', options);
18 | app.use(bundler.middleware());
19 |
20 | bundler.on('buildEnd', () => {
21 | console.log('Parcel proxy server has started at: http://localhost:8080');
22 | });
23 |
24 | app.listen(8080);
25 |
26 |
--------------------------------------------------------------------------------
/ch24/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ch24/jsutils.py:
--------------------------------------------------------------------------------
1 | import time
2 | from pyreact import ReactGA
3 |
4 |
5 | console = window.console
6 |
7 | deepcopy = require('deepcopy')
8 |
9 | polyfill = require("@babel/polyfill") # required by async/await
10 |
11 | async def fetch(url, callback):
12 | t_start = time.time()
13 | try:
14 | response = await window.fetch(url)
15 | if response.status != 200:
16 | console.error('Fetch error - Status Code: ' + response.status)
17 | else:
18 | data = await response.json()
19 | t_elapsed = time.time() - t_start
20 | ReactGA.timing({'category': 'API',
21 | 'variable': 'fetch',
22 | 'value': int(t_elapsed * 1000),
23 | 'label': url}
24 | )
25 | callback(data)
26 | except object as e:
27 | console.error(e)
28 |
29 |
--------------------------------------------------------------------------------
/ch24/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch25",
3 | "version": "1.0.1",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "dev": "node dev-server.js",
10 | "version": "echo \"version = '$npm_package_version'\" > ./version.py;git add ./version.py",
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "keywords": [],
14 | "author": "",
15 | "license": "ISC",
16 | "dependencies": {
17 | "@babel/polyfill": "^7.12.1",
18 | "@material-ui/core": "^4.11.0",
19 | "deepcopy": "^2.1.0",
20 | "react": "^16.14.0",
21 | "react-dom": "^16.14.0",
22 | "react-ga": "^3.2.0"
23 | },
24 | "devDependencies": {
25 | "express": "^4.17.1",
26 | "http-proxy-middleware": "^1.0.6",
27 | "parcel-bundler": "^1.12.4",
28 | "parcel-plugin-transcrypt": "^1.0.20"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/ch24/pymui.py:
--------------------------------------------------------------------------------
1 | # Basic MUI components
2 | Button = require('@material-ui/core/Button')['default']
3 | List = require('@material-ui/core/List')['default']
4 | ListItem = require('@material-ui/core/ListItem')['default']
5 | Typography = require('@material-ui/core/Typography')['default']
6 | Input = require('@material-ui/core/Input')['default']
7 | InputLabel = require('@material-ui/core/InputLabel')['default']
8 | Box = require('@material-ui/core/Box')['default']
9 | TextField = require('@material-ui/core/TextField')['default']
10 | Paper = require('@material-ui/core/Paper')['default']
11 | AppBar = require('@material-ui/core/AppBar')['default']
12 | Container = require('@material-ui/core/Container')['default']
13 |
14 | # Theming
15 | ThemeProvider = require('@material-ui/styles/ThemeProvider')['default']
16 | useTheme = require('@material-ui/styles/useTheme')['default']
17 | createMuiTheme = require('@material-ui/core/styles/createMuiTheme')['default']
18 | colors = require('@material-ui/core/colors')
19 | makeStyles = require('@material-ui/styles/makeStyles')['default']
20 | styled = require('@material-ui/styles/styled')['default']
21 |
22 |
--------------------------------------------------------------------------------
/ch24/pyreact.py:
--------------------------------------------------------------------------------
1 | # Load React and ReactDOM JavaScript libraries into local namespace
2 | React = require('react')
3 | ReactDOM = require('react-dom')
4 | ReactGA = require('react-ga')
5 |
6 | # Map React javaScript objects to Python identifiers
7 | createElement = React.createElement
8 | useState = React.useState
9 | useEffect = React.useEffect
10 | createContext = React.createContext
11 | useContext = React.useContext
12 |
13 |
14 | def render(root_component, props, container):
15 | def main():
16 | ReactDOM.render(
17 | React.createElement(root_component, props),
18 | document.getElementById(container)
19 | )
20 |
21 | document.addEventListener('DOMContentLoaded', main)
22 |
23 |
24 | # JavaScript function mappings
25 | alert = window.alert
26 |
27 |
28 | def setTitle(title):
29 | document.title = title
30 |
31 |
--------------------------------------------------------------------------------
/ch24/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask>=2.0.0
2 | Transcrypt==3.7.16
3 |
4 |
--------------------------------------------------------------------------------
/ch24/version.py:
--------------------------------------------------------------------------------
1 | version = '1.0.1'
2 |
--------------------------------------------------------------------------------
/ch24/webserver.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, jsonify
2 |
3 | users = [
4 | {"ID": 1, "FirstName": "Valeria", "LastName": "Lammerding",
5 | "Email": "vlammerding0@flickr.com", "JobTitle": "Geologist III",
6 | "Username": "vlammerding0", "Active": False},
7 | {"ID": 2, "FirstName": "Bond", "LastName": "Tomczynski",
8 | "Email": "btomczynski1@ehow.com", "JobTitle": "Environmental Specialist",
9 | "Username": "btomczynski1", "Active": True},
10 | {"ID": 3, "FirstName": "Nowell", "LastName": "Triplet",
11 | "Email": "ntriplet2@sciencedirect.com", "JobTitle": "Business Analyst",
12 | "Username": "ntriplet2", "Active": False},
13 | {"ID": 4, "FirstName": "Patience", "LastName": "Boulds",
14 | "Email": "pboulds3@reverbnation.com", "JobTitle": "Assistant Manager",
15 | "Username": "pboulds3", "Active": True},
16 | {"ID": 5, "FirstName": "Darelle", "LastName": "Lemonby",
17 | "Email": "dlemonby4@prweb.com", "JobTitle": "Staff Accountant I",
18 | "Username": "dlemonby4", "Active": True}
19 | ]
20 |
21 | app = Flask(__name__)
22 |
23 | @app.route('/user/')
24 | def get_user(userid):
25 | person = next((user for user in users if str(user['ID']) == userid), {})
26 | return jsonify(person)
27 |
28 | @app.route('/users')
29 | def get_userlist():
30 | user_list = []
31 | for user in users:
32 | user_name = ', '.join([user['LastName'], user['FirstName']])
33 | user_list.append([user['ID'], user_name])
34 | return jsonify(user_list)
35 |
36 | if __name__ == "__main__":
37 | app.run(debug=True, port=8000)
38 |
39 |
--------------------------------------------------------------------------------
/ch25/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ch26",
3 | "version": "1.0.1",
4 | "description": "React to Python",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development parcel --log-level 4 index.html --out-dir dist/dev",
8 | "build": "NODE_ENV=production parcel --log-level 4 build index.html --no-source-maps --out-dir dist/prod",
9 | "dev": "node dev-server.js",
10 | "version": "echo \"version = '$npm_package_version'\" > ./version.py;git add ./version.py",
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "keywords": [],
14 | "author": "",
15 | "license": "ISC",
16 | "dependencies": {
17 | "@babel/polyfill": "^7.12.1",
18 | "@material-ui/core": "^4.11.0",
19 | "deepcopy": "^2.1.0",
20 | "react": "^16.14.0",
21 | "react-dom": "^16.14.0",
22 | "react-ga": "^3.2.0"
23 | },
24 | "devDependencies": {
25 | "express": "^4.17.1",
26 | "http-proxy-middleware": "^1.0.6",
27 | "parcel-bundler": "^1.12.4",
28 | "parcel-plugin-bundle-visualiser": "^1.2.0",
29 | "parcel-plugin-transcrypt": "^1.0.20"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ch26/README.md:
--------------------------------------------------------------------------------
1 | There are no code modules for Chapter 26.
2 |
3 |
--------------------------------------------------------------------------------