@baishuyun/ai-design 2.1.0-alpha.0 → 2.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main.ts +31 -31
- package/.storybook/preview.ts +16 -16
- package/CHANGELOG.md +530 -512
- package/components.json +22 -22
- package/package.json +5 -5
- package/scripts/strip-layers.cjs +41 -41
- package/src/components/abs-fullscreen-gradient-bg.tsx +35 -35
- package/src/components/agent-helper/agent-category-select-menu/index.tsx +57 -57
- package/src/components/agent-helper/agent-entry-card/index.tsx +94 -94
- package/src/components/agent-helper/agent-helper-card/index.tsx +179 -179
- package/src/components/agent-helper/agent-helper-card-list/index.tsx +59 -59
- package/src/components/agent-helper/agent-helper-main-layout/index.tsx +60 -60
- package/src/components/agent-helper/agent-sort-select/index.tsx +89 -89
- package/src/components/agent-helper/agent-tag/index.tsx +29 -29
- package/src/components/agent-helper/app-select/index.tsx +559 -559
- package/src/components/agent-helper/condition-group/index.tsx +75 -75
- package/src/components/agent-helper/condition-input/index.tsx +233 -233
- package/src/components/agent-helper/condition-item/index.tsx +237 -237
- package/src/components/agent-helper/condition-quantifier-select/index.tsx +142 -142
- package/src/components/agent-helper/condition-trigger-description/index.tsx +49 -49
- package/src/components/agent-helper/date-picker/calendar-panel.tsx +164 -164
- package/src/components/agent-helper/date-picker/date-input.tsx +91 -91
- package/src/components/agent-helper/date-picker/date-picker.tsx +327 -327
- package/src/components/agent-helper/date-picker/footer-panel.tsx +44 -44
- package/src/components/agent-helper/date-picker/index.ts +3 -3
- package/src/components/agent-helper/date-picker/month-panel.tsx +82 -82
- package/src/components/agent-helper/date-picker/time-panel.tsx +146 -146
- package/src/components/agent-helper/date-picker/types.ts +37 -37
- package/src/components/agent-helper/date-picker/utils.ts +121 -121
- package/src/components/agent-helper/date-picker/year-panel.tsx +86 -86
- package/src/components/agent-helper/exec-log-filter/index.tsx +232 -232
- package/src/components/agent-helper/field-edit-preference-select/index.tsx +291 -291
- package/src/components/agent-helper/field-select/index.tsx +466 -466
- package/src/components/agent-helper/field-value-input/china-address-data.ts +4117 -4117
- package/src/components/agent-helper/field-value-input/display.tsx +71 -71
- package/src/components/agent-helper/field-value-input/icons.tsx +38 -38
- package/src/components/agent-helper/field-value-input/index.tsx +144 -144
- package/src/components/agent-helper/field-value-input/inputs.tsx +247 -247
- package/src/components/agent-helper/field-value-input/picker-inputs.tsx +1144 -1141
- package/src/components/agent-helper/flow-node-action-trigger-select/index.tsx +57 -57
- package/src/components/agent-helper/form-action-trigger-select/index.tsx +60 -60
- package/src/components/agent-helper/form-divider/index.tsx +44 -44
- package/src/components/agent-helper/form-select/index.tsx +732 -732
- package/src/components/agent-helper/monthdays-select/index.tsx +378 -378
- package/src/components/agent-helper/offset-preference-select/index.tsx +268 -268
- package/src/components/agent-helper/offset-type-select/index.tsx +66 -66
- package/src/components/agent-helper/offset-unit-select/index.tsx +54 -54
- package/src/components/agent-helper/placeholder-card/index.tsx +75 -75
- package/src/components/agent-helper/prompt-input/index.tsx +646 -646
- package/src/components/agent-helper/relational-operator-select/index.tsx +179 -179
- package/src/components/agent-helper/repeat-by-type-select/index.tsx +53 -53
- package/src/components/agent-helper/repeat-preference-select/index.tsx +162 -162
- package/src/components/agent-helper/repeat-preference-type-select/index.tsx +58 -58
- package/src/components/agent-helper/search-input/index.tsx +35 -35
- package/src/components/agent-helper/table/index.tsx +114 -114
- package/src/components/agent-helper/trigger-select/index.tsx +121 -121
- package/src/components/agent-helper/trigger-time-preference-select/index.tsx +189 -189
- package/src/components/agent-helper/trigger-time-source-type-select/index.tsx +53 -53
- package/src/components/agent-helper/weekdays-select/index.tsx +230 -230
- package/src/components/agent-scroll-selector.tsx +193 -193
- package/src/components/agent-select.tsx +154 -154
- package/src/components/attachment-part-group.tsx +35 -35
- package/src/components/attachment-part.tsx +296 -296
- package/src/components/attachments-previewer.tsx +272 -272
- package/src/components/base-button.tsx +49 -49
- package/src/components/border-animation.tsx +107 -107
- package/src/components/border-color-animation.tsx +66 -66
- package/src/components/bot-avatar-name.tsx +36 -36
- package/src/components/bot-icon.tsx +23 -23
- package/src/components/bs-entry-select/app-group-folder.tsx +61 -61
- package/src/components/bs-entry-select/bs-entry-select.tsx +296 -296
- package/src/components/bs-entry-select/build-tree.ts +265 -265
- package/src/components/bs-entry-select/entry-item.tsx +45 -45
- package/src/components/bs-entry-select/entry-popover.tsx +99 -99
- package/src/components/bs-entry-select/index.ts +7 -7
- package/src/components/bs-entry-select/other-app-section.tsx +96 -96
- package/src/components/bs-entry-select/scroll-slider.tsx +872 -872
- package/src/components/bs-entry-select/search-input.tsx +4 -4
- package/src/components/bs-entry-select/tree-item-node.tsx +84 -84
- package/src/components/bs-entry-select/types.ts +46 -46
- package/src/components/bs-icons.tsx +1362 -1362
- package/src/components/card-field.tsx +23 -23
- package/src/components/card.tsx +36 -36
- package/src/components/chat-area-header.tsx +65 -65
- package/src/components/check-card.tsx +77 -77
- package/src/components/circle-checker.tsx +66 -66
- package/src/components/collapsible-txt-msg.tsx +80 -75
- package/src/components/collapsible.tsx +9 -9
- package/src/components/confirm-dialog.tsx +139 -139
- package/src/components/date-range-picker.tsx +91 -91
- package/src/components/fake-bot-msg.tsx +53 -51
- package/src/components/field-checker-list-msg.tsx +146 -146
- package/src/components/field-checker.tsx +185 -185
- package/src/components/field-icon.tsx +13 -13
- package/src/components/field-value-checker.tsx +116 -116
- package/src/components/fields-design-info-table.tsx +160 -160
- package/src/components/fields-generating-indicator.tsx +95 -95
- package/src/components/fields-portal.tsx +18 -18
- package/src/components/fields-previewer.tsx +216 -216
- package/src/components/font-icon.tsx +65 -65
- package/src/components/form-info-editor.tsx +263 -263
- package/src/components/formula-tag.tsx +32 -32
- package/src/components/generate-animation.tsx +56 -56
- package/src/components/icon-btn.tsx +43 -43
- package/src/components/icon-card.tsx +67 -67
- package/src/components/icons/calendar-icon.tsx +5 -5
- package/src/components/icons/clear-all-icon.tsx +5 -5
- package/src/components/icons/empty-agent-icon.tsx +93 -93
- package/src/components/icons/empty-table-icon.tsx +104 -104
- package/src/components/icons/grids-icon.tsx +5 -5
- package/src/components/img-part.tsx +27 -27
- package/src/components/legacy-slot.tsx +110 -110
- package/src/components/line-checker.tsx +168 -168
- package/src/components/linear-gradient-border-btn.tsx +48 -48
- package/src/components/linear-gradient-color-bg-animation.tsx +103 -103
- package/src/components/memoized-markdown.tsx +32 -32
- package/src/components/message-content.tsx +21 -21
- package/src/components/number-part.tsx +46 -46
- package/src/components/office-container.tsx +289 -289
- package/src/components/option-item.tsx +27 -27
- package/src/components/output-config-editor.tsx +478 -478
- package/src/components/previewer-header.tsx +270 -270
- package/src/components/primary-confirm-btn.tsx +58 -58
- package/src/components/primary-entry-btn.tsx +48 -48
- package/src/components/radio-card.tsx +63 -63
- package/src/components/scroll-to-bottom-btn.tsx +28 -28
- package/src/components/split-line.tsx +9 -9
- package/src/components/square-checker.tsx +87 -87
- package/src/components/subform-fields-value-checker.tsx +184 -184
- package/src/components/tab-radio-group.tsx +83 -83
- package/src/components/tooltip.tsx +53 -53
- package/src/components/ui/button.tsx +51 -51
- package/src/components/ui/calendar.tsx +57 -57
- package/src/components/ui/dialog.tsx +104 -104
- package/src/components/ui/drawer.tsx +279 -279
- package/src/components/ui/form.tsx +202 -202
- package/src/components/ui/icons.tsx +1267 -1267
- package/src/components/ui/input.tsx +28 -28
- package/src/components/ui/label.tsx +30 -30
- package/src/components/ui/popover.tsx +85 -85
- package/src/components/ui/select.tsx +236 -236
- package/src/components/ui/spinner.tsx +17 -17
- package/src/components/ui/switch.tsx +33 -33
- package/src/components/ui/tabs.tsx +62 -62
- package/src/components/ui/textarea.tsx +28 -28
- package/src/components/ui/tooltip.tsx +53 -53
- package/src/components/user-txt-msg-bubble.tsx +47 -47
- package/src/components/warning-msg.tsx +27 -27
- package/src/const/ui.ts +119 -119
- package/src/css.d.ts +4 -4
- package/src/hooks/use-column-appear.ts +78 -78
- package/src/hooks/use-portal-container.ts +27 -27
- package/src/hooks/use-resizable-width.ts +129 -129
- package/src/hooks/use-select-popover-position.ts +74 -74
- package/src/hooks/use-shadow.ts +127 -127
- package/src/index.ts +226 -226
- package/src/lib/utils.ts +138 -138
- package/src/stories/AbsFullscreenGradientBg.stories.tsx +36 -36
- package/src/stories/AgentCategorySelectMenu.stories.tsx +33 -33
- package/src/stories/AgentEntryCard.stories.tsx +90 -90
- package/src/stories/AgentHelperCard.stories.tsx +105 -105
- package/src/stories/AgentHelperCardList.stories.tsx +120 -120
- package/src/stories/AgentHelperMainLayout.stories.tsx +71 -71
- package/src/stories/AgentScrollSelector.stories.tsx +95 -95
- package/src/stories/AgentSelect.stories.tsx +134 -134
- package/src/stories/AgentTag.stories.tsx +44 -44
- package/src/stories/AppSelect.stories.tsx +250 -250
- package/src/stories/AttachmentPart.stories.tsx +122 -122
- package/src/stories/AttachmentPartGroup.stories.tsx +152 -152
- package/src/stories/AttachmentsPreviewer.stories.tsx +306 -306
- package/src/stories/BaseButton.stories.tsx +33 -33
- package/src/stories/BorderAnimation.stories.tsx +116 -116
- package/src/stories/BorderColorAnimation.stories.tsx +63 -63
- package/src/stories/BotAvatarAndName.stories.ts +24 -24
- package/src/stories/BotIcon.stories.ts +22 -22
- package/src/stories/BsConditionInput.stories.tsx +460 -460
- package/src/stories/BsEntrySelect.stories.tsx +343 -343
- package/src/stories/BsTooltip.stories.tsx +32 -32
- package/src/stories/Card.stories.tsx +28 -28
- package/src/stories/CardField.stories.tsx +87 -87
- package/src/stories/ChatAreaHeader.stories.ts +26 -26
- package/src/stories/CheckCard.stories.tsx +114 -114
- package/src/stories/CircleChecker.stories.tsx +89 -89
- package/src/stories/CollapsibleTextMsg.stories.ts +44 -44
- package/src/stories/ConditionGroup.stories.tsx +216 -216
- package/src/stories/ConditionInput.stories.tsx +502 -502
- package/src/stories/ConditionItem.stories.tsx +163 -163
- package/src/stories/ConditonQuantifierSelect.stories.tsx +54 -54
- package/src/stories/ConditonTriggerDescription.stories.tsx +61 -61
- package/src/stories/ConfirmDialog.stories.tsx +123 -123
- package/src/stories/DataTable.stories.tsx +191 -191
- package/src/stories/DatePicker.stories.tsx +174 -174
- package/src/stories/ExecLogFilter.stories.tsx +145 -145
- package/src/stories/FakeBotMsg.stories.tsx +36 -36
- package/src/stories/FieldEditPreferenceSelect.stories.tsx +161 -161
- package/src/stories/FieldSelect.stories.tsx +228 -228
- package/src/stories/FieldValueInput.stories.tsx +442 -442
- package/src/stories/FlowNodeActionTriggerSelect.stories.tsx +80 -80
- package/src/stories/Form.stories.tsx +302 -302
- package/src/stories/FormActionTriggerSelect.stories.tsx +80 -80
- package/src/stories/FormDivider.stories.tsx +31 -31
- package/src/stories/FormInfoEditor.stories.tsx +173 -173
- package/src/stories/FormSelect.stories.tsx +222 -222
- package/src/stories/FormulaTag.stories.tsx +68 -68
- package/src/stories/GenerateAnimation.stories.ts +20 -20
- package/src/stories/IconBtn.stories.tsx +32 -32
- package/src/stories/IconCard.stories.tsx +35 -35
- package/src/stories/IconLib.tsx +45 -45
- package/src/stories/Icons.stories.ts +22 -22
- package/src/stories/ImgMsgPart.stories.tsx +42 -42
- package/src/stories/Input.stories.tsx +102 -102
- package/src/stories/Label.stories.tsx +66 -66
- package/src/stories/LegacySlot.stories.tsx +56 -56
- package/src/stories/LineChecker.stories.tsx +133 -133
- package/src/stories/LinearGradientBorderBtn.stories.tsx +86 -86
- package/src/stories/LinearGradientColorBgAnimation.stories.tsx +60 -60
- package/src/stories/MemoizedMarkdown.stories.tsx +102 -102
- package/src/stories/MessageContent.stories.tsx +36 -36
- package/src/stories/MonthdaysSelect.stories.tsx +78 -78
- package/src/stories/NumbersMsgPart.stories.tsx +42 -42
- package/src/stories/OfficeContainer.stories.tsx +287 -287
- package/src/stories/OffsetPreferenceSelect.stories.tsx +118 -118
- package/src/stories/OffsetTypeSelect.stories.tsx +75 -75
- package/src/stories/OffsetUnitSelect.stories.tsx +61 -61
- package/src/stories/OptionItem.stories.tsx +77 -77
- package/src/stories/OutputConfigEditor.stories.tsx +96 -96
- package/src/stories/PlaceholderCard.stories.tsx +35 -35
- package/src/stories/PreviewerHeader.stories.tsx +91 -91
- package/src/stories/PrimaryConfirmBtn.stories.tsx +60 -60
- package/src/stories/PrimaryEntryBtn.stories.tsx +74 -74
- package/src/stories/PromptInput.stories.tsx +228 -228
- package/src/stories/RadioCard.stories.tsx +103 -103
- package/src/stories/RelationalOperatorSelect.stories.tsx +61 -61
- package/src/stories/RepeatByTypeSelect.stories.tsx +58 -58
- package/src/stories/RepeatPreferenceSelect.stories.tsx +107 -107
- package/src/stories/RepeatPreferenceTypeSelect.stories.tsx +58 -58
- package/src/stories/ScrollToBottom.stories.tsx +24 -24
- package/src/stories/SearchInput.stories.tsx +50 -50
- package/src/stories/Select.stories.tsx +128 -128
- package/src/stories/SplitLine.stories.tsx +40 -40
- package/src/stories/SquareChecker.stories.tsx +96 -96
- package/src/stories/TabRadioGroup.stories.tsx +84 -84
- package/src/stories/Textarea.stories.tsx +67 -67
- package/src/stories/TriggerSelect.stories.tsx +48 -48
- package/src/stories/TriggerTimePreferenceSelect.stories.tsx +106 -106
- package/src/stories/TriggerTimeSourceTypeSelect.stories.tsx +68 -68
- package/src/stories/UserTextMsgBubble.stories.ts +24 -24
- package/src/stories/WarningMessage.stories.tsx +23 -23
- package/src/stories/WeekdaysSelect.stories.tsx +66 -66
- package/src/stories/assets/demo.css +539 -539
- package/src/stories/assets/demo_index.html +7988 -7988
- package/src/stories/assets/iconfont.css +1377 -1377
- package/src/stories/assets/iconfont.json +2382 -2382
- package/src/stories/assets/iconfont.svg +697 -697
- package/src/stories/collapsible.stories.tsx +47 -47
- package/src/stories/drawer.stories.tsx +196 -196
- package/src/stories/field-checker-list-msg.stories.tsx +95 -95
- package/src/stories/field-checker.stories.tsx +117 -117
- package/src/stories/field-icon.stories.tsx +70 -70
- package/src/stories/field-value-checker.stories.tsx +113 -113
- package/src/stories/fields-design-info-table.stories.tsx +203 -203
- package/src/stories/fields-generating.stories.tsx +41 -41
- package/src/stories/fields-previewer.stories.tsx +77 -77
- package/src/stories/subform-fields-value-checker.stories.tsx +98 -98
- package/src/style.css +226 -226
- package/src/types.ts +8 -8
- package/style.css +3652 -3652
- package/tsconfig.json +13 -13
- package/vite.config.ts +44 -44
|
@@ -1,296 +1,296 @@
|
|
|
1
|
-
import { ComponentType } from 'react';
|
|
2
|
-
import { getIconUnicode } from '@baishuyun/ai-feat-utils';
|
|
3
|
-
import { cn } from '../lib/utils';
|
|
4
|
-
import { FontIcon } from './font-icon';
|
|
5
|
-
import { ImgMsgPart } from './img-part';
|
|
6
|
-
import { XlsxIcon, CsvFileIcon, TxtFileIcon, UnknownFileIcon } from './bs-icons';
|
|
7
|
-
|
|
8
|
-
/** File part from AI message */
|
|
9
|
-
export interface FilePart {
|
|
10
|
-
type: 'file';
|
|
11
|
-
url: string;
|
|
12
|
-
name?: string;
|
|
13
|
-
mediaType?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface AttachmentPartProps {
|
|
17
|
-
part: FilePart;
|
|
18
|
-
className?: string;
|
|
19
|
-
/** Size in pixels (width & height), defaults to 102 */
|
|
20
|
-
size?: number;
|
|
21
|
-
/** Show only the icon without file name */
|
|
22
|
-
iconOnly?: boolean;
|
|
23
|
-
/** Click handler for the attachment */
|
|
24
|
-
onClick?: (part: FilePart) => void;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
type FileIconComponent = ComponentType<{ color?: string }>;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 字体图标方案:文件类型图标使用 baishu 主字体的 icon-bsy-* 字形
|
|
31
|
-
* (码位见 ai-feat-utils/iconfont.ts)。注意宿主的 .icon-bsy-form 类自带
|
|
32
|
-
* color 规则(PC 为 #0265ff),继承色会被覆盖,因此颜色必须通过
|
|
33
|
-
* FontIcon 的 color prop 以内联样式落到 <i> 上
|
|
34
|
-
*/
|
|
35
|
-
const createFontFileIcon = (iconName: string): FileIconComponent => {
|
|
36
|
-
const FontFileIcon = ({ color }: { color?: string }) => (
|
|
37
|
-
<FontIcon
|
|
38
|
-
fontCls="icon-bsy-form"
|
|
39
|
-
code={getIconUnicode(iconName)}
|
|
40
|
-
color={color}
|
|
41
|
-
style={{ fontSize: 16, lineHeight: 1 }}
|
|
42
|
-
/>
|
|
43
|
-
);
|
|
44
|
-
FontFileIcon.displayName = `FontFileIcon(${iconName})`;
|
|
45
|
-
return FontFileIcon;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const DocFileIcon = createFontFileIcon('bsy-doc');
|
|
49
|
-
const DocxFileIcon = createFontFileIcon('bsy-docx');
|
|
50
|
-
const PptFileIcon = createFontFileIcon('bsy-ppt');
|
|
51
|
-
const PptxFileIcon = createFontFileIcon('bsy-pptx');
|
|
52
|
-
const PdfFileIcon = createFontFileIcon('bsy-pdf');
|
|
53
|
-
const ZipFileIcon = createFontFileIcon('bsy-zip');
|
|
54
|
-
const RarFileIcon = createFontFileIcon('bsy-rar');
|
|
55
|
-
const Mp4FileIcon = createFontFileIcon('bsy-mp4');
|
|
56
|
-
const WebmFileIcon = createFontFileIcon('bsy-webm');
|
|
57
|
-
const MovFileIcon = createFontFileIcon('bsy-mov');
|
|
58
|
-
const AudioFileIcon = createFontFileIcon('bsy-ogg');
|
|
59
|
-
|
|
60
|
-
export interface FileTypeConfig {
|
|
61
|
-
icon: FileIconComponent;
|
|
62
|
-
/** Icon original size for scaling */
|
|
63
|
-
iconSize: number;
|
|
64
|
-
/** Icon color (对齐 baishu BsyFileUpload 的 icon-bsy-* 配色),仅对字体图标生效 */
|
|
65
|
-
color?: string;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Map media types to icon configurations */
|
|
69
|
-
const FILE_TYPE_MAP: Record<string, FileTypeConfig> = {
|
|
70
|
-
// Excel files
|
|
71
|
-
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {
|
|
72
|
-
icon: XlsxIcon,
|
|
73
|
-
iconSize: 52,
|
|
74
|
-
},
|
|
75
|
-
'application/vnd.ms-excel': { icon: XlsxIcon, iconSize: 52 },
|
|
76
|
-
|
|
77
|
-
// CSV files
|
|
78
|
-
'text/csv': { icon: CsvFileIcon, iconSize: 16 },
|
|
79
|
-
|
|
80
|
-
// Text files
|
|
81
|
-
'text/plain': { icon: TxtFileIcon, iconSize: 16 },
|
|
82
|
-
'application/octet-stream': { icon: TxtFileIcon, iconSize: 16 }, // treat as text for ts/js files
|
|
83
|
-
|
|
84
|
-
// Word files
|
|
85
|
-
'application/msword': { icon: DocFileIcon, iconSize: 16, color: '#096dd9' },
|
|
86
|
-
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': {
|
|
87
|
-
icon: DocxFileIcon,
|
|
88
|
-
iconSize: 16,
|
|
89
|
-
color: '#096dd9',
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
// PowerPoint files
|
|
93
|
-
'application/vnd.ms-powerpoint': { icon: PptFileIcon, iconSize: 16, color: '#d46b08' },
|
|
94
|
-
'application/vnd.openxmlformats-officedocument.presentationml.presentation': {
|
|
95
|
-
icon: PptxFileIcon,
|
|
96
|
-
iconSize: 16,
|
|
97
|
-
color: '#08979c',
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
// PDF files
|
|
101
|
-
'application/pdf': { icon: PdfFileIcon, iconSize: 16, color: '#d4380d' },
|
|
102
|
-
|
|
103
|
-
// Archive files
|
|
104
|
-
'application/zip': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
|
|
105
|
-
'application/x-zip-compressed': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
|
|
106
|
-
'application/x-rar-compressed': { icon: RarFileIcon, iconSize: 16, color: '#389e0d' },
|
|
107
|
-
'application/vnd.rar': { icon: RarFileIcon, iconSize: 16, color: '#389e0d' },
|
|
108
|
-
'application/x-7z-compressed': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
|
|
109
|
-
'application/gzip': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
|
|
110
|
-
|
|
111
|
-
// Video files
|
|
112
|
-
'video/mp4': { icon: Mp4FileIcon, iconSize: 16, color: '#1d39c4' },
|
|
113
|
-
'video/webm': { icon: WebmFileIcon, iconSize: 16, color: '#d46b08' },
|
|
114
|
-
'video/quicktime': { icon: MovFileIcon, iconSize: 16, color: '#1d39c4' }, // mov
|
|
115
|
-
|
|
116
|
-
// Audio files
|
|
117
|
-
'audio/ogg': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
|
|
118
|
-
'audio/mpeg': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' }, // mp3
|
|
119
|
-
'audio/wav': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
|
|
120
|
-
'audio/x-wav': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const DEFAULT_FILE_CONFIG: FileTypeConfig = {
|
|
124
|
-
icon: UnknownFileIcon,
|
|
125
|
-
iconSize: 16,
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/** Check if media type is an image */
|
|
129
|
-
export const isImageType = (mediaType?: string): boolean => {
|
|
130
|
-
return Boolean(mediaType?.startsWith('image/'));
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
/** Get file config based on media type and file extension */
|
|
134
|
-
export const getFileConfig = (mediaType?: string, fileName?: string): FileTypeConfig => {
|
|
135
|
-
// Check media type first
|
|
136
|
-
if (mediaType && FILE_TYPE_MAP[mediaType]) {
|
|
137
|
-
return FILE_TYPE_MAP[mediaType];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Prefix match for video/audio (covers mkv/avi/flac/m4a etc.)
|
|
141
|
-
if (mediaType?.startsWith('video/')) {
|
|
142
|
-
return FILE_TYPE_MAP['video/mp4'];
|
|
143
|
-
}
|
|
144
|
-
if (mediaType?.startsWith('audio/')) {
|
|
145
|
-
return FILE_TYPE_MAP['audio/ogg'];
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Fallback to file extension
|
|
149
|
-
if (fileName) {
|
|
150
|
-
const ext = fileName.split('.').pop()?.toLowerCase();
|
|
151
|
-
switch (ext) {
|
|
152
|
-
case 'xlsx':
|
|
153
|
-
case 'xls':
|
|
154
|
-
return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
|
|
155
|
-
case 'csv':
|
|
156
|
-
return FILE_TYPE_MAP['text/csv'];
|
|
157
|
-
case 'txt':
|
|
158
|
-
case 'ts':
|
|
159
|
-
case 'tsx':
|
|
160
|
-
case 'js':
|
|
161
|
-
case 'jsx':
|
|
162
|
-
case 'json':
|
|
163
|
-
case 'md':
|
|
164
|
-
return FILE_TYPE_MAP['text/plain'];
|
|
165
|
-
case 'doc':
|
|
166
|
-
return FILE_TYPE_MAP['application/msword'];
|
|
167
|
-
case 'docx':
|
|
168
|
-
return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
|
|
169
|
-
case 'ppt':
|
|
170
|
-
return FILE_TYPE_MAP['application/vnd.ms-powerpoint'];
|
|
171
|
-
case 'pptx':
|
|
172
|
-
return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.presentationml.presentation'];
|
|
173
|
-
case 'pdf':
|
|
174
|
-
return FILE_TYPE_MAP['application/pdf'];
|
|
175
|
-
case 'zip':
|
|
176
|
-
case '7z':
|
|
177
|
-
case 'gz':
|
|
178
|
-
case 'tar':
|
|
179
|
-
return FILE_TYPE_MAP['application/zip'];
|
|
180
|
-
case 'rar':
|
|
181
|
-
return FILE_TYPE_MAP['application/x-rar-compressed'];
|
|
182
|
-
case 'mp4':
|
|
183
|
-
case 'mkv':
|
|
184
|
-
case 'avi':
|
|
185
|
-
return FILE_TYPE_MAP['video/mp4'];
|
|
186
|
-
case 'webm':
|
|
187
|
-
return FILE_TYPE_MAP['video/webm'];
|
|
188
|
-
case 'mov':
|
|
189
|
-
return FILE_TYPE_MAP['video/quicktime'];
|
|
190
|
-
case 'ogg':
|
|
191
|
-
case 'mp3':
|
|
192
|
-
case 'wav':
|
|
193
|
-
case 'flac':
|
|
194
|
-
case 'aac':
|
|
195
|
-
case 'm4a':
|
|
196
|
-
return FILE_TYPE_MAP['audio/ogg'];
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return DEFAULT_FILE_CONFIG;
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
/** Extract display file name from full path */
|
|
204
|
-
const getDisplayFileName = (name?: string): string | undefined => {
|
|
205
|
-
if (!name) return undefined;
|
|
206
|
-
// Get last segment of path
|
|
207
|
-
const segments = name.split('/');
|
|
208
|
-
return segments[segments.length - 1];
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
/** Generic file message part component */
|
|
212
|
-
const FileMsgPart = ({
|
|
213
|
-
fileName,
|
|
214
|
-
icon: Icon,
|
|
215
|
-
iconSize,
|
|
216
|
-
color,
|
|
217
|
-
className,
|
|
218
|
-
size = 102,
|
|
219
|
-
}: {
|
|
220
|
-
fileName?: string;
|
|
221
|
-
icon: FileIconComponent;
|
|
222
|
-
iconSize: number;
|
|
223
|
-
color?: string;
|
|
224
|
-
className?: string;
|
|
225
|
-
size?: number;
|
|
226
|
-
}) => {
|
|
227
|
-
const scale = size / 102;
|
|
228
|
-
const scaledIconSize = (52 / 102) * size; // Target icon takes ~51% of container
|
|
229
|
-
const iconScale = scaledIconSize / iconSize;
|
|
230
|
-
const hasFileName = Boolean(fileName);
|
|
231
|
-
|
|
232
|
-
return (
|
|
233
|
-
<div
|
|
234
|
-
className={cn(
|
|
235
|
-
'relative flex flex-col items-center justify-center rounded-[10px] bg-[#EFF0F6] px-[10px]',
|
|
236
|
-
hasFileName && 'gap-[6px]',
|
|
237
|
-
className
|
|
238
|
-
)}
|
|
239
|
-
style={{ width: size, height: size }}
|
|
240
|
-
>
|
|
241
|
-
<div
|
|
242
|
-
className="shrink-0 flex items-center justify-center"
|
|
243
|
-
style={{
|
|
244
|
-
width: scaledIconSize,
|
|
245
|
-
height: scaledIconSize,
|
|
246
|
-
color,
|
|
247
|
-
}}
|
|
248
|
-
>
|
|
249
|
-
<div
|
|
250
|
-
style={{
|
|
251
|
-
transform: iconScale !== 1 ? `scale(${iconScale})` : undefined,
|
|
252
|
-
transformOrigin: 'center',
|
|
253
|
-
}}
|
|
254
|
-
>
|
|
255
|
-
<Icon color={color} />
|
|
256
|
-
</div>
|
|
257
|
-
</div>
|
|
258
|
-
{hasFileName && (
|
|
259
|
-
<p
|
|
260
|
-
className="block max-w-full shrink-0 truncate text-center text-[14px] leading-normal text-[#030303]"
|
|
261
|
-
style={{ fontSize: 14 * scale, maxWidth: '100%', display: 'block' }}
|
|
262
|
-
title={fileName}
|
|
263
|
-
>
|
|
264
|
-
{fileName}
|
|
265
|
-
</p>
|
|
266
|
-
)}
|
|
267
|
-
</div>
|
|
268
|
-
);
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
export const AttachmentPart = ({ part, className, size = 102, iconOnly, onClick }: AttachmentPartProps) => {
|
|
272
|
-
const { url, name, mediaType } = part;
|
|
273
|
-
const handleClick = onClick ? () => onClick(part) : undefined;
|
|
274
|
-
|
|
275
|
-
// Render image for image types
|
|
276
|
-
if (isImageType(mediaType)) {
|
|
277
|
-
return <ImgMsgPart url={url} alt={name} className={className} size={size} onClick={handleClick} />;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Render file icon for other types
|
|
281
|
-
const { icon, iconSize, color } = getFileConfig(mediaType, name);
|
|
282
|
-
const displayName = iconOnly ? undefined : getDisplayFileName(name);
|
|
283
|
-
|
|
284
|
-
return (
|
|
285
|
-
<div onClick={handleClick} className={onClick ? 'cursor-pointer' : undefined}>
|
|
286
|
-
<FileMsgPart
|
|
287
|
-
fileName={displayName}
|
|
288
|
-
icon={icon}
|
|
289
|
-
iconSize={iconSize}
|
|
290
|
-
color={color}
|
|
291
|
-
className={className}
|
|
292
|
-
size={size}
|
|
293
|
-
/>
|
|
294
|
-
</div>
|
|
295
|
-
);
|
|
296
|
-
};
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
import { getIconUnicode } from '@baishuyun/ai-feat-utils';
|
|
3
|
+
import { cn } from '../lib/utils';
|
|
4
|
+
import { FontIcon } from './font-icon';
|
|
5
|
+
import { ImgMsgPart } from './img-part';
|
|
6
|
+
import { XlsxIcon, CsvFileIcon, TxtFileIcon, UnknownFileIcon } from './bs-icons';
|
|
7
|
+
|
|
8
|
+
/** File part from AI message */
|
|
9
|
+
export interface FilePart {
|
|
10
|
+
type: 'file';
|
|
11
|
+
url: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
mediaType?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AttachmentPartProps {
|
|
17
|
+
part: FilePart;
|
|
18
|
+
className?: string;
|
|
19
|
+
/** Size in pixels (width & height), defaults to 102 */
|
|
20
|
+
size?: number;
|
|
21
|
+
/** Show only the icon without file name */
|
|
22
|
+
iconOnly?: boolean;
|
|
23
|
+
/** Click handler for the attachment */
|
|
24
|
+
onClick?: (part: FilePart) => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type FileIconComponent = ComponentType<{ color?: string }>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 字体图标方案:文件类型图标使用 baishu 主字体的 icon-bsy-* 字形
|
|
31
|
+
* (码位见 ai-feat-utils/iconfont.ts)。注意宿主的 .icon-bsy-form 类自带
|
|
32
|
+
* color 规则(PC 为 #0265ff),继承色会被覆盖,因此颜色必须通过
|
|
33
|
+
* FontIcon 的 color prop 以内联样式落到 <i> 上
|
|
34
|
+
*/
|
|
35
|
+
const createFontFileIcon = (iconName: string): FileIconComponent => {
|
|
36
|
+
const FontFileIcon = ({ color }: { color?: string }) => (
|
|
37
|
+
<FontIcon
|
|
38
|
+
fontCls="icon-bsy-form"
|
|
39
|
+
code={getIconUnicode(iconName)}
|
|
40
|
+
color={color}
|
|
41
|
+
style={{ fontSize: 16, lineHeight: 1 }}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
FontFileIcon.displayName = `FontFileIcon(${iconName})`;
|
|
45
|
+
return FontFileIcon;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const DocFileIcon = createFontFileIcon('bsy-doc');
|
|
49
|
+
const DocxFileIcon = createFontFileIcon('bsy-docx');
|
|
50
|
+
const PptFileIcon = createFontFileIcon('bsy-ppt');
|
|
51
|
+
const PptxFileIcon = createFontFileIcon('bsy-pptx');
|
|
52
|
+
const PdfFileIcon = createFontFileIcon('bsy-pdf');
|
|
53
|
+
const ZipFileIcon = createFontFileIcon('bsy-zip');
|
|
54
|
+
const RarFileIcon = createFontFileIcon('bsy-rar');
|
|
55
|
+
const Mp4FileIcon = createFontFileIcon('bsy-mp4');
|
|
56
|
+
const WebmFileIcon = createFontFileIcon('bsy-webm');
|
|
57
|
+
const MovFileIcon = createFontFileIcon('bsy-mov');
|
|
58
|
+
const AudioFileIcon = createFontFileIcon('bsy-ogg');
|
|
59
|
+
|
|
60
|
+
export interface FileTypeConfig {
|
|
61
|
+
icon: FileIconComponent;
|
|
62
|
+
/** Icon original size for scaling */
|
|
63
|
+
iconSize: number;
|
|
64
|
+
/** Icon color (对齐 baishu BsyFileUpload 的 icon-bsy-* 配色),仅对字体图标生效 */
|
|
65
|
+
color?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Map media types to icon configurations */
|
|
69
|
+
const FILE_TYPE_MAP: Record<string, FileTypeConfig> = {
|
|
70
|
+
// Excel files
|
|
71
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': {
|
|
72
|
+
icon: XlsxIcon,
|
|
73
|
+
iconSize: 52,
|
|
74
|
+
},
|
|
75
|
+
'application/vnd.ms-excel': { icon: XlsxIcon, iconSize: 52 },
|
|
76
|
+
|
|
77
|
+
// CSV files
|
|
78
|
+
'text/csv': { icon: CsvFileIcon, iconSize: 16 },
|
|
79
|
+
|
|
80
|
+
// Text files
|
|
81
|
+
'text/plain': { icon: TxtFileIcon, iconSize: 16 },
|
|
82
|
+
'application/octet-stream': { icon: TxtFileIcon, iconSize: 16 }, // treat as text for ts/js files
|
|
83
|
+
|
|
84
|
+
// Word files
|
|
85
|
+
'application/msword': { icon: DocFileIcon, iconSize: 16, color: '#096dd9' },
|
|
86
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': {
|
|
87
|
+
icon: DocxFileIcon,
|
|
88
|
+
iconSize: 16,
|
|
89
|
+
color: '#096dd9',
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// PowerPoint files
|
|
93
|
+
'application/vnd.ms-powerpoint': { icon: PptFileIcon, iconSize: 16, color: '#d46b08' },
|
|
94
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation': {
|
|
95
|
+
icon: PptxFileIcon,
|
|
96
|
+
iconSize: 16,
|
|
97
|
+
color: '#08979c',
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// PDF files
|
|
101
|
+
'application/pdf': { icon: PdfFileIcon, iconSize: 16, color: '#d4380d' },
|
|
102
|
+
|
|
103
|
+
// Archive files
|
|
104
|
+
'application/zip': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
|
|
105
|
+
'application/x-zip-compressed': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
|
|
106
|
+
'application/x-rar-compressed': { icon: RarFileIcon, iconSize: 16, color: '#389e0d' },
|
|
107
|
+
'application/vnd.rar': { icon: RarFileIcon, iconSize: 16, color: '#389e0d' },
|
|
108
|
+
'application/x-7z-compressed': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
|
|
109
|
+
'application/gzip': { icon: ZipFileIcon, iconSize: 16, color: '#d46b08' },
|
|
110
|
+
|
|
111
|
+
// Video files
|
|
112
|
+
'video/mp4': { icon: Mp4FileIcon, iconSize: 16, color: '#1d39c4' },
|
|
113
|
+
'video/webm': { icon: WebmFileIcon, iconSize: 16, color: '#d46b08' },
|
|
114
|
+
'video/quicktime': { icon: MovFileIcon, iconSize: 16, color: '#1d39c4' }, // mov
|
|
115
|
+
|
|
116
|
+
// Audio files
|
|
117
|
+
'audio/ogg': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
|
|
118
|
+
'audio/mpeg': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' }, // mp3
|
|
119
|
+
'audio/wav': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
|
|
120
|
+
'audio/x-wav': { icon: AudioFileIcon, iconSize: 16, color: '#d4380d' },
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const DEFAULT_FILE_CONFIG: FileTypeConfig = {
|
|
124
|
+
icon: UnknownFileIcon,
|
|
125
|
+
iconSize: 16,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/** Check if media type is an image */
|
|
129
|
+
export const isImageType = (mediaType?: string): boolean => {
|
|
130
|
+
return Boolean(mediaType?.startsWith('image/'));
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/** Get file config based on media type and file extension */
|
|
134
|
+
export const getFileConfig = (mediaType?: string, fileName?: string): FileTypeConfig => {
|
|
135
|
+
// Check media type first
|
|
136
|
+
if (mediaType && FILE_TYPE_MAP[mediaType]) {
|
|
137
|
+
return FILE_TYPE_MAP[mediaType];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Prefix match for video/audio (covers mkv/avi/flac/m4a etc.)
|
|
141
|
+
if (mediaType?.startsWith('video/')) {
|
|
142
|
+
return FILE_TYPE_MAP['video/mp4'];
|
|
143
|
+
}
|
|
144
|
+
if (mediaType?.startsWith('audio/')) {
|
|
145
|
+
return FILE_TYPE_MAP['audio/ogg'];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Fallback to file extension
|
|
149
|
+
if (fileName) {
|
|
150
|
+
const ext = fileName.split('.').pop()?.toLowerCase();
|
|
151
|
+
switch (ext) {
|
|
152
|
+
case 'xlsx':
|
|
153
|
+
case 'xls':
|
|
154
|
+
return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
|
|
155
|
+
case 'csv':
|
|
156
|
+
return FILE_TYPE_MAP['text/csv'];
|
|
157
|
+
case 'txt':
|
|
158
|
+
case 'ts':
|
|
159
|
+
case 'tsx':
|
|
160
|
+
case 'js':
|
|
161
|
+
case 'jsx':
|
|
162
|
+
case 'json':
|
|
163
|
+
case 'md':
|
|
164
|
+
return FILE_TYPE_MAP['text/plain'];
|
|
165
|
+
case 'doc':
|
|
166
|
+
return FILE_TYPE_MAP['application/msword'];
|
|
167
|
+
case 'docx':
|
|
168
|
+
return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
|
|
169
|
+
case 'ppt':
|
|
170
|
+
return FILE_TYPE_MAP['application/vnd.ms-powerpoint'];
|
|
171
|
+
case 'pptx':
|
|
172
|
+
return FILE_TYPE_MAP['application/vnd.openxmlformats-officedocument.presentationml.presentation'];
|
|
173
|
+
case 'pdf':
|
|
174
|
+
return FILE_TYPE_MAP['application/pdf'];
|
|
175
|
+
case 'zip':
|
|
176
|
+
case '7z':
|
|
177
|
+
case 'gz':
|
|
178
|
+
case 'tar':
|
|
179
|
+
return FILE_TYPE_MAP['application/zip'];
|
|
180
|
+
case 'rar':
|
|
181
|
+
return FILE_TYPE_MAP['application/x-rar-compressed'];
|
|
182
|
+
case 'mp4':
|
|
183
|
+
case 'mkv':
|
|
184
|
+
case 'avi':
|
|
185
|
+
return FILE_TYPE_MAP['video/mp4'];
|
|
186
|
+
case 'webm':
|
|
187
|
+
return FILE_TYPE_MAP['video/webm'];
|
|
188
|
+
case 'mov':
|
|
189
|
+
return FILE_TYPE_MAP['video/quicktime'];
|
|
190
|
+
case 'ogg':
|
|
191
|
+
case 'mp3':
|
|
192
|
+
case 'wav':
|
|
193
|
+
case 'flac':
|
|
194
|
+
case 'aac':
|
|
195
|
+
case 'm4a':
|
|
196
|
+
return FILE_TYPE_MAP['audio/ogg'];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return DEFAULT_FILE_CONFIG;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/** Extract display file name from full path */
|
|
204
|
+
const getDisplayFileName = (name?: string): string | undefined => {
|
|
205
|
+
if (!name) return undefined;
|
|
206
|
+
// Get last segment of path
|
|
207
|
+
const segments = name.split('/');
|
|
208
|
+
return segments[segments.length - 1];
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/** Generic file message part component */
|
|
212
|
+
const FileMsgPart = ({
|
|
213
|
+
fileName,
|
|
214
|
+
icon: Icon,
|
|
215
|
+
iconSize,
|
|
216
|
+
color,
|
|
217
|
+
className,
|
|
218
|
+
size = 102,
|
|
219
|
+
}: {
|
|
220
|
+
fileName?: string;
|
|
221
|
+
icon: FileIconComponent;
|
|
222
|
+
iconSize: number;
|
|
223
|
+
color?: string;
|
|
224
|
+
className?: string;
|
|
225
|
+
size?: number;
|
|
226
|
+
}) => {
|
|
227
|
+
const scale = size / 102;
|
|
228
|
+
const scaledIconSize = (52 / 102) * size; // Target icon takes ~51% of container
|
|
229
|
+
const iconScale = scaledIconSize / iconSize;
|
|
230
|
+
const hasFileName = Boolean(fileName);
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<div
|
|
234
|
+
className={cn(
|
|
235
|
+
'relative flex flex-col items-center justify-center rounded-[10px] bg-[#EFF0F6] px-[10px]',
|
|
236
|
+
hasFileName && 'gap-[6px]',
|
|
237
|
+
className
|
|
238
|
+
)}
|
|
239
|
+
style={{ width: size, height: size }}
|
|
240
|
+
>
|
|
241
|
+
<div
|
|
242
|
+
className="shrink-0 flex items-center justify-center"
|
|
243
|
+
style={{
|
|
244
|
+
width: scaledIconSize,
|
|
245
|
+
height: scaledIconSize,
|
|
246
|
+
color,
|
|
247
|
+
}}
|
|
248
|
+
>
|
|
249
|
+
<div
|
|
250
|
+
style={{
|
|
251
|
+
transform: iconScale !== 1 ? `scale(${iconScale})` : undefined,
|
|
252
|
+
transformOrigin: 'center',
|
|
253
|
+
}}
|
|
254
|
+
>
|
|
255
|
+
<Icon color={color} />
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
{hasFileName && (
|
|
259
|
+
<p
|
|
260
|
+
className="block max-w-full shrink-0 truncate text-center text-[14px] leading-normal text-[#030303]"
|
|
261
|
+
style={{ fontSize: 14 * scale, maxWidth: '100%', display: 'block' }}
|
|
262
|
+
title={fileName}
|
|
263
|
+
>
|
|
264
|
+
{fileName}
|
|
265
|
+
</p>
|
|
266
|
+
)}
|
|
267
|
+
</div>
|
|
268
|
+
);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export const AttachmentPart = ({ part, className, size = 102, iconOnly, onClick }: AttachmentPartProps) => {
|
|
272
|
+
const { url, name, mediaType } = part;
|
|
273
|
+
const handleClick = onClick ? () => onClick(part) : undefined;
|
|
274
|
+
|
|
275
|
+
// Render image for image types
|
|
276
|
+
if (isImageType(mediaType)) {
|
|
277
|
+
return <ImgMsgPart url={url} alt={name} className={className} size={size} onClick={handleClick} />;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Render file icon for other types
|
|
281
|
+
const { icon, iconSize, color } = getFileConfig(mediaType, name);
|
|
282
|
+
const displayName = iconOnly ? undefined : getDisplayFileName(name);
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<div onClick={handleClick} className={onClick ? 'cursor-pointer' : undefined}>
|
|
286
|
+
<FileMsgPart
|
|
287
|
+
fileName={displayName}
|
|
288
|
+
icon={icon}
|
|
289
|
+
iconSize={iconSize}
|
|
290
|
+
color={color}
|
|
291
|
+
className={className}
|
|
292
|
+
size={size}
|
|
293
|
+
/>
|
|
294
|
+
</div>
|
|
295
|
+
);
|
|
296
|
+
};
|