Skip to content

Commit 3831b5c

Browse files
committed
refactor: refactors in sidebar components
- rework of CSidebar component, - fix passing exact prop in CSidebarNavLink, - add CSidebarClose component, - change CSidebarNavDropdown open prop to show - update tests and typings
1 parent f56cff5 commit 3831b5c

11 files changed

+163
-215
lines changed

src/components/Sidebar/CSidebar.vue

Lines changed: 47 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
<template>
2-
<div :class="sidebarClasses">
3-
<slot>Sidebar</slot>
2+
<div :class="sidebarClasses" @click="sidebarClick">
3+
<slot></slot>
44
</div>
55
</template>
66

77
<script>
8-
import elementResizeDetectorMaker from 'element-resize-detector'
9-
import { getStyle } from '@coreui/coreui/dist/js/coreui-utilities'
10-
118
export default {
129
name: 'CSidebar',
1310
props: {
1411
fixed: Boolean,
12+
unfoldable: Boolean,
1513
breakpoint: {
16-
type: String,
14+
type: [String, Boolean],
1715
default: 'lg',
18-
validator: val => ['sm', 'md', 'lg', 'xl'].includes(val)
16+
validator: val => [false, '', 'sm', 'md', 'lg', 'xl'].includes(val)
1917
},
2018
minimize: Boolean,
2119
show: {
20+
type: Boolean,
21+
default: null
22+
},
23+
hideOnMobileClick: {
2224
type: Boolean,
2325
default: true
2426
},
25-
showOnMobile: Boolean,
26-
noHideOnMobileClick: Boolean,
2727
aside: Boolean,
28-
light: Boolean,
29-
dropdownStateOnRouteChange: {
28+
colorScheme: {
29+
type: String,
30+
default: 'dark'
31+
},
32+
dropdownMode: {
3033
type: String,
3134
default: 'openActive',
3235
validator: (mode) => {
@@ -37,142 +40,68 @@ export default {
3740
}
3841
},
3942
provide () {
40-
const state = {}
41-
Object.defineProperty(state, 'minimized', {
42-
get: () => this.minimized
43-
})
44-
Object.defineProperty(state, 'open', {
45-
get: () => this.open
46-
})
47-
Object.defineProperty(state, 'mobileOpen', {
48-
get: () => this.mobileOpen
49-
})
50-
const dropdownStateOnRouteChange = this.dropdownStateOnRouteChange
51-
return { state, dropdownStateOnRouteChange }
43+
return { dropdownMode: this.dropdownMode }
5244
},
5345
data () {
5446
return {
5547
open: this.show,
56-
mobileOpen: this.showOnMobile,
57-
minimized: this.minimize,
58-
erd: elementResizeDetectorMaker(),
59-
bodyWidth: undefined,
6048
}
6149
},
62-
mounted () {
63-
this.erd.listenTo(document.body, (el) => this.bodyWidth = el.clientWidth)
64-
65-
this.$root.$on(this.listenedEvents, () => {
66-
if (this.isOnMobile) {
67-
this.switchState('mobileOpen')
68-
} else {
69-
this.switchState('open')
70-
}
71-
})
72-
},
7350
watch: {
7451
show (val) {
7552
this.open = val
76-
},
77-
open: {
78-
immediate: true,
79-
handler (val) {
80-
this.emitCurrentState('show', val)
81-
}
82-
},
83-
showOnMobile (val) {
84-
this.mobileOpen = val
85-
},
86-
mobileOpen: {
87-
immediate: true,
88-
handler (val) {
89-
this.emitCurrentState('showOnMobile', val)
90-
}
91-
},
92-
minimize (val) {
93-
this.minimized = val
94-
},
95-
minimized: {
96-
immediate: true,
97-
handler (val) {
98-
this.emitCurrentState('minimize', val)
99-
}
100-
},
101-
isOnMobile: {
102-
immediate: true,
103-
handler (val, oldVal) {
104-
if (val === true && val !== oldVal) {
105-
document.body.addEventListener('click', this.mobileClick)
106-
} else if (oldVal === true) {
107-
document.body.removeEventListener('click', this.mobileClick)
108-
}
109-
110-
if (val !== undefined) {
111-
this.$root.$emit('c-sidebar-mobile-state', val)
112-
}
113-
}
11453
}
11554
},
11655
computed: {
117-
listenedEvents () {
118-
const componentEvent = this.aside ? 'c-aside-toggle' : 'c-sidebar-toggle'
119-
if (this.$attrs.id) {
120-
return [componentEvent, this.$attrs.id]
56+
displayClasses () {
57+
const breakpointPrefix = this.breakpoint === false ? '' : `-${this.breakpoint}`
58+
const responsiveClass = `c-sidebar${breakpointPrefix}-show`
59+
if (this.open === false) {
60+
return null
61+
} else if (this.open === true) {
62+
return ['c-sidebar-show', breakpointPrefix ? responsiveClass : '' ]
63+
} else if (this.open === null) {
64+
return responsiveClass
12165
}
122-
return componentEvent
12366
},
67+
12468
sidebarClasses () {
125-
const mobileClasses = {
126-
'c-sidebar-show': this.mobileOpen,
127-
}
128-
const desktopClasses = {
129-
'c-sidebar-minimized': this.minimized,
130-
[`c-sidebar-${this.breakpoint}-show`]: this.open,
131-
}
13269
return [
133-
this.isOnMobile ? mobileClasses : desktopClasses,
13470
'c-sidebar',
135-
`c-sidebar-${this.light ? 'light' : 'dark'}`,
71+
`c-sidebar-${this.colorScheme}`,
72+
this.displayClasses,
13673
{
13774
'c-sidebar-fixed': this.fixed,
138-
'c-sidebar-right': this.aside
75+
'c-sidebar-right': this.aside,
76+
'c-sidebar-minimized': this.minimize && !this.unfoldable,
77+
'c-sidebar-minimized-unfoldable': this.minimize && this.unfoldable
13978
}
14079
]
141-
},
142-
breakpoints () {
143-
return {
144-
'sm': getStyle('--breakpoint-sm') || '576px',
145-
'md': getStyle('--breakpoint-md') || '768px',
146-
'lg': getStyle('--breakpoint-lg') || '992px',
147-
'xl': getStyle('--breakpoint-xl') || '1200px'
148-
}
149-
},
150-
isOnMobile () {
151-
const mobileWidth = parseFloat(this.breakpoints[this.breakpoint])
152-
return this.bodyWidth && (this.bodyWidth < mobileWidth)
15380
}
15481
},
15582
methods: {
156-
mobileClick (event) {
157-
if (!this.mobileOpen || this.noHideOnMobileClick) {
158-
return
159-
}
160-
const classList = Array.from(event.target.classList).join()
161-
const clickedOutsideSidebar = !this.$el.contains(event.target)
83+
sidebarClick (event) {
84+
const hiddingElementClicked = event.target.tagName === 'A'
16285
if (
163-
(clickedOutsideSidebar && !classList.includes('c-header-toggler')) ||
164-
(!clickedOutsideSidebar && event.target.tagName === 'A')
86+
hiddingElementClicked &&
87+
this.hideOnMobileClick &&
88+
this.isOnMobile(event)
16589
) {
166-
this.switchState('mobileOpen')
90+
this.open = false
91+
this.$emit('update:show', false)
16792
}
16893
},
169-
switchState (variable) {
170-
this[variable] = !this[variable]
171-
},
172-
emitCurrentState (variableName, value) {
173-
this.$emit(`update:${variableName}`, value)
174-
this.$root.$emit(`c-sidebar-${variableName}-state`, value)
94+
isOnMobile (event) {
95+
return Boolean(getComputedStyle(this.$el).getPropertyValue('--on-mobile'))
17596
}
17697
}
17798
}
17899
</script>
100+
101+
<style scoped>
102+
@media (max-width: 992px) {
103+
.c-sidebar-lg-show {
104+
--on-mobile: true;
105+
}
106+
}
107+
</style>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<template>
2+
<CButtonClose buttonClasses="c-sidebar-close"/>
3+
</template>
4+
5+
<script>
6+
import CButtonClose from '../Button/CButtonClose'
7+
export default {
8+
name: 'CSidebarClose',
9+
components: {
10+
CButtonClose
11+
}
12+
}
13+
</script>

src/components/Sidebar/CSidebarNavDropdown.vue

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,32 @@ export default {
2020
validator: val => val.length > 0
2121
},
2222
icon: String,
23-
open: Boolean
23+
show: Boolean
2424
},
2525
data () {
2626
return {
27-
isOpen: this.open
27+
open: this.show
2828
}
2929
},
3030
inject: {
31-
dropdownStateOnRouteChange: {
31+
dropdownMode: {
3232
default: 'openActive'
3333
}
3434
},
3535
watch: {
36-
open (val) {
37-
this.isOpen = val
36+
show (val) {
37+
this.open = val
3838
},
3939
$route: {
4040
immediate: true,
4141
handler (route) {
42-
const mode = this.dropdownStateOnRouteChange
42+
const mode = this.dropdownMode
4343
if (mode === 'close') {
44-
this.isOpen = false
44+
this.open = false
4545
} else if (mode === 'closeInactive' && this.route) {
46-
this.isOpen = route.fullPath.includes(this.route)
47-
} else if (mode === 'openActive' && !this.isOpen && this.route) {
48-
this.isOpen = route.fullPath.includes(this.route)
46+
this.open = route.fullPath.includes(this.route)
47+
} else if (mode === 'openActive' && !this.open && this.route) {
48+
this.open = route.fullPath.includes(this.route)
4949
}
5050
// else if (mode === 'noAction') {
5151
// return
@@ -60,15 +60,15 @@ export default {
6060
dropdownClasses () {
6161
return [
6262
'c-nav-item c-nav-dropdown',
63-
{ 'c-open': this.isOpen }
63+
{ 'c-open': this.open }
6464
]
6565
}
6666
},
6767
methods: {
6868
handleClick (e) {
6969
e.preventDefault()
70-
this.isOpen = !this.isOpen
71-
this.$emit('update:open', this.isOpen)
70+
this.open = !this.open
71+
this.$emit('update:show', this.open)
7272
},
7373
itemClicked (e) {
7474
this.$emit('item-clicked', e)

src/components/Sidebar/CSidebarNavLink.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
<li class="c-nav-item">
33
<CLink
44
:class="linkClasses"
5-
:exact="true"
6-
v-bind="linkProps"
5+
v-bind="computedLinkProps"
76
@click.native="click"
87
>
98
<slot>
@@ -44,6 +43,12 @@ export default {
4443
return props
4544
}, {})
4645
},
46+
addedLinkProps () {
47+
return this.$options.propsData.exact === undefined ? { exact: true } : {}
48+
},
49+
computedLinkProps () {
50+
return Object.assign(this.linkProps, this.addedLinkProps)
51+
},
4752
linkClasses () {
4853
return [
4954
this.label ? 'c-nav-label' : 'c-nav-link',

src/components/Sidebar/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import CSidebar from './CSidebar'
22
import CSidebarBrand from './CSidebarBrand'
3+
import CSidebarClose from './CSidebarClose'
34
import CSidebarFooter from './CSidebarFooter'
45
import CSidebarForm from './CSidebarForm'
56
import CSidebarHeader from './CSidebarHeader'
6-
import CSidebarNav from './CSidebarNav'
77
import CSidebarMinimizer from './CSidebarMinimizer'
8+
import CSidebarNav from './CSidebarNav'
89
import CSidebarNavDivider from './CSidebarNavDivider'
910
import CSidebarNavDropdown from './CSidebarNavDropdown'
1011
import CSidebarNavLink from './CSidebarNavLink'
@@ -14,6 +15,7 @@ import CSidebarToggler from './CSidebarToggler'
1415
export {
1516
CSidebar,
1617
CSidebarBrand,
18+
CSidebarClose,
1719
CSidebarFooter,
1820
CSidebarForm,
1921
CSidebarHeader,

0 commit comments

Comments
 (0)