nextui-org / nextui

🚀 Beautiful, fast and modern React UI library.
https://nextui.org
MIT License
22.03k stars 1.55k forks source link

[Feature Request] Autocomplete should have seperate prop for `filterText` #2195

Open pdevito3 opened 10 months ago

pdevito3 commented 10 months ago

Is your feature request related to a problem? Please describe.

Currently, we're restricted to having the autocomplete only be filtered down using the displayed textValue, this is fine for simple autocompletes, but if I have a richer autocomplete item with multiple values shown in the dropdown that should be searchable, I have to either put them all into a single joined string for the display text (not good UI) or only have it be searchable on one value.

For example, say i have a list of orderable items that have a name and code and a list of tests that can be ordered.

export function ManageAccessionTestOrders({
  orderables,
  accessionId,
  testOrders,
}: {
  orderables: OrderablePanelsAndTestsDto | undefined;
  accessionId: string;
  testOrders: TestOrderDto[] | undefined;
}) {
  const orderableItems = (
    dto: OrderablePanelsAndTestsDto
  ): {
    entityId: string;
    orderableCode: string;
    orderableName: string;
    searchString: string;
    isPanel: boolean;
    tests: OrderableTest[];
  }[] => {
    const panelItems = dto.panels.map((panel) => ({
      entityId: panel.id,
      orderableCode: panel.panelCode,
      orderableName: panel.panelName,
      tests: panel.tests,
      isPanel: true,
      searchString: `${panel.panelName} ${panel.panelCode} ${panel.tests
        .map((test) => test.testName)
        .join(" ")}`,
    }));

    const testItems = dto.tests.map((test) => ({
      entityId: test.id,
      orderableCode: test.testCode,
      orderableName: test.testName,
      tests: [test],
      isPanel: false,
      searchString: `${test.testName} ${test.testCode}`,
    }));

    return [...panelItems, ...testItems];
  };

  return (
    <div className="">
      <NewAutocomplete
        placeholder="Search for a panel or test"
        items={orderables ? orderableItems(orderables) : []}
        renderItem={(item) => (
          <AutocompleteItem key={item.entityId} textValue={item.orderableName}>
            <div className="flex items-center justify-between">
              <div className="flex items-center gap-2">
                <div className="flex flex-col">
                  <span className="text-small">{item.orderableName}</span>
                  <span className="text-tiny text-default-400">
                    {item.orderableCode}
                  </span>
                </div>
              </div>
            </div>
          </AutocompleteItem>
        )}
      />
      <div className="flex h-full space-x-12">
        <Orderables orderables={orderables} accessionId={accessionId} />
        <OrdersPlaced testOrders={testOrders} accessionId={accessionId} />
      </div>
    </div>
  );
}

function NewAutocomplete<T>({
  items,
  renderItem,
  placeholder,
  ariaLabel,
}: {
  items: Iterable<T> | undefined;
  renderItem: (item: T) => React.ReactElement<typeof AutocompleteItem>;
  placeholder: string;
  ariaLabel?: string;
}) {
  return (
    <Autocomplete
      defaultItems={items}
      placeholder={placeholder}
      aria-label={ariaLabel ?? placeholder}
      startContent={
        <SearchIcon className="text-default-400" strokeWidth={2.5} size={20} />
      }
    >
      {(item: T) => renderItem(item)}
    </Autocomplete>
  );
}

Describe the solution you'd like

I'd propose adding a new optional property called filterValue on AutoCompleteItem -- if it is not provided, then you use the textValue like you do today, but if filterValue is provided, then use that value to filter on instead while having a distinct textValue for display on selection.

<NewAutocomplete
        placeholder="Search for a panel or test"
        items={orderables ? orderableItems(orderables) : []}
        renderItem={(item) => (
          <AutocompleteItem key={item.entityId} textValue={item.orderableName} filterValue={item.searchString}>
            <div className="flex items-center justify-between">
              <div className="flex items-center gap-2">
                <div className="flex flex-col">
                  <span className="text-small">{item.orderableName}</span>
                  <span className="text-tiny text-default-400">
                    {item.orderableCode}
                  </span>
                </div>
              </div>
            </div>
          </AutocompleteItem>
        )}
      />

Describe alternatives you've considered

My only options right now are

  1. concat all the values i want to be filterable into textValue which looks like crap
  2. put whatever i want to have display in textvalue and deal with the limitation that i can only filter on that

Screenshots or Videos

No response

user72356 commented 2 months ago

I second this request