1- import { defineComponent , h } from 'vue'
1+ import { computed , defineComponent , h , PropType } from 'vue'
22
33import { Color } from '../props'
4+ import { CTableBody } from './CTableBody'
5+ import { CTableCaption } from './CTableCaption'
6+ import { CTableDataCell } from './CTableDataCell'
7+ import { CTableFoot } from './CTableFoot'
8+ import { CTableHead } from './CTableHead'
9+ import { CTableHeaderCell } from './CTableHeaderCell'
10+ import { CTableRow } from './CTableRow'
11+
12+ export interface Column {
13+ label ?: string
14+ key : string
15+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16+ _style ?: any
17+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18+ _props ?: any
19+ }
20+
21+ export interface FooterItem {
22+ label ?: string
23+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24+ _props ?: any
25+ }
26+
27+ export type Item = {
28+ [ key : string ] : number | string | any
29+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30+ _props ?: any
31+ }
32+
33+ const pretifyName = ( name : string ) => {
34+ return name
35+ . replace ( / [ - _ . ] / g, ' ' )
36+ . replace ( / + / g, ' ' )
37+ . replace ( / ( [ a - z 0 - 9 ] ) ( [ A - Z ] ) / g, '$1 $2' )
38+ . split ( ' ' )
39+ . map ( ( word ) => word . charAt ( 0 ) . toUpperCase ( ) + word . slice ( 1 ) )
40+ . join ( ' ' )
41+ }
42+
43+ const label = ( column : Column | string ) =>
44+ typeof column === 'object'
45+ ? column . label !== undefined
46+ ? column . label
47+ : pretifyName ( column . key )
48+ : pretifyName ( column )
449
550const CTable = defineComponent ( {
651 name : 'CTable' ,
@@ -41,22 +86,61 @@ const CTable = defineComponent({
4186 /**
4287 * Put the `<caption>` on the top of the table.
4388 *
44- * @values 'top'
89+ * @values 'top' | string
4590 */
4691 caption : {
4792 type : String ,
4893 default : undefined ,
4994 required : false ,
50- validator : ( value : string ) => {
51- return value === 'top'
52- } ,
95+ } ,
96+ /**
97+ * Set the text of the table caption and the caption on the top of the table.
98+ *
99+ * @since 4.5.0
100+ */
101+ captionTop : {
102+ type : String ,
103+ default : undefined ,
104+ required : false ,
105+ } ,
106+ /**
107+ * Prop for table columns configuration. If prop is not defined, table will display columns based on the first item keys, omitting keys that begins with underscore (e.g. '_props')
108+ *
109+ * In columns prop each array item represents one column. Item might be specified in two ways:
110+ * String: each item define column name equal to item value.
111+ * Object: item is object with following keys available as column configuration:
112+ * - key (required)(String) - define column name equal to item key.
113+ * - label (String) - define visible label of column. If not defined, label will be generated automatically based on column name, by converting kebab-case and snake_case to individual words and capitalization of each word.
114+ * - _props (Object) - adds classes to all cels in column, ex. _props: { scope: 'col', className: 'custom-class' },
115+ * - _style (Object) - adds styles to the column header (useful for defining widths)
116+ *
117+ * @since 4.5.0
118+ */
119+ columns : {
120+ type : Array as PropType < Column [ ] | string [ ] > ,
121+ required : false ,
53122 } ,
54123 /**
55124 * Sets the color context of the component to one of CoreUI’s themed colors.
56125 *
57126 * @values 'primary', 'secondary', 'success', 'danger', 'warning', 'info', 'dark', 'light', string
58127 */
59128 color : Color ,
129+ /**
130+ * Array of objects or strings, where each element represents one cell in the table footer.
131+ *
132+ * Example items:
133+ * ['FooterCell', 'FooterCell', 'FooterCell']
134+ * or
135+ * [{ label: 'FooterCell', _props: { color: 'success' }, ...]
136+ *
137+ * @since 4.5.0
138+ */
139+ footer : {
140+ type : Array as PropType < FooterItem [ ] > ,
141+ default : ( ) => [ ] ,
142+ required : false ,
143+ } ,
60144 /**
61145 * Enable a hover state on table rows within a `<CTableBody>`.
62146 */
@@ -65,10 +149,18 @@ const CTable = defineComponent({
65149 required : false ,
66150 } ,
67151 /**
68- * Make any table responsive across all viewports or pick a maximum breakpoint with which to have a responsive table up to .
152+ * Array of objects, where each object represents one item - row in table. Additionally, you can add style classes to each row by passing them by '_props' key and to single cell by '_cellProps' .
69153 *
70- * @values boolean, 'sm', 'md', 'lg', 'xl', 'xxl'
154+ * Example item:
155+ * { name: 'John' , age: 12, _props: { color: 'success' }, _cellProps: { age: { className: 'fw-bold'}}}
156+ *
157+ * @since 4.5.0
71158 */
159+ items : {
160+ type : Array as PropType < Item [ ] > ,
161+ default : ( ) => [ ] ,
162+ required : false ,
163+ } ,
72164 responsive : {
73165 type : [ Boolean , String ] ,
74166 default : undefined ,
@@ -106,8 +198,39 @@ const CTable = defineComponent({
106198 type : Boolean ,
107199 required : false ,
108200 } ,
201+ /**
202+ * Properties that will be passed to the table footer component.
203+ *
204+ * Properties to [CTableFoot](#ctablefoot) component.
205+ * @since 4.5.0
206+ */
207+ tableFootProps : {
208+ type : Object ,
209+ default : undefined ,
210+ required : false ,
211+ } ,
212+ /**
213+ * Properties that will be passed to the table head component.
214+ *
215+ * Properties to [CTableHead](#ctablehead) component.
216+ * @since 4.5.0
217+ */
218+ tableHeadProps : {
219+ type : Object ,
220+ default : undefined ,
221+ required : false ,
222+ } ,
109223 } ,
110224 setup ( props , { slots, attrs } ) {
225+ const rawColumnNames = computed ( ( ) =>
226+ props . columns
227+ ? props . columns . map ( ( column : Column | string ) => {
228+ if ( typeof column === 'object' ) return column . key
229+ else return column
230+ } )
231+ : Object . keys ( props . items [ 0 ] || { } ) . filter ( ( el ) => el . charAt ( 0 ) !== '_' ) ,
232+ )
233+
111234 const table = ( ) =>
112235 h (
113236 'table' ,
@@ -116,7 +239,7 @@ const CTable = defineComponent({
116239 'table' ,
117240 {
118241 [ `align-${ props . align } ` ] : props . align ,
119- [ `caption-${ props . caption } ` ] : props . caption ,
242+ [ `caption-top ` ] : props . captionTop || props . caption === 'top' ,
120243 [ `border-${ props . borderColor } ` ] : props . borderColor ,
121244 'table-bordered' : props . bordered ,
122245 'table-borderless' : props . borderless ,
@@ -129,7 +252,118 @@ const CTable = defineComponent({
129252 attrs . class ,
130253 ] ,
131254 } ,
132- slots . default && slots . default ( ) ,
255+ {
256+ default : ( ) => [
257+ ( ( props . caption && props . caption !== 'top' ) || props . captionTop ) &&
258+ h (
259+ CTableCaption ,
260+ { } ,
261+ {
262+ default : ( ) => props . caption || props . captionTop ,
263+ } ,
264+ ) ,
265+ props . columns &&
266+ h (
267+ CTableHead ,
268+ {
269+ ...props . tableHeadProps ,
270+ } ,
271+ {
272+ default : ( ) =>
273+ h (
274+ CTableRow ,
275+ { } ,
276+ {
277+ default : ( ) => [
278+ props . columns &&
279+ props . columns . map ( ( column : Column | string ) =>
280+ h (
281+ CTableHeaderCell ,
282+ {
283+ ...( typeof column === 'object' &&
284+ column . _props && { ...column . _props } ) ,
285+ ...( typeof column === 'object' &&
286+ column . _style && { style : { ...column . _style } } ) ,
287+ } ,
288+ {
289+ default : ( ) => label ( column ) ,
290+ } ,
291+ ) ,
292+ ) ,
293+ ] ,
294+ } ,
295+ ) ,
296+ } ,
297+ ) ,
298+ props . items &&
299+ h (
300+ CTableBody ,
301+ { } ,
302+ {
303+ default : ( ) => [
304+ props . items . map ( ( item : Item ) =>
305+ h (
306+ CTableRow ,
307+ {
308+ ...( item . _props && { ...item . _props } ) ,
309+ } ,
310+ {
311+ default : ( ) => [
312+ rawColumnNames . value . map (
313+ ( colName : string ) =>
314+ item [ colName ] &&
315+ h (
316+ CTableDataCell ,
317+ {
318+ ...( item . _cellProps &&
319+ item . _cellProps [ 'all' ] && { ...item . _cellProps [ 'all' ] } ) ,
320+ ...( item . _cellProps &&
321+ item . _cellProps [ colName ] && { ...item . _cellProps [ colName ] } ) ,
322+ } ,
323+ {
324+ default : ( ) => item [ colName ] ,
325+ } ,
326+ ) ,
327+ ) ,
328+ ] ,
329+ } ,
330+ ) ,
331+ ) ,
332+ ] ,
333+ } ,
334+ ) ,
335+ slots . default && slots . default ( ) ,
336+ props . footer &&
337+ h (
338+ CTableFoot ,
339+ {
340+ ...props . tableFootProps ,
341+ } ,
342+ {
343+ default : ( ) =>
344+ h (
345+ CTableRow ,
346+ { } ,
347+ {
348+ default : ( ) => [
349+ props . footer . map ( ( item : FooterItem | string ) =>
350+ h (
351+ CTableDataCell ,
352+ {
353+ ...( typeof item === 'object' && item . _props && { ...item . _props } ) ,
354+ } ,
355+ {
356+ default : ( ) => ( typeof item === 'object' ? item . label : item ) ,
357+ } ,
358+ ) ,
359+ ) ,
360+ ] ,
361+ } ,
362+ ) ,
363+ } ,
364+ ) ,
365+ ] ,
366+ } ,
133367 )
134368 return ( ) => [
135369 props . responsive
0 commit comments