chore: added disabled prop to multiple select components (#4724)

* chore: added disabled prop to mutliple select group hoc

* style: fix empty space
This commit is contained in:
Aaryan Khandelwal 2024-06-07 13:59:57 +05:30 committed by GitHub
parent cdb932ab67
commit 1c849103f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 62 additions and 18 deletions

View File

@ -18,6 +18,8 @@ export const MultipleSelectEntityAction: React.FC<Props> = (props) => {
// derived values // derived values
const isSelected = selectionHelpers.getIsEntitySelected(id); const isSelected = selectionHelpers.getIsEntitySelected(id);
if (selectionHelpers.isSelectionDisabled) return null;
return ( return (
<Checkbox <Checkbox
className={cn("!outline-none size-3.5", className)} className={cn("!outline-none size-3.5", className)}

View File

@ -17,6 +17,8 @@ export const MultipleSelectGroupAction: React.FC<Props> = (props) => {
// derived values // derived values
const groupSelectionStatus = selectionHelpers.isGroupSelected(groupID); const groupSelectionStatus = selectionHelpers.isGroupSelected(groupID);
if (selectionHelpers.isSelectionDisabled) return null;
return ( return (
<Checkbox <Checkbox
className={cn("size-3.5 !outline-none", className)} className={cn("size-3.5 !outline-none", className)}

View File

@ -5,14 +5,16 @@ import { TSelectionHelper, useMultipleSelect } from "@/hooks/use-multiple-select
type Props = { type Props = {
children: (helpers: TSelectionHelper) => React.ReactNode; children: (helpers: TSelectionHelper) => React.ReactNode;
containerRef: React.MutableRefObject<HTMLElement | null>; containerRef: React.MutableRefObject<HTMLElement | null>;
disabled?: boolean;
entities: Record<string, string[]>; // { groupID: entityIds[] } entities: Record<string, string[]>; // { groupID: entityIds[] }
}; };
export const MultipleSelectGroup: React.FC<Props> = observer((props) => { export const MultipleSelectGroup: React.FC<Props> = observer((props) => {
const { children, containerRef, entities } = props; const { children, containerRef, disabled = false, entities } = props;
const helpers = useMultipleSelect({ const helpers = useMultipleSelect({
containerRef, containerRef,
disabled,
entities, entities,
}); });

View File

@ -118,6 +118,7 @@ export const GanttChartMainContent: React.FC<Props> = observer((props) => {
entities={{ entities={{
[GANTT_SELECT_GROUP]: chartBlocks?.map((block) => block.id) ?? [], [GANTT_SELECT_GROUP]: chartBlocks?.map((block) => block.id) ?? [],
}} }}
disabled
> >
{(helpers) => ( {(helpers) => (
<> <>

View File

@ -11,11 +11,11 @@ type Props = {
}; };
export const IssueBulkOperationsRoot: React.FC<Props> = observer((props) => { export const IssueBulkOperationsRoot: React.FC<Props> = observer((props) => {
const { className } = props; const { className, selectionHelpers } = props;
// store hooks // store hooks
const { isSelectionActive } = useMultipleSelectStore(); const { isSelectionActive } = useMultipleSelectStore();
if (!isSelectionActive) return null; if (!isSelectionActive || selectionHelpers.isSelectionDisabled) return null;
return <BulkOperationsUpgradeBanner className={className} />; return <BulkOperationsUpgradeBanner className={className} />;
}); });

View File

@ -72,7 +72,7 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
enableBlockMove={isAllowed} enableBlockMove={isAllowed}
enableReorder={appliedDisplayFilters?.order_by === "sort_order" && isAllowed} enableReorder={appliedDisplayFilters?.order_by === "sort_order" && isAllowed}
enableAddBlock={isAllowed} enableAddBlock={isAllowed}
enableSelection={isAllowed} enableSelection={false}
quickAdd={ quickAdd={
enableIssueCreation && isAllowed ? ( enableIssueCreation && isAllowed ? (
<GanttQuickAddIssueForm quickAddCallback={issues.quickAddIssue} viewId={viewId} /> <GanttQuickAddIssueForm quickAddCallback={issues.quickAddIssue} viewId={viewId} />

View File

@ -102,6 +102,7 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
const isIssueSelected = selectionHelpers.getIsEntitySelected(issue.id); const isIssueSelected = selectionHelpers.getIsEntitySelected(issue.id);
const isIssueActive = selectionHelpers.getIsEntityActive(issue.id); const isIssueActive = selectionHelpers.getIsEntityActive(issue.id);
const isSubIssue = nestingLevel !== 0; const isSubIssue = nestingLevel !== 0;
const canSelectIssues = canEditIssueProperties && !selectionHelpers.isSelectionDisabled;
const marginLeft = `${spacingLeft}px`; const marginLeft = `${spacingLeft}px`;
@ -149,7 +150,7 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
<div className="flex flex-grow items-center gap-0.5 truncate"> <div className="flex flex-grow items-center gap-0.5 truncate">
<div className="flex items-center gap-1" style={isSubIssue ? { marginLeft } : {}}> <div className="flex items-center gap-1" style={isSubIssue ? { marginLeft } : {}}>
{/* select checkbox */} {/* select checkbox */}
{projectId && canEditIssueProperties && ( {projectId && canSelectIssues && (
<Tooltip <Tooltip
tooltipContent={ tooltipContent={
<> <>

View File

@ -134,7 +134,7 @@ const GroupByList: React.FC<IGroupByList> = observer((props) => {
return ( return (
<div className="relative size-full flex flex-col"> <div className="relative size-full flex flex-col">
{groups && ( {groups && (
<MultipleSelectGroup containerRef={containerRef} entities={entities}> <MultipleSelectGroup containerRef={containerRef} entities={entities} disabled>
{(helpers) => ( {(helpers) => (
<> <>
<div <div

View File

@ -57,7 +57,7 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
const existingIssuesListModalPayload = moduleId ? { module: moduleId.toString() } : { cycle: true }; const existingIssuesListModalPayload = moduleId ? { module: moduleId.toString() } : { cycle: true };
const isGroupSelectionEmpty = selectionHelpers.isGroupSelected(groupID) === "empty"; const isGroupSelectionEmpty = selectionHelpers.isGroupSelected(groupID) === "empty";
// auth // auth
const canSelectIssues = canEditProperties(projectId?.toString()); const canSelectIssues = canEditProperties(projectId?.toString()) && !selectionHelpers.isSelectionDisabled;
const handleAddIssuesToView = async (data: ISearchIssueResponse[]) => { const handleAddIssuesToView = async (data: ISearchIssueResponse[]) => {
if (!workspaceSlug || !projectId) return; if (!workspaceSlug || !projectId) return;
@ -83,7 +83,7 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
return ( return (
<> <>
<div className="group/list-header relative w-full flex-shrink-0 flex items-center gap-2 py-1.5"> <div className="group/list-header relative w-full flex-shrink-0 flex items-center gap-2 py-1.5 pl-1">
{canSelectIssues && ( {canSelectIssues && (
<div className="flex-shrink-0 flex items-center w-3.5"> <div className="flex-shrink-0 flex items-center w-3.5">
<MultipleSelectGroupAction <MultipleSelectGroupAction

View File

@ -222,6 +222,8 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
const subIssuesCount = issueDetail?.sub_issues_count ?? 0; const subIssuesCount = issueDetail?.sub_issues_count ?? 0;
const isIssueSelected = selectionHelpers.getIsEntitySelected(issueDetail.id); const isIssueSelected = selectionHelpers.getIsEntitySelected(issueDetail.id);
const canSelectIssues = !disableUserActions && !selectionHelpers.isSelectionDisabled;
//TODO: add better logic. This is to have a min width for ID/Key based on the length of project identifier //TODO: add better logic. This is to have a min width for ID/Key based on the length of project identifier
const keyMinWidth = (getProjectIdentifierById(issueDetail.project_id)?.length ?? 0 + 5) * 7; const keyMinWidth = (getProjectIdentifierById(issueDetail.project_id)?.length ?? 0 + 5) * 7;
@ -250,7 +252,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
> >
<div className="flex items-center gap-0.5 min-w-min py-2.5 pl-2"> <div className="flex items-center gap-0.5 min-w-min py-2.5 pl-2">
{/* select checkbox */} {/* select checkbox */}
{projectId && !disableUserActions && ( {projectId && canSelectIssues && (
<Tooltip <Tooltip
tooltipContent={ tooltipContent={
<> <>

View File

@ -38,7 +38,7 @@ export const SpreadsheetHeader = observer((props: Props) => {
// derived values // derived values
const isGroupSelectionEmpty = selectionHelpers.isGroupSelected(SPREADSHEET_SELECT_GROUP) === "empty"; const isGroupSelectionEmpty = selectionHelpers.isGroupSelected(SPREADSHEET_SELECT_GROUP) === "empty";
// auth // auth
const canSelectIssues = canEditProperties(projectId?.toString()); const canSelectIssues = canEditProperties(projectId?.toString()) && !selectionHelpers.isSelectionDisabled;
return ( return (
<thead className="sticky top-0 left-0 z-[12] border-b-[0.5px] border-custom-border-100"> <thead className="sticky top-0 left-0 z-[12] border-b-[0.5px] border-custom-border-100">

View File

@ -81,6 +81,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
entities={{ entities={{
[SPREADSHEET_SELECT_GROUP]: issueIds, [SPREADSHEET_SELECT_GROUP]: issueIds,
}} }}
disabled
> >
{(helpers) => ( {(helpers) => (
<> <>

View File

@ -10,6 +10,7 @@ export type TEntityDetails = {
type Props = { type Props = {
containerRef: React.MutableRefObject<HTMLElement | null>; containerRef: React.MutableRefObject<HTMLElement | null>;
disabled: boolean;
entities: Record<string, string[]>; // { groupID: entityIds[] } entities: Record<string, string[]>; // { groupID: entityIds[] }
}; };
@ -25,10 +26,11 @@ export type TSelectionHelper = {
getIsEntityActive: (entityID: string) => boolean; getIsEntityActive: (entityID: string) => boolean;
handleGroupClick: (groupID: string) => void; handleGroupClick: (groupID: string) => void;
isGroupSelected: (groupID: string) => "empty" | "partial" | "complete"; isGroupSelected: (groupID: string) => "empty" | "partial" | "complete";
isSelectionDisabled: boolean;
}; };
export const useMultipleSelect = (props: Props) => { export const useMultipleSelect = (props: Props) => {
const { containerRef, entities } = props; const { containerRef, disabled, entities } = props;
// router // router
const router = useRouter(); const router = useRouter();
// store hooks // store hooks
@ -97,6 +99,8 @@ export const useMultipleSelect = (props: Props) => {
const handleActiveEntityChange = useCallback( const handleActiveEntityChange = useCallback(
(entityDetails: TEntityDetails | null, shouldScroll: boolean = true) => { (entityDetails: TEntityDetails | null, shouldScroll: boolean = true) => {
if (disabled) return;
if (!entityDetails) { if (!entityDetails) {
updateActiveEntityDetails(null); updateActiveEntityDetails(null);
updatePreviousActiveEntity(null); updatePreviousActiveEntity(null);
@ -134,6 +138,7 @@ export const useMultipleSelect = (props: Props) => {
}, },
[ [
containerRef, containerRef,
disabled,
getPreviousAndNextEntities, getPreviousAndNextEntities,
updateActiveEntityDetails, updateActiveEntityDetails,
updateNextActiveEntity, updateNextActiveEntity,
@ -147,6 +152,8 @@ export const useMultipleSelect = (props: Props) => {
shouldScroll: boolean = true, shouldScroll: boolean = true,
forceAction: "force-add" | "force-remove" | null = null forceAction: "force-add" | "force-remove" | null = null
) => { ) => {
if (disabled) return;
if (Array.isArray(entityDetails)) { if (Array.isArray(entityDetails)) {
bulkUpdateSelectedEntityDetails(entityDetails, forceAction === "force-add" ? "add" : "remove"); bulkUpdateSelectedEntityDetails(entityDetails, forceAction === "force-add" ? "add" : "remove");
if (forceAction === "force-add" && entityDetails.length > 0) { if (forceAction === "force-add" && entityDetails.length > 0) {
@ -176,7 +183,13 @@ export const useMultipleSelect = (props: Props) => {
handleActiveEntityChange(entityDetails, shouldScroll); handleActiveEntityChange(entityDetails, shouldScroll);
} }
}, },
[bulkUpdateSelectedEntityDetails, getIsEntitySelected, handleActiveEntityChange, updateSelectedEntityDetails] [
bulkUpdateSelectedEntityDetails,
disabled,
getIsEntitySelected,
handleActiveEntityChange,
updateSelectedEntityDetails,
]
); );
/** /**
@ -187,6 +200,7 @@ export const useMultipleSelect = (props: Props) => {
*/ */
const handleEntityClick = useCallback( const handleEntityClick = useCallback(
(e: React.MouseEvent, entityID: string, groupID: string) => { (e: React.MouseEvent, entityID: string, groupID: string) => {
if (disabled) return;
const lastSelectedEntityDetails = getLastSelectedEntityDetails(); const lastSelectedEntityDetails = getLastSelectedEntityDetails();
if (e.shiftKey && lastSelectedEntityDetails) { if (e.shiftKey && lastSelectedEntityDetails) {
const currentEntityIndex = entitiesList.findIndex((entity) => entity?.entityID === entityID); const currentEntityIndex = entitiesList.findIndex((entity) => entity?.entityID === entityID);
@ -223,7 +237,7 @@ export const useMultipleSelect = (props: Props) => {
handleEntitySelection({ entityID, groupID }, false); handleEntitySelection({ entityID, groupID }, false);
}, },
[entitiesList, handleEntitySelection, getLastSelectedEntityDetails] [disabled, entitiesList, handleEntitySelection, getLastSelectedEntityDetails]
); );
/** /**
@ -248,15 +262,19 @@ export const useMultipleSelect = (props: Props) => {
*/ */
const handleGroupClick = useCallback( const handleGroupClick = useCallback(
(groupID: string) => { (groupID: string) => {
if (disabled) return;
const groupEntities = entitiesList.filter((entity) => entity.groupID === groupID); const groupEntities = entitiesList.filter((entity) => entity.groupID === groupID);
const groupSelectionStatus = isGroupSelected(groupID); const groupSelectionStatus = isGroupSelected(groupID);
handleEntitySelection(groupEntities, false, groupSelectionStatus === "empty" ? "force-add" : "force-remove"); handleEntitySelection(groupEntities, false, groupSelectionStatus === "empty" ? "force-add" : "force-remove");
}, },
[entitiesList, handleEntitySelection, isGroupSelected] [disabled, entitiesList, handleEntitySelection, isGroupSelected]
); );
// clear selection on escape key press // clear selection on escape key press
useEffect(() => { useEffect(() => {
if (disabled) return;
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") clearSelection(); if (e.key === "Escape") clearSelection();
}; };
@ -265,10 +283,12 @@ export const useMultipleSelect = (props: Props) => {
return () => { return () => {
window.removeEventListener("keydown", handleKeyDown); window.removeEventListener("keydown", handleKeyDown);
}; };
}, [clearSelection]); }, [clearSelection, disabled]);
// select entities on shift + arrow up/down key press // select entities on shift + arrow up/down key press
useEffect(() => { useEffect(() => {
if (disabled) return;
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
if (!e.shiftKey) return; if (!e.shiftKey) return;
@ -291,6 +311,7 @@ export const useMultipleSelect = (props: Props) => {
window.removeEventListener("keydown", handleKeyDown); window.removeEventListener("keydown", handleKeyDown);
}; };
}, [ }, [
disabled,
getActiveEntityDetails, getActiveEntityDetails,
handleEntitySelection, handleEntitySelection,
getLastSelectedEntityDetails, getLastSelectedEntityDetails,
@ -299,6 +320,8 @@ export const useMultipleSelect = (props: Props) => {
]); ]);
useEffect(() => { useEffect(() => {
if (disabled) return;
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
if (e.shiftKey) return; if (e.shiftKey) return;
const activeEntityDetails = getActiveEntityDetails(); const activeEntityDetails = getActiveEntityDetails();
@ -331,7 +354,7 @@ export const useMultipleSelect = (props: Props) => {
return () => { return () => {
window.removeEventListener("keydown", handleKeyDown); window.removeEventListener("keydown", handleKeyDown);
}; };
}, [getActiveEntityDetails, entitiesList, groups, getPreviousAndNextEntities, handleActiveEntityChange]); }, [disabled, getActiveEntityDetails, entitiesList, groups, getPreviousAndNextEntities, handleActiveEntityChange]);
// clear selection on route change // clear selection on route change
useEffect(() => { useEffect(() => {
@ -346,6 +369,7 @@ export const useMultipleSelect = (props: Props) => {
// when entities list change, remove entityIds from the selected entities array, which are not present in the new list // when entities list change, remove entityIds from the selected entities array, which are not present in the new list
useEffect(() => { useEffect(() => {
if (disabled) return;
selectedEntityIds.map((entityID) => { selectedEntityIds.map((entityID) => {
const isEntityPresent = entitiesList.find((en) => en.entityID === entityID); const isEntityPresent = entitiesList.find((en) => en.entityID === entityID);
if (!isEntityPresent) { if (!isEntityPresent) {
@ -355,7 +379,7 @@ export const useMultipleSelect = (props: Props) => {
} }
} }
}); });
}, [entitiesList, getEntityDetailsFromEntityID, handleEntitySelection, selectedEntityIds]); }, [disabled, entitiesList, getEntityDetailsFromEntityID, handleEntitySelection, selectedEntityIds]);
/** /**
* @description helper functions for selection * @description helper functions for selection
@ -368,8 +392,17 @@ export const useMultipleSelect = (props: Props) => {
getIsEntityActive, getIsEntityActive,
handleGroupClick, handleGroupClick,
isGroupSelected, isGroupSelected,
isSelectionDisabled: disabled,
}), }),
[clearSelection, getIsEntityActive, getIsEntitySelected, handleEntityClick, handleGroupClick, isGroupSelected] [
clearSelection,
disabled,
getIsEntityActive,
getIsEntitySelected,
handleEntityClick,
handleGroupClick,
isGroupSelected,
]
); );
return helpers; return helpers;