chore: space folders (#8707)
* chore: change the space folders structure * fix: format
This commit is contained in:
parent
be8836642a
commit
7fb6696c67
154 changed files with 30 additions and 197 deletions
|
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { MutableRefObject } from "react";
|
||||
import { forwardRef, useCallback, useRef, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
//types
|
||||
import type {
|
||||
TGroupedIssues,
|
||||
IIssueDisplayProperties,
|
||||
TSubGroupedIssues,
|
||||
TIssueGroupByOptions,
|
||||
TPaginationData,
|
||||
TLoader,
|
||||
} from "@plane/types";
|
||||
import { cn } from "@plane/utils";
|
||||
// hooks
|
||||
import { useIntersectionObserver } from "@/hooks/use-intersection-observer";
|
||||
// local imports
|
||||
import { KanbanIssueBlocksList } from "./blocks-list";
|
||||
|
||||
interface IKanbanGroup {
|
||||
groupId: string;
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
subGroupBy: TIssueGroupByOptions | undefined;
|
||||
subGroupId: string;
|
||||
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
|
||||
getGroupIssueCount: (
|
||||
groupId: string | undefined,
|
||||
subGroupId: string | undefined,
|
||||
isSubGroupCumulative: boolean
|
||||
) => number | undefined;
|
||||
getPaginationData: (groupId: string | undefined, subGroupId: string | undefined) => TPaginationData | undefined;
|
||||
getIssueLoader: (groupId?: string, subGroupId?: string) => TLoader;
|
||||
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
// Loader components
|
||||
const KanbanIssueBlockLoader = forwardRef(function KanbanIssueBlockLoader(
|
||||
props: Record<string, unknown>,
|
||||
ref: React.ForwardedRef<HTMLSpanElement>
|
||||
) {
|
||||
return (
|
||||
<span ref={ref} className="m-1.5 block h-28 animate-pulse rounded-sm bg-[var(--illustration-fill-quaternary)]" />
|
||||
);
|
||||
});
|
||||
KanbanIssueBlockLoader.displayName = "KanbanIssueBlockLoader";
|
||||
|
||||
export const KanbanGroup = observer(function KanbanGroup(props: IKanbanGroup) {
|
||||
const {
|
||||
groupId,
|
||||
subGroupId,
|
||||
subGroupBy,
|
||||
displayProperties,
|
||||
groupedIssueIds,
|
||||
loadMoreIssues,
|
||||
getGroupIssueCount,
|
||||
getPaginationData,
|
||||
getIssueLoader,
|
||||
scrollableContainerRef,
|
||||
} = props;
|
||||
|
||||
// hooks
|
||||
const [intersectionElement, setIntersectionElement] = useState<HTMLSpanElement | null>(null);
|
||||
const columnRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const containerRef = subGroupBy && scrollableContainerRef ? scrollableContainerRef : columnRef;
|
||||
|
||||
const loadMoreIssuesInThisGroup = useCallback(() => {
|
||||
loadMoreIssues(groupId, subGroupId === "null" ? undefined : subGroupId);
|
||||
}, [loadMoreIssues, groupId, subGroupId]);
|
||||
|
||||
const isPaginating = !!getIssueLoader(groupId, subGroupId);
|
||||
|
||||
useIntersectionObserver(
|
||||
containerRef,
|
||||
isPaginating ? null : intersectionElement,
|
||||
loadMoreIssuesInThisGroup,
|
||||
`0% 100% 100% 100%`
|
||||
);
|
||||
|
||||
const isSubGroup = !!subGroupId && subGroupId !== "null";
|
||||
|
||||
const issueIds = isSubGroup
|
||||
? ((groupedIssueIds as TSubGroupedIssues)?.[groupId]?.[subGroupId] ?? [])
|
||||
: ((groupedIssueIds as TGroupedIssues)?.[groupId] ?? []);
|
||||
|
||||
const groupIssueCount = getGroupIssueCount(groupId, subGroupId, false) ?? 0;
|
||||
const nextPageResults = getPaginationData(groupId, subGroupId)?.nextPageResults;
|
||||
|
||||
const loadMore = isPaginating ? (
|
||||
<KanbanIssueBlockLoader />
|
||||
) : (
|
||||
<div
|
||||
className="w-full cursor-pointer p-3 text-13 font-medium text-accent-primary hover:text-accent-secondary hover:underline"
|
||||
onClick={loadMoreIssuesInThisGroup}
|
||||
role="button"
|
||||
>
|
||||
{" "}
|
||||
Load More ↓
|
||||
</div>
|
||||
);
|
||||
|
||||
const shouldLoadMore = nextPageResults === undefined ? issueIds?.length < groupIssueCount : !!nextPageResults;
|
||||
|
||||
return (
|
||||
<div
|
||||
id={`${groupId}__${subGroupId}`}
|
||||
className={cn("relative h-full min-h-[120px] transition-all", { "vertical-scrollbar scrollbar-md": !subGroupBy })}
|
||||
ref={columnRef}
|
||||
>
|
||||
<KanbanIssueBlocksList
|
||||
subGroupId={subGroupId}
|
||||
groupId={groupId}
|
||||
issueIds={issueIds || []}
|
||||
displayProperties={displayProperties}
|
||||
scrollableContainerRef={scrollableContainerRef}
|
||||
/>
|
||||
|
||||
{shouldLoadMore &&
|
||||
(isSubGroup ? (
|
||||
<>{loadMore}</>
|
||||
) : (
|
||||
<div className="flex flex-col gap-2">
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<KanbanIssueBlockLoader key={index} />
|
||||
))}
|
||||
<KanbanIssueBlockLoader ref={setIntersectionElement} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue