// As defined on the list of supported events: https://reactjs.org/docs/events.html export const clipboardEvents = ['onCopy', 'onCut', 'onPaste'] as const; export const compositionEvents = [ 'onCompositionEnd', 'onCompositionStart', 'onCompositionUpdate', ] as const; export const focusEvents = ['onFocus', 'onBlur'] as const; export const formEvents = ['onInput', 'onInvalid', 'onReset', 'onSubmit'] as const; export const imageEvents = ['onLoad', 'onError'] as const; export const keyboardEvents = ['onKeyDown', 'onKeyPress', 'onKeyUp'] as const; export const mediaEvents = [ 'onAbort', 'onCanPlay', 'onCanPlayThrough', 'onDurationChange', 'onEmptied', 'onEncrypted', 'onEnded', 'onError', 'onLoadedData', 'onLoadedMetadata', 'onLoadStart', 'onPause', 'onPlay', 'onPlaying', 'onProgress', 'onRateChange', 'onSeeked', 'onSeeking', 'onStalled', 'onSuspend', 'onTimeUpdate', 'onVolumeChange', 'onWaiting', ] as const; export const mouseEvents = [ 'onClick', 'onContextMenu', 'onDoubleClick', 'onMouseDown', 'onMouseEnter', 'onMouseLeave', 'onMouseMove', 'onMouseOut', 'onMouseOver', 'onMouseUp', ] as const; export const dragEvents = [ 'onDrag', 'onDragEnd', 'onDragEnter', 'onDragExit', 'onDragLeave', 'onDragOver', 'onDragStart', 'onDrop', ] as const; export const selectionEvents = ['onSelect'] as const; export const touchEvents = ['onTouchCancel', 'onTouchEnd', 'onTouchMove', 'onTouchStart'] as const; export const pointerEvents = [ 'onPointerDown', 'onPointerMove', 'onPointerUp', 'onPointerCancel', 'onGotPointerCapture', 'onLostPointerCapture', 'onPointerEnter', 'onPointerLeave', 'onPointerOver', 'onPointerOut', ] as const; export const uiEvents = ['onScroll'] as const; export const wheelEvents = ['onWheel'] as const; export const animationEvents = [ 'onAnimationStart', 'onAnimationEnd', 'onAnimationIteration', ] as const; export const transitionEvents = ['onTransitionEnd'] as const; export const otherEvents = ['onToggle'] as const; export const changeEvents = ['onChange'] as const; export const allEvents = [ ...clipboardEvents, ...compositionEvents, ...focusEvents, ...formEvents, ...imageEvents, ...keyboardEvents, ...mediaEvents, ...mouseEvents, ...dragEvents, ...selectionEvents, ...touchEvents, ...pointerEvents, ...uiEvents, ...wheelEvents, ...animationEvents, ...transitionEvents, ...changeEvents, ...otherEvents, ] as const; type AllEvents = (typeof allEvents)[number]; // eslint-disable-next-line @typescript-eslint/no-explicit-any type EventHandler = (event: any, args: ArgsType) => void; // Creates inferred type for event handler without args. type EventHandlerWithoutArgs = OriginalEventHandler extends ( event: infer Event, args: ArgsType, ) => void ? (event: Event) => void : never; export type EventProps = { [K in AllEvents]?: EventHandler; }; type Props = Record & EventProps; type EventPropsWithoutArgs = { [K in keyof PropsType as K extends AllEvents ? K : never]: EventHandlerWithoutArgs< ArgsType, PropsType[K] >; }; /** * Returns an object with on-event callback props curried with provided args. * @param {Object} props Props passed to a component. * @param {Function=} getArgs A function that returns argument(s) on-event callbacks * shall be curried with. */ export default function makeEventProps< ArgsType, PropsType extends Props = Props, >( props: PropsType, getArgs?: (eventName: string) => ArgsType, ): EventPropsWithoutArgs { const eventProps: EventPropsWithoutArgs = {} as EventPropsWithoutArgs< ArgsType, PropsType >; allEvents.forEach((eventName) => { type EventHandlerType = EventPropsWithoutArgs[typeof eventName]; const eventHandler = props[eventName]; if (!eventHandler) { return; } if (getArgs) { eventProps[eventName] = ((event) => eventHandler(event, getArgs(eventName))) as EventHandlerType; } else { eventProps[eventName] = eventHandler as EventHandlerType; } }); return eventProps; }