Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .storybook/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,9 @@
"@alfalab/core-components-vars": ["../packages/vars/src"],
"@alfalab/core-components-vars/*": ["../packages/vars/src/*"],
"@alfalab/core-components-with-suffix": ["../packages/with-suffix/src"],
"@alfalab/core-components-with-suffix/*": ["../packages/with-suffix/src/*"]
"@alfalab/core-components-with-suffix/*": ["../packages/with-suffix/src/*"],
"@alfalab/core-components-image": ["../packages/image/src"],
"@alfalab/core-components-image/*": ["../packages/image/src/*"]
}
}
}
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
"packages/underlay",
"packages/universal-date-input",
"packages/universal-modal",
"packages/with-suffix"
"packages/with-suffix",
"packages/image"
]
}
3 changes: 3 additions & 0 deletions packages/config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
"dependencies": {
"tslib": "^2.4.0"
},
"devDependencies": {
"@alfalab/core-components-image": "^0.0.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.1 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.1 || ^18.0.0"
Expand Down
31 changes: 29 additions & 2 deletions packages/config/src/CoreConfigContext.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import { createContext, useContext } from 'react';

import { type ImageProps } from '@alfalab/core-components-image';

export type CoreConfigContextValue = {
breakpoint: number;
client: 'desktop' | 'mobile';
breakpoint?: number;
client?: 'desktop' | 'mobile';
components?: CoreConfigContextComponents;
};

// Добавить типы из компонентов нет возможности, так как будет зацикленный импорт зависимостей
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type CoreConfigContextComponents = {
Image: Partial<ImageProps>;
};

export const CoreConfigContext = createContext<CoreConfigContextValue>({
breakpoint: 1024,
client: 'desktop',
components: {
Image: {},
},
});

export const useCoreConfig = (overrides: Partial<CoreConfigContextValue> = {}) => {
Expand All @@ -21,3 +33,18 @@ export const useCoreConfig = (overrides: Partial<CoreConfigContextValue> = {}) =
...passedOverrides,
};
};

/**
* Получение глобальных настроек для компонента
*/
export const useComponentOverrides = <T extends object>(
component: keyof CoreConfigContextComponents,
): T | undefined => {
const context = useCoreConfig();

if (context?.components && context.components[component]) {
return context.components[component];
}

return undefined;
};
7 changes: 5 additions & 2 deletions packages/config/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
"@alfalab/core-components-screenshot-utils": ["../screenshot-utils/src"],
"@alfalab/core-components-screenshot-utils/*": ["../screenshot-utils/src/*"],
"@alfalab/core-components-test-utils": ["../test-utils/src"],
"@alfalab/core-components-test-utils/*": ["../test-utils/src/*"]
"@alfalab/core-components-test-utils/*": ["../test-utils/src/*"],
"@alfalab/core-components-image": ["../image/src"],
"@alfalab/core-components-image/*": ["../image/src/*"]
}
},
"references": [
{ "path": "../screenshot-utils/tsconfig.build.json" },
{ "path": "../test-utils/tsconfig.build.json" }
{ "path": "../test-utils/tsconfig.build.json" },
{ "path": "../image/tsconfig.build.json" }
]
}
20 changes: 20 additions & 0 deletions packages/image/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@alfalab/core-components-image",
"version": "1.0.0",
"description": "",
"keywords": [],
"license": "MIT",
"main": "index.js",
"module": "./esm/index.js",
"publishConfig": {
"access": "public",
"directory": "dist"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.1 || ^18.0.0"
},
"dependencies": {
"tslib": "^2.4.0",
"@alfalab/core-components-config": "^0.0.0"
}
}
83 changes: 83 additions & 0 deletions packages/image/src/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { type ImgHTMLAttributes, memo, useMemo } from 'react';
import cn from 'classnames';

import { useComponentOverrides } from '@alfalab/core-components-config';

import { useInViewRef } from './hooks/use-in-view-ref';

import styles from './index.module.css';

export type ImageProxyMap = Array<{ from: string; to: string }>;

// FOR TEST
const prodResourcesMap: ImageProxyMap = [
{
from: 'https://web.alfabank.ru/mobile',
to: 'https://alfaonline.servicecdn.ru/public',
},
];
// FOR TEST
const devResourcesMap: ImageProxyMap = [
{
from: 'https://web-test.alfabank.ru/mobile',
to: 'https://web-test.servicecdn.ru/public',
},
{
from: 'https://web.alfabank.ru/mobile',
to: 'https://alfaonline.servicecdn.ru/public',
},
];

export type ImageProps = {
/** Источник изображения */
src: string;
/** Сss класс для стилизации общей обёртки */
className?: string;
/** Загружать ли изображение, если оно находится в viewPort пользователя */
inViewOption?: boolean;
/** Id компонента для тестов */
dataTestId?: string;
/** Карта проксирующих ресурсов */
proxyMap?: ImageProxyMap;
} & ImgHTMLAttributes<unknown>;
/**
* Компонент, который проксирует картинки из источников
* Также смотрящий за viewPort пользователя, загружая то что видит человек
*
* Например из https://web-test.alfabank.ru/mobile/s3/static/loyalty/services/travel_300x300.png => https://web-test.servicecdn.ru/public/s3/static/loyalty/services/travel_300x300.png
*
* [Макет]()
*/
export const Image = memo(
({ className, dataTestId, src, proxyMap, inViewOption, ...props }: ImageProps) => {
const componentContext = useComponentOverrides<ImageProps>('Image');
const [imgRef, inView] = useInViewRef({
inViewOption: inViewOption || componentContext?.inViewOption,
});

const url = useMemo(() => {
const map = proxyMap || componentContext?.proxyMap || [];

const resourseMap = map.find((resourse) => src.includes(resourse.from));

if (resourseMap) {
return src.replace(resourseMap.from, resourseMap.to);
}

return src;
}, [src, componentContext?.proxyMap, proxyMap]);

return (
<img
alt=''
{...props}
src={inView ? url : undefined}
className={cn(className, {
[styles.fullSize]: Boolean(props.style?.objectFit),
})}
data-test-id={dataTestId}
ref={imgRef}
/>
);
},
);
44 changes: 44 additions & 0 deletions packages/image/src/docs/Component.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { CoreConfigContext } from '@alfalab/core-components-config';

import { Image } from '..';

const meta: Meta<typeof Image> = {
title: 'Components/Image',
component: Image,
id: 'Image',
};

type Story = StoryObj<typeof Image>;

export const image: Story = {
name: 'Image',
render: () => {
return (
<CoreConfigContext.Provider
value={{
components: {
Image: {
inViewOption: true,
proxyMap: [
{
from: 'https://web.alfabank.ru/mobile',
to: 'https://alfaonline.servicecdn.ru/public',
},
],
},
},
}}
>
<div>1</div>
<div style={{ height: '1000px' }}></div>
<Image src='https://web-test.alfabank.ru/mobile/s3/static/loyalty/services/travel_300x300.png' />
<div style={{ height: '300px' }}></div>
<Image src='https://web.alfabank.ru/mobile/s3/static/loyalty/widget/pforfit_492%D1%85469.png' />
</CoreConfigContext.Provider>
);
},
};

export default meta;
37 changes: 37 additions & 0 deletions packages/image/src/hooks/use-in-view-ref/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useEffect, useRef, useState } from 'react';

const defaultConfig: IntersectionObserverInit = {
root: null,
rootMargin: '0px 0px 0px 0px',
threshold: [0, 1],
};

type Params = {
inViewOption?: boolean;
} & Partial<IntersectionObserverInit>;

export const useInViewRef = (options?: Params): [React.MutableRefObject<null>, boolean] => {
const enabled = options?.inViewOption;
const [inView, setInView] = useState<boolean>(!enabled);
const ref = useRef(null);

// eslint-disable-next-line consistent-return
useEffect(() => {
if (ref.current && enabled) {
const observer = new IntersectionObserver(([entry], observer) => {
if (entry.isIntersecting) {
setInView(entry.isIntersecting);
observer.disconnect();
}
}, options || defaultConfig);

observer.observe(ref.current);

return () => {
observer.disconnect();
};
}
}, [options, ref.current]);

return [ref, inView];
};
4 changes: 4 additions & 0 deletions packages/image/src/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.fullSize {
width: 100%;
height: 100%;
}
1 change: 1 addition & 0 deletions packages/image/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './component';
16 changes: 16 additions & 0 deletions packages/image/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@alfalab/core-components-env/tsconfig.base.json",
"include": ["src", "src/**/*.json"],
"exclude": ["**/*.stories.*", "**/*.test.*"],
"compilerOptions": {
"rootDir": "src",
"outDir": "ts-dist",
"paths": {
"@alfalab/core-components-image": ["./src"],
"@alfalab/core-components-image/*": ["./src/*"],
"@alfalab/core-components-config": ["../config/src"],
"@alfalab/core-components-config/*": ["../config/src/*"]
}
}
}
20 changes: 20 additions & 0 deletions packages/image/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@alfalab/core-components-env/tsconfig.base.json",
"include": ["src", "src/**/*.json"],
"compilerOptions": {
"rootDir": "src",
"outDir": "no-dist",
"paths": {
"@alfalab/core-components-image": ["./src"],
"@alfalab/core-components-image/*": ["./src/*"],
"@alfalab/core-components-config": ["../config/src"],
"@alfalab/core-components-config/*": ["../config/src/*"]
}
},
"references": [
{ "path": "../config/tsconfig.build.json" },
{ "path": "../screenshot-utils/tsconfig.build.json" },
{ "path": "../test-utils/tsconfig.build.json" }
]
}
Loading