Skip to content

Commit

Permalink
Merge pull request #1077 from datopian/feature/download-loading-message
Browse files Browse the repository at this point in the history
Created property to present a component while loading the download of the file and fixed download bug on pagination
  • Loading branch information
Gutts-n committed Jan 24, 2024
2 parents 7c30842 + 6d7acd2 commit a58e2b8
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 155 deletions.
5 changes: 5 additions & 0 deletions .changeset/four-brooms-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@portaljs/components': patch
---

Created property to present a component while is loading the download of the file and fixed download bug on pagination
249 changes: 138 additions & 111 deletions packages/components/src/components/BucketViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { CSSProperties, ReactNode, useEffect, useState } from 'react';
import LoadingSpinner from './LoadingSpinner';

export interface BucketViewerFilterSearchedDataEvent {
export interface BucketViewerFilterSearchedDataEvent {
startDate?: Date;
endDate?: Date;
endDate?: Date;
}

export interface BucketViewerProps {
onLoadTotalNumberOfItems?: (total: number) => void;
domain: string;
downloadConfig?: {
downloadingMessageComponent?: ReactNode;
hoverOfTheFileComponent?: ReactNode;
};
suffix?: string;
className?: string;
downloadComponent?: ReactNode;
paginationConfig?: BucketViewerPaginationConfig;
filterState?: BucketViewerFilterSearchedDataEvent;
dataMapperFn: (rawData: Response) => Promise<BucketViewerData[]>;
Expand All @@ -34,20 +37,24 @@ export interface BucketViewerData {

export function BucketViewer({
domain,
downloadComponent,
suffix,
dataMapperFn,
className,
filterState,
paginationConfig,
onLoadTotalNumberOfItems
downloadConfig,
onLoadTotalNumberOfItems,
}: BucketViewerProps) {

suffix = suffix ?? '/';
downloadComponent = downloadComponent ?? <></>;

const { downloadingMessageComponent, hoverOfTheFileComponent } =
downloadConfig ?? {};
const [isLoading, setIsLoading] = useState<boolean>(false);
const [showDownloadComponentOnLine, setShowDownloadComponentOnLine] = useState(-1);
const [showDownloadComponentOnLine, setShowDownloadComponentOnLine] =
useState(-1);
const [showDownloadLoadingOnFile, setShowDownloadLoadingOnFile] = useState(
new Map<string, boolean>()
);
const [currentPage, setCurrentPage] = useState<number>(0);
const [lastPage, setLastPage] = useState<number>(0);
const [bucketFiles, setBucketFiles] = useState<BucketViewerData[]>([]);
Expand All @@ -65,124 +72,146 @@ export function BucketViewer({
.finally(() => setIsLoading(false));
}, [domain, suffix]);

useEffect(
() => {
if(paginationConfig) {
const startIndex = paginationConfig
? currentPage * paginationConfig.itemsPerPage
: 0;

const endIndex = paginationConfig
? startIndex + paginationConfig.itemsPerPage
: 0;

setLastPage(Math.ceil(filteredData.length / paginationConfig.itemsPerPage) - 1);
setPaginatedData(filteredData.slice(startIndex, endIndex));
}
},
[currentPage, filteredData]
);
useEffect(() => {
if (paginationConfig) {
const startIndex = paginationConfig
? currentPage * paginationConfig.itemsPerPage
: 0;

const endIndex = paginationConfig
? startIndex + paginationConfig.itemsPerPage
: 0;

setLastPage(
Math.ceil(filteredData.length / paginationConfig.itemsPerPage) - 1
);
setPaginatedData(filteredData.slice(startIndex, endIndex));
}
}, [currentPage, filteredData]);

useEffect(
() => {
if(onLoadTotalNumberOfItems) onLoadTotalNumberOfItems(filteredData.length);
},
[filteredData]
)
useEffect(() => {
if (onLoadTotalNumberOfItems) onLoadTotalNumberOfItems(filteredData.length);
}, [filteredData]);

useEffect(() => {
if(!filterState) return;

if (filterState.startDate && filterState.endDate) {
setFilteredData(bucketFiles.filter(({ dateProps }) =>
dateProps
?
dateProps.date.getTime() >= filterState.startDate.getTime()
&& dateProps.date.getTime() <= filterState.endDate.getTime()
if (!filterState) return;

if (filterState.startDate && filterState.endDate) {
setFilteredData(
bucketFiles.filter(({ dateProps }) =>
dateProps
? dateProps.date.getTime() >= filterState.startDate.getTime() &&
dateProps.date.getTime() <= filterState.endDate.getTime()
: true
));
} else if(filterState.startDate) {
setFilteredData(bucketFiles.filter(({ dateProps }) =>
dateProps ? dateProps.date.getTime() >= filterState.startDate.getTime() : true
));
} else if(filterState.endDate) {
setFilteredData(bucketFiles.filter(({ dateProps }) =>
dateProps ? dateProps.date.getTime() <= filterState.endDate.getTime() : true
));
} else {
setFilteredData(bucketFiles);
}
},
[filterState]
)
)
);
} else if (filterState.startDate) {
setFilteredData(
bucketFiles.filter(({ dateProps }) =>
dateProps
? dateProps.date.getTime() >= filterState.startDate.getTime()
: true
)
);
} else if (filterState.endDate) {
setFilteredData(
bucketFiles.filter(({ dateProps }) =>
dateProps
? dateProps.date.getTime() <= filterState.endDate.getTime()
: true
)
);
} else {
setFilteredData(bucketFiles);
}
}, [filterState]);

return isLoading ? (
<div className="w-full flex items-center justify-center h-[300px]">
<LoadingSpinner />
</div>
) : bucketFiles ? (
<>
{...(paginationConfig && bucketFiles
? paginatedData
: filteredData
)?.map((data, i) => (
<ul
onClick={() => {
const anchorId = `download_anchor_${i}`;
const a: HTMLAnchorElement =
(document.getElementById(anchorId) as HTMLAnchorElement | null) ??
document.createElement('a');
a.id = anchorId;
if (a.download) a.click();
else {
setIsLoading(true);
fetch(data.downloadFileUri)
.then((res) => res.blob())
.then((res) => {
a.href = URL.createObjectURL(res);
a.download = res.name ?? data.fileName;
document.body.appendChild(a);
a.click();
})
.finally(() => setIsLoading(false));
}
}}
key={i}
onMouseEnter={() => setShowDownloadComponentOnLine(i)}
onMouseLeave={() => setShowDownloadComponentOnLine(undefined)}
className={`${
className ??
'mb-2 border-b-[2px] border-b-[red] hover:cursor-pointer'
}`}
>
{
downloadComponent && showDownloadComponentOnLine === i
? downloadComponent
: <></>
}
<div>
<li>{data.fileName}</li>
{data.dateProps && data.dateProps.dateFormatter ? (
<li>{data.dateProps.dateFormatter(data.dateProps.date)}</li>
{...(paginationConfig && bucketFiles ? paginatedData : filteredData)?.map(
(data, i) => (
<ul
onClick={() => {
const anchorId = `download_anchor_${data.fileName} `;
const a: HTMLAnchorElement =
(document.getElementById(
anchorId
) as HTMLAnchorElement | null) ?? document.createElement('a');
a.id = anchorId;
if (a.download) a.click();
else {
setShowDownloadLoadingOnFile((lastState) => {
lastState.set(data.fileName, true);
return new Map(lastState);
});
fetch(data.downloadFileUri)
.then((res) => res.blob())
.then((res) => {
setShowDownloadLoadingOnFile((lastState) => {
lastState.set(data.fileName, false);
return new Map(lastState);
});
a.href = URL.createObjectURL(res);
a.download = res.name ?? data.fileName;
document.body.appendChild(a);
a.click();
});
}
}}
key={i}
onMouseEnter={() => setShowDownloadComponentOnLine(i)}
onMouseLeave={() => setShowDownloadComponentOnLine(undefined)}
className={`${
className ??
'mb-2 border-b-[2px] border-b-[red] hover:cursor-pointer'
}`}
>
{hoverOfTheFileComponent && showDownloadComponentOnLine === i ? (
hoverOfTheFileComponent
) : (
<></>
)}
</div>
</ul>
))}
<div className="flex justify-between w-full items-center">
<div>
<li>{data.fileName}</li>
{data.dateProps && data.dateProps.dateFormatter ? (
<li>{data.dateProps.dateFormatter(data.dateProps.date)}</li>
) : (
<></>
)}
</div>
{showDownloadLoadingOnFile.get(data.fileName) ? (
downloadingMessageComponent ?? (
<label>Downloading file...</label>
)
) : (
<></>
)}
</div>
</ul>
)
)}
{paginationConfig ? (
<ul className={
paginationConfig.containerClassName
? paginationConfig.containerClassName
: "flex justify-end gap-x-[0.5rem] w-full"
}
style={paginationConfig.containerStyles ?? {}}
>
<ul
className={
paginationConfig.containerClassName
? paginationConfig.containerClassName
: 'flex justify-end gap-x-[0.5rem] w-full'
}
style={paginationConfig.containerStyles ?? {}}
>
<li>
<button
<button
className="hover:cursor-pointer hover:disabled:cursor-not-allowed"
disabled={currentPage === 0}
onClick={() => setCurrentPage(0)}>First</button>
onClick={() => setCurrentPage(0)}
>
First
</button>
</li>
<li>
<button
Expand All @@ -193,9 +222,7 @@ export function BucketViewer({
Previous
</button>
</li>
<label>
{currentPage + 1}
</label>
<label>{currentPage + 1}</label>

<li>
<button
Expand Down

1 comment on commit a58e2b8

@vercel
Copy link

@vercel vercel bot commented on a58e2b8 Jan 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

portaljs-storybook – ./packages/components/

portaljs-storybook.vercel.app
portaljs-storybook-datopian1.vercel.app
storybook.portaljs.org
portaljs-storybook-git-main-datopian1.vercel.app

Please sign in to comment.