Skip to content

Commit

Permalink
refactor templates and add next variants
Browse files Browse the repository at this point in the history
  • Loading branch information
KryptXBSA committed Jan 11, 2025
1 parent 54ffceb commit 2f3b1b1
Show file tree
Hide file tree
Showing 40 changed files with 695 additions and 185 deletions.
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"emblor": "^1.4.7",
"formbuilder-core": "workspace:*",
"handlebars": "^4.7.8",
"handlebars-loader": "^1.7.3",
"immer": "^10.1.1",
"input-otp": "^1.4.1",
"json-schema-to-zod": "^2.5.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// TODO: not needed?
import type { FormSchema } from "formbuilder-core";
import { useFormContext } from "react-hook-form";

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/mock/mockFields.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FormField } from "formbuilder-core";

export const mockFields: FormField[] = [
export const mockFields: FormField<"nextjs">[] = [
{
id: "username",
label: "Username",
Expand Down
10 changes: 5 additions & 5 deletions apps/web/src/mock/mockForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import type { FormSchema } from "formbuilder-core";
export const mockForm: FormSchema = {
id: 1,
settings: {
importAliasComponents: "aa",
importAliasUtils: "az",
importAliasComponents: "@/components/ui",
importAliasUtils: "@/lib/utils",
noDescription: false,
noPlaceholder: false,
},
Expand Down Expand Up @@ -39,7 +39,7 @@ export const mockForm: FormSchema = {
label: "Email",
key: "email",
kind: "text",
variant: "next-shadcn-text-markdown",
variant: "next-shadcn-text-input",
required: true,
defaultValue: "",
validation: { min: 1, max: 255, email: true },
Expand All @@ -51,7 +51,7 @@ export const mockForm: FormSchema = {
key: "securityEmails",
defaultValue: false,
kind: "boolean",
variant: "next-shadcn-boolean-toggle",
variant: "next-shadcn-boolean-switch",
required: true,
},
],
Expand All @@ -63,7 +63,7 @@ export const mockForm: FormSchema = {
description: "Your date of birth is used to calculate your age.",
key: "dateOfBirth",
kind: "date",
variant: "next-shadcn-date-calendar-picker",
variant: "next-shadcn-date-date",
required: true,
},
{
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/state/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ function createNewField<F extends FormFramework>(
): FormField<F> {
const currentForm = $appState.get().forms[$appState.get().selectedForm];
const { noDescription, noPlaceholder } = currentForm.settings;

// TODO: newField.ts is broken after the new types
// switch (chosenField.kind) {
// case "text":
Expand All @@ -151,6 +150,9 @@ function createNewField<F extends FormFramework>(
kind: chosenField.kind,
variant: chosenField.variant,
};
if (chosenField.kind === "enum") {
(baseField as any).enumName = "myEnum";
}

// Apply settings
if (!noDescription) baseField.description = "";
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/utils/newField.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// TODO: this is not needed?
import type { FormField } from "formbuilder-core";

import { randNum } from "./randNum";
Expand Down
Binary file modified bun.lockb
Binary file not shown.
19 changes: 9 additions & 10 deletions packages/core/src/codegen/imports/generateImports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@ export function generateImports<F extends FormFramework>(framework: FormFramewor
let imports = initialImports;
const addedVariants: Set<string> = new Set();

for (const row of fields) {
for (const col of row) {
if (!addedVariants.has(col.variant)) {
if (col.kind === "heading") {
imports += COMPONENTS[col.variant].imports.replace("{{headingLevel}}", col.headingLevel!);
} else {
imports += COMPONENTS[col.variant].imports;
}
addedVariants.add(col.variant);

fields.flat().forEach((field) => {
if (!addedVariants.has(field.variant)) {
if (field.kind === "heading") {
imports += COMPONENTS[field.variant].imports.replace("{{headingLevel}}", field.headingLevel!);
} else {
imports += COMPONENTS[field.variant].imports;
}
addedVariants.add(field.variant);
}
}
});

return imports;
}
72 changes: 45 additions & 27 deletions packages/core/src/codegen/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,14 @@ import type { FormFramework, FormSchema } from "@/types";
import Handlebars from "handlebars";
import { generateImports } from "./imports/generateImports";
import {
booleanInputTemplate,
comboboxInputTemplate,
dateInputTemplate,
mainTemplate,
numberInputTemplate,
radioInputTemplate,
selectInputTemplate,
stringInputTemplate,
textareaInputTemplate,
} from "./templates";
import { formToZodSchema } from "./utils";
import * as prettier from "prettier/standalone";
import * as parserTypeScript from "prettier/parser-typescript";
import * as prettierPluginEstree from "prettier/plugins/estree";

registerPartials();
import { COMPONENTS } from "../components/components";
import type { FormField } from "@/types/field";

Handlebars.registerHelper("ifEquals", function (arg1, arg2, options) {
//@ts-ignore
Expand All @@ -28,6 +20,12 @@ Handlebars.registerHelper("ifNotEquals", function (arg1, arg2, options) {
return arg1 !== arg2 ? options.fn(this) : options.inverse(this);
});

// Handlebars.registerHelper("eq", function (arg1, arg2, options) {
// //@ts-ignore
// return arg1 === arg2 ? options.fn(this) : options.inverse(this);
// });

// TODOfix default values
Handlebars.registerHelper("defaultValues", (fields) => {
let output = "{\n";
for (const field of fields) {
Expand All @@ -41,15 +39,46 @@ Handlebars.registerHelper("defaultValues", (fields) => {
return new Handlebars.SafeString(output);
});

// biome-ignore lint/complexity/useArrowFunction: <explanation>
Handlebars.registerHelper("lookupComponent", function (field) {
const componentKey = `${field.variant}`;

if (!COMPONENTS[componentKey]) {
console.warn(`No component found for: ${componentKey}`);
return "";
}

let templateText = COMPONENTS[componentKey].template;
const entities: Record<string, string> = {
"&#96;": "`",
"&#36;": "$",
"&quot;": '"',
"&apos;": "'",
"&lt;": "<",
"&gt;": ">",
"&amp;": "&"
};

templateText = templateText.replace(/(&[#\w]+;)/g, entity => entities[entity] || entity);

const template = Handlebars.compile(templateText);
return new Handlebars.SafeString(template(field));
});

const main = Handlebars.compile(mainTemplate);

export async function generateCode(framework: FormFramework, form: FormSchema) {
// const zodFormSchema = formToZodSchema(form);
// const mainCode = main({ ...form, zodFormSchema });
const zodFormSchema = formToZodSchema(form);
// TODO: maybe flat is wrong? because of the nested fields and the way they should rendered (flex)
// YEP!
const flattedFields = form.fields.flat();
const mainCode = main({ ...form, fields: flattedFields, zodFormSchema });

let generatedCode = "";
const imports = Handlebars.compile(generateImports(framework, form.fields))
generatedCode += imports({ importAliasUtils: form.settings.importAliasUtils, importAliasComponents: form.settings.importAliasComponents, });
const imports = Handlebars.compile(generateImports(framework, form.fields));
const generatedCode = imports({
importAliasUtils: form.settings.importAliasUtils,
importAliasComponents: form.settings.importAliasComponents
}) + mainCode;

const formattedCode = await prettier.format(generatedCode, {
parser: "typescript",
Expand All @@ -59,15 +88,4 @@ export async function generateCode(framework: FormFramework, form: FormSchema) {
plugins: [parserTypeScript, prettierPluginEstree],
});
return formattedCode;
}

function registerPartials() {
Handlebars.registerPartial("numberInput", numberInputTemplate);
Handlebars.registerPartial("dateInput", dateInputTemplate);
Handlebars.registerPartial("booleanInput", booleanInputTemplate);
Handlebars.registerPartial("stringInput", stringInputTemplate);
Handlebars.registerPartial("radioInput", radioInputTemplate);
Handlebars.registerPartial("selectInput", selectInputTemplate);
Handlebars.registerPartial("comboboxInput", comboboxInputTemplate);
Handlebars.registerPartial("textareaInput", textareaInputTemplate);
}
}
22 changes: 0 additions & 22 deletions packages/core/src/codegen/templates/boolean-input.ts

This file was deleted.

8 changes: 0 additions & 8 deletions packages/core/src/codegen/templates/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1 @@
export { mainTemplate } from "./main";
export { stringInputTemplate } from "./string-input";
export { dateInputTemplate } from "./date-input";
export { booleanInputTemplate } from "./boolean-input";
export { selectInputTemplate } from "./select-input";
export { radioInputTemplate } from "./radio-input";
export { comboboxInputTemplate } from "./combobox-input";
export { numberInputTemplate } from "./number-input";
export { textareaInputTemplate } from "./textarea-input";
51 changes: 14 additions & 37 deletions packages/core/src/codegen/templates/main.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
// TODO fix main, add remaining code.

// {{#each fields}}
// {{#if (eq kind "enum")}}
// {{#if (eq style "combobox")}}
// const {{enumName}} = [
// {{#each enumValues}}
// { label: "{{label}}", value: "{{value}}" },
// {{/each}}
// ]
// {{/if}}
// {{/if}}
// {{/each}}
export const mainTemplate = `const formSchema = {{{zodFormSchema}}}
{{#each fields}}
{{#ifEquals kind "enum"}}
{{#ifEquals style "combobox"}}
const {{enumName}} = [
{{#each enumValues}}
{ label: "{{label}}", value: "{{value}}" },
{{/each}}
]
{{/ifEquals}}
{{/ifEquals}}
{{/each}}
export function MyForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
Expand All @@ -25,32 +27,7 @@ export function MyForm() {
<Form {...form}>
<form noValidate onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
{{#each fields}}
{{#ifEquals kind "number"}}
{{> numberInput }}
{{/ifEquals}}
{{#ifEquals kind "enum"}}
{{#ifEquals style "radio"}}
{{> radioInput }}
{{/ifEquals}}
{{#ifEquals style "combobox"}}
{{> comboboxInput }}
{{/ifEquals}}
{{#ifEquals style "select"}}
{{> selectInput }}
{{/ifEquals}}
{{/ifEquals}}
{{#ifEquals kind "date"}}
{{> dateInput this}}
{{/ifEquals}}
{{#ifEquals kind "string"}}
{{> stringInput }}
{{/ifEquals}}
{{#ifEquals kind "boolean"}}
{{> booleanInput this}}
{{/ifEquals}}
{{#ifEquals kind "textarea"}}
{{> textareaInput }}
{{/ifEquals}}
{{{lookupComponent this}}}
{{/each}}
<Button type="submit">Submit</Button>
</form>
Expand Down
23 changes: 23 additions & 0 deletions packages/core/src/codegen/templates/next/boolean/checkbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const checkbox = `
<FormField
control={form.control}
name="{{key}}"
render={({ field }) => (
<FormItem>
<FormControl>
<div className="flex items-center space-x-2">
<ShadcnCheckbox value={field.value}id={f.key} onClick={(e)=>console.log("elick",field.value)} onChange={(e)=>console.log("eeeez",e)} />
<label
htmlFor={f.key}
className="font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{{label}}
</label>
</div>
</FormControl>
<FormDescription>{{description}}</FormDescription>
<FormMessage />
</FormItem>
)}
/>
`;
6 changes: 6 additions & 0 deletions packages/core/src/codegen/templates/next/boolean/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { checkbox } from "./checkbox";
import { switchTemplate } from "./switch";
export const booleanTemplates = {
checkbox,
switchTemplate,
};
18 changes: 18 additions & 0 deletions packages/core/src/codegen/templates/next/boolean/switch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const switchTemplate = `
<FormField
control={form.control}
name="{{key}}"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="">
<FormLabel className="text-base">{{label}}</FormLabel>
<FormDescription>{{description}}</FormDescription>
</div>
<FormControl>
<Switch {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
`;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const dateInputTemplate = `
export const date = `
<FormField
control={form.control}
name="{{key}}"
Expand Down Expand Up @@ -29,15 +29,12 @@ export const dateInputTemplate = `
mode="single"
selected={field.value}
onSelect={field.onChange}
// disabled={(date) =>
// date > new Date() || date < new Date("1900-01-01")
// }
initialFocus
/>
</PopoverContent>
</Popover>
<FormDescription>
{{desc}}
{{description}}
</FormDescription>
<FormMessage />
</FormItem>
Expand Down
Loading

0 comments on commit 2f3b1b1

Please sign in to comment.