auto-form icon indicating copy to clipboard operation
auto-form copied to clipboard

String Arrays Support

Open bidrang opened this issue 2 years ago • 5 comments

Hi there, First of all thanks for the great tool which you developed. For my case, I need AutoForm to support String Arrays. which unfortunately, I couldn't figure out how to do it.

The field is as below:

highlights: z
      .array(
        z
          .string()
          .describe(
            "e.g. Increased profits by 20% from 2011-2012 through viral advertising"
          )
      )
      .describe("Specify multiple accomplishments")
      .optional(),

bidrang avatar Oct 07 '23 14:10 bidrang

Currently, AutoForm only supports array of objects (https://github.com/vantezzen/auto-form#arrays) as inferring field labels etc. needs to be done another way otherwise. PRs welcome if you want to extend the functionality there!

vantezzen avatar Oct 13 '23 10:10 vantezzen

Thanks Bennett for your response. I've resolved my issue by creating a new INPUT_COMPONENTS and tweaking the AutoForm as needed. However, given the specific nature of my modifications and the number of custom changes involved, submitting a pull request might not be feasible.

bidrang avatar Oct 14 '23 14:10 bidrang

Thanks Bennett for your response. I've resolved my issue by creating a new INPUT_COMPONENTS and tweaking the AutoForm as needed. However, given the specific nature of my modifications and the number of custom changes involved, submitting a pull request might not be feasible.

Hi, can you share the modifications? I have also stumbled upon this problem

salat-23 avatar Oct 27 '23 07:10 salat-23

Hi, can you share the modifications? I have also stumbled upon this problem

For my case I created a Component for Tags listing for my project as blow:

import { useEffect, useRef, useState } from "react";
import { Button } from "./ui/button";
import { XIcon } from "lucide-react";

type Params = {
  onValueChange?: (value: string[]) => void;
  value: string[];
  readonly?: boolean;
};

export function TagsListInput({ value, onValueChange, readonly }: Params) {
  const [tags, setTags] = useState<string[]>();
  const inputRef = useRef(null);

  useEffect(() => {
    if (value) {
      setTags(value);
    }
  }, [value]);

  useEffect(() => {
    if (onValueChange && tags) {
      onValueChange(tags);
    }
  }, [tags]);

  return (
    <>
      {tags &&
        tags.map((item, index) => (
          <Button
            key={"tag-" + index + "item"}
            className="me-1 font-normal text-xs px-2.5 py-0.5 h-auto mb-1"
            variant="secondary"
            onClick={(e) => {
              let temp = tags.filter((str, idx) => idx != index);
              setTags(temp);
              e.preventDefault();
            }}
          >
            {item}
            <XIcon className="ms-2 w-3" />
          </Button>
        ))}
      <input
        type="text"
        placeholder="Enter Keyword"
        className="text-sm px-2.5 py-0.5 border-muted border w-32"
        ref={inputRef}
        onKeyDown={(e) => {
          if (e.key === "Enter" || e.key === "Tab") {
            let temp = tags;
            if (!temp) temp = [];
            if (inputRef.current) {
              temp.push((inputRef.current as HTMLInputElement).value);
              (inputRef.current as HTMLInputElement).value = "";
            }
            setTags([...temp]);
            e.preventDefault();
          }
        }}
      />
    </>
  );
}

And I added an Autoform Field to the Autoform.ts file to support this new field as below:

function AutoFormTagsInput({
  label,
  isRequired,
  field,
  fieldConfigItem,
  fieldProps,
}: AutoFormInputComponentProps) {
  return (
    <FormItem>
      <FormLabel>
        {label}
        {isRequired && <span className="text-destructive"> *</span>}
      </FormLabel>
      <FormControl>
        <TagsListInput
          value={field.value}
          onValueChange={field.onChange}
          {...fieldProps}
        />
      </FormControl>
      {fieldConfigItem.description && (
        <FormDescription>{fieldConfigItem.description}</FormDescription>
      )}
      <FormMessage />
    </FormItem>
  );
}

I added the support for the new component to the INPUT_COMPONENTS type as below:

const INPUT_COMPONENTS = {
 ...
  tags: AutoFormTagsInput,
  textlist: AutoFormTextListInput,
};

And in AutoForm usage I set the FieldConfig as below:

fieldConfig={{
                keywords: { fieldType: "tags" },
              }}

BTW, as I noted Bennett has refactored the project a lot. So, you must figure out how to do this approach on the updated project structure.

I hope it helps.

bidrang avatar Oct 28 '23 09:10 bidrang

this is my implementation for handling primitive arrays. Huge thanks to the projects I forked and to all the other people whose answers I found useful. The list is virtualized, to manage large enums. This is draft code, but it should be sufficient for someone to refine and use. pull my code do yarn install then yarn run dev and you can see it working

https://shadcnui-expansions.typeart.cc/docs/multiple-selector https://github.com/oaarnikoivu/shadcn-virtualized-combobox/tree/main

you can see it in my forked repo

example https://github.com/vozmarkov/auto-form/blob/main/src/examples/ArrayPrimitive.tsx

multi select virtualized and uses popover for content https://github.com/vozmarkov/auto-form/blob/main/src/components/ui/multiple-selector.tsx

autofrom component https://github.com/vozmarkov/auto-form/blob/main/src/components/ui/auto-form/fields/enum-multi-input.tsx

auto from object.tsx array logic https://github.com/vozmarkov/auto-form/blob/7d5df68dcb312b2451e9cc3e6e577ed9aa4739a3/src/components/ui/auto-form/fields/object.tsx#L133

vozmarkov avatar Jul 14 '24 08:07 vozmarkov