@batono/ui
v0.0.32
Published
UI primitives for the Batono server-driven interaction protocol.
Maintainers
Readme
@batono/ui
UI primitives for the Batono server-driven interaction protocol.
@batono/ui provides the standard UI building blocks for the Batono protocol — layout containers, content primitives,
and action definitions. It builds on top of @batono/core and is designed to be used together with it.
Features
- ✅ Peer dependency on
@batono/coreonly - ✅ Layout primitives (
rows,row) - ✅ Content primitives (
text,link,image,icon,button,divider) - ✅ Action definitions (
request,navigate,modal,event) - ✅ Escape hatch for custom nodes (
custom) - ✅ Typed result interfaces for frontend renderers
- ✅ Fully typed with TypeScript
Installation
npm install @batono/ui @batono/coreBasic Usage
import {bt} from '@batono/core'
import {bt as btUi} from '@batono/ui'
const deleteUser = bt.defineFlow(
btUi.request('DELETE', '/users/42')
)
const graph = bt.graph(
btUi.rows(
btUi.row(
btUi.button('Delete User', deleteUser)
)
)
)
res.json(graph)Layout
rows
A vertical stack of items.
btUi.rows(
btUi.row(btUi.text('First')),
btUi.row(btUi.text('Second'))
)Accepts an optional RowsOptions as first argument:
btUi.rows({gap: 8}, btUi.row(...), btUi.row(...))| Option | Type | Description |
|---------|-----------------------------------------|--------------------------------|
| gap | number | Spacing between items |
| align | 'start' \| 'center' \| 'end' \| 'stretch' | Horizontal alignment of items |
row
A horizontal container.
btUi.row(btUi.text('Left'), btUi.text('Right'))Accepts an optional RowOptions as first argument:
btUi.row({gap: 8, wrap: true}, btUi.text('A'), btUi.text('B'))| Option | Type | Description |
|---------|-----------------------------------------|--------------------------------|
| gap | number | Spacing between items |
| align | 'start' \| 'center' \| 'end' \| 'stretch' | Alignment of items |
| wrap | boolean | Allow items to wrap |
Both rows and row support when for conditional items:
import {when} from '@batono/core'
btUi.rows(
btUi.row(btUi.text('Always')),
when(user.isAdmin, btUi.row(btUi.text('Admin only')))
)Content Primitives
text
btUi.text('Hello world')link
btUi.link('Open Profile', '/profile/42')
btUi.link('Open in new tab', '/profile/42', '_blank')image
btUi.image('/avatar.png', 'User avatar')
btUi.image('/avatar.png', 'User avatar', {width: 64, height: 64})icon
btUi.icon('trash')
btUi.icon('trash', {width: 24, height: 24})
btUi.icon('trash', undefined, {color: 'red', variant: 'outline'})button
btUi.button('Delete', deleteFlow)
btUi.button('Delete', deleteFlow, {variant: 'ghost', disabled: false})divider
btUi.divider()custom
Escape hatch for renderer-specific nodes not covered by the standard primitives.
btUi.custom('my-special-widget', {foo: 'bar'})Actions
Action definitions are passed to bt.defineFlow() from @batono/core.
request
btUi.request('POST', '/bookings')
btUi.request('DELETE', '/users/42').withPayload({id: 42})
btUi.request('POST', '/bookings', {id: 1}, {headers: {'X-Token': 'abc'}, timeout: 5000})navigate
btUi.navigate('/dashboard')
btUi.navigate('/profile/42', {id: 42}, {target: '_blank'})modal
btUi.modal('confirm-delete')
btUi.modal('confirm-delete').withPayload({id: 42})event
A generic custom event — handled by the renderer.
btUi.event('user:selected')
btUi.event('user:selected', {id: 42})Frontend Types
@batono/ui exports typed result interfaces for frontend renderers:
import type {
RowsResult,
RowResult,
TextResult,
LinkResult,
ImageResult,
IconResult,
ButtonResult,
DividerResult,
CustomResult,
RequestActionResult,
NavigateActionResult,
ModalActionResult,
EventActionResult,
} from '@batono/ui'All result types extend Defined from @batono/core and carry a $type discriminator for exhaustive switch rendering.
Utilities
import {NBSP} from '@batono/ui'
btUi.text(`${NBSP}·${NBSP}`) // non-breaking space separatorDesign Goals
- Minimal by default — standard primitives only, no framework opinions
- Extensible — use
customfor anything not covered, or implementIBuildabledirectly - Frontend-agnostic — result interfaces work with any renderer (Vue, React, vanilla)
- Composable — every definition is an
IBuildable, freely nestable
License
MIT
