@salesforce/webapp-template-feature-react-file-upload-experimental 1.61.2 → 1.61.3
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/dist/CHANGELOG.md +8 -0
- package/dist/force-app/main/default/webapplications/feature-react-file-upload/package-lock.json +18 -18
- package/dist/package.json +1 -1
- package/package.json +5 -14
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/api/fileUpload.ts +0 -146
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/appLayout.tsx +0 -9
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/assets/icon-image-close.svg +0 -3
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/assets/image.svg +0 -3
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/assets/success.svg +0 -3
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/assets/symbols.svg +0 -1
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUpload.tsx +0 -100
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDialog.tsx +0 -79
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadDropZone.tsx +0 -90
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadFileItem.tsx +0 -99
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/FileUploadIcons.tsx +0 -90
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/__inherit__button.tsx +0 -39
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/components/ui/__inherit__dialog.tsx +0 -102
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUpload.ts +0 -312
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/hooks/useFileUploadDialog.ts +0 -70
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/index.ts +0 -74
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/pages/UploadTest.tsx +0 -56
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/routes.tsx +0 -22
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/types/fileUpload.ts +0 -28
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/utils/fileUploadUtils.ts +0 -54
- package/src/force-app/main/default/webapplications/feature-react-file-upload/src/utils/labels.ts +0 -23
- package/src/force-app/main/default/webapplications/feature-react-file-upload/vite.config.ts +0 -44
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { LABELS } from "../utils/labels";
|
|
2
|
-
|
|
3
|
-
import { ImageIcon } from "./FileUploadIcons";
|
|
4
|
-
|
|
5
|
-
export interface FileUploadDropZoneProps {
|
|
6
|
-
/** Props for the hidden file input (ref, type, accept, multiple, onChange) */
|
|
7
|
-
inputProps: {
|
|
8
|
-
ref: React.RefObject<HTMLInputElement | null>;
|
|
9
|
-
type: "file";
|
|
10
|
-
accept?: string;
|
|
11
|
-
multiple: boolean;
|
|
12
|
-
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
13
|
-
};
|
|
14
|
-
/** Props for the drop zone (onClick, onDragOver, onDragLeave, onDrop, onKeyDown) */
|
|
15
|
-
dropZoneProps: {
|
|
16
|
-
onClick: () => void;
|
|
17
|
-
onDragOver: (e: React.DragEvent) => void;
|
|
18
|
-
onDragLeave: (e: React.DragEvent) => void;
|
|
19
|
-
onDrop: (e: React.DragEvent) => void;
|
|
20
|
-
onKeyDown: (e: React.KeyboardEvent) => void;
|
|
21
|
-
};
|
|
22
|
-
/** Whether the user is currently dragging over the drop zone */
|
|
23
|
-
isDragging: boolean;
|
|
24
|
-
/** Optional format hint (e.g. "JPEG, PNG, PDF, and MP4 formats, up to 50MB"). Defaults to LABELS.formatHint. */
|
|
25
|
-
formatHint?: string;
|
|
26
|
-
/** Optional CSS class for the drop zone (e.g. "h-full" for flex layouts) */
|
|
27
|
-
className?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const DROP_ZONE_BASE_CLASSES =
|
|
31
|
-
"mb-2 flex cursor-pointer flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed bg-white px-6 pt-4 pb-8 transition-colors";
|
|
32
|
-
const DROP_ZONE_DRAGGING_CLASSES = "border-blue-500 bg-blue-50";
|
|
33
|
-
const DROP_ZONE_IDLE_CLASSES = "border-gray-300 hover:border-gray-400 hover:bg-gray-50";
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Drop zone for file selection. Renders a dashed border area with document icon,
|
|
37
|
-
* primary instruction text, and format hint. Accepts click and drag-and-drop.
|
|
38
|
-
* Uses a hidden file input; props come from useFileUpload.
|
|
39
|
-
*/
|
|
40
|
-
export function FileUploadDropZone({
|
|
41
|
-
inputProps,
|
|
42
|
-
dropZoneProps,
|
|
43
|
-
isDragging,
|
|
44
|
-
formatHint = LABELS.formatHint,
|
|
45
|
-
className = "",
|
|
46
|
-
}: FileUploadDropZoneProps) {
|
|
47
|
-
const dropZoneClassName = [
|
|
48
|
-
DROP_ZONE_BASE_CLASSES,
|
|
49
|
-
isDragging ? DROP_ZONE_DRAGGING_CLASSES : DROP_ZONE_IDLE_CLASSES,
|
|
50
|
-
className,
|
|
51
|
-
]
|
|
52
|
-
.filter(Boolean)
|
|
53
|
-
.join(" ");
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<div
|
|
57
|
-
role="button"
|
|
58
|
-
tabIndex={0}
|
|
59
|
-
onClick={dropZoneProps.onClick}
|
|
60
|
-
onDragOver={dropZoneProps.onDragOver}
|
|
61
|
-
onDragLeave={dropZoneProps.onDragLeave}
|
|
62
|
-
onDrop={dropZoneProps.onDrop}
|
|
63
|
-
onKeyDown={dropZoneProps.onKeyDown}
|
|
64
|
-
className={dropZoneClassName}
|
|
65
|
-
aria-label={LABELS.dropZone}
|
|
66
|
-
data-testid="file-upload-drop-zone"
|
|
67
|
-
>
|
|
68
|
-
<input
|
|
69
|
-
id="file-upload-input-id"
|
|
70
|
-
ref={inputProps.ref}
|
|
71
|
-
type="file"
|
|
72
|
-
accept={inputProps.accept}
|
|
73
|
-
multiple={inputProps.multiple}
|
|
74
|
-
onChange={inputProps.onChange}
|
|
75
|
-
className="sr-only"
|
|
76
|
-
aria-hidden
|
|
77
|
-
/>
|
|
78
|
-
<span
|
|
79
|
-
className="inline-flex h-12 w-12 shrink-0 items-center justify-center text-gray-500"
|
|
80
|
-
aria-hidden
|
|
81
|
-
>
|
|
82
|
-
<ImageIcon size="lg" />
|
|
83
|
-
</span>
|
|
84
|
-
<p className="text-center text-sm font-medium text-gray-900">
|
|
85
|
-
{isDragging ? LABELS.dropFilesHere : LABELS.chooseFileOrDrop}
|
|
86
|
-
</p>
|
|
87
|
-
<p className="text-center text-xs text-gray-500">{formatHint}</p>
|
|
88
|
-
</div>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { Button } from "./ui/__inherit__button";
|
|
2
|
-
import { FileTypeIcon, IconImageClose, SuccessIcon } from "./FileUploadIcons";
|
|
3
|
-
import {
|
|
4
|
-
formatFileSize,
|
|
5
|
-
getFileExtension,
|
|
6
|
-
getProgressWidth,
|
|
7
|
-
isUploading,
|
|
8
|
-
} from "../utils/fileUploadUtils";
|
|
9
|
-
import { LABELS } from "../utils/labels";
|
|
10
|
-
import type { FileUploadItem } from "../types/fileUpload";
|
|
11
|
-
|
|
12
|
-
export interface FileUploadFileItemProps {
|
|
13
|
-
/** The file item with upload state, progress, and optional error */
|
|
14
|
-
item: FileUploadItem;
|
|
15
|
-
/** Called when the user cancels an in-progress upload */
|
|
16
|
-
onCancel: (fileName: string) => void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Renders a single file upload item with file type icon, name, size, progress bar,
|
|
21
|
-
* cancel button (when uploading), and status icon (success, error, or cancelled).
|
|
22
|
-
*/
|
|
23
|
-
export function FileUploadFileItem({ item, onCancel }: FileUploadFileItemProps) {
|
|
24
|
-
const extension = getFileExtension(item.file.name);
|
|
25
|
-
const progressWidth = getProgressWidth(item.state, item.progress);
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<li
|
|
29
|
-
className="grid min-w-0 grid-cols-[auto_minmax(0,4fr)_minmax(0,5fr)] items-center gap-2 rounded-lg border border-gray-200 bg-gray-100 p-2"
|
|
30
|
-
data-testid="file-upload-item"
|
|
31
|
-
>
|
|
32
|
-
<FileTypeIcon extension={extension} />
|
|
33
|
-
<div className="min-w-0 overflow-hidden">
|
|
34
|
-
<p
|
|
35
|
-
className="truncate text-sm font-medium text-gray-900"
|
|
36
|
-
title={item.file.name}
|
|
37
|
-
aria-label={LABELS.fileName(item.file.name)}
|
|
38
|
-
>
|
|
39
|
-
{item.file.name}
|
|
40
|
-
</p>
|
|
41
|
-
<p
|
|
42
|
-
className="truncate text-xs text-gray-600"
|
|
43
|
-
aria-label={LABELS.fileSize(formatFileSize(item.file.size))}
|
|
44
|
-
>
|
|
45
|
-
{formatFileSize(item.file.size)}
|
|
46
|
-
</p>
|
|
47
|
-
</div>
|
|
48
|
-
<div className="flex min-w-0 items-center gap-1.5">
|
|
49
|
-
<div
|
|
50
|
-
className="h-1.5 min-w-0 flex-1 overflow-hidden rounded-full bg-gray-200"
|
|
51
|
-
role="progressbar"
|
|
52
|
-
aria-label={LABELS.uploadProgress(item.file.name)}
|
|
53
|
-
aria-valuenow={progressWidth}
|
|
54
|
-
aria-valuemin={0}
|
|
55
|
-
aria-valuemax={100}
|
|
56
|
-
>
|
|
57
|
-
<div
|
|
58
|
-
className="h-full bg-blue-600 transition-all duration-300"
|
|
59
|
-
style={{ width: `${progressWidth}%` }}
|
|
60
|
-
/>
|
|
61
|
-
</div>
|
|
62
|
-
{isUploading(item.state) && (
|
|
63
|
-
<Button
|
|
64
|
-
type="button"
|
|
65
|
-
variant="ghost"
|
|
66
|
-
size="icon"
|
|
67
|
-
className="h-7 w-7 shrink-0 text-gray-600 hover:text-red-600"
|
|
68
|
-
onClick={() => onCancel(item.file.name)}
|
|
69
|
-
aria-label={LABELS.cancelUpload(item.file.name)}
|
|
70
|
-
>
|
|
71
|
-
<IconImageClose fill="rgb(116, 116, 116)" />
|
|
72
|
-
</Button>
|
|
73
|
-
)}
|
|
74
|
-
{renderStatusIcon(item.state)}
|
|
75
|
-
{item.state === "error" && item.error && <p className="sr-only">{item.error}</p>}
|
|
76
|
-
</div>
|
|
77
|
-
</li>
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function renderStatusIcon(state: FileUploadItem["state"]): React.ReactNode {
|
|
82
|
-
if (isUploading(state)) return null;
|
|
83
|
-
if (state === "success") return <SuccessIcon fill="rgb(46, 132, 74)" />;
|
|
84
|
-
if (state === "cancelled") {
|
|
85
|
-
return (
|
|
86
|
-
<span className="shrink-0 text-xs text-gray-500" aria-label={LABELS.cancelled}>
|
|
87
|
-
{LABELS.cancelled}
|
|
88
|
-
</span>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
return (
|
|
92
|
-
<span
|
|
93
|
-
className="inline-flex h-4 w-4 shrink-0 items-center justify-center text-red-600"
|
|
94
|
-
aria-label={LABELS.uploadFailed}
|
|
95
|
-
>
|
|
96
|
-
×
|
|
97
|
-
</span>
|
|
98
|
-
);
|
|
99
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import symbolsUrl from "../assets/symbols.svg?url";
|
|
2
|
-
|
|
3
|
-
const IMAGE_EXTENSIONS = new Set([
|
|
4
|
-
"jpg",
|
|
5
|
-
"jpeg",
|
|
6
|
-
"png",
|
|
7
|
-
"gif",
|
|
8
|
-
"webp",
|
|
9
|
-
"svg",
|
|
10
|
-
"bmp",
|
|
11
|
-
"ico",
|
|
12
|
-
"tiff",
|
|
13
|
-
"tif",
|
|
14
|
-
]);
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Renders a file-type icon based on extension. Uses symbols.svg: "unknown" for
|
|
18
|
-
* no extension, "image" for image types, or the extension (e.g. pdf) for others.
|
|
19
|
-
*/
|
|
20
|
-
export function FileTypeIcon({ extension }: { extension: string }) {
|
|
21
|
-
/** Symbol ID in symbols.svg: unknown (no ext), image (image types), or extension (e.g. pdf). */
|
|
22
|
-
const ext = extension.toLowerCase();
|
|
23
|
-
const symbolId = extension === "FILE" ? "unknown" : IMAGE_EXTENSIONS.has(ext) ? "image" : ext;
|
|
24
|
-
return (
|
|
25
|
-
<span
|
|
26
|
-
className="inline-flex h-8 w-8 shrink-0 items-center justify-center"
|
|
27
|
-
aria-hidden
|
|
28
|
-
title={extension}
|
|
29
|
-
>
|
|
30
|
-
<svg className="h-full w-full" aria-hidden>
|
|
31
|
-
<use href={`${symbolsUrl}#${symbolId}`} />
|
|
32
|
-
</svg>
|
|
33
|
-
</span>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
import iconImageCloseSvg from "../assets/icon-image-close.svg?raw";
|
|
38
|
-
import successSvg from "../assets/success.svg?raw";
|
|
39
|
-
import imageSvg from "../assets/image.svg?raw";
|
|
40
|
-
|
|
41
|
-
/** Props for icon components (close, success, image) */
|
|
42
|
-
export interface IconProps {
|
|
43
|
-
size?: "sm" | "md" | "lg";
|
|
44
|
-
fill?: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const sizeClass = (size: IconProps["size"]) =>
|
|
48
|
-
size === "sm" ? "h-4 w-4" : size === "lg" ? "h-12 w-12" : "h-6 w-6";
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Close icon (circle with X). Renders from icon-image-close.svg.
|
|
52
|
-
*/
|
|
53
|
-
export function IconImageClose({ size = "md", fill = "currentColor" }: IconProps) {
|
|
54
|
-
return (
|
|
55
|
-
<span
|
|
56
|
-
className={`inline-flex ${sizeClass(size)} shrink-0 items-center justify-center`}
|
|
57
|
-
style={{ color: fill }}
|
|
58
|
-
aria-hidden
|
|
59
|
-
dangerouslySetInnerHTML={{ __html: iconImageCloseSvg }}
|
|
60
|
-
/>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Success icon (circle with checkmark). Renders from success.svg.
|
|
66
|
-
*/
|
|
67
|
-
export function SuccessIcon({ size = "md", fill = "currentColor" }: IconProps) {
|
|
68
|
-
return (
|
|
69
|
-
<span
|
|
70
|
-
className={`inline-flex ${sizeClass(size)} shrink-0 items-center justify-center`}
|
|
71
|
-
style={{ color: fill }}
|
|
72
|
-
aria-hidden
|
|
73
|
-
dangerouslySetInnerHTML={{ __html: successSvg }}
|
|
74
|
-
/>
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Image icon (picture frame). Renders from image.svg. Used for drop zone.
|
|
80
|
-
*/
|
|
81
|
-
export function ImageIcon({ size = "lg", fill = "currentColor" }: IconProps) {
|
|
82
|
-
return (
|
|
83
|
-
<span
|
|
84
|
-
className={`inline-flex ${sizeClass(size)} shrink-0 items-center justify-center`}
|
|
85
|
-
style={{ color: fill }}
|
|
86
|
-
aria-hidden
|
|
87
|
-
dangerouslySetInnerHTML={{ __html: imageSvg }}
|
|
88
|
-
/>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
-
|
|
4
|
-
const buttonVariants = cva(
|
|
5
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
6
|
-
{
|
|
7
|
-
variants: {
|
|
8
|
-
variant: {
|
|
9
|
-
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
10
|
-
destructive:
|
|
11
|
-
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
12
|
-
outline:
|
|
13
|
-
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
14
|
-
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
15
|
-
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
16
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
17
|
-
},
|
|
18
|
-
size: {
|
|
19
|
-
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
20
|
-
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
21
|
-
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
22
|
-
icon: "size-9",
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
defaultVariants: {
|
|
26
|
-
variant: "default",
|
|
27
|
-
size: "default",
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
function Button({}: React.ComponentProps<"button"> &
|
|
33
|
-
VariantProps<typeof buttonVariants> & {
|
|
34
|
-
asChild?: boolean;
|
|
35
|
-
}) {
|
|
36
|
-
return <> </>;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export { Button, buttonVariants };
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { Dialog as DialogPrimitive } from "radix-ui";
|
|
3
|
-
|
|
4
|
-
import { Button } from "./__inherit__button";
|
|
5
|
-
import { XIcon } from "lucide-react";
|
|
6
|
-
|
|
7
|
-
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
8
|
-
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
12
|
-
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
16
|
-
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
20
|
-
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function DialogOverlay({
|
|
24
|
-
className,
|
|
25
|
-
...props
|
|
26
|
-
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
27
|
-
return <DialogPrimitive.Overlay data-slot="dialog-overlay" {...props} />;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function DialogContent({
|
|
31
|
-
className,
|
|
32
|
-
children,
|
|
33
|
-
showCloseButton = true,
|
|
34
|
-
...props
|
|
35
|
-
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
36
|
-
showCloseButton?: boolean;
|
|
37
|
-
}) {
|
|
38
|
-
return (
|
|
39
|
-
<DialogPortal>
|
|
40
|
-
<DialogOverlay />
|
|
41
|
-
<DialogPrimitive.Content data-slot="dialog-content" {...props}>
|
|
42
|
-
{children}
|
|
43
|
-
{showCloseButton && (
|
|
44
|
-
<DialogPrimitive.Close data-slot="dialog-close" asChild>
|
|
45
|
-
<Button variant="ghost" className="absolute top-2 right-2" size="icon">
|
|
46
|
-
<XIcon />
|
|
47
|
-
<span className="sr-only">Close</span>
|
|
48
|
-
</Button>
|
|
49
|
-
</DialogPrimitive.Close>
|
|
50
|
-
)}
|
|
51
|
-
</DialogPrimitive.Content>
|
|
52
|
-
</DialogPortal>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
57
|
-
return <div data-slot="dialog-header" {...props} />;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function DialogFooter({
|
|
61
|
-
className,
|
|
62
|
-
showCloseButton = false,
|
|
63
|
-
children,
|
|
64
|
-
...props
|
|
65
|
-
}: React.ComponentProps<"div"> & {
|
|
66
|
-
showCloseButton?: boolean;
|
|
67
|
-
}) {
|
|
68
|
-
return (
|
|
69
|
-
<div data-slot="dialog-footer" {...props}>
|
|
70
|
-
{children}
|
|
71
|
-
{showCloseButton && (
|
|
72
|
-
<DialogPrimitive.Close asChild>
|
|
73
|
-
<Button variant="outline">Close</Button>
|
|
74
|
-
</DialogPrimitive.Close>
|
|
75
|
-
)}
|
|
76
|
-
</div>
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
81
|
-
return <DialogPrimitive.Title data-slot="dialog-title" {...props} />;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function DialogDescription({
|
|
85
|
-
className,
|
|
86
|
-
...props
|
|
87
|
-
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
88
|
-
return <DialogPrimitive.Description data-slot="dialog-description" {...props} />;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export {
|
|
92
|
-
Dialog,
|
|
93
|
-
DialogClose,
|
|
94
|
-
DialogContent,
|
|
95
|
-
DialogDescription,
|
|
96
|
-
DialogFooter,
|
|
97
|
-
DialogHeader,
|
|
98
|
-
DialogOverlay,
|
|
99
|
-
DialogPortal,
|
|
100
|
-
DialogTitle,
|
|
101
|
-
DialogTrigger,
|
|
102
|
-
};
|