@lumx/react 2.1.9-alpha-thumbnail → 2.1.9-alpha-thumbnail2
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.
|
@@ -75,7 +75,7 @@ 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
76
|
var x = (((focusPoint === null || focusPoint === void 0 ? void 0 : focusPoint.x) || 0) + 1) / 2 * 100;
|
|
77
77
|
var y = (((focusPoint === null || focusPoint === void 0 ? void 0 : focusPoint.y) || 0) - 1) / 2 * 100;
|
|
78
|
-
return "".concat(x, "% ").concat(y, "%");
|
|
78
|
+
return "".concat(Math.round(x), "% ").concat(Math.round(y), "%");
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* Thumbnail component.
|
|
@@ -148,10 +148,8 @@ var Thumbnail = forwardRef(function (props, ref) {
|
|
|
148
148
|
className: "".concat(CLASSNAME, "__background")
|
|
149
149
|
}, React.createElement("img", _extends({}, imgProps, {
|
|
150
150
|
style: _objectSpread2({}, imgProps === null || imgProps === void 0 ? void 0 : imgProps.style, {
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
// Hide while loading.
|
|
154
|
-
visibility: hasError || hasError && isLoading ? 'hidden' : undefined,
|
|
151
|
+
// Hide on error.
|
|
152
|
+
visibility: hasError ? 'hidden' : undefined,
|
|
155
153
|
// Focus point.
|
|
156
154
|
objectPosition: getObjectPosition(aspectRatio, focusPoint)
|
|
157
155
|
}),
|
|
@@ -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 `${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 //\n //display: hasError && (!imgProps?.width || !imgProps?.height) ? 'none' : undefined,\n // Hide while loading.\n visibility: hasError || (hasError && isLoading) ? '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","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 = (((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":";;;;;;;;AAIA,SAASA,QAAT,CAAkBC,GAAlB,EAA4DC,KAA5D,EAA2E;AACvE;AACA,MAAI,CAAAA,KAAK,SAAL,IAAAA,KAAK,WAAL,YAAAA,KAAK,CAAEC,IAAP,MAAgB,OAAhB,IAA4B,CAAAF,GAAG,SAAH,IAAAA,GAAG,WAAH,YAAAA,GAAG,CAAEG,QAAL,MAAkB,CAAAH,GAAG,SAAH,IAAAA,GAAG,WAAH,YAAAA,GAAG,CAAEI,YAAL,MAAsB,CAAtB,IAA2B,CAAAJ,GAAG,SAAH,IAAAA,GAAG,WAAH,YAAAA,GAAG,CAAEK,aAAL,MAAuB,CAApE,CAAhC,EAAyG;AACrG,WAAO,UAAP;AACH,GAJsE;;;AAMvE,MAAI,CAACL,GAAD,IAAQ,CAACA,GAAG,CAACG,QAAjB,EAA2B;AACvB,WAAO,WAAP;AACH,GARsE;;;AAUvE,SAAO,UAAP;AACH;;AAEM,SAASG,YAAT,CAAsBC,QAAtB,EAAwCC,MAAxC,EAA4F;AAAA;;AAAA,kBACrEC,QAAQ,CAAeV,QAAQ,CAACS,MAAD,aAACA,MAAD,uBAACA,MAAM,CAAEE,OAAT,CAAvB,CAD6D;AAAA;AAAA,MACxFC,KADwF;AAAA,MACjFC,QADiF;;;AAI/FC,EAAAA,SAAS,CAAC,YAAM;AACZD,IAAAA,QAAQ,CAACb,QAAQ,CAACS,MAAD,aAACA,MAAD,uBAACA,MAAM,CAAEE,OAAT,CAAT,CAAR;AACH,GAFQ,EAEN,CAACH,QAAD,EAAWC,MAAX,CAFM,CAAT,CAJ+F;;AAS/FK,EAAAA,SAAS,CAAC,YAAM;AACZ,QAAMb,GAAG,GAAGQ,MAAH,aAAGA,MAAH,uBAAGA,MAAM,CAAEE,OAApB;AACA,QAAI,CAACV,GAAL,EAAU,OAAOc,SAAP;;AACV,QAAMC,MAAM,GAAG,SAATA,MAAS,CAACd,KAAD;AAAA,aAAmBW,QAAQ,CAACb,QAAQ,CAACC,GAAD,EAAMC,KAAN,CAAT,CAA3B;AAAA,KAAf;;AACAD,IAAAA,GAAG,CAACgB,gBAAJ,CAAqB,MAArB,EAA6BD,MAA7B;AACAf,IAAAA,GAAG,CAACgB,gBAAJ,CAAqB,OAArB,EAA8BD,MAA9B;AACA,WAAO,YAAM;AACTf,MAAAA,GAAG,CAACiB,mBAAJ,CAAwB,MAAxB,EAAgCF,MAAhC;AACAf,MAAAA,GAAG,CAACiB,mBAAJ,CAAwB,OAAxB,EAAiCF,MAAjC;AACH,KAHD;AAIH,GAVQ,EAUN,CAACP,MAAD,EAASA,MAAT,aAASA,MAAT,0CAASA,MAAM,CAAEE,OAAjB,oDAAS,gBAAiBQ,GAA1B,CAVM,CAAT;AAYA,SAAOP,KAAP;AACH;;AC8BD;;;AAGA,IAAMQ,cAAc,GAAG,WAAvB;AAEA;;;;AAGA,IAAMC,SAAS,GAAGC,gBAAgB,CAACF,cAAD,CAAlC;AAEA;;;;AAGA,IAAMG,aAAsC,GAAG;AAC3CC,EAAAA,QAAQ,EAAEC,cADiC;AAE3CC,EAAAA,OAAO,EAAE,MAFkC;AAG3CC,EAAAA,KAAK,EAAEC,KAAK,CAACC;AAH8B,CAA/C;;AAMA,SAASC,iBAAT,CAA2BC,WAA3B,EAAqDC,UAArD,EAA8E;AAC1E,MAAID,WAAW,KAAKE,WAAW,CAACC,QAA5B,IAAyC,EAACF,UAAD,aAACA,UAAD,uBAACA,UAAU,CAAEG,CAAb,KAAkB,EAACH,UAAD,aAACA,UAAD,uBAACA,UAAU,CAAEI,CAAb,CAA/D,EAAgF,OAAOrB,SAAP;AAChF,MAAMqB,CAAC,GAAI,CAAC,CAAC,CAAAJ,UAAU,SAAV,IAAAA,UAAU,WAAV,YAAAA,UAAU,CAAEI,CAAZ,KAAiB,CAAlB,IAAuB,CAAxB,IAA6B,CAA9B,GAAmC,GAA7C;AACA,MAAMD,CAAC,GAAI,CAAC,CAAC,CAAAH,UAAU,SAAV,IAAAA,UAAU,WAAV,YAAAA,UAAU,CAAEG,CAAZ,KAAiB,CAAlB,IAAuB,CAAxB,IAA6B,CAA9B,GAAmC,GAA7C;AACA,mBAAUE,IAAI,CAACC,KAAL,CAAWF,CAAX,CAAV,eAA4BC,IAAI,CAACC,KAAL,CAAWH,CAAX,CAA5B;AACH;AAED;;;;;;;;;IAOaI,SAA+B,GAAGC,UAAU,CAAC,UAACC,KAAD,EAAQC,GAAR,EAAgB;AAAA,MAElEC,KAFkE,GAuBlEF,KAvBkE,CAElEE,KAFkE;AAAA,MAGlEC,GAHkE,GAuBlEH,KAvBkE,CAGlEG,GAHkE;AAAA,2BAuBlEH,KAvBkE,CAIlEV,WAJkE;AAAA,MAIlEA,WAJkE,mCAIpDE,WAAW,CAACC,QAJwC;AAAA,MAKlEW,KALkE,GAuBlEJ,KAvBkE,CAKlEI,KALkE;AAAA,MAMlEC,SANkE,GAuBlEL,KAvBkE,CAMlEK,SANkE;AAAA,MAOlEC,WAPkE,GAuBlEN,KAvBkE,CAOlEM,WAPkE;AAAA,MAQlEvB,QARkE,GAuBlEiB,KAvBkE,CAQlEjB,QARkE;AAAA,MASlEwB,UATkE,GAuBlEP,KAvBkE,CASlEO,UATkE;AAAA,MAUlEhB,UAVkE,GAuBlES,KAvBkE,CAUlET,UAVkE;AAAA,MAWlEiB,KAXkE,GAuBlER,KAvBkE,CAWlEQ,KAXkE;AAAA,MAYlEC,QAZkE,GAuBlET,KAvBkE,CAYlES,QAZkE;AAAA,MAa1DC,UAb0D,GAuBlEV,KAvBkE,CAalEhC,MAbkE;AAAA,MAcvD2C,aAduD,GAuBlEX,KAvBkE,CAclEY,SAdkE;AAAA,MAelE3B,OAfkE,GAuBlEe,KAvBkE,CAelEf,OAfkE;AAAA,MAgBlE4B,IAhBkE,GAuBlEb,KAvBkE,CAgBlEa,IAhBkE;AAAA,MAiBlE3B,KAjBkE,GAuBlEc,KAvBkE,CAiBlEd,KAjBkE;AAAA,MAkBlE4B,OAlBkE,GAuBlEd,KAvBkE,CAkBlEc,OAlBkE;AAAA,MAmBlEC,SAnBkE,GAuBlEf,KAvBkE,CAmBlEe,SAnBkE;AAAA,MAoBlEC,MApBkE,GAuBlEhB,KAvBkE,CAoBlEgB,MApBkE;AAAA,8BAuBlEhB,KAvBkE,CAqBlEiB,mBArBkE;AAAA,MAqBlEA,mBArBkE,sCAqB5C,IArB4C;AAAA,MAsB/DC,cAtB+D,4BAuBlElB,KAvBkE;;AAwBtE,MAAMhC,MAAM,GAAGmD,MAAM,CAAmB,IAAnB,CAArB,CAxBsE;;AA2BtE,MAAMC,YAAY,GAAGtD,YAAY,CAAC0C,KAAD,EAAQxC,MAAR,CAAjC;AACA,MAAM4C,SAAS,GAAGD,aAAa,IAAIS,YAAY,KAAK,WAApD;AACA,MAAMC,QAAQ,GAAGD,YAAY,KAAK,UAAlC;AAEA,MAAME,MAAM,GAAGC,OAAO,CAAC,CAAAR,SAAS,SAAT,IAAAA,SAAS,WAAT,YAAAA,SAAS,CAAES,IAAX,KAAmBR,MAApB,CAAtB;AACA,MAAMS,QAAQ,GAAG,CAAC,CAACP,cAAc,CAACQ,OAAlC;AACA,MAAMC,WAAW,GAAGF,QAAQ,IAAIH,MAAhC;AAEA,MAAIM,OAAY,GAAG,KAAnB;;AACA,MAAMC,YAAY,sBAAQX,cAAR,CAAlB;;AACA,MAAII,MAAJ,EAAY;AACRM,IAAAA,OAAO,GAAGZ,MAAM,IAAI,GAApB;AACAc,IAAAA,MAAM,CAACC,MAAP,CAAcF,YAAd,EAA4Bd,SAA5B;AACH,GAHD,MAGO,IAAIU,QAAJ,EAAc;AACjBG,IAAAA,OAAO,GAAG,QAAV;AACH;;AAED,SACI,oBAAC,OAAD,eACQC,YADR;AAEI,IAAA,GAAG,EAAE5B,GAFT;AAGI,IAAA,SAAS,EAAE+B,UAAU,CACjBjB,SADiB,aACjBA,SADiB,uBACjBA,SAAS,CAAEV,SADM,EAEjBA,SAFiB,EAGjB4B,kBAAkB,CAAC;AACf/B,MAAAA,KAAK,EAALA,KADe;AAEfZ,MAAAA,WAAW,EAAXA,WAFe;AAGf4C,MAAAA,MAAM,EAAEtD,SAHO;AAIfiC,MAAAA,IAAI,EAAJA,IAJe;AAKf3B,MAAAA,KAAK,EAALA,KALe;AAMf4B,MAAAA,OAAO,EAAPA,OANe;AAOfa,MAAAA,WAAW,EAAXA,WAPe;AAQfN,MAAAA,QAAQ,EAARA,QARe;AASfT,MAAAA,SAAS,EAAEK,mBAAmB,IAAIL,SATnB;AAUfuB,MAAAA,QAAQ,EAAE,CAAC,CAAC/B;AAVG,KAAD,CAHD,EAejBG,UAAU,cAAO3B,SAAP,kBAfO;AAHzB,MAqBI;AAAK,IAAA,SAAS,YAAKA,SAAL;AAAd,KACI,wCACQ6B,QADR;AAEI,IAAA,KAAK,qBACEA,QADF,aACEA,QADF,uBACEA,QAAQ,CAAE2B,KADZ;AAED;AACAC,MAAAA,UAAU,EAAEhB,QAAQ,GAAG,QAAH,GAAc/C,SAHjC;AAID;AACAgE,MAAAA,cAAc,EAAEjD,iBAAiB,CAACC,WAAD,EAAcC,UAAd;AALhC,MAFT;AASI,IAAA,GAAG,EAAEgD,SAAS,CAACvE,MAAD,EAAS0C,UAAT,CATlB;AAUI,IAAA,SAAS,EAAEsB,UAAU,WAAIpD,SAAJ,cAAwBgC,SAAS,cAAOhC,SAAP,wBAAjC,CAVzB;AAWI,IAAA,WAAW,EAAE0B,WAXjB;AAYI,IAAA,GAAG,EAAEE,KAZT;AAaI,IAAA,GAAG,EAAEL,GAbT;AAcI,IAAA,OAAO,EAAElB;AAdb,KADJ,EAiBK,CAAC2B,SAAD,IAAcS,QAAd,IACG;AAAK,IAAA,SAAS,YAAKzC,SAAL;AAAd,KACK,OAAOG,QAAP,KAAoB,QAApB,GACG,oBAAC,IAAD;AAAM,IAAA,IAAI,EAAEA,QAAZ;AAAsB,IAAA,IAAI,EAAEyD,IAAI,CAACC,GAAjC;AAAsC,IAAA,KAAK,EAAEvD;AAA7C,IADH,GAGGH,QAJR,CAlBR,CArBJ,EAgDKqB,KAAK,IACFsC,KAAK,CAACC,YAAN,CAAmBvC,KAAnB,EAA0B;AAAEC,IAAAA,SAAS,EAAE2B,UAAU,WAAIpD,SAAJ,cAAwBwB,KAAK,CAACJ,KAAN,CAAYK,SAApC;AAAvB,GAA1B,CAjDR,CADJ;AAqDH,CAjGwD;AAkGzDP,SAAS,CAAC8C,WAAV,GAAwBjE,cAAxB;AACAmB,SAAS,CAACO,SAAV,GAAsBzB,SAAtB;AACAkB,SAAS,CAAC+C,YAAV,GAAyB/D,aAAzB;;;;"}
|
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-thumbnail2",
|
|
11
|
+
"@lumx/icons": "^2.1.9-alpha-thumbnail2",
|
|
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-thumbnail2",
|
|
124
|
+
"gitHead": "105e60082267526f6ac979d2c2d725417a1d6190"
|
|
125
125
|
}
|
|
@@ -90,7 +90,7 @@ function getObjectPosition(aspectRatio: AspectRatio, focusPoint?: FocusPoint) {
|
|
|
90
90
|
if (aspectRatio === AspectRatio.original || (!focusPoint?.y && !focusPoint?.x)) return undefined;
|
|
91
91
|
const x = (((focusPoint?.x || 0) + 1) / 2) * 100;
|
|
92
92
|
const y = (((focusPoint?.y || 0) - 1) / 2) * 100;
|
|
93
|
-
return `${x}% ${y}%`;
|
|
93
|
+
return `${Math.round(x)}% ${Math.round(y)}%`;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
@@ -171,10 +171,8 @@ export const Thumbnail: Comp<ThumbnailProps> = forwardRef((props, ref) => {
|
|
|
171
171
|
{...imgProps}
|
|
172
172
|
style={{
|
|
173
173
|
...imgProps?.style,
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
// Hide while loading.
|
|
177
|
-
visibility: hasError || (hasError && isLoading) ? 'hidden' : undefined,
|
|
174
|
+
// Hide on error.
|
|
175
|
+
visibility: hasError ? 'hidden' : undefined,
|
|
178
176
|
// Focus point.
|
|
179
177
|
objectPosition: getObjectPosition(aspectRatio, focusPoint),
|
|
180
178
|
}}
|