From 49f22395cca5407d6fd050348d3a5f8e68cf4703 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Fri, 1 Dec 2023 13:09:49 +0000 Subject: [PATCH] Readme (#15) --- README.md | 172 ++++++++++++++++++++++++++++- package-lock.json | 8 +- packages/fastui-prebuilt/README.md | 2 +- packages/fastui/README.md | 2 +- packages/fastui/src/Defaults.tsx | 6 +- screenshot.png | Bin 0 -> 718092 bytes 6 files changed, 176 insertions(+), 14 deletions(-) create mode 100644 screenshot.png diff --git a/README.md b/README.md index c14329b2..27131f5c 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,172 @@ [![versions](https://img.shields.io/pypi/pyversions/fastui.svg)](https://github.com/samuelcolvin/FastUI) [![license](https://img.shields.io/github/license/samuelcolvin/FastUI.svg)](https://github.com/samuelcolvin/FastUI/blob/main/LICENSE) -Packages: +**Please note:** FastUI is still an active work in progress, do not expect it to be complete. -- npm: [`@pydantic/fastui`](https://www.npmjs.com/package/@pydantic/fastui) -- npm: [`@pydantic/fastui-bootstrap`](https://www.npmjs.com/package/@pydantic/fastui-bootstrap) -- cdn: [`@pydantic/fastui-prebuilt`](https://www.jsdelivr.com/package/npm/@pydantic/fastui-prebuilt) - jsdelivr via npm package +## The Principle (short version) + +You can see a simple demo of an application built with FastUI [here](https://fastui-demo.onrender.com). + +FastUI is a new way to build web application user interfaces defined by declarative Python code. + +This means: + +- **If you're a Python developer** — you can build responsive web applications using React without writing a single line of JavaScript, or touching `npm`. +- **If you're a frontend developer** — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view. +- **For everyone** — a true separation of concerns, the backend defines the entire application; while the frontend is free to implement just the user interface + +At its heart, FastUI is a set of matching [Pydantic](https://docs.pydantic.dev) models and TypeScript interfaces that allow you to define a user interface. This interface is validated at build time by TypeScript and pyright/mypy and at runtime by Pydantic. + +## The Practice — Usage + +FastUI is made up of 4 things: + +- [`fastui` PyPI package](https://pypi.python.org/pypi/fastui) - Pydantic models for UI components, and some utilities. While it works well with [FastAPI](https://fastapi.tiangolo.com) it doesn't depend on FastAPI, and most of it could be used with any python web framework. +- [`@pydantic/fastui` npm package](https://www.npmjs.com/package/@pydantic/fastui) - a React TypeScript package that let's you reuse the machinery and types of FastUI while implementing your own components +- [`@pydantic/fastui-bootstrap` npm package](https://www.npmjs.com/package/@pydantic/fastui-bootstrap) - implementation/customisation of all FastUI components using [Bootstrap](https://getbootstrap.com) +- [`@pydantic/fastui-prebuilt` npm package](https://www.jsdelivr.com/package/npm/@pydantic/fastui-prebuilt) (available on [jsdelivr.com CDN](https://www.jsdelivr.com/package/npm/@pydantic/fastui-prebuilt)) providing a pre-built version of the FastUI React app so you can use it without installing any npm packages or building anything yourself. The Python package provides a simple HTML page to serve this app. + +Here's a simple but complete FastAPI application that uses FastUI to show some user profiles: + +```python +from datetime import date + +from fastapi import FastAPI, HTTPException +from fastapi.responses import HTMLResponse +from fastui import FastUI, AnyComponent, prebuilt_html, components as c +from fastui.components.display import DisplayMode, DisplayLookup +from fastui.events import GoToEvent, BackEvent +from pydantic import BaseModel, Field + +app = FastAPI() + + +class User(BaseModel): + id: int + name: str + dob: date = Field(title='Date of Birth') + + +# define some users +users = [ + User(id=1, name='John', dob=date(1990, 1, 1)), + User(id=2, name='Jack', dob=date(1991, 1, 1)), + User(id=3, name='Jill', dob=date(1992, 1, 1)), + User(id=4, name='Jane', dob=date(1993, 1, 1)), +] + + +@app.get("/api/", response_model=FastUI, response_model_exclude_none=True) +def users_table() -> list[AnyComponent]: + """ + Show a table of four users, `/api` is the endpoint the frontend will connect to + when a user fixes `/` to fetch components to render. + """ + return [ + c.Page( # Page provides a basic container for components + components=[ + c.Heading(text='Users', level=2), # renders `

Users

` + c.Table[User]( # c.Table is a generic component parameterized with the model used for rows + data=users, + # define two columns for the table + columns=[ + # the first is the users, name rendered as a link to their profile + DisplayLookup(field='name', on_click=GoToEvent(url='/user/{id}/')), + # the second is the date of birth, rendered as a date + DisplayLookup(field='dob', mode=DisplayMode.date), + ], + ), + ] + ), + ] + + +@app.get("/api/user/{user_id}/", response_model=FastUI, response_model_exclude_none=True) +def user_profile(user_id: int) -> list[AnyComponent]: + """ + User profile page, the frontend will fetch this when the user visits `/user/{id}/`. + """ + try: + user = next(u for u in users if u.id == user_id) + except StopIteration: + raise HTTPException(status_code=404, detail="User not found") + return [ + c.Page( + components=[ + c.Heading(text=user.name, level=2), + c.Link(components=[c.Text(text='Back')], on_click=BackEvent()), + c.Details(data=user), + ] + ), + ] + + +@app.get('/{path:path}') +async def html_landing() -> HTMLResponse: + """Simple HTML page which serves the React app, comes last as it matches all paths.""" + return HTMLResponse(prebuilt_html(title='FastUI Demo')) +``` + +Which renders like this: + +![screenshot](./screenshot.png) + +Of course, that's a very simple application, the [full demo](https://fastui-demo.onrender.com) is more complete. + +### Components + +FastUI already defines the following components: + +- `Text` - renders a string +- `Paragraph` - renders a string as a paragraph +- `PageTitle` - renders nothing, sets the HTML page title +- `Div` - renders a `
` with arbitrary components inside +- `Page` - a container for components, [example](https://fastui-demo.onrender.com) +- `Heading` - renders a heading `

` to `

`, [example](https://fastui-demo.onrender.com) +- `Markdown` - renders markdown, [example](https://fastui-demo.onrender.com) +- `Code` - renders code with highlighting in a `
`
+- `Button` - renders a `