Create form component for drag-drop in Atri project
A form component can have different kinds of form controls such as input, radio and checkbox etc. For a more detailed example, you can see the code here. A minimal boilerplate to start can be found here.
Create manifest
In the manifest, notice the custom
field, it has a fields
key that uses array_typed_map
. The array_typed_map
will allow your users to pick the type of children component the user want inside the form. In this example, we are adding text, number, password and email.
// file: CustomForm/CustomForm.manifest.tsx
import { createComponentManifest } from "@atrilabs/utils";
export default createComponentManifest({
name: "CustomForm",
// add typography related options
styles: {
typographyOptions: true,
},
custom: {
fields: {
type: "array_typed_map",
attributes: [
{
fieldName: "text",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "placeholder", type: "text" },
],
},
{
fieldName: "number",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "placeholder", type: "text" },
],
},
{
fieldName: "password",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "placeholder", type: "text" },
],
},
{
fieldName: "email",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "placeholder", type: "text" },
],
},
],
},
},
});
Create a type file
// file: CustomForm/types.ts
export type CommonFormControlOptions = {
label?: string;
id?: string;
placeholder?: string;
};
Create form React component
// file: CustomForm/CustomForm.tsx
import React from "react";
import { CommonFormControlOptions } from "./types";
import { Text } from "./components/Text";
const Form = React.forwardRef<
HTMLFormElement,
{
className: string;
id: string;
styles: React.CSSProperties;
attrs: { class: string };
custom: {
fields: {
selectedOption:
| "none"
| "text"
| "number"
| "password"
| "email";
text: CommonFormControlOptions;
password: CommonFormControlOptions;
email: CommonFormControlOptions;
number: CommonFormControlOptions;
}[];
};
}
>((props, ref) => {
return (
<form
ref={ref}
className={`${props.className} ${props.attrs?.class || ""}`}
id={props.id}
style={props.styles}
>
</form>
);
});
export default Form;
Create form control React components
// file: CustomForm/components/Text.tsx
import { CommonFormControlOptions } from "../types";
export const Text = (props?: CommonFormControlOptions & { key: string }) => {
return (
<div>
<label htmlFor={props?.id}>{props?.label}</label>
<input {...props} key={props?.key} name={props?.id} type="text" />
</div>
);
};
Add form control to form component
// file: CustomForm/CustomForm.tsx
import React from "react";
import { CommonFormControlOptions } from "./types";
import { Text } from "./components/Text";
const Form = React.forwardRef<
HTMLFormElement,
{
className: string;
id: string;
styles: React.CSSProperties;
attrs: { class: string };
custom: {
fields: {
selectedOption:
| "none"
| "text"
| "number"
| "password"
| "email";
text: CommonFormControlOptions;
password: CommonFormControlOptions;
email: CommonFormControlOptions;
number: CommonFormControlOptions;
}[];
};
}
>((props, ref) => {
return (
<form
ref={ref}
className={`${props.className} ${props.attrs?.class || ""}`}
id={props.id}
style={props.styles}
>
{/** Add different types of input widgets */}
{props.custom.fields.map((field, index) => {
if (field.selectedOption === "text") {
return <Text {...field.text} key={`${props.id}${index}`} />;
}
return <div></div>;
})}
</form>
);
});
export default Form;
Other common form control types
For adding more input types, you have to make changes in CustomForm/CustomForm.manifest.tsx
and CustomForm/CustomForm.tsx
// file: CustomForm/CustomForm.manifest.tsx
import { createComponentManifest } from "@atrilabs/utils";
export default createComponentManifest({
name: "CustomForm",
// add typography related options
styles: {
typographyOptions: true,
},
custom: {
fields: {
type: "array_typed_map",
attributes: [
{
fieldName: "text",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "placeholder", type: "text" },
],
},
{
fieldName: "number",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "placeholder", type: "text" },
],
},
{
fieldName: "password",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "placeholder", type: "text" },
],
},
{
fieldName: "radio",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{
fieldName: "options",
type: "json",
schema: Joi.array()
.unique()
.items(
Joi.object({
value: Joi.string().required(),
label: Joi.string().required(),
})
)
.id("radio"),
},
],
},
{
fieldName: "checkbox",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{
fieldName: "options",
type: "json",
schema: Joi.array()
.unique()
.items(
Joi.object({
value: Joi.string().required(),
label: Joi.string().required(),
})
)
.id("checkbox"),
},
],
},
{
fieldName: "color",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
],
},
{
fieldName: "date",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
],
},
{
fieldName: "datetimeLocal",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
],
},
{
fieldName: "email",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "placeholder", type: "text" },
],
},
{
fieldName: "time",
type: "map",
attributes: [
{ fieldName: "label", type: "text" },
{ fieldName: "id", type: "text" },
],
},
{
fieldName: "url",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "placeholder", type: "text" },
],
},
{
fieldName: "search",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "placeholder", type: "text" },
],
},
{
fieldName: "file",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "multiple", type: "boolean" },
],
},
{
fieldName: "select",
type: "map",
attributes: [
{ fieldName: "id", type: "text" },
{ fieldName: "label", type: "text" },
{ fieldName: "multiple", type: "boolean" },
{
fieldName: "options",
type: "json",
schema: Joi.array()
.unique()
.items(
Joi.object({
value: Joi.string().required(),
label: Joi.string().required(),
})
)
.id("select"),
},
],
},
],
},
},
});