import React, { forwardRef, SyntheticEvent, useEffect, useState } from 'react';
import { useWebMergeStyles } from '@cleartrip/ct-design-style-manager';
import type { Styles } from '@cleartrip/ct-design-types';
import NextImage from 'next/image';
import { IImageProps, IImageWebRendererProps } from './type';
import staticImageStyles from './style';
/**
* Default web renderer. Emits a plain `
` so the design-system
* package stays framework-agnostic. Consumers that want `next/image`
* (or any other image optimizer) can inject it via the `Component`
* prop — the injected component is handed the same minimal surface
* described by `IImageWebRendererProps`.
*/
const DefaultImgComponent = forwardRef(
({ src, alt, width, height, className, onClick, onLoad, onError }, ref) => (
),
);
DefaultImgComponent.displayName = 'ImageDefaultRenderer';
const NextImageComponent = forwardRef(
(
{
src,
alt,
width,
height,
className,
onClick,
onLoad,
onError,
priority,
loading,
blurDataURL,
unoptimized,
fetchPriority,
},
ref,
) => (
),
);
NextImageComponent.displayName = 'NextImageComponent';
/**
* Web implementation of `Image`.
*
* Migrated from yagami (aldenui) per `Migration.MD`:
* - `next/image` coupling dropped; the renderer is injectable via
* the `Component` prop (defaults to a semantic `
`).
* - `useWebMergeStyles` composes caller-provided `styleConfig.root`
* slots into a single className string handed to the renderer.
* - Fallback-URL handling mirrors the yagami behavior bit-for-bit:
* initial `src || fallbackUrl`, and onError swaps in `fallbackUrl`
* once (guarded by `imageSource !== fallbackUrl` to prevent an
* infinite error loop when the fallback itself 404s).
*
* The ref is forwarded to the underlying `HTMLImageElement` so
* consumers can still measure or imperatively trigger loads.
*/
const Image = forwardRef(
(
{
src,
alt,
width,
height,
fallbackUrl,
styleConfig,
onClick,
onLoad,
onError,
Component,
priority,
loading,
blurDataURL,
unoptimized,
fetchPriority,
},
ref,
) => {
const [imageSource, setImageSource] = useState(src || fallbackUrl || '');
const { root: customRootStyles = [] } = styleConfig || {};
// Note: customRootStyles is a `ViewStyle`-shaped array; the
// style-manager flattens it into real CSS at resolution time.
const mergedRootClassName = useWebMergeStyles(
[staticImageStyles.root as Styles, ...(customRootStyles as unknown as Styles[])],
[staticImageStyles.root, customRootStyles],
);
useEffect(() => {
setImageSource(src || fallbackUrl || '');
}, [fallbackUrl, src]);
const onImageError = (e: SyntheticEvent) => {
if (fallbackUrl && imageSource !== fallbackUrl) {
setImageSource(fallbackUrl);
}
if (onError) {
onError(e);
}
};
// The default renderer is a `forwardRef`-ed `
` so the ref
// passes straight through. User-injected renderers SHOULD also be
// forwardRef-wrapped (next/image is); we widen the type here so
// the `ref` attribute is accepted unconditionally.
const Renderer = (Component || NextImageComponent) as React.ComponentType<
IImageWebRendererProps & { ref?: React.Ref }
>;
return (
);
},
);
Image.displayName = 'Image';
export default Image;