Skip to content
This repository has been archived by the owner on Mar 17, 2021. It is now read-only.

Commit

Permalink
Added popover component
Browse files Browse the repository at this point in the history
  • Loading branch information
romanslonov committed Apr 4, 2019
1 parent 304e03f commit abc43e6
Show file tree
Hide file tree
Showing 11 changed files with 671 additions and 395 deletions.
2 changes: 2 additions & 0 deletions build/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ const external = [
// eg. 'jquery'
'fuzzysearch',
'vue',
'popper.js',
];
const globals = {
// Provide global variable names to replace your external imports
// eg. jquery: '$'
fuzzysearch: 'fuzzysearch',
vue: 'Vue',
'popper.js': 'Popper',
};

export default [
Expand Down
2 changes: 1 addition & 1 deletion dist/modulist-vue.common.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/modulist-vue.esm.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/modulist-vue.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {
'components/modal',
'components/notification',
'components/pagination',
'components/popover',
'components/progress',
'components/radio',
'components/radio-group',
Expand Down
25 changes: 25 additions & 0 deletions docs/components/popover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Popover <badge text="development" type="warn" />
Popover component used to show content in container, usually used in as part of dropdown.

## Example
<div class="p-3 border rounded-2 my-3">
<v-popover>
<template v-slot:toggle>
<v-button appearance="primary">Open popover</v-button>
</template>
<div>Default popover</div>
</v-popover>
</div>

## Props
Name | Type | Description | Default | Required
------------------ | --------- | ----------- | ------- | --------
trigger | String | Popover can be opened on `click` or 'hover' event | 'click' | false
containFocus | Boolean | Should focus stay inside popover or not | false | false
hasMaxHeight | Boolean | Should popover be limited by height or not | true | false
disabled | Boolean | Prevent opening popover | false | false
placement | String | Placement of popover | 'bottom' | false
offset | [String, Number] | Popover offset between trigger and container | '0' | false
manualOpen | Boolean | Disable built logic to open/close popover. If `true` you have to implement your own logic | false | false
manualClose | Boolean | Disable built logic to open/close popover. If `true` you have to implement your own logic | false | false
returnFocusOnClose | Boolean | Trigger get back focus when popover close | true | false
823 changes: 435 additions & 388 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"prepublish": "bash scripts/prepublish.sh"
},
"devDependencies": {
"@modulist/css": "0.0.29",
"@modulist/css": "0.0.31",
"@vue/cli-plugin-babel": "^3.5.0",
"@vue/cli-plugin-eslint": "^3.5.0",
"@vue/cli-plugin-unit-jest": "^3.5.0",
Expand All @@ -47,8 +47,15 @@
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,vue}": ["vue-cli-service lint", "git add"],
"ignore": ["**/dist/*.js"]
"linters": {
"*.{js,vue}": [
"vue-cli-service lint",
"git add"
]
},
"ignore": [
"**/dist/*.js"
]
},
"directories": {
"doc": "docs",
Expand All @@ -65,6 +72,7 @@
},
"homepage": "https://github.com/simplystack/modulist-vue#readme",
"dependencies": {
"fuzzysearch": "^1.0.3"
"fuzzysearch": "^1.0.3",
"popper.js": "^1.14.7"
}
}
8 changes: 8 additions & 0 deletions src/components/Popover/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Popover from './main.vue';

// eslint-disable-next-line func-names
Popover.install = function (Vue) {
Vue.component('VPopover', Popover);
};

export default Popover;
184 changes: 184 additions & 0 deletions src/components/Popover/main.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<template>
<div class="popover" @keydown.esc="close" :class="{'popover--has-max-height': hasMaxHeight}">
<div class="popover__trigger" ref="trigger">
<slot name="toggle"></slot>
</div>
<div
ref="content"
role="dialog"
class="popover__content"
aria-haspopup="true"
:aria-expanded="isActive ? 'true' : 'false'"
tabindex="-1"
v-if="isActive"
>
<slot></slot>

<div class="popover__focus-redirector" tabindex="0" @focus="restrictFocus"></div>
</div>
</div>
</template>

<script>
import Popper from 'popper.js';
export default {
name: 'VPopover',
props: {
trigger: {
type: String,
default: 'click',
},
containFocus: {
type: Boolean,
default: false,
},
hasMaxHeight: {
type: Boolean,
default: true,
},
disabled: {
type: Boolean,
default: false,
},
placement: {
type: String,
default: 'bottom',
},
offset: {
type: [String, Number],
default: '0',
},
manualOpen: {
type: Boolean,
default: false,
},
manualClose: {
type: Boolean,
default: false,
},
returnFocusOnClose: {
type: Boolean,
default: true,
},
focusRedirector: Function,
},
data() {
return {
isActive: false,
popperInstance: null,
focusedElBeforeOpen: null,
};
},
mounted() {
this.addEventsListeners();
},
computed: {
triggerEl() {
return this.$refs.trigger;
},
popperOptions() {
return {
placement: this.placement,
modifiers: {
offset: {
offset: this.offset,
},
},
};
},
},
methods: {
addEventsListeners() {
switch (this.trigger) {
case 'click':
if (!this.manualClose) document.addEventListener('click', this.handleClickOutside, true);
if (!this.manualOpen) this.triggerEl.addEventListener('click', this.toggle);
break;
case 'hover':
if (!this.manualOpen) this.triggerEl.addEventListener('mouseenter', this.show);
if (!this.manualClose) document.addEventListener('mousemove', this.handleClickOutside, true);
break;
default:
throw new Error(`[popover] ${this.trigger} is not defined`);
}
},
removeEventsListeners() {
switch (this.trigger) {
case 'click':
if (!this.manualClose) document.removeEventListener('click', this.handleClickOutside, true);
this.triggerEl.removeEventListener('click', this.toggle);
break;
case 'hover':
if (!this.manualOpen) this.triggerEl.removeEventListener('mouseenter', this.show);
if (!this.manualClose) document.removeEventListener('mousemove', this.handleClickOutside, true);
break;
default:
throw new Error(`[popover] ${this.trigger} is not defined`);
}
},
toggle() {
if (this.isActive) {
this.close();
} else {
this.show();
}
},
show() {
if (this.disabled) return;
if (this.isActive) return;
this.focusedElBeforeOpen = document.activeElement;
this.isActive = true;
this.initializePopper();
this.$nextTick(() => {
this.$el.focus();
});
this.$emit('open');
},
close() {
this.isActive = false;
this.$nextTick(() => {
this.destroyPopper();
});
this.$emit('close');
if (this.returnFocusOnClose) {
this.triggerEl.focus();
// this.focusedElBeforeOpen.focus();
}
},
restrictFocus(e) {
if (!this.containFocus) {
this.close();
return;
}
e.stopPropagation();
if (this.focusRedirector) {
this.focusRedirector(e);
} else {
this.$el.focus();
}
},
initializePopper() {
this.$nextTick(() => {
this.popperInstance = new Popper(this.triggerEl, this.$refs.content, this.popperOptions);
});
},
destroyPopper() {
if (this.popperInstance) {
this.popperInstance.destroy();
this.popperInstance = null;
}
},
handleClickOutside(e) {
if (!this.$el.contains(e.target) && !this.triggerEl.contains(e.target) && this.isActive) {
this.close();
}
},
},
beforeDestroy() {
this.removeEventsListeners();
},
};
</script>
1 change: 1 addition & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { default as VModal } from './Modal';
export { default as VNotification } from './Notification';
export { default as VNotificationContainer } from './NotificationContainer';
export { default as VPagination } from './Pagination';
export { default as VPopover } from './Popover';
export { default as VProgress } from './Progress';
export { default as VRadio } from './Radio';
export { default as VRadioGroup } from './RadioGroup';
Expand Down

0 comments on commit abc43e6

Please sign in to comment.