-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
238 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<!doctype html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<title>Alfort-Dom • Simple Counter</title> | ||
</head> | ||
<body> | ||
<script | ||
type="text/javascript" | ||
src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js" | ||
></script> | ||
<script type="text/javascript"> | ||
async function main() { | ||
let pyodide = await loadPyodide(); | ||
await pyodide.loadPackage("micropip"); | ||
|
||
const initScript = await fetch("./pkg_install.py"); | ||
const initScriptText = await initScript.text(); | ||
await pyodide.runPythonAsync(initScriptText); | ||
|
||
const script = await fetch("./main.py"); | ||
const scriptText = await script.text(); | ||
await pyodide.runPythonAsync(scriptText); | ||
} | ||
main(); | ||
</script> | ||
<div id="root"></div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
from dataclasses import dataclass | ||
from typing import Any, Callable, Coroutine, TypeAlias | ||
from urllib.parse import ParseResult as URL | ||
from urllib.parse import urlparse | ||
|
||
import pyodide.http | ||
from alfort import Dispatch, Effect | ||
from alfort.vdom import VDom, el | ||
|
||
from alfort_dom import AlfortDom | ||
|
||
|
||
@dataclass(frozen=True) | ||
class Photo: | ||
album_id: int | ||
id: int | ||
title: str | ||
url: URL | ||
thumbnail_url: URL | ||
|
||
|
||
@dataclass(frozen=True) | ||
class State: | ||
album_id: int | ||
is_fetching: bool | ||
photos: list[Photo] | ||
|
||
|
||
@dataclass(frozen=True) | ||
class SelectAlbum: | ||
alumb_id: int | ||
|
||
|
||
@dataclass(frozen=True) | ||
class ReceivePhotos: | ||
photos: list[Photo] | ||
|
||
|
||
Msg: TypeAlias = SelectAlbum | ReceivePhotos | ||
|
||
|
||
async def fetch_photos(album_id: int) -> list[Photo]: | ||
url = f"https://jsonplaceholder.typicode.com/albums/{album_id}/photos" | ||
res = await pyodide.http.pyfetch(url) | ||
return [ | ||
Photo( | ||
album_id=obj["albumId"], | ||
id=obj["id"], | ||
title=obj["title"], | ||
url=urlparse(obj["url"]), | ||
thumbnail_url=urlparse(obj["thumbnailUrl"]), | ||
) | ||
for obj in await res.json() | ||
] | ||
|
||
|
||
def title(text: str) -> VDom: | ||
return el("h1", {}, [text]) | ||
|
||
|
||
def album_selector(album_id: int) -> VDom: | ||
def _on_change(e: Any) -> Msg: | ||
return SelectAlbum(e.target.value) | ||
|
||
return el( | ||
"div", | ||
{"style": {"margin": "15px"}}, | ||
[ | ||
el("label", {"style": {"margin-right": "8px"}}, ["Album ID"]), | ||
el( | ||
"select", | ||
{ | ||
"onchange": _on_change, | ||
}, | ||
[ | ||
el( | ||
"option", | ||
{ | ||
"value": i, | ||
"selected": album_id == i, | ||
}, | ||
[str(i)], | ||
) | ||
for i in range(15) | ||
], | ||
), | ||
], | ||
) | ||
|
||
|
||
def album_photos(photos: list[Photo]) -> VDom: | ||
photo_elms: list[VDom] = [] | ||
for photo in photos: | ||
photo_elms.append( | ||
el( | ||
"img", | ||
{"src": photo.thumbnail_url.geturl(), "style": {"margin": "3px"}}, | ||
[], | ||
) | ||
) | ||
|
||
return el("div", {"style": {"width": "75%", "line-height": "0px"}}, photo_elms) | ||
|
||
|
||
def fetching_dialog() -> VDom: | ||
return el("div", {}, ["Loading..."]) | ||
|
||
|
||
def view(state: State) -> VDom: | ||
return el( | ||
"div", | ||
{ | ||
"style": { | ||
"display": "flex", | ||
"justify-content": "center", | ||
"align-items": "center", | ||
"flex-flow": "column", | ||
} | ||
}, | ||
[ | ||
title("Simple Photo Album"), | ||
album_selector(state.album_id), | ||
fetching_dialog() if state.is_fetching else album_photos(state.photos), | ||
], | ||
) | ||
|
||
|
||
def create_fetch_effect( | ||
album_id: int, | ||
) -> Callable[[Dispatch[Msg]], Coroutine[None, None, Any]]: | ||
async def _fetch(dispatch: Dispatch[Msg]) -> None: | ||
recv_photos = await fetch_photos(album_id=album_id) | ||
dispatch(ReceivePhotos(recv_photos)) | ||
|
||
return _fetch | ||
|
||
|
||
def init() -> tuple[State, list[Effect[Msg]]]: | ||
return (State(album_id=1, is_fetching=True, photos=[]), [create_fetch_effect(1)]) | ||
|
||
|
||
def update(msg: Msg, state: State) -> tuple[State, list[Effect[Msg]]]: | ||
match msg: | ||
case SelectAlbum(album_id): | ||
state = State(album_id=album_id, is_fetching=True, photos=[]) | ||
return (state, [create_fetch_effect(album_id=album_id)]) | ||
case ReceivePhotos(photos): | ||
state = State(album_id=state.album_id, is_fetching=False, photos=photos) | ||
return (state, []) | ||
|
||
|
||
app = AlfortDom[State, Msg]( | ||
init=init, | ||
view=view, | ||
update=update, | ||
) | ||
app.main(root="root") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import micropip # type: ignore # noqa: F401 | ||
|
||
try: | ||
# try to use development version | ||
await micropip.install("../dist/alfort_dom-0.0.0.dev0-py3-none-any.whl") # type: ignore # noqa: F704 | ||
except ValueError: | ||
await micropip.install("alfort-dom") # type: ignore # noqa: F704 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# derived from https://github.com/pyodide/pyodide/blob/main/src/py/pyodide/http.py | ||
|
||
from io import StringIO | ||
from typing import Any, BinaryIO, TextIO | ||
|
||
from .. import JsProxy | ||
|
||
def open_url(url: str) -> StringIO: ... | ||
|
||
class FetchResponse: | ||
def __init__(self, url: str, js_response: JsProxy) -> None: ... | ||
@property | ||
def body_used(self) -> bool: ... | ||
@property | ||
def ok(self) -> bool: ... | ||
@property | ||
def redirected(self) -> bool: ... | ||
@property | ||
def status(self) -> str: ... | ||
@property | ||
def status_text(self) -> str: ... | ||
@property | ||
def type(self) -> str: ... | ||
@property | ||
def url(self) -> str: ... | ||
def clone(self) -> "FetchResponse": ... | ||
async def buffer(self) -> JsProxy: ... | ||
async def string(self) -> str: ... | ||
async def json(self, **kwargs: Any) -> Any: ... | ||
async def memoryview(self) -> memoryview: ... | ||
async def bytes(self) -> bytes: ... | ||
async def _into_file(self, f: TextIO | BinaryIO) -> None: ... | ||
async def unpack_archive( | ||
self, *, extract_dir: str | None, format: str | None | ||
) -> None: ... | ||
|
||
async def pyfetch(url: str, **kwargs: Any) -> FetchResponse: ... |