mirror of
https://github.com/langgenius/dify.git
synced 2026-06-17 06:21:07 +08:00
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: yyh <yuanyouhuilyz@gmail.com> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: hjlarry <hjlarry@163.com> Co-authored-by: fatelei <fatelei@gmail.com> Co-authored-by: Asuka Minato <i@asukaminato.eu.org> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com> Co-authored-by: gigglewang <gigglewang@dify.ai> Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai> Co-authored-by: chariri <w@chariri.moe> Co-authored-by: Evan <2869018789@qq.com> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
152 lines
4.9 KiB
TypeScript
152 lines
4.9 KiB
TypeScript
'use client'
|
|
|
|
import type { App } from '@/models/explore'
|
|
import type { TryAppSelection } from '@/types/try-app'
|
|
import { cn } from '@langgenius/dify-ui/cn'
|
|
import * as React from 'react'
|
|
import { useEffect, useRef, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { useLearnDifyAppList } from '@/service/use-explore'
|
|
import { useLearnDifyVisibleValue, useSetLearnDifyHidden } from './atoms'
|
|
import LearnDifyItem from './item'
|
|
|
|
type LearnDifyProps = {
|
|
canCreate?: boolean
|
|
className?: string
|
|
dismissible?: boolean
|
|
itemLimit?: number
|
|
loadingFallback?: React.ReactNode
|
|
onCreate?: (app: App) => void
|
|
onTry?: (params: TryAppSelection) => void
|
|
showDescription?: boolean
|
|
title?: string
|
|
}
|
|
|
|
type LearnDifyContentProps = LearnDifyProps & {
|
|
onHide?: () => void
|
|
}
|
|
|
|
const LearnDifyContent = ({
|
|
canCreate = false,
|
|
className,
|
|
itemLimit,
|
|
loadingFallback = null,
|
|
onHide,
|
|
onCreate,
|
|
onTry,
|
|
showDescription = true,
|
|
title,
|
|
}: LearnDifyContentProps) => {
|
|
const { t } = useTranslation()
|
|
const [isClosing, setIsClosing] = useState(false)
|
|
const [collapseTransform, setCollapseTransform] = useState<string>()
|
|
const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
const sectionRef = useRef<HTMLElement>(null)
|
|
const { data: learnDifyItems = [], isLoading } = useLearnDifyAppList()
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (hideTimerRef.current)
|
|
clearTimeout(hideTimerRef.current)
|
|
}
|
|
}, [])
|
|
|
|
const handleHide = () => {
|
|
const sectionRect = sectionRef.current?.getBoundingClientRect()
|
|
const helpTargetRect = document.querySelector('[data-learn-dify-help-target]')?.getBoundingClientRect()
|
|
if (sectionRect && helpTargetRect) {
|
|
const sectionCenterX = sectionRect.left + sectionRect.width / 2
|
|
const sectionCenterY = sectionRect.top + sectionRect.height / 2
|
|
const helpCenterX = helpTargetRect.left + helpTargetRect.width / 2
|
|
const helpCenterY = helpTargetRect.top + helpTargetRect.height / 2
|
|
|
|
setCollapseTransform(`translate3d(${helpCenterX - sectionCenterX}px, ${helpCenterY - sectionCenterY}px, 0) scale(0.08)`)
|
|
}
|
|
else {
|
|
setCollapseTransform('scale(0.08)')
|
|
}
|
|
setIsClosing(true)
|
|
hideTimerRef.current = setTimeout(() => {
|
|
onHide?.()
|
|
setIsClosing(false)
|
|
setCollapseTransform(undefined)
|
|
}, 800)
|
|
}
|
|
|
|
const visibleItems = itemLimit ? learnDifyItems.slice(0, itemLimit) : learnDifyItems
|
|
const sectionTitle = title ?? t('learnDify.title', { ns: 'explore' })
|
|
|
|
if (isLoading)
|
|
return loadingFallback
|
|
if (visibleItems.length === 0)
|
|
return null
|
|
|
|
return (
|
|
<section
|
|
ref={sectionRef}
|
|
className={cn(
|
|
'px-8 pb-6 transition-[opacity,transform] duration-800 ease-in-out',
|
|
isClosing && 'pointer-events-none relative z-50 opacity-20',
|
|
className,
|
|
)}
|
|
style={isClosing ? { transform: collapseTransform, transformOrigin: 'center center' } : undefined}
|
|
aria-labelledby="learn-dify-title"
|
|
>
|
|
<div className="-mx-4 rounded-2xl bg-background-section p-4">
|
|
<div className="flex items-start justify-between gap-4 pb-2.5">
|
|
<div className="min-w-0">
|
|
<h2 id="learn-dify-title" className="truncate system-xl-medium text-text-primary" title={sectionTitle}>
|
|
{sectionTitle}
|
|
</h2>
|
|
{showDescription && (
|
|
<p className="mt-0.5 truncate system-xs-regular text-text-tertiary">
|
|
{t('learnDify.description', { ns: 'explore' })}
|
|
</p>
|
|
)}
|
|
</div>
|
|
{onHide && (
|
|
<button
|
|
type="button"
|
|
className="flex size-8 shrink-0 items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary focus-visible:bg-state-base-hover focus-visible:ring-1 focus-visible:ring-components-input-border-hover focus-visible:outline-hidden"
|
|
aria-label={t('learnDify.hide', { ns: 'explore' })}
|
|
onClick={handleHide}
|
|
>
|
|
<span className="i-ri-close-line size-4" aria-hidden="true" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
<div className="grid grid-cols-1 gap-2.5 sm:grid-cols-2 xl:grid-cols-4">
|
|
{visibleItems.map(item => (
|
|
<LearnDifyItem
|
|
key={item.app_id}
|
|
canCreate={canCreate}
|
|
item={item}
|
|
onCreate={onCreate}
|
|
onTry={onTry}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
const DismissibleLearnDify = (props: LearnDifyProps) => {
|
|
const visible = useLearnDifyVisibleValue()
|
|
const setHidden = useSetLearnDifyHidden()
|
|
|
|
if (!visible)
|
|
return null
|
|
|
|
return <LearnDifyContent {...props} onHide={() => setHidden(true)} />
|
|
}
|
|
|
|
const LearnDify = (props: LearnDifyProps) => {
|
|
if (props.dismissible === false)
|
|
return <LearnDifyContent {...props} />
|
|
|
|
return <DismissibleLearnDify {...props} />
|
|
}
|
|
|
|
export default React.memo(LearnDify)
|