Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | 94x 91x 62x 13x 49x 11x 38x 1x 37x 90x 74x 16x 1x 15x 15x 27x 27x 1x 26x 29x 29x 29x 94x 3x 3x 12x 3x 91x 1x 90x 90x 94x 94x 94x 94x 29x 82x 21x 21x 68x 68x 68x 21x 1x 3x 3x | import type {
FieldSchema,
FormSchema,
FormValues,
NormalizedField,
NormalizedFormSchema,
NormalizedSchemaNode,
NormalizedSection,
SchemaNode,
SectionSchema
} from "./types";
function isSection(node: SchemaNode): node is SectionSchema {
return node.type === "section";
}
function normalizeId(value: string): string {
return value
.trim()
.toLowerCase()
.replace(/[^a-z0-9_-]+/g, "-")
.replace(/^-+|-+$/g, "");
}
function defaultValueForField(field: FieldSchema) {
if (field.defaultValue !== undefined) {
return field.defaultValue;
}
if (field.type === "checkbox") {
return false;
}
if (field.type === "number") {
return null;
}
return "";
}
function validateOptionField(field: FieldSchema, schemaId: string): void {
if (field.type !== "select" && field.type !== "radio") {
return;
}
if (!field.options || field.options.length === 0) {
throw new Error(`Field "${field.name}" in schema "${schemaId}" requires at least one option.`);
}
const optionValues = new Set<string>();
field.options.forEach((option) => {
const optionValue = String(option.value);
if (optionValues.has(optionValue)) {
throw new Error(`Field "${field.name}" in schema "${schemaId}" has duplicate option value "${optionValue}".`);
}
optionValues.add(optionValue);
});
}
/**
* Prepares a public schema for rendering and validation.
*
* Normalization keeps consumer-facing schema data unchanged while deriving
* stable IDs, traversal paths, field lookup maps, and duplicate/option checks.
*/
export function normalizeSchema(schema: FormSchema): NormalizedFormSchema {
const fieldMap = new Map<string, NormalizedField>();
const fieldOrder: string[] = [];
const normalizeNode = (node: SchemaNode, path: string[]): NormalizedSchemaNode => {
if (isSection(node)) {
const sectionId = node.id ? normalizeId(node.id) : normalizeId([...path, node.title].join("-"));
const normalizedSection: NormalizedSection = {
...node,
id: sectionId,
path,
fields: node.fields.map((child, index) =>
normalizeNode(child, [...path, sectionId || `section-${index}`])
)
};
return normalizedSection;
}
if (fieldMap.has(node.name)) {
throw new Error(`Duplicate field name "${node.name}" in schema "${schema.id}".`);
}
validateOptionField(node, schema.id);
const fieldId = node.id ? normalizeId(node.id) : normalizeId(node.name);
const normalizedField: NormalizedField = {
...node,
id: fieldId,
path
};
fieldMap.set(node.name, normalizedField);
fieldOrder.push(node.name);
return normalizedField;
};
return {
...schema,
fields: schema.fields.map((node, index) => normalizeNode(node, [schema.id, String(index)])),
fieldMap,
fieldOrder
};
}
export function getInitialValues(schema: NormalizedFormSchema, values: FormValues = {}): FormValues {
const initialValues: FormValues = {};
schema.fieldOrder.forEach((fieldName) => {
const field = schema.fieldMap.get(fieldName);
Iif (!field) {
return;
}
initialValues[fieldName] =
values[fieldName] !== undefined ? values[fieldName] : defaultValueForField(field);
});
return initialValues;
}
/** Return all normalized value-producing fields in schema order. */
export function flattenFields(schema: NormalizedFormSchema): NormalizedField[] {
return schema.fieldOrder
.map((fieldName) => schema.fieldMap.get(fieldName))
.filter((field): field is NormalizedField => Boolean(field));
}
|