@minutemailer/kit 1.2.1 → 1.2.2

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.
@@ -1,4 +1,4 @@
1
- import { type ReactNode } from 'react';
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
2
  import { type S3ImageProps } from '../components/s3-image.js';
3
3
  export interface ImageSlotAction {
4
4
  name: string;
@@ -19,6 +19,7 @@ export interface ImageSlotProps {
19
19
  loading?: boolean;
20
20
  accept?: string[];
21
21
  className?: string;
22
+ style?: CSSProperties;
22
23
  }
23
- declare function ImageSlot({ src, s3, alt, width, height, actions, onAction, onRemove, onFile, loading, accept, className, }: ImageSlotProps): import("react/jsx-runtime").JSX.Element;
24
+ declare function ImageSlot({ src, s3, alt, width, height, actions, onAction, onRemove, onFile, loading, accept, className, style, }: ImageSlotProps): import("react/jsx-runtime").JSX.Element;
24
25
  export { ImageSlot };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useRef, useState } from 'react';
2
+ import { useRef, useState, } from 'react';
3
3
  import { cn } from '../utils/utils.js';
4
4
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../components/ui/dropdown-menu.js';
5
5
  import { Button } from '../components/ui/button.js';
@@ -7,11 +7,12 @@ import { Spinner } from '../components/ui/spinner.js';
7
7
  import { toast } from 'sonner';
8
8
  import { ChevronDownIcon, ImageIcon, Trash2Icon } from 'lucide-react';
9
9
  import { S3Image } from '../components/s3-image.js';
10
- function ImageSlot({ src, s3, alt = '', width, height, actions = [], onAction, onRemove, onFile, loading = false, accept = ['image/gif', 'image/png', 'image/jpeg'], className, }) {
10
+ function ImageSlot({ src, s3, alt = '', width, height, actions = [], onAction, onRemove, onFile, loading = false, accept = ['image/gif', 'image/png', 'image/jpeg'], className, style, }) {
11
11
  const aspectRatio = width / height;
12
12
  const [dragState, setDragState] = useState('idle');
13
13
  const dragCounterRef = useRef(0);
14
14
  const fileInputRef = useRef(null);
15
+ const [s3ImageData, setS3ImageData] = useState(null);
15
16
  const handleAction = (action) => {
16
17
  onAction?.(action);
17
18
  };
@@ -78,8 +79,22 @@ function ImageSlot({ src, s3, alt = '', width, height, actions = [], onAction, o
78
79
  });
79
80
  }
80
81
  };
82
+ const onS3ImageLoaded = (result) => {
83
+ s3?.onLoaded?.(result);
84
+ setS3ImageData(result);
85
+ };
86
+ const handleRemove = () => {
87
+ onRemove?.();
88
+ setS3ImageData(null);
89
+ };
90
+ const containerWidth = s3ImageData ? s3ImageData.width : width;
91
+ const containerHeight = s3ImageData ? s3ImageData.height : height;
81
92
  if (src || s3) {
82
- return (_jsxs("div", { className: cn('relative group', className), style: { width, height }, children: [s3 ? (_jsx(S3Image, { ...s3, width: width, height: height, alt: alt, className: "block w-full h-full object-cover" })) : (_jsx("img", { src: src, alt: alt, width: width, height: height, className: "block w-full h-full object-cover" })), onRemove && (_jsx("div", { className: "absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center", children: _jsx(Button, { variant: "outline", className: "rounded-full", size: "icon", onClick: onRemove, children: _jsx(Trash2Icon, {}) }) }))] }));
93
+ return (_jsxs("div", { className: cn('relative group', className), style: {
94
+ width: containerWidth,
95
+ height: containerHeight,
96
+ ...style,
97
+ }, children: [s3 ? (_jsx(S3Image, { ...s3, width: width, height: height, onLoaded: onS3ImageLoaded, alt: alt, className: "block w-full h-full object-cover" })) : (_jsx("img", { src: src, alt: alt, width: width, height: height, className: "block w-full h-full object-cover" })), onRemove && (_jsx("div", { className: "absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center", children: _jsx(Button, { variant: "outline", className: "rounded-full", size: "icon", onClick: handleRemove, children: _jsx(Trash2Icon, {}) }) }))] }));
83
98
  }
84
99
  // Placeholder view
85
100
  return (_jsx("div", { "data-slot": "image-slot", className: cn('bg-muted flex items-center justify-center @container transition-colors', dragState === 'over' && 'bg-green-100 dark:bg-green-950', className), style: {
@@ -87,6 +102,7 @@ function ImageSlot({ src, s3, alt = '', width, height, actions = [], onAction, o
87
102
  maxHeight: height,
88
103
  maxWidth: '100%',
89
104
  height: width / aspectRatio,
105
+ ...style,
90
106
  }, onDragEnter: handleDragEnter, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: loading ? (_jsx(Spinner, { className: "size-8" })) : dragState === 'over' ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Drop image to upload" })) : (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", accept: accept.join(','), onChange: handleFileInputChange, className: "hidden" }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", children: [_jsx(ImageIcon, { className: "@xs:hidden" }), _jsx("span", { className: "hidden @xs:inline", children: "Add image" }), _jsx(ChevronDownIcon, { className: "hidden @xs:inline" })] }) }), _jsx(DropdownMenuContent, { children: actions.map((action) => (_jsxs(DropdownMenuItem, { onClick: () => handleActionClick(action), children: [action.icon, action.name] }, action.value))) })] })] })) }));
91
107
  }
92
108
  export { ImageSlot };
@@ -1,5 +1,5 @@
1
1
  import { type ImgHTMLAttributes } from 'react';
2
- import type { FitOptions, ImageEdits, OverlayOptions } from '../utils/s3Image/types.js';
2
+ import type { FitOptions, ImageEdits, OverlayOptions, S3ImageResult } from '../utils/s3Image/types.js';
3
3
  export interface S3ImageProps extends ImgHTMLAttributes<HTMLImageElement> {
4
4
  imageKey: string;
5
5
  width: number | string | 'auto';
@@ -9,7 +9,7 @@ export interface S3ImageProps extends ImgHTMLAttributes<HTMLImageElement> {
9
9
  overlayWith?: OverlayOptions;
10
10
  edits?: ImageEdits;
11
11
  containerClassName?: string;
12
- onLoaded?: () => void;
12
+ onLoaded?: (result: S3ImageResult) => void;
13
13
  onError?: () => void;
14
14
  }
15
15
  declare function S3Image({ imageKey, width, height, fit, retina, overlayWith, edits, containerClassName, className, alt, onLoaded, onError, ...imgProps }: S3ImageProps): import("react/jsx-runtime").JSX.Element | null;
@@ -31,7 +31,7 @@ function S3Image({ imageKey, width, height = 0, fit = 'inside', retina = true, o
31
31
  .then((result) => {
32
32
  setImageData(result);
33
33
  setLoading(false);
34
- onLoaded?.();
34
+ onLoaded?.(result);
35
35
  })
36
36
  .catch(() => {
37
37
  setError(true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minutemailer/kit",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Minutemailer UI Kit",
5
5
  "license": "MIT",
6
6
  "author": "Minutemailer",