55
66 <p class =" title" >sandboxのトピックログ</p >
77
8+ <div class =" field" >
9+ <div class =" control has-icons-left" >
10+ <input
11+ v-model =" searchQuery"
12+ class =" input"
13+ type =" text"
14+ placeholder =" トピックを検索..."
15+ >
16+ <span class =" icon is-left" >
17+ <i class =" ri-search-line" />
18+ </span >
19+ </div >
20+ </div >
21+
822 <div class =" control is-spaced" >
923 並び替え:
1024 <label class =" radio" >
4862 <div class =" table-container" >
4963 <table class =" table topics" >
5064 <tbody >
51- <tr v-for =" {message, likes, isLiked} in sortedTopics " :key =" message.ts" >
65+ <tr v-for =" {message, likes, isLiked} in paginatedTopics " :key =" message.ts" >
5266 <td class =" topic-text" >
53- <nuxt-link v-if =" message.user" :to =" `/users/${message.user}`" >
67+ <nuxt-link
68+ v-if =" message.user"
69+ :to =" `/users/${message.user}`"
70+ class =" topic-user"
71+ >
5472 <img
5573 class =" topic-icon"
5674 :src =" getUserIcon(message)"
5775 >
5876 {{getUserName(message)}}
5977 </nuxt-link >
60- <span v-else >
78+ <span v-else class = " topic-user " >
6179 <img
6280 class =" topic-icon"
6381 :src =" getUserIcon(message)"
88106 </tbody >
89107 </table >
90108 </div >
109+
110+ <nav
111+ v-if =" totalPages > 1"
112+ class =" pagination is-centered"
113+ role =" navigation"
114+ aria-label =" pagination"
115+ >
116+ <a
117+ class =" pagination-previous"
118+ :class =" {'is-disabled': currentPage === 1}"
119+ @click =" changePage(currentPage - 1)"
120+ >
121+ 前へ
122+ </a >
123+ <a
124+ class =" pagination-next"
125+ :class =" {'is-disabled': currentPage === totalPages}"
126+ @click =" changePage(currentPage + 1)"
127+ >
128+ 次へ
129+ </a >
130+ <ul class =" pagination-list" >
131+ <li v-for =" (page, index) in paginationRange" :key =" index" >
132+ <span v-if =" page === '...'" class =" pagination-ellipsis" >&hellip ; </span >
133+ <a
134+ v-else
135+ class =" pagination-link"
136+ :class =" {'is-current': page === currentPage}"
137+ :aria-label =" `ページ ${page}`"
138+ :aria-current =" page === currentPage ? 'page' : null"
139+ @click =" () => changePage(page)"
140+ >
141+ {{page}}
142+ </a >
143+ </li >
144+ </ul >
145+ </nav >
91146 </div >
92147</template >
93148
@@ -97,11 +152,15 @@ import get from 'lodash/get';
97152import sortBy from ' lodash/sortBy' ;
98153import {mapActions , mapGetters , mapState } from ' vuex' ;
99154
155+ const ITEMS_PER_PAGE = 100 ;
156+
100157export default {
101158 data () {
102159 return {
103160 isLoading: true ,
104161 sortBy: ' timestamp' ,
162+ currentPage: 1 ,
163+ searchQuery: ' ' ,
105164 };
106165 },
107166 async fetch ({store}) {
@@ -124,28 +183,79 @@ export default {
124183 ),
125184 }),
126185 ... mapGetters (' slackInfos' , [' getUser' ]),
186+ filteredTopics () {
187+ if (! this .searchQuery .trim ()) {
188+ return this .topicMessages ;
189+ }
190+ const query = this .searchQuery .toLowerCase ().normalize (' NFKC' );
191+ return this .topicMessages .filter (({message}) => {
192+ const text = (message .text ? .toLowerCase () || ' ' ).normalize (' NFKC' );
193+ const username = this .getUserName (message).toLowerCase ().normalize (' NFKC' );
194+ return text .includes (query) || username .includes (query);
195+ });
196+ },
127197 sortedTopics () {
198+ let sorted;
128199 if (this .sortBy === ' timestamp' ) {
129- return sortBy (this .topicMessages , ({message}) => message .ts ).reverse ();
130- }
131- if (this .sortBy === ' username' ) {
132- return sortBy (this .topicMessages , [
200+ sorted = sortBy (this .filteredTopics , ({message}) => message .ts ).reverse ();
201+ } else if (this .sortBy === ' username' ) {
202+ sorted = sortBy (this .filteredTopics , [
133203 ({message}) => this .getUserName (message),
134204 ({message}) => - parseFloat (message .ts ),
135205 ]);
136- }
137- if (this .sortBy === ' likes' ) {
138- return sortBy (this .topicMessages , [
206+ } else if (this .sortBy === ' likes' ) {
207+ sorted = sortBy (this .filteredTopics , [
139208 ({likes}) => - likes .length ,
140209 ({message}) => - parseFloat (message .ts ),
141210 ]);
142- }
143- if (this .sortBy === ' random' ) {
144- return sortBy (this .topicMessages , [
211+ } else if (this .sortBy === ' random' ) {
212+ sorted = sortBy (this .filteredTopics , [
145213 ({randomSortKey}) => randomSortKey,
146214 ]);
215+ } else {
216+ sorted = this .filteredTopics ;
217+ }
218+ return sorted;
219+ },
220+ totalPages () {
221+ return Math .ceil (this .sortedTopics .length / ITEMS_PER_PAGE );
222+ },
223+ paginatedTopics () {
224+ const start = (this .currentPage - 1 ) * ITEMS_PER_PAGE ;
225+ const end = start + ITEMS_PER_PAGE ;
226+ return this .sortedTopics .slice (start, end);
227+ },
228+ paginationRange () {
229+ const range = [];
230+ const total = this .totalPages ;
231+ const current = this .currentPage ;
232+ const delta = 2 ;
233+
234+ for (let i = Math .max (2 , current - delta); i <= Math .min (total - 1 , current + delta); i++ ) {
235+ range .push (i);
147236 }
148- return this .topicMessages ;
237+
238+ if (current - delta > 2 ) {
239+ range .unshift (' ...' );
240+ }
241+ if (current + delta < total - 1 ) {
242+ range .push (' ...' );
243+ }
244+
245+ range .unshift (1 );
246+ if (total > 1 ) {
247+ range .push (total);
248+ }
249+
250+ return range;
251+ },
252+ },
253+ watch: {
254+ sortBy () {
255+ this .currentPage = 1 ;
256+ },
257+ searchQuery () {
258+ this .currentPage = 1 ;
149259 },
150260 },
151261 mounted () {
@@ -157,6 +267,12 @@ export default {
157267 });
158268 },
159269 methods: {
270+ changePage (page ) {
271+ if (page >= 1 && page <= this .totalPages ) {
272+ this .currentPage = page;
273+ window .scrollTo ({top: 0 , behavior: ' smooth' });
274+ }
275+ },
160276 ... mapActions ({
161277 likeTopicMessage: ' slackInfos/likeTopicMessage' ,
162278 unlikeTopicMessage: ' slackInfos/unlikeTopicMessage' ,
@@ -202,6 +318,12 @@ export default {
202318< / script>
203319
204320< style>
321+ .control {
322+ .radio {
323+ margin- inline- start: 0 .5em ;
324+ }
325+ }
326+
205327.topics .table td, .topics .table th {
206328 padding- left: 0 .25em ;
207329 padding- right: 0 .25em ;
@@ -210,6 +332,10 @@ export default {
210332.topic - text {
211333 line- break: anywhere;
212334 min- width: 20em ;
335+
336+ .topic - user {
337+ margin- inline- end: 0 .2em ;
338+ }
213339}
214340
215341.topic - icon {
@@ -238,5 +364,14 @@ export default {
238364.topic - like .icon {
239365 vertical- align: bottom;
240366}
367+
368+ .pagination {
369+ margin- top: 2rem ;
370+ margin- bottom: 2rem ;
371+ }
372+
373+ .field {
374+ margin- bottom: 1 .5rem ;
375+ }
241376< / style>
242377
0 commit comments