= ({
5 | label,
6 | checked,
7 | onChange,
8 | }) => (
9 |
10 | onChange(e.target.checked)}
14 | className="w-4 h-4 rounded bg-zinc-800 min-h-[16px] accent-zinc-800"
15 | aria-label={label}
16 | />
17 |
20 |
21 | );
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "builder-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@builder.io/dev-tools": "^1.3.9",
13 | "@builder.io/react": "^8.0.4",
14 | "@builder.io/sdk": "^6.0.2",
15 | "next": "15.1.4",
16 | "react": "^19.0.0",
17 | "react-dom": "^19.0.0"
18 | },
19 | "devDependencies": {
20 | "@eslint/eslintrc": "^3",
21 | "@types/node": "^20",
22 | "@types/react": "^19",
23 | "@types/react-dom": "^19",
24 | "eslint": "^9",
25 | "eslint-config-next": "15.1.4",
26 | "postcss": "^8",
27 | "tailwindcss": "^3.4.1",
28 | "typescript": "^5"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Geist, Geist_Mono } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const geistSans = Geist({
6 | variable: "--font-geist-sans",
7 | subsets: ["latin"],
8 | });
9 |
10 | const geistMono = Geist_Mono({
11 | variable: "--font-geist-mono",
12 | subsets: ["latin"],
13 | });
14 |
15 | export const metadata: Metadata = {
16 | title: "Create Next App",
17 | description: "Generated by create next app",
18 | };
19 |
20 | export default function RootLayout({
21 | children,
22 | }: Readonly<{
23 | children: React.ReactNode;
24 | }>) {
25 | return (
26 |
27 |
30 | {children}
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/src/mappings/FormShipping.mapper.tsx:
--------------------------------------------------------------------------------
1 | import { figmaMapping, type BaseFigmaProps } from "@builder.io/dev-tools/figma";
2 | import { ShippingForm } from "@/components/shipping/ShippingForm";
3 |
4 | // ❖ Form Shipping
5 | interface FigmaFormShippingProps extends BaseFigmaProps {
6 | Title?: string;
7 | SubTitle?: string;
8 | }
9 |
10 | // Read more at https://www.builder.io/c/docs/mapping-functions
11 | figmaMapping({
12 | componentKey: "0ce3612fd8600e5fc29e790c18a3366d60dfcfba",
13 | mapper(figma: FigmaFormShippingProps) {
14 | return (
15 |
23 | );
24 | },
25 | });
26 |
--------------------------------------------------------------------------------
/src/components/shipping/InputField.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { InputFieldProps } from "./types";
3 |
4 | export const InputField: React.FC = ({
5 | label,
6 | value,
7 | onChange,
8 | className = "",
9 | }) => (
10 |
11 |
17 | onChange(e.target.value)}
22 | className="overflow-hidden flex-1 shrink self-stretch px-4 py-3 mt-2 w-full leading-none whitespace-nowrap bg-white rounded-lg border border-solid border-zinc-300 min-w-[240px]"
23 | aria-label={label}
24 | />
25 |
26 | );
27 |
--------------------------------------------------------------------------------
/src/builder-registry.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { builder, Builder } from "@builder.io/react";
3 | import { ShippingForm } from "./components/shipping/ShippingForm";
4 |
5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!);
6 |
7 | Builder.registerComponent(ShippingForm, {
8 | name: "ShippingForm",
9 | inputs: [
10 | {
11 | name: "submitButtonText",
12 | type: "string",
13 | required: true,
14 | },
15 | {
16 | name: "subtitle",
17 | type: "string",
18 | required: true,
19 | },
20 | {
21 | name: "termsLinkText",
22 | type: "string",
23 | required: true,
24 | },
25 | {
26 | name: "termsText",
27 | type: "string",
28 | required: true,
29 | },
30 | {
31 | name: "termsUrl",
32 | type: "string",
33 | required: true,
34 | },
35 | {
36 | name: "title",
37 | type: "string",
38 | required: true,
39 | },
40 | ],
41 | });
42 |
--------------------------------------------------------------------------------
/src/components/shipping/types.ts:
--------------------------------------------------------------------------------
1 | export interface ShippingFormData {
2 | fullName: string;
3 | location: string;
4 | deliveryNote: string;
5 | termsAccepted: boolean;
6 | }
7 |
8 | export interface InputFieldProps {
9 | label: string;
10 | value: string;
11 | onChange: (value: string) => void;
12 | className?: string;
13 | }
14 |
15 | export interface LocationSelectProps extends Omit {
16 | value: string;
17 | options: Array<{ value: string; label: string }>;
18 | }
19 |
20 | export interface TextAreaFieldProps extends InputFieldProps {
21 | minHeight?: string;
22 | iconSrc: string;
23 | }
24 |
25 | export interface CheckboxProps {
26 | label: string;
27 | checked: boolean;
28 | onChange: (checked: boolean) => void;
29 | }
30 |
31 | export interface ShippingFormProps {
32 | title: string;
33 | subtitle: string;
34 | submitButtonText: string;
35 | termsText: string;
36 | termsLinkText: string;
37 | termsUrl: string;
38 | }
39 |
--------------------------------------------------------------------------------
/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/figma-imports/page.tsx:
--------------------------------------------------------------------------------
1 | import { builder } from "@builder.io/sdk";
2 | import { RenderBuilderContent } from "../../components/builder";
3 |
4 | // Builder Public API Key set in .env file
5 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!);
6 |
7 | interface PageProps {
8 | params: Promise<{
9 | page: string[];
10 | }>;
11 | }
12 |
13 | export default async function Page(props: PageProps) {
14 | const builderModelName = "figma-imports";
15 |
16 | const content = await builder
17 | // Get the page content from Builder with the specified options
18 | .get(builderModelName, {
19 | userAttributes: {
20 | // Use the page path specified in the URL to fetch the content
21 | urlPath: "/" + ((await props?.params)?.page?.join("/") || ""),
22 | },
23 | })
24 | // Convert the result to a promise
25 | .toPromise();
26 |
27 | return (
28 | <>
29 | {/* Render the Builder page */}
30 |
31 | >
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/builder.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { ComponentProps } from "react";
3 | import { BuilderComponent, useIsPreviewing } from "@builder.io/react";
4 | import { BuilderContent, builder } from "@builder.io/sdk";
5 | import DefaultErrorPage from "next/error";
6 | import "../builder-registry";
7 |
8 | type BuilderPageProps = ComponentProps;
9 |
10 | // Builder Public API Key set in .env file
11 | builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!);
12 |
13 | export function RenderBuilderContent({ content, model }: BuilderPageProps) {
14 | // Call the useIsPreviewing hook to determine if
15 | // the page is being previewed in Builder
16 | const isPreviewing = useIsPreviewing();
17 | // If "content" has a value or the page is being previewed in Builder,
18 | // render the BuilderComponent with the specified content and model props.
19 | if (content || isPreviewing) {
20 | return ;
21 | }
22 | // If the "content" is falsy and the page is
23 | // not being previewed in Builder, render the
24 | // DefaultErrorPage with a 404.
25 | return ;
26 | }
27 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/shipping/TextAreaField.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { TextAreaFieldProps } from "./types";
3 |
4 | export const TextAreaField: React.FC = ({
5 | label,
6 | value,
7 | onChange,
8 | iconSrc,
9 | minHeight = "80px",
10 | className = "",
11 | }) => (
12 |
13 |
19 |
37 |
38 | );
39 |
--------------------------------------------------------------------------------
/src/components/shipping/LocationSelect.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { LocationSelectProps } from "./types";
3 |
4 | export const LocationSelect: React.FC = ({
5 | label,
6 | value,
7 | options,
8 | onChange,
9 | className = "",
10 | }) => (
11 |
14 |
20 |
21 |
34 |
43 |
44 |
45 | );
46 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { ShippingForm } from "@/components/shipping/ShippingForm";
2 | import Image from "next/image";
3 |
4 | export default function Home() {
5 | return (
6 |
7 |
8 |
9 |
17 |
18 |
19 |
66 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/shipping/ShippingForm.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import * as React from "react";
3 | import { InputField } from "./InputField";
4 | import { LocationSelect } from "./LocationSelect";
5 | import { TextAreaField } from "./TextAreaField";
6 | import { Checkbox } from "./Checkbox";
7 | import { ShippingFormProps, ShippingFormData } from "./types";
8 |
9 | const US_CITIES = [
10 | { value: "nyc", label: "New York City, NY" },
11 | { value: "la", label: "Los Angeles, CA" },
12 | { value: "chicago", label: "Chicago, IL" },
13 | { value: "houston", label: "Houston, TX" },
14 | { value: "phoenix", label: "Phoenix, AZ" },
15 | { value: "philadelphia", label: "Philadelphia, PA" },
16 | { value: "san-antonio", label: "San Antonio, TX" },
17 | { value: "san-diego", label: "San Diego, CA" },
18 | { value: "dallas", label: "Dallas, TX" },
19 | { value: "san-jose", label: "San Jose, CA" },
20 | ];
21 |
22 | export const ShippingForm: React.FC = ({
23 | title,
24 | subtitle,
25 | submitButtonText,
26 | termsText,
27 | termsLinkText,
28 | termsUrl,
29 | }) => {
30 | const [formData, setFormData] = React.useState({
31 | fullName: "",
32 | location: US_CITIES[0].value,
33 | deliveryNote: "",
34 | termsAccepted: false,
35 | });
36 |
37 | const handleSubmit = (e: React.FormEvent) => {
38 | e.preventDefault();
39 | console.log(formData);
40 | };
41 |
42 | const updateField = (
43 | field: K,
44 | value: ShippingFormData[K]
45 | ) => {
46 | setFormData((prev) => ({ ...prev, [field]: value }));
47 | };
48 |
49 | return (
50 |
110 | );
111 | };
112 |
--------------------------------------------------------------------------------