Skip to content

Commit

Permalink
docs: use pyodide (#2)
Browse files Browse the repository at this point in the history
* docs: use pyodide
  • Loading branch information
maartenbreddels committed Jul 13, 2022
1 parent dda9065 commit 40b36c4
Show file tree
Hide file tree
Showing 13 changed files with 659 additions and 74 deletions.
13 changes: 13 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2
conda:
environment: docs/environment.yml
mkdocs:
configuration: mkdocs.yml
fail_on_warning: false
build:
os: "ubuntu-20.04"
tools:
python: "mambaforge-4.10"
jobs:
pre_build:
- ./docs/build.sh
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[![Documentation](https://readthedocs.org/projects/react-ipywidgets/badge/?version=latest)](https://react-ipywidgets.readthedocs.io/)
[![Jupyter Lab](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://react-ipywidgets--2.org.readthedocs.build/en/2/_output/lab/index.html)

# React for ipywidgets

Expand Down Expand Up @@ -135,6 +136,26 @@ The `MarkdownEditor` component also shows another feature we can provide: All co
[![Documentation](https://readthedocs.org/projects/react-ipywidgets/badge/?version=latest)](https://react-ipywidgets.readthedocs.io/)


## Examples

API documentation is great, but like writing, you learn by reading.

Our example notebooks can be found at:

* [https://github.com/widgetti/react-ipywidgets/tree/master/notebooks](https://github.com/widgetti/react-ipywidgets/tree/master/notebooks)


Or try them out directly in a Jupyter environment (JupyterLite)

* [![Jupyter Lab](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://react-ipywidgets--2.org.readthedocs.build/en/2/_output/lab/index.html)

Direct link to examples:

* [ButtonClick](https://react-ipywidgets--2.org.readthedocs.build/en/2/_output/lab/index.html?path=click-button.ipynb)
* [Calculator](https://react-ipywidgets--2.org.readthedocs.build/en/2/_output/lab/index.html?path=calculator.ipynb)
* [Todo-app](https://react-ipywidgets--2.org.readthedocs.build/en/2/_output/lab/index.html?path=todo-app.ipynb)


# Installation
## User

Expand Down
5 changes: 5 additions & 0 deletions docs/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
echo `pwd`
ls -lR .
cd docs
jupyter lite build --config jupyterlite_config.json --contents ../notebooks
ls -lR .
18 changes: 18 additions & 0 deletions docs/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: xeus-python-kernel-docs

channels:
- conda-forge

dependencies:
- python==3.9.10
- Jinja2==3.0.3
- mkdocs==1.3.0
- mkdocs-material==8.2.11
- mamba
- emsdk

- pip:
- jupyterlite
- jupyterlite-sphinx
- jupyterlite-xeus-python==0.5.1
- empack
14 changes: 12 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,18 @@ API documentation is great, but like writing, you learn by reading.

Our example notebooks can be found at:

* TODO: [https://github.com/widgetti/react-ipywidgets/tree/master/notebooks](https://github.com/widgetti/react-ipywidgets/tree/master/notebooks)
* TODO: In browser Jupyter environment (JupyterLite)
* [https://github.com/widgetti/react-ipywidgets/tree/master/notebooks](https://github.com/widgetti/react-ipywidgets/tree/master/notebooks)


Or try them out directly in a Jupyter environment (JupyterLite)

* [![Jupyter Lab](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://react-ipywidgets--2.org.readthedocs.build/en/2/_output/lab/index.html)

Direct link to examples:

* [ButtonClick](https://react-ipywidgets--2.org.readthedocs.build/en/2/_output/lab/index.html?path=click-button.ipynb)
* [Calculator](https://react-ipywidgets--2.org.readthedocs.build/en/2/_output/lab/index.html?path=calculator.ipynb)
* [Todo-app](https://react-ipywidgets--2.org.readthedocs.build/en/2/_output/lab/index.html?path=todo-app.ipynb)


## API
Expand Down
14 changes: 14 additions & 0 deletions docs/jupyter-lite.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"LiteBuildConfig": {
"federated_extensions": [
"https://conda.anaconda.org/conda-forge/noarch/bqplot-0.12.33-pyhd8ed1ab_0.tar.bz2"
],
"ignore_sys_prefix": true
},
"jupyter-config-data": {
"disabledExtensions": [
"@jupyterlite/javascript-kernel-extension",
"@jupyterlite/pyolite-kernel-extension"
]
}
}
13 changes: 13 additions & 0 deletions docs/jupyterlite_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"XeusPythonEnv": {
"packages": [
"numpy",
"ipycanvas",
"ipywidgets==7.7.1",
"react-ipywidgets",
"bqplot",
"ipyvue",
"ipyvuetify"
]
}
}
3 changes: 0 additions & 3 deletions docs/requirements.txt

This file was deleted.

273 changes: 273 additions & 0 deletions notebooks/calculator.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "96381759",
"metadata": {},
"source": [
"# Calculator\n",
"All the calculator logic is put in a 'reducer'"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "20bae5ea",
"metadata": {},
"outputs": [],
"source": [
"import ast\n",
"import dataclasses\n",
"import operator\n",
"from typing import Any, Optional\n",
"\n",
"DEBUG = False\n",
"operator_map = {\n",
" \"x\": operator.mul,\n",
" \"/\": operator.truediv,\n",
" \"+\": operator.add,\n",
" \"-\": operator.sub,\n",
"}\n",
"\n",
"\n",
"@dataclasses.dataclass(frozen=True)\n",
"class CalculatorState:\n",
" input: str = \"\"\n",
" output: str = \"\"\n",
" left: float = 0\n",
" right: Optional[float] = None\n",
" operator: Any = operator.add\n",
" error: str = \"\"\n",
"\n",
"\n",
"initial_state = CalculatorState()\n",
"\n",
"\n",
"def calculate(state: CalculatorState):\n",
" result = state.operator(state.left, state.right)\n",
" return dataclasses.replace(state, left=result)\n",
"\n",
"\n",
"def calculator_reducer(state: CalculatorState, action):\n",
" action_type, payload = action\n",
" if DEBUG:\n",
" print(\"reducer\", state, action_type, payload) # noqa\n",
" state = dataclasses.replace(state, error=\"\")\n",
"\n",
" if action_type == \"digit\":\n",
" digit = payload\n",
" input = state.input + digit\n",
" return dataclasses.replace(state, input=input, output=input)\n",
" elif action_type == \"percent\":\n",
" if state.input:\n",
" try:\n",
" value = ast.literal_eval(state.input)\n",
" except Exception as e:\n",
" return dataclasses.replace(state, error=str(e))\n",
" state = dataclasses.replace(state, right=value / 100)\n",
" state = calculate(state)\n",
" output = f\"{value / 100:,}\"\n",
" return dataclasses.replace(state, output=output, input=\"\")\n",
" else:\n",
" output = f\"{state.left / 100:,}\"\n",
" return dataclasses.replace(state, left=state.left / 100, output=output)\n",
" elif action_type == \"negate\":\n",
" if state.input:\n",
" input = state.output\n",
" input = input[1:] if input[0] == \"-\" else \"-\" + input\n",
" output = input\n",
" return dataclasses.replace(state, input=input, output=output)\n",
" else:\n",
" output = f\"{-state.left:,}\"\n",
" return dataclasses.replace(state, left=-state.left, output=output)\n",
" elif action_type == \"clear\":\n",
" return dataclasses.replace(state, input=\"\", output=\"\")\n",
" elif action_type == \"reset\":\n",
" return initial_state\n",
" elif action_type == \"calculate\":\n",
" if state.input:\n",
" try:\n",
" value = ast.literal_eval(state.input)\n",
" except Exception as e:\n",
" return dataclasses.replace(state, error=str(e))\n",
" state = dataclasses.replace(state, right=value)\n",
" state = calculate(state)\n",
" output = f\"{state.left:,}\"\n",
" state = dataclasses.replace(state, output=output, input=\"\")\n",
" return state\n",
" elif action_type == \"operator\":\n",
" if state.input:\n",
" state = calculator_reducer(state, (\"calculate\", None))\n",
" state = dataclasses.replace(state, operator=payload, input=\"\")\n",
" else:\n",
" # e.g. 2+3=*= should give 5,25\n",
" state = dataclasses.replace(state, operator=payload, right=state.left)\n",
" return state\n",
" else:\n",
" print(\"invalid action\", action) # noqa\n",
" return state"
]
},
{
"cell_type": "markdown",
"id": "1bb5252e",
"metadata": {},
"source": [
"## UI using ipywidgets\n",
"\n",
"The reducer is used for the logic, we only declare a UI in the component."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1512b84b",
"metadata": {},
"outputs": [],
"source": [
"import react_ipywidgets as react\n",
"import react_ipywidgets.ipywidgets as w\n",
"\n",
"\n",
"@react.component\n",
"def Calculator():\n",
" state, dispatch = react.use_reducer(calculator_reducer, initial_state)\n",
" with w.VBox() as main:\n",
" w.HTML(value=\"<h2>Calculator</h2><h3>Using React-IPywidgets<h3>\")\n",
" with w.VBox():\n",
" w.Label(value=state.error or state.output or \"0\")\n",
"\n",
" \n",
" with w.HBox():\n",
" if state.input:\n",
" w.Button(description=\"C\", on_click=lambda: dispatch((\"clear\", None)))\n",
" else:\n",
" w.Button(description=\"AC\", on_click=lambda: dispatch((\"reset\", None)))\n",
" w.Button(description=\"+/-\", on_click=lambda: dispatch((\"negate\", None)))\n",
" w.Button(description=\"%\", on_click=lambda: dispatch((\"percent\", None)))\n",
" w.Button(description=\"/\", on_click=lambda: dispatch((\"operator\", operator_map[\"/\"])))\n",
"\n",
" column_op = [\"x\", \"-\", \"+\"]\n",
" for i in range(3):\n",
" with w.HBox():\n",
" for j in range(3):\n",
" digit = str(j + (2 - i) * 3 + 1)\n",
" w.Button(description=digit, on_click=lambda digit=digit: dispatch((\"digit\", digit)))\n",
" op_symbol = column_op[i]\n",
" op = operator_map[op_symbol]\n",
" w.Button(description=op_symbol, on_click=lambda op=op: dispatch((\"operator\", op)))\n",
" with w.HBox():\n",
" def boom():\n",
" print(\"boom\")\n",
" raise ValueError(\"boom\")\n",
"\n",
" w.Button(description=\"?\", on_click=boom)\n",
"\n",
" w.Button(description=\"0\", on_click=lambda: dispatch((\"digit\", \"0\")))\n",
" w.Button(description=\".\", on_click=lambda: dispatch((\"digit\", \".\")))\n",
"\n",
" w.Button(description=\"=\", on_click=lambda: dispatch((\"calculate\", None)))\n",
"\n",
" return main\n",
"\n",
"Calculator()\n"
]
},
{
"cell_type": "markdown",
"id": "33756dbf",
"metadata": {},
"source": [
"## Using ipyvuetify\n",
"\n",
"We can make it prettier using ipyvuetify, using the *same* reducer, so we do not need to repeat the calculator logic."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d15b1a72",
"metadata": {},
"outputs": [],
"source": [
"import react_ipywidgets.ipyvuetify as v\n",
"\n",
"\n",
"@react.component\n",
"def CalculatorVuetify():\n",
" state, dispatch = react.use_reducer(calculator_reducer, initial_state)\n",
" with v.Card(elevation=10, class_=\"ma-4\") as main:\n",
" with v.CardTitle(children=[\"Calculator\"]):\n",
" pass\n",
" with v.CardSubtitle(children=[\"With ipyvuetify and ipywidgets-react\"]):\n",
" pass\n",
" with v.CardText():\n",
" with w.VBox():\n",
" v.Label(children=[state.error or state.output or \"0\"])\n",
" class_ = \"pa-0 ma-1\"\n",
"\n",
" with w.HBox():\n",
" if state.input:\n",
" v.BtnWithClick(children=\"C\", on_click=lambda: dispatch((\"clear\", None)), dark=True, class_=class_)\n",
" else:\n",
" v.BtnWithClick(children=\"AC\", on_click=lambda: dispatch((\"reset\", None)), dark=True, class_=class_)\n",
" v.BtnWithClick(children=\"+/-\", on_click=lambda: dispatch((\"negate\", None)), dark=True, class_=class_)\n",
" v.BtnWithClick(children=\"%\", on_click=lambda: dispatch((\"percent\", None)), dark=True, class_=class_)\n",
" v.BtnWithClick(children=\"/\", color=\"primary\", on_click=lambda: dispatch((\"operator\", operator_map[\"/\"])), class_=class_)\n",
"\n",
" column_op = [\"x\", \"-\", \"+\"]\n",
" for i in range(3):\n",
" with w.HBox():\n",
" for j in range(3):\n",
" digit = str(j + (2 - i) * 3 + 1)\n",
" v.BtnWithClick(children=digit, on_click=lambda digit=digit: dispatch((\"digit\", digit)), class_=class_)\n",
" op_symbol = column_op[i]\n",
" op = operator_map[op_symbol]\n",
" v.BtnWithClick(children=op_symbol, color=\"primary\", on_click=lambda op=op: dispatch((\"operator\", op)), class_=class_)\n",
" with w.HBox():\n",
" def boom():\n",
" print(\"boom\")\n",
" raise ValueError(\"boom\")\n",
"\n",
" v.BtnWithClick(children=\"?\", on_click=boom, class_=class_)\n",
"\n",
" v.BtnWithClick(children=\"0\", on_click=lambda: dispatch((\"digit\", \"0\")), class_=class_)\n",
" v.BtnWithClick(children=\".\", on_click=lambda: dispatch((\"digit\", \".\")), class_=class_)\n",
"\n",
" v.BtnWithClick(children=\"=\", color=\"primary\", on_click=lambda: dispatch((\"calculate\", None)), class_=class_)\n",
"\n",
" return main\n",
"CalculatorVuetify()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2f7a7582",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading

0 comments on commit 40b36c4

Please sign in to comment.