BelkacemYerfa / shadcn-extension

An open source component collection , that extends your ui library , built using shadcn component
https://shadcn-extension.vercel.app/
MIT License
917 stars 36 forks source link

Feedback #50

Closed lenocin closed 2 months ago

lenocin commented 6 months ago

Since my "key" is a database Id, it wasn't feasable to use the component as is (I certainly did not want to display db ids as the selected items)

So made this change which does the trick for me:

interface Option {
  value: string;
  label: string; 
}
interface MultiSelectorTriggerProps
  extends React.HTMLAttributes<HTMLDivElement> {
  options: Option[];
}
const MultiSelectorTrigger = forwardRef<
  HTMLDivElement,
  MultiSelectorTriggerProps
>(({ className, children, options, ...props }, ref) => {
  const { value, onValueChange, activeIndex } = useMultiSelect();

  const mousePreventDefault = useCallback((e: React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
  }, []);

  return (
    <div
      ref={ref}
      className={cn(
        'flex flex-wrap gap-1 p-1 py-2 border border-muted rounded-lg bg-background',
        className
      )}
      {...props}
    >
      {value.map((item, index) => (
        <Badge
          key={item}
          className={cn(
            'px-1 rounded-xl flex items-center gap-1',
            activeIndex === index && 'ring-2 ring-muted-foreground '
          )}
          variant={'secondary'}
        >
          <span className="text-xs">
            {options.find((option) => option.value === item)?.label}
          </span>
          <button
            aria-label={`Remove ${item} option`}
            aria-roledescription="button to remove option"
            type="button"
            onMouseDown={mousePreventDefault}
            onClick={() => onValueChange(item)}
          >
            <span className="sr-only">Remove {item} option</span>
            <RemoveIcon className="h-4 w-4 hover:stroke-destructive" />
          </button>
        </Badge>
      ))}
      {children}
    </div>
  );
});
BelkacemYerfa commented 6 months ago

Hi @lenocin .

Thanks for you feedback, if I understand correctly you want a way to support a type of Options[] instead of having the string[].

But I wanted to ask you of why like you want to use it like that , you can totally use the id for db as a key and the value gonna be whatever value you have in the returned array that you have.

jakubjenis commented 5 months ago

@BelkacemYerfa I had exactly the same problem. If I want to change both the label inside the dropdown when selecting new elements and label inside the Badge, I need to modify the component like @lenocin suggests.

It does not make much sense to not have the text inside the appear in the Badge after click. Very common use case is binding to { "value":id, "label": name} but the id not appearing anywhere visually, only as a form value. Don't know how to achieve this in your version of the component.

Otherwise, I like it the best from all the shadcn based multiselect components, good job!

BelkacemYerfa commented 5 months ago

Hi @jakubjenis

Thanks for your feedback, i understand the current issue that you're facing now. We gonna introduce the fix in the upcoming release.

jakubjenis commented 5 months ago

Great stuff!

gabrielmadruga commented 3 months ago

Another option is to pass a Record<string, string> with the value to label mapping:

...
const MultiSelectorTrigger = forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement> & { valueLabels: Record<string, string> }
>(({ className, children, valueLabels, ...props }, ref) => {
...
...
<span className="text-xs">{valueLabels[item]}</span>
...
<span className="sr-only">Remove {valueLabels[item]} option</span>
...

This is better that doing the find for every value.