@umodeapporg/ui 0.8.0 → 0.8.1
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.
|
@@ -15,70 +15,76 @@ const AttachmentPreviewList = ({
|
|
|
15
15
|
}) => {
|
|
16
16
|
const { t } = useTranslation("ui");
|
|
17
17
|
if (!stagedAttachments.length) return null;
|
|
18
|
-
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: stagedAttachments.map((attachment) =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: stagedAttachments.map((attachment) => {
|
|
19
|
+
const actions = isEditableImage(attachment) ? (imageActions ?? []).filter((action) => !action.hidden?.(attachment)) : [];
|
|
20
|
+
return /* @__PURE__ */ jsxs(
|
|
21
|
+
"div",
|
|
22
|
+
{
|
|
23
|
+
className: classNames(
|
|
24
|
+
"relative flex animate-pop-in flex-col overflow-hidden rounded-xl border bg-surface",
|
|
25
|
+
// The buttons sit on the same row as the remove one, so the chip grows to keep the
|
|
26
|
+
// file name readable instead of squeezing it to a couple of characters.
|
|
27
|
+
actions.length > 0 ? "w-56" : "w-44",
|
|
28
|
+
attachment.status === "error" ? "border-danger-border" : "border-border"
|
|
29
|
+
),
|
|
30
|
+
children: [
|
|
31
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2 p-2", children: [
|
|
32
|
+
attachment.previewUrl && attachment.file.type.startsWith("image/") ? /* @__PURE__ */ jsx(ControlledImage, { src: attachment.previewUrl, className: "size-10 shrink-0 rounded-lg object-cover" }) : /* @__PURE__ */ jsx("span", { className: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-brand-soft text-brand-emphasis", children: /* @__PURE__ */ jsx(MaterialSymbol, { icon: iconForMimeType(attachment.file.type), size: 20 }) }),
|
|
33
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
34
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-xs font-medium text-foreground", children: attachment.file.name }),
|
|
35
|
+
/* @__PURE__ */ jsx("span", { className: "text-xxs text-foreground-subtle", children: attachment.status === "error" ? t("followUp.attachments.uploadFailed") : formatFileSize(attachment.file.size) })
|
|
36
|
+
] }),
|
|
37
|
+
actions.map((action) => /* @__PURE__ */ jsx(ControlledTooltip, { content: action.label, className: "z-[9999]", children: /* @__PURE__ */ jsx(
|
|
38
|
+
ControlledButton,
|
|
39
|
+
{
|
|
40
|
+
defaultHtmlButton: true,
|
|
41
|
+
onClick: () => action.onClick(attachment),
|
|
42
|
+
className: "flex size-6 shrink-0 items-center justify-center rounded-full text-foreground-faint transition-colors hover:bg-muted-hover hover:text-brand-emphasis",
|
|
43
|
+
children: /* @__PURE__ */ jsx(MaterialSymbol, { icon: action.icon, size: 14 })
|
|
44
|
+
}
|
|
45
|
+
) }, action.key)),
|
|
46
|
+
attachment.status === "error" && /* @__PURE__ */ jsx(ControlledTooltip, { content: t("actions.tryAgain"), className: "z-[9999]", children: /* @__PURE__ */ jsx(
|
|
47
|
+
ControlledButton,
|
|
48
|
+
{
|
|
49
|
+
defaultHtmlButton: true,
|
|
50
|
+
onClick: () => onRetry(attachment.stagingId),
|
|
51
|
+
className: "flex size-6 shrink-0 items-center justify-center rounded-full text-danger-emphasis hover:bg-danger-soft",
|
|
52
|
+
children: /* @__PURE__ */ jsx(MaterialSymbol, { icon: "refresh", size: 14 })
|
|
53
|
+
}
|
|
54
|
+
) }),
|
|
55
|
+
/* @__PURE__ */ jsx(ControlledTooltip, { content: t("actions.remove"), className: "z-[9999]", children: /* @__PURE__ */ jsx(
|
|
56
|
+
ControlledButton,
|
|
57
|
+
{
|
|
58
|
+
defaultHtmlButton: true,
|
|
59
|
+
onClick: () => onRemove(attachment.stagingId),
|
|
60
|
+
className: "flex size-6 shrink-0 items-center justify-center rounded-full text-foreground-faint hover:bg-muted-hover hover:text-foreground-muted",
|
|
61
|
+
children: /* @__PURE__ */ jsx(MaterialSymbol, { icon: "close", size: 14 })
|
|
62
|
+
}
|
|
63
|
+
) })
|
|
31
64
|
] }),
|
|
32
|
-
attachment.status === "
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
defaultHtmlButton: true,
|
|
36
|
-
onClick: () => onRetry(attachment.stagingId),
|
|
37
|
-
className: "flex size-6 shrink-0 items-center justify-center rounded-full text-danger-emphasis hover:bg-danger-soft",
|
|
38
|
-
children: /* @__PURE__ */ jsx(MaterialSymbol, { icon: "refresh", size: 14 })
|
|
39
|
-
}
|
|
40
|
-
) }),
|
|
41
|
-
/* @__PURE__ */ jsx(ControlledTooltip, { content: t("actions.remove"), className: "z-[9999]", children: /* @__PURE__ */ jsx(
|
|
42
|
-
ControlledButton,
|
|
65
|
+
attachment.status === "uploading" && /* @__PURE__ */ jsx(
|
|
66
|
+
"div",
|
|
43
67
|
{
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
68
|
+
className: "h-1 w-full bg-muted-hover",
|
|
69
|
+
role: "progressbar",
|
|
70
|
+
"aria-valuemin": 0,
|
|
71
|
+
"aria-valuemax": 100,
|
|
72
|
+
"aria-valuenow": attachment.progress,
|
|
73
|
+
"aria-label": t("followUp.attachments.uploading"),
|
|
74
|
+
children: /* @__PURE__ */ jsx(
|
|
75
|
+
"div",
|
|
76
|
+
{
|
|
77
|
+
className: "h-1 rounded-r-full bg-brand transition-interactive duration-base",
|
|
78
|
+
style: { width: `${attachment.progress}%` }
|
|
79
|
+
}
|
|
80
|
+
)
|
|
48
81
|
}
|
|
49
|
-
)
|
|
50
|
-
]
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
onClick: () => action.onClick(attachment),
|
|
56
|
-
className: "flex size-6 shrink-0 items-center justify-center rounded-full text-foreground-faint transition-colors hover:bg-muted-hover hover:text-brand-emphasis",
|
|
57
|
-
children: /* @__PURE__ */ jsx(MaterialSymbol, { icon: action.icon, size: 14 })
|
|
58
|
-
}
|
|
59
|
-
) }, action.key)) }),
|
|
60
|
-
attachment.status === "uploading" && /* @__PURE__ */ jsx(
|
|
61
|
-
"div",
|
|
62
|
-
{
|
|
63
|
-
className: "h-1 w-full bg-muted-hover",
|
|
64
|
-
role: "progressbar",
|
|
65
|
-
"aria-valuemin": 0,
|
|
66
|
-
"aria-valuemax": 100,
|
|
67
|
-
"aria-valuenow": attachment.progress,
|
|
68
|
-
"aria-label": t("followUp.attachments.uploading"),
|
|
69
|
-
children: /* @__PURE__ */ jsx(
|
|
70
|
-
"div",
|
|
71
|
-
{
|
|
72
|
-
className: "h-1 rounded-r-full bg-brand transition-interactive duration-base",
|
|
73
|
-
style: { width: `${attachment.progress}%` }
|
|
74
|
-
}
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
)
|
|
78
|
-
]
|
|
79
|
-
},
|
|
80
|
-
attachment.stagingId
|
|
81
|
-
)) });
|
|
82
|
+
)
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
attachment.stagingId
|
|
86
|
+
);
|
|
87
|
+
}) });
|
|
82
88
|
};
|
|
83
89
|
export {
|
|
84
90
|
AttachmentPreviewList as default
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AttachmentPreviewList.js","sources":["../../../../src/components/Form/attachments/AttachmentPreviewList.tsx"],"sourcesContent":["import classNames from 'classnames';\nimport { useTranslation } from 'react-i18next';\n\nimport ControlledButton from '@/components/Form/ControlledButton';\nimport ControlledImage from '@/components/Form/ControlledImage';\nimport ControlledTooltip from '@/components/UI/ControlledTooltip';\nimport { MaterialSymbol } from '@/components/UI/MaterialSymbol';\nimport { formatFileSize, iconForMimeType } from '@/helpers/fileAttachments';\nimport { IStagedAttachment } from '@/hooks/useFileAttachments';\n\nimport { SymbolCodepoints } from '../../UI/MaterialSymbol';\n\n/**\n * An action offered on a staged IMAGE before it is sent (annotate it, run an AI adjustment). The\n * library does not know what any of them do: it draws the button and hands the staged file back.\n */\nexport interface IStagedImageAction {\n key: string;\n icon: SymbolCodepoints;\n label: string;\n onClick: (attachment: IStagedAttachment) => void;\n hidden?: (attachment: IStagedAttachment) => boolean;\n}\n\ninterface IAttachmentPreviewListProps {\n stagedAttachments: Array<IStagedAttachment>;\n onRemove: (stagingId: string) => void;\n onRetry: (stagingId: string) => void;\n /** Offered only on an image whose upload finished: editing one mid-flight would race the upload. */\n imageActions?: Array<IStagedImageAction>;\n}\n\nconst isEditableImage = (attachment: IStagedAttachment): boolean =>\n attachment.status === 'done' && Boolean(attachment.previewUrl) && attachment.file.type.startsWith('image/');\n\nconst AttachmentPreviewList: React.FC<IAttachmentPreviewListProps> = ({\n stagedAttachments,\n onRemove,\n onRetry,\n imageActions,\n}) => {\n const { t } = useTranslation('ui');\n\n if (!stagedAttachments.length) return null;\n\n return (\n <div className=\"flex flex-wrap gap-2\">\n {stagedAttachments.map((attachment) => (\n <div\n key={attachment.stagingId}\n className={classNames(\n 'relative flex
|
|
1
|
+
{"version":3,"file":"AttachmentPreviewList.js","sources":["../../../../src/components/Form/attachments/AttachmentPreviewList.tsx"],"sourcesContent":["import classNames from 'classnames';\nimport { useTranslation } from 'react-i18next';\n\nimport ControlledButton from '@/components/Form/ControlledButton';\nimport ControlledImage from '@/components/Form/ControlledImage';\nimport ControlledTooltip from '@/components/UI/ControlledTooltip';\nimport { MaterialSymbol } from '@/components/UI/MaterialSymbol';\nimport { formatFileSize, iconForMimeType } from '@/helpers/fileAttachments';\nimport { IStagedAttachment } from '@/hooks/useFileAttachments';\n\nimport { SymbolCodepoints } from '../../UI/MaterialSymbol';\n\n/**\n * An action offered on a staged IMAGE before it is sent (annotate it, run an AI adjustment). The\n * library does not know what any of them do: it draws the button and hands the staged file back.\n */\nexport interface IStagedImageAction {\n key: string;\n icon: SymbolCodepoints;\n label: string;\n onClick: (attachment: IStagedAttachment) => void;\n hidden?: (attachment: IStagedAttachment) => boolean;\n}\n\ninterface IAttachmentPreviewListProps {\n stagedAttachments: Array<IStagedAttachment>;\n onRemove: (stagingId: string) => void;\n onRetry: (stagingId: string) => void;\n /** Offered only on an image whose upload finished: editing one mid-flight would race the upload. */\n imageActions?: Array<IStagedImageAction>;\n}\n\nconst isEditableImage = (attachment: IStagedAttachment): boolean =>\n attachment.status === 'done' && Boolean(attachment.previewUrl) && attachment.file.type.startsWith('image/');\n\nconst AttachmentPreviewList: React.FC<IAttachmentPreviewListProps> = ({\n stagedAttachments,\n onRemove,\n onRetry,\n imageActions,\n}) => {\n const { t } = useTranslation('ui');\n\n if (!stagedAttachments.length) return null;\n\n return (\n <div className=\"flex flex-wrap gap-2\">\n {stagedAttachments.map((attachment) => {\n const actions = isEditableImage(attachment)\n ? (imageActions ?? []).filter((action) => !action.hidden?.(attachment))\n : [];\n\n return (\n <div\n key={attachment.stagingId}\n className={classNames(\n 'relative flex animate-pop-in flex-col overflow-hidden rounded-xl border bg-surface',\n // The buttons sit on the same row as the remove one, so the chip grows to keep the\n // file name readable instead of squeezing it to a couple of characters.\n actions.length > 0 ? 'w-56' : 'w-44',\n attachment.status === 'error' ? 'border-danger-border' : 'border-border',\n )}\n >\n <div className=\"flex items-center gap-x-2 p-2\">\n {attachment.previewUrl && attachment.file.type.startsWith('image/') ? (\n <ControlledImage src={attachment.previewUrl} className=\"size-10 shrink-0 rounded-lg object-cover\" />\n ) : (\n <span className=\"flex size-10 shrink-0 items-center justify-center rounded-lg bg-brand-soft text-brand-emphasis\">\n <MaterialSymbol icon={iconForMimeType(attachment.file.type)} size={20} />\n </span>\n )}\n\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate text-xs font-medium text-foreground\">{attachment.file.name}</span>\n <span className=\"text-xxs text-foreground-subtle\">\n {attachment.status === 'error'\n ? t('followUp.attachments.uploadFailed')\n : formatFileSize(attachment.file.size)}\n </span>\n </span>\n\n {actions.map((action) => (\n <ControlledTooltip key={action.key} content={action.label} className=\"z-[9999]\">\n <ControlledButton\n defaultHtmlButton\n onClick={() => action.onClick(attachment)}\n className=\"flex size-6 shrink-0 items-center justify-center rounded-full text-foreground-faint transition-colors hover:bg-muted-hover hover:text-brand-emphasis\"\n >\n <MaterialSymbol icon={action.icon} size={14} />\n </ControlledButton>\n </ControlledTooltip>\n ))}\n\n {attachment.status === 'error' && (\n <ControlledTooltip content={t('actions.tryAgain')} className=\"z-[9999]\">\n <ControlledButton\n defaultHtmlButton\n onClick={() => onRetry(attachment.stagingId)}\n className=\"flex size-6 shrink-0 items-center justify-center rounded-full text-danger-emphasis hover:bg-danger-soft\"\n >\n <MaterialSymbol icon=\"refresh\" size={14} />\n </ControlledButton>\n </ControlledTooltip>\n )}\n\n <ControlledTooltip content={t('actions.remove')} className=\"z-[9999]\">\n <ControlledButton\n defaultHtmlButton\n onClick={() => onRemove(attachment.stagingId)}\n className=\"flex size-6 shrink-0 items-center justify-center rounded-full text-foreground-faint hover:bg-muted-hover hover:text-foreground-muted\"\n >\n <MaterialSymbol icon=\"close\" size={14} />\n </ControlledButton>\n </ControlledTooltip>\n </div>\n\n {attachment.status === 'uploading' && (\n <div\n className=\"h-1 w-full bg-muted-hover\"\n role=\"progressbar\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={attachment.progress}\n aria-label={t('followUp.attachments.uploading')}\n >\n <div\n className=\"h-1 rounded-r-full bg-brand transition-interactive duration-base\"\n style={{ width: `${attachment.progress}%` }}\n />\n </div>\n )}\n </div>\n );\n })}\n </div>\n );\n};\n\nexport default AttachmentPreviewList;\n"],"names":[],"mappings":"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"}
|