-
Notifications
You must be signed in to change notification settings - Fork 133
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
Support sub-menu for dropdown #1455
Conversation
@ang-zeyu this is ready for review and do let me know on how this can be improved. If the implementation is fine, will proceed to add in tests and documentation as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a review on the overall approach / issues:
- I was thinking of using the https://v3.vuejs.org/guide/component-provide-inject.html#working-with-reactivity.
e.g.
you could do
<dropdown>
<div class="some-wrapper">
<dropdown></dropdown> // would still work when nested
</div>
</dropdown>
the approach here is more consistent with vue-strap's, but as you might have observed its a rather low level approach that ties itself heavily to the dom. (note vue-strap didn't have this api at the time)
But I would be ok with accepting this as quite a bit of work has been done already. If you're up for it, let's change this over to provide/inject (could even make a separate PR to shift the original implementation to it first if it makes things easier)
-
Positioning of submenus needs to be dynamic (we can assume right position first then flip it over to the left otherwise). (note the submenu may overflow over the screen currently) You could explore the popperjs library to do this (bootstrap-vue uses this internally for its popovers,dropdowns...)
-
Really small nit: Make the submenu show on-hover as well? 🤔
One more note since you're tackling #980 as well: you may want to finish shifting the mobile navbar design to it first, as dropdowns contained within navbars look / behave quite differently - less work to do here |
@ang-zeyu I've made some changes accordingly.
I didn't use provide/inject in the end as when I was trying to implement this, I managed to remove all the unnecessary communication between the
I tried out popperjs but the auto positioning didn't work very well/a little buggy. I switched to using a modified bootstrap javascript which similarly solves this issue. Hopefully that's fine ;) I've also added tests and updated the docs. |
716e2f9
to
a26f606
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't use provide/inject in the end as when I was trying to implement this, I managed to remove all the unnecessary communication between the
dropdown
andsubmenu
components.
Not so much for removing unnecessary communication but just allowing parent / child communication across different compilation scopes.
Nice work isolating the implementation as much as possible - only have one not-so-beneficial suggestion where this can be used now. (see comment below)
You could take a look at the following for examples how it could be used more beneficially:
- bv components
- our own
<quiz>
/<question>
/<q-option>
components
Not sure where else to use it as the rest of the search of the virtual DOM seems necessary, for example listening for clicks on
li>a
as these are user defined.
yes this is still necessary unfortunately, due to our use of standard dom elements.
I tried out popperjs but the auto positioning didn't work very well/a little buggy. I switched to using a modified bootstrap javascript which similarly solves this issue. Hopefully that's fine ;)
Seems ok too 👍
Some strange behaviours:
(main / submenu closing on hover)
Some suggestions:
@@ -13,6 +13,13 @@ | |||
</ul> | |||
</slot> | |||
</li> | |||
<submenu v-else-if="isSubmenu" ref="submenu"> | |||
<slot slot="button" name=button></slot> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could do
<template v-for="(node, name) in $slots" :slot="name">
<slot :name="name"></slot>
</template>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Updated :)
@@ -72,6 +79,7 @@ export default { | |||
return toBoolean(this.disabled); | |||
}, | |||
isLi () { return this.$parent._navbar || this.$parent.menu || this.$parent._tabset }, | |||
isSubmenu() { return this.$parent._dropdown || this.$parent._submenu }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simple example where provide / inject could be used to remove the $parent
dependency, but not so beneficial:
provide: { hasParentDropdown: true }
inject: ['hasParentDropdown']
isSubmenu() { return this.hasParentDropdown}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clarification. Have added this in :)
type: Boolean, | ||
default: false, | ||
}, | ||
'class': null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Updated :)
}, | ||
computed: { | ||
classes() { | ||
return [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's place this directly in the template only for consistency with other components'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright. Updated :)
docs/userGuide/syntax/dropdowns.mbdf
Outdated
<include src="codeAndOutput.md" boilerplate > | ||
<variable name="highlightStyle">html</variable> | ||
<variable name="code"> | ||
<!-- Can simply nest the dropdown syntax --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<!-- Can simply nest the dropdown syntax --> | |
<!-- Nest the dropdown syntax to create dropdown submenus --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated :)
}, | ||
addClassIfSubmenu(a, li) { | ||
let el = a.parentElement; | ||
while (el != li) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!==
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Updated :)
}, | ||
methods: { | ||
hideSubmenu() { | ||
this.show = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's use data()
for properties that could change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated :)
} | ||
|
||
@media (max-width: 767px) { | ||
.dropdown-submenu > ul { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm can this be moved to the same media query below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Updated :)
@ang-zeyu Thanks for the suggestions and clarification 😄. I've updated it accordingly.
For the main menu, it is closing because when the inner children of the submenu toggle is clicked/hovered, it triggers a As for the submenu closing on hover, it is because when the user hovers over the submenu toggle, it will mirror a click. That is the reason why the second hover will close the submenu. One workaround is to only allow submenus to be opened by hovering over and not by clicking. This is the current implementation for now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple more nits:
@@ -112,7 +120,7 @@ export default { | |||
showDropdownMenu() { | |||
this.show = true; | |||
$(this.$refs.dropdown).findChildren('ul').each(ul => ul.classList.toggle('show', true)); | |||
}, | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's correct these in another PR; we could enable linting for dropdown.vue
as well. (the unlinted ones are files that haven't changed much from vue-strap)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright sure will open another PR for this 👍
disabledBool() { | ||
return toBoolean(this.disabled); | ||
}, | ||
showBool() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this (could replace with just show
) and slots()
dosen't seem used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Replaced all showBool
with show
</script> | ||
|
||
<style scoped> | ||
.dropdown-submenu { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's indent these to fit the stylelint rules too (they've never been validated in our scripts for .vue
files but should be)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated :)
@ang-zeyu PR is updated accordingly :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lgtm 👍
What is the purpose of this pull request?
Overview of changes:
Resolves #212.
Sub-menus can be added by nesting dropdowns so that users do not have to remember a new syntax. A new component
<submenu>
is created to handle the logic.<dropdown>
component is edited such that if a<dropdown>
is nested, it will be converted to a<submenu>
. Sub-menus can also contain sub-menus using the same syntax for multiple levels. Currently, sub-menus will appear to the right of a dropdown on desktop version and becomes an accordion on mobile.Anything you'd like to highlight / discuss:
Implementation.
Testing instructions:
Sub-menus can be added by nesting
<dropdown>
.For example:
A dropdown with 2 levels of submenu will be created.
Proposed commit message: (wrap lines at 72 characters)
Add sub-menu support for dropdown
Checklist: ☑️