hasanharman / form-builder

A dynamic form-building tool that allows users to create, customize, and validate forms seamlessly within web applications.
https://www.shadcn-form.com/
GNU Affero General Public License v3.0
1.34k stars 135 forks source link

Consider grid layout as part of the roadmap #1

Closed dvirben123 closed 2 weeks ago

dvirben123 commented 1 month ago

Hi Nice Job for the form builder :) It will be nice to have a grid layout with TailwincCSS so that you can have a custom layout for the components' positions, Also, it will be nice to have their slider with range. Thanks

hasanharman commented 1 month ago

Noted! Do you have any examples in terms of UX? Since we mostly developers I can open the classnames or layout button like figma?

Slider will be added soon

dvirben123 commented 1 month ago

Noted! Do you have any examples in terms of UX? Since we mostly developers I can open the classnames or layout button like figma?

Slider will be added soon

image

I am struggling to make this design to be enough flexible in my form generator, hope that you will succeed to make it works

hasanharman commented 1 month ago

Perfect example. I understand now. I will add column button. I think we also need heading or text fields to name the sections.

hasanharman commented 1 month ago

@dvirben123 I added a slider component today and changed the layout. What do you think?

dvirben123 commented 1 month ago

It looks good, it can be also nice if you can drag the items into rows and columns, kind of grid with titles and borders. I can share with you what I have built until now, I had some blockers but I am open to using something more generic that the I can build the forms like the design I sent earlier. I generated the components according to the zod schema with some additional fields (for the onchange that can effect other fields in the form)

dvirben123 commented 1 month ago

maybe using it: https://react-grid-layout.github.io/react-grid-layout/examples/0-showcase.html , can help to define the layout of the form

hasanharman commented 1 month ago

@dvirben123 Great great example :) I need to do some experiments. Thank you very much indeed for your time.

I have one more question in my mind. I wonder what you think. This tool was developed entirely for web developers, but when I checked the website traffic, half of the people accessed it from mobile.

I deliberately under-coded the mobile side a few times and started receiving direct messages.

How can this grid structure be made on mobile? Wouldn't that be more challenging?

dvirben123 commented 1 month ago

yes, it will be more challenging, for mobile I am not so sure I didn't test it.

hasanharman commented 1 month ago

@dvirben123 I managed to create a grid pattern. There are some little issues with resizing. I will update this week. Thank you for the suggesstion

hasanharman commented 1 month ago

@dvirben123 I push the update. Also, update the video can you check when you are free? I am waiting your feedbacks

dvirben123 commented 1 month ago

@hasanharman it looks great! I think you need to add max-width or use flex-wrap to save the max width of the form. image

also, after adding a component to existing row, and then dragging it to row below, it duplicate the row : image

Also, it can be nice to have more types of input (email, number and etc. you can add it to the input edit pop up mode). And last thing, as we already spoke about it, adding sections with titles and borders will be great, also the option to do a wizard (or multi-step form) form can be also nice. and maybe we can save some code by removing the FormField and use "register(name)" in order to make the form a little bit cleaner, this is of course my opinion, for example phone component (using 'useFormContext) :

`'use client';
import PhoneInput from 'react-phone-input-2';
import 'react-phone-input-2/lib/style.css';
import * as React from 'react';
import { useFormContext } from 'react-hook-form';
import { getNestedError } from '@/lib/utils';

export const PhoneField = ({
  name = '',
  label = '',
  variant = 'default',
  defaultValue = ''
}) => {
  const {
    register,
    formState: { errors, touchedFields },
    getValues,
    setValue,
    trigger
  } = useFormContext();

  // Initialize phone input with default value
  React.useEffect(() => {
    register(name); // Register the field
    const currentDefaultValue = getValues(name) || defaultValue; // Get default value from form context or prop
    if (currentDefaultValue) {
      setValue(name, currentDefaultValue); // Set the default value on init
    }
  }, [register, name, setValue, defaultValue, getValues]);

  const fieldError: any = getNestedError(name, errors);
  const isTouched = touchedFields[name]; // To determine if the field has been touched

  return (
    <div className="font-body-12-regular-125 flex w-full flex-col items-start justify-start gap-2 text-left text-sm text-grey-500">
      {label && (
        <div className="relative self-stretch font-general-sans">{label}</div>
      )}
      <div className="w-full">
        <PhoneInput
          inputStyle={{
            width: '100%',
            height: '40px',
            borderColor: fieldError ? 'red' : '#e2e8f0', // Dynamically set the border color
            borderWidth: '1px',
            borderStyle: 'solid',
            background: '#FAFBFB'
          }}
          buttonStyle={{
            borderColor: fieldError ? 'red' : '#e2e8f0' // Dynamically set border color for dropdown
          }}
          containerStyle={{ width: '100%' }} // Ensure container takes full width
          country={'us'}
          onlyCountries={['us']}
          value={getValues(name) || ''}
          disableDropdown={false} // Enable dropdown
          onChange={(phone) => {
            setValue(name, phone);
            trigger(name); // Trigger validation on change to update errors dynamically
          }}
        />
      </div>
      <input
        type="hidden"
        {...register(name)} // Registering phone input field
      />
      {fieldError && (
        <span className="text-xs text-red-600">{fieldError?.message}</span>
      )}
    </div>
  );
};
` 

In that case you will have just the phone component under the form element of react hook form. But, in overall I think you made a great job there :)