forked from github/plane
chore: filters view more and less buttons (#2583)
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
parent
36152ea2fa
commit
d63e7cf254
@ -9,15 +9,14 @@ import { IUserLite } from "types";
|
|||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
|
||||||
members: IUserLite[] | undefined;
|
members: IUserLite[] | undefined;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
viewButtons: React.ReactNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterAssignees: React.FC<Props> = (props) => {
|
export const FilterAssignees: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, members, searchQuery, viewButtons } = props;
|
const { appliedFilters, handleUpdate, members, searchQuery } = props;
|
||||||
|
|
||||||
|
const [itemsToRender, setItemsToRender] = useState(5);
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
@ -26,6 +25,13 @@ export const FilterAssignees: React.FC<Props> = (props) => {
|
|||||||
member.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
member.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleViewToggle = () => {
|
||||||
|
if (!filteredOptions) return;
|
||||||
|
|
||||||
|
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
||||||
|
else setItemsToRender(filteredOptions.length);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FilterHeader
|
<FilterHeader
|
||||||
@ -47,7 +53,15 @@ export const FilterAssignees: React.FC<Props> = (props) => {
|
|||||||
title={member.display_name}
|
title={member.display_name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{viewButtons}
|
{filteredOptions.length > 5 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-custom-primary-100 text-xs font-medium ml-8"
|
||||||
|
onClick={handleViewToggle}
|
||||||
|
>
|
||||||
|
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
|
@ -9,15 +9,14 @@ import { IUserLite } from "types";
|
|||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
|
||||||
members: IUserLite[] | undefined;
|
members: IUserLite[] | undefined;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
viewButtons: React.ReactNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterCreatedBy: React.FC<Props> = (props) => {
|
export const FilterCreatedBy: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, members, searchQuery, viewButtons } = props;
|
const { appliedFilters, handleUpdate, members, searchQuery } = props;
|
||||||
|
|
||||||
|
const [itemsToRender, setItemsToRender] = useState(5);
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
@ -26,6 +25,13 @@ export const FilterCreatedBy: React.FC<Props> = (props) => {
|
|||||||
member.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
member.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleViewToggle = () => {
|
||||||
|
if (!filteredOptions) return;
|
||||||
|
|
||||||
|
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
||||||
|
else setItemsToRender(filteredOptions.length);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FilterHeader
|
<FilterHeader
|
||||||
@ -47,7 +53,15 @@ export const FilterCreatedBy: React.FC<Props> = (props) => {
|
|||||||
title={member.display_name}
|
title={member.display_name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{viewButtons}
|
{filteredOptions.length > 5 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-custom-primary-100 text-xs font-medium ml-8"
|
||||||
|
onClick={handleViewToggle}
|
||||||
|
>
|
||||||
|
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { Search, X } from "lucide-react";
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
FilterAssignees,
|
FilterAssignees,
|
||||||
@ -14,15 +14,10 @@ import {
|
|||||||
FilterStateGroup,
|
FilterStateGroup,
|
||||||
FilterTargetDate,
|
FilterTargetDate,
|
||||||
} from "components/issues";
|
} from "components/issues";
|
||||||
// icons
|
|
||||||
import { Search, X } from "lucide-react";
|
|
||||||
// helpers
|
|
||||||
import { getStatesList } from "helpers/state.helper";
|
|
||||||
// types
|
// types
|
||||||
import { IIssueFilterOptions, IIssueLabels, IProject, IStateResponse, IUserLite } from "types";
|
import { IIssueFilterOptions, IIssueLabels, IProject, IStateResponse, IUserLite } from "types";
|
||||||
// constants
|
// constants
|
||||||
import { ILayoutDisplayFiltersOptions, ISSUE_PRIORITIES, ISSUE_STATE_GROUPS } from "constants/issue";
|
import { ILayoutDisplayFiltersOptions } from "constants/issue";
|
||||||
import { DATE_FILTER_OPTIONS } from "constants/filters";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
filters: IIssueFilterOptions;
|
filters: IIssueFilterOptions;
|
||||||
@ -34,129 +29,11 @@ type Props = {
|
|||||||
states?: IStateResponse | undefined;
|
states?: IStateResponse | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ViewButtonProps = {
|
|
||||||
handleLess: () => void;
|
|
||||||
handleMore: () => void;
|
|
||||||
isViewLessVisible: boolean;
|
|
||||||
isViewMoreVisible: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ViewButtons = ({ handleLess, handleMore, isViewLessVisible, isViewMoreVisible }: ViewButtonProps) => (
|
|
||||||
<div className="flex items-center gap-2 ml-7 mt-1">
|
|
||||||
{/* TODO: handle view more and less in a better way */}
|
|
||||||
{isViewMoreVisible && (
|
|
||||||
<button className="text-custom-primary-100 text-xs font-medium" onClick={handleMore}>
|
|
||||||
View more
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{isViewLessVisible && (
|
|
||||||
<button className="text-custom-primary-100 text-xs font-medium" onClick={handleLess}>
|
|
||||||
View less
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const FilterSelection: React.FC<Props> = observer((props) => {
|
export const FilterSelection: React.FC<Props> = observer((props) => {
|
||||||
const { filters, handleFiltersUpdate, layoutDisplayFiltersOptions, labels, members, projects, states } = props;
|
const { filters, handleFiltersUpdate, layoutDisplayFiltersOptions, labels, members, projects, states } = props;
|
||||||
|
|
||||||
const [filtersSearchQuery, setFiltersSearchQuery] = useState("");
|
const [filtersSearchQuery, setFiltersSearchQuery] = useState("");
|
||||||
|
|
||||||
const statesList = getStatesList(states);
|
|
||||||
|
|
||||||
const [filtersToRender, setFiltersToRender] = useState<{
|
|
||||||
[key in keyof IIssueFilterOptions]: {
|
|
||||||
currentLength: number;
|
|
||||||
totalLength: number;
|
|
||||||
};
|
|
||||||
}>({
|
|
||||||
assignees: {
|
|
||||||
currentLength: 5,
|
|
||||||
totalLength: members?.length ?? 0,
|
|
||||||
},
|
|
||||||
mentions: {
|
|
||||||
currentLength: 5,
|
|
||||||
totalLength: members?.length ?? 0,
|
|
||||||
},
|
|
||||||
created_by: {
|
|
||||||
currentLength: 5,
|
|
||||||
totalLength: members?.length ?? 0,
|
|
||||||
},
|
|
||||||
labels: {
|
|
||||||
currentLength: 5,
|
|
||||||
totalLength: labels?.length ?? 0,
|
|
||||||
},
|
|
||||||
priority: {
|
|
||||||
currentLength: 5,
|
|
||||||
totalLength: ISSUE_PRIORITIES.length,
|
|
||||||
},
|
|
||||||
project: {
|
|
||||||
currentLength: 5,
|
|
||||||
totalLength: projects?.length ?? 0,
|
|
||||||
},
|
|
||||||
state_group: {
|
|
||||||
currentLength: 5,
|
|
||||||
totalLength: ISSUE_STATE_GROUPS.length,
|
|
||||||
},
|
|
||||||
state: {
|
|
||||||
currentLength: 5,
|
|
||||||
totalLength: statesList?.length ?? 0,
|
|
||||||
},
|
|
||||||
start_date: {
|
|
||||||
currentLength: 5,
|
|
||||||
totalLength: DATE_FILTER_OPTIONS.length + 1,
|
|
||||||
},
|
|
||||||
target_date: {
|
|
||||||
currentLength: 5,
|
|
||||||
totalLength: DATE_FILTER_OPTIONS.length + 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleViewMore = (filterName: keyof IIssueFilterOptions) => {
|
|
||||||
const filterDetails = filtersToRender[filterName];
|
|
||||||
|
|
||||||
if (!filterDetails) return;
|
|
||||||
|
|
||||||
if (filterDetails.currentLength <= filterDetails.totalLength)
|
|
||||||
setFiltersToRender((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[filterName]: {
|
|
||||||
...prev[filterName],
|
|
||||||
currentLength: filterDetails.currentLength + 5,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleViewLess = (filterName: keyof IIssueFilterOptions) => {
|
|
||||||
const filterDetails = filtersToRender[filterName];
|
|
||||||
|
|
||||||
if (!filterDetails) return;
|
|
||||||
|
|
||||||
setFiltersToRender((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[filterName]: {
|
|
||||||
...prev[filterName],
|
|
||||||
currentLength: 5,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const isViewMoreVisible = (filterName: keyof IIssueFilterOptions): boolean => {
|
|
||||||
const filterDetails = filtersToRender[filterName];
|
|
||||||
|
|
||||||
if (!filterDetails) return false;
|
|
||||||
|
|
||||||
return filterDetails.currentLength < filterDetails.totalLength;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isViewLessVisible = (filterName: keyof IIssueFilterOptions): boolean => {
|
|
||||||
const filterDetails = filtersToRender[filterName];
|
|
||||||
|
|
||||||
if (!filterDetails) return false;
|
|
||||||
|
|
||||||
return filterDetails.currentLength > 5;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isFilterEnabled = (filter: keyof IIssueFilterOptions) => layoutDisplayFiltersOptions?.filters.includes(filter);
|
const isFilterEnabled = (filter: keyof IIssueFilterOptions) => layoutDisplayFiltersOptions?.filters.includes(filter);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -186,16 +63,7 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
<FilterPriority
|
<FilterPriority
|
||||||
appliedFilters={filters.priority ?? null}
|
appliedFilters={filters.priority ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("priority", val)}
|
handleUpdate={(val) => handleFiltersUpdate("priority", val)}
|
||||||
itemsToRender={filtersToRender.priority?.currentLength ?? 0}
|
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
|
||||||
<ViewButtons
|
|
||||||
isViewLessVisible={isViewLessVisible("priority")}
|
|
||||||
isViewMoreVisible={isViewMoreVisible("priority")}
|
|
||||||
handleLess={() => handleViewLess("priority")}
|
|
||||||
handleMore={() => handleViewMore("priority")}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -206,16 +74,7 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
<FilterStateGroup
|
<FilterStateGroup
|
||||||
appliedFilters={filters.state_group ?? null}
|
appliedFilters={filters.state_group ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("state_group", val)}
|
handleUpdate={(val) => handleFiltersUpdate("state_group", val)}
|
||||||
itemsToRender={filtersToRender.state_group?.currentLength ?? 0}
|
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
|
||||||
<ViewButtons
|
|
||||||
isViewLessVisible={isViewLessVisible("state_group")}
|
|
||||||
isViewMoreVisible={isViewMoreVisible("state_group")}
|
|
||||||
handleLess={() => handleViewLess("state_group")}
|
|
||||||
handleMore={() => handleViewMore("state_group")}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -226,17 +85,8 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
<FilterState
|
<FilterState
|
||||||
appliedFilters={filters.state ?? null}
|
appliedFilters={filters.state ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("state", val)}
|
handleUpdate={(val) => handleFiltersUpdate("state", val)}
|
||||||
itemsToRender={filtersToRender.state?.currentLength ?? 0}
|
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
states={states}
|
states={states}
|
||||||
viewButtons={
|
|
||||||
<ViewButtons
|
|
||||||
isViewLessVisible={isViewLessVisible("state")}
|
|
||||||
isViewMoreVisible={isViewMoreVisible("state")}
|
|
||||||
handleLess={() => handleViewLess("state")}
|
|
||||||
handleMore={() => handleViewMore("state")}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -247,17 +97,8 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
<FilterAssignees
|
<FilterAssignees
|
||||||
appliedFilters={filters.assignees ?? null}
|
appliedFilters={filters.assignees ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("assignees", val)}
|
handleUpdate={(val) => handleFiltersUpdate("assignees", val)}
|
||||||
itemsToRender={filtersToRender.assignees?.currentLength ?? 0}
|
|
||||||
members={members}
|
members={members}
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
|
||||||
<ViewButtons
|
|
||||||
isViewLessVisible={isViewLessVisible("assignees")}
|
|
||||||
isViewMoreVisible={isViewMoreVisible("assignees")}
|
|
||||||
handleLess={() => handleViewLess("assignees")}
|
|
||||||
handleMore={() => handleViewMore("assignees")}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -268,17 +109,8 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
<FilterMentions
|
<FilterMentions
|
||||||
appliedFilters={filters.mentions ?? null}
|
appliedFilters={filters.mentions ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("mentions", val)}
|
handleUpdate={(val) => handleFiltersUpdate("mentions", val)}
|
||||||
itemsToRender={filtersToRender.mentions?.currentLength ?? 0}
|
|
||||||
members={members}
|
members={members}
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
|
||||||
<ViewButtons
|
|
||||||
isViewLessVisible={isViewLessVisible("mentions")}
|
|
||||||
isViewMoreVisible={isViewMoreVisible("mentions")}
|
|
||||||
handleLess={() => handleViewLess("mentions")}
|
|
||||||
handleMore={() => handleViewMore("mentions")}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -289,17 +121,8 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
<FilterCreatedBy
|
<FilterCreatedBy
|
||||||
appliedFilters={filters.created_by ?? null}
|
appliedFilters={filters.created_by ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("created_by", val)}
|
handleUpdate={(val) => handleFiltersUpdate("created_by", val)}
|
||||||
itemsToRender={filtersToRender.created_by?.currentLength ?? 0}
|
|
||||||
members={members}
|
members={members}
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
|
||||||
<ViewButtons
|
|
||||||
isViewLessVisible={isViewLessVisible("created_by")}
|
|
||||||
isViewMoreVisible={isViewMoreVisible("created_by")}
|
|
||||||
handleLess={() => handleViewLess("created_by")}
|
|
||||||
handleMore={() => handleViewMore("created_by")}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -310,17 +133,8 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
<FilterLabels
|
<FilterLabels
|
||||||
appliedFilters={filters.labels ?? null}
|
appliedFilters={filters.labels ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("labels", val)}
|
handleUpdate={(val) => handleFiltersUpdate("labels", val)}
|
||||||
itemsToRender={filtersToRender.labels?.currentLength ?? 0}
|
|
||||||
labels={labels}
|
labels={labels}
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
|
||||||
<ViewButtons
|
|
||||||
isViewLessVisible={isViewLessVisible("labels")}
|
|
||||||
isViewMoreVisible={isViewMoreVisible("labels")}
|
|
||||||
handleLess={() => handleViewLess("labels")}
|
|
||||||
handleMore={() => handleViewMore("labels")}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -332,16 +146,7 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
appliedFilters={filters.project ?? null}
|
appliedFilters={filters.project ?? null}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("project", val)}
|
handleUpdate={(val) => handleFiltersUpdate("project", val)}
|
||||||
itemsToRender={filtersToRender.project?.currentLength ?? 0}
|
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
viewButtons={
|
|
||||||
<ViewButtons
|
|
||||||
isViewLessVisible={isViewLessVisible("project")}
|
|
||||||
isViewMoreVisible={isViewMoreVisible("project")}
|
|
||||||
handleLess={() => handleViewLess("project")}
|
|
||||||
handleMore={() => handleViewMore("project")}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -352,7 +157,6 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
<FilterStartDate
|
<FilterStartDate
|
||||||
appliedFilters={filters.start_date ?? null}
|
appliedFilters={filters.start_date ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("start_date", val)}
|
handleUpdate={(val) => handleFiltersUpdate("start_date", val)}
|
||||||
itemsToRender={filtersToRender.start_date?.currentLength ?? 0}
|
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -364,7 +168,6 @@ export const FilterSelection: React.FC<Props> = observer((props) => {
|
|||||||
<FilterTargetDate
|
<FilterTargetDate
|
||||||
appliedFilters={filters.target_date ?? null}
|
appliedFilters={filters.target_date ?? null}
|
||||||
handleUpdate={(val) => handleFiltersUpdate("target_date", val)}
|
handleUpdate={(val) => handleFiltersUpdate("target_date", val)}
|
||||||
itemsToRender={filtersToRender.target_date?.currentLength ?? 0}
|
|
||||||
searchQuery={filtersSearchQuery}
|
searchQuery={filtersSearchQuery}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,21 +14,27 @@ const LabelIcons = ({ color }: { color: string }) => (
|
|||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
|
||||||
labels: IIssueLabels[] | undefined;
|
labels: IIssueLabels[] | undefined;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
viewButtons: React.ReactNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterLabels: React.FC<Props> = (props) => {
|
export const FilterLabels: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, labels, searchQuery, viewButtons } = props;
|
const { appliedFilters, handleUpdate, labels, searchQuery } = props;
|
||||||
|
|
||||||
|
const [itemsToRender, setItemsToRender] = useState(5);
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const filteredOptions = labels?.filter((label) => label.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
const filteredOptions = labels?.filter((label) => label.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
||||||
|
|
||||||
|
const handleViewToggle = () => {
|
||||||
|
if (!filteredOptions) return;
|
||||||
|
|
||||||
|
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
||||||
|
else setItemsToRender(filteredOptions.length);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FilterHeader
|
<FilterHeader
|
||||||
@ -50,7 +56,15 @@ export const FilterLabels: React.FC<Props> = (props) => {
|
|||||||
title={label.name}
|
title={label.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{viewButtons}
|
{filteredOptions.length > 5 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-custom-primary-100 text-xs font-medium ml-8"
|
||||||
|
onClick={handleViewToggle}
|
||||||
|
>
|
||||||
|
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { FilterHeader, FilterOption } from "components/issues";
|
import { FilterHeader, FilterOption } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
@ -10,15 +9,14 @@ import { IUserLite } from "types";
|
|||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
|
||||||
members: IUserLite[] | undefined;
|
members: IUserLite[] | undefined;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
viewButtons: React.ReactNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterMentions: React.FC<Props> = (props) => {
|
export const FilterMentions: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, members, searchQuery, viewButtons } = props;
|
const { appliedFilters, handleUpdate, members, searchQuery } = props;
|
||||||
|
|
||||||
|
const [itemsToRender, setItemsToRender] = useState(5);
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
@ -27,6 +25,13 @@ export const FilterMentions: React.FC<Props> = (props) => {
|
|||||||
member.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
member.display_name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleViewToggle = () => {
|
||||||
|
if (!filteredOptions) return;
|
||||||
|
|
||||||
|
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
||||||
|
else setItemsToRender(filteredOptions.length);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FilterHeader
|
<FilterHeader
|
||||||
@ -48,7 +53,15 @@ export const FilterMentions: React.FC<Props> = (props) => {
|
|||||||
title={member.display_name}
|
title={member.display_name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{viewButtons}
|
{filteredOptions.length > 5 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-custom-primary-100 text-xs font-medium ml-8"
|
||||||
|
onClick={handleViewToggle}
|
||||||
|
>
|
||||||
|
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
|
@ -51,13 +51,11 @@ const PriorityIcons = ({
|
|||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
viewButtons: React.ReactNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterPriority: React.FC<Props> = observer((props) => {
|
export const FilterPriority: React.FC<Props> = observer((props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, searchQuery, viewButtons } = props;
|
const { appliedFilters, handleUpdate, searchQuery } = props;
|
||||||
|
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
@ -75,8 +73,7 @@ export const FilterPriority: React.FC<Props> = observer((props) => {
|
|||||||
{previewEnabled && (
|
{previewEnabled && (
|
||||||
<div>
|
<div>
|
||||||
{filteredOptions.length > 0 ? (
|
{filteredOptions.length > 0 ? (
|
||||||
<>
|
filteredOptions.map((priority) => (
|
||||||
{filteredOptions.slice(0, itemsToRender).map((priority) => (
|
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={priority.key}
|
key={priority.key}
|
||||||
isChecked={appliedFilters?.includes(priority.key) ? true : false}
|
isChecked={appliedFilters?.includes(priority.key) ? true : false}
|
||||||
@ -84,9 +81,7 @@ export const FilterPriority: React.FC<Props> = observer((props) => {
|
|||||||
icon={<PriorityIcons priority={priority.key} />}
|
icon={<PriorityIcons priority={priority.key} />}
|
||||||
title={priority.title}
|
title={priority.title}
|
||||||
/>
|
/>
|
||||||
))}
|
))
|
||||||
{viewButtons}
|
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
)}
|
)}
|
||||||
|
@ -12,21 +12,27 @@ import { IProject } from "types";
|
|||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
|
||||||
projects: IProject[] | undefined;
|
projects: IProject[] | undefined;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
viewButtons: React.ReactNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterProjects: React.FC<Props> = (props) => {
|
export const FilterProjects: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, projects, searchQuery, viewButtons } = props;
|
const { appliedFilters, handleUpdate, projects, searchQuery } = props;
|
||||||
|
|
||||||
|
const [itemsToRender, setItemsToRender] = useState(5);
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const filteredOptions = projects?.filter((project) => project.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
const filteredOptions = projects?.filter((project) => project.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
||||||
|
|
||||||
|
const handleViewToggle = () => {
|
||||||
|
if (!filteredOptions) return;
|
||||||
|
|
||||||
|
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
||||||
|
else setItemsToRender(filteredOptions.length);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FilterHeader
|
<FilterHeader
|
||||||
@ -62,7 +68,15 @@ export const FilterProjects: React.FC<Props> = (props) => {
|
|||||||
title={project.name}
|
title={project.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{viewButtons}
|
{filteredOptions.length > 5 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-custom-primary-100 text-xs font-medium ml-8"
|
||||||
|
onClick={handleViewToggle}
|
||||||
|
>
|
||||||
|
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
|
@ -10,12 +10,11 @@ import { DATE_FILTER_OPTIONS } from "constants/filters";
|
|||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string | string[]) => void;
|
handleUpdate: (val: string | string[]) => void;
|
||||||
itemsToRender: number;
|
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterStartDate: React.FC<Props> = observer((props) => {
|
export const FilterStartDate: React.FC<Props> = observer((props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, searchQuery } = props;
|
const { appliedFilters, handleUpdate, searchQuery } = props;
|
||||||
|
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false);
|
const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false);
|
||||||
@ -43,7 +42,7 @@ export const FilterStartDate: React.FC<Props> = observer((props) => {
|
|||||||
<div>
|
<div>
|
||||||
{filteredOptions.length > 0 ? (
|
{filteredOptions.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((option) => (
|
{filteredOptions.map((option) => (
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={option.value}
|
key={option.value}
|
||||||
isChecked={appliedFilters?.includes(option.value) ? true : false}
|
isChecked={appliedFilters?.includes(option.value) ? true : false}
|
||||||
|
@ -11,20 +11,26 @@ import { ISSUE_STATE_GROUPS } from "constants/issue";
|
|||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
viewButtons: React.ReactNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterStateGroup: React.FC<Props> = observer((props) => {
|
export const FilterStateGroup: React.FC<Props> = observer((props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, searchQuery, viewButtons } = props;
|
const { appliedFilters, handleUpdate, searchQuery } = props;
|
||||||
|
|
||||||
|
const [itemsToRender, setItemsToRender] = useState(5);
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
|
||||||
const filteredOptions = ISSUE_STATE_GROUPS.filter((s) => s.key.includes(searchQuery.toLowerCase()));
|
const filteredOptions = ISSUE_STATE_GROUPS.filter((s) => s.key.includes(searchQuery.toLowerCase()));
|
||||||
|
|
||||||
|
const handleViewToggle = () => {
|
||||||
|
if (!filteredOptions) return;
|
||||||
|
|
||||||
|
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
||||||
|
else setItemsToRender(filteredOptions.length);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FilterHeader
|
<FilterHeader
|
||||||
@ -45,7 +51,15 @@ export const FilterStateGroup: React.FC<Props> = observer((props) => {
|
|||||||
title={stateGroup.title}
|
title={stateGroup.title}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{viewButtons}
|
{filteredOptions.length > 5 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-custom-primary-100 text-xs font-medium ml-8"
|
||||||
|
onClick={handleViewToggle}
|
||||||
|
>
|
||||||
|
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
|
@ -12,15 +12,14 @@ import { IStateResponse } from "types";
|
|||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string) => void;
|
handleUpdate: (val: string) => void;
|
||||||
itemsToRender: number;
|
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
states: IStateResponse | undefined;
|
states: IStateResponse | undefined;
|
||||||
viewButtons: React.ReactNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterState: React.FC<Props> = (props) => {
|
export const FilterState: React.FC<Props> = (props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, searchQuery, states, viewButtons } = props;
|
const { appliedFilters, handleUpdate, searchQuery, states } = props;
|
||||||
|
|
||||||
|
const [itemsToRender, setItemsToRender] = useState(5);
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
|
|
||||||
const statesList = getStatesList(states);
|
const statesList = getStatesList(states);
|
||||||
@ -29,6 +28,13 @@ export const FilterState: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const filteredOptions = statesList?.filter((s) => s.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
const filteredOptions = statesList?.filter((s) => s.name.toLowerCase().includes(searchQuery.toLowerCase()));
|
||||||
|
|
||||||
|
const handleViewToggle = () => {
|
||||||
|
if (!filteredOptions) return;
|
||||||
|
|
||||||
|
if (itemsToRender === filteredOptions.length) setItemsToRender(5);
|
||||||
|
else setItemsToRender(filteredOptions.length);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FilterHeader
|
<FilterHeader
|
||||||
@ -50,7 +56,15 @@ export const FilterState: React.FC<Props> = (props) => {
|
|||||||
title={state.name}
|
title={state.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{viewButtons}
|
{filteredOptions.length > 5 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-custom-primary-100 text-xs font-medium ml-8"
|
||||||
|
onClick={handleViewToggle}
|
||||||
|
>
|
||||||
|
{itemsToRender === filteredOptions.length ? "View less" : "View all"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
<p className="text-xs text-custom-text-400 italic">No matches found</p>
|
||||||
|
@ -10,12 +10,11 @@ import { DATE_FILTER_OPTIONS } from "constants/filters";
|
|||||||
type Props = {
|
type Props = {
|
||||||
appliedFilters: string[] | null;
|
appliedFilters: string[] | null;
|
||||||
handleUpdate: (val: string | string[]) => void;
|
handleUpdate: (val: string | string[]) => void;
|
||||||
itemsToRender: number;
|
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterTargetDate: React.FC<Props> = observer((props) => {
|
export const FilterTargetDate: React.FC<Props> = observer((props) => {
|
||||||
const { appliedFilters, handleUpdate, itemsToRender, searchQuery } = props;
|
const { appliedFilters, handleUpdate, searchQuery } = props;
|
||||||
|
|
||||||
const [previewEnabled, setPreviewEnabled] = useState(true);
|
const [previewEnabled, setPreviewEnabled] = useState(true);
|
||||||
const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false);
|
const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false);
|
||||||
@ -43,7 +42,7 @@ export const FilterTargetDate: React.FC<Props> = observer((props) => {
|
|||||||
<div>
|
<div>
|
||||||
{filteredOptions.length > 0 ? (
|
{filteredOptions.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{filteredOptions.slice(0, itemsToRender).map((option) => (
|
{filteredOptions.map((option) => (
|
||||||
<FilterOption
|
<FilterOption
|
||||||
key={option.value}
|
key={option.value}
|
||||||
isChecked={appliedFilters?.includes(option.value) ? true : false}
|
isChecked={appliedFilters?.includes(option.value) ? true : false}
|
||||||
|
Loading…
Reference in New Issue
Block a user