export function DataTableFilterCommands()

in studio/src/components/analytics/data-table-faceted-filter.tsx [198:523]


export function DataTableFilterCommands<TData, TValue>({
  onSelect,
  selectedOptions,
  title,
  options,
  customOptions,
}: DataTableFacetedFilter<TData, TValue>) {
  const selectedValues = new Set(selectedOptions);
  const [input, setInput] = useState("");
  const [range, setRange] = useState<{ start: number; end: number }>({
    start: 0,
    end: 10,
  });
  let content: React.ReactNode;

  // the options are filtered based on the search input
  const [filteredOptions, setFilteredOptions] = useState(options);
  const [shouldPrefixSearch, setShouldPrefixSearch] = useState(false);
  const [searchValue, setSearchValue] = useState<string | undefined>(undefined);

  useEffect(() => {
    if (
      !selectedOptions ||
      selectedOptions.length === 0 ||
      customOptions !== CustomOptions.Range
    )
      return;
    const option1 = JSON.parse(selectedOptions[0]).value;
    const option2 = JSON.parse(selectedOptions[1]).value;

    const time1 = option1 / 10 ** 9;
    const time2 = option2 / 10 ** 9;
    if (time1 >= time2) {
      setRange({ start: time2, end: time1 });
    } else {
      setRange({ start: time1, end: time2 });
    }
  }, [customOptions, selectedOptions]);

  const updateRangeFilters = ({
    rangeValue,
  }: {
    rangeValue: { start: number; end: number };
  }) => {
    selectedValues.clear();
    setRange({
      start: rangeValue.start,
      end: rangeValue.end,
    });
    selectedValues.add(
      JSON.stringify({
        label: (rangeValue.start * 10 ** 9).toString(),
        value: (rangeValue.start * 10 ** 9).toString(),
        operator: 4,
      }),
    );
    selectedValues.add(
      JSON.stringify({
        label: (rangeValue.end * 10 ** 9).toString(),
        value: (rangeValue.end * 10 ** 9).toString(),
        operator: 5,
      }),
    );
    const filterValues = Array.from(selectedValues);
    onSelect?.(filterValues);
  };

  switch (customOptions) {
    case CustomOptions.Text:
      content = (
        <div className="flex flex-col py-2">
          <p className="px-2 text-xs text-muted-foreground">Custom Input</p>
          <div className="flex items-center gap-x-2 px-2">
            <Input
              value={input}
              onChange={(e) => setInput(e.target.value)}
              placeholder={`Enter ${title}`}
              className="border-none !bg-transparent p-0 focus-visible:ring-0"
            />
            <Button
              size="icon-sm"
              variant="ghost"
              className="flex-shrink-0"
              disabled={!input}
              onClick={() => {
                selectedValues.add(
                  JSON.stringify({
                    label: input,
                    value: input,
                    operator: 0,
                  }),
                );
                const filterValues = Array.from(selectedValues);
                onSelect?.(filterValues);
                setInput("");
              }}
            >
              <PlusCircleIcon className="h-5 w-5" />
            </Button>
          </div>
          <Separator />
          {selectedValues.size > 0 && (
            <>
              <div className="mt-2 flex flex-col px-2">
                {Array.from(selectedValues).map((val) => {
                  const selected = JSON.parse(
                    val,
                  ) as AnalyticsFilter["options"][number];

                  return (
                    <div
                      className="flex w-full items-center justify-between gap-x-4 text-sm"
                      key={selected.value}
                    >
                      <span className="w-full truncate">{selected.label}</span>
                      <Button
                        size="icon-sm"
                        variant="ghost"
                        className="flex-shrink-0 text-muted-foreground"
                        onClick={() => {
                          selectedValues.delete(JSON.stringify(selected));
                          const filterValues = Array.from(selectedValues);
                          onSelect?.(
                            filterValues.length ? filterValues : undefined,
                          );
                        }}
                      >
                        <XCircleIcon className="h-5 w-5" />
                      </Button>
                    </div>
                  );
                })}
              </div>
              <Button
                className="mx-1 mt-2"
                variant="ghost"
                size="sm"
                onClick={() => {
                  onSelect?.(undefined);
                  setInput("");
                }}
              >
                Clear Filters
              </Button>
            </>
          )}
        </div>
      );
      break;
    case CustomOptions.Range:
      content = (
        <div className="flex flex-col py-2">
          <p className="px-2 text-xs text-muted-foreground">{`Select ${title} (seconds)`}</p>
          <SliderWithOptions
            key={`slider-${range.start}-${range.end}`}
            unit="sec"
            defaultRange={{ start: range.start, end: range.end }}
            onValueChange={updateRangeFilters}
          />
          {selectedValues.size > 0 && (
            <Button
              className="mx-1 mt-2"
              variant="ghost"
              size="sm"
              onClick={() => {
                onSelect?.(undefined);
                setInput("");
              }}
            >
              Clear Filters
            </Button>
          )}
        </div>
      );
      break;
    default:
      content = <></>;
      break;
  }

  useEffect(() => {
    if (!searchValue) {
      setFilteredOptions(options);
      return;
    }
    const filtered = options.filter((option) =>
      shouldPrefixSearch
        ? option.label.toLowerCase().startsWith(searchValue.toLowerCase())
        : option.label.toLowerCase().includes(searchValue.toLowerCase()),
    );
    setFilteredOptions(filtered);
  }, [options, searchValue, shouldPrefixSearch]);

  return (
    <Command
      className="w-72"
      filter={shouldPrefixSearch ? prefixFilter : regularFilter}
      key={shouldPrefixSearch ? "prefix" : "regular"}
    >
      {customOptions === undefined && (
        <>
          <div className="relative">
            <CommandInput
              placeholder={title}
              disabled={options.length === 0}
              value={searchValue}
              onValueChange={(value) => {
                setSearchValue(value);
              }}
            />
            <div className="absolute right-[2px] top-[2px]">
              <Tooltip delayDuration={100}>
                <TooltipTrigger>
                  <Toggle
                    size="sm"
                    pressed={shouldPrefixSearch}
                    onPressedChange={(pressed) =>
                      setShouldPrefixSearch(pressed)
                    }
                  >
                    <MdTextRotationNone className="h-4 w-4" />
                  </Toggle>
                </TooltipTrigger>
                <TooltipContent>Prefix Search</TooltipContent>
              </Tooltip>
            </div>
          </div>
          <CommandList>
            <CommandEmpty>No results found.</CommandEmpty>
            {options.length > 0 ? (
              <CommandGroup>
                {options.map((option, index) => {
                  const isSelected = selectedValues.has(option.value);
                  return (
                    <CommandItem
                      key={option.value}
                      onSelect={() => {
                        if (isSelected) {
                          selectedValues.delete(option.value);
                        } else {
                          selectedValues.add(option.value);
                        }
                        const filterValues = Array.from(selectedValues);
                        onSelect?.(
                          filterValues.length ? filterValues : undefined,
                        );
                      }}
                    >
                      <div
                        className={cn(
                          "mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
                          isSelected
                            ? "bg-primary text-primary-foreground"
                            : "opacity-50 [&_svg]:invisible",
                        )}
                      >
                        <CheckIcon className={cn("h-4 w-4")} />
                      </div>
                      {option.icon && (
                        <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
                      )}
                      <span className="truncate">{option.label}</span>
                    </CommandItem>
                  );
                })}
              </CommandGroup>
            ) : (
              <div className="flex w-full items-center justify-center p-4 text-sm">
                No filters.
              </div>
            )}
          </CommandList>
          <>
            <Separator orientation="horizontal" />
            <div className="flex justify-center gap-x-2 pt-1">
              <Button
                variant="ghost"
                className="w-full justify-center text-center"
                onClick={() => {
                  const filterValues = Array.from(selectedValues);
                  onSelect?.(
                    filterValues.length
                      ? [
                          ...filterValues,
                          ...filteredOptions.map((option) => option.value),
                        ]
                      : filteredOptions.map((option) => option.value),
                  );
                }}
                disabled={areAllFilteredOptionsSelected({
                  selectedValues,
                  filteredOptions,
                  options,
                })}
              >
                {areAllFilteredOptionsSelected({
                  selectedValues,
                  filteredOptions,
                  options,
                })
                  ? "Selected All"
                  : "Select All"}
              </Button>

              {selectedValues.size > 0 && (
                <>
                  <Separator orientation="vertical" className="h-8" />
                  <Button
                    variant="ghost"
                    className="w-full justify-center text-center"
                    onClick={() => {
                      onSelect?.(undefined);
                    }}
                  >
                    Clear Selection
                  </Button>
                </>
              )}
            </div>
          </>
        </>
      )}
      {customOptions !== undefined && <>{content}</>}
    </Command>
  );
}