Skip to content

Commit

Permalink
chore(lib): update Navbar
Browse files Browse the repository at this point in the history
- `Navbar` migrates to Vue 3. The custom directive `clickOutside` is
  also updated. Changes sufficient to render the documentation page of
  `Navbar` are made.
- In `src/components/navbar/Navbar.vue`,
    - `render` takes no arguments because Vue 3 no longer supplies a
      create element function. The global `h` function is used as the
      `createElement` function instead. VNode generation functions
      that takes `createElement` as the first argument no longer take
      it.
    - The `directives` argument given to `createElement` is replaced
      with `withDirectives`.
    - `$scopedSlots` --> `$slots`.
    - `beforeDestroy` --> `beforeUnmount`.
    - `v-model` binding is replaced so that the default binding of Vue
      3 can be used.
        - `active` prop --> `modelValue`
        - `update:active` event --> `update:modelValue`
    - `update:modelValue` is listed in `emits`.
- In `src/components/navbar/NavbarBurger.vue`,
    - `v-on="$listeners"` --> `v-bind="$attrs"`. Because `$listeners`
      is integrated into `$attrs` on Vue 3.
- In `src/components/navbar/NavbarItem.vue`,
    - `v-on="$listeners"` is removed because `$listeners` is
      integrated into `$attrs` on Vue 3.
    - `beforeDestroy` --> `beforeUnmount`.
- In `src/components/navbar/NavbarDropdown.vue`,
    - Removes `v-on="$listeners"` because `$listeners` is integrated
      into `$attrs` on Vue 3.
    - Lists "active-change" in `emits`.
- In `src/directives/clickOutside.js`,
    - The lifecycle hooks conform to Vue 3.
        - `bind` --> `beforeMount`
        - `update` --> `updated`
        - `unbind` --> `unmounted`
- Common,
    - Automatic ESLint fix is applied.
  • Loading branch information
kikuomax committed Jul 7, 2023
1 parent 294d545 commit 5bc62ff
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 74 deletions.
132 changes: 73 additions & 59 deletions src/components/navbar/Navbar.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
<script>
import {
h as createElement,
resolveComponent,
resolveDirective,
withDirectives
} from 'vue'
import NavbarBurger from './NavbarBurger.vue'
import clickOutside from '../../directives/clickOutside'
Expand All @@ -20,11 +27,6 @@ export default {
directives: {
clickOutside
},
// deprecated, to replace with default 'value' in the next breaking change
model: {
prop: 'active',
event: 'update:active'
},
props: {
type: [String, Object],
transparent: {
Expand All @@ -39,7 +41,7 @@ export default {
type: Boolean,
default: false
},
active: {
modelValue: {
type: Boolean,
default: false
},
Expand All @@ -61,9 +63,10 @@ export default {
spaced: Boolean,
shadow: Boolean
},
emits: ['update:modelValue'],
data() {
return {
internalIsActive: this.active,
internalIsActive: this.modelValue,
_isNavBar: true // Used internally by NavbarItem
}
},
Expand All @@ -86,7 +89,7 @@ export default {
}
},
watch: {
active: {
modelValue: {
handler(active) {
this.internalIsActive = active
},
Expand All @@ -113,7 +116,7 @@ export default {
}
},
emitUpdateParentEvent() {
this.$emit('update:active', this.internalIsActive)
this.$emit('update:modelValue', this.internalIsActive)
},
setBodyClass(className) {
if (typeof window !== 'undefined') {
Expand All @@ -131,78 +134,87 @@ export default {
throw new Error('You should choose if the BNavbar is fixed bottom or fixed top, but not both')
}
},
genNavbar(createElement) {
let navBarSlots = [
this.genNavbarBrandNode(createElement),
this.genNavbarSlotsNode(createElement)
genNavbar() {
const navBarSlots = [
this.genNavbarBrandNode(),
this.genNavbarSlotsNode()
]
if (!isFilled(this.wrapperClass)) {
return this.genNavbarSlots(createElement, navBarSlots)
return this.genNavbarSlots(navBarSlots)
}
// It wraps the slots into a div with the provided wrapperClass prop
const navWrapper = createElement('div', {
class: this.wrapperClass
}, navBarSlots)
const navWrapper = createElement(
'div',
{ class: this.wrapperClass },
navBarSlots
)
return this.genNavbarSlots(createElement, [navWrapper])
return this.genNavbarSlots([navWrapper])
},
genNavbarSlots(createElement, slots) {
return createElement('nav', {
staticClass: 'navbar',
class: this.computedClasses,
attrs: {
genNavbarSlots(slots) {
const vnode = createElement(
'nav',
{
class: ['navbar', this.computedClasses],
role: 'navigation',
'aria-label': 'main navigation'
},
directives: [
{
name: 'click-outside',
value: this.closeMenu
}
]
}, slots)
slots
)
return withDirectives(vnode, [
[resolveDirective('click-outside'), this.closeMenu]
])
},
genNavbarBrandNode(createElement) {
return createElement('div', {
class: 'navbar-brand'
}, [this.$slots.brand, this.genBurgerNode(createElement)])
genNavbarBrandNode() {
return createElement(
'div',
{ class: 'navbar-brand' },
[this.$slots.brand(), this.genBurgerNode()]
)
},
genBurgerNode(createElement) {
genBurgerNode() {
if (this.mobileBurger) {
const defaultBurgerNode = createElement('navbar-burger', {
props: {
isOpened: this.isOpened
},
on: {
click: this.toggleActive,
keyup: (event) => {
const defaultBurgerNode = createElement(
resolveComponent('navbar-burger'),
{
isOpened: this.isOpened,
onClick: this.toggleActive,
onKeyup: (event) => {
if (event.keyCode !== 13) return
this.toggleActive()
}
}
})
)
const hasBurgerSlot = !!this.$scopedSlots.burger
const hasBurgerSlot = !!this.$slots.burger
return hasBurgerSlot
? this.$scopedSlots.burger({
? this.$slots.burger({
isOpened: this.isOpened,
toggleActive: this.toggleActive
})
: defaultBurgerNode
}
},
genNavbarSlotsNode(createElement) {
return createElement('div', {
staticClass: 'navbar-menu',
class: { 'is-active': this.isOpened }
}, [this.genMenuPosition(createElement, 'start'), this.genMenuPosition(createElement, 'end')])
genNavbarSlotsNode() {
return createElement(
'div',
{ class: ['navbar-menu', { 'is-active': this.isOpened }] },
[
this.genMenuPosition('start'),
this.genMenuPosition('end')
]
)
},
genMenuPosition(createElement, positionName) {
return createElement('div', {
staticClass: `navbar-${positionName}`
}, this.$slots[positionName])
genMenuPosition(positionName) {
return createElement(
'div',
{ class: `navbar-${positionName}` },
this.$slots[positionName] != null
? this.$slots[positionName]()
: []
)
},
setBodyFixedTopClass(isSet) {
this.checkIfFixedPropertiesAreColliding()
Expand Down Expand Up @@ -233,19 +245,21 @@ export default {
this.fixedTop && this.setBodyFixedTopClass(true)
this.fixedBottom && this.setBodyFixedBottomClass(true)
},
beforeDestroy() {
beforeUnmount() {
if (this.fixedTop) {
const className = this.spaced
? BODY_SPACED_FIXED_TOP_CLASS : BODY_FIXED_TOP_CLASS
? BODY_SPACED_FIXED_TOP_CLASS
: BODY_FIXED_TOP_CLASS
this.removeBodyClass(className)
} else if (this.fixedBottom) {
const className = this.spaced
? BODY_SPACED_FIXED_BOTTOM_CLASS : BODY_FIXED_BOTTOM_CLASS
? BODY_SPACED_FIXED_BOTTOM_CLASS
: BODY_FIXED_BOTTOM_CLASS
this.removeBodyClass(className)
}
},
render(createElement, fn) {
return this.genNavbar(createElement)
render() {
return this.genNavbar()
}
}
</script>
8 changes: 4 additions & 4 deletions src/components/navbar/NavbarBurger.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
:class="{ 'is-active': isOpened }"
aria-label="menu"
:aria-expanded="isOpened"
v-on="$listeners"
v-bind="$attrs"
tabindex="0"
>
<span aria-hidden="true"/>
<span aria-hidden="true"/>
<span aria-hidden="true"/>
<span aria-hidden="true" />
<span aria-hidden="true" />
<span aria-hidden="true" />
</a>
</template>

Expand Down
6 changes: 4 additions & 2 deletions src/components/navbar/NavbarDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
'is-active': newActive && collapsible
}"
v-bind="$attrs"
v-on="$listeners"
aria-haspopup="true"
@click.prevent="toggleMenu"
@keyup.enter="toggleMenu"
tabindex="0"
>
<template v-if="label">{{ label }}</template>
<template v-if="label">
{{ label }}
</template>
<slot v-else name="label" />
</component>
<div
Expand Down Expand Up @@ -73,6 +74,7 @@ export default {
default: 'a'
}
},
emits: ['active-change'],
data() {
return {
newActive: this.active,
Expand Down
6 changes: 3 additions & 3 deletions src/components/navbar/NavbarItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
'is-active': active
}"
v-bind="$attrs"
v-on="$listeners">
<slot/>
>
<slot />
</component>
</template>

Expand Down Expand Up @@ -64,7 +64,7 @@ export default {
document.addEventListener('keyup', this.keyPress)
}
},
beforeDestroy() {
beforeUnmount() {
if (typeof window !== 'undefined') {
this.$el.removeEventListener('click', this.handleClickEvent)
document.removeEventListener('keyup', this.keyPress)
Expand Down
12 changes: 6 additions & 6 deletions src/directives/clickOutside.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function toggleEventListeners({ eventHandlers } = {}, action = 'add') {
})
}

function bind(el, { value }) {
function beforeMount(el, { value }) {
const { handler, middleware, events } = processArgs(value)

const instance = {
Expand All @@ -49,7 +49,7 @@ function bind(el, { value }) {
instances.push(instance)
}

function update(el, { value }) {
function updated(el, { value }) {
const { handler, middleware, events } = processArgs(value)
// `filter` instead of `find` for compat with IE
const instance = instances.filter((instance) => instance.el === el)[0]
Expand All @@ -64,17 +64,17 @@ function update(el, { value }) {
toggleEventListeners(instance, 'add')
}

function unbind(el) {
function unmounted(el) {
// `filter` instead of `find` for compat with IE
const instance = instances.filter((instance) => instance.el === el)[0]

toggleEventListeners(instance, 'remove')
}

const directive = {
bind,
update,
unbind,
beforeMount,
updated,
unmounted,
instances
}

Expand Down

0 comments on commit 5bc62ff

Please sign in to comment.