Skip to content

Commit

Permalink
feat: New popper.js based tooltip/popover directives and components (#…
Browse files Browse the repository at this point in the history
…923)

* Create tooltip.js

* Warn on mount that Popper.js not available

* Update index.js

* Create README.md

* [tooltip] Create shared class

* [popover] Common shared class

* Fix template compiler

* [popover] v-b-popover directive

* Remove tether dependency

* [tooltip.js class] Code optimizations

* create classes index

* force tip template recompile on next show, in case template changes

* [tooltip class] Add comments as to which methods PopOver overwrites

* new tooltip.vue component

* New popover.vue component

* Make new popover.vue and tooltip.vue components available

* Update README.md

* Allow reactive components in b-popover content

* b-popover: callback hooks for events

* b-tooltip: callback hooks for events

* [tooltip class]: add callback hooks

* popover meta.json: document events

* b-tooltip meta.json: document events

* [tooltip class] Allow reactive content in title element

* [popover class] Allow reactive content in title and content

* [BvEvent]: add relatedTarget descriptor

* [tooltip.js class] Better callback handling + cancelable hide & show events

Better callback handling, and allow hide & show callback to cancel hide or show of tooltip/popover

* [b-popover]: Enable canclable show and hide events

* [b-tooltip]: Add cancelable show and hide events

* [tooltip class]: Emit events on $root

* [tooltip popover] namespace events emitted on $root

Root events are emitted with namespaced event name. i.e. `bv:tooltip::show` and `bv:popover::hidden`

* [tooltip class] Prepare for future bv:modal:hidden $root event name

* Update rollup.config.js

* Update webpack.config.js

* fix: include popper.js in dist bundle

* refactor(eslint): remove semicolons to placate eslint

* refactor: rm semicolons in classes & add BvEvent to class dir

* [tooltip.js] Optimise isWithContent()

* Fixes to Popover & Tooltip Components Docs
Fix to ToolTip component

* [popover.js] Optimise isWithContent()
  • Loading branch information
tmorehouse committed Aug 28, 2017
1 parent c4358e0 commit 33c4cab
Show file tree
Hide file tree
Showing 24 changed files with 2,056 additions and 221 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Expand Up @@ -77,7 +77,7 @@ module.exports = {
'eqeqeq': 'off',
'func-call-spacing': 'error',
'func-name-matching': 'error',
'func-style': 'error',
'func-style': 'warn',
'generator-star-spacing': 'error',
'global-require': 'off',
'guard-for-in': 'error',
Expand Down
58 changes: 29 additions & 29 deletions build/rollup.config.js
@@ -1,61 +1,61 @@
const fs = require('fs');
const path = require('path');
const vue = require('rollup-plugin-vue');
const buble = require('rollup-plugin-buble');
const resolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');
const uglify = require('rollup-plugin-uglify');
const {minify} = require('uglify-es');
const CleanCSS = require('clean-css');
const {camelCase} = require('lodash');
const {name, dependencies} = require('../package.json');
const fs = require("fs");
const path = require("path");
const vue = require("rollup-plugin-vue");
const buble = require("rollup-plugin-buble");
const resolve = require("rollup-plugin-node-resolve");
const commonjs = require("rollup-plugin-commonjs");
const uglify = require("rollup-plugin-uglify");
const { minify } = require("uglify-es");
const CleanCSS = require("clean-css");
const { camelCase } = require("lodash");
const { name, dependencies } = require("../package.json");

const base = path.resolve(__dirname, '..');
const lib = path.resolve(base, 'lib');
const dist = path.resolve(base, 'dist');
const base = path.resolve(__dirname, "..");
const lib = path.resolve(base, "lib");
const dist = path.resolve(base, "dist");

// Ensure dist directory exists
if (!fs.existsSync(dist)) {
fs.mkdirSync(dist);
}

module.exports = {
input: path.resolve(lib, 'index.js'),
external: Object.keys(dependencies),
name: name,
input: path.resolve(lib, "index.js"),
// Libs in `external` will not be bundled to dist,
// since they are expected to be provided later.
// We want to include Popper.js in the build, so we exclude it here.
external: Object.keys(dependencies).filter(dep => dep !== "popper.js"),
name,
plugins: [
vue({
cssModules: {
generateScopedName: '[name]__[local]'
generateScopedName: "[name]__[local]"
},
css(style) {
fs.writeFileSync(path.resolve(dist, `${name}.css`), new CleanCSS().minify(style).styles);
}
}),
resolve({external: ['vue']}),
resolve({ external: ["vue"] }),
commonjs(),
buble({objectAssign: 'Object.assign'}),
buble({ objectAssign: "Object.assign" }),
uglify({}, minify)
],
globals: {
tether: 'Tether'
},
output: [
{
format: 'cjs',
format: "cjs",
name: camelCase(name),
file: path.resolve(dist, name + '.common.js'),
file: path.resolve(dist, name + ".common.js"),
sourcemap: true
},
{
format: 'es',
file: path.resolve(dist, name + '.esm.js'),
format: "es",
file: path.resolve(dist, name + ".esm.js"),
sourcemap: true
},
{
format: 'umd',
format: "umd",
modulename: camelCase(name),
file: path.resolve(dist, name + '.js'),
file: path.resolve(dist, name + ".js"),
sourcemap: true
}
]
Expand Down
3 changes: 1 addition & 2 deletions build/webpack.config.js
Expand Up @@ -26,8 +26,7 @@ module.exports = function (env) {
// External dependencies
config.externals = [
'vue-style-loader',
'vue',
'tether'
'vue'
];

// Library entry
Expand Down
168 changes: 107 additions & 61 deletions docs/components/popover/README.md
@@ -1,102 +1,148 @@
# Popover

> The Popover feature, which provides a tooltip-like behavior, can be easily applied to any interactive
element, and is customizable.

**Beta Warning: popover is currently under re-development to align with Boostrap V4.beta CSS
and code changes.**
element via the `<b-popover>` component or `v-b-popover` directive.
## `<b-popover>` Component Usage
```html
<template>
<div>

<h4 class="mt-sm-4 ms-sm-4 text-muted">Placement</h4>
<div class="row">
<div class="col-md-3 my-1 text-center" v-for="placement in ['top', 'left', 'right', 'bottom']">
<b-popover :placement="placement" content="Heya!">
<b-btn variant="primary">{{ placement }}</b-btn>
<div class="col-md-3 my-1 text-center"
v-for="placement in ['top', 'left', 'right', 'bottom']">
<b-btn :id="'exPopover1-'+placement" variant="primary">{{ placement }}</b-btn>
<b-popover :target-id="'exPopover1-'+placement"
:placement="placement"
title="Popover!"
:content="placement">
</b-popover>
</div>
</div>

<h4 class="mt-sm-4 ms-sm-4 text-muted">Triggers</h4>
<div class="row">
<div class="col-md-4 my-1 text-center" v-for="trigger in triggerExamples">
<b-popover :triggers="trigger" content="Trigger warning!">
<b-btn variant="primary">{{ triggersToString(trigger) }}</b-btn>
</b-popover>
</div>
</div>

<h4 class="mt-sm-4 ms-sm-4 text-muted">Content via properties or slots</h4>
<div class="row">
<div class="col-md-6 my-1 text-center">
<b-popover content="Embedding content using properties is easier as well as simpler to make dynamic.">
<b-btn variant="primary">Using properties</b-btn>
<b-btn id="exPopover2" variant="primary">Using properties</b-btn>
<b-popover target-id="exPopover2"
title="Prop Examples"
triggers="hover focus"
content="Embedding content using properties is easy">
</b-popover>
</div>
<div class="col-md-6 my-1 text-center">
<b-popover>
<b-btn variant="primary">Using slots</b-btn>
<span slot="content">
Embedding content <span
style="color: red">using slots</span> affords you <em>greater <strong>control.</strong></em>
</span>
<b-btn id="exPopover3" variant="primary">Using slots</b-btn>
<b-popover target-id="exPopover3">
<template slot="title">Content via Slots</template>
Embedding content <span class="text-danger">using slots</span> affords you
<em>greater <strong>control.</strong></em> and basic HTML support/
</b-popover>
</div>
</div>

<h4 class="mt-sm-4 ms-sm-4 text-muted">Delay</h4>
<div class="row">
</div>
</template>

<div class="col-md-4 my-1 text-center">
<b-popover :delay="1000" content="Sorry, I'm a little sleepy." :triggers="['click','hover']">
<b-btn variant="primary">1000ms</b-btn>
</b-popover>
</div>
<!-- popover-1.vue -->
```

<div class="col-md-4 my-1 text-center">
<b-popover :delay="{show: 1000, hide: 0}" content="This will disappear right away!" :triggers="['click','hover']">
<b-btn variant="primary">1000ms on show</b-btn>
</b-popover>
</div>
### Advanced usage with reactive content

<div class="col-md-4 my-1 text-center">
<b-popover :delay="{show: 0, hide: 1000}" content="This will disappear after a second's delay." :triggers="['click','hover']">
<b-btn variant="primary">1000ms on hide</b-btn>
</b-popover>
```html
<template>
<div>
<h4 class="mt-sm-4 ms-sm-4 text-muted">Reactive Content</h4>
<div class="row">
<div class="col-12 my-3 text-center">
<b-btn id="exPopoverReactive1" variant="primary">Using slots</b-btn>
</div>
</div>

<b-popover target-id="exPopoverReactive1" trigger="click" ref="popover">
<template slot="title">
<b-btn @click="onClose" class="close" aria-label="Close">
<span class="d-inline-block" aria-hidden="true">&times;</span>
</b-btn>
Interactive Content
</template>
<b-form-group label="Name" description="Enter your name">
<b-form-input size="sm" v-model="input1"></b-form-input>
</b-form-group>
<b-form-group label="Color" description="Pick a color">
<b-form-select size="sm" v-model="input2" :options="options"></b-form-select>
</b-form-group>
<b-card title="data from above">
<p class="card-text">Name: <strong>{{ input1 }}</strong></p>
<p class="card-text">Color: <strong>{{ input2 }}</strong></p>
</b-card>
<b-btn @click="onCancel" variant="danger">Cancel</b-btn>
<b-btn @click="onOk" variant="primary">Ok</b-btn>
</b-popover>
</div>
</template>

<script>
export default {
data: {
triggerExamples: [
'click',
'focus',
'hover', ['click', 'focus'],
['click', 'hover'],
['focus', 'hover']
],
},
methods: {
clickEventConfirmed() {
alert('Form submitted!');
export default {
data: {
input1: '',
input2: '',
options: [{text:'- Chose 1 -', value:''},'Red','Green','Blue']
},
methods: {
onClose() {
this.$refs.popover.$emit('close');
},
triggersToString(input) {
if (Array.isArray(input)) {
return input.join(' + ');
onCancel() {
this.$refs.popover.$emit('close');
},
onOk() {
if (!this.input1 || !this.input2) {
alert('Please enter something');
} else {
alert('Thats great!');
this.$refs.popover.$emit('close');
}
return input;
}
}
}
}
};
</script>
<!-- popover-2.vue -->
```

**Note:** _The `<b-popover>` component inserts a hidden (`display:none`) `<div>` container
element at the point in the DOM where the `<b-popover>` component is placed. This may
affect layout and/or styling of components such as `<b-button-group>`, `<b-button-toolbar>`,
and `<b-input-group>`. To avoid these posible layout issues, place the `<b-popover>`
component **outside** of theese types of components._

## `v-b-popover` Directive Usage

```html
<template>
<div>

<h4 class="mt-sm-4 ms-sm-4 text-muted">Placement</h4>
<div class="row">
<div class="col-md-3 my-1 text-center">
<b-btn v-b-popover.top="'I am Top'" title="Popover!" variant="primary">Top</b-btn>
</div>
<div class="col-md-3 my-1 text-center">
<b-btn v-b-popover.left="'I am Left'" title="Popover!" variant="primary">Left</b-btn>
</div>
<div class="col-md-3 my-1 text-center">
<b-btn v-b-popover.right="'I am Right'" title="Popover!" variant="primary">Right</b-btn>
</div>
<div class="col-md-3 my-1 text-center">
<b-btn v-b-popover.bottom="'I am Bottom'" title="Popover!" variant="primary">Bottom</b-btn>
</div>
</div>

</div>
</template>

<!-- popover.vue -->
<!-- popover-directive-1.vue -->
```

For more advanced usage, you can use <a href="https://github.com/SirLamer/click-confirm" target="_blank">Click Confirm</a> component by @SirLamer which is based on popover.
Refer to the [`v-b-popover` directive](/docs/directives/popover) documentation for detailed
information on the directive usage.

0 comments on commit 33c4cab

Please sign in to comment.