@form-eng/core 1.1.1 → 1.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.
- package/dist/index.d.mts +157 -42
- package/dist/index.d.ts +157 -42
- package/dist/index.js +207 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +192 -30
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -4
package/dist/index.mjs
CHANGED
|
@@ -488,7 +488,27 @@ function resetValueFunctionRegistry() {
|
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
// src/helpers/ExpressionEngine.ts
|
|
491
|
+
import { Parser } from "expr-eval";
|
|
492
|
+
var _parser = new Parser();
|
|
493
|
+
var _origAdd = _parser.binaryOps["+"];
|
|
494
|
+
_parser.binaryOps["+"] = (a, b) => {
|
|
495
|
+
if (typeof a === "string" || typeof b === "string") return String(a) + String(b);
|
|
496
|
+
return _origAdd(a, b);
|
|
497
|
+
};
|
|
498
|
+
_parser.consts["NaN"] = NaN;
|
|
491
499
|
function evaluateExpression(expression, values, fieldName, parentEntity, currentUserId) {
|
|
500
|
+
const singleFnMatch = /^\s*\$fn\.([a-zA-Z_][a-zA-Z0-9_]*)\(\)\s*$/.exec(expression);
|
|
501
|
+
if (singleFnMatch) {
|
|
502
|
+
const fn = getValueFunction(singleFnMatch[1]);
|
|
503
|
+
if (!fn) return void 0;
|
|
504
|
+
return fn({
|
|
505
|
+
fieldName: fieldName ?? "",
|
|
506
|
+
fieldValue: fieldName ? values[fieldName] : void 0,
|
|
507
|
+
values,
|
|
508
|
+
parentEntity,
|
|
509
|
+
currentUserId
|
|
510
|
+
});
|
|
511
|
+
}
|
|
492
512
|
let resolved = expression.replace(
|
|
493
513
|
/\$fn\.([a-zA-Z_][a-zA-Z0-9_]*)\(\)/g,
|
|
494
514
|
(_, fnName) => {
|
|
@@ -501,48 +521,35 @@ function evaluateExpression(expression, values, fieldName, parentEntity, current
|
|
|
501
521
|
parentEntity,
|
|
502
522
|
currentUserId
|
|
503
523
|
});
|
|
504
|
-
if (result === null || result === void 0) return "
|
|
524
|
+
if (result === null || result === void 0) return "NaN";
|
|
505
525
|
if (typeof result === "string") return JSON.stringify(result);
|
|
506
|
-
if (result instanceof Date) return
|
|
526
|
+
if (result instanceof Date) return String(result.getTime());
|
|
507
527
|
return String(result);
|
|
508
528
|
}
|
|
509
|
-
return "
|
|
529
|
+
return "NaN";
|
|
510
530
|
}
|
|
511
531
|
);
|
|
532
|
+
const serializeValue = (value) => {
|
|
533
|
+
if (value === null || value === void 0) return "NaN";
|
|
534
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
535
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
536
|
+
if (value instanceof Date) return String(value.getTime());
|
|
537
|
+
return String(value);
|
|
538
|
+
};
|
|
512
539
|
resolved = resolved.replace(
|
|
513
540
|
/\$parent\.([a-zA-Z_][a-zA-Z0-9_.]*)/g,
|
|
514
|
-
(_, fieldPath) => {
|
|
515
|
-
const value = getNestedValue2(parentEntity ?? {}, fieldPath);
|
|
516
|
-
if (value === null || value === void 0) return "undefined";
|
|
517
|
-
if (typeof value === "string") return JSON.stringify(value);
|
|
518
|
-
return String(value);
|
|
519
|
-
}
|
|
541
|
+
(_, fieldPath) => serializeValue(getNestedValue2(parentEntity ?? {}, fieldPath))
|
|
520
542
|
);
|
|
521
543
|
resolved = resolved.replace(
|
|
522
544
|
/\$root\.([a-zA-Z_][a-zA-Z0-9_.]*)/g,
|
|
523
|
-
(_, fieldPath) =>
|
|
524
|
-
const value = getNestedValue2(values, fieldPath);
|
|
525
|
-
if (value === null || value === void 0) return "undefined";
|
|
526
|
-
if (typeof value === "string") return JSON.stringify(value);
|
|
527
|
-
return String(value);
|
|
528
|
-
}
|
|
545
|
+
(_, fieldPath) => serializeValue(getNestedValue2(values, fieldPath))
|
|
529
546
|
);
|
|
530
547
|
resolved = resolved.replace(
|
|
531
548
|
/\$values\.([a-zA-Z_][a-zA-Z0-9_.]*)/g,
|
|
532
|
-
(_, fieldPath) =>
|
|
533
|
-
const value = getNestedValue2(values, fieldPath);
|
|
534
|
-
if (value === null || value === void 0) return "undefined";
|
|
535
|
-
if (typeof value === "string") return JSON.stringify(value);
|
|
536
|
-
return String(value);
|
|
537
|
-
}
|
|
549
|
+
(_, fieldPath) => serializeValue(getNestedValue2(values, fieldPath))
|
|
538
550
|
);
|
|
539
551
|
try {
|
|
540
|
-
|
|
541
|
-
"Math",
|
|
542
|
-
"Date",
|
|
543
|
-
`"use strict"; return (${resolved});`
|
|
544
|
-
);
|
|
545
|
-
return safeEval(Math, Date);
|
|
552
|
+
return _parser.evaluate(resolved, {});
|
|
546
553
|
} catch {
|
|
547
554
|
return void 0;
|
|
548
555
|
}
|
|
@@ -1844,6 +1851,7 @@ var RenderField = (props) => {
|
|
|
1844
1851
|
readOnly,
|
|
1845
1852
|
disabled,
|
|
1846
1853
|
options,
|
|
1854
|
+
optionsLoading,
|
|
1847
1855
|
validate,
|
|
1848
1856
|
parentEntityId,
|
|
1849
1857
|
parentEntityType,
|
|
@@ -1938,6 +1946,7 @@ var RenderField = (props) => {
|
|
|
1938
1946
|
savePending,
|
|
1939
1947
|
config,
|
|
1940
1948
|
options,
|
|
1949
|
+
optionsLoading,
|
|
1941
1950
|
label,
|
|
1942
1951
|
type,
|
|
1943
1952
|
description,
|
|
@@ -1970,6 +1979,7 @@ var RenderField = (props) => {
|
|
|
1970
1979
|
readOnly,
|
|
1971
1980
|
disabled,
|
|
1972
1981
|
options,
|
|
1982
|
+
optionsLoading,
|
|
1973
1983
|
softHidden,
|
|
1974
1984
|
renderLabel,
|
|
1975
1985
|
renderError,
|
|
@@ -2110,6 +2120,7 @@ var ConfirmInputsModal_default = ConfirmInputsModal;
|
|
|
2110
2120
|
|
|
2111
2121
|
// src/components/InlineFormFields.tsx
|
|
2112
2122
|
import React9 from "react";
|
|
2123
|
+
import { useFormContext as useFormContext4 } from "react-hook-form";
|
|
2113
2124
|
|
|
2114
2125
|
// src/components/FormErrorBoundary.tsx
|
|
2115
2126
|
import React8 from "react";
|
|
@@ -2169,6 +2180,33 @@ var FormFields = (props) => {
|
|
|
2169
2180
|
renderStatus,
|
|
2170
2181
|
analytics
|
|
2171
2182
|
} = props;
|
|
2183
|
+
const [asyncOptions, setAsyncOptions] = React9.useState({});
|
|
2184
|
+
const [asyncOptionsLoading, setAsyncOptionsLoading] = React9.useState({});
|
|
2185
|
+
const asyncCacheKeyRef = React9.useRef({});
|
|
2186
|
+
const { getValues } = useFormContext4();
|
|
2187
|
+
React9.useEffect(() => {
|
|
2188
|
+
if (!fields) return;
|
|
2189
|
+
Object.entries(fields).forEach(([fieldId, fieldConfig]) => {
|
|
2190
|
+
if (!fieldConfig.loadOptions) return;
|
|
2191
|
+
const depValues = (fieldConfig.optionsDependsOn ?? []).reduce(
|
|
2192
|
+
(acc, dep) => {
|
|
2193
|
+
acc[dep] = getValues(dep);
|
|
2194
|
+
return acc;
|
|
2195
|
+
},
|
|
2196
|
+
{}
|
|
2197
|
+
);
|
|
2198
|
+
const cacheKey = JSON.stringify(depValues);
|
|
2199
|
+
if (asyncCacheKeyRef.current[fieldId] === cacheKey) return;
|
|
2200
|
+
asyncCacheKeyRef.current[fieldId] = cacheKey;
|
|
2201
|
+
setAsyncOptionsLoading((prev) => ({ ...prev, [fieldId]: true }));
|
|
2202
|
+
fieldConfig.loadOptions({ fieldId, values: getValues() }).then((options) => {
|
|
2203
|
+
setAsyncOptions((prev) => ({ ...prev, [fieldId]: options }));
|
|
2204
|
+
}).catch(() => {
|
|
2205
|
+
}).finally(() => {
|
|
2206
|
+
setAsyncOptionsLoading((prev) => ({ ...prev, [fieldId]: false }));
|
|
2207
|
+
});
|
|
2208
|
+
});
|
|
2209
|
+
});
|
|
2172
2210
|
const collapsedClass = !isExpanded && (expandEnabled || expandEnabled === void 0) ? "collapsed" : "";
|
|
2173
2211
|
const fieldsToRender = GetFieldsToRender(fieldRenderLimit ?? 0, fieldOrder ?? [], formState?.fieldStates);
|
|
2174
2212
|
const loadingKey = `${programName}-${entityType}-${entityId}-form-loaded`;
|
|
@@ -2196,7 +2234,8 @@ var FormFields = (props) => {
|
|
|
2196
2234
|
hidden: fieldState.hidden,
|
|
2197
2235
|
required: fieldState.required,
|
|
2198
2236
|
readOnly: fieldState.readOnly,
|
|
2199
|
-
options: fieldState.options,
|
|
2237
|
+
options: asyncOptions[fieldName] ?? fieldState.options,
|
|
2238
|
+
optionsLoading: asyncOptionsLoading[fieldName] ?? false,
|
|
2200
2239
|
validate: fieldState.validate,
|
|
2201
2240
|
parentEntityId,
|
|
2202
2241
|
parentEntityType,
|
|
@@ -2254,9 +2293,13 @@ var FormEngine = (props) => {
|
|
|
2254
2293
|
renderDialog,
|
|
2255
2294
|
renderSaveButton,
|
|
2256
2295
|
formErrors,
|
|
2296
|
+
fieldErrors,
|
|
2257
2297
|
renderLabel,
|
|
2258
2298
|
renderError,
|
|
2259
|
-
renderStatus
|
|
2299
|
+
renderStatus,
|
|
2300
|
+
onSubmit: onSubmitProp,
|
|
2301
|
+
onSubmitError,
|
|
2302
|
+
renderSubmitButton
|
|
2260
2303
|
} = props;
|
|
2261
2304
|
const fields = formConfig?.fields ?? props.fieldConfigs ?? {};
|
|
2262
2305
|
const formSettings = formConfig?.settings;
|
|
@@ -2292,6 +2335,12 @@ var FormEngine = (props) => {
|
|
|
2292
2335
|
React10.useEffect(() => {
|
|
2293
2336
|
initForm(defaultValues);
|
|
2294
2337
|
}, [areAllFieldsReadonly]);
|
|
2338
|
+
React10.useEffect(() => {
|
|
2339
|
+
if (!fieldErrors) return;
|
|
2340
|
+
Object.entries(fieldErrors).forEach(([fieldName, message]) => {
|
|
2341
|
+
setError(fieldName, { type: "server", message });
|
|
2342
|
+
});
|
|
2343
|
+
}, [fieldErrors]);
|
|
2295
2344
|
const initForm = (entityData) => {
|
|
2296
2345
|
const { formState: loadedState, initEntityData } = isCreate ? InitOnCreateFormState(configName, fields, entityData, parentEntity ?? {}, currentUserId ?? "", setValue, initFormState) : InitOnEditFormState(configName, fields, entityData, areAllFieldsReadonly ?? false, initFormState);
|
|
2297
2346
|
setExpandEnabled(IsExpandVisible(loadedState.fieldStates, effectiveExpandCutoff));
|
|
@@ -2437,6 +2486,26 @@ var FormEngine = (props) => {
|
|
|
2437
2486
|
}
|
|
2438
2487
|
});
|
|
2439
2488
|
};
|
|
2489
|
+
const handleFormSubmit = React10.useCallback(() => {
|
|
2490
|
+
if (!onSubmitProp) return;
|
|
2491
|
+
const cfg = rulesStateRef.current.configs[configName];
|
|
2492
|
+
if (cfg?.fieldStates) {
|
|
2493
|
+
Object.keys(cfg.fieldStates).forEach((fieldName) => {
|
|
2494
|
+
if (cfg.fieldStates[fieldName]?.hidden) clearErrors(fieldName);
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2497
|
+
trigger().then((valid) => {
|
|
2498
|
+
if (!valid) {
|
|
2499
|
+
setIsExpanded(true);
|
|
2500
|
+
(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : setTimeout)(() => focusFirstError());
|
|
2501
|
+
onSubmitError?.(formStateRef.current.errors);
|
|
2502
|
+
} else {
|
|
2503
|
+
const values = formMethods.getValues();
|
|
2504
|
+
Promise.resolve(onSubmitProp(values)).catch(() => {
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
2507
|
+
});
|
|
2508
|
+
}, [onSubmitProp, onSubmitError, configName, trigger, clearErrors]);
|
|
2440
2509
|
const onFilterChange = React10.useCallback((value) => {
|
|
2441
2510
|
if (filterTimeoutRef.current) clearTimeout(filterTimeoutRef.current);
|
|
2442
2511
|
filterTimeoutRef.current = setTimeout(() => setFilterText(value), 500);
|
|
@@ -2492,7 +2561,8 @@ var FormEngine = (props) => {
|
|
|
2492
2561
|
reset();
|
|
2493
2562
|
initForm(defaultValues);
|
|
2494
2563
|
}, disabled: !isDirty || isSubmitting, children: FormStrings.cancel })
|
|
2495
|
-
] }))
|
|
2564
|
+
] })),
|
|
2565
|
+
onSubmitProp && (renderSubmitButton ? renderSubmitButton({ onSubmit: handleFormSubmit, isDirty, isValid, isSubmitting }) : /* @__PURE__ */ jsx10("div", { className: "fe-submit-actions", style: { marginTop: "16px" }, children: /* @__PURE__ */ jsx10("button", { type: "button", className: "submit-button", onClick: handleFormSubmit, disabled: isSubmitting, children: FormStrings.save }) }))
|
|
2496
2566
|
] }),
|
|
2497
2567
|
/* @__PURE__ */ jsx10(
|
|
2498
2568
|
ConfirmInputsModal_default,
|
|
@@ -4121,6 +4191,86 @@ function useBeforeUnload(shouldWarn, message, onAbandonment) {
|
|
|
4121
4191
|
}, [shouldWarn, message]);
|
|
4122
4192
|
}
|
|
4123
4193
|
|
|
4194
|
+
// src/helpers/FieldUtils.ts
|
|
4195
|
+
var GetFieldDataTestId = (fieldName, programName, entityType, entityId) => {
|
|
4196
|
+
return `${programName}-${entityType}-${entityId}-${fieldName}`;
|
|
4197
|
+
};
|
|
4198
|
+
var FieldClassName = (className, error) => {
|
|
4199
|
+
return error ? `${className} error` : className;
|
|
4200
|
+
};
|
|
4201
|
+
function getFieldState(props) {
|
|
4202
|
+
if (props.error) return "error";
|
|
4203
|
+
if (props.required) return "required";
|
|
4204
|
+
if (props.readOnly) return "readonly";
|
|
4205
|
+
if (props.disabled) return "disabled";
|
|
4206
|
+
return void 0;
|
|
4207
|
+
}
|
|
4208
|
+
function formatDateTime(dateStr, options) {
|
|
4209
|
+
if (!dateStr) return "";
|
|
4210
|
+
const date = new Date(dateStr);
|
|
4211
|
+
if (isNaN(date.getTime())) return dateStr;
|
|
4212
|
+
if (options?.hideTimestamp) {
|
|
4213
|
+
return date.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
|
|
4214
|
+
}
|
|
4215
|
+
return date.toLocaleString(void 0, { year: "numeric", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
4216
|
+
}
|
|
4217
|
+
function formatDateTimeValue(value) {
|
|
4218
|
+
if (!value) return "";
|
|
4219
|
+
try {
|
|
4220
|
+
return formatDateTime(value);
|
|
4221
|
+
} catch {
|
|
4222
|
+
return String(value);
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
function formatDateRange(value) {
|
|
4226
|
+
if (!value) return "";
|
|
4227
|
+
const v = value;
|
|
4228
|
+
if (!v.start && !v.end) return "";
|
|
4229
|
+
if (v.start && v.end) return `${v.start} \u2013 ${v.end}`;
|
|
4230
|
+
return v.start || v.end;
|
|
4231
|
+
}
|
|
4232
|
+
function getFileNames(value) {
|
|
4233
|
+
if (!value) return "";
|
|
4234
|
+
if (Array.isArray(value)) return value.map((f) => f.name).join(", ");
|
|
4235
|
+
return value.name ?? "";
|
|
4236
|
+
}
|
|
4237
|
+
function extractDigits(value) {
|
|
4238
|
+
return value.replace(/\D/g, "");
|
|
4239
|
+
}
|
|
4240
|
+
function formatPhone(digits, format) {
|
|
4241
|
+
if (format === "raw") return digits;
|
|
4242
|
+
if (format === "international") {
|
|
4243
|
+
const d2 = digits.slice(0, 12);
|
|
4244
|
+
if (d2.length === 0) return "";
|
|
4245
|
+
if (d2.length <= 1) return `+${d2}`;
|
|
4246
|
+
if (d2.length <= 4) return `+${d2[0]} ${d2.slice(1)}`;
|
|
4247
|
+
if (d2.length <= 7) return `+${d2[0]} ${d2.slice(1, 4)} ${d2.slice(4)}`;
|
|
4248
|
+
return `+${d2[0]} ${d2.slice(1, 4)} ${d2.slice(4, 7)} ${d2.slice(7)}`;
|
|
4249
|
+
}
|
|
4250
|
+
const d = digits.slice(0, 10);
|
|
4251
|
+
if (d.length === 0) return "";
|
|
4252
|
+
if (d.length <= 3) return `(${d}`;
|
|
4253
|
+
if (d.length <= 6) return `(${d.slice(0, 3)}) ${d.slice(3)}`;
|
|
4254
|
+
return `(${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}`;
|
|
4255
|
+
}
|
|
4256
|
+
function ellipsifyText(value, maxChars) {
|
|
4257
|
+
if (!value || value.length <= maxChars) return value ?? "";
|
|
4258
|
+
const cutoff = maxChars - 3;
|
|
4259
|
+
return `${value.substring(0, cutoff)}...`;
|
|
4260
|
+
}
|
|
4261
|
+
var MAX_FILE_SIZE_MB_DEFAULT = 10;
|
|
4262
|
+
var DocumentLinksStrings = {
|
|
4263
|
+
link: "Link",
|
|
4264
|
+
addLink: "Add Link",
|
|
4265
|
+
addAnotherLink: "Add Another Link",
|
|
4266
|
+
deleteLink: "Delete Link",
|
|
4267
|
+
confirmDeleteLink: "Are you sure you want to delete",
|
|
4268
|
+
delete: "Delete",
|
|
4269
|
+
cancel: "Cancel",
|
|
4270
|
+
saveChanges: "Save Changes",
|
|
4271
|
+
save: "Save"
|
|
4272
|
+
};
|
|
4273
|
+
|
|
4124
4274
|
// src/helpers/RuleTracer.ts
|
|
4125
4275
|
var traceEnabled = false;
|
|
4126
4276
|
var traceLog = [];
|
|
@@ -4156,9 +4306,11 @@ export {
|
|
|
4156
4306
|
CheckValidDropdownOptions,
|
|
4157
4307
|
ComponentTypes,
|
|
4158
4308
|
ConfirmInputsModal_default as ConfirmInputsModal,
|
|
4309
|
+
DocumentLinksStrings,
|
|
4159
4310
|
ExecuteComputedValue,
|
|
4160
4311
|
FIELD_PARENT_PREFIX,
|
|
4161
4312
|
FieldArray,
|
|
4313
|
+
FieldClassName,
|
|
4162
4314
|
FieldWrapper,
|
|
4163
4315
|
FormConstants,
|
|
4164
4316
|
FormDevTools,
|
|
@@ -4170,11 +4322,13 @@ export {
|
|
|
4170
4322
|
GetComputedValuesOnCreate,
|
|
4171
4323
|
GetComputedValuesOnDirtyFields,
|
|
4172
4324
|
GetConfirmInputModalProps,
|
|
4325
|
+
GetFieldDataTestId,
|
|
4173
4326
|
GetFieldsToRender,
|
|
4174
4327
|
InitOnCreateFormState,
|
|
4175
4328
|
InitOnEditFormState,
|
|
4176
4329
|
InjectedFieldProvider,
|
|
4177
4330
|
IsExpandVisible,
|
|
4331
|
+
MAX_FILE_SIZE_MB_DEFAULT,
|
|
4178
4332
|
RenderField_default as RenderField,
|
|
4179
4333
|
RulesEngineActionType,
|
|
4180
4334
|
RulesEngineProvider,
|
|
@@ -4202,6 +4356,7 @@ export {
|
|
|
4202
4356
|
detectDependencyCycles,
|
|
4203
4357
|
detectSelfDependencies,
|
|
4204
4358
|
disableRuleTracing,
|
|
4359
|
+
ellipsifyText,
|
|
4205
4360
|
enableRuleTracing,
|
|
4206
4361
|
evaluateAffectedFields,
|
|
4207
4362
|
evaluateAllRules,
|
|
@@ -4209,12 +4364,19 @@ export {
|
|
|
4209
4364
|
evaluateExpression,
|
|
4210
4365
|
executeValueFunction,
|
|
4211
4366
|
extractConditionDependencies,
|
|
4367
|
+
extractDigits,
|
|
4212
4368
|
extractExpressionDependencies,
|
|
4213
4369
|
extractFunctionDependencies,
|
|
4214
4370
|
flushRenderCycle,
|
|
4371
|
+
formatDateRange,
|
|
4372
|
+
formatDateTime,
|
|
4373
|
+
formatDateTimeValue,
|
|
4374
|
+
formatPhone,
|
|
4215
4375
|
fromRjsfSchema,
|
|
4216
4376
|
getAllValidatorMetadata,
|
|
4217
4377
|
getCurrentLocale,
|
|
4378
|
+
getFieldState,
|
|
4379
|
+
getFileNames,
|
|
4218
4380
|
getLastRenderedFields,
|
|
4219
4381
|
getLocaleString,
|
|
4220
4382
|
getRenderCounts,
|