feat: choose tool sticky

This commit is contained in:
Joel 2024-10-18 18:18:42 +08:00
parent 0f60fe7f2a
commit bdb81fe20d
4 changed files with 98 additions and 18 deletions

View File

@ -1,5 +1,6 @@
import { import {
useMemo, useMemo,
useRef,
useState, useState,
} from 'react' } from 'react'
import type { import type {
@ -51,6 +52,10 @@ const AllTools = ({
}) })
}) })
}, [activeTab, buildInTools, customTools, workflowTools, searchText, language]) }, [activeTab, buildInTools, customTools, workflowTools, searchText, language])
const pluginRef = useRef(null)
const wrapElemRef = useRef<HTMLDivElement>(null)
return ( return (
<div> <div>
<div className='flex items-center justify-between px-3 bg-background-default-hover border-b-[0.5px] border-black/[0.08] shadow-xs'> <div className='flex items-center justify-between px-3 bg-background-default-hover border-b-[0.5px] border-black/[0.08] shadow-xs'>
@ -73,13 +78,19 @@ const AllTools = ({
</div> </div>
<ViewTypeSelect viewType={activeView} onChange={setActiveView} /> <ViewTypeSelect viewType={activeView} onChange={setActiveView} />
</div> </div>
<PluginList list={[toolNotion, extensionDallE, modelGPT4] as any} /> <div
<Tools ref={wrapElemRef}
showWorkflowEmpty={activeTab === ToolTypeEnum.Workflow} className='max-h-[464px] overflow-y-auto'
tools={tools} onScroll={(pluginRef.current as any)?.handleScroll}
onSelect={onSelect} >
viewType={activeView} <Tools
/> showWorkflowEmpty={activeTab === ToolTypeEnum.Workflow}
tools={tools}
onSelect={onSelect}
viewType={activeView}
/>
<PluginList wrapElemRef={wrapElemRef} list={[toolNotion, extensionDallE, modelGPT4] as any} ref={pluginRef} />
</div>
</div> </div>
) )
} }

View File

@ -1,26 +1,50 @@
'use client' 'use client'
import type { FC } from 'react' import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import useStickyScroll, { ScrollPosition } from '../use-sticky-scroll'
import Item from './item' import Item from './item'
import type { Plugin } from '@/app/components/plugins/types.ts' import type { Plugin } from '@/app/components/plugins/types.ts'
import cn from '@/utils/classnames'
type Props = { type Props = {
wrapElemRef: React.RefObject<HTMLElement>
list: Plugin[] list: Plugin[]
// onInstall: () =>
} }
const List: FC<Props> = ({ const List = ({
wrapElemRef,
list, list,
}) => { }: Props, ref: any) => {
const { t } = useTranslation() const { t } = useTranslation()
const nextToStickyELemRef = useRef<HTMLDivElement>(null)
const { handleScroll, scrollPosition } = useStickyScroll({
wrapElemRef,
nextToStickyELemRef,
})
const stickyClassName = useMemo(() => {
switch (scrollPosition) {
case ScrollPosition.aboveTheWrap:
return 'top-0 shadow-md bg-white'
case ScrollPosition.showing:
return 'bottom-0'
case ScrollPosition.belowTheWrap:
return 'bottom-0 border-t border-gray-500 bg-white text-blue-500'
}
}, [scrollPosition])
useImperativeHandle(ref, () => ({
handleScroll,
}))
return ( return (
<div> <>
<div className='pt-3 px-4 py-1 text-text-primary system-sm-medium'> <div
className={cn('sticky z-10 pt-3 px-4 py-1 text-text-primary system-sm-medium', stickyClassName)}>
{t('plugin.fromMarketplace')} {t('plugin.fromMarketplace')}
</div> </div>
<div className='p-1'> <div className='p-1 pb-[500px]' ref={nextToStickyELemRef}>
{list.map((item, index) => ( {list.map((item, index) => (
<Item <Item
key={index} key={index}
@ -29,7 +53,7 @@ const List: FC<Props> = ({
/> />
))} ))}
</div> </div>
</div> </>
) )
} }
export default React.memo(List) export default forwardRef(List)

View File

@ -75,7 +75,7 @@ const Blocks = ({
} }
return ( return (
<div className='p-1 max-w-[320px] max-h-[464px] overflow-y-auto'> <div className='p-1 max-w-[320px]'>
{ {
!tools.length && !showWorkflowEmpty && ( !tools.length && !showWorkflowEmpty && (
<div className='flex items-center px-3 h-[22px] text-xs font-medium text-gray-500'>{t('workflow.tabs.noResult')}</div> <div className='flex items-center px-3 h-[22px] text-xs font-medium text-gray-500'>{t('workflow.tabs.noResult')}</div>

View File

@ -0,0 +1,45 @@
import React from 'react'
import { useThrottleFn } from 'ahooks'
export enum ScrollPosition {
belowTheWrap = 'belowTheWrap',
showing = 'showing',
aboveTheWrap = 'aboveTheWrap',
}
type Params = {
wrapElemRef: React.RefObject<HTMLElement>
nextToStickyELemRef: React.RefObject<HTMLElement>
}
const useStickyScroll = ({
wrapElemRef,
nextToStickyELemRef,
}: Params) => {
const [scrollPosition, setScrollPosition] = React.useState<ScrollPosition>(ScrollPosition.belowTheWrap)
const { run: handleScroll } = useThrottleFn(() => {
const wrapDom = wrapElemRef.current
const stickyDOM = nextToStickyELemRef.current
if (!wrapDom || !stickyDOM)
return
const { height: wrapHeight, top: wrapTop } = wrapDom.getBoundingClientRect()
const { top: nextToStickyTop } = stickyDOM.getBoundingClientRect()
let scrollPositionNew = ScrollPosition.belowTheWrap
if (nextToStickyTop - wrapTop >= wrapHeight)
scrollPositionNew = ScrollPosition.belowTheWrap
else if (nextToStickyTop <= wrapTop)
scrollPositionNew = ScrollPosition.aboveTheWrap
else
scrollPositionNew = ScrollPosition.showing
if (scrollPosition !== scrollPositionNew)
setScrollPosition(scrollPositionNew)
}, { wait: 100 })
return {
handleScroll,
scrollPosition,
}
}
export default useStickyScroll