mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
add logic to render all the issues at once
This commit is contained in:
parent
76808e6dc9
commit
73ffed3fd0
@ -10,6 +10,10 @@ type Props = {
|
|||||||
as?: keyof JSX.IntrinsicElements;
|
as?: keyof JSX.IntrinsicElements;
|
||||||
classNames?: string;
|
classNames?: string;
|
||||||
alwaysRender?: boolean;
|
alwaysRender?: boolean;
|
||||||
|
getShouldRender?: (index: number) => boolean;
|
||||||
|
updateRenderTracker?: (index: number, isVisble: boolean) => void;
|
||||||
|
placeholderChildren?: ReactNode;
|
||||||
|
index: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const RenderIfVisible: React.FC<Props> = (props) => {
|
const RenderIfVisible: React.FC<Props> = (props) => {
|
||||||
@ -22,14 +26,26 @@ const RenderIfVisible: React.FC<Props> = (props) => {
|
|||||||
children,
|
children,
|
||||||
classNames = "",
|
classNames = "",
|
||||||
alwaysRender = false,
|
alwaysRender = false,
|
||||||
...others
|
getShouldRender,
|
||||||
|
updateRenderTracker,
|
||||||
|
placeholderChildren = null,
|
||||||
|
index,
|
||||||
} = props;
|
} = props;
|
||||||
const [shouldVisible, setShouldVisible] = useState<boolean>(alwaysRender);
|
const defaultVisible = !!getShouldRender && getShouldRender(index);
|
||||||
|
const [shouldVisible, setShouldVisible] = useState<boolean>(alwaysRender || defaultVisible);
|
||||||
const placeholderHeight = useRef<number>(defaultHeight);
|
const placeholderHeight = useRef<number>(defaultHeight);
|
||||||
const intersectionRef = useRef<HTMLElement | null>(null);
|
const intersectionRef = useRef<HTMLElement | null>(null);
|
||||||
|
|
||||||
const isVisible = alwaysRender || shouldVisible;
|
const isVisible = alwaysRender || shouldVisible;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (getShouldRender && getShouldRender(index)) setShouldVisible(true);
|
||||||
|
}, [getShouldRender, index]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateRenderTracker && updateRenderTracker(index, shouldVisible);
|
||||||
|
}, [index, shouldVisible]);
|
||||||
|
|
||||||
// Set visibility with intersection observer
|
// Set visibility with intersection observer
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (intersectionRef.current) {
|
if (intersectionRef.current) {
|
||||||
@ -64,9 +80,9 @@ const RenderIfVisible: React.FC<Props> = (props) => {
|
|||||||
}
|
}
|
||||||
}, [isVisible, intersectionRef, alwaysRender]);
|
}, [isVisible, intersectionRef, alwaysRender]);
|
||||||
|
|
||||||
const child = isVisible ? <>{children}</> : null;
|
const child = isVisible ? <>{children}</> : placeholderChildren;
|
||||||
const style = isVisible ? {} : { height: placeholderHeight.current, width: "100%" };
|
const style = isVisible ? {} : { height: placeholderHeight.current, width: "100%" };
|
||||||
const className = isVisible ? classNames : cn(classNames, "animate-pulse");
|
const className = isVisible ? classNames : cn(classNames, "animate-pulse bg-custom-background-80");
|
||||||
|
|
||||||
return React.createElement(as, { ref: intersectionRef, style, className }, child);
|
return React.createElement(as, { ref: intersectionRef, style, className }, child);
|
||||||
};
|
};
|
||||||
|
@ -139,6 +139,7 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = memo((props) => {
|
|||||||
defaultHeight={100}
|
defaultHeight={100}
|
||||||
horizonatlOffset={50}
|
horizonatlOffset={50}
|
||||||
alwaysRender={snapshot.isDragging}
|
alwaysRender={snapshot.isDragging}
|
||||||
|
index={index}
|
||||||
>
|
>
|
||||||
<KanbanIssueDetailsBlock
|
<KanbanIssueDetailsBlock
|
||||||
issue={issue}
|
issue={issue}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
import { useRef } from "react";
|
||||||
// components
|
// components
|
||||||
import { IssueBlock, IssueBlocksList, ListQuickAddIssueForm } from "components/issues";
|
import { IssueBlock, ListQuickAddIssueForm } from "components/issues";
|
||||||
|
import { HeaderGroupByCard } from "./headers/group-by-card";
|
||||||
|
import RenderIfVisible from "components/core/render-if-visible-HOC";
|
||||||
// hooks
|
// hooks
|
||||||
import { useLabel, useMember, useProject, useProjectState } from "hooks/store";
|
import { useLabel, useMember, useProject, useProjectState } from "hooks/store";
|
||||||
// types
|
// types
|
||||||
@ -14,11 +17,8 @@ import {
|
|||||||
} from "@plane/types";
|
} from "@plane/types";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
// constants
|
// constants
|
||||||
import { HeaderGroupByCard } from "./headers/group-by-card";
|
|
||||||
import { getGroupByColumns, getIssueFlatList } from "../utils";
|
|
||||||
import { EIssueListRow, TCreateModalStoreTypes } from "constants/issue";
|
import { EIssueListRow, TCreateModalStoreTypes } from "constants/issue";
|
||||||
import { useRef } from "react";
|
import { getGroupByColumns, getIssueFlatList } from "../utils";
|
||||||
import RenderIfVisible from "components/core/render-if-visible-HOC";
|
|
||||||
|
|
||||||
export interface IGroupByList {
|
export interface IGroupByList {
|
||||||
issueIds: TGroupedIssues | TUnGroupedIssues | any;
|
issueIds: TGroupedIssues | TUnGroupedIssues | any;
|
||||||
@ -108,7 +108,7 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
|||||||
<div ref={containerRef} className="relative overflow-auto h-full w-full">
|
<div ref={containerRef} className="relative overflow-auto h-full w-full">
|
||||||
{list &&
|
{list &&
|
||||||
list.length > 0 &&
|
list.length > 0 &&
|
||||||
list.map((listRow: IIssueListRow) => {
|
list.map((listRow: IIssueListRow, index) => {
|
||||||
switch (listRow.type) {
|
switch (listRow.type) {
|
||||||
case EIssueListRow.HEADER:
|
case EIssueListRow.HEADER:
|
||||||
return (
|
return (
|
||||||
@ -159,6 +159,7 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
|||||||
defaultHeight={45}
|
defaultHeight={45}
|
||||||
root={containerRef}
|
root={containerRef}
|
||||||
classNames={"relative border border-transparent border-b-custom-border-200 last:border-b-transparent"}
|
classNames={"relative border border-transparent border-b-custom-border-200 last:border-b-transparent"}
|
||||||
|
index={index}
|
||||||
>
|
>
|
||||||
<IssueBlock
|
<IssueBlock
|
||||||
issueId={listRow.id}
|
issueId={listRow.id}
|
||||||
@ -237,5 +238,3 @@ export const List: React.FC<IList> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,9 +5,8 @@ import { EIssueActions } from "../types";
|
|||||||
//components
|
//components
|
||||||
import { SpreadsheetIssueRow } from "./issue-row";
|
import { SpreadsheetIssueRow } from "./issue-row";
|
||||||
import { SpreadsheetHeader } from "./spreadsheet-header";
|
import { SpreadsheetHeader } from "./spreadsheet-header";
|
||||||
import { MutableRefObject, useRef } from "react";
|
import { MutableRefObject } from "react";
|
||||||
import RenderIfVisible from "components/core/render-if-visible-HOC";
|
import RenderIfVisible from "components/core/render-if-visible-HOC";
|
||||||
import { cn } from "helpers/common.helper";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
displayProperties: IIssueDisplayProperties;
|
displayProperties: IIssueDisplayProperties;
|
||||||
@ -49,8 +48,15 @@ export const SpreadsheetTable = observer((props: Props) => {
|
|||||||
isEstimateEnabled={isEstimateEnabled}
|
isEstimateEnabled={isEstimateEnabled}
|
||||||
/>
|
/>
|
||||||
<tbody>
|
<tbody>
|
||||||
{issueIds.map((id) => (
|
{issueIds.map((id, index) => (
|
||||||
<RenderIfVisible key={id} as="tr" defaultHeight={44.5} root={containerRef}>
|
<RenderIfVisible
|
||||||
|
key={id}
|
||||||
|
as="tr"
|
||||||
|
defaultHeight={44.5}
|
||||||
|
root={containerRef}
|
||||||
|
placeholderChildren={<td colSpan={100} className="border-b-[0.5px]" />}
|
||||||
|
index={index}
|
||||||
|
>
|
||||||
<SpreadsheetIssueRow
|
<SpreadsheetIssueRow
|
||||||
issueId={id}
|
issueId={id}
|
||||||
displayProperties={displayProperties}
|
displayProperties={displayProperties}
|
||||||
|
32
web/hooks/use-virtualization-helper.tsx
Normal file
32
web/hooks/use-virtualization-helper.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { useRef } from "react";
|
||||||
|
|
||||||
|
const initializeRows = (rows: number) => {
|
||||||
|
const initailizedRows: { [id: number]: boolean } = {};
|
||||||
|
for (let i = 0; i < rows; ++i) initailizedRows[i] = true;
|
||||||
|
return initailizedRows;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMinMaxIndex = (renderedRows: { [id: number]: boolean }) => {
|
||||||
|
const rowIndexes = Object.keys(renderedRows).sort((a: string, b: string) => parseInt(a) - parseInt(b));
|
||||||
|
|
||||||
|
return { min: parseInt(rowIndexes[0]), max: parseInt(rowIndexes[rowIndexes.length - 1]) };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useVirtualizationHelper = (defaultNumberOfRows: number) => {
|
||||||
|
const renderedRows = useRef<{ [id: number]: boolean }>(initializeRows(defaultNumberOfRows));
|
||||||
|
|
||||||
|
const getShouldRender = (index: number) => {
|
||||||
|
const { min, max } = getMinMaxIndex(renderedRows.current);
|
||||||
|
return min !== undefined && max !== undefined && index !== undefined && index >= min && max >= index;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateRenderTracker = (index: number, isVisble: boolean) => {
|
||||||
|
if (isVisble) renderedRows.current[index] = true;
|
||||||
|
else if (renderedRows.current[index]) delete renderedRows.current[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getShouldRender,
|
||||||
|
updateRenderTracker,
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user