Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- "use client";
- import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query";
- import React, { useRef, useState } from "react";
- import { RiArrowDropDownLine } from "react-icons/ri";
- import { RxCross2 } from "react-icons/rx";
- import { useInView } from "react-intersection-observer";
- import Skeleton from "react-loading-skeleton";
- import KlassyImage from "@/components/Admin/common/KlassyImage";
- import UseAxiosClient from "@/hooks/UseAxiosClient";
- import useDebounce from "@/hooks/UseDebounce";
- import { prefixImagePath } from "@/utils/imagePathFinder";
- const OutsideClickHandler = ({ children, onOutsideClick, className }) => {
- const containerRef = useRef();
- React.useEffect(() => {
- const handleOutsideClick = (event) => {
- if (
- containerRef.current &&
- !containerRef.current.contains(event.target)
- ) {
- onOutsideClick();
- }
- };
- document.addEventListener("click", handleOutsideClick);
- return () => {
- document.removeEventListener("click", handleOutsideClick);
- };
- }, [onOutsideClick]);
- return (
- <div className={className} ref={containerRef}>
- {children}
- </div>
- );
- };
- const InfiniteInput = ({
- totalSize = 15,
- queryName,
- apiPrefix,
- selectedValue,
- apiResKeys,
- accessor = ["id"],
- accessorVariation = ["id"],
- placeholder,
- inputClass = "",
- width = "w-4/5",
- height = "max-h-[250px]",
- searchShow,
- debounceTime = 300,
- skeletonHeight = 25,
- isMulti,
- showImage = false,
- maxMulti = Infinity,
- isShowSelectedValue = isMulti,
- zIndex = 10,
- showValue = true,
- defaultValue,
- exchangeOrder = false,
- searchKey = "name",
- defaultMulti = [],
- onRemoveFn,
- }) => {
- const url = UseAxiosClient();
- const [page, setPage] = useState(1);
- const [search, setSearch] = useState("");
- const [openInput, setOpenInput] = useState(false);
- const [selectedName, setSelectedName] = useState("");
- const [onValuePress, setOnValuePress] = useState(false);
- const [multiValue, setMultiValue] = useState(defaultMulti);
- const [selectedItems, setSelectedItems] = useState([]);
- const { ref, inView } = useInView();
- const queryClient = useQueryClient();
- const { debouncedSearch, debounceLoading } = useDebounce(
- search,
- debounceTime
- );
- const { data, fetchNextPage } = useInfiniteQuery({
- queryKey: [queryName, debouncedSearch],
- queryFn: async ({ pageParam = 1 }) => {
- // increase the page dynamically
- const res = await url.get(apiPrefix, {
- params: {
- [searchKey]: debouncedSearch,
- pages: pageParam,
- pageSize: totalSize,
- },
- });
- return res.data;
- },
- getNextPageParam: (lastPage, pages) => {
- return lastPage.hasMore ? pages.length + 1 : undefined;
- },
- getPreviousPageParam: (firstPage) => {
- return firstPage.hasMore ? firstPage : undefined;
- },
- initialPageParam: 1,
- refetchOnWindowFocus: false,
- });
- React.useEffect(() => {
- if (inView && page <= data?.pages[0]["totalPages"]) {
- fetchNextPage();
- setPage((prev) => prev + 1);
- }
- }, [fetchNextPage, inView]);
- React.useEffect(() => {
- setPage(1);
- queryClient.resetQueries({ queryKey: [queryName, debouncedSearch] });
- }, [search, debouncedSearch]);
- const handleRemove = () => {
- setSelectedName("");
- setSearch("");
- selectedValue(null);
- setMultiValue([]);
- setOpenInput(false);
- };
- React.useEffect(() => {
- if (isMulti) {
- selectedValue(multiValue);
- }
- }, [multiValue]);
- const handleButtonClick = (item, isVariation) => {
- setSelectedName(
- !isVariation
- ? exchangeOrder
- ? item.offline_customer_id
- ? item.offline_customers.name
- : item.users.name
- : item[searchShow]
- : `${item?.products?.name} -- ${item?.attributes?.name}:${item?.attribute_values?.name}`
- );
- setOpenInput(false);
- setOnValuePress(true);
- if (!isVariation) {
- let accessorValues = {};
- [...new Set([...accessor, "id"])].forEach((acc) => {
- accessorValues[acc] = item[acc];
- });
- setMultiValue((prev) => [...prev, accessorValues]);
- if (!isMulti) {
- selectedValue(accessorValues);
- }
- setSelectedItems((prev) => [...prev, item]);
- } else if (isVariation) {
- let accessorValues = {};
- [...new Set([...accessorVariation, "id"])].forEach((acc) => {
- accessorValues[acc] = item[acc];
- });
- selectedValue({ ...accessorValues, name: selectedName });
- isMulti && setMultiValue((prev) => [...prev, accessorValues]);
- setSelectedName(null);
- setSelectedItems((prev) => [...prev, item]);
- }
- };
- const handleMultiRemove = (removeValueId) => {
- setMultiValue(multiValue.filter((item) => item.id !== removeValueId));
- setSelectedItems(selectedItems.filter((item) => item.id !== removeValueId));
- if (onRemoveFn) {
- const removedItem = Object.assign(
- {},
- ...multiValue.filter((item) => item.id === removeValueId)
- );
- onRemoveFn(removedItem?.id);
- }
- };
- const mergedItems = selectedItems.concat(
- data?.pages.flatMap((page) => page[apiResKeys] || []).filter((item) => !selectedItems.some((selected) => selected.id === item.id))
- );
- const CloseProps = (
- <button
- onClick={(e) => {
- e.preventDefault();
- handleRemove();
- }}
- className="p-2 bg-white dark:bg-inherit"
- style={{
- position: "absolute",
- right: "1px",
- top: "20px",
- transform: "translateY(-50%)",
- }}
- >
- {selectedName === "" ? (
- <RiArrowDropDownLine />
- ) : (
- <RxCross2 className="text-red-500" />
- )}
- </button>
- );
- return (
- <OutsideClickHandler
- className={`z-${zIndex} m-auto relative ${width}`}
- onOutsideClick={() => setOpenInput(false)}
- >
- <input
- type="text"
- value={
- defaultValue
- ? defaultValue
- : onValuePress && showValue
- ? selectedName
- : search
- }
- onChange={(e) => setSearch(e.target.value)}
- onFocusCapture={() => setOpenInput(true)}
- onKeyDown={() => setOnValuePress(false)}
- placeholder={placeholder}
- className={`${inputClass} border border-gray-300 dark:bg-slate-700 p-2 rounded-md w-full placeholder:text-[15px]`}
- />
- {showValue && CloseProps}
- {openInput && (
- <div>
- <div
- style={{ position: "absolute" }}
- className={`w-full text-sm border-l-2 border-r-2 ${
- mergedItems.length !== 0 && "border-b-2"
- } bg-white dark:bg-slate-700 rounded-md scroll-m-0 scrollbar-thumb-gray-800 scrollbar-track-black overflow-y-auto ${height}`}
- >
- {debounceLoading ? (
- <Skeleton count={totalSize} height={skeletonHeight} />
- ) : mergedItems.length !== 0 ? (
- mergedItems.map((item, index) =>
- item?.is_variant !== 1 ? (
- <button
- className={`text-left ${
- showImage
- ? "grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 justify-start align-middle items-center"
- : ""
- } w-full border-b-[1px] text-gray-500 dark:text-gray-100 cursor-pointer p-2 border-b-gray-300 disabled:text-black-bold disabled:bg-gray-200 mb-1 disabled:cursor-not-allowed`}
- ref={ref}
- key={index}
- onClick={(e) => {
- e.preventDefault();
- handleButtonClick(item);
- // setSelectedName(item[searchShow]);
- }}
- disabled={
- isMulti && multiValue.length >= maxMulti
- ? true
- : isMulti
- ? multiValue.find(
- (multiVal) => multiVal.id === item.id
- )
- : false
- }
- >
- {showImage && (
- <KlassyImage
- src={prefixImagePath(item["thumbnail_img"])}
- className="col-span-1"
- />
- )}
- <p className="col-span-3 sm:col-span-4 md:col-span-5">
- {exchangeOrder
- ? item.offline_customer_id
- ? item.offline_customers.name
- : item.users.name
- : item[searchShow]}
- </p>
- </button>
- ) : (
- item?.product_variations.map((variation) => (
- <button
- className={`text-left ${
- showImage
- ? "grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 justify-start align-middle items-center"
- : ""
- } w-full border-b-[1px] text-gray-500 dark:text-gray-100 cursor-pointer p-2 border-b-gray-300 disabled:text-black-bold disabled:bg-gray-200 mb-1 disabled:cursor-not-allowed`}
- ref={ref}
- key={variation.id}
- onClick={(e) => {
- e.preventDefault();
- handleButtonClick(variation, true);
- // setSelectedName(item[searchShow]);
- }}
- >
- {showImage && (
- <KlassyImage
- src={prefixImagePath(
- variation?.products?.thumbnail_img
- )}
- className="col-span-1"
- />
- )}
- <p className="col-span-3 sm:col-span-4 md:col-span-5">
- {`${variation?.products?.name} -- ${variation?.attributes?.name}:
- ${variation?.attribute_values?.name}`}
- </p>
- </button>
- ))
- )
- )
- ) : (
- <div className="p-2 text-center">
- <h1 className="text-base text-gray-500">
- Not found any{" "}
- <u className="first-letter:capitalize">{searchShow}</u> of{" "}
- <u className="first-letter:capitalize">{apiResKeys}</u>.
- </h1>
- </div>
- )}
- </div>
- </div>
- )}
- {multiValue.length !== 0 && isShowSelectedValue && (
- <div className="border-[1px] mt-2 bg-[#F9F9F9] dark:bg-gray-800 p-2 mb-5 border-dashed rounded-sm border-[#B3B3B3]">
- <div className="flex flex-wrap gap-2">
- {multiValue.map((acc, index) => (
- <button
- className="flex items-center justify-center p-1 bg-[#FFF0F0] rounded-sm w-fit dark:text-black"
- key={index}
- onClick={(e) => e.preventDefault()}
- >
- <p className="text-sm">
- {exchangeOrder
- ? acc.offline_customer_id
- ? acc.offline_customers.name
- : acc.users.name
- : acc[searchShow]}
- </p>
- <RxCross2
- className="ml-1 text-base text-red-500 cursor-pointer"
- onClick={(e) => {
- e.preventDefault();
- handleMultiRemove(acc.id);
- }}
- />
- </button>
- ))}
- </div>
- </div>
- )}
- </OutsideClickHandler>
- );
- };
- export default InfiniteInput;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement