Skip to content
Permalink
Browse files

feat(MdTable): reactive selection (#1358)

* fix(MdTable): multiple selection with table sorting

an easy way to handle multiple selection

fix #1348

* refactor(MdTable): single selection using instance comparing instead of id comparing

both `MdTable` and `MdTableRow` emit `md-selected` event to avoid breaking change

* feat(MdTable): new props `:md-selected-value.sync` for reactive selection

fix #1292

* fix(MdTable): `syncSelectedValue` without `mdSelectedValue` in multiple selecting mode

* fix(MdTableHeadSelection): fix `allSelected`, now disabled row could be tick from `md-selected-value

* fix(MdTableRow): remove `md-selected` event

too many duplicated events on a selection. It should be emit from `MdTable`

BREAKING CHANGE: no more `md-selected` event from `MdTableRow`

* fix(MdTable): select event should be triggered after select update event

* fix(MdTableHeadSelection): select all only take effect on selectable items
  • Loading branch information...
VdustR authored and marcosmoura committed Dec 29, 2017
1 parent ba876d7 commit 906a91da065fe56cc253188164b76b21853ea74d
@@ -1,11 +1,11 @@
<template>
<div>
<md-table v-model="people" md-card>
<md-table v-model="people" md-card @md-selected="onSelect">
<md-table-toolbar>
<h1 class="md-title">Selection Colors</h1>
</md-table-toolbar>

<md-table-row slot="md-table-row" slot-scope="{ item }" :class="getClass(item)" md-selectable="single" @md-selected="onSelect">
<md-table-row slot="md-table-row" slot-scope="{ item }" :class="getClass(item)" md-selectable="single">
<md-table-cell md-label="ID" md-sort-by="id" md-numeric>{{ item.id }}</md-table-cell>
<md-table-cell md-label="Name" md-sort-by="name">{{ item.name }}</md-table-cell>
<md-table-cell md-label="Email" md-sort-by="email">{{ item.email }}</md-table-cell>
@@ -27,7 +27,8 @@
v-for="(item, index) in value"
:key="getRowId(item[mdModelId])"
:md-id="getRowId(item[mdModelId])"
:md-index="index">
:md-index="index"
:md-item="item">
<slot name="md-table-row" :item="item" />
</md-table-row-ghost>
</tbody>
@@ -69,7 +70,7 @@
return value
}
export default {
name: 'MdTable',
components: {
@@ -119,6 +120,9 @@
return aAttr.localeCompare(bAttr)
})
}
},
mdSelectedValue: {
type: [Array, Object]
}
},
data () {
@@ -130,8 +134,8 @@
sort: null,
sortOrder: null,
singleSelection: null,
selectedItems: {},
selectable: {},
selectedItems: [],
selectable: [],
fixedHeader: null,
contentPadding: null,
contentEl: null,
@@ -142,7 +146,8 @@
sortTable: this.sortTable,
manageItemSelection: this.manageItemSelection,
getModel: this.getModel,
getModelItem: this.getModelItem
getModelItem: this.getModelItem,
selectingMode: null
}
}
},
@@ -158,7 +163,7 @@
return Object.keys(this.MdTable.items).length
},
selectedCount () {
return Object.keys(this.MdTable.selectedItems).length
return this.MdTable.selectedItems.length
},
headerStyles () {
if (this.mdFixedHeader) {
@@ -213,6 +218,15 @@
handler () {
this.MdTable.hasValue = this.hasValue
}
},
'MdTable.selectedItems' (val) {
this.select(val)
},
'MdTable.singleSelection' (val) {
this.select(val)
},
mdSelectedValue () {
this.syncSelectedValue()
}
},
methods: {
@@ -251,24 +265,38 @@
getModelItem (index) {
return this.value[index]
},
manageItemSelection (index) {
if (this.MdTable.selectedItems[index]) {
this.$delete(this.MdTable.selectedItems, index)
manageItemSelection (item) {
if (this.MdTable.selectedItems.includes(item)) {
this.MdTable.selectedItems = this.MdTable.selectedItems.filter(target => target !== item)
} else {
this.$set(this.MdTable.selectedItems, index, this.value[index])
this.MdTable.selectedItems.push(item)
}
this.sendSelectionEvent()
},
sendSelectionEvent () {
this.$emit('md-selected', Object.values(this.MdTable.selectedItems))
},
sortTable () {
if (Array.isArray(this.value)) {
this.$emit('input', this.mdSortFn(this.value))
}
},
select (val) {
this.$emit('update:mdSelectedValue', val)
this.$emit('md-selected', val)
},
syncSelectedValue () {
switch (this.MdTable.selectingMode) {
case 'single':
this.MdTable.singleSelection = this.mdSelectedValue
break
case 'multiple':
this.MdTable.selectedItems = this.mdSelectedValue || []
break
}
}
},
async created () {
// wait for `selectingMode` from `TableRow`
await this.$nextTick()
this.syncSelectedValue()
},
mounted () {
this.setContentEl()
@@ -1,7 +1,7 @@
<template>
<md-table-head class="md-table-cell-selection" v-if="selectableCount">
<div class="md-table-cell-container">
<md-checkbox v-model="allSelected" :disabled="isDisabled" @change="onChange" />
<md-checkbox :model="allSelected" :disabled="isDisabled" @change="onChange" />
</div>
</md-table-head>
</template>
@@ -15,9 +15,6 @@
MdTableHead
},
inject: ['MdTable'],
data: () => ({
allSelected: false
}),
computed: {
selectableCount () {
return Object.keys(this.selectable).length
@@ -30,28 +27,22 @@
},
selectedItems () {
return this.MdTable.selectedItems
}
},
watch: {
selectedItems: {
immediate: true,
deep: true,
handler (items) {
window.setTimeout(() => {
const countSelected = Object.keys(items).length
if (this.selectableCount > 0 && countSelected > 0) {
this.allSelected = countSelected === this.selectableCount
}
}, 10)
},
allSelected () {
if (this.selectableCount === 0) {
return false
}
return this.selectable.every(item => this.selectedItems.includes(item))
}
},
methods: {
onChange () {
Object.values(this.MdTable.selectable).forEach(callback => {
callback(this.allSelected)
})
onChange (val) {
if (val) {
this.MdTable.selectedItems = this.selectedItems.concat(this.selectable.filter(item => !this.selectedItems.includes(item)))
} else {
this.MdTable.selectedItems = this.selectedItems.filter(item => !this.selectable.includes(item))
}
}
}
}
@@ -27,7 +27,8 @@
...MdPropValidator('md-selectable', ['multiple', 'single'])
},
mdDisabled: Boolean,
mdAutoSelect: Boolean
mdAutoSelect: Boolean,
mdItem: Object
},
inject: ['MdTable'],
data: () => ({
@@ -39,7 +40,7 @@
return Object.keys(this.MdTable.selectable).length
},
isSingleSelected () {
return this.MdTable.singleSelection === this.mdId
return this.MdTable.singleSelection === this.mdItem
},
hasMultipleSelection () {
return this.MdTable.hasValue && this.mdSelectable === 'multiple'
@@ -55,6 +56,9 @@
'md-selected-single': this.isSingleSelected
}
}
},
isInSelectedItems () {
return this.MdTable.selectedItems.includes(this.mdItem)
}
},
watch: {
@@ -65,12 +69,20 @@
this.addSelectableItem()
}
},
mdId (newId, oldId) {
this.removeSelectableItem(oldId)
this.addSelectableItem(newId)
isSelected (val) {
let noChange = (val && this.isInSelectedItems) || (!val && !this.isInSelectedItems)
if (noChange) {
return false
}
this.MdTable.manageItemSelection(this.mdItem)
},
isInSelectedItems (val) {
this.isSelected = val
},
isSelected () {
this.MdTable.manageItemSelection(this.mdIndex)
mdSelectable () {
this.MdTable.selectingMode = this.mdSelectable
}
},
methods: {
@@ -87,34 +99,39 @@
this.isSelected = !this.isSelected
},
selectRowIfSingle () {
if (this.MdTable.singleSelection === this.mdId) {
if (this.MdTable.singleSelection === this.mdItem) {
this.MdTable.singleSelection = null
this.$emit('md-selected', null)
} else {
this.MdTable.singleSelection = this.mdId
this.$emit('md-selected', this.MdTable.getModelItem(this.mdIndex))
this.MdTable.singleSelection = this.mdItem
}
},
selectRowIfMultiple () {
if (this.mdAutoSelect) {
this.toggleSelection()
}
},
addSelectableItem (id) {
if (this.hasMultipleSelection && !this.mdDisabled) {
this.$set(this.MdTable.selectable, id || this.mdId, isSelected => {
this.isSelected = isSelected
})
addSelectableItem () {
if (!this.hasMultipleSelection || this.mdDisabled) {
return
}
if (this.MdTable.selectable.includes(this.mdItem)) {
return
}
this.MdTable.selectable.push(this.mdItem)
},
removeSelectableItem (id) {
if (this.hasMultipleSelection) {
this.$delete(this.MdTable.selectable, id || this.mdId)
removeSelectableItem () {
if (!this.hasMultipleSelection) {
return
}
this.MdTable.selectable = this.MdTable.selectable.filter(item => item !== this.mdItem)
}
},
created () {
this.addSelectableItem()
this.MdTable.selectingMode = this.mdSelectable
},
beforeDestroy () {
this.removeSelectableItem()
@@ -4,11 +4,13 @@
abstract: true,
props: {
mdIndex: [String, Number],
mdId: [String, Number]
mdId: [String, Number],
mdItem: Object
},
render () {
this.$slots.default[0].componentOptions.propsData.mdIndex = this.mdIndex
this.$slots.default[0].componentOptions.propsData.mdId = this.mdId
this.$slots.default[0].componentOptions.propsData.mdItem = this.mdItem
return this.$slots.default[0]
}

0 comments on commit 906a91d

Please sign in to comment.
You can’t perform that action at this time.