@son426/vite-image 0.1.2 โ†’ 0.1.4

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/README.md CHANGED
@@ -1,19 +1,47 @@
1
1
  # @son426/vite-image
2
2
 
3
- **Next.js-style Image component for Vite.**
3
+ **The Next.js `<Image />` experience, now in Vite.**
4
4
 
5
- This library provides a Next.js-like Image component experience in Vite. The core philosophy is to bring static bundler capabilities similar to Next.js Image component to Vite projects. Simply add `viteImage()` to your `vite.config.ts` and start using the `<Image />` component.
5
+ - **Bring the power of Next.js's automatic image optimization to your Vite projects.**
6
+ - **Dedicated to the Vite + React ecosystem.**
6
7
 
7
- ## Features
8
+ BSimply add the plugin to your config, and start using the `<Image />` component immediately. No complex setups, just performant images.
8
9
 
9
- - ๐Ÿ–ผ๏ธ **Optimized Images**: Automatic image optimization using vite-imagetools
10
- - ๐ŸŽจ **LQIP Support**: Low Quality Image Placeholder for smooth loading experience
11
- - ๐Ÿ“ฑ **Responsive Images**: Built-in support for srcSet and sizes attributes
12
- - ๐ŸŽฏ **TypeScript**: Full TypeScript support with type definitions
13
- - ๐Ÿš€ **Simple Setup**: Easy configuration with sensible defaults
10
+ ## โœจ Why use this?
11
+
12
+ - **โšก Next.js-like Experience**: Familiar Image API for those coming from Next.js.
13
+ - **๐Ÿ–ผ๏ธ Zero-Config Optimization**: Automatic format conversion, resizing, and compression via `vite-imagetools`.
14
+ - **๐ŸŽจ Built-in LQIP**: Automatic Low Quality Image Placeholders (blur effect) while loading.
15
+ - **๐Ÿ“ฑ Responsive Ready**: Auto-generated `srcSet` and `sizes` for all viewports.
16
+ - **๐ŸŽฏ Type-Safe**: Full TypeScript support with tight integration.
17
+
18
+ ---
19
+
20
+ ## ๐Ÿš€ Quick Look
21
+
22
+ Add it to `vite.config.ts`, and use it like this:
23
+
24
+ ```tsx
25
+ import Image from "@son426/vite-image/react";
26
+ // 1. Import with the required query
27
+ import myBg from "./assets/background.jpg?vite-image";
28
+
29
+ export default function Page() {
30
+ return (
31
+ // 2. Pass the object directly to src
32
+ <Image
33
+ src={myBg}
34
+ alt="Optimized Background"
35
+ fill // Optional: Fill parent container
36
+ />
37
+ );
38
+ }
39
+ ```
14
40
 
15
41
  ## Installation
16
42
 
43
+ Since `vite-imagetools` is handled internally, you only need to install this package.
44
+
17
45
  ```bash
18
46
  pnpm add @son426/vite-image
19
47
  # or
@@ -22,13 +50,12 @@ npm install @son426/vite-image
22
50
  yarn add @son426/vite-image
23
51
  ```
24
52
 
25
- ## Peer Dependencies
53
+ ## Requirements
26
54
 
27
- The following packages are required:
55
+ Just a standard Vite + React project.
28
56
 
29
- ```bash
30
- pnpm add react vite vite-imagetools
31
- ```
57
+ vite (>= 4.0.0)
58
+ react (>= 18.0.0)
32
59
 
33
60
  ## Usage
34
61
 
@@ -48,13 +75,11 @@ export default defineConfig({
48
75
  });
49
76
  ```
50
77
 
51
- **Important**: Use the spread operator (`...viteImage()`) to properly register both plugins.
52
-
53
78
  ### 2. Use the Component
54
79
 
55
- #### Method 1: Using `?vite-image` query (Recommended)
80
+ #### Using `?vite-image` query (Required)
56
81
 
57
- The simplest way is to use the `?vite-image` query parameter, which automatically generates all required image data:
82
+ The `?vite-image` query parameter is required and automatically generates all required image data. When using `?vite-image`, the `src` prop must be an object (not a string).
58
83
 
59
84
  ```typescript
60
85
  import Image from "@son426/vite-image/react";
@@ -63,7 +88,7 @@ import bgImage from "@/assets/image.webp?vite-image";
63
88
  function MyComponent() {
64
89
  return (
65
90
  <Image
66
- imgData={bgImage}
91
+ src={bgImage}
67
92
  fill={false}
68
93
  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 100vw, 1920px"
69
94
  alt="Description"
@@ -72,6 +97,8 @@ function MyComponent() {
72
97
  }
73
98
  ```
74
99
 
100
+ **Important**: When using `?vite-image`, the `src` prop must receive the imported object directly. String URLs are not supported for `?vite-image` imports.
101
+
75
102
  The `?vite-image` query automatically generates:
76
103
 
77
104
  - `src`: Optimized image URL
@@ -79,40 +106,13 @@ The `?vite-image` query automatically generates:
79
106
  - `lqipSrc`: Low Quality Image Placeholder (base64 inline)
80
107
  - `width` and `height`: Image dimensions
81
108
 
82
- #### Method 2: Using individual props
83
-
84
- You can also import and use individual image data:
85
-
86
- ```typescript
87
- import Image from "@son426/vite-image/react";
88
-
89
- import imageSrcSet from "@/assets/image.webp?w=640;1024;1920&format=webp&as=srcset";
90
- import imageMeta from "@/assets/image.webp?w=1920&format=webp&as=meta";
91
- import imageLqipSrc from "@/assets/image.webp?w=20&blur=2&quality=20&format=webp&inline";
92
-
93
- function MyComponent() {
94
- return (
95
- <Image
96
- src={imageMeta.src}
97
- srcSet={imageSrcSet}
98
- lqipSrc={imageLqipSrc}
99
- width={imageMeta.width}
100
- height={imageMeta.height}
101
- fill={false}
102
- sizes="(max-width: 640px) 100vw, (max-width: 1024px) 100vw, 1920px"
103
- alt="Description"
104
- />
105
- );
106
- }
107
- ```
108
-
109
109
  ### Fill Mode
110
110
 
111
111
  For images that fill their container (similar to Next.js Image):
112
112
 
113
113
  ```typescript
114
114
  <div style={{ position: "relative", width: "100%", height: "400px" }}>
115
- <Image imgData={bgImage} fill={true} sizes="100vw" alt="Description" />
115
+ <Image src={bgImage} fill={true} sizes="100vw" alt="Description" />
116
116
  </div>
117
117
  ```
118
118
 
@@ -120,21 +120,16 @@ For images that fill their container (similar to Next.js Image):
120
120
 
121
121
  ### Image Props
122
122
 
123
- | Prop | Type | Required | Description |
124
- | ----------- | --------------------- | -------- | ------------------------------------------- |
125
- | `imgData` | `ResponsiveImageData` | No\* | Image data object from `?vite-image` query |
126
- | `src` | `string` | Yes\* | Image source URL |
127
- | `srcSet` | `string` | No | Responsive image srcSet |
128
- | `lqipSrc` | `string` | No | Low Quality Image Placeholder source |
129
- | `fill` | `boolean` | No | Fill container mode (default: `false`) |
130
- | `width` | `number` | Yes\* | Image width (required when `fill={false}`) |
131
- | `height` | `number` | Yes\* | Image height (required when `fill={false}`) |
132
- | `sizes` | `string` | No | Sizes attribute (default: `"100vw"`) |
133
- | `className` | `string` | No | Additional CSS classes |
134
- | `style` | `CSSProperties` | No | Additional inline styles |
135
- | `...props` | `ImgHTMLAttributes` | No | All standard img element attributes |
136
-
137
- \* Either `imgData` or `src` (with `width` and `height` when `fill={false}`) is required.
123
+ | Prop | Type | Required | Description |
124
+ | ----------- | --------------------- | -------- | ------------------------------------------ |
125
+ | `src` | `ResponsiveImageData` | Yes | Image data object from `?vite-image` query |
126
+ | `fill` | `boolean` | No | Fill container mode (default: `false`) |
127
+ | `sizes` | `string` | No | Sizes attribute (default: `"100vw"`) |
128
+ | `className` | `string` | No | Additional CSS classes |
129
+ | `style` | `CSSProperties` | No | Additional inline styles |
130
+ | `...props` | `ImgHTMLAttributes` | No | All standard img element attributes |
131
+
132
+ **Note**: The `src` prop must be an object imported from `?vite-image` query. String URLs are not supported. The `width` and `height` are automatically extracted from the `src` object.
138
133
 
139
134
  ### ResponsiveImageData
140
135
 
@@ -12,14 +12,9 @@ interface ResponsiveImageData {
12
12
  lqipSrc?: string;
13
13
  }
14
14
 
15
- interface BaseImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "width" | "height" | "src" | "srcSet"> {
15
+ interface BaseImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "srcSet" | "width" | "height"> {
16
+ src: ResponsiveImageData;
16
17
  sizes?: string;
17
- src?: string;
18
- srcSet?: string;
19
- lqipSrc?: string;
20
- width?: number;
21
- height?: number;
22
- imgData?: ResponsiveImageData;
23
18
  }
24
19
  interface FillImageProps extends BaseImageProps {
25
20
  fill: true;
@@ -28,6 +23,7 @@ interface StandardImageProps extends BaseImageProps {
28
23
  fill?: false | undefined;
29
24
  }
30
25
  type ImageProps = FillImageProps | StandardImageProps;
31
- declare function Image({ imgData, src, srcSet, lqipSrc, width, height, fill, sizes, className, style, ...props }: ImageProps): react_jsx_runtime.JSX.Element | null;
26
+ declare function Image({ src, // ์ด์ œ ์ด src๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
27
+ fill, sizes, className, style, ...props }: ImageProps): react_jsx_runtime.JSX.Element;
32
28
 
33
29
  export { type ImageProps, type ResponsiveImageData, Image as default };
@@ -3,12 +3,8 @@ import { jsxs, jsx } from 'react/jsx-runtime';
3
3
 
4
4
  // src/react/Image.tsx
5
5
  function Image({
6
- imgData,
7
6
  src,
8
- srcSet,
9
- lqipSrc,
10
- width,
11
- height,
7
+ // ์ด์ œ ์ด src๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
12
8
  fill = false,
13
9
  sizes = "100vw",
14
10
  className = "",
@@ -16,15 +12,13 @@ function Image({
16
12
  ...props
17
13
  }) {
18
14
  const [isImageLoaded, setIsImageLoaded] = useState(false);
19
- const currentSrc = imgData?.src || src;
20
- const currentSrcSet = imgData?.srcSet || srcSet;
21
- const currentLqip = imgData?.lqipSrc || lqipSrc;
22
- const currentWidth = imgData?.width || width;
23
- const currentHeight = imgData?.height || height;
24
- if (!currentSrc) {
25
- console.warn("Image: 'src' or 'imgData' is required.");
26
- return null;
27
- }
15
+ const {
16
+ src: currentSrc,
17
+ srcSet: currentSrcSet,
18
+ lqipSrc: currentLqip,
19
+ width: currentWidth,
20
+ height: currentHeight
21
+ } = src;
28
22
  const containerStyle = fill ? {
29
23
  position: "absolute",
30
24
  top: 0,
@@ -55,14 +49,10 @@ function Image({
55
49
  const lqipStyle = {
56
50
  ...imgStyle,
57
51
  filter: "blur(20px)",
58
- // blur ํšจ๊ณผ ๊ฐ•ํ™” (์„ ํƒ์‚ฌํ•ญ)
59
52
  transform: "scale(1.1)",
60
- // blur ๊ฒฝ๊ณ„์„  ์ˆจ๊ธฐ๊ธฐ
61
53
  transition: "opacity 500ms ease-out",
62
- // ๋ถ€๋“œ๋Ÿฌ์šด ์ „ํ™˜
63
54
  opacity: isImageLoaded ? 0 : 1,
64
55
  zIndex: 1
65
- // ๋กœ๋”ฉ ์ค‘์—๋Š” ์œ„์— ํ‘œ์‹œ
66
56
  };
67
57
  return /* @__PURE__ */ jsxs("div", { className, style: mergedContainerStyle, children: [
68
58
  /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/Image.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { useState, type ImgHTMLAttributes, type CSSProperties } from \"react\";\nimport type { ResponsiveImageData } from \"../types\";\n\n// ?vite-image import ๊ฒฐ๊ณผ ํƒ€์ž…\ninterface BaseImageProps\n extends Omit<\n ImgHTMLAttributes<HTMLImageElement>,\n \"width\" | \"height\" | \"src\" | \"srcSet\"\n > {\n sizes?: string;\n src?: string;\n srcSet?: string;\n lqipSrc?: string;\n width?: number;\n height?: number;\n imgData?: ResponsiveImageData;\n}\n\ninterface FillImageProps extends BaseImageProps {\n fill: true;\n}\n\ninterface StandardImageProps extends BaseImageProps {\n fill?: false | undefined;\n}\n\nexport type ImageProps = FillImageProps | StandardImageProps;\n\nexport default function Image({\n imgData,\n src,\n srcSet,\n lqipSrc,\n width,\n height,\n fill = false,\n sizes = \"100vw\",\n className = \"\",\n style,\n ...props\n}: ImageProps) {\n const [isImageLoaded, setIsImageLoaded] = useState(false);\n\n // 1. ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ: imgData๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ๊ฑธ ์“ฐ๊ณ , ์—†์œผ๋ฉด ๊ฐœ๋ณ„ prop ์‚ฌ์šฉ\n const currentSrc = imgData?.src || src;\n const currentSrcSet = imgData?.srcSet || srcSet;\n const currentLqip = imgData?.lqipSrc || lqipSrc;\n const currentWidth = imgData?.width || width;\n const currentHeight = imgData?.height || height;\n\n if (!currentSrc) {\n console.warn(\"Image: 'src' or 'imgData' is required.\");\n return null;\n }\n\n // 2. ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ ๊ณ„์‚ฐ\n const containerStyle: CSSProperties = fill\n ? {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n }\n : {\n position: \"relative\",\n width: \"100%\",\n overflow: \"hidden\",\n // Standard ๋ชจ๋“œ์ผ ๋•Œ aspect-ratio ์ฒ˜๋ฆฌ\n ...(currentWidth && currentHeight\n ? { aspectRatio: `${currentWidth} / ${currentHeight}` }\n : {}),\n };\n\n const mergedContainerStyle = { ...containerStyle, ...style };\n\n // 3. ์‹ค์ œ ์ด๋ฏธ์ง€ ์Šคํƒ€์ผ\n const imgStyle: CSSProperties = {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n };\n\n // 4. LQIP ์Šคํƒ€์ผ\n const lqipStyle: CSSProperties = {\n ...imgStyle,\n filter: \"blur(20px)\", // blur ํšจ๊ณผ ๊ฐ•ํ™” (์„ ํƒ์‚ฌํ•ญ)\n transform: \"scale(1.1)\", // blur ๊ฒฝ๊ณ„์„  ์ˆจ๊ธฐ๊ธฐ\n transition: \"opacity 500ms ease-out\", // ๋ถ€๋“œ๋Ÿฌ์šด ์ „ํ™˜\n opacity: isImageLoaded ? 0 : 1,\n zIndex: 1, // ๋กœ๋”ฉ ์ค‘์—๋Š” ์œ„์— ํ‘œ์‹œ\n };\n\n return (\n <div className={className} style={mergedContainerStyle}>\n {/* ์‹ค์ œ ์ด๋ฏธ์ง€ */}\n <img\n {...props}\n src={currentSrc}\n srcSet={currentSrcSet}\n sizes={sizes}\n width={fill ? undefined : currentWidth}\n height={fill ? undefined : currentHeight}\n onLoad={() => setIsImageLoaded(true)}\n style={{ ...imgStyle, zIndex: 0 }}\n />\n\n {/* LQIP ๋ ˆ์ด์–ด */}\n {currentLqip && (\n <img src={currentLqip} alt=\"\" aria-hidden=\"true\" style={lqipStyle} />\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../../src/react/Image.tsx"],"names":[],"mappings":";;;;AAwBe,SAAR,KAAA,CAAuB;AAAA,EAC5B,GAAA;AAAA;AAAA,EACA,IAAA,GAAO,KAAA;AAAA,EACP,KAAA,GAAQ,OAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,KAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAe;AACb,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AAGxD,EAAA,MAAM;AAAA,IACJ,GAAA,EAAK,UAAA;AAAA,IACL,MAAA,EAAQ,aAAA;AAAA,IACR,OAAA,EAAS,WAAA;AAAA,IACT,KAAA,EAAO,YAAA;AAAA,IACP,MAAA,EAAQ;AAAA,GACV,GAAI,GAAA;AAGJ,EAAA,MAAM,iBAAgC,IAAA,GAClC;AAAA,IACE,QAAA,EAAU,UAAA;AAAA,IACV,GAAA,EAAK,CAAA;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA,EAAQ,CAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,QAAA,EAAU;AAAA,GACZ,GACA;AAAA,IACE,QAAA,EAAU,UAAA;AAAA,IACV,KAAA,EAAO,MAAA;AAAA,IACP,QAAA,EAAU,QAAA;AAAA;AAAA,IAEV,GAAI,YAAA,IAAgB,aAAA,GAChB,EAAE,WAAA,EAAa,CAAA,EAAG,YAAY,CAAA,GAAA,EAAM,aAAa,CAAA,CAAA,EAAG,GACpD;AAAC,GACP;AAEJ,EAAA,MAAM,oBAAA,GAAuB,EAAE,GAAG,cAAA,EAAgB,GAAG,KAAA,EAAM;AAG3D,EAAA,MAAM,QAAA,GAA0B;AAAA,IAC9B,QAAA,EAAU,UAAA;AAAA,IACV,GAAA,EAAK,CAAA;AAAA,IACL,IAAA,EAAM,CAAA;AAAA,IACN,KAAA,EAAO,CAAA;AAAA,IACP,MAAA,EAAQ,CAAA;AAAA,IACR,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MAAA;AAAA,IACR,SAAA,EAAW;AAAA,GACb;AAGA,EAAA,MAAM,SAAA,GAA2B;AAAA,IAC/B,GAAG,QAAA;AAAA,IACH,MAAA,EAAQ,YAAA;AAAA,IACR,SAAA,EAAW,YAAA;AAAA,IACX,UAAA,EAAY,wBAAA;AAAA,IACZ,OAAA,EAAS,gBAAgB,CAAA,GAAI,CAAA;AAAA,IAC7B,MAAA,EAAQ;AAAA,GACV;AAEA,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAsB,KAAA,EAAO,oBAAA,EAEhC,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACE,GAAG,KAAA;AAAA,QACJ,GAAA,EAAK,UAAA;AAAA,QACL,MAAA,EAAQ,aAAA;AAAA,QACR,KAAA;AAAA,QACA,KAAA,EAAO,OAAO,MAAA,GAAY,YAAA;AAAA,QAC1B,MAAA,EAAQ,OAAO,MAAA,GAAY,aAAA;AAAA,QAC3B,MAAA,EAAQ,MAAM,gBAAA,CAAiB,IAAI,CAAA;AAAA,QACnC,KAAA,EAAO,EAAE,GAAG,QAAA,EAAU,QAAQ,CAAA;AAAE;AAAA,KAClC;AAAA,IAGC,WAAA,oBACC,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,WAAA,EAAa,KAAI,EAAA,EAAG,aAAA,EAAY,MAAA,EAAO,KAAA,EAAO,SAAA,EAAW;AAAA,GAAA,EAEvE,CAAA;AAEJ","file":"index.js","sourcesContent":["import { useState, type ImgHTMLAttributes, type CSSProperties } from \"react\";\nimport type { ResponsiveImageData } from \"../types\";\n\n// ?vite-image import ๊ฒฐ๊ณผ ํƒ€์ž…\ninterface BaseImageProps\n extends Omit<\n ImgHTMLAttributes<HTMLImageElement>,\n \"src\" | \"srcSet\" | \"width\" | \"height\"\n > {\n // ํ•ต์‹ฌ ๋ณ€๊ฒฝ: src๋Š” ๋ฌด์กฐ๊ฑด ์ตœ์ ํ™”๋œ ์ด๋ฏธ์ง€ ๊ฐ์ฒด๋งŒ ๋ฐ›์Œ\n src: ResponsiveImageData;\n sizes?: string;\n}\n\ninterface FillImageProps extends BaseImageProps {\n fill: true;\n}\n\ninterface StandardImageProps extends BaseImageProps {\n fill?: false | undefined;\n}\n\nexport type ImageProps = FillImageProps | StandardImageProps;\n\nexport default function Image({\n src, // ์ด์ œ ์ด src๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.\n fill = false,\n sizes = \"100vw\",\n className = \"\",\n style,\n ...props\n}: ImageProps) {\n const [isImageLoaded, setIsImageLoaded] = useState(false);\n\n // 1. ๋ฐ์ดํ„ฐ ์ถ”์ถœ: src ๊ฐ์ฒด์—์„œ ๋ฐ”๋กœ ๊บผ๋‚ด์”€ (๋ณ‘ํ•ฉ ๋กœ์ง ์ œ๊ฑฐ)\n const {\n src: currentSrc,\n srcSet: currentSrcSet,\n lqipSrc: currentLqip,\n width: currentWidth,\n height: currentHeight,\n } = src;\n\n // 2. ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ ๊ณ„์‚ฐ (๊ธฐ์กด ๋กœ์ง ์œ ์ง€)\n const containerStyle: CSSProperties = fill\n ? {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n }\n : {\n position: \"relative\",\n width: \"100%\",\n overflow: \"hidden\",\n // Standard ๋ชจ๋“œ์ผ ๋•Œ aspect-ratio ์ฒ˜๋ฆฌ\n ...(currentWidth && currentHeight\n ? { aspectRatio: `${currentWidth} / ${currentHeight}` }\n : {}),\n };\n\n const mergedContainerStyle = { ...containerStyle, ...style };\n\n // 3. ์‹ค์ œ ์ด๋ฏธ์ง€ ์Šคํƒ€์ผ (๊ธฐ์กด ๋กœ์ง ์œ ์ง€)\n const imgStyle: CSSProperties = {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n };\n\n // 4. LQIP ์Šคํƒ€์ผ (๊ธฐ์กด ๋กœ์ง ์œ ์ง€)\n const lqipStyle: CSSProperties = {\n ...imgStyle,\n filter: \"blur(20px)\",\n transform: \"scale(1.1)\",\n transition: \"opacity 500ms ease-out\",\n opacity: isImageLoaded ? 0 : 1,\n zIndex: 1,\n };\n\n return (\n <div className={className} style={mergedContainerStyle}>\n {/* ์‹ค์ œ ์ด๋ฏธ์ง€ */}\n <img\n {...props}\n src={currentSrc}\n srcSet={currentSrcSet}\n sizes={sizes}\n width={fill ? undefined : currentWidth}\n height={fill ? undefined : currentHeight}\n onLoad={() => setIsImageLoaded(true)}\n style={{ ...imgStyle, zIndex: 0 }}\n />\n\n {/* LQIP ๋ ˆ์ด์–ด */}\n {currentLqip && (\n <img src={currentLqip} alt=\"\" aria-hidden=\"true\" style={lqipStyle} />\n )}\n </div>\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@son426/vite-image",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "A Vite plugin and React component for optimized images with LQIP support using vite-imagetools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -47,10 +47,12 @@
47
47
  "type": "git",
48
48
  "url": ""
49
49
  },
50
+ "dependencies": {
51
+ "vite-imagetools": "^9.0.2"
52
+ },
50
53
  "peerDependencies": {
51
54
  "react": "^18.0.0 || ^19.0.0",
52
- "vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
53
- "vite-imagetools": "^9.0.0"
55
+ "vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
54
56
  },
55
57
  "devDependencies": {
56
58
  "@types/node": "^20.0.0",
@@ -58,8 +60,7 @@
58
60
  "react": "^19.1.0",
59
61
  "tsup": "^8.0.0",
60
62
  "typescript": "^5.0.0",
61
- "vite": "^7.0.4",
62
- "vite-imagetools": "^9.0.2"
63
+ "vite": "^7.0.4"
63
64
  },
64
65
  "publishConfig": {
65
66
  "access": "public"
package/types.d.ts CHANGED
@@ -5,14 +5,7 @@
5
5
  * These types extend the default vite-imagetools types
6
6
  */
7
7
 
8
- // ?vite-image import ๊ฒฐ๊ณผ ํƒ€์ž…
9
- interface ResponsiveImageData {
10
- src: string;
11
- width: number;
12
- height: number;
13
- srcSet?: string;
14
- lqipSrc?: string;
15
- }
8
+ import type { ResponsiveImageData } from "./types";
16
9
 
17
10
  // vite-image ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…
18
11
  declare module "*?vite-image" {
@@ -43,14 +36,3 @@ declare module "*?w=*&blur=*&quality=*&format=*&inline" {
43
36
  const src: string;
44
37
  export default src;
45
38
  }
46
-
47
- // ์ผ๋ฐ˜ imagetools ํ™•์žฅ
48
- declare module "*&imagetools" {
49
- type ImageToolsOutput =
50
- | string // ๊ธฐ๋ณธ ์ถœ๋ ฅ
51
- | { src: string; width: number; height: number } // meta
52
- | string; // srcset
53
-
54
- const out: ImageToolsOutput;
55
- export default out;
56
- }