marketplace
parent
49ee9ca5f1
commit
ecd2a1be9f
@ -1,41 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import type { ReactNode } from 'react'
|
|
||||||
import { useState } from 'react'
|
|
||||||
import {
|
|
||||||
createContext,
|
|
||||||
useContextSelector,
|
|
||||||
} from 'use-context-selector'
|
|
||||||
|
|
||||||
export type MarketplaceContextValue = {
|
|
||||||
scrollIntersected: boolean
|
|
||||||
setScrollIntersected: (scrollIntersected: boolean) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MarketplaceContext = createContext<MarketplaceContextValue>({
|
|
||||||
scrollIntersected: false,
|
|
||||||
setScrollIntersected: () => {},
|
|
||||||
})
|
|
||||||
|
|
||||||
type MarketplaceContextProviderProps = {
|
|
||||||
children: ReactNode
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useMarketplaceContext(selector: (value: MarketplaceContextValue) => any) {
|
|
||||||
return useContextSelector(MarketplaceContext, selector)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const MarketplaceContextProvider = ({
|
|
||||||
children,
|
|
||||||
}: MarketplaceContextProviderProps) => {
|
|
||||||
const [scrollIntersected, setScrollIntersected] = useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MarketplaceContext.Provider value={{
|
|
||||||
scrollIntersected,
|
|
||||||
setScrollIntersected,
|
|
||||||
}}>
|
|
||||||
{children}
|
|
||||||
</MarketplaceContext.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
|
import { useCallback } from 'react'
|
||||||
|
import IntersectionLine from '../intersection-line'
|
||||||
|
import { usePluginPageContext } from '@/app/components/plugins/plugin-page/context'
|
||||||
|
|
||||||
|
type DescriptionWrapperProps = {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
const DescriptionWrapper = ({
|
||||||
|
children,
|
||||||
|
}: DescriptionWrapperProps) => {
|
||||||
|
const containerRef = usePluginPageContext(v => v.containerRef)
|
||||||
|
const scrollDisabled = usePluginPageContext(v => v.scrollDisabled)
|
||||||
|
const setScrollDisabled = usePluginPageContext(v => v.setScrollDisabled)
|
||||||
|
|
||||||
|
const handleScrollIntersectionChange = useCallback((isIntersecting: boolean) => {
|
||||||
|
if (!isIntersecting && !scrollDisabled) {
|
||||||
|
setScrollDisabled(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
if (containerRef && containerRef.current)
|
||||||
|
containerRef.current.scrollTop = 0
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}, [containerRef, scrollDisabled, setScrollDisabled])
|
||||||
|
|
||||||
|
return !scrollDisabled && (
|
||||||
|
<>
|
||||||
|
{children}
|
||||||
|
<IntersectionLine
|
||||||
|
containerRef={containerRef}
|
||||||
|
intersectedCallback={handleScrollIntersectionChange}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DescriptionWrapper
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import Description from '../description'
|
||||||
|
import DescriptionWrapper from '../description/wrapper'
|
||||||
|
import SearchBoxWrapper from '../search-box/wrapper'
|
||||||
|
import PluginTypeSwitch from '../plugin-type-switch'
|
||||||
|
|
||||||
|
const Header = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DescriptionWrapper>
|
||||||
|
<Description />
|
||||||
|
</DescriptionWrapper>
|
||||||
|
<div className='flex items-center justify-center mt-[15px] mb-4'>
|
||||||
|
<SearchBoxWrapper />
|
||||||
|
</div>
|
||||||
|
<PluginTypeSwitch />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header
|
||||||
@ -1,30 +1,21 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useContextSelector } from 'use-context-selector'
|
|
||||||
import { PluginPageContext } from '../../plugin-page/context'
|
|
||||||
import { MarketplaceContext } from '../context'
|
|
||||||
|
|
||||||
export const useScrollIntersection = (
|
export const useScrollIntersection = (
|
||||||
|
containerRef: React.RefObject<HTMLDivElement>,
|
||||||
anchorRef: React.RefObject<HTMLDivElement>,
|
anchorRef: React.RefObject<HTMLDivElement>,
|
||||||
|
callback: (isIntersecting: boolean) => void,
|
||||||
) => {
|
) => {
|
||||||
const containerRef = useContextSelector(PluginPageContext, v => v.containerRef)
|
|
||||||
const scrollIntersected = useContextSelector(MarketplaceContext, v => v.scrollIntersected)
|
|
||||||
const setScrollIntersected = useContextSelector(MarketplaceContext, v => v.setScrollIntersected)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let observer: IntersectionObserver | undefined
|
let observer: IntersectionObserver | undefined
|
||||||
if (containerRef.current && anchorRef.current) {
|
if (containerRef?.current && anchorRef.current) {
|
||||||
observer = new IntersectionObserver((entries) => {
|
observer = new IntersectionObserver((entries) => {
|
||||||
console.log(entries, 'entries')
|
const isIntersecting = entries[0].isIntersecting
|
||||||
if (entries[0].isIntersecting && !scrollIntersected)
|
callback(isIntersecting)
|
||||||
setScrollIntersected(true)
|
|
||||||
|
|
||||||
if (!entries[0].isIntersecting && scrollIntersected)
|
|
||||||
setScrollIntersected(false)
|
|
||||||
}, {
|
}, {
|
||||||
root: containerRef.current,
|
root: containerRef.current,
|
||||||
})
|
})
|
||||||
observer.observe(anchorRef.current)
|
observer.observe(anchorRef.current)
|
||||||
}
|
}
|
||||||
return () => observer?.disconnect()
|
return () => observer?.disconnect()
|
||||||
}, [containerRef, anchorRef, scrollIntersected, setScrollIntersected])
|
}, [containerRef, anchorRef, callback])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import type { ReactNode } from 'react'
|
|
||||||
import cn from '@/utils/classnames'
|
|
||||||
|
|
||||||
type ListWrapperProps = {
|
|
||||||
children: ReactNode
|
|
||||||
}
|
|
||||||
const ListWrapper = ({
|
|
||||||
children,
|
|
||||||
}: ListWrapperProps) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
'px-12 py-2 bg-background-default-subtle',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ListWrapper
|
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
|
import { usePluginPageContext } from '@/app/components/plugins/plugin-page/context'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type ListWrapperProps = {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
const ListWrapper = ({
|
||||||
|
children,
|
||||||
|
}: ListWrapperProps) => {
|
||||||
|
const scrollDisabled = usePluginPageContext(v => v.scrollDisabled)
|
||||||
|
const setScrollDisabled = usePluginPageContext(v => v.setScrollDisabled)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
scrollDisabled && (
|
||||||
|
<div className='h-[60px]'></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'px-12 py-2 bg-background-default-subtle',
|
||||||
|
scrollDisabled && 'grow h-0 overflow-y-auto',
|
||||||
|
)}
|
||||||
|
onScroll={(e) => {
|
||||||
|
if ((e.target as HTMLElement).scrollTop <= 0)
|
||||||
|
setScrollDisabled(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ListWrapper
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import SearchBox from '.'
|
||||||
|
import { usePluginPageContext } from '@/app/components/plugins/plugin-page/context'
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
const scrollDisabled = usePluginPageContext(v => v.scrollDisabled)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SearchBox widthShouldChange={scrollDisabled} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Wrapper
|
||||||
Loading…
Reference in New Issue