@tekkare/romulus 0.1.63 → 0.1.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/components/Table.vue +106 -2
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -78,6 +78,12 @@ export interface RmTableProps {
|
|
|
78
78
|
groupLabel?: (value: any) => string
|
|
79
79
|
/** Chaque ligne se deplie sur un detail, rendu par le slot `expanded`. */
|
|
80
80
|
expandable?: boolean
|
|
81
|
+
/**
|
|
82
|
+
* Lignes par page. A zero, la table rend tout : c'est le bon choix tant
|
|
83
|
+
* qu'on tient sous la centaine de lignes, la molette y va plus vite qu'un
|
|
84
|
+
* clic. Au-dela, paginer evite de poser des milliers de `<tr>` dans le DOM.
|
|
85
|
+
*/
|
|
86
|
+
pageSize?: number
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
const props = withDefaults(defineProps<RmTableProps>(), {
|
|
@@ -97,6 +103,7 @@ const props = withDefaults(defineProps<RmTableProps>(), {
|
|
|
97
103
|
groupBy: '',
|
|
98
104
|
groupLabel: undefined,
|
|
99
105
|
expandable: false,
|
|
106
|
+
pageSize: 0,
|
|
100
107
|
})
|
|
101
108
|
|
|
102
109
|
const emit = defineEmits<{
|
|
@@ -104,6 +111,9 @@ const emit = defineEmits<{
|
|
|
104
111
|
rowClick: [row: Record<string, any>, index: number]
|
|
105
112
|
}>()
|
|
106
113
|
|
|
114
|
+
/** Page courante, a partir de 1. Exposee pour qu'une page puisse la rejouer. */
|
|
115
|
+
const page = defineModel<number>('page', { default: 1 })
|
|
116
|
+
|
|
107
117
|
/** Les lignes retenues, par leur cle. */
|
|
108
118
|
const selected = defineModel<(string | number)[]>('selected', { default: () => [] })
|
|
109
119
|
|
|
@@ -158,7 +168,10 @@ const sortedRows = computed(() => {
|
|
|
158
168
|
* intitule : le rendu reste une suite de `<tr>` dans un seul `<tbody>`.
|
|
159
169
|
*/
|
|
160
170
|
const sections = computed(() => {
|
|
161
|
-
|
|
171
|
+
// L'index reste celui de la liste entiere : un slot ou un `rowClick` doit
|
|
172
|
+
// designer la meme ligne, quelle que soit la page ouverte.
|
|
173
|
+
const offset = props.pageSize > 0 ? (currentPage.value - 1) * props.pageSize : 0
|
|
174
|
+
const rows = pagedRows.value.map((row, i) => ({ row, index: offset + i }))
|
|
162
175
|
if (!props.groupBy) return [{ label: '', rows }]
|
|
163
176
|
|
|
164
177
|
const buckets = new Map<any, typeof rows>()
|
|
@@ -173,6 +186,55 @@ const sections = computed(() => {
|
|
|
173
186
|
}))
|
|
174
187
|
})
|
|
175
188
|
|
|
189
|
+
const totalPages = computed(() =>
|
|
190
|
+
props.pageSize > 0 ? Math.max(1, Math.ceil(sortedRows.value.length / props.pageSize)) : 1,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* La page demandee, ramenee dans les bornes. Un tri ou un filtre qui raccourcit
|
|
195
|
+
* la liste laisserait sinon la table sur une page vide, ce qui se lit comme
|
|
196
|
+
* une recherche sans resultat.
|
|
197
|
+
*/
|
|
198
|
+
const currentPage = computed(() => Math.min(Math.max(1, page.value), totalPages.value))
|
|
199
|
+
|
|
200
|
+
const pagedRows = computed(() => {
|
|
201
|
+
if (props.pageSize <= 0) return sortedRows.value
|
|
202
|
+
const start = (currentPage.value - 1) * props.pageSize
|
|
203
|
+
return sortedRows.value.slice(start, start + props.pageSize)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
/** Le rang de la premiere et de la derniere ligne affichees, pour le compte. */
|
|
207
|
+
const rangeFrom = computed(() =>
|
|
208
|
+
sortedRows.value.length === 0 ? 0 : (currentPage.value - 1) * props.pageSize + 1,
|
|
209
|
+
)
|
|
210
|
+
const rangeTo = computed(() =>
|
|
211
|
+
Math.min(currentPage.value * props.pageSize, sortedRows.value.length),
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Les numeros a poser : les deux voisins de la page courante, les deux bouts,
|
|
216
|
+
* et des ellipses pour le reste. Aligner cinquante numeros ferait une barre
|
|
217
|
+
* plus longue que la table.
|
|
218
|
+
*/
|
|
219
|
+
const pageItems = computed<(number | '...')[]>(() => {
|
|
220
|
+
const total = totalPages.value
|
|
221
|
+
const here = currentPage.value
|
|
222
|
+
if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1)
|
|
223
|
+
|
|
224
|
+
const items: (number | '...')[] = [1]
|
|
225
|
+
const from = Math.max(2, here - 1)
|
|
226
|
+
const to = Math.min(total - 1, here + 1)
|
|
227
|
+
if (from > 2) items.push('...')
|
|
228
|
+
for (let i = from; i <= to; i++) items.push(i)
|
|
229
|
+
if (to < total - 1) items.push('...')
|
|
230
|
+
items.push(total)
|
|
231
|
+
return items
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
function goTo(target: number) {
|
|
235
|
+
page.value = Math.min(Math.max(1, target), totalPages.value)
|
|
236
|
+
}
|
|
237
|
+
|
|
176
238
|
const colspan = computed(
|
|
177
239
|
() => props.columns.length + (props.selectable ? 1 : 0) + (props.expandable ? 1 : 0),
|
|
178
240
|
)
|
|
@@ -354,9 +416,51 @@ function heatClass(col: RmTableColumn, row: Record<string, any>) {
|
|
|
354
416
|
</tbody>
|
|
355
417
|
</table>
|
|
356
418
|
</div>
|
|
419
|
+
|
|
420
|
+
<!-- Le compte a gauche, les numeros a droite. Sans le compte, « page 3 »
|
|
421
|
+
ne dit pas combien il reste a lire. -->
|
|
422
|
+
<nav v-if="pageSize > 0 && totalPages > 1" class="rm-table__pager" aria-label="Pagination">
|
|
423
|
+
<span class="rm-table__pager-count">
|
|
424
|
+
{{ rangeFrom }}–{{ rangeTo }} sur {{ sortedRows.length }}
|
|
425
|
+
</span>
|
|
426
|
+
|
|
427
|
+
<div class="rm-table__pager-controls">
|
|
428
|
+
<RmButton
|
|
429
|
+
icon-only
|
|
430
|
+
size="sm"
|
|
431
|
+
variant="secondary"
|
|
432
|
+
prefix-icon="CaretLeft"
|
|
433
|
+
aria-label="Page précédente"
|
|
434
|
+
:disabled="currentPage === 1"
|
|
435
|
+
@click="goTo(currentPage - 1)"
|
|
436
|
+
/>
|
|
437
|
+
|
|
438
|
+
<template v-for="(item, i) in pageItems" :key="`${item}-${i}`">
|
|
439
|
+
<span v-if="item === '...'" class="rm-table__pager-gap">…</span>
|
|
440
|
+
<button
|
|
441
|
+
v-else
|
|
442
|
+
type="button"
|
|
443
|
+
class="rm-table__pager-page"
|
|
444
|
+
:class="{ 'is-current': item === currentPage }"
|
|
445
|
+
:aria-current="item === currentPage ? 'page' : undefined"
|
|
446
|
+
@click="goTo(item as number)"
|
|
447
|
+
>{{ item }}</button>
|
|
448
|
+
</template>
|
|
449
|
+
|
|
450
|
+
<RmButton
|
|
451
|
+
icon-only
|
|
452
|
+
size="sm"
|
|
453
|
+
variant="secondary"
|
|
454
|
+
prefix-icon="CaretRight"
|
|
455
|
+
aria-label="Page suivante"
|
|
456
|
+
:disabled="currentPage === totalPages"
|
|
457
|
+
@click="goTo(currentPage + 1)"
|
|
458
|
+
/>
|
|
459
|
+
</div>
|
|
460
|
+
</nav>
|
|
357
461
|
</div>
|
|
358
462
|
</template>
|
|
359
463
|
|
|
360
464
|
<style scoped>
|
|
361
|
-
.rm-table-wrapper{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-md,8px);flex:1;overflow:hidden}.rm-table__scroll{overflow:auto}.rm-table{border-collapse:collapse;font-size:var(--font-13,13px);width:100%}.rm-sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0;white-space:nowrap}.rm-table caption{caption-side:top;color:var(--text-primary);font-size:var(--font-13,13px);font-weight:700;padding:0 0 var(--space-8,8px);text-align:left}.rm-table__th{background:var(--bg-surface-2);border-bottom:1px solid var(--border-default);color:var(--text-secondary);font-size:var(--font-11,11px);font-weight:700;letter-spacing:.08em;padding:12px 14px;text-align:left;text-transform:uppercase;white-space:nowrap}.rm-table--sticky .rm-table__th{border-bottom:none;box-shadow:inset 0 -1px 0 var(--border-default);position:sticky;top:0;z-index:2}.rm-table__th--center{text-align:center}.rm-table__th--num,.rm-table__th--right{text-align:right}.rm-table__th--check,.rm-table__th--expand{padding-right:0;width:36px}.rm-table__sort{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:inline-flex;font:inherit;gap:var(--space-6,6px);letter-spacing:inherit;padding:0;text-transform:inherit}.rm-table__sort:hover{color:var(--text-primary)}.rm-table__sort:after{border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid var(--text-muted);content:"";display:inline-block;height:0;opacity:.5;vertical-align:middle;width:0}.rm-table__th[aria-sort=ascending] .rm-table__sort:after{border-bottom:5px solid var(--color-primary);border-top:none;opacity:1}.rm-table__th[aria-sort=descending] .rm-table__sort:after{border-top:5px solid var(--color-primary);opacity:1}.rm-table a:focus-visible,.rm-table button:focus-visible{border-radius:var(--radius-sm,4px);outline:2px solid var(--color-primary);outline-offset:2px}.rm-table__td{border-bottom:1px solid var(--border-soft);color:var(--text-primary);padding:12px 14px;text-align:left;vertical-align:top}th.rm-table__td{background:transparent;font-weight:700;letter-spacing:0;text-transform:none}.rm-table__td--center{text-align:center}.rm-table__td--right{text-align:right}.rm-table__td--num{font-variant-numeric:tabular-nums;text-align:right}.rm-table__td--check,.rm-table__td--expand{color:var(--text-muted);padding-right:0;width:36px}.rm-table__row--striped>.rm-table__td{background:var(--bg-surface-2)}.rm-table--hoverable .rm-table__row:hover>.rm-table__td,.rm-table__row--selected>.rm-table__td{background:var(--bg-hover)}.rm-table__row--expandable{cursor:pointer}.rm-table__caret{display:inline-flex;transition:transform .16s var(--ease-out,ease)}.rm-table__caret.is-open{color:var(--color-primary);transform:rotate(90deg)}.rm-table__group>th{background:var(--bg-hover);color:var(--color-primary);font-size:var(--font-11,11px);font-weight:700;letter-spacing:.08em;padding:var(--space-8,8px) var(--space-12,12px);text-align:left;text-transform:uppercase}.rm-table__detail>td{background:var(--bg-surface-2);border-bottom:1px solid var(--border-soft);padding:0}.rm-table__detail-body{color:var(--text-secondary);font-size:var(--font-12,12px);line-height:1.6;padding:var(--space-12,12px) var(--space-20,20px)}.rm-table__td--heat-1{background:#fee5e5!important;color:#911616}.rm-table__td--heat-2{background:#ffe7d2!important;color:#8a4309}.rm-table__td--heat-3{background:#fff6cc!important;color:#6b5400}.rm-table__td--heat-4{background:#d8f3dc!important;color:#1b5e25}.rm-table__td--heat-5{background:#b7e4c7!important;color:#0e4b19}:root[data-theme=dark] .rm-table__td--heat-1{background:#3f1414!important;color:#ffb4b4}:root[data-theme=dark] .rm-table__td--heat-2{background:#3d2410!important;color:#ffc58a}:root[data-theme=dark] .rm-table__td--heat-3{background:#3a3210!important;color:#ffe07a}:root[data-theme=dark] .rm-table__td--heat-4{background:#18351f!important;color:#8ae0a4}:root[data-theme=dark] .rm-table__td--heat-5{background:#0f4019!important;color:#b4f0c2}.rm-table--compact .rm-table__td,.rm-table--compact .rm-table__th{font-size:var(--font-12,12px);padding:var(--space-6,6px) var(--space-12,12px)}.rm-table--compact .rm-table__th{font-size:var(--font-10,10px)}.rm-table__bulk{align-items:center;background:var(--bg-hover);border-bottom:1px solid var(--color-violet-pale);display:flex;font-size:var(--font-12,12px);justify-content:space-between;padding:var(--space-8,8px) var(--space-16,16px)}.rm-table__bulk-count b{color:var(--color-primary)}.rm-table__bulk-actions{display:flex;gap:var(--space-4,4px)}.rm-table__empty{color:var(--text-muted);padding:32px 16px;text-align:center}.rm-table__skeleton{height:14px;width:80%}
|
|
465
|
+
.rm-table-wrapper{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:var(--radius-md,8px);flex:1;overflow:hidden}.rm-table__scroll{overflow:auto}.rm-table{border-collapse:collapse;font-size:var(--font-13,13px);width:100%}.rm-sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0;white-space:nowrap}.rm-table caption{caption-side:top;color:var(--text-primary);font-size:var(--font-13,13px);font-weight:700;padding:0 0 var(--space-8,8px);text-align:left}.rm-table__th{background:var(--bg-surface-2);border-bottom:1px solid var(--border-default);color:var(--text-secondary);font-size:var(--font-11,11px);font-weight:700;letter-spacing:.08em;padding:12px 14px;text-align:left;text-transform:uppercase;white-space:nowrap}.rm-table--sticky .rm-table__th{border-bottom:none;box-shadow:inset 0 -1px 0 var(--border-default);position:sticky;top:0;z-index:2}.rm-table__th--center{text-align:center}.rm-table__th--num,.rm-table__th--right{text-align:right}.rm-table__th--check,.rm-table__th--expand{padding-right:0;width:36px}.rm-table__sort{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:inline-flex;font:inherit;gap:var(--space-6,6px);letter-spacing:inherit;padding:0;text-transform:inherit}.rm-table__sort:hover{color:var(--text-primary)}.rm-table__sort:after{border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid var(--text-muted);content:"";display:inline-block;height:0;opacity:.5;vertical-align:middle;width:0}.rm-table__th[aria-sort=ascending] .rm-table__sort:after{border-bottom:5px solid var(--color-primary);border-top:none;opacity:1}.rm-table__th[aria-sort=descending] .rm-table__sort:after{border-top:5px solid var(--color-primary);opacity:1}.rm-table a:focus-visible,.rm-table button:focus-visible{border-radius:var(--radius-sm,4px);outline:2px solid var(--color-primary);outline-offset:2px}.rm-table__td{border-bottom:1px solid var(--border-soft);color:var(--text-primary);padding:12px 14px;text-align:left;vertical-align:top}th.rm-table__td{background:transparent;font-weight:700;letter-spacing:0;text-transform:none}.rm-table__td--center{text-align:center}.rm-table__td--right{text-align:right}.rm-table__td--num{font-variant-numeric:tabular-nums;text-align:right}.rm-table__td--check,.rm-table__td--expand{color:var(--text-muted);padding-right:0;width:36px}.rm-table__row--striped>.rm-table__td{background:var(--bg-surface-2)}.rm-table--hoverable .rm-table__row:hover>.rm-table__td,.rm-table__row--selected>.rm-table__td{background:var(--bg-hover)}.rm-table__row--expandable{cursor:pointer}.rm-table__caret{display:inline-flex;transition:transform .16s var(--ease-out,ease)}.rm-table__caret.is-open{color:var(--color-primary);transform:rotate(90deg)}.rm-table__group>th{background:var(--bg-hover);color:var(--color-primary);font-size:var(--font-11,11px);font-weight:700;letter-spacing:.08em;padding:var(--space-8,8px) var(--space-12,12px);text-align:left;text-transform:uppercase}.rm-table__detail>td{background:var(--bg-surface-2);border-bottom:1px solid var(--border-soft);padding:0}.rm-table__detail-body{color:var(--text-secondary);font-size:var(--font-12,12px);line-height:1.6;padding:var(--space-12,12px) var(--space-20,20px)}.rm-table__td--heat-1{background:#fee5e5!important;color:#911616}.rm-table__td--heat-2{background:#ffe7d2!important;color:#8a4309}.rm-table__td--heat-3{background:#fff6cc!important;color:#6b5400}.rm-table__td--heat-4{background:#d8f3dc!important;color:#1b5e25}.rm-table__td--heat-5{background:#b7e4c7!important;color:#0e4b19}:root[data-theme=dark] .rm-table__td--heat-1{background:#3f1414!important;color:#ffb4b4}:root[data-theme=dark] .rm-table__td--heat-2{background:#3d2410!important;color:#ffc58a}:root[data-theme=dark] .rm-table__td--heat-3{background:#3a3210!important;color:#ffe07a}:root[data-theme=dark] .rm-table__td--heat-4{background:#18351f!important;color:#8ae0a4}:root[data-theme=dark] .rm-table__td--heat-5{background:#0f4019!important;color:#b4f0c2}.rm-table--compact .rm-table__td,.rm-table--compact .rm-table__th{font-size:var(--font-12,12px);padding:var(--space-6,6px) var(--space-12,12px)}.rm-table--compact .rm-table__th{font-size:var(--font-10,10px)}.rm-table__bulk{align-items:center;background:var(--bg-hover);border-bottom:1px solid var(--color-violet-pale);display:flex;font-size:var(--font-12,12px);justify-content:space-between;padding:var(--space-8,8px) var(--space-16,16px)}.rm-table__bulk-count b{color:var(--color-primary)}.rm-table__bulk-actions{display:flex;gap:var(--space-4,4px)}.rm-table__pager{align-items:center;background:var(--bg-surface);border-top:1px solid var(--border-default);display:flex;font-size:var(--font-12,12px);gap:var(--space-12,12px);justify-content:space-between;padding:var(--space-8,8px) var(--space-12,12px)}.rm-table__pager-count{color:var(--text-secondary);font-variant-numeric:tabular-nums}.rm-table__pager-controls{align-items:center;display:flex;gap:var(--space-4,4px)}.rm-table__pager-page{background:none;border:1px solid transparent;border-radius:var(--radius-sm,6px);color:var(--text-secondary);cursor:pointer;font:inherit;font-variant-numeric:tabular-nums;height:28px;min-width:28px;padding:0 6px}.rm-table__pager-page:hover{background:var(--bg-hover);color:var(--color-secondary)}.rm-table__pager-page.is-current{background:var(--color-primary);border-color:var(--color-primary);color:var(--color-text-inverse)}.rm-table__pager-gap{color:var(--text-muted);padding:0 2px}.rm-table__empty{color:var(--text-muted);padding:32px 16px;text-align:center}.rm-table__skeleton{height:14px;width:80%}
|
|
362
466
|
</style>
|