Skip to content

Commit f32e5bf

Browse files
authored
Extending Hyperdiv (#9)
Add a docs section named "Extending Hyperdiv", documenting the various ways Hyperdiv can be extended with custom CSS, JS, and new components.
1 parent 9e244ad commit f32e5bf

File tree

9 files changed

+688
-3
lines changed

9 files changed

+688
-3
lines changed

assets/iframe-example.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<html>
2+
<body>
3+
Hello, this is an iframe!
4+
</body>
5+
</html>

hyperdiv_docs/code_examples.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def code_example(code, code_to_execute=None):
6060
with hd.box(grow=1, basis=0, min_width=18):
6161
with hd.box(padding=1, gap=1):
6262
if state.error:
63-
hd.text(f"Error: {state.error}", font_color="red")
63+
hd.markdown(f"`Error: {state.error}`", font_color="red")
6464
if hd.button("Reset", size="small").clicked:
6565
state.error = None
6666
else:

hyperdiv_docs/menu.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
from .pages.guide.static_assets import static_assets
1717
from .pages.guide.deploying import deploying
1818
from .pages.guide.matplotlib_charts import matplotlib_charts
19-
from .pages.guide.plugins import plugins
19+
20+
from .pages.extending_hyperdiv.overview import overview as extending_overview
21+
from .pages.extending_hyperdiv.plugins import plugins
22+
from .pages.extending_hyperdiv.custom_assets import custom_assets
23+
from .pages.extending_hyperdiv.built_in_components import built_in_components
24+
from .pages.extending_hyperdiv.new_components import new_components
2025

2126
from .pages.reference.components import components
2227
from .pages.reference.env_variables import env_variables
@@ -45,9 +50,15 @@
4550
"Using The App Template": {"href": using_the_app_template.path},
4651
"Matplotlib Charts": {"href": matplotlib_charts.path},
4752
"Static Assets": {"href": static_assets.path},
48-
"Building Custom Components": {"href": plugins.path},
4953
"Deploying Hyperdiv": {"href": deploying.path},
5054
},
55+
"Extending Hyperdiv": {
56+
"Overview": {"href": extending_overview.path},
57+
"Building Plugins": {"href": plugins.path},
58+
"Loading Custom Assets": {"href": custom_assets.path},
59+
"Extending Built-In Components": {"href": built_in_components.path},
60+
"Creating New Components": {"href": new_components.path},
61+
},
5162
"Reference": {
5263
"Hyperdiv API": {"href": components.path},
5364
"Design Tokens": {"href": design_tokens.path},

hyperdiv_docs/pages/extending_hyperdiv/__init__.py

Whitespace-only changes.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import hyperdiv as hd
2+
from ...router import router
3+
from ...page import page
4+
from ...code_examples import docs_markdown
5+
6+
7+
@router.route("/extending-hyperdiv/built-in-components")
8+
def built_in_components():
9+
with page() as p:
10+
p.title("# Extending Built-In Components")
11+
12+
docs_markdown(
13+
"""
14+
15+
Hyperdiv built-in components can be extended with new
16+
style props. Also, if a built-in style prop doesn't expose
17+
CSS attributes that you need, you can override/replace
18+
that prop with a prop that does.
19+
20+
Hyperdiv's prop and type system enables defining props
21+
that are compiled to CSS and applied to the component in
22+
the browser. For example, we can create a new component
23+
type that extends @component(box) with an
24+
[opacity](https://developer.mozilla.org/en-US/docs/Web/CSS/opacity)
25+
prop:
26+
27+
```py
28+
class opacity_box(hd.box):
29+
opacity = hd.Prop(
30+
hd.CSSField(
31+
"opacity",
32+
hd.Optional(hd.ClampedFloat(0, 1))
33+
),
34+
None
35+
)
36+
37+
with opacity_box(
38+
opacity=0.3,
39+
border="1px solid green",
40+
padding=1,
41+
gap=1,
42+
) as box:
43+
hd.text("A low-opacity box.")
44+
hd.button("A button")
45+
46+
```
47+
48+
Hyperdiv provides a type, @prop_type(CSSField), that
49+
enables defining new CSS attributes. In the example above,
50+
we define a new prop, `opacity`. Its type,
51+
`CSSField("opacity", hd.Optional(hd.ClampedFloat(0, 1)))`,
52+
specifies that
53+
54+
1. this prop will be compiled to CSS
55+
2. the CSS attribute name is `"opacity"`
56+
3. the allowed values are floats between 0 and 1. The
57+
value of the prop will be rendered directly in the CSS
58+
`opacity` field.
59+
60+
The CSS `opacity: 0.3;` will be added to instances of this
61+
component.
62+
63+
CSS props work like normal Hyperdiv props and can be read
64+
and mutated by Python code. For example, we can control
65+
the box opacity with a slider:
66+
67+
```py
68+
class opacity_box(hd.box):
69+
opacity = hd.Prop(
70+
hd.CSSField(
71+
"opacity",
72+
hd.Optional(hd.ClampedFloat(0, 1))
73+
),
74+
None
75+
)
76+
77+
opacity = hd.slider(
78+
min_value=0,
79+
max_value=1,
80+
value=1,
81+
step=0.01
82+
)
83+
84+
with opacity_box(
85+
opacity=opacity.value,
86+
border="1px solid green",
87+
padding=1,
88+
gap=1,
89+
) as box:
90+
hd.text("Drag the slider to change the opacity.")
91+
hd.button("A button")
92+
93+
```
94+
95+
Note that when the value of a CSS prop is `None`, its
96+
corresponding CSS field will not be sent to the browser at
97+
all. `None` corresponds to default browser behavior.
98+
99+
The props defined by @component(Styled), from which most
100+
Hyperdiv components inherit, are defined in this way.
101+
102+
"""
103+
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import hyperdiv as hd
2+
from ...router import router
3+
from ...page import page
4+
from ...code_examples import docs_markdown
5+
6+
7+
@router.route("/extending-hyperdiv/custom-assets")
8+
def custom_assets():
9+
with page() as p:
10+
p.title("# Loading Custom Assets")
11+
12+
docs_markdown(
13+
"""
14+
15+
Hyperdiv can be extended by loading custom JS and CSS at
16+
the top level of the app. For example, you can [set up
17+
Google
18+
Analytics](https://www.w3schools.com/howto/howto_google_analytics.asp)
19+
for your app, which involves adding Google's custom
20+
Javascript tags to the app's top-level. Or, load your own
21+
custom Javascript that runs every time the app is loaded.
22+
23+
To learn how to load custom assets at the top level, see
24+
the documentation for @component(index_page).
25+
26+
"""
27+
)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import hyperdiv as hd
2+
from ...router import router
3+
from ...page import page
4+
from ...code_examples import docs_markdown
5+
6+
7+
@router.route("/extending-hyperdiv/new-components")
8+
def new_components():
9+
with page() as p:
10+
p.title("# Creating New Components")
11+
12+
docs_markdown(
13+
"""
14+
15+
In addition to being able to extend existing components
16+
with custom CSS props, you can create simple components
17+
targeting HTML tags that are currently unsupported by core
18+
Hyperdiv. Note that this technique only works when you
19+
don't need to run custom Javascript. If you need custom
20+
Javascript, build a [plugin](/extending-hyperdiv/plugins).
21+
22+
For example, Hyperdiv does not include a built-in
23+
[`iframe`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)
24+
component, but we can create one:
25+
26+
```py
27+
class iframe(hd.Component, hd.Styled):
28+
_tag = "iframe"
29+
30+
src = hd.Prop(hd.PureString)
31+
32+
iframe(
33+
src="/assets/iframe-example.html",
34+
border="1px solid neutral",
35+
height=10,
36+
)
37+
```
38+
39+
We create a new component by inheriting from Hyperdiv's
40+
component base class, @component(Component). Hyperdiv
41+
expects a component's HTML tag to be set using the `_tag`
42+
class variable. In this case the tag is `iframe`, causing
43+
Hyperdiv to render this component in the browser using an
44+
`<iframe>` tag.
45+
46+
In the example above, we also inherit from
47+
@component(Styled), allowing us to use style props to
48+
style the outer `<iframe>` container with style props like
49+
`width` and `border`.
50+
51+
Iframes work by loading a web page specified by the `src`
52+
attribute, so we define a string prop named `src`.
53+
54+
The example above will be rendered in the browser as:
55+
56+
```html
57+
<iframe id="..." src="/assets/iframe-example.html"></iframe>
58+
```
59+
60+
Obviously, additional props can be defined, targeting
61+
additional HTML attributes supported by `iframe`.
62+
63+
"""
64+
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import hyperdiv as hd
2+
from ...router import router
3+
from ...page import page
4+
5+
6+
@router.route("/extending-hyperdiv")
7+
def overview():
8+
with page() as p:
9+
p.title("# Extending Hyperdiv")
10+
11+
hd.markdown(
12+
"""
13+
14+
Hyperdiv can be extended in multiple ways, with new
15+
functionality that isn't already built-in.
16+
17+
The sub-sections of this part of the documentation explore
18+
the various ways Hyperdiv can be extended with new
19+
functionality:
20+
21+
* [Building Plugins](/extending-hyperdiv/plugins) -
22+
building new, self-contained, reusable components with
23+
custom Javascript, CSS, and other assets.
24+
25+
* [Loading Custom Assets](/extending-hyperdiv/assets) -
26+
Loading custom Javascript and CSS assets into the
27+
top-level application.
28+
29+
* [Extending Built-In
30+
Components](/extending-hyperdiv/built-in-components) -
31+
Subclassing and extending existing Hyperdiv components.
32+
33+
* [Creating New
34+
Components](/extending-hyperdiv/new-components) - An
35+
alternative way to define new, simple components without
36+
building plugins.
37+
38+
"""
39+
)

0 commit comments

Comments
 (0)