Files
react-test/docs/src/components/home/ElementPointer.tsx
how2ice 005cf56baf
Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
init project
2025-12-12 14:26:25 +09:00

118 lines
3.4 KiB
TypeScript

import * as React from 'react';
import Box, { BoxProps } from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { debounce } from '@mui/material/utils';
const PointerContext = React.createContext<undefined | ((data: Data) => void)>(undefined);
export const withPointer = <T extends React.ElementType>(
Component: T,
options: { id: string; name: string },
) => {
function WithPointer(props: object) {
const root = React.useRef<HTMLElement>(null);
const handleMouseOver = React.useContext(PointerContext);
return (
<React.Fragment>
{/* @ts-ignore */}
<Component
ref={root}
{...props}
onMouseOver={(event: React.MouseEvent) => {
event.stopPropagation();
if (handleMouseOver && root.current) {
handleMouseOver({
id: options.id,
target: root.current,
name: options.name,
});
}
}}
/>
</React.Fragment>
);
}
return WithPointer as T;
};
export type Data = { id: null | string; name: null | string; target: null | HTMLElement };
export default function PointerContainer({
onElementChange,
...props
}: BoxProps & { onElementChange?: (data: Data) => void }) {
const container = React.useRef<HTMLDivElement>(null);
const [data, setData] = React.useState<Data>({
id: null,
name: null,
target: null,
});
const handleMouseOver = React.useMemo(
() =>
debounce((elementData: Data) => {
setData(elementData);
}, 200),
[],
);
React.useEffect(() => {
if (onElementChange) {
onElementChange(data);
}
}, [data, onElementChange]);
return (
<PointerContext.Provider value={handleMouseOver}>
<Box
ref={container}
{...props}
onMouseLeave={() => handleMouseOver({ id: null, name: null, target: null })}
sx={{ position: 'relative', ...props.sx }}
>
{props.children}
{container.current && data.target && (
<Box
sx={{
border: '1px solid',
borderColor: '#0072E5',
pointerEvents: 'none',
position: 'absolute',
zIndex: 10,
transition: 'none !important',
...(() => {
const containerRect = container.current.getBoundingClientRect();
const targetRect = data.target.getBoundingClientRect();
return {
top: targetRect.top - containerRect.top,
left: targetRect.left - containerRect.left,
width: `${targetRect.width}px`,
height: `${targetRect.height}px`,
};
})(),
}}
>
<Box
sx={{
bgcolor: '#0072E5',
borderTopLeftRadius: '2px',
borderTopRightRadius: '2px',
px: 0.5,
position: 'absolute',
top: 0,
transform: 'translateY(-100%)',
left: -1,
}}
>
<Typography
color="#fff"
sx={{ fontSize: '0.625rem', fontWeight: 500, display: 'block' }}
>
{data.name}
</Typography>
</Box>
</Box>
)}
</Box>
</PointerContext.Provider>
);
}