foldkit@0.154.0
Minor Changes
-
186020f: Add composable
OnClickcontrols for preventing the browser default, stopping DOM propagation, and synchronously focusing an existing element before dispatch. The existing one-argument call keeps its allow-and-bubble behavior, whileOnClickFocusremains source compatible and is deprecated in favor of the new focus control. Scene now follows the full click propagation path, honors the default-action and propagation controls, and runs submit-button default actions. -
a36b809: Add
OnFocusEnterandOnFocusLeaveattributes for modeling focus across a compound region. Put them on a common ancestor and Foldkit dispatches only when focus crosses that ancestor's boundary, not when it moves between descendants. The newScene.focusEnterandScene.focusLeaveinteractions exercise the same Messages in scene tests. -
e13c3a0:
Update.foldChildandUpdate.foldChildStepcan now emit a derived parent OutMessage fromfoldOutMessage. Type the fold asUpdate.StepWithOutMessagewhen handling the child fact may also produce a different fact from the parent.Imagine this code lives inside a settings page module with a reusable
SelectSubmodel. Choosing "Dark" makes the Select emitSelect.OutMessage.Selected. The settings page owns the theme Model, so its localchangeThemeStep applies the selection:// settings/main.ts const changeTheme = (theme: Theme): Update.StepWithOutMessage<Model, Message, OutMessage> => model => ({ model: evo(model, { theme: () => theme }), commands: [SaveThemePreference({ theme })], outMessage: OutMessage.ChangedTheme({ theme }), })
changeThemeevolves the settings Model, returns the Command that saves the preference, and reports the change to the settings page's parent.Before,
foldOutMessagecould only return a plainUpdate.Step, so it could not callchangeTheme. The fold had to leave the selection for the parent Message handler:Before:
// settings/main.ts const foldThemeSelectOutMessage = M.type<Select.OutMessage<Theme>>().pipe( M.withReturnType<Update.Step<Model, Message>>(), M.tagsExhaustive({ Selected: () => model => ({ model }), }), )
The
GotThemeSelectMessagebranch then had to run the child fold, inspect the child Message again, callchangeTheme, combine both Commands collections, and preserve the optional parent OutMessage:// settings/main.ts GotThemeSelectMessage: ({ message }) => { const themeSelectFold = foldThemeSelect(model, message) return Select.Message.match< Update.ReturnWithOutMessage<Model, Message, OutMessage> >(message, { SelectedOption: ({ option }) => { const themeChange = changeTheme(option)(themeSelectFold.model) return { ...themeChange, commands: [ ...(themeSelectFold.commands ?? []), ...(themeChange.commands ?? []), ], } }, }) },
After:
// settings/main.ts const foldThemeSelectOutMessage = M.type<Select.OutMessage<Theme>>().pipe( M.withReturnType<Update.StepWithOutMessage<Model, Message, OutMessage>>(), M.tagsExhaustive({ Selected: ({ value: theme }) => changeTheme(theme), }), )
Set
foldOutMessagetofoldThemeSelectOutMessagein the existingUpdate.foldChildconfig. TheGotThemeSelectMessagebranch only routes the child Message now:// settings/main.ts const foldThemeSelect = Update.foldChild({ update: Select.update, read: model => Option.some(model.themeSelect), write: (model, nextThemeSelect) => evo(model, { themeSelect: () => nextThemeSelect }), toParentMessage: message => Message.GotThemeSelectMessage({ message }), foldOutMessage: foldThemeSelectOutMessage, }) GotThemeSelectMessage: ({ message }) => foldThemeSelect(model, message),
The Step returned by
changeThemenow runs insidefoldThemeSelect.Update.foldChildpreserves the Select Commands and returns the settings page's next Model, save Command, and derivedChangedThemeOutMessage together.Keep
toParentOutMessagefor one-to-one forwarding of a child fact. No adapter is needed when every parent OutMessage is derived byfoldOutMessage. When both paths emit, the derived OutMessage replaces the lift for that dispatch. If the Step emits nothing, the lift still runs.
Patch Changes
- 6716de6: Clarify the public TSDoc for update returns and child folds. The revised guidance explains which child OutMessages continue to the parent, which stop at the current Submodel, and when
foldOutMessagestill runs locally.