From 7f33575da595f74a6935bd976a691a0ec769dc6d Mon Sep 17 00:00:00 2001 From: Alexei Skutov Date: Fri, 17 Apr 2026 15:57:31 +0300 Subject: [PATCH 1/3] feat(broken-default-image): add broken image logic --- packages/file-upload-item/src/Component.tsx | 3 + .../extension-icon/index.test.tsx | 63 ++++++++++ .../status-control/extension-icon/index.tsx | 8 +- .../src/components/status-control/index.tsx | 3 +- .../src/context/file-upload-item-context.ts | 4 + .../base-shape/check-image-is-broken.test.ts | 119 ++++++++++++++++++ .../base-shape/check-image-is-broken.ts | 48 +++++++ .../src/components/base-shape/component.tsx | 45 ++++++- 8 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 packages/file-upload-item/src/components/status-control/extension-icon/index.test.tsx create mode 100644 packages/icon-view/src/components/base-shape/check-image-is-broken.test.ts create mode 100644 packages/icon-view/src/components/base-shape/check-image-is-broken.ts diff --git a/packages/file-upload-item/src/Component.tsx b/packages/file-upload-item/src/Component.tsx index f04ed5d9a9..0ece569624 100644 --- a/packages/file-upload-item/src/Component.tsx +++ b/packages/file-upload-item/src/Component.tsx @@ -40,6 +40,7 @@ export const FileUploadItemComponent: React.FC = ({ backgroundColor, }) => { const [actionsPresent, setActionsPresent] = useState(false); + const [isBrokenImage, setIsBrokenImage] = useState(false); return (
= ({ customContent, truncate, imageUrl, + isBrokenImage, + setIsBrokenImage, backgroundColor, actionsPresent, setActionsPresent, diff --git a/packages/file-upload-item/src/components/status-control/extension-icon/index.test.tsx b/packages/file-upload-item/src/components/status-control/extension-icon/index.test.tsx new file mode 100644 index 0000000000..53d9d5f069 --- /dev/null +++ b/packages/file-upload-item/src/components/status-control/extension-icon/index.test.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +import { FileUploadItemContext } from '../../../context/file-upload-item-context'; + +import { ExtensionIcon } from '.'; + +jest.mock('@alfalab/icons-glyph/DocumentImageOffMIcon', () => ({ + DocumentImageOffMIcon: () =>
, +})); + +describe('ExtensionIcon', () => { + it('should hide icon when image is valid', () => { + const CustomIcon = () =>
; + + render( + + + , + ); + + expect(screen.queryByTestId('custom-icon')).not.toBeInTheDocument(); + }); + + it('should show fallback icon when image is broken', () => { + render( + + + , + ); + + expect(screen.getByTestId('broken-image-icon')).toBeInTheDocument(); + }); + + it('should keep custom icon priority for broken image', () => { + const CustomIcon = () =>
; + + render( + + + , + ); + + expect(screen.getByTestId('custom-icon')).toBeInTheDocument(); + }); +}); diff --git a/packages/file-upload-item/src/components/status-control/extension-icon/index.tsx b/packages/file-upload-item/src/components/status-control/extension-icon/index.tsx index df681b92b7..07daf53aa8 100644 --- a/packages/file-upload-item/src/components/status-control/extension-icon/index.tsx +++ b/packages/file-upload-item/src/components/status-control/extension-icon/index.tsx @@ -5,6 +5,7 @@ import { Document1CMIcon } from '@alfalab/icons-glyph/Document1CMIcon'; import { DocumentDocMIcon } from '@alfalab/icons-glyph/DocumentDocMIcon'; import { DocumentExcelMIcon } from '@alfalab/icons-glyph/DocumentExcelMIcon'; import { DocumentImageMIcon } from '@alfalab/icons-glyph/DocumentImageMIcon'; +import { DocumentImageOffMIcon } from '@alfalab/icons-glyph/DocumentImageOffMIcon'; import { DocumentMIcon } from '@alfalab/icons-glyph/DocumentMIcon'; import { DocumentOffMIcon } from '@alfalab/icons-glyph/DocumentOffMIcon'; import { DocumentPdfMIcon } from '@alfalab/icons-glyph/DocumentPdfMIcon'; @@ -22,13 +23,18 @@ export const ExtensionIcon = () => { iconStyle, customIcon: CustomIcon, imageUrl, + isBrokenImage, showRestore, } = useContext(FileUploadItemContext); - if (imageUrl) { + if (imageUrl && !isBrokenImage) { return null; } + if (imageUrl && isBrokenImage && !CustomIcon) { + return ; + } + if (CustomIcon) { return ; } diff --git a/packages/file-upload-item/src/components/status-control/index.tsx b/packages/file-upload-item/src/components/status-control/index.tsx index b275d042aa..237c4d46b5 100644 --- a/packages/file-upload-item/src/components/status-control/index.tsx +++ b/packages/file-upload-item/src/components/status-control/index.tsx @@ -17,6 +17,7 @@ export const StatusControl = () => { progressBar = 0, progressBarAvailable = true, imageUrl, + setIsBrokenImage, backgroundColor, actionsPresent, isClickable, @@ -27,7 +28,6 @@ export const StatusControl = () => { const strokeDashoffset = Math.max(measureDashoffset, 0); const hasFullStatus = isSuccessStatus(uploadStatus) || isErrorStatus(uploadStatus); - return (
{ diff --git a/packages/file-upload-item/src/context/file-upload-item-context.ts b/packages/file-upload-item/src/context/file-upload-item-context.ts index 5147b0ac16..3f74192e0e 100644 --- a/packages/file-upload-item/src/context/file-upload-item-context.ts +++ b/packages/file-upload-item/src/context/file-upload-item-context.ts @@ -34,6 +34,8 @@ type TFileUploadItemContext = { customContent?: ElementType; truncate?: boolean; imageUrl?: string; + isBrokenImage?: boolean; + setIsBrokenImage?: (isBroken: boolean) => void; backgroundColor?: SuperEllipseProps['backgroundColor']; actionsPresent?: boolean; setActionsPresent?: (present: boolean) => void; @@ -64,6 +66,8 @@ export const FileUploadItemContext = createContext({ customContent: undefined, truncate: false, imageUrl: undefined, + isBrokenImage: false, + setIsBrokenImage: undefined, backgroundColor: undefined, actionsPresent: false, setActionsPresent: undefined, diff --git a/packages/icon-view/src/components/base-shape/check-image-is-broken.test.ts b/packages/icon-view/src/components/base-shape/check-image-is-broken.test.ts new file mode 100644 index 0000000000..4b716c687b --- /dev/null +++ b/packages/icon-view/src/components/base-shape/check-image-is-broken.test.ts @@ -0,0 +1,119 @@ +import { checkImageIsBroken } from './check-image-is-broken'; + +type EventType = 'load' | 'error'; + +type MockImageConstructorParams = { + eventType: EventType; + decode: () => Promise; +}; + +const getMockImageConstructor = ({ eventType, decode }: MockImageConstructorParams) => { + function MockImage(this: { + onload: null | (() => void); + onerror: null | (() => void); + decode: () => Promise; + }) { + this.onload = null; + this.onerror = null; + this.decode = decode; + } + + Object.defineProperty(MockImage.prototype, 'src', { + set(this: { onload: null | (() => void); onerror: null | (() => void) }) { + if (eventType === 'load') { + this.onload?.(); + } else { + this.onerror?.(); + } + }, + }); + + return MockImage as unknown as typeof Image; +}; + +describe('checkImageIsBroken', () => { + const originalCreateImageBitmap = global.createImageBitmap; + const originalImage = global.Image; + + afterEach(() => { + global.createImageBitmap = originalCreateImageBitmap; + global.Image = originalImage; + }); + + it('should return false for valid image via createImageBitmap', async () => { + const close = jest.fn(); + + global.createImageBitmap = jest.fn().mockResolvedValue({ + close, + } as unknown as ImageBitmap); + global.Image = getMockImageConstructor({ + eventType: 'load', + decode: jest.fn().mockResolvedValue(undefined), + }); + + await new Promise((resolve) => { + checkImageIsBroken({ + imageUrl: 'https://test-url', + onResolve: (isBroken) => { + expect(isBroken).toBe(false); + expect(close).toHaveBeenCalled(); + resolve(); + }, + }); + }); + }); + + it('should use decode fallback when createImageBitmap fails', async () => { + global.createImageBitmap = jest.fn().mockRejectedValue(new Error('bitmap failed')); + global.Image = getMockImageConstructor({ + eventType: 'load', + decode: jest.fn().mockResolvedValue(undefined), + }); + + await new Promise((resolve) => { + checkImageIsBroken({ + imageUrl: 'https://test-url', + onResolve: (isBroken) => { + expect(isBroken).toBe(false); + resolve(); + }, + }); + }); + }); + + it('should return true when decode fallback also fails', async () => { + global.createImageBitmap = jest.fn().mockRejectedValue(new Error('bitmap failed')); + global.Image = getMockImageConstructor({ + eventType: 'load', + decode: jest.fn().mockRejectedValue(new Error('decode failed')), + }); + + await new Promise((resolve) => { + checkImageIsBroken({ + imageUrl: 'https://test-url', + onResolve: (isBroken) => { + expect(isBroken).toBe(true); + resolve(); + }, + }); + }); + }); + + it('should return true on image onerror', async () => { + global.createImageBitmap = jest.fn(); + global.Image = getMockImageConstructor({ + eventType: 'error', + decode: jest.fn().mockResolvedValue(undefined), + }); + + await new Promise((resolve) => { + checkImageIsBroken({ + imageUrl: 'https://test-url', + onResolve: (isBroken) => { + expect(isBroken).toBe(true); + resolve(); + }, + }); + }); + }); +}); diff --git a/packages/icon-view/src/components/base-shape/check-image-is-broken.ts b/packages/icon-view/src/components/base-shape/check-image-is-broken.ts new file mode 100644 index 0000000000..125def6d1d --- /dev/null +++ b/packages/icon-view/src/components/base-shape/check-image-is-broken.ts @@ -0,0 +1,48 @@ +type CheckImageIsBrokenParams = { + imageUrl: string; + onResolve: (isBroken: boolean) => void; +}; + +export const checkImageIsBroken = ({ imageUrl, onResolve }: CheckImageIsBrokenParams) => { + const image = new Image(); + + const resolveWithDecode = () => { + if (typeof image.decode === 'function') { + image + .decode() + .then(() => { + onResolve(false); + }) + .catch(() => { + onResolve(true); + }); + + return; + } + + onResolve(false); + }; + + image.onload = () => { + if (typeof createImageBitmap === 'function') { + createImageBitmap(image) + .then((imageBitmap) => { + imageBitmap.close(); + onResolve(false); + }) + .catch(() => { + resolveWithDecode(); + }); + + return; + } + + resolveWithDecode(); + }; + + image.onerror = () => { + onResolve(true); + }; + + image.src = imageUrl; +}; diff --git a/packages/icon-view/src/components/base-shape/component.tsx b/packages/icon-view/src/components/base-shape/component.tsx index a40ca36492..9cba1825b6 100644 --- a/packages/icon-view/src/components/base-shape/component.tsx +++ b/packages/icon-view/src/components/base-shape/component.tsx @@ -1,8 +1,16 @@ -import React, { type ElementType, forwardRef, Fragment, type ReactNode } from 'react'; +import React, { + type ElementType, + forwardRef, + Fragment, + type ReactNode, + useEffect, + useState, +} from 'react'; import cn from 'classnames'; import { useId, useImageLoadingState } from '@alfalab/hooks'; +import { checkImageIsBroken } from './check-image-is-broken'; import { getPath, getPathName, type PathsMap } from './utils'; import styles from './index.module.css'; @@ -47,6 +55,12 @@ export type BaseShapeProps = { */ imageUrl?: string; + /** + * Колбек изменения статуса изображения. + * Возвращает true, если изображение не удалось загрузить/декодировать. + */ + onImageBrokenChange?: (isBrokenImage: boolean) => void; + /** * Режим масштабирования изображения * 'fill' - изображение заполняет всё доступное пространство и может быть обрезано @@ -118,6 +132,7 @@ export const BaseShape = forwardRef( border = false, backgroundColor, imageUrl, + onImageBrokenChange, scale = 'fill', backgroundIcon: Icon, className, @@ -136,6 +151,32 @@ export const BaseShape = forwardRef( ) => { const [width, height] = typeof size === 'object' ? [size.width, size.height] : [size, size]; const imageLoadingState = useImageLoadingState({ src: imageUrl || '' }); + const [isBrokenImage, setIsBrokenImage] = useState(false); + + useEffect(() => { + let isUnmounted = false; + + setIsBrokenImage(false); + onImageBrokenChange?.(false); + + if (!imageUrl) { + return; + } + + checkImageIsBroken({ + imageUrl, + onResolve: (isBroken) => { + if (!isUnmounted) { + setIsBrokenImage(isBroken); + onImageBrokenChange?.(isBroken); + } + }, + }); + + return () => { + isUnmounted = true; + }; + }, [imageUrl, onImageBrokenChange]); const clipPathId = useId(); @@ -188,7 +229,7 @@ export const BaseShape = forwardRef( d={shapeDPath} /> - {imageUrl && imageLoadingState !== 'error' && ( + {imageUrl && imageLoadingState !== 'error' && !isBrokenImage && ( From 61699acd9162d5dcb4ba0475f007f36d3d440f6b Mon Sep 17 00:00:00 2001 From: KaPuTaH-UluTka Date: Tue, 2 Jun 2026 17:59:07 +0300 Subject: [PATCH 2/3] feat(file-upload-item): move logic to hook --- .changeset/four-geckos-teach.md | 7 ++ .../status-control/extension-icon/index.tsx | 61 +++++++------ .../src/components/status-control/index.tsx | 1 + .../base-shape/check-image-is-broken.ts | 45 ++++------ .../src/components/base-shape/component.tsx | 38 +------- .../use-check-image-is-broken.test.ts | 87 +++++++++++++++++++ .../base-shape/use-check-image-is-broken.ts | 46 ++++++++++ 7 files changed, 194 insertions(+), 91 deletions(-) create mode 100644 .changeset/four-geckos-teach.md create mode 100644 packages/icon-view/src/components/base-shape/use-check-image-is-broken.test.ts create mode 100644 packages/icon-view/src/components/base-shape/use-check-image-is-broken.ts diff --git a/.changeset/four-geckos-teach.md b/.changeset/four-geckos-teach.md new file mode 100644 index 0000000000..fcfdff807d --- /dev/null +++ b/.changeset/four-geckos-teach.md @@ -0,0 +1,7 @@ +--- +'@alfalab/core-components-icon-view': minor +'@alfalab/core-components-file-upload-item': patch +--- + +- В `icon-view` добавлен callback `onImageBrokenChange` и улучшена обработка битых изображений в `BaseShape`. +- В `file-upload-item` для битого `imageUrl` добавлено отображение fallback-иконки `DocumentImageOff`. diff --git a/packages/file-upload-item/src/components/status-control/extension-icon/index.tsx b/packages/file-upload-item/src/components/status-control/extension-icon/index.tsx index 07daf53aa8..eeb70eaad1 100644 --- a/packages/file-upload-item/src/components/status-control/extension-icon/index.tsx +++ b/packages/file-upload-item/src/components/status-control/extension-icon/index.tsx @@ -16,6 +16,38 @@ import { getExtension, isInitialStatus } from '../../../utils'; import styles from './index.module.css'; +const getDefaultFileIcon = (title: string, isColoredIcon: boolean) => { + const fileType = getExtension(title); + + switch (fileType) { + case 'pdf': + case 'ppt': + case 'pptx': + return ; + case 'doc': + case 'docx': + return ; + case 'xls': + case 'xlsx': + return ( + + ); + case '1c': + return ; + case 'png': + case 'jpg': + case 'jpeg': + case 'svg': + case 'tif': + case 'tiff': + return ; + default: + return ( + + ); + } +}; + export const ExtensionIcon = () => { const { title = '', @@ -48,33 +80,6 @@ export const ExtensionIcon = () => { } const isColoredIcon = iconStyle === 'colored'; - const fileType = getExtension(title); - switch (fileType) { - case 'pdf': - case 'ppt': - case 'pptx': - return ; - case 'doc': - case 'docx': - return ; - case 'xls': - case 'xlsx': - return ( - - ); - case '1c': - return ; - case 'png': - case 'jpg': - case 'jpeg': - case 'svg': - case 'tif': - case 'tiff': - return ; - default: - return ( - - ); - } + return getDefaultFileIcon(title, isColoredIcon); }; diff --git a/packages/file-upload-item/src/components/status-control/index.tsx b/packages/file-upload-item/src/components/status-control/index.tsx index 237c4d46b5..86afadb713 100644 --- a/packages/file-upload-item/src/components/status-control/index.tsx +++ b/packages/file-upload-item/src/components/status-control/index.tsx @@ -28,6 +28,7 @@ export const StatusControl = () => { const strokeDashoffset = Math.max(measureDashoffset, 0); const hasFullStatus = isSuccessStatus(uploadStatus) || isErrorStatus(uploadStatus); + return (
{ - if (typeof image.decode === 'function') { - image - .decode() - .then(() => { - onResolve(false); - }) - .catch(() => { - onResolve(true); - }); - - return; - } - - onResolve(false); + Promise.resolve() + .then(() => image.decode()) + .then(() => { + onResolve(false); + }) + .catch(() => { + onResolve(true); + }); }; image.onload = () => { - if (typeof createImageBitmap === 'function') { - createImageBitmap(image) - .then((imageBitmap) => { - imageBitmap.close(); - onResolve(false); - }) - .catch(() => { - resolveWithDecode(); - }); - - return; - } - - resolveWithDecode(); + Promise.resolve() + .then(() => createImageBitmap(image)) + .then((imageBitmap) => { + imageBitmap.close(); + onResolve(false); + }) + .catch(() => { + resolveWithDecode(); + }); }; image.onerror = () => { diff --git a/packages/icon-view/src/components/base-shape/component.tsx b/packages/icon-view/src/components/base-shape/component.tsx index 9cba1825b6..fb942ffdde 100644 --- a/packages/icon-view/src/components/base-shape/component.tsx +++ b/packages/icon-view/src/components/base-shape/component.tsx @@ -1,16 +1,9 @@ -import React, { - type ElementType, - forwardRef, - Fragment, - type ReactNode, - useEffect, - useState, -} from 'react'; +import React, { type ElementType, forwardRef, Fragment, type ReactNode } from 'react'; import cn from 'classnames'; import { useId, useImageLoadingState } from '@alfalab/hooks'; -import { checkImageIsBroken } from './check-image-is-broken'; +import { useCheckImageIsBroken } from './use-check-image-is-broken'; import { getPath, getPathName, type PathsMap } from './utils'; import styles from './index.module.css'; @@ -151,32 +144,7 @@ export const BaseShape = forwardRef( ) => { const [width, height] = typeof size === 'object' ? [size.width, size.height] : [size, size]; const imageLoadingState = useImageLoadingState({ src: imageUrl || '' }); - const [isBrokenImage, setIsBrokenImage] = useState(false); - - useEffect(() => { - let isUnmounted = false; - - setIsBrokenImage(false); - onImageBrokenChange?.(false); - - if (!imageUrl) { - return; - } - - checkImageIsBroken({ - imageUrl, - onResolve: (isBroken) => { - if (!isUnmounted) { - setIsBrokenImage(isBroken); - onImageBrokenChange?.(isBroken); - } - }, - }); - - return () => { - isUnmounted = true; - }; - }, [imageUrl, onImageBrokenChange]); + const isBrokenImage = useCheckImageIsBroken({ imageUrl, onImageBrokenChange }); const clipPathId = useId(); diff --git a/packages/icon-view/src/components/base-shape/use-check-image-is-broken.test.ts b/packages/icon-view/src/components/base-shape/use-check-image-is-broken.test.ts new file mode 100644 index 0000000000..d99632a874 --- /dev/null +++ b/packages/icon-view/src/components/base-shape/use-check-image-is-broken.test.ts @@ -0,0 +1,87 @@ +import { renderHook, act } from '@testing-library/react-hooks'; + +import { checkImageIsBroken } from './check-image-is-broken'; +import { useCheckImageIsBroken } from './use-check-image-is-broken'; + +jest.mock('./check-image-is-broken', () => ({ + checkImageIsBroken: jest.fn(), +})); + +const mockedCheckImageIsBroken = jest.mocked(checkImageIsBroken); + +describe('useCheckImageIsBroken', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should keep default state for empty imageUrl', () => { + const onImageBrokenChange = jest.fn(); + const { result } = renderHook(() => + useCheckImageIsBroken({ imageUrl: undefined, onImageBrokenChange }), + ); + + expect(result.current).toBe(false); + expect(onImageBrokenChange).toHaveBeenCalledWith(false); + expect(mockedCheckImageIsBroken).not.toHaveBeenCalled(); + }); + + it('should set false for valid image', () => { + mockedCheckImageIsBroken.mockImplementation(({ onResolve }) => { + onResolve(false); + }); + + const onImageBrokenChange = jest.fn(); + const { result } = renderHook(() => + useCheckImageIsBroken({ imageUrl: 'https://valid-image', onImageBrokenChange }), + ); + + expect(result.current).toBe(false); + expect(onImageBrokenChange).toHaveBeenNthCalledWith(1, false); + expect(onImageBrokenChange).toHaveBeenNthCalledWith(2, false); + }); + + it('should set true for broken image', () => { + mockedCheckImageIsBroken.mockImplementation(({ onResolve }) => { + onResolve(true); + }); + + const onImageBrokenChange = jest.fn(); + const { result } = renderHook(() => + useCheckImageIsBroken({ imageUrl: 'https://broken-image', onImageBrokenChange }), + ); + + expect(result.current).toBe(true); + expect(onImageBrokenChange).toHaveBeenNthCalledWith(1, false); + expect(onImageBrokenChange).toHaveBeenNthCalledWith(2, true); + }); + + it('should ignore stale check result after image change', () => { + const resolvers: Record void> = {}; + + mockedCheckImageIsBroken.mockImplementation(({ imageUrl, onResolve }) => { + resolvers[imageUrl] = onResolve; + }); + + const onImageBrokenChange = jest.fn(); + const { result, rerender } = renderHook( + ({ imageUrl }: { imageUrl?: string }) => + useCheckImageIsBroken({ imageUrl, onImageBrokenChange }), + { initialProps: { imageUrl: 'https://first-image' } }, + ); + + rerender({ imageUrl: 'https://second-image' }); + + act(() => { + resolvers['https://first-image'](true); + }); + + expect(result.current).toBe(false); + + act(() => { + resolvers['https://second-image'](true); + }); + + expect(result.current).toBe(true); + expect(onImageBrokenChange).toHaveBeenLastCalledWith(true); + }); +}); diff --git a/packages/icon-view/src/components/base-shape/use-check-image-is-broken.ts b/packages/icon-view/src/components/base-shape/use-check-image-is-broken.ts new file mode 100644 index 0000000000..7cd7f6bdb2 --- /dev/null +++ b/packages/icon-view/src/components/base-shape/use-check-image-is-broken.ts @@ -0,0 +1,46 @@ +import { useEffect, useState } from 'react'; + +import { checkImageIsBroken } from './check-image-is-broken'; + +type UseCheckImageIsBrokenParams = { + imageUrl?: string; + onImageBrokenChange?: (isBrokenImage: boolean) => void; +}; + +export const useCheckImageIsBroken = ({ + imageUrl, + onImageBrokenChange, +}: UseCheckImageIsBrokenParams) => { + const [isBrokenImage, setIsBrokenImage] = useState(false); + + useEffect(() => { + let isActive = true; + + setIsBrokenImage(false); + onImageBrokenChange?.(false); + + if (!imageUrl) { + return () => { + isActive = false; + }; + } + + checkImageIsBroken({ + imageUrl, + onResolve: (isBroken) => { + if (!isActive) { + return; + } + + setIsBrokenImage(isBroken); + onImageBrokenChange?.(isBroken); + }, + }); + + return () => { + isActive = false; + }; + }, [imageUrl, onImageBrokenChange]); + + return isBrokenImage; +}; From 63c0af23e1e948b7a911515efd03d9bb31c55d8e Mon Sep 17 00:00:00 2001 From: KaPuTaH-UluTka Date: Wed, 3 Jun 2026 18:19:09 +0300 Subject: [PATCH 3/3] feat(file-upload-item): rename type --- .../src/components/base-shape/use-check-image-is-broken.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/icon-view/src/components/base-shape/use-check-image-is-broken.ts b/packages/icon-view/src/components/base-shape/use-check-image-is-broken.ts index 7cd7f6bdb2..37798d2cad 100644 --- a/packages/icon-view/src/components/base-shape/use-check-image-is-broken.ts +++ b/packages/icon-view/src/components/base-shape/use-check-image-is-broken.ts @@ -2,15 +2,12 @@ import { useEffect, useState } from 'react'; import { checkImageIsBroken } from './check-image-is-broken'; -type UseCheckImageIsBrokenParams = { +type Params = { imageUrl?: string; onImageBrokenChange?: (isBrokenImage: boolean) => void; }; -export const useCheckImageIsBroken = ({ - imageUrl, - onImageBrokenChange, -}: UseCheckImageIsBrokenParams) => { +export const useCheckImageIsBroken = ({ imageUrl, onImageBrokenChange }: Params) => { const [isBrokenImage, setIsBrokenImage] = useState(false); useEffect(() => {