diff --git a/.eslintrc.js b/.eslintrc.js index 781ced1..77bfc4e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires const prettierConfig = require('./.prettierrc.js'); module.exports = { diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 03d9549..f21e1f1 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,7 @@ \ No newline at end of file diff --git a/.idea/jsLinters/eslint.xml b/.idea/jsLinters/eslint.xml deleted file mode 100644 index 541945b..0000000 --- a/.idea/jsLinters/eslint.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/components/badges.tsx b/app/components/badges.tsx index 301b9ed..25ba619 100644 --- a/app/components/badges.tsx +++ b/app/components/badges.tsx @@ -6,8 +6,14 @@ export default function Badges() { return (
+

Badges

( - function ButtonBase({ centralRipple = false, ...props }, ref) { + ({ centralRipple = false, variant, ...props }, ref) => { const [isActive, setIsActive] = useState(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( {...props} {...events} className={classes} - disabled={disabled} + disabled={props.disabled} id={buttonId} ref={ref} > @@ -40,6 +38,7 @@ export const ButtonLayout = forwardRef( ); ButtonLayout.propTypes = { + variant: string, centralRipple: bool, children: string, }; diff --git a/src/primitive-components/button-layout/button-layout.types.ts b/src/primitive-components/button-layout/button-layout.types.ts index 84488b7..208688c 100644 --- a/src/primitive-components/button-layout/button-layout.types.ts +++ b/src/primitive-components/button-layout/button-layout.types.ts @@ -1,6 +1,6 @@ import { ButtonHTMLAttributes } from 'react'; import { IRippleProps } from '../ripple/ripple.types'; -export interface ButtonLayoutProps - extends IRippleProps, - ButtonHTMLAttributes {} +export type ButtonLayoutProps = IRippleProps & ButtonHTMLAttributes & { + variant?: string; +}; diff --git a/src/primitive-components/button/button.types.ts b/src/primitive-components/button/button.types.ts index e4fd1b8..8947a8e 100644 --- a/src/primitive-components/button/button.types.ts +++ b/src/primitive-components/button/button.types.ts @@ -5,6 +5,7 @@ export interface ButtonMainProps extends ButtonHTMLAttributes { disabled?: boolean; variant?: 'filled' | 'outlined' | 'elevated' | 'tonal' | 'text'; + icon?: string; } -export interface ButtonProps extends IRippleProps, ButtonMainProps {} +export type ButtonProps = IRippleProps & ButtonMainProps; diff --git a/src/primitive-components/checkbox/checkbox.types.ts b/src/primitive-components/checkbox/checkbox.types.ts index fd0b6a6..05f6bc5 100644 --- a/src/primitive-components/checkbox/checkbox.types.ts +++ b/src/primitive-components/checkbox/checkbox.types.ts @@ -1,6 +1,7 @@ import { InputHTMLAttributes } from 'react'; import { IRippleProps } from '../ripple/ripple.types'; -export interface CheckboxProps - extends InputHTMLAttributes, - IRippleProps {} +export type CheckboxProps = InputHTMLAttributes & + IRippleProps & { + indeterminate?: boolean; + }; diff --git a/src/primitive-components/fab/fab.types.ts b/src/primitive-components/fab/fab.types.ts index 6d7102c..8ffeb51 100644 --- a/src/primitive-components/fab/fab.types.ts +++ b/src/primitive-components/fab/fab.types.ts @@ -1,12 +1,14 @@ -import React from 'react'; +import { ButtonHTMLAttributes } from 'react'; import { IRippleProps } from '../ripple/ripple.types'; -export interface FABMainProps - extends React.ButtonHTMLAttributes { +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; diff --git a/src/primitive-components/icon-button/icon-button.tsx b/src/primitive-components/icon-button/icon-button.tsx index 79c7095..248627d 100644 --- a/src/primitive-components/icon-button/icon-button.tsx +++ b/src/primitive-components/icon-button/icon-button.tsx @@ -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( const buttonRef = useRef(null); const callback = useCallback( - (e: MouseEvent) => { + (event: MouseEventHandler) => { if (toggled) { if (toggleIcon.state === 'selected') { toggle('', toggled.unselected ?? 'add_circle'); @@ -53,7 +54,7 @@ export const IconButton = forwardRef( } } if (props.onClick) { - props.onClick.apply(null, e); + props.onClick.apply(null, event); } }, [toggleIcon], @@ -72,7 +73,7 @@ export const IconButton = forwardRef( variant={variant ? variant : 'default'} > diff --git a/src/primitive-components/icon-button/icon-button.types.ts b/src/primitive-components/icon-button/icon-button.types.ts index a335b5c..08b3b78 100644 --- a/src/primitive-components/icon-button/icon-button.types.ts +++ b/src/primitive-components/icon-button/icon-button.types.ts @@ -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 { - icon: string; - toggled?: false | ToggleButtonType; - disabled?: boolean; - variant?: 'default' | 'filled' | 'tonal' | 'outlined'; -} - -export interface IconButtonProps extends IconButtonMainProps, IRippleProps {} +export type IconButtonProps = IconButtonMainProps & + IRippleProps & + ButtonHTMLAttributes; \ No newline at end of file diff --git a/src/primitive-components/radio/radio.types.ts b/src/primitive-components/radio/radio.types.ts index b8a9531..59d2c0a 100644 --- a/src/primitive-components/radio/radio.types.ts +++ b/src/primitive-components/radio/radio.types.ts @@ -1,8 +1,7 @@ import { InputHTMLAttributes } from 'react'; import { IRippleProps } from '../ripple/ripple.types'; -export interface RadioProps - extends InputHTMLAttributes, - IRippleProps { - centralRipple?: boolean; -} +export type RadioProps = InputHTMLAttributes & + IRippleProps & { + centralRipple?: boolean; + }; diff --git a/src/primitive-components/ripple/hooks/useRippleEffect.ts b/src/primitive-components/ripple/hooks/useRippleEffect.ts index cb30bdb..f10ab27 100644 --- a/src/primitive-components/ripple/hooks/useRippleEffect.ts +++ b/src/primitive-components/ripple/hooks/useRippleEffect.ts @@ -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 + extends MouseEvent, + TouchEvent, + DragEvent, + FocusEvent, + MouseEventHandler, + DragEventHandler, + FocusEventHandler, + TouchEventHandler {} + const UseRippleEffect = (ref, callback): undefined | RippleEventHandlers => { const [mounted, setMounted] = useState(false); diff --git a/src/primitive-components/ripple/ripple-area.tsx b/src/primitive-components/ripple/ripple-area.tsx index 52da9e8..c39d17c 100644 --- a/src/primitive-components/ripple/ripple-area.tsx +++ b/src/primitive-components/ripple/ripple-area.tsx @@ -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>([]), - rippleDomain = useRef(null), - clicked = useRef(false), - uniqueKey = useRef(0), - uniqueId = useId(); +const RippleArea = forwardRef( + ({ central = false, ...props }: RippleAreaProps, ref) => { + const [ripples, setRipples] = useState>([]), + rippleDomain = useRef(null), + clicked = useRef(false), + uniqueKey = useRef(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) => { + setRipples((prevRipples: Array) => { if (prevRipples.length === 0) { return [ void) => { - clicked.current = false; - cb(clicked.current); + const stop = ( + _event: InteractionEventsType, + cb: (state: boolean) => void, + ) => { + clicked.current = false; + cb(clicked.current); - setRipples((prevRipples: Array) => { - if (prevRipples.length > 0) { - const old = [...prevRipples]; - old.shift(); - return old; - } - return prevRipples; - }); - }, []); + setRipples((prevRipples: Array) => { + 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 ( - - - {ripples} - - - ); -}); + return ( + + + {ripples} + + + ); + }, +); export { rippleAreaContext, RippleArea }; diff --git a/src/primitive-components/ripple/ripple.tsx b/src/primitive-components/ripple/ripple.tsx index d173e93..1262dbf 100644 --- a/src/primitive-components/ripple/ripple.tsx +++ b/src/primitive-components/ripple/ripple.tsx @@ -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, -) { +const Ripples = (props: RipplesProps) => { const [ripples, setRipples] = useState({}); const firstRender = useRef(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, -) { - const { rippleX, rippleY, rippleS, endLifetime, lifetime } = props; +}; +const Ripple = ({ + rippleX, + rippleY, + rippleS, + endLifetime, + lifetime, +}: RippleProps) => { const clicked = useContext(rippleAreaContext); const [classes, setClasses] = useState('m3 ripple visible'); @@ -81,6 +76,6 @@ const Ripple = forwardRef(function Ripple( }} /> ); -}); +}; export { Ripple, Ripples }; diff --git a/src/primitive-components/ripple/ripple.types.ts b/src/primitive-components/ripple/ripple.types.ts index 43fead5..09e33ac 100644 --- a/src/primitive-components/ripple/ripple.types.ts +++ b/src/primitive-components/ripple/ripple.types.ts @@ -1,21 +1,23 @@ -import { Dispatch, SetStateAction } from 'react'; +import { Dispatch, HTMLAttributes, ReactElement, SetStateAction } from 'react'; -export interface IRippleProps extends PropsWithChildren { +export interface RipplesProps extends HTMLAttributes { + children?: ReactElement[]; +} + +export interface IRippleProps extends HTMLAttributes { centralRipple?: boolean; } -export interface RippleAreaProps extends PropsWithChildren { +export interface RippleAreaProps extends HTMLAttributes { callback: Dispatch>; central?: boolean; } -export type rippleProps = { +export interface RippleProps extends HTMLAttributes { rippleX: number; rippleY: number; rippleS: number; endLifetime?: () => void; lifetime: number; key?: number; -}; - -import { PropsWithChildren } from 'react'; +} diff --git a/src/primitive-components/ripple/utils/array-convert-to-obj.ts b/src/primitive-components/ripple/utils/array-convert-to-obj.ts index 8a02e34..c1166ee 100644 --- a/src/primitive-components/ripple/utils/array-convert-to-obj.ts +++ b/src/primitive-components/ripple/utils/array-convert-to-obj.ts @@ -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), diff --git a/src/primitive-components/ripple/utils/ripple-effect-builder.ts b/src/primitive-components/ripple/utils/ripple-effect-builder.ts index ed8b4eb..0e9655c 100644 --- a/src/primitive-components/ripple/utils/ripple-effect-builder.ts +++ b/src/primitive-components/ripple/utils/ripple-effect-builder.ts @@ -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; diff --git a/src/primitive-components/ripple/utils/utils.ts b/src/primitive-components/ripple/utils/utils.ts index 770d0fb..7d663b9 100644 --- a/src/primitive-components/ripple/utils/utils.ts +++ b/src/primitive-components/ripple/utils/utils.ts @@ -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; } diff --git a/src/primitive-components/text-field/text-field.tsx b/src/primitive-components/text-field/text-field.tsx index 67174df..0a8551c 100644 --- a/src/primitive-components/text-field/text-field.tsx +++ b/src/primitive-components/text-field/text-field.tsx @@ -15,7 +15,7 @@ export const TextField = forwardRef( }, ref, ) => { - const [raised, setRaised] = useState(!props.placeholder); + const [raised, setRaised] = useState(!!props.placeholder); const callback = (e: FocusEvent): void => { if ( @@ -92,10 +92,10 @@ export const TextField = forwardRef( 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']), };