@codeleap/form 6.3.0 → 6.8.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.
Files changed (69) hide show
  1. package/dist/fields/bool.d.ts +17 -0
  2. package/dist/fields/bool.d.ts.map +1 -0
  3. package/dist/fields/date.d.ts +22 -0
  4. package/dist/fields/date.d.ts.map +1 -0
  5. package/dist/fields/file.d.ts +17 -0
  6. package/dist/fields/file.d.ts.map +1 -0
  7. package/dist/fields/group.d.ts +17 -0
  8. package/dist/fields/group.d.ts.map +1 -0
  9. package/dist/fields/index.d.ts +87 -0
  10. package/dist/fields/index.d.ts.map +1 -0
  11. package/dist/fields/list.d.ts +34 -0
  12. package/dist/fields/list.d.ts.map +1 -0
  13. package/dist/fields/number.d.ts +23 -0
  14. package/dist/fields/number.d.ts.map +1 -0
  15. package/dist/fields/selectable.d.ts +25 -0
  16. package/dist/fields/selectable.d.ts.map +1 -0
  17. package/dist/fields/text.d.ts +21 -0
  18. package/dist/fields/text.d.ts.map +1 -0
  19. package/dist/hooks/index.d.ts +3 -0
  20. package/dist/hooks/index.d.ts.map +1 -0
  21. package/dist/hooks/useField.d.ts +16 -0
  22. package/dist/hooks/useField.d.ts.map +1 -0
  23. package/dist/hooks/useValidate.d.ts +23 -0
  24. package/dist/hooks/useValidate.d.ts.map +1 -0
  25. package/dist/index.d.ts +6 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/lib/Field.d.ts +193 -0
  28. package/dist/lib/Field.d.ts.map +1 -0
  29. package/dist/lib/Form.d.ts +88 -0
  30. package/dist/lib/Form.d.ts.map +1 -0
  31. package/dist/lib/factories.d.ts +14 -0
  32. package/dist/lib/factories.d.ts.map +1 -0
  33. package/dist/lib/index.d.ts +3 -0
  34. package/dist/lib/index.d.ts.map +1 -0
  35. package/dist/lib/useFieldBinding.d.ts +14 -0
  36. package/dist/lib/useFieldBinding.d.ts.map +1 -0
  37. package/dist/types/field.d.ts +22 -0
  38. package/dist/types/field.d.ts.map +1 -0
  39. package/dist/types/form.d.ts +26 -0
  40. package/dist/types/form.d.ts.map +1 -0
  41. package/dist/types/globals.d.ts +15 -0
  42. package/dist/types/globals.d.ts.map +1 -0
  43. package/dist/types/index.d.ts +5 -0
  44. package/dist/types/index.d.ts.map +1 -0
  45. package/dist/types/validation.d.ts +9 -0
  46. package/dist/types/validation.d.ts.map +1 -0
  47. package/dist/validators/index.d.ts +2 -0
  48. package/dist/validators/index.d.ts.map +1 -0
  49. package/dist/validators/zod.d.ts +22 -0
  50. package/dist/validators/zod.d.ts.map +1 -0
  51. package/package.json +26 -10
  52. package/src/fields/bool.ts +6 -0
  53. package/src/fields/date.ts +9 -0
  54. package/src/fields/file.ts +7 -0
  55. package/src/fields/group.ts +6 -0
  56. package/src/fields/index.ts +17 -2
  57. package/src/fields/list.ts +66 -17
  58. package/src/fields/number.ts +9 -0
  59. package/src/fields/selectable.ts +9 -0
  60. package/src/fields/text.ts +8 -1
  61. package/src/hooks/useField.ts +13 -0
  62. package/src/hooks/useValidate.ts +16 -0
  63. package/src/lib/Field.ts +166 -22
  64. package/src/lib/Form.ts +98 -13
  65. package/src/lib/factories.tsx +8 -0
  66. package/src/lib/useFieldBinding.ts +13 -2
  67. package/src/types/globals.ts +6 -2
  68. package/src/validators/zod.ts +16 -1
  69. package/package.json.bak +0 -30
@@ -0,0 +1,88 @@
1
+ import { GlobalState } from "@codeleap/store";
2
+ import { FieldPaths, FieldTuples, FormDef, FormValues, PropertyForKeys, ValidationResult } from "../types";
3
+ type FormSelector<T extends FormDef, S> = (form: Form<T>) => S;
4
+ /**
5
+ * Container that owns the shared nanostores `GlobalState` atom for a group of
6
+ * fields and wires each field's individual atom to a slice of that shared
7
+ * state. This ensures a single source of truth: reading `form.values` always
8
+ * reflects the live atom values of every field.
9
+ *
10
+ * **Initialization order**
11
+ * The constructor calls `attachState()` synchronously, which replaces each
12
+ * field's own atom with a derived slice and sets the field's `name` from its
13
+ * object key in the shape. Fields must therefore be fully constructed before
14
+ * being passed to `Form`.
15
+ *
16
+ * **React usage**
17
+ * - `use(selector)` — mounts the form in a component, subscribes to state
18
+ * changes via `selector`, and resets all field values on unmount. Prefer
19
+ * this for full-page forms.
20
+ * - `useShared(selector)` — subscribes without the unmount-reset side-effect.
21
+ * Use when the form outlives the component (e.g. a global singleton).
22
+ *
23
+ * **Validation**
24
+ * `validate()` runs every field's synchronous validator and returns a map of
25
+ * results keyed by field name. Pass `revealErrors: true` to simultaneously
26
+ * flip `errorRevealed` on every field, triggering error display in the UI.
27
+ */
28
+ declare class Form<T extends FormDef> {
29
+ id: string;
30
+ fields: T;
31
+ state: GlobalState<FormValues<T>>;
32
+ constructor(id: string, shape: T);
33
+ get values(): FormValues<T> extends infer T_1 ? T_1 extends FormValues<T> ? T_1 extends undefined ? FormValues<T> : T_1 : never : never;
34
+ get isChanged(): boolean;
35
+ changed(): boolean;
36
+ get isValid(): boolean;
37
+ slice<K extends FieldPaths<T>>(field: K): import("nanostores").WritableAtom<unknown>;
38
+ iterFields<V>(cb: (field: FieldTuples<T>, index: number) => V): V[];
39
+ /**
40
+ * Bulk-sets field values from a partial record. Boolean fields require an
41
+ * explicit `boolean` value; all other fields skip `undefined` and falsy
42
+ * values. Use `resetValues()` to restore all fields to their initial values.
43
+ */
44
+ setValues(values: Partial<FormValues<T>>): void;
45
+ resetValues(): void;
46
+ attachState(): void;
47
+ /**
48
+ * Returns the first field (in definition order) that fails validation, along
49
+ * with its `ValidationResult`. Returns `undefined` when all fields are
50
+ * valid. Useful for scrolling to the first error on submit.
51
+ */
52
+ firstInvalid(): {
53
+ field: import("./Field").Field<any, any, unknown, unknown>;
54
+ validation: ValidationResult<unknown, unknown>;
55
+ } | undefined;
56
+ validate<Fields extends FieldPaths<T>[] = FieldPaths<T>[]>(options?: {
57
+ fields?: Fields;
58
+ revealErrors?: boolean;
59
+ }): PropertyForKeys<T, Fields[number], '__validationRes'>;
60
+ /**
61
+ * Returns the transformed props for `field` via `Field.props()`. Throws if
62
+ * the field key does not exist in this form's shape, making typos a runtime
63
+ * error rather than a silent no-op.
64
+ */
65
+ register(field: FieldPaths<T>): import("..").IFieldProps;
66
+ use<Selected>(selector: FormSelector<T, Selected>): Selected;
67
+ useReset(): void;
68
+ useShared<Selected>(selector: FormSelector<T, Selected>): Selected;
69
+ }
70
+ /**
71
+ * Creates a `Form` instance that is stable for the lifetime of the component
72
+ * (memoised on `name`). Use this when the form is local to a single mounted
73
+ * component. For forms that must survive unmounts (e.g. multi-step flows),
74
+ * construct the `Form` outside React via `form()`.
75
+ *
76
+ * Note: `def` is only read on the first render — changing it after mount has
77
+ * no effect.
78
+ */
79
+ export declare function useForm<T extends FormDef>(name: string, def: T): Form<T>;
80
+ /**
81
+ * Constructs a `Form` outside of React — suitable for module-level singletons,
82
+ * server-side construction, or multi-step flows where the form must outlive
83
+ * any individual component. Values are **not** automatically reset on unmount;
84
+ * call `resetValues()` explicitly when needed.
85
+ */
86
+ export declare function form<Def extends FormDef>(...args: ConstructorParameters<typeof Form<Def>>): Form<Def>;
87
+ export {};
88
+ //# sourceMappingURL=Form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Form.d.ts","sourceRoot":"","sources":["../../src/lib/Form.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,WAAW,EAAe,MAAM,iBAAiB,CAAA;AAG5E,OAAO,EAAE,UAAU,EAAuB,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAiB/H,KAAK,YAAY,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,KAAM,CAAC,CAAA;AAI/D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,cAAM,IAAI,CAAC,CAAC,SAAS,OAAO;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,CAAC,CAAA;IACT,KAAK,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;gBAGrB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAchC,IAAI,MAAM,8HAET;IAED,IAAI,SAAS,YAEZ;IAGD,OAAO;IAQP,IAAI,OAAO,YAIV;IAED,KAAK,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;IAgBvC,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC;IAe7D;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAQxC,WAAW;IAMX,WAAW;IAUX;;;;OAIG;IACH,YAAY;;;;IAWZ,QAAQ,CAAC,MAAM,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,eAAe,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;IA2BxK;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;IAO7B,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,QAAQ;IAQ5D,QAAQ;IAKR,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,QAAQ;CAiBnE;AAGD;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,WAO9D;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,GAAG,SAAS,OAAO,EAAE,GAAG,IAAI,EAAE,qBAAqB,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,aAEzF"}
@@ -0,0 +1,14 @@
1
+ import { Validator } from "../types";
2
+ import { Field } from "./Field";
3
+ type FieldBuilder<T, Validate extends Validator<any, any, any>> = typeof Field<T, Validate>;
4
+ /**
5
+ * Wraps a `Field` subclass constructor in a factory function, eliminating the
6
+ * need to call `new` at the call site and improving inference of the
7
+ * `validate` option's generic type. The returned factory accepts the same
8
+ * options as the class constructor and produces a fully typed `Field` instance.
9
+ *
10
+ * All entries in the `fields` namespace are built with this helper.
11
+ */
12
+ export declare function fieldFactory<T extends FieldBuilder<any, any>, Value = T extends FieldBuilder<infer V, any> ? V : never, Validator = T extends FieldBuilder<infer VL, any> ? VL : never>(cls: T): <A extends ConstructorParameters<T>[0]>(options?: A) => Field<Value, A["validate"]>;
13
+ export {};
14
+ //# sourceMappingURL=factories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factories.d.ts","sourceRoot":"","sources":["../../src/lib/factories.tsx"],"names":[],"mappings":"AACA,OAAO,EAAW,SAAS,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/B,KAAK,YAAY,CAAC,CAAC,EAAE,QAAQ,SAAS,SAAS,CAAC,GAAG,EAAC,GAAG,EAAC,GAAG,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;AAEzF;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,CAAC,SAAS,YAAY,CAAC,GAAG,EAAC,GAAG,CAAC,EAC/B,KAAK,GAAG,CAAC,SAAS,YAAY,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,EACxD,SAAS,GAAG,CAAC,SAAS,YAAY,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,KAAK,EAC9D,GAAG,EAAE,CAAC,IACE,CAAC,SAAS,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,KAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAKzF"}
@@ -0,0 +1,3 @@
1
+ export * from './Form';
2
+ export { Field } from './Field';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AACtB,OAAO,EAAC,KAAK,EAAC,MAAM,SAAS,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { IFieldRef } from "../types";
2
+ /**
3
+ * Attaches an imperative handle to `ref` via `useImperativeHandle`. Every
4
+ * method in {@link IFieldRef} is given a default implementation that throws
5
+ * `"ref.<method> not implemented"`, so callers get a clear error rather than a
6
+ * silent no-op when a method is missing from `impl`.
7
+ *
8
+ * `impl` is a partial override — only supply the methods your component
9
+ * actually supports. Unimplemented methods remain as throwing stubs.
10
+ * `deps` is forwarded directly to `useImperativeHandle`; include anything
11
+ * `impl` closes over that can change between renders.
12
+ */
13
+ export declare function useFieldBinding<T>(ref: React.Ref<IFieldRef<T> | null> | undefined, impl: Partial<IFieldRef<T>>, deps?: React.DependencyList): void;
14
+ //# sourceMappingURL=useFieldBinding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFieldBinding.d.ts","sourceRoot":"","sources":["../../src/lib/useFieldBinding.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEpC;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,GAAE,KAAK,CAAC,cAAmB,QAoC/I"}
@@ -0,0 +1,22 @@
1
+ import { WritableStore } from 'nanostores';
2
+ import { Validator } from './validation';
3
+ export type FieldState<T> = WritableStore<T>;
4
+ export interface ExtraFieldOptions {
5
+ }
6
+ export type FieldOptions<T, Validate extends Validator<T, any, any>> = {
7
+ name?: string;
8
+ defaultValue?: T | null;
9
+ state?: FieldState<T>;
10
+ validate?: Validate;
11
+ loader?: (form: any) => Partial<Omit<FieldOptions<T, Validate>, 'loader'>>;
12
+ onValueChange?: (newValue: T) => void;
13
+ } & ExtraFieldOptions;
14
+ export type FieldMeasureResult = {
15
+ x?: number;
16
+ y?: number;
17
+ width?: number;
18
+ height?: number;
19
+ pageX?: number;
20
+ pageY?: number;
21
+ };
22
+ //# sourceMappingURL=field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../src/types/field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAG,aAAa,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAIxC,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAA;AAE5C,MAAM,WAAW,iBAAiB;CAEjC;AAED,MAAM,MAAM,YAAY,CACtB,CAAC,EACD,QAAQ,SAAS,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IACrC;IACF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,CAAC,GAAG,IAAI,CAAA;IACvB,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;IAErB,QAAQ,CAAC,EAAE,QAAQ,CAAA;IAEnB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAC7B,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,QAAQ,CAAE,EAAE,QAAQ,CAAC,CAC3C,CAAA;IAED,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAA;CAEtC,GAAG,iBAAiB,CAAA;AAGrB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA"}
@@ -0,0 +1,26 @@
1
+ import { Field } from "../lib/Field";
2
+ import { ValidationResult } from "./validation";
3
+ export type FormDef = Record<string, Field<any, any>>;
4
+ export type NarrowKeyof<T> = Extract<keyof T, string>;
5
+ export type FormValues<T extends FormDef> = {
6
+ [K in NarrowKeyof<T>]: T[K]['value'];
7
+ };
8
+ export type FormErrors<T extends FormDef> = {
9
+ [K in NarrowKeyof<T>]: ReturnType<T[K]['validate']> extends ValidationResult<any, infer E> ? E : never;
10
+ };
11
+ export type FormResults<T extends FormDef> = {
12
+ [K in NarrowKeyof<T>]: ReturnType<T[K]['validate']> extends ValidationResult<infer R, any> ? R : never;
13
+ };
14
+ export type FieldTuples<T extends FormDef> = {
15
+ [K in NarrowKeyof<T>]: [K, T[K]];
16
+ }[NarrowKeyof<T>];
17
+ export type PropertyForKeys<T extends FormDef, Keys extends FieldPaths<T>, Property extends keyof Field<any, any>> = {
18
+ [K in Keys]: T[K][Property];
19
+ };
20
+ export type FieldPropertyTuples<T extends FormDef, Property extends keyof Field<any, any>> = {
21
+ [K in NarrowKeyof<T>]: [K, T[K][Property]];
22
+ }[NarrowKeyof<T>];
23
+ export type FieldPaths<T extends FormDef> = ({
24
+ [K in NarrowKeyof<T>]: K;
25
+ })[NarrowKeyof<T>];
26
+ //# sourceMappingURL=form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../src/types/form.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAG,KAAK,CAAC,GAAG,EAAC,GAAG,CAAC,CAAC,CAAA;AAErD,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;AAErD,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,OAAO,IAAI;KACzC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;CACtC,CAAA;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,OAAO,IAAI;KACzC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,SAAS,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK;CACxG,CAAA;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI;KAC1C,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,SAAS,gBAAgB,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK;CACxG,CAAA;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,IAAI;KAC1C,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;CACjC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;AAEjB,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,SAAS,UAAU,CAAC,CAAC,CAAC,EAAG,QAAQ,SAAS,MAAM,KAAK,CAAC,GAAG,EAAC,GAAG,CAAC,IAAI;KAClH,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;CAC5B,CAAA;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,OAAO,EAAE,QAAQ,SAAS,MAAM,KAAK,CAAC,GAAG,EAAC,GAAG,CAAC,IAAI;KACzF,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;CAC3C,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;AAIjB,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,OAAO,IAAI,CAAC;KAC1C,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC;CACzB,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA"}
@@ -0,0 +1,15 @@
1
+ import { AnyRecord } from '@codeleap/types';
2
+ export interface IFieldRef<T> {
3
+ getValue(): T;
4
+ scrollIntoView(): Promise<void>;
5
+ focus(): void;
6
+ blur(): void;
7
+ revealValue(): void;
8
+ toggleValueVisibility(): void;
9
+ hideValue(): void;
10
+ emit(event: string, ...args: any[]): void;
11
+ }
12
+ export interface IFieldProps {
13
+ }
14
+ export type PropTransformer = (props: AnyRecord) => AnyRecord;
15
+ //# sourceMappingURL=globals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"globals.d.ts","sourceRoot":"","sources":["../../src/types/globals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,QAAQ,IAAI,CAAC,CAAA;IACb,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/B,KAAK,IAAI,IAAI,CAAA;IACb,IAAI,IAAI,IAAI,CAAA;IACZ,WAAW,IAAI,IAAI,CAAA;IACnB,qBAAqB,IAAI,IAAI,CAAA;IAC7B,SAAS,IAAI,IAAI,CAAA;IACjB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;CAC1C;AAGD,MAAM,WAAW,WAAW;CAE3B;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,SAAS,CAAA"}
@@ -0,0 +1,5 @@
1
+ export type * from './field';
2
+ export type * from './form';
3
+ export type * from './validation';
4
+ export type * from './globals';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,SAAS,CAAA;AAC5B,mBAAmB,QAAQ,CAAA;AAC3B,mBAAmB,cAAc,CAAA;AACjC,mBAAmB,WAAW,CAAA"}
@@ -0,0 +1,9 @@
1
+ export type ValidationResult<Result, Err> = {
2
+ isValid: boolean;
3
+ result?: Result;
4
+ error?: Err;
5
+ readableError?: string;
6
+ };
7
+ export type ValidatorFunction<Value, Result, Err> = (value: Value, form: any) => ValidationResult<Result, Err>;
8
+ export type Validator<Value, Result, Err> = ValidatorFunction<Value, Result, Err>;
9
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/types/validation.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,CAAC,MAAM,EAAE,GAAG,IAAI;IAC1C,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,GAAG,CAAA;IACX,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,gBAAgB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AAE9G,MAAM,MAAM,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from './zod';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validators/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAA"}
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod';
2
+ import { ValidationResult } from '../types';
3
+ type ZodValidationResult<T extends z.ZodType> = ValidationResult<z.infer<T>, z.ZodError['issues']>;
4
+ /**
5
+ * Adapts a Zod schema into the `Validator` contract expected by `Field` and
6
+ * `useValidate`. Uses `safeParse` internally so Zod errors are captured as
7
+ * `{ isValid: false, error: ZodIssue[] }` rather than thrown exceptions.
8
+ *
9
+ * The returned function is synchronous — async Zod schemas (`z.promise`,
10
+ * `.parseAsync`) are not supported.
11
+ */
12
+ export declare function zodValidator<T extends z.ZodType>(model: T): (value: unknown) => ZodValidationResult<T>;
13
+ /**
14
+ * Type guard that narrows a `ValidationResult`-shaped value to one produced by
15
+ * `zodValidator`. A valid result must have `result` present; an invalid result
16
+ * must have `error` as an array of `ZodIssue` objects. Use this to
17
+ * distinguish Zod results from results produced by custom validators when
18
+ * handling errors generically.
19
+ */
20
+ export declare function isZodValidationResult(val: any): val is ZodValidationResult<any>;
21
+ export {};
22
+ //# sourceMappingURL=zod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod.d.ts","sourceRoot":"","sources":["../../src/validators/zod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAY,MAAM,KAAK,CAAA;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAG3C,KAAK,mBAAmB,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,IAAI,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAA;AAElG;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,IAChD,OAAO,OAAO,KAAG,mBAAmB,CAAC,CAAC,CAAC,CAShD;AAOD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,mBAAmB,CAAC,GAAG,CAAC,CAa/E"}
package/package.json CHANGED
@@ -1,7 +1,20 @@
1
1
  {
2
2
  "name": "@codeleap/form",
3
- "version": "6.3.0",
3
+ "version": "6.8.0",
4
4
  "main": "src/index.ts",
5
+ "types": "dist/index.d.ts",
6
+ "exports": {
7
+ ".": {
8
+ "source": "./src/index.ts",
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src"
17
+ ],
5
18
  "license": "UNLICENSED",
6
19
  "repository": {
7
20
  "url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
@@ -9,22 +22,25 @@
9
22
  "directory": "packages/form"
10
23
  },
11
24
  "devDependencies": {
12
- "@codeleap/config": "6.3.0",
13
- "@codeleap/types": "6.3.0",
14
- "@codeleap/store": "6.3.0",
15
- "zod": "3.23.8",
25
+ "@codeleap/config": "6.8.0",
26
+ "@codeleap/types": "6.8.0",
27
+ "@codeleap/store": "6.8.0",
28
+ "@codeleap/hooks": "6.8.0",
29
+ "zod": "4.4.3",
16
30
  "ts-node-dev": "1.1.8"
17
31
  },
18
32
  "scripts": {
19
- "build": "echo 'No build needed'",
33
+ "build": "tsc --build tsconfig.build.json",
34
+ "typecheck": "bun tsc --noEmit -p ./tsconfig.json",
20
35
  "playground": "bun src/test.ts"
21
36
  },
22
37
  "peerDependencies": {
23
- "@codeleap/types": "6.3.0",
24
- "@codeleap/store": "6.3.0",
25
- "@codeleap/logger": "6.3.0",
38
+ "@codeleap/types": "6.8.0",
39
+ "@codeleap/store": "6.8.0",
40
+ "@codeleap/logger": "6.8.0",
41
+ "@codeleap/hooks": "6.8.0",
26
42
  "zod": "*",
27
43
  "react": "19.1.0",
28
44
  "typescript": "5.5.2"
29
45
  }
30
- }
46
+ }
@@ -5,10 +5,16 @@ import { zodValidator } from "../validators"
5
5
 
6
6
  type VALUE = boolean
7
7
 
8
+ /** Validator signature for boolean-valued fields. */
8
9
  export type BooleanValidator<R = any, Err = any> = Validator<VALUE, R, Err>
9
10
 
10
11
  type BooleanFieldOptions<Validate extends BooleanValidator> = FieldOptions<VALUE, Validate>
11
12
 
13
+ /**
14
+ * Field for boolean values (checkboxes, toggles). `Form.setValues` treats
15
+ * this field specially: it will only set a value when the incoming value is
16
+ * strictly `boolean`, because falsy coercion would prevent setting `false`.
17
+ */
12
18
  export class BooleanField<Validate extends BooleanValidator> extends Field<boolean, Validate> {
13
19
  _type = "BOOLEAN"
14
20
 
@@ -4,6 +4,7 @@ import { FieldOptions, Validator } from "../types"
4
4
  import { zodValidator } from "../validators"
5
5
  import dayjs from 'dayjs'
6
6
 
7
+ /** Validator signature for `Date`-valued fields. */
7
8
  export type DateValidator<R = any, Err = any> = Validator<Date, R, Err>
8
9
 
9
10
  type DateFieldOptions<Validate extends DateValidator> = FieldOptions<Date, Validate> & {
@@ -24,6 +25,14 @@ function normalizeToDate(val: unknown): Date | undefined {
24
25
  return undefined
25
26
  }
26
27
 
28
+ /**
29
+ * Field for `Date` values with optional `minimumDate` / `maximumDate` bounds.
30
+ * The built-in validator normalises the raw input via `dayjs` before running
31
+ * boundary checks, so it transparently accepts `Date` objects, dayjs instances,
32
+ * and ISO date strings. Invalid or unparseable values are normalised to
33
+ * `undefined`, which fails the `z.date()` check. Bounds are enforced at the
34
+ * start of the day (`startOf('day')`) for dayjs inputs.
35
+ */
27
36
  export class DateField<Validate extends DateValidator> extends Field<Date, Validate> {
28
37
  _type = "DATE"
29
38
 
@@ -3,10 +3,17 @@ import { z } from 'zod'
3
3
  import { FieldOptions, Validator } from "../types"
4
4
  import { zodValidator } from "../validators"
5
5
 
6
+ /** Validator signature for file-valued fields. The value type is intentionally `any` to accommodate platform-specific file objects (web `File`, React Native asset objects, etc.). */
6
7
  export type FileValidator<R = any, Err = any> = Validator<any, R, Err>
7
8
 
8
9
  type FileFieldOptions<Validate extends FileValidator> = FieldOptions<any, Validate>
9
10
 
11
+ /**
12
+ * Field for file uploads. The value type is `any` because the shape of a
13
+ * "file" differs between web (`File` / `FileList`) and React Native (asset
14
+ * descriptor objects). Supply a custom `validate` function to enforce whatever
15
+ * shape your platform produces.
16
+ */
10
17
  export class FileField<Validate extends FileValidator> extends Field<any, Validate> {
11
18
  _type = "FILE"
12
19
 
@@ -9,6 +9,12 @@ type GroupValidator<R = any, Err = any> = Validator<VALUE, R, Err>
9
9
 
10
10
  type GroupFieldOptions<Validate extends GroupValidator> = FieldOptions<VALUE, Validate>
11
11
 
12
+ /**
13
+ * Field that represents a selected group or category, stored as a string key.
14
+ * Structurally identical to `TextField` but carries the `"group"` type tag so
15
+ * platform renderers can distinguish it and render a group-picker control
16
+ * instead of a free-text input.
17
+ */
12
18
  export class GroupField<Validate extends GroupValidator> extends Field<string, Validate> {
13
19
  _type = "group"
14
20
 
@@ -1,15 +1,30 @@
1
1
  import { fieldFactory } from "../lib/factories"
2
2
  import { TextField } from "./text"
3
- import { ListField } from "./list"
3
+ import { ListField, listFieldFactory} from "./list"
4
4
  import { NumberField } from "./number"
5
5
  import { BooleanField } from "./bool"
6
6
  import { SelectableField } from "./selectable"
7
7
  import { DateField } from "./date"
8
8
  import { FileField } from "./file"
9
9
 
10
+
11
+ /**
12
+ * Convenience namespace that exposes factory functions for every built-in field
13
+ * type. Prefer these over calling constructors directly — they provide better
14
+ * generic inference for the `validate` option.
15
+ *
16
+ * ```ts
17
+ * const emailField = fields.text({ validate: zodValidator(z.string().email()) })
18
+ * const tagsField = fields.list({ item: fields.text(), defaultValue: [] })
19
+ * ```
20
+ *
21
+ * Note: `fields.list` is the only entry that is not wrapped with
22
+ * `fieldFactory`, because `ListField` requires the `item` option's type to be
23
+ * inferred at the call site.
24
+ */
10
25
  export const fields = {
11
26
  text: fieldFactory(TextField),
12
- list: fieldFactory(ListField),
27
+ list: listFieldFactory,
13
28
  number: fieldFactory(NumberField),
14
29
  boolean: fieldFactory(BooleanField),
15
30
  selectable: fieldFactory(SelectableField),
@@ -1,36 +1,85 @@
1
1
  import { Field } from "../lib/Field"
2
2
  import { FieldOptions, Validator } from "../types"
3
3
 
4
- export type ListValidator<Val, R = any, Err = any> = Validator<Val[] | Val, R, Err>
4
+ /** Validator signature for array-valued fields. `R` and `Err` are the result and error types for the whole list, not individual items. */
5
+ export type ListValidator<Val, R = any, Err = any> = Validator<Val[], R, Err>
5
6
 
6
- type ListFieldOptions<T, Validate extends Validator<T | T[], any, any>> = FieldOptions<T, Validate> & {
7
- item: Field<T, Validate>
8
- }
7
+ type ExtractFieldValue<T> = T extends Field<infer Value, any> ? Value : never
8
+
9
+ type AsListValidator<T> = T extends Field<infer Value, infer Validate> ?
10
+ Validator<
11
+ Value[],
12
+ Validate extends Validator<any, infer R, any> ? R[] : never,
13
+ Validate extends Validator<any, any, infer Err> ? Err[] : never
14
+ >
15
+ : never
16
+
17
+ export type _ListFieldOptions<T> = T extends Field<infer Value, infer Validate> ? FieldOptions<
18
+ Value[],
19
+ Validator<
20
+ Value[],
21
+ Validate extends Validator<any, infer R, any> ? R[] : never,
22
+ Validate extends Validator<any, any, infer Err> ? Err[] : never
23
+ >
24
+ > : never
9
25
 
10
- export class ListField<T, Validate extends ListValidator<T>> extends Field<T | T[], Validate> {
26
+ export type ListFieldOptions<ItemField extends Field<any, any>> = {
27
+ item: ItemField
28
+ } & _ListFieldOptions<ItemField>
29
+
30
+ /**
31
+ * Field that holds an array of values, each validated by a delegate `item`
32
+ * field. The built-in validator iterates every element through
33
+ * `options.item.validate` and collects all failures into an error array. The
34
+ * list is considered valid only when every element passes. If `item` has no
35
+ * validator, the list is always valid.
36
+ *
37
+ * The `item` field acts as a prototype — it is not mounted independently and
38
+ * does not own its own atom. Do not call `use()` on it directly.
39
+ */
40
+ export class ListField<T extends Field<any, any>> extends Field<ExtractFieldValue<T>[], AsListValidator<T>> {
11
41
  _type = "LIST"
12
42
 
13
- constructor(options: ListFieldOptions<T[] | T, Validate>) {
43
+ constructor(options: ListFieldOptions<T>) {
14
44
  super({
15
- // @ts-ignore fix this
16
- validate: (v) => {
17
- if (!options.validate) return {
45
+ ...options,
46
+ validate: ((v, form) => {
47
+ if (!options.item?.validate) return {
18
48
  isValid: true
19
49
  }
20
50
 
21
- for (const value of v as T[]) {
22
- const validation = options.validate?.(value, null)
51
+ const errors = []
52
+
53
+ for (const value of v) {
54
+ const validation = options.item?.validate?.(value)
23
55
 
24
56
  if (!validation.isValid) {
25
- return validation
57
+ errors.push(validation)
58
+ }
59
+ }
60
+
61
+ if (errors.length) {
62
+ return {
63
+ isValid: false,
64
+ error: errors,
26
65
  }
27
66
  }
28
67
 
29
68
  return {
30
- isValid: true
69
+ isValid: true,
31
70
  }
32
- },
33
- ...options
34
- })
71
+ }) as AsListValidator<T>,
72
+ } as unknown as FieldOptions<ExtractFieldValue<T>[], AsListValidator<T>>)
35
73
  }
36
- }
74
+ }
75
+
76
+ /**
77
+ * Convenience factory for `ListField`. Unlike `fieldFactory`-based helpers,
78
+ * `ListField` requires an `item` option whose generic type must be inferred at
79
+ * the call site, so it cannot share the same factory wrapper. This function
80
+ * exists to provide a consistent `fields.list(...)` call signature alongside
81
+ * the other entries in the `fields` namespace.
82
+ */
83
+ export function listFieldFactory<T extends Field<any,any>>(options: ListFieldOptions<T>): ListField<T> {
84
+ return new ListField(options)
85
+ }
@@ -3,6 +3,7 @@ import { z } from 'zod'
3
3
  import { FieldOptions, Validator } from "../types"
4
4
  import { zodValidator } from "../validators"
5
5
 
6
+ /** Validator signature for numeric fields. Accepts either a single number or an array of numbers (for range inputs). */
6
7
  export type NumberValidator<R = any, Err = any> = Validator<number | number[], R, Err>
7
8
 
8
9
  type Props = {
@@ -14,6 +15,14 @@ type NumberFieldOptions<Validate extends NumberValidator> = FieldOptions<number
14
15
 
15
16
  const MAX_VALID_DIGITS = 1000000000000000 // maximum number of digits that the input supports to perform operations
16
17
 
18
+ /**
19
+ * Field for numeric values. Supports both single numbers and arrays of numbers
20
+ * (range / multi-handle sliders) — the mode is inferred from whether
21
+ * `defaultValue` is an array. `min` defaults to `0` and `max` defaults to
22
+ * `1_000_000_000_000_000` (the maximum safe integer range the underlying input
23
+ * supports for arithmetic operations). Values outside the range fail
24
+ * validation.
25
+ */
17
26
  export class NumberField<Validate extends NumberValidator> extends Field<number | number[], Validate> {
18
27
  _type = "NUMBER"
19
28
 
@@ -6,6 +6,7 @@ import { Options } from '@codeleap/types'
6
6
 
7
7
  type VALUE = string | number
8
8
 
9
+ /** Validator signature for selectable fields whose value is a `string` or `number` key. */
9
10
  export type SelectableValidator<V extends VALUE, R = any, Err = any> = Validator<V, R, Err>
10
11
 
11
12
  type SelectableFieldOptions<V extends VALUE, Validate extends SelectableValidator<V>> = FieldOptions<V, Validate> & {
@@ -14,6 +15,14 @@ type SelectableFieldOptions<V extends VALUE, Validate extends SelectableValidato
14
15
  maxItems?: number
15
16
  }
16
17
 
18
+ /**
19
+ * Field for single or multi-select inputs whose options are a fixed list of
20
+ * string/number keys. `minItems` defaults to `1` (at least one selection
21
+ * required) and `maxItems` defaults to the length of the provided `options`
22
+ * array (all items selectable). The built-in validator accepts either a single
23
+ * value or an array, so the same field type covers both radio-style and
24
+ * checkbox-style UIs.
25
+ */
17
26
  export class SelectableField<V extends VALUE, Validate extends SelectableValidator<V>> extends Field<V, Validate> {
18
27
  _type = "SELECTABLE"
19
28
 
@@ -5,6 +5,7 @@ import { zodValidator } from "../validators"
5
5
 
6
6
  type VALUE = string
7
7
 
8
+ /** Validator signature for string-valued fields. */
8
9
  export type TextValidator<R = any, Err = any> = Validator<VALUE, R, Err>
9
10
 
10
11
  type TextFieldOptions<Validate extends TextValidator> = FieldOptions<VALUE, Validate> & {
@@ -12,12 +13,18 @@ type TextFieldOptions<Validate extends TextValidator> = FieldOptions<VALUE, Vali
12
13
  multiline?: boolean
13
14
  }
14
15
 
16
+ /**
17
+ * Field for plain string values. Ships with a `z.string()` fallback validator
18
+ * so the field is always valid when no custom `validate` option is provided.
19
+ * Accepts `secure` (password masking) and `multiline` hints that are forwarded
20
+ * as props to the underlying input component.
21
+ */
15
22
  export class TextField<Validate extends TextValidator> extends Field<string, Validate> {
16
23
  _type = "TEXT"
17
24
 
18
25
  constructor(options: TextFieldOptions<Validate>) {
19
26
  super({
20
- validate: zodValidator(z.string()) as unknown as Validate,
27
+ validate: zodValidator(z.string().optional()) as unknown as Validate,
21
28
  ...options
22
29
  })
23
30
  }