dify
This commit is contained in:
69
dify/web/app/components/plugins/card/base/card-icon.tsx
Normal file
69
dify/web/app/components/plugins/card/base/card-icon.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { RiCheckLine, RiCloseLine } from '@remixicon/react'
|
||||
import { Mcp } from '@/app/components/base/icons/src/vender/other'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import cn from '@/utils/classnames'
|
||||
import { shouldUseMcpIcon } from '@/utils/mcp'
|
||||
|
||||
const iconSizeMap = {
|
||||
xs: 'w-4 h-4 text-base',
|
||||
tiny: 'w-6 h-6 text-base',
|
||||
small: 'w-8 h-8',
|
||||
medium: 'w-9 h-9',
|
||||
large: 'w-10 h-10',
|
||||
}
|
||||
const Icon = ({
|
||||
className,
|
||||
src,
|
||||
installed = false,
|
||||
installFailed = false,
|
||||
size = 'large',
|
||||
}: {
|
||||
className?: string
|
||||
src: string | {
|
||||
content: string
|
||||
background: string
|
||||
}
|
||||
installed?: boolean
|
||||
installFailed?: boolean
|
||||
size?: 'xs' | 'tiny' | 'small' | 'medium' | 'large'
|
||||
}) => {
|
||||
const iconClassName = 'flex justify-center items-center gap-2 absolute bottom-[-4px] right-[-4px] w-[18px] h-[18px] rounded-full border-2 border-components-panel-bg'
|
||||
if (typeof src === 'object') {
|
||||
return (
|
||||
<div className={cn('relative', className)}>
|
||||
<AppIcon
|
||||
size={size}
|
||||
iconType={'emoji'}
|
||||
icon={src.content}
|
||||
background={src.background}
|
||||
className='rounded-md'
|
||||
innerIcon={shouldUseMcpIcon(src) ? <Mcp className='h-8 w-8 text-text-primary-on-surface' /> : undefined}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('relative shrink-0 rounded-md bg-contain bg-center bg-no-repeat', iconSizeMap[size], className)}
|
||||
style={{
|
||||
backgroundImage: `url(${src})`,
|
||||
}}
|
||||
>
|
||||
{
|
||||
installed
|
||||
&& <div className={cn(iconClassName, 'bg-state-success-solid')}>
|
||||
<RiCheckLine className='h-3 w-3 text-text-primary-on-surface' />
|
||||
</div>
|
||||
}
|
||||
{
|
||||
installFailed
|
||||
&& <div className={cn(iconClassName, 'bg-state-destructive-solid')}>
|
||||
<RiCloseLine className='h-3 w-3 text-text-primary-on-surface' />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Icon
|
||||
12
dify/web/app/components/plugins/card/base/corner-mark.tsx
Normal file
12
dify/web/app/components/plugins/card/base/corner-mark.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { LeftCorner } from '../../../base/icons/src/vender/plugin'
|
||||
|
||||
const CornerMark = ({ text }: { text: string }) => {
|
||||
return (
|
||||
<div className='absolute right-0 top-0 flex pl-[13px] '>
|
||||
<LeftCorner className="text-background-section" />
|
||||
<div className="system-2xs-medium-uppercase h-5 rounded-tr-xl bg-background-section pr-2 leading-5 text-text-tertiary">{text}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CornerMark
|
||||
31
dify/web/app/components/plugins/card/base/description.tsx
Normal file
31
dify/web/app/components/plugins/card/base/description.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
text: string
|
||||
descriptionLineRows: number
|
||||
}
|
||||
|
||||
const Description: FC<Props> = ({
|
||||
className,
|
||||
text,
|
||||
descriptionLineRows,
|
||||
}) => {
|
||||
const lineClassName = useMemo(() => {
|
||||
if (descriptionLineRows === 1)
|
||||
return 'h-4 truncate'
|
||||
else if (descriptionLineRows === 2)
|
||||
return 'h-8 line-clamp-2'
|
||||
else
|
||||
return 'h-12 line-clamp-3'
|
||||
}, [descriptionLineRows])
|
||||
return (
|
||||
<div className={cn('system-xs-regular text-text-tertiary', lineClassName, className)}>
|
||||
{text}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Description
|
||||
19
dify/web/app/components/plugins/card/base/download-count.tsx
Normal file
19
dify/web/app/components/plugins/card/base/download-count.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { RiInstallLine } from '@remixicon/react'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
|
||||
type Props = {
|
||||
downloadCount: number
|
||||
}
|
||||
|
||||
const DownloadCount = ({
|
||||
downloadCount,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-1 text-text-tertiary">
|
||||
<RiInstallLine className="h-3 w-3 shrink-0" />
|
||||
<div className="system-xs-regular">{formatNumber(downloadCount)}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DownloadCount
|
||||
30
dify/web/app/components/plugins/card/base/org-info.tsx
Normal file
30
dify/web/app/components/plugins/card/base/org-info.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import cn from '@/utils/classnames'
|
||||
type Props = {
|
||||
className?: string
|
||||
orgName?: string
|
||||
packageName: string
|
||||
packageNameClassName?: string
|
||||
}
|
||||
|
||||
const OrgInfo = ({
|
||||
className,
|
||||
orgName,
|
||||
packageName,
|
||||
packageNameClassName,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div className={cn('flex h-4 items-center space-x-0.5', className)}>
|
||||
{orgName && (
|
||||
<>
|
||||
<span className='system-xs-regular shrink-0 text-text-tertiary'>{orgName}</span>
|
||||
<span className='system-xs-regular shrink-0 text-text-quaternary'>/</span>
|
||||
</>
|
||||
)}
|
||||
<span className={cn('system-xs-regular w-0 shrink-0 grow truncate text-text-tertiary', packageNameClassName)}>
|
||||
{packageName}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrgInfo
|
||||
51
dify/web/app/components/plugins/card/base/placeholder.tsx
Normal file
51
dify/web/app/components/plugins/card/base/placeholder.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Group } from '../../../base/icons/src/vender/other'
|
||||
import Title from './title'
|
||||
import { SkeletonContainer, SkeletonPoint, SkeletonRectangle, SkeletonRow } from '@/app/components/base/skeleton'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
wrapClassName: string
|
||||
loadingFileName?: string
|
||||
}
|
||||
|
||||
export const LoadingPlaceholder = ({ className }: { className?: string }) => (
|
||||
<div className={cn('h-2 rounded-sm bg-text-quaternary opacity-20', className)} />
|
||||
)
|
||||
|
||||
const Placeholder = ({
|
||||
wrapClassName,
|
||||
loadingFileName,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div className={wrapClassName}>
|
||||
<SkeletonRow>
|
||||
<div
|
||||
className='flex h-10 w-10 items-center justify-center gap-2 rounded-[10px] border-[0.5px]
|
||||
border-components-panel-border bg-background-default p-1 backdrop-blur-sm'>
|
||||
<div className='flex h-5 w-5 items-center justify-center'>
|
||||
<Group className='text-text-tertiary' />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grow">
|
||||
<SkeletonContainer>
|
||||
<div className="flex h-5 items-center">
|
||||
{loadingFileName ? (
|
||||
<Title title={loadingFileName} />
|
||||
) : (
|
||||
<SkeletonRectangle className="w-[260px]" />
|
||||
)}
|
||||
</div>
|
||||
<SkeletonRow className="h-4">
|
||||
<SkeletonRectangle className="w-[41px]" />
|
||||
<SkeletonPoint />
|
||||
<SkeletonRectangle className="w-[180px]" />
|
||||
</SkeletonRow>
|
||||
</SkeletonContainer>
|
||||
</div>
|
||||
</SkeletonRow>
|
||||
<SkeletonRectangle className="mt-3 w-[420px]" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Placeholder
|
||||
13
dify/web/app/components/plugins/card/base/title.tsx
Normal file
13
dify/web/app/components/plugins/card/base/title.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
const Title = ({
|
||||
title,
|
||||
}: {
|
||||
title: string
|
||||
}) => {
|
||||
return (
|
||||
<div className='system-md-semibold truncate text-text-secondary'>
|
||||
{title}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Title
|
||||
Reference in New Issue
Block a user