Blocks

Form 1

Forms
Text Align Left1 ColumnAvatar ImageForm
Form 1

Code

relume-form1.tsx
"use client";

import { useState } from "react";
import { Button, type ButtonProps } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { Mail, Visibility } from "relume-icons";

type ImageProps = {
  src: string;
  alt?: string;
};

type Props = {
  heading: string;
  description: string;
  buttons: ButtonProps[];
  image: ImageProps;
};

export type Form1Props = React.ComponentPropsWithoutRef<"section"> & Partial<Props>;

export const Form1 = (props: Form1Props) => {
  const { heading, description, buttons, image } = {
    ...Form1Defaults,
    ...props,
  };

  const [photoInput, setPhotoInput] = useState("");
  const [userNameInput, setUserNameInput] = useState("");
  const [websiteInput, setWebsiteInput] = useState("");
  const [emailInput, setEmailInput] = useState("");
  const [aboutInput, setAboutInput] = useState("");
  const [passwordInput, setPasswordInput] = useState("");
  const [newPasswordInput, setNewPasswordInput] = useState("");
  const [languageInput, setLanguageInput] = useState("");

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    console.log({
      photoInput,
      userNameInput,
      websiteInput,
      emailInput,
      aboutInput,
      passwordInput,
      newPasswordInput,
      languageInput,
    });
  };

  const selectItems = [
    { value: "first-choice", label: "First Choice" },
    { value: "second-choice", label: "Second Choice" },
    { value: "third-choice", label: "Third Choice" },
  ];

  return (
    <section>
      <div className="pb-5 md:pb-6">
        <h2 className="text-h5 font-bold">{heading}</h2>
        <p className="mt-2">{description}</p>
      </div>
      <form onSubmit={handleSubmit} className="grid grid-cols-1 gap-6">
        <div className="grid w-full max-w-md grid-cols-1 items-center">
          <Label className="mb-2">Photo</Label>
          <div className="flex items-center gap-6">
            <div className="size-20 shrink-0 overflow-hidden rounded-full">
              <img src={image.src} alt={image.alt} />
            </div>
            <Label htmlFor="photo" className="cursor-pointer border border-scheme-border px-5 py-2">
              Upload photo
              <input
                id="photo"
                type="file"
                className="hidden"
                value={photoInput}
                onChange={(e) => setPhotoInput(e.target.value)}
              />
            </Label>
          </div>
        </div>
        <div className="grid w-full max-w-md grid-cols-1 items-center">
          <Label htmlFor="userName" className="mb-2">
            Username
          </Label>
          <Input
            type="text"
            id="userName"
            value={userNameInput}
            onChange={(e) => setUserNameInput(e.target.value)}
          />
        </div>
        <div className="grid w-full max-w-md grid-cols-1 items-center">
          <Label htmlFor="website" className="mb-2">
            Website
          </Label>
          <Input
            type="text"
            id="website"
            placeholder="www.relume.io"
            prefix="http://"
            value={websiteInput}
            onChange={(e) => setWebsiteInput(e.target.value)}
          />
        </div>
        <div className="grid w-full max-w-md grid-cols-1 items-center">
          <Label htmlFor="email" className="mb-2">
            Email address
          </Label>
          <Input
            type="email"
            id="email"
            value={emailInput}
            icon={<Mail className="size-6 text-scheme-text" />}
            placeholder="hello@relume.io"
            onChange={(e) => setEmailInput(e.target.value)}
          />
        </div>
        <div className="grid w-full max-w-md grid-cols-1 items-center">
          <Label htmlFor="about" className="mb-2">
            About
          </Label>
          <Textarea
            id="about"
            placeholder="Type your message..."
            className="h-auto min-h-[11.25rem] p-3"
            maxLength={263}
            value={aboutInput}
            onChange={(e) => setAboutInput(e.target.value)}
          />
          <p className="mt-2 text-small">263 characters left</p>
        </div>
        <div className="grid w-full max-w-md grid-cols-1 items-center">
          <Label htmlFor="password" className="mb-2">
            Password
          </Label>
          <div className="flex flex-col space-y-4">
            <Input
              type="password"
              id="password"
              value={passwordInput}
              icon={<Visibility className="size-5 text-scheme-text" />}
              iconPosition="right"
              placeholder="Current password"
              onChange={(e) => setPasswordInput(e.target.value)}
            />
            <Input
              type="password"
              id="newPassword"
              value={newPasswordInput}
              icon={<Visibility className="size-5 text-scheme-text" />}
              iconPosition="right"
              placeholder="New password"
              onChange={(e) => setNewPasswordInput(e.target.value)}
            />
          </div>
        </div>
        <div className="grid w-full max-w-md grid-cols-1 items-center">
          <Label className="mb-2">Language</Label>
          <Select onValueChange={setLanguageInput}>
            <SelectTrigger>
              <SelectValue placeholder="Select one..." />
            </SelectTrigger>
            <SelectContent>
              {selectItems.map((item, index) => (
                <SelectItem key={index} value={item.value}>
                  {item.label}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>
        <div className="mt-5 flex items-center justify-end gap-4 md:mt-6">
          {buttons.map((button, index) => (
            <Button key={index} {...button}>
              {button.title}
            </Button>
          ))}
        </div>
      </form>
    </section>
  );
};

export const Form1Defaults: Props = {
  heading: "Account",
  description:
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros.",
  buttons: [
    { title: "Cancel", variant: "secondary", size: "sm" },
    { title: "Save", size: "sm" },
  ],
  image: {
    src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image-small.svg",
    alt: "Relume placeholder image",
  },
};

npm i @hookform/resolvers@^3.10.0 @radix-ui/react-label @radix-ui/react-popover @radix-ui/react-select @radix-ui/react-slot class-variance-authority@0.7.1 clsx@^2.1.1 luxon@^3.5.0 react-day-picker@9.5.0 react-hook-form@^7.62.0 relume-icons@1.3.0 tailwind-merge@^2.2.2 zod@^3.24.1