/**
 * Merges multiple sets of React props. It follows the Object.assign pattern where the rightmost object's fields overwrite
 * the conflicting ones from others. This doesn't apply to event handlers, `className` and `style` props.
 * Event handlers are merged such that they are called in sequence (the rightmost one being called first),
 * and allows the user to prevent the subsequent event handlers from being
 * executed by attaching a `preventBaseUIHandler` method.
 * It also merges the `className` and `style` props, whereby the classes are concatenated
 * and the rightmost styles overwrite the subsequent ones.
 *
 * Props can either be provided as objects or as functions that take the previous props as an argument.
 * The function will receive the merged props up to that point (going from left to right):
 * so in the case of `(obj1, obj2, fn, obj3)`, `fn` will receive the merged props of `obj1` and `obj2`.
 * The function is responsible for chaining event handlers if needed (i.e. we don't run the merge logic).
 *
 * Event handlers returned by the functions are not automatically prevented when `preventBaseUIHandler` is called.
 * They must check `event.baseUIHandlerPrevented` themselves and bail out if it's true.
 *
 * @important **`ref` is not merged.**
 * @param props props to merge.
 * @returns the merged props.
 */
export function mergeProps(...props) {
  if (props.length === 0) {
    return {};
  }
  if (props.length === 1) {
    return resolvePropsGetter(props[0], {});
  }
  let merged = resolvePropsGetter(props[0], {});
  for (let i = 1; i < props.length; i += 1) {
    const propsOrPropsGetter = props[i];
    if (!propsOrPropsGetter) {
      continue;
    }
    if (isPropsGetter(propsOrPropsGetter)) {
      merged = propsOrPropsGetter(merged);
    } else {
      merged = merge(merged, propsOrPropsGetter);
    }
  }
  return merged ?? {};
}
function resolvePropsGetter(propsOrPropsGetter, previousProps) {
  if (isPropsGetter(propsOrPropsGetter)) {
    return propsOrPropsGetter(previousProps);
  }
  return propsOrPropsGetter ?? {};
}

/**
 * Merges two sets of props. In case of conflicts, the external props take precedence.
 */
function merge(internalProps, externalProps) {
  if (!externalProps) {
    if (!internalProps) {
      return {};
    }
    return internalProps;
  }
  if (!internalProps) {
    return externalProps;
  }
  return Object.entries(externalProps).reduce((mergedProps, [propName, externalPropValue]) => {
    if (isEventHandler(propName, externalPropValue)) {
      mergedProps[propName] = mergeEventHandlers(internalProps[propName], externalPropValue);
    } else if (propName === 'style') {
      mergedProps[propName] = mergeStyles(internalProps.style, externalPropValue);
    } else if (propName === 'className') {
      mergedProps[propName] = mergeClassNames(internalProps.className, externalPropValue);
    } else {
      mergedProps[propName] = externalPropValue;
    }
    return mergedProps;
  }, {
    ...internalProps
  });
}
function isEventHandler(key, value) {
  // This approach is more efficient than using a regex.
  const thirdCharCode = key.charCodeAt(2);
  return key[0] === 'o' && key[1] === 'n' && thirdCharCode >= 65 /* A */ && thirdCharCode <= 90 /* Z */ && typeof value === 'function';
}
function isPropsGetter(propsOrPropsGetter) {
  return typeof propsOrPropsGetter === 'function';
}
function mergeEventHandlers(ourHandler, theirHandler) {
  return event => {
    if (isSyntheticEvent(event)) {
      const baseUIEvent = event;
      makeEventPreventable(baseUIEvent);
      const result = theirHandler(baseUIEvent);
      if (!baseUIEvent.baseUIHandlerPrevented) {
        ourHandler?.(baseUIEvent);
      }
      return result;
    }
    const result = theirHandler(event);
    ourHandler?.(event);
    return result;
  };
}
export function makeEventPreventable(event) {
  event.preventBaseUIHandler = () => {
    event.baseUIHandlerPrevented = true;
  };
  return event;
}
function mergeStyles(ourStyle, theirStyle) {
  if (theirStyle || ourStyle) {
    return {
      ...ourStyle,
      ...theirStyle
    };
  }
  return undefined;
}
function mergeClassNames(ourClassName, theirClassName) {
  if (theirClassName) {
    if (ourClassName) {
      // eslint-disable-next-line prefer-template
      return theirClassName + ' ' + ourClassName;
    }
    return theirClassName;
  }
  return ourClassName;
}
function isSyntheticEvent(event) {
  return event != null && typeof event === 'object' && 'nativeEvent' in event;
}