@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.
@@ -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, { onCropChange })
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: "12",
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
- marginInlineEnd: "12",
28
- marginBlock: rem(-11),
29
- marginInlineStart: rem(-12),
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 variant=\"info\" border=\"none\" css={{ '& .alert__title + *': { marginBlockStart: 0 } }} {...rest}>\n {illustration ? (\n <Box flexShrink={0} marginInlineEnd=\"12\" aria-hidden>\n {illustration}\n </Box>\n ) : (\n <Icon\n asChild\n width={rem(72)}\n height={rem(68)}\n marginInlineEnd=\"12\"\n marginBlock={rem(-11)}\n marginInlineStart={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 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;EAAY,SAAQ;EAAO,QAAO;EAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,EAAE;EAAG,GAAI;YAAtG,CACG,eACC,oBAAC,KAAD;GAAK,YAAY;GAAG,iBAAgB;GAAK,eAAA;aACtC;EACE,CAAA,IAEL,oBAAC,MAAD;GACE,SAAA;GACA,OAAO,IAAI,EAAE;GACb,QAAQ,IAAI,EAAE;GACd,iBAAgB;GAChB,aAAa,IAAI,GAAG;GACpB,mBAAmB,IAAI,GAAG;aAE1B,oBAAC,YAAD,CAAY,CAAA;EACR,CAAA,GAER,oBAAC,qBAAD;GACE,QAAQ;GACR,cAAc;GACD;GACb,aAAa;GACJ;GACT,WAAW;EACZ,CAAA,CACS;;AAEhB;AAEA,kBAAkB,cAAc"}
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
- flex: "1",
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: "center",
50
+ alignItems: stackable ? {
51
+ base: "flex-start",
52
+ tablet: "center"
53
+ } : "center",
35
54
  columnGap: "16",
36
- rowGap: rem(11),
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: `1 1 ${rem(240)}`,
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 marginBlock={rem(-4)}\n size=\"sm\"\n onClick={onClose}\n colorVariant={colorVariant}\n />\n);\n\nexport interface NotificationContentProps {\n action?: NotificationAction;\n colorVariant: NotificationColorVariant;\n dismissible?: boolean;\n messageText: ReactNode;\n onClose?: () => void;\n titleText?: ReactNode;\n}\n\nexport const NotificationContent = ({\n action,\n colorVariant,\n dismissible,\n messageText,\n onClose,\n titleText,\n}: NotificationContentProps) => (\n <>\n {/* content + action share a wrapping row so the action drops below the text on narrow widths */}\n <Box display=\"flex\" flex=\"1\" minWidth=\"0\" flexWrap=\"wrap\" alignItems=\"center\" columnGap=\"16\" rowGap={rem(11)}>\n <Alert.Content minWidth=\"0\" flex={`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} />}\n </>\n);\n"],"mappings":";;;;;;;AAmBA,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,cAKA,oBAAC,mBAAD;CACE,WAAU;CACV,aAAa,IAAI,EAAE;CACnB,MAAK;CACL,SAAS;CACK;AACf,CAAA;AAYH,IAAa,uBAAuB,EAClC,QACA,cACA,aACA,aACA,SACA,gBAEA,qBAAA,UAAA,EAAA,UAAA,CAEE,qBAAC,KAAD;CAAK,SAAQ;CAAO,MAAK;CAAI,UAAS;CAAI,UAAS;CAAO,YAAW;CAAS,WAAU;CAAK,QAAQ,IAAI,EAAE;WAA3G,CACE,qBAAC,MAAM,SAAP;EAAe,UAAS;EAAI,MAAM,OAAO,IAAI,GAAG;YAAhD,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;AAAU,CAAA,CAC9E,EAAA,CAAA"}
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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bitrise/bitkit-v2",
3
3
  "private": false,
4
- "version": "0.3.283",
4
+ "version": "0.3.285",
5
5
  "description": "Bitrise Design System Components built with Chakra UI V3",
6
6
  "keywords": [
7
7
  "react",