FIXED: Interfaces

This commit is contained in:
2024-02-01 22:24:37 +04:00
parent ab4ae37015
commit 218b8f61d6
20 changed files with 163 additions and 137 deletions

View File

@@ -1,17 +1,36 @@
import React, { useEffect, useState } from 'react';
import {
DragEventHandler,
FocusEventHandler,
MouseEventHandler,
TouchEventHandler,
useEffect,
useState,
} from 'react';
interface RippleEventHandlers {
onBlur: React.FocusEventHandler;
onContextMenu: React.MouseEventHandler;
onDragLeave: React.DragEventHandler;
onMouseDown: React.MouseEventHandler;
onMouseLeave: React.MouseEventHandler;
onMouseUp: React.MouseEventHandler;
onTouchEnd: React.TouchEventHandler;
onTouchMove: React.TouchEventHandler;
onTouchStart: React.TouchEventHandler;
onBlur: FocusEventHandler;
onContextMenu: MouseEventHandler;
onDragLeave: DragEventHandler;
onMouseDown: MouseEventHandler;
onMouseLeave: MouseEventHandler;
onMouseUp: MouseEventHandler;
onTouchEnd: TouchEventHandler;
onTouchMove: TouchEventHandler;
onTouchStart: TouchEventHandler;
}
export type InteractionEventsType = MouseEvent & TouchEvent & DragEvent & FocusEvent
export interface InteractionEvents<T>
extends MouseEvent,
TouchEvent,
DragEvent,
FocusEvent,
MouseEventHandler<T>,
DragEventHandler<T>,
FocusEventHandler<T>,
TouchEventHandler<T> {}
const UseRippleEffect = (ref, callback): undefined | RippleEventHandlers => {
const [mounted, setMounted] = useState<boolean>(false);

View File

@@ -2,7 +2,7 @@
import React, {
forwardRef,
useCallback,
ReactElement,
useId,
useImperativeHandle,
useRef,
@@ -11,26 +11,27 @@ import React, {
import { Ripple } from './ripple';
import { Ripples } from './ripple';
import { RippleAreaProps } from './ripple.types';
import {InteractionEvents, InteractionEventsType} from './hooks/useRippleEffect';
const TIMEOUT: number = 550;
const rippleAreaContext = React.createContext(false);
const RippleArea = forwardRef(function RippleArea(
{ central = false, callback, ...props }: RippleAreaProps,
ref,
) {
const [ripples, setRipples] = useState<Array<JSX.Element>>([]),
rippleDomain = useRef<any>(null),
clicked = useRef<boolean>(false),
uniqueKey = useRef<number>(0),
uniqueId = useId();
const RippleArea = forwardRef(
({ central = false, ...props }: RippleAreaProps, ref) => {
const [ripples, setRipples] = useState<Array<ReactElement>>([]),
rippleDomain = useRef(null),
clicked = useRef<boolean>(false),
uniqueKey = useRef<number>(0),
uniqueId = useId();
const classes = props.className
? `m3 m3-ripple-domain ${props.className}`.trimEnd()
: 'm3 m3-ripple-domain';
const classes = props.className
? `m3 m3-ripple-domain ${props.className}`.trimEnd()
: 'm3 m3-ripple-domain';
const start = useCallback(
(event: any, cb: (state: boolean) => void): void => {
const start = (
event: InteractionEventsType,
cb: (state: boolean) => void,
): void => {
clicked.current = true;
cb(clicked.current);
@@ -65,7 +66,7 @@ const RippleArea = forwardRef(function RippleArea(
2,
rippleS: number = (rippleSizeX ** 2 + rippleSizeY ** 2) ** 0.5;
setRipples((prevRipples: Array<JSX.Element>) => {
setRipples((prevRipples: Array<ReactElement>) => {
if (prevRipples.length === 0) {
return [
<Ripple
@@ -91,40 +92,42 @@ const RippleArea = forwardRef(function RippleArea(
});
uniqueKey.current += 1;
},
[],
);
};
const stop = useCallback((_event: any, cb: (state: boolean) => void) => {
clicked.current = false;
cb(clicked.current);
const stop = (
_event: InteractionEventsType,
cb: (state: boolean) => void,
) => {
clicked.current = false;
cb(clicked.current);
setRipples((prevRipples: Array<JSX.Element>) => {
if (prevRipples.length > 0) {
const old = [...prevRipples];
old.shift();
return old;
}
return prevRipples;
});
}, []);
setRipples((prevRipples: Array<ReactElement>) => {
if (prevRipples.length > 0) {
const old = [...prevRipples];
old.shift();
return old;
}
return prevRipples;
});
};
useImperativeHandle(
ref,
() => ({
start,
stop,
}),
[start, stop],
);
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>
);
});
return (
<span className={classes} id={uniqueId} ref={rippleDomain}>
<rippleAreaContext.Provider value={clicked.current}>
<Ripples>{ripples}</Ripples>
</rippleAreaContext.Provider>
</span>
);
},
);
export { rippleAreaContext, RippleArea };

View File

@@ -1,14 +1,11 @@
'use client';
import isEmpty from './utils/utils';
import { rippleProps } from './ripple.types';
import { RippleProps, RipplesProps } from './ripple.types';
import { rippleAreaContext } from './ripple-area';
import RippleEffectBuild from './utils/ripple-effect-builder';
import React, {
ForwardedRef,
forwardRef,
JSX,
useCallback,
ReactElement,
useContext,
useEffect,
useRef,
@@ -16,15 +13,12 @@ import React, {
useTransition,
} from 'react';
const Ripples = forwardRef(function Ripples(
props: any,
ref: ForwardedRef<any>,
) {
const Ripples = (props: RipplesProps) => {
const [ripples, setRipples] = useState({});
const firstRender = useRef<boolean>(true);
const [pending, startTransition] = useTransition();
const LifetimeEnd = useCallback((child: JSX.Element) => {
const LifetimeEnd = (child: ReactElement) => {
if (child.props.endLifetime) {
child.props.endLifetime();
}
@@ -34,10 +28,10 @@ const Ripples = forwardRef(function Ripples(
delete children[child.key];
return children;
});
}, []);
};
useEffect(() => {
if (props.children.length > 0) {
if (props.children.length > 0 && !pending) {
startTransition(() => {
if (firstRender.current || isEmpty(ripples)) {
setRipples(RippleEffectBuild(props.children, LifetimeEnd));
@@ -52,14 +46,15 @@ const Ripples = forwardRef(function Ripples(
}, [props.children]);
return <>{Object.values(ripples)}</>;
});
const Ripple = forwardRef(function Ripple(
props: rippleProps,
ref: ForwardedRef<any>,
) {
const { rippleX, rippleY, rippleS, endLifetime, lifetime } = props;
};
const Ripple = ({
rippleX,
rippleY,
rippleS,
endLifetime,
lifetime,
}: RippleProps) => {
const clicked = useContext<boolean>(rippleAreaContext);
const [classes, setClasses] = useState<string>('m3 ripple visible');
@@ -81,6 +76,6 @@ const Ripple = forwardRef(function Ripple(
}}
/>
);
});
};
export { Ripple, Ripples };

View File

@@ -1,21 +1,23 @@
import { Dispatch, SetStateAction } from 'react';
import { Dispatch, HTMLAttributes, ReactElement, SetStateAction } from 'react';
export interface IRippleProps extends PropsWithChildren<any> {
export interface RipplesProps extends HTMLAttributes<HTMLElement> {
children?: ReactElement[];
}
export interface IRippleProps extends HTMLAttributes<HTMLElement> {
centralRipple?: boolean;
}
export interface RippleAreaProps extends PropsWithChildren<any> {
export interface RippleAreaProps extends HTMLAttributes<HTMLElement> {
callback: Dispatch<SetStateAction<boolean>>;
central?: boolean;
}
export type rippleProps = {
export interface RippleProps extends HTMLAttributes<HTMLElement> {
rippleX: number;
rippleY: number;
rippleS: number;
endLifetime?: () => void;
lifetime: number;
key?: number;
};
import { PropsWithChildren } from 'react';
}

View File

@@ -1,12 +1,12 @@
import { cloneElement, ReactElement } from 'react';
export default function ArrayConvertToObj(
obj: Object,
obj: object,
nextChildren: ReactElement[],
callback: (child: any) => void,
callback: (child: ReactElement) => void,
): void {
Object.values(nextChildren).forEach(
(child: JSX.Element) =>
(child: ReactElement) =>
(obj[child.key] = cloneElement(child, {
...child.props,
endLifetime: callback.bind(null, child),

View File

@@ -1,11 +1,11 @@
import isEmpty from './utils';
import ArrayConvertToObj from './array-convert-to-obj';
import { cloneElement, ReactElement } from 'react';
import isEmpty from './utils';
export default function RippleEffectBuild(
nextRipples: ReactElement[],
callback: (child: any) => void,
prevRipples?: any | null,
callback: (child: ReactElement) => void,
prevRipples?: object | null,
) {
const empty: boolean = isEmpty(prevRipples);
const preparedRipples: object = empty ? {} : prevRipples;

View File

@@ -1,4 +1,4 @@
export default function isEmpty(obj: Object): boolean {
export default function isEmpty(obj: object): boolean {
for (const _i in obj) {
return false;
}