Skip to content

Commit 2d1a9d1

Browse files
committed
add inital work for responding to events
print statements in examples are now displayed in the same view
1 parent 42126d2 commit 2d1a9d1

File tree

14 files changed

+291
-49
lines changed

14 files changed

+291
-49
lines changed

docs/app.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from sanic import Sanic, response
66

7-
from idom.config import IDOM_WED_MODULES_DIR
87
from idom.server.sanic import PerClientStateServer
98
from idom.widgets import multiview
109

@@ -42,8 +41,8 @@ def run():
4241

4342
def make_app():
4443
app = Sanic(__name__)
44+
4545
app.static("/docs", str(HERE / "build"))
46-
app.static("/_modules", str(IDOM_WED_MODULES_DIR.current))
4746

4847
@app.route("/")
4948
async def forward_to_index(request):

docs/examples.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from io import StringIO
34
from pathlib import Path
45
from traceback import format_exc
56
from typing import Callable, Iterator
@@ -32,6 +33,7 @@ def load_one_example(file_or_name: Path | str) -> Callable[[], ComponentType]:
3233
raise FileNotFoundError(str(file))
3334

3435
captured_component_constructor = None
36+
capture_print, ShowPrint = _printout_viewer()
3537

3638
def capture_component(component_constructor):
3739
nonlocal captured_component_constructor
@@ -40,7 +42,7 @@ def capture_component(component_constructor):
4042
idom.run = capture_component
4143
try:
4244
code = compile(file.read_text(), str(file.absolute()), "exec")
43-
exec(code, {})
45+
exec(code, {"print": capture_print})
4446
except Exception:
4547
return _make_error_display()
4648
finally:
@@ -49,7 +51,12 @@ def capture_component(component_constructor):
4951
if captured_component_constructor is None:
5052
return _make_example_did_not_run(str(file))
5153
else:
52-
return captured_component_constructor
54+
55+
@idom.component
56+
def Wrapper():
57+
return idom.html.div(captured_component_constructor(), ShowPrint())
58+
59+
return Wrapper
5360

5461

5562
def get_example_name_from_file(file: Path) -> str:
@@ -64,6 +71,37 @@ def get_js_example_file_by_name(name: str) -> Path:
6471
return EXAMPLES_DIR.joinpath(*name.split(".")).with_suffix(".js")
6572

6673

74+
def _printout_viewer():
75+
print_callbacks: set[Callable[[str], None]] = set()
76+
77+
@idom.component
78+
def ShowPrint():
79+
buffer = idom.hooks.use_state(StringIO)[0]
80+
update = _use_force_update()
81+
82+
@idom.hooks.use_effect
83+
def add_buffer():
84+
print_callbacks.add(buffer.write)
85+
update()
86+
87+
value = buffer.getvalue()
88+
return idom.html.pre({"class": "printout"}, value) if value else idom.html.div()
89+
90+
def capture_print(*args, **kwargs):
91+
buffer = StringIO()
92+
print(*args, file=buffer, **kwargs)
93+
value = buffer.getvalue()
94+
for cb in print_callbacks:
95+
cb(value)
96+
97+
return capture_print, ShowPrint
98+
99+
100+
def _use_force_update():
101+
toggle, set_toggle = idom.hooks.use_state(False)
102+
return lambda: set_toggle(not toggle)
103+
104+
67105
def _make_example_did_not_run(example_name):
68106
@idom.component
69107
def ExampleDidNotRun():
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from idom import component, html, run
2+
3+
4+
@component
5+
def Button():
6+
return html.button("I don't do anything yet")
7+
8+
9+
run(Button)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from idom import component, html, run
2+
3+
4+
@component
5+
def Button():
6+
def handle_event(event):
7+
print(event)
8+
9+
return html.button({"onClick": handle_event}, "Print event to terminal")
10+
11+
12+
run(Button)

docs/source/_static/css/interactive-widget.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,17 @@
66
}
77
.widget-container {
88
padding: 15px;
9+
overflow: auto;
910
background-color: var(--color-code-background);
1011
min-height: 75px;
1112
}
1213

14+
.widget-container .printout {
15+
margin-top: 20px;
16+
border-top: solid 2px var(--color-foreground-border);
17+
padding-top: 20px;
18+
}
19+
1320
.widget-container > div {
1421
width: 100%;
1522
}

docs/source/_static/custom.js

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1977,30 +1977,64 @@ function LazyPromise() {
19771977
});
19781978
}
19791979

1980-
const LOC = window.location;
1981-
const HTTP_PROTO = LOC.protocol;
1982-
const WS_PROTO = HTTP_PROTO === "https:" ? "wss:" : "ws:";
1983-
const IDOM_MODULES_PATH = "/_modules";
1980+
function mountWithLayoutServer(
1981+
element,
1982+
serverInfo,
1983+
maxReconnectTimeout
1984+
) {
1985+
const loadImportSource = (source, sourceType) =>
1986+
import(sourceType == "NAME" ? serverInfo.path.module(source) : source);
1987+
1988+
mountLayoutWithWebSocket(
1989+
element,
1990+
serverInfo.path.stream,
1991+
loadImportSource,
1992+
maxReconnectTimeout
1993+
);
1994+
}
1995+
1996+
function LayoutServerInfo({ host, port, path, query, secure }) {
1997+
const wsProtocol = "ws" + (secure ? "s" : "");
1998+
const httpProtocol = "http" + (secure ? "s" : "");
1999+
2000+
const uri = host + ":" + port;
2001+
const url = (uri + path).split("/").slice(0, -1).join("/");
2002+
2003+
const wsBaseUrl = wsProtocol + "://" + url;
2004+
const httpBaseUrl = httpProtocol + "://" + url;
2005+
2006+
this.path = {
2007+
stream: wsBaseUrl + "/stream" + "?" + query,
2008+
module: (source) => httpBaseUrl + `/modules/${source}`,
2009+
};
2010+
}
19842011

19852012
function mountWidgetExample(
19862013
mountID,
19872014
viewID,
19882015
idomServerHost,
19892016
useActivateButton
19902017
) {
1991-
const idomUrl = "//" + (idomServerHost || LOC.host);
1992-
const httpIdomUrl = HTTP_PROTO + idomUrl ;
1993-
const wsIdomUrl = WS_PROTO + idomUrl ;
2018+
let idomHost, idomPort;
2019+
if (idomServerHost) {
2020+
[idomHost, idomPort] = idomServerHost.split(":", 2);
2021+
} else {
2022+
idomHost = window.location.hostname;
2023+
idomPort = window.location.port;
2024+
}
2025+
2026+
const serverInfo = new LayoutServerInfo({
2027+
host: idomHost,
2028+
port: idomPort,
2029+
path: "/_idom/",
2030+
query: `view_id=${viewID}`,
2031+
secure: window.location.protocol == "https",
2032+
});
19942033

19952034
const mountEl = document.getElementById(mountID);
19962035

19972036
if (!useActivateButton) {
1998-
mountLayoutWithWebSocket(
1999-
mountEl,
2000-
wsIdomUrl + `/_idom/stream?view_id=${viewID}`,
2001-
(source, sourceType) =>
2002-
loadImportSource(httpIdomUrl, source, sourceType)
2003-
);
2037+
mountWithLayoutServer(mountEl, serverInfo);
20042038
return;
20052039
}
20062040

@@ -2013,12 +2047,7 @@ function mountWidgetExample(
20132047
{
20142048
mountEl.removeChild(enableWidgetButton);
20152049
mountEl.setAttribute("class", "interactive widget-container");
2016-
mountLayoutWithWebSocket(
2017-
mountEl,
2018-
wsIdomUrl + `/_idom/stream?view_id=${viewID}`,
2019-
(source, sourceType) =>
2020-
loadImportSource(httpIdomUrl, source, sourceType)
2021-
);
2050+
mountWithLayoutServer(mountEl, serverInfo);
20222051
}
20232052
})
20242053
);
@@ -2046,12 +2075,4 @@ function mountWidgetExample(
20462075
mountEl.appendChild(enableWidgetButton);
20472076
}
20482077

2049-
function loadImportSource(baseUrl, source, sourceType) {
2050-
if (sourceType == "NAME") {
2051-
return import(baseUrl + IDOM_MODULES_PATH + "/" + source);
2052-
} else {
2053-
return import(source);
2054-
}
2055-
}
2056-
20572078
export { mountWidgetExample };

docs/source/adding-interactivity/index.rst

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,110 @@ Adding Interactivity
99
state-as-a-snapshot
1010
dangers-of-mutability
1111
batched-updates
12+
13+
14+
.. dropdown:: :octicon:`bookmark-fill;2em` What You'll Learn
15+
:color: info
16+
:animate: fade-in
17+
:open:
18+
19+
.. grid:: 2
20+
21+
.. grid-item-card:: :octicon:`bell` Responding to Events
22+
:link: responding-to-events
23+
:link-type: doc
24+
25+
...
26+
27+
.. grid-item-card:: :octicon:`package-dependencies` Components With State
28+
:link: components-with-state
29+
:link-type: doc
30+
31+
...
32+
33+
.. grid-item-card:: :octicon:`device-camera-video` State as a Snapshot
34+
:link: state-as-a-snapshot
35+
:link-type: doc
36+
37+
...
38+
39+
.. grid-item-card:: :octicon:`issue-opened` Dangers of Mutability
40+
:link: dangers-of-mutability
41+
:link-type: doc
42+
43+
...
44+
45+
.. grid-item-card:: :octicon:`versions` Batched Updates
46+
:link: batched-updates
47+
:link-type: doc
48+
49+
...
50+
51+
...
52+
53+
54+
Section 1: Responding to Events
55+
-------------------------------
56+
57+
...
58+
59+
.. card::
60+
:link: responding-to-events
61+
:link-type: doc
62+
63+
:octicon:`book` Read More
64+
^^^^^^^^^^^^^^^^^^^^^^^^^
65+
66+
...
67+
68+
69+
Section 2: Components with State
70+
--------------------------------
71+
72+
.. card::
73+
:link: components-with-state
74+
:link-type: doc
75+
76+
:octicon:`book` Read More
77+
^^^^^^^^^^^^^^^^^^^^^^^^^
78+
79+
...
80+
81+
82+
Section 3: State as a Snapshot
83+
------------------------------
84+
85+
.. card::
86+
:link: state-as-a-snapshot
87+
:link-type: doc
88+
89+
:octicon:`book` Read More
90+
^^^^^^^^^^^^^^^^^^^^^^^^^
91+
92+
...
93+
94+
95+
Section 4: Dangers of Mutability
96+
--------------------------------
97+
98+
.. card::
99+
:link: dangers-of-mutability
100+
:link-type: doc
101+
102+
:octicon:`book` Read More
103+
^^^^^^^^^^^^^^^^^^^^^^^^^
104+
105+
...
106+
107+
108+
Section 3: Batched Updates
109+
--------------------------
110+
111+
.. card::
112+
:link: batched-updates
113+
:link-type: doc
114+
115+
:octicon:`book` Read More
116+
^^^^^^^^^^^^^^^^^^^^^^^^^
117+
118+
...
Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,29 @@
11
Responding to Events
22
====================
33

4-
Under construction :)
4+
IDOM lets you add event handlers to your parts of the interface. This means that you can
5+
assign functions that are triggered when a particular user interaction occurs like
6+
clicking, hovering, of focusing on form inputs, and more.
7+
8+
9+
Adding Event Handlers
10+
---------------------
11+
12+
To start out we'll just display a button that, for the moment, doesn't do anything:
13+
14+
.. example:: adding_interactivity.button_does_nothing
15+
:activate-result:
16+
17+
To add an event handler to this button we'll do three things:
18+
19+
1. Declare a function called ``handle_event(event)`` inside the body of our ``Button`` component
20+
2. Add logic to ``handle_event`` that will print the ``event`` it receives to the console.
21+
3. Add an ``"onClick": handle_event`` attribute to the ``<button>`` element.
22+
23+
.. example:: adding_interactivity.button_prints_event
24+
:activate-result:
25+
26+
.. note::
27+
28+
Normally print statements will only be displayed in the terminal where you launched
29+
the server.

docs/source/credits-and-licenses.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
Credits and Licenses
22
====================
33

4-
Much of this documentation was created with heavy influence from https://reactjs.org
5-
which uses the `Creative Commons Attribution 4.0 International
4+
Much of this documentation, including its layout and content, was created with heavy
5+
influence from https://reactjs.org which uses the `Creative Commons Attribution 4.0
6+
International
67
<https://raw.githubusercontent.com/reactjs/reactjs.org/b2d5613b6ae20855ced7c83067b604034bebbb44/LICENSE-DOCS.md>`__
7-
license. While much of the content has been transformed in one way or another, we
8-
paraphrase or copy directly in places where React's behavior is mirroried by IDOM's.
9-
Additionally, the outline of the documentation closely mirror's React's documentation
8+
license. While many things have been transformed, we paraphrase and, in some places,
9+
copy language or examples where React's behavior is directly mirroried by IDOM's.
1010

1111

1212
Source Code License

0 commit comments

Comments
 (0)