@tekkare/romulus 0.1.62 → 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
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
Slots : #CardTitle (libelle de la puce si `customTitle`), #Rail (sur la
|
|
12
12
|
barre, a cote de la puce : le reglage qu'on change tout le temps et qui ne
|
|
13
|
-
merite pas d'ouvrir le panneau
|
|
13
|
+
merite pas d'ouvrir le panneau ; il passe sur sa propre rangee quand la page
|
|
14
|
+
fournit aussi #Search), #Search, #Filters.
|
|
14
15
|
Le contenu projete vient encore, dans les apps, des champs Auriga
|
|
15
16
|
(arg-advanced-search-bar) : les regles `:deep` en bas les remettent a la
|
|
16
17
|
hauteur et au rayon de la rangee. Elles tomberont avec ces champs.
|
|
@@ -73,6 +74,14 @@ const slots = useSlots()
|
|
|
73
74
|
const hasAdvanced = computed(() => !!slots.Advanced)
|
|
74
75
|
const hasCustomize = computed(() => !!slots.Customize)
|
|
75
76
|
const hasRail = computed(() => !!slots.Rail)
|
|
77
|
+
const hasSearch = computed(() => !!slots.Search)
|
|
78
|
+
/**
|
|
79
|
+
* Le rail ne descend sur sa propre rangee que si la barre porte AUSSI un champ
|
|
80
|
+
* de recherche : c'est le champ qui lui dispute la place, pas les puces. Sans
|
|
81
|
+
* champ, il tient a cote de « Filtres » et une rangee de plus ne ferait
|
|
82
|
+
* qu'eloigner le contenu du bandeau.
|
|
83
|
+
*/
|
|
84
|
+
const railBelow = computed(() => hasRail.value && hasSearch.value)
|
|
76
85
|
|
|
77
86
|
function toggle(which: RmFilterBarPanel) {
|
|
78
87
|
panel.value = panel.value === which ? null : which
|
|
@@ -178,6 +187,12 @@ onBeforeUnmount(() => {
|
|
|
178
187
|
<span>{{ t.customize }}</span>
|
|
179
188
|
</button>
|
|
180
189
|
|
|
190
|
+
<!-- Rail pose sur la rangee des puces : cas ordinaire, une page sans
|
|
191
|
+
champ de recherche. -->
|
|
192
|
+
<div v-if="hasRail && !railBelow" class="rm-fbar__rail">
|
|
193
|
+
<slot name="Rail" />
|
|
194
|
+
</div>
|
|
195
|
+
|
|
181
196
|
<div class="rm-fbar__search">
|
|
182
197
|
<slot name="Search" />
|
|
183
198
|
|
|
@@ -202,13 +217,13 @@ onBeforeUnmount(() => {
|
|
|
202
217
|
A reserver a ce qu'on change a chaque lecture ; tout le reste va
|
|
203
218
|
dans le panneau, sinon la barre redevient un panneau ouvert.
|
|
204
219
|
|
|
205
|
-
Sa propre rangee
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
<div v-if="
|
|
220
|
+
Sa propre rangee UNIQUEMENT face a un champ de recherche : les deux
|
|
221
|
+
se disputaient la premiere, un rail de huit segments (les niveaux
|
|
222
|
+
d'agregation, du produit au fabricant) sortait du cadre par la droite,
|
|
223
|
+
ses derniers segments coupes, et le champ tombait a sa largeur
|
|
224
|
+
minimale. Chacun sur sa ligne prend alors toute la largeur de la
|
|
225
|
+
colonne. -->
|
|
226
|
+
<div v-if="railBelow" class="rm-fbar__rail-row">
|
|
212
227
|
<div class="rm-fbar__rail">
|
|
213
228
|
<slot name="Rail" />
|
|
214
229
|
</div>
|
|
@@ -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>
|