Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[dev,pandas]"
python -m pip install -e ".[all]"

- name: Run tests
run: python -m pytest tests
Expand Down
1 change: 0 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ include README.md
include LICENSE
include requirements.txt
include requirements-dev.txt
include requirements-docs.txt
include requirements-pandas.txt
recursive-include src *.yml *.yaml
144 changes: 136 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,32 @@ It currently provides three main entry points:
- `form(source, flavor="")` for plugin-driven forms
- `table(source, variant="std")` for plugin-driven tabular views

When `nicegui_builder` is imported, it also attaches those entry points to NiceGUI's `ui` object at runtime:

- `ui.builder(...)`
- `ui.form_builder(...)`
- `ui.table_builder(...)`

This is intentionally a runtime patch of the NiceGUI `ui` object rather than an officially supported NiceGUI extension point.
It works well in practice, but it should be understood as a convenience layer provided by `nicegui-builder`, not as a contract owned by NiceGUI itself.

If you want editor completion for those extra methods without modifying NiceGUI itself, prefer importing `ui` from `nicegui_builder`:

```python
from nicegui_builder import ui
```

That exported `ui` is the same runtime object as `nicegui.ui`, but `nicegui_builder` ships typing metadata for the added methods.

For this workspace, VS Code/Pylance can also enrich `from nicegui import ui` directly through the local stub path configured in [`.vscode/settings.json`](.vscode/settings.json).
That setup uses [`typings/nicegui/ui.pyi`](typings/nicegui/ui.pyi) to expose `ui.builder(...)`, `ui.form_builder(...)`, and `ui.table_builder(...)` to code completion without modifying NiceGUI itself.

Patch alert:

- the runtime methods are attached dynamically by `nicegui_builder`
- the editor completion for `from nicegui import ui` comes from local workspace stubs, not from NiceGUI upstream
- outside a workspace that loads those stubs, code completion may fall back to whatever the editor infers from the installed NiceGUI package alone

## Install

Base install:
Expand All @@ -35,6 +61,18 @@ Optional `pandas` support:
pip install "nicegui-builder[pandas]"
```

Optional `pydantic` support:

```bash
pip install "nicegui-builder[pydantic]"
```

Everything included:

```bash
pip install "nicegui-builder[all]"
```

CLI entry point:

```bash
Expand Down Expand Up @@ -67,6 +105,32 @@ builder(layout)
ui.run()
```

`builder(...)` returns the root NiceGUI component.
If layout nodes declare `ref`, that root component also exposes a `component_refs` dictionary for later lookup.

Example:

```yaml
- card:
ref: profile_card
children:
- label:
ref: title_label
params:
text: Profile
```

```python
root = builder(layout)
root.component_refs["profile_card"]
root.component_refs["title_label"]
```

For the declarative layout language itself, see:

- [`docs/layout-schema.md`](docs/layout-schema.md)
- [`schemas/layout.schema.json`](schemas/layout.schema.json)

### `form(source, flavor="")`

Use `form(...)` when you want a plugin to inspect a supported source and render a form.
Expand Down Expand Up @@ -107,20 +171,37 @@ form(Contact, flavor="filters")
handle = form(Contact, flavor="actionable")
```

For `datetime` fields, the built-in `pydantic` plugin uses a split `date_input + time_input` widget by default.
That split stays grouped as one logical field, but the layout can choose its wrapper container.
For `datetime` fields, the built-in `pydantic` plugin uses a dedicated `datetime_input` component.
That component internally renders a coordinated `date_input + time_input` pair as one logical field, and the layout can choose its wrapper container.

Example:

```yaml
- field__starts_at:
container: grid
params:
columns: 2
container:
methods: grid
params:
columns: 2
classes: gap-2
classes: col-span-12 gap-2
```

That lets you keep the date/time pair together while placing it inside a `row`, `column`, `grid`, or another declarative container.
That lets you keep the internal date/time pair together while placing it inside a `row`, `column`, `grid`, or another declarative container.

All `pydantic` fields are also exposed through `handle.component_refs`.
By default, field refs use the logical name `field:<fieldname>`.

For split `datetime` fields, the logical ref resolves to a composite object with `.date` and `.time`.

Example:

```python
starts_at = handle.component_refs["field:starts_at"]
starts_at.container
starts_at.date
starts_at.time
```

### `table(source, variant="std")`

Expand Down Expand Up @@ -150,6 +231,28 @@ For the `pandas` plugin, a richer filtered table variant is also available:
handle = table(df, variant="filters")
```

The built-in filtered table UI exposes operator symbols out of the box:

- `∋` contains
- `=` equals
- `≠` not equals
- `>` greater than
- `≥` greater than or equal
- `<` less than
- `≤` less than or equal
- `⋖` starts with
- `⋗` ends with
- `∈` in
- `∉` not in
- `≈` regex
- `⋯` between

The UI lets you choose a field, an operator, and one or more values, then add that filter to an active filter list.
Active filters can be enabled or disabled with a checkbox and removed from the list without losing the builder state.
For `in` and `notIn`, the current UI accepts comma-separated values.
For numeric and datetime columns, `between` renders two inputs.
For `datetime` columns, the built-in filter UI reuses the same `datetime_input` component as the `pydantic` forms.

## Working With Form Handles

`form(...)` returns a `FormHandle`.
Expand Down Expand Up @@ -243,6 +346,17 @@ handle.action_bar(
)
```

Component refs:

```python
handle.component_refs["field:email"]
handle.get_component("field:email")

starts_at = handle.component_refs["field:starts_at"]
starts_at.date
starts_at.time
```

## Working With Table Handles

`table(...)` returns a `TableHandle`.
Expand All @@ -269,8 +383,19 @@ Filter:
```python
handle.set_filter("name", "ada", op="contains")
handle.set_filter("score", [10, 20], op="between")
handle.set_filter("status", "confirmed, waitlist", op="in")
handle.apply_filters()
handle.clear_filters().apply_filters()
handle.normalized_filter_values()
handle.clear_filters()
```

`normalized_filter_values()` returns the active filter clauses in normalized form:

```python
{
"name": {"op": "contains", "value": "ada"},
"score": {"op": "between", "value": [10, 20]},
}
```

CRUD-style actions:
Expand Down Expand Up @@ -350,9 +475,12 @@ Example:
methods: email
classes: w-full
- field__starts_at:
container: grid
params:
columns: 2
container:
methods: grid
params:
columns: 2
classes: gap-2
classes: col-span-12 gap-2
```

Expand Down
34 changes: 29 additions & 5 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Examples:
- validate and submit a form
- sort/filter/export a table
- expose the spec and the root component
- expose component refs for later component lookup

## View 1: Core Specs

Expand Down Expand Up @@ -195,9 +196,9 @@ classDiagram
class FieldPlugin {
<<protocol>>
+inspect_fields(source)
+resolve_field_node(model_class, model_instance, fieldname, value)
+build_layout(source, flavor)
+build_field_context(model_class, model_instance, fieldname)
+resolve_field_node(model_class, model_instance, fieldname, value)
+resolve_widget(spec, variant)
+render_form(source, flavor)
}
Expand All @@ -206,6 +207,8 @@ classDiagram
<<protocol>>
+inspect_collection(source)
+resolve_collection_widget(spec, variant)
+prepare_rows(source)
+filter_rows(source, filter_values)
+render_collection(source, spec, variant)
}

Expand Down Expand Up @@ -238,7 +241,7 @@ classDiagram
### Important idea

The registry resolves the right plugin for a source.
After that, the rest of the pipeline works through plugin capabilities rather than source-specific conditionals spread across the codebase.
After that, the rest of the pipeline works through an explicit contract for that plugin family rather than repeating capability checks at every call site.

## View 3: Runtime Handles

Expand All @@ -249,10 +252,12 @@ classDiagram
class ViewHandle {
+root_component
+plugin
+component_refs
+spec
+plugin_name
+spec_type
+describe()
+get_component(ref)
+refresh()
+show()
+hide()
Expand Down Expand Up @@ -319,6 +324,7 @@ They are lightweight view-oriented facades:
- `ViewHandle` gives a common base
- `FormHandle` adds form state, validation, submit, and live helpers
- `TableHandle` adds rows, filters, selection, export, and table actions
- handles can also expose `component_refs` for named runtime lookup

## Builder Role

Expand All @@ -329,7 +335,8 @@ What matters architecturally is this:
- it renders normalized declarative nodes
- it resolves context values
- it supports plugin-assisted node expansion such as `field__name`
- it registers refs so runtime handles can interact with rendered components
- it keeps a small explicit runtime context for refs and root-component tracking
- it collects named refs into `component_refs`

In other words, the builder is the bridge between normalized layout decisions and actual NiceGUI components.

Expand All @@ -349,7 +356,8 @@ Examples of notable behavior:

- automatic form layouts
- widget mapping from `pydantic-nicegui.yml`
- split `datetime` handling as `date + time`
- split `datetime` handling through the shared `datetime_input` component
- automatic field refs in `component_refs`, including composite logical refs for split `datetime`

### `pandas`

Expand All @@ -364,7 +372,8 @@ Examples of notable behavior:

- sortable table defaults
- pagination and selection support
- richer filtering workflow
- richer filtering workflow with operator-aware filter building and an active filter list
- canonical filter clauses normalized before filtering

## Design Principles

Expand All @@ -375,6 +384,21 @@ Examples of notable behavior:
- Separate pure state from UI-bound behavior when possible.
- Keep the top-level API stable even while internals evolve.

## Patchy Integrations

Some conveniences in the project are intentionally implemented as thin patches rather than as first-class upstream extension points.

Current examples:

- `nicegui_builder` attaches `builder`, `form`, and `table` to NiceGUI's runtime `ui` object
- the workspace can provide editor completion for those added methods through local stubs in [`typings/nicegui/ui.pyi`](../typings/nicegui/ui.pyi)

These integrations are useful and deliberate, but they should still be understood as package-owned glue:

- runtime behavior is provided by `nicegui-builder`
- editor behavior depends on the local typing setup
- neither mechanism implies that NiceGUI itself natively declares those methods

## Practical Reading Order

If someone is new to the project, the best order is:
Expand Down
9 changes: 5 additions & 4 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[Home](../README.md)

[Previous: Public API](public-api.md) | [Next: Product Roadmap](product-roadmap.md)
[Previous: Layout Schema](layout-schema.md) | [Next: Product Roadmap](product-roadmap.md)

This document proposes a progressive set of examples to document `nicegui-builder`.

Expand Down Expand Up @@ -179,9 +179,9 @@ Goal:
Covers:

- default `datetime` resolution
- split widgets
- the shared `datetime_input` component
- collected combined value
- choosing a wrapper container from the layout when desired
- choosing a wrapper container through `params.container` when desired

Suggested file:

Expand All @@ -196,8 +196,9 @@ Goal:
Covers:

- rich plugin rendering
- filter controls
- a filter builder with an active filter list
- automatic filter metadata
- operator symbols, comma-separated `in` / `notIn`, and `between`

Suggested file:

Expand Down
Loading
Loading