1414 <var-loading :loading =" loading" >
1515 <div :class =" n('main')" >
1616 <table :class =" n('table')" >
17+ <colgroup >
18+ <col
19+ v-for =" (column, index) in columns"
20+ :key =" column.key ?? column.type ?? index"
21+ :style =" getColStyle(column)"
22+ />
23+ </colgroup >
24+
1725 <thead v-if =" columns.length" >
1826 <tr :class =" n('header-row')" >
1927 <th
2028 v-for =" column in columns"
21- :key =" column.key"
22- :class =" classes(n('cell'), n('header-cell'))"
29+ :key =" column.key ?? column.type "
30+ :class =" classes(n('cell'), n('header-cell'), [isSelectionColumn(column), n('selection-cell')] )"
2331 :style =" getHeaderCellStyle(column)"
2432 >
25- {{ column.title }}
33+ <var-checkbox
34+ v-if =" isSelectionColumn(column)"
35+ :model-value =" allCurrentRowsSelected"
36+ :indeterminate =" someCurrentRowsSelected"
37+ :disabled =" !currentSelectableRows.length"
38+ tabindex =" -1"
39+ @update:model-value =" toggleAllCurrentRows"
40+ />
41+ <template v-else >{{ column.title }}</template >
2642 </th >
2743 </tr >
2844 </thead >
3147 <tr
3248 v-for =" (row, pageRowIndex) in currentData"
3349 :key =" getRowKey(row, getAbsoluteRowIndex(pageRowIndex))"
34- :class =" classes( n('row'), [striped, n('row--striped')] )"
50+ :class =" n('row')"
3551 v-bind =" getRowProps(row, pageRowIndex)"
3652 >
3753 <td
3854 v-for =" column in columns"
39- :key =" column.key"
40- :class =" classes(n('cell'), n('body-cell'))"
55+ :key =" column.key ?? column.type "
56+ :class =" classes(n('cell'), n('body-cell'), [isSelectionColumn(column), n('selection-cell')] )"
4157 :style =" getBodyCellStyle(column)"
4258 v-bind =" getCellProps(row, column, pageRowIndex)"
4359 >
44- <MaybeVNode :is =" renderCell (row , column , pageRowIndex )" tag="div" />
60+ <var-checkbox
61+ v-if =" isSelectionColumn(column)"
62+ :model-value =" isRowSelected(row, pageRowIndex)"
63+ :disabled =" !isRowSelectable(row, pageRowIndex, column)"
64+ tabindex =" -1"
65+ @update:model-value =" toggleRowSelection(row, pageRowIndex, $event)"
66+ />
67+ <MaybeVNode v-else :is =" renderCell (row , column , pageRowIndex )" tag="div" />
4568 </td >
4669 </tr >
4770 </tbody >
4871 </table >
4972
5073 <div v-if =" !currentData.length" :class =" n('empty')" >
51- {{ resolvedEmptyText }}
74+ <slot name =" empty" >
75+ {{ resolvedEmptyText }}
76+ </slot >
5277 </div >
5378 </div >
5479
78103</template >
79104
80105<script lang="ts">
81- import { call , isFunction , toNumber } from ' @varlet/shared'
82- import { computed , defineComponent , watch } from ' vue'
106+ import { call , callOrReturn , clamp , isFunction , toNumber } from ' @varlet/shared'
107+ import { computed , defineComponent , ref , watch } from ' vue'
108+ import VarCheckbox from ' ../checkbox'
83109import VarLoading from ' ../loading'
84110import { t } from ' ../locale'
85111import { injectLocaleProvider } from ' ../locale-provider/provide'
86112import VarPagination from ' ../pagination'
87113import { createNamespace , formatElevation , MaybeVNode } from ' ../utils/components'
88- import { props , type DataTableAlign , type DataTableColumn , type DataTablePagination } from ' ./props'
114+ import { toSizeUnit } from ' ../utils/elements'
115+ import {
116+ props ,
117+ type DataTableAlign ,
118+ type DataTableColumn ,
119+ type DataTableKey ,
120+ type DataTablePagination ,
121+ type DataTableSelectionColumn ,
122+ } from ' ./props'
89123
90124const { name, n, classes } = createNamespace (' data-table' )
91125
@@ -100,13 +134,15 @@ type NormalizedPaginationOptions = Required<
100134export default defineComponent ({
101135 name ,
102136 components: {
137+ VarCheckbox ,
103138 VarLoading ,
104139 VarPagination ,
105140 MaybeVNode ,
106141 },
107142 props ,
108143 setup(props ) {
109144 const { t : pt } = injectLocaleProvider ()
145+ const checkedRowKeys = ref <DataTableKey []>([... props .checkedRowKeys ])
110146 const defaultPaginationOptions: NormalizedPaginationOptions = {
111147 simple: false ,
112148 disabled: false ,
@@ -134,16 +170,16 @@ export default defineComponent({
134170 }
135171 })
136172
137- const currentPage = computed (() => Math . max ( 1 , toNumber (props .page ) || 1 ))
138- const currentPageSize = computed (() => Math . max ( 1 , toNumber (props .pageSize ) || 10 ))
173+ const currentPage = computed (() => clamp ( toNumber (props .page ) || 1 , 1 , Number .MAX_SAFE_INTEGER ))
174+ const currentPageSize = computed (() => clamp ( toNumber (props .pageSize ) || 10 , 1 , Number .MAX_SAFE_INTEGER ))
139175 const localTotal = computed (() => props .data .length )
140176 const paginationTotal = computed (() => {
141177 if (! paginationEnabled .value ) {
142178 return props .data .length
143179 }
144180
145181 if (props .remote ) {
146- return Math . max ( 0 , toNumber (props .total ) || 0 )
182+ return clamp ( toNumber (props .total ) || 0 , 0 , Number .MAX_SAFE_INTEGER )
147183 }
148184
149185 return localTotal .value
@@ -164,7 +200,7 @@ export default defineComponent({
164200 return 1
165201 }
166202
167- return Math . min (currentPage .value , pageCount .value )
203+ return clamp (currentPage .value , 1 , pageCount .value )
168204 })
169205
170206 const currentData = computed (() => {
@@ -178,7 +214,42 @@ export default defineComponent({
178214 return props .data .slice (start , end )
179215 })
180216
181- const resolvedEmptyText = computed (() => props .emptyText || (pt ? pt : t )(' selectEmptyText' ))
217+ const resolvedEmptyText = computed (() => (pt ? pt : t )(' selectEmptyText' ))
218+ const selectionColumn = computed (() => props .columns .find (isSelectionColumn ))
219+ const checkedRowKeySet = computed (() => new Set (checkedRowKeys .value ))
220+ const currentSelectableRows = computed (() => {
221+ if (! selectionColumn .value ) {
222+ return []
223+ }
224+
225+ return currentData .value
226+ .map ((row , pageRowIndex ) => ({
227+ row ,
228+ pageRowIndex ,
229+ rowIndex: getAbsoluteRowIndex (pageRowIndex ),
230+ key: getRowKey (row , getAbsoluteRowIndex (pageRowIndex )),
231+ }))
232+ .filter (({ row , rowIndex , pageRowIndex }) =>
233+ isRowSelectable (row , pageRowIndex , selectionColumn .value ! , rowIndex ),
234+ )
235+ })
236+ const allCurrentRowsSelected = computed (
237+ () =>
238+ currentSelectableRows .value .length > 0 &&
239+ currentSelectableRows .value .every (({ key }) => checkedRowKeySet .value .has (key )),
240+ )
241+ const someCurrentRowsSelected = computed (
242+ () =>
243+ currentSelectableRows .value .some (({ key }) => checkedRowKeySet .value .has (key )) && ! allCurrentRowsSelected .value ,
244+ )
245+
246+ watch (
247+ () => props .checkedRowKeys ,
248+ (value ) => {
249+ checkedRowKeys .value = [... value ]
250+ },
251+ { deep: true },
252+ )
182253
183254 watch (
184255 [hasPagination , () => props .remote , currentPage , normalizedPage ],
@@ -208,9 +279,62 @@ export default defineComponent({
208279 return row [props .rowKey ] ?? rowIndex
209280 }
210281
282+ function isSelectionColumn(column : DataTableColumn ): column is DataTableSelectionColumn {
283+ return column .type === ' selection'
284+ }
285+
286+ function isRowSelectable(
287+ row : Record <string , any >,
288+ pageRowIndex : number ,
289+ column ? : DataTableSelectionColumn ,
290+ rowIndex = getAbsoluteRowIndex (pageRowIndex ),
291+ ) {
292+ if (! column ?.selectable ) {
293+ return true
294+ }
295+
296+ return column .selectable ({
297+ row ,
298+ rowIndex ,
299+ pageRowIndex ,
300+ })
301+ }
302+
303+ function updateCheckedRowKeys(value : DataTableKey []) {
304+ checkedRowKeys .value = value
305+ call (props [' onUpdate:checkedRowKeys' ], value )
306+ }
307+
308+ function isRowSelected(row : Record <string , any >, pageRowIndex : number ) {
309+ return checkedRowKeySet .value .has (getRowKey (row , getAbsoluteRowIndex (pageRowIndex )))
310+ }
311+
312+ function toggleRowSelection(row : Record <string , any >, pageRowIndex : number , selected : boolean ) {
313+ const key = getRowKey (row , getAbsoluteRowIndex (pageRowIndex ))
314+ const nextKeys = new Set (checkedRowKeys .value )
315+
316+ selected ? nextKeys .add (key ) : nextKeys .delete (key )
317+
318+ updateCheckedRowKeys ([... nextKeys ])
319+ }
320+
321+ function toggleAllCurrentRows(selected : boolean ) {
322+ const nextKeys = new Set (checkedRowKeys .value )
323+
324+ currentSelectableRows .value .forEach (({ key }) => {
325+ selected ? nextKeys .add (key ) : nextKeys .delete (key )
326+ })
327+
328+ updateCheckedRowKeys ([... nextKeys ])
329+ }
330+
211331 const renderCell = (row : Record <string , any >, column : DataTableColumn , pageRowIndex : number ) => {
212332 const rowIndex = getAbsoluteRowIndex (pageRowIndex )
213333
334+ if (isSelectionColumn (column )) {
335+ return undefined
336+ }
337+
214338 if (column .render ) {
215339 return column .render ({
216340 row ,
@@ -230,15 +354,11 @@ export default defineComponent({
230354
231355 const rowIndex = getAbsoluteRowIndex (pageRowIndex )
232356
233- if (isFunction (props .rowProps )) {
234- return props .rowProps ({
235- row ,
236- rowIndex ,
237- pageRowIndex ,
238- })
239- }
240-
241- return props .rowProps
357+ return callOrReturn (props .rowProps , {
358+ row ,
359+ rowIndex ,
360+ pageRowIndex ,
361+ })
242362 }
243363
244364 const getCellProps = (row : Record <string , any >, column : DataTableColumn , pageRowIndex : number ) => {
@@ -248,29 +368,26 @@ export default defineComponent({
248368
249369 const rowIndex = getAbsoluteRowIndex (pageRowIndex )
250370
251- if (isFunction (column .cellProps )) {
252- return column .cellProps ({
253- row ,
254- rowIndex ,
255- pageRowIndex ,
256- column ,
257- })
258- }
259-
260- return column .cellProps
371+ return callOrReturn (column .cellProps , {
372+ row ,
373+ rowIndex ,
374+ pageRowIndex ,
375+ column ,
376+ })
261377 }
262378
263379 const getAlign = (align ? : DataTableAlign ) => align ?? ' left'
264380
381+ const getColStyle = (column : DataTableColumn ) => ({
382+ width: column .width != null ? toSizeUnit (column .width ) : isSelectionColumn (column ) ? ' 52px' : undefined ,
383+ minWidth: column .minWidth != null ? toSizeUnit (column .minWidth ) : isSelectionColumn (column ) ? ' 52px' : undefined ,
384+ })
385+
265386 const getHeaderCellStyle = (column : DataTableColumn ) => ({
266- width: column .width != null ? toSizeUnit (column .width ) : undefined ,
267- minWidth: column .minWidth != null ? toSizeUnit (column .minWidth ) : undefined ,
268387 textAlign: getAlign (column .titleAlign ?? column .align ),
269388 })
270389
271390 const getBodyCellStyle = (column : DataTableColumn ) => ({
272- width: column .width != null ? toSizeUnit (column .width ) : undefined ,
273- minWidth: column .minWidth != null ? toSizeUnit (column .minWidth ) : undefined ,
274391 textAlign: getAlign (column .align ),
275392 })
276393
@@ -287,11 +404,20 @@ export default defineComponent({
287404 paginationTotal ,
288405 resolvedEmptyText ,
289406 hasPagination ,
407+ allCurrentRowsSelected ,
408+ someCurrentRowsSelected ,
290409 getAbsoluteRowIndex ,
291410 getRowKey ,
292411 getRowProps ,
293412 getCellProps ,
413+ isSelectionColumn ,
414+ isRowSelectable ,
415+ isRowSelected ,
416+ toggleAllCurrentRows ,
417+ toggleRowSelection ,
418+ currentSelectableRows ,
294419 renderCell ,
420+ getColStyle ,
295421 getHeaderCellStyle ,
296422 getBodyCellStyle ,
297423 handlePaginationChange ,
@@ -306,5 +432,18 @@ export default defineComponent({
306432<style lang="less">
307433@import ' ../styles/common' ;
308434@import ' ../styles/elevation' ;
435+ @import ' ../ripple/ripple' ;
436+ @import ' ../form-details/formDetails' ;
437+ @import ' ../icon/icon' ;
438+ @import ' ../hover-overlay/hoverOverlay' ;
439+ @import ' ../checkbox/checkbox' ;
440+ @import ' ../loading/loading' ;
441+ @import ' ../menu/menu' ;
442+ @import ' ../menu-select/menuSelect' ;
443+ @import ' ../menu-option/menuOption' ;
444+ @import ' ../cell/cell' ;
445+ @import ' ../field-decorator/fieldDecorator' ;
446+ @import ' ../input/input' ;
447+ @import ' ../pagination/pagination' ;
309448@import ' ./dataTable' ;
310449 </style >
0 commit comments