1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | import {chain} from './chain';
|
14 | import clsx from 'clsx';
|
15 | import {mergeIds} from './useId';
|
16 |
|
17 | interface Props {
|
18 | [key: string]: any
|
19 | }
|
20 |
|
21 | type PropsArg = Props | null | undefined;
|
22 |
|
23 |
|
24 | type TupleTypes<T> = { [P in keyof T]: T[P] } extends { [key: number]: infer V } ? NullToObject<V> : never;
|
25 | type NullToObject<T> = T extends (null | undefined) ? {} : T;
|
26 |
|
27 | type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
28 |
|
29 | /**
|
30 | * Merges multiple props objects together. Event handlers are chained,
|
31 | * classNames are combined, and ids are deduplicated - different ids
|
32 | * will trigger a side-effect and re-render components hooked up with `useId`.
|
33 | * For all other props, the last prop object overrides all previous ones.
|
34 | * @param args - Multiple sets of props to merge together.
|
35 | */
|
36 | export function mergeProps<T extends PropsArg[]>(...args: T): UnionToIntersection<TupleTypes<T>> {
|
37 | // Start with a base clone of the first argument. This is a lot faster than starting
|
38 | // with an empty object and adding properties as we go.
|
39 | let result: Props = {...args[0]};
|
40 | for (let i = 1; i < args.length; i++) {
|
41 | let props = args[i];
|
42 | for (let key in props) {
|
43 | let a = result[key];
|
44 | let b = props[key];
|
45 |
|
46 | // Chain events
|
47 | if (
|
48 | typeof a === 'function' &&
|
49 | typeof b === 'function' &&
|
50 |
|
51 | key[0] === 'o' &&
|
52 | key[1] === 'n' &&
|
53 | key.charCodeAt(2) >= 65 &&
|
54 | key.charCodeAt(2) <= 90
|
55 | ) {
|
56 | result[key] = chain(a, b);
|
57 |
|
58 | // Merge classnames, sometimes classNames are empty string which eval to false, so we just need to do a type check
|
59 | } else if (
|
60 | (key === 'className' || key === 'UNSAFE_className') &&
|
61 | typeof a === 'string' &&
|
62 | typeof b === 'string'
|
63 | ) {
|
64 | result[key] = clsx(a, b);
|
65 | } else if (key === 'id' && a && b) {
|
66 | result.id = mergeIds(a, b);
|
67 | // Override others
|
68 | } else {
|
69 | result[key] = b !== undefined ? b : a;
|
70 | }
|
71 | }
|
72 | }
|
73 |
|
74 | return result as UnionToIntersection<TupleTypes<T>>;
|
75 | }
|
76 |
|
\ | No newline at end of file |