mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
* fix(date-picker): error state (#5317) * fix(date-range-picker): fixed the error state in preset * Update giant-sloths-shop.md * Removed if statement * chore(date-picker): prettier --------- Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix(theme): clear button in mobile (#5252) * fix(toast): fixed close button hover position * fix(input): fixed the clear button rendering on smaller devices * Delete .changeset/soft-spoons-march.md * Update input.ts * Undo unrelated toast changes * fix(toast): icons (#5246) * feat(shared-icons): add loading icon * fix(toast): icons * chore(toast): revise types for icons * chore(changeset): add changeset * refactor: migrate eslint to v9 (#5267) * refactor: migrate eslint to v9 * chore: lint * chore: update eslint command * chore: fix lint warnings * chore: separate lint and lint:fix * chore: exclude contentlayer generated code * fix(scripts): add missing await * fix(autocomplete): persist last selected item position (#5286) * refactor(select): remove unnecessary code * fix(autocomplete): persist last selected item position * chore(changeset): add changeset * chore(deps): bump framer-motion version (#5287) * chore(deps): bump framer-motion version * fix: typing issues * chore(changeset): add changeset --------- Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * chore(docs): supplement onAction & selectionBehavior (#5289) * fix(autocomplete): ensure focused item matches selected item after filter, selection (#5290) * fix(autocomplete): ensure focused item matches selected item after filter, selection * chore: apply type and default value * chore: add perpose coment in updated code * test: add focuskey management testcode * docs: add changeset * docs: update changeset * chore: remove comment * fix: broken components in stories (#5291) * chore(switch): remove xl size * chore(docs): remove xl size * chore(system-rsc): remove xl size * chore(circular-progress): remove xl size * chore: undo * chore(deps): bump RA versions (#5310) * chore(deps): ra version bump * chore(changeset): add changeset * fix(scripts): incorrect docs path --------- Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * chore(docs): update meta data (#5311) * docs(layout.tsx): added text-foreground (#5316) * feat(tabs): add click handling for tab items in tests and implementation (#3917) Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix issues in tabs examples (#2405) Co-authored-by: WK Wong <wingkwong.code@gmail.com> * chore(docs): add missing onValueChange in CheckboxGroup (#5332) * ci(changesets): version packages (#5323) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * chore(deps): bump RA versions (#5361) * chore(deps): bump RA versions * chore(deps): bump RA versions * chore(deps): bump RA versions * chore: changeset * refactor(listbox): already extends in AriaListBoxProps * chore(docs): remove herohack announcement (#5363) * chore: remove herohack announcement * Update carbon-ad.tsx * chore(docs): fixed lint errors * chore(docs): requested changes * Update carbon-ad.tsx * Update carbon-ad.tsx * fix(theme): consistent faded styling for isInvalid in InputOtp and DateInput (#5349) * fix(input-otp): remove bg and border styles from faded variant when isInvalid * fix(date-input): remove bg styles from faded variant when isInvalid * chore(changeset): add changeset * chore: bump theme peerDependencies * chore: bump theme peerDependencies * fix: wrong version * chore: extra line --------- Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix(theme): helperWrapper padding (#5350) * fix(number-input): decreased helperWrapper padding to maintain consistency * Update beige-laws-heal.md * chore(theme): change to p-1 * chore(deps): bump peerDependencies for theme pkg * fix(number-input): incorrect versions * chore(changeset): include number input --------- Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix(autocomplete): onClear (#5365) * fix(autocomplete): add onClear * feat(autocomplete): add test case for onClear * chore(changeset): add changeset * fix(number-input): only allow number type (#5368) * refactor(number-input): avoid non number type passing to number input * chore(changeset): add changeset * refactor: optimization (#5362) * chore(deps): bump RA versions * chore(deps): bump RA versions * chore(deps): bump RA versions * chore: changeset * chore(deps): remove unnecessary dependencies * fix(calendar): typing issue * refactor(system): remove unused SupportedCalendars * refactor(system): move I18nProviderProps to type * refactor: use `spectrumCalendarProps<DateValue>["createCalendar"]` * feat: add consistent-type-imports * fix: eslint * chore: add changeset * refactor: remove unused deps * ci(changesets): version packages (#5364) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> --------- Co-authored-by: Vishv Salvi <82429084+Vishvsalvi@users.noreply.github.com> Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> Co-authored-by: KumJungMin <37934668+KumJungMin@users.noreply.github.com> Co-authored-by: liaoyinglong <vigossliao@gmail.com> Co-authored-by: zhengjitf <zhengjitf@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Anuj Kuralkar <76731106+anuj-kuralkar@users.noreply.github.com>
153 lines
4.1 KiB
TypeScript
153 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import type {FC} from "react";
|
|
|
|
import {useInView} from "framer-motion";
|
|
import {useRef, useEffect, useState, useCallback} from "react";
|
|
import {Button, cn, Spinner, Tooltip} from "@heroui/react";
|
|
|
|
import {PlayBoldIcon, PauseBoldIcon} from "@/components/icons";
|
|
import {RotateLeftLinearIcon} from "@/components/icons";
|
|
|
|
interface VideoInViewProps {
|
|
src: string;
|
|
playMode?: "auto" | "manual";
|
|
autoPlay?: boolean;
|
|
poster?: string;
|
|
width?: number;
|
|
height?: number;
|
|
controls?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
export const VideoInView: FC<VideoInViewProps> = ({
|
|
src,
|
|
width,
|
|
height,
|
|
poster,
|
|
autoPlay = true,
|
|
playMode = "manual",
|
|
controls = false,
|
|
className,
|
|
}) => {
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isPlaying, setIsPlaying] = useState(false);
|
|
|
|
const videoRef = useRef<HTMLVideoElement>(null);
|
|
|
|
const isVisible = useInView(videoRef);
|
|
|
|
// play video when it is visible and playMode is auto
|
|
useEffect(() => {
|
|
if (playMode !== "auto") {
|
|
return;
|
|
}
|
|
|
|
if (isVisible) {
|
|
videoRef.current?.play();
|
|
} else {
|
|
videoRef.current?.pause();
|
|
}
|
|
}, [isVisible]);
|
|
|
|
const handleCanPlay = useCallback(() => {
|
|
setIsLoading(false);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const videoEl = videoRef.current;
|
|
|
|
if (videoEl) {
|
|
if (videoEl.readyState > 3) {
|
|
// HAVE_FUTURE_DATA: enough data to start playing
|
|
handleCanPlay();
|
|
} else {
|
|
videoEl.addEventListener("canplaythrough", handleCanPlay);
|
|
}
|
|
|
|
// Cleanup the event listener
|
|
return () => {
|
|
videoEl.removeEventListener("canplaythrough", handleCanPlay);
|
|
};
|
|
}
|
|
}, []);
|
|
|
|
const onRestart = useCallback(() => {
|
|
if (videoRef.current) {
|
|
videoRef.current.currentTime = 0;
|
|
videoRef.current.play();
|
|
setIsPlaying(true);
|
|
}
|
|
}, []);
|
|
|
|
const onTogglePlay = useCallback(() => {
|
|
if (videoRef.current) {
|
|
if (!isPlaying) {
|
|
videoRef.current.play();
|
|
} else {
|
|
videoRef.current.pause();
|
|
}
|
|
|
|
setIsPlaying((v) => !v);
|
|
}
|
|
}, [isPlaying]);
|
|
|
|
return (
|
|
<div
|
|
className="relative data-[playing=true]:after:opacity-0 data-[playing=true]:after:z-[-1] after:content-[''] after:absolute after:inset-0 after:bg-black/30 after:z-20 after:transition-opacity"
|
|
data-playing={isPlaying}
|
|
>
|
|
{isLoading && (
|
|
<Spinner
|
|
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
|
|
color="secondary"
|
|
size="lg"
|
|
/>
|
|
)}
|
|
<Tooltip content={isPlaying ? "Pause" : "Play"} delay={1000}>
|
|
<Button
|
|
isIconOnly
|
|
className="absolute z-50 right-12 top-3 border-1 border-transparent bg-transparent before:bg-white/10 before:content-[''] before:block before:z-[-1] before:absolute before:inset-0 before:backdrop-blur-md before:backdrop-saturate-100 before:rounded-lg"
|
|
size="sm"
|
|
variant="bordered"
|
|
onPress={onTogglePlay}
|
|
>
|
|
{isPlaying ? (
|
|
<PauseBoldIcon className="text-white" size={16} />
|
|
) : (
|
|
<PlayBoldIcon className="text-white" size={16} />
|
|
)}
|
|
</Button>
|
|
</Tooltip>
|
|
|
|
<Tooltip content="Restart" delay={1000}>
|
|
<Button
|
|
isIconOnly
|
|
className="absolute z-50 right-3 top-3 border-1 border-transparent bg-transparent before:bg-white/10 before:content-[''] before:block before:z-[-1] before:absolute before:inset-0 before:backdrop-blur-md before:backdrop-saturate-100 before:rounded-lg"
|
|
size="sm"
|
|
variant="bordered"
|
|
onPress={onRestart}
|
|
>
|
|
<RotateLeftLinearIcon className="text-white" size={16} />
|
|
</Button>
|
|
</Tooltip>
|
|
<video
|
|
ref={videoRef}
|
|
loop
|
|
muted
|
|
autoPlay={autoPlay && playMode === "auto"}
|
|
className={cn(
|
|
"w-full border border-transparent dark:border-default-200/50 object-fit rounded-xl shadow-lg",
|
|
className,
|
|
)}
|
|
controls={controls}
|
|
height={height}
|
|
poster={poster}
|
|
src={src}
|
|
width={width}
|
|
onCanPlay={handleCanPlay}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|