Skip to content

Instantly share code, notes, and snippets.

@DurandA
Created November 21, 2023 19:51
Show Gist options
  • Save DurandA/41b49dc1e1d95689af338b4e2e4376e4 to your computer and use it in GitHub Desktop.
Save DurandA/41b49dc1e1d95689af338b4e2e4376e4 to your computer and use it in GitHub Desktop.
HTMX demo with dynamic filters and infinite scroll
Display the source blob
Display the rendered blob
Raw
<svg width="16" height="16" viewBox="0 0 135 140" xmlns="http://www.w3.org/2000/svg" fill="#494949">
<rect y="10" width="15" height="120" rx="6">
<animate attributeName="height"
begin="0.5s" dur="1s"
values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="y"
begin="0.5s" dur="1s"
values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear"
repeatCount="indefinite" />
</rect>
<rect x="30" y="10" width="15" height="120" rx="6">
<animate attributeName="height"
begin="0.25s" dur="1s"
values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="y"
begin="0.25s" dur="1s"
values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear"
repeatCount="indefinite" />
</rect>
<rect x="60" width="15" height="140" rx="6">
<animate attributeName="height"
begin="0s" dur="1s"
values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="y"
begin="0s" dur="1s"
values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear"
repeatCount="indefinite" />
</rect>
<rect x="90" y="10" width="15" height="120" rx="6">
<animate attributeName="height"
begin="0.25s" dur="1s"
values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="y"
begin="0.25s" dur="1s"
values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear"
repeatCount="indefinite" />
</rect>
<rect x="120" y="10" width="15" height="120" rx="6">
<animate attributeName="height"
begin="0.5s" dur="1s"
values="120;110;100;90;80;70;60;50;40;140;120" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="y"
begin="0.5s" dur="1s"
values="10;15;20;25;30;35;40;45;50;0;10" calcMode="linear"
repeatCount="indefinite" />
</rect>
</svg>
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
import random
import asyncio
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/", response_class=HTMLResponse)
async def read_root():
return """
<!DOCTYPE html>
<html>
<head>
<title>HTMX Demo</title>
<script src="https://unpkg.com/[email protected]" integrity="sha384-rgjA7mptc2ETQqXoYC3/zJvkU7K/aP44Y+z7xQuJiVnB/422P/Ak+F/AqFR7E4Wr" crossorigin="anonymous"></script>
</head>
<body>
<h1>Dynamic Filters with Infinite Scroll</h1>
<form action="/filter" method="get" hx-boost="true" hx-target="#results" hx-replace-url="false">
<select name="category">
<option value="">Select Category</option>
<option value="Category1">Category 1</option>
<option value="Category2">Category 2</option>
</select>
<select name="color">
<option value="">Select Color</option>
<option value="Red">Red</option>
<option value="Blue">Blue</option>
</select>
<input type="submit" value="Filter">
</form>
<ul id="results">
<!-- Filtered results will appear here -->
</ul>
<img id="indicator"class="htmx-indicator" width="60" src="/static/bars.svg">
</body>
</html>
"""
def generate_fake_data(page: int, category: str, color: str):
return [{"name": f"Item {i}", "category": category or f"Category{i % 3 + 1}", "color": color or random.choice(["Red", "Blue", "Green"])} for i in range(page * 10, (page + 1) * 10)]
@app.get("/filter", response_class=HTMLResponse)
async def filter(request: Request, category: str = "", color: str = "", page: int = 0):
await asyncio.sleep(0.5)
filtered_data = generate_fake_data(page, category, color)
next_page = page + 1
hx_load_more_attrs = f"""
hx-get="/filter?page={next_page}&category={category}&color={color}"
hx-target="#results"
hx-swap="beforeend"
hx-trigger="revealed"
hx-indicator="#indicator"
"""
return ''.join(
f"""
<li {hx_load_more_attrs if item == filtered_data[-1] else ""}>
{item["name"]} - {item["category"]} - {item["color"]}
</li>
"""
for item in filtered_data
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment