@bitrise/bitkit-v2 0.3.283 → 0.3.285
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/components/BitkitFileInput/BitkitFileInput.d.ts +6 -0
- package/dist/components/BitkitFileInput/BitkitFileInput.js +49 -9
- package/dist/components/BitkitFileInput/BitkitFileInput.js.map +1 -1
- package/dist/components/BitkitPromoBanner/BitkitPromoBanner.js +36 -5
- package/dist/components/BitkitPromoBanner/BitkitPromoBanner.js.map +1 -1
- package/dist/components/common/NotificationContent.d.ts +3 -1
- package/dist/components/common/NotificationContent.js +39 -9
- package/dist/components/common/NotificationContent.js.map +1 -1
- package/package.json +1 -1
|
@@ -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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"}
|
|
@@ -11,22 +11,52 @@ var PROMO_BANNER_COLOR = BUTTON_COLORS_MAP.info;
|
|
|
11
11
|
var BitkitPromoBanner = (props) => {
|
|
12
12
|
const { actionable, dismissible, illustration, message, onClose, title, ...rest } = props;
|
|
13
13
|
return /* @__PURE__ */ jsxs(Alert.Root, {
|
|
14
|
+
...rest,
|
|
14
15
|
variant: "info",
|
|
15
16
|
border: "none",
|
|
17
|
+
position: "relative",
|
|
18
|
+
flexDirection: {
|
|
19
|
+
base: "column",
|
|
20
|
+
tablet: "row"
|
|
21
|
+
},
|
|
22
|
+
alignItems: {
|
|
23
|
+
base: "stretch",
|
|
24
|
+
tablet: "center"
|
|
25
|
+
},
|
|
26
|
+
rowGap: {
|
|
27
|
+
base: "16",
|
|
28
|
+
tablet: "0"
|
|
29
|
+
},
|
|
30
|
+
paddingBlockEnd: {
|
|
31
|
+
base: actionable ? "20" : "16",
|
|
32
|
+
tablet: rem(11)
|
|
33
|
+
},
|
|
16
34
|
css: { "& .alert__title + *": { marginBlockStart: 0 } },
|
|
17
|
-
...rest,
|
|
18
35
|
children: [illustration ? /* @__PURE__ */ jsx(Box, {
|
|
19
36
|
flexShrink: 0,
|
|
20
|
-
marginInlineEnd:
|
|
37
|
+
marginInlineEnd: {
|
|
38
|
+
base: "0",
|
|
39
|
+
tablet: "12"
|
|
40
|
+
},
|
|
21
41
|
"aria-hidden": true,
|
|
22
42
|
children: illustration
|
|
23
43
|
}) : /* @__PURE__ */ jsx(Icon, {
|
|
24
44
|
asChild: true,
|
|
25
45
|
width: rem(72),
|
|
26
46
|
height: rem(68),
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
47
|
+
flexShrink: 0,
|
|
48
|
+
marginInlineEnd: {
|
|
49
|
+
base: "0",
|
|
50
|
+
tablet: "12"
|
|
51
|
+
},
|
|
52
|
+
marginBlock: {
|
|
53
|
+
base: "0",
|
|
54
|
+
tablet: rem(-11)
|
|
55
|
+
},
|
|
56
|
+
marginInlineStart: {
|
|
57
|
+
base: "0",
|
|
58
|
+
tablet: rem(-12)
|
|
59
|
+
},
|
|
30
60
|
children: /* @__PURE__ */ jsx(ForwardRef, {})
|
|
31
61
|
}), /* @__PURE__ */ jsx(NotificationContent, {
|
|
32
62
|
action: actionable,
|
|
@@ -34,6 +64,7 @@ var BitkitPromoBanner = (props) => {
|
|
|
34
64
|
dismissible,
|
|
35
65
|
messageText: message,
|
|
36
66
|
onClose,
|
|
67
|
+
stackable: true,
|
|
37
68
|
titleText: title
|
|
38
69
|
})]
|
|
39
70
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitPromoBanner.js","names":[],"sources":["../../../lib/components/BitkitPromoBanner/BitkitPromoBanner.tsx"],"sourcesContent":["import { Alert, type AlertRootProps } from '@chakra-ui/react/alert';\nimport { Box } from '@chakra-ui/react/box';\nimport { Icon } from '@chakra-ui/react/icon';\nimport { type ReactNode } from 'react';\n\nimport { rem } from '../../theme/themeUtils';\nimport { NotificationContent } from '../common/NotificationContent';\nimport { BUTTON_COLORS_MAP, type NotificationAction } from '../common/notificationMaps';\nimport MascotSvg from './mascot.svg?react';\n\nexport type PromoBannerAction = NotificationAction;\n\nconst PROMO_BANNER_COLOR = BUTTON_COLORS_MAP.info;\n\nexport interface BitkitPromoBannerProps extends Omit<AlertRootProps, 'title' | 'variant'> {\n actionable?: PromoBannerAction;\n dismissible?: boolean;\n /** Custom leading illustration. Defaults to the built-in mascot. */\n illustration?: ReactNode;\n message: ReactNode;\n onClose?: () => void;\n title: ReactNode;\n}\n\nconst BitkitPromoBanner = (props: BitkitPromoBannerProps) => {\n const { actionable, dismissible, illustration, message, onClose, title, ...rest } = props;\n\n return (\n <Alert.Root
|
|
1
|
+
{"version":3,"file":"BitkitPromoBanner.js","names":[],"sources":["../../../lib/components/BitkitPromoBanner/BitkitPromoBanner.tsx"],"sourcesContent":["import { Alert, type AlertRootProps } from '@chakra-ui/react/alert';\nimport { Box } from '@chakra-ui/react/box';\nimport { Icon } from '@chakra-ui/react/icon';\nimport { type ReactNode } from 'react';\n\nimport { rem } from '../../theme/themeUtils';\nimport { NotificationContent } from '../common/NotificationContent';\nimport { BUTTON_COLORS_MAP, type NotificationAction } from '../common/notificationMaps';\nimport MascotSvg from './mascot.svg?react';\n\nexport type PromoBannerAction = NotificationAction;\n\nconst PROMO_BANNER_COLOR = BUTTON_COLORS_MAP.info;\n\nexport interface BitkitPromoBannerProps extends Omit<AlertRootProps, 'title' | 'variant'> {\n actionable?: PromoBannerAction;\n dismissible?: boolean;\n /** Custom leading illustration. Defaults to the built-in mascot. */\n illustration?: ReactNode;\n message: ReactNode;\n onClose?: () => void;\n title: ReactNode;\n}\n\nconst BitkitPromoBanner = (props: BitkitPromoBannerProps) => {\n const { actionable, dismissible, illustration, message, onClose, title, ...rest } = props;\n\n return (\n <Alert.Root\n {...rest}\n variant=\"info\"\n border=\"none\"\n position=\"relative\"\n flexDirection={{ base: 'column', tablet: 'row' }}\n alignItems={{ base: 'stretch', tablet: 'center' }}\n rowGap={{ base: '16', tablet: '0' }}\n paddingBlockEnd={{ base: actionable ? '20' : '16', tablet: rem(11) }}\n css={{ '& .alert__title + *': { marginBlockStart: 0 } }}\n >\n {illustration ? (\n <Box flexShrink={0} marginInlineEnd={{ base: '0', tablet: '12' }} aria-hidden>\n {illustration}\n </Box>\n ) : (\n <Icon\n asChild\n width={rem(72)}\n height={rem(68)}\n flexShrink={0}\n marginInlineEnd={{ base: '0', tablet: '12' }}\n marginBlock={{ base: '0', tablet: rem(-11) }}\n marginInlineStart={{ base: '0', tablet: rem(-12) }}\n >\n <MascotSvg />\n </Icon>\n )}\n <NotificationContent\n action={actionable}\n colorVariant={PROMO_BANNER_COLOR}\n dismissible={dismissible}\n messageText={message}\n onClose={onClose}\n stackable\n titleText={title}\n />\n </Alert.Root>\n );\n};\n\nBitkitPromoBanner.displayName = 'BitkitPromoBanner';\n\nexport default BitkitPromoBanner;\n"],"mappings":";;;;;;;;;AAYA,IAAM,qBAAqB,kBAAkB;AAY7C,IAAM,qBAAqB,UAAkC;CAC3D,MAAM,EAAE,YAAY,aAAa,cAAc,SAAS,SAAS,OAAO,GAAG,SAAS;CAEpF,OACE,qBAAC,MAAM,MAAP;EACE,GAAI;EACJ,SAAQ;EACR,QAAO;EACP,UAAS;EACT,eAAe;GAAE,MAAM;GAAU,QAAQ;EAAM;EAC/C,YAAY;GAAE,MAAM;GAAW,QAAQ;EAAS;EAChD,QAAQ;GAAE,MAAM;GAAM,QAAQ;EAAI;EAClC,iBAAiB;GAAE,MAAM,aAAa,OAAO;GAAM,QAAQ,IAAI,EAAE;EAAE;EACnE,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,EAAE;YATxD,CAWG,eACC,oBAAC,KAAD;GAAK,YAAY;GAAG,iBAAiB;IAAE,MAAM;IAAK,QAAQ;GAAK;GAAG,eAAA;aAC/D;EACE,CAAA,IAEL,oBAAC,MAAD;GACE,SAAA;GACA,OAAO,IAAI,EAAE;GACb,QAAQ,IAAI,EAAE;GACd,YAAY;GACZ,iBAAiB;IAAE,MAAM;IAAK,QAAQ;GAAK;GAC3C,aAAa;IAAE,MAAM;IAAK,QAAQ,IAAI,GAAG;GAAE;GAC3C,mBAAmB;IAAE,MAAM;IAAK,QAAQ,IAAI,GAAG;GAAE;aAEjD,oBAAC,YAAD,CAAY,CAAA;EACR,CAAA,GAER,oBAAC,qBAAD;GACE,QAAQ;GACR,cAAc;GACD;GACb,aAAa;GACJ;GACT,WAAA;GACA,WAAW;EACZ,CAAA,CACS;;AAEhB;AAEA,kBAAkB,cAAc"}
|
|
@@ -7,7 +7,9 @@ export interface NotificationContentProps {
|
|
|
7
7
|
dismissible?: boolean;
|
|
8
8
|
messageText: ReactNode;
|
|
9
9
|
onClose?: () => void;
|
|
10
|
+
/** Opt in to responsive stacking below `tablet`. Requires Root `position="relative"`. */
|
|
11
|
+
stackable?: boolean;
|
|
10
12
|
titleText?: ReactNode;
|
|
11
13
|
}
|
|
12
|
-
export declare const NotificationContent: ({ action, colorVariant, dismissible, messageText, onClose, titleText, }: NotificationContentProps) => import("react").JSX.Element;
|
|
14
|
+
export declare const NotificationContent: ({ action, colorVariant, dismissible, messageText, onClose, stackable, titleText, }: NotificationContentProps) => import("react").JSX.Element;
|
|
13
15
|
export {};
|
|
@@ -19,24 +19,53 @@ var ActionButton = ({ action, colorVariant }) => /* @__PURE__ */ jsx(BitkitColor
|
|
|
19
19
|
whiteSpace: "nowrap",
|
|
20
20
|
children: action.label
|
|
21
21
|
});
|
|
22
|
-
var CloseButton = ({ colorVariant, onClose }) => /* @__PURE__ */ jsx(BitkitCloseButton, {
|
|
22
|
+
var CloseButton = ({ colorVariant, onClose, stackable }) => /* @__PURE__ */ jsx(BitkitCloseButton, {
|
|
23
23
|
alignSelf: "flex-start",
|
|
24
|
-
marginBlock: rem(-4),
|
|
25
24
|
size: "sm",
|
|
26
25
|
onClick: onClose,
|
|
27
|
-
colorVariant
|
|
26
|
+
colorVariant,
|
|
27
|
+
position: stackable ? {
|
|
28
|
+
base: "absolute",
|
|
29
|
+
tablet: "static"
|
|
30
|
+
} : void 0,
|
|
31
|
+
insetBlockStart: stackable ? "8" : void 0,
|
|
32
|
+
insetInlineEnd: stackable ? "8" : void 0,
|
|
33
|
+
marginBlock: {
|
|
34
|
+
base: stackable ? "0" : rem(-4),
|
|
35
|
+
tablet: rem(-4)
|
|
36
|
+
}
|
|
28
37
|
});
|
|
29
|
-
var NotificationContent = ({ action, colorVariant, dismissible, messageText, onClose, titleText }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Box, {
|
|
38
|
+
var NotificationContent = ({ action, colorVariant, dismissible, messageText, onClose, stackable, titleText }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Box, {
|
|
30
39
|
display: "flex",
|
|
31
|
-
|
|
40
|
+
flexDirection: stackable ? {
|
|
41
|
+
base: "column",
|
|
42
|
+
tablet: "row"
|
|
43
|
+
} : void 0,
|
|
44
|
+
flex: stackable ? {
|
|
45
|
+
base: "0 1 0%",
|
|
46
|
+
tablet: "1"
|
|
47
|
+
} : "1",
|
|
32
48
|
minWidth: "0",
|
|
33
49
|
flexWrap: "wrap",
|
|
34
|
-
alignItems:
|
|
50
|
+
alignItems: stackable ? {
|
|
51
|
+
base: "flex-start",
|
|
52
|
+
tablet: "center"
|
|
53
|
+
} : "center",
|
|
35
54
|
columnGap: "16",
|
|
36
|
-
rowGap:
|
|
55
|
+
rowGap: stackable ? {
|
|
56
|
+
base: "20",
|
|
57
|
+
tablet: rem(11)
|
|
58
|
+
} : rem(11),
|
|
59
|
+
paddingInlineEnd: stackable && dismissible ? {
|
|
60
|
+
base: "40",
|
|
61
|
+
tablet: "0"
|
|
62
|
+
} : void 0,
|
|
37
63
|
children: [/* @__PURE__ */ jsxs(Alert.Content, {
|
|
38
64
|
minWidth: "0",
|
|
39
|
-
flex:
|
|
65
|
+
flex: stackable ? {
|
|
66
|
+
base: "none",
|
|
67
|
+
tablet: `1 1 ${rem(240)}`
|
|
68
|
+
} : `1 1 ${rem(240)}`,
|
|
40
69
|
children: [titleText && /* @__PURE__ */ jsx(Alert.Title, { children: titleText }), /* @__PURE__ */ jsx(Alert.Description, { children: messageText })]
|
|
41
70
|
}), !!action && /* @__PURE__ */ jsx(ActionButton, {
|
|
42
71
|
action,
|
|
@@ -44,7 +73,8 @@ var NotificationContent = ({ action, colorVariant, dismissible, messageText, onC
|
|
|
44
73
|
})]
|
|
45
74
|
}), !!dismissible && /* @__PURE__ */ jsx(CloseButton, {
|
|
46
75
|
colorVariant,
|
|
47
|
-
onClose
|
|
76
|
+
onClose,
|
|
77
|
+
stackable
|
|
48
78
|
})] });
|
|
49
79
|
//#endregion
|
|
50
80
|
export { NotificationContent };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationContent.js","names":[],"sources":["../../../lib/components/common/NotificationContent.tsx"],"sourcesContent":["/* Shared inner structure for notification-like components (Alert, PromoBanner):\n the content + action row and the trailing close button, plus the two buttons themselves.\n Alert.Root is the flex container, so this fragment's row and close button land as flex\n siblings next to the leading visual (indicator / illustration).\n The constants here are load-bearing and must not drift across the two components:\n `rem(11)` rowGap + the button `rem(-4)` bleed keep a single-line notification on 48px,\n and `flex: 1 1 rem(240)` on the content lets the action wrap below the text on narrow widths. */\n\nimport { Alert } from '@chakra-ui/react/alert';\nimport { Box } from '@chakra-ui/react/box';\nimport { type ReactNode } from 'react';\n\nimport { rem } from '../../theme/themeUtils';\nimport BitkitCloseButton, { type BitkitCloseButtonProps } from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitColorButton, { type BitkitColorButtonProps } from '../BitkitColorButton/BitkitColorButton';\nimport { type BUTTON_COLORS_MAP, type NotificationAction } from './notificationMaps';\n\ntype NotificationColorVariant = (typeof BUTTON_COLORS_MAP)[keyof typeof BUTTON_COLORS_MAP];\n\nconst ActionButton = ({\n action,\n colorVariant,\n}: {\n action: NotificationAction;\n colorVariant: BitkitColorButtonProps['colorVariant'];\n}) => (\n <BitkitColorButton\n as={action.href ? 'a' : 'button'}\n colorVariant={colorVariant}\n {...(action.href && {\n href: action.href,\n target: action.target,\n rel: action.target === '_blank' ? 'noopener noreferrer' : undefined,\n })}\n onClick={action.onClick}\n marginBlock={rem(-4)}\n marginInlineEnd=\"12\"\n whiteSpace=\"nowrap\"\n >\n {action.label}\n </BitkitColorButton>\n);\n\nconst CloseButton = ({\n colorVariant,\n onClose,\n}: {\n colorVariant: BitkitCloseButtonProps['colorVariant'];\n onClose?: () => void;\n}) => (\n <BitkitCloseButton\n alignSelf=\"flex-start\"\n
|
|
1
|
+
{"version":3,"file":"NotificationContent.js","names":[],"sources":["../../../lib/components/common/NotificationContent.tsx"],"sourcesContent":["/* Shared inner structure for notification-like components (Alert, PromoBanner):\n the content + action row and the trailing close button, plus the two buttons themselves.\n Alert.Root is the flex container, so this fragment's row and close button land as flex\n siblings next to the leading visual (indicator / illustration).\n The constants here are load-bearing and must not drift across the two components:\n `rem(11)` rowGap + the button `rem(-4)` bleed keep a single-line notification on 48px,\n and `flex: 1 1 rem(240)` on the content lets the action wrap below the text on narrow widths.\n\n `stackable` (opt-in, currently only PromoBanner) makes the elements this fragment owns\n responsive: below `tablet` the content/action row stacks vertically and the close button\n floats to the top-right corner. It positions the button absolutely, so the consuming\n component must give Alert.Root `position=\"relative\"`. Because the button floats over the\n top-right corner (32px at `inset 8` ⇒ a 40px band), the content row reserves a matching\n inline-end gutter below `tablet` so title/message text never runs under it — independent of\n how tall the leading visual is. The leading visual and the Root's own flex direction / gap /\n padding stay with the consumer — they differ per component. */\n\nimport { Alert } from '@chakra-ui/react/alert';\nimport { Box } from '@chakra-ui/react/box';\nimport { type ReactNode } from 'react';\n\nimport { rem } from '../../theme/themeUtils';\nimport BitkitCloseButton, { type BitkitCloseButtonProps } from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitColorButton, { type BitkitColorButtonProps } from '../BitkitColorButton/BitkitColorButton';\nimport { type BUTTON_COLORS_MAP, type NotificationAction } from './notificationMaps';\n\ntype NotificationColorVariant = (typeof BUTTON_COLORS_MAP)[keyof typeof BUTTON_COLORS_MAP];\n\nconst ActionButton = ({\n action,\n colorVariant,\n}: {\n action: NotificationAction;\n colorVariant: BitkitColorButtonProps['colorVariant'];\n}) => (\n <BitkitColorButton\n as={action.href ? 'a' : 'button'}\n colorVariant={colorVariant}\n {...(action.href && {\n href: action.href,\n target: action.target,\n rel: action.target === '_blank' ? 'noopener noreferrer' : undefined,\n })}\n onClick={action.onClick}\n marginBlock={rem(-4)}\n marginInlineEnd=\"12\"\n whiteSpace=\"nowrap\"\n >\n {action.label}\n </BitkitColorButton>\n);\n\nconst CloseButton = ({\n colorVariant,\n onClose,\n stackable,\n}: {\n colorVariant: BitkitCloseButtonProps['colorVariant'];\n onClose?: () => void;\n stackable?: boolean;\n}) => (\n <BitkitCloseButton\n alignSelf=\"flex-start\"\n size=\"sm\"\n onClick={onClose}\n colorVariant={colorVariant}\n // Below tablet (stackable only) it floats to the top-right corner (needs Root\n // position=\"relative\"); otherwise inline with the -4 bleed that keeps a single line on 48px.\n position={stackable ? { base: 'absolute', tablet: 'static' } : undefined}\n insetBlockStart={stackable ? '8' : undefined}\n insetInlineEnd={stackable ? '8' : undefined}\n marginBlock={{ base: stackable ? '0' : rem(-4), tablet: rem(-4) }}\n />\n);\n\nexport interface NotificationContentProps {\n action?: NotificationAction;\n colorVariant: NotificationColorVariant;\n dismissible?: boolean;\n messageText: ReactNode;\n onClose?: () => void;\n /** Opt in to responsive stacking below `tablet`. Requires Root `position=\"relative\"`. */\n stackable?: boolean;\n titleText?: ReactNode;\n}\n\nexport const NotificationContent = ({\n action,\n colorVariant,\n dismissible,\n messageText,\n onClose,\n stackable,\n titleText,\n}: NotificationContentProps) => (\n <>\n {/* content + action share a wrapping row so the action drops below the text on narrow widths;\n when stacking, the row switches to a column below tablet */}\n <Box\n display=\"flex\"\n flexDirection={stackable ? { base: 'column', tablet: 'row' } : undefined}\n flex={stackable ? { base: '0 1 0%', tablet: '1' } : '1'}\n minWidth=\"0\"\n flexWrap=\"wrap\"\n alignItems={stackable ? { base: 'flex-start', tablet: 'center' } : 'center'}\n columnGap=\"16\"\n rowGap={stackable ? { base: '20', tablet: rem(11) } : rem(11)}\n paddingInlineEnd={stackable && dismissible ? { base: '40', tablet: '0' } : undefined}\n >\n <Alert.Content minWidth=\"0\" flex={stackable ? { base: 'none', tablet: `1 1 ${rem(240)}` } : `1 1 ${rem(240)}`}>\n {titleText && <Alert.Title>{titleText}</Alert.Title>}\n <Alert.Description>{messageText}</Alert.Description>\n </Alert.Content>\n {!!action && <ActionButton action={action} colorVariant={colorVariant} />}\n </Box>\n {!!dismissible && <CloseButton colorVariant={colorVariant} onClose={onClose} stackable={stackable} />}\n </>\n);\n"],"mappings":";;;;;;;AA4BA,IAAM,gBAAgB,EACpB,QACA,mBAKA,oBAAC,mBAAD;CACE,IAAI,OAAO,OAAO,MAAM;CACV;CACd,GAAK,OAAO,QAAQ;EAClB,MAAM,OAAO;EACb,QAAQ,OAAO;EACf,KAAK,OAAO,WAAW,WAAW,wBAAwB,KAAA;CAC5D;CACA,SAAS,OAAO;CAChB,aAAa,IAAI,EAAE;CACnB,iBAAgB;CAChB,YAAW;WAEV,OAAO;AACS,CAAA;AAGrB,IAAM,eAAe,EACnB,cACA,SACA,gBAMA,oBAAC,mBAAD;CACE,WAAU;CACV,MAAK;CACL,SAAS;CACK;CAGd,UAAU,YAAY;EAAE,MAAM;EAAY,QAAQ;CAAS,IAAI,KAAA;CAC/D,iBAAiB,YAAY,MAAM,KAAA;CACnC,gBAAgB,YAAY,MAAM,KAAA;CAClC,aAAa;EAAE,MAAM,YAAY,MAAM,IAAI,EAAE;EAAG,QAAQ,IAAI,EAAE;CAAE;AACjE,CAAA;AAcH,IAAa,uBAAuB,EAClC,QACA,cACA,aACA,aACA,SACA,WACA,gBAEA,qBAAA,UAAA,EAAA,UAAA,CAGE,qBAAC,KAAD;CACE,SAAQ;CACR,eAAe,YAAY;EAAE,MAAM;EAAU,QAAQ;CAAM,IAAI,KAAA;CAC/D,MAAM,YAAY;EAAE,MAAM;EAAU,QAAQ;CAAI,IAAI;CACpD,UAAS;CACT,UAAS;CACT,YAAY,YAAY;EAAE,MAAM;EAAc,QAAQ;CAAS,IAAI;CACnE,WAAU;CACV,QAAQ,YAAY;EAAE,MAAM;EAAM,QAAQ,IAAI,EAAE;CAAE,IAAI,IAAI,EAAE;CAC5D,kBAAkB,aAAa,cAAc;EAAE,MAAM;EAAM,QAAQ;CAAI,IAAI,KAAA;WAT7E,CAWE,qBAAC,MAAM,SAAP;EAAe,UAAS;EAAI,MAAM,YAAY;GAAE,MAAM;GAAQ,QAAQ,OAAO,IAAI,GAAG;EAAI,IAAI,OAAO,IAAI,GAAG;YAA1G,CACG,aAAa,oBAAC,MAAM,OAAP,EAAA,UAAc,UAAuB,CAAA,GACnD,oBAAC,MAAM,aAAP,EAAA,UAAoB,YAA+B,CAAA,CACtC;KACd,CAAC,CAAC,UAAU,oBAAC,cAAD;EAAsB;EAAsB;CAAe,CAAA,CACrE;IACJ,CAAC,CAAC,eAAe,oBAAC,aAAD;CAA2B;CAAuB;CAAoB;AAAY,CAAA,CACpG,EAAA,CAAA"}
|