114 lines
3.6 KiB
TypeScript
114 lines
3.6 KiB
TypeScript
|
"use client"
|
||
|
|
||
|
import React, {
|
||
|
useId,
|
||
|
useRef,
|
||
|
useState,
|
||
|
forwardRef,
|
||
|
useCallback,
|
||
|
useImperativeHandle
|
||
|
} from 'react';
|
||
|
import {Ripple} from "./ripple";
|
||
|
import {Ripples} from "./ripple";
|
||
|
import {RippleAreaProps} from "./ripple.types";
|
||
|
|
||
|
const TIMEOUT : number = 550;
|
||
|
const rippleAreaContext = React.createContext(false);
|
||
|
|
||
|
const RippleArea = forwardRef(
|
||
|
|
||
|
function RippleArea(props : RippleAreaProps, ref) {
|
||
|
|
||
|
const [ripples, setRipples] = useState<Array<JSX.Element>>([]),
|
||
|
rippleDomain = useRef<any>(null),
|
||
|
clicked = useRef<boolean>(false),
|
||
|
uniqueKey = useRef<number>(0),
|
||
|
uniqueId = useId();
|
||
|
|
||
|
let classes = props.className ?
|
||
|
`m3 m3-ripple-domain ${props.className}`.trimEnd() :
|
||
|
"m3 m3-ripple-domain";
|
||
|
|
||
|
const start = useCallback((event : any, cb : (state : boolean) => void) : void => {
|
||
|
|
||
|
clicked.current = true;
|
||
|
cb(clicked.current);
|
||
|
|
||
|
const rippleDomainChar = rippleDomain.current ? rippleDomain.current.getBoundingClientRect() : {
|
||
|
width: 0,
|
||
|
height: 0,
|
||
|
left: 0,
|
||
|
top: 0,
|
||
|
}
|
||
|
|
||
|
let rippleX : number = event.clientX - rippleDomainChar.left,
|
||
|
rippleY : number = event.clientY - rippleDomainChar.top,
|
||
|
rippleSizeX : number = Math.max(Math.abs(rippleDomainChar.width - rippleX), rippleX) * 2 + 2,
|
||
|
rippleSizeY : number = Math.max(Math.abs(rippleDomainChar.height - rippleY), rippleY) * 2 + 2,
|
||
|
rippleS : number = (rippleSizeX ** 2 + rippleSizeY ** 2) ** 0.5;
|
||
|
|
||
|
setRipples((prevRipples : Array<JSX.Element>) => {
|
||
|
if(prevRipples.length === 0){
|
||
|
return [
|
||
|
<Ripple rippleX={rippleX}
|
||
|
rippleY={rippleY}
|
||
|
rippleS={rippleS}
|
||
|
key={uniqueKey.current}
|
||
|
lifetime={TIMEOUT}/>
|
||
|
]
|
||
|
} else {
|
||
|
let old = [...prevRipples];
|
||
|
old.push(
|
||
|
<Ripple rippleX={rippleX}
|
||
|
rippleY={rippleY}
|
||
|
rippleS={rippleS}
|
||
|
key={uniqueKey.current}
|
||
|
lifetime={TIMEOUT}/>
|
||
|
)
|
||
|
return old;
|
||
|
}
|
||
|
}
|
||
|
)
|
||
|
|
||
|
uniqueKey.current += 1;
|
||
|
|
||
|
}, []);
|
||
|
|
||
|
const stop = useCallback((_event : any, cb : (state : boolean) => void) => {
|
||
|
|
||
|
clicked.current = false;
|
||
|
cb(clicked.current);
|
||
|
|
||
|
setRipples((prevRipples : Array<JSX.Element>) => {
|
||
|
if(prevRipples.length > 0) {
|
||
|
let old = [...prevRipples];
|
||
|
old.shift();
|
||
|
return old;
|
||
|
}
|
||
|
return prevRipples
|
||
|
});
|
||
|
|
||
|
},[]);
|
||
|
|
||
|
useImperativeHandle(ref, () => ({
|
||
|
start,
|
||
|
stop,
|
||
|
}), [start, stop]);
|
||
|
|
||
|
return (
|
||
|
<span className={classes}
|
||
|
id={uniqueId}
|
||
|
ref={rippleDomain}>
|
||
|
<rippleAreaContext.Provider value={clicked.current}>
|
||
|
<Ripples>
|
||
|
{ripples}
|
||
|
</Ripples>
|
||
|
</rippleAreaContext.Provider>
|
||
|
</span>
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
)
|
||
|
|
||
|
export {rippleAreaContext, RippleArea};
|