Merge pull request #412 from storytold/bflat-fy-new-profile-pagination

pagination for profile tabs
This commit is contained in:
BFlat authored and GitHub committed 2023-12-05 23:39:21 +07:00
commit e8e6e856c6
9 files changed
+413 -71

No files matched your search

+2
View File
@@ -15,6 +15,8 @@
"@fortawesome/pro-regular-svg-icons": "^6.2.1",
"@fortawesome/pro-solid-svg-icons": "^6.2.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@types/react-paginate": "^7.1.4",
"react-paginate": "^8.2.0",
"@react-spring/types": "^9.7.3",
"@react-spring/web": "^9.7.3",
"@storyteller/components": "0.1.0",
@@ -0,0 +1,43 @@
@import "scss/colours.scss";
.pagination {
.page-link {
color: rgba($white, 75%) !important;
background-color: $secondary;
border: 1px solid $secondary;
transition: 0.2s;
&:hover {
color: #fff;
background-color: lighten($secondary, 4%);
border-color: lighten($secondary, 4%);
}
&:focus {
box-shadow: none;
}
}
.active .page-link {
color: $white !important;
background-color: lighten($secondary, 15%);
border-color: lighten($secondary, 15%);
}
.disabled .page-link {
color: rgba($white, 20%) !important;
background-color: $secondary;
border-color: $secondary;
}
.page-item {
&:last-child .page-link {
border-top-right-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
}
&:first-child .page-link {
border-top-left-radius: 0.5rem;
border-bottom-left-radius: 0.5rem;
}
}
}
@@ -0,0 +1,47 @@
import {
faChevronLeft,
faChevronRight,
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from "react";
import ReactPaginate from "react-paginate";
import "./Pagination.scss";
interface PaginationComponentProps {
currentPage: number;
itemsPerPage: number;
totalItems: number;
onPageChange: (selectedItem: { selected: number }) => void;
}
export default function Pagination({
itemsPerPage,
totalItems,
onPageChange,
currentPage,
}: PaginationComponentProps) {
const pageCount = Math.ceil(totalItems / itemsPerPage);
return (
<ReactPaginate
previousLabel={<FontAwesomeIcon icon={faChevronLeft} />}
nextLabel={<FontAwesomeIcon icon={faChevronRight} />}
breakLabel={"..."}
pageCount={pageCount}
marginPagesDisplayed={1}
pageRangeDisplayed={3}
onPageChange={onPageChange}
containerClassName={"pagination"}
activeClassName={"active"}
pageClassName={"page-item"}
pageLinkClassName={"page-link"}
previousClassName={"page-item"}
previousLinkClassName={"page-link"}
nextClassName={"page-item"}
nextLinkClassName={"page-link"}
breakClassName={"page-item"}
breakLinkClassName={"page-link"}
forcePage={currentPage}
/>
);
}
@@ -0,0 +1 @@
export { default } from "./Pagination";
@@ -3,54 +3,159 @@ import MasonryGrid from "components/common/MasonryGrid/MasonryGrid";
import AudioCard from "components/common/Card/AudioCard";
import ImageCard from "components/common/Card/ImageCard";
import VideoCard from "components/common/Card/VideoCard";
import AudioPlayerProvider from "components/common/AudioPlayer/AudioPlayerContext";
import SkeletonCard from "components/common/Card/SkeletonCard";
import mockWeightsData from "./mockWeightsData";
import Select from "components/common/Select";
import {
faArrowDownWideShort,
faFilter,
} from "@fortawesome/pro-solid-svg-icons";
import Pagination from "components/common/Pagination";
export default function BookmarksTab() {
const gridContainerRef = useRef<HTMLDivElement | null>(null);
const [data] = useState(mockWeightsData);
const [isLoading] = useState(false);
const [selectedFilter, setSelectedFilter] = useState("all");
const [currentPage, setCurrentPage] = useState(0);
const itemsPerPage = 10;
const gridContainerRef = useRef<HTMLDivElement | null>(null);
const handlePageClick = (selectedItem: { selected: number }) => {
setCurrentPage(selectedItem.selected);
};
const currentItems = data.slice(
currentPage * itemsPerPage,
(currentPage + 1) * itemsPerPage
);
const filterOptions = [
{ value: "all", label: "All Weights" },
{ value: "tts", label: "Text to Speech" },
{ value: "vc", label: "Voice to Voice" },
{ value: "sd", label: "Image Generation" },
];
const sortOptions = [
{ value: "newest", label: "Newest" },
{ value: "oldest", label: "Oldest" },
{ value: "mostliked", label: "Most Bookmarked" },
];
const modelTtsOptions = [
{ value: "all", label: "All Types" },
{ value: "tt2", label: "Tacotron 2" },
];
const modelVcOptions = [
{ value: "all", label: "All Types" },
{ value: "rvc", label: "RVCv2" },
{ value: "svc", label: "SoVitsSvc" },
];
const modelSdOptions = [
{ value: "all", label: "All Types" },
{ value: "lora", label: "LoRA" },
{ value: "SD15", label: "SD 1.5" },
{ value: "SDXL", label: "SD XL" },
];
const handleFilterChange = (option: any) => {
const selectedOption = option as { value: string; label: string };
setSelectedFilter(selectedOption.value);
};
return (
<>
<AudioPlayerProvider>
{isLoading ? (
<div className="row gx-3 gy-3">
{Array.from({ length: 6 }).map((_, index) => (
<SkeletonCard key={index} />
))}
</div>
) : (
<MasonryGrid
gridRef={gridContainerRef}
onLayoutComplete={() => console.log("Layout complete!")}
>
{data.map((data, index) => {
let card;
switch (data.media_type) {
case "audio":
card = <AudioCard key={index} data={data} type="weights" />;
break;
case "image":
card = <ImageCard key={index} data={data} type="weights" />;
break;
case "video":
card = <VideoCard key={index} data={data} type="weights" />;
break;
default:
card = <div key={index}>Unsupported media type</div>;
}
return (
<div key={index} className="col-12 col-sm-6 col-xl-4 grid-item">
{card}
</div>
);
})}
</MasonryGrid>
)}
</AudioPlayerProvider>
<div className="d-flex flex-wrap gap-3 mb-3">
<div className="d-flex gap-2 flex-grow-1">
<Select
icon={faArrowDownWideShort}
options={sortOptions}
defaultValue={sortOptions[0]}
isSearchable={false}
/>
<Select
icon={faFilter}
options={filterOptions}
defaultValue={filterOptions[0]}
isSearchable={false}
onChange={handleFilterChange}
/>
{selectedFilter === "tts" && (
<Select
options={modelTtsOptions}
defaultValue={modelTtsOptions[0]}
isSearchable={false}
/>
)}
{selectedFilter === "sd" && (
<Select
options={modelSdOptions}
defaultValue={modelSdOptions[0]}
isSearchable={false}
/>
)}
{selectedFilter === "vc" && (
<Select
options={modelVcOptions}
defaultValue={modelVcOptions[0]}
isSearchable={false}
/>
)}
</div>
<Pagination
itemsPerPage={itemsPerPage}
totalItems={data.length}
onPageChange={handlePageClick}
currentPage={currentPage}
/>
</div>
{isLoading ? (
<div className="row gx-3 gy-3">
{Array.from({ length: 6 }).map((_, index) => (
<SkeletonCard key={index} />
))}
</div>
) : (
<MasonryGrid
gridRef={gridContainerRef}
onLayoutComplete={() => console.log("Layout complete!")}
>
{currentItems.map((data, index) => {
let card;
switch (data.media_type) {
case "audio":
card = <AudioCard key={index} data={data} type="weights" />;
break;
case "image":
card = <ImageCard key={index} data={data} type="weights" />;
break;
case "video":
card = <VideoCard key={index} data={data} type="weights" />;
break;
default:
card = <div key={index}>Unsupported media type</div>;
}
return (
<div key={index} className="col-12 col-sm-6 col-xl-4 grid-item">
{card}
</div>
);
})}
</MasonryGrid>
)}
<div className="d-flex justify-content-end mt-4">
<Pagination
itemsPerPage={itemsPerPage}
totalItems={data.length}
onPageChange={handlePageClick}
currentPage={currentPage}
/>
</div>
</>
);
}
@@ -4,19 +4,30 @@ import mockMediaData from "./mockMediaData";
import AudioCard from "components/common/Card/AudioCard";
import ImageCard from "components/common/Card/ImageCard";
import VideoCard from "components/common/Card/VideoCard";
import { Select } from "components/common/Inputs/Inputs";
import Select from "components/common/Select";
import {
faArrowDownWideShort,
faFilter,
} from "@fortawesome/pro-solid-svg-icons";
import AudioPlayerProvider from "components/common/AudioPlayer/AudioPlayerContext";
import SkeletonCard from "components/common/Card/SkeletonCard";
import Pagination from "components/common/Pagination";
export default function MediaTab() {
const gridContainerRef = useRef<HTMLDivElement | null>(null);
const [data] = useState(mockMediaData);
const [isLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(0);
const itemsPerPage = 10;
const gridContainerRef = useRef<HTMLDivElement | null>(null);
const handlePageClick = (selectedItem: { selected: number }) => {
setCurrentPage(selectedItem.selected);
};
const currentItems = data.slice(
currentPage * itemsPerPage,
(currentPage + 1) * itemsPerPage
);
const filterOptions = [
{ value: "all", label: "All Media" },
@@ -33,18 +44,26 @@ export default function MediaTab() {
return (
<>
<div className="d-flex gap-2 mb-3">
<Select
icon={faArrowDownWideShort}
options={sortOptions}
defaultValue={sortOptions[0]}
isSearchable={false}
/>
<Select
icon={faFilter}
options={filterOptions}
defaultValue={filterOptions[0]}
isSearchable={false}
<div className="d-flex flex-wrap gap-3 mb-3">
<div className="d-flex gap-2 flex-grow-1">
<Select
icon={faArrowDownWideShort}
options={sortOptions}
defaultValue={sortOptions[0]}
isSearchable={false}
/>
<Select
icon={faFilter}
options={filterOptions}
defaultValue={filterOptions[0]}
isSearchable={false}
/>
</div>
<Pagination
itemsPerPage={itemsPerPage}
totalItems={data.length}
onPageChange={handlePageClick}
currentPage={currentPage}
/>
</div>
<AudioPlayerProvider>
@@ -59,7 +78,7 @@ export default function MediaTab() {
gridRef={gridContainerRef}
onLayoutComplete={() => console.log("Layout complete!")}
>
{data.map((data, index) => {
{currentItems.map((data, index) => {
let card;
switch (data.media_type) {
case "audio":
@@ -83,6 +102,15 @@ export default function MediaTab() {
</MasonryGrid>
)}
</AudioPlayerProvider>
<div className="d-flex justify-content-end mt-4">
<Pagination
itemsPerPage={itemsPerPage}
totalItems={data.length}
onPageChange={handlePageClick}
currentPage={currentPage}
/>
</div>
</>
);
}
@@ -4,46 +4,113 @@ import mockWeightsData from "./mockWeightsData";
import AudioCard from "components/common/Card/AudioCard";
import ImageCard from "components/common/Card/ImageCard";
import VideoCard from "components/common/Card/VideoCard";
import { Select } from "components/common/Inputs/Inputs";
import Select from "components/common/Select";
import {
faArrowDownWideShort,
faFilter,
} from "@fortawesome/pro-solid-svg-icons";
import SkeletonCard from "components/common/Card/SkeletonCard";
import Pagination from "components/common/Pagination";
export default function WeightsTab() {
const gridContainerRef = useRef<HTMLDivElement | null>(null);
const [data] = useState(mockWeightsData);
const [isLoading] = useState(false);
const [selectedFilter, setSelectedFilter] = useState("all");
const [currentPage, setCurrentPage] = useState(0);
const itemsPerPage = 10;
const gridContainerRef = useRef<HTMLDivElement | null>(null);
const handlePageClick = (selectedItem: { selected: number }) => {
setCurrentPage(selectedItem.selected);
};
const currentItems = data.slice(
currentPage * itemsPerPage,
(currentPage + 1) * itemsPerPage
);
const filterOptions = [
{ value: "all", label: "All Weights" },
{ value: "tts", label: "Text to Speech" },
{ value: "vc", label: "Voice to Voice" },
{ value: "vd", label: "Voice Designer" },
{ value: "sd", label: "Image Generation" },
];
const sortOptions = [
{ value: "newest", label: "Newest" },
{ value: "oldest", label: "Oldest" },
{ value: "mostliked", label: "Most Favorited" },
{ value: "mostliked", label: "Most Bookmarked" },
];
const modelTtsOptions = [
{ value: "all", label: "All Types" },
{ value: "tt2", label: "Tacotron 2" },
];
const modelVcOptions = [
{ value: "all", label: "All Types" },
{ value: "rvc", label: "RVCv2" },
{ value: "svc", label: "SoVitsSvc" },
];
const modelSdOptions = [
{ value: "all", label: "All Types" },
{ value: "lora", label: "LoRA" },
{ value: "SD15", label: "SD 1.5" },
{ value: "SDXL", label: "SD XL" },
];
const handleFilterChange = (option: any) => {
const selectedOption = option as { value: string; label: string };
setSelectedFilter(selectedOption.value);
};
return (
<>
<div className="d-flex gap-2 mb-3">
<Select
icon={faArrowDownWideShort}
options={sortOptions}
defaultValue={sortOptions[0]}
isSearchable={false}
/>
<Select
icon={faFilter}
options={filterOptions}
defaultValue={filterOptions[0]}
isSearchable={false}
<div className="d-flex flex-wrap gap-3 mb-3">
<div className="d-flex gap-2 flex-grow-1">
<Select
icon={faArrowDownWideShort}
options={sortOptions}
defaultValue={sortOptions[0]}
isSearchable={false}
/>
<Select
icon={faFilter}
options={filterOptions}
defaultValue={filterOptions[0]}
isSearchable={false}
onChange={handleFilterChange}
/>
{selectedFilter === "tts" && (
<Select
options={modelTtsOptions}
defaultValue={modelTtsOptions[0]}
isSearchable={false}
/>
)}
{selectedFilter === "sd" && (
<Select
options={modelSdOptions}
defaultValue={modelSdOptions[0]}
isSearchable={false}
/>
)}
{selectedFilter === "vc" && (
<Select
options={modelVcOptions}
defaultValue={modelVcOptions[0]}
isSearchable={false}
/>
)}
</div>
<Pagination
itemsPerPage={itemsPerPage}
totalItems={data.length}
onPageChange={handlePageClick}
currentPage={currentPage}
/>
</div>
{isLoading ? (
@@ -57,7 +124,7 @@ export default function WeightsTab() {
gridRef={gridContainerRef}
onLayoutComplete={() => console.log("Layout complete!")}
>
{data.map((data, index) => {
{currentItems.map((data, index) => {
let card;
switch (data.media_type) {
case "audio":
@@ -80,6 +147,15 @@ export default function WeightsTab() {
})}
</MasonryGrid>
)}
<div className="d-flex justify-content-end mt-4">
<Pagination
itemsPerPage={itemsPerPage}
totalItems={data.length}
onPageChange={handlePageClick}
currentPage={currentPage}
/>
</div>
</>
);
}
@@ -379,6 +379,24 @@ let mockMediaData = [
isLiked: true,
created_at: "2023-08-10T09:00:00.000Z",
},
{
token: "token21",
weight_name: "Watery Landscape",
media_type: "image",
public_bucket_path: "/images/dummy-image-4.jpg",
likes: 150,
isLiked: true,
created_at: "2023-08-10T09:00:00.000Z",
},
{
token: "token22",
weight_name: "Watery Landscape",
media_type: "image",
public_bucket_path: "/images/dummy-image-4.jpg",
likes: 150,
isLiked: true,
created_at: "2023-08-10T09:00:00.000Z",
},
];
export default mockMediaData;
+23 -1
View File
@@ -4582,6 +4582,7 @@ __metadata:
resolution: "@storyteller/root@workspace:."
dependencies:
"@react-spring/types": ^9.7.3
"@types/react-paginate": ^7.1.4
craco: ^0.0.3
cross-env: ^7.0.3
husky: ^8.0.3
@@ -4589,6 +4590,7 @@ __metadata:
moment: ^2.29.4
prettier: ^3.1.0
pretty-quick: ^3.1.3
react-paginate: ^8.2.0
universal-cookie: ^6.1.1
yarn: ^1.22.19
languageName: unknown
@@ -5508,6 +5510,15 @@ __metadata:
languageName: node
linkType: hard
"@types/react-paginate@npm:^7.1.4":
version: 7.1.4
resolution: "@types/react-paginate@npm:7.1.4"
dependencies:
"@types/react": "*"
checksum: 3dbada2ab364307382adce1283220c83b743bfb056508dac6c513cfc1ce6b703e98129717abb052739106ae02eaab847b8b7037e7ca6b666eba86c9879e70d74
languageName: node
linkType: hard
"@types/react-router-dom@npm:^5.1.7":
version: 5.3.3
resolution: "@types/react-router-dom@npm:5.3.3"
@@ -17411,7 +17422,7 @@ __metadata:
languageName: node
linkType: hard
"prop-types@npm:^15.5.10, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
"prop-types@npm:^15, prop-types@npm:^15.5.10, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
version: 15.8.1
resolution: "prop-types@npm:15.8.1"
dependencies:
@@ -17846,6 +17857,17 @@ __metadata:
languageName: node
linkType: hard
"react-paginate@npm:^8.2.0":
version: 8.2.0
resolution: "react-paginate@npm:8.2.0"
dependencies:
prop-types: ^15
peerDependencies:
react: ^16 || ^17 || ^18
checksum: a0969b4ef27be466ef3e052e2c7c61fca60af24e0d10c76770ebd9953ad6131206a276dfc335e61cf35aa494ff3aacc2610682e3192d7f2ee9541928edb46721
languageName: node
linkType: hard
"react-parallax-tilt@npm:^1.7.110":
version: 1.7.110
resolution: "react-parallax-tilt@npm:1.7.110"