mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Virtualization like core changes with intersection observer
This commit is contained in:
parent
7d08a57be6
commit
08487fb7d4
9
packages/types/src/issues.d.ts
vendored
9
packages/types/src/issues.d.ts
vendored
@ -221,3 +221,12 @@ export interface IGroupByColumn {
|
||||
export interface IIssueMap {
|
||||
[key: string]: TIssue;
|
||||
}
|
||||
|
||||
export interface IIssueListRow {
|
||||
id: string;
|
||||
groupId: string;
|
||||
type: "HEADER" | "NO_ISSUES" | "QUICK_ADD" | "ISSUE";
|
||||
name?: string;
|
||||
icon?: ReactElement | undefined;
|
||||
payload?: Partial<TIssue>;
|
||||
}
|
||||
|
74
web/components/core/render-if-visible-HOC.tsx
Normal file
74
web/components/core/render-if-visible-HOC.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import { cn } from "helpers/common.helper";
|
||||
import React, { useState, useRef, useEffect, ReactNode, MutableRefObject } from "react";
|
||||
|
||||
type Props = {
|
||||
defaultHeight?: number;
|
||||
verticalOffset?: number;
|
||||
horizonatlOffset?: number;
|
||||
root?: MutableRefObject<HTMLElement | null>;
|
||||
children: ReactNode;
|
||||
as?: keyof JSX.IntrinsicElements;
|
||||
classNames?: string;
|
||||
alwaysRender?: boolean;
|
||||
};
|
||||
|
||||
const RenderIfVisible: React.FC<Props> = (props) => {
|
||||
const {
|
||||
defaultHeight = 300,
|
||||
root,
|
||||
verticalOffset = 50,
|
||||
horizonatlOffset = 0,
|
||||
as = "div",
|
||||
children,
|
||||
classNames = "",
|
||||
alwaysRender = false,
|
||||
...others
|
||||
} = props;
|
||||
const [shouldVisible, setShouldVisible] = useState<boolean>(alwaysRender);
|
||||
const placeholderHeight = useRef<number>(defaultHeight);
|
||||
const intersectionRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
const isVisible = alwaysRender || shouldVisible;
|
||||
|
||||
// Set visibility with intersection observer
|
||||
useEffect(() => {
|
||||
if (intersectionRef.current) {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (typeof window !== undefined && window.requestIdleCallback) {
|
||||
window.requestIdleCallback(() => setShouldVisible(entries[0].isIntersecting), {
|
||||
timeout: 600,
|
||||
});
|
||||
} else {
|
||||
setShouldVisible(entries[0].isIntersecting);
|
||||
}
|
||||
},
|
||||
{
|
||||
root: root?.current,
|
||||
rootMargin: `${verticalOffset}% ${horizonatlOffset}% ${verticalOffset}% ${horizonatlOffset}%`,
|
||||
}
|
||||
);
|
||||
observer.observe(intersectionRef.current);
|
||||
return () => {
|
||||
if (intersectionRef.current) {
|
||||
observer.unobserve(intersectionRef.current);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [root?.current, intersectionRef, children]);
|
||||
|
||||
// Set height after render
|
||||
useEffect(() => {
|
||||
if (intersectionRef.current && isVisible) {
|
||||
placeholderHeight.current = intersectionRef.current.offsetHeight;
|
||||
}
|
||||
}, [isVisible, intersectionRef, alwaysRender]);
|
||||
|
||||
const child = isVisible ? <>{children}</> : null;
|
||||
const style = isVisible ? {} : { height: placeholderHeight.current, width: "100%" };
|
||||
const className = isVisible ? classNames : cn(classNames, "animate-pulse");
|
||||
|
||||
return React.createElement(as, { ref: intersectionRef, style, className }, child);
|
||||
};
|
||||
|
||||
export default RenderIfVisible;
|
@ -1,10 +1,10 @@
|
||||
import { Avatar, PriorityIcon, StateGroupIcon } from "@plane/ui";
|
||||
import { ISSUE_PRIORITIES } from "constants/issue";
|
||||
import { EIssueListRow, ISSUE_PRIORITIES } from "constants/issue";
|
||||
import { renderEmoji } from "helpers/emoji.helper";
|
||||
import { IMemberRootStore } from "store/member";
|
||||
import { IProjectStore } from "store/project/project.store";
|
||||
import { IStateStore } from "store/state.store";
|
||||
import { GroupByColumnTypes, IGroupByColumn } from "@plane/types";
|
||||
import { GroupByColumnTypes, IGroupByColumn, IIssueListRow, TGroupedIssues, TUnGroupedIssues } from "@plane/types";
|
||||
import { STATE_GROUPS } from "constants/state";
|
||||
import { ILabelStore } from "store/label.store";
|
||||
|
||||
@ -155,3 +155,47 @@ const getCreatedByColumns = (member: IMemberRootStore) => {
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export function getIssueFlatList(
|
||||
groups: IGroupByColumn[],
|
||||
issueIds: TGroupedIssues | TUnGroupedIssues,
|
||||
showEmptyGroup: boolean
|
||||
): IIssueListRow[] | undefined {
|
||||
let list: IIssueListRow[] = [];
|
||||
|
||||
if (Array.isArray(issueIds)) {
|
||||
return wrapIssuesWithHeaderAndFooter(groups[0], issueIds, showEmptyGroup);
|
||||
}
|
||||
|
||||
for (const group of groups) {
|
||||
const groupList = wrapIssuesWithHeaderAndFooter(group, issueIds[group.id], showEmptyGroup);
|
||||
|
||||
if (!groupList) continue;
|
||||
|
||||
list = list.concat(groupList);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
function wrapIssuesWithHeaderAndFooter(
|
||||
group: IGroupByColumn,
|
||||
issueIds: string[],
|
||||
showEmptyGroup: boolean
|
||||
): IIssueListRow[] | undefined {
|
||||
const header: IIssueListRow = { ...group, groupId: group.id, type: EIssueListRow.HEADER };
|
||||
const quickAdd: IIssueListRow = { ...group, groupId: group.id, type: EIssueListRow.QUICK_ADD };
|
||||
if (issueIds && issueIds.length > 0) {
|
||||
const list: IIssueListRow[] = [header];
|
||||
|
||||
for (const issueId of issueIds) {
|
||||
list.push({ id: issueId, groupId: group.id, type: EIssueListRow.ISSUE });
|
||||
}
|
||||
|
||||
list.push(quickAdd);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
if (showEmptyGroup) return [header, { id: group.id, groupId: group.id, type: EIssueListRow.NO_ISSUES }, quickAdd];
|
||||
}
|
@ -412,6 +412,13 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: {
|
||||
},
|
||||
};
|
||||
|
||||
export enum EIssueListRow {
|
||||
HEADER = "HEADER",
|
||||
ISSUE = "ISSUE",
|
||||
NO_ISSUES = "NO_ISSUES",
|
||||
QUICK_ADD = "QUICK_ADD",
|
||||
}
|
||||
|
||||
export const getValueFromObject = (object: Object, key: string): string | number | boolean | null => {
|
||||
const keys = key ? key.split(".") : [];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user