pydantic / FastUI

Build better UIs faster.
https://fastui-demo.onrender.com
MIT License
8.15k stars 312 forks source link

Custom Head Component #55

Open 0417taehyun opened 10 months ago

0417taehyun commented 10 months ago

Make custom head tag with meta, title, script, and link tags.

For example, it looks like the one below.

class Head(pydanctic.BaseModel,  extra='forbid')
    elements: list[AnyHeadElement]

def render_head_element(head: Head) -> str:
    additional_tags: str = "\n".join([element in element in head.elements])
    return f"""
    <head>
      <meta charset="UTF-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      {additional_tags}
      <script type="module" crossorigin src="{_PREBUILT_CDN_URL}/index.js"></script>
      <link rel="stylesheet" crossorigin href="{_PREBUILT_CDN_URL}/index.css">        
    </head>
"""

def prebuilt_html(head: Head):
    """
    Returns a simple HTML page which includes the FastUI react frontend, loaded from https://www.jsdelivr.com/.

    Arguments:
        title: page title

    Returns:
        HTML string which can be returned by an endpoint to serve the FastUI frontend.
    """
    # language=HTML
    head: str = render_head_element(head=head)
    return f"""\
<!doctype html>
<html lang="en">
  {head}
  <body>
    <div id="root"></div>
  </body>
</html>
"""

If it is reasonable, can I contribute as pull request?

samuelcolvin commented 10 months ago

Seems reasonable, yes pr please.

Also they might want to customise the CDN URL they use.

hasansezertasan commented 10 months ago

One suggestion from me might be Knio/dominate for more pythonic way to build the HTML instead of writing plain HTML.

@samuelcolvin I can work on it if you are OK to add dominate as a dependency or I can come up with a simpler/smaller alternative to dominate.

samuelcolvin commented 10 months ago

Looks like a cool library, but I'd rather not add the dependency for just this.

hasansezertasan commented 10 months ago

Thank you @samuelcolvin for your response.

@0417taehyun, I believe this could be a code-first solution for you.

import dominate
from dominate.tags import div, link, meta, script
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get(
    path="/{path:path}",
    response_class=HTMLResponse,
)
async def html_landing() -> HTMLResponse:
    """Simple HTML page which serves the React app, comes last as it matches all paths."""
    doc = dominate.document(title="FastUI Demo", lang="en")

    with doc.head:
        meta(charset="utf-8")
        meta(
            name="viewport",
            content="width=device-width, initial-scale=1.0",
        )
        script(
            type="module",
            src="https://cdn.jsdelivr.net/npm/@pydantic/fastui-prebuilt@0.0.10/dist/assets/index.js",
        )
        link(
            rel="stylesheet",
            href="https://cdn.jsdelivr.net/npm/@pydantic/fastui-prebuilt@0.0.10/dist/assets/index.css",
        )

    with doc:
        div(id="root")

    return doc.render()

HTML is created (and editable) programmatically without any JSX or template mess. You can update the title of the document or add another element into the head tag with ease.