pdyform 2.1.0 → 2.2.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.
@@ -1,17 +1,37 @@
1
1
  import * as React from 'react';
2
2
  import React__default from 'react';
3
- import { FormSchema, FormField } from 'pdyform/core';
3
+ import { createFormStore, FormStore, FormRuntimeState, FormSchema, FormField } from 'pdyform-core';
4
4
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
5
5
  import * as class_variance_authority_types from 'class-variance-authority/types';
6
6
  import * as LabelPrimitive from '@radix-ui/react-label';
7
7
  import { VariantProps } from 'class-variance-authority';
8
8
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
9
9
  import * as SelectPrimitive from '@radix-ui/react-select';
10
+ import * as SwitchPrimitives from '@radix-ui/react-switch';
11
+
12
+ interface UseFormOptions {
13
+ schema: FormSchema;
14
+ }
15
+ interface UseFormReturn {
16
+ store: ReturnType<typeof createFormStore>;
17
+ state: FormStore;
18
+ setValue: (name: string, value: any) => Promise<void>;
19
+ getValue: (name: string) => any;
20
+ setError: (name: string, error: string) => void;
21
+ validate: () => Promise<{
22
+ hasError: boolean;
23
+ values: any;
24
+ state: FormRuntimeState;
25
+ }>;
26
+ reset: () => void;
27
+ }
28
+ declare function useForm({ schema }: UseFormOptions): UseFormReturn;
10
29
 
11
30
  interface DynamicFormProps {
12
31
  schema: FormSchema;
13
32
  onSubmit: (values: Record<string, any>) => void;
14
33
  className?: string;
34
+ form?: UseFormReturn;
15
35
  }
16
36
  declare const DynamicForm: React__default.FC<DynamicFormProps>;
17
37
 
@@ -25,6 +45,8 @@ declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.Re
25
45
 
26
46
  declare const Checkbox: React.ForwardRefExoticComponent<Omit<CheckboxPrimitive.CheckboxProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
27
47
 
48
+ declare const Switch: React.ForwardRefExoticComponent<Omit<SwitchPrimitives.SwitchProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
49
+
28
50
  declare const RadioGroup: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitive.RadioGroupProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
29
51
  declare const RadioGroupItem: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitive.RadioGroupItemProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
30
52
 
@@ -44,6 +66,11 @@ interface FieldRenderContext {
44
66
  onChange: (value: any) => void;
45
67
  onBlur?: () => void;
46
68
  fieldId: string;
69
+ errorId?: string;
70
+ descriptionId?: string;
71
+ ariaInvalid?: boolean;
72
+ ariaRequired?: boolean;
73
+ ariaDescribedBy?: string;
47
74
  }
48
75
  /** A React component that renders a specific field type */
49
76
  type FieldRenderer = React__default.ComponentType<FieldRenderContext>;
@@ -60,6 +87,10 @@ declare const CheckboxRenderer: React__default.FC<FieldRenderContext>;
60
87
 
61
88
  declare const RadioRenderer: React__default.FC<FieldRenderContext>;
62
89
 
90
+ declare const DateRenderer: React__default.FC<FieldRenderContext>;
91
+
92
+ declare const SwitchRenderer: React__default.FC<FieldRenderContext>;
93
+
63
94
  /**
64
95
  * The default built-in component map.
65
96
  * Import and spread this to extend or override individual field types:
@@ -93,4 +124,4 @@ interface FormFieldRendererProps {
93
124
  }
94
125
  declare const FormFieldRenderer: React__default.FC<FormFieldRendererProps>;
95
126
 
96
- export { Checkbox, CheckboxRenderer, DynamicForm, type FieldComponentMap, type FieldRenderContext, type FieldRenderer, FormFieldRenderer, type FormFieldRendererProps, Input, InputRenderer, Label, RadioGroup, RadioGroupItem, RadioRenderer, Select, SelectContent, SelectGroup, SelectItem, SelectRenderer, SelectTrigger, SelectValue, Textarea, TextareaRenderer, defaultComponentMap };
127
+ export { Checkbox, CheckboxRenderer, DateRenderer, DynamicForm, type FieldComponentMap, type FieldRenderContext, type FieldRenderer, FormFieldRenderer, type FormFieldRendererProps, Input, InputRenderer, Label, RadioGroup, RadioGroupItem, RadioRenderer, Select, SelectContent, SelectGroup, SelectItem, SelectRenderer, SelectTrigger, SelectValue, Switch, SwitchRenderer, Textarea, TextareaRenderer, type UseFormOptions, type UseFormReturn, defaultComponentMap, useForm };
@@ -1,411 +1 @@
1
- // src/DynamicForm.tsx
2
- import { useState } from "react";
3
- import {
4
- createFormRuntimeState,
5
- applyFieldChange,
6
- applyFieldBlur,
7
- runSubmitValidation,
8
- setSubmitting
9
- } from "pdyform/core";
10
-
11
- // src/components/Input.tsx
12
- import * as React from "react";
13
-
14
- // src/utils.ts
15
- import { clsx } from "clsx";
16
- import { twMerge } from "tailwind-merge";
17
- function cn(...inputs) {
18
- return twMerge(clsx(inputs));
19
- }
20
-
21
- // src/components/Input.tsx
22
- import { jsx } from "react/jsx-runtime";
23
- var Input = React.forwardRef(
24
- ({ className, type, ...props }, ref) => {
25
- return /* @__PURE__ */ jsx(
26
- "input",
27
- {
28
- type,
29
- className: cn(
30
- "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
31
- className
32
- ),
33
- ref,
34
- ...props
35
- }
36
- );
37
- }
38
- );
39
- Input.displayName = "Input";
40
-
41
- // src/components/Textarea.tsx
42
- import * as React2 from "react";
43
- import { jsx as jsx2 } from "react/jsx-runtime";
44
- var Textarea = React2.forwardRef(
45
- ({ className, ...props }, ref) => {
46
- return /* @__PURE__ */ jsx2(
47
- "textarea",
48
- {
49
- className: cn(
50
- "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
51
- className
52
- ),
53
- ref,
54
- ...props
55
- }
56
- );
57
- }
58
- );
59
- Textarea.displayName = "Textarea";
60
-
61
- // src/components/Checkbox.tsx
62
- import * as React3 from "react";
63
- import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
64
- import { Check } from "lucide-react";
65
- import { jsx as jsx3 } from "react/jsx-runtime";
66
- var Checkbox = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx3(
67
- CheckboxPrimitive.Root,
68
- {
69
- ref,
70
- className: cn(
71
- "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
72
- className
73
- ),
74
- ...props,
75
- children: /* @__PURE__ */ jsx3(
76
- CheckboxPrimitive.Indicator,
77
- {
78
- className: cn("flex items-center justify-center text-current"),
79
- children: /* @__PURE__ */ jsx3(Check, { className: "h-4 w-4" })
80
- }
81
- )
82
- }
83
- ));
84
- Checkbox.displayName = CheckboxPrimitive.Root.displayName;
85
-
86
- // src/components/RadioGroup.tsx
87
- import * as React4 from "react";
88
- import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
89
- import { Circle } from "lucide-react";
90
- import { jsx as jsx4 } from "react/jsx-runtime";
91
- var RadioGroup = React4.forwardRef(({ className, ...props }, ref) => {
92
- return /* @__PURE__ */ jsx4(
93
- RadioGroupPrimitive.Root,
94
- {
95
- className: cn("grid gap-2", className),
96
- ...props,
97
- ref
98
- }
99
- );
100
- });
101
- RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
102
- var RadioGroupItem = React4.forwardRef(({ className, ...props }, ref) => {
103
- return /* @__PURE__ */ jsx4(
104
- RadioGroupPrimitive.Item,
105
- {
106
- ref,
107
- className: cn(
108
- "aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
109
- className
110
- ),
111
- ...props,
112
- children: /* @__PURE__ */ jsx4(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx4(Circle, { className: "h-2.5 w-2.5 fill-current text-current" }) })
113
- }
114
- );
115
- });
116
- RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
117
-
118
- // src/components/Select.tsx
119
- import * as React5 from "react";
120
- import * as SelectPrimitive from "@radix-ui/react-select";
121
- import { Check as Check2, ChevronDown } from "lucide-react";
122
- import { jsx as jsx5, jsxs } from "react/jsx-runtime";
123
- var Select = SelectPrimitive.Root;
124
- var SelectGroup = SelectPrimitive.Group;
125
- var SelectValue = SelectPrimitive.Value;
126
- var SelectTrigger = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
127
- SelectPrimitive.Trigger,
128
- {
129
- ref,
130
- className: cn(
131
- "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
132
- className
133
- ),
134
- ...props,
135
- children: [
136
- children,
137
- /* @__PURE__ */ jsx5(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx5(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
138
- ]
139
- }
140
- ));
141
- SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
142
- var SelectContent = React5.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx5(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsx5(
143
- SelectPrimitive.Content,
144
- {
145
- ref,
146
- className: cn(
147
- "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
148
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
149
- className
150
- ),
151
- position,
152
- ...props,
153
- children: /* @__PURE__ */ jsx5(
154
- SelectPrimitive.Viewport,
155
- {
156
- className: cn(
157
- "p-1",
158
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
159
- ),
160
- children
161
- }
162
- )
163
- }
164
- ) }));
165
- SelectContent.displayName = SelectPrimitive.Content.displayName;
166
- var SelectItem = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
167
- SelectPrimitive.Item,
168
- {
169
- ref,
170
- className: cn(
171
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
172
- className
173
- ),
174
- ...props,
175
- children: [
176
- /* @__PURE__ */ jsx5("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx5(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx5(Check2, { className: "h-4 w-4" }) }) }),
177
- /* @__PURE__ */ jsx5(SelectPrimitive.ItemText, { children })
178
- ]
179
- }
180
- ));
181
- SelectItem.displayName = SelectPrimitive.Item.displayName;
182
-
183
- // src/components/Label.tsx
184
- import * as React6 from "react";
185
- import * as LabelPrimitive from "@radix-ui/react-label";
186
- import { cva } from "class-variance-authority";
187
- import { jsx as jsx6 } from "react/jsx-runtime";
188
- var labelVariants = cva(
189
- "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
190
- );
191
- var Label = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
192
- LabelPrimitive.Root,
193
- {
194
- ref,
195
- className: cn(labelVariants(), className),
196
- ...props
197
- }
198
- ));
199
- Label.displayName = LabelPrimitive.Root.displayName;
200
-
201
- // src/components/InputRenderer.tsx
202
- import { normalizeFieldValue } from "pdyform/core";
203
- import { jsx as jsx7 } from "react/jsx-runtime";
204
- var InputRenderer = ({ field, value, onChange, onBlur, fieldId }) => {
205
- const handleChange = (nextValue) => {
206
- onChange(normalizeFieldValue(field, nextValue));
207
- };
208
- return /* @__PURE__ */ jsx7(
209
- Input,
210
- {
211
- id: fieldId,
212
- type: field.type,
213
- placeholder: field.placeholder,
214
- value: value ?? "",
215
- onChange: (e) => handleChange(e.target.value),
216
- onBlur,
217
- disabled: field.disabled,
218
- name: field.name
219
- }
220
- );
221
- };
222
- var InputRenderer_default = InputRenderer;
223
-
224
- // src/components/TextareaRenderer.tsx
225
- import { jsx as jsx8 } from "react/jsx-runtime";
226
- var TextareaRenderer = ({ field, value, onChange, onBlur, fieldId }) => /* @__PURE__ */ jsx8(
227
- Textarea,
228
- {
229
- id: fieldId,
230
- placeholder: field.placeholder,
231
- value: value ?? "",
232
- onChange: (e) => onChange(e.target.value),
233
- onBlur,
234
- disabled: field.disabled,
235
- name: field.name
236
- }
237
- );
238
- var TextareaRenderer_default = TextareaRenderer;
239
-
240
- // src/components/SelectRenderer.tsx
241
- import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
242
- var SelectRenderer = ({ field, value, onChange, onBlur, fieldId }) => /* @__PURE__ */ jsxs2(
243
- Select,
244
- {
245
- value: value != null ? String(value) : "",
246
- onValueChange: onChange,
247
- disabled: field.disabled,
248
- name: field.name,
249
- children: [
250
- /* @__PURE__ */ jsx9(SelectTrigger, { id: fieldId, onBlur, children: /* @__PURE__ */ jsx9(SelectValue, { placeholder: field.placeholder || "Select an option" }) }),
251
- /* @__PURE__ */ jsx9(SelectContent, { children: /* @__PURE__ */ jsx9(SelectGroup, { children: field.options?.map((opt) => /* @__PURE__ */ jsx9(SelectItem, { value: String(opt.value), children: opt.label }, opt.value)) }) })
252
- ]
253
- }
254
- );
255
- var SelectRenderer_default = SelectRenderer;
256
-
257
- // src/components/CheckboxRenderer.tsx
258
- import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
259
- var CheckboxRenderer = ({ field, value, onChange, onBlur }) => /* @__PURE__ */ jsx10("div", { className: "flex flex-wrap gap-4", children: field.options?.map((opt) => {
260
- const checked = Array.isArray(value) && value.includes(opt.value);
261
- return /* @__PURE__ */ jsxs3("div", { className: "flex items-center space-x-2", children: [
262
- /* @__PURE__ */ jsx10(
263
- Checkbox,
264
- {
265
- id: `checkbox-${field.name}-${opt.value}`,
266
- checked,
267
- disabled: field.disabled,
268
- onCheckedChange: (c) => {
269
- const next = Array.isArray(value) ? [...value] : [];
270
- if (c) {
271
- next.push(opt.value);
272
- } else {
273
- const idx = next.indexOf(opt.value);
274
- if (idx > -1) next.splice(idx, 1);
275
- }
276
- onChange(next);
277
- },
278
- onBlur
279
- }
280
- ),
281
- /* @__PURE__ */ jsx10(Label, { htmlFor: `checkbox-${field.name}-${opt.value}`, className: "font-normal", children: opt.label })
282
- ] }, opt.value);
283
- }) });
284
- var CheckboxRenderer_default = CheckboxRenderer;
285
-
286
- // src/components/RadioRenderer.tsx
287
- import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
288
- var RadioRenderer = ({ field, value, onChange, onBlur }) => /* @__PURE__ */ jsx11(
289
- RadioGroup,
290
- {
291
- value: value != null ? String(value) : "",
292
- onValueChange: onChange,
293
- disabled: field.disabled,
294
- name: field.name,
295
- className: "flex flex-wrap gap-4",
296
- children: field.options?.map((opt) => /* @__PURE__ */ jsxs4("div", { className: "flex items-center space-x-2", children: [
297
- /* @__PURE__ */ jsx11(
298
- RadioGroupItem,
299
- {
300
- value: String(opt.value),
301
- id: `radio-${field.name}-${opt.value}`,
302
- onBlur
303
- }
304
- ),
305
- /* @__PURE__ */ jsx11(Label, { htmlFor: `radio-${field.name}-${opt.value}`, className: "font-normal", children: opt.label })
306
- ] }, opt.value))
307
- }
308
- );
309
- var RadioRenderer_default = RadioRenderer;
310
-
311
- // src/components/index.ts
312
- var defaultComponentMap = {
313
- text: InputRenderer_default,
314
- number: InputRenderer_default,
315
- password: InputRenderer_default,
316
- email: InputRenderer_default,
317
- date: InputRenderer_default,
318
- textarea: TextareaRenderer_default,
319
- select: SelectRenderer_default,
320
- checkbox: CheckboxRenderer_default,
321
- radio: RadioRenderer_default
322
- };
323
-
324
- // src/FormFieldRenderer.tsx
325
- import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
326
- var FormFieldRenderer = ({
327
- field,
328
- value,
329
- onChange,
330
- onBlur,
331
- error,
332
- componentMap
333
- }) => {
334
- const { label, description, name, type } = field;
335
- const fieldId = `field-${name}`;
336
- const resolvedMap = componentMap ? { ...defaultComponentMap, ...componentMap } : defaultComponentMap;
337
- const FieldComponent = resolvedMap[type] ?? InputRenderer_default;
338
- return /* @__PURE__ */ jsxs5("div", { className: `space-y-2 ${field.className || ""}`, children: [
339
- label && /* @__PURE__ */ jsx12(Label, { htmlFor: fieldId, children: label }),
340
- /* @__PURE__ */ jsx12(FieldComponent, { field, value, onChange, onBlur, fieldId }),
341
- description && /* @__PURE__ */ jsx12("p", { className: "text-[0.8rem] text-muted-foreground", children: description }),
342
- error && /* @__PURE__ */ jsx12("p", { className: "text-[0.8rem] font-medium text-destructive", children: error })
343
- ] });
344
- };
345
-
346
- // src/DynamicForm.tsx
347
- import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
348
- var DynamicForm = ({ schema, onSubmit, className }) => {
349
- const [formState, setFormState] = useState(() => createFormRuntimeState(schema.fields));
350
- const handleFieldChange = (name, value) => {
351
- setFormState((prev) => applyFieldChange(schema.fields, prev, name, value));
352
- };
353
- const handleFieldBlur = (name) => {
354
- setFormState((prev) => applyFieldBlur(schema.fields, prev, name));
355
- };
356
- const handleSubmit = (e) => {
357
- e.preventDefault();
358
- const submittingState = setSubmitting(formState, true);
359
- const { state: validatedState, hasError } = runSubmitValidation(schema.fields, submittingState);
360
- setFormState(validatedState);
361
- if (!hasError) {
362
- onSubmit(validatedState.values);
363
- }
364
- };
365
- return /* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className: `space-y-6 ${className || ""}`, children: [
366
- schema.title && /* @__PURE__ */ jsx13("h2", { className: "text-2xl font-bold tracking-tight", children: schema.title }),
367
- schema.description && /* @__PURE__ */ jsx13("p", { className: "text-muted-foreground", children: schema.description }),
368
- /* @__PURE__ */ jsx13("div", { className: "space-y-4", children: schema.fields.map((field) => !field.hidden && /* @__PURE__ */ jsx13(
369
- FormFieldRenderer,
370
- {
371
- field,
372
- value: formState.values[field.name],
373
- onChange: (val) => handleFieldChange(field.name, val),
374
- onBlur: () => handleFieldBlur(field.name),
375
- error: formState.errors[field.name]
376
- },
377
- field.name
378
- )) }),
379
- /* @__PURE__ */ jsx13(
380
- "button",
381
- {
382
- type: "submit",
383
- disabled: formState.isSubmitting,
384
- className: "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2 w-full",
385
- children: formState.isSubmitting ? "Submitting..." : schema.submitButtonText || "Submit"
386
- }
387
- )
388
- ] });
389
- };
390
- export {
391
- Checkbox,
392
- CheckboxRenderer_default as CheckboxRenderer,
393
- DynamicForm,
394
- FormFieldRenderer,
395
- Input,
396
- InputRenderer_default as InputRenderer,
397
- Label,
398
- RadioGroup,
399
- RadioGroupItem,
400
- RadioRenderer_default as RadioRenderer,
401
- Select,
402
- SelectContent,
403
- SelectGroup,
404
- SelectItem,
405
- SelectRenderer_default as SelectRenderer,
406
- SelectTrigger,
407
- SelectValue,
408
- Textarea,
409
- TextareaRenderer_default as TextareaRenderer,
410
- defaultComponentMap
411
- };
1
+ import{get as Ue}from"pdyform-core";import*as ee from"react";import{clsx as ue}from"clsx";import{twMerge as be}from"tailwind-merge";function l(...e){return be(ue(e))}import{jsx as Re}from"react/jsx-runtime";var h=ee.forwardRef(({className:e,type:t,...r},i)=>Re("input",{type:t,className:l("flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",e),ref:i,...r}));h.displayName="Input";import*as te from"react";import{jsx as ve}from"react/jsx-runtime";var I=te.forwardRef(({className:e,...t},r)=>ve("textarea",{className:l("flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",e),ref:r,...t}));I.displayName="Textarea";import*as re from"react";import*as C from"@radix-ui/react-checkbox";import{Check as ge}from"lucide-react";import{jsx as A}from"react/jsx-runtime";var k=re.forwardRef(({className:e,...t},r)=>A(C.Root,{ref:r,className:l("peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",e),...t,children:A(C.Indicator,{className:l("flex items-center justify-center text-current"),children:A(ge,{className:"h-4 w-4"})})}));k.displayName=C.Root.displayName;import*as ie from"react";import*as F from"@radix-ui/react-switch";import{jsx as oe}from"react/jsx-runtime";var T=ie.forwardRef(({className:e,...t},r)=>oe(F.Root,{className:l("peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",e),...t,ref:r,children:oe(F.Thumb,{className:l("pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0")})}));T.displayName=F.Root.displayName;import*as W from"react";import*as R from"@radix-ui/react-radio-group";import{Circle as xe}from"lucide-react";import{jsx as V}from"react/jsx-runtime";var G=W.forwardRef(({className:e,...t},r)=>V(R.Root,{className:l("grid gap-2",e),...t,ref:r}));G.displayName=R.Root.displayName;var E=W.forwardRef(({className:e,...t},r)=>V(R.Item,{ref:r,className:l("aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",e),...t,children:V(R.Indicator,{className:"flex items-center justify-center",children:V(xe,{className:"h-2.5 w-2.5 fill-current text-current"})})}));E.displayName=R.Item.displayName;import*as L from"react";import*as a from"@radix-ui/react-select";import{Check as ye,ChevronDown as he}from"lucide-react";import{jsx as f,jsxs as ae}from"react/jsx-runtime";var q=a.Root,H=a.Group,U=a.Value,M=L.forwardRef(({className:e,children:t,...r},i)=>ae(a.Trigger,{ref:i,className:l("flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",e),...r,children:[t,f(a.Icon,{asChild:!0,children:f(he,{className:"h-4 w-4 opacity-50"})})]}));M.displayName=a.Trigger.displayName;var B=L.forwardRef(({className:e,children:t,position:r="popper",...i},o)=>f(a.Portal,{children:f(a.Content,{ref:o,className:l("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",r==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",e),position:r,...i,children:f(a.Viewport,{className:l("p-1",r==="popper"&&"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"),children:t})})}));B.displayName=a.Content.displayName;var $=L.forwardRef(({className:e,children:t,...r},i)=>ae(a.Item,{ref:i,className:l("relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",e),...r,children:[f("span",{className:"absolute left-2 flex h-3.5 w-3.5 items-center justify-center",children:f(a.ItemIndicator,{children:f(ye,{className:"h-4 w-4"})})}),f(a.ItemText,{children:t})]}));$.displayName=a.Item.displayName;import*as ne from"react";import*as z from"@radix-ui/react-label";import{cva as Se}from"class-variance-authority";import{jsx as Fe}from"react/jsx-runtime";var Ce=Se("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"),v=ne.forwardRef(({className:e,...t},r)=>Fe(z.Root,{ref:r,className:l(Ce(),e),...t}));v.displayName=z.Root.displayName;import{normalizeFieldValue as Pe}from"pdyform-core";import{jsx as Ne}from"react/jsx-runtime";var we=({field:e,value:t,onChange:r,onBlur:i,fieldId:o,ariaInvalid:n,ariaRequired:s,ariaDescribedBy:d})=>{let c=p=>{r(Pe(e,p))};return Ne(h,{id:o,type:e.type,placeholder:e.placeholder,value:t??"",onChange:p=>c(p.target.value),onBlur:i,disabled:typeof e.disabled=="boolean"?e.disabled:void 0,name:e.name,"aria-invalid":n,"aria-required":s,"aria-describedby":d})},g=we;import{jsx as ke}from"react/jsx-runtime";var Ie=({field:e,value:t,onChange:r,onBlur:i,fieldId:o})=>ke(I,{id:o,placeholder:e.placeholder,value:t??"",onChange:n=>r(n.target.value),onBlur:i,disabled:typeof e.disabled=="boolean"?e.disabled:void 0,name:e.name}),O=Ie;import{jsx as P,jsxs as Ve}from"react/jsx-runtime";var Te=({field:e,value:t,onChange:r,onBlur:i,fieldId:o})=>Ve(q,{value:t!=null?String(t):"",onValueChange:r,disabled:typeof e.disabled=="boolean"?e.disabled:void 0,name:e.name,children:[P(M,{id:o,onBlur:i,children:P(U,{placeholder:e.placeholder||"Select an option"})}),P(B,{children:P(H,{children:e.options?.map(n=>P($,{value:String(n.value),children:n.label},n.value))})})]}),J=Te;import{jsx as K,jsxs as Ee}from"react/jsx-runtime";var Ge=({field:e,value:t,onChange:r,onBlur:i})=>K("div",{className:"flex flex-wrap gap-4",children:e.options?.map(o=>{let n=Array.isArray(t)&&t.includes(o.value);return Ee("div",{className:"flex items-center space-x-2",children:[K(k,{id:`checkbox-${e.name}-${o.value}`,checked:n,disabled:typeof e.disabled=="boolean"?e.disabled:void 0,onCheckedChange:s=>{let d=Array.isArray(t)?[...t]:[];if(s)d.push(o.value);else{let c=d.indexOf(o.value);c>-1&&d.splice(c,1)}r(d)},onBlur:i}),K(v,{htmlFor:`checkbox-${e.name}-${o.value}`,className:"font-normal",children:o.label})]},o.value)})}),Q=Ge;import{jsx as X,jsxs as Me}from"react/jsx-runtime";var Le=({field:e,value:t,onChange:r,onBlur:i})=>X(G,{value:t!=null?String(t):"",onValueChange:r,disabled:typeof e.disabled=="boolean"?e.disabled:void 0,name:e.name,className:"flex flex-wrap gap-4",children:e.options?.map(o=>Me("div",{className:"flex items-center space-x-2",children:[X(E,{value:String(o.value),id:`radio-${e.name}-${o.value}`,onBlur:i}),X(v,{htmlFor:`radio-${e.name}-${o.value}`,className:"font-normal",children:o.label})]},o.value))}),Y=Le;import{jsx as $e}from"react/jsx-runtime";var Be=({field:e,value:t,onChange:r,onBlur:i,fieldId:o,ariaInvalid:n,ariaRequired:s,ariaDescribedBy:d})=>$e(h,{id:o,type:"date",value:t??"",onChange:c=>r(c.target.value),onBlur:i,disabled:typeof e.disabled=="boolean"?e.disabled:void 0,name:e.name,"aria-invalid":n,"aria-required":s,"aria-describedby":d}),Z=Be;import{jsx as se}from"react/jsx-runtime";var De=({field:e,value:t,onChange:r,onBlur:i,fieldId:o,ariaInvalid:n,ariaRequired:s,ariaDescribedBy:d})=>se("div",{className:"flex items-center space-x-2",children:se(T,{id:o,checked:!!t,onCheckedChange:r,disabled:typeof e.disabled=="boolean"?e.disabled:void 0,name:e.name,"aria-invalid":n,"aria-required":s,"aria-describedby":d})}),_=De;var j={text:g,number:g,password:g,email:g,textarea:O,select:J,checkbox:Q,radio:Y,date:Z,switch:_};import{jsx as D,jsxs as le}from"react/jsx-runtime";var de=({field:e,value:t,onChange:r,onBlur:i,error:o,componentMap:n})=>{let{label:s,description:d,name:c,type:p,validations:m}=e,u=`field-${c}`,x=`${u}-description`,y=`${u}-error`,b=m?.some(fe=>fe.type==="required"),ce=(n?{...j,...n}:j)[p]??g,pe=[d?x:null,o?y:null].filter(Boolean).join(" ");return le("div",{className:`space-y-2 ${e.className||""}`,children:[s&&le(v,{htmlFor:u,className:b?"flex items-center gap-1":"",children:[s,b&&D("span",{className:"text-destructive",children:"*"})]}),D(ce,{field:e,value:t,onChange:r,onBlur:i,fieldId:u,errorId:y,descriptionId:x,ariaInvalid:!!o,ariaRequired:b,ariaDescribedBy:pe||void 0}),d&&D("p",{id:x,className:"text-[0.8rem] text-muted-foreground",children:d}),o&&D("p",{id:y,className:"text-[0.8rem] font-medium text-destructive",children:o})]})};import{useMemo as Ae,useCallback as w}from"react";import{useStore as We}from"zustand";import{createFormStore as qe,get as He}from"pdyform-core";function me({schema:e}){let t=Ae(()=>qe(e.fields,e.resolver,e.errorMessages),[e]),r=We(t),i=w(async(c,p)=>{await t.getState().setFieldValue(c,p)},[t]),o=w(c=>He(t.getState().values,c),[t]),n=w((c,p)=>{t.setState(m=>({errors:{...m.errors,[c]:p}}))},[t]),s=w(async()=>{let{hasError:c,state:p}=await t.getState().runSubmitValidation();return{hasError:c,values:p.values,state:p}},[t]),d=w(()=>{t.setState({values:{},errors:{},isSubmitting:!1})},[t]);return{store:t,state:r,setValue:i,getValue:o,setError:n,validate:s,reset:d}}import{jsx as N,jsxs as ze}from"react/jsx-runtime";var Ir=({schema:e,onSubmit:t,className:r,form:i})=>{let o=me({schema:e}),n=i||o,{state:s,store:d}=n,c=async m=>{m.preventDefault();let{hasError:u,values:x,state:y}=await n.validate();if(u){let b=e.fields.find(S=>y.errors[S.name]);if(b){let S=document.getElementById(`field-${b.name}`);S?.focus(),S?.scrollIntoView({behavior:"smooth",block:"center"})}return}t(x)},p=s.validatingFields.length>0;return ze("form",{onSubmit:c,className:`space-y-6 ${r||""}`,children:[e.title&&N("h2",{className:"text-2xl font-bold tracking-tight",children:e.title}),e.description&&N("p",{className:"text-muted-foreground",children:e.description}),N("div",{className:"space-y-4",children:e.fields.map(m=>{let u=typeof m.hidden=="function"?m.hidden(s.values):m.hidden,x=typeof m.disabled=="function"?m.disabled(s.values):m.disabled,y=s.validatingFields.includes(m.name);return!u&&N(de,{field:{...m,disabled:x||y},value:Ue(s.values,m.name),onChange:b=>d.getState().setFieldValue(m.name,b),onBlur:()=>d.getState().setFieldBlur(m.name),error:s.errors[m.name]},m.name)})}),N("button",{type:"submit",disabled:s.isSubmitting||p,className:"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2 w-full",children:s.isSubmitting?"Submitting...":p?"Validating...":e.submitButtonText||"Submit"})]})};export{k as Checkbox,Q as CheckboxRenderer,Z as DateRenderer,Ir as DynamicForm,de as FormFieldRenderer,h as Input,g as InputRenderer,v as Label,G as RadioGroup,E as RadioGroupItem,Y as RadioRenderer,q as Select,B as SelectContent,H as SelectGroup,$ as SelectItem,J as SelectRenderer,M as SelectTrigger,U as SelectValue,T as Switch,_ as SwitchRenderer,I as Textarea,O as TextareaRenderer,j as defaultComponentMap,me as useForm};
@@ -2,13 +2,18 @@ import { Component } from 'vue';
2
2
  import { ComponentOptionsMixin } from 'vue';
3
3
  import { ComponentProvideOptions } from 'vue';
4
4
  import { DefineComponent } from 'vue';
5
- import { FormField } from 'pdyform/core';
6
- import { FormSchema } from 'pdyform/core';
5
+ import { FormField } from 'pdyform-core';
6
+ import { FormRuntimeState } from 'pdyform-core';
7
+ import { FormSchema } from 'pdyform-core';
8
+ import { FormStore } from 'pdyform-core';
7
9
  import { PublicProps } from 'vue';
10
+ import { Ref } from 'vue';
11
+ import { StoreApi } from 'zustand/vanilla';
8
12
 
9
13
  declare type __VLS_Props = {
10
14
  schema: FormSchema;
11
15
  className?: string;
16
+ form?: UseFormReturn;
12
17
  };
13
18
 
14
19
  declare type __VLS_Props_2 = {
@@ -33,7 +38,6 @@ declare type __VLS_Props_2 = {
33
38
  componentMap?: FieldComponentMap;
34
39
  };
35
40
 
36
- /** The default built-in component map */
37
41
  export declare const defaultComponentMap: FieldComponentMap;
38
42
 
39
43
  export declare const DynamicForm: DefineComponent<__VLS_Props, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
@@ -42,23 +46,65 @@ submit: (...args: any[]) => void;
42
46
  onSubmit?: ((...args: any[]) => any) | undefined;
43
47
  }>, {}, {}, {}, {}, string, ComponentProvideOptions, false, {}, HTMLFormElement>;
44
48
 
45
- /** Map from field type to a Vue component */
46
49
  export declare type FieldComponentMap = Record<string, Component>;
47
50
 
48
- /**
49
- * The props interface that every field renderer component must accept.
50
- * Custom field renderers must implement this interface.
51
- */
52
51
  export declare interface FieldRendererProps {
53
52
  field: FormField;
54
- modelValue: any;
55
53
  fieldId: string;
54
+ modelValue: any;
55
+ ariaInvalid?: boolean;
56
+ ariaRequired?: boolean;
57
+ ariaDescribedby?: string;
56
58
  }
57
59
 
58
60
  export declare const FormFieldRenderer: DefineComponent<__VLS_Props_2, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
59
61
  "update:modelValue": (...args: any[]) => void;
62
+ blur: (...args: any[]) => void;
60
63
  }, string, PublicProps, Readonly<__VLS_Props_2> & Readonly<{
61
64
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
65
+ onBlur?: ((...args: any[]) => any) | undefined;
62
66
  }>, {}, {}, {}, {}, string, ComponentProvideOptions, false, {}, HTMLDivElement>;
63
67
 
68
+ export declare function useForm({ schema }: UseFormOptions): {
69
+ store: StoreApi<FormStore>;
70
+ state: Ref< {
71
+ setFieldValue: (name: string, rawValue: unknown) => Promise<void>;
72
+ setFieldBlur: (name: string) => Promise<void>;
73
+ setSubmitting: (isSubmitting: boolean) => void;
74
+ runSubmitValidation: () => Promise<{
75
+ state: FormRuntimeState;
76
+ hasError: boolean;
77
+ }>;
78
+ values: Record<string, any>;
79
+ errors: Record<string, string>;
80
+ validatingFields: string[];
81
+ isSubmitting: boolean;
82
+ }, FormStore | {
83
+ setFieldValue: (name: string, rawValue: unknown) => Promise<void>;
84
+ setFieldBlur: (name: string) => Promise<void>;
85
+ setSubmitting: (isSubmitting: boolean) => void;
86
+ runSubmitValidation: () => Promise<{
87
+ state: FormRuntimeState;
88
+ hasError: boolean;
89
+ }>;
90
+ values: Record<string, any>;
91
+ errors: Record<string, string>;
92
+ validatingFields: string[];
93
+ isSubmitting: boolean;
94
+ }>;
95
+ setValue: (name: string, value: any) => Promise<void>;
96
+ getValue: (name: string) => any;
97
+ validate: () => Promise<{
98
+ hasError: boolean;
99
+ values: Record<string, any>;
100
+ }>;
101
+ reset: () => void;
102
+ };
103
+
104
+ export declare interface UseFormOptions {
105
+ schema: FormSchema;
106
+ }
107
+
108
+ export declare type UseFormReturn = ReturnType<typeof useForm>;
109
+
64
110
  export { }