Mixins / foldkit-mixins-ui
toView and resolve
Every component adapter (all but Anchor, which is a Behavior) has both:
Adapter.toView(mixins, { h, input? }, draw) -> the component's toView
Adapter.resolve(attributes, mixins, { h, input? }) -> the resolved bundlestoView is resolve placed where the component calls back, and is the normal
path. It fits a stateless component's view and a Submodel's viewInputs
alike. The Message type comes from h, and a Submodel's value type (a
RadioGroup's or Tabs' Value) from where the toView goes, so neither needs
writing out.
A button drawn as a button needs no draw at all. Button.view takes the
label, a style, and @foldkit/ui's own config:
import { Button } from 'foldkit-mixins-ui'
const saveButton = (disabled: boolean, h: HtmlBuilder<Message>) =>
Button.view(
{
label: 'Save',
style: SaveStyle,
type: 'submit',
disabled,
onClick: Message.Saved(),
},
h,
)A toggle button, shadcn's Toggle, is a Button with pressed: it writes
aria-pressed, and each variant has its on look.
An input or a textarea places its own bundles, so its view takes a draw.
Input.view and Textarea.view take the value and Messages, a style, and
@foldkit/ui's own config (type and placeholder, rows for a textarea):
import { Input } from 'foldkit-mixins-ui'
const emailField = (value: string, h: HtmlBuilder<Message>) =>
Input.view(
{
id: 'email',
value,
onInput: value => Message.TypedEmail({ value }),
type: 'email',
placeholder: 'you@example.com',
style: FieldStyle,
draw: ({ input, label }, h) => h.div([], [h.label(label, ['Email']), h.input(input)]),
},
h,
)Input.field and Textarea.field draw a whole field from a
FieldValidation.Field: the value, its first error or checking mark as the
description, and aria-invalid where the field is invalid. changed maps the
typed value to a Message; label is the text over the control; and draw
places the resolved label, control, and description bundles (the default
stacks them):
import { Input } from 'foldkit-mixins-ui'
const titleField = (field: FieldValidation.Field<string>, h: HtmlBuilder<Message>) =>
Input.field(
{ id: 'title', label: 'Title', field, changed: value => Message.TypedTitle({ value }) },
h,
)resolve is the seam underneath, for bundles already in hand: a view that
receives a Tabs render as its input, or a Calendar whose Mixins read the
mode it is showing, which is only known once the attributes arrive:
import type { CalendarAttributes } from '@foldkit/ui/calendar'
import { Calendar, CalendarSlots } from 'foldkit-mixins-ui'
const CalendarStyle = Style.forSlots(CalendarSlots)({
root: Style.whenInput<CalendarAttributes['_tag']>(mode => mode === 'Years', Style.class('years')),
})
const resolveCalendar = (attributes: CalendarAttributes, h: HtmlBuilder<Message>) =>
Calendar.resolve(attributes, [CalendarStyle.mixin], { input: attributes._tag, h })