Skip to content

Commit

Permalink
- fix and improve filter in dropdown filler
Browse files Browse the repository at this point in the history
  • Loading branch information
aswinshenoy committed Mar 30, 2024
1 parent 5f48e3e commit 43cddcb
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 37 deletions.
40 changes: 21 additions & 19 deletions src/components/DataTableManager/filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ export type DataTableFilterConfig = {
};

const DataTableManagerFilters = ({
filterConfig, filters, setFilters = () => {}, showFilters = false,
filterConfig, filters, setFilters = () => {},
} : {
filterConfig: DataTableFilterConfig[],
filters?: FilterInputs,
setFilters?: (f: FilterInputs) => void,
showFilters?: boolean,
}) => {

const [lastFetchKeyword, setFetchKeyword] = useState<string | null>(null);
const [cachedOptions, setCachedOptions] = useState<{ [key: string]: { label: string, value: string }[] }>({});
const [allOptions, setAllOptions] = useState<{ [key: string]: { label: string, value: string }[] }>({
...filterConfig.reduce((acc, f) => ({ ...acc, [f.key]: [] }), {}),
});

const getOptions = (f: DataTableFilterConfig) => {
if (typeof f.onFetch === 'function')
return cachedOptions[f.key] || [];
return allOptions[f.key] || [];
if (f.options) return f.options ?? [];
return [];
};
Expand All @@ -57,32 +57,34 @@ const DataTableManagerFilters = ({
), [filterConfig]);

const fetch = async (key: string, keyword: string, onFetch: (keyword: string) => Promise<{ label: string, value: string }[]>) => {
if (lastFetchKeyword === keyword)
return cachedOptions[key];

const options = await onFetch(keyword);

setCachedOptions({ ...cachedOptions, [key]: options });
setFetchKeyword(keyword);
setAllOptions((prev) => ({
...prev,
[key]: [...new Set([
...(prev[key] || []),
...(filterConfig.find((f) => f.key === key)?.options ?? []),
...options,
])],
}));

return options;
};

return showFilters ? (
return (
<div
className={clsx([
'dark:bg-gray-500/20 bg-gray-500/10 border dark:border-neutral-500/70 border-neutral-500/10',
'mx-2 mb-2 shadow-inner rounded-lg p-2',
])}
>
<div className="flex items-center flex-wrap gap-3">
<div className="flex items-center flex-wrap gap-1 w-full">
{filterConfig.filter((f) =>
(typeof f.onFetch === 'function' || f.options?.length) && !f.isHidden,
).map((f) => {
const optionsLength = getOptions(f).length;
const isFiltered = (
filters?.[f.key] && filters?.[f.key]?.length &&
(optionsLength < 2 || (optionsLength > 1 && (filters?.[f.key]?.length < optionsLength)))
(optionsLength < 2 || (optionsLength > 1 && (filters?.[f.key]?.length < optionsLength)))
);
return (
<div key={f.key} className="p-1">
Expand All @@ -91,7 +93,7 @@ const DataTableManagerFilters = ({
isAsync={typeof f.onFetch === 'function'}
onFetch={(keyword) =>
typeof f.onFetch === 'function' ? fetch(f.key, keyword, f.onFetch) : Promise.resolve([])
}
}
labels={{
searchLabel: f.labels?.searchLabel,
optionsTitle: f.labels?.optionsTitle,
Expand All @@ -114,15 +116,15 @@ const DataTableManagerFilters = ({
})}
</div>
{(isFilteredView) ? (
<div className="flex items-center flex-wrap gap-2 mt-1 border-t pt-2">
<div className="flex items-center flex-wrap gap-2 mt-2 border-t dark:border-gray-500/70 border-gray-500/10 pt-2">
{filterConfig.filter((f) => {
const optionsLength = getOptions(f).length;
return (
filters?.[f.key] && filters?.[f.key]?.length &&
(optionsLength < 2 || (optionsLength > 1 && (filters?.[f.key]?.length < optionsLength)))
);
}).map((f) => (
<div className="md:flex items-center pb-3 pt-1" key={f.key}>
<div className="md:flex items-center px-1 pb-3 pt-1" key={f.key}>
{(f.labels && f.labels.label && f.labels.label?.length > 0) ? (
<div className="md:mr-2 mb-1 md:mb-0 text-sm font-semibold">
{`${f.labels?.label}:`}
Expand All @@ -139,7 +141,7 @@ const DataTableManagerFilters = ({
[f.key]: filters?.[f.key]?.filter((_s: any) => _s !== s),
})}
>
{filterConfig?.find((c) => f.key === c.key)?.options?.find((o) => o.value === s)?.label ?? s}
{allOptions[f.key]?.find((o) => o.value === s)?.label ?? s}
<i className="ri-close-line" title="cancel" />
</Button>
))}
Expand All @@ -148,7 +150,7 @@ const DataTableManagerFilters = ({
</div>
) : null}
</div>
) : <div />;
);

};

Expand Down
23 changes: 19 additions & 4 deletions src/components/DataTableManager/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ const DataTableManager = ({
/>
) : null}
<div className="flex items-center gap-3 p-2">
{(filters && !showFilters) && (
{(filters && !showFilters) ? (
<Button
type="button"
variant="minimal"
Expand All @@ -169,7 +169,23 @@ const DataTableManager = ({
>
<i className="ri-filter-line text-lg" title="filter" />
</Button>
)}
) : filters && showFilters ? (
<Button
variant="minimal"
color="shade"
className="px-2 py-0.5"
onClick={() => {
setFilters({
...filters,
...Object.fromEntries((filterConfig || []).map((f) => [f.key, []])),
});
setShowFilters(false);
}}
title="Clear Filters"
>
<i className="ri-filter-off-line text-lg" title="close" />
</Button>
) : null}
{(columns && columns?.length > 0) ? (
<DropdownFilter
align="end"
Expand Down Expand Up @@ -221,12 +237,11 @@ const DataTableManager = ({
)}
</div>
</div>
{filterConfig && filterConfig?.length > 0 ? (
{showFilters && filterConfig && filterConfig?.length > 0 ? (
<DataTableManagerFilters
filterConfig={filterConfig}
filters={filters}
setFilters={setFilters}
showFilters={showFilters}
/>
) : null}
{(selections && (selections?.selected || selections.excluded)) ? (
Expand Down
24 changes: 10 additions & 14 deletions src/components/DropdownFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import React, { ReactElement, useEffect, useRef, useState } from 'react';
import { nanoid } from 'nanoid';
import clsx from 'clsx';
import _ from 'lodash';
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';

import mcs from '../utils/merge';
Expand Down Expand Up @@ -131,9 +130,9 @@ const DropdownRender = ({
hideLabel
autoFocus
keyword={keyword}
inputClassName="py-1 px-2 !border-0 !rounded-b-none !border-b !bg-transparent !border-neutral-200/50"
buttonClassName="p-1 !border-none !outline-none !rounded-b-none !bg-transparent"
buttonWrapperClassName="!border-0 !outline-none !rounded-b-none !border-b !bg-transparent !border-neutral-200/50"
inputClassName="py-1 px-2 border-none rounded-b-none border-b bg-transparent dark:border-gray-500/70 border-gray-500/10"
buttonClassName="py-1 px-2 border-none outline-none rounded-b-none bg-transparent"
buttonWrapperClassName="border-none outline-none rounded-b-none border-b bg-transparent dark:border-gray-500/70 border-gray-500/10"
onKeyDown={handleKeyDown}
labels={{
placeholder: labels.searchPlaceholder,
Expand All @@ -143,15 +142,15 @@ const DropdownRender = ({
/>
</div>
{labels.optionsTitle?.length > 0 ? (
<div className="w-full px-2 py-1 bg-background border-b border-neutral-200/50">
<div className="w-full px-2 py-1 bg-background border-b dark:border-gray-500/70 border-gray-500/10">
<span className="opacity-80 uppercase font-semibold text-xs">
{labels.optionsTitle}
</span>
</div>
) : null}
<div
className={clsx([
!(labels.optionsTitle?.length > 0) && 'border-t border-neutral-200/50',
!(labels.optionsTitle?.length > 0) && 'border-t dark:border-gray-500/70 border-gray-500/10',
'max-h-[30vh] overflow-y-auto',
])}
>
Expand Down Expand Up @@ -206,13 +205,13 @@ const DropdownRender = ({
</ul>
)}
</div>
<div className="flex justify-between border-t border-neutral-200/50 items-center">
<div className="flex justify-between border-t dark:border-gray-500/70 border-gray-500/10 items-center">
<button
key={nanoid()}
className={clsx([
'flex items-center text-center rounded-b-lg font-semibold justify-center',
'gap-2 px-3 py-2 hover:bg-white/20 w-full',
'border-r border-neutral-200/50',
'border-r dark:border-gray-500/70 border-gray-500/10',
'hover:bg-neutral-500/10 rounded-r-none',
])}
onClick={() => setSelections(availableOptions.map((f) => f.value))}
Expand Down Expand Up @@ -260,7 +259,7 @@ const DropdownFilter = ({
const [isFetching, setIsFetching] = useState(false);
const [options, setOptions] = useState<DropdownFilterOptionType[]>(_options ?? []);

const fetchOptions = _.debounce(async (searchKeyword: string) => {
const fetchOptions = async (searchKeyword: string) => {
try {
const fetchedOptions = await onFetch(searchKeyword);
const options = typeof fetchedOptions === 'object' && fetchedOptions.length ? fetchedOptions : [];
Expand All @@ -272,17 +271,14 @@ const DropdownFilter = ({
} finally {
setIsFetching(false);
}
}, 500);
};

useEffect(() => {
if (isAsync) {
setIsFetching(true);
fetchOptions(keyword);
}
return () => {
fetchOptions.cancel();
};
}, [isAsync, keyword, onFetch]);
}, [isAsync, keyword]);

return (
<Dropdown
Expand Down

0 comments on commit 43cddcb

Please sign in to comment.