@tekkare/romulus 0.1.153 → 0.1.154

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
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.16.0"
6
6
  },
7
- "version": "0.1.152",
7
+ "version": "0.1.153",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
@@ -17,6 +17,8 @@
17
17
  | `columnStyle.size: number[]` | `width` par colonne | les parts d'Auriga se rendent en pourcentage |
18
18
  | `columnStyle.align` | `align` par colonne | |
19
19
  | `tableMaxHeight="600"` | `max-height="600px"` | ATTENTION : Auriga ajoutait `px`, ici la valeur est du CSS et un nombre nu desactive la hauteur en silence |
20
+ | `search` | `search` | le filtre porte sur toutes les colonnes declarees, pas sur la seule premiere |
21
+ | `white-lines: number[]` | `striped` / `emphasis-key` | Auriga recevait des index de lignes a peindre en blanc, et les appels s'en servaient pour deux choses opposees : refaire la zebre a la main (c'est `striped`, pose par defaut) ou detacher une ligne qui porte un total ou une racine de nomenclature (c'est `emphasis-key`, un champ booleen de la ligne) |
20
22
 
21
23
  Des qu'il faut comparer plusieurs entites sur plusieurs dimensions. Pour une
22
24
  seule metrique, c'est un KPI ; pour une liste sans structure de colonnes,
@@ -42,7 +44,7 @@
42
44
  par colonne pour la carte de chaleur.
43
45
  -->
44
46
  <script setup lang="ts">
45
- import { computed, ref } from 'vue'
47
+ import { computed, ref, watch } from 'vue'
46
48
 
47
49
  export interface RmTableColumn {
48
50
  key: string
@@ -108,6 +110,21 @@ export interface RmTableProps {
108
110
  groupLabel?: (value: any) => string
109
111
  /** Chaque ligne se deplie sur un detail, rendu par le slot `expanded`. */
110
112
  expandable?: boolean
113
+ /**
114
+ * Champ booleen de la ligne qui la met en avant : une racine de
115
+ * nomenclature au-dessus de ses niveaux, un total au milieu d'un detail.
116
+ * Elle garde ses colonnes et ses chiffres, la ou un intitule de section
117
+ * (`groupBy`) fusionne la ligne entiere et ne peut donc rien porter.
118
+ */
119
+ emphasisKey?: string
120
+ /**
121
+ * Champ de recherche au-dessus de la table. `auto` le pose au-dela de
122
+ * SEARCH_THRESHOLD lignes : une table de six lignes se lit, une de cinq
123
+ * cents se cherche. Le filtre porte sur le texte rendu de chaque colonne,
124
+ * pas sur le seul libelle.
125
+ */
126
+ search?: boolean | 'auto'
127
+ searchPlaceholder?: string
111
128
  /**
112
129
  * Lignes par page. A zero, la table rend tout : c'est le bon choix tant
113
130
  * qu'on tient sous la centaine de lignes, la molette y va plus vite qu'un
@@ -134,9 +151,15 @@ const props = withDefaults(defineProps<RmTableProps>(), {
134
151
  groupBy: '',
135
152
  groupLabel: undefined,
136
153
  expandable: false,
154
+ emphasisKey: '',
155
+ search: false,
156
+ searchPlaceholder: undefined,
137
157
  pageSize: 0,
138
158
  })
139
159
 
160
+ /** Au-dela de ce nombre de lignes, `search: 'auto'` pose le champ. */
161
+ const SEARCH_THRESHOLD = 25
162
+
140
163
  const emit = defineEmits<{
141
164
  sort: [field: string, order: 'asc' | 'desc']
142
165
  rowClick: [row: Record<string, any>, index: number]
@@ -188,9 +211,41 @@ function keyOf(row: Record<string, any>, index: number) {
188
211
  return props.rowKey ? cellValue(row, props.rowKey) : index
189
212
  }
190
213
 
214
+ const hasSearch = computed(() =>
215
+ props.search === 'auto' ? props.rows.length > SEARCH_THRESHOLD : props.search,
216
+ )
217
+
218
+ const searchTerm = ref('')
219
+
220
+ /** Le texte d'une cellule, quelle que soit la forme de la valeur. */
221
+ function cellText(value: any): string {
222
+ if (value === null || value === undefined) return ''
223
+ if (Array.isArray(value)) return value.map(cellText).join(' ')
224
+ if (typeof value === 'object') return Object.values(value).map(cellText).join(' ')
225
+ return String(value)
226
+ }
227
+
228
+ /**
229
+ * Les lignes retenues par la recherche. Le filtre porte sur toutes les
230
+ * colonnes declarees : chercher « accord » dans un tableau de produits doit
231
+ * trouver le laboratoire comme la marque, sans que l'appelant ait a dire ou
232
+ * regarder.
233
+ */
234
+ const filteredRows = computed(() => {
235
+ const needle = searchTerm.value.trim().toLowerCase()
236
+ if (!hasSearch.value || !needle) return props.rows
237
+ return props.rows.filter(row =>
238
+ props.columns.some(col => cellText(cellValue(row, col.key)).toLowerCase().includes(needle)),
239
+ )
240
+ })
241
+
242
+ // Une recherche qui raccourcit la liste laisserait la table sur une page
243
+ // vide : elle revient a la premiere.
244
+ watch(searchTerm, () => { page.value = 1 })
245
+
191
246
  const sortedRows = computed(() => {
192
- if (!currentSort.value) return props.rows
193
- return [...props.rows].sort((a, b) => {
247
+ if (!currentSort.value) return filteredRows.value
248
+ return [...filteredRows.value].sort((a, b) => {
194
249
  const aVal = cellValue(a, currentSort.value) ?? ''
195
250
  const bVal = cellValue(b, currentSort.value) ?? ''
196
251
  const aStr = Array.isArray(aVal) ? aVal.join(', ') : String(aVal)
@@ -300,6 +355,11 @@ function toggleExpand(key: string | number) {
300
355
  : [...expanded.value, key]
301
356
  }
302
357
 
358
+ /** La ligne porte-t-elle la marque de mise en avant ? */
359
+ function isEmphasis(row: Record<string, any>): boolean {
360
+ return props.emphasisKey ? !!cellValue(row, props.emphasisKey) : false
361
+ }
362
+
303
363
  function heatClass(col: RmTableColumn, row: Record<string, any>) {
304
364
  if (!col.heat) return undefined
305
365
  const level = col.heat(cellValue(row, col.key), row)
@@ -320,6 +380,17 @@ function heatClass(col: RmTableColumn, row: Record<string, any>) {
320
380
  </span>
321
381
  </div>
322
382
 
383
+ <div v-if="hasSearch" class="rm-table__search">
384
+ <RmIcon name="MagnifyingGlass" :size="16" />
385
+ <input
386
+ v-model="searchTerm"
387
+ type="search"
388
+ class="rm-table__search-input"
389
+ :placeholder="searchPlaceholder ?? 'Rechercher...'"
390
+ :aria-label="searchPlaceholder ?? 'Rechercher dans le tableau'"
391
+ >
392
+ </div>
393
+
323
394
  <div
324
395
  class="rm-table__scroll"
325
396
  :style="stickyHeader && maxHeight !== 'auto' ? { maxHeight } : undefined"
@@ -398,7 +469,8 @@ function heatClass(col: RmTableColumn, row: Record<string, any>) {
398
469
  <tr
399
470
  class="rm-table__row"
400
471
  :class="{
401
- 'rm-table__row--striped': striped && i % 2 === 1,
472
+ 'rm-table__row--striped': striped && i % 2 === 1 && !isEmphasis(entry.row),
473
+ 'rm-table__row--emphasis': isEmphasis(entry.row),
402
474
  'rm-table__row--selected': selectable && selected.includes(keyOf(entry.row, entry.index)),
403
475
  'rm-table__row--expandable': expandable,
404
476
  }"
@@ -502,5 +574,5 @@ function heatClass(col: RmTableColumn, row: Record<string, any>) {
502
574
  </template>
503
575
 
504
576
  <style scoped>
505
- .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-wrapper--flat{background:transparent;border:none;border-radius:0}.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,var(--space-8));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:var(--space-12) 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,var(--space-6));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:var(--space-12) 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,var(--space-8)) var(--space-12,var(--space-12));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,var(--space-12)) var(--space-20,var(--space-20))}.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}body.night .rm-table__td--heat-1{background:#3f1414!important;color:#ffb4b4}body.night .rm-table__td--heat-2{background:#3d2410!important;color:#ffc58a}body.night .rm-table__td--heat-3{background:#3a3210!important;color:#ffe07a}body.night .rm-table__td--heat-4{background:#18351f!important;color:#8ae0a4}body.night .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,var(--space-6)) var(--space-12,var(--space-12))}.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,var(--space-8)) var(--space-16,var(--space-16))}.rm-table__bulk-count b{color:var(--color-primary)}.rm-table__bulk-actions{display:flex;gap:var(--space-4,var(--space-4))}.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,var(--space-12));justify-content:space-between;padding:var(--space-8,var(--space-8)) var(--space-12,var(--space-12))}.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,var(--space-4))}.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 var(--space-6)}.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 var(--space-2)}.rm-table__empty{color:var(--text-muted);padding:var(--space-32) var(--space-16);text-align:center}.rm-table__skeleton{height:14px;width:80%}
577
+ .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-wrapper--flat{background:transparent;border:none;border-radius:0}.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,var(--space-8));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:var(--space-12) 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,var(--space-6));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:var(--space-12) 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__row--emphasis>.rm-table__td{background:var(--bg-hover);font-weight:var(--weight-semibold,600)}.rm-table__row--emphasis>.rm-table__td:first-child{box-shadow:inset 3px 0 0 var(--color-primary)}.rm-table__search{align-items:center;border-bottom:1px solid var(--border-default);display:flex;gap:var(--space-8);padding:var(--space-8) var(--space-12)}.rm-table__search :deep(svg){color:var(--text-muted);flex-shrink:0}.rm-table__search-input{background:none;border:none;color:var(--text-primary);font-family:inherit;font-size:var(--font-13,13px);width:100%}.rm-table__search-input::-moz-placeholder{color:var(--text-muted)}.rm-table__search-input::placeholder{color:var(--text-muted)}.rm-table__search-input:focus-visible{outline:none}.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,var(--space-8)) var(--space-12,var(--space-12));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,var(--space-12)) var(--space-20,var(--space-20))}.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}body.night .rm-table__td--heat-1{background:#3f1414!important;color:#ffb4b4}body.night .rm-table__td--heat-2{background:#3d2410!important;color:#ffc58a}body.night .rm-table__td--heat-3{background:#3a3210!important;color:#ffe07a}body.night .rm-table__td--heat-4{background:#18351f!important;color:#8ae0a4}body.night .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,var(--space-6)) var(--space-12,var(--space-12))}.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,var(--space-8)) var(--space-16,var(--space-16))}.rm-table__bulk-count b{color:var(--color-primary)}.rm-table__bulk-actions{display:flex;gap:var(--space-4,var(--space-4))}.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,var(--space-12));justify-content:space-between;padding:var(--space-8,var(--space-8)) var(--space-12,var(--space-12))}.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,var(--space-4))}.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 var(--space-6)}.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 var(--space-2)}.rm-table__empty{color:var(--text-muted);padding:var(--space-32) var(--space-16);text-align:center}.rm-table__skeleton{height:14px;width:80%}
506
578
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekkare/romulus",
3
- "version": "0.1.153",
3
+ "version": "0.1.154",
4
4
  "description": "Romulus Design System - Nuxt module by Tekkare",
5
5
  "license": "MIT",
6
6
  "type": "module",