11/* eslint-disable jsx-a11y/no-static-element-interactions,jsx-a11y/click-events-have-key-events */
22import type { IAttachmentCellValue } from '@teable/core' ;
33import { FieldKeyType } from '@teable/core' ;
4- import { ArrowDown , ArrowUp , Image , Maximize2 , Trash } from '@teable/icons' ;
4+ import { ArrowDown , ArrowUp , Copy , Image , Maximize2 , Trash } from '@teable/icons' ;
55import type { IRecordInsertOrderRo } from '@teable/openapi' ;
6- import { createRecords , deleteRecord } from '@teable/openapi' ;
7- import { CellValue , getFileCover } from '@teable/sdk/components' ;
8- import { useAttachmentPreviewI18Map } from '@teable/sdk/components/hooks' ;
6+ import { createRecords , deleteRecord , duplicateRecord } from '@teable/openapi' ;
7+ import { CellValue } from '@teable/sdk/components' ;
98import { useFieldStaticGetter , useTableId , useViewId } from '@teable/sdk/hooks' ;
109import type { Record } from '@teable/sdk/model' ;
11- import { FilePreviewItem , FilePreviewProvider } from '@teable/ui-lib/base' ;
1210import {
13- Carousel ,
14- CarouselContent ,
15- CarouselItem ,
16- CarouselNext ,
17- CarouselPrevious ,
1811 ContextMenu ,
1912 ContextMenuContent ,
2013 ContextMenuItem ,
@@ -26,6 +19,7 @@ import { useTranslation } from 'react-i18next';
2619import { tableConfig } from '@/features/i18n/table.config' ;
2720import { useGallery } from '../hooks' ;
2821import { CARD_COVER_HEIGHT , CARD_STYLE } from '../utils' ;
22+ import { CardCarousel } from './CardCarousel' ;
2923
3024interface IKanbanCardProps {
3125 card : Record ;
@@ -36,7 +30,6 @@ export const Card = (props: IKanbanCardProps) => {
3630 const tableId = useTableId ( ) ;
3731 const viewId = useViewId ( ) ;
3832 const getFieldStatic = useFieldStaticGetter ( ) ;
39- const i18nMap = useAttachmentPreviewI18Map ( ) ;
4033 const { t } = useTranslation ( tableConfig . i18nNamespaces ) ;
4134 const {
4235 coverField,
@@ -70,6 +63,11 @@ export const Card = (props: IKanbanCardProps) => {
7063 deleteRecord ( tableId , card . id ) ;
7164 } ;
7265
66+ const onDuplicate = ( ) => {
67+ if ( tableId == null || viewId == null ) return ;
68+ duplicateRecord ( tableId , card . id , { viewId, anchorId : card . id , position : 'after' } ) ;
69+ } ;
70+
7371 const onInsert = async ( position : IRecordInsertOrderRo [ 'position' ] ) => {
7472 if ( tableId == null || viewId == null ) return ;
7573 const res = await createRecords ( tableId , {
@@ -98,61 +96,7 @@ export const Card = (props: IKanbanCardProps) => {
9896 { coverFieldId && (
9997 < Fragment >
10098 { coverCellValue ?. length ? (
101- < FilePreviewProvider i18nMap = { i18nMap } >
102- < Carousel
103- opts = { {
104- watchDrag : false ,
105- watchResize : false ,
106- watchSlides : false ,
107- } }
108- className = "border-b"
109- >
110- < CarouselContent className = "ml-0" >
111- { coverCellValue . map (
112- ( { id, name, size, mimetype, presignedUrl, lgThumbnailUrl } ) => {
113- const url = lgThumbnailUrl ?? getFileCover ( mimetype , presignedUrl ) ;
114- return (
115- < CarouselItem
116- key = { id }
117- style = { { height : CARD_COVER_HEIGHT } }
118- className = "relative size-full pl-0"
119- >
120- < FilePreviewItem
121- key = { id }
122- className = "size-full cursor-pointer"
123- src = { presignedUrl || '' }
124- name = { name }
125- mimetype = { mimetype }
126- size = { size }
127- >
128- < img
129- src = { url }
130- alt = "card cover"
131- className = "size-full"
132- style = { {
133- objectFit : isCoverFit ? 'contain' : 'cover' ,
134- } }
135- />
136- </ FilePreviewItem >
137- </ CarouselItem >
138- ) ;
139- }
140- ) }
141- </ CarouselContent >
142- { coverCellValue ?. length > 1 && (
143- < Fragment >
144- < CarouselPrevious
145- className = "left-1 size-7"
146- onClick = { ( e ) => e . stopPropagation ( ) }
147- />
148- < CarouselNext
149- className = "right-1 size-7"
150- onClick = { ( e ) => e . stopPropagation ( ) }
151- />
152- </ Fragment >
153- ) }
154- </ Carousel >
155- </ FilePreviewProvider >
99+ < CardCarousel value = { coverCellValue } isCoverFit = { isCoverFit } />
156100 ) : (
157101 < div
158102 style = { { height : CARD_COVER_HEIGHT } }
@@ -204,6 +148,10 @@ export const Card = (props: IKanbanCardProps) => {
204148 { t ( 'table:kanban.cardMenu.insertCardBelow' ) }
205149 </ ContextMenuItem >
206150 < ContextMenuSeparator />
151+ < ContextMenuItem onClick = { onDuplicate } >
152+ < Copy className = "mr-2 size-4" />
153+ { t ( 'table:kanban.cardMenu.duplicateCard' ) }
154+ </ ContextMenuItem >
207155 </ >
208156 ) }
209157 < ContextMenuItem onClick = { onExpand } >
0 commit comments