// Type definitions for ui/Skinnable

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;

export interface SkinnableConfig extends Object {
  /**
 * The prop in which to pass the skinVariants value to the wrapped component. The recommended
value is "skinVariants".
 * 
 * If left unset, the skinVariant will not be passed to the wrapped component.
 */
  variantsProp?: string;
  /**
   * The prop in which to pass the effective skin to the wrapped component.
   *
   * If left unset, the current skin will not be passed to the wrapped component.
   */
  prop?: string;
  /**
   * A hash mapping the available skin names to their CSS class name.
   *
   * The keys are accepted as the only valid values for the  `skin`  prop on the wrapped component.
   */
  skins?: object;
  /**
 * Assign a default skin from the  `skins`  list.
 * 
 * This will be used if the instantiator of the wrapped component provides no value to the
 `skin`  prop.
 */
  defaultSkin?: string;
  /**
 * Initial collection of applied variants
 * 
 * This will be used if the instantiator of the wrapped component provides no value to the
 `skinVariants`  prop.
 */
  defaultVariants?: string | string[];
  /**
 * A complete list of all supported variants.
 * 
 * These will translate to CSS class names so should not conflict with any skin names.
CamelCase is recommended for the values.
 */
  allowedVariants?: string[];
}
export interface SkinnableProps {
  /**
 * The name of the skin a component should use to render itself. Available skins are
defined in the "defaultConfig" for this HOC.
 */
  skin?: string;
  /**
 * The variant(s) on a skin that a component should use when rendering. These will
typically alter the appearance of a skin's existing definition in a way that does not
override that skin's general styling.
 * 
 * Multiple data types are supported by this prop, which afford different conveniences
and abilities. String and Array are effectively the same, supporting just additions
to the variants being applied to a component, and are much more convenient. Objects
may also be used, and have the ability to disable variants being passed by their
ancestors. Objects take the format of a basic hash, with variants as key names and
true/false Booleans as values, depicting their state. If a variant is excluded from
any version of data type used to set this prop, that variant will ignored, falling
back to the defaultVariant or parent variant, in that order.
 * 
 * skinVariants examples:
 * ```
 // String
 skinVariants="highContrast"

 // Array
 skinVariants={['highContrast']}

 // Object
 skinVariants={{
 	highContrast: true,
 	grayscale: false
 }}
```
 */
  skinVariants?: string | string[] | object;
}
export function Skinnable<P>(
  config: SkinnableConfig,
  Component: React.ComponentType<P> | string,
): React.ComponentType<P & SkinnableProps>;

export function Skinnable<P>(
  Component: React.ComponentType<P> | string,
): React.ComponentType<P & SkinnableProps>;

export default Skinnable;
