diff --git a/web/components/issues/issue-layouts/filters/header/filters/assignee.tsx b/web/components/issues/issue-layouts/filters/header/filters/assignee.tsx index de080fd4e..7c92b0ed9 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/assignee.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/assignee.tsx @@ -9,15 +9,14 @@ import { IUserLite } from "types"; type Props = { appliedFilters: string[] | null; handleUpdate: (val: string) => void; - itemsToRender: number; members: IUserLite[] | undefined; searchQuery: string; - viewButtons: React.ReactNode; }; export const FilterAssignees: React.FC = (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 appliedFiltersCount = appliedFilters?.length ?? 0; @@ -26,6 +25,13 @@ export const FilterAssignees: React.FC = (props) => { member.display_name.toLowerCase().includes(searchQuery.toLowerCase()) ); + const handleViewToggle = () => { + if (!filteredOptions) return; + + if (itemsToRender === filteredOptions.length) setItemsToRender(5); + else setItemsToRender(filteredOptions.length); + }; + return ( <> = (props) => { title={member.display_name} /> ))} - {viewButtons} + {filteredOptions.length > 5 && ( + + )} ) : (

No matches found

diff --git a/web/components/issues/issue-layouts/filters/header/filters/created-by.tsx b/web/components/issues/issue-layouts/filters/header/filters/created-by.tsx index 310ce4e8a..90873cb7b 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/created-by.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/created-by.tsx @@ -9,15 +9,14 @@ import { IUserLite } from "types"; type Props = { appliedFilters: string[] | null; handleUpdate: (val: string) => void; - itemsToRender: number; members: IUserLite[] | undefined; searchQuery: string; - viewButtons: React.ReactNode; }; export const FilterCreatedBy: React.FC = (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 appliedFiltersCount = appliedFilters?.length ?? 0; @@ -26,6 +25,13 @@ export const FilterCreatedBy: React.FC = (props) => { member.display_name.toLowerCase().includes(searchQuery.toLowerCase()) ); + const handleViewToggle = () => { + if (!filteredOptions) return; + + if (itemsToRender === filteredOptions.length) setItemsToRender(5); + else setItemsToRender(filteredOptions.length); + }; + return ( <> = (props) => { title={member.display_name} /> ))} - {viewButtons} + {filteredOptions.length > 5 && ( + + )} ) : (

No matches found

diff --git a/web/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx b/web/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx index 66ebf3a78..f07a47974 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { observer } from "mobx-react-lite"; - +import { Search, X } from "lucide-react"; // components import { FilterAssignees, @@ -14,15 +14,10 @@ import { FilterStateGroup, FilterTargetDate, } from "components/issues"; -// icons -import { Search, X } from "lucide-react"; -// helpers -import { getStatesList } from "helpers/state.helper"; // types import { IIssueFilterOptions, IIssueLabels, IProject, IStateResponse, IUserLite } from "types"; // constants -import { ILayoutDisplayFiltersOptions, ISSUE_PRIORITIES, ISSUE_STATE_GROUPS } from "constants/issue"; -import { DATE_FILTER_OPTIONS } from "constants/filters"; +import { ILayoutDisplayFiltersOptions } from "constants/issue"; type Props = { filters: IIssueFilterOptions; @@ -34,129 +29,11 @@ type Props = { states?: IStateResponse | undefined; }; -type ViewButtonProps = { - handleLess: () => void; - handleMore: () => void; - isViewLessVisible: boolean; - isViewMoreVisible: boolean; -}; - -const ViewButtons = ({ handleLess, handleMore, isViewLessVisible, isViewMoreVisible }: ViewButtonProps) => ( -
- {/* TODO: handle view more and less in a better way */} - {isViewMoreVisible && ( - - )} - {isViewLessVisible && ( - - )} -
-); - export const FilterSelection: React.FC = observer((props) => { const { filters, handleFiltersUpdate, layoutDisplayFiltersOptions, labels, members, projects, states } = props; 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); return ( @@ -186,16 +63,7 @@ export const FilterSelection: React.FC = observer((props) => { handleFiltersUpdate("priority", val)} - itemsToRender={filtersToRender.priority?.currentLength ?? 0} searchQuery={filtersSearchQuery} - viewButtons={ - handleViewLess("priority")} - handleMore={() => handleViewMore("priority")} - /> - } /> )} @@ -206,16 +74,7 @@ export const FilterSelection: React.FC = observer((props) => { handleFiltersUpdate("state_group", val)} - itemsToRender={filtersToRender.state_group?.currentLength ?? 0} searchQuery={filtersSearchQuery} - viewButtons={ - handleViewLess("state_group")} - handleMore={() => handleViewMore("state_group")} - /> - } /> )} @@ -226,17 +85,8 @@ export const FilterSelection: React.FC = observer((props) => { handleFiltersUpdate("state", val)} - itemsToRender={filtersToRender.state?.currentLength ?? 0} searchQuery={filtersSearchQuery} states={states} - viewButtons={ - handleViewLess("state")} - handleMore={() => handleViewMore("state")} - /> - } /> )} @@ -247,17 +97,8 @@ export const FilterSelection: React.FC = observer((props) => { handleFiltersUpdate("assignees", val)} - itemsToRender={filtersToRender.assignees?.currentLength ?? 0} members={members} searchQuery={filtersSearchQuery} - viewButtons={ - handleViewLess("assignees")} - handleMore={() => handleViewMore("assignees")} - /> - } /> )} @@ -268,17 +109,8 @@ export const FilterSelection: React.FC = observer((props) => { handleFiltersUpdate("mentions", val)} - itemsToRender={filtersToRender.mentions?.currentLength ?? 0} members={members} searchQuery={filtersSearchQuery} - viewButtons={ - handleViewLess("mentions")} - handleMore={() => handleViewMore("mentions")} - /> - } /> )} @@ -289,17 +121,8 @@ export const FilterSelection: React.FC = observer((props) => { handleFiltersUpdate("created_by", val)} - itemsToRender={filtersToRender.created_by?.currentLength ?? 0} members={members} searchQuery={filtersSearchQuery} - viewButtons={ - handleViewLess("created_by")} - handleMore={() => handleViewMore("created_by")} - /> - } /> )} @@ -310,17 +133,8 @@ export const FilterSelection: React.FC = observer((props) => { handleFiltersUpdate("labels", val)} - itemsToRender={filtersToRender.labels?.currentLength ?? 0} labels={labels} searchQuery={filtersSearchQuery} - viewButtons={ - handleViewLess("labels")} - handleMore={() => handleViewMore("labels")} - /> - } /> )} @@ -332,16 +146,7 @@ export const FilterSelection: React.FC = observer((props) => { appliedFilters={filters.project ?? null} projects={projects} handleUpdate={(val) => handleFiltersUpdate("project", val)} - itemsToRender={filtersToRender.project?.currentLength ?? 0} searchQuery={filtersSearchQuery} - viewButtons={ - handleViewLess("project")} - handleMore={() => handleViewMore("project")} - /> - } /> )} @@ -352,7 +157,6 @@ export const FilterSelection: React.FC = observer((props) => { handleFiltersUpdate("start_date", val)} - itemsToRender={filtersToRender.start_date?.currentLength ?? 0} searchQuery={filtersSearchQuery} /> @@ -364,7 +168,6 @@ export const FilterSelection: React.FC = observer((props) => { handleFiltersUpdate("target_date", val)} - itemsToRender={filtersToRender.target_date?.currentLength ?? 0} searchQuery={filtersSearchQuery} /> diff --git a/web/components/issues/issue-layouts/filters/header/filters/labels.tsx b/web/components/issues/issue-layouts/filters/header/filters/labels.tsx index 37f6846f9..764d29951 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/labels.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/labels.tsx @@ -14,21 +14,27 @@ const LabelIcons = ({ color }: { color: string }) => ( type Props = { appliedFilters: string[] | null; handleUpdate: (val: string) => void; - itemsToRender: number; labels: IIssueLabels[] | undefined; searchQuery: string; - viewButtons: React.ReactNode; }; export const FilterLabels: React.FC = (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 appliedFiltersCount = appliedFilters?.length ?? 0; 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 ( <> = (props) => { title={label.name} /> ))} - {viewButtons} + {filteredOptions.length > 5 && ( + + )} ) : (

No matches found

diff --git a/web/components/issues/issue-layouts/filters/header/filters/mentions.tsx b/web/components/issues/issue-layouts/filters/header/filters/mentions.tsx index c3c2c448c..5d7a63097 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/mentions.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/mentions.tsx @@ -1,5 +1,4 @@ import React, { useState } from "react"; - // components import { FilterHeader, FilterOption } from "components/issues"; // ui @@ -10,15 +9,14 @@ import { IUserLite } from "types"; type Props = { appliedFilters: string[] | null; handleUpdate: (val: string) => void; - itemsToRender: number; members: IUserLite[] | undefined; searchQuery: string; - viewButtons: React.ReactNode; }; export const FilterMentions: React.FC = (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 appliedFiltersCount = appliedFilters?.length ?? 0; @@ -27,6 +25,13 @@ export const FilterMentions: React.FC = (props) => { member.display_name.toLowerCase().includes(searchQuery.toLowerCase()) ); + const handleViewToggle = () => { + if (!filteredOptions) return; + + if (itemsToRender === filteredOptions.length) setItemsToRender(5); + else setItemsToRender(filteredOptions.length); + }; + return ( <> = (props) => { title={member.display_name} /> ))} - {viewButtons} + {filteredOptions.length > 5 && ( + + )} ) : (

No matches found

diff --git a/web/components/issues/issue-layouts/filters/header/filters/priority.tsx b/web/components/issues/issue-layouts/filters/header/filters/priority.tsx index 4eff176e0..660897100 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/priority.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/priority.tsx @@ -51,13 +51,11 @@ const PriorityIcons = ({ type Props = { appliedFilters: string[] | null; handleUpdate: (val: string) => void; - itemsToRender: number; searchQuery: string; - viewButtons: React.ReactNode; }; export const FilterPriority: React.FC = observer((props) => { - const { appliedFilters, handleUpdate, itemsToRender, searchQuery, viewButtons } = props; + const { appliedFilters, handleUpdate, searchQuery } = props; const [previewEnabled, setPreviewEnabled] = useState(true); @@ -75,18 +73,15 @@ export const FilterPriority: React.FC = observer((props) => { {previewEnabled && (
{filteredOptions.length > 0 ? ( - <> - {filteredOptions.slice(0, itemsToRender).map((priority) => ( - handleUpdate(priority.key)} - icon={} - title={priority.title} - /> - ))} - {viewButtons} - + filteredOptions.map((priority) => ( + handleUpdate(priority.key)} + icon={} + title={priority.title} + /> + )) ) : (

No matches found

)} diff --git a/web/components/issues/issue-layouts/filters/header/filters/project.tsx b/web/components/issues/issue-layouts/filters/header/filters/project.tsx index de02e9692..9dcc6b542 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/project.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/project.tsx @@ -12,21 +12,27 @@ import { IProject } from "types"; type Props = { appliedFilters: string[] | null; handleUpdate: (val: string) => void; - itemsToRender: number; projects: IProject[] | undefined; searchQuery: string; - viewButtons: React.ReactNode; }; export const FilterProjects: React.FC = (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 appliedFiltersCount = appliedFilters?.length ?? 0; 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 ( <> = (props) => { title={project.name} /> ))} - {viewButtons} + {filteredOptions.length > 5 && ( + + )} ) : (

No matches found

diff --git a/web/components/issues/issue-layouts/filters/header/filters/start-date.tsx b/web/components/issues/issue-layouts/filters/header/filters/start-date.tsx index e93f1fa7d..e58533270 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/start-date.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/start-date.tsx @@ -10,12 +10,11 @@ import { DATE_FILTER_OPTIONS } from "constants/filters"; type Props = { appliedFilters: string[] | null; handleUpdate: (val: string | string[]) => void; - itemsToRender: number; searchQuery: string; }; export const FilterStartDate: React.FC = observer((props) => { - const { appliedFilters, handleUpdate, itemsToRender, searchQuery } = props; + const { appliedFilters, handleUpdate, searchQuery } = props; const [previewEnabled, setPreviewEnabled] = useState(true); const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false); @@ -43,7 +42,7 @@ export const FilterStartDate: React.FC = observer((props) => {
{filteredOptions.length > 0 ? ( <> - {filteredOptions.slice(0, itemsToRender).map((option) => ( + {filteredOptions.map((option) => ( void; - itemsToRender: number; searchQuery: string; - viewButtons: React.ReactNode; }; export const FilterStateGroup: React.FC = 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 appliedFiltersCount = appliedFilters?.length ?? 0; 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 ( <> = observer((props) => { title={stateGroup.title} /> ))} - {viewButtons} + {filteredOptions.length > 5 && ( + + )} ) : (

No matches found

diff --git a/web/components/issues/issue-layouts/filters/header/filters/state.tsx b/web/components/issues/issue-layouts/filters/header/filters/state.tsx index afb64fad6..20caebc20 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/state.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/state.tsx @@ -12,15 +12,14 @@ import { IStateResponse } from "types"; type Props = { appliedFilters: string[] | null; handleUpdate: (val: string) => void; - itemsToRender: number; searchQuery: string; states: IStateResponse | undefined; - viewButtons: React.ReactNode; }; export const FilterState: React.FC = (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 statesList = getStatesList(states); @@ -29,6 +28,13 @@ export const FilterState: React.FC = (props) => { 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 ( <> = (props) => { title={state.name} /> ))} - {viewButtons} + {filteredOptions.length > 5 && ( + + )} ) : (

No matches found

diff --git a/web/components/issues/issue-layouts/filters/header/filters/target-date.tsx b/web/components/issues/issue-layouts/filters/header/filters/target-date.tsx index c007aa039..f914eb194 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/target-date.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/target-date.tsx @@ -10,12 +10,11 @@ import { DATE_FILTER_OPTIONS } from "constants/filters"; type Props = { appliedFilters: string[] | null; handleUpdate: (val: string | string[]) => void; - itemsToRender: number; searchQuery: string; }; export const FilterTargetDate: React.FC = observer((props) => { - const { appliedFilters, handleUpdate, itemsToRender, searchQuery } = props; + const { appliedFilters, handleUpdate, searchQuery } = props; const [previewEnabled, setPreviewEnabled] = useState(true); const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false); @@ -43,7 +42,7 @@ export const FilterTargetDate: React.FC = observer((props) => {
{filteredOptions.length > 0 ? ( <> - {filteredOptions.slice(0, itemsToRender).map((option) => ( + {filteredOptions.map((option) => (