@bitrise/bitkit-v2 0.3.283 → 0.3.284
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.
|
@@ -2,6 +2,12 @@ import { FileUpload } from '@chakra-ui/react/file-upload';
|
|
|
2
2
|
import { BitkitFieldProps } from '../BitkitField/BitkitField';
|
|
3
3
|
declare const BitkitFileInput: import('react').ForwardRefExoticComponent<Omit<BitkitFieldProps, "children" | "state"> & Omit<FileUpload.RootProps, "disabled" | "invalid"> & {
|
|
4
4
|
aspectRatio?: number;
|
|
5
|
+
/**
|
|
6
|
+
* Max width/height (px) of the cropped output. The Ark cropper emits at the
|
|
7
|
+
* selection's natural resolution with no size cap, so a large source image
|
|
8
|
+
* can produce an oversized file; set this to downscale the output to fit.
|
|
9
|
+
*/
|
|
10
|
+
maxCroppedSize?: number;
|
|
5
11
|
onCropChange?: (file: File) => void;
|
|
6
12
|
onFileAccepted?: (file: File) => void;
|
|
7
13
|
state?: "error" | "disabled";
|
|
@@ -11,6 +11,37 @@ import { FileUpload, useFileUploadContext } from "@chakra-ui/react/file-upload";
|
|
|
11
11
|
import { VStack } from "@chakra-ui/react/stack";
|
|
12
12
|
import { splitProps } from "@zag-js/file-upload";
|
|
13
13
|
//#region lib/components/BitkitFileInput/BitkitFileInput.tsx
|
|
14
|
+
var downscaleBlob = (blob, maxSize) => {
|
|
15
|
+
if (!Number.isFinite(maxSize) || maxSize <= 0) return Promise.resolve(blob);
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
const url = URL.createObjectURL(blob);
|
|
18
|
+
const image = new Image();
|
|
19
|
+
image.onload = () => {
|
|
20
|
+
URL.revokeObjectURL(url);
|
|
21
|
+
const largestSide = Math.max(image.naturalWidth, image.naturalHeight);
|
|
22
|
+
if (largestSide <= maxSize) {
|
|
23
|
+
resolve(blob);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const scale = maxSize / largestSide;
|
|
27
|
+
const canvas = document.createElement("canvas");
|
|
28
|
+
canvas.width = Math.round(image.naturalWidth * scale);
|
|
29
|
+
canvas.height = Math.round(image.naturalHeight * scale);
|
|
30
|
+
const context = canvas.getContext("2d");
|
|
31
|
+
if (!context) {
|
|
32
|
+
resolve(blob);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
36
|
+
canvas.toBlob((result) => resolve(result ?? blob), blob.type);
|
|
37
|
+
};
|
|
38
|
+
image.onerror = () => {
|
|
39
|
+
URL.revokeObjectURL(url);
|
|
40
|
+
resolve(blob);
|
|
41
|
+
};
|
|
42
|
+
image.src = url;
|
|
43
|
+
});
|
|
44
|
+
};
|
|
14
45
|
var Dropzone = () => {
|
|
15
46
|
const { disabled } = useFileUploadContext();
|
|
16
47
|
const dropzoneLabel = disabled ? "Cannot upload file" : "Drag and drop file here or click to select";
|
|
@@ -20,7 +51,7 @@ var Dropzone = () => {
|
|
|
20
51
|
})] }) });
|
|
21
52
|
};
|
|
22
53
|
var ImagePreviewContent = (props) => {
|
|
23
|
-
const { onCropChange } = props;
|
|
54
|
+
const { onCropChange, maxCroppedSize } = props;
|
|
24
55
|
const { acceptedFiles } = useFileUploadContext();
|
|
25
56
|
const { dragging, getCroppedImage } = useImageCropperContext();
|
|
26
57
|
const acceptedFile = acceptedFiles[0];
|
|
@@ -34,7 +65,10 @@ var ImagePreviewContent = (props) => {
|
|
|
34
65
|
if (!onCropChange || dragging) return;
|
|
35
66
|
if (!acceptedFile) return;
|
|
36
67
|
let cancelled = false;
|
|
37
|
-
getCroppedImage().then((result) => {
|
|
68
|
+
getCroppedImage({ type: acceptedFile.type || void 0 }).then((result) => {
|
|
69
|
+
if (!(result instanceof Blob)) return result;
|
|
70
|
+
return maxCroppedSize ? downscaleBlob(result, maxCroppedSize) : result;
|
|
71
|
+
}).then((result) => {
|
|
38
72
|
if (!cancelled && result instanceof Blob) onCropChange(new File([result], acceptedFile.name, { type: result.type }));
|
|
39
73
|
});
|
|
40
74
|
return () => {
|
|
@@ -44,7 +78,8 @@ var ImagePreviewContent = (props) => {
|
|
|
44
78
|
getCroppedImage,
|
|
45
79
|
acceptedFile,
|
|
46
80
|
onCropChange,
|
|
47
|
-
dragging
|
|
81
|
+
dragging,
|
|
82
|
+
maxCroppedSize
|
|
48
83
|
]);
|
|
49
84
|
return /* @__PURE__ */ jsxs(ImageCropper_default.Viewport, { children: [/* @__PURE__ */ jsx(ImageCropper_default.Image, { src: objectUrl }), /* @__PURE__ */ jsx(ImageCropper_default.Selection, { children: ImageCropper_default.handles.map((position) => /* @__PURE__ */ jsx(ImageCropper_default.Handle, {
|
|
50
85
|
position,
|
|
@@ -52,11 +87,14 @@ var ImagePreviewContent = (props) => {
|
|
|
52
87
|
}, position)) })] });
|
|
53
88
|
};
|
|
54
89
|
var ImagePreview = (props) => {
|
|
55
|
-
const { aspectRatio, onCropChange } = props;
|
|
90
|
+
const { aspectRatio, onCropChange, maxCroppedSize } = props;
|
|
56
91
|
return /* @__PURE__ */ jsx(ImageCropper_default.Root, {
|
|
57
92
|
aspectRatio,
|
|
58
93
|
height: "100%",
|
|
59
|
-
children: /* @__PURE__ */ jsx(ImagePreviewContent, {
|
|
94
|
+
children: /* @__PURE__ */ jsx(ImagePreviewContent, {
|
|
95
|
+
onCropChange,
|
|
96
|
+
maxCroppedSize
|
|
97
|
+
})
|
|
60
98
|
});
|
|
61
99
|
};
|
|
62
100
|
var NonImagePreview = () => {
|
|
@@ -97,19 +135,20 @@ var NonImagePreview = () => {
|
|
|
97
135
|
});
|
|
98
136
|
};
|
|
99
137
|
var BitkitFileInputContent = (props) => {
|
|
100
|
-
const { variant, aspectRatio, onCropChange } = props;
|
|
138
|
+
const { variant, aspectRatio, onCropChange, maxCroppedSize } = props;
|
|
101
139
|
const acceptedFile = useFileUploadContext().acceptedFiles[0];
|
|
102
140
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
103
141
|
!acceptedFile && /* @__PURE__ */ jsx(Dropzone, {}),
|
|
104
142
|
acceptedFile && variant === "image" && /* @__PURE__ */ jsx(ImagePreview, {
|
|
105
143
|
aspectRatio,
|
|
106
|
-
onCropChange
|
|
144
|
+
onCropChange,
|
|
145
|
+
maxCroppedSize
|
|
107
146
|
}),
|
|
108
147
|
acceptedFile && variant !== "image" && /* @__PURE__ */ jsx(NonImagePreview, {})
|
|
109
148
|
] });
|
|
110
149
|
};
|
|
111
150
|
var BitkitFileInput = forwardRef((props, ref) => {
|
|
112
|
-
const { aspectRatio, onCropChange, onFileAccept, onFileAccepted, state, variant, ...rest } = props;
|
|
151
|
+
const { aspectRatio, maxCroppedSize, onCropChange, onFileAccept, onFileAccepted, state, variant, ...rest } = props;
|
|
113
152
|
const [fileUploadProps, fieldProps] = splitProps(rest);
|
|
114
153
|
const handleFileAccept = (details) => {
|
|
115
154
|
onFileAccept?.(details);
|
|
@@ -118,7 +157,8 @@ var BitkitFileInput = forwardRef((props, ref) => {
|
|
|
118
157
|
const contentProps = {
|
|
119
158
|
variant,
|
|
120
159
|
aspectRatio,
|
|
121
|
-
onCropChange
|
|
160
|
+
onCropChange,
|
|
161
|
+
maxCroppedSize
|
|
122
162
|
};
|
|
123
163
|
return /* @__PURE__ */ jsx(BitkitField, {
|
|
124
164
|
ref,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitFileInput.js","names":[],"sources":["../../../lib/components/BitkitFileInput/BitkitFileInput.tsx"],"sourcesContent":["import { Card } from '@chakra-ui/react/card';\nimport {\n FileUpload,\n type FileUploadFileAcceptDetails,\n type FileUploadRootProps,\n useFileUploadContext,\n} from '@chakra-ui/react/file-upload';\nimport { VStack } from '@chakra-ui/react/stack';\nimport { Text } from '@chakra-ui/react/text';\nimport { splitProps as splitFileUploadProps } from '@zag-js/file-upload';\nimport { forwardRef, useEffect, useMemo } from 'react';\n\nimport ImageCropper, { useImageCropperContext } from '../../atoms/ImageCropper/ImageCropper';\nimport { IconMinusCircle, IconUpload } from '../../icons';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField';\n\ntype BitkitFileInputProps = Omit<BitkitFieldProps, 'children' | 'state'> &\n Omit<FileUploadRootProps, 'disabled' | 'invalid'> & {\n aspectRatio?: number;\n onCropChange?: (file: File) => void;\n onFileAccepted?: (file: File) => void;\n state?: 'error' | 'disabled';\n };\n\nconst Dropzone = () => {\n const { disabled } = useFileUploadContext();\n const dropzoneLabel = disabled ? 'Cannot upload file' : 'Drag and drop file here or click to select';\n\n return (\n <FileUpload.Dropzone>\n <FileUpload.DropzoneContent>\n <IconUpload />\n <Text as=\"span\">{dropzoneLabel}</Text>\n </FileUpload.DropzoneContent>\n </FileUpload.Dropzone>\n );\n};\n\nconst ImagePreviewContent = (props: Pick<BitkitFileInputProps, 'onCropChange'>) => {\n const { onCropChange } = props;\n\n const { acceptedFiles } = useFileUploadContext();\n const { dragging, getCroppedImage } = useImageCropperContext();\n\n const acceptedFile = acceptedFiles[0];\n const objectUrl = useMemo(() => (acceptedFile ? URL.createObjectURL(acceptedFile) : undefined), [acceptedFile]);\n\n useEffect(() => {\n return () => {\n if (objectUrl) {\n URL.revokeObjectURL(objectUrl);\n }\n };\n }, [objectUrl]);\n\n useEffect(() => {\n if (!onCropChange || dragging) {\n return;\n }\n\n if (!acceptedFile) {\n return;\n }\n\n let cancelled = false;\n getCroppedImage().then((result) => {\n if (!cancelled && result instanceof Blob) {\n onCropChange(new File([result], acceptedFile.name, { type: result.type }));\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [getCroppedImage, acceptedFile, onCropChange, dragging]);\n\n return (\n <ImageCropper.Viewport>\n <ImageCropper.Image src={objectUrl} />\n <ImageCropper.Selection>\n {ImageCropper.handles.map((position) => (\n <ImageCropper.Handle key={position} position={position}>\n <div />\n </ImageCropper.Handle>\n ))}\n </ImageCropper.Selection>\n </ImageCropper.Viewport>\n );\n};\n\nconst ImagePreview = (props: Pick<BitkitFileInputProps, 'aspectRatio' | 'onCropChange'>) => {\n const { aspectRatio, onCropChange } = props;\n\n return (\n <ImageCropper.Root aspectRatio={aspectRatio} height=\"100%\">\n <ImagePreviewContent onCropChange={onCropChange} />\n </ImageCropper.Root>\n );\n};\n\nconst NonImagePreview = () => {\n const fileUpload = useFileUploadContext();\n const acceptedFile = fileUpload.acceptedFiles[0];\n\n return (\n <Card.Root asChild elevation={false}>\n <Card.Body display=\"flex\" paddingBlock=\"20\" paddingInline=\"24\" alignItems=\"center\" flex=\"1\">\n <VStack gap=\"4\" alignItems=\"flex-start\" flex=\"1\" minWidth=\"0\">\n <Text textStyle=\"comp/input/label\">Selected file</Text>\n <Text whiteSpace=\"nowrap\" textOverflow=\"ellipsis\" overflow=\"hidden\" maxWidth=\"100%\">\n {acceptedFile.name}\n </Text>\n </VStack>\n <BitkitButton icon={IconMinusCircle} variant=\"secondary\" size=\"md\" onClick={fileUpload.clearFiles}>\n Remove\n </BitkitButton>\n </Card.Body>\n </Card.Root>\n );\n};\n\nconst BitkitFileInputContent = (props: Pick<BitkitFileInputProps, 'variant' | 'aspectRatio' | 'onCropChange'>) => {\n const { variant, aspectRatio, onCropChange } = props;\n const acceptedFile = useFileUploadContext().acceptedFiles[0];\n\n return (\n <>\n {!acceptedFile && <Dropzone />}\n {acceptedFile && variant === 'image' && <ImagePreview aspectRatio={aspectRatio} onCropChange={onCropChange} />}\n {acceptedFile && variant !== 'image' && <NonImagePreview />}\n </>\n );\n};\n\nconst BitkitFileInput = forwardRef<HTMLDivElement, BitkitFileInputProps>((props, ref) => {\n const { aspectRatio, onCropChange, onFileAccept, onFileAccepted, state, variant, ...rest } = props;\n const [fileUploadProps, fieldProps] = splitFileUploadProps(rest as never);\n\n const handleFileAccept = (details: FileUploadFileAcceptDetails) => {\n onFileAccept?.(details);\n onFileAccepted?.(details.files[0]);\n };\n\n const contentProps = {\n variant,\n aspectRatio,\n onCropChange,\n };\n\n return (\n <BitkitField ref={ref} state={state} {...fieldProps}>\n <FileUpload.Root variant={variant} onFileAccept={handleFileAccept} {...fileUploadProps}>\n <FileUpload.HiddenInput />\n <BitkitFileInputContent {...contentProps} />\n </FileUpload.Root>\n </BitkitField>\n );\n});\n\nBitkitFileInput.displayName = 'BitkitFileInput';\n\nexport default BitkitFileInput;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BitkitFileInput.js","names":[],"sources":["../../../lib/components/BitkitFileInput/BitkitFileInput.tsx"],"sourcesContent":["import { Card } from '@chakra-ui/react/card';\nimport {\n FileUpload,\n type FileUploadFileAcceptDetails,\n type FileUploadRootProps,\n useFileUploadContext,\n} from '@chakra-ui/react/file-upload';\nimport { VStack } from '@chakra-ui/react/stack';\nimport { Text } from '@chakra-ui/react/text';\nimport { splitProps as splitFileUploadProps } from '@zag-js/file-upload';\nimport { forwardRef, useEffect, useMemo } from 'react';\n\nimport ImageCropper, { useImageCropperContext } from '../../atoms/ImageCropper/ImageCropper';\nimport { IconMinusCircle, IconUpload } from '../../icons';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField';\n\ntype BitkitFileInputProps = Omit<BitkitFieldProps, 'children' | 'state'> &\n Omit<FileUploadRootProps, 'disabled' | 'invalid'> & {\n aspectRatio?: number;\n /**\n * Max width/height (px) of the cropped output. The Ark cropper emits at the\n * selection's natural resolution with no size cap, so a large source image\n * can produce an oversized file; set this to downscale the output to fit.\n */\n maxCroppedSize?: number;\n onCropChange?: (file: File) => void;\n onFileAccepted?: (file: File) => void;\n state?: 'error' | 'disabled';\n };\n\n// Ark's getCroppedImage() emits at the selection's natural resolution and only\n// controls format/quality (no size cap), so downscale here when a max is set —\n// this keeps a large source image from producing an oversized upload. Preserves\n// the blob's MIME type; falls back to the original blob if it can't be decoded.\nconst downscaleBlob = (blob: Blob, maxSize: number): Promise<Blob> => {\n // A non-positive or non-finite max would produce a 0×0 / NaN canvas and corrupt\n // the output, so treat it as \"no cap\" and pass the blob through untouched.\n if (!Number.isFinite(maxSize) || maxSize <= 0) {\n return Promise.resolve(blob);\n }\n\n return new Promise((resolve) => {\n const url = URL.createObjectURL(blob);\n const image = new Image();\n\n image.onload = () => {\n URL.revokeObjectURL(url);\n const largestSide = Math.max(image.naturalWidth, image.naturalHeight);\n if (largestSide <= maxSize) {\n resolve(blob);\n return;\n }\n const scale = maxSize / largestSide;\n const canvas = document.createElement('canvas');\n canvas.width = Math.round(image.naturalWidth * scale);\n canvas.height = Math.round(image.naturalHeight * scale);\n const context = canvas.getContext('2d');\n if (!context) {\n resolve(blob);\n return;\n }\n context.drawImage(image, 0, 0, canvas.width, canvas.height);\n canvas.toBlob((result) => resolve(result ?? blob), blob.type);\n };\n\n image.onerror = () => {\n URL.revokeObjectURL(url);\n resolve(blob);\n };\n\n image.src = url;\n });\n};\n\nconst Dropzone = () => {\n const { disabled } = useFileUploadContext();\n const dropzoneLabel = disabled ? 'Cannot upload file' : 'Drag and drop file here or click to select';\n\n return (\n <FileUpload.Dropzone>\n <FileUpload.DropzoneContent>\n <IconUpload />\n <Text as=\"span\">{dropzoneLabel}</Text>\n </FileUpload.DropzoneContent>\n </FileUpload.Dropzone>\n );\n};\n\nconst ImagePreviewContent = (props: Pick<BitkitFileInputProps, 'onCropChange' | 'maxCroppedSize'>) => {\n const { onCropChange, maxCroppedSize } = props;\n\n const { acceptedFiles } = useFileUploadContext();\n const { dragging, getCroppedImage } = useImageCropperContext();\n\n const acceptedFile = acceptedFiles[0];\n const objectUrl = useMemo(() => (acceptedFile ? URL.createObjectURL(acceptedFile) : undefined), [acceptedFile]);\n\n useEffect(() => {\n return () => {\n if (objectUrl) {\n URL.revokeObjectURL(objectUrl);\n }\n };\n }, [objectUrl]);\n\n useEffect(() => {\n if (!onCropChange || dragging) {\n return;\n }\n\n if (!acceptedFile) {\n return;\n }\n\n let cancelled = false;\n // Preserve the source MIME type — the Ark cropper defaults to image/png, which\n // would mismatch a .jpg name and bloat the file.\n getCroppedImage({ type: acceptedFile.type || undefined })\n .then((result) => {\n if (!(result instanceof Blob)) {\n return result;\n }\n return maxCroppedSize ? downscaleBlob(result, maxCroppedSize) : result;\n })\n .then((result) => {\n if (!cancelled && result instanceof Blob) {\n onCropChange(new File([result], acceptedFile.name, { type: result.type }));\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [getCroppedImage, acceptedFile, onCropChange, dragging, maxCroppedSize]);\n\n return (\n <ImageCropper.Viewport>\n <ImageCropper.Image src={objectUrl} />\n <ImageCropper.Selection>\n {ImageCropper.handles.map((position) => (\n <ImageCropper.Handle key={position} position={position}>\n <div />\n </ImageCropper.Handle>\n ))}\n </ImageCropper.Selection>\n </ImageCropper.Viewport>\n );\n};\n\nconst ImagePreview = (props: Pick<BitkitFileInputProps, 'aspectRatio' | 'onCropChange' | 'maxCroppedSize'>) => {\n const { aspectRatio, onCropChange, maxCroppedSize } = props;\n\n return (\n <ImageCropper.Root aspectRatio={aspectRatio} height=\"100%\">\n <ImagePreviewContent onCropChange={onCropChange} maxCroppedSize={maxCroppedSize} />\n </ImageCropper.Root>\n );\n};\n\nconst NonImagePreview = () => {\n const fileUpload = useFileUploadContext();\n const acceptedFile = fileUpload.acceptedFiles[0];\n\n return (\n <Card.Root asChild elevation={false}>\n <Card.Body display=\"flex\" paddingBlock=\"20\" paddingInline=\"24\" alignItems=\"center\" flex=\"1\">\n <VStack gap=\"4\" alignItems=\"flex-start\" flex=\"1\" minWidth=\"0\">\n <Text textStyle=\"comp/input/label\">Selected file</Text>\n <Text whiteSpace=\"nowrap\" textOverflow=\"ellipsis\" overflow=\"hidden\" maxWidth=\"100%\">\n {acceptedFile.name}\n </Text>\n </VStack>\n <BitkitButton icon={IconMinusCircle} variant=\"secondary\" size=\"md\" onClick={fileUpload.clearFiles}>\n Remove\n </BitkitButton>\n </Card.Body>\n </Card.Root>\n );\n};\n\nconst BitkitFileInputContent = (\n props: Pick<BitkitFileInputProps, 'variant' | 'aspectRatio' | 'onCropChange' | 'maxCroppedSize'>,\n) => {\n const { variant, aspectRatio, onCropChange, maxCroppedSize } = props;\n const acceptedFile = useFileUploadContext().acceptedFiles[0];\n\n return (\n <>\n {!acceptedFile && <Dropzone />}\n {acceptedFile && variant === 'image' && (\n <ImagePreview aspectRatio={aspectRatio} onCropChange={onCropChange} maxCroppedSize={maxCroppedSize} />\n )}\n {acceptedFile && variant !== 'image' && <NonImagePreview />}\n </>\n );\n};\n\nconst BitkitFileInput = forwardRef<HTMLDivElement, BitkitFileInputProps>((props, ref) => {\n const { aspectRatio, maxCroppedSize, onCropChange, onFileAccept, onFileAccepted, state, variant, ...rest } = props;\n const [fileUploadProps, fieldProps] = splitFileUploadProps(rest as never);\n\n const handleFileAccept = (details: FileUploadFileAcceptDetails) => {\n onFileAccept?.(details);\n onFileAccepted?.(details.files[0]);\n };\n\n const contentProps = {\n variant,\n aspectRatio,\n onCropChange,\n maxCroppedSize,\n };\n\n return (\n <BitkitField ref={ref} state={state} {...fieldProps}>\n <FileUpload.Root variant={variant} onFileAccept={handleFileAccept} {...fileUploadProps}>\n <FileUpload.HiddenInput />\n <BitkitFileInputContent {...contentProps} />\n </FileUpload.Root>\n </BitkitField>\n );\n});\n\nBitkitFileInput.displayName = 'BitkitFileInput';\n\nexport default BitkitFileInput;\n"],"mappings":"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"}
|