Skip to content

Commit 3600d48

Browse files
committed
feat(DsfrDataTable): permet le tri par une colonne
1 parent cc663da commit 3600d48

File tree

5 files changed

+112
-24
lines changed

5 files changed

+112
-24
lines changed

src/components/DsfrDataTable/DsfrDataTable.types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@ import type { Page } from '../DsfrPagination/DsfrPagination.types'
33
export type DsfrDataTableRow = (string | number | boolean | bigint | symbol)[]
44
| Record<string | symbol | number, unknown>
55

6+
export type DsfrDataTableHeaderCellObject = { key: string, label: string, headerAttrs?: Record<string, unknown> }
7+
export type DsfrDataTableHeaderCell = (string | DsfrDataTableHeaderCellObject)
8+
69
export type DsfrDataTableProps = {
710
id?: string
811
title: string
912
rowKey?: string | number
10-
headersRow: (string | { key: string, label: string, headerAttrs?: Record<string, unknown> })[]
13+
headersRow: DsfrDataTableHeaderCell[]
1114
rows: DsfrDataTableRow[]
1215
topActionsRow?: string[]
1316
bottomActionsRow?: string[]
1417
selectableRows?: boolean
18+
sortableRows?: boolean
19+
sorted: string
20+
sortFn?: (a: unknown, b: unknown) => number
1521
verticalBorders?: boolean
1622
bottomCaption?: boolean
1723
noCaption?: boolean

src/components/DsfrDataTable/DsfrDataTable.vue

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,40 @@
22
import { computed, ref } from 'vue'
33
44
import { getRandomId } from '@/utils/random-utils'
5-
import DsfrPagination, { type Page } from '../DsfrPagination/DsfrPagination.vue'
6-
import type { DsfrDataTableProps } from './DsfrDataTable.types'
5+
import VIcon from '../VIcon/VIcon.vue'
6+
import DsfrPagination from '../DsfrPagination/DsfrPagination.vue'
7+
8+
export type Page = { href?: string, label: string, title: string }
9+
10+
export type DsfrDataTableRow = (string | number | boolean | bigint | symbol)[]
11+
| Record<string | symbol | number, unknown>
12+
13+
export type DsfrDataTableHeaderCellObject = { key: string, label: string, headerAttrs?: Record<string, unknown> }
14+
export type DsfrDataTableHeaderCell = (string | DsfrDataTableHeaderCellObject)
15+
16+
export type DsfrDataTableProps = {
17+
id?: string
18+
title: string
19+
rowKey?: string | number
20+
headersRow: DsfrDataTableHeaderCell[]
21+
rows: DsfrDataTableRow[]
22+
topActionsRow?: string[]
23+
bottomActionsRow?: string[]
24+
selectableRows?: boolean
25+
sortableRows?: boolean
26+
sorted: string
27+
sortFn?: (a: unknown, b: unknown) => number
28+
verticalBorders?: boolean
29+
bottomCaption?: boolean
30+
noCaption?: boolean
31+
pages?: Page[]
32+
pagination?: boolean
33+
paginationOptions?: number[]
34+
currentPage?: number
35+
rowsPerPage?: number
36+
bottomActionBarClass?: string | Record<string, boolean> | Array<string | Record<string, boolean>>
37+
paginationWrapperClass?: string | Record<string, boolean> | Array<string | Record<string, boolean>>
38+
}
739
840
const props = withDefaults(defineProps<DsfrDataTableProps>(), {
941
id: () => getRandomId('table'),
@@ -32,6 +64,42 @@ const pages = computed<Page[]>(() => props.pages ?? Array.from({ length: pageCou
3264
const lowestLimit = computed(() => currentPage.value * rowsPerPage.value)
3365
const highestLimit = computed(() => (currentPage.value + 1) * rowsPerPage.value)
3466
67+
function defaultSortFn (a, b) {
68+
const key = props.sorted
69+
if ((a[key] ?? a) < (b[key] ?? b)) {
70+
return -1
71+
}
72+
if ((a[key] ?? a) > (b[key] ?? b)) {
73+
return 1
74+
}
75+
return 0
76+
}
77+
78+
const sorted = defineModel<string | undefined>('sorted', { default: undefined })
79+
const sortedDesc = defineModel('sortedDesc', { default: false })
80+
function sortBy (key: string) {
81+
if (!props.sortableRows) {
82+
return
83+
}
84+
if (sorted.value === key) {
85+
if (sortedDesc.value) {
86+
sorted.value = undefined
87+
sortedDesc.value = false
88+
return
89+
}
90+
sortedDesc.value = true
91+
return
92+
}
93+
sortedDesc.value = false
94+
sorted.value = key
95+
}
96+
const sortedRows = computed(() => {
97+
const _sortedRows = sorted.value ? props.rows.slice().sort(props.sortFn ?? defaultSortFn) : props.rows.slice()
98+
if (sortedDesc.value) {
99+
_sortedRows.reverse()
100+
}
101+
return _sortedRows
102+
})
35103
const finalRows = computed(() => {
36104
const rowKeys = props.headersRow.map((header) => {
37105
if (typeof header !== 'object') {
@@ -40,7 +108,7 @@ const finalRows = computed(() => {
40108
return header.key
41109
})
42110
43-
const rows = props.rows.map((row) => {
111+
const rows = sortedRows.value.map((row) => {
44112
if (Array.isArray(row)) {
45113
return row
46114
}
@@ -108,17 +176,34 @@ function onPaginationOptionsChange () {
108176
</div>
109177
</th>
110178
<th
111-
v-for="header of headersRow"
179+
v-for="(header, idx) of headersRow"
112180
:key="typeof header === 'object' ? header.key : header"
113181
scope="col"
114182
v-bind="typeof header === 'object' && header.headerAttrs"
183+
:tabindex="sortableRows ? 0 : undefined"
184+
@click="sortBy((header as DsfrDataTableHeaderCellObject).key ?? (Array.isArray(rows[0]) ? idx : header))"
185+
@keydown.enter="sortBy((header as DsfrDataTableHeaderCellObject).key ?? header)"
186+
@keydown.space="sortBy((header as DsfrDataTableHeaderCellObject).key ?? header)"
115187
>
116-
<slot
117-
name="header"
118-
v-bind="typeof header === 'object' ? header : { key: header, label: header }"
188+
<div
189+
:class="{ 'sortable-header': sortableRows }"
119190
>
120-
{{ typeof header === 'object' ? header.label : header }}
121-
</slot>
191+
<slot
192+
name="header"
193+
v-bind="typeof header === 'object' ? header : { key: header, label: header }"
194+
>
195+
{{ typeof header === 'object' ? header.label : header }}
196+
</slot>
197+
<span v-if="sorted !== ((header as DsfrDataTableHeaderCellObject).key ?? header) && sortableRows">
198+
<VIcon
199+
name="ri-sort-asc"
200+
color="var(--grey-625-425)"
201+
/>
202+
</span>
203+
<span v-else-if="sorted === ((header as DsfrDataTableHeaderCellObject).key ?? header)">
204+
<VIcon :name="sortedDesc ? 'ri-sort-desc' : 'ri-sort-asc'" />
205+
</span>
206+
</div>
122207
</th>
123208
</tr>
124209
</thead>
@@ -247,4 +332,9 @@ function onPaginationOptionsChange () {
247332
:deep(.fr-pagination__link) {
248333
margin-bottom: 0 !important;
249334
}
335+
.sortable-header {
336+
display: flex;
337+
justify-content: space-between;
338+
cursor: pointer;
339+
}
250340
</style>

src/components/DsfrDataTable/docs-demo/DsfrDataTableDemoComplexe.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const selection = ref<string[]>([])
4040
:headers-row="headers"
4141
:rows="rows"
4242
selectable-rows
43+
sortable-rows
4344
:row-key="0"
4445
>
4546
<template #header="{ key, label }">

src/components/DsfrDataTable/docs-demo/DsfrDataTableDemoPlusComplexe.vue

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,11 @@ const headers: DsfrDataTableProps['headersRow'] = [
2020
]
2121
2222
const rows = [
23-
{ id: 1, name: 'John Doe', email: 'john.doe@gmail.com' },
2423
{ id: 2, name: 'Jane Doe', email: 'jane.doe@gmail.com' },
24+
{ id: 1, name: 'John Doe', email: 'john.doe@gmail.com' },
2525
{ id: 3, name: 'James Bond', email: 'james.bond@mi6.gov.uk' },
2626
]
2727
28-
const click = (event: MouseEvent, key: string) => {
29-
console.warn(event, key)
30-
}
31-
3228
const selection = ref<string[]>([])
3329
const currentPage = ref<number>(0)
3430
</script>
@@ -48,11 +44,11 @@ const currentPage = ref<number>(0)
4844
:pagination-options="[1, 2, 3]"
4945
bottom-action-bar-class="bottom-action-bar-class"
5046
pagination-wrapper-class="pagination-wrapper-class"
47+
sorted="id"
48+
sortable-rows
5149
>
52-
<template #header="{ key, label }">
53-
<div @click="click($event, key)">
54-
<em>{{ label }}</em>
55-
</div>
50+
<template #header="{ label }">
51+
<em>{{ label }}</em>
5652
</template>
5753

5854
<template #cell="{ colKey, cell }">

src/components/DsfrTabs/DsfrTabs.vue

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,6 @@ provide(registerTabKey, (tabId: Ref<string>) => {
4545
4646
const isVisible = computed(() => myIndex === activeTab.value)
4747
48-
// watch(tabId, () => {
49-
// console.log({ myIndex, tabId: tabId.value })
50-
// tabs.value.set(myIndex, tabId.value)
51-
// })
52-
5348
watch(tabId, () => {
5449
tabs.value.set(myIndex, tabId.value)
5550
})

0 commit comments

Comments
 (0)