@lumx/react 2.1.9-alpha-thumbnail2 → 2.1.9-alpha-thumbnail3
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/esm/_internal/ImageBlock.js +0 -1
- package/esm/_internal/ImageBlock.js.map +1 -1
- package/esm/_internal/Thumbnail2.js +3 -3
- package/esm/_internal/Thumbnail2.js.map +1 -1
- package/package.json +4 -4
- package/src/components/image-block/ImageBlock.tsx +0 -1
- package/src/components/thumbnail/Thumbnail.stories.tsx +70 -14
- package/src/components/thumbnail/Thumbnail.tsx +3 -3
|
@@ -68,7 +68,6 @@ var ImageBlock = forwardRef(function (props, ref) {
|
|
|
68
68
|
}), fillHeight && "".concat(CLASSNAME, "--fill-height"))
|
|
69
69
|
}), React.createElement(Thumbnail, _extends({}, thumbnailProps, {
|
|
70
70
|
className: classnames("".concat(CLASSNAME, "__image"), thumbnailProps === null || thumbnailProps === void 0 ? void 0 : thumbnailProps.className),
|
|
71
|
-
fillHeight: fillHeight,
|
|
72
71
|
align: align,
|
|
73
72
|
image: image,
|
|
74
73
|
size: size,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageBlock.js","sources":["../../../src/components/image-block/ImageBlock.tsx"],"sourcesContent":["import React, { CSSProperties, forwardRef, ReactNode } from 'react';\n\nimport classNames from 'classnames';\n\nimport isObject from 'lodash/isObject';\n\nimport { Alignment, HorizontalAlignment, Size, Theme, Thumbnail } from '@lumx/react';\n\nimport { Comp, GenericProps, getRootClassName, handleBasicClasses, ValueOf } from '@lumx/react/utils';\nimport { ThumbnailProps } from '../thumbnail/Thumbnail';\n\n/**\n * Image block variants.\n */\nexport const ImageBlockCaptionPosition = {\n below: 'below',\n over: 'over',\n} as const;\nexport type ImageBlockCaptionPosition = ValueOf<typeof ImageBlockCaptionPosition>;\n\n/**\n * Image block sizes.\n */\nexport type ImageBlockSize = Extract<Size, 'xl' | 'xxl'>;\n\n/**\n * Defines the props of the component.\n */\nexport interface ImageBlockProps extends GenericProps {\n /** Action toolbar content. */\n actions?: ReactNode;\n /** Alignment. */\n align?: HorizontalAlignment;\n /** Image alternative text. */\n alt: string;\n /** Caption position. */\n captionPosition?: ImageBlockCaptionPosition;\n /** Caption custom CSS style. */\n captionStyle?: CSSProperties;\n /** Image description. Can be either a string, or sanitized html. */\n description?: string | { __html: string };\n /** Whether the image has to fill its container height or not. */\n fillHeight?: boolean;\n /** Image URL. */\n image: string;\n /** Size variant. */\n size?: ImageBlockSize;\n /** Tag content. */\n tags?: ReactNode;\n /** Theme adapting the component to light or dark background. */\n theme?: Theme;\n /** Props to pass to the thumbnail (minus those already set by the ImageBlock props). */\n thumbnailProps?: Omit<ThumbnailProps, 'image' | 'size' | 'theme' | 'align' | 'fillHeight'>;\n /** Image title to display in the caption. */\n title?: string;\n}\n\n/**\n * Component display name.\n */\nconst COMPONENT_NAME = 'ImageBlock';\n\n/**\n * Component default class name and class prefix.\n */\nconst CLASSNAME = getRootClassName(COMPONENT_NAME);\n\n/**\n * Component default props.\n */\nconst DEFAULT_PROPS: Partial<ImageBlockProps> = {\n captionPosition: ImageBlockCaptionPosition.below,\n theme: Theme.light,\n align: Alignment.left,\n};\n\n/**\n * ImageBlock component.\n *\n * @param props Component props.\n * @param ref Component ref.\n * @return React element.\n */\nexport const ImageBlock: Comp<ImageBlockProps, HTMLDivElement> = forwardRef((props, ref) => {\n const {\n actions,\n align,\n alt,\n captionPosition,\n captionStyle,\n className,\n description,\n fillHeight,\n image,\n size,\n tags,\n theme,\n thumbnailProps,\n title,\n ...forwardedProps\n } = props;\n return (\n <figure\n ref={ref}\n {...forwardedProps}\n className={classNames(\n className,\n handleBasicClasses({\n prefix: CLASSNAME,\n captionPosition,\n align,\n size,\n theme,\n }),\n fillHeight && `${CLASSNAME}--fill-height`,\n )}\n >\n <Thumbnail\n {...thumbnailProps}\n className={classNames(`${CLASSNAME}__image`, thumbnailProps?.className)}\n
|
|
1
|
+
{"version":3,"file":"ImageBlock.js","sources":["../../../src/components/image-block/ImageBlock.tsx"],"sourcesContent":["import React, { CSSProperties, forwardRef, ReactNode } from 'react';\n\nimport classNames from 'classnames';\n\nimport isObject from 'lodash/isObject';\n\nimport { Alignment, HorizontalAlignment, Size, Theme, Thumbnail } from '@lumx/react';\n\nimport { Comp, GenericProps, getRootClassName, handleBasicClasses, ValueOf } from '@lumx/react/utils';\nimport { ThumbnailProps } from '../thumbnail/Thumbnail';\n\n/**\n * Image block variants.\n */\nexport const ImageBlockCaptionPosition = {\n below: 'below',\n over: 'over',\n} as const;\nexport type ImageBlockCaptionPosition = ValueOf<typeof ImageBlockCaptionPosition>;\n\n/**\n * Image block sizes.\n */\nexport type ImageBlockSize = Extract<Size, 'xl' | 'xxl'>;\n\n/**\n * Defines the props of the component.\n */\nexport interface ImageBlockProps extends GenericProps {\n /** Action toolbar content. */\n actions?: ReactNode;\n /** Alignment. */\n align?: HorizontalAlignment;\n /** Image alternative text. */\n alt: string;\n /** Caption position. */\n captionPosition?: ImageBlockCaptionPosition;\n /** Caption custom CSS style. */\n captionStyle?: CSSProperties;\n /** Image description. Can be either a string, or sanitized html. */\n description?: string | { __html: string };\n /** Whether the image has to fill its container height or not. */\n fillHeight?: boolean;\n /** Image URL. */\n image: string;\n /** Size variant. */\n size?: ImageBlockSize;\n /** Tag content. */\n tags?: ReactNode;\n /** Theme adapting the component to light or dark background. */\n theme?: Theme;\n /** Props to pass to the thumbnail (minus those already set by the ImageBlock props). */\n thumbnailProps?: Omit<ThumbnailProps, 'image' | 'size' | 'theme' | 'align' | 'fillHeight'>;\n /** Image title to display in the caption. */\n title?: string;\n}\n\n/**\n * Component display name.\n */\nconst COMPONENT_NAME = 'ImageBlock';\n\n/**\n * Component default class name and class prefix.\n */\nconst CLASSNAME = getRootClassName(COMPONENT_NAME);\n\n/**\n * Component default props.\n */\nconst DEFAULT_PROPS: Partial<ImageBlockProps> = {\n captionPosition: ImageBlockCaptionPosition.below,\n theme: Theme.light,\n align: Alignment.left,\n};\n\n/**\n * ImageBlock component.\n *\n * @param props Component props.\n * @param ref Component ref.\n * @return React element.\n */\nexport const ImageBlock: Comp<ImageBlockProps, HTMLDivElement> = forwardRef((props, ref) => {\n const {\n actions,\n align,\n alt,\n captionPosition,\n captionStyle,\n className,\n description,\n fillHeight,\n image,\n size,\n tags,\n theme,\n thumbnailProps,\n title,\n ...forwardedProps\n } = props;\n return (\n <figure\n ref={ref}\n {...forwardedProps}\n className={classNames(\n className,\n handleBasicClasses({\n prefix: CLASSNAME,\n captionPosition,\n align,\n size,\n theme,\n }),\n fillHeight && `${CLASSNAME}--fill-height`,\n )}\n >\n <Thumbnail\n {...thumbnailProps}\n className={classNames(`${CLASSNAME}__image`, thumbnailProps?.className)}\n align={align}\n image={image}\n size={size}\n theme={theme}\n alt={(alt || title) as string}\n />\n {(title || description || tags) && (\n <figcaption className={`${CLASSNAME}__wrapper`} style={captionStyle}>\n {(title || description) && (\n <div className={`${CLASSNAME}__caption`}>\n {title && <span className={`${CLASSNAME}__title`}>{title}</span>}\n {/* Add an ` ` when there is description and title. */}\n {title && description && '\\u00A0'}\n {isObject(description) && description.__html ? (\n // eslint-disable-next-line react/no-danger\n <span dangerouslySetInnerHTML={description} className={`${CLASSNAME}__description`} />\n ) : (\n <span className={`${CLASSNAME}__description`}>{description}</span>\n )}\n </div>\n )}\n {tags && <div className={`${CLASSNAME}__tags`}>{tags}</div>}\n </figcaption>\n )}\n {actions && <div className={`${CLASSNAME}__actions`}>{actions}</div>}\n </figure>\n );\n});\nImageBlock.displayName = COMPONENT_NAME;\nImageBlock.className = CLASSNAME;\nImageBlock.defaultProps = DEFAULT_PROPS;\n"],"names":["ImageBlockCaptionPosition","below","over","COMPONENT_NAME","CLASSNAME","getRootClassName","DEFAULT_PROPS","captionPosition","theme","Theme","light","align","Alignment","left","ImageBlock","forwardRef","props","ref","actions","alt","captionStyle","className","description","fillHeight","image","size","tags","thumbnailProps","title","forwardedProps","classNames","handleBasicClasses","prefix","isObject","__html","displayName","defaultProps"],"mappings":"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"}
|
|
@@ -73,9 +73,9 @@ var DEFAULT_PROPS = {
|
|
|
73
73
|
|
|
74
74
|
function getObjectPosition(aspectRatio, focusPoint) {
|
|
75
75
|
if (aspectRatio === AspectRatio.original || !(focusPoint === null || focusPoint === void 0 ? void 0 : focusPoint.y) && !(focusPoint === null || focusPoint === void 0 ? void 0 : focusPoint.x)) return undefined;
|
|
76
|
-
var x = (((focusPoint === null || focusPoint === void 0 ? void 0 : focusPoint.x) || 0) + 1) / 2 * 100;
|
|
77
|
-
var y = (((focusPoint === null || focusPoint === void 0 ? void 0 : focusPoint.y) || 0) - 1) / 2 * 100;
|
|
78
|
-
return "".concat(
|
|
76
|
+
var x = Math.round(Math.abs((((focusPoint === null || focusPoint === void 0 ? void 0 : focusPoint.x) || 0) + 1) / 2) * 100);
|
|
77
|
+
var y = Math.round(Math.abs((((focusPoint === null || focusPoint === void 0 ? void 0 : focusPoint.y) || 0) - 1) / 2) * 100);
|
|
78
|
+
return "".concat(x, "% ").concat(y, "%");
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* Thumbnail component.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Thumbnail2.js","sources":["../../../src/components/thumbnail/useImageLoad.ts","../../../src/components/thumbnail/Thumbnail.tsx"],"sourcesContent":["import { RefObject, useEffect, useState } from 'react';\n\nexport type LoadingState = 'isLoading' | 'isLoaded' | 'hasError';\n\nfunction getState(img: HTMLImageElement | null | undefined, event?: Event) {\n // Error event occurred or image loaded empty.\n if (event?.type === 'error' || (img?.complete && (img?.naturalWidth === 0 || img?.naturalHeight === 0))) {\n return 'hasError';\n }\n // Image is undefined or incomplete.\n if (!img || !img.complete) {\n return 'isLoading';\n }\n // Else loaded.\n return 'isLoaded';\n}\n\nexport function useImageLoad(imageURL: string, imgRef?: RefObject<HTMLImageElement>): LoadingState {\n const [state, setState] = useState<LoadingState>(getState(imgRef?.current));\n\n // Update state when changing image URL or DOM reference.\n useEffect(() => {\n setState(getState(imgRef?.current));\n }, [imageURL, imgRef]);\n\n // Listen to `load` and `error` event on image\n useEffect(() => {\n const img = imgRef?.current;\n if (!img) return undefined;\n const update = (event?: Event) => setState(getState(img, event));\n img.addEventListener('load', update);\n img.addEventListener('error', update);\n return () => {\n img.removeEventListener('load', update);\n img.removeEventListener('error', update);\n };\n }, [imgRef, imgRef?.current?.src]);\n\n return state;\n}\n","import React, {\n forwardRef,\n ImgHTMLAttributes,\n KeyboardEventHandler,\n MouseEventHandler,\n ReactElement,\n ReactNode,\n Ref,\n useRef,\n} from 'react';\nimport classNames from 'classnames';\n\nimport { AspectRatio, HorizontalAlignment, Icon, Size, Theme } from '@lumx/react';\n\nimport { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';\n\nimport { mdiImageBroken } from '@lumx/icons';\nimport { mergeRefs } from '@lumx/react/utils/mergeRefs';\nimport { useImageLoad } from '@lumx/react/components/thumbnail/useImageLoad';\nimport { FocusPoint, ThumbnailSize, ThumbnailVariant } from './types';\n\ntype ImgHTMLProps = ImgHTMLAttributes<HTMLImageElement>;\n\n/**\n * Defines the props of the component.\n */\nexport interface ThumbnailProps extends GenericProps {\n /** Alignment of the thumbnail in it's parent (requires flex parent). */\n align?: HorizontalAlignment;\n /** Image alternative text. */\n alt: string;\n /** Image aspect ratio. */\n aspectRatio?: AspectRatio;\n /** Badge. */\n badge?: ReactElement;\n /** Image cross origin resource policy. */\n crossOrigin?: ImgHTMLProps['crossOrigin'];\n /** Fallback icon (SVG path) or react node when image fails to load. */\n fallback?: string | ReactNode;\n /** Whether the thumbnail should fill it's parent size (requires flex parent) or not. */\n fillHeight?: boolean;\n /** Apply relative vertical and horizontal shift (from -1 to 1) on the image position inside the thumbnail. */\n focusPoint?: FocusPoint;\n /** Image URL. */\n image: string;\n /** Props to inject into the native <img> element. */\n imgProps?: ImgHTMLProps;\n /** Reference to the native <img> element. */\n imgRef?: Ref<HTMLImageElement>;\n /** Set to true to force the display of the loading skeleton. */\n isLoading?: boolean;\n /** Size variant of the component. */\n size?: ThumbnailSize;\n /** Image loading mode. */\n loading?: ImgHTMLProps['loading'];\n /** On click callback. */\n onClick?: MouseEventHandler<HTMLDivElement>;\n /** On key press callback. */\n onKeyPress?: KeyboardEventHandler<HTMLDivElement>;\n /** Theme adapting the component to light or dark background. */\n theme?: Theme;\n /** Variant of the component. */\n variant?: ThumbnailVariant;\n /** Props to pass to the link wrapping the thumbnail. */\n linkProps?: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;\n /** Custom react component for the link (can be used to inject react router Link). */\n linkAs?: 'a' | any;\n}\n\n/**\n * Component display name.\n */\nconst COMPONENT_NAME = 'Thumbnail';\n\n/**\n * Component default class name and class prefix.\n */\nconst CLASSNAME = getRootClassName(COMPONENT_NAME);\n\n/**\n * Component default props.\n */\nconst DEFAULT_PROPS: Partial<ThumbnailProps> = {\n fallback: mdiImageBroken,\n loading: 'lazy',\n theme: Theme.light,\n};\n\nfunction getObjectPosition(aspectRatio: AspectRatio, focusPoint?: FocusPoint) {\n if (aspectRatio === AspectRatio.original || (!focusPoint?.y && !focusPoint?.x)) return undefined;\n const x = (((focusPoint?.x || 0) + 1) / 2) * 100;\n const y = (((focusPoint?.y || 0) - 1) / 2) * 100;\n return `${Math.round(x)}% ${Math.round(y)}%`;\n}\n\n/**\n * Thumbnail component.\n *\n * @param props Component props.\n * @param ref Component ref.\n * @return React element.\n */\nexport const Thumbnail: Comp<ThumbnailProps> = forwardRef((props, ref) => {\n const {\n align,\n alt,\n aspectRatio = AspectRatio.original,\n badge,\n className,\n crossOrigin,\n fallback,\n fillHeight,\n focusPoint,\n image,\n imgProps,\n imgRef: propImgRef,\n isLoading: isLoadingProp,\n loading,\n size,\n theme,\n variant,\n linkProps,\n linkAs,\n showSkeletonLoading = true,\n ...forwardedProps\n } = props;\n const imgRef = useRef<HTMLImageElement>(null);\n\n // Image loading state.\n const loadingState = useImageLoad(image, imgRef);\n const isLoading = isLoadingProp || loadingState === 'isLoading';\n const hasError = loadingState === 'hasError';\n\n const isLink = Boolean(linkProps?.href || linkAs);\n const isButton = !!forwardedProps.onClick;\n const isClickable = isButton || isLink;\n\n let Wrapper: any = 'div';\n const wrapperProps = { ...forwardedProps };\n if (isLink) {\n Wrapper = linkAs || 'a';\n Object.assign(wrapperProps, linkProps);\n } else if (isButton) {\n Wrapper = 'button';\n }\n\n return (\n <Wrapper\n {...wrapperProps}\n ref={ref}\n className={classNames(\n linkProps?.className,\n className,\n handleBasicClasses({\n align,\n aspectRatio,\n prefix: CLASSNAME,\n size,\n theme,\n variant,\n isClickable,\n hasError,\n isLoading: showSkeletonLoading && isLoading,\n hasBadge: !!badge,\n }),\n fillHeight && `${CLASSNAME}--fill-height`,\n )}\n >\n <div className={`${CLASSNAME}__background`}>\n <img\n {...imgProps}\n style={{\n ...imgProps?.style,\n // Hide on error.\n visibility: hasError ? 'hidden' : undefined,\n // Focus point.\n objectPosition: getObjectPosition(aspectRatio, focusPoint),\n }}\n ref={mergeRefs(imgRef, propImgRef)}\n className={classNames(`${CLASSNAME}__image`, isLoading && `${CLASSNAME}__image--is-loading`)}\n crossOrigin={crossOrigin}\n src={image}\n alt={alt}\n loading={loading}\n />\n {!isLoading && hasError && (\n <div className={`${CLASSNAME}__fallback`}>\n {typeof fallback === 'string' ? (\n <Icon icon={fallback} size={Size.xxs} theme={theme} />\n ) : (\n fallback\n )}\n </div>\n )}\n </div>\n {badge &&\n React.cloneElement(badge, { className: classNames(`${CLASSNAME}__badge`, badge.props.className) })}\n </Wrapper>\n );\n});\nThumbnail.displayName = COMPONENT_NAME;\nThumbnail.className = CLASSNAME;\nThumbnail.defaultProps = DEFAULT_PROPS;\n"],"names":["getState","img","event","type","complete","naturalWidth","naturalHeight","useImageLoad","imageURL","imgRef","useState","current","state","setState","useEffect","undefined","update","addEventListener","removeEventListener","src","COMPONENT_NAME","CLASSNAME","getRootClassName","DEFAULT_PROPS","fallback","mdiImageBroken","loading","theme","Theme","light","getObjectPosition","aspectRatio","focusPoint","AspectRatio","original","y","x","Math","round","Thumbnail","forwardRef","props","ref","align","alt","badge","className","crossOrigin","fillHeight","image","imgProps","propImgRef","isLoadingProp","isLoading","size","variant","linkProps","linkAs","showSkeletonLoading","forwardedProps","useRef","loadingState","hasError","isLink","Boolean","href","isButton","onClick","isClickable","Wrapper","wrapperProps","Object","assign","classNames","handleBasicClasses","prefix","hasBadge","style","visibility","objectPosition","mergeRefs","Size","xxs","React","cloneElement","displayName","defaultProps"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Thumbnail2.js","sources":["../../../src/components/thumbnail/useImageLoad.ts","../../../src/components/thumbnail/Thumbnail.tsx"],"sourcesContent":["import { RefObject, useEffect, useState } from 'react';\n\nexport type LoadingState = 'isLoading' | 'isLoaded' | 'hasError';\n\nfunction getState(img: HTMLImageElement | null | undefined, event?: Event) {\n // Error event occurred or image loaded empty.\n if (event?.type === 'error' || (img?.complete && (img?.naturalWidth === 0 || img?.naturalHeight === 0))) {\n return 'hasError';\n }\n // Image is undefined or incomplete.\n if (!img || !img.complete) {\n return 'isLoading';\n }\n // Else loaded.\n return 'isLoaded';\n}\n\nexport function useImageLoad(imageURL: string, imgRef?: RefObject<HTMLImageElement>): LoadingState {\n const [state, setState] = useState<LoadingState>(getState(imgRef?.current));\n\n // Update state when changing image URL or DOM reference.\n useEffect(() => {\n setState(getState(imgRef?.current));\n }, [imageURL, imgRef]);\n\n // Listen to `load` and `error` event on image\n useEffect(() => {\n const img = imgRef?.current;\n if (!img) return undefined;\n const update = (event?: Event) => setState(getState(img, event));\n img.addEventListener('load', update);\n img.addEventListener('error', update);\n return () => {\n img.removeEventListener('load', update);\n img.removeEventListener('error', update);\n };\n }, [imgRef, imgRef?.current?.src]);\n\n return state;\n}\n","import React, {\n forwardRef,\n ImgHTMLAttributes,\n KeyboardEventHandler,\n MouseEventHandler,\n ReactElement,\n ReactNode,\n Ref,\n useRef,\n} from 'react';\nimport classNames from 'classnames';\n\nimport { AspectRatio, HorizontalAlignment, Icon, Size, Theme } from '@lumx/react';\n\nimport { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';\n\nimport { mdiImageBroken } from '@lumx/icons';\nimport { mergeRefs } from '@lumx/react/utils/mergeRefs';\nimport { useImageLoad } from '@lumx/react/components/thumbnail/useImageLoad';\nimport { FocusPoint, ThumbnailSize, ThumbnailVariant } from './types';\n\ntype ImgHTMLProps = ImgHTMLAttributes<HTMLImageElement>;\n\n/**\n * Defines the props of the component.\n */\nexport interface ThumbnailProps extends GenericProps {\n /** Alignment of the thumbnail in it's parent (requires flex parent). */\n align?: HorizontalAlignment;\n /** Image alternative text. */\n alt: string;\n /** Image aspect ratio. */\n aspectRatio?: AspectRatio;\n /** Badge. */\n badge?: ReactElement;\n /** Image cross origin resource policy. */\n crossOrigin?: ImgHTMLProps['crossOrigin'];\n /** Fallback icon (SVG path) or react node when image fails to load. */\n fallback?: string | ReactNode;\n /** Whether the thumbnail should fill it's parent size (requires flex parent) or not. */\n fillHeight?: boolean;\n /** Apply relative vertical and horizontal shift (from -1 to 1) on the image position inside the thumbnail. */\n focusPoint?: FocusPoint;\n /** Image URL. */\n image: string;\n /** Props to inject into the native <img> element. */\n imgProps?: ImgHTMLProps;\n /** Reference to the native <img> element. */\n imgRef?: Ref<HTMLImageElement>;\n /** Set to true to force the display of the loading skeleton. */\n isLoading?: boolean;\n /** Size variant of the component. */\n size?: ThumbnailSize;\n /** Image loading mode. */\n loading?: ImgHTMLProps['loading'];\n /** On click callback. */\n onClick?: MouseEventHandler<HTMLDivElement>;\n /** On key press callback. */\n onKeyPress?: KeyboardEventHandler<HTMLDivElement>;\n /** Theme adapting the component to light or dark background. */\n theme?: Theme;\n /** Variant of the component. */\n variant?: ThumbnailVariant;\n /** Props to pass to the link wrapping the thumbnail. */\n linkProps?: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;\n /** Custom react component for the link (can be used to inject react router Link). */\n linkAs?: 'a' | any;\n}\n\n/**\n * Component display name.\n */\nconst COMPONENT_NAME = 'Thumbnail';\n\n/**\n * Component default class name and class prefix.\n */\nconst CLASSNAME = getRootClassName(COMPONENT_NAME);\n\n/**\n * Component default props.\n */\nconst DEFAULT_PROPS: Partial<ThumbnailProps> = {\n fallback: mdiImageBroken,\n loading: 'lazy',\n theme: Theme.light,\n};\n\nfunction getObjectPosition(aspectRatio: AspectRatio, focusPoint?: FocusPoint) {\n if (aspectRatio === AspectRatio.original || (!focusPoint?.y && !focusPoint?.x)) return undefined;\n const x = Math.round(Math.abs(((focusPoint?.x || 0) + 1) / 2) * 100);\n const y = Math.round(Math.abs(((focusPoint?.y || 0) - 1) / 2) * 100);\n return `${x}% ${y}%`;\n}\n\n/**\n * Thumbnail component.\n *\n * @param props Component props.\n * @param ref Component ref.\n * @return React element.\n */\nexport const Thumbnail: Comp<ThumbnailProps> = forwardRef((props, ref) => {\n const {\n align,\n alt,\n aspectRatio = AspectRatio.original,\n badge,\n className,\n crossOrigin,\n fallback,\n fillHeight,\n focusPoint,\n image,\n imgProps,\n imgRef: propImgRef,\n isLoading: isLoadingProp,\n loading,\n size,\n theme,\n variant,\n linkProps,\n linkAs,\n showSkeletonLoading = true,\n ...forwardedProps\n } = props;\n const imgRef = useRef<HTMLImageElement>(null);\n\n // Image loading state.\n const loadingState = useImageLoad(image, imgRef);\n const isLoading = isLoadingProp || loadingState === 'isLoading';\n const hasError = loadingState === 'hasError';\n\n const isLink = Boolean(linkProps?.href || linkAs);\n const isButton = !!forwardedProps.onClick;\n const isClickable = isButton || isLink;\n\n let Wrapper: any = 'div';\n const wrapperProps = { ...forwardedProps };\n if (isLink) {\n Wrapper = linkAs || 'a';\n Object.assign(wrapperProps, linkProps);\n } else if (isButton) {\n Wrapper = 'button';\n }\n\n return (\n <Wrapper\n {...wrapperProps}\n ref={ref}\n className={classNames(\n linkProps?.className,\n className,\n handleBasicClasses({\n align,\n aspectRatio,\n prefix: CLASSNAME,\n size,\n theme,\n variant,\n isClickable,\n hasError,\n isLoading: showSkeletonLoading && isLoading,\n hasBadge: !!badge,\n }),\n fillHeight && `${CLASSNAME}--fill-height`,\n )}\n >\n <div className={`${CLASSNAME}__background`}>\n <img\n {...imgProps}\n style={{\n ...imgProps?.style,\n // Hide on error.\n visibility: hasError ? 'hidden' : undefined,\n // Focus point.\n objectPosition: getObjectPosition(aspectRatio, focusPoint),\n }}\n ref={mergeRefs(imgRef, propImgRef)}\n className={classNames(`${CLASSNAME}__image`, isLoading && `${CLASSNAME}__image--is-loading`)}\n crossOrigin={crossOrigin}\n src={image}\n alt={alt}\n loading={loading}\n />\n {!isLoading && hasError && (\n <div className={`${CLASSNAME}__fallback`}>\n {typeof fallback === 'string' ? (\n <Icon icon={fallback} size={Size.xxs} theme={theme} />\n ) : (\n fallback\n )}\n </div>\n )}\n </div>\n {badge &&\n React.cloneElement(badge, { className: classNames(`${CLASSNAME}__badge`, badge.props.className) })}\n </Wrapper>\n );\n});\nThumbnail.displayName = COMPONENT_NAME;\nThumbnail.className = CLASSNAME;\nThumbnail.defaultProps = DEFAULT_PROPS;\n"],"names":["getState","img","event","type","complete","naturalWidth","naturalHeight","useImageLoad","imageURL","imgRef","useState","current","state","setState","useEffect","undefined","update","addEventListener","removeEventListener","src","COMPONENT_NAME","CLASSNAME","getRootClassName","DEFAULT_PROPS","fallback","mdiImageBroken","loading","theme","Theme","light","getObjectPosition","aspectRatio","focusPoint","AspectRatio","original","y","x","Math","round","abs","Thumbnail","forwardRef","props","ref","align","alt","badge","className","crossOrigin","fillHeight","image","imgProps","propImgRef","isLoadingProp","isLoading","size","variant","linkProps","linkAs","showSkeletonLoading","forwardedProps","useRef","loadingState","hasError","isLink","Boolean","href","isButton","onClick","isClickable","Wrapper","wrapperProps","Object","assign","classNames","handleBasicClasses","prefix","hasBadge","style","visibility","objectPosition","mergeRefs","Size","xxs","React","cloneElement","displayName","defaultProps"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@juggle/resize-observer": "^3.2.0",
|
|
10
|
-
"@lumx/core": "^2.1.9-alpha-
|
|
11
|
-
"@lumx/icons": "^2.1.9-alpha-
|
|
10
|
+
"@lumx/core": "^2.1.9-alpha-thumbnail3",
|
|
11
|
+
"@lumx/icons": "^2.1.9-alpha-thumbnail3",
|
|
12
12
|
"@popperjs/core": "^2.5.4",
|
|
13
13
|
"body-scroll-lock": "^3.1.5",
|
|
14
14
|
"classnames": "^2.2.6",
|
|
@@ -120,6 +120,6 @@
|
|
|
120
120
|
"build:storybook": "cd storybook && ./build"
|
|
121
121
|
},
|
|
122
122
|
"sideEffects": false,
|
|
123
|
-
"version": "2.1.9-alpha-
|
|
124
|
-
"gitHead": "
|
|
123
|
+
"version": "2.1.9-alpha-thumbnail3",
|
|
124
|
+
"gitHead": "e57c222583d0f3fff98a3934a16a754302c88e3d"
|
|
125
125
|
}
|
|
@@ -118,7 +118,6 @@ export const ImageBlock: Comp<ImageBlockProps, HTMLDivElement> = forwardRef((pro
|
|
|
118
118
|
<Thumbnail
|
|
119
119
|
{...thumbnailProps}
|
|
120
120
|
className={classNames(`${CLASSNAME}__image`, thumbnailProps?.className)}
|
|
121
|
-
fillHeight={fillHeight}
|
|
122
121
|
align={align}
|
|
123
122
|
image={image}
|
|
124
123
|
size={size}
|
|
@@ -20,19 +20,6 @@ import { sizeKnob } from '@lumx/react/stories/knobs/sizeKnob';
|
|
|
20
20
|
import { action } from '@storybook/addon-actions';
|
|
21
21
|
import classNames from 'classnames';
|
|
22
22
|
|
|
23
|
-
const knobAspectRatio = () => {
|
|
24
|
-
const ratiosProps = {
|
|
25
|
-
Original: {},
|
|
26
|
-
'Original (with natural size)': { imgNaturalSize: { width: 800, height: 600 } },
|
|
27
|
-
'Free (with fill height)': { aspectRatio: AspectRatio.free },
|
|
28
|
-
Horizontal: { aspectRatio: AspectRatio.horizontal },
|
|
29
|
-
Wide: { aspectRatio: AspectRatio.wide },
|
|
30
|
-
Vertical: { aspectRatio: AspectRatio.vertical },
|
|
31
|
-
Square: { aspectRatio: AspectRatio.square },
|
|
32
|
-
} as const;
|
|
33
|
-
return select('Aspect ratio', ratiosProps as any, ratiosProps.Original as any);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
23
|
export default { title: 'LumX components/thumbnail/Thumbnail' };
|
|
37
24
|
|
|
38
25
|
/** Default thumbnail props (editable via knobs) */
|
|
@@ -121,7 +108,13 @@ export const ClickableCustomLink = () => (
|
|
|
121
108
|
);
|
|
122
109
|
|
|
123
110
|
export const FillHeight = () => {
|
|
124
|
-
const parentStyle = {
|
|
111
|
+
const parentStyle = {
|
|
112
|
+
width: 600,
|
|
113
|
+
height: 240,
|
|
114
|
+
border: '1px solid red',
|
|
115
|
+
overflow: 'hidden',
|
|
116
|
+
resize: 'both',
|
|
117
|
+
} as const;
|
|
125
118
|
return (
|
|
126
119
|
<>
|
|
127
120
|
<h2>Default</h2>
|
|
@@ -213,6 +206,11 @@ export const Original = () => (
|
|
|
213
206
|
export const Vertical = () => (
|
|
214
207
|
<>
|
|
215
208
|
<h1>Ratio: vertical</h1>
|
|
209
|
+
<h2>Default</h2>
|
|
210
|
+
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
211
|
+
<Thumbnail alt="" aspectRatio="vertical" image={IMAGES.landscape1} />
|
|
212
|
+
<Thumbnail alt="" aspectRatio="vertical" image={IMAGES.portrait1} />
|
|
213
|
+
</FlexBox>
|
|
216
214
|
<h2>Constraint parent size</h2>
|
|
217
215
|
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
218
216
|
<div className="parent" style={{ width: 220 }}>
|
|
@@ -261,6 +259,11 @@ export const Vertical = () => (
|
|
|
261
259
|
export const Wide = () => (
|
|
262
260
|
<>
|
|
263
261
|
<h1>Ratio: wide</h1>
|
|
262
|
+
<h2>Default</h2>
|
|
263
|
+
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
264
|
+
<Thumbnail alt="" aspectRatio="wide" image={IMAGES.landscape1} />
|
|
265
|
+
<Thumbnail alt="" aspectRatio="wide" image={IMAGES.portrait1} />
|
|
266
|
+
</FlexBox>
|
|
264
267
|
<h2>Constrained parent size</h2>
|
|
265
268
|
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
266
269
|
<div className="parent" style={{ width: 220 }}>
|
|
@@ -306,6 +309,59 @@ export const Wide = () => (
|
|
|
306
309
|
</>
|
|
307
310
|
);
|
|
308
311
|
|
|
312
|
+
export const Square = () => (
|
|
313
|
+
<>
|
|
314
|
+
<h1>Ratio: square</h1>
|
|
315
|
+
<h2>Default</h2>
|
|
316
|
+
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
317
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1} />
|
|
318
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1} />
|
|
319
|
+
</FlexBox>
|
|
320
|
+
<h2>Constrained parent size</h2>
|
|
321
|
+
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
322
|
+
<div className="parent" style={{ width: 220 }}>
|
|
323
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1} />
|
|
324
|
+
</div>
|
|
325
|
+
<div className="parent" style={{ width: 220 }}>
|
|
326
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1} />
|
|
327
|
+
</div>
|
|
328
|
+
</FlexBox>
|
|
329
|
+
<h2>Constrained parent size & smaller image</h2>
|
|
330
|
+
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
331
|
+
<div className="parent" style={{ width: 220 }}>
|
|
332
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1s200} />
|
|
333
|
+
</div>
|
|
334
|
+
<div className="parent" style={{ width: 220 }}>
|
|
335
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1s200} />
|
|
336
|
+
</div>
|
|
337
|
+
</FlexBox>
|
|
338
|
+
<h2>With size</h2>
|
|
339
|
+
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
340
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1} size="xxl" />
|
|
341
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1} size="xxl" />
|
|
342
|
+
</FlexBox>
|
|
343
|
+
<h2>With size & smaller image</h2>
|
|
344
|
+
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
345
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1s200} size="xxl" />
|
|
346
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1s200} size="xxl" />
|
|
347
|
+
</FlexBox>
|
|
348
|
+
<h2>With size & smaller image & fill height</h2>
|
|
349
|
+
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
350
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1s200} size="xxl" fillHeight />
|
|
351
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1s200} size="xxl" fillHeight />
|
|
352
|
+
</FlexBox>
|
|
353
|
+
<h2>Constrained parent size & smaller image & fill height</h2>
|
|
354
|
+
<FlexBox orientation="horizontal" vAlign="center" gap="huge">
|
|
355
|
+
<div className="parent" style={{ width: 220 }}>
|
|
356
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1s200} fillHeight />
|
|
357
|
+
</div>
|
|
358
|
+
<div className="parent" style={{ width: 220 }}>
|
|
359
|
+
<Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1s200} fillHeight />
|
|
360
|
+
</div>
|
|
361
|
+
</FlexBox>
|
|
362
|
+
</>
|
|
363
|
+
);
|
|
364
|
+
|
|
309
365
|
export const ParentSizeConstraint = () => {
|
|
310
366
|
const fillHeight = boolean('Fill Height', true);
|
|
311
367
|
return Object.values(AspectRatio).map((aspectRatio) => (
|
|
@@ -88,9 +88,9 @@ const DEFAULT_PROPS: Partial<ThumbnailProps> = {
|
|
|
88
88
|
|
|
89
89
|
function getObjectPosition(aspectRatio: AspectRatio, focusPoint?: FocusPoint) {
|
|
90
90
|
if (aspectRatio === AspectRatio.original || (!focusPoint?.y && !focusPoint?.x)) return undefined;
|
|
91
|
-
const x = (((focusPoint?.x || 0) + 1) / 2) * 100;
|
|
92
|
-
const y = (((focusPoint?.y || 0) - 1) / 2) * 100;
|
|
93
|
-
return `${
|
|
91
|
+
const x = Math.round(Math.abs(((focusPoint?.x || 0) + 1) / 2) * 100);
|
|
92
|
+
const y = Math.round(Math.abs(((focusPoint?.y || 0) - 1) / 2) * 100);
|
|
93
|
+
return `${x}% ${y}%`;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|