@kushagradhawan/kookie-ui 0.1.14 → 0.1.15
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/components.css +3 -0
- package/dist/cjs/components/image.d.ts +19 -2
- package/dist/cjs/components/image.d.ts.map +1 -1
- package/dist/cjs/components/image.js +1 -1
- package/dist/cjs/components/image.js.map +3 -3
- package/dist/esm/components/image.d.ts +19 -2
- package/dist/esm/components/image.d.ts.map +1 -1
- package/dist/esm/components/image.js +1 -1
- package/dist/esm/components/image.js.map +3 -3
- package/package.json +1 -1
- package/src/components/image.css +5 -0
- package/src/components/image.tsx +131 -8
- package/styles.css +3 -0
package/components.css
CHANGED
|
@@ -7,13 +7,30 @@ import type { HeightProps } from '../props/height.props.js';
|
|
|
7
7
|
import type { GetPropDefTypes } from '../props/prop-def.js';
|
|
8
8
|
type ImageOwnProps = GetPropDefTypes<typeof imagePropDefs> & {
|
|
9
9
|
loading?: 'eager' | 'lazy';
|
|
10
|
+
/**
|
|
11
|
+
* Placeholder image URL to show while the main image is loading.
|
|
12
|
+
* Can be a low-quality/blurred version of the main image.
|
|
13
|
+
*/
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Shows a skeleton placeholder while loading instead of a blank space.
|
|
17
|
+
*/
|
|
18
|
+
showSkeleton?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Callback fired when the image successfully loads.
|
|
21
|
+
*/
|
|
22
|
+
onLoad?: (event: React.SyntheticEvent<HTMLImageElement>) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Callback fired when the image fails to load.
|
|
25
|
+
*/
|
|
26
|
+
onError?: (event: React.SyntheticEvent<HTMLImageElement>) => void;
|
|
10
27
|
};
|
|
11
28
|
interface ImageProps extends ComponentPropsWithout<'img', RemovedProps | 'color' | 'width' | 'height' | 'alt'>, MarginProps, WidthProps, HeightProps, ImageOwnProps {
|
|
12
29
|
/**
|
|
13
30
|
* The alt attribute provides alternative information for an image if a user for some reason cannot view it.
|
|
14
|
-
* Required for accessibility when not using asChild.
|
|
31
|
+
* Required for accessibility when not using asChild. Use empty string for decorative images.
|
|
15
32
|
*/
|
|
16
|
-
alt
|
|
33
|
+
alt: string;
|
|
17
34
|
}
|
|
18
35
|
declare const Image: React.ForwardRefExoticComponent<ImageProps & React.RefAttributes<HTMLImageElement>>;
|
|
19
36
|
export { Image };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/components/image.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/components/image.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAOjD,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D,KAAK,aAAa,GAAG,eAAe,CAAC,OAAO,aAAa,CAAC,GAAG;IAC3D,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IACjE;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;CACnE,CAAC;AAEF,UAAU,UACR,SAAQ,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC,EACvF,WAAW,EACX,UAAU,EACV,WAAW,EACX,aAAa;IACf;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;CACb;AAED,QAAA,MAAM,KAAK,qFA+PT,CAAC;AAIH,OAAO,EAAE,KAAK,EAAE,CAAC;AACjB,YAAY,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";"use client";var
|
|
1
|
+
"use strict";"use client";var $=Object.create;var y=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var F=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty;var K=(t,r)=>{for(var o in r)y(t,o,{get:r[o],enumerable:!0})},D=(t,r,o,d)=>{if(r&&typeof r=="object"||typeof r=="function")for(let i of B(r))!J.call(t,i)&&i!==o&&y(t,i,{get:()=>r[i],enumerable:!(d=A(r,i))||d.enumerable});return t};var C=(t,r,o)=>(o=t!=null?$(F(t)):{},D(r||!t||!t.__esModule?y(o,"default",{value:t,enumerable:!0}):o,t)),Q=t=>D(y({},"__esModule",{value:!0}),t);var R={};K(R,{Image:()=>P});module.exports=Q(R);var e=C(require("react")),s=C(require("classnames")),H=require("./image.props.js"),M=require("../helpers/extract-props.js"),T=require("../props/margin.props.js"),j=require("../props/width.props.js"),W=require("../props/height.props.js"),z=require("./skeleton.js");const P=e.forwardRef((t,r)=>{const{variant:o="surface",children:d}=t,{asChild:i,className:l,radius:n,style:p,loading:c="lazy",alt:u,src:m,placeholder:h,showSkeleton:E=!1,onLoad:v,onError:N,children:U,...g}=(0,M.extractProps)(t,H.imagePropDefs,T.marginPropDefs,j.widthPropDefs,W.heightPropDefs),[f,w]=e.useState(!1),[O,L]=e.useState(!1),[q,x]=e.useState(!!h),I=e.useCallback(a=>{w(!0),L(!1),x(!1),v?.(a)},[v]),b=e.useCallback(a=>{w(!1),L(!0),x(!1),N?.(a)},[N]);if(!m)return console.warn("Image component: src prop is required"),null;!i&&u===void 0&&console.warn("Image component: alt prop is required for accessibility when not using asChild");const G=E&&!f&&!O?e.createElement(z.Skeleton,{style:{...p,width:"100%",height:"100px",borderRadius:n?`var(--radius-${n})`:void 0},className:l}):null,_=h&&q?e.createElement("img",{"data-radius":n,style:{...p,position:"absolute",top:0,left:0,width:"100%",height:"100%",filter:"blur(4px)",opacity:.7,transition:"opacity 0.3s ease-out"},className:(0,s.default)("rt-reset","rt-Image","rt-Image--placeholder",l),alt:"",src:h}):null,k=e.createElement("img",{"data-radius":n,loading:c,style:{...p,opacity:f?1:0,transition:"opacity 0.3s ease-out"},className:(0,s.default)("rt-reset","rt-Image",o==="blur"&&"rt-Image--blur",l),alt:u,src:m,onLoad:I,onError:b,...g,ref:r}),S=h||E?e.createElement("div",{style:{position:"relative",display:"inline-block"}},G,_,k):k;if(i&&d){const a=e.Children.only(d);return o==="blur"?e.cloneElement(a,{className:(0,s.default)(a.props?.className,"rt-variant-blur"),style:{position:"relative",display:"inline-block",textDecoration:"none",color:"inherit",border:"none",background:"none",padding:0,font:"inherit",cursor:"pointer",...a.props?.style},children:e.createElement(e.Fragment,null,e.createElement("img",{"data-radius":n,loading:c,style:{...p,position:"absolute",top:"8px",left:"0"},className:(0,s.default)("rt-reset","rt-Image","rt-Image--blur","rt-Image--blur-bg",l),alt:"",src:m,...g}),e.createElement("img",{"data-radius":n,loading:c,style:{...p,position:"relative",zIndex:1,opacity:f?1:0,transition:"opacity 0.3s ease-out"},className:(0,s.default)("rt-reset","rt-Image","rt-Image--blur",l),alt:u,src:m,onLoad:I,onError:b,...g,ref:r}))}):e.cloneElement(a,{className:(0,s.default)(a.props?.className,"rt-Image"),style:{textDecoration:"none",color:"inherit",border:"none",background:"none",padding:0,font:"inherit",cursor:"pointer",...a.props?.style},children:S})}return o==="blur"?e.createElement("div",{className:"rt-variant-blur",style:{position:"relative",display:"inline-block"}},e.createElement("img",{"data-radius":n,loading:c,style:{...p,position:"absolute",top:"8px",left:"0"},className:(0,s.default)("rt-reset","rt-Image","rt-Image--blur","rt-Image--blur-bg",l),alt:"",src:m,...g}),e.createElement("img",{"data-radius":n,loading:c,style:{...p,position:"relative",zIndex:1,opacity:f?1:0,transition:"opacity 0.3s ease-out"},className:(0,s.default)("rt-reset","rt-Image","rt-Image--blur",l),alt:u,src:m,onLoad:I,onError:b,...g,ref:r})):S});P.displayName="Image";
|
|
2
2
|
//# sourceMappingURL=image.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/image.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client';\n\nimport * as React from 'react';\nimport classNames from 'classnames';\n\nimport { imagePropDefs } from './image.props.js';\nimport { extractProps } from '../helpers/extract-props.js';\nimport { marginPropDefs } from '../props/margin.props.js';\nimport { widthPropDefs } from '../props/width.props.js';\nimport { heightPropDefs } from '../props/height.props.js';\n\nimport type { ComponentPropsWithout, RemovedProps } from '../helpers/component-props.js';\nimport type { MarginProps } from '../props/margin.props.js';\nimport type { WidthProps } from '../props/width.props.js';\nimport type { HeightProps } from '../props/height.props.js';\nimport type { GetPropDefTypes } from '../props/prop-def.js';\n\ntype ImageElement = React.ElementRef<'img'>;\ntype ImageOwnProps = GetPropDefTypes<typeof imagePropDefs> & {\n loading?: 'eager' | 'lazy';\n};\n\ninterface ImageProps\n extends ComponentPropsWithout<'img', RemovedProps | 'color' | 'width' | 'height' | 'alt'>,\n MarginProps,\n WidthProps,\n HeightProps,\n ImageOwnProps {\n /**\n * The alt attribute provides alternative information for an image if a user for some reason cannot view it.\n * Required for accessibility when not using asChild.\n */\n alt
|
|
5
|
-
"mappings": "ukBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,WAAAE,IAAA,eAAAC,EAAAH,GAEA,IAAAI,EAAuB,oBACvBC,EAAuB,yBAEvBC,EAA8B,4BAC9BC,EAA6B,uCAC7BC,EAA+B,oCAC/BC,EAA8B,mCAC9BC,EAA+B,
|
|
6
|
-
"names": ["image_exports", "__export", "Image", "__toCommonJS", "React", "import_classnames", "import_image_props", "import_extract_props", "import_margin_props", "import_width_props", "import_height_props", "
|
|
4
|
+
"sourcesContent": ["'use client';\n\nimport * as React from 'react';\nimport classNames from 'classnames';\n\nimport { imagePropDefs } from './image.props.js';\nimport { extractProps } from '../helpers/extract-props.js';\nimport { marginPropDefs } from '../props/margin.props.js';\nimport { widthPropDefs } from '../props/width.props.js';\nimport { heightPropDefs } from '../props/height.props.js';\nimport { Skeleton } from './skeleton.js';\n\nimport type { ComponentPropsWithout, RemovedProps } from '../helpers/component-props.js';\nimport type { MarginProps } from '../props/margin.props.js';\nimport type { WidthProps } from '../props/width.props.js';\nimport type { HeightProps } from '../props/height.props.js';\nimport type { GetPropDefTypes } from '../props/prop-def.js';\n\ntype ImageElement = React.ElementRef<'img'>;\ntype ImageOwnProps = GetPropDefTypes<typeof imagePropDefs> & {\n loading?: 'eager' | 'lazy';\n /**\n * Placeholder image URL to show while the main image is loading.\n * Can be a low-quality/blurred version of the main image.\n */\n placeholder?: string;\n /**\n * Shows a skeleton placeholder while loading instead of a blank space.\n */\n showSkeleton?: boolean;\n /**\n * Callback fired when the image successfully loads.\n */\n onLoad?: (event: React.SyntheticEvent<HTMLImageElement>) => void;\n /**\n * Callback fired when the image fails to load.\n */\n onError?: (event: React.SyntheticEvent<HTMLImageElement>) => void;\n};\n\ninterface ImageProps\n extends ComponentPropsWithout<'img', RemovedProps | 'color' | 'width' | 'height' | 'alt'>,\n MarginProps,\n WidthProps,\n HeightProps,\n ImageOwnProps {\n /**\n * The alt attribute provides alternative information for an image if a user for some reason cannot view it.\n * Required for accessibility when not using asChild. Use empty string for decorative images.\n */\n alt: string;\n}\n\nconst Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) => {\n const { variant = 'surface', children } = props;\n const {\n asChild,\n className,\n radius,\n style,\n loading = 'lazy',\n alt,\n src,\n placeholder,\n showSkeleton = false,\n onLoad: userOnLoad,\n onError: userOnError,\n children: _children, // Extract children to exclude from imgProps\n ...imgProps\n } = extractProps(props, imagePropDefs, marginPropDefs, widthPropDefs, heightPropDefs);\n\n // Loading state management\n const [imageLoaded, setImageLoaded] = React.useState(false);\n const [imageError, setImageError] = React.useState(false);\n const [showPlaceholder, setShowPlaceholder] = React.useState(!!placeholder);\n\n // Handle image load - moved to top to avoid conditional hook call\n const handleLoad = React.useCallback((event: React.SyntheticEvent<HTMLImageElement>) => {\n setImageLoaded(true);\n setImageError(false);\n setShowPlaceholder(false);\n userOnLoad?.(event);\n }, [userOnLoad]);\n\n // Handle image error - moved to top to avoid conditional hook call\n const handleError = React.useCallback((event: React.SyntheticEvent<HTMLImageElement>) => {\n setImageLoaded(false);\n setImageError(true);\n setShowPlaceholder(false);\n userOnError?.(event);\n }, [userOnError]);\n\n // Validate required props\n if (!src) {\n console.warn('Image component: src prop is required');\n return null;\n }\n\n if (!asChild && alt === undefined) {\n console.warn('Image component: alt prop is required for accessibility when not using asChild');\n }\n\n // Create skeleton placeholder\n const skeletonElement = showSkeleton && !imageLoaded && !imageError ? (\n <Skeleton\n style={{\n ...style,\n width: '100%',\n height: '100px', // Default height, can be overridden by style\n borderRadius: radius ? `var(--radius-${radius})` : undefined,\n }}\n className={className}\n />\n ) : null;\n\n // Create placeholder image element\n const placeholderElement = placeholder && showPlaceholder ? (\n <img\n data-radius={radius}\n style={{\n ...style,\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n filter: 'blur(4px)',\n opacity: 0.7,\n transition: 'opacity 0.3s ease-out',\n }}\n className={classNames(\n 'rt-reset',\n 'rt-Image',\n 'rt-Image--placeholder',\n className,\n )}\n alt=\"\"\n src={placeholder}\n />\n ) : null;\n\n // Create the standard img element\n const imgElement = (\n <img\n data-radius={radius}\n loading={loading}\n style={{\n ...style,\n opacity: imageLoaded ? 1 : 0,\n transition: 'opacity 0.3s ease-out',\n }}\n className={classNames(\n 'rt-reset',\n 'rt-Image',\n variant === 'blur' && 'rt-Image--blur',\n className,\n )}\n alt={alt}\n src={src}\n onLoad={handleLoad}\n onError={handleError}\n {...imgProps}\n ref={forwardedRef}\n />\n );\n\n // Wrapper for images with placeholders\n const imageWithPlaceholder = (placeholder || showSkeleton) ? (\n <div style={{ position: 'relative', display: 'inline-block' }}>\n {skeletonElement}\n {placeholderElement}\n {imgElement}\n </div>\n ) : imgElement;\n\n // Handle asChild - inject img into the child element\n if (asChild && children) {\n const child = React.Children.only(children) as React.ReactElement<any>;\n\n if (variant === 'blur') {\n // For blur variant with asChild\n return React.cloneElement(child, {\n className: classNames(child.props?.className, 'rt-variant-blur'),\n style: {\n position: 'relative',\n display: 'inline-block',\n textDecoration: 'none', // Reset link underlines\n color: 'inherit', // Reset link colors\n border: 'none', // Reset button borders\n background: 'none', // Reset button backgrounds\n padding: 0, // Reset button padding\n font: 'inherit', // Reset button fonts\n cursor: 'pointer', // Ensure interactive cursor\n ...child.props?.style,\n },\n children: (\n <>\n {/* Background blurred image */}\n <img\n data-radius={radius}\n loading={loading}\n style={{\n ...style,\n position: 'absolute',\n top: '8px',\n left: '0',\n }}\n className={classNames(\n 'rt-reset',\n 'rt-Image',\n 'rt-Image--blur',\n 'rt-Image--blur-bg',\n className,\n )}\n alt=\"\"\n src={src}\n {...imgProps}\n />\n {/* Foreground image */}\n <img\n data-radius={radius}\n loading={loading}\n style={{ \n ...style, \n position: 'relative', \n zIndex: 1,\n opacity: imageLoaded ? 1 : 0,\n transition: 'opacity 0.3s ease-out',\n }}\n className={classNames('rt-reset', 'rt-Image', 'rt-Image--blur', className)}\n alt={alt}\n src={src}\n onLoad={handleLoad}\n onError={handleError}\n {...imgProps}\n ref={forwardedRef}\n />\n </>\n ),\n });\n } else {\n // For surface variant with asChild\n return React.cloneElement(child, {\n className: classNames(child.props?.className, 'rt-Image'),\n style: {\n textDecoration: 'none', // Reset link underlines\n color: 'inherit', // Reset link colors\n border: 'none', // Reset button borders\n background: 'none', // Reset button backgrounds\n padding: 0, // Reset button padding\n font: 'inherit', // Reset button fonts\n cursor: 'pointer', // Ensure interactive cursor\n ...child.props?.style, // Allow user overrides\n },\n children: imageWithPlaceholder,\n });\n }\n }\n\n // Regular rendering without asChild\n if (variant === 'blur') {\n return (\n <div className=\"rt-variant-blur\" style={{ position: 'relative', display: 'inline-block' }}>\n {/* Background blurred image */}\n <img\n data-radius={radius}\n loading={loading}\n style={{\n ...style,\n position: 'absolute',\n top: '8px',\n left: '0',\n }}\n className={classNames(\n 'rt-reset',\n 'rt-Image',\n 'rt-Image--blur',\n 'rt-Image--blur-bg',\n className,\n )}\n alt=\"\"\n src={src}\n {...imgProps}\n />\n {/* Foreground image */}\n <img\n data-radius={radius}\n loading={loading}\n style={{ \n ...style, \n position: 'relative', \n zIndex: 1,\n opacity: imageLoaded ? 1 : 0,\n transition: 'opacity 0.3s ease-out',\n }}\n className={classNames('rt-reset', 'rt-Image', 'rt-Image--blur', className)}\n alt={alt}\n src={src}\n onLoad={handleLoad}\n onError={handleError}\n {...imgProps}\n ref={forwardedRef}\n />\n </div>\n );\n }\n\n return imageWithPlaceholder;\n});\n\nImage.displayName = 'Image';\n\nexport { Image };\nexport type { ImageProps };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["image_exports", "__export", "Image", "__toCommonJS", "React", "import_classnames", "import_image_props", "import_extract_props", "import_margin_props", "import_width_props", "import_height_props", "import_skeleton", "props", "forwardedRef", "variant", "children", "asChild", "className", "radius", "style", "loading", "alt", "src", "placeholder", "showSkeleton", "userOnLoad", "userOnError", "_children", "imgProps", "imageLoaded", "setImageLoaded", "imageError", "setImageError", "showPlaceholder", "setShowPlaceholder", "handleLoad", "event", "handleError", "skeletonElement", "placeholderElement", "classNames", "imgElement", "imageWithPlaceholder", "child"]
|
|
7
7
|
}
|
|
@@ -7,13 +7,30 @@ import type { HeightProps } from '../props/height.props.js';
|
|
|
7
7
|
import type { GetPropDefTypes } from '../props/prop-def.js';
|
|
8
8
|
type ImageOwnProps = GetPropDefTypes<typeof imagePropDefs> & {
|
|
9
9
|
loading?: 'eager' | 'lazy';
|
|
10
|
+
/**
|
|
11
|
+
* Placeholder image URL to show while the main image is loading.
|
|
12
|
+
* Can be a low-quality/blurred version of the main image.
|
|
13
|
+
*/
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Shows a skeleton placeholder while loading instead of a blank space.
|
|
17
|
+
*/
|
|
18
|
+
showSkeleton?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Callback fired when the image successfully loads.
|
|
21
|
+
*/
|
|
22
|
+
onLoad?: (event: React.SyntheticEvent<HTMLImageElement>) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Callback fired when the image fails to load.
|
|
25
|
+
*/
|
|
26
|
+
onError?: (event: React.SyntheticEvent<HTMLImageElement>) => void;
|
|
10
27
|
};
|
|
11
28
|
interface ImageProps extends ComponentPropsWithout<'img', RemovedProps | 'color' | 'width' | 'height' | 'alt'>, MarginProps, WidthProps, HeightProps, ImageOwnProps {
|
|
12
29
|
/**
|
|
13
30
|
* The alt attribute provides alternative information for an image if a user for some reason cannot view it.
|
|
14
|
-
* Required for accessibility when not using asChild.
|
|
31
|
+
* Required for accessibility when not using asChild. Use empty string for decorative images.
|
|
15
32
|
*/
|
|
16
|
-
alt
|
|
33
|
+
alt: string;
|
|
17
34
|
}
|
|
18
35
|
declare const Image: React.ForwardRefExoticComponent<ImageProps & React.RefAttributes<HTMLImageElement>>;
|
|
19
36
|
export { Image };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/components/image.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/components/image.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAOjD,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D,KAAK,aAAa,GAAG,eAAe,CAAC,OAAO,aAAa,CAAC,GAAG;IAC3D,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;OAEG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IACjE;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;CACnE,CAAC;AAEF,UAAU,UACR,SAAQ,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC,EACvF,WAAW,EACX,UAAU,EACV,WAAW,EACX,aAAa;IACf;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;CACb;AAED,QAAA,MAAM,KAAK,qFA+PT,CAAC;AAIH,OAAO,EAAE,KAAK,EAAE,CAAC;AACjB,YAAY,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import*as e from"react";import
|
|
1
|
+
"use client";import*as e from"react";import o from"classnames";import{imagePropDefs as M}from"./image.props.js";import{extractProps as T}from"../helpers/extract-props.js";import{marginPropDefs as j}from"../props/margin.props.js";import{widthPropDefs as W}from"../props/width.props.js";import{heightPropDefs as z}from"../props/height.props.js";import{Skeleton as O}from"./skeleton.js";const k=e.forwardRef((f,c)=>{const{variant:g="surface",children:y}=f,{asChild:I,className:a,radius:r,style:s,loading:n="lazy",alt:p,src:i,placeholder:m,showSkeleton:b=!1,onLoad:P,onError:E,children:q,...l}=T(f,M,j,W,z),[d,v]=e.useState(!1),[S,N]=e.useState(!1),[D,w]=e.useState(!!m),u=e.useCallback(t=>{v(!0),N(!1),w(!1),P?.(t)},[P]),h=e.useCallback(t=>{v(!1),N(!0),w(!1),E?.(t)},[E]);if(!i)return console.warn("Image component: src prop is required"),null;!I&&p===void 0&&console.warn("Image component: alt prop is required for accessibility when not using asChild");const C=b&&!d&&!S?e.createElement(O,{style:{...s,width:"100%",height:"100px",borderRadius:r?`var(--radius-${r})`:void 0},className:a}):null,H=m&&D?e.createElement("img",{"data-radius":r,style:{...s,position:"absolute",top:0,left:0,width:"100%",height:"100%",filter:"blur(4px)",opacity:.7,transition:"opacity 0.3s ease-out"},className:o("rt-reset","rt-Image","rt-Image--placeholder",a),alt:"",src:m}):null,L=e.createElement("img",{"data-radius":r,loading:n,style:{...s,opacity:d?1:0,transition:"opacity 0.3s ease-out"},className:o("rt-reset","rt-Image",g==="blur"&&"rt-Image--blur",a),alt:p,src:i,onLoad:u,onError:h,...l,ref:c}),x=m||b?e.createElement("div",{style:{position:"relative",display:"inline-block"}},C,H,L):L;if(I&&y){const t=e.Children.only(y);return g==="blur"?e.cloneElement(t,{className:o(t.props?.className,"rt-variant-blur"),style:{position:"relative",display:"inline-block",textDecoration:"none",color:"inherit",border:"none",background:"none",padding:0,font:"inherit",cursor:"pointer",...t.props?.style},children:e.createElement(e.Fragment,null,e.createElement("img",{"data-radius":r,loading:n,style:{...s,position:"absolute",top:"8px",left:"0"},className:o("rt-reset","rt-Image","rt-Image--blur","rt-Image--blur-bg",a),alt:"",src:i,...l}),e.createElement("img",{"data-radius":r,loading:n,style:{...s,position:"relative",zIndex:1,opacity:d?1:0,transition:"opacity 0.3s ease-out"},className:o("rt-reset","rt-Image","rt-Image--blur",a),alt:p,src:i,onLoad:u,onError:h,...l,ref:c}))}):e.cloneElement(t,{className:o(t.props?.className,"rt-Image"),style:{textDecoration:"none",color:"inherit",border:"none",background:"none",padding:0,font:"inherit",cursor:"pointer",...t.props?.style},children:x})}return g==="blur"?e.createElement("div",{className:"rt-variant-blur",style:{position:"relative",display:"inline-block"}},e.createElement("img",{"data-radius":r,loading:n,style:{...s,position:"absolute",top:"8px",left:"0"},className:o("rt-reset","rt-Image","rt-Image--blur","rt-Image--blur-bg",a),alt:"",src:i,...l}),e.createElement("img",{"data-radius":r,loading:n,style:{...s,position:"relative",zIndex:1,opacity:d?1:0,transition:"opacity 0.3s ease-out"},className:o("rt-reset","rt-Image","rt-Image--blur",a),alt:p,src:i,onLoad:u,onError:h,...l,ref:c})):x});k.displayName="Image";export{k as Image};
|
|
2
2
|
//# sourceMappingURL=image.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/image.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client';\n\nimport * as React from 'react';\nimport classNames from 'classnames';\n\nimport { imagePropDefs } from './image.props.js';\nimport { extractProps } from '../helpers/extract-props.js';\nimport { marginPropDefs } from '../props/margin.props.js';\nimport { widthPropDefs } from '../props/width.props.js';\nimport { heightPropDefs } from '../props/height.props.js';\n\nimport type { ComponentPropsWithout, RemovedProps } from '../helpers/component-props.js';\nimport type { MarginProps } from '../props/margin.props.js';\nimport type { WidthProps } from '../props/width.props.js';\nimport type { HeightProps } from '../props/height.props.js';\nimport type { GetPropDefTypes } from '../props/prop-def.js';\n\ntype ImageElement = React.ElementRef<'img'>;\ntype ImageOwnProps = GetPropDefTypes<typeof imagePropDefs> & {\n loading?: 'eager' | 'lazy';\n};\n\ninterface ImageProps\n extends ComponentPropsWithout<'img', RemovedProps | 'color' | 'width' | 'height' | 'alt'>,\n MarginProps,\n WidthProps,\n HeightProps,\n ImageOwnProps {\n /**\n * The alt attribute provides alternative information for an image if a user for some reason cannot view it.\n * Required for accessibility when not using asChild.\n */\n alt
|
|
5
|
-
"mappings": "aAEA,UAAYA,MAAW,QACvB,OAAOC,MAAgB,aAEvB,OAAS,iBAAAC,MAAqB,mBAC9B,OAAS,gBAAAC,MAAoB,8BAC7B,OAAS,kBAAAC,MAAsB,2BAC/B,OAAS,iBAAAC,MAAqB,0BAC9B,OAAS,kBAAAC,MAAsB,
|
|
6
|
-
"names": ["React", "classNames", "imagePropDefs", "extractProps", "marginPropDefs", "widthPropDefs", "heightPropDefs", "Image", "props", "forwardedRef", "variant", "
|
|
4
|
+
"sourcesContent": ["'use client';\n\nimport * as React from 'react';\nimport classNames from 'classnames';\n\nimport { imagePropDefs } from './image.props.js';\nimport { extractProps } from '../helpers/extract-props.js';\nimport { marginPropDefs } from '../props/margin.props.js';\nimport { widthPropDefs } from '../props/width.props.js';\nimport { heightPropDefs } from '../props/height.props.js';\nimport { Skeleton } from './skeleton.js';\n\nimport type { ComponentPropsWithout, RemovedProps } from '../helpers/component-props.js';\nimport type { MarginProps } from '../props/margin.props.js';\nimport type { WidthProps } from '../props/width.props.js';\nimport type { HeightProps } from '../props/height.props.js';\nimport type { GetPropDefTypes } from '../props/prop-def.js';\n\ntype ImageElement = React.ElementRef<'img'>;\ntype ImageOwnProps = GetPropDefTypes<typeof imagePropDefs> & {\n loading?: 'eager' | 'lazy';\n /**\n * Placeholder image URL to show while the main image is loading.\n * Can be a low-quality/blurred version of the main image.\n */\n placeholder?: string;\n /**\n * Shows a skeleton placeholder while loading instead of a blank space.\n */\n showSkeleton?: boolean;\n /**\n * Callback fired when the image successfully loads.\n */\n onLoad?: (event: React.SyntheticEvent<HTMLImageElement>) => void;\n /**\n * Callback fired when the image fails to load.\n */\n onError?: (event: React.SyntheticEvent<HTMLImageElement>) => void;\n};\n\ninterface ImageProps\n extends ComponentPropsWithout<'img', RemovedProps | 'color' | 'width' | 'height' | 'alt'>,\n MarginProps,\n WidthProps,\n HeightProps,\n ImageOwnProps {\n /**\n * The alt attribute provides alternative information for an image if a user for some reason cannot view it.\n * Required for accessibility when not using asChild. Use empty string for decorative images.\n */\n alt: string;\n}\n\nconst Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) => {\n const { variant = 'surface', children } = props;\n const {\n asChild,\n className,\n radius,\n style,\n loading = 'lazy',\n alt,\n src,\n placeholder,\n showSkeleton = false,\n onLoad: userOnLoad,\n onError: userOnError,\n children: _children, // Extract children to exclude from imgProps\n ...imgProps\n } = extractProps(props, imagePropDefs, marginPropDefs, widthPropDefs, heightPropDefs);\n\n // Loading state management\n const [imageLoaded, setImageLoaded] = React.useState(false);\n const [imageError, setImageError] = React.useState(false);\n const [showPlaceholder, setShowPlaceholder] = React.useState(!!placeholder);\n\n // Handle image load - moved to top to avoid conditional hook call\n const handleLoad = React.useCallback((event: React.SyntheticEvent<HTMLImageElement>) => {\n setImageLoaded(true);\n setImageError(false);\n setShowPlaceholder(false);\n userOnLoad?.(event);\n }, [userOnLoad]);\n\n // Handle image error - moved to top to avoid conditional hook call\n const handleError = React.useCallback((event: React.SyntheticEvent<HTMLImageElement>) => {\n setImageLoaded(false);\n setImageError(true);\n setShowPlaceholder(false);\n userOnError?.(event);\n }, [userOnError]);\n\n // Validate required props\n if (!src) {\n console.warn('Image component: src prop is required');\n return null;\n }\n\n if (!asChild && alt === undefined) {\n console.warn('Image component: alt prop is required for accessibility when not using asChild');\n }\n\n // Create skeleton placeholder\n const skeletonElement = showSkeleton && !imageLoaded && !imageError ? (\n <Skeleton\n style={{\n ...style,\n width: '100%',\n height: '100px', // Default height, can be overridden by style\n borderRadius: radius ? `var(--radius-${radius})` : undefined,\n }}\n className={className}\n />\n ) : null;\n\n // Create placeholder image element\n const placeholderElement = placeholder && showPlaceholder ? (\n <img\n data-radius={radius}\n style={{\n ...style,\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n filter: 'blur(4px)',\n opacity: 0.7,\n transition: 'opacity 0.3s ease-out',\n }}\n className={classNames(\n 'rt-reset',\n 'rt-Image',\n 'rt-Image--placeholder',\n className,\n )}\n alt=\"\"\n src={placeholder}\n />\n ) : null;\n\n // Create the standard img element\n const imgElement = (\n <img\n data-radius={radius}\n loading={loading}\n style={{\n ...style,\n opacity: imageLoaded ? 1 : 0,\n transition: 'opacity 0.3s ease-out',\n }}\n className={classNames(\n 'rt-reset',\n 'rt-Image',\n variant === 'blur' && 'rt-Image--blur',\n className,\n )}\n alt={alt}\n src={src}\n onLoad={handleLoad}\n onError={handleError}\n {...imgProps}\n ref={forwardedRef}\n />\n );\n\n // Wrapper for images with placeholders\n const imageWithPlaceholder = (placeholder || showSkeleton) ? (\n <div style={{ position: 'relative', display: 'inline-block' }}>\n {skeletonElement}\n {placeholderElement}\n {imgElement}\n </div>\n ) : imgElement;\n\n // Handle asChild - inject img into the child element\n if (asChild && children) {\n const child = React.Children.only(children) as React.ReactElement<any>;\n\n if (variant === 'blur') {\n // For blur variant with asChild\n return React.cloneElement(child, {\n className: classNames(child.props?.className, 'rt-variant-blur'),\n style: {\n position: 'relative',\n display: 'inline-block',\n textDecoration: 'none', // Reset link underlines\n color: 'inherit', // Reset link colors\n border: 'none', // Reset button borders\n background: 'none', // Reset button backgrounds\n padding: 0, // Reset button padding\n font: 'inherit', // Reset button fonts\n cursor: 'pointer', // Ensure interactive cursor\n ...child.props?.style,\n },\n children: (\n <>\n {/* Background blurred image */}\n <img\n data-radius={radius}\n loading={loading}\n style={{\n ...style,\n position: 'absolute',\n top: '8px',\n left: '0',\n }}\n className={classNames(\n 'rt-reset',\n 'rt-Image',\n 'rt-Image--blur',\n 'rt-Image--blur-bg',\n className,\n )}\n alt=\"\"\n src={src}\n {...imgProps}\n />\n {/* Foreground image */}\n <img\n data-radius={radius}\n loading={loading}\n style={{ \n ...style, \n position: 'relative', \n zIndex: 1,\n opacity: imageLoaded ? 1 : 0,\n transition: 'opacity 0.3s ease-out',\n }}\n className={classNames('rt-reset', 'rt-Image', 'rt-Image--blur', className)}\n alt={alt}\n src={src}\n onLoad={handleLoad}\n onError={handleError}\n {...imgProps}\n ref={forwardedRef}\n />\n </>\n ),\n });\n } else {\n // For surface variant with asChild\n return React.cloneElement(child, {\n className: classNames(child.props?.className, 'rt-Image'),\n style: {\n textDecoration: 'none', // Reset link underlines\n color: 'inherit', // Reset link colors\n border: 'none', // Reset button borders\n background: 'none', // Reset button backgrounds\n padding: 0, // Reset button padding\n font: 'inherit', // Reset button fonts\n cursor: 'pointer', // Ensure interactive cursor\n ...child.props?.style, // Allow user overrides\n },\n children: imageWithPlaceholder,\n });\n }\n }\n\n // Regular rendering without asChild\n if (variant === 'blur') {\n return (\n <div className=\"rt-variant-blur\" style={{ position: 'relative', display: 'inline-block' }}>\n {/* Background blurred image */}\n <img\n data-radius={radius}\n loading={loading}\n style={{\n ...style,\n position: 'absolute',\n top: '8px',\n left: '0',\n }}\n className={classNames(\n 'rt-reset',\n 'rt-Image',\n 'rt-Image--blur',\n 'rt-Image--blur-bg',\n className,\n )}\n alt=\"\"\n src={src}\n {...imgProps}\n />\n {/* Foreground image */}\n <img\n data-radius={radius}\n loading={loading}\n style={{ \n ...style, \n position: 'relative', \n zIndex: 1,\n opacity: imageLoaded ? 1 : 0,\n transition: 'opacity 0.3s ease-out',\n }}\n className={classNames('rt-reset', 'rt-Image', 'rt-Image--blur', className)}\n alt={alt}\n src={src}\n onLoad={handleLoad}\n onError={handleError}\n {...imgProps}\n ref={forwardedRef}\n />\n </div>\n );\n }\n\n return imageWithPlaceholder;\n});\n\nImage.displayName = 'Image';\n\nexport { Image };\nexport type { ImageProps };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["React", "classNames", "imagePropDefs", "extractProps", "marginPropDefs", "widthPropDefs", "heightPropDefs", "Skeleton", "Image", "props", "forwardedRef", "variant", "children", "asChild", "className", "radius", "style", "loading", "alt", "src", "placeholder", "showSkeleton", "userOnLoad", "userOnError", "_children", "imgProps", "imageLoaded", "setImageLoaded", "imageError", "setImageError", "showPlaceholder", "setShowPlaceholder", "handleLoad", "event", "handleError", "skeletonElement", "placeholderElement", "imgElement", "imageWithPlaceholder", "child"]
|
|
7
7
|
}
|
package/package.json
CHANGED
package/src/components/image.css
CHANGED
package/src/components/image.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { extractProps } from '../helpers/extract-props.js';
|
|
|
8
8
|
import { marginPropDefs } from '../props/margin.props.js';
|
|
9
9
|
import { widthPropDefs } from '../props/width.props.js';
|
|
10
10
|
import { heightPropDefs } from '../props/height.props.js';
|
|
11
|
+
import { Skeleton } from './skeleton.js';
|
|
11
12
|
|
|
12
13
|
import type { ComponentPropsWithout, RemovedProps } from '../helpers/component-props.js';
|
|
13
14
|
import type { MarginProps } from '../props/margin.props.js';
|
|
@@ -18,6 +19,23 @@ import type { GetPropDefTypes } from '../props/prop-def.js';
|
|
|
18
19
|
type ImageElement = React.ElementRef<'img'>;
|
|
19
20
|
type ImageOwnProps = GetPropDefTypes<typeof imagePropDefs> & {
|
|
20
21
|
loading?: 'eager' | 'lazy';
|
|
22
|
+
/**
|
|
23
|
+
* Placeholder image URL to show while the main image is loading.
|
|
24
|
+
* Can be a low-quality/blurred version of the main image.
|
|
25
|
+
*/
|
|
26
|
+
placeholder?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Shows a skeleton placeholder while loading instead of a blank space.
|
|
29
|
+
*/
|
|
30
|
+
showSkeleton?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Callback fired when the image successfully loads.
|
|
33
|
+
*/
|
|
34
|
+
onLoad?: (event: React.SyntheticEvent<HTMLImageElement>) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Callback fired when the image fails to load.
|
|
37
|
+
*/
|
|
38
|
+
onError?: (event: React.SyntheticEvent<HTMLImageElement>) => void;
|
|
21
39
|
};
|
|
22
40
|
|
|
23
41
|
interface ImageProps
|
|
@@ -28,13 +46,13 @@ interface ImageProps
|
|
|
28
46
|
ImageOwnProps {
|
|
29
47
|
/**
|
|
30
48
|
* The alt attribute provides alternative information for an image if a user for some reason cannot view it.
|
|
31
|
-
* Required for accessibility when not using asChild.
|
|
49
|
+
* Required for accessibility when not using asChild. Use empty string for decorative images.
|
|
32
50
|
*/
|
|
33
|
-
alt
|
|
51
|
+
alt: string;
|
|
34
52
|
}
|
|
35
53
|
|
|
36
54
|
const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) => {
|
|
37
|
-
const { variant = 'surface',
|
|
55
|
+
const { variant = 'surface', children } = props;
|
|
38
56
|
const {
|
|
39
57
|
asChild,
|
|
40
58
|
className,
|
|
@@ -43,16 +61,94 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
43
61
|
loading = 'lazy',
|
|
44
62
|
alt,
|
|
45
63
|
src,
|
|
64
|
+
placeholder,
|
|
65
|
+
showSkeleton = false,
|
|
66
|
+
onLoad: userOnLoad,
|
|
67
|
+
onError: userOnError,
|
|
46
68
|
children: _children, // Extract children to exclude from imgProps
|
|
47
69
|
...imgProps
|
|
48
70
|
} = extractProps(props, imagePropDefs, marginPropDefs, widthPropDefs, heightPropDefs);
|
|
49
71
|
|
|
72
|
+
// Loading state management
|
|
73
|
+
const [imageLoaded, setImageLoaded] = React.useState(false);
|
|
74
|
+
const [imageError, setImageError] = React.useState(false);
|
|
75
|
+
const [showPlaceholder, setShowPlaceholder] = React.useState(!!placeholder);
|
|
76
|
+
|
|
77
|
+
// Handle image load - moved to top to avoid conditional hook call
|
|
78
|
+
const handleLoad = React.useCallback((event: React.SyntheticEvent<HTMLImageElement>) => {
|
|
79
|
+
setImageLoaded(true);
|
|
80
|
+
setImageError(false);
|
|
81
|
+
setShowPlaceholder(false);
|
|
82
|
+
userOnLoad?.(event);
|
|
83
|
+
}, [userOnLoad]);
|
|
84
|
+
|
|
85
|
+
// Handle image error - moved to top to avoid conditional hook call
|
|
86
|
+
const handleError = React.useCallback((event: React.SyntheticEvent<HTMLImageElement>) => {
|
|
87
|
+
setImageLoaded(false);
|
|
88
|
+
setImageError(true);
|
|
89
|
+
setShowPlaceholder(false);
|
|
90
|
+
userOnError?.(event);
|
|
91
|
+
}, [userOnError]);
|
|
92
|
+
|
|
93
|
+
// Validate required props
|
|
94
|
+
if (!src) {
|
|
95
|
+
console.warn('Image component: src prop is required');
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!asChild && alt === undefined) {
|
|
100
|
+
console.warn('Image component: alt prop is required for accessibility when not using asChild');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Create skeleton placeholder
|
|
104
|
+
const skeletonElement = showSkeleton && !imageLoaded && !imageError ? (
|
|
105
|
+
<Skeleton
|
|
106
|
+
style={{
|
|
107
|
+
...style,
|
|
108
|
+
width: '100%',
|
|
109
|
+
height: '100px', // Default height, can be overridden by style
|
|
110
|
+
borderRadius: radius ? `var(--radius-${radius})` : undefined,
|
|
111
|
+
}}
|
|
112
|
+
className={className}
|
|
113
|
+
/>
|
|
114
|
+
) : null;
|
|
115
|
+
|
|
116
|
+
// Create placeholder image element
|
|
117
|
+
const placeholderElement = placeholder && showPlaceholder ? (
|
|
118
|
+
<img
|
|
119
|
+
data-radius={radius}
|
|
120
|
+
style={{
|
|
121
|
+
...style,
|
|
122
|
+
position: 'absolute',
|
|
123
|
+
top: 0,
|
|
124
|
+
left: 0,
|
|
125
|
+
width: '100%',
|
|
126
|
+
height: '100%',
|
|
127
|
+
filter: 'blur(4px)',
|
|
128
|
+
opacity: 0.7,
|
|
129
|
+
transition: 'opacity 0.3s ease-out',
|
|
130
|
+
}}
|
|
131
|
+
className={classNames(
|
|
132
|
+
'rt-reset',
|
|
133
|
+
'rt-Image',
|
|
134
|
+
'rt-Image--placeholder',
|
|
135
|
+
className,
|
|
136
|
+
)}
|
|
137
|
+
alt=""
|
|
138
|
+
src={placeholder}
|
|
139
|
+
/>
|
|
140
|
+
) : null;
|
|
141
|
+
|
|
50
142
|
// Create the standard img element
|
|
51
143
|
const imgElement = (
|
|
52
144
|
<img
|
|
53
145
|
data-radius={radius}
|
|
54
146
|
loading={loading}
|
|
55
|
-
style={
|
|
147
|
+
style={{
|
|
148
|
+
...style,
|
|
149
|
+
opacity: imageLoaded ? 1 : 0,
|
|
150
|
+
transition: 'opacity 0.3s ease-out',
|
|
151
|
+
}}
|
|
56
152
|
className={classNames(
|
|
57
153
|
'rt-reset',
|
|
58
154
|
'rt-Image',
|
|
@@ -61,11 +157,22 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
61
157
|
)}
|
|
62
158
|
alt={alt}
|
|
63
159
|
src={src}
|
|
160
|
+
onLoad={handleLoad}
|
|
161
|
+
onError={handleError}
|
|
64
162
|
{...imgProps}
|
|
65
163
|
ref={forwardedRef}
|
|
66
164
|
/>
|
|
67
165
|
);
|
|
68
166
|
|
|
167
|
+
// Wrapper for images with placeholders
|
|
168
|
+
const imageWithPlaceholder = (placeholder || showSkeleton) ? (
|
|
169
|
+
<div style={{ position: 'relative', display: 'inline-block' }}>
|
|
170
|
+
{skeletonElement}
|
|
171
|
+
{placeholderElement}
|
|
172
|
+
{imgElement}
|
|
173
|
+
</div>
|
|
174
|
+
) : imgElement;
|
|
175
|
+
|
|
69
176
|
// Handle asChild - inject img into the child element
|
|
70
177
|
if (asChild && children) {
|
|
71
178
|
const child = React.Children.only(children) as React.ReactElement<any>;
|
|
@@ -113,10 +220,18 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
113
220
|
<img
|
|
114
221
|
data-radius={radius}
|
|
115
222
|
loading={loading}
|
|
116
|
-
style={{
|
|
223
|
+
style={{
|
|
224
|
+
...style,
|
|
225
|
+
position: 'relative',
|
|
226
|
+
zIndex: 1,
|
|
227
|
+
opacity: imageLoaded ? 1 : 0,
|
|
228
|
+
transition: 'opacity 0.3s ease-out',
|
|
229
|
+
}}
|
|
117
230
|
className={classNames('rt-reset', 'rt-Image', 'rt-Image--blur', className)}
|
|
118
231
|
alt={alt}
|
|
119
232
|
src={src}
|
|
233
|
+
onLoad={handleLoad}
|
|
234
|
+
onError={handleError}
|
|
120
235
|
{...imgProps}
|
|
121
236
|
ref={forwardedRef}
|
|
122
237
|
/>
|
|
@@ -137,7 +252,7 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
137
252
|
cursor: 'pointer', // Ensure interactive cursor
|
|
138
253
|
...child.props?.style, // Allow user overrides
|
|
139
254
|
},
|
|
140
|
-
children:
|
|
255
|
+
children: imageWithPlaceholder,
|
|
141
256
|
});
|
|
142
257
|
}
|
|
143
258
|
}
|
|
@@ -171,10 +286,18 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
171
286
|
<img
|
|
172
287
|
data-radius={radius}
|
|
173
288
|
loading={loading}
|
|
174
|
-
style={{
|
|
289
|
+
style={{
|
|
290
|
+
...style,
|
|
291
|
+
position: 'relative',
|
|
292
|
+
zIndex: 1,
|
|
293
|
+
opacity: imageLoaded ? 1 : 0,
|
|
294
|
+
transition: 'opacity 0.3s ease-out',
|
|
295
|
+
}}
|
|
175
296
|
className={classNames('rt-reset', 'rt-Image', 'rt-Image--blur', className)}
|
|
176
297
|
alt={alt}
|
|
177
298
|
src={src}
|
|
299
|
+
onLoad={handleLoad}
|
|
300
|
+
onError={handleError}
|
|
178
301
|
{...imgProps}
|
|
179
302
|
ref={forwardedRef}
|
|
180
303
|
/>
|
|
@@ -182,7 +305,7 @@ const Image = React.forwardRef<ImageElement, ImageProps>((props, forwardedRef) =
|
|
|
182
305
|
);
|
|
183
306
|
}
|
|
184
307
|
|
|
185
|
-
return
|
|
308
|
+
return imageWithPlaceholder;
|
|
186
309
|
});
|
|
187
310
|
|
|
188
311
|
Image.displayName = 'Image';
|
package/styles.css
CHANGED
|
@@ -10925,6 +10925,9 @@
|
|
|
10925
10925
|
.rt-r-shadow-6 {
|
|
10926
10926
|
--box-shadow: var(--shadow-6);
|
|
10927
10927
|
}
|
|
10928
|
+
.rt-Image--placeholder {
|
|
10929
|
+
z-index: 0;
|
|
10930
|
+
}
|
|
10928
10931
|
.rt-Inset {
|
|
10929
10932
|
box-sizing: border-box;
|
|
10930
10933
|
--margin-top: 0px;
|