@reformer/ui 1.0.0-beta.1

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.
@@ -0,0 +1,12 @@
1
+ export { FormNavigation } from './FormNavigation';
2
+ export { FormNavigationStep } from './FormNavigationStep';
3
+ export { FormNavigationIndicator } from './FormNavigationIndicator';
4
+ export { FormNavigationActions } from './FormNavigationActions';
5
+ export { FormNavigationProgress } from './FormNavigationProgress';
6
+ export { useFormNavigation, FormNavigationContext } from './FormNavigationContext';
7
+ export type { FormNavigationHandle, FormNavigationProps, FormNavigationConfig } from './types';
8
+ export type { FormNavigationStepProps } from './FormNavigationStep';
9
+ export type { FormNavigationContextValue } from './FormNavigationContext';
10
+ export type { FormNavigationIndicatorProps, FormNavigationIndicatorStep, FormNavigationIndicatorStepWithState, FormNavigationIndicatorRenderProps, } from './FormNavigationIndicator';
11
+ export type { FormNavigationActionsProps, FormNavigationActionsRenderProps, FormNavigationButtonProps, FormNavigationSubmitProps, } from './FormNavigationActions';
12
+ export type { FormNavigationProgressProps, FormNavigationProgressRenderProps, } from './FormNavigationProgress';
@@ -0,0 +1,52 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { GroupNodeWithControls, ValidationSchemaFn } from '@reformer/core';
3
+ /**
4
+ * Configuration for multi-step form navigation
5
+ * Note: totalSteps is inferred from children count
6
+ */
7
+ export interface FormNavigationConfig<T extends Record<string, any>> {
8
+ /** Validation schemas per step (1-based indexing) */
9
+ stepValidations: Record<number, ValidationSchemaFn<T>>;
10
+ /** Full validation schema for submit */
11
+ fullValidation: ValidationSchemaFn<T>;
12
+ }
13
+ /**
14
+ * Handle for external access to FormNavigation methods via ref
15
+ */
16
+ export interface FormNavigationHandle<T extends Record<string, any>> {
17
+ /** Current step (1-based) */
18
+ currentStep: number;
19
+ /** Completed steps */
20
+ completedSteps: number[];
21
+ /** Validate current step */
22
+ validateCurrentStep: () => Promise<boolean>;
23
+ /** Go to next step (with validation) */
24
+ goToNextStep: () => Promise<boolean>;
25
+ /** Go to previous step */
26
+ goToPreviousStep: () => void;
27
+ /** Go to specific step */
28
+ goToStep: (step: number) => boolean;
29
+ /** Submit form (with full validation) */
30
+ submit: <R>(onSubmit: (values: T) => Promise<R> | R) => Promise<R | null>;
31
+ /** Is this the first step */
32
+ isFirstStep: boolean;
33
+ /** Is this the last step */
34
+ isLastStep: boolean;
35
+ /** Is validation in progress */
36
+ isValidating: boolean;
37
+ }
38
+ /**
39
+ * Props for FormNavigation component
40
+ */
41
+ export interface FormNavigationProps<T extends Record<string, any>> {
42
+ /** Form instance */
43
+ form: GroupNodeWithControls<T>;
44
+ /** Step configuration (validation schemas) */
45
+ config: FormNavigationConfig<T>;
46
+ /** Children (Step components, Indicator, Actions, Progress, or any ReactNode) */
47
+ children: ReactNode;
48
+ /** Callback when step changes */
49
+ onStepChange?: (step: number) => void;
50
+ /** Scroll to top on step change */
51
+ scrollToTop?: boolean;
52
+ }
@@ -0,0 +1,2 @@
1
+ export * from './form-navigation/index'
2
+ export {}
@@ -0,0 +1,10 @@
1
+ import { F as i, c as t, e as r, b as s, d as n, a as g, u as m } from "./FormNavigation-3rg4tJyl.js";
2
+ export {
3
+ i as FormNavigation,
4
+ t as FormNavigationActions,
5
+ r as FormNavigationContext,
6
+ s as FormNavigationIndicator,
7
+ n as FormNavigationProgress,
8
+ g as FormNavigationStep,
9
+ m as useFormNavigation
10
+ };
@@ -0,0 +1,4 @@
1
+ export { FormArray, FormArrayList, FormArrayAddButton, FormArrayRemoveButton, FormArrayEmpty, FormArrayCount, FormArrayItemIndex, useFormArray, FormArrayContext, FormArrayItemContext, useFormArrayContext, useFormArrayItemContext, } from './form-array';
2
+ export type { FormArrayHandle, FormArrayItem, UseFormArrayReturn, FormArrayRootProps, FormArrayListProps, FormArrayItemRenderProps, FormArrayAddButtonProps, FormArrayRemoveButtonProps, FormArrayEmptyProps, FormArrayCountProps, FormArrayItemIndexProps, FormArrayContextValue, FormArrayItemContextValue, } from './form-array';
3
+ export { FormNavigation, FormNavigationStep, FormNavigationIndicator, FormNavigationActions, FormNavigationProgress, useFormNavigation, FormNavigationContext, } from './form-navigation';
4
+ export type { FormNavigationHandle, FormNavigationProps, FormNavigationConfig, FormNavigationStepProps, FormNavigationContextValue, FormNavigationIndicatorProps, FormNavigationIndicatorStep, FormNavigationIndicatorStepWithState, FormNavigationIndicatorRenderProps, FormNavigationActionsProps, FormNavigationActionsRenderProps, FormNavigationButtonProps, FormNavigationSubmitProps, FormNavigationProgressProps, FormNavigationProgressRenderProps, } from './form-navigation';
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ import { F as o, b as t, g as s, e as m, d as e, h as F, f as i, a as n, c as A, u as y, i as g, j as u } from "./FormArray-K2YVvq1u.js";
2
+ import { F as x, c as N, e as d, b as C, d as I, a as c, u as p } from "./FormNavigation-3rg4tJyl.js";
3
+ export {
4
+ o as FormArray,
5
+ t as FormArrayAddButton,
6
+ s as FormArrayContext,
7
+ m as FormArrayCount,
8
+ e as FormArrayEmpty,
9
+ F as FormArrayItemContext,
10
+ i as FormArrayItemIndex,
11
+ n as FormArrayList,
12
+ A as FormArrayRemoveButton,
13
+ x as FormNavigation,
14
+ N as FormNavigationActions,
15
+ d as FormNavigationContext,
16
+ C as FormNavigationIndicator,
17
+ I as FormNavigationProgress,
18
+ c as FormNavigationStep,
19
+ y as useFormArray,
20
+ g as useFormArrayContext,
21
+ u as useFormArrayItemContext,
22
+ p as useFormNavigation
23
+ };
package/llms.txt ADDED
@@ -0,0 +1,343 @@
1
+ # @reformer/ui - LLM Integration Guide
2
+
3
+ Headless UI components for @reformer/core forms.
4
+
5
+ # Overview
6
+
7
+ `@reformer/ui` provides headless UI components for `@reformer/core` forms.
8
+
9
+ ## Key Concepts
10
+
11
+ - **Headless**: No default UI or styles - you build the interface
12
+ - **Compound Components**: Composable, declarative API
13
+ - **Render Props**: Children as function for full control
14
+ - **Context-based**: State shared via React Context
15
+
16
+ ## Components
17
+
18
+ | Component | Purpose |
19
+ |-----------|---------|
20
+ | `FormArray` | Manage dynamic form arrays |
21
+ | `FormNavigation` | Multi-step form wizard |
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install @reformer/ui @reformer/core
27
+ ```
28
+
29
+ ## Import Patterns
30
+
31
+ ```typescript
32
+ // All components
33
+ import { FormArray, FormNavigation } from '@reformer/ui';
34
+
35
+ // Tree-shaking (recommended)
36
+ import { FormArray, useFormArray } from '@reformer/ui/form-array';
37
+ import { FormNavigation, useFormNavigation } from '@reformer/ui/form-navigation';
38
+ ```
39
+
40
+ # FormArray
41
+
42
+ Headless compound component for managing form arrays.
43
+
44
+ ## Basic Usage
45
+
46
+ ```tsx
47
+ import { FormArray } from '@reformer/ui/form-array';
48
+
49
+ <FormArray.Root control={form.items}>
50
+ <FormArray.Empty>
51
+ <p>No items added</p>
52
+ </FormArray.Empty>
53
+
54
+ <FormArray.List>
55
+ {({ control, index, remove }) => (
56
+ <div key={control.id}>
57
+ <h4>Item #{index + 1}</h4>
58
+ <ItemForm control={control} />
59
+ <button onClick={remove}>Remove</button>
60
+ </div>
61
+ )}
62
+ </FormArray.List>
63
+
64
+ <FormArray.AddButton>Add Item</FormArray.AddButton>
65
+ </FormArray.Root>
66
+ ```
67
+
68
+ ## Sub-components
69
+
70
+ | Component | Props | Purpose |
71
+ |-----------|-------|---------|
72
+ | `FormArray.Root` | `control: ArrayNode<T>` | Context provider |
73
+ | `FormArray.List` | `children: (item) => ReactNode` | Iterates items (render props) |
74
+ | `FormArray.AddButton` | `initialValue?: Partial<T>` | Adds new item |
75
+ | `FormArray.RemoveButton` | - | Removes current item (inside List) |
76
+ | `FormArray.Empty` | `children: ReactNode` | Shows when array is empty |
77
+ | `FormArray.Count` | `render?: (count) => ReactNode` | Displays item count |
78
+ | `FormArray.ItemIndex` | `render?: (index) => ReactNode` | Displays current index |
79
+
80
+ ## List Render Props
81
+
82
+ ```typescript
83
+ interface FormArrayItemRenderProps<T> {
84
+ control: GroupNodeWithControls<T>; // Form control for item
85
+ index: number; // Zero-based index
86
+ id: string | number; // Unique key
87
+ remove: () => void; // Remove this item
88
+ }
89
+ ```
90
+
91
+ ## External Control via Ref
92
+
93
+ ```tsx
94
+ import { useRef } from 'react';
95
+ import { FormArray, FormArrayHandle } from '@reformer/ui/form-array';
96
+
97
+ const arrayRef = useRef<FormArrayHandle<ItemType>>(null);
98
+
99
+ // Control from outside
100
+ arrayRef.current?.add({ name: 'New' });
101
+ arrayRef.current?.removeAt(0);
102
+ arrayRef.current?.clear();
103
+
104
+ <FormArray.Root ref={arrayRef} control={form.items}>
105
+ ...
106
+ </FormArray.Root>
107
+ ```
108
+
109
+ ## FormArrayHandle API
110
+
111
+ ```typescript
112
+ interface FormArrayHandle<T> {
113
+ add: (value?: Partial<T>) => void;
114
+ clear: () => void;
115
+ insert: (index: number, value?: Partial<T>) => void;
116
+ removeAt: (index: number) => void;
117
+ length: number;
118
+ isEmpty: boolean;
119
+ at: (index: number) => GroupNodeWithControls<T> | undefined;
120
+ }
121
+ ```
122
+
123
+ ## useFormArray Hook
124
+
125
+ For full customization without compound components:
126
+
127
+ ```tsx
128
+ import { useFormArray } from '@reformer/ui/form-array';
129
+
130
+ function CustomList() {
131
+ const { items, add, isEmpty, length } = useFormArray(form.items);
132
+
133
+ return (
134
+ <div>
135
+ <span>Total: {length}</span>
136
+ {items.map(({ control, id, remove }) => (
137
+ <div key={id}>
138
+ <ItemForm control={control} />
139
+ <button onClick={remove}>X</button>
140
+ </div>
141
+ ))}
142
+ {isEmpty && <p>Empty</p>}
143
+ <button onClick={() => add()}>Add</button>
144
+ </div>
145
+ );
146
+ }
147
+ ```
148
+
149
+ # FormNavigation
150
+
151
+ Headless compound component for multi-step form wizards.
152
+
153
+ ## Basic Usage
154
+
155
+ ```tsx
156
+ import { FormNavigation } from '@reformer/ui/form-navigation';
157
+
158
+ const config = {
159
+ stepValidations: {
160
+ 1: step1Schema,
161
+ 2: step2Schema,
162
+ },
163
+ fullValidation: fullFormSchema,
164
+ };
165
+
166
+ <FormNavigation form={form} config={config}>
167
+ <FormNavigation.Step component={Step1Form} control={form} />
168
+ <FormNavigation.Step component={Step2Form} control={form} />
169
+
170
+ <FormNavigation.Actions onSubmit={handleSubmit}>
171
+ {({ prev, next, submit, isFirstStep, isLastStep }) => (
172
+ <div>
173
+ {!isFirstStep && <button {...prev}>Back</button>}
174
+ {!isLastStep ? (
175
+ <button {...next}>Next</button>
176
+ ) : (
177
+ <button {...submit}>Submit</button>
178
+ )}
179
+ </div>
180
+ )}
181
+ </FormNavigation.Actions>
182
+ </FormNavigation>
183
+ ```
184
+
185
+ ## Sub-components
186
+
187
+ | Component | Purpose |
188
+ |-----------|---------|
189
+ | `FormNavigation` | Root provider |
190
+ | `FormNavigation.Step` | Renders component when step is current |
191
+ | `FormNavigation.Indicator` | Headless step indicator (render props) |
192
+ | `FormNavigation.Actions` | Headless navigation buttons (render props) |
193
+ | `FormNavigation.Progress` | Headless progress display (render props) |
194
+
195
+ ## FormNavigation.Indicator
196
+
197
+ ```tsx
198
+ <FormNavigation.Indicator steps={STEPS}>
199
+ {({ steps, goToStep, currentStep }) => (
200
+ <nav>
201
+ {steps.map((step) => (
202
+ <button
203
+ key={step.number}
204
+ onClick={() => goToStep(step.number)}
205
+ disabled={!step.canNavigate}
206
+ aria-current={step.isCurrent ? 'step' : undefined}
207
+ >
208
+ {step.isCompleted ? '✓' : step.number} {step.title}
209
+ </button>
210
+ ))}
211
+ </nav>
212
+ )}
213
+ </FormNavigation.Indicator>
214
+ ```
215
+
216
+ ### Step Definition
217
+
218
+ ```typescript
219
+ interface FormNavigationIndicatorStep {
220
+ number: number; // 1-based step number
221
+ title: string;
222
+ icon?: string;
223
+ }
224
+ ```
225
+
226
+ ### Render Props
227
+
228
+ ```typescript
229
+ interface FormNavigationIndicatorRenderProps {
230
+ steps: FormNavigationIndicatorStepWithState[];
231
+ goToStep: (step: number) => boolean;
232
+ currentStep: number;
233
+ totalSteps: number;
234
+ completedSteps: number[];
235
+ }
236
+
237
+ interface FormNavigationIndicatorStepWithState {
238
+ number: number;
239
+ title: string;
240
+ icon?: string;
241
+ isCurrent: boolean;
242
+ isCompleted: boolean;
243
+ canNavigate: boolean;
244
+ }
245
+ ```
246
+
247
+ ## FormNavigation.Actions
248
+
249
+ ```tsx
250
+ <FormNavigation.Actions onSubmit={handleSubmit}>
251
+ {({ prev, next, submit, isFirstStep, isLastStep, isValidating }) => (
252
+ <div>
253
+ {!isFirstStep && (
254
+ <button onClick={prev.onClick} disabled={prev.disabled}>
255
+ Back
256
+ </button>
257
+ )}
258
+ {!isLastStep ? (
259
+ <button onClick={next.onClick} disabled={next.disabled}>
260
+ {isValidating ? 'Validating...' : 'Next'}
261
+ </button>
262
+ ) : (
263
+ <button onClick={submit.onClick} disabled={submit.disabled}>
264
+ {submit.isSubmitting ? 'Submitting...' : 'Submit'}
265
+ </button>
266
+ )}
267
+ </div>
268
+ )}
269
+ </FormNavigation.Actions>
270
+ ```
271
+
272
+ ### Render Props
273
+
274
+ ```typescript
275
+ interface FormNavigationActionsRenderProps {
276
+ prev: { onClick: () => void; disabled: boolean };
277
+ next: { onClick: () => void; disabled: boolean };
278
+ submit: { onClick: () => void; disabled: boolean; isSubmitting: boolean };
279
+ isFirstStep: boolean;
280
+ isLastStep: boolean;
281
+ isValidating: boolean;
282
+ isSubmitting: boolean;
283
+ }
284
+ ```
285
+
286
+ ## FormNavigation.Progress
287
+
288
+ ```tsx
289
+ <FormNavigation.Progress>
290
+ {({ current, total, percent }) => (
291
+ <div>
292
+ Step {current} of {total} ({percent}%)
293
+ <div style={{ width: `${percent}%` }} />
294
+ </div>
295
+ )}
296
+ </FormNavigation.Progress>
297
+ ```
298
+
299
+ ### Render Props
300
+
301
+ ```typescript
302
+ interface FormNavigationProgressRenderProps {
303
+ current: number;
304
+ total: number;
305
+ percent: number;
306
+ completedCount: number;
307
+ isFirstStep: boolean;
308
+ isLastStep: boolean;
309
+ }
310
+ ```
311
+
312
+ ## External Control via Ref
313
+
314
+ ```tsx
315
+ const navRef = useRef<FormNavigationHandle<FormType>>(null);
316
+
317
+ // Programmatic navigation
318
+ navRef.current?.goToStep(2);
319
+ navRef.current?.goToNextStep();
320
+ navRef.current?.goToPreviousStep();
321
+
322
+ // Submit with validation
323
+ const result = await navRef.current?.submit(async (values) => {
324
+ return api.submit(values);
325
+ });
326
+
327
+ <FormNavigation ref={navRef} form={form} config={config}>
328
+ ...
329
+ </FormNavigation>
330
+ ```
331
+
332
+ ## Configuration
333
+
334
+ ```typescript
335
+ interface FormNavigationConfig<T> {
336
+ stepValidations: Record<number, ValidationSchemaFn<T>>;
337
+ fullValidation: ValidationSchemaFn<T>;
338
+ }
339
+ ```
340
+
341
+ Validation happens automatically:
342
+ - On `next.onClick`: validates current step
343
+ - On `submit.onClick`: validates entire form
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@reformer/ui",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "Headless UI components for @reformer/core - form arrays, multi-step wizards, and more",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./form-array": {
15
+ "types": "./dist/form-array.d.ts",
16
+ "import": "./dist/form-array.js"
17
+ },
18
+ "./form-navigation": {
19
+ "types": "./dist/form-navigation.d.ts",
20
+ "import": "./dist/form-navigation.js"
21
+ }
22
+ },
23
+ "sideEffects": false,
24
+ "scripts": {
25
+ "generate:llms": "npx tsx scripts/generate-llms.ts",
26
+ "build": "npm run generate:llms && vite build && tsc -p tsconfig.json",
27
+ "dev": "vite",
28
+ "test": "vitest",
29
+ "test:watch": "vitest watch"
30
+ },
31
+ "keywords": [
32
+ "react",
33
+ "forms",
34
+ "headless-ui",
35
+ "compound-components",
36
+ "form-array",
37
+ "multi-step-form",
38
+ "wizard",
39
+ "reformer",
40
+ "typescript"
41
+ ],
42
+ "engines": {
43
+ "node": ">=18.0.0"
44
+ },
45
+ "author": "Alexandr Bukhtatyy",
46
+ "license": "MIT",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://github.com/AlexandrBukhtatyy/ReFormer.git",
50
+ "directory": "packages/reformer-ui"
51
+ },
52
+ "homepage": "https://alexandrbukhtatyy.github.io/ReFormer/",
53
+ "bugs": {
54
+ "url": "https://github.com/AlexandrBukhtatyy/ReFormer/issues"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
59
+ "files": [
60
+ "dist",
61
+ "README.md",
62
+ "LICENSE",
63
+ "llms.txt"
64
+ ],
65
+ "peerDependencies": {
66
+ "@reformer/core": "^1.0.0",
67
+ "react": "^18.0.0 || ^19.0.0",
68
+ "react-dom": "^18.0.0 || ^19.0.0"
69
+ },
70
+ "devDependencies": {
71
+ "@reformer/core": "*",
72
+ "@types/node": "^24.10.1",
73
+ "@types/react": "^19.2.7",
74
+ "@types/react-dom": "^19.2.3",
75
+ "@vitejs/plugin-react": "^5.1.0",
76
+ "@vitest/utils": "^4.0.8",
77
+ "react": "^19.2.1",
78
+ "react-dom": "^19.2.1",
79
+ "tsx": "^4.19.2",
80
+ "typescript": "^5.9.3",
81
+ "vite": "^7.2.2",
82
+ "vite-plugin-dts": "^4.5.4",
83
+ "vitest": "^4.0.8"
84
+ }
85
+ }