From c1e0a939b0b37b427c8ad0d75b6458a6f51a6e49 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Sat, 12 Oct 2024 12:46:29 +0800 Subject: [PATCH] marketplace --- .../plugins/marketplace/context.tsx | 41 ++++++ .../plugins/marketplace/header-wrapper.tsx | 23 ++++ .../components/plugins/marketplace/header.tsx | 39 ++++++ .../components/plugins/marketplace/hooks.ts | 20 --- .../components/plugins/marketplace/index.tsx | 43 ++---- .../marketplace/intersection-line/hooks.ts | 30 ++++ .../marketplace/intersection-line/index.tsx | 16 +++ .../plugins/marketplace/list-wrapper.tsx | 23 ++++ .../components/plugins/marketplace/list.tsx | 130 +++++++++++++++++- .../plugins/marketplace/search-box/index.tsx | 10 +- 10 files changed, 321 insertions(+), 54 deletions(-) create mode 100644 web/app/components/plugins/marketplace/context.tsx create mode 100644 web/app/components/plugins/marketplace/header-wrapper.tsx create mode 100644 web/app/components/plugins/marketplace/header.tsx delete mode 100644 web/app/components/plugins/marketplace/hooks.ts create mode 100644 web/app/components/plugins/marketplace/intersection-line/hooks.ts create mode 100644 web/app/components/plugins/marketplace/intersection-line/index.tsx create mode 100644 web/app/components/plugins/marketplace/list-wrapper.tsx diff --git a/web/app/components/plugins/marketplace/context.tsx b/web/app/components/plugins/marketplace/context.tsx new file mode 100644 index 0000000000..86eab52006 --- /dev/null +++ b/web/app/components/plugins/marketplace/context.tsx @@ -0,0 +1,41 @@ +'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({ + 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 ( + + {children} + + ) +} diff --git a/web/app/components/plugins/marketplace/header-wrapper.tsx b/web/app/components/plugins/marketplace/header-wrapper.tsx new file mode 100644 index 0000000000..6e18309a5f --- /dev/null +++ b/web/app/components/plugins/marketplace/header-wrapper.tsx @@ -0,0 +1,23 @@ +'use client' + +import type { ReactNode } from 'react' +import cn from '@/utils/classnames' + +type HeaderWrapperProps = { + children: ReactNode +} +const HeaderWrapper = ({ + children, +}: HeaderWrapperProps) => { + return ( +
+ {children} +
+ ) +} + +export default HeaderWrapper diff --git a/web/app/components/plugins/marketplace/header.tsx b/web/app/components/plugins/marketplace/header.tsx new file mode 100644 index 0000000000..e8f0920287 --- /dev/null +++ b/web/app/components/plugins/marketplace/header.tsx @@ -0,0 +1,39 @@ +import SearchBox from './search-box' +import PluginTypeSwitch from './plugin-type-switch' +import IntersectionLine from './intersection-line' + +const Header = () => { + return ( + <> +

+ Empower your AI development +

+

+ Discover + + models + + , + + tools + + , + + extensions + + and + + bundles + + in Dify Marketplace +

+ +
+ +
+ + + ) +} + +export default Header diff --git a/web/app/components/plugins/marketplace/hooks.ts b/web/app/components/plugins/marketplace/hooks.ts deleted file mode 100644 index 3846bbac2f..0000000000 --- a/web/app/components/plugins/marketplace/hooks.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useEffect } from 'react' - -export const useScrollIntersection = ( - rootRef: React.RefObject, - anchorRef: React.RefObject, - callback: (isIntersecting: boolean) => void, -) => { - useEffect(() => { - let observer: IntersectionObserver | undefined - if (rootRef.current && anchorRef.current) { - observer = new IntersectionObserver((entries) => { - callback(entries[0].isIntersecting) - }, { - root: rootRef.current, - }) - observer.observe(anchorRef.current) - } - return () => observer?.disconnect() - }, [rootRef, anchorRef, callback]) -} diff --git a/web/app/components/plugins/marketplace/index.tsx b/web/app/components/plugins/marketplace/index.tsx index b07db8e920..cbf1c870ce 100644 --- a/web/app/components/plugins/marketplace/index.tsx +++ b/web/app/components/plugins/marketplace/index.tsx @@ -1,39 +1,20 @@ -import SearchBox from './search-box' -import PluginTypeSwitch from './plugin-type-switch' +import { MarketplaceContextProvider } from './context' +import HeaderWrapper from './header-wrapper' +import Header from './header' +import ListWrapper from './list-wrapper' import List from './list' const Marketplace = () => { return (
-
-

- Empower your AI development -

-

- Discover - - models - - , - - tools - - , - - extensions - - and - - bundles - - in Dify Marketplace -

-
- -
- -
- + + +
+ + + + +
) } diff --git a/web/app/components/plugins/marketplace/intersection-line/hooks.ts b/web/app/components/plugins/marketplace/intersection-line/hooks.ts new file mode 100644 index 0000000000..ed79daaa5a --- /dev/null +++ b/web/app/components/plugins/marketplace/intersection-line/hooks.ts @@ -0,0 +1,30 @@ +import { useEffect } from 'react' +import { useContextSelector } from 'use-context-selector' +import { PluginPageContext } from '../../plugin-page/context' +import { MarketplaceContext } from '../context' + +export const useScrollIntersection = ( + anchorRef: React.RefObject, +) => { + const containerRef = useContextSelector(PluginPageContext, v => v.containerRef) + const scrollIntersected = useContextSelector(MarketplaceContext, v => v.scrollIntersected) + const setScrollIntersected = useContextSelector(MarketplaceContext, v => v.setScrollIntersected) + + useEffect(() => { + let observer: IntersectionObserver | undefined + if (containerRef.current && anchorRef.current) { + observer = new IntersectionObserver((entries) => { + console.log(entries, 'entries') + if (entries[0].isIntersecting && !scrollIntersected) + setScrollIntersected(true) + + if (!entries[0].isIntersecting && scrollIntersected) + setScrollIntersected(false) + }, { + root: containerRef.current, + }) + observer.observe(anchorRef.current) + } + return () => observer?.disconnect() + }, [containerRef, anchorRef, scrollIntersected, setScrollIntersected]) +} diff --git a/web/app/components/plugins/marketplace/intersection-line/index.tsx b/web/app/components/plugins/marketplace/intersection-line/index.tsx new file mode 100644 index 0000000000..647247c995 --- /dev/null +++ b/web/app/components/plugins/marketplace/intersection-line/index.tsx @@ -0,0 +1,16 @@ +'use client' + +import { useRef } from 'react' +import { useScrollIntersection } from './hooks' + +const IntersectionLine = () => { + const ref = useRef(null) + + useScrollIntersection(ref) + + return ( +
+ ) +} + +export default IntersectionLine diff --git a/web/app/components/plugins/marketplace/list-wrapper.tsx b/web/app/components/plugins/marketplace/list-wrapper.tsx new file mode 100644 index 0000000000..6dd58bdcf5 --- /dev/null +++ b/web/app/components/plugins/marketplace/list-wrapper.tsx @@ -0,0 +1,23 @@ +'use client' + +import type { ReactNode } from 'react' +import cn from '@/utils/classnames' + +type ListWrapperProps = { + children: ReactNode +} +const ListWrapper = ({ + children, +}: ListWrapperProps) => { + return ( +
+ {children} +
+ ) +} + +export default ListWrapper diff --git a/web/app/components/plugins/marketplace/list.tsx b/web/app/components/plugins/marketplace/list.tsx index d9365fa54a..7a8d6daa5d 100644 --- a/web/app/components/plugins/marketplace/list.tsx +++ b/web/app/components/plugins/marketplace/list.tsx @@ -7,7 +7,7 @@ const List = () => { const locale = getLocaleOnServer() return ( -
+ <>
Featured
Our top picks to get you started
@@ -95,9 +95,135 @@ const List = () => { } /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + />
- + ) } diff --git a/web/app/components/plugins/marketplace/search-box/index.tsx b/web/app/components/plugins/marketplace/search-box/index.tsx index cb01f0ad10..923b16096c 100644 --- a/web/app/components/plugins/marketplace/search-box/index.tsx +++ b/web/app/components/plugins/marketplace/search-box/index.tsx @@ -5,8 +5,10 @@ import { useState, } from 'react' import { RiCloseLine } from '@remixicon/react' +import { useMarketplaceContext } from '../context' import TagsFilter from './tags-filter' import ActionButton from '@/app/components/base/action-button' +import cn from '@/utils/classnames' type SearchBoxProps = { onChange?: (searchText: string, tags: string[]) => void @@ -16,6 +18,7 @@ const SearchBox = ({ }: SearchBoxProps) => { const [searchText, setSearchText] = useState('') const [selectedTags, setSelectedTags] = useState([]) + const scrollIntersected = useMarketplaceContext(v => v.scrollIntersected) const handleTagsChange = useCallback((tags: string[]) => { setSelectedTags(tags) @@ -23,7 +26,12 @@ const SearchBox = ({ }, [searchText, onChange]) return ( -
+