2
0
mirror of https://github.com/tenrok/vue-context.git synced 2026-06-07 21:52:24 +03:00

Release/v5 (#43)

This commit is contained in:
Randall Wilk
2019-11-14 13:38:00 -06:00
committed by GitHub
parent 39adea968a
commit a799c3788a
17 changed files with 303 additions and 356 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
node_modules/
.idea
npm-debug.log*
**/mix-manifest.json
**/mix-manifest.json
test/js/dist/
-2
View File
@@ -1,5 +1,3 @@
docs/
src/
test/
build/
.idea
+44 -26
View File
@@ -2,12 +2,30 @@
All notable changes to this project will be documented here.
<a name="5.0.0"></a>
## [5.0.0](https://github.com/rawilk/vue-context/releases/tag/5.0.0)
Released 2019-11-14
### Added
- Add default export in entry point ([#41](https://github.com/rawilk/vue-context/issues/41)).
- Add support for nested context menus ([#37](https://github.com/rawilk/vue-context/pull/37)).
### Changes
- Update entry point to source ([#33](https://github.com/rawilk/vue-context/issues/33)).
- No longer build component with webpack ([#33](https://github.com/rawilk/vue-context/issues/33)).
- Component source is available through npm now, including the sass files.
### Release notes
- Even though there shouldn't be any breaking changes in this release, it's a major release because
breaking changes were introduced in version 4.1.0, which should have been a major release instead.
<a name="4.1.1"></a>
## [4.1.1](https://github.com/rawilk/vue-context/releases/tag/4.1.1)
Released 2019-10-20
### Bug Fixes 4.1.1
### Bug Fixes
- Fix bug of "Unknown custom element" ([#40](https://github.com/rawilk/vue-context/issues/40)).
<a name="4.1.0"></a>
@@ -15,10 +33,10 @@ Released 2019-10-20
Released 2019-10-11
### Breaking Changes 4.1.0
### Breaking Changes
- Move menu styles from js to its own stylesheet ([#36](https://github.com/rawilk/vue-context/issues/36))
### Changes 4.1.0
### Changes
- Remove core-js as a dependency in favor of local polyfills for a smaller build size ([#33](https://github.com/rawilk/vue-context/issues/33))
<a name="4.0.3"></a>
@@ -26,10 +44,10 @@ Released 2019-10-11
Released 2019-07-28
### Bug Fixes 4.0.3
### Bug Fixes
- Make `localItemSelector` reactive to changes from `itemSelector` prop ([#30](https://github.com/rawilk/vue-context/issues/30)).
### Updates 4.0.3
### Updates
- Update dev dependencies
<a name="4.0.2"></a>
@@ -37,7 +55,7 @@ Released 2019-07-28
Released 2019-06-22
### Bug Fixes 4.0.2
### Bug Fixes
- Fix menu item width issues ([#26](https://github.com/rawilk/vue-context/issues/26)).
- Remove outline from menu and menu items when given focus ([#26](https://github.com/rawilk/vue-context/issues/26)).
@@ -46,7 +64,7 @@ Released 2019-06-22
Released 2019-06-03
### Bug Fixes 4.0.1
### Bug Fixes
- Always emit the close event on click. See issue [#23](https://github.com/rawilk/vue-context/issues/23)
<a name="4.0.0"></a>
@@ -54,17 +72,17 @@ Released 2019-06-03
Released 2019-05-18
### Added 4.0.0
### Added
- Added support for keyboard navigation (up and down arrows).
- Added ability to close menu on esc.
- Added `lazy` prop as an alternative to `v-show`.
- Added `tag` prop to specify menu tag (defaults to `<ul>`).
### Changes 4.0.0
### Changes
- Default menu tag is now `<ul>` and menu is now the top-level element.
- Changed how the menu is styled.
### Updates 4.0.0
### Updates
- Updated build process and project structure.
- Ran `npm audit fix` to fix vulnerabilities found from dependencies.
@@ -73,7 +91,7 @@ Released 2019-05-18
Released 2019-04-05
### Bug Fixes 3.4.2
### Bug Fixes
- Only add scroll event listener on `closeOnScroll` prop value change if the menu is open.
<a name="3.4.1"></a>
@@ -81,7 +99,7 @@ Released 2019-04-05
Released 2019-04-03
### Updates 3.4.1
### Updates
- **Scroll Listener:** Only attach the close scroll event listener when opened and immediately remove it when menu is closed
to prevent it being called unnecessarily.
- **Dependencies:** Removed Vue as a dependency as it never really was one since v3.0.0.
@@ -92,7 +110,7 @@ to prevent it being called unnecessarily.
Released 2018-11-19
### Bug fixes 3.4.0
### Bug fixes
- **Close event:** only emit the event if menu is actually open (fixes [#13](https://github.com/rawilk/vue-context/issues/13))
<a name="3.3.1"></a>
@@ -100,10 +118,10 @@ Released 2018-11-19
Released 2018-10-23
### Changes 3.3.1
### Changes
- Context menu now closes via [clickaway](https://github.com/simplesmiler/vue-clickaway) instead of a blur event. Credit: [robjbrain](https://github.com/robjbrain)
### Updates 3.3.1
### Updates
- Updated vue and other dev dependencies.
<a name="3.3.0"></a>
@@ -111,7 +129,7 @@ Released 2018-10-23
Released 2018-10-15
### Features 3.3.0
### Features
- **Events:** both open and close events are now emitted by the component. ([#10](https://github.com/rawilk/vue-context/issues/10))
<a name="3.2.0"></a>
@@ -119,7 +137,7 @@ Released 2018-10-15
Released 2018-09-12
### Features 3.2.0
### Features
- **Close on click prop:** added a prop to prevent closing the context menu on click. ([#8](https://github.com/rawilk/vue-context/issues/8))
<a name="3.1.1"></a>
@@ -127,7 +145,7 @@ Released 2018-09-12
Released 2018-06-23
### Updates 3.1.1
### Updates
- Updated README.md
- Added code comments
@@ -136,7 +154,7 @@ Released 2018-06-23
Released 2018-05-29
### Features 3.1.0
### Features
- **Scroll prop:** added a prop to close the context menu automatically on window scroll. ([#2](https://github.com/rawilk/vue-context/issues/2))
<a name="3.0.2"></a>
@@ -144,10 +162,10 @@ Released 2018-05-29
Released 2018-05-29
### Updates 3.0.2
### Updates
- Updated documentation
### Added 3.0.2
### Added
- Added [demos](https://vue-context.randallwilk.com) for the component.
<a name="3.0.0"></a>
@@ -155,15 +173,15 @@ Released 2018-05-29
Released 2018-05-26
### Breaking changes 3.0.0
### Breaking changes
- Changed slot scope definition from `userData` to just `data`.
- Component gets imported as `{ VueContext }` instead of `VContext` now.
### Updates 3.0.0
### Updates
- Updated dependencies
- Updated documentation
### Changes 3.0.0
### Changes
- Changed code structure and build process.
<a name="2.0.1"></a>
@@ -171,11 +189,11 @@ Released 2018-05-26
Released 2017-08-18
### Added 2.0.1
### Added
- License file
- Changelog file
### Removed 2.0.1
### Removed
- Removed bottom border from context menu line items.
<a name="2.0.0"></a>
+10
View File
@@ -75,12 +75,22 @@ Next add an element to the page that will trigger the context menu to appear, an
```bash
@import '~vue-context/dist/css/vue-context.css';
// Or
@import '~vue-context/src/sass/vue-context';
```
## Documentation/Demo
For full documentation and demos, go here: https://vue-context.com/docs
## Contributors
This project exists thanks to all the people who contribute. [[Contribute]](CONTRIBUTING.md).
- [rawilk](https://github.com/rawilk)
- [wol-soft](https://github.com/wol-soft)
## License
`vue-context` uses the MIT License (MIT). Please see the [license file](https://github.com/rawilk/vue-context/blob/master/LICENSE) for more information.
-5
View File
@@ -1,5 +0,0 @@
const mix = require('laravel-mix');
mix
.setPublicPath('dist/css')
.sass('src/sass/vue-context.scss', 'vue-context.css');
+2 -11
View File
@@ -1,14 +1,5 @@
const mix = require('laravel-mix');
const inProduction = mix.inProduction();
mix
.setPublicPath('dist/js')
.js('src/js/index.js', 'vue-context.js')
.sourceMaps(! inProduction)
.webpackConfig({
output: {
libraryTarget: 'umd',
umdNamedDefine: true
}
});
.setPublicPath('dist/css')
.sass('src/sass/vue-context.scss', 'vue-context.css');
+1 -1
View File
@@ -1 +1 @@
.v-context{background-color:#fff;background-clip:padding-box;border-radius:.25rem;border:1px solid rgba(0,0,0,.15);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);display:block;margin:0;padding:10px 0;min-width:10rem;z-index:1500;position:fixed;list-style:none;box-sizing:border-box}.v-context>li{margin:0}.v-context>li>a{display:block;padding:.5rem 1.5rem;font-weight:400;color:#212529;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.v-context>li>a:focus,.v-context>li>a:hover{text-decoration:none;color:#212529;background-color:#f8f9fa}.v-context:focus,.v-context>li>a:focus{outline:0}
.v-context,.v-context ul{background-color:#fff;background-clip:padding-box;border-radius:.25rem;border:1px solid rgba(0,0,0,.15);box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);display:block;margin:0;padding:10px 0;min-width:10rem;z-index:1500;position:fixed;list-style:none;box-sizing:border-box;max-height:calc(100% - 50px);overflow-y:auto}.v-context>li,.v-context ul>li{margin:0;position:relative}.v-context>li>a,.v-context ul>li>a{display:block;padding:.5rem 1.5rem;font-weight:400;color:#212529;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.v-context>li>a:focus,.v-context>li>a:hover,.v-context ul>li>a:focus,.v-context ul>li>a:hover{text-decoration:none;color:#212529;background-color:#f8f9fa}.v-context:focus,.v-context>li>a:focus,.v-context ul:focus,.v-context ul>li>a:focus{outline:0}.v-context__sub>a:after{content:"\2BC8";float:right;padding-left:1rem}.v-context__sub>ul{display:none}
-1
View File
File diff suppressed because one or more lines are too long
+2 -3
View File
@@ -2,13 +2,12 @@
"name": "vue-context",
"version": "4.1.1",
"description": "A simple vue context menu component.",
"main": "dist/js/vue-context.js",
"main": "src/js/index.js",
"scripts": {
"dev": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --env.mixfile=build/webpack.mix.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"dev-test": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --env.mixfile=build/webpack-test.mix.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-test": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --env.mixfile=build/webpack-test.mix.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --env.mixfile=build/webpack.mix.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"styles": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --env.mixfile=build/webpack-styles.mix.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --env.mixfile=build/webpack.mix.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"keywords": [
"Vue",
+1
View File
@@ -1 +1,2 @@
export { default } from './vue-context';
export { default as VueContext } from './vue-context';
+13 -1
View File
@@ -17,7 +17,9 @@ export const isArray = Array.isArray;
export const keyCodes = {
ESC: 27,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40
};
@@ -51,7 +53,7 @@ export const filterVisible = elements => (elements || []).filter(isVisible);
// Return the Bounding Client Rect of an element
// Returns `null` if not an element
const getBCR = el => (isElement(el) ? el.getBoundingClientRect() : null);
export const getBCR = el => (isElement(el) ? el.getBoundingClientRect() : null);
// Determine if an element is an HTML element
const isElement = el => Boolean(el && el.nodeType === Node.ELEMENT_NODE);
@@ -81,3 +83,13 @@ export const setAttr = (el, attr, value) => {
el.setAttribute(attr, value);
}
};
export const parentElementByClassName = (element, className) => {
let parentElement = element.parentElement;
while (parentElement !== null && !parentElement.classList.contains(className)) {
parentElement = parentElement.parentElement;
}
return parentElement;
};
+119 -9
View File
@@ -1,5 +1,15 @@
import { directive as onClickaway } from 'vue-clickaway/index';
import { eventOff, eventOn, filterVisible, isArray, keyCodes, selectAll, setAttr } from './utils';
import {
eventOff,
eventOn,
filterVisible,
isArray,
keyCodes,
selectAll,
setAttr,
getBCR,
parentElementByClassName
} from './utils';
import { normalizeSlot } from './normalize-slot';
export default {
@@ -48,7 +58,8 @@ export default {
left: null,
show: false,
data: null,
localItemSelector: ''
localItemSelector: '',
activeSubMenu: null
};
},
@@ -67,12 +78,27 @@ export default {
eventOn(window, 'scroll', this.close);
},
addHoverEventListener(element) {
element.querySelectorAll('.v-context__sub').forEach(
subMenuNode => {
eventOn(subMenuNode, 'mouseenter', this.openSubMenu);
eventOn(subMenuNode, 'mouseleave', this.closeSubMenu);
}
);
},
close() {
if (! this.show) {
return;
}
// make sure all sub menus are closed
while (this.activeSubMenu !== null) {
parentElementByClassName(this.activeSubMenu, 'v-context__sub').dispatchEvent(new Event('mouseleave'));
}
this.resetData();
this.removeHoverEventListener(this.$el);
if (this.closeOnScroll) {
this.removeScrollEventListener();
@@ -118,7 +144,8 @@ export default {
},
getItems() {
return filterVisible(selectAll(this.localItemSelector, this.$el));
// if a sub menu is active only return the elements of the sub menu to keep the scope
return filterVisible(selectAll(this.localItemSelector, this.activeSubMenu || this.$el));
},
mapItemSelector(itemSelector) {
@@ -147,6 +174,27 @@ export default {
} else if (key === keyCodes.UP) {
// Up arrow
this.focusNext(event, true);
} else if (key === keyCodes.RIGHT) {
// check if a parent element which is associated with a sub menu can be found.
const menuContainer = parentElementByClassName(event.target, 'v-context__sub');
// try to open a sub menu if the sub menu isn't the current sub menu
if (menuContainer && menuContainer.getElementsByClassName('v-context')[0] !== this.activeSubMenu) {
menuContainer.dispatchEvent(new Event('mouseenter'));
this.focusNext(event, false);
}
} else if (key === keyCodes.LEFT) {
if (!this.activeSubMenu) {
return;
}
const parentMenu = parentElementByClassName(this.activeSubMenu, 'v-context__sub');
parentMenu.dispatchEvent(new Event('mouseleave'));
const items = this.getItems(),
index = items.indexOf(parentMenu.getElementsByTagName('a')[0]);
this.focusItem(index, items);
}
},
@@ -155,9 +203,11 @@ export default {
this.show = true;
this.$nextTick(() => {
this.positionMenu(event.clientY, event.clientX);
[this.top, this.left] = this.positionMenu(event.clientY, event.clientX, this.$el);
this.$el.focus();
this.setItemRoles();
this.addHoverEventListener(this.$el);
if (this.closeOnScroll) {
this.addScrollEventListener();
@@ -167,9 +217,61 @@ export default {
});
},
positionMenu(top, left) {
const largestHeight = window.innerHeight - this.$el.offsetHeight - 25;
const largestWidth = window.innerWidth - this.$el.offsetWidth - 25;
openSubMenu(event) {
const subMenuElement = this.getSubMenuElementByEvent(event),
parentMenu = parentElementByClassName(subMenuElement.parentElement, 'v-context'),
bcr = getBCR(event.target);
// check if another sub menu is open. In this case make sure no other as well as no nested sub menu is open
if (this.activeSubMenu !== parentMenu) {
while (this.activeSubMenu !== null
&& this.activeSubMenu !== parentMenu
&& this.activeSubMenu !== subMenuElement
) {
parentElementByClassName(this.activeSubMenu, 'v-context__sub')
.dispatchEvent(new Event('mouseleave'));
}
}
// first set the display and afterwards execute position calculation for correct element offsets
subMenuElement.style.display = 'block';
let [elementTop, elementLeft] = this.positionMenu(bcr.top, bcr.right - 10, subMenuElement);
subMenuElement.style.left = `${elementLeft}px`;
subMenuElement.style.top = `${elementTop}px`;
this.activeSubMenu = subMenuElement;
},
closeSubMenu(event) {
const subMenuElement = this.getSubMenuElementByEvent(event),
parentMenu = parentElementByClassName(subMenuElement, 'v-context');
// if a sub menu is closed and it's not the currently active sub menu (eg. a lowe layered sub menu closed
// by a mouseleave event) close all nested sub menus
if (this.activeSubMenu !== subMenuElement) {
while (this.activeSubMenu !== null && this.activeSubMenu !== subMenuElement) {
parentElementByClassName(this.activeSubMenu, 'v-context__sub')
.dispatchEvent(new Event('mouseleave'));
}
}
subMenuElement.style.display = 'none';
// check if a parent menu exists and the parent menu is a sub menu to keep track of the correct sub menu
this.activeSubMenu = parentMenu && parentElementByClassName(parentMenu, 'v-context__sub')
? parentMenu
: null;
},
getSubMenuElementByEvent (event) {
return event.target.getElementsByTagName('ul')[0];
},
positionMenu(top, left, element) {
const largestHeight = window.innerHeight - element.offsetHeight - 25;
const largestWidth = window.innerWidth - element.offsetWidth - 25;
if (top > largestHeight) {
top = largestHeight;
@@ -179,14 +281,22 @@ export default {
left = largestWidth;
}
this.top = top;
this.left = left;
return [top, left];
},
removeScrollEventListener() {
eventOff(window, 'scroll', this.close);
},
removeHoverEventListener(element) {
element.querySelectorAll('.v-context__sub').forEach(
(subMenuNode) => {
eventOff(subMenuNode, 'mouseenter', this.openSubMenu);
eventOff(subMenuNode, 'mouseleave', this.closeSubMenu);
}
);
},
resetData() {
this.top = null;
this.left = null;
+50 -33
View File
@@ -1,47 +1,64 @@
@import "config";
.v-context {
background-color: $menu-bg;
background-clip: padding-box;
border-radius: .25rem;
border: 1px solid $menu-border;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
display: block;
margin: 0;
padding: 10px 0;
min-width: 10rem;
z-index: 1500;
position: fixed;
list-style: none;
box-sizing: border-box;
> li {
&, & ul {
background-color: $menu-bg;
background-clip: padding-box;
border-radius: .25rem;
border: 1px solid $menu-border;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
display: block;
margin: 0;
padding: 10px 0;
min-width: 10rem;
z-index: 1500;
position: fixed;
list-style: none;
box-sizing: border-box;
max-height: calc(100% - 50px);
overflow-y: auto;
> a {
display: block;
padding: .5rem 1.5rem;
font-weight: 400;
color: $item-color;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border: 0;
> li {
margin: 0;
position: relative;
&:hover,
&:focus {
> a {
display: block;
padding: .5rem 1.5rem;
font-weight: 400;
color: $item-color;
text-decoration: none;
color: $item-hover-color;
background-color: $item-hover-bg;
}
white-space: nowrap;
background-color: transparent;
border: 0;
&:focus {
outline: 0;
&:hover,
&:focus {
text-decoration: none;
color: $item-hover-color;
background-color: $item-hover-bg;
}
&:focus {
outline: 0;
}
}
}
&:focus {
outline: 0;
}
}
&:focus {
outline: 0;
&__sub {
> a:after {
content: "\2bc8";
float: right;
padding-left: 1rem;
}
> ul {
display: none;
}
}
}
+58 -1
View File
@@ -27,12 +27,69 @@
Do something
</a>
</li>
<li>
<li class="v-context__sub">
<a href="#" class="v-context-item"
@click.prevent="onClick('item 2')"
>
Do something else
</a>
<ul class="v-context">
<li tabindex="0">
<a href="#" class="v-context-item"
@click.prevent="onClick('sub item 1')"
>
Submenu
</a>
</li>
<li class="v-context__sub">
<a href="#" class="v-context-item"
@click.prevent="onClick('sub item 2')"
>
Submenu next
</a>
<ul class="v-context">
<li tabindex="0">
<a href="#" class="v-context-item"
@click.prevent="onClick('sub sub item 1')"
>
We need to go deeper
</a>
</li>
<li tabindex="0">
<a href="#" class="v-context-item"
@click.prevent="onClick('sub sub item 2')"
>
double nested Submenu
</a>
</li>
</ul>
</li>
<li>
<a href="#" class="v-context-item"
@click.prevent="onClick('sub item 3')"
>
Submenu next
</a>
</li>
<li class="v-context__sub">
<a href="#" class="v-context-item"
@click.prevent="onClick('sub item 4')"
>
second nested Submenu
</a>
<ul class="v-context">
<li tabindex="0">
<a href="#" class="v-context-item"
@click.prevent="onClick('sub sub item 3')"
>
sub sub
</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#" class="v-context-item"
-46
View File
@@ -1,46 +0,0 @@
.v-context {
background-color: #fff;
background-clip: padding-box;
border-radius: 0.25rem;
border: 1px solid rgba(0, 0, 0, 0.15);
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
display: block;
margin: 0;
padding: 10px 0;
min-width: 10rem;
z-index: 1500;
position: fixed;
list-style: none;
box-sizing: border-box;
}
.v-context > li {
margin: 0;
}
.v-context > li > a {
display: block;
padding: 0.5rem 1.5rem;
font-weight: 400;
color: #212529;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border: 0;
}
.v-context > li > a:hover,
.v-context > li > a:focus {
text-decoration: none;
color: #212529;
background-color: #f8f9fa;
}
.v-context > li > a:focus {
outline: 0;
}
.v-context:focus {
outline: 0;
}
-214
View File
File diff suppressed because one or more lines are too long
+1 -2
View File
@@ -1,6 +1,5 @@
import Vue from 'vue';
// import { VueContext } from '../../../src/js/index';
import { VueContext } from '../../../dist/js/vue-context';
import VueContext from '../../../src/js/index';
new Vue({
components: {