1616 </div >
1717
1818 <div
19- v-if =" havePaginationMenu() "
19+ v-if =" optionsRow !== 'noPagination' "
2020 class =" c-col-sm-6 c-p-0"
2121 :class =" optionsRow === 'noFilter' ? 'c-offset-sm-6' : ''"
2222 >
2929 <option value =" " selected disabled hidden >
3030 {{perPageItems}}
3131 </option >
32- <option v-for =" number in [5,10,20,50]" val =" number" >
32+ <option
33+ v-for =" (number, key) in [5,10,20,50]"
34+ :val =" number"
35+ :key =" key"
36+ >
3337 {{number}}
3438 </option >
3539 </select >
4953 @click =" changeSort(rawColumnNames[index], index)"
5054 :class =" [headerClass(index), sortingIconStyles]"
5155 :style =" headerStyles(index)"
56+ :key =" index"
5257 >
5358 <slot :name =" `${rawColumnNames[index]}-header`" >
5459 <div class =" c-d-inline" >{{name}}</div >
8085 />
8186 </th >
8287 <template v-for =" (colName , index ) in rawColumnNames " >
83- <th :class =" headerClass(index)" >
88+ <th :class =" headerClass(index)" :key = " index " >
8489 <slot :name =" `${rawColumnNames[index]}-filter`" >
8590 <input
8691 v-if =" !fields || !fields[index].noFilter"
8792 class =" c-w-100 c-table-filter"
8893 @input =" addColumnFilter(colName, $event.target.value)"
8994 :value =" columnFilter[colName]"
90- ></ input >
95+ / >
9196 </slot >
9297 </th >
9398 </template >
100105 <tr
101106 :class =" item._classes" :tabindex =" bodyStyle ? 0 : null"
102107 @click =" rowClicked(item, itemIndex + firstItemIndex)"
108+ :key =" itemIndex"
103109 >
104110 <slot
105111 v-if =" indexColumn"
119125 :item =" item"
120126 :index =" itemIndex + firstItemIndex"
121127 />
122- <td v-else :class =" cellClass(item, colName, index)" >
128+ <td
129+ v-else
130+ :class =" cellClass(item, colName, index)"
131+ :key =" index"
132+ >
123133 {{item[colName]}}
124134 </td >
125135 </template >
128138 v-if =" $scopedSlots.details"
129139 class =" c-p-0"
130140 style =" border :none !important "
141+ :key =" 'details' + itemIndex"
131142 >
132143 <td
133144 :colspan =" colspan"
168179 @click =" changeSort(rawColumnNames[index], index)"
169180 :class =" [headerClass(index), sortingIconStyles]"
170181 :style =" headerStyles(index)"
182+ :key =" index"
171183 >
172184 <slot :name =" `${rawColumnNames[index]}-header`" >
173185 <div class =" c-d-inline" >{{name}}</div >
@@ -269,25 +281,34 @@ export default {
269281 column: this .defaultSorter .column || null ,
270282 asc: this .defaultSorter .asc || true
271283 },
272- firstItemIndex: 0 ,
273284 page: this .activePage || 1 ,
274285 perPageItems: this .perPage ,
275286 passedItems: this .items || []
276287 }
277288 },
278289 computed: {
279290 columnFiltered () {
280- let items = this .passedItems
291+ let items = this .passedItems . slice ()
281292 Object .keys (this .columnFilter ).forEach (key => {
282- items = items .filter (item => String (item[key]).toLowerCase ().includes (this .columnFilter [key].toLowerCase ()))
293+ items = items .filter (item => {
294+ const columnFilter = this .columnFilter [key].toLowerCase ()
295+ return String (item[key]).toLowerCase ().includes (columnFilter)
296+ })
283297 })
284298 return items
285299 },
300+ filterableCols () {
301+ return this .rawColumnNames .filter (name => {
302+ return this .generatedColumnNames .includes (name)
303+ })
304+ },
286305 tableFiltered () {
287- let items = this .columnFiltered
306+ let items = this .columnFiltered . slice ()
288307 if (this .tableFilter ) {
308+ const filter = this .tableFilter .toLowerCase ()
309+ const hasFilter = (item ) => String (item).toLowerCase ().includes (filter)
289310 items = items .filter (item => {
290- return Object . keys (item) .filter (key => String (item[key]). toLowerCase (). includes ( this . tableFilter . toLowerCase () )).length
311+ return this . filterableCols .filter (key => hasFilter (item[key])).length
291312 })
292313 }
293314 return items
@@ -297,41 +318,46 @@ export default {
297318 if (! col || ! this .rawColumnNames .includes (col)) {
298319 return this .tableFiltered
299320 }
300- // if numbers should be sorted by numeric value they all have to be valid js numbers
321+ // if values in column are to be sorted by numeric value they all have to be type number
301322 const flip = this .sorter .asc ? 1 : - 1
302- return this .tableFiltered .sort ((a ,b ) => {
303- // escape html
304- let c = typeof a[col] === ' string' ? a[col].replace (/ <(?:. | \n )*? >/ gm , ' ' ) : a[col]
305- let d = typeof b[col] === ' string' ? b[col].replace (/ <(?:. | \n )*? >/ gm , ' ' ) : b[col]
306- // if (typeof c !== typeof d) {
307- // c = String(c)
308- // d = String(d)
309- // }
310- return (c > d) ? 1 * flip : ((d > c) ? - 1 * flip : 0 )
323+ return this .tableFiltered .slice ().sort ((a ,b ) => {
324+ return (a[col] > b[col]) ? 1 * flip : ((b[col] > a[col]) ? - 1 * flip : 0 )
311325 })
312326 },
327+ firstItemIndex () {
328+ return (this .computedPage - 1 ) * this .perPageItems || 0
329+ },
330+ paginatedItems () {
331+ return this .sortedItems .slice (
332+ this .firstItemIndex ,
333+ this .firstItemIndex + this .perPageItems
334+ )
335+ },
313336 currentItems () {
314- if (this .computedPage ) {
315- this .firstItemIndex = (this .computedPage - 1 ) * this .perPageItems
316- return this .sortedItems .slice (this .firstItemIndex , this .firstItemIndex + this .perPageItems )
317- }
318- return this .sortedItems
337+ return this .computedPage ? this .paginatedItems : this .sortedItems
319338 },
320339 totalPages () {
321340 return Math .ceil ((this .sortedItems .length )/ this .perPageItems ) || 1
322341 },
323342 computedPage () {
324343 return this .pagination ? this .page : this .activePage
325344 },
345+ generatedColumnNames () {
346+ return Object .keys (this .passedItems [0 ]).filter (el => el .charAt (0 ) !== ' _' )
347+ },
326348 rawColumnNames () {
327- if (this .fields )
328- return typeof this .fields [0 ] === ' object' ? this .fields .map (el => el .key ) : this .fields
329- return Object .keys (this .currentItems [0 ]).filter (el => el .charAt (0 ) !== ' _' )
349+ if (this .fields ) {
350+ return this .fields .map (el => el .key || el)
351+ }
352+ return this .generatedColumnNames
330353 },
331354 columnNames () {
332- if (this .fields )
333- return this .fields .map (el => el .label !== undefined ? el .label : this .columnNamePretify (el .key || el))
334- return this .rawColumnNames .map (el => this .columnNamePretify (el))
355+ if (this .fields ) {
356+ return this .fields .map (f => {
357+ return f .label !== undefined ? f .label : this .pretifyName (f .key || f)
358+ })
359+ }
360+ return this .rawColumnNames .map (el => this .pretifyName (el))
335361 },
336362 tableClasses () {
337363 return [
@@ -356,26 +382,28 @@ export default {
356382 return ! this .noSorting ? ' c-position-relative c-pr-4' : ' '
357383 },
358384 colspan () {
359- return this .indexColumn ? this . rawColumnNames .length + 1 : this . rawColumnNames . length
385+ return this .rawColumnNames .length + ( this . indexColumn ? 1 : 0 )
360386 },
361387 topLoadingPosition () {
362388 const headerHeight = (this .filterRow ? 38 : 0 ) + ( this .small ? 32 + 4 : 46 + 7 )
363389 return ` top:${ headerHeight} px`
364390 },
365391 spinnerSize () {
366- const size = this .small ? ' 1.4rem ' : this .currentItems .length === 1 ? ' 2rem ' : ' 3rem '
367- return ` width:${ size} ;height:${ size} `
392+ const size = this .small ? 1.4 : this .currentItems .length === 1 ? 2 : 3
393+ return ` width:${ size + ' rem ' } ;height:${ size + ' rem ' } `
368394 },
369395 isFiltered () {
370- return this .tableFilter || Object .keys (this .columnFilter ).filter (key => {
371- return this .columnFilter [key]
372- }).length
396+ return this .tableFilter || Object .values (this .columnFilter ).join (' ' )
373397 }
374398 },
375399 watch: {
376400 items (val , oldVal ) {
377- if (val .length !== oldVal .length || JSON .stringify (val) !== JSON .stringify (oldVal))
401+ if (
402+ val .length !== oldVal .length ||
403+ JSON .stringify (val) !== JSON .stringify (oldVal)
404+ ) {
378405 this .passedItems = val
406+ }
379407 },
380408 totalPages: {
381409 immediate: true ,
@@ -405,35 +433,38 @@ export default {
405433 this .sorter .asc = true
406434 const inputs = this .$el .getElementsByClassName (' c-table-filter' )
407435 for (let input of inputs)
408- input .value = ' '
436+ input .value = ' '
409437 },
410- columnNamePretify (name ) {
411- const withSpaces = name .replace (/ [-_] / g , ' ' )
412- return withSpaces .split (' ' )
413- .map (word => word .charAt (0 ).toUpperCase () + word .slice (1 ))
414- .join (' ' )
438+ pretifyName (name ) {
439+ return name .replace (/ [-_] / g , ' ' ).split (' ' ).map (word => {
440+ return word .charAt (0 ).toUpperCase () + word .slice (1 )
441+ }).join (' ' )
415442 },
416443 cellClass (item , colName , index ) {
417444 let classes = []
418- if (item ._cellClasses && item ._cellClasses [colName])
445+ if (item ._cellClasses && item ._cellClasses [colName]) {
419446 classes .push (item ._cellClasses [colName])
420- if (this .fields && this .fields [index]._classes )
447+ }
448+ if (this .fields && this .fields [index]._classes ) {
421449 classes .push (this .fields [index]._classes )
450+ }
422451 return classes
423452 },
424453 sortable (index ) {
425454 return ! this .noSorting && (! this .fields || ! this .fields [index].noSorting )
426455 },
427456 headerClass (index ) {
428- return this . fields && this .fields [index]. _classes ?
429- this . fields [index]._classes : ' '
457+ const fields = this .fields
458+ return fields && fields[index]. _classes ? fields[index]._classes : ' '
430459 },
431460 headerStyles (index ) {
432461 let style = ' '
433- if (this .sortable (index))
462+ if (this .sortable (index)) {
434463 style += ` cursor:pointer;`
435- if (this .fields && this .fields [index] && this .fields [index]._style )
464+ }
465+ if (this .fields && this .fields [index] && this .fields [index]._style ) {
436466 style += this .fields [index]._style
467+ }
437468 return style
438469 },
439470 rowClicked (item , index ) {
@@ -456,12 +487,8 @@ export default {
456487 paginationChange (e ) {
457488 this .$emit (' pagination-change' , e .target .value )
458489 this .perPageItems = Number (e .target .value )
459- },
460- havePaginationMenu () {
461- return this .optionsRow !== ' noPagination' &&
462- (this .pagination || this .$listeners [' pages-change' ])
463490 }
464- },
491+ }
465492}
466493 </script >
467494<style scoped>
0 commit comments