phihung commited on
Commit
2531445
·
1 Parent(s): d1c33d3

Add more examples

Browse files
src/tutorial/_02_bulk_update.py CHANGED
@@ -7,7 +7,7 @@ css = """\
7
 
8
  #toast {
9
  opacity: 0;
10
- transition: opacity 3s ease-out;
11
  background: blue;
12
  color: orange;
13
  }
 
7
 
8
  #toast {
9
  opacity: 0;
10
+ transition: opacity 3s ease-out !important;
11
  background: blue;
12
  color: orange;
13
  }
src/tutorial/_06_lazy_loading.py CHANGED
@@ -7,7 +7,7 @@ css = """
7
  opacity: 0;
8
  }
9
  img {
10
- transition: opacity 300ms ease-in;
11
  }
12
  """
13
 
 
7
  opacity: 0;
8
  }
9
  img {
10
+ transition: opacity 300ms ease-in !important;
11
  }
12
  """
13
 
src/tutorial/_10_progress_bar.py CHANGED
@@ -22,7 +22,7 @@ css = """
22
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
23
  -webkit-transition: width .6s ease;
24
  -o-transition: width .6s ease;
25
- transition: width .6s ease;
26
  }
27
  """
28
 
 
22
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
23
  -webkit-transition: width .6s ease;
24
  -o-transition: width .6s ease;
25
+ transition: width .6s ease !important;
26
  }
27
  """
28
 
src/tutorial/_11_cascading_select.py CHANGED
@@ -5,21 +5,19 @@ from fasthtml.common import H3, Div, Form, Img, Label, Option, Select, fast_app
5
  app, rt = fast_app()
6
 
7
 
8
- @app.get("/page")
9
- def main_page():
10
  return Div(
11
  H3("Pick A Make/Model"),
12
  Form(
13
  Div(
14
  Label("Make"),
15
  Select(
 
 
16
  Option("Audi", value="audi"),
17
  Option("Toyota", value="toyota"),
18
  Option("BMW", value="bmw"),
19
- name="make",
20
- hx_get="/models?sleep=1",
21
- hx_target="#models",
22
- hx_indicator=".htmx-indicator",
23
  ),
24
  ),
25
  Div(
@@ -51,7 +49,7 @@ In this example we show how to make the values in one select depend on the value
51
  To begin we start with a default value for the make select: Audi. We render the model select for this make. We then have the make select trigger a GET to /models to retrieve the models options and target the models select.
52
 
53
  Here is the code:
54
- ::main_page::
55
 
56
  When a request is made to the /models end point, we return the models for that make:
57
  ::load_models::
 
5
  app, rt = fast_app()
6
 
7
 
8
+ @app.get
9
+ def page():
10
  return Div(
11
  H3("Pick A Make/Model"),
12
  Form(
13
  Div(
14
  Label("Make"),
15
  Select(
16
+ name="make", hx_get=load_models.rt(sleep=1), hx_target="#models", hx_indicator=".htmx-indicator"
17
+ )(
18
  Option("Audi", value="audi"),
19
  Option("Toyota", value="toyota"),
20
  Option("BMW", value="bmw"),
 
 
 
 
21
  ),
22
  ),
23
  Div(
 
49
  To begin we start with a default value for the make select: Audi. We render the model select for this make. We then have the make select trigger a GET to /models to retrieve the models options and target the models select.
50
 
51
  Here is the code:
52
+ ::page::
53
 
54
  When a request is made to the /models end point, we return the models for that make:
55
  ::load_models::
src/tutorial/_12_animations.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+
3
+ from fasthtml.common import Button, Div, Form, Hr, Input, Script, Style, fast_app
4
+
5
+ css_demo1 = """
6
+ .smooth {
7
+ transition: all 1s ease-in !important;
8
+ }
9
+ """
10
+ css_demo2 = """
11
+ .fade-me-out.htmx-swapping {
12
+ transition: opacity 2s ease-out !important;
13
+ opacity: 0;
14
+ }
15
+ """
16
+ css_demo3 = """
17
+ #demo3.htmx-added {
18
+ opacity: 0;
19
+ }
20
+ #demo3 {
21
+ opacity: 1;
22
+ transition: opacity 1s ease-out !important;
23
+ }
24
+ """
25
+ css_demo4 = """
26
+ form.htmx-request {
27
+ opacity: .5;
28
+ transition: opacity 300ms linear !important;
29
+ }
30
+ """
31
+ css_demo5 = """
32
+ .demo5.faded {
33
+ opacity:.3;
34
+ }
35
+ .demo5 {
36
+ opacity:1;
37
+ transition: opacity ease-in 900ms !important;
38
+ }
39
+ """
40
+
41
+ app, rt = fast_app(
42
+ hdrs=[
43
+ Style(css_demo1 + css_demo2 + css_demo3 + css_demo4 + css_demo5),
44
+ Script(src="https://unpkg.com/[email protected]/class-tools.js"),
45
+ ],
46
+ pico=False,
47
+ )
48
+
49
+
50
+ @app.get
51
+ def page():
52
+ return Div(
53
+ Div(Div("Example 1: Color Swap"), demo1()),
54
+ Hr(),
55
+ Div(Div("Example 2: Fade Out On Swap"), demo2()),
56
+ Hr(),
57
+ Div(Div("Example 3: Fade In On Addition"), demo3()),
58
+ Hr(),
59
+ Div(Div("Example 4: Request In Flight Animation"), demo4()),
60
+ Hr(),
61
+ Div(Div("Example 5: Class-tools Extension"), demo5()),
62
+ cls="container",
63
+ )
64
+
65
+
66
+ @app.get
67
+ def demo1(idx: int = 0):
68
+ palette = ["red", "blue", "green", "orange"]
69
+ next_idx = (idx + 1) % len(palette)
70
+ return Div(
71
+ "Color Swap Demo",
72
+ id="color-demo",
73
+ style=f"color:{palette[idx]}",
74
+ hx_get=demo1.rt(idx=next_idx),
75
+ hx_swap="outerHTML",
76
+ hx_trigger="every 1s",
77
+ cls="smooth",
78
+ )
79
+
80
+
81
+ @app.get
82
+ def demo2():
83
+ return Button("Fade Me Out", cls="fade-me-out", hx_delete=demo2_delete.rt(), hx_swap="outerHTML swap:2s")
84
+
85
+
86
+ @app.delete
87
+ def demo2_delete():
88
+ return None
89
+
90
+
91
+ @app.get
92
+ def demo3():
93
+ return Button("Fade Me In", hx_get=demo3.rt(), hx_swap="outerHTML settle:1s", id="demo3")
94
+
95
+
96
+ @app.get
97
+ def demo4():
98
+ return Form(hx_post=demo4_form.rt(), hx_swap="outerHTML")(
99
+ Input(name="name", placeholder="Your name here..."), Button("Submit", cls="btn primary")
100
+ )
101
+
102
+
103
+ @app.post
104
+ def demo4_form():
105
+ time.sleep(1)
106
+ return Div("Submitted!")
107
+
108
+
109
+ @app.get
110
+ def demo5():
111
+ return Div(Div("Toggle Demo", cls="demo5", classes="toggle faded:1s"), hx_ext="class-tools")
112
+
113
+
114
+ DESC = "Demonstrates making the values of a select dependent on another select"
115
+ HTMX_URL = "https://htmx.org/examples/value-select/"
116
+ DOC = """
117
+ htmx is designed to allow you to use CSS transitions to add smooth animations and transitions to your web page using only CSS and HTML. Below are a few examples of various animation techniques.
118
+
119
+ htmx also allows you to use the new View Transitions API for creating animations.
120
+
121
+ <blockquote>
122
+ We have use <code>!important</code> in css to bypass <code>(prefers-reduced-motion: reduce)</code> setup by picocss
123
+ </blockquote>
124
+
125
+ ### Basic CSS Animations
126
+
127
+ #### Color Throb
128
+ The simplest animation technique in htmx is to keep the id of an element stable across a content swap. If the id of an element is kept stable, htmx will swap it in such a way that CSS transitions can be written between the old version of the element and the new one.
129
+
130
+ Consider this div:
131
+ ::css_demo1 demo1::
132
+
133
+ This div will poll every second and will get replaced with new content which changes the color style to a new value (e.g. blue):
134
+
135
+ Because the div has a stable id, color-demo, htmx will structure the swap such that a CSS transition, defined on the .smooth class, applies to the style update from red to blue, and smoothly transitions between them.
136
+
137
+ Color Swap Demo
138
+ #### Smooth Progress Bar
139
+ The [Progress Bar demo](/progress-bar) uses this basic CSS animation technique as well, by updating the length property of a progress bar element, allowing for a smooth animation.
140
+
141
+ ### Swap Transitions
142
+ #### Fade Out On Swap
143
+ If you want to fade out an element that is going to be removed when the request ends, you want to take advantage of the htmx-swapping class with some CSS and extend the swap phase to be long enough for your animation to complete. This can be done like so:
144
+ ::css_demo2 demo2 demo2_delete::
145
+
146
+ ### Settling Transitions
147
+ #### Fade In On Addition
148
+ Building on the last example, we can fade in the new content by using the htmx-added class during the settle phase. You can also write CSS transitions against the target, rather than the new content, by using the htmx-settling class.
149
+ ::css_demo3 demo3::
150
+
151
+ ### Request In Flight Animation
152
+ You can also take advantage of the htmx-request class, which is applied to the element that triggers a request. Below is a form that on submit will change its look to indicate that a request is being processed:
153
+ ::css_demo4 demo4::
154
+
155
+ ### Using the htmx class-tools Extension
156
+ Many interesting animations can be created by using the [class-tools](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/class-tools/README.md) extension.
157
+
158
+ Here is an example that toggles the opacity of a div. Note that we set the toggle time to be a bit longer than the transition time. This avoids flickering that can happen if the transition is interrupted by a class change.
159
+ ::css_demo5 demo5::
160
+
161
+ #### Using the View Transition API
162
+ TODO
163
+
164
+ ### Conclusion
165
+ You can use the techniques above to create quite a few interesting and pleasing effects with plain old HTML while using htmx.
166
+ """
src/tutorial/_13_file_upload.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fasthtml.common import H3, Button, Div, Form, Hr, Input, Progress, Script, fast_app
2
+ from starlette.datastructures import UploadFile
3
+
4
+ app, rt = fast_app(hdrs=[Script(src="https://unpkg.com/[email protected]")])
5
+
6
+
7
+ @app.get
8
+ def page():
9
+ return Div(
10
+ Div(H3("Method 1: Pure JS"), Div(method1())),
11
+ Hr(),
12
+ Div(H3("Method 2: Hyperscript"), Div(method2())),
13
+ cls="container",
14
+ )
15
+
16
+
17
+ @app.get
18
+ def method1():
19
+ js = """
20
+ htmx.on('#form', 'htmx:xhr:progress', function(evt) {
21
+ htmx.find('#progress').setAttribute('value', evt.detail.loaded/evt.detail.total * 100)
22
+ });
23
+ """
24
+ form = Form(
25
+ Input(type="file", name="file"),
26
+ Button("Upload"),
27
+ Progress(id="progress", value="0", max="100"),
28
+ Div(id="output"),
29
+ hx_target="#output",
30
+ hx_post=upload.rt(),
31
+ id="form",
32
+ )
33
+ return form, Script(js)
34
+
35
+
36
+ @app.get
37
+ def method2():
38
+ return Form(
39
+ Input(type="file", name="file"),
40
+ Button("Upload"),
41
+ Progress(id="progress2", value="0", max="100"),
42
+ Div(id="output2"),
43
+ hx_target="#output2",
44
+ hx_encoding="multipart/form-data",
45
+ hx_post=upload.rt(),
46
+ _="on htmx:xhr:progress(loaded, total) set #progress2.value to (loaded/total)*100",
47
+ )
48
+
49
+
50
+ @app.post
51
+ async def upload(file: UploadFile):
52
+ # print(len(await file.read()))
53
+ return Div(f"Uploaded! Filename: {file.filename}. Filesize: {file.size}")
54
+
55
+
56
+ DESC = "Demonstrates how to upload a file via ajax with a progress bar"
57
+ DOC = """
58
+ In this example we show how to create a file upload form that will be submitted via ajax, along with a progress bar.
59
+
60
+ We will show two different implementation, one in pure javascript (using some utility methods in htmx) and one in hyperscript
61
+
62
+ First the pure javascript version.
63
+
64
+ - We have a form of type multipart/form-data so that the file will be properly encoded
65
+ - We post the form to /upload
66
+ - We have a progress element
67
+ - We listen for the htmx:xhr:progress event and update the value attribute of the progress bar based on the loaded and total properties in the event detail.
68
+
69
+ ::method1::
70
+
71
+ The Hyperscript version is very similar, except:
72
+
73
+ The script is embedded directly on the form element
74
+ Hyperscript offers nicer syntax (although the htmx API is pretty nice too!)
75
+ ```python
76
+ app, rt = fast_app(hdrs=[Script(src="https://unpkg.com/[email protected]")])
77
+ ```
78
+ ::method2::
79
+
80
+ Note that hyperscript allows you to destructure properties from details directly into variables
81
+ """