몽땅뚝딱 개발자

[Framer] CMS code override로 페이지네이션 만들기 본문

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} />
    }
}

 

 

Comments