ember-attacher
Version:
Tooltips and popovers for Ember.js apps
322 lines (231 loc) • 10.5 kB
Markdown
# ember-attacher
[](http://badge.fury.io/js/ember-attacher)
[](https://www.npmjs.com/package/ember-attacher)
[](https://www.npmjs.com/package/ember-attacher)
[](http://emberobserver.com/addons/ember-attacher)
[](https://github.com/tylerturdenpants/ember-attacher/actions/workflows/ci.yml)
## Compatibility
------------------------------------------------------------------------------
***NOTE***
The addon's dummy application needs a serious upgrade. I [](https://github.com/tylerturdenpants) have removed as much orf the support on flexi and flexi styles that I can, but the dummy app will still produces warnings regarding SASS division.
As the only supporter of this addon, a father, and a full time employee at a software company, I really need the help to fully pull flexi out and to revamp the dummy app. Once these blockers are removed I can do the work to bring this addon into the future.
----
* Ember.js v3.20 through v5.12
* Ember CLI v3.13 or above
* Node.js v12 or above
Tooltips and popovers made easy.
Just drop an `<AttachTooltip/>` or `<AttachPopover/>` in a parent and your floating element is ready to go!
```hbs
<button>
Click me
<AttachTooltip>
I'm a tooltip!
</AttachTooltip>
</button>
<button class="other-button">
No click me!
<AttachPopover
='ember-attacher'
='click'
={{true}}
='click'
>
I'm a popover!
</AttachPopover>
</button>
```
See [the example site](https://tylerturdenpants.github.io/ember-attacher/) for a demonstration of all
available options.
## Installation
```bash
ember install ember-attacher
```
If you're upgrading from 1.x to 2.x [see the upgrade guide](./docs/upgrade-guide-2.0.md).
## Components
### `<AttachPopover/>`
A popover attacher.
* Has no default class or roles.
* Does not modify the target in any way.
* Adds `aria-hidden` attribute to the floating element
### `<AttachTooltip/>`
A tooltip attacher. Subclass of `<AttachPopover/>`
* Has the default class `'ember-attacher-floating ember-attacher-tooltip'`
* The default tooltip classes can be modified by altering the `tooltipClass`
default. See [here](#user-defined-defaults) for details on editing default values.
* Default `role` attribute of `tooltip`.
* Causes the target to gain an `aria-describedby` attribute set to the tooltip's ID.
## Options
Below is a list of all available options, along with their defaults.
```javascript
{
// The animation used to present the animation.
// Options: ['fade', 'fill', 'none', 'perspective', 'scale', 'shift']
animation: 'fill',
// Whether or not an arrow will be displayed next to the attachment.
arrow: false,
// Add listeners that will automatically call an update function
// Pass `true` to use the Floating UI default options or Options object to override them
// Example: { ancestorScroll: false }
// For more details see https://floating-ui.com/docs/autoUpdate
autoUpdate: false,
// A class that will be applied to the attachment.
class: null,
// The flip priority of the attacment.
// Space-delimited string, any combination of ['left', 'right', 'top', 'bottom']
//
// Example: 'left top bottom'
flip: null,
// The delay, in milliseconds, before the attachment will be hidden.
hideDelay: 0,
// The duration, in milliseconds, of the hide animation.
hideDuration: 300,
// Events that will cause the attachment to hide, typically in reference to the target.
//
// Space-delimited string, any combination of:
// ['click', 'clickout', 'mouseleave blur escapekey']
hideOn: 'mouseleave blur escapekey',
// Interactive tooltips will not close when clicked or hovered over.
interactive: false,
// Set this to true if you have an interactive attachment that hides on mouseout and the
// attachment is offset from its target. This should only be the case if you are using custom
// CSS that offsets that attachment.
isOffset: false,
// Whether or not the attachment is initially shown.
isShown: false,
// If true, the attachment will only be inserted into the DOM on the first "show" trigger.
// Useful for performance reasons, but will hide your attachment from search engines.
lazyRender: false,
// A middleware array that will be merged into computePosition options
middleware: null,
// A function to be fired when the attachment's visibility changes. The new visibility is passed
// to the function as an arg.
onChange: null,
// The initial position of the attachment.
// Options: ['left', 'right', 'top', 'bottom']
placement: 'top',
// The container where the attachment's DOM will be inserted.
floatingElementContainer: '.ember-application',
// An options object that will be passed to Floating UI "computePosition" function.
floatingUiOptions: null,
// NOT RECOMMENDED: We currently allow you to pass an explicit target, but this may be removed
// in a future release.
// Please provide your thoughts here: https://github.com/kybishop/ember-attacher/issues/109
explicitTarget: null,
// Whether or not to render the attachment in place in the DOM, as opposed to
// on the floatingElementContainer. NOTE: Rendering in place can cause z-index issues.
renderInPlace: false,
// The delay, in milliseconds, before the attachment will be shown.
showDelay: 0,
// The duration, in milliseconds, of the show animation.
showDuration: 300,
// Events on the target that will cause the attachment to show. For performance reasons, we
// recommend using some combination of 'mouseenter', 'focus', and 'click'
showOn: 'mouseenter focus',
// Whether to add event listeners for attachment show and hide in the capturing phase rather
// than the bubbling phase. This should be set to true when there are elements on the page that
// are stopping event propagation in the bubbling phase, and as a result preventing correct
// showing and hiding of popovers and tooltips.
useCapture: false,
// The default padding if collision happens. Set "false" if no collision prevention needed
overflowPadding: 5
}
```
## User-defined defaults
User-defined defaults can be set in the consuming app or addon's config/environment.js. These defaults will be applied to every `<AttachPopover/>` and `<AttachTooltip/>`
```javascript
// config/environment.js
module.exports = function(environment) {
var ENV = {
emberAttacher: {
animation: 'shift',
arrow: true
}
};
}
```
You can also set the user-defined defaults separately like so:
```javascript
// config/environment.js
module.exports = function(environment) {
var ENV = {
emberAttacher: {
tooltip: {
animation: 'fade',
arrow: true
},
popover: {
animation: 'shift',
arrow: false
}
}
};
}
```
And finally you can do shared defaults along with user-defined separated defaults:
```javascript
// config/environment.js
module.exports = function(environment) {
var ENV = {
emberAttacher: {
showDuration: 300,
hideDuration: 300,
tooltip: {
animation: 'fade',
arrow: true
},
popover: {
animation: 'shift',
arrow: false
}
}
};
}
```
The full list of editable defaults can be seen [here](https://github.com/kybishop/ember-attacher/blob/master/addon/defaults.js).
## Styles
`ember-attacher` provides [styles](https://github.com/kybishop/ember-attacher/blob/master/addon/styles/addon.scss#L85) for the default tooltip class, `ember-attacher-tooltip`, but no styles are included for `{{attach-popover}}`.
Example styling for a popover [can be found in the dummy app](https://github.com/kybishop/ember-attacher/blob/master/tests/dummy/app/styles/app.scss#L132). Note how the arrow must also be styled to match the popover (background color, size, etc.)
## Testing
Use the `isVisible()` test helper to check if an attachment is visible.
```javascript
import { click, find } from 'ember-native-dom-helpers';
import { isVisible } from 'ember-attacher';
test('example', async function(assert) {
this.render(hbs`
<button id="toggle">
Click me, captain!
<AttachPopover
='attachment'
='click'
='click'
>
Click-toggled popover
<AttachPopover/>
</button>
`);
const attachment = find('#attachment');
assert.equal(isVisible(attachment), false, 'Initially hidden');
await click('#toggle');
// You can also pass in a selector
assert.equal(isVisible('#attachment'), true, 'Now shown');
});
```
## Development setup
See the [Contributing](CONTRIBUTING.md) guide for details.
## FAQ
### How animations are implemented
Attachments are composed of two containers:
* [An outer container](https://github.com/kybishop/ember-attacher/blob/master/addon/templates/components/attach-popover.hbs#L2) for positioning (via [floating-ui](https://github.com/floating-ui/floating-ui)).
* [An inner container](https://github.com/kybishop/ember-attacher/blob/master/addon/templates/components/attach-popover.hbs#L12) for the content. This is the container that is animated.
The outer container is positioned right next to the target via the CSS `transform` property. The inner container is required because animations also use `transform`, which would otherwise conflict with the container's position.
`transform` and `tansition-duration` are the CSS magic that allows animations to smoothly shift up/down, left/right, etc.
Note that animations require an implementation for each position (left, right, top, and bottom):
* <https://github.com/kybishop/ember-attacher/blob/master/addon/styles/addon.scss#L67>
* <https://github.com/kybishop/ember-attacher/blob/master/addon/styles/_mixins.scss#L75>
## Credits
* [tippy.js](https://github.com/atomiks/tippyjs), the library that inspired
ember-attacher.
* [floating-ui](https://github.com/floating-ui/floating-ui), the library that powers
positioning
* [ember-tooltips](https://github.com/sir-dunxalot/ember-tooltips), the addon that
influenced much of ember-attacher's API.