mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-17 14:45:58 +08:00
feat: choose tool sticky
This commit is contained in:
parent
0f60fe7f2a
commit
bdb81fe20d
@ -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
|
||||||
|
ref={wrapElemRef}
|
||||||
|
className='max-h-[464px] overflow-y-auto'
|
||||||
|
onScroll={(pluginRef.current as any)?.handleScroll}
|
||||||
|
>
|
||||||
<Tools
|
<Tools
|
||||||
showWorkflowEmpty={activeTab === ToolTypeEnum.Workflow}
|
showWorkflowEmpty={activeTab === ToolTypeEnum.Workflow}
|
||||||
tools={tools}
|
tools={tools}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
viewType={activeView}
|
viewType={activeView}
|
||||||
/>
|
/>
|
||||||
|
<PluginList wrapElemRef={wrapElemRef} list={[toolNotion, extensionDallE, modelGPT4] as any} ref={pluginRef} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user