Skip to content

Commit 31177ef

Browse files
committed
feat: Custom scrollbar for QScrollArea; "nomouse" modifier for v-touch-* directives; fixed mouse wheel scroll speed issue
1 parent fbf3cb9 commit 31177ef

File tree

16 files changed

+337
-53
lines changed

16 files changed

+337
-53
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<template>
2+
<div class="layout-padding">
3+
<!--
4+
This is for fast tests.
5+
Use this page but don't add it into your commits (leave it outside
6+
of your commit).
7+
8+
For some test that you think it should be persistent,
9+
make a new *.vue file here or in another folder under /dev/components.
10+
-->
11+
<div style="height: 300px"></div>
12+
13+
<q-scroll-area style="width: 400px; height: 500px;" class="bg-yellow">
14+
<div style="margin-top: 150px"></div>
15+
<div style="margin-bottom: 25px" v-for="n in number">{{n}} Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <q-btn>Click</q-btn></div>
16+
</q-scroll-area>
17+
18+
<q-btn @click="number--">Less</q-btn>
19+
<q-btn @click="number++">More</q-btn>
20+
21+
<div style="height: 1000px"></div>
22+
</div>
23+
</template>
24+
25+
<script>
26+
export default {
27+
data () {
28+
return {
29+
number: 10,
30+
height: 1
31+
}
32+
}
33+
}
34+
</script>

dev/components/test-layout/layout.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
<q-route-tab slot="title" icon="input" to="/test-layout/drawer" replace>Drawer</q-route-tab>
3232
</q-tabs>
3333

34-
<div slot="left">
34+
<q-scroll-area slot="left" style="width: 100%; height: 100%;">
3535
<div>FirstL</div>
3636
<q-side-link class="item link" to="/test-layout/about">About</q-side-link>
3737
<q-side-link class="item link" to="/test-layout/toolbar">Toolbar</q-side-link>
@@ -40,9 +40,11 @@
4040
<div v-for="n in 60">left{{n}}</div>
4141
<q-input v-model="gigi" />
4242
<div>Last</div>
43-
</div>
44-
<div v-if="right" slot="right">
43+
</q-scroll-area>
44+
45+
<div slot="right">
4546
<div>FirstR</div>
47+
<q-side-link class="item link" to="/test-layout/about">About</q-side-link>
4648
<span id="gigi" class="bg-white text-black"></span>
4749
<span id="gogu"></span>
4850
<div v-for="n in 60">right{{n}}</div>

dev/components/web-tests/fast-test.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export default {
1616
data () {
1717
return {
1818
}
19+
},
20+
methods: {
1921
}
2022
}
2123
</script>

src/components.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export * from './components/search'
3737
export * from './components/select'
3838
export * from './components/slide-transition'
3939
export * from './components/slider'
40-
export * from './components/spinner' // ?
40+
export * from './components/spinner'
4141
export * from './components/stepper'
4242
export * from './components/tab'
4343
export * from './components/toggle'

src/components/data-table/plugins/scroll/scroll.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { getMouseWheelDirection } from '../../../../utils/event'
1+
import { getMouseWheelDistance } from '../../../../utils/event'
22
import { getScrollbarWidth } from '../../../../utils/scroll'
33

4-
const wheelOffset = 40
5-
64
export default {
75
data () {
86
return {
@@ -31,7 +29,7 @@ export default {
3129
}
3230

3331
let body = this.$refs.body
34-
body.scrollTop -= getMouseWheelDirection(e) * wheelOffset
32+
body.scrollTop += getMouseWheelDistance(e).pixelY
3533
if (body.scrollTop > 0 && body.scrollTop + body.clientHeight < body.scrollHeight) {
3634
e.preventDefault()
3735
}

src/components/layout/QLayout.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
></div>
2828

2929
<aside
30-
ref="left"
31-
class="layout-aside layout-aside-left"
30+
class="layout-aside layout-aside-left scroll"
3231
:class="computedLeftClass"
3332
:style="computedLeftStyle"
3433
v-touch-pan.horizontal="__closeLeftByTouch"
@@ -41,8 +40,7 @@
4140
</aside>
4241

4342
<aside
44-
ref="right"
45-
class="layout-aside layout-aside-right"
43+
class="layout-aside layout-aside-right scroll"
4644
:class="computedRightClass"
4745
:style="computedRightStyle"
4846
v-touch-pan.horizontal="__closeRightByTouch"

src/components/layout/layout.ios.styl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ $toolbar-padding ?= 4px 10px
1818
border-top $layout-border
1919

2020
.layout-aside
21-
overflow-y auto
2221
position absolute
2322
top 0
2423
bottom 0

src/components/layout/layout.mat.styl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ $toolbar-padding ?= 4px 12px
2020
box-shadow $layout-footer-shadow
2121

2222
.layout-aside
23-
overflow-y auto
2423
position absolute
2524
top 0
2625
bottom 0

src/components/scroll-area/QScrollArea.js

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<template>
2+
<div
3+
v-if="$q.platform.is.desktop"
4+
class="q-scrollarea scroll relative-position overflow-hidden"
5+
@mousewheel="__mouseWheel"
6+
@DOMMouseScroll="__mouseWheel"
7+
@mouseenter="hover = true"
8+
@mouseleave="hover = false"
9+
>
10+
<div class="absolute" v-touch-pan.vertical.nomouse="__panContainer">
11+
<slot></slot>
12+
<q-resize-observable @resize="__updateScrollHeight"></q-resize-observable>
13+
<q-scroll-observable @scroll="__updateScroll"></q-scroll-observable>
14+
</div>
15+
16+
<q-resize-observable @resize="__updateContainer"></q-resize-observable>
17+
<div
18+
class="q-scrollarea-thumb absolute-right"
19+
:style="thumbStyle"
20+
:class="{visible: thumbVisible}"
21+
v-touch-pan.vertical="__panThumb"
22+
></div>
23+
</div>
24+
<div
25+
v-else
26+
class="scroll relative-position"
27+
>
28+
<slot></slot>
29+
</div>
30+
</template>
31+
32+
<script>
33+
import { between } from '../../utils/format'
34+
import { getMouseWheelDistance } from '../../utils/event'
35+
import { QResizeObservable, QScrollObservable } from '../observables'
36+
import TouchPan from '../../directives/touch-pan'
37+
38+
export default {
39+
name: 'q-scroll-area',
40+
components: {
41+
QResizeObservable,
42+
QScrollObservable
43+
},
44+
directives: {
45+
TouchPan
46+
},
47+
props: {
48+
width: {
49+
type: String,
50+
default: '10px'
51+
},
52+
delay: {
53+
type: Number,
54+
default: 1000
55+
}
56+
},
57+
data () {
58+
return {
59+
active: false,
60+
hover: false,
61+
containerHeight: 0,
62+
scrollPosition: 0,
63+
scrollHeight: 0
64+
}
65+
},
66+
computed: {
67+
thumbVisible () {
68+
return this.thumbHeight < this.scrollHeight && (this.active || this.hover)
69+
},
70+
thumbHeight () {
71+
return Math.round(Math.max(20, this.containerHeight * this.containerHeight / this.scrollHeight))
72+
},
73+
thumbStyle () {
74+
const top = Math.min(
75+
this.scrollPosition + (this.scrollPercentage * (this.containerHeight - this.thumbHeight)),
76+
this.scrollHeight - this.thumbHeight
77+
)
78+
return {
79+
top: `${top}px`,
80+
width: this.width,
81+
height: `${this.thumbHeight}px`
82+
}
83+
},
84+
scrollPercentage () {
85+
const p = between(this.scrollPosition / (this.scrollHeight - this.containerHeight), 0, 1)
86+
return Math.round(p * 10000) / 10000
87+
}
88+
},
89+
methods: {
90+
__updateContainer (size) {
91+
if (this.containerHeight !== size.height) {
92+
this.containerHeight = size.height
93+
this.__setActive(true, true)
94+
}
95+
},
96+
__updateScroll (scroll) {
97+
if (this.scrollPosition !== scroll.position) {
98+
this.scrollPosition = scroll.position
99+
this.__setActive(true, true)
100+
}
101+
},
102+
__updateScrollHeight ({height}) {
103+
if (this.scrollHeight !== height) {
104+
this.scrollHeight = height
105+
this.__setActive(true, true)
106+
}
107+
},
108+
__panThumb (e) {
109+
if (e.isFirst) {
110+
this.refPos = this.scrollPosition
111+
this.__setActive(true, true)
112+
}
113+
if (e.isFinal) {
114+
this.__setActive(false)
115+
}
116+
117+
const
118+
sign = (e.direction === 'down' ? 1 : -1),
119+
multiplier = (this.scrollHeight - this.containerHeight) / (this.containerHeight - this.thumbHeight)
120+
121+
this.$el.scrollTop = this.refPos + sign * e.distance.y * multiplier
122+
},
123+
__panContainer (e) {
124+
if (e.isFirst) {
125+
this.refPos = this.scrollPosition
126+
this.__setActive(true, true)
127+
}
128+
if (e.isFinal) {
129+
this.__setActive(false)
130+
}
131+
132+
const el = this.$el
133+
el.scrollTop = this.refPos + (e.direction === 'down' ? -1 : 1) * e.distance.y
134+
if (el.scrollTop > 0 && el.scrollTop + this.containerHeight < this.scrollHeight) {
135+
e.evt.preventDefault()
136+
}
137+
},
138+
__mouseWheel (e) {
139+
const el = this.$el
140+
el.scrollTop += getMouseWheelDistance(e).pixelY
141+
if (el.scrollTop > 0 && el.scrollTop + this.containerHeight < this.scrollHeight) {
142+
e.preventDefault()
143+
}
144+
},
145+
__setActive (active, timer) {
146+
clearTimeout(this.timer)
147+
if (active === this.active) {
148+
if (active && this.timer) {
149+
this.__startTimer()
150+
}
151+
return
152+
}
153+
154+
if (active) {
155+
this.active = true
156+
if (timer) {
157+
this.__startTimer()
158+
}
159+
}
160+
else {
161+
this.active = false
162+
}
163+
},
164+
__startTimer () {
165+
this.timer = setTimeout(() => {
166+
this.active = false
167+
this.timer = null
168+
}, this.delay)
169+
}
170+
}
171+
}
172+
</script>

0 commit comments

Comments
 (0)