diff --git a/web/app/(commonLayout)/plugins/test/card/page.tsx b/web/app/(commonLayout)/plugins/test/card/page.tsx index facff918bc..a916606dfc 100644 --- a/web/app/(commonLayout)/plugins/test/card/page.tsx +++ b/web/app/(commonLayout)/plugins/test/card/page.tsx @@ -1,5 +1,5 @@ import Card from '@/app/components/plugins/card' -import { extensionDallE, modelGPT4, toolNotion } from '@/app/components/plugins/card-mock' +import { extensionDallE, modelGPT4, toolNotion } from '@/app/components/plugins/card/card-mock' import PluginItem from '@/app/components/plugins/plugin-item' const PluginList = async () => { diff --git a/web/app/components/plugins/card.tsx b/web/app/components/plugins/card.tsx deleted file mode 100644 index c3897e22c6..0000000000 --- a/web/app/components/plugins/card.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import React, { useMemo } from 'react' -import { RiCheckLine, RiVerifiedBadgeLine } from '@remixicon/react' -import type { FC } from 'react' -import { LeftCorner } from '../base/icons/src/vender/plugin' -import type { Plugin } from './types' -import { getLocaleOnServer } from '@/i18n/server' -import cn from '@/utils/classnames' - -export const CornerMark = ({ text }: { text: string }) => { - return ( -
- -
{text}
-
- ) -} - -export const Icon = ({ - className, - src, - installed = false, -}: { - className?: string - src: string - installed?: boolean -}) => { - return ( -
- {installed - &&
-
- -
-
- } -
- ) -} - -export const Title = ({ - title, -}: { - title: string -}) => { - return ( -
- {title} -
- ) -} - -export const OrgInfo = ({ - className, - orgName, - packageName, -}: { - className?: string - orgName: string - packageName: string -}) => { - return
- {orgName} - / - {packageName} -
-} - -type DescriptionProps = { - className?: string - text: string - descriptionLineRows: number -} - -export const Description: FC = ({ - className, - text, - descriptionLineRows, -}) => { - const lineClassName = useMemo(() => { - if (descriptionLineRows === 1) - return 'truncate' - else if (descriptionLineRows === 2) - return 'line-clamp-2' - else - return 'line-clamp-3' - }, [descriptionLineRows]) - return ( -
- {text} -
- ) -} - -type Props = { - className?: string - payload: Plugin - installed?: boolean - descriptionLineRows?: number - footer?: React.ReactNode -} - -const Card = ({ - className, - payload, - installed, - descriptionLineRows = 2, - footer, -}: Props) => { - const locale = getLocaleOnServer() - - const { type, name, org, label } = payload - return ( -
- - {/* Header */} -
- -
-
- - <RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" /> - </div> - <OrgInfo - className="mt-0.5" - orgName={org} - packageName={name} - /> - </div> - </div> - <Description - className="mt-3" - text={payload.brief[locale]} - descriptionLineRows={descriptionLineRows} - /> - {footer && <div>{footer}</div>} - </div> - ) -} - -export default Card diff --git a/web/app/components/plugins/card/base/corner-mark.tsx b/web/app/components/plugins/card/base/corner-mark.tsx new file mode 100644 index 0000000000..cdb9eb5417 --- /dev/null +++ b/web/app/components/plugins/card/base/corner-mark.tsx @@ -0,0 +1,12 @@ +import { LeftCorner } from '../../../base/icons/src/vender/plugin' + +const CornerMark = ({ text }: { text: string }) => { + return ( + <div className='absolute top-0 right-0 flex pl-[13px] '> + <LeftCorner className="text-background-section" /> + <div className="h-5 leading-5 rounded-tr-xl pr-2 bg-background-section text-text-tertiary system-2xs-medium-uppercase">{text}</div> + </div> + ) +} + +export default CornerMark diff --git a/web/app/components/plugins/card/base/description.tsx b/web/app/components/plugins/card/base/description.tsx new file mode 100644 index 0000000000..678e7b651e --- /dev/null +++ b/web/app/components/plugins/card/base/description.tsx @@ -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 'truncate' + else if (descriptionLineRows === 2) + return 'line-clamp-2' + else + return 'line-clamp-3' + }, [descriptionLineRows]) + return ( + <div className={cn('text-text-tertiary system-xs-regular', lineClassName, className)}> + {text} + </div> + ) +} + +export default Description diff --git a/web/app/components/plugins/card/base/icon.tsx b/web/app/components/plugins/card/base/icon.tsx new file mode 100644 index 0000000000..60be58007f --- /dev/null +++ b/web/app/components/plugins/card/base/icon.tsx @@ -0,0 +1,31 @@ +import { RiCheckLine } from '@remixicon/react' +import cn from '@/utils/classnames' + +const Icon = ({ + className, + src, + installed = false, +}: { + className?: string + src: string + installed?: boolean +}) => { + return ( + <div + className={cn('shrink-0 relative w-10 h-10 rounded-md bg-center bg-no-repeat bg-contain', className)} + style={{ + backgroundImage: `url(${src})`, + }} + > + {installed + && <div className='p-0.5 absolute bottom-[-4px] right-[-4px] w-3 h-3 rounded-full bg-white '> + <div className='h-full rounded-full bg-state-success-solid'> + <RiCheckLine className='w-full h-full text-text-primary-on-surface' /> + </div> + </div> + } + </div> + ) +} + +export default Icon diff --git a/web/app/components/plugins/card/base/org-info.tsx b/web/app/components/plugins/card/base/org-info.tsx new file mode 100644 index 0000000000..972b871375 --- /dev/null +++ b/web/app/components/plugins/card/base/org-info.tsx @@ -0,0 +1,19 @@ +import cn from '@/utils/classnames' + +const OrgInfo = ({ + className, + orgName, + packageName, +}: { + className?: string + orgName: string + packageName: string +}) => { + return <div className={cn('flex items-center h-4 space-x-0.5', className)}> + <span className="shrink-0 text-text-tertiary system-xs-regular">{orgName}</span> + <span className='shrink-0 text-text-quaternary system-xs-regular'>/</span> + <span className="shrink-0 w-0 grow truncate text-text-tertiary system-xs-regular">{packageName}</span> + </div> +} + +export default OrgInfo diff --git a/web/app/components/plugins/card/base/title.tsx b/web/app/components/plugins/card/base/title.tsx new file mode 100644 index 0000000000..bfdcd7fc2b --- /dev/null +++ b/web/app/components/plugins/card/base/title.tsx @@ -0,0 +1,13 @@ +const Title = ({ + title, +}: { + title: string +}) => { + return ( + <div className='max-w-[150px] truncate text-text-secondary system-md-semibold'> + {title} + </div> + ) +} + +export default Title diff --git a/web/app/components/plugins/card-mock.ts b/web/app/components/plugins/card/card-mock.ts similarity index 97% rename from web/app/components/plugins/card-mock.ts rename to web/app/components/plugins/card/card-mock.ts index 27f7fd5386..a3e75a3573 100644 --- a/web/app/components/plugins/card-mock.ts +++ b/web/app/components/plugins/card/card-mock.ts @@ -1,4 +1,4 @@ -import { PluginType } from './types' +import { PluginType } from '../types' export const toolNotion = { type: PluginType.tool, diff --git a/web/app/components/plugins/card-more-info.tsx b/web/app/components/plugins/card/card-more-info.tsx similarity index 100% rename from web/app/components/plugins/card-more-info.tsx rename to web/app/components/plugins/card/card-more-info.tsx diff --git a/web/app/components/plugins/card/index.tsx b/web/app/components/plugins/card/index.tsx new file mode 100644 index 0000000000..1cec64491e --- /dev/null +++ b/web/app/components/plugins/card/index.tsx @@ -0,0 +1,58 @@ +import React from 'react' +import { RiVerifiedBadgeLine } from '@remixicon/react' +import type { Plugin } from '../types' +import CornerMark from './base/corner-mark' +import Icon from './base/icon' +import Title from './base/title' +import OrgInfo from './base/org-info' +import Description from './base/description' +import cn from '@/utils/classnames' +import { getLocaleOnServer } from '@/i18n/server' + +type Props = { + className?: string + payload: Plugin + installed?: boolean + descriptionLineRows?: number + footer?: React.ReactNode +} + +const Card = ({ + className, + payload, + installed, + descriptionLineRows = 2, + footer, +}: Props) => { + const locale = getLocaleOnServer() + + const { type, name, org, label } = payload + return ( + <div className={cn('relative p-4 pb-3 border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg hover-bg-components-panel-on-panel-item-bg rounded-xl shadow-xs', className)}> + <CornerMark text={type} /> + {/* Header */} + <div className="flex"> + <Icon src={payload.icon} installed={installed} /> + <div className="ml-3 grow"> + <div className="flex items-center h-5"> + <Title title={label[locale]} /> + <RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" /> + </div> + <OrgInfo + className="mt-0.5" + orgName={org} + packageName={name} + /> + </div> + </div> + <Description + className="mt-3" + text={payload.brief[locale]} + descriptionLineRows={descriptionLineRows} + /> + {footer && <div>{footer}</div>} + </div> + ) +} + +export default Card diff --git a/web/app/components/plugins/plugin-item.tsx b/web/app/components/plugins/plugin-item.tsx index d685517ba4..c2d09cfa73 100644 --- a/web/app/components/plugins/plugin-item.tsx +++ b/web/app/components/plugins/plugin-item.tsx @@ -3,7 +3,12 @@ import React from 'react' import { RiArrowRightUpLine, RiVerifiedBadgeLine } from '@remixicon/react' import { Github } from '../base/icons/src/public/common' import type { Plugin } from './types' -import { CornerMark, Description, Icon, OrgInfo, Title } from '@/app/components/plugins/card' +import CornerMark from './card/base/corner-mark' +import Description from './card/base/description' +import Icon from './card/base/icon' +import OrgInfo from './card/base/org-info' +import Title from './card/base/title' + import cn from '@/utils/classnames' import { getLocaleOnServer } from '@/i18n/server'