aboutsummaryrefslogtreecommitdiff
path: root/src/lib/react.ts
blob: 668cdf1fbecad44181b350cf0f654c36e3d8e5bc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import {
  Children,
  cloneElement,
  type FC,
  Fragment,
  isValidElement,
  type ReactElement,
  type ReactNode,
} from 'react';

export function getFragmentChildren(children: ReactNode) {
  return (children as ReactElement)?.type === Fragment
    ? (children as ReactElement).props.children
    : children;
}

export function isValidChild(child: ReactElement, types: FC | FC[]) {
  if (!isValidElement(child)) {
    return false;
  }
  return (Array.isArray(types) ? types : [types]).find(type => type === child.type);
}

export function mapChildren(
  children: ReactNode,
  handler: (child: ReactElement, index: number) => any,
) {
  return Children.map(getFragmentChildren(children) as ReactElement[], (child, index) => {
    if (!child?.props) {
      return null;
    }
    return handler(child, index);
  });
}

export function cloneChildren(
  children: ReactNode,
  handler: (child: ReactElement, index: number) => any,
  options?: { validChildren?: any[]; onlyRenderValid?: boolean },
): ReactNode {
  if (!children) {
    return null;
  }

  const { validChildren, onlyRenderValid = false } = options || {};

  return mapChildren(children, (child, index) => {
    const invalid = validChildren && !isValidChild(child as ReactElement, validChildren);

    if (onlyRenderValid && invalid) {
      return null;
    }

    if (!invalid && isValidElement(child)) {
      return cloneElement(child, handler(child, index));
    }

    return child;
  });
}

export function renderChildren(
  children: ReactNode | ((item: any, index: number, array: any) => ReactNode),
  items: any[],
  handler: (child: ReactElement, index: number) => object | undefined,
  options?: { validChildren?: any[]; onlyRenderValid?: boolean },
): ReactNode {
  if (typeof children === 'function' && items?.length > 0) {
    return cloneChildren(items.map(children), handler, options);
  }

  return cloneChildren(getFragmentChildren(children as ReactNode), handler, options);
}

export function countChildren(children: ReactNode): number {
  return Children.count(getFragmentChildren(children));
}