@react-typed-forms/schemas 14.2.0 → 14.3.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.
@@ -0,0 +1,217 @@
1
+ import {
2
+ ControlDefinition,
3
+ DataControlDefinition,
4
+ isDataControl,
5
+ ControlDataContext,
6
+ getRootDataNode,
7
+ getJsonPath,
8
+ } from "./controlDefinition";
9
+ import {
10
+ Control,
11
+ useValidator,
12
+ useValueChangeEffect,
13
+ } from "@react-typed-forms/core";
14
+ import { useCallback } from "react";
15
+ import { useUpdatedRef } from "./util";
16
+ import { useJsonataExpression } from "./hooks";
17
+ import { makeHookDepString } from "./dynamicHooks";
18
+ import {
19
+ DateComparison,
20
+ DateValidator,
21
+ JsonataValidator,
22
+ LengthValidator,
23
+ SchemaValidator,
24
+ ValidatorType,
25
+ } from "./schemaValidator";
26
+ import { FieldType, SchemaField, ValidationMessageType } from "./schemaField";
27
+
28
+ interface ValidationHookContext {
29
+ hiddenControl: Control<boolean | null | undefined>;
30
+ dataContext: ControlDataContext;
31
+ control: Control<any>;
32
+ }
33
+
34
+ export interface ValidationContext extends ValidationHookContext {
35
+ definition: DataControlDefinition;
36
+ field: SchemaField;
37
+ index: number;
38
+ }
39
+
40
+ export function useMakeValidationHook(
41
+ definition: ControlDefinition,
42
+ useValidatorFor: (
43
+ validator: SchemaValidator,
44
+ ctx: ValidationContext,
45
+ ) => void = useDefaultValidator,
46
+ ): (ctx: ValidationHookContext) => void {
47
+ const dd = isDataControl(definition) ? definition : undefined;
48
+
49
+ const refData = useUpdatedRef({ dd, useValidatorFor });
50
+ const depString = dd
51
+ ? makeHookDepString(dd.validators ?? [], (x) => x.type)
52
+ : "~";
53
+ return useCallback(
54
+ (ctx) => {
55
+ const { dd } = refData.current;
56
+ if (!dd) return;
57
+ const field = ctx.dataContext.dataNode?.schema.field ?? {
58
+ field: "__missing",
59
+ type: FieldType.Any,
60
+ };
61
+ const {
62
+ control,
63
+ hiddenControl,
64
+ dataContext: { schemaInterface },
65
+ } = ctx;
66
+
67
+ useValueChangeEffect(control, () => control.setError("default", ""));
68
+ if (dd.required)
69
+ useValidator(
70
+ control,
71
+ (v) => {
72
+ return !hiddenControl.value &&
73
+ schemaInterface.isEmptyValue(field, v)
74
+ ? schemaInterface.validationMessageText(
75
+ field,
76
+ ValidationMessageType.NotEmpty,
77
+ false,
78
+ true,
79
+ )
80
+ : null;
81
+ },
82
+ "required",
83
+ );
84
+ dd.validators?.forEach((v, i) =>
85
+ useValidatorFor(v, { ...ctx, index: i, field, definition: dd }),
86
+ );
87
+ },
88
+ [!!dd, dd?.required, depString, useValidatorFor],
89
+ );
90
+ }
91
+
92
+ function useDefaultValidator(
93
+ validator: SchemaValidator,
94
+ ctx: ValidationContext,
95
+ ) {
96
+ switch (validator.type) {
97
+ case ValidatorType.Length:
98
+ useLengthValidator(validator as LengthValidator, ctx);
99
+ break;
100
+ case ValidatorType.Jsonata:
101
+ useJsonataValidator(validator as JsonataValidator, ctx);
102
+ break;
103
+ case ValidatorType.Date:
104
+ useDateValidator(validator as DateValidator, ctx);
105
+ break;
106
+ }
107
+ }
108
+
109
+ export function useJsonataValidator(
110
+ validator: JsonataValidator,
111
+ ctx: ValidationContext,
112
+ ) {
113
+ const sdn = ctx.dataContext.parentNode;
114
+ const errorMsg = useJsonataExpression(
115
+ validator.expression,
116
+ getRootDataNode(sdn).control!,
117
+ getJsonPath(sdn),
118
+ undefined,
119
+ (v) => (v == null ? null : typeof v === "string" ? v : JSON.stringify(v)),
120
+ );
121
+ useValidator(
122
+ ctx.control,
123
+ () => (!ctx.hiddenControl.value ? errorMsg.value : null),
124
+ "jsonata" + ctx.index,
125
+ );
126
+ }
127
+
128
+ export function useLengthValidator(
129
+ lv: LengthValidator,
130
+ ctx: ValidationContext,
131
+ ) {
132
+ const {
133
+ control,
134
+ dataContext: { schemaInterface },
135
+ hiddenControl,
136
+ field,
137
+ } = ctx;
138
+ useValidator(
139
+ control,
140
+ (v) => {
141
+ const len = schemaInterface.controlLength(field, control);
142
+ const hidden = hiddenControl.value;
143
+ if (hidden) {
144
+ return undefined;
145
+ }
146
+ if (lv.min != null && len < lv.min) {
147
+ if (field?.collection) {
148
+ control.setValue((v) =>
149
+ Array.isArray(v)
150
+ ? v.concat(Array.from({ length: lv.min! - v.length }))
151
+ : Array.from({ length: lv.min! }),
152
+ );
153
+ } else {
154
+ return schemaInterface.validationMessageText(
155
+ field,
156
+ ValidationMessageType.MinLength,
157
+ len,
158
+ lv.min,
159
+ );
160
+ }
161
+ } else if (lv.max != null && len > lv.max) {
162
+ return schemaInterface.validationMessageText(
163
+ field,
164
+ ValidationMessageType.MaxLength,
165
+ len,
166
+ lv.max,
167
+ );
168
+ }
169
+ return undefined;
170
+ },
171
+ "length" + ctx.index,
172
+ );
173
+ }
174
+
175
+ export function useDateValidator(dv: DateValidator, ctx: ValidationContext) {
176
+ const {
177
+ control,
178
+ field,
179
+ index,
180
+ dataContext: { schemaInterface },
181
+ } = ctx;
182
+ let comparisonDate: number;
183
+ if (dv.fixedDate) {
184
+ comparisonDate = schemaInterface.parseToMillis(field, dv.fixedDate);
185
+ } else {
186
+ const nowDate = new Date();
187
+ comparisonDate = Date.UTC(
188
+ nowDate.getFullYear(),
189
+ nowDate.getMonth(),
190
+ nowDate.getDate(),
191
+ );
192
+ if (dv.daysFromCurrent) {
193
+ comparisonDate += dv.daysFromCurrent * 86400000;
194
+ }
195
+ }
196
+ useValidator(
197
+ control,
198
+ (v) => {
199
+ if (v) {
200
+ const selDate = schemaInterface.parseToMillis(field, v);
201
+ const notAfter = dv.comparison === DateComparison.NotAfter;
202
+ if (notAfter ? selDate > comparisonDate : selDate < comparisonDate) {
203
+ return schemaInterface.validationMessageText(
204
+ field,
205
+ notAfter
206
+ ? ValidationMessageType.NotAfterDate
207
+ : ValidationMessageType.NotBeforeDate,
208
+ selDate,
209
+ comparisonDate,
210
+ );
211
+ }
212
+ }
213
+ return null;
214
+ },
215
+ "date" + index,
216
+ );
217
+ }