UNPKG

2.84 kBPlain TextView Raw
1/*
2 * Copyright 2020 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12
13import {chain} from './chain';
14import clsx from 'clsx';
15import {mergeIds} from './useId';
16
17interface Props {
18 [key: string]: any
19}
20
21// taken from: https://stackoverflow.com/questions/51603250/typescript-3-parameter-list-intersection-type/51604379#51604379
22type TupleTypes<T> = { [P in keyof T]: T[P] } extends { [key: number]: infer V } ? V : never;
23// eslint-disable-next-line no-undef, @typescript-eslint/no-unused-vars
24type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
25
26/**
27 * Merges multiple props objects together. Event handlers are chained,
28 * classNames are combined, and ids are deduplicated - different ids
29 * will trigger a side-effect and re-render components hooked up with `useId`.
30 * For all other props, the last prop object overrides all previous ones.
31 * @param args - Multiple sets of props to merge together.
32 */
33export function mergeProps<T extends Props[]>(...args: T): UnionToIntersection<TupleTypes<T>> {
34 // Start with a base clone of the first argument. This is a lot faster than starting
35 // with an empty object and adding properties as we go.
36 let result: Props = {...args[0]};
37 for (let i = 1; i < args.length; i++) {
38 let props = args[i];
39 for (let key in props) {
40 let a = result[key];
41 let b = props[key];
42
43 // Chain events
44 if (
45 typeof a === 'function' &&
46 typeof b === 'function' &&
47 // This is a lot faster than a regex.
48 key[0] === 'o' &&
49 key[1] === 'n' &&
50 key.charCodeAt(2) >= /* 'A' */ 65 &&
51 key.charCodeAt(2) <= /* 'Z' */ 90
52 ) {
53 result[key] = chain(a, b);
54
55 // Merge classnames, sometimes classNames are empty string which eval to false, so we just need to do a type check
56 } else if (
57 (key === 'className' || key === 'UNSAFE_className') &&
58 typeof a === 'string' &&
59 typeof b === 'string'
60 ) {
61 result[key] = clsx(a, b);
62 } else if (key === 'id' && a && b) {
63 result.id = mergeIds(a, b);
64 // Override others
65 } else {
66 result[key] = b !== undefined ? b : a;
67 }
68 }
69 }
70
71 return result as UnionToIntersection<TupleTypes<T>>;
72}
73
\No newline at end of file