Skip to content

feat(v-b-hover): new directive for reacting to hover changes #4771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 48 commits into from
Feb 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
20cfdd5
feat(v-b-hover): new directive for reacting to hover changes
tmorehouse Feb 15, 2020
c66ad63
Create README.md
tmorehouse Feb 15, 2020
d3ba083
Create index.js
tmorehouse Feb 15, 2020
4b65123
Create index.d.ts
tmorehouse Feb 15, 2020
b0e9b03
Create package.json
tmorehouse Feb 15, 2020
af802c2
Update index.d.ts
tmorehouse Feb 15, 2020
99460c1
Update index.js
tmorehouse Feb 15, 2020
f57df03
Update hover.js
tmorehouse Feb 15, 2020
faa1df5
Update hover.js
tmorehouse Feb 15, 2020
50b2d88
Update README.md
tmorehouse Feb 15, 2020
d338588
Update package.json
tmorehouse Feb 15, 2020
28539dc
Update hover.js
tmorehouse Feb 15, 2020
e23accb
Update hover.js
tmorehouse Feb 15, 2020
a557933
Update hover.js
tmorehouse Feb 15, 2020
c9bccc0
Update hover.js
tmorehouse Feb 15, 2020
706b342
Create hover.spec.js
tmorehouse Feb 15, 2020
fec437f
Update hover.spec.js
tmorehouse Feb 15, 2020
aad4fa3
Update hover.spec.js
tmorehouse Feb 15, 2020
d9be513
Update hover.spec.js
tmorehouse Feb 15, 2020
dc86219
Update README.md
tmorehouse Feb 15, 2020
7de55a0
Update README.md
tmorehouse Feb 15, 2020
e54b914
Update index.js
tmorehouse Feb 15, 2020
dd779cd
Update README.md
tmorehouse Feb 15, 2020
0faf33f
Update README.md
jacobmllr95 Feb 15, 2020
9a158c2
Update hover.js
jacobmllr95 Feb 15, 2020
e990362
Merge branch 'feat/v-b-hover' of https://github.com/bootstrap-vue/boo…
jacobmllr95 Feb 15, 2020
acddac3
Update hover.js
jacobmllr95 Feb 15, 2020
52b4356
Update hover.js
tmorehouse Feb 15, 2020
4454bf9
Update hover.js
tmorehouse Feb 15, 2020
0b3f0eb
Update hover.js
tmorehouse Feb 15, 2020
8075818
Improve handler change handling
jacobmllr95 Feb 15, 2020
8a989c0
Update hover.js
tmorehouse Feb 15, 2020
26d92f3
Merge branch 'feat/v-b-hover' of https://github.com/bootstrap-vue/boo…
jacobmllr95 Feb 15, 2020
66c7f36
Merge branch 'feat/v-b-hover' of https://github.com/bootstrap-vue/boo…
jacobmllr95 Feb 15, 2020
f0be2d1
chore: delete hoverswap private util component in favor of `v-b-hover…
tmorehouse Feb 15, 2020
e6edbfa
Update hover.js
tmorehouse Feb 15, 2020
02e30aa
Update hover.js
jacobmllr95 Feb 15, 2020
aa626a1
Update hover.spec.js
jacobmllr95 Feb 15, 2020
05aac2c
Update hover.js
jacobmllr95 Feb 15, 2020
0fe7990
Update hover.js
tmorehouse Feb 15, 2020
51543c7
Update hover.js
tmorehouse Feb 15, 2020
cb50abd
Update hover.js
jacobmllr95 Feb 15, 2020
2c1b8f8
Update hover.js
tmorehouse Feb 15, 2020
a4c4001
Update hover.js
jacobmllr95 Feb 15, 2020
06f79bd
Update hover.js
tmorehouse Feb 15, 2020
aa205de
Merge branch 'feat/v-b-hover' of https://github.com/bootstrap-vue/boo…
jacobmllr95 Feb 15, 2020
a1ff580
Merge branch 'feat/v-b-hover' of https://github.com/bootstrap-vue/boo…
jacobmllr95 Feb 15, 2020
bcf7e95
Update package.json
tmorehouse Feb 15, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions src/directives/hover/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Hover

> `v-b-hover` is a lightweight directive that allows you to react when an element either becomes
> hovered or unhovered.

The `v-b-hover` directive can be used as an alternative to using custom CSS to handle hover states.

The `v-b-hover` directive was added in version `2.5.0`.

## Overview

- `v-b-hover` will call your callback method with a boolean value indicating if the element is
hovered or not.
- The directive can be placed on almost any element or component.
- Internally, BootstrapVue uses this directive in several components.

## Directive syntax and usage

```html
<div v-b-hover="callback">content</div>
```

Where callback is required:

- A function reference that will be called whenever hover state changes. The callback is passed a
single boolean argument. `true` indicates that the element (or component) is hovered by the users
pointing device, or `false` if the element is not hovered.

The directive has no modifiers.

### Usage example

```html
<template>
<div v-b-hover="hoverHandler"> ... </div>
</template>

<script>
export default {
methods: {
hoverHandler(isHovered) {
if (isHovered) {
// Do something
} else {
// Do something else
}
}
}
}
</script>
```

## Live example

In the following, we are swapping icons and tet color depending on the hover state of the element:

```html
<template>
<div>
<div v-b-hover="handleHover" class="border rounded py-3 px-4">
<b-icon v-if="isHovered" icon="battery-full" scale="2"></b-icon>
<b-icon v-else icon="battery" scale="2"></b-icon>
<span class="ml-2" :class="isHovered ? 'text-danger' : ''">Hover this area</span>
</div>
</div>
</template>

<script>
export default {
data() {
return {
isHovered: false
}
},
methods: {
handleHover(hovered) {
this.isHovered = hovered
}
}
}
</script>

<!-- b-v-hover-example.vue -->
```

## Accessibility concerns

Hover state should not be used to convey special meaning, as screen reader users and keyboard only
users typically ac not typically trigger hover state on elements.
53 changes: 53 additions & 0 deletions src/directives/hover/hover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// v-b-hover directive
import { isBrowser } from '../../utils/env'
import { EVENT_OPTIONS_NO_CAPTURE, eventOnOff } from '../../utils/events'
import { isFunction } from '../../utils/inspect'

// --- Constants ---

const PROP = '__BV_hover_handler__'
const MOUSEENTER = 'mouseenter'
const MOUSELEAVE = 'mouseleave'

// --- Utility methods ---

const createListener = handler => {
const listener = evt => {
handler(evt.type === MOUSEENTER, evt)
}
listener.fn = handler
return listener
}

const updateListeners = (on, el, listener) => {
eventOnOff(on, el, MOUSEENTER, listener, EVENT_OPTIONS_NO_CAPTURE)
eventOnOff(on, el, MOUSELEAVE, listener, EVENT_OPTIONS_NO_CAPTURE)
}

// --- Directive bind/unbind/update handler ---

const directive = (el, { value: handler = null }) => {
if (isBrowser) {
const listener = el[PROP]
const hasListener = isFunction(listener)
const handlerChanged = !(hasListener && listener.fn === handler)
if (hasListener && handlerChanged) {
updateListeners(false, el, listener)
delete el[PROP]
}
if (isFunction(handler) && handlerChanged) {
el[PROP] = createListener(handler)
updateListeners(true, el, el[PROP])
}
}
}

// VBHover directive

export const VBHover = {
bind: directive,
componentUpdated: directive,
unbind(el) {
directive(el, { value: null })
}
}
61 changes: 61 additions & 0 deletions src/directives/hover/hover.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { mount, createLocalVue as CreateLocalVue } from '@vue/test-utils'
import { waitNT } from '../../../tests/utils'
import { VBHover } from './hover'

describe('v-b-hover directive', () => {
it('works', async () => {
const localVue = new CreateLocalVue()
let hovered1 = false
let hovered2 = false
const App = localVue.extend({
data() {
return {
text: 'FOO',
changeHandler: false
}
},
directives: {
BHover: VBHover
},
methods: {
handleHover1(isHovered) {
hovered1 = isHovered
},
handleHover2(isHovered) {
hovered2 = isHovered
}
},
template: `<div v-b-hover="changeHandler ? handleHover2 : handleHover1"><span>{{ text }}</span></div>`
})
const wrapper = mount(App)

expect(wrapper.isVueInstance()).toBe(true)
expect(hovered1).toBe(false)

wrapper.trigger('mouseenter')
await waitNT(wrapper.vm)

expect(hovered1).toBe(true)

wrapper.trigger('mouseleave')
await waitNT(wrapper.vm)

expect(hovered1).toBe(false)

wrapper.setData({ text: 'BAR' })

wrapper.trigger('mouseenter')
await waitNT(wrapper.vm)

expect(hovered1).toBe(true)

wrapper.setData({ changeHandler: true })

wrapper.trigger('mouseenter')
await waitNT(wrapper.vm)

expect(hovered2).toBe(true)

wrapper.destroy()
})
})
11 changes: 11 additions & 0 deletions src/directives/hover/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// VBHover
//
import Vue, { DirectiveOptions } from 'vue'
import { BvPlugin } from '../../'

// Plugin
export declare const VBHoverPlugin: BvPlugin

// directive: v-b-hover
export declare const VBHover: DirectiveOptions
8 changes: 8 additions & 0 deletions src/directives/hover/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { VBHover } from './hover'
import { pluginFactory } from '../../utils/plugins'

const VBHoverPlugin = /*#__PURE__*/ pluginFactory({
directives: { VBHover }
})

export { VBHoverPlugin, VBHover }
14 changes: 14 additions & 0 deletions src/directives/hover/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@bootstrap-vue/v-b-hover",
"version": "1.0.0",
"meta": {
"title": "Hover",
"description": "A lightweight directive that allows you to react when an element either becomes hovered or unhovered",
"directive": "VBHover",
"new": true,
"version": "2.5.0",
"expression": [
"Function"
]
}
}
1 change: 1 addition & 0 deletions src/directives/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BvPlugin } from '../'
export declare const directivesPlugin: BvPlugin

// Named exports of all directives
export * from './hover'
export * from './modal'
export * from './popover'
export * from './scrollspy'
Expand Down
2 changes: 2 additions & 0 deletions src/directives/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { pluginFactory } from '../utils/plugins'

import { VBHoverPlugin } from './hover'
import { VBModalPlugin } from './modal'
import { VBPopoverPlugin } from './popover'
import { VBScrollspyPlugin } from './scrollspy'
Expand All @@ -10,6 +11,7 @@ import { VBVisiblePlugin } from './visible'
// Main plugin for installing all directive plugins
export const directivesPlugin = /*#__PURE__*/ pluginFactory({
plugins: {
VBHoverPlugin,
VBModalPlugin,
VBPopoverPlugin,
VBScrollspyPlugin,
Expand Down
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ export { BTooltip } from './components/tooltip/tooltip'
// can be reverted back to `export * from './scrollspy'` when Webpack v5 is released
// https://github.com/webpack/webpack/pull/9203 (available in Webpack v5.0.0-alpha.15)

// export * from './directives/hover'
export { VBHoverPlugin } from './directives/hover'
export { VBHover } from './directives/hover/hover'

// export * from './directives/modal'
export { VBModalPlugin } from './directives/modal'
export { VBModal } from './directives/modal/modal'
Expand Down
61 changes: 0 additions & 61 deletions src/utils/bv-hover-swap.js

This file was deleted.

Loading