@grapu-design/react-image 0.1.1 → 0.1.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.
@@ -0,0 +1,160 @@
1
+ 'use client';
2
+ var jsxRuntime = require('react/jsx-runtime');
3
+ var reactComposeRefs = require('@radix-ui/react-compose-refs');
4
+ var reactUseLayoutEffect = require('@radix-ui/react-use-layout-effect');
5
+ var domUtils = require('@grape-design/dom-utils');
6
+ var reactPrimitive = require('@grape-design/react-primitive');
7
+ var react = require('react');
8
+ var reactUseCallbackRef = require('@radix-ui/react-use-callback-ref');
9
+
10
+ function useImage(props) {
11
+ const onLoadingStatusChange = reactUseCallbackRef.useCallbackRef(props.onLoadingStatusChange);
12
+ const [loadingStatus, setLoadingStatus] = react.useState("loading");
13
+ const imageRef = react.useRef(null);
14
+ reactUseLayoutEffect.useLayoutEffect(()=>{
15
+ if (imageRef.current) {
16
+ if (imageRef.current.complete) {
17
+ if (imageRef.current.naturalWidth === 0 || imageRef.current.naturalHeight === 0) {
18
+ setLoadingStatus("error");
19
+ onLoadingStatusChange?.("error");
20
+ } else {
21
+ setLoadingStatus("loaded");
22
+ onLoadingStatusChange?.("loaded");
23
+ }
24
+ }
25
+ }
26
+ }, [
27
+ onLoadingStatusChange
28
+ ]);
29
+ const isLoaded = loadingStatus === "loaded";
30
+ const stateProps = react.useMemo(()=>domUtils.elementProps({
31
+ "data-loading-state": loadingStatus
32
+ }), [
33
+ loadingStatus
34
+ ]);
35
+ const setSrc = react.useCallback((src)=>{
36
+ if (src === undefined || src === null) {
37
+ setLoadingStatus("error");
38
+ onLoadingStatusChange?.("error");
39
+ } else {
40
+ setLoadingStatus("loading");
41
+ onLoadingStatusChange?.("loading");
42
+ }
43
+ }, [
44
+ onLoadingStatusChange
45
+ ]);
46
+ const getContentProps = react.useCallback(({ src })=>{
47
+ return domUtils.imgProps({
48
+ hidden: !isLoaded,
49
+ "data-visible": domUtils.dataAttr(isLoaded),
50
+ src,
51
+ ...stateProps
52
+ });
53
+ }, [
54
+ isLoaded,
55
+ stateProps
56
+ ]);
57
+ const handleLoad = react.useCallback(()=>{
58
+ setLoadingStatus("loaded");
59
+ onLoadingStatusChange?.("loaded");
60
+ }, [
61
+ onLoadingStatusChange
62
+ ]);
63
+ const handleError = react.useCallback(()=>{
64
+ setLoadingStatus("error");
65
+ onLoadingStatusChange?.("error");
66
+ }, [
67
+ onLoadingStatusChange
68
+ ]);
69
+ const fallbackProps = react.useMemo(()=>domUtils.elementProps({
70
+ hidden: isLoaded,
71
+ "data-visible": domUtils.dataAttr(!isLoaded),
72
+ ...stateProps
73
+ }), [
74
+ isLoaded,
75
+ stateProps
76
+ ]);
77
+ return {
78
+ refs: {
79
+ image: imageRef
80
+ },
81
+ loadingStatus,
82
+ stateProps,
83
+ rootProps: stateProps,
84
+ setSrc,
85
+ getContentProps,
86
+ handleLoad,
87
+ handleError,
88
+ fallbackProps
89
+ };
90
+ }
91
+
92
+ const ImageContext = /*#__PURE__*/ react.createContext(null);
93
+ const ImageProvider = ImageContext.Provider;
94
+ function useImageContext({ strict = true } = {}) {
95
+ const context = react.useContext(ImageContext);
96
+ if (!context && strict) {
97
+ throw new Error("useImageContext must be used within an Image");
98
+ }
99
+ return context;
100
+ }
101
+
102
+ const ImageRoot = /*#__PURE__*/ react.forwardRef((props, ref)=>{
103
+ const { onLoadingStatusChange, ...otherProps } = props;
104
+ const api = useImage({
105
+ onLoadingStatusChange
106
+ });
107
+ return /*#__PURE__*/ jsxRuntime.jsx(ImageProvider, {
108
+ value: api,
109
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactPrimitive.Primitive.div, {
110
+ ref: ref,
111
+ ...domUtils.mergeProps(api.rootProps, otherProps)
112
+ })
113
+ });
114
+ });
115
+ ImageRoot.displayName = "ImageRoot";
116
+ const ImageContent = /*#__PURE__*/ react.forwardRef((props, ref)=>{
117
+ const { src, onLoad, onError, ...otherProps } = props;
118
+ const { refs, setSrc, getContentProps, handleLoad, handleError } = useImageContext();
119
+ reactUseLayoutEffect.useLayoutEffect(()=>{
120
+ setSrc(src);
121
+ }, [
122
+ src,
123
+ setSrc
124
+ ]);
125
+ const contentProps = getContentProps({
126
+ src
127
+ });
128
+ return /*#__PURE__*/ jsxRuntime.jsx(reactPrimitive.Primitive.img, {
129
+ ref: reactComposeRefs.composeRefs(refs.image, ref),
130
+ ...domUtils.mergeProps(contentProps, otherProps, {
131
+ // if loading is lazy, we should not hide the image even if it's not loaded yet,
132
+ // because the browser should be able to check if it's in the viewport.
133
+ // TODO: it should be better than this; why doesn't useImage properly handle this case?
134
+ hidden: otherProps.loading === "lazy" ? false : contentProps.hidden
135
+ }),
136
+ onLoad: (e)=>{
137
+ handleLoad();
138
+ onLoad?.(e);
139
+ },
140
+ onError: (e)=>{
141
+ handleError();
142
+ onError?.(e);
143
+ }
144
+ });
145
+ });
146
+ ImageContent.displayName = "ImageContent";
147
+ const ImageFallback = /*#__PURE__*/ react.forwardRef((props, ref)=>{
148
+ const { fallbackProps } = useImageContext();
149
+ return /*#__PURE__*/ jsxRuntime.jsx(reactPrimitive.Primitive.div, {
150
+ ref: ref,
151
+ ...domUtils.mergeProps(fallbackProps, props)
152
+ });
153
+ });
154
+ ImageFallback.displayName = "ImageFallback";
155
+
156
+ exports.ImageContent = ImageContent;
157
+ exports.ImageFallback = ImageFallback;
158
+ exports.ImageRoot = ImageRoot;
159
+ exports.useImage = useImage;
160
+ exports.useImageContext = useImageContext;
@@ -0,0 +1,156 @@
1
+ 'use client';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { composeRefs } from '@radix-ui/react-compose-refs';
4
+ import { useLayoutEffect } from '@radix-ui/react-use-layout-effect';
5
+ import { elementProps, imgProps, dataAttr, mergeProps } from '@grape-design/dom-utils';
6
+ import { Primitive } from '@grape-design/react-primitive';
7
+ import { useState, useRef, useMemo, useCallback, createContext, useContext, forwardRef } from 'react';
8
+ import { useCallbackRef } from '@radix-ui/react-use-callback-ref';
9
+
10
+ function useImage(props) {
11
+ const onLoadingStatusChange = useCallbackRef(props.onLoadingStatusChange);
12
+ const [loadingStatus, setLoadingStatus] = useState("loading");
13
+ const imageRef = useRef(null);
14
+ useLayoutEffect(()=>{
15
+ if (imageRef.current) {
16
+ if (imageRef.current.complete) {
17
+ if (imageRef.current.naturalWidth === 0 || imageRef.current.naturalHeight === 0) {
18
+ setLoadingStatus("error");
19
+ onLoadingStatusChange?.("error");
20
+ } else {
21
+ setLoadingStatus("loaded");
22
+ onLoadingStatusChange?.("loaded");
23
+ }
24
+ }
25
+ }
26
+ }, [
27
+ onLoadingStatusChange
28
+ ]);
29
+ const isLoaded = loadingStatus === "loaded";
30
+ const stateProps = useMemo(()=>elementProps({
31
+ "data-loading-state": loadingStatus
32
+ }), [
33
+ loadingStatus
34
+ ]);
35
+ const setSrc = useCallback((src)=>{
36
+ if (src === undefined || src === null) {
37
+ setLoadingStatus("error");
38
+ onLoadingStatusChange?.("error");
39
+ } else {
40
+ setLoadingStatus("loading");
41
+ onLoadingStatusChange?.("loading");
42
+ }
43
+ }, [
44
+ onLoadingStatusChange
45
+ ]);
46
+ const getContentProps = useCallback(({ src })=>{
47
+ return imgProps({
48
+ hidden: !isLoaded,
49
+ "data-visible": dataAttr(isLoaded),
50
+ src,
51
+ ...stateProps
52
+ });
53
+ }, [
54
+ isLoaded,
55
+ stateProps
56
+ ]);
57
+ const handleLoad = useCallback(()=>{
58
+ setLoadingStatus("loaded");
59
+ onLoadingStatusChange?.("loaded");
60
+ }, [
61
+ onLoadingStatusChange
62
+ ]);
63
+ const handleError = useCallback(()=>{
64
+ setLoadingStatus("error");
65
+ onLoadingStatusChange?.("error");
66
+ }, [
67
+ onLoadingStatusChange
68
+ ]);
69
+ const fallbackProps = useMemo(()=>elementProps({
70
+ hidden: isLoaded,
71
+ "data-visible": dataAttr(!isLoaded),
72
+ ...stateProps
73
+ }), [
74
+ isLoaded,
75
+ stateProps
76
+ ]);
77
+ return {
78
+ refs: {
79
+ image: imageRef
80
+ },
81
+ loadingStatus,
82
+ stateProps,
83
+ rootProps: stateProps,
84
+ setSrc,
85
+ getContentProps,
86
+ handleLoad,
87
+ handleError,
88
+ fallbackProps
89
+ };
90
+ }
91
+
92
+ const ImageContext = /*#__PURE__*/ createContext(null);
93
+ const ImageProvider = ImageContext.Provider;
94
+ function useImageContext({ strict = true } = {}) {
95
+ const context = useContext(ImageContext);
96
+ if (!context && strict) {
97
+ throw new Error("useImageContext must be used within an Image");
98
+ }
99
+ return context;
100
+ }
101
+
102
+ const ImageRoot = /*#__PURE__*/ forwardRef((props, ref)=>{
103
+ const { onLoadingStatusChange, ...otherProps } = props;
104
+ const api = useImage({
105
+ onLoadingStatusChange
106
+ });
107
+ return /*#__PURE__*/ jsx(ImageProvider, {
108
+ value: api,
109
+ children: /*#__PURE__*/ jsx(Primitive.div, {
110
+ ref: ref,
111
+ ...mergeProps(api.rootProps, otherProps)
112
+ })
113
+ });
114
+ });
115
+ ImageRoot.displayName = "ImageRoot";
116
+ const ImageContent = /*#__PURE__*/ forwardRef((props, ref)=>{
117
+ const { src, onLoad, onError, ...otherProps } = props;
118
+ const { refs, setSrc, getContentProps, handleLoad, handleError } = useImageContext();
119
+ useLayoutEffect(()=>{
120
+ setSrc(src);
121
+ }, [
122
+ src,
123
+ setSrc
124
+ ]);
125
+ const contentProps = getContentProps({
126
+ src
127
+ });
128
+ return /*#__PURE__*/ jsx(Primitive.img, {
129
+ ref: composeRefs(refs.image, ref),
130
+ ...mergeProps(contentProps, otherProps, {
131
+ // if loading is lazy, we should not hide the image even if it's not loaded yet,
132
+ // because the browser should be able to check if it's in the viewport.
133
+ // TODO: it should be better than this; why doesn't useImage properly handle this case?
134
+ hidden: otherProps.loading === "lazy" ? false : contentProps.hidden
135
+ }),
136
+ onLoad: (e)=>{
137
+ handleLoad();
138
+ onLoad?.(e);
139
+ },
140
+ onError: (e)=>{
141
+ handleError();
142
+ onError?.(e);
143
+ }
144
+ });
145
+ });
146
+ ImageContent.displayName = "ImageContent";
147
+ const ImageFallback = /*#__PURE__*/ forwardRef((props, ref)=>{
148
+ const { fallbackProps } = useImageContext();
149
+ return /*#__PURE__*/ jsx(Primitive.div, {
150
+ ref: ref,
151
+ ...mergeProps(fallbackProps, props)
152
+ });
153
+ });
154
+ ImageFallback.displayName = "ImageFallback";
155
+
156
+ export { ImageContent as I, ImageFallback as a, ImageRoot as b, useImageContext as c, useImage as u };
package/lib/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- var Image12s = require('./Image-12s-O0sy0c4i.cjs');
1
+ var Image12s = require('./Image-12s-EMlItKgm.cjs');
2
2
 
3
3
  var Image_namespace = {
4
4
  __proto__: null,
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { PrimitiveProps } from '@grapu-design/react-primitive';
1
+ import { PrimitiveProps } from '@grape-design/react-primitive';
2
2
  import * as react from 'react';
3
3
 
4
4
  type ImageLoadingStatus = "loading" | "loaded" | "error";
@@ -551,7 +551,6 @@ declare function useImage(props: UseImageProps): {
551
551
  getContentProps: ({ src }: {
552
552
  src?: string;
553
553
  }) => {
554
- loading?: "eager" | "lazy" | undefined;
555
554
  defaultChecked?: boolean | undefined;
556
555
  defaultValue?: string | number | readonly string[] | undefined;
557
556
  suppressContentEditableWarning?: boolean | undefined;
@@ -818,14 +817,15 @@ declare function useImage(props: UseImageProps): {
818
817
  onAnimationIterationCapture?: react.AnimationEventHandler<HTMLImageElement>;
819
818
  onTransitionEnd?: react.TransitionEventHandler<HTMLImageElement>;
820
819
  onTransitionEndCapture?: react.TransitionEventHandler<HTMLImageElement>;
821
- src?: string | undefined;
822
820
  alt?: string | undefined;
823
821
  crossOrigin?: "" | "anonymous" | "use-credentials";
824
822
  decoding?: "async" | "auto" | "sync" | undefined;
825
823
  fetchPriority?: "high" | "low" | "auto";
826
824
  height?: number | string | undefined;
825
+ loading?: "eager" | "lazy" | undefined;
827
826
  referrerPolicy?: react.HTMLAttributeReferrerPolicy | undefined;
828
827
  sizes?: string | undefined;
828
+ src?: string | undefined;
829
829
  srcSet?: string | undefined;
830
830
  useMap?: string | undefined;
831
831
  width?: number | string | undefined;
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { I as ImageContent, a as ImageFallback, b as ImageRoot } from './Image-12s-SR1fYJF4.js';
2
- export { u as useImage, c as useImageContext } from './Image-12s-SR1fYJF4.js';
1
+ import { I as ImageContent, a as ImageFallback, b as ImageRoot } from './Image-12s-fNttHZ8N.js';
2
+ export { u as useImage, c as useImageContext } from './Image-12s-fNttHZ8N.js';
3
3
 
4
4
  var Image_namespace = {
5
5
  __proto__: null,
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@grapu-design/react-image",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "repository": {
5
5
  "type": "git",
6
- "url": "git+https://github.com/grapu-design/designsystem.git",
6
+ "url": "git+https://github.com/grape-design/designsystem.git",
7
7
  "directory": "packages/react-headless/image"
8
8
  },
9
9
  "sideEffects": false,
@@ -30,8 +30,8 @@
30
30
  "@radix-ui/react-compose-refs": "^1.1.2",
31
31
  "@radix-ui/react-use-callback-ref": "^1.1.1",
32
32
  "@radix-ui/react-use-layout-effect": "^1.1.1",
33
- "@grapu-design/dom-utils": "^0.1.0",
34
- "@grapu-design/react-primitive": "^0.1.0"
33
+ "@grapu-design/dom-utils": "^0.1.2",
34
+ "@grapu-design/react-primitive": "^0.1.2"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/react": "^19.1.6",
package/src/Image.tsx CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { composeRefs } from "@radix-ui/react-compose-refs";
4
4
  import { useLayoutEffect } from "@radix-ui/react-use-layout-effect";
5
- import { mergeProps } from "@grapu-design/dom-utils";
6
- import { Primitive, type PrimitiveProps } from "@grapu-design/react-primitive";
5
+ import { mergeProps } from "@grape-design/dom-utils";
6
+ import { Primitive, type PrimitiveProps } from "@grape-design/react-primitive";
7
7
  import type * as React from "react";
8
8
  import { forwardRef } from "react";
9
9
  import { useImage, type UseImageProps } from "./useImage";
package/src/useImage.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
2
2
  import { useLayoutEffect } from "@radix-ui/react-use-layout-effect";
3
- import { dataAttr, elementProps, imgProps } from "@grapu-design/dom-utils";
3
+ import { dataAttr, elementProps, imgProps } from "@grape-design/dom-utils";
4
4
  import { useCallback, useMemo, useRef, useState } from "react";
5
5
 
6
6
  export type ImageLoadingStatus = "loading" | "loaded" | "error";