// Copyright 2015 Palantir Technologies, Inc. All rights reserved.
// Licensed under the BSD-3 License as modified (the “License”); you may obtain a copy
// of the license at https://github.com/palantir/blueprint/blob/master/LICENSE
// and https://github.com/palantir/blueprint/blob/master/PATENTS

@import "./common";

/*
Popovers

Popovers display floating content next to a target element.

@react-example PopoverExample

Styleguide components.popover
*/

/*
JavaScript API

The `Popover` component is available in the __@blueprintjs/core__ package.
Make sure to review the [general usage docs for JS components](#components.usage).

When creating a popover, you must specify both:
- its _content_, by setting the `content` prop, and
- its _target_, as a single child element.

The target acts as the trigger for the popover. The popover's position on the page is based on the
location of the target.

Internally, the child of a `Popover` component is wrapped in a `<span>` and rendered inline in the
HTML in the component's place.

<div class="pt-callout pt-intent-warning pt-icon-warning-sign">
  <h5>Button targets</h5>
  Buttons make great popover targets, but the `disabled` attribute on a `<button>` blocks all
  events, which interferes with the popover functioning. If you need to disable a button that
  triggers a popover, you should use [`AnchorButton`](#components.button.js.anchor-button) instead.
  See the [callout here](#components.button.js) for more details.
</div>

```
const { Popover, PopoverInteractionKind, Position } = BlueprintComponents;

export class PopoverExample extends React.Component<{}, {}> {
    public render() {
        let popoverContent = (
            <div>
                <h5>Popover Title</h5>
                <p>...</p>
                <button className="pt-button pt-popover-dismiss">Dismiss</button>
            </div>
        );

        // popover content gets no padding by default, so we can add the
        // .pt-popover-content-sizing class to get nice padding between
        // the edge of the popover and our popover content. We also get
        // a default width for our content if the popover is inline.
        return (
            <Popover content={popoverContent}
                     interactionKind={PopoverInteractionKind.CLICK}
                     popoverClassName="pt-popover-content-sizing"
                     position={Position.RIGHT}
                     useSmartPositioning={false}>
                <button className="pt-button pt-intent-primary">Popover Target</button>
            </Popover>
        );
    }
}
```

@interface IPopoverProps

Weight: -10

Styleguide components.popover.js
*/

/*
Controlled mode

If you prefer to have more control over your popover's behavior, you can specify the `isOpen`
property to use the component in __controlled mode__. You are now in charge of the component's
state.

Providing a non-null value for `isOpen` disables all automatic interaction and instead invokes
the `onInteraction` callback prop any time the opened state _would have changed_ in response to
user interaction under the current `interactionKind`. As a result, the `isDisabled` prop is
incompatible with `isOpen`, and an error is thrown if both are set.

Note that there are cases where `onInteraction` is invoked with an unchanged open state.
It is important to pay attention to the value of the `nextOpenState` parameter and determine
in your application logic whether you should care about a particular invocation (for instance,
if the `nextOpenState` is not the same as the `Popover`'s current state).

##### Example Controlled Usage

```
const { Popover, Position } = BlueprintComponent;

export class ControlledPopoverExample extends React.Component<{}, {isOpen: boolean}> {
    public state = {
        isOpen: false
    };

    public render() {
        let popoverContent = (
            <div>
                <h5>Popover Title</h5>
                <p>...</p>
                <button class="pt-button pt-popover-dismiss">Close Popover</button>
            </div>
        );

        return (
            <Popover content={popoverContent}
                     interactionKind={PopoverInteractionKind.CLICK}
                     isOpen={this.state.isOpen}
                     onInteraction={(state) => this.handleInteraction(state)}
                     position={Position.RIGHT}
                     useSmartPositioning={false}>
                <button className="pt-button pt-intent-primary">Popover Target</button>
            </Popover>
        );
    }

    private handleInteraction(nextOpenState: boolean) {
        this.setState({ isOpen: nextOpenState });
    }
}
```

Weight: -10

Styleguide components.popover.js.controlled
*/

/*
Inline popovers

By default, popover contents are rendered in a newly created element appended to `document.body`.

This works well for most layouts, because you want popovers to appear above everything else in your
application without having to manually adjust z-indices. For these "detached" popovers, we use the
[Tether](http://github.hubspot.com/tether/) library to handle positioning popovers correctly
relative to their targets. Tether is great at maintaining position in complex, dynamic UIs.

However, there are cases where it's preferable to render the popover contents inline.

Take, for example, a scrolling table where certain cells have tooltips attached to them. As row
items go out of view, you want their tooltips to slide out of the viewport as well. This is best
accomplished with inline popovers. Enable this feature by setting `inline={true}`.

It is also important to note that "inline" popovers are much more performant than "detached" ones,
particularly in response to page scrolling, because their position does not need to be recomputed on
every interaction.

Weight: -9

Styleguide components.popover.js.inline
*/

/*
Opening & closing popovers

<div class="pt-callout pt-intent-success pt-icon-info-sign">
  <h5>Conditionally styling popover targets</h5>
  When a popover is open, the target has a `.pt-popover-open` class applied to it.
  You can use this to style the target differently depending on whether the popover is open.
</div>

The different interaction kinds specify whether the popover closes when the user interacts with the
target or the rest of the document, but by default, a user interacting with a popover's *contents*
does __not__ close the popover.

To enable click-to-close behavior on an element inside a popover, simply add the class
`pt-popover-dismiss` to that element. The "Dismiss" button in the demo [above](#components.popover)
has this class. To enable this behavior on the entire popover, pass the
`popoverClassName="pt-popover-dismiss"` prop.

Note that dismiss elements won't have any effect in a popover with
`PopoverInteractionKind.HOVER_TARGET_ONLY` because there is no way to interact with the popover
content itself (the popover is dismissed the moment the user mouses away from the target).

Styleguide components.popover.js.closing
*/

/*
Modal popovers

Setting the `isModal` prop to `true` will:

- Render a transparent backdrop beneath the popover that covers the entire viewport and prevents
  interaction with the document until the popover is closed. This is useful for preventing stray
  clicks or hovers in your app when the user tries to close a popover.
- Focus the popover when opened to allow keyboard accessibility.

Clicking the backdrop will:

- _in uncontrolled mode_, close the popover.
- _in controlled mode_, invoke the `onInteraction` callback with an argument of `false`.

Modal behavior is only available for popovers with `interactionKind={PopoverInteractionKind.CLICK}`
and an error is thrown if used otherwise.

By default, the popover backdrop is invisible, but you can easily add your own styles to
`.pt-popover-backdrop` to customize the appearance of the backdrop (for example, you could give it
a translucent background color, like the backdrop for the [`Dialog`](#components.dialog) component).

The backdrop element has the same opacity fade transition as the `Dialog` backdrop.

<div class="pt-callout pt-intent-danger pt-icon-error">
  <h5>Dangerous edge case</h5>
  Rendering a `<Popover isOpen={true} isModal={true}>` outside the viewport bounds can easily break
  your application by covering the UI with an invisible non-interactive backdrop. This edge case
  must be handled by your application code or simply avoided if possible.
</div>

Styleguide components.popover.js.modal
*/

/*
Sizing popovers

Popovers by default have a max-width but no max-height. To constrain the height of a popover
and make its content scrollable, set the appropriate CSS rules on `.pt-popover-content`:

```css.less
// pass "my-popover" to `popoverClassName` prop.
.my-popover .pt-popover-content {
  max-height: $pt-grid-size * 30;
  overflow-y: auto;
}
```

Styleguide components.popover.js.sizing
*/

/*
SVG popover

`SVGPopover` is a convenience component provided for SVG contexts. It is a simple wrapper around
`Popover` that sets `rootElementTag="g"`.

Styleguide components.popover.js.svg
*/

/*
Minimal popovers

You can create a minimal popover with the `pt-minimal` modifier: `popoverClassName="pt-minimal"`.
This removes the arrow from the popover and makes the transitions more subtle.

This minimal style is recommended for popovers that are not triggered by an obvious action like the
user clicking or hovering over something. For example, a minimal popover is useful for making
typeahead menus where the menu appears almost instantly after the user starts typing.

Minimal popovers are also useful for context menus that require quick enter and leave animations to
support fast workflows. You can see an example in the [Context Menus](#components.context-menu)
documentation.

Styleguide components.popover.js.minimal
*/

$popover-width: $pt-grid-size * 35 !default;

.pt-popover {
  @include popover-sizing(
    $arrow-square-size: 30px,
    $arrow-offset: 4px,
    $arrow-target-offset: -4px
  );

  @include popover-appearance(
    $popover-background-color,
    inherit,
    $pt-popover-box-shadow,
    $pt-drop-shadow-opacity,
    $pt-border-shadow-opacity
  );

  @include scale-transition();

  display: inline-block;
  z-index: $pt-z-index-overlay;
  border-radius: $pt-border-radius;

  .pt-popover-content {
    position: relative;
    border-radius: $pt-border-radius;
  }

  &.pt-popover-content-sizing {
    .pt-popover-content {
      max-width: $popover-width;
      padding: $pt-grid-size * 2;
    }

    // only inline popovers get a width if this class is applied
    .pt-popover-target & {
      width: $popover-width;
    }
  }

  &.pt-minimal {
    // popovers with no obvious trigger will never have margin because the arrow
    // is hidden so it is safe to remove in all cases always
    margin: 0 !important; // stylelint-disable-line declaration-no-important

    .pt-popover-arrow {
      display: none;
    }

    &.pt-popover {
      @include react-transition(
        "pt-popover",
        (transform: scale(1) scale(1)),
        $duration: $pt-transition-duration,
        $after: "> &"
      );
    }
  }

  /*
  Dark theme

  The `Popover` component automatically detects whether its trigger is nested inside a `.pt-dark`
  container and applies the same class to itself. You can also explicitly apply the dark theme to
  the React component by providing the prop `popoverClassName="pt-dark"`.

  As a result, any component that you place inside a `Popover` (such as a `Menu`) automatically
  inherits the dark theme styles. Note that `Tooltip` uses `Popover` internally, so it also benefits
  from this behavior.

  This behavior can be disabled when the `Popover` is not rendered inline via the `inheritDarkTheme`
  prop.

  Weight: 10

  Styleguide components.popover.js.dark
  */

  &.pt-dark,
  .pt-dark & {
    @include popover-appearance(
      $dark-popover-background-color,
      inherit,
      $pt-dark-popover-box-shadow,
      $pt-dark-drop-shadow-opacity,
      $pt-dark-border-shadow-opacity
    );
  }
}

// the box-shadow under the arrow SVG paths
.pt-popover-arrow::before {
  display: block;
  position: absolute;
  transform: rotate(45deg);
  border-radius: $pt-border-radius - 1;
  content: "";
}

// remove arrow when popover is pinned inside its container
.pt-tether-pinned .pt-popover-arrow {
  display: none;
}

.pt-popover-backdrop {
  background: rgba($white, 0);
}

.pt-transition-container {
  @include fade-transition();
  z-index: $pt-z-index-overlay;

  &:focus {
    outline: none;
  }

  // popover content should ignore pointer events during a popover's exit transition
  &.pt-popover-leave .pt-popover-content {
    pointer-events: none;
  }
}

.pt-popover-target {
  display: inline-block;
  position: relative;
  vertical-align: top;

  // avoid tether positioning bugs when nesting `display: inline` targets (BLUEPRINT-526)
  // example: <Popover><Tooltip><button /></Tooltip></Popover>
  > .pt-popover-target {
    display: inline-block;
  }

  // position the transition container using CSS when it is inline
  .pt-transition-container {
    @include popover-inline-position();
  }
}

// fix positioning of popovers inside button groups (lots of extra nested elements)
.pt-button-group {
  &.pt-vertical .pt-popover-target {
    display: block;
  }

  &:not(.pt-vertical) {
    // make all containers float left to ensure proper sizing
    .pt-popover-target,
    .pt-tether-target {
      float: left;
    }
  }
}
