declare module 'popper.js' {
  declare type Position = 'top' | 'right' | 'bottom' | 'left';
  declare type Placement =
    | 'auto-start'
    | 'auto'
    | 'auto-end'
    | 'top-start'
    | 'top'
    | 'top-end'
    | 'right-start'
    | 'right'
    | 'right-end'
    | 'bottom-end'
    | 'bottom'
    | 'bottom-start'
    | 'left-end'
    | 'left'
    | 'left-start';

  declare type Offset = {
    top: number,
    left: number,
    width: number,
    height: number,
    position: Position,
  };

  declare type Boundary = 'scrollParent' | 'viewport' | 'window';

  declare type Behavior = 'flip' | 'clockwise' | 'counterclockwise';

  declare type Data = {
    instance: Popper,
    placement: Placement,
    originalPlacement: Placement,
    flipped: boolean,
    hide: boolean,
    arrowElement: Element,
    styles: CSSStyleDeclaration,
    arrowStyles: CSSStyleDeclaration,
    boundaries: Object,
    offsets: {
      popper: Offset,
      reference: Offset,
      arrow: {
        top: number,
        left: number,
      },
    },
  };

  declare type ModifierFn = (data: Data, options: Object) => Data;

  declare type Padding = {
    top?: number,
    bottom?: number,
    left?: number,
    right?: number,
  }

  declare type BaseModifier = {
    order?: number,
    enabled?: boolean,
    fn?: ModifierFn,
  };

  declare type Modifiers = {
    shift?: BaseModifier,
    offset?: BaseModifier & {
      offset?: number | string,
    },
    preventOverflow?: BaseModifier & {
      priority?: Position[],
      padding?: number | Padding,
      boundariesElement?: Boundary | Element,
      escapeWithReference?: boolean,
    },
    keepTogether?: BaseModifier,
    arrow?: BaseModifier & {
      element?: string | Element | null,
    },
    flip?: BaseModifier & {
      behavior?: Behavior | Position[],
      padding?: number | Padding,
      boundariesElement?: Boundary | Element,
    },
    inner?: BaseModifier,
    hide?: BaseModifier,
    applyStyle?: BaseModifier & {
      onLoad?: Function,
      gpuAcceleration?: boolean,
    },
    computeStyle?: BaseModifier & {
      gpuAcceleration?: boolean,
      x?: 'bottom' | 'top',
      y?: 'left' | 'right',
    },

    [name: string]: (BaseModifier & { [string]: * }) | null,
  };

  declare type Options = {
    placement?: Placement,
    positionFixed?: boolean,
    eventsEnabled?: boolean,
    modifiers?: Modifiers,
    removeOnDestroy?: boolean,

    onCreate?: (data: Data) => void,

    onUpdate?: (data: Data) => void,
  };

  declare var placements: Placement;

  declare type ReferenceObject = {
    +clientHeight: number,
    +clientWidth: number,

    getBoundingClientRect():
      | ClientRect
      | {
          width: number,
          height: number,
          top: number,
          right: number,
          bottom: number,
          left: number,
        },
  };

  declare type Instance = {
    destroy: () => void,
    scheduleUpdate: () => void,
    update: () => void,
    enableEventListeners: () => void,
    disableEventListeners: () => void,
  };

  declare class Popper {
    static placements: Placement;

    constructor(
      reference: Element | ReferenceObject,
      popper: Element,
      options?: Options
    ): Instance;
  }

  declare module.exports: Class<Popper>;
}
