mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-06 03:50:18 +02:00
161 lines
4.8 KiB
TypeScript
161 lines
4.8 KiB
TypeScript
import { StackItem } from "@fluentui/react";
|
|
import { makeStyles } from "@griffel/react";
|
|
import {
|
|
CSSProperties,
|
|
ComponentType,
|
|
HTMLAttributes,
|
|
ReactNode,
|
|
useImperativeHandle,
|
|
useMemo,
|
|
useRef,
|
|
useState,
|
|
} from "react";
|
|
import { forwardRef } from "../utils/with-display-name";
|
|
import { ResizeObserver, Size } from "./resize-observer";
|
|
|
|
export interface DeviceViewProps extends HTMLAttributes<HTMLDivElement> {
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
BottomElement?: ComponentType<{
|
|
className: string;
|
|
style: CSSProperties;
|
|
children: ReactNode;
|
|
}>;
|
|
|
|
children?: ReactNode;
|
|
}
|
|
|
|
export interface DeviceViewRef {
|
|
enterFullscreen(): void;
|
|
}
|
|
|
|
const useClasses = makeStyles({
|
|
outer: {
|
|
width: "100%",
|
|
height: "100%",
|
|
backgroundColor: "black",
|
|
},
|
|
inner: {
|
|
position: "absolute",
|
|
transformOrigin: "top left",
|
|
},
|
|
bottom: {
|
|
position: "absolute",
|
|
},
|
|
});
|
|
|
|
export const DeviceView = forwardRef<DeviceViewRef>("DeviceView")(
|
|
(
|
|
{ width, height, BottomElement, children, ...props }: DeviceViewProps,
|
|
ref
|
|
) => {
|
|
const classes = useClasses();
|
|
|
|
const [containerSize, setContainerSize] = useState<Size>({
|
|
width: 0,
|
|
height: 0,
|
|
});
|
|
const [bottomSize, setBottomSize] = useState<Size>({
|
|
width: 0,
|
|
height: 0,
|
|
});
|
|
|
|
// Container size minus bottom element size
|
|
const usableSize = useMemo(
|
|
() => ({
|
|
width: containerSize.width,
|
|
height: containerSize.height - bottomSize.height,
|
|
}),
|
|
[containerSize, bottomSize]
|
|
);
|
|
|
|
// Compute sizes after scaling
|
|
const childrenStyle = useMemo(() => {
|
|
let scale: number;
|
|
let childrenWidth: number;
|
|
let childrenHeight: number;
|
|
let childrenTop: number;
|
|
let childrenLeft: number;
|
|
|
|
if (width === 0 || usableSize.width === 0) {
|
|
scale = 1;
|
|
childrenWidth = 0;
|
|
childrenHeight = 0;
|
|
childrenTop = 0;
|
|
childrenLeft = 0;
|
|
} else {
|
|
const videoRatio = width / height;
|
|
const containerRatio = usableSize.width / usableSize.height;
|
|
|
|
if (videoRatio > containerRatio) {
|
|
scale = usableSize.width / width;
|
|
childrenWidth = usableSize.width;
|
|
childrenHeight = height * scale;
|
|
childrenTop = (usableSize.height - childrenHeight) / 2;
|
|
childrenLeft = 0;
|
|
} else {
|
|
scale = usableSize.height / height;
|
|
childrenWidth = width * scale;
|
|
childrenHeight = usableSize.height;
|
|
childrenTop = 0;
|
|
childrenLeft = (usableSize.width - childrenWidth) / 2;
|
|
}
|
|
}
|
|
|
|
return {
|
|
scale,
|
|
width: childrenWidth,
|
|
height: childrenHeight,
|
|
top: childrenTop,
|
|
left: childrenLeft,
|
|
};
|
|
}, [width, height, usableSize]);
|
|
|
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
useImperativeHandle(
|
|
ref,
|
|
() => ({
|
|
enterFullscreen() {
|
|
containerRef.current!.requestFullscreen();
|
|
},
|
|
}),
|
|
[]
|
|
);
|
|
|
|
return (
|
|
<StackItem grow styles={{ root: { position: "relative" } }}>
|
|
<div ref={containerRef} className={classes.outer} {...props}>
|
|
<ResizeObserver onResize={setContainerSize} />
|
|
|
|
<div
|
|
className={classes.inner}
|
|
style={{
|
|
top: childrenStyle.top,
|
|
left: childrenStyle.left,
|
|
width,
|
|
height,
|
|
transform: `scale(${childrenStyle.scale})`,
|
|
}}
|
|
>
|
|
{children}
|
|
</div>
|
|
|
|
{!!width && !!BottomElement && (
|
|
<BottomElement
|
|
className={classes.bottom}
|
|
style={{
|
|
top: childrenStyle.top + childrenStyle.height,
|
|
left: childrenStyle.left,
|
|
width: childrenStyle.width,
|
|
}}
|
|
>
|
|
<ResizeObserver onResize={setBottomSize} />
|
|
</BottomElement>
|
|
)}
|
|
</div>
|
|
</StackItem>
|
|
);
|
|
}
|
|
);
|