1
1
import looseEqual from '../../../utils/loose-equal'
2
+ import range from '../../../utils/range'
2
3
import { isArray , arrayIncludes } from '../../../utils/array'
3
4
import { getComponentConfig } from '../../../utils/config'
5
+ import { isNumber } from '../../../utils/inspect'
4
6
import sanitizeRow from './sanitize-row'
5
7
6
8
export default {
@@ -11,7 +13,8 @@ export default {
11
13
} ,
12
14
selectMode : {
13
15
type : String ,
14
- default : 'multi'
16
+ default : 'multi' ,
17
+ validator : val => arrayIncludes ( [ 'range' , 'multi' , 'single' ] , val )
15
18
} ,
16
19
selectedVariant : {
17
20
type : String ,
@@ -25,36 +28,42 @@ export default {
25
28
}
26
29
} ,
27
30
computed : {
31
+ isSelectable ( ) {
32
+ return this . selectable && this . selectMode
33
+ } ,
34
+ selectableHasSelection ( ) {
35
+ return (
36
+ this . isSelectable &&
37
+ this . selectedRows &&
38
+ this . selectedRows . length > 0 &&
39
+ this . selectedRows . some ( Boolean )
40
+ )
41
+ } ,
42
+ selectableIsMultiSelect ( ) {
43
+ return this . isSelectable && arrayIncludes ( [ 'range' , 'multi' ] , this . selectMode )
44
+ } ,
28
45
selectableTableClasses ( ) {
29
- const selectable = this . selectable
30
- const isSelecting = selectable && this . selectedRows && this . selectedRows . some ( Boolean )
31
46
return {
32
- 'b-table-selectable' : selectable ,
33
- [ `b-table-select-${ this . selectMode } ` ] : selectable ,
34
- 'b-table-selecting' : isSelecting
47
+ 'b-table-selectable' : this . isSelectable ,
48
+ [ `b-table-select-${ this . selectMode } ` ] : this . isSelectable ,
49
+ 'b-table-selecting' : this . selectableHasSelection
35
50
}
36
51
} ,
37
52
selectableTableAttrs ( ) {
38
53
return {
39
- 'aria-multiselectable' : this . selectableIsMultiSelect
40
- }
41
- } ,
42
- selectableIsMultiSelect ( ) {
43
- if ( this . selectable ) {
44
- return arrayIncludes ( [ 'range' , 'multi' ] , this . selectMode ) ? 'true' : 'false'
45
- } else {
46
- return null
54
+ 'aria-multiselectable' : ! this . isSelectable
55
+ ? null
56
+ : this . selectableIsMultiSelect
57
+ ? 'true'
58
+ : 'false'
47
59
}
48
60
}
49
61
} ,
50
62
watch : {
51
63
computedItems ( newVal , oldVal ) {
52
64
// Reset for selectable
53
- // TODO: Should selectedLastClicked be reset here?
54
- // As changes to _showDetails would trigger it to reset
55
- this . selectedLastRow = - 1
56
65
let equal = false
57
- if ( this . selectable && this . selectedRows . length > 0 ) {
66
+ if ( this . isSelectable && this . selectedRows . length > 0 ) {
58
67
// Quick check against array length
59
68
equal = isArray ( newVal ) && isArray ( oldVal ) && newVal . length === oldVal . length
60
69
for ( let i = 0 ; equal && i < newVal . length ; i ++ ) {
@@ -74,9 +83,9 @@ export default {
74
83
this . clearSelected ( )
75
84
} ,
76
85
selectedRows ( selectedRows , oldVal ) {
77
- if ( this . selectable && ! looseEqual ( selectedRows , oldVal ) ) {
86
+ if ( this . isSelectable && ! looseEqual ( selectedRows , oldVal ) ) {
78
87
const items = [ ]
79
- // forEach skips over non-existant indicies (on sparse arrays)
88
+ // `. forEach()` skips over non-existent indices (on sparse arrays)
80
89
selectedRows . forEach ( ( v , idx ) => {
81
90
if ( v ) {
82
91
items . push ( this . computedItems [ idx ] )
@@ -88,35 +97,67 @@ export default {
88
97
} ,
89
98
beforeMount ( ) {
90
99
// Set up handlers
91
- if ( this . selectable ) {
100
+ if ( this . isSelectable ) {
92
101
this . setSelectionHandlers ( true )
93
102
}
94
103
} ,
95
104
methods : {
96
- isRowSelected ( idx ) {
97
- return Boolean ( this . selectedRows [ idx ] )
105
+ // Public methods
106
+ selectRow ( index ) {
107
+ // Select a particular row (indexed based on computedItems)
108
+ if (
109
+ this . isSelectable &&
110
+ isNumber ( index ) &&
111
+ index >= 0 &&
112
+ index < this . computedItems . length &&
113
+ ! this . isRowSelected ( index )
114
+ ) {
115
+ const selectedRows = this . selectableIsMultiSelect ? this . selectedRows . slice ( ) : [ ]
116
+ selectedRows [ index ] = true
117
+ this . selectedLastClicked = - 1
118
+ this . selectedRows = selectedRows
119
+ }
98
120
} ,
99
- selectableRowClasses ( idx ) {
100
- const rowSelected = this . isRowSelected ( idx )
101
- const base = this . dark ? 'bg' : 'table'
102
- const variant = this . selectedVariant
103
- return {
104
- 'b-table-row-selected' : this . selectable && rowSelected ,
105
- [ ` ${ base } - ${ variant } ` ] : this . selectable && rowSelected && variant
121
+ unselectRow ( index ) {
122
+ // Un-select a particular row (indexed based on `computedItems` )
123
+ if ( this . isSelectable && isNumber ( index ) && this . isRowSelected ( index ) ) {
124
+ const selectedRows = this . selectedRows . slice ( )
125
+ selectedRows [ index ] = false
126
+ this . selectedLastClicked = - 1
127
+ this . selectedRows = selectedRows
106
128
}
107
129
} ,
108
- selectableRowAttrs ( idx ) {
109
- return {
110
- 'aria-selected' : ! this . selectable ? null : this . isRowSelected ( idx ) ? 'true' : 'false'
130
+ selectAllRows ( ) {
131
+ const length = this . computedItems . length
132
+ if ( this . isSelectable && length > 0 ) {
133
+ this . selectedLastClicked = - 1
134
+ this . selectedRows = this . selectableIsMultiSelect ? range ( length ) . map ( i => true ) : [ true ]
111
135
}
112
136
} ,
137
+ isRowSelected ( index ) {
138
+ // Determine if a row is selected (indexed based on `computedItems`)
139
+ return Boolean ( isNumber ( index ) && this . selectedRows [ index ] )
140
+ } ,
113
141
clearSelected ( ) {
114
- const hasSelection = this . selectedRows . reduce ( ( prev , v ) => {
115
- return prev || v
116
- } , false )
117
- if ( hasSelection ) {
118
- this . selectedLastClicked = - 1
119
- this . selectedRows = [ ]
142
+ // Clear any active selected row(s)
143
+ this . selectedLastClicked = - 1
144
+ this . selectedRows = [ ]
145
+ } ,
146
+ // Internal private methods
147
+ selectableRowClasses ( index ) {
148
+ if ( this . isSelectable && this . isRowSelected ( index ) ) {
149
+ const variant = this . selectedVariant
150
+ return {
151
+ 'b-table-row-selected' : true ,
152
+ [ `${ this . dark ? 'bg' : 'table' } -${ variant } ` ] : variant
153
+ }
154
+ } else {
155
+ return { }
156
+ }
157
+ } ,
158
+ selectableRowAttrs ( index ) {
159
+ return {
160
+ 'aria-selected' : ! this . isSelectable ? null : this . isRowSelected ( index ) ? 'true' : 'false'
120
161
}
121
162
} ,
122
163
setSelectionHandlers ( on ) {
@@ -129,20 +170,20 @@ export default {
129
170
} ,
130
171
selectionHandler ( item , index , evt ) {
131
172
/* istanbul ignore if: should never happen */
132
- if ( ! this . selectable ) {
173
+ if ( ! this . isSelectable ) {
133
174
// Don't do anything if table is not in selectable mode
134
175
/* istanbul ignore next: should never happen */
135
176
this . clearSelected ( )
136
177
/* istanbul ignore next: should never happen */
137
178
return
138
179
}
180
+ const selectMode = this . selectMode
139
181
let selectedRows = this . selectedRows . slice ( )
140
182
let selected = ! selectedRows [ index ]
141
- const mode = this . selectMode
142
- // Note 'multi' mode needs no special handling
143
- if ( mode === 'single' ) {
183
+ // Note 'multi' mode needs no special event handling
184
+ if ( selectMode === 'single' ) {
144
185
selectedRows = [ ]
145
- } else if ( mode === 'range' ) {
186
+ } else if ( selectMode === 'range' ) {
146
187
if ( this . selectedLastRow > - 1 && evt . shiftKey ) {
147
188
// range
148
189
for (
@@ -155,7 +196,7 @@ export default {
155
196
selected = true
156
197
} else {
157
198
if ( ! ( evt . ctrlKey || evt . metaKey ) ) {
158
- // clear range selection if any
199
+ // Clear range selection if any
159
200
selectedRows = [ ]
160
201
selected = true
161
202
}
0 commit comments