/* * Copyright 2024 Adobe. All rights reserved. * This file is licensed to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. You may obtain a copy * of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ import {RenderProps, RuntimeStyleFunction} from './types'; // taken from: https://stackoverflow.com/questions/51603250/typescript-3-parameter-list-intersection-type/51604379#51604379 type ArgTypes = T extends (props: infer V) => any ? NullToObject : never; type NullToObject = T extends (null | undefined) ? {} : T; type BoxedTupleTypes = { [P in keyof T]: [ArgTypes] }[Exclude]; type BoxedReturnTypes = { [P in keyof T]: [InferReturn] }[Exclude]; type UnboxIntersection = T extends { 0: infer U } ? U : never; type Arg = RuntimeStyleFunction | null | undefined; type NoInfer = [T, void][T extends any ? 0 : 1]; type InferReturn = T extends (props: any) => infer R ? NullToObject : never; type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; type InferReturnType = UnboxIntersection>>; // Two overloads: // 1. If a render props type is expected based on the return type, forward that type to all arguments. // 2. Otherwise, infer the return type based on the arguments. export function merge = never, X = {}>(...args: Arg, NoInfer>[]): RuntimeStyleFunction; export function merge[]>(...args: T): RuntimeStyleFunction, UnboxIntersection>>>; export function merge(...args: any[]): RuntimeStyleFunction { return (props) => { return mergeStyles(...args.map(f => typeof f === 'function' ? f(props) : null)); }; } export function mergeStyles(...styles: (string | null | undefined)[]): string { let map = new Map(); for (let style of styles) { if (style) { for (let [k, v] of parse(style)) { map.set(k, v); } } } let res = ''; for (let value of map.values()) { res += value; } return res; } function parse(s: string) { let properties = new Map(); let i = 0; while (i < s.length) { while (i < s.length && s[i] === ' ') { i++; } let start = i; readValue(); // property index // read conditions (up to the last segment) let condition = i; while (i < s.length && s[i] !== ' ') { readValue(); } let property = s.slice(start, condition); properties.set(property, (properties.get(property) || '') + ' ' + s.slice(start, i)); } function readValue() { if (s[i] === '-') { // the beginning and end of arbitrary values are marked with - while (i < s.length && s[i] !== ' ') { i++; if (s[i] === '-') { i++; break; } } } else { while (i < s.length) { if (s[i] === '_') { i++; } else { i++; break; } } } } return properties; }