diff --git a/packages/vkui/src/components/ImageBase/ImageBase.module.css b/packages/vkui/src/components/ImageBase/ImageBase.module.css index 85b435c010..3ddf965ac7 100644 --- a/packages/vkui/src/components/ImageBase/ImageBase.module.css +++ b/packages/vkui/src/components/ImageBase/ImageBase.module.css @@ -71,6 +71,15 @@ object-fit: scale-down; } +.withObjectPosition { + --vkui_internal--ImageBase_object_position_default: 50% 50%; + + object-position: var( + --vkui_internal--ImageBase_object_position, + var(--vkui_internal--ImageBase_object_position_default) + ); +} + .loaded .img { visibility: visible; } diff --git a/packages/vkui/src/components/ImageBase/ImageBase.test.tsx b/packages/vkui/src/components/ImageBase/ImageBase.test.tsx index 80b16256a3..f36b0c1c45 100644 --- a/packages/vkui/src/components/ImageBase/ImageBase.test.tsx +++ b/packages/vkui/src/components/ImageBase/ImageBase.test.tsx @@ -220,6 +220,15 @@ describe(ImageBase, () => { render(} size={28} />); expect(logStub).toHaveBeenCalledTimes(1); }); + + it('should apply custom objectPosition style', () => { + render(); + + expect(getImageBaseImgEl()).toHaveClass(styles.withObjectPosition); + expect(getImageBaseImgEl()).toHaveStyle({ + '--vkui_internal--ImageBase_object_position': 'center bottom', + }); + }); }); describe(getOverlayIconSizeByImageBaseSize, () => { diff --git a/packages/vkui/src/components/ImageBase/ImageBase.tsx b/packages/vkui/src/components/ImageBase/ImageBase.tsx index d8c1a9f279..5fd019b0ae 100644 --- a/packages/vkui/src/components/ImageBase/ImageBase.tsx +++ b/packages/vkui/src/components/ImageBase/ImageBase.tsx @@ -3,10 +3,17 @@ import { useRef } from 'react'; import * as React from 'react'; import { classNames } from '@vkontakte/vkjs'; +import { mergeStyle } from '../../helpers/mergeStyle'; import { useExternRef } from '../../hooks/useExternRef'; import { minOr } from '../../lib/comparing'; import { getFetchPriorityProp } from '../../lib/utils'; -import type { AnchorHTMLAttributesOnly, HasRef, HasRootRef, LiteralUnion } from '../../types'; +import type { + AnchorHTMLAttributesOnly, + CSSCustomProperties, + HasRef, + HasRootRef, + LiteralUnion, +} from '../../types'; import { Clickable } from '../Clickable/Clickable'; import { ImageBaseBadge, type ImageBaseBadgeProps } from './ImageBaseBadge/ImageBaseBadge'; import { @@ -94,6 +101,11 @@ export interface ImageBaseProps * Подробнее можно почитать в [документации](https://developer.mozilla.org/ru/docs/Web/CSS/object-fit) */ objectFit?: React.CSSProperties['objectFit']; + /** + * Пользовательское значения стиля object-position + * Подробнее можно почитать в [документации](https://developer.mozilla.org/ru/docs/Web/CSS/object-position) + */ + objectPosition?: React.CSSProperties['objectPosition']; /** * Флаг для сохранения пропорций картинки. * Для корректной работы необходимо задать размеры хотя бы одной стороны картинки @@ -160,6 +172,7 @@ export const ImageBase: React.FC & { onError, withTransparentBackground, objectFit = 'cover', + objectPosition, keepAspectRatio = false, getRootRef, ...restProps @@ -238,6 +251,19 @@ export const ImageBase: React.FC & { [size], ); + const imgStyles: CSSCustomProperties | undefined = objectPosition + ? { + '--vkui_internal--ImageBase_object_position': objectPosition, + } + : undefined; + + const keepAspectRationStyles = keepAspectRatio + ? { + width: widthImg || width, + height: heightImg || height, + } + : undefined; + return ( & { className={classNames( styles.img, getObjectFitClassName(objectFit), + objectPosition && styles.withObjectPosition, keepAspectRatio && styles.imgKeepRatio, )} crossOrigin={crossOrigin} decoding={decoding} loading={loading} referrerPolicy={referrerPolicy} - style={ - keepAspectRatio - ? { - width: widthImg || width, - height: heightImg || height, - } - : undefined - } + style={mergeStyle(keepAspectRationStyles, imgStyles)} sizes={sizes} src={src} srcSet={srcSet}