onesine / react-tailwindcss-select

Tailwind Select Component for React.js
https://demo-react-tailwindcss-select.vercel.app/
MIT License
183 stars 38 forks source link

Better Typescript support #22

Open rogueturnip opened 1 year ago

rogueturnip commented 1 year ago

Hi! Your project is really great. It works really well for what I need but I did find some issues when trying to properly type my handler.

Basically what I want to is be able to pull out just the values from the saved array so that I can send that to the database for storage. I don't need to carry the label or disabled keys values.

When to do "map" on the data stored it kept telling me that map is to valid on Options. If I set the typing to "any" it works just fine.

This isn't stopping me from getting things working functionally but it just impacts having nicely typed code.

onesine commented 1 year ago

Hi @rogueturnip 👋

Thanks for using this library and thanks also for the feedback.

Normally, if you respect the typing of the Options, there should be no problem.

export interface Option {
    value: string;
    label: string;
    disabled?: boolean;
    isSelected?: boolean;
}

export interface GroupOption {
    label: string;
    options: Option[];
}

export type Options = Array<Option | GroupOption>;

Here is the type of options

rogueturnip commented 1 year ago

Thanks for the response. I did use those to set my types, but the problem I have is that for the to work you have to set your useState<Option | Options | null>(null) it seems (to make the value set work). Then when I do value={JSON.stringify( classes ? classes.map((item: any) => item.value) : [] )} It throws type errors because Option doesn't support map. It would seem to me that if you were to have just Options as the typing (have not multiple just have an array of one item) this would sort out the issue I'm seeing.

Also wondering, any plans to add a "maxItems" to set a limit to the number that can be selected in a multi select?

Again, great component, some of this might just be me implementing but I wanted to mention since the examples are just plain JS. It wouldn't be an issue but my API requires an array of strings.

onesine commented 1 year ago

I can see your code.

rogueturnip commented 1 year ago

Sure. Here is a snip of what's giving me type issues. If I set everything to "any" it works. I posted images of the typing errors from viscose.

import { useState } from "react";
import Select from "react-tailwindcss-select";
import type { Option } from "react-tailwindcss-select/dist/components/type";

const list: Option[] = [
  { value: "car", label: "Car" },
  { value: "truck", label: "Truck" },
  { value: "van", label: "Van" },
];

export default function Index() {
  const [vehicles, setVehicles] = useState<
    Option[] | Option | null | undefined
  >(null);

  const handleChange = (value: Option | Option[] | null) => {
    setVehicles(() => value);
  };

  return (
    <div>
      <div className="flex items-center">
        Vehicles:
        <input
          type="hidden"
          name="races"
          value={JSON.stringify(
            vehicles ? vehicles.map((item: any) => item.value) : []
          )}
        />
        <Select
          isClearable
          primaryColor="blue"
          isMultiple
          value={vehicles}
          onChange={handleChange}
          options={list}
        />
      </div>
    </div>
  );
}

image image

onesine commented 1 year ago

Sorry for the delay, use the Options type for the list constant instead.

For value and onChange

value: Option | Option[] | null;
onChange: (value: Option | Option[] | null) => void;

But you don't have to specify all types. Use the one that suits your situation.

rogueturnip commented 1 year ago

Thanks! Changing the list to Options still has the same 2 type issues above (map and onChange), but it also added the "value" one to not be typed right now.

I'm wondering, do you have a TS example? The "map" really is one that throws for a loop here because "Option" doesn't have map so to use it now I need logic to check for Option or Options.

If this is a structural issue with the component that's fine, I can just go to using "any" and it all works just fine.

import { useState } from "react";
import Select from "react-tailwindcss-select";
import type {
  Option,
  Options,
} from "react-tailwindcss-select/dist/components/type";

const list: Options = [
  { value: "car", label: "Car" },
  { value: "truck", label: "Truck" },
  { value: "van", label: "Van" },
];

export default function Index() {
  const [vehicles, setVehicles] = useState<
    Option[] | Option | null | undefined
  >(null);

  const handleChange = (value: Option | Option[] | null): void => {
    setVehicles(() => value);
  };

  return (
    <div>
      <div className="flex items-center">
        Vehicles:
        <input
          type="hidden"
          name="races"
          value={JSON.stringify(
            vehicles ? vehicles.map((item: any) => item.value) : []
          )}
        />
        <Select
          isClearable
          primaryColor="blue"
          isMultiple
          value={vehicles}
          onChange={handleChange}
          options={list}
        />
      </div>
    </div>
  );
}