Surface / foldkit-surface
Surfaces
A Surface binds a projection and the Messages a feature may use into a named,
inspectable contract. App.surface(name, { params?, model, messages? }) is
the form to write: params are the fields of a Schema.Struct (or a schema,
kept as is), and model returns a Projection, or an object of Projections and
field refs that becomes Projection.struct.
const TodoDetail = App.surface('TodoDetail', {
model: ({ model }) => ({ todos: model.todos, selectedTodoId: model.selectedTodoId }),
messages: [Message.ToggledTodo],
})
// A parameterized Surface may read `params`; dynamic lookups are OptionalRefs.
const ById = App.surface('ById', {
params: { id: Schema.String },
model: ({ model, params }) => ({ todo: model.todosById.at(params.id) }),
})
Surface.read(TodoDetail, root) // project purely
Surface.read(ById, root, { id: 't1' })
Surface.view(TodoDetail, render) // bind a renderer
Surface.rootView(TodoDetail, undefined, render) // bound to the app rootThe Surface's Model is the projected value ({ todos; selectedTodoId }), its
Params the fields, and its Message the union of the constructors listed; a
constructor from another application is a compile error at the constructor.
A parameterized Surface's projection is not evaluated until it has params.
Surface.at(surface, params) is the Surface as the Model activates it: params
is the value, or a function of the Model returning it as an Option (none while
the Surface is inactive, on another route say). Its projectionOf(model) is the
projection for those params, an Option too, and it carries the Surface's
owner and messages, the tags of the Messages the Surface lists; a Subscription derives what to fetch from a list of them
(foldkit-remote's Data.subscriptions).
Surface.each(child, { from, instances }) is one Surface definition as zero to
many keyed instances: instances maps the parent's projected Model to
{ key, params } items, and there is an instance per item while the parent is
active, none while it is not. Keys are stable strings (__proto__ and
duplicates throw). The family keeps its from parent, so a consumer can fetch
the parent first and what the parent reveals after. SurfaceSource is the
shared shape — at/when values resolve their lone instance through it, keyed
by Surface name — read with Surface.instances(source, model).
Surface.make, the explicit form
Surface.make(app, name, { Params?, model, messages? }) takes the wrappers
App.surface lifts: Params as a schema and model returning a Projection
(Projection.struct({ … }) for several fields). App.surface compiles to it.