@playfast/reform-forms-react 0.0.10 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,7 +46,7 @@ contract `props`. `field(path).as(Component, props?)` returns a `ReactNode` whos
46
46
  Full API: [playbook/api.doc.md](./playbook/api.doc.md). Pairs with core
47
47
  [`@playfast/reform`](https://www.npmjs.com/package/@playfast/reform), form state
48
48
  [`@playfast/forms`](https://www.npmjs.com/package/@playfast/forms), and the React host
49
- [`@playfast/react`](https://www.npmjs.com/package/@playfast/react).
49
+ [`@playfast/reform-react`](https://www.npmjs.com/package/@playfast/reform-react).
50
50
 
51
51
  ## License
52
52
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@playfast/reform-forms-react",
3
3
  "playbook": "./playbook",
4
- "version": "0.0.10",
4
+ "version": "0.1.0",
5
5
  "type": "module",
6
6
  "description": "React bindings for @playfast/forms — render a typed reform form View with scoped field/array/variant helpers.",
7
7
  "keywords": [
@@ -32,8 +32,8 @@ export const ok: ViewImpl<CheckoutContract> = FormUi.make(CheckoutUi, CheckoutFo
32
32
  <>
33
33
  {field('email').as(TextInput)}
34
34
  {field('age').as(NumberInput)}
35
- {array('items', ({ items }) => (
36
- <>{items.map((item) => item.field('name').as(TextInput))}</>
35
+ {array('items', ({ items: rows }) => (
36
+ <>{rows.map((row) => row.field('name').as(TextInput))}</>
37
37
  ))}
38
38
  {variant('payment', {
39
39
  Card: ({ field }) => field('cardNumber').as(TextInput),
package/src/index.tsx CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createElement, type ComponentType, type ReactNode } from 'react'
2
+ import { Function as Fn } from 'effect'
2
3
  import { Ui, type UiClass, type UiContract, type ViewImpl } from '@playfast/reform'
3
4
  import type {
4
5
  ArrayItem,
@@ -67,7 +68,10 @@ const makeFieldHandle = <A,>(
67
68
  field: FieldBinding<A>,
68
69
  ): FieldHandle<A> => ({
69
70
  as: <P extends object>(component: FieldComponent<A, P>, props?: P): ReactNode =>
70
- createElement(component, { ...(props ?? {}), field } as { readonly field: FieldBinding<A> } & P),
71
+ createElement(
72
+ component,
73
+ Fn.unsafeCoerce<object, { readonly field: FieldBinding<A> } & P>({ ...props, field }),
74
+ ),
71
75
  })
72
76
 
73
77
  const makeScope = <ScopeValues,>(
@@ -76,30 +80,35 @@ const makeScope = <ScopeValues,>(
76
80
  ): ScopeHelpers<ScopeValues> => {
77
81
  const full = (path: string): string => joinPath(prefix, path)
78
82
  return {
79
- field: (path) => makeFieldHandle(form.field(full(path as string))),
83
+ field: (path) => makeFieldHandle(form.field(full(String(path)))),
80
84
  array: (path, render) => {
81
- const binding = form.array(full(path as string))
85
+ const binding = form.array(full(String(path)))
82
86
  return render({
83
87
  append: binding.append,
84
88
  remove: binding.remove,
85
89
  move: binding.move,
86
90
  swap: binding.swap,
87
- items: binding.items.map((item) => ({
88
- ...makeScope(form, `${binding.path}[${item.index}]`),
89
- key: item.key,
90
- index: item.index,
91
- value: item.value as ArrayItem<ScopeValues, typeof path>,
92
- remove: item.remove,
93
- move: item.move,
94
- })) as ReadonlyArray<ArrayItemScope<ArrayItem<ScopeValues, typeof path>>>,
91
+ items: Fn.unsafeCoerce(
92
+ binding.items.map((element) => ({
93
+ ...makeScope(form, `${binding.path}[${element.index}]`),
94
+ key: element.key,
95
+ index: element.index,
96
+ value: element.value,
97
+ remove: element.remove,
98
+ move: element.move,
99
+ })),
100
+ ),
95
101
  })
96
102
  },
97
103
  variant: (path, cases) => {
98
- const variantPath = full(path as string)
99
- const value = form.variantValue(variantPath)
100
- if (value === null || typeof value !== 'object' || !('_tag' in value)) return null
101
- const tag = String((value as { readonly _tag: string })._tag)
102
- const render = (cases as Record<string, (scope: ScopeHelpers<unknown>) => ReactNode>)[tag]
104
+ const variantPath = full(String(path))
105
+ const current = form.variantValue(variantPath)
106
+ if (current === null || typeof current !== 'object' || !('_tag' in current)) {
107
+ return null
108
+ }
109
+ const tag = String(current._tag)
110
+ const handlers = Fn.unsafeCoerce<typeof cases, Record<string, (scope: ScopeHelpers<unknown>) => ReactNode>>(cases)
111
+ const render = handlers[tag]
103
112
  return render === undefined ? null : render(makeScope(form, variantPath))
104
113
  },
105
114
  }