Composition / foldkit-builder
The selection in the URL
A link that opens the editor on a Block is the Builder's selection mirrored
into the URL. The Builder owns the selection, and a selection does more than
set a field (the layers' focus moves, the rows above it open), so the URL
never writes it directly: a navigation becomes a Selected Message, and the
selection is read back out into the URL.
In, from the parent's own update, for a form key named document placed as
page:
const blockIn = (url: Url): Option.Option<NodeId> =>
Option.map(
Option.filter(
Option.fromNullOr(new URLSearchParams(Option.getOrElse(url.search, () => '')).get('block')),
id => id !== '',
),
NodeId.make,
)
const selectFrom = (url: Url) =>
Message.GotPageMessage({
message: PageForm.control('document').send(
Option.match(blockIn(url), {
onNone: () => BuilderMessage.Deselected(),
onSome: id => BuilderMessage.Selected({ id }),
}),
),
})
const update = placements.update((model, message) =>
message._tag === 'UrlChanged'
? {
model,
commands: [{ name: 'SelectFromUrl', effect: Effect.succeed(selectFrom(message.url)) }],
}
: { model },
)Out, as one of the parent's own Subscriptions, given to
placements.subscriptions(selectionUrl):
const selectionUrl = Subscription.make<Model, Message>()(entry => ({
selectionUrl: entry(
{ block: Schema.Option(Schema.String) },
{
modelToDependencies: model => ({
block: PageForm.control('document').field(model.page).value.selected,
}),
dependenciesToStream: ({ block }) =>
Option.match(block, {
onNone: () => Stream.empty,
onSome: id => Stream.fromEffect(Navigation.replaceUrl(`?block=${id}`)).pipe(Stream.drain),
}),
},
),
}))The Builder refuses an id its page lacks, so a link opened before the page is
loaded selects nothing: send selectFrom again once the form is filled.
Nothing selected leaves the URL as it is, so that link is not lost while the
page loads. An application with other query parameters builds the URL with
them. test/url.test.ts runs this recipe, and the CMS example's page editor
(examples/cms, at /pages) routes this way, with the page and the Block both in
the address.
Previewing a context
A page whose Catalog declares a context shows some nodes only under
conditions (Conditions). The Builder
keeps what the author previews the page as in preview, by context key,
starting from the preview it was made with:
Builder.make('PageBuilder', { catalog: Site, renderer, starters, preview: { audience: 'guest' } })The page is drawn in edit mode with preview as its context, so a node hidden
for it is still drawn, marked data-composition-hidden. Previewing changes
nothing in the page and is not an edit.