Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions web/core/components/core/render-if-visible-HOC.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Props = {
classNames?: string;
placeholderChildren?: ReactNode;
defaultValue?: boolean;
shouldRecordHeights?: boolean;
useIdletime?: boolean;
};

Expand All @@ -23,6 +24,8 @@ const RenderIfVisible: React.FC<Props> = (props) => {
as = "div",
children,
classNames = "",
shouldRecordHeights = true,
//placeholder children
placeholderChildren = null, //placeholder children
defaultValue = false,
useIdletime = false,
Expand Down Expand Up @@ -64,15 +67,15 @@ const RenderIfVisible: React.FC<Props> = (props) => {

//Set height after render
useEffect(() => {
if (intersectionRef.current && isVisible) {
placeholderHeight.current = `${intersectionRef.current.offsetHeight}px`;
if (intersectionRef.current && isVisible && shouldRecordHeights) {
window.requestIdleCallback(() => {
if (intersectionRef.current) placeholderHeight.current = `${intersectionRef.current.offsetHeight}px`;
});
}
}, [isVisible, intersectionRef]);
}, [isVisible, intersectionRef, shouldRecordHeights]);

const child = isVisible ? <>{children}</> : placeholderChildren;
const style: { width?: string; height?: string } = isVisible
? {}
: { height: placeholderHeight.current, width: "100%" };
const style = isVisible || placeholderChildren ? {} : { height: placeholderHeight.current, width: "100%" };
const className = isVisible || placeholderChildren ? classNames : cn(classNames, "bg-custom-background-80");

return React.createElement(as, { ref: intersectionRef, style, className }, child);
Expand Down
9 changes: 8 additions & 1 deletion web/core/components/issues/issue-layouts/list/block-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import { IIssueDisplayProperties, TIssue, TIssueMap } from "@plane/types";
import { DropIndicator } from "@plane/ui";
import RenderIfVisible from "@/components/core/render-if-visible-HOC";
import { IssueBlock } from "@/components/issues/issue-layouts/list";
import { ListLoaderItemRow } from "@/components/ui";
// hooks
import { useIssueDetail } from "@/hooks/store";
import { TSelectionHelper } from "@/hooks/use-multiple-select";
import { usePlatformOS } from "@/hooks/use-platform-os";
// types
import { HIGHLIGHT_CLASS, getIssueBlockId, isIssueNew } from "../utils";
import { TRenderQuickActions } from "./list-view-types";
Expand Down Expand Up @@ -66,6 +68,8 @@ export const IssueBlockRoot: FC<Props> = observer((props) => {
const [isCurrentBlockDragging, setIsCurrentBlockDragging] = useState(false);
// ref
const issueBlockRef = useRef<HTMLDivElement | null>(null);
// hooks
const { isMobile } = usePlatformOS();
// store hooks
const { subIssues: subIssuesStore } = useIssueDetail();

Expand Down Expand Up @@ -125,11 +129,14 @@ export const IssueBlockRoot: FC<Props> = observer((props) => {
<DropIndicator classNames={"absolute top-0 z-[2]"} isVisible={instruction === "DRAG_OVER"} />
<RenderIfVisible
key={`${issueId}`}
defaultHeight="3rem"
root={containerRef}
classNames={`relative ${isLastChild && !isExpanded ? "" : "border-b border-b-custom-border-200"}`}
verticalOffset={100}
defaultValue={shouldRenderByDefault || isIssueNew(issuesMap[issueId])}
placeholderChildren={
<ListLoaderItemRow shouldAnimate={false} renderForPlaceHolder={true} defaultPropertyCount={4} />
}
shouldRecordHeights={isMobile}
>
<IssueBlock
issueId={issueId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,20 @@ export const SpreadsheetIssueRow = observer((props: Props) => {
{/* first column/ issue name and key column */}
<RenderIfVisible
as="tr"
defaultHeight="calc(2.75rem - 1px)"
root={containerRef}
placeholderChildren={
<td colSpan={100} className="border-[0.5px] border-transparent border-b-custom-border-200" />
<td
colSpan={100}
className="border-[0.5px] border-transparent border-b-custom-border-200"
style={{ height: "calc(2.75rem - 1px)" }}
/>
}
classNames={cn("bg-custom-background-100 transition-[background-color]", {
"group selected-issue-row": isIssueSelected,
"border-[0.5px] border-custom-border-400": isIssueActive,
})}
verticalOffset={100}
shouldRecordHeights={false}
>
<IssueRowDetails
issueId={issueId}
Expand Down
45 changes: 38 additions & 7 deletions web/core/components/ui/loader/layouts/list-layout-loader.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,51 @@
import { Fragment, forwardRef } from "react";
import { cn } from "@plane/editor";
import { Row } from "@plane/ui";
import { getRandomInt, getRandomLength } from "../utils";

export const ListLoaderItemRow = forwardRef<HTMLDivElement>((props, ref) => (
<Row ref={ref} className="flex items-center justify-between h-11 py-3 border-b border-custom-border-200">
export const ListLoaderItemRow = forwardRef<
HTMLDivElement,
{ shouldAnimate?: boolean; renderForPlaceHolder?: boolean; defaultPropertyCount?: number }
>(({ shouldAnimate = true, renderForPlaceHolder = false, defaultPropertyCount = 6 }, ref) => (
<Row
ref={ref}
className={cn("flex items-center justify-between h-11 py-3 ", {
"bg-custom-background-100": renderForPlaceHolder,
"border-b border-custom-border-200": !renderForPlaceHolder,
})}
>
<div className="flex items-center gap-3">
<span className="h-5 w-10 bg-custom-background-80 rounded animate-pulse" />
<span className={`h-5 w-${getRandomLength(["32", "52", "72"])} bg-custom-background-80 rounded animate-pulse`} />
<span
className={cn("h-5 w-10 bg-custom-background-80 rounded", {
"animate-pulse": shouldAnimate,
"bg-custom-background-90": renderForPlaceHolder,
})}
/>
<span
className={cn(`h-5 w-${getRandomLength(["32", "52", "72"])} bg-custom-background-80 rounded`, {
"animate-pulse": shouldAnimate,
"bg-custom-background-90": renderForPlaceHolder,
})}
/>
</div>
<div className="flex items-center gap-2">
{[...Array(6)].map((_, index) => (
{[...Array(defaultPropertyCount)].map((_, index) => (
<Fragment key={index}>
{getRandomInt(1, 2) % 2 === 0 ? (
<span key={index} className="h-5 w-5 bg-custom-background-80 rounded animate-pulse" />
<span
key={index}
className={cn("h-5 w-5 bg-custom-background-80 rounded", {
"animate-pulse": shouldAnimate,
"bg-custom-background-90": renderForPlaceHolder,
})}
/>
) : (
<span className="h-5 w-16 bg-custom-background-80 rounded animate-pulse" />
<span
className={cn("h-5 w-16 bg-custom-background-80 rounded", {
"animate-pulse": shouldAnimate,
"bg-custom-background-90": renderForPlaceHolder,
})}
/>
)}
</Fragment>
))}
Expand Down
8 changes: 4 additions & 4 deletions web/core/store/issue/issue.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class IssueStore implements IIssueStore {
* @returns {void}
*/
updateIssue = (issueId: string, issue: Partial<TIssue>) => {
if (!issue || !issueId || isEmpty(this.issuesMap) || !this.issuesMap[issueId]) return;
if (!issue || !issueId || !this.issuesMap[issueId]) return;
runInAction(() => {
set(this.issuesMap, [issueId, "updated_at"], getCurrentDateTimeInISO());
Object.keys(issue).forEach((key) => {
Expand All @@ -92,7 +92,7 @@ export class IssueStore implements IIssueStore {
* @returns {void}
*/
removeIssue = (issueId: string) => {
if (!issueId || isEmpty(this.issuesMap) || !this.issuesMap[issueId]) return;
if (!issueId || !this.issuesMap[issueId]) return;
runInAction(() => {
delete this.issuesMap[issueId];
});
Expand All @@ -105,7 +105,7 @@ export class IssueStore implements IIssueStore {
* @returns {TIssue | undefined}
*/
getIssueById = computedFn((issueId: string) => {
if (!issueId || isEmpty(this.issuesMap) || !this.issuesMap[issueId]) return undefined;
if (!issueId || !this.issuesMap[issueId]) return undefined;
return this.issuesMap[issueId];
});

Expand All @@ -116,7 +116,7 @@ export class IssueStore implements IIssueStore {
* @returns {Record<string, TIssue> | undefined}
*/
getIssuesByIds = computedFn((issueIds: string[], type: "archived" | "un-archived") => {
if (!issueIds || issueIds.length <= 0 || isEmpty(this.issuesMap)) return [];
if (!issueIds || issueIds.length <= 0) return [];
const filteredIssues: TIssue[] = [];
Object.values(issueIds).forEach((issueId) => {
// if type is archived then check archived_at is not null
Expand Down