FIXED: Interfaces

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

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const prettierConfig = require('./.prettierrc.js');
module.exports = {

View File

@ -1,6 +1,7 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ES6PreferShortImport" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EslintConfiguration">
<option name="fix-on-save" value="true" />
</component>
</project>

View File

@ -6,8 +6,14 @@ export default function Badges() {
return (
<div
className={'m3 m3-wrapper'}
style={{ display: 'flex', flexDirection: 'row' }}
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
>
<h1> Badges </h1>
<div>
<div
style={{

View File

@ -7,16 +7,14 @@ import useRippleEffect from '../ripple/hooks/useRippleEffect';
import React, { forwardRef, useId, useRef, useState } from 'react';
export const ButtonLayout = forwardRef<HTMLButtonElement, ButtonLayoutProps>(
function ButtonBase({ centralRipple = false, ...props }, ref) {
({ centralRipple = false, variant, ...props }, ref) => {
const [isActive, setIsActive] = useState<boolean>(false),
ripplesRef = useRef(null),
buttonId = useId(),
events = useRippleEffect(ripplesRef, setIsActive);
const { variant, disabled, className } = props;
const classes = className
? `m3 ${className} ${variant}${isActive ? ' is-active' : ''}`
const classes = props.className
? `m3 ${props.className} ${variant}${isActive ? ' is-active' : ''}`
: `m3 ${variant}${isActive ? ' is-active' : ''}`;
return (
@ -24,7 +22,7 @@ export const ButtonLayout = forwardRef<HTMLButtonElement, ButtonLayoutProps>(
{...props}
{...events}
className={classes}
disabled={disabled}
disabled={props.disabled}
id={buttonId}
ref={ref}
>
@ -40,6 +38,7 @@ export const ButtonLayout = forwardRef<HTMLButtonElement, ButtonLayoutProps>(
);
ButtonLayout.propTypes = {
variant: string,
centralRipple: bool,
children: string,
};

View File

@ -1,6 +1,6 @@
import { ButtonHTMLAttributes } from 'react';
import { IRippleProps } from '../ripple/ripple.types';
export interface ButtonLayoutProps
extends IRippleProps,
ButtonHTMLAttributes<HTMLButtonElement> {}
export type ButtonLayoutProps = IRippleProps & ButtonHTMLAttributes<HTMLButtonElement> & {
variant?: string;
};

View File

@ -5,6 +5,7 @@ export interface ButtonMainProps
extends ButtonHTMLAttributes<HTMLButtonElement> {
disabled?: boolean;
variant?: 'filled' | 'outlined' | 'elevated' | 'tonal' | 'text';
icon?: string;
}
export interface ButtonProps extends IRippleProps, ButtonMainProps {}
export type ButtonProps = IRippleProps & ButtonMainProps;

View File

@ -1,6 +1,7 @@
import { InputHTMLAttributes } from 'react';
import { IRippleProps } from '../ripple/ripple.types';
export interface CheckboxProps
extends InputHTMLAttributes<HTMLInputElement>,
IRippleProps {}
export type CheckboxProps = InputHTMLAttributes<HTMLInputElement> &
IRippleProps & {
indeterminate?: boolean;
};

View File

@ -1,12 +1,14 @@
import React from 'react';
import { ButtonHTMLAttributes } from 'react';
import { IRippleProps } from '../ripple/ripple.types';
export interface FABMainProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
export interface FABMainProps {
icon: string;
disabled?: boolean;
elevated?: boolean;
size?: 'small' | 'default' | 'large' | 'extended';
variant?: 'surface' | 'primary' | 'secondary' | 'tertiary';
}
export interface FABProps extends FABMainProps, IRippleProps {}
export type FABProps = FABMainProps &
IRippleProps &
ButtonHTMLAttributes<HTMLButtonElement>;

View File

@ -5,6 +5,7 @@ import { ButtonLayout } from '../button-layout/button-layout';
import { IconButtonProps, StateToggleIconType } from './icon-button.types';
import {
forwardRef,
MouseEventHandler,
useCallback,
useImperativeHandle,
useRef,
@ -44,7 +45,7 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
const buttonRef = useRef<HTMLButtonElement>(null);
const callback = useCallback(
(e: MouseEvent) => {
(event: MouseEventHandler<HTMLButtonElement>) => {
if (toggled) {
if (toggleIcon.state === 'selected') {
toggle('', toggled.unselected ?? 'add_circle');
@ -53,7 +54,7 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
}
}
if (props.onClick) {
props.onClick.apply(null, e);
props.onClick.apply(null, event);
}
},
[toggleIcon],
@ -72,7 +73,7 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
variant={variant ? variant : 'default'}
>
<Icon
fill={toggleIcon.state === 'selected' ? 1 : 0}
fillIcon={toggleIcon.state === 'selected' ? 1 : 0}
iconSize={28}
svgSize={40}
>

View File

@ -1,6 +1,14 @@
import React from 'react';
import { ButtonHTMLAttributes } from 'react';
import { IRippleProps } from '../ripple/ripple.types';
export interface IconButtonMainProps {
icon: string;
selected?: boolean;
disabled?: boolean;
toggled?: false | ToggleButtonType;
variant?: 'default' | 'filled' | 'tonal' | 'outlined';
}
export type StateToggleIconType = {
state: string;
icon: string;
@ -11,12 +19,6 @@ export type ToggleButtonType = {
unselected: string;
};
export interface IconButtonMainProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
icon: string;
toggled?: false | ToggleButtonType;
disabled?: boolean;
variant?: 'default' | 'filled' | 'tonal' | 'outlined';
}
export interface IconButtonProps extends IconButtonMainProps, IRippleProps {}
export type IconButtonProps = IconButtonMainProps &
IRippleProps &
ButtonHTMLAttributes<HTMLButtonElement>;

View File

@ -1,8 +1,7 @@
import { InputHTMLAttributes } from 'react';
import { IRippleProps } from '../ripple/ripple.types';
export interface RadioProps
extends InputHTMLAttributes<HTMLInputElement>,
IRippleProps {
centralRipple?: boolean;
}
export type RadioProps = InputHTMLAttributes<HTMLInputElement> &
IRippleProps & {
centralRipple?: boolean;
};

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;
}

View File

@ -15,7 +15,7 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldInterface>(
},
ref,
) => {
const [raised, setRaised] = useState<boolean>(!props.placeholder);
const [raised, setRaised] = useState<boolean>(!!props.placeholder);
const callback = (e: FocusEvent<HTMLInputElement>): void => {
if (
@ -92,10 +92,10 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldInterface>(
TextField.propTypes = {
children: string,
withBeforeIcon: bool,
withAfterIcon: bool,
className: string,
variant: oneOf(['filled', 'outlined']),
placeholder: string,
withAfterIcon: bool,
withBeforeIcon: bool,
supportingText: string,
variant: oneOf(['filled', 'outlined']),
};