@seed-design/react-image 0.1.0
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/lib/Image-12s-BLdJ3cIN.js +151 -0
- package/lib/Image-12s-LZ-Fna1t.cjs +155 -0
- package/lib/index.cjs +15 -0
- package/lib/index.d.ts +1183 -0
- package/lib/index.js +11 -0
- package/package.json +48 -0
- package/src/Image.namespace.ts +8 -0
- package/src/Image.tsx +66 -0
- package/src/index.ts +19 -0
- package/src/useImage.ts +101 -0
- package/src/useImageContext.tsx +17 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { I as ImageContent, a as ImageFallback, b as ImageRoot } from './Image-12s-BLdJ3cIN.js';
|
|
2
|
+
export { c as useImage, u as useImageContext } from './Image-12s-BLdJ3cIN.js';
|
|
3
|
+
|
|
4
|
+
var Image_namespace = {
|
|
5
|
+
__proto__: null,
|
|
6
|
+
Content: ImageContent,
|
|
7
|
+
Fallback: ImageFallback,
|
|
8
|
+
Root: ImageRoot
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export { Image_namespace as Image, ImageContent, ImageFallback, ImageRoot };
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seed-design/react-image",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/daangn/seed-design.git",
|
|
7
|
+
"directory": "packages/react-headless/image"
|
|
8
|
+
},
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"type": "module",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./lib/index.d.ts",
|
|
14
|
+
"import": "./lib/index.js",
|
|
15
|
+
"require": "./lib/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"main": "./lib/index.cjs",
|
|
20
|
+
"files": [
|
|
21
|
+
"lib",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"clean": "rm -rf lib",
|
|
26
|
+
"build": "bunchee",
|
|
27
|
+
"lint:publish": "bun publint"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@radix-ui/react-compose-refs": "^1.1.2",
|
|
31
|
+
"@radix-ui/react-use-callback-ref": "^1.1.1",
|
|
32
|
+
"@radix-ui/react-use-layout-effect": "^1.1.1",
|
|
33
|
+
"@seed-design/dom-utils": "1.0.0",
|
|
34
|
+
"@seed-design/react-primitive": "1.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/react": "^19.1.6",
|
|
38
|
+
"react": "^19.1.0",
|
|
39
|
+
"react-dom": "^19.1.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": ">=18.0.0",
|
|
43
|
+
"react-dom": ">=18.0.0"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/Image.tsx
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { composeRefs } from "@radix-ui/react-compose-refs";
|
|
4
|
+
import { useLayoutEffect } from "@radix-ui/react-use-layout-effect";
|
|
5
|
+
import { mergeProps } from "@seed-design/dom-utils";
|
|
6
|
+
import { Primitive, type PrimitiveProps } from "@seed-design/react-primitive";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { forwardRef } from "react";
|
|
9
|
+
import { useImage, type UseImageProps } from "./useImage";
|
|
10
|
+
import { ImageProvider, useImageContext } from "./useImageContext";
|
|
11
|
+
|
|
12
|
+
export interface ImageRootProps
|
|
13
|
+
extends UseImageProps,
|
|
14
|
+
PrimitiveProps,
|
|
15
|
+
React.HTMLAttributes<HTMLDivElement> {}
|
|
16
|
+
|
|
17
|
+
export const ImageRoot = forwardRef<HTMLDivElement, ImageRootProps>((props, ref) => {
|
|
18
|
+
const { onLoadingStatusChange, ...otherProps } = props;
|
|
19
|
+
const api = useImage({ onLoadingStatusChange });
|
|
20
|
+
return (
|
|
21
|
+
<ImageProvider value={api}>
|
|
22
|
+
<Primitive.div ref={ref} {...mergeProps(api.rootProps, otherProps)} />
|
|
23
|
+
</ImageProvider>
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
ImageRoot.displayName = "ImageRoot";
|
|
27
|
+
|
|
28
|
+
export interface ImageContentProps
|
|
29
|
+
extends PrimitiveProps,
|
|
30
|
+
React.ImgHTMLAttributes<HTMLImageElement> {}
|
|
31
|
+
|
|
32
|
+
export const ImageContent = forwardRef<HTMLImageElement, ImageContentProps>((props, ref) => {
|
|
33
|
+
const { src, onLoad, onError, ...otherProps } = props;
|
|
34
|
+
|
|
35
|
+
const { refs, setSrc, getContentProps, handleLoad, handleError } = useImageContext();
|
|
36
|
+
|
|
37
|
+
useLayoutEffect(() => {
|
|
38
|
+
setSrc(src);
|
|
39
|
+
}, [src, setSrc]);
|
|
40
|
+
|
|
41
|
+
const contentProps = getContentProps({ src });
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Primitive.img
|
|
45
|
+
ref={composeRefs(refs.image, ref)}
|
|
46
|
+
{...mergeProps(contentProps, otherProps)}
|
|
47
|
+
onLoad={(e) => {
|
|
48
|
+
handleLoad();
|
|
49
|
+
onLoad?.(e);
|
|
50
|
+
}}
|
|
51
|
+
onError={(e) => {
|
|
52
|
+
handleError();
|
|
53
|
+
onError?.(e);
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
ImageContent.displayName = "ImageContent";
|
|
59
|
+
|
|
60
|
+
export interface ImageFallbackProps extends PrimitiveProps, React.HTMLAttributes<HTMLDivElement> {}
|
|
61
|
+
|
|
62
|
+
export const ImageFallback = forwardRef<HTMLDivElement, ImageFallbackProps>((props, ref) => {
|
|
63
|
+
const { fallbackProps } = useImageContext();
|
|
64
|
+
return <Primitive.div ref={ref} {...mergeProps(fallbackProps, props)} />;
|
|
65
|
+
});
|
|
66
|
+
ImageFallback.displayName = "ImageFallback";
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ImageRoot,
|
|
3
|
+
ImageContent,
|
|
4
|
+
ImageFallback,
|
|
5
|
+
type ImageRootProps,
|
|
6
|
+
type ImageContentProps,
|
|
7
|
+
type ImageFallbackProps,
|
|
8
|
+
} from "./Image";
|
|
9
|
+
|
|
10
|
+
export { useImageContext, type UseImageContext } from "./useImageContext";
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
useImage,
|
|
14
|
+
type UseImageProps,
|
|
15
|
+
type UseImageReturn,
|
|
16
|
+
type ImageLoadingStatus,
|
|
17
|
+
} from "./useImage";
|
|
18
|
+
|
|
19
|
+
export * as Image from "./Image.namespace";
|
package/src/useImage.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
|
|
2
|
+
import { useLayoutEffect } from "@radix-ui/react-use-layout-effect";
|
|
3
|
+
import { dataAttr, elementProps, imgProps } from "@seed-design/dom-utils";
|
|
4
|
+
import { useCallback, useMemo, useRef, useState } from "react";
|
|
5
|
+
|
|
6
|
+
export type ImageLoadingStatus = "loading" | "loaded" | "error";
|
|
7
|
+
|
|
8
|
+
export interface UseImageProps {
|
|
9
|
+
onLoadingStatusChange?: (status: ImageLoadingStatus) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type UseImageReturn = ReturnType<typeof useImage>;
|
|
13
|
+
|
|
14
|
+
export function useImage(props: UseImageProps) {
|
|
15
|
+
const onLoadingStatusChange = useCallbackRef(props.onLoadingStatusChange);
|
|
16
|
+
const [loadingStatus, setLoadingStatus] = useState<ImageLoadingStatus>("loading");
|
|
17
|
+
const imageRef = useRef<HTMLImageElement>(null);
|
|
18
|
+
|
|
19
|
+
useLayoutEffect(() => {
|
|
20
|
+
if (imageRef.current) {
|
|
21
|
+
if (imageRef.current.complete) {
|
|
22
|
+
if (imageRef.current.naturalWidth === 0 || imageRef.current.naturalHeight === 0) {
|
|
23
|
+
setLoadingStatus("error");
|
|
24
|
+
onLoadingStatusChange?.("error");
|
|
25
|
+
} else {
|
|
26
|
+
setLoadingStatus("loaded");
|
|
27
|
+
onLoadingStatusChange?.("loaded");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}, [onLoadingStatusChange]);
|
|
32
|
+
|
|
33
|
+
const isLoaded = loadingStatus === "loaded";
|
|
34
|
+
|
|
35
|
+
const stateProps = useMemo(
|
|
36
|
+
() =>
|
|
37
|
+
elementProps({
|
|
38
|
+
"data-loading-state": loadingStatus,
|
|
39
|
+
}),
|
|
40
|
+
[loadingStatus],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const setSrc = useCallback(
|
|
44
|
+
(src: string | undefined) => {
|
|
45
|
+
if (src === undefined || src === null) {
|
|
46
|
+
setLoadingStatus("error");
|
|
47
|
+
onLoadingStatusChange?.("error");
|
|
48
|
+
} else {
|
|
49
|
+
setLoadingStatus("loading");
|
|
50
|
+
onLoadingStatusChange?.("loading");
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
[onLoadingStatusChange],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const getContentProps = useCallback(
|
|
57
|
+
({ src }: { src?: string }) => {
|
|
58
|
+
return imgProps({
|
|
59
|
+
hidden: !isLoaded,
|
|
60
|
+
"data-visible": dataAttr(isLoaded),
|
|
61
|
+
src,
|
|
62
|
+
...stateProps,
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
[isLoaded, stateProps],
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const handleLoad = useCallback(() => {
|
|
69
|
+
setLoadingStatus("loaded");
|
|
70
|
+
onLoadingStatusChange?.("loaded");
|
|
71
|
+
}, [onLoadingStatusChange]);
|
|
72
|
+
|
|
73
|
+
const handleError = useCallback(() => {
|
|
74
|
+
setLoadingStatus("error");
|
|
75
|
+
onLoadingStatusChange?.("error");
|
|
76
|
+
}, [onLoadingStatusChange]);
|
|
77
|
+
|
|
78
|
+
const fallbackProps = useMemo(
|
|
79
|
+
() =>
|
|
80
|
+
elementProps({
|
|
81
|
+
hidden: isLoaded,
|
|
82
|
+
"data-visible": dataAttr(!isLoaded),
|
|
83
|
+
...stateProps,
|
|
84
|
+
}),
|
|
85
|
+
[isLoaded, stateProps],
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
refs: {
|
|
90
|
+
image: imageRef,
|
|
91
|
+
},
|
|
92
|
+
loadingStatus,
|
|
93
|
+
stateProps,
|
|
94
|
+
rootProps: stateProps,
|
|
95
|
+
setSrc,
|
|
96
|
+
getContentProps,
|
|
97
|
+
handleLoad,
|
|
98
|
+
handleError,
|
|
99
|
+
fallbackProps,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
import type { UseImageReturn } from "./useImage";
|
|
3
|
+
|
|
4
|
+
export interface UseImageContext extends UseImageReturn {}
|
|
5
|
+
|
|
6
|
+
const ImageContext = createContext<UseImageContext | null>(null);
|
|
7
|
+
|
|
8
|
+
export const ImageProvider = ImageContext.Provider;
|
|
9
|
+
|
|
10
|
+
export function useImageContext({ strict = true }: { strict?: boolean } = {}): UseImageContext {
|
|
11
|
+
const context = useContext(ImageContext);
|
|
12
|
+
if (!context && strict) {
|
|
13
|
+
throw new Error("useImageContext must be used within an Image");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return context as UseImageContext;
|
|
17
|
+
}
|