mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
chore(image): package deleted
This commit is contained in:
parent
83d4206300
commit
830a4838d0
@ -1,24 +0,0 @@
|
||||
# @nextui-org/image
|
||||
|
||||
A Quick description of the component
|
||||
|
||||
> This is an internal utility, not intended for public usage.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
yarn add @nextui-org/image
|
||||
# or
|
||||
npm i @nextui-org/image
|
||||
```
|
||||
|
||||
## Contribution
|
||||
|
||||
Yes please! See the
|
||||
[contributing guidelines](https://github.com/nextui-org/nextui/blob/master/CONTRIBUTING.md)
|
||||
for details.
|
||||
|
||||
## Licence
|
||||
|
||||
This project is licensed under the terms of the
|
||||
[MIT license](https://github.com/nextui-org/nextui/blob/master/LICENSE).
|
||||
@ -1,51 +0,0 @@
|
||||
import * as React from "react";
|
||||
import {act, render, waitFor} from "@testing-library/react";
|
||||
|
||||
import {Image} from "../src";
|
||||
|
||||
const url =
|
||||
"data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA" +
|
||||
"AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO" +
|
||||
"9TXL0Y4OHwAAAABJRU5ErkJggg==";
|
||||
|
||||
describe("Image", () => {
|
||||
it("should render correctly", () => {
|
||||
const wrapper = render(<Image src={url} />);
|
||||
|
||||
expect(() => wrapper.unmount()).not.toThrow();
|
||||
});
|
||||
|
||||
it("ref should be forwarded", () => {
|
||||
const ref = React.createRef<HTMLImageElement>();
|
||||
|
||||
render(<Image ref={ref} src={url} />);
|
||||
expect(ref.current).not.toBeNull();
|
||||
});
|
||||
|
||||
it("should work correctly with skeleton", async () => {
|
||||
const {container} = render(<Image showSkeleton src={url} />);
|
||||
|
||||
expect(container.querySelector(".nextui-image-skeleton")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("should remove skeleton when timeout", async () => {
|
||||
Object.defineProperty((global as any).Image.prototype, "complete", {
|
||||
get() {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
const {container} = render(<Image maxDelay={100} src={url} />);
|
||||
|
||||
const img = container.querySelector("img");
|
||||
|
||||
// simulate img load
|
||||
act(() => {
|
||||
img?.dispatchEvent(new Event("load"));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector(".nextui-image-skeleton")).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
{
|
||||
"replace": {
|
||||
"main": "dist/index.cjs.js",
|
||||
"module": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {"import": "./dist/index.esm.js", "require": "./dist/index.cjs.js"},
|
||||
"./package.json": "./package.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
{
|
||||
"name": "@nextui-org/image",
|
||||
"version": "2.0.0-beta.1",
|
||||
"description": "The Image component is used to display images with support for fallback.",
|
||||
"keywords": [
|
||||
"image"
|
||||
],
|
||||
"author": "Junior Garcia <jrgarciadev@gmail.com>",
|
||||
"homepage": "https://nextui.org",
|
||||
"license": "MIT",
|
||||
"main": "src/index.ts",
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/nextui-org/nextui.git",
|
||||
"directory": "packages/components/image"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/nextui-org/nextui/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup src --dts",
|
||||
"dev": "yarn build:fast -- --watch",
|
||||
"clean": "rimraf dist .turbo",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build:fast": "tsup src",
|
||||
"prepack": "clean-package",
|
||||
"postpack": "clean-package restore"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nextui-org/system": "workspace:*",
|
||||
"@nextui-org/shared-utils": "workspace:*",
|
||||
"@nextui-org/dom-utils": "workspace:*",
|
||||
"@nextui-org/use-ref-state": "workspace:*",
|
||||
"@nextui-org/use-real-shape": "workspace:*",
|
||||
"@nextui-org/use-resize": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"clean-package": "2.2.0",
|
||||
"react": "^18.0.0"
|
||||
},
|
||||
"tsup": {
|
||||
"clean": true,
|
||||
"target": "es2019",
|
||||
"format": [
|
||||
"cjs",
|
||||
"esm"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
import {forwardRef, HTMLNextUIProps} from "@nextui-org/system";
|
||||
import {useDOMRef} from "@nextui-org/dom-utils";
|
||||
import {clsx, __DEV__} from "@nextui-org/shared-utils";
|
||||
|
||||
import {StyledImageSkeleton} from "./image.styles";
|
||||
|
||||
export interface ImageSkeletonProps extends HTMLNextUIProps<"div"> {
|
||||
/**
|
||||
* The skeleton opacity - between 0 and 1.
|
||||
* @default 0.5
|
||||
*/
|
||||
opacity?: number;
|
||||
}
|
||||
|
||||
const ImageSkeleton = forwardRef<ImageSkeletonProps, "div">((props, ref) => {
|
||||
const {opacity, css, className, ...otherProps} = props;
|
||||
|
||||
const domRef = useDOMRef(ref);
|
||||
|
||||
return (
|
||||
<StyledImageSkeleton
|
||||
ref={domRef}
|
||||
className={clsx("nextui-image-skeleton", className)}
|
||||
css={{
|
||||
opacity,
|
||||
...css,
|
||||
}}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
if (__DEV__) {
|
||||
ImageSkeleton.displayName = "NextUI.ImageSkeleton";
|
||||
}
|
||||
|
||||
ImageSkeleton.toString = () => ".nextui-image-skeleton";
|
||||
|
||||
export default ImageSkeleton;
|
||||
@ -1,10 +0,0 @@
|
||||
import {keyframes} from "@nextui-org/system";
|
||||
|
||||
export const loading = keyframes({
|
||||
"0%": {
|
||||
backgroundPosition: "200% 0",
|
||||
},
|
||||
to: {
|
||||
backgroundPosition: "-200% 0",
|
||||
},
|
||||
});
|
||||
@ -1,45 +0,0 @@
|
||||
import {styled} from "@nextui-org/system";
|
||||
|
||||
import {loading} from "./image.animations";
|
||||
|
||||
export const StyledImage = styled("img", {
|
||||
size: "100%",
|
||||
display: "block",
|
||||
});
|
||||
|
||||
export const StyledImageContainer = styled("div", {
|
||||
opacity: 0,
|
||||
margin: "0 auto",
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
maxWidth: "100%",
|
||||
transition: "transform 250ms ease 0ms, opacity 200ms ease-in 0ms",
|
||||
"@motion": {
|
||||
transition: "none",
|
||||
},
|
||||
variants: {
|
||||
isLoaded: {
|
||||
true: {
|
||||
opacity: 1,
|
||||
},
|
||||
false: {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const StyledImageSkeleton = styled("div", {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
size: "100%",
|
||||
borderRadius: "inherit",
|
||||
backgroundImage:
|
||||
"linear-gradient(270deg, $colors$accents1, $colors$accents2, $colors$accents2, $colors$accents1)",
|
||||
backgroundSize: "400% 100%",
|
||||
animation: `${loading} 5s ease-in-out infinite`,
|
||||
transition: "opacity 300ms ease-out",
|
||||
});
|
||||
@ -1,69 +0,0 @@
|
||||
import {forwardRef} from "@nextui-org/system";
|
||||
import {clsx, __DEV__} from "@nextui-org/shared-utils";
|
||||
|
||||
import ImageSkeleton from "./image-skeleton";
|
||||
import {StyledImage, StyledImageContainer} from "./image.styles";
|
||||
import {UseImageProps, useImage} from "./use-image";
|
||||
|
||||
export interface ImageProps extends Omit<UseImageProps, "ref"> {}
|
||||
|
||||
const Image = forwardRef<ImageProps, "img">((props, ref) => {
|
||||
const {
|
||||
w,
|
||||
css,
|
||||
src,
|
||||
width,
|
||||
height,
|
||||
imageRef,
|
||||
objectFit,
|
||||
isLoading,
|
||||
showSkeleton,
|
||||
zoomHeight,
|
||||
state,
|
||||
className,
|
||||
containerCss,
|
||||
onLoad,
|
||||
...otherProps
|
||||
} = useImage({
|
||||
ref,
|
||||
...props,
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledImageContainer
|
||||
className={clsx("nextui-image-container", className)}
|
||||
css={{
|
||||
width: w,
|
||||
height: zoomHeight,
|
||||
...containerCss,
|
||||
}}
|
||||
data-state={state}
|
||||
isLoaded={!isLoading || showSkeleton}
|
||||
>
|
||||
{showSkeleton && <ImageSkeleton opacity={1} />}
|
||||
<StyledImage
|
||||
ref={imageRef}
|
||||
alt={props.alt || ""}
|
||||
className="nextui-image"
|
||||
css={{
|
||||
objectFit,
|
||||
...css,
|
||||
}}
|
||||
data-state={state}
|
||||
height={height}
|
||||
src={src}
|
||||
width={width}
|
||||
onLoad={onLoad}
|
||||
{...otherProps}
|
||||
/>
|
||||
</StyledImageContainer>
|
||||
);
|
||||
});
|
||||
|
||||
if (__DEV__) {
|
||||
Image.displayName = "NextUI.Image";
|
||||
}
|
||||
|
||||
Image.toString = () => ".nextui-image";
|
||||
|
||||
export default Image;
|
||||
@ -1,5 +0,0 @@
|
||||
// export types
|
||||
export type {ImageProps} from "./image";
|
||||
|
||||
// export component
|
||||
export {default as Image} from "./image";
|
||||
@ -1,154 +0,0 @@
|
||||
import {useState, useEffect, useMemo} from "react";
|
||||
import {HTMLNextUIProps, CSS} from "@nextui-org/system";
|
||||
import {useDOMRef} from "@nextui-org/dom-utils";
|
||||
import {ReactRef} from "@nextui-org/shared-utils";
|
||||
import {useRefState} from "@nextui-org/use-ref-state";
|
||||
import {useRealShape} from "@nextui-org/use-real-shape";
|
||||
import {useResize} from "@nextui-org/use-resize";
|
||||
|
||||
export interface UseImageProps extends Omit<HTMLNextUIProps<"img">, "height" | "width"> {
|
||||
/**
|
||||
* The image ref.
|
||||
*/
|
||||
ref?: ReactRef<HTMLImageElement | null>;
|
||||
/**
|
||||
* The image source (local or remote)
|
||||
*/
|
||||
src: string;
|
||||
/**
|
||||
* Resize Image to fits screen width
|
||||
* @default false
|
||||
*/
|
||||
autoResize?: boolean;
|
||||
/**
|
||||
* Shows loading Skeleton while image is loading
|
||||
* @default true
|
||||
*/
|
||||
showSkeleton?: boolean;
|
||||
/**
|
||||
* Specifies Image width
|
||||
*/
|
||||
width?: number | string;
|
||||
/**
|
||||
* Specifies Image height
|
||||
*/
|
||||
height?: number | string;
|
||||
/**
|
||||
* Specifies how long Image Skeleton Renders Animation
|
||||
* @default 3000
|
||||
*/
|
||||
maxDelay?: number;
|
||||
/**
|
||||
* Property tells the content to fill the container
|
||||
*/
|
||||
objectFit?: CSS["objectFit"];
|
||||
/**
|
||||
* Override default Image container styles
|
||||
*/
|
||||
containerCss?: CSS;
|
||||
|
||||
/**
|
||||
* Function to be called when the image is loaded
|
||||
*/
|
||||
onLoad?: () => void;
|
||||
}
|
||||
|
||||
export function useImage(props: UseImageProps) {
|
||||
const {
|
||||
ref,
|
||||
width,
|
||||
height,
|
||||
showSkeleton: showSkeletonProp = true,
|
||||
maxDelay = 3000,
|
||||
autoResize = false,
|
||||
objectFit = "scale-down",
|
||||
onLoad: onLoadProp,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const imageRef = useDOMRef(ref);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [showSkeleton, setShowSkeleton] = useState(showSkeletonProp);
|
||||
|
||||
const {w, h} = useMemo(() => {
|
||||
return {
|
||||
w: width ? (typeof width === "number" ? `${width}px` : width) : "auto",
|
||||
h: height ? (typeof height === "number" ? `${height}px` : height) : "auto",
|
||||
};
|
||||
}, [width, height]);
|
||||
|
||||
const [zoomHeight, setZoomHeight, zoomHeightRef] = useRefState<string>(h);
|
||||
const [shape, updateShape] = useRealShape(imageRef);
|
||||
|
||||
const showAnimation = showSkeletonProp && !!width && !!height;
|
||||
|
||||
const onLoad = () => {
|
||||
setIsLoading(false);
|
||||
onLoadProp?.();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!imageRef.current) return;
|
||||
if (imageRef.current.complete) {
|
||||
setIsLoading(false);
|
||||
setShowSkeleton(false);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (showAnimation) {
|
||||
setShowSkeleton(false);
|
||||
}
|
||||
clearTimeout(timer);
|
||||
}, maxDelay);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [isLoading]);
|
||||
|
||||
/**
|
||||
* On mobile devices, the render witdth may be less than CSS width value.
|
||||
* If the image is scaled, set the height manually.
|
||||
* This is to ensure the aspect ratio of the image.
|
||||
*
|
||||
* If the image is auto width, ignore all.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!autoResize) return;
|
||||
const notLoaded = shape.width === 0;
|
||||
const isAutoZoom = zoomHeightRef.current === "auto";
|
||||
|
||||
if (notLoaded || !width || !height) return;
|
||||
if (shape.width < width) {
|
||||
!isAutoZoom && setZoomHeight("auto");
|
||||
} else {
|
||||
isAutoZoom && setZoomHeight(h);
|
||||
}
|
||||
}, [shape, width]);
|
||||
|
||||
useResize(() => {
|
||||
if (!autoResize) return;
|
||||
updateShape();
|
||||
});
|
||||
|
||||
const state = useMemo(() => {
|
||||
return isLoading ? "loading" : "loaded";
|
||||
}, [isLoading]);
|
||||
|
||||
return {
|
||||
w,
|
||||
state,
|
||||
width,
|
||||
height,
|
||||
imageRef,
|
||||
objectFit,
|
||||
zoomHeight,
|
||||
isLoading,
|
||||
showSkeleton,
|
||||
onLoad,
|
||||
...otherProps,
|
||||
};
|
||||
}
|
||||
|
||||
export type UseImageReturn = ReturnType<typeof useImage>;
|
||||
@ -1,68 +0,0 @@
|
||||
import React from "react";
|
||||
import {Meta} from "@storybook/react";
|
||||
import {Grid} from "@nextui-org/grid";
|
||||
|
||||
import {Image} from "../src";
|
||||
|
||||
export default {
|
||||
title: "Display/Image",
|
||||
component: Image,
|
||||
} as Meta;
|
||||
|
||||
export const Default = () => (
|
||||
<Image
|
||||
alt="Default Image"
|
||||
height={180}
|
||||
objectFit="cover"
|
||||
src="https://github.com/nextui-org/nextui/blob/next/apps/docs/public/nextui-banner.jpeg?raw=true"
|
||||
width={320}
|
||||
/>
|
||||
);
|
||||
|
||||
export const Sizes = () => (
|
||||
<Grid.Container gap={2}>
|
||||
<Grid>
|
||||
<Image alt="Default Image" height={50} src="http://placehold.jp/50x50.png" width={50} />
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Image alt="Default Image" height={100} src="http://placehold.jp/100x100.png" width={100} />
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Image alt="Default Image" height={150} src="http://placehold.jp/150x150.png" width={150} />
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
);
|
||||
|
||||
export const Skeleton = () => (
|
||||
<Image
|
||||
showSkeleton
|
||||
alt="Default Image"
|
||||
height={180}
|
||||
maxDelay={5000}
|
||||
src="http://www.deelay.me/5000/https://github.com/nextui-org/nextui/blob/next/apps/docs/public/nextui-banner.jpeg?raw=true"
|
||||
width={320}
|
||||
/>
|
||||
);
|
||||
|
||||
export const ObjectFit = () => (
|
||||
<Grid.Container gap={2}>
|
||||
<Grid>
|
||||
<Image
|
||||
alt="Default Image"
|
||||
height={180}
|
||||
objectFit="contain"
|
||||
src="https://github.com/nextui-org/nextui/blob/next/apps/docs/public/nextui-banner.jpeg?raw=true"
|
||||
width={320}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Image
|
||||
alt="Default Image"
|
||||
height={180}
|
||||
objectFit="cover"
|
||||
src="https://github.com/nextui-org/nextui/blob/next/apps/docs/public/nextui-banner.jpeg?raw=true"
|
||||
width={320}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid.Container>
|
||||
);
|
||||
@ -1,10 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"tailwind-variants": ["../../../node_modules/tailwind-variants"]
|
||||
},
|
||||
},
|
||||
"include": ["src", "index.ts"]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user