Skip to content

Latest commit

 

History

History
191 lines (154 loc) · 5.07 KB

File metadata and controls

191 lines (154 loc) · 5.07 KB

Framework Adapters

Greeble components are backend-agnostic, but the library includes helpers and patterns for the most common Python frameworks. Use the CLI to scaffold templates and static assets, then wire endpoints as shown below.

FastAPI

uv run greeble add modal
uv run greeble add table
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from greeble.adapters.fastapi import template_response

app = FastAPI()
templates = Jinja2Templates("templates")

@app.get("/", response_class=HTMLResponse)
async def home(request: Request) -> HTMLResponse:
    return templates.TemplateResponse("greeble/modal.html", {"request": request})

@app.get("/modal/example")
async def modal_example(request: Request) -> HTMLResponse:
    return template_response(
        templates,
        template_name="greeble/modal.html",
        context={"request": request},
        request=request,
        partial_template="greeble/modal.partial.html",
    )

Django

uv run greeble add table --project ./django_project

Add templates/greeble/table.html to your Django project, then reference it from a view:

from django.shortcuts import render


def inventory_view(request):
    return render(request, "greeble/table.html", {})

Use helpers from greeble.adapters.django to detect HTMX and render partials conditionally:

from django.http import HttpRequest, HttpResponse
from greeble.adapters import django as g_django

def table(request: HttpRequest) -> HttpResponse:
    context = {"rows": [...]}
    return g_django.template_response(
        template_name="greeble/table.html",
        partial_template="greeble/table.partial.html",
        context=context,
        request=request,
        triggers={"greeble:table:update": {"page": 1}},
    )

Load the template tags and add CSRF to HTMX requests via hx-headers:

{% load greeble_tags %}

<form
  hx-post="/form/submit"
  hx-headers='{% greeble_csrf_headers %}'
  hx-target="#form-status" hx-swap="innerHTML">
  ...
</form>

{% with pagination=greeble_pagination_context paginated_accounts %}
  {% include "greeble/pagination.html" with pagination=pagination %}
{% endwith %}

Django settings configuration:

# settings.py
INSTALLED_APPS = [
    # ...
    "greeble.adapters",            # optional: legacy helpers (`greeble.adapters.django`)
    "packages.adapters.greeble_django",  # provides template tags and middleware
]

MIDDLEWARE = [
    # ...
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "packages.adapters.greeble_django.middleware.GreebleMessagesToToastsMiddleware",
]

Project code can also import helpers directly from the packaged adapter:

from packages.adapters.greeble_django import (
    csrf_header,
    csrf_headers_json,
    paginate_sequence,
    pagination_context,
)

Those utilities mirror the FastAPI adapter behaviour and keep imports lazy so they can safely ship in starter templates even before Django is installed.

Client toast listener (minimal example):

<script>
  (function () {
    function renderToast(t) {
      var root = document.getElementById("greeble-toasts");
      if (!root) return;
      var el = document.createElement("div");
      el.className = "greeble-toast greeble-toast--" + (t.level || "info");
      el.setAttribute("role", "status");
      var body = document.createElement("div");
      body.className = "greeble-toast__body";
      var title = document.createElement("p");
      title.className = "greeble-toast__title";
      title.textContent = t.title || "";
      var msg = document.createElement("p");
      msg.className = "greeble-toast__message";
      msg.textContent = t.message || "";
      body.appendChild(title);
      body.appendChild(msg);
      var btn = document.createElement("button");
      btn.className = "greeble-toast__dismiss";
      btn.setAttribute("aria-label", "Dismiss");
      btn.textContent = "×";
      btn.addEventListener("click", function () { el.remove(); });
      el.appendChild(body);
      el.appendChild(btn);
      root.appendChild(el);
      setTimeout(function () { el.remove(); }, 5000);
    }
    document.addEventListener("greeble:toast", function (e) {
      var d = e.detail;
      if (Array.isArray(d)) d.forEach(renderToast); else if (d) renderToast(d);
    });
  })();
  </script>

Flask

uv run greeble add drawer --project ./flask_app
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def home():
    return render_template("greeble/drawer.html")

Use helpers from greeble.adapters.flask to detect HTMX and render fragments:

from flask import Flask, request
from greeble.adapters import flask as g_flask

app = Flask(__name__)

@app.get("/drawer/open")
def drawer_open():
    return g_flask.template_response(
        template_name="greeble/drawer.html",
        partial_template="greeble/drawer.partial.html",
        context={},
        request=request,
        triggers={"greeble:drawer:open": True},
    )