@son426/vite-image 0.1.0 → 0.1.1
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 +13 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/plugin/index.d.ts +5 -5
- package/dist/plugin/index.js +2 -2
- package/dist/plugin/index.js.map +1 -1
- package/dist/react/index.d.ts +3 -3
- package/dist/react/index.js +3 -3
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @son426/vite-image
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Next.js 수준의 Image 컴포넌트를 Vite에서 제공합니다.**
|
|
4
|
+
|
|
5
|
+
정적 번들러인 Vite에서 Next.js와 유사한 스펙의 Image 컴포넌트를 구현한 것이 이 라이브러리의 핵심 철학입니다. `vite.config.ts`에 `viteImage()`를 추가하고, `<Image />` 컴포넌트를 바로 활용할 수 있습니다.
|
|
4
6
|
|
|
5
7
|
## Features
|
|
6
8
|
|
|
@@ -22,7 +24,7 @@ yarn add @son426/vite-image
|
|
|
22
24
|
|
|
23
25
|
## Peer Dependencies
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
다음 패키지들이 필요합니다:
|
|
26
28
|
|
|
27
29
|
```bash
|
|
28
30
|
pnpm add react vite vite-imagetools
|
|
@@ -32,15 +34,15 @@ pnpm add react vite vite-imagetools
|
|
|
32
34
|
|
|
33
35
|
### 1. Setup Vite Plugin
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
`vite.config.ts`에 플러그인을 추가하세요:
|
|
36
38
|
|
|
37
39
|
```typescript
|
|
38
40
|
import { defineConfig } from "vite";
|
|
39
|
-
import {
|
|
41
|
+
import { viteImage } from "@son426/vite-image/plugin";
|
|
40
42
|
|
|
41
43
|
export default defineConfig({
|
|
42
44
|
plugins: [
|
|
43
|
-
|
|
45
|
+
viteImage({
|
|
44
46
|
// Optional: vite-imagetools options
|
|
45
47
|
defaultDirectives: (url) => {
|
|
46
48
|
if (url.searchParams.has("webp")) {
|
|
@@ -56,7 +58,7 @@ export default defineConfig({
|
|
|
56
58
|
### 2. Use the Component
|
|
57
59
|
|
|
58
60
|
```typescript
|
|
59
|
-
import {
|
|
61
|
+
import { Image } from "@son426/vite-image/react";
|
|
60
62
|
|
|
61
63
|
// Import optimized images
|
|
62
64
|
import imageSrcSet from "@/assets/image.webp?w=640;1024;1920&format=webp&as=srcset";
|
|
@@ -65,7 +67,7 @@ import imageLqipSrc from "@/assets/image.webp?w=20&blur=2&quality=20&format=webp
|
|
|
65
67
|
|
|
66
68
|
function MyComponent() {
|
|
67
69
|
return (
|
|
68
|
-
<
|
|
70
|
+
<Image
|
|
69
71
|
src={imageMeta.src}
|
|
70
72
|
srcSet={imageSrcSet}
|
|
71
73
|
lqipSrc={imageLqipSrc}
|
|
@@ -81,11 +83,11 @@ function MyComponent() {
|
|
|
81
83
|
|
|
82
84
|
### Fill Mode
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
컨테이너를 채우는 이미지 (Next.js Image와 유사):
|
|
85
87
|
|
|
86
88
|
```typescript
|
|
87
89
|
<div style={{ position: "relative", width: "100%", height: "400px" }}>
|
|
88
|
-
<
|
|
90
|
+
<Image
|
|
89
91
|
src={imageMeta.src}
|
|
90
92
|
srcSet={imageSrcSet}
|
|
91
93
|
lqipSrc={imageLqipSrc}
|
|
@@ -98,7 +100,7 @@ For images that fill their container (similar to Next.js Image):
|
|
|
98
100
|
|
|
99
101
|
## API
|
|
100
102
|
|
|
101
|
-
###
|
|
103
|
+
### Image Props
|
|
102
104
|
|
|
103
105
|
| Prop | Type | Required | Description |
|
|
104
106
|
| --------- | ----------------------- | -------- | ---------------------------------------------- |
|
|
@@ -119,7 +121,7 @@ For images that fill their container (similar to Next.js Image):
|
|
|
119
121
|
Type definitions are included. The package also extends vite-imagetools types for better TypeScript support:
|
|
120
122
|
|
|
121
123
|
```typescript
|
|
122
|
-
import type {
|
|
124
|
+
import type { ImageProps } from "@son426/vite-image/react";
|
|
123
125
|
```
|
|
124
126
|
|
|
125
127
|
## License
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { Image, ImageProps } from './react/index.js';
|
|
2
|
+
export { ViteImagePluginOptions, viteImage } from './plugin/index.js';
|
|
3
3
|
import 'react/jsx-runtime';
|
|
4
4
|
import 'react';
|
|
5
5
|
import 'vite';
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@ import { useState } from 'react';
|
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { imagetools } from 'vite-imagetools';
|
|
4
4
|
|
|
5
|
-
// src/react/
|
|
6
|
-
function
|
|
5
|
+
// src/react/Image.tsx
|
|
6
|
+
function Image({
|
|
7
7
|
src,
|
|
8
8
|
srcSet,
|
|
9
9
|
lqipSrc,
|
|
@@ -68,10 +68,10 @@ function CustomImage({
|
|
|
68
68
|
lqipSrc && /* @__PURE__ */ jsx("img", { src: lqipSrc, alt: "", "aria-hidden": "true", style: lqipStyle })
|
|
69
69
|
] });
|
|
70
70
|
}
|
|
71
|
-
function
|
|
71
|
+
function viteImage(options) {
|
|
72
72
|
return imagetools(options);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export {
|
|
75
|
+
export { Image, viteImage };
|
|
76
76
|
//# sourceMappingURL=index.js.map
|
|
77
77
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react/
|
|
1
|
+
{"version":3,"sources":["../src/react/Image.tsx","../src/plugin/index.ts"],"names":[],"mappings":";;;;;AAuBO,SAAS,KAAA,CAAM;AAAA,EACpB,GAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,IAAA,GAAO,KAAA;AAAA,EACP,KAAA,GAAQ,OAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,EAAe;AACb,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AAGxD,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,IACV,GAAI,CAAC,IAAA,IAAQ,KAAA,IAAS,MAAA,GAClB,EAAE,WAAA,EAAa,CAAA,EAAG,KAAK,CAAA,GAAA,EAAM,MAAM,CAAA,CAAA,KACnC;AAAC,GACP;AAGJ,EAAA,MAAM,UAAA,GAA4B;AAAA,IAChC,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,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,OAAA;AAAA,IACX,UAAA,EAAY,2BAAA;AAAA,IACZ,SAAA,EAAW,YAAA;AAAA,IACX,OAAA,EAAS,gBAAgB,CAAA,GAAI;AAAA,GAC/B;AAEA,EAAA,4BACG,KAAA,EAAA,EAAI,SAAA,EAAW,SAAA,IAAa,MAAA,EAAW,OAAO,cAAA,EAC7C,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACE,GAAG,KAAA;AAAA,QACJ,GAAA;AAAA,QACA,MAAA;AAAA,QACA,KAAA;AAAA,QACA,MAAA,EAAQ,MAAM,gBAAA,CAAiB,IAAI,CAAA;AAAA,QACnC,KAAA,EAAO;AAAA;AAAA,KACT;AAAA,IACC,OAAA,oBACC,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,OAAA,EAAS,KAAI,EAAA,EAAG,aAAA,EAAY,MAAA,EAAO,KAAA,EAAO,SAAA,EAAW;AAAA,GAAA,EAEnE,CAAA;AAEJ;ACpEO,SAAS,UACd,OAAA,EACQ;AACR,EAAA,OAAO,WAAW,OAAO,CAAA;AAC3B","file":"index.js","sourcesContent":["import { useState, type ImgHTMLAttributes, type CSSProperties } from \"react\";\n\ninterface BaseImageProps\n extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"width\" | \"height\"> {\n src: string;\n srcSet?: string;\n lqipSrc?: string;\n}\n\ninterface FillImageProps extends BaseImageProps {\n fill: true;\n width?: never;\n height?: never;\n}\n\ninterface StandardImageProps extends BaseImageProps {\n fill: false | undefined;\n width: number;\n height: number;\n}\n\nexport type ImageProps = FillImageProps | StandardImageProps;\n\nexport function Image({\n src,\n srcSet,\n lqipSrc,\n width,\n height,\n fill = false,\n sizes = \"100vw\",\n className = \"\",\n ...props\n}: ImageProps) {\n const [isImageLoaded, setIsImageLoaded] = useState(false);\n\n // 컨테이너 스타일\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 ...(!fill && width && height\n ? { aspectRatio: `${width} / ${height}` }\n : {}),\n };\n\n // 실제 이미지 스타일\n const imageStyle: 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 // LQIP 이미지 스타일\n const lqipStyle: 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 transition: \"opacity 300ms ease-in-out\",\n transform: \"scale(1.1)\",\n opacity: isImageLoaded ? 0 : 1,\n };\n\n return (\n <div className={className || undefined} style={containerStyle}>\n <img\n {...props}\n src={src}\n srcSet={srcSet}\n sizes={sizes}\n onLoad={() => setIsImageLoaded(true)}\n style={imageStyle}\n />\n {lqipSrc && (\n <img src={lqipSrc} alt=\"\" aria-hidden=\"true\" style={lqipStyle} />\n )}\n </div>\n );\n}\n","import type { Plugin } from \"vite\";\nimport { imagetools } from \"vite-imagetools\";\n\nexport type ViteImagePluginOptions = Parameters<typeof imagetools>[0];\n\n/**\n * Vite plugin for image optimization using vite-imagetools\n * \n * @param options - Options to pass to vite-imagetools\n * @returns Vite plugin\n * \n * @example\n * ```ts\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import { viteImage } from '@son426/vite-image/plugin';\n * \n * export default defineConfig({\n * plugins: [\n * viteImage({\n * defaultDirectives: (url) => {\n * if (url.searchParams.has('webp')) {\n * return new URLSearchParams('format=webp');\n * }\n * return new URLSearchParams();\n * },\n * }),\n * ],\n * });\n * ```\n */\nexport function viteImage(\n options?: ViteImagePluginOptions\n): Plugin {\n return imagetools(options);\n}\n\n"]}
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
import { imagetools } from 'vite-imagetools';
|
|
3
3
|
|
|
4
|
-
type
|
|
4
|
+
type ViteImagePluginOptions = Parameters<typeof imagetools>[0];
|
|
5
5
|
/**
|
|
6
6
|
* Vite plugin for image optimization using vite-imagetools
|
|
7
7
|
*
|
|
@@ -12,11 +12,11 @@ type CustomImagePluginOptions = Parameters<typeof imagetools>[0];
|
|
|
12
12
|
* ```ts
|
|
13
13
|
* // vite.config.ts
|
|
14
14
|
* import { defineConfig } from 'vite';
|
|
15
|
-
* import {
|
|
15
|
+
* import { viteImage } from '@son426/vite-image/plugin';
|
|
16
16
|
*
|
|
17
17
|
* export default defineConfig({
|
|
18
18
|
* plugins: [
|
|
19
|
-
*
|
|
19
|
+
* viteImage({
|
|
20
20
|
* defaultDirectives: (url) => {
|
|
21
21
|
* if (url.searchParams.has('webp')) {
|
|
22
22
|
* return new URLSearchParams('format=webp');
|
|
@@ -28,6 +28,6 @@ type CustomImagePluginOptions = Parameters<typeof imagetools>[0];
|
|
|
28
28
|
* });
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
-
declare function
|
|
31
|
+
declare function viteImage(options?: ViteImagePluginOptions): Plugin;
|
|
32
32
|
|
|
33
|
-
export { type
|
|
33
|
+
export { type ViteImagePluginOptions, viteImage };
|
package/dist/plugin/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { imagetools } from 'vite-imagetools';
|
|
2
2
|
|
|
3
3
|
// src/plugin/index.ts
|
|
4
|
-
function
|
|
4
|
+
function viteImage(options) {
|
|
5
5
|
return imagetools(options);
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export {
|
|
8
|
+
export { viteImage };
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
|
10
10
|
//# sourceMappingURL=index.js.map
|
package/dist/plugin/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugin/index.ts"],"names":[],"mappings":";;;AA+BO,SAAS,
|
|
1
|
+
{"version":3,"sources":["../../src/plugin/index.ts"],"names":[],"mappings":";;;AA+BO,SAAS,UACd,OAAA,EACQ;AACR,EAAA,OAAO,WAAW,OAAO,CAAA;AAC3B","file":"index.js","sourcesContent":["import type { Plugin } from \"vite\";\nimport { imagetools } from \"vite-imagetools\";\n\nexport type ViteImagePluginOptions = Parameters<typeof imagetools>[0];\n\n/**\n * Vite plugin for image optimization using vite-imagetools\n * \n * @param options - Options to pass to vite-imagetools\n * @returns Vite plugin\n * \n * @example\n * ```ts\n * // vite.config.ts\n * import { defineConfig } from 'vite';\n * import { viteImage } from '@son426/vite-image/plugin';\n * \n * export default defineConfig({\n * plugins: [\n * viteImage({\n * defaultDirectives: (url) => {\n * if (url.searchParams.has('webp')) {\n * return new URLSearchParams('format=webp');\n * }\n * return new URLSearchParams();\n * },\n * }),\n * ],\n * });\n * ```\n */\nexport function viteImage(\n options?: ViteImagePluginOptions\n): Plugin {\n return imagetools(options);\n}\n\n"]}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ interface StandardImageProps extends BaseImageProps {
|
|
|
16
16
|
width: number;
|
|
17
17
|
height: number;
|
|
18
18
|
}
|
|
19
|
-
type
|
|
20
|
-
declare function
|
|
19
|
+
type ImageProps = FillImageProps | StandardImageProps;
|
|
20
|
+
declare function Image({ src, srcSet, lqipSrc, width, height, fill, sizes, className, ...props }: ImageProps): react_jsx_runtime.JSX.Element;
|
|
21
21
|
|
|
22
|
-
export {
|
|
22
|
+
export { Image, type ImageProps };
|
package/dist/react/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
|
-
// src/react/
|
|
5
|
-
function
|
|
4
|
+
// src/react/Image.tsx
|
|
5
|
+
function Image({
|
|
6
6
|
src,
|
|
7
7
|
srcSet,
|
|
8
8
|
lqipSrc,
|
|
@@ -68,6 +68,6 @@ function CustomImage({
|
|
|
68
68
|
] });
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
export {
|
|
71
|
+
export { Image };
|
|
72
72
|
//# sourceMappingURL=index.js.map
|
|
73
73
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/
|
|
1
|
+
{"version":3,"sources":["../../src/react/Image.tsx"],"names":[],"mappings":";;;;AAuBO,SAAS,KAAA,CAAM;AAAA,EACpB,GAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,IAAA,GAAO,KAAA;AAAA,EACP,KAAA,GAAQ,OAAA;AAAA,EACR,SAAA,GAAY,EAAA;AAAA,EACZ,GAAG;AACL,CAAA,EAAe;AACb,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AAGxD,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,IACV,GAAI,CAAC,IAAA,IAAQ,KAAA,IAAS,MAAA,GAClB,EAAE,WAAA,EAAa,CAAA,EAAG,KAAK,CAAA,GAAA,EAAM,MAAM,CAAA,CAAA,KACnC;AAAC,GACP;AAGJ,EAAA,MAAM,UAAA,GAA4B;AAAA,IAChC,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,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,OAAA;AAAA,IACX,UAAA,EAAY,2BAAA;AAAA,IACZ,SAAA,EAAW,YAAA;AAAA,IACX,OAAA,EAAS,gBAAgB,CAAA,GAAI;AAAA,GAC/B;AAEA,EAAA,4BACG,KAAA,EAAA,EAAI,SAAA,EAAW,SAAA,IAAa,MAAA,EAAW,OAAO,cAAA,EAC7C,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACE,GAAG,KAAA;AAAA,QACJ,GAAA;AAAA,QACA,MAAA;AAAA,QACA,KAAA;AAAA,QACA,MAAA,EAAQ,MAAM,gBAAA,CAAiB,IAAI,CAAA;AAAA,QACnC,KAAA,EAAO;AAAA;AAAA,KACT;AAAA,IACC,OAAA,oBACC,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,OAAA,EAAS,KAAI,EAAA,EAAG,aAAA,EAAY,MAAA,EAAO,KAAA,EAAO,SAAA,EAAW;AAAA,GAAA,EAEnE,CAAA;AAEJ","file":"index.js","sourcesContent":["import { useState, type ImgHTMLAttributes, type CSSProperties } from \"react\";\n\ninterface BaseImageProps\n extends Omit<ImgHTMLAttributes<HTMLImageElement>, \"width\" | \"height\"> {\n src: string;\n srcSet?: string;\n lqipSrc?: string;\n}\n\ninterface FillImageProps extends BaseImageProps {\n fill: true;\n width?: never;\n height?: never;\n}\n\ninterface StandardImageProps extends BaseImageProps {\n fill: false | undefined;\n width: number;\n height: number;\n}\n\nexport type ImageProps = FillImageProps | StandardImageProps;\n\nexport function Image({\n src,\n srcSet,\n lqipSrc,\n width,\n height,\n fill = false,\n sizes = \"100vw\",\n className = \"\",\n ...props\n}: ImageProps) {\n const [isImageLoaded, setIsImageLoaded] = useState(false);\n\n // 컨테이너 스타일\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 ...(!fill && width && height\n ? { aspectRatio: `${width} / ${height}` }\n : {}),\n };\n\n // 실제 이미지 스타일\n const imageStyle: 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 // LQIP 이미지 스타일\n const lqipStyle: 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 transition: \"opacity 300ms ease-in-out\",\n transform: \"scale(1.1)\",\n opacity: isImageLoaded ? 0 : 1,\n };\n\n return (\n <div className={className || undefined} style={containerStyle}>\n <img\n {...props}\n src={src}\n srcSet={srcSet}\n sizes={sizes}\n onLoad={() => setIsImageLoaded(true)}\n style={imageStyle}\n />\n {lqipSrc && (\n <img src={lqipSrc} alt=\"\" aria-hidden=\"true\" style={lqipStyle} />\n )}\n </div>\n );\n}\n"]}
|
package/package.json
CHANGED