diff --git a/packages/apsara-ui/src/InfiniteScroll/InfiniteScroll.stories.tsx b/packages/apsara-ui/src/InfiniteScroll/InfiniteScroll.stories.tsx new file mode 100644 index 00000000..2c640ee5 --- /dev/null +++ b/packages/apsara-ui/src/InfiniteScroll/InfiniteScroll.stories.tsx @@ -0,0 +1,134 @@ +import React, { useCallback, useEffect, useState } from "react"; +import InfiniteScroll from "./InfiniteScroll"; +import styled from "styled-components"; + +export default { + title: "Data Display/InfiniteScroll", + component: InfiniteScroll, +}; + +interface User { + key: number; + name: string; + status: "active" | "inactive"; + age: number; + address: string; +} + +function getData(page = 1, page_size = 10): User[] { + return new Array(page * page_size).fill(0).map((_, index) => { + return { + key: index, + name: `name ${index}`, + status: index % 2 ? "active" : "inactive", + age: index, + address: `A${index} Downing Street`, + }; + }); +} + +function getDataAsync(page = 1, page_size = 10): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve(getData(page, page_size)); + }, 1000); + }); +} + +export const basic = () => { + const [data, setData] = useState([]); + const [isFetching, setIsFetching] = useState(false); + + const fetchData = useCallback(() => { + setIsFetching(true); + getDataAsync() + .then((d) => setData((curr) => curr.concat(d))) + .finally(() => setIsFetching(false)); + }, []); + + useEffect(() => { + fetchData(); + }, [fetchData]); + + const onScroll = () => { + if (isFetching) return; + + fetchData(); + }; + + return ( +
+ + items={data} + renderItem={(item) => } + onBottomScroll={onScroll} + isLoading={isFetching} + /> +
+ ); +}; + +const Card = ({ user }: { user: User }) => { + return ( + +
{user.name}
+
+
+
Address
+
{user.address}
+
+
+
Age
+
{user.age}
+
+
+
+ ); +}; + +const UserCard = styled.div` + margin-bottom: 16px; + border-radius: 2px; + box-shadow: rgb(179 179 179 / 31%) 0px 1px 5px; + + .title { + margin: 10px; + color: #4d85f4; + font-size: 16px; + } + + .description { + margin-top: 20px; + word-break: break-all; + } + + .body { + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 0px 20px 12px 20px; + + > .body-left { + margin-top: 16px; + flex: 1; + } + + > .body-right { + width: 240px; + display: flex; + flex-direction: column; + align-items: flex-end; + justify-content: space-between; + + > div { + display: flex; + align-items: flex-end; + flex-direction: column; + } + + .tag_content { + text-transform: uppercase; + } + } + } +`; diff --git a/packages/apsara-ui/src/InfiniteScroll/InfiniteScroll.styles.tsx b/packages/apsara-ui/src/InfiniteScroll/InfiniteScroll.styles.tsx deleted file mode 100644 index f5b7b08b..00000000 --- a/packages/apsara-ui/src/InfiniteScroll/InfiniteScroll.styles.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import styled from "styled-components"; - -export const InfiniteScrollWrapper = styled.div` - position: relative; - height: calc(100% - 0px); - height: calc(100% - 200px); - overflow-y: scroll; -`; diff --git a/packages/apsara-ui/src/InfiniteScroll/InfiniteScroll.tsx b/packages/apsara-ui/src/InfiniteScroll/InfiniteScroll.tsx index 166df768..e920a981 100644 --- a/packages/apsara-ui/src/InfiniteScroll/InfiniteScroll.tsx +++ b/packages/apsara-ui/src/InfiniteScroll/InfiniteScroll.tsx @@ -1,11 +1,10 @@ import React, { useEffect, useRef } from "react"; -import { InfiniteScrollWrapper } from "./InfiniteScroll.styles"; - interface InfiniteScrollProps { renderItem: (item: T) => React.ReactNode; onBottomScroll: () => void; items: T[]; + height?: string; style?: React.CSSProperties; isLoading?: boolean; noMoreData?: boolean; @@ -20,13 +19,13 @@ const InfiniteScroll = ({ onBottomScroll, items, style, + height, isLoading, noMoreData, containerRef, - threshold = 0, + threshold = 1, loadingComponent, noMoreDataComponent, - ...props }: InfiniteScrollProps) => { const defaultContainerRef = useRef(null); const containerElem = containerRef ? containerRef.current : defaultContainerRef.current; @@ -38,7 +37,7 @@ const InfiniteScroll = ({ if (isBottom(containerElem, threshold)) { onBottomScroll(); } - } + }; containerElem.addEventListener("scroll", onScroll); @@ -47,19 +46,23 @@ const InfiniteScroll = ({ }; }, [containerElem, onBottomScroll, threshold]); - const loadingComp = loadingComponent || ; + const loadingComp = loadingComponent || ; + + const scrollStyle: React.CSSProperties = !containerRef + ? { + height: height || "100%", + overflow: "scroll", + position: "relative", + ...style, + } + : {}; return ( - +
{items?.map(renderItem)} {isLoading && loadingComp} {!noMoreData && noMoreDataComponent} - +
); }; @@ -73,8 +76,6 @@ const isBottom = (elem: HTMLElement, threshold: number): boolean => { const a = scrollTop + clientHeight; const b = scrollHeight - threshold; - console.log(a); - console.log(b); return a >= b; }; diff --git a/packages/apsara-ui/src/InfiniteScroll/Listing.stories.tsx b/packages/apsara-ui/src/InfiniteScroll/Listing.stories.tsx deleted file mode 100644 index 7a2e4714..00000000 --- a/packages/apsara-ui/src/InfiniteScroll/Listing.stories.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import React, { FormEvent, useCallback, useEffect, useState } from "react"; -import InfiniteScroll from "./InfiniteScroll"; -import Search from "../Search"; -import styled from "styled-components"; - -export default { - title: "Data Display/InfiniteScroll", - component: InfiniteScroll, -}; - -interface User { - key: number; - name: string; - status: "active" | "inactive"; - age: number; - address: string; -} - -function getData(page = 1, page_size = 10): User[] { - return new Array(page * page_size).fill(0).map((_, index) => { - return { - key: index, - name: `name ${index}`, - status: index % 2 ? "active" : "inactive", - age: index, - address: `A${index} Downing Street`, - }; - }); -} - -function getDataAsync(page = 1, page_size = 10): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve(getData(page, page_size)); - }, 1000); - }); -} - -export const basic = () => { - const [data, setData] = useState([]); - const [isFetching, setIsFetching] = useState(false); - - const fetchData = useCallback(() => { - setIsFetching(true) - getDataAsync() - .then(d => setData(curr => curr.concat(d))) - .finally(() => setIsFetching(false)) - }, []); - - useEffect(() => { - fetchData(); - }, [fetchData]) - - const onScroll = () => { - if (isFetching) return; - - fetchData(); - } - - return ( -
- - items={data} - renderItem={(item) =>
{item.name}
} - onBottomScroll={onScroll} - isLoading={isFetching} - /> -
- ) -} - -// export const infiniteListWithCustomComponent = () => { -// const pageSize = 10; -// const [searchTerm, setSearchTerm] = useState(""); -// const [filter, setFilter] = useState(""); -// const [page, setPage] = useState(1); - -// const fetchMore = async (page: number, pageSize: number, filter: string) => { -// if (page === 4) return []; - -// let records = getData(page); -// if (filter) { -// records = records.filter((record) => record.name.toLowerCase().includes(filter.toLowerCase())); -// } - -// setPage((prevPage) => prevPage + 1); -// records = records.slice(0, pageSize); -// return records; -// }; - -// const Card = ({ user }: { user: User }) => { -// return ( -// -//
{user.name}
-//
-//
-//
Address
-//
{user.address}
-//
-//
-//
Age
-//
{user.age}
-//
-//
-//
-// ); -// }; - -// const onSubmit = (e: FormEvent) => { -// e.preventDefault(); -// setPage(1); -// setFilter(searchTerm); -// }; - -// return ( -//
-//
-// { -// return setSearchTerm(e.target.value); -// }} -// placeholder="Type your search query here.." -// // @ts-ignore -// secondary={true} -// /> -// -//
-// -// fetchMoreData={fetchMore} -// filters={filter} -// pageSize={pageSize} -// renderItem={(user: User) => } -// loadingComponent={
Loading...
} -// noMoreDataComponent={
No more data to fetch!
} -// > -//
-//
-// ); -// }; - -const UserCard = styled.div` - margin-bottom: 16px; - border-radius: 2px; - box-shadow: rgb(179 179 179 / 31%) 0px 1px 5px; - - .title { - margin: 10px; - color: #4d85f4; - font-size: 16px; - } - - .description { - margin-top: 20px; - word-break: break-all; - } - - .body { - display: flex; - flex-direction: row; - justify-content: space-between; - padding: 0px 20px 12px 20px; - - > .body-left { - margin-top: 16px; - flex: 1; - } - - > .body-right { - width: 240px; - display: flex; - flex-direction: column; - align-items: flex-end; - justify-content: space-between; - - > div { - display: flex; - align-items: flex-end; - flex-direction: column; - } - - .tag_content { - text-transform: uppercase; - } - } - } -`; diff --git a/packages/apsara-ui/src/Listing/Listing.stories.tsx b/packages/apsara-ui/src/Listing/Listing.stories.tsx index e62bf3c8..88677439 100644 --- a/packages/apsara-ui/src/Listing/Listing.stories.tsx +++ b/packages/apsara-ui/src/Listing/Listing.stories.tsx @@ -28,7 +28,7 @@ function getData(page = 1): User[] { } export const listing = () => ( -
+

Listing

loading={false}