Skip to content

Commit 87d40cf

Browse files
committed
feat: CTable: allow external sorting and filtering
1 parent c26f826 commit 87d40cf

11 files changed

+1997
-1907
lines changed

dist/coreui-vue.common.js

Lines changed: 383 additions & 374 deletions
Large diffs are not rendered by default.

dist/coreui-vue.common.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/coreui-vue.esm.js

Lines changed: 1063 additions & 1073 deletions
Large diffs are not rendered by default.

dist/coreui-vue.umd.js

Lines changed: 383 additions & 374 deletions
Large diffs are not rendered by default.

dist/coreui-vue.umd.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/coreui-vue.umd.min.js

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/coreui-vue.umd.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/index.d.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,6 @@ export declare class CDataTable extends Vue {
560560
pagination: [boolean, object]
561561
addTableClasses: [string, Array<any>, object]
562562
responsive: boolean
563-
sorting: boolean
564563
small: boolean
565564
dark: boolean
566565
striped: boolean
@@ -570,9 +569,12 @@ export declare class CDataTable extends Vue {
570569
outlined: boolean
571570
optionsRow: [boolean, string]
572571
footer: boolean
573-
defaultSorter: object
574-
defaultTableFilter: string
575-
defaultColumnFilter: object
572+
sorter: [boolean, string]
573+
columnFilter: [boolean, string]
574+
tableFilter: [boolean, string]
575+
sorterValue: object
576+
tableFilterValue: string
577+
columnFilterValue: object
576578
loading: boolean
577579
}
578580

src/components/table/CDataTable.vue

Lines changed: 101 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
class="form-control table-filter"
1111
type="text"
1212
placeholder="type string..."
13-
@input="tableFilterVal = $event.target.value"
14-
:value="tableFilterVal"
13+
@input="tableFilterChange($event.target.value)"
14+
@change="tableFilterChange($event.target.value, 'change')"
15+
:value="tableFilterState"
1516
>
1617
</div>
1718

@@ -77,10 +78,11 @@
7778
<th :class="headerClass(index)" :key="index">
7879
<slot :name="`${rawColumnNames[index]}-filter`">
7980
<input
80-
v-if="!fields || !fields[index].filterable !== false"
81+
v-if="!fields || fields[index].filter !== false"
8182
class="w-100 table-filter"
82-
@input="addColumnFilter(colName, $event.target.value)"
83-
:value="columnFilterVal[colName]"
83+
@input="columnFilterEvent(colName, $event.target.value, 'input')"
84+
@change="columnFilterEvent(colName, $event.target.value, 'change')"
85+
:value="columnFilterState[colName]"
8486
/>
8587
</slot>
8688
</th>
@@ -227,14 +229,12 @@ export default {
227229
default: 10
228230
},
229231
activePage: Number,
230-
columnFilter: Boolean,
231232
pagination: [Boolean, Object],
232233
addTableClasses: [String, Array, Object],
233234
responsive: {
234235
type: Boolean,
235236
default: true
236237
},
237-
sortable: Boolean,
238238
size: String,
239239
dark: Boolean,
240240
striped: Boolean,
@@ -243,41 +243,85 @@ export default {
243243
border: Boolean,
244244
outlined: Boolean,
245245
itemsPerPageSelect: Boolean,
246-
tableFilter: Boolean,
247-
footer: Boolean,
248-
defaultSorter: {
246+
sorter: [Boolean, String],
247+
tableFilter: [Boolean, String],
248+
columnFilter: [Boolean, String],
249+
sorterValue: {
249250
type: Object,
250251
default: () => { return {} }
251252
},
252-
defaultTableFilter: String,
253-
defaultColumnFilter: Object,
253+
tableFilterValue: String,
254+
columnFilterValue: Object,
255+
footer: Boolean,
254256
loading: Boolean,
255257
clickableRows: Boolean
256258
},
257259
data () {
258260
return {
259-
tableFilterVal: this.defaultTableFilter,
260-
columnFilterVal: this.defaultColumnFilter || {},
261-
sorter: {
262-
column: this.defaultSorter.column || null,
263-
asc: this.defaultSorter.asc === false ? false : true
261+
tableFilterState: this.tableFilterValue,
262+
columnFilterState: {},
263+
sorterState: {
264+
column: undefined,
265+
asc: true
264266
},
265267
page: this.activePage || 1,
266268
perPageItems: this.itemsPerPage,
267269
passedItems: this.items || []
268270
}
269271
},
272+
watch: {
273+
sorterValue: {
274+
immediate: true,
275+
handler (val) {
276+
this.sorterState.column = val.column
277+
this.sorterState.asc = val.asc === false ? false : true
278+
}
279+
},
280+
tableFilterValue (val) {
281+
this.tableFilterState = val
282+
},
283+
columnFilterValue: {
284+
immediate: true,
285+
handler (val) {
286+
this.columnFilterState = Object.assign({}, val)
287+
}
288+
// const state = this.columnFilterState
289+
// const currentColumns = Object.keys(state)
290+
// Object.keys(val).forEach(colName => {
291+
// if (!currentColumns.includes(colName)) {
292+
// this.setColumnFilter(colName, val[colName] || '')
293+
// }
294+
// })
295+
// currentColumns.forEach(colName => state[colName] = val[colName] || '')
296+
},
297+
items (val, oldVal) {
298+
if (
299+
val.length !== oldVal.length ||
300+
JSON.stringify(val) !== JSON.stringify(oldVal)
301+
) {
302+
this.passedItems = val
303+
}
304+
},
305+
totalPages: {
306+
immediate: true,
307+
handler (val) {
308+
this.$emit('pages-change', val)
309+
}
310+
}
311+
},
270312
computed: {
271313
columnFiltered () {
272314
let items = this.passedItems.slice()
273-
Object.entries(this.columnFilterVal).forEach(([key, value]) => {
274-
if (value && this.rawColumnNames.includes(key)) {
275-
const columnFilter = String(value).toLowerCase()
276-
items = items.filter(item => {
277-
return String(item[key]).toLowerCase().includes(columnFilter)
278-
})
279-
}
280-
})
315+
if (this.columnFilter === true) {
316+
Object.entries(this.columnFilterState).forEach(([key, value]) => {
317+
if (value && this.rawColumnNames.includes(key)) {
318+
const columnFilter = String(value).toLowerCase()
319+
items = items.filter(item => {
320+
return String(item[key]).toLowerCase().includes(columnFilter)
321+
})
322+
}
323+
})
324+
}
281325
return items
282326
},
283327
filterableCols () {
@@ -287,8 +331,8 @@ export default {
287331
},
288332
tableFiltered () {
289333
let items = this.columnFiltered.slice()
290-
if (this.tableFilterVal) {
291-
const filter = this.tableFilterVal.toLowerCase()
334+
if (this.tableFilter === true && this.tableFilterState) {
335+
const filter = this.tableFilterState.toLowerCase()
292336
const hasFilter = (item) => String(item).toLowerCase().includes(filter)
293337
items = items.filter(item => {
294338
return this.filterableCols.filter(key => hasFilter(item[key])).length
@@ -297,12 +341,12 @@ export default {
297341
return items
298342
},
299343
sortedItems () {
300-
const col = this.sorter.column
301-
if (!col || !this.rawColumnNames.includes(col)) {
344+
const col = this.sorterState.column
345+
if (!col || this.sorter !== true || !this.rawColumnNames.includes(col)) {
302346
return this.tableFiltered
303347
}
304348
//if values in column are to be sorted by numeric value they all have to be type number
305-
const flip = this.sorter.asc ? 1 : -1
349+
const flip = this.sorterState.asc ? 1 : -1
306350
return this.tableFiltered.slice().sort((a,b) => {
307351
return (a[col] > b[col]) ? 1 * flip : ((b[col] > a[col]) ? -1 * flip : 0)
308352
})
@@ -358,48 +402,44 @@ export default {
358402
]
359403
},
360404
sortingIconStyles () {
361-
return {'position-relative pr-4' : this.sortable }
405+
return {'position-relative pr-4' : this.sorter }
362406
},
363407
colspan () {
364408
return this.rawColumnNames.length
365409
},
366-
isFiltered () {
367-
return this.tableFilterVal || Object.values(this.columnFilterVal).join('')
368-
}
369-
},
370-
watch: {
371-
items (val, oldVal) {
372-
if (
373-
val.length !== oldVal.length ||
374-
JSON.stringify(val) !== JSON.stringify(oldVal)
375-
) {
376-
this.passedItems = val
377-
}
378-
},
379-
totalPages: {
380-
immediate: true,
381-
handler (val) {
382-
this.$emit('pages-change', val)
383-
}
384-
}
410+
// isFiltered () {
411+
// return this.tableFilterState || Object.values(this.columnFilterState).join('')
412+
// }
385413
},
386414
methods: {
387415
changeSort (column, index) {
388416
if (column && !this.isSortable(index)) {
389417
return
390418
}
391419
//if column changed or sort was descending change asc to true
392-
this.sorter.asc = this.sorter.column !== column || !this.sorter.asc
393-
this.sorter.column = column
420+
const state = this.sorterState
421+
state.asc = state.column !== column || !state.asc
422+
state.column = column
423+
this.$emit('update:sorter-value', this.sorterState)
424+
},
425+
columnFilterEvent (colName, value, type) {
426+
this.setColumnFilter(colName, value)
427+
const e = type === 'input' ? 'column-filter-input' : 'update:column-filter-value'
428+
this.$emit(e, this.columnFilterState)
429+
},
430+
setColumnFilter (colName, value) {
431+
this.$set(this.columnFilterState, colName, value)
394432
},
395-
addColumnFilter (colName, value) {
396-
this.$set(this.columnFilterVal, colName, value)
433+
tableFilterChange (value, type = 'input') {
434+
this.tableFilterState = value
435+
const e = type === 'input' ? 'table-filter-input' : 'update:table-filter-value'
436+
this.$emit(e, this.tableFilterState)
397437
},
398438
// clear () {
399-
// this.tableFilterVal = ''
400-
// this.columnFilterVal = {}
401-
// this.sorter.column = ''
402-
// this.sorter.asc = true
439+
// this.tableFilterState = ''
440+
// this.columnFilterState = {}
441+
// this.sorterState.column = ''
442+
// this.sorterState.asc = true
403443
// const inputs = this.$el.getElementsByClassName('table-filter')
404444
// for (let input of inputs) {
405445
// input.value = ''
@@ -424,7 +464,7 @@ export default {
424464
return classes
425465
},
426466
isSortable (index) {
427-
return this.sortable && (!this.fields || this.fields[index].sortable !== false)
467+
return this.sorter && (!this.fields || this.fields[index].sorter !== false)
428468
},
429469
headerClass (index) {
430470
const fields = this.fields
@@ -444,8 +484,8 @@ export default {
444484
this.$emit('row-clicked', item, index)
445485
},
446486
getIconState (index) {
447-
const direction = this.sorter.asc ? 'asc' : 'desc'
448-
return this.rawColumnNames[index] === this.sorter.column ? direction : 0
487+
const direction = this.sorterState.asc ? 'asc' : 'desc'
488+
return this.rawColumnNames[index] === this.sorterState.column ? direction : 0
449489
},
450490
iconClasses (index) {
451491
const state = this.getIconState(index)

src/components/table/tests/CDataTable.spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const items = [
1111
{username: 'Yiorgos Avraamu', registered: '2012/01/01', role: 'Member', status: 'Active'},
1212
{
1313
username: 'Friderik Dávid',
14-
registered: '2012/01/21',
14+
registered: '2012/01/21',
1515
role: 'Staff',
1616
status: 'Active',
1717
_cellClasses: { registered: 'custom-cell-class' }
@@ -32,7 +32,7 @@ const customWrapper = mount(Component, {
3232
tableFilter: true,
3333
itemsPerPageSelect: true,
3434
addTableClasses: 'additional-table-class',
35-
sortable: true,
35+
sorter: true,
3636
small: false,
3737
dark: true,
3838
striped: true,
@@ -42,8 +42,8 @@ const customWrapper = mount(Component, {
4242
outlined: true,
4343
columnFilter: true,
4444
footer: true,
45-
defaultSorter: { column: 'username', asc: false },
46-
defaultColumnFilter: { registered: '2012' },
45+
sorterValue: { column: 'username', asc: false },
46+
columnFilterValue: { registered: '2012' },
4747
pagination: true
4848
}
4949
})
@@ -81,9 +81,9 @@ describe(ComponentName, () => {
8181
// expect(customWrapper.vm.colspan).toBe(colspanWithIndexColumn - 1)
8282
// })
8383
it('table filter works correctly', () => {
84-
customWrapper.setData({ tableFilterVal: 'Yiorgos' })
84+
customWrapper.setProps({ tableFilterValue: 'Yiorgos' })
8585
expect(customWrapper.vm.sortedItems.length).toBe(1)
86-
customWrapper.setData({ tableFilterVal: null })
86+
customWrapper.setProps({ tableFilterValue: null })
8787
})
8888
it('shows loading layer when loading prop is set', () => {
8989
customWrapper.setProps({ loading: true })

0 commit comments

Comments
 (0)