Development/Web
[Framer] CMS code override로 페이지네이션 만들기
레오나르도 다빈츠
2024. 8. 4. 15:50
받아오는 방식은 props를 intercept 해서 가공한 뒤 Component의 props으로 넘긴다.
import type { ComponentType } from "react"
import { useState, useEffect } from "react"
import { createStore } from "https://framer.com/m/framer/store.js@^1.0.0"
const LIMIT = 6
const OFFSET = 0
const SCROLL_THRESHOLD = 200
const PREVIOUS_CLASS = "Previous"
const PREVIOUS_DISABLED_CLASS = "Previous Disabled"
const NEXT_CLASS = "Next"
const NEXT_DISABLED_CLASS = "Next Disabled"
const PAGE_NUMBER_CLASS = "Page Number"
const PAGE_NUMBER_ACTIVE_CLASS = "Page Number Active"
const useStore = createStore({
limit: LIMIT,
offset: OFFSET,
totalItems: null,
currentPage: 1,
})
export const withCollectionList = (Component): ComponentType => {
return (props: any) => {
const [store, setStore] = useStore()
const [newProps, setNewProps] = useState(props)
useEffect(() => {
if (store.totalItems === null) {
const target = `.${props.className}none .${props.className}`
const queryTotalItems =
document.querySelector(target).children.length
const newTotalItems = queryTotalItems - OFFSET
setStore({
totalItems: Math.max(newTotalItems, 0),
})
}
}, [props, store])
useEffect(() => {
setNewProps({
...props,
children: {
...props.children,
props: {
...props.children.props,
children: {
...props.children.props.children,
props: {
...props.children.props.children.props,
query: {
...props.children.props.children.props
.query,
limit: {
type: "LiteralValue",
value: store.limit,
},
offset: {
type: "LiteralValue",
value: store.offset,
},
},
},
},
},
},
})
}, [store.offset, store.limit, props])
useEffect(() => {
console.log("newProps >>> ", newProps)
}, [newProps])
return (
<>
{store.totalItems === null && (
<div
className={`${props.className}none`}
style={{ display: "none" }}
>
<Component {...props} />
</div>
)}
<Component {...newProps} />
</>
)
}
}
export const withPrevious = (Component): ComponentType => {
return (props) => {
const [store, setStore] = useStore()
useEffect(() => {
setStore({
totalItems: null,
limit: LIMIT,
})
}, [])
const handlePreviousPage = () => {
if (store.currentPage > 1) {
const newPage = store.currentPage - 1
const newOffset = (newPage - 1) * LIMIT
setStore({
offset: newOffset,
currentPage: newPage,
})
window.scrollTo(0, 0)
}
}
return (
<Component
{...props}
onClick={handlePreviousPage}
variant={
store.currentPage !== 1
? PREVIOUS_CLASS
: PREVIOUS_DISABLED_CLASS
}
/>
)
}
}
export const withNext = (Component): ComponentType => {
return (props) => {
const [store, setStore] = useStore()
useEffect(() => {
setStore({
totalItems: null,
limit: LIMIT,
})
}, [])
const handleNextPage = () => {
if (store.currentPage * LIMIT < store.totalItems) {
const newPage = store.currentPage + 1
const newOffset = (newPage - 1) * LIMIT
setStore({
offset: newOffset,
currentPage: newPage,
})
window.scrollTo(0, 0)
}
}
return (
<Component
{...props}
onClick={handleNextPage}
variant={
store.currentPage * LIMIT < store.totalItems
? NEXT_CLASS
: NEXT_DISABLED_CLASS
}
/>
)
}
}
export const withCurrentPage = (Component): ComponentType => {
return (props) => {
const [store, setStore] = useStore()
useEffect(() => {
setStore({
totalItems: null,
limit: LIMIT,
})
}, [])
const [currentPage, setCurrentPage] = useState(() =>
store.currentPage.toString()
)
const [totalPages, setTotalPages] = useState(() =>
Math.ceil(store.totalItems / LIMIT)
)
useEffect(() => {
setCurrentPage(store.currentPage.toString())
setTotalPages(Math.ceil(store.totalItems / LIMIT))
}, [store.currentPage, store.totalItems])
return <Component {...props} page={`${currentPage}/${totalPages}`} />
}
}
export const withPageSelector = (Component): ComponentType => {
return (props) => {
const [store, setStore] = useStore()
const [pages, setPages] = useState([])
useEffect(() => {
setStore({
totalItems: null,
limit: LIMIT,
})
}, [])
useEffect(() => {
const generatePages = () => {
const totalPages = Math.ceil(store.totalItems / LIMIT)
const newPages = Array.from(
{ length: totalPages },
(_, index) => index + 1
)
setPages(newPages)
}
generatePages()
}, [store.totalItems])
const handleNewPage = (newPage) => {
const newOffset = (newPage - 1) * LIMIT
setStore({
offset: newOffset,
currentPage: newPage,
})
window.scrollTo(0, 0)
}
return (
<div style={{ display: "flex", flexDirection: "row" }}>
{pages.map((item, index) => {
return (
<Component
key={index}
{...props}
page={item.toString()}
onClick={() => handleNewPage(item)}
variant={
store.currentPage === item
? PAGE_NUMBER_ACTIVE_CLASS
: PAGE_NUMBER_CLASS
}
/>
)
})}
</div>
)
}
}
export const withTablePagination = (Component): ComponentType => {
return (props) => {
const [store, setStore] = useStore()
const [pages, setPages] = useState([])
useEffect(() => {
setStore({
totalItems: null,
limit: LIMIT,
})
}, [])
useEffect(() => {
const generatePages = () => {
const totalPages = Math.ceil(store.totalItems / LIMIT)
const adjacentPages =
store.currentPage === 1 || store.currentPage === totalPages
? 2
: 1
const pages = []
for (let i = 1; i <= totalPages; i++) {
const isWithinRange =
i >= store.currentPage - adjacentPages &&
i <= store.currentPage + adjacentPages
if (i === 1 || i === totalPages || isWithinRange) {
pages.push(i)
} else if (
(i === store.currentPage - adjacentPages - 1 &&
i !== 1) ||
(i === store.currentPage + adjacentPages + 1 &&
i !== totalPages)
) {
pages.push("...")
}
}
setPages(pages)
}
generatePages()
}, [store.totalItems, store.currentPage])
const handleNewPage = (newPage) => {
const newOffset = (newPage - 1) * LIMIT
setStore({
offset: newOffset,
currentPage: newPage,
})
window.scrollTo(0, 0)
}
return (
<div style={{ display: "flex", flexDirection: "row" }}>
{pages.map((item, index, array) => {
const isEllipsis = item === "..."
return (
<Component
key={index}
{...props}
page={item.toString()}
onClick={() => !isEllipsis && handleNewPage(item)}
variant={
store.currentPage === item || isEllipsis
? PAGE_NUMBER_ACTIVE_CLASS
: PAGE_NUMBER_CLASS
}
/>
)
})}
</div>
)
}
}
export const withClear = (Component): ComponentType => {
return (props) => {
const [store, setStore] = useStore()
const handleClick = () => {
setStore({
offset: OFFSET,
totalItems: null,
currentPage: 1,
limit: LIMIT,
})
}
return <Component {...props} onClick={handleClick} />
}
}