Skip to content

Commit b0472bb

Browse files
committed
docs: chat demo
1 parent d57af3f commit b0472bb

File tree

3 files changed

+217
-7
lines changed

3 files changed

+217
-7
lines changed

docs-src/src/App.vue

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,32 @@
55
<span class="package-name">vue-virtual-scroller</span>
66
</span>
77

8-
<router-link :to="{ name: 'home' }" exact>Home</router-link>
9-
<router-link :to="{ name: 'recycle' }">Recycle scroller</router-link>
10-
<router-link :to="{ name: 'dynamic' }">Dynamic scroller</router-link>
11-
<router-link :to="{ name: 'horizontal' }">Horizontal</router-link>
12-
<router-link :to="{ name: 'test-chat' }">Scroll to bottom</router-link>
13-
<router-link :to="{ name: 'simple-list' }">Simple array</router-link>
8+
<router-link
9+
:to="{ name: 'home' }"
10+
exact
11+
>
12+
Home
13+
</router-link>
14+
<router-link :to="{ name: 'recycle' }">
15+
Recycle scroller
16+
</router-link>
17+
<router-link :to="{ name: 'dynamic' }">
18+
Dynamic scroller
19+
</router-link>
20+
<router-link :to="{ name: 'horizontal' }">
21+
Horizontal
22+
</router-link>
23+
<router-link :to="{ name: 'test-chat' }">
24+
Scroll to bottom
25+
</router-link>
26+
<router-link :to="{ name: 'simple-list' }">
27+
Simple array
28+
</router-link>
29+
<router-link :to="{ name: 'chat' }">
30+
Chat demo
31+
</router-link>
1432
</nav>
15-
<router-view class="page"/>
33+
<router-view class="page" />
1634
</div>
1735
</template>
1836

docs-src/src/components/ChatDemo.vue

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
<template>
2+
<div class="chat-demo">
3+
<div class="toolbar">
4+
<button
5+
v-if="!streaming"
6+
@click="startStream()"
7+
>
8+
Start stream
9+
</button>
10+
<button
11+
v-else
12+
@click="stopStream()"
13+
>
14+
Stop stream
15+
</button>
16+
17+
<input
18+
v-model="search"
19+
placeholder="Filter..."
20+
>
21+
</div>
22+
23+
<DynamicScroller
24+
ref="scroller"
25+
:items="filteredItems"
26+
:min-item-size="54"
27+
class="scroller"
28+
>
29+
<template #before>
30+
<div class="notice">
31+
The message heights are unknown.
32+
</div>
33+
</template>
34+
35+
<template v-slot="{ item, index, active }">
36+
<DynamicScrollerItem
37+
:item="item"
38+
:active="active"
39+
:size-dependencies="[
40+
item.message,
41+
]"
42+
:data-index="index"
43+
:data-active="active"
44+
:title="`Click to change message ${index}`"
45+
class="message"
46+
@click.native="changeMessage(item)"
47+
>
48+
<div class="avatar">
49+
<img
50+
:key="item.avatar"
51+
:src="item.avatar"
52+
alt="avatar"
53+
class="image"
54+
>
55+
</div>
56+
<div class="text">
57+
{{ item.message }}
58+
</div>
59+
<div class="index">
60+
<span>{{ item.id }} (id)</span>
61+
<span>{{ index }} (index)</span>
62+
</div>
63+
</DynamicScrollerItem>
64+
</template>
65+
</DynamicScroller>
66+
</div>
67+
</template>
68+
69+
<script>
70+
import { generateMessage } from '../data'
71+
72+
let id = 0
73+
74+
const messages = []
75+
for (let i = 0; i < 10000; i++) {
76+
messages.push(generateMessage())
77+
}
78+
79+
export default {
80+
data () {
81+
return {
82+
items: [],
83+
search: '',
84+
streaming: false,
85+
}
86+
},
87+
88+
computed: {
89+
filteredItems () {
90+
const { search, items } = this
91+
if (!search) return items
92+
const lowerCaseSearch = search.toLowerCase()
93+
return items.filter(i => i.message.toLowerCase().includes(lowerCaseSearch))
94+
},
95+
},
96+
97+
destroyed () {
98+
this.stopStream()
99+
},
100+
101+
methods: {
102+
changeMessage (message) {
103+
Object.assign(message, generateMessage())
104+
},
105+
106+
addMessage () {
107+
this.items.push({
108+
id: id++,
109+
...messages[id % 10000],
110+
})
111+
this.scrollToBottom()
112+
113+
if (this.streaming) {
114+
requestAnimationFrame(this.addMessage)
115+
}
116+
},
117+
118+
scrollToBottom () {
119+
this.$refs.scroller.scrollToBottom()
120+
},
121+
122+
startStream () {
123+
if (this.streaming) return
124+
this.streaming = true
125+
this.addMessage()
126+
},
127+
128+
stopStream () {
129+
this.streaming = false
130+
},
131+
},
132+
}
133+
</script>
134+
135+
<style scoped>
136+
.chat-demo,
137+
.scroller {
138+
height: 100%;
139+
}
140+
141+
.chat-demo {
142+
overflow: hidden;
143+
}
144+
145+
.notice {
146+
padding: 24px;
147+
font-size: 20px;
148+
color: #999;
149+
}
150+
151+
.message {
152+
display: flex;
153+
min-height: 32px;
154+
padding: 12px;
155+
box-sizing: border-box;
156+
}
157+
158+
.avatar {
159+
flex: auto 0 0;
160+
width: 32px;
161+
height: 32px;
162+
border-radius: 50%;
163+
margin-right: 12px;
164+
}
165+
166+
.avatar .image {
167+
max-width: 100%;
168+
max-height: 100%;
169+
border-radius: 50%;
170+
}
171+
172+
.index,
173+
.text {
174+
flex: 1;
175+
}
176+
177+
.text {
178+
max-width: 400px;
179+
}
180+
181+
.index {
182+
opacity: .5;
183+
}
184+
185+
.index span {
186+
display: inline-block;
187+
width: 160px;
188+
text-align: right;
189+
}
190+
</style>

docs-src/src/router.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Dynamic from './components/DynamicScrollerDemo.vue'
77
import TestChat from './components/TestChat.vue'
88
import SimpleList from './components/SimpleList.vue'
99
import HorizontalDemo from './components/HorizontalDemo.vue'
10+
import ChatDemo from './components/ChatDemo.vue'
1011

1112
Vue.use(VueRouter)
1213

@@ -18,5 +19,6 @@ export default new VueRouter({
1819
{ path: '/test-chat', name: 'test-chat', component: TestChat },
1920
{ path: '/simple-list', name: 'simple-list', component: SimpleList },
2021
{ path: '/horizontal', name: 'horizontal', component: HorizontalDemo },
22+
{ path: '/chat', name: 'chat', component: ChatDemo },
2123
],
2224
})

0 commit comments

Comments
 (0)