@mediamonks/react-kit 2.0.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import { type ReactElement, type ReactFragment } from 'react';
2
- export declare function childrenAreEqual(previousChildren: ReactElement | ReactFragment | null, nextChildren: ReactElement | ReactFragment | null): boolean;
1
+ import { type ReactElement } from 'react';
2
+ export declare function childrenAreEqual(previousChildren: ReactElement | null, nextChildren: ReactElement | null): boolean;
@@ -1,11 +1,12 @@
1
1
  /// <reference types="gsap/types/split-text.js" />
2
2
  import SplitText from 'gsap/SplitText';
3
- import { type ComponentProps, type ReactElement, type RefAttributes } from 'react';
3
+ import { type ComponentPropsWithoutRef, type JSX, type JSXElementConstructor, type ReactNode, type RefObject } from 'react';
4
4
  /**
5
5
  * Allowed as prop values
6
6
  */
7
- type KnownTarget = Exclude<keyof JSX.IntrinsicElements, symbol | object>;
7
+ type KnownTarget = keyof JSX.IntrinsicElements | JSXElementConstructor<any>;
8
8
  type SplitTextWrapperProps<T extends KnownTarget> = {
9
+ ref: RefObject<SplitText>;
9
10
  /**
10
11
  * The SplitText variables
11
12
  * @link https://greensock.com/docs/v3/Plugins/SplitText
@@ -19,11 +20,6 @@ type SplitTextWrapperProps<T extends KnownTarget> = {
19
20
  * Split the first element child of the element passed
20
21
  */
21
22
  splitFirstElementChild?: boolean;
22
- };
23
- /**
24
- * Polymorphic component type, necessary to get all the attributes/props for the
25
- * as prop component
26
- */
27
- type SplitTextWrapperComponent = <T extends KnownTarget = 'div'>(props: SplitTextWrapperProps<T> & Omit<ComponentProps<T>, keyof SplitTextWrapperProps<T> | 'ref'> & RefAttributes<SplitText | null>) => ReactElement;
28
- export declare const SplitTextWrapper: SplitTextWrapperComponent;
23
+ } & ComponentPropsWithoutRef<T>;
24
+ export declare function SplitTextWrapper<T extends KnownTarget>({ variables, as, children, splitFirstElementChild, ref, ...props }: SplitTextWrapperProps<T>): ReactNode;
29
25
  export {};
@@ -2,31 +2,31 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  import gsap from 'gsap';
3
3
  import SplitText from 'gsap/SplitText';
4
4
  import { renderToString } from 'react-dom/server';
5
- import { ensuredForwardRef } from '../../../hocs/ensuredForwardRef/ensuredForwardRef.js';
5
+ import { useObjectRef } from '../../../hooks/useObjectRef/useObjectRef.js';
6
6
  if (typeof window !== 'undefined') {
7
7
  gsap.registerPlugin(SplitText);
8
8
  }
9
- // @ts-expect-error polymorphic type is not compatible with ensuredForwardRef function factory
10
- export const SplitTextWrapper = ensuredForwardRef(({ variables = {}, as, children, splitFirstElementChild = false, ...props }, ref) => {
9
+ export function SplitTextWrapper({ variables = {}, as, children, splitFirstElementChild = false, ref, ...props }) {
10
+ const objectRef = useObjectRef(ref);
11
11
  /**
12
12
  * Not using useCallback on purpose so that a new SplitText instance is
13
13
  * created whenever this component rerenders the children
14
14
  */
15
- const onRef = async (element) => {
16
- if (ref.current && 'isSplit' in ref.current && ref.current.isSplit) {
15
+ const onRef = (element) => {
16
+ if (objectRef.current && 'isSplit' in objectRef.current && objectRef.current.isSplit) {
17
17
  return;
18
18
  }
19
19
  if (splitFirstElementChild && element.childElementCount > 1) {
20
20
  // eslint-disable-next-line no-console
21
21
  console.warn("Split text wrapper should only contain 1 element when 'splitFirstElementChild' is set to true");
22
22
  }
23
- ref.current = new SplitText(splitFirstElementChild ? element.firstElementChild : element, variables);
23
+ objectRef.current = new SplitText(splitFirstElementChild ? element.firstElementChild : element, variables);
24
24
  };
25
- const Component = (as ?? 'div');
25
+ const Component = as ?? 'div';
26
26
  return (_jsx(Component, { ...props, dangerouslySetInnerHTML: props.dangerouslySetInnerHTML ?? {
27
27
  // eslint-disable-next-line @typescript-eslint/naming-convention, react/jsx-no-useless-fragment
28
28
  __html: renderToString(_jsx(_Fragment, { children: children })),
29
29
  },
30
30
  // eslint-disable-next-line react/jsx-no-bind
31
31
  ref: onRef }));
32
- });
32
+ }
@@ -4,7 +4,9 @@ import { useCallback, useEffect, useRef } from 'react';
4
4
  * unmounted or when a new callback function is provided.
5
5
  */
6
6
  export function useAnimation(callback, dependencies) {
7
- const animation = useRef();
7
+ const animation = useRef(
8
+ // eslint-disable-next-line unicorn/no-useless-undefined
9
+ undefined);
8
10
  // eslint-disable-next-line react-hooks/exhaustive-deps, no-underscore-dangle
9
11
  const _callback = useCallback(callback, dependencies);
10
12
  useEffect(() => {
@@ -1,3 +1,3 @@
1
- import { type MutableRefObject } from 'react';
1
+ import { type RefObject } from 'react';
2
2
  import { type Unreffable } from '../../../utils/unref/unref.js';
3
- export declare function useFlip(ref: Unreffable<HTMLElement | null>, flipStateVariables?: Flip.FromToVars): MutableRefObject<Flip.FlipState | undefined>;
3
+ export declare function useFlip(ref: Unreffable<HTMLElement | null>, flipStateVariables?: Flip.FromToVars): RefObject<Flip.FlipState | undefined>;
@@ -6,7 +6,9 @@ if (typeof window !== 'undefined') {
6
6
  gsap.registerPlugin(Flip);
7
7
  }
8
8
  export function useFlip(ref, flipStateVariables = {}) {
9
- const flipStateRef = useRef();
9
+ const flipStateRef = useRef(
10
+ // eslint-disable-next-line unicorn/no-useless-undefined
11
+ undefined);
10
12
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
11
13
  if (globalThis.window !== undefined) {
12
14
  flipStateRef.current = Flip.getState(unref(ref));
@@ -1,4 +1,7 @@
1
1
  import { type ForwardRefExoticComponent, type MutableRefObject, type PropsWithoutRef, type ReactElement, type RefAttributes } from 'react';
2
+ /**
3
+ * @deprecated use `useObjectRef` instead
4
+ */
2
5
  export interface EnsuredForwardRefRenderFunction<T, P = Record<string | number | symbol, unknown>> {
3
6
  (props: P, ref: MutableRefObject<T | null>): ReactElement | null;
4
7
  displayName?: string | undefined;
@@ -8,4 +11,7 @@ export interface EnsuredForwardRefRenderFunction<T, P = Record<string | number |
8
11
  defaultProps?: never;
9
12
  propTypes?: never;
10
13
  }
14
+ /**
15
+ * @deprecated use `useObjectRef` instead
16
+ */
11
17
  export declare function ensuredForwardRef<T, P = Record<string | number | symbol, unknown>>(Component: EnsuredForwardRefRenderFunction<T, P>): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;
@@ -1,4 +1,7 @@
1
1
  import { createRef, forwardRef, useMemo, } from 'react';
2
+ /**
3
+ * @deprecated use `useObjectRef` instead
4
+ */
2
5
  export function ensuredForwardRef(
3
6
  // eslint-disable-next-line @typescript-eslint/naming-convention
4
7
  Component) {
@@ -26,6 +29,7 @@ Component) {
26
29
  },
27
30
  });
28
31
  }, [ref]);
32
+ // @ts-expect-error - deprecated in React 19
29
33
  return Component(props, refObject);
30
34
  });
31
35
  }
@@ -15,7 +15,9 @@ import { useRefValue } from '../useRefValue/useRefValue.js';
15
15
  */
16
16
  export function useIntersectionObserver(targetOrTargets, callback, options) {
17
17
  const callbackRef = useRefValue(callback);
18
- const intersectionObserverInstance = useClientSideValue(() => new IntersectionObserver((entries, observer) => callbackRef.current?.(entries, observer), options));
18
+ const intersectionObserverInstance = useClientSideValue(() => new IntersectionObserver((entries, observer) => {
19
+ callbackRef.current?.(entries, observer);
20
+ }, options));
19
21
  useEffect(() => {
20
22
  const targets = Array.isArray(targetOrTargets.current)
21
23
  ? targetOrTargets.current
@@ -9,7 +9,9 @@ import { useRefValue } from '../useRefValue/useRefValue.js';
9
9
  */
10
10
  export function useInterval(callback, ms, enabled = true) {
11
11
  const callbackRef = useRefValue(callback);
12
- const intervalRef = useRef();
12
+ const intervalRef = useRef(
13
+ // eslint-disable-next-line unicorn/no-useless-undefined
14
+ undefined);
13
15
  useEffect(() => {
14
16
  if (!enabled) {
15
17
  clearInterval(intervalRef.current);
@@ -1,5 +1,5 @@
1
- import { type MutableRefObject } from 'react';
1
+ import { type RefObject } from 'react';
2
2
  /**
3
3
  * Retrieves the duration of the audio / video file in seconds.
4
4
  */
5
- export declare function useMediaDuration(mediaElementRef: MutableRefObject<HTMLMediaElement | null>): number;
5
+ export declare function useMediaDuration(mediaElementRef: RefObject<HTMLMediaElement | null>): number;
@@ -0,0 +1,18 @@
1
+ import { type Ref, type RefObject } from 'react';
2
+ /**
3
+ * A React hook that creates a memoized object reference which can handle both RefObject and function refs.
4
+ * This hook provides a unified way to work with different types of React refs.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * // Using with RefObject
9
+ * const elementRef = useRef<HTMLDivElement>(null);
10
+ * const objRef = useObjectRef(elementRef);
11
+ *
12
+ * // Using with function ref
13
+ * const objRef = useObjectRef((element) => {
14
+ * console.log('Element changed:', element);
15
+ * });
16
+ * ```
17
+ */
18
+ export declare function useObjectRef<T>(ref?: Ref<T>): RefObject<T | null>;
@@ -0,0 +1,35 @@
1
+ import { createRef, useMemo } from 'react';
2
+ /**
3
+ * A React hook that creates a memoized object reference which can handle both RefObject and function refs.
4
+ * This hook provides a unified way to work with different types of React refs.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * // Using with RefObject
9
+ * const elementRef = useRef<HTMLDivElement>(null);
10
+ * const objRef = useObjectRef(elementRef);
11
+ *
12
+ * // Using with function ref
13
+ * const objRef = useObjectRef((element) => {
14
+ * console.log('Element changed:', element);
15
+ * });
16
+ * ```
17
+ */
18
+ export function useObjectRef(ref) {
19
+ return useMemo(() => {
20
+ if (ref !== null && ref !== undefined && 'current' in ref) {
21
+ return ref;
22
+ }
23
+ return new Proxy(createRef(), {
24
+ set(target, prop, newValue) {
25
+ if (prop !== 'current') {
26
+ return false;
27
+ }
28
+ target.current = newValue;
29
+ // Call ref function when it exists
30
+ ref?.(newValue);
31
+ return true;
32
+ },
33
+ });
34
+ }, [ref]);
35
+ }
@@ -8,6 +8,7 @@ export type Refs<T extends UnknownRecord = UnknownRecord> = {
8
8
  };
9
9
  /**
10
10
  * Type utility to create an object type where all values are MutableRefObjects
11
+ * @deprecated use `Refs` instead
11
12
  */
12
13
  export type MutableRefs<T extends UnknownRecord = UnknownRecord> = {
13
14
  [P in keyof T]: MutableRefObject<T[P] | null>;
@@ -1,8 +1,8 @@
1
1
  /// <reference types="lodash" resolution-mode="require"/>
2
- import type { MutableRefObject } from 'react';
2
+ import type { RefObject } from 'react';
3
3
  /**
4
4
  * Helper to set element in RefObject<Array>
5
5
  */
6
- declare function arrayRefInternal<T extends MutableRefObject<Array<unknown> | null>>(ref: T, index: number): (element: NonNullable<T['current']>[number]) => void;
6
+ declare function arrayRefInternal<T extends RefObject<Array<unknown> | null>>(ref: T, index: number): (element: NonNullable<T['current']>[number]) => void;
7
7
  export declare const arrayRef: typeof arrayRefInternal & import("lodash").MemoizedFunction;
8
8
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mediamonks/react-kit",
3
- "version": "2.0.5",
3
+ "version": "2.1.0",
4
4
  "description": "A collection of React hooks, components, utilities that we use at Media.Monks",
5
5
  "keywords": [
6
6
  "react",
@@ -72,7 +72,6 @@
72
72
  "@storybook/types": "^8.5.2",
73
73
  "@testing-library/react": "^16.2.0",
74
74
  "@types/lodash-es": "^4.17.11",
75
- "@types/react": "^18.0.25",
76
75
  "@vitejs/plugin-react": "^4.0.0",
77
76
  "concurrently": "^8.2.2",
78
77
  "eslint": "^8.28.0",
@@ -90,7 +89,7 @@
90
89
  "storybook": "^8.5.2",
91
90
  "ts-node": "^10.9.1",
92
91
  "typescript": "^5.0.2",
93
- "vitest": "^0.34.3",
92
+ "vitest": "^3.0.5",
94
93
  "wait-on": "^7.1.0"
95
94
  },
96
95
  "dependencies": {