Skip to content

Commit 080b8df

Browse files
committed
feat: rewrite CSidebar mechanism, delete hide mobile mixin
- allow menaging sidebar state by props, - detect if sidebar is in mobile mode by elementResizeDetector - separate mobile and desktop sidebar state, - listen for closing click only in mobile mode (performance), - replace 'display' prop with 'breakpoint', - add noHideOnMobileClick which disabled sidebar closing mechanism in mobile mode
1 parent de3de62 commit 080b8df

File tree

3 files changed

+103
-39
lines changed

3 files changed

+103
-39
lines changed

src/components/Sidebar/CSidebar.vue

Lines changed: 102 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
<template>
2-
<div :class="sidebarClasses" v-on-clickaway="hideMobile">
2+
<div :class="sidebarClasses">
33
<slot>Sidebar</slot>
44
</div>
55
</template>
6-
<script>
7-
import { mixin as clickaway } from 'vue-clickaway2'
8-
import { hideMobile } from './hideMobile'
96

7+
<script>
8+
import elementResizeDetectorMaker from 'element-resize-detector'
9+
import { getStyle } from '@coreui/coreui/dist/js/coreui-utilities'
1010
export default {
1111
name: 'CSidebar',
12-
mixins: [clickaway, hideMobile],
1312
props: {
1413
fixed: Boolean,
1514
minimize: Boolean,
16-
display: {
17-
type: [Boolean, String],
15+
show: {
16+
type: Boolean,
17+
default: true
18+
},
19+
mobileShow: Boolean,
20+
noHideOnMobileClick: Boolean,
21+
breakpoint: {
22+
type: [String, Boolean],
1823
default: 'lg',
19-
validator: val => [true, false, 'sm', 'md', 'lg', 'xl'].includes(val)
24+
validator: val => [false, 'sm', 'md', 'lg', 'xl'].includes(val)
2025
},
21-
mobile: Boolean,
2226
aside: Boolean,
2327
light: Boolean
2428
},
@@ -31,36 +35,110 @@ export default {
3135
},
3236
data () {
3337
return {
34-
displayed: this.display,
35-
minimized: this.minimize
38+
open: this.show,
39+
mobileOpen: this.mobileShow,
40+
minimized: this.initialMinimize,
41+
erd: elementResizeDetectorMaker(),
42+
bodyWidth: undefined,
3643
}
3744
},
3845
mounted () {
46+
this.erd.listenTo(document.body, (el) => this.bodyWidth = el.clientWidth)
47+
3948
this.$root.$on(`c-${this.mode}-toggle-minimize`, () => {
40-
this.minimized = !this.minimized
49+
this.switchState('minimized')
4150
})
4251
this.$root.$on(`c-${this.mode}-toggle`, () => {
43-
this.displayed = this.displayed ? false : this.display || true
52+
if (this.isOnMobile) {
53+
this.switchState('mobileOpen')
54+
} else {
55+
this.switchState('open')
56+
}
4457
})
4558
},
59+
watch: {
60+
show (val, oldVal) {
61+
if (val !== oldVal && val !== this.open) {
62+
this.switchState('open')
63+
}
64+
},
65+
mobileShow (val, oldVal) {
66+
if (val !== oldVal && val !== this.mobileOpen) {
67+
this.switchState('mobileOpen')
68+
}
69+
},
70+
minimize (val, oldVal) {
71+
if (val !== oldVal && val !== this.minimized) {
72+
this.switchState('minimized')
73+
}
74+
},
75+
isOnMobile: {
76+
immediate: true,
77+
handler (val, oldVal) {
78+
if (val === true && val !== oldVal) {
79+
document.body.addEventListener('click', this.mobileClick)
80+
} else if (oldVal === true) {
81+
document.body.removeEventListener('click', this.mobileClick)
82+
}
83+
}
84+
}
85+
},
4686
computed: {
4787
mode () {
4888
return this.aside ? 'aside' : 'sidebar'
4989
},
50-
breakpoint () {
51-
return this.displayed === true || this.mobile ? '' : '-' + this.display
90+
isVisible () {
91+
if (this.bodyWidth) {
92+
return this.isOnMobile ? this.mobileOpen : this.open
93+
}
94+
return false
5295
},
5396
sidebarClasses () {
5497
return [
55-
'c-sidebar',
56-
`c-sidebar-${this.light ? 'light' : 'dark'}`,
57-
{
58-
[`c-sidebar${this.breakpoint}-show`]: this.displayed,
59-
'c-sidebar-fixed': this.fixed,
60-
'c-sidebar-minimized': this.minimized,
61-
'c-sidebar-right': this.aside
62-
}
63-
]
98+
this.displayClass,
99+
'c-sidebar',
100+
`c-sidebar-${this.light ? 'light' : 'dark'}`,
101+
{
102+
'c-sidebar-show': this.isOnMobile && this.mobileOpen,
103+
[`c-sidebar-${this.breakpoint}-show`]: this.open,
104+
'c-sidebar-fixed': this.fixed,
105+
'c-sidebar-minimized': this.minimized,
106+
'c-sidebar-right': this.aside
107+
}
108+
]
109+
},
110+
breakpoints () {
111+
return {
112+
'sm': getStyle('--breakpoint-sm') || '576px',
113+
'md': getStyle('--breakpoint-md') || '768px',
114+
'lg': getStyle('--breakpoint-lg') || '992px',
115+
'xl': getStyle('--breakpoint-xl') || '1200px'
116+
}
117+
},
118+
isOnMobile () {
119+
const mobileWidth = parseFloat(this.breakpoints[this.breakpoint]) || 0
120+
return this.bodyWidth && (this.bodyWidth < mobileWidth)
121+
}
122+
},
123+
methods: {
124+
mobileClick (event) {
125+
if (!this.mobileOpen || this.noHideOnMobileClick) {
126+
return
127+
}
128+
const classList = Array.from(event.target.classList).join()
129+
const clickedOutsideSidebar = !this.$el.contains(event.target)
130+
if (
131+
(clickedOutsideSidebar && !classList.includes('c-header-toggler')) || (!clickedOutsideSidebar && event.target.tagName === 'A')
132+
) {
133+
this.switchState('mobileOpen')
134+
}
135+
},
136+
switchState (variable) {
137+
const propNames = {
138+
open: 'show', minimized: 'minimize', mobileOpen: 'mobileShow'
139+
}
140+
this.$emit(`update:${propNames[variable]}`, !this[variable])
141+
this[variable] = !this[variable]
64142
}
65143
}
66144
}

src/components/Sidebar/CSidebarNavItem.vue

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
/>
1414
</CSidebarNavDropdown>
1515
<li
16-
:class="['c-nav-item', item.classes]"
17-
@click="hideMobile"
1816
v-else
17+
:class="['c-nav-item', item.classes]"
1918
>
2019
<slot>
2120
<CSidebarNavLink v-bind="item"/>
@@ -24,10 +23,8 @@
2423
</template>
2524

2625
<script>
27-
import { hideMobile } from './hideMobile'
2826
export default {
2927
name: 'CSidebarNavItem',
30-
mixins: [ hideMobile ],
3128
props: {
3229
item: [String, Array, Object]
3330
}

src/components/Sidebar/hideMobile.js

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)